sound updates for 5.3
Many updates in this development cycle are found in ASoC where it got a wide range of changes for the continued refactoring. Some highlights are below. ASoC: * Continued refactoring work by Morimoto-san toward the full componentization; the changes are seen allover the places * Support for force disconnecting muxes in DAPM * Continued development of ASoC Intel SOF stuff * New drivers for Cirrus Logic CS47L35, CS47L85 and CS47L90, Conexant CX2072X, Realtek RT1011 and RT1308 HD-audio: * More fixes and adjustments for ASoC SOF HD-audio * Fix for resume problem on some Realtek codecs USB-audio: * A few fixes for the issues reported by syzbot USB fuzzer * Fix for UAC2 extension unit parser * Quirks for Line6 Helix, Emgaic Unitor 8 FireWire: * Lots of code refactoring and fixes in most of its components -----BEGIN PGP SIGNATURE----- iQJCBAABCAAsFiEEIXTw5fNLNI7mMiVaLtJE4w1nLE8FAl0kp+oOHHRpd2FpQHN1 c2UuZGUACgkQLtJE4w1nLE902A/+ISwG/QtN41cnJxBaEMMQryJ9jDG2ux89NSST TFbmXuJj3CRsBoQ4gkqgRGtEvxf8DK5BohEl8AarxYaTrtZALEjLJCm9oQ5YUF1X Vkwqw9cuj+lptZmB6j/t4f352edsvuf4ZyG6plb331xbzJzzh06s0PpUyIispdMH hmxBPrKuWl1pPtpRaLqmhUrq3g4aMA3WKbVChwIdlMc3laJkbsIlMBHs74V4yo8s MauzOdNaFFgr7KKjopWO6b/qLVRgcMsm5Cr/+PaIcylcbsJ9UiMew6s7H7CFjTj+ AzER9ydTBp9hmJHdXyVmEHzTpYJL+5nMUToYbv8on4gzScIVhimpAjJVDjT6ALui q7+NyicdnGitpFyrbE0mS3Y0yUtbfwmJA1txrmpoyVnX9HhJ5ZNvRMEW0HmylKi5 CLvJcdg2XG42xCogaNtOkI54AAMZIDgulAQwuYemY5C/wAvsOxQjhjvfv8oTFMPv 3Q0BhTjuH7xACcxIBIZSvZmw822PB07/lrR8lMyJz4CTHJwNts1ycnc5Kw1n/8qs 73f6RZWiWI0eqMMm0Pahv6bGD9lLurp66Y0NnwBl7HXSvXAdL9L+xlzecqqTYeCI r1czm7qli+mKZY9Wml/sfgXgBaLw0/UhhU99IyzrVYwrTrNRRAb6UJqCRmw0mTuj 1f0Q69w= =H+RV -----END PGP SIGNATURE----- Merge tag 'sound-5.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound Pull sound updates from Takashi Iwai: "Many updates in this development cycle are found in ASoC where it got a wide range of changes for the continued refactoring. Some highlights are below. ASoC: - Continued refactoring work by Morimoto-san toward the full componentization; the changes are seen allover the places - Support for force disconnecting muxes in DAPM - Continued development of ASoC Intel SOF stuff - New drivers for Cirrus Logic CS47L35, CS47L85 and CS47L90, Conexant CX2072X, Realtek RT1011 and RT1308 HD-audio: - More fixes and adjustments for ASoC SOF HD-audio - Fix for resume problem on some Realtek codecs USB-audio: - A few fixes for the issues reported by syzbot USB fuzzer - Fix for UAC2 extension unit parser - Quirks for Line6 Helix, Emgaic Unitor 8 FireWire: - Lots of code refactoring and fixes in most of its components" * tag 'sound-5.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (626 commits) ALSA: firewire-lib: code refactoring for local variables ALSA: firewire-lib: code refactoring for post operation to data block counter ALSA: firewire-lib: code refactoring for error path of parser for CIP header ALSA: firewire-lib: fix different data block counter between probed event and transferred isochronous packet ALSA: firewire-lib: fix initial value of data block count for IR context without CIP_DBC_IS_END_EVENT ALSA: firewire-lib/fireface: fix initial value of data block counter for IR context with CIP_NO_HEADER ALSA: firewire-lib: fix invalid length of rx packet payload for tracepoint events ALSA: usb-audio: fix Line6 Helix audio format rates firewire-motu: fix wrong reference count for stream functionality at error path of rawmidi interface ALSA: firewire-digi00x: fix wrong reference count for stream functionality at error path of rawmidi interface ALSA: dice: fix wrong reference count for stream functionality at error path of rawmidi interface ALSA: oxfw: fix wrong reference count for stream functionality at error path of rawmidi interface ALSA: fireworks: fix wrong reference count for stream functionality at error path of rawmidi interface ALSA: bebob: fix wrong reference count for stream functionality at error path of rawmidi interface ASoC: SOF: Intel: implement runtime idle for CNL/APL ASoC: SOF: add runtime idle callback ASoC: hdac_hdmi: report codec link up/down status to bus ASoC: SOF: debug: fix possible memory leak in sof_dfsentry_write() ASoC: sunxi: sun50i-codec-analog: Add earpiece ASoC: rt5665: remove redundant assignment to variable idx ...
This commit is contained in:
commit
4cdd5f9186
|
@ -11,7 +11,7 @@ Required properties:
|
|||
- clock-names: must contain "mclk", which is the DCMI peripherial clock
|
||||
- pinctrl: the pincontrol settings to configure muxing properly
|
||||
for pins that connect to DCMI device.
|
||||
See Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.txt.
|
||||
See Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.yaml.
|
||||
- dmas: phandle to DMA controller node,
|
||||
see Documentation/devicetree/bindings/dma/stm32-dma.txt
|
||||
- dma-names: must contain "tx", which is the transmit channel from DCMI to DMA
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0+ OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/sound/allwinner,sun4i-a10-i2s.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Allwinner A10 I2S Controller Device Tree Bindings
|
||||
|
||||
maintainers:
|
||||
- Chen-Yu Tsai <wens@csie.org>
|
||||
- Maxime Ripard <maxime.ripard@bootlin.com>
|
||||
|
||||
properties:
|
||||
"#sound-dai-cells":
|
||||
const: 0
|
||||
|
||||
compatible:
|
||||
oneOf:
|
||||
- const: allwinner,sun4i-a10-i2s
|
||||
- const: allwinner,sun6i-a31-i2s
|
||||
- const: allwinner,sun8i-a83t-i2s
|
||||
- const: allwinner,sun8i-h3-i2s
|
||||
- const: allwinner,sun50i-a64-codec-i2s
|
||||
- items:
|
||||
- const: allwinner,sun50i-a64-i2s
|
||||
- const: allwinner,sun8i-h3-i2s
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: Bus Clock
|
||||
- description: Module Clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: apb
|
||||
- const: mod
|
||||
|
||||
# Even though it only applies to subschemas under the conditionals,
|
||||
# not listing them here will trigger a warning because of the
|
||||
# additionalsProperties set to false.
|
||||
dmas: true
|
||||
dma-names: true
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- allwinner,sun6i-a31-i2s
|
||||
- allwinner,sun8i-a83t-i2s
|
||||
- allwinner,sun8i-h3-i2s
|
||||
- allwinner,sun50i-a64-codec-i2s
|
||||
|
||||
then:
|
||||
required:
|
||||
- resets
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: allwinner,sun8i-a83t-i2s
|
||||
|
||||
then:
|
||||
properties:
|
||||
dmas:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
items:
|
||||
- description: RX DMA Channel
|
||||
- description: TX DMA Channel
|
||||
description:
|
||||
Some controllers cannot receive but can only transmit
|
||||
data. In such a case, the RX DMA channel is to be omitted.
|
||||
|
||||
dma-names:
|
||||
oneOf:
|
||||
- items:
|
||||
- const: rx
|
||||
- const: tx
|
||||
- const: tx
|
||||
description:
|
||||
Some controllers cannot receive but can only transmit
|
||||
data. In such a case, the RX name is to be omitted.
|
||||
|
||||
else:
|
||||
properties:
|
||||
dmas:
|
||||
items:
|
||||
- description: RX DMA Channel
|
||||
- description: TX DMA Channel
|
||||
|
||||
dma-names:
|
||||
items:
|
||||
- const: rx
|
||||
- const: tx
|
||||
|
||||
required:
|
||||
- "#sound-dai-cells"
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
- dmas
|
||||
- dma-names
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2s0: i2s@1c22400 {
|
||||
#sound-dai-cells = <0>;
|
||||
compatible = "allwinner,sun4i-a10-i2s";
|
||||
reg = <0x01c22400 0x400>;
|
||||
interrupts = <0 16 4>;
|
||||
clocks = <&apb0_gates 3>, <&i2s0_clk>;
|
||||
clock-names = "apb", "mod";
|
||||
dmas = <&dma 0 3>, <&dma 0 3>;
|
||||
dma-names = "rx", "tx";
|
||||
};
|
||||
|
||||
...
|
|
@ -0,0 +1,120 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/sound/allwinner,sun4i-a10-spdif.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Allwinner A10 S/PDIF Controller Device Tree Bindings
|
||||
|
||||
maintainers:
|
||||
- Chen-Yu Tsai <wens@csie.org>
|
||||
- Liam Girdwood <lgirdwood@gmail.com>
|
||||
- Mark Brown <broonie@kernel.org>
|
||||
- Maxime Ripard <maxime.ripard@bootlin.com>
|
||||
|
||||
properties:
|
||||
"#sound-dai-cells":
|
||||
const: 0
|
||||
|
||||
compatible:
|
||||
oneOf:
|
||||
- const: allwinner,sun4i-a10-spdif
|
||||
- const: allwinner,sun6i-a31-spdif
|
||||
- const: allwinner,sun8i-h3-spdif
|
||||
- const: allwinner,sun50i-h6-spdif
|
||||
- items:
|
||||
- const: allwinner,sun8i-a83t-spdif
|
||||
- const: allwinner,sun8i-h3-spdif
|
||||
- items:
|
||||
- const: allwinner,sun50i-a64-spdif
|
||||
- const: allwinner,sun8i-h3-spdif
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: Bus Clock
|
||||
- description: Module Clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: apb
|
||||
- const: spdif
|
||||
|
||||
# Even though it only applies to subschemas under the conditionals,
|
||||
# not listing them here will trigger a warning because of the
|
||||
# additionalsProperties set to false.
|
||||
dmas: true
|
||||
dma-names: true
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- allwinner,sun6i-a31-spdif
|
||||
- allwinner,sun8i-h3-spdif
|
||||
|
||||
then:
|
||||
required:
|
||||
- resets
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: allwinner,sun8i-h3-spdif
|
||||
|
||||
then:
|
||||
properties:
|
||||
dmas:
|
||||
description: TX DMA Channel
|
||||
|
||||
dma-names:
|
||||
const: tx
|
||||
|
||||
else:
|
||||
properties:
|
||||
dmas:
|
||||
items:
|
||||
- description: RX DMA Channel
|
||||
- description: TX DMA Channel
|
||||
|
||||
dma-names:
|
||||
items:
|
||||
- const: rx
|
||||
- const: tx
|
||||
|
||||
required:
|
||||
- "#sound-dai-cells"
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
- dmas
|
||||
- dma-names
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
spdif: spdif@1c21000 {
|
||||
#sound-dai-cells = <0>;
|
||||
compatible = "allwinner,sun4i-a10-spdif";
|
||||
reg = <0x01c21000 0x40>;
|
||||
interrupts = <13>;
|
||||
clocks = <&apb0_gates 1>, <&spdif_clk>;
|
||||
clock-names = "apb", "spdif";
|
||||
dmas = <&dma 0 2>, <&dma 0 2>;
|
||||
dma-names = "rx", "tx";
|
||||
};
|
||||
|
||||
...
|
|
@ -15,11 +15,15 @@ Required properties:
|
|||
* "lrclk" : sample clock
|
||||
* "lrclk_sel": sample clock input multiplexer
|
||||
|
||||
Example of TDMOUT_A on the A113 SoC:
|
||||
Optional property:
|
||||
- resets: phandle to the dedicated reset line of the tdm formatter.
|
||||
|
||||
Example of TDMOUT_A on the S905X2 SoC:
|
||||
|
||||
tdmout_a: audio-controller@500 {
|
||||
compatible = "amlogic,axg-tdmout";
|
||||
reg = <0x0 0x500 0x0 0x40>;
|
||||
resets = <&clkc_audio AUD_RESET_TDMOUT_A>;
|
||||
clocks = <&clkc_audio AUD_CLKID_TDMOUT_A>,
|
||||
<&clkc_audio AUD_CLKID_TDMOUT_A_SCLK>,
|
||||
<&clkc_audio AUD_CLKID_TDMOUT_A_SCLK_SEL>,
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
* Amlogic HDMI Tx control glue
|
||||
|
||||
Required properties:
|
||||
- compatible: "amlogic,g12a-tohdmitx"
|
||||
- reg: physical base address of the controller and length of memory
|
||||
mapped region.
|
||||
- #sound-dai-cells: should be 1.
|
||||
|
||||
Example on the S905X2 SoC:
|
||||
|
||||
tohdmitx: audio-controller@744 {
|
||||
compatible = "amlogic,g12a-tohdmitx";
|
||||
reg = <0x0 0x744 0x0 0x4>;
|
||||
#sound-dai-cells = <1>;
|
||||
};
|
||||
|
||||
Example of an 'amlogic,axg-sound-card':
|
||||
|
||||
sound {
|
||||
compatible = "amlogic,axg-sound-card";
|
||||
|
||||
[...]
|
||||
|
||||
dai-link-x {
|
||||
sound-dai = <&tdmif_a>;
|
||||
dai-format = "i2s";
|
||||
dai-tdm-slot-tx-mask-0 = <1 1>;
|
||||
|
||||
codec-0 {
|
||||
sound-dai = <&tohdmitx TOHDMITX_I2S_IN_A>;
|
||||
};
|
||||
|
||||
codec-1 {
|
||||
sound-dai = <&external_dac>;
|
||||
};
|
||||
};
|
||||
|
||||
dai-link-y {
|
||||
sound-dai = <&tdmif_c>;
|
||||
dai-format = "i2s";
|
||||
dai-tdm-slot-tx-mask-0 = <1 1>;
|
||||
|
||||
codec {
|
||||
sound-dai = <&tohdmitx TOHDMITX_I2S_IN_C>;
|
||||
};
|
||||
};
|
||||
|
||||
dai-link-z {
|
||||
sound-dai = <&tohdmitx TOHDMITX_I2S_OUT>;
|
||||
|
||||
codec {
|
||||
sound-dai = <&hdmi_tx>;
|
||||
};
|
||||
};
|
||||
};
|
|
@ -14,6 +14,11 @@ Required properties:
|
|||
- VA-supply, VD-supply, VLS-supply, VLC-supply: power supplies for the device,
|
||||
as covered in Documentation/devicetree/bindings/regulator/regulator.txt
|
||||
|
||||
Optional properties:
|
||||
|
||||
- reset-gpios : a GPIO spec to define which pin is connected to the chip's
|
||||
!RESET pin
|
||||
|
||||
Example:
|
||||
|
||||
cs42888: codec@48 {
|
||||
|
@ -25,4 +30,5 @@ cs42888: codec@48 {
|
|||
VD-supply = <®_audio>;
|
||||
VLS-supply = <®_audio>;
|
||||
VLC-supply = <®_audio>;
|
||||
reset-gpios = <&pca9557_b 1 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
|
|
|
@ -44,6 +44,9 @@ Optional properties:
|
|||
please refer to pinctrl-bindings.txt
|
||||
- fck_parent : Should contain a valid clock name which will be used as parent
|
||||
for the McASP fck
|
||||
- auxclk-fs-ratio: When McASP is bus master indicates the ratio between AUCLK
|
||||
and FS rate if applicable:
|
||||
AUCLK rate = auxclk-fs-ratio * FS rate
|
||||
|
||||
Optional GPIO support:
|
||||
If any McASP pin need to be used as GPIO then the McASP node must have:
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
Cirrus Logic Madera class audio codecs
|
||||
|
||||
This describes audio configuration bindings for these codecs.
|
||||
|
||||
See also the core bindings for the parent MFD driver:
|
||||
See Documentation/devicetree/bindings/mfd/madera.txt
|
||||
|
||||
and defines for values used in these bindings:
|
||||
include/dt-bindings/sound/madera.h
|
||||
|
||||
These properties are all contained in the parent MFD node.
|
||||
|
||||
Optional properties:
|
||||
- cirrus,dmic-ref : Indicates how the MICBIAS pins have been externally
|
||||
connected to DMICs on each input, one cell per input.
|
||||
<IN1 IN2 IN3 ...>
|
||||
A value of 0 indicates MICVDD and is the default, other values depend on the
|
||||
codec:
|
||||
For CS47L35 one of the CS47L35_DMIC_REF_xxx values
|
||||
For all other codecs one of the MADERA_DMIC_REF_xxx values
|
||||
Also see the datasheet for a description of the INn_DMIC_SUP field.
|
||||
|
||||
- cirrus,inmode : A list of input mode settings for each input. A maximum of
|
||||
16 cells, with four cells per input in the order INnAL, INnAR INnBL INnBR.
|
||||
For non-muxed inputs the first two cells for that input set the mode for
|
||||
the left and right channel and the second two cells must be 0.
|
||||
For muxed inputs the first two cells for that input set the mode of the
|
||||
left and right A inputs and the second two cells set the mode of the left
|
||||
and right B inputs.
|
||||
Valid mode values are one of the MADERA_INMODE_xxx. If the array is shorter
|
||||
than the number of inputs the unspecified inputs default to
|
||||
MADERA_INMODE_DIFF.
|
||||
|
||||
- cirrus,out-mono : Mono bit for each output, maximum of six cells if the
|
||||
array is shorter outputs will be set to stereo.
|
||||
|
||||
- cirrus,max-channels-clocked : Maximum number of channels that I2S clocks
|
||||
will be generated for. Useful when clock master for systems where the I2S
|
||||
bus has multiple data lines.
|
||||
One cell for each AIF, use a value of zero for AIFs that should be handled
|
||||
normally.
|
||||
|
||||
- cirrus,pdm-fmt : PDM speaker data format, must contain 2 cells
|
||||
(OUT5 and OUT6). See the PDM_SPKn_FMT field in the datasheet for a
|
||||
description of this value.
|
||||
The second cell is ignored for codecs that do not have OUT6.
|
||||
|
||||
- cirrus,pdm-mute : PDM mute format, must contain 2 cells
|
||||
(OUT5 and OUT6). See the PDM_SPKn_CTRL_1 register in the datasheet for a
|
||||
description of this value.
|
||||
The second cell is ignored for codecs that do not have OUT6.
|
||||
|
||||
Example:
|
||||
|
||||
cs47l35@0 {
|
||||
compatible = "cirrus,cs47l35";
|
||||
|
||||
cirrus,dmic-ref = <0 0 CS47L35_DMIC_REF_MICBIAS1B 0>;
|
||||
cirrus,inmode = <
|
||||
MADERA_INMODE_DMIC MADERA_INMODE_DMIC /* IN1A digital */
|
||||
MADERA_INMODE_SE MADERA_INMODE_SE /* IN1B single-ended */
|
||||
MADERA_INMODE_DIFF MADERA_INMODE_DIFF /* IN2 differential */
|
||||
0 0 /* not used on this codec */
|
||||
>;
|
||||
cirrus,out-mono = <0 0 0 0 0 0>;
|
||||
cirrus,max-channels-clocked = <2 0 0>;
|
||||
};
|
|
@ -9,6 +9,10 @@ Optional properties:
|
|||
- sdmode-gpios : GPIO specifier for the chip's SD_MODE pin.
|
||||
If this option is not specified then driver does not manage
|
||||
the pin state (e.g. chip is always on).
|
||||
- sdmode-delay : specify delay time for SD_MODE pin.
|
||||
If this option is specified, which means it's required i2s clocks
|
||||
ready before SD_MODE is unmuted in order to avoid the speaker pop noise.
|
||||
It's observed that 5ms is sufficient.
|
||||
|
||||
Example:
|
||||
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
RT1011 Mono Class D Audio Amplifier
|
||||
|
||||
This device supports I2C only.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "realtek,rt1011".
|
||||
|
||||
- reg : The I2C address of the device. This I2C address decide by
|
||||
two input pins (ASEL1 and ASEL2).
|
||||
-------------------------------------
|
||||
| ASEL2 | ASEL1 | Address |
|
||||
-------------------------------------
|
||||
| 0 | 0 | 0x38 |
|
||||
-------------------------------------
|
||||
| 0 | 1 | 0x39 |
|
||||
-------------------------------------
|
||||
| 1 | 0 | 0x3a |
|
||||
-------------------------------------
|
||||
| 1 | 1 | 0x3b |
|
||||
-------------------------------------
|
||||
|
||||
Pins on the device (for linking into audio routes) for RT1011:
|
||||
|
||||
* SPO
|
||||
|
||||
Example:
|
||||
|
||||
rt1011: codec@38 {
|
||||
compatible = "realtek,rt1011";
|
||||
reg = <0x38>;
|
||||
};
|
|
@ -0,0 +1,17 @@
|
|||
RT1308 audio Amplifier
|
||||
|
||||
This device supports I2C only.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "realtek,rt1308".
|
||||
|
||||
- reg : The I2C address of the device.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
rt1308: rt1308@10 {
|
||||
compatible = "realtek,rt1308";
|
||||
reg = <0x10>;
|
||||
};
|
|
@ -18,7 +18,7 @@ Required properties:
|
|||
See Documentation/devicetree/bindings/dma/stm32-dma.txt.
|
||||
- dma-names: Identifier for each DMA request line. Must be "tx" and "rx".
|
||||
- pinctrl-names: should contain only value "default"
|
||||
- pinctrl-0: see Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.txt
|
||||
- pinctrl-0: see Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.yaml
|
||||
|
||||
Optional properties:
|
||||
- resets: Reference to a reset controller asserting the reset controller
|
||||
|
|
|
@ -41,7 +41,7 @@ SAI subnodes required properties:
|
|||
"tx": if sai sub-block is configured as playback DAI
|
||||
"rx": if sai sub-block is configured as capture DAI
|
||||
- pinctrl-names: should contain only value "default"
|
||||
- pinctrl-0: see Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.txt
|
||||
- pinctrl-0: see Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.yaml
|
||||
|
||||
SAI subnodes Optional properties:
|
||||
- st,sync: specify synchronization mode.
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
* Allwinner A10 I2S controller
|
||||
|
||||
The I2S bus (Inter-IC sound bus) is a serial link for digital
|
||||
audio data transfer between devices in the system.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: should be one of the following:
|
||||
- "allwinner,sun4i-a10-i2s"
|
||||
- "allwinner,sun6i-a31-i2s"
|
||||
- "allwinner,sun8i-a83t-i2s"
|
||||
- "allwinner,sun8i-h3-i2s"
|
||||
- "allwinner,sun50i-a64-codec-i2s"
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
- interrupts: should contain the I2S interrupt.
|
||||
- dmas: DMA specifiers for tx and rx dma. See the DMA client binding,
|
||||
Documentation/devicetree/bindings/dma/dma.txt
|
||||
- dma-names: should include "tx" and "rx".
|
||||
- clocks: a list of phandle + clock-specifer pairs, one for each entry in clock-names.
|
||||
- clock-names: should contain the following:
|
||||
- "apb" : clock for the I2S bus interface
|
||||
- "mod" : module clock for the I2S controller
|
||||
- #sound-dai-cells : Must be equal to 0
|
||||
|
||||
Required properties for the following compatibles:
|
||||
- "allwinner,sun6i-a31-i2s"
|
||||
- "allwinner,sun8i-a83t-i2s"
|
||||
- "allwinner,sun8i-h3-i2s"
|
||||
- "allwinner,sun50i-a64-codec-i2s"
|
||||
- resets: phandle to the reset line for this codec
|
||||
|
||||
Example:
|
||||
|
||||
i2s0: i2s@1c22400 {
|
||||
#sound-dai-cells = <0>;
|
||||
compatible = "allwinner,sun4i-a10-i2s";
|
||||
reg = <0x01c22400 0x400>;
|
||||
interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&apb0_gates 3>, <&i2s0_clk>;
|
||||
clock-names = "apb", "mod";
|
||||
dmas = <&dma SUN4I_DMA_NORMAL 3>,
|
||||
<&dma SUN4I_DMA_NORMAL 3>;
|
||||
dma-names = "rx", "tx";
|
||||
};
|
|
@ -1,42 +0,0 @@
|
|||
Allwinner Sony/Philips Digital Interface Format (S/PDIF) Controller
|
||||
|
||||
The Allwinner S/PDIF audio block is a transceiver that allows the
|
||||
processor to receive and transmit digital audio via an coaxial cable or
|
||||
a fibre cable.
|
||||
For now only playback is supported.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : should be one of the following:
|
||||
- "allwinner,sun4i-a10-spdif": for the Allwinner A10 SoC
|
||||
- "allwinner,sun6i-a31-spdif": for the Allwinner A31 SoC
|
||||
- "allwinner,sun8i-h3-spdif": for the Allwinner H3 SoC
|
||||
|
||||
- reg : Offset and length of the register set for the device.
|
||||
|
||||
- interrupts : Contains the spdif interrupt.
|
||||
|
||||
- dmas : Generic dma devicetree binding as described in
|
||||
Documentation/devicetree/bindings/dma/dma.txt.
|
||||
|
||||
- dma-names : Two dmas have to be defined, "tx" and "rx".
|
||||
|
||||
- clocks : Contains an entry for each entry in clock-names.
|
||||
|
||||
- clock-names : Includes the following entries:
|
||||
"apb" clock for the spdif bus.
|
||||
"spdif" clock for spdif controller.
|
||||
|
||||
- resets : reset specifier for the ahb reset (A31 and newer only)
|
||||
|
||||
Example:
|
||||
|
||||
spdif: spdif@1c21000 {
|
||||
compatible = "allwinner,sun4i-a10-spdif";
|
||||
reg = <0x01c21000 0x40>;
|
||||
interrupts = <13>;
|
||||
clocks = <&apb0_gates 1>, <&spdif_clk>;
|
||||
clock-names = "apb", "spdif";
|
||||
dmas = <&dma 0 2>, <&dma 0 2>;
|
||||
dma-names = "rx", "tx";
|
||||
};
|
|
@ -1297,7 +1297,7 @@ ARM PRIMECELL SSP PL022 SPI DRIVER
|
|||
M: Linus Walleij <linus.walleij@linaro.org>
|
||||
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/spi/spi_pl022.txt
|
||||
F: Documentation/devicetree/bindings/spi/spi-pl022.yaml
|
||||
F: drivers/spi/spi-pl022.c
|
||||
|
||||
ARM PRIMECELL UART PL010 AND PL011 DRIVERS
|
||||
|
@ -3942,13 +3942,18 @@ W: https://github.com/CirrusLogic/linux-drivers/wiki
|
|||
S: Supported
|
||||
F: Documentation/devicetree/bindings/mfd/madera.txt
|
||||
F: Documentation/devicetree/bindings/pinctrl/cirrus,madera-pinctrl.txt
|
||||
F: Documentation/devicetree/bindings/sound/madera.txt
|
||||
F: include/dt-bindings/sound/madera*
|
||||
F: include/linux/irqchip/irq-madera*
|
||||
F: include/linux/mfd/madera/*
|
||||
F: include/sound/madera*
|
||||
F: drivers/gpio/gpio-madera*
|
||||
F: drivers/irqchip/irq-madera*
|
||||
F: drivers/mfd/madera*
|
||||
F: drivers/mfd/cs47l*
|
||||
F: drivers/pinctrl/cirrus/*
|
||||
F: sound/soc/codecs/cs47l*
|
||||
F: sound/soc/codecs/madera*
|
||||
|
||||
CLANG-FORMAT FILE
|
||||
M: Miguel Ojeda <miguel.ojeda.sandonis@gmail.com>
|
||||
|
|
|
@ -58,6 +58,9 @@
|
|||
struct vc4_hdmi_audio {
|
||||
struct snd_soc_card card;
|
||||
struct snd_soc_dai_link link;
|
||||
struct snd_soc_dai_link_component cpu;
|
||||
struct snd_soc_dai_link_component codec;
|
||||
struct snd_soc_dai_link_component platform;
|
||||
int samplerate;
|
||||
int channels;
|
||||
struct snd_dmaengine_dai_dma_data dma_data;
|
||||
|
@ -1085,12 +1088,20 @@ static int vc4_hdmi_audio_init(struct vc4_hdmi *hdmi)
|
|||
return ret;
|
||||
}
|
||||
|
||||
dai_link->cpus = &hdmi->audio.cpu;
|
||||
dai_link->codecs = &hdmi->audio.codec;
|
||||
dai_link->platforms = &hdmi->audio.platform;
|
||||
|
||||
dai_link->num_cpus = 1;
|
||||
dai_link->num_codecs = 1;
|
||||
dai_link->num_platforms = 1;
|
||||
|
||||
dai_link->name = "MAI";
|
||||
dai_link->stream_name = "MAI PCM";
|
||||
dai_link->codec_dai_name = vc4_hdmi_audio_codec_dai_drv.name;
|
||||
dai_link->cpu_dai_name = dev_name(dev);
|
||||
dai_link->codec_name = dev_name(dev);
|
||||
dai_link->platform_name = dev_name(dev);
|
||||
dai_link->codecs->dai_name = vc4_hdmi_audio_codec_dai_drv.name;
|
||||
dai_link->cpus->dai_name = dev_name(dev);
|
||||
dai_link->codecs->name = dev_name(dev);
|
||||
dai_link->platforms->name = dev_name(dev);
|
||||
|
||||
card->dai_link = dai_link;
|
||||
card->num_links = 1;
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Device Tree defines for Madera codecs
|
||||
*
|
||||
* Copyright (C) 2016-2017 Cirrus Logic, Inc. and
|
||||
* Cirrus Logic International Semiconductor Ltd.
|
||||
*/
|
||||
|
||||
#ifndef DT_BINDINGS_SOUND_MADERA_H
|
||||
#define DT_BINDINGS_SOUND_MADERA_H
|
||||
|
||||
#define MADERA_INMODE_DIFF 0
|
||||
#define MADERA_INMODE_SE 1
|
||||
#define MADERA_INMODE_DMIC 2
|
||||
|
||||
#define MADERA_DMIC_REF_MICVDD 0
|
||||
#define MADERA_DMIC_REF_MICBIAS1 1
|
||||
#define MADERA_DMIC_REF_MICBIAS2 2
|
||||
#define MADERA_DMIC_REF_MICBIAS3 3
|
||||
|
||||
#define CS47L35_DMIC_REF_MICBIAS1B 1
|
||||
#define CS47L35_DMIC_REF_MICBIAS2A 2
|
||||
#define CS47L35_DMIC_REF_MICBIAS2B 3
|
||||
|
||||
#endif
|
|
@ -0,0 +1,13 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef __DT_MESON_G12A_TOHDMITX_H
|
||||
#define __DT_MESON_G12A_TOHDMITX_H
|
||||
|
||||
#define TOHDMITX_I2S_IN_A 0
|
||||
#define TOHDMITX_I2S_IN_B 1
|
||||
#define TOHDMITX_I2S_IN_C 2
|
||||
#define TOHDMITX_I2S_OUT 3
|
||||
#define TOHDMITX_SPDIF_IN_A 4
|
||||
#define TOHDMITX_SPDIF_IN_B 5
|
||||
#define TOHDMITX_SPDIF_OUT 6
|
||||
|
||||
#endif /* __DT_MESON_G12A_TOHDMITX_H */
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/regulator/arizona-ldo1.h>
|
||||
#include <linux/regulator/arizona-micsupp.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <sound/madera-pdata.h>
|
||||
|
||||
#define MADERA_MAX_MICBIAS 4
|
||||
#define MADERA_MAX_CHILD_MICBIAS 4
|
||||
|
@ -39,6 +40,7 @@ struct madera_codec_pdata;
|
|||
* @gpsw: General purpose switch mode setting. Depends on the external
|
||||
* hardware connected to the switch. (See the SW1_MODE field
|
||||
* in the datasheet for the available values for your codec)
|
||||
* @codec: Substruct of pdata for the ASoC codec driver
|
||||
*/
|
||||
struct madera_pdata {
|
||||
struct gpio_desc *reset;
|
||||
|
@ -53,6 +55,8 @@ struct madera_pdata {
|
|||
int n_gpio_configs;
|
||||
|
||||
u32 gpsw[MADERA_MAX_GPSW];
|
||||
|
||||
struct madera_codec_pdata codec;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -18,6 +18,9 @@
|
|||
#include <sound/hda_verbs.h>
|
||||
#include <sound/hda_regmap.h>
|
||||
|
||||
#define IS_BXT(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x5a98)
|
||||
#define IS_CFL(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0xa348)
|
||||
|
||||
/*
|
||||
* Structures
|
||||
*/
|
||||
|
@ -268,9 +271,6 @@ struct hda_codec {
|
|||
unsigned long jackpoll_interval; /* In jiffies. Zero means no poll, rely on unsol events */
|
||||
struct delayed_work jackpoll_work;
|
||||
|
||||
/* jack detection */
|
||||
struct snd_array jacks;
|
||||
|
||||
int depop_delay; /* depop delay in ms, -1 for default delay time */
|
||||
|
||||
/* fix-up list */
|
||||
|
|
|
@ -120,7 +120,7 @@ void snd_hdac_device_unregister(struct hdac_device *codec);
|
|||
int snd_hdac_device_set_chip_name(struct hdac_device *codec, const char *name);
|
||||
int snd_hdac_codec_modalias(struct hdac_device *hdac, char *buf, size_t size);
|
||||
|
||||
int snd_hdac_refresh_widgets(struct hdac_device *codec, bool sysfs);
|
||||
int snd_hdac_refresh_widgets(struct hdac_device *codec);
|
||||
|
||||
unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid,
|
||||
unsigned int verb, unsigned int parm);
|
||||
|
@ -358,6 +358,9 @@ struct hdac_bus {
|
|||
bool align_bdle_4k:1; /* BDLE align 4K boundary */
|
||||
bool reverse_assign:1; /* assign devices in reverse order */
|
||||
bool corbrp_self_clear:1; /* CORBRP clears itself after reset */
|
||||
bool polling_mode:1;
|
||||
|
||||
int poll_count;
|
||||
|
||||
int bdl_pos_adj; /* BDL position adjustment */
|
||||
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Platform data for Madera codec driver
|
||||
*
|
||||
* Copyright (C) 2016-2019 Cirrus Logic, Inc. and
|
||||
* Cirrus Logic International Semiconductor Ltd.
|
||||
*/
|
||||
|
||||
#ifndef MADERA_CODEC_PDATA_H
|
||||
#define MADERA_CODEC_PDATA_H
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#define MADERA_MAX_INPUT 6
|
||||
#define MADERA_MAX_MUXED_CHANNELS 4
|
||||
#define MADERA_MAX_OUTPUT 6
|
||||
#define MADERA_MAX_AIF 4
|
||||
#define MADERA_MAX_PDM_SPK 2
|
||||
#define MADERA_MAX_DSP 7
|
||||
|
||||
/**
|
||||
* struct madera_codec_pdata
|
||||
*
|
||||
* @max_channels_clocked: Maximum number of channels that I2S clocks will be
|
||||
* generated for. Useful when clock master for systems
|
||||
* where the I2S bus has multiple data lines.
|
||||
* @dmic_ref: Indicates how the MICBIAS pins have been externally
|
||||
* connected to DMICs on each input. A value of 0
|
||||
* indicates MICVDD and is the default. Other values are:
|
||||
* For CS47L35 one of the CS47L35_DMIC_REF_xxx values
|
||||
* For all other codecs one of the MADERA_DMIC_REF_xxx
|
||||
* Also see the datasheet for a description of the
|
||||
* INn_DMIC_SUP field.
|
||||
* @inmode: Mode for the ADC inputs. One of the MADERA_INMODE_xxx
|
||||
* values. Two-dimensional array
|
||||
* [input_number][channel number], with four slots per
|
||||
* input in the order
|
||||
* [n][0]=INnAL [n][1]=INnAR [n][2]=INnBL [n][3]=INnBR
|
||||
* @out_mono: For each output set the value to TRUE to indicate that
|
||||
* the output is mono. [0]=OUT1, [1]=OUT2, ...
|
||||
* @pdm_fmt: PDM speaker data format. See the PDM_SPKn_FMT field in
|
||||
* the datasheet for a description of this value.
|
||||
* @pdm_mute: PDM mute format. See the PDM_SPKn_CTRL_1 register
|
||||
* in the datasheet for a description of this value.
|
||||
*/
|
||||
struct madera_codec_pdata {
|
||||
u32 max_channels_clocked[MADERA_MAX_AIF];
|
||||
|
||||
u32 dmic_ref[MADERA_MAX_INPUT];
|
||||
|
||||
u32 inmode[MADERA_MAX_INPUT][MADERA_MAX_MUXED_CHANNELS];
|
||||
|
||||
bool out_mono[MADERA_MAX_OUTPUT];
|
||||
|
||||
u32 pdm_fmt[MADERA_MAX_PDM_SPK];
|
||||
u32 pdm_mute[MADERA_MAX_PDM_SPK];
|
||||
};
|
||||
|
||||
#endif
|
|
@ -42,6 +42,7 @@ struct asoc_simple_priv {
|
|||
struct simple_dai_props {
|
||||
struct asoc_simple_dai *cpu_dai;
|
||||
struct asoc_simple_dai *codec_dai;
|
||||
struct snd_soc_dai_link_component cpus; /* single cpu */
|
||||
struct snd_soc_dai_link_component codecs; /* single codec */
|
||||
struct snd_soc_dai_link_component platforms;
|
||||
struct asoc_simple_data adata;
|
||||
|
@ -80,16 +81,12 @@ int asoc_simple_parse_card_name(struct snd_soc_card *card,
|
|||
char *prefix);
|
||||
|
||||
#define asoc_simple_parse_clk_cpu(dev, node, dai_link, simple_dai) \
|
||||
asoc_simple_parse_clk(dev, node, dai_link->cpu_of_node, simple_dai, \
|
||||
dai_link->cpu_dai_name, NULL)
|
||||
asoc_simple_parse_clk(dev, node, simple_dai, dai_link->cpus)
|
||||
#define asoc_simple_parse_clk_codec(dev, node, dai_link, simple_dai) \
|
||||
asoc_simple_parse_clk(dev, node, dai_link->codec_of_node, simple_dai,\
|
||||
dai_link->codec_dai_name, dai_link->codecs)
|
||||
asoc_simple_parse_clk(dev, node, simple_dai, dai_link->codecs)
|
||||
int asoc_simple_parse_clk(struct device *dev,
|
||||
struct device_node *node,
|
||||
struct device_node *dai_of_node,
|
||||
struct asoc_simple_dai *simple_dai,
|
||||
const char *dai_name,
|
||||
struct snd_soc_dai_link_component *dlc);
|
||||
int asoc_simple_startup(struct snd_pcm_substream *substream);
|
||||
void asoc_simple_shutdown(struct snd_pcm_substream *substream);
|
||||
|
@ -100,16 +97,11 @@ int asoc_simple_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
|
|||
struct snd_pcm_hw_params *params);
|
||||
|
||||
#define asoc_simple_parse_cpu(node, dai_link, is_single_link) \
|
||||
asoc_simple_parse_dai(node, NULL, \
|
||||
&dai_link->cpu_of_node, \
|
||||
&dai_link->cpu_dai_name, is_single_link)
|
||||
asoc_simple_parse_dai(node, dai_link->cpus, is_single_link)
|
||||
#define asoc_simple_parse_codec(node, dai_link) \
|
||||
asoc_simple_parse_dai(node, dai_link->codecs, \
|
||||
&dai_link->codec_of_node, \
|
||||
&dai_link->codec_dai_name, NULL)
|
||||
asoc_simple_parse_dai(node, dai_link->codecs, NULL)
|
||||
#define asoc_simple_parse_platform(node, dai_link) \
|
||||
asoc_simple_parse_dai(node, dai_link->platforms, \
|
||||
&dai_link->platform_of_node, NULL, NULL)
|
||||
asoc_simple_parse_dai(node, dai_link->platforms, NULL)
|
||||
|
||||
#define asoc_simple_parse_tdm(np, dai) \
|
||||
snd_soc_of_parse_tdm_slot(np, &(dai)->tx_slot_mask, \
|
||||
|
|
|
@ -900,17 +900,6 @@ struct snd_soc_dai_link {
|
|||
const char *name; /* Codec name */
|
||||
const char *stream_name; /* Stream name */
|
||||
|
||||
/*
|
||||
* cpu_name
|
||||
* cpu_of_node
|
||||
* cpu_dai_name
|
||||
*
|
||||
* These are legacy style, and will be replaced to
|
||||
* modern style (= snd_soc_dai_link_component) in the future,
|
||||
* but, not yet supported so far.
|
||||
* If modern style was supported for CPU, all driver will switch
|
||||
* to use it, and, legacy style code will be removed from ALSA SoC.
|
||||
*/
|
||||
/*
|
||||
* You MAY specify the link's CPU-side device, either by device name,
|
||||
* or by DT/OF node, but not both. If this information is omitted,
|
||||
|
@ -918,57 +907,27 @@ struct snd_soc_dai_link {
|
|||
* must be globally unique. These fields are currently typically used
|
||||
* only for codec to codec links, or systems using device tree.
|
||||
*/
|
||||
const char *cpu_name;
|
||||
struct device_node *cpu_of_node;
|
||||
/*
|
||||
* You MAY specify the DAI name of the CPU DAI. If this information is
|
||||
* omitted, the CPU-side DAI is matched using .cpu_name/.cpu_of_node
|
||||
* only, which only works well when that device exposes a single DAI.
|
||||
*/
|
||||
const char *cpu_dai_name;
|
||||
struct snd_soc_dai_link_component *cpus;
|
||||
unsigned int num_cpus;
|
||||
|
||||
/*
|
||||
* codec_name
|
||||
* codec_of_node
|
||||
* codec_dai_name
|
||||
*
|
||||
* These are legacy style, it will be converted to modern style
|
||||
* (= snd_soc_dai_link_component) automatically in soc-core
|
||||
* if driver is using legacy style.
|
||||
* Driver shouldn't use both legacy and modern style in the same time.
|
||||
* If modern style was supported for CPU, all driver will switch
|
||||
* to use it, and, legacy style code will be removed from ALSA SoC.
|
||||
*/
|
||||
/*
|
||||
* You MUST specify the link's codec, either by device name, or by
|
||||
* DT/OF node, but not both.
|
||||
*/
|
||||
const char *codec_name;
|
||||
struct device_node *codec_of_node;
|
||||
/* You MUST specify the DAI name within the codec */
|
||||
const char *codec_dai_name;
|
||||
|
||||
struct snd_soc_dai_link_component *codecs;
|
||||
unsigned int num_codecs;
|
||||
|
||||
/*
|
||||
* platform_name
|
||||
* platform_of_node
|
||||
*
|
||||
* These are legacy style, it will be converted to modern style
|
||||
* (= snd_soc_dai_link_component) automatically in soc-core
|
||||
* if driver is using legacy style.
|
||||
* Driver shouldn't use both legacy and modern style in the same time.
|
||||
* If modern style was supported for CPU, all driver will switch
|
||||
* to use it, and, legacy style code will be removed from ALSA SoC.
|
||||
*/
|
||||
/*
|
||||
* You MAY specify the link's platform/PCM/DMA driver, either by
|
||||
* device name, or by DT/OF node, but not both. Some forms of link
|
||||
* do not need a platform.
|
||||
* do not need a platform. In such case, platforms are not mandatory.
|
||||
*/
|
||||
const char *platform_name;
|
||||
struct device_node *platform_of_node;
|
||||
struct snd_soc_dai_link_component *platforms;
|
||||
unsigned int num_platforms;
|
||||
|
||||
|
@ -1030,12 +989,6 @@ struct snd_soc_dai_link {
|
|||
/* Do not create a PCM for this DAI link (Backend link) */
|
||||
unsigned int ignore:1;
|
||||
|
||||
/*
|
||||
* This driver uses legacy platform naming. Set by the core, machine
|
||||
* drivers should not modify this value.
|
||||
*/
|
||||
unsigned int legacy_platform:1;
|
||||
|
||||
struct list_head list; /* DAI link list of the soc card */
|
||||
struct snd_soc_dobj dobj; /* For topology */
|
||||
};
|
||||
|
@ -1044,6 +997,100 @@ struct snd_soc_dai_link {
|
|||
((i) < link->num_codecs) && ((codec) = &link->codecs[i]); \
|
||||
(i)++)
|
||||
|
||||
#define for_each_link_platforms(link, i, platform) \
|
||||
for ((i) = 0; \
|
||||
((i) < link->num_platforms) && \
|
||||
((platform) = &link->platforms[i]); \
|
||||
(i)++)
|
||||
|
||||
/*
|
||||
* Sample 1 : Single CPU/Codec/Platform
|
||||
*
|
||||
* SND_SOC_DAILINK_DEFS(test,
|
||||
* DAILINK_COMP_ARRAY(COMP_CPU("cpu_dai")),
|
||||
* DAILINK_COMP_ARRAY(COMP_CODEC("codec", "codec_dai")),
|
||||
* DAILINK_COMP_ARRAY(COMP_PLATFORM("platform")));
|
||||
*
|
||||
* struct snd_soc_dai_link link = {
|
||||
* ...
|
||||
* SND_SOC_DAILINK_REG(test),
|
||||
* };
|
||||
*
|
||||
* Sample 2 : Multi CPU/Codec, no Platform
|
||||
*
|
||||
* SND_SOC_DAILINK_DEFS(test,
|
||||
* DAILINK_COMP_ARRAY(COMP_CPU("cpu_dai1"),
|
||||
* COMP_CPU("cpu_dai2")),
|
||||
* DAILINK_COMP_ARRAY(COMP_CODEC("codec1", "codec_dai1"),
|
||||
* COMP_CODEC("codec2", "codec_dai2")));
|
||||
*
|
||||
* struct snd_soc_dai_link link = {
|
||||
* ...
|
||||
* SND_SOC_DAILINK_REG(test),
|
||||
* };
|
||||
*
|
||||
* Sample 3 : Define each CPU/Codec/Platform manually
|
||||
*
|
||||
* SND_SOC_DAILINK_DEF(test_cpu,
|
||||
* DAILINK_COMP_ARRAY(COMP_CPU("cpu_dai1"),
|
||||
* COMP_CPU("cpu_dai2")));
|
||||
* SND_SOC_DAILINK_DEF(test_codec,
|
||||
* DAILINK_COMP_ARRAY(COMP_CODEC("codec1", "codec_dai1"),
|
||||
* COMP_CODEC("codec2", "codec_dai2")));
|
||||
* SND_SOC_DAILINK_DEF(test_platform,
|
||||
* DAILINK_COMP_ARRAY(COMP_PLATFORM("platform")));
|
||||
*
|
||||
* struct snd_soc_dai_link link = {
|
||||
* ...
|
||||
* SND_SOC_DAILINK_REG(test_cpu,
|
||||
* test_codec,
|
||||
* test_platform),
|
||||
* };
|
||||
*
|
||||
* Sample 4 : Sample3 without platform
|
||||
*
|
||||
* struct snd_soc_dai_link link = {
|
||||
* ...
|
||||
* SND_SOC_DAILINK_REG(test_cpu,
|
||||
* test_codec);
|
||||
* };
|
||||
*/
|
||||
|
||||
#define SND_SOC_DAILINK_REG1(name) SND_SOC_DAILINK_REG3(name##_cpus, name##_codecs, name##_platforms)
|
||||
#define SND_SOC_DAILINK_REG2(cpu, codec) SND_SOC_DAILINK_REG3(cpu, codec, null_dailink_component)
|
||||
#define SND_SOC_DAILINK_REG3(cpu, codec, platform) \
|
||||
.cpus = cpu, \
|
||||
.num_cpus = ARRAY_SIZE(cpu), \
|
||||
.codecs = codec, \
|
||||
.num_codecs = ARRAY_SIZE(codec), \
|
||||
.platforms = platform, \
|
||||
.num_platforms = ARRAY_SIZE(platform)
|
||||
|
||||
#define SND_SOC_DAILINK_REGx(_1, _2, _3, func, ...) func
|
||||
#define SND_SOC_DAILINK_REG(...) \
|
||||
SND_SOC_DAILINK_REGx(__VA_ARGS__, \
|
||||
SND_SOC_DAILINK_REG3, \
|
||||
SND_SOC_DAILINK_REG2, \
|
||||
SND_SOC_DAILINK_REG1)(__VA_ARGS__)
|
||||
|
||||
#define SND_SOC_DAILINK_DEF(name, def...) \
|
||||
static struct snd_soc_dai_link_component name[] = { def }
|
||||
|
||||
#define SND_SOC_DAILINK_DEFS(name, cpu, codec, platform...) \
|
||||
SND_SOC_DAILINK_DEF(name##_cpus, cpu); \
|
||||
SND_SOC_DAILINK_DEF(name##_codecs, codec); \
|
||||
SND_SOC_DAILINK_DEF(name##_platforms, platform)
|
||||
|
||||
#define DAILINK_COMP_ARRAY(param...) param
|
||||
#define COMP_EMPTY() { }
|
||||
#define COMP_CPU(_dai) { .dai_name = _dai, }
|
||||
#define COMP_CODEC(_name, _dai) { .name = _name, .dai_name = _dai, }
|
||||
#define COMP_PLATFORM(_name) { .name = _name }
|
||||
#define COMP_DUMMY() { .name = "snd-soc-dummy", .dai_name = "snd-soc-dummy-dai", }
|
||||
|
||||
extern struct snd_soc_dai_link_component null_dailink_component[0];
|
||||
|
||||
|
||||
struct snd_soc_codec_conf {
|
||||
/*
|
||||
* specify device either by device name, or by
|
||||
|
@ -1189,7 +1236,7 @@ struct snd_soc_card {
|
|||
(i)++)
|
||||
|
||||
#define for_each_card_links(card, link) \
|
||||
list_for_each_entry(dai_link, &(card)->dai_link_list, list)
|
||||
list_for_each_entry(link, &(card)->dai_link_list, list)
|
||||
#define for_each_card_links_safe(card, link, _link) \
|
||||
list_for_each_entry_safe(link, _link, &(card)->dai_link_list, list)
|
||||
|
||||
|
@ -1214,7 +1261,6 @@ struct snd_soc_pcm_runtime {
|
|||
|
||||
/* Dynamic PCM BE runtime data */
|
||||
struct snd_soc_dpcm_runtime dpcm[2];
|
||||
int fe_compr;
|
||||
|
||||
long pmdown_time;
|
||||
|
||||
|
@ -1239,6 +1285,7 @@ struct snd_soc_pcm_runtime {
|
|||
/* bit field */
|
||||
unsigned int dev_registered:1;
|
||||
unsigned int pop_wait:1;
|
||||
unsigned int fe_compr:1; /* for Dynamic PCM */
|
||||
};
|
||||
#define for_each_rtd_codec_dai(rtd, i, dai)\
|
||||
for ((i) = 0; \
|
||||
|
@ -1607,15 +1654,11 @@ int snd_soc_fixup_dai_links_platform_name(struct snd_soc_card *card,
|
|||
if (!name)
|
||||
return -ENOMEM;
|
||||
|
||||
if (dai_link->platforms)
|
||||
/* only single platform is supported for now */
|
||||
dai_link->platforms->name = name;
|
||||
else
|
||||
/*
|
||||
* legacy mode, this case will be removed when all
|
||||
* derivers are switched to modern style dai_link.
|
||||
*/
|
||||
dai_link->platform_name = name;
|
||||
if (!dai_link->platforms)
|
||||
return -EINVAL;
|
||||
|
||||
/* only single platform is supported for now */
|
||||
dai_link->platforms->name = name;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -167,9 +167,10 @@ struct sof_ipc_dai_dmic_params {
|
|||
|
||||
uint32_t wake_up_time; /**< Time from clock start to data (us) */
|
||||
uint32_t min_clock_on_time; /**< Min. time that clk is kept on (us) */
|
||||
uint32_t unmute_ramp_time; /**< Length of logarithmic gain ramp (ms) */
|
||||
|
||||
/* reserved for future use */
|
||||
uint32_t reserved[6];
|
||||
uint32_t reserved[5];
|
||||
|
||||
/**< variable number of pdm controller config */
|
||||
struct sof_ipc_dai_dmic_pdm_ctrl pdm[0];
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#define SOF_IPC_GLB_DAI_MSG SOF_GLB_TYPE(0x8U)
|
||||
#define SOF_IPC_GLB_TRACE_MSG SOF_GLB_TYPE(0x9U)
|
||||
#define SOF_IPC_GLB_GDB_DEBUG SOF_GLB_TYPE(0xAU)
|
||||
#define SOF_IPC_GLB_TEST_MSG SOF_GLB_TYPE(0xBU)
|
||||
|
||||
/*
|
||||
* DSP Command Message Types
|
||||
|
@ -99,9 +100,13 @@
|
|||
#define SOF_IPC_STREAM_VORBIS_PARAMS SOF_CMD_TYPE(0x010)
|
||||
#define SOF_IPC_STREAM_VORBIS_FREE SOF_CMD_TYPE(0x011)
|
||||
|
||||
/* trace and debug */
|
||||
/* trace */
|
||||
#define SOF_IPC_TRACE_DMA_PARAMS SOF_CMD_TYPE(0x001)
|
||||
#define SOF_IPC_TRACE_DMA_POSITION SOF_CMD_TYPE(0x002)
|
||||
#define SOF_IPC_TRACE_DMA_PARAMS_EXT SOF_CMD_TYPE(0x003)
|
||||
|
||||
/* debug */
|
||||
#define SOF_IPC_TEST_IPC_FLOOD SOF_CMD_TYPE(0x001)
|
||||
|
||||
/* Get message component id */
|
||||
#define SOF_IPC_MESSAGE_ID(x) ((x) & 0xffff)
|
||||
|
|
|
@ -35,6 +35,7 @@ enum sof_comp_type {
|
|||
SOF_COMP_KEYWORD_DETECT,
|
||||
SOF_COMP_KPB, /* A key phrase buffer component */
|
||||
SOF_COMP_SELECTOR, /**< channel selector component */
|
||||
SOF_COMP_DEMUX,
|
||||
/* keep FILEREAD/FILEWRITE as the last ones */
|
||||
SOF_COMP_FILEREAD = 10000, /**< host test based file IO */
|
||||
SOF_COMP_FILEWRITE = 10001, /**< host test based file IO */
|
||||
|
@ -83,9 +84,9 @@ struct sof_ipc_buffer {
|
|||
struct sof_ipc_comp_config {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
uint32_t periods_sink; /**< 0 means variable */
|
||||
uint32_t periods_source; /**< 0 means variable */
|
||||
uint32_t periods_source;/**< 0 means variable */
|
||||
uint32_t reserved1; /**< reserved */
|
||||
uint32_t frame_fmt; /**< SOF_IPC_FRAME_ */
|
||||
uint32_t frame_fmt; /**< SOF_IPC_FRAME_ */
|
||||
uint32_t xrun_action;
|
||||
|
||||
/* reserved for future use */
|
||||
|
@ -175,6 +176,8 @@ enum sof_ipc_process_type {
|
|||
SOF_PROCESS_KEYWORD_DETECT, /**< Keyword Detection */
|
||||
SOF_PROCESS_KPB, /**< KeyPhrase Buffer Manager */
|
||||
SOF_PROCESS_CHAN_SELECTOR, /**< Channel Selector */
|
||||
SOF_PROCESS_MUX,
|
||||
SOF_PROCESS_DEMUX,
|
||||
};
|
||||
|
||||
/* generic "effect", "codec" or proprietary processing component */
|
||||
|
|
|
@ -19,12 +19,22 @@
|
|||
#define SOF_TRACE_FILENAME_SIZE 32
|
||||
|
||||
/* DMA for Trace params info - SOF_IPC_DEBUG_DMA_PARAMS */
|
||||
/* Deprecated - use sof_ipc_dma_trace_params_ext */
|
||||
struct sof_ipc_dma_trace_params {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
struct sof_ipc_host_buffer buffer;
|
||||
uint32_t stream_tag;
|
||||
} __packed;
|
||||
|
||||
/* DMA for Trace params info - SOF_IPC_DEBUG_DMA_PARAMS_EXT */
|
||||
struct sof_ipc_dma_trace_params_ext {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
struct sof_ipc_host_buffer buffer;
|
||||
uint32_t stream_tag;
|
||||
uint64_t timestamp_ns; /* in nanosecond */
|
||||
uint32_t reserved[8];
|
||||
} __packed;
|
||||
|
||||
/* DMA for Trace params info - SOF_IPC_DEBUG_DMA_PARAMS */
|
||||
struct sof_ipc_dma_trace_posn {
|
||||
struct sof_ipc_reply rhdr;
|
||||
|
@ -56,7 +66,9 @@ struct sof_ipc_dma_trace_posn {
|
|||
#define SOF_IPC_PANIC_WFI (SOF_IPC_PANIC_MAGIC | 0xa)
|
||||
#define SOF_IPC_PANIC_ASSERT (SOF_IPC_PANIC_MAGIC | 0xb)
|
||||
|
||||
/* panic info include filename and line number */
|
||||
/* panic info include filename and line number
|
||||
* filename array will not include null terminator if fully filled
|
||||
*/
|
||||
struct sof_ipc_panic_info {
|
||||
struct sof_ipc_hdr hdr;
|
||||
uint32_t code; /* SOF_IPC_PANIC_ */
|
||||
|
|
|
@ -450,6 +450,43 @@ static inline __u8 *uac_processing_unit_specific(struct uac_processing_unit_desc
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Extension Unit (XU) has almost compatible layout with Processing Unit, but
|
||||
* on UAC2, it has a different bmControls size (bControlSize); it's 1 byte for
|
||||
* XU while 2 bytes for PU. The last iExtension field is a one-byte index as
|
||||
* well as iProcessing field of PU.
|
||||
*/
|
||||
static inline __u8 uac_extension_unit_bControlSize(struct uac_processing_unit_descriptor *desc,
|
||||
int protocol)
|
||||
{
|
||||
switch (protocol) {
|
||||
case UAC_VERSION_1:
|
||||
return desc->baSourceID[desc->bNrInPins + 4];
|
||||
case UAC_VERSION_2:
|
||||
return 1; /* in UAC2, this value is constant */
|
||||
case UAC_VERSION_3:
|
||||
return 4; /* in UAC3, this value is constant */
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static inline __u8 uac_extension_unit_iExtension(struct uac_processing_unit_descriptor *desc,
|
||||
int protocol)
|
||||
{
|
||||
__u8 control_size = uac_extension_unit_bControlSize(desc, protocol);
|
||||
|
||||
switch (protocol) {
|
||||
case UAC_VERSION_1:
|
||||
case UAC_VERSION_2:
|
||||
default:
|
||||
return *(uac_processing_unit_bmControls(desc, protocol)
|
||||
+ control_size);
|
||||
case UAC_VERSION_3:
|
||||
return 0; /* UAC3 does not have this field */
|
||||
}
|
||||
}
|
||||
|
||||
/* 4.5.2 Class-Specific AS Interface Descriptor */
|
||||
struct uac1_as_header_descriptor {
|
||||
__u8 bLength; /* in bytes: 7 */
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
/* SOF ABI version major, minor and patch numbers */
|
||||
#define SOF_ABI_MAJOR 3
|
||||
#define SOF_ABI_MINOR 6
|
||||
#define SOF_ABI_MINOR 8
|
||||
#define SOF_ABI_PATCH 0
|
||||
|
||||
/* SOF ABI version number. Format within 32bit word is MMmmmppp */
|
||||
|
|
|
@ -1,172 +0,0 @@
|
|||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_UAPI_SOUND_SOF_USER_EQ_H__
|
||||
#define __INCLUDE_UAPI_SOUND_SOF_USER_EQ_H__
|
||||
|
||||
/* FIR EQ type */
|
||||
|
||||
#define SOF_EQ_FIR_IDX_SWITCH 0
|
||||
|
||||
#define SOF_EQ_FIR_MAX_SIZE 4096 /* Max size allowed for coef data in bytes */
|
||||
|
||||
#define SOF_EQ_FIR_MAX_LENGTH 192 /* Max length for individual filter */
|
||||
|
||||
#define SOF_EQ_FIR_MAX_RESPONSES 8 /* A blob can define max 8 FIR EQs */
|
||||
|
||||
/*
|
||||
* eq_fir_configuration data structure contains this information
|
||||
* uint32_t size
|
||||
* This is the number of bytes need to store the received EQ
|
||||
* configuration.
|
||||
* uint16_t channels_in_config
|
||||
* This describes the number of channels in this EQ config data. It
|
||||
* can be different from PLATFORM_MAX_CHANNELS.
|
||||
* uint16_t number_of_responses
|
||||
* 0=no responses, 1=one response defined, 2=two responses defined, etc.
|
||||
* int16_t data[]
|
||||
* assign_response[channels_in_config]
|
||||
* 0 = use first response, 1 = use 2nd response, etc.
|
||||
* E.g. {0, 0, 0, 0, 1, 1, 1, 1} would apply to channels 0-3 the
|
||||
* same first defined response and for to channels 4-7 the second.
|
||||
* coef_data[]
|
||||
* Repeated data
|
||||
* { filter_length, output_shift, h[] }
|
||||
* for every EQ response defined where vector h has filter_length
|
||||
* number of coefficients. Coefficients in h[] are in Q1.15 format.
|
||||
* E.g. 16384 (Q1.15) = 0.5. The shifts are number of right shifts.
|
||||
*
|
||||
* NOTE: The channels_in_config must be even to have coef_data aligned to
|
||||
* 32 bit word in RAM. Therefore a mono EQ assign must be duplicated to 2ch
|
||||
* even if it would never used. Similarly a 5ch EQ assign must be increased
|
||||
* to 6ch. EQ init will return an error if this is not met.
|
||||
*
|
||||
* NOTE: The filter_length must be multiple of four. Therefore the filter must
|
||||
* be padded from the end with zeros have this condition met.
|
||||
*/
|
||||
|
||||
struct sof_eq_fir_config {
|
||||
uint32_t size;
|
||||
uint16_t channels_in_config;
|
||||
uint16_t number_of_responses;
|
||||
|
||||
/* reserved */
|
||||
uint32_t reserved[4];
|
||||
|
||||
int16_t data[];
|
||||
} __packed;
|
||||
|
||||
struct sof_eq_fir_coef_data {
|
||||
int16_t length; /* Number of FIR taps */
|
||||
int16_t out_shift; /* Amount of right shifts at output */
|
||||
|
||||
/* reserved */
|
||||
uint32_t reserved[4];
|
||||
|
||||
int16_t coef[]; /* FIR coefficients */
|
||||
} __packed;
|
||||
|
||||
/* In the struct above there's two 16 bit words (length, shift) and four
|
||||
* reserved 32 bit words before the actual FIR coefficients. This information
|
||||
* is used in parsing of the configuration blob.
|
||||
*/
|
||||
#define SOF_EQ_FIR_COEF_NHEADER \
|
||||
(sizeof(struct sof_eq_fir_coef_data) / sizeof(int16_t))
|
||||
|
||||
/* IIR EQ type */
|
||||
|
||||
#define SOF_EQ_IIR_IDX_SWITCH 0
|
||||
|
||||
#define SOF_EQ_IIR_MAX_SIZE 1024 /* Max size allowed for coef data in bytes */
|
||||
|
||||
#define SOF_EQ_IIR_MAX_RESPONSES 8 /* A blob can define max 8 IIR EQs */
|
||||
|
||||
/* eq_iir_configuration
|
||||
* uint32_t channels_in_config
|
||||
* This describes the number of channels in this EQ config data. It
|
||||
* can be different from PLATFORM_MAX_CHANNELS.
|
||||
* uint32_t number_of_responses_defined
|
||||
* 0=no responses, 1=one response defined, 2=two responses defined, etc.
|
||||
* int32_t data[]
|
||||
* Data consist of two parts. First is the response assign vector that
|
||||
* has length of channels_in_config. The latter part is coefficient
|
||||
* data.
|
||||
* uint32_t assign_response[channels_in_config]
|
||||
* -1 = not defined, 0 = use first response, 1 = use 2nd, etc.
|
||||
* E.g. {0, 0, 0, 0, -1, -1, -1, -1} would apply to channels 0-3 the
|
||||
* same first defined response and leave channels 4-7 unequalized.
|
||||
* coefficient_data[]
|
||||
* <1st EQ>
|
||||
* uint32_t num_biquads
|
||||
* uint32_t num_biquads_in_series
|
||||
* <1st biquad>
|
||||
* int32_t coef_a2 Q2.30 format
|
||||
* int32_t coef_a1 Q2.30 format
|
||||
* int32_t coef_b2 Q2.30 format
|
||||
* int32_t coef_b1 Q2.30 format
|
||||
* int32_t coef_b0 Q2.30 format
|
||||
* int32_t output_shift number of shifts right, shift left is negative
|
||||
* int32_t output_gain Q2.14 format
|
||||
* <2nd biquad>
|
||||
* ...
|
||||
* <2nd EQ>
|
||||
*
|
||||
* Note: A flat response biquad can be made with a section set to
|
||||
* b0 = 1.0, gain = 1.0, and other parameters set to 0
|
||||
* {0, 0, 0, 0, 1073741824, 0, 16484}
|
||||
*/
|
||||
|
||||
struct sof_eq_iir_config {
|
||||
uint32_t size;
|
||||
uint32_t channels_in_config;
|
||||
uint32_t number_of_responses;
|
||||
|
||||
/* reserved */
|
||||
uint32_t reserved[4];
|
||||
|
||||
int32_t data[]; /* eq_assign[channels], eq 0, eq 1, ... */
|
||||
} __packed;
|
||||
|
||||
struct sof_eq_iir_header_df2t {
|
||||
uint32_t num_sections;
|
||||
uint32_t num_sections_in_series;
|
||||
|
||||
/* reserved */
|
||||
uint32_t reserved[4];
|
||||
|
||||
int32_t biquads[]; /* Repeated biquad coefficients */
|
||||
} __packed;
|
||||
|
||||
struct sof_eq_iir_biquad_df2t {
|
||||
int32_t a2; /* Q2.30 */
|
||||
int32_t a1; /* Q2.30 */
|
||||
int32_t b2; /* Q2.30 */
|
||||
int32_t b1; /* Q2.30 */
|
||||
int32_t b0; /* Q2.30 */
|
||||
int32_t output_shift; /* Number of right shifts */
|
||||
int32_t output_gain; /* Q2.14 */
|
||||
} __packed;
|
||||
|
||||
/* A full 22th order equalizer with 11 biquads cover octave bands 1-11 in
|
||||
* in the 0 - 20 kHz bandwidth.
|
||||
*/
|
||||
#define SOF_EQ_IIR_DF2T_BIQUADS_MAX 11
|
||||
|
||||
/* The number of int32_t words in sof_eq_iir_header_df2t:
|
||||
* num_sections, num_sections_in_series, reserved[4]
|
||||
*/
|
||||
#define SOF_EQ_IIR_NHEADER_DF2T \
|
||||
(sizeof(struct sof_eq_iir_header_df2t) / sizeof(int32_t))
|
||||
|
||||
/* The number of int32_t words in sof_eq_iir_biquad_df2t:
|
||||
* a2, a1, b2, b1, b0, output_shift, output_gain
|
||||
*/
|
||||
#define SOF_EQ_IIR_NBIQUAD_DF2T \
|
||||
(sizeof(struct sof_eq_iir_biquad_df2t) / sizeof(int32_t))
|
||||
|
||||
#endif
|
|
@ -1,188 +0,0 @@
|
|||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_UAPI_SOUND_SOF_USER_MANIFEST_H__
|
||||
#define __INCLUDE_UAPI_SOUND_SOF_USER_MANIFEST_H__
|
||||
|
||||
/* start offset for base FW module */
|
||||
#define SOF_MAN_ELF_TEXT_OFFSET 0x2000
|
||||
|
||||
/* FW Extended Manifest Header id = $AE1 */
|
||||
#define SOF_MAN_EXT_HEADER_MAGIC 0x31454124
|
||||
|
||||
/* module type load type */
|
||||
#define SOF_MAN_MOD_TYPE_BUILTIN 0
|
||||
#define SOF_MAN_MOD_TYPE_MODULE 1
|
||||
|
||||
struct sof_man_module_type {
|
||||
uint32_t load_type:4; /* SOF_MAN_MOD_TYPE_ */
|
||||
uint32_t auto_start:1;
|
||||
uint32_t domain_ll:1;
|
||||
uint32_t domain_dp:1;
|
||||
uint32_t rsvd_:25;
|
||||
};
|
||||
|
||||
/* segment flags.type */
|
||||
#define SOF_MAN_SEGMENT_TEXT 0
|
||||
#define SOF_MAN_SEGMENT_RODATA 1
|
||||
#define SOF_MAN_SEGMENT_DATA 1
|
||||
#define SOF_MAN_SEGMENT_BSS 2
|
||||
#define SOF_MAN_SEGMENT_EMPTY 15
|
||||
|
||||
union sof_man_segment_flags {
|
||||
uint32_t ul;
|
||||
struct {
|
||||
uint32_t contents:1;
|
||||
uint32_t alloc:1;
|
||||
uint32_t load:1;
|
||||
uint32_t readonly:1;
|
||||
uint32_t code:1;
|
||||
uint32_t data:1;
|
||||
uint32_t _rsvd0:2;
|
||||
uint32_t type:4; /* MAN_SEGMENT_ */
|
||||
uint32_t _rsvd1:4;
|
||||
uint32_t length:16; /* of segment in pages */
|
||||
} r;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Module segment descriptor. Used by ROM - Immutable.
|
||||
*/
|
||||
struct sof_man_segment_desc {
|
||||
union sof_man_segment_flags flags;
|
||||
uint32_t v_base_addr;
|
||||
uint32_t file_offset;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* The firmware binary can be split into several modules.
|
||||
*/
|
||||
|
||||
#define SOF_MAN_MOD_ID_LEN 4
|
||||
#define SOF_MAN_MOD_NAME_LEN 8
|
||||
#define SOF_MAN_MOD_SHA256_LEN 32
|
||||
#define SOF_MAN_MOD_ID {'$', 'A', 'M', 'E'}
|
||||
|
||||
/*
|
||||
* Each module has an entry in the FW header. Used by ROM - Immutable.
|
||||
*/
|
||||
struct sof_man_module {
|
||||
uint8_t struct_id[SOF_MAN_MOD_ID_LEN]; /* SOF_MAN_MOD_ID */
|
||||
uint8_t name[SOF_MAN_MOD_NAME_LEN];
|
||||
uint8_t uuid[16];
|
||||
struct sof_man_module_type type;
|
||||
uint8_t hash[SOF_MAN_MOD_SHA256_LEN];
|
||||
uint32_t entry_point;
|
||||
uint16_t cfg_offset;
|
||||
uint16_t cfg_count;
|
||||
uint32_t affinity_mask;
|
||||
uint16_t instance_max_count; /* max number of instances */
|
||||
uint16_t instance_bss_size; /* instance (pages) */
|
||||
struct sof_man_segment_desc segment[3];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Each module has a configuration in the FW header. Used by ROM - Immutable.
|
||||
*/
|
||||
struct sof_man_mod_config {
|
||||
uint32_t par[4]; /* module parameters */
|
||||
uint32_t is_pages; /* actual size of instance .bss (pages) */
|
||||
uint32_t cps; /* cycles per second */
|
||||
uint32_t ibs; /* input buffer size (bytes) */
|
||||
uint32_t obs; /* output buffer size (bytes) */
|
||||
uint32_t module_flags; /* flags, reserved for future use */
|
||||
uint32_t cpc; /* cycles per single run */
|
||||
uint32_t obls; /* output block size, reserved for future use */
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* FW Manifest Header
|
||||
*/
|
||||
|
||||
#define SOF_MAN_FW_HDR_FW_NAME_LEN 8
|
||||
#define SOF_MAN_FW_HDR_ID {'$', 'A', 'M', '1'}
|
||||
#define SOF_MAN_FW_HDR_NAME "ADSPFW"
|
||||
#define SOF_MAN_FW_HDR_FLAGS 0x0
|
||||
#define SOF_MAN_FW_HDR_FEATURES 0xff
|
||||
|
||||
/*
|
||||
* The firmware has a standard header that is checked by the ROM on firmware
|
||||
* loading. preload_page_count is used by DMA code loader and is entire
|
||||
* image size on CNL. i.e. CNL: total size of the binary’s .text and .rodata
|
||||
* Used by ROM - Immutable.
|
||||
*/
|
||||
struct sof_man_fw_header {
|
||||
uint8_t header_id[4];
|
||||
uint32_t header_len;
|
||||
uint8_t name[SOF_MAN_FW_HDR_FW_NAME_LEN];
|
||||
/* number of pages of preloaded image loaded by driver */
|
||||
uint32_t preload_page_count;
|
||||
uint32_t fw_image_flags;
|
||||
uint32_t feature_mask;
|
||||
uint16_t major_version;
|
||||
uint16_t minor_version;
|
||||
uint16_t hotfix_version;
|
||||
uint16_t build_version;
|
||||
uint32_t num_module_entries;
|
||||
uint32_t hw_buf_base_addr;
|
||||
uint32_t hw_buf_length;
|
||||
/* target address for binary loading as offset in IMR - must be == base offset */
|
||||
uint32_t load_offset;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Firmware manifest descriptor. This can contain N modules and N module
|
||||
* configs. Used by ROM - Immutable.
|
||||
*/
|
||||
struct sof_man_fw_desc {
|
||||
struct sof_man_fw_header header;
|
||||
|
||||
/* Warning - hack for module arrays. For some unknown reason the we
|
||||
* have a variable size array of struct man_module followed by a
|
||||
* variable size array of struct mod_config. These should have been
|
||||
* merged into a variable array of a parent structure. We have to hack
|
||||
* around this in many places....
|
||||
*
|
||||
* struct sof_man_module man_module[];
|
||||
* struct sof_man_mod_config mod_config[];
|
||||
*/
|
||||
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Component Descriptor. Used by ROM - Immutable.
|
||||
*/
|
||||
struct sof_man_component_desc {
|
||||
uint32_t reserved[2]; /* all 0 */
|
||||
uint32_t version;
|
||||
uint8_t hash[SOF_MAN_MOD_SHA256_LEN];
|
||||
uint32_t base_offset;
|
||||
uint32_t limit_offset;
|
||||
uint32_t attributes[4];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Audio DSP extended metadata. Used by ROM - Immutable.
|
||||
*/
|
||||
struct sof_man_adsp_meta_file_ext {
|
||||
uint32_t ext_type; /* always 17 for ADSP extension */
|
||||
uint32_t ext_len;
|
||||
uint32_t imr_type;
|
||||
uint8_t reserved[16]; /* all 0 */
|
||||
struct sof_man_component_desc comp_desc[1];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Module Manifest for rimage module metadata. Not used by ROM.
|
||||
*/
|
||||
struct sof_man_module_manifest {
|
||||
struct sof_man_module module;
|
||||
uint32_t text_size;
|
||||
} __packed;
|
||||
|
||||
#endif
|
|
@ -85,6 +85,7 @@
|
|||
#define SOF_TKN_INTEL_DMIC_NUM_PDM_ACTIVE 605
|
||||
#define SOF_TKN_INTEL_DMIC_SAMPLE_RATE 608
|
||||
#define SOF_TKN_INTEL_DMIC_FIFO_WORD_LENGTH 609
|
||||
#define SOF_TKN_INTEL_DMIC_UNMUTE_RAMP_TIME_MS 610
|
||||
|
||||
/* DMIC PDM */
|
||||
#define SOF_TKN_INTEL_DMIC_PDM_CTRL_ID 700
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_UAPI_SOUND_SOF_USER_TONE_H__
|
||||
#define __INCLUDE_UAPI_SOUND_SOF_USER_TONE_H__
|
||||
|
||||
#define SOF_TONE_IDX_FREQUENCY 0
|
||||
#define SOF_TONE_IDX_AMPLITUDE 1
|
||||
#define SOF_TONE_IDX_FREQ_MULT 2
|
||||
#define SOF_TONE_IDX_AMPL_MULT 3
|
||||
#define SOF_TONE_IDX_LENGTH 4
|
||||
#define SOF_TONE_IDX_PERIOD 5
|
||||
#define SOF_TONE_IDX_REPEATS 6
|
||||
#define SOF_TONE_IDX_LIN_RAMP_STEP 7
|
||||
|
||||
#endif
|
|
@ -1,66 +0,0 @@
|
|||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_UAPI_SOUND_SOF_USER_TRACE_H__
|
||||
#define __INCLUDE_UAPI_SOUND_SOF_USER_TRACE_H__
|
||||
|
||||
/*
|
||||
* Host system time.
|
||||
*
|
||||
* This property is used by the driver to pass down information about
|
||||
* current system time. It is expressed in us.
|
||||
* FW translates timestamps (in log entries, probe pockets) to this time
|
||||
* domain.
|
||||
*
|
||||
* (cavs: SystemTime).
|
||||
*/
|
||||
struct system_time {
|
||||
uint32_t val_l; /* Lower dword of current host time value */
|
||||
uint32_t val_u; /* Upper dword of current host time value */
|
||||
} __packed;
|
||||
|
||||
#define LOG_ENABLE 1 /* Enable logging */
|
||||
#define LOG_DISABLE 0 /* Disable logging */
|
||||
|
||||
#define LOG_LEVEL_CRITICAL 1 /* (FDK fatal) */
|
||||
#define LOG_LEVEL_VERBOSE 2
|
||||
|
||||
/*
|
||||
* Layout of a log fifo.
|
||||
*/
|
||||
struct log_buffer_layout {
|
||||
uint32_t read_ptr; /*read pointer */
|
||||
uint32_t write_ptr; /* write pointer */
|
||||
uint32_t buffer[0]; /* buffer */
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Log buffer status reported by FW.
|
||||
*/
|
||||
struct log_buffer_status {
|
||||
uint32_t core_id; /* ID of core that logged to other half */
|
||||
} __packed;
|
||||
|
||||
#define TRACE_ID_LENGTH 12
|
||||
|
||||
/*
|
||||
* Log entry header.
|
||||
*
|
||||
* The header is followed by an array of arguments (uint32_t[]).
|
||||
* Number of arguments is specified by the params_num field of log_entry
|
||||
*/
|
||||
struct log_entry_header {
|
||||
uint32_t id_0 : TRACE_ID_LENGTH; /* e.g. Pipeline ID */
|
||||
uint32_t id_1 : TRACE_ID_LENGTH; /* e.g. Component ID */
|
||||
uint32_t core_id : 8; /* Reporting core's id */
|
||||
|
||||
uint64_t timestamp; /* Timestamp (in dsp ticks) */
|
||||
uint32_t log_entry_address; /* Address of log entry in ELF */
|
||||
} __packed;
|
||||
|
||||
#endif
|
|
@ -196,16 +196,12 @@ EXPORT_SYMBOL(snd_ctl_notify);
|
|||
static int snd_ctl_new(struct snd_kcontrol **kctl, unsigned int count,
|
||||
unsigned int access, struct snd_ctl_file *file)
|
||||
{
|
||||
unsigned int size;
|
||||
unsigned int idx;
|
||||
|
||||
if (count == 0 || count > MAX_CONTROL_COUNT)
|
||||
return -EINVAL;
|
||||
|
||||
size = sizeof(struct snd_kcontrol);
|
||||
size += sizeof(struct snd_kcontrol_volatile) * count;
|
||||
|
||||
*kctl = kzalloc(size, GFP_KERNEL);
|
||||
*kctl = kzalloc(struct_size(*kctl, vd, count), GFP_KERNEL);
|
||||
if (!*kctl)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
|
@ -323,8 +323,8 @@ int snd_pcm_plugin_build_rate(struct snd_pcm_substream *plug,
|
|||
|
||||
err = snd_pcm_plugin_build(plug, "rate conversion",
|
||||
src_format, dst_format,
|
||||
sizeof(struct rate_priv) +
|
||||
src_format->channels * sizeof(struct rate_channel),
|
||||
struct_size(data, channels,
|
||||
src_format->channels),
|
||||
&plugin);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
|
|
@ -82,7 +82,7 @@ int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate,
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
s->fdf = AMDTP_FDF_AM824 | s->sfc;
|
||||
s->ctx_data.rx.fdf = AMDTP_FDF_AM824 | s->sfc;
|
||||
|
||||
p->pcm_channels = pcm_channels;
|
||||
p->midi_ports = midi_ports;
|
||||
|
@ -320,7 +320,7 @@ static void read_midi_messages(struct amdtp_stream *s,
|
|||
u8 *b;
|
||||
|
||||
for (f = 0; f < frames; f++) {
|
||||
port = (8 - s->tx_first_dbc + s->data_block_counter + f) % 8;
|
||||
port = (8 - s->ctx_data.tx.first_dbc + s->data_block_counter + f) % 8;
|
||||
b = (u8 *)&buffer[p->midi_position];
|
||||
|
||||
len = b[0] - 0x80;
|
||||
|
|
|
@ -13,103 +13,16 @@
|
|||
|
||||
#include <linux/tracepoint.h>
|
||||
|
||||
TRACE_EVENT(in_packet,
|
||||
TP_PROTO(const struct amdtp_stream *s, u32 cycles, u32 *cip_header, unsigned int payload_length, unsigned int index),
|
||||
TP_ARGS(s, cycles, cip_header, payload_length, index),
|
||||
TP_STRUCT__entry(
|
||||
__field(unsigned int, second)
|
||||
__field(unsigned int, cycle)
|
||||
__field(int, channel)
|
||||
__field(int, src)
|
||||
__field(int, dest)
|
||||
__field(u32, cip_header0)
|
||||
__field(u32, cip_header1)
|
||||
__field(unsigned int, payload_quadlets)
|
||||
__field(unsigned int, packet_index)
|
||||
__field(unsigned int, irq)
|
||||
__field(unsigned int, index)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->second = cycles / CYCLES_PER_SECOND;
|
||||
__entry->cycle = cycles % CYCLES_PER_SECOND;
|
||||
__entry->channel = s->context->channel;
|
||||
__entry->src = fw_parent_device(s->unit)->node_id;
|
||||
__entry->dest = fw_parent_device(s->unit)->card->node_id;
|
||||
__entry->cip_header0 = cip_header[0];
|
||||
__entry->cip_header1 = cip_header[1];
|
||||
__entry->payload_quadlets = payload_length / 4;
|
||||
__entry->packet_index = s->packet_index;
|
||||
__entry->irq = !!in_interrupt();
|
||||
__entry->index = index;
|
||||
),
|
||||
TP_printk(
|
||||
"%02u %04u %04x %04x %02d %08x %08x %03u %02u %01u %02u",
|
||||
__entry->second,
|
||||
__entry->cycle,
|
||||
__entry->src,
|
||||
__entry->dest,
|
||||
__entry->channel,
|
||||
__entry->cip_header0,
|
||||
__entry->cip_header1,
|
||||
__entry->payload_quadlets,
|
||||
__entry->packet_index,
|
||||
__entry->irq,
|
||||
__entry->index)
|
||||
);
|
||||
|
||||
TRACE_EVENT(out_packet,
|
||||
TP_PROTO(const struct amdtp_stream *s, u32 cycles, __be32 *cip_header, unsigned int payload_length, unsigned int index),
|
||||
TP_ARGS(s, cycles, cip_header, payload_length, index),
|
||||
TP_STRUCT__entry(
|
||||
__field(unsigned int, second)
|
||||
__field(unsigned int, cycle)
|
||||
__field(int, channel)
|
||||
__field(int, src)
|
||||
__field(int, dest)
|
||||
__field(u32, cip_header0)
|
||||
__field(u32, cip_header1)
|
||||
__field(unsigned int, payload_quadlets)
|
||||
__field(unsigned int, packet_index)
|
||||
__field(unsigned int, irq)
|
||||
__field(unsigned int, index)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->second = cycles / CYCLES_PER_SECOND;
|
||||
__entry->cycle = cycles % CYCLES_PER_SECOND;
|
||||
__entry->channel = s->context->channel;
|
||||
__entry->src = fw_parent_device(s->unit)->card->node_id;
|
||||
__entry->dest = fw_parent_device(s->unit)->node_id;
|
||||
__entry->cip_header0 = be32_to_cpu(cip_header[0]);
|
||||
__entry->cip_header1 = be32_to_cpu(cip_header[1]);
|
||||
__entry->payload_quadlets = payload_length / 4;
|
||||
__entry->packet_index = s->packet_index;
|
||||
__entry->irq = !!in_interrupt();
|
||||
__entry->index = index;
|
||||
),
|
||||
TP_printk(
|
||||
"%02u %04u %04x %04x %02d %08x %08x %03u %02u %01u %02u",
|
||||
__entry->second,
|
||||
__entry->cycle,
|
||||
__entry->src,
|
||||
__entry->dest,
|
||||
__entry->channel,
|
||||
__entry->cip_header0,
|
||||
__entry->cip_header1,
|
||||
__entry->payload_quadlets,
|
||||
__entry->packet_index,
|
||||
__entry->irq,
|
||||
__entry->index)
|
||||
);
|
||||
|
||||
TRACE_EVENT(in_packet_without_header,
|
||||
TP_PROTO(const struct amdtp_stream *s, u32 cycles, unsigned int payload_quadlets, unsigned int data_blocks, unsigned int index),
|
||||
TP_ARGS(s, cycles, payload_quadlets, data_blocks, index),
|
||||
TRACE_EVENT(amdtp_packet,
|
||||
TP_PROTO(const struct amdtp_stream *s, u32 cycles, const __be32 *cip_header, unsigned int payload_length, unsigned int data_blocks, unsigned int index),
|
||||
TP_ARGS(s, cycles, cip_header, payload_length, data_blocks, index),
|
||||
TP_STRUCT__entry(
|
||||
__field(unsigned int, second)
|
||||
__field(unsigned int, cycle)
|
||||
__field(int, channel)
|
||||
__field(int, src)
|
||||
__field(int, dest)
|
||||
__dynamic_array(u8, cip_header, cip_header ? 8 : 0)
|
||||
__field(unsigned int, payload_quadlets)
|
||||
__field(unsigned int, data_blocks)
|
||||
__field(unsigned int, data_block_counter)
|
||||
|
@ -121,17 +34,26 @@ TRACE_EVENT(in_packet_without_header,
|
|||
__entry->second = cycles / CYCLES_PER_SECOND;
|
||||
__entry->cycle = cycles % CYCLES_PER_SECOND;
|
||||
__entry->channel = s->context->channel;
|
||||
__entry->src = fw_parent_device(s->unit)->node_id;
|
||||
__entry->dest = fw_parent_device(s->unit)->card->node_id;
|
||||
__entry->payload_quadlets = payload_quadlets;
|
||||
__entry->data_blocks = data_blocks,
|
||||
if (s->direction == AMDTP_IN_STREAM) {
|
||||
__entry->src = fw_parent_device(s->unit)->node_id;
|
||||
__entry->dest = fw_parent_device(s->unit)->card->node_id;
|
||||
} else {
|
||||
__entry->src = fw_parent_device(s->unit)->card->node_id;
|
||||
__entry->dest = fw_parent_device(s->unit)->node_id;
|
||||
}
|
||||
if (cip_header) {
|
||||
memcpy(__get_dynamic_array(cip_header), cip_header,
|
||||
__get_dynamic_array_len(cip_header));
|
||||
}
|
||||
__entry->payload_quadlets = payload_length / sizeof(__be32);
|
||||
__entry->data_blocks = data_blocks;
|
||||
__entry->data_block_counter = s->data_block_counter,
|
||||
__entry->packet_index = s->packet_index;
|
||||
__entry->irq = !!in_interrupt();
|
||||
__entry->index = index;
|
||||
),
|
||||
TP_printk(
|
||||
"%02u %04u %04x %04x %02d %03u %02u %03u %02u %01u %02u",
|
||||
"%02u %04u %04x %04x %02d %03u %02u %03u %02u %01u %02u %s",
|
||||
__entry->second,
|
||||
__entry->cycle,
|
||||
__entry->src,
|
||||
|
@ -142,51 +64,10 @@ TRACE_EVENT(in_packet_without_header,
|
|||
__entry->data_block_counter,
|
||||
__entry->packet_index,
|
||||
__entry->irq,
|
||||
__entry->index)
|
||||
);
|
||||
|
||||
TRACE_EVENT(out_packet_without_header,
|
||||
TP_PROTO(const struct amdtp_stream *s, u32 cycles, unsigned int payload_length, unsigned int data_blocks, unsigned int index),
|
||||
TP_ARGS(s, cycles, payload_length, data_blocks, index),
|
||||
TP_STRUCT__entry(
|
||||
__field(unsigned int, second)
|
||||
__field(unsigned int, cycle)
|
||||
__field(int, channel)
|
||||
__field(int, src)
|
||||
__field(int, dest)
|
||||
__field(unsigned int, payload_quadlets)
|
||||
__field(unsigned int, data_blocks)
|
||||
__field(unsigned int, data_block_counter)
|
||||
__field(unsigned int, packet_index)
|
||||
__field(unsigned int, irq)
|
||||
__field(unsigned int, index)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->second = cycles / CYCLES_PER_SECOND;
|
||||
__entry->cycle = cycles % CYCLES_PER_SECOND;
|
||||
__entry->channel = s->context->channel;
|
||||
__entry->src = fw_parent_device(s->unit)->card->node_id;
|
||||
__entry->dest = fw_parent_device(s->unit)->node_id;
|
||||
__entry->payload_quadlets = payload_length / 4;
|
||||
__entry->data_blocks = data_blocks,
|
||||
__entry->data_block_counter = s->data_block_counter,
|
||||
__entry->packet_index = s->packet_index;
|
||||
__entry->irq = !!in_interrupt();
|
||||
__entry->index = index;
|
||||
),
|
||||
TP_printk(
|
||||
"%02u %04u %04x %04x %02d %03u %02u %03u %02u %01u %02u",
|
||||
__entry->second,
|
||||
__entry->cycle,
|
||||
__entry->src,
|
||||
__entry->dest,
|
||||
__entry->channel,
|
||||
__entry->payload_quadlets,
|
||||
__entry->data_blocks,
|
||||
__entry->data_block_counter,
|
||||
__entry->packet_index,
|
||||
__entry->irq,
|
||||
__entry->index)
|
||||
__entry->index,
|
||||
__print_array(__get_dynamic_array(cip_header),
|
||||
__get_dynamic_array_len(cip_header),
|
||||
sizeof(u8)))
|
||||
);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -56,10 +56,15 @@
|
|||
#define INTERRUPT_INTERVAL 16
|
||||
#define QUEUE_LENGTH 48
|
||||
|
||||
#define IR_HEADER_SIZE 8 // For header and timestamp.
|
||||
#define OUT_PACKET_HEADER_SIZE 0
|
||||
// For iso header, tstamp and 2 CIP header.
|
||||
#define IR_CTX_HEADER_SIZE_CIP 16
|
||||
// For iso header and tstamp.
|
||||
#define IR_CTX_HEADER_SIZE_NO_CIP 8
|
||||
#define HEADER_TSTAMP_MASK 0x0000ffff
|
||||
|
||||
#define IT_PKT_HEADER_SIZE_CIP 8 // For 2 CIP header.
|
||||
#define IT_PKT_HEADER_SIZE_NO_CIP 0 // Nothing.
|
||||
|
||||
static void pcm_period_tasklet(unsigned long data);
|
||||
|
||||
/**
|
||||
|
@ -260,11 +265,18 @@ int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate,
|
|||
s->data_block_quadlets = data_block_quadlets;
|
||||
s->syt_interval = amdtp_syt_intervals[sfc];
|
||||
|
||||
/* default buffering in the device */
|
||||
s->transfer_delay = TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE;
|
||||
if (s->flags & CIP_BLOCKING)
|
||||
/* additional buffering needed to adjust for no-data packets */
|
||||
s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate;
|
||||
// default buffering in the device.
|
||||
if (s->direction == AMDTP_OUT_STREAM) {
|
||||
s->ctx_data.rx.transfer_delay =
|
||||
TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE;
|
||||
|
||||
if (s->flags & CIP_BLOCKING) {
|
||||
// additional buffering needed to adjust for no-data
|
||||
// packets.
|
||||
s->ctx_data.rx.transfer_delay +=
|
||||
TICKS_PER_SECOND * s->syt_interval / rate;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -280,15 +292,15 @@ EXPORT_SYMBOL(amdtp_stream_set_parameters);
|
|||
unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s)
|
||||
{
|
||||
unsigned int multiplier = 1;
|
||||
unsigned int header_size = 0;
|
||||
unsigned int cip_header_size = 0;
|
||||
|
||||
if (s->flags & CIP_JUMBO_PAYLOAD)
|
||||
multiplier = 5;
|
||||
if (!(s->flags & CIP_NO_HEADER))
|
||||
header_size = 8;
|
||||
cip_header_size = sizeof(__be32) * 2;
|
||||
|
||||
return header_size +
|
||||
s->syt_interval * s->data_block_quadlets * 4 * multiplier;
|
||||
return cip_header_size +
|
||||
s->syt_interval * s->data_block_quadlets * sizeof(__be32) * multiplier;
|
||||
}
|
||||
EXPORT_SYMBOL(amdtp_stream_get_max_payload);
|
||||
|
||||
|
@ -321,10 +333,10 @@ static unsigned int calculate_data_blocks(struct amdtp_stream *s,
|
|||
/* Non-blocking mode. */
|
||||
} else {
|
||||
if (!cip_sfc_is_base_44100(s->sfc)) {
|
||||
/* Sample_rate / 8000 is an integer, and precomputed. */
|
||||
data_blocks = s->data_block_state;
|
||||
// Sample_rate / 8000 is an integer, and precomputed.
|
||||
data_blocks = s->ctx_data.rx.data_block_state;
|
||||
} else {
|
||||
phase = s->data_block_state;
|
||||
phase = s->ctx_data.rx.data_block_state;
|
||||
|
||||
/*
|
||||
* This calculates the number of data blocks per packet so that
|
||||
|
@ -343,7 +355,7 @@ static unsigned int calculate_data_blocks(struct amdtp_stream *s,
|
|||
data_blocks = 11 * (s->sfc >> 1) + (phase == 0);
|
||||
if (++phase >= (80 >> (s->sfc >> 1)))
|
||||
phase = 0;
|
||||
s->data_block_state = phase;
|
||||
s->ctx_data.rx.data_block_state = phase;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -355,9 +367,10 @@ static unsigned int calculate_syt(struct amdtp_stream *s,
|
|||
{
|
||||
unsigned int syt_offset, phase, index, syt;
|
||||
|
||||
if (s->last_syt_offset < TICKS_PER_CYCLE) {
|
||||
if (s->ctx_data.rx.last_syt_offset < TICKS_PER_CYCLE) {
|
||||
if (!cip_sfc_is_base_44100(s->sfc))
|
||||
syt_offset = s->last_syt_offset + s->syt_offset_state;
|
||||
syt_offset = s->ctx_data.rx.last_syt_offset +
|
||||
s->ctx_data.rx.syt_offset_state;
|
||||
else {
|
||||
/*
|
||||
* The time, in ticks, of the n'th SYT_INTERVAL sample is:
|
||||
|
@ -369,21 +382,21 @@ static unsigned int calculate_syt(struct amdtp_stream *s,
|
|||
* 1386 1386 1387 1386 1386 1386 1387 1386 1386 1386 1387 ...
|
||||
* This code generates _exactly_ the same sequence.
|
||||
*/
|
||||
phase = s->syt_offset_state;
|
||||
phase = s->ctx_data.rx.syt_offset_state;
|
||||
index = phase % 13;
|
||||
syt_offset = s->last_syt_offset;
|
||||
syt_offset = s->ctx_data.rx.last_syt_offset;
|
||||
syt_offset += 1386 + ((index && !(index & 3)) ||
|
||||
phase == 146);
|
||||
if (++phase >= 147)
|
||||
phase = 0;
|
||||
s->syt_offset_state = phase;
|
||||
s->ctx_data.rx.syt_offset_state = phase;
|
||||
}
|
||||
} else
|
||||
syt_offset = s->last_syt_offset - TICKS_PER_CYCLE;
|
||||
s->last_syt_offset = syt_offset;
|
||||
syt_offset = s->ctx_data.rx.last_syt_offset - TICKS_PER_CYCLE;
|
||||
s->ctx_data.rx.last_syt_offset = syt_offset;
|
||||
|
||||
if (syt_offset < TICKS_PER_CYCLE) {
|
||||
syt_offset += s->transfer_delay;
|
||||
syt_offset += s->ctx_data.rx.transfer_delay;
|
||||
syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12;
|
||||
syt += syt_offset % TICKS_PER_CYCLE;
|
||||
|
||||
|
@ -420,23 +433,15 @@ static void pcm_period_tasklet(unsigned long data)
|
|||
snd_pcm_period_elapsed(pcm);
|
||||
}
|
||||
|
||||
static int queue_packet(struct amdtp_stream *s, unsigned int header_length,
|
||||
unsigned int payload_length)
|
||||
static int queue_packet(struct amdtp_stream *s, struct fw_iso_packet *params)
|
||||
{
|
||||
struct fw_iso_packet p = {0};
|
||||
int err = 0;
|
||||
int err;
|
||||
|
||||
if (IS_ERR(s->context))
|
||||
goto end;
|
||||
params->interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL);
|
||||
params->tag = s->tag;
|
||||
params->sy = 0;
|
||||
|
||||
p.interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL);
|
||||
p.tag = s->tag;
|
||||
p.header_length = header_length;
|
||||
if (payload_length > 0)
|
||||
p.payload_length = payload_length;
|
||||
else
|
||||
p.skip = true;
|
||||
err = fw_iso_context_queue(s->context, &p, &s->buffer.iso_buffer,
|
||||
err = fw_iso_context_queue(s->context, params, &s->buffer.iso_buffer,
|
||||
s->buffer.packets[s->packet_index].offset);
|
||||
if (err < 0) {
|
||||
dev_err(&s->unit->device, "queueing error: %d\n", err);
|
||||
|
@ -450,112 +455,83 @@ static int queue_packet(struct amdtp_stream *s, unsigned int header_length,
|
|||
}
|
||||
|
||||
static inline int queue_out_packet(struct amdtp_stream *s,
|
||||
unsigned int payload_length)
|
||||
struct fw_iso_packet *params)
|
||||
{
|
||||
return queue_packet(s, OUT_PACKET_HEADER_SIZE, payload_length);
|
||||
params->skip =
|
||||
!!(params->header_length == 0 && params->payload_length == 0);
|
||||
return queue_packet(s, params);
|
||||
}
|
||||
|
||||
static inline int queue_in_packet(struct amdtp_stream *s)
|
||||
static inline int queue_in_packet(struct amdtp_stream *s,
|
||||
struct fw_iso_packet *params)
|
||||
{
|
||||
return queue_packet(s, IR_HEADER_SIZE, s->max_payload_length);
|
||||
// Queue one packet for IR context.
|
||||
params->header_length = s->ctx_data.tx.ctx_header_size;
|
||||
params->payload_length = s->ctx_data.tx.max_ctx_payload_length;
|
||||
params->skip = false;
|
||||
return queue_packet(s, params);
|
||||
}
|
||||
|
||||
static int handle_out_packet(struct amdtp_stream *s,
|
||||
unsigned int payload_length, unsigned int cycle,
|
||||
unsigned int index)
|
||||
static void generate_cip_header(struct amdtp_stream *s, __be32 cip_header[2],
|
||||
unsigned int syt)
|
||||
{
|
||||
__be32 *buffer;
|
||||
unsigned int syt;
|
||||
unsigned int data_blocks;
|
||||
unsigned int pcm_frames;
|
||||
struct snd_pcm_substream *pcm;
|
||||
|
||||
buffer = s->buffer.packets[s->packet_index].buffer;
|
||||
syt = calculate_syt(s, cycle);
|
||||
data_blocks = calculate_data_blocks(s, syt);
|
||||
pcm_frames = s->process_data_blocks(s, buffer + 2, data_blocks, &syt);
|
||||
|
||||
if (s->flags & CIP_DBC_IS_END_EVENT)
|
||||
s->data_block_counter =
|
||||
(s->data_block_counter + data_blocks) & 0xff;
|
||||
|
||||
buffer[0] = cpu_to_be32(READ_ONCE(s->source_node_id_field) |
|
||||
cip_header[0] = cpu_to_be32(READ_ONCE(s->source_node_id_field) |
|
||||
(s->data_block_quadlets << CIP_DBS_SHIFT) |
|
||||
((s->sph << CIP_SPH_SHIFT) & CIP_SPH_MASK) |
|
||||
s->data_block_counter);
|
||||
buffer[1] = cpu_to_be32(CIP_EOH |
|
||||
((s->fmt << CIP_FMT_SHIFT) & CIP_FMT_MASK) |
|
||||
((s->fdf << CIP_FDF_SHIFT) & CIP_FDF_MASK) |
|
||||
(syt & CIP_SYT_MASK));
|
||||
cip_header[1] = cpu_to_be32(CIP_EOH |
|
||||
((s->fmt << CIP_FMT_SHIFT) & CIP_FMT_MASK) |
|
||||
((s->ctx_data.rx.fdf << CIP_FDF_SHIFT) & CIP_FDF_MASK) |
|
||||
(syt & CIP_SYT_MASK));
|
||||
}
|
||||
|
||||
if (!(s->flags & CIP_DBC_IS_END_EVENT))
|
||||
static void build_it_pkt_header(struct amdtp_stream *s, unsigned int cycle,
|
||||
struct fw_iso_packet *params,
|
||||
unsigned int data_blocks, unsigned int syt,
|
||||
unsigned int index)
|
||||
{
|
||||
unsigned int payload_length;
|
||||
__be32 *cip_header;
|
||||
|
||||
payload_length = data_blocks * sizeof(__be32) * s->data_block_quadlets;
|
||||
params->payload_length = payload_length;
|
||||
|
||||
if (s->flags & CIP_DBC_IS_END_EVENT) {
|
||||
s->data_block_counter =
|
||||
(s->data_block_counter + data_blocks) & 0xff;
|
||||
payload_length = 8 + data_blocks * 4 * s->data_block_quadlets;
|
||||
}
|
||||
|
||||
trace_out_packet(s, cycle, buffer, payload_length, index);
|
||||
if (!(s->flags & CIP_NO_HEADER)) {
|
||||
cip_header = (__be32 *)params->header;
|
||||
generate_cip_header(s, cip_header, syt);
|
||||
params->header_length = 2 * sizeof(__be32);
|
||||
payload_length += params->header_length;
|
||||
} else {
|
||||
cip_header = NULL;
|
||||
}
|
||||
|
||||
if (queue_out_packet(s, payload_length) < 0)
|
||||
return -EIO;
|
||||
trace_amdtp_packet(s, cycle, cip_header, payload_length, data_blocks,
|
||||
index);
|
||||
|
||||
pcm = READ_ONCE(s->pcm);
|
||||
if (pcm && pcm_frames > 0)
|
||||
update_pcm_pointers(s, pcm, pcm_frames);
|
||||
|
||||
/* No need to return the number of handled data blocks. */
|
||||
return 0;
|
||||
if (!(s->flags & CIP_DBC_IS_END_EVENT)) {
|
||||
s->data_block_counter =
|
||||
(s->data_block_counter + data_blocks) & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
static int handle_out_packet_without_header(struct amdtp_stream *s,
|
||||
unsigned int payload_length, unsigned int cycle,
|
||||
unsigned int index)
|
||||
static int check_cip_header(struct amdtp_stream *s, const __be32 *buf,
|
||||
unsigned int payload_length,
|
||||
unsigned int *data_blocks, unsigned int *dbc,
|
||||
unsigned int *syt)
|
||||
{
|
||||
__be32 *buffer;
|
||||
unsigned int syt;
|
||||
unsigned int data_blocks;
|
||||
unsigned int pcm_frames;
|
||||
struct snd_pcm_substream *pcm;
|
||||
|
||||
buffer = s->buffer.packets[s->packet_index].buffer;
|
||||
syt = calculate_syt(s, cycle);
|
||||
data_blocks = calculate_data_blocks(s, syt);
|
||||
pcm_frames = s->process_data_blocks(s, buffer, data_blocks, &syt);
|
||||
s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
|
||||
|
||||
payload_length = data_blocks * 4 * s->data_block_quadlets;
|
||||
|
||||
trace_out_packet_without_header(s, cycle, payload_length, data_blocks,
|
||||
index);
|
||||
|
||||
if (queue_out_packet(s, payload_length) < 0)
|
||||
return -EIO;
|
||||
|
||||
pcm = READ_ONCE(s->pcm);
|
||||
if (pcm && pcm_frames > 0)
|
||||
update_pcm_pointers(s, pcm, pcm_frames);
|
||||
|
||||
/* No need to return the number of handled data blocks. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_in_packet(struct amdtp_stream *s,
|
||||
unsigned int payload_length, unsigned int cycle,
|
||||
unsigned int index)
|
||||
{
|
||||
__be32 *buffer;
|
||||
u32 cip_header[2];
|
||||
unsigned int sph, fmt, fdf, syt;
|
||||
unsigned int data_block_quadlets, data_block_counter, dbc_interval;
|
||||
unsigned int data_blocks;
|
||||
struct snd_pcm_substream *pcm;
|
||||
unsigned int pcm_frames;
|
||||
unsigned int sph;
|
||||
unsigned int fmt;
|
||||
unsigned int fdf;
|
||||
bool lost;
|
||||
|
||||
buffer = s->buffer.packets[s->packet_index].buffer;
|
||||
cip_header[0] = be32_to_cpu(buffer[0]);
|
||||
cip_header[1] = be32_to_cpu(buffer[1]);
|
||||
|
||||
trace_in_packet(s, cycle, cip_header, payload_length, index);
|
||||
cip_header[0] = be32_to_cpu(buf[0]);
|
||||
cip_header[1] = be32_to_cpu(buf[1]);
|
||||
|
||||
/*
|
||||
* This module supports 'Two-quadlet CIP header with SYT field'.
|
||||
|
@ -567,9 +543,7 @@ static int handle_in_packet(struct amdtp_stream *s,
|
|||
dev_info_ratelimited(&s->unit->device,
|
||||
"Invalid CIP header for AMDTP: %08X:%08X\n",
|
||||
cip_header[0], cip_header[1]);
|
||||
data_blocks = 0;
|
||||
pcm_frames = 0;
|
||||
goto end;
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/* Check valid protocol or not. */
|
||||
|
@ -579,19 +553,17 @@ static int handle_in_packet(struct amdtp_stream *s,
|
|||
dev_info_ratelimited(&s->unit->device,
|
||||
"Detect unexpected protocol: %08x %08x\n",
|
||||
cip_header[0], cip_header[1]);
|
||||
data_blocks = 0;
|
||||
pcm_frames = 0;
|
||||
goto end;
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/* Calculate data blocks */
|
||||
fdf = (cip_header[1] & CIP_FDF_MASK) >> CIP_FDF_SHIFT;
|
||||
if (payload_length < 12 ||
|
||||
if (payload_length < sizeof(__be32) * 2 ||
|
||||
(fmt == CIP_FMT_AM && fdf == AMDTP_FDF_NO_DATA)) {
|
||||
data_blocks = 0;
|
||||
*data_blocks = 0;
|
||||
} else {
|
||||
data_block_quadlets =
|
||||
(cip_header[0] & CIP_DBS_MASK) >> CIP_DBS_SHIFT;
|
||||
unsigned int data_block_quadlets =
|
||||
(cip_header[0] & CIP_DBS_MASK) >> CIP_DBS_SHIFT;
|
||||
/* avoid division by zero */
|
||||
if (data_block_quadlets == 0) {
|
||||
dev_err(&s->unit->device,
|
||||
|
@ -602,95 +574,97 @@ static int handle_in_packet(struct amdtp_stream *s,
|
|||
if (s->flags & CIP_WRONG_DBS)
|
||||
data_block_quadlets = s->data_block_quadlets;
|
||||
|
||||
data_blocks = (payload_length / 4 - 2) /
|
||||
*data_blocks = (payload_length / sizeof(__be32) - 2) /
|
||||
data_block_quadlets;
|
||||
}
|
||||
|
||||
/* Check data block counter continuity */
|
||||
data_block_counter = cip_header[0] & CIP_DBC_MASK;
|
||||
if (data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) &&
|
||||
*dbc = cip_header[0] & CIP_DBC_MASK;
|
||||
if (*data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) &&
|
||||
s->data_block_counter != UINT_MAX)
|
||||
data_block_counter = s->data_block_counter;
|
||||
*dbc = s->data_block_counter;
|
||||
|
||||
if (((s->flags & CIP_SKIP_DBC_ZERO_CHECK) &&
|
||||
data_block_counter == s->tx_first_dbc) ||
|
||||
*dbc == s->ctx_data.tx.first_dbc) ||
|
||||
s->data_block_counter == UINT_MAX) {
|
||||
lost = false;
|
||||
} else if (!(s->flags & CIP_DBC_IS_END_EVENT)) {
|
||||
lost = data_block_counter != s->data_block_counter;
|
||||
lost = *dbc != s->data_block_counter;
|
||||
} else {
|
||||
if (data_blocks > 0 && s->tx_dbc_interval > 0)
|
||||
dbc_interval = s->tx_dbc_interval;
|
||||
else
|
||||
dbc_interval = data_blocks;
|
||||
unsigned int dbc_interval;
|
||||
|
||||
lost = data_block_counter !=
|
||||
((s->data_block_counter + dbc_interval) & 0xff);
|
||||
if (*data_blocks > 0 && s->ctx_data.tx.dbc_interval > 0)
|
||||
dbc_interval = s->ctx_data.tx.dbc_interval;
|
||||
else
|
||||
dbc_interval = *data_blocks;
|
||||
|
||||
lost = *dbc != ((s->data_block_counter + dbc_interval) & 0xff);
|
||||
}
|
||||
|
||||
if (lost) {
|
||||
dev_err(&s->unit->device,
|
||||
"Detect discontinuity of CIP: %02X %02X\n",
|
||||
s->data_block_counter, data_block_counter);
|
||||
s->data_block_counter, *dbc);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK;
|
||||
pcm_frames = s->process_data_blocks(s, buffer + 2, data_blocks, &syt);
|
||||
|
||||
if (s->flags & CIP_DBC_IS_END_EVENT)
|
||||
s->data_block_counter = data_block_counter;
|
||||
else
|
||||
s->data_block_counter =
|
||||
(data_block_counter + data_blocks) & 0xff;
|
||||
end:
|
||||
if (queue_in_packet(s) < 0)
|
||||
return -EIO;
|
||||
|
||||
pcm = READ_ONCE(s->pcm);
|
||||
if (pcm && pcm_frames > 0)
|
||||
update_pcm_pointers(s, pcm, pcm_frames);
|
||||
*syt = cip_header[1] & CIP_SYT_MASK;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_in_packet_without_header(struct amdtp_stream *s,
|
||||
unsigned int payload_length, unsigned int cycle,
|
||||
unsigned int index)
|
||||
static int parse_ir_ctx_header(struct amdtp_stream *s, unsigned int cycle,
|
||||
const __be32 *ctx_header,
|
||||
unsigned int *payload_length,
|
||||
unsigned int *data_blocks, unsigned int *syt,
|
||||
unsigned int index)
|
||||
{
|
||||
__be32 *buffer;
|
||||
unsigned int payload_quadlets;
|
||||
unsigned int data_blocks;
|
||||
struct snd_pcm_substream *pcm;
|
||||
unsigned int pcm_frames;
|
||||
unsigned int dbc;
|
||||
const __be32 *cip_header;
|
||||
int err;
|
||||
|
||||
buffer = s->buffer.packets[s->packet_index].buffer;
|
||||
payload_quadlets = payload_length / 4;
|
||||
data_blocks = payload_quadlets / s->data_block_quadlets;
|
||||
|
||||
trace_in_packet_without_header(s, cycle, payload_quadlets, data_blocks,
|
||||
index);
|
||||
|
||||
pcm_frames = s->process_data_blocks(s, buffer, data_blocks, NULL);
|
||||
s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
|
||||
|
||||
if (queue_in_packet(s) < 0)
|
||||
*payload_length = be32_to_cpu(ctx_header[0]) >> ISO_DATA_LENGTH_SHIFT;
|
||||
if (*payload_length > s->ctx_data.tx.ctx_header_size +
|
||||
s->ctx_data.tx.max_ctx_payload_length) {
|
||||
dev_err(&s->unit->device,
|
||||
"Detect jumbo payload: %04x %04x\n",
|
||||
*payload_length, s->ctx_data.tx.max_ctx_payload_length);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
pcm = READ_ONCE(s->pcm);
|
||||
if (pcm && pcm_frames > 0)
|
||||
update_pcm_pointers(s, pcm, pcm_frames);
|
||||
if (!(s->flags & CIP_NO_HEADER)) {
|
||||
cip_header = ctx_header + 2;
|
||||
err = check_cip_header(s, cip_header, *payload_length,
|
||||
data_blocks, &dbc, syt);
|
||||
if (err < 0)
|
||||
return err;
|
||||
} else {
|
||||
cip_header = NULL;
|
||||
err = 0;
|
||||
*data_blocks = *payload_length / sizeof(__be32) /
|
||||
s->data_block_quadlets;
|
||||
*syt = 0;
|
||||
|
||||
return 0;
|
||||
if (s->data_block_counter != UINT_MAX)
|
||||
dbc = s->data_block_counter;
|
||||
else
|
||||
dbc = 0;
|
||||
}
|
||||
|
||||
s->data_block_counter = dbc;
|
||||
|
||||
trace_amdtp_packet(s, cycle, cip_header, *payload_length, *data_blocks,
|
||||
index);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* In CYCLE_TIMER register of IEEE 1394, 7 bits are used to represent second. On
|
||||
* the other hand, in DMA descriptors of 1394 OHCI, 3 bits are used to represent
|
||||
* it. Thus, via Linux firewire subsystem, we can get the 3 bits for second.
|
||||
*/
|
||||
static inline u32 compute_cycle_count(u32 tstamp)
|
||||
// In CYCLE_TIMER register of IEEE 1394, 7 bits are used to represent second. On
|
||||
// the other hand, in DMA descriptors of 1394 OHCI, 3 bits are used to represent
|
||||
// it. Thus, via Linux firewire subsystem, we can get the 3 bits for second.
|
||||
static inline u32 compute_cycle_count(__be32 ctx_header_tstamp)
|
||||
{
|
||||
u32 tstamp = be32_to_cpu(ctx_header_tstamp) & HEADER_TSTAMP_MASK;
|
||||
return (((tstamp >> 13) & 0x07) * 8000) + (tstamp & 0x1fff);
|
||||
}
|
||||
|
||||
|
@ -702,31 +676,68 @@ static inline u32 increment_cycle_count(u32 cycle, unsigned int addend)
|
|||
return cycle;
|
||||
}
|
||||
|
||||
// Align to actual cycle count for the packet which is going to be scheduled.
|
||||
// This module queued the same number of isochronous cycle as QUEUE_LENGTH to
|
||||
// skip isochronous cycle, therefore it's OK to just increment the cycle by
|
||||
// QUEUE_LENGTH for scheduled cycle.
|
||||
static inline u32 compute_it_cycle(const __be32 ctx_header_tstamp)
|
||||
{
|
||||
u32 cycle = compute_cycle_count(ctx_header_tstamp);
|
||||
return increment_cycle_count(cycle, QUEUE_LENGTH);
|
||||
}
|
||||
|
||||
static inline void cancel_stream(struct amdtp_stream *s)
|
||||
{
|
||||
s->packet_index = -1;
|
||||
if (in_interrupt())
|
||||
amdtp_stream_pcm_abort(s);
|
||||
WRITE_ONCE(s->pcm_buffer_pointer, SNDRV_PCM_POS_XRUN);
|
||||
}
|
||||
|
||||
static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
|
||||
size_t header_length, void *header,
|
||||
void *private_data)
|
||||
{
|
||||
struct amdtp_stream *s = private_data;
|
||||
unsigned int i, packets = header_length / 4;
|
||||
u32 cycle;
|
||||
const __be32 *ctx_header = header;
|
||||
unsigned int packets = header_length / sizeof(*ctx_header);
|
||||
int i;
|
||||
|
||||
if (s->packet_index < 0)
|
||||
return;
|
||||
|
||||
cycle = compute_cycle_count(tstamp);
|
||||
|
||||
/* Align to actual cycle count for the last packet. */
|
||||
cycle = increment_cycle_count(cycle, QUEUE_LENGTH - packets);
|
||||
|
||||
for (i = 0; i < packets; ++i) {
|
||||
cycle = increment_cycle_count(cycle, 1);
|
||||
if (s->handle_packet(s, 0, cycle, i) < 0) {
|
||||
s->packet_index = -1;
|
||||
if (in_interrupt())
|
||||
amdtp_stream_pcm_abort(s);
|
||||
WRITE_ONCE(s->pcm_buffer_pointer, SNDRV_PCM_POS_XRUN);
|
||||
u32 cycle;
|
||||
unsigned int syt;
|
||||
unsigned int data_blocks;
|
||||
__be32 *buffer;
|
||||
unsigned int pcm_frames;
|
||||
struct {
|
||||
struct fw_iso_packet params;
|
||||
__be32 header[IT_PKT_HEADER_SIZE_CIP / sizeof(__be32)];
|
||||
} template = { {0}, {0} };
|
||||
struct snd_pcm_substream *pcm;
|
||||
|
||||
cycle = compute_it_cycle(*ctx_header);
|
||||
syt = calculate_syt(s, cycle);
|
||||
data_blocks = calculate_data_blocks(s, syt);
|
||||
buffer = s->buffer.packets[s->packet_index].buffer;
|
||||
pcm_frames = s->process_data_blocks(s, buffer, data_blocks,
|
||||
&syt);
|
||||
|
||||
build_it_pkt_header(s, cycle, &template.params, data_blocks,
|
||||
syt, i);
|
||||
|
||||
if (queue_out_packet(s, &template.params) < 0) {
|
||||
cancel_stream(s);
|
||||
return;
|
||||
}
|
||||
|
||||
pcm = READ_ONCE(s->pcm);
|
||||
if (pcm && pcm_frames > 0)
|
||||
update_pcm_pointers(s, pcm, pcm_frames);
|
||||
|
||||
++ctx_header;
|
||||
}
|
||||
|
||||
fw_iso_context_queue_flush(s->context);
|
||||
|
@ -738,46 +749,55 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
|
|||
{
|
||||
struct amdtp_stream *s = private_data;
|
||||
unsigned int i, packets;
|
||||
unsigned int payload_length, max_payload_length;
|
||||
__be32 *ctx_header = header;
|
||||
|
||||
if (s->packet_index < 0)
|
||||
return;
|
||||
|
||||
/* The number of packets in buffer */
|
||||
packets = header_length / IR_HEADER_SIZE;
|
||||
|
||||
/* For buffer-over-run prevention. */
|
||||
max_payload_length = s->max_payload_length;
|
||||
// The number of packets in buffer.
|
||||
packets = header_length / s->ctx_data.tx.ctx_header_size;
|
||||
|
||||
for (i = 0; i < packets; i++) {
|
||||
u32 iso_header = be32_to_cpu(ctx_header[0]);
|
||||
unsigned int cycle;
|
||||
u32 cycle;
|
||||
unsigned int payload_length;
|
||||
unsigned int data_blocks;
|
||||
unsigned int syt;
|
||||
__be32 *buffer;
|
||||
unsigned int pcm_frames = 0;
|
||||
struct fw_iso_packet params = {0};
|
||||
struct snd_pcm_substream *pcm;
|
||||
int err;
|
||||
|
||||
tstamp = be32_to_cpu(ctx_header[1]) & HEADER_TSTAMP_MASK;
|
||||
cycle = compute_cycle_count(tstamp);
|
||||
|
||||
/* The number of bytes in this packet */
|
||||
payload_length = iso_header >> ISO_DATA_LENGTH_SHIFT;
|
||||
if (payload_length > max_payload_length) {
|
||||
dev_err(&s->unit->device,
|
||||
"Detect jumbo payload: %04x %04x\n",
|
||||
payload_length, max_payload_length);
|
||||
cycle = compute_cycle_count(ctx_header[1]);
|
||||
err = parse_ir_ctx_header(s, cycle, ctx_header, &payload_length,
|
||||
&data_blocks, &syt, i);
|
||||
if (err < 0 && err != -EAGAIN)
|
||||
break;
|
||||
|
||||
if (err >= 0) {
|
||||
buffer = s->buffer.packets[s->packet_index].buffer;
|
||||
pcm_frames = s->process_data_blocks(s, buffer,
|
||||
data_blocks, &syt);
|
||||
|
||||
if (!(s->flags & CIP_DBC_IS_END_EVENT)) {
|
||||
s->data_block_counter += data_blocks;
|
||||
s->data_block_counter &= 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
if (s->handle_packet(s, payload_length, cycle, i) < 0)
|
||||
if (queue_in_packet(s, ¶ms) < 0)
|
||||
break;
|
||||
|
||||
ctx_header += IR_HEADER_SIZE / sizeof(__be32);
|
||||
pcm = READ_ONCE(s->pcm);
|
||||
if (pcm && pcm_frames > 0)
|
||||
update_pcm_pointers(s, pcm, pcm_frames);
|
||||
|
||||
ctx_header += s->ctx_data.tx.ctx_header_size / sizeof(*ctx_header);
|
||||
}
|
||||
|
||||
/* Queueing error or detecting invalid payload. */
|
||||
if (i < packets) {
|
||||
s->packet_index = -1;
|
||||
if (in_interrupt())
|
||||
amdtp_stream_pcm_abort(s);
|
||||
WRITE_ONCE(s->pcm_buffer_pointer, SNDRV_PCM_POS_XRUN);
|
||||
cancel_stream(s);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -790,9 +810,8 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
|
|||
void *header, void *private_data)
|
||||
{
|
||||
struct amdtp_stream *s = private_data;
|
||||
__be32 *ctx_header = header;
|
||||
const __be32 *ctx_header = header;
|
||||
u32 cycle;
|
||||
unsigned int packets;
|
||||
|
||||
/*
|
||||
* For in-stream, first packet has come.
|
||||
|
@ -802,23 +821,13 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
|
|||
wake_up(&s->callback_wait);
|
||||
|
||||
if (s->direction == AMDTP_IN_STREAM) {
|
||||
tstamp = be32_to_cpu(ctx_header[1]) & HEADER_TSTAMP_MASK;
|
||||
cycle = compute_cycle_count(tstamp);
|
||||
cycle = compute_cycle_count(ctx_header[1]);
|
||||
|
||||
context->callback.sc = in_stream_callback;
|
||||
if (s->flags & CIP_NO_HEADER)
|
||||
s->handle_packet = handle_in_packet_without_header;
|
||||
else
|
||||
s->handle_packet = handle_in_packet;
|
||||
} else {
|
||||
packets = header_length / 4;
|
||||
cycle = compute_cycle_count(tstamp);
|
||||
cycle = increment_cycle_count(cycle, QUEUE_LENGTH - packets);
|
||||
cycle = compute_it_cycle(*ctx_header);
|
||||
|
||||
context->callback.sc = out_stream_callback;
|
||||
if (s->flags & CIP_NO_HEADER)
|
||||
s->handle_packet = handle_out_packet_without_header;
|
||||
else
|
||||
s->handle_packet = handle_out_packet;
|
||||
}
|
||||
|
||||
s->start_cycle = cycle;
|
||||
|
@ -841,7 +850,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
|
|||
static const struct {
|
||||
unsigned int data_block;
|
||||
unsigned int syt_offset;
|
||||
} initial_state[] = {
|
||||
} *entry, initial_state[] = {
|
||||
[CIP_SFC_32000] = { 4, 3072 },
|
||||
[CIP_SFC_48000] = { 6, 1024 },
|
||||
[CIP_SFC_96000] = { 12, 1024 },
|
||||
|
@ -850,7 +859,8 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
|
|||
[CIP_SFC_88200] = { 0, 67 },
|
||||
[CIP_SFC_176400] = { 0, 67 },
|
||||
};
|
||||
unsigned int header_size;
|
||||
unsigned int ctx_header_size;
|
||||
unsigned int max_ctx_payload_size;
|
||||
enum dma_data_direction dir;
|
||||
int type, tag, err;
|
||||
|
||||
|
@ -862,32 +872,46 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
|
|||
goto err_unlock;
|
||||
}
|
||||
|
||||
if (s->direction == AMDTP_IN_STREAM)
|
||||
if (s->direction == AMDTP_IN_STREAM) {
|
||||
s->data_block_counter = UINT_MAX;
|
||||
else
|
||||
} else {
|
||||
entry = &initial_state[s->sfc];
|
||||
|
||||
s->data_block_counter = 0;
|
||||
s->data_block_state = initial_state[s->sfc].data_block;
|
||||
s->syt_offset_state = initial_state[s->sfc].syt_offset;
|
||||
s->last_syt_offset = TICKS_PER_CYCLE;
|
||||
s->ctx_data.rx.data_block_state = entry->data_block;
|
||||
s->ctx_data.rx.syt_offset_state = entry->syt_offset;
|
||||
s->ctx_data.rx.last_syt_offset = TICKS_PER_CYCLE;
|
||||
}
|
||||
|
||||
/* initialize packet buffer */
|
||||
if (s->direction == AMDTP_IN_STREAM) {
|
||||
dir = DMA_FROM_DEVICE;
|
||||
type = FW_ISO_CONTEXT_RECEIVE;
|
||||
header_size = IR_HEADER_SIZE;
|
||||
if (!(s->flags & CIP_NO_HEADER))
|
||||
ctx_header_size = IR_CTX_HEADER_SIZE_CIP;
|
||||
else
|
||||
ctx_header_size = IR_CTX_HEADER_SIZE_NO_CIP;
|
||||
|
||||
max_ctx_payload_size = amdtp_stream_get_max_payload(s) -
|
||||
ctx_header_size;
|
||||
} else {
|
||||
dir = DMA_TO_DEVICE;
|
||||
type = FW_ISO_CONTEXT_TRANSMIT;
|
||||
header_size = OUT_PACKET_HEADER_SIZE;
|
||||
ctx_header_size = 0; // No effect for IT context.
|
||||
|
||||
max_ctx_payload_size = amdtp_stream_get_max_payload(s);
|
||||
if (!(s->flags & CIP_NO_HEADER))
|
||||
max_ctx_payload_size -= IT_PKT_HEADER_SIZE_CIP;
|
||||
}
|
||||
|
||||
err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH,
|
||||
amdtp_stream_get_max_payload(s), dir);
|
||||
max_ctx_payload_size, dir);
|
||||
if (err < 0)
|
||||
goto err_unlock;
|
||||
|
||||
s->context = fw_iso_context_create(fw_parent_device(s->unit)->card,
|
||||
type, channel, speed, header_size,
|
||||
amdtp_stream_first_callback, s);
|
||||
type, channel, speed, ctx_header_size,
|
||||
amdtp_stream_first_callback, s);
|
||||
if (IS_ERR(s->context)) {
|
||||
err = PTR_ERR(s->context);
|
||||
if (err == -EBUSY)
|
||||
|
@ -898,8 +922,10 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
|
|||
|
||||
amdtp_stream_update(s);
|
||||
|
||||
if (s->direction == AMDTP_IN_STREAM)
|
||||
s->max_payload_length = amdtp_stream_get_max_payload(s);
|
||||
if (s->direction == AMDTP_IN_STREAM) {
|
||||
s->ctx_data.tx.max_ctx_payload_length = max_ctx_payload_size;
|
||||
s->ctx_data.tx.ctx_header_size = ctx_header_size;
|
||||
}
|
||||
|
||||
if (s->flags & CIP_NO_HEADER)
|
||||
s->tag = TAG_NO_CIP_HEADER;
|
||||
|
@ -908,10 +934,14 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
|
|||
|
||||
s->packet_index = 0;
|
||||
do {
|
||||
if (s->direction == AMDTP_IN_STREAM)
|
||||
err = queue_in_packet(s);
|
||||
else
|
||||
err = queue_out_packet(s, 0);
|
||||
struct fw_iso_packet params;
|
||||
if (s->direction == AMDTP_IN_STREAM) {
|
||||
err = queue_in_packet(s, ¶ms);
|
||||
} else {
|
||||
params.header_length = 0;
|
||||
params.payload_length = 0;
|
||||
err = queue_out_packet(s, ¶ms);
|
||||
}
|
||||
if (err < 0)
|
||||
goto err_context;
|
||||
} while (s->packet_index > 0);
|
||||
|
|
|
@ -108,10 +108,31 @@ struct amdtp_stream {
|
|||
struct iso_packets_buffer buffer;
|
||||
int packet_index;
|
||||
int tag;
|
||||
int (*handle_packet)(struct amdtp_stream *s,
|
||||
unsigned int payload_quadlets, unsigned int cycle,
|
||||
unsigned int index);
|
||||
unsigned int max_payload_length;
|
||||
union {
|
||||
struct {
|
||||
unsigned int ctx_header_size;
|
||||
|
||||
// limit for payload of iso packet.
|
||||
unsigned int max_ctx_payload_length;
|
||||
|
||||
// For quirks of CIP headers.
|
||||
// Fixed interval of dbc between previos/current
|
||||
// packets.
|
||||
unsigned int dbc_interval;
|
||||
// Indicate the value of dbc field in a first packet.
|
||||
unsigned int first_dbc;
|
||||
} tx;
|
||||
struct {
|
||||
// To calculate CIP data blocks and tstamp.
|
||||
unsigned int transfer_delay;
|
||||
unsigned int data_block_state;
|
||||
unsigned int last_syt_offset;
|
||||
unsigned int syt_offset_state;
|
||||
|
||||
// To generate CIP header.
|
||||
unsigned int fdf;
|
||||
} rx;
|
||||
} ctx_data;
|
||||
|
||||
/* For CIP headers. */
|
||||
unsigned int source_node_id_field;
|
||||
|
@ -119,19 +140,10 @@ struct amdtp_stream {
|
|||
unsigned int data_block_counter;
|
||||
unsigned int sph;
|
||||
unsigned int fmt;
|
||||
unsigned int fdf;
|
||||
/* quirk: fixed interval of dbc between previos/current packets. */
|
||||
unsigned int tx_dbc_interval;
|
||||
/* quirk: indicate the value of dbc field in a first packet. */
|
||||
unsigned int tx_first_dbc;
|
||||
|
||||
/* Internal flags. */
|
||||
enum cip_sfc sfc;
|
||||
unsigned int syt_interval;
|
||||
unsigned int transfer_delay;
|
||||
unsigned int data_block_state;
|
||||
unsigned int last_syt_offset;
|
||||
unsigned int syt_offset_state;
|
||||
|
||||
/* For a PCM substream processing. */
|
||||
struct snd_pcm_substream *pcm;
|
||||
|
|
|
@ -92,8 +92,6 @@ struct snd_bebob {
|
|||
unsigned int midi_input_ports;
|
||||
unsigned int midi_output_ports;
|
||||
|
||||
bool connected;
|
||||
|
||||
struct amdtp_stream tx_stream;
|
||||
struct amdtp_stream rx_stream;
|
||||
struct cmp_connection out_conn;
|
||||
|
@ -217,7 +215,8 @@ int snd_bebob_stream_get_clock_src(struct snd_bebob *bebob,
|
|||
enum snd_bebob_clock_type *src);
|
||||
int snd_bebob_stream_discover(struct snd_bebob *bebob);
|
||||
int snd_bebob_stream_init_duplex(struct snd_bebob *bebob);
|
||||
int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate);
|
||||
int snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate);
|
||||
int snd_bebob_stream_start_duplex(struct snd_bebob *bebob);
|
||||
void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob);
|
||||
void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob);
|
||||
|
||||
|
|
|
@ -7,58 +7,31 @@
|
|||
|
||||
#include "bebob.h"
|
||||
|
||||
static int midi_capture_open(struct snd_rawmidi_substream *substream)
|
||||
static int midi_open(struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
struct snd_bebob *bebob = substream->rmidi->private_data;
|
||||
int err;
|
||||
|
||||
err = snd_bebob_stream_lock_try(bebob);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
return err;
|
||||
|
||||
mutex_lock(&bebob->mutex);
|
||||
bebob->substreams_counter++;
|
||||
err = snd_bebob_stream_start_duplex(bebob, 0);
|
||||
err = snd_bebob_stream_reserve_duplex(bebob, 0);
|
||||
if (err >= 0) {
|
||||
++bebob->substreams_counter;
|
||||
err = snd_bebob_stream_start_duplex(bebob);
|
||||
if (err < 0)
|
||||
--bebob->substreams_counter;
|
||||
}
|
||||
mutex_unlock(&bebob->mutex);
|
||||
if (err < 0)
|
||||
snd_bebob_stream_lock_release(bebob);
|
||||
end:
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int midi_playback_open(struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
struct snd_bebob *bebob = substream->rmidi->private_data;
|
||||
int err;
|
||||
|
||||
err = snd_bebob_stream_lock_try(bebob);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
mutex_lock(&bebob->mutex);
|
||||
bebob->substreams_counter++;
|
||||
err = snd_bebob_stream_start_duplex(bebob, 0);
|
||||
mutex_unlock(&bebob->mutex);
|
||||
if (err < 0)
|
||||
snd_bebob_stream_lock_release(bebob);
|
||||
end:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int midi_capture_close(struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
struct snd_bebob *bebob = substream->rmidi->private_data;
|
||||
|
||||
mutex_lock(&bebob->mutex);
|
||||
bebob->substreams_counter--;
|
||||
snd_bebob_stream_stop_duplex(bebob);
|
||||
mutex_unlock(&bebob->mutex);
|
||||
|
||||
snd_bebob_stream_lock_release(bebob);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int midi_playback_close(struct snd_rawmidi_substream *substream)
|
||||
static int midi_close(struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
struct snd_bebob *bebob = substream->rmidi->private_data;
|
||||
|
||||
|
@ -120,13 +93,13 @@ static void set_midi_substream_names(struct snd_bebob *bebob,
|
|||
int snd_bebob_create_midi_devices(struct snd_bebob *bebob)
|
||||
{
|
||||
static const struct snd_rawmidi_ops capture_ops = {
|
||||
.open = midi_capture_open,
|
||||
.close = midi_capture_close,
|
||||
.open = midi_open,
|
||||
.close = midi_close,
|
||||
.trigger = midi_capture_trigger,
|
||||
};
|
||||
static const struct snd_rawmidi_ops playback_ops = {
|
||||
.open = midi_playback_open,
|
||||
.close = midi_playback_close,
|
||||
.open = midi_open,
|
||||
.close = midi_close,
|
||||
.trigger = midi_playback_trigger,
|
||||
};
|
||||
struct snd_rawmidi *rmidi;
|
||||
|
|
|
@ -184,9 +184,8 @@ pcm_close(struct snd_pcm_substream *substream)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
pcm_capture_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
static int pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct snd_bebob *bebob = substream->private_data;
|
||||
int err;
|
||||
|
@ -197,61 +196,30 @@ pcm_capture_hw_params(struct snd_pcm_substream *substream,
|
|||
return err;
|
||||
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
|
||||
unsigned int rate = params_rate(hw_params);
|
||||
|
||||
mutex_lock(&bebob->mutex);
|
||||
bebob->substreams_counter++;
|
||||
err = snd_bebob_stream_reserve_duplex(bebob, rate);
|
||||
if (err >= 0)
|
||||
++bebob->substreams_counter;
|
||||
mutex_unlock(&bebob->mutex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int
|
||||
pcm_playback_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct snd_bebob *bebob = substream->private_data;
|
||||
int err;
|
||||
|
||||
err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
|
||||
params_buffer_bytes(hw_params));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
|
||||
mutex_lock(&bebob->mutex);
|
||||
bebob->substreams_counter++;
|
||||
mutex_unlock(&bebob->mutex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
pcm_capture_hw_free(struct snd_pcm_substream *substream)
|
||||
static int pcm_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_bebob *bebob = substream->private_data;
|
||||
|
||||
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) {
|
||||
mutex_lock(&bebob->mutex);
|
||||
mutex_lock(&bebob->mutex);
|
||||
|
||||
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
|
||||
bebob->substreams_counter--;
|
||||
mutex_unlock(&bebob->mutex);
|
||||
}
|
||||
|
||||
snd_bebob_stream_stop_duplex(bebob);
|
||||
|
||||
return snd_pcm_lib_free_vmalloc_buffer(substream);
|
||||
}
|
||||
static int
|
||||
pcm_playback_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_bebob *bebob = substream->private_data;
|
||||
|
||||
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) {
|
||||
mutex_lock(&bebob->mutex);
|
||||
bebob->substreams_counter--;
|
||||
mutex_unlock(&bebob->mutex);
|
||||
}
|
||||
|
||||
snd_bebob_stream_stop_duplex(bebob);
|
||||
mutex_unlock(&bebob->mutex);
|
||||
|
||||
return snd_pcm_lib_free_vmalloc_buffer(substream);
|
||||
}
|
||||
|
@ -260,10 +228,9 @@ static int
|
|||
pcm_capture_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_bebob *bebob = substream->private_data;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
int err;
|
||||
|
||||
err = snd_bebob_stream_start_duplex(bebob, runtime->rate);
|
||||
err = snd_bebob_stream_start_duplex(bebob);
|
||||
if (err >= 0)
|
||||
amdtp_stream_pcm_prepare(&bebob->tx_stream);
|
||||
|
||||
|
@ -273,10 +240,9 @@ static int
|
|||
pcm_playback_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_bebob *bebob = substream->private_data;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
int err;
|
||||
|
||||
err = snd_bebob_stream_start_duplex(bebob, runtime->rate);
|
||||
err = snd_bebob_stream_start_duplex(bebob);
|
||||
if (err >= 0)
|
||||
amdtp_stream_pcm_prepare(&bebob->rx_stream);
|
||||
|
||||
|
@ -353,8 +319,8 @@ int snd_bebob_create_pcm_devices(struct snd_bebob *bebob)
|
|||
.open = pcm_open,
|
||||
.close = pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = pcm_capture_hw_params,
|
||||
.hw_free = pcm_capture_hw_free,
|
||||
.hw_params = pcm_hw_params,
|
||||
.hw_free = pcm_hw_free,
|
||||
.prepare = pcm_capture_prepare,
|
||||
.trigger = pcm_capture_trigger,
|
||||
.pointer = pcm_capture_pointer,
|
||||
|
@ -365,8 +331,8 @@ int snd_bebob_create_pcm_devices(struct snd_bebob *bebob)
|
|||
.open = pcm_open,
|
||||
.close = pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = pcm_playback_hw_params,
|
||||
.hw_free = pcm_playback_hw_free,
|
||||
.hw_params = pcm_hw_params,
|
||||
.hw_free = pcm_hw_free,
|
||||
.prepare = pcm_playback_prepare,
|
||||
.trigger = pcm_playback_trigger,
|
||||
.pointer = pcm_playback_pointer,
|
||||
|
|
|
@ -375,24 +375,6 @@ map_data_channels(struct snd_bebob *bebob, struct amdtp_stream *s)
|
|||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
init_both_connections(struct snd_bebob *bebob)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = cmp_connection_init(&bebob->in_conn,
|
||||
bebob->unit, CMP_INPUT, 0);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
err = cmp_connection_init(&bebob->out_conn,
|
||||
bebob->unit, CMP_OUTPUT, 0);
|
||||
if (err < 0)
|
||||
cmp_connection_destroy(&bebob->in_conn);
|
||||
end:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
check_connection_used_by_others(struct snd_bebob *bebob, struct amdtp_stream *s)
|
||||
{
|
||||
|
@ -417,49 +399,21 @@ check_connection_used_by_others(struct snd_bebob *bebob, struct amdtp_stream *s)
|
|||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
make_both_connections(struct snd_bebob *bebob, unsigned int rate)
|
||||
static int make_both_connections(struct snd_bebob *bebob)
|
||||
{
|
||||
int index, pcm_channels, midi_channels, err = 0;
|
||||
int err = 0;
|
||||
|
||||
if (bebob->connected)
|
||||
goto end;
|
||||
err = cmp_connection_establish(&bebob->out_conn);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* confirm params for both streams */
|
||||
err = get_formation_index(rate, &index);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
pcm_channels = bebob->tx_stream_formations[index].pcm;
|
||||
midi_channels = bebob->tx_stream_formations[index].midi;
|
||||
err = amdtp_am824_set_parameters(&bebob->tx_stream, rate,
|
||||
pcm_channels, midi_channels * 8,
|
||||
false);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
pcm_channels = bebob->rx_stream_formations[index].pcm;
|
||||
midi_channels = bebob->rx_stream_formations[index].midi;
|
||||
err = amdtp_am824_set_parameters(&bebob->rx_stream, rate,
|
||||
pcm_channels, midi_channels * 8,
|
||||
false);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
/* establish connections for both streams */
|
||||
err = cmp_connection_establish(&bebob->out_conn,
|
||||
amdtp_stream_get_max_payload(&bebob->tx_stream));
|
||||
if (err < 0)
|
||||
goto end;
|
||||
err = cmp_connection_establish(&bebob->in_conn,
|
||||
amdtp_stream_get_max_payload(&bebob->rx_stream));
|
||||
err = cmp_connection_establish(&bebob->in_conn);
|
||||
if (err < 0) {
|
||||
cmp_connection_break(&bebob->out_conn);
|
||||
goto end;
|
||||
return err;
|
||||
}
|
||||
|
||||
bebob->connected = true;
|
||||
end:
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -468,23 +422,13 @@ break_both_connections(struct snd_bebob *bebob)
|
|||
cmp_connection_break(&bebob->in_conn);
|
||||
cmp_connection_break(&bebob->out_conn);
|
||||
|
||||
bebob->connected = false;
|
||||
|
||||
/* These models seems to be in transition state for a longer time. */
|
||||
if (bebob->maudio_special_quirk != NULL)
|
||||
msleep(200);
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_both_connections(struct snd_bebob *bebob)
|
||||
{
|
||||
cmp_connection_destroy(&bebob->in_conn);
|
||||
cmp_connection_destroy(&bebob->out_conn);
|
||||
}
|
||||
|
||||
static int
|
||||
start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream,
|
||||
unsigned int rate)
|
||||
start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream)
|
||||
{
|
||||
struct cmp_connection *conn;
|
||||
int err = 0;
|
||||
|
@ -509,190 +453,252 @@ start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream,
|
|||
return err;
|
||||
}
|
||||
|
||||
static int init_stream(struct snd_bebob *bebob, struct amdtp_stream *stream)
|
||||
{
|
||||
enum amdtp_stream_direction dir_stream;
|
||||
struct cmp_connection *conn;
|
||||
enum cmp_direction dir_conn;
|
||||
int err;
|
||||
|
||||
if (stream == &bebob->tx_stream) {
|
||||
dir_stream = AMDTP_IN_STREAM;
|
||||
conn = &bebob->out_conn;
|
||||
dir_conn = CMP_OUTPUT;
|
||||
} else {
|
||||
dir_stream = AMDTP_OUT_STREAM;
|
||||
conn = &bebob->in_conn;
|
||||
dir_conn = CMP_INPUT;
|
||||
}
|
||||
|
||||
err = cmp_connection_init(conn, bebob->unit, dir_conn, 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = amdtp_am824_init(stream, bebob->unit, dir_stream, CIP_BLOCKING);
|
||||
if (err < 0) {
|
||||
cmp_connection_destroy(conn);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (stream == &bebob->tx_stream) {
|
||||
// BeBoB v3 transfers packets with these qurks:
|
||||
// - In the beginning of streaming, the value of dbc is
|
||||
// incremented even if no data blocks are transferred.
|
||||
// - The value of dbc is reset suddenly.
|
||||
if (bebob->version > 2)
|
||||
bebob->tx_stream.flags |= CIP_EMPTY_HAS_WRONG_DBC |
|
||||
CIP_SKIP_DBC_ZERO_CHECK;
|
||||
|
||||
// At high sampling rate, M-Audio special firmware transmits
|
||||
// empty packet with the value of dbc incremented by 8 but the
|
||||
// others are valid to IEC 61883-1.
|
||||
if (bebob->maudio_special_quirk)
|
||||
bebob->tx_stream.flags |= CIP_EMPTY_HAS_WRONG_DBC;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void destroy_stream(struct snd_bebob *bebob, struct amdtp_stream *stream)
|
||||
{
|
||||
amdtp_stream_destroy(stream);
|
||||
|
||||
if (stream == &bebob->tx_stream)
|
||||
cmp_connection_destroy(&bebob->out_conn);
|
||||
else
|
||||
cmp_connection_destroy(&bebob->in_conn);
|
||||
}
|
||||
|
||||
int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = init_both_connections(bebob);
|
||||
err = init_stream(bebob, &bebob->tx_stream);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
return err;
|
||||
|
||||
err = amdtp_am824_init(&bebob->tx_stream, bebob->unit,
|
||||
AMDTP_IN_STREAM, CIP_BLOCKING);
|
||||
err = init_stream(bebob, &bebob->rx_stream);
|
||||
if (err < 0) {
|
||||
amdtp_stream_destroy(&bebob->tx_stream);
|
||||
destroy_both_connections(bebob);
|
||||
goto end;
|
||||
destroy_stream(bebob, &bebob->tx_stream);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* BeBoB v3 transfers packets with these qurks:
|
||||
* - In the beginning of streaming, the value of dbc is incremented
|
||||
* even if no data blocks are transferred.
|
||||
* - The value of dbc is reset suddenly.
|
||||
*/
|
||||
if (bebob->version > 2)
|
||||
bebob->tx_stream.flags |= CIP_EMPTY_HAS_WRONG_DBC |
|
||||
CIP_SKIP_DBC_ZERO_CHECK;
|
||||
|
||||
/*
|
||||
* At high sampling rate, M-Audio special firmware transmits empty
|
||||
* packet with the value of dbc incremented by 8 but the others are
|
||||
* valid to IEC 61883-1.
|
||||
*/
|
||||
if (bebob->maudio_special_quirk)
|
||||
bebob->tx_stream.flags |= CIP_EMPTY_HAS_WRONG_DBC;
|
||||
|
||||
err = amdtp_am824_init(&bebob->rx_stream, bebob->unit,
|
||||
AMDTP_OUT_STREAM, CIP_BLOCKING);
|
||||
if (err < 0) {
|
||||
amdtp_stream_destroy(&bebob->tx_stream);
|
||||
amdtp_stream_destroy(&bebob->rx_stream);
|
||||
destroy_both_connections(bebob);
|
||||
}
|
||||
end:
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
|
||||
static int keep_resources(struct snd_bebob *bebob, struct amdtp_stream *stream,
|
||||
unsigned int rate, unsigned int index)
|
||||
{
|
||||
struct snd_bebob_stream_formation *formation;
|
||||
struct cmp_connection *conn;
|
||||
int err;
|
||||
|
||||
if (stream == &bebob->tx_stream) {
|
||||
formation = bebob->tx_stream_formations + index;
|
||||
conn = &bebob->out_conn;
|
||||
} else {
|
||||
formation = bebob->rx_stream_formations + index;
|
||||
conn = &bebob->in_conn;
|
||||
}
|
||||
|
||||
err = amdtp_am824_set_parameters(stream, rate, formation->pcm,
|
||||
formation->midi, false);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return cmp_connection_reserve(conn, amdtp_stream_get_max_payload(stream));
|
||||
}
|
||||
|
||||
int snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate)
|
||||
{
|
||||
const struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
|
||||
unsigned int curr_rate;
|
||||
int err = 0;
|
||||
int err;
|
||||
|
||||
/* Need no substreams */
|
||||
if (bebob->substreams_counter == 0)
|
||||
goto end;
|
||||
|
||||
/*
|
||||
* Considering JACK/FFADO streaming:
|
||||
* TODO: This can be removed hwdep functionality becomes popular.
|
||||
*/
|
||||
// Considering JACK/FFADO streaming:
|
||||
// TODO: This can be removed hwdep functionality becomes popular.
|
||||
err = check_connection_used_by_others(bebob, &bebob->rx_stream);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
return err;
|
||||
|
||||
/*
|
||||
* packet queueing error or detecting discontinuity
|
||||
*
|
||||
* At bus reset, connections should not be broken here. So streams need
|
||||
* to be re-started. This is a reason to use SKIP_INIT_DBC_CHECK flag.
|
||||
*/
|
||||
if (amdtp_streaming_error(&bebob->rx_stream))
|
||||
amdtp_stream_stop(&bebob->rx_stream);
|
||||
if (amdtp_streaming_error(&bebob->tx_stream))
|
||||
amdtp_stream_stop(&bebob->tx_stream);
|
||||
if (!amdtp_stream_running(&bebob->rx_stream) &&
|
||||
!amdtp_stream_running(&bebob->tx_stream))
|
||||
break_both_connections(bebob);
|
||||
|
||||
/* stop streams if rate is different */
|
||||
err = rate_spec->get(bebob, &curr_rate);
|
||||
if (err < 0) {
|
||||
dev_err(&bebob->unit->device,
|
||||
"fail to get sampling rate: %d\n", err);
|
||||
goto end;
|
||||
}
|
||||
err = bebob->spec->rate->get(bebob, &curr_rate);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (rate == 0)
|
||||
rate = curr_rate;
|
||||
if (rate != curr_rate) {
|
||||
if (curr_rate != rate) {
|
||||
amdtp_stream_stop(&bebob->tx_stream);
|
||||
amdtp_stream_stop(&bebob->rx_stream);
|
||||
|
||||
break_both_connections(bebob);
|
||||
|
||||
cmp_connection_release(&bebob->out_conn);
|
||||
cmp_connection_release(&bebob->in_conn);
|
||||
}
|
||||
|
||||
if (bebob->substreams_counter == 0 || curr_rate != rate) {
|
||||
unsigned int index;
|
||||
|
||||
// NOTE:
|
||||
// If establishing connections at first, Yamaha GO46
|
||||
// (and maybe Terratec X24) don't generate sound.
|
||||
//
|
||||
// For firmware customized by M-Audio, refer to next NOTE.
|
||||
err = bebob->spec->rate->set(bebob, rate);
|
||||
if (err < 0) {
|
||||
dev_err(&bebob->unit->device,
|
||||
"fail to set sampling rate: %d\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = get_formation_index(rate, &index);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = keep_resources(bebob, &bebob->tx_stream, rate, index);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = keep_resources(bebob, &bebob->rx_stream, rate, index);
|
||||
if (err < 0) {
|
||||
cmp_connection_release(&bebob->out_conn);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_bebob_stream_start_duplex(struct snd_bebob *bebob)
|
||||
{
|
||||
int err;
|
||||
|
||||
// Need no substreams.
|
||||
if (bebob->substreams_counter == 0)
|
||||
return -EIO;
|
||||
|
||||
// packet queueing error or detecting discontinuity
|
||||
if (amdtp_streaming_error(&bebob->rx_stream) ||
|
||||
amdtp_streaming_error(&bebob->tx_stream)) {
|
||||
amdtp_stream_stop(&bebob->rx_stream);
|
||||
amdtp_stream_stop(&bebob->tx_stream);
|
||||
|
||||
break_both_connections(bebob);
|
||||
}
|
||||
|
||||
/* master should be always running */
|
||||
if (!amdtp_stream_running(&bebob->rx_stream)) {
|
||||
/*
|
||||
* NOTE:
|
||||
* If establishing connections at first, Yamaha GO46
|
||||
* (and maybe Terratec X24) don't generate sound.
|
||||
*
|
||||
* For firmware customized by M-Audio, refer to next NOTE.
|
||||
*/
|
||||
if (bebob->maudio_special_quirk == NULL) {
|
||||
err = rate_spec->set(bebob, rate);
|
||||
if (err < 0) {
|
||||
dev_err(&bebob->unit->device,
|
||||
"fail to set sampling rate: %d\n",
|
||||
err);
|
||||
goto end;
|
||||
}
|
||||
unsigned int curr_rate;
|
||||
|
||||
if (bebob->maudio_special_quirk) {
|
||||
err = bebob->spec->rate->get(bebob, &curr_rate);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = make_both_connections(bebob, rate);
|
||||
err = make_both_connections(bebob);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
return err;
|
||||
|
||||
err = start_stream(bebob, &bebob->rx_stream, rate);
|
||||
err = start_stream(bebob, &bebob->rx_stream);
|
||||
if (err < 0) {
|
||||
dev_err(&bebob->unit->device,
|
||||
"fail to run AMDTP master stream:%d\n", err);
|
||||
break_both_connections(bebob);
|
||||
goto end;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE:
|
||||
* The firmware customized by M-Audio uses these commands to
|
||||
* start transmitting stream. This is not usual way.
|
||||
*/
|
||||
if (bebob->maudio_special_quirk != NULL) {
|
||||
err = rate_spec->set(bebob, rate);
|
||||
// NOTE:
|
||||
// The firmware customized by M-Audio uses these commands to
|
||||
// start transmitting stream. This is not usual way.
|
||||
if (bebob->maudio_special_quirk) {
|
||||
err = bebob->spec->rate->set(bebob, curr_rate);
|
||||
if (err < 0) {
|
||||
dev_err(&bebob->unit->device,
|
||||
"fail to ensure sampling rate: %d\n",
|
||||
err);
|
||||
amdtp_stream_stop(&bebob->rx_stream);
|
||||
break_both_connections(bebob);
|
||||
goto end;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
/* wait first callback */
|
||||
if (!amdtp_stream_wait_callback(&bebob->rx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
amdtp_stream_stop(&bebob->rx_stream);
|
||||
break_both_connections(bebob);
|
||||
err = -ETIMEDOUT;
|
||||
goto end;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
/* start slave if needed */
|
||||
if (!amdtp_stream_running(&bebob->tx_stream)) {
|
||||
err = start_stream(bebob, &bebob->tx_stream, rate);
|
||||
err = start_stream(bebob, &bebob->tx_stream);
|
||||
if (err < 0) {
|
||||
dev_err(&bebob->unit->device,
|
||||
"fail to run AMDTP slave stream:%d\n", err);
|
||||
amdtp_stream_stop(&bebob->rx_stream);
|
||||
break_both_connections(bebob);
|
||||
goto end;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* wait first callback */
|
||||
if (!amdtp_stream_wait_callback(&bebob->tx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
amdtp_stream_stop(&bebob->tx_stream);
|
||||
amdtp_stream_stop(&bebob->rx_stream);
|
||||
break_both_connections(bebob);
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
end:
|
||||
|
||||
return 0;
|
||||
error:
|
||||
amdtp_stream_stop(&bebob->tx_stream);
|
||||
amdtp_stream_stop(&bebob->rx_stream);
|
||||
break_both_connections(bebob);
|
||||
return err;
|
||||
}
|
||||
|
||||
void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob)
|
||||
{
|
||||
if (bebob->substreams_counter == 0) {
|
||||
amdtp_stream_pcm_abort(&bebob->rx_stream);
|
||||
amdtp_stream_stop(&bebob->rx_stream);
|
||||
|
||||
amdtp_stream_pcm_abort(&bebob->tx_stream);
|
||||
amdtp_stream_stop(&bebob->tx_stream);
|
||||
|
||||
break_both_connections(bebob);
|
||||
|
||||
cmp_connection_release(&bebob->out_conn);
|
||||
cmp_connection_release(&bebob->in_conn);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -702,10 +708,8 @@ void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob)
|
|||
*/
|
||||
void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob)
|
||||
{
|
||||
amdtp_stream_destroy(&bebob->rx_stream);
|
||||
amdtp_stream_destroy(&bebob->tx_stream);
|
||||
|
||||
destroy_both_connections(bebob);
|
||||
destroy_stream(bebob, &bebob->tx_stream);
|
||||
destroy_stream(bebob, &bebob->rx_stream);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -185,6 +185,37 @@ void cmp_connection_destroy(struct cmp_connection *c)
|
|||
}
|
||||
EXPORT_SYMBOL(cmp_connection_destroy);
|
||||
|
||||
int cmp_connection_reserve(struct cmp_connection *c,
|
||||
unsigned int max_payload_bytes)
|
||||
{
|
||||
int err;
|
||||
|
||||
mutex_lock(&c->mutex);
|
||||
|
||||
if (WARN_ON(c->resources.allocated)) {
|
||||
err = -EBUSY;
|
||||
goto end;
|
||||
}
|
||||
|
||||
c->speed = min(c->max_speed,
|
||||
fw_parent_device(c->resources.unit)->max_speed);
|
||||
|
||||
err = fw_iso_resources_allocate(&c->resources, max_payload_bytes,
|
||||
c->speed);
|
||||
end:
|
||||
mutex_unlock(&c->mutex);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(cmp_connection_reserve);
|
||||
|
||||
void cmp_connection_release(struct cmp_connection *c)
|
||||
{
|
||||
mutex_lock(&c->mutex);
|
||||
fw_iso_resources_free(&c->resources);
|
||||
mutex_unlock(&c->mutex);
|
||||
}
|
||||
EXPORT_SYMBOL(cmp_connection_release);
|
||||
|
||||
static __be32 ipcr_set_modify(struct cmp_connection *c, __be32 ipcr)
|
||||
{
|
||||
|
@ -270,25 +301,18 @@ static int pcr_set_check(struct cmp_connection *c, __be32 pcr)
|
|||
* When this function succeeds, the caller is responsible for starting
|
||||
* transmitting packets.
|
||||
*/
|
||||
int cmp_connection_establish(struct cmp_connection *c,
|
||||
unsigned int max_payload_bytes)
|
||||
int cmp_connection_establish(struct cmp_connection *c)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (WARN_ON(c->connected))
|
||||
return -EISCONN;
|
||||
|
||||
c->speed = min(c->max_speed,
|
||||
fw_parent_device(c->resources.unit)->max_speed);
|
||||
|
||||
mutex_lock(&c->mutex);
|
||||
|
||||
retry_after_bus_reset:
|
||||
err = fw_iso_resources_allocate(&c->resources,
|
||||
max_payload_bytes, c->speed);
|
||||
if (err < 0)
|
||||
goto err_mutex;
|
||||
if (WARN_ON(c->connected)) {
|
||||
mutex_unlock(&c->mutex);
|
||||
return -EISCONN;
|
||||
}
|
||||
|
||||
retry_after_bus_reset:
|
||||
if (c->direction == CMP_OUTPUT)
|
||||
err = pcr_modify(c, opcr_set_modify, pcr_set_check,
|
||||
ABORT_ON_BUS_RESET);
|
||||
|
@ -297,21 +321,13 @@ int cmp_connection_establish(struct cmp_connection *c,
|
|||
ABORT_ON_BUS_RESET);
|
||||
|
||||
if (err == -EAGAIN) {
|
||||
fw_iso_resources_free(&c->resources);
|
||||
goto retry_after_bus_reset;
|
||||
err = fw_iso_resources_update(&c->resources);
|
||||
if (err >= 0)
|
||||
goto retry_after_bus_reset;
|
||||
}
|
||||
if (err < 0)
|
||||
goto err_resources;
|
||||
if (err >= 0)
|
||||
c->connected = true;
|
||||
|
||||
c->connected = true;
|
||||
|
||||
mutex_unlock(&c->mutex);
|
||||
|
||||
return 0;
|
||||
|
||||
err_resources:
|
||||
fw_iso_resources_free(&c->resources);
|
||||
err_mutex:
|
||||
mutex_unlock(&c->mutex);
|
||||
|
||||
return err;
|
||||
|
@ -351,14 +367,12 @@ int cmp_connection_update(struct cmp_connection *c)
|
|||
SUCCEED_ON_BUS_RESET);
|
||||
|
||||
if (err < 0)
|
||||
goto err_resources;
|
||||
goto err_unconnect;
|
||||
|
||||
mutex_unlock(&c->mutex);
|
||||
|
||||
return 0;
|
||||
|
||||
err_resources:
|
||||
fw_iso_resources_free(&c->resources);
|
||||
err_unconnect:
|
||||
c->connected = false;
|
||||
mutex_unlock(&c->mutex);
|
||||
|
@ -395,8 +409,6 @@ void cmp_connection_break(struct cmp_connection *c)
|
|||
if (err < 0)
|
||||
cmp_error(c, "plug is still connected\n");
|
||||
|
||||
fw_iso_resources_free(&c->resources);
|
||||
|
||||
c->connected = false;
|
||||
|
||||
mutex_unlock(&c->mutex);
|
||||
|
|
|
@ -42,8 +42,11 @@ int cmp_connection_init(struct cmp_connection *connection,
|
|||
int cmp_connection_check_used(struct cmp_connection *connection, bool *used);
|
||||
void cmp_connection_destroy(struct cmp_connection *connection);
|
||||
|
||||
int cmp_connection_establish(struct cmp_connection *connection,
|
||||
unsigned int max_payload);
|
||||
int cmp_connection_reserve(struct cmp_connection *connection,
|
||||
unsigned int max_payload);
|
||||
void cmp_connection_release(struct cmp_connection *connection);
|
||||
|
||||
int cmp_connection_establish(struct cmp_connection *connection);
|
||||
int cmp_connection_update(struct cmp_connection *connection);
|
||||
void cmp_connection_break(struct cmp_connection *connection);
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
snd-dice-objs := dice-transaction.o dice-stream.o dice-proc.o dice-midi.o \
|
||||
dice-pcm.o dice-hwdep.o dice.o dice-tcelectronic.o \
|
||||
dice-alesis.o dice-extension.o dice-mytek.o
|
||||
dice-alesis.o dice-extension.o dice-mytek.o dice-presonus.o
|
||||
obj-$(CONFIG_SND_DICE) += snd-dice.o
|
||||
|
|
|
@ -17,8 +17,13 @@ static int midi_open(struct snd_rawmidi_substream *substream)
|
|||
|
||||
mutex_lock(&dice->mutex);
|
||||
|
||||
dice->substreams_counter++;
|
||||
err = snd_dice_stream_start_duplex(dice, 0);
|
||||
err = snd_dice_stream_reserve_duplex(dice, 0);
|
||||
if (err >= 0) {
|
||||
++dice->substreams_counter;
|
||||
err = snd_dice_stream_start_duplex(dice);
|
||||
if (err < 0)
|
||||
--dice->substreams_counter;
|
||||
}
|
||||
|
||||
mutex_unlock(&dice->mutex);
|
||||
|
||||
|
@ -34,7 +39,7 @@ static int midi_close(struct snd_rawmidi_substream *substream)
|
|||
|
||||
mutex_lock(&dice->mutex);
|
||||
|
||||
dice->substreams_counter--;
|
||||
--dice->substreams_counter;
|
||||
snd_dice_stream_stop_duplex(dice);
|
||||
|
||||
mutex_unlock(&dice->mutex);
|
||||
|
|
|
@ -230,8 +230,8 @@ static int pcm_close(struct snd_pcm_substream *substream)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int capture_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
static int pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct snd_dice *dice = substream->private_data;
|
||||
int err;
|
||||
|
@ -242,57 +242,26 @@ static int capture_hw_params(struct snd_pcm_substream *substream,
|
|||
return err;
|
||||
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
|
||||
unsigned int rate = params_rate(hw_params);
|
||||
|
||||
mutex_lock(&dice->mutex);
|
||||
dice->substreams_counter++;
|
||||
err = snd_dice_stream_reserve_duplex(dice, rate);
|
||||
if (err >= 0)
|
||||
++dice->substreams_counter;
|
||||
mutex_unlock(&dice->mutex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int playback_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct snd_dice *dice = substream->private_data;
|
||||
int err;
|
||||
|
||||
err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
|
||||
params_buffer_bytes(hw_params));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
|
||||
mutex_lock(&dice->mutex);
|
||||
dice->substreams_counter++;
|
||||
mutex_unlock(&dice->mutex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int capture_hw_free(struct snd_pcm_substream *substream)
|
||||
static int pcm_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_dice *dice = substream->private_data;
|
||||
|
||||
mutex_lock(&dice->mutex);
|
||||
|
||||
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
|
||||
dice->substreams_counter--;
|
||||
|
||||
snd_dice_stream_stop_duplex(dice);
|
||||
|
||||
mutex_unlock(&dice->mutex);
|
||||
|
||||
return snd_pcm_lib_free_vmalloc_buffer(substream);
|
||||
}
|
||||
|
||||
static int playback_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_dice *dice = substream->private_data;
|
||||
|
||||
mutex_lock(&dice->mutex);
|
||||
|
||||
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
|
||||
dice->substreams_counter--;
|
||||
--dice->substreams_counter;
|
||||
|
||||
snd_dice_stream_stop_duplex(dice);
|
||||
|
||||
|
@ -308,7 +277,7 @@ static int capture_prepare(struct snd_pcm_substream *substream)
|
|||
int err;
|
||||
|
||||
mutex_lock(&dice->mutex);
|
||||
err = snd_dice_stream_start_duplex(dice, substream->runtime->rate);
|
||||
err = snd_dice_stream_start_duplex(dice);
|
||||
mutex_unlock(&dice->mutex);
|
||||
if (err >= 0)
|
||||
amdtp_stream_pcm_prepare(stream);
|
||||
|
@ -322,7 +291,7 @@ static int playback_prepare(struct snd_pcm_substream *substream)
|
|||
int err;
|
||||
|
||||
mutex_lock(&dice->mutex);
|
||||
err = snd_dice_stream_start_duplex(dice, substream->runtime->rate);
|
||||
err = snd_dice_stream_start_duplex(dice);
|
||||
mutex_unlock(&dice->mutex);
|
||||
if (err >= 0)
|
||||
amdtp_stream_pcm_prepare(stream);
|
||||
|
@ -404,8 +373,8 @@ int snd_dice_create_pcm(struct snd_dice *dice)
|
|||
.open = pcm_open,
|
||||
.close = pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = capture_hw_params,
|
||||
.hw_free = capture_hw_free,
|
||||
.hw_params = pcm_hw_params,
|
||||
.hw_free = pcm_hw_free,
|
||||
.prepare = capture_prepare,
|
||||
.trigger = capture_trigger,
|
||||
.pointer = capture_pointer,
|
||||
|
@ -416,8 +385,8 @@ int snd_dice_create_pcm(struct snd_dice *dice)
|
|||
.open = pcm_open,
|
||||
.close = pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = playback_hw_params,
|
||||
.hw_free = playback_hw_free,
|
||||
.hw_params = pcm_hw_params,
|
||||
.hw_free = pcm_hw_free,
|
||||
.prepare = playback_prepare,
|
||||
.trigger = playback_trigger,
|
||||
.pointer = playback_pointer,
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// dice-presonus.c - a part of driver for DICE based devices
|
||||
//
|
||||
// Copyright (c) 2019 Takashi Sakamoto
|
||||
//
|
||||
// Licensed under the terms of the GNU General Public License, version 2.
|
||||
|
||||
#include "dice.h"
|
||||
|
||||
struct dice_presonus_spec {
|
||||
unsigned int tx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT];
|
||||
unsigned int rx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT];
|
||||
bool has_midi;
|
||||
};
|
||||
|
||||
static const struct dice_presonus_spec dice_presonus_firesutio = {
|
||||
.tx_pcm_chs = {{16, 16, 0}, {10, 2, 0} },
|
||||
.rx_pcm_chs = {{16, 16, 0}, {10, 2, 0} },
|
||||
.has_midi = true,
|
||||
};
|
||||
|
||||
int snd_dice_detect_presonus_formats(struct snd_dice *dice)
|
||||
{
|
||||
static const struct {
|
||||
u32 model_id;
|
||||
const struct dice_presonus_spec *spec;
|
||||
} *entry, entries[] = {
|
||||
{0x000008, &dice_presonus_firesutio},
|
||||
};
|
||||
struct fw_csr_iterator it;
|
||||
int key, val, model_id;
|
||||
int i;
|
||||
|
||||
model_id = 0;
|
||||
fw_csr_iterator_init(&it, dice->unit->directory);
|
||||
while (fw_csr_iterator_next(&it, &key, &val)) {
|
||||
if (key == CSR_MODEL) {
|
||||
model_id = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(entries); ++i) {
|
||||
entry = entries + i;
|
||||
if (entry->model_id == model_id)
|
||||
break;
|
||||
}
|
||||
if (i == ARRAY_SIZE(entries))
|
||||
return -ENODEV;
|
||||
|
||||
memcpy(dice->tx_pcm_chs, entry->spec->tx_pcm_chs,
|
||||
MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * sizeof(unsigned int));
|
||||
memcpy(dice->rx_pcm_chs, entry->spec->rx_pcm_chs,
|
||||
MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * sizeof(unsigned int));
|
||||
|
||||
if (entry->spec->has_midi) {
|
||||
dice->tx_midi_ports[0] = 1;
|
||||
dice->rx_midi_ports[0] = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -137,18 +137,9 @@ static int get_register_params(struct snd_dice *dice,
|
|||
|
||||
static void release_resources(struct snd_dice *dice)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < MAX_STREAMS; i++) {
|
||||
if (amdtp_stream_running(&dice->tx_stream[i])) {
|
||||
amdtp_stream_pcm_abort(&dice->tx_stream[i]);
|
||||
amdtp_stream_stop(&dice->tx_stream[i]);
|
||||
}
|
||||
if (amdtp_stream_running(&dice->rx_stream[i])) {
|
||||
amdtp_stream_pcm_abort(&dice->rx_stream[i]);
|
||||
amdtp_stream_stop(&dice->rx_stream[i]);
|
||||
}
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_STREAMS; ++i) {
|
||||
fw_iso_resources_free(&dice->tx_resources[i]);
|
||||
fw_iso_resources_free(&dice->rx_resources[i]);
|
||||
}
|
||||
|
@ -163,10 +154,14 @@ static void stop_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
|
|||
for (i = 0; i < params->count; i++) {
|
||||
reg = cpu_to_be32((u32)-1);
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
amdtp_stream_stop(&dice->tx_stream[i]);
|
||||
|
||||
snd_dice_transaction_write_tx(dice,
|
||||
params->size * i + TX_ISOCHRONOUS,
|
||||
®, sizeof(reg));
|
||||
} else {
|
||||
amdtp_stream_stop(&dice->rx_stream[i]);
|
||||
|
||||
snd_dice_transaction_write_rx(dice,
|
||||
params->size * i + RX_ISOCHRONOUS,
|
||||
®, sizeof(reg));
|
||||
|
@ -174,35 +169,22 @@ static void stop_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
|
|||
}
|
||||
}
|
||||
|
||||
static int keep_resources(struct snd_dice *dice,
|
||||
enum amdtp_stream_direction dir, unsigned int index,
|
||||
unsigned int rate, unsigned int pcm_chs,
|
||||
unsigned int midi_ports)
|
||||
static int keep_resources(struct snd_dice *dice, struct amdtp_stream *stream,
|
||||
struct fw_iso_resources *resources, unsigned int rate,
|
||||
unsigned int pcm_chs, unsigned int midi_ports)
|
||||
{
|
||||
struct amdtp_stream *stream;
|
||||
struct fw_iso_resources *resources;
|
||||
bool double_pcm_frames;
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
stream = &dice->tx_stream[index];
|
||||
resources = &dice->tx_resources[index];
|
||||
} else {
|
||||
stream = &dice->rx_stream[index];
|
||||
resources = &dice->rx_resources[index];
|
||||
}
|
||||
|
||||
/*
|
||||
* At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in
|
||||
* one data block of AMDTP packet. Thus sampling transfer frequency is
|
||||
* a half of PCM sampling frequency, i.e. PCM frames at 192.0 kHz are
|
||||
* transferred on AMDTP packets at 96 kHz. Two successive samples of a
|
||||
* channel are stored consecutively in the packet. This quirk is called
|
||||
* as 'Dual Wire'.
|
||||
* For this quirk, blocking mode is required and PCM buffer size should
|
||||
* be aligned to SYT_INTERVAL.
|
||||
*/
|
||||
// At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in
|
||||
// one data block of AMDTP packet. Thus sampling transfer frequency is
|
||||
// a half of PCM sampling frequency, i.e. PCM frames at 192.0 kHz are
|
||||
// transferred on AMDTP packets at 96 kHz. Two successive samples of a
|
||||
// channel are stored consecutively in the packet. This quirk is called
|
||||
// as 'Dual Wire'.
|
||||
// For this quirk, blocking mode is required and PCM buffer size should
|
||||
// be aligned to SYT_INTERVAL.
|
||||
double_pcm_frames = rate > 96000;
|
||||
if (double_pcm_frames) {
|
||||
rate /= 2;
|
||||
|
@ -229,40 +211,40 @@ static int keep_resources(struct snd_dice *dice,
|
|||
fw_parent_device(dice->unit)->max_speed);
|
||||
}
|
||||
|
||||
static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
|
||||
unsigned int rate, struct reg_params *params)
|
||||
static int keep_dual_resources(struct snd_dice *dice, unsigned int rate,
|
||||
enum amdtp_stream_direction dir,
|
||||
struct reg_params *params)
|
||||
{
|
||||
__be32 reg[2];
|
||||
enum snd_dice_rate_mode mode;
|
||||
unsigned int i, pcm_chs, midi_ports;
|
||||
struct amdtp_stream *streams;
|
||||
struct fw_iso_resources *resources;
|
||||
struct fw_device *fw_dev = fw_parent_device(dice->unit);
|
||||
int err = 0;
|
||||
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
streams = dice->tx_stream;
|
||||
resources = dice->tx_resources;
|
||||
} else {
|
||||
streams = dice->rx_stream;
|
||||
resources = dice->rx_resources;
|
||||
}
|
||||
int i;
|
||||
int err;
|
||||
|
||||
err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
for (i = 0; i < params->count; i++) {
|
||||
for (i = 0; i < params->count; ++i) {
|
||||
__be32 reg[2];
|
||||
struct amdtp_stream *stream;
|
||||
struct fw_iso_resources *resources;
|
||||
unsigned int pcm_cache;
|
||||
unsigned int midi_cache;
|
||||
unsigned int pcm_chs;
|
||||
unsigned int midi_ports;
|
||||
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
stream = &dice->tx_stream[i];
|
||||
resources = &dice->tx_resources[i];
|
||||
|
||||
pcm_cache = dice->tx_pcm_chs[i][mode];
|
||||
midi_cache = dice->tx_midi_ports[i];
|
||||
err = snd_dice_transaction_read_tx(dice,
|
||||
params->size * i + TX_NUMBER_AUDIO,
|
||||
reg, sizeof(reg));
|
||||
} else {
|
||||
stream = &dice->rx_stream[i];
|
||||
resources = &dice->rx_resources[i];
|
||||
|
||||
pcm_cache = dice->rx_pcm_chs[i][mode];
|
||||
midi_cache = dice->rx_midi_ports[i];
|
||||
err = snd_dice_transaction_read_rx(dice,
|
||||
|
@ -274,7 +256,7 @@ static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
|
|||
pcm_chs = be32_to_cpu(reg[0]);
|
||||
midi_ports = be32_to_cpu(reg[1]);
|
||||
|
||||
/* These are important for developer of this driver. */
|
||||
// These are important for developer of this driver.
|
||||
if (pcm_chs != pcm_cache || midi_ports != midi_cache) {
|
||||
dev_info(&dice->unit->device,
|
||||
"cache mismatch: pcm: %u:%u, midi: %u:%u\n",
|
||||
|
@ -282,141 +264,170 @@ static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
|
|||
return -EPROTO;
|
||||
}
|
||||
|
||||
err = keep_resources(dice, dir, i, rate, pcm_chs, midi_ports);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
reg[0] = cpu_to_be32(resources[i].channel);
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
err = snd_dice_transaction_write_tx(dice,
|
||||
params->size * i + TX_ISOCHRONOUS,
|
||||
reg, sizeof(reg[0]));
|
||||
} else {
|
||||
err = snd_dice_transaction_write_rx(dice,
|
||||
params->size * i + RX_ISOCHRONOUS,
|
||||
reg, sizeof(reg[0]));
|
||||
}
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
reg[0] = cpu_to_be32(fw_dev->max_speed);
|
||||
err = snd_dice_transaction_write_tx(dice,
|
||||
params->size * i + TX_SPEED,
|
||||
reg, sizeof(reg[0]));
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = amdtp_stream_start(&streams[i], resources[i].channel,
|
||||
fw_dev->max_speed);
|
||||
err = keep_resources(dice, stream, resources, rate, pcm_chs,
|
||||
midi_ports);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int start_duplex_streams(struct snd_dice *dice, unsigned int rate)
|
||||
static void finish_session(struct snd_dice *dice, struct reg_params *tx_params,
|
||||
struct reg_params *rx_params)
|
||||
{
|
||||
struct reg_params tx_params, rx_params;
|
||||
int i;
|
||||
stop_streams(dice, AMDTP_IN_STREAM, tx_params);
|
||||
stop_streams(dice, AMDTP_OUT_STREAM, rx_params);
|
||||
|
||||
snd_dice_transaction_clear_enable(dice);
|
||||
}
|
||||
|
||||
int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate)
|
||||
{
|
||||
unsigned int curr_rate;
|
||||
int err;
|
||||
|
||||
err = get_register_params(dice, &tx_params, &rx_params);
|
||||
// Check sampling transmission frequency.
|
||||
err = snd_dice_transaction_get_rate(dice, &curr_rate);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (rate == 0)
|
||||
rate = curr_rate;
|
||||
|
||||
/* Stop transmission. */
|
||||
stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
|
||||
stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
|
||||
snd_dice_transaction_clear_enable(dice);
|
||||
release_resources(dice);
|
||||
if (dice->substreams_counter == 0 || curr_rate != rate) {
|
||||
struct reg_params tx_params, rx_params;
|
||||
|
||||
err = ensure_phase_lock(dice, rate);
|
||||
if (err < 0) {
|
||||
dev_err(&dice->unit->device, "fail to ensure phase lock\n");
|
||||
return err;
|
||||
}
|
||||
err = get_register_params(dice, &tx_params, &rx_params);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Likely to have changed stream formats. */
|
||||
err = get_register_params(dice, &tx_params, &rx_params);
|
||||
if (err < 0)
|
||||
return err;
|
||||
finish_session(dice, &tx_params, &rx_params);
|
||||
|
||||
/* Start both streams. */
|
||||
err = start_streams(dice, AMDTP_IN_STREAM, rate, &tx_params);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
err = start_streams(dice, AMDTP_OUT_STREAM, rate, &rx_params);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
release_resources(dice);
|
||||
|
||||
err = snd_dice_transaction_set_enable(dice);
|
||||
if (err < 0) {
|
||||
dev_err(&dice->unit->device, "fail to enable interface\n");
|
||||
goto error;
|
||||
}
|
||||
// Just after owning the unit (GLOBAL_OWNER), the unit can
|
||||
// return invalid stream formats. Selecting clock parameters
|
||||
// have an effect for the unit to refine it.
|
||||
err = ensure_phase_lock(dice, rate);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
for (i = 0; i < MAX_STREAMS; i++) {
|
||||
if ((i < tx_params.count &&
|
||||
!amdtp_stream_wait_callback(&dice->tx_stream[i],
|
||||
CALLBACK_TIMEOUT)) ||
|
||||
(i < rx_params.count &&
|
||||
!amdtp_stream_wait_callback(&dice->rx_stream[i],
|
||||
CALLBACK_TIMEOUT))) {
|
||||
err = -ETIMEDOUT;
|
||||
// After changing sampling transfer frequency, the value of
|
||||
// register can be changed.
|
||||
err = get_register_params(dice, &tx_params, &rx_params);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = keep_dual_resources(dice, rate, AMDTP_IN_STREAM,
|
||||
&tx_params);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = keep_dual_resources(dice, rate, AMDTP_OUT_STREAM,
|
||||
&rx_params);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
error:
|
||||
stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
|
||||
stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
|
||||
snd_dice_transaction_clear_enable(dice);
|
||||
release_resources(dice);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
|
||||
unsigned int rate, struct reg_params *params)
|
||||
{
|
||||
unsigned int max_speed = fw_parent_device(dice->unit)->max_speed;
|
||||
int i;
|
||||
int err;
|
||||
|
||||
for (i = 0; i < params->count; i++) {
|
||||
struct amdtp_stream *stream;
|
||||
struct fw_iso_resources *resources;
|
||||
__be32 reg;
|
||||
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
stream = dice->tx_stream + i;
|
||||
resources = dice->tx_resources + i;
|
||||
} else {
|
||||
stream = dice->rx_stream + i;
|
||||
resources = dice->rx_resources + i;
|
||||
}
|
||||
|
||||
reg = cpu_to_be32(resources->channel);
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
err = snd_dice_transaction_write_tx(dice,
|
||||
params->size * i + TX_ISOCHRONOUS,
|
||||
®, sizeof(reg));
|
||||
} else {
|
||||
err = snd_dice_transaction_write_rx(dice,
|
||||
params->size * i + RX_ISOCHRONOUS,
|
||||
®, sizeof(reg));
|
||||
}
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
reg = cpu_to_be32(max_speed);
|
||||
err = snd_dice_transaction_write_tx(dice,
|
||||
params->size * i + TX_SPEED,
|
||||
®, sizeof(reg));
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = amdtp_stream_start(stream, resources->channel, max_speed);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* MEMO: After this function, there're two states of streams:
|
||||
* - None streams are running.
|
||||
* - All streams are running.
|
||||
*/
|
||||
int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate)
|
||||
int snd_dice_stream_start_duplex(struct snd_dice *dice)
|
||||
{
|
||||
unsigned int curr_rate;
|
||||
unsigned int generation = dice->rx_resources[0].generation;
|
||||
struct reg_params tx_params, rx_params;
|
||||
unsigned int i;
|
||||
unsigned int rate;
|
||||
enum snd_dice_rate_mode mode;
|
||||
int err;
|
||||
|
||||
if (dice->substreams_counter == 0)
|
||||
return -EIO;
|
||||
|
||||
/* Check sampling transmission frequency. */
|
||||
err = snd_dice_transaction_get_rate(dice, &curr_rate);
|
||||
if (err < 0) {
|
||||
dev_err(&dice->unit->device,
|
||||
"fail to get sampling rate\n");
|
||||
err = get_register_params(dice, &tx_params, &rx_params);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
if (rate == 0)
|
||||
rate = curr_rate;
|
||||
if (rate != curr_rate)
|
||||
goto restart;
|
||||
|
||||
/* Check error of packet streaming. */
|
||||
// Check error of packet streaming.
|
||||
for (i = 0; i < MAX_STREAMS; ++i) {
|
||||
if (amdtp_streaming_error(&dice->tx_stream[i]))
|
||||
break;
|
||||
if (amdtp_streaming_error(&dice->rx_stream[i]))
|
||||
if (amdtp_streaming_error(&dice->tx_stream[i]) ||
|
||||
amdtp_streaming_error(&dice->rx_stream[i])) {
|
||||
finish_session(dice, &tx_params, &rx_params);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i < MAX_STREAMS)
|
||||
goto restart;
|
||||
|
||||
/* Check required streams are running or not. */
|
||||
if (generation != fw_parent_device(dice->unit)->card->generation) {
|
||||
for (i = 0; i < MAX_STREAMS; ++i) {
|
||||
if (i < tx_params.count)
|
||||
fw_iso_resources_update(dice->tx_resources + i);
|
||||
if (i < rx_params.count)
|
||||
fw_iso_resources_update(dice->rx_resources + i);
|
||||
}
|
||||
}
|
||||
|
||||
// Check required streams are running or not.
|
||||
err = snd_dice_transaction_get_rate(dice, &rate);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
@ -428,12 +439,40 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate)
|
|||
!amdtp_stream_running(&dice->rx_stream[i]))
|
||||
break;
|
||||
}
|
||||
if (i < MAX_STREAMS)
|
||||
goto restart;
|
||||
if (i < MAX_STREAMS) {
|
||||
// Start both streams.
|
||||
err = start_streams(dice, AMDTP_IN_STREAM, rate, &tx_params);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = start_streams(dice, AMDTP_OUT_STREAM, rate, &rx_params);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_dice_transaction_set_enable(dice);
|
||||
if (err < 0) {
|
||||
dev_err(&dice->unit->device,
|
||||
"fail to enable interface\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_STREAMS; i++) {
|
||||
if ((i < tx_params.count &&
|
||||
!amdtp_stream_wait_callback(&dice->tx_stream[i],
|
||||
CALLBACK_TIMEOUT)) ||
|
||||
(i < rx_params.count &&
|
||||
!amdtp_stream_wait_callback(&dice->rx_stream[i],
|
||||
CALLBACK_TIMEOUT))) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
restart:
|
||||
return start_duplex_streams(dice, rate);
|
||||
error:
|
||||
finish_session(dice, &tx_params, &rx_params);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -445,17 +484,12 @@ void snd_dice_stream_stop_duplex(struct snd_dice *dice)
|
|||
{
|
||||
struct reg_params tx_params, rx_params;
|
||||
|
||||
if (dice->substreams_counter > 0)
|
||||
return;
|
||||
if (dice->substreams_counter == 0) {
|
||||
if (get_register_params(dice, &tx_params, &rx_params) >= 0)
|
||||
finish_session(dice, &tx_params, &rx_params);
|
||||
|
||||
snd_dice_transaction_clear_enable(dice);
|
||||
|
||||
if (get_register_params(dice, &tx_params, &rx_params) == 0) {
|
||||
stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
|
||||
stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
|
||||
release_resources(dice);
|
||||
}
|
||||
|
||||
release_resources(dice);
|
||||
}
|
||||
|
||||
static int init_stream(struct snd_dice *dice, enum amdtp_stream_direction dir,
|
||||
|
|
|
@ -19,6 +19,7 @@ MODULE_LICENSE("GPL v2");
|
|||
#define OUI_MAUDIO 0x000d6c
|
||||
#define OUI_MYTEK 0x001ee8
|
||||
#define OUI_SSL 0x0050c2 // Actually ID reserved by IEEE.
|
||||
#define OUI_PRESONUS 0x000a92
|
||||
|
||||
#define DICE_CATEGORY_ID 0x04
|
||||
#define WEISS_CATEGORY_ID 0x00
|
||||
|
@ -371,6 +372,14 @@ static const struct ieee1394_device_id dice_id_table[] = {
|
|||
.vendor_id = OUI_SSL,
|
||||
.model_id = 0x000070,
|
||||
},
|
||||
// Presonus FireStudio.
|
||||
{
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID |
|
||||
IEEE1394_MATCH_MODEL_ID,
|
||||
.vendor_id = OUI_PRESONUS,
|
||||
.model_id = 0x000008,
|
||||
.driver_data = (kernel_ulong_t)snd_dice_detect_presonus_formats,
|
||||
},
|
||||
{
|
||||
.match_flags = IEEE1394_MATCH_VERSION,
|
||||
.version = DICE_INTERFACE,
|
||||
|
|
|
@ -204,10 +204,11 @@ extern const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT];
|
|||
|
||||
int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate,
|
||||
enum snd_dice_rate_mode *mode);
|
||||
int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate);
|
||||
int snd_dice_stream_start_duplex(struct snd_dice *dice);
|
||||
void snd_dice_stream_stop_duplex(struct snd_dice *dice);
|
||||
int snd_dice_stream_init_duplex(struct snd_dice *dice);
|
||||
void snd_dice_stream_destroy_duplex(struct snd_dice *dice);
|
||||
int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate);
|
||||
void snd_dice_stream_update_duplex(struct snd_dice *dice);
|
||||
int snd_dice_stream_detect_current_formats(struct snd_dice *dice);
|
||||
|
||||
|
@ -226,5 +227,6 @@ int snd_dice_detect_tcelectronic_formats(struct snd_dice *dice);
|
|||
int snd_dice_detect_alesis_formats(struct snd_dice *dice);
|
||||
int snd_dice_detect_extension_formats(struct snd_dice *dice);
|
||||
int snd_dice_detect_mytek_formats(struct snd_dice *dice);
|
||||
int snd_dice_detect_presonus_formats(struct snd_dice *dice);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -127,7 +127,7 @@ int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate,
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
s->fdf = AMDTP_FDF_AM824 | s->sfc;
|
||||
s->ctx_data.rx.fdf = AMDTP_FDF_AM824 | s->sfc;
|
||||
|
||||
p->pcm_channels = pcm_channels;
|
||||
|
||||
|
|
|
@ -17,8 +17,13 @@ static int midi_open(struct snd_rawmidi_substream *substream)
|
|||
return err;
|
||||
|
||||
mutex_lock(&dg00x->mutex);
|
||||
dg00x->substreams_counter++;
|
||||
err = snd_dg00x_stream_start_duplex(dg00x, 0);
|
||||
err = snd_dg00x_stream_reserve_duplex(dg00x, 0);
|
||||
if (err >= 0) {
|
||||
++dg00x->substreams_counter;
|
||||
err = snd_dg00x_stream_start_duplex(dg00x);
|
||||
if (err < 0)
|
||||
--dg00x->substreams_counter;
|
||||
}
|
||||
mutex_unlock(&dg00x->mutex);
|
||||
if (err < 0)
|
||||
snd_dg00x_stream_lock_release(dg00x);
|
||||
|
@ -31,7 +36,7 @@ static int midi_close(struct snd_rawmidi_substream *substream)
|
|||
struct snd_dg00x *dg00x = substream->rmidi->private_data;
|
||||
|
||||
mutex_lock(&dg00x->mutex);
|
||||
dg00x->substreams_counter--;
|
||||
--dg00x->substreams_counter;
|
||||
snd_dg00x_stream_stop_duplex(dg00x);
|
||||
mutex_unlock(&dg00x->mutex);
|
||||
|
||||
|
|
|
@ -154,8 +154,8 @@ static int pcm_close(struct snd_pcm_substream *substream)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
static int pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct snd_dg00x *dg00x = substream->private_data;
|
||||
int err;
|
||||
|
@ -166,58 +166,26 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
|
|||
return err;
|
||||
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
|
||||
unsigned int rate = params_rate(hw_params);
|
||||
|
||||
mutex_lock(&dg00x->mutex);
|
||||
dg00x->substreams_counter++;
|
||||
err = snd_dg00x_stream_reserve_duplex(dg00x, rate);
|
||||
if (err >= 0)
|
||||
++dg00x->substreams_counter;
|
||||
mutex_unlock(&dg00x->mutex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct snd_dg00x *dg00x = substream->private_data;
|
||||
int err;
|
||||
|
||||
err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
|
||||
params_buffer_bytes(hw_params));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
|
||||
mutex_lock(&dg00x->mutex);
|
||||
dg00x->substreams_counter++;
|
||||
mutex_unlock(&dg00x->mutex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
|
||||
static int pcm_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_dg00x *dg00x = substream->private_data;
|
||||
|
||||
mutex_lock(&dg00x->mutex);
|
||||
|
||||
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
|
||||
dg00x->substreams_counter--;
|
||||
|
||||
snd_dg00x_stream_stop_duplex(dg00x);
|
||||
|
||||
mutex_unlock(&dg00x->mutex);
|
||||
|
||||
return snd_pcm_lib_free_vmalloc_buffer(substream);
|
||||
}
|
||||
|
||||
static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_dg00x *dg00x = substream->private_data;
|
||||
|
||||
mutex_lock(&dg00x->mutex);
|
||||
|
||||
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
|
||||
dg00x->substreams_counter--;
|
||||
--dg00x->substreams_counter;
|
||||
|
||||
snd_dg00x_stream_stop_duplex(dg00x);
|
||||
|
||||
|
@ -229,12 +197,11 @@ static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
|
|||
static int pcm_capture_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_dg00x *dg00x = substream->private_data;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
int err;
|
||||
|
||||
mutex_lock(&dg00x->mutex);
|
||||
|
||||
err = snd_dg00x_stream_start_duplex(dg00x, runtime->rate);
|
||||
err = snd_dg00x_stream_start_duplex(dg00x);
|
||||
if (err >= 0)
|
||||
amdtp_stream_pcm_prepare(&dg00x->tx_stream);
|
||||
|
||||
|
@ -246,12 +213,11 @@ static int pcm_capture_prepare(struct snd_pcm_substream *substream)
|
|||
static int pcm_playback_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_dg00x *dg00x = substream->private_data;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
int err;
|
||||
|
||||
mutex_lock(&dg00x->mutex);
|
||||
|
||||
err = snd_dg00x_stream_start_duplex(dg00x, runtime->rate);
|
||||
err = snd_dg00x_stream_start_duplex(dg00x);
|
||||
if (err >= 0) {
|
||||
amdtp_stream_pcm_prepare(&dg00x->rx_stream);
|
||||
amdtp_dot_reset(&dg00x->rx_stream);
|
||||
|
@ -332,8 +298,8 @@ int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x)
|
|||
.open = pcm_open,
|
||||
.close = pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = pcm_capture_hw_params,
|
||||
.hw_free = pcm_capture_hw_free,
|
||||
.hw_params = pcm_hw_params,
|
||||
.hw_free = pcm_hw_free,
|
||||
.prepare = pcm_capture_prepare,
|
||||
.trigger = pcm_capture_trigger,
|
||||
.pointer = pcm_capture_pointer,
|
||||
|
@ -344,8 +310,8 @@ int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x)
|
|||
.open = pcm_open,
|
||||
.close = pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = pcm_playback_hw_params,
|
||||
.hw_free = pcm_playback_hw_free,
|
||||
.hw_params = pcm_hw_params,
|
||||
.hw_free = pcm_hw_free,
|
||||
.prepare = pcm_playback_prepare,
|
||||
.trigger = pcm_playback_trigger,
|
||||
.pointer = pcm_playback_pointer,
|
||||
|
|
|
@ -124,11 +124,25 @@ int snd_dg00x_stream_get_external_rate(struct snd_dg00x *dg00x,
|
|||
|
||||
static void finish_session(struct snd_dg00x *dg00x)
|
||||
{
|
||||
__be32 data = cpu_to_be32(0x00000003);
|
||||
__be32 data;
|
||||
|
||||
amdtp_stream_stop(&dg00x->tx_stream);
|
||||
amdtp_stream_stop(&dg00x->rx_stream);
|
||||
|
||||
data = cpu_to_be32(0x00000003);
|
||||
snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_SET,
|
||||
&data, sizeof(data), 0);
|
||||
|
||||
// Unregister isochronous channels for both direction.
|
||||
data = 0;
|
||||
snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
|
||||
&data, sizeof(data), 0);
|
||||
|
||||
// Just after finishing the session, the device may lost transmitting
|
||||
// functionality for a short time.
|
||||
msleep(50);
|
||||
}
|
||||
|
||||
static int begin_session(struct snd_dg00x *dg00x)
|
||||
|
@ -137,11 +151,20 @@ static int begin_session(struct snd_dg00x *dg00x)
|
|||
u32 curr;
|
||||
int err;
|
||||
|
||||
// Register isochronous channels for both direction.
|
||||
data = cpu_to_be32((dg00x->tx_resources.channel << 16) |
|
||||
dg00x->rx_resources.channel);
|
||||
err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
|
||||
&data, sizeof(data), 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
|
||||
DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_STATE,
|
||||
&data, sizeof(data), 0);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
return err;
|
||||
curr = be32_to_cpu(data);
|
||||
|
||||
if (curr == 0)
|
||||
|
@ -156,39 +179,23 @@ static int begin_session(struct snd_dg00x *dg00x)
|
|||
DG00X_OFFSET_STREAMING_SET,
|
||||
&data, sizeof(data), 0);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
break;
|
||||
|
||||
msleep(20);
|
||||
curr--;
|
||||
}
|
||||
|
||||
return 0;
|
||||
error:
|
||||
finish_session(dg00x);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void release_resources(struct snd_dg00x *dg00x)
|
||||
static int keep_resources(struct snd_dg00x *dg00x, struct amdtp_stream *stream,
|
||||
unsigned int rate)
|
||||
{
|
||||
__be32 data = 0;
|
||||
|
||||
/* Unregister isochronous channels for both direction. */
|
||||
snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
|
||||
&data, sizeof(data), 0);
|
||||
|
||||
/* Release isochronous resources. */
|
||||
fw_iso_resources_free(&dg00x->tx_resources);
|
||||
fw_iso_resources_free(&dg00x->rx_resources);
|
||||
}
|
||||
|
||||
static int keep_resources(struct snd_dg00x *dg00x, unsigned int rate)
|
||||
{
|
||||
unsigned int i;
|
||||
__be32 data;
|
||||
struct fw_iso_resources *resources;
|
||||
int i;
|
||||
int err;
|
||||
|
||||
/* Check sampling rate. */
|
||||
// Check sampling rate.
|
||||
for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
|
||||
if (snd_dg00x_stream_rates[i] == rate)
|
||||
break;
|
||||
|
@ -196,41 +203,19 @@ static int keep_resources(struct snd_dg00x *dg00x, unsigned int rate)
|
|||
if (i == SND_DG00X_RATE_COUNT)
|
||||
return -EINVAL;
|
||||
|
||||
/* Keep resources for out-stream. */
|
||||
err = amdtp_dot_set_parameters(&dg00x->rx_stream, rate,
|
||||
if (stream == &dg00x->tx_stream)
|
||||
resources = &dg00x->tx_resources;
|
||||
else
|
||||
resources = &dg00x->rx_resources;
|
||||
|
||||
err = amdtp_dot_set_parameters(stream, rate,
|
||||
snd_dg00x_stream_pcm_channels[i]);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = fw_iso_resources_allocate(&dg00x->rx_resources,
|
||||
amdtp_stream_get_max_payload(&dg00x->rx_stream),
|
||||
|
||||
return fw_iso_resources_allocate(resources,
|
||||
amdtp_stream_get_max_payload(stream),
|
||||
fw_parent_device(dg00x->unit)->max_speed);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Keep resources for in-stream. */
|
||||
err = amdtp_dot_set_parameters(&dg00x->tx_stream, rate,
|
||||
snd_dg00x_stream_pcm_channels[i]);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = fw_iso_resources_allocate(&dg00x->tx_resources,
|
||||
amdtp_stream_get_max_payload(&dg00x->tx_stream),
|
||||
fw_parent_device(dg00x->unit)->max_speed);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
/* Register isochronous channels for both direction. */
|
||||
data = cpu_to_be32((dg00x->tx_resources.channel << 16) |
|
||||
dg00x->rx_resources.channel);
|
||||
err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
|
||||
&data, sizeof(data), 0);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
error:
|
||||
release_resources(dg00x);
|
||||
return err;
|
||||
}
|
||||
|
||||
int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x)
|
||||
|
@ -272,28 +257,61 @@ void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x)
|
|||
fw_iso_resources_destroy(&dg00x->tx_resources);
|
||||
}
|
||||
|
||||
int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x, unsigned int rate)
|
||||
int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate)
|
||||
{
|
||||
unsigned int curr_rate;
|
||||
int err;
|
||||
|
||||
err = snd_dg00x_stream_get_local_rate(dg00x, &curr_rate);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (rate == 0)
|
||||
rate = curr_rate;
|
||||
|
||||
if (dg00x->substreams_counter == 0 || curr_rate != rate) {
|
||||
finish_session(dg00x);
|
||||
|
||||
fw_iso_resources_free(&dg00x->tx_resources);
|
||||
fw_iso_resources_free(&dg00x->rx_resources);
|
||||
|
||||
err = snd_dg00x_stream_set_local_rate(dg00x, rate);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = keep_resources(dg00x, &dg00x->rx_stream, rate);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = keep_resources(dg00x, &dg00x->tx_stream, rate);
|
||||
if (err < 0) {
|
||||
fw_iso_resources_free(&dg00x->rx_resources);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x)
|
||||
{
|
||||
unsigned int generation = dg00x->rx_resources.generation;
|
||||
int err = 0;
|
||||
|
||||
if (dg00x->substreams_counter == 0)
|
||||
goto end;
|
||||
return 0;
|
||||
|
||||
/* Check current sampling rate. */
|
||||
err = snd_dg00x_stream_get_local_rate(dg00x, &curr_rate);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
if (rate == 0)
|
||||
rate = curr_rate;
|
||||
if (curr_rate != rate ||
|
||||
amdtp_streaming_error(&dg00x->tx_stream) ||
|
||||
amdtp_streaming_error(&dg00x->rx_stream)) {
|
||||
if (amdtp_streaming_error(&dg00x->tx_stream) ||
|
||||
amdtp_streaming_error(&dg00x->rx_stream))
|
||||
finish_session(dg00x);
|
||||
|
||||
amdtp_stream_stop(&dg00x->tx_stream);
|
||||
amdtp_stream_stop(&dg00x->rx_stream);
|
||||
release_resources(dg00x);
|
||||
if (generation != fw_parent_device(dg00x->unit)->card->generation) {
|
||||
err = fw_iso_resources_update(&dg00x->tx_resources);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = fw_iso_resources_update(&dg00x->rx_resources);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -301,14 +319,6 @@ int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x, unsigned int rate)
|
|||
* which source of clock is used.
|
||||
*/
|
||||
if (!amdtp_stream_running(&dg00x->rx_stream)) {
|
||||
err = snd_dg00x_stream_set_local_rate(dg00x, rate);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = keep_resources(dg00x, rate);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = begin_session(dg00x);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
@ -343,33 +353,22 @@ int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x, unsigned int rate)
|
|||
goto error;
|
||||
}
|
||||
}
|
||||
end:
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
error:
|
||||
finish_session(dg00x);
|
||||
|
||||
amdtp_stream_stop(&dg00x->tx_stream);
|
||||
amdtp_stream_stop(&dg00x->rx_stream);
|
||||
release_resources(dg00x);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x)
|
||||
{
|
||||
if (dg00x->substreams_counter > 0)
|
||||
return;
|
||||
if (dg00x->substreams_counter == 0) {
|
||||
finish_session(dg00x);
|
||||
|
||||
amdtp_stream_stop(&dg00x->tx_stream);
|
||||
amdtp_stream_stop(&dg00x->rx_stream);
|
||||
finish_session(dg00x);
|
||||
release_resources(dg00x);
|
||||
|
||||
/*
|
||||
* Just after finishing the session, the device may lost transmitting
|
||||
* functionality for a short time.
|
||||
*/
|
||||
msleep(50);
|
||||
fw_iso_resources_free(&dg00x->tx_resources);
|
||||
fw_iso_resources_free(&dg00x->rx_resources);
|
||||
}
|
||||
}
|
||||
|
||||
void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x)
|
||||
|
|
|
@ -139,7 +139,8 @@ int snd_dg00x_stream_get_clock(struct snd_dg00x *dg00x,
|
|||
int snd_dg00x_stream_check_external_clock(struct snd_dg00x *dg00x,
|
||||
bool *detect);
|
||||
int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x);
|
||||
int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x, unsigned int rate);
|
||||
int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate);
|
||||
int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x);
|
||||
void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x);
|
||||
void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x);
|
||||
void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x);
|
||||
|
|
|
@ -198,8 +198,8 @@ static int pcm_close(struct snd_pcm_substream *substream)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
static int pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct snd_ff *ff = substream->private_data;
|
||||
int err;
|
||||
|
@ -210,58 +210,26 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
|
|||
return err;
|
||||
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
|
||||
unsigned int rate = params_rate(hw_params);
|
||||
|
||||
mutex_lock(&ff->mutex);
|
||||
ff->substreams_counter++;
|
||||
err = snd_ff_stream_reserve_duplex(ff, rate);
|
||||
if (err >= 0)
|
||||
++ff->substreams_counter;
|
||||
mutex_unlock(&ff->mutex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct snd_ff *ff = substream->private_data;
|
||||
int err;
|
||||
|
||||
err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
|
||||
params_buffer_bytes(hw_params));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
|
||||
mutex_lock(&ff->mutex);
|
||||
ff->substreams_counter++;
|
||||
mutex_unlock(&ff->mutex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
|
||||
static int pcm_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_ff *ff = substream->private_data;
|
||||
|
||||
mutex_lock(&ff->mutex);
|
||||
|
||||
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
|
||||
ff->substreams_counter--;
|
||||
|
||||
snd_ff_stream_stop_duplex(ff);
|
||||
|
||||
mutex_unlock(&ff->mutex);
|
||||
|
||||
return snd_pcm_lib_free_vmalloc_buffer(substream);
|
||||
}
|
||||
|
||||
static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_ff *ff = substream->private_data;
|
||||
|
||||
mutex_lock(&ff->mutex);
|
||||
|
||||
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
|
||||
ff->substreams_counter--;
|
||||
--ff->substreams_counter;
|
||||
|
||||
snd_ff_stream_stop_duplex(ff);
|
||||
|
||||
|
@ -374,8 +342,8 @@ int snd_ff_create_pcm_devices(struct snd_ff *ff)
|
|||
.open = pcm_open,
|
||||
.close = pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = pcm_capture_hw_params,
|
||||
.hw_free = pcm_capture_hw_free,
|
||||
.hw_params = pcm_hw_params,
|
||||
.hw_free = pcm_hw_free,
|
||||
.prepare = pcm_capture_prepare,
|
||||
.trigger = pcm_capture_trigger,
|
||||
.pointer = pcm_capture_pointer,
|
||||
|
@ -386,8 +354,8 @@ int snd_ff_create_pcm_devices(struct snd_ff *ff)
|
|||
.open = pcm_open,
|
||||
.close = pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = pcm_playback_hw_params,
|
||||
.hw_free = pcm_playback_hw_free,
|
||||
.hw_params = pcm_hw_params,
|
||||
.hw_free = pcm_hw_free,
|
||||
.prepare = pcm_playback_prepare,
|
||||
.trigger = pcm_playback_trigger,
|
||||
.pointer = pcm_playback_pointer,
|
||||
|
|
|
@ -293,27 +293,6 @@ static int former_fill_midi_msg(struct snd_ff *ff,
|
|||
|
||||
#define FF800_TX_PACKET_ISOC_CH 0x0000801c0008
|
||||
|
||||
static int allocate_rx_resources(struct snd_ff *ff)
|
||||
{
|
||||
u32 data;
|
||||
__le32 reg;
|
||||
int err;
|
||||
|
||||
// Controllers should allocate isochronous resources for rx stream.
|
||||
err = fw_iso_resources_allocate(&ff->rx_resources,
|
||||
amdtp_stream_get_max_payload(&ff->rx_stream),
|
||||
fw_parent_device(ff->unit)->max_speed);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
// Set isochronous channel and the number of quadlets of rx packets.
|
||||
data = ff->rx_stream.data_block_quadlets << 3;
|
||||
data = (data << 8) | ff->rx_resources.channel;
|
||||
reg = cpu_to_le32(data);
|
||||
return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
FF800_RX_PACKET_FORMAT, ®, sizeof(reg), 0);
|
||||
}
|
||||
|
||||
static int allocate_tx_resources(struct snd_ff *ff)
|
||||
{
|
||||
__le32 reg;
|
||||
|
@ -355,8 +334,9 @@ static int allocate_tx_resources(struct snd_ff *ff)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ff800_begin_session(struct snd_ff *ff, unsigned int rate)
|
||||
static int ff800_allocate_resources(struct snd_ff *ff, unsigned int rate)
|
||||
{
|
||||
u32 data;
|
||||
__le32 reg;
|
||||
int err;
|
||||
|
||||
|
@ -371,14 +351,38 @@ static int ff800_begin_session(struct snd_ff *ff, unsigned int rate)
|
|||
// Let's sleep for a bit.
|
||||
msleep(100);
|
||||
|
||||
err = allocate_rx_resources(ff);
|
||||
// Controllers should allocate isochronous resources for rx stream.
|
||||
err = fw_iso_resources_allocate(&ff->rx_resources,
|
||||
amdtp_stream_get_max_payload(&ff->rx_stream),
|
||||
fw_parent_device(ff->unit)->max_speed);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = allocate_tx_resources(ff);
|
||||
// Set isochronous channel and the number of quadlets of rx packets.
|
||||
// This should be done before the allocation of tx resources to avoid
|
||||
// periodical noise.
|
||||
data = ff->rx_stream.data_block_quadlets << 3;
|
||||
data = (data << 8) | ff->rx_resources.channel;
|
||||
reg = cpu_to_le32(data);
|
||||
err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
FF800_RX_PACKET_FORMAT, ®, sizeof(reg), 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return allocate_tx_resources(ff);
|
||||
}
|
||||
|
||||
static int ff800_begin_session(struct snd_ff *ff, unsigned int rate)
|
||||
{
|
||||
unsigned int generation = ff->rx_resources.generation;
|
||||
__le32 reg;
|
||||
|
||||
if (generation != fw_parent_device(ff->unit)->card->generation) {
|
||||
int err = fw_iso_resources_update(&ff->rx_resources);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
reg = cpu_to_le32(0x80000000);
|
||||
reg |= cpu_to_le32(ff->tx_stream.data_block_quadlets);
|
||||
if (fw_parent_device(ff->unit)->max_speed == SCODE_800)
|
||||
|
@ -420,6 +424,7 @@ const struct snd_ff_protocol snd_ff_protocol_ff800 = {
|
|||
.fill_midi_msg = former_fill_midi_msg,
|
||||
.get_clock = former_get_clock,
|
||||
.switch_fetching_mode = former_switch_fetching_mode,
|
||||
.allocate_resources = ff800_allocate_resources,
|
||||
.begin_session = ff800_begin_session,
|
||||
.finish_session = ff800_finish_session,
|
||||
.dump_status = former_dump_status,
|
||||
|
@ -431,12 +436,11 @@ const struct snd_ff_protocol snd_ff_protocol_ff800 = {
|
|||
#define FF400_TX_PACKET_FORMAT 0x00008010050cull
|
||||
#define FF400_ISOC_COMM_STOP 0x000080100510ull
|
||||
|
||||
/*
|
||||
* Fireface 400 manages isochronous channel number in 3 bit field. Therefore,
|
||||
* we can allocate between 0 and 7 channel.
|
||||
*/
|
||||
static int keep_resources(struct snd_ff *ff, unsigned int rate)
|
||||
// Fireface 400 manages isochronous channel number in 3 bit field. Therefore,
|
||||
// we can allocate between 0 and 7 channel.
|
||||
static int ff400_allocate_resources(struct snd_ff *ff, unsigned int rate)
|
||||
{
|
||||
__le32 reg;
|
||||
enum snd_ff_stream_mode mode;
|
||||
int i;
|
||||
int err;
|
||||
|
@ -449,11 +453,20 @@ static int keep_resources(struct snd_ff *ff, unsigned int rate)
|
|||
if (i >= CIP_SFC_COUNT)
|
||||
return -EINVAL;
|
||||
|
||||
// Set the number of data blocks transferred in a second.
|
||||
reg = cpu_to_le32(rate);
|
||||
err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
FF400_STF, ®, sizeof(reg), 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
msleep(100);
|
||||
|
||||
err = snd_ff_stream_get_multiplier_mode(i, &mode);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Keep resources for in-stream. */
|
||||
// Keep resources for in-stream.
|
||||
ff->tx_resources.channels_mask = 0x00000000000000ffuLL;
|
||||
err = fw_iso_resources_allocate(&ff->tx_resources,
|
||||
amdtp_stream_get_max_payload(&ff->tx_stream),
|
||||
|
@ -461,7 +474,7 @@ static int keep_resources(struct snd_ff *ff, unsigned int rate)
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Keep resources for out-stream. */
|
||||
// Keep resources for out-stream.
|
||||
ff->rx_resources.channels_mask = 0x00000000000000ffuLL;
|
||||
err = fw_iso_resources_allocate(&ff->rx_resources,
|
||||
amdtp_stream_get_max_payload(&ff->rx_stream),
|
||||
|
@ -474,26 +487,22 @@ static int keep_resources(struct snd_ff *ff, unsigned int rate)
|
|||
|
||||
static int ff400_begin_session(struct snd_ff *ff, unsigned int rate)
|
||||
{
|
||||
unsigned int generation = ff->rx_resources.generation;
|
||||
__le32 reg;
|
||||
int err;
|
||||
|
||||
err = keep_resources(ff, rate);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (generation != fw_parent_device(ff->unit)->card->generation) {
|
||||
err = fw_iso_resources_update(&ff->tx_resources);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Set the number of data blocks transferred in a second. */
|
||||
reg = cpu_to_le32(rate);
|
||||
err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
FF400_STF, ®, sizeof(reg), 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = fw_iso_resources_update(&ff->rx_resources);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
msleep(100);
|
||||
|
||||
/*
|
||||
* Set isochronous channel and the number of quadlets of received
|
||||
* packets.
|
||||
*/
|
||||
// Set isochronous channel and the number of quadlets of received
|
||||
// packets.
|
||||
reg = cpu_to_le32(((ff->rx_stream.data_block_quadlets << 3) << 8) |
|
||||
ff->rx_resources.channel);
|
||||
err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
|
@ -501,11 +510,9 @@ static int ff400_begin_session(struct snd_ff *ff, unsigned int rate)
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* Set isochronous channel and the number of quadlets of transmitted
|
||||
* packet.
|
||||
*/
|
||||
/* TODO: investigate the purpose of this 0x80. */
|
||||
// Set isochronous channel and the number of quadlets of transmitted
|
||||
// packet.
|
||||
// TODO: investigate the purpose of this 0x80.
|
||||
reg = cpu_to_le32((0x80 << 24) |
|
||||
(ff->tx_resources.channel << 5) |
|
||||
(ff->tx_stream.data_block_quadlets));
|
||||
|
@ -514,7 +521,7 @@ static int ff400_begin_session(struct snd_ff *ff, unsigned int rate)
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Allow to transmit packets. */
|
||||
// Allow to transmit packets.
|
||||
reg = cpu_to_le32(0x00000001);
|
||||
return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
FF400_ISOC_COMM_START, ®, sizeof(reg), 0);
|
||||
|
@ -591,6 +598,7 @@ const struct snd_ff_protocol snd_ff_protocol_ff400 = {
|
|||
.fill_midi_msg = former_fill_midi_msg,
|
||||
.get_clock = former_get_clock,
|
||||
.switch_fetching_mode = former_switch_fetching_mode,
|
||||
.allocate_resources = ff400_allocate_resources,
|
||||
.begin_session = ff400_begin_session,
|
||||
.finish_session = ff400_finish_session,
|
||||
.dump_status = former_dump_status,
|
||||
|
|
|
@ -97,75 +97,31 @@ static int latter_switch_fetching_mode(struct snd_ff *ff, bool enable)
|
|||
LATTER_FETCH_MODE, ®, sizeof(reg), 0);
|
||||
}
|
||||
|
||||
static int keep_resources(struct snd_ff *ff, unsigned int rate)
|
||||
static int latter_allocate_resources(struct snd_ff *ff, unsigned int rate)
|
||||
{
|
||||
enum snd_ff_stream_mode mode;
|
||||
int i;
|
||||
int err;
|
||||
|
||||
// Check whether the given value is supported or not.
|
||||
for (i = 0; i < CIP_SFC_COUNT; i++) {
|
||||
if (amdtp_rate_table[i] == rate)
|
||||
break;
|
||||
}
|
||||
if (i >= CIP_SFC_COUNT)
|
||||
return -EINVAL;
|
||||
|
||||
err = snd_ff_stream_get_multiplier_mode(i, &mode);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Keep resources for in-stream. */
|
||||
ff->tx_resources.channels_mask = 0x00000000000000ffuLL;
|
||||
err = fw_iso_resources_allocate(&ff->tx_resources,
|
||||
amdtp_stream_get_max_payload(&ff->tx_stream),
|
||||
fw_parent_device(ff->unit)->max_speed);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Keep resources for out-stream. */
|
||||
ff->rx_resources.channels_mask = 0x00000000000000ffuLL;
|
||||
err = fw_iso_resources_allocate(&ff->rx_resources,
|
||||
amdtp_stream_get_max_payload(&ff->rx_stream),
|
||||
fw_parent_device(ff->unit)->max_speed);
|
||||
if (err < 0)
|
||||
fw_iso_resources_free(&ff->tx_resources);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int latter_begin_session(struct snd_ff *ff, unsigned int rate)
|
||||
{
|
||||
static const struct {
|
||||
unsigned int stf;
|
||||
unsigned int code;
|
||||
unsigned int flag;
|
||||
} *entry, rate_table[] = {
|
||||
{ 32000, 0x00, 0x92, },
|
||||
{ 44100, 0x02, 0x92, },
|
||||
{ 48000, 0x04, 0x92, },
|
||||
{ 64000, 0x08, 0x8e, },
|
||||
{ 88200, 0x0a, 0x8e, },
|
||||
{ 96000, 0x0c, 0x8e, },
|
||||
{ 128000, 0x10, 0x8c, },
|
||||
{ 176400, 0x12, 0x8c, },
|
||||
{ 192000, 0x14, 0x8c, },
|
||||
};
|
||||
u32 data;
|
||||
unsigned int code;
|
||||
__le32 reg;
|
||||
unsigned int count;
|
||||
int i;
|
||||
int err;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(rate_table); ++i) {
|
||||
entry = rate_table + i;
|
||||
if (entry->stf == rate)
|
||||
break;
|
||||
}
|
||||
if (i == ARRAY_SIZE(rate_table))
|
||||
// Set the number of data blocks transferred in a second.
|
||||
if (rate % 32000 == 0)
|
||||
code = 0x00;
|
||||
else if (rate % 44100 == 0)
|
||||
code = 0x02;
|
||||
else if (rate % 48000 == 0)
|
||||
code = 0x04;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
reg = cpu_to_le32(entry->code);
|
||||
if (rate >= 64000 && rate < 128000)
|
||||
code |= 0x08;
|
||||
else if (rate >= 128000 && rate < 192000)
|
||||
code |= 0x10;
|
||||
|
||||
reg = cpu_to_le32(code);
|
||||
err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
LATTER_STF, ®, sizeof(reg), 0);
|
||||
if (err < 0)
|
||||
|
@ -187,10 +143,63 @@ static int latter_begin_session(struct snd_ff *ff, unsigned int rate)
|
|||
if (count == 10)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
err = keep_resources(ff, rate);
|
||||
for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); ++i) {
|
||||
if (rate == amdtp_rate_table[i])
|
||||
break;
|
||||
}
|
||||
if (i == ARRAY_SIZE(amdtp_rate_table))
|
||||
return -EINVAL;
|
||||
|
||||
err = snd_ff_stream_get_multiplier_mode(i, &mode);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
// Keep resources for in-stream.
|
||||
ff->tx_resources.channels_mask = 0x00000000000000ffuLL;
|
||||
err = fw_iso_resources_allocate(&ff->tx_resources,
|
||||
amdtp_stream_get_max_payload(&ff->tx_stream),
|
||||
fw_parent_device(ff->unit)->max_speed);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
// Keep resources for out-stream.
|
||||
ff->rx_resources.channels_mask = 0x00000000000000ffuLL;
|
||||
err = fw_iso_resources_allocate(&ff->rx_resources,
|
||||
amdtp_stream_get_max_payload(&ff->rx_stream),
|
||||
fw_parent_device(ff->unit)->max_speed);
|
||||
if (err < 0)
|
||||
fw_iso_resources_free(&ff->tx_resources);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int latter_begin_session(struct snd_ff *ff, unsigned int rate)
|
||||
{
|
||||
unsigned int generation = ff->rx_resources.generation;
|
||||
unsigned int flag;
|
||||
u32 data;
|
||||
__le32 reg;
|
||||
int err;
|
||||
|
||||
if (rate >= 32000 && rate <= 48000)
|
||||
flag = 0x92;
|
||||
else if (rate >= 64000 && rate <= 96000)
|
||||
flag = 0x8e;
|
||||
else if (rate >= 128000 && rate <= 192000)
|
||||
flag = 0x8c;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
if (generation != fw_parent_device(ff->unit)->card->generation) {
|
||||
err = fw_iso_resources_update(&ff->tx_resources);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = fw_iso_resources_update(&ff->rx_resources);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
data = (ff->tx_resources.channel << 8) | ff->rx_resources.channel;
|
||||
reg = cpu_to_le32(data);
|
||||
err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
|
@ -200,7 +209,7 @@ static int latter_begin_session(struct snd_ff *ff, unsigned int rate)
|
|||
|
||||
// Always use the maximum number of data channels in data block of
|
||||
// packet.
|
||||
reg = cpu_to_le32(entry->flag);
|
||||
reg = cpu_to_le32(flag);
|
||||
return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
LATTER_ISOC_START, ®, sizeof(reg), 0);
|
||||
}
|
||||
|
@ -424,6 +433,7 @@ const struct snd_ff_protocol snd_ff_protocol_latter = {
|
|||
.fill_midi_msg = latter_fill_midi_msg,
|
||||
.get_clock = latter_get_clock,
|
||||
.switch_fetching_mode = latter_switch_fetching_mode,
|
||||
.allocate_resources = latter_allocate_resources,
|
||||
.begin_session = latter_begin_session,
|
||||
.finish_session = latter_finish_session,
|
||||
.dump_status = latter_dump_status,
|
||||
|
|
|
@ -30,14 +30,11 @@ int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void release_resources(struct snd_ff *ff)
|
||||
{
|
||||
fw_iso_resources_free(&ff->tx_resources);
|
||||
fw_iso_resources_free(&ff->rx_resources);
|
||||
}
|
||||
|
||||
static inline void finish_session(struct snd_ff *ff)
|
||||
{
|
||||
amdtp_stream_stop(&ff->tx_stream);
|
||||
amdtp_stream_stop(&ff->rx_stream);
|
||||
|
||||
ff->spec->protocol->finish_session(ff);
|
||||
ff->spec->protocol->switch_fetching_mode(ff, false);
|
||||
}
|
||||
|
@ -103,37 +100,25 @@ void snd_ff_stream_destroy_duplex(struct snd_ff *ff)
|
|||
destroy_stream(ff, AMDTP_OUT_STREAM);
|
||||
}
|
||||
|
||||
int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
|
||||
int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate)
|
||||
{
|
||||
unsigned int curr_rate;
|
||||
enum snd_ff_clock_src src;
|
||||
int err;
|
||||
|
||||
if (ff->substreams_counter == 0)
|
||||
return 0;
|
||||
|
||||
err = ff->spec->protocol->get_clock(ff, &curr_rate, &src);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (curr_rate != rate ||
|
||||
amdtp_streaming_error(&ff->tx_stream) ||
|
||||
amdtp_streaming_error(&ff->rx_stream)) {
|
||||
finish_session(ff);
|
||||
|
||||
amdtp_stream_stop(&ff->tx_stream);
|
||||
amdtp_stream_stop(&ff->rx_stream);
|
||||
|
||||
release_resources(ff);
|
||||
}
|
||||
|
||||
/*
|
||||
* Regardless of current source of clock signal, drivers transfer some
|
||||
* packets. Then, the device transfers packets.
|
||||
*/
|
||||
if (!amdtp_stream_running(&ff->rx_stream)) {
|
||||
if (ff->substreams_counter == 0 || curr_rate != rate) {
|
||||
enum snd_ff_stream_mode mode;
|
||||
int i;
|
||||
|
||||
finish_session(ff);
|
||||
|
||||
fw_iso_resources_free(&ff->tx_resources);
|
||||
fw_iso_resources_free(&ff->rx_resources);
|
||||
|
||||
for (i = 0; i < CIP_SFC_COUNT; ++i) {
|
||||
if (amdtp_rate_table[i] == rate)
|
||||
break;
|
||||
|
@ -155,6 +140,30 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = ff->spec->protocol->allocate_resources(ff, rate);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (ff->substreams_counter == 0)
|
||||
return 0;
|
||||
|
||||
if (amdtp_streaming_error(&ff->tx_stream) ||
|
||||
amdtp_streaming_error(&ff->rx_stream))
|
||||
finish_session(ff);
|
||||
|
||||
/*
|
||||
* Regardless of current source of clock signal, drivers transfer some
|
||||
* packets. Then, the device transfers packets.
|
||||
*/
|
||||
if (!amdtp_stream_running(&ff->rx_stream)) {
|
||||
err = ff->spec->protocol->begin_session(ff, rate);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
@ -192,37 +201,29 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
|
|||
|
||||
return 0;
|
||||
error:
|
||||
amdtp_stream_stop(&ff->tx_stream);
|
||||
amdtp_stream_stop(&ff->rx_stream);
|
||||
|
||||
finish_session(ff);
|
||||
release_resources(ff);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void snd_ff_stream_stop_duplex(struct snd_ff *ff)
|
||||
{
|
||||
if (ff->substreams_counter > 0)
|
||||
return;
|
||||
if (ff->substreams_counter == 0) {
|
||||
finish_session(ff);
|
||||
|
||||
amdtp_stream_stop(&ff->tx_stream);
|
||||
amdtp_stream_stop(&ff->rx_stream);
|
||||
finish_session(ff);
|
||||
release_resources(ff);
|
||||
fw_iso_resources_free(&ff->tx_resources);
|
||||
fw_iso_resources_free(&ff->rx_resources);
|
||||
}
|
||||
}
|
||||
|
||||
void snd_ff_stream_update_duplex(struct snd_ff *ff)
|
||||
{
|
||||
/* The device discontinue to transfer packets. */
|
||||
// The device discontinue to transfer packets.
|
||||
amdtp_stream_pcm_abort(&ff->tx_stream);
|
||||
amdtp_stream_stop(&ff->tx_stream);
|
||||
|
||||
amdtp_stream_pcm_abort(&ff->rx_stream);
|
||||
amdtp_stream_stop(&ff->rx_stream);
|
||||
|
||||
fw_iso_resources_update(&ff->tx_resources);
|
||||
fw_iso_resources_update(&ff->rx_resources);
|
||||
}
|
||||
|
||||
void snd_ff_stream_lock_changed(struct snd_ff *ff)
|
||||
|
|
|
@ -112,6 +112,7 @@ struct snd_ff_protocol {
|
|||
int (*get_clock)(struct snd_ff *ff, unsigned int *rate,
|
||||
enum snd_ff_clock_src *src);
|
||||
int (*switch_fetching_mode)(struct snd_ff *ff, bool enable);
|
||||
int (*allocate_resources)(struct snd_ff *ff, unsigned int rate);
|
||||
int (*begin_session)(struct snd_ff *ff, unsigned int rate);
|
||||
void (*finish_session)(struct snd_ff *ff);
|
||||
void (*dump_status)(struct snd_ff *ff, struct snd_info_buffer *buffer);
|
||||
|
@ -136,6 +137,7 @@ int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc,
|
|||
enum snd_ff_stream_mode *mode);
|
||||
int snd_ff_stream_init_duplex(struct snd_ff *ff);
|
||||
void snd_ff_stream_destroy_duplex(struct snd_ff *ff);
|
||||
int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate);
|
||||
int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate);
|
||||
void snd_ff_stream_stop_duplex(struct snd_ff *ff);
|
||||
void snd_ff_stream_update_duplex(struct snd_ff *ff);
|
||||
|
|
|
@ -88,8 +88,7 @@ struct snd_efw {
|
|||
struct amdtp_stream rx_stream;
|
||||
struct cmp_connection out_conn;
|
||||
struct cmp_connection in_conn;
|
||||
unsigned int capture_substreams;
|
||||
unsigned int playback_substreams;
|
||||
unsigned int substreams_counter;
|
||||
|
||||
/* hardware metering parameters */
|
||||
unsigned int phys_out;
|
||||
|
@ -206,7 +205,8 @@ int snd_efw_command_get_sampling_rate(struct snd_efw *efw, unsigned int *rate);
|
|||
int snd_efw_command_set_sampling_rate(struct snd_efw *efw, unsigned int rate);
|
||||
|
||||
int snd_efw_stream_init_duplex(struct snd_efw *efw);
|
||||
int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate);
|
||||
int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate);
|
||||
int snd_efw_stream_start_duplex(struct snd_efw *efw);
|
||||
void snd_efw_stream_stop_duplex(struct snd_efw *efw);
|
||||
void snd_efw_stream_update_duplex(struct snd_efw *efw);
|
||||
void snd_efw_stream_destroy_duplex(struct snd_efw *efw);
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
#include "fireworks.h"
|
||||
|
||||
static int midi_capture_open(struct snd_rawmidi_substream *substream)
|
||||
static int midi_open(struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
struct snd_efw *efw = substream->rmidi->private_data;
|
||||
int err;
|
||||
|
@ -17,28 +17,13 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream)
|
|||
goto end;
|
||||
|
||||
mutex_lock(&efw->mutex);
|
||||
efw->capture_substreams++;
|
||||
err = snd_efw_stream_start_duplex(efw, 0);
|
||||
mutex_unlock(&efw->mutex);
|
||||
if (err < 0)
|
||||
snd_efw_stream_lock_release(efw);
|
||||
|
||||
end:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int midi_playback_open(struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
struct snd_efw *efw = substream->rmidi->private_data;
|
||||
int err;
|
||||
|
||||
err = snd_efw_stream_lock_try(efw);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
mutex_lock(&efw->mutex);
|
||||
efw->playback_substreams++;
|
||||
err = snd_efw_stream_start_duplex(efw, 0);
|
||||
err = snd_efw_stream_reserve_duplex(efw, 0);
|
||||
if (err >= 0) {
|
||||
++efw->substreams_counter;
|
||||
err = snd_efw_stream_start_duplex(efw);
|
||||
if (err < 0)
|
||||
--efw->substreams_counter;
|
||||
}
|
||||
mutex_unlock(&efw->mutex);
|
||||
if (err < 0)
|
||||
snd_efw_stream_lock_release(efw);
|
||||
|
@ -46,25 +31,12 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream)
|
|||
return err;
|
||||
}
|
||||
|
||||
static int midi_capture_close(struct snd_rawmidi_substream *substream)
|
||||
static int midi_close(struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
struct snd_efw *efw = substream->rmidi->private_data;
|
||||
|
||||
mutex_lock(&efw->mutex);
|
||||
efw->capture_substreams--;
|
||||
snd_efw_stream_stop_duplex(efw);
|
||||
mutex_unlock(&efw->mutex);
|
||||
|
||||
snd_efw_stream_lock_release(efw);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int midi_playback_close(struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
struct snd_efw *efw = substream->rmidi->private_data;
|
||||
|
||||
mutex_lock(&efw->mutex);
|
||||
efw->playback_substreams--;
|
||||
--efw->substreams_counter;
|
||||
snd_efw_stream_stop_duplex(efw);
|
||||
mutex_unlock(&efw->mutex);
|
||||
|
||||
|
@ -120,13 +92,13 @@ static void set_midi_substream_names(struct snd_efw *efw,
|
|||
int snd_efw_create_midi_devices(struct snd_efw *efw)
|
||||
{
|
||||
static const struct snd_rawmidi_ops capture_ops = {
|
||||
.open = midi_capture_open,
|
||||
.close = midi_capture_close,
|
||||
.open = midi_open,
|
||||
.close = midi_close,
|
||||
.trigger = midi_capture_trigger,
|
||||
};
|
||||
static const struct snd_rawmidi_ops playback_ops = {
|
||||
.open = midi_playback_open,
|
||||
.close = midi_playback_close,
|
||||
.open = midi_open,
|
||||
.close = midi_close,
|
||||
.trigger = midi_playback_trigger,
|
||||
};
|
||||
struct snd_rawmidi *rmidi;
|
||||
|
|
|
@ -218,7 +218,7 @@ static int pcm_close(struct snd_pcm_substream *substream)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
|
||||
static int pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct snd_efw *efw = substream->private_data;
|
||||
|
@ -230,58 +230,30 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
|
|||
return err;
|
||||
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
|
||||
unsigned int rate = params_rate(hw_params);
|
||||
|
||||
mutex_lock(&efw->mutex);
|
||||
efw->capture_substreams++;
|
||||
err = snd_efw_stream_reserve_duplex(efw, rate);
|
||||
if (err >= 0)
|
||||
++efw->substreams_counter;
|
||||
mutex_unlock(&efw->mutex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct snd_efw *efw = substream->private_data;
|
||||
int err;
|
||||
|
||||
err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
|
||||
params_buffer_bytes(hw_params));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
|
||||
mutex_lock(&efw->mutex);
|
||||
efw->playback_substreams++;
|
||||
mutex_unlock(&efw->mutex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
|
||||
static int pcm_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_efw *efw = substream->private_data;
|
||||
|
||||
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) {
|
||||
mutex_lock(&efw->mutex);
|
||||
efw->capture_substreams--;
|
||||
mutex_unlock(&efw->mutex);
|
||||
}
|
||||
mutex_lock(&efw->mutex);
|
||||
|
||||
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
|
||||
--efw->substreams_counter;
|
||||
|
||||
snd_efw_stream_stop_duplex(efw);
|
||||
|
||||
return snd_pcm_lib_free_vmalloc_buffer(substream);
|
||||
}
|
||||
static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_efw *efw = substream->private_data;
|
||||
|
||||
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) {
|
||||
mutex_lock(&efw->mutex);
|
||||
efw->playback_substreams--;
|
||||
mutex_unlock(&efw->mutex);
|
||||
}
|
||||
|
||||
snd_efw_stream_stop_duplex(efw);
|
||||
mutex_unlock(&efw->mutex);
|
||||
|
||||
return snd_pcm_lib_free_vmalloc_buffer(substream);
|
||||
}
|
||||
|
@ -289,10 +261,9 @@ static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
|
|||
static int pcm_capture_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_efw *efw = substream->private_data;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
int err;
|
||||
|
||||
err = snd_efw_stream_start_duplex(efw, runtime->rate);
|
||||
err = snd_efw_stream_start_duplex(efw);
|
||||
if (err >= 0)
|
||||
amdtp_stream_pcm_prepare(&efw->tx_stream);
|
||||
|
||||
|
@ -301,10 +272,9 @@ static int pcm_capture_prepare(struct snd_pcm_substream *substream)
|
|||
static int pcm_playback_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_efw *efw = substream->private_data;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
int err;
|
||||
|
||||
err = snd_efw_stream_start_duplex(efw, runtime->rate);
|
||||
err = snd_efw_stream_start_duplex(efw);
|
||||
if (err >= 0)
|
||||
amdtp_stream_pcm_prepare(&efw->rx_stream);
|
||||
|
||||
|
@ -377,8 +347,8 @@ int snd_efw_create_pcm_devices(struct snd_efw *efw)
|
|||
.open = pcm_open,
|
||||
.close = pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = pcm_capture_hw_params,
|
||||
.hw_free = pcm_capture_hw_free,
|
||||
.hw_params = pcm_hw_params,
|
||||
.hw_free = pcm_hw_free,
|
||||
.prepare = pcm_capture_prepare,
|
||||
.trigger = pcm_capture_trigger,
|
||||
.pointer = pcm_capture_pointer,
|
||||
|
@ -389,8 +359,8 @@ int snd_efw_create_pcm_devices(struct snd_efw *efw)
|
|||
.open = pcm_open,
|
||||
.close = pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = pcm_playback_hw_params,
|
||||
.hw_free = pcm_playback_hw_free,
|
||||
.hw_params = pcm_hw_params,
|
||||
.hw_free = pcm_hw_free,
|
||||
.prepare = pcm_playback_prepare,
|
||||
.trigger = pcm_playback_trigger,
|
||||
.pointer = pcm_playback_pointer,
|
||||
|
|
|
@ -42,7 +42,6 @@ init_stream(struct snd_efw *efw, struct amdtp_stream *stream)
|
|||
static void
|
||||
stop_stream(struct snd_efw *efw, struct amdtp_stream *stream)
|
||||
{
|
||||
amdtp_stream_pcm_abort(stream);
|
||||
amdtp_stream_stop(stream);
|
||||
|
||||
if (stream == &efw->tx_stream)
|
||||
|
@ -51,54 +50,37 @@ stop_stream(struct snd_efw *efw, struct amdtp_stream *stream)
|
|||
cmp_connection_break(&efw->in_conn);
|
||||
}
|
||||
|
||||
static int
|
||||
start_stream(struct snd_efw *efw, struct amdtp_stream *stream,
|
||||
unsigned int sampling_rate)
|
||||
static int start_stream(struct snd_efw *efw, struct amdtp_stream *stream,
|
||||
unsigned int rate)
|
||||
{
|
||||
struct cmp_connection *conn;
|
||||
unsigned int mode, pcm_channels, midi_ports;
|
||||
int err;
|
||||
|
||||
err = snd_efw_get_multiplier_mode(sampling_rate, &mode);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
if (stream == &efw->tx_stream) {
|
||||
if (stream == &efw->tx_stream)
|
||||
conn = &efw->out_conn;
|
||||
pcm_channels = efw->pcm_capture_channels[mode];
|
||||
midi_ports = efw->midi_out_ports;
|
||||
} else {
|
||||
else
|
||||
conn = &efw->in_conn;
|
||||
pcm_channels = efw->pcm_playback_channels[mode];
|
||||
midi_ports = efw->midi_in_ports;
|
||||
}
|
||||
|
||||
err = amdtp_am824_set_parameters(stream, sampling_rate,
|
||||
pcm_channels, midi_ports, false);
|
||||
// Establish connection via CMP.
|
||||
err = cmp_connection_establish(conn);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
return err;
|
||||
|
||||
/* establish connection via CMP */
|
||||
err = cmp_connection_establish(conn,
|
||||
amdtp_stream_get_max_payload(stream));
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
/* start amdtp stream */
|
||||
err = amdtp_stream_start(stream,
|
||||
conn->resources.channel,
|
||||
conn->speed);
|
||||
// Start amdtp stream.
|
||||
err = amdtp_stream_start(stream, conn->resources.channel, conn->speed);
|
||||
if (err < 0) {
|
||||
stop_stream(efw, stream);
|
||||
goto end;
|
||||
cmp_connection_break(conn);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* wait first callback */
|
||||
// Wait first callback.
|
||||
if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) {
|
||||
stop_stream(efw, stream);
|
||||
err = -ETIMEDOUT;
|
||||
amdtp_stream_stop(stream);
|
||||
cmp_connection_break(conn);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
end:
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -164,13 +146,13 @@ int snd_efw_stream_init_duplex(struct snd_efw *efw)
|
|||
(efw->firmware_version == 0x5070000 ||
|
||||
efw->firmware_version == 0x5070300 ||
|
||||
efw->firmware_version == 0x5080000))
|
||||
efw->tx_stream.tx_first_dbc = 0x02;
|
||||
efw->tx_stream.ctx_data.tx.first_dbc = 0x02;
|
||||
/* AudioFire9 always reports wrong dbs. */
|
||||
if (efw->is_af9)
|
||||
efw->tx_stream.flags |= CIP_WRONG_DBS;
|
||||
/* Firmware version 5.5 reports fixed interval for dbc. */
|
||||
if (efw->firmware_version == 0x5050000)
|
||||
efw->tx_stream.tx_dbc_interval = 8;
|
||||
efw->tx_stream.ctx_data.tx.dbc_interval = 8;
|
||||
|
||||
err = init_stream(efw, &efw->rx_stream);
|
||||
if (err < 0) {
|
||||
|
@ -188,75 +170,135 @@ int snd_efw_stream_init_duplex(struct snd_efw *efw)
|
|||
return err;
|
||||
}
|
||||
|
||||
int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate)
|
||||
static int keep_resources(struct snd_efw *efw, struct amdtp_stream *stream,
|
||||
unsigned int rate, unsigned int mode)
|
||||
{
|
||||
unsigned int pcm_channels;
|
||||
unsigned int midi_ports;
|
||||
struct cmp_connection *conn;
|
||||
int err;
|
||||
|
||||
if (stream == &efw->tx_stream) {
|
||||
pcm_channels = efw->pcm_capture_channels[mode];
|
||||
midi_ports = efw->midi_out_ports;
|
||||
conn = &efw->out_conn;
|
||||
} else {
|
||||
pcm_channels = efw->pcm_playback_channels[mode];
|
||||
midi_ports = efw->midi_in_ports;
|
||||
conn = &efw->in_conn;
|
||||
}
|
||||
|
||||
err = amdtp_am824_set_parameters(stream, rate, pcm_channels,
|
||||
midi_ports, false);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return cmp_connection_reserve(conn, amdtp_stream_get_max_payload(stream));
|
||||
}
|
||||
|
||||
int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate)
|
||||
{
|
||||
unsigned int curr_rate;
|
||||
int err = 0;
|
||||
int err;
|
||||
|
||||
/* Need no substreams */
|
||||
if (efw->playback_substreams == 0 && efw->capture_substreams == 0)
|
||||
goto end;
|
||||
|
||||
/*
|
||||
* Considering JACK/FFADO streaming:
|
||||
* TODO: This can be removed hwdep functionality becomes popular.
|
||||
*/
|
||||
// Considering JACK/FFADO streaming:
|
||||
// TODO: This can be removed hwdep functionality becomes popular.
|
||||
err = check_connection_used_by_others(efw, &efw->rx_stream);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
return err;
|
||||
|
||||
/* packet queueing error */
|
||||
if (amdtp_streaming_error(&efw->tx_stream))
|
||||
stop_stream(efw, &efw->tx_stream);
|
||||
if (amdtp_streaming_error(&efw->rx_stream))
|
||||
stop_stream(efw, &efw->rx_stream);
|
||||
|
||||
/* stop streams if rate is different */
|
||||
// stop streams if rate is different.
|
||||
err = snd_efw_command_get_sampling_rate(efw, &curr_rate);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
return err;
|
||||
if (rate == 0)
|
||||
rate = curr_rate;
|
||||
if (rate != curr_rate) {
|
||||
stop_stream(efw, &efw->tx_stream);
|
||||
stop_stream(efw, &efw->rx_stream);
|
||||
|
||||
cmp_connection_release(&efw->out_conn);
|
||||
cmp_connection_release(&efw->in_conn);
|
||||
}
|
||||
|
||||
if (efw->substreams_counter == 0 || rate != curr_rate) {
|
||||
unsigned int mode;
|
||||
|
||||
err = snd_efw_command_set_sampling_rate(efw, rate);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = snd_efw_get_multiplier_mode(rate, &mode);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = keep_resources(efw, &efw->tx_stream, rate, mode);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = keep_resources(efw, &efw->rx_stream, rate, mode);
|
||||
if (err < 0) {
|
||||
cmp_connection_release(&efw->in_conn);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_efw_stream_start_duplex(struct snd_efw *efw)
|
||||
{
|
||||
unsigned int rate;
|
||||
int err = 0;
|
||||
|
||||
// Need no substreams.
|
||||
if (efw->substreams_counter == 0)
|
||||
return -EIO;
|
||||
|
||||
err = snd_efw_command_get_sampling_rate(efw, &rate);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (amdtp_streaming_error(&efw->rx_stream) ||
|
||||
amdtp_streaming_error(&efw->tx_stream)) {
|
||||
stop_stream(efw, &efw->rx_stream);
|
||||
stop_stream(efw, &efw->tx_stream);
|
||||
}
|
||||
|
||||
/* master should be always running */
|
||||
if (!amdtp_stream_running(&efw->rx_stream)) {
|
||||
err = snd_efw_command_set_sampling_rate(efw, rate);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
err = start_stream(efw, &efw->rx_stream, rate);
|
||||
if (err < 0) {
|
||||
dev_err(&efw->unit->device,
|
||||
"fail to start AMDTP master stream:%d\n", err);
|
||||
goto end;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
/* start slave if needed */
|
||||
if (efw->capture_substreams > 0 &&
|
||||
!amdtp_stream_running(&efw->tx_stream)) {
|
||||
if (!amdtp_stream_running(&efw->tx_stream)) {
|
||||
err = start_stream(efw, &efw->tx_stream, rate);
|
||||
if (err < 0) {
|
||||
dev_err(&efw->unit->device,
|
||||
"fail to start AMDTP slave stream:%d\n", err);
|
||||
stop_stream(efw, &efw->rx_stream);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
end:
|
||||
|
||||
return 0;
|
||||
error:
|
||||
stop_stream(efw, &efw->rx_stream);
|
||||
stop_stream(efw, &efw->tx_stream);
|
||||
return err;
|
||||
}
|
||||
|
||||
void snd_efw_stream_stop_duplex(struct snd_efw *efw)
|
||||
{
|
||||
if (efw->capture_substreams == 0) {
|
||||
if (efw->substreams_counter == 0) {
|
||||
stop_stream(efw, &efw->tx_stream);
|
||||
stop_stream(efw, &efw->rx_stream);
|
||||
|
||||
if (efw->playback_substreams == 0)
|
||||
stop_stream(efw, &efw->rx_stream);
|
||||
cmp_connection_release(&efw->out_conn);
|
||||
cmp_connection_release(&efw->in_conn);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ static void copy_sph(u32 *frame, __be32 *buffer, unsigned int data_blocks,
|
|||
static void copy_message(u64 *frames, __be32 *buffer, unsigned int data_blocks,
|
||||
unsigned int data_block_quadlets);
|
||||
|
||||
TRACE_EVENT(in_data_block_sph,
|
||||
TRACE_EVENT(data_block_sph,
|
||||
TP_PROTO(struct amdtp_stream *s, unsigned int data_blocks, __be32 *buffer),
|
||||
TP_ARGS(s, data_blocks, buffer),
|
||||
TP_STRUCT__entry(
|
||||
|
@ -28,8 +28,13 @@ TRACE_EVENT(in_data_block_sph,
|
|||
__dynamic_array(u32, tstamps, data_blocks)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->src = fw_parent_device(s->unit)->node_id;
|
||||
__entry->dst = fw_parent_device(s->unit)->card->node_id;
|
||||
if (s->direction == AMDTP_IN_STREAM) {
|
||||
__entry->src = fw_parent_device(s->unit)->node_id;
|
||||
__entry->dst = fw_parent_device(s->unit)->card->node_id;
|
||||
} else {
|
||||
__entry->src = fw_parent_device(s->unit)->card->node_id;
|
||||
__entry->dst = fw_parent_device(s->unit)->node_id;
|
||||
}
|
||||
__entry->data_blocks = data_blocks;
|
||||
copy_sph(__get_dynamic_array(tstamps), buffer, data_blocks, s->data_block_quadlets);
|
||||
),
|
||||
|
@ -42,31 +47,7 @@ TRACE_EVENT(in_data_block_sph,
|
|||
)
|
||||
);
|
||||
|
||||
TRACE_EVENT(out_data_block_sph,
|
||||
TP_PROTO(struct amdtp_stream *s, unsigned int data_blocks, __be32 *buffer),
|
||||
TP_ARGS(s, data_blocks, buffer),
|
||||
TP_STRUCT__entry(
|
||||
__field(int, src)
|
||||
__field(int, dst)
|
||||
__field(unsigned int, data_blocks)
|
||||
__dynamic_array(u32, tstamps, data_blocks)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->src = fw_parent_device(s->unit)->card->node_id;
|
||||
__entry->dst = fw_parent_device(s->unit)->node_id;
|
||||
__entry->data_blocks = data_blocks;
|
||||
copy_sph(__get_dynamic_array(tstamps), buffer, data_blocks, s->data_block_quadlets);
|
||||
),
|
||||
TP_printk(
|
||||
"%04x %04x %u %s",
|
||||
__entry->src,
|
||||
__entry->dst,
|
||||
__entry->data_blocks,
|
||||
__print_array(__get_dynamic_array(tstamps), __entry->data_blocks, 4)
|
||||
)
|
||||
);
|
||||
|
||||
TRACE_EVENT(in_data_block_message,
|
||||
TRACE_EVENT(data_block_message,
|
||||
TP_PROTO(struct amdtp_stream *s, unsigned int data_blocks, __be32 *buffer),
|
||||
TP_ARGS(s, data_blocks, buffer),
|
||||
TP_STRUCT__entry(
|
||||
|
@ -76,32 +57,13 @@ TRACE_EVENT(in_data_block_message,
|
|||
__dynamic_array(u64, messages, data_blocks)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->src = fw_parent_device(s->unit)->node_id;
|
||||
__entry->dst = fw_parent_device(s->unit)->card->node_id;
|
||||
__entry->data_blocks = data_blocks;
|
||||
copy_message(__get_dynamic_array(messages), buffer, data_blocks, s->data_block_quadlets);
|
||||
),
|
||||
TP_printk(
|
||||
"%04x %04x %u %s",
|
||||
__entry->src,
|
||||
__entry->dst,
|
||||
__entry->data_blocks,
|
||||
__print_array(__get_dynamic_array(messages), __entry->data_blocks, 8)
|
||||
)
|
||||
);
|
||||
|
||||
TRACE_EVENT(out_data_block_message,
|
||||
TP_PROTO(struct amdtp_stream *s, unsigned int data_blocks, __be32 *buffer),
|
||||
TP_ARGS(s, data_blocks, buffer),
|
||||
TP_STRUCT__entry(
|
||||
__field(int, src)
|
||||
__field(int, dst)
|
||||
__field(unsigned int, data_blocks)
|
||||
__dynamic_array(u64, messages, data_blocks)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->src = fw_parent_device(s->unit)->card->node_id;
|
||||
__entry->dst = fw_parent_device(s->unit)->node_id;
|
||||
if (s->direction == AMDTP_IN_STREAM) {
|
||||
__entry->src = fw_parent_device(s->unit)->node_id;
|
||||
__entry->dst = fw_parent_device(s->unit)->card->node_id;
|
||||
} else {
|
||||
__entry->src = fw_parent_device(s->unit)->card->node_id;
|
||||
__entry->dst = fw_parent_device(s->unit)->node_id;
|
||||
}
|
||||
__entry->data_blocks = data_blocks;
|
||||
copy_message(__get_dynamic_array(messages), buffer, data_blocks, s->data_block_quadlets);
|
||||
),
|
||||
|
|
|
@ -305,8 +305,8 @@ static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
|
|||
struct amdtp_motu *p = s->protocol;
|
||||
struct snd_pcm_substream *pcm;
|
||||
|
||||
trace_in_data_block_sph(s, data_blocks, buffer);
|
||||
trace_in_data_block_message(s, data_blocks, buffer);
|
||||
trace_data_block_sph(s, data_blocks, buffer);
|
||||
trace_data_block_message(s, data_blocks, buffer);
|
||||
|
||||
if (p->midi_ports)
|
||||
read_midi_messages(s, buffer, data_blocks);
|
||||
|
@ -383,8 +383,8 @@ static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
|
|||
|
||||
write_sph(s, buffer, data_blocks);
|
||||
|
||||
trace_out_data_block_sph(s, data_blocks, buffer);
|
||||
trace_out_data_block_message(s, data_blocks, buffer);
|
||||
trace_data_block_sph(s, data_blocks, buffer);
|
||||
trace_data_block_message(s, data_blocks, buffer);
|
||||
|
||||
return data_blocks;
|
||||
}
|
||||
|
@ -428,7 +428,7 @@ int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
|
|||
return err;
|
||||
|
||||
s->sph = 1;
|
||||
s->fdf = MOTU_FDF_AM824;
|
||||
s->ctx_data.rx.fdf = MOTU_FDF_AM824;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
#include "motu.h"
|
||||
|
||||
static int midi_capture_open(struct snd_rawmidi_substream *substream)
|
||||
static int midi_open(struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
struct snd_motu *motu = substream->rmidi->private_data;
|
||||
int err;
|
||||
|
@ -17,8 +17,13 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream)
|
|||
|
||||
mutex_lock(&motu->mutex);
|
||||
|
||||
motu->capture_substreams++;
|
||||
err = snd_motu_stream_start_duplex(motu, 0);
|
||||
err = snd_motu_stream_reserve_duplex(motu, 0);
|
||||
if (err >= 0) {
|
||||
++motu->substreams_counter;
|
||||
err = snd_motu_stream_start_duplex(motu);
|
||||
if (err < 0)
|
||||
--motu->substreams_counter;
|
||||
}
|
||||
|
||||
mutex_unlock(&motu->mutex);
|
||||
|
||||
|
@ -28,50 +33,13 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream)
|
|||
return err;
|
||||
}
|
||||
|
||||
static int midi_playback_open(struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
struct snd_motu *motu = substream->rmidi->private_data;
|
||||
int err;
|
||||
|
||||
err = snd_motu_stream_lock_try(motu);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
mutex_lock(&motu->mutex);
|
||||
|
||||
motu->playback_substreams++;
|
||||
err = snd_motu_stream_start_duplex(motu, 0);
|
||||
|
||||
mutex_unlock(&motu->mutex);
|
||||
|
||||
if (err < 0)
|
||||
snd_motu_stream_lock_release(motu);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int midi_capture_close(struct snd_rawmidi_substream *substream)
|
||||
static int midi_close(struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
struct snd_motu *motu = substream->rmidi->private_data;
|
||||
|
||||
mutex_lock(&motu->mutex);
|
||||
|
||||
motu->capture_substreams--;
|
||||
snd_motu_stream_stop_duplex(motu);
|
||||
|
||||
mutex_unlock(&motu->mutex);
|
||||
|
||||
snd_motu_stream_lock_release(motu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int midi_playback_close(struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
struct snd_motu *motu = substream->rmidi->private_data;
|
||||
|
||||
mutex_lock(&motu->mutex);
|
||||
|
||||
motu->playback_substreams--;
|
||||
--motu->substreams_counter;
|
||||
snd_motu_stream_stop_duplex(motu);
|
||||
|
||||
mutex_unlock(&motu->mutex);
|
||||
|
@ -128,13 +96,13 @@ static void set_midi_substream_names(struct snd_motu *motu,
|
|||
int snd_motu_create_midi_devices(struct snd_motu *motu)
|
||||
{
|
||||
static const struct snd_rawmidi_ops capture_ops = {
|
||||
.open = midi_capture_open,
|
||||
.close = midi_capture_close,
|
||||
.open = midi_open,
|
||||
.close = midi_close,
|
||||
.trigger = midi_capture_trigger,
|
||||
};
|
||||
static const struct snd_rawmidi_ops playback_ops = {
|
||||
.open = midi_playback_open,
|
||||
.close = midi_playback_close,
|
||||
.open = midi_open,
|
||||
.close = midi_close,
|
||||
.trigger = midi_playback_trigger,
|
||||
};
|
||||
struct snd_rawmidi *rmidi;
|
||||
|
|
|
@ -189,8 +189,8 @@ static int pcm_close(struct snd_pcm_substream *substream)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int capture_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
static int pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct snd_motu *motu = substream->private_data;
|
||||
int err;
|
||||
|
@ -201,57 +201,26 @@ static int capture_hw_params(struct snd_pcm_substream *substream,
|
|||
return err;
|
||||
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
|
||||
unsigned int rate = params_rate(hw_params);
|
||||
|
||||
mutex_lock(&motu->mutex);
|
||||
motu->capture_substreams++;
|
||||
err = snd_motu_stream_reserve_duplex(motu, rate);
|
||||
if (err >= 0)
|
||||
++motu->substreams_counter;
|
||||
mutex_unlock(&motu->mutex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int playback_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct snd_motu *motu = substream->private_data;
|
||||
int err;
|
||||
|
||||
err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
|
||||
params_buffer_bytes(hw_params));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
|
||||
mutex_lock(&motu->mutex);
|
||||
motu->playback_substreams++;
|
||||
mutex_unlock(&motu->mutex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int capture_hw_free(struct snd_pcm_substream *substream)
|
||||
static int pcm_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_motu *motu = substream->private_data;
|
||||
|
||||
mutex_lock(&motu->mutex);
|
||||
|
||||
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
|
||||
motu->capture_substreams--;
|
||||
|
||||
snd_motu_stream_stop_duplex(motu);
|
||||
|
||||
mutex_unlock(&motu->mutex);
|
||||
|
||||
return snd_pcm_lib_free_vmalloc_buffer(substream);
|
||||
}
|
||||
|
||||
static int playback_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_motu *motu = substream->private_data;
|
||||
|
||||
mutex_lock(&motu->mutex);
|
||||
|
||||
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
|
||||
motu->playback_substreams--;
|
||||
--motu->substreams_counter;
|
||||
|
||||
snd_motu_stream_stop_duplex(motu);
|
||||
|
||||
|
@ -266,7 +235,7 @@ static int capture_prepare(struct snd_pcm_substream *substream)
|
|||
int err;
|
||||
|
||||
mutex_lock(&motu->mutex);
|
||||
err = snd_motu_stream_start_duplex(motu, substream->runtime->rate);
|
||||
err = snd_motu_stream_start_duplex(motu);
|
||||
mutex_unlock(&motu->mutex);
|
||||
if (err >= 0)
|
||||
amdtp_stream_pcm_prepare(&motu->tx_stream);
|
||||
|
@ -279,7 +248,7 @@ static int playback_prepare(struct snd_pcm_substream *substream)
|
|||
int err;
|
||||
|
||||
mutex_lock(&motu->mutex);
|
||||
err = snd_motu_stream_start_duplex(motu, substream->runtime->rate);
|
||||
err = snd_motu_stream_start_duplex(motu);
|
||||
mutex_unlock(&motu->mutex);
|
||||
if (err >= 0)
|
||||
amdtp_stream_pcm_prepare(&motu->rx_stream);
|
||||
|
@ -355,8 +324,8 @@ int snd_motu_create_pcm_devices(struct snd_motu *motu)
|
|||
.open = pcm_open,
|
||||
.close = pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = capture_hw_params,
|
||||
.hw_free = capture_hw_free,
|
||||
.hw_params = pcm_hw_params,
|
||||
.hw_free = pcm_hw_free,
|
||||
.prepare = capture_prepare,
|
||||
.trigger = capture_trigger,
|
||||
.pointer = capture_pointer,
|
||||
|
@ -367,8 +336,8 @@ int snd_motu_create_pcm_devices(struct snd_motu *motu)
|
|||
.open = pcm_open,
|
||||
.close = pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = playback_hw_params,
|
||||
.hw_free = playback_hw_free,
|
||||
.hw_params = pcm_hw_params,
|
||||
.hw_free = pcm_hw_free,
|
||||
.prepare = playback_prepare,
|
||||
.trigger = playback_trigger,
|
||||
.pointer = playback_pointer,
|
||||
|
|
|
@ -25,48 +25,47 @@
|
|||
#define RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS 0x00000040
|
||||
#define TX_PACKET_TRANSMISSION_SPEED_MASK 0x0000000f
|
||||
|
||||
static int start_both_streams(struct snd_motu *motu, unsigned int rate)
|
||||
static int keep_resources(struct snd_motu *motu, unsigned int rate,
|
||||
struct amdtp_stream *stream)
|
||||
{
|
||||
struct fw_iso_resources *resources;
|
||||
struct snd_motu_packet_format *packet_format;
|
||||
unsigned int midi_ports = 0;
|
||||
int err;
|
||||
|
||||
if (stream == &motu->rx_stream) {
|
||||
resources = &motu->rx_resources;
|
||||
packet_format = &motu->rx_packet_formats;
|
||||
|
||||
if ((motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_2ND_Q) ||
|
||||
(motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_3RD_Q))
|
||||
midi_ports = 1;
|
||||
} else {
|
||||
resources = &motu->tx_resources;
|
||||
packet_format = &motu->tx_packet_formats;
|
||||
|
||||
if ((motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_2ND_Q) ||
|
||||
(motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_3RD_Q))
|
||||
midi_ports = 1;
|
||||
}
|
||||
|
||||
err = amdtp_motu_set_parameters(stream, rate, midi_ports,
|
||||
packet_format);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return fw_iso_resources_allocate(resources,
|
||||
amdtp_stream_get_max_payload(stream),
|
||||
fw_parent_device(motu->unit)->max_speed);
|
||||
}
|
||||
|
||||
static int begin_session(struct snd_motu *motu)
|
||||
{
|
||||
__be32 reg;
|
||||
u32 data;
|
||||
int err;
|
||||
|
||||
if ((motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_2ND_Q) ||
|
||||
(motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_3RD_Q))
|
||||
midi_ports = 1;
|
||||
|
||||
/* Set packet formation to our packet streaming engine. */
|
||||
err = amdtp_motu_set_parameters(&motu->rx_stream, rate, midi_ports,
|
||||
&motu->rx_packet_formats);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if ((motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_2ND_Q) ||
|
||||
(motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_3RD_Q))
|
||||
midi_ports = 1;
|
||||
else
|
||||
midi_ports = 0;
|
||||
|
||||
err = amdtp_motu_set_parameters(&motu->tx_stream, rate, midi_ports,
|
||||
&motu->tx_packet_formats);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Get isochronous resources on the bus. */
|
||||
err = fw_iso_resources_allocate(&motu->rx_resources,
|
||||
amdtp_stream_get_max_payload(&motu->rx_stream),
|
||||
fw_parent_device(motu->unit)->max_speed);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = fw_iso_resources_allocate(&motu->tx_resources,
|
||||
amdtp_stream_get_max_payload(&motu->tx_stream),
|
||||
fw_parent_device(motu->unit)->max_speed);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Configure the unit to start isochronous communication. */
|
||||
// Configure the unit to start isochronous communication.
|
||||
err = snd_motu_transaction_read(motu, ISOC_COMM_CONTROL_OFFSET, ®,
|
||||
sizeof(reg));
|
||||
if (err < 0)
|
||||
|
@ -83,7 +82,7 @@ static int start_both_streams(struct snd_motu *motu, unsigned int rate)
|
|||
sizeof(reg));
|
||||
}
|
||||
|
||||
static void stop_both_streams(struct snd_motu *motu)
|
||||
static void finish_session(struct snd_motu *motu)
|
||||
{
|
||||
__be32 reg;
|
||||
u32 data;
|
||||
|
@ -93,6 +92,9 @@ static void stop_both_streams(struct snd_motu *motu)
|
|||
if (err < 0)
|
||||
return;
|
||||
|
||||
amdtp_stream_stop(&motu->tx_stream);
|
||||
amdtp_stream_stop(&motu->rx_stream);
|
||||
|
||||
err = snd_motu_transaction_read(motu, ISOC_COMM_CONTROL_OFFSET, ®,
|
||||
sizeof(reg));
|
||||
if (err < 0)
|
||||
|
@ -105,9 +107,6 @@ static void stop_both_streams(struct snd_motu *motu)
|
|||
reg = cpu_to_be32(data);
|
||||
snd_motu_transaction_write(motu, ISOC_COMM_CONTROL_OFFSET, ®,
|
||||
sizeof(reg));
|
||||
|
||||
fw_iso_resources_free(&motu->tx_resources);
|
||||
fw_iso_resources_free(&motu->rx_resources);
|
||||
}
|
||||
|
||||
static int start_isoc_ctx(struct snd_motu *motu, struct amdtp_stream *stream)
|
||||
|
@ -125,28 +124,12 @@ static int start_isoc_ctx(struct snd_motu *motu, struct amdtp_stream *stream)
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) {
|
||||
amdtp_stream_stop(stream);
|
||||
fw_iso_resources_free(resources);
|
||||
if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT))
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stop_isoc_ctx(struct snd_motu *motu, struct amdtp_stream *stream)
|
||||
{
|
||||
struct fw_iso_resources *resources;
|
||||
|
||||
if (stream == &motu->rx_stream)
|
||||
resources = &motu->rx_resources;
|
||||
else
|
||||
resources = &motu->tx_resources;
|
||||
|
||||
amdtp_stream_stop(stream);
|
||||
fw_iso_resources_free(resources);
|
||||
}
|
||||
|
||||
int snd_motu_stream_cache_packet_formats(struct snd_motu *motu)
|
||||
{
|
||||
int err;
|
||||
|
@ -174,6 +157,48 @@ int snd_motu_stream_cache_packet_formats(struct snd_motu *motu)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate)
|
||||
{
|
||||
unsigned int curr_rate;
|
||||
int err;
|
||||
|
||||
err = motu->spec->protocol->get_clock_rate(motu, &curr_rate);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (rate == 0)
|
||||
rate = curr_rate;
|
||||
|
||||
if (motu->substreams_counter == 0 || curr_rate != rate) {
|
||||
finish_session(motu);
|
||||
|
||||
fw_iso_resources_free(&motu->tx_resources);
|
||||
fw_iso_resources_free(&motu->rx_resources);
|
||||
|
||||
err = motu->spec->protocol->set_clock_rate(motu, rate);
|
||||
if (err < 0) {
|
||||
dev_err(&motu->unit->device,
|
||||
"fail to set sampling rate: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = snd_motu_stream_cache_packet_formats(motu);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = keep_resources(motu, rate, &motu->tx_stream);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = keep_resources(motu, rate, &motu->rx_stream);
|
||||
if (err < 0) {
|
||||
fw_iso_resources_free(&motu->tx_resources);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ensure_packet_formats(struct snd_motu *motu)
|
||||
{
|
||||
__be32 reg;
|
||||
|
@ -200,55 +225,34 @@ static int ensure_packet_formats(struct snd_motu *motu)
|
|||
sizeof(reg));
|
||||
}
|
||||
|
||||
int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate)
|
||||
int snd_motu_stream_start_duplex(struct snd_motu *motu)
|
||||
{
|
||||
const struct snd_motu_protocol *protocol = motu->spec->protocol;
|
||||
unsigned int curr_rate;
|
||||
unsigned int generation = motu->rx_resources.generation;
|
||||
int err = 0;
|
||||
|
||||
if (motu->capture_substreams == 0 && motu->playback_substreams == 0)
|
||||
if (motu->substreams_counter == 0)
|
||||
return 0;
|
||||
|
||||
/* Some packet queueing errors. */
|
||||
if (amdtp_streaming_error(&motu->rx_stream) ||
|
||||
amdtp_streaming_error(&motu->tx_stream)) {
|
||||
amdtp_stream_stop(&motu->rx_stream);
|
||||
amdtp_stream_stop(&motu->tx_stream);
|
||||
stop_both_streams(motu);
|
||||
}
|
||||
amdtp_streaming_error(&motu->tx_stream))
|
||||
finish_session(motu);
|
||||
|
||||
err = snd_motu_stream_cache_packet_formats(motu);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (generation != fw_parent_device(motu->unit)->card->generation) {
|
||||
err = fw_iso_resources_update(&motu->rx_resources);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Stop stream if rate is different. */
|
||||
err = protocol->get_clock_rate(motu, &curr_rate);
|
||||
if (err < 0) {
|
||||
dev_err(&motu->unit->device,
|
||||
"fail to get sampling rate: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
if (rate == 0)
|
||||
rate = curr_rate;
|
||||
if (rate != curr_rate) {
|
||||
amdtp_stream_stop(&motu->rx_stream);
|
||||
amdtp_stream_stop(&motu->tx_stream);
|
||||
stop_both_streams(motu);
|
||||
err = fw_iso_resources_update(&motu->tx_resources);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!amdtp_stream_running(&motu->rx_stream)) {
|
||||
err = protocol->set_clock_rate(motu, rate);
|
||||
if (err < 0) {
|
||||
dev_err(&motu->unit->device,
|
||||
"fail to set sampling rate: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = ensure_packet_formats(motu);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = start_both_streams(motu, rate);
|
||||
err = begin_session(motu);
|
||||
if (err < 0) {
|
||||
dev_err(&motu->unit->device,
|
||||
"fail to start isochronous comm: %d\n", err);
|
||||
|
@ -262,7 +266,7 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate)
|
|||
goto stop_streams;
|
||||
}
|
||||
|
||||
err = protocol->switch_fetching_mode(motu, true);
|
||||
err = motu->spec->protocol->switch_fetching_mode(motu, true);
|
||||
if (err < 0) {
|
||||
dev_err(&motu->unit->device,
|
||||
"fail to enable frame fetching: %d\n", err);
|
||||
|
@ -270,13 +274,11 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate)
|
|||
}
|
||||
}
|
||||
|
||||
if (!amdtp_stream_running(&motu->tx_stream) &&
|
||||
motu->capture_substreams > 0) {
|
||||
if (!amdtp_stream_running(&motu->tx_stream)) {
|
||||
err = start_isoc_ctx(motu, &motu->tx_stream);
|
||||
if (err < 0) {
|
||||
dev_err(&motu->unit->device,
|
||||
"fail to start IR context: %d", err);
|
||||
amdtp_stream_stop(&motu->rx_stream);
|
||||
goto stop_streams;
|
||||
}
|
||||
}
|
||||
|
@ -284,21 +286,17 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate)
|
|||
return 0;
|
||||
|
||||
stop_streams:
|
||||
stop_both_streams(motu);
|
||||
finish_session(motu);
|
||||
return err;
|
||||
}
|
||||
|
||||
void snd_motu_stream_stop_duplex(struct snd_motu *motu)
|
||||
{
|
||||
if (motu->capture_substreams == 0) {
|
||||
if (amdtp_stream_running(&motu->tx_stream))
|
||||
stop_isoc_ctx(motu, &motu->tx_stream);
|
||||
if (motu->substreams_counter == 0) {
|
||||
finish_session(motu);
|
||||
|
||||
if (motu->playback_substreams == 0) {
|
||||
if (amdtp_stream_running(&motu->rx_stream))
|
||||
stop_isoc_ctx(motu, &motu->rx_stream);
|
||||
stop_both_streams(motu);
|
||||
}
|
||||
fw_iso_resources_free(&motu->tx_resources);
|
||||
fw_iso_resources_free(&motu->rx_resources);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -371,8 +369,7 @@ void snd_motu_stream_destroy_duplex(struct snd_motu *motu)
|
|||
destroy_stream(motu, AMDTP_IN_STREAM);
|
||||
destroy_stream(motu, AMDTP_OUT_STREAM);
|
||||
|
||||
motu->playback_substreams = 0;
|
||||
motu->capture_substreams = 0;
|
||||
motu->substreams_counter = 0;
|
||||
}
|
||||
|
||||
static void motu_lock_changed(struct snd_motu *motu)
|
||||
|
|
|
@ -59,8 +59,7 @@ struct snd_motu {
|
|||
struct amdtp_stream rx_stream;
|
||||
struct fw_iso_resources tx_resources;
|
||||
struct fw_iso_resources rx_resources;
|
||||
unsigned int capture_substreams;
|
||||
unsigned int playback_substreams;
|
||||
unsigned int substreams_counter;
|
||||
|
||||
/* For notification. */
|
||||
struct fw_address_handler async_handler;
|
||||
|
@ -153,7 +152,8 @@ void snd_motu_transaction_unregister(struct snd_motu *motu);
|
|||
int snd_motu_stream_init_duplex(struct snd_motu *motu);
|
||||
void snd_motu_stream_destroy_duplex(struct snd_motu *motu);
|
||||
int snd_motu_stream_cache_packet_formats(struct snd_motu *motu);
|
||||
int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate);
|
||||
int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate);
|
||||
int snd_motu_stream_start_duplex(struct snd_motu *motu);
|
||||
void snd_motu_stream_stop_duplex(struct snd_motu *motu);
|
||||
int snd_motu_stream_lock_try(struct snd_motu *motu);
|
||||
void snd_motu_stream_lock_release(struct snd_motu *motu);
|
||||
|
|
|
@ -18,8 +18,13 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream)
|
|||
|
||||
mutex_lock(&oxfw->mutex);
|
||||
|
||||
oxfw->capture_substreams++;
|
||||
err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->tx_stream, 0, 0);
|
||||
err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->tx_stream, 0, 0);
|
||||
if (err >= 0) {
|
||||
++oxfw->substreams_count;
|
||||
err = snd_oxfw_stream_start_duplex(oxfw);
|
||||
if (err < 0)
|
||||
--oxfw->substreams_count;
|
||||
}
|
||||
|
||||
mutex_unlock(&oxfw->mutex);
|
||||
|
||||
|
@ -40,8 +45,11 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream)
|
|||
|
||||
mutex_lock(&oxfw->mutex);
|
||||
|
||||
oxfw->playback_substreams++;
|
||||
err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->rx_stream, 0, 0);
|
||||
err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->rx_stream, 0, 0);
|
||||
if (err >= 0) {
|
||||
++oxfw->substreams_count;
|
||||
err = snd_oxfw_stream_start_duplex(oxfw);
|
||||
}
|
||||
|
||||
mutex_unlock(&oxfw->mutex);
|
||||
|
||||
|
@ -57,8 +65,8 @@ static int midi_capture_close(struct snd_rawmidi_substream *substream)
|
|||
|
||||
mutex_lock(&oxfw->mutex);
|
||||
|
||||
oxfw->capture_substreams--;
|
||||
snd_oxfw_stream_stop_simplex(oxfw, &oxfw->tx_stream);
|
||||
--oxfw->substreams_count;
|
||||
snd_oxfw_stream_stop_duplex(oxfw);
|
||||
|
||||
mutex_unlock(&oxfw->mutex);
|
||||
|
||||
|
@ -72,8 +80,8 @@ static int midi_playback_close(struct snd_rawmidi_substream *substream)
|
|||
|
||||
mutex_lock(&oxfw->mutex);
|
||||
|
||||
oxfw->playback_substreams--;
|
||||
snd_oxfw_stream_stop_simplex(oxfw, &oxfw->rx_stream);
|
||||
--oxfw->substreams_count;
|
||||
snd_oxfw_stream_stop_duplex(oxfw);
|
||||
|
||||
mutex_unlock(&oxfw->mutex);
|
||||
|
||||
|
|
|
@ -219,12 +219,18 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
|
|||
return err;
|
||||
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
|
||||
unsigned int rate = params_rate(hw_params);
|
||||
unsigned int channels = params_channels(hw_params);
|
||||
|
||||
mutex_lock(&oxfw->mutex);
|
||||
oxfw->capture_substreams++;
|
||||
err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->tx_stream,
|
||||
rate, channels);
|
||||
if (err >= 0)
|
||||
++oxfw->substreams_count;
|
||||
mutex_unlock(&oxfw->mutex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
|
@ -238,8 +244,14 @@ static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
|
|||
return err;
|
||||
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
|
||||
unsigned int rate = params_rate(hw_params);
|
||||
unsigned int channels = params_channels(hw_params);
|
||||
|
||||
mutex_lock(&oxfw->mutex);
|
||||
oxfw->playback_substreams++;
|
||||
err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->tx_stream,
|
||||
rate, channels);
|
||||
if (err >= 0)
|
||||
++oxfw->substreams_count;
|
||||
mutex_unlock(&oxfw->mutex);
|
||||
}
|
||||
|
||||
|
@ -253,9 +265,9 @@ static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
|
|||
mutex_lock(&oxfw->mutex);
|
||||
|
||||
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
|
||||
oxfw->capture_substreams--;
|
||||
--oxfw->substreams_count;
|
||||
|
||||
snd_oxfw_stream_stop_simplex(oxfw, &oxfw->tx_stream);
|
||||
snd_oxfw_stream_stop_duplex(oxfw);
|
||||
|
||||
mutex_unlock(&oxfw->mutex);
|
||||
|
||||
|
@ -268,9 +280,9 @@ static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
|
|||
mutex_lock(&oxfw->mutex);
|
||||
|
||||
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
|
||||
oxfw->playback_substreams--;
|
||||
--oxfw->substreams_count;
|
||||
|
||||
snd_oxfw_stream_stop_simplex(oxfw, &oxfw->rx_stream);
|
||||
snd_oxfw_stream_stop_duplex(oxfw);
|
||||
|
||||
mutex_unlock(&oxfw->mutex);
|
||||
|
||||
|
@ -280,12 +292,10 @@ static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
|
|||
static int pcm_capture_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_oxfw *oxfw = substream->private_data;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
int err;
|
||||
|
||||
mutex_lock(&oxfw->mutex);
|
||||
err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->tx_stream,
|
||||
runtime->rate, runtime->channels);
|
||||
err = snd_oxfw_stream_start_duplex(oxfw);
|
||||
mutex_unlock(&oxfw->mutex);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
@ -297,12 +307,10 @@ static int pcm_capture_prepare(struct snd_pcm_substream *substream)
|
|||
static int pcm_playback_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_oxfw *oxfw = substream->private_data;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
int err;
|
||||
|
||||
mutex_lock(&oxfw->mutex);
|
||||
err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->rx_stream,
|
||||
runtime->rate, runtime->channels);
|
||||
err = snd_oxfw_stream_start_duplex(oxfw);
|
||||
mutex_unlock(&oxfw->mutex);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
|
|
@ -100,85 +100,34 @@ static int set_stream_format(struct snd_oxfw *oxfw, struct amdtp_stream *s,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void stop_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
|
||||
static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
|
||||
{
|
||||
amdtp_stream_pcm_abort(stream);
|
||||
amdtp_stream_stop(stream);
|
||||
|
||||
if (stream == &oxfw->tx_stream)
|
||||
cmp_connection_break(&oxfw->out_conn);
|
||||
else
|
||||
cmp_connection_break(&oxfw->in_conn);
|
||||
}
|
||||
|
||||
static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream,
|
||||
unsigned int rate, unsigned int pcm_channels)
|
||||
{
|
||||
u8 **formats;
|
||||
struct cmp_connection *conn;
|
||||
struct snd_oxfw_stream_formation formation;
|
||||
unsigned int i, midi_ports;
|
||||
int err;
|
||||
|
||||
if (stream == &oxfw->rx_stream) {
|
||||
formats = oxfw->rx_stream_formats;
|
||||
if (stream == &oxfw->rx_stream)
|
||||
conn = &oxfw->in_conn;
|
||||
} else {
|
||||
formats = oxfw->tx_stream_formats;
|
||||
else
|
||||
conn = &oxfw->out_conn;
|
||||
}
|
||||
|
||||
/* Get stream format */
|
||||
for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
|
||||
if (formats[i] == NULL)
|
||||
break;
|
||||
|
||||
err = snd_oxfw_stream_parse_format(formats[i], &formation);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
if (rate != formation.rate)
|
||||
continue;
|
||||
if (pcm_channels == 0 || pcm_channels == formation.pcm)
|
||||
break;
|
||||
}
|
||||
if (i == SND_OXFW_STREAM_FORMAT_ENTRIES) {
|
||||
err = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
pcm_channels = formation.pcm;
|
||||
midi_ports = formation.midi * 8;
|
||||
|
||||
/* The stream should have one pcm channels at least */
|
||||
if (pcm_channels == 0) {
|
||||
err = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
err = amdtp_am824_set_parameters(stream, rate, pcm_channels, midi_ports,
|
||||
false);
|
||||
err = cmp_connection_establish(conn);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
return err;
|
||||
|
||||
err = cmp_connection_establish(conn,
|
||||
amdtp_stream_get_max_payload(stream));
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
err = amdtp_stream_start(stream,
|
||||
conn->resources.channel,
|
||||
conn->speed);
|
||||
err = amdtp_stream_start(stream, conn->resources.channel, conn->speed);
|
||||
if (err < 0) {
|
||||
cmp_connection_break(conn);
|
||||
goto end;
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Wait first packet */
|
||||
// Wait first packet.
|
||||
if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) {
|
||||
stop_stream(oxfw, stream);
|
||||
err = -ETIMEDOUT;
|
||||
amdtp_stream_stop(stream);
|
||||
cmp_connection_break(conn);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
end:
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_connection_used_by_others(struct snd_oxfw *oxfw,
|
||||
|
@ -205,8 +154,7 @@ static int check_connection_used_by_others(struct snd_oxfw *oxfw,
|
|||
return err;
|
||||
}
|
||||
|
||||
int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
|
||||
struct amdtp_stream *stream)
|
||||
static int init_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
|
||||
{
|
||||
struct cmp_connection *conn;
|
||||
enum cmp_direction c_dir;
|
||||
|
@ -225,13 +173,12 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
|
|||
|
||||
err = cmp_connection_init(conn, oxfw->unit, c_dir, 0);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
return err;
|
||||
|
||||
err = amdtp_am824_init(stream, oxfw->unit, s_dir, CIP_NONBLOCKING);
|
||||
if (err < 0) {
|
||||
amdtp_stream_destroy(stream);
|
||||
cmp_connection_destroy(conn);
|
||||
goto end;
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -245,115 +192,195 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
|
|||
if (oxfw->wrong_dbs)
|
||||
oxfw->tx_stream.flags |= CIP_WRONG_DBS;
|
||||
}
|
||||
end:
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw,
|
||||
struct amdtp_stream *stream,
|
||||
unsigned int rate, unsigned int pcm_channels)
|
||||
static int keep_resources(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
|
||||
{
|
||||
struct amdtp_stream *opposite;
|
||||
struct snd_oxfw_stream_formation formation;
|
||||
enum avc_general_plug_dir dir;
|
||||
unsigned int substreams, opposite_substreams;
|
||||
int err = 0;
|
||||
|
||||
if (stream == &oxfw->tx_stream) {
|
||||
substreams = oxfw->capture_substreams;
|
||||
opposite = &oxfw->rx_stream;
|
||||
opposite_substreams = oxfw->playback_substreams;
|
||||
dir = AVC_GENERAL_PLUG_DIR_OUT;
|
||||
} else {
|
||||
substreams = oxfw->playback_substreams;
|
||||
opposite_substreams = oxfw->capture_substreams;
|
||||
|
||||
if (oxfw->has_output)
|
||||
opposite = &oxfw->rx_stream;
|
||||
else
|
||||
opposite = NULL;
|
||||
u8 **formats;
|
||||
struct snd_oxfw_stream_formation formation;
|
||||
struct cmp_connection *conn;
|
||||
int i;
|
||||
int err;
|
||||
|
||||
if (stream == &oxfw->rx_stream) {
|
||||
dir = AVC_GENERAL_PLUG_DIR_IN;
|
||||
formats = oxfw->rx_stream_formats;
|
||||
conn = &oxfw->in_conn;
|
||||
} else {
|
||||
dir = AVC_GENERAL_PLUG_DIR_OUT;
|
||||
formats = oxfw->tx_stream_formats;
|
||||
conn = &oxfw->out_conn;
|
||||
}
|
||||
|
||||
if (substreams == 0)
|
||||
goto end;
|
||||
|
||||
/*
|
||||
* Considering JACK/FFADO streaming:
|
||||
* TODO: This can be removed hwdep functionality becomes popular.
|
||||
*/
|
||||
err = check_connection_used_by_others(oxfw, stream);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
/* packet queueing error */
|
||||
if (amdtp_streaming_error(stream))
|
||||
stop_stream(oxfw, stream);
|
||||
|
||||
err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
if (rate == 0)
|
||||
return err;
|
||||
|
||||
for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
|
||||
struct snd_oxfw_stream_formation fmt;
|
||||
|
||||
if (formats[i] == NULL)
|
||||
break;
|
||||
|
||||
err = snd_oxfw_stream_parse_format(formats[i], &fmt);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (fmt.rate == formation.rate && fmt.pcm == formation.pcm &&
|
||||
fmt.midi == formation.midi)
|
||||
break;
|
||||
}
|
||||
if (i == SND_OXFW_STREAM_FORMAT_ENTRIES)
|
||||
return -EINVAL;
|
||||
|
||||
// The stream should have one pcm channels at least.
|
||||
if (formation.pcm == 0)
|
||||
return -EINVAL;
|
||||
|
||||
err = amdtp_am824_set_parameters(stream, formation.rate, formation.pcm,
|
||||
formation.midi * 8, false);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return cmp_connection_reserve(conn, amdtp_stream_get_max_payload(stream));
|
||||
}
|
||||
|
||||
int snd_oxfw_stream_reserve_duplex(struct snd_oxfw *oxfw,
|
||||
struct amdtp_stream *stream,
|
||||
unsigned int rate, unsigned int pcm_channels)
|
||||
{
|
||||
struct snd_oxfw_stream_formation formation;
|
||||
enum avc_general_plug_dir dir;
|
||||
int err;
|
||||
|
||||
// Considering JACK/FFADO streaming:
|
||||
// TODO: This can be removed hwdep functionality becomes popular.
|
||||
err = check_connection_used_by_others(oxfw, &oxfw->rx_stream);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (oxfw->has_output) {
|
||||
err = check_connection_used_by_others(oxfw, &oxfw->tx_stream);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (stream == &oxfw->tx_stream)
|
||||
dir = AVC_GENERAL_PLUG_DIR_OUT;
|
||||
else
|
||||
dir = AVC_GENERAL_PLUG_DIR_IN;
|
||||
|
||||
err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (rate == 0) {
|
||||
rate = formation.rate;
|
||||
if (pcm_channels == 0)
|
||||
pcm_channels = formation.pcm;
|
||||
}
|
||||
if (formation.rate != rate || formation.pcm != pcm_channels) {
|
||||
amdtp_stream_stop(&oxfw->rx_stream);
|
||||
cmp_connection_break(&oxfw->in_conn);
|
||||
cmp_connection_release(&oxfw->in_conn);
|
||||
|
||||
if ((formation.rate != rate) || (formation.pcm != pcm_channels)) {
|
||||
if (opposite != NULL) {
|
||||
err = check_connection_used_by_others(oxfw, opposite);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
stop_stream(oxfw, opposite);
|
||||
if (oxfw->has_output) {
|
||||
amdtp_stream_stop(&oxfw->tx_stream);
|
||||
cmp_connection_break(&oxfw->out_conn);
|
||||
cmp_connection_release(&oxfw->out_conn);
|
||||
}
|
||||
stop_stream(oxfw, stream);
|
||||
}
|
||||
|
||||
if (oxfw->substreams_count == 0 ||
|
||||
formation.rate != rate || formation.pcm != pcm_channels) {
|
||||
err = set_stream_format(oxfw, stream, rate, pcm_channels);
|
||||
if (err < 0) {
|
||||
dev_err(&oxfw->unit->device,
|
||||
"fail to set stream format: %d\n", err);
|
||||
goto end;
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Start opposite stream if needed. */
|
||||
if (opposite && !amdtp_stream_running(opposite) &&
|
||||
(opposite_substreams > 0)) {
|
||||
err = start_stream(oxfw, opposite, rate, 0);
|
||||
err = keep_resources(oxfw, &oxfw->rx_stream);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (oxfw->has_output) {
|
||||
err = keep_resources(oxfw, &oxfw->tx_stream);
|
||||
if (err < 0) {
|
||||
dev_err(&oxfw->unit->device,
|
||||
"fail to restart stream: %d\n", err);
|
||||
goto end;
|
||||
cmp_connection_release(&oxfw->in_conn);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Start requested stream. */
|
||||
if (!amdtp_stream_running(stream)) {
|
||||
err = start_stream(oxfw, stream, rate, pcm_channels);
|
||||
if (err < 0)
|
||||
dev_err(&oxfw->unit->device,
|
||||
"fail to start stream: %d\n", err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (oxfw->substreams_count == 0)
|
||||
return -EIO;
|
||||
|
||||
if (amdtp_streaming_error(&oxfw->rx_stream) ||
|
||||
amdtp_streaming_error(&oxfw->tx_stream)) {
|
||||
amdtp_stream_stop(&oxfw->rx_stream);
|
||||
cmp_connection_break(&oxfw->in_conn);
|
||||
|
||||
if (oxfw->has_output) {
|
||||
amdtp_stream_stop(&oxfw->tx_stream);
|
||||
cmp_connection_break(&oxfw->out_conn);
|
||||
}
|
||||
}
|
||||
|
||||
if (!amdtp_stream_running(&oxfw->rx_stream)) {
|
||||
err = start_stream(oxfw, &oxfw->rx_stream);
|
||||
if (err < 0) {
|
||||
dev_err(&oxfw->unit->device,
|
||||
"fail to start rx stream: %d\n", err);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (oxfw->has_output) {
|
||||
if (!amdtp_stream_running(&oxfw->tx_stream)) {
|
||||
err = start_stream(oxfw, &oxfw->tx_stream);
|
||||
if (err < 0) {
|
||||
dev_err(&oxfw->unit->device,
|
||||
"fail to start tx stream: %d\n", err);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
error:
|
||||
amdtp_stream_stop(&oxfw->rx_stream);
|
||||
cmp_connection_break(&oxfw->in_conn);
|
||||
if (oxfw->has_output) {
|
||||
amdtp_stream_stop(&oxfw->tx_stream);
|
||||
cmp_connection_break(&oxfw->out_conn);
|
||||
}
|
||||
end:
|
||||
return err;
|
||||
}
|
||||
|
||||
void snd_oxfw_stream_stop_simplex(struct snd_oxfw *oxfw,
|
||||
struct amdtp_stream *stream)
|
||||
void snd_oxfw_stream_stop_duplex(struct snd_oxfw *oxfw)
|
||||
{
|
||||
if (((stream == &oxfw->tx_stream) && (oxfw->capture_substreams > 0)) ||
|
||||
((stream == &oxfw->rx_stream) && (oxfw->playback_substreams > 0)))
|
||||
return;
|
||||
if (oxfw->substreams_count == 0) {
|
||||
amdtp_stream_stop(&oxfw->rx_stream);
|
||||
cmp_connection_break(&oxfw->in_conn);
|
||||
cmp_connection_release(&oxfw->in_conn);
|
||||
|
||||
stop_stream(oxfw, stream);
|
||||
if (oxfw->has_output) {
|
||||
amdtp_stream_stop(&oxfw->tx_stream);
|
||||
cmp_connection_break(&oxfw->out_conn);
|
||||
cmp_connection_release(&oxfw->out_conn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function should be called before starting the stream or after stopping
|
||||
* the streams.
|
||||
*/
|
||||
void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw,
|
||||
struct amdtp_stream *stream)
|
||||
static void destroy_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
|
||||
{
|
||||
struct cmp_connection *conn;
|
||||
|
||||
|
@ -366,20 +393,48 @@ void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw,
|
|||
cmp_connection_destroy(conn);
|
||||
}
|
||||
|
||||
void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw,
|
||||
struct amdtp_stream *stream)
|
||||
int snd_oxfw_stream_init_duplex(struct snd_oxfw *oxfw)
|
||||
{
|
||||
struct cmp_connection *conn;
|
||||
int err;
|
||||
|
||||
if (stream == &oxfw->tx_stream)
|
||||
conn = &oxfw->out_conn;
|
||||
else
|
||||
conn = &oxfw->in_conn;
|
||||
err = init_stream(oxfw, &oxfw->rx_stream);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (cmp_connection_update(conn) < 0)
|
||||
stop_stream(oxfw, stream);
|
||||
else
|
||||
amdtp_stream_update(stream);
|
||||
if (oxfw->has_output) {
|
||||
err = init_stream(oxfw, &oxfw->tx_stream);
|
||||
if (err < 0) {
|
||||
destroy_stream(oxfw, &oxfw->rx_stream);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// This function should be called before starting the stream or after stopping
|
||||
// the streams.
|
||||
void snd_oxfw_stream_destroy_duplex(struct snd_oxfw *oxfw)
|
||||
{
|
||||
destroy_stream(oxfw, &oxfw->rx_stream);
|
||||
|
||||
if (oxfw->has_output)
|
||||
destroy_stream(oxfw, &oxfw->tx_stream);
|
||||
}
|
||||
|
||||
void snd_oxfw_stream_update_duplex(struct snd_oxfw *oxfw)
|
||||
{
|
||||
amdtp_stream_stop(&oxfw->rx_stream);
|
||||
cmp_connection_break(&oxfw->in_conn);
|
||||
|
||||
amdtp_stream_pcm_abort(&oxfw->rx_stream);
|
||||
|
||||
if (oxfw->has_output) {
|
||||
amdtp_stream_stop(&oxfw->tx_stream);
|
||||
cmp_connection_break(&oxfw->out_conn);
|
||||
|
||||
amdtp_stream_pcm_abort(&oxfw->tx_stream);
|
||||
}
|
||||
}
|
||||
|
||||
int snd_oxfw_stream_get_current_formation(struct snd_oxfw *oxfw,
|
||||
|
|
|
@ -118,9 +118,7 @@ static void oxfw_card_free(struct snd_card *card)
|
|||
{
|
||||
struct snd_oxfw *oxfw = card->private_data;
|
||||
|
||||
snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream);
|
||||
if (oxfw->has_output)
|
||||
snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream);
|
||||
snd_oxfw_stream_destroy_duplex(oxfw);
|
||||
}
|
||||
|
||||
static int detect_quirks(struct snd_oxfw *oxfw)
|
||||
|
@ -208,14 +206,9 @@ static void do_registration(struct work_struct *work)
|
|||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->rx_stream);
|
||||
err = snd_oxfw_stream_init_duplex(oxfw);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
if (oxfw->has_output) {
|
||||
err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->tx_stream);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
err = snd_oxfw_create_pcm(oxfw);
|
||||
if (err < 0)
|
||||
|
@ -282,11 +275,7 @@ static void oxfw_bus_reset(struct fw_unit *unit)
|
|||
|
||||
if (oxfw->registered) {
|
||||
mutex_lock(&oxfw->mutex);
|
||||
|
||||
snd_oxfw_stream_update_simplex(oxfw, &oxfw->rx_stream);
|
||||
if (oxfw->has_output)
|
||||
snd_oxfw_stream_update_simplex(oxfw, &oxfw->tx_stream);
|
||||
|
||||
snd_oxfw_stream_update_duplex(oxfw);
|
||||
mutex_unlock(&oxfw->mutex);
|
||||
|
||||
if (oxfw->entry->vendor_id == OUI_STANTON)
|
||||
|
|
|
@ -52,8 +52,7 @@ struct snd_oxfw {
|
|||
struct cmp_connection in_conn;
|
||||
struct amdtp_stream tx_stream;
|
||||
struct amdtp_stream rx_stream;
|
||||
unsigned int capture_substreams;
|
||||
unsigned int playback_substreams;
|
||||
unsigned int substreams_count;
|
||||
|
||||
unsigned int midi_input_ports;
|
||||
unsigned int midi_output_ports;
|
||||
|
@ -99,17 +98,14 @@ int avc_general_inquiry_sig_fmt(struct fw_unit *unit, unsigned int rate,
|
|||
enum avc_general_plug_dir dir,
|
||||
unsigned short pid);
|
||||
|
||||
int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
|
||||
struct amdtp_stream *stream);
|
||||
int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw,
|
||||
struct amdtp_stream *stream,
|
||||
unsigned int rate, unsigned int pcm_channels);
|
||||
void snd_oxfw_stream_stop_simplex(struct snd_oxfw *oxfw,
|
||||
struct amdtp_stream *stream);
|
||||
void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw,
|
||||
struct amdtp_stream *stream);
|
||||
void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw,
|
||||
struct amdtp_stream *stream);
|
||||
int snd_oxfw_stream_init_duplex(struct snd_oxfw *oxfw);
|
||||
int snd_oxfw_stream_reserve_duplex(struct snd_oxfw *oxfw,
|
||||
struct amdtp_stream *stream,
|
||||
unsigned int rate, unsigned int pcm_channels);
|
||||
int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw);
|
||||
void snd_oxfw_stream_stop_duplex(struct snd_oxfw *oxfw);
|
||||
void snd_oxfw_stream_destroy_duplex(struct snd_oxfw *oxfw);
|
||||
void snd_oxfw_stream_update_duplex(struct snd_oxfw *oxfw);
|
||||
|
||||
struct snd_oxfw_stream_formation {
|
||||
unsigned int rate;
|
||||
|
|
|
@ -223,7 +223,7 @@ int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit,
|
|||
return 0;
|
||||
|
||||
/* Use fixed value for FDF field. */
|
||||
s->fdf = 0x00;
|
||||
s->ctx_data.rx.fdf = 0x00;
|
||||
|
||||
/* This protocol uses fixed number of data channels for PCM samples. */
|
||||
p = s->protocol;
|
||||
|
|
|
@ -83,8 +83,8 @@ static int pcm_close(struct snd_pcm_substream *substream)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
static int pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct snd_tscm *tscm = substream->private_data;
|
||||
int err;
|
||||
|
@ -95,58 +95,26 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
|
|||
return err;
|
||||
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
|
||||
unsigned int rate = params_rate(hw_params);
|
||||
|
||||
mutex_lock(&tscm->mutex);
|
||||
tscm->substreams_counter++;
|
||||
err = snd_tscm_stream_reserve_duplex(tscm, rate);
|
||||
if (err >= 0)
|
||||
++tscm->substreams_counter;
|
||||
mutex_unlock(&tscm->mutex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct snd_tscm *tscm = substream->private_data;
|
||||
int err;
|
||||
|
||||
err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
|
||||
params_buffer_bytes(hw_params));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
|
||||
mutex_lock(&tscm->mutex);
|
||||
tscm->substreams_counter++;
|
||||
mutex_unlock(&tscm->mutex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
|
||||
static int pcm_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_tscm *tscm = substream->private_data;
|
||||
|
||||
mutex_lock(&tscm->mutex);
|
||||
|
||||
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
|
||||
tscm->substreams_counter--;
|
||||
|
||||
snd_tscm_stream_stop_duplex(tscm);
|
||||
|
||||
mutex_unlock(&tscm->mutex);
|
||||
|
||||
return snd_pcm_lib_free_vmalloc_buffer(substream);
|
||||
}
|
||||
|
||||
static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_tscm *tscm = substream->private_data;
|
||||
|
||||
mutex_lock(&tscm->mutex);
|
||||
|
||||
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
|
||||
tscm->substreams_counter--;
|
||||
--tscm->substreams_counter;
|
||||
|
||||
snd_tscm_stream_stop_duplex(tscm);
|
||||
|
||||
|
@ -259,8 +227,8 @@ int snd_tscm_create_pcm_devices(struct snd_tscm *tscm)
|
|||
.open = pcm_open,
|
||||
.close = pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = pcm_capture_hw_params,
|
||||
.hw_free = pcm_capture_hw_free,
|
||||
.hw_params = pcm_hw_params,
|
||||
.hw_free = pcm_hw_free,
|
||||
.prepare = pcm_capture_prepare,
|
||||
.trigger = pcm_capture_trigger,
|
||||
.pointer = pcm_capture_pointer,
|
||||
|
@ -271,8 +239,8 @@ int snd_tscm_create_pcm_devices(struct snd_tscm *tscm)
|
|||
.open = pcm_open,
|
||||
.close = pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = pcm_playback_hw_params,
|
||||
.hw_free = pcm_playback_hw_free,
|
||||
.hw_params = pcm_hw_params,
|
||||
.hw_free = pcm_hw_free,
|
||||
.prepare = pcm_playback_prepare,
|
||||
.trigger = pcm_playback_trigger,
|
||||
.pointer = pcm_playback_pointer,
|
||||
|
|
|
@ -165,7 +165,7 @@ static int set_stream_formats(struct snd_tscm *tscm, unsigned int rate)
|
|||
__be32 reg;
|
||||
int err;
|
||||
|
||||
/* Set an option for unknown purpose. */
|
||||
// Set an option for unknown purpose.
|
||||
reg = cpu_to_be32(0x00200000);
|
||||
err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_SET_OPTION,
|
||||
|
@ -173,17 +173,16 @@ static int set_stream_formats(struct snd_tscm *tscm, unsigned int rate)
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = enable_data_channels(tscm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return set_clock(tscm, rate, INT_MAX);
|
||||
return enable_data_channels(tscm);
|
||||
}
|
||||
|
||||
static void finish_session(struct snd_tscm *tscm)
|
||||
{
|
||||
__be32 reg;
|
||||
|
||||
amdtp_stream_stop(&tscm->rx_stream);
|
||||
amdtp_stream_stop(&tscm->tx_stream);
|
||||
|
||||
reg = 0;
|
||||
snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING,
|
||||
|
@ -194,6 +193,19 @@ static void finish_session(struct snd_tscm *tscm)
|
|||
TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_ON,
|
||||
®, sizeof(reg), 0);
|
||||
|
||||
// Unregister channels.
|
||||
reg = cpu_to_be32(0x00000000);
|
||||
snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH,
|
||||
®, sizeof(reg), 0);
|
||||
reg = cpu_to_be32(0x00000000);
|
||||
snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN,
|
||||
®, sizeof(reg), 0);
|
||||
reg = cpu_to_be32(0x00000000);
|
||||
snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH,
|
||||
®, sizeof(reg), 0);
|
||||
}
|
||||
|
||||
static int begin_session(struct snd_tscm *tscm)
|
||||
|
@ -201,6 +213,30 @@ static int begin_session(struct snd_tscm *tscm)
|
|||
__be32 reg;
|
||||
int err;
|
||||
|
||||
// Register the isochronous channel for transmitting stream.
|
||||
reg = cpu_to_be32(tscm->tx_resources.channel);
|
||||
err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH,
|
||||
®, sizeof(reg), 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
// Unknown.
|
||||
reg = cpu_to_be32(0x00000002);
|
||||
err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN,
|
||||
®, sizeof(reg), 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
// Register the isochronous channel for receiving stream.
|
||||
reg = cpu_to_be32(tscm->rx_resources.channel);
|
||||
err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH,
|
||||
®, sizeof(reg), 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
reg = cpu_to_be32(0x00000001);
|
||||
err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING,
|
||||
|
@ -215,7 +251,7 @@ static int begin_session(struct snd_tscm *tscm)
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Set an option for unknown purpose. */
|
||||
// Set an option for unknown purpose.
|
||||
reg = cpu_to_be32(0x00002000);
|
||||
err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_SET_OPTION,
|
||||
|
@ -223,7 +259,7 @@ static int begin_session(struct snd_tscm *tscm)
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Start multiplexing PCM samples on packets. */
|
||||
// Start multiplexing PCM samples on packets.
|
||||
reg = cpu_to_be32(0x00000001);
|
||||
return snd_fw_transaction(tscm->unit,
|
||||
TCODE_WRITE_QUADLET_REQUEST,
|
||||
|
@ -231,82 +267,24 @@ static int begin_session(struct snd_tscm *tscm)
|
|||
®, sizeof(reg), 0);
|
||||
}
|
||||
|
||||
static void release_resources(struct snd_tscm *tscm)
|
||||
static int keep_resources(struct snd_tscm *tscm, unsigned int rate,
|
||||
struct amdtp_stream *stream)
|
||||
{
|
||||
__be32 reg;
|
||||
|
||||
/* Unregister channels. */
|
||||
reg = cpu_to_be32(0x00000000);
|
||||
snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH,
|
||||
®, sizeof(reg), 0);
|
||||
reg = cpu_to_be32(0x00000000);
|
||||
snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN,
|
||||
®, sizeof(reg), 0);
|
||||
reg = cpu_to_be32(0x00000000);
|
||||
snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH,
|
||||
®, sizeof(reg), 0);
|
||||
|
||||
/* Release isochronous resources. */
|
||||
fw_iso_resources_free(&tscm->tx_resources);
|
||||
fw_iso_resources_free(&tscm->rx_resources);
|
||||
}
|
||||
|
||||
static int keep_resources(struct snd_tscm *tscm, unsigned int rate)
|
||||
{
|
||||
__be32 reg;
|
||||
struct fw_iso_resources *resources;
|
||||
int err;
|
||||
|
||||
/* Keep resources for in-stream. */
|
||||
err = amdtp_tscm_set_parameters(&tscm->tx_stream, rate);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = fw_iso_resources_allocate(&tscm->tx_resources,
|
||||
amdtp_stream_get_max_payload(&tscm->tx_stream),
|
||||
fw_parent_device(tscm->unit)->max_speed);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
if (stream == &tscm->tx_stream)
|
||||
resources = &tscm->tx_resources;
|
||||
else
|
||||
resources = &tscm->rx_resources;
|
||||
|
||||
/* Keep resources for out-stream. */
|
||||
err = amdtp_tscm_set_parameters(&tscm->rx_stream, rate);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = fw_iso_resources_allocate(&tscm->rx_resources,
|
||||
amdtp_stream_get_max_payload(&tscm->rx_stream),
|
||||
fw_parent_device(tscm->unit)->max_speed);
|
||||
err = amdtp_tscm_set_parameters(stream, rate);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Register the isochronous channel for transmitting stream. */
|
||||
reg = cpu_to_be32(tscm->tx_resources.channel);
|
||||
err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH,
|
||||
®, sizeof(reg), 0);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
/* Unknown */
|
||||
reg = cpu_to_be32(0x00000002);
|
||||
err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN,
|
||||
®, sizeof(reg), 0);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
/* Register the isochronous channel for receiving stream. */
|
||||
reg = cpu_to_be32(tscm->rx_resources.channel);
|
||||
err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH,
|
||||
®, sizeof(reg), 0);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
error:
|
||||
release_resources(tscm);
|
||||
return err;
|
||||
return fw_iso_resources_allocate(resources,
|
||||
amdtp_stream_get_max_payload(stream),
|
||||
fw_parent_device(tscm->unit)->max_speed);
|
||||
}
|
||||
|
||||
int snd_tscm_stream_init_duplex(struct snd_tscm *tscm)
|
||||
|
@ -345,7 +323,7 @@ int snd_tscm_stream_init_duplex(struct snd_tscm *tscm)
|
|||
return err;
|
||||
}
|
||||
|
||||
/* At bus reset, streaming is stopped and some registers are clear. */
|
||||
// At bus reset, streaming is stopped and some registers are clear.
|
||||
void snd_tscm_stream_update_duplex(struct snd_tscm *tscm)
|
||||
{
|
||||
amdtp_stream_pcm_abort(&tscm->tx_stream);
|
||||
|
@ -368,33 +346,62 @@ void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm)
|
|||
fw_iso_resources_destroy(&tscm->tx_resources);
|
||||
}
|
||||
|
||||
int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
|
||||
int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate)
|
||||
{
|
||||
unsigned int curr_rate;
|
||||
int err;
|
||||
|
||||
err = snd_tscm_stream_get_rate(tscm, &curr_rate);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (tscm->substreams_counter == 0 || rate != curr_rate) {
|
||||
finish_session(tscm);
|
||||
|
||||
fw_iso_resources_free(&tscm->tx_resources);
|
||||
fw_iso_resources_free(&tscm->rx_resources);
|
||||
|
||||
err = set_clock(tscm, rate, INT_MAX);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = keep_resources(tscm, rate, &tscm->tx_stream);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = keep_resources(tscm, rate, &tscm->rx_stream);
|
||||
if (err < 0) {
|
||||
fw_iso_resources_free(&tscm->tx_resources);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
|
||||
{
|
||||
unsigned int generation = tscm->rx_resources.generation;
|
||||
int err;
|
||||
|
||||
if (tscm->substreams_counter == 0)
|
||||
return 0;
|
||||
|
||||
err = snd_tscm_stream_get_rate(tscm, &curr_rate);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (curr_rate != rate ||
|
||||
amdtp_streaming_error(&tscm->rx_stream) ||
|
||||
amdtp_streaming_error(&tscm->tx_stream)) {
|
||||
if (amdtp_streaming_error(&tscm->rx_stream) ||
|
||||
amdtp_streaming_error(&tscm->tx_stream))
|
||||
finish_session(tscm);
|
||||
|
||||
amdtp_stream_stop(&tscm->rx_stream);
|
||||
amdtp_stream_stop(&tscm->tx_stream);
|
||||
|
||||
release_resources(tscm);
|
||||
}
|
||||
|
||||
if (!amdtp_stream_running(&tscm->rx_stream)) {
|
||||
err = keep_resources(tscm, rate);
|
||||
if (generation != fw_parent_device(tscm->unit)->card->generation) {
|
||||
err = fw_iso_resources_update(&tscm->tx_resources);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = fw_iso_resources_update(&tscm->rx_resources);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!amdtp_stream_running(&tscm->rx_stream)) {
|
||||
err = set_stream_formats(tscm, rate);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
@ -432,25 +439,19 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
|
|||
|
||||
return 0;
|
||||
error:
|
||||
amdtp_stream_stop(&tscm->rx_stream);
|
||||
amdtp_stream_stop(&tscm->tx_stream);
|
||||
|
||||
finish_session(tscm);
|
||||
release_resources(tscm);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm)
|
||||
{
|
||||
if (tscm->substreams_counter > 0)
|
||||
return;
|
||||
if (tscm->substreams_counter == 0) {
|
||||
finish_session(tscm);
|
||||
|
||||
amdtp_stream_stop(&tscm->tx_stream);
|
||||
amdtp_stream_stop(&tscm->rx_stream);
|
||||
|
||||
finish_session(tscm);
|
||||
release_resources(tscm);
|
||||
fw_iso_resources_free(&tscm->tx_resources);
|
||||
fw_iso_resources_free(&tscm->rx_resources);
|
||||
}
|
||||
}
|
||||
|
||||
void snd_tscm_stream_lock_changed(struct snd_tscm *tscm)
|
||||
|
|
|
@ -146,6 +146,7 @@ int snd_tscm_stream_get_clock(struct snd_tscm *tscm,
|
|||
int snd_tscm_stream_init_duplex(struct snd_tscm *tscm);
|
||||
void snd_tscm_stream_update_duplex(struct snd_tscm *tscm);
|
||||
void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm);
|
||||
int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate);
|
||||
int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate);
|
||||
void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm);
|
||||
|
||||
|
|
|
@ -85,7 +85,6 @@ int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev,
|
|||
const struct hdac_ext_bus_ops *ext_ops)
|
||||
{
|
||||
int ret;
|
||||
static int idx;
|
||||
|
||||
/* check if io ops are provided, if not load the defaults */
|
||||
if (io_ops == NULL)
|
||||
|
@ -96,7 +95,12 @@ int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev,
|
|||
return ret;
|
||||
|
||||
bus->ext_ops = ext_ops;
|
||||
bus->idx = idx++;
|
||||
/* FIXME:
|
||||
* Currently only one bus is supported, if there is device with more
|
||||
* buses, bus->idx should be greater than 0, but there needs to be a
|
||||
* reliable way to always assign same number.
|
||||
*/
|
||||
bus->idx = 0;
|
||||
bus->cmd_dma_state = true;
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -79,6 +79,8 @@ void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus)
|
|||
snd_hdac_chip_writew(bus, RINTCNT, 1);
|
||||
/* enable rirb dma and response irq */
|
||||
snd_hdac_chip_writeb(bus, RIRBCTL, AZX_RBCTL_DMA_EN | AZX_RBCTL_IRQ_EN);
|
||||
/* Accept unsolicited responses */
|
||||
snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, AZX_GCTL_UNSOL);
|
||||
spin_unlock_irq(&bus->reg_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_bus_init_cmd_io);
|
||||
|
@ -241,6 +243,8 @@ int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr,
|
|||
|
||||
for (loopcounter = 0;; loopcounter++) {
|
||||
spin_lock_irq(&bus->reg_lock);
|
||||
if (bus->polling_mode)
|
||||
snd_hdac_bus_update_rirb(bus);
|
||||
if (!bus->rirb.cmds[addr]) {
|
||||
if (res)
|
||||
*res = bus->rirb.res[addr]; /* the last value */
|
||||
|
@ -415,9 +419,6 @@ int snd_hdac_bus_reset_link(struct hdac_bus *bus, bool full_reset)
|
|||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* Accept unsolicited responses */
|
||||
snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, AZX_GCTL_UNSOL);
|
||||
|
||||
/* detect codecs */
|
||||
if (!bus->codec_mask) {
|
||||
bus->codec_mask = snd_hdac_chip_readw(bus, STATESTS);
|
||||
|
|
|
@ -90,7 +90,7 @@ int snd_hdac_device_init(struct hdac_device *codec, struct hdac_bus *bus,
|
|||
|
||||
fg = codec->afg ? codec->afg : codec->mfg;
|
||||
|
||||
err = snd_hdac_refresh_widgets(codec, false);
|
||||
err = snd_hdac_refresh_widgets(codec);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
|
@ -395,9 +395,8 @@ static void setup_fg_nodes(struct hdac_device *codec)
|
|||
/**
|
||||
* snd_hdac_refresh_widgets - Reset the widget start/end nodes
|
||||
* @codec: the codec object
|
||||
* @sysfs: re-initialize sysfs tree, too
|
||||
*/
|
||||
int snd_hdac_refresh_widgets(struct hdac_device *codec, bool sysfs)
|
||||
int snd_hdac_refresh_widgets(struct hdac_device *codec)
|
||||
{
|
||||
hda_nid_t start_nid;
|
||||
int nums, err = 0;
|
||||
|
@ -415,11 +414,9 @@ int snd_hdac_refresh_widgets(struct hdac_device *codec, bool sysfs)
|
|||
goto unlock;
|
||||
}
|
||||
|
||||
if (sysfs) {
|
||||
err = hda_widget_sysfs_reinit(codec, start_nid, nums);
|
||||
if (err < 0)
|
||||
goto unlock;
|
||||
}
|
||||
err = hda_widget_sysfs_reinit(codec, start_nid, nums);
|
||||
if (err < 0)
|
||||
goto unlock;
|
||||
|
||||
codec->num_nodes = nums;
|
||||
codec->start_nid = start_nid;
|
||||
|
|
|
@ -428,7 +428,7 @@ int hda_widget_sysfs_reinit(struct hdac_device *codec,
|
|||
int i;
|
||||
|
||||
if (!codec->widgets)
|
||||
return hda_widget_sysfs_init(codec);
|
||||
return 0;
|
||||
|
||||
tree = kmemdup(codec->widgets, sizeof(*tree), GFP_KERNEL);
|
||||
if (!tree)
|
||||
|
|
|
@ -1519,7 +1519,6 @@ static int snd_asihpi_volume_get(struct snd_kcontrol *kcontrol,
|
|||
static int snd_asihpi_volume_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
int change;
|
||||
u32 h_control = kcontrol->private_value;
|
||||
short an_gain_mB[HPI_MAX_CHANNELS];
|
||||
|
||||
|
@ -1530,9 +1529,8 @@ static int snd_asihpi_volume_put(struct snd_kcontrol *kcontrol,
|
|||
/* change = asihpi->mixer_volume[addr][0] != left ||
|
||||
asihpi->mixer_volume[addr][1] != right;
|
||||
*/
|
||||
change = 1;
|
||||
hpi_handle_error(hpi_volume_set_gain(h_control, an_gain_mB));
|
||||
return change;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const DECLARE_TLV_DB_SCALE(db_scale_100, -10000, VOL_STEP_mB, 0);
|
||||
|
@ -1555,13 +1553,12 @@ static int snd_asihpi_volume_mute_put(struct snd_kcontrol *kcontrol,
|
|||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
u32 h_control = kcontrol->private_value;
|
||||
int change = 1;
|
||||
/* HPI currently only supports all or none muting of multichannel volume
|
||||
ALSA Switch element has opposite sense to HPI mute: on==unmuted, off=muted
|
||||
*/
|
||||
int mute = ucontrol->value.integer.value[0] ? 0 : HPI_BITMASK_ALL_CHANNELS;
|
||||
hpi_handle_error(hpi_volume_set_mute(h_control, mute));
|
||||
return change;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int snd_asihpi_volume_add(struct snd_card_asihpi *asihpi,
|
||||
|
|
|
@ -694,7 +694,7 @@ static int snd_cs4281_trigger(struct snd_pcm_substream *substream, int cmd)
|
|||
|
||||
static unsigned int snd_cs4281_rate(unsigned int rate, unsigned int *real_rate)
|
||||
{
|
||||
unsigned int val = ~0;
|
||||
unsigned int val;
|
||||
|
||||
if (real_rate)
|
||||
*real_rate = rate;
|
||||
|
@ -707,9 +707,8 @@ static unsigned int snd_cs4281_rate(unsigned int rate, unsigned int *real_rate)
|
|||
case 44100: return 1;
|
||||
case 48000: return 0;
|
||||
default:
|
||||
goto __variable;
|
||||
break;
|
||||
}
|
||||
__variable:
|
||||
val = 1536000 / rate;
|
||||
if (real_rate)
|
||||
*real_rate = 1536000 / val;
|
||||
|
|
|
@ -1058,7 +1058,6 @@ static int allocate_pipes(struct echoaudio *chip, struct audiopipe *pipe,
|
|||
{
|
||||
int i;
|
||||
u32 channel_mask;
|
||||
char is_cyclic;
|
||||
|
||||
dev_dbg(chip->card->dev,
|
||||
"allocate_pipes: ch=%d int=%d\n", pipe_index, interleave);
|
||||
|
@ -1066,8 +1065,6 @@ static int allocate_pipes(struct echoaudio *chip, struct audiopipe *pipe,
|
|||
if (chip->bad_board)
|
||||
return -EIO;
|
||||
|
||||
is_cyclic = 1; /* This driver uses cyclic buffers only */
|
||||
|
||||
for (channel_mask = i = 0; i < interleave; i++)
|
||||
channel_mask |= 1 << (pipe_index + i);
|
||||
if (chip->pipe_alloc_mask & channel_mask) {
|
||||
|
@ -1078,8 +1075,8 @@ static int allocate_pipes(struct echoaudio *chip, struct audiopipe *pipe,
|
|||
|
||||
chip->comm_page->position[pipe_index] = 0;
|
||||
chip->pipe_alloc_mask |= channel_mask;
|
||||
if (is_cyclic)
|
||||
chip->pipe_cyclic_mask |= channel_mask;
|
||||
/* This driver uses cyclic buffers only */
|
||||
chip->pipe_cyclic_mask |= channel_mask;
|
||||
pipe->index = pipe_index;
|
||||
pipe->interleave = interleave;
|
||||
pipe->state = PIPE_STATE_STOPPED;
|
||||
|
|
|
@ -108,7 +108,7 @@ static int add_conn_list(struct hda_codec *codec, hda_nid_t nid, int len,
|
|||
{
|
||||
struct hda_conn_list *p;
|
||||
|
||||
p = kmalloc(sizeof(*p) + len * sizeof(hda_nid_t), GFP_KERNEL);
|
||||
p = kmalloc(struct_size(p, conns, len), GFP_KERNEL);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
p->len = len;
|
||||
|
@ -1002,7 +1002,7 @@ int snd_hda_codec_update_widgets(struct hda_codec *codec)
|
|||
hda_nid_t fg;
|
||||
int err;
|
||||
|
||||
err = snd_hdac_refresh_widgets(&codec->core, true);
|
||||
err = snd_hdac_refresh_widgets(&codec->core);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
|
|
@ -795,11 +795,11 @@ static int azx_rirb_get_response(struct hdac_bus *bus, unsigned int addr,
|
|||
|
||||
for (loopcounter = 0;; loopcounter++) {
|
||||
spin_lock_irq(&bus->reg_lock);
|
||||
if (chip->polling_mode || do_poll)
|
||||
if (bus->polling_mode || do_poll)
|
||||
snd_hdac_bus_update_rirb(bus);
|
||||
if (!bus->rirb.cmds[addr]) {
|
||||
if (!do_poll)
|
||||
chip->poll_count = 0;
|
||||
bus->poll_count = 0;
|
||||
if (res)
|
||||
*res = bus->rirb.res[addr]; /* the last value */
|
||||
spin_unlock_irq(&bus->reg_lock);
|
||||
|
@ -819,21 +819,21 @@ static int azx_rirb_get_response(struct hdac_bus *bus, unsigned int addr,
|
|||
if (hbus->no_response_fallback)
|
||||
return -EIO;
|
||||
|
||||
if (!chip->polling_mode && chip->poll_count < 2) {
|
||||
if (!bus->polling_mode && bus->poll_count < 2) {
|
||||
dev_dbg(chip->card->dev,
|
||||
"azx_get_response timeout, polling the codec once: last cmd=0x%08x\n",
|
||||
bus->last_cmd[addr]);
|
||||
do_poll = 1;
|
||||
chip->poll_count++;
|
||||
bus->poll_count++;
|
||||
goto again;
|
||||
}
|
||||
|
||||
|
||||
if (!chip->polling_mode) {
|
||||
if (!bus->polling_mode) {
|
||||
dev_warn(chip->card->dev,
|
||||
"azx_get_response timeout, switching to polling mode: last cmd=0x%08x\n",
|
||||
bus->last_cmd[addr]);
|
||||
chip->polling_mode = 1;
|
||||
bus->polling_mode = 1;
|
||||
goto again;
|
||||
}
|
||||
|
||||
|
|
|
@ -133,11 +133,9 @@ struct azx {
|
|||
|
||||
/* flags */
|
||||
int bdl_pos_adj;
|
||||
int poll_count;
|
||||
unsigned int running:1;
|
||||
unsigned int fallback_to_single_cmd:1;
|
||||
unsigned int single_cmd:1;
|
||||
unsigned int polling_mode:1;
|
||||
unsigned int msi:1;
|
||||
unsigned int probing:1; /* codec probing phase */
|
||||
unsigned int snoop:1;
|
||||
|
|
|
@ -1687,10 +1687,6 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci,
|
|||
else
|
||||
chip->bdl_pos_adj = bdl_pos_adj[dev];
|
||||
|
||||
/* Workaround for a communication error on CFL (bko#199007) and CNL */
|
||||
if (IS_CFL(pci) || IS_CNL(pci))
|
||||
chip->polling_mode = 1;
|
||||
|
||||
err = azx_bus_init(chip, model[dev], &pci_hda_io_ops);
|
||||
if (err < 0) {
|
||||
kfree(hda);
|
||||
|
@ -1698,6 +1694,10 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci,
|
|||
return err;
|
||||
}
|
||||
|
||||
/* Workaround for a communication error on CFL (bko#199007) and CNL */
|
||||
if (IS_CFL(pci) || IS_CNL(pci))
|
||||
azx_bus(chip)->polling_mode = 1;
|
||||
|
||||
if (chip->driver_type == AZX_DRIVER_NVIDIA) {
|
||||
dev_dbg(chip->card->dev, "Enable delay in RIRB handling\n");
|
||||
chip->bus.needs_damn_long_delay = 1;
|
||||
|
@ -2374,6 +2374,9 @@ static const struct pci_device_id azx_ids[] = {
|
|||
/* Icelake */
|
||||
{ PCI_DEVICE(0x8086, 0x34c8),
|
||||
.driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
|
||||
/* Elkhart Lake */
|
||||
{ PCI_DEVICE(0x8086, 0x4b55),
|
||||
.driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
|
||||
/* Broxton-P(Apollolake) */
|
||||
{ PCI_DEVICE(0x8086, 0x5a98),
|
||||
.driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON },
|
||||
|
|
|
@ -559,7 +559,7 @@ static void call_jack_callback(struct hda_codec *codec, unsigned int res,
|
|||
void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res)
|
||||
{
|
||||
struct hda_jack_tbl *event;
|
||||
int tag = (res >> AC_UNSOL_RES_TAG_SHIFT) & 0x7f;
|
||||
int tag = (res & AC_UNSOL_RES_TAG) >> AC_UNSOL_RES_TAG_SHIFT;
|
||||
|
||||
event = snd_hda_jack_tbl_get_from_tag(codec, tag);
|
||||
if (!event)
|
||||
|
|
|
@ -2718,7 +2718,7 @@ static bool is_last(const struct dsp_image_seg *p)
|
|||
|
||||
static size_t dsp_sizeof(const struct dsp_image_seg *p)
|
||||
{
|
||||
return sizeof(*p) + p->count*sizeof(u32);
|
||||
return struct_size(p, data, p->count);
|
||||
}
|
||||
|
||||
static const struct dsp_image_seg *get_next_seg_ptr(
|
||||
|
@ -5980,7 +5980,7 @@ static int ca0132_alt_volume_put(struct snd_kcontrol *kcontrol,
|
|||
int ch = get_amp_channels(kcontrol);
|
||||
long *valp = ucontrol->value.integer.value;
|
||||
hda_nid_t vnid = 0;
|
||||
int changed = 1;
|
||||
int changed;
|
||||
|
||||
switch (nid) {
|
||||
case 0x02:
|
||||
|
|
|
@ -1614,7 +1614,8 @@ static void sync_eld_via_acomp(struct hda_codec *codec,
|
|||
if (jack == NULL)
|
||||
goto unlock;
|
||||
snd_jack_report(jack,
|
||||
eld->monitor_present ? SND_JACK_AVOUT : 0);
|
||||
(eld->monitor_present && eld->eld_valid) ?
|
||||
SND_JACK_AVOUT : 0);
|
||||
unlock:
|
||||
mutex_unlock(&per_pin->lock);
|
||||
}
|
||||
|
|
|
@ -3255,6 +3255,7 @@ static void alc256_init(struct hda_codec *codec)
|
|||
alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x4); /* Hight power */
|
||||
alc_update_coefex_idx(codec, 0x53, 0x02, 0x8000, 1 << 15); /* Clear bit */
|
||||
alc_update_coefex_idx(codec, 0x53, 0x02, 0x8000, 0 << 15);
|
||||
alc_update_coef_idx(codec, 0x36, 1 << 13, 1 << 5); /* Switch pcbeep path to Line in path*/
|
||||
}
|
||||
|
||||
static void alc256_shutup(struct hda_codec *codec)
|
||||
|
@ -7825,7 +7826,6 @@ static int patch_alc269(struct hda_codec *codec)
|
|||
spec->shutup = alc256_shutup;
|
||||
spec->init_hook = alc256_init;
|
||||
spec->gen.mixer_nid = 0; /* ALC256 does not have any loopback mixer path */
|
||||
alc_update_coef_idx(codec, 0x36, 1 << 13, 1 << 5); /* Switch pcbeep path to Line in path*/
|
||||
break;
|
||||
case 0x10ec0257:
|
||||
spec->codec_variant = ALC269_TYPE_ALC257;
|
||||
|
|
|
@ -986,8 +986,6 @@ static int lx_interrupt_handle_async_events(struct lx6464es *chip, u32 irqsrc,
|
|||
* Stat[8] LSB overrun
|
||||
* */
|
||||
|
||||
u64 orun_mask;
|
||||
u64 urun_mask;
|
||||
int eb_pending_out = (irqsrc & MASK_SYS_STATUS_EOBO) ? 1 : 0;
|
||||
int eb_pending_in = (irqsrc & MASK_SYS_STATUS_EOBI) ? 1 : 0;
|
||||
|
||||
|
@ -1010,9 +1008,6 @@ static int lx_interrupt_handle_async_events(struct lx6464es *chip, u32 irqsrc,
|
|||
*r_notified_out_pipe_mask);
|
||||
}
|
||||
|
||||
orun_mask = ((u64)stat[7] << 32) + stat[8];
|
||||
urun_mask = ((u64)stat[5] << 32) + stat[6];
|
||||
|
||||
/* todo: handle xrun notification */
|
||||
|
||||
return err;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue