USB / Thunderbolt driver changes for 6.3-rc1
Here is the big set of USB and Thunderbolt driver changes for 6.3-rc1. Nothing major in here, just lots of good development, including: - Thunderbolt additions for new device support and features - xhci driver updates and cleanups - USB gadget media driver updates (includes media core changes that were acked by the v4l2 maintainers) - lots of other USB gadget driver updates for new features - dwc3 driver updates and fixes - minor debugfs leak fixes - typec driver updates and additions - dt-bindings conversions to yaml - other small bugfixes and driver updates All have been in linux-next for a while with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCY/ivpQ8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ymkdQCeOS6N613eggYrXwnbjJhxMQDtKAcAmweK6kXh 3o1IKOYqIMOx5E7zxn6W =7ajf -----END PGP SIGNATURE----- Merge tag 'usb-6.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb Pull USB / Thunderbolt driver updates from Greg KH: "Here is the big set of USB and Thunderbolt driver changes for 6.3-rc1. Nothing major in here, just lots of good development, including: - Thunderbolt additions for new device support and features - xhci driver updates and cleanups - USB gadget media driver updates (includes media core changes that were acked by the v4l2 maintainers) - lots of other USB gadget driver updates for new features - dwc3 driver updates and fixes - minor debugfs leak fixes - typec driver updates and additions - dt-bindings conversions to yaml - other small bugfixes and driver updates All have been in linux-next for a while with no reported issues" * tag 'usb-6.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (237 commits) usb: dwc3: xilinx: Remove unused of_gpio,h usb: typec: pd: Add higher capability sysfs for sink PDO usb: typec: pd: Remove usb_suspend_supported sysfs from sink PDO usb: dwc3: pci: add support for the Intel Meteor Lake-M usb: gadget: u_ether: Don't warn in gether_setup_name_default() usb: gadget: u_ether: Convert prints to device prints usb: gadget: u_serial: Add null pointer check in gserial_resume usb: gadget: uvc: fix missing mutex_unlock() if kstrtou8() fails xhci: host: potential NULL dereference in xhci_generic_plat_probe() dt-bindings: usb: amlogic,meson-g12a-usb-ctrl: make G12A usb3-phy0 optional usb: host: fsl-mph-dr-of: reuse device_set_of_node_from_dev of: device: Do not ignore error code in of_device_uevent_modalias of: device: Ignore modalias of reused nodes usb: gadget: configfs: Fix set but not used variable warning usb: gadget: uvc: Use custom strings if available usb: gadget: uvc: Allow linking function to string descs usb: gadget: uvc: Pick up custom string descriptor IDs usb: gadget: uvc: Allow linking XUs to string descriptors usb: gadget: configfs: Attach arbitrary strings to cdev usb: gadget: configfs: Support arbitrary string descriptors ...
This commit is contained in:
commit
72bffe7e1e
@ -143,3 +143,16 @@ Description:
|
||||
qw_sign an identifier to be reported as "OS String"
|
||||
proper
|
||||
============= ===============================================
|
||||
|
||||
What: /config/usb-gadget/gadget/webusb
|
||||
Date: Dec 2022
|
||||
KernelVersion: 6.3
|
||||
Description:
|
||||
This group contains "WebUSB" extension handling attributes.
|
||||
|
||||
============= ===============================================
|
||||
use flag turning "WebUSB" support on/off
|
||||
bcdVersion bcd WebUSB specification version number
|
||||
bVendorCode one-byte value used for custom per-device
|
||||
landingPage UTF-8 encoded URL of the device's landing page
|
||||
============= ===============================================
|
||||
|
@ -15,12 +15,14 @@ Date: Dec 2014
|
||||
KernelVersion: 4.0
|
||||
Description: Control descriptors
|
||||
|
||||
All attributes read only:
|
||||
All attributes read only except enable_interrupt_ep:
|
||||
|
||||
================ =============================
|
||||
=================== =============================
|
||||
bInterfaceNumber USB interface number for this
|
||||
streaming interface
|
||||
================ =============================
|
||||
enable_interrupt_ep flag to enable the interrupt
|
||||
endpoint for the VC interface
|
||||
=================== =============================
|
||||
|
||||
What: /config/usb-gadget/gadget/functions/uvc.name/control/class
|
||||
Date: Dec 2014
|
||||
@ -52,7 +54,7 @@ Date: Dec 2014
|
||||
KernelVersion: 4.0
|
||||
Description: Default output terminal descriptors
|
||||
|
||||
All attributes read only:
|
||||
All attributes read only except bSourceID:
|
||||
|
||||
============== =============================================
|
||||
iTerminal index of string descriptor
|
||||
@ -111,6 +113,34 @@ Description: Default processing unit descriptors
|
||||
bUnitID a non-zero id of this unit
|
||||
=============== ========================================
|
||||
|
||||
What: /config/usb-gadget/gadget/functions/uvc.name/control/extensions
|
||||
Date: Nov 2022
|
||||
KernelVersion: 6.1
|
||||
Description: Extension unit descriptors
|
||||
|
||||
What: /config/usb-gadget/gadget/functions/uvc.name/control/extensions/name
|
||||
Date: Nov 2022
|
||||
KernelVersion: 6.1
|
||||
Description: Extension Unit (XU) Descriptor
|
||||
|
||||
bLength, bUnitID and iExtension are read-only. All others are
|
||||
read-write.
|
||||
|
||||
================= ========================================
|
||||
bLength size of the descriptor in bytes
|
||||
bUnitID non-zero ID of this unit
|
||||
guidExtensionCode Vendor-specific code identifying the XU
|
||||
bNumControls number of controls in this XU
|
||||
bNrInPins number of input pins for this unit
|
||||
baSourceID list of the IDs of the units or terminals
|
||||
to which this XU is connected
|
||||
bControlSize size of the bmControls field in bytes
|
||||
bmControls list of bitmaps detailing which vendor
|
||||
specific controls are supported
|
||||
iExtension index of a string descriptor that describes
|
||||
this extension unit
|
||||
================= ========================================
|
||||
|
||||
What: /config/usb-gadget/gadget/functions/uvc.name/control/header
|
||||
Date: Dec 2014
|
||||
KernelVersion: 4.0
|
||||
@ -165,7 +195,24 @@ Date: Dec 2014
|
||||
KernelVersion: 4.0
|
||||
Description: Default color matching descriptors
|
||||
|
||||
All attributes read only:
|
||||
All attributes read/write:
|
||||
|
||||
======================== ======================================
|
||||
bMatrixCoefficients matrix used to compute luma and
|
||||
chroma values from the color primaries
|
||||
bTransferCharacteristics optoelectronic transfer
|
||||
characteristic of the source picture,
|
||||
also called the gamma function
|
||||
bColorPrimaries color primaries and the reference
|
||||
white
|
||||
======================== ======================================
|
||||
|
||||
What: /config/usb-gadget/gadget/functions/uvc.name/streaming/color_matching/name
|
||||
Date: Dec 2022
|
||||
KernelVersion: 6.3
|
||||
Description: Additional color matching descriptors
|
||||
|
||||
All attributes read/write:
|
||||
|
||||
======================== ======================================
|
||||
bMatrixCoefficients matrix used to compute luma and
|
||||
|
@ -69,7 +69,7 @@ Description:
|
||||
This file contains boolean value that tells does the device
|
||||
support both source and sink power roles.
|
||||
|
||||
What: /sys/class/usb_power_delivery/.../<capability>/1:fixed_supply/usb_suspend_supported
|
||||
What: /sys/class/usb_power_delivery/.../source-capabilities/1:fixed_supply/usb_suspend_supported
|
||||
Date: May 2022
|
||||
Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
|
||||
Description:
|
||||
@ -78,6 +78,15 @@ Description:
|
||||
will follow the USB 2.0 and USB 3.2 rules for suspend and
|
||||
resume.
|
||||
|
||||
What: /sys/class/usb_power_delivery/.../sink-capabilities/1:fixed_supply/higher_capability
|
||||
Date: February 2023
|
||||
Contact: Saranya Gopal <saranya.gopal@linux.intel.com>
|
||||
Description:
|
||||
This file shows the value of the Higher capability bit in
|
||||
vsafe5V Fixed Supply Object. If the bit is set, then the sink
|
||||
needs more than vsafe5V(eg. 12 V) to provide full functionality.
|
||||
Valid values: 0, 1
|
||||
|
||||
What: /sys/class/usb_power_delivery/.../<capability>/1:fixed_supply/unconstrained_power
|
||||
Date: May 2022
|
||||
Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
|
||||
|
@ -47,3 +47,18 @@ Description:
|
||||
USB SuperSpeed protocol. From user perspective pin assignments C
|
||||
and E are equal, where all channels on the connector are used
|
||||
for carrying DisplayPort protocol (allowing higher resolutions).
|
||||
|
||||
What: /sys/bus/typec/devices/.../displayport/hpd
|
||||
Date: Dec 2022
|
||||
Contact: Badhri Jagan Sridharan <badhri@google.com>
|
||||
Description:
|
||||
VESA DisplayPort Alt Mode on USB Type-C Standard defines how
|
||||
HotPlugDetect(HPD) shall be supported on the USB-C connector when
|
||||
operating in DisplayPort Alt Mode. This is a read only node which
|
||||
reflects the current state of HPD.
|
||||
|
||||
Valid values:
|
||||
- 1: when HPD’s logical state is high (HPD_High) as defined
|
||||
by VESA DisplayPort Alt Mode on USB Type-C Standard.
|
||||
- 0 when HPD’s logical state is low (HPD_Low) as defined by
|
||||
VESA DisplayPort Alt Mode on USB Type-C Standard.
|
||||
|
@ -64,8 +64,8 @@ Required properties if child node exists:
|
||||
Properties for children:
|
||||
|
||||
The OMAP HS USB Host subsystem contains EHCI and OHCI controllers.
|
||||
See Documentation/devicetree/bindings/usb/ehci-omap.txt and
|
||||
Documentation/devicetree/bindings/usb/ohci-omap3.txt.
|
||||
See Documentation/devicetree/bindings/usb/generic-ehci.yaml and
|
||||
Documentation/devicetree/bindings/usb/generic-ohci.yaml.
|
||||
|
||||
Example for OMAP4:
|
||||
|
||||
@ -78,14 +78,14 @@ usbhshost: usbhshost@4a064000 {
|
||||
ranges;
|
||||
|
||||
usbhsohci: ohci@4a064800 {
|
||||
compatible = "ti,ohci-omap3", "usb-ohci";
|
||||
compatible = "ti,ohci-omap3";
|
||||
reg = <0x4a064800 0x400>;
|
||||
interrupt-parent = <&gic>;
|
||||
interrupts = <0 76 0x4>;
|
||||
};
|
||||
|
||||
usbhsehci: ehci@4a064c00 {
|
||||
compatible = "ti,ehci-omap", "usb-ehci";
|
||||
compatible = "ti,ehci-omap";
|
||||
reg = <0x4a064c00 0x400>;
|
||||
interrupt-parent = <&gic>;
|
||||
interrupts = <0 77 0x4>;
|
||||
|
@ -97,16 +97,6 @@ Nintendo Wii device tree
|
||||
- reg : should contain the EXI registers location and length
|
||||
- interrupts : should contain the EXI interrupt
|
||||
|
||||
1.g) The Open Host Controller Interface (OHCI) nodes
|
||||
|
||||
Represent the USB 1.x Open Host Controller Interfaces.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : should be "nintendo,hollywood-usb-ohci","usb-ohci"
|
||||
- reg : should contain the OHCI registers location and length
|
||||
- interrupts : should contain the OHCI interrupt
|
||||
|
||||
1.h) The Enhanced Host Controller Interface (EHCI) node
|
||||
|
||||
Represents the USB 2.0 Enhanced Host Controller Interface.
|
||||
|
@ -13,10 +13,12 @@ maintainers:
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- const: allwinner,sun4i-a10-musb
|
||||
- const: allwinner,sun6i-a31-musb
|
||||
- const: allwinner,sun8i-a33-musb
|
||||
- const: allwinner,sun8i-h3-musb
|
||||
- enum:
|
||||
- allwinner,sun4i-a10-musb
|
||||
- allwinner,sun6i-a31-musb
|
||||
- allwinner,sun8i-a33-musb
|
||||
- allwinner,sun8i-h3-musb
|
||||
- allwinner,suniv-f1c100s-musb
|
||||
- items:
|
||||
- enum:
|
||||
- allwinner,sun8i-a83t-musb
|
||||
|
@ -108,6 +108,7 @@ allOf:
|
||||
then:
|
||||
properties:
|
||||
phy-names:
|
||||
minItems: 2
|
||||
items:
|
||||
- const: usb2-phy0 # USB2 PHY0 if USBHOST_A port is used
|
||||
- const: usb2-phy1 # USB2 PHY1 if USBOTG_B port is used
|
||||
|
@ -1,11 +0,0 @@
|
||||
* Broadcom USB controllers
|
||||
|
||||
Required properties:
|
||||
- compatible: "brcm,bcm3384-ohci", "brcm,bcm3384-ehci"
|
||||
|
||||
These currently use the generic-ohci and generic-ehci drivers. On some
|
||||
systems, special handling may be needed in the following cases:
|
||||
|
||||
- Restoring state after systemwide power save modes
|
||||
- Sharing PHYs with the USBD (UDC) hardware
|
||||
- Figuring out which controllers are disabled on ASIC bondout variants
|
@ -11,6 +11,7 @@ Required properties:
|
||||
"fsl,imx6ul-usb"
|
||||
"fsl,imx7d-usb"
|
||||
"fsl,imx7ulp-usb"
|
||||
"fsl,imx8mm-usb"
|
||||
"lsi,zevio-usb"
|
||||
"qcom,ci-hdrc"
|
||||
"chipidea,usb2"
|
||||
|
98
Documentation/devicetree/bindings/usb/cypress,cypd4226.yaml
Normal file
98
Documentation/devicetree/bindings/usb/cypress,cypd4226.yaml
Normal file
@ -0,0 +1,98 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/usb/cypress,cypd4226.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Cypress cypd4226 Type-C Controller
|
||||
|
||||
maintainers:
|
||||
- Wayne Chang <waynec@nvidia.com>
|
||||
|
||||
description:
|
||||
The Cypress cypd4226 is a dual Type-C controller that is controlled
|
||||
via an I2C interface.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: cypress,cypd4226
|
||||
|
||||
'#address-cells':
|
||||
const: 1
|
||||
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
reg:
|
||||
const: 0x08
|
||||
|
||||
interrupts:
|
||||
items:
|
||||
- description: cypd4226 host interrupt
|
||||
|
||||
firmware-name:
|
||||
enum:
|
||||
- nvidia,gpu
|
||||
- nvidia,jetson-agx-xavier
|
||||
description: |
|
||||
The name of the CCGx firmware built for product series.
|
||||
should be set one of following:
|
||||
- "nvidia,gpu" for the NVIDIA RTX product series
|
||||
- "nvidia,jetson-agx-xavier" for the NVIDIA Jetson product series
|
||||
|
||||
patternProperties:
|
||||
'^connector@[01]$':
|
||||
$ref: /schemas/connector/usb-connector.yaml#
|
||||
unevaluatedProperties: false
|
||||
properties:
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
anyOf:
|
||||
- required:
|
||||
- connector@0
|
||||
- required:
|
||||
- connector@1
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/tegra194-gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
#interrupt-cells = <2>;
|
||||
|
||||
typec@8 {
|
||||
compatible = "cypress,cypd4226";
|
||||
reg = <0x08>;
|
||||
interrupt-parent = <&gpio_aon>;
|
||||
interrupts = <TEGRA194_AON_GPIO(BB, 2) IRQ_TYPE_LEVEL_LOW>;
|
||||
firmware-name = "nvidia,jetson-agx-xavier";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
connector@0 {
|
||||
compatible = "usb-c-connector";
|
||||
reg = <0>;
|
||||
label = "USB-C";
|
||||
data-role = "dual";
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
endpoint {
|
||||
remote-endpoint = <&usb_role_switch0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
@ -1,31 +0,0 @@
|
||||
OMAP HS USB EHCI controller
|
||||
|
||||
This device is usually the child of the omap-usb-host
|
||||
Documentation/devicetree/bindings/mfd/omap-usb-host.txt
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: should be "ti,ehci-omap"
|
||||
- reg: should contain one register range i.e. start and length
|
||||
- interrupts: description of the interrupt line
|
||||
|
||||
Optional properties:
|
||||
|
||||
- phys: list of phandles to PHY nodes.
|
||||
This property is required if at least one of the ports are in
|
||||
PHY mode i.e. OMAP_EHCI_PORT_MODE_PHY
|
||||
|
||||
To specify the port mode, see
|
||||
Documentation/devicetree/bindings/mfd/omap-usb-host.txt
|
||||
|
||||
Example for OMAP4:
|
||||
|
||||
usbhsehci: ehci@4a064c00 {
|
||||
compatible = "ti,ehci-omap";
|
||||
reg = <0x4a064c00 0x400>;
|
||||
interrupts = <0 77 0x4>;
|
||||
};
|
||||
|
||||
&usbhsehci {
|
||||
phys = <&hsusb1_phy 0 &hsusb3_phy>;
|
||||
};
|
@ -1,22 +0,0 @@
|
||||
* EHCI controller, Orion Marvell variants
|
||||
|
||||
Required properties:
|
||||
- compatible: must be one of the following
|
||||
"marvell,orion-ehci"
|
||||
"marvell,armada-3700-ehci"
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
- interrupts: The EHCI interrupt
|
||||
|
||||
Optional properties:
|
||||
- clocks: reference to the clock
|
||||
- phys: reference to the USB PHY
|
||||
- phy-names: name of the USB PHY, should be "usb"
|
||||
|
||||
Example:
|
||||
|
||||
ehci@50000 {
|
||||
compatible = "marvell,orion-ehci";
|
||||
reg = <0x50000 0x1000>;
|
||||
interrupts = <19>;
|
||||
};
|
@ -5,7 +5,7 @@
|
||||
$id: http://devicetree.org/schemas/usb/faraday,fotg210.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Faraday Technology FOTG210 HS OTG USB 2.0 controller
|
||||
title: Faraday Technology FOTG200 series HS OTG USB 2.0 controller
|
||||
|
||||
maintainers:
|
||||
- Linus Walleij <linus.walleij@linaro.org>
|
||||
@ -17,10 +17,11 @@ allOf:
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- const: faraday,fotg200
|
||||
- const: faraday,fotg210
|
||||
- items:
|
||||
- const: cortina,gemini-usb
|
||||
- const: faraday,fotg210
|
||||
- const: faraday,fotg200
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
@ -66,7 +67,7 @@ examples:
|
||||
#include <dt-bindings/clock/cortina,gemini-clock.h>
|
||||
#include <dt-bindings/reset/cortina,gemini-reset.h>
|
||||
usb0: usb@68000000 {
|
||||
compatible = "cortina,gemini-usb", "faraday,fotg210";
|
||||
compatible = "cortina,gemini-usb", "faraday,fotg200";
|
||||
reg = <0x68000000 0x1000>;
|
||||
interrupts = <10 IRQ_TYPE_LEVEL_HIGH>;
|
||||
resets = <&syscon GEMINI_RESET_USB0>;
|
||||
|
@ -51,7 +51,7 @@ examples:
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
fsa4480@42 {
|
||||
typec-mux@42 {
|
||||
compatible = "fcs,fsa4480";
|
||||
reg = <0x42>;
|
||||
|
||||
|
@ -71,6 +71,9 @@ properties:
|
||||
description:
|
||||
Power pad (PWR) polarity is active low.
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
# Required child node:
|
||||
|
||||
patternProperties:
|
||||
@ -87,12 +90,14 @@ required:
|
||||
- clocks
|
||||
- clock-names
|
||||
- interrupts
|
||||
- power-domains
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/imx8mp-clock.h>
|
||||
#include <dt-bindings/power/imx8mp-power.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
usb3_0: usb@32f10100 {
|
||||
compatible = "fsl,imx8mp-dwc3";
|
||||
@ -102,6 +107,7 @@ examples:
|
||||
<&clk IMX8MP_CLK_USB_ROOT>;
|
||||
clock-names = "hsio", "suspend";
|
||||
interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
|
||||
power-domains = <&hsio_blk_ctrl IMX8MP_HSIOBLK_PD_USB>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
dma-ranges = <0x40000000 0x40000000 0xc0000000>;
|
||||
|
@ -74,6 +74,11 @@ properties:
|
||||
- const: usb-ehci
|
||||
- enum:
|
||||
- generic-ehci
|
||||
- marvell,armada-3700-ehci
|
||||
- marvell,orion-ehci
|
||||
- nuvoton,npcm750-ehci
|
||||
- nuvoton,npcm845-ehci
|
||||
- ti,ehci-omap
|
||||
- usb-ehci
|
||||
|
||||
reg:
|
||||
|
@ -6,9 +6,6 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: USB OHCI Controller
|
||||
|
||||
allOf:
|
||||
- $ref: "usb-hcd.yaml"
|
||||
|
||||
maintainers:
|
||||
- Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
|
||||
@ -49,7 +46,16 @@ properties:
|
||||
- ingenic,jz4740-ohci
|
||||
- snps,hsdk-v1.0-ohci
|
||||
- const: generic-ohci
|
||||
- const: generic-ohci
|
||||
- enum:
|
||||
- generic-ohci
|
||||
- ti,ohci-omap3
|
||||
- items:
|
||||
- enum:
|
||||
- cavium,octeon-6335-ohci
|
||||
- nintendo,hollywood-usb-ohci
|
||||
- nxp,ohci-nxp
|
||||
- st,spear600-ohci
|
||||
- const: usb-ohci
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
@ -119,11 +125,29 @@ properties:
|
||||
- host
|
||||
- otg
|
||||
|
||||
transceiver:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
description:
|
||||
The associated ISP1301 device. Necessary for the UDC controller for
|
||||
connecting to the USB physical layer.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
allOf:
|
||||
- $ref: usb-hcd.yaml
|
||||
- if:
|
||||
not:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: nxp,ohci-nxp
|
||||
then:
|
||||
properties:
|
||||
transceiver: false
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
|
@ -16,6 +16,7 @@ properties:
|
||||
compatible:
|
||||
enum:
|
||||
- usb5e3,608
|
||||
- usb5e3,610
|
||||
|
||||
reg: true
|
||||
|
||||
|
110
Documentation/devicetree/bindings/usb/gpio-sbu-mux.yaml
Normal file
110
Documentation/devicetree/bindings/usb/gpio-sbu-mux.yaml
Normal file
@ -0,0 +1,110 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: "http://devicetree.org/schemas/usb/gpio-sbu-mux.yaml#"
|
||||
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
|
||||
|
||||
title: GPIO-based SBU mux
|
||||
|
||||
maintainers:
|
||||
- Bjorn Andersson <andersson@kernel.org>
|
||||
|
||||
description:
|
||||
In USB Type-C applications the SBU lines needs to be connected, disconnected
|
||||
and swapped depending on the altmode and orientation. This binding describes
|
||||
a family of hardware solutions which switches between these modes using GPIO
|
||||
signals.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- onnn,fsusb43l10x
|
||||
- pericom,pi3usb102
|
||||
- const: gpio-sbu-mux
|
||||
|
||||
enable-gpios:
|
||||
description: Switch enable GPIO
|
||||
|
||||
select-gpios:
|
||||
description: Orientation select
|
||||
|
||||
vcc-supply:
|
||||
description: power supply
|
||||
|
||||
mode-switch:
|
||||
description: Flag the port as possible handle of altmode switching
|
||||
type: boolean
|
||||
|
||||
orientation-switch:
|
||||
description: Flag the port as possible handler of orientation switching
|
||||
type: boolean
|
||||
|
||||
port:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
description:
|
||||
A port node to link the SBU mux to a TypeC controller for the purpose of
|
||||
handling altmode muxing and orientation switching.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- enable-gpios
|
||||
- select-gpios
|
||||
- mode-switch
|
||||
- orientation-switch
|
||||
- port
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
tcpm {
|
||||
connector {
|
||||
compatible = "usb-c-connector";
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
tcpm_hs_out: endpoint {
|
||||
remote-endpoint = <&usb_hs_phy_in>;
|
||||
};
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
tcpm_ss_out: endpoint {
|
||||
remote-endpoint = <&usb_ss_phy_in>;
|
||||
};
|
||||
};
|
||||
|
||||
port@2 {
|
||||
reg = <2>;
|
||||
tcpm_sbu_out: endpoint {
|
||||
remote-endpoint = <&sbu_mux_in>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
sbu-mux {
|
||||
compatible = "pericom,pi3usb102", "gpio-sbu-mux";
|
||||
|
||||
enable-gpios = <&tlmm 101 GPIO_ACTIVE_LOW>;
|
||||
select-gpios = <&tlmm 164 GPIO_ACTIVE_HIGH>;
|
||||
|
||||
mode-switch;
|
||||
orientation-switch;
|
||||
|
||||
port {
|
||||
sbu_mux_in: endpoint {
|
||||
remote-endpoint = <&tcpm_sbu_out>;
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
@ -35,6 +35,7 @@ properties:
|
||||
- mediatek,mt8188-xhci
|
||||
- mediatek,mt8192-xhci
|
||||
- mediatek,mt8195-xhci
|
||||
- mediatek,mt8365-xhci
|
||||
- const: mediatek,mtk-xhci
|
||||
|
||||
reg:
|
||||
|
@ -28,6 +28,7 @@ properties:
|
||||
- mediatek,mt8188-mtu3
|
||||
- mediatek,mt8192-mtu3
|
||||
- mediatek,mt8195-mtu3
|
||||
- mediatek,mt8365-mtu3
|
||||
- const: mediatek,mtu3
|
||||
|
||||
reg:
|
||||
|
@ -1,20 +0,0 @@
|
||||
Nuvoton NPCM7XX SoC USB controllers:
|
||||
-----------------------------
|
||||
|
||||
EHCI:
|
||||
-----
|
||||
|
||||
Required properties:
|
||||
- compatible: should be one of
|
||||
"nuvoton,npcm750-ehci"
|
||||
"nuvoton,npcm845-ehci"
|
||||
- interrupts: Should contain the EHCI interrupt
|
||||
- reg: Physical address and length of the register set for the device
|
||||
|
||||
Example:
|
||||
|
||||
ehci1: usb@f0806000 {
|
||||
compatible = "nuvoton,npcm750-ehci";
|
||||
reg = <0xf0806000 0x1000>;
|
||||
interrupts = <0 61 4>;
|
||||
};
|
@ -22,6 +22,7 @@ properties:
|
||||
- nvidia,tegra210-xudc # For Tegra210
|
||||
- nvidia,tegra186-xudc # For Tegra186
|
||||
- nvidia,tegra194-xudc # For Tegra194
|
||||
- nvidia,tegra234-xudc # For Tegra234
|
||||
|
||||
reg:
|
||||
minItems: 2
|
||||
@ -112,6 +113,8 @@ properties:
|
||||
hvdd-usb-supply:
|
||||
description: USB controller power supply. Must supply 3.3 V.
|
||||
|
||||
dma-coherent: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
@ -153,6 +156,7 @@ allOf:
|
||||
enum:
|
||||
- nvidia,tegra186-xudc
|
||||
- nvidia,tegra194-xudc
|
||||
- nvidia,tegra234-xudc
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
@ -164,6 +168,17 @@ allOf:
|
||||
clock-names:
|
||||
maxItems: 4
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- nvidia,tegra194-xudc
|
||||
- nvidia,tegra234-xudc
|
||||
then:
|
||||
required:
|
||||
- dma-coherent
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
|
159
Documentation/devicetree/bindings/usb/nvidia,tegra234-xusb.yaml
Normal file
159
Documentation/devicetree/bindings/usb/nvidia,tegra234-xusb.yaml
Normal file
@ -0,0 +1,159 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/usb/nvidia,tegra234-xusb.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: NVIDIA Tegra234 xHCI controller
|
||||
|
||||
maintainers:
|
||||
- Thierry Reding <thierry.reding@gmail.com>
|
||||
- Jon Hunter <jonathanh@nvidia.com>
|
||||
|
||||
description: |
|
||||
The Tegra xHCI controller supports both USB2 and USB3 interfaces exposed by
|
||||
the Tegra XUSB pad controller. The xHCI controller controls up to eight
|
||||
ports; there are four USB 2.0 ports and four USB 3.2 Gen1 x1 ports.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: nvidia,tegra234-xusb
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description: xHCI host registers
|
||||
- description: XUSB FPCI registers
|
||||
- description: XUSB bar2 registers
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: hcd
|
||||
- const: fpci
|
||||
- const: bar2
|
||||
|
||||
interrupts:
|
||||
items:
|
||||
- description: xHCI host interrupt
|
||||
- description: mailbox interrupt
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: XUSB host clock
|
||||
- description: XUSB Falcon source clock
|
||||
- description: XUSB SuperSpeed clock
|
||||
- description: XUSB SuperSpeed source clock
|
||||
- description: XUSB HighSpeed clock source
|
||||
- description: XUSB FullSpeed clock source
|
||||
- description: USB PLL
|
||||
- description: reference clock
|
||||
- description: I/O PLL
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: xusb_host
|
||||
- const: xusb_falcon_src
|
||||
- const: xusb_ss
|
||||
- const: xusb_ss_src
|
||||
- const: xusb_hs_src
|
||||
- const: xusb_fs_src
|
||||
- const: pll_u_480m
|
||||
- const: clk_m
|
||||
- const: pll_e
|
||||
|
||||
interconnects:
|
||||
items:
|
||||
- description: read client
|
||||
- description: write client
|
||||
|
||||
interconnect-names:
|
||||
items:
|
||||
- const: dma-mem # read
|
||||
- const: write
|
||||
|
||||
iommus:
|
||||
maxItems: 1
|
||||
|
||||
nvidia,xusb-padctl:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
description: phandle to the XUSB pad controller that is used to configure
|
||||
the USB pads used by the XHCI controller
|
||||
|
||||
phys:
|
||||
minItems: 1
|
||||
maxItems: 8
|
||||
|
||||
phy-names:
|
||||
minItems: 1
|
||||
maxItems: 8
|
||||
items:
|
||||
enum:
|
||||
- usb2-0
|
||||
- usb2-1
|
||||
- usb2-2
|
||||
- usb2-3
|
||||
- usb3-0
|
||||
- usb3-1
|
||||
- usb3-2
|
||||
- usb3-3
|
||||
|
||||
power-domains:
|
||||
items:
|
||||
- description: XUSBC power domain (for Host and USB 2.0)
|
||||
- description: XUSBA power domain (for SuperSpeed)
|
||||
|
||||
power-domain-names:
|
||||
items:
|
||||
- const: xusb_host
|
||||
- const: xusb_ss
|
||||
|
||||
dma-coherent: true
|
||||
|
||||
allOf:
|
||||
- $ref: usb-xhci.yaml
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/tegra234-clock.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/memory/tegra234-mc.h>
|
||||
#include <dt-bindings/power/tegra234-powergate.h>
|
||||
|
||||
usb@3610000 {
|
||||
compatible = "nvidia,tegra234-xusb";
|
||||
reg = <0x03610000 0x40000>,
|
||||
<0x03600000 0x10000>,
|
||||
<0x03650000 0x10000>;
|
||||
reg-names = "hcd", "fpci", "bar2";
|
||||
|
||||
interrupts = <GIC_SPI 163 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 164 IRQ_TYPE_LEVEL_HIGH>;
|
||||
|
||||
clocks = <&bpmp TEGRA234_CLK_XUSB_CORE_HOST>,
|
||||
<&bpmp TEGRA234_CLK_XUSB_FALCON>,
|
||||
<&bpmp TEGRA234_CLK_XUSB_CORE_SS>,
|
||||
<&bpmp TEGRA234_CLK_XUSB_SS>,
|
||||
<&bpmp TEGRA234_CLK_CLK_M>,
|
||||
<&bpmp TEGRA234_CLK_XUSB_FS>,
|
||||
<&bpmp TEGRA234_CLK_UTMIP_PLL>,
|
||||
<&bpmp TEGRA234_CLK_CLK_M>,
|
||||
<&bpmp TEGRA234_CLK_PLLE>;
|
||||
clock-names = "xusb_host", "xusb_falcon_src",
|
||||
"xusb_ss", "xusb_ss_src", "xusb_hs_src",
|
||||
"xusb_fs_src", "pll_u_480m", "clk_m",
|
||||
"pll_e";
|
||||
interconnects = <&mc TEGRA234_MEMORY_CLIENT_XUSB_HOSTR &emc>,
|
||||
<&mc TEGRA234_MEMORY_CLIENT_XUSB_HOSTW &emc>;
|
||||
interconnect-names = "dma-mem", "write";
|
||||
iommus = <&smmu_niso1 TEGRA234_SID_XUSB_HOST>;
|
||||
|
||||
power-domains = <&bpmp TEGRA234_POWER_DOMAIN_XUSBC>,
|
||||
<&bpmp TEGRA234_POWER_DOMAIN_XUSBA>;
|
||||
power-domain-names = "xusb_host", "xusb_ss";
|
||||
|
||||
nvidia,xusb-padctl = <&xusb_padctl>;
|
||||
|
||||
phys = <&pad_lanes_usb2_0>;
|
||||
phy-names = "usb2-0";
|
||||
};
|
@ -1,24 +0,0 @@
|
||||
* OHCI controller, NXP ohci-nxp variant
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "nxp,ohci-nxp"
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
- interrupts: The OHCI interrupt
|
||||
- transceiver: phandle of the associated ISP1301 device - this is necessary for
|
||||
the UDC controller for connecting to the USB physical layer
|
||||
|
||||
Example (LPC32xx):
|
||||
|
||||
isp1301: usb-transceiver@2c {
|
||||
compatible = "nxp,isp1301";
|
||||
reg = <0x2c>;
|
||||
};
|
||||
|
||||
ohci@31020000 {
|
||||
compatible = "nxp,ohci-nxp";
|
||||
reg = <0x31020000 0x300>;
|
||||
interrupt-parent = <&mic>;
|
||||
interrupts = <0x3b 0>;
|
||||
transceiver = <&isp1301>;
|
||||
};
|
@ -1,15 +0,0 @@
|
||||
OMAP HS USB OHCI controller (OMAP3 and later)
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: should be "ti,ohci-omap3"
|
||||
- reg: should contain one register range i.e. start and length
|
||||
- interrupts: description of the interrupt line
|
||||
|
||||
Example for OMAP4:
|
||||
|
||||
usbhsohci: ohci@4a064800 {
|
||||
compatible = "ti,ohci-omap3";
|
||||
reg = <0x4a064800 0x400>;
|
||||
interrupts = <0 76 0x4>;
|
||||
};
|
@ -22,7 +22,7 @@ Optional properties:
|
||||
Example:
|
||||
|
||||
usb0: ohci@4c000000 {
|
||||
compatible = "marvell,pxa-ohci", "usb-ohci";
|
||||
compatible = "marvell,pxa-ohci";
|
||||
reg = <0x4c000000 0x100000>;
|
||||
interrupts = <18>;
|
||||
marvell,enable-port1;
|
||||
|
@ -58,6 +58,9 @@ properties:
|
||||
description: specifies a phandle to PM domain provider node
|
||||
maxItems: 1
|
||||
|
||||
required-opps:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
description: |
|
||||
Several clocks are used, depending on the variant. Typical ones are::
|
||||
|
68
Documentation/devicetree/bindings/usb/renesas,rzn1-usbf.yaml
Normal file
68
Documentation/devicetree/bindings/usb/renesas,rzn1-usbf.yaml
Normal file
@ -0,0 +1,68 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/usb/renesas,rzn1-usbf.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Renesas RZ/N1 SoCs USBF (USB Function) controller
|
||||
|
||||
description: |
|
||||
The Renesas USBF controller is an USB2.0 device
|
||||
controller (UDC).
|
||||
|
||||
maintainers:
|
||||
- Herve Codina <herve.codina@bootlin.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- renesas,r9a06g032-usbf
|
||||
- const: renesas,rzn1-usbf
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: Internal bus clock (AHB) for Function
|
||||
- description: Internal bus clock (AHB) for Power Management
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: hclkf
|
||||
- const: hclkpm
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
items:
|
||||
- description: The USBF EPC interrupt
|
||||
- description: The USBF AHB-EPC interrupt
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- clock-names
|
||||
- power-domains
|
||||
- interrupts
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/clock/r9a06g032-sysctrl.h>
|
||||
|
||||
usb@4001e000 {
|
||||
compatible = "renesas,r9a06g032-usbf", "renesas,rzn1-usbf";
|
||||
reg = <0x4001e000 0x2000>;
|
||||
interrupts = <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&sysctrl R9A06G032_HCLK_USBF>,
|
||||
<&sysctrl R9A06G032_HCLK_USBPM>;
|
||||
clock-names = "hclkf", "hclkpm";
|
||||
power-domains = <&sysctrl>;
|
||||
};
|
129
Documentation/devicetree/bindings/usb/renesas,rzv2m-usb3drd.yaml
Normal file
129
Documentation/devicetree/bindings/usb/renesas,rzv2m-usb3drd.yaml
Normal file
@ -0,0 +1,129 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/usb/renesas,rzv2m-usb3drd.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Renesas RZ/V2M USB 3.1 DRD controller
|
||||
|
||||
maintainers:
|
||||
- Biju Das <biju.das.jz@bp.renesas.com>
|
||||
|
||||
description: |
|
||||
The RZ/V2{M, MA} USB3.1 DRD module supports the following functions
|
||||
* Role swapping function by the ID pin of the Micro-AB receptacle
|
||||
* Battery Charging Specification Revision 1.2
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- renesas,r9a09g011-usb3drd # RZ/V2M
|
||||
- renesas,r9a09g055-usb3drd # RZ/V2MA
|
||||
- const: renesas,rzv2m-usb3drd
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
items:
|
||||
- description: Dual Role Device (DRD)
|
||||
- description: Battery Charging
|
||||
- description: Global Purpose Input
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: drd
|
||||
- const: bc
|
||||
- const: gpi
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: Peripheral AXI clock
|
||||
- description: APB clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: axi
|
||||
- const: reg
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
ranges: true
|
||||
|
||||
'#address-cells':
|
||||
enum: [ 1, 2 ]
|
||||
|
||||
'#size-cells':
|
||||
enum: [ 1, 2 ]
|
||||
|
||||
patternProperties:
|
||||
"^usb3peri@[0-9a-f]+$":
|
||||
type: object
|
||||
$ref: /schemas/usb/renesas,usb3-peri.yaml
|
||||
|
||||
"^usb@[0-9a-f]+$":
|
||||
type: object
|
||||
$ref: renesas,usb-xhci.yaml#
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- interrupt-names
|
||||
- clocks
|
||||
- clock-names
|
||||
- power-domains
|
||||
- resets
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/r9a09g011-cpg.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
usb3drd: usb@85070400 {
|
||||
compatible = "renesas,r9a09g011-usb3drd", "renesas,rzv2m-usb3drd";
|
||||
reg = <0x85070400 0x100>;
|
||||
interrupts = <GIC_SPI 242 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 243 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 244 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "drd", "bc", "gpi";
|
||||
clocks = <&cpg CPG_MOD R9A09G011_USB_ACLK_P>,
|
||||
<&cpg CPG_MOD R9A09G011_USB_PCLK>;
|
||||
clock-names = "axi", "reg";
|
||||
power-domains = <&cpg>;
|
||||
resets = <&cpg R9A09G011_USB_DRD_RESET>;
|
||||
ranges;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
usb3host: usb@85060000 {
|
||||
compatible = "renesas,r9a09g011-xhci",
|
||||
"renesas,rzv2m-xhci";
|
||||
reg = <0x85060000 0x2000>;
|
||||
interrupts = <GIC_SPI 245 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&cpg CPG_MOD R9A09G011_USB_ACLK_H>,
|
||||
<&cpg CPG_MOD R9A09G011_USB_PCLK>;
|
||||
clock-names = "axi", "reg";
|
||||
power-domains = <&cpg>;
|
||||
resets = <&cpg R9A09G011_USB_ARESETN_H>;
|
||||
};
|
||||
|
||||
usb3peri: usb3peri@85070000 {
|
||||
compatible = "renesas,r9a09g011-usb3-peri",
|
||||
"renesas,rzv2m-usb3-peri";
|
||||
reg = <0x85070000 0x400>;
|
||||
interrupts = <GIC_SPI 246 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&cpg CPG_MOD R9A09G011_USB_ACLK_P>,
|
||||
<&cpg CPG_MOD R9A09G011_USB_PCLK>;
|
||||
clock-names = "axi", "reg";
|
||||
power-domains = <&cpg>;
|
||||
resets = <&cpg R9A09G011_USB_ARESETN_P>;
|
||||
};
|
||||
};
|
@ -10,9 +10,6 @@ maintainers:
|
||||
- Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
|
||||
- Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
|
||||
|
||||
allOf:
|
||||
- $ref: "usb-xhci.yaml"
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
@ -37,6 +34,11 @@ properties:
|
||||
- renesas,xhci-r8a77965 # R-Car M3-N
|
||||
- renesas,xhci-r8a77990 # R-Car E3
|
||||
- const: renesas,rcar-gen3-xhci # R-Car Gen3 and RZ/G2
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,r9a09g011-xhci # RZ/V2M
|
||||
- renesas,r9a09g055-xhci # RZ/V2MA
|
||||
- const: renesas,rzv2m-xhci # RZ/{V2M, V2MA}
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
@ -45,7 +47,16 @@ properties:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
minItems: 1
|
||||
items:
|
||||
- description: Main clock for host
|
||||
- description: Register access clock
|
||||
|
||||
clock-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- const: axi
|
||||
- const: reg
|
||||
|
||||
phys:
|
||||
maxItems: 1
|
||||
@ -68,6 +79,28 @@ required:
|
||||
- power-domains
|
||||
- resets
|
||||
|
||||
allOf:
|
||||
- $ref: usb-xhci.yaml
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- renesas,rzv2m-xhci
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 2
|
||||
clock-names:
|
||||
minItems: 2
|
||||
required:
|
||||
- clock-names
|
||||
else:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
|
@ -28,26 +28,14 @@ properties:
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,r9a09g011-usb3-peri # RZ/V2M
|
||||
- renesas,r9a09g055-usb3-peri # RZ/V2MA
|
||||
- const: renesas,rzv2m-usb3-peri
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
minItems: 1
|
||||
items:
|
||||
- description: Combined interrupt for DMA, SYS and ERR
|
||||
- description: Dual Role Device (DRD)
|
||||
- description: Battery Charging
|
||||
- description: Global Purpose Input
|
||||
|
||||
interrupt-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- const: all_p
|
||||
- const: drd
|
||||
- const: bc
|
||||
- const: gpi
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
minItems: 1
|
||||
@ -58,7 +46,7 @@ properties:
|
||||
clock-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- const: aclk
|
||||
- const: axi
|
||||
- const: reg
|
||||
|
||||
phys:
|
||||
@ -71,15 +59,7 @@ properties:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
minItems: 1
|
||||
items:
|
||||
- description: Peripheral reset
|
||||
- description: DRD reset
|
||||
|
||||
reset-names:
|
||||
items:
|
||||
- const: aresetn_p
|
||||
- const: drd_reset
|
||||
maxItems: 1
|
||||
|
||||
usb-role-switch:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
@ -127,25 +107,13 @@ allOf:
|
||||
minItems: 2
|
||||
clock-names:
|
||||
minItems: 2
|
||||
interrupts:
|
||||
minItems: 4
|
||||
interrupt-names:
|
||||
minItems: 4
|
||||
resets:
|
||||
minItems: 2
|
||||
required:
|
||||
- clock-names
|
||||
- interrupt-names
|
||||
- resets
|
||||
- reset-names
|
||||
else:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 1
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
|
@ -29,7 +29,6 @@ select:
|
||||
contains:
|
||||
enum:
|
||||
- rockchip,rk3328-dwc3
|
||||
- rockchip,rk3399-dwc3
|
||||
- rockchip,rk3568-dwc3
|
||||
required:
|
||||
- compatible
|
||||
@ -39,7 +38,6 @@ properties:
|
||||
items:
|
||||
- enum:
|
||||
- rockchip,rk3328-dwc3
|
||||
- rockchip,rk3399-dwc3
|
||||
- rockchip,rk3568-dwc3
|
||||
- const: snps,dwc3
|
||||
|
||||
@ -90,7 +88,7 @@ required:
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/rk3399-cru.h>
|
||||
#include <dt-bindings/clock/rk3328-cru.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
bus {
|
||||
@ -98,11 +96,11 @@ examples:
|
||||
#size-cells = <2>;
|
||||
|
||||
usbdrd3_0: usb@fe800000 {
|
||||
compatible = "rockchip,rk3399-dwc3", "snps,dwc3";
|
||||
compatible = "rockchip,rk3328-dwc3", "snps,dwc3";
|
||||
reg = <0x0 0xfe800000 0x0 0x100000>;
|
||||
interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&cru SCLK_USB3OTG0_REF>, <&cru SCLK_USB3OTG0_SUSPEND>,
|
||||
<&cru ACLK_USB3OTG0>, <&cru ACLK_USB3_GRF>;
|
||||
clocks = <&cru SCLK_USB3OTG_REF>, <&cru SCLK_USB3OTG_SUSPEND>,
|
||||
<&cru ACLK_USB3OTG>;
|
||||
clock-names = "ref_clk", "suspend_clk",
|
||||
"bus_clk", "grf_clk";
|
||||
dr_mode = "otg";
|
||||
|
115
Documentation/devicetree/bindings/usb/rockchip,rk3399-dwc3.yaml
Normal file
115
Documentation/devicetree/bindings/usb/rockchip,rk3399-dwc3.yaml
Normal file
@ -0,0 +1,115 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/usb/rockchip,rk3399-dwc3.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Rockchip RK3399 SuperSpeed DWC3 USB SoC controller
|
||||
|
||||
maintainers:
|
||||
- Heiko Stuebner <heiko@sntech.de>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: rockchip,rk3399-dwc3
|
||||
|
||||
'#address-cells':
|
||||
const: 2
|
||||
|
||||
'#size-cells':
|
||||
const: 2
|
||||
|
||||
ranges: true
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description:
|
||||
Controller reference clock, must to be 24 MHz
|
||||
- description:
|
||||
Controller suspend clock, must to be 24 MHz or 32 KHz
|
||||
- description:
|
||||
Master/Core clock, must to be >= 62.5 MHz for SS
|
||||
operation and >= 30MHz for HS operation
|
||||
- description:
|
||||
USB3 aclk peri
|
||||
- description:
|
||||
USB3 aclk
|
||||
- description:
|
||||
Controller grf clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: ref_clk
|
||||
- const: suspend_clk
|
||||
- const: bus_clk
|
||||
- const: aclk_usb3_rksoc_axi_perf
|
||||
- const: aclk_usb3
|
||||
- const: grf_clk
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
reset-names:
|
||||
const: usb3-otg
|
||||
|
||||
patternProperties:
|
||||
'^usb@':
|
||||
$ref: snps,dwc3.yaml#
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- '#address-cells'
|
||||
- '#size-cells'
|
||||
- ranges
|
||||
- clocks
|
||||
- clock-names
|
||||
- resets
|
||||
- reset-names
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/rk3399-cru.h>
|
||||
#include <dt-bindings/power/rk3399-power.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
bus {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
usb {
|
||||
compatible = "rockchip,rk3399-dwc3";
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
clocks = <&cru SCLK_USB3OTG0_REF>, <&cru SCLK_USB3OTG0_SUSPEND>,
|
||||
<&cru ACLK_USB3OTG0>, <&cru ACLK_USB3_RKSOC_AXI_PERF>,
|
||||
<&cru ACLK_USB3>, <&cru ACLK_USB3_GRF>;
|
||||
clock-names = "ref_clk", "suspend_clk",
|
||||
"bus_clk", "aclk_usb3_rksoc_axi_perf",
|
||||
"aclk_usb3", "grf_clk";
|
||||
resets = <&cru SRST_A_USB3_OTG0>;
|
||||
reset-names = "usb3-otg";
|
||||
|
||||
usb@fe800000 {
|
||||
compatible = "snps,dwc3";
|
||||
reg = <0x0 0xfe800000 0x0 0x100000>;
|
||||
interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH 0>;
|
||||
clocks = <&cru SCLK_USB3OTG0_REF>, <&cru ACLK_USB3OTG0>,
|
||||
<&cru SCLK_USB3OTG0_SUSPEND>;
|
||||
clock-names = "ref", "bus_early", "suspend";
|
||||
dr_mode = "otg";
|
||||
phys = <&u2phy0_otg>, <&tcphy0_usb3>;
|
||||
phy-names = "usb2-phy", "usb3-phy";
|
||||
phy_type = "utmi_wide";
|
||||
snps,dis_enblslpm_quirk;
|
||||
snps,dis-u2-freeclk-exists-quirk;
|
||||
snps,dis_u2_susphy_quirk;
|
||||
snps,dis-del-phy-power-chg-quirk;
|
||||
snps,dis-tx-ipgap-linecheck-quirk;
|
||||
power-domains = <&power RK3399_PD_USB3>;
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
@ -108,19 +108,19 @@ examples:
|
||||
#include <dt-bindings/clock/exynos5420.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
usb {
|
||||
usb@12000000 {
|
||||
compatible = "samsung,exynos5250-dwusb3";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges;
|
||||
ranges = <0x0 0x12000000 0x10000>;
|
||||
clocks = <&clock CLK_USBD300>;
|
||||
clock-names = "usbdrd30";
|
||||
vdd33-supply = <&ldo9_reg>;
|
||||
vdd10-supply = <&ldo11_reg>;
|
||||
|
||||
usb@12000000 {
|
||||
usb@0 {
|
||||
compatible = "snps,dwc3";
|
||||
reg = <0x12000000 0x10000>;
|
||||
reg = <0x0 0x10000>;
|
||||
interrupts = <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>;
|
||||
phys = <&usbdrd_phy0 0>, <&usbdrd_phy0 1>;
|
||||
phy-names = "usb2-phy", "usb3-phy";
|
||||
|
@ -91,6 +91,16 @@ properties:
|
||||
- usb2-phy
|
||||
- usb3-phy
|
||||
|
||||
power-domains:
|
||||
description:
|
||||
The DWC3 has 2 power-domains. The power management unit (PMU) and
|
||||
everything else. The PMU is typically always powered and may not have an
|
||||
entry.
|
||||
minItems: 1
|
||||
items:
|
||||
- description: Core
|
||||
- description: Power management unit
|
||||
|
||||
resets:
|
||||
minItems: 1
|
||||
|
||||
|
@ -1,35 +0,0 @@
|
||||
ST SPEAr SoC USB controllers:
|
||||
-----------------------------
|
||||
|
||||
EHCI:
|
||||
-----
|
||||
|
||||
Required properties:
|
||||
- compatible: "st,spear600-ehci"
|
||||
- interrupts: Should contain the EHCI interrupt
|
||||
|
||||
Example:
|
||||
|
||||
ehci@e1800000 {
|
||||
compatible = "st,spear600-ehci", "usb-ehci";
|
||||
reg = <0xe1800000 0x1000>;
|
||||
interrupt-parent = <&vic1>;
|
||||
interrupts = <27>;
|
||||
};
|
||||
|
||||
|
||||
OHCI:
|
||||
-----
|
||||
|
||||
Required properties:
|
||||
- compatible: "st,spear600-ohci"
|
||||
- interrupts: Should contain the OHCI interrupt
|
||||
|
||||
Example:
|
||||
|
||||
ohci@e1900000 {
|
||||
compatible = "st,spear600-ohci", "usb-ohci";
|
||||
reg = <0xe1800000 0x1000>;
|
||||
interrupt-parent = <&vic1>;
|
||||
interrupts = <26>;
|
||||
};
|
@ -46,7 +46,6 @@ properties:
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
|
@ -23,6 +23,8 @@ properties:
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
wakeup-source: true
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
@ -48,6 +50,7 @@ examples:
|
||||
tps6598x: tps6598x@38 {
|
||||
compatible = "ti,tps6598x";
|
||||
reg = <0x38>;
|
||||
wakeup-source;
|
||||
|
||||
interrupt-parent = <&msmgpio>;
|
||||
interrupts = <107 IRQ_TYPE_LEVEL_LOW>;
|
||||
|
@ -35,7 +35,7 @@ properties:
|
||||
maxItems: 1
|
||||
|
||||
vbus-regulator:
|
||||
description: Should specifiy the regulator supplying current drawn from
|
||||
description: Should specify the regulator supplying current drawn from
|
||||
the VBus line.
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
|
||||
|
@ -8,6 +8,7 @@ Required properties:
|
||||
"fsl,imx6sx-usbmisc" for imx6sx
|
||||
"fsl,imx7d-usbmisc" for imx7d
|
||||
"fsl,imx7ulp-usbmisc" for imx7ulp
|
||||
"fsl,imx8mm-usbmisc" for imx8mm
|
||||
- reg: Should contain registers location and length
|
||||
|
||||
Examples:
|
||||
|
71
Documentation/devicetree/bindings/usb/vialab,vl817.yaml
Normal file
71
Documentation/devicetree/bindings/usb/vialab,vl817.yaml
Normal file
@ -0,0 +1,71 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only or BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/usb/vialab,vl817.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Via labs VL817 USB 3.1 hub controller
|
||||
|
||||
maintainers:
|
||||
- Anand Moon <linux.amoon@gmail.com>
|
||||
|
||||
allOf:
|
||||
- $ref: usb-device.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- usb2109,2817
|
||||
- usb2109,817
|
||||
|
||||
reg: true
|
||||
|
||||
reset-gpios:
|
||||
maxItems: 1
|
||||
description:
|
||||
GPIO controlling the RESET# pin.
|
||||
|
||||
vdd-supply:
|
||||
description:
|
||||
phandle to the regulator that provides power to the hub.
|
||||
|
||||
peer-hub:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
description:
|
||||
phandle to the peer hub on the controller.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- reset-gpios
|
||||
- vdd-supply
|
||||
- peer-hub
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
usb {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
/* 2.0 hub on port 1 */
|
||||
hub_2_0: hub@1 {
|
||||
compatible = "usb2109,2817";
|
||||
reg = <1>;
|
||||
vdd-supply = <&vcc_5v>;
|
||||
peer-hub = <&hub_3_0>;
|
||||
reset-gpios = <&gpio 20 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
|
||||
/* 3.1 hub on port 4 */
|
||||
hub_3_0: hub@2 {
|
||||
compatible = "usb2109,817";
|
||||
reg = <2>;
|
||||
vdd-supply = <&vcc_5v>;
|
||||
peer-hub = <&hub_2_0>;
|
||||
reset-gpios = <&gpio 20 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
};
|
@ -1422,6 +1422,8 @@ patternProperties:
|
||||
description: Vertexcom Technologies, Inc.
|
||||
"^via,.*":
|
||||
description: VIA Technologies, Inc.
|
||||
"^vialab,.*":
|
||||
description: VIA Labs, Inc.
|
||||
"^vicor,.*":
|
||||
description: Vicor Corporation
|
||||
"^videostrong,.*":
|
||||
|
@ -35,10 +35,10 @@ which can show otg fsm variables and some controller registers value::
|
||||
1) Power up 2 Freescale i.MX6Q sabre SD boards with gadget class driver loaded
|
||||
(e.g. g_mass_storage).
|
||||
|
||||
2) Connect 2 boards with usb cable with one end is micro A plug, the other end
|
||||
2) Connect 2 boards with usb cable: one end is micro A plug, the other end
|
||||
is micro B plug.
|
||||
|
||||
The A-device(with micro A plug inserted) should enumerate B-device.
|
||||
The A-device (with micro A plug inserted) should enumerate B-device.
|
||||
|
||||
3) Role switch
|
||||
|
||||
@ -54,18 +54,19 @@ which can show otg fsm variables and some controller registers value::
|
||||
|
||||
echo 0 > /sys/bus/platform/devices/ci_hdrc.0/inputs/b_bus_req
|
||||
|
||||
or, by introducing HNP polling, B-Host can know when A-peripheral wish
|
||||
to be host role, so this role switch also can be trigged in A-peripheral
|
||||
side by answering the polling from B-Host, this can be done on A-device::
|
||||
or, by introducing HNP polling, B-Host can know when A-peripheral wishes to
|
||||
be in the host role, so this role switch also can be triggered in
|
||||
A-peripheral side by answering the polling from B-Host. This can be done on
|
||||
A-device::
|
||||
|
||||
echo 1 > /sys/bus/platform/devices/ci_hdrc.0/inputs/a_bus_req
|
||||
|
||||
A-device should switch back to host and enumerate B-device.
|
||||
|
||||
5) Remove B-device(unplug micro B plug) and insert again in 10 seconds,
|
||||
5) Remove B-device (unplug micro B plug) and insert again in 10 seconds;
|
||||
A-device should enumerate B-device again.
|
||||
|
||||
6) Remove B-device(unplug micro B plug) and insert again after 10 seconds,
|
||||
6) Remove B-device (unplug micro B plug) and insert again after 10 seconds;
|
||||
A-device should NOT enumerate B-device.
|
||||
|
||||
if A-device wants to use bus:
|
||||
@ -105,7 +106,7 @@ July 27, 2012 Revision 2.0 version 1.1a"
|
||||
2. How to enable USB as system wakeup source
|
||||
--------------------------------------------
|
||||
Below is the example for how to enable USB as system wakeup source
|
||||
at imx6 platform.
|
||||
on an imx6 platform.
|
||||
|
||||
2.1 Enable core's wakeup::
|
||||
|
||||
@ -128,6 +129,6 @@ at imx6 platform.
|
||||
echo enabled > /sys/bus/usb/devices/1-1/power/wakeup
|
||||
|
||||
If the system has only one usb port, and you want usb wakeup at this port, you
|
||||
can use below script to enable usb wakeup::
|
||||
can use the below script to enable usb wakeup::
|
||||
|
||||
for i in $(find /sys -name wakeup | grep usb);do echo enabled > $i;done;
|
||||
|
@ -813,7 +813,7 @@ the user must provide the following:
|
||||
================== ====================================================
|
||||
|
||||
Each frame description contains frame interval specification, and each
|
||||
such specification consists of a number of lines with an inverval value
|
||||
such specification consists of a number of lines with an interval value
|
||||
in each line. The rules stated above are best illustrated with an example::
|
||||
|
||||
# mkdir functions/uvc.usb0/control/header/h
|
||||
|
@ -90,6 +90,16 @@ Then the strings can be specified::
|
||||
$ echo <manufacturer> > strings/0x409/manufacturer
|
||||
$ echo <product> > strings/0x409/product
|
||||
|
||||
Further custom string descriptors can be created as directories within the
|
||||
language's directory, with the string text being written to the "s" attribute
|
||||
within the string's directory:
|
||||
|
||||
$ mkdir strings/0x409/xu.0
|
||||
$ echo <string text> > strings/0x409/xu.0/s
|
||||
|
||||
Where function drivers support it, functions may allow symlinks to these custom
|
||||
string descriptors to associate those strings with class descriptors.
|
||||
|
||||
2. Creating the configurations
|
||||
------------------------------
|
||||
|
||||
|
@ -150,7 +150,7 @@ Module parameters
|
||||
- bcdDevice -- USB Device version (BCD) (16 bit integer)
|
||||
- iManufacturer -- USB Manufacturer string (string)
|
||||
- iProduct -- USB Product string (string)
|
||||
- iSerialNumber -- SerialNumber string (sting)
|
||||
- iSerialNumber -- SerialNumber string (string)
|
||||
|
||||
sysfs entries
|
||||
=============
|
||||
|
@ -17811,6 +17811,14 @@ S: Maintained
|
||||
F: Documentation/devicetree/bindings/rtc/renesas,rzn1-rtc.yaml
|
||||
F: drivers/rtc/rtc-rzn1.c
|
||||
|
||||
RENESAS RZ/N1 USBF CONTROLLER DRIVER
|
||||
M: Herve Codina <herve.codina@bootlin.com>
|
||||
L: linux-renesas-soc@vger.kernel.org
|
||||
L: linux-usb@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/usb/renesas,rzn1-usbf.yaml
|
||||
F: drivers/usb/gadget/udc/renesas_usbf.c
|
||||
|
||||
RENESAS R-CAR GEN3 & RZ/N1 NAND CONTROLLER DRIVER
|
||||
M: Miquel Raynal <miquel.raynal@bootlin.com>
|
||||
L: linux-mtd@lists.infradead.org
|
||||
|
@ -281,19 +281,6 @@
|
||||
"J7 Header Pin 6", "J7 Header Pin 5",
|
||||
"J7 Header Pin 7", "HDMI_CEC",
|
||||
"SYS_LED", "", "";
|
||||
|
||||
/*
|
||||
* WARNING: The USB Hub on the Odroid-C1/C1+ needs a reset signal
|
||||
* to be turned high in order to be detected by the USB Controller.
|
||||
* This signal should be handled by a USB specific power sequence
|
||||
* in order to reset the Hub when USB bus is powered down.
|
||||
*/
|
||||
usb-hub {
|
||||
gpio-hog;
|
||||
gpios = <GPIOAO_4 GPIO_ACTIVE_HIGH>;
|
||||
output-high;
|
||||
line-name = "usb-hub-reset";
|
||||
};
|
||||
};
|
||||
|
||||
&ir_receiver {
|
||||
@ -381,5 +368,16 @@
|
||||
};
|
||||
|
||||
&usb1 {
|
||||
dr_mode = "host";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
status = "okay";
|
||||
|
||||
hub@1 {
|
||||
/* Genesys Logic GL852G usb hub */
|
||||
compatible = "usb5e3,610";
|
||||
reg = <1>;
|
||||
vdd-supply = <&p5v0>;
|
||||
reset-gpio = <&gpio_ao GPIOAO_4 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
};
|
||||
|
@ -250,21 +250,6 @@
|
||||
};
|
||||
};
|
||||
|
||||
&gpio_ao {
|
||||
/*
|
||||
* WARNING: The USB Hub on the Odroid-C2 needs a reset signal
|
||||
* to be turned high in order to be detected by the USB Controller
|
||||
* This signal should be handled by a USB specific power sequence
|
||||
* in order to reset the Hub when USB bus is powered down.
|
||||
*/
|
||||
hog-0 {
|
||||
gpio-hog;
|
||||
gpios = <GPIOAO_4 GPIO_ACTIVE_HIGH>;
|
||||
output-high;
|
||||
line-name = "usb-hub-reset";
|
||||
};
|
||||
};
|
||||
|
||||
&hdmi_tx {
|
||||
status = "okay";
|
||||
pinctrl-0 = <&hdmi_hpd_pins>, <&hdmi_i2c_pins>;
|
||||
@ -414,5 +399,16 @@
|
||||
};
|
||||
|
||||
&usb1 {
|
||||
dr_mode = "host";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
status = "okay";
|
||||
|
||||
hub@1 {
|
||||
/* Genesys Logic GL852G USB 2.0 hub */
|
||||
compatible = "usb5e3,610";
|
||||
reg = <1>;
|
||||
vdd-supply = <&p5v0>;
|
||||
reset-gpio = <&gpio_ao GPIOAO_4 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
};
|
||||
|
@ -26,20 +26,30 @@
|
||||
sound {
|
||||
model = "ODROID-C4";
|
||||
};
|
||||
};
|
||||
|
||||
&gpio {
|
||||
/*
|
||||
* WARNING: The USB Hub on the Odroid-C4 needs a reset signal
|
||||
* to be turned high in order to be detected by the USB Controller
|
||||
* This signal should be handled by a USB specific power sequence
|
||||
* in order to reset the Hub when USB bus is powered down.
|
||||
*/
|
||||
hog-0 {
|
||||
gpio-hog;
|
||||
gpios = <GPIOH_4 GPIO_ACTIVE_HIGH>;
|
||||
output-high;
|
||||
line-name = "usb-hub-reset";
|
||||
/* USB hub supports both USB 2.0 and USB 3.0 root hub */
|
||||
usb-hub {
|
||||
dr_mode = "host";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
/* 2.0 hub on port 1 */
|
||||
hub_2_0: hub@1 {
|
||||
compatible = "usb2109,2817";
|
||||
reg = <1>;
|
||||
peer-hub = <&hub_3_0>;
|
||||
reset-gpios = <&gpio GPIOH_4 GPIO_ACTIVE_LOW>;
|
||||
vdd-supply = <&vcc_5v>;
|
||||
};
|
||||
|
||||
/* 3.1 hub on port 4 */
|
||||
hub_3_0: hub@2 {
|
||||
compatible = "usb2109,817";
|
||||
reg = <2>;
|
||||
peer-hub = <&hub_2_0>;
|
||||
reset-gpios = <&gpio GPIOH_4 GPIO_ACTIVE_LOW>;
|
||||
vdd-supply = <&vcc_5v>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -938,6 +938,7 @@ CONFIG_USB_SERIAL_FTDI_SIO=m
|
||||
CONFIG_USB_SERIAL_OPTION=m
|
||||
CONFIG_USB_QCOM_EUD=m
|
||||
CONFIG_USB_HSIC_USB3503=y
|
||||
CONFIG_USB_ONBOARD_HUB=m
|
||||
CONFIG_NOP_USB_XCEIV=y
|
||||
CONFIG_USB_GADGET=y
|
||||
CONFIG_USB_RENESAS_USBHS_UDC=m
|
||||
@ -960,6 +961,8 @@ CONFIG_TYPEC_TCPCI=m
|
||||
CONFIG_TYPEC_FUSB302=m
|
||||
CONFIG_TYPEC_TPS6598X=m
|
||||
CONFIG_TYPEC_HD3SS3220=m
|
||||
CONFIG_TYPEC_UCSI=m
|
||||
CONFIG_UCSI_CCG=m
|
||||
CONFIG_TYPEC_MUX_GPIO_SBU=m
|
||||
CONFIG_MMC=y
|
||||
CONFIG_MMC_BLOCK_MINORS=32
|
||||
|
@ -25,6 +25,8 @@
|
||||
#include <linux/spinlock.h>
|
||||
#include <dt-bindings/clock/r9a06g032-sysctrl.h>
|
||||
|
||||
#define R9A06G032_SYSCTRL_USB 0x00
|
||||
#define R9A06G032_SYSCTRL_USB_H2MODE (1<<1)
|
||||
#define R9A06G032_SYSCTRL_DMAMUX 0xA0
|
||||
|
||||
struct r9a06g032_gate {
|
||||
@ -918,6 +920,29 @@ static void r9a06g032_clocks_del_clk_provider(void *data)
|
||||
of_clk_del_provider(data);
|
||||
}
|
||||
|
||||
static void __init r9a06g032_init_h2mode(struct r9a06g032_priv *clocks)
|
||||
{
|
||||
struct device_node *usbf_np = NULL;
|
||||
u32 usb;
|
||||
|
||||
while ((usbf_np = of_find_compatible_node(usbf_np, NULL,
|
||||
"renesas,rzn1-usbf"))) {
|
||||
if (of_device_is_available(usbf_np))
|
||||
break;
|
||||
}
|
||||
|
||||
usb = readl(clocks->reg + R9A06G032_SYSCTRL_USB);
|
||||
if (usbf_np) {
|
||||
/* 1 host and 1 device mode */
|
||||
usb &= ~R9A06G032_SYSCTRL_USB_H2MODE;
|
||||
of_node_put(usbf_np);
|
||||
} else {
|
||||
/* 2 hosts mode */
|
||||
usb |= R9A06G032_SYSCTRL_USB_H2MODE;
|
||||
}
|
||||
writel(usb, clocks->reg + R9A06G032_SYSCTRL_USB);
|
||||
}
|
||||
|
||||
static int __init r9a06g032_clocks_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
@ -947,6 +972,9 @@ static int __init r9a06g032_clocks_probe(struct platform_device *pdev)
|
||||
clocks->reg = of_iomap(np, 0);
|
||||
if (WARN_ON(!clocks->reg))
|
||||
return -ENOMEM;
|
||||
|
||||
r9a06g032_init_h2mode(clocks);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(r9a06g032_clocks); ++i) {
|
||||
const struct r9a06g032_clkdesc *d = &r9a06g032_clocks[i];
|
||||
const char *parent_name = d->source ?
|
||||
|
@ -4150,17 +4150,6 @@ void intel_execlists_show_requests(struct intel_engine_cs *engine,
|
||||
spin_unlock_irqrestore(&sched_engine->lock, flags);
|
||||
}
|
||||
|
||||
static unsigned long list_count(struct list_head *list)
|
||||
{
|
||||
struct list_head *pos;
|
||||
unsigned long count = 0;
|
||||
|
||||
list_for_each(pos, list)
|
||||
count++;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void intel_execlists_dump_active_requests(struct intel_engine_cs *engine,
|
||||
struct i915_request *hung_rq,
|
||||
struct drm_printer *m)
|
||||
@ -4171,8 +4160,8 @@ void intel_execlists_dump_active_requests(struct intel_engine_cs *engine,
|
||||
|
||||
intel_engine_dump_active_requests(&engine->sched_engine->requests, hung_rq, m);
|
||||
|
||||
drm_printf(m, "\tOn hold?: %lu\n",
|
||||
list_count(&engine->sched_engine->hold));
|
||||
drm_printf(m, "\tOn hold?: %zu\n",
|
||||
list_count_nodes(&engine->sched_engine->hold));
|
||||
|
||||
spin_unlock_irqrestore(&engine->sched_engine->lock, flags);
|
||||
}
|
||||
|
@ -259,8 +259,8 @@ static const struct pci_device_id gpu_i2c_ids[] = {
|
||||
MODULE_DEVICE_TABLE(pci, gpu_i2c_ids);
|
||||
|
||||
static const struct property_entry ccgx_props[] = {
|
||||
/* Use FW built for NVIDIA (nv) only */
|
||||
PROPERTY_ENTRY_U16("ccgx,firmware-build", ('n' << 8) | 'v'),
|
||||
/* Use FW built for NVIDIA GPU only */
|
||||
PROPERTY_ENTRY_STRING("firmware-name", "nvidia,gpu"),
|
||||
{ }
|
||||
};
|
||||
|
||||
|
@ -14,6 +14,9 @@ config TTPCI_EEPROM
|
||||
tristate
|
||||
depends on I2C
|
||||
|
||||
config UVC_COMMON
|
||||
tristate
|
||||
|
||||
config VIDEO_CX2341X
|
||||
tristate
|
||||
|
||||
|
@ -5,5 +5,6 @@ obj-y += b2c2/ siano/ v4l2-tpg/ videobuf2/
|
||||
# (e. g. LC_ALL=C sort Makefile)
|
||||
obj-$(CONFIG_CYPRESS_FIRMWARE) += cypress_firmware.o
|
||||
obj-$(CONFIG_TTPCI_EEPROM) += ttpci-eeprom.o
|
||||
obj-$(CONFIG_UVC_COMMON) += uvc.o
|
||||
obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o
|
||||
obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o
|
||||
|
183
drivers/media/common/uvc.c
Normal file
183
drivers/media/common/uvc.c
Normal file
@ -0,0 +1,183 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/usb/uvc.h>
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
* Video formats
|
||||
*/
|
||||
|
||||
static const struct uvc_format_desc uvc_fmts[] = {
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_YUY2,
|
||||
.fcc = V4L2_PIX_FMT_YUYV,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_YUY2_ISIGHT,
|
||||
.fcc = V4L2_PIX_FMT_YUYV,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_NV12,
|
||||
.fcc = V4L2_PIX_FMT_NV12,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_MJPEG,
|
||||
.fcc = V4L2_PIX_FMT_MJPEG,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_YV12,
|
||||
.fcc = V4L2_PIX_FMT_YVU420,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_I420,
|
||||
.fcc = V4L2_PIX_FMT_YUV420,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_M420,
|
||||
.fcc = V4L2_PIX_FMT_M420,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_UYVY,
|
||||
.fcc = V4L2_PIX_FMT_UYVY,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_Y800,
|
||||
.fcc = V4L2_PIX_FMT_GREY,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_Y8,
|
||||
.fcc = V4L2_PIX_FMT_GREY,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_D3DFMT_L8,
|
||||
.fcc = V4L2_PIX_FMT_GREY,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_KSMEDIA_L8_IR,
|
||||
.fcc = V4L2_PIX_FMT_GREY,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_Y10,
|
||||
.fcc = V4L2_PIX_FMT_Y10,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_Y12,
|
||||
.fcc = V4L2_PIX_FMT_Y12,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_Y16,
|
||||
.fcc = V4L2_PIX_FMT_Y16,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_BY8,
|
||||
.fcc = V4L2_PIX_FMT_SBGGR8,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_BA81,
|
||||
.fcc = V4L2_PIX_FMT_SBGGR8,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_GBRG,
|
||||
.fcc = V4L2_PIX_FMT_SGBRG8,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_GRBG,
|
||||
.fcc = V4L2_PIX_FMT_SGRBG8,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_RGGB,
|
||||
.fcc = V4L2_PIX_FMT_SRGGB8,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_RGBP,
|
||||
.fcc = V4L2_PIX_FMT_RGB565,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_BGR3,
|
||||
.fcc = V4L2_PIX_FMT_BGR24,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_BGR4,
|
||||
.fcc = V4L2_PIX_FMT_XBGR32,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_H264,
|
||||
.fcc = V4L2_PIX_FMT_H264,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_H265,
|
||||
.fcc = V4L2_PIX_FMT_HEVC,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_Y8I,
|
||||
.fcc = V4L2_PIX_FMT_Y8I,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_Y12I,
|
||||
.fcc = V4L2_PIX_FMT_Y12I,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_Z16,
|
||||
.fcc = V4L2_PIX_FMT_Z16,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_RW10,
|
||||
.fcc = V4L2_PIX_FMT_SRGGB10P,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_BG16,
|
||||
.fcc = V4L2_PIX_FMT_SBGGR16,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_GB16,
|
||||
.fcc = V4L2_PIX_FMT_SGBRG16,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_RG16,
|
||||
.fcc = V4L2_PIX_FMT_SRGGB16,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_GR16,
|
||||
.fcc = V4L2_PIX_FMT_SGRBG16,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_INVZ,
|
||||
.fcc = V4L2_PIX_FMT_Z16,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_INVI,
|
||||
.fcc = V4L2_PIX_FMT_Y10,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_INZI,
|
||||
.fcc = V4L2_PIX_FMT_INZI,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_CNF4,
|
||||
.fcc = V4L2_PIX_FMT_CNF4,
|
||||
},
|
||||
{
|
||||
.guid = UVC_GUID_FORMAT_HEVC,
|
||||
.fcc = V4L2_PIX_FMT_HEVC,
|
||||
},
|
||||
};
|
||||
|
||||
const struct uvc_format_desc *uvc_format_by_guid(const u8 guid[16])
|
||||
{
|
||||
unsigned int len = ARRAY_SIZE(uvc_fmts);
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < len; ++i) {
|
||||
if (memcmp(guid, uvc_fmts[i].guid, 16) == 0)
|
||||
return &uvc_fmts[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(uvc_format_by_guid);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
@ -3,6 +3,7 @@ config USB_VIDEO_CLASS
|
||||
tristate "USB Video Class (UVC)"
|
||||
depends on VIDEO_DEV
|
||||
select VIDEOBUF2_VMALLOC
|
||||
select UVC_COMMON
|
||||
help
|
||||
Support for the USB Video Class (UVC). Currently only video
|
||||
input devices, such as webcams, are supported.
|
||||
|
@ -6,19 +6,21 @@
|
||||
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
|
||||
*/
|
||||
|
||||
#include <asm/barrier.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/uvc.h>
|
||||
#include <linux/videodev2.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
#include <media/v4l2-uvc.h>
|
||||
|
||||
#include "uvcvideo.h"
|
||||
|
||||
@ -363,19 +365,45 @@ static const u32 uvc_control_classes[] = {
|
||||
V4L2_CID_USER_CLASS,
|
||||
};
|
||||
|
||||
static const struct uvc_menu_info power_line_frequency_controls[] = {
|
||||
{ 0, "Disabled" },
|
||||
{ 1, "50 Hz" },
|
||||
{ 2, "60 Hz" },
|
||||
{ 3, "Auto" },
|
||||
};
|
||||
static const int exposure_auto_mapping[] = { 2, 1, 4, 8 };
|
||||
|
||||
static const struct uvc_menu_info exposure_auto_controls[] = {
|
||||
{ 2, "Auto Mode" },
|
||||
{ 1, "Manual Mode" },
|
||||
{ 4, "Shutter Priority Mode" },
|
||||
{ 8, "Aperture Priority Mode" },
|
||||
};
|
||||
/*
|
||||
* This function translates the V4L2 menu index @idx, as exposed to userspace as
|
||||
* the V4L2 control value, to the corresponding UVC control value used by the
|
||||
* device. The custom menu_mapping in the control @mapping is used when
|
||||
* available, otherwise the function assumes that the V4L2 and UVC values are
|
||||
* identical.
|
||||
*
|
||||
* For controls of type UVC_CTRL_DATA_TYPE_BITMASK, the UVC control value is
|
||||
* expressed as a bitmask and is thus guaranteed to have a single bit set.
|
||||
*
|
||||
* The function returns -EINVAL if the V4L2 menu index @idx isn't valid for the
|
||||
* control, which includes all controls whose type isn't UVC_CTRL_DATA_TYPE_ENUM
|
||||
* or UVC_CTRL_DATA_TYPE_BITMASK.
|
||||
*/
|
||||
static int uvc_mapping_get_menu_value(const struct uvc_control_mapping *mapping,
|
||||
u32 idx)
|
||||
{
|
||||
if (!test_bit(idx, &mapping->menu_mask))
|
||||
return -EINVAL;
|
||||
|
||||
if (mapping->menu_mapping)
|
||||
return mapping->menu_mapping[idx];
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
static const char *
|
||||
uvc_mapping_get_menu_name(const struct uvc_control_mapping *mapping, u32 idx)
|
||||
{
|
||||
if (!test_bit(idx, &mapping->menu_mask))
|
||||
return NULL;
|
||||
|
||||
if (mapping->menu_names)
|
||||
return mapping->menu_names[idx];
|
||||
|
||||
return v4l2_ctrl_get_menu(mapping->id)[idx];
|
||||
}
|
||||
|
||||
static s32 uvc_ctrl_get_zoom(struct uvc_control_mapping *mapping,
|
||||
u8 query, const u8 *data)
|
||||
@ -524,8 +552,9 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
|
||||
.offset = 0,
|
||||
.v4l2_type = V4L2_CTRL_TYPE_MENU,
|
||||
.data_type = UVC_CTRL_DATA_TYPE_BITMASK,
|
||||
.menu_info = exposure_auto_controls,
|
||||
.menu_count = ARRAY_SIZE(exposure_auto_controls),
|
||||
.menu_mapping = exposure_auto_mapping,
|
||||
.menu_mask = GENMASK(V4L2_EXPOSURE_APERTURE_PRIORITY,
|
||||
V4L2_EXPOSURE_AUTO),
|
||||
.slave_ids = { V4L2_CID_EXPOSURE_ABSOLUTE, },
|
||||
},
|
||||
{
|
||||
@ -721,32 +750,50 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static const struct uvc_control_mapping uvc_ctrl_mappings_uvc11[] = {
|
||||
{
|
||||
.id = V4L2_CID_POWER_LINE_FREQUENCY,
|
||||
.entity = UVC_GUID_UVC_PROCESSING,
|
||||
.selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
|
||||
.size = 2,
|
||||
.offset = 0,
|
||||
.v4l2_type = V4L2_CTRL_TYPE_MENU,
|
||||
.data_type = UVC_CTRL_DATA_TYPE_ENUM,
|
||||
.menu_info = power_line_frequency_controls,
|
||||
.menu_count = ARRAY_SIZE(power_line_frequency_controls) - 1,
|
||||
},
|
||||
const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited = {
|
||||
.id = V4L2_CID_POWER_LINE_FREQUENCY,
|
||||
.entity = UVC_GUID_UVC_PROCESSING,
|
||||
.selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
|
||||
.size = 2,
|
||||
.offset = 0,
|
||||
.v4l2_type = V4L2_CTRL_TYPE_MENU,
|
||||
.data_type = UVC_CTRL_DATA_TYPE_ENUM,
|
||||
.menu_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_60HZ,
|
||||
V4L2_CID_POWER_LINE_FREQUENCY_50HZ),
|
||||
};
|
||||
|
||||
static const struct uvc_control_mapping uvc_ctrl_mappings_uvc15[] = {
|
||||
{
|
||||
.id = V4L2_CID_POWER_LINE_FREQUENCY,
|
||||
.entity = UVC_GUID_UVC_PROCESSING,
|
||||
.selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
|
||||
.size = 2,
|
||||
.offset = 0,
|
||||
.v4l2_type = V4L2_CTRL_TYPE_MENU,
|
||||
.data_type = UVC_CTRL_DATA_TYPE_ENUM,
|
||||
.menu_info = power_line_frequency_controls,
|
||||
.menu_count = ARRAY_SIZE(power_line_frequency_controls),
|
||||
},
|
||||
const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc11 = {
|
||||
.id = V4L2_CID_POWER_LINE_FREQUENCY,
|
||||
.entity = UVC_GUID_UVC_PROCESSING,
|
||||
.selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
|
||||
.size = 2,
|
||||
.offset = 0,
|
||||
.v4l2_type = V4L2_CTRL_TYPE_MENU,
|
||||
.data_type = UVC_CTRL_DATA_TYPE_ENUM,
|
||||
.menu_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_60HZ,
|
||||
V4L2_CID_POWER_LINE_FREQUENCY_DISABLED),
|
||||
};
|
||||
|
||||
static const struct uvc_control_mapping *uvc_ctrl_mappings_uvc11[] = {
|
||||
&uvc_ctrl_power_line_mapping_uvc11,
|
||||
NULL, /* Sentinel */
|
||||
};
|
||||
|
||||
static const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc15 = {
|
||||
.id = V4L2_CID_POWER_LINE_FREQUENCY,
|
||||
.entity = UVC_GUID_UVC_PROCESSING,
|
||||
.selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
|
||||
.size = 2,
|
||||
.offset = 0,
|
||||
.v4l2_type = V4L2_CTRL_TYPE_MENU,
|
||||
.data_type = UVC_CTRL_DATA_TYPE_ENUM,
|
||||
.menu_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_AUTO,
|
||||
V4L2_CID_POWER_LINE_FREQUENCY_DISABLED),
|
||||
};
|
||||
|
||||
static const struct uvc_control_mapping *uvc_ctrl_mappings_uvc15[] = {
|
||||
&uvc_ctrl_power_line_mapping_uvc15,
|
||||
NULL, /* Sentinel */
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
@ -972,11 +1019,17 @@ static s32 __uvc_ctrl_get_value(struct uvc_control_mapping *mapping,
|
||||
s32 value = mapping->get(mapping, UVC_GET_CUR, data);
|
||||
|
||||
if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) {
|
||||
const struct uvc_menu_info *menu = mapping->menu_info;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < mapping->menu_count; ++i, ++menu) {
|
||||
if (menu->value == value) {
|
||||
for (i = 0; BIT(i) <= mapping->menu_mask; ++i) {
|
||||
u32 menu_value;
|
||||
|
||||
if (!test_bit(i, &mapping->menu_mask))
|
||||
continue;
|
||||
|
||||
menu_value = uvc_mapping_get_menu_value(mapping, i);
|
||||
|
||||
if (menu_value == value) {
|
||||
value = i;
|
||||
break;
|
||||
}
|
||||
@ -1085,11 +1138,28 @@ static int uvc_query_v4l2_class(struct uvc_video_chain *chain, u32 req_id,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if control @v4l2_id can be accessed by the given control @ioctl
|
||||
* (VIDIOC_G_EXT_CTRLS, VIDIOC_TRY_EXT_CTRLS or VIDIOC_S_EXT_CTRLS).
|
||||
*
|
||||
* For set operations on slave controls, check if the master's value is set to
|
||||
* manual, either in the others controls set in the same ioctl call, or from
|
||||
* the master's current value. This catches VIDIOC_S_EXT_CTRLS calls that set
|
||||
* both the master and slave control, such as for instance setting
|
||||
* auto_exposure=1, exposure_time_absolute=251.
|
||||
*/
|
||||
int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
|
||||
bool read)
|
||||
const struct v4l2_ext_controls *ctrls,
|
||||
unsigned long ioctl)
|
||||
{
|
||||
struct uvc_control_mapping *master_map = NULL;
|
||||
struct uvc_control *master_ctrl = NULL;
|
||||
struct uvc_control_mapping *mapping;
|
||||
struct uvc_control *ctrl;
|
||||
bool read = ioctl == VIDIOC_G_EXT_CTRLS;
|
||||
s32 val;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
if (__uvc_query_v4l2_class(chain, v4l2_id, 0) >= 0)
|
||||
return -EACCES;
|
||||
@ -1104,6 +1174,29 @@ int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
|
||||
if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR) && !read)
|
||||
return -EACCES;
|
||||
|
||||
if (ioctl != VIDIOC_S_EXT_CTRLS || !mapping->master_id)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Iterate backwards in cases where the master control is accessed
|
||||
* multiple times in the same ioctl. We want the last value.
|
||||
*/
|
||||
for (i = ctrls->count - 1; i >= 0; i--) {
|
||||
if (ctrls->controls[i].id == mapping->master_id)
|
||||
return ctrls->controls[i].value ==
|
||||
mapping->master_manual ? 0 : -EACCES;
|
||||
}
|
||||
|
||||
__uvc_find_control(ctrl->entity, mapping->master_id, &master_map,
|
||||
&master_ctrl, 0);
|
||||
|
||||
if (!master_ctrl || !(master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR))
|
||||
return 0;
|
||||
|
||||
ret = __uvc_ctrl_get(chain, master_ctrl, master_map, &val);
|
||||
if (ret >= 0 && val != mapping->master_manual)
|
||||
return -EACCES;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1121,6 +1214,25 @@ static const char *uvc_map_get_name(const struct uvc_control_mapping *map)
|
||||
return "Unknown Control";
|
||||
}
|
||||
|
||||
static u32 uvc_get_ctrl_bitmap(struct uvc_control *ctrl,
|
||||
struct uvc_control_mapping *mapping)
|
||||
{
|
||||
/*
|
||||
* Some controls, like CT_AE_MODE_CONTROL, use GET_RES to represent
|
||||
* the number of bits supported. Those controls do not list GET_MAX
|
||||
* as supported.
|
||||
*/
|
||||
if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)
|
||||
return mapping->get(mapping, UVC_GET_RES,
|
||||
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
|
||||
|
||||
if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX)
|
||||
return mapping->get(mapping, UVC_GET_MAX,
|
||||
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX));
|
||||
|
||||
return ~0;
|
||||
}
|
||||
|
||||
static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
|
||||
struct uvc_control *ctrl,
|
||||
struct uvc_control_mapping *mapping,
|
||||
@ -1128,7 +1240,6 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
|
||||
{
|
||||
struct uvc_control_mapping *master_map = NULL;
|
||||
struct uvc_control *master_ctrl = NULL;
|
||||
const struct uvc_menu_info *menu;
|
||||
unsigned int i;
|
||||
|
||||
memset(v4l2_ctrl, 0, sizeof(*v4l2_ctrl));
|
||||
@ -1169,13 +1280,19 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
|
||||
|
||||
switch (mapping->v4l2_type) {
|
||||
case V4L2_CTRL_TYPE_MENU:
|
||||
v4l2_ctrl->minimum = 0;
|
||||
v4l2_ctrl->maximum = mapping->menu_count - 1;
|
||||
v4l2_ctrl->minimum = ffs(mapping->menu_mask) - 1;
|
||||
v4l2_ctrl->maximum = fls(mapping->menu_mask) - 1;
|
||||
v4l2_ctrl->step = 1;
|
||||
|
||||
menu = mapping->menu_info;
|
||||
for (i = 0; i < mapping->menu_count; ++i, ++menu) {
|
||||
if (menu->value == v4l2_ctrl->default_value) {
|
||||
for (i = 0; BIT(i) <= mapping->menu_mask; ++i) {
|
||||
u32 menu_value;
|
||||
|
||||
if (!test_bit(i, &mapping->menu_mask))
|
||||
continue;
|
||||
|
||||
menu_value = uvc_mapping_get_menu_value(mapping, i);
|
||||
|
||||
if (menu_value == v4l2_ctrl->default_value) {
|
||||
v4l2_ctrl->default_value = i;
|
||||
break;
|
||||
}
|
||||
@ -1195,6 +1312,12 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
|
||||
v4l2_ctrl->step = 0;
|
||||
return 0;
|
||||
|
||||
case V4L2_CTRL_TYPE_BITMASK:
|
||||
v4l2_ctrl->minimum = 0;
|
||||
v4l2_ctrl->maximum = uvc_get_ctrl_bitmap(ctrl, mapping);
|
||||
v4l2_ctrl->step = 0;
|
||||
return 0;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -1268,11 +1391,11 @@ done:
|
||||
int uvc_query_v4l2_menu(struct uvc_video_chain *chain,
|
||||
struct v4l2_querymenu *query_menu)
|
||||
{
|
||||
const struct uvc_menu_info *menu_info;
|
||||
struct uvc_control_mapping *mapping;
|
||||
struct uvc_control *ctrl;
|
||||
u32 index = query_menu->index;
|
||||
u32 id = query_menu->id;
|
||||
const char *name;
|
||||
int ret;
|
||||
|
||||
memset(query_menu, 0, sizeof(*query_menu));
|
||||
@ -1289,16 +1412,13 @@ int uvc_query_v4l2_menu(struct uvc_video_chain *chain,
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (query_menu->index >= mapping->menu_count) {
|
||||
if (!test_bit(query_menu->index, &mapping->menu_mask)) {
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
menu_info = &mapping->menu_info[query_menu->index];
|
||||
|
||||
if (mapping->data_type == UVC_CTRL_DATA_TYPE_BITMASK &&
|
||||
(ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)) {
|
||||
s32 bitmap;
|
||||
if (mapping->data_type == UVC_CTRL_DATA_TYPE_BITMASK) {
|
||||
int mask;
|
||||
|
||||
if (!ctrl->cached) {
|
||||
ret = uvc_ctrl_populate_cache(chain, ctrl);
|
||||
@ -1306,15 +1426,25 @@ int uvc_query_v4l2_menu(struct uvc_video_chain *chain,
|
||||
goto done;
|
||||
}
|
||||
|
||||
bitmap = mapping->get(mapping, UVC_GET_RES,
|
||||
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
|
||||
if (!(bitmap & menu_info->value)) {
|
||||
mask = uvc_mapping_get_menu_value(mapping, query_menu->index);
|
||||
if (mask < 0) {
|
||||
ret = mask;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!(uvc_get_ctrl_bitmap(ctrl, mapping) & mask)) {
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
strscpy(query_menu->name, menu_info->name, sizeof(query_menu->name));
|
||||
name = uvc_mapping_get_menu_name(mapping, query_menu->index);
|
||||
if (!name) {
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
strscpy(query_menu->name, name, sizeof(query_menu->name));
|
||||
|
||||
done:
|
||||
mutex_unlock(&chain->ctrl_mutex);
|
||||
@ -1442,6 +1572,10 @@ static void uvc_ctrl_status_event_work(struct work_struct *work)
|
||||
|
||||
uvc_ctrl_status_event(w->chain, w->ctrl, w->data);
|
||||
|
||||
/* The barrier is needed to synchronize with uvc_status_stop(). */
|
||||
if (smp_load_acquire(&dev->flush_status))
|
||||
return;
|
||||
|
||||
/* Resubmit the URB. */
|
||||
w->urb->interval = dev->int_ep->desc.bInterval;
|
||||
ret = usb_submit_urb(w->urb, GFP_KERNEL);
|
||||
@ -1791,31 +1925,44 @@ int uvc_ctrl_set(struct uvc_fh *handle,
|
||||
value = xctrl->value;
|
||||
break;
|
||||
|
||||
case V4L2_CTRL_TYPE_BITMASK:
|
||||
if (!ctrl->cached) {
|
||||
ret = uvc_ctrl_populate_cache(chain, ctrl);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
xctrl->value &= uvc_get_ctrl_bitmap(ctrl, mapping);
|
||||
value = xctrl->value;
|
||||
break;
|
||||
|
||||
case V4L2_CTRL_TYPE_BOOLEAN:
|
||||
xctrl->value = clamp(xctrl->value, 0, 1);
|
||||
value = xctrl->value;
|
||||
break;
|
||||
|
||||
case V4L2_CTRL_TYPE_MENU:
|
||||
if (xctrl->value < 0 || xctrl->value >= mapping->menu_count)
|
||||
if (xctrl->value < (ffs(mapping->menu_mask) - 1) ||
|
||||
xctrl->value > (fls(mapping->menu_mask) - 1))
|
||||
return -ERANGE;
|
||||
value = mapping->menu_info[xctrl->value].value;
|
||||
|
||||
if (!test_bit(xctrl->value, &mapping->menu_mask))
|
||||
return -EINVAL;
|
||||
|
||||
value = uvc_mapping_get_menu_value(mapping, xctrl->value);
|
||||
|
||||
/*
|
||||
* Valid menu indices are reported by the GET_RES request for
|
||||
* UVC controls that support it.
|
||||
*/
|
||||
if (mapping->data_type == UVC_CTRL_DATA_TYPE_BITMASK &&
|
||||
(ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)) {
|
||||
if (mapping->data_type == UVC_CTRL_DATA_TYPE_BITMASK) {
|
||||
if (!ctrl->cached) {
|
||||
ret = uvc_ctrl_populate_cache(chain, ctrl);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
step = mapping->get(mapping, UVC_GET_RES,
|
||||
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
|
||||
if (!(step & value))
|
||||
if (!(uvc_get_ctrl_bitmap(ctrl, mapping) & value))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -2218,31 +2365,42 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
|
||||
unsigned int i;
|
||||
|
||||
/*
|
||||
* Most mappings come from static kernel data and need to be duplicated.
|
||||
* Most mappings come from static kernel data, and need to be duplicated.
|
||||
* Mappings that come from userspace will be unnecessarily duplicated,
|
||||
* this could be optimized.
|
||||
*/
|
||||
map = kmemdup(mapping, sizeof(*mapping), GFP_KERNEL);
|
||||
if (map == NULL)
|
||||
if (!map)
|
||||
return -ENOMEM;
|
||||
|
||||
map->name = NULL;
|
||||
map->menu_names = NULL;
|
||||
map->menu_mapping = NULL;
|
||||
|
||||
/* For UVCIOC_CTRL_MAP custom control */
|
||||
if (mapping->name) {
|
||||
map->name = kstrdup(mapping->name, GFP_KERNEL);
|
||||
if (!map->name) {
|
||||
kfree(map);
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (!map->name)
|
||||
goto err_nomem;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&map->ev_subs);
|
||||
|
||||
size = sizeof(*mapping->menu_info) * mapping->menu_count;
|
||||
map->menu_info = kmemdup(mapping->menu_info, size, GFP_KERNEL);
|
||||
if (map->menu_info == NULL) {
|
||||
kfree(map->name);
|
||||
kfree(map);
|
||||
return -ENOMEM;
|
||||
if (mapping->menu_mapping && mapping->menu_mask) {
|
||||
size = sizeof(mapping->menu_mapping[0])
|
||||
* fls(mapping->menu_mask);
|
||||
map->menu_mapping = kmemdup(mapping->menu_mapping, size,
|
||||
GFP_KERNEL);
|
||||
if (!map->menu_mapping)
|
||||
goto err_nomem;
|
||||
}
|
||||
if (mapping->menu_names && mapping->menu_mask) {
|
||||
size = sizeof(mapping->menu_names[0])
|
||||
* fls(mapping->menu_mask);
|
||||
map->menu_names = kmemdup(mapping->menu_names, size,
|
||||
GFP_KERNEL);
|
||||
if (!map->menu_names)
|
||||
goto err_nomem;
|
||||
}
|
||||
|
||||
if (map->get == NULL)
|
||||
@ -2264,6 +2422,13 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
|
||||
ctrl->info.selector);
|
||||
|
||||
return 0;
|
||||
|
||||
err_nomem:
|
||||
kfree(map->menu_names);
|
||||
kfree(map->menu_mapping);
|
||||
kfree(map->name);
|
||||
kfree(map);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
|
||||
@ -2421,8 +2586,7 @@ static void uvc_ctrl_prune_entity(struct uvc_device *dev,
|
||||
static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain,
|
||||
struct uvc_control *ctrl)
|
||||
{
|
||||
const struct uvc_control_mapping *mappings;
|
||||
unsigned int num_mappings;
|
||||
const struct uvc_control_mapping **mappings;
|
||||
unsigned int i;
|
||||
|
||||
/*
|
||||
@ -2489,16 +2653,11 @@ static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain,
|
||||
}
|
||||
|
||||
/* Finally process version-specific mappings. */
|
||||
if (chain->dev->uvc_version < 0x0150) {
|
||||
mappings = uvc_ctrl_mappings_uvc11;
|
||||
num_mappings = ARRAY_SIZE(uvc_ctrl_mappings_uvc11);
|
||||
} else {
|
||||
mappings = uvc_ctrl_mappings_uvc15;
|
||||
num_mappings = ARRAY_SIZE(uvc_ctrl_mappings_uvc15);
|
||||
}
|
||||
mappings = chain->dev->uvc_version < 0x0150
|
||||
? uvc_ctrl_mappings_uvc11 : uvc_ctrl_mappings_uvc15;
|
||||
|
||||
for (i = 0; i < num_mappings; ++i) {
|
||||
const struct uvc_control_mapping *mapping = &mappings[i];
|
||||
for (i = 0; mappings[i]; ++i) {
|
||||
const struct uvc_control_mapping *mapping = mappings[i];
|
||||
|
||||
if (uvc_entity_match_guid(ctrl->entity, mapping->entity) &&
|
||||
ctrl->info.selector == mapping->selector)
|
||||
@ -2591,7 +2750,8 @@ static void uvc_ctrl_cleanup_mappings(struct uvc_device *dev,
|
||||
|
||||
list_for_each_entry_safe(mapping, nm, &ctrl->info.mappings, list) {
|
||||
list_del(&mapping->list);
|
||||
kfree(mapping->menu_info);
|
||||
kfree(mapping->menu_names);
|
||||
kfree(mapping->menu_mapping);
|
||||
kfree(mapping->name);
|
||||
kfree(mapping);
|
||||
}
|
||||
|
@ -7,12 +7,14 @@
|
||||
*/
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/uvc.h>
|
||||
#include <linux/videodev2.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/wait.h>
|
||||
@ -20,7 +22,6 @@
|
||||
|
||||
#include <media/v4l2-common.h>
|
||||
#include <media/v4l2-ioctl.h>
|
||||
#include <media/v4l2-uvc.h>
|
||||
|
||||
#include "uvcvideo.h"
|
||||
|
||||
@ -224,7 +225,7 @@ static int uvc_parse_format(struct uvc_device *dev,
|
||||
{
|
||||
struct usb_interface *intf = streaming->intf;
|
||||
struct usb_host_interface *alts = intf->cur_altsetting;
|
||||
struct uvc_format_desc *fmtdesc;
|
||||
const struct uvc_format_desc *fmtdesc;
|
||||
struct uvc_frame *frame;
|
||||
const unsigned char *start = buffer;
|
||||
unsigned int width_multiplier = 1;
|
||||
@ -251,14 +252,10 @@ static int uvc_parse_format(struct uvc_device *dev,
|
||||
fmtdesc = uvc_format_by_guid(&buffer[5]);
|
||||
|
||||
if (fmtdesc != NULL) {
|
||||
strscpy(format->name, fmtdesc->name,
|
||||
sizeof(format->name));
|
||||
format->fcc = fmtdesc->fcc;
|
||||
} else {
|
||||
dev_info(&streaming->intf->dev,
|
||||
"Unknown video format %pUl\n", &buffer[5]);
|
||||
snprintf(format->name, sizeof(format->name), "%pUl\n",
|
||||
&buffer[5]);
|
||||
format->fcc = 0;
|
||||
}
|
||||
|
||||
@ -270,8 +267,6 @@ static int uvc_parse_format(struct uvc_device *dev,
|
||||
*/
|
||||
if (dev->quirks & UVC_QUIRK_FORCE_Y8) {
|
||||
if (format->fcc == V4L2_PIX_FMT_YUYV) {
|
||||
strscpy(format->name, "Greyscale 8-bit (Y8 )",
|
||||
sizeof(format->name));
|
||||
format->fcc = V4L2_PIX_FMT_GREY;
|
||||
format->bpp = 8;
|
||||
width_multiplier = 2;
|
||||
@ -312,7 +307,6 @@ static int uvc_parse_format(struct uvc_device *dev,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
strscpy(format->name, "MJPEG", sizeof(format->name));
|
||||
format->fcc = V4L2_PIX_FMT_MJPEG;
|
||||
format->flags = UVC_FMT_FLAG_COMPRESSED;
|
||||
format->bpp = 0;
|
||||
@ -328,17 +322,7 @@ static int uvc_parse_format(struct uvc_device *dev,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (buffer[8] & 0x7f) {
|
||||
case 0:
|
||||
strscpy(format->name, "SD-DV", sizeof(format->name));
|
||||
break;
|
||||
case 1:
|
||||
strscpy(format->name, "SDL-DV", sizeof(format->name));
|
||||
break;
|
||||
case 2:
|
||||
strscpy(format->name, "HD-DV", sizeof(format->name));
|
||||
break;
|
||||
default:
|
||||
if ((buffer[8] & 0x7f) > 2) {
|
||||
uvc_dbg(dev, DESCR,
|
||||
"device %d videostreaming interface %d: unknown DV format %u\n",
|
||||
dev->udev->devnum,
|
||||
@ -346,9 +330,6 @@ static int uvc_parse_format(struct uvc_device *dev,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
strlcat(format->name, buffer[8] & (1 << 7) ? " 60Hz" : " 50Hz",
|
||||
sizeof(format->name));
|
||||
|
||||
format->fcc = V4L2_PIX_FMT_DV;
|
||||
format->flags = UVC_FMT_FLAG_COMPRESSED | UVC_FMT_FLAG_STREAM;
|
||||
format->bpp = 0;
|
||||
@ -375,7 +356,7 @@ static int uvc_parse_format(struct uvc_device *dev,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
uvc_dbg(dev, DESCR, "Found format %s\n", format->name);
|
||||
uvc_dbg(dev, DESCR, "Found format %p4cc", &format->fcc);
|
||||
|
||||
buflen -= buffer[0];
|
||||
buffer += buffer[0];
|
||||
@ -732,6 +713,7 @@ static int uvc_parse_streaming(struct uvc_device *dev,
|
||||
/* Parse the alternate settings to find the maximum bandwidth. */
|
||||
for (i = 0; i < intf->num_altsetting; ++i) {
|
||||
struct usb_host_endpoint *ep;
|
||||
|
||||
alts = &intf->altsetting[i];
|
||||
ep = uvc_find_endpoint(alts,
|
||||
streaming->header.bEndpointAddress);
|
||||
@ -813,6 +795,27 @@ static struct uvc_entity *uvc_alloc_entity(u16 type, u16 id,
|
||||
return entity;
|
||||
}
|
||||
|
||||
static void uvc_entity_set_name(struct uvc_device *dev, struct uvc_entity *entity,
|
||||
const char *type_name, u8 string_id)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* First attempt to read the entity name from the device. If the entity
|
||||
* has no associated string, or if reading the string fails (most
|
||||
* likely due to a buggy firmware), fall back to default names based on
|
||||
* the entity type.
|
||||
*/
|
||||
if (string_id) {
|
||||
ret = usb_string(dev->udev, string_id, entity->name,
|
||||
sizeof(entity->name));
|
||||
if (!ret)
|
||||
return;
|
||||
}
|
||||
|
||||
sprintf(entity->name, "%s %u", type_name, entity->id);
|
||||
}
|
||||
|
||||
/* Parse vendor-specific extensions. */
|
||||
static int uvc_parse_vendor_control(struct uvc_device *dev,
|
||||
const unsigned char *buffer, int buflen)
|
||||
@ -879,11 +882,7 @@ static int uvc_parse_vendor_control(struct uvc_device *dev,
|
||||
+ n;
|
||||
memcpy(unit->extension.bmControls, &buffer[23+p], 2*n);
|
||||
|
||||
if (buffer[24+p+2*n] != 0)
|
||||
usb_string(udev, buffer[24+p+2*n], unit->name,
|
||||
sizeof(unit->name));
|
||||
else
|
||||
sprintf(unit->name, "Extension %u", buffer[3]);
|
||||
uvc_entity_set_name(dev, unit, "Extension", buffer[24+p+2*n]);
|
||||
|
||||
list_add_tail(&unit->list, &dev->entities);
|
||||
handled = 1;
|
||||
@ -901,6 +900,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
|
||||
struct usb_interface *intf;
|
||||
struct usb_host_interface *alts = dev->intf->cur_altsetting;
|
||||
unsigned int i, n, p, len;
|
||||
const char *type_name;
|
||||
u16 type;
|
||||
|
||||
switch (buffer[2]) {
|
||||
@ -1006,15 +1006,14 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
|
||||
memcpy(term->media.bmTransportModes, &buffer[10+n], p);
|
||||
}
|
||||
|
||||
if (buffer[7] != 0)
|
||||
usb_string(udev, buffer[7], term->name,
|
||||
sizeof(term->name));
|
||||
else if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA)
|
||||
sprintf(term->name, "Camera %u", buffer[3]);
|
||||
if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA)
|
||||
type_name = "Camera";
|
||||
else if (UVC_ENTITY_TYPE(term) == UVC_ITT_MEDIA_TRANSPORT_INPUT)
|
||||
sprintf(term->name, "Media %u", buffer[3]);
|
||||
type_name = "Media";
|
||||
else
|
||||
sprintf(term->name, "Input %u", buffer[3]);
|
||||
type_name = "Input";
|
||||
|
||||
uvc_entity_set_name(dev, term, type_name, buffer[7]);
|
||||
|
||||
list_add_tail(&term->list, &dev->entities);
|
||||
break;
|
||||
@ -1047,11 +1046,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
|
||||
|
||||
memcpy(term->baSourceID, &buffer[7], 1);
|
||||
|
||||
if (buffer[8] != 0)
|
||||
usb_string(udev, buffer[8], term->name,
|
||||
sizeof(term->name));
|
||||
else
|
||||
sprintf(term->name, "Output %u", buffer[3]);
|
||||
uvc_entity_set_name(dev, term, "Output", buffer[8]);
|
||||
|
||||
list_add_tail(&term->list, &dev->entities);
|
||||
break;
|
||||
@ -1072,11 +1067,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
|
||||
|
||||
memcpy(unit->baSourceID, &buffer[5], p);
|
||||
|
||||
if (buffer[5+p] != 0)
|
||||
usb_string(udev, buffer[5+p], unit->name,
|
||||
sizeof(unit->name));
|
||||
else
|
||||
sprintf(unit->name, "Selector %u", buffer[3]);
|
||||
uvc_entity_set_name(dev, unit, "Selector", buffer[5+p]);
|
||||
|
||||
list_add_tail(&unit->list, &dev->entities);
|
||||
break;
|
||||
@ -1105,11 +1096,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
|
||||
if (dev->uvc_version >= 0x0110)
|
||||
unit->processing.bmVideoStandards = buffer[9+n];
|
||||
|
||||
if (buffer[8+n] != 0)
|
||||
usb_string(udev, buffer[8+n], unit->name,
|
||||
sizeof(unit->name));
|
||||
else
|
||||
sprintf(unit->name, "Processing %u", buffer[3]);
|
||||
uvc_entity_set_name(dev, unit, "Processing", buffer[8+n]);
|
||||
|
||||
list_add_tail(&unit->list, &dev->entities);
|
||||
break;
|
||||
@ -1136,11 +1123,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
|
||||
unit->extension.bmControls = (u8 *)unit + sizeof(*unit);
|
||||
memcpy(unit->extension.bmControls, &buffer[23+p], n);
|
||||
|
||||
if (buffer[23+p+n] != 0)
|
||||
usb_string(udev, buffer[23+p+n], unit->name,
|
||||
sizeof(unit->name));
|
||||
else
|
||||
sprintf(unit->name, "Extension %u", buffer[3]);
|
||||
uvc_entity_set_name(dev, unit, "Extension", buffer[23+p+n]);
|
||||
|
||||
list_add_tail(&unit->list, &dev->entities);
|
||||
break;
|
||||
@ -1173,7 +1156,8 @@ static int uvc_parse_control(struct uvc_device *dev)
|
||||
buffer[1] != USB_DT_CS_INTERFACE)
|
||||
goto next_descriptor;
|
||||
|
||||
if ((ret = uvc_parse_standard_control(dev, buffer, buflen)) < 0)
|
||||
ret = uvc_parse_standard_control(dev, buffer, buflen);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
next_descriptor:
|
||||
@ -1856,12 +1840,14 @@ static void uvc_delete(struct kref *kref)
|
||||
|
||||
list_for_each_safe(p, n, &dev->chains) {
|
||||
struct uvc_video_chain *chain;
|
||||
|
||||
chain = list_entry(p, struct uvc_video_chain, list);
|
||||
kfree(chain);
|
||||
}
|
||||
|
||||
list_for_each_safe(p, n, &dev->entities) {
|
||||
struct uvc_entity *entity;
|
||||
|
||||
entity = list_entry(p, struct uvc_entity, list);
|
||||
#ifdef CONFIG_MEDIA_CONTROLLER
|
||||
uvc_mc_cleanup_entity(entity);
|
||||
@ -1871,6 +1857,7 @@ static void uvc_delete(struct kref *kref)
|
||||
|
||||
list_for_each_safe(p, n, &dev->streams) {
|
||||
struct uvc_streaming *streaming;
|
||||
|
||||
streaming = list_entry(p, struct uvc_streaming, list);
|
||||
usb_driver_release_interface(&uvc_driver.driver,
|
||||
streaming->intf);
|
||||
@ -2206,7 +2193,8 @@ static int uvc_probe(struct usb_interface *intf,
|
||||
usb_set_intfdata(intf, dev);
|
||||
|
||||
/* Initialize the interrupt URB. */
|
||||
if ((ret = uvc_status_init(dev)) < 0) {
|
||||
ret = uvc_status_init(dev);
|
||||
if (ret < 0) {
|
||||
dev_info(&dev->udev->dev,
|
||||
"Unable to initialize the status endpoint (%d), status interrupt will not be supported.\n",
|
||||
ret);
|
||||
@ -2353,40 +2341,23 @@ static int uvc_clock_param_set(const char *val, const struct kernel_param *kp)
|
||||
}
|
||||
|
||||
module_param_call(clock, uvc_clock_param_set, uvc_clock_param_get,
|
||||
&uvc_clock_param, S_IRUGO|S_IWUSR);
|
||||
&uvc_clock_param, 0644);
|
||||
MODULE_PARM_DESC(clock, "Video buffers timestamp clock");
|
||||
module_param_named(hwtimestamps, uvc_hw_timestamps_param, uint, S_IRUGO|S_IWUSR);
|
||||
module_param_named(hwtimestamps, uvc_hw_timestamps_param, uint, 0644);
|
||||
MODULE_PARM_DESC(hwtimestamps, "Use hardware timestamps");
|
||||
module_param_named(nodrop, uvc_no_drop_param, uint, S_IRUGO|S_IWUSR);
|
||||
module_param_named(nodrop, uvc_no_drop_param, uint, 0644);
|
||||
MODULE_PARM_DESC(nodrop, "Don't drop incomplete frames");
|
||||
module_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR);
|
||||
module_param_named(quirks, uvc_quirks_param, uint, 0644);
|
||||
MODULE_PARM_DESC(quirks, "Forced device quirks");
|
||||
module_param_named(trace, uvc_dbg_param, uint, S_IRUGO|S_IWUSR);
|
||||
module_param_named(trace, uvc_dbg_param, uint, 0644);
|
||||
MODULE_PARM_DESC(trace, "Trace level bitmask");
|
||||
module_param_named(timeout, uvc_timeout_param, uint, S_IRUGO|S_IWUSR);
|
||||
module_param_named(timeout, uvc_timeout_param, uint, 0644);
|
||||
MODULE_PARM_DESC(timeout, "Streaming control requests timeout");
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
* Driver initialization and cleanup
|
||||
*/
|
||||
|
||||
static const struct uvc_menu_info power_line_frequency_controls_limited[] = {
|
||||
{ 1, "50 Hz" },
|
||||
{ 2, "60 Hz" },
|
||||
};
|
||||
|
||||
static const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited = {
|
||||
.id = V4L2_CID_POWER_LINE_FREQUENCY,
|
||||
.entity = UVC_GUID_UVC_PROCESSING,
|
||||
.selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
|
||||
.size = 2,
|
||||
.offset = 0,
|
||||
.v4l2_type = V4L2_CTRL_TYPE_MENU,
|
||||
.data_type = UVC_CTRL_DATA_TYPE_ENUM,
|
||||
.menu_info = power_line_frequency_controls_limited,
|
||||
.menu_count = ARRAY_SIZE(power_line_frequency_controls_limited),
|
||||
};
|
||||
|
||||
static const struct uvc_device_info uvc_ctrl_power_line_limited = {
|
||||
.mappings = (const struct uvc_control_mapping *[]) {
|
||||
&uvc_ctrl_power_line_mapping_limited,
|
||||
@ -2394,6 +2365,13 @@ static const struct uvc_device_info uvc_ctrl_power_line_limited = {
|
||||
},
|
||||
};
|
||||
|
||||
static const struct uvc_device_info uvc_ctrl_power_line_uvc11 = {
|
||||
.mappings = (const struct uvc_control_mapping *[]) {
|
||||
&uvc_ctrl_power_line_mapping_uvc11,
|
||||
NULL, /* Sentinel */
|
||||
},
|
||||
};
|
||||
|
||||
static const struct uvc_device_info uvc_quirk_probe_minmax = {
|
||||
.quirks = UVC_QUIRK_PROBE_MINMAX,
|
||||
};
|
||||
@ -2496,6 +2474,24 @@ static const struct usb_device_id uvc_ids[] = {
|
||||
.bInterfaceSubClass = 1,
|
||||
.bInterfaceProtocol = 0,
|
||||
.driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax },
|
||||
/* Logitech, Webcam C910 */
|
||||
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
|
||||
| USB_DEVICE_ID_MATCH_INT_INFO,
|
||||
.idVendor = 0x046d,
|
||||
.idProduct = 0x0821,
|
||||
.bInterfaceClass = USB_CLASS_VIDEO,
|
||||
.bInterfaceSubClass = 1,
|
||||
.bInterfaceProtocol = 0,
|
||||
.driver_info = UVC_INFO_QUIRK(UVC_QUIRK_WAKE_AUTOSUSPEND)},
|
||||
/* Logitech, Webcam B910 */
|
||||
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
|
||||
| USB_DEVICE_ID_MATCH_INT_INFO,
|
||||
.idVendor = 0x046d,
|
||||
.idProduct = 0x0823,
|
||||
.bInterfaceClass = USB_CLASS_VIDEO,
|
||||
.bInterfaceSubClass = 1,
|
||||
.bInterfaceProtocol = 0,
|
||||
.driver_info = UVC_INFO_QUIRK(UVC_QUIRK_WAKE_AUTOSUSPEND)},
|
||||
/* Logitech Quickcam Fusion */
|
||||
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
|
||||
| USB_DEVICE_ID_MATCH_INT_INFO,
|
||||
@ -2973,6 +2969,15 @@ static const struct usb_device_id uvc_ids[] = {
|
||||
.bInterfaceSubClass = 1,
|
||||
.bInterfaceProtocol = 0,
|
||||
.driver_info = UVC_INFO_QUIRK(UVC_QUIRK_FORCE_BPP) },
|
||||
/* Lenovo Integrated Camera */
|
||||
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
|
||||
| USB_DEVICE_ID_MATCH_INT_INFO,
|
||||
.idVendor = 0x30c9,
|
||||
.idProduct = 0x0093,
|
||||
.bInterfaceClass = USB_CLASS_VIDEO,
|
||||
.bInterfaceSubClass = 1,
|
||||
.bInterfaceProtocol = UVC_PC_PROTOCOL_15,
|
||||
.driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_uvc11 },
|
||||
/* Sonix Technology USB 2.0 Camera */
|
||||
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
|
||||
| USB_DEVICE_ID_MATCH_INT_INFO,
|
||||
@ -2991,6 +2996,24 @@ static const struct usb_device_id uvc_ids[] = {
|
||||
.bInterfaceSubClass = 1,
|
||||
.bInterfaceProtocol = 0,
|
||||
.driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited },
|
||||
/* Acer EasyCamera */
|
||||
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
|
||||
| USB_DEVICE_ID_MATCH_INT_INFO,
|
||||
.idVendor = 0x5986,
|
||||
.idProduct = 0x1180,
|
||||
.bInterfaceClass = USB_CLASS_VIDEO,
|
||||
.bInterfaceSubClass = 1,
|
||||
.bInterfaceProtocol = 0,
|
||||
.driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited },
|
||||
/* Acer EasyCamera */
|
||||
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
|
||||
| USB_DEVICE_ID_MATCH_INT_INFO,
|
||||
.idVendor = 0x5986,
|
||||
.idProduct = 0x1180,
|
||||
.bInterfaceClass = USB_CLASS_VIDEO,
|
||||
.bInterfaceSubClass = 1,
|
||||
.bInterfaceProtocol = 0,
|
||||
.driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited },
|
||||
/* Intel RealSense D4M */
|
||||
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
|
||||
| USB_DEVICE_ID_MATCH_INT_INFO,
|
||||
|
@ -37,7 +37,7 @@ static int uvc_mc_create_links(struct uvc_video_chain *chain,
|
||||
continue;
|
||||
|
||||
remote = uvc_entity_by_id(chain->dev, entity->baSourceID[i]);
|
||||
if (remote == NULL)
|
||||
if (remote == NULL || remote->num_pads == 0)
|
||||
return -EINVAL;
|
||||
|
||||
source = (UVC_ENTITY_TYPE(remote) == UVC_TT_STREAMING)
|
||||
|
@ -6,6 +6,7 @@
|
||||
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
|
||||
*/
|
||||
|
||||
#include <asm/barrier.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/slab.h>
|
||||
@ -18,11 +19,34 @@
|
||||
* Input device
|
||||
*/
|
||||
#ifdef CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV
|
||||
|
||||
static bool uvc_input_has_button(struct uvc_device *dev)
|
||||
{
|
||||
struct uvc_streaming *stream;
|
||||
|
||||
/*
|
||||
* The device has button events if both bTriggerSupport and
|
||||
* bTriggerUsage are one. Otherwise the camera button does not
|
||||
* exist or is handled automatically by the camera without host
|
||||
* driver or client application intervention.
|
||||
*/
|
||||
list_for_each_entry(stream, &dev->streams, list) {
|
||||
if (stream->header.bTriggerSupport == 1 &&
|
||||
stream->header.bTriggerUsage == 1)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int uvc_input_init(struct uvc_device *dev)
|
||||
{
|
||||
struct input_dev *input;
|
||||
int ret;
|
||||
|
||||
if (!uvc_input_has_button(dev))
|
||||
return 0;
|
||||
|
||||
input = input_allocate_device();
|
||||
if (input == NULL)
|
||||
return -ENOMEM;
|
||||
@ -73,38 +97,23 @@ static void uvc_input_report_key(struct uvc_device *dev, unsigned int code,
|
||||
/* --------------------------------------------------------------------------
|
||||
* Status interrupt endpoint
|
||||
*/
|
||||
struct uvc_streaming_status {
|
||||
u8 bStatusType;
|
||||
u8 bOriginator;
|
||||
u8 bEvent;
|
||||
u8 bValue[];
|
||||
} __packed;
|
||||
|
||||
struct uvc_control_status {
|
||||
u8 bStatusType;
|
||||
u8 bOriginator;
|
||||
u8 bEvent;
|
||||
u8 bSelector;
|
||||
u8 bAttribute;
|
||||
u8 bValue[];
|
||||
} __packed;
|
||||
|
||||
static void uvc_event_streaming(struct uvc_device *dev,
|
||||
struct uvc_streaming_status *status, int len)
|
||||
struct uvc_status *status, int len)
|
||||
{
|
||||
if (len < 3) {
|
||||
if (len <= offsetof(struct uvc_status, bEvent)) {
|
||||
uvc_dbg(dev, STATUS,
|
||||
"Invalid streaming status event received\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (status->bEvent == 0) {
|
||||
if (len < 4)
|
||||
if (len <= offsetof(struct uvc_status, streaming))
|
||||
return;
|
||||
|
||||
uvc_dbg(dev, STATUS, "Button (intf %u) %s len %d\n",
|
||||
status->bOriginator,
|
||||
status->bValue[0] ? "pressed" : "released", len);
|
||||
uvc_input_report_key(dev, KEY_CAMERA, status->bValue[0]);
|
||||
status->streaming.button ? "pressed" : "released", len);
|
||||
uvc_input_report_key(dev, KEY_CAMERA, status->streaming.button);
|
||||
} else {
|
||||
uvc_dbg(dev, STATUS, "Stream %u error event %02x len %d\n",
|
||||
status->bOriginator, status->bEvent, len);
|
||||
@ -131,7 +140,7 @@ static struct uvc_control *uvc_event_entity_find_ctrl(struct uvc_entity *entity,
|
||||
}
|
||||
|
||||
static struct uvc_control *uvc_event_find_ctrl(struct uvc_device *dev,
|
||||
const struct uvc_control_status *status,
|
||||
const struct uvc_status *status,
|
||||
struct uvc_video_chain **chain)
|
||||
{
|
||||
list_for_each_entry((*chain), &dev->chains, list) {
|
||||
@ -143,7 +152,7 @@ static struct uvc_control *uvc_event_find_ctrl(struct uvc_device *dev,
|
||||
continue;
|
||||
|
||||
ctrl = uvc_event_entity_find_ctrl(entity,
|
||||
status->bSelector);
|
||||
status->control.bSelector);
|
||||
if (ctrl)
|
||||
return ctrl;
|
||||
}
|
||||
@ -153,7 +162,7 @@ static struct uvc_control *uvc_event_find_ctrl(struct uvc_device *dev,
|
||||
}
|
||||
|
||||
static bool uvc_event_control(struct urb *urb,
|
||||
const struct uvc_control_status *status, int len)
|
||||
const struct uvc_status *status, int len)
|
||||
{
|
||||
static const char *attrs[] = { "value", "info", "failure", "min", "max" };
|
||||
struct uvc_device *dev = urb->context;
|
||||
@ -161,24 +170,24 @@ static bool uvc_event_control(struct urb *urb,
|
||||
struct uvc_control *ctrl;
|
||||
|
||||
if (len < 6 || status->bEvent != 0 ||
|
||||
status->bAttribute >= ARRAY_SIZE(attrs)) {
|
||||
status->control.bAttribute >= ARRAY_SIZE(attrs)) {
|
||||
uvc_dbg(dev, STATUS, "Invalid control status event received\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
uvc_dbg(dev, STATUS, "Control %u/%u %s change len %d\n",
|
||||
status->bOriginator, status->bSelector,
|
||||
attrs[status->bAttribute], len);
|
||||
status->bOriginator, status->control.bSelector,
|
||||
attrs[status->control.bAttribute], len);
|
||||
|
||||
/* Find the control. */
|
||||
ctrl = uvc_event_find_ctrl(dev, status, &chain);
|
||||
if (!ctrl)
|
||||
return false;
|
||||
|
||||
switch (status->bAttribute) {
|
||||
switch (status->control.bAttribute) {
|
||||
case UVC_CTRL_VALUE_CHANGE:
|
||||
return uvc_ctrl_status_event_async(urb, chain, ctrl,
|
||||
status->bValue);
|
||||
status->control.bValue);
|
||||
|
||||
case UVC_CTRL_INFO_CHANGE:
|
||||
case UVC_CTRL_FAILURE_CHANGE:
|
||||
@ -214,28 +223,22 @@ static void uvc_status_complete(struct urb *urb)
|
||||
|
||||
len = urb->actual_length;
|
||||
if (len > 0) {
|
||||
switch (dev->status[0] & 0x0f) {
|
||||
switch (dev->status->bStatusType & 0x0f) {
|
||||
case UVC_STATUS_TYPE_CONTROL: {
|
||||
struct uvc_control_status *status =
|
||||
(struct uvc_control_status *)dev->status;
|
||||
|
||||
if (uvc_event_control(urb, status, len))
|
||||
if (uvc_event_control(urb, dev->status, len))
|
||||
/* The URB will be resubmitted in work context. */
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
case UVC_STATUS_TYPE_STREAMING: {
|
||||
struct uvc_streaming_status *status =
|
||||
(struct uvc_streaming_status *)dev->status;
|
||||
|
||||
uvc_event_streaming(dev, status, len);
|
||||
uvc_event_streaming(dev, dev->status, len);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
uvc_dbg(dev, STATUS, "Unknown status event type %u\n",
|
||||
dev->status[0]);
|
||||
dev->status->bStatusType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -259,12 +262,12 @@ int uvc_status_init(struct uvc_device *dev)
|
||||
|
||||
uvc_input_init(dev);
|
||||
|
||||
dev->status = kzalloc(UVC_MAX_STATUS_SIZE, GFP_KERNEL);
|
||||
if (dev->status == NULL)
|
||||
dev->status = kzalloc(sizeof(*dev->status), GFP_KERNEL);
|
||||
if (!dev->status)
|
||||
return -ENOMEM;
|
||||
|
||||
dev->int_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (dev->int_urb == NULL) {
|
||||
if (!dev->int_urb) {
|
||||
kfree(dev->status);
|
||||
return -ENOMEM;
|
||||
}
|
||||
@ -281,7 +284,7 @@ int uvc_status_init(struct uvc_device *dev)
|
||||
interval = fls(interval) - 1;
|
||||
|
||||
usb_fill_int_urb(dev->int_urb, dev->udev, pipe,
|
||||
dev->status, UVC_MAX_STATUS_SIZE, uvc_status_complete,
|
||||
dev->status, sizeof(*dev->status), uvc_status_complete,
|
||||
dev, interval);
|
||||
|
||||
return 0;
|
||||
@ -309,5 +312,41 @@ int uvc_status_start(struct uvc_device *dev, gfp_t flags)
|
||||
|
||||
void uvc_status_stop(struct uvc_device *dev)
|
||||
{
|
||||
struct uvc_ctrl_work *w = &dev->async_ctrl;
|
||||
|
||||
/*
|
||||
* Prevent the asynchronous control handler from requeing the URB. The
|
||||
* barrier is needed so the flush_status change is visible to other
|
||||
* CPUs running the asynchronous handler before usb_kill_urb() is
|
||||
* called below.
|
||||
*/
|
||||
smp_store_release(&dev->flush_status, true);
|
||||
|
||||
/*
|
||||
* Cancel any pending asynchronous work. If any status event was queued,
|
||||
* process it synchronously.
|
||||
*/
|
||||
if (cancel_work_sync(&w->work))
|
||||
uvc_ctrl_status_event(w->chain, w->ctrl, w->data);
|
||||
|
||||
/* Kill the urb. */
|
||||
usb_kill_urb(dev->int_urb);
|
||||
|
||||
/*
|
||||
* The URB completion handler may have queued asynchronous work. This
|
||||
* won't resubmit the URB as flush_status is set, but it needs to be
|
||||
* cancelled before returning or it could then race with a future
|
||||
* uvc_status_start() call.
|
||||
*/
|
||||
if (cancel_work_sync(&w->work))
|
||||
uvc_ctrl_status_event(w->chain, w->ctrl, w->data);
|
||||
|
||||
/*
|
||||
* From this point, there are no events on the queue and the status URB
|
||||
* is dead. No events will be queued until uvc_status_start() is called.
|
||||
* The barrier is needed to make sure that flush_status is visible to
|
||||
* uvc_ctrl_status_event_work() when uvc_status_start() will be called
|
||||
* again.
|
||||
*/
|
||||
smp_store_release(&dev->flush_status, false);
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
|
||||
*/
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
@ -25,14 +26,84 @@
|
||||
|
||||
#include "uvcvideo.h"
|
||||
|
||||
static int uvc_control_add_xu_mapping(struct uvc_video_chain *chain,
|
||||
struct uvc_control_mapping *map,
|
||||
const struct uvc_xu_control_mapping *xmap)
|
||||
{
|
||||
unsigned int i;
|
||||
size_t size;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Prevent excessive memory consumption, as well as integer
|
||||
* overflows.
|
||||
*/
|
||||
if (xmap->menu_count == 0 ||
|
||||
xmap->menu_count > UVC_MAX_CONTROL_MENU_ENTRIES)
|
||||
return -EINVAL;
|
||||
|
||||
map->menu_names = NULL;
|
||||
map->menu_mapping = NULL;
|
||||
|
||||
map->menu_mask = BIT_MASK(xmap->menu_count);
|
||||
|
||||
size = xmap->menu_count * sizeof(*map->menu_mapping);
|
||||
map->menu_mapping = kzalloc(size, GFP_KERNEL);
|
||||
if (!map->menu_mapping) {
|
||||
ret = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
|
||||
for (i = 0; i < xmap->menu_count ; i++) {
|
||||
if (copy_from_user((u32 *)&map->menu_mapping[i],
|
||||
&xmap->menu_info[i].value,
|
||||
sizeof(map->menu_mapping[i]))) {
|
||||
ret = -EACCES;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Always use the standard naming if available, otherwise copy the
|
||||
* names supplied by userspace.
|
||||
*/
|
||||
if (!v4l2_ctrl_get_menu(map->id)) {
|
||||
size = xmap->menu_count * sizeof(map->menu_names[0]);
|
||||
map->menu_names = kzalloc(size, GFP_KERNEL);
|
||||
if (!map->menu_names) {
|
||||
ret = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
|
||||
for (i = 0; i < xmap->menu_count ; i++) {
|
||||
/* sizeof(names[i]) - 1: to take care of \0 */
|
||||
if (copy_from_user((char *)map->menu_names[i],
|
||||
xmap->menu_info[i].name,
|
||||
sizeof(map->menu_names[i]) - 1)) {
|
||||
ret = -EACCES;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = uvc_ctrl_add_mapping(chain, map);
|
||||
|
||||
done:
|
||||
kfree(map->menu_names);
|
||||
map->menu_names = NULL;
|
||||
kfree(map->menu_mapping);
|
||||
map->menu_mapping = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
* UVC ioctls
|
||||
*/
|
||||
static int uvc_ioctl_ctrl_map(struct uvc_video_chain *chain,
|
||||
struct uvc_xu_control_mapping *xmap)
|
||||
static int uvc_ioctl_xu_ctrl_map(struct uvc_video_chain *chain,
|
||||
struct uvc_xu_control_mapping *xmap)
|
||||
{
|
||||
struct uvc_control_mapping *map;
|
||||
unsigned int size;
|
||||
int ret;
|
||||
|
||||
map = kzalloc(sizeof(*map), GFP_KERNEL);
|
||||
@ -60,39 +131,20 @@ static int uvc_ioctl_ctrl_map(struct uvc_video_chain *chain,
|
||||
case V4L2_CTRL_TYPE_INTEGER:
|
||||
case V4L2_CTRL_TYPE_BOOLEAN:
|
||||
case V4L2_CTRL_TYPE_BUTTON:
|
||||
ret = uvc_ctrl_add_mapping(chain, map);
|
||||
break;
|
||||
|
||||
case V4L2_CTRL_TYPE_MENU:
|
||||
/*
|
||||
* Prevent excessive memory consumption, as well as integer
|
||||
* overflows.
|
||||
*/
|
||||
if (xmap->menu_count == 0 ||
|
||||
xmap->menu_count > UVC_MAX_CONTROL_MENU_ENTRIES) {
|
||||
ret = -EINVAL;
|
||||
goto free_map;
|
||||
}
|
||||
|
||||
size = xmap->menu_count * sizeof(*map->menu_info);
|
||||
map->menu_info = memdup_user(xmap->menu_info, size);
|
||||
if (IS_ERR(map->menu_info)) {
|
||||
ret = PTR_ERR(map->menu_info);
|
||||
goto free_map;
|
||||
}
|
||||
|
||||
map->menu_count = xmap->menu_count;
|
||||
ret = uvc_control_add_xu_mapping(chain, map, xmap);
|
||||
break;
|
||||
|
||||
default:
|
||||
uvc_dbg(chain->dev, CONTROL,
|
||||
"Unsupported V4L2 control type %u\n", xmap->v4l2_type);
|
||||
ret = -ENOTTY;
|
||||
goto free_map;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = uvc_ctrl_add_mapping(chain, map);
|
||||
|
||||
kfree(map->menu_info);
|
||||
free_map:
|
||||
kfree(map);
|
||||
|
||||
@ -660,8 +712,6 @@ static int uvc_ioctl_enum_fmt(struct uvc_streaming *stream,
|
||||
fmt->flags = 0;
|
||||
if (format->flags & UVC_FMT_FLAG_COMPRESSED)
|
||||
fmt->flags |= V4L2_FMT_FLAG_COMPRESSED;
|
||||
strscpy(fmt->description, format->name, sizeof(fmt->description));
|
||||
fmt->description[sizeof(fmt->description) - 1] = 0;
|
||||
fmt->pixelformat = format->fcc;
|
||||
return 0;
|
||||
}
|
||||
@ -1020,8 +1070,7 @@ static int uvc_ctrl_check_access(struct uvc_video_chain *chain,
|
||||
int ret = 0;
|
||||
|
||||
for (i = 0; i < ctrls->count; ++ctrl, ++i) {
|
||||
ret = uvc_ctrl_is_accessible(chain, ctrl->id,
|
||||
ioctl == VIDIOC_G_EXT_CTRLS);
|
||||
ret = uvc_ctrl_is_accessible(chain, ctrl->id, ctrls, ioctl);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
@ -1316,7 +1365,7 @@ static long uvc_ioctl_default(struct file *file, void *fh, bool valid_prio,
|
||||
switch (cmd) {
|
||||
/* Dynamic controls. */
|
||||
case UVCIOC_CTRL_MAP:
|
||||
return uvc_ioctl_ctrl_map(chain, arg);
|
||||
return uvc_ioctl_xu_ctrl_map(chain, arg);
|
||||
|
||||
case UVCIOC_CTRL_QUERY:
|
||||
return uvc_xu_ctrl_query(chain, arg);
|
||||
@ -1429,7 +1478,7 @@ static long uvc_v4l2_compat_ioctl32(struct file *file,
|
||||
ret = uvc_v4l2_get_xu_mapping(&karg.xmap, up);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = uvc_ioctl_ctrl_map(handle->chain, &karg.xmap);
|
||||
ret = uvc_ioctl_xu_ctrl_map(handle->chain, &karg.xmap);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = uvc_v4l2_put_xu_mapping(&karg.xmap, up);
|
||||
|
@ -79,13 +79,14 @@ int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit,
|
||||
if (likely(ret == size))
|
||||
return 0;
|
||||
|
||||
dev_err(&dev->udev->dev,
|
||||
"Failed to query (%s) UVC control %u on unit %u: %d (exp. %u).\n",
|
||||
uvc_query_name(query), cs, unit, ret, size);
|
||||
|
||||
if (ret != -EPIPE)
|
||||
return ret;
|
||||
if (ret != -EPIPE) {
|
||||
dev_err(&dev->udev->dev,
|
||||
"Failed to query (%s) UVC control %u on unit %u: %d (exp. %u).\n",
|
||||
uvc_query_name(query), cs, unit, ret, size);
|
||||
return ret < 0 ? ret : -EPIPE;
|
||||
}
|
||||
|
||||
/* Reuse data[0] to request the error code. */
|
||||
tmp = *(u8 *)data;
|
||||
|
||||
ret = __uvc_query_ctrl(dev, UVC_GET_CUR, 0, intfnum,
|
||||
@ -107,7 +108,7 @@ int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit,
|
||||
case 1: /* Not ready */
|
||||
return -EBUSY;
|
||||
case 2: /* Wrong state */
|
||||
return -EILSEQ;
|
||||
return -EACCES;
|
||||
case 3: /* Power */
|
||||
return -EREMOTE;
|
||||
case 4: /* Out of range */
|
||||
@ -129,12 +130,13 @@ int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit,
|
||||
return -EPIPE;
|
||||
}
|
||||
|
||||
static const struct usb_device_id elgato_cam_link_4k = {
|
||||
USB_DEVICE(0x0fd9, 0x0066)
|
||||
};
|
||||
|
||||
static void uvc_fixup_video_ctrl(struct uvc_streaming *stream,
|
||||
struct uvc_streaming_control *ctrl)
|
||||
{
|
||||
static const struct usb_device_id elgato_cam_link_4k = {
|
||||
USB_DEVICE(0x0fd9, 0x0066)
|
||||
};
|
||||
struct uvc_format *format = NULL;
|
||||
struct uvc_frame *frame = NULL;
|
||||
unsigned int i;
|
||||
@ -297,7 +299,7 @@ static int uvc_get_video_ctrl(struct uvc_streaming *stream,
|
||||
dev_err(&stream->intf->dev,
|
||||
"Failed to query (%u) UVC %s control : %d (exp. %u).\n",
|
||||
query, probe ? "probe" : "commit", ret, size);
|
||||
ret = -EIO;
|
||||
ret = (ret == -EPROTO) ? -EPROTO : -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -516,7 +518,9 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf,
|
||||
|
||||
/*
|
||||
* To limit the amount of data, drop SCRs with an SOF identical to the
|
||||
* previous one.
|
||||
* previous one. This filtering is also needed to support UVC 1.5, where
|
||||
* all the data packets of the same frame contains the same SOF. In that
|
||||
* case only the first one will match the host_sof.
|
||||
*/
|
||||
dev_sof = get_unaligned_le16(&data[header_size - 2]);
|
||||
if (dev_sof == stream->clock.last_sof)
|
||||
@ -1352,7 +1356,9 @@ static void uvc_video_decode_meta(struct uvc_streaming *stream,
|
||||
if (has_scr)
|
||||
memcpy(stream->clock.last_scr, scr, 6);
|
||||
|
||||
memcpy(&meta->length, mem, length);
|
||||
meta->length = mem[0];
|
||||
meta->flags = mem[1];
|
||||
memcpy(meta->buf, &mem[2], length - 2);
|
||||
meta_buf->bytesused += length + sizeof(meta->ns) + sizeof(meta->sof);
|
||||
|
||||
uvc_dbg(stream->dev, FRAME,
|
||||
@ -1965,6 +1971,17 @@ static int uvc_video_start_transfer(struct uvc_streaming *stream,
|
||||
"Selecting alternate setting %u (%u B/frame bandwidth)\n",
|
||||
altsetting, best_psize);
|
||||
|
||||
/*
|
||||
* Some devices, namely the Logitech C910 and B910, are unable
|
||||
* to recover from a USB autosuspend, unless the alternate
|
||||
* setting of the streaming interface is toggled.
|
||||
*/
|
||||
if (stream->dev->quirks & UVC_QUIRK_WAKE_AUTOSUSPEND) {
|
||||
usb_set_interface(stream->dev->udev, intfnum,
|
||||
altsetting);
|
||||
usb_set_interface(stream->dev->udev, intfnum, 0);
|
||||
}
|
||||
|
||||
ret = usb_set_interface(stream->dev->udev, intfnum, altsetting);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@ -2121,6 +2138,21 @@ int uvc_video_init(struct uvc_streaming *stream)
|
||||
* request on the probe control, as required by the UVC specification.
|
||||
*/
|
||||
ret = uvc_get_video_ctrl(stream, probe, 1, UVC_GET_CUR);
|
||||
|
||||
/*
|
||||
* Elgato Cam Link 4k can be in a stalled state if the resolution of
|
||||
* the external source has changed while the firmware initializes.
|
||||
* Once in this state, the device is useless until it receives a
|
||||
* USB reset. It has even been observed that the stalled state will
|
||||
* continue even after unplugging the device.
|
||||
*/
|
||||
if (ret == -EPROTO &&
|
||||
usb_match_one_id(stream->dev->intf, &elgato_cam_link_4k)) {
|
||||
dev_err(&stream->intf->dev, "Elgato Cam Link 4K firmware crash detected\n");
|
||||
dev_err(&stream->intf->dev, "Resetting the device, unplug and replug to recover\n");
|
||||
usb_reset_device(stream->dev->udev);
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -51,8 +51,6 @@
|
||||
#define UVC_URBS 5
|
||||
/* Maximum number of packets per URB. */
|
||||
#define UVC_MAX_PACKETS 32
|
||||
/* Maximum status buffer size in bytes of interrupt URB. */
|
||||
#define UVC_MAX_STATUS_SIZE 16
|
||||
|
||||
#define UVC_CTRL_CONTROL_TIMEOUT 5000
|
||||
#define UVC_CTRL_STREAMING_TIMEOUT 5000
|
||||
@ -74,6 +72,7 @@
|
||||
#define UVC_QUIRK_RESTORE_CTRLS_ON_INIT 0x00000400
|
||||
#define UVC_QUIRK_FORCE_Y8 0x00000800
|
||||
#define UVC_QUIRK_FORCE_BPP 0x00001000
|
||||
#define UVC_QUIRK_WAKE_AUTOSUSPEND 0x00002000
|
||||
|
||||
/* Format flags */
|
||||
#define UVC_FMT_FLAG_COMPRESSED 0x00000001
|
||||
@ -116,8 +115,9 @@ struct uvc_control_mapping {
|
||||
enum v4l2_ctrl_type v4l2_type;
|
||||
u32 data_type;
|
||||
|
||||
const struct uvc_menu_info *menu_info;
|
||||
u32 menu_count;
|
||||
const u32 *menu_mapping;
|
||||
const char (*menu_names)[UVC_MENU_NAME_LEN];
|
||||
unsigned long menu_mask;
|
||||
|
||||
u32 master_id;
|
||||
s32 master_manual;
|
||||
@ -264,8 +264,6 @@ struct uvc_format {
|
||||
u32 fcc;
|
||||
u32 flags;
|
||||
|
||||
char name[32];
|
||||
|
||||
unsigned int nframes;
|
||||
struct uvc_frame *frame;
|
||||
};
|
||||
@ -527,6 +525,26 @@ struct uvc_device_info {
|
||||
const struct uvc_control_mapping **mappings;
|
||||
};
|
||||
|
||||
struct uvc_status_streaming {
|
||||
u8 button;
|
||||
} __packed;
|
||||
|
||||
struct uvc_status_control {
|
||||
u8 bSelector;
|
||||
u8 bAttribute;
|
||||
u8 bValue[11];
|
||||
} __packed;
|
||||
|
||||
struct uvc_status {
|
||||
u8 bStatusType;
|
||||
u8 bOriginator;
|
||||
u8 bEvent;
|
||||
union {
|
||||
struct uvc_status_control control;
|
||||
struct uvc_status_streaming streaming;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
struct uvc_device {
|
||||
struct usb_device *udev;
|
||||
struct usb_interface *intf;
|
||||
@ -559,7 +577,9 @@ struct uvc_device {
|
||||
/* Status Interrupt Endpoint */
|
||||
struct usb_host_endpoint *int_ep;
|
||||
struct urb *int_urb;
|
||||
u8 *status;
|
||||
struct uvc_status *status;
|
||||
bool flush_status;
|
||||
|
||||
struct input_dev *input;
|
||||
char input_phys[64];
|
||||
|
||||
@ -728,6 +748,8 @@ int uvc_status_start(struct uvc_device *dev, gfp_t flags);
|
||||
void uvc_status_stop(struct uvc_device *dev);
|
||||
|
||||
/* Controls */
|
||||
extern const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited;
|
||||
extern const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc11;
|
||||
extern const struct v4l2_subscribed_event_ops uvc_ctrl_sub_ev_ops;
|
||||
|
||||
int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
|
||||
@ -761,7 +783,8 @@ static inline int uvc_ctrl_rollback(struct uvc_fh *handle)
|
||||
int uvc_ctrl_get(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl);
|
||||
int uvc_ctrl_set(struct uvc_fh *handle, struct v4l2_ext_control *xctrl);
|
||||
int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
|
||||
bool read);
|
||||
const struct v4l2_ext_controls *ctrls,
|
||||
unsigned long ioctl);
|
||||
|
||||
int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
|
||||
struct uvc_xu_control_query *xqry);
|
||||
|
@ -256,7 +256,7 @@ static ssize_t of_device_get_modalias(struct device *dev, char *str, ssize_t len
|
||||
ssize_t csize;
|
||||
ssize_t tsize;
|
||||
|
||||
if ((!dev) || (!dev->of_node))
|
||||
if ((!dev) || (!dev->of_node) || dev->of_node_reused)
|
||||
return -ENODEV;
|
||||
|
||||
/* Name & Type */
|
||||
@ -376,7 +376,7 @@ int of_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env)
|
||||
{
|
||||
int sl;
|
||||
|
||||
if ((!dev) || (!dev->of_node))
|
||||
if ((!dev) || (!dev->of_node) || dev->of_node_reused)
|
||||
return -ENODEV;
|
||||
|
||||
/* Devicetree modalias is tricky, we add it in 2 steps */
|
||||
@ -385,6 +385,8 @@ int of_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env)
|
||||
|
||||
sl = of_device_get_modalias(dev, &env->buf[env->buflen-1],
|
||||
sizeof(env->buf) - env->buflen);
|
||||
if (sl < 0)
|
||||
return sl;
|
||||
if (sl >= (sizeof(env->buf) - env->buflen))
|
||||
return -ENOMEM;
|
||||
env->buflen += sl;
|
||||
|
@ -7,4 +7,5 @@ phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_132_SOC) += xusb-tegra124.o
|
||||
phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_210_SOC) += xusb-tegra210.o
|
||||
phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_186_SOC) += xusb-tegra186.o
|
||||
phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_194_SOC) += xusb-tegra186.o
|
||||
phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_234_SOC) += xusb-tegra186.o
|
||||
obj-$(CONFIG_PHY_TEGRA194_P2U) += phy-tegra194-p2u.o
|
||||
|
@ -89,6 +89,11 @@
|
||||
#define USB2_TRK_START_TIMER(x) (((x) & 0x7f) << 12)
|
||||
#define USB2_TRK_DONE_RESET_TIMER(x) (((x) & 0x7f) << 19)
|
||||
#define USB2_PD_TRK BIT(26)
|
||||
#define USB2_TRK_COMPLETED BIT(31)
|
||||
|
||||
#define XUSB_PADCTL_USB2_BIAS_PAD_CTL2 0x28c
|
||||
#define USB2_TRK_HW_MODE BIT(0)
|
||||
#define CYA_TRK_CODE_UPDATE_ON_IDLE BIT(31)
|
||||
|
||||
#define XUSB_PADCTL_HSIC_PADX_CTL0(x) (0x300 + (x) * 0x20)
|
||||
#define HSIC_PD_TX_DATA0 BIT(1)
|
||||
@ -609,6 +614,32 @@ static void tegra186_utmi_bias_pad_power_on(struct tegra_xusb_padctl *padctl)
|
||||
value &= ~USB2_PD_TRK;
|
||||
padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
|
||||
|
||||
if (padctl->soc->poll_trk_completed) {
|
||||
err = padctl_readl_poll(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1,
|
||||
USB2_TRK_COMPLETED, USB2_TRK_COMPLETED, 100);
|
||||
if (err) {
|
||||
/* The failure with polling on trk complete will not
|
||||
* cause the failure of powering on the bias pad.
|
||||
*/
|
||||
dev_warn(dev, "failed to poll USB2 trk completed: %d\n", err);
|
||||
}
|
||||
|
||||
value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
|
||||
value |= USB2_TRK_COMPLETED;
|
||||
padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
|
||||
} else {
|
||||
udelay(100);
|
||||
}
|
||||
|
||||
if (padctl->soc->trk_hw_mode) {
|
||||
value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL2);
|
||||
value |= USB2_TRK_HW_MODE;
|
||||
value &= ~CYA_TRK_CODE_UPDATE_ON_IDLE;
|
||||
padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL2);
|
||||
} else {
|
||||
clk_disable_unprepare(priv->usb2_trk_clk);
|
||||
}
|
||||
|
||||
mutex_unlock(&padctl->lock);
|
||||
}
|
||||
|
||||
@ -633,7 +664,12 @@ static void tegra186_utmi_bias_pad_power_off(struct tegra_xusb_padctl *padctl)
|
||||
value |= USB2_PD_TRK;
|
||||
padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
|
||||
|
||||
clk_disable_unprepare(priv->usb2_trk_clk);
|
||||
if (padctl->soc->trk_hw_mode) {
|
||||
value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL2);
|
||||
value &= ~USB2_TRK_HW_MODE;
|
||||
padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL2);
|
||||
clk_disable_unprepare(priv->usb2_trk_clk);
|
||||
}
|
||||
|
||||
mutex_unlock(&padctl->lock);
|
||||
}
|
||||
@ -1557,7 +1593,8 @@ const struct tegra_xusb_padctl_soc tegra186_xusb_padctl_soc = {
|
||||
EXPORT_SYMBOL_GPL(tegra186_xusb_padctl_soc);
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC)
|
||||
#if IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) || \
|
||||
IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC)
|
||||
static const char * const tegra194_xusb_padctl_supply_names[] = {
|
||||
"avdd-usb",
|
||||
"vclamp-usb",
|
||||
@ -1613,8 +1650,31 @@ const struct tegra_xusb_padctl_soc tegra194_xusb_padctl_soc = {
|
||||
.supply_names = tegra194_xusb_padctl_supply_names,
|
||||
.num_supplies = ARRAY_SIZE(tegra194_xusb_padctl_supply_names),
|
||||
.supports_gen2 = true,
|
||||
.poll_trk_completed = true,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(tegra194_xusb_padctl_soc);
|
||||
|
||||
const struct tegra_xusb_padctl_soc tegra234_xusb_padctl_soc = {
|
||||
.num_pads = ARRAY_SIZE(tegra194_pads),
|
||||
.pads = tegra194_pads,
|
||||
.ports = {
|
||||
.usb2 = {
|
||||
.ops = &tegra186_usb2_port_ops,
|
||||
.count = 4,
|
||||
},
|
||||
.usb3 = {
|
||||
.ops = &tegra186_usb3_port_ops,
|
||||
.count = 4,
|
||||
},
|
||||
},
|
||||
.ops = &tegra186_xusb_padctl_ops,
|
||||
.supply_names = tegra194_xusb_padctl_supply_names,
|
||||
.num_supplies = ARRAY_SIZE(tegra194_xusb_padctl_supply_names),
|
||||
.supports_gen2 = true,
|
||||
.poll_trk_completed = true,
|
||||
.trk_hw_mode = true,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(tegra234_xusb_padctl_soc);
|
||||
#endif
|
||||
|
||||
MODULE_AUTHOR("JC Kuo <jckuo@nvidia.com>");
|
||||
|
@ -71,6 +71,12 @@ static const struct of_device_id tegra_xusb_padctl_of_match[] = {
|
||||
.compatible = "nvidia,tegra194-xusb-padctl",
|
||||
.data = &tegra194_xusb_padctl_soc,
|
||||
},
|
||||
#endif
|
||||
#if defined(CONFIG_ARCH_TEGRA_234_SOC)
|
||||
{
|
||||
.compatible = "nvidia,tegra234-xusb-padctl",
|
||||
.data = &tegra234_xusb_padctl_soc,
|
||||
},
|
||||
#endif
|
||||
{ }
|
||||
};
|
||||
|
@ -8,6 +8,7 @@
|
||||
#define __PHY_TEGRA_XUSB_H
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
@ -431,6 +432,8 @@ struct tegra_xusb_padctl_soc {
|
||||
unsigned int num_supplies;
|
||||
bool supports_gen2;
|
||||
bool need_fake_usb3_port;
|
||||
bool poll_trk_completed;
|
||||
bool trk_hw_mode;
|
||||
};
|
||||
|
||||
struct tegra_xusb_padctl {
|
||||
@ -473,6 +476,23 @@ static inline u32 padctl_readl(struct tegra_xusb_padctl *padctl,
|
||||
return value;
|
||||
}
|
||||
|
||||
static inline u32 padctl_readl_poll(struct tegra_xusb_padctl *padctl,
|
||||
unsigned long offset, u32 val, u32 mask,
|
||||
int us)
|
||||
{
|
||||
u32 regval;
|
||||
int err;
|
||||
|
||||
err = readl_poll_timeout(padctl->regs + offset, regval,
|
||||
(regval & mask) == val, 1, us);
|
||||
if (err) {
|
||||
dev_err(padctl->dev, "%08lx poll timeout > %08x\n", offset,
|
||||
regval);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
struct tegra_xusb_lane *tegra_xusb_find_lane(struct tegra_xusb_padctl *padctl,
|
||||
const char *name,
|
||||
unsigned int index);
|
||||
@ -489,5 +509,8 @@ extern const struct tegra_xusb_padctl_soc tegra186_xusb_padctl_soc;
|
||||
#if defined(CONFIG_ARCH_TEGRA_194_SOC)
|
||||
extern const struct tegra_xusb_padctl_soc tegra194_xusb_padctl_soc;
|
||||
#endif
|
||||
#if defined(CONFIG_ARCH_TEGRA_234_SOC)
|
||||
extern const struct tegra_xusb_padctl_soc tegra234_xusb_padctl_soc;
|
||||
#endif
|
||||
|
||||
#endif /* __PHY_TEGRA_XUSB_H */
|
||||
|
@ -36,16 +36,13 @@ static acpi_status tb_acpi_add_link(acpi_handle handle, u32 level, void *data,
|
||||
* We need to do this because the xHCI driver might not yet be
|
||||
* bound so the USB3 SuperSpeed ports are not yet created.
|
||||
*/
|
||||
dev = acpi_get_first_physical_node(adev);
|
||||
while (!dev) {
|
||||
adev = acpi_dev_parent(adev);
|
||||
if (!adev)
|
||||
break;
|
||||
do {
|
||||
dev = acpi_get_first_physical_node(adev);
|
||||
}
|
||||
if (dev)
|
||||
break;
|
||||
|
||||
if (!dev)
|
||||
goto out_put;
|
||||
adev = acpi_dev_parent(adev);
|
||||
} while (adev);
|
||||
|
||||
/*
|
||||
* Check that the device is PCIe. This is because USB3
|
||||
|
@ -230,7 +230,6 @@ static int check_config_address(struct tb_cfg_address addr,
|
||||
static struct tb_cfg_result decode_error(const struct ctl_pkg *response)
|
||||
{
|
||||
struct cfg_error_pkg *pkg = response->buffer;
|
||||
struct tb_ctl *ctl = response->ctl;
|
||||
struct tb_cfg_result res = { 0 };
|
||||
res.response_route = tb_cfg_get_route(&pkg->header);
|
||||
res.response_port = 0;
|
||||
@ -239,13 +238,6 @@ static struct tb_cfg_result decode_error(const struct ctl_pkg *response)
|
||||
if (res.err)
|
||||
return res;
|
||||
|
||||
if (pkg->zero1)
|
||||
tb_ctl_warn(ctl, "pkg->zero1 is %#x\n", pkg->zero1);
|
||||
if (pkg->zero2)
|
||||
tb_ctl_warn(ctl, "pkg->zero2 is %#x\n", pkg->zero2);
|
||||
if (pkg->zero3)
|
||||
tb_ctl_warn(ctl, "pkg->zero3 is %#x\n", pkg->zero3);
|
||||
|
||||
res.err = 1;
|
||||
res.tb_error = pkg->error;
|
||||
res.response_port = pkg->port;
|
||||
@ -416,6 +408,7 @@ static int tb_async_error(const struct ctl_pkg *pkg)
|
||||
case TB_CFG_ERROR_LINK_ERROR:
|
||||
case TB_CFG_ERROR_HEC_ERROR_DETECTED:
|
||||
case TB_CFG_ERROR_FLOW_CONTROL_ERROR:
|
||||
case TB_CFG_ERROR_DP_BW:
|
||||
return true;
|
||||
|
||||
default:
|
||||
@ -735,6 +728,47 @@ void tb_ctl_stop(struct tb_ctl *ctl)
|
||||
|
||||
/* public interface, commands */
|
||||
|
||||
/**
|
||||
* tb_cfg_ack_notification() - Ack notification
|
||||
* @ctl: Control channel to use
|
||||
* @route: Router that originated the event
|
||||
* @error: Pointer to the notification package
|
||||
*
|
||||
* Call this as response for non-plug notification to ack it. Returns
|
||||
* %0 on success or an error code on failure.
|
||||
*/
|
||||
int tb_cfg_ack_notification(struct tb_ctl *ctl, u64 route,
|
||||
const struct cfg_error_pkg *error)
|
||||
{
|
||||
struct cfg_ack_pkg pkg = {
|
||||
.header = tb_cfg_make_header(route),
|
||||
};
|
||||
const char *name;
|
||||
|
||||
switch (error->error) {
|
||||
case TB_CFG_ERROR_LINK_ERROR:
|
||||
name = "link error";
|
||||
break;
|
||||
case TB_CFG_ERROR_HEC_ERROR_DETECTED:
|
||||
name = "HEC error";
|
||||
break;
|
||||
case TB_CFG_ERROR_FLOW_CONTROL_ERROR:
|
||||
name = "flow control error";
|
||||
break;
|
||||
case TB_CFG_ERROR_DP_BW:
|
||||
name = "DP_BW";
|
||||
break;
|
||||
default:
|
||||
name = "unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
tb_ctl_dbg(ctl, "acking %s (%#x) notification on %llx\n", name,
|
||||
error->error, route);
|
||||
|
||||
return tb_ctl_tx(ctl, &pkg, sizeof(pkg), TB_CFG_PKG_NOTIFY_ACK);
|
||||
}
|
||||
|
||||
/**
|
||||
* tb_cfg_ack_plug() - Ack hot plug/unplug event
|
||||
* @ctl: Control channel to use
|
||||
@ -754,7 +788,7 @@ int tb_cfg_ack_plug(struct tb_ctl *ctl, u64 route, u32 port, bool unplug)
|
||||
.pg = unplug ? TB_CFG_ERROR_PG_HOT_UNPLUG
|
||||
: TB_CFG_ERROR_PG_HOT_PLUG,
|
||||
};
|
||||
tb_ctl_dbg(ctl, "acking hot %splug event on %llx:%x\n",
|
||||
tb_ctl_dbg(ctl, "acking hot %splug event on %llx:%u\n",
|
||||
unplug ? "un" : "", route, port);
|
||||
return tb_ctl_tx(ctl, &pkg, sizeof(pkg), TB_CFG_PKG_ERROR);
|
||||
}
|
||||
|
@ -122,6 +122,8 @@ static inline struct tb_cfg_header tb_cfg_make_header(u64 route)
|
||||
return header;
|
||||
}
|
||||
|
||||
int tb_cfg_ack_notification(struct tb_ctl *ctl, u64 route,
|
||||
const struct cfg_error_pkg *error);
|
||||
int tb_cfg_ack_plug(struct tb_ctl *ctl, u64 route, u32 port, bool unplug);
|
||||
struct tb_cfg_result tb_cfg_reset(struct tb_ctl *ctl, u64 route);
|
||||
struct tb_cfg_result tb_cfg_read_raw(struct tb_ctl *ctl, void *buffer,
|
||||
|
@ -1159,7 +1159,10 @@ static void port_cap_show(struct tb_port *port, struct seq_file *s,
|
||||
if (tb_port_is_pcie_down(port) || tb_port_is_pcie_up(port)) {
|
||||
length = PORT_CAP_PCIE_LEN;
|
||||
} else if (tb_port_is_dpin(port) || tb_port_is_dpout(port)) {
|
||||
length = PORT_CAP_DP_LEN;
|
||||
if (usb4_dp_port_bw_mode_supported(port))
|
||||
length = PORT_CAP_DP_LEN + 1;
|
||||
else
|
||||
length = PORT_CAP_DP_LEN;
|
||||
} else if (tb_port_is_usb3_down(port) ||
|
||||
tb_port_is_usb3_up(port)) {
|
||||
length = PORT_CAP_USB3_LEN;
|
||||
|
@ -513,36 +513,44 @@ int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged)
|
||||
|
||||
while (retries--) {
|
||||
state = tb_port_state(port);
|
||||
if (state < 0)
|
||||
return state;
|
||||
if (state == TB_PORT_DISABLED) {
|
||||
switch (state) {
|
||||
case TB_PORT_DISABLED:
|
||||
tb_port_dbg(port, "is disabled (state: 0)\n");
|
||||
return 0;
|
||||
}
|
||||
if (state == TB_PORT_UNPLUGGED) {
|
||||
|
||||
case TB_PORT_UNPLUGGED:
|
||||
if (wait_if_unplugged) {
|
||||
/* used during resume */
|
||||
tb_port_dbg(port,
|
||||
"is unplugged (state: 7), retrying...\n");
|
||||
msleep(100);
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
tb_port_dbg(port, "is unplugged (state: 7)\n");
|
||||
return 0;
|
||||
}
|
||||
if (state == TB_PORT_UP) {
|
||||
tb_port_dbg(port, "is connected, link is up (state: 2)\n");
|
||||
|
||||
case TB_PORT_UP:
|
||||
case TB_PORT_TX_CL0S:
|
||||
case TB_PORT_RX_CL0S:
|
||||
case TB_PORT_CL1:
|
||||
case TB_PORT_CL2:
|
||||
tb_port_dbg(port, "is connected, link is up (state: %d)\n", state);
|
||||
return 1;
|
||||
|
||||
default:
|
||||
if (state < 0)
|
||||
return state;
|
||||
|
||||
/*
|
||||
* After plug-in the state is TB_PORT_CONNECTING. Give it some
|
||||
* time.
|
||||
*/
|
||||
tb_port_dbg(port,
|
||||
"is connected, link is not up (state: %d), retrying...\n",
|
||||
state);
|
||||
msleep(100);
|
||||
}
|
||||
|
||||
/*
|
||||
* After plug-in the state is TB_PORT_CONNECTING. Give it some
|
||||
* time.
|
||||
*/
|
||||
tb_port_dbg(port,
|
||||
"is connected, link is not up (state: %d), retrying...\n",
|
||||
state);
|
||||
msleep(100);
|
||||
}
|
||||
tb_port_warn(port,
|
||||
"failed to reach state TB_PORT_UP. Ignoring port...\n");
|
||||
|
@ -16,7 +16,8 @@
|
||||
#include "tb_regs.h"
|
||||
#include "tunnel.h"
|
||||
|
||||
#define TB_TIMEOUT 100 /* ms */
|
||||
#define TB_TIMEOUT 100 /* ms */
|
||||
#define MAX_GROUPS 7 /* max Group_ID is 7 */
|
||||
|
||||
/**
|
||||
* struct tb_cm - Simple Thunderbolt connection manager
|
||||
@ -28,12 +29,14 @@
|
||||
* after cfg has been paused.
|
||||
* @remove_work: Work used to remove any unplugged routers after
|
||||
* runtime resume
|
||||
* @groups: Bandwidth groups used in this domain.
|
||||
*/
|
||||
struct tb_cm {
|
||||
struct list_head tunnel_list;
|
||||
struct list_head dp_resources;
|
||||
bool hotplug_active;
|
||||
struct delayed_work remove_work;
|
||||
struct tb_bandwidth_group groups[MAX_GROUPS];
|
||||
};
|
||||
|
||||
static inline struct tb *tcm_to_tb(struct tb_cm *tcm)
|
||||
@ -49,6 +52,112 @@ struct tb_hotplug_event {
|
||||
bool unplug;
|
||||
};
|
||||
|
||||
static void tb_init_bandwidth_groups(struct tb_cm *tcm)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tcm->groups); i++) {
|
||||
struct tb_bandwidth_group *group = &tcm->groups[i];
|
||||
|
||||
group->tb = tcm_to_tb(tcm);
|
||||
group->index = i + 1;
|
||||
INIT_LIST_HEAD(&group->ports);
|
||||
}
|
||||
}
|
||||
|
||||
static void tb_bandwidth_group_attach_port(struct tb_bandwidth_group *group,
|
||||
struct tb_port *in)
|
||||
{
|
||||
if (!group || WARN_ON(in->group))
|
||||
return;
|
||||
|
||||
in->group = group;
|
||||
list_add_tail(&in->group_list, &group->ports);
|
||||
|
||||
tb_port_dbg(in, "attached to bandwidth group %d\n", group->index);
|
||||
}
|
||||
|
||||
static struct tb_bandwidth_group *tb_find_free_bandwidth_group(struct tb_cm *tcm)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tcm->groups); i++) {
|
||||
struct tb_bandwidth_group *group = &tcm->groups[i];
|
||||
|
||||
if (list_empty(&group->ports))
|
||||
return group;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct tb_bandwidth_group *
|
||||
tb_attach_bandwidth_group(struct tb_cm *tcm, struct tb_port *in,
|
||||
struct tb_port *out)
|
||||
{
|
||||
struct tb_bandwidth_group *group;
|
||||
struct tb_tunnel *tunnel;
|
||||
|
||||
/*
|
||||
* Find all DP tunnels that go through all the same USB4 links
|
||||
* as this one. Because we always setup tunnels the same way we
|
||||
* can just check for the routers at both ends of the tunnels
|
||||
* and if they are the same we have a match.
|
||||
*/
|
||||
list_for_each_entry(tunnel, &tcm->tunnel_list, list) {
|
||||
if (!tb_tunnel_is_dp(tunnel))
|
||||
continue;
|
||||
|
||||
if (tunnel->src_port->sw == in->sw &&
|
||||
tunnel->dst_port->sw == out->sw) {
|
||||
group = tunnel->src_port->group;
|
||||
if (group) {
|
||||
tb_bandwidth_group_attach_port(group, in);
|
||||
return group;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Pick up next available group then */
|
||||
group = tb_find_free_bandwidth_group(tcm);
|
||||
if (group)
|
||||
tb_bandwidth_group_attach_port(group, in);
|
||||
else
|
||||
tb_port_warn(in, "no available bandwidth groups\n");
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
static void tb_discover_bandwidth_group(struct tb_cm *tcm, struct tb_port *in,
|
||||
struct tb_port *out)
|
||||
{
|
||||
if (usb4_dp_port_bw_mode_enabled(in)) {
|
||||
int index, i;
|
||||
|
||||
index = usb4_dp_port_group_id(in);
|
||||
for (i = 0; i < ARRAY_SIZE(tcm->groups); i++) {
|
||||
if (tcm->groups[i].index == index) {
|
||||
tb_bandwidth_group_attach_port(&tcm->groups[i], in);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tb_attach_bandwidth_group(tcm, in, out);
|
||||
}
|
||||
|
||||
static void tb_detach_bandwidth_group(struct tb_port *in)
|
||||
{
|
||||
struct tb_bandwidth_group *group = in->group;
|
||||
|
||||
if (group) {
|
||||
in->group = NULL;
|
||||
list_del_init(&in->group_list);
|
||||
|
||||
tb_port_dbg(in, "detached from bandwidth group %d\n", group->index);
|
||||
}
|
||||
}
|
||||
|
||||
static void tb_handle_hotplug(struct work_struct *work);
|
||||
|
||||
static void tb_queue_hotplug(struct tb *tb, u64 route, u8 port, bool unplug)
|
||||
@ -193,9 +302,14 @@ static void tb_discover_tunnels(struct tb *tb)
|
||||
parent = tb_switch_parent(parent);
|
||||
}
|
||||
} else if (tb_tunnel_is_dp(tunnel)) {
|
||||
struct tb_port *in = tunnel->src_port;
|
||||
struct tb_port *out = tunnel->dst_port;
|
||||
|
||||
/* Keep the domain from powering down */
|
||||
pm_runtime_get_sync(&tunnel->src_port->sw->dev);
|
||||
pm_runtime_get_sync(&tunnel->dst_port->sw->dev);
|
||||
pm_runtime_get_sync(&in->sw->dev);
|
||||
pm_runtime_get_sync(&out->sw->dev);
|
||||
|
||||
tb_discover_bandwidth_group(tcm, in, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -350,10 +464,13 @@ static int tb_available_bandwidth(struct tb *tb, struct tb_port *src_port,
|
||||
struct tb_tunnel *tunnel;
|
||||
struct tb_port *port;
|
||||
|
||||
tb_port_dbg(dst_port, "calculating available bandwidth\n");
|
||||
tb_dbg(tb, "calculating available bandwidth between %llx:%u <-> %llx:%u\n",
|
||||
tb_route(src_port->sw), src_port->port, tb_route(dst_port->sw),
|
||||
dst_port->port);
|
||||
|
||||
tunnel = tb_find_first_usb3_tunnel(tb, src_port, dst_port);
|
||||
if (tunnel) {
|
||||
if (tunnel && tunnel->src_port != src_port &&
|
||||
tunnel->dst_port != dst_port) {
|
||||
ret = tb_tunnel_consumed_bandwidth(tunnel, &usb3_consumed_up,
|
||||
&usb3_consumed_down);
|
||||
if (ret)
|
||||
@ -387,7 +504,8 @@ static int tb_available_bandwidth(struct tb *tb, struct tb_port *src_port,
|
||||
up_bw -= up_bw / 10;
|
||||
down_bw = up_bw;
|
||||
|
||||
tb_port_dbg(port, "link total bandwidth %d Mb/s\n", up_bw);
|
||||
tb_port_dbg(port, "link total bandwidth %d/%d Mb/s\n", up_bw,
|
||||
down_bw);
|
||||
|
||||
/*
|
||||
* Find all DP tunnels that cross the port and reduce
|
||||
@ -396,12 +514,24 @@ static int tb_available_bandwidth(struct tb *tb, struct tb_port *src_port,
|
||||
list_for_each_entry(tunnel, &tcm->tunnel_list, list) {
|
||||
int dp_consumed_up, dp_consumed_down;
|
||||
|
||||
if (tb_tunnel_is_invalid(tunnel))
|
||||
continue;
|
||||
|
||||
if (!tb_tunnel_is_dp(tunnel))
|
||||
continue;
|
||||
|
||||
if (!tb_tunnel_port_on_path(tunnel, port))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Ignore the DP tunnel between src_port and
|
||||
* dst_port because it is the same tunnel and we
|
||||
* may be re-calculating estimated bandwidth.
|
||||
*/
|
||||
if (tunnel->src_port == src_port &&
|
||||
tunnel->dst_port == dst_port)
|
||||
continue;
|
||||
|
||||
ret = tb_tunnel_consumed_bandwidth(tunnel,
|
||||
&dp_consumed_up,
|
||||
&dp_consumed_down);
|
||||
@ -762,6 +892,7 @@ static void tb_deactivate_and_free_tunnel(struct tb_tunnel *tunnel)
|
||||
|
||||
switch (tunnel->type) {
|
||||
case TB_TUNNEL_DP:
|
||||
tb_detach_bandwidth_group(src_port);
|
||||
/*
|
||||
* In case of DP tunnel make sure the DP IN resource is
|
||||
* deallocated properly.
|
||||
@ -879,6 +1010,99 @@ out:
|
||||
return tb_find_unused_port(sw, TB_TYPE_PCIE_DOWN);
|
||||
}
|
||||
|
||||
static void
|
||||
tb_recalc_estimated_bandwidth_for_group(struct tb_bandwidth_group *group)
|
||||
{
|
||||
struct tb_tunnel *first_tunnel;
|
||||
struct tb *tb = group->tb;
|
||||
struct tb_port *in;
|
||||
int ret;
|
||||
|
||||
tb_dbg(tb, "re-calculating bandwidth estimation for group %u\n",
|
||||
group->index);
|
||||
|
||||
first_tunnel = NULL;
|
||||
list_for_each_entry(in, &group->ports, group_list) {
|
||||
int estimated_bw, estimated_up, estimated_down;
|
||||
struct tb_tunnel *tunnel;
|
||||
struct tb_port *out;
|
||||
|
||||
if (!usb4_dp_port_bw_mode_enabled(in))
|
||||
continue;
|
||||
|
||||
tunnel = tb_find_tunnel(tb, TB_TUNNEL_DP, in, NULL);
|
||||
if (WARN_ON(!tunnel))
|
||||
break;
|
||||
|
||||
if (!first_tunnel) {
|
||||
/*
|
||||
* Since USB3 bandwidth is shared by all DP
|
||||
* tunnels under the host router USB4 port, even
|
||||
* if they do not begin from the host router, we
|
||||
* can release USB3 bandwidth just once and not
|
||||
* for each tunnel separately.
|
||||
*/
|
||||
first_tunnel = tunnel;
|
||||
ret = tb_release_unused_usb3_bandwidth(tb,
|
||||
first_tunnel->src_port, first_tunnel->dst_port);
|
||||
if (ret) {
|
||||
tb_port_warn(in,
|
||||
"failed to release unused bandwidth\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
out = tunnel->dst_port;
|
||||
ret = tb_available_bandwidth(tb, in, out, &estimated_up,
|
||||
&estimated_down);
|
||||
if (ret) {
|
||||
tb_port_warn(in,
|
||||
"failed to re-calculate estimated bandwidth\n");
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Estimated bandwidth includes:
|
||||
* - already allocated bandwidth for the DP tunnel
|
||||
* - available bandwidth along the path
|
||||
* - bandwidth allocated for USB 3.x but not used.
|
||||
*/
|
||||
tb_port_dbg(in, "re-calculated estimated bandwidth %u/%u Mb/s\n",
|
||||
estimated_up, estimated_down);
|
||||
|
||||
if (in->sw->config.depth < out->sw->config.depth)
|
||||
estimated_bw = estimated_down;
|
||||
else
|
||||
estimated_bw = estimated_up;
|
||||
|
||||
if (usb4_dp_port_set_estimated_bw(in, estimated_bw))
|
||||
tb_port_warn(in, "failed to update estimated bandwidth\n");
|
||||
}
|
||||
|
||||
if (first_tunnel)
|
||||
tb_reclaim_usb3_bandwidth(tb, first_tunnel->src_port,
|
||||
first_tunnel->dst_port);
|
||||
|
||||
tb_dbg(tb, "bandwidth estimation for group %u done\n", group->index);
|
||||
}
|
||||
|
||||
static void tb_recalc_estimated_bandwidth(struct tb *tb)
|
||||
{
|
||||
struct tb_cm *tcm = tb_priv(tb);
|
||||
int i;
|
||||
|
||||
tb_dbg(tb, "bandwidth consumption changed, re-calculating estimated bandwidth\n");
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tcm->groups); i++) {
|
||||
struct tb_bandwidth_group *group = &tcm->groups[i];
|
||||
|
||||
if (!list_empty(&group->ports))
|
||||
tb_recalc_estimated_bandwidth_for_group(group);
|
||||
}
|
||||
|
||||
tb_dbg(tb, "bandwidth re-calculation done\n");
|
||||
}
|
||||
|
||||
static struct tb_port *tb_find_dp_out(struct tb *tb, struct tb_port *in)
|
||||
{
|
||||
struct tb_port *host_port, *port;
|
||||
@ -892,7 +1116,7 @@ static struct tb_port *tb_find_dp_out(struct tb *tb, struct tb_port *in)
|
||||
continue;
|
||||
|
||||
if (tb_port_is_enabled(port)) {
|
||||
tb_port_dbg(port, "in use\n");
|
||||
tb_port_dbg(port, "DP OUT in use\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -941,7 +1165,7 @@ static void tb_tunnel_dp(struct tb *tb)
|
||||
continue;
|
||||
|
||||
if (tb_port_is_enabled(port)) {
|
||||
tb_port_dbg(port, "in use\n");
|
||||
tb_port_dbg(port, "DP IN in use\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -993,17 +1217,19 @@ static void tb_tunnel_dp(struct tb *tb)
|
||||
goto err_rpm_put;
|
||||
}
|
||||
|
||||
if (!tb_attach_bandwidth_group(tcm, in, out))
|
||||
goto err_dealloc_dp;
|
||||
|
||||
/* Make all unused USB3 bandwidth available for the new DP tunnel */
|
||||
ret = tb_release_unused_usb3_bandwidth(tb, in, out);
|
||||
if (ret) {
|
||||
tb_warn(tb, "failed to release unused bandwidth\n");
|
||||
goto err_dealloc_dp;
|
||||
goto err_detach_group;
|
||||
}
|
||||
|
||||
ret = tb_available_bandwidth(tb, in, out, &available_up,
|
||||
&available_down);
|
||||
ret = tb_available_bandwidth(tb, in, out, &available_up, &available_down);
|
||||
if (ret)
|
||||
goto err_reclaim;
|
||||
goto err_reclaim_usb;
|
||||
|
||||
tb_dbg(tb, "available bandwidth for new DP tunnel %u/%u Mb/s\n",
|
||||
available_up, available_down);
|
||||
@ -1012,7 +1238,7 @@ static void tb_tunnel_dp(struct tb *tb)
|
||||
available_down);
|
||||
if (!tunnel) {
|
||||
tb_port_dbg(out, "could not allocate DP tunnel\n");
|
||||
goto err_reclaim;
|
||||
goto err_reclaim_usb;
|
||||
}
|
||||
|
||||
if (tb_tunnel_activate(tunnel)) {
|
||||
@ -1022,6 +1248,10 @@ static void tb_tunnel_dp(struct tb *tb)
|
||||
|
||||
list_add_tail(&tunnel->list, &tcm->tunnel_list);
|
||||
tb_reclaim_usb3_bandwidth(tb, in, out);
|
||||
|
||||
/* Update the domain with the new bandwidth estimation */
|
||||
tb_recalc_estimated_bandwidth(tb);
|
||||
|
||||
/*
|
||||
* In case of DP tunnel exists, change host router's 1st children
|
||||
* TMU mode to HiFi for CL0s to work.
|
||||
@ -1032,8 +1262,10 @@ static void tb_tunnel_dp(struct tb *tb)
|
||||
|
||||
err_free:
|
||||
tb_tunnel_free(tunnel);
|
||||
err_reclaim:
|
||||
err_reclaim_usb:
|
||||
tb_reclaim_usb3_bandwidth(tb, in, out);
|
||||
err_detach_group:
|
||||
tb_detach_bandwidth_group(in);
|
||||
err_dealloc_dp:
|
||||
tb_switch_dealloc_dp_resource(in->sw, in);
|
||||
err_rpm_put:
|
||||
@ -1066,6 +1298,7 @@ static void tb_dp_resource_unavailable(struct tb *tb, struct tb_port *port)
|
||||
* See if there is another DP OUT port that can be used for
|
||||
* to create another tunnel.
|
||||
*/
|
||||
tb_recalc_estimated_bandwidth(tb);
|
||||
tb_tunnel_dp(tb);
|
||||
}
|
||||
|
||||
@ -1313,6 +1546,7 @@ static void tb_handle_hotplug(struct work_struct *work)
|
||||
if (port->dual_link_port)
|
||||
port->dual_link_port->remote = NULL;
|
||||
/* Maybe we can create another DP tunnel */
|
||||
tb_recalc_estimated_bandwidth(tb);
|
||||
tb_tunnel_dp(tb);
|
||||
} else if (port->xdomain) {
|
||||
struct tb_xdomain *xd = tb_xdomain_get(port->xdomain);
|
||||
@ -1370,6 +1604,239 @@ out:
|
||||
kfree(ev);
|
||||
}
|
||||
|
||||
static int tb_alloc_dp_bandwidth(struct tb_tunnel *tunnel, int *requested_up,
|
||||
int *requested_down)
|
||||
{
|
||||
int allocated_up, allocated_down, available_up, available_down, ret;
|
||||
int requested_up_corrected, requested_down_corrected, granularity;
|
||||
int max_up, max_down, max_up_rounded, max_down_rounded;
|
||||
struct tb *tb = tunnel->tb;
|
||||
struct tb_port *in, *out;
|
||||
|
||||
ret = tb_tunnel_allocated_bandwidth(tunnel, &allocated_up, &allocated_down);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
in = tunnel->src_port;
|
||||
out = tunnel->dst_port;
|
||||
|
||||
tb_port_dbg(in, "bandwidth allocated currently %d/%d Mb/s\n",
|
||||
allocated_up, allocated_down);
|
||||
|
||||
/*
|
||||
* If we get rounded up request from graphics side, say HBR2 x 4
|
||||
* that is 17500 instead of 17280 (this is because of the
|
||||
* granularity), we allow it too. Here the graphics has already
|
||||
* negotiated with the DPRX the maximum possible rates (which is
|
||||
* 17280 in this case).
|
||||
*
|
||||
* Since the link cannot go higher than 17280 we use that in our
|
||||
* calculations but the DP IN adapter Allocated BW write must be
|
||||
* the same value (17500) otherwise the adapter will mark it as
|
||||
* failed for graphics.
|
||||
*/
|
||||
ret = tb_tunnel_maximum_bandwidth(tunnel, &max_up, &max_down);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = usb4_dp_port_granularity(in);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
granularity = ret;
|
||||
|
||||
max_up_rounded = roundup(max_up, granularity);
|
||||
max_down_rounded = roundup(max_down, granularity);
|
||||
|
||||
/*
|
||||
* This will "fix" the request down to the maximum supported
|
||||
* rate * lanes if it is at the maximum rounded up level.
|
||||
*/
|
||||
requested_up_corrected = *requested_up;
|
||||
if (requested_up_corrected == max_up_rounded)
|
||||
requested_up_corrected = max_up;
|
||||
else if (requested_up_corrected < 0)
|
||||
requested_up_corrected = 0;
|
||||
requested_down_corrected = *requested_down;
|
||||
if (requested_down_corrected == max_down_rounded)
|
||||
requested_down_corrected = max_down;
|
||||
else if (requested_down_corrected < 0)
|
||||
requested_down_corrected = 0;
|
||||
|
||||
tb_port_dbg(in, "corrected bandwidth request %d/%d Mb/s\n",
|
||||
requested_up_corrected, requested_down_corrected);
|
||||
|
||||
if ((*requested_up >= 0 && requested_up_corrected > max_up_rounded) ||
|
||||
(*requested_down >= 0 && requested_down_corrected > max_down_rounded)) {
|
||||
tb_port_dbg(in, "bandwidth request too high (%d/%d Mb/s > %d/%d Mb/s)\n",
|
||||
requested_up_corrected, requested_down_corrected,
|
||||
max_up_rounded, max_down_rounded);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
if ((*requested_up >= 0 && requested_up_corrected <= allocated_up) ||
|
||||
(*requested_down >= 0 && requested_down_corrected <= allocated_down)) {
|
||||
/*
|
||||
* If requested bandwidth is less or equal than what is
|
||||
* currently allocated to that tunnel we simply change
|
||||
* the reservation of the tunnel. Since all the tunnels
|
||||
* going out from the same USB4 port are in the same
|
||||
* group the released bandwidth will be taken into
|
||||
* account for the other tunnels automatically below.
|
||||
*/
|
||||
return tb_tunnel_alloc_bandwidth(tunnel, requested_up,
|
||||
requested_down);
|
||||
}
|
||||
|
||||
/*
|
||||
* More bandwidth is requested. Release all the potential
|
||||
* bandwidth from USB3 first.
|
||||
*/
|
||||
ret = tb_release_unused_usb3_bandwidth(tb, in, out);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Then go over all tunnels that cross the same USB4 ports (they
|
||||
* are also in the same group but we use the same function here
|
||||
* that we use with the normal bandwidth allocation).
|
||||
*/
|
||||
ret = tb_available_bandwidth(tb, in, out, &available_up, &available_down);
|
||||
if (ret)
|
||||
goto reclaim;
|
||||
|
||||
tb_port_dbg(in, "bandwidth available for allocation %d/%d Mb/s\n",
|
||||
available_up, available_down);
|
||||
|
||||
if ((*requested_up >= 0 && available_up >= requested_up_corrected) ||
|
||||
(*requested_down >= 0 && available_down >= requested_down_corrected)) {
|
||||
ret = tb_tunnel_alloc_bandwidth(tunnel, requested_up,
|
||||
requested_down);
|
||||
} else {
|
||||
ret = -ENOBUFS;
|
||||
}
|
||||
|
||||
reclaim:
|
||||
tb_reclaim_usb3_bandwidth(tb, in, out);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void tb_handle_dp_bandwidth_request(struct work_struct *work)
|
||||
{
|
||||
struct tb_hotplug_event *ev = container_of(work, typeof(*ev), work);
|
||||
int requested_bw, requested_up, requested_down, ret;
|
||||
struct tb_port *in, *out;
|
||||
struct tb_tunnel *tunnel;
|
||||
struct tb *tb = ev->tb;
|
||||
struct tb_cm *tcm = tb_priv(tb);
|
||||
struct tb_switch *sw;
|
||||
|
||||
pm_runtime_get_sync(&tb->dev);
|
||||
|
||||
mutex_lock(&tb->lock);
|
||||
if (!tcm->hotplug_active)
|
||||
goto unlock;
|
||||
|
||||
sw = tb_switch_find_by_route(tb, ev->route);
|
||||
if (!sw) {
|
||||
tb_warn(tb, "bandwidth request from non-existent router %llx\n",
|
||||
ev->route);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
in = &sw->ports[ev->port];
|
||||
if (!tb_port_is_dpin(in)) {
|
||||
tb_port_warn(in, "bandwidth request to non-DP IN adapter\n");
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
tb_port_dbg(in, "handling bandwidth allocation request\n");
|
||||
|
||||
if (!usb4_dp_port_bw_mode_enabled(in)) {
|
||||
tb_port_warn(in, "bandwidth allocation mode not enabled\n");
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
ret = usb4_dp_port_requested_bw(in);
|
||||
if (ret < 0) {
|
||||
if (ret == -ENODATA)
|
||||
tb_port_dbg(in, "no bandwidth request active\n");
|
||||
else
|
||||
tb_port_warn(in, "failed to read requested bandwidth\n");
|
||||
goto unlock;
|
||||
}
|
||||
requested_bw = ret;
|
||||
|
||||
tb_port_dbg(in, "requested bandwidth %d Mb/s\n", requested_bw);
|
||||
|
||||
tunnel = tb_find_tunnel(tb, TB_TUNNEL_DP, in, NULL);
|
||||
if (!tunnel) {
|
||||
tb_port_warn(in, "failed to find tunnel\n");
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
out = tunnel->dst_port;
|
||||
|
||||
if (in->sw->config.depth < out->sw->config.depth) {
|
||||
requested_up = -1;
|
||||
requested_down = requested_bw;
|
||||
} else {
|
||||
requested_up = requested_bw;
|
||||
requested_down = -1;
|
||||
}
|
||||
|
||||
ret = tb_alloc_dp_bandwidth(tunnel, &requested_up, &requested_down);
|
||||
if (ret) {
|
||||
if (ret == -ENOBUFS)
|
||||
tb_port_warn(in, "not enough bandwidth available\n");
|
||||
else
|
||||
tb_port_warn(in, "failed to change bandwidth allocation\n");
|
||||
} else {
|
||||
tb_port_dbg(in, "bandwidth allocation changed to %d/%d Mb/s\n",
|
||||
requested_up, requested_down);
|
||||
|
||||
/* Update other clients about the allocation change */
|
||||
tb_recalc_estimated_bandwidth(tb);
|
||||
}
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&tb->lock);
|
||||
|
||||
pm_runtime_mark_last_busy(&tb->dev);
|
||||
pm_runtime_put_autosuspend(&tb->dev);
|
||||
}
|
||||
|
||||
static void tb_queue_dp_bandwidth_request(struct tb *tb, u64 route, u8 port)
|
||||
{
|
||||
struct tb_hotplug_event *ev;
|
||||
|
||||
ev = kmalloc(sizeof(*ev), GFP_KERNEL);
|
||||
if (!ev)
|
||||
return;
|
||||
|
||||
ev->tb = tb;
|
||||
ev->route = route;
|
||||
ev->port = port;
|
||||
INIT_WORK(&ev->work, tb_handle_dp_bandwidth_request);
|
||||
queue_work(tb->wq, &ev->work);
|
||||
}
|
||||
|
||||
static void tb_handle_notification(struct tb *tb, u64 route,
|
||||
const struct cfg_error_pkg *error)
|
||||
{
|
||||
if (tb_cfg_ack_notification(tb->ctl, route, error))
|
||||
tb_warn(tb, "could not ack notification on %llx\n", route);
|
||||
|
||||
switch (error->error) {
|
||||
case TB_CFG_ERROR_DP_BW:
|
||||
tb_queue_dp_bandwidth_request(tb, route, error->port);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Ack is enough */
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* tb_schedule_hotplug_handler() - callback function for the control channel
|
||||
*
|
||||
@ -1379,15 +1846,19 @@ static void tb_handle_event(struct tb *tb, enum tb_cfg_pkg_type type,
|
||||
const void *buf, size_t size)
|
||||
{
|
||||
const struct cfg_event_pkg *pkg = buf;
|
||||
u64 route;
|
||||
u64 route = tb_cfg_get_route(&pkg->header);
|
||||
|
||||
if (type != TB_CFG_PKG_EVENT) {
|
||||
switch (type) {
|
||||
case TB_CFG_PKG_ERROR:
|
||||
tb_handle_notification(tb, route, (const struct cfg_error_pkg *)buf);
|
||||
return;
|
||||
case TB_CFG_PKG_EVENT:
|
||||
break;
|
||||
default:
|
||||
tb_warn(tb, "unexpected event %#x, ignoring\n", type);
|
||||
return;
|
||||
}
|
||||
|
||||
route = tb_cfg_get_route(&pkg->header);
|
||||
|
||||
if (tb_cfg_ack_plug(tb->ctl, route, pkg->port, pkg->unplug)) {
|
||||
tb_warn(tb, "could not ack plug event on %llx:%x\n", route,
|
||||
pkg->port);
|
||||
@ -1817,6 +2288,7 @@ struct tb *tb_probe(struct tb_nhi *nhi)
|
||||
INIT_LIST_HEAD(&tcm->tunnel_list);
|
||||
INIT_LIST_HEAD(&tcm->dp_resources);
|
||||
INIT_DELAYED_WORK(&tcm->remove_work, tb_remove_work);
|
||||
tb_init_bandwidth_groups(tcm);
|
||||
|
||||
tb_dbg(tb, "using software connection manager\n");
|
||||
|
||||
|
@ -223,6 +223,23 @@ struct tb_switch {
|
||||
enum tb_clx clx;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct tb_bandwidth_group - Bandwidth management group
|
||||
* @tb: Pointer to the domain the group belongs to
|
||||
* @index: Index of the group (aka Group_ID). Valid values %1-%7
|
||||
* @ports: DP IN adapters belonging to this group are linked here
|
||||
*
|
||||
* Any tunnel that requires isochronous bandwidth (that's DP for now) is
|
||||
* attached to a bandwidth group. All tunnels going through the same
|
||||
* USB4 links share the same group and can dynamically distribute the
|
||||
* bandwidth within the group.
|
||||
*/
|
||||
struct tb_bandwidth_group {
|
||||
struct tb *tb;
|
||||
int index;
|
||||
struct list_head ports;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct tb_port - a thunderbolt port, part of a tb_switch
|
||||
* @config: Cached port configuration read from registers
|
||||
@ -247,6 +264,9 @@ struct tb_switch {
|
||||
* @ctl_credits: Buffers reserved for control path
|
||||
* @dma_credits: Number of credits allocated for DMA tunneling for all
|
||||
* DMA paths through this port.
|
||||
* @group: Bandwidth allocation group the adapter is assigned to. Only
|
||||
* used for DP IN adapters for now.
|
||||
* @group_list: The adapter is linked to the group's list of ports through this
|
||||
*
|
||||
* In USB4 terminology this structure represents an adapter (protocol or
|
||||
* lane adapter).
|
||||
@ -272,6 +292,8 @@ struct tb_port {
|
||||
unsigned int total_credits;
|
||||
unsigned int ctl_credits;
|
||||
unsigned int dma_credits;
|
||||
struct tb_bandwidth_group *group;
|
||||
struct list_head group_list;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1047,7 +1069,7 @@ void tb_port_lane_bonding_disable(struct tb_port *port);
|
||||
int tb_port_wait_for_link_width(struct tb_port *port, int width,
|
||||
int timeout_msec);
|
||||
int tb_port_update_credits(struct tb_port *port);
|
||||
bool tb_port_is_clx_enabled(struct tb_port *port, enum tb_clx clx);
|
||||
bool tb_port_is_clx_enabled(struct tb_port *port, unsigned int clx);
|
||||
|
||||
int tb_switch_find_vse_cap(struct tb_switch *sw, enum tb_switch_vse_cap vsec);
|
||||
int tb_switch_find_cap(struct tb_switch *sw, enum tb_switch_cap cap);
|
||||
@ -1238,6 +1260,21 @@ int usb4_usb3_port_allocate_bandwidth(struct tb_port *port, int *upstream_bw,
|
||||
int usb4_usb3_port_release_bandwidth(struct tb_port *port, int *upstream_bw,
|
||||
int *downstream_bw);
|
||||
|
||||
int usb4_dp_port_set_cm_id(struct tb_port *port, int cm_id);
|
||||
bool usb4_dp_port_bw_mode_supported(struct tb_port *port);
|
||||
bool usb4_dp_port_bw_mode_enabled(struct tb_port *port);
|
||||
int usb4_dp_port_set_cm_bw_mode_supported(struct tb_port *port, bool supported);
|
||||
int usb4_dp_port_group_id(struct tb_port *port);
|
||||
int usb4_dp_port_set_group_id(struct tb_port *port, int group_id);
|
||||
int usb4_dp_port_nrd(struct tb_port *port, int *rate, int *lanes);
|
||||
int usb4_dp_port_set_nrd(struct tb_port *port, int rate, int lanes);
|
||||
int usb4_dp_port_granularity(struct tb_port *port);
|
||||
int usb4_dp_port_set_granularity(struct tb_port *port, int granularity);
|
||||
int usb4_dp_port_set_estimated_bw(struct tb_port *port, int bw);
|
||||
int usb4_dp_port_allocated_bw(struct tb_port *port);
|
||||
int usb4_dp_port_allocate_bw(struct tb_port *port, int bw);
|
||||
int usb4_dp_port_requested_bw(struct tb_port *port);
|
||||
|
||||
static inline bool tb_is_usb4_port_device(const struct device *dev)
|
||||
{
|
||||
return dev->type == &usb4_port_device_type;
|
||||
|
@ -29,6 +29,7 @@ enum tb_cfg_error {
|
||||
TB_CFG_ERROR_HEC_ERROR_DETECTED = 12,
|
||||
TB_CFG_ERROR_FLOW_CONTROL_ERROR = 13,
|
||||
TB_CFG_ERROR_LOCK = 15,
|
||||
TB_CFG_ERROR_DP_BW = 32,
|
||||
};
|
||||
|
||||
/* common header */
|
||||
@ -64,14 +65,16 @@ struct cfg_write_pkg {
|
||||
/* TB_CFG_PKG_ERROR */
|
||||
struct cfg_error_pkg {
|
||||
struct tb_cfg_header header;
|
||||
enum tb_cfg_error error:4;
|
||||
u32 zero1:4;
|
||||
enum tb_cfg_error error:8;
|
||||
u32 port:6;
|
||||
u32 zero2:2; /* Both should be zero, still they are different fields. */
|
||||
u32 zero3:14;
|
||||
u32 reserved:16;
|
||||
u32 pg:2;
|
||||
} __packed;
|
||||
|
||||
struct cfg_ack_pkg {
|
||||
struct tb_cfg_header header;
|
||||
};
|
||||
|
||||
#define TB_CFG_ERROR_PG_HOT_PLUG 0x2
|
||||
#define TB_CFG_ERROR_PG_HOT_UNPLUG 0x3
|
||||
|
||||
|
@ -50,6 +50,10 @@ enum tb_port_state {
|
||||
TB_PORT_DISABLED = 0, /* tb_cap_phy.disable == 1 */
|
||||
TB_PORT_CONNECTING = 1, /* retry */
|
||||
TB_PORT_UP = 2,
|
||||
TB_PORT_TX_CL0S = 3,
|
||||
TB_PORT_RX_CL0S = 4,
|
||||
TB_PORT_CL1 = 5,
|
||||
TB_PORT_CL2 = 6,
|
||||
TB_PORT_UNPLUGGED = 7,
|
||||
};
|
||||
|
||||
@ -381,15 +385,42 @@ struct tb_regs_port_header {
|
||||
#define ADP_DP_CS_1_AUX_RX_HOPID_MASK GENMASK(21, 11)
|
||||
#define ADP_DP_CS_1_AUX_RX_HOPID_SHIFT 11
|
||||
#define ADP_DP_CS_2 0x02
|
||||
#define ADP_DP_CS_2_NRD_MLC_MASK GENMASK(2, 0)
|
||||
#define ADP_DP_CS_2_HDP BIT(6)
|
||||
#define ADP_DP_CS_2_NRD_MLR_MASK GENMASK(9, 7)
|
||||
#define ADP_DP_CS_2_NRD_MLR_SHIFT 7
|
||||
#define ADP_DP_CS_2_CA BIT(10)
|
||||
#define ADP_DP_CS_2_GR_MASK GENMASK(12, 11)
|
||||
#define ADP_DP_CS_2_GR_SHIFT 11
|
||||
#define ADP_DP_CS_2_GR_0_25G 0x0
|
||||
#define ADP_DP_CS_2_GR_0_5G 0x1
|
||||
#define ADP_DP_CS_2_GR_1G 0x2
|
||||
#define ADP_DP_CS_2_GROUP_ID_MASK GENMASK(15, 13)
|
||||
#define ADP_DP_CS_2_GROUP_ID_SHIFT 13
|
||||
#define ADP_DP_CS_2_CM_ID_MASK GENMASK(19, 16)
|
||||
#define ADP_DP_CS_2_CM_ID_SHIFT 16
|
||||
#define ADP_DP_CS_2_CMMS BIT(20)
|
||||
#define ADP_DP_CS_2_ESTIMATED_BW_MASK GENMASK(31, 24)
|
||||
#define ADP_DP_CS_2_ESTIMATED_BW_SHIFT 24
|
||||
#define ADP_DP_CS_3 0x03
|
||||
#define ADP_DP_CS_3_HDPC BIT(9)
|
||||
#define DP_LOCAL_CAP 0x04
|
||||
#define DP_REMOTE_CAP 0x05
|
||||
/* For DP IN adapter */
|
||||
#define DP_STATUS 0x06
|
||||
#define DP_STATUS_ALLOCATED_BW_MASK GENMASK(31, 24)
|
||||
#define DP_STATUS_ALLOCATED_BW_SHIFT 24
|
||||
/* For DP OUT adapter */
|
||||
#define DP_STATUS_CTRL 0x06
|
||||
#define DP_STATUS_CTRL_CMHS BIT(25)
|
||||
#define DP_STATUS_CTRL_UF BIT(26)
|
||||
#define DP_COMMON_CAP 0x07
|
||||
/* Only if DP IN supports BW allocation mode */
|
||||
#define ADP_DP_CS_8 0x08
|
||||
#define ADP_DP_CS_8_REQUESTED_BW_MASK GENMASK(7, 0)
|
||||
#define ADP_DP_CS_8_DPME BIT(30)
|
||||
#define ADP_DP_CS_8_DR BIT(31)
|
||||
|
||||
/*
|
||||
* DP_COMMON_CAP offsets work also for DP_LOCAL_CAP and DP_REMOTE_CAP
|
||||
* with exception of DPRX done.
|
||||
@ -406,7 +437,12 @@ struct tb_regs_port_header {
|
||||
#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_BW_MODE BIT(28)
|
||||
#define DP_COMMON_CAP_DPRX_DONE BIT(31)
|
||||
/* Only present if DP IN supports BW allocation mode */
|
||||
#define ADP_DP_CS_8 0x08
|
||||
#define ADP_DP_CS_8_DPME BIT(30)
|
||||
#define ADP_DP_CS_8_DR BIT(31)
|
||||
|
||||
/* PCIe adapter registers */
|
||||
#define ADP_PCIE_CS_0 0x00
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/ktime.h>
|
||||
|
||||
#include "tunnel.h"
|
||||
#include "tb.h"
|
||||
@ -44,12 +45,17 @@
|
||||
/* Minimum number of credits for DMA path */
|
||||
#define TB_MIN_DMA_CREDITS 1U
|
||||
|
||||
static bool bw_alloc_mode = true;
|
||||
module_param(bw_alloc_mode, bool, 0444);
|
||||
MODULE_PARM_DESC(bw_alloc_mode,
|
||||
"enable bandwidth allocation mode if supported (default: true)");
|
||||
|
||||
static const char * const tb_tunnel_names[] = { "PCI", "DP", "DMA", "USB3" };
|
||||
|
||||
#define __TB_TUNNEL_PRINT(level, tunnel, fmt, arg...) \
|
||||
do { \
|
||||
struct tb_tunnel *__tunnel = (tunnel); \
|
||||
level(__tunnel->tb, "%llx:%x <-> %llx:%x (%s): " fmt, \
|
||||
level(__tunnel->tb, "%llx:%u <-> %llx:%u (%s): " fmt, \
|
||||
tb_route(__tunnel->src_port->sw), \
|
||||
__tunnel->src_port->port, \
|
||||
tb_route(__tunnel->dst_port->sw), \
|
||||
@ -339,9 +345,10 @@ static bool tb_dp_is_usb4(const struct tb_switch *sw)
|
||||
return tb_switch_is_usb4(sw) || tb_switch_is_titan_ridge(sw);
|
||||
}
|
||||
|
||||
static int tb_dp_cm_handshake(struct tb_port *in, struct tb_port *out)
|
||||
static int tb_dp_cm_handshake(struct tb_port *in, struct tb_port *out,
|
||||
int timeout_msec)
|
||||
{
|
||||
int timeout = 10;
|
||||
ktime_t timeout = ktime_add_ms(ktime_get(), timeout_msec);
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
@ -368,8 +375,8 @@ static int tb_dp_cm_handshake(struct tb_port *in, struct tb_port *out)
|
||||
return ret;
|
||||
if (!(val & DP_STATUS_CTRL_CMHS))
|
||||
return 0;
|
||||
usleep_range(10, 100);
|
||||
} while (timeout--);
|
||||
usleep_range(100, 150);
|
||||
} while (ktime_before(ktime_get(), timeout));
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
@ -519,7 +526,7 @@ static int tb_dp_xchg_caps(struct tb_tunnel *tunnel)
|
||||
* Perform connection manager handshake between IN and OUT ports
|
||||
* before capabilities exchange can take place.
|
||||
*/
|
||||
ret = tb_dp_cm_handshake(in, out);
|
||||
ret = tb_dp_cm_handshake(in, out, 1500);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -597,6 +604,133 @@ static int tb_dp_xchg_caps(struct tb_tunnel *tunnel)
|
||||
in->cap_adap + DP_REMOTE_CAP, 1);
|
||||
}
|
||||
|
||||
static int tb_dp_bw_alloc_mode_enable(struct tb_tunnel *tunnel)
|
||||
{
|
||||
int ret, estimated_bw, granularity, tmp;
|
||||
struct tb_port *out = tunnel->dst_port;
|
||||
struct tb_port *in = tunnel->src_port;
|
||||
u32 out_dp_cap, out_rate, out_lanes;
|
||||
u32 in_dp_cap, in_rate, in_lanes;
|
||||
u32 rate, lanes;
|
||||
|
||||
if (!bw_alloc_mode)
|
||||
return 0;
|
||||
|
||||
ret = usb4_dp_port_set_cm_bw_mode_supported(in, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = usb4_dp_port_set_group_id(in, in->group->index);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Get the non-reduced rate and lanes based on the lowest
|
||||
* capability of both adapters.
|
||||
*/
|
||||
ret = tb_port_read(in, &in_dp_cap, TB_CFG_PORT,
|
||||
in->cap_adap + DP_LOCAL_CAP, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = tb_port_read(out, &out_dp_cap, TB_CFG_PORT,
|
||||
out->cap_adap + DP_LOCAL_CAP, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
in_rate = tb_dp_cap_get_rate(in_dp_cap);
|
||||
in_lanes = tb_dp_cap_get_lanes(in_dp_cap);
|
||||
out_rate = tb_dp_cap_get_rate(out_dp_cap);
|
||||
out_lanes = tb_dp_cap_get_lanes(out_dp_cap);
|
||||
|
||||
rate = min(in_rate, out_rate);
|
||||
lanes = min(in_lanes, out_lanes);
|
||||
tmp = tb_dp_bandwidth(rate, lanes);
|
||||
|
||||
tb_port_dbg(in, "non-reduced bandwidth %u Mb/s x%u = %u Mb/s\n", rate,
|
||||
lanes, tmp);
|
||||
|
||||
ret = usb4_dp_port_set_nrd(in, rate, lanes);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (granularity = 250; tmp / granularity > 255 && granularity <= 1000;
|
||||
granularity *= 2)
|
||||
;
|
||||
|
||||
tb_port_dbg(in, "granularity %d Mb/s\n", granularity);
|
||||
|
||||
/*
|
||||
* Returns -EINVAL if granularity above is outside of the
|
||||
* accepted ranges.
|
||||
*/
|
||||
ret = usb4_dp_port_set_granularity(in, granularity);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Bandwidth estimation is pretty much what we have in
|
||||
* max_up/down fields. For discovery we just read what the
|
||||
* estimation was set to.
|
||||
*/
|
||||
if (in->sw->config.depth < out->sw->config.depth)
|
||||
estimated_bw = tunnel->max_down;
|
||||
else
|
||||
estimated_bw = tunnel->max_up;
|
||||
|
||||
tb_port_dbg(in, "estimated bandwidth %d Mb/s\n", estimated_bw);
|
||||
|
||||
ret = usb4_dp_port_set_estimated_bw(in, estimated_bw);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Initial allocation should be 0 according the spec */
|
||||
ret = usb4_dp_port_allocate_bw(in, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
tb_port_dbg(in, "bandwidth allocation mode enabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tb_dp_init(struct tb_tunnel *tunnel)
|
||||
{
|
||||
struct tb_port *in = tunnel->src_port;
|
||||
struct tb_switch *sw = in->sw;
|
||||
struct tb *tb = in->sw->tb;
|
||||
int ret;
|
||||
|
||||
ret = tb_dp_xchg_caps(tunnel);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!tb_switch_is_usb4(sw))
|
||||
return 0;
|
||||
|
||||
if (!usb4_dp_port_bw_mode_supported(in))
|
||||
return 0;
|
||||
|
||||
tb_port_dbg(in, "bandwidth allocation mode supported\n");
|
||||
|
||||
ret = usb4_dp_port_set_cm_id(in, tb->index);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return tb_dp_bw_alloc_mode_enable(tunnel);
|
||||
}
|
||||
|
||||
static void tb_dp_deinit(struct tb_tunnel *tunnel)
|
||||
{
|
||||
struct tb_port *in = tunnel->src_port;
|
||||
|
||||
if (!usb4_dp_port_bw_mode_supported(in))
|
||||
return;
|
||||
if (usb4_dp_port_bw_mode_enabled(in)) {
|
||||
usb4_dp_port_set_cm_bw_mode_supported(in, false);
|
||||
tb_port_dbg(in, "bandwidth allocation mode disabled\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int tb_dp_activate(struct tb_tunnel *tunnel, bool active)
|
||||
{
|
||||
int ret;
|
||||
@ -634,49 +768,275 @@ static int tb_dp_activate(struct tb_tunnel *tunnel, bool active)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* max_bw is rounded up to next granularity */
|
||||
static int tb_dp_nrd_bandwidth(struct tb_tunnel *tunnel, int *max_bw)
|
||||
{
|
||||
struct tb_port *in = tunnel->src_port;
|
||||
int ret, rate, lanes, nrd_bw;
|
||||
|
||||
ret = usb4_dp_port_nrd(in, &rate, &lanes);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
nrd_bw = tb_dp_bandwidth(rate, lanes);
|
||||
|
||||
if (max_bw) {
|
||||
ret = usb4_dp_port_granularity(in);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*max_bw = roundup(nrd_bw, ret);
|
||||
}
|
||||
|
||||
return nrd_bw;
|
||||
}
|
||||
|
||||
static int tb_dp_bw_mode_consumed_bandwidth(struct tb_tunnel *tunnel,
|
||||
int *consumed_up, int *consumed_down)
|
||||
{
|
||||
struct tb_port *out = tunnel->dst_port;
|
||||
struct tb_port *in = tunnel->src_port;
|
||||
int ret, allocated_bw, max_bw;
|
||||
|
||||
if (!usb4_dp_port_bw_mode_enabled(in))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!tunnel->bw_mode)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* Read what was allocated previously if any */
|
||||
ret = usb4_dp_port_allocated_bw(in);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
allocated_bw = ret;
|
||||
|
||||
ret = tb_dp_nrd_bandwidth(tunnel, &max_bw);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (allocated_bw == max_bw)
|
||||
allocated_bw = ret;
|
||||
|
||||
tb_port_dbg(in, "consumed bandwidth through allocation mode %d Mb/s\n",
|
||||
allocated_bw);
|
||||
|
||||
if (in->sw->config.depth < out->sw->config.depth) {
|
||||
*consumed_up = 0;
|
||||
*consumed_down = allocated_bw;
|
||||
} else {
|
||||
*consumed_up = allocated_bw;
|
||||
*consumed_down = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tb_dp_allocated_bandwidth(struct tb_tunnel *tunnel, int *allocated_up,
|
||||
int *allocated_down)
|
||||
{
|
||||
struct tb_port *out = tunnel->dst_port;
|
||||
struct tb_port *in = tunnel->src_port;
|
||||
|
||||
/*
|
||||
* If we have already set the allocated bandwidth then use that.
|
||||
* Otherwise we read it from the DPRX.
|
||||
*/
|
||||
if (usb4_dp_port_bw_mode_enabled(in) && tunnel->bw_mode) {
|
||||
int ret, allocated_bw, max_bw;
|
||||
|
||||
ret = usb4_dp_port_allocated_bw(in);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
allocated_bw = ret;
|
||||
|
||||
ret = tb_dp_nrd_bandwidth(tunnel, &max_bw);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (allocated_bw == max_bw)
|
||||
allocated_bw = ret;
|
||||
|
||||
if (in->sw->config.depth < out->sw->config.depth) {
|
||||
*allocated_up = 0;
|
||||
*allocated_down = allocated_bw;
|
||||
} else {
|
||||
*allocated_up = allocated_bw;
|
||||
*allocated_down = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return tunnel->consumed_bandwidth(tunnel, allocated_up,
|
||||
allocated_down);
|
||||
}
|
||||
|
||||
static int tb_dp_alloc_bandwidth(struct tb_tunnel *tunnel, int *alloc_up,
|
||||
int *alloc_down)
|
||||
{
|
||||
struct tb_port *out = tunnel->dst_port;
|
||||
struct tb_port *in = tunnel->src_port;
|
||||
int max_bw, ret, tmp;
|
||||
|
||||
if (!usb4_dp_port_bw_mode_enabled(in))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ret = tb_dp_nrd_bandwidth(tunnel, &max_bw);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (in->sw->config.depth < out->sw->config.depth) {
|
||||
tmp = min(*alloc_down, max_bw);
|
||||
ret = usb4_dp_port_allocate_bw(in, tmp);
|
||||
if (ret)
|
||||
return ret;
|
||||
*alloc_down = tmp;
|
||||
*alloc_up = 0;
|
||||
} else {
|
||||
tmp = min(*alloc_up, max_bw);
|
||||
ret = usb4_dp_port_allocate_bw(in, tmp);
|
||||
if (ret)
|
||||
return ret;
|
||||
*alloc_down = 0;
|
||||
*alloc_up = tmp;
|
||||
}
|
||||
|
||||
/* Now we can use BW mode registers to figure out the bandwidth */
|
||||
/* TODO: need to handle discovery too */
|
||||
tunnel->bw_mode = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tb_dp_read_dprx(struct tb_tunnel *tunnel, u32 *rate, u32 *lanes,
|
||||
int timeout_msec)
|
||||
{
|
||||
ktime_t timeout = ktime_add_ms(ktime_get(), timeout_msec);
|
||||
struct tb_port *in = tunnel->src_port;
|
||||
|
||||
/*
|
||||
* Wait for DPRX done. Normally it should be already set for
|
||||
* active tunnel.
|
||||
*/
|
||||
do {
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
ret = tb_port_read(in, &val, TB_CFG_PORT,
|
||||
in->cap_adap + DP_COMMON_CAP, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (val & DP_COMMON_CAP_DPRX_DONE) {
|
||||
*rate = tb_dp_cap_get_rate(val);
|
||||
*lanes = tb_dp_cap_get_lanes(val);
|
||||
|
||||
tb_port_dbg(in, "consumed bandwidth through DPRX %d Mb/s\n",
|
||||
tb_dp_bandwidth(*rate, *lanes));
|
||||
return 0;
|
||||
}
|
||||
usleep_range(100, 150);
|
||||
} while (ktime_before(ktime_get(), timeout));
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/* Read cap from tunnel DP IN */
|
||||
static int tb_dp_read_cap(struct tb_tunnel *tunnel, unsigned int cap, u32 *rate,
|
||||
u32 *lanes)
|
||||
{
|
||||
struct tb_port *in = tunnel->src_port;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
switch (cap) {
|
||||
case DP_LOCAL_CAP:
|
||||
case DP_REMOTE_CAP:
|
||||
break;
|
||||
|
||||
default:
|
||||
tb_tunnel_WARN(tunnel, "invalid capability index %#x\n", cap);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read from the copied remote cap so that we take into account
|
||||
* if capabilities were reduced during exchange.
|
||||
*/
|
||||
ret = tb_port_read(in, &val, TB_CFG_PORT, in->cap_adap + cap, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*rate = tb_dp_cap_get_rate(val);
|
||||
*lanes = tb_dp_cap_get_lanes(val);
|
||||
|
||||
tb_port_dbg(in, "bandwidth from %#x capability %d Mb/s\n", cap,
|
||||
tb_dp_bandwidth(*rate, *lanes));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tb_dp_maximum_bandwidth(struct tb_tunnel *tunnel, int *max_up,
|
||||
int *max_down)
|
||||
{
|
||||
struct tb_port *in = tunnel->src_port;
|
||||
u32 rate, lanes;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* DP IN adapter DP_LOCAL_CAP gets updated to the lowest AUX read
|
||||
* parameter values so this so we can use this to determine the
|
||||
* maximum possible bandwidth over this link.
|
||||
*/
|
||||
ret = tb_dp_read_cap(tunnel, DP_LOCAL_CAP, &rate, &lanes);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (in->sw->config.depth < tunnel->dst_port->sw->config.depth) {
|
||||
*max_up = 0;
|
||||
*max_down = tb_dp_bandwidth(rate, lanes);
|
||||
} else {
|
||||
*max_up = tb_dp_bandwidth(rate, lanes);
|
||||
*max_down = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tb_dp_consumed_bandwidth(struct tb_tunnel *tunnel, int *consumed_up,
|
||||
int *consumed_down)
|
||||
{
|
||||
struct tb_port *in = tunnel->src_port;
|
||||
const struct tb_switch *sw = in->sw;
|
||||
u32 val, rate = 0, lanes = 0;
|
||||
u32 rate = 0, lanes = 0;
|
||||
int ret;
|
||||
|
||||
if (tb_dp_is_usb4(sw)) {
|
||||
int timeout = 20;
|
||||
|
||||
/*
|
||||
* Wait for DPRX done. Normally it should be already set
|
||||
* for active tunnel.
|
||||
* On USB4 routers check if the bandwidth allocation
|
||||
* mode is enabled first and then read the bandwidth
|
||||
* through those registers.
|
||||
*/
|
||||
do {
|
||||
ret = tb_port_read(in, &val, TB_CFG_PORT,
|
||||
in->cap_adap + DP_COMMON_CAP, 1);
|
||||
ret = tb_dp_bw_mode_consumed_bandwidth(tunnel, consumed_up,
|
||||
consumed_down);
|
||||
if (ret < 0) {
|
||||
if (ret != -EOPNOTSUPP)
|
||||
return ret;
|
||||
} else if (!ret) {
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* Then see if the DPRX negotiation is ready and if yes
|
||||
* return that bandwidth (it may be smaller than the
|
||||
* reduced one). Otherwise return the remote (possibly
|
||||
* reduced) caps.
|
||||
*/
|
||||
ret = tb_dp_read_dprx(tunnel, &rate, &lanes, 150);
|
||||
if (ret) {
|
||||
if (ret == -ETIMEDOUT)
|
||||
ret = tb_dp_read_cap(tunnel, DP_REMOTE_CAP,
|
||||
&rate, &lanes);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (val & DP_COMMON_CAP_DPRX_DONE) {
|
||||
rate = tb_dp_cap_get_rate(val);
|
||||
lanes = tb_dp_cap_get_lanes(val);
|
||||
break;
|
||||
}
|
||||
msleep(250);
|
||||
} while (timeout--);
|
||||
|
||||
if (!timeout)
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
} else if (sw->generation >= 2) {
|
||||
/*
|
||||
* Read from the copied remote cap so that we take into
|
||||
* account if capabilities were reduced during exchange.
|
||||
*/
|
||||
ret = tb_port_read(in, &val, TB_CFG_PORT,
|
||||
in->cap_adap + DP_REMOTE_CAP, 1);
|
||||
ret = tb_dp_read_cap(tunnel, DP_REMOTE_CAP, &rate, &lanes);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
rate = tb_dp_cap_get_rate(val);
|
||||
lanes = tb_dp_cap_get_lanes(val);
|
||||
} else {
|
||||
/* No bandwidth management for legacy devices */
|
||||
*consumed_up = 0;
|
||||
@ -798,8 +1158,12 @@ struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in,
|
||||
if (!tunnel)
|
||||
return NULL;
|
||||
|
||||
tunnel->init = tb_dp_xchg_caps;
|
||||
tunnel->init = tb_dp_init;
|
||||
tunnel->deinit = tb_dp_deinit;
|
||||
tunnel->activate = tb_dp_activate;
|
||||
tunnel->maximum_bandwidth = tb_dp_maximum_bandwidth;
|
||||
tunnel->allocated_bandwidth = tb_dp_allocated_bandwidth;
|
||||
tunnel->alloc_bandwidth = tb_dp_alloc_bandwidth;
|
||||
tunnel->consumed_bandwidth = tb_dp_consumed_bandwidth;
|
||||
tunnel->src_port = in;
|
||||
|
||||
@ -887,8 +1251,12 @@ struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in,
|
||||
if (!tunnel)
|
||||
return NULL;
|
||||
|
||||
tunnel->init = tb_dp_xchg_caps;
|
||||
tunnel->init = tb_dp_init;
|
||||
tunnel->deinit = tb_dp_deinit;
|
||||
tunnel->activate = tb_dp_activate;
|
||||
tunnel->maximum_bandwidth = tb_dp_maximum_bandwidth;
|
||||
tunnel->allocated_bandwidth = tb_dp_allocated_bandwidth;
|
||||
tunnel->alloc_bandwidth = tb_dp_alloc_bandwidth;
|
||||
tunnel->consumed_bandwidth = tb_dp_consumed_bandwidth;
|
||||
tunnel->src_port = in;
|
||||
tunnel->dst_port = out;
|
||||
@ -1713,6 +2081,72 @@ static bool tb_tunnel_is_active(const struct tb_tunnel *tunnel)
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* tb_tunnel_maximum_bandwidth() - Return maximum possible bandwidth
|
||||
* @tunnel: Tunnel to check
|
||||
* @max_up: Maximum upstream bandwidth in Mb/s
|
||||
* @max_down: Maximum downstream bandwidth in Mb/s
|
||||
*
|
||||
* Returns maximum possible bandwidth this tunnel can go if not limited
|
||||
* by other bandwidth clients. If the tunnel does not support this
|
||||
* returns %-EOPNOTSUPP.
|
||||
*/
|
||||
int tb_tunnel_maximum_bandwidth(struct tb_tunnel *tunnel, int *max_up,
|
||||
int *max_down)
|
||||
{
|
||||
if (!tb_tunnel_is_active(tunnel))
|
||||
return -EINVAL;
|
||||
|
||||
if (tunnel->maximum_bandwidth)
|
||||
return tunnel->maximum_bandwidth(tunnel, max_up, max_down);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/**
|
||||
* tb_tunnel_allocated_bandwidth() - Return bandwidth allocated for the tunnel
|
||||
* @tunnel: Tunnel to check
|
||||
* @allocated_up: Currently allocated upstream bandwidth in Mb/s is stored here
|
||||
* @allocated_down: Currently allocated downstream bandwidth in Mb/s is
|
||||
* stored here
|
||||
*
|
||||
* Returns the bandwidth allocated for the tunnel. This may be higher
|
||||
* than what the tunnel actually consumes.
|
||||
*/
|
||||
int tb_tunnel_allocated_bandwidth(struct tb_tunnel *tunnel, int *allocated_up,
|
||||
int *allocated_down)
|
||||
{
|
||||
if (!tb_tunnel_is_active(tunnel))
|
||||
return -EINVAL;
|
||||
|
||||
if (tunnel->allocated_bandwidth)
|
||||
return tunnel->allocated_bandwidth(tunnel, allocated_up,
|
||||
allocated_down);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/**
|
||||
* tb_tunnel_alloc_bandwidth() - Change tunnel bandwidth allocation
|
||||
* @tunnel: Tunnel whose bandwidth allocation to change
|
||||
* @alloc_up: New upstream bandwidth in Mb/s
|
||||
* @alloc_down: New downstream bandwidth in Mb/s
|
||||
*
|
||||
* Tries to change tunnel bandwidth allocation. If succeeds returns %0
|
||||
* and updates @alloc_up and @alloc_down to that was actually allocated
|
||||
* (it may not be the same as passed originally). Returns negative errno
|
||||
* in case of failure.
|
||||
*/
|
||||
int tb_tunnel_alloc_bandwidth(struct tb_tunnel *tunnel, int *alloc_up,
|
||||
int *alloc_down)
|
||||
{
|
||||
if (!tb_tunnel_is_active(tunnel))
|
||||
return -EINVAL;
|
||||
|
||||
if (tunnel->alloc_bandwidth)
|
||||
return tunnel->alloc_bandwidth(tunnel, alloc_up, alloc_down);
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/**
|
||||
* tb_tunnel_consumed_bandwidth() - Return bandwidth consumed by the tunnel
|
||||
* @tunnel: Tunnel to check
|
||||
|
@ -29,6 +29,9 @@ enum tb_tunnel_type {
|
||||
* @init: Optional tunnel specific initialization
|
||||
* @deinit: Optional tunnel specific de-initialization
|
||||
* @activate: Optional tunnel specific activation/deactivation
|
||||
* @maximum_bandwidth: Returns maximum possible bandwidth for this tunnel
|
||||
* @allocated_bandwidth: Return how much bandwidth is allocated for the tunnel
|
||||
* @alloc_bandwidth: Change tunnel bandwidth allocation
|
||||
* @consumed_bandwidth: Return how much bandwidth the tunnel consumes
|
||||
* @release_unused_bandwidth: Release all unused bandwidth
|
||||
* @reclaim_available_bandwidth: Reclaim back available bandwidth
|
||||
@ -40,6 +43,8 @@ enum tb_tunnel_type {
|
||||
* Only set if the bandwidth needs to be limited.
|
||||
* @allocated_up: Allocated upstream bandwidth (only for USB3)
|
||||
* @allocated_down: Allocated downstream bandwidth (only for USB3)
|
||||
* @bw_mode: DP bandwidth allocation mode registers can be used to
|
||||
* determine consumed and allocated bandwidth
|
||||
*/
|
||||
struct tb_tunnel {
|
||||
struct tb *tb;
|
||||
@ -50,6 +55,12 @@ struct tb_tunnel {
|
||||
int (*init)(struct tb_tunnel *tunnel);
|
||||
void (*deinit)(struct tb_tunnel *tunnel);
|
||||
int (*activate)(struct tb_tunnel *tunnel, bool activate);
|
||||
int (*maximum_bandwidth)(struct tb_tunnel *tunnel, int *max_up,
|
||||
int *max_down);
|
||||
int (*allocated_bandwidth)(struct tb_tunnel *tunnel, int *allocated_up,
|
||||
int *allocated_down);
|
||||
int (*alloc_bandwidth)(struct tb_tunnel *tunnel, int *alloc_up,
|
||||
int *alloc_down);
|
||||
int (*consumed_bandwidth)(struct tb_tunnel *tunnel, int *consumed_up,
|
||||
int *consumed_down);
|
||||
int (*release_unused_bandwidth)(struct tb_tunnel *tunnel);
|
||||
@ -62,6 +73,7 @@ struct tb_tunnel {
|
||||
int max_down;
|
||||
int allocated_up;
|
||||
int allocated_down;
|
||||
bool bw_mode;
|
||||
};
|
||||
|
||||
struct tb_tunnel *tb_tunnel_discover_pci(struct tb *tb, struct tb_port *down,
|
||||
@ -92,6 +104,12 @@ void tb_tunnel_deactivate(struct tb_tunnel *tunnel);
|
||||
bool tb_tunnel_is_invalid(struct tb_tunnel *tunnel);
|
||||
bool tb_tunnel_port_on_path(const struct tb_tunnel *tunnel,
|
||||
const struct tb_port *port);
|
||||
int tb_tunnel_maximum_bandwidth(struct tb_tunnel *tunnel, int *max_up,
|
||||
int *max_down);
|
||||
int tb_tunnel_allocated_bandwidth(struct tb_tunnel *tunnel, int *allocated_up,
|
||||
int *allocated_down);
|
||||
int tb_tunnel_alloc_bandwidth(struct tb_tunnel *tunnel, int *alloc_up,
|
||||
int *alloc_down);
|
||||
int tb_tunnel_consumed_bandwidth(struct tb_tunnel *tunnel, int *consumed_up,
|
||||
int *consumed_down);
|
||||
int tb_tunnel_release_unused_bandwidth(struct tb_tunnel *tunnel);
|
||||
|
@ -2186,3 +2186,575 @@ err_request:
|
||||
usb4_usb3_port_clear_cm_request(port);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool is_usb4_dpin(const struct tb_port *port)
|
||||
{
|
||||
if (!tb_port_is_dpin(port))
|
||||
return false;
|
||||
if (!tb_switch_is_usb4(port->sw))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* usb4_dp_port_set_cm_id() - Assign CM ID to the DP IN adapter
|
||||
* @port: DP IN adapter
|
||||
* @cm_id: CM ID to assign
|
||||
*
|
||||
* Sets CM ID for the @port. Returns %0 on success and negative errno
|
||||
* otherwise. Speficially returns %-EOPNOTSUPP if the @port does not
|
||||
* support this.
|
||||
*/
|
||||
int usb4_dp_port_set_cm_id(struct tb_port *port, int cm_id)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
if (!is_usb4_dpin(port))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ret = tb_port_read(port, &val, TB_CFG_PORT,
|
||||
port->cap_adap + ADP_DP_CS_2, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val &= ~ADP_DP_CS_2_CM_ID_MASK;
|
||||
val |= cm_id << ADP_DP_CS_2_CM_ID_SHIFT;
|
||||
|
||||
return tb_port_write(port, &val, TB_CFG_PORT,
|
||||
port->cap_adap + ADP_DP_CS_2, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb4_dp_port_bw_mode_supported() - Is the bandwidth allocation mode supported
|
||||
* @port: DP IN adapter to check
|
||||
*
|
||||
* Can be called to any DP IN adapter. Returns true if the adapter
|
||||
* supports USB4 bandwidth allocation mode, false otherwise.
|
||||
*/
|
||||
bool usb4_dp_port_bw_mode_supported(struct tb_port *port)
|
||||
{
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
if (!is_usb4_dpin(port))
|
||||
return false;
|
||||
|
||||
ret = tb_port_read(port, &val, TB_CFG_PORT,
|
||||
port->cap_adap + DP_LOCAL_CAP, 1);
|
||||
if (ret)
|
||||
return false;
|
||||
|
||||
return !!(val & DP_COMMON_CAP_BW_MODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb4_dp_port_bw_mode_enabled() - Is the bandwidth allocation mode enabled
|
||||
* @port: DP IN adapter to check
|
||||
*
|
||||
* Can be called to any DP IN adapter. Returns true if the bandwidth
|
||||
* allocation mode has been enabled, false otherwise.
|
||||
*/
|
||||
bool usb4_dp_port_bw_mode_enabled(struct tb_port *port)
|
||||
{
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
if (!is_usb4_dpin(port))
|
||||
return false;
|
||||
|
||||
ret = tb_port_read(port, &val, TB_CFG_PORT,
|
||||
port->cap_adap + ADP_DP_CS_8, 1);
|
||||
if (ret)
|
||||
return false;
|
||||
|
||||
return !!(val & ADP_DP_CS_8_DPME);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb4_dp_port_set_cm_bw_mode_supported() - Set/clear CM support for bandwidth allocation mode
|
||||
* @port: DP IN adapter
|
||||
* @supported: Does the CM support bandwidth allocation mode
|
||||
*
|
||||
* Can be called to any DP IN adapter. Sets or clears the CM support bit
|
||||
* of the DP IN adapter. Returns %0 in success and negative errno
|
||||
* otherwise. Specifically returns %-OPNOTSUPP if the passed in adapter
|
||||
* does not support this.
|
||||
*/
|
||||
int usb4_dp_port_set_cm_bw_mode_supported(struct tb_port *port, bool supported)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
if (!is_usb4_dpin(port))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ret = tb_port_read(port, &val, TB_CFG_PORT,
|
||||
port->cap_adap + ADP_DP_CS_2, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (supported)
|
||||
val |= ADP_DP_CS_2_CMMS;
|
||||
else
|
||||
val &= ~ADP_DP_CS_2_CMMS;
|
||||
|
||||
return tb_port_write(port, &val, TB_CFG_PORT,
|
||||
port->cap_adap + ADP_DP_CS_2, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb4_dp_port_group_id() - Return Group ID assigned for the adapter
|
||||
* @port: DP IN adapter
|
||||
*
|
||||
* Reads bandwidth allocation Group ID from the DP IN adapter and
|
||||
* returns it. If the adapter does not support setting Group_ID
|
||||
* %-EOPNOTSUPP is returned.
|
||||
*/
|
||||
int usb4_dp_port_group_id(struct tb_port *port)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
if (!is_usb4_dpin(port))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ret = tb_port_read(port, &val, TB_CFG_PORT,
|
||||
port->cap_adap + ADP_DP_CS_2, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return (val & ADP_DP_CS_2_GROUP_ID_MASK) >> ADP_DP_CS_2_GROUP_ID_SHIFT;
|
||||
}
|
||||
|
||||
/**
|
||||
* usb4_dp_port_set_group_id() - Set adapter Group ID
|
||||
* @port: DP IN adapter
|
||||
* @group_id: Group ID for the adapter
|
||||
*
|
||||
* Sets bandwidth allocation mode Group ID for the DP IN adapter.
|
||||
* Returns %0 in case of success and negative errno otherwise.
|
||||
* Specifically returns %-EOPNOTSUPP if the adapter does not support
|
||||
* this.
|
||||
*/
|
||||
int usb4_dp_port_set_group_id(struct tb_port *port, int group_id)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
if (!is_usb4_dpin(port))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ret = tb_port_read(port, &val, TB_CFG_PORT,
|
||||
port->cap_adap + ADP_DP_CS_2, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val &= ~ADP_DP_CS_2_GROUP_ID_MASK;
|
||||
val |= group_id << ADP_DP_CS_2_GROUP_ID_SHIFT;
|
||||
|
||||
return tb_port_write(port, &val, TB_CFG_PORT,
|
||||
port->cap_adap + ADP_DP_CS_2, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb4_dp_port_nrd() - Read non-reduced rate and lanes
|
||||
* @port: DP IN adapter
|
||||
* @rate: Non-reduced rate in Mb/s is placed here
|
||||
* @lanes: Non-reduced lanes are placed here
|
||||
*
|
||||
* Reads the non-reduced rate and lanes from the DP IN adapter. Returns
|
||||
* %0 in success and negative errno otherwise. Specifically returns
|
||||
* %-EOPNOTSUPP if the adapter does not support this.
|
||||
*/
|
||||
int usb4_dp_port_nrd(struct tb_port *port, int *rate, int *lanes)
|
||||
{
|
||||
u32 val, tmp;
|
||||
int ret;
|
||||
|
||||
if (!is_usb4_dpin(port))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ret = tb_port_read(port, &val, TB_CFG_PORT,
|
||||
port->cap_adap + ADP_DP_CS_2, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
tmp = (val & ADP_DP_CS_2_NRD_MLR_MASK) >> ADP_DP_CS_2_NRD_MLR_SHIFT;
|
||||
switch (tmp) {
|
||||
case DP_COMMON_CAP_RATE_RBR:
|
||||
*rate = 1620;
|
||||
break;
|
||||
case DP_COMMON_CAP_RATE_HBR:
|
||||
*rate = 2700;
|
||||
break;
|
||||
case DP_COMMON_CAP_RATE_HBR2:
|
||||
*rate = 5400;
|
||||
break;
|
||||
case DP_COMMON_CAP_RATE_HBR3:
|
||||
*rate = 8100;
|
||||
break;
|
||||
}
|
||||
|
||||
tmp = val & ADP_DP_CS_2_NRD_MLC_MASK;
|
||||
switch (tmp) {
|
||||
case DP_COMMON_CAP_1_LANE:
|
||||
*lanes = 1;
|
||||
break;
|
||||
case DP_COMMON_CAP_2_LANES:
|
||||
*lanes = 2;
|
||||
break;
|
||||
case DP_COMMON_CAP_4_LANES:
|
||||
*lanes = 4;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* usb4_dp_port_set_nrd() - Set non-reduced rate and lanes
|
||||
* @port: DP IN adapter
|
||||
* @rate: Non-reduced rate in Mb/s
|
||||
* @lanes: Non-reduced lanes
|
||||
*
|
||||
* Before the capabilities reduction this function can be used to set
|
||||
* the non-reduced values for the DP IN adapter. Returns %0 in success
|
||||
* and negative errno otherwise. If the adapter does not support this
|
||||
* %-EOPNOTSUPP is returned.
|
||||
*/
|
||||
int usb4_dp_port_set_nrd(struct tb_port *port, int rate, int lanes)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
if (!is_usb4_dpin(port))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ret = tb_port_read(port, &val, TB_CFG_PORT,
|
||||
port->cap_adap + ADP_DP_CS_2, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val &= ~ADP_DP_CS_2_NRD_MLR_MASK;
|
||||
|
||||
switch (rate) {
|
||||
case 1620:
|
||||
break;
|
||||
case 2700:
|
||||
val |= (DP_COMMON_CAP_RATE_HBR << ADP_DP_CS_2_NRD_MLR_SHIFT)
|
||||
& ADP_DP_CS_2_NRD_MLR_MASK;
|
||||
break;
|
||||
case 5400:
|
||||
val |= (DP_COMMON_CAP_RATE_HBR2 << ADP_DP_CS_2_NRD_MLR_SHIFT)
|
||||
& ADP_DP_CS_2_NRD_MLR_MASK;
|
||||
break;
|
||||
case 8100:
|
||||
val |= (DP_COMMON_CAP_RATE_HBR3 << ADP_DP_CS_2_NRD_MLR_SHIFT)
|
||||
& ADP_DP_CS_2_NRD_MLR_MASK;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
val &= ~ADP_DP_CS_2_NRD_MLC_MASK;
|
||||
|
||||
switch (lanes) {
|
||||
case 1:
|
||||
break;
|
||||
case 2:
|
||||
val |= DP_COMMON_CAP_2_LANES;
|
||||
break;
|
||||
case 4:
|
||||
val |= DP_COMMON_CAP_4_LANES;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return tb_port_write(port, &val, TB_CFG_PORT,
|
||||
port->cap_adap + ADP_DP_CS_2, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb4_dp_port_granularity() - Return granularity for the bandwidth values
|
||||
* @port: DP IN adapter
|
||||
*
|
||||
* Reads the programmed granularity from @port. If the DP IN adapter does
|
||||
* not support bandwidth allocation mode returns %-EOPNOTSUPP and negative
|
||||
* errno in other error cases.
|
||||
*/
|
||||
int usb4_dp_port_granularity(struct tb_port *port)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
if (!is_usb4_dpin(port))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ret = tb_port_read(port, &val, TB_CFG_PORT,
|
||||
port->cap_adap + ADP_DP_CS_2, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val &= ADP_DP_CS_2_GR_MASK;
|
||||
val >>= ADP_DP_CS_2_GR_SHIFT;
|
||||
|
||||
switch (val) {
|
||||
case ADP_DP_CS_2_GR_0_25G:
|
||||
return 250;
|
||||
case ADP_DP_CS_2_GR_0_5G:
|
||||
return 500;
|
||||
case ADP_DP_CS_2_GR_1G:
|
||||
return 1000;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* usb4_dp_port_set_granularity() - Set granularity for the bandwidth values
|
||||
* @port: DP IN adapter
|
||||
* @granularity: Granularity in Mb/s. Supported values: 1000, 500 and 250.
|
||||
*
|
||||
* Sets the granularity used with the estimated, allocated and requested
|
||||
* bandwidth. Returns %0 in success and negative errno otherwise. If the
|
||||
* adapter does not support this %-EOPNOTSUPP is returned.
|
||||
*/
|
||||
int usb4_dp_port_set_granularity(struct tb_port *port, int granularity)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
if (!is_usb4_dpin(port))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ret = tb_port_read(port, &val, TB_CFG_PORT,
|
||||
port->cap_adap + ADP_DP_CS_2, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val &= ~ADP_DP_CS_2_GR_MASK;
|
||||
|
||||
switch (granularity) {
|
||||
case 250:
|
||||
val |= ADP_DP_CS_2_GR_0_25G << ADP_DP_CS_2_GR_SHIFT;
|
||||
break;
|
||||
case 500:
|
||||
val |= ADP_DP_CS_2_GR_0_5G << ADP_DP_CS_2_GR_SHIFT;
|
||||
break;
|
||||
case 1000:
|
||||
val |= ADP_DP_CS_2_GR_1G << ADP_DP_CS_2_GR_SHIFT;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return tb_port_write(port, &val, TB_CFG_PORT,
|
||||
port->cap_adap + ADP_DP_CS_2, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb4_dp_port_set_estimated_bw() - Set estimated bandwidth
|
||||
* @port: DP IN adapter
|
||||
* @bw: Estimated bandwidth in Mb/s.
|
||||
*
|
||||
* Sets the estimated bandwidth to @bw. Set the granularity by calling
|
||||
* usb4_dp_port_set_granularity() before calling this. The @bw is round
|
||||
* down to the closest granularity multiplier. Returns %0 in success
|
||||
* and negative errno otherwise. Specifically returns %-EOPNOTSUPP if
|
||||
* the adapter does not support this.
|
||||
*/
|
||||
int usb4_dp_port_set_estimated_bw(struct tb_port *port, int bw)
|
||||
{
|
||||
u32 val, granularity;
|
||||
int ret;
|
||||
|
||||
if (!is_usb4_dpin(port))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ret = usb4_dp_port_granularity(port);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
granularity = ret;
|
||||
|
||||
ret = tb_port_read(port, &val, TB_CFG_PORT,
|
||||
port->cap_adap + ADP_DP_CS_2, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val &= ~ADP_DP_CS_2_ESTIMATED_BW_MASK;
|
||||
val |= (bw / granularity) << ADP_DP_CS_2_ESTIMATED_BW_SHIFT;
|
||||
|
||||
return tb_port_write(port, &val, TB_CFG_PORT,
|
||||
port->cap_adap + ADP_DP_CS_2, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb4_dp_port_allocated_bw() - Return allocated bandwidth
|
||||
* @port: DP IN adapter
|
||||
*
|
||||
* Reads and returns allocated bandwidth for @port in Mb/s (taking into
|
||||
* account the programmed granularity). Returns negative errno in case
|
||||
* of error.
|
||||
*/
|
||||
int usb4_dp_port_allocated_bw(struct tb_port *port)
|
||||
{
|
||||
u32 val, granularity;
|
||||
int ret;
|
||||
|
||||
if (!is_usb4_dpin(port))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ret = usb4_dp_port_granularity(port);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
granularity = ret;
|
||||
|
||||
ret = tb_port_read(port, &val, TB_CFG_PORT,
|
||||
port->cap_adap + DP_STATUS, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val &= DP_STATUS_ALLOCATED_BW_MASK;
|
||||
val >>= DP_STATUS_ALLOCATED_BW_SHIFT;
|
||||
|
||||
return val * granularity;
|
||||
}
|
||||
|
||||
static int __usb4_dp_port_set_cm_ack(struct tb_port *port, bool ack)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
ret = tb_port_read(port, &val, TB_CFG_PORT,
|
||||
port->cap_adap + ADP_DP_CS_2, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (ack)
|
||||
val |= ADP_DP_CS_2_CA;
|
||||
else
|
||||
val &= ~ADP_DP_CS_2_CA;
|
||||
|
||||
return tb_port_write(port, &val, TB_CFG_PORT,
|
||||
port->cap_adap + ADP_DP_CS_2, 1);
|
||||
}
|
||||
|
||||
static inline int usb4_dp_port_set_cm_ack(struct tb_port *port)
|
||||
{
|
||||
return __usb4_dp_port_set_cm_ack(port, true);
|
||||
}
|
||||
|
||||
static int usb4_dp_port_wait_and_clear_cm_ack(struct tb_port *port,
|
||||
int timeout_msec)
|
||||
{
|
||||
ktime_t end;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
ret = __usb4_dp_port_set_cm_ack(port, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
end = ktime_add_ms(ktime_get(), timeout_msec);
|
||||
do {
|
||||
ret = tb_port_read(port, &val, TB_CFG_PORT,
|
||||
port->cap_adap + ADP_DP_CS_8, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!(val & ADP_DP_CS_8_DR))
|
||||
break;
|
||||
|
||||
usleep_range(50, 100);
|
||||
} while (ktime_before(ktime_get(), end));
|
||||
|
||||
if (val & ADP_DP_CS_8_DR)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
ret = tb_port_read(port, &val, TB_CFG_PORT,
|
||||
port->cap_adap + ADP_DP_CS_2, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val &= ~ADP_DP_CS_2_CA;
|
||||
return tb_port_write(port, &val, TB_CFG_PORT,
|
||||
port->cap_adap + ADP_DP_CS_2, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb4_dp_port_allocate_bw() - Set allocated bandwidth
|
||||
* @port: DP IN adapter
|
||||
* @bw: New allocated bandwidth in Mb/s
|
||||
*
|
||||
* Communicates the new allocated bandwidth with the DPCD (graphics
|
||||
* driver). Takes into account the programmed granularity. Returns %0 in
|
||||
* success and negative errno in case of error.
|
||||
*/
|
||||
int usb4_dp_port_allocate_bw(struct tb_port *port, int bw)
|
||||
{
|
||||
u32 val, granularity;
|
||||
int ret;
|
||||
|
||||
if (!is_usb4_dpin(port))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ret = usb4_dp_port_granularity(port);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
granularity = ret;
|
||||
|
||||
ret = tb_port_read(port, &val, TB_CFG_PORT,
|
||||
port->cap_adap + DP_STATUS, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val &= ~DP_STATUS_ALLOCATED_BW_MASK;
|
||||
val |= (bw / granularity) << DP_STATUS_ALLOCATED_BW_SHIFT;
|
||||
|
||||
ret = tb_port_write(port, &val, TB_CFG_PORT,
|
||||
port->cap_adap + DP_STATUS, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = usb4_dp_port_set_cm_ack(port);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return usb4_dp_port_wait_and_clear_cm_ack(port, 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb4_dp_port_requested_bw() - Read requested bandwidth
|
||||
* @port: DP IN adapter
|
||||
*
|
||||
* Reads the DPCD (graphics driver) requested bandwidth and returns it
|
||||
* in Mb/s. Takes the programmed granularity into account. In case of
|
||||
* error returns negative errno. Specifically returns %-EOPNOTSUPP if
|
||||
* the adapter does not support bandwidth allocation mode, and %ENODATA
|
||||
* if there is no active bandwidth request from the graphics driver.
|
||||
*/
|
||||
int usb4_dp_port_requested_bw(struct tb_port *port)
|
||||
{
|
||||
u32 val, granularity;
|
||||
int ret;
|
||||
|
||||
if (!is_usb4_dpin(port))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ret = usb4_dp_port_granularity(port);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
granularity = ret;
|
||||
|
||||
ret = tb_port_read(port, &val, TB_CFG_PORT,
|
||||
port->cap_adap + ADP_DP_CS_8, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!(val & ADP_DP_CS_8_DR))
|
||||
return -ENODATA;
|
||||
|
||||
return (val & ADP_DP_CS_8_REQUESTED_BW_MASK) * granularity;
|
||||
}
|
||||
|
@ -378,7 +378,7 @@ int cdnsp_ep_enqueue(struct cdnsp_ep *pep, struct cdnsp_request *preq)
|
||||
ret = cdnsp_queue_bulk_tx(pdev, preq);
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
ret = cdnsp_queue_isoc_tx_prepare(pdev, preq);
|
||||
ret = cdnsp_queue_isoc_tx(pdev, preq);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
|
@ -1532,8 +1532,8 @@ void cdnsp_queue_stop_endpoint(struct cdnsp_device *pdev,
|
||||
unsigned int ep_index);
|
||||
int cdnsp_queue_ctrl_tx(struct cdnsp_device *pdev, struct cdnsp_request *preq);
|
||||
int cdnsp_queue_bulk_tx(struct cdnsp_device *pdev, struct cdnsp_request *preq);
|
||||
int cdnsp_queue_isoc_tx_prepare(struct cdnsp_device *pdev,
|
||||
struct cdnsp_request *preq);
|
||||
int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev,
|
||||
struct cdnsp_request *preq);
|
||||
void cdnsp_queue_configure_endpoint(struct cdnsp_device *pdev,
|
||||
dma_addr_t in_ctx_ptr);
|
||||
void cdnsp_queue_reset_ep(struct cdnsp_device *pdev, unsigned int ep_index);
|
||||
|
@ -1333,6 +1333,20 @@ static int cdnsp_handle_tx_event(struct cdnsp_device *pdev,
|
||||
ep_ring->dequeue, td->last_trb,
|
||||
ep_trb_dma);
|
||||
|
||||
desc = td->preq->pep->endpoint.desc;
|
||||
|
||||
if (ep_seg) {
|
||||
ep_trb = &ep_seg->trbs[(ep_trb_dma - ep_seg->dma)
|
||||
/ sizeof(*ep_trb)];
|
||||
|
||||
trace_cdnsp_handle_transfer(ep_ring,
|
||||
(struct cdnsp_generic_trb *)ep_trb);
|
||||
|
||||
if (pep->skip && usb_endpoint_xfer_isoc(desc) &&
|
||||
td->last_trb != ep_trb)
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/*
|
||||
* Skip the Force Stopped Event. The event_trb(ep_trb_dma)
|
||||
* of FSE is not in the current TD pointed by ep_ring->dequeue
|
||||
@ -1347,7 +1361,6 @@ static int cdnsp_handle_tx_event(struct cdnsp_device *pdev,
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
desc = td->preq->pep->endpoint.desc;
|
||||
if (!ep_seg) {
|
||||
if (!pep->skip || !usb_endpoint_xfer_isoc(desc)) {
|
||||
/* Something is busted, give up! */
|
||||
@ -1374,12 +1387,6 @@ static int cdnsp_handle_tx_event(struct cdnsp_device *pdev,
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ep_trb = &ep_seg->trbs[(ep_trb_dma - ep_seg->dma)
|
||||
/ sizeof(*ep_trb)];
|
||||
|
||||
trace_cdnsp_handle_transfer(ep_ring,
|
||||
(struct cdnsp_generic_trb *)ep_trb);
|
||||
|
||||
if (cdnsp_trb_is_noop(ep_trb))
|
||||
goto cleanup;
|
||||
|
||||
@ -1726,11 +1733,6 @@ static unsigned int count_sg_trbs_needed(struct cdnsp_request *preq)
|
||||
return num_trbs;
|
||||
}
|
||||
|
||||
static unsigned int count_isoc_trbs_needed(struct cdnsp_request *preq)
|
||||
{
|
||||
return cdnsp_count_trbs(preq->request.dma, preq->request.length);
|
||||
}
|
||||
|
||||
static void cdnsp_check_trb_math(struct cdnsp_request *preq, int running_total)
|
||||
{
|
||||
if (running_total != preq->request.length)
|
||||
@ -2192,28 +2194,48 @@ static unsigned int
|
||||
}
|
||||
|
||||
/* Queue function isoc transfer */
|
||||
static int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev,
|
||||
struct cdnsp_request *preq)
|
||||
int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev,
|
||||
struct cdnsp_request *preq)
|
||||
{
|
||||
int trb_buff_len, td_len, td_remain_len, ret;
|
||||
unsigned int trb_buff_len, td_len, td_remain_len, block_len;
|
||||
unsigned int burst_count, last_burst_pkt;
|
||||
unsigned int total_pkt_count, max_pkt;
|
||||
struct cdnsp_generic_trb *start_trb;
|
||||
struct scatterlist *sg = NULL;
|
||||
bool more_trbs_coming = true;
|
||||
struct cdnsp_ring *ep_ring;
|
||||
unsigned int num_sgs = 0;
|
||||
int running_total = 0;
|
||||
u32 field, length_field;
|
||||
u64 addr, send_addr;
|
||||
int start_cycle;
|
||||
int trbs_per_td;
|
||||
u64 addr;
|
||||
int i;
|
||||
int i, sent_len, ret;
|
||||
|
||||
ep_ring = preq->pep->ring;
|
||||
|
||||
td_len = preq->request.length;
|
||||
|
||||
if (preq->request.num_sgs) {
|
||||
num_sgs = preq->request.num_sgs;
|
||||
sg = preq->request.sg;
|
||||
addr = (u64)sg_dma_address(sg);
|
||||
block_len = sg_dma_len(sg);
|
||||
trbs_per_td = count_sg_trbs_needed(preq);
|
||||
} else {
|
||||
addr = (u64)preq->request.dma;
|
||||
block_len = td_len;
|
||||
trbs_per_td = count_trbs_needed(preq);
|
||||
}
|
||||
|
||||
ret = cdnsp_prepare_transfer(pdev, preq, trbs_per_td);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
start_trb = &ep_ring->enqueue->generic;
|
||||
start_cycle = ep_ring->cycle_state;
|
||||
td_len = preq->request.length;
|
||||
addr = (u64)preq->request.dma;
|
||||
td_remain_len = td_len;
|
||||
send_addr = addr;
|
||||
|
||||
max_pkt = usb_endpoint_maxp(preq->pep->endpoint.desc);
|
||||
total_pkt_count = DIV_ROUND_UP(td_len, max_pkt);
|
||||
@ -2225,11 +2247,6 @@ static int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev,
|
||||
burst_count = cdnsp_get_burst_count(pdev, preq, total_pkt_count);
|
||||
last_burst_pkt = cdnsp_get_last_burst_packet_count(pdev, preq,
|
||||
total_pkt_count);
|
||||
trbs_per_td = count_isoc_trbs_needed(preq);
|
||||
|
||||
ret = cdnsp_prepare_transfer(pdev, preq, trbs_per_td);
|
||||
if (ret)
|
||||
goto cleanup;
|
||||
|
||||
/*
|
||||
* Set isoc specific data for the first TRB in a TD.
|
||||
@ -2248,6 +2265,7 @@ static int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev,
|
||||
|
||||
/* Calculate TRB length. */
|
||||
trb_buff_len = TRB_BUFF_LEN_UP_TO_BOUNDARY(addr);
|
||||
trb_buff_len = min(trb_buff_len, block_len);
|
||||
if (trb_buff_len > td_remain_len)
|
||||
trb_buff_len = td_remain_len;
|
||||
|
||||
@ -2256,7 +2274,8 @@ static int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev,
|
||||
trb_buff_len, td_len, preq,
|
||||
more_trbs_coming, 0);
|
||||
|
||||
length_field = TRB_LEN(trb_buff_len) | TRB_INTR_TARGET(0);
|
||||
length_field = TRB_LEN(trb_buff_len) | TRB_TD_SIZE(remainder) |
|
||||
TRB_INTR_TARGET(0);
|
||||
|
||||
/* Only first TRB is isoc, overwrite otherwise. */
|
||||
if (i) {
|
||||
@ -2281,12 +2300,27 @@ static int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev,
|
||||
}
|
||||
|
||||
cdnsp_queue_trb(pdev, ep_ring, more_trbs_coming,
|
||||
lower_32_bits(addr), upper_32_bits(addr),
|
||||
lower_32_bits(send_addr), upper_32_bits(send_addr),
|
||||
length_field, field);
|
||||
|
||||
running_total += trb_buff_len;
|
||||
addr += trb_buff_len;
|
||||
td_remain_len -= trb_buff_len;
|
||||
|
||||
sent_len = trb_buff_len;
|
||||
while (sg && sent_len >= block_len) {
|
||||
/* New sg entry */
|
||||
--num_sgs;
|
||||
sent_len -= block_len;
|
||||
if (num_sgs != 0) {
|
||||
sg = sg_next(sg);
|
||||
block_len = sg_dma_len(sg);
|
||||
addr = (u64)sg_dma_address(sg);
|
||||
addr += sent_len;
|
||||
}
|
||||
}
|
||||
block_len -= sent_len;
|
||||
send_addr = addr;
|
||||
}
|
||||
|
||||
/* Check TD length */
|
||||
@ -2324,30 +2358,6 @@ cleanup:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cdnsp_queue_isoc_tx_prepare(struct cdnsp_device *pdev,
|
||||
struct cdnsp_request *preq)
|
||||
{
|
||||
struct cdnsp_ring *ep_ring;
|
||||
u32 ep_state;
|
||||
int num_trbs;
|
||||
int ret;
|
||||
|
||||
ep_ring = preq->pep->ring;
|
||||
ep_state = GET_EP_CTX_STATE(preq->pep->out_ctx);
|
||||
num_trbs = count_isoc_trbs_needed(preq);
|
||||
|
||||
/*
|
||||
* Check the ring to guarantee there is enough room for the whole
|
||||
* request. Do not insert any td of the USB Request to the ring if the
|
||||
* check failed.
|
||||
*/
|
||||
ret = cdnsp_prepare_ring(pdev, ep_ring, ep_state, num_trbs, GFP_ATOMIC);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return cdnsp_queue_isoc_tx(pdev, preq);
|
||||
}
|
||||
|
||||
/**** Command Ring Operations ****/
|
||||
/*
|
||||
* Generic function for queuing a command TRB on the command ring.
|
||||
|
@ -413,15 +413,19 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
|
||||
data->phy = devm_usb_get_phy_by_phandle(dev, "fsl,usbphy", 0);
|
||||
if (IS_ERR(data->phy)) {
|
||||
ret = PTR_ERR(data->phy);
|
||||
if (ret != -ENODEV)
|
||||
if (ret != -ENODEV) {
|
||||
dev_err_probe(dev, ret, "Failed to parse fsl,usbphy\n");
|
||||
goto err_clk;
|
||||
}
|
||||
data->phy = devm_usb_get_phy_by_phandle(dev, "phys", 0);
|
||||
if (IS_ERR(data->phy)) {
|
||||
ret = PTR_ERR(data->phy);
|
||||
if (ret == -ENODEV)
|
||||
if (ret == -ENODEV) {
|
||||
data->phy = NULL;
|
||||
else
|
||||
} else {
|
||||
dev_err_probe(dev, ret, "Failed to parse phys\n");
|
||||
goto err_clk;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -364,5 +364,5 @@ void dbg_create_files(struct ci_hdrc *ci)
|
||||
*/
|
||||
void dbg_remove_files(struct ci_hdrc *ci)
|
||||
{
|
||||
debugfs_remove(debugfs_lookup(dev_name(ci->dev), usb_debug_root));
|
||||
debugfs_lookup_and_remove(dev_name(ci->dev), usb_debug_root);
|
||||
}
|
||||
|
@ -1263,14 +1263,8 @@ static int usbmisc_imx_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usbmisc_imx_remove(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver usbmisc_imx_driver = {
|
||||
.probe = usbmisc_imx_probe,
|
||||
.remove = usbmisc_imx_remove,
|
||||
.driver = {
|
||||
.name = "usbmisc_imx",
|
||||
.of_match_table = usbmisc_imx_dt_ids,
|
||||
|
@ -271,7 +271,7 @@ static int ulpi_regs_show(struct seq_file *seq, void *data)
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(ulpi_regs);
|
||||
|
||||
#define ULPI_ROOT debugfs_lookup(KBUILD_MODNAME, NULL)
|
||||
static struct dentry *ulpi_root;
|
||||
|
||||
static int ulpi_register(struct device *dev, struct ulpi *ulpi)
|
||||
{
|
||||
@ -301,7 +301,7 @@ static int ulpi_register(struct device *dev, struct ulpi *ulpi)
|
||||
return ret;
|
||||
}
|
||||
|
||||
root = debugfs_create_dir(dev_name(dev), ULPI_ROOT);
|
||||
root = debugfs_create_dir(dev_name(dev), ulpi_root);
|
||||
debugfs_create_file("regs", 0444, root, ulpi, &ulpi_regs_fops);
|
||||
|
||||
dev_dbg(&ulpi->dev, "registered ULPI PHY: vendor %04x, product %04x\n",
|
||||
@ -349,8 +349,7 @@ EXPORT_SYMBOL_GPL(ulpi_register_interface);
|
||||
*/
|
||||
void ulpi_unregister_interface(struct ulpi *ulpi)
|
||||
{
|
||||
debugfs_remove_recursive(debugfs_lookup(dev_name(&ulpi->dev),
|
||||
ULPI_ROOT));
|
||||
debugfs_lookup_and_remove(dev_name(&ulpi->dev), ulpi_root);
|
||||
device_unregister(&ulpi->dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ulpi_unregister_interface);
|
||||
@ -360,12 +359,11 @@ EXPORT_SYMBOL_GPL(ulpi_unregister_interface);
|
||||
static int __init ulpi_init(void)
|
||||
{
|
||||
int ret;
|
||||
struct dentry *root;
|
||||
|
||||
root = debugfs_create_dir(KBUILD_MODNAME, NULL);
|
||||
ulpi_root = debugfs_create_dir(KBUILD_MODNAME, NULL);
|
||||
ret = bus_register(&ulpi_bus);
|
||||
if (ret)
|
||||
debugfs_remove(root);
|
||||
debugfs_remove(ulpi_root);
|
||||
return ret;
|
||||
}
|
||||
subsys_initcall(ulpi_init);
|
||||
@ -373,7 +371,7 @@ subsys_initcall(ulpi_init);
|
||||
static void __exit ulpi_exit(void)
|
||||
{
|
||||
bus_unregister(&ulpi_bus);
|
||||
debugfs_remove_recursive(ULPI_ROOT);
|
||||
debugfs_remove(ulpi_root);
|
||||
}
|
||||
module_exit(ulpi_exit);
|
||||
|
||||
|
@ -2389,9 +2389,8 @@ static int usb_enumerate_device_otg(struct usb_device *udev)
|
||||
* usb_enumerate_device - Read device configs/intfs/otg (usbcore-internal)
|
||||
* @udev: newly addressed device (in ADDRESS state)
|
||||
*
|
||||
* This is only called by usb_new_device() and usb_authorize_device()
|
||||
* and FIXME -- all comments that apply to them apply here wrt to
|
||||
* environment.
|
||||
* This is only called by usb_new_device() -- all comments that apply there
|
||||
* apply here wrt to environment.
|
||||
*
|
||||
* If the device is WUSB and not authorized, we don't attempt to read
|
||||
* the string descriptors, as they will be errored out by the device
|
||||
|
@ -869,11 +869,7 @@ read_descriptors(struct file *filp, struct kobject *kobj,
|
||||
size_t srclen, n;
|
||||
int cfgno;
|
||||
void *src;
|
||||
int retval;
|
||||
|
||||
retval = usb_lock_device_interruptible(udev);
|
||||
if (retval < 0)
|
||||
return -EINTR;
|
||||
/* The binary attribute begins with the device descriptor.
|
||||
* Following that are the raw descriptor entries for all the
|
||||
* configurations (config plus subsidiary descriptors).
|
||||
@ -898,7 +894,6 @@ read_descriptors(struct file *filp, struct kobject *kobj,
|
||||
off -= srclen;
|
||||
}
|
||||
}
|
||||
usb_unlock_device(udev);
|
||||
return count - nleft;
|
||||
}
|
||||
|
||||
|
@ -998,7 +998,7 @@ static void usb_debugfs_init(void)
|
||||
|
||||
static void usb_debugfs_cleanup(void)
|
||||
{
|
||||
debugfs_remove(debugfs_lookup("devices", usb_debug_root));
|
||||
debugfs_lookup_and_remove("devices", usb_debug_root);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1117,6 +1117,7 @@ struct dwc3_scratchpad_array {
|
||||
* address.
|
||||
* @num_ep_resized: carries the current number endpoints which have had its tx
|
||||
* fifo resized.
|
||||
* @debug_root: root debugfs directory for this device to put its files in.
|
||||
*/
|
||||
struct dwc3 {
|
||||
struct work_struct drd_work;
|
||||
@ -1332,6 +1333,7 @@ struct dwc3 {
|
||||
int max_cfg_eps;
|
||||
int last_fifo_depth;
|
||||
int num_ep_resized;
|
||||
struct dentry *debug_root;
|
||||
};
|
||||
|
||||
#define INCRX_BURST_MODE 0
|
||||
|
@ -414,11 +414,14 @@ static inline const char *dwc3_gadget_generic_cmd_status_string(int status)
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
extern void dwc3_debugfs_create_endpoint_dir(struct dwc3_ep *dep);
|
||||
extern void dwc3_debugfs_remove_endpoint_dir(struct dwc3_ep *dep);
|
||||
extern void dwc3_debugfs_init(struct dwc3 *d);
|
||||
extern void dwc3_debugfs_exit(struct dwc3 *d);
|
||||
#else
|
||||
static inline void dwc3_debugfs_create_endpoint_dir(struct dwc3_ep *dep)
|
||||
{ }
|
||||
static inline void dwc3_debugfs_remove_endpoint_dir(struct dwc3_ep *dep)
|
||||
{ }
|
||||
static inline void dwc3_debugfs_init(struct dwc3 *d)
|
||||
{ }
|
||||
static inline void dwc3_debugfs_exit(struct dwc3 *d)
|
||||
|
@ -873,27 +873,23 @@ static const struct dwc3_ep_file_map dwc3_ep_file_map[] = {
|
||||
{ "GDBGEPINFO", &dwc3_ep_info_register_fops, },
|
||||
};
|
||||
|
||||
static void dwc3_debugfs_create_endpoint_files(struct dwc3_ep *dep,
|
||||
struct dentry *parent)
|
||||
void dwc3_debugfs_create_endpoint_dir(struct dwc3_ep *dep)
|
||||
{
|
||||
struct dentry *dir;
|
||||
int i;
|
||||
|
||||
dir = debugfs_create_dir(dep->name, dep->dwc->debug_root);
|
||||
for (i = 0; i < ARRAY_SIZE(dwc3_ep_file_map); i++) {
|
||||
const struct file_operations *fops = dwc3_ep_file_map[i].fops;
|
||||
const char *name = dwc3_ep_file_map[i].name;
|
||||
|
||||
debugfs_create_file(name, 0444, parent, dep, fops);
|
||||
debugfs_create_file(name, 0444, dir, dep, fops);
|
||||
}
|
||||
}
|
||||
|
||||
void dwc3_debugfs_create_endpoint_dir(struct dwc3_ep *dep)
|
||||
void dwc3_debugfs_remove_endpoint_dir(struct dwc3_ep *dep)
|
||||
{
|
||||
struct dentry *dir;
|
||||
struct dentry *root;
|
||||
|
||||
root = debugfs_lookup(dev_name(dep->dwc->dev), usb_debug_root);
|
||||
dir = debugfs_create_dir(dep->name, root);
|
||||
dwc3_debugfs_create_endpoint_files(dep, dir);
|
||||
debugfs_lookup_and_remove(dep->name, dep->dwc->debug_root);
|
||||
}
|
||||
|
||||
void dwc3_debugfs_init(struct dwc3 *dwc)
|
||||
@ -911,6 +907,7 @@ void dwc3_debugfs_init(struct dwc3 *dwc)
|
||||
dwc->regset->base = dwc->regs - DWC3_GLOBALS_REGS_START;
|
||||
|
||||
root = debugfs_create_dir(dev_name(dwc->dev), usb_debug_root);
|
||||
dwc->debug_root = root;
|
||||
debugfs_create_regset32("regdump", 0444, root, dwc->regset);
|
||||
debugfs_create_file("lsp_dump", 0644, root, dwc, &dwc3_lsp_fops);
|
||||
|
||||
@ -929,6 +926,6 @@ void dwc3_debugfs_init(struct dwc3 *dwc)
|
||||
|
||||
void dwc3_debugfs_exit(struct dwc3 *dwc)
|
||||
{
|
||||
debugfs_remove(debugfs_lookup(dev_name(dwc->dev), usb_debug_root));
|
||||
debugfs_lookup_and_remove(dev_name(dwc->dev), usb_debug_root);
|
||||
kfree(dwc->regset);
|
||||
}
|
||||
|
@ -47,6 +47,7 @@
|
||||
#define PCI_DEVICE_ID_INTEL_ADLS 0x7ae1
|
||||
#define PCI_DEVICE_ID_INTEL_RPL 0xa70e
|
||||
#define PCI_DEVICE_ID_INTEL_RPLS 0x7a61
|
||||
#define PCI_DEVICE_ID_INTEL_MTLM 0x7eb1
|
||||
#define PCI_DEVICE_ID_INTEL_MTLP 0x7ec1
|
||||
#define PCI_DEVICE_ID_INTEL_MTL 0x7e7e
|
||||
#define PCI_DEVICE_ID_INTEL_TGL 0x9a15
|
||||
@ -467,6 +468,9 @@ static const struct pci_device_id dwc3_pci_id_table[] = {
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_RPLS),
|
||||
(kernel_ulong_t) &dwc3_pci_intel_swnode, },
|
||||
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_MTLM),
|
||||
(kernel_ulong_t) &dwc3_pci_intel_swnode, },
|
||||
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_MTLP),
|
||||
(kernel_ulong_t) &dwc3_pci_intel_swnode, },
|
||||
|
||||
|
@ -14,7 +14,6 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/reset.h>
|
||||
|
@ -3194,9 +3194,7 @@ static void dwc3_gadget_free_endpoints(struct dwc3 *dwc)
|
||||
list_del(&dep->endpoint.ep_list);
|
||||
}
|
||||
|
||||
debugfs_remove_recursive(debugfs_lookup(dep->name,
|
||||
debugfs_lookup(dev_name(dep->dwc->dev),
|
||||
usb_debug_root)));
|
||||
dwc3_debugfs_remove_endpoint_dir(dep);
|
||||
kfree(dep);
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user