USB/Thunderbolt patches for 5.18-rc1
Here is the big set of USB and Thunderbolt changes for 5.18-rc1. Nothing major in here, just lots of little improvements and cleanups and new device support. Highlights are: - list iterator fixups for when we walk past the end of the list (a common problem that was cut/pasted in almost all USB gadget drivers) - xen USB driver "hardening" for malicious hosts - xhci driver updates and fixes for more hardware types - xhci debug cable fixes to make it actually work again - usb gadget audio driver improvements - usb gadget storage fixes to work with OS-X - lots of other small usb gadget fixes and updates - USB DWC3 driver improvements for more hardware types - Lots of other small USB driver improvements - DTS updates for some USB platforms Note, the DTS updates will have a merge conflict in your tree. The fixup should be simple, but if not, I can provide a merged tree if needed. All of these have been in linux-next for a while with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCYj7qpQ8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ylRsQCcCryYifAvE2gHaNfI4B01JRWk7gQAoLNiUgvl l+srEAXgIVueDhmDxy5P =/Ppv -----END PGP SIGNATURE----- Merge tag 'usb-5.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb Pull USB/Thunderbolt updates from Greg KH: "Here is the big set of USB and Thunderbolt changes for 5.18-rc1. Nothing major in here, just lots of little improvements and cleanups and new device support. Highlights are: - list iterator fixups for when we walk past the end of the list (a common problem that was cut/pasted in almost all USB gadget drivers) - xen USB driver "hardening" for malicious hosts - xhci driver updates and fixes for more hardware types - xhci debug cable fixes to make it actually work again - usb gadget audio driver improvements - usb gadget storage fixes to work with OS-X - lots of other small usb gadget fixes and updates - USB DWC3 driver improvements for more hardware types - Lots of other small USB driver improvements - DTS updates for some USB platforms All of these have been in linux-next for a while with no reported issues" * tag 'usb-5.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (172 commits) usb: gadget: fsl_qe_udc: Add missing semicolon in qe_ep_dequeue() dt-bindings: usb: mtk-xhci: add compatible for mt8186 usb: dwc3: Issue core soft reset before enabling run/stop usb: gadget: Makefile: remove ccflags-y USB: usb-storage: Fix use of bitfields for hardware data in ene_ub6250.c usb: gadget: eliminate anonymous module_init & module_exit usb: usbip: eliminate anonymous module_init & module_exit xen/usb: harden xen_hcd against malicious backends usb: dwc3: gadget: Wait for ep0 xfers to complete during dequeue usb: dwc3: gadget: move cmd_endtransfer to extra function usb: dwc3: gadget: ep_queue simplify isoc start condition xen/usb: don't use arbitrary_virt_to_machine() usb: isp1760: remove redundant max_packet() macro usb: oxu210hp-hcd: remove redundant call to max_packet() macro usb: common: usb-conn-gpio: Make VBUS supply completely optional USB: storage: ums-realtek: fix error code in rts51x_read_mem() usb: early: xhci-dbc: Fix xdbc number parsing usb: early: xhci-dbc: Remove duplicate keep parsing x86/tsc: Be consistent about use_tsc_delay() usb: gadget: udc: s3c2410: remove usage of list iterator past the loop body ...
This commit is contained in:
commit
710f5d627a
|
@ -6,7 +6,7 @@ Description:
|
|||
|
||||
===================== =======================================
|
||||
c_chmask capture channel mask
|
||||
c_srate capture sampling rate
|
||||
c_srate list of capture sampling rates (comma-separated)
|
||||
c_ssize capture sample size (bytes)
|
||||
c_mute_present capture mute control enable
|
||||
c_volume_present capture volume control enable
|
||||
|
@ -17,7 +17,7 @@ Description:
|
|||
c_volume_res capture volume control resolution
|
||||
(in 1/256 dB)
|
||||
p_chmask playback channel mask
|
||||
p_srate playback sampling rate
|
||||
p_srate list of playback sampling rates (comma-separated)
|
||||
p_ssize playback sample size (bytes)
|
||||
p_mute_present playback mute control enable
|
||||
p_volume_present playback volume control enable
|
||||
|
@ -29,4 +29,5 @@ Description:
|
|||
(in 1/256 dB)
|
||||
req_number the number of pre-allocated requests
|
||||
for both capture and playback
|
||||
function_name name of the interface
|
||||
===================== =======================================
|
||||
|
|
|
@ -6,8 +6,9 @@ Description:
|
|||
|
||||
===================== =======================================
|
||||
c_chmask capture channel mask
|
||||
c_srate capture sampling rate
|
||||
c_srate list of capture sampling rates (comma-separated)
|
||||
c_ssize capture sample size (bytes)
|
||||
c_hs_bint capture bInterval for HS/SS (1-4: fixed, 0: auto)
|
||||
c_sync capture synchronization type
|
||||
(async/adaptive)
|
||||
c_mute_present capture mute control enable
|
||||
|
@ -20,8 +21,9 @@ Description:
|
|||
(in 1/256 dB)
|
||||
fb_max maximum extra bandwidth in async mode
|
||||
p_chmask playback channel mask
|
||||
p_srate playback sampling rate
|
||||
p_srate list of playback sampling rates (comma-separated)
|
||||
p_ssize playback sample size (bytes)
|
||||
p_hs_bint playback bInterval for HS/SS (1-4: fixed, 0: auto)
|
||||
p_mute_present playback mute control enable
|
||||
p_volume_present playback volume control enable
|
||||
p_volume_min playback volume control min value
|
||||
|
@ -32,4 +34,5 @@ Description:
|
|||
(in 1/256 dB)
|
||||
req_number the number of pre-allocated requests
|
||||
for both capture and playback
|
||||
function_name name of the interface
|
||||
===================== =======================================
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
What: /sys/bus/platform/drivers/eud/.../enable
|
||||
Date: February 2022
|
||||
Contact: Souradeep Chowdhury <quic_schowdhu@quicinc.com>
|
||||
Description:
|
||||
The Enable/Disable sysfs interface for Embedded
|
||||
USB Debugger(EUD). This enables and disables the
|
||||
EUD based on a 1 or a 0 value. By enabling EUD,
|
||||
the user is able to activate the mini-usb hub of
|
||||
EUD for debug and trace capabilities.
|
|
@ -0,0 +1,77 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: "http://devicetree.org/schemas/soc/qcom/qcom,eud.yaml#"
|
||||
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
|
||||
|
||||
title: Qualcomm Embedded USB Debugger
|
||||
|
||||
maintainers:
|
||||
- Souradeep Chowdhury <quic_schowdhu@quicinc.com>
|
||||
|
||||
description:
|
||||
This binding is used to describe the Qualcomm Embedded USB Debugger, which is
|
||||
mini USB-hub implemented on chip to support USB-based debug capabilities.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- qcom,sc7280-eud
|
||||
- const: qcom,eud
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description: EUD Base Register Region
|
||||
- description: EUD Mode Manager Register
|
||||
|
||||
interrupts:
|
||||
description: EUD interrupt
|
||||
maxItems: 1
|
||||
|
||||
ports:
|
||||
$ref: /schemas/graph.yaml#/properties/ports
|
||||
description:
|
||||
These ports is to be attached to the endpoint of the DWC3 controller node
|
||||
and type C connector node. The controller has the "usb-role-switch"
|
||||
property.
|
||||
|
||||
properties:
|
||||
port@0:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
description: This port is to be attached to the DWC3 controller.
|
||||
|
||||
port@1:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
description: This port is to be attached to the type C connector.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- ports
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
eud@88e0000 {
|
||||
compatible = "qcom,sc7280-eud","qcom,eud";
|
||||
reg = <0x88e0000 0x2000>,
|
||||
<0x88e2000 0x1000>;
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
eud_ep: endpoint {
|
||||
remote-endpoint = <&usb2_role_switch>;
|
||||
};
|
||||
};
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
eud_con: endpoint {
|
||||
remote-endpoint = <&con_eud>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
|
@ -1,115 +0,0 @@
|
|||
Samsung Exynos SoC USB controller
|
||||
|
||||
The USB devices interface with USB controllers on Exynos SOCs.
|
||||
The device node has following properties.
|
||||
|
||||
EHCI
|
||||
Required properties:
|
||||
- compatible: should be "samsung,exynos4210-ehci" for USB 2.0
|
||||
EHCI controller in host mode.
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
- interrupts: interrupt number to the cpu.
|
||||
- clocks: from common clock binding: handle to usb clock.
|
||||
- clock-names: from common clock binding: Shall be "usbhost".
|
||||
- phys: from the *Generic PHY* bindings; array specifying phy(s) used
|
||||
by the root port.
|
||||
- phy-names: from the *Generic PHY* bindings; array of the names for
|
||||
each phy for the root ports, must be a subset of the following:
|
||||
"host", "hsic0", "hsic1".
|
||||
|
||||
Optional properties:
|
||||
- samsung,vbus-gpio: if present, specifies the GPIO that
|
||||
needs to be pulled up for the bus to be powered.
|
||||
|
||||
Example:
|
||||
|
||||
usb@12110000 {
|
||||
compatible = "samsung,exynos4210-ehci";
|
||||
reg = <0x12110000 0x100>;
|
||||
interrupts = <0 71 0>;
|
||||
samsung,vbus-gpio = <&gpx2 6 1 3 3>;
|
||||
|
||||
clocks = <&clock 285>;
|
||||
clock-names = "usbhost";
|
||||
|
||||
phys = <&usb2phy 1>;
|
||||
phy-names = "host";
|
||||
};
|
||||
|
||||
OHCI
|
||||
Required properties:
|
||||
- compatible: should be "samsung,exynos4210-ohci" for USB 2.0
|
||||
OHCI companion controller in host mode.
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
- interrupts: interrupt number to the cpu.
|
||||
- clocks: from common clock binding: handle to usb clock.
|
||||
- clock-names: from common clock binding: Shall be "usbhost".
|
||||
- phys: from the *Generic PHY* bindings; array specifying phy(s) used
|
||||
by the root port.
|
||||
- phy-names: from the *Generic PHY* bindings; array of the names for
|
||||
each phy for the root ports, must be a subset of the following:
|
||||
"host", "hsic0", "hsic1".
|
||||
|
||||
Example:
|
||||
usb@12120000 {
|
||||
compatible = "samsung,exynos4210-ohci";
|
||||
reg = <0x12120000 0x100>;
|
||||
interrupts = <0 71 0>;
|
||||
|
||||
clocks = <&clock 285>;
|
||||
clock-names = "usbhost";
|
||||
|
||||
phys = <&usb2phy 1>;
|
||||
phy-names = "host";
|
||||
};
|
||||
|
||||
DWC3
|
||||
Required properties:
|
||||
- compatible: should be one of the following -
|
||||
"samsung,exynos5250-dwusb3": for USB 3.0 DWC3 controller on
|
||||
Exynos5250/5420.
|
||||
"samsung,exynos5433-dwusb3": for USB 3.0 DWC3 controller on
|
||||
Exynos5433.
|
||||
"samsung,exynos7-dwusb3": for USB 3.0 DWC3 controller on Exynos7.
|
||||
- #address-cells, #size-cells : should be '1' if the device has sub-nodes
|
||||
with 'reg' property.
|
||||
- ranges: allows valid 1:1 translation between child's address space and
|
||||
parent's address space
|
||||
- clocks: Clock IDs array as required by the controller.
|
||||
- clock-names: Names of clocks corresponding to IDs in the clock property.
|
||||
Following clock names shall be provided for different
|
||||
compatibles:
|
||||
- samsung,exynos5250-dwusb3: "usbdrd30",
|
||||
- samsung,exynos5433-dwusb3: "aclk", "susp_clk", "pipe_pclk",
|
||||
"phyclk",
|
||||
- samsung,exynos7-dwusb3: "usbdrd30", "usbdrd30_susp_clk",
|
||||
"usbdrd30_axius_clk"
|
||||
- vdd10-supply: 1.0V powr supply
|
||||
- vdd33-supply: 3.0V/3.3V power supply
|
||||
|
||||
Sub-nodes:
|
||||
The dwc3 core should be added as subnode to Exynos dwc3 glue.
|
||||
- dwc3 :
|
||||
The binding details of dwc3 can be found in:
|
||||
Documentation/devicetree/bindings/usb/snps,dwc3.yaml
|
||||
|
||||
Example:
|
||||
usb@12000000 {
|
||||
compatible = "samsung,exynos5250-dwusb3";
|
||||
clocks = <&clock 286>;
|
||||
clock-names = "usbdrd30";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges;
|
||||
vdd10-supply = <&ldo11_reg>;
|
||||
vdd33-supply = <&ldo9_reg>;
|
||||
|
||||
dwc3 {
|
||||
compatible = "synopsys,dwc3";
|
||||
reg = <0x12000000 0x10000>;
|
||||
interrupts = <0 72 0>;
|
||||
usb-phy = <&usb2_phy &usb3_phy>;
|
||||
};
|
||||
};
|
|
@ -15,9 +15,9 @@ properties:
|
|||
const: fsl,imx8mp-dwc3
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
description: Address and length of the register set for the wrapper of
|
||||
dwc3 core on the SOC.
|
||||
items:
|
||||
- description: Address and length of the register set for HSIO Block Control
|
||||
- description: Address and length of the register set for the wrapper of dwc3 core on the SOC.
|
||||
|
||||
"#address-cells":
|
||||
enum: [ 1, 2 ]
|
||||
|
@ -49,6 +49,28 @@ properties:
|
|||
- const: hsio
|
||||
- const: suspend
|
||||
|
||||
fsl,permanently-attached:
|
||||
type: boolean
|
||||
description:
|
||||
Indicates if the device atached to a downstream port is
|
||||
permanently attached.
|
||||
|
||||
fsl,disable-port-power-control:
|
||||
type: boolean
|
||||
description:
|
||||
Indicates whether the host controller implementation includes port
|
||||
power control. Defines Bit 3 in capability register (HCCPARAMS).
|
||||
|
||||
fsl,over-current-active-low:
|
||||
type: boolean
|
||||
description:
|
||||
Over current signal polarity is active low.
|
||||
|
||||
fsl,power-active-low:
|
||||
type: boolean
|
||||
description:
|
||||
Power pad (PWR) polarity is active low.
|
||||
|
||||
# Required child node:
|
||||
|
||||
patternProperties:
|
||||
|
@ -74,7 +96,8 @@ examples:
|
|||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
usb3_0: usb@32f10100 {
|
||||
compatible = "fsl,imx8mp-dwc3";
|
||||
reg = <0x32f10100 0x8>;
|
||||
reg = <0x32f10100 0x8>,
|
||||
<0x381f0000 0x20>;
|
||||
clocks = <&clk IMX8MP_CLK_HSIO_ROOT>,
|
||||
<&clk IMX8MP_CLK_USB_ROOT>;
|
||||
clock-names = "hsio", "suspend";
|
||||
|
|
|
@ -30,6 +30,7 @@ properties:
|
|||
- mediatek,mt7629-xhci
|
||||
- mediatek,mt8173-xhci
|
||||
- mediatek,mt8183-xhci
|
||||
- mediatek,mt8186-xhci
|
||||
- mediatek,mt8192-xhci
|
||||
- mediatek,mt8195-xhci
|
||||
- const: mediatek,mtk-xhci
|
||||
|
@ -146,7 +147,11 @@ properties:
|
|||
2 - used by mt2712 etc, revision 2 following IPM rule;
|
||||
101 - used by mt8183, specific 1.01;
|
||||
102 - used by mt8192, specific 1.02;
|
||||
enum: [1, 2, 101, 102]
|
||||
103 - used by mt8195, IP0, specific 1.03;
|
||||
104 - used by mt8195, IP1, specific 1.04;
|
||||
105 - used by mt8195, IP2, specific 1.05;
|
||||
106 - used by mt8195, IP3, specific 1.06;
|
||||
enum: [1, 2, 101, 102, 103, 104, 105, 106]
|
||||
|
||||
mediatek,u3p-dis-msk:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/usb/microchip,mpfs-musb.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Microchip MPFS USB Controller Device Tree Bindings
|
||||
|
||||
allOf:
|
||||
- $ref: usb-drd.yaml#
|
||||
|
||||
maintainers:
|
||||
- Conor Dooley <conor.dooley@microchip.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- microchip,mpfs-musb
|
||||
|
||||
dr_mode: true
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
minItems: 2
|
||||
maxItems: 2
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: dma
|
||||
- const: mc
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- interrupt-names
|
||||
- clocks
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include "dt-bindings/clock/microchip,mpfs-clock.h"
|
||||
usb@20201000 {
|
||||
compatible = "microchip,mpfs-musb";
|
||||
reg = <0x20201000 0x1000>;
|
||||
clocks = <&clkcfg CLK_USB>;
|
||||
interrupt-parent = <&plic>;
|
||||
interrupts = <86>, <87>;
|
||||
interrupt-names = "dma", "mc";
|
||||
dr_mode = "host";
|
||||
};
|
||||
|
||||
...
|
|
@ -16,6 +16,7 @@ properties:
|
|||
- qcom,ipq4019-dwc3
|
||||
- qcom,ipq6018-dwc3
|
||||
- qcom,ipq8064-dwc3
|
||||
- qcom,msm8953-dwc3
|
||||
- qcom,msm8996-dwc3
|
||||
- qcom,msm8998-dwc3
|
||||
- qcom,sc7180-dwc3
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: "http://devicetree.org/schemas/usb/richtek,rt1719.yaml#"
|
||||
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
|
||||
|
||||
title: Richtek RT1719 sink-only Type-C PD controller bindings
|
||||
|
||||
maintainers:
|
||||
- ChiYuan Huang <cy_huang@richtek.com>
|
||||
|
||||
description: |
|
||||
The RT1719 is a sink-only USB Type-C contoller that complies with the latest
|
||||
USB Type-C and PD standards. It does the USB Type-C detection including attach
|
||||
and orientation. It integrates the physical layer of the USB BMC power
|
||||
delivery protocol to allow up to 100W of power. The BMC PD block enables full
|
||||
support for alternative interfaces of the Type-C specification.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- richtek,rt1719
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
wakeup-source:
|
||||
description: enable IRQ remote wakeup, see power/wakeup-source.txt
|
||||
type: boolean
|
||||
|
||||
connector:
|
||||
type: object
|
||||
$ref: ../connector/usb-connector.yaml#
|
||||
description:
|
||||
Properties for usb c connector.
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- connector
|
||||
- interrupts
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
i2c0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
rt1719@43 {
|
||||
compatible = "richtek,rt1719";
|
||||
reg = <0x43>;
|
||||
interrupts-extended = <&gpio26 2 IRQ_TYPE_LEVEL_LOW>;
|
||||
wakeup-source;
|
||||
|
||||
connector {
|
||||
compatible = "usb-c-connector";
|
||||
label = "USB-C";
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
endpoint {
|
||||
remote-endpoint = <&usb_hs>;
|
||||
};
|
||||
};
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
endpoint {
|
||||
remote-endpoint = <&usb_ss>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
|
@ -30,6 +30,7 @@ select:
|
|||
enum:
|
||||
- rockchip,rk3328-dwc3
|
||||
- rockchip,rk3399-dwc3
|
||||
- rockchip,rk3568-dwc3
|
||||
required:
|
||||
- compatible
|
||||
|
||||
|
@ -39,6 +40,7 @@ properties:
|
|||
- enum:
|
||||
- rockchip,rk3328-dwc3
|
||||
- rockchip,rk3399-dwc3
|
||||
- rockchip,rk3568-dwc3
|
||||
- const: snps,dwc3
|
||||
|
||||
reg:
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/usb/samsung,exynos-dwc3.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Samsung Exynos SoC USB 3.0 DWC3 Controller
|
||||
|
||||
maintainers:
|
||||
- Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- samsung,exynos5250-dwusb3
|
||||
- samsung,exynos5433-dwusb3
|
||||
- samsung,exynos7-dwusb3
|
||||
|
||||
'#address-cells':
|
||||
const: 1
|
||||
|
||||
clocks:
|
||||
minItems: 1
|
||||
maxItems: 4
|
||||
|
||||
clock-names:
|
||||
minItems: 1
|
||||
maxItems: 4
|
||||
|
||||
ranges: true
|
||||
|
||||
'#size-cells':
|
||||
const: 1
|
||||
|
||||
vdd10-supply:
|
||||
description: 1.0V power supply
|
||||
|
||||
vdd33-supply:
|
||||
description: 3.0V/3.3V power supply
|
||||
|
||||
patternProperties:
|
||||
"^usb@[0-9a-f]+$":
|
||||
$ref: snps,dwc3.yaml#
|
||||
description: Required child node
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- '#address-cells'
|
||||
- clocks
|
||||
- clock-names
|
||||
- ranges
|
||||
- '#size-cells'
|
||||
- vdd10-supply
|
||||
- vdd33-supply
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: samsung,exynos5250-dwusb3
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 1
|
||||
maxItems: 1
|
||||
clock-names:
|
||||
items:
|
||||
- const: usbdrd30
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: samsung,exynos54333-dwusb3
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 4
|
||||
maxItems: 4
|
||||
clock-names:
|
||||
items:
|
||||
- const: aclk
|
||||
- const: susp_clk
|
||||
- const: pipe_pclk
|
||||
- const: phyclk
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: samsung,exynos7-dwusb3
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 3
|
||||
maxItems: 3
|
||||
clock-names:
|
||||
items:
|
||||
- const: usbdrd30
|
||||
- const: usbdrd30_susp_clk
|
||||
- const: usbdrd30_axius_clk
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/exynos5420.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
usb {
|
||||
compatible = "samsung,exynos5250-dwusb3";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges;
|
||||
clocks = <&clock CLK_USBD300>;
|
||||
clock-names = "usbdrd30";
|
||||
vdd33-supply = <&ldo9_reg>;
|
||||
vdd10-supply = <&ldo11_reg>;
|
||||
|
||||
usb@12000000 {
|
||||
compatible = "snps,dwc3";
|
||||
reg = <0x12000000 0x10000>;
|
||||
interrupts = <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>;
|
||||
phys = <&usbdrd_phy0 0>, <&usbdrd_phy0 1>;
|
||||
phy-names = "usb2-phy", "usb3-phy";
|
||||
snps,dis_u3_susphy_quirk;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,117 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/usb/samsung,exynos-usb2.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Samsung Exynos SoC USB 2.0 EHCI/OHCI Controller
|
||||
|
||||
maintainers:
|
||||
- Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- samsung,exynos4210-ehci
|
||||
- samsung,exynos4210-ohci
|
||||
|
||||
'#address-cells':
|
||||
const: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: usbhost
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
phys:
|
||||
minItems: 1
|
||||
maxItems: 3
|
||||
|
||||
phy-names:
|
||||
items:
|
||||
enum: [host, hsic0, hsic1]
|
||||
minItems: 1
|
||||
maxItems: 3
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
samsung,vbus-gpio:
|
||||
description:
|
||||
Only for controller in EHCI mode, if present, specifies the GPIO that
|
||||
needs to be pulled up for the bus to be powered.
|
||||
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
"^.*@[0-9a-f]{1,2}$":
|
||||
description: The hard wired USB devices
|
||||
type: object
|
||||
$ref: /usb/usb-device.yaml
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- clocks
|
||||
- clock-names
|
||||
- interrupts
|
||||
- phys
|
||||
- phy-names
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: samsung,exynos4210-ohci
|
||||
then:
|
||||
properties:
|
||||
samsung,vbus-gpio: false
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/exynos5420.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
usb@12110000 {
|
||||
compatible = "samsung,exynos4210-ehci";
|
||||
reg = <0x12110000 0x100>;
|
||||
interrupts = <GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clock CLK_USBH20>;
|
||||
clock-names = "usbhost";
|
||||
phys = <&usb2_phy 0>;
|
||||
phy-names = "host";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
hub@1 {
|
||||
compatible = "usb0424,9514";
|
||||
reg = <1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
usbether@1 {
|
||||
compatible = "usb0424,ec00";
|
||||
reg = <1>;
|
||||
local-mac-address = [00 00 00 00 00 00];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
usb@12120000 {
|
||||
compatible = "samsung,exynos4210-ohci";
|
||||
reg = <0x12120000 0x100>;
|
||||
interrupts = <GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clock CLK_USBH20>;
|
||||
clock-names = "usbhost";
|
||||
phys = <&usb2_phy 0>;
|
||||
phy-names = "host";
|
||||
};
|
|
@ -263,8 +263,11 @@ properties:
|
|||
Value for REFCLKPER field of GUCTL register for reference clock period in
|
||||
nanoseconds, when the hardware set default does not match the actual
|
||||
clock.
|
||||
minimum: 1
|
||||
maximum: 0x3ff
|
||||
|
||||
This binding is deprecated. Instead, provide an appropriate reference clock.
|
||||
minimum: 8
|
||||
maximum: 62
|
||||
deprecated: true
|
||||
|
||||
snps,rx-thr-num-pkt-prd:
|
||||
description:
|
||||
|
@ -332,6 +335,12 @@ properties:
|
|||
items:
|
||||
enum: [1, 4, 8, 16, 32, 64, 128, 256]
|
||||
|
||||
port:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
description:
|
||||
This port is used with the 'usb-role-switch' property to connect the
|
||||
dwc3 to type C connector.
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
required:
|
||||
|
|
|
@ -33,7 +33,7 @@ patternProperties:
|
|||
"^.*@[0-9a-f]{1,2}$":
|
||||
description: The hard wired USB devices
|
||||
type: object
|
||||
$ref: /usb/usb-device.yaml
|
||||
$ref: /schemas/usb/usb-device.yaml
|
||||
|
||||
additionalProperties: true
|
||||
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/usb/willsemi,wusb3801.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: WUSB3801 Type-C port controller DT bindings
|
||||
|
||||
description:
|
||||
The Will Semiconductor WUSB3801 is a USB Type-C port controller which
|
||||
supports role and plug orientation detection using the CC pins. It is
|
||||
compatible with the USB Type-C Cable and Connector Specification v1.2.
|
||||
|
||||
maintainers:
|
||||
- Samuel Holland <samuel@sholland.org>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- willsemi,wusb3801
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
connector:
|
||||
type: object
|
||||
$ref: ../connector/usb-connector.yaml#
|
||||
description:
|
||||
The managed USB Type-C connector. Since WUSB3801 does not support
|
||||
Power Delivery, the node should have the "pd-disable" property.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: usb-c-connector
|
||||
|
||||
required:
|
||||
- pd-disable
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- connector
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
tcpc@60 {
|
||||
compatible = "willsemi,wusb3801";
|
||||
reg = <0x60>;
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <4 IRQ_TYPE_LEVEL_LOW>;
|
||||
|
||||
connector {
|
||||
compatible = "usb-c-connector";
|
||||
label = "USB-C";
|
||||
vbus-supply = <&otg_switch>;
|
||||
power-role = "dual";
|
||||
try-power-role = "sink";
|
||||
data-role = "dual";
|
||||
typec-power-opmode = "default";
|
||||
pd-disable;
|
||||
};
|
||||
};
|
||||
};
|
|
@ -1371,6 +1371,8 @@ patternProperties:
|
|||
description: Wi2Wi, Inc.
|
||||
"^wiligear,.*":
|
||||
description: Wiligear, Ltd.
|
||||
"^willsemi,.*":
|
||||
description: Will Semiconductor Ltd.
|
||||
"^winbond,.*":
|
||||
description: Winbond Electronics corp.
|
||||
"^wingtech,.*":
|
||||
|
|
|
@ -726,7 +726,7 @@ The uac2 function provides these attributes in its function directory:
|
|||
|
||||
================ ====================================================
|
||||
c_chmask capture channel mask
|
||||
c_srate capture sampling rate
|
||||
c_srate list of capture sampling rates (comma-separated)
|
||||
c_ssize capture sample size (bytes)
|
||||
c_sync capture synchronization type (async/adaptive)
|
||||
c_mute_present capture mute control enable
|
||||
|
@ -734,17 +734,20 @@ The uac2 function provides these attributes in its function directory:
|
|||
c_volume_min capture volume control min value (in 1/256 dB)
|
||||
c_volume_max capture volume control max value (in 1/256 dB)
|
||||
c_volume_res capture volume control resolution (in 1/256 dB)
|
||||
c_hs_bint capture bInterval for HS/SS (1-4: fixed, 0: auto)
|
||||
fb_max maximum extra bandwidth in async mode
|
||||
p_chmask playback channel mask
|
||||
p_srate playback sampling rate
|
||||
p_srate list of playback sampling rates (comma-separated)
|
||||
p_ssize playback sample size (bytes)
|
||||
p_mute_present playback mute control enable
|
||||
p_volume_present playback volume control enable
|
||||
p_volume_min playback volume control min value (in 1/256 dB)
|
||||
p_volume_max playback volume control max value (in 1/256 dB)
|
||||
p_volume_res playback volume control resolution (in 1/256 dB)
|
||||
p_hs_bint playback bInterval for HS/SS (1-4: fixed, 0: auto)
|
||||
req_number the number of pre-allocated request for both capture
|
||||
and playback
|
||||
function_name name of the interface
|
||||
================ ====================================================
|
||||
|
||||
The attributes have sane default values.
|
||||
|
@ -916,7 +919,7 @@ The uac1 function provides these attributes in its function directory:
|
|||
|
||||
================ ====================================================
|
||||
c_chmask capture channel mask
|
||||
c_srate capture sampling rate
|
||||
c_srate list of capture sampling rates (comma-separated)
|
||||
c_ssize capture sample size (bytes)
|
||||
c_mute_present capture mute control enable
|
||||
c_volume_present capture volume control enable
|
||||
|
@ -924,7 +927,7 @@ The uac1 function provides these attributes in its function directory:
|
|||
c_volume_max capture volume control max value (in 1/256 dB)
|
||||
c_volume_res capture volume control resolution (in 1/256 dB)
|
||||
p_chmask playback channel mask
|
||||
p_srate playback sampling rate
|
||||
p_srate list of playback sampling rates (comma-separated)
|
||||
p_ssize playback sample size (bytes)
|
||||
p_mute_present playback mute control enable
|
||||
p_volume_present playback volume control enable
|
||||
|
@ -933,6 +936,7 @@ The uac1 function provides these attributes in its function directory:
|
|||
p_volume_res playback volume control resolution (in 1/256 dB)
|
||||
req_number the number of pre-allocated requests for both capture
|
||||
and playback
|
||||
function_name name of the interface
|
||||
================ ====================================================
|
||||
|
||||
The attributes have sane default values.
|
||||
|
|
|
@ -15917,6 +15917,14 @@ F: sound/soc/codecs/wcd-clsh-v2.*
|
|||
F: sound/soc/codecs/wsa881x.c
|
||||
F: sound/soc/qcom/
|
||||
|
||||
QCOM EMBEDDED USB DEBUGGER (EUD)
|
||||
M: Souradeep Chowdhury <quic_schowdhu@quicinc.com>
|
||||
L: linux-arm-msm@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/ABI/testing/sysfs-driver-eud
|
||||
F: Documentation/devicetree/bindings/soc/qcom/qcom,eud.yaml
|
||||
F: drivers/usb/misc/qcom_eud.c
|
||||
|
||||
QCOM IPA DRIVER
|
||||
M: Alex Elder <elder@kernel.org>
|
||||
L: netdev@vger.kernel.org
|
||||
|
|
|
@ -921,7 +921,8 @@
|
|||
|
||||
usb3_0: usb@32f10100 {
|
||||
compatible = "fsl,imx8mp-dwc3";
|
||||
reg = <0x32f10100 0x8>;
|
||||
reg = <0x32f10100 0x8>,
|
||||
<0x381f0000 0x20>;
|
||||
clocks = <&clk IMX8MP_CLK_HSIO_ROOT>,
|
||||
<&clk IMX8MP_CLK_USB_ROOT>;
|
||||
clock-names = "hsio", "suspend";
|
||||
|
@ -963,7 +964,8 @@
|
|||
|
||||
usb3_1: usb@32f10108 {
|
||||
compatible = "fsl,imx8mp-dwc3";
|
||||
reg = <0x32f10108 0x8>;
|
||||
reg = <0x32f10108 0x8>,
|
||||
<0x382f0000 0x20>;
|
||||
clocks = <&clk IMX8MP_CLK_HSIO_ROOT>,
|
||||
<&clk IMX8MP_CLK_USB_ROOT>;
|
||||
clock-names = "hsio", "suspend";
|
||||
|
|
|
@ -752,12 +752,13 @@
|
|||
interrupts = <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>;
|
||||
phys = <&qusb_phy_0>, <&usb0_ssphy>;
|
||||
phy-names = "usb2-phy", "usb3-phy";
|
||||
clocks = <&xo>;
|
||||
clock-names = "ref";
|
||||
tx-fifo-resize;
|
||||
snps,is-utmi-l1-suspend;
|
||||
snps,hird-threshold = /bits/ 8 <0x0>;
|
||||
snps,dis_u2_susphy_quirk;
|
||||
snps,dis_u3_susphy_quirk;
|
||||
snps,ref-clock-period-ns = <0x29>;
|
||||
dr_mode = "host";
|
||||
};
|
||||
};
|
||||
|
|
|
@ -223,11 +223,11 @@
|
|||
clocks = <&zynqmp_clk UART1_REF>, <&zynqmp_clk LPD_LSBUS>;
|
||||
};
|
||||
|
||||
&usb0 {
|
||||
&dwc3_0 {
|
||||
clocks = <&zynqmp_clk USB0_BUS_REF>, <&zynqmp_clk USB3_DUAL_REF>;
|
||||
};
|
||||
|
||||
&usb1 {
|
||||
&dwc3_1 {
|
||||
clocks = <&zynqmp_clk USB1_BUS_REF>, <&zynqmp_clk USB3_DUAL_REF>;
|
||||
};
|
||||
|
||||
|
|
|
@ -809,7 +809,6 @@
|
|||
status = "disabled";
|
||||
compatible = "xlnx,zynqmp-dwc3";
|
||||
reg = <0x0 0xff9d0000 0x0 0x100>;
|
||||
clock-names = "bus_clk", "ref_clk";
|
||||
power-domains = <&zynqmp_firmware PD_USB_0>;
|
||||
resets = <&zynqmp_reset ZYNQMP_RESET_USB0_CORERESET>,
|
||||
<&zynqmp_reset ZYNQMP_RESET_USB0_HIBERRESET>,
|
||||
|
@ -823,6 +822,7 @@
|
|||
interrupt-parent = <&gic>;
|
||||
interrupt-names = "dwc_usb3", "otg";
|
||||
interrupts = <0 65 4>, <0 69 4>;
|
||||
clock-names = "bus_early", "ref";
|
||||
iommus = <&smmu 0x860>;
|
||||
snps,quirk-frame-length-adjustment = <0x20>;
|
||||
/* dma-coherent; */
|
||||
|
@ -835,7 +835,6 @@
|
|||
status = "disabled";
|
||||
compatible = "xlnx,zynqmp-dwc3";
|
||||
reg = <0x0 0xff9e0000 0x0 0x100>;
|
||||
clock-names = "bus_clk", "ref_clk";
|
||||
power-domains = <&zynqmp_firmware PD_USB_1>;
|
||||
resets = <&zynqmp_reset ZYNQMP_RESET_USB1_CORERESET>,
|
||||
<&zynqmp_reset ZYNQMP_RESET_USB1_HIBERRESET>,
|
||||
|
@ -849,6 +848,7 @@
|
|||
interrupt-parent = <&gic>;
|
||||
interrupt-names = "dwc_usb3", "otg";
|
||||
interrupts = <0 70 4>, <0 74 4>;
|
||||
clock-names = "bus_early", "ref";
|
||||
iommus = <&smmu 0x861>;
|
||||
snps,quirk-frame-length-adjustment = <0x20>;
|
||||
/* dma-coherent; */
|
||||
|
|
|
@ -387,7 +387,7 @@ static int __init setup_early_printk(char *buf)
|
|||
#endif
|
||||
#ifdef CONFIG_EARLY_PRINTK_USB_XDBC
|
||||
if (!strncmp(buf, "xdbc", 4))
|
||||
early_xdbc_parse_parameter(buf + 4);
|
||||
early_xdbc_parse_parameter(buf + 4, keep);
|
||||
#endif
|
||||
|
||||
buf++;
|
||||
|
|
|
@ -1487,6 +1487,9 @@ static unsigned long __init get_loops_per_jiffy(void)
|
|||
|
||||
static void __init tsc_enable_sched_clock(void)
|
||||
{
|
||||
loops_per_jiffy = get_loops_per_jiffy();
|
||||
use_tsc_delay();
|
||||
|
||||
/* Sanitize TSC ADJUST before cyc2ns gets initialized */
|
||||
tsc_store_and_check_tsc_adjust(true);
|
||||
cyc2ns_init_boot_cpu();
|
||||
|
@ -1502,8 +1505,6 @@ void __init tsc_early_init(void)
|
|||
return;
|
||||
if (!determine_cpu_tsc_frequencies(true))
|
||||
return;
|
||||
loops_per_jiffy = get_loops_per_jiffy();
|
||||
|
||||
tsc_enable_sched_clock();
|
||||
}
|
||||
|
||||
|
@ -1537,7 +1538,6 @@ void __init tsc_init(void)
|
|||
enable_sched_clock_irqtime();
|
||||
|
||||
lpj_fine = get_loops_per_jiffy();
|
||||
use_tsc_delay();
|
||||
|
||||
check_system_tsc_reliable();
|
||||
|
||||
|
|
|
@ -14,15 +14,15 @@
|
|||
static acpi_status tb_acpi_add_link(acpi_handle handle, u32 level, void *data,
|
||||
void **return_value)
|
||||
{
|
||||
struct acpi_device *adev = acpi_fetch_acpi_dev(handle);
|
||||
struct fwnode_reference_args args;
|
||||
struct fwnode_handle *fwnode;
|
||||
struct tb_nhi *nhi = data;
|
||||
struct acpi_device *adev;
|
||||
struct pci_dev *pdev;
|
||||
struct device *dev;
|
||||
int ret;
|
||||
|
||||
if (acpi_bus_get_device(handle, &adev))
|
||||
if (!adev)
|
||||
return AE_OK;
|
||||
|
||||
fwnode = acpi_fwnode_handle(adev);
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
static int tb_eeprom_ctl_write(struct tb_switch *sw, struct tb_eeprom_ctl *ctl)
|
||||
{
|
||||
return tb_sw_write(sw, ctl, TB_CFG_SWITCH, sw->cap_plug_events + 4, 1);
|
||||
return tb_sw_write(sw, ctl, TB_CFG_SWITCH, sw->cap_plug_events + ROUTER_CS_4, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -25,7 +25,7 @@ static int tb_eeprom_ctl_write(struct tb_switch *sw, struct tb_eeprom_ctl *ctl)
|
|||
*/
|
||||
static int tb_eeprom_ctl_read(struct tb_switch *sw, struct tb_eeprom_ctl *ctl)
|
||||
{
|
||||
return tb_sw_read(sw, ctl, TB_CFG_SWITCH, sw->cap_plug_events + 4, 1);
|
||||
return tb_sw_read(sw, ctl, TB_CFG_SWITCH, sw->cap_plug_events + ROUTER_CS_4, 1);
|
||||
}
|
||||
|
||||
enum tb_eeprom_transfer {
|
||||
|
@ -46,18 +46,18 @@ static int tb_eeprom_active(struct tb_switch *sw, bool enable)
|
|||
if (res)
|
||||
return res;
|
||||
if (enable) {
|
||||
ctl.access_high = 1;
|
||||
ctl.bit_banging_enable = 1;
|
||||
res = tb_eeprom_ctl_write(sw, &ctl);
|
||||
if (res)
|
||||
return res;
|
||||
ctl.access_low = 0;
|
||||
ctl.fl_cs = 0;
|
||||
return tb_eeprom_ctl_write(sw, &ctl);
|
||||
} else {
|
||||
ctl.access_low = 1;
|
||||
ctl.fl_cs = 1;
|
||||
res = tb_eeprom_ctl_write(sw, &ctl);
|
||||
if (res)
|
||||
return res;
|
||||
ctl.access_high = 0;
|
||||
ctl.bit_banging_enable = 0;
|
||||
return tb_eeprom_ctl_write(sw, &ctl);
|
||||
}
|
||||
}
|
||||
|
@ -65,8 +65,8 @@ static int tb_eeprom_active(struct tb_switch *sw, bool enable)
|
|||
/*
|
||||
* tb_eeprom_transfer - transfer one bit
|
||||
*
|
||||
* If TB_EEPROM_IN is passed, then the bit can be retrieved from ctl->data_in.
|
||||
* If TB_EEPROM_OUT is passed, then ctl->data_out will be written.
|
||||
* If TB_EEPROM_IN is passed, then the bit can be retrieved from ctl->fl_do.
|
||||
* If TB_EEPROM_OUT is passed, then ctl->fl_di will be written.
|
||||
*/
|
||||
static int tb_eeprom_transfer(struct tb_switch *sw, struct tb_eeprom_ctl *ctl,
|
||||
enum tb_eeprom_transfer direction)
|
||||
|
@ -77,7 +77,7 @@ static int tb_eeprom_transfer(struct tb_switch *sw, struct tb_eeprom_ctl *ctl,
|
|||
if (res)
|
||||
return res;
|
||||
}
|
||||
ctl->clock = 1;
|
||||
ctl->fl_sk = 1;
|
||||
res = tb_eeprom_ctl_write(sw, ctl);
|
||||
if (res)
|
||||
return res;
|
||||
|
@ -86,7 +86,7 @@ static int tb_eeprom_transfer(struct tb_switch *sw, struct tb_eeprom_ctl *ctl,
|
|||
if (res)
|
||||
return res;
|
||||
}
|
||||
ctl->clock = 0;
|
||||
ctl->fl_sk = 0;
|
||||
return tb_eeprom_ctl_write(sw, ctl);
|
||||
}
|
||||
|
||||
|
@ -101,7 +101,7 @@ static int tb_eeprom_out(struct tb_switch *sw, u8 val)
|
|||
if (res)
|
||||
return res;
|
||||
for (i = 0; i < 8; i++) {
|
||||
ctl.data_out = val & 0x80;
|
||||
ctl.fl_di = val & 0x80;
|
||||
res = tb_eeprom_transfer(sw, &ctl, TB_EEPROM_OUT);
|
||||
if (res)
|
||||
return res;
|
||||
|
@ -126,7 +126,7 @@ static int tb_eeprom_in(struct tb_switch *sw, u8 *val)
|
|||
res = tb_eeprom_transfer(sw, &ctl, TB_EEPROM_IN);
|
||||
if (res)
|
||||
return res;
|
||||
*val |= ctl.data_in;
|
||||
*val |= ctl.fl_do;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -553,9 +553,9 @@ static int tb_drom_parse(struct tb_switch *sw)
|
|||
crc = tb_crc8((u8 *) &header->uid, 8);
|
||||
if (crc != header->uid_crc8) {
|
||||
tb_sw_warn(sw,
|
||||
"DROM UID CRC8 mismatch (expected: %#x, got: %#x), aborting\n",
|
||||
"DROM UID CRC8 mismatch (expected: %#x, got: %#x)\n",
|
||||
header->uid_crc8, crc);
|
||||
return -EINVAL;
|
||||
return -EILSEQ;
|
||||
}
|
||||
if (!sw->uid)
|
||||
sw->uid = header->uid;
|
||||
|
@ -654,6 +654,7 @@ int tb_drom_read(struct tb_switch *sw)
|
|||
sw->drom = kzalloc(size, GFP_KERNEL);
|
||||
if (!sw->drom)
|
||||
return -ENOMEM;
|
||||
read:
|
||||
res = tb_drom_read_n(sw, 0, sw->drom, size);
|
||||
if (res)
|
||||
goto err;
|
||||
|
@ -662,7 +663,11 @@ parse:
|
|||
header = (void *) sw->drom;
|
||||
|
||||
if (header->data_len + TB_DROM_DATA_START != size) {
|
||||
tb_sw_warn(sw, "drom size mismatch, aborting\n");
|
||||
tb_sw_warn(sw, "drom size mismatch\n");
|
||||
if (retries--) {
|
||||
msleep(100);
|
||||
goto read;
|
||||
}
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
@ -683,11 +688,9 @@ parse:
|
|||
|
||||
/* If the DROM parsing fails, wait a moment and retry once */
|
||||
if (res == -EILSEQ && retries--) {
|
||||
tb_sw_warn(sw, "parsing DROM failed, retrying\n");
|
||||
tb_sw_warn(sw, "parsing DROM failed\n");
|
||||
msleep(100);
|
||||
res = tb_drom_read_n(sw, 0, sw->drom, size);
|
||||
if (!res)
|
||||
goto parse;
|
||||
goto read;
|
||||
}
|
||||
|
||||
if (!res)
|
||||
|
|
|
@ -217,6 +217,116 @@ bool tb_lc_is_clx_supported(struct tb_port *port)
|
|||
return !!(val & TB_LC_LINK_ATTR_CPS);
|
||||
}
|
||||
|
||||
/**
|
||||
* tb_lc_is_usb_plugged() - Is there USB device connected to port
|
||||
* @port: Device router lane 0 adapter
|
||||
*
|
||||
* Returns true if the @port has USB type-C device connected.
|
||||
*/
|
||||
bool tb_lc_is_usb_plugged(struct tb_port *port)
|
||||
{
|
||||
struct tb_switch *sw = port->sw;
|
||||
int cap, ret;
|
||||
u32 val;
|
||||
|
||||
if (sw->generation != 3)
|
||||
return false;
|
||||
|
||||
cap = find_port_lc_cap(port);
|
||||
if (cap < 0)
|
||||
return false;
|
||||
|
||||
ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, cap + TB_LC_CS_42, 1);
|
||||
if (ret)
|
||||
return false;
|
||||
|
||||
return !!(val & TB_LC_CS_42_USB_PLUGGED);
|
||||
}
|
||||
|
||||
/**
|
||||
* tb_lc_is_xhci_connected() - Is the internal xHCI connected
|
||||
* @port: Device router lane 0 adapter
|
||||
*
|
||||
* Returns true if the internal xHCI has been connected to @port.
|
||||
*/
|
||||
bool tb_lc_is_xhci_connected(struct tb_port *port)
|
||||
{
|
||||
struct tb_switch *sw = port->sw;
|
||||
int cap, ret;
|
||||
u32 val;
|
||||
|
||||
if (sw->generation != 3)
|
||||
return false;
|
||||
|
||||
cap = find_port_lc_cap(port);
|
||||
if (cap < 0)
|
||||
return false;
|
||||
|
||||
ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, cap + TB_LC_LINK_REQ, 1);
|
||||
if (ret)
|
||||
return false;
|
||||
|
||||
return !!(val & TB_LC_LINK_REQ_XHCI_CONNECT);
|
||||
}
|
||||
|
||||
static int __tb_lc_xhci_connect(struct tb_port *port, bool connect)
|
||||
{
|
||||
struct tb_switch *sw = port->sw;
|
||||
int cap, ret;
|
||||
u32 val;
|
||||
|
||||
if (sw->generation != 3)
|
||||
return -EINVAL;
|
||||
|
||||
cap = find_port_lc_cap(port);
|
||||
if (cap < 0)
|
||||
return cap;
|
||||
|
||||
ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, cap + TB_LC_LINK_REQ, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (connect)
|
||||
val |= TB_LC_LINK_REQ_XHCI_CONNECT;
|
||||
else
|
||||
val &= ~TB_LC_LINK_REQ_XHCI_CONNECT;
|
||||
|
||||
return tb_sw_write(sw, &val, TB_CFG_SWITCH, cap + TB_LC_LINK_REQ, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* tb_lc_xhci_connect() - Connect internal xHCI
|
||||
* @port: Device router lane 0 adapter
|
||||
*
|
||||
* Tells LC to connect the internal xHCI to @port. Returns %0 on success
|
||||
* and negative errno in case of failure. Can be called for Thunderbolt 3
|
||||
* routers only.
|
||||
*/
|
||||
int tb_lc_xhci_connect(struct tb_port *port)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = __tb_lc_xhci_connect(port, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
tb_port_dbg(port, "xHCI connected\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* tb_lc_xhci_disconnect() - Disconnect internal xHCI
|
||||
* @port: Device router lane 0 adapter
|
||||
*
|
||||
* Tells LC to disconnect the internal xHCI from @port. Can be called
|
||||
* for Thunderbolt 3 routers only.
|
||||
*/
|
||||
void tb_lc_xhci_disconnect(struct tb_port *port)
|
||||
{
|
||||
__tb_lc_xhci_connect(port, false);
|
||||
tb_port_dbg(port, "xHCI disconnected\n");
|
||||
}
|
||||
|
||||
static int tb_lc_set_wake_one(struct tb_switch *sw, unsigned int offset,
|
||||
unsigned int flags)
|
||||
{
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
|
@ -1229,8 +1230,6 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|||
spin_lock_init(&nhi->lock);
|
||||
|
||||
res = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
|
||||
if (res)
|
||||
res = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
|
||||
if (res) {
|
||||
dev_err(&pdev->dev, "failed to set DMA mask\n");
|
||||
return res;
|
||||
|
|
|
@ -1528,7 +1528,13 @@ static int tb_plug_events_active(struct tb_switch *sw, bool active)
|
|||
case PCI_DEVICE_ID_INTEL_PORT_RIDGE:
|
||||
break;
|
||||
default:
|
||||
data |= 4;
|
||||
/*
|
||||
* Skip Alpine Ridge, it needs to have vendor
|
||||
* specific USB hotplug event enabled for the
|
||||
* internal xHCI to work.
|
||||
*/
|
||||
if (!tb_switch_is_alpine_ridge(sw))
|
||||
data |= TB_PLUG_EVENTS_USB_DISABLE;
|
||||
}
|
||||
} else {
|
||||
data = data | 0x7c;
|
||||
|
@ -2778,10 +2784,8 @@ int tb_switch_add(struct tb_switch *sw)
|
|||
|
||||
/* read drom */
|
||||
ret = tb_drom_read(sw);
|
||||
if (ret) {
|
||||
dev_err(&sw->dev, "reading DROM failed\n");
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
dev_warn(&sw->dev, "reading DROM failed: %d\n", ret);
|
||||
tb_sw_dbg(sw, "uid: %#llx\n", sw->uid);
|
||||
|
||||
tb_check_quirks(sw);
|
||||
|
@ -2974,6 +2978,10 @@ int tb_switch_resume(struct tb_switch *sw)
|
|||
return err;
|
||||
}
|
||||
|
||||
/* We don't have any way to confirm this was the same device */
|
||||
if (!sw->uid)
|
||||
return -ENODEV;
|
||||
|
||||
if (tb_switch_is_usb4(sw))
|
||||
err = usb4_switch_read_uid(sw, &uid);
|
||||
else
|
||||
|
@ -3689,3 +3697,66 @@ int tb_switch_pcie_l1_enable(struct tb_switch *sw)
|
|||
/* Write to Upstream PCIe bridge #0 aka Up0 */
|
||||
return tb_switch_pcie_bridge_write(sw, 0, 0x143, 0x0c5806b1);
|
||||
}
|
||||
|
||||
/**
|
||||
* tb_switch_xhci_connect() - Connect internal xHCI
|
||||
* @sw: Router whose xHCI to connect
|
||||
*
|
||||
* Can be called to any router. For Alpine Ridge and Titan Ridge
|
||||
* performs special flows that bring the xHCI functional for any device
|
||||
* connected to the type-C port. Call only after PCIe tunnel has been
|
||||
* established. The function only does the connect if not done already
|
||||
* so can be called several times for the same router.
|
||||
*/
|
||||
int tb_switch_xhci_connect(struct tb_switch *sw)
|
||||
{
|
||||
bool usb_port1, usb_port3, xhci_port1, xhci_port3;
|
||||
struct tb_port *port1, *port3;
|
||||
int ret;
|
||||
|
||||
port1 = &sw->ports[1];
|
||||
port3 = &sw->ports[3];
|
||||
|
||||
if (tb_switch_is_alpine_ridge(sw)) {
|
||||
usb_port1 = tb_lc_is_usb_plugged(port1);
|
||||
usb_port3 = tb_lc_is_usb_plugged(port3);
|
||||
xhci_port1 = tb_lc_is_xhci_connected(port1);
|
||||
xhci_port3 = tb_lc_is_xhci_connected(port3);
|
||||
|
||||
/* Figure out correct USB port to connect */
|
||||
if (usb_port1 && !xhci_port1) {
|
||||
ret = tb_lc_xhci_connect(port1);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
if (usb_port3 && !xhci_port3)
|
||||
return tb_lc_xhci_connect(port3);
|
||||
} else if (tb_switch_is_titan_ridge(sw)) {
|
||||
ret = tb_lc_xhci_connect(port1);
|
||||
if (ret)
|
||||
return ret;
|
||||
return tb_lc_xhci_connect(port3);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* tb_switch_xhci_disconnect() - Disconnect internal xHCI
|
||||
* @sw: Router whose xHCI to disconnect
|
||||
*
|
||||
* The opposite of tb_switch_xhci_connect(). Disconnects xHCI on both
|
||||
* ports.
|
||||
*/
|
||||
void tb_switch_xhci_disconnect(struct tb_switch *sw)
|
||||
{
|
||||
if (sw->generation == 3) {
|
||||
struct tb_port *port1 = &sw->ports[1];
|
||||
struct tb_port *port3 = &sw->ports[3];
|
||||
|
||||
tb_lc_xhci_disconnect(port1);
|
||||
tb_port_dbg(port1, "disconnected xHCI\n");
|
||||
tb_lc_xhci_disconnect(port3);
|
||||
tb_port_dbg(port3, "disconnected xHCI\n");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1054,6 +1054,8 @@ static int tb_disconnect_pci(struct tb *tb, struct tb_switch *sw)
|
|||
if (WARN_ON(!tunnel))
|
||||
return -ENODEV;
|
||||
|
||||
tb_switch_xhci_disconnect(sw);
|
||||
|
||||
tb_tunnel_deactivate(tunnel);
|
||||
list_del(&tunnel->list);
|
||||
tb_tunnel_free(tunnel);
|
||||
|
@ -1099,6 +1101,9 @@ static int tb_tunnel_pci(struct tb *tb, struct tb_switch *sw)
|
|||
if (tb_switch_pcie_l1_enable(sw))
|
||||
tb_sw_warn(sw, "failed to enable PCIe L1 for Titan Ridge\n");
|
||||
|
||||
if (tb_switch_xhci_connect(sw))
|
||||
tb_sw_warn(sw, "failed to connect xHCI\n");
|
||||
|
||||
list_add_tail(&tunnel->list, &tcm->tunnel_list);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1256,12 +1261,18 @@ static void tb_handle_hotplug(struct work_struct *work)
|
|||
tb_port_unconfigure_xdomain(port);
|
||||
} else if (tb_port_is_dpout(port) || tb_port_is_dpin(port)) {
|
||||
tb_dp_resource_unavailable(tb, port);
|
||||
} else if (!port->port) {
|
||||
tb_sw_dbg(sw, "xHCI disconnect request\n");
|
||||
tb_switch_xhci_disconnect(sw);
|
||||
} else {
|
||||
tb_port_dbg(port,
|
||||
"got unplug event for disconnected port, ignoring\n");
|
||||
}
|
||||
} else if (port->remote) {
|
||||
tb_port_dbg(port, "got plug event for connected port, ignoring\n");
|
||||
} else if (!port->port && sw->authorized) {
|
||||
tb_sw_dbg(sw, "xHCI connect request\n");
|
||||
tb_switch_xhci_connect(sw);
|
||||
} else {
|
||||
if (tb_port_is_null(port)) {
|
||||
tb_port_dbg(port, "hotplug: scanning\n");
|
||||
|
|
|
@ -855,6 +855,7 @@ static inline bool tb_switch_is_alpine_ridge(const struct tb_switch *sw)
|
|||
if (sw->config.vendor_id == PCI_VENDOR_ID_INTEL) {
|
||||
switch (sw->config.device_id) {
|
||||
case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_BRIDGE:
|
||||
case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_BRIDGE:
|
||||
case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_BRIDGE:
|
||||
case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_BRIDGE:
|
||||
case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_BRIDGE:
|
||||
|
@ -987,6 +988,9 @@ int tb_switch_mask_clx_objections(struct tb_switch *sw);
|
|||
|
||||
int tb_switch_pcie_l1_enable(struct tb_switch *sw);
|
||||
|
||||
int tb_switch_xhci_connect(struct tb_switch *sw);
|
||||
void tb_switch_xhci_disconnect(struct tb_switch *sw);
|
||||
|
||||
int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged);
|
||||
int tb_port_add_nfc_credits(struct tb_port *port, int credits);
|
||||
int tb_port_clear_counter(struct tb_port *port, int counter);
|
||||
|
@ -1081,6 +1085,10 @@ int tb_lc_configure_xdomain(struct tb_port *port);
|
|||
void tb_lc_unconfigure_xdomain(struct tb_port *port);
|
||||
int tb_lc_start_lane_initialization(struct tb_port *port);
|
||||
bool tb_lc_is_clx_supported(struct tb_port *port);
|
||||
bool tb_lc_is_usb_plugged(struct tb_port *port);
|
||||
bool tb_lc_is_xhci_connected(struct tb_port *port);
|
||||
int tb_lc_xhci_connect(struct tb_port *port);
|
||||
void tb_lc_xhci_disconnect(struct tb_port *port);
|
||||
int tb_lc_set_wake(struct tb_switch *sw, unsigned int flags);
|
||||
int tb_lc_set_sleep(struct tb_switch *sw);
|
||||
bool tb_lc_lane_bonding_possible(struct tb_switch *sw);
|
||||
|
|
|
@ -133,11 +133,11 @@ struct tb_cap_phy {
|
|||
} __packed;
|
||||
|
||||
struct tb_eeprom_ctl {
|
||||
bool clock:1; /* send pulse to transfer one bit */
|
||||
bool access_low:1; /* set to 0 before access */
|
||||
bool data_out:1; /* to eeprom */
|
||||
bool data_in:1; /* from eeprom */
|
||||
bool access_high:1; /* set to 1 before access */
|
||||
bool fl_sk:1; /* send pulse to transfer one bit */
|
||||
bool fl_cs:1; /* set to 0 before access */
|
||||
bool fl_di:1; /* to eeprom */
|
||||
bool fl_do:1; /* from eeprom */
|
||||
bool bit_banging_enable:1; /* set to 1 before access */
|
||||
bool not_present:1; /* should be 0 */
|
||||
bool unknown1:1;
|
||||
bool present:1; /* should be 1 */
|
||||
|
@ -146,14 +146,14 @@ struct tb_eeprom_ctl {
|
|||
|
||||
struct tb_cap_plug_events {
|
||||
struct tb_cap_extended_short cap_header;
|
||||
u32 __unknown1:2;
|
||||
u32 plug_events:5;
|
||||
u32 __unknown2:25;
|
||||
u32 __unknown3;
|
||||
u32 __unknown4;
|
||||
u32 __unknown1:2; /* VSC_CS_1 */
|
||||
u32 plug_events:5; /* VSC_CS_1 */
|
||||
u32 __unknown2:25; /* VSC_CS_1 */
|
||||
u32 vsc_cs_2;
|
||||
u32 vsc_cs_3;
|
||||
struct tb_eeprom_ctl eeprom_ctl;
|
||||
u32 __unknown5[7];
|
||||
u32 drom_offset; /* 32 bit register, but eeprom addresses are 16 bit */
|
||||
u32 __unknown5[7]; /* VSC_CS_5 -> VSC_CS_11 */
|
||||
u32 drom_offset; /* VSC_CS_12: 32 bit register, but eeprom addresses are 16 bit */
|
||||
} __packed;
|
||||
|
||||
/* device headers */
|
||||
|
@ -389,6 +389,7 @@ struct tb_regs_port_header {
|
|||
#define DP_COMMON_CAP_1_LANE 0x0
|
||||
#define DP_COMMON_CAP_2_LANES 0x1
|
||||
#define DP_COMMON_CAP_4_LANES 0x2
|
||||
#define DP_COMMON_CAP_LTTPR_NS BIT(27)
|
||||
#define DP_COMMON_CAP_DPRX_DONE BIT(31)
|
||||
|
||||
/* PCIe adapter registers */
|
||||
|
@ -462,6 +463,12 @@ struct tb_regs_hop {
|
|||
#define TMU_ADP_CS_6_DISABLE_TMU_OBJ_CL2 BIT(3)
|
||||
|
||||
/* Plug Events registers */
|
||||
#define TB_PLUG_EVENTS_USB_DISABLE BIT(2)
|
||||
#define TB_PLUG_EVENTS_CS_1_LANE_DISABLE BIT(3)
|
||||
#define TB_PLUG_EVENTS_CS_1_DPOUT_DISABLE BIT(4)
|
||||
#define TB_PLUG_EVENTS_CS_1_LOW_DPIN_DISABLE BIT(5)
|
||||
#define TB_PLUG_EVENTS_CS_1_HIGH_DPIN_DISABLE BIT(6)
|
||||
|
||||
#define TB_PLUG_EVENTS_PCIE_WR_DATA 0x1b
|
||||
#define TB_PLUG_EVENTS_PCIE_CMD 0x1c
|
||||
#define TB_PLUG_EVENTS_PCIE_CMD_DW_OFFSET_MASK GENMASK(9, 0)
|
||||
|
@ -501,6 +508,9 @@ struct tb_regs_hop {
|
|||
#define TB_LC_POWER 0x740
|
||||
|
||||
/* Link controller registers */
|
||||
#define TB_LC_CS_42 0x2a
|
||||
#define TB_LC_CS_42_USB_PLUGGED BIT(31)
|
||||
|
||||
#define TB_LC_PORT_ATTR 0x8d
|
||||
#define TB_LC_PORT_ATTR_BE BIT(12)
|
||||
|
||||
|
@ -521,4 +531,7 @@ struct tb_regs_hop {
|
|||
#define TB_LC_LINK_ATTR 0x97
|
||||
#define TB_LC_LINK_ATTR_CPS BIT(18)
|
||||
|
||||
#define TB_LC_LINK_REQ 0xad
|
||||
#define TB_LC_LINK_REQ_XHCI_CONNECT BIT(31)
|
||||
|
||||
#endif
|
||||
|
|
|
@ -580,6 +580,16 @@ static int tb_dp_xchg_caps(struct tb_tunnel *tunnel)
|
|||
out_dp_cap = tb_dp_cap_set_lanes(out_dp_cap, new_lanes);
|
||||
}
|
||||
|
||||
/*
|
||||
* Titan Ridge does not disable AUX timers when it gets
|
||||
* SET_CONFIG with SET_LTTPR_MODE set. This causes problems with
|
||||
* DP tunneling.
|
||||
*/
|
||||
if (tb_route(out->sw) && tb_switch_is_titan_ridge(out->sw)) {
|
||||
out_dp_cap |= DP_COMMON_CAP_LTTPR_NS;
|
||||
tb_port_dbg(out, "disabling LTTPR\n");
|
||||
}
|
||||
|
||||
return tb_port_write(in, &out_dp_cap, TB_CFG_PORT,
|
||||
in->cap_adap + DP_REMOTE_CAP, 1);
|
||||
}
|
||||
|
|
|
@ -182,208 +182,211 @@ static inline const char *cdnsp_decode_trb(char *str, size_t size, u32 field0,
|
|||
int ep_id = TRB_TO_EP_INDEX(field3) - 1;
|
||||
int type = TRB_FIELD_TO_TYPE(field3);
|
||||
unsigned int ep_num;
|
||||
int ret = 0;
|
||||
int ret;
|
||||
u32 temp;
|
||||
|
||||
ep_num = DIV_ROUND_UP(ep_id, 2);
|
||||
|
||||
switch (type) {
|
||||
case TRB_LINK:
|
||||
ret += snprintf(str, size,
|
||||
"LINK %08x%08x intr %ld type '%s' flags %c:%c:%c:%c",
|
||||
field1, field0, GET_INTR_TARGET(field2),
|
||||
cdnsp_trb_type_string(type),
|
||||
field3 & TRB_IOC ? 'I' : 'i',
|
||||
field3 & TRB_CHAIN ? 'C' : 'c',
|
||||
field3 & TRB_TC ? 'T' : 't',
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
ret = snprintf(str, size,
|
||||
"LINK %08x%08x intr %ld type '%s' flags %c:%c:%c:%c",
|
||||
field1, field0, GET_INTR_TARGET(field2),
|
||||
cdnsp_trb_type_string(type),
|
||||
field3 & TRB_IOC ? 'I' : 'i',
|
||||
field3 & TRB_CHAIN ? 'C' : 'c',
|
||||
field3 & TRB_TC ? 'T' : 't',
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_TRANSFER:
|
||||
case TRB_COMPLETION:
|
||||
case TRB_PORT_STATUS:
|
||||
case TRB_HC_EVENT:
|
||||
ret += snprintf(str, size,
|
||||
"ep%d%s(%d) type '%s' TRB %08x%08x status '%s'"
|
||||
" len %ld slot %ld flags %c:%c",
|
||||
ep_num, ep_id % 2 ? "out" : "in",
|
||||
TRB_TO_EP_INDEX(field3),
|
||||
cdnsp_trb_type_string(type), field1, field0,
|
||||
cdnsp_trb_comp_code_string(GET_COMP_CODE(field2)),
|
||||
EVENT_TRB_LEN(field2), TRB_TO_SLOT_ID(field3),
|
||||
field3 & EVENT_DATA ? 'E' : 'e',
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
ret = snprintf(str, size,
|
||||
"ep%d%s(%d) type '%s' TRB %08x%08x status '%s'"
|
||||
" len %ld slot %ld flags %c:%c",
|
||||
ep_num, ep_id % 2 ? "out" : "in",
|
||||
TRB_TO_EP_INDEX(field3),
|
||||
cdnsp_trb_type_string(type), field1, field0,
|
||||
cdnsp_trb_comp_code_string(GET_COMP_CODE(field2)),
|
||||
EVENT_TRB_LEN(field2), TRB_TO_SLOT_ID(field3),
|
||||
field3 & EVENT_DATA ? 'E' : 'e',
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_MFINDEX_WRAP:
|
||||
ret += snprintf(str, size, "%s: flags %c",
|
||||
cdnsp_trb_type_string(type),
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
ret = snprintf(str, size, "%s: flags %c",
|
||||
cdnsp_trb_type_string(type),
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_SETUP:
|
||||
ret += snprintf(str, size,
|
||||
"type '%s' bRequestType %02x bRequest %02x "
|
||||
"wValue %02x%02x wIndex %02x%02x wLength %d "
|
||||
"length %ld TD size %ld intr %ld Setup ID %ld "
|
||||
"flags %c:%c:%c",
|
||||
cdnsp_trb_type_string(type),
|
||||
field0 & 0xff,
|
||||
(field0 & 0xff00) >> 8,
|
||||
(field0 & 0xff000000) >> 24,
|
||||
(field0 & 0xff0000) >> 16,
|
||||
(field1 & 0xff00) >> 8,
|
||||
field1 & 0xff,
|
||||
(field1 & 0xff000000) >> 16 |
|
||||
(field1 & 0xff0000) >> 16,
|
||||
TRB_LEN(field2), GET_TD_SIZE(field2),
|
||||
GET_INTR_TARGET(field2),
|
||||
TRB_SETUPID_TO_TYPE(field3),
|
||||
field3 & TRB_IDT ? 'D' : 'd',
|
||||
field3 & TRB_IOC ? 'I' : 'i',
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
ret = snprintf(str, size,
|
||||
"type '%s' bRequestType %02x bRequest %02x "
|
||||
"wValue %02x%02x wIndex %02x%02x wLength %d "
|
||||
"length %ld TD size %ld intr %ld Setup ID %ld "
|
||||
"flags %c:%c:%c",
|
||||
cdnsp_trb_type_string(type),
|
||||
field0 & 0xff,
|
||||
(field0 & 0xff00) >> 8,
|
||||
(field0 & 0xff000000) >> 24,
|
||||
(field0 & 0xff0000) >> 16,
|
||||
(field1 & 0xff00) >> 8,
|
||||
field1 & 0xff,
|
||||
(field1 & 0xff000000) >> 16 |
|
||||
(field1 & 0xff0000) >> 16,
|
||||
TRB_LEN(field2), GET_TD_SIZE(field2),
|
||||
GET_INTR_TARGET(field2),
|
||||
TRB_SETUPID_TO_TYPE(field3),
|
||||
field3 & TRB_IDT ? 'D' : 'd',
|
||||
field3 & TRB_IOC ? 'I' : 'i',
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_DATA:
|
||||
ret += snprintf(str, size,
|
||||
"type '%s' Buffer %08x%08x length %ld TD size %ld "
|
||||
"intr %ld flags %c:%c:%c:%c:%c:%c:%c",
|
||||
cdnsp_trb_type_string(type),
|
||||
field1, field0, TRB_LEN(field2),
|
||||
GET_TD_SIZE(field2),
|
||||
GET_INTR_TARGET(field2),
|
||||
field3 & TRB_IDT ? 'D' : 'i',
|
||||
field3 & TRB_IOC ? 'I' : 'i',
|
||||
field3 & TRB_CHAIN ? 'C' : 'c',
|
||||
field3 & TRB_NO_SNOOP ? 'S' : 's',
|
||||
field3 & TRB_ISP ? 'I' : 'i',
|
||||
field3 & TRB_ENT ? 'E' : 'e',
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
ret = snprintf(str, size,
|
||||
"type '%s' Buffer %08x%08x length %ld TD size %ld "
|
||||
"intr %ld flags %c:%c:%c:%c:%c:%c:%c",
|
||||
cdnsp_trb_type_string(type),
|
||||
field1, field0, TRB_LEN(field2),
|
||||
GET_TD_SIZE(field2),
|
||||
GET_INTR_TARGET(field2),
|
||||
field3 & TRB_IDT ? 'D' : 'i',
|
||||
field3 & TRB_IOC ? 'I' : 'i',
|
||||
field3 & TRB_CHAIN ? 'C' : 'c',
|
||||
field3 & TRB_NO_SNOOP ? 'S' : 's',
|
||||
field3 & TRB_ISP ? 'I' : 'i',
|
||||
field3 & TRB_ENT ? 'E' : 'e',
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_STATUS:
|
||||
ret += snprintf(str, size,
|
||||
"Buffer %08x%08x length %ld TD size %ld intr"
|
||||
"%ld type '%s' flags %c:%c:%c:%c",
|
||||
field1, field0, TRB_LEN(field2),
|
||||
GET_TD_SIZE(field2),
|
||||
GET_INTR_TARGET(field2),
|
||||
cdnsp_trb_type_string(type),
|
||||
field3 & TRB_IOC ? 'I' : 'i',
|
||||
field3 & TRB_CHAIN ? 'C' : 'c',
|
||||
field3 & TRB_ENT ? 'E' : 'e',
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
ret = snprintf(str, size,
|
||||
"Buffer %08x%08x length %ld TD size %ld intr"
|
||||
"%ld type '%s' flags %c:%c:%c:%c",
|
||||
field1, field0, TRB_LEN(field2),
|
||||
GET_TD_SIZE(field2),
|
||||
GET_INTR_TARGET(field2),
|
||||
cdnsp_trb_type_string(type),
|
||||
field3 & TRB_IOC ? 'I' : 'i',
|
||||
field3 & TRB_CHAIN ? 'C' : 'c',
|
||||
field3 & TRB_ENT ? 'E' : 'e',
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_NORMAL:
|
||||
case TRB_ISOC:
|
||||
case TRB_EVENT_DATA:
|
||||
case TRB_TR_NOOP:
|
||||
ret += snprintf(str, size,
|
||||
"type '%s' Buffer %08x%08x length %ld "
|
||||
"TD size %ld intr %ld "
|
||||
"flags %c:%c:%c:%c:%c:%c:%c:%c:%c",
|
||||
cdnsp_trb_type_string(type),
|
||||
field1, field0, TRB_LEN(field2),
|
||||
GET_TD_SIZE(field2),
|
||||
GET_INTR_TARGET(field2),
|
||||
field3 & TRB_BEI ? 'B' : 'b',
|
||||
field3 & TRB_IDT ? 'T' : 't',
|
||||
field3 & TRB_IOC ? 'I' : 'i',
|
||||
field3 & TRB_CHAIN ? 'C' : 'c',
|
||||
field3 & TRB_NO_SNOOP ? 'S' : 's',
|
||||
field3 & TRB_ISP ? 'I' : 'i',
|
||||
field3 & TRB_ENT ? 'E' : 'e',
|
||||
field3 & TRB_CYCLE ? 'C' : 'c',
|
||||
!(field3 & TRB_EVENT_INVALIDATE) ? 'V' : 'v');
|
||||
ret = snprintf(str, size,
|
||||
"type '%s' Buffer %08x%08x length %ld "
|
||||
"TD size %ld intr %ld "
|
||||
"flags %c:%c:%c:%c:%c:%c:%c:%c:%c",
|
||||
cdnsp_trb_type_string(type),
|
||||
field1, field0, TRB_LEN(field2),
|
||||
GET_TD_SIZE(field2),
|
||||
GET_INTR_TARGET(field2),
|
||||
field3 & TRB_BEI ? 'B' : 'b',
|
||||
field3 & TRB_IDT ? 'T' : 't',
|
||||
field3 & TRB_IOC ? 'I' : 'i',
|
||||
field3 & TRB_CHAIN ? 'C' : 'c',
|
||||
field3 & TRB_NO_SNOOP ? 'S' : 's',
|
||||
field3 & TRB_ISP ? 'I' : 'i',
|
||||
field3 & TRB_ENT ? 'E' : 'e',
|
||||
field3 & TRB_CYCLE ? 'C' : 'c',
|
||||
!(field3 & TRB_EVENT_INVALIDATE) ? 'V' : 'v');
|
||||
break;
|
||||
case TRB_CMD_NOOP:
|
||||
case TRB_ENABLE_SLOT:
|
||||
ret += snprintf(str, size, "%s: flags %c",
|
||||
cdnsp_trb_type_string(type),
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
ret = snprintf(str, size, "%s: flags %c",
|
||||
cdnsp_trb_type_string(type),
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_DISABLE_SLOT:
|
||||
ret += snprintf(str, size, "%s: slot %ld flags %c",
|
||||
cdnsp_trb_type_string(type),
|
||||
TRB_TO_SLOT_ID(field3),
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
ret = snprintf(str, size, "%s: slot %ld flags %c",
|
||||
cdnsp_trb_type_string(type),
|
||||
TRB_TO_SLOT_ID(field3),
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_ADDR_DEV:
|
||||
ret += snprintf(str, size,
|
||||
"%s: ctx %08x%08x slot %ld flags %c:%c",
|
||||
cdnsp_trb_type_string(type), field1, field0,
|
||||
TRB_TO_SLOT_ID(field3),
|
||||
field3 & TRB_BSR ? 'B' : 'b',
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
ret = snprintf(str, size,
|
||||
"%s: ctx %08x%08x slot %ld flags %c:%c",
|
||||
cdnsp_trb_type_string(type), field1, field0,
|
||||
TRB_TO_SLOT_ID(field3),
|
||||
field3 & TRB_BSR ? 'B' : 'b',
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_CONFIG_EP:
|
||||
ret += snprintf(str, size,
|
||||
"%s: ctx %08x%08x slot %ld flags %c:%c",
|
||||
cdnsp_trb_type_string(type), field1, field0,
|
||||
TRB_TO_SLOT_ID(field3),
|
||||
field3 & TRB_DC ? 'D' : 'd',
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
ret = snprintf(str, size,
|
||||
"%s: ctx %08x%08x slot %ld flags %c:%c",
|
||||
cdnsp_trb_type_string(type), field1, field0,
|
||||
TRB_TO_SLOT_ID(field3),
|
||||
field3 & TRB_DC ? 'D' : 'd',
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_EVAL_CONTEXT:
|
||||
ret += snprintf(str, size,
|
||||
"%s: ctx %08x%08x slot %ld flags %c",
|
||||
cdnsp_trb_type_string(type), field1, field0,
|
||||
TRB_TO_SLOT_ID(field3),
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
ret = snprintf(str, size,
|
||||
"%s: ctx %08x%08x slot %ld flags %c",
|
||||
cdnsp_trb_type_string(type), field1, field0,
|
||||
TRB_TO_SLOT_ID(field3),
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_RESET_EP:
|
||||
case TRB_HALT_ENDPOINT:
|
||||
case TRB_FLUSH_ENDPOINT:
|
||||
ret += snprintf(str, size,
|
||||
"%s: ep%d%s(%d) ctx %08x%08x slot %ld flags %c",
|
||||
cdnsp_trb_type_string(type),
|
||||
ep_num, ep_id % 2 ? "out" : "in",
|
||||
TRB_TO_EP_INDEX(field3), field1, field0,
|
||||
TRB_TO_SLOT_ID(field3),
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
ret = snprintf(str, size,
|
||||
"%s: ep%d%s(%d) ctx %08x%08x slot %ld flags %c",
|
||||
cdnsp_trb_type_string(type),
|
||||
ep_num, ep_id % 2 ? "out" : "in",
|
||||
TRB_TO_EP_INDEX(field3), field1, field0,
|
||||
TRB_TO_SLOT_ID(field3),
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_STOP_RING:
|
||||
ret += snprintf(str, size,
|
||||
"%s: ep%d%s(%d) slot %ld sp %d flags %c",
|
||||
cdnsp_trb_type_string(type),
|
||||
ep_num, ep_id % 2 ? "out" : "in",
|
||||
TRB_TO_EP_INDEX(field3),
|
||||
TRB_TO_SLOT_ID(field3),
|
||||
TRB_TO_SUSPEND_PORT(field3),
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
ret = snprintf(str, size,
|
||||
"%s: ep%d%s(%d) slot %ld sp %d flags %c",
|
||||
cdnsp_trb_type_string(type),
|
||||
ep_num, ep_id % 2 ? "out" : "in",
|
||||
TRB_TO_EP_INDEX(field3),
|
||||
TRB_TO_SLOT_ID(field3),
|
||||
TRB_TO_SUSPEND_PORT(field3),
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_SET_DEQ:
|
||||
ret += snprintf(str, size,
|
||||
"%s: ep%d%s(%d) deq %08x%08x stream %ld slot %ld flags %c",
|
||||
cdnsp_trb_type_string(type),
|
||||
ep_num, ep_id % 2 ? "out" : "in",
|
||||
TRB_TO_EP_INDEX(field3), field1, field0,
|
||||
TRB_TO_STREAM_ID(field2),
|
||||
TRB_TO_SLOT_ID(field3),
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
ret = snprintf(str, size,
|
||||
"%s: ep%d%s(%d) deq %08x%08x stream %ld slot %ld flags %c",
|
||||
cdnsp_trb_type_string(type),
|
||||
ep_num, ep_id % 2 ? "out" : "in",
|
||||
TRB_TO_EP_INDEX(field3), field1, field0,
|
||||
TRB_TO_STREAM_ID(field2),
|
||||
TRB_TO_SLOT_ID(field3),
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_RESET_DEV:
|
||||
ret += snprintf(str, size, "%s: slot %ld flags %c",
|
||||
cdnsp_trb_type_string(type),
|
||||
TRB_TO_SLOT_ID(field3),
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
ret = snprintf(str, size, "%s: slot %ld flags %c",
|
||||
cdnsp_trb_type_string(type),
|
||||
TRB_TO_SLOT_ID(field3),
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_ENDPOINT_NRDY:
|
||||
temp = TRB_TO_HOST_STREAM(field2);
|
||||
temp = TRB_TO_HOST_STREAM(field2);
|
||||
|
||||
ret += snprintf(str, size,
|
||||
"%s: ep%d%s(%d) H_SID %x%s%s D_SID %lx flags %c:%c",
|
||||
cdnsp_trb_type_string(type),
|
||||
ep_num, ep_id % 2 ? "out" : "in",
|
||||
TRB_TO_EP_INDEX(field3), temp,
|
||||
temp == STREAM_PRIME_ACK ? "(PRIME)" : "",
|
||||
temp == STREAM_REJECTED ? "(REJECTED)" : "",
|
||||
TRB_TO_DEV_STREAM(field0),
|
||||
field3 & TRB_STAT ? 'S' : 's',
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
ret = snprintf(str, size,
|
||||
"%s: ep%d%s(%d) H_SID %x%s%s D_SID %lx flags %c:%c",
|
||||
cdnsp_trb_type_string(type),
|
||||
ep_num, ep_id % 2 ? "out" : "in",
|
||||
TRB_TO_EP_INDEX(field3), temp,
|
||||
temp == STREAM_PRIME_ACK ? "(PRIME)" : "",
|
||||
temp == STREAM_REJECTED ? "(REJECTED)" : "",
|
||||
TRB_TO_DEV_STREAM(field0),
|
||||
field3 & TRB_STAT ? 'S' : 's',
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
default:
|
||||
ret += snprintf(str, size,
|
||||
"type '%s' -> raw %08x %08x %08x %08x",
|
||||
cdnsp_trb_type_string(type),
|
||||
field0, field1, field2, field3);
|
||||
ret = snprintf(str, size,
|
||||
"type '%s' -> raw %08x %08x %08x %08x",
|
||||
cdnsp_trb_type_string(type),
|
||||
field0, field1, field2, field3);
|
||||
}
|
||||
|
||||
if (ret >= size)
|
||||
pr_info("CDNSP: buffer overflowed.\n");
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
|
|
|
@ -1243,12 +1243,9 @@ static int cdnsp_run(struct cdnsp_device *pdev,
|
|||
enum usb_device_speed speed)
|
||||
{
|
||||
u32 fs_speed = 0;
|
||||
u64 temp_64;
|
||||
u32 temp;
|
||||
int ret;
|
||||
|
||||
temp_64 = cdnsp_read_64(&pdev->ir_set->erst_dequeue);
|
||||
temp_64 &= ~ERST_PTR_MASK;
|
||||
temp = readl(&pdev->ir_set->irq_control);
|
||||
temp &= ~IMOD_INTERVAL_MASK;
|
||||
temp |= ((IMOD_DEFAULT_INTERVAL / 250) & IMOD_INTERVAL_MASK);
|
||||
|
|
|
@ -2152,7 +2152,7 @@ static void udc_id_switch_for_host(struct ci_hdrc *ci)
|
|||
{
|
||||
/*
|
||||
* host doesn't care B_SESSION_VALID event
|
||||
* so clear and disbale BSV irq
|
||||
* so clear and disable BSV irq
|
||||
*/
|
||||
if (ci->is_otg)
|
||||
hw_write_otgsc(ci, OTGSC_BSVIE | OTGSC_BSVIS, OTGSC_BSVIS);
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/clk/clk-conf.h>
|
||||
|
@ -232,9 +233,64 @@ err:
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ulpi_regs_read(struct seq_file *seq, void *data)
|
||||
{
|
||||
struct ulpi *ulpi = seq->private;
|
||||
|
||||
#define ulpi_print(name, reg) do { \
|
||||
int ret = ulpi_read(ulpi, reg); \
|
||||
if (ret < 0) \
|
||||
return ret; \
|
||||
seq_printf(seq, name " %.02x\n", ret); \
|
||||
} while (0)
|
||||
|
||||
ulpi_print("Vendor ID Low ", ULPI_VENDOR_ID_LOW);
|
||||
ulpi_print("Vendor ID High ", ULPI_VENDOR_ID_HIGH);
|
||||
ulpi_print("Product ID Low ", ULPI_PRODUCT_ID_LOW);
|
||||
ulpi_print("Product ID High ", ULPI_PRODUCT_ID_HIGH);
|
||||
ulpi_print("Function Control ", ULPI_FUNC_CTRL);
|
||||
ulpi_print("Interface Control ", ULPI_IFC_CTRL);
|
||||
ulpi_print("OTG Control ", ULPI_OTG_CTRL);
|
||||
ulpi_print("USB Interrupt Enable Rising ", ULPI_USB_INT_EN_RISE);
|
||||
ulpi_print("USB Interrupt Enable Falling", ULPI_USB_INT_EN_FALL);
|
||||
ulpi_print("USB Interrupt Status ", ULPI_USB_INT_STS);
|
||||
ulpi_print("USB Interrupt Latch ", ULPI_USB_INT_LATCH);
|
||||
ulpi_print("Debug ", ULPI_DEBUG);
|
||||
ulpi_print("Scratch Register ", ULPI_SCRATCH);
|
||||
ulpi_print("Carkit Control ", ULPI_CARKIT_CTRL);
|
||||
ulpi_print("Carkit Interrupt Delay ", ULPI_CARKIT_INT_DELAY);
|
||||
ulpi_print("Carkit Interrupt Enable ", ULPI_CARKIT_INT_EN);
|
||||
ulpi_print("Carkit Interrupt Status ", ULPI_CARKIT_INT_STS);
|
||||
ulpi_print("Carkit Interrupt Latch ", ULPI_CARKIT_INT_LATCH);
|
||||
ulpi_print("Carkit Pulse Control ", ULPI_CARKIT_PLS_CTRL);
|
||||
ulpi_print("Transmit Positive Width ", ULPI_TX_POS_WIDTH);
|
||||
ulpi_print("Transmit Negative Width ", ULPI_TX_NEG_WIDTH);
|
||||
ulpi_print("Receive Polarity Recovery ", ULPI_POLARITY_RECOVERY);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ulpi_regs_open(struct inode *inode, struct file *f)
|
||||
{
|
||||
struct ulpi *ulpi = inode->i_private;
|
||||
|
||||
return single_open(f, ulpi_regs_read, ulpi);
|
||||
}
|
||||
|
||||
static const struct file_operations ulpi_regs_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = ulpi_regs_open,
|
||||
.release = single_release,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek
|
||||
};
|
||||
|
||||
#define ULPI_ROOT debugfs_lookup(KBUILD_MODNAME, NULL)
|
||||
|
||||
static int ulpi_register(struct device *dev, struct ulpi *ulpi)
|
||||
{
|
||||
int ret;
|
||||
struct dentry *root;
|
||||
|
||||
ulpi->dev.parent = dev; /* needed early for ops */
|
||||
ulpi->dev.bus = &ulpi_bus;
|
||||
|
@ -259,6 +315,9 @@ static int ulpi_register(struct device *dev, struct ulpi *ulpi)
|
|||
return ret;
|
||||
}
|
||||
|
||||
root = debugfs_create_dir(dev_name(dev), ULPI_ROOT);
|
||||
debugfs_create_file("regs", 0444, root, ulpi, &ulpi_regs_ops);
|
||||
|
||||
dev_dbg(&ulpi->dev, "registered ULPI PHY: vendor %04x, product %04x\n",
|
||||
ulpi->id.vendor, ulpi->id.product);
|
||||
|
||||
|
@ -304,6 +363,8 @@ EXPORT_SYMBOL_GPL(ulpi_register_interface);
|
|||
*/
|
||||
void ulpi_unregister_interface(struct ulpi *ulpi)
|
||||
{
|
||||
debugfs_remove_recursive(debugfs_lookup(dev_name(&ulpi->dev),
|
||||
ULPI_ROOT));
|
||||
device_unregister(&ulpi->dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ulpi_unregister_interface);
|
||||
|
@ -312,13 +373,21 @@ EXPORT_SYMBOL_GPL(ulpi_unregister_interface);
|
|||
|
||||
static int __init ulpi_init(void)
|
||||
{
|
||||
return bus_register(&ulpi_bus);
|
||||
int ret;
|
||||
struct dentry *root;
|
||||
|
||||
root = debugfs_create_dir(KBUILD_MODNAME, NULL);
|
||||
ret = bus_register(&ulpi_bus);
|
||||
if (ret)
|
||||
debugfs_remove(root);
|
||||
return ret;
|
||||
}
|
||||
subsys_initcall(ulpi_init);
|
||||
|
||||
static void __exit ulpi_exit(void)
|
||||
{
|
||||
bus_unregister(&ulpi_bus);
|
||||
debugfs_remove_recursive(ULPI_ROOT);
|
||||
}
|
||||
module_exit(ulpi_exit);
|
||||
|
||||
|
|
|
@ -175,7 +175,6 @@ static int usb_conn_probe(struct platform_device *pdev)
|
|||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct usb_conn_info *info;
|
||||
bool need_vbus = true;
|
||||
int ret = 0;
|
||||
|
||||
info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
|
||||
|
@ -205,22 +204,9 @@ static int usb_conn_probe(struct platform_device *pdev)
|
|||
|
||||
INIT_DELAYED_WORK(&info->dw_det, usb_conn_detect_cable);
|
||||
|
||||
/*
|
||||
* If the USB connector is a child of a USB port and that port already provides the VBUS
|
||||
* supply, there's no need for the USB connector to provide it again.
|
||||
*/
|
||||
if (dev->parent && dev->parent->of_node) {
|
||||
if (of_find_property(dev->parent->of_node, "vbus-supply", NULL))
|
||||
need_vbus = false;
|
||||
}
|
||||
|
||||
if (!need_vbus) {
|
||||
info->vbus = devm_regulator_get_optional(dev, "vbus");
|
||||
if (PTR_ERR(info->vbus) == -ENODEV)
|
||||
info->vbus = NULL;
|
||||
} else {
|
||||
info->vbus = devm_regulator_get(dev, "vbus");
|
||||
}
|
||||
info->vbus = devm_regulator_get_optional(dev, "vbus");
|
||||
if (PTR_ERR(info->vbus) == -ENODEV)
|
||||
info->vbus = NULL;
|
||||
|
||||
if (IS_ERR(info->vbus)) {
|
||||
ret = PTR_ERR(info->vbus);
|
||||
|
|
|
@ -139,30 +139,42 @@ MODULE_PARM_DESC(usbfs_memory_mb,
|
|||
/* Hard limit, necessary to avoid arithmetic overflow */
|
||||
#define USBFS_XFER_MAX (UINT_MAX / 2 - 1000000)
|
||||
|
||||
static atomic64_t usbfs_memory_usage; /* Total memory currently allocated */
|
||||
static DEFINE_SPINLOCK(usbfs_memory_usage_lock);
|
||||
static u64 usbfs_memory_usage; /* Total memory currently allocated */
|
||||
|
||||
/* Check whether it's okay to allocate more memory for a transfer */
|
||||
static int usbfs_increase_memory_usage(u64 amount)
|
||||
{
|
||||
u64 lim;
|
||||
u64 lim, total_mem;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
lim = READ_ONCE(usbfs_memory_mb);
|
||||
lim <<= 20;
|
||||
|
||||
atomic64_add(amount, &usbfs_memory_usage);
|
||||
ret = 0;
|
||||
spin_lock_irqsave(&usbfs_memory_usage_lock, flags);
|
||||
total_mem = usbfs_memory_usage + amount;
|
||||
if (lim > 0 && total_mem > lim)
|
||||
ret = -ENOMEM;
|
||||
else
|
||||
usbfs_memory_usage = total_mem;
|
||||
spin_unlock_irqrestore(&usbfs_memory_usage_lock, flags);
|
||||
|
||||
if (lim > 0 && atomic64_read(&usbfs_memory_usage) > lim) {
|
||||
atomic64_sub(amount, &usbfs_memory_usage);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Memory for a transfer is being deallocated */
|
||||
static void usbfs_decrease_memory_usage(u64 amount)
|
||||
{
|
||||
atomic64_sub(amount, &usbfs_memory_usage);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&usbfs_memory_usage_lock, flags);
|
||||
if (amount > usbfs_memory_usage)
|
||||
usbfs_memory_usage = 0;
|
||||
else
|
||||
usbfs_memory_usage -= amount;
|
||||
spin_unlock_irqrestore(&usbfs_memory_usage_lock, flags);
|
||||
}
|
||||
|
||||
static int connected(struct usb_dev_state *ps)
|
||||
|
|
|
@ -248,7 +248,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id,
|
|||
hcd->rsrc_len, driver->description))
|
||||
break;
|
||||
}
|
||||
if (region == PCI_ROM_RESOURCE) {
|
||||
if (region == PCI_STD_NUM_BARS) {
|
||||
dev_dbg(&dev->dev, "no i/o regions available\n");
|
||||
retval = -EBUSY;
|
||||
goto put_hcd;
|
||||
|
|
|
@ -2983,8 +2983,12 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
|
|||
status);
|
||||
}
|
||||
|
||||
/* Check for disconnect or reset */
|
||||
if (status == 0 || status == -ENOTCONN || status == -ENODEV) {
|
||||
/*
|
||||
* Check for disconnect or reset, and bail out after several
|
||||
* reset attempts to avoid warm reset loop.
|
||||
*/
|
||||
if (status == 0 || status == -ENOTCONN || status == -ENODEV ||
|
||||
(status == -EBUSY && i == PORT_RESET_TRIES - 1)) {
|
||||
usb_clear_port_feature(hub->hdev, port1,
|
||||
USB_PORT_FEAT_C_RESET);
|
||||
|
||||
|
@ -5005,6 +5009,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
retval = usb_get_bos_descriptor(udev);
|
||||
if (!retval) {
|
||||
udev->lpm_capable = usb_device_supports_lpm(udev);
|
||||
udev->lpm_disable_count = 1;
|
||||
usb_set_lpm_parameters(udev);
|
||||
}
|
||||
}
|
||||
|
@ -5928,16 +5933,6 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
|
|||
*/
|
||||
usb_disable_usb2_hardware_lpm(udev);
|
||||
|
||||
/* Disable LPM while we reset the device and reinstall the alt settings.
|
||||
* Device-initiated LPM, and system exit latency settings are cleared
|
||||
* when the device is reset, so we have to set them up again.
|
||||
*/
|
||||
ret = usb_unlocked_disable_lpm(udev);
|
||||
if (ret) {
|
||||
dev_err(&udev->dev, "%s Failed to disable LPM\n", __func__);
|
||||
goto re_enumerate_no_bos;
|
||||
}
|
||||
|
||||
bos = udev->bos;
|
||||
udev->bos = NULL;
|
||||
|
||||
|
@ -6042,8 +6037,6 @@ done:
|
|||
re_enumerate:
|
||||
usb_release_bos_descriptor(udev);
|
||||
udev->bos = bos;
|
||||
re_enumerate_no_bos:
|
||||
/* LPM state doesn't matter when we're about to destroy the device. */
|
||||
hub_port_logical_disconnect(parent_hub, port1);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
|
|
@ -166,7 +166,7 @@ usb_acpi_get_companion_for_port(struct usb_port *port_dev)
|
|||
if (!parent_handle)
|
||||
return NULL;
|
||||
|
||||
acpi_bus_get_device(parent_handle, &adev);
|
||||
adev = acpi_fetch_acpi_dev(parent_handle);
|
||||
port1 = port_dev->portnum;
|
||||
}
|
||||
|
||||
|
|
|
@ -688,6 +688,10 @@ EXPORT_SYMBOL_GPL(usb_alloc_dev);
|
|||
* Drivers for USB interfaces should normally record such references in
|
||||
* their probe() methods, when they bind to an interface, and release
|
||||
* them by calling usb_put_dev(), in their disconnect() methods.
|
||||
* However, if a driver does not access the usb_device structure after
|
||||
* its disconnect() method returns then refcounting is not necessary,
|
||||
* because the USB core guarantees that a usb_device will not be
|
||||
* deallocated until after all of its interface drivers have been unbound.
|
||||
*
|
||||
* Return: A pointer to the device with the incremented reference counter.
|
||||
*/
|
||||
|
@ -722,6 +726,10 @@ EXPORT_SYMBOL_GPL(usb_put_dev);
|
|||
* Drivers for USB interfaces should normally record such references in
|
||||
* their probe() methods, when they bind to an interface, and release
|
||||
* them by calling usb_put_intf(), in their disconnect() methods.
|
||||
* However, if a driver does not access the usb_interface structure after
|
||||
* its disconnect() method returns then refcounting is not necessary,
|
||||
* because the USB core guarantees that a usb_interface will not be
|
||||
* deallocated until after its driver has been unbound.
|
||||
*
|
||||
* Return: A pointer to the interface with the incremented reference counter.
|
||||
*/
|
||||
|
|
|
@ -82,6 +82,14 @@ static void dwc2_set_s3c6400_params(struct dwc2_hsotg *hsotg)
|
|||
p->phy_utmi_width = 8;
|
||||
}
|
||||
|
||||
static void dwc2_set_socfpga_agilex_params(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dwc2_core_params *p = &hsotg->params;
|
||||
|
||||
p->power_down = DWC2_POWER_DOWN_PARAM_NONE;
|
||||
p->no_clock_gating = true;
|
||||
}
|
||||
|
||||
static void dwc2_set_rk_params(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dwc2_core_params *p = &hsotg->params;
|
||||
|
@ -239,6 +247,8 @@ const struct of_device_id dwc2_of_match_table[] = {
|
|||
.data = dwc2_set_stm32mp15_fsotg_params },
|
||||
{ .compatible = "st,stm32mp15-hsotg",
|
||||
.data = dwc2_set_stm32mp15_hsotg_params },
|
||||
{ .compatible = "intel,socfpga-agilex-hsotg",
|
||||
.data = dwc2_set_socfpga_agilex_params },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dwc2_of_match_table);
|
||||
|
|
|
@ -115,8 +115,6 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
|
|||
dwc->current_dr_role = mode;
|
||||
}
|
||||
|
||||
static int dwc3_core_soft_reset(struct dwc3 *dwc);
|
||||
|
||||
static void __dwc3_set_mode(struct work_struct *work)
|
||||
{
|
||||
struct dwc3 *dwc = work_to_dwc(work);
|
||||
|
@ -261,7 +259,7 @@ u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type)
|
|||
* dwc3_core_soft_reset - Issues core soft reset and PHY reset
|
||||
* @dwc: pointer to our context structure
|
||||
*/
|
||||
static int dwc3_core_soft_reset(struct dwc3 *dwc)
|
||||
int dwc3_core_soft_reset(struct dwc3 *dwc)
|
||||
{
|
||||
u32 reg;
|
||||
int retries = 1000;
|
||||
|
@ -347,17 +345,64 @@ static void dwc3_frame_length_adjustment(struct dwc3 *dwc)
|
|||
*/
|
||||
static void dwc3_ref_clk_period(struct dwc3 *dwc)
|
||||
{
|
||||
unsigned long period;
|
||||
unsigned long fladj;
|
||||
unsigned long decr;
|
||||
unsigned long rate;
|
||||
u32 reg;
|
||||
|
||||
if (dwc->ref_clk_per == 0)
|
||||
if (dwc->ref_clk) {
|
||||
rate = clk_get_rate(dwc->ref_clk);
|
||||
if (!rate)
|
||||
return;
|
||||
period = NSEC_PER_SEC / rate;
|
||||
} else if (dwc->ref_clk_per) {
|
||||
period = dwc->ref_clk_per;
|
||||
rate = NSEC_PER_SEC / period;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUCTL);
|
||||
reg &= ~DWC3_GUCTL_REFCLKPER_MASK;
|
||||
reg |= FIELD_PREP(DWC3_GUCTL_REFCLKPER_MASK, dwc->ref_clk_per);
|
||||
reg |= FIELD_PREP(DWC3_GUCTL_REFCLKPER_MASK, period);
|
||||
dwc3_writel(dwc->regs, DWC3_GUCTL, reg);
|
||||
}
|
||||
|
||||
if (DWC3_VER_IS_PRIOR(DWC3, 250A))
|
||||
return;
|
||||
|
||||
/*
|
||||
* The calculation below is
|
||||
*
|
||||
* 125000 * (NSEC_PER_SEC / (rate * period) - 1)
|
||||
*
|
||||
* but rearranged for fixed-point arithmetic. The division must be
|
||||
* 64-bit because 125000 * NSEC_PER_SEC doesn't fit in 32 bits (and
|
||||
* neither does rate * period).
|
||||
*
|
||||
* Note that rate * period ~= NSEC_PER_SECOND, minus the number of
|
||||
* nanoseconds of error caused by the truncation which happened during
|
||||
* the division when calculating rate or period (whichever one was
|
||||
* derived from the other). We first calculate the relative error, then
|
||||
* scale it to units of 8 ppm.
|
||||
*/
|
||||
fladj = div64_u64(125000ULL * NSEC_PER_SEC, (u64)rate * period);
|
||||
fladj -= 125000;
|
||||
|
||||
/*
|
||||
* The documented 240MHz constant is scaled by 2 to get PLS1 as well.
|
||||
*/
|
||||
decr = 480000000 / rate;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GFLADJ);
|
||||
reg &= ~DWC3_GFLADJ_REFCLK_FLADJ_MASK
|
||||
& ~DWC3_GFLADJ_240MHZDECR
|
||||
& ~DWC3_GFLADJ_240MHZDECR_PLS1;
|
||||
reg |= FIELD_PREP(DWC3_GFLADJ_REFCLK_FLADJ_MASK, fladj)
|
||||
| FIELD_PREP(DWC3_GFLADJ_240MHZDECR, decr >> 1)
|
||||
| FIELD_PREP(DWC3_GFLADJ_240MHZDECR_PLS1, decr & 1);
|
||||
dwc3_writel(dwc->regs, DWC3_GFLADJ, reg);
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_free_one_event_buffer - Frees one event buffer
|
||||
|
@ -745,6 +790,38 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_clk_enable(struct dwc3 *dwc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(dwc->bus_clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(dwc->ref_clk);
|
||||
if (ret)
|
||||
goto disable_bus_clk;
|
||||
|
||||
ret = clk_prepare_enable(dwc->susp_clk);
|
||||
if (ret)
|
||||
goto disable_ref_clk;
|
||||
|
||||
return 0;
|
||||
|
||||
disable_ref_clk:
|
||||
clk_disable_unprepare(dwc->ref_clk);
|
||||
disable_bus_clk:
|
||||
clk_disable_unprepare(dwc->bus_clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void dwc3_clk_disable(struct dwc3 *dwc)
|
||||
{
|
||||
clk_disable_unprepare(dwc->susp_clk);
|
||||
clk_disable_unprepare(dwc->ref_clk);
|
||||
clk_disable_unprepare(dwc->bus_clk);
|
||||
}
|
||||
|
||||
static void dwc3_core_exit(struct dwc3 *dwc)
|
||||
{
|
||||
dwc3_event_buffers_cleanup(dwc);
|
||||
|
@ -758,7 +835,7 @@ static void dwc3_core_exit(struct dwc3 *dwc)
|
|||
usb_phy_set_suspend(dwc->usb3_phy, 1);
|
||||
phy_power_off(dwc->usb2_generic_phy);
|
||||
phy_power_off(dwc->usb3_generic_phy);
|
||||
clk_bulk_disable_unprepare(dwc->num_clks, dwc->clks);
|
||||
dwc3_clk_disable(dwc);
|
||||
reset_control_assert(dwc->reset);
|
||||
}
|
||||
|
||||
|
@ -1088,6 +1165,11 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
|||
if (dwc->parkmode_disable_ss_quirk)
|
||||
reg |= DWC3_GUCTL1_PARKMODE_DISABLE_SS;
|
||||
|
||||
if (DWC3_VER_IS_WITHIN(DWC3, 290A, ANY) &&
|
||||
(dwc->maximum_speed == USB_SPEED_HIGH ||
|
||||
dwc->maximum_speed == USB_SPEED_FULL))
|
||||
reg |= DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
|
||||
}
|
||||
|
||||
|
@ -1605,25 +1687,31 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(dwc->reset);
|
||||
|
||||
if (dev->of_node) {
|
||||
ret = devm_clk_bulk_get_all(dev, &dwc->clks);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
return ret;
|
||||
/*
|
||||
* Clocks are optional, but new DT platforms should support all
|
||||
* clocks as required by the DT-binding.
|
||||
*/
|
||||
if (ret < 0)
|
||||
dwc->num_clks = 0;
|
||||
else
|
||||
dwc->num_clks = ret;
|
||||
dwc->bus_clk = devm_clk_get_optional(dev, "bus_early");
|
||||
if (IS_ERR(dwc->bus_clk))
|
||||
return dev_err_probe(dev, PTR_ERR(dwc->bus_clk),
|
||||
"could not get bus clock\n");
|
||||
|
||||
dwc->ref_clk = devm_clk_get_optional(dev, "ref");
|
||||
if (IS_ERR(dwc->ref_clk))
|
||||
return dev_err_probe(dev, PTR_ERR(dwc->ref_clk),
|
||||
"could not get ref clock\n");
|
||||
|
||||
dwc->susp_clk = devm_clk_get_optional(dev, "suspend");
|
||||
if (IS_ERR(dwc->susp_clk))
|
||||
return dev_err_probe(dev, PTR_ERR(dwc->susp_clk),
|
||||
"could not get suspend clock\n");
|
||||
}
|
||||
|
||||
ret = reset_control_deassert(dwc->reset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_bulk_prepare_enable(dwc->num_clks, dwc->clks);
|
||||
ret = dwc3_clk_enable(dwc);
|
||||
if (ret)
|
||||
goto assert_reset;
|
||||
|
||||
|
@ -1711,7 +1799,7 @@ err1:
|
|||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
disable_clks:
|
||||
clk_bulk_disable_unprepare(dwc->num_clks, dwc->clks);
|
||||
dwc3_clk_disable(dwc);
|
||||
assert_reset:
|
||||
reset_control_assert(dwc->reset);
|
||||
|
||||
|
@ -1755,7 +1843,7 @@ static int dwc3_core_init_for_resume(struct dwc3 *dwc)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_bulk_prepare_enable(dwc->num_clks, dwc->clks);
|
||||
ret = dwc3_clk_enable(dwc);
|
||||
if (ret)
|
||||
goto assert_reset;
|
||||
|
||||
|
@ -1766,7 +1854,7 @@ static int dwc3_core_init_for_resume(struct dwc3 *dwc)
|
|||
return 0;
|
||||
|
||||
disable_clks:
|
||||
clk_bulk_disable_unprepare(dwc->num_clks, dwc->clks);
|
||||
dwc3_clk_disable(dwc);
|
||||
assert_reset:
|
||||
reset_control_assert(dwc->reset);
|
||||
|
||||
|
|
|
@ -259,6 +259,7 @@
|
|||
/* Global User Control 1 Register */
|
||||
#define DWC3_GUCTL1_DEV_DECOUPLE_L1L2_EVT BIT(31)
|
||||
#define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28)
|
||||
#define DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK BIT(26)
|
||||
#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24)
|
||||
#define DWC3_GUCTL1_PARKMODE_DISABLE_SS BIT(17)
|
||||
|
||||
|
@ -388,6 +389,9 @@
|
|||
/* Global Frame Length Adjustment Register */
|
||||
#define DWC3_GFLADJ_30MHZ_SDBND_SEL BIT(7)
|
||||
#define DWC3_GFLADJ_30MHZ_MASK 0x3f
|
||||
#define DWC3_GFLADJ_REFCLK_FLADJ_MASK GENMASK(21, 8)
|
||||
#define DWC3_GFLADJ_240MHZDECR GENMASK(30, 24)
|
||||
#define DWC3_GFLADJ_240MHZDECR_PLS1 BIT(31)
|
||||
|
||||
/* Global User Control Register*/
|
||||
#define DWC3_GUCTL_REFCLKPER_MASK 0xffc00000
|
||||
|
@ -733,6 +737,7 @@ struct dwc3_ep {
|
|||
#define DWC3_EP_FIRST_STREAM_PRIMED BIT(10)
|
||||
#define DWC3_EP_PENDING_CLEAR_STALL BIT(11)
|
||||
#define DWC3_EP_TXFIFO_RESIZED BIT(12)
|
||||
#define DWC3_EP_DELAY_STOP BIT(13)
|
||||
|
||||
/* This last one is specific to EP0 */
|
||||
#define DWC3_EP0_DIR_IN BIT(31)
|
||||
|
@ -978,8 +983,9 @@ struct dwc3_scratchpad_array {
|
|||
* @eps: endpoint array
|
||||
* @gadget: device side representation of the peripheral controller
|
||||
* @gadget_driver: pointer to the gadget driver
|
||||
* @clks: array of clocks
|
||||
* @num_clks: number of clocks
|
||||
* @bus_clk: clock for accessing the registers
|
||||
* @ref_clk: reference clock
|
||||
* @susp_clk: clock used when the SS phy is in low power (S3) state
|
||||
* @reset: reset control
|
||||
* @regs: base address for our registers
|
||||
* @regs_size: address space size
|
||||
|
@ -1134,8 +1140,9 @@ struct dwc3 {
|
|||
struct usb_gadget *gadget;
|
||||
struct usb_gadget_driver *gadget_driver;
|
||||
|
||||
struct clk_bulk_data *clks;
|
||||
int num_clks;
|
||||
struct clk *bus_clk;
|
||||
struct clk *ref_clk;
|
||||
struct clk *susp_clk;
|
||||
|
||||
struct reset_control *reset;
|
||||
|
||||
|
@ -1525,6 +1532,8 @@ bool dwc3_has_imod(struct dwc3 *dwc);
|
|||
int dwc3_event_buffers_setup(struct dwc3 *dwc);
|
||||
void dwc3_event_buffers_cleanup(struct dwc3 *dwc);
|
||||
|
||||
int dwc3_core_soft_reset(struct dwc3 *dwc);
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
|
||||
int dwc3_host_init(struct dwc3 *dwc);
|
||||
void dwc3_host_exit(struct dwc3 *dwc);
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include <linux/extcon.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
|
||||
|
@ -559,6 +560,18 @@ static int dwc3_setup_role_switch(struct dwc3 *dwc)
|
|||
if (IS_ERR(dwc->role_sw))
|
||||
return PTR_ERR(dwc->role_sw);
|
||||
|
||||
if (dwc->dev->of_node) {
|
||||
/* populate connector entry */
|
||||
int ret = devm_of_platform_populate(dwc->dev);
|
||||
|
||||
if (ret) {
|
||||
usb_role_switch_unregister(dwc->role_sw);
|
||||
dwc->role_sw = NULL;
|
||||
dev_err(dwc->dev, "DWC3 platform devices creation failed: %i\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
dwc3_set_mode(dwc, mode);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -36,9 +36,21 @@
|
|||
|
||||
#define USB_WAKEUP_EN_MASK GENMASK(5, 0)
|
||||
|
||||
/* USB glue registers */
|
||||
#define USB_CTRL0 0x00
|
||||
#define USB_CTRL1 0x04
|
||||
|
||||
#define USB_CTRL0_PORTPWR_EN BIT(12) /* 1 - PPC enabled (default) */
|
||||
#define USB_CTRL0_USB3_FIXED BIT(22) /* 1 - USB3 permanent attached */
|
||||
#define USB_CTRL0_USB2_FIXED BIT(23) /* 1 - USB2 permanent attached */
|
||||
|
||||
#define USB_CTRL1_OC_POLARITY BIT(16) /* 0 - HIGH / 1 - LOW */
|
||||
#define USB_CTRL1_PWR_POLARITY BIT(17) /* 0 - HIGH / 1 - LOW */
|
||||
|
||||
struct dwc3_imx8mp {
|
||||
struct device *dev;
|
||||
struct platform_device *dwc3;
|
||||
void __iomem *hsio_blk_base;
|
||||
void __iomem *glue_base;
|
||||
struct clk *hsio_clk;
|
||||
struct clk *suspend_clk;
|
||||
|
@ -47,6 +59,42 @@ struct dwc3_imx8mp {
|
|||
bool wakeup_pending;
|
||||
};
|
||||
|
||||
static void imx8mp_configure_glue(struct dwc3_imx8mp *dwc3_imx)
|
||||
{
|
||||
struct device *dev = dwc3_imx->dev;
|
||||
u32 value;
|
||||
|
||||
if (!dwc3_imx->glue_base)
|
||||
return;
|
||||
|
||||
value = readl(dwc3_imx->glue_base + USB_CTRL0);
|
||||
|
||||
if (device_property_read_bool(dev, "fsl,permanently-attached"))
|
||||
value |= (USB_CTRL0_USB2_FIXED | USB_CTRL0_USB3_FIXED);
|
||||
else
|
||||
value &= ~(USB_CTRL0_USB2_FIXED | USB_CTRL0_USB3_FIXED);
|
||||
|
||||
if (device_property_read_bool(dev, "fsl,disable-port-power-control"))
|
||||
value &= ~(USB_CTRL0_PORTPWR_EN);
|
||||
else
|
||||
value |= USB_CTRL0_PORTPWR_EN;
|
||||
|
||||
writel(value, dwc3_imx->glue_base + USB_CTRL0);
|
||||
|
||||
value = readl(dwc3_imx->glue_base + USB_CTRL1);
|
||||
if (device_property_read_bool(dev, "fsl,over-current-active-low"))
|
||||
value |= USB_CTRL1_OC_POLARITY;
|
||||
else
|
||||
value &= ~USB_CTRL1_OC_POLARITY;
|
||||
|
||||
if (device_property_read_bool(dev, "fsl,power-active-low"))
|
||||
value |= USB_CTRL1_PWR_POLARITY;
|
||||
else
|
||||
value &= ~USB_CTRL1_PWR_POLARITY;
|
||||
|
||||
writel(value, dwc3_imx->glue_base + USB_CTRL1);
|
||||
}
|
||||
|
||||
static void dwc3_imx8mp_wakeup_enable(struct dwc3_imx8mp *dwc3_imx)
|
||||
{
|
||||
struct dwc3 *dwc3 = platform_get_drvdata(dwc3_imx->dwc3);
|
||||
|
@ -55,7 +103,7 @@ static void dwc3_imx8mp_wakeup_enable(struct dwc3_imx8mp *dwc3_imx)
|
|||
if (!dwc3)
|
||||
return;
|
||||
|
||||
val = readl(dwc3_imx->glue_base + USB_WAKEUP_CTRL);
|
||||
val = readl(dwc3_imx->hsio_blk_base + USB_WAKEUP_CTRL);
|
||||
|
||||
if ((dwc3->current_dr_role == DWC3_GCTL_PRTCAP_HOST) && dwc3->xhci)
|
||||
val |= USB_WAKEUP_EN | USB_WAKEUP_SS_CONN |
|
||||
|
@ -64,16 +112,16 @@ static void dwc3_imx8mp_wakeup_enable(struct dwc3_imx8mp *dwc3_imx)
|
|||
val |= USB_WAKEUP_EN | USB_WAKEUP_VBUS_EN |
|
||||
USB_WAKEUP_VBUS_SRC_SESS_VAL;
|
||||
|
||||
writel(val, dwc3_imx->glue_base + USB_WAKEUP_CTRL);
|
||||
writel(val, dwc3_imx->hsio_blk_base + USB_WAKEUP_CTRL);
|
||||
}
|
||||
|
||||
static void dwc3_imx8mp_wakeup_disable(struct dwc3_imx8mp *dwc3_imx)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl(dwc3_imx->glue_base + USB_WAKEUP_CTRL);
|
||||
val = readl(dwc3_imx->hsio_blk_base + USB_WAKEUP_CTRL);
|
||||
val &= ~(USB_WAKEUP_EN | USB_WAKEUP_EN_MASK);
|
||||
writel(val, dwc3_imx->glue_base + USB_WAKEUP_CTRL);
|
||||
writel(val, dwc3_imx->hsio_blk_base + USB_WAKEUP_CTRL);
|
||||
}
|
||||
|
||||
static irqreturn_t dwc3_imx8mp_interrupt(int irq, void *_dwc3_imx)
|
||||
|
@ -100,6 +148,7 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
|
|||
struct device *dev = &pdev->dev;
|
||||
struct device_node *dwc3_np, *node = dev->of_node;
|
||||
struct dwc3_imx8mp *dwc3_imx;
|
||||
struct resource *res;
|
||||
int err, irq;
|
||||
|
||||
if (!node) {
|
||||
|
@ -115,9 +164,18 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
|
|||
|
||||
dwc3_imx->dev = dev;
|
||||
|
||||
dwc3_imx->glue_base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(dwc3_imx->glue_base))
|
||||
return PTR_ERR(dwc3_imx->glue_base);
|
||||
dwc3_imx->hsio_blk_base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(dwc3_imx->hsio_blk_base))
|
||||
return PTR_ERR(dwc3_imx->hsio_blk_base);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
if (!res) {
|
||||
dev_warn(dev, "Base address for glue layer missing. Continuing without, some features are missing though.");
|
||||
} else {
|
||||
dwc3_imx->glue_base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(dwc3_imx->glue_base))
|
||||
return PTR_ERR(dwc3_imx->glue_base);
|
||||
}
|
||||
|
||||
dwc3_imx->hsio_clk = devm_clk_get(dev, "hsio");
|
||||
if (IS_ERR(dwc3_imx->hsio_clk)) {
|
||||
|
@ -152,6 +210,8 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
|
|||
}
|
||||
dwc3_imx->irq = irq;
|
||||
|
||||
imx8mp_configure_glue(dwc3_imx);
|
||||
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
err = pm_runtime_get_sync(dev);
|
||||
|
@ -252,6 +312,9 @@ static int __maybe_unused dwc3_imx8mp_resume(struct dwc3_imx8mp *dwc3_imx,
|
|||
dwc3_imx8mp_wakeup_disable(dwc3_imx);
|
||||
dwc3_imx->pm_suspended = false;
|
||||
|
||||
/* Upon power loss any previous configuration is lost, restore it */
|
||||
imx8mp_configure_glue(dwc3_imx);
|
||||
|
||||
if (dwc3_imx->wakeup_pending) {
|
||||
dwc3_imx->wakeup_pending = false;
|
||||
if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE) {
|
||||
|
|
|
@ -188,7 +188,7 @@ static int dwc3_meson_gxl_usb_post_init(struct dwc3_meson_g12a *priv);
|
|||
* reset to recover usage of the port.
|
||||
*/
|
||||
|
||||
static struct dwc3_meson_g12a_drvdata gxl_drvdata = {
|
||||
static const struct dwc3_meson_g12a_drvdata gxl_drvdata = {
|
||||
.otg_switch_supported = true,
|
||||
.otg_phy_host_port_disable = true,
|
||||
.clks = meson_gxl_clocks,
|
||||
|
@ -202,7 +202,7 @@ static struct dwc3_meson_g12a_drvdata gxl_drvdata = {
|
|||
.usb_post_init = dwc3_meson_gxl_usb_post_init,
|
||||
};
|
||||
|
||||
static struct dwc3_meson_g12a_drvdata gxm_drvdata = {
|
||||
static const struct dwc3_meson_g12a_drvdata gxm_drvdata = {
|
||||
.otg_switch_supported = true,
|
||||
.otg_phy_host_port_disable = true,
|
||||
.clks = meson_gxl_clocks,
|
||||
|
@ -216,7 +216,7 @@ static struct dwc3_meson_g12a_drvdata gxm_drvdata = {
|
|||
.usb_post_init = dwc3_meson_gxl_usb_post_init,
|
||||
};
|
||||
|
||||
static struct dwc3_meson_g12a_drvdata axg_drvdata = {
|
||||
static const struct dwc3_meson_g12a_drvdata axg_drvdata = {
|
||||
.otg_switch_supported = true,
|
||||
.clks = meson_gxl_clocks,
|
||||
.num_clks = ARRAY_SIZE(meson_gxl_clocks),
|
||||
|
@ -229,7 +229,7 @@ static struct dwc3_meson_g12a_drvdata axg_drvdata = {
|
|||
.usb_post_init = dwc3_meson_gxl_usb_post_init,
|
||||
};
|
||||
|
||||
static struct dwc3_meson_g12a_drvdata g12a_drvdata = {
|
||||
static const struct dwc3_meson_g12a_drvdata g12a_drvdata = {
|
||||
.otg_switch_supported = true,
|
||||
.clks = meson_g12a_clocks,
|
||||
.num_clks = ARRAY_SIZE(meson_g12a_clocks),
|
||||
|
@ -241,7 +241,7 @@ static struct dwc3_meson_g12a_drvdata g12a_drvdata = {
|
|||
.usb_init = dwc3_meson_g12a_usb_init,
|
||||
};
|
||||
|
||||
static struct dwc3_meson_g12a_drvdata a1_drvdata = {
|
||||
static const struct dwc3_meson_g12a_drvdata a1_drvdata = {
|
||||
.otg_switch_supported = false,
|
||||
.clks = meson_a1_clocks,
|
||||
.num_clks = ARRAY_SIZE(meson_a1_clocks),
|
||||
|
|
|
@ -242,7 +242,7 @@ static void dwc3_omap_set_mailbox(struct dwc3_omap *omap,
|
|||
break;
|
||||
|
||||
case OMAP_DWC3_ID_FLOAT:
|
||||
if (omap->vbus_reg)
|
||||
if (omap->vbus_reg && regulator_is_enabled(omap->vbus_reg))
|
||||
regulator_disable(omap->vbus_reg);
|
||||
val = dwc3_omap_read_utmi_ctrl(omap);
|
||||
val |= USBOTGSS_UTMI_OTG_CTRL_IDDIG;
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#define PCI_DEVICE_ID_INTEL_TGPLP 0xa0ee
|
||||
#define PCI_DEVICE_ID_INTEL_TGPH 0x43ee
|
||||
#define PCI_DEVICE_ID_INTEL_JSP 0x4dee
|
||||
#define PCI_DEVICE_ID_INTEL_ADL 0x465e
|
||||
#define PCI_DEVICE_ID_INTEL_ADLP 0x51ee
|
||||
#define PCI_DEVICE_ID_INTEL_ADLM 0x54ee
|
||||
#define PCI_DEVICE_ID_INTEL_ADLS 0x7ae1
|
||||
|
@ -120,6 +121,13 @@ static const struct property_entry dwc3_pci_intel_properties[] = {
|
|||
{}
|
||||
};
|
||||
|
||||
static const struct property_entry dwc3_pci_intel_phy_charger_detect_properties[] = {
|
||||
PROPERTY_ENTRY_STRING("dr_mode", "peripheral"),
|
||||
PROPERTY_ENTRY_BOOL("snps,dis_u2_susphy_quirk"),
|
||||
PROPERTY_ENTRY_BOOL("linux,phy_charger_detect"),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct property_entry dwc3_pci_intel_byt_properties[] = {
|
||||
PROPERTY_ENTRY_STRING("dr_mode", "peripheral"),
|
||||
PROPERTY_ENTRY_BOOL("snps,dis_u2_susphy_quirk"),
|
||||
|
@ -169,6 +177,10 @@ static const struct software_node dwc3_pci_intel_swnode = {
|
|||
.properties = dwc3_pci_intel_properties,
|
||||
};
|
||||
|
||||
static const struct software_node dwc3_pci_intel_phy_charger_detect_swnode = {
|
||||
.properties = dwc3_pci_intel_phy_charger_detect_properties,
|
||||
};
|
||||
|
||||
static const struct software_node dwc3_pci_intel_byt_swnode = {
|
||||
.properties = dwc3_pci_intel_byt_properties,
|
||||
};
|
||||
|
@ -185,7 +197,8 @@ static const struct software_node dwc3_pci_amd_mr_swnode = {
|
|||
.properties = dwc3_pci_mr_properties,
|
||||
};
|
||||
|
||||
static int dwc3_pci_quirks(struct dwc3_pci *dwc)
|
||||
static int dwc3_pci_quirks(struct dwc3_pci *dwc,
|
||||
const struct software_node *swnode)
|
||||
{
|
||||
struct pci_dev *pdev = dwc->pci;
|
||||
|
||||
|
@ -239,10 +252,30 @@ static int dwc3_pci_quirks(struct dwc3_pci *dwc)
|
|||
gpiod_put(gpio);
|
||||
usleep_range(10000, 11000);
|
||||
}
|
||||
|
||||
/*
|
||||
* Make the pdev name predictable (only 1 DWC3 on BYT)
|
||||
* and patch the phy dev-name into the lookup table so
|
||||
* that the phy-driver can get the GPIOs.
|
||||
*/
|
||||
dwc->dwc3->id = PLATFORM_DEVID_NONE;
|
||||
platform_bytcr_gpios.dev_id = "dwc3.ulpi";
|
||||
|
||||
/*
|
||||
* Some Android tablets with a Crystal Cove PMIC
|
||||
* (INT33FD), rely on the TUSB1211 phy for charger
|
||||
* detection. These can be identified by them _not_
|
||||
* using the standard ACPI battery and ac drivers.
|
||||
*/
|
||||
if (acpi_dev_present("INT33FD", "1", 2) &&
|
||||
acpi_quirk_skip_acpi_ac_and_battery()) {
|
||||
dev_info(&pdev->dev, "Using TUSB1211 phy for charger detection\n");
|
||||
swnode = &dwc3_pci_intel_phy_charger_detect_swnode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return device_add_software_node(&dwc->dwc3->dev, swnode);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
@ -307,11 +340,7 @@ static int dwc3_pci_probe(struct pci_dev *pci, const struct pci_device_id *id)
|
|||
dwc->dwc3->dev.parent = dev;
|
||||
ACPI_COMPANION_SET(&dwc->dwc3->dev, ACPI_COMPANION(dev));
|
||||
|
||||
ret = device_add_software_node(&dwc->dwc3->dev, (void *)id->driver_data);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
ret = dwc3_pci_quirks(dwc);
|
||||
ret = dwc3_pci_quirks(dwc, (void *)id->driver_data);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
|
@ -412,6 +441,9 @@ static const struct pci_device_id dwc3_pci_id_table[] = {
|
|||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_JSP),
|
||||
(kernel_ulong_t) &dwc3_pci_intel_swnode, },
|
||||
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ADL),
|
||||
(kernel_ulong_t) &dwc3_pci_intel_swnode, },
|
||||
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ADLP),
|
||||
(kernel_ulong_t) &dwc3_pci_intel_swnode, },
|
||||
|
||||
|
|
|
@ -271,6 +271,7 @@ void dwc3_ep0_out_start(struct dwc3 *dwc)
|
|||
{
|
||||
struct dwc3_ep *dep;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
complete(&dwc->ep0_in_setup);
|
||||
|
||||
|
@ -279,6 +280,19 @@ void dwc3_ep0_out_start(struct dwc3 *dwc)
|
|||
DWC3_TRBCTL_CONTROL_SETUP, false);
|
||||
ret = dwc3_ep0_start_trans(dep);
|
||||
WARN_ON(ret < 0);
|
||||
for (i = 2; i < DWC3_ENDPOINTS_NUM; i++) {
|
||||
struct dwc3_ep *dwc3_ep;
|
||||
|
||||
dwc3_ep = dwc->eps[i];
|
||||
if (!dwc3_ep)
|
||||
continue;
|
||||
|
||||
if (!(dwc3_ep->flags & DWC3_EP_DELAY_STOP))
|
||||
continue;
|
||||
|
||||
dwc3_ep->flags &= ~DWC3_EP_DELAY_STOP;
|
||||
dwc3_stop_active_transfer(dwc3_ep, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
static struct dwc3_ep *dwc3_wIndex_to_dep(struct dwc3 *dwc, __le16 wIndex_le)
|
||||
|
|
|
@ -654,9 +654,6 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)
|
|||
return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETEPCONFIG, ¶ms);
|
||||
}
|
||||
|
||||
static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
|
||||
bool interrupt);
|
||||
|
||||
/**
|
||||
* dwc3_gadget_calc_tx_fifo_size - calculates the txfifo size value
|
||||
* @dwc: pointer to the DWC3 context
|
||||
|
@ -1673,6 +1670,40 @@ static int __dwc3_gadget_get_frame(struct dwc3 *dwc)
|
|||
return DWC3_DSTS_SOFFN(reg);
|
||||
}
|
||||
|
||||
/**
|
||||
* __dwc3_stop_active_transfer - stop the current active transfer
|
||||
* @dep: isoc endpoint
|
||||
* @force: set forcerm bit in the command
|
||||
* @interrupt: command complete interrupt after End Transfer command
|
||||
*
|
||||
* When setting force, the ForceRM bit will be set. In that case
|
||||
* the controller won't update the TRB progress on command
|
||||
* completion. It also won't clear the HWO bit in the TRB.
|
||||
* The command will also not complete immediately in that case.
|
||||
*/
|
||||
static int __dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt)
|
||||
{
|
||||
struct dwc3_gadget_ep_cmd_params params;
|
||||
u32 cmd;
|
||||
int ret;
|
||||
|
||||
cmd = DWC3_DEPCMD_ENDTRANSFER;
|
||||
cmd |= force ? DWC3_DEPCMD_HIPRI_FORCERM : 0;
|
||||
cmd |= interrupt ? DWC3_DEPCMD_CMDIOC : 0;
|
||||
cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
|
||||
WARN_ON_ONCE(ret);
|
||||
dep->resource_index = 0;
|
||||
|
||||
if (!interrupt)
|
||||
dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
|
||||
else if (!ret)
|
||||
dep->flags |= DWC3_EP_END_TRANSFER_PENDING;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_gadget_start_isoc_quirk - workaround invalid frame number
|
||||
* @dep: isoc endpoint
|
||||
|
@ -1830,7 +1861,13 @@ static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep)
|
|||
}
|
||||
|
||||
for (i = 0; i < DWC3_ISOC_MAX_RETRIES; i++) {
|
||||
dep->frame_number = DWC3_ALIGN_FRAME(dep, i + 1);
|
||||
int future_interval = i + 1;
|
||||
|
||||
/* Give the controller at least 500us to schedule transfers */
|
||||
if (desc->bInterval < 3)
|
||||
future_interval += 3 - desc->bInterval;
|
||||
|
||||
dep->frame_number = DWC3_ALIGN_FRAME(dep, future_interval);
|
||||
|
||||
ret = __dwc3_gadget_kick_transfer(dep);
|
||||
if (ret != -EAGAIN)
|
||||
|
@ -1842,21 +1879,8 @@ static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep)
|
|||
* status, issue END_TRANSFER command and retry on the next XferNotReady
|
||||
* event.
|
||||
*/
|
||||
if (ret == -EAGAIN) {
|
||||
struct dwc3_gadget_ep_cmd_params params;
|
||||
u32 cmd;
|
||||
|
||||
cmd = DWC3_DEPCMD_ENDTRANSFER |
|
||||
DWC3_DEPCMD_CMDIOC |
|
||||
DWC3_DEPCMD_PARAM(dep->resource_index);
|
||||
|
||||
dep->resource_index = 0;
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
|
||||
ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
|
||||
if (!ret)
|
||||
dep->flags |= DWC3_EP_END_TRANSFER_PENDING;
|
||||
}
|
||||
if (ret == -EAGAIN)
|
||||
ret = __dwc3_stop_active_transfer(dep, false, true);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -1899,6 +1923,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
|||
*/
|
||||
if ((dep->flags & DWC3_EP_END_TRANSFER_PENDING) ||
|
||||
(dep->flags & DWC3_EP_WEDGE) ||
|
||||
(dep->flags & DWC3_EP_DELAY_STOP) ||
|
||||
(dep->flags & DWC3_EP_STALL)) {
|
||||
dep->flags |= DWC3_EP_DELAY_START;
|
||||
return 0;
|
||||
|
@ -1913,13 +1938,11 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
|||
* errors which will force us issue EndTransfer command.
|
||||
*/
|
||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
|
||||
if (!(dep->flags & DWC3_EP_PENDING_REQUEST) &&
|
||||
!(dep->flags & DWC3_EP_TRANSFER_STARTED))
|
||||
return 0;
|
||||
|
||||
if ((dep->flags & DWC3_EP_PENDING_REQUEST)) {
|
||||
if (!(dep->flags & DWC3_EP_TRANSFER_STARTED))
|
||||
if (!(dep->flags & DWC3_EP_TRANSFER_STARTED)) {
|
||||
if ((dep->flags & DWC3_EP_PENDING_REQUEST))
|
||||
return __dwc3_gadget_start_isoc(dep);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2033,6 +2056,16 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
|
|||
if (r == req) {
|
||||
struct dwc3_request *t;
|
||||
|
||||
/*
|
||||
* If a Setup packet is received but yet to DMA out, the controller will
|
||||
* not process the End Transfer command of any endpoint. Polling of its
|
||||
* DEPCMD.CmdAct may block setting up TRB for Setup packet, causing a
|
||||
* timeout. Delay issuing the End Transfer command until the Setup TRB is
|
||||
* prepared.
|
||||
*/
|
||||
if (dwc->ep0state != EP0_SETUP_PHASE && !dwc->delayed_status)
|
||||
dep->flags |= DWC3_EP_DELAY_STOP;
|
||||
|
||||
/* wait until it is processed */
|
||||
dwc3_stop_active_transfer(dep, true, true);
|
||||
|
||||
|
@ -2116,7 +2149,8 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
|
|||
list_for_each_entry_safe(req, tmp, &dep->started_list, list)
|
||||
dwc3_gadget_move_cancelled_request(req, DWC3_REQUEST_STATUS_STALLED);
|
||||
|
||||
if (dep->flags & DWC3_EP_END_TRANSFER_PENDING) {
|
||||
if (dep->flags & DWC3_EP_END_TRANSFER_PENDING ||
|
||||
(dep->flags & DWC3_EP_DELAY_STOP)) {
|
||||
dep->flags |= DWC3_EP_PENDING_CLEAR_STALL;
|
||||
return 0;
|
||||
}
|
||||
|
@ -2544,6 +2578,17 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
|
|||
dwc->ev_buf->length;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* In the Synopsys DWC_usb31 1.90a programming guide section
|
||||
* 4.1.9, it specifies that for a reconnect after a
|
||||
* device-initiated disconnect requires a core soft reset
|
||||
* (DCTL.CSftRst) before enabling the run/stop bit.
|
||||
*/
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
dwc3_core_soft_reset(dwc);
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
|
||||
dwc3_event_buffers_setup(dwc);
|
||||
__dwc3_gadget_start(dwc);
|
||||
}
|
||||
|
||||
|
@ -3596,14 +3641,11 @@ static void dwc3_reset_gadget(struct dwc3 *dwc)
|
|||
}
|
||||
}
|
||||
|
||||
static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
|
||||
void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
|
||||
bool interrupt)
|
||||
{
|
||||
struct dwc3_gadget_ep_cmd_params params;
|
||||
u32 cmd;
|
||||
int ret;
|
||||
|
||||
if (!(dep->flags & DWC3_EP_TRANSFER_STARTED) ||
|
||||
(dep->flags & DWC3_EP_DELAY_STOP) ||
|
||||
(dep->flags & DWC3_EP_END_TRANSFER_PENDING))
|
||||
return;
|
||||
|
||||
|
@ -3634,19 +3676,7 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
|
|||
* This mode is NOT available on the DWC_usb31 IP.
|
||||
*/
|
||||
|
||||
cmd = DWC3_DEPCMD_ENDTRANSFER;
|
||||
cmd |= force ? DWC3_DEPCMD_HIPRI_FORCERM : 0;
|
||||
cmd |= interrupt ? DWC3_DEPCMD_CMDIOC : 0;
|
||||
cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
|
||||
WARN_ON_ONCE(ret);
|
||||
dep->resource_index = 0;
|
||||
|
||||
if (!interrupt)
|
||||
dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
|
||||
else
|
||||
dep->flags |= DWC3_EP_END_TRANSFER_PENDING;
|
||||
__dwc3_stop_active_transfer(dep, force, interrupt);
|
||||
}
|
||||
|
||||
static void dwc3_clear_stall_all_ep(struct dwc3 *dwc)
|
||||
|
|
|
@ -116,6 +116,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
|
|||
gfp_t gfp_flags);
|
||||
int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol);
|
||||
void dwc3_ep0_send_delayed_status(struct dwc3 *dwc);
|
||||
void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt);
|
||||
|
||||
/**
|
||||
* dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW
|
||||
|
|
|
@ -599,23 +599,26 @@ static int __init xdbc_early_setup(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int __init early_xdbc_parse_parameter(char *s)
|
||||
int __init early_xdbc_parse_parameter(char *s, int keep_early)
|
||||
{
|
||||
unsigned long dbgp_num = 0;
|
||||
u32 bus, dev, func, offset;
|
||||
char *e;
|
||||
int ret;
|
||||
|
||||
if (!early_pci_allowed())
|
||||
return -EPERM;
|
||||
|
||||
if (strstr(s, "keep"))
|
||||
early_console_keep = true;
|
||||
early_console_keep = keep_early;
|
||||
|
||||
if (xdbc.xdbc_reg)
|
||||
return 0;
|
||||
|
||||
if (*s && kstrtoul(s, 0, &dbgp_num))
|
||||
dbgp_num = 0;
|
||||
if (*s) {
|
||||
dbgp_num = simple_strtoul(s, &e, 10);
|
||||
if (s == e)
|
||||
dbgp_num = 0;
|
||||
}
|
||||
|
||||
pr_notice("dbgp_num: %lu\n", dbgp_num);
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
#
|
||||
subdir-ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG
|
||||
subdir-ccflags-$(CONFIG_USB_GADGET_VERBOSE) += -DVERBOSE_DEBUG
|
||||
ccflags-y += -I$(srctree)/drivers/usb/gadget/udc
|
||||
|
||||
obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o
|
||||
libcomposite-y := usbstring.o config.o epautoconf.o
|
||||
|
|
|
@ -863,24 +863,25 @@ static int set_config(struct usb_composite_dev *cdev,
|
|||
const struct usb_ctrlrequest *ctrl, unsigned number)
|
||||
{
|
||||
struct usb_gadget *gadget = cdev->gadget;
|
||||
struct usb_configuration *c = NULL;
|
||||
struct usb_configuration *c = NULL, *iter;
|
||||
int result = -EINVAL;
|
||||
unsigned power = gadget_is_otg(gadget) ? 8 : 100;
|
||||
int tmp;
|
||||
|
||||
if (number) {
|
||||
list_for_each_entry(c, &cdev->configs, list) {
|
||||
if (c->bConfigurationValue == number) {
|
||||
/*
|
||||
* We disable the FDs of the previous
|
||||
* configuration only if the new configuration
|
||||
* is a valid one
|
||||
*/
|
||||
if (cdev->config)
|
||||
reset_config(cdev);
|
||||
result = 0;
|
||||
break;
|
||||
}
|
||||
list_for_each_entry(iter, &cdev->configs, list) {
|
||||
if (iter->bConfigurationValue != number)
|
||||
continue;
|
||||
/*
|
||||
* We disable the FDs of the previous
|
||||
* configuration only if the new configuration
|
||||
* is a valid one
|
||||
*/
|
||||
if (cdev->config)
|
||||
reset_config(cdev);
|
||||
c = iter;
|
||||
result = 0;
|
||||
break;
|
||||
}
|
||||
if (result < 0)
|
||||
goto done;
|
||||
|
@ -1690,6 +1691,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|
|||
u16 w_value = le16_to_cpu(ctrl->wValue);
|
||||
u16 w_length = le16_to_cpu(ctrl->wLength);
|
||||
struct usb_function *f = NULL;
|
||||
struct usb_function *iter;
|
||||
u8 endp;
|
||||
|
||||
if (w_length > USB_COMP_EP0_BUFSIZ) {
|
||||
|
@ -2046,12 +2048,12 @@ unknown:
|
|||
if (!cdev->config)
|
||||
break;
|
||||
endp = ((w_index & 0x80) >> 3) | (w_index & 0x0f);
|
||||
list_for_each_entry(f, &cdev->config->functions, list) {
|
||||
if (test_bit(endp, f->endpoints))
|
||||
list_for_each_entry(iter, &cdev->config->functions, list) {
|
||||
if (test_bit(endp, iter->endpoints)) {
|
||||
f = iter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (&f->list == &cdev->config->functions)
|
||||
f = NULL;
|
||||
break;
|
||||
}
|
||||
try_fun_setup:
|
||||
|
|
|
@ -418,7 +418,7 @@ static int config_usb_cfg_link(
|
|||
|
||||
struct usb_function_instance *fi =
|
||||
to_usb_function_instance(usb_func_ci);
|
||||
struct usb_function_instance *a_fi;
|
||||
struct usb_function_instance *a_fi = NULL, *iter;
|
||||
struct usb_function *f;
|
||||
int ret;
|
||||
|
||||
|
@ -428,11 +428,13 @@ static int config_usb_cfg_link(
|
|||
* from another gadget or a random directory.
|
||||
* Also a function instance can only be linked once.
|
||||
*/
|
||||
list_for_each_entry(a_fi, &gi->available_func, cfs_list) {
|
||||
if (a_fi == fi)
|
||||
break;
|
||||
list_for_each_entry(iter, &gi->available_func, cfs_list) {
|
||||
if (iter != fi)
|
||||
continue;
|
||||
a_fi = iter;
|
||||
break;
|
||||
}
|
||||
if (a_fi != fi) {
|
||||
if (!a_fi) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
@ -882,15 +884,17 @@ static int os_desc_link(struct config_item *os_desc_ci,
|
|||
struct gadget_info *gi = os_desc_item_to_gadget_info(os_desc_ci);
|
||||
struct usb_composite_dev *cdev = &gi->cdev;
|
||||
struct config_usb_cfg *c_target = to_config_usb_cfg(usb_cfg_ci);
|
||||
struct usb_configuration *c;
|
||||
struct usb_configuration *c = NULL, *iter;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&gi->lock);
|
||||
list_for_each_entry(c, &cdev->configs, list) {
|
||||
if (c == &c_target->c)
|
||||
break;
|
||||
list_for_each_entry(iter, &cdev->configs, list) {
|
||||
if (iter != &c_target->c)
|
||||
continue;
|
||||
c = iter;
|
||||
break;
|
||||
}
|
||||
if (c != &c_target->c) {
|
||||
if (!c) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
|
|
@ -919,12 +919,12 @@ static ssize_t __ffs_epfile_read_data(struct ffs_epfile *epfile,
|
|||
data_len, ret);
|
||||
|
||||
data_len -= ret;
|
||||
buf = kmalloc(sizeof(*buf) + data_len, GFP_KERNEL);
|
||||
buf = kmalloc(struct_size(buf, storage, data_len), GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
buf->length = data_len;
|
||||
buf->data = buf->storage;
|
||||
memcpy(buf->storage, data + ret, data_len);
|
||||
memcpy(buf->storage, data + ret, flex_array_size(buf, storage, data_len));
|
||||
|
||||
/*
|
||||
* At this point read_buffer is NULL or READ_BUFFER_DROP (if
|
||||
|
|
|
@ -1189,6 +1189,8 @@ static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh)
|
|||
int msf = common->cmnd[1] & 0x02;
|
||||
int start_track = common->cmnd[6];
|
||||
u8 *buf = (u8 *)bh->buf;
|
||||
u8 format;
|
||||
int i, len;
|
||||
|
||||
if ((common->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */
|
||||
start_track > 1) {
|
||||
|
@ -1196,18 +1198,62 @@ static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
memset(buf, 0, 20);
|
||||
buf[1] = (20-2); /* TOC data length */
|
||||
buf[2] = 1; /* First track number */
|
||||
buf[3] = 1; /* Last track number */
|
||||
buf[5] = 0x16; /* Data track, copying allowed */
|
||||
buf[6] = 0x01; /* Only track is number 1 */
|
||||
store_cdrom_address(&buf[8], msf, 0);
|
||||
format = common->cmnd[2] & 0xf;
|
||||
/*
|
||||
* Check if CDB is old style SFF-8020i
|
||||
* i.e. format is in 2 MSBs of byte 9
|
||||
* Mac OS-X host sends us this.
|
||||
*/
|
||||
if (format == 0)
|
||||
format = (common->cmnd[9] >> 6) & 0x3;
|
||||
|
||||
buf[13] = 0x16; /* Lead-out track is data */
|
||||
buf[14] = 0xAA; /* Lead-out track number */
|
||||
store_cdrom_address(&buf[16], msf, curlun->num_sectors);
|
||||
return 20;
|
||||
switch (format) {
|
||||
case 0:
|
||||
/* Formatted TOC */
|
||||
len = 4 + 2*8; /* 4 byte header + 2 descriptors */
|
||||
memset(buf, 0, len);
|
||||
buf[1] = len - 2; /* TOC Length excludes length field */
|
||||
buf[2] = 1; /* First track number */
|
||||
buf[3] = 1; /* Last track number */
|
||||
buf[5] = 0x16; /* Data track, copying allowed */
|
||||
buf[6] = 0x01; /* Only track is number 1 */
|
||||
store_cdrom_address(&buf[8], msf, 0);
|
||||
|
||||
buf[13] = 0x16; /* Lead-out track is data */
|
||||
buf[14] = 0xAA; /* Lead-out track number */
|
||||
store_cdrom_address(&buf[16], msf, curlun->num_sectors);
|
||||
return len;
|
||||
|
||||
case 2:
|
||||
/* Raw TOC */
|
||||
len = 4 + 3*11; /* 4 byte header + 3 descriptors */
|
||||
memset(buf, 0, len); /* Header + A0, A1 & A2 descriptors */
|
||||
buf[1] = len - 2; /* TOC Length excludes length field */
|
||||
buf[2] = 1; /* First complete session */
|
||||
buf[3] = 1; /* Last complete session */
|
||||
|
||||
buf += 4;
|
||||
/* fill in A0, A1 and A2 points */
|
||||
for (i = 0; i < 3; i++) {
|
||||
buf[0] = 1; /* Session number */
|
||||
buf[1] = 0x16; /* Data track, copying allowed */
|
||||
/* 2 - Track number 0 -> TOC */
|
||||
buf[3] = 0xA0 + i; /* A0, A1, A2 point */
|
||||
/* 4, 5, 6 - Min, sec, frame is zero */
|
||||
buf[8] = 1; /* Pmin: last track number */
|
||||
buf += 11; /* go to next track descriptor */
|
||||
}
|
||||
buf -= 11; /* go back to A2 descriptor */
|
||||
|
||||
/* For A2, 7, 8, 9, 10 - zero, Pmin, Psec, Pframe of Lead out */
|
||||
store_cdrom_address(&buf[7], msf, curlun->num_sectors);
|
||||
return len;
|
||||
|
||||
default:
|
||||
/* Multi-session, PMA, ATIP, CD-TEXT not supported/required */
|
||||
curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int do_mode_sense(struct fsg_common *common, struct fsg_buffhd *bh)
|
||||
|
@ -1945,7 +1991,7 @@ static int do_scsi_command(struct fsg_common *common)
|
|||
common->data_size_from_cmnd =
|
||||
get_unaligned_be16(&common->cmnd[7]);
|
||||
reply = check_command(common, 10, DATA_DIR_TO_HOST,
|
||||
(7<<6) | (1<<1), 1,
|
||||
(0xf<<6) | (3<<1), 1,
|
||||
"READ TOC");
|
||||
if (reply == 0)
|
||||
reply = do_read_toc(common, bh);
|
||||
|
|
|
@ -668,10 +668,8 @@ static struct usb_function *phonet_alloc(struct usb_function_instance *fi)
|
|||
{
|
||||
struct f_phonet *fp;
|
||||
struct f_phonet_opts *opts;
|
||||
int size;
|
||||
|
||||
size = sizeof(*fp) + (phonet_rxq_size * sizeof(struct usb_request *));
|
||||
fp = kzalloc(size, GFP_KERNEL);
|
||||
fp = kzalloc(struct_size(fp, out_reqv, phonet_rxq_size), GFP_KERNEL);
|
||||
if (!fp)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
|
|
|
@ -345,6 +345,10 @@ static void gser_free(struct usb_function *f)
|
|||
|
||||
static void gser_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct f_gser *gser = func_to_gser(f);
|
||||
|
||||
/* Ensure port is disconnected before unbinding */
|
||||
gserial_disconnect(&gser->port);
|
||||
usb_free_all_descriptors(f);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* f_uac1.c -- USB Audio Class 1.0 Function (using u_audio API)
|
||||
*
|
||||
* Copyright (C) 2016 Ruslan Bilovol <ruslan.bilovol@gmail.com>
|
||||
* Copyright (C) 2021 Julian Scheel <julian@jusst.de>
|
||||
*
|
||||
* This driver doesn't expect any real Audio codec to be present
|
||||
* on the device - the audio streams are simply sinked to and
|
||||
|
@ -42,6 +43,9 @@ struct f_uac1 {
|
|||
/* Interrupt IN endpoint of AC interface */
|
||||
struct usb_ep *int_ep;
|
||||
atomic_t int_count;
|
||||
int ctl_id; /* EP id */
|
||||
int c_srate; /* current capture srate */
|
||||
int p_srate; /* current playback prate */
|
||||
};
|
||||
|
||||
static inline struct f_uac1 *func_to_uac1(struct usb_function *f)
|
||||
|
@ -188,16 +192,18 @@ static struct uac1_as_header_descriptor as_in_header_desc = {
|
|||
.wFormatTag = cpu_to_le16(UAC_FORMAT_TYPE_I_PCM),
|
||||
};
|
||||
|
||||
DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1);
|
||||
DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(UAC_MAX_RATES);
|
||||
#define uac_format_type_i_discrete_descriptor \
|
||||
uac_format_type_i_discrete_descriptor_##UAC_MAX_RATES
|
||||
|
||||
static struct uac_format_type_i_discrete_descriptor_1 as_out_type_i_desc = {
|
||||
.bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1),
|
||||
static struct uac_format_type_i_discrete_descriptor as_out_type_i_desc = {
|
||||
.bLength = 0, /* filled on rate setup */
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubtype = UAC_FORMAT_TYPE,
|
||||
.bFormatType = UAC_FORMAT_TYPE_I,
|
||||
.bSubframeSize = 2,
|
||||
.bBitResolution = 16,
|
||||
.bSamFreqType = 1,
|
||||
.bSamFreqType = 0, /* filled on rate setup */
|
||||
};
|
||||
|
||||
/* Standard ISO OUT Endpoint Descriptor */
|
||||
|
@ -221,14 +227,14 @@ static struct uac_iso_endpoint_descriptor as_iso_out_desc = {
|
|||
.wLockDelay = cpu_to_le16(1),
|
||||
};
|
||||
|
||||
static struct uac_format_type_i_discrete_descriptor_1 as_in_type_i_desc = {
|
||||
.bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1),
|
||||
static struct uac_format_type_i_discrete_descriptor as_in_type_i_desc = {
|
||||
.bLength = 0, /* filled on rate setup */
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubtype = UAC_FORMAT_TYPE,
|
||||
.bFormatType = UAC_FORMAT_TYPE_I,
|
||||
.bSubframeSize = 2,
|
||||
.bBitResolution = 16,
|
||||
.bSamFreqType = 1,
|
||||
.bSamFreqType = 0, /* filled on rate setup */
|
||||
};
|
||||
|
||||
/* Standard ISO OUT Endpoint Descriptor */
|
||||
|
@ -303,7 +309,7 @@ enum {
|
|||
};
|
||||
|
||||
static struct usb_string strings_uac1[] = {
|
||||
[STR_AC_IF].s = "AC Interface",
|
||||
/* [STR_AC_IF].s = DYNAMIC, */
|
||||
[STR_USB_OUT_IT].s = "Playback Input terminal",
|
||||
[STR_USB_OUT_IT_CH_NAMES].s = "Playback Channels",
|
||||
[STR_IO_OUT_OT].s = "Playback Output terminal",
|
||||
|
@ -333,6 +339,30 @@ static struct usb_gadget_strings *uac1_strings[] = {
|
|||
* This function is an ALSA sound card following USB Audio Class Spec 1.0.
|
||||
*/
|
||||
|
||||
static void uac_cs_attr_sample_rate(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
struct usb_function *fn = ep->driver_data;
|
||||
struct usb_composite_dev *cdev = fn->config->cdev;
|
||||
struct g_audio *agdev = func_to_g_audio(fn);
|
||||
struct f_uac1 *uac1 = func_to_uac1(fn);
|
||||
u8 *buf = (u8 *)req->buf;
|
||||
u32 val = 0;
|
||||
|
||||
if (req->actual != 3) {
|
||||
WARN(cdev, "Invalid data size for UAC_EP_CS_ATTR_SAMPLE_RATE.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
val = buf[0] | (buf[1] << 8) | (buf[2] << 16);
|
||||
if (uac1->ctl_id == (USB_DIR_IN | 2)) {
|
||||
uac1->p_srate = val;
|
||||
u_audio_set_playback_srate(agdev, uac1->p_srate);
|
||||
} else if (uac1->ctl_id == (USB_DIR_OUT | 1)) {
|
||||
uac1->c_srate = val;
|
||||
u_audio_set_capture_srate(agdev, uac1->c_srate);
|
||||
}
|
||||
}
|
||||
|
||||
static void audio_notify_complete(struct usb_ep *_ep, struct usb_request *req)
|
||||
{
|
||||
struct g_audio *audio = req->context;
|
||||
|
@ -707,18 +737,27 @@ static int audio_set_endpoint_req(struct usb_function *f,
|
|||
const struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
struct usb_composite_dev *cdev = f->config->cdev;
|
||||
struct usb_request *req = f->config->cdev->req;
|
||||
struct f_uac1 *uac1 = func_to_uac1(f);
|
||||
int value = -EOPNOTSUPP;
|
||||
u16 ep = le16_to_cpu(ctrl->wIndex);
|
||||
u16 len = le16_to_cpu(ctrl->wLength);
|
||||
u16 w_value = le16_to_cpu(ctrl->wValue);
|
||||
u8 cs = w_value >> 8;
|
||||
|
||||
DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
|
||||
ctrl->bRequest, w_value, len, ep);
|
||||
|
||||
switch (ctrl->bRequest) {
|
||||
case UAC_SET_CUR:
|
||||
case UAC_SET_CUR: {
|
||||
if (cs == UAC_EP_CS_ATTR_SAMPLE_RATE) {
|
||||
cdev->gadget->ep0->driver_data = f;
|
||||
uac1->ctl_id = ep;
|
||||
req->complete = uac_cs_attr_sample_rate;
|
||||
}
|
||||
value = len;
|
||||
break;
|
||||
}
|
||||
|
||||
case UAC_SET_MIN:
|
||||
break;
|
||||
|
@ -743,16 +782,33 @@ static int audio_get_endpoint_req(struct usb_function *f,
|
|||
const struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
struct usb_composite_dev *cdev = f->config->cdev;
|
||||
struct usb_request *req = f->config->cdev->req;
|
||||
struct f_uac1 *uac1 = func_to_uac1(f);
|
||||
u8 *buf = (u8 *)req->buf;
|
||||
int value = -EOPNOTSUPP;
|
||||
u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
|
||||
u8 ep = le16_to_cpu(ctrl->wIndex);
|
||||
u16 len = le16_to_cpu(ctrl->wLength);
|
||||
u16 w_value = le16_to_cpu(ctrl->wValue);
|
||||
u8 cs = w_value >> 8;
|
||||
u32 val = 0;
|
||||
|
||||
DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
|
||||
ctrl->bRequest, w_value, len, ep);
|
||||
|
||||
switch (ctrl->bRequest) {
|
||||
case UAC_GET_CUR:
|
||||
case UAC_GET_CUR: {
|
||||
if (cs == UAC_EP_CS_ATTR_SAMPLE_RATE) {
|
||||
if (ep == (USB_DIR_IN | 2))
|
||||
val = uac1->p_srate;
|
||||
else if (ep == (USB_DIR_OUT | 1))
|
||||
val = uac1->c_srate;
|
||||
buf[2] = (val >> 16) & 0xff;
|
||||
buf[1] = (val >> 8) & 0xff;
|
||||
buf[0] = val & 0xff;
|
||||
}
|
||||
value = len;
|
||||
break;
|
||||
}
|
||||
case UAC_GET_MIN:
|
||||
case UAC_GET_MAX:
|
||||
case UAC_GET_RES:
|
||||
|
@ -905,6 +961,14 @@ static void f_audio_disable(struct usb_function *f)
|
|||
usb_ep_disable(uac1->int_ep);
|
||||
}
|
||||
|
||||
static void
|
||||
f_audio_suspend(struct usb_function *f)
|
||||
{
|
||||
struct f_uac1 *uac1 = func_to_uac1(f);
|
||||
|
||||
u_audio_suspend(&uac1->g_audio);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
static struct uac_feature_unit_descriptor *build_fu_desc(int chmask)
|
||||
{
|
||||
|
@ -1074,10 +1138,10 @@ static int f_audio_validate_opts(struct g_audio *audio, struct device *dev)
|
|||
} else if ((opts->c_ssize < 1) || (opts->c_ssize > 4)) {
|
||||
dev_err(dev, "Error: incorrect capture sample size\n");
|
||||
return -EINVAL;
|
||||
} else if (!opts->p_srate) {
|
||||
} else if (!opts->p_srates[0]) {
|
||||
dev_err(dev, "Error: incorrect playback sampling rate\n");
|
||||
return -EINVAL;
|
||||
} else if (!opts->c_srate) {
|
||||
} else if (!opts->c_srates[0]) {
|
||||
dev_err(dev, "Error: incorrect capture sampling rate\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -1118,10 +1182,9 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
struct f_uac1_opts *audio_opts;
|
||||
struct usb_ep *ep = NULL;
|
||||
struct usb_string *us;
|
||||
u8 *sam_freq;
|
||||
int rate;
|
||||
int ba_iface_id;
|
||||
int status;
|
||||
int idx, i;
|
||||
|
||||
status = f_audio_validate_opts(audio, dev);
|
||||
if (status)
|
||||
|
@ -1129,6 +1192,8 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
|
||||
audio_opts = container_of(f->fi, struct f_uac1_opts, func_inst);
|
||||
|
||||
strings_uac1[STR_AC_IF].s = audio_opts->function_name;
|
||||
|
||||
us = usb_gstrings_attach(cdev, uac1_strings, ARRAY_SIZE(strings_uac1));
|
||||
if (IS_ERR(us))
|
||||
return PTR_ERR(us);
|
||||
|
@ -1213,12 +1278,25 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
}
|
||||
|
||||
/* Set sample rates */
|
||||
rate = audio_opts->c_srate;
|
||||
sam_freq = as_out_type_i_desc.tSamFreq[0];
|
||||
memcpy(sam_freq, &rate, 3);
|
||||
rate = audio_opts->p_srate;
|
||||
sam_freq = as_in_type_i_desc.tSamFreq[0];
|
||||
memcpy(sam_freq, &rate, 3);
|
||||
for (i = 0, idx = 0; i < UAC_MAX_RATES; i++) {
|
||||
if (audio_opts->c_srates[i] == 0)
|
||||
break;
|
||||
memcpy(as_out_type_i_desc.tSamFreq[idx++],
|
||||
&audio_opts->c_srates[i], 3);
|
||||
}
|
||||
as_out_type_i_desc.bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(idx);
|
||||
as_out_type_i_desc.bSamFreqType = idx;
|
||||
|
||||
for (i = 0, idx = 0; i < UAC_MAX_RATES; i++) {
|
||||
if (audio_opts->p_srates[i] == 0)
|
||||
break;
|
||||
memcpy(as_in_type_i_desc.tSamFreq[idx++],
|
||||
&audio_opts->p_srates[i], 3);
|
||||
}
|
||||
as_in_type_i_desc.bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(idx);
|
||||
as_in_type_i_desc.bSamFreqType = idx;
|
||||
uac1->p_srate = audio_opts->p_srates[0];
|
||||
uac1->c_srate = audio_opts->c_srates[0];
|
||||
|
||||
/* allocate instance-specific interface IDs, and patch descriptors */
|
||||
status = usb_interface_id(c, f);
|
||||
|
@ -1297,7 +1375,8 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
audio->out_ep_maxpsize = le16_to_cpu(as_out_ep_desc.wMaxPacketSize);
|
||||
audio->in_ep_maxpsize = le16_to_cpu(as_in_ep_desc.wMaxPacketSize);
|
||||
audio->params.c_chmask = audio_opts->c_chmask;
|
||||
audio->params.c_srate = audio_opts->c_srate;
|
||||
memcpy(audio->params.c_srates, audio_opts->c_srates,
|
||||
sizeof(audio->params.c_srates));
|
||||
audio->params.c_ssize = audio_opts->c_ssize;
|
||||
if (FUIN_EN(audio_opts)) {
|
||||
audio->params.p_fu.id = USB_IN_FU_ID;
|
||||
|
@ -1309,7 +1388,8 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
audio->params.p_fu.volume_res = audio_opts->p_volume_res;
|
||||
}
|
||||
audio->params.p_chmask = audio_opts->p_chmask;
|
||||
audio->params.p_srate = audio_opts->p_srate;
|
||||
memcpy(audio->params.p_srates, audio_opts->p_srates,
|
||||
sizeof(audio->params.p_srates));
|
||||
audio->params.p_ssize = audio_opts->p_ssize;
|
||||
if (FUOUT_EN(audio_opts)) {
|
||||
audio->params.c_fu.id = USB_OUT_FU_ID;
|
||||
|
@ -1414,11 +1494,106 @@ end: \
|
|||
\
|
||||
CONFIGFS_ATTR(f_uac1_opts_, name)
|
||||
|
||||
#define UAC1_RATE_ATTRIBUTE(name) \
|
||||
static ssize_t f_uac1_opts_##name##_show(struct config_item *item, \
|
||||
char *page) \
|
||||
{ \
|
||||
struct f_uac1_opts *opts = to_f_uac1_opts(item); \
|
||||
int result = 0; \
|
||||
int i; \
|
||||
\
|
||||
mutex_lock(&opts->lock); \
|
||||
page[0] = '\0'; \
|
||||
for (i = 0; i < UAC_MAX_RATES; i++) { \
|
||||
if (opts->name##s[i] == 0) \
|
||||
break; \
|
||||
result += sprintf(page + strlen(page), "%u,", \
|
||||
opts->name##s[i]); \
|
||||
} \
|
||||
if (strlen(page) > 0) \
|
||||
page[strlen(page) - 1] = '\n'; \
|
||||
mutex_unlock(&opts->lock); \
|
||||
\
|
||||
return result; \
|
||||
} \
|
||||
\
|
||||
static ssize_t f_uac1_opts_##name##_store(struct config_item *item, \
|
||||
const char *page, size_t len) \
|
||||
{ \
|
||||
struct f_uac1_opts *opts = to_f_uac1_opts(item); \
|
||||
char *split_page = NULL; \
|
||||
int ret = -EINVAL; \
|
||||
char *token; \
|
||||
u32 num; \
|
||||
int i; \
|
||||
\
|
||||
mutex_lock(&opts->lock); \
|
||||
if (opts->refcnt) { \
|
||||
ret = -EBUSY; \
|
||||
goto end; \
|
||||
} \
|
||||
\
|
||||
i = 0; \
|
||||
memset(opts->name##s, 0x00, sizeof(opts->name##s)); \
|
||||
split_page = kstrdup(page, GFP_KERNEL); \
|
||||
while ((token = strsep(&split_page, ",")) != NULL) { \
|
||||
ret = kstrtou32(token, 0, &num); \
|
||||
if (ret) \
|
||||
goto end; \
|
||||
\
|
||||
opts->name##s[i++] = num; \
|
||||
ret = len; \
|
||||
}; \
|
||||
\
|
||||
end: \
|
||||
kfree(split_page); \
|
||||
mutex_unlock(&opts->lock); \
|
||||
return ret; \
|
||||
} \
|
||||
\
|
||||
CONFIGFS_ATTR(f_uac1_opts_, name)
|
||||
|
||||
#define UAC1_ATTRIBUTE_STRING(name) \
|
||||
static ssize_t f_uac1_opts_##name##_show(struct config_item *item, \
|
||||
char *page) \
|
||||
{ \
|
||||
struct f_uac1_opts *opts = to_f_uac1_opts(item); \
|
||||
int result; \
|
||||
\
|
||||
mutex_lock(&opts->lock); \
|
||||
result = snprintf(page, sizeof(opts->name), "%s", opts->name); \
|
||||
mutex_unlock(&opts->lock); \
|
||||
\
|
||||
return result; \
|
||||
} \
|
||||
\
|
||||
static ssize_t f_uac1_opts_##name##_store(struct config_item *item, \
|
||||
const char *page, size_t len) \
|
||||
{ \
|
||||
struct f_uac1_opts *opts = to_f_uac1_opts(item); \
|
||||
int ret = 0; \
|
||||
\
|
||||
mutex_lock(&opts->lock); \
|
||||
if (opts->refcnt) { \
|
||||
ret = -EBUSY; \
|
||||
goto end; \
|
||||
} \
|
||||
\
|
||||
ret = snprintf(opts->name, min(sizeof(opts->name), len), \
|
||||
"%s", page); \
|
||||
\
|
||||
end: \
|
||||
mutex_unlock(&opts->lock); \
|
||||
return ret; \
|
||||
} \
|
||||
\
|
||||
CONFIGFS_ATTR(f_uac1_opts_, name)
|
||||
|
||||
UAC1_ATTRIBUTE(u32, c_chmask);
|
||||
UAC1_ATTRIBUTE(u32, c_srate);
|
||||
UAC1_RATE_ATTRIBUTE(c_srate);
|
||||
UAC1_ATTRIBUTE(u32, c_ssize);
|
||||
UAC1_ATTRIBUTE(u32, p_chmask);
|
||||
UAC1_ATTRIBUTE(u32, p_srate);
|
||||
UAC1_RATE_ATTRIBUTE(p_srate);
|
||||
UAC1_ATTRIBUTE(u32, p_ssize);
|
||||
UAC1_ATTRIBUTE(u32, req_number);
|
||||
|
||||
|
@ -1433,6 +1608,7 @@ UAC1_ATTRIBUTE(bool, c_volume_present);
|
|||
UAC1_ATTRIBUTE(s16, c_volume_min);
|
||||
UAC1_ATTRIBUTE(s16, c_volume_max);
|
||||
UAC1_ATTRIBUTE(s16, c_volume_res);
|
||||
UAC1_ATTRIBUTE_STRING(function_name);
|
||||
|
||||
static struct configfs_attribute *f_uac1_attrs[] = {
|
||||
&f_uac1_opts_attr_c_chmask,
|
||||
|
@ -1455,6 +1631,8 @@ static struct configfs_attribute *f_uac1_attrs[] = {
|
|||
&f_uac1_opts_attr_c_volume_max,
|
||||
&f_uac1_opts_attr_c_volume_res,
|
||||
|
||||
&f_uac1_opts_attr_function_name,
|
||||
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
@ -1487,10 +1665,10 @@ static struct usb_function_instance *f_audio_alloc_inst(void)
|
|||
&f_uac1_func_type);
|
||||
|
||||
opts->c_chmask = UAC1_DEF_CCHMASK;
|
||||
opts->c_srate = UAC1_DEF_CSRATE;
|
||||
opts->c_srates[0] = UAC1_DEF_CSRATE;
|
||||
opts->c_ssize = UAC1_DEF_CSSIZE;
|
||||
opts->p_chmask = UAC1_DEF_PCHMASK;
|
||||
opts->p_srate = UAC1_DEF_PSRATE;
|
||||
opts->p_srates[0] = UAC1_DEF_PSRATE;
|
||||
opts->p_ssize = UAC1_DEF_PSSIZE;
|
||||
|
||||
opts->p_mute_present = UAC1_DEF_MUTE_PRESENT;
|
||||
|
@ -1506,6 +1684,9 @@ static struct usb_function_instance *f_audio_alloc_inst(void)
|
|||
opts->c_volume_res = UAC1_DEF_RES_DB;
|
||||
|
||||
opts->req_number = UAC1_DEF_REQ_NUM;
|
||||
|
||||
snprintf(opts->function_name, sizeof(opts->function_name), "AC Interface");
|
||||
|
||||
return &opts->func_inst;
|
||||
}
|
||||
|
||||
|
@ -1562,6 +1743,7 @@ static struct usb_function *f_audio_alloc(struct usb_function_instance *fi)
|
|||
uac1->g_audio.func.get_alt = f_audio_get_alt;
|
||||
uac1->g_audio.func.setup = f_audio_setup;
|
||||
uac1->g_audio.func.disable = f_audio_disable;
|
||||
uac1->g_audio.func.suspend = f_audio_suspend;
|
||||
uac1->g_audio.func.free_func = f_audio_free;
|
||||
|
||||
return &uac1->g_audio.func;
|
||||
|
|
|
@ -70,6 +70,8 @@ struct f_uac2 {
|
|||
/* Interrupt IN endpoint of AC interface */
|
||||
struct usb_ep *int_ep;
|
||||
atomic_t int_count;
|
||||
/* transient state, only valid during handling of a single control request */
|
||||
int clock_id;
|
||||
};
|
||||
|
||||
static inline struct f_uac2 *func_to_uac2(struct usb_function *f)
|
||||
|
@ -104,14 +106,11 @@ enum {
|
|||
STR_AS_IN_ALT1,
|
||||
};
|
||||
|
||||
static char clksrc_in[8];
|
||||
static char clksrc_out[8];
|
||||
|
||||
static struct usb_string strings_fn[] = {
|
||||
[STR_ASSOC].s = "Source/Sink",
|
||||
/* [STR_ASSOC].s = DYNAMIC, */
|
||||
[STR_IF_CTRL].s = "Topology Control",
|
||||
[STR_CLKSRC_IN].s = clksrc_in,
|
||||
[STR_CLKSRC_OUT].s = clksrc_out,
|
||||
[STR_CLKSRC_IN].s = "Input Clock",
|
||||
[STR_CLKSRC_OUT].s = "Output Clock",
|
||||
[STR_USB_IT].s = "USBH Out",
|
||||
[STR_IO_IT].s = "USBD Out",
|
||||
[STR_USB_OT].s = "USBH In",
|
||||
|
@ -125,6 +124,16 @@ static struct usb_string strings_fn[] = {
|
|||
{ },
|
||||
};
|
||||
|
||||
static const char *const speed_names[] = {
|
||||
[USB_SPEED_UNKNOWN] = "UNKNOWN",
|
||||
[USB_SPEED_LOW] = "LS",
|
||||
[USB_SPEED_FULL] = "FS",
|
||||
[USB_SPEED_HIGH] = "HS",
|
||||
[USB_SPEED_WIRELESS] = "W",
|
||||
[USB_SPEED_SUPER] = "SS",
|
||||
[USB_SPEED_SUPER_PLUS] = "SS+",
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings str_fn = {
|
||||
.language = 0x0409, /* en-us */
|
||||
.strings = strings_fn,
|
||||
|
@ -166,7 +175,7 @@ static struct uac_clock_source_descriptor in_clk_src_desc = {
|
|||
.bDescriptorSubtype = UAC2_CLOCK_SOURCE,
|
||||
/* .bClockID = DYNAMIC */
|
||||
.bmAttributes = UAC_CLOCK_SOURCE_TYPE_INT_FIXED,
|
||||
.bmControls = (CONTROL_RDONLY << CLK_FREQ_CTRL),
|
||||
.bmControls = (CONTROL_RDWR << CLK_FREQ_CTRL),
|
||||
.bAssocTerminal = 0,
|
||||
};
|
||||
|
||||
|
@ -178,7 +187,7 @@ static struct uac_clock_source_descriptor out_clk_src_desc = {
|
|||
.bDescriptorSubtype = UAC2_CLOCK_SOURCE,
|
||||
/* .bClockID = DYNAMIC */
|
||||
.bmAttributes = UAC_CLOCK_SOURCE_TYPE_INT_FIXED,
|
||||
.bmControls = (CONTROL_RDONLY << CLK_FREQ_CTRL),
|
||||
.bmControls = (CONTROL_RDWR << CLK_FREQ_CTRL),
|
||||
.bAssocTerminal = 0,
|
||||
};
|
||||
|
||||
|
@ -344,7 +353,7 @@ static struct usb_endpoint_descriptor hs_epout_desc = {
|
|||
|
||||
/* .bmAttributes = DYNAMIC */
|
||||
/* .wMaxPacketSize = DYNAMIC */
|
||||
.bInterval = 4,
|
||||
/* .bInterval = DYNAMIC */
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor ss_epout_desc = {
|
||||
|
@ -354,7 +363,7 @@ static struct usb_endpoint_descriptor ss_epout_desc = {
|
|||
.bEndpointAddress = USB_DIR_OUT,
|
||||
/* .bmAttributes = DYNAMIC */
|
||||
/* .wMaxPacketSize = DYNAMIC */
|
||||
.bInterval = 4,
|
||||
/* .bInterval = DYNAMIC */
|
||||
};
|
||||
|
||||
static struct usb_ss_ep_comp_descriptor ss_epout_desc_comp = {
|
||||
|
@ -478,7 +487,7 @@ static struct usb_endpoint_descriptor hs_epin_desc = {
|
|||
|
||||
.bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
|
||||
/* .wMaxPacketSize = DYNAMIC */
|
||||
.bInterval = 4,
|
||||
/* .bInterval = DYNAMIC */
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor ss_epin_desc = {
|
||||
|
@ -488,7 +497,7 @@ static struct usb_endpoint_descriptor ss_epin_desc = {
|
|||
.bEndpointAddress = USB_DIR_IN,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
|
||||
/* .wMaxPacketSize = DYNAMIC */
|
||||
.bInterval = 4,
|
||||
/* .bInterval = DYNAMIC */
|
||||
};
|
||||
|
||||
static struct usb_ss_ep_comp_descriptor ss_epin_desc_comp = {
|
||||
|
@ -634,44 +643,50 @@ struct cntrl_cur_lay3 {
|
|||
__le32 dCUR;
|
||||
};
|
||||
|
||||
struct cntrl_range_lay3 {
|
||||
__le16 wNumSubRanges;
|
||||
struct cntrl_subrange_lay3 {
|
||||
__le32 dMIN;
|
||||
__le32 dMAX;
|
||||
__le32 dRES;
|
||||
} __packed;
|
||||
|
||||
static int set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts,
|
||||
struct usb_endpoint_descriptor *ep_desc,
|
||||
enum usb_device_speed speed, bool is_playback)
|
||||
#define ranges_lay3_size(c) (sizeof(c.wNumSubRanges) \
|
||||
+ le16_to_cpu(c.wNumSubRanges) \
|
||||
* sizeof(struct cntrl_subrange_lay3))
|
||||
|
||||
#define DECLARE_UAC2_CNTRL_RANGES_LAY3(k, n) \
|
||||
struct cntrl_ranges_lay3_##k { \
|
||||
__le16 wNumSubRanges; \
|
||||
struct cntrl_subrange_lay3 r[n]; \
|
||||
} __packed
|
||||
|
||||
DECLARE_UAC2_CNTRL_RANGES_LAY3(srates, UAC_MAX_RATES);
|
||||
|
||||
static int get_max_srate(const int *srates)
|
||||
{
|
||||
int i, max_srate = 0;
|
||||
|
||||
for (i = 0; i < UAC_MAX_RATES; i++) {
|
||||
if (srates[i] == 0)
|
||||
break;
|
||||
if (srates[i] > max_srate)
|
||||
max_srate = srates[i];
|
||||
}
|
||||
return max_srate;
|
||||
}
|
||||
|
||||
static int get_max_bw_for_bint(const struct f_uac2_opts *uac2_opts,
|
||||
u8 bint, unsigned int factor, bool is_playback)
|
||||
{
|
||||
int chmask, srate, ssize;
|
||||
u16 max_size_bw, max_size_ep;
|
||||
unsigned int factor;
|
||||
|
||||
switch (speed) {
|
||||
case USB_SPEED_FULL:
|
||||
max_size_ep = 1023;
|
||||
factor = 1000;
|
||||
break;
|
||||
|
||||
case USB_SPEED_HIGH:
|
||||
case USB_SPEED_SUPER:
|
||||
max_size_ep = 1024;
|
||||
factor = 8000;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
u16 max_size_bw;
|
||||
|
||||
if (is_playback) {
|
||||
chmask = uac2_opts->p_chmask;
|
||||
srate = uac2_opts->p_srate;
|
||||
srate = get_max_srate(uac2_opts->p_srates);
|
||||
ssize = uac2_opts->p_ssize;
|
||||
} else {
|
||||
chmask = uac2_opts->c_chmask;
|
||||
srate = uac2_opts->c_srate;
|
||||
srate = get_max_srate(uac2_opts->c_srates);
|
||||
ssize = uac2_opts->c_ssize;
|
||||
}
|
||||
|
||||
|
@ -681,14 +696,76 @@ static int set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts,
|
|||
srate = srate * (1000 + uac2_opts->fb_max) / 1000;
|
||||
// updated srate is always bigger, therefore DIV_ROUND_UP always yields +1
|
||||
max_size_bw = num_channels(chmask) * ssize *
|
||||
(DIV_ROUND_UP(srate, factor / (1 << (ep_desc->bInterval - 1))));
|
||||
(DIV_ROUND_UP(srate, factor / (1 << (bint - 1))));
|
||||
} else {
|
||||
// adding 1 frame provision for Win10
|
||||
max_size_bw = num_channels(chmask) * ssize *
|
||||
(DIV_ROUND_UP(srate, factor / (1 << (ep_desc->bInterval - 1))) + 1);
|
||||
(DIV_ROUND_UP(srate, factor / (1 << (bint - 1))) + 1);
|
||||
}
|
||||
ep_desc->wMaxPacketSize = cpu_to_le16(min_t(u16, max_size_bw,
|
||||
max_size_ep));
|
||||
return max_size_bw;
|
||||
}
|
||||
|
||||
static int set_ep_max_packet_size_bint(struct device *dev, const struct f_uac2_opts *uac2_opts,
|
||||
struct usb_endpoint_descriptor *ep_desc,
|
||||
enum usb_device_speed speed, bool is_playback)
|
||||
{
|
||||
u16 max_size_bw, max_size_ep;
|
||||
u8 bint, opts_bint;
|
||||
char *dir;
|
||||
|
||||
switch (speed) {
|
||||
case USB_SPEED_FULL:
|
||||
max_size_ep = 1023;
|
||||
// fixed
|
||||
bint = ep_desc->bInterval;
|
||||
max_size_bw = get_max_bw_for_bint(uac2_opts, bint, 1000, is_playback);
|
||||
break;
|
||||
|
||||
case USB_SPEED_HIGH:
|
||||
case USB_SPEED_SUPER:
|
||||
max_size_ep = 1024;
|
||||
if (is_playback)
|
||||
opts_bint = uac2_opts->p_hs_bint;
|
||||
else
|
||||
opts_bint = uac2_opts->c_hs_bint;
|
||||
|
||||
if (opts_bint > 0) {
|
||||
/* fixed bint */
|
||||
bint = opts_bint;
|
||||
max_size_bw = get_max_bw_for_bint(uac2_opts, bint, 8000, is_playback);
|
||||
} else {
|
||||
/* checking bInterval from 4 to 1 whether the required bandwidth fits */
|
||||
for (bint = 4; bint > 0; --bint) {
|
||||
max_size_bw = get_max_bw_for_bint(
|
||||
uac2_opts, bint, 8000, is_playback);
|
||||
if (max_size_bw <= max_size_ep)
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (is_playback)
|
||||
dir = "Playback";
|
||||
else
|
||||
dir = "Capture";
|
||||
|
||||
if (max_size_bw <= max_size_ep)
|
||||
dev_dbg(dev,
|
||||
"%s %s: Would use wMaxPacketSize %d and bInterval %d\n",
|
||||
speed_names[speed], dir, max_size_bw, bint);
|
||||
else {
|
||||
dev_warn(dev,
|
||||
"%s %s: Req. wMaxPacketSize %d at bInterval %d > max ISOC %d, may drop data!\n",
|
||||
speed_names[speed], dir, max_size_bw, bint, max_size_ep);
|
||||
max_size_bw = max_size_ep;
|
||||
}
|
||||
|
||||
ep_desc->wMaxPacketSize = cpu_to_le16(max_size_bw);
|
||||
ep_desc->bInterval = bint;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -896,50 +973,45 @@ static void setup_descriptor(struct f_uac2_opts *opts)
|
|||
static int afunc_validate_opts(struct g_audio *agdev, struct device *dev)
|
||||
{
|
||||
struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev);
|
||||
const char *msg = NULL;
|
||||
|
||||
if (!opts->p_chmask && !opts->c_chmask) {
|
||||
dev_err(dev, "Error: no playback and capture channels\n");
|
||||
return -EINVAL;
|
||||
} else if (opts->p_chmask & ~UAC2_CHANNEL_MASK) {
|
||||
dev_err(dev, "Error: unsupported playback channels mask\n");
|
||||
return -EINVAL;
|
||||
} else if (opts->c_chmask & ~UAC2_CHANNEL_MASK) {
|
||||
dev_err(dev, "Error: unsupported capture channels mask\n");
|
||||
return -EINVAL;
|
||||
} else if ((opts->p_ssize < 1) || (opts->p_ssize > 4)) {
|
||||
dev_err(dev, "Error: incorrect playback sample size\n");
|
||||
return -EINVAL;
|
||||
} else if ((opts->c_ssize < 1) || (opts->c_ssize > 4)) {
|
||||
dev_err(dev, "Error: incorrect capture sample size\n");
|
||||
return -EINVAL;
|
||||
} else if (!opts->p_srate) {
|
||||
dev_err(dev, "Error: incorrect playback sampling rate\n");
|
||||
return -EINVAL;
|
||||
} else if (!opts->c_srate) {
|
||||
dev_err(dev, "Error: incorrect capture sampling rate\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!opts->p_chmask && !opts->c_chmask)
|
||||
msg = "no playback and capture channels";
|
||||
else if (opts->p_chmask & ~UAC2_CHANNEL_MASK)
|
||||
msg = "unsupported playback channels mask";
|
||||
else if (opts->c_chmask & ~UAC2_CHANNEL_MASK)
|
||||
msg = "unsupported capture channels mask";
|
||||
else if ((opts->p_ssize < 1) || (opts->p_ssize > 4))
|
||||
msg = "incorrect playback sample size";
|
||||
else if ((opts->c_ssize < 1) || (opts->c_ssize > 4))
|
||||
msg = "incorrect capture sample size";
|
||||
else if (!opts->p_srates[0])
|
||||
msg = "incorrect playback sampling rate";
|
||||
else if (!opts->c_srates[0])
|
||||
msg = "incorrect capture sampling rate";
|
||||
|
||||
if (opts->p_volume_max <= opts->p_volume_min) {
|
||||
dev_err(dev, "Error: incorrect playback volume max/min\n");
|
||||
return -EINVAL;
|
||||
} else if (opts->c_volume_max <= opts->c_volume_min) {
|
||||
dev_err(dev, "Error: incorrect capture volume max/min\n");
|
||||
return -EINVAL;
|
||||
} else if (opts->p_volume_res <= 0) {
|
||||
dev_err(dev, "Error: negative/zero playback volume resolution\n");
|
||||
return -EINVAL;
|
||||
} else if (opts->c_volume_res <= 0) {
|
||||
dev_err(dev, "Error: negative/zero capture volume resolution\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
else if (opts->p_volume_max <= opts->p_volume_min)
|
||||
msg = "incorrect playback volume max/min";
|
||||
else if (opts->c_volume_max <= opts->c_volume_min)
|
||||
msg = "incorrect capture volume max/min";
|
||||
else if (opts->p_volume_res <= 0)
|
||||
msg = "negative/zero playback volume resolution";
|
||||
else if (opts->c_volume_res <= 0)
|
||||
msg = "negative/zero capture volume resolution";
|
||||
|
||||
if ((opts->p_volume_max - opts->p_volume_min) % opts->p_volume_res) {
|
||||
dev_err(dev, "Error: incorrect playback volume resolution\n");
|
||||
return -EINVAL;
|
||||
} else if ((opts->c_volume_max - opts->c_volume_min) % opts->c_volume_res) {
|
||||
dev_err(dev, "Error: incorrect capture volume resolution\n");
|
||||
return -EINVAL;
|
||||
else if ((opts->p_volume_max - opts->p_volume_min) % opts->p_volume_res)
|
||||
msg = "incorrect playback volume resolution";
|
||||
else if ((opts->c_volume_max - opts->c_volume_min) % opts->c_volume_res)
|
||||
msg = "incorrect capture volume resolution";
|
||||
|
||||
else if ((opts->p_hs_bint < 0) || (opts->p_hs_bint > 4))
|
||||
msg = "incorrect playback HS/SS bInterval (1-4: fixed, 0: auto)";
|
||||
else if ((opts->c_hs_bint < 0) || (opts->c_hs_bint > 4))
|
||||
msg = "incorrect capture HS/SS bInterval (1-4: fixed, 0: auto)";
|
||||
|
||||
if (msg) {
|
||||
dev_err(dev, "Error: %s\n", msg);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -961,6 +1033,8 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
strings_fn[STR_ASSOC].s = uac2_opts->function_name;
|
||||
|
||||
us = usb_gstrings_attach(cdev, fn_strings, ARRAY_SIZE(strings_fn));
|
||||
if (IS_ERR(us))
|
||||
return PTR_ERR(us);
|
||||
|
@ -1037,9 +1111,6 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
|
|||
*bma = cpu_to_le32(control);
|
||||
}
|
||||
|
||||
snprintf(clksrc_in, sizeof(clksrc_in), "%uHz", uac2_opts->p_srate);
|
||||
snprintf(clksrc_out, sizeof(clksrc_out), "%uHz", uac2_opts->c_srate);
|
||||
|
||||
ret = usb_interface_id(cfg, fn);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
|
||||
|
@ -1103,44 +1174,49 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
|
|||
std_ac_if_desc.bNumEndpoints = 1;
|
||||
}
|
||||
|
||||
hs_epin_desc.bInterval = uac2_opts->p_hs_bint;
|
||||
ss_epin_desc.bInterval = uac2_opts->p_hs_bint;
|
||||
hs_epout_desc.bInterval = uac2_opts->c_hs_bint;
|
||||
ss_epout_desc.bInterval = uac2_opts->c_hs_bint;
|
||||
|
||||
/* Calculate wMaxPacketSize according to audio bandwidth */
|
||||
ret = set_ep_max_packet_size(uac2_opts, &fs_epin_desc, USB_SPEED_FULL,
|
||||
true);
|
||||
ret = set_ep_max_packet_size_bint(dev, uac2_opts, &fs_epin_desc,
|
||||
USB_SPEED_FULL, true);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = set_ep_max_packet_size(uac2_opts, &fs_epout_desc, USB_SPEED_FULL,
|
||||
false);
|
||||
ret = set_ep_max_packet_size_bint(dev, uac2_opts, &fs_epout_desc,
|
||||
USB_SPEED_FULL, false);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = set_ep_max_packet_size(uac2_opts, &hs_epin_desc, USB_SPEED_HIGH,
|
||||
true);
|
||||
ret = set_ep_max_packet_size_bint(dev, uac2_opts, &hs_epin_desc,
|
||||
USB_SPEED_HIGH, true);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = set_ep_max_packet_size(uac2_opts, &hs_epout_desc, USB_SPEED_HIGH,
|
||||
false);
|
||||
ret = set_ep_max_packet_size_bint(dev, uac2_opts, &hs_epout_desc,
|
||||
USB_SPEED_HIGH, false);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = set_ep_max_packet_size(uac2_opts, &ss_epin_desc, USB_SPEED_SUPER,
|
||||
true);
|
||||
ret = set_ep_max_packet_size_bint(dev, uac2_opts, &ss_epin_desc,
|
||||
USB_SPEED_SUPER, true);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = set_ep_max_packet_size(uac2_opts, &ss_epout_desc, USB_SPEED_SUPER,
|
||||
false);
|
||||
ret = set_ep_max_packet_size_bint(dev, uac2_opts, &ss_epout_desc,
|
||||
USB_SPEED_SUPER, false);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
|
||||
return ret;
|
||||
|
@ -1209,7 +1285,8 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
|
|||
agdev->gadget = gadget;
|
||||
|
||||
agdev->params.p_chmask = uac2_opts->p_chmask;
|
||||
agdev->params.p_srate = uac2_opts->p_srate;
|
||||
memcpy(agdev->params.p_srates, uac2_opts->p_srates,
|
||||
sizeof(agdev->params.p_srates));
|
||||
agdev->params.p_ssize = uac2_opts->p_ssize;
|
||||
if (FUIN_EN(uac2_opts)) {
|
||||
agdev->params.p_fu.id = USB_IN_FU_ID;
|
||||
|
@ -1220,7 +1297,8 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
|
|||
agdev->params.p_fu.volume_res = uac2_opts->p_volume_res;
|
||||
}
|
||||
agdev->params.c_chmask = uac2_opts->c_chmask;
|
||||
agdev->params.c_srate = uac2_opts->c_srate;
|
||||
memcpy(agdev->params.c_srates, uac2_opts->c_srates,
|
||||
sizeof(agdev->params.c_srates));
|
||||
agdev->params.c_ssize = uac2_opts->c_ssize;
|
||||
if (FUOUT_EN(uac2_opts)) {
|
||||
agdev->params.c_fu.id = USB_OUT_FU_ID;
|
||||
|
@ -1411,6 +1489,14 @@ afunc_disable(struct usb_function *fn)
|
|||
usb_ep_disable(uac2->int_ep);
|
||||
}
|
||||
|
||||
static void
|
||||
afunc_suspend(struct usb_function *fn)
|
||||
{
|
||||
struct f_uac2 *uac2 = func_to_uac2(fn);
|
||||
|
||||
u_audio_suspend(&uac2->g_audio);
|
||||
}
|
||||
|
||||
static int
|
||||
in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
|
||||
{
|
||||
|
@ -1423,10 +1509,10 @@ in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
|
|||
u8 entity_id = (w_index >> 8) & 0xff;
|
||||
u8 control_selector = w_value >> 8;
|
||||
int value = -EOPNOTSUPP;
|
||||
int p_srate, c_srate;
|
||||
u32 p_srate, c_srate;
|
||||
|
||||
p_srate = opts->p_srate;
|
||||
c_srate = opts->c_srate;
|
||||
u_audio_get_playback_srate(agdev, &p_srate);
|
||||
u_audio_get_capture_srate(agdev, &c_srate);
|
||||
|
||||
if ((entity_id == USB_IN_CLK_ID) || (entity_id == USB_OUT_CLK_ID)) {
|
||||
if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
|
||||
|
@ -1500,28 +1586,39 @@ in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr)
|
|||
u8 entity_id = (w_index >> 8) & 0xff;
|
||||
u8 control_selector = w_value >> 8;
|
||||
int value = -EOPNOTSUPP;
|
||||
int p_srate, c_srate;
|
||||
|
||||
p_srate = opts->p_srate;
|
||||
c_srate = opts->c_srate;
|
||||
|
||||
if ((entity_id == USB_IN_CLK_ID) || (entity_id == USB_OUT_CLK_ID)) {
|
||||
if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
|
||||
struct cntrl_range_lay3 r;
|
||||
struct cntrl_ranges_lay3_srates rs;
|
||||
int i;
|
||||
int wNumSubRanges = 0;
|
||||
int srate;
|
||||
int *srates;
|
||||
|
||||
if (entity_id == USB_IN_CLK_ID)
|
||||
r.dMIN = cpu_to_le32(p_srate);
|
||||
srates = opts->p_srates;
|
||||
else if (entity_id == USB_OUT_CLK_ID)
|
||||
r.dMIN = cpu_to_le32(c_srate);
|
||||
srates = opts->c_srates;
|
||||
else
|
||||
return -EOPNOTSUPP;
|
||||
for (i = 0; i < UAC_MAX_RATES; i++) {
|
||||
srate = srates[i];
|
||||
if (srate == 0)
|
||||
break;
|
||||
|
||||
r.dMAX = r.dMIN;
|
||||
r.dRES = 0;
|
||||
r.wNumSubRanges = cpu_to_le16(1);
|
||||
|
||||
value = min_t(unsigned int, w_length, sizeof(r));
|
||||
memcpy(req->buf, &r, value);
|
||||
rs.r[wNumSubRanges].dMIN = cpu_to_le32(srate);
|
||||
rs.r[wNumSubRanges].dMAX = cpu_to_le32(srate);
|
||||
rs.r[wNumSubRanges].dRES = 0;
|
||||
wNumSubRanges++;
|
||||
dev_dbg(&agdev->gadget->dev,
|
||||
"%s(): clk %d: rate ID %d: %d\n",
|
||||
__func__, entity_id, wNumSubRanges, srate);
|
||||
}
|
||||
rs.wNumSubRanges = cpu_to_le16(wNumSubRanges);
|
||||
value = min_t(unsigned int, w_length, ranges_lay3_size(rs));
|
||||
dev_dbg(&agdev->gadget->dev, "%s(): sending %d rates, size %d\n",
|
||||
__func__, rs.wNumSubRanges, value);
|
||||
memcpy(req->buf, &rs, value);
|
||||
} else {
|
||||
dev_err(&agdev->gadget->dev,
|
||||
"%s:%d control_selector=%d TODO!\n",
|
||||
|
@ -1580,6 +1677,25 @@ ac_rq_in(struct usb_function *fn, const struct usb_ctrlrequest *cr)
|
|||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static void uac2_cs_control_sam_freq(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
struct usb_function *fn = ep->driver_data;
|
||||
struct g_audio *agdev = func_to_g_audio(fn);
|
||||
struct f_uac2 *uac2 = func_to_uac2(fn);
|
||||
u32 val;
|
||||
|
||||
if (req->actual != 4)
|
||||
return;
|
||||
|
||||
val = le32_to_cpu(*((__le32 *)req->buf));
|
||||
dev_dbg(&agdev->gadget->dev, "%s val: %d.\n", __func__, val);
|
||||
if (uac2->clock_id == USB_IN_CLK_ID) {
|
||||
u_audio_set_playback_srate(agdev, val);
|
||||
} else if (uac2->clock_id == USB_OUT_CLK_ID) {
|
||||
u_audio_set_capture_srate(agdev, val);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
out_rq_cur_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
|
@ -1631,6 +1747,7 @@ out_rq_cur_complete(struct usb_ep *ep, struct usb_request *req)
|
|||
static int
|
||||
out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
|
||||
{
|
||||
struct usb_composite_dev *cdev = fn->config->cdev;
|
||||
struct usb_request *req = fn->config->cdev->req;
|
||||
struct g_audio *agdev = func_to_g_audio(fn);
|
||||
struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev);
|
||||
|
@ -1640,10 +1757,17 @@ out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
|
|||
u16 w_value = le16_to_cpu(cr->wValue);
|
||||
u8 entity_id = (w_index >> 8) & 0xff;
|
||||
u8 control_selector = w_value >> 8;
|
||||
u8 clock_id = w_index >> 8;
|
||||
|
||||
if ((entity_id == USB_IN_CLK_ID) || (entity_id == USB_OUT_CLK_ID)) {
|
||||
if (control_selector == UAC2_CS_CONTROL_SAM_FREQ)
|
||||
if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
|
||||
dev_dbg(&agdev->gadget->dev,
|
||||
"control_selector UAC2_CS_CONTROL_SAM_FREQ, clock: %d\n", clock_id);
|
||||
cdev->gadget->ep0->driver_data = fn;
|
||||
uac2->clock_id = clock_id;
|
||||
req->complete = uac2_cs_control_sam_freq;
|
||||
return w_length;
|
||||
}
|
||||
} else if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
|
||||
(FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
|
||||
memcpy(&uac2->setup_cr, cr, sizeof(*cr));
|
||||
|
@ -1731,10 +1855,12 @@ static struct configfs_item_operations f_uac2_item_ops = {
|
|||
.release = f_uac2_attr_release,
|
||||
};
|
||||
|
||||
#define uac2_kstrtou8 kstrtou8
|
||||
#define uac2_kstrtou32 kstrtou32
|
||||
#define uac2_kstrtos16 kstrtos16
|
||||
#define uac2_kstrtobool(s, base, res) kstrtobool((s), (res))
|
||||
|
||||
static const char *u8_fmt = "%u\n";
|
||||
static const char *u32_fmt = "%u\n";
|
||||
static const char *s16_fmt = "%hd\n";
|
||||
static const char *bool_fmt = "%u\n";
|
||||
|
@ -1836,13 +1962,110 @@ end: \
|
|||
\
|
||||
CONFIGFS_ATTR(f_uac2_opts_, name)
|
||||
|
||||
#define UAC2_RATE_ATTRIBUTE(name) \
|
||||
static ssize_t f_uac2_opts_##name##_show(struct config_item *item, \
|
||||
char *page) \
|
||||
{ \
|
||||
struct f_uac2_opts *opts = to_f_uac2_opts(item); \
|
||||
int result = 0; \
|
||||
int i; \
|
||||
\
|
||||
mutex_lock(&opts->lock); \
|
||||
page[0] = '\0'; \
|
||||
for (i = 0; i < UAC_MAX_RATES; i++) { \
|
||||
if (opts->name##s[i] == 0) \
|
||||
break; \
|
||||
result += sprintf(page + strlen(page), "%u,", \
|
||||
opts->name##s[i]); \
|
||||
} \
|
||||
if (strlen(page) > 0) \
|
||||
page[strlen(page) - 1] = '\n'; \
|
||||
mutex_unlock(&opts->lock); \
|
||||
\
|
||||
return result; \
|
||||
} \
|
||||
\
|
||||
static ssize_t f_uac2_opts_##name##_store(struct config_item *item, \
|
||||
const char *page, size_t len) \
|
||||
{ \
|
||||
struct f_uac2_opts *opts = to_f_uac2_opts(item); \
|
||||
char *split_page = NULL; \
|
||||
int ret = -EINVAL; \
|
||||
char *token; \
|
||||
u32 num; \
|
||||
int i; \
|
||||
\
|
||||
mutex_lock(&opts->lock); \
|
||||
if (opts->refcnt) { \
|
||||
ret = -EBUSY; \
|
||||
goto end; \
|
||||
} \
|
||||
\
|
||||
i = 0; \
|
||||
memset(opts->name##s, 0x00, sizeof(opts->name##s)); \
|
||||
split_page = kstrdup(page, GFP_KERNEL); \
|
||||
while ((token = strsep(&split_page, ",")) != NULL) { \
|
||||
ret = kstrtou32(token, 0, &num); \
|
||||
if (ret) \
|
||||
goto end; \
|
||||
\
|
||||
opts->name##s[i++] = num; \
|
||||
ret = len; \
|
||||
}; \
|
||||
\
|
||||
end: \
|
||||
kfree(split_page); \
|
||||
mutex_unlock(&opts->lock); \
|
||||
return ret; \
|
||||
} \
|
||||
\
|
||||
CONFIGFS_ATTR(f_uac2_opts_, name)
|
||||
|
||||
#define UAC2_ATTRIBUTE_STRING(name) \
|
||||
static ssize_t f_uac2_opts_##name##_show(struct config_item *item, \
|
||||
char *page) \
|
||||
{ \
|
||||
struct f_uac2_opts *opts = to_f_uac2_opts(item); \
|
||||
int result; \
|
||||
\
|
||||
mutex_lock(&opts->lock); \
|
||||
result = snprintf(page, sizeof(opts->name), "%s", opts->name); \
|
||||
mutex_unlock(&opts->lock); \
|
||||
\
|
||||
return result; \
|
||||
} \
|
||||
\
|
||||
static ssize_t f_uac2_opts_##name##_store(struct config_item *item, \
|
||||
const char *page, size_t len) \
|
||||
{ \
|
||||
struct f_uac2_opts *opts = to_f_uac2_opts(item); \
|
||||
int ret = 0; \
|
||||
\
|
||||
mutex_lock(&opts->lock); \
|
||||
if (opts->refcnt) { \
|
||||
ret = -EBUSY; \
|
||||
goto end; \
|
||||
} \
|
||||
\
|
||||
ret = snprintf(opts->name, min(sizeof(opts->name), len), \
|
||||
"%s", page); \
|
||||
\
|
||||
end: \
|
||||
mutex_unlock(&opts->lock); \
|
||||
return ret; \
|
||||
} \
|
||||
\
|
||||
CONFIGFS_ATTR(f_uac2_opts_, name)
|
||||
|
||||
UAC2_ATTRIBUTE(u32, p_chmask);
|
||||
UAC2_ATTRIBUTE(u32, p_srate);
|
||||
UAC2_RATE_ATTRIBUTE(p_srate);
|
||||
UAC2_ATTRIBUTE(u32, p_ssize);
|
||||
UAC2_ATTRIBUTE(u8, p_hs_bint);
|
||||
UAC2_ATTRIBUTE(u32, c_chmask);
|
||||
UAC2_ATTRIBUTE(u32, c_srate);
|
||||
UAC2_RATE_ATTRIBUTE(c_srate);
|
||||
UAC2_ATTRIBUTE_SYNC(c_sync);
|
||||
UAC2_ATTRIBUTE(u32, c_ssize);
|
||||
UAC2_ATTRIBUTE(u8, c_hs_bint);
|
||||
UAC2_ATTRIBUTE(u32, req_number);
|
||||
|
||||
UAC2_ATTRIBUTE(bool, p_mute_present);
|
||||
|
@ -1857,14 +2080,17 @@ UAC2_ATTRIBUTE(s16, c_volume_min);
|
|||
UAC2_ATTRIBUTE(s16, c_volume_max);
|
||||
UAC2_ATTRIBUTE(s16, c_volume_res);
|
||||
UAC2_ATTRIBUTE(u32, fb_max);
|
||||
UAC2_ATTRIBUTE_STRING(function_name);
|
||||
|
||||
static struct configfs_attribute *f_uac2_attrs[] = {
|
||||
&f_uac2_opts_attr_p_chmask,
|
||||
&f_uac2_opts_attr_p_srate,
|
||||
&f_uac2_opts_attr_p_ssize,
|
||||
&f_uac2_opts_attr_p_hs_bint,
|
||||
&f_uac2_opts_attr_c_chmask,
|
||||
&f_uac2_opts_attr_c_srate,
|
||||
&f_uac2_opts_attr_c_ssize,
|
||||
&f_uac2_opts_attr_c_hs_bint,
|
||||
&f_uac2_opts_attr_c_sync,
|
||||
&f_uac2_opts_attr_req_number,
|
||||
&f_uac2_opts_attr_fb_max,
|
||||
|
@ -1881,6 +2107,8 @@ static struct configfs_attribute *f_uac2_attrs[] = {
|
|||
&f_uac2_opts_attr_c_volume_max,
|
||||
&f_uac2_opts_attr_c_volume_res,
|
||||
|
||||
&f_uac2_opts_attr_function_name,
|
||||
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
@ -1913,11 +2141,13 @@ static struct usb_function_instance *afunc_alloc_inst(void)
|
|||
&f_uac2_func_type);
|
||||
|
||||
opts->p_chmask = UAC2_DEF_PCHMASK;
|
||||
opts->p_srate = UAC2_DEF_PSRATE;
|
||||
opts->p_srates[0] = UAC2_DEF_PSRATE;
|
||||
opts->p_ssize = UAC2_DEF_PSSIZE;
|
||||
opts->p_hs_bint = UAC2_DEF_PHSBINT;
|
||||
opts->c_chmask = UAC2_DEF_CCHMASK;
|
||||
opts->c_srate = UAC2_DEF_CSRATE;
|
||||
opts->c_srates[0] = UAC2_DEF_CSRATE;
|
||||
opts->c_ssize = UAC2_DEF_CSSIZE;
|
||||
opts->c_hs_bint = UAC2_DEF_CHSBINT;
|
||||
opts->c_sync = UAC2_DEF_CSYNC;
|
||||
|
||||
opts->p_mute_present = UAC2_DEF_MUTE_PRESENT;
|
||||
|
@ -1934,6 +2164,9 @@ static struct usb_function_instance *afunc_alloc_inst(void)
|
|||
|
||||
opts->req_number = UAC2_DEF_REQ_NUM;
|
||||
opts->fb_max = FBACK_FAST_MAX;
|
||||
|
||||
snprintf(opts->function_name, sizeof(opts->function_name), "Source/Sink");
|
||||
|
||||
return &opts->func_inst;
|
||||
}
|
||||
|
||||
|
@ -1985,6 +2218,7 @@ static struct usb_function *afunc_alloc(struct usb_function_instance *fi)
|
|||
uac2->g_audio.func.set_alt = afunc_set_alt;
|
||||
uac2->g_audio.func.get_alt = afunc_get_alt;
|
||||
uac2->g_audio.func.disable = afunc_disable;
|
||||
uac2->g_audio.func.suspend = afunc_suspend;
|
||||
uac2->g_audio.func.setup = afunc_setup;
|
||||
uac2->g_audio.func.free_func = afunc_free;
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ enum {
|
|||
UAC_P_PITCH_CTRL,
|
||||
UAC_MUTE_CTRL,
|
||||
UAC_VOLUME_CTRL,
|
||||
UAC_RATE_CTRL,
|
||||
};
|
||||
|
||||
/* Runtime data params for one stream */
|
||||
|
@ -62,6 +63,10 @@ struct uac_rtd_params {
|
|||
s16 volume;
|
||||
int mute;
|
||||
|
||||
struct snd_kcontrol *snd_kctl_rate; /* read-only current rate */
|
||||
int srate; /* selected samplerate */
|
||||
int active; /* playback/capture running */
|
||||
|
||||
spinlock_t lock; /* lock for control transfers */
|
||||
|
||||
};
|
||||
|
@ -150,8 +155,6 @@ static void u_audio_iso_complete(struct usb_ep *ep, struct usb_request *req)
|
|||
struct snd_pcm_runtime *runtime;
|
||||
struct uac_rtd_params *prm = req->context;
|
||||
struct snd_uac_chip *uac = prm->uac;
|
||||
struct g_audio *audio_dev = uac->audio_dev;
|
||||
struct uac_params *params = &audio_dev->params;
|
||||
unsigned int frames, p_pktsize;
|
||||
unsigned long long pitched_rate_mil, p_pktsize_residue_mil,
|
||||
residue_frames_mil, div_result;
|
||||
|
@ -196,15 +199,14 @@ static void u_audio_iso_complete(struct usb_ep *ep, struct usb_request *req)
|
|||
*/
|
||||
unsigned long long p_interval_mil = uac->p_interval * 1000000ULL;
|
||||
|
||||
pitched_rate_mil = (unsigned long long)
|
||||
params->p_srate * prm->pitch;
|
||||
pitched_rate_mil = (unsigned long long) prm->srate * prm->pitch;
|
||||
div_result = pitched_rate_mil;
|
||||
do_div(div_result, uac->p_interval);
|
||||
do_div(div_result, 1000000);
|
||||
frames = (unsigned int) div_result;
|
||||
|
||||
pr_debug("p_srate %d, pitch %d, interval_mil %llu, frames %d\n",
|
||||
params->p_srate, prm->pitch, p_interval_mil, frames);
|
||||
prm->srate, prm->pitch, p_interval_mil, frames);
|
||||
|
||||
p_pktsize = min_t(unsigned int,
|
||||
uac->p_framesize * frames,
|
||||
|
@ -281,7 +283,6 @@ static void u_audio_iso_fback_complete(struct usb_ep *ep,
|
|||
struct uac_rtd_params *prm = req->context;
|
||||
struct snd_uac_chip *uac = prm->uac;
|
||||
struct g_audio *audio_dev = uac->audio_dev;
|
||||
struct uac_params *params = &audio_dev->params;
|
||||
int status = req->status;
|
||||
|
||||
/* i/f shutting down */
|
||||
|
@ -303,7 +304,7 @@ static void u_audio_iso_fback_complete(struct usb_ep *ep,
|
|||
__func__, status, req->actual, req->length);
|
||||
|
||||
u_audio_set_fback_frequency(audio_dev->gadget->speed, audio_dev->out_ep,
|
||||
params->c_srate, prm->pitch,
|
||||
prm->srate, prm->pitch,
|
||||
req->buf);
|
||||
|
||||
if (usb_ep_queue(ep, req, GFP_ATOMIC))
|
||||
|
@ -387,16 +388,14 @@ static int uac_pcm_open(struct snd_pcm_substream *substream)
|
|||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct g_audio *audio_dev;
|
||||
struct uac_params *params;
|
||||
struct uac_rtd_params *prm;
|
||||
int p_ssize, c_ssize;
|
||||
int p_srate, c_srate;
|
||||
int p_chmask, c_chmask;
|
||||
|
||||
audio_dev = uac->audio_dev;
|
||||
params = &audio_dev->params;
|
||||
p_ssize = params->p_ssize;
|
||||
c_ssize = params->c_ssize;
|
||||
p_srate = params->p_srate;
|
||||
c_srate = params->c_srate;
|
||||
p_chmask = params->p_chmask;
|
||||
c_chmask = params->c_chmask;
|
||||
uac->p_residue_mil = 0;
|
||||
|
@ -404,19 +403,18 @@ static int uac_pcm_open(struct snd_pcm_substream *substream)
|
|||
runtime->hw = uac_pcm_hardware;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
runtime->hw.rate_min = p_srate;
|
||||
runtime->hw.formats = uac_ssize_to_fmt(p_ssize);
|
||||
runtime->hw.channels_min = num_channels(p_chmask);
|
||||
runtime->hw.period_bytes_min = 2 * uac->p_prm.max_psize
|
||||
/ runtime->hw.periods_min;
|
||||
prm = &uac->p_prm;
|
||||
} else {
|
||||
runtime->hw.rate_min = c_srate;
|
||||
runtime->hw.formats = uac_ssize_to_fmt(c_ssize);
|
||||
runtime->hw.channels_min = num_channels(c_chmask);
|
||||
runtime->hw.period_bytes_min = 2 * uac->c_prm.max_psize
|
||||
/ runtime->hw.periods_min;
|
||||
prm = &uac->c_prm;
|
||||
}
|
||||
|
||||
runtime->hw.period_bytes_min = 2 * prm->max_psize
|
||||
/ runtime->hw.periods_min;
|
||||
runtime->hw.rate_min = prm->srate;
|
||||
runtime->hw.rate_max = runtime->hw.rate_min;
|
||||
runtime->hw.channels_max = runtime->hw.channels_min;
|
||||
|
||||
|
@ -493,6 +491,99 @@ static inline void free_ep_fback(struct uac_rtd_params *prm, struct usb_ep *ep)
|
|||
dev_err(uac->card->dev, "%s:%d Error!\n", __func__, __LINE__);
|
||||
}
|
||||
|
||||
static void set_active(struct uac_rtd_params *prm, bool active)
|
||||
{
|
||||
// notifying through the Rate ctrl
|
||||
struct snd_kcontrol *kctl = prm->snd_kctl_rate;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&prm->lock, flags);
|
||||
if (prm->active != active) {
|
||||
prm->active = active;
|
||||
snd_ctl_notify(prm->uac->card, SNDRV_CTL_EVENT_MASK_VALUE,
|
||||
&kctl->id);
|
||||
}
|
||||
spin_unlock_irqrestore(&prm->lock, flags);
|
||||
}
|
||||
|
||||
int u_audio_set_capture_srate(struct g_audio *audio_dev, int srate)
|
||||
{
|
||||
struct uac_params *params = &audio_dev->params;
|
||||
struct snd_uac_chip *uac = audio_dev->uac;
|
||||
struct uac_rtd_params *prm;
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
dev_dbg(&audio_dev->gadget->dev, "%s: srate %d\n", __func__, srate);
|
||||
prm = &uac->c_prm;
|
||||
for (i = 0; i < UAC_MAX_RATES; i++) {
|
||||
if (params->c_srates[i] == srate) {
|
||||
spin_lock_irqsave(&prm->lock, flags);
|
||||
prm->srate = srate;
|
||||
spin_unlock_irqrestore(&prm->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
if (params->c_srates[i] == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(u_audio_set_capture_srate);
|
||||
|
||||
int u_audio_get_capture_srate(struct g_audio *audio_dev, u32 *val)
|
||||
{
|
||||
struct snd_uac_chip *uac = audio_dev->uac;
|
||||
struct uac_rtd_params *prm;
|
||||
unsigned long flags;
|
||||
|
||||
prm = &uac->c_prm;
|
||||
spin_lock_irqsave(&prm->lock, flags);
|
||||
*val = prm->srate;
|
||||
spin_unlock_irqrestore(&prm->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(u_audio_get_capture_srate);
|
||||
|
||||
int u_audio_set_playback_srate(struct g_audio *audio_dev, int srate)
|
||||
{
|
||||
struct uac_params *params = &audio_dev->params;
|
||||
struct snd_uac_chip *uac = audio_dev->uac;
|
||||
struct uac_rtd_params *prm;
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
dev_dbg(&audio_dev->gadget->dev, "%s: srate %d\n", __func__, srate);
|
||||
prm = &uac->p_prm;
|
||||
for (i = 0; i < UAC_MAX_RATES; i++) {
|
||||
if (params->p_srates[i] == srate) {
|
||||
spin_lock_irqsave(&prm->lock, flags);
|
||||
prm->srate = srate;
|
||||
spin_unlock_irqrestore(&prm->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
if (params->p_srates[i] == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(u_audio_set_playback_srate);
|
||||
|
||||
int u_audio_get_playback_srate(struct g_audio *audio_dev, u32 *val)
|
||||
{
|
||||
struct snd_uac_chip *uac = audio_dev->uac;
|
||||
struct uac_rtd_params *prm;
|
||||
unsigned long flags;
|
||||
|
||||
prm = &uac->p_prm;
|
||||
spin_lock_irqsave(&prm->lock, flags);
|
||||
*val = prm->srate;
|
||||
spin_unlock_irqrestore(&prm->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(u_audio_get_playback_srate);
|
||||
|
||||
int u_audio_start_capture(struct g_audio *audio_dev)
|
||||
{
|
||||
struct snd_uac_chip *uac = audio_dev->uac;
|
||||
|
@ -504,8 +595,9 @@ int u_audio_start_capture(struct g_audio *audio_dev)
|
|||
struct uac_params *params = &audio_dev->params;
|
||||
int req_len, i;
|
||||
|
||||
ep = audio_dev->out_ep;
|
||||
prm = &uac->c_prm;
|
||||
dev_dbg(dev, "start capture with rate %d\n", prm->srate);
|
||||
ep = audio_dev->out_ep;
|
||||
config_ep_by_speed(gadget, &audio_dev->func, ep);
|
||||
req_len = ep->maxpacket;
|
||||
|
||||
|
@ -531,6 +623,8 @@ int u_audio_start_capture(struct g_audio *audio_dev)
|
|||
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
|
||||
}
|
||||
|
||||
set_active(&uac->c_prm, true);
|
||||
|
||||
ep_fback = audio_dev->in_ep_fback;
|
||||
if (!ep_fback)
|
||||
return 0;
|
||||
|
@ -562,7 +656,7 @@ int u_audio_start_capture(struct g_audio *audio_dev)
|
|||
*/
|
||||
prm->pitch = 1000000;
|
||||
u_audio_set_fback_frequency(audio_dev->gadget->speed, ep,
|
||||
params->c_srate, prm->pitch,
|
||||
prm->srate, prm->pitch,
|
||||
req_fback->buf);
|
||||
|
||||
if (usb_ep_queue(ep_fback, req_fback, GFP_ATOMIC))
|
||||
|
@ -576,6 +670,7 @@ void u_audio_stop_capture(struct g_audio *audio_dev)
|
|||
{
|
||||
struct snd_uac_chip *uac = audio_dev->uac;
|
||||
|
||||
set_active(&uac->c_prm, false);
|
||||
if (audio_dev->in_ep_fback)
|
||||
free_ep_fback(&uac->c_prm, audio_dev->in_ep_fback);
|
||||
free_ep(&uac->c_prm, audio_dev->out_ep);
|
||||
|
@ -596,8 +691,9 @@ int u_audio_start_playback(struct g_audio *audio_dev)
|
|||
int req_len, i;
|
||||
unsigned int p_pktsize;
|
||||
|
||||
ep = audio_dev->in_ep;
|
||||
prm = &uac->p_prm;
|
||||
dev_dbg(dev, "start playback with rate %d\n", prm->srate);
|
||||
ep = audio_dev->in_ep;
|
||||
config_ep_by_speed(gadget, &audio_dev->func, ep);
|
||||
|
||||
ep_desc = ep->desc;
|
||||
|
@ -618,7 +714,7 @@ int u_audio_start_playback(struct g_audio *audio_dev)
|
|||
uac->p_interval = factor / (1 << (ep_desc->bInterval - 1));
|
||||
p_pktsize = min_t(unsigned int,
|
||||
uac->p_framesize *
|
||||
(params->p_srate / uac->p_interval),
|
||||
(prm->srate / uac->p_interval),
|
||||
ep->maxpacket);
|
||||
|
||||
req_len = p_pktsize;
|
||||
|
@ -646,6 +742,8 @@ int u_audio_start_playback(struct g_audio *audio_dev)
|
|||
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
|
||||
}
|
||||
|
||||
set_active(&uac->p_prm, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(u_audio_start_playback);
|
||||
|
@ -654,10 +752,20 @@ void u_audio_stop_playback(struct g_audio *audio_dev)
|
|||
{
|
||||
struct snd_uac_chip *uac = audio_dev->uac;
|
||||
|
||||
set_active(&uac->p_prm, false);
|
||||
free_ep(&uac->p_prm, audio_dev->in_ep);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(u_audio_stop_playback);
|
||||
|
||||
void u_audio_suspend(struct g_audio *audio_dev)
|
||||
{
|
||||
struct snd_uac_chip *uac = audio_dev->uac;
|
||||
|
||||
set_active(&uac->p_prm, false);
|
||||
set_active(&uac->c_prm, false);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(u_audio_suspend);
|
||||
|
||||
int u_audio_get_volume(struct g_audio *audio_dev, int playback, s16 *val)
|
||||
{
|
||||
struct snd_uac_chip *uac = audio_dev->uac;
|
||||
|
@ -943,6 +1051,68 @@ static int u_audio_volume_put(struct snd_kcontrol *kcontrol,
|
|||
return change;
|
||||
}
|
||||
|
||||
static int get_max_srate(const int *srates)
|
||||
{
|
||||
int i, max_srate = 0;
|
||||
|
||||
for (i = 0; i < UAC_MAX_RATES; i++) {
|
||||
if (srates[i] == 0)
|
||||
break;
|
||||
if (srates[i] > max_srate)
|
||||
max_srate = srates[i];
|
||||
}
|
||||
return max_srate;
|
||||
}
|
||||
|
||||
static int get_min_srate(const int *srates)
|
||||
{
|
||||
int i, min_srate = INT_MAX;
|
||||
|
||||
for (i = 0; i < UAC_MAX_RATES; i++) {
|
||||
if (srates[i] == 0)
|
||||
break;
|
||||
if (srates[i] < min_srate)
|
||||
min_srate = srates[i];
|
||||
}
|
||||
return min_srate;
|
||||
}
|
||||
|
||||
static int u_audio_rate_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
const int *srates;
|
||||
struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol);
|
||||
struct snd_uac_chip *uac = prm->uac;
|
||||
struct g_audio *audio_dev = uac->audio_dev;
|
||||
struct uac_params *params = &audio_dev->params;
|
||||
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = 1;
|
||||
|
||||
if (prm == &uac->c_prm)
|
||||
srates = params->c_srates;
|
||||
else
|
||||
srates = params->p_srates;
|
||||
uinfo->value.integer.min = get_min_srate(srates);
|
||||
uinfo->value.integer.max = get_max_srate(srates);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int u_audio_rate_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&prm->lock, flags);
|
||||
if (prm->active)
|
||||
ucontrol->value.integer.value[0] = prm->srate;
|
||||
else
|
||||
/* not active: reporting zero rate */
|
||||
ucontrol->value.integer.value[0] = 0;
|
||||
spin_unlock_irqrestore(&prm->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_kcontrol_new u_audio_controls[] = {
|
||||
[UAC_FBACK_CTRL] {
|
||||
|
@ -973,6 +1143,13 @@ static struct snd_kcontrol_new u_audio_controls[] = {
|
|||
.get = u_audio_volume_get,
|
||||
.put = u_audio_volume_put,
|
||||
},
|
||||
[UAC_RATE_CTRL] {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "", /* will be filled later */
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.info = u_audio_rate_info,
|
||||
.get = u_audio_rate_get,
|
||||
},
|
||||
};
|
||||
|
||||
int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
|
||||
|
@ -1005,6 +1182,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
|
|||
spin_lock_init(&prm->lock);
|
||||
uac->c_prm.uac = uac;
|
||||
prm->max_psize = g_audio->out_ep_maxpsize;
|
||||
prm->srate = params->c_srates[0];
|
||||
|
||||
prm->reqs = kcalloc(params->req_number,
|
||||
sizeof(struct usb_request *),
|
||||
|
@ -1029,6 +1207,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
|
|||
spin_lock_init(&prm->lock);
|
||||
uac->p_prm.uac = uac;
|
||||
prm->max_psize = g_audio->in_ep_maxpsize;
|
||||
prm->srate = params->p_srates[0];
|
||||
|
||||
prm->reqs = kcalloc(params->req_number,
|
||||
sizeof(struct usb_request *),
|
||||
|
@ -1186,6 +1365,25 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
|
|||
prm->volume_min = fu->volume_min;
|
||||
prm->volume_res = fu->volume_res;
|
||||
}
|
||||
|
||||
/* Add rate control */
|
||||
snprintf(ctrl_name, sizeof(ctrl_name),
|
||||
"%s Rate", direction);
|
||||
u_audio_controls[UAC_RATE_CTRL].name = ctrl_name;
|
||||
|
||||
kctl = snd_ctl_new1(&u_audio_controls[UAC_RATE_CTRL], prm);
|
||||
if (!kctl) {
|
||||
err = -ENOMEM;
|
||||
goto snd_fail;
|
||||
}
|
||||
|
||||
kctl->id.device = pcm->device;
|
||||
kctl->id.subdevice = 0;
|
||||
|
||||
err = snd_ctl_add(card, kctl);
|
||||
if (err < 0)
|
||||
goto snd_fail;
|
||||
prm->snd_kctl_rate = kctl;
|
||||
}
|
||||
|
||||
strscpy(card->driver, card_name, sizeof(card->driver));
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#define __U_AUDIO_H
|
||||
|
||||
#include <linux/usb/composite.h>
|
||||
#include "uac_common.h"
|
||||
|
||||
/*
|
||||
* Same maximum frequency deviation on the slower side as in
|
||||
|
@ -40,16 +41,18 @@ struct uac_fu_params {
|
|||
struct uac_params {
|
||||
/* playback */
|
||||
int p_chmask; /* channel mask */
|
||||
int p_srate; /* rate in Hz */
|
||||
int p_srates[UAC_MAX_RATES]; /* available rates in Hz (0 terminated list) */
|
||||
int p_ssize; /* sample size */
|
||||
struct uac_fu_params p_fu; /* Feature Unit parameters */
|
||||
|
||||
/* capture */
|
||||
int c_chmask; /* channel mask */
|
||||
int c_srate; /* rate in Hz */
|
||||
int c_srates[UAC_MAX_RATES]; /* available rates in Hz (0 terminated list) */
|
||||
int c_ssize; /* sample size */
|
||||
struct uac_fu_params c_fu; /* Feature Unit parameters */
|
||||
|
||||
/* rates are dynamic, in uac_rtd_params */
|
||||
|
||||
int req_number; /* number of preallocated requests */
|
||||
int fb_max; /* upper frequency drift feedback limit per-mil */
|
||||
};
|
||||
|
@ -117,9 +120,16 @@ void u_audio_stop_capture(struct g_audio *g_audio);
|
|||
int u_audio_start_playback(struct g_audio *g_audio);
|
||||
void u_audio_stop_playback(struct g_audio *g_audio);
|
||||
|
||||
int u_audio_get_capture_srate(struct g_audio *audio_dev, u32 *val);
|
||||
int u_audio_set_capture_srate(struct g_audio *audio_dev, int srate);
|
||||
int u_audio_get_playback_srate(struct g_audio *audio_dev, u32 *val);
|
||||
int u_audio_set_playback_srate(struct g_audio *audio_dev, int srate);
|
||||
|
||||
int u_audio_get_volume(struct g_audio *g_audio, int playback, s16 *val);
|
||||
int u_audio_set_volume(struct g_audio *g_audio, int playback, s16 val);
|
||||
int u_audio_get_mute(struct g_audio *g_audio, int playback, int *val);
|
||||
int u_audio_set_mute(struct g_audio *g_audio, int playback, int val);
|
||||
|
||||
void u_audio_suspend(struct g_audio *g_audio);
|
||||
|
||||
#endif /* __U_AUDIO_H */
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#define __U_UAC1_H
|
||||
|
||||
#include <linux/usb/composite.h>
|
||||
#include "uac_common.h"
|
||||
|
||||
#define UAC1_OUT_EP_MAX_PACKET_SIZE 200
|
||||
#define UAC1_DEF_CCHMASK 0x3
|
||||
|
@ -30,10 +31,10 @@
|
|||
struct f_uac1_opts {
|
||||
struct usb_function_instance func_inst;
|
||||
int c_chmask;
|
||||
int c_srate;
|
||||
int c_srates[UAC_MAX_RATES];
|
||||
int c_ssize;
|
||||
int p_chmask;
|
||||
int p_srate;
|
||||
int p_srates[UAC_MAX_RATES];
|
||||
int p_ssize;
|
||||
|
||||
bool p_mute_present;
|
||||
|
@ -51,6 +52,8 @@ struct f_uac1_opts {
|
|||
int req_number;
|
||||
unsigned bound:1;
|
||||
|
||||
char function_name[32];
|
||||
|
||||
struct mutex lock;
|
||||
int refcnt;
|
||||
};
|
||||
|
|
|
@ -14,13 +14,16 @@
|
|||
#define U_UAC2_H
|
||||
|
||||
#include <linux/usb/composite.h>
|
||||
#include "uac_common.h"
|
||||
|
||||
#define UAC2_DEF_PCHMASK 0x3
|
||||
#define UAC2_DEF_PSRATE 48000
|
||||
#define UAC2_DEF_PSSIZE 2
|
||||
#define UAC2_DEF_PHSBINT 0
|
||||
#define UAC2_DEF_CCHMASK 0x3
|
||||
#define UAC2_DEF_CSRATE 64000
|
||||
#define UAC2_DEF_CSSIZE 2
|
||||
#define UAC2_DEF_CHSBINT 0
|
||||
#define UAC2_DEF_CSYNC USB_ENDPOINT_SYNC_ASYNC
|
||||
|
||||
#define UAC2_DEF_MUTE_PRESENT 1
|
||||
|
@ -35,12 +38,14 @@
|
|||
struct f_uac2_opts {
|
||||
struct usb_function_instance func_inst;
|
||||
int p_chmask;
|
||||
int p_srate;
|
||||
int p_srates[UAC_MAX_RATES];
|
||||
int p_ssize;
|
||||
u8 p_hs_bint;
|
||||
int c_chmask;
|
||||
int c_srate;
|
||||
int c_srates[UAC_MAX_RATES];
|
||||
int c_ssize;
|
||||
int c_sync;
|
||||
u8 c_hs_bint;
|
||||
|
||||
bool p_mute_present;
|
||||
bool p_volume_present;
|
||||
|
@ -58,6 +63,8 @@ struct f_uac2_opts {
|
|||
int fb_max;
|
||||
bool bound;
|
||||
|
||||
char function_name[32];
|
||||
|
||||
struct mutex lock;
|
||||
int refcnt;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
*/
|
||||
|
||||
#ifndef UAC_COMMON_H
|
||||
#define UAC_COMMON_H
|
||||
|
||||
#define UAC_MAX_RATES 10 /* maximum number of rates configurable by f_uac1/2 */
|
||||
#endif
|
|
@ -22,91 +22,108 @@ USB_GADGET_COMPOSITE_OPTIONS();
|
|||
|
||||
/* Playback(USB-IN) Default Stereo - Fl/Fr */
|
||||
static int p_chmask = UAC2_DEF_PCHMASK;
|
||||
module_param(p_chmask, uint, S_IRUGO);
|
||||
module_param(p_chmask, uint, 0444);
|
||||
MODULE_PARM_DESC(p_chmask, "Playback Channel Mask");
|
||||
|
||||
/* Playback Default 48 KHz */
|
||||
static int p_srate = UAC2_DEF_PSRATE;
|
||||
module_param(p_srate, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(p_srate, "Playback Sampling Rate");
|
||||
static int p_srates[UAC_MAX_RATES] = {UAC2_DEF_PSRATE};
|
||||
static int p_srates_cnt = 1;
|
||||
module_param_array_named(p_srate, p_srates, uint, &p_srates_cnt, 0444);
|
||||
MODULE_PARM_DESC(p_srate, "Playback Sampling Rates (array)");
|
||||
|
||||
/* Playback Default 16bits/sample */
|
||||
static int p_ssize = UAC2_DEF_PSSIZE;
|
||||
module_param(p_ssize, uint, S_IRUGO);
|
||||
module_param(p_ssize, uint, 0444);
|
||||
MODULE_PARM_DESC(p_ssize, "Playback Sample Size(bytes)");
|
||||
|
||||
/* Playback bInterval for HS/SS (1-4: fixed, 0: auto) */
|
||||
static u8 p_hs_bint = UAC2_DEF_PHSBINT;
|
||||
module_param(p_hs_bint, byte, 0444);
|
||||
MODULE_PARM_DESC(p_hs_bint,
|
||||
"Playback bInterval for HS/SS (1-4: fixed, 0: auto)");
|
||||
|
||||
/* Capture(USB-OUT) Default Stereo - Fl/Fr */
|
||||
static int c_chmask = UAC2_DEF_CCHMASK;
|
||||
module_param(c_chmask, uint, S_IRUGO);
|
||||
module_param(c_chmask, uint, 0444);
|
||||
MODULE_PARM_DESC(c_chmask, "Capture Channel Mask");
|
||||
|
||||
/* Capture Default 64 KHz */
|
||||
static int c_srate = UAC2_DEF_CSRATE;
|
||||
module_param(c_srate, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(c_srate, "Capture Sampling Rate");
|
||||
static int c_srates[UAC_MAX_RATES] = {UAC2_DEF_CSRATE};
|
||||
static int c_srates_cnt = 1;
|
||||
module_param_array_named(c_srate, c_srates, uint, &c_srates_cnt, 0444);
|
||||
MODULE_PARM_DESC(c_srate, "Capture Sampling Rates (array)");
|
||||
|
||||
/* Capture Default 16bits/sample */
|
||||
static int c_ssize = UAC2_DEF_CSSIZE;
|
||||
module_param(c_ssize, uint, S_IRUGO);
|
||||
module_param(c_ssize, uint, 0444);
|
||||
MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)");
|
||||
|
||||
/* capture bInterval for HS/SS (1-4: fixed, 0: auto) */
|
||||
static u8 c_hs_bint = UAC2_DEF_CHSBINT;
|
||||
module_param(c_hs_bint, byte, 0444);
|
||||
MODULE_PARM_DESC(c_hs_bint,
|
||||
"Capture bInterval for HS/SS (1-4: fixed, 0: auto)");
|
||||
|
||||
#else
|
||||
#ifndef CONFIG_GADGET_UAC1_LEGACY
|
||||
#include "u_uac1.h"
|
||||
|
||||
/* Playback(USB-IN) Default Stereo - Fl/Fr */
|
||||
static int p_chmask = UAC1_DEF_PCHMASK;
|
||||
module_param(p_chmask, uint, S_IRUGO);
|
||||
module_param(p_chmask, uint, 0444);
|
||||
MODULE_PARM_DESC(p_chmask, "Playback Channel Mask");
|
||||
|
||||
/* Playback Default 48 KHz */
|
||||
static int p_srate = UAC1_DEF_PSRATE;
|
||||
module_param(p_srate, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(p_srate, "Playback Sampling Rate");
|
||||
static int p_srates[UAC_MAX_RATES] = {UAC1_DEF_PSRATE};
|
||||
static int p_srates_cnt = 1;
|
||||
module_param_array_named(p_srate, p_srates, uint, &p_srates_cnt, 0444);
|
||||
MODULE_PARM_DESC(p_srate, "Playback Sampling Rates (array)");
|
||||
|
||||
/* Playback Default 16bits/sample */
|
||||
static int p_ssize = UAC1_DEF_PSSIZE;
|
||||
module_param(p_ssize, uint, S_IRUGO);
|
||||
module_param(p_ssize, uint, 0444);
|
||||
MODULE_PARM_DESC(p_ssize, "Playback Sample Size(bytes)");
|
||||
|
||||
/* Capture(USB-OUT) Default Stereo - Fl/Fr */
|
||||
static int c_chmask = UAC1_DEF_CCHMASK;
|
||||
module_param(c_chmask, uint, S_IRUGO);
|
||||
module_param(c_chmask, uint, 0444);
|
||||
MODULE_PARM_DESC(c_chmask, "Capture Channel Mask");
|
||||
|
||||
/* Capture Default 48 KHz */
|
||||
static int c_srate = UAC1_DEF_CSRATE;
|
||||
module_param(c_srate, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(c_srate, "Capture Sampling Rate");
|
||||
static int c_srates[UAC_MAX_RATES] = {UAC1_DEF_CSRATE};
|
||||
static int c_srates_cnt = 1;
|
||||
module_param_array_named(c_srate, c_srates, uint, &c_srates_cnt, 0444);
|
||||
MODULE_PARM_DESC(c_srate, "Capture Sampling Rates (array)");
|
||||
|
||||
/* Capture Default 16bits/sample */
|
||||
static int c_ssize = UAC1_DEF_CSSIZE;
|
||||
module_param(c_ssize, uint, S_IRUGO);
|
||||
module_param(c_ssize, uint, 0444);
|
||||
MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)");
|
||||
#else /* CONFIG_GADGET_UAC1_LEGACY */
|
||||
#include "u_uac1_legacy.h"
|
||||
|
||||
static char *fn_play = FILE_PCM_PLAYBACK;
|
||||
module_param(fn_play, charp, S_IRUGO);
|
||||
module_param(fn_play, charp, 0444);
|
||||
MODULE_PARM_DESC(fn_play, "Playback PCM device file name");
|
||||
|
||||
static char *fn_cap = FILE_PCM_CAPTURE;
|
||||
module_param(fn_cap, charp, S_IRUGO);
|
||||
module_param(fn_cap, charp, 0444);
|
||||
MODULE_PARM_DESC(fn_cap, "Capture PCM device file name");
|
||||
|
||||
static char *fn_cntl = FILE_CONTROL;
|
||||
module_param(fn_cntl, charp, S_IRUGO);
|
||||
module_param(fn_cntl, charp, 0444);
|
||||
MODULE_PARM_DESC(fn_cntl, "Control device file name");
|
||||
|
||||
static int req_buf_size = UAC1_OUT_EP_MAX_PACKET_SIZE;
|
||||
module_param(req_buf_size, int, S_IRUGO);
|
||||
module_param(req_buf_size, int, 0444);
|
||||
MODULE_PARM_DESC(req_buf_size, "ISO OUT endpoint request buffer size");
|
||||
|
||||
static int req_count = UAC1_REQ_COUNT;
|
||||
module_param(req_count, int, S_IRUGO);
|
||||
module_param(req_count, int, 0444);
|
||||
MODULE_PARM_DESC(req_count, "ISO OUT endpoint request count");
|
||||
|
||||
static int audio_buf_size = UAC1_AUDIO_BUF_SIZE;
|
||||
module_param(audio_buf_size, int, S_IRUGO);
|
||||
module_param(audio_buf_size, int, 0444);
|
||||
MODULE_PARM_DESC(audio_buf_size, "Audio buffer size");
|
||||
#endif /* CONFIG_GADGET_UAC1_LEGACY */
|
||||
#endif
|
||||
|
@ -237,9 +254,11 @@ static int audio_bind(struct usb_composite_dev *cdev)
|
|||
{
|
||||
#ifndef CONFIG_GADGET_UAC1
|
||||
struct f_uac2_opts *uac2_opts;
|
||||
int i;
|
||||
#else
|
||||
#ifndef CONFIG_GADGET_UAC1_LEGACY
|
||||
struct f_uac1_opts *uac1_opts;
|
||||
int i;
|
||||
#else
|
||||
struct f_uac1_legacy_opts *uac1_opts;
|
||||
#endif
|
||||
|
@ -263,20 +282,34 @@ static int audio_bind(struct usb_composite_dev *cdev)
|
|||
#ifndef CONFIG_GADGET_UAC1
|
||||
uac2_opts = container_of(fi_uac2, struct f_uac2_opts, func_inst);
|
||||
uac2_opts->p_chmask = p_chmask;
|
||||
uac2_opts->p_srate = p_srate;
|
||||
|
||||
for (i = 0; i < p_srates_cnt; ++i)
|
||||
uac2_opts->p_srates[i] = p_srates[i];
|
||||
|
||||
uac2_opts->p_ssize = p_ssize;
|
||||
uac2_opts->p_hs_bint = p_hs_bint;
|
||||
uac2_opts->c_chmask = c_chmask;
|
||||
uac2_opts->c_srate = c_srate;
|
||||
|
||||
for (i = 0; i < c_srates_cnt; ++i)
|
||||
uac2_opts->c_srates[i] = c_srates[i];
|
||||
|
||||
uac2_opts->c_ssize = c_ssize;
|
||||
uac2_opts->c_hs_bint = c_hs_bint;
|
||||
uac2_opts->req_number = UAC2_DEF_REQ_NUM;
|
||||
#else
|
||||
#ifndef CONFIG_GADGET_UAC1_LEGACY
|
||||
uac1_opts = container_of(fi_uac1, struct f_uac1_opts, func_inst);
|
||||
uac1_opts->p_chmask = p_chmask;
|
||||
uac1_opts->p_srate = p_srate;
|
||||
|
||||
for (i = 0; i < p_srates_cnt; ++i)
|
||||
uac1_opts->p_srates[i] = p_srates[i];
|
||||
|
||||
uac1_opts->p_ssize = p_ssize;
|
||||
uac1_opts->c_chmask = c_chmask;
|
||||
uac1_opts->c_srate = c_srate;
|
||||
|
||||
for (i = 0; i < c_srates_cnt; ++i)
|
||||
uac1_opts->c_srates[i] = c_srates[i];
|
||||
|
||||
uac1_opts->c_ssize = c_ssize;
|
||||
uac1_opts->req_number = UAC1_DEF_REQ_NUM;
|
||||
#else /* CONFIG_GADGET_UAC1_LEGACY */
|
||||
|
|
|
@ -134,7 +134,7 @@ static int hid_bind(struct usb_composite_dev *cdev)
|
|||
{
|
||||
struct usb_gadget *gadget = cdev->gadget;
|
||||
struct list_head *tmp;
|
||||
struct hidg_func_node *n, *m;
|
||||
struct hidg_func_node *n = NULL, *m, *iter_n;
|
||||
struct f_hid_opts *hid_opts;
|
||||
int status, funcs = 0;
|
||||
|
||||
|
@ -144,18 +144,19 @@ static int hid_bind(struct usb_composite_dev *cdev)
|
|||
if (!funcs)
|
||||
return -ENODEV;
|
||||
|
||||
list_for_each_entry(n, &hidg_func_list, node) {
|
||||
n->fi = usb_get_function_instance("hid");
|
||||
if (IS_ERR(n->fi)) {
|
||||
status = PTR_ERR(n->fi);
|
||||
list_for_each_entry(iter_n, &hidg_func_list, node) {
|
||||
iter_n->fi = usb_get_function_instance("hid");
|
||||
if (IS_ERR(iter_n->fi)) {
|
||||
status = PTR_ERR(iter_n->fi);
|
||||
n = iter_n;
|
||||
goto put;
|
||||
}
|
||||
hid_opts = container_of(n->fi, struct f_hid_opts, func_inst);
|
||||
hid_opts->subclass = n->func->subclass;
|
||||
hid_opts->protocol = n->func->protocol;
|
||||
hid_opts->report_length = n->func->report_length;
|
||||
hid_opts->report_desc_length = n->func->report_desc_length;
|
||||
hid_opts->report_desc = n->func->report_desc;
|
||||
hid_opts = container_of(iter_n->fi, struct f_hid_opts, func_inst);
|
||||
hid_opts->subclass = iter_n->func->subclass;
|
||||
hid_opts->protocol = iter_n->func->protocol;
|
||||
hid_opts->report_length = iter_n->func->report_length;
|
||||
hid_opts->report_desc_length = iter_n->func->report_desc_length;
|
||||
hid_opts->report_desc = iter_n->func->report_desc;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -2101,7 +2101,7 @@ MODULE_ALIAS_FS("gadgetfs");
|
|||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
static int __init init (void)
|
||||
static int __init gadgetfs_init (void)
|
||||
{
|
||||
int status;
|
||||
|
||||
|
@ -2111,12 +2111,12 @@ static int __init init (void)
|
|||
shortname, driver_desc);
|
||||
return status;
|
||||
}
|
||||
module_init (init);
|
||||
module_init (gadgetfs_init);
|
||||
|
||||
static void __exit cleanup (void)
|
||||
static void __exit gadgetfs_cleanup (void)
|
||||
{
|
||||
pr_debug ("unregister %s\n", shortname);
|
||||
unregister_filesystem (&gadgetfs_type);
|
||||
}
|
||||
module_exit (cleanup);
|
||||
module_exit (gadgetfs_cleanup);
|
||||
|
||||
|
|
|
@ -758,6 +758,7 @@ static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value)
|
|||
unsigned long flags;
|
||||
struct usb_endpoint_descriptor *desc;
|
||||
struct raw_ep *ep;
|
||||
bool ep_props_matched = false;
|
||||
|
||||
desc = memdup_user((void __user *)value, sizeof(*desc));
|
||||
if (IS_ERR(desc))
|
||||
|
@ -787,13 +788,14 @@ static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value)
|
|||
|
||||
for (i = 0; i < dev->eps_num; i++) {
|
||||
ep = &dev->eps[i];
|
||||
if (ep->state != STATE_EP_DISABLED)
|
||||
continue;
|
||||
if (ep->addr != usb_endpoint_num(desc) &&
|
||||
ep->addr != USB_RAW_EP_ADDR_ANY)
|
||||
continue;
|
||||
if (!usb_gadget_ep_match_desc(dev->gadget, ep->ep, desc, NULL))
|
||||
continue;
|
||||
ep_props_matched = true;
|
||||
if (ep->state != STATE_EP_DISABLED)
|
||||
continue;
|
||||
ep->ep->desc = desc;
|
||||
ret = usb_ep_enable(ep->ep);
|
||||
if (ret < 0) {
|
||||
|
@ -815,8 +817,13 @@ static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value)
|
|||
goto out_unlock;
|
||||
}
|
||||
|
||||
dev_dbg(&dev->gadget->dev, "fail, no gadget endpoints available\n");
|
||||
ret = -EBUSY;
|
||||
if (!ep_props_matched) {
|
||||
dev_dbg(&dev->gadget->dev, "fail, bad endpoint descriptor\n");
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
dev_dbg(&dev->gadget->dev, "fail, no endpoints available\n");
|
||||
ret = -EBUSY;
|
||||
}
|
||||
|
||||
out_free:
|
||||
kfree(desc);
|
||||
|
@ -1157,7 +1164,7 @@ static int raw_ioctl_eps_info(struct raw_dev *dev, unsigned long value)
|
|||
struct usb_raw_eps_info *info;
|
||||
struct raw_ep *ep;
|
||||
|
||||
info = kmalloc(sizeof(*info), GFP_KERNEL);
|
||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (!info) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
|
@ -1177,7 +1184,6 @@ static int raw_ioctl_eps_info(struct raw_dev *dev, unsigned long value)
|
|||
goto out_free;
|
||||
}
|
||||
|
||||
memset(info, 0, sizeof(*info));
|
||||
for (i = 0; i < dev->eps_num; i++) {
|
||||
ep = &dev->eps[i];
|
||||
strscpy(&info->eps[i].name[0], ep->ep->name,
|
||||
|
|
|
@ -273,7 +273,7 @@ static struct usb_composite_driver gserial_driver = {
|
|||
static int switch_gserial_enable(bool do_enable)
|
||||
{
|
||||
if (!serial_config_driver.label)
|
||||
/* init() was not called, yet */
|
||||
/* gserial_init() was not called, yet */
|
||||
return 0;
|
||||
|
||||
if (do_enable)
|
||||
|
@ -283,7 +283,7 @@ static int switch_gserial_enable(bool do_enable)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int __init init(void)
|
||||
static int __init gserial_init(void)
|
||||
{
|
||||
/* We *could* export two configs; that'd be much cleaner...
|
||||
* but neither of these product IDs was defined that way.
|
||||
|
@ -314,11 +314,11 @@ static int __init init(void)
|
|||
|
||||
return usb_composite_probe(&gserial_driver);
|
||||
}
|
||||
module_init(init);
|
||||
module_init(gserial_init);
|
||||
|
||||
static void __exit cleanup(void)
|
||||
static void __exit gserial_cleanup(void)
|
||||
{
|
||||
if (enable)
|
||||
usb_composite_unregister(&gserial_driver);
|
||||
}
|
||||
module_exit(cleanup);
|
||||
module_exit(gserial_cleanup);
|
||||
|
|
|
@ -466,19 +466,21 @@ static int ast_vhub_epn_dequeue(struct usb_ep* u_ep, struct usb_request *u_req)
|
|||
{
|
||||
struct ast_vhub_ep *ep = to_ast_ep(u_ep);
|
||||
struct ast_vhub *vhub = ep->vhub;
|
||||
struct ast_vhub_req *req;
|
||||
struct ast_vhub_req *req = NULL, *iter;
|
||||
unsigned long flags;
|
||||
int rc = -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&vhub->lock, flags);
|
||||
|
||||
/* Make sure it's actually queued on this endpoint */
|
||||
list_for_each_entry (req, &ep->queue, queue) {
|
||||
if (&req->req == u_req)
|
||||
break;
|
||||
list_for_each_entry(iter, &ep->queue, queue) {
|
||||
if (&iter->req != u_req)
|
||||
continue;
|
||||
req = iter;
|
||||
break;
|
||||
}
|
||||
|
||||
if (&req->req == u_req) {
|
||||
if (req) {
|
||||
EPVDBG(ep, "dequeue req @%p active=%d\n",
|
||||
req, req->active);
|
||||
if (req->active)
|
||||
|
|
|
@ -704,7 +704,7 @@ done:
|
|||
static int at91_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
||||
{
|
||||
struct at91_ep *ep;
|
||||
struct at91_request *req;
|
||||
struct at91_request *req = NULL, *iter;
|
||||
unsigned long flags;
|
||||
struct at91_udc *udc;
|
||||
|
||||
|
@ -717,11 +717,13 @@ static int at91_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
|||
spin_lock_irqsave(&udc->lock, flags);
|
||||
|
||||
/* make sure it's actually queued on this endpoint */
|
||||
list_for_each_entry (req, &ep->queue, queue) {
|
||||
if (&req->req == _req)
|
||||
break;
|
||||
list_for_each_entry(iter, &ep->queue, queue) {
|
||||
if (&iter->req != _req)
|
||||
continue;
|
||||
req = iter;
|
||||
break;
|
||||
}
|
||||
if (&req->req != _req) {
|
||||
if (!req) {
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
|
@ -860,7 +860,8 @@ static int usba_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
|||
{
|
||||
struct usba_ep *ep = to_usba_ep(_ep);
|
||||
struct usba_udc *udc = ep->udc;
|
||||
struct usba_request *req;
|
||||
struct usba_request *req = NULL;
|
||||
struct usba_request *iter;
|
||||
unsigned long flags;
|
||||
u32 status;
|
||||
|
||||
|
@ -869,12 +870,14 @@ static int usba_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
|||
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
|
||||
list_for_each_entry(req, &ep->queue, queue) {
|
||||
if (&req->req == _req)
|
||||
break;
|
||||
list_for_each_entry(iter, &ep->queue, queue) {
|
||||
if (&iter->req != _req)
|
||||
continue;
|
||||
req = iter;
|
||||
break;
|
||||
}
|
||||
|
||||
if (&req->req != _req) {
|
||||
if (!req) {
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
|
@ -1757,6 +1757,7 @@ static int bdc_gadget_ep_dequeue(struct usb_ep *_ep,
|
|||
struct usb_request *_req)
|
||||
{
|
||||
struct bdc_req *req;
|
||||
struct bdc_req *iter;
|
||||
unsigned long flags;
|
||||
struct bdc_ep *ep;
|
||||
struct bdc *bdc;
|
||||
|
@ -1771,12 +1772,16 @@ static int bdc_gadget_ep_dequeue(struct usb_ep *_ep,
|
|||
dev_dbg(bdc->dev, "%s ep:%s req:%p\n", __func__, ep->name, req);
|
||||
bdc_dbg_bd_list(bdc, ep);
|
||||
spin_lock_irqsave(&bdc->lock, flags);
|
||||
|
||||
req = NULL;
|
||||
/* make sure it's still queued on this endpoint */
|
||||
list_for_each_entry(req, &ep->queue, queue) {
|
||||
if (&req->usb_req == _req)
|
||||
break;
|
||||
list_for_each_entry(iter, &ep->queue, queue) {
|
||||
if (&iter->usb_req != _req)
|
||||
continue;
|
||||
req = iter;
|
||||
break;
|
||||
}
|
||||
if (&req->usb_req != _req) {
|
||||
if (!req) {
|
||||
spin_unlock_irqrestore(&bdc->lock, flags);
|
||||
dev_err(bdc->dev, "usb_req !=req n");
|
||||
return -EINVAL;
|
||||
|
|
|
@ -1525,7 +1525,7 @@ err1:
|
|||
|
||||
int usb_gadget_probe_driver(struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct usb_udc *udc = NULL;
|
||||
struct usb_udc *udc = NULL, *iter;
|
||||
int ret = -ENODEV;
|
||||
|
||||
if (!driver || !driver->bind || !driver->setup)
|
||||
|
@ -1533,10 +1533,12 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver)
|
|||
|
||||
mutex_lock(&udc_lock);
|
||||
if (driver->udc_name) {
|
||||
list_for_each_entry(udc, &udc_list, list) {
|
||||
ret = strcmp(driver->udc_name, dev_name(&udc->dev));
|
||||
if (!ret)
|
||||
break;
|
||||
list_for_each_entry(iter, &udc_list, list) {
|
||||
ret = strcmp(driver->udc_name, dev_name(&iter->dev));
|
||||
if (ret)
|
||||
continue;
|
||||
udc = iter;
|
||||
break;
|
||||
}
|
||||
if (ret)
|
||||
ret = -ENODEV;
|
||||
|
@ -1545,10 +1547,12 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver)
|
|||
else
|
||||
goto found;
|
||||
} else {
|
||||
list_for_each_entry(udc, &udc_list, list) {
|
||||
list_for_each_entry(iter, &udc_list, list) {
|
||||
/* For now we take the first one */
|
||||
if (!udc->driver)
|
||||
goto found;
|
||||
if (iter->driver)
|
||||
continue;
|
||||
udc = iter;
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -751,7 +751,7 @@ static int dummy_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
|||
struct dummy *dum;
|
||||
int retval = -EINVAL;
|
||||
unsigned long flags;
|
||||
struct dummy_request *req = NULL;
|
||||
struct dummy_request *req = NULL, *iter;
|
||||
|
||||
if (!_ep || !_req)
|
||||
return retval;
|
||||
|
@ -763,13 +763,14 @@ static int dummy_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
|||
|
||||
local_irq_save(flags);
|
||||
spin_lock(&dum->lock);
|
||||
list_for_each_entry(req, &ep->queue, queue) {
|
||||
if (&req->req == _req) {
|
||||
list_del_init(&req->queue);
|
||||
_req->status = -ECONNRESET;
|
||||
retval = 0;
|
||||
break;
|
||||
}
|
||||
list_for_each_entry(iter, &ep->queue, queue) {
|
||||
if (&iter->req != _req)
|
||||
continue;
|
||||
list_del_init(&iter->queue);
|
||||
_req->status = -ECONNRESET;
|
||||
req = iter;
|
||||
retval = 0;
|
||||
break;
|
||||
}
|
||||
spin_unlock(&dum->lock);
|
||||
|
||||
|
@ -2765,7 +2766,7 @@ static struct platform_driver dummy_hcd_driver = {
|
|||
static struct platform_device *the_udc_pdev[MAX_NUM_UDC];
|
||||
static struct platform_device *the_hcd_pdev[MAX_NUM_UDC];
|
||||
|
||||
static int __init init(void)
|
||||
static int __init dummy_hcd_init(void)
|
||||
{
|
||||
int retval = -ENOMEM;
|
||||
int i;
|
||||
|
@ -2887,9 +2888,9 @@ err_alloc_udc:
|
|||
platform_device_put(the_hcd_pdev[i]);
|
||||
return retval;
|
||||
}
|
||||
module_init(init);
|
||||
module_init(dummy_hcd_init);
|
||||
|
||||
static void __exit cleanup(void)
|
||||
static void __exit dummy_hcd_cleanup(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
@ -2905,4 +2906,4 @@ static void __exit cleanup(void)
|
|||
platform_driver_unregister(&dummy_udc_driver);
|
||||
platform_driver_unregister(&dummy_hcd_driver);
|
||||
}
|
||||
module_exit(cleanup);
|
||||
module_exit(dummy_hcd_cleanup);
|
||||
|
|
|
@ -1776,7 +1776,8 @@ static int qe_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
|
|||
static int qe_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
||||
{
|
||||
struct qe_ep *ep = container_of(_ep, struct qe_ep, ep);
|
||||
struct qe_req *req;
|
||||
struct qe_req *req = NULL;
|
||||
struct qe_req *iter;
|
||||
unsigned long flags;
|
||||
|
||||
if (!_ep || !_req)
|
||||
|
@ -1785,12 +1786,14 @@ static int qe_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
|||
spin_lock_irqsave(&ep->udc->lock, flags);
|
||||
|
||||
/* make sure it's actually queued on this endpoint */
|
||||
list_for_each_entry(req, &ep->queue, queue) {
|
||||
if (&req->req == _req)
|
||||
break;
|
||||
list_for_each_entry(iter, &ep->queue, queue) {
|
||||
if (&iter->req != _req)
|
||||
continue;
|
||||
req = iter;
|
||||
break;
|
||||
}
|
||||
|
||||
if (&req->req != _req) {
|
||||
if (!req) {
|
||||
spin_unlock_irqrestore(&ep->udc->lock, flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
|
@ -918,7 +918,8 @@ fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
|
|||
static int fsl_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
||||
{
|
||||
struct fsl_ep *ep = container_of(_ep, struct fsl_ep, ep);
|
||||
struct fsl_req *req;
|
||||
struct fsl_req *req = NULL;
|
||||
struct fsl_req *iter;
|
||||
unsigned long flags;
|
||||
int ep_num, stopped, ret = 0;
|
||||
u32 epctrl;
|
||||
|
@ -940,11 +941,13 @@ static int fsl_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
|||
fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]);
|
||||
|
||||
/* make sure it's actually queued on this endpoint */
|
||||
list_for_each_entry(req, &ep->queue, queue) {
|
||||
if (&req->req == _req)
|
||||
break;
|
||||
list_for_each_entry(iter, &ep->queue, queue) {
|
||||
if (&iter->req != _req)
|
||||
continue;
|
||||
req = iter;
|
||||
break;
|
||||
}
|
||||
if (&req->req != _req) {
|
||||
if (!req) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
|
|
@ -809,7 +809,7 @@ static void nuke(struct goku_ep *ep, int status)
|
|||
/* dequeue JUST ONE request */
|
||||
static int goku_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
||||
{
|
||||
struct goku_request *req;
|
||||
struct goku_request *req = NULL, *iter;
|
||||
struct goku_ep *ep;
|
||||
struct goku_udc *dev;
|
||||
unsigned long flags;
|
||||
|
@ -833,11 +833,13 @@ static int goku_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
|||
spin_lock_irqsave(&dev->lock, flags);
|
||||
|
||||
/* make sure it's actually queued on this endpoint */
|
||||
list_for_each_entry (req, &ep->queue, queue) {
|
||||
if (&req->req == _req)
|
||||
break;
|
||||
list_for_each_entry(iter, &ep->queue, queue) {
|
||||
if (&iter->req != _req)
|
||||
continue;
|
||||
req = iter;
|
||||
break;
|
||||
}
|
||||
if (&req->req != _req) {
|
||||
if (!req) {
|
||||
spin_unlock_irqrestore (&dev->lock, flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
|
@ -1690,7 +1690,7 @@ static int gr_queue_ext(struct usb_ep *_ep, struct usb_request *_req,
|
|||
/* Dequeue JUST ONE request */
|
||||
static int gr_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
||||
{
|
||||
struct gr_request *req;
|
||||
struct gr_request *req = NULL, *iter;
|
||||
struct gr_ep *ep;
|
||||
struct gr_udc *dev;
|
||||
int ret = 0;
|
||||
|
@ -1710,11 +1710,13 @@ static int gr_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
|||
spin_lock_irqsave(&dev->lock, flags);
|
||||
|
||||
/* Make sure it's actually queued on this endpoint */
|
||||
list_for_each_entry(req, &ep->queue, queue) {
|
||||
if (&req->req == _req)
|
||||
break;
|
||||
list_for_each_entry(iter, &ep->queue, queue) {
|
||||
if (&iter->req != _req)
|
||||
continue;
|
||||
req = iter;
|
||||
break;
|
||||
}
|
||||
if (&req->req != _req) {
|
||||
if (!req) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
|
|
@ -1830,7 +1830,7 @@ static int lpc32xx_ep_queue(struct usb_ep *_ep,
|
|||
static int lpc32xx_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
||||
{
|
||||
struct lpc32xx_ep *ep;
|
||||
struct lpc32xx_request *req;
|
||||
struct lpc32xx_request *req = NULL, *iter;
|
||||
unsigned long flags;
|
||||
|
||||
ep = container_of(_ep, struct lpc32xx_ep, ep);
|
||||
|
@ -1840,11 +1840,13 @@ static int lpc32xx_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
|||
spin_lock_irqsave(&ep->udc->lock, flags);
|
||||
|
||||
/* make sure it's actually queued on this endpoint */
|
||||
list_for_each_entry(req, &ep->queue, queue) {
|
||||
if (&req->req == _req)
|
||||
break;
|
||||
list_for_each_entry(iter, &ep->queue, queue) {
|
||||
if (&iter->req != _req)
|
||||
continue;
|
||||
req = iter;
|
||||
break;
|
||||
}
|
||||
if (&req->req != _req) {
|
||||
if (!req) {
|
||||
spin_unlock_irqrestore(&ep->udc->lock, flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
|
@ -1044,22 +1044,26 @@ static int max3420_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
|
|||
|
||||
static int max3420_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
||||
{
|
||||
struct max3420_req *t, *req = to_max3420_req(_req);
|
||||
struct max3420_req *t = NULL;
|
||||
struct max3420_req *req = to_max3420_req(_req);
|
||||
struct max3420_req *iter;
|
||||
struct max3420_ep *ep = to_max3420_ep(_ep);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ep->lock, flags);
|
||||
|
||||
/* Pluck the descriptor from queue */
|
||||
list_for_each_entry(t, &ep->queue, queue)
|
||||
if (t == req) {
|
||||
list_del_init(&req->queue);
|
||||
break;
|
||||
}
|
||||
list_for_each_entry(iter, &ep->queue, queue) {
|
||||
if (iter != req)
|
||||
continue;
|
||||
list_del_init(&req->queue);
|
||||
t = iter;
|
||||
break;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&ep->lock, flags);
|
||||
|
||||
if (t == req)
|
||||
if (t)
|
||||
max3420_req_done(req, -ECONNRESET);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -844,7 +844,7 @@ mv_u3d_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
|
|||
static int mv_u3d_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
||||
{
|
||||
struct mv_u3d_ep *ep;
|
||||
struct mv_u3d_req *req;
|
||||
struct mv_u3d_req *req = NULL, *iter;
|
||||
struct mv_u3d *u3d;
|
||||
struct mv_u3d_ep_context *ep_context;
|
||||
struct mv_u3d_req *next_req;
|
||||
|
@ -861,11 +861,13 @@ static int mv_u3d_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
|||
spin_lock_irqsave(&ep->u3d->lock, flags);
|
||||
|
||||
/* make sure it's actually queued on this endpoint */
|
||||
list_for_each_entry(req, &ep->queue, queue) {
|
||||
if (&req->req == _req)
|
||||
break;
|
||||
list_for_each_entry(iter, &ep->queue, queue) {
|
||||
if (&iter->req != _req)
|
||||
continue;
|
||||
req = iter;
|
||||
break;
|
||||
}
|
||||
if (&req->req != _req) {
|
||||
if (!req) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
|
|
@ -771,7 +771,7 @@ static void mv_prime_ep(struct mv_ep *ep, struct mv_req *req)
|
|||
static int mv_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
||||
{
|
||||
struct mv_ep *ep = container_of(_ep, struct mv_ep, ep);
|
||||
struct mv_req *req;
|
||||
struct mv_req *req = NULL, *iter;
|
||||
struct mv_udc *udc = ep->udc;
|
||||
unsigned long flags;
|
||||
int stopped, ret = 0;
|
||||
|
@ -793,11 +793,13 @@ static int mv_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
|||
writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]);
|
||||
|
||||
/* make sure it's actually queued on this endpoint */
|
||||
list_for_each_entry(req, &ep->queue, queue) {
|
||||
if (&req->req == _req)
|
||||
break;
|
||||
list_for_each_entry(iter, &ep->queue, queue) {
|
||||
if (&iter->req != _req)
|
||||
continue;
|
||||
req = iter;
|
||||
break;
|
||||
}
|
||||
if (&req->req != _req) {
|
||||
if (!req) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
|
|
@ -926,7 +926,7 @@ static int
|
|||
net2272_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
||||
{
|
||||
struct net2272_ep *ep;
|
||||
struct net2272_request *req;
|
||||
struct net2272_request *req = NULL, *iter;
|
||||
unsigned long flags;
|
||||
int stopped;
|
||||
|
||||
|
@ -939,11 +939,13 @@ net2272_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
|||
ep->stopped = 1;
|
||||
|
||||
/* make sure it's still queued on this endpoint */
|
||||
list_for_each_entry(req, &ep->queue, queue) {
|
||||
if (&req->req == _req)
|
||||
break;
|
||||
list_for_each_entry(iter, &ep->queue, queue) {
|
||||
if (&iter->req != _req)
|
||||
continue;
|
||||
req = iter;
|
||||
break;
|
||||
}
|
||||
if (&req->req != _req) {
|
||||
if (!req) {
|
||||
ep->stopped = stopped;
|
||||
spin_unlock_irqrestore(&ep->dev->lock, flags);
|
||||
return -EINVAL;
|
||||
|
@ -954,7 +956,6 @@ net2272_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
|||
dev_dbg(ep->dev->dev, "unlink (%s) pio\n", _ep->name);
|
||||
net2272_done(ep, req, -ECONNRESET);
|
||||
}
|
||||
req = NULL;
|
||||
ep->stopped = stopped;
|
||||
|
||||
spin_unlock_irqrestore(&ep->dev->lock, flags);
|
||||
|
|
|
@ -1240,7 +1240,8 @@ static void nuke(struct net2280_ep *ep)
|
|||
static int net2280_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
||||
{
|
||||
struct net2280_ep *ep;
|
||||
struct net2280_request *req;
|
||||
struct net2280_request *req = NULL;
|
||||
struct net2280_request *iter;
|
||||
unsigned long flags;
|
||||
u32 dmactl;
|
||||
int stopped;
|
||||
|
@ -1266,11 +1267,13 @@ static int net2280_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
|||
}
|
||||
|
||||
/* make sure it's still queued on this endpoint */
|
||||
list_for_each_entry(req, &ep->queue, queue) {
|
||||
if (&req->req == _req)
|
||||
break;
|
||||
list_for_each_entry(iter, &ep->queue, queue) {
|
||||
if (&iter->req != _req)
|
||||
continue;
|
||||
req = iter;
|
||||
break;
|
||||
}
|
||||
if (&req->req != _req) {
|
||||
if (!req) {
|
||||
ep->stopped = stopped;
|
||||
spin_unlock_irqrestore(&ep->dev->lock, flags);
|
||||
ep_dbg(ep->dev, "%s: Request mismatch\n", __func__);
|
||||
|
|
|
@ -1003,7 +1003,7 @@ irq_wait:
|
|||
static int omap_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
||||
{
|
||||
struct omap_ep *ep = container_of(_ep, struct omap_ep, ep);
|
||||
struct omap_req *req;
|
||||
struct omap_req *req = NULL, *iter;
|
||||
unsigned long flags;
|
||||
|
||||
if (!_ep || !_req)
|
||||
|
@ -1012,11 +1012,13 @@ static int omap_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
|||
spin_lock_irqsave(&ep->udc->lock, flags);
|
||||
|
||||
/* make sure it's actually queued on this endpoint */
|
||||
list_for_each_entry(req, &ep->queue, queue) {
|
||||
if (&req->req == _req)
|
||||
break;
|
||||
list_for_each_entry(iter, &ep->queue, queue) {
|
||||
if (&iter->req != _req)
|
||||
continue;
|
||||
req = iter;
|
||||
break;
|
||||
}
|
||||
if (&req->req != _req) {
|
||||
if (!req) {
|
||||
spin_unlock_irqrestore(&ep->udc->lock, flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
|
@ -966,7 +966,8 @@ static void nuke(struct pxa25x_ep *ep, int status)
|
|||
static int pxa25x_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
||||
{
|
||||
struct pxa25x_ep *ep;
|
||||
struct pxa25x_request *req;
|
||||
struct pxa25x_request *req = NULL;
|
||||
struct pxa25x_request *iter;
|
||||
unsigned long flags;
|
||||
|
||||
ep = container_of(_ep, struct pxa25x_ep, ep);
|
||||
|
@ -976,11 +977,13 @@ static int pxa25x_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
|||
local_irq_save(flags);
|
||||
|
||||
/* make sure it's actually queued on this endpoint */
|
||||
list_for_each_entry (req, &ep->queue, queue) {
|
||||
if (&req->req == _req)
|
||||
break;
|
||||
list_for_each_entry(iter, &ep->queue, queue) {
|
||||
if (&iter->req != _req)
|
||||
continue;
|
||||
req = iter;
|
||||
break;
|
||||
}
|
||||
if (&req->req != _req) {
|
||||
if (!req) {
|
||||
local_irq_restore(flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
|
@ -1159,7 +1159,7 @@ static int pxa_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
|||
{
|
||||
struct pxa_ep *ep;
|
||||
struct udc_usb_ep *udc_usb_ep;
|
||||
struct pxa27x_request *req;
|
||||
struct pxa27x_request *req = NULL, *iter;
|
||||
unsigned long flags;
|
||||
int rc = -EINVAL;
|
||||
|
||||
|
@ -1173,11 +1173,12 @@ static int pxa_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
|||
spin_lock_irqsave(&ep->lock, flags);
|
||||
|
||||
/* make sure it's actually queued on this endpoint */
|
||||
list_for_each_entry(req, &ep->queue, queue) {
|
||||
if (&req->req == _req) {
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
list_for_each_entry(iter, &ep->queue, queue) {
|
||||
if (&iter->req != _req)
|
||||
continue;
|
||||
req = iter;
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&ep->lock, flags);
|
||||
|
|
|
@ -2730,7 +2730,7 @@ static const struct soc_device_attribute renesas_usb3_quirks_match[] = {
|
|||
.soc_id = "r8a7795", .revision = "ES1.*",
|
||||
.data = &renesas_usb3_priv_r8a7795_es1,
|
||||
},
|
||||
{ /* sentinel */ },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static const unsigned int renesas_usb3_cable[] = {
|
||||
|
|
|
@ -877,7 +877,7 @@ static int s3c_hsudc_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
|||
{
|
||||
struct s3c_hsudc_ep *hsep = our_ep(_ep);
|
||||
struct s3c_hsudc *hsudc = hsep->dev;
|
||||
struct s3c_hsudc_req *hsreq;
|
||||
struct s3c_hsudc_req *hsreq = NULL, *iter;
|
||||
unsigned long flags;
|
||||
|
||||
hsep = our_ep(_ep);
|
||||
|
@ -886,11 +886,13 @@ static int s3c_hsudc_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
|||
|
||||
spin_lock_irqsave(&hsudc->lock, flags);
|
||||
|
||||
list_for_each_entry(hsreq, &hsep->queue, queue) {
|
||||
if (&hsreq->req == _req)
|
||||
break;
|
||||
list_for_each_entry(iter, &hsep->queue, queue) {
|
||||
if (&iter->req != _req)
|
||||
continue;
|
||||
hsreq = iter;
|
||||
break;
|
||||
}
|
||||
if (&hsreq->req != _req) {
|
||||
if (!hsreq) {
|
||||
spin_unlock_irqrestore(&hsudc->lock, flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
|
@ -1265,7 +1265,7 @@ static int s3c2410_udc_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
|||
struct s3c2410_ep *ep = to_s3c2410_ep(_ep);
|
||||
int retval = -EINVAL;
|
||||
unsigned long flags;
|
||||
struct s3c2410_request *req = NULL;
|
||||
struct s3c2410_request *req = NULL, *iter;
|
||||
|
||||
dprintk(DEBUG_VERBOSE, "%s(%p,%p)\n", __func__, _ep, _req);
|
||||
|
||||
|
@ -1277,13 +1277,14 @@ static int s3c2410_udc_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
|||
|
||||
local_irq_save(flags);
|
||||
|
||||
list_for_each_entry(req, &ep->queue, queue) {
|
||||
if (&req->req == _req) {
|
||||
list_del_init(&req->queue);
|
||||
_req->status = -ECONNRESET;
|
||||
retval = 0;
|
||||
break;
|
||||
}
|
||||
list_for_each_entry(iter, &ep->queue, queue) {
|
||||
if (&iter->req != _req)
|
||||
continue;
|
||||
list_del_init(&iter->queue);
|
||||
_req->status = -ECONNRESET;
|
||||
req = iter;
|
||||
retval = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (retval == 0) {
|
||||
|
|
|
@ -80,7 +80,7 @@ static int stop_timer;
|
|||
* This cannot be solved by letting the RX DMA disabled until a
|
||||
* request gets queued because there may be other OUT packets
|
||||
* in the FIFO (important for not blocking control traffic).
|
||||
* The value of set_rde controls the correspondig timer.
|
||||
* The value of set_rde controls the corresponding timer.
|
||||
*
|
||||
* set_rde -1 == not used, means it is alloed to be set to 0 or 1
|
||||
* set_rde 0 == do not touch RDE, do no start the RDE timer
|
||||
|
|
|
@ -32,9 +32,6 @@
|
|||
#include <linux/workqueue.h>
|
||||
|
||||
/* XUSB_DEV registers */
|
||||
#define SPARAM 0x000
|
||||
#define SPARAM_ERSTMAX_MASK GENMASK(20, 16)
|
||||
#define SPARAM_ERSTMAX(x) (((x) << 16) & SPARAM_ERSTMAX_MASK)
|
||||
#define DB 0x004
|
||||
#define DB_TARGET_MASK GENMASK(15, 8)
|
||||
#define DB_TARGET(x) (((x) << 8) & DB_TARGET_MASK)
|
||||
|
@ -275,8 +272,10 @@ BUILD_EP_CONTEXT_RW(deq_hi, deq_hi, 0, 0xffffffff)
|
|||
BUILD_EP_CONTEXT_RW(avg_trb_len, tx_info, 0, 0xffff)
|
||||
BUILD_EP_CONTEXT_RW(max_esit_payload, tx_info, 16, 0xffff)
|
||||
BUILD_EP_CONTEXT_RW(edtla, rsvd[0], 0, 0xffffff)
|
||||
BUILD_EP_CONTEXT_RW(seq_num, rsvd[0], 24, 0xff)
|
||||
BUILD_EP_CONTEXT_RW(rsvd, rsvd[0], 24, 0x1)
|
||||
BUILD_EP_CONTEXT_RW(partial_td, rsvd[0], 25, 0x1)
|
||||
BUILD_EP_CONTEXT_RW(splitxstate, rsvd[0], 26, 0x1)
|
||||
BUILD_EP_CONTEXT_RW(seq_num, rsvd[0], 27, 0x1f)
|
||||
BUILD_EP_CONTEXT_RW(cerrcnt, rsvd[1], 18, 0x3)
|
||||
BUILD_EP_CONTEXT_RW(data_offset, rsvd[2], 0, 0x1ffff)
|
||||
BUILD_EP_CONTEXT_RW(numtrbs, rsvd[2], 22, 0x1f)
|
||||
|
@ -1413,18 +1412,20 @@ __tegra_xudc_ep_dequeue(struct tegra_xudc_ep *ep,
|
|||
struct tegra_xudc_request *req)
|
||||
{
|
||||
struct tegra_xudc *xudc = ep->xudc;
|
||||
struct tegra_xudc_request *r;
|
||||
struct tegra_xudc_request *r = NULL, *iter;
|
||||
struct tegra_xudc_trb *deq_trb;
|
||||
bool busy, kick_queue = false;
|
||||
int ret = 0;
|
||||
|
||||
/* Make sure the request is actually queued to this endpoint. */
|
||||
list_for_each_entry(r, &ep->queue, list) {
|
||||
if (r == req)
|
||||
break;
|
||||
list_for_each_entry(iter, &ep->queue, list) {
|
||||
if (iter != req)
|
||||
continue;
|
||||
r = iter;
|
||||
break;
|
||||
}
|
||||
|
||||
if (r != req)
|
||||
if (!r)
|
||||
return -EINVAL;
|
||||
|
||||
/* Request hasn't been queued in the transfer ring yet. */
|
||||
|
@ -1557,6 +1558,9 @@ static int __tegra_xudc_ep_set_halt(struct tegra_xudc_ep *ep, bool halt)
|
|||
ep_reload(xudc, ep->index);
|
||||
|
||||
ep_ctx_write_state(ep->context, EP_STATE_RUNNING);
|
||||
ep_ctx_write_rsvd(ep->context, 0);
|
||||
ep_ctx_write_partial_td(ep->context, 0);
|
||||
ep_ctx_write_splitxstate(ep->context, 0);
|
||||
ep_ctx_write_seq_num(ep->context, 0);
|
||||
|
||||
ep_reload(xudc, ep->index);
|
||||
|
@ -2812,7 +2816,10 @@ static void tegra_xudc_reset(struct tegra_xudc *xudc)
|
|||
xudc->setup_seq_num = 0;
|
||||
xudc->queued_setup_packet = false;
|
||||
|
||||
ep_ctx_write_seq_num(ep0->context, xudc->setup_seq_num);
|
||||
ep_ctx_write_rsvd(ep0->context, 0);
|
||||
ep_ctx_write_partial_td(ep0->context, 0);
|
||||
ep_ctx_write_splitxstate(ep0->context, 0);
|
||||
ep_ctx_write_seq_num(ep0->context, 0);
|
||||
|
||||
deq_ptr = trb_virt_to_phys(ep0, &ep0->transfer_ring[ep0->deq_ptr]);
|
||||
|
||||
|
@ -3295,11 +3302,6 @@ static void tegra_xudc_init_event_ring(struct tegra_xudc *xudc)
|
|||
unsigned int i;
|
||||
u32 val;
|
||||
|
||||
val = xudc_readl(xudc, SPARAM);
|
||||
val &= ~(SPARAM_ERSTMAX_MASK);
|
||||
val |= SPARAM_ERSTMAX(XUDC_NR_EVENT_RINGS);
|
||||
xudc_writel(xudc, val, SPARAM);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(xudc->event_ring); i++) {
|
||||
memset(xudc->event_ring[i], 0, XUDC_EVENT_RING_SIZE *
|
||||
sizeof(*xudc->event_ring[i]));
|
||||
|
|
|
@ -1136,17 +1136,20 @@ static int xudc_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
|
|||
static int xudc_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
||||
{
|
||||
struct xusb_ep *ep = to_xusb_ep(_ep);
|
||||
struct xusb_req *req = to_xusb_req(_req);
|
||||
struct xusb_req *req = NULL;
|
||||
struct xusb_req *iter;
|
||||
struct xusb_udc *udc = ep->udc;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
/* Make sure it's actually queued on this endpoint */
|
||||
list_for_each_entry(req, &ep->queue, queue) {
|
||||
if (&req->usb_req == _req)
|
||||
break;
|
||||
list_for_each_entry(iter, &ep->queue, queue) {
|
||||
if (&iter->usb_req != _req)
|
||||
continue;
|
||||
req = iter;
|
||||
break;
|
||||
}
|
||||
if (&req->usb_req != _req) {
|
||||
if (!req) {
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue