diff options
140 files changed, 7398 insertions, 977 deletions
diff --git a/Documentation/devicetree/bindings/arm/msm/msm.txt b/Documentation/devicetree/bindings/arm/msm/msm.txt index 1c0dfa43952c..a35a8f0d6622 100644 --- a/Documentation/devicetree/bindings/arm/msm/msm.txt +++ b/Documentation/devicetree/bindings/arm/msm/msm.txt @@ -255,6 +255,7 @@ compatible = "qcom,msmhamster-rumi" compatible = "qcom,msmhamster-cdp" compatible = "qcom,msmhamster-mtp" compatible = "qcom,msmfalcon-sim" +compatible = "qcom,msmfalcon-rumi" compatible = "qcom,msm8952-rumi" compatible = "qcom,msm8952-sim" compatible = "qcom,msm8952-qrd" diff --git a/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt b/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt index 827f96c341b6..1102cb5d4f6f 100644 --- a/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt +++ b/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt @@ -6,6 +6,8 @@ camera sensor when background light is dim to capture good picture. It can also be used for flashlight/torch application. It is part of PMIC on Qualcomm Technologies Inc. reference platforms. +Main node: + Required properties: - compatible : Should be "qcom,qpnp-flash-led-v2" - reg : Base address and size for flash LED modules @@ -14,10 +16,17 @@ Optional properties: - qcom,hdrm-auto-mode : Boolean type to select headroom auto mode enabled or not - qcom,isc-delay : Integer type to specify short circuit delay. Valid values are 32, 64, 128, 192. Unit is us. +- qcom,hw-strobe-option : Integer type to specify hardware strobe option. Based on the specified + value, additional GPIO configuration may be required to provide strobing + support. Supported values are: + 0: Flash strobe is used for LED1, LED2, LED3 + 1: Flash strobe is used for LED1, LED2 and GPIO10 is used for LED3 + 2: Flash strobe is used for LED1; GPIO9 is used for LED2; GPIO10 is used for LED3 + +Child node: Contains settings for each individual LED. Each LED channel needs a flash node and +torch node for itself, and an individual switch node to serve as an overall switch. -Required properties inside child node. Child node contains settings for each individual LED. -Each LED channel needs a flash node and torch node for itself, and an individual switch node to -serve as an overall switch. +Required Properties: - label : Type of led that will be used, either "flash", "torch", or "switch. - qcom,led-name : Name of the LED. - qcom,default-led-trigger : Trigger for the camera flash and torch. Accepted values are @@ -29,28 +38,54 @@ serve as an overall switch. not required for switch node. - qcom,max-current : Maximum current allowed on this LED. Valid values should be integer from 0 to 1500 inclusive. Flash 2 should have maximum current of - 750 per hardware requirement. Unit is mA. This is not required for switch - node. + 750 per hardware requirement. Unit is mA. For torch, the maximum current + is clamped at 500 mA. This is not required for the switch node. - qcom,duration-ms : Required property for flash nodes but not needed for torch. Integer type specifying flash duration. Values are from 10ms to 1280ms with 10ms resolution. This is not required for switch node. +- qcom,led-mask : Required property for switch nodes. Bitmask to indicate which leds are + controlled by this switch node. Accepted values are in the range 1 to 7, + inclusive. Example: + qcom,led-mask = <4>; /* This switch node controls the flash2/torch2 led. */ -Optional properties inside child node: +Optional properties: +- qcom,current-ma : operational current intensity for LED in mA. Accepted values are a + positive integer in the range of 0 to qcom,max-current inclusive. - qcom,ires-ua : Integer type to specify current resolution. Accepted values should be 12500, 10000, 7500, and 5000. Unit is uA. - qcom,hdrm-voltage-mv : Integer type specifying headroom voltage. Values are from 125mV to 500mV with 25mV resolution. Default setting is 325mV - qcom,hdrm-vol-hi-lo-win-mv : Integer type to specify headroom voltage swing range. Values are from 0mV to 375mV with 25mV resolution. Default setting is 100mV. -- pinctrl-names : This should be defined if a target uses pinctrl framework and there is GPIO - requirement for flash LEDs. See "pinctrl" in - Documentation/devicetree/bindings/pinctrl/msm-pinctrl.txt. It should specify - the names of the configs that pinctrl can install in driver. - Following are the pinctrl configs that can be installed: - "led_enable" : Enablement configuration of pins. This should specify active - config defined in each pin or pin group. - "led_disable" : Disablement configuration of pins. This should specify inactive - config defined in each pin or pin groups. +- pinctrl-names : Name of the pinctrl configuration that will be used when external GPIOs + are used for enabling/disabling, HW strobing of flash LEDs. For more + information on using pinctrl, please refer + Documentation/devicetree/bindings/pinctrl/msm-pinctrl.txt + Following are the pinctrl configs that can be specified: + "led_enable" : pinctrl config to enable led. This should specify the active + configuration defined for each pin or pin group. + "led_disable" : pinctrl config to disable led. This should specify the sleep + configuration defined for each pin or pin group. + "strobe_enable" : pinctrl config to enable hw-strobe. This should specify the + active configuration defined for each pin or pin group. + "strobe_disable" : pinctrl config to disable hw-strobe. This should specify the + sleep configuration defined for each pin or pin group. +- qcom,hw-strobe-gpio : phandle to specify GPIO for hardware strobing. This is used when there is no + pinctrl support or PMIC GPIOs are used. +- qcom,hw-strobe-sel : Boolean property to enable hardware strobe. If not defined, software strobe + will be used. +- qcom,hw-strobe-edge-trigger : Boolean property to select trigger type. If defined, hw-strobe is set to + be edge triggered. Otherwise, it is level triggered. +- qcom,hw-strobe-active-low : Boolean property to select strobe signal polarity. If defined, hw-strobe + signal polarity is set to active-low, else it is active-high. +- reg<n> : reg<n> (<n> represents number. e.g. 0,1,2,..) subnode is to add support for + multiple power sources. This subnode should only be specified for switch nodes. + Required property inside regulator node: + - regulator-name : Name of the regulator which has to be used for this + switch node. + Optional property inside regulator node: + - max-voltage-uv : This specifies max voltage of regulator. Some switch + or boost regulator does not need this property. Example: qcom,leds@d300 { @@ -69,6 +104,7 @@ Example: qcom,default-led-trigger = "flash0_trigger"; qcom,id = <0>; + qcom,current-ma = <1000>; qcom,duration-ms = <1280>; qcom,ires-ua = <12500>; qcom,hdrm-voltage-mv = <325>; @@ -82,6 +118,7 @@ Example: qcom,default-led-trigger = "flash1_trigger"; qcom,id = <1>; + qcom,current-ma = <1000>; qcom,duration-ms = <1280>; qcom,ires-ua = <12500>; qcom,hdrm-voltage-mv = <325>; @@ -95,6 +132,7 @@ Example: qcom,default-led-trigger = "flash2_trigger"; qcom,id = <2>; + qcom,current-ma = <500>; qcom,duration-ms = <1280>; qcom,ires-ua = <12500>; qcom,hdrm-voltage-mv = <325>; @@ -107,10 +145,11 @@ Example: pmi8998_torch0: qcom,torch_0 { label = "torch"; qcom,led-name = "led:torch_0"; - qcom,max-current = <200>; + qcom,max-current = <500>; qcom,default-led-trigger = "torch0_trigger"; qcom,id = <0>; + qcom,current-ma = <300>; qcom,ires-ua = <12500>; qcom,hdrm-voltage-mv = <325>; qcom,hdrm-vol-hi-lo-win-mv = <100>; @@ -119,10 +158,11 @@ Example: pmi8998_torch1: qcom,torch_1 { label = "torch"; qcom,led-name = "led:torch_1"; - qcom,max-current = <200>; + qcom,max-current = <500>; qcom,default-led-trigger = "torch1_trigger"; qcom,id = <1>; + qcom,current-ma = <300>; qcom,ires-ua = <12500>; qcom,hdrm-voltage-mv = <325>; qcom,hdrm-vol-hi-lo-win-mv = <100>; @@ -131,10 +171,11 @@ Example: pmi8998_torch2: qcom,torch_2 { label = "torch"; qcom,led-name = "led:torch_2"; - qcom,max-current = <200>; + qcom,max-current = <500>; qcom,default-led-trigger = "torch2_trigger"; qcom,id = <2>; + qcom,current-ma = <300>; qcom,ires-ua = <12500>; qcom,hdrm-voltage-mv = <325>; qcom,hdrm-vol-hi-lo-win-mv = <100>; @@ -143,11 +184,28 @@ Example: pinctrl-1 = <&led_disable>; }; - pmi8998_switch: qcom,led_switch { + pmi8998_switch0: qcom,led_switch_0 { + label = "switch"; + qcom,led-name = "led:switch_0"; + qcom,led-mask = <3>; + qcom,default-led-trigger = + "switch0_trigger"; + reg0 { + regulator-name = "pmicobalt_bob"; + max-voltage-uv = <3600000>; + }; + }; + + pmi8998_switch1: qcom,led_switch_1 { label = "switch"; - qcom,led-name = "led:switch"; + qcom,led-name = "led:switch_1"; + qcom,led-mask = <4>; qcom,default-led-trigger = - "switch_trigger"; + "switch1_trigger"; + reg0 { + regulator-name = "pmicobalt_bob"; + max-voltage-uv = <3600000>; + }; }; }; diff --git a/Documentation/devicetree/bindings/mfd/qcom-i2c-pmic.txt b/Documentation/devicetree/bindings/mfd/qcom-i2c-pmic.txt index 9c7c856ea42b..7e9aee1a96b3 100644 --- a/Documentation/devicetree/bindings/mfd/qcom-i2c-pmic.txt +++ b/Documentation/devicetree/bindings/mfd/qcom-i2c-pmic.txt @@ -32,29 +32,29 @@ Platform independent properties: Definition: 7-bit I2C address of the device. - interrupt-parent - Usage: required + Usage: optional Value type: <phandle> Definition: phandle of the interrupt controller which services the summary interrupt. - interrupts - Usage: required + Usage: optional Value type: <prop-encoded-array> Definition: Summary interrupt specifier. - interrupt-controller - Usage: required + Usage: optional Value type: <empty> Definition: Boolean flag which indicates this device node is an interrupt controller. - #interrupt-cells - Usage: required + Usage: optional Value type: <u32> Definition: Number of cells to encode an interrupt source. - qcom,periph-map - Usage: required + Usage: optional Value type: <prop-encoded-array> Definition: A list of u32 arrays. This provides a mapping between the summary status register bits and peripheral addresses. diff --git a/Documentation/devicetree/bindings/sound/qcom-usb-audio-qmi-dev.txt b/Documentation/devicetree/bindings/sound/qcom-usb-audio-qmi-dev.txt new file mode 100644 index 000000000000..9d3fb78f96a7 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/qcom-usb-audio-qmi-dev.txt @@ -0,0 +1,26 @@ +QTI USB Audio QMI Device + +USB Audio QMI device is used to attach to remote processor IOMMU and +map USB Audio driver specific memory to iova to share with remote +processor. + +Required Properties: + +- compatible : "qcom,usb-audio-qmi-dev" + +- iommus : A list of phandle and IOMMU specifier pairs that describe the + IOMMU master interfaces of the device. + +- qcom,usb-audio-stream-id : Stream id is prepended to iova before passing + iova to remote processor. This allows remote processor to access iova. + +- qcom,usb-audio-intr-num : Interrupter number for external sub system + destination. + +Example: + usb_audio_qmi_dev { + compatible = "qcom,usb-audio-qmi-dev"; + iommus = <&lpass_q6_smmu 12>; + qcom,usb-audio-stream-id = <12>; + qcom,usb-audio-intr-num = <1>; + }; diff --git a/Documentation/devicetree/bindings/usb/msm-phy.txt b/Documentation/devicetree/bindings/usb/msm-phy.txt index e909c67ca20a..35bb94b2ef70 100644 --- a/Documentation/devicetree/bindings/usb/msm-phy.txt +++ b/Documentation/devicetree/bindings/usb/msm-phy.txt @@ -143,7 +143,7 @@ Example: QUSB2 High-Speed PHY Required properties: - - compatible: Should be "qcom,qusb2phy" + - compatible: Should be "qcom,qusb2phy" or "qcom,qusb2phy-v2" - reg: Address and length of the QUSB2 PHY register set - reg-names: Should be "qusb_phy_base". - <supply-name>-supply: phandle to the regulator device tree node diff --git a/arch/arm/boot/dts/qcom/Makefile b/arch/arm/boot/dts/qcom/Makefile index dd955be6c908..a42019b58897 100644 --- a/arch/arm/boot/dts/qcom/Makefile +++ b/arch/arm/boot/dts/qcom/Makefile @@ -116,7 +116,8 @@ dtb-$(CONFIG_ARCH_MSMCOBALT) += msmcobalt-sim.dtb \ dtb-$(CONFIG_ARCH_MSMHAMSTER) += msmhamster-rumi.dtb -dtb-$(CONFIG_ARCH_MSMFALCON) += msmfalcon-sim.dtb +dtb-$(CONFIG_ARCH_MSMFALCON) += msmfalcon-sim.dtb \ + msmfalcon-rumi.dtb ifeq ($(CONFIG_ARM64),y) always := $(dtb-y) diff --git a/arch/arm/boot/dts/qcom/msm-arm-smmu-cobalt.dtsi b/arch/arm/boot/dts/qcom/msm-arm-smmu-cobalt.dtsi index 97abec77bed7..ab46221089f3 100644 --- a/arch/arm/boot/dts/qcom/msm-arm-smmu-cobalt.dtsi +++ b/arch/arm/boot/dts/qcom/msm-arm-smmu-cobalt.dtsi @@ -113,6 +113,7 @@ reg = <0xcd00000 0x40000>; #iommu-cells = <1>; qcom,register-save; + qcom,no-smr-check; qcom,skip-init; #global-interrupts = <2>; interrupts = <GIC_SPI 229 IRQ_TYPE_LEVEL_HIGH>, diff --git a/arch/arm/boot/dts/qcom/msm-audio-lpass.dtsi b/arch/arm/boot/dts/qcom/msm-audio-lpass.dtsi index ab6b614471e5..6729899379c5 100644 --- a/arch/arm/boot/dts/qcom/msm-audio-lpass.dtsi +++ b/arch/arm/boot/dts/qcom/msm-audio-lpass.dtsi @@ -51,7 +51,6 @@ voice: qcom,msm-pcm-voice { compatible = "qcom,msm-pcm-voice"; qcom,destroy-cvd; - qcom,vote-bms; }; stub_codec: qcom,msm-stub-codec { diff --git a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi index b612ec3f42b5..fad834199be5 100644 --- a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi +++ b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi @@ -383,7 +383,6 @@ qcom,supported-sizes = <6>, <9>; qcom,ramp-index = <2>; #pwm-cells = <2>; - status = "disabled"; }; pmicobalt_pwm_4: pwm@b400 { @@ -396,7 +395,6 @@ qcom,supported-sizes = <6>, <9>; qcom,ramp-index = <3>; #pwm-cells = <2>; - status = "disabled"; }; pmicobalt_pwm_5: pwm@b500 { @@ -409,7 +407,6 @@ qcom,supported-sizes = <6>, <9>; qcom,ramp-index = <4>; #pwm-cells = <2>; - status = "disabled"; }; pmicobalt_pwm_6: pwm@b600 { @@ -425,6 +422,50 @@ status = "disabled"; }; + qcom,leds@d000 { + compatible = "qcom,leds-qpnp"; + reg = <0xd000 0x100>; + label = "rgb"; + status = "okay"; + + red_led: qcom,rgb_0 { + label = "rgb"; + qcom,id = <3>; + qcom,mode = "pwm"; + pwms = <&pmicobalt_pwm_5 0 0>; + qcom,pwm-us = <1000>; + qcom,max-current = <12>; + qcom,default-state = "off"; + linux,name = "red"; + linux,default-trigger = + "battery-charging"; + }; + + green_led: qcom,rgb_1 { + label = "rgb"; + qcom,id = <4>; + qcom,mode = "pwm"; + pwms = <&pmicobalt_pwm_4 0 0>; + qcom,pwm-us = <1000>; + qcom,max-current = <12>; + qcom,default-state = "off"; + linux,name = "green"; + linux,default-trigger = "battery-full"; + }; + + blue_led: qcom,rgb_2 { + label = "rgb"; + qcom,id = <5>; + qcom,mode = "pwm"; + pwms = <&pmicobalt_pwm_3 0 0>; + qcom,pwm-us = <1000>; + qcom,max-current = <12>; + qcom,default-state = "off"; + linux,name = "blue"; + linux,default-trigger = "boot-indication"; + }; + }; + labibb: qpnp-labibb-regulator { compatible = "qcom,qpnp-labibb-regulator"; #address-cells = <1>; @@ -565,9 +606,9 @@ label = "flash"; qcom,led-name = "led:flash_0"; qcom,max-current = <1500>; - qcom,default-led-trigger = - "flash0_trigger"; + qcom,default-led-trigger = "flash0_trigger"; qcom,id = <0>; + qcom,current-ma = <1000>; qcom,duration-ms = <1280>; qcom,ires-ua = <12500>; qcom,hdrm-voltage-mv = <325>; @@ -578,9 +619,9 @@ label = "flash"; qcom,led-name = "led:flash_1"; qcom,max-current = <1500>; - qcom,default-led-trigger = - "flash1_trigger"; + qcom,default-led-trigger = "flash1_trigger"; qcom,id = <1>; + qcom,current-ma = <1000>; qcom,duration-ms = <1280>; qcom,ires-ua = <12500>; qcom,hdrm-voltage-mv = <325>; @@ -591,9 +632,9 @@ label = "flash"; qcom,led-name = "led:flash_2"; qcom,max-current = <750>; - qcom,default-led-trigger = - "flash2_trigger"; + qcom,default-led-trigger = "flash2_trigger"; qcom,id = <2>; + qcom,current-ma = <500>; qcom,duration-ms = <1280>; qcom,ires-ua = <12500>; qcom,hdrm-voltage-mv = <325>; @@ -603,11 +644,65 @@ pinctrl-1 = <&led_disable>; }; - pmicobalt_switch: qcom,led_switch { + pmicobalt_torch0: qcom,torch_0 { + label = "torch"; + qcom,led-name = "led:torch_0"; + qcom,max-current = <500>; + qcom,default-led-trigger = "torch0_trigger"; + qcom,id = <0>; + qcom,current-ma = <300>; + qcom,ires-ua = <12500>; + qcom,hdrm-voltage-mv = <325>; + qcom,hdrm-vol-hi-lo-win-mv = <100>; + }; + + pmicobalt_torch1: qcom,torch_1 { + label = "torch"; + qcom,led-name = "led:torch_1"; + qcom,max-current = <500>; + qcom,default-led-trigger = "torch1_trigger"; + qcom,id = <1>; + qcom,current-ma = <300>; + qcom,ires-ua = <12500>; + qcom,hdrm-voltage-mv = <325>; + qcom,hdrm-vol-hi-lo-win-mv = <100>; + }; + + pmicobalt_torch2: qcom,torch_2 { + label = "torch"; + qcom,led-name = "led:torch_2"; + qcom,max-current = <500>; + qcom,default-led-trigger = "torch2_trigger"; + qcom,id = <2>; + qcom,current-ma = <300>; + qcom,ires-ua = <12500>; + qcom,hdrm-voltage-mv = <325>; + qcom,hdrm-vol-hi-lo-win-mv = <100>; + pinctrl-names = "led_enable","led_disable"; + pinctrl-0 = <&led_enable>; + pinctrl-1 = <&led_disable>; + }; + + pmicobalt_switch0: qcom,led_switch_0 { + label = "switch"; + qcom,led-name = "led:switch_0"; + qcom,led-mask = <3>; + qcom,default-led-trigger = "switch0_trigger"; + reg0 { + regulator-name = "pmicobalt_bob"; + max-voltage-uv = <3600000>; + }; + }; + + pmicobalt_switch1: qcom,led_switch_1 { label = "switch"; - qcom,led-name = "led:switch"; - qcom,default-led-trigger = - "switch_trigger"; + qcom,led-name = "led:switch_1"; + qcom,led-mask = <4>; + qcom,default-led-trigger = "switch1_trigger"; + reg0 { + regulator-name = "pmicobalt_bob"; + max-voltage-uv = <3600000>; + }; }; }; }; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi index ae878c7f5bac..b414a215cbaa 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi @@ -30,6 +30,7 @@ sound-9335 { compatible = "qcom,msmcobalt-asoc-snd-tasha"; qcom,model = "msmcobalt-tasha-snd-card"; + qcom,hdmi-audio-rx; qcom,audio-routing = "AIF4 VI", "MCLK", @@ -98,8 +99,9 @@ "msm-dai-q6-dev.16396", "msm-dai-q6-dev.16398", "msm-dai-q6-dev.16399", "msm-dai-q6-dev.16401", "msm-dai-q6-dev.28672", "msm-dai-q6-dev.28673"; - asoc-codec = <&stub_codec>; - asoc-codec-names = "msm-stub-codec.1"; + asoc-codec = <&stub_codec>, <&hdmi_audio>; + asoc-codec-names = "msm-stub-codec.1", + "msm-hdmi-audio-codec-rx"; qcom,wsa-max-devs = <2>; qcom,wsa-devs = <&wsa881x_211>, <&wsa881x_212>, <&wsa881x_213>, <&wsa881x_214>; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-cdp.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-cdp.dtsi index 83b55da92fa2..3ed038069319 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-cdp.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-cdp.dtsi @@ -16,7 +16,7 @@ cell-index = <0>; compatible = "qcom,camera-flash"; qcom,flash-source = <&pmicobalt_flash0 &pmicobalt_flash1>; - qcom,switch-source = <&pmicobalt_switch>; + qcom,switch-source = <&pmicobalt_switch0>; status = "ok"; }; @@ -24,7 +24,7 @@ cell-index = <1>; compatible = "qcom,camera-flash"; qcom,flash-source = <&pmicobalt_flash2>; - qcom,switch-source = <&pmicobalt_switch>; + qcom,switch-source = <&pmicobalt_switch1>; status = "ok"; }; }; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-mtp.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-mtp.dtsi index 83b55da92fa2..d152c0049f96 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-mtp.dtsi @@ -16,7 +16,7 @@ cell-index = <0>; compatible = "qcom,camera-flash"; qcom,flash-source = <&pmicobalt_flash0 &pmicobalt_flash1>; - qcom,switch-source = <&pmicobalt_switch>; + qcom,switch-source = <&pmicobalt_switch0>; status = "ok"; }; @@ -24,7 +24,7 @@ cell-index = <1>; compatible = "qcom,camera-flash"; qcom,flash-source = <&pmicobalt_flash2>; - qcom,switch-source = <&pmicobalt_switch>; + qcom,switch-source = <&pmicobalt_switch1>; status = "ok"; }; }; @@ -198,7 +198,7 @@ reg = <0x0>; qcom,csiphy-sd-index = <0>; qcom,csid-sd-index = <0>; - qcom,mount-angle = <90>; + qcom,mount-angle = <270>; qcom,led-flash-src = <&led_flash0>; qcom,actuator-src = <&actuator0>; qcom,ois-src = <&ois0>; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-mdss-panels.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-mdss-panels.dtsi index df9d6c1e98c0..307d7a25441c 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-mdss-panels.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-mdss-panels.dtsi @@ -76,6 +76,8 @@ qcom,mdss-dsi-panel-timings = [00 1a 04 06 0a 0a 05 06 05 03 04 00]; qcom,mdss-dsi-t-clk-post = <0x0d>; qcom,mdss-dsi-t-clk-pre = <0x2d>; + qcom,esd-check-enabled; + qcom,mdss-dsi-panel-status-check-mode = "bta_check"; }; &dsi_dual_nt35597_cmd { diff --git a/arch/arm/boot/dts/qcom/msmcobalt-pm.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-pm.dtsi index 73f612516f84..b5d3a85f9b15 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-pm.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-pm.dtsi @@ -382,7 +382,13 @@ <0xff 324>, /* lpass_qca_slimbus_bam_ee1_irq */ <0xff 432>, /* smmu_Cirpt[8] */ <0xff 433>, /* smmu_Cirpt[9] */ - <0xff 461>; /* o_ocimem_nonsec_irq */ + <0xff 461>, /* o_ocimem_nonsec_irq */ + <0xff 69>, /* o_pwr_dcvsh_interrupt */ + <0xff 70>, /* o_perf_dcvsh_interrupt */ + <0xff 166>, /* o_lm_int_2qgic */ + <0xff 238>, /* crypto_bam_irq[0] */ + <0xff 132>; /* qup_irq */ + qcom,gpio-parent = <&tlmm>; qcom,gpio-map = <3 1>, diff --git a/arch/arm/boot/dts/qcom/msmcobalt-qrd.dts b/arch/arm/boot/dts/qcom/msmcobalt-qrd.dts index 9848d6da33a5..d95507b505c2 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-qrd.dts +++ b/arch/arm/boot/dts/qcom/msmcobalt-qrd.dts @@ -14,7 +14,7 @@ /dts-v1/; #include "msmcobalt.dtsi" -#include "msmcobalt-mtp.dtsi" +#include "msmcobalt-qrd.dtsi" / { model = "Qualcomm Technologies, Inc. MSM COBALT QRD"; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-qrd.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-qrd.dtsi new file mode 100644 index 000000000000..1720aca3b298 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msmcobalt-qrd.dtsi @@ -0,0 +1,13 @@ +/* Copyright (c) 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. + */ + +#include "msmcobalt-mtp.dtsi" diff --git a/arch/arm/boot/dts/qcom/msmcobalt-regulator.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-regulator.dtsi index 1d43d694eee5..0f3fe60465c1 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-regulator.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-regulator.dtsi @@ -149,6 +149,9 @@ pmcobalt_l1: regulator-l1 { regulator-min-microvolt = <880000>; regulator-max-microvolt = <880000>; + proxy-supply = <&pmcobalt_l1>; + qcom,proxy-consumer-enable; + qcom,proxy-consumer-current = <73400>; status = "okay"; }; }; @@ -158,6 +161,9 @@ pmcobalt_l2: regulator-l2 { regulator-min-microvolt = <1200000>; regulator-max-microvolt = <1200000>; + proxy-supply = <&pmcobalt_l2>; + qcom,proxy-consumer-enable; + qcom,proxy-consumer-current = <12560>; status = "okay"; }; }; @@ -298,6 +304,9 @@ pmcobalt_l14: regulator-l14 { regulator-min-microvolt = <1880000>; regulator-max-microvolt = <1880000>; + proxy-supply = <&pmcobalt_l14>; + qcom,proxy-consumer-enable; + qcom,proxy-consumer-current = <32000>; status = "okay"; }; }; @@ -862,23 +871,23 @@ qcom,cpr-open-loop-voltage-fuse-adjustment = <0 0 0 0>, - <0 0 0 20000>, - <0 0 0 20000>, - <0 0 0 20000>, - <0 0 0 20000>, - <0 0 0 20000>, - <0 0 0 20000>, - <0 0 0 20000>; + <8000 0 0 52000>, + <8000 0 0 52000>, + <8000 0 0 52000>, + <8000 0 0 52000>, + <8000 0 0 52000>, + <8000 0 0 52000>, + <8000 0 0 52000>; qcom,cpr-closed-loop-voltage-fuse-adjustment = <0 0 0 0>, - <1000 0 0 21000>, - <1000 0 0 21000>, - <1000 0 0 21000>, - <1000 0 0 21000>, - <1000 0 0 21000>, - <1000 0 0 21000>, - <1000 0 0 21000>; + <0 0 0 50000>, + <0 0 0 50000>, + <0 0 0 50000>, + <0 0 0 50000>, + <0 0 0 50000>, + <0 0 0 50000>, + <0 0 0 50000>; qcom,allow-voltage-interpolation; qcom,allow-quotient-interpolation; @@ -1001,35 +1010,35 @@ 1580 1602 2158 3042 2780 2069 0 0>; qcom,cpr-open-loop-voltage-fuse-adjustment = - < 0 0 0 0>, - < 0 0 0 0>, - < 0 0 0 0>, - < 0 0 0 0>, - < 0 0 0 0>, - < 0 0 0 0>, - < 0 0 0 0>, - < 0 0 0 0>; + < 72000 0 0 0>, + < 72000 0 0 0>, + < 72000 0 0 0>, + < 72000 0 0 0>, + < 72000 0 0 0>, + < 72000 0 0 0>, + < 72000 0 0 0>, + < 72000 0 0 0>; qcom,cpr-closed-loop-voltage-adjustment = - < 150000 150000 150000 150000 - 150000 150000>, - < 150000 150000 150000 150000 - 150000 150000>, - < 150000 150000 150000 150000 - 150000 150000>, - < 150000 150000 150000 150000 - 150000 150000>, - < 150000 150000 150000 150000 - 150000 150000>, - < 150000 150000 150000 150000 - 150000 150000>, - < 150000 150000 150000 150000 - 150000 150000>, - < 150000 150000 150000 150000 - 150000 150000>; + < 65000 26000 8000 0 + 0 0>, + < 65000 26000 8000 0 + 0 0>, + < 65000 26000 8000 0 + 0 0>, + < 65000 26000 8000 0 + 0 0>, + < 65000 26000 8000 0 + 0 0>, + < 65000 26000 8000 0 + 0 0>, + < 65000 26000 8000 0 + 0 0>, + < 65000 26000 8000 0 + 0 0>; qcom,cpr-floor-to-ceiling-max-range = - <50000 50000 50000 50000 50000 50000>; + <75000 75000 75000 75000 75000 75000>; qcom,cpr-fused-closed-loop-voltage-adjustment-map = <0 0 1 2 3 4>; diff --git a/arch/arm/boot/dts/qcom/msmcobalt.dtsi b/arch/arm/boot/dts/qcom/msmcobalt.dtsi index 99debfb0f4a0..1fa683bb08f3 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt.dtsi @@ -259,7 +259,7 @@ removed_regions: removed_regions@85800000 { compatible = "removed-dma-pool"; no-map; - reg = <0 0x85800000 0 0x5300000>; + reg = <0 0x85800000 0 0x3700000>; }; peripheral_mem: peripheral_region@91900000 { @@ -311,6 +311,16 @@ alignment = <0 0x200000>; size = <0 0x5c00000>; }; + + /* global autoconfigured region for contiguous allocations */ + linux,cma { + compatible = "shared-dma-pool"; + alloc-ranges = <0 0x00000000 0 0xffffffff>; + reusable; + alignment = <0 0x400000>; + size = <0 0x2000000>; + linux,cma-default; + }; }; }; @@ -647,7 +657,31 @@ < 1881600 >; qcom,cpufreq-table-4 = - < 300000 >; + < 300000 >, + < 345600 >, + < 422400 >, + < 480000 >, + < 556800 >, + < 633600 >, + < 710400 >, + < 787200 >, + < 844800 >, + < 902400 >, + < 979200 >, + < 1056000 >, + < 1171200 >, + < 1248000 >, + < 1324800 >, + < 1401600 >, + < 1478400 >, + < 1536000 >, + < 1632000 >, + < 1708800 >, + < 1785600 >, + < 1862400 >, + < 1939200 >, + < 2016000 >, + < 2092800 >; }; arm64-cpu-erp { @@ -775,7 +809,31 @@ < 1881600000 0x04040062 0x094e004e 0x4 >; qcom,perfcl-speedbin0-v0 = - < 300000000 0x0004000f 0x03200020 0x1>; + < 300000000 0x0004000f 0x01200020 0x1 >, + < 345600000 0x05040012 0x02200020 0x1 >, + < 422400000 0x05040016 0x02200020 0x1 >, + < 480000000 0x05040019 0x02200020 0x1 >, + < 556800000 0x0504001d 0x03200020 0x1 >, + < 633600000 0x05040021 0x03200020 0x1 >, + < 710400000 0x05040025 0x03200020 0x1 >, + < 787200000 0x05040029 0x04200020 0x1 >, + < 844800000 0x0404002c 0x04230023 0x1 >, + < 902400000 0x0404002f 0x04260026 0x1 >, + < 979200000 0x04040033 0x05290029 0x1 >, + < 1056000000 0x04040037 0x052c002c 0x1 >, + < 1171200000 0x0404003d 0x06310031 0x2 >, + < 1248000000 0x04040041 0x06340034 0x2 >, + < 1324800000 0x04040045 0x06370037 0x2 >, + < 1401600000 0x04040049 0x073a003a 0x2 >, + < 1478400000 0x0404004d 0x073e003e 0x2 >, + < 1536000000 0x04040050 0x07400040 0x2 >, + < 1632000000 0x04040055 0x08440044 0x3 >, + < 1708800000 0x04040059 0x08470047 0x3 >, + < 1785600000 0x0404005d 0x094a004a 0x3 >, + < 1862400000 0x04040061 0x094e004e 0x3 >, + < 1939200000 0x04040065 0x09510051 0x3 >, + < 2016000000 0x04040069 0x0a540054 0x3 >, + < 2092800000 0x0404006d 0x0a570057 0x3 >; qcom,up-timer = <1000 1000>; @@ -1118,11 +1176,6 @@ label = "adsprpc-smd"; iommus = <&lpass_q6_smmu 11>; }; - qcom,msm_fastrpc_compute_cb5 { - compatible = "qcom,msm-fastrpc-compute-cb"; - label = "adsprpc-smd"; - iommus = <&lpass_q6_smmu 12>; - }; qcom,msm_fastrpc_compute_cb6 { compatible = "qcom,msm-fastrpc-compute-cb"; label = "adsprpc-smd"; @@ -1907,6 +1960,13 @@ "phy_phy_reset", "ref_clk_src", "ref_clk"; }; + usb_audio_qmi_dev { + compatible = "qcom,usb-audio-qmi-dev"; + iommus = <&lpass_q6_smmu 12>; + qcom,usb-audio-stream-id = <12>; + qcom,usb-audio-intr-num = <2>; + }; + dbm_1p5: dbm@a8f8000 { compatible = "qcom,usb-dbm-1p5"; reg = <0xa8f8000 0x300>; @@ -2498,6 +2558,12 @@ qcom,rtb-size = <0x100000>; }; + qcom,mpm2-sleep-counter@10a3000 { + compatible = "qcom,mpm2-sleep-counter"; + reg = <0x010a3000 0x1000>; + clock-frequency = <32768>; + }; + qcom,msm-imem@146bf000 { compatible = "qcom,msm-imem"; reg = <0x146bf000 0x1000>; @@ -2759,6 +2825,8 @@ &gdsc_bimc_smmu { clock-names = "bus_clk"; clocks = <&clock_mmss clk_mmss_bimc_smmu_axi_clk>; + proxy-supply = <&gdsc_bimc_smmu>; + qcom,proxy-consumer-enable; status = "ok"; }; diff --git a/arch/arm/boot/dts/qcom/msmfalcon-rumi.dts b/arch/arm/boot/dts/qcom/msmfalcon-rumi.dts new file mode 100644 index 000000000000..6631d31bac6d --- /dev/null +++ b/arch/arm/boot/dts/qcom/msmfalcon-rumi.dts @@ -0,0 +1,29 @@ +/* Copyright (c) 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. + */ + + +/dts-v1/; + +#include "msmfalcon.dtsi" +#include "msmfalcon-pinctrl.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. MSM FALCON RUMI"; + compatible = "qcom,msmfalcon-rumi", "qcom,msmfalcon", "qcom,rumi"; + qcom,board-id = <15 0>; +}; + +&uartblsp2dm1 { + status = "ok"; + pinctrl-names = "default"; + pinctrl-0 = <&uart_console_active>; +}; diff --git a/arch/arm/configs/msmcortex_defconfig b/arch/arm/configs/msmcortex_defconfig new file mode 100644 index 000000000000..5a74e7e69ae8 --- /dev/null +++ b/arch/arm/configs/msmcortex_defconfig @@ -0,0 +1,562 @@ +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_AUDIT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_RCU_EXPERT=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_SCHED_HMP=y +CONFIG_NAMESPACES=y +# CONFIG_UTS_NS is not set +# CONFIG_PID_NS is not set +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_MEMBARRIER is not set +CONFIG_EMBEDDED=y +# CONFIG_COMPAT_BRK is not set +CONFIG_PROFILING=y +CONFIG_CC_STACKPROTECTOR_REGULAR=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SIG=y +CONFIG_MODULE_SIG_SHA512=y +# CONFIG_BLK_DEV_BSG is not set +CONFIG_PARTITION_ADVANCED=y +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_ARCH_QCOM=y +CONFIG_ARCH_MSMFALCON=y +CONFIG_SMP=y +CONFIG_SCHED_MC=y +CONFIG_NR_CPUS=8 +CONFIG_ARM_PSCI=y +CONFIG_PREEMPT=y +CONFIG_AEABI=y +CONFIG_HIGHMEM=y +# CONFIG_HIGHPTE is not set +CONFIG_CLEANCACHE=y +CONFIG_CMA=y +CONFIG_ZSMALLOC=y +CONFIG_SECCOMP=y +CONFIG_ARM_APPENDED_DTB=y +CONFIG_SCHED_FREQ_INPUT=y +# CONFIG_CPU_FREQ_STAT is not set +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_IDLE=y +CONFIG_VFP=y +CONFIG_NEON=y +CONFIG_KERNEL_MODE_NEON=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_PM_AUTOSLEEP=y +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_WAKELOCKS_LIMIT=0 +# CONFIG_PM_WAKELOCKS_GC is not set +CONFIG_PM_DEBUG=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM_USER=y +CONFIG_XFRM_STATISTICS=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_INET_AH=y +CONFIG_INET_ESP=y +CONFIG_INET_IPCOMP=y +# CONFIG_INET_LRO is not set +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_SUBTREES=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y +CONFIG_NETFILTER_XT_TARGET_HARDIDLETIMER=y +CONFIG_NETFILTER_XT_TARGET_LOG=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_NOTRACK=y +CONFIG_NETFILTER_XT_TARGET_TEE=y +CONFIG_NETFILTER_XT_TARGET_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_TRACE=y +CONFIG_NETFILTER_XT_TARGET_SECMARK=y +CONFIG_NETFILTER_XT_TARGET_TCPMSS=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_DSCP=y +CONFIG_NETFILTER_XT_MATCH_ESP=y +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +# CONFIG_NETFILTER_XT_MATCH_L2TP is not set +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_SECURITY=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +CONFIG_BRIDGE_NF_EBTABLES=y +CONFIG_BRIDGE_EBT_BROUTE=y +CONFIG_L2TP=y +CONFIG_L2TP_DEBUGFS=y +CONFIG_L2TP_V3=y +CONFIG_L2TP_IP=y +CONFIG_L2TP_ETH=y +CONFIG_BRIDGE=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_SCH_PRIO=y +CONFIG_NET_SCH_MULTIQ=y +CONFIG_NET_SCH_INGRESS=y +CONFIG_NET_CLS_FW=y +CONFIG_NET_CLS_U32=y +CONFIG_CLS_U32_MARK=y +CONFIG_NET_CLS_FLOW=y +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_CMP=y +CONFIG_NET_EMATCH_NBYTE=y +CONFIG_NET_EMATCH_U32=y +CONFIG_NET_EMATCH_META=y +CONFIG_NET_EMATCH_TEXT=y +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_GACT=y +CONFIG_NET_ACT_MIRRED=y +CONFIG_NET_ACT_SKBEDIT=y +CONFIG_DNS_RESOLVER=y +CONFIG_RMNET_DATA=y +CONFIG_RMNET_DATA_FC=y +CONFIG_RMNET_DATA_DEBUG_PKT=y +CONFIG_SOCKEV_NLMCAST=y +CONFIG_BT=y +CONFIG_MSM_BT_POWER=y +CONFIG_BTFM_SLIM=y +CONFIG_BTFM_SLIM_WCN3990=y +CONFIG_CFG80211=y +CONFIG_CFG80211_INTERNAL_REGDB=y +# CONFIG_CFG80211_CRDA_SUPPORT is not set +CONFIG_RFKILL=y +CONFIG_NFC_NQ=y +CONFIG_IPC_ROUTER=y +CONFIG_IPC_ROUTER_SECURITY=y +CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y +CONFIG_DMA_CMA=y +CONFIG_CMA_SIZE_MBYTES=40 +CONFIG_ZRAM=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_UID_STAT=y +CONFIG_UID_CPUTIME=y +CONFIG_MSM_ULTRASOUND=y +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=y +CONFIG_CHR_DEV_SCH=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +CONFIG_SCSI_UFSHCD=y +CONFIG_SCSI_UFSHCD_PLATFORM=y +CONFIG_SCSI_UFS_QCOM=y +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_UEVENT=y +CONFIG_DM_VERITY=y +CONFIG_NETDEVICES=y +CONFIG_BONDING=y +CONFIG_DUMMY=y +CONFIG_TUN=y +CONFIG_RNDIS_IPA=y +CONFIG_PHYLIB=y +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_MPPE=y +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +CONFIG_WCNSS_MEM_PRE_ALLOC=y +CONFIG_ATH_CARDS=y +CONFIG_CLD_LL_CORE=y +CONFIG_QPNP_POWER_ON=y +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_KEYRESET=y +CONFIG_KEYBOARD_GPIO=y +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_JOYSTICK=y +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21=y +CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v21=y +CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_HBTP_INPUT=y +CONFIG_INPUT_KEYCHORD=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_SERIO_SERPORT is not set +# CONFIG_VT is not set +# CONFIG_LEGACY_PTYS is not set +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y +CONFIG_SERIAL_MSM_HS=y +CONFIG_SERIAL_MSM_SMD=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_MSM_LEGACY=y +CONFIG_MSM_ADSPRPC=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_MSM_V2=y +CONFIG_SLIMBUS=y +CONFIG_SLIMBUS_MSM_NGD=y +CONFIG_SOUNDWIRE=y +CONFIG_SPI=y +CONFIG_SPI_QUP=y +CONFIG_SPI_SPIDEV=y +CONFIG_SPMI=y +CONFIG_PINCTRL_MSMCOBALT=y +CONFIG_PINCTRL_MSMFALCON=y +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_QPNP_PIN=y +CONFIG_APSS_CORE_EA=y +CONFIG_MSM_APM=y +CONFIG_QPNP_SMBCHARGER=y +CONFIG_SMB135X_CHARGER=y +CONFIG_SMB1351_USB_CHARGER=y +CONFIG_MSM_BCL_CTL=y +CONFIG_MSM_BCL_PERIPHERAL_CTL=y +CONFIG_QPNP_SMB2=y +CONFIG_SMB138X_CHARGER=y +CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y +CONFIG_THERMAL=y +CONFIG_MFD_SPMI_PMIC=y +CONFIG_MFD_I2C_PMIC=y +CONFIG_MSM_CDC_PINCTRL=y +CONFIG_MSM_CDC_SUPPLY=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_RPM_SMD=y +CONFIG_REGULATOR_QPNP=y +CONFIG_REGULATOR_QPNP_LABIBB=y +CONFIG_REGULATOR_SPM=y +CONFIG_REGULATOR_CPR3_HMSS=y +CONFIG_REGULATOR_CPR3_MMSS=y +CONFIG_REGULATOR_CPRH_KBSS=y +CONFIG_REGULATOR_MEM_ACC=y +CONFIG_REGULATOR_PROXY_CONSUMER=y +CONFIG_REGULATOR_STUB=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_V4L2_SUBDEV_API=y +CONFIG_VIDEO_ADV_DEBUG=y +CONFIG_VIDEO_FIXED_MINOR_RANGES=y +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_MSM_CAMERA=y +CONFIG_MSM_CAMERA_DEBUG=y +CONFIG_MSM_SDE_ROTATOR=y +CONFIG_QCOM_KGSL=y +CONFIG_FB=y +CONFIG_FB_VIRTUAL=y +CONFIG_LOGO=y +# CONFIG_LOGO_LINUX_MONO is not set +# CONFIG_LOGO_LINUX_VGA16 is not set +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_DYNAMIC_MINORS=y +CONFIG_SND_SOC=y +CONFIG_UHID=y +CONFIG_HID_APPLE=y +CONFIG_HID_MICROSOFT=y +CONFIG_USB=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_HCD_PLATFORM=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_HCD_PLATFORM=y +CONFIG_USB_STORAGE=y +CONFIG_USB_DWC3=y +CONFIG_USB_ISP1760=y +CONFIG_USB_ISP1760_HOST_ROLE=y +CONFIG_USB_OTG_WAKELOCK=y +CONFIG_NOP_USB_XCEIV=y +CONFIG_USB_MSM_SSPHY_QMP=y +CONFIG_MSM_QUSB_PHY=y +CONFIG_DUAL_ROLE_USB_INTF=y +CONFIG_USB_GADGET=y +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_CONFIGFS=y +CONFIG_USB_CONFIGFS_MASS_STORAGE=y +CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_USB_CONFIGFS_F_MTP=y +CONFIG_USB_CONFIGFS_F_PTP=y +CONFIG_USB_CONFIGFS_F_ACC=y +CONFIG_USB_CONFIGFS_UEVENT=y +CONFIG_USB_CONFIGFS_F_HID=y +CONFIG_USB_CONFIGFS_F_DIAG=y +CONFIG_USB_CONFIGFS_F_GSI=y +CONFIG_USB_CONFIGFS_F_CDEV=y +CONFIG_USB_CONFIGFS_F_QDSS=y +CONFIG_MMC=y +CONFIG_MMC_PERF_PROFILING=y +CONFIG_MMC_PARANOID_SD_INIT=y +CONFIG_MMC_CLKGATE=y +CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_TEST=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_MSM=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_QPNP=y +CONFIG_LEDS_QPNP_FLASH_V2=y +CONFIG_LEDS_QPNP_WLED=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_CPU=y +CONFIG_SWITCH=y +CONFIG_EDAC=y +CONFIG_EDAC_MM_EDAC=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_QPNP=y +CONFIG_DMADEVICES=y +CONFIG_QCOM_SPS_DMA=y +CONFIG_UIO=y +CONFIG_UIO_MSM_SHAREDMEM=y +CONFIG_STAGING=y +CONFIG_ASHMEM=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_SYNC=y +CONFIG_ION=y +CONFIG_ION_MSM=y +CONFIG_QPNP_REVID=y +CONFIG_QPNP_COINCELL=y +CONFIG_SPS=y +CONFIG_SPS_SUPPORT_NDP_BAM=y +CONFIG_IPA=y +CONFIG_RMNET_IPA=y +CONFIG_GSI=y +CONFIG_IPA3=y +CONFIG_RMNET_IPA3=y +CONFIG_GPIO_USB_DETECT=y +CONFIG_USB_BAM=y +CONFIG_REMOTE_SPINLOCK_MSM=y +CONFIG_ARM_SMMU=y +CONFIG_QCOM_COMMON_LOG=y +CONFIG_MSM_SMEM=y +CONFIG_QPNP_HAPTIC=y +CONFIG_MSM_SMD=y +CONFIG_MSM_SMD_DEBUG=y +CONFIG_MSM_GLINK=y +CONFIG_MSM_GLINK_LOOPBACK_SERVER=y +CONFIG_MSM_GLINK_SMD_XPRT=y +CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y +CONFIG_MSM_SPCOM=y +CONFIG_MSM_SMEM_LOGGING=y +CONFIG_MSM_SMP2P=y +CONFIG_MSM_SMP2P_TEST=y +CONFIG_MSM_QMI_INTERFACE=y +CONFIG_QCOM_BUS_SCALING=y +CONFIG_MSM_SERVICE_LOCATOR=y +CONFIG_QCOM_DCC=y +CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y +CONFIG_MSM_SYSMON_GLINK_COMM=y +CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y +CONFIG_MSM_GLINK_PKT=y +CONFIG_MSM_SPM=y +CONFIG_QCOM_SCM=y +CONFIG_QCOM_WATCHDOG_V2=y +CONFIG_QCOM_MEMORY_DUMP_V2=y +CONFIG_ICNSS=y +CONFIG_MSM_GLADIATOR_ERP_V2=y +CONFIG_PANIC_ON_GLADIATOR_ERROR_V2=y +CONFIG_MSM_GLADIATOR_HANG_DETECT=y +CONFIG_MSM_CORE_HANG_DETECT=y +CONFIG_MSM_RUN_QUEUE_STATS=y +CONFIG_MSM_BOOT_STATS=y +CONFIG_QCOM_CPUSS_DUMP=y +CONFIG_MSM_QDSP6_APRV2_GLINK=y +CONFIG_MSM_PERFORMANCE=y +CONFIG_MSM_SUBSYSTEM_RESTART=y +CONFIG_MSM_PIL=y +CONFIG_MSM_PIL_SSR_GENERIC=y +CONFIG_MSM_PIL_MSS_QDSP6V5=y +CONFIG_TRACER_PKT=y +CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y +CONFIG_MSM_MPM_OF=y +CONFIG_MSM_EVENT_TIMER=y +CONFIG_MSM_CORE_CTL_HELPER=y +CONFIG_MSM_SERVICE_NOTIFIER=y +CONFIG_MEM_SHARE_QMI_SERVICE=y +CONFIG_QCOM_BIMC_BWMON=y +CONFIG_DEVFREQ_GOV_QCOM_BW_HWMON=y +CONFIG_QCOM_DEVFREQ_DEVBW=y +CONFIG_EXTCON=y +CONFIG_IIO=y +CONFIG_QCOM_RRADC=y +CONFIG_PWM=y +CONFIG_PWM_QPNP=y +CONFIG_ARM_GIC_V3_ACL=y +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_MSM_TZ_LOG=y +CONFIG_SENSORS_SSC=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT3_FS=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_FUSE_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_ECRYPT_FS=y +CONFIG_ECRYPT_FS_MESSAGING=y +# CONFIG_NETWORK_FILESYSTEMS is not set +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y +CONFIG_DYNAMIC_DEBUG=y +CONFIG_DEBUG_INFO=y +CONFIG_PAGE_OWNER=y +CONFIG_PAGE_OWNER_ENABLE_DEFAULT=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_SLUB_DEBUG_PANIC_ON=y +CONFIG_DEBUG_OBJECTS=y +CONFIG_DEBUG_OBJECTS_FREE=y +CONFIG_DEBUG_OBJECTS_TIMERS=y +CONFIG_DEBUG_OBJECTS_WORK=y +CONFIG_DEBUG_OBJECTS_RCU_HEAD=y +CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER=y +CONFIG_SLUB_DEBUG_ON=y +CONFIG_DEBUG_KMEMLEAK=y +CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=4000 +CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y +CONFIG_DEBUG_STACK_USAGE=y +CONFIG_DEBUG_MEMORY_INIT=y +CONFIG_LOCKUP_DETECTOR=y +CONFIG_WQ_WATCHDOG=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_PANIC_ON_SCHED_BUG=y +CONFIG_PANIC_ON_RT_THROTTLING=y +CONFIG_SCHEDSTATS=y +CONFIG_SCHED_STACK_END_CHECK=y +CONFIG_TIMER_STATS=y +# CONFIG_DEBUG_PREEMPT is not set +CONFIG_DEBUG_SPINLOCK=y +CONFIG_DEBUG_MUTEXES=y +CONFIG_DEBUG_ATOMIC_SLEEP=y +CONFIG_DEBUG_LIST=y +CONFIG_FAULT_INJECTION=y +CONFIG_FAIL_PAGE_ALLOC=y +CONFIG_UFS_FAULT_INJECTION=y +CONFIG_FAULT_INJECTION_DEBUG_FS=y +CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y +CONFIG_IPC_LOGGING=y +CONFIG_QCOM_RTB=y +CONFIG_FUNCTION_TRACER=y +CONFIG_TRACER_SNAPSHOT=y +CONFIG_BLK_DEV_IO_TRACE=y +CONFIG_CPU_FREQ_SWITCH_PROFILER=y +CONFIG_MEMTEST=y +CONFIG_PANIC_ON_DATA_CORRUPTION=y +CONFIG_PID_IN_CONTEXTIDR=y +CONFIG_DEBUG_SET_MODULE_RONX=y +CONFIG_CORESIGHT=y +CONFIG_CORESIGHT_REMOTE_ETM=y +CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE=0 +CONFIG_CORESIGHT_CTI=y +CONFIG_CORESIGHT_TPDA=y +CONFIG_CORESIGHT_TPDM=y +CONFIG_CORESIGHT_QPDI=y +CONFIG_CORESIGHT_SOURCE_DUMMY=y +CONFIG_SECURITY=y +CONFIG_SECURITY_SELINUX=y +CONFIG_SECURITY_SMACK=y +CONFIG_CRYPTO_ECHAINIV=y +CONFIG_CRYPTO_XCBC=y +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_ANSI_CPRNG=y +CONFIG_CRYPTO_DEV_QCRYPTO=y +CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y +CONFIG_CRYPTO_DEV_QCEDEV=y +CONFIG_CRYPTO_DEV_OTA_CRYPTO=y +CONFIG_CRYPTO_DEV_QCE=y +CONFIG_XZ_DEC=y diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms index 4e38de7ddd3e..084b942d102d 100644 --- a/arch/arm64/Kconfig.platforms +++ b/arch/arm64/Kconfig.platforms @@ -56,6 +56,7 @@ config ARCH_QCOM select SOC_BUS select MSM_IRQ select THERMAL_WRITABLE_TRIPS + select RATIONAL help This enables support for the ARMv8 based Qualcomm chipsets. diff --git a/arch/arm64/configs/msm-perf_defconfig b/arch/arm64/configs/msm-perf_defconfig index bf60b4047cb1..fc2cce36bff9 100644 --- a/arch/arm64/configs/msm-perf_defconfig +++ b/arch/arm64/configs/msm-perf_defconfig @@ -36,6 +36,9 @@ CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y CONFIG_MODULE_FORCE_UNLOAD=y CONFIG_MODVERSIONS=y +CONFIG_MODULE_SIG=y +CONFIG_MODULE_SIG_FORCE=y +CONFIG_MODULE_SIG_SHA512=y CONFIG_PARTITION_ADVANCED=y CONFIG_ARCH_QCOM=y CONFIG_ARCH_MSM8996=y @@ -92,6 +95,7 @@ CONFIG_INET6_IPCOMP=y CONFIG_IPV6_MIP6=y CONFIG_IPV6_MULTIPLE_TABLES=y CONFIG_IPV6_SUBTREES=y +# CONFIG_NET_ACTIVITY_STATS is not set CONFIG_NETFILTER=y CONFIG_NF_CONNTRACK=y CONFIG_NF_CONNTRACK_SECMARK=y @@ -140,7 +144,6 @@ CONFIG_NETFILTER_XT_MATCH_MARK=y CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y CONFIG_NETFILTER_XT_MATCH_POLICY=y CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y -CONFIG_NETFILTER_XT_MATCH_QTAGUID=y CONFIG_NETFILTER_XT_MATCH_QUOTA=y CONFIG_NETFILTER_XT_MATCH_QUOTA2=y CONFIG_NETFILTER_XT_MATCH_SOCKET=y @@ -228,7 +231,6 @@ CONFIG_ZRAM=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 -CONFIG_UID_STAT=y CONFIG_QSEECOM=y CONFIG_HDCP_QSEECOM=y CONFIG_UID_CPUTIME=y @@ -566,7 +568,6 @@ CONFIG_CPU_FREQ_SWITCH_PROFILER=y CONFIG_DEBUG_SET_MODULE_RONX=y CONFIG_DEBUG_RODATA=y CONFIG_DEBUG_ALIGN_RODATA=y -CONFIG_KEYS=y CONFIG_PFK=y CONFIG_SECURITY=y CONFIG_SECURITY_NETWORK=y @@ -574,7 +575,6 @@ CONFIG_SECURITY_SELINUX=y CONFIG_CRYPTO_ECHAINIV=y CONFIG_CRYPTO_XCBC=y CONFIG_CRYPTO_MD4=y -CONFIG_CRYPTO_SHA512=y CONFIG_CRYPTO_TWOFISH=y CONFIG_CRYPTO_ANSI_CPRNG=y CONFIG_CRYPTO_DEV_QCRYPTO=y diff --git a/arch/arm64/configs/msm_defconfig b/arch/arm64/configs/msm_defconfig index 6c37c8fdf0fc..38e489936895 100644 --- a/arch/arm64/configs/msm_defconfig +++ b/arch/arm64/configs/msm_defconfig @@ -33,6 +33,9 @@ CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y CONFIG_MODULE_FORCE_UNLOAD=y CONFIG_MODVERSIONS=y +CONFIG_MODULE_SIG=y +CONFIG_MODULE_SIG_FORCE=y +CONFIG_MODULE_SIG_SHA512=y CONFIG_PARTITION_ADVANCED=y CONFIG_ARCH_QCOM=y CONFIG_ARCH_MSM8996=y @@ -95,6 +98,7 @@ CONFIG_INET6_IPCOMP=y CONFIG_IPV6_MIP6=y CONFIG_IPV6_MULTIPLE_TABLES=y CONFIG_IPV6_SUBTREES=y +# CONFIG_NET_ACTIVITY_STATS is not set CONFIG_NETFILTER=y CONFIG_NF_CONNTRACK=y CONFIG_NF_CONNTRACK_SECMARK=y @@ -143,7 +147,6 @@ CONFIG_NETFILTER_XT_MATCH_MARK=y CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y CONFIG_NETFILTER_XT_MATCH_POLICY=y CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y -CONFIG_NETFILTER_XT_MATCH_QTAGUID=y CONFIG_NETFILTER_XT_MATCH_QUOTA=y CONFIG_NETFILTER_XT_MATCH_QUOTA2=y CONFIG_NETFILTER_XT_MATCH_SOCKET=y @@ -221,7 +224,6 @@ CONFIG_ZRAM=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 -CONFIG_UID_STAT=y CONFIG_QSEECOM=y CONFIG_HDCP_QSEECOM=y CONFIG_UID_CPUTIME=y @@ -604,7 +606,6 @@ CONFIG_ARM64_PTDUMP=y CONFIG_DEBUG_SET_MODULE_RONX=y CONFIG_FREE_PAGES_RDONLY=y CONFIG_KERNEL_TEXT_RDONLY=y -CONFIG_KEYS=y CONFIG_PFK=y CONFIG_SECURITY=y CONFIG_SECURITY_SELINUX=y diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig index 706d01e147f5..3ee7e7827f63 100644 --- a/arch/arm64/configs/msmcortex-perf_defconfig +++ b/arch/arm64/configs/msmcortex-perf_defconfig @@ -35,6 +35,9 @@ CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y CONFIG_MODULE_FORCE_UNLOAD=y CONFIG_MODVERSIONS=y +CONFIG_MODULE_SIG=y +CONFIG_MODULE_SIG_FORCE=y +CONFIG_MODULE_SIG_SHA512=y CONFIG_PARTITION_ADVANCED=y CONFIG_ARCH_QCOM=y CONFIG_ARCH_MSMCOBALT=y @@ -85,7 +88,6 @@ CONFIG_IP_PNP_DHCP=y CONFIG_INET_AH=y CONFIG_INET_ESP=y CONFIG_INET_IPCOMP=y -# CONFIG_INET_LRO is not set CONFIG_IPV6_ROUTER_PREF=y CONFIG_IPV6_ROUTE_INFO=y CONFIG_IPV6_OPTIMISTIC_DAD=y @@ -95,6 +97,7 @@ CONFIG_INET6_IPCOMP=y CONFIG_IPV6_MIP6=y CONFIG_IPV6_MULTIPLE_TABLES=y CONFIG_IPV6_SUBTREES=y +# CONFIG_NET_ACTIVITY_STATS is not set CONFIG_NETFILTER=y CONFIG_NF_CONNTRACK=y CONFIG_NF_CONNTRACK_SECMARK=y @@ -143,7 +146,6 @@ CONFIG_NETFILTER_XT_MATCH_MARK=y CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y CONFIG_NETFILTER_XT_MATCH_POLICY=y CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y -CONFIG_NETFILTER_XT_MATCH_QTAGUID=y CONFIG_NETFILTER_XT_MATCH_QUOTA=y CONFIG_NETFILTER_XT_MATCH_QUOTA2=y CONFIG_NETFILTER_XT_MATCH_SOCKET=y @@ -222,7 +224,6 @@ CONFIG_ZRAM=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 -CONFIG_UID_STAT=y CONFIG_QSEECOM=y CONFIG_HDCP_QSEECOM=y CONFIG_SCSI=y @@ -383,6 +384,7 @@ CONFIG_LOGO=y CONFIG_SOUND=y CONFIG_SND=y CONFIG_SND_USB_AUDIO=y +CONFIG_SND_USB_AUDIO_QMI=y CONFIG_SND_SOC=y CONFIG_SND_SOC_MSMCOBALT=y CONFIG_UHID=y @@ -540,7 +542,6 @@ CONFIG_CPU_FREQ_SWITCH_PROFILER=y CONFIG_DEBUG_SET_MODULE_RONX=y CONFIG_DEBUG_RODATA=y CONFIG_DEBUG_ALIGN_RODATA=y -CONFIG_KEYS=y CONFIG_PFK=y CONFIG_SECURITY=y CONFIG_SECURITY_SELINUX=y diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig index 0b275d433fc3..63f8969399f2 100644 --- a/arch/arm64/configs/msmcortex_defconfig +++ b/arch/arm64/configs/msmcortex_defconfig @@ -34,6 +34,9 @@ CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y CONFIG_MODULE_FORCE_UNLOAD=y CONFIG_MODVERSIONS=y +CONFIG_MODULE_SIG=y +CONFIG_MODULE_SIG_FORCE=y +CONFIG_MODULE_SIG_SHA512=y # CONFIG_BLK_DEV_BSG is not set CONFIG_PARTITION_ADVANCED=y # CONFIG_IOSCHED_DEADLINE is not set @@ -86,7 +89,6 @@ CONFIG_IP_PNP_DHCP=y CONFIG_INET_AH=y CONFIG_INET_ESP=y CONFIG_INET_IPCOMP=y -# CONFIG_INET_LRO is not set CONFIG_IPV6_ROUTER_PREF=y CONFIG_IPV6_ROUTE_INFO=y CONFIG_IPV6_OPTIMISTIC_DAD=y @@ -96,6 +98,7 @@ CONFIG_INET6_IPCOMP=y CONFIG_IPV6_MIP6=y CONFIG_IPV6_MULTIPLE_TABLES=y CONFIG_IPV6_SUBTREES=y +# CONFIG_NET_ACTIVITY_STATS is not set CONFIG_NETFILTER=y CONFIG_NF_CONNTRACK=y CONFIG_NF_CONNTRACK_SECMARK=y @@ -144,7 +147,6 @@ CONFIG_NETFILTER_XT_MATCH_MARK=y CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y CONFIG_NETFILTER_XT_MATCH_POLICY=y CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y -CONFIG_NETFILTER_XT_MATCH_QTAGUID=y CONFIG_NETFILTER_XT_MATCH_QUOTA=y CONFIG_NETFILTER_XT_MATCH_QUOTA2=y CONFIG_NETFILTER_XT_MATCH_SOCKET=y @@ -221,12 +223,10 @@ CONFIG_IPC_ROUTER=y CONFIG_IPC_ROUTER_SECURITY=y CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y CONFIG_DMA_CMA=y -CONFIG_CMA_SIZE_MBYTES=40 CONFIG_ZRAM=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 -CONFIG_UID_STAT=y CONFIG_QSEECOM=y CONFIG_HDCP_QSEECOM=y CONFIG_UID_CPUTIME=y @@ -389,6 +389,7 @@ CONFIG_LOGO=y CONFIG_SOUND=y CONFIG_SND=y CONFIG_SND_USB_AUDIO=y +CONFIG_SND_USB_AUDIO_QMI=y CONFIG_SND_SOC=y CONFIG_SND_SOC_MSMCOBALT=y CONFIG_UHID=y @@ -625,7 +626,6 @@ CONFIG_CORESIGHT_TPDA=y CONFIG_CORESIGHT_TPDM=y CONFIG_CORESIGHT_QPDI=y CONFIG_CORESIGHT_SOURCE_DUMMY=y -CONFIG_KEYS=y CONFIG_PFK=y CONFIG_SECURITY=y CONFIG_SECURITY_SELINUX=y diff --git a/arch/arm64/include/asm/topology.h b/arch/arm64/include/asm/topology.h index a3e9d6fdbf21..51b1c57c4443 100644 --- a/arch/arm64/include/asm/topology.h +++ b/arch/arm64/include/asm/topology.h @@ -21,6 +21,7 @@ extern struct cpu_topology cpu_topology[NR_CPUS]; void init_cpu_topology(void); void store_cpu_topology(unsigned int cpuid); const struct cpumask *cpu_coregroup_mask(int cpu); +unsigned long arch_get_cpu_efficiency(int cpu); #include <asm-generic/topology.h> diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c index 67fc833a26af..349b131baec3 100644 --- a/arch/arm64/kernel/topology.c +++ b/arch/arm64/kernel/topology.c @@ -215,6 +215,7 @@ unsigned long arch_get_cpu_efficiency(int cpu) { return per_cpu(cpu_efficiency, cpu); } +EXPORT_SYMBOL(arch_get_cpu_efficiency); /* * Iterate all CPUs' descriptor in DT and compute the efficiency diff --git a/arch/um/configs/x86_64_um_defconfig b/arch/um/configs/x86_64_um_defconfig index 078e6d981acd..487b68bad250 100644 --- a/arch/um/configs/x86_64_um_defconfig +++ b/arch/um/configs/x86_64_um_defconfig @@ -655,7 +655,7 @@ CONFIG_IPV6_MULTIPLE_TABLES=y # CONFIG_IPV6_SUBTREES is not set # CONFIG_IPV6_MROUTE is not set # CONFIG_ANDROID_PARANOID_NETWORK is not set -CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NET_ACTIVITY_STATS is not set # CONFIG_NETWORK_SECMARK is not set # CONFIG_NET_PTP_CLASSIFY is not set # CONFIG_NETWORK_PHY_TIMESTAMPING is not set diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index f16fa6cc23bc..8d456e3e42a0 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -667,6 +667,7 @@ void regmap_debugfs_init(struct regmap *map, const char *name) debugfs_create_x32("address", 0600, map->debugfs, &map->dump_address); + map->dump_count = 1; debugfs_create_u32("count", 0600, map->debugfs, &map->dump_count); debugfs_create_file("data", registers_mode, map->debugfs, diff --git a/drivers/clk/msm/clock-alpha-pll.c b/drivers/clk/msm/clock-alpha-pll.c index 5c8c45fe4ff6..b9a1167a790d 100644 --- a/drivers/clk/msm/clock-alpha-pll.c +++ b/drivers/clk/msm/clock-alpha-pll.c @@ -1040,7 +1040,7 @@ static enum handoff fabia_alpha_pll_handoff(struct clk *c) if (!is_locked(pll)) { if (c->rate && fabia_alpha_pll_set_rate(c, c->rate)) WARN(1, "%s: Failed to configure rate\n", c->dbg_name); - __init_alpha_pll(c); + __init_fabia_alpha_pll(c); return HANDOFF_DISABLED_CLK; } else if (pll->fsm_en_mask && !is_fsm_mode(MODE_REG(pll))) { WARN(1, "%s should be in FSM mode but is not\n", c->dbg_name); diff --git a/drivers/clk/msm/clock-gpu-cobalt.c b/drivers/clk/msm/clock-gpu-cobalt.c index 63870aaa487b..ce3e7916e658 100644 --- a/drivers/clk/msm/clock-gpu-cobalt.c +++ b/drivers/clk/msm/clock-gpu-cobalt.c @@ -285,9 +285,10 @@ static struct rcg_clk rbbmtimer_clk_src = { }; static struct clk_freq_tbl ftbl_gfx3d_isense_clk_src[] = { - F( 40000000, gpucc_gpll0, 15, 0, 0), - F( 200000000, gpucc_gpll0, 3, 0, 0), - F( 300000000, gpucc_gpll0, 2, 0, 0), + F( 19200000, gpucc_cxo_clk, 1, 0, 0), + F( 40000000, gpucc_gpll0, 15, 0, 0), + F( 200000000, gpucc_gpll0, 3, 0, 0), + F( 300000000, gpucc_gpll0, 2, 0, 0), F_END }; diff --git a/drivers/clk/msm/clock-local2.c b/drivers/clk/msm/clock-local2.c index 55fa76046def..6cf53c78d4d6 100644 --- a/drivers/clk/msm/clock-local2.c +++ b/drivers/clk/msm/clock-local2.c @@ -21,6 +21,7 @@ #include <linux/io.h> #include <linux/spinlock.h> #include <linux/delay.h> +#include <linux/rational.h> #include <linux/clk.h> #include <linux/clk/msm-clk-provider.h> #include <linux/clk/msm-clk.h> @@ -1735,6 +1736,46 @@ static struct clk *edp_clk_get_parent(struct clk *c) return freq->src_clk; } +static int rcg_clk_set_rate_dp(struct clk *clk, unsigned long rate) +{ + struct rcg_clk *rcg = to_rcg_clk(clk); + struct clk_freq_tbl *freq_tbl = rcg->current_freq; + unsigned long src_rate; + unsigned long num, den, flags; + + src_rate = clk_get_rate(clk->parent); + if (src_rate <= 0) { + pr_err("Invalid RCG parent rate\n"); + return -EINVAL; + } + + rational_best_approximation(src_rate, rate, + (unsigned long)(1 << 16) - 1, + (unsigned long)(1 << 16) - 1, &den, &num); + + if (!num || !den) { + pr_err("Invalid MN values derived for requested rate %lu\n", + rate); + return -EINVAL; + } + + freq_tbl->div_src_val &= ~BM(4, 0); + if (num == den) { + freq_tbl->m_val = 0; + freq_tbl->n_val = 0; + } else { + freq_tbl->m_val = num; + freq_tbl->n_val = ~(den - num); + freq_tbl->d_val = ~den; + } + + spin_lock_irqsave(&local_clock_reg_lock, flags); + if (!is_same_rcg_config(rcg, freq_tbl, true)) + __set_rate_mnd(rcg, freq_tbl); + spin_unlock_irqrestore(&local_clock_reg_lock, flags); + return 0; +} + static int gate_clk_enable(struct clk *c) { unsigned long flags; @@ -2291,6 +2332,15 @@ struct clk_ops clk_ops_rcg_edp = { .list_registers = rcg_hid_clk_list_registers, }; +struct clk_ops clk_ops_rcg_dp = { + .enable = rcg_clk_enable, + .disable = rcg_clk_disable, + .set_rate = rcg_clk_set_rate_dp, + .list_rate = rcg_clk_list_rate, + .handoff = pixel_rcg_handoff, + .list_registers = rcg_mnd_clk_list_registers, +}; + struct clk_ops clk_ops_branch = { .enable = branch_clk_enable, .prepare = branch_clk_prepare, diff --git a/drivers/clk/msm/clock-mmss-cobalt.c b/drivers/clk/msm/clock-mmss-cobalt.c index 47466ad04311..53c657ef3a25 100644 --- a/drivers/clk/msm/clock-mmss-cobalt.c +++ b/drivers/clk/msm/clock-mmss-cobalt.c @@ -46,7 +46,9 @@ static void __iomem *virt_base; #define mmpll10_pll_out_mm_source_val 4 #define dsi0phypll_mm_source_val 1 #define dsi1phypll_mm_source_val 2 -#define hdmiphypll_mm_source_val 1 +#define hdmiphypll_mm_source_val 1 +#define ext_dp_phy_pll_link_mm_source_val 1 +#define ext_dp_phy_pll_vco_mm_source_val 2 #define FIXDIV(div) (div ? (2 * (div) - 1) : (0)) @@ -61,9 +63,23 @@ static void __iomem *virt_base; | BVAL(10, 8, s##_mm_source_val), \ } +#define F_SLEW(f, s_f, s, div, m, n) \ + { \ + .freq_hz = (f), \ + .src_freq = (s_f), \ + .src_clk = &s.c, \ + .m_val = (m), \ + .n_val = ~((n)-(m)) * !!(n), \ + .d_val = ~(n),\ + .div_src_val = BVAL(4, 0, (int)(2*(div) - 1)) \ + | BVAL(10, 8, s##_mm_source_val), \ + } + DEFINE_EXT_CLK(mmsscc_xo, NULL); DEFINE_EXT_CLK(mmsscc_gpll0, NULL); DEFINE_EXT_CLK(mmsscc_gpll0_div, NULL); +DEFINE_EXT_CLK(ext_dp_phy_pll_vco, NULL); +DEFINE_EXT_CLK(ext_dp_phy_pll_link, NULL); static DEFINE_VDD_REGULATORS(vdd_dig, VDD_DIG_NUM, 1, vdd_corner, NULL); @@ -1091,6 +1107,80 @@ static struct rcg_clk dp_aux_clk_src = { }, }; +static struct clk_freq_tbl ftbl_dp_pixel_clk_src[] = { + { + .div_src_val = BVAL(10, 8, ext_dp_phy_pll_vco_mm_source_val), + .src_clk = &ext_dp_phy_pll_vco.c, + }, + F_END +}; + +static struct rcg_clk dp_pixel_clk_src = { + .cmd_rcgr_reg = MMSS_DP_PIXEL_CMD_RCGR, + .set_rate = set_rate_mnd, + .current_freq = ftbl_dp_pixel_clk_src, + .base = &virt_base, + .c = { + .dbg_name = "dp_pixel_clk_src", + .parent = &ext_dp_phy_pll_vco.c, + .ops = &clk_ops_rcg_dp, + VDD_DIG_FMAX_MAP3(LOWER, 148380000, LOW, 296740000, + NOMINAL, 593470000), + CLK_INIT(dp_pixel_clk_src.c), + }, +}; + +static struct clk_freq_tbl ftbl_dp_link_clk_src[] = { + F_SLEW( 162000000, 324000000, ext_dp_phy_pll_link, 2, 0, 0), + F_SLEW( 270000000, 540000000, ext_dp_phy_pll_link, 2, 0, 0), + F_SLEW( 540000000, 1080000000, ext_dp_phy_pll_link, 2, 0, 0), + F_END +}; + +static struct rcg_clk dp_link_clk_src = { + .cmd_rcgr_reg = MMSS_DP_LINK_CMD_RCGR, + .set_rate = set_rate_hid, + .freq_tbl = ftbl_dp_link_clk_src, + .current_freq = ftbl_dp_link_clk_src, + .base = &virt_base, + .c = { + .dbg_name = "dp_link_clk_src", + .ops = &clk_ops_rcg, + .flags = CLKFLAG_NO_RATE_CACHE, + VDD_DIG_FMAX_MAP3(LOWER, 162000000, LOW, 270000000, + NOMINAL, 540000000), + CLK_INIT(dp_link_clk_src.c), + }, +}; + +/* + * Current understanding is that the DP PLL is going to be configured by using + * the set_rate ops for the dp_link_clk_src and dp_pixel_clk_src. When set_rate + * is called on this RCG, the rate call never makes it to the external DP + * clocks. + */ +static struct clk_freq_tbl ftbl_dp_crypto_clk_src[] = { + F_MM( 101250000, ext_dp_phy_pll_link, 1, 5, 16), + F_MM( 168750000, ext_dp_phy_pll_link, 1, 5, 16), + F_MM( 337500000, ext_dp_phy_pll_link, 1, 5, 16), + F_END +}; + +static struct rcg_clk dp_crypto_clk_src = { + .cmd_rcgr_reg = MMSS_DP_CRYPTO_CMD_RCGR, + .set_rate = set_rate_mnd, + .freq_tbl = ftbl_dp_crypto_clk_src, + .current_freq = ftbl_dp_crypto_clk_src, + .base = &virt_base, + .c = { + .dbg_name = "dp_crypto_clk_src", + .ops = &clk_ops_rcg_mnd, + VDD_DIG_FMAX_MAP3(LOWER, 101250000, LOW, 168750000, + NOMINAL, 337500000), + CLK_INIT(dp_crypto_clk_src.c), + }, +}; + static struct branch_clk mmss_bimc_smmu_ahb_clk = { .cbcr_reg = MMSS_BIMC_SMMU_AHB_CBCR, .has_sibling = 1, @@ -1936,6 +2026,56 @@ static struct branch_clk mmss_mdss_dp_aux_clk = { }, }; +static struct branch_clk mmss_mdss_dp_pixel_clk = { + .cbcr_reg = MMSS_MDSS_DP_PIXEL_CBCR, + .has_sibling = 0, + .base = &virt_base, + .c = { + .dbg_name = "mmss_mdss_dp_pixel_clk", + .parent = &dp_pixel_clk_src.c, + .ops = &clk_ops_branch, + CLK_INIT(mmss_mdss_dp_pixel_clk.c), + }, +}; + +static struct branch_clk mmss_mdss_dp_link_clk = { + .cbcr_reg = MMSS_MDSS_DP_LINK_CBCR, + .has_sibling = 0, + .base = &virt_base, + .c = { + .dbg_name = "mmss_mdss_dp_link_clk", + .parent = &dp_link_clk_src.c, + .flags = CLKFLAG_NO_RATE_CACHE, + .ops = &clk_ops_branch, + CLK_INIT(mmss_mdss_dp_link_clk.c), + }, +}; + +/* Reset state of MMSS_MDSS_DP_LINK_INTF_DIV is 0x3 (div-4) */ +static struct branch_clk mmss_mdss_dp_link_intf_clk = { + .cbcr_reg = MMSS_MDSS_DP_LINK_INTF_CBCR, + .has_sibling = 1, + .base = &virt_base, + .c = { + .dbg_name = "mmss_mdss_dp_link_intf_clk", + .parent = &dp_link_clk_src.c, + .ops = &clk_ops_branch, + CLK_INIT(mmss_mdss_dp_link_intf_clk.c), + }, +}; + +static struct branch_clk mmss_mdss_dp_crypto_clk = { + .cbcr_reg = MMSS_MDSS_DP_CRYPTO_CBCR, + .has_sibling = 0, + .base = &virt_base, + .c = { + .dbg_name = "mmss_mdss_dp_crypto_clk", + .parent = &dp_crypto_clk_src.c, + .ops = &clk_ops_branch, + CLK_INIT(mmss_mdss_dp_crypto_clk.c), + }, +}; + static struct branch_clk mmss_mdss_dp_gtc_clk = { .cbcr_reg = MMSS_MDSS_DP_GTC_CBCR, .has_sibling = 0, @@ -2313,10 +2453,14 @@ static struct mux_clk mmss_debug_mux = { { &mmss_fd_core_clk.c, 0x0089 }, { &mmss_fd_core_uar_clk.c, 0x008a }, { &mmss_fd_ahb_clk.c, 0x008c }, - { &mmss_camss_cphy_csid0_clk.c, 0x008d}, - { &mmss_camss_cphy_csid1_clk.c, 0x008e}, - { &mmss_camss_cphy_csid2_clk.c, 0x008f}, - { &mmss_camss_cphy_csid3_clk.c, 0x0090}, + { &mmss_camss_cphy_csid0_clk.c, 0x008d }, + { &mmss_camss_cphy_csid1_clk.c, 0x008e }, + { &mmss_camss_cphy_csid2_clk.c, 0x008f }, + { &mmss_camss_cphy_csid3_clk.c, 0x0090 }, + { &mmss_mdss_dp_link_clk.c, 0x0098 }, + { &mmss_mdss_dp_link_intf_clk.c, 0x0099 }, + { &mmss_mdss_dp_crypto_clk.c, 0x009a }, + { &mmss_mdss_dp_pixel_clk.c, 0x009b }, { &mmss_mdss_dp_aux_clk.c, 0x009c }, { &mmss_mdss_dp_gtc_clk.c, 0x009d }, { &mmss_mdss_byte0_intf_clk.c, 0x00ad }, @@ -2384,6 +2528,11 @@ static struct clk_lookup msm_clocks_mmss_cobalt[] = { CLK_LIST(pclk1_clk_src), CLK_LIST(ext_extpclk_clk_src), CLK_LIST(extpclk_clk_src), + CLK_LIST(ext_dp_phy_pll_vco), + CLK_LIST(ext_dp_phy_pll_link), + CLK_LIST(dp_pixel_clk_src), + CLK_LIST(dp_link_clk_src), + CLK_LIST(dp_crypto_clk_src), CLK_LIST(csi0phytimer_clk_src), CLK_LIST(csi1phytimer_clk_src), CLK_LIST(csi2phytimer_clk_src), @@ -2463,6 +2612,10 @@ static struct clk_lookup msm_clocks_mmss_cobalt[] = { CLK_LIST(mmss_mdss_byte0_intf_div_clk), CLK_LIST(mmss_mdss_byte1_intf_clk), CLK_LIST(mmss_mdss_dp_aux_clk), + CLK_LIST(mmss_mdss_dp_crypto_clk), + CLK_LIST(mmss_mdss_dp_pixel_clk), + CLK_LIST(mmss_mdss_dp_link_clk), + CLK_LIST(mmss_mdss_dp_link_intf_clk), CLK_LIST(mmss_mdss_dp_gtc_clk), CLK_LIST(mmss_mdss_esc0_clk), CLK_LIST(mmss_mdss_esc1_clk), @@ -2646,6 +2799,11 @@ int msm_mmsscc_cobalt_probe(struct platform_device *pdev) ext_extpclk_clk_src.dev = &pdev->dev; ext_extpclk_clk_src.clk_id = "extpclk_src"; + ext_dp_phy_pll_link.dev = &pdev->dev; + ext_dp_phy_pll_link.clk_id = "dp_link_src"; + ext_dp_phy_pll_vco.dev = &pdev->dev; + ext_dp_phy_pll_vco.clk_id = "dp_vco_div"; + is_vq = of_device_is_compatible(pdev->dev.of_node, "qcom,mmsscc-hamster"); if (is_vq) diff --git a/drivers/clk/msm/mdss/mdss-dsi-pll-cobalt.c b/drivers/clk/msm/mdss/mdss-dsi-pll-cobalt.c index e6153553e48a..1751f49b798c 100644 --- a/drivers/clk/msm/mdss/mdss-dsi-pll-cobalt.c +++ b/drivers/clk/msm/mdss/mdss-dsi-pll-cobalt.c @@ -509,7 +509,7 @@ static unsigned long dsi_pll_get_vco_rate(struct clk *c) 16); /* OUTDIV_1:0 field is (log(outdiv, 2)) */ - outdiv = MDSS_PLL_REG_R(pll->pll_base, PLL_OUTDIV); + outdiv = MDSS_PLL_REG_R(pll->pll_base, PLL_PLL_OUTDIV_RATE); outdiv &= 0x3; outdiv = 1 << outdiv; @@ -521,12 +521,12 @@ static unsigned long dsi_pll_get_vco_rate(struct clk *c) multiplier = 1 << 18; pll_freq = dec * (ref_clk * 2); tmp64 = (ref_clk * 2 * frac); - pll_freq += do_div(tmp64, multiplier); + pll_freq += div_u64(tmp64, multiplier); - vco_rate = do_div(pll_freq, outdiv); + vco_rate = div_u64(pll_freq, outdiv); - pr_debug("dec=0x%x\n, frac=0x%x, outdiv=%d, vco=%lu\n", - dec, frac, outdiv, (unsigned long)vco_rate); + pr_debug("dec=0x%x, frac=0x%x, outdiv=%d, vco=%llu\n", + dec, frac, outdiv, vco_rate); (void)mdss_pll_resource_enable(pll, false); diff --git a/drivers/gpu/msm/kgsl_pool.c b/drivers/gpu/msm/kgsl_pool.c index 48b7b0872425..7fb3b37ac191 100644 --- a/drivers/gpu/msm/kgsl_pool.c +++ b/drivers/gpu/msm/kgsl_pool.c @@ -298,17 +298,18 @@ int kgsl_pool_alloc_page(int *page_size, struct page **pages, return -EINVAL; pool = _kgsl_get_pool_from_order(order); - pool_idx = kgsl_pool_idx_lookup(order); + if (pool == NULL) + return -EINVAL; - if (pool != NULL) - page = _kgsl_pool_get_page(pool); + pool_idx = kgsl_pool_idx_lookup(order); + page = _kgsl_pool_get_page(pool); /* Allocate a new page if not allocated from pool */ if (page == NULL) { gfp_t gfp_mask = kgsl_gfp_mask(order); /* Only allocate non-reserved memory for certain pools */ - if (!pool->allocation_allowed) { + if (!pool->allocation_allowed && pool_idx > 0) { *page_size = PAGE_SIZE << kgsl_pools[pool_idx-1].pool_order; *align = ilog2(*page_size); diff --git a/drivers/iommu/io-pgtable-fast.c b/drivers/iommu/io-pgtable-fast.c index 44307f67fd22..9a25ebfdc778 100644 --- a/drivers/iommu/io-pgtable-fast.c +++ b/drivers/iommu/io-pgtable-fast.c @@ -420,6 +420,9 @@ av8l_fast_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie) struct av8l_fast_io_pgtable *data = av8l_fast_alloc_pgtable_data(cfg); + if (!data) + return NULL; + /* restrict according to the fast map requirements */ cfg->ias = 32; cfg->pgsize_bitmap = SZ_4K; diff --git a/drivers/iommu/iommu-debug.c b/drivers/iommu/iommu-debug.c index a97c082d18d8..c78178c0e1a1 100644 --- a/drivers/iommu/iommu-debug.c +++ b/drivers/iommu/iommu-debug.c @@ -1379,7 +1379,7 @@ static int __apply_to_new_mapping(struct seq_file *s, struct dma_iommu_mapping *mapping; struct iommu_debug_device *ddev = s->private; struct device *dev = ddev->dev; - int ret, fast = 1; + int ret = -EINVAL, fast = 1; phys_addr_t pt_phys; mapping = arm_iommu_create_mapping(&platform_bus_type, 0, SZ_1G * 4ULL); @@ -1688,7 +1688,7 @@ static const struct file_operations iommu_debug_atos_fops = { static ssize_t iommu_debug_map_write(struct file *file, const char __user *ubuf, size_t count, loff_t *offset) { - ssize_t retval; + ssize_t retval = -EINVAL; int ret; char *comma1, *comma2, *comma3; char buf[100]; diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c index 89636557dec1..786ffa822851 100644 --- a/drivers/leds/leds-qpnp-flash-v2.c +++ b/drivers/leds/leds-qpnp-flash-v2.c @@ -16,36 +16,39 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/gpio.h> #include <linux/regmap.h> #include <linux/platform_device.h> +#include <linux/regulator/consumer.h> #include <linux/leds-qpnp-flash-v2.h> #define FLASH_LED_REG_SAFETY_TMR(base) (base + 0x40) #define FLASH_LED_REG_TGR_CURRENT(base) (base + 0x43) #define FLASH_LED_REG_MOD_CTRL(base) (base + 0x46) #define FLASH_LED_REG_IRES(base) (base + 0x47) +#define FLASH_LED_REG_STROBE_CFG(base) (base + 0x48) #define FLASH_LED_REG_STROBE_CTRL(base) (base + 0x49) -#define FLASH_LED_REG_CHANNEL_CTRL(base) (base + 0x4C) +#define FLASH_LED_EN_LED_CTRL(base) (base + 0x4C) #define FLASH_LED_REG_HDRM_PRGM(base) (base + 0x4D) #define FLASH_LED_REG_HDRM_AUTO_MODE_CTRL(base) (base + 0x50) #define FLASH_LED_REG_ISC_DELAY(base) (base + 0x52) -#define FLASH_LED_HDRM_MODE_PRGM_MASK 0xFF -#define FLASH_LED_HDRM_VOL_MASK 0xF0 -#define FLASH_LED_CURRENT_MASK 0x3F -#define FLASH_LED_STROBE_CTRL_MASK 0x07 -#define FLASH_LED_SAFETY_TMR_MASK_MASK 0x7F -#define FLASH_LED_MOD_CTRL_MASK 0x80 -#define FLASH_LED_ISC_DELAY_MASK 0x03 +#define FLASH_LED_HDRM_MODE_PRGM_MASK GENMASK(7, 0) +#define FLASH_LED_HDRM_VOL_MASK GENMASK(7, 4) +#define FLASH_LED_CURRENT_MASK GENMASK(6, 0) +#define FLASH_LED_ENABLE_MASK GENMASK(2, 0) +#define FLASH_LED_SAFETY_TMR_MASK GENMASK(7, 0) +#define FLASH_LED_ISC_DELAY_MASK GENMASK(1, 0) +#define FLASH_LED_MOD_CTRL_MASK BIT(7) +#define FLASH_LED_HW_SW_STROBE_SEL_MASK BIT(2) -#define FLASH_LED_TYPE_FLASH 0 -#define FLASH_LED_TYPE_TORCH 1 #define FLASH_LED_HEADROOM_AUTO_MODE_ENABLED true #define FLASH_LED_ISC_DELAY_SHIFT 6 #define FLASH_LED_ISC_DELAY_DEFAULT_US 3 #define FLASH_LED_SAFETY_TMR_VAL_OFFSET 1 #define FLASH_LED_SAFETY_TMR_VAL_DIVISOR 10 -#define FLASH_LED_SAFETY_TMR_ENABLED 0x08 +#define FLASH_LED_SAFETY_TMR_ENABLE BIT(7) #define FLASH_LED_IRES_BASE 3 #define FLASH_LED_IRES_DIVISOR 2500 #define FLASH_LED_IRES_MIN_UA 5000 @@ -56,16 +59,74 @@ #define FLASH_LED_HDRM_VOL_HI_LO_WIN_DEFAULT_MV 0x04 #define FLASH_LED_HDRM_VOL_BASE_MV 125 #define FLASH_LED_HDRM_VOL_STEP_MV 25 -#define FLASH_LED_STROBE_ENABLE 0x01 -#define FLASH_LED_MOD_ENABLE 0x80 +#define FLASH_LED_STROBE_CFG_DEFAULT 0x00 +#define FLASH_LED_HW_STROBE_OPTION_1 0x00 +#define FLASH_LED_HW_STROBE_OPTION_2 0x01 +#define FLASH_LED_HW_STROBE_OPTION_3 0x02 +#define FLASH_LED_ENABLE BIT(0) +#define FLASH_LED_MOD_ENABLE BIT(7) #define FLASH_LED_DISABLE 0x00 #define FLASH_LED_SAFETY_TMR_DISABLED 0x13 +#define FLASH_LED_MIN_CURRENT_MA 25 + +enum flash_led_type { + FLASH_LED_TYPE_FLASH, + FLASH_LED_TYPE_TORCH, +}; + +enum { + LED1 = 0, + LED2, + LED3, +}; + +/* + * Configurations for each individual LED + */ +struct flash_node_data { + struct platform_device *pdev; + struct led_classdev cdev; + struct pinctrl *pinctrl; + struct pinctrl_state *gpio_state_active; + struct pinctrl_state *gpio_state_suspend; + struct pinctrl_state *hw_strobe_state_active; + struct pinctrl_state *hw_strobe_state_suspend; + int hw_strobe_gpio; + int ires_ua; + int max_current; + int current_ma; + u8 duration; + u8 id; + u8 type; + u8 ires; + u8 hdrm_val; + u8 current_reg_val; + u8 trigger; + bool led_on; +}; + +struct flash_regulator_data { + struct regulator *vreg; + const char *reg_name; + u32 max_volt_uv; +}; + +struct flash_switch_data { + struct platform_device *pdev; + struct led_classdev cdev; + struct flash_regulator_data *reg_data; + int led_mask; + int num_regulators; + bool regulator_on; + bool enabled; +}; /* * Flash LED configuration read from device tree */ struct flash_led_platform_data { u8 isc_delay_us; + u8 hw_strobe_option; bool hdrm_auto_mode_en; }; @@ -79,8 +140,9 @@ struct qpnp_flash_led { struct flash_node_data *fnode; struct flash_switch_data *snode; spinlock_t lock; - int num_led_nodes; - int num_avail_leds; + int num_fnodes; + int num_snodes; + int enable; u16 base; }; @@ -91,12 +153,13 @@ qpnp_flash_led_masked_write(struct qpnp_flash_led *led, u16 addr, u8 mask, int rc; rc = regmap_update_bits(led->regmap, addr, mask, val); - if (rc) + if (rc < 0) dev_err(&led->pdev->dev, "Unable to update bits from 0x%02X, rc = %d\n", addr, rc); - - dev_dbg(&led->pdev->dev, "Write 0x%02X to addr 0x%02X\n", val, addr); + else + dev_dbg(&led->pdev->dev, "Wrote 0x%02X to addr 0x%02X\n", + val, addr); return rc; } @@ -112,13 +175,13 @@ static int qpnp_flash_led_init_settings(struct qpnp_flash_led *led) int rc, i, addr_offset; u8 val = 0; - for (i = 0; i < led->num_avail_leds; i++) { + for (i = 0; i < led->num_fnodes; i++) { addr_offset = led->fnode[i].id; rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_HDRM_PRGM(led->base + addr_offset), FLASH_LED_HDRM_MODE_PRGM_MASK, led->fnode[i].hdrm_val); - if (rc) + if (rc < 0) return rc; val |= 0x1 << led->fnode[i].id; @@ -127,133 +190,306 @@ static int qpnp_flash_led_init_settings(struct qpnp_flash_led *led) rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_HDRM_AUTO_MODE_CTRL(led->base), FLASH_LED_HDRM_MODE_PRGM_MASK, val); - if (rc) + if (rc < 0) return rc; rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_ISC_DELAY(led->base), FLASH_LED_ISC_DELAY_MASK, led->pdata->isc_delay_us); - if (rc) + if (rc < 0) return rc; return 0; } +static int qpnp_flash_led_hw_strobe_enable(struct flash_node_data *fnode, + int hw_strobe_option, bool on) +{ + int rc = 0; + + /* + * If the LED controlled by this fnode is not GPIO controlled + * for the given strobe_option, return. + */ + if (hw_strobe_option == FLASH_LED_HW_STROBE_OPTION_1) + return 0; + else if (hw_strobe_option == FLASH_LED_HW_STROBE_OPTION_2 + && fnode->id != LED3) + return 0; + else if (hw_strobe_option == FLASH_LED_HW_STROBE_OPTION_3 + && fnode->id == LED1) + return 0; + + if (gpio_is_valid(fnode->hw_strobe_gpio)) { + gpio_set_value(fnode->hw_strobe_gpio, on ? 1 : 0); + } else if (fnode->hw_strobe_state_active && + fnode->hw_strobe_state_suspend) { + rc = pinctrl_select_state(fnode->pinctrl, + on ? fnode->hw_strobe_state_active : + fnode->hw_strobe_state_suspend); + if (rc < 0) { + dev_err(&fnode->pdev->dev, + "failed to change hw strobe pin state\n"); + return rc; + } + } + + return rc; +} + +static int qpnp_flash_led_regulator_enable(struct qpnp_flash_led *led, + struct flash_switch_data *snode, bool on) +{ + int i, rc = 0; + + if (snode->regulator_on == on) + return 0; + + if (on == false) { + i = snode->num_regulators; + goto out; + } + + for (i = 0; i < snode->num_regulators; i++) { + rc = regulator_enable(snode->reg_data[i].vreg); + if (rc < 0) { + dev_err(&led->pdev->dev, + "regulator enable failed, rc=%d\n", rc); + goto out; + } + } + snode->regulator_on = true; + + return rc; + +out: + while (i--) + regulator_disable(snode->reg_data[i].vreg); + + snode->regulator_on = false; + return rc; +} + +static int qpnp_flash_led_get_max_avail_current(struct flash_switch_data *snode, + struct qpnp_flash_led *led) +{ + return 3750; +} + static void qpnp_flash_led_node_set(struct flash_node_data *fnode, int value) { - int prgm_current_ma; + int prgm_current_ma = value; + + if (value <= 0) + prgm_current_ma = 0; + else if (value < FLASH_LED_MIN_CURRENT_MA) + prgm_current_ma = FLASH_LED_MIN_CURRENT_MA; - prgm_current_ma = value < 0 ? 0 : value; - prgm_current_ma = value > fnode->cdev.max_brightness ? - fnode->cdev.max_brightness : value; + prgm_current_ma = min(prgm_current_ma, fnode->max_current); + fnode->current_ma = prgm_current_ma; fnode->cdev.brightness = prgm_current_ma; - fnode->brightness = prgm_current_ma * 1000 / fnode->ires_ua + 1; + fnode->current_reg_val = prgm_current_ma * 1000 / fnode->ires_ua + 1; fnode->led_on = prgm_current_ma != 0; } +static int qpnp_flash_led_switch_disable(struct flash_switch_data *snode) +{ + struct qpnp_flash_led *led = dev_get_drvdata(&snode->pdev->dev); + int i, rc, addr_offset; + + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_EN_LED_CTRL(led->base), + snode->led_mask, FLASH_LED_DISABLE); + if (rc < 0) + return rc; + + led->enable--; + if (led->enable == 0) { + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_MOD_CTRL(led->base), + FLASH_LED_MOD_CTRL_MASK, FLASH_LED_DISABLE); + if (rc < 0) + return rc; + } + + for (i = 0; i < led->num_fnodes; i++) { + if (!led->fnode[i].led_on || + !(snode->led_mask & BIT(led->fnode[i].id))) + continue; + + addr_offset = led->fnode[i].id; + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_TGR_CURRENT(led->base + addr_offset), + FLASH_LED_CURRENT_MASK, 0); + if (rc < 0) + return rc; + + led->fnode[i].led_on = false; + + if (led->fnode[i].pinctrl) { + rc = pinctrl_select_state(led->fnode[i].pinctrl, + led->fnode[i].gpio_state_suspend); + if (rc < 0) { + dev_err(&led->pdev->dev, + "failed to disable GPIO, rc=%d\n", rc); + return rc; + } + } + + if (led->fnode[i].trigger & FLASH_LED_HW_SW_STROBE_SEL_MASK) { + rc = qpnp_flash_led_hw_strobe_enable(&led->fnode[i], + led->pdata->hw_strobe_option, false); + if (rc < 0) { + dev_err(&led->pdev->dev, + "Unable to disable hw strobe, rc=%d\n", + rc); + return rc; + } + } + } + + qpnp_flash_led_regulator_enable(led, snode, false); + snode->enabled = false; + return 0; +} + static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on) { struct qpnp_flash_led *led = dev_get_drvdata(&snode->pdev->dev); int rc, i, addr_offset; u8 val; - if (!on) - goto leds_turn_off; + if (snode->enabled == on) { + dev_warn(&led->pdev->dev, "Switch node is already %s!\n", + on ? "enabled" : "disabled"); + return 0; + } + + if (!on) { + rc = qpnp_flash_led_switch_disable(snode); + return rc; + } + + rc = qpnp_flash_led_regulator_enable(led, snode, true); + if (rc < 0) + return rc; + /* Iterate over all leds for this switch node */ val = 0; - for (i = 0; i < led->num_led_nodes; i++) - val |= led->fnode[i].ires << (led->fnode[i].id * 2); + for (i = 0; i < led->num_fnodes; i++) + if (snode->led_mask & BIT(led->fnode[i].id)) + val |= led->fnode[i].ires << (led->fnode[i].id * 2); + rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_IRES(led->base), FLASH_LED_CURRENT_MASK, val); - if (rc) + if (rc < 0) + return rc; + + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_STROBE_CFG(led->base), + FLASH_LED_ENABLE_MASK, + led->pdata->hw_strobe_option); + if (rc < 0) return rc; val = 0; - for (i = 0; i < led->num_avail_leds; i++) { - if (!led->fnode[i].led_on) + for (i = 0; i < led->num_fnodes; i++) { + if (!led->fnode[i].led_on || + !(snode->led_mask & BIT(led->fnode[i].id))) continue; addr_offset = led->fnode[i].id; rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_STROBE_CTRL(led->base + addr_offset), - FLASH_LED_STROBE_CTRL_MASK, FLASH_LED_STROBE_ENABLE); - if (rc) + FLASH_LED_ENABLE_MASK, led->fnode[i].trigger); + if (rc < 0) return rc; rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_TGR_CURRENT(led->base + addr_offset), - FLASH_LED_CURRENT_MASK, led->fnode[i].brightness); - if (rc) + FLASH_LED_CURRENT_MASK, led->fnode[i].current_reg_val); + if (rc < 0) return rc; rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_SAFETY_TMR(led->base + addr_offset), - FLASH_LED_SAFETY_TMR_MASK_MASK, led->fnode[i].duration); - if (rc) + FLASH_LED_SAFETY_TMR_MASK, led->fnode[i].duration); + if (rc < 0) return rc; - val |= FLASH_LED_STROBE_ENABLE << led->fnode[i].id; + val |= FLASH_LED_ENABLE << led->fnode[i].id; if (led->fnode[i].pinctrl) { rc = pinctrl_select_state(led->fnode[i].pinctrl, led->fnode[i].gpio_state_active); - if (rc) { + if (rc < 0) { dev_err(&led->pdev->dev, "failed to enable GPIO\n"); return rc; } } + + if (led->fnode[i].trigger & FLASH_LED_HW_SW_STROBE_SEL_MASK) { + rc = qpnp_flash_led_hw_strobe_enable(&led->fnode[i], + led->pdata->hw_strobe_option, true); + if (rc < 0) { + dev_err(&led->pdev->dev, + "Unable to enable hw strobe\n"); + return rc; + } + } } - rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_MOD_CTRL(led->base), + if (led->enable == 0) { + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_MOD_CTRL(led->base), FLASH_LED_MOD_CTRL_MASK, FLASH_LED_MOD_ENABLE); - if (rc) - return rc; + if (rc < 0) + return rc; + } + led->enable++; rc = qpnp_flash_led_masked_write(led, - FLASH_LED_REG_CHANNEL_CTRL(led->base), - FLASH_LED_STROBE_CTRL_MASK, val); - if (rc) + FLASH_LED_EN_LED_CTRL(led->base), + snode->led_mask, val); + if (rc < 0) return rc; + snode->enabled = true; return 0; +} -leds_turn_off: - rc = qpnp_flash_led_masked_write(led, - FLASH_LED_REG_CHANNEL_CTRL(led->base), - FLASH_LED_STROBE_CTRL_MASK, FLASH_LED_DISABLE); - if (rc) - return rc; - - rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_MOD_CTRL(led->base), - FLASH_LED_MOD_CTRL_MASK, FLASH_LED_DISABLE); - if (rc) - return rc; +int qpnp_flash_led_prepare(struct led_classdev *led_cdev, int options) +{ + struct flash_switch_data *snode = + container_of(led_cdev, struct flash_switch_data, cdev); + struct qpnp_flash_led *led = dev_get_drvdata(&snode->pdev->dev); + int rc, val = 0; - for (i = 0; i < led->num_led_nodes; i++) { - if (!led->fnode[i].led_on) - continue; + if (!(options & (ENABLE_REGULATOR | QUERY_MAX_CURRENT))) { + dev_err(&led->pdev->dev, "Invalid options %d\n", options); + return -EINVAL; + } - addr_offset = led->fnode[i].id; - rc = qpnp_flash_led_masked_write(led, - FLASH_LED_REG_TGR_CURRENT(led->base + addr_offset), - FLASH_LED_CURRENT_MASK, 0); - if (rc) + if (options & ENABLE_REGULATOR) { + rc = qpnp_flash_led_regulator_enable(led, snode, true); + if (rc < 0) { + dev_err(&led->pdev->dev, + "enable regulator failed, rc=%d\n", rc); return rc; - led->fnode[i].led_on = false; + } + } - if (led->fnode[i].pinctrl) { - rc = pinctrl_select_state(led->fnode[i].pinctrl, - led->fnode[i].gpio_state_suspend); - if (rc) { - dev_err(&led->pdev->dev, - "failed to disable GPIO\n"); - return rc; - } + if (options & QUERY_MAX_CURRENT) { + val = qpnp_flash_led_get_max_avail_current(snode, led); + if (val < 0) { + dev_err(&led->pdev->dev, + "query max current failed, rc=%d\n", val); + return val; } } - return 0; + return val; } static void qpnp_flash_led_brightness_set(struct led_classdev *led_cdev, @@ -264,7 +500,7 @@ static void qpnp_flash_led_brightness_set(struct led_classdev *led_cdev, struct qpnp_flash_led *led = dev_get_drvdata(&fnode->pdev->dev); int rc; - if (!strcmp(led_cdev->name, "led:switch")) { + if (!strncmp(led_cdev->name, "led:switch", strlen("led:switch"))) { snode = container_of(led_cdev, struct flash_switch_data, cdev); led = dev_get_drvdata(&snode->pdev->dev); } else { @@ -273,53 +509,141 @@ static void qpnp_flash_led_brightness_set(struct led_classdev *led_cdev, } spin_lock(&led->lock); - if (!fnode) { + if (snode) { rc = qpnp_flash_led_switch_set(snode, value > 0); - if (rc) { + if (rc < 0) dev_err(&led->pdev->dev, "Failed to set flash LED switch\n"); - goto exit; - } - } else { + } else if (fnode) { qpnp_flash_led_node_set(fnode, value); } -exit: spin_unlock(&led->lock); } +static int qpnp_flash_led_regulator_setup(struct qpnp_flash_led *led, + struct flash_switch_data *snode, bool on) +{ + int i, rc = 0; + + if (on == false) { + i = snode->num_regulators; + goto out; + } + + for (i = 0; i < snode->num_regulators; i++) { + snode->reg_data[i].vreg = regulator_get(snode->cdev.dev, + snode->reg_data[i].reg_name); + if (IS_ERR(snode->reg_data[i].vreg)) { + rc = PTR_ERR(snode->reg_data[i].vreg); + dev_err(&led->pdev->dev, + "Failed to get regulator, rc=%d\n", rc); + goto out; + } + + if (regulator_count_voltages(snode->reg_data[i].vreg) > 0) { + rc = regulator_set_voltage(snode->reg_data[i].vreg, + snode->reg_data[i].max_volt_uv, + snode->reg_data[i].max_volt_uv); + if (rc < 0) { + dev_err(&led->pdev->dev, + "regulator set voltage failed, rc=%d\n", + rc); + regulator_put(snode->reg_data[i].vreg); + goto out; + } + } + } + + return rc; + +out: + while (i--) { + if (regulator_count_voltages(snode->reg_data[i].vreg) > 0) + regulator_set_voltage(snode->reg_data[i].vreg, 0, + snode->reg_data[i].max_volt_uv); + + regulator_put(snode->reg_data[i].vreg); + } + + return rc; +} + +static int qpnp_flash_led_regulator_parse_dt(struct qpnp_flash_led *led, + struct flash_switch_data *snode, + struct device_node *node) { + + int i = 0, rc = 0, num_regs = 0; + struct device_node *temp = NULL; + const char *temp_string; + u32 val; + + while ((temp = of_get_next_available_child(node, temp))) { + if (of_find_property(temp, "regulator-name", NULL)) + num_regs++; + } + snode->num_regulators = num_regs; + + if (snode->num_regulators == 0) + return 0; + + snode->reg_data = devm_kcalloc(&led->pdev->dev, snode->num_regulators, + sizeof(*snode->reg_data), + GFP_KERNEL); + if (!snode->reg_data) + return -ENOMEM; + + for_each_available_child_of_node(node, temp) { + rc = of_property_read_string(temp, "regulator-name", + &temp_string); + if (!rc) + snode->reg_data[i].reg_name = temp_string; + else { + dev_err(&led->pdev->dev, + "Unable to read regulator name, rc=%d\n", rc); + return rc; + } + + rc = of_property_read_u32(temp, "max-voltage-uv", &val); + if (!rc) { + snode->reg_data[i].max_volt_uv = val; + } else if (rc != -EINVAL) { + dev_err(&led->pdev->dev, + "Unable to read max voltage, rc=%d\n", rc); + return rc; + } + + i++; + } + + return 0; +} + static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led, struct flash_node_data *fnode, struct device_node *node) { const char *temp_string; int rc; u32 val; + bool strobe_sel = 0, edge_trigger = 0, active_high = 0; fnode->pdev = led->pdev; fnode->cdev.brightness_set = qpnp_flash_led_brightness_set; fnode->cdev.brightness_get = qpnp_flash_led_brightness_get; rc = of_property_read_string(node, "qcom,led-name", &fnode->cdev.name); - if (rc) { + if (rc < 0) { dev_err(&led->pdev->dev, "Unable to read flash LED names\n"); return rc; } - rc = of_property_read_u32(node, "qcom,max-current", &val); - if (!rc) { - fnode->cdev.max_brightness = val; - } else { - dev_err(&led->pdev->dev, "Unable to read max current\n"); - return rc; - } - rc = of_property_read_string(node, "label", &temp_string); if (!rc) { - if (!strcmp(temp_string, "flash")) + if (!strcmp(temp_string, "flash")) { fnode->type = FLASH_LED_TYPE_FLASH; - else if (!strcmp(temp_string, "torch")) + } else if (!strcmp(temp_string, "torch")) { fnode->type = FLASH_LED_TYPE_TORCH; - else { + } else { dev_err(&led->pdev->dev, "Wrong flash LED type\n"); return rc; } @@ -338,7 +662,7 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led, rc = of_property_read_string(node, "qcom,default-led-trigger", &fnode->cdev.default_trigger); - if (rc) { + if (rc < 0) { dev_err(&led->pdev->dev, "Unable to read trigger name\n"); return rc; } @@ -355,13 +679,43 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led, return rc; } + rc = of_property_read_u32(node, "qcom,max-current", &val); + if (!rc) { + if (val < FLASH_LED_MIN_CURRENT_MA) + val = FLASH_LED_MIN_CURRENT_MA; + fnode->max_current = val; + fnode->cdev.max_brightness = val; + } else { + dev_err(&led->pdev->dev, + "Unable to read max current, rc=%d\n", rc); + return rc; + } + + rc = of_property_read_u32(node, "qcom,current-ma", &val); + if (!rc) { + if (val < FLASH_LED_MIN_CURRENT_MA || + val > fnode->max_current) + dev_warn(&led->pdev->dev, + "Invalid operational current specified, capping it\n"); + if (val < FLASH_LED_MIN_CURRENT_MA) + val = FLASH_LED_MIN_CURRENT_MA; + if (val > fnode->max_current) + val = fnode->max_current; + fnode->current_ma = val; + fnode->cdev.brightness = val; + } else if (rc != -EINVAL) { + dev_err(&led->pdev->dev, + "Unable to read operational current, rc=%d\n", rc); + return rc; + } + fnode->duration = FLASH_LED_SAFETY_TMR_DISABLED; rc = of_property_read_u32(node, "qcom,duration-ms", &val); if (!rc) { fnode->duration = (u8)(((val - FLASH_LED_SAFETY_TMR_VAL_OFFSET) / FLASH_LED_SAFETY_TMR_VAL_DIVISOR) | - FLASH_LED_SAFETY_TMR_ENABLED); + FLASH_LED_SAFETY_TMR_ENABLE); } else if (rc == -EINVAL) { if (fnode->type == FLASH_LED_TYPE_FLASH) { dev_err(&led->pdev->dev, @@ -398,17 +752,62 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led, return rc; } + strobe_sel = of_property_read_bool(node, "qcom,hw-strobe-sel"); + if (strobe_sel) { + edge_trigger = of_property_read_bool(node, + "qcom,hw-strobe-edge-trigger"); + active_high = !of_property_read_bool(node, + "qcom,hw-strobe-active-low"); + } + fnode->trigger = (strobe_sel << 2) | (edge_trigger << 1) | active_high; + + if (fnode->trigger & FLASH_LED_HW_SW_STROBE_SEL_MASK) { + if (of_find_property(node, "qcom,hw-strobe-gpio", NULL)) { + fnode->hw_strobe_gpio = of_get_named_gpio(node, + "qcom,hw-strobe-gpio", 0); + if (fnode->hw_strobe_gpio < 0) { + dev_err(&led->pdev->dev, + "Invalid gpio specified\n"); + return fnode->hw_strobe_gpio; + } + gpio_direction_output(fnode->hw_strobe_gpio, 0); + } else { + fnode->hw_strobe_gpio = -1; + fnode->hw_strobe_state_active = + pinctrl_lookup_state(fnode->pinctrl, + "strobe_enable"); + if (IS_ERR_OR_NULL(fnode->hw_strobe_state_active)) { + dev_err(&led->pdev->dev, + "No active pin for hardware strobe, rc=%ld\n", + PTR_ERR(fnode->hw_strobe_state_active)); + fnode->hw_strobe_state_active = NULL; + } + + fnode->hw_strobe_state_suspend = + pinctrl_lookup_state(fnode->pinctrl, + "strobe_disable"); + if (IS_ERR_OR_NULL(fnode->hw_strobe_state_suspend)) { + dev_err(&led->pdev->dev, + "No suspend pin for hardware strobe, rc=%ld\n", + PTR_ERR(fnode->hw_strobe_state_suspend) + ); + fnode->hw_strobe_state_suspend = NULL; + } + } + } + rc = led_classdev_register(&led->pdev->dev, &fnode->cdev); - if (rc) { + if (rc < 0) { dev_err(&led->pdev->dev, "Unable to register led node %d\n", fnode->id); return rc; } + fnode->cdev.dev->of_node = node; fnode->pinctrl = devm_pinctrl_get(fnode->cdev.dev); if (IS_ERR_OR_NULL(fnode->pinctrl)) { - dev_err(&led->pdev->dev, "No pinctrl defined\n"); + dev_warn(&led->pdev->dev, "No pinctrl defined\n"); fnode->pinctrl = NULL; } else { fnode->gpio_state_active = @@ -436,34 +835,64 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led, } static int qpnp_flash_led_parse_and_register_switch(struct qpnp_flash_led *led, + struct flash_switch_data *snode, struct device_node *node) { - int rc; + int rc = 0; - rc = of_property_read_string(node, "qcom,led-name", - &led->snode->cdev.name); - if (rc) { - dev_err(&led->pdev->dev, "Failed to read switch node name\n"); + rc = of_property_read_string(node, "qcom,led-name", &snode->cdev.name); + if (rc < 0) { + dev_err(&led->pdev->dev, + "Failed to read switch node name, rc=%d\n", rc); return rc; } rc = of_property_read_string(node, "qcom,default-led-trigger", - &led->snode->cdev.default_trigger); - if (rc) { - dev_err(&led->pdev->dev, "Unable to read trigger name\n"); + &snode->cdev.default_trigger); + if (rc < 0) { + dev_err(&led->pdev->dev, + "Unable to read trigger name, rc=%d\n", rc); + return rc; + } + + rc = of_property_read_u32(node, "qcom,led-mask", &snode->led_mask); + if (rc < 0) { + dev_err(&led->pdev->dev, "Unable to read led mask rc=%d\n", rc); + return rc; + } + + if (snode->led_mask < 1 || snode->led_mask > 7) { + dev_err(&led->pdev->dev, "Invalid value for led-mask\n"); + return -EINVAL; + } + + rc = qpnp_flash_led_regulator_parse_dt(led, snode, node); + if (rc < 0) { + dev_err(&led->pdev->dev, + "Unable to parse regulator data, rc=%d\n", rc); return rc; } - led->snode->pdev = led->pdev; - led->snode->cdev.brightness_set = qpnp_flash_led_brightness_set; - led->snode->cdev.brightness_get = qpnp_flash_led_brightness_get; - rc = led_classdev_register(&led->pdev->dev, &led->snode->cdev); - if (rc) { + if (snode->num_regulators) { + rc = qpnp_flash_led_regulator_setup(led, snode, true); + if (rc < 0) { + dev_err(&led->pdev->dev, + "Unable to setup regulator, rc=%d\n", rc); + return rc; + } + } + + snode->pdev = led->pdev; + snode->cdev.brightness_set = qpnp_flash_led_brightness_set; + snode->cdev.brightness_get = qpnp_flash_led_brightness_get; + rc = led_classdev_register(&led->pdev->dev, &snode->cdev); + if (rc < 0) { dev_err(&led->pdev->dev, "Unable to register led switch node\n"); return rc; } + snode->cdev.dev->of_node = node; return 0; } @@ -486,6 +915,14 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led, return rc; } + rc = of_property_read_u32(node, "qcom,hw-strobe-option", &val); + if (!rc) { + led->pdata->hw_strobe_option = (u8)val; + } else if (rc != -EINVAL) { + dev_err(&led->pdev->dev, "Unable to parse hw strobe option\n"); + return rc; + } + return 0; } @@ -493,6 +930,7 @@ static int qpnp_flash_led_probe(struct platform_device *pdev) { struct qpnp_flash_led *led; struct device_node *node, *temp; + const char *temp_string; unsigned int base; int rc, i = 0; @@ -503,7 +941,7 @@ static int qpnp_flash_led_probe(struct platform_device *pdev) } rc = of_property_read_u32(node, "reg", &base); - if (rc) { + if (rc < 0) { dev_err(&pdev->dev, "Couldn't find reg in node %s, rc = %d\n", node->full_name, rc); return rc; @@ -528,56 +966,77 @@ static int qpnp_flash_led_probe(struct platform_device *pdev) return -ENOMEM; rc = qpnp_flash_led_parse_common_dt(led, node); - if (rc) { + if (rc < 0) { dev_err(&pdev->dev, "Failed to parse common flash LED device tree\n"); return rc; } - for_each_child_of_node(node, temp) - led->num_led_nodes++; - if (!led->num_led_nodes) { + for_each_available_child_of_node(node, temp) { + rc = of_property_read_string(temp, "label", &temp_string); + if (rc < 0) { + dev_err(&pdev->dev, + "Failed to parse label, rc=%d\n", rc); + return rc; + } + + if (!strcmp("switch", temp_string)) { + led->num_snodes++; + } else if (!strcmp("flash", temp_string) || + !strcmp("torch", temp_string)) { + led->num_fnodes++; + } else { + dev_err(&pdev->dev, + "Invalid label for led node\n"); + return -EINVAL; + } + } + + if (!led->num_fnodes) { dev_err(&pdev->dev, "No LED nodes defined\n"); return -ECHILD; } - led->fnode = devm_kzalloc(&pdev->dev, - sizeof(struct flash_node_data) * (--led->num_led_nodes), - GFP_KERNEL); + led->fnode = devm_kcalloc(&pdev->dev, led->num_fnodes, + sizeof(*led->fnode), + GFP_KERNEL); if (!led->fnode) return -ENOMEM; + led->snode = devm_kcalloc(&pdev->dev, led->num_snodes, + sizeof(*led->snode), + GFP_KERNEL); + if (!led->snode) + return -ENOMEM; + temp = NULL; - for (i = 0; i < led->num_led_nodes; i++) { - temp = of_get_next_child(node, temp); + for (i = 0; i < led->num_fnodes; i++) { + temp = of_get_next_available_child(node, temp); rc = qpnp_flash_led_parse_each_led_dt(led, &led->fnode[i], temp); - if (rc) { + if (rc < 0) { dev_err(&pdev->dev, - "Unable to parse flash node %d\n", i); + "Unable to parse flash node %d rc=%d\n", i, rc); goto error_led_register; } } - led->num_avail_leds = i; - - led->snode = devm_kzalloc(&pdev->dev, - sizeof(struct flash_switch_data), GFP_KERNEL); - if (!led->snode) { - rc = -ENOMEM; - goto error_led_register; - } - temp = of_get_next_child(node, temp); - rc = qpnp_flash_led_parse_and_register_switch(led, temp); - if (rc) { - dev_err(&pdev->dev, - "Unable to parse and register switch node\n"); - goto error_led_register; + for (i = 0; i < led->num_snodes; i++) { + temp = of_get_next_available_child(node, temp); + rc = qpnp_flash_led_parse_and_register_switch(led, + &led->snode[i], temp); + if (rc < 0) { + dev_err(&pdev->dev, + "Unable to parse and register switch node, rc=%d\n", + rc); + goto error_switch_register; + } } rc = qpnp_flash_led_init_settings(led); - if (rc) { - dev_err(&pdev->dev, "Failed to initialize flash LED\n"); + if (rc < 0) { + dev_err(&pdev->dev, + "Failed to initialize flash LED, rc=%d\n", rc); goto error_switch_register; } @@ -588,7 +1047,9 @@ static int qpnp_flash_led_probe(struct platform_device *pdev) return 0; error_switch_register: - led_classdev_unregister(&led->snode->cdev); + while (i > 0) + led_classdev_unregister(&led->snode[--i].cdev); + i = led->num_fnodes; error_led_register: while (i > 0) led_classdev_unregister(&led->fnode[--i].cdev); @@ -599,9 +1060,21 @@ error_led_register: static int qpnp_flash_led_remove(struct platform_device *pdev) { struct qpnp_flash_led *led = dev_get_drvdata(&pdev->dev); - int i = led->num_led_nodes; + int i; + + for (i = 0; i < led->num_snodes; i++) { + if (led->snode[i].num_regulators) { + if (led->snode[i].regulator_on) + qpnp_flash_led_regulator_enable(led, + &led->snode[i], false); + qpnp_flash_led_regulator_setup(led, + &led->snode[i], false); + } + } - led_classdev_unregister(&led->snode->cdev); + while (i > 0) + led_classdev_unregister(&led->snode[--i].cdev); + i = led->num_fnodes; while (i > 0) led_classdev_unregister(&led->fnode[--i].cdev); diff --git a/drivers/media/platform/msm/camera_v2/camera/camera.c b/drivers/media/platform/msm/camera_v2/camera/camera.c index 60a834588767..c1aeb8c43e81 100644 --- a/drivers/media/platform/msm/camera_v2/camera/camera.c +++ b/drivers/media/platform/msm/camera_v2/camera/camera.c @@ -475,6 +475,38 @@ static int camera_v4l2_unsubscribe_event(struct v4l2_fh *fh, return rc; } +static long camera_v4l2_vidioc_private_ioctl(struct file *filep, void *fh, + bool valid_prio, unsigned int cmd, void *arg) +{ + struct camera_v4l2_private *sp = fh_to_private(fh); + struct msm_video_device *pvdev = video_drvdata(filep); + struct msm_camera_private_ioctl_arg *k_ioctl = arg; + long rc = -EINVAL; + + if (WARN_ON(!k_ioctl || !pvdev)) + return -EIO; + + switch (k_ioctl->id) { + case MSM_CAMERA_PRIV_IOCTL_ID_RETURN_BUF: { + struct msm_camera_return_buf ptr, *tmp = NULL; + + MSM_CAM_GET_IOCTL_ARG_PTR(&tmp, &k_ioctl->ioctl_ptr, + sizeof(tmp)); + if (copy_from_user(&ptr, tmp, + sizeof(struct msm_camera_return_buf))) { + return -EFAULT; + } + rc = msm_vb2_return_buf_by_idx(pvdev->vdev->num, sp->stream_id, + ptr.index); + } + break; + default: + pr_debug("unimplemented id %d", k_ioctl->id); + return -EINVAL; + } + return rc; +} + static const struct v4l2_ioctl_ops camera_v4l2_ioctl_ops = { .vidioc_querycap = camera_v4l2_querycap, .vidioc_s_crop = camera_v4l2_s_crop, @@ -499,6 +531,7 @@ static const struct v4l2_ioctl_ops camera_v4l2_ioctl_ops = { /* event subscribe/unsubscribe */ .vidioc_subscribe_event = camera_v4l2_subscribe_event, .vidioc_unsubscribe_event = camera_v4l2_unsubscribe_event, + .vidioc_default = camera_v4l2_vidioc_private_ioctl, }; static int camera_v4l2_fh_open(struct file *filep) @@ -747,10 +780,62 @@ static int camera_v4l2_close(struct file *filep) } #ifdef CONFIG_COMPAT +static long camera_handle_internal_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + long rc = 0; + struct msm_camera_private_ioctl_arg k_ioctl; + void __user *tmp_compat_ioctl_ptr = NULL; + + rc = msm_copy_camera_private_ioctl_args(arg, + &k_ioctl, &tmp_compat_ioctl_ptr); + if (rc < 0) { + pr_err("Subdev cmd %d failed\n", cmd); + return rc; + } + switch (k_ioctl.id) { + case MSM_CAMERA_PRIV_IOCTL_ID_RETURN_BUF: { + if (k_ioctl.size != sizeof(struct msm_camera_return_buf)) { + pr_debug("Invalid size for id %d with size %d", + k_ioctl.id, k_ioctl.size); + return -EINVAL; + } + k_ioctl.ioctl_ptr = (__u64)tmp_compat_ioctl_ptr; + if (!k_ioctl.ioctl_ptr) { + pr_debug("Invalid ptr for id %d", k_ioctl.id); + return -EINVAL; + } + rc = camera_v4l2_vidioc_private_ioctl(file, file->private_data, + 0, cmd, (void *)&k_ioctl); + } + break; + default: + pr_debug("unimplemented id %d", k_ioctl.id); + return -EINVAL; + } + return rc; +} + long camera_v4l2_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - return -ENOIOCTLCMD; + long ret = 0; + + switch (cmd) { + case VIDIOC_MSM_CAMERA_PRIVATE_IOCTL_CMD: { + ret = camera_handle_internal_compat_ioctl(file, cmd, arg); + if (ret < 0) { + pr_debug("Subdev cmd %d fail\n", cmd); + return ret; + } + } + break; + default: + ret = -ENOIOCTLCMD; + break; + + } + return ret; } #endif static struct v4l2_file_operations camera_v4l2_fops = { diff --git a/drivers/media/platform/msm/camera_v2/fd/Makefile b/drivers/media/platform/msm/camera_v2/fd/Makefile index 82b37a73bfa3..8d01d3a8708d 100644 --- a/drivers/media/platform/msm/camera_v2/fd/Makefile +++ b/drivers/media/platform/msm/camera_v2/fd/Makefile @@ -1,5 +1,8 @@ GCC_VERSION := $(shell $(CONFIG_SHELL) $(srctree)/scripts/gcc-version.sh $(CROSS_COMPILE)gcc) ccflags-y += -Idrivers/media/video/msm ccflags-y += -Idrivers/media/platform/msm/camera_v2/common +ccflags-y += -Idrivers/media/platform/msm/camera_v2 +ccflags-y += -Idrivers/media/platform/msm/camera_v2/pproc/cpp +ccflags-y += -Idrivers/media/platform/msm/camera_v2/msm_buf_mgr/ obj-$(CONFIG_MSM_FD) += msm_fd_dev.o msm_fd_hw.o diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h index 95465fb24feb..763b6a575326 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h @@ -40,6 +40,7 @@ #define VFE40_8952_VERSION 0x10060000 #define VFE40_8976_VERSION 0x10050000 #define VFE40_8937_VERSION 0x10080000 +#define VFE40_8917_VERSION 0x10080001 #define VFE40_8953_VERSION 0x10090000 #define VFE32_8909_VERSION 0x30600 @@ -652,6 +653,7 @@ struct dual_vfe_resource { struct msm_vfe_stats_shared_data *stats_data[MAX_VFE]; struct msm_vfe_axi_shared_data *axi_data[MAX_VFE]; uint32_t wm_reload_mask[MAX_VFE]; + uint32_t epoch_sync_mask; }; struct master_slave_resource_info { diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c index d4f0453d72ff..a76ccc06c9e1 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c @@ -326,6 +326,7 @@ static void msm_vfe40_init_hardware_reg(struct vfe_device *vfe_dev) break; case VFE40_8937_VERSION: case VFE40_8953_VERSION: + case VFE40_8917_VERSION: default: ISP_DBG("%s: No special QOS\n", __func__); } @@ -1475,6 +1476,7 @@ static void msm_vfe40_axi_cfg_wm_reg( wm_bit_shift = VFE40_WM_BIT_SHIFT; } else if (vfe_dev->vfe_hw_version == VFE40_8976_VERSION || vfe_dev->vfe_hw_version == VFE40_8937_VERSION || + vfe_dev->vfe_hw_version == VFE40_8917_VERSION || vfe_dev->vfe_hw_version == VFE40_8953_VERSION) { burst_len = VFE40_BURST_LEN_8952_VERSION; wm_bit_shift = VFE40_WM_BIT_SHIFT_8976_VERSION; @@ -1986,6 +1988,7 @@ static void msm_vfe40_stats_cfg_ub(struct vfe_device *vfe_dev) if (vfe_dev->vfe_hw_version == VFE40_8916_VERSION || vfe_dev->vfe_hw_version == VFE40_8939_VERSION || vfe_dev->vfe_hw_version == VFE40_8937_VERSION || + vfe_dev->vfe_hw_version == VFE40_8917_VERSION || vfe_dev->vfe_hw_version == VFE40_8953_VERSION) { stats_burst_len = VFE40_STATS_BURST_LEN_8916_VERSION; ub_offset = VFE40_UB_SIZE_8916; 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 fbac6d81ded0..3dd55e02826d 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 @@ -540,6 +540,89 @@ static void msm_isp_cfg_framedrop_reg(struct vfe_device *vfe_dev, } /** + * msm_isp_check_epoch_status() - check the epock signal for framedrop + * + * @vfe_dev: The h/w on which the epoch signel is reveived + * @frame_src: The source of the epoch signal for this frame + * + * For dual vfe case and pixel stream, if both vfe's epoch signal is + * received, this function will return success. + * It will also return the vfe1 for further process + * For none dual VFE stream or none pixl source, this + * funciton will just return success. + * + * Returns 1 - epoch received is complete. + * 0 - epoch reveived is not complete. + */ +static int msm_isp_check_epoch_status(struct vfe_device **vfe_dev, + enum msm_vfe_input_src frame_src) +{ + struct vfe_device *vfe_dev_cur = *vfe_dev; + struct vfe_device *vfe_dev_other = NULL; + uint32_t vfe_id_other = 0; + uint32_t vfe_id_cur = 0; + uint32_t epoch_mask = 0; + unsigned long flags; + int completed = 0; + + spin_lock_irqsave( + &vfe_dev_cur->common_data->common_dev_data_lock, flags); + + if (vfe_dev_cur->is_split && + frame_src == VFE_PIX_0) { + if (vfe_dev_cur->pdev->id == ISP_VFE0) { + vfe_id_cur = ISP_VFE0; + vfe_id_other = ISP_VFE1; + } else { + vfe_id_cur = ISP_VFE1; + vfe_id_other = ISP_VFE0; + } + vfe_dev_other = vfe_dev_cur->common_data->dual_vfe_res-> + vfe_dev[vfe_id_other]; + + if (vfe_dev_cur->common_data->dual_vfe_res-> + epoch_sync_mask & (1 << vfe_id_cur)) { + /* serious scheduling delay */ + pr_err("Missing epoch: vfe %d, epoch mask 0x%x\n", + vfe_dev_cur->pdev->id, + vfe_dev_cur->common_data->dual_vfe_res-> + epoch_sync_mask); + goto fatal; + } + + vfe_dev_cur->common_data->dual_vfe_res-> + epoch_sync_mask |= (1 << vfe_id_cur); + + epoch_mask = (1 << vfe_id_cur) | (1 << vfe_id_other); + if ((vfe_dev_cur->common_data->dual_vfe_res-> + epoch_sync_mask & epoch_mask) == epoch_mask) { + + if (vfe_id_other == ISP_VFE0) + *vfe_dev = vfe_dev_cur; + else + *vfe_dev = vfe_dev_other; + + vfe_dev_cur->common_data->dual_vfe_res-> + epoch_sync_mask &= ~epoch_mask; + completed = 1; + } + } else + completed = 1; + + spin_unlock_irqrestore( + &vfe_dev_cur->common_data->common_dev_data_lock, flags); + + return completed; +fatal: + spin_unlock_irqrestore( + &vfe_dev_cur->common_data->common_dev_data_lock, flags); + /* new error event code will be added later */ + msm_isp_halt_send_error(vfe_dev_cur, ISP_EVENT_PING_PONG_MISMATCH); + return 0; +} + + +/** * msm_isp_update_framedrop_reg() - Update frame period pattern on h/w * @vfe_dev: The h/w on which the perion pattern is updated. * @frame_src: Input source. @@ -554,10 +637,15 @@ void msm_isp_update_framedrop_reg(struct vfe_device *vfe_dev, enum msm_vfe_input_src frame_src) { int i; - struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; + struct msm_vfe_axi_shared_data *axi_data = NULL; struct msm_vfe_axi_stream *stream_info; unsigned long flags; + if (msm_isp_check_epoch_status(&vfe_dev, frame_src) != 1) + return; + + axi_data = &vfe_dev->axi_data; + for (i = 0; i < VFE_AXI_SRC_MAX; i++) { if (SRC_TO_INTF(axi_data->stream_info[i].stream_src) != frame_src) { @@ -1937,6 +2025,13 @@ static void msm_isp_get_camif_update_state_and_halt( pix_stream_cnt++; } + if (vfe_dev->axi_data.num_active_stream == stream_cfg_cmd->num_streams + && (stream_cfg_cmd->cmd == STOP_STREAM || + stream_cfg_cmd->cmd == STOP_IMMEDIATELY)) + *halt = 1; + else + *halt = 0; + if ((pix_stream_cnt) && (axi_data->src_info[VFE_PIX_0].input_mux != EXTERNAL_READ)) { if (cur_pix_stream_cnt == 0 && pix_stream_cnt && @@ -1944,24 +2039,17 @@ static void msm_isp_get_camif_update_state_and_halt( *camif_update = ENABLE_CAMIF; else if (cur_pix_stream_cnt && (cur_pix_stream_cnt - pix_stream_cnt) == 0 && - stream_cfg_cmd->cmd == STOP_STREAM) - *camif_update = DISABLE_CAMIF; - else if (cur_pix_stream_cnt && - (cur_pix_stream_cnt - pix_stream_cnt) == 0 && - stream_cfg_cmd->cmd == STOP_IMMEDIATELY) - *camif_update = DISABLE_CAMIF_IMMEDIATELY; + (stream_cfg_cmd->cmd == STOP_STREAM || + stream_cfg_cmd->cmd == STOP_IMMEDIATELY)) { + if (*halt) + *camif_update = DISABLE_CAMIF_IMMEDIATELY; + else + *camif_update = DISABLE_CAMIF; + } else *camif_update = NO_UPDATE; } else *camif_update = NO_UPDATE; - - if (vfe_dev->axi_data.num_active_stream == stream_cfg_cmd->num_streams - && (stream_cfg_cmd->cmd == STOP_STREAM || - stream_cfg_cmd->cmd == STOP_IMMEDIATELY)) - *halt = 1; - else - *halt = 0; - } static void msm_isp_update_camif_output_count( @@ -2584,6 +2672,7 @@ static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev, vfe_dev->hw_info->vfe_ops.core_ops. update_camif_state(vfe_dev, camif_update); vfe_dev->axi_data.camif_state = CAMIF_ENABLE; + vfe_dev->common_data->dual_vfe_res->epoch_sync_mask = 0; } if (wait_for_complete) { @@ -2645,34 +2734,20 @@ static int msm_isp_stop_axi_stream(struct vfe_device *vfe_dev, wait_for_complete_for_this_stream = 0; stream_info->state = STOP_PENDING; - ISP_DBG("Stop axi Stream 0x%x\n", stream_info->stream_id); - if (stream_info->stream_src == CAMIF_RAW || - stream_info->stream_src == IDEAL_RAW) { - /* We dont get reg update IRQ for raw snapshot - * so frame skip cant be ocnfigured - */ - if ((camif_update != DISABLE_CAMIF_IMMEDIATELY) && - (!ext_read)) - wait_for_complete_for_this_stream = 1; - - } else if (stream_info->stream_type == BURST_STREAM && - stream_info->runtime_num_burst_capture == 0) { - /* Configure AXI writemasters to stop immediately - * since for burst case, write masters already skip - * all frames. - */ - if (stream_info->stream_src == RDI_INTF_0 || - stream_info->stream_src == RDI_INTF_1 || - stream_info->stream_src == RDI_INTF_2) - wait_for_complete_for_this_stream = 1; - } else { - if ((camif_update != DISABLE_CAMIF_IMMEDIATELY) && - !halt && (!ext_read)) - wait_for_complete_for_this_stream = 1; - } - ISP_DBG("%s: vfe_dev %d camif_update %d halt %d wait %d\n", - __func__, vfe_dev->pdev->id, camif_update, halt, + + if (!halt && !ext_read && + !(stream_info->stream_type == BURST_STREAM && + stream_info->runtime_num_burst_capture == 0)) + wait_for_complete_for_this_stream = 1; + + ISP_DBG("%s: stream 0x%x, vfe %d camif %d halt %d wait %d\n", + __func__, + stream_info->stream_id, + vfe_dev->pdev->id, + camif_update, + halt, wait_for_complete_for_this_stream); + intf = SRC_TO_INTF(stream_info->stream_src); if (!wait_for_complete_for_this_stream || stream_info->state == INACTIVE || 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 460746089c53..5b12c1239bf4 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 @@ -1917,11 +1917,16 @@ int msm_isp_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) ISP_DBG("%s open_cnt %u\n", __func__, vfe_dev->vfe_open_cnt); - if (vfe_dev->common_data == NULL) { - pr_err("%s: Error in probe. No common_data\n", __func__); + if (vfe_dev->common_data == NULL || + vfe_dev->common_data->dual_vfe_res == NULL) { + pr_err("%s: Error in probe. No common_data or dual vfe res\n", + __func__); return -EINVAL; } + if (vfe_dev->pdev->id == ISP_VFE0) + vfe_dev->common_data->dual_vfe_res->epoch_sync_mask = 0; + mutex_lock(&vfe_dev->realtime_mutex); mutex_lock(&vfe_dev->core_mutex); diff --git a/drivers/media/platform/msm/camera_v2/msm.c b/drivers/media/platform/msm/camera_v2/msm.c index a34dbb80b468..c6dc4b75e479 100644 --- a/drivers/media/platform/msm/camera_v2/msm.c +++ b/drivers/media/platform/msm/camera_v2/msm.c @@ -1099,6 +1099,28 @@ struct msm_stream *msm_get_stream_from_vb2q(struct vb2_queue *q) } EXPORT_SYMBOL(msm_get_stream_from_vb2q); +#ifdef CONFIG_COMPAT +long msm_copy_camera_private_ioctl_args(unsigned long arg, + struct msm_camera_private_ioctl_arg *k_ioctl, + void __user **tmp_compat_ioctl_ptr) +{ + struct msm_camera_private_ioctl_arg *up_ioctl_ptr = + (struct msm_camera_private_ioctl_arg *)arg; + + if (WARN_ON(!arg || !k_ioctl || !tmp_compat_ioctl_ptr)) + return -EIO; + + k_ioctl->id = up_ioctl_ptr->id; + k_ioctl->size = up_ioctl_ptr->size; + k_ioctl->result = up_ioctl_ptr->result; + k_ioctl->reserved = up_ioctl_ptr->reserved; + *tmp_compat_ioctl_ptr = compat_ptr(up_ioctl_ptr->ioctl_ptr); + + return 0; +} +EXPORT_SYMBOL(msm_copy_camera_private_ioctl_args); +#endif + static void msm_sd_notify(struct v4l2_subdev *sd, unsigned int notification, void *arg) { diff --git a/drivers/media/platform/msm/camera_v2/msm.h b/drivers/media/platform/msm/camera_v2/msm.h index cab07df2a5bb..2b3576b8edd2 100644 --- a/drivers/media/platform/msm/camera_v2/msm.h +++ b/drivers/media/platform/msm/camera_v2/msm.h @@ -133,4 +133,9 @@ 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_session_find(unsigned int session_id); +#ifdef CONFIG_COMPAT +long msm_copy_camera_private_ioctl_args(unsigned long arg, + struct msm_camera_private_ioctl_arg *k_ioctl, + void __user **tmp_compat_ioctl_ptr); +#endif #endif /*_MSM_H */ diff --git a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c index b6cbdcc32360..ac9a4b2048d1 100644 --- a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c +++ b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c @@ -48,6 +48,7 @@ static int32_t msm_buf_mngr_hdl_cont_get_buf(struct msm_buf_mngr_device *dev, } return 0; } + static int32_t msm_buf_mngr_get_buf(struct msm_buf_mngr_device *dev, void __user *argp) { @@ -91,6 +92,52 @@ static int32_t msm_buf_mngr_get_buf(struct msm_buf_mngr_device *dev, return rc; } +static int32_t msm_buf_mngr_get_buf_by_idx(struct msm_buf_mngr_device *dev, + void *argp) +{ + unsigned long flags; + int32_t rc = 0; + struct msm_buf_mngr_info *buf_info = + (struct msm_buf_mngr_info *)argp; + struct msm_get_bufs *new_entry = + kzalloc(sizeof(struct msm_get_bufs), GFP_KERNEL); + + if (!new_entry) + return -ENOMEM; + + if (!buf_info) { + kfree(new_entry); + return -EIO; + } + + INIT_LIST_HEAD(&new_entry->entry); + new_entry->vb2_v4l2_buf = dev->vb2_ops.get_buf_by_idx( + buf_info->session_id, buf_info->stream_id, buf_info->index); + if (!new_entry->vb2_v4l2_buf) { + pr_debug("%s:Get buf is null\n", __func__); + kfree(new_entry); + return -EINVAL; + } + new_entry->session_id = buf_info->session_id; + new_entry->stream_id = buf_info->stream_id; + new_entry->index = new_entry->vb2_v4l2_buf->vb2_buf.index; + spin_lock_irqsave(&dev->buf_q_spinlock, flags); + list_add_tail(&new_entry->entry, &dev->buf_qhead); + spin_unlock_irqrestore(&dev->buf_q_spinlock, flags); + if (buf_info->type == MSM_CAMERA_BUF_MNGR_BUF_USER) { + mutex_lock(&dev->cont_mutex); + if (!list_empty(&dev->cont_qhead)) { + rc = msm_buf_mngr_hdl_cont_get_buf(dev, buf_info); + } else { + pr_err("Nothing mapped in user buf for %d,%d\n", + buf_info->session_id, buf_info->stream_id); + rc = -EINVAL; + } + mutex_unlock(&dev->cont_mutex); + } + return rc; +} + static int32_t msm_buf_mngr_buf_done(struct msm_buf_mngr_device *buf_mngr_dev, struct msm_buf_mngr_info *buf_info) { @@ -413,6 +460,67 @@ static int msm_generic_buf_mngr_close(struct v4l2_subdev *sd, return rc; } +int msm_cam_buf_mgr_ops(unsigned int cmd, void *argp) +{ + int rc = 0; + + if (!msm_buf_mngr_dev) + return -ENODEV; + if (!argp) + return -EINVAL; + + switch (cmd) { + case VIDIOC_MSM_BUF_MNGR_GET_BUF: + rc = msm_buf_mngr_get_buf(msm_buf_mngr_dev, argp); + break; + case VIDIOC_MSM_BUF_MNGR_BUF_DONE: + rc = msm_buf_mngr_buf_done(msm_buf_mngr_dev, argp); + break; + case VIDIOC_MSM_BUF_MNGR_PUT_BUF: + rc = msm_buf_mngr_put_buf(msm_buf_mngr_dev, argp); + break; + case VIDIOC_MSM_BUF_MNGR_IOCTL_CMD: { + struct msm_camera_private_ioctl_arg *k_ioctl = argp; + + switch (k_ioctl->id) { + case MSM_CAMERA_BUF_MNGR_IOCTL_ID_GET_BUF_BY_IDX: { + struct msm_buf_mngr_info *tmp = NULL; + + if (!k_ioctl->ioctl_ptr) + return -EINVAL; + if (k_ioctl->size != sizeof(struct msm_buf_mngr_info)) + return -EINVAL; + + MSM_CAM_GET_IOCTL_ARG_PTR(&tmp, &k_ioctl->ioctl_ptr, + sizeof(tmp)); + rc = msm_buf_mngr_get_buf_by_idx(msm_buf_mngr_dev, + tmp); + } + break; + default: + pr_debug("unimplemented id %d", k_ioctl->id); + return -EINVAL; + } + break; + } + default: + return -ENOIOCTLCMD; + } + + return rc; +} + +int msm_cam_buf_mgr_register_ops(struct msm_cam_buf_mgr_req_ops *cb_struct) +{ + if (!msm_buf_mngr_dev) + return -ENODEV; + if (!cb_struct) + return -EINVAL; + + cb_struct->msm_cam_buf_mgr_ops = msm_cam_buf_mgr_ops; + return 0; +} + static long msm_buf_mngr_subdev_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { @@ -425,16 +533,45 @@ static long msm_buf_mngr_subdev_ioctl(struct v4l2_subdev *sd, rc = -ENOMEM; return rc; } - switch (cmd) { - case VIDIOC_MSM_BUF_MNGR_GET_BUF: - rc = msm_buf_mngr_get_buf(buf_mngr_dev, argp); + case VIDIOC_MSM_BUF_MNGR_IOCTL_CMD: { + struct msm_camera_private_ioctl_arg k_ioctl, *ptr; + + if (!arg) + return -EINVAL; + ptr = arg; + k_ioctl = *ptr; + switch (k_ioctl.id) { + case MSM_CAMERA_BUF_MNGR_IOCTL_ID_GET_BUF_BY_IDX: { + struct msm_buf_mngr_info buf_info, *tmp = NULL; + + if (k_ioctl.size != sizeof(struct msm_buf_mngr_info)) + return -EINVAL; + if (!k_ioctl.ioctl_ptr) + return -EINVAL; + + MSM_CAM_GET_IOCTL_ARG_PTR(&tmp, &k_ioctl.ioctl_ptr, + sizeof(tmp)); + if (copy_from_user(&buf_info, tmp, + sizeof(struct msm_buf_mngr_info))) { + return -EFAULT; + } + MSM_CAM_GET_IOCTL_ARG_PTR(&k_ioctl.ioctl_ptr, + &buf_info, sizeof(void *)); + argp = &k_ioctl; + rc = msm_cam_buf_mgr_ops(cmd, argp); + } + break; + default: + pr_debug("unimplemented id %d", k_ioctl.id); + return -EINVAL; + } break; + } + case VIDIOC_MSM_BUF_MNGR_GET_BUF: case VIDIOC_MSM_BUF_MNGR_BUF_DONE: - rc = msm_buf_mngr_buf_done(buf_mngr_dev, argp); - break; case VIDIOC_MSM_BUF_MNGR_PUT_BUF: - rc = msm_buf_mngr_put_buf(buf_mngr_dev, argp); + rc = msm_cam_buf_mgr_ops(cmd, argp); break; case VIDIOC_MSM_BUF_MNGR_INIT: rc = msm_generic_buf_mngr_open(sd, NULL); @@ -462,6 +599,107 @@ static long msm_buf_mngr_subdev_ioctl(struct v4l2_subdev *sd, } #ifdef CONFIG_COMPAT +static long msm_camera_buf_mgr_fetch_buf_info( + struct msm_buf_mngr_info32_t *buf_info32, + struct msm_buf_mngr_info *buf_info, unsigned long arg) +{ + if (!arg || !buf_info32 || !buf_info) + return -EINVAL; + + if (copy_from_user(buf_info32, (void __user *)arg, + sizeof(struct msm_buf_mngr_info32_t))) + return -EFAULT; + + buf_info->session_id = buf_info32->session_id; + buf_info->stream_id = buf_info32->stream_id; + buf_info->frame_id = buf_info32->frame_id; + buf_info->index = buf_info32->index; + buf_info->timestamp.tv_sec = (long) buf_info32->timestamp.tv_sec; + buf_info->timestamp.tv_usec = (long) buf_info32-> + timestamp.tv_usec; + buf_info->reserved = buf_info32->reserved; + buf_info->type = buf_info32->type; + return 0; +} + +static long msm_camera_buf_mgr_update_buf_info( + struct msm_buf_mngr_info32_t *buf_info32, + struct msm_buf_mngr_info *buf_info, unsigned long arg) +{ + if (!arg || !buf_info32 || !buf_info) + return -EINVAL; + + buf_info32->session_id = buf_info->session_id; + buf_info32->stream_id = buf_info->stream_id; + buf_info32->index = buf_info->index; + buf_info32->timestamp.tv_sec = (int32_t) buf_info-> + timestamp.tv_sec; + buf_info32->timestamp.tv_usec = (int32_t) buf_info->timestamp. + tv_usec; + buf_info32->reserved = buf_info->reserved; + buf_info32->type = buf_info->type; + buf_info32->user_buf.buf_cnt = buf_info->user_buf.buf_cnt; + memcpy(&buf_info32->user_buf.buf_idx, + &buf_info->user_buf.buf_idx, + sizeof(buf_info->user_buf.buf_idx)); + if (copy_to_user((void __user *)arg, buf_info32, + sizeof(struct msm_buf_mngr_info32_t))) + return -EFAULT; + return 0; +} +static long msm_camera_buf_mgr_internal_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct video_device *vdev = video_devdata(file); + struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); + long rc = 0; + struct msm_camera_private_ioctl_arg k_ioctl; + void __user *tmp_compat_ioctl_ptr = NULL; + + rc = msm_copy_camera_private_ioctl_args(arg, + &k_ioctl, &tmp_compat_ioctl_ptr); + if (rc < 0) { + pr_err("Subdev cmd %d failed\n", cmd); + return rc; + } + + switch (k_ioctl.id) { + case MSM_CAMERA_BUF_MNGR_IOCTL_ID_GET_BUF_BY_IDX: { + struct msm_buf_mngr_info32_t buf_info32; + struct msm_buf_mngr_info buf_info; + + if (k_ioctl.size != sizeof(struct msm_buf_mngr_info32_t)) { + pr_err("Invalid size for id %d with size %d", + k_ioctl.id, k_ioctl.size); + return -EINVAL; + } + if (!tmp_compat_ioctl_ptr) { + pr_err("Invalid ptr for id %d", k_ioctl.id); + return -EINVAL; + } + k_ioctl.ioctl_ptr = (__u64)&buf_info; + rc = msm_camera_buf_mgr_fetch_buf_info(&buf_info32, &buf_info, + (unsigned long)tmp_compat_ioctl_ptr); + if (rc < 0) { + pr_err("Fetch buf info failed for cmd=%d", cmd); + return rc; + } + rc = v4l2_subdev_call(sd, core, ioctl, cmd, &k_ioctl); + if (rc < 0) { + pr_err("Subdev cmd %d failed for id %d", cmd, + k_ioctl.id); + return rc; + } + } + break; + default: + pr_debug("unimplemented id %d", k_ioctl.id); + return -EINVAL; + } + + return 0; +} + static long msm_bmgr_subdev_fops_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -469,8 +707,6 @@ static long msm_bmgr_subdev_fops_compat_ioctl(struct file *file, struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); int32_t rc = 0; - void __user *up = (void __user *)arg; - /* Convert 32 bit IOCTL ID's to 64 bit IOCTL ID's * except VIDIOC_MSM_CPP_CFG32, which needs special * processing @@ -486,13 +722,14 @@ static long msm_bmgr_subdev_fops_compat_ioctl(struct file *file, cmd = VIDIOC_MSM_BUF_MNGR_PUT_BUF; break; case VIDIOC_MSM_BUF_MNGR_CONT_CMD: - cmd = VIDIOC_MSM_BUF_MNGR_CONT_CMD; break; case VIDIOC_MSM_BUF_MNGR_FLUSH32: cmd = VIDIOC_MSM_BUF_MNGR_FLUSH; break; + case VIDIOC_MSM_BUF_MNGR_IOCTL_CMD: + break; default: - pr_debug("%s : unsupported compat type", __func__); + pr_debug("unsupported compat type\n"); return -ENOIOCTLCMD; } @@ -504,65 +741,51 @@ static long msm_bmgr_subdev_fops_compat_ioctl(struct file *file, struct msm_buf_mngr_info32_t buf_info32; struct msm_buf_mngr_info buf_info; - if (copy_from_user(&buf_info32, (void __user *)up, - sizeof(struct msm_buf_mngr_info32_t))) - return -EFAULT; - - buf_info.session_id = buf_info32.session_id; - buf_info.stream_id = buf_info32.stream_id; - buf_info.frame_id = buf_info32.frame_id; - buf_info.index = buf_info32.index; - buf_info.timestamp.tv_sec = (long) buf_info32.timestamp.tv_sec; - buf_info.timestamp.tv_usec = (long) buf_info32. - timestamp.tv_usec; - buf_info.reserved = buf_info32.reserved; - buf_info.type = buf_info32.type; - + rc = msm_camera_buf_mgr_fetch_buf_info(&buf_info32, &buf_info, + arg); + if (rc < 0) { + pr_err("Fetch buf info failed for cmd=%d\n", cmd); + return rc; + } rc = v4l2_subdev_call(sd, core, ioctl, cmd, &buf_info); if (rc < 0) { - pr_debug("%s : Subdev cmd %d fail", __func__, cmd); + pr_debug("Subdev cmd %d fail\n", cmd); + return rc; + } + rc = msm_camera_buf_mgr_update_buf_info(&buf_info32, &buf_info, + arg); + if (rc < 0) { + pr_err("Update buf info failed for cmd=%d\n", cmd); + return rc; + } + break; + } + case VIDIOC_MSM_BUF_MNGR_IOCTL_CMD: { + rc = msm_camera_buf_mgr_internal_compat_ioctl(file, cmd, arg); + if (rc < 0) { + pr_debug("Subdev cmd %d fail\n", cmd); return rc; } - - buf_info32.session_id = buf_info.session_id; - buf_info32.stream_id = buf_info.stream_id; - buf_info32.index = buf_info.index; - buf_info32.timestamp.tv_sec = (int32_t) buf_info. - timestamp.tv_sec; - buf_info32.timestamp.tv_usec = (int32_t) buf_info.timestamp. - tv_usec; - buf_info32.reserved = buf_info.reserved; - buf_info32.type = buf_info.type; - buf_info32.user_buf.buf_cnt = buf_info.user_buf.buf_cnt; - memcpy(&buf_info32.user_buf.buf_idx, - &buf_info.user_buf.buf_idx, - sizeof(buf_info.user_buf.buf_idx)); - if (copy_to_user((void __user *)up, &buf_info32, - sizeof(struct msm_buf_mngr_info32_t))) - return -EFAULT; } break; case VIDIOC_MSM_BUF_MNGR_CONT_CMD: { struct msm_buf_mngr_main_cont_info cont_cmd; - if (copy_from_user(&cont_cmd, (void __user *)up, + if (copy_from_user(&cont_cmd, (void __user *)arg, sizeof(struct msm_buf_mngr_main_cont_info))) return -EFAULT; rc = v4l2_subdev_call(sd, core, ioctl, cmd, &cont_cmd); if (rc < 0) { - pr_debug("%s : Subdev cmd %d fail", __func__, cmd); + pr_debug("Subdev cmd %d fail\n", cmd); return rc; } } break; default: - pr_debug("%s : unsupported compat type", __func__); + pr_debug("unsupported compat type\n"); return -ENOIOCTLCMD; break; } - - - return 0; } #endif diff --git a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h index dc7f745f6190..c2f1a1527800 100644 --- a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h +++ b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-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 @@ -52,4 +52,14 @@ struct msm_buf_mngr_user_buf_cont_info { uint32_t cnt; struct ion_handle *ion_handle; }; + +/* kernel space functions*/ +struct msm_cam_buf_mgr_req_ops { + int (*msm_cam_buf_mgr_ops)(unsigned int cmd, void *argp); +}; + +/* API to register callback from client. This assumes cb_struct is allocated by + * client. + */ +int msm_cam_buf_mgr_register_ops(struct msm_cam_buf_mgr_req_ops *cb_struct); #endif diff --git a/drivers/media/platform/msm/camera_v2/msm_sd.h b/drivers/media/platform/msm/camera_v2/msm_sd.h index 6bb95ad0de87..d893d9fc07e3 100644 --- a/drivers/media/platform/msm/camera_v2/msm_sd.h +++ b/drivers/media/platform/msm/camera_v2/msm_sd.h @@ -73,6 +73,8 @@ struct msm_sd_req_vb2_q { unsigned int stream_id); struct vb2_queue * (*get_vb2_queue)(int session_id, unsigned int stream_id); + struct vb2_v4l2_buffer * (*get_buf_by_idx)(int session_id, + unsigned int stream_id, uint32_t index); int (*put_buf)(struct vb2_v4l2_buffer *vb2_buf, int session_id, unsigned int stream_id); int (*buf_done)(struct vb2_v4l2_buffer *vb2_v4l2_buf, int session_id, @@ -85,6 +87,9 @@ struct msm_sd_req_vb2_q { #define MSM_SD_NOTIFY_PUT_SD 0x00000002 #define MSM_SD_NOTIFY_REQ_CB 0x00000003 +#define MSM_CAM_GET_IOCTL_ARG_PTR(ptr, \ + ioctl_ptr, len) memcpy(ptr, ioctl_ptr, len) + int msm_sd_register(struct msm_sd_subdev *msm_subdev); int msm_sd_unregister(struct msm_sd_subdev *sd); struct v4l2_subdev *msm_sd_get_subdev(struct v4l2_subdev *sd, 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 6e0dac4608a9..59135225cb15 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 @@ -231,6 +231,41 @@ end: return vb2_v4l2_buf; } +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 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)) + return NULL; + + spin_lock_irqsave(&stream->stream_lock, flags); + + if (!stream->vb2_q) { + pr_err("%s: stream q not available\n", __func__); + goto end; + } + + list_for_each_entry(msm_vb2, &(stream->queued_list), list) { + vb2_v4l2_buf = &(msm_vb2->vb2_v4l2_buf); + if ((vb2_v4l2_buf->vb2_buf.index != index) || msm_vb2->in_freeq + || vb2_v4l2_buf->vb2_buf.state != VB2_BUF_STATE_ACTIVE) + continue; + + msm_vb2->in_freeq = 1; + goto end; + } + msm_vb2 = NULL; + vb2_v4l2_buf = NULL; +end: + spin_unlock_irqrestore(&stream->stream_lock, flags); + return vb2_v4l2_buf; +} + static int msm_vb2_put_buf(struct vb2_v4l2_buffer *vb, int session_id, unsigned int stream_id) { @@ -320,6 +355,48 @@ static int msm_vb2_buf_done(struct vb2_v4l2_buffer *vb, int session_id, return rc; } +long msm_vb2_return_buf_by_idx(int session_id, unsigned int stream_id, + uint32_t index) +{ + struct msm_stream *stream; + 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)) + return rc; + + spin_lock_irqsave(&stream->stream_lock, flags); + + if (!stream->vb2_q) { + pr_err("%s: stream q not available\n", __func__); + goto end; + } + + list_for_each_entry(msm_vb2, &(stream->queued_list), list) { + vb2_v4l2_buf = &(msm_vb2->vb2_v4l2_buf); + if ((vb2_v4l2_buf->vb2_buf.index != index) + || vb2_v4l2_buf->vb2_buf.state != VB2_BUF_STATE_ACTIVE) + continue; + + if (!msm_vb2->in_freeq) { + vb2_buffer_done(&vb2_v4l2_buf->vb2_buf, + VB2_BUF_STATE_ERROR); + rc = 0; + } else { + rc = -EINVAL; + } + break; + } + +end: + spin_unlock_irqrestore(&stream->stream_lock, flags); + return rc; +} +EXPORT_SYMBOL(msm_vb2_return_buf_by_idx); + static int msm_vb2_flush_buf(int session_id, unsigned int stream_id) { unsigned long flags; @@ -350,11 +427,11 @@ int msm_vb2_request_cb(struct msm_sd_req_vb2_q *req) } req->get_buf = msm_vb2_get_buf; + req->get_buf_by_idx = msm_vb2_get_buf_by_idx; req->get_vb2_queue = msm_vb2_get_queue; req->put_buf = msm_vb2_put_buf; req->buf_done = msm_vb2_buf_done; req->flush_buf = msm_vb2_flush_buf; - 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 96fa1d4c64c9..53511d5416d7 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-2014, The Linux Foundation. All rights reserved. +/* 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 @@ -65,5 +65,7 @@ struct msm_stream { struct vb2_ops *msm_vb2_get_q_ops(void); 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); #endif /*_MSM_VB_H */ diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c index a7ac094f6c1e..140d33582c87 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c @@ -90,10 +90,42 @@ static const struct v4l2_fmtdesc fmtdesc[] = { .pixelformat = SDE_PIX_FMT_BGRX_8888, }, { + .description = "SDE/XBGR_8888", + .pixelformat = SDE_PIX_FMT_XBGR_8888, + }, + { .description = "RGBA_5551", .pixelformat = SDE_PIX_FMT_RGBA_5551, }, { + .description = "ARGB_1555", + .pixelformat = SDE_PIX_FMT_ARGB_1555, + }, + { + .description = "ABGR_1555", + .pixelformat = SDE_PIX_FMT_ABGR_1555, + }, + { + .description = "BGRA_5551", + .pixelformat = SDE_PIX_FMT_BGRA_5551, + }, + { + .description = "BGRX_5551", + .pixelformat = SDE_PIX_FMT_BGRX_5551, + }, + { + .description = "RGBX_5551", + .pixelformat = SDE_PIX_FMT_RGBX_5551, + }, + { + .description = "XBGR_1555", + .pixelformat = SDE_PIX_FMT_XBGR_1555, + }, + { + .description = "XRGB_1555", + .pixelformat = SDE_PIX_FMT_XRGB_1555, + }, + { .description = "ARGB_4444", .pixelformat = SDE_PIX_FMT_ARGB_4444, }, @@ -102,6 +134,30 @@ static const struct v4l2_fmtdesc fmtdesc[] = { .pixelformat = SDE_PIX_FMT_RGBA_4444, }, { + .description = "BGRA_4444", + .pixelformat = SDE_PIX_FMT_BGRA_4444, + }, + { + .description = "ABGR_4444", + .pixelformat = SDE_PIX_FMT_ABGR_4444, + }, + { + .description = "RGBX_4444", + .pixelformat = SDE_PIX_FMT_RGBX_4444, + }, + { + .description = "XRGB_4444", + .pixelformat = SDE_PIX_FMT_XRGB_4444, + }, + { + .description = "BGRX_4444", + .pixelformat = SDE_PIX_FMT_BGRX_4444, + }, + { + .description = "XBGR_4444", + .pixelformat = SDE_PIX_FMT_XBGR_4444, + }, + { .description = "RGB_888", .pixelformat = SDE_PIX_FMT_RGB_888, }, diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_formats.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_formats.c index 8e5793362875..5318c9da1277 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_formats.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_formats.c @@ -318,6 +318,10 @@ static struct sde_mdp_format_params sde_mdp_format_map[] = { SDE_PIX_FMT_BGRX_8888, SDE_MDP_FMT_LINEAR, VALID_ROT_WB_ALL | VALID_MDP_WB_INTF_FORMAT, 0, C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, SDE_MDP_COMPRESS_NONE), + FMT_RGB_8888( + SDE_PIX_FMT_XBGR_8888, SDE_MDP_FMT_LINEAR, + VALID_ROT_WB_ALL | VALID_MDP_WB_INTF_FORMAT, + 0, C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, SDE_MDP_COMPRESS_NONE), FMT_YUV_PSEUDO(SDE_PIX_FMT_Y_CRCB_H2V1, SDE_MDP_FMT_LINEAR, SDE_MDP_CHROMA_H2V1, SDE_MDP_PIXEL_NORMAL, @@ -401,10 +405,36 @@ static struct sde_mdp_format_params sde_mdp_format_map[] = { }, FMT_RGB_1555(SDE_PIX_FMT_RGBA_5551, 1, VALID_ROT_WB_ALL, C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr), + FMT_RGB_1555(SDE_PIX_FMT_ARGB_1555, 1, VALID_ROT_WB_ALL, + C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA), + FMT_RGB_1555(SDE_PIX_FMT_ABGR_1555, 1, VALID_ROT_WB_ALL, + C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA), + FMT_RGB_1555(SDE_PIX_FMT_BGRA_5551, 1, VALID_ROT_WB_ALL, + C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb), + FMT_RGB_1555(SDE_PIX_FMT_BGRX_5551, 0, VALID_ROT_WB_ALL, + C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb), + FMT_RGB_1555(SDE_PIX_FMT_RGBX_5551, 0, VALID_ROT_WB_ALL, + C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr), + FMT_RGB_1555(SDE_PIX_FMT_XBGR_1555, 0, VALID_ROT_WB_ALL, + C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA), + FMT_RGB_1555(SDE_PIX_FMT_XRGB_1555, 0, VALID_ROT_WB_ALL, + C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA), FMT_RGB_4444(SDE_PIX_FMT_RGBA_4444, 1, VALID_ROT_WB_ALL, C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr), FMT_RGB_4444(SDE_PIX_FMT_ARGB_4444, 1, VALID_ROT_WB_ALL, C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA), + FMT_RGB_4444(SDE_PIX_FMT_BGRA_4444, 1, VALID_ROT_WB_ALL, + C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb), + FMT_RGB_4444(SDE_PIX_FMT_ABGR_4444, 1, VALID_ROT_WB_ALL, + C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA), + FMT_RGB_4444(SDE_PIX_FMT_RGBX_4444, 0, VALID_ROT_WB_ALL, + C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr), + FMT_RGB_4444(SDE_PIX_FMT_XRGB_4444, 0, VALID_ROT_WB_ALL, + C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA), + FMT_RGB_4444(SDE_PIX_FMT_BGRX_4444, 0, VALID_ROT_WB_ALL, + C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb), + FMT_RGB_4444(SDE_PIX_FMT_XBGR_4444, 0, VALID_ROT_WB_ALL, + C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA), FMT_RGB_1010102(SDE_PIX_FMT_RGBA_1010102, SDE_MDP_FMT_LINEAR, VALID_ROT_R3_WB_FORMAT | VALID_MDP_WB_INTF_FORMAT, 1, C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, SDE_MDP_COMPRESS_NONE), diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c index d5388db4a9b1..c141797bcd3c 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c @@ -575,14 +575,6 @@ static u32 sde_hw_rotator_start_regdma(struct sde_hw_rotator_context *ctx, if (rot->irq_num >= 0) reinit_completion(&ctx->regdma_comp); - /* enable IRQ for first regdma submission from idle */ - if (atomic_read(&rot->regdma_submit_count) == - atomic_read(&rot->regdma_done_count)) { - SDEROT_DBG("Enable IRQ! regdma submitcnt==donecnt -> %d\n", - atomic_read(&rot->regdma_submit_count)); - enable_irq(rot->irq_num); - } - /* * Last ROT command must be ROT_START before REGDMA start */ @@ -640,9 +632,6 @@ static u32 sde_hw_rotator_start_regdma(struct sde_hw_rotator_context *ctx, enableInt | (ts_length << 14) | offset); } - /* Update REGDMA submit count */ - atomic_inc(&rot->regdma_submit_count); - /* Update command queue write ptr */ sde_hw_rotator_put_regdma_segment(ctx, wrptr); @@ -731,7 +720,6 @@ static u32 sde_hw_rotator_wait_done_regdma( u32 last_ts; u32 int_id; u32 sts = 0; - u32 d_count; unsigned long flags; if (rot->irq_num >= 0) { @@ -753,6 +741,8 @@ static u32 sde_hw_rotator_wait_done_regdma( SDEROT_ERR( "Timeout wait for regdma interrupt status, ts:%X\n", ctx->timestamp); + SDEROT_ERR("last_isr:0x%X, last_ts:0x%X, rc=%d\n", + last_isr, last_ts, rc); if (status & REGDMA_WATCHDOG_INT) SDEROT_ERR("REGDMA watchdog interrupt\n"); @@ -784,17 +774,6 @@ static u32 sde_hw_rotator_wait_done_regdma( status = 0; } - /* regardless success or timeout, update done count */ - d_count = atomic_inc_return(&rot->regdma_done_count); - - /* disable IRQ if no more regdma submission in queue */ - if (d_count == atomic_read(&rot->regdma_submit_count)) { - SDEROT_DBG( - "Disable IRQ!! regdma donecnt==submitcnt -> %d\n", - d_count); - disable_irq_nosync(rot->irq_num); - } - spin_unlock_irqrestore(&rot->rotisr_lock, flags); } else { int cnt = 200; @@ -1027,6 +1006,9 @@ static struct sde_rot_hw_resource *sde_hw_rotator_alloc_ext( sde_hw_rotator_swts_create(resinfo->rot); } + if (resinfo->rot->irq_num >= 0) + enable_irq(resinfo->rot->irq_num); + SDEROT_DBG("New rotator resource:%p, priority:%d\n", resinfo, wb_id); @@ -1053,6 +1035,9 @@ static void sde_hw_rotator_free_ext(struct sde_rot_mgr *mgr, resinfo, hw->wb_id, atomic_read(&hw->num_active), hw->pending_count); + if (resinfo->rot->irq_num >= 0) + disable_irq(resinfo->rot->irq_num); + devm_kfree(&mgr->pdev->dev, resinfo); } @@ -1505,8 +1490,7 @@ static irqreturn_t sde_hw_rotator_regdmairq_handler(int irq, void *ptr) ctx->last_regdma_isr_status = isr; ctx->last_regdma_timestamp = ts; SDEROT_DBG( - "regdma complete: ctx:%p, ts:%X, dcount:%X\n", - ctx, ts, atomic_read(&rot->regdma_done_count)); + "regdma complete: ctx:%p, ts:%X\n", ctx, ts); complete_all(&ctx->regdma_comp); ts = (ts - 1) & SDE_REGDMA_SWTS_MASK; @@ -1854,8 +1838,6 @@ int sde_rotator_r3_init(struct sde_rot_mgr *mgr) atomic_set(&rot->timestamp[0], 0); atomic_set(&rot->timestamp[1], 0); - atomic_set(&rot->regdma_submit_count, 0); - atomic_set(&rot->regdma_done_count, 0); ret = sde_rotator_hw_rev_init(rot); if (ret) diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h index 5196b1351542..610caf16c764 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h @@ -263,9 +263,6 @@ struct sde_hw_rotator { struct completion rot_comp; struct completion regdma_comp; - atomic_t regdma_submit_count; - atomic_t regdma_done_count; - spinlock_t rotctx_lock; spinlock_t rotisr_lock; diff --git a/drivers/media/platform/msm/vidc/msm_smem.c b/drivers/media/platform/msm/vidc/msm_smem.c index 009d8271563d..4ae13944362a 100644 --- a/drivers/media/platform/msm/vidc/msm_smem.c +++ b/drivers/media/platform/msm/vidc/msm_smem.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. +/* 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 @@ -462,6 +462,23 @@ struct msm_smem *msm_smem_user_to_kernel(void *clt, int fd, u32 offset, return mem; } +bool msm_smem_compare_buffers(void *clt, int fd, void *priv) +{ + struct smem_client *client = clt; + struct ion_handle *handle = NULL; + bool ret = false; + + if (!clt || !priv) { + dprintk(VIDC_ERR, "Invalid params: %p, %p\n", + clt, priv); + return false; + } + handle = ion_import_dma_buf(client->clnt, fd); + ret = handle == priv; + handle ? ion_free(client->clnt, handle) : 0; + return ret; +} + static int ion_cache_operations(struct smem_client *client, struct msm_smem *mem, enum smem_cache_ops cache_op) { diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c index 14b62984eae9..5c9dc6a53223 100644 --- a/drivers/media/platform/msm/vidc/msm_venc.c +++ b/drivers/media/platform/msm/vidc/msm_venc.c @@ -1552,52 +1552,6 @@ static int msm_venc_toggle_hier_p(struct msm_vidc_inst *inst, int layers) return rc; } -static int set_bitrate_for_each_layer(struct msm_vidc_inst *inst, - u32 num_enh_layers, u32 total_bitrate) -{ - u32 property_id = 0; - int i = 0, rc = 0; - struct hfi_device *hdev = NULL; - struct hal_bitrate bitrate; - struct hal_enable enable; - int bitrate_table[3][4] = { - {50, 50, 0, 0}, - {34, 33, 33, 0}, - {25, 25, 25, 25} - }; - - if (!inst || !inst->core || !inst->core->device) { - dprintk(VIDC_ERR, "%s - invalid input\n", __func__); - return -EINVAL; - } - - if (!num_enh_layers || num_enh_layers > ARRAY_SIZE(bitrate_table)) { - dprintk(VIDC_ERR, "%s - invalid number of enh layers: %d\n", - __func__, num_enh_layers); - return -EINVAL; - } - hdev = inst->core->device; - - property_id = HAL_PARAM_VENC_BITRATE_TYPE; - enable.enable = V4L2_CID_MPEG_VIDC_VIDEO_VENC_BITRATE_ENABLE; - rc = call_hfi_op(hdev, session_set_property, - (void *)inst->session, property_id, &enable); - if (rc) { - dprintk(VIDC_ERR, "Failed to set layerwise bitrate\n"); - return false; - } - - for (i = 0; !rc && i <= num_enh_layers; i++) { - property_id = HAL_CONFIG_VENC_TARGET_BITRATE; - bitrate.bit_rate = (u32)((total_bitrate * - bitrate_table[num_enh_layers - 1][i]) / 100); - bitrate.layer_id = i; - rc = call_hfi_op(hdev, session_set_property, - (void *)inst->session, property_id, &bitrate); - } - return rc; -} - static inline int msm_venc_power_save_mode_enable(struct msm_vidc_inst *inst) { u32 rc = 0; @@ -2269,26 +2223,10 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) } case V4L2_CID_MPEG_VIDEO_BITRATE: { - struct v4l2_ctrl *hier_p = TRY_GET_CTRL( - V4L2_CID_MPEG_VIDC_VIDEO_HIER_P_NUM_LAYERS); - + property_id = HAL_CONFIG_VENC_TARGET_BITRATE; + bitrate.bit_rate = ctrl->val; bitrate.layer_id = 0; - if (hier_p->val && - inst->fmts[CAPTURE_PORT]->fourcc == - V4L2_PIX_FMT_H264) { - rc = set_bitrate_for_each_layer(inst, - hier_p->val, ctrl->val); - if (rc) { - dprintk(VIDC_ERR, - "failed to set bitrate for multiple layers\n"); - rc = -EINVAL; - } - } else { - property_id = HAL_CONFIG_VENC_TARGET_BITRATE; - bitrate.bit_rate = ctrl->val; - bitrate.layer_id = 0; - pdata = &bitrate; - } + pdata = &bitrate; break; } case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: @@ -3236,8 +3174,8 @@ static int try_set_ext_ctrl(struct msm_vidc_inst *inst, bitrate.bit_rate = control[i].value; property_id = HAL_CONFIG_VENC_TARGET_BITRATE; pdata = &bitrate; - dprintk(VIDC_DBG, "layerwise bitrate for %d\n", - i); + dprintk(VIDC_DBG, "bitrate for layer(%d)=%d\n", + i, bitrate.bit_rate); rc = call_hfi_op(hdev, session_set_property, (void *)inst->session, property_id, pdata); diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c index 51be8df7ff35..2dab1f98f30a 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc.c +++ b/drivers/media/platform/msm/vidc/msm_vidc.c @@ -213,7 +213,9 @@ struct buffer_info *get_registered_buf(struct msm_vidc_inst *inst, *plane = 0; list_for_each_entry(temp, &inst->registeredbufs.list, list) { for (i = 0; i < min(temp->num_planes, VIDEO_MAX_PLANES); i++) { - bool fd_matches = fd == temp->fd[i]; + bool ion_hndl_matches = temp->handle[i] ? + msm_smem_compare_buffers(inst->mem_client, fd, + temp->handle[i]->smem_priv) : false; bool device_addr_matches = device_addr == temp->device_addr[i]; bool contains_within = CONTAINS(temp->buff_off[i], @@ -223,7 +225,7 @@ struct buffer_info *get_registered_buf(struct msm_vidc_inst *inst, temp->buff_off[i], temp->size[i]); if (!temp->inactive && - (fd_matches || device_addr_matches) && + (ion_hndl_matches || device_addr_matches) && (contains_within || overlaps)) { dprintk(VIDC_DBG, "This memory region is already mapped\n"); @@ -386,13 +388,6 @@ static inline bool is_dynamic_output_buffer_mode(struct v4l2_buffer *b, } -static inline bool is_encoder_input_buffer(struct v4l2_buffer *b, - struct msm_vidc_inst *inst) -{ - return b->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && - inst->session_type == MSM_VIDC_ENCODER; -} - static inline void save_v4l2_buffer(struct v4l2_buffer *b, struct buffer_info *binfo) { @@ -907,8 +902,7 @@ int msm_vidc_dqbuf(void *instance, struct v4l2_buffer *b) for (i = 0; i < b->length; i++) { if (EXTRADATA_IDX(b->length) && - (i == EXTRADATA_IDX(b->length)) && - !b->m.planes[i].m.userptr) { + i == EXTRADATA_IDX(b->length)) { continue; } buffer_info = device_to_uvaddr(&inst->registeredbufs, @@ -943,8 +937,8 @@ int msm_vidc_dqbuf(void *instance, struct v4l2_buffer *b) if (rc) return rc; - if (is_dynamic_output_buffer_mode(b, inst) || - is_encoder_input_buffer(b, inst)) { + + if (is_dynamic_output_buffer_mode(b, inst)) { buffer_info->dequeued = true; dprintk(VIDC_DBG, "[DEQUEUED]: fd[0] = %d\n", diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c index 81a73728fb0c..05bfabce2bb2 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_common.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c @@ -1704,36 +1704,6 @@ static struct vb2_buffer *get_vb_from_device_addr(struct buf_queue *bufq, return vb; } - -static void handle_dynamic_input_buffer(struct msm_vidc_inst *inst, - ion_phys_addr_t device_addr) -{ - struct buffer_info *binfo = NULL, *temp = NULL; - - if (inst->session_type == MSM_VIDC_ENCODER) { - binfo = device_to_uvaddr(&inst->registeredbufs, device_addr); - if (!binfo) { - dprintk(VIDC_ERR, - "%s buffer not found in registered list\n", - __func__); - return; - } - dprintk(VIDC_DBG, - "EBD fd[0] = %d -> EBD_ref_released, addr: %pa\n", - binfo->fd[0], &device_addr); - - mutex_lock(&inst->registeredbufs.lock); - list_for_each_entry(temp, &inst->registeredbufs.list, - list) { - if (temp == binfo) { - binfo->pending_deletion = true; - break; - } - } - mutex_unlock(&inst->registeredbufs.lock); - } -} - static void handle_ebd(enum hal_command_response cmd, void *data) { struct msm_vidc_cb_data_done *response = data; @@ -1754,8 +1724,6 @@ static void handle_ebd(enum hal_command_response cmd, void *data) return; } - handle_dynamic_input_buffer(inst, response->input_done.packet_buffer); - vb = get_vb_from_device_addr(&inst->bufq[OUTPUT_PORT], response->input_done.packet_buffer); if (vb) { diff --git a/drivers/media/platform/msm/vidc/msm_vidc_internal.h b/drivers/media/platform/msm/vidc/msm_vidc_internal.h index c127a17b6157..72c1ddcf3a70 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_internal.h +++ b/drivers/media/platform/msm/vidc/msm_vidc_internal.h @@ -373,6 +373,7 @@ struct msm_smem *msm_smem_user_to_kernel(void *clt, int fd, u32 offset, struct context_bank_info *msm_smem_get_context_bank(void *clt, bool is_secure, enum hal_buffer buffer_type); void msm_vidc_fw_unload_handler(struct work_struct *work); +bool msm_smem_compare_buffers(void *clt, int fd, void *priv); /* XXX: normally should be in msm_vidc.h, but that's meant for public APIs, * whereas this is private */ int msm_vidc_destroy(struct msm_vidc_inst *inst); diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c index 5525e35a029f..d19d8b624c19 100644 --- a/drivers/media/platform/msm/vidc/venus_hfi.c +++ b/drivers/media/platform/msm/vidc/venus_hfi.c @@ -1238,6 +1238,13 @@ static unsigned long __get_clock_rate_with_bitrate(struct clock_info *clock, break; } } + + /* + * Current bitrate is higher than max supported load. + * Select max frequency to handle this load. + */ + if (i < 0) + supported_clk[j] = table[0].freq; } for (i = 0; i < data->num_sessions; i++) @@ -2218,9 +2225,14 @@ static int venus_hfi_core_init(void *device) if (rc || __iface_cmdq_write(dev, &version_pkt)) dprintk(VIDC_WARN, "Failed to send image version pkt to f/w\n"); - if (dev->res->pm_qos_latency_us) + if (dev->res->pm_qos_latency_us) { +#ifdef CONFIG_SMP + dev->qos.type = PM_QOS_REQ_AFFINE_IRQ; + dev->qos.irq = dev->hal_data->irq; +#endif pm_qos_add_request(&dev->qos, PM_QOS_CPU_DMA_LATENCY, dev->res->pm_qos_latency_us); + } mutex_unlock(&dev->lock); return rc; @@ -2244,7 +2256,8 @@ static int venus_hfi_core_release(void *dev) mutex_lock(&device->lock); - if (device->res->pm_qos_latency_us) + if (device->res->pm_qos_latency_us && + pm_qos_request_active(&device->qos)) pm_qos_remove_request(&device->qos); __set_state(device, VENUS_STATE_DEINIT); __unload_fw(device); @@ -4208,7 +4221,8 @@ static inline int __suspend(struct venus_hfi_device *device) dprintk(VIDC_DBG, "Entering power collapse\n"); - if (device->res->pm_qos_latency_us) + if (device->res->pm_qos_latency_us && + pm_qos_request_active(&device->qos)) pm_qos_remove_request(&device->qos); rc = __tzbsp_set_video_state(TZBSP_VIDEO_STATE_SUSPEND); @@ -4270,9 +4284,14 @@ static inline int __resume(struct venus_hfi_device *device) */ __set_threshold_registers(device); - if (device->res->pm_qos_latency_us) + if (device->res->pm_qos_latency_us) { +#ifdef CONFIG_SMP + device->qos.type = PM_QOS_REQ_AFFINE_IRQ; + device->qos.irq = device->hal_data->irq; +#endif pm_qos_add_request(&device->qos, PM_QOS_CPU_DMA_LATENCY, device->res->pm_qos_latency_us); + } dprintk(VIDC_INFO, "Resumed from power collapse\n"); exit: device->skip_pc_count = 0; diff --git a/drivers/mfd/qcom-i2c-pmic.c b/drivers/mfd/qcom-i2c-pmic.c index 4d0bdce755a6..9eb75d876577 100644 --- a/drivers/mfd/qcom-i2c-pmic.c +++ b/drivers/mfd/qcom-i2c-pmic.c @@ -58,7 +58,6 @@ struct i2c_pmic_periph { struct i2c_pmic { struct device *dev; - struct i2c_client *client; struct regmap *regmap; struct irq_domain *domain; struct i2c_pmic_periph *periph; @@ -497,25 +496,28 @@ static struct regmap_config i2c_pmic_regmap_config = { static int i2c_pmic_probe(struct i2c_client *client, const struct i2c_device_id *id) { - int rc; struct i2c_pmic *chip; + int rc = 0; chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); if (!chip) return -ENOMEM; + chip->dev = &client->dev; chip->regmap = devm_regmap_init_i2c(client, &i2c_pmic_regmap_config); if (!chip->regmap) return -ENODEV; + i2c_set_clientdata(client, chip); + if (!of_property_read_bool(chip->dev->of_node, "interrupt-controller")) + goto probe_children; + chip->domain = irq_domain_add_tree(client->dev.of_node, &i2c_pmic_domain_ops, chip); - if (!chip->domain) - return -ENOMEM; - - chip->client = client; - chip->dev = &client->dev; - i2c_set_clientdata(client, chip); + if (!chip->domain) { + rc = -ENOMEM; + goto cleanup; + } rc = i2c_pmic_parse_dt(chip); if (rc < 0) { @@ -549,6 +551,8 @@ static int i2c_pmic_probe(struct i2c_client *client, } enable_irq_wake(client->irq); + +probe_children: of_platform_populate(chip->dev->of_node, NULL, NULL, chip->dev); pr_info("I2C PMIC probe successful\n"); return rc; @@ -565,7 +569,8 @@ static int i2c_pmic_remove(struct i2c_client *client) struct i2c_pmic *chip = i2c_get_clientdata(client); of_platform_depopulate(chip->dev); - irq_domain_remove(chip->domain); + if (chip->domain) + irq_domain_remove(chip->domain); i2c_set_clientdata(client, NULL); return 0; } diff --git a/drivers/mfd/wcd9xxx-irq.c b/drivers/mfd/wcd9xxx-irq.c index 8024fab80295..1b93c83ae98e 100644 --- a/drivers/mfd/wcd9xxx-irq.c +++ b/drivers/mfd/wcd9xxx-irq.c @@ -390,18 +390,21 @@ void wcd9xxx_free_irq(struct wcd9xxx_core_resource *wcd9xxx_res, void wcd9xxx_enable_irq(struct wcd9xxx_core_resource *wcd9xxx_res, int irq) { - enable_irq(phyirq_to_virq(wcd9xxx_res, irq)); + if (wcd9xxx_res->irq) + enable_irq(phyirq_to_virq(wcd9xxx_res, irq)); } void wcd9xxx_disable_irq(struct wcd9xxx_core_resource *wcd9xxx_res, int irq) { - disable_irq_nosync(phyirq_to_virq(wcd9xxx_res, irq)); + if (wcd9xxx_res->irq) + disable_irq_nosync(phyirq_to_virq(wcd9xxx_res, irq)); } void wcd9xxx_disable_irq_sync( struct wcd9xxx_core_resource *wcd9xxx_res, int irq) { - disable_irq(phyirq_to_virq(wcd9xxx_res, irq)); + if (wcd9xxx_res->irq) + disable_irq(phyirq_to_virq(wcd9xxx_res, irq)); } static int wcd9xxx_irq_setup_downstream_irq( @@ -551,6 +554,7 @@ void wcd9xxx_irq_exit(struct wcd9xxx_core_resource *wcd9xxx_res) disable_irq_wake(wcd9xxx_res->irq); free_irq(wcd9xxx_res->irq, wcd9xxx_res); /* Release parent's of node */ + wcd9xxx_res->irq = 0; wcd9xxx_irq_put_upstream_irq(wcd9xxx_res); } mutex_destroy(&wcd9xxx_res->irq_lock); diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c index 1aec5e559dd5..273728482227 100644 --- a/drivers/misc/qseecom.c +++ b/drivers/misc/qseecom.c @@ -433,7 +433,6 @@ static int qseecom_scm_call2(uint32_t svc_id, uint32_t tz_cmd_id, smc_id = TZ_OS_APP_SHUTDOWN_ID; desc.arginfo = TZ_OS_APP_SHUTDOWN_ID_PARAM_ID; desc.args[0] = req->app_id; - __qseecom_reentrancy_check_if_no_app_blocked(smc_id); ret = scm_call2(smc_id, &desc); break; } @@ -2104,6 +2103,9 @@ static int qseecom_unload_app(struct qseecom_dev_handle *data, return 0; } + __qseecom_cleanup_app(data); + __qseecom_reentrancy_check_if_no_app_blocked(TZ_OS_APP_SHUTDOWN_ID); + if (data->client.app_id > 0) { spin_lock_irqsave(&qseecom.registered_app_list_lock, flags); list_for_each_entry(ptr_app, &qseecom.registered_app_list_head, @@ -2132,11 +2134,9 @@ static int qseecom_unload_app(struct qseecom_dev_handle *data, } } - if (found_dead_app) { + if (found_dead_app) pr_warn("cleanup app_id %d(%s)\n", data->client.app_id, (char *)data->client.app_name); - __qseecom_cleanup_app(data); - } if (unload) { struct qseecom_unload_app_ireq req; @@ -2165,7 +2165,6 @@ static int qseecom_unload_app(struct qseecom_dev_handle *data, if (resp.result == QSEOS_RESULT_SUCCESS) pr_debug("App (%d) is unloaded!!\n", data->client.app_id); - __qseecom_cleanup_app(data); if (resp.result == QSEOS_RESULT_INCOMPLETE) { ret = __qseecom_process_incomplete_cmd(data, &resp); if (ret) { diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 49eb2e27b11b..18e95148e891 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -378,6 +378,10 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, /* social scan on P2P_DEVICE is handled as p2p search */ if (wdev->iftype == NL80211_IFTYPE_P2P_DEVICE && wil_p2p_is_social_scan(request)) { + if (!wil->p2p.p2p_dev_started) { + wil_err(wil, "P2P search requested on stopped P2P device\n"); + return -EIO; + } wil->scan_request = request; wil->radio_wdev = wdev; rc = wil_p2p_search(wil, request); @@ -1351,6 +1355,7 @@ static int wil_cfg80211_start_p2p_device(struct wiphy *wiphy, struct wil6210_priv *wil = wiphy_to_wil(wiphy); wil_dbg_misc(wil, "%s: entered\n", __func__); + wil->p2p.p2p_dev_started = 1; return 0; } @@ -1358,8 +1363,19 @@ static void wil_cfg80211_stop_p2p_device(struct wiphy *wiphy, struct wireless_dev *wdev) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); + u8 started; wil_dbg_misc(wil, "%s: entered\n", __func__); + mutex_lock(&wil->mutex); + started = wil_p2p_stop_discovery(wil); + if (started && wil->scan_request) { + cfg80211_scan_done(wil->scan_request, 1); + wil->scan_request = NULL; + wil->radio_wdev = wil->wdev; + } + mutex_unlock(&wil->mutex); + + wil->p2p.p2p_dev_started = 0; } static struct cfg80211_ops wil_cfg80211_ops = { diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c index 2a05031ba631..bd4c17ca2484 100644 --- a/drivers/net/wireless/ath/wil6210/netdev.c +++ b/drivers/net/wireless/ath/wil6210/netdev.c @@ -14,10 +14,15 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include <linux/moduleparam.h> #include <linux/etherdevice.h> #include "wil6210.h" #include "txrx.h" +static bool alt_ifname; /* = false; */ +module_param(alt_ifname, bool, S_IRUGO); +MODULE_PARM_DESC(alt_ifname, " use an alternate interface name wigigN instead of wlanN"); + static int wil_open(struct net_device *ndev) { struct wil6210_priv *wil = ndev_to_wil(ndev); @@ -136,6 +141,7 @@ void *wil_if_alloc(struct device *dev) struct wil6210_priv *wil; struct ieee80211_channel *ch; int rc = 0; + const char *ifname = alt_ifname ? "wigig%d" : "wlan%d"; wdev = wil_cfg80211_init(dev); if (IS_ERR(wdev)) { @@ -160,7 +166,7 @@ void *wil_if_alloc(struct device *dev) ch = wdev->wiphy->bands[IEEE80211_BAND_60GHZ]->channels; cfg80211_chandef_create(&wdev->preset_chandef, ch, NL80211_CHAN_NO_HT); - ndev = alloc_netdev(0, "wlan%d", NET_NAME_UNKNOWN, wil_dev_setup); + ndev = alloc_netdev(0, ifname, NET_NAME_UNKNOWN, wil_dev_setup); if (!ndev) { dev_err(dev, "alloc_netdev_mqs failed\n"); rc = -ENOMEM; diff --git a/drivers/net/wireless/ath/wil6210/p2p.c b/drivers/net/wireless/ath/wil6210/p2p.c index 1c9153894dca..213b8259638c 100644 --- a/drivers/net/wireless/ath/wil6210/p2p.c +++ b/drivers/net/wireless/ath/wil6210/p2p.c @@ -114,8 +114,10 @@ int wil_p2p_listen(struct wil6210_priv *wil, unsigned int duration, u8 channel = P2P_DMG_SOCIAL_CHANNEL; int rc; - if (chan) - channel = chan->hw_value; + if (!chan) + return -EINVAL; + + channel = chan->hw_value; wil_dbg_misc(wil, "%s: duration %d\n", __func__, duration); diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index a67725419c76..cd0bfdf34248 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -461,6 +461,7 @@ struct wil_tid_crypto_rx { struct wil_p2p_info { struct ieee80211_channel listen_chan; u8 discovery_started; + u8 p2p_dev_started; u64 cookie; struct timer_list discovery_timer; /* listen/search duration */ struct work_struct discovery_expired_work; /* listen/search expire */ diff --git a/drivers/net/wireless/cnss/cnss_common.c b/drivers/net/wireless/cnss/cnss_common.c index 3639d3245077..498dd87e1a91 100644 --- a/drivers/net/wireless/cnss/cnss_common.c +++ b/drivers/net/wireless/cnss/cnss_common.c @@ -21,7 +21,7 @@ #include <linux/mutex.h> #include <linux/rwsem.h> #include <net/cnss.h> -#include <net/cnss_common.h> +#include "cnss_common.h" #include <net/cfg80211.h> enum cnss_dev_bus_type { diff --git a/drivers/net/wireless/cnss/cnss_common.h b/drivers/net/wireless/cnss/cnss_common.h new file mode 100644 index 000000000000..db5ed02d47ab --- /dev/null +++ b/drivers/net/wireless/cnss/cnss_common.h @@ -0,0 +1,40 @@ +/* Copyright (c) 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 _NET_CNSS_COMMON_H_ +#define _NET_CNSS_COMMON_H_ + +#define MAX_FIRMWARE_SIZE (1 * 1024 * 1024) +/* max 20mhz channel count */ +#define CNSS_MAX_CH_NUM 45 + +int cnss_pci_request_bus_bandwidth(int bandwidth); +int cnss_sdio_request_bus_bandwidth(int bandwidth); + +void cnss_sdio_device_crashed(void); +void cnss_pci_device_crashed(void); + +void cnss_pci_device_self_recovery(void); +void cnss_sdio_device_self_recovery(void); + +void *cnss_pci_get_virt_ramdump_mem(unsigned long *size); +void *cnss_sdio_get_virt_ramdump_mem(unsigned long *size); + +void cnss_sdio_schedule_recovery_work(void); +void cnss_pci_schedule_recovery_work(void); + +int cnss_pcie_set_wlan_mac_address(const u8 *in, uint32_t len); +int cnss_sdio_set_wlan_mac_address(const u8 *in, uint32_t len); + +u8 *cnss_pci_get_wlan_mac_address(uint32_t *num); +u8 *cnss_sdio_get_wlan_mac_address(uint32_t *num); +#endif /* _NET_CNSS_COMMON_H_ */ diff --git a/drivers/net/wireless/cnss/cnss_pci.c b/drivers/net/wireless/cnss/cnss_pci.c index 322de8b58cd5..daa7f65690db 100644 --- a/drivers/net/wireless/cnss/cnss_pci.c +++ b/drivers/net/wireless/cnss/cnss_pci.c @@ -48,7 +48,7 @@ #include <net/cfg80211.h> #include <soc/qcom/memory_dump.h> #include <net/cnss.h> -#include <net/cnss_common.h> +#include "cnss_common.h" #ifdef CONFIG_WCNSS_MEM_PRE_ALLOC #include <net/cnss_prealloc.h> @@ -2059,7 +2059,6 @@ end: *num = 0; return NULL; } -EXPORT_SYMBOL(cnss_pci_get_wlan_mac_address); /** * cnss_get_wlan_mac_address() - API to return MAC addresses buffer @@ -2149,7 +2148,6 @@ int cnss_pcie_set_wlan_mac_address(const u8 *in, uint32_t len) } return 0; } -EXPORT_SYMBOL(cnss_pcie_set_wlan_mac_address); int cnss_wlan_register_driver(struct cnss_wlan_driver *driver) { diff --git a/drivers/net/wireless/cnss/cnss_sdio.c b/drivers/net/wireless/cnss/cnss_sdio.c index e8833e499bcd..bb64e2149742 100644 --- a/drivers/net/wireless/cnss/cnss_sdio.c +++ b/drivers/net/wireless/cnss/cnss_sdio.c @@ -27,7 +27,7 @@ #include <soc/qcom/ramdump.h> #include <soc/qcom/memory_dump.h> #include <net/cnss.h> -#include <net/cnss_common.h> +#include "cnss_common.h" #include <linux/pm_qos.h> #include <linux/msm-bus.h> #include <linux/msm-bus-board.h> @@ -1161,14 +1161,12 @@ int cnss_sdio_set_wlan_mac_address(const u8 *in, uint32_t len) { return 0; } -EXPORT_SYMBOL(cnss_sdio_set_wlan_mac_address); u8 *cnss_sdio_get_wlan_mac_address(uint32_t *num) { *num = 0; return NULL; } -EXPORT_SYMBOL(cnss_sdio_get_wlan_mac_address); static const struct of_device_id cnss_sdio_dt_match[] = { {.compatible = "qcom,cnss_sdio"}, diff --git a/drivers/platform/msm/ipa/ipa_rm.c b/drivers/platform/msm/ipa/ipa_rm.c index a9813311f156..bf6352452283 100644 --- a/drivers/platform/msm/ipa/ipa_rm.c +++ b/drivers/platform/msm/ipa/ipa_rm.c @@ -986,6 +986,8 @@ int ipa_rm_stat(char *buf, int size) unsigned long flags; int i, cnt = 0, result = EINVAL; struct ipa_rm_resource *resource = NULL; + u32 sum_bw_prod = 0; + u32 sum_bw_cons = 0; if (!buf || size < 0) return result; @@ -1005,6 +1007,24 @@ int ipa_rm_stat(char *buf, int size) cnt += result; } } + + for (i = 0; i < IPA_RM_RESOURCE_PROD_MAX; i++) + sum_bw_prod += ipa_rm_ctx->prof_vote.bw_prods[i]; + + for (i = 0; i < IPA_RM_RESOURCE_CONS_MAX; i++) + sum_bw_cons += ipa_rm_ctx->prof_vote.bw_cons[i]; + + result = scnprintf(buf + cnt, size - cnt, + "All prod bandwidth: %d, All cons bandwidth: %d\n", + sum_bw_prod, sum_bw_cons); + cnt += result; + + result = scnprintf(buf + cnt, size - cnt, + "Voting: voltage %d, bandwidth %d\n", + ipa_rm_ctx->prof_vote.curr_volt, + ipa_rm_ctx->prof_vote.curr_bw); + cnt += result; + result = cnt; bail: spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags); diff --git a/drivers/platform/msm/ipa/ipa_rm_resource.c b/drivers/platform/msm/ipa/ipa_rm_resource.c index cc00f8cf7fb6..0a3f66307eee 100644 --- a/drivers/platform/msm/ipa/ipa_rm_resource.c +++ b/drivers/platform/msm/ipa/ipa_rm_resource.c @@ -1133,7 +1133,7 @@ int ipa_rm_resource_producer_print_stat( nbytes = scnprintf(buf + cnt, size - cnt, ipa_rm_resource_str(resource->name)); cnt += nbytes; - nbytes = scnprintf(buf + cnt, size - cnt, "["); + nbytes = scnprintf(buf + cnt, size - cnt, "[%d, ", resource->max_bw); cnt += nbytes; switch (resource->state) { @@ -1170,7 +1170,8 @@ int ipa_rm_resource_producer_print_stat( nbytes = scnprintf(buf + cnt, size - cnt, ipa_rm_resource_str(consumer->name)); cnt += nbytes; - nbytes = scnprintf(buf + cnt, size - cnt, "["); + nbytes = scnprintf(buf + cnt, size - cnt, "[%d, ", + consumer->max_bw); cnt += nbytes; switch (consumer->state) { diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c index 747ae3d82b68..c553be1ad717 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c @@ -218,6 +218,7 @@ static struct { bool disable_htw; bool fast_map; bool s1_bypass; + bool use_64_bit_dma_mask; u32 ipa_base; u32 ipa_size; } smmu_info; @@ -3961,7 +3962,7 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p, goto fail_bind; } - result = ipa3_init_mem_partition(ipa_dev->of_node); + result = ipa3_init_mem_partition(master_dev->of_node); if (result) { IPAERR(":ipa3_init_mem_partition failed!\n"); result = -ENODEV; @@ -4715,7 +4716,7 @@ static int ipa_smmu_uc_cb_probe(struct device *dev) cb->va_end = cb->va_start + cb->va_size; IPADBG("UC va_start=0x%x va_sise=0x%x\n", cb->va_start, cb->va_size); - if (ipa3_ctx->use_64_bit_dma_mask) { + if (smmu_info.use_64_bit_dma_mask) { if (dma_set_mask(dev, DMA_BIT_MASK(64)) || dma_set_coherent_mask(dev, DMA_BIT_MASK(64))) { IPAERR("DMA set 64bit mask failed\n"); @@ -4826,7 +4827,7 @@ static int ipa_smmu_ap_cb_probe(struct device *dev) cb->va_end = cb->va_start + cb->va_size; IPADBG("AP va_start=0x%x va_sise=0x%x\n", cb->va_start, cb->va_size); - if (ipa3_ctx->use_64_bit_dma_mask) { + if (smmu_info.use_64_bit_dma_mask) { if (dma_set_mask(dev, DMA_BIT_MASK(64)) || dma_set_coherent_mask(dev, DMA_BIT_MASK(64))) { IPAERR("DMA set 64bit mask failed\n"); @@ -5035,6 +5036,9 @@ int ipa3_plat_drv_probe(struct platform_device *pdev_p, if (of_property_read_bool(pdev_p->dev.of_node, "qcom,smmu-fast-map")) smmu_info.fast_map = true; + if (of_property_read_bool(pdev_p->dev.of_node, + "qcom,use-64-bit-dma-mask")) + smmu_info.use_64_bit_dma_mask = true; smmu_info.arm_smmu = true; pr_info("IPA smmu_info.s1_bypass=%d smmu_info.fast_map=%d\n", smmu_info.s1_bypass, smmu_info.fast_map); @@ -5059,16 +5063,16 @@ int ipa3_plat_drv_probe(struct platform_device *pdev_p, return -EOPNOTSUPP; } } - } - if (!ipa3_bus_scale_table) - ipa3_bus_scale_table = msm_bus_cl_get_pdata(pdev_p); + if (!ipa3_bus_scale_table) + ipa3_bus_scale_table = msm_bus_cl_get_pdata(pdev_p); - /* Proceed to real initialization */ - result = ipa3_pre_init(&ipa3_res, dev); - if (result) { - IPAERR("ipa3_init failed\n"); - return result; + /* Proceed to real initialization */ + result = ipa3_pre_init(&ipa3_res, dev); + if (result) { + IPAERR("ipa3_init failed\n"); + return result; + } } return result; diff --git a/drivers/power/qcom-charger/qpnp-smb2.c b/drivers/power/qcom-charger/qpnp-smb2.c index dbdcd9026541..3c934cb0b1f9 100644 --- a/drivers/power/qcom-charger/qpnp-smb2.c +++ b/drivers/power/qcom-charger/qpnp-smb2.c @@ -145,6 +145,7 @@ static enum power_supply_property smb2_usb_props[] = { POWER_SUPPLY_PROP_TYPEC_CC_ORIENTATION, POWER_SUPPLY_PROP_PD_ALLOWED, POWER_SUPPLY_PROP_PD_ACTIVE, + POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED, }; static int smb2_usb_get_prop(struct power_supply *psy, @@ -201,6 +202,9 @@ static int smb2_usb_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_PD_ACTIVE: val->intval = chg->pd_active; break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED: + rc = smblib_get_prop_input_current_settled(chg, val); + break; default: pr_err("get prop %d is not supported\n", psp); rc = -EINVAL; diff --git a/drivers/power/qcom-charger/smb-lib.c b/drivers/power/qcom-charger/smb-lib.c index 46b824376847..a7bda329cadd 100644 --- a/drivers/power/qcom-charger/smb-lib.c +++ b/drivers/power/qcom-charger/smb-lib.c @@ -783,22 +783,16 @@ int smblib_get_prop_batt_health(struct smb_charger *chg, goto done; } - switch (stat & BAT_TEMP_STATUS_MASK) { - case BAT_TEMP_STATUS_TOO_COLD_BIT: + if (stat & BAT_TEMP_STATUS_TOO_COLD_BIT) val->intval = POWER_SUPPLY_HEALTH_COLD; - break; - case BAT_TEMP_STATUS_TOO_HOT_BIT: + else if (stat & BAT_TEMP_STATUS_TOO_HOT_BIT) val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; - break; - case BAT_TEMP_STATUS_COLD_SOFT_LIMIT_BIT: + else if (stat & BAT_TEMP_STATUS_COLD_SOFT_LIMIT_BIT) val->intval = POWER_SUPPLY_HEALTH_COOL; - break; - case BAT_TEMP_STATUS_HOT_SOFT_LIMIT_BIT: + else if (stat & BAT_TEMP_STATUS_HOT_SOFT_LIMIT_BIT) val->intval = POWER_SUPPLY_HEALTH_WARM; - break; - default: + else val->intval = POWER_SUPPLY_HEALTH_GOOD; - } done: return rc; @@ -1066,6 +1060,12 @@ int smblib_get_prop_pd_allowed(struct smb_charger *chg, return 0; } +int smblib_get_prop_input_current_settled(struct smb_charger *chg, + union power_supply_propval *val) +{ + return smblib_get_charge_param(chg, &chg->param.icl_stat, &val->intval); +} + /******************* * USB PSY SETTERS * * *****************/ diff --git a/drivers/power/qcom-charger/smb-lib.h b/drivers/power/qcom-charger/smb-lib.h index fbadf02d9958..fe75e0625230 100644 --- a/drivers/power/qcom-charger/smb-lib.h +++ b/drivers/power/qcom-charger/smb-lib.h @@ -182,6 +182,8 @@ int smblib_get_prop_typec_power_role(struct smb_charger *chg, union power_supply_propval *val); int smblib_get_prop_pd_allowed(struct smb_charger *chg, union power_supply_propval *val); +int smblib_get_prop_input_current_settled(struct smb_charger *chg, + union power_supply_propval *val); int smblib_set_prop_usb_current_max(struct smb_charger *chg, const union power_supply_propval *val); int smblib_set_prop_usb_voltage_min(struct smb_charger *chg, diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index f4b63ec4b325..807349533dd1 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -97,6 +97,16 @@ } while (0) #endif +enum icnss_debug_quirks { + HW_ALWAY_ON, + HW_DEBUG_ENABLE, +}; + +#define ICNSS_QUIRKS_DEFAULT 0 + +unsigned long quirks = ICNSS_QUIRKS_DEFAULT; +module_param(quirks, ulong, 0600); + void *icnss_ipc_log_context; enum icnss_driver_event_type { @@ -123,6 +133,7 @@ enum icnss_driver_state { ICNSS_FW_READY, ICNSS_DRIVER_PROBED, ICNSS_FW_TEST_MODE, + ICNSS_SUSPEND, }; struct ce_irq_list { @@ -783,6 +794,8 @@ static int wlfw_wlan_mode_send_sync_msg(enum wlfw_driver_mode_enum_v01 mode) memset(&resp, 0, sizeof(resp)); req.mode = mode; + req.hw_debug_valid = 1; + req.hw_debug = !!test_bit(HW_DEBUG_ENABLE, &quirks); req_desc.max_msg_len = WLFW_WLAN_MODE_REQ_MSG_V01_MAX_MSG_LEN; req_desc.msg_id = QMI_WLFW_WLAN_MODE_REQ_V01; @@ -1990,6 +2003,9 @@ static int icnss_stats_show_state(struct seq_file *s, struct icnss_data *priv) case ICNSS_FW_TEST_MODE: seq_puts(s, "FW TEST MODE"); continue; + case ICNSS_SUSPEND: + seq_puts(s, "DRIVER SUSPENDED"); + continue; } seq_printf(s, "UNKNOWN-%d", i); @@ -2350,6 +2366,54 @@ static int icnss_remove(struct platform_device *pdev) return 0; } +static int icnss_suspend(struct platform_device *pdev, + pm_message_t state) +{ + int ret = 0; + + if (!penv) { + ret = -ENODEV; + goto out; + } + + icnss_pr_dbg("Driver suspending, state: 0x%lx\n", + penv->state); + + if (!penv->ops) + goto out; + + if (penv->ops->suspend) + ret = penv->ops->suspend(&pdev->dev, state); + +out: + if (ret == 0) + set_bit(ICNSS_SUSPEND, &penv->state); + return ret; +} + +static int icnss_resume(struct platform_device *pdev) +{ + int ret = 0; + + if (!penv) { + ret = -ENODEV; + goto out; + } + + icnss_pr_dbg("Driver resuming, state: 0x%lx\n", + penv->state); + + if (!penv->ops) + goto out; + + if (penv->ops->resume) + ret = penv->ops->resume(&pdev->dev); + +out: + if (ret == 0) + clear_bit(ICNSS_SUSPEND, &penv->state); + return ret; +} static const struct of_device_id icnss_dt_match[] = { {.compatible = "qcom,icnss"}, @@ -2361,6 +2425,8 @@ MODULE_DEVICE_TABLE(of, icnss_dt_match); static struct platform_driver icnss_driver = { .probe = icnss_probe, .remove = icnss_remove, + .suspend = icnss_suspend, + .resume = icnss_resume, .driver = { .name = "icnss", .owner = THIS_MODULE, diff --git a/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c b/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c index 14e46b38c6df..7e5f1661932f 100644 --- a/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c +++ b/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c @@ -330,6 +330,7 @@ struct apr_svc_ch_dev *apr_tal_open(uint32_t clnt, uint32_t dest, uint32_t dl, if (IS_ERR_OR_NULL(apr_ch->handle)) { pr_err("%s: glink_open failed %s\n", __func__, svc_names[dest][clnt]); + apr_ch->handle = NULL; goto unlock; } diff --git a/drivers/soc/qcom/wlan_firmware_service_v01.c b/drivers/soc/qcom/wlan_firmware_service_v01.c index e899459a5a40..b33575116c18 100644 --- a/drivers/soc/qcom/wlan_firmware_service_v01.c +++ b/drivers/soc/qcom/wlan_firmware_service_v01.c @@ -462,6 +462,24 @@ struct elem_info wlfw_wlan_mode_req_msg_v01_ei[] = { mode), }, { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wlfw_wlan_mode_req_msg_v01, + hw_debug_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wlfw_wlan_mode_req_msg_v01, + hw_debug), + }, + { .data_type = QMI_EOTI, .is_array = NO_ARRAY, .is_array = QMI_COMMON_TLV_TYPE, diff --git a/drivers/soc/qcom/wlan_firmware_service_v01.h b/drivers/soc/qcom/wlan_firmware_service_v01.h index 6e96cbabd9d8..bf47da7cdecb 100644 --- a/drivers/soc/qcom/wlan_firmware_service_v01.h +++ b/drivers/soc/qcom/wlan_firmware_service_v01.h @@ -180,8 +180,10 @@ extern struct elem_info wlfw_pin_connect_result_ind_msg_v01_ei[]; struct wlfw_wlan_mode_req_msg_v01 { enum wlfw_driver_mode_enum_v01 mode; + uint8_t hw_debug_valid; + uint8_t hw_debug; }; -#define WLFW_WLAN_MODE_REQ_MSG_V01_MAX_MSG_LEN 7 +#define WLFW_WLAN_MODE_REQ_MSG_V01_MAX_MSG_LEN 11 extern struct elem_info wlfw_wlan_mode_req_msg_v01_ei[]; struct wlfw_wlan_mode_resp_msg_v01 { diff --git a/drivers/thermal/msm_lmh_dcvs.c b/drivers/thermal/msm_lmh_dcvs.c index 3c37f0728481..cd45eeccbfe7 100644 --- a/drivers/thermal/msm_lmh_dcvs.c +++ b/drivers/thermal/msm_lmh_dcvs.c @@ -44,11 +44,13 @@ #define MSM_LIMITS_ALGO_MODE_ENABLE 0x454E424C #define MSM_LIMITS_HI_THRESHOLD 0x48494748 -#define MSM_LIMITS_LO_THRESHOLD 0x4C4F5700 +#define MSM_LIMITS_ARM_THRESHOLD 0x41524D00 #define MSM_LIMITS_CLUSTER_0 0x6370302D #define MSM_LIMITS_CLUSTER_1 0x6370312D +#define MSM_LIMITS_HIGH_THRESHOLD_VAL 95000 +#define MSM_LIMITS_ARM_THRESHOLD_VAL 65000 #define MSM_LIMITS_POLLING_DELAY_MS 10 #define MSM_LIMITS_CLUSTER_0_REQ 0x179C1B04 #define MSM_LIMITS_CLUSTER_1_REQ 0x179C3B04 @@ -203,7 +205,7 @@ static int lmh_activate_trip(struct thermal_zone_device *dev, hw->temp_limits[LIMITS_TRIP_HI]) return -EINVAL; - thresh = (trip == LIMITS_TRIP_LO) ? MSM_LIMITS_LO_THRESHOLD : + thresh = (trip == LIMITS_TRIP_LO) ? MSM_LIMITS_ARM_THRESHOLD : MSM_LIMITS_HI_THRESHOLD; temp = hw->temp_limits[trip]; @@ -306,11 +308,11 @@ static int msm_lmh_dcvs_probe(struct platform_device *pdev) if (ret) return ret; - hw->default_lo.temp = 65000; + hw->default_lo.temp = MSM_LIMITS_ARM_THRESHOLD_VAL; hw->default_lo.trip = THERMAL_TRIP_CONFIGURABLE_LOW; hw->default_lo.notify = trip_notify; - hw->default_hi.temp = 95000; + hw->default_hi.temp = MSM_LIMITS_HIGH_THRESHOLD_VAL; hw->default_hi.trip = THERMAL_TRIP_CONFIGURABLE_HI; hw->default_hi.notify = trip_notify; diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 9b2b98cf1823..5cc655908fda 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2202,8 +2202,65 @@ int usb_hcd_get_frame_number (struct usb_device *udev) return hcd->driver->get_frame_number (hcd); } +int usb_hcd_sec_event_ring_setup(struct usb_device *udev, + unsigned intr_num) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + if (!HCD_RH_RUNNING(hcd)) + return 0; + + return hcd->driver->sec_event_ring_setup(hcd, intr_num); +} + +int usb_hcd_sec_event_ring_cleanup(struct usb_device *udev, + unsigned intr_num) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + if (!HCD_RH_RUNNING(hcd)) + return 0; + + return hcd->driver->sec_event_ring_cleanup(hcd, intr_num); +} + /*-------------------------------------------------------------------------*/ +dma_addr_t +usb_hcd_get_sec_event_ring_dma_addr(struct usb_device *udev, + unsigned intr_num) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + if (!HCD_RH_RUNNING(hcd)) + return 0; + + return hcd->driver->get_sec_event_ring_dma_addr(hcd, intr_num); +} + +dma_addr_t +usb_hcd_get_dcba_dma_addr(struct usb_device *udev) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + if (!HCD_RH_RUNNING(hcd)) + return 0; + + return hcd->driver->get_dcba_dma_addr(hcd, udev); +} + +dma_addr_t +usb_hcd_get_xfer_ring_dma_addr(struct usb_device *udev, + struct usb_host_endpoint *ep) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + if (!HCD_RH_RUNNING(hcd)) + return 0; + + return hcd->driver->get_xfer_ring_dma_addr(hcd, udev, ep); +} + #ifdef CONFIG_PM int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg) diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index f8bbd0b6d9fe..b43d542e3bd4 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -669,6 +669,57 @@ int usb_get_current_frame_number(struct usb_device *dev) } EXPORT_SYMBOL_GPL(usb_get_current_frame_number); +int usb_sec_event_ring_setup(struct usb_device *dev, + unsigned intr_num) +{ + if (dev->state == USB_STATE_NOTATTACHED) + return 0; + + return usb_hcd_sec_event_ring_setup(dev, intr_num); +} +EXPORT_SYMBOL(usb_sec_event_ring_setup); + +int usb_sec_event_ring_cleanup(struct usb_device *dev, + unsigned intr_num) +{ + if (dev->state == USB_STATE_NOTATTACHED) + return 0; + + return usb_hcd_sec_event_ring_cleanup(dev, intr_num); +} +EXPORT_SYMBOL(usb_sec_event_ring_cleanup); + +dma_addr_t +usb_get_sec_event_ring_dma_addr(struct usb_device *dev, + unsigned intr_num) +{ + if (dev->state == USB_STATE_NOTATTACHED) + return 0; + + return usb_hcd_get_sec_event_ring_dma_addr(dev, intr_num); +} +EXPORT_SYMBOL(usb_get_sec_event_ring_dma_addr); + +dma_addr_t +usb_get_dcba_dma_addr(struct usb_device *dev) +{ + if (dev->state == USB_STATE_NOTATTACHED) + return 0; + + return usb_hcd_get_dcba_dma_addr(dev); +} +EXPORT_SYMBOL(usb_get_dcba_dma_addr); + +dma_addr_t usb_get_xfer_ring_dma_addr(struct usb_device *dev, + struct usb_host_endpoint *ep) +{ + if (dev->state == USB_STATE_NOTATTACHED) + return 0; + + return usb_hcd_get_xfer_ring_dma_addr(dev, ep); +} +EXPORT_SYMBOL(usb_get_xfer_ring_dma_addr); + /*-------------------------------------------------------------------*/ /* * __usb_get_extra_descriptor() finds a descriptor of specific type in the diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index a395097a2098..7585c603cb3d 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1021,8 +1021,8 @@ static int dwc3_probe(struct platform_device *pdev) /* will be enabled in dwc3_msm_resume() */ irq_set_status_flags(irq, IRQ_NOAUTOEN); - ret = devm_request_irq(dev, irq, dwc3_interrupt, IRQF_SHARED, "dwc3", - dwc); + ret = devm_request_threaded_irq(dev, irq, NULL, dwc3_interrupt, + IRQF_SHARED | IRQF_ONESHOT, "dwc3", dwc); if (ret) { dev_err(dwc->dev, "failed to request irq #%d --> %d\n", irq, ret); diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c index d79e85979e40..468a7bcd8dbd 100644 --- a/drivers/usb/gadget/function/f_gsi.c +++ b/drivers/usb/gadget/function/f_gsi.c @@ -2459,12 +2459,12 @@ static int gsi_bind(struct usb_configuration *c, struct usb_function *f) info.ss_desc_hdr = ecm_gsi_ss_function; info.in_epname = "gsi-epin"; info.out_epname = "gsi-epout"; - gsi->d_port.in_aggr_size = GSI_IN_ECM_AGGR_SIZE; + gsi->d_port.in_aggr_size = GSI_ECM_AGGR_SIZE; info.in_req_buf_len = GSI_IN_BUFF_SIZE; info.in_req_num_buf = num_in_bufs; - gsi->d_port.out_aggr_size = GSI_OUT_AGGR_SIZE; + gsi->d_port.out_aggr_size = GSI_ECM_AGGR_SIZE; info.out_req_buf_len = GSI_OUT_ECM_BUF_LEN; - info.out_req_num_buf = num_out_bufs; + info.out_req_num_buf = GSI_ECM_NUM_OUT_BUFFERS; info.notify_buf_len = GSI_CTRL_NOTIFY_BUFF_LEN; /* export host's Ethernet address in CDC format */ @@ -2574,11 +2574,16 @@ static void gsi_unbind(struct usb_configuration *c, struct usb_function *f) if (gsi->prot_id == IPA_USB_MBIM) mbim_gsi_ext_config_desc.function.subCompatibleID[0] = 0; - if (gadget_is_superspeed(c->cdev->gadget)) + if (gadget_is_superspeed(c->cdev->gadget)) { usb_free_descriptors(f->ss_descriptors); - if (gadget_is_dualspeed(c->cdev->gadget)) + f->ss_descriptors = NULL; + } + if (gadget_is_dualspeed(c->cdev->gadget)) { usb_free_descriptors(f->hs_descriptors); + f->hs_descriptors = NULL; + } usb_free_descriptors(f->fs_descriptors); + f->fs_descriptors = NULL; if (gsi->c_port.notify) { kfree(gsi->c_port.notify_req->buf); diff --git a/drivers/usb/gadget/function/f_gsi.h b/drivers/usb/gadget/function/f_gsi.h index eb42feff0712..d56012779c78 100644 --- a/drivers/usb/gadget/function/f_gsi.h +++ b/drivers/usb/gadget/function/f_gsi.h @@ -38,12 +38,13 @@ #define GSI_NUM_IN_BUFFERS 7 #define GSI_IN_BUFF_SIZE 2048 #define GSI_NUM_OUT_BUFFERS 7 +#define GSI_ECM_NUM_OUT_BUFFERS 31 #define GSI_OUT_AGGR_SIZE 24576 #define GSI_IN_RNDIS_AGGR_SIZE 9216 #define GSI_IN_MBIM_AGGR_SIZE 16384 #define GSI_IN_RMNET_AGGR_SIZE 16384 -#define GSI_IN_ECM_AGGR_SIZE 2048 +#define GSI_ECM_AGGR_SIZE 2048 #define GSI_OUT_MBIM_BUF_LEN 16384 #define GSI_OUT_RMNET_BUF_LEN 16384 diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index c48cbe731356..2ac142e3cce9 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1786,25 +1786,73 @@ void xhci_free_command(struct xhci_hcd *xhci, kfree(command); } -void xhci_mem_cleanup(struct xhci_hcd *xhci) +int xhci_sec_event_ring_cleanup(struct usb_hcd *hcd, unsigned intr_num) { + int size; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); struct device *dev = xhci_to_hcd(xhci)->self.controller; + + if (intr_num > xhci->max_interrupters) { + xhci_err(xhci, "invalid secondary interrupter num %d\n", + intr_num); + return -EINVAL; + } + + size = + sizeof(struct xhci_erst_entry)*(xhci->sec_erst[intr_num].num_entries); + if (xhci->sec_erst[intr_num].entries) + dma_free_coherent(dev, size, xhci->sec_erst[intr_num].entries, + xhci->sec_erst[intr_num].erst_dma_addr); + xhci->sec_erst[intr_num].entries = NULL; + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed SEC ERST#%d", + intr_num); + if (xhci->sec_event_ring[intr_num]) + xhci_ring_free(xhci, xhci->sec_event_ring[intr_num]); + xhci->sec_event_ring[intr_num] = NULL; + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "Freed sec event ring"); + + return 0; +} + +void xhci_event_ring_cleanup(struct xhci_hcd *xhci) +{ int size; - int i, j, num_ports; + unsigned int i; + struct device *dev = xhci_to_hcd(xhci)->self.controller; - del_timer_sync(&xhci->cmd_timer); + /* sec event ring clean up */ + for (i = 1; i <= xhci->max_interrupters; i++) + xhci_sec_event_ring_cleanup(xhci_to_hcd(xhci), i); - /* Free the Event Ring Segment Table and the actual Event Ring */ + kfree(xhci->sec_ir_set); + xhci->sec_ir_set = NULL; + kfree(xhci->sec_erst); + xhci->sec_erst = NULL; + kfree(xhci->sec_event_ring); + xhci->sec_event_ring = NULL; + + /* primary event ring clean up */ size = sizeof(struct xhci_erst_entry)*(xhci->erst.num_entries); if (xhci->erst.entries) dma_free_coherent(dev, size, xhci->erst.entries, xhci->erst.erst_dma_addr); xhci->erst.entries = NULL; - xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed ERST"); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed primary ERST"); if (xhci->event_ring) xhci_ring_free(xhci, xhci->event_ring); xhci->event_ring = NULL; - xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed event ring"); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed priamry event ring"); +} + +void xhci_mem_cleanup(struct xhci_hcd *xhci) +{ + struct device *dev = xhci_to_hcd(xhci)->self.controller; + int i, j, num_ports; + + del_timer_sync(&xhci->cmd_timer); + + xhci_event_ring_cleanup(xhci); if (xhci->lpm_command) xhci_free_command(xhci, xhci->lpm_command); @@ -2039,30 +2087,6 @@ static int xhci_check_trb_in_td_math(struct xhci_hcd *xhci) return 0; } -static void xhci_set_hc_event_deq(struct xhci_hcd *xhci) -{ - u64 temp; - dma_addr_t deq; - - deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, - xhci->event_ring->dequeue); - if (deq == 0 && !in_interrupt()) - xhci_warn(xhci, "WARN something wrong with SW event ring " - "dequeue ptr.\n"); - /* Update HC event ring dequeue pointer */ - temp = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); - temp &= ERST_PTR_MASK; - /* Don't clear the EHB bit (which is RW1C) because - * there might be more events to service. - */ - temp &= ~ERST_EHB; - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "// Write event ring dequeue pointer, " - "preserving EHB bit"); - xhci_write_64(xhci, ((u64) deq & (u64) ~ERST_PTR_MASK) | temp, - &xhci->ir_set->erst_dequeue); -} - static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, __le32 __iomem *addr, u8 major_revision, int max_caps) { @@ -2340,13 +2364,184 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) return 0; } +int xhci_event_ring_setup(struct xhci_hcd *xhci, struct xhci_ring **er, + struct xhci_intr_reg __iomem *ir_set, struct xhci_erst *erst, + unsigned int intr_num, gfp_t flags) +{ + dma_addr_t dma, deq; + u64 val_64; + unsigned int val; + struct xhci_segment *seg; + struct device *dev = xhci_to_hcd(xhci)->self.controller; + + *er = xhci_ring_alloc(xhci, ERST_NUM_SEGS, 1, + TYPE_EVENT, flags); + if (!*er) + return -ENOMEM; + + erst->entries = dma_alloc_coherent(dev, + sizeof(struct xhci_erst_entry) * ERST_NUM_SEGS, &dma, + flags); + if (!erst->entries) { + xhci_ring_free(xhci, *er); + return -ENOMEM; + } + + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "intr# %d: Allocated event ring segment table at 0x%llx", + intr_num, (unsigned long long)dma); + + memset(erst->entries, 0, sizeof(struct xhci_erst_entry)*ERST_NUM_SEGS); + erst->num_entries = ERST_NUM_SEGS; + erst->erst_dma_addr = dma; + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "intr# %d: num segs = %i, virt addr = %p, dma addr = 0x%llx", + intr_num, + erst->num_entries, + erst->entries, + (unsigned long long)erst->erst_dma_addr); + + /* set ring base address and size for each segment table entry */ + for (val = 0, seg = (*er)->first_seg; val < ERST_NUM_SEGS; val++) { + struct xhci_erst_entry *entry = &erst->entries[val]; + + entry->seg_addr = cpu_to_le64(seg->dma); + entry->seg_size = cpu_to_le32(TRBS_PER_SEGMENT); + entry->rsvd = 0; + seg = seg->next; + } + + /* set ERST count with the number of entries in the segment table */ + val = readl_relaxed(&ir_set->erst_size); + val &= ERST_SIZE_MASK; + val |= ERST_NUM_SEGS; + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "Write ERST size = %i to ir_set %d (some bits preserved)", val, + intr_num); + writel_relaxed(val, &ir_set->erst_size); + + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "intr# %d: Set ERST entries to point to event ring.", + intr_num); + /* set the segment table base address */ + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "Set ERST base address for ir_set %d = 0x%llx", + intr_num, + (unsigned long long)erst->erst_dma_addr); + val_64 = xhci_read_64(xhci, &ir_set->erst_base); + val_64 &= ERST_PTR_MASK; + val_64 |= (erst->erst_dma_addr & (u64) ~ERST_PTR_MASK); + xhci_write_64(xhci, val_64, &ir_set->erst_base); + + /* Set the event ring dequeue address */ + deq = xhci_trb_virt_to_dma((*er)->deq_seg, (*er)->dequeue); + if (deq == 0 && !in_interrupt()) + xhci_warn(xhci, + "intr# %d:WARN something wrong with SW event ring deq ptr.\n", + intr_num); + /* Update HC event ring dequeue pointer */ + val_64 = xhci_read_64(xhci, &ir_set->erst_dequeue); + val_64 &= ERST_PTR_MASK; + /* Don't clear the EHB bit (which is RW1C) because + * there might be more events to service. + */ + val_64 &= ~ERST_EHB; + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "intr# %d:Write event ring dequeue pointer, preserving EHB bit", + intr_num); + xhci_write_64(xhci, ((u64) deq & (u64) ~ERST_PTR_MASK) | val_64, + &ir_set->erst_dequeue); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "Wrote ERST address to ir_set %d.", intr_num); + xhci_print_ir_set(xhci, intr_num); + + return 0; +} + +int xhci_sec_event_ring_setup(struct usb_hcd *hcd, unsigned intr_num) +{ + int ret; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + + if ((xhci->xhc_state & XHCI_STATE_HALTED) || !xhci->sec_ir_set + || !xhci->sec_event_ring || !xhci->sec_erst || + intr_num > xhci->max_interrupters) { + xhci_err(xhci, + "%s:state %x ir_set %p evt_ring %p erst %p intr# %d\n", + __func__, xhci->xhc_state, xhci->sec_ir_set, + xhci->sec_event_ring, xhci->sec_erst, intr_num); + return -EINVAL; + } + + if (xhci->sec_event_ring && xhci->sec_event_ring[intr_num] + && xhci->sec_event_ring[intr_num]->first_seg) + goto done; + + xhci->sec_ir_set[intr_num] = &xhci->run_regs->ir_set[intr_num]; + ret = xhci_event_ring_setup(xhci, + &xhci->sec_event_ring[intr_num], + xhci->sec_ir_set[intr_num], + &xhci->sec_erst[intr_num], + intr_num, GFP_KERNEL); + if (ret) { + xhci_err(xhci, "sec event ring setup failed inter#%d\n", + intr_num); + return ret; + } +done: + return 0; +} + +int xhci_event_ring_init(struct xhci_hcd *xhci, gfp_t flags) +{ + int ret = 0; + + /* primary + secondary */ + xhci->max_interrupters = HCS_MAX_INTRS(xhci->hcs_params1); + + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "// Allocating primary event ring"); + + /* Set ir_set to interrupt register set 0 */ + xhci->ir_set = &xhci->run_regs->ir_set[0]; + ret = xhci_event_ring_setup(xhci, &xhci->event_ring, xhci->ir_set, + &xhci->erst, 0, flags); + if (ret) { + xhci_err(xhci, "failed to setup primary event ring\n"); + goto fail; + } + + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "// Allocating sec event ring related pointers"); + + xhci->sec_ir_set = kcalloc(xhci->max_interrupters, + sizeof(*xhci->sec_ir_set), flags); + if (!xhci->sec_ir_set) { + ret = -ENOMEM; + goto fail; + } + + xhci->sec_event_ring = kcalloc(xhci->max_interrupters, + sizeof(*xhci->sec_event_ring), flags); + if (!xhci->sec_event_ring) { + ret = -ENOMEM; + goto fail; + } + + xhci->sec_erst = kcalloc(xhci->max_interrupters, + sizeof(*xhci->sec_erst), flags); + if (!xhci->sec_erst) + ret = -ENOMEM; +fail: + return ret; +} + int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) { dma_addr_t dma; struct device *dev = xhci_to_hcd(xhci)->self.controller; unsigned int val, val2; u64 val_64; - struct xhci_segment *seg; u32 page_size, temp; int i; @@ -2472,73 +2667,16 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) xhci->dba = (void __iomem *) xhci->cap_regs + val; xhci_dbg_regs(xhci); xhci_print_run_regs(xhci); - /* Set ir_set to interrupt register set 0 */ - xhci->ir_set = &xhci->run_regs->ir_set[0]; /* * Event ring setup: Allocate a normal ring, but also setup * the event ring segment table (ERST). Section 4.9.3. */ - xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Allocating event ring"); - xhci->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, 1, TYPE_EVENT, - flags); - if (!xhci->event_ring) - goto fail; - if (xhci_check_trb_in_td_math(xhci) < 0) + if (xhci_event_ring_init(xhci, GFP_KERNEL)) goto fail; - xhci->erst.entries = dma_alloc_coherent(dev, - sizeof(struct xhci_erst_entry) * ERST_NUM_SEGS, &dma, - GFP_KERNEL); - if (!xhci->erst.entries) + if (xhci_check_trb_in_td_math(xhci) < 0) goto fail; - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "// Allocated event ring segment table at 0x%llx", - (unsigned long long)dma); - - memset(xhci->erst.entries, 0, sizeof(struct xhci_erst_entry)*ERST_NUM_SEGS); - xhci->erst.num_entries = ERST_NUM_SEGS; - xhci->erst.erst_dma_addr = dma; - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "Set ERST to 0; private num segs = %i, virt addr = %p, dma addr = 0x%llx", - xhci->erst.num_entries, - xhci->erst.entries, - (unsigned long long)xhci->erst.erst_dma_addr); - - /* set ring base address and size for each segment table entry */ - for (val = 0, seg = xhci->event_ring->first_seg; val < ERST_NUM_SEGS; val++) { - struct xhci_erst_entry *entry = &xhci->erst.entries[val]; - entry->seg_addr = cpu_to_le64(seg->dma); - entry->seg_size = cpu_to_le32(TRBS_PER_SEGMENT); - entry->rsvd = 0; - seg = seg->next; - } - - /* set ERST count with the number of entries in the segment table */ - val = readl(&xhci->ir_set->erst_size); - val &= ERST_SIZE_MASK; - val |= ERST_NUM_SEGS; - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "// Write ERST size = %i to ir_set 0 (some bits preserved)", - val); - writel(val, &xhci->ir_set->erst_size); - - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "// Set ERST entries to point to event ring."); - /* set the segment table base address */ - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "// Set ERST base address for ir_set 0 = 0x%llx", - (unsigned long long)xhci->erst.erst_dma_addr); - val_64 = xhci_read_64(xhci, &xhci->ir_set->erst_base); - val_64 &= ERST_PTR_MASK; - val_64 |= (xhci->erst.erst_dma_addr & (u64) ~ERST_PTR_MASK); - xhci_write_64(xhci, val_64, &xhci->ir_set->erst_base); - - /* Set the event ring dequeue address */ - xhci_set_hc_event_deq(xhci); - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "Wrote ERST address to ir_set 0."); - xhci_print_ir_set(xhci, 0); /* * XXX: Might need to set the Interrupter Moderation Register to diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 197757c20102..8125a8f96311 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -237,11 +237,11 @@ static int xhci_plat_probe(struct platform_device *pdev) goto put_usb3_hcd; } - ret = usb_add_hcd(hcd, irq, IRQF_SHARED); + ret = usb_add_hcd(hcd, irq, IRQF_SHARED | IRQF_ONESHOT); if (ret) goto disable_usb_phy; - ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED); + ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED | IRQF_ONESHOT); if (ret) goto dealloc_usb2_hcd; diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index e57340a86eb3..42f74d55e3bd 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -4968,6 +4968,61 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) } EXPORT_SYMBOL_GPL(xhci_gen_setup); +dma_addr_t xhci_get_sec_event_ring_dma_addr(struct usb_hcd *hcd, + unsigned intr_num) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + + if (intr_num > xhci->max_interrupters) { + xhci_err(xhci, "intr num %d > max intrs %d\n", intr_num, + xhci->max_interrupters); + return 0; + } + + if (!(xhci->xhc_state & XHCI_STATE_HALTED) && + xhci->sec_event_ring && xhci->sec_event_ring[intr_num] + && xhci->sec_event_ring[intr_num]->first_seg) + return xhci->sec_event_ring[intr_num]->first_seg->dma; + + return 0; +} + +dma_addr_t xhci_get_dcba_dma_addr(struct usb_hcd *hcd, + struct usb_device *udev) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + + if (!(xhci->xhc_state & XHCI_STATE_HALTED) && xhci->dcbaa) + return xhci->dcbaa->dev_context_ptrs[udev->slot_id]; + + return 0; +} + +dma_addr_t xhci_get_xfer_ring_dma_addr(struct usb_hcd *hcd, + struct usb_device *udev, struct usb_host_endpoint *ep) +{ + int ret; + unsigned int ep_index; + struct xhci_virt_device *virt_dev; + + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + + ret = xhci_check_args(hcd, udev, ep, 1, true, __func__); + if (ret <= 0) { + xhci_err(xhci, "%s: invalid args\n", __func__); + return 0; + } + + virt_dev = xhci->devs[udev->slot_id]; + ep_index = xhci_get_endpoint_index(&ep->desc); + + if (virt_dev->eps[ep_index].ring && + virt_dev->eps[ep_index].ring->first_seg) + return virt_dev->eps[ep_index].ring->first_seg->dma; + + return 0; +} + static const struct hc_driver xhci_hc_driver = { .description = "xhci-hcd", .product_desc = "xHCI Host Controller", @@ -5027,6 +5082,11 @@ static const struct hc_driver xhci_hc_driver = { .enable_usb3_lpm_timeout = xhci_enable_usb3_lpm_timeout, .disable_usb3_lpm_timeout = xhci_disable_usb3_lpm_timeout, .find_raw_port_number = xhci_find_raw_port_number, + .sec_event_ring_setup = xhci_sec_event_ring_setup, + .sec_event_ring_cleanup = xhci_sec_event_ring_cleanup, + .get_sec_event_ring_dma_addr = xhci_get_sec_event_ring_dma_addr, + .get_xfer_ring_dma_addr = xhci_get_xfer_ring_dma_addr, + .get_dcba_dma_addr = xhci_get_dcba_dma_addr, }; void xhci_init_driver(struct hc_driver *drv, diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 0b9451250e33..a6e94e029a10 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1514,6 +1514,9 @@ struct xhci_hcd { /* Our HCD's current interrupter register set */ struct xhci_intr_reg __iomem *ir_set; + /* secondary interrupter */ + struct xhci_intr_reg __iomem **sec_ir_set; + /* Cached register copies of read-only HC data */ __u32 hcs_params1; __u32 hcs_params2; @@ -1554,6 +1557,11 @@ struct xhci_hcd { struct xhci_command *current_cmd; struct xhci_ring *event_ring; struct xhci_erst erst; + + /* secondary event ring and erst */ + struct xhci_ring **sec_event_ring; + struct xhci_erst *sec_erst; + /* Scratchpad */ struct xhci_scratchpad *scratchpad; /* Store LPM test failed devices' information */ @@ -1811,6 +1819,8 @@ struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci, void xhci_urb_free_priv(struct urb_priv *urb_priv); void xhci_free_command(struct xhci_hcd *xhci, struct xhci_command *command); +int xhci_sec_event_ring_setup(struct usb_hcd *hcd, unsigned intr_num); +int xhci_sec_event_ring_cleanup(struct usb_hcd *hcd, unsigned intr_num); /* xHCI host controller glue */ typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *); diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index 5857f8ee8317..170f3ce3e881 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -26,7 +26,7 @@ obj-$(CONFIG_USB_QCOM_8X16_PHY) += phy-qcom-8x16-usb.o obj-$(CONFIG_USB_MSM_HSPHY) += phy-msm-hsusb.o obj-$(CONFIG_USB_MSM_SSPHY) += phy-msm-ssusb.o obj-$(CONFIG_USB_MSM_SSPHY_QMP) += phy-msm-ssusb-qmp.o -obj-$(CONFIG_MSM_QUSB_PHY) += phy-msm-qusb.o +obj-$(CONFIG_MSM_QUSB_PHY) += phy-msm-qusb.o phy-msm-qusb-v2.o obj-$(CONFIG_USB_MV_OTG) += phy-mv-usb.o obj-$(CONFIG_USB_MXS_PHY) += phy-mxs-usb.o obj-$(CONFIG_USB_RCAR_PHY) += phy-rcar-usb.o diff --git a/drivers/usb/phy/phy-msm-qusb-v2.c b/drivers/usb/phy/phy-msm-qusb-v2.c new file mode 100644 index 000000000000..1bc6b6ba4517 --- /dev/null +++ b/drivers/usb/phy/phy-msm-qusb-v2.c @@ -0,0 +1,955 @@ +/* + * 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 + * 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/err.h> +#include <linux/slab.h> +#include <linux/clk.h> +#include <linux/clk/msm-clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/regulator/consumer.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/usb/phy.h> +#include <linux/usb/msm_hsusb.h> + +#define QUSB2PHY_PWR_CTRL1 0x210 +#define PWR_CTRL1_POWR_DOWN BIT(0) + +#define QUSB2PHY_PLL_COMMON_STATUS_ONE 0x1A0 +#define CORE_READY_STATUS BIT(0) + +/* In case Efuse register shows zero, use this value */ +#define TUNE2_DEFAULT_HIGH_NIBBLE 0xB +#define TUNE2_DEFAULT_LOW_NIBBLE 0x3 + +/* Get TUNE2's high nibble value read from efuse */ +#define TUNE2_HIGH_NIBBLE_VAL(val, pos, mask) ((val >> pos) & mask) + +#define QUSB2PHY_INTR_CTRL 0x22C +#define DMSE_INTR_HIGH_SEL BIT(4) +#define DPSE_INTR_HIGH_SEL BIT(3) +#define CHG_DET_INTR_EN BIT(2) +#define DMSE_INTR_EN BIT(1) +#define DPSE_INTR_EN BIT(0) + +#define QUSB2PHY_INTR_STAT 0x230 +#define DMSE_INTERRUPT BIT(1) +#define DPSE_INTERRUPT BIT(0) + +#define QUSB2PHY_PORT_TUNE2 0x240 + +#define HS_PHY_CTRL_REG 0x10 +#define UTMI_OTG_VBUS_VALID BIT(20) +#define SW_SESSVLD_SEL BIT(28) + +#define QUSB2PHY_1P8_VOL_MIN 1800000 /* uV */ +#define QUSB2PHY_1P8_VOL_MAX 1800000 /* uV */ +#define QUSB2PHY_1P8_HPM_LOAD 30000 /* uA */ + +#define QUSB2PHY_3P3_VOL_MIN 3075000 /* uV */ +#define QUSB2PHY_3P3_VOL_MAX 3200000 /* uV */ +#define QUSB2PHY_3P3_HPM_LOAD 30000 /* uA */ + +unsigned int phy_tune2; +module_param(phy_tune2, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(phy_tune2, "QUSB PHY v2 TUNE2"); + +struct qusb_phy { + struct usb_phy phy; + void __iomem *base; + void __iomem *qscratch_base; + void __iomem *tune2_efuse_reg; + + struct clk *ref_clk_src; + struct clk *ref_clk; + struct clk *cfg_ahb_clk; + struct clk *phy_reset; + + struct regulator *vdd; + struct regulator *vdda33; + struct regulator *vdda18; + int vdd_levels[3]; /* none, low, high */ + int init_seq_len; + int *qusb_phy_init_seq; + + u32 tune2_val; + int tune2_efuse_bit_pos; + int tune2_efuse_num_of_bits; + + bool power_enabled; + bool clocks_enabled; + bool cable_connected; + bool suspended; + bool rm_pulldown; + + struct regulator_desc dpdm_rdesc; + struct regulator_dev *dpdm_rdev; + + /* emulation targets specific */ + void __iomem *emu_phy_base; + bool emulation; + int *emu_init_seq; + int emu_init_seq_len; + int *phy_pll_reset_seq; + int phy_pll_reset_seq_len; + int *emu_dcm_reset_seq; + int emu_dcm_reset_seq_len; +}; + +static void qusb_phy_enable_clocks(struct qusb_phy *qphy, bool on) +{ + dev_dbg(qphy->phy.dev, "%s(): clocks_enabled:%d on:%d\n", + __func__, qphy->clocks_enabled, on); + + if (!qphy->clocks_enabled && on) { + clk_prepare_enable(qphy->ref_clk_src); + clk_prepare_enable(qphy->ref_clk); + clk_prepare_enable(qphy->cfg_ahb_clk); + qphy->clocks_enabled = true; + } + + if (qphy->clocks_enabled && !on) { + clk_disable_unprepare(qphy->ref_clk); + clk_disable_unprepare(qphy->ref_clk_src); + clk_disable_unprepare(qphy->cfg_ahb_clk); + qphy->clocks_enabled = false; + } + + dev_dbg(qphy->phy.dev, "%s(): clocks_enabled:%d\n", __func__, + qphy->clocks_enabled); +} + +static int qusb_phy_config_vdd(struct qusb_phy *qphy, int high) +{ + int min, ret; + + min = high ? 1 : 0; /* low or none? */ + ret = regulator_set_voltage(qphy->vdd, qphy->vdd_levels[min], + qphy->vdd_levels[2]); + if (ret) { + dev_err(qphy->phy.dev, "unable to set voltage for qusb vdd\n"); + return ret; + } + + dev_dbg(qphy->phy.dev, "min_vol:%d max_vol:%d\n", + qphy->vdd_levels[min], qphy->vdd_levels[2]); + return ret; +} + +static int qusb_phy_enable_power(struct qusb_phy *qphy, bool on, + bool toggle_vdd) +{ + int ret = 0; + + dev_dbg(qphy->phy.dev, "%s turn %s regulators. power_enabled:%d\n", + __func__, on ? "on" : "off", qphy->power_enabled); + + if (toggle_vdd && qphy->power_enabled == on) { + dev_dbg(qphy->phy.dev, "PHYs' regulators are already ON.\n"); + return 0; + } + + if (!on) + goto disable_vdda33; + + if (toggle_vdd) { + ret = qusb_phy_config_vdd(qphy, true); + if (ret) { + dev_err(qphy->phy.dev, "Unable to config VDD:%d\n", + ret); + goto err_vdd; + } + + ret = regulator_enable(qphy->vdd); + if (ret) { + dev_err(qphy->phy.dev, "Unable to enable VDD\n"); + goto unconfig_vdd; + } + } + + ret = regulator_set_load(qphy->vdda18, QUSB2PHY_1P8_HPM_LOAD); + if (ret < 0) { + dev_err(qphy->phy.dev, "Unable to set HPM of vdda18:%d\n", ret); + goto disable_vdd; + } + + ret = regulator_set_voltage(qphy->vdda18, QUSB2PHY_1P8_VOL_MIN, + QUSB2PHY_1P8_VOL_MAX); + if (ret) { + dev_err(qphy->phy.dev, + "Unable to set voltage for vdda18:%d\n", ret); + goto put_vdda18_lpm; + } + + ret = regulator_enable(qphy->vdda18); + if (ret) { + dev_err(qphy->phy.dev, "Unable to enable vdda18:%d\n", ret); + goto unset_vdda18; + } + + ret = regulator_set_load(qphy->vdda33, QUSB2PHY_3P3_HPM_LOAD); + if (ret < 0) { + dev_err(qphy->phy.dev, "Unable to set HPM of vdda33:%d\n", ret); + goto disable_vdda18; + } + + ret = regulator_set_voltage(qphy->vdda33, QUSB2PHY_3P3_VOL_MIN, + QUSB2PHY_3P3_VOL_MAX); + if (ret) { + dev_err(qphy->phy.dev, + "Unable to set voltage for vdda33:%d\n", ret); + goto put_vdda33_lpm; + } + + ret = regulator_enable(qphy->vdda33); + if (ret) { + dev_err(qphy->phy.dev, "Unable to enable vdda33:%d\n", ret); + goto unset_vdd33; + } + + if (toggle_vdd) + qphy->power_enabled = true; + + pr_debug("%s(): QUSB PHY's regulators are turned ON.\n", __func__); + return ret; + +disable_vdda33: + ret = regulator_disable(qphy->vdda33); + if (ret) + dev_err(qphy->phy.dev, "Unable to disable vdda33:%d\n", ret); + +unset_vdd33: + ret = regulator_set_voltage(qphy->vdda33, 0, QUSB2PHY_3P3_VOL_MAX); + if (ret) + dev_err(qphy->phy.dev, + "Unable to set (0) voltage for vdda33:%d\n", ret); + +put_vdda33_lpm: + ret = regulator_set_load(qphy->vdda33, 0); + if (ret < 0) + dev_err(qphy->phy.dev, "Unable to set (0) HPM of vdda33\n"); + +disable_vdda18: + ret = regulator_disable(qphy->vdda18); + if (ret) + dev_err(qphy->phy.dev, "Unable to disable vdda18:%d\n", ret); + +unset_vdda18: + ret = regulator_set_voltage(qphy->vdda18, 0, QUSB2PHY_1P8_VOL_MAX); + if (ret) + dev_err(qphy->phy.dev, + "Unable to set (0) voltage for vdda18:%d\n", ret); + +put_vdda18_lpm: + ret = regulator_set_load(qphy->vdda18, 0); + if (ret < 0) + dev_err(qphy->phy.dev, "Unable to set LPM of vdda18\n"); + +disable_vdd: + if (toggle_vdd) { + ret = regulator_disable(qphy->vdd); + if (ret) + dev_err(qphy->phy.dev, "Unable to disable vdd:%d\n", + ret); + +unconfig_vdd: + ret = qusb_phy_config_vdd(qphy, false); + if (ret) + dev_err(qphy->phy.dev, "Unable unconfig VDD:%d\n", + ret); + } +err_vdd: + if (toggle_vdd) + qphy->power_enabled = false; + dev_dbg(qphy->phy.dev, "QUSB PHY's regulators are turned OFF.\n"); + return ret; +} + +static int qusb_phy_update_dpdm(struct usb_phy *phy, int value) +{ + struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy); + int ret = 0; + + dev_dbg(phy->dev, "%s value:%d rm_pulldown:%d\n", + __func__, value, qphy->rm_pulldown); + + switch (value) { + case POWER_SUPPLY_DP_DM_DPF_DMF: + dev_dbg(phy->dev, "POWER_SUPPLY_DP_DM_DPF_DMF\n"); + if (!qphy->rm_pulldown) { + ret = qusb_phy_enable_power(qphy, true, false); + if (ret >= 0) { + qphy->rm_pulldown = true; + dev_dbg(phy->dev, "DP_DM_F: rm_pulldown:%d\n", + qphy->rm_pulldown); + } + } + + break; + + case POWER_SUPPLY_DP_DM_DPR_DMR: + dev_dbg(phy->dev, "POWER_SUPPLY_DP_DM_DPR_DMR\n"); + if (qphy->rm_pulldown) { + ret = qusb_phy_enable_power(qphy, false, false); + if (ret >= 0) { + qphy->rm_pulldown = false; + dev_dbg(phy->dev, "DP_DM_R: rm_pulldown:%d\n", + qphy->rm_pulldown); + } + } + break; + + default: + ret = -EINVAL; + dev_err(phy->dev, "Invalid power supply property(%d)\n", value); + break; + } + + return ret; +} + +static void qusb_phy_get_tune2_param(struct qusb_phy *qphy) +{ + u8 num_of_bits; + u32 bit_mask = 1; + + pr_debug("%s(): num_of_bits:%d bit_pos:%d\n", __func__, + qphy->tune2_efuse_num_of_bits, + qphy->tune2_efuse_bit_pos); + + /* get bit mask based on number of bits to use with efuse reg */ + if (qphy->tune2_efuse_num_of_bits) { + num_of_bits = qphy->tune2_efuse_num_of_bits; + bit_mask = (bit_mask << num_of_bits) - 1; + } + + /* + * Read EFUSE register having TUNE2 parameter's high nibble. + * If efuse register shows value as 0x0, then use default value + * as 0xB as high nibble. Otherwise use efuse register based + * value for this purpose. + */ + qphy->tune2_val = readl_relaxed(qphy->tune2_efuse_reg); + pr_debug("%s(): bit_mask:%d efuse based tune2 value:%d\n", + __func__, bit_mask, qphy->tune2_val); + + qphy->tune2_val = TUNE2_HIGH_NIBBLE_VAL(qphy->tune2_val, + qphy->tune2_efuse_bit_pos, bit_mask); + + if (!qphy->tune2_val) + qphy->tune2_val = TUNE2_DEFAULT_HIGH_NIBBLE; + + /* Get TUNE2 byte value using high and low nibble value */ + qphy->tune2_val = ((qphy->tune2_val << 0x4) | + TUNE2_DEFAULT_LOW_NIBBLE); +} + +static void qusb_phy_write_seq(void __iomem *base, u32 *seq, int cnt, + unsigned long delay) +{ + int i; + + pr_debug("Seq count:%d\n", cnt); + for (i = 0; i < cnt; i = i+2) { + pr_debug("write 0x%02x to 0x%02x\n", seq[i], seq[i+1]); + writel_relaxed(seq[i], base + seq[i+1]); + if (delay) + usleep_range(delay, (delay + 2000)); + } +} + +static int qusb_phy_init(struct usb_phy *phy) +{ + struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy); + int ret; + u8 reg; + + dev_dbg(phy->dev, "%s\n", __func__); + + ret = qusb_phy_enable_power(qphy, true, true); + if (ret) + return ret; + + qusb_phy_enable_clocks(qphy, true); + + /* Perform phy reset */ + clk_reset(qphy->phy_reset, CLK_RESET_ASSERT); + usleep_range(100, 150); + clk_reset(qphy->phy_reset, CLK_RESET_DEASSERT); + + if (qphy->emulation) { + if (qphy->emu_init_seq) + qusb_phy_write_seq(qphy->emu_phy_base, + qphy->emu_init_seq, qphy->emu_init_seq_len, 0); + + if (qphy->qusb_phy_init_seq) + qusb_phy_write_seq(qphy->base, qphy->qusb_phy_init_seq, + qphy->init_seq_len, 0); + + /* Wait for 5ms as per QUSB2 RUMI sequence */ + usleep_range(5000, 7000); + + if (qphy->phy_pll_reset_seq) + qusb_phy_write_seq(qphy->base, qphy->phy_pll_reset_seq, + qphy->phy_pll_reset_seq_len, 10000); + + if (qphy->emu_dcm_reset_seq) + qusb_phy_write_seq(qphy->emu_phy_base, + qphy->emu_dcm_reset_seq, + qphy->emu_dcm_reset_seq_len, 10000); + + return 0; + } + + /* Disable the PHY */ + writel_relaxed(readl_relaxed(qphy->base + QUSB2PHY_PWR_CTRL1) | + PWR_CTRL1_POWR_DOWN, + qphy->base + QUSB2PHY_PWR_CTRL1); + + if (qphy->qusb_phy_init_seq) + qusb_phy_write_seq(qphy->base, qphy->qusb_phy_init_seq, + qphy->init_seq_len, 0); + /* + * Check for EFUSE value only if tune2_efuse_reg is available + * and try to read EFUSE value only once i.e. not every USB + * cable connect case. + */ + if (qphy->tune2_efuse_reg) { + if (!qphy->tune2_val) + qusb_phy_get_tune2_param(qphy); + + pr_debug("%s(): Programming TUNE2 parameter as:%x\n", __func__, + qphy->tune2_val); + writel_relaxed(qphy->tune2_val, + qphy->base + QUSB2PHY_PORT_TUNE2); + } + + /* If phy_tune2 modparam set, override tune2 value */ + if (phy_tune2) { + pr_debug("%s(): (modparam) TUNE2 val:0x%02x\n", + __func__, phy_tune2); + writel_relaxed(phy_tune2, + qphy->base + QUSB2PHY_PORT_TUNE2); + } + + /* ensure above writes are completed before re-enabling PHY */ + wmb(); + + /* Enable the PHY */ + writel_relaxed(readl_relaxed(qphy->base + QUSB2PHY_PWR_CTRL1) & + ~PWR_CTRL1_POWR_DOWN, + qphy->base + QUSB2PHY_PWR_CTRL1); + + /* Ensure above write is completed before turning ON ref clk */ + wmb(); + + /* Require to get phy pll lock successfully */ + usleep_range(150, 160); + + reg = readb_relaxed(qphy->base + QUSB2PHY_PLL_COMMON_STATUS_ONE); + dev_dbg(phy->dev, "QUSB2PHY_PLL_COMMON_STATUS_ONE:%x\n", reg); + if (!(reg & CORE_READY_STATUS)) { + dev_err(phy->dev, "QUSB PHY PLL LOCK fails:%x\n", reg); + WARN_ON(1); + } + return 0; +} + +static void qusb_phy_shutdown(struct usb_phy *phy) +{ + struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy); + + dev_dbg(phy->dev, "%s\n", __func__); + + qusb_phy_enable_clocks(qphy, true); + + /* Disable the PHY */ + writel_relaxed(readl_relaxed(qphy->base + QUSB2PHY_PWR_CTRL1) | + PWR_CTRL1_POWR_DOWN, + qphy->base + QUSB2PHY_PWR_CTRL1); + + /* Makes sure that above write goes through */ + wmb(); + + qusb_phy_enable_clocks(qphy, false); +} +/** + * Performs QUSB2 PHY suspend/resume functionality. + * + * @uphy - usb phy pointer. + * @suspend - to enable suspend or not. 1 - suspend, 0 - resume + * + */ +static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend) +{ + struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy); + u32 linestate = 0, intr_mask = 0; + + if (qphy->suspended && suspend) { + dev_dbg(phy->dev, "%s: USB PHY is already suspended\n", + __func__); + return 0; + } + + if (suspend) { + /* Bus suspend case */ + if (qphy->cable_connected || + (qphy->phy.flags & PHY_HOST_MODE)) { + /* Disable all interrupts */ + writel_relaxed(0x00, + qphy->base + QUSB2PHY_INTR_CTRL); + + linestate = readl_relaxed(qphy->base + + QUSB2PHY_INTR_STAT); + /* + * D+/D- interrupts are level-triggered, but we are + * only interested if the line state changes, so enable + * the high/low trigger based on current state. In + * other words, enable the triggers _opposite_ of what + * the current D+/D- levels are. + * e.g. if currently D+ high, D- low (HS 'J'/Suspend), + * configure the mask to trigger on D+ low OR D- high + */ + intr_mask = DMSE_INTERRUPT | DPSE_INTERRUPT; + if (!(linestate & DPSE_INTR_EN)) /* D+ low */ + intr_mask |= DPSE_INTR_HIGH_SEL; + if (!(linestate & DMSE_INTR_EN)) /* D- low */ + intr_mask |= DMSE_INTR_HIGH_SEL; + + writel_relaxed(intr_mask, + qphy->base + QUSB2PHY_INTR_CTRL); + + /* Makes sure that above write goes through */ + wmb(); + qusb_phy_enable_clocks(qphy, false); + } else { /* Cable disconnect case */ + /* Disable all interrupts */ + writel_relaxed(0x00, + qphy->base + QUSB2PHY_INTR_CTRL); + + /* Put PHY into non-driving mode */ + writel_relaxed(0x23, + qphy->base + QUSB2PHY_PWR_CTRL1); + + /* Makes sure that above write goes through */ + wmb(); + + qusb_phy_enable_clocks(qphy, false); + qusb_phy_enable_power(qphy, false, true); + } + qphy->suspended = true; + } else { + /* Bus resume case */ + if (qphy->cable_connected || + (qphy->phy.flags & PHY_HOST_MODE)) { + qusb_phy_enable_clocks(qphy, true); + /* Clear all interrupts on resume */ + writel_relaxed(0x00, + qphy->base + QUSB2PHY_INTR_CTRL); + + /* Makes sure that above write goes through */ + wmb(); + } else { /* Cable connect case */ + qusb_phy_enable_power(qphy, true, true); + qusb_phy_enable_clocks(qphy, true); + } + qphy->suspended = false; + } + + return 0; +} + +static void qusb_write_readback(void *base, u32 offset, + const u32 mask, u32 val) +{ + u32 write_val, tmp = readl_relaxed(base + offset); + + tmp &= ~mask; /* retain other bits */ + write_val = tmp | val; + + writel_relaxed(write_val, base + offset); + + /* Read back to see if val was written */ + tmp = readl_relaxed(base + offset); + tmp &= mask; /* clear other bits */ + + if (tmp != val) + pr_err("%s: write: %x to QSCRATCH: %x FAILED\n", + __func__, val, offset); +} + +static int qusb_phy_notify_connect(struct usb_phy *phy, + enum usb_device_speed speed) +{ + struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy); + + qphy->cable_connected = true; + + dev_dbg(phy->dev, " cable_connected=%d\n", qphy->cable_connected); + + /* Set OTG VBUS Valid from HSPHY to controller */ + qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG, + UTMI_OTG_VBUS_VALID, + UTMI_OTG_VBUS_VALID); + + /* Indicate value is driven by UTMI_OTG_VBUS_VALID bit */ + qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG, + SW_SESSVLD_SEL, SW_SESSVLD_SEL); + + dev_dbg(phy->dev, "QUSB2 phy connect notification\n"); + return 0; +} + +static int qusb_phy_notify_disconnect(struct usb_phy *phy, + enum usb_device_speed speed) +{ + struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy); + + qphy->cable_connected = false; + + dev_dbg(phy->dev, " cable_connected=%d\n", qphy->cable_connected); + + /* Set OTG VBUS Valid from HSPHY to controller */ + qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG, + UTMI_OTG_VBUS_VALID, 0); + + /* Indicate value is driven by UTMI_OTG_VBUS_VALID bit */ + qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG, + SW_SESSVLD_SEL, 0); + + dev_dbg(phy->dev, "QUSB2 phy disconnect notification\n"); + return 0; +} + +static int qusb_phy_dpdm_regulator_enable(struct regulator_dev *rdev) +{ + struct qusb_phy *qphy = rdev_get_drvdata(rdev); + + dev_dbg(qphy->phy.dev, "%s\n", __func__); + return qusb_phy_update_dpdm(&qphy->phy, POWER_SUPPLY_DP_DM_DPF_DMF); +} + +static int qusb_phy_dpdm_regulator_disable(struct regulator_dev *rdev) +{ + struct qusb_phy *qphy = rdev_get_drvdata(rdev); + + dev_dbg(qphy->phy.dev, "%s\n", __func__); + return qusb_phy_update_dpdm(&qphy->phy, POWER_SUPPLY_DP_DM_DPR_DMR); +} + +static int qusb_phy_dpdm_regulator_is_enabled(struct regulator_dev *rdev) +{ + struct qusb_phy *qphy = rdev_get_drvdata(rdev); + + dev_dbg(qphy->phy.dev, "%s qphy->rm_pulldown = %d\n", __func__, + qphy->rm_pulldown); + return qphy->rm_pulldown; +} + +static struct regulator_ops qusb_phy_dpdm_regulator_ops = { + .enable = qusb_phy_dpdm_regulator_enable, + .disable = qusb_phy_dpdm_regulator_disable, + .is_enabled = qusb_phy_dpdm_regulator_is_enabled, +}; + +static int qusb_phy_regulator_init(struct qusb_phy *qphy) +{ + struct device *dev = qphy->phy.dev; + struct regulator_config cfg = {}; + struct regulator_init_data *init_data; + + init_data = devm_kzalloc(dev, sizeof(*init_data), GFP_KERNEL); + if (!init_data) + return -ENOMEM; + + init_data->constraints.valid_ops_mask |= REGULATOR_CHANGE_STATUS; + qphy->dpdm_rdesc.owner = THIS_MODULE; + qphy->dpdm_rdesc.type = REGULATOR_VOLTAGE; + qphy->dpdm_rdesc.ops = &qusb_phy_dpdm_regulator_ops; + qphy->dpdm_rdesc.name = kbasename(dev->of_node->full_name); + + cfg.dev = dev; + cfg.init_data = init_data; + cfg.driver_data = qphy; + cfg.of_node = dev->of_node; + + qphy->dpdm_rdev = devm_regulator_register(dev, &qphy->dpdm_rdesc, &cfg); + if (IS_ERR(qphy->dpdm_rdev)) + return PTR_ERR(qphy->dpdm_rdev); + + return 0; +} + +static int qusb_phy_probe(struct platform_device *pdev) +{ + struct qusb_phy *qphy; + struct device *dev = &pdev->dev; + struct resource *res; + int ret = 0, size = 0; + + qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL); + if (!qphy) + return -ENOMEM; + + qphy->phy.dev = dev; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "qusb_phy_base"); + qphy->base = devm_ioremap_resource(dev, res); + if (IS_ERR(qphy->base)) + return PTR_ERR(qphy->base); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "qscratch_base"); + if (res) { + qphy->qscratch_base = devm_ioremap_resource(dev, res); + if (IS_ERR(qphy->qscratch_base)) { + dev_dbg(dev, "couldn't ioremap qscratch_base\n"); + qphy->qscratch_base = NULL; + } + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "emu_phy_base"); + if (res) { + qphy->emu_phy_base = devm_ioremap_resource(dev, res); + if (IS_ERR(qphy->emu_phy_base)) { + dev_dbg(dev, "couldn't ioremap emu_phy_base\n"); + qphy->emu_phy_base = NULL; + } + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "tune2_efuse_addr"); + if (res) { + qphy->tune2_efuse_reg = devm_ioremap_nocache(dev, res->start, + resource_size(res)); + if (!IS_ERR_OR_NULL(qphy->tune2_efuse_reg)) { + ret = of_property_read_u32(dev->of_node, + "qcom,tune2-efuse-bit-pos", + &qphy->tune2_efuse_bit_pos); + if (!ret) { + ret = of_property_read_u32(dev->of_node, + "qcom,tune2-efuse-num-bits", + &qphy->tune2_efuse_num_of_bits); + } + + if (ret) { + dev_err(dev, + "DT Value for tune2 efuse is invalid.\n"); + return -EINVAL; + } + } + } + + qphy->ref_clk_src = devm_clk_get(dev, "ref_clk_src"); + if (IS_ERR(qphy->ref_clk_src)) + dev_dbg(dev, "clk get failed for ref_clk_src\n"); + + qphy->ref_clk = devm_clk_get(dev, "ref_clk"); + if (IS_ERR(qphy->ref_clk)) + dev_dbg(dev, "clk get failed for ref_clk\n"); + else + clk_set_rate(qphy->ref_clk, 19200000); + + qphy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb_clk"); + if (IS_ERR(qphy->cfg_ahb_clk)) + return PTR_ERR(qphy->cfg_ahb_clk); + + qphy->phy_reset = devm_clk_get(dev, "phy_reset"); + if (IS_ERR(qphy->phy_reset)) + return PTR_ERR(qphy->phy_reset); + + qphy->emulation = of_property_read_bool(dev->of_node, + "qcom,emulation"); + + of_get_property(dev->of_node, "qcom,emu-init-seq", &size); + if (size) { + qphy->emu_init_seq = devm_kzalloc(dev, + size, GFP_KERNEL); + if (qphy->emu_init_seq) { + qphy->emu_init_seq_len = + (size / sizeof(*qphy->emu_init_seq)); + if (qphy->emu_init_seq_len % 2) { + dev_err(dev, "invalid emu_init_seq_len\n"); + return -EINVAL; + } + + of_property_read_u32_array(dev->of_node, + "qcom,qemu-init-seq", + qphy->emu_init_seq, + qphy->emu_init_seq_len); + } else { + dev_dbg(dev, + "error allocating memory for emu_init_seq\n"); + } + } + + of_get_property(dev->of_node, "qcom,phy-pll-reset-seq", &size); + if (size) { + qphy->phy_pll_reset_seq = devm_kzalloc(dev, + size, GFP_KERNEL); + if (qphy->phy_pll_reset_seq) { + qphy->phy_pll_reset_seq_len = + (size / sizeof(*qphy->phy_pll_reset_seq)); + if (qphy->phy_pll_reset_seq_len % 2) { + dev_err(dev, "invalid phy_pll_reset_seq_len\n"); + return -EINVAL; + } + + of_property_read_u32_array(dev->of_node, + "qcom,phy-pll-reset-seq", + qphy->phy_pll_reset_seq, + qphy->phy_pll_reset_seq_len); + } else { + dev_dbg(dev, + "error allocating memory for phy_pll_reset_seq\n"); + } + } + + of_get_property(dev->of_node, "qcom,emu-dcm-reset-seq", &size); + if (size) { + qphy->emu_dcm_reset_seq = devm_kzalloc(dev, + size, GFP_KERNEL); + if (qphy->emu_dcm_reset_seq) { + qphy->emu_dcm_reset_seq_len = + (size / sizeof(*qphy->emu_dcm_reset_seq)); + if (qphy->emu_dcm_reset_seq_len % 2) { + dev_err(dev, "invalid emu_dcm_reset_seq_len\n"); + return -EINVAL; + } + + of_property_read_u32_array(dev->of_node, + "qcom,emu-dcm-reset-seq", + qphy->emu_dcm_reset_seq, + qphy->emu_dcm_reset_seq_len); + } else { + dev_dbg(dev, + "error allocating memory for emu_dcm_reset_seq\n"); + } + } + + of_get_property(dev->of_node, "qcom,qusb-phy-init-seq", &size); + if (size) { + qphy->qusb_phy_init_seq = devm_kzalloc(dev, + size, GFP_KERNEL); + if (qphy->qusb_phy_init_seq) { + qphy->init_seq_len = + (size / sizeof(*qphy->qusb_phy_init_seq)); + if (qphy->init_seq_len % 2) { + dev_err(dev, "invalid init_seq_len\n"); + return -EINVAL; + } + + of_property_read_u32_array(dev->of_node, + "qcom,qusb-phy-init-seq", + qphy->qusb_phy_init_seq, + qphy->init_seq_len); + } else { + dev_err(dev, + "error allocating memory for phy_init_seq\n"); + } + } + + ret = of_property_read_u32_array(dev->of_node, "qcom,vdd-voltage-level", + (u32 *) qphy->vdd_levels, + ARRAY_SIZE(qphy->vdd_levels)); + if (ret) { + dev_err(dev, "error reading qcom,vdd-voltage-level property\n"); + return ret; + } + + qphy->vdd = devm_regulator_get(dev, "vdd"); + if (IS_ERR(qphy->vdd)) { + dev_err(dev, "unable to get vdd supply\n"); + return PTR_ERR(qphy->vdd); + } + + qphy->vdda33 = devm_regulator_get(dev, "vdda33"); + if (IS_ERR(qphy->vdda33)) { + dev_err(dev, "unable to get vdda33 supply\n"); + return PTR_ERR(qphy->vdda33); + } + + qphy->vdda18 = devm_regulator_get(dev, "vdda18"); + if (IS_ERR(qphy->vdda18)) { + dev_err(dev, "unable to get vdda18 supply\n"); + return PTR_ERR(qphy->vdda18); + } + + platform_set_drvdata(pdev, qphy); + + qphy->phy.label = "msm-qusb-phy-v2"; + qphy->phy.init = qusb_phy_init; + qphy->phy.set_suspend = qusb_phy_set_suspend; + qphy->phy.shutdown = qusb_phy_shutdown; + qphy->phy.type = USB_PHY_TYPE_USB2; + + if (qphy->qscratch_base) { + qphy->phy.notify_connect = qusb_phy_notify_connect; + qphy->phy.notify_disconnect = qusb_phy_notify_disconnect; + } + + ret = usb_add_phy_dev(&qphy->phy); + if (ret) + return ret; + + ret = qusb_phy_regulator_init(qphy); + if (ret) + usb_remove_phy(&qphy->phy); + + return ret; +} + +static int qusb_phy_remove(struct platform_device *pdev) +{ + struct qusb_phy *qphy = platform_get_drvdata(pdev); + + usb_remove_phy(&qphy->phy); + + if (qphy->clocks_enabled) { + clk_disable_unprepare(qphy->cfg_ahb_clk); + clk_disable_unprepare(qphy->ref_clk); + clk_disable_unprepare(qphy->ref_clk_src); + qphy->clocks_enabled = false; + } + + qusb_phy_enable_power(qphy, false, true); + + return 0; +} + +static const struct of_device_id qusb_phy_id_table[] = { + { .compatible = "qcom,qusb2phy-v2", }, + { }, +}; +MODULE_DEVICE_TABLE(of, qusb_phy_id_table); + +static struct platform_driver qusb_phy_driver = { + .probe = qusb_phy_probe, + .remove = qusb_phy_remove, + .driver = { + .name = "msm-qusb-phy-v2", + .of_match_table = of_match_ptr(qusb_phy_id_table), + }, +}; + +module_platform_driver(qusb_phy_driver); + +MODULE_DESCRIPTION("MSM QUSB2 PHY v2 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel.c b/drivers/video/fbdev/msm/mdss_dsi_panel.c index cd770eaac639..9548ea471385 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_panel.c +++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c @@ -1681,6 +1681,38 @@ static void mdss_dsi_parse_esd_params(struct device_node *np, if (!pinfo->esd_check_enabled) return; + ctrl->status_mode = ESD_MAX; + rc = of_property_read_string(np, + "qcom,mdss-dsi-panel-status-check-mode", &string); + if (!rc) { + if (!strcmp(string, "bta_check")) { + ctrl->status_mode = ESD_BTA; + } else if (!strcmp(string, "reg_read")) { + ctrl->status_mode = ESD_REG; + ctrl->check_read_status = + mdss_dsi_gen_read_status; + } else if (!strcmp(string, "reg_read_nt35596")) { + ctrl->status_mode = ESD_REG_NT35596; + ctrl->status_error_count = 0; + ctrl->check_read_status = + mdss_dsi_nt35596_read_status; + } else if (!strcmp(string, "te_signal_check")) { + if (pinfo->mipi.mode == DSI_CMD_MODE) { + ctrl->status_mode = ESD_TE; + } else { + pr_err("TE-ESD not valid for video mode\n"); + goto error; + } + } else { + pr_err("No valid panel-status-check-mode string\n"); + goto error; + } + } + + if ((ctrl->status_mode == ESD_BTA) || (ctrl->status_mode == ESD_TE) || + (ctrl->status_mode == ESD_MAX)) + return; + mdss_dsi_parse_dcs_cmds(np, &ctrl->status_cmds, "qcom,mdss-dsi-panel-status-command", "qcom,mdss-dsi-panel-status-command-state"); @@ -1736,43 +1768,14 @@ static void mdss_dsi_parse_esd_params(struct device_node *np, memset(ctrl->status_value, 0, ctrl->groups * status_len); } - ctrl->status_mode = ESD_MAX; - rc = of_property_read_string(np, - "qcom,mdss-dsi-panel-status-check-mode", &string); - if (!rc) { - if (!strcmp(string, "bta_check")) { - ctrl->status_mode = ESD_BTA; - } else if (!strcmp(string, "reg_read")) { - ctrl->status_mode = ESD_REG; - ctrl->check_read_status = - mdss_dsi_gen_read_status; - } else if (!strcmp(string, "reg_read_nt35596")) { - ctrl->status_mode = ESD_REG_NT35596; - ctrl->status_error_count = 0; - ctrl->check_read_status = - mdss_dsi_nt35596_read_status; - } else if (!strcmp(string, "te_signal_check")) { - if (pinfo->mipi.mode == DSI_CMD_MODE) { - ctrl->status_mode = ESD_TE; - } else { - pr_err("TE-ESD not valid for video mode\n"); - goto error; - } - } else { - pr_err("No valid panel-status-check-mode string\n"); - goto error; - } - } - return; -error: - kfree(ctrl->return_buf); error2: kfree(ctrl->status_value); error1: kfree(ctrl->status_valid_params); kfree(ctrl->status_cmds_rlen); +error: pinfo->esd_check_enabled = false; } diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.c b/drivers/video/fbdev/msm/mdss_hdmi_edid.c index 77d7c04e6f0e..6a6cdc8b502c 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_edid.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.c @@ -67,6 +67,13 @@ enum edid_sink_mode { SINK_MODE_HDMI }; +enum luminance_value { + NO_LUMINANCE_DATA = 3, + MAXIMUM_LUMINANCE = 4, + FRAME_AVERAGE_LUMINANCE = 5, + MINIMUM_LUMINANCE = 6 +}; + enum data_block_types { RESERVED, AUDIO_DATA_BLOCK, @@ -82,6 +89,7 @@ enum extended_data_block_types { VIDEO_CAPABILITY_DATA_BLOCK = 0x0, VENDOR_SPECIFIC_VIDEO_DATA_BLOCK = 0x01, HDMI_VIDEO_DATA_BLOCK = 0x04, + HDR_STATIC_METADATA_DATA_BLOCK = 0x06, Y420_VIDEO_DATA_BLOCK = 0x0E, VIDEO_FORMAT_PREFERENCE_DATA_BLOCK = 0x0D, Y420_CAPABILITY_MAP_DATA_BLOCK = 0x0F, @@ -143,11 +151,13 @@ struct hdmi_edid_ctrl { char vendor_id[EDID_VENDOR_ID_SIZE]; bool keep_resv_timings; bool edid_override; + bool hdr_supported; struct hdmi_edid_sink_data sink_data; struct hdmi_edid_init_data init_data; struct hdmi_edid_sink_caps sink_caps; struct hdmi_edid_override_data override_data; + struct hdmi_edid_hdr_data hdr_data; }; static bool hdmi_edid_is_mode_supported(struct hdmi_edid_ctrl *edid_ctrl, @@ -203,6 +213,14 @@ static int hdmi_edid_reset_parser(struct hdmi_edid_ctrl *edid_ctrl) if (!edid_ctrl->keep_resv_timings) hdmi_reset_resv_timing_info(); + /* reset HDR related data */ + edid_ctrl->hdr_supported = false; + edid_ctrl->hdr_data.eotf = 0; + edid_ctrl->hdr_data.descriptor = 0; + edid_ctrl->hdr_data.max_luminance = 0; + edid_ctrl->hdr_data.avg_luminance = 0; + edid_ctrl->hdr_data.min_luminance = 0; + return 0; } @@ -762,6 +780,30 @@ err: } static DEVICE_ATTR(add_res, S_IWUSR, NULL, hdmi_edid_sysfs_wta_add_resolution); +static ssize_t hdmi_edid_sysfs_rda_hdr_data(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + struct hdmi_edid_ctrl *edid_ctrl = hdmi_edid_get_ctrl(dev); + + if (!edid_ctrl) { + DEV_ERR("%s: invalid input\n", __func__); + return -EINVAL; + } + + ret = scnprintf(buf, PAGE_SIZE, "%d, %u, %u, %u, %u, %u\n", + edid_ctrl->hdr_supported, + edid_ctrl->hdr_data.eotf, + edid_ctrl->hdr_data.descriptor, + edid_ctrl->hdr_data.max_luminance, + edid_ctrl->hdr_data.avg_luminance, + edid_ctrl->hdr_data.min_luminance); + DEV_DBG("%s: '%s'\n", __func__, buf); + + return ret; +} +static DEVICE_ATTR(hdr_data, S_IRUGO, hdmi_edid_sysfs_rda_hdr_data, NULL); + static struct attribute *hdmi_edid_fs_attrs[] = { &dev_attr_edid_modes.attr, &dev_attr_pa.attr, @@ -775,6 +817,7 @@ static struct attribute *hdmi_edid_fs_attrs[] = { &dev_attr_res_info.attr, &dev_attr_res_info_data.attr, &dev_attr_add_res.attr, + &dev_attr_hdr_data.attr, NULL, }; @@ -899,6 +942,47 @@ static void hdmi_edid_parse_Y420VDB(struct hdmi_edid_ctrl *edid_ctrl, } } +static bool hdmi_edid_is_luminance_value_present(u32 block_length, + enum luminance_value value) +{ + return block_length > NO_LUMINANCE_DATA && value <= block_length; +} + +static void hdmi_edid_parse_hdrdb(struct hdmi_edid_ctrl *edid_ctrl, + const u8 *data_block) +{ + u8 len = 0; + + if (!edid_ctrl || !data_block) { + pr_err("%s: invalid input\n", __func__); + return; + } + + /* Byte 1: Length of Data Block */ + len = data_block[0] & 0x1F; + + /* Byte 3: Electro-Optical Transfer Functions */ + edid_ctrl->hdr_data.eotf = data_block[2] & 0x3F; + + /* Byte 4: Static Metadata Descriptors */ + edid_ctrl->hdr_data.descriptor = data_block[3] & 0x1; + + /* Byte 5: Desired Content Maximum Luminance */ + if (hdmi_edid_is_luminance_value_present(len, MAXIMUM_LUMINANCE)) + edid_ctrl->hdr_data.max_luminance = + data_block[MAXIMUM_LUMINANCE]; + + /* Byte 6: Desired Content Max Frame-average Luminance */ + if (hdmi_edid_is_luminance_value_present(len, FRAME_AVERAGE_LUMINANCE)) + edid_ctrl->hdr_data.avg_luminance = + data_block[FRAME_AVERAGE_LUMINANCE]; + + /* Byte 7: Desired Content Min Luminance */ + if (hdmi_edid_is_luminance_value_present(len, MINIMUM_LUMINANCE)) + edid_ctrl->hdr_data.min_luminance = + data_block[MINIMUM_LUMINANCE]; +} + static void hdmi_edid_parse_Y420CMDB(struct hdmi_edid_ctrl *edid_ctrl, const u8 *in_buf) { @@ -1045,6 +1129,12 @@ static void hdmi_edid_extract_extended_data_blocks( __func__, etag[2]); hdmi_edid_parse_Y420VDB(edid_ctrl, etag); break; + case HDR_STATIC_METADATA_DATA_BLOCK: + DEV_DBG("%s found HDR Static Metadata. Byte 3 = 0x%x", + __func__, etag[2]); + hdmi_edid_parse_hdrdb(edid_ctrl, etag); + edid_ctrl->hdr_supported = true; + break; default: DEV_DBG("%s: Tag Code %d not supported\n", __func__, etag[1]); @@ -2355,6 +2445,31 @@ u8 hdmi_edid_get_deep_color(void *input) return edid_ctrl->deep_color; } +/** + * hdmi_edid_get_hdr_data() - get the HDR capabiliies of the sink + * @input: edid parser data + * + * This API returns HDR info associated with the sink: + * electro-optical transfer function, static metadata descriptor, + * desired content max luminance data, desired content max + * frame-average luminance data, and desired content min luminance + * data. + * + * Return: HDR data. + */ +void hdmi_edid_get_hdr_data(void *input, + struct hdmi_edid_hdr_data *hdr_data) +{ + struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input; + + if (!edid_ctrl || !hdr_data) { + DEV_ERR("%s: invalid input\n", __func__); + return; + } + + hdr_data = &edid_ctrl->hdr_data; +} + bool hdmi_edid_is_s3d_mode_supported(void *input, u32 video_mode, u32 s3d_mode) { int i; diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.h b/drivers/video/fbdev/msm/mdss_hdmi_edid.h index 7b4b2dccc802..c818f3fc0d19 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_edid.h +++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.h @@ -28,6 +28,22 @@ struct hdmi_edid_init_data { u32 buf_size; }; +/* + * struct hdmi_edid_hdr_data - HDR Static Metadata + * @eotf: Electro-Optical Transfer Function + * @descriptor: Static Metadata Descriptor + * @max_luminance: Desired Content Maximum Luminance + * @avg_luminance: Desired Content Frame-average Luminance + * @min_luminance: Desired Content Minimum Luminance + */ +struct hdmi_edid_hdr_data { + u32 eotf; + u32 descriptor; + u32 max_luminance; + u32 avg_luminance; + u32 min_luminance; +}; + int hdmi_edid_parser(void *edid_ctrl); u32 hdmi_edid_get_raw_data(void *edid_ctrl, u8 *buf, u32 size); u8 hdmi_edid_get_sink_scaninfo(void *edid_ctrl, u32 resolution); @@ -44,5 +60,7 @@ void *hdmi_edid_init(struct hdmi_edid_init_data *init_data); bool hdmi_edid_is_s3d_mode_supported(void *input, u32 video_mode, u32 s3d_mode); u8 hdmi_edid_get_deep_color(void *edid_ctrl); +void hdmi_edid_get_hdr_data(void *edid_ctrl, + struct hdmi_edid_hdr_data *hdr_data); #endif /* __HDMI_EDID_H__ */ diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.c b/drivers/video/fbdev/msm/mdss_hdmi_tx.c index 33f18cd28de9..3d773371713d 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_tx.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.c @@ -287,7 +287,7 @@ static inline bool hdmi_tx_is_hdcp_enabled(struct hdmi_tx_ctrl *hdmi_ctrl) static inline bool hdmi_tx_dc_support(struct hdmi_tx_ctrl *hdmi_ctrl) { - return hdmi_ctrl->dc_support && + return hdmi_ctrl->dc_feature_on && hdmi_ctrl->dc_support && (hdmi_edid_get_deep_color( hdmi_tx_get_fd(HDMI_TX_FEAT_EDID)) & BIT(1)); } @@ -2204,8 +2204,12 @@ static int hdmi_tx_check_capability(struct hdmi_tx_ctrl *hdmi_ctrl) } } - DEV_DBG("%s: Features <HDMI:%s, HDCP:%s>\n", __func__, - hdmi_disabled ? "OFF" : "ON", hdcp_disabled ? "OFF" : "ON"); + if (hdmi_ctrl->hdmi_tx_version >= HDMI_TX_VERSION_403) + hdmi_ctrl->dc_feature_on = true; + + DEV_DBG("%s: Features <HDMI:%s, HDCP:%s, Deep Color:%s>\n", __func__, + hdmi_disabled ? "OFF" : "ON", hdcp_disabled ? "OFF" : "ON", + hdmi_ctrl->dc_feature_on ? "OFF" : "ON"); if (hdmi_disabled) { DEV_ERR("%s: HDMI disabled\n", __func__); @@ -2837,6 +2841,9 @@ static int hdmi_tx_setup_tmds_clk_rate(struct hdmi_tx_ctrl *hdmi_ctrl) rate = timing->pixel_freq / rate_ratio; + if (hdmi_tx_dc_support(hdmi_ctrl)) + rate += rate >> 2; + end: return rate; } diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.h b/drivers/video/fbdev/msm/mdss_hdmi_tx.h index b606ffedaa26..462edac31c09 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_tx.h +++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.h @@ -126,6 +126,7 @@ struct hdmi_tx_ctrl { bool hdcp22_present; bool power_data_enable[HDMI_TX_MAX_PM]; bool dc_support; + bool dc_feature_on; void (*hdmi_tx_hpd_done)(void *data); void *downstream_data; diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h index e60f37733186..d6cd6825e262 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.h +++ b/drivers/video/fbdev/msm/mdss_mdp.h @@ -86,6 +86,11 @@ #define MAX_LAYER_COUNT 0xC +/* For SRC QSEED3, when user space does not send the scaler information, + * this flag allows pixel _extension to be programmed when scaler is disabled + */ +#define ENABLE_PIXEL_EXT_ONLY 0x80000000 + /** * Destination Scaler control flags setting * diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c index 457c5462c44f..2baa1b9cd8b5 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c +++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c @@ -561,7 +561,8 @@ int mdss_mdp_overlay_setup_scaling(struct mdss_mdp_pipe *pipe) rc, src, pipe->dst.h); } - if (test_bit(MDSS_CAPS_QSEED3, mdata->mdss_caps_map)) + if (test_bit(MDSS_CAPS_QSEED3, mdata->mdss_caps_map) && + (pipe->type == MDSS_MDP_PIPE_TYPE_VIG)) mdss_mdp_pipe_calc_qseed3_cfg(pipe); else mdss_mdp_pipe_calc_pixel_extn(pipe); diff --git a/drivers/video/fbdev/msm/mdss_mdp_pipe.c b/drivers/video/fbdev/msm/mdss_mdp_pipe.c index 220f2b503716..bcb4867b4ffd 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pipe.c +++ b/drivers/video/fbdev/msm/mdss_mdp_pipe.c @@ -3136,6 +3136,13 @@ void mdss_mdp_pipe_calc_qseed3_cfg(struct mdss_mdp_pipe *pipe) pipe->scaler.src_height[i]); } + if ((!pipe->src_fmt->is_yuv) && + (pipe->src.w == pipe->dst.w) && + (pipe->src.h == pipe->dst.h)) { + pipe->scaler.enable = ENABLE_PIXEL_EXT_ONLY; + return; + } + pipe->scaler.dst_width = pipe->dst.w; pipe->scaler.dst_height = pipe->dst.h; /* assign filters */ @@ -3143,5 +3150,6 @@ void mdss_mdp_pipe_calc_qseed3_cfg(struct mdss_mdp_pipe *pipe) pipe->scaler.uv_filter_cfg = FILTER_BILINEAR; pipe->scaler.alpha_filter_cfg = FILTER_ALPHA_BILINEAR; pipe->scaler.lut_flag = 0; + pipe->scaler.blend_cfg = 1; pipe->scaler.enable = ENABLE_SCALE; } diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp.c b/drivers/video/fbdev/msm/mdss_mdp_pp.c index 437e01535924..c4cbc220baf8 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pp.c +++ b/drivers/video/fbdev/msm/mdss_mdp_pp.c @@ -1491,13 +1491,18 @@ int mdss_mdp_scaler_lut_cfg(struct mdp_scale_data_v2 *scaler, uint32_t *lut_type[QSEED3_FILTERS] = {NULL, NULL, NULL, NULL, NULL}; uint32_t lut_offset, lut_len; struct mdss_mdp_qseed3_lut_tbl *lut_tbl; - /* for each filter, 4 lut regions offset and length table */ + /* for each filter, 4 lut regions length and offset table */ static uint32_t offset_tbl[QSEED3_FILTERS][QSEED3_LUT_REGIONS][2] = { + /* DIR */ {{18, 0x000}, {12, 0x120}, {12, 0x1E0}, {8, 0x2A0} }, + /* Y_CIR */ {{6, 0x320}, {3, 0x3E0}, {3, 0x440}, {3, 0x4A0} }, - {{6, 0x380}, {3, 0x410}, {3, 0x470}, {3, 0x4d0} }, - {{6, 0x500}, {3, 0x5c0}, {3, 0x620}, {3, 0x680} }, - {{6, 0x560}, {3, 0x5f0}, {3, 0x650}, {3, 0x6b0} }, + /* Y_SEP */ + {{6, 0x380}, {3, 0x410}, {3, 0x470}, {3, 0x4D0} }, + /* UV_CIR */ + {{6, 0x500}, {3, 0x5C0}, {3, 0x620}, {3, 0x680} }, + /* UV_SEP */ + {{6, 0x560}, {3, 0x5F0}, {3, 0x650}, {3, 0x6B0} }, }; mdata = mdss_mdp_get_mdata(); @@ -1518,13 +1523,14 @@ int mdss_mdp_scaler_lut_cfg(struct mdp_scale_data_v2 *scaler, lut_type[1] = lut_tbl->cir_lut + scaler->y_rgb_cir_lut_idx * CIR_LUT_COEFFS; - if (scaler->lut_flag & SCALER_LUT_UV_CIR_WR) - lut_type[2] = lut_tbl->cir_lut + - scaler->uv_cir_lut_idx * CIR_LUT_COEFFS; if (scaler->lut_flag & SCALER_LUT_Y_SEP_WR) - lut_type[3] = + lut_type[2] = lut_tbl->sep_lut + scaler->y_rgb_sep_lut_idx * SEP_LUT_COEFFS; + if (scaler->lut_flag & SCALER_LUT_UV_CIR_WR) + lut_type[3] = + lut_tbl->cir_lut + scaler->uv_cir_lut_idx * + CIR_LUT_COEFFS; if (scaler->lut_flag & SCALER_LUT_UV_SEP_WR) lut_type[4] = lut_tbl->sep_lut + scaler->uv_sep_lut_idx * @@ -1622,25 +1628,26 @@ int mdss_mdp_qseed3_setup(struct mdp_scale_data_v2 *scaler, pr_debug("scaler->enable=%d", scaler->enable); - if (scaler->enable) { + + if (fmt->is_yuv) + op_mode |= (1 << SCALER_COLOR_SPACE); + if (fmt->alpha_enable) + op_mode |= SCALER_ALPHA_EN; + if (!fmt->unpack_dx_format) + op_mode |= 0x1 << SCALER_BIT_WIDTH; + + if (scaler->enable & ENABLE_SCALE) { op_mode |= SCALER_EN; op_mode |= (scaler->y_rgb_filter_cfg & 0x3) << Y_FILTER_CFG; - if (fmt->is_yuv) { - op_mode |= (1 << SCALER_COLOR_SPACE); + if (fmt->is_yuv) op_mode |= (scaler->uv_filter_cfg & 0x3) << UV_FILTER_CFG; - } - if (fmt->alpha_enable) { - op_mode |= SCALER_ALPHA_EN; + if (fmt->alpha_enable) op_mode |= (scaler->alpha_filter_cfg & 1) << ALPHA_FILTER_CFG; - } - - if (!fmt->unpack_dx_format) - op_mode |= 0x1 << SCALER_BIT_WIDTH; op_mode |= (scaler->blend_cfg & 1) << SCALER_BLEND_CFG; @@ -1712,9 +1719,9 @@ int mdss_mdp_qseed3_setup(struct mdp_scale_data_v2 *scaler, writel_relaxed(scaler->phase_step_y[1] & PHASE_STEP_BITS, MDSS_MDP_REG_SCALER_PHASE_STEP_UV_V + offset); - writel_relaxed(preload, MDSS_MDP_REG_SCALER_PRELOAD + offset); + writel_relaxed(src_y_rgb, MDSS_MDP_REG_SCALER_SRC_SIZE_Y_RGB_A + offset); @@ -1723,10 +1730,22 @@ int mdss_mdp_qseed3_setup(struct mdp_scale_data_v2 *scaler, writel_relaxed(dst, MDSS_MDP_REG_SCALER_DST_SIZE + offset); + + pr_debug("phase_init %x, step_y_h %x, step_y_v %x, step_uv_h %x, step_uv_v %x\n", + phase_init, + scaler->phase_step_x[0] & PHASE_STEP_BITS, + scaler->phase_step_x[1] & PHASE_STEP_BITS, + scaler->phase_step_y[0] & PHASE_STEP_BITS, + scaler->phase_step_y[1] & PHASE_STEP_BITS); + pr_debug("src_size_y_rgb %x, src_size_uv %x, dst_size %x,\n", + src_y_rgb, + src_uv, + dst); } else { op_mode &= ~SCALER_EN; } + pr_debug("op_mode %x\n", op_mode); writel_relaxed(op_mode, MDSS_MDP_REG_SCALER_OP_MODE + offset); return rc; @@ -3099,8 +3118,8 @@ static int pp_ad_calc_bl(struct msm_fb_data_type *mfd, int bl_in, int *bl_out, mutex_lock(&ad->lock); if (!mfd->ad_bl_level) mfd->ad_bl_level = bl_in; - if (!(ad->state & PP_AD_STATE_RUN)) { - pr_debug("AD is not running.\n"); + if (!(ad->sts & PP_STS_ENABLE)) { + pr_debug("AD is not enabled.\n"); mutex_unlock(&ad->lock); return -EPERM; } @@ -3123,22 +3142,27 @@ static int pp_ad_calc_bl(struct msm_fb_data_type *mfd, int bl_in, int *bl_out, return ret; } - ret = pp_ad_attenuate_bl(ad, temp, &temp); - if (ret) { - pr_err("Failed to attenuate BL: %d\n", ret); - mutex_unlock(&ad->lock); - return ret; - } - ad_bl_out = temp; + if (ad->init.alpha > 0) { + ret = pp_ad_attenuate_bl(ad, temp, &temp); + if (ret) { + pr_err("Failed to attenuate BL: %d\n", ret); + mutex_unlock(&ad->lock); + return ret; + } + ad_bl_out = temp; - ret = pp_ad_linearize_bl(ad, temp, &temp, MDP_PP_AD_BL_LINEAR_INV); - if (ret) { - pr_err("Failed to inverse linearize BL: %d\n", ret); - mutex_unlock(&ad->lock); - return ret; + ret = pp_ad_linearize_bl(ad, temp, &temp, + MDP_PP_AD_BL_LINEAR_INV); + if (ret) { + pr_err("Failed to inverse linearize BL: %d\n", ret); + mutex_unlock(&ad->lock); + return ret; + } + *bl_out = temp; + } else { + ad_bl_out = temp; } - *bl_out = temp; if (pp_ad_bl_threshold_check(ad->init.al_thresh, ad->init.alpha_base, ad->last_bl, ad_bl_out)) { mfd->ad_bl_level = ad_bl_out; @@ -6193,6 +6217,8 @@ static int mdss_mdp_ad_setup(struct msm_fb_data_type *mfd) ad->sts &= ~PP_AD_STS_DIRTY_DATA; ad->state |= PP_AD_STATE_DATA; pr_debug("dirty data, last_bl = %d\n", ad->last_bl); + if (!bl_mfd->ad_bl_level) + bl_mfd->ad_bl_level = bl_mfd->bl_level; bl = bl_mfd->ad_bl_level; if (ad->last_bl != bl) { @@ -6280,6 +6306,7 @@ static int mdss_mdp_ad_setup(struct msm_fb_data_type *mfd) memset(&ad->cfg, 0, sizeof(struct mdss_ad_cfg)); bl_mfd->ext_bl_ctrl = 0; bl_mfd->ext_ad_ctrl = -1; + bl_mfd->ad_bl_level = 0; } ad->state &= ~PP_AD_STATE_RUN; } @@ -6384,7 +6411,7 @@ static void pp_ad_cfg_lut(char __iomem *addr, u32 *data) } /* must call this function from within ad->lock */ -static int pp_ad_attenuate_bl(struct mdss_ad_info *ad, u32 bl, u32 *bl_out) +static int pp_ad_attenuate_bl(struct mdss_ad_info *ad, u32 bl, u32 *bl_out) { u32 shift = 0, ratio_temp = 0; u32 n, lut_interval, bl_att; @@ -7401,8 +7428,8 @@ static int pp_mfd_ad_release_all(struct msm_fb_data_type *mfd) ad->mfd = NULL; ad->bl_mfd = NULL; ad->state = 0; - cancel_work_sync(&ad->calc_work); mutex_unlock(&ad->lock); + cancel_work_sync(&ad->calc_work); ctl = mfd_to_ctl(mfd); if (ctl && ctl->ops.remove_vsync_handler) diff --git a/include/dt-bindings/clock/msm-clocks-cobalt.h b/include/dt-bindings/clock/msm-clocks-cobalt.h index d3cfc622b323..5cdeb01a173b 100644 --- a/include/dt-bindings/clock/msm-clocks-cobalt.h +++ b/include/dt-bindings/clock/msm-clocks-cobalt.h @@ -306,6 +306,11 @@ #define clk_csi2_clk_src 0x4113589f #define clk_csi3_clk_src 0xfd934012 #define clk_fd_core_clk_src 0xe4799ab7 +#define clk_ext_dp_phy_pll_vco 0x441b576b +#define clk_ext_dp_phy_pll_link 0xea12644c +#define clk_dp_link_clk_src 0x370d0626 +#define clk_dp_crypto_clk_src 0xf8faa811 +#define clk_dp_pixel_clk_src 0xf5dfbabf #define clk_ext_extpclk_clk_src 0xe5b273af #define clk_ext_pclk0_clk_src 0x087c1612 #define clk_ext_pclk1_clk_src 0x8067c5a3 @@ -405,6 +410,10 @@ #define clk_mmss_mdss_byte1_intf_clk 0xcf654d8e #define clk_mmss_mdss_byte1_intf_div_clk 0xcdf334c5 #define clk_mmss_mdss_dp_aux_clk 0x23125eb6 +#define clk_mmss_mdss_dp_crypto_clk 0x9a072d4e +#define clk_mmss_mdss_dp_link_clk 0x8dd302d1 +#define clk_mmss_mdss_dp_link_intf_clk 0x70e386e6 +#define clk_mmss_mdss_dp_pixel_clk 0xb707b765 #define clk_mmss_mdss_dp_gtc_clk 0xb59c151a #define clk_mmss_mdss_esc0_clk 0x5721ff83 #define clk_mmss_mdss_esc1_clk 0xc3d0376b diff --git a/include/linux/inet_lro.h b/include/linux/inet_lro.h index 9a715cfa1fe3..365fb3be7ee7 100644 --- a/include/linux/inet_lro.h +++ b/include/linux/inet_lro.h @@ -81,6 +81,7 @@ struct net_lro_mgr { #define LRO_F_EXTRACT_VLAN_ID 2 /* Set flag if VLAN IDs are extracted from received packets and eth protocol is still ETH_P_8021Q */ +#define LRO_F_NI 4 /* If not NAPI, Pass packets to stack via NI */ /* * Set for generated SKBs that are not added to @@ -122,6 +123,50 @@ struct net_lro_mgr { }; /* + * Large Receive Offload (LRO) information provided by the driver + * + * Fields must be set by driver when using the lro_receive_skb_ext() + */ +struct net_lro_info { + /* bitmask indicating the supported fields */ + unsigned long valid_fields; + /* + * Driver has checked the LRO eligibilty of the skb + */ + #define LRO_ELIGIBILITY_CHECKED (1 << 0) + /* + * Driver has provided the TCP payload checksum + */ + #define LRO_TCP_DATA_CSUM (1 << 1) + /* + * Driver has extracted the TCP window from the skb + * The value is in network format + */ + #define LRO_TCP_WIN (1 << 2) + /* + * Driver has extracted the TCP sequence number from skb + * The value is in network format + */ + #define LRO_TCP_SEQ_NUM (1 << 3) + /* + * Driver has extracted the TCP ack number from the skb + * The value is in network format + */ + #define LRO_TCP_ACK_NUM (1 << 4) + /* + * Driver has provided the LRO descriptor + */ + #define LRO_DESC (1 << 5) + + bool lro_eligible; + __wsum tcp_data_csum; + __be16 tcp_win; + __be32 tcp_seq_num; + __be32 tcp_ack_num; + struct net_lro_desc *lro_desc; +}; + +/* * Processes a SKB * * @lro_mgr: LRO manager to use @@ -133,10 +178,54 @@ struct net_lro_mgr { void lro_receive_skb(struct net_lro_mgr *lro_mgr, struct sk_buff *skb, void *priv); + +/* + * Processes an SKB + * + * This API provides means to pass any LRO information that has already + * been extracted by the driver + * + * @lro_mgr: LRO manager to use + * @skb: SKB to aggregate + * @priv: Private data that may be used by driver functions + * (for example get_tcp_ip_hdr) + * @lro_info: LRO information extracted by the driver + */ + +void lro_receive_skb_ext(struct net_lro_mgr *lro_mgr, + struct sk_buff *skb, + void *priv, + struct net_lro_info *lro_info); + +/* + * Processes a fragment list + * + * This functions aggregate fragments and generate SKBs do pass + * the packets to the stack. + * + * @lro_mgr: LRO manager to use + * @frags: Fragment to be processed. Must contain entire header in first + * element. + * @len: Length of received data + * @true_size: Actual size of memory the fragment is consuming + * @priv: Private data that may be used by driver functions + * (for example get_tcp_ip_hdr) + */ + +void lro_receive_frags(struct net_lro_mgr *lro_mgr, + struct skb_frag_struct *frags, + int len, int true_size, void *priv, __wsum sum); + /* * Forward all aggregated SKBs held by lro_mgr to network stack */ void lro_flush_all(struct net_lro_mgr *lro_mgr); +void lro_flush_pkt(struct net_lro_mgr *lro_mgr, + struct iphdr *iph, struct tcphdr *tcph); + +void lro_flush_desc(struct net_lro_mgr *lro_mgr, + struct net_lro_desc *lro_desc); + #endif diff --git a/include/linux/leds-qpnp-flash-v2.h b/include/linux/leds-qpnp-flash-v2.h index 353466f6c108..47fd0699a9c1 100644 --- a/include/linux/leds-qpnp-flash-v2.h +++ b/include/linux/leds-qpnp-flash-v2.h @@ -16,29 +16,9 @@ #include <linux/leds.h> #include "leds.h" -/* - * Configurations for each individual LED - */ -struct flash_node_data { - struct platform_device *pdev; - struct led_classdev cdev; - struct pinctrl *pinctrl; - struct pinctrl_state *gpio_state_active; - struct pinctrl_state *gpio_state_suspend; - int ires_ua; - u16 prgm_current; - u8 duration; - u8 id; - u8 type; - u8 ires; - u8 hdrm_val; - u8 brightness; - bool led_on; -}; +#define ENABLE_REGULATOR BIT(0) +#define QUERY_MAX_CURRENT BIT(1) -struct flash_switch_data { - struct platform_device *pdev; - struct led_classdev cdev; -}; +int qpnp_flash_led_prepare(struct led_classdev *led_cdev, int options); #endif diff --git a/include/linux/sched.h b/include/linux/sched.h index 61a5c00e66cd..06dd540192c7 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1482,6 +1482,7 @@ struct task_struct { u32 init_load_pct; u64 last_wake_ts; u64 last_switch_out_ts; + u64 last_cpu_selected_ts; struct related_thread_group *grp; struct list_head grp_list; u64 cpu_cycles; diff --git a/include/linux/usb.h b/include/linux/usb.h index b79925dd2b41..246945be000c 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -734,6 +734,17 @@ static inline bool usb_device_no_sg_constraint(struct usb_device *udev) /* for drivers using iso endpoints */ extern int usb_get_current_frame_number(struct usb_device *usb_dev); +extern int usb_sec_event_ring_setup(struct usb_device *dev, + unsigned intr_num); +extern int usb_sec_event_ring_cleanup(struct usb_device *dev, + unsigned intr_num); + +extern dma_addr_t +usb_get_sec_event_ring_dma_addr(struct usb_device *dev, + unsigned intr_num); +extern dma_addr_t usb_get_dcba_dma_addr(struct usb_device *dev); +extern dma_addr_t usb_get_xfer_ring_dma_addr(struct usb_device *dev, + struct usb_host_endpoint *ep); /* Sets up a group of bulk endpoints to support multiple stream IDs. */ extern int usb_alloc_streams(struct usb_interface *interface, diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index f89c24bd53a4..3740366d9fc5 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -396,6 +396,14 @@ struct hc_driver { /* Call for power on/off the port if necessary */ int (*port_power)(struct usb_hcd *hcd, int portnum, bool enable); + int (*sec_event_ring_setup)(struct usb_hcd *hcd, unsigned intr_num); + int (*sec_event_ring_cleanup)(struct usb_hcd *hcd, unsigned intr_num); + dma_addr_t (*get_sec_event_ring_dma_addr)(struct usb_hcd *hcd, + unsigned intr_num); + dma_addr_t (*get_xfer_ring_dma_addr)(struct usb_hcd *hcd, + struct usb_device *udev, struct usb_host_endpoint *ep); + dma_addr_t (*get_dcba_dma_addr)(struct usb_hcd *hcd, + struct usb_device *udev); }; static inline int hcd_giveback_urb_in_bh(struct usb_hcd *hcd) @@ -434,6 +442,17 @@ extern int usb_hcd_alloc_bandwidth(struct usb_device *udev, struct usb_host_interface *old_alt, struct usb_host_interface *new_alt); extern int usb_hcd_get_frame_number(struct usb_device *udev); +extern int usb_hcd_sec_event_ring_setup(struct usb_device *udev, + unsigned intr_num); +extern int usb_hcd_sec_event_ring_cleanup(struct usb_device *udev, + unsigned intr_num); +extern dma_addr_t +usb_hcd_get_sec_event_ring_dma_addr(struct usb_device *udev, + unsigned intr_num); +extern dma_addr_t usb_hcd_get_dcba_dma_addr(struct usb_device *udev); +extern dma_addr_t +usb_hcd_get_xfer_ring_dma_addr(struct usb_device *udev, + struct usb_host_endpoint *ep); extern struct usb_hcd *usb_create_hcd(const struct hc_driver *driver, struct device *dev, const char *bus_name); diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 647ebfe5174f..4984d372b04b 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -17,7 +17,7 @@ #include <linux/poll.h> #include <linux/dma-buf.h> -#define VB2_MAX_FRAME (32) +#define VB2_MAX_FRAME (64) #define VB2_MAX_PLANES (8) enum vb2_memory { diff --git a/include/net/cnss.h b/include/net/cnss.h index 78d68fd22ded..ab9b50100504 100644 --- a/include/net/cnss.h +++ b/include/net/cnss.h @@ -15,7 +15,6 @@ #include <linux/device.h> #include <linux/skbuff.h> #include <linux/pci.h> -#include <net/cnss_common.h> #include <linux/mmc/sdio_func.h> #ifdef CONFIG_CNSS @@ -212,4 +211,35 @@ extern int cnss_wlan_query_oob_status(void); extern int cnss_wlan_register_oob_irq_handler(oob_irq_handler_t handler, void *pm_oob); extern int cnss_wlan_unregister_oob_irq_handler(void *pm_oob); + + +extern void cnss_dump_stack(struct task_struct *task); +extern u8 *cnss_common_get_wlan_mac_address(struct device *dev, uint32_t *num); +extern void cnss_init_work(struct work_struct *work, work_func_t func); +extern void cnss_flush_delayed_work(void *dwork); +extern void cnss_flush_work(void *work); +extern void cnss_pm_wake_lock_timeout(struct wakeup_source *ws, ulong msec); +extern void cnss_pm_wake_lock_release(struct wakeup_source *ws); +extern void cnss_pm_wake_lock_destroy(struct wakeup_source *ws); +extern void cnss_get_monotonic_boottime(struct timespec *ts); +extern void cnss_get_boottime(struct timespec *ts); +extern void cnss_init_delayed_work(struct delayed_work *work, work_func_t + func); +extern int cnss_vendor_cmd_reply(struct sk_buff *skb); +extern int cnss_set_cpus_allowed_ptr(struct task_struct *task, ulong cpu); +extern int cnss_set_wlan_unsafe_channel(u16 *unsafe_ch_list, u16 ch_count); +extern int cnss_get_wlan_unsafe_channel(u16 *unsafe_ch_list, u16 *ch_count, + u16 buf_len); +extern int cnss_wlan_set_dfs_nol(const void *info, u16 info_len); +extern int cnss_wlan_get_dfs_nol(void *info, u16 info_len); +extern int cnss_common_request_bus_bandwidth(struct device *dev, int + bandwidth); +extern void cnss_common_device_crashed(struct device *dev); +extern void cnss_common_device_self_recovery(struct device *dev); +extern void *cnss_common_get_virt_ramdump_mem(struct device *dev, unsigned long + *size); +extern void cnss_common_schedule_recovery_work(struct device *dev); +extern int cnss_common_set_wlan_mac_address(struct device *dev, const u8 *in, + uint32_t len); +extern u8 *cnss_common_get_wlan_mac_address(struct device *dev, uint32_t *num); #endif /* _NET_CNSS_H_ */ diff --git a/include/net/cnss_common.h b/include/net/cnss_common.h deleted file mode 100644 index c5175a190b37..000000000000 --- a/include/net/cnss_common.h +++ /dev/null @@ -1,73 +0,0 @@ -/* Copyright (c) 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 _NET_CNSS_COMMON_H_ -#define _NET_CNSS_COMMON_H_ - -#ifdef CONFIG_CNSS - -#define MAX_FIRMWARE_SIZE (1 * 1024 * 1024) -/* max 20mhz channel count */ -#define CNSS_MAX_CH_NUM 45 - -extern int cnss_set_wlan_unsafe_channel(u16 *unsafe_ch_list, u16 ch_count); -extern int cnss_get_wlan_unsafe_channel(u16 *unsafe_ch_list, - u16 *ch_count, u16 buf_len); - -extern int cnss_wlan_set_dfs_nol(const void *info, u16 info_len); -extern int cnss_wlan_get_dfs_nol(void *info, u16 info_len); - -extern void cnss_init_work(struct work_struct *work, work_func_t func); -extern void cnss_flush_work(void *work); -extern void cnss_flush_delayed_work(void *dwork); -extern void cnss_pm_wake_lock_timeout(struct wakeup_source *ws, ulong msec); -extern void cnss_pm_wake_lock_release(struct wakeup_source *ws); -extern void cnss_pm_wake_lock_destroy(struct wakeup_source *ws); -extern void cnss_get_monotonic_boottime(struct timespec *ts); -extern void cnss_get_boottime(struct timespec *ts); -extern void cnss_init_delayed_work(struct delayed_work *work, work_func_t func); -extern int cnss_vendor_cmd_reply(struct sk_buff *skb); -extern int cnss_set_cpus_allowed_ptr(struct task_struct *task, ulong cpu); -extern void cnss_dump_stack(struct task_struct *task); - -int cnss_pci_request_bus_bandwidth(int bandwidth); -int cnss_sdio_request_bus_bandwidth(int bandwidth); -extern int cnss_common_request_bus_bandwidth(struct device *dev, - int bandwidth); - -void cnss_sdio_device_crashed(void); -void cnss_pci_device_crashed(void); -extern void cnss_common_device_crashed(struct device *dev); - -void cnss_pci_device_self_recovery(void); -void cnss_sdio_device_self_recovery(void); -extern void cnss_common_device_self_recovery(struct device *dev); - -void *cnss_pci_get_virt_ramdump_mem(unsigned long *size); -void *cnss_sdio_get_virt_ramdump_mem(unsigned long *size); -extern void *cnss_common_get_virt_ramdump_mem(struct device *dev, - unsigned long *size); - -void cnss_sdio_schedule_recovery_work(void); -void cnss_pci_schedule_recovery_work(void); -extern void cnss_common_schedule_recovery_work(struct device *dev); - -extern int cnss_pcie_set_wlan_mac_address(const u8 *in, uint32_t len); -extern int cnss_sdio_set_wlan_mac_address(const u8 *in, uint32_t len); -extern int cnss_common_set_wlan_mac_address(struct device *dev, - const u8 *in, uint32_t len); - -u8 *cnss_pci_get_wlan_mac_address(uint32_t *num); -u8 *cnss_sdio_get_wlan_mac_address(uint32_t *num); -extern u8 *cnss_common_get_wlan_mac_address(struct device *dev, uint32_t *num); -#endif -#endif /* _NET_CNSS_COMMON_H_ */ diff --git a/include/soc/qcom/clock-local2.h b/include/soc/qcom/clock-local2.h index 7f785cf4d3a2..4f2fa36e920f 100644 --- a/include/soc/qcom/clock-local2.h +++ b/include/soc/qcom/clock-local2.h @@ -255,6 +255,7 @@ extern struct clk_ops clk_ops_branch_hw_ctl; extern struct clk_ops clk_ops_vote; extern struct clk_ops clk_ops_rcg_hdmi; extern struct clk_ops clk_ops_rcg_edp; +extern struct clk_ops clk_ops_rcg_dp; extern struct clk_ops clk_ops_byte; extern struct clk_ops clk_ops_pixel; extern struct clk_ops clk_ops_byte_multiparent; diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index c7bb78a0d57b..46b0402a730f 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -70,7 +70,7 @@ * Common stuff for both V4L1 and V4L2 * Moved from videodev.h */ -#define VIDEO_MAX_FRAME 32 +#define VIDEO_MAX_FRAME 64 #define VIDEO_MAX_PLANES 8 /* diff --git a/include/uapi/media/msm_sde_rotator.h b/include/uapi/media/msm_sde_rotator.h index d771959f2e03..461a171a42c1 100644 --- a/include/uapi/media/msm_sde_rotator.h +++ b/include/uapi/media/msm_sde_rotator.h @@ -13,9 +13,23 @@ #define SDE_PIX_FMT_BGRA_8888 V4L2_PIX_FMT_ARGB32 #define SDE_PIX_FMT_RGBX_8888 v4l2_fourcc('X', 'B', '2', '4') #define SDE_PIX_FMT_BGRX_8888 V4L2_PIX_FMT_XRGB32 +#define SDE_PIX_FMT_XBGR_8888 v4l2_fourcc('R', 'X', '2', '4') #define SDE_PIX_FMT_RGBA_5551 v4l2_fourcc('R', 'A', '1', '5') +#define SDE_PIX_FMT_ARGB_1555 V4L2_PIX_FMT_ARGB555 +#define SDE_PIX_FMT_ABGR_1555 v4l2_fourcc('A', 'B', '1', '5') +#define SDE_PIX_FMT_BGRA_5551 v4l2_fourcc('B', 'A', '1', '5') +#define SDE_PIX_FMT_BGRX_5551 v4l2_fourcc('B', 'X', '1', '5') +#define SDE_PIX_FMT_RGBX_5551 v4l2_fourcc('R', 'X', '1', '5') +#define SDE_PIX_FMT_XBGR_1555 v4l2_fourcc('X', 'B', '1', '5') +#define SDE_PIX_FMT_XRGB_1555 V4L2_PIX_FMT_XRGB555 #define SDE_PIX_FMT_ARGB_4444 V4L2_PIX_FMT_ARGB444 #define SDE_PIX_FMT_RGBA_4444 v4l2_fourcc('R', 'A', '1', '2') +#define SDE_PIX_FMT_BGRA_4444 v4l2_fourcc('B', 'A', '1', '2') +#define SDE_PIX_FMT_ABGR_4444 v4l2_fourcc('A', 'B', '1', '2') +#define SDE_PIX_FMT_RGBX_4444 v4l2_fourcc('R', 'X', '1', '2') +#define SDE_PIX_FMT_XRGB_4444 V4L2_PIX_FMT_XRGB444 +#define SDE_PIX_FMT_BGRX_4444 v4l2_fourcc('B', 'X', '1', '2') +#define SDE_PIX_FMT_XBGR_4444 v4l2_fourcc('X', 'B', '1', '2') #define SDE_PIX_FMT_RGB_888 V4L2_PIX_FMT_RGB24 #define SDE_PIX_FMT_BGR_888 V4L2_PIX_FMT_BGR24 #define SDE_PIX_FMT_RGB_565 V4L2_PIX_FMT_RGB565 diff --git a/include/uapi/media/msmb_camera.h b/include/uapi/media/msmb_camera.h index 092551d0cc16..fe70daa772df 100644 --- a/include/uapi/media/msmb_camera.h +++ b/include/uapi/media/msmb_camera.h @@ -202,5 +202,24 @@ struct msm_camera_user_buf_cont_t { unsigned int buf_idx[MSM_CAMERA_MAX_USER_BUFF_CNT]; }; +struct msm_camera_return_buf { + __u32 index; + __u32 reserved; +}; + +#define MSM_CAMERA_PRIV_IOCTL_ID_BASE 0 +#define MSM_CAMERA_PRIV_IOCTL_ID_RETURN_BUF 1 + +struct msm_camera_private_ioctl_arg { + __u32 id; + __u32 size; + __u32 result; + __u32 reserved; + __user __u64 ioctl_ptr; +}; + +#define VIDIOC_MSM_CAMERA_PRIVATE_IOCTL_CMD \ + _IOWR('V', BASE_VIDIOC_PRIVATE, struct msm_camera_private_ioctl_arg) + #endif diff --git a/include/uapi/media/msmb_generic_buf_mgr.h b/include/uapi/media/msmb_generic_buf_mgr.h index a68b174b97bb..2961cae1e7c1 100644 --- a/include/uapi/media/msmb_generic_buf_mgr.h +++ b/include/uapi/media/msmb_generic_buf_mgr.h @@ -34,6 +34,9 @@ struct msm_buf_mngr_main_cont_info { int32_t cont_fd; }; +#define MSM_CAMERA_BUF_MNGR_IOCTL_ID_BASE 0 +#define MSM_CAMERA_BUF_MNGR_IOCTL_ID_GET_BUF_BY_IDX 1 + #define VIDIOC_MSM_BUF_MNGR_GET_BUF \ _IOWR('V', BASE_VIDIOC_PRIVATE + 33, struct msm_buf_mngr_info) @@ -55,5 +58,9 @@ struct msm_buf_mngr_main_cont_info { #define VIDIOC_MSM_BUF_MNGR_FLUSH \ _IOWR('V', BASE_VIDIOC_PRIVATE + 39, struct msm_buf_mngr_info) +#define VIDIOC_MSM_BUF_MNGR_IOCTL_CMD \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 40, \ + struct msm_camera_private_ioctl_arg) + #endif diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 2fd5c5688dd0..631dbb0a7041 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -3056,6 +3056,7 @@ static inline void mark_task_starting(struct task_struct *p) wallclock = sched_ktime_clock(); p->ravg.mark_start = p->last_wake_ts = wallclock; + p->last_cpu_selected_ts = wallclock; p->last_switch_out_ts = 0; update_task_cpu_cycles(p, cpu_of(rq)); } diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index b9ee3dc4a1a6..859416724e5a 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -2678,6 +2678,9 @@ static unsigned int __read_mostly sched_short_sleep_task_threshold = 2000 * NSEC_PER_USEC; unsigned int __read_mostly sysctl_sched_select_prev_cpu_us = 2000; +static unsigned int __read_mostly +sched_long_cpu_selection_threshold = 100 * NSEC_PER_MSEC; + unsigned int __read_mostly sysctl_sched_restrict_cluster_spill; void update_up_down_migrate(void) @@ -3455,6 +3458,7 @@ bias_to_prev_cpu(struct cpu_select_env *env, struct cluster_cpu_stats *stats) struct sched_cluster *cluster; if (env->boost_type != SCHED_BOOST_NONE || env->reason || + !task->ravg.mark_start || env->need_idle || !sched_short_sleep_task_threshold) return false; @@ -3463,6 +3467,10 @@ bias_to_prev_cpu(struct cpu_select_env *env, struct cluster_cpu_stats *stats) unlikely(!cpu_active(prev_cpu))) return false; + if (task->ravg.mark_start - task->last_cpu_selected_ts >= + sched_long_cpu_selection_threshold) + return false; + /* * This function should be used by task wake up path only as it's * assuming p->last_switch_out_ts as last sleep time. @@ -3604,6 +3612,7 @@ retry: if (stats.best_capacity_cpu >= 0) target = stats.best_capacity_cpu; } + p->last_cpu_selected_ts = sched_ktime_clock(); out: rcu_read_unlock(); diff --git a/net/ipv4/inet_lro.c b/net/ipv4/inet_lro.c index f17ea49b28fb..8f26145c34e2 100644 --- a/net/ipv4/inet_lro.c +++ b/net/ipv4/inet_lro.c @@ -145,20 +145,37 @@ static __wsum lro_tcp_data_csum(struct iphdr *iph, struct tcphdr *tcph, int len) } static void lro_init_desc(struct net_lro_desc *lro_desc, struct sk_buff *skb, - struct iphdr *iph, struct tcphdr *tcph) + struct iphdr *iph, struct tcphdr *tcph, + struct net_lro_info *lro_info) { int nr_frags; __be32 *ptr; u32 tcp_data_len = TCP_PAYLOAD_LENGTH(iph, tcph); + u64 hw_marked = 0; + + if (lro_info) + hw_marked = lro_info->valid_fields; nr_frags = skb_shinfo(skb)->nr_frags; lro_desc->parent = skb; lro_desc->next_frag = &(skb_shinfo(skb)->frags[nr_frags]); lro_desc->iph = iph; lro_desc->tcph = tcph; - lro_desc->tcp_next_seq = ntohl(tcph->seq) + tcp_data_len; - lro_desc->tcp_ack = tcph->ack_seq; - lro_desc->tcp_window = tcph->window; + + if (hw_marked & LRO_TCP_SEQ_NUM) + lro_desc->tcp_next_seq = lro_info->tcp_seq_num + tcp_data_len; + else + lro_desc->tcp_next_seq = ntohl(tcph->seq) + tcp_data_len; + + if (hw_marked & LRO_TCP_ACK_NUM) + lro_desc->tcp_ack = htonl(lro_info->tcp_ack_num); + else + lro_desc->tcp_ack = tcph->ack_seq; + + if (hw_marked & LRO_TCP_WIN) + lro_desc->tcp_window = htons(lro_info->tcp_win); + else + lro_desc->tcp_window = tcph->window; lro_desc->pkt_aggr_cnt = 1; lro_desc->ip_tot_len = ntohs(iph->tot_len); @@ -173,8 +190,11 @@ static void lro_init_desc(struct net_lro_desc *lro_desc, struct sk_buff *skb, lro_desc->mss = tcp_data_len; lro_desc->active = 1; - lro_desc->data_csum = lro_tcp_data_csum(iph, tcph, - tcp_data_len); + if (hw_marked & LRO_TCP_DATA_CSUM) + lro_desc->data_csum = lro_info->tcp_data_csum; + else + lro_desc->data_csum = lro_tcp_data_csum(iph, tcph, + tcp_data_len); } static inline void lro_clear_desc(struct net_lro_desc *lro_desc) @@ -183,16 +203,29 @@ static inline void lro_clear_desc(struct net_lro_desc *lro_desc) } static void lro_add_common(struct net_lro_desc *lro_desc, struct iphdr *iph, - struct tcphdr *tcph, int tcp_data_len) + struct tcphdr *tcph, int tcp_data_len, + struct net_lro_info *lro_info) { struct sk_buff *parent = lro_desc->parent; __be32 *topt; + u64 hw_marked = 0; + + if (lro_info) + hw_marked = lro_info->valid_fields; lro_desc->pkt_aggr_cnt++; lro_desc->ip_tot_len += tcp_data_len; lro_desc->tcp_next_seq += tcp_data_len; - lro_desc->tcp_window = tcph->window; - lro_desc->tcp_ack = tcph->ack_seq; + + if (hw_marked & LRO_TCP_WIN) + lro_desc->tcp_window = htons(lro_info->tcp_win); + else + lro_desc->tcp_window = tcph->window; + + if (hw_marked & LRO_TCP_ACK_NUM) + lro_desc->tcp_ack = htonl(lro_info->tcp_ack_num); + else + lro_desc->tcp_ack = tcph->ack_seq; /* don't update tcp_rcv_tsval, would not work with PAWS */ if (lro_desc->tcp_saw_tstamp) { @@ -200,10 +233,17 @@ static void lro_add_common(struct net_lro_desc *lro_desc, struct iphdr *iph, lro_desc->tcp_rcv_tsecr = *(topt + 2); } - lro_desc->data_csum = csum_block_add(lro_desc->data_csum, - lro_tcp_data_csum(iph, tcph, - tcp_data_len), - parent->len); + if (hw_marked & LRO_TCP_DATA_CSUM) + lro_desc->data_csum = csum_block_add(lro_desc->data_csum, + lro_info->tcp_data_csum, + parent->len); + else + lro_desc->data_csum = + csum_block_add(lro_desc->data_csum, + lro_tcp_data_csum(iph, + tcph, + tcp_data_len), + parent->len); parent->len += tcp_data_len; parent->data_len += tcp_data_len; @@ -212,12 +252,13 @@ static void lro_add_common(struct net_lro_desc *lro_desc, struct iphdr *iph, } static void lro_add_packet(struct net_lro_desc *lro_desc, struct sk_buff *skb, - struct iphdr *iph, struct tcphdr *tcph) + struct iphdr *iph, struct tcphdr *tcph, + struct net_lro_info *lro_info) { struct sk_buff *parent = lro_desc->parent; int tcp_data_len = TCP_PAYLOAD_LENGTH(iph, tcph); - lro_add_common(lro_desc, iph, tcph, tcp_data_len); + lro_add_common(lro_desc, iph, tcph, tcp_data_len, lro_info); skb_pull(skb, (skb->len - tcp_data_len)); parent->truesize += skb->truesize; @@ -230,6 +271,29 @@ static void lro_add_packet(struct net_lro_desc *lro_desc, struct sk_buff *skb, lro_desc->last_skb = skb; } +static void lro_add_frags(struct net_lro_desc *lro_desc, + int len, int hlen, int truesize, + struct skb_frag_struct *skb_frags, + struct iphdr *iph, struct tcphdr *tcph) +{ + struct sk_buff *skb = lro_desc->parent; + int tcp_data_len = TCP_PAYLOAD_LENGTH(iph, tcph); + + lro_add_common(lro_desc, iph, tcph, tcp_data_len, NULL); + + skb->truesize += truesize; + + skb_frags[0].page_offset += hlen; + skb_frag_size_sub(&skb_frags[0], hlen); + + while (tcp_data_len > 0) { + *lro_desc->next_frag = *skb_frags; + tcp_data_len -= skb_frag_size(skb_frags); + lro_desc->next_frag++; + skb_frags++; + skb_shinfo(skb)->nr_frags++; + } +} static int lro_check_tcp_conn(struct net_lro_desc *lro_desc, struct iphdr *iph, @@ -284,6 +348,8 @@ static void lro_flush(struct net_lro_mgr *lro_mgr, if (lro_mgr->features & LRO_F_NAPI) netif_receive_skb(lro_desc->parent); + else if (lro_mgr->features & LRO_F_NI) + netif_rx_ni(lro_desc->parent); else netif_rx(lro_desc->parent); @@ -292,12 +358,13 @@ static void lro_flush(struct net_lro_mgr *lro_mgr, } static int __lro_proc_skb(struct net_lro_mgr *lro_mgr, struct sk_buff *skb, - void *priv) + void *priv, struct net_lro_info *lro_info) { struct net_lro_desc *lro_desc; struct iphdr *iph; struct tcphdr *tcph; u64 flags; + u64 hw_marked = 0; int vlan_hdr_len = 0; if (!lro_mgr->get_skb_header || @@ -308,7 +375,14 @@ static int __lro_proc_skb(struct net_lro_mgr *lro_mgr, struct sk_buff *skb, if (!(flags & LRO_IPV4) || !(flags & LRO_TCP)) goto out; - lro_desc = lro_get_desc(lro_mgr, lro_mgr->lro_arr, iph, tcph); + if (lro_info) + hw_marked = lro_info->valid_fields; + + if (hw_marked & LRO_DESC) + lro_desc = lro_info->lro_desc; + else + lro_desc = lro_get_desc(lro_mgr, lro_mgr->lro_arr, iph, tcph); + if (!lro_desc) goto out; @@ -317,22 +391,38 @@ static int __lro_proc_skb(struct net_lro_mgr *lro_mgr, struct sk_buff *skb, vlan_hdr_len = VLAN_HLEN; if (!lro_desc->active) { /* start new lro session */ - if (lro_tcp_ip_check(iph, tcph, skb->len - vlan_hdr_len, NULL)) - goto out; + if (hw_marked & LRO_ELIGIBILITY_CHECKED) { + if (!lro_info->lro_eligible) + goto out; + } else { + if (lro_tcp_ip_check(iph, tcph, + skb->len - vlan_hdr_len, NULL)) + goto out; + } skb->ip_summed = lro_mgr->ip_summed_aggr; - lro_init_desc(lro_desc, skb, iph, tcph); + lro_init_desc(lro_desc, skb, iph, tcph, lro_info); LRO_INC_STATS(lro_mgr, aggregated); return 0; } - if (lro_desc->tcp_next_seq != ntohl(tcph->seq)) - goto out2; + if (hw_marked & LRO_TCP_SEQ_NUM) { + if (lro_desc->tcp_next_seq != lro_info->tcp_seq_num) + goto out2; + } else { + if (lro_desc->tcp_next_seq != ntohl(tcph->seq)) + goto out2; + } - if (lro_tcp_ip_check(iph, tcph, skb->len, lro_desc)) - goto out2; + if (hw_marked & LRO_ELIGIBILITY_CHECKED) { + if (!lro_info->lro_eligible) + goto out2; + } else { + if (lro_tcp_ip_check(iph, tcph, skb->len, lro_desc)) + goto out2; + } - lro_add_packet(lro_desc, skb, iph, tcph); + lro_add_packet(lro_desc, skb, iph, tcph, lro_info); LRO_INC_STATS(lro_mgr, aggregated); if ((lro_desc->pkt_aggr_cnt >= lro_mgr->max_aggr) || @@ -348,19 +438,161 @@ out: return 1; } +static struct sk_buff *lro_gen_skb(struct net_lro_mgr *lro_mgr, + struct skb_frag_struct *frags, + int len, int true_size, + void *mac_hdr, + int hlen, __wsum sum, + u32 ip_summed) +{ + struct sk_buff *skb; + struct skb_frag_struct *skb_frags; + int data_len = len; + int hdr_len = min(len, hlen); + + skb = netdev_alloc_skb(lro_mgr->dev, hlen + lro_mgr->frag_align_pad); + if (!skb) + return NULL; + + skb_reserve(skb, lro_mgr->frag_align_pad); + skb->len = len; + skb->data_len = len - hdr_len; + skb->truesize += true_size; + skb->tail += hdr_len; + + memcpy(skb->data, mac_hdr, hdr_len); + + skb_frags = skb_shinfo(skb)->frags; + while (data_len > 0) { + *skb_frags = *frags; + data_len -= skb_frag_size(frags); + skb_frags++; + frags++; + skb_shinfo(skb)->nr_frags++; + } + + skb_shinfo(skb)->frags[0].page_offset += hdr_len; + skb_frag_size_sub(&skb_shinfo(skb)->frags[0], hdr_len); + + skb->ip_summed = ip_summed; + skb->csum = sum; + skb->protocol = eth_type_trans(skb, lro_mgr->dev); + return skb; +} + +static struct sk_buff *__lro_proc_segment(struct net_lro_mgr *lro_mgr, + struct skb_frag_struct *frags, + int len, int true_size, + void *priv, __wsum sum) +{ + struct net_lro_desc *lro_desc; + struct iphdr *iph; + struct tcphdr *tcph; + struct sk_buff *skb; + u64 flags; + void *mac_hdr; + int mac_hdr_len; + int hdr_len = LRO_MAX_PG_HLEN; + int vlan_hdr_len = 0; + + if (!lro_mgr->get_frag_header || + lro_mgr->get_frag_header(frags, (void *)&mac_hdr, (void *)&iph, + (void *)&tcph, &flags, priv)) { + mac_hdr = skb_frag_address(frags); + goto out1; + } + + if (!(flags & LRO_IPV4) || !(flags & LRO_TCP)) + goto out1; + + hdr_len = (int)((void *)(tcph) + TCP_HDR_LEN(tcph) - mac_hdr); + mac_hdr_len = (int)((void *)(iph) - mac_hdr); + + lro_desc = lro_get_desc(lro_mgr, lro_mgr->lro_arr, iph, tcph); + if (!lro_desc) + goto out1; + + if (!lro_desc->active) { /* start new lro session */ + if (lro_tcp_ip_check(iph, tcph, len - mac_hdr_len, NULL)) + goto out1; + + skb = lro_gen_skb(lro_mgr, frags, len, true_size, mac_hdr, + hdr_len, 0, lro_mgr->ip_summed_aggr); + if (!skb) + goto out; + + if ((skb->protocol == htons(ETH_P_8021Q)) && + !(lro_mgr->features & LRO_F_EXTRACT_VLAN_ID)) + vlan_hdr_len = VLAN_HLEN; + + iph = (void *)(skb->data + vlan_hdr_len); + tcph = (void *)((u8 *)skb->data + vlan_hdr_len + + IP_HDR_LEN(iph)); + + lro_init_desc(lro_desc, skb, iph, tcph, NULL); + LRO_INC_STATS(lro_mgr, aggregated); + return NULL; + } + + if (lro_desc->tcp_next_seq != ntohl(tcph->seq)) + goto out2; + + if (lro_tcp_ip_check(iph, tcph, len - mac_hdr_len, lro_desc)) + goto out2; + + lro_add_frags(lro_desc, len, hdr_len, true_size, frags, iph, tcph); + LRO_INC_STATS(lro_mgr, aggregated); + + if ((skb_shinfo(lro_desc->parent)->nr_frags >= lro_mgr->max_aggr) || + lro_desc->parent->len > (0xFFFF - lro_mgr->dev->mtu)) + lro_flush(lro_mgr, lro_desc); + + return NULL; + +out2: /* send aggregated packets to the stack */ + lro_flush(lro_mgr, lro_desc); + +out1: /* Original packet has to be posted to the stack */ + skb = lro_gen_skb(lro_mgr, frags, len, true_size, mac_hdr, + hdr_len, sum, lro_mgr->ip_summed); +out: + return skb; +} + void lro_receive_skb(struct net_lro_mgr *lro_mgr, struct sk_buff *skb, void *priv) { - if (__lro_proc_skb(lro_mgr, skb, priv)) { + if (__lro_proc_skb(lro_mgr, skb, priv, NULL)) { if (lro_mgr->features & LRO_F_NAPI) netif_receive_skb(skb); + else if (lro_mgr->features & LRO_F_NI) + netif_rx_ni(skb); else netif_rx(skb); } } EXPORT_SYMBOL(lro_receive_skb); +void lro_receive_frags(struct net_lro_mgr *lro_mgr, + struct skb_frag_struct *frags, + int len, int true_size, void *priv, __wsum sum) +{ + struct sk_buff *skb; + + skb = __lro_proc_segment(lro_mgr, frags, len, true_size, priv, sum); + if (!skb) + return; + + if (lro_mgr->features & LRO_F_NAPI) + netif_receive_skb(skb); + else if (lro_mgr->features & LRO_F_NI) + netif_rx_ni(skb); + else + netif_rx(skb); +} +EXPORT_SYMBOL(lro_receive_frags); + void lro_flush_all(struct net_lro_mgr *lro_mgr) { int i; @@ -372,3 +604,35 @@ void lro_flush_all(struct net_lro_mgr *lro_mgr) } } EXPORT_SYMBOL(lro_flush_all); + +void lro_flush_pkt(struct net_lro_mgr *lro_mgr, struct iphdr *iph, + struct tcphdr *tcph) +{ + struct net_lro_desc *lro_desc; + + lro_desc = lro_get_desc(lro_mgr, lro_mgr->lro_arr, iph, tcph); + if (lro_desc && lro_desc->active) + lro_flush(lro_mgr, lro_desc); +} +EXPORT_SYMBOL(lro_flush_pkt); + +void lro_flush_desc(struct net_lro_mgr *lro_mgr, struct net_lro_desc *lro_desc) +{ + if (lro_desc->active) + lro_flush(lro_mgr, lro_desc); +} +EXPORT_SYMBOL(lro_flush_desc); + +void lro_receive_skb_ext(struct net_lro_mgr *lro_mgr, struct sk_buff *skb, + void *priv, struct net_lro_info *lro_info) +{ + if (__lro_proc_skb(lro_mgr, skb, priv, lro_info)) { + if (lro_mgr->features & LRO_F_NAPI) + netif_receive_skb(skb); + else if (lro_mgr->features & LRO_F_NI) + netif_rx_ni(skb); + else + netif_rx(skb); + } +} +EXPORT_SYMBOL(lro_receive_skb_ext); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 816c9075ba3c..ad4b729262fd 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3456,16 +3456,16 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) params.smps_mode = NL80211_SMPS_OFF; } + params.pbss = nla_get_flag(info->attrs[NL80211_ATTR_PBSS]); + if (params.pbss && !rdev->wiphy.bands[IEEE80211_BAND_60GHZ]) + return -EOPNOTSUPP; + if (info->attrs[NL80211_ATTR_ACL_POLICY]) { params.acl = parse_acl_data(&rdev->wiphy, info); if (IS_ERR(params.acl)) return PTR_ERR(params.acl); } - params.pbss = nla_get_flag(info->attrs[NL80211_ATTR_PBSS]); - if (params.pbss && !rdev->wiphy.bands[IEEE80211_BAND_60GHZ]) - return -EOPNOTSUPP; - wdev_lock(wdev); err = rdev_start_ap(rdev, dev, ¶ms); if (!err) { diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 913bc0e06098..2963d00ea6e5 100755 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -967,7 +967,7 @@ config SND_SOC_MSM_STUB config SND_SOC_MSM_HDMI_CODEC_RX bool "HDMI Audio Playback" - depends on FB_MSM_MDSS_HDMI_PANEL && (SND_SOC_APQ8084 || SND_SOC_MSM8994 || SND_SOC_MSM8996) + depends on FB_MSM_MDSS_HDMI_PANEL && (SND_SOC_APQ8084 || SND_SOC_MSM8994 || SND_SOC_MSM8996 || SND_SOC_MSMCOBALT) help HDMI audio drivers should be built only if the platform supports hdmi panel. diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index c941729be2aa..d8787ed249db 100755 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -349,6 +349,12 @@ enum { CPE_NOMINAL, HPH_PA_DELAY, SB_CLK_GEAR, + ANC_MIC_AMIC1, + ANC_MIC_AMIC2, + ANC_MIC_AMIC3, + ANC_MIC_AMIC4, + ANC_MIC_AMIC5, + ANC_MIC_AMIC6, }; enum { @@ -576,6 +582,7 @@ static struct snd_soc_dai_driver tasha_dai[]; static int wcd9335_get_micb_vout_ctl_val(u32 micb_mv); static int tasha_config_compander(struct snd_soc_codec *, int, int); +static void tasha_codec_set_tx_hold(struct snd_soc_codec *, u16, bool); /* Hold instance to soundwire platform device */ struct tasha_swr_ctrl_data { @@ -3775,7 +3782,7 @@ static int tasha_codec_enable_anc(struct snd_soc_dapm_widget *w, WCD9335_CDC_ANC0_IIR_COEFF_2_CTL); anc_writes_size = anc_cal_size / 2; snd_soc_update_bits(codec, - WCD9335_CDC_ANC0_CLK_RESET_CTL, 0x38, 0x38); + WCD9335_CDC_ANC0_CLK_RESET_CTL, 0x39, 0x39); } else if (!strcmp(w->name, "RX INT2 DAC") || !strcmp(w->name, "RX INT4 DAC")) { tasha_realign_anc_coeff(codec, @@ -3783,7 +3790,7 @@ static int tasha_codec_enable_anc(struct snd_soc_dapm_widget *w, WCD9335_CDC_ANC1_IIR_COEFF_2_CTL); i = anc_cal_size / 2; snd_soc_update_bits(codec, - WCD9335_CDC_ANC1_CLK_RESET_CTL, 0x38, 0x38); + WCD9335_CDC_ANC1_CLK_RESET_CTL, 0x39, 0x39); } for (; i < anc_writes_size; i++) { @@ -3793,16 +3800,23 @@ static int tasha_codec_enable_anc(struct snd_soc_dapm_widget *w, if (!strcmp(w->name, "RX INT1 DAC") || !strcmp(w->name, "RX INT3 DAC")) { snd_soc_update_bits(codec, - WCD9335_CDC_ANC0_CLK_RESET_CTL, 0x38, 0x00); + WCD9335_CDC_ANC0_CLK_RESET_CTL, 0x08, 0x08); } else if (!strcmp(w->name, "RX INT2 DAC") || !strcmp(w->name, "RX INT4 DAC")) { snd_soc_update_bits(codec, - WCD9335_CDC_ANC1_CLK_RESET_CTL, 0x38, 0x00); + WCD9335_CDC_ANC1_CLK_RESET_CTL, 0x08, 0x08); } if (!hwdep_cal) release_firmware(fw); break; + case SND_SOC_DAPM_POST_PMU: + /* Remove ANC Rx from reset */ + snd_soc_update_bits(codec, WCD9335_CDC_ANC0_CLK_RESET_CTL, + 0x08, 0x00); + snd_soc_update_bits(codec, WCD9335_CDC_ANC1_CLK_RESET_CTL, + 0x08, 0x00); + break; case SND_SOC_DAPM_POST_PMD: if (!strcmp(w->name, "ANC HPHL PA") || !strcmp(w->name, "ANC EAR PA") || @@ -3842,6 +3856,22 @@ err: return ret; } +static void tasha_codec_clear_anc_tx_hold(struct tasha_priv *tasha) +{ + if (test_and_clear_bit(ANC_MIC_AMIC1, &tasha->status_mask)) + tasha_codec_set_tx_hold(tasha->codec, WCD9335_ANA_AMIC1, false); + if (test_and_clear_bit(ANC_MIC_AMIC2, &tasha->status_mask)) + tasha_codec_set_tx_hold(tasha->codec, WCD9335_ANA_AMIC2, false); + if (test_and_clear_bit(ANC_MIC_AMIC3, &tasha->status_mask)) + tasha_codec_set_tx_hold(tasha->codec, WCD9335_ANA_AMIC3, false); + if (test_and_clear_bit(ANC_MIC_AMIC4, &tasha->status_mask)) + tasha_codec_set_tx_hold(tasha->codec, WCD9335_ANA_AMIC4, false); + if (test_and_clear_bit(ANC_MIC_AMIC5, &tasha->status_mask)) + tasha_codec_set_tx_hold(tasha->codec, WCD9335_ANA_AMIC5, false); + if (test_and_clear_bit(ANC_MIC_AMIC6, &tasha->status_mask)) + tasha_codec_set_tx_hold(tasha->codec, WCD9335_ANA_AMIC6, false); +} + static void tasha_codec_hph_post_pa_config(struct tasha_priv *tasha, int mode, int event) { @@ -3860,6 +3890,13 @@ static void tasha_codec_hph_post_pa_config(struct tasha_priv *tasha, scale_val = 0x1; break; } + if (tasha->anc_func) { + /* Clear Tx FE HOLD if both PAs are enabled */ + if ((snd_soc_read(tasha->codec, WCD9335_ANA_HPH) & + 0xC0) == 0xC0) { + tasha_codec_clear_anc_tx_hold(tasha); + } + } break; case SND_SOC_DAPM_PRE_PMD: scale_val = 0x6; @@ -3926,9 +3963,20 @@ static int tasha_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: + if ((!(strcmp(w->name, "ANC HPHR PA"))) && + (test_bit(HPH_PA_DELAY, &tasha->status_mask))) { + snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0xC0, 0xC0); + } set_bit(HPH_PA_DELAY, &tasha->status_mask); break; case SND_SOC_DAPM_POST_PMU: + if ((snd_soc_read(codec, WCD9335_ANA_HPH) & 0xC0) != 0xC0) + /* + * If PA_EN is not set (potentially in ANC case) then + * do nothing for POST_PMU and let left channel handle + * everything. + */ + break; /* * 7ms sleep is required after PA is enabled as per * HW requirement @@ -3946,13 +3994,31 @@ static int tasha_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, snd_soc_update_bits(codec, WCD9335_CDC_RX2_RX_PATH_MIX_CTL, 0x10, 0x00); + + if (!(strcmp(w->name, "ANC HPHR PA"))) { + /* Do everything needed for left channel */ + snd_soc_update_bits(codec, WCD9335_CDC_RX1_RX_PATH_CTL, + 0x10, 0x00); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, + WCD9335_CDC_RX1_RX_PATH_MIX_CTL)) & + 0x10) + snd_soc_update_bits(codec, + WCD9335_CDC_RX1_RX_PATH_MIX_CTL, + 0x10, 0x00); + /* Remove ANC Rx from reset */ + ret = tasha_codec_enable_anc(w, kcontrol, event); + } tasha_codec_override(codec, hph_mode, event); break; + case SND_SOC_DAPM_PRE_PMD: blocking_notifier_call_chain(&tasha->notifier, WCD_EVENT_PRE_HPHR_PA_OFF, &tasha->mbhc); tasha_codec_hph_post_pa_config(tasha, hph_mode, event); + if (!(strcmp(w->name, "ANC HPHR PA"))) + snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0x40, 0x00); break; case SND_SOC_DAPM_POST_PMD: /* 5ms sleep is required after PA is disabled as per @@ -3988,9 +4054,20 @@ static int tasha_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: + if ((!(strcmp(w->name, "ANC HPHL PA"))) && + (test_bit(HPH_PA_DELAY, &tasha->status_mask))) { + snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0xC0, 0xC0); + } set_bit(HPH_PA_DELAY, &tasha->status_mask); break; case SND_SOC_DAPM_POST_PMU: + if ((snd_soc_read(codec, WCD9335_ANA_HPH) & 0xC0) != 0xC0) + /* + * If PA_EN is not set (potentially in ANC case) then + * do nothing for POST_PMU and let right channel handle + * everything. + */ + break; /* * 7ms sleep is required after PA is enabled as per * HW requirement @@ -4009,6 +4086,22 @@ static int tasha_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, snd_soc_update_bits(codec, WCD9335_CDC_RX1_RX_PATH_MIX_CTL, 0x10, 0x00); + + if (!(strcmp(w->name, "ANC HPHL PA"))) { + /* Do everything needed for right channel */ + snd_soc_update_bits(codec, WCD9335_CDC_RX2_RX_PATH_CTL, + 0x10, 0x00); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, + WCD9335_CDC_RX2_RX_PATH_MIX_CTL)) & + 0x10) + snd_soc_update_bits(codec, + WCD9335_CDC_RX2_RX_PATH_MIX_CTL, + 0x10, 0x00); + + /* Remove ANC Rx from reset */ + ret = tasha_codec_enable_anc(w, kcontrol, event); + } tasha_codec_override(codec, hph_mode, event); break; case SND_SOC_DAPM_PRE_PMD: @@ -4016,6 +4109,8 @@ static int tasha_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, WCD_EVENT_PRE_HPHL_PA_OFF, &tasha->mbhc); tasha_codec_hph_post_pa_config(tasha, hph_mode, event); + if (!(strcmp(w->name, "ANC HPHL PA"))) + snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0x80, 0x00); break; case SND_SOC_DAPM_POST_PMD: /* 5ms sleep is required after PA is disabled as per @@ -4083,6 +4178,9 @@ static int tasha_codec_enable_lineout_pa(struct snd_soc_dapm_widget *w, snd_soc_update_bits(codec, lineout_mix_vol_reg, 0x10, 0x00); + if (!(strcmp(w->name, "ANC LINEOUT1 PA")) || + !(strcmp(w->name, "ANC LINEOUT2 PA"))) + ret = tasha_codec_enable_anc(w, kcontrol, event); tasha_codec_override(codec, CLS_AB, event); break; case SND_SOC_DAPM_POST_PMD: @@ -5394,8 +5492,8 @@ static int tasha_codec_tx_adc_cfg(struct snd_soc_dapm_widget *w, { int adc_mux_n = w->shift; struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); int amic_n; - u16 amic_reg; dev_dbg(codec->dev, "%s: event: %d\n", __func__, event); @@ -5403,8 +5501,13 @@ static int tasha_codec_tx_adc_cfg(struct snd_soc_dapm_widget *w, case SND_SOC_DAPM_POST_PMU: amic_n = tasha_codec_find_amic_input(codec, adc_mux_n); if (amic_n) { - amic_reg = WCD9335_ANA_AMIC1 + amic_n - 1; - tasha_codec_set_tx_hold(codec, amic_reg, false); + /* + * Prevent ANC Rx pop by leaving Tx FE in HOLD + * state until PA is up. Track AMIC being used + * so we can release the HOLD later. + */ + set_bit(ANC_MIC_AMIC1 + amic_n - 1, + &tasha->status_mask); } break; default: @@ -7511,9 +7614,9 @@ static int tasha_mad_input_put(struct snd_kcontrol *kcontrol, "%s: tasha input widget = %s\n", __func__, mad_input_widget); - for (i = 0; i < card->num_dapm_routes; i++) { - if (!strcmp(card->dapm_routes[i].sink, mad_input_widget)) { - source_widget = card->dapm_routes[i].source; + for (i = 0; i < card->num_of_dapm_routes; i++) { + if (!strcmp(card->of_dapm_routes[i].sink, mad_input_widget)) { + source_widget = card->of_dapm_routes[i].source; if (!source_widget) { dev_err(codec->dev, "%s: invalid source widget\n", @@ -10512,11 +10615,11 @@ static const struct snd_soc_dapm_widget tasha_dapm_widgets[] = { tasha_codec_enable_ear_pa, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_PGA_E("ANC HPHL PA", WCD9335_ANA_HPH, 7, 0, NULL, 0, + SND_SOC_DAPM_PGA_E("ANC HPHL PA", SND_SOC_NOPM, 0, 0, NULL, 0, tasha_codec_enable_hphl_pa, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_PGA_E("ANC HPHR PA", WCD9335_ANA_HPH, 6, 0, NULL, 0, + SND_SOC_DAPM_PGA_E("ANC HPHR PA", SND_SOC_NOPM, 0, 0, NULL, 0, tasha_codec_enable_hphr_pa, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), diff --git a/sound/soc/msm/msmcobalt.c b/sound/soc/msm/msmcobalt.c index e4ad523bacb7..955a886e3fdc 100644 --- a/sound/soc/msm/msmcobalt.c +++ b/sound/soc/msm/msmcobalt.c @@ -89,13 +89,7 @@ enum { SLIM_TX_MAX, }; -struct slim_ch_config { - u32 sample_rate; - u32 bit_format; - u32 channels; -}; - -struct usb_be_config { +struct dev_config { u32 sample_rate; u32 bit_format; u32 channels; @@ -122,7 +116,7 @@ struct msm_asoc_wcd93xx_codec { }; /* Default configuration of slimbus channels */ -static struct slim_ch_config slim_rx_cfg[] = { +static struct dev_config slim_rx_cfg[] = { [SLIM_RX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, [SLIM_RX_1] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, [SLIM_RX_2] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, @@ -133,7 +127,7 @@ static struct slim_ch_config slim_rx_cfg[] = { [SLIM_RX_7] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, }; -static struct slim_ch_config slim_tx_cfg[] = { +static struct dev_config slim_tx_cfg[] = { [SLIM_TX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, [SLIM_TX_1] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, [SLIM_TX_2] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, @@ -145,18 +139,24 @@ static struct slim_ch_config slim_tx_cfg[] = { [SLIM_TX_8] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, }; -static struct usb_be_config usb_rx_cfg = { +static struct dev_config usb_rx_cfg = { .sample_rate = SAMPLING_RATE_48KHZ, .bit_format = SNDRV_PCM_FORMAT_S16_LE, .channels = 2, }; -static struct usb_be_config usb_tx_cfg = { +static struct dev_config usb_tx_cfg = { .sample_rate = SAMPLING_RATE_48KHZ, .bit_format = SNDRV_PCM_FORMAT_S16_LE, .channels = 1, }; +static struct dev_config hdmi_rx_cfg = { + .sample_rate = SAMPLING_RATE_48KHZ, + .bit_format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 2, +}; + static int msm_vi_feed_tx_ch = 2; static const char *const slim_rx_ch_text[] = {"One", "Two"}; static const char *const slim_tx_ch_text[] = {"One", "Two", "Three", "Four", @@ -169,10 +169,14 @@ static char const *slim_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_96", "KHZ_192"}; static char const *bt_sco_sample_rate_text[] = {"KHZ_8", "KHZ_16"}; static const char *const usb_ch_text[] = {"One", "Two"}; +static char const *hdmi_rx_ch_text[] = {"Two", "Three", "Four", "Five", + "Six", "Seven", "Eight"}; static char const *usb_sample_rate_text[] = {"KHZ_8", "KHZ_11P025", "KHZ_16", "KHZ_22P05", "KHZ_32", "KHZ_44P1", "KHZ_48", "KHZ_96", "KHZ_192"}; +static char const *hdmi_rx_sample_rate_text[] = {"KHZ_48", "KHZ_96", + "KHZ_192"}; static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_chs, slim_rx_ch_text); static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_chs, slim_tx_ch_text); @@ -182,12 +186,14 @@ static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_chs, slim_rx_ch_text); static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_chs, usb_ch_text); static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_chs, usb_ch_text); static SOC_ENUM_SINGLE_EXT_DECL(vi_feed_tx_chs, vi_feed_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(hdmi_rx_chs, hdmi_rx_ch_text); static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_format, bit_format_text); static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_format, bit_format_text); static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_format, bit_format_text); static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_format, bit_format_text); static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_format, bit_format_text); static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(hdmi_rx_format, bit_format_text); static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_sample_rate, slim_sample_rate_text); static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_sample_rate, slim_sample_rate_text); static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_sample_rate, slim_sample_rate_text); @@ -195,6 +201,7 @@ static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_sample_rate, slim_sample_rate_text); static SOC_ENUM_SINGLE_EXT_DECL(bt_sco_sample_rate, bt_sco_sample_rate_text); static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_sample_rate, usb_sample_rate_text); static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_sample_rate, usb_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(hdmi_rx_sample_rate, hdmi_rx_sample_rate_text); static struct platform_device *spdev; @@ -565,9 +572,9 @@ static int msm_slim_tx_ch_put(struct snd_kcontrol *kcontrol, if (ch_num < 0) return ch_num; - slim_rx_cfg[ch_num].channels = ucontrol->value.enumerated.item[0] + 1; + slim_tx_cfg[ch_num].channels = ucontrol->value.enumerated.item[0] + 1; pr_debug("%s: msm_slim_[%d]_tx_ch = %d\n", __func__, - ch_num, slim_rx_cfg[ch_num].channels); + ch_num, slim_tx_cfg[ch_num].channels); return 1; } @@ -914,6 +921,130 @@ static int usb_audio_tx_format_put(struct snd_kcontrol *kcontrol, return rc; } +static int hdmi_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + switch (hdmi_rx_cfg.bit_format) { + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: hdmi_rx_cfg = %d, ucontrol value = %ld\n", + __func__, hdmi_rx_cfg.bit_format, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int hdmi_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 2: + hdmi_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + hdmi_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + hdmi_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: hdmi_rx_cfg.bit_format = %d, ucontrol value = %ld\n", + __func__, hdmi_rx_cfg.bit_format, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int hdmi_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: hdmi_rx_cfg.channels = %d\n", __func__, + hdmi_rx_cfg.channels); + ucontrol->value.integer.value[0] = hdmi_rx_cfg.channels - 2; + + return 0; +} + +static int hdmi_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + hdmi_rx_cfg.channels = ucontrol->value.integer.value[0] + 2; + if (hdmi_rx_cfg.channels > 8) { + pr_err("%s: channels %d exceeded 8.Limiting to max chs-8\n", + __func__, hdmi_rx_cfg.channels); + hdmi_rx_cfg.channels = 8; + } + pr_debug("%s: hdmi_rx_cfg.channels = %d\n", __func__, + hdmi_rx_cfg.channels); + + return 1; +} + +static int hdmi_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val = 0; + + switch (hdmi_rx_cfg.sample_rate) { + case SAMPLING_RATE_192KHZ: + sample_rate_val = 2; + break; + + case SAMPLING_RATE_96KHZ: + sample_rate_val = 1; + break; + + case SAMPLING_RATE_48KHZ: + default: + sample_rate_val = 0; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: hdmi_rx_sample_rate = %d\n", __func__, + hdmi_rx_cfg.sample_rate); + + return 0; +} + +static int hdmi_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: ucontrol value = %ld\n", __func__, + ucontrol->value.integer.value[0]); + + switch (ucontrol->value.integer.value[0]) { + case 2: + hdmi_rx_cfg.sample_rate = SAMPLING_RATE_192KHZ; + break; + case 1: + hdmi_rx_cfg.sample_rate = SAMPLING_RATE_96KHZ; + break; + case 0: + default: + hdmi_rx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + } + + pr_debug("%s: hdmi_rx_cfg.sample_rate = %d\n", __func__, + hdmi_rx_cfg.sample_rate); + + return 0; +} + static const struct snd_kcontrol_new msm_snd_controls[] = { SOC_ENUM_EXT("SLIM_0_RX Channels", slim_0_rx_chs, msm_slim_rx_ch_get, msm_slim_rx_ch_put), @@ -931,6 +1062,8 @@ static const struct snd_kcontrol_new msm_snd_controls[] = { usb_audio_rx_ch_get, usb_audio_rx_ch_put), SOC_ENUM_EXT("USB_AUDIO_TX Channels", usb_tx_chs, usb_audio_tx_ch_get, usb_audio_tx_ch_put), + SOC_ENUM_EXT("HDMI_RX Channels", hdmi_rx_chs, + hdmi_rx_ch_get, hdmi_rx_ch_put), SOC_ENUM_EXT("SLIM_0_RX Format", slim_0_rx_format, slim_rx_bit_format_get, slim_rx_bit_format_put), SOC_ENUM_EXT("SLIM_5_RX Format", slim_5_rx_format, @@ -943,6 +1076,8 @@ static const struct snd_kcontrol_new msm_snd_controls[] = { usb_audio_rx_format_get, usb_audio_rx_format_put), SOC_ENUM_EXT("USB_AUDIO_TX Format", usb_tx_format, usb_audio_tx_format_get, usb_audio_tx_format_put), + SOC_ENUM_EXT("HDMI_RX Bit Format", hdmi_rx_format, + hdmi_rx_format_get, hdmi_rx_format_put), SOC_ENUM_EXT("SLIM_0_RX SampleRate", slim_0_rx_sample_rate, slim_rx_sample_rate_get, slim_rx_sample_rate_put), SOC_ENUM_EXT("SLIM_0_TX SampleRate", slim_0_tx_sample_rate, @@ -960,6 +1095,9 @@ static const struct snd_kcontrol_new msm_snd_controls[] = { SOC_ENUM_EXT("USB_AUDIO_TX SampleRate", usb_tx_sample_rate, usb_audio_tx_sample_rate_get, usb_audio_tx_sample_rate_put), + SOC_ENUM_EXT("HDMI_RX SampleRate", hdmi_rx_sample_rate, + hdmi_rx_sample_rate_get, + hdmi_rx_sample_rate_put), }; static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, @@ -1147,6 +1285,15 @@ static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, channels->min = channels->max = usb_tx_cfg.channels; break; + case MSM_BACKEND_DAI_HDMI_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + hdmi_rx_cfg.bit_format); + if (channels->max < 2) + channels->min = channels->max = 2; + rate->min = rate->max = hdmi_rx_cfg.sample_rate; + channels->min = channels->max = hdmi_rx_cfg.channels; + break; + default: rate->min = rate->max = SAMPLING_RATE_48KHZ; break; @@ -1246,6 +1393,13 @@ static int msm_adsp_power_up_config(struct snd_soc_codec *codec) pr_debug("%s: ADSP Audio is ready\n", __func__); adsp_ready = 1; break; + } else { + /* + * ADSP will be coming up after subsystem restart and + * it might not be fully up when the control reaches + * here. So, wait for 50msec before checking ADSP state + */ + msleep(50); } } while (time_after(timeout, jiffies)); @@ -2761,12 +2915,31 @@ static struct snd_soc_dai_link msm_wcn_be_dai_links[] = { }, }; +static struct snd_soc_dai_link hdmi_be_dai_link[] = { + /* HDMI BACK END DAI Link */ + { + .name = LPASS_BE_HDMI, + .stream_name = "HDMI Playback", + .cpu_dai_name = "msm-dai-q6-hdmi.8", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-hdmi-audio-codec-rx", + .codec_dai_name = "msm_hdmi_audio_codec_rx_dai", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_HDMI_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, +}; + static struct snd_soc_dai_link msm_tasha_dai_links[ ARRAY_SIZE(msm_common_dai_links) + ARRAY_SIZE(msm_tasha_fe_dai_links) + ARRAY_SIZE(msm_common_be_dai_links) + ARRAY_SIZE(msm_tasha_be_dai_links) + - ARRAY_SIZE(msm_wcn_be_dai_links)]; + ARRAY_SIZE(msm_wcn_be_dai_links) + + ARRAY_SIZE(hdmi_be_dai_link)]; static int msm_snd_card_late_probe(struct snd_soc_card *card) { @@ -3083,10 +3256,9 @@ static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) { struct snd_soc_card *card = NULL; struct snd_soc_dai_link *dailink; - int len_1, len_2, len_3, len_4; + int len_1, len_2, len_3; int total_links; const struct of_device_id *match; - bool use_wcn_btfm = false; match = of_match_node(msmcobalt_asoc_machine_of_match, dev->of_node); if (!match) { @@ -3095,26 +3267,12 @@ static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) return NULL; } - if (of_property_read_bool(dev->of_node, "qcom,wcn-btfm")) { - use_wcn_btfm = true; - dev_dbg(dev, "%s(): WCN BTFM support present\n", __func__); - } else { - dev_dbg(dev, "%s(): No WCN BTFM support\n", __func__); - } - if (!strcmp(match->data, "tasha_codec")) { card = &snd_soc_card_tasha_msm; len_1 = ARRAY_SIZE(msm_common_dai_links); len_2 = len_1 + ARRAY_SIZE(msm_tasha_fe_dai_links); len_3 = len_2 + ARRAY_SIZE(msm_common_be_dai_links); - len_4 = len_3 + ARRAY_SIZE(msm_tasha_be_dai_links); - if (use_wcn_btfm) { - total_links = len_4 + - ARRAY_SIZE(msm_wcn_be_dai_links); - } else { - total_links = len_4; - } - + total_links = len_3 + ARRAY_SIZE(msm_tasha_be_dai_links); memcpy(msm_tasha_dai_links, msm_common_dai_links, sizeof(msm_common_dai_links)); @@ -3127,10 +3285,23 @@ static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) memcpy(msm_tasha_dai_links + len_3, msm_tasha_be_dai_links, sizeof(msm_tasha_be_dai_links)); - if (use_wcn_btfm) - memcpy(msm_tasha_dai_links + len_4, + + if (of_property_read_bool(dev->of_node, "qcom,wcn-btfm")) { + dev_dbg(dev, "%s(): WCN BTFM support present\n", + __func__); + memcpy(msm_tasha_dai_links + total_links, msm_wcn_be_dai_links, sizeof(msm_wcn_be_dai_links)); + total_links += ARRAY_SIZE(msm_wcn_be_dai_links); + } + + if (of_property_read_bool(dev->of_node, "qcom,hdmi-audio-rx")) { + dev_dbg(dev, "%s(): HDMI support present\n", __func__); + memcpy(msm_tasha_dai_links + total_links, + hdmi_be_dai_link, + sizeof(hdmi_be_dai_link)); + total_links += ARRAY_SIZE(hdmi_be_dai_link); + } dailink = msm_tasha_dai_links; } else if (!strcmp(match->data, "stub_codec")) { diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c index 9b2ef351e4f2..30cdda80019c 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-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 @@ -29,7 +29,9 @@ #include "msm-pcm-routing-v2.h" #define LOOPBACK_VOL_MAX_STEPS 0x2000 +#define LOOPBACK_SESSION_MAX 4 +static DEFINE_MUTEX(loopback_session_lock); static const DECLARE_TLV_DB_LINEAR(loopback_rx_vol_gain, 0, LOOPBACK_VOL_MAX_STEPS); @@ -51,7 +53,21 @@ struct msm_pcm_loopback { int volume; }; +struct fe_dai_session_map { + char stream_name[32]; + struct msm_pcm_loopback *loopback_priv; +}; + +static struct fe_dai_session_map session_map[LOOPBACK_SESSION_MAX] = { + { {}, NULL}, + { {}, NULL}, + { {}, NULL}, + { {}, NULL}, +}; + static void stop_pcm(struct msm_pcm_loopback *pcm); +static int msm_pcm_loopback_get_session(struct snd_soc_pcm_runtime *rtd, + struct msm_pcm_loopback **pcm); static void msm_pcm_route_event_handler(enum msm_pcm_routing_event event, void *priv_data) @@ -111,18 +127,76 @@ static int pcm_loopback_set_volume(struct msm_pcm_loopback *prtd, int volume) return rc; } +static int msm_pcm_loopback_get_session(struct snd_soc_pcm_runtime *rtd, + struct msm_pcm_loopback **pcm) +{ + int ret = 0; + int n, index = -1; + + dev_dbg(rtd->platform->dev, "%s: stream %s\n", __func__, + rtd->dai_link->stream_name); + + mutex_lock(&loopback_session_lock); + for (n = 0; n < LOOPBACK_SESSION_MAX; n++) { + if (!strcmp(rtd->dai_link->stream_name, + session_map[n].stream_name)) { + *pcm = session_map[n].loopback_priv; + goto exit; + } + /* + * Store the min index value for allocating a new session. + * Here, if session stream name is not found in the + * existing entries after the loop iteration, then this + * index will be used to allocate the new session. + * This index variable is expected to point to the topmost + * available free session. + */ + if (!(session_map[n].stream_name[0]) && (index < 0)) + index = n; + } + + if (index < 0) { + dev_err(rtd->platform->dev, "%s: Max Sessions allocated\n", + __func__); + ret = -EAGAIN; + goto exit; + } + + session_map[index].loopback_priv = kzalloc( + sizeof(struct msm_pcm_loopback), GFP_KERNEL); + if (!session_map[index].loopback_priv) { + ret = -ENOMEM; + goto exit; + } + + strlcpy(session_map[index].stream_name, + rtd->dai_link->stream_name, + sizeof(session_map[index].stream_name)); + dev_dbg(rtd->platform->dev, "%s: stream %s index %d\n", + __func__, session_map[index].stream_name, index); + + mutex_init(&session_map[index].loopback_priv->lock); + *pcm = session_map[index].loopback_priv; +exit: + mutex_unlock(&loopback_session_lock); + return ret; +} + static int msm_pcm_open(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); - struct msm_pcm_loopback *pcm; + struct msm_pcm_loopback *pcm = NULL; int ret = 0; uint16_t bits_per_sample = 16; struct msm_pcm_routing_evt event; struct asm_session_mtmx_strtr_param_window_v2_t asm_mtmx_strtr_window; uint32_t param_id; - pcm = dev_get_drvdata(rtd->platform->dev); + ret = msm_pcm_loopback_get_session(rtd, &pcm); + if (ret) + return ret; + mutex_lock(&pcm->lock); pcm->volume = 0x2000; @@ -230,7 +304,8 @@ static int msm_pcm_close(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct msm_pcm_loopback *pcm = runtime->private_data; struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); - int ret = 0; + int ret = 0, n; + bool found = false; mutex_lock(&pcm->lock); @@ -247,6 +322,29 @@ static int msm_pcm_close(struct snd_pcm_substream *substream) stop_pcm(pcm); } + if (!pcm->instance) { + mutex_lock(&loopback_session_lock); + for (n = 0; n < LOOPBACK_SESSION_MAX; n++) { + if (!strcmp(rtd->dai_link->stream_name, + session_map[n].stream_name)) { + found = true; + break; + } + } + if (found) { + memset(session_map[n].stream_name, 0, + sizeof(session_map[n].stream_name)); + mutex_unlock(&pcm->lock); + mutex_destroy(&session_map[n].loopback_priv->lock); + session_map[n].loopback_priv = NULL; + kfree(pcm); + dev_dbg(rtd->platform->dev, "%s: stream freed %s\n", + __func__, rtd->dai_link->stream_name); + mutex_unlock(&loopback_session_lock); + return 0; + } + mutex_unlock(&loopback_session_lock); + } mutex_unlock(&pcm->lock); return ret; } @@ -368,33 +466,15 @@ static struct snd_soc_platform_driver msm_soc_platform = { static int msm_pcm_probe(struct platform_device *pdev) { - struct msm_pcm_loopback *pcm; - - dev_dbg(&pdev->dev, "%s: dev name %s\n", __func__, dev_name(&pdev->dev)); - pcm = kzalloc(sizeof(struct msm_pcm_loopback), GFP_KERNEL); - if (!pcm) { - dev_err(&pdev->dev, "%s Failed to allocate memory for pcm\n", - __func__); - return -ENOMEM; - } else { - mutex_init(&pcm->lock); - dev_set_drvdata(&pdev->dev, pcm); - } return snd_soc_register_platform(&pdev->dev, &msm_soc_platform); } static int msm_pcm_remove(struct platform_device *pdev) { - struct msm_pcm_loopback *pcm; - - pcm = dev_get_drvdata(&pdev->dev); - mutex_destroy(&pcm->lock); - kfree(pcm); - snd_soc_unregister_platform(&pdev->dev); return 0; } diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c index 7d1dfa8f3a75..4fb61470f118 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c @@ -9928,7 +9928,7 @@ static struct platform_driver msm_routing_pcm_driver = { int msm_routing_check_backend_enabled(int fedai_id) { int i; - if (fedai_id >= MSM_FRONTEND_DAI_MM_MAX_ID) { + if (fedai_id > MSM_FRONTEND_DAI_MM_MAX_ID) { /* bad ID assigned in machine driver */ pr_err("%s: bad MM ID\n", __func__); return 0; diff --git a/sound/soc/msm/qdsp6v2/q6core.c b/sound/soc/msm/qdsp6v2/q6core.c index cc26af528aba..cc7616f1d897 100644 --- a/sound/soc/msm/qdsp6v2/q6core.c +++ b/sound/soc/msm/qdsp6v2/q6core.c @@ -357,14 +357,14 @@ int core_dts_eagle_set(int size, char *data) } size_aligned4byte = (size+3) & 0xFFFFFFFC; + mutex_lock(&(q6core_lcl.cmd_lock)); ocm_core_open(); if (q6core_lcl.core_handle_q) { payload = kzalloc(sizeof(struct adsp_dts_eagle) + size_aligned4byte, GFP_KERNEL); if (!payload) { - pr_err("DTS_EAGLE_CORE - %s: out of memory (aligned size %i).\n", - __func__, size_aligned4byte); - return -ENOMEM; + rc = -ENOMEM; + goto exit; } payload->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT, APR_HDR_LEN(APR_HDR_SIZE), @@ -387,6 +387,9 @@ int core_dts_eagle_set(int size, char *data) } kfree(payload); } + +exit: + mutex_unlock(&(q6core_lcl.cmd_lock)); return rc; } @@ -401,6 +404,7 @@ int core_dts_eagle_get(int id, int size, char *data) __func__, size, data); return -EINVAL; } + mutex_lock(&(q6core_lcl.cmd_lock)); ocm_core_open(); if (q6core_lcl.core_handle_q) { ah.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT, @@ -415,9 +419,8 @@ int core_dts_eagle_get(int id, int size, char *data) generic_get_data = kzalloc(sizeof(struct generic_get_data_) + size, GFP_KERNEL); if (!generic_get_data) { - pr_err("DTS_EAGLE_CORE - %s: error allocating memory of size %i\n", - __func__, size); - return -ENOMEM; + rc = -ENOMEM; + goto exit; } rc = apr_send_pkt(q6core_lcl.core_handle_q, @@ -425,7 +428,7 @@ int core_dts_eagle_get(int id, int size, char *data) if (rc < 0) { pr_err("DTS_EAGLE_CORE - %s: failed op[0x%x]rc[%d]\n", __func__, ah.opcode, rc); - goto fail_cmd_2; + goto exit; } rc = wait_event_timeout(q6core_lcl.bus_bw_req_wait, @@ -435,7 +438,7 @@ int core_dts_eagle_get(int id, int size, char *data) pr_err("DTS_EAGLE_CORE - %s: EAGLE get params timed out\n", __func__); rc = -EINVAL; - goto fail_cmd_2; + goto exit; } if (generic_get_data->valid) { rc = 0; @@ -447,9 +450,10 @@ int core_dts_eagle_get(int id, int size, char *data) } } -fail_cmd_2: +exit: kfree(generic_get_data); generic_get_data = NULL; + mutex_unlock(&(q6core_lcl.cmd_lock)); return rc; } @@ -496,6 +500,7 @@ bool q6core_is_adsp_ready(void) hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, 0); hdr.opcode = AVCS_CMD_ADSP_EVENT_GET_STATE; + mutex_lock(&(q6core_lcl.cmd_lock)); ocm_core_open(); q6core_lcl.bus_bw_resp_received = 0; rc = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *)&hdr); @@ -515,6 +520,7 @@ bool q6core_is_adsp_ready(void) } bail: pr_debug("%s: leave, rc %d, adsp ready %d\n", __func__, rc, ret); + mutex_unlock(&(q6core_lcl.cmd_lock)); return ret; } @@ -692,6 +698,11 @@ static int q6core_send_custom_topologies(void) struct cal_block_data *cal_block = NULL; struct avcs_cmd_register_topologies reg_top; + if (!q6core_is_adsp_ready()) { + pr_err("%s: ADSP is not ready!\n", __func__); + return -ENODEV; + } + memset(®_top, 0, sizeof(reg_top)); mutex_lock(&q6core_lcl.cal_data[CUST_TOP_CAL]->lock); mutex_lock(&q6core_lcl.cmd_lock); @@ -708,12 +719,6 @@ static int q6core_send_custom_topologies(void) goto unlock; } - if (!q6core_is_adsp_ready()) { - pr_err("%s: ADSP is not ready!\n", __func__); - ret = -ENODEV; - goto unlock; - } - q6core_dereg_all_custom_topologies(); ret = q6core_map_memory_regions(&cal_block->cal_data.paddr, 0, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index c9e54acb06ec..cac2d4975a15 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -876,6 +876,12 @@ struct snd_soc_component *soc_find_component( { struct snd_soc_component *component; + if (!of_node && !name) { + pr_err("%s: Either of_node or name must be valid\n", + __func__); + return NULL; + } + lockdep_assert_held(&client_mutex); list_for_each_entry(component, &component_list, list) { diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index b4ffe3904dcc..4ef20ba02cb4 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -698,6 +698,20 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) snd_soc_dai_digital_mute(cpu_dai, 1, substream->stream); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (snd_soc_runtime_ignore_pmdown_time(rtd)) { + /* powered down playback stream now */ + snd_soc_dapm_stream_event(rtd, + SNDRV_PCM_STREAM_PLAYBACK, + SND_SOC_DAPM_STREAM_STOP); + } else { + /* start delayed pop wq here for playback streams */ + rtd->pop_wait = 1; + queue_delayed_work(system_power_efficient_wq, + &rtd->delayed_work, + msecs_to_jiffies(rtd->pmdown_time)); + } + } if (cpu_dai->driver->ops->shutdown) cpu_dai->driver->ops->shutdown(substream, cpu_dai); @@ -713,20 +727,7 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) if (platform->driver->ops && platform->driver->ops->close) platform->driver->ops->close(substream); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - if (snd_soc_runtime_ignore_pmdown_time(rtd)) { - /* powered down playback stream now */ - snd_soc_dapm_stream_event(rtd, - SNDRV_PCM_STREAM_PLAYBACK, - SND_SOC_DAPM_STREAM_STOP); - } else { - /* start delayed pop wq here for playback streams */ - rtd->pop_wait = 1; - queue_delayed_work(system_power_efficient_wq, - &rtd->delayed_work, - msecs_to_jiffies(rtd->pmdown_time)); - } - } else { + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) { /* capture streams can be powered down now */ snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE, SND_SOC_DAPM_STREAM_STOP); diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig index a452ad7cec40..f32cfa40c192 100644 --- a/sound/usb/Kconfig +++ b/sound/usb/Kconfig @@ -162,5 +162,13 @@ config SND_BCD2000 source "sound/usb/line6/Kconfig" +config SND_USB_AUDIO_QMI + tristate "USB Audio QMI Service driver" + depends on MSM_QMI_INTERFACE + help + Starts USB Audio QMI server to communicate with remote entity + to perform operations like enable or disable particular audio + stream on a connected USB device. + endif # SND_USB diff --git a/sound/usb/Makefile b/sound/usb/Makefile index 2d2d122b069f..d2ac0386d3da 100644 --- a/sound/usb/Makefile +++ b/sound/usb/Makefile @@ -26,3 +26,4 @@ obj-$(CONFIG_SND_USB_US122L) += snd-usbmidi-lib.o obj-$(CONFIG_SND) += misc/ usx2y/ caiaq/ 6fire/ hiface/ bcd2000/ obj-$(CONFIG_SND_USB_LINE6) += line6/ +obj-$(CONFIG_SND_USB_AUDIO_QMI) += usb_audio_qmi_v01.o usb_audio_qmi_svc.o diff --git a/sound/usb/card.c b/sound/usb/card.c index 7e1001c1cf1e..e94f4d2f2620 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -110,6 +110,71 @@ static DEFINE_MUTEX(register_mutex); static struct snd_usb_audio *usb_chip[SNDRV_CARDS]; static struct usb_driver usb_audio_driver; +struct snd_usb_substream *find_snd_usb_substream(unsigned int card_num, + unsigned int pcm_idx, unsigned int direction, struct snd_usb_audio + **uchip, void (*disconnect_cb)(struct snd_usb_audio *chip)) +{ + int idx; + struct snd_usb_stream *as; + struct snd_usb_substream *subs = NULL; + struct snd_usb_audio *chip = NULL; + + mutex_lock(®ister_mutex); + /* + * legacy audio snd card number assignment is dynamic. Hence + * search using chip->card->number + */ + for (idx = 0; idx < SNDRV_CARDS; idx++) { + if (!usb_chip[idx]) + continue; + if (usb_chip[idx]->card->number == card_num) { + chip = usb_chip[idx]; + break; + } + } + + if (!chip || atomic_read(&chip->shutdown)) { + pr_debug("%s: instance of usb crad # %d does not exist\n", + __func__, card_num); + goto err; + } + + if (pcm_idx >= chip->pcm_devs) { + pr_err("%s: invalid pcm dev number %u > %d\n", __func__, + pcm_idx, chip->pcm_devs); + goto err; + } + + if (direction > SNDRV_PCM_STREAM_CAPTURE) { + pr_err("%s: invalid direction %u\n", __func__, direction); + goto err; + } + + list_for_each_entry(as, &chip->pcm_list, list) { + if (as->pcm_index == pcm_idx) { + subs = &as->substream[direction]; + if (subs->interface < 0 && !subs->data_endpoint && + !subs->sync_endpoint) { + pr_debug("%s: stream disconnected, bail out\n", + __func__); + subs = NULL; + goto err; + } + goto done; + } + } + +done: + chip->card_num = card_num; + chip->disconnect_cb = disconnect_cb; +err: + *uchip = chip; + if (!subs) + pr_debug("%s: substream instance not found\n", __func__); + mutex_unlock(®ister_mutex); + return subs; +} + /* * disconnect streams * called from usb_audio_disconnect() @@ -322,6 +387,7 @@ static int snd_usb_audio_free(struct snd_usb_audio *chip) list_for_each_entry_safe(ep, n, &chip->ep_list, list) snd_usb_endpoint_free(ep); + mutex_destroy(&chip->dev_lock); mutex_destroy(&chip->mutex); kfree(chip); return 0; @@ -377,6 +443,7 @@ static int snd_usb_audio_create(struct usb_interface *intf, } mutex_init(&chip->mutex); + mutex_init(&chip->dev_lock); init_waitqueue_head(&chip->shutdown_wait); chip->index = idx; chip->dev = dev; @@ -639,6 +706,8 @@ static void usb_audio_disconnect(struct usb_interface *intf) if (chip->num_interfaces <= 0) { usb_chip[chip->index] = NULL; mutex_unlock(®ister_mutex); + if (chip->disconnect_cb) + chip->disconnect_cb(chip); snd_card_free_when_closed(card); } else { mutex_unlock(®ister_mutex); diff --git a/sound/usb/card.h b/sound/usb/card.h index 71778ca4b26a..7421ee53c29f 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -167,4 +167,8 @@ struct snd_usb_stream { struct list_head list; }; +struct snd_usb_substream *find_snd_usb_substream(unsigned int card_num, + unsigned int pcm_idx, unsigned int direction, struct snd_usb_audio + **uchip, void (*disconnect_cb)(struct snd_usb_audio *chip)); + #endif /* __USBAUDIO_CARD_H */ diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 9245f52d43bd..9a9b789a56ef 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -552,6 +552,64 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) return 0; } +int snd_usb_enable_audio_stream(struct snd_usb_substream *subs, + bool enable) +{ + struct audioformat *fmt; + struct usb_host_interface *alts; + struct usb_interface *iface; + int ret; + + if (!enable) { + if (subs->interface >= 0) { + usb_set_interface(subs->dev, subs->interface, 0); + subs->altset_idx = 0; + subs->interface = -1; + subs->cur_audiofmt = NULL; + } + + snd_usb_autosuspend(subs->stream->chip); + return 0; + } + + snd_usb_autoresume(subs->stream->chip); + fmt = find_format(subs); + if (!fmt) { + dev_dbg(&subs->dev->dev, + "cannot set format: format = %#x, rate = %d, channels = %d\n", + subs->pcm_format, subs->cur_rate, subs->channels); + return -EINVAL; + } + + subs->altset_idx = 0; + subs->interface = -1; + if (atomic_read(&subs->stream->chip->shutdown)) { + ret = -ENODEV; + } else { + ret = set_format(subs, fmt); + if (ret < 0) + return ret; + + iface = usb_ifnum_to_if(subs->dev, subs->cur_audiofmt->iface); + alts = &iface->altsetting[subs->cur_audiofmt->altset_idx]; + ret = snd_usb_init_sample_rate(subs->stream->chip, + subs->cur_audiofmt->iface, + alts, + subs->cur_audiofmt, + subs->cur_rate); + if (ret < 0) { + dev_err(&subs->dev->dev, "failed to set rate %d\n", + subs->cur_rate); + return ret; + } + } + + subs->interface = fmt->iface; + subs->altset_idx = fmt->altset_idx; + + return 0; +} + /* * Return the score of matching two audioformats. * Veto the audioformat if: diff --git a/sound/usb/pcm.h b/sound/usb/pcm.h index df7a003682ad..d581f94b3fbf 100644 --- a/sound/usb/pcm.h +++ b/sound/usb/pcm.h @@ -9,6 +9,7 @@ void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream); int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface, struct usb_host_interface *alts, struct audioformat *fmt); - +int snd_usb_enable_audio_stream(struct snd_usb_substream *subs, + bool enable); #endif /* __USBAUDIO_PCM_H */ diff --git a/sound/usb/stream.c b/sound/usb/stream.c index 8ee14f2365e7..81172a395e55 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -69,9 +69,14 @@ static void snd_usb_audio_stream_free(struct snd_usb_stream *stream) static void snd_usb_audio_pcm_free(struct snd_pcm *pcm) { struct snd_usb_stream *stream = pcm->private_data; + struct snd_usb_audio *chip; + if (stream) { + mutex_lock(&stream->chip->dev_lock); + chip = stream->chip; stream->pcm = NULL; snd_usb_audio_stream_free(stream); + mutex_unlock(&chip->dev_lock); } } diff --git a/sound/usb/usb_audio_qmi_svc.c b/sound/usb/usb_audio_qmi_svc.c new file mode 100644 index 000000000000..9d2b75876bfe --- /dev/null +++ b/sound/usb/usb_audio_qmi_svc.c @@ -0,0 +1,1122 @@ +/* Copyright (c) 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. + * + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <linux/debugfs.h> +#include <linux/usb/audio.h> +#include <linux/uaccess.h> +#include <sound/pcm.h> +#include <sound/core.h> +#include <sound/asound.h> +#include <linux/usb.h> +#include <linux/qmi_encdec.h> +#include <soc/qcom/msm_qmi_interface.h> +#include <linux/iommu.h> +#include <linux/qcom_iommu.h> +#include <linux/platform_device.h> + +#include "usbaudio.h" +#include "card.h" +#include "helper.h" +#include "pcm.h" +#include "usb_audio_qmi_v01.h" + +#define SND_PCM_CARD_NUM_MASK 0xffff0000 +#define SND_PCM_DEV_NUM_MASK 0xff00 +#define SND_PCM_STREAM_DIRECTION 0xff + +#define PREPEND_SID_TO_IOVA(iova, sid) (u64)(((u64)(iova)) | \ + (((u64)sid) << 32)) + +/* event ring iova base address */ +#define IOVA_BASE 0x1000 + +#define IOVA_DCBA_BASE 0x2000 +#define IOVA_XFER_RING_BASE (IOVA_DCBA_BASE + PAGE_SIZE * (SNDRV_CARDS + 1)) +#define IOVA_XFER_BUF_BASE (IOVA_XFER_RING_BASE + PAGE_SIZE * SNDRV_CARDS * 32) +#define IOVA_XFER_RING_MAX (IOVA_XFER_BUF_BASE - PAGE_SIZE) +#define IOVA_XFER_BUF_MAX (0xfffff000 - PAGE_SIZE) + +#define MAX_XFER_BUFF_LEN (2 * PAGE_SIZE) + +struct iova_info { + struct list_head list; + unsigned long start_iova; + size_t size; + bool in_use; +}; + +struct intf_info { + unsigned long data_xfer_ring_va; + size_t data_xfer_ring_size; + unsigned long sync_xfer_ring_va; + size_t sync_xfer_ring_size; + unsigned long xfer_buf_va; + size_t xfer_buf_size; + phys_addr_t xfer_buf_pa; + u8 *xfer_buf; + bool in_use; +}; + +struct uaudio_dev { + struct usb_device *udev; + unsigned int card_num; + atomic_t in_use; + struct kref kref; + unsigned long dcba_iova; + size_t dcba_size; + wait_queue_head_t disconnect_wq; + + /* interface specific */ + int num_intf; + struct intf_info *info; +}; + +static struct uaudio_dev uadev[SNDRV_CARDS]; + +struct uaudio_qmi_dev { + struct device *dev; + u32 sid; + u32 intr_num; + struct iommu_domain *domain; + + /* list to keep track of available iova */ + struct list_head dcba_list; + size_t dcba_iova_size; + unsigned long curr_dcba_iova; + struct list_head xfer_ring_list; + size_t xfer_ring_iova_size; + unsigned long curr_xfer_ring_iova; + struct list_head xfer_buf_list; + size_t xfer_buf_iova_size; + unsigned long curr_xfer_buf_iova; + /* bit fields representing pcm card enabled */ + unsigned long card_slot; + /* cache event ring phys addr */ + u64 er_phys_addr; +}; + +static struct uaudio_qmi_dev *uaudio_qdev; + +struct uaudio_qmi_svc { + struct qmi_handle *uaudio_svc_hdl; + void *curr_conn; + struct work_struct recv_msg_work; + struct workqueue_struct *uaudio_wq; + ktime_t t_request_recvd; + ktime_t t_resp_sent; +}; + +static struct uaudio_qmi_svc *uaudio_svc; + +static struct msg_desc uaudio_stream_req_desc = { + .max_msg_len = QMI_UAUDIO_STREAM_REQ_MSG_V01_MAX_MSG_LEN, + .msg_id = QMI_UAUDIO_STREAM_REQ_V01, + .ei_array = qmi_uaudio_stream_req_msg_v01_ei, +}; + +static struct msg_desc uaudio_stream_resp_desc = { + .max_msg_len = QMI_UAUDIO_STREAM_RESP_MSG_V01_MAX_MSG_LEN, + .msg_id = QMI_UAUDIO_STREAM_RESP_V01, + .ei_array = qmi_uaudio_stream_resp_msg_v01_ei, +}; + +enum mem_type { + MEM_EVENT_RING, + MEM_DCBA, + MEM_XFER_RING, + MEM_XFER_BUF, +}; + +static unsigned long uaudio_get_iova(unsigned long *curr_iova, + size_t *curr_iova_size, struct list_head *head, size_t size) +{ + struct iova_info *info, *new_info = NULL; + struct list_head *curr_head; + unsigned long va = 0; + size_t tmp_size = size; + bool found = false; + + if (size % PAGE_SIZE) { + pr_err("%s: size %zu is not page size multiple\n", __func__, + size); + goto done; + } + + if (size > *curr_iova_size) { + pr_err("%s: size %zu > curr size %zu\n", __func__, size, + *curr_iova_size); + goto done; + } + if (*curr_iova_size == 0) { + pr_err("%s: iova mapping is full\n", __func__); + goto done; + } + + list_for_each_entry(info, head, list) { + /* exact size iova_info */ + if (!info->in_use && info->size == size) { + info->in_use = true; + va = info->start_iova; + *curr_iova_size -= size; + found = true; + pr_debug("%s: exact size :%zu found\n", __func__, size); + goto done; + } else if (!info->in_use && tmp_size >= info->size) { + if (!new_info) + new_info = info; + pr_debug("%s: partial size: %zu found\n", __func__, + info->size); + tmp_size -= info->size; + if (tmp_size) + continue; + + va = new_info->start_iova; + for (curr_head = &new_info->list; curr_head != + &info->list; curr_head = curr_head->next) { + new_info = list_entry(curr_head, struct + iova_info, list); + new_info->in_use = true; + } + info->in_use = true; + *curr_iova_size -= size; + found = true; + goto done; + } else { + /* iova region in use */ + new_info = NULL; + tmp_size = size; + } + } + + info = kzalloc(sizeof(struct iova_info), GFP_KERNEL); + if (!info) { + va = 0; + goto done; + } + + va = info->start_iova = *curr_iova; + info->size = size; + info->in_use = true; + *curr_iova += size; + *curr_iova_size -= size; + found = true; + list_add_tail(&info->list, head); + +done: + if (!found) + pr_err("%s: unable to find %zu size iova\n", __func__, size); + else + pr_debug("%s: va:%lu curr_iova:%lu curr_iova_size:%zu\n", + __func__, va, *curr_iova, *curr_iova_size); + + return va; +} + +static unsigned long uaudio_iommu_map(enum mem_type mtype, phys_addr_t pa, + size_t size) +{ + unsigned long va = 0; + bool map = true; + int ret; + + switch (mtype) { + case MEM_EVENT_RING: + va = IOVA_BASE; + /* er already mapped */ + if (uaudio_qdev->er_phys_addr == pa) + map = false; + break; + case MEM_DCBA: + va = uaudio_get_iova(&uaudio_qdev->curr_dcba_iova, + &uaudio_qdev->dcba_iova_size, &uaudio_qdev->dcba_list, size); + break; + case MEM_XFER_RING: + va = uaudio_get_iova(&uaudio_qdev->curr_xfer_ring_iova, + &uaudio_qdev->xfer_ring_iova_size, &uaudio_qdev->xfer_ring_list, + size); + break; + case MEM_XFER_BUF: + va = uaudio_get_iova(&uaudio_qdev->curr_xfer_buf_iova, + &uaudio_qdev->xfer_buf_iova_size, &uaudio_qdev->xfer_buf_list, + size); + break; + default: + pr_err("%s: unknown mem type %d\n", __func__, mtype); + } + + if (!va) + map = false; + + if (!map) + goto done; + + pr_debug("%s: map pa %pa to iova %lu for memtype %d\n", __func__, &pa, + va, mtype); + ret = iommu_map(uaudio_qdev->domain, va, pa, size, + IOMMU_READ | IOMMU_WRITE | IOMMU_DEVICE); + if (ret) + pr_err("%s:failed to map pa:%pa iova:%lu memtype:%d ret:%d\n", + __func__, &pa, va, mtype, ret); +done: + return va; +} + +static void uaudio_put_iova(unsigned long va, size_t size, struct list_head + *head, size_t *curr_iova_size) +{ + struct iova_info *info; + size_t tmp_size = size; + bool found = false; + + list_for_each_entry(info, head, list) { + if (info->start_iova == va) { + if (!info->in_use) { + pr_err("%s: va %lu is not in use\n", __func__, + va); + return; + } + found = true; + info->in_use = false; + if (info->size == size) + goto done; + } + + if (found && tmp_size >= info->size) { + info->in_use = false; + tmp_size -= info->size; + if (!tmp_size) + goto done; + } + } + + if (!found) { + pr_err("%s: unable to find the va %lu\n", __func__, va); + return; + } +done: + *curr_iova_size += size; + pr_debug("%s: curr_iova_size %zu\n", __func__, *curr_iova_size); +} + +static void uaudio_iommu_unmap(enum mem_type mtype, unsigned long va, + size_t size) +{ + size_t umap_size; + bool unmap = true; + + if (!va || !size) + return; + + switch (mtype) { + case MEM_EVENT_RING: + if (uaudio_qdev->er_phys_addr) + uaudio_qdev->er_phys_addr = 0; + else + unmap = false; + break; + case MEM_DCBA: + uaudio_put_iova(va, size, &uaudio_qdev->dcba_list, + &uaudio_qdev->dcba_iova_size); + break; + case MEM_XFER_RING: + uaudio_put_iova(va, size, &uaudio_qdev->xfer_ring_list, + &uaudio_qdev->xfer_ring_iova_size); + break; + case MEM_XFER_BUF: + uaudio_put_iova(va, size, &uaudio_qdev->xfer_buf_list, + &uaudio_qdev->xfer_buf_iova_size); + break; + default: + pr_err("%s: unknown mem type %d\n", __func__, mtype); + unmap = false; + } + + if (!unmap) + return; + + pr_debug("%s: unmap iova %lu for memtype %d\n", __func__, va, mtype); + + umap_size = iommu_unmap(uaudio_qdev->domain, va, size); + if (umap_size != size) + pr_err("%s: unmapped size %zu for iova %lu\n", __func__, + umap_size, va); +} + +static int prepare_qmi_response(struct snd_usb_substream *subs, + struct qmi_uaudio_stream_resp_msg_v01 *resp, u32 xfer_buf_len, + int card_num) +{ + int ret = -ENODEV; + struct usb_interface *iface; + struct usb_host_interface *alts; + struct usb_interface_descriptor *altsd; + struct usb_host_endpoint *ep; + struct uac_format_type_i_continuous_descriptor *fmt; + struct uac_format_type_i_discrete_descriptor *fmt_v1; + struct uac_format_type_i_ext_descriptor *fmt_v2; + struct uac1_as_header_descriptor *as; + struct uac1_ac_header_descriptor *ac; + int protocol; + u8 *xfer_buf; + u32 len, mult, remainder; + unsigned long va, tr_data_va = 0, tr_sync_va = 0, dcba_va = 0, + xfer_buf_va = 0; + phys_addr_t xhci_pa, xfer_buf_pa; + + iface = usb_ifnum_to_if(subs->dev, subs->interface); + if (!iface) { + pr_err("%s: interface # %d does not exist\n", __func__, + subs->interface); + goto err; + } + + alts = &iface->altsetting[subs->altset_idx]; + altsd = get_iface_desc(alts); + protocol = altsd->bInterfaceProtocol; + + /* get format type */ + fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, + UAC_FORMAT_TYPE); + if (!fmt) { + pr_err("%s: %u:%d : no UAC_FORMAT_TYPE desc\n", __func__, + subs->interface, subs->altset_idx); + goto err; + } + + if (protocol == UAC_VERSION_1) { + as = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, + UAC_AS_GENERAL); + if (!as) { + pr_err("%s: %u:%d : no UAC_AS_GENERAL desc\n", __func__, + subs->interface, subs->altset_idx); + goto err; + } + resp->bDelay = as->bDelay; + fmt_v1 = (struct uac_format_type_i_discrete_descriptor *)fmt; + resp->bSubslotSize = fmt_v1->bSubframeSize; + } else if (protocol == UAC_VERSION_2) { + fmt_v2 = (struct uac_format_type_i_ext_descriptor *)fmt; + resp->bSubslotSize = fmt_v2->bSubslotSize; + } else { + pr_err("%s: unknown protocol version %x\n", __func__, protocol); + goto err; + } + + ac = snd_usb_find_csint_desc(alts->extra, + alts->extralen, + NULL, UAC_HEADER); + if (!ac) { + pr_err("%s: %u:%d : no UAC_HEADER desc\n", __func__, + subs->interface, subs->altset_idx); + goto err; + } + resp->bcdADC = ac->bcdADC; + + resp->slot_id = subs->dev->slot_id; + + memcpy(&resp->std_as_opr_intf_desc, &alts->desc, sizeof(alts->desc)); + + ep = usb_pipe_endpoint(subs->dev, subs->data_endpoint->pipe); + if (!ep) { + pr_err("%s: data ep # %d context is null\n", __func__, + subs->data_endpoint->ep_num); + goto err; + } + memcpy(&resp->std_as_data_ep_desc, &ep->desc, sizeof(ep->desc)); + + xhci_pa = usb_get_xfer_ring_dma_addr(subs->dev, ep); + if (!xhci_pa) { + pr_err("%s:failed to get data ep ring dma address\n", __func__); + goto err; + } + + resp->xhci_mem_info.tr_data.pa = xhci_pa; + + if (subs->sync_endpoint) { + ep = usb_pipe_endpoint(subs->dev, subs->sync_endpoint->pipe); + if (!ep) { + pr_err("%s: sync ep # %d context is null\n", __func__, + subs->sync_endpoint->ep_num); + goto err; + } + memcpy(&resp->std_as_sync_ep_desc, &ep->desc, sizeof(ep->desc)); + xhci_pa = usb_get_xfer_ring_dma_addr(subs->dev, ep); + if (!xhci_pa) { + pr_err("%s:failed to get sync ep ring dma address\n", + __func__); + goto err; + } + resp->xhci_mem_info.tr_sync.pa = xhci_pa; + } + + resp->interrupter_num = uaudio_qdev->intr_num; + + /* map xhci data structures PA memory to iova */ + + /* event ring */ + ret = usb_sec_event_ring_setup(subs->dev, resp->interrupter_num); + if (ret) { + pr_err("%s: failed to setup sec event ring ret %d\n", __func__, + ret); + goto err; + } + xhci_pa = usb_get_sec_event_ring_dma_addr(subs->dev, + resp->interrupter_num); + if (!xhci_pa) { + pr_err("%s: failed to get sec event ring dma address\n", + __func__); + goto err; + } + + va = uaudio_iommu_map(MEM_EVENT_RING, xhci_pa, PAGE_SIZE); + if (!va) + goto err; + + resp->xhci_mem_info.evt_ring.va = PREPEND_SID_TO_IOVA(va, + uaudio_qdev->sid); + resp->xhci_mem_info.evt_ring.pa = xhci_pa; + resp->xhci_mem_info.evt_ring.size = PAGE_SIZE; + uaudio_qdev->er_phys_addr = xhci_pa; + + /* dcba */ + xhci_pa = usb_get_dcba_dma_addr(subs->dev); + if (!xhci_pa) { + pr_err("%s:failed to get dcba dma address\n", __func__); + goto unmap_er; + } + + if (!uadev[card_num].dcba_iova) { /* mappped per usb device */ + va = uaudio_iommu_map(MEM_DCBA, xhci_pa, PAGE_SIZE); + if (!va) + goto unmap_er; + + uadev[card_num].dcba_iova = va; + uadev[card_num].dcba_size = PAGE_SIZE; + } + + dcba_va = uadev[card_num].dcba_iova; + resp->xhci_mem_info.dcba.va = PREPEND_SID_TO_IOVA(dcba_va, + uaudio_qdev->sid); + resp->xhci_mem_info.dcba.pa = xhci_pa; + resp->xhci_mem_info.dcba.size = PAGE_SIZE; + + /* data transfer ring */ + xhci_pa = resp->xhci_mem_info.tr_data.pa; + va = uaudio_iommu_map(MEM_XFER_RING, xhci_pa, PAGE_SIZE); + if (!va) + goto unmap_dcba; + + tr_data_va = va; + resp->xhci_mem_info.tr_data.va = PREPEND_SID_TO_IOVA(va, + uaudio_qdev->sid); + resp->xhci_mem_info.tr_data.size = PAGE_SIZE; + + /* sync transfer ring */ + if (!resp->xhci_mem_info.tr_sync.pa) + goto skip_sync; + + xhci_pa = resp->xhci_mem_info.tr_sync.pa; + va = uaudio_iommu_map(MEM_XFER_RING, xhci_pa, PAGE_SIZE); + if (!va) + goto unmap_data; + + tr_sync_va = va; + resp->xhci_mem_info.tr_sync.va = PREPEND_SID_TO_IOVA(va, + uaudio_qdev->sid); + resp->xhci_mem_info.tr_sync.size = PAGE_SIZE; + +skip_sync: + /* xfer buffer, multiple of 4K only */ + if (!xfer_buf_len) + xfer_buf_len = PAGE_SIZE; + + mult = xfer_buf_len / PAGE_SIZE; + remainder = xfer_buf_len % PAGE_SIZE; + len = mult * PAGE_SIZE; + len += remainder ? PAGE_SIZE : 0; + + if (len > MAX_XFER_BUFF_LEN) { + pr_err("%s: req buf len %d > max buf len %lu, setting %lu\n", + __func__, len, MAX_XFER_BUFF_LEN, MAX_XFER_BUFF_LEN); + len = MAX_XFER_BUFF_LEN; + } + + xfer_buf = usb_alloc_coherent(subs->dev, len, GFP_KERNEL, &xfer_buf_pa); + if (!xfer_buf) + goto unmap_sync; + + resp->xhci_mem_info.xfer_buff.pa = xfer_buf_pa; + resp->xhci_mem_info.xfer_buff.size = len; + + va = uaudio_iommu_map(MEM_XFER_BUF, xfer_buf_pa, len); + if (!va) + goto unmap_sync; + + xfer_buf_va = va; + resp->xhci_mem_info.xfer_buff.va = PREPEND_SID_TO_IOVA(va, + uaudio_qdev->sid); + + if (!atomic_read(&uadev[card_num].in_use)) { + kref_init(&uadev[card_num].kref); + init_waitqueue_head(&uadev[card_num].disconnect_wq); + uadev[card_num].num_intf = + subs->dev->config->desc.bNumInterfaces; + uadev[card_num].info = + kzalloc(sizeof(struct intf_info) * + uadev[card_num].num_intf, GFP_KERNEL); + if (!uadev[card_num].info) { + ret = -ENOMEM; + goto unmap_xfer_buf; + } + uadev[card_num].udev = subs->dev; + atomic_set(&uadev[card_num].in_use, 1); + } else { + kref_get(&uadev[card_num].kref); + } + + if (uadev[card_num].info[subs->interface].in_use) { + pr_err("%s interface# %d already in use card# %d\n", __func__, + subs->interface, card_num); + goto unmap_xfer_buf; + } + + uadev[card_num].card_num = card_num; + + /* cache intf specific info to use it for unmap and free xfer buf */ + uadev[card_num].info[subs->interface].data_xfer_ring_va = tr_data_va; + uadev[card_num].info[subs->interface].data_xfer_ring_size = PAGE_SIZE; + uadev[card_num].info[subs->interface].sync_xfer_ring_va = tr_sync_va; + uadev[card_num].info[subs->interface].sync_xfer_ring_size = PAGE_SIZE; + uadev[card_num].info[subs->interface].xfer_buf_va = xfer_buf_va; + uadev[card_num].info[subs->interface].xfer_buf_pa = xfer_buf_pa; + uadev[card_num].info[subs->interface].xfer_buf_size = len; + uadev[card_num].info[subs->interface].xfer_buf = xfer_buf; + uadev[card_num].info[subs->interface].in_use = true; + + set_bit(card_num, &uaudio_qdev->card_slot); + + return 0; + +unmap_xfer_buf: + uaudio_iommu_unmap(MEM_XFER_BUF, xfer_buf_va, len); +unmap_sync: + usb_free_coherent(subs->dev, len, xfer_buf, xfer_buf_pa); + uaudio_iommu_unmap(MEM_XFER_RING, tr_sync_va, PAGE_SIZE); +unmap_data: + uaudio_iommu_unmap(MEM_XFER_RING, tr_data_va, PAGE_SIZE); +unmap_dcba: + uaudio_iommu_unmap(MEM_DCBA, dcba_va, PAGE_SIZE); +unmap_er: + uaudio_iommu_unmap(MEM_EVENT_RING, IOVA_BASE, PAGE_SIZE); +err: + return ret; +} + +void uaudio_disconnect_cb(struct snd_usb_audio *chip) +{ + int ret, if_idx; + struct uaudio_dev *dev; + int card_num = chip->card_num; + + pr_debug("%s: for card# %d\n", __func__, card_num); + + mutex_lock(&chip->dev_lock); + if (card_num >= SNDRV_CARDS) { + pr_err("%s: invalid card number\n", __func__); + goto done; + } + + dev = &uadev[card_num]; + if (atomic_read(&dev->in_use)) { + ret = wait_event_interruptible(dev->disconnect_wq, + !atomic_read(&dev->in_use)); + if (ret < 0) { + pr_debug("%s: failed with ret %d\n", __func__, ret); + goto done; + } + } + + /* clean up */ + if (!dev->udev) { + pr_debug("%s: no clean up required\n", __func__); + goto done; + } + + /* free xfer buffer and unmap xfer ring and buf per interface */ + for (if_idx = 0; if_idx < dev->num_intf; if_idx++) { + if (!dev->info[if_idx].in_use) + continue; + usb_free_coherent(dev->udev, + dev->info[if_idx].xfer_buf_size, + dev->info[if_idx].xfer_buf, + dev->info[if_idx].xfer_buf_pa); + + uaudio_iommu_unmap(MEM_XFER_RING, + dev->info[if_idx].data_xfer_ring_va, + dev->info[if_idx].data_xfer_ring_size); + dev->info[if_idx].data_xfer_ring_va = 0; + dev->info[if_idx].data_xfer_ring_size = 0; + + uaudio_iommu_unmap(MEM_XFER_RING, + dev->info[if_idx].sync_xfer_ring_va, + dev->info[if_idx].sync_xfer_ring_size); + dev->info[if_idx].sync_xfer_ring_va = 0; + dev->info[if_idx].sync_xfer_ring_size = 0; + + uaudio_iommu_unmap(MEM_XFER_BUF, + dev->info[if_idx].xfer_buf_va, + dev->info[if_idx].xfer_buf_size); + dev->info[if_idx].xfer_buf_va = 0; + dev->info[if_idx].xfer_buf_size = 0; + pr_debug("%s: release resources: intf# %d card# %d\n", __func__, + if_idx, card_num); + } + + /* iommu_unmap dcba iova for a usb device */ + uaudio_iommu_unmap(MEM_DCBA, dev->dcba_iova, dev->dcba_size); + + dev->dcba_iova = 0; + dev->dcba_size = 0; + dev->num_intf = 0; + + /* free interface info */ + kfree(dev->info); + dev->info = NULL; + + clear_bit(card_num, &uaudio_qdev->card_slot); + + /* all audio devices are disconnected */ + if (!uaudio_qdev->card_slot) { + uaudio_iommu_unmap(MEM_EVENT_RING, IOVA_BASE, PAGE_SIZE); + usb_sec_event_ring_cleanup(dev->udev, uaudio_qdev->intr_num); + pr_debug("%s: all audio devices disconnected\n", __func__); + } + + dev->udev = NULL; +done: + mutex_unlock(&chip->dev_lock); +} + +static void uaudio_dev_release(struct kref *kref) +{ + struct uaudio_dev *dev = container_of(kref, struct uaudio_dev, kref); + + pr_debug("%s for dev %p\n", __func__, dev); + + atomic_set(&dev->in_use, 0); + + clear_bit(dev->card_num, &uaudio_qdev->card_slot); + + /* all audio devices are disconnected */ + if (!uaudio_qdev->card_slot) { + usb_sec_event_ring_cleanup(dev->udev, uaudio_qdev->intr_num); + uaudio_iommu_unmap(MEM_EVENT_RING, IOVA_BASE, PAGE_SIZE); + pr_debug("%s: all audio devices disconnected\n", __func__); + } + + wake_up(&dev->disconnect_wq); +} + +static int handle_uaudio_stream_req(void *req_h, void *req) +{ + struct qmi_uaudio_stream_req_msg_v01 *req_msg; + struct qmi_uaudio_stream_resp_msg_v01 resp = {0}; + struct snd_usb_substream *subs; + struct snd_usb_audio *chip = NULL; + struct uaudio_qmi_svc *svc = uaudio_svc; + struct intf_info *info; + u8 pcm_card_num, pcm_dev_num, direction; + int intf_num = -1, ret = 0; + + req_msg = (struct qmi_uaudio_stream_req_msg_v01 *)req; + + direction = req_msg->usb_token & SND_PCM_STREAM_DIRECTION; + pcm_dev_num = (req_msg->usb_token & SND_PCM_DEV_NUM_MASK) >> 8; + pcm_card_num = (req_msg->usb_token & SND_PCM_CARD_NUM_MASK) >> 16; + + pr_debug("%s:card#:%d dev#:%d dir:%d en:%d fmt:%d rate:%d #ch:%d\n", + __func__, pcm_card_num, pcm_dev_num, direction, req_msg->enable, + req_msg->audio_format, req_msg->bit_rate, + req_msg->number_of_ch); + + if (pcm_card_num >= SNDRV_CARDS) { + pr_err("%s: invalid card # %u", __func__, pcm_card_num); + ret = -EINVAL; + goto response; + } + + subs = find_snd_usb_substream(pcm_card_num, pcm_dev_num, direction, + &chip, uaudio_disconnect_cb); + if (!subs || !chip || atomic_read(&chip->shutdown)) { + pr_err("%s: can't find substream for card# %u, dev# %u dir%u\n", + __func__, pcm_card_num, pcm_dev_num, direction); + ret = -ENODEV; + goto response; + } + + mutex_lock(&chip->dev_lock); + intf_num = subs->interface; + if (atomic_read(&chip->shutdown) || !subs->stream || !subs->stream->pcm + || !subs->stream->chip) { + ret = -ENODEV; + mutex_unlock(&chip->dev_lock); + goto response; + } + + subs->pcm_format = req_msg->audio_format; + subs->channels = req_msg->number_of_ch; + subs->cur_rate = req_msg->bit_rate; + + ret = snd_usb_enable_audio_stream(subs, req_msg->enable); + + if (!ret && req_msg->enable) + ret = prepare_qmi_response(subs, &resp, req_msg->xfer_buff_size, + pcm_card_num); + + mutex_unlock(&chip->dev_lock); + +response: + if (!req_msg->enable && ret != -EINVAL) { + if (intf_num >= 0) { + mutex_lock(&chip->dev_lock); + info = &uadev[pcm_card_num].info[intf_num]; + uaudio_iommu_unmap(MEM_XFER_RING, + info->data_xfer_ring_va, + info->data_xfer_ring_size); + info->data_xfer_ring_va = 0; + info->data_xfer_ring_size = 0; + + uaudio_iommu_unmap(MEM_XFER_RING, + info->sync_xfer_ring_va, + info->sync_xfer_ring_size); + info->sync_xfer_ring_va = 0; + info->sync_xfer_ring_size = 0; + + uaudio_iommu_unmap(MEM_XFER_BUF, + info->xfer_buf_va, + info->xfer_buf_size); + info->xfer_buf_va = 0; + + usb_free_coherent(uadev[pcm_card_num].udev, + info->xfer_buf_size, + info->xfer_buf, + info->xfer_buf_pa); + info->xfer_buf_size = 0; + info->xfer_buf = NULL; + info->xfer_buf_pa = 0; + info->in_use = false; + pr_debug("%s:release resources: intf# %d card# %d\n", + __func__, intf_num, pcm_card_num); + mutex_unlock(&chip->dev_lock); + } + if (atomic_read(&uadev[pcm_card_num].in_use)) + kref_put(&uadev[pcm_card_num].kref, + uaudio_dev_release); + } + + resp.status = ret; + ret = qmi_send_resp_from_cb(svc->uaudio_svc_hdl, svc->curr_conn, req_h, + &uaudio_stream_resp_desc, &resp, sizeof(resp)); + + svc->t_resp_sent = ktime_get(); + + pr_debug("%s: t_resp sent - t_req recvd (in ms) %lld\n", __func__, + ktime_to_ms(ktime_sub(svc->t_resp_sent, svc->t_request_recvd))); + + return ret; +} + +static int uaudio_qmi_svc_connect_cb(struct qmi_handle *handle, + void *conn_h) +{ + struct uaudio_qmi_svc *svc = uaudio_svc; + + if (svc->uaudio_svc_hdl != handle || !conn_h) { + pr_err("%s: handle mismatch\n", __func__); + return -EINVAL; + } + if (svc->curr_conn) { + pr_err("%s: Service is busy\n", __func__); + return -ECONNREFUSED; + } + svc->curr_conn = conn_h; + return 0; +} + +static int uaudio_qmi_svc_disconnect_cb(struct qmi_handle *handle, + void *conn_h) +{ + struct uaudio_qmi_svc *svc = uaudio_svc; + + if (svc->uaudio_svc_hdl != handle || svc->curr_conn != conn_h) { + pr_err("%s: handle mismatch\n", __func__); + return -EINVAL; + } + + svc->curr_conn = NULL; + return 0; +} + +static int uaudio_qmi_svc_req_cb(struct qmi_handle *handle, void *conn_h, + void *req_h, unsigned int msg_id, void *req) +{ + int ret; + struct uaudio_qmi_svc *svc = uaudio_svc; + + if (svc->uaudio_svc_hdl != handle || svc->curr_conn != conn_h) { + pr_err("%s: handle mismatch\n", __func__); + return -EINVAL; + } + + switch (msg_id) { + case QMI_UAUDIO_STREAM_REQ_V01: + ret = handle_uaudio_stream_req(req_h, req); + break; + + default: + ret = -ENOTSUPP; + break; + } + return ret; +} + +static int uaudio_qmi_svc_req_desc_cb(unsigned int msg_id, + struct msg_desc **req_desc) +{ + int ret; + + pr_debug("%s: msg_id %d\n", __func__, msg_id); + + switch (msg_id) { + case QMI_UAUDIO_STREAM_REQ_V01: + *req_desc = &uaudio_stream_req_desc; + ret = sizeof(struct qmi_uaudio_stream_req_msg_v01); + break; + + default: + ret = -ENOTSUPP; + break; + } + return ret; +} + +static void uaudio_qmi_svc_recv_msg(struct work_struct *w) +{ + int ret; + struct uaudio_qmi_svc *svc = container_of(w, struct uaudio_qmi_svc, + recv_msg_work); + + do { + pr_debug("%s: Notified about a Receive Event", __func__); + } while ((ret = qmi_recv_msg(svc->uaudio_svc_hdl)) == 0); + + if (ret != -ENOMSG) + pr_err("%s: Error receiving message\n", __func__); +} + +static void uaudio_qmi_svc_ntfy(struct qmi_handle *handle, + enum qmi_event_type event, void *priv) +{ + struct uaudio_qmi_svc *svc = uaudio_svc; + + pr_debug("%s: event %d", __func__, event); + + svc->t_request_recvd = ktime_get(); + + switch (event) { + case QMI_RECV_MSG: + queue_work(svc->uaudio_wq, &svc->recv_msg_work); + break; + default: + break; + } +} + +static struct qmi_svc_ops_options uaudio_svc_ops_options = { + .version = 1, + .service_id = UAUDIO_STREAM_SERVICE_ID_V01, + .service_vers = UAUDIO_STREAM_SERVICE_VERS_V01, + .connect_cb = uaudio_qmi_svc_connect_cb, + .disconnect_cb = uaudio_qmi_svc_disconnect_cb, + .req_desc_cb = uaudio_qmi_svc_req_desc_cb, + .req_cb = uaudio_qmi_svc_req_cb, +}; + +static int uaudio_qmi_plat_probe(struct platform_device *pdev) +{ + int ret; + struct device_node *node = pdev->dev.of_node; + + uaudio_qdev = devm_kzalloc(&pdev->dev, sizeof(struct uaudio_qmi_dev), + GFP_KERNEL); + if (!uaudio_qdev) + return -ENOMEM; + + uaudio_qdev->dev = &pdev->dev; + + ret = of_property_read_u32(node, "qcom,usb-audio-stream-id", + &uaudio_qdev->sid); + if (ret) { + dev_err(&pdev->dev, "failed to read sid.\n"); + return -ENODEV; + } + + ret = of_property_read_u32(node, "qcom,usb-audio-intr-num", + &uaudio_qdev->intr_num); + if (ret) { + dev_err(&pdev->dev, "failed to read intr num.\n"); + return -ENODEV; + } + + uaudio_qdev->domain = iommu_domain_alloc(msm_iommu_get_bus(&pdev->dev)); + if (!uaudio_qdev) { + dev_err(&pdev->dev, "failed to callocate iommu domin\n"); + return -ENODEV; + } + + /* attach to external processor iommu */ + ret = iommu_attach_device(uaudio_qdev->domain, &pdev->dev); + if (ret) { + dev_err(&pdev->dev, "failed to attach device ret = %d\n", ret); + goto free_domain; + } + + /* initialize dcba, xfer ring and xfer buf iova list */ + INIT_LIST_HEAD(&uaudio_qdev->dcba_list); + uaudio_qdev->curr_dcba_iova = IOVA_DCBA_BASE; + uaudio_qdev->dcba_iova_size = SNDRV_CARDS * PAGE_SIZE; + + INIT_LIST_HEAD(&uaudio_qdev->xfer_ring_list); + uaudio_qdev->curr_xfer_ring_iova = IOVA_XFER_RING_BASE; + uaudio_qdev->xfer_ring_iova_size = + IOVA_XFER_RING_MAX - IOVA_XFER_RING_BASE; + + INIT_LIST_HEAD(&uaudio_qdev->xfer_buf_list); + uaudio_qdev->curr_xfer_buf_iova = IOVA_XFER_BUF_BASE; + uaudio_qdev->xfer_buf_iova_size = + IOVA_XFER_BUF_MAX - IOVA_XFER_BUF_BASE; + + return 0; + +free_domain: + iommu_domain_free(uaudio_qdev->domain); + return ret; +} + +static int uaudio_qmi_plat_remove(struct platform_device *pdev) +{ + iommu_detach_device(uaudio_qdev->domain, &pdev->dev); + iommu_domain_free(uaudio_qdev->domain); + uaudio_qdev->domain = NULL; + + return 0; +} + +static const struct of_device_id of_uaudio_matach[] = { + { + .compatible = "qcom,usb-audio-qmi-dev", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, of_uaudio_matach); + +static struct platform_driver uaudio_qmi_driver = { + .probe = uaudio_qmi_plat_probe, + .remove = uaudio_qmi_plat_remove, + .driver = { + .name = "uaudio-qmi", + .of_match_table = of_uaudio_matach, + }, +}; + +static int uaudio_qmi_svc_init(void) +{ + int ret; + struct uaudio_qmi_svc *svc; + + svc = kzalloc(sizeof(struct uaudio_qmi_svc), GFP_KERNEL); + if (!svc) + return -ENOMEM; + + svc->uaudio_wq = create_singlethread_workqueue("uaudio_svc"); + if (!svc->uaudio_wq) { + ret = -ENOMEM; + goto free_svc; + } + + svc->uaudio_svc_hdl = qmi_handle_create(uaudio_qmi_svc_ntfy, NULL); + if (!svc->uaudio_svc_hdl) { + pr_err("%s: Error creating svc_hdl\n", __func__); + ret = -EFAULT; + goto destroy_uaudio_wq; + } + + ret = qmi_svc_register(svc->uaudio_svc_hdl, &uaudio_svc_ops_options); + if (ret < 0) { + pr_err("%s:Error registering uaudio svc %d\n", __func__, ret); + goto destroy_svc_handle; + } + + INIT_WORK(&svc->recv_msg_work, uaudio_qmi_svc_recv_msg); + + uaudio_svc = svc; + + return 0; + +destroy_svc_handle: + qmi_handle_destroy(svc->uaudio_svc_hdl); +destroy_uaudio_wq: + destroy_workqueue(svc->uaudio_wq); +free_svc: + kfree(svc); + return ret; +} + +static void uaudio_qmi_svc_exit(void) +{ + struct uaudio_qmi_svc *svc = uaudio_svc; + + qmi_svc_unregister(svc->uaudio_svc_hdl); + flush_workqueue(svc->uaudio_wq); + qmi_handle_destroy(svc->uaudio_svc_hdl); + destroy_workqueue(svc->uaudio_wq); + kfree(svc); + uaudio_svc = NULL; +} + +static int __init uaudio_qmi_plat_init(void) +{ + int ret; + + ret = platform_driver_register(&uaudio_qmi_driver); + if (ret) + return ret; + + return uaudio_qmi_svc_init(); +} + +static void __exit uaudio_qmi_plat_exit(void) +{ + uaudio_qmi_svc_exit(); + platform_driver_unregister(&uaudio_qmi_driver); +} + +module_init(uaudio_qmi_plat_init); +module_exit(uaudio_qmi_plat_exit); + +MODULE_DESCRIPTION("USB AUDIO QMI Service Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/usb/usb_audio_qmi_v01.c b/sound/usb/usb_audio_qmi_v01.c new file mode 100644 index 000000000000..31b1ba74d5c7 --- /dev/null +++ b/sound/usb/usb_audio_qmi_v01.c @@ -0,0 +1,565 @@ + /* Copyright (c) 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. + * + */ +#include <linux/qmi_encdec.h> + +#include <soc/qcom/msm_qmi_interface.h> + +#include "usb_audio_qmi_v01.h" + +static struct elem_info mem_info_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint64_t), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct mem_info_v01, + va), + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint64_t), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct mem_info_v01, + pa), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct mem_info_v01, + size), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct elem_info apps_mem_info_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct mem_info_v01), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct apps_mem_info_v01, + evt_ring), + .ei_array = mem_info_v01_ei, + }, + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct mem_info_v01), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct apps_mem_info_v01, + tr_data), + .ei_array = mem_info_v01_ei, + }, + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct mem_info_v01), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct apps_mem_info_v01, + tr_sync), + .ei_array = mem_info_v01_ei, + }, + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct mem_info_v01), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct apps_mem_info_v01, + xfer_buff), + .ei_array = mem_info_v01_ei, + }, + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct mem_info_v01), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct apps_mem_info_v01, + dcba), + .ei_array = mem_info_v01_ei, + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct elem_info usb_endpoint_descriptor_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct usb_endpoint_descriptor_v01, + bLength), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct usb_endpoint_descriptor_v01, + bDescriptorType), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct usb_endpoint_descriptor_v01, + bEndpointAddress), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct usb_endpoint_descriptor_v01, + bmAttributes), + }, + { + .data_type = QMI_UNSIGNED_2_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint16_t), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct usb_endpoint_descriptor_v01, + wMaxPacketSize), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct usb_endpoint_descriptor_v01, + bInterval), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct usb_endpoint_descriptor_v01, + bRefresh), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct usb_endpoint_descriptor_v01, + bSynchAddress), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct elem_info usb_interface_descriptor_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct usb_interface_descriptor_v01, + bLength), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct usb_interface_descriptor_v01, + bDescriptorType), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct usb_interface_descriptor_v01, + bInterfaceNumber), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct usb_interface_descriptor_v01, + bAlternateSetting), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct usb_interface_descriptor_v01, + bNumEndpoints), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct usb_interface_descriptor_v01, + bInterfaceClass), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct usb_interface_descriptor_v01, + bInterfaceSubClass), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct usb_interface_descriptor_v01, + bInterfaceProtocol), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct usb_interface_descriptor_v01, + iInterface), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info qmi_uaudio_stream_req_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01, + priv_data), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01, + enable), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = 0x03, + .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01, + usb_token), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = 0x04, + .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01, + audio_format), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = 0x05, + .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01, + number_of_ch), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = 0x06, + .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01, + bit_rate), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = 0x07, + .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01, + xfer_buff_size), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info qmi_uaudio_stream_resp_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof( + struct qmi_uaudio_stream_resp_msg_v01, + priv_data), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof( + struct qmi_uaudio_stream_resp_msg_v01, + status), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = 0x03, + .offset = offsetof( + struct qmi_uaudio_stream_resp_msg_v01, + slot_id), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x04, + .offset = offsetof( + struct qmi_uaudio_stream_resp_msg_v01, + bSubslotSize), + }, + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct usb_interface_descriptor_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x05, + .offset = offsetof( + struct qmi_uaudio_stream_resp_msg_v01, + std_as_opr_intf_desc), + .ei_array = usb_interface_descriptor_v01_ei, + }, + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct usb_endpoint_descriptor_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x06, + .offset = offsetof( + struct qmi_uaudio_stream_resp_msg_v01, + std_as_data_ep_desc), + .ei_array = usb_endpoint_descriptor_v01_ei, + }, + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct usb_endpoint_descriptor_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x07, + .offset = offsetof( + struct qmi_uaudio_stream_resp_msg_v01, + std_as_sync_ep_desc), + .ei_array = usb_endpoint_descriptor_v01_ei, + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x08, + .offset = offsetof( + struct qmi_uaudio_stream_resp_msg_v01, + bDelay), + }, + { + .data_type = QMI_UNSIGNED_2_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint16_t), + .is_array = NO_ARRAY, + .tlv_type = 0x09, + .offset = offsetof( + struct qmi_uaudio_stream_resp_msg_v01, + bcdADC), + }, + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct apps_mem_info_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x0A, + .offset = offsetof( + struct qmi_uaudio_stream_resp_msg_v01, + xhci_mem_info), + .ei_array = apps_mem_info_v01_ei, + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x0B, + .offset = offsetof( + struct qmi_uaudio_stream_resp_msg_v01, + interrupter_num), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info qmi_uaudio_stream_ind_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01, + usb_token), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01, + priv_data), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = 0x03, + .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01, + status), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = 0x04, + .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01, + slot_id), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x05, + .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01, + bSubslotSize), + }, + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct usb_interface_descriptor_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x06, + .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01, + std_as_opr_intf_desc), + .ei_array = usb_interface_descriptor_v01_ei, + }, + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct usb_endpoint_descriptor_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x07, + .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01, + std_as_data_ep_desc), + .ei_array = usb_endpoint_descriptor_v01_ei, + }, + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct usb_endpoint_descriptor_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x08, + .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01, + std_as_sync_ep_desc), + .ei_array = usb_endpoint_descriptor_v01_ei, + }, + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct apps_mem_info_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x09, + .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01, + xhci_mem_info), + .ei_array = apps_mem_info_v01_ei, + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; diff --git a/sound/usb/usb_audio_qmi_v01.h b/sound/usb/usb_audio_qmi_v01.h new file mode 100644 index 000000000000..7ad1ab8a61a9 --- /dev/null +++ b/sound/usb/usb_audio_qmi_v01.h @@ -0,0 +1,103 @@ + /* Copyright (c) 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 USB_QMI_V01_H +#define USB_QMI_V01_H + +#define UAUDIO_STREAM_SERVICE_ID_V01 0x41C +#define UAUDIO_STREAM_SERVICE_VERS_V01 0x01 + +#define QMI_UAUDIO_STREAM_RESP_V01 0x0001 +#define QMI_UAUDIO_STREAM_REQ_V01 0x0001 +#define QMI_UADUIO_STREAM_IND_V01 0x0001 + + +struct mem_info_v01 { + uint64_t va; + uint64_t pa; + uint32_t size; +}; + +struct apps_mem_info_v01 { + struct mem_info_v01 evt_ring; + struct mem_info_v01 tr_data; + struct mem_info_v01 tr_sync; + struct mem_info_v01 xfer_buff; + struct mem_info_v01 dcba; +}; + +struct usb_endpoint_descriptor_v01 { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bEndpointAddress; + uint8_t bmAttributes; + uint16_t wMaxPacketSize; + uint8_t bInterval; + uint8_t bRefresh; + uint8_t bSynchAddress; +}; + +struct usb_interface_descriptor_v01 { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bInterfaceNumber; + uint8_t bAlternateSetting; + uint8_t bNumEndpoints; + uint8_t bInterfaceClass; + uint8_t bInterfaceSubClass; + uint8_t bInterfaceProtocol; + uint8_t iInterface; +}; + +struct qmi_uaudio_stream_req_msg_v01 { + uint32_t priv_data; + uint8_t enable; + uint32_t usb_token; + uint32_t audio_format; + uint32_t number_of_ch; + uint32_t bit_rate; + uint32_t xfer_buff_size; +}; +#define QMI_UAUDIO_STREAM_REQ_MSG_V01_MAX_MSG_LEN 46 +extern struct elem_info qmi_uaudio_stream_req_msg_v01_ei[]; + +struct qmi_uaudio_stream_resp_msg_v01 { + uint32_t priv_data; + uint32_t status; + uint32_t slot_id; + uint8_t bSubslotSize; + struct usb_interface_descriptor_v01 std_as_opr_intf_desc; + struct usb_endpoint_descriptor_v01 std_as_data_ep_desc; + struct usb_endpoint_descriptor_v01 std_as_sync_ep_desc; + uint8_t bDelay; + uint16_t bcdADC; + struct apps_mem_info_v01 xhci_mem_info; + uint8_t interrupter_num; +}; +#define QMI_UAUDIO_STREAM_RESP_MSG_V01_MAX_MSG_LEN 177 +extern struct elem_info qmi_uaudio_stream_resp_msg_v01_ei[]; + +struct qmi_uaudio_stream_ind_msg_v01 { + uint32_t usb_token; + uint32_t priv_data; + uint32_t status; + uint32_t slot_id; + uint8_t bSubslotSize; + struct usb_interface_descriptor_v01 std_as_opr_intf_desc; + struct usb_endpoint_descriptor_v01 std_as_data_ep_desc; + struct usb_endpoint_descriptor_v01 std_as_sync_ep_desc; + struct apps_mem_info_v01 xhci_mem_info; +}; +#define QMI_UAUDIO_STREAM_IND_MSG_V01_MAX_MSG_LEN 171 +extern struct elem_info qmi_uaudio_stream_ind_msg_v01_ei[]; + +#endif diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index b665d85555cb..b776122d0ca9 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -60,6 +60,10 @@ struct snd_usb_audio { bool autoclock; /* from the 'autoclock' module param */ struct usb_host_interface *ctrl_intf; /* the audio control interface */ + + struct mutex dev_lock; /* to protect any race with disconnect */ + int card_num; /* cache pcm card number to use upon disconnect */ + void (*disconnect_cb)(struct snd_usb_audio *chip); }; #define usb_audio_err(chip, fmt, args...) \ |
