sound updates for 5.4
As shown in diffstat and logs, it was again a busy development cycle at this time, too. The most significant changes are still on-going refactoring / modernization works for ASoC core and drivers, but there are lots of other changes as well. Here we go, some highlights below: ASoC: - Quite lots of cleanup / refactoring of ASoC core and APIs; most of them are systematic, but also including cleanups and modernization - A bulk of updates for some ASoC platforms, Freescale, sunxi and Intel SST/SOF - Initial support for Sound Open Firmware on i.MX8 - Removal of deprecated w90x900 and nuc900 drivers - New support for Cirrus Logic CS47L15 and CS47L92, Freescale i.MX 7ULP and 8MQ, Meson G12A and NXP UDA1334 USB-audio: - More validations of descriptor units for hardening against bugs reported by fuzzers - PCM device assignment workaround for a past call-order change - Scarlett Gen2 mixer interface, a few more more quirks HD-audio: - Support for audio component with AMD/ATI and Nvidia HDMI codecs - Clean up HD-audio core and remove indirect access ops for Intel SOF - DMIC detection at probe; it would make systems automatically falling back to SST/SOF driver on devices that need DMIC handling. Needs a new Kconfig to set, and beware that it's still new and a bit experimental FireWire: - Lots of code refactoring and cleanups -----BEGIN PGP SIGNATURE----- iQJCBAABCAAsFiEEIXTw5fNLNI7mMiVaLtJE4w1nLE8FAl1/dEsOHHRpd2FpQHN1 c2UuZGUACgkQLtJE4w1nLE+2WA//YoEoxXgx/Aok5eJfKPSn0Pd7HccvU53Q9Syx z8DaUSUbd6S7g9P8zzXm1l5bIfAur1FQDkvfYrA23iDhixna/w+jnkeUbRBCFbnB tdUX4mMBMO/wfJZ5I1Amw9OaKsEaWoSaw8EezgWLkQUkVjfAVLitz/oH4qxlitxv ssOP2pLNaNKMqJjgV49DJ6wXjAFnndB+nSoEGzDkoDYv17VeahDG6XRPRGvmf6ZM 06w2KfGJqstNniH5Cw2us0GQf1UD6Ra1aZJRekoOjih5PeX8/Mjl+6HgQVwEZwZY ZnwAgJaL/ujUPywJO9cBlMv2yiePMqgXA8b5sNO1HJdzwIP7wAjD/bbY6V9+8IDZ zQpqHWen7B/PySLym7y19QvVZYmc+y43vJJev9M6kc0LofOltWmeeAUr5x3gktWx WuwN6KEHP5NA2cwkc8mW1LwiUavon1hl7PGOVqx7R3k4upPAKY1ZXBHNXgN2+M5N lX9fbAMLB2W0cPkD/Ob29JP04qDxmLT+nhHNh2lzSQBlHHpsuVUyGvJH4PwOWnTb tk1xTPxF+zVaVTV3TUGHziiK98PHY/Sj1hlyvKGuBiD+efvvnXAVkivjL9Egj4aq QGGGshTdOi7l7Anon3TEbHA8cFNkttRukKq1bpskZHydlaQtTOW0CEotRmvEJfpR ghQn5mY= =/+1Q -----END PGP SIGNATURE----- Merge tag 'sound-5.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound Pull sound updates from Takashi Iwai: "As shown in diffstat and logs, it was again a busy development cycle at this time, too. The most significant changes are still on-going refactoring / modernization works for ASoC core and drivers, but there are lots of other changes as well. Here we go, some highlights below: ASoC: - Quite a lot of cleanup / refactoring of ASoC core and APIs; most of them are systematic, but also including cleanups and modernization - A bulk of updates for some ASoC platforms, Freescale, sunxi and Intel SST/SOF - Initial support for Sound Open Firmware on i.MX8 - Removal of deprecated w90x900 and nuc900 drivers - New support for Cirrus Logic CS47L15 and CS47L92, Freescale i.MX 7ULP and 8MQ, Meson G12A and NXP UDA1334 USB-audio: - More validations of descriptor units for hardening against bugs reported by fuzzers - PCM device assignment workaround for a past call-order change - Scarlett Gen2 mixer interface, a few more more quirks HD-audio: - Support for audio component with AMD/ATI and Nvidia HDMI codecs - Clean up HD-audio core and remove indirect access ops for Intel SOF - DMIC detection at probe; it would make systems automatically falling back to SST/SOF driver on devices that need DMIC handling. Needs a new Kconfig to set, and beware that it's still new and a bit experimental FireWire: - Lots of code refactoring and cleanups" * tag 'sound-5.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (521 commits) ASoC: sdm845: remove unneeded semicolon ASoC: fsl_sai: Implement set_bclk_ratio ASoC: dmaengine: Replace strncpy() with strscpy_pad() for pcm->name ASoC: wcd9335: remove redundant use of ret variable ALSA: firewire-tascam: check intermediate state of clock status and retry ALSA: firewire-tascam: handle error code when getting current source of clock ASoC: hdmi-codec: Add an op to set callback function for plug event ASoC: rt5677: keep analog power register at SND_SOC_BIAS_OFF ASoC: rt5677: Remove magic number register writes ASoC: soc-core: self contained soc_unbind_aux_dev() ASoC: soc-core: add soc_unbind_aux_dev() ASoC: soc-core: self contained soc_bind_aux_dev() ASoC: soc-core: move soc_probe_link_dais() next to soc_remove_link_dais() ASoC: soc-core: self contained soc_probe_link_dais() ASoC: soc-core: add new soc_link_init() ASoC: soc-core: move soc_probe_dai() next to soc_remove_dai() ASoC: soc-core: self contained soc_remove_link_dais() ASoC: soc-core: self contained soc_remove_link_components() ASoC: soc-core: self contained soc_probe_link_components() ASoC: rt1308: make array pd static const, makes object smaller ...
This commit is contained in:
Коммит
6ab8ad3160
|
@ -0,0 +1,88 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/dsp/fsl,dsp.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: NXP i.MX8 DSP core
|
||||
|
||||
maintainers:
|
||||
- Daniel Baluta <daniel.baluta@nxp.com>
|
||||
|
||||
description: |
|
||||
Some boards from i.MX8 family contain a DSP core used for
|
||||
advanced pre- and post- audio processing.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- fsl,imx8qxp-dsp
|
||||
|
||||
reg:
|
||||
description: Should contain register location and length
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: ipg clock
|
||||
- description: ocram clock
|
||||
- description: core clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: ipg
|
||||
- const: ocram
|
||||
- const: core
|
||||
|
||||
power-domains:
|
||||
description:
|
||||
List of phandle and PM domain specifier as documented in
|
||||
Documentation/devicetree/bindings/power/power_domain.txt
|
||||
maxItems: 4
|
||||
|
||||
mboxes:
|
||||
description:
|
||||
List of <&phandle type channel> - 2 channels for TXDB, 2 channels for RXDB
|
||||
(see mailbox/fsl,mu.txt)
|
||||
maxItems: 4
|
||||
|
||||
mbox-names:
|
||||
items:
|
||||
- const: txdb0
|
||||
- const: txdb1
|
||||
- const: rxdb0
|
||||
- const: rxdb1
|
||||
|
||||
memory-region:
|
||||
description:
|
||||
phandle to a node describing reserved memory (System RAM memory)
|
||||
used by DSP (see bindings/reserved-memory/reserved-memory.txt)
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- clock-names
|
||||
- power-domains
|
||||
- mboxes
|
||||
- mbox-names
|
||||
- memory-region
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/firmware/imx/rsrc.h>
|
||||
#include <dt-bindings/clock/imx8-clock.h>
|
||||
dsp@596e8000 {
|
||||
compatible = "fsl,imx8qxp-dsp";
|
||||
reg = <0x596e8000 0x88000>;
|
||||
clocks = <&adma_lpcg IMX_ADMA_LPCG_DSP_IPG_CLK>,
|
||||
<&adma_lpcg IMX_ADMA_LPCG_OCRAM_IPG_CLK>,
|
||||
<&adma_lpcg IMX_ADMA_LPCG_DSP_CORE_CLK>;
|
||||
clock-names = "ipg", "ocram", "core";
|
||||
power-domains = <&pd IMX_SC_R_MU_13A>,
|
||||
<&pd IMX_SC_R_MU_13B>,
|
||||
<&pd IMX_SC_R_DSP>,
|
||||
<&pd IMX_SC_R_DSP_RAM>;
|
||||
mbox-names = "txdb0", "txdb1", "rxdb0", "rxdb1";
|
||||
mboxes = <&lsio_mu13 2 0>, <&lsio_mu13 2 1>, <&lsio_mu13 3 0>, <&lsio_mu13 3 1>;
|
||||
};
|
|
@ -70,7 +70,9 @@ allOf:
|
|||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: allwinner,sun8i-h3-spdif
|
||||
enum:
|
||||
- allwinner,sun8i-h3-spdif
|
||||
- allwinner,sun50i-h6-spdif
|
||||
|
||||
then:
|
||||
properties:
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/sound/allwinner,sun50i-a64-codec-analog.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Allwinner A64 Analog Codec Device Tree Bindings
|
||||
|
||||
maintainers:
|
||||
- Chen-Yu Tsai <wens@csie.org>
|
||||
- Maxime Ripard <maxime.ripard@bootlin.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: allwinner,sun50i-a64-codec-analog
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
cpvdd-supply:
|
||||
description:
|
||||
Regulator for the headphone amplifier
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- cpvdd-supply
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
codec_analog: codec-analog@1f015c0 {
|
||||
compatible = "allwinner,sun50i-a64-codec-analog";
|
||||
reg = <0x01f015c0 0x4>;
|
||||
cpvdd-supply = <®_eldo1>;
|
||||
};
|
||||
|
||||
...
|
|
@ -0,0 +1,57 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/sound/allwinner,sun8i-a33-codec.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Allwinner A33 Codec Device Tree Bindings
|
||||
|
||||
maintainers:
|
||||
- Chen-Yu Tsai <wens@csie.org>
|
||||
- Maxime Ripard <maxime.ripard@bootlin.com>
|
||||
|
||||
properties:
|
||||
"#sound-dai-cells":
|
||||
const: 0
|
||||
|
||||
compatible:
|
||||
const: allwinner,sun8i-a33-codec
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: Bus Clock
|
||||
- description: Module Clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: bus
|
||||
- const: mod
|
||||
|
||||
required:
|
||||
- "#sound-dai-cells"
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
audio-codec@1c22e00 {
|
||||
#sound-dai-cells = <0>;
|
||||
compatible = "allwinner,sun8i-a33-codec";
|
||||
reg = <0x01c22e00 0x400>;
|
||||
interrupts = <0 29 4>;
|
||||
clocks = <&ccu 47>, <&ccu 92>;
|
||||
clock-names = "bus", "mod";
|
||||
};
|
||||
|
||||
...
|
|
@ -4,13 +4,18 @@ Required properties:
|
|||
- compatible: 'amlogic,axg-toddr' or
|
||||
'amlogic,axg-toddr' or
|
||||
'amlogic,g12a-frddr' or
|
||||
'amlogic,g12a-toddr'
|
||||
'amlogic,g12a-toddr' or
|
||||
'amlogic,sm1-frddr' or
|
||||
'amlogic,sm1-toddr'
|
||||
- reg: physical base address of the controller and length of memory
|
||||
mapped region.
|
||||
- interrupts: interrupt specifier for the fifo.
|
||||
- clocks: phandle to the fifo peripheral clock provided by the audio
|
||||
clock controller.
|
||||
- resets: phandle to memory ARB line provided by the arb reset controller.
|
||||
- resets: list of reset phandle, one for each entry reset-names.
|
||||
- reset-names: should contain the following:
|
||||
* "arb" : memory ARB line (required)
|
||||
* "rst" : dedicated device reset line (optional)
|
||||
- #sound-dai-cells: must be 0.
|
||||
|
||||
Example of FRDDR A on the A113 SoC:
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
Required properties:
|
||||
- compatible: 'amlogic,axg-pdm' or
|
||||
'amlogic,g12a-pdm'
|
||||
'amlogic,g12a-pdm' or
|
||||
'amlogic,sm1-pdm'
|
||||
- reg: physical base address of the controller and length of memory
|
||||
mapped region.
|
||||
- clocks: list of clock phandle, one for each entry clock-names.
|
||||
|
@ -12,6 +13,9 @@ Required properties:
|
|||
* "sysclk" : dsp system clock
|
||||
- #sound-dai-cells: must be 0.
|
||||
|
||||
Optional property:
|
||||
- resets: phandle to the dedicated reset line of the pdm input.
|
||||
|
||||
Example of PDM on the A113 SoC:
|
||||
|
||||
pdm: audio-controller@ff632000 {
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
Required properties:
|
||||
- compatible: 'amlogic,axg-spdifin' or
|
||||
'amlogic,g12a-spdifin'
|
||||
'amlogic,g12a-spdifin' or
|
||||
'amlogic,sm1-spdifin'
|
||||
- interrupts: interrupt specifier for the spdif input.
|
||||
- clocks: list of clock phandle, one for each entry clock-names.
|
||||
- clock-names: should contain the following:
|
||||
|
@ -10,6 +11,9 @@ Required properties:
|
|||
* "refclk" : spdif input reference clock
|
||||
- #sound-dai-cells: must be 0.
|
||||
|
||||
Optional property:
|
||||
- resets: phandle to the dedicated reset line of the spdif input.
|
||||
|
||||
Example on the A113 SoC:
|
||||
|
||||
spdifin: audio-controller@400 {
|
||||
|
|
|
@ -2,13 +2,17 @@
|
|||
|
||||
Required properties:
|
||||
- compatible: 'amlogic,axg-spdifout' or
|
||||
'amlogic,g12a-spdifout'
|
||||
'amlogic,g12a-spdifout' or
|
||||
'amlogic,sm1-spdifout'
|
||||
- clocks: list of clock phandle, one for each entry clock-names.
|
||||
- clock-names: should contain the following:
|
||||
* "pclk" : peripheral clock.
|
||||
* "mclk" : master clock
|
||||
- #sound-dai-cells: must be 0.
|
||||
|
||||
Optional property:
|
||||
- resets: phandle to the dedicated reset line of the spdif output.
|
||||
|
||||
Example on the A113 SoC:
|
||||
|
||||
spdifout: audio-controller@480 {
|
||||
|
|
|
@ -4,7 +4,9 @@ Required properties:
|
|||
- compatible: 'amlogic,axg-tdmin' or
|
||||
'amlogic,axg-tdmout' or
|
||||
'amlogic,g12a-tdmin' or
|
||||
'amlogic,g12a-tdmout'
|
||||
'amlogic,g12a-tdmout' or
|
||||
'amlogic,sm1-tdmin' or
|
||||
'amlogic,sm1-tdmout
|
||||
- reg: physical base address of the controller and length of memory
|
||||
mapped region.
|
||||
- clocks: list of clock phandle, one for each entry clock-names.
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
* Amlogic HDMI Tx control glue
|
||||
|
||||
Required properties:
|
||||
- compatible: "amlogic,g12a-tohdmitx"
|
||||
- compatible: "amlogic,g12a-tohdmitx" or
|
||||
"amlogic,sm1-tohdmitx"
|
||||
- reg: physical base address of the controller and length of memory
|
||||
mapped region.
|
||||
- #sound-dai-cells: should be 1.
|
||||
- resets: phandle to the dedicated reset line of the hdmitx glue.
|
||||
|
||||
Example on the S905X2 SoC:
|
||||
|
||||
|
@ -12,6 +14,7 @@ tohdmitx: audio-controller@744 {
|
|||
compatible = "amlogic,g12a-tohdmitx";
|
||||
reg = <0x0 0x744 0x0 0x4>;
|
||||
#sound-dai-cells = <1>;
|
||||
resets = <&clkc_audio AUD_RESET_TOHDMITX>;
|
||||
};
|
||||
|
||||
Example of an 'amlogic,axg-sound-card':
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
Everest ES8316 audio CODEC
|
||||
|
||||
This device supports both I2C and SPI.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : should be "everest,es8316"
|
||||
- reg : the I2C address of the device for I2C
|
||||
|
||||
Optional properties:
|
||||
|
||||
- clocks : a list of phandle, should contain entries for clock-names
|
||||
- clock-names : should include as follows:
|
||||
"mclk" : master clock (MCLK) of the device
|
||||
|
||||
Example:
|
||||
|
||||
es8316: codec@11 {
|
||||
compatible = "everest,es8316";
|
||||
reg = <0x11>;
|
||||
clocks = <&clks 10>;
|
||||
clock-names = "mclk";
|
||||
};
|
|
@ -7,8 +7,11 @@ other DSPs. It has up to six transmitters and four receivers.
|
|||
|
||||
Required properties:
|
||||
|
||||
- compatible : Compatible list, must contain "fsl,imx35-esai" or
|
||||
"fsl,vf610-esai"
|
||||
- compatible : Compatible list, should contain one of the following
|
||||
compatibles:
|
||||
"fsl,imx35-esai",
|
||||
"fsl,vf610-esai",
|
||||
"fsl,imx6ull-esai",
|
||||
|
||||
- reg : Offset and length of the register set for the device.
|
||||
|
||||
|
|
|
@ -8,7 +8,9 @@ codec/DSP interfaces.
|
|||
Required properties:
|
||||
|
||||
- compatible : Compatible list, contains "fsl,vf610-sai",
|
||||
"fsl,imx6sx-sai" or "fsl,imx6ul-sai"
|
||||
"fsl,imx6sx-sai", "fsl,imx6ul-sai",
|
||||
"fsl,imx7ulp-sai", "fsl,imx8mq-sai" or
|
||||
"fsl,imx8qm-sai".
|
||||
|
||||
- reg : Offset and length of the register set for the device.
|
||||
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
* Allwinner A64 Codec Analog Controls
|
||||
|
||||
Required properties:
|
||||
- compatible: must be one of the following compatibles:
|
||||
- "allwinner,sun50i-a64-codec-analog"
|
||||
- reg: must contain the registers location and length
|
||||
- cpvdd-supply: Regulator supply for the headphone amplifier
|
||||
|
||||
Example:
|
||||
codec_analog: codec-analog@1f015c0 {
|
||||
compatible = "allwinner,sun50i-a64-codec-analog";
|
||||
reg = <0x01f015c0 0x4>;
|
||||
cpvdd-supply = <®_eldo1>;
|
||||
};
|
|
@ -1,63 +0,0 @@
|
|||
Allwinner SUN8I audio codec
|
||||
------------------------------------
|
||||
|
||||
On Sun8i-A33 SoCs, the audio is separated in different parts:
|
||||
- A DAI driver. It uses the "sun4i-i2s" driver which is
|
||||
documented here:
|
||||
Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-i2s.yaml
|
||||
- An analog part of the codec which is handled as PRCM registers.
|
||||
See Documentation/devicetree/bindings/sound/sun8i-codec-analog.txt
|
||||
- An digital part of the codec which is documented in this current
|
||||
binding documentation.
|
||||
- And finally, an audio card which links all the above components.
|
||||
The simple-audio card will be used.
|
||||
See Documentation/devicetree/bindings/sound/simple-card.txt
|
||||
|
||||
This bindings documentation exposes Sun8i codec (digital part).
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "allwinner,sun8i-a33-codec"
|
||||
- reg: must contain the registers location and length
|
||||
- interrupts: must contain the codec interrupt
|
||||
- clocks: a list of phandle + clock-specifer pairs, one for each entry
|
||||
in clock-names.
|
||||
- clock-names: should contain followings:
|
||||
- "bus": the parent APB clock for this controller
|
||||
- "mod": the parent module clock
|
||||
|
||||
Here is an example to add a sound card and the codec binding on sun8i SoCs that
|
||||
are similar to A33 using simple-card:
|
||||
|
||||
sound {
|
||||
compatible = "simple-audio-card";
|
||||
simple-audio-card,name = "sun8i-a33-audio";
|
||||
simple-audio-card,format = "i2s";
|
||||
simple-audio-card,frame-master = <&link_codec>;
|
||||
simple-audio-card,bitclock-master = <&link_codec>;
|
||||
simple-audio-card,mclk-fs = <512>;
|
||||
simple-audio-card,aux-devs = <&codec_analog>;
|
||||
simple-audio-card,routing =
|
||||
"Left DAC", "Digital Left DAC",
|
||||
"Right DAC", "Digital Right DAC";
|
||||
|
||||
simple-audio-card,cpu {
|
||||
sound-dai = <&dai>;
|
||||
};
|
||||
|
||||
link_codec: simple-audio-card,codec {
|
||||
sound-dai = <&codec>;
|
||||
};
|
||||
|
||||
soc@1c00000 {
|
||||
[...]
|
||||
|
||||
audio-codec@1c22e00 {
|
||||
#sound-dai-cells = <0>;
|
||||
compatible = "allwinner,sun8i-a33-codec";
|
||||
reg = <0x01c22e00 0x400>;
|
||||
interrupts = <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&ccu CLK_BUS_CODEC>, <&ccu CLK_AC_DIG>;
|
||||
clock-names = "bus", "mod";
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
UDA1334 audio CODEC
|
||||
|
||||
This device uses simple GPIO pins for controlling codec settings.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "nxp,uda1334"
|
||||
- nxp,mute-gpios: a GPIO spec for the MUTE pin.
|
||||
- nxp,deemph-gpios: a GPIO spec for the De-emphasis pin
|
||||
|
||||
Example:
|
||||
|
||||
uda1334: audio-codec {
|
||||
compatible = "nxp,uda1334";
|
||||
nxp,mute-gpios = <&gpio1 8 GPIO_ACTIVE_LOW>;
|
||||
nxp,deemph-gpios = <&gpio3 3 GPIO_ACTIVE_LOW>;
|
||||
};
|
|
@ -1001,6 +1001,8 @@ position_fix
|
|||
2 = POSBUF: use position buffer,
|
||||
3 = VIACOMBO: VIA-specific workaround for capture,
|
||||
4 = COMBO: use LPIB for playback, auto for capture stream
|
||||
5 = SKL+: apply the delay calculation available on recent Intel chips
|
||||
6 = FIFO: correct the position with the fixed FIFO size, for recent AMD chips
|
||||
probe_mask
|
||||
Bitmask to probe codecs (default = -1, meaning all slots);
|
||||
When the bit 8 (0x100) is set, the lower 8 bits are used
|
||||
|
|
|
@ -260,6 +260,9 @@ alc295-hp-x360
|
|||
HP Spectre X360 fixups
|
||||
alc-sense-combo
|
||||
Headset button support for Chrome platform
|
||||
huawei-mbx-stereo
|
||||
Enable initialization verbs for Huawei MBX stereo speakers;
|
||||
might be risky, try this at your own risk
|
||||
|
||||
ALC66x/67x/892
|
||||
==============
|
||||
|
|
|
@ -66,6 +66,11 @@ by comparing both LPIB and position-buffer values.
|
|||
``position_fix=4`` is another combination available for all controllers,
|
||||
and uses LPIB for the playback and the position-buffer for the capture
|
||||
streams.
|
||||
``position_fix=5`` is specific to Intel platforms, so far, for Skylake
|
||||
and onward. It applies the delay calculation for the precise position
|
||||
reporting.
|
||||
``position_fix=6`` is to correct the position with the fixed FIFO
|
||||
size, mainly targeted for the recent AMD controllers.
|
||||
0 is the default value for all other
|
||||
controllers, the automatic check and fallback to LPIB as described in
|
||||
the above. If you get a problem of repeated sounds, this option might
|
||||
|
|
|
@ -6270,12 +6270,14 @@ S: Maintained
|
|||
F: drivers/hwmon/f75375s.c
|
||||
F: include/linux/f75375s.h
|
||||
|
||||
FIREWIRE AUDIO DRIVERS
|
||||
FIREWIRE AUDIO DRIVERS and IEC 61883-1/6 PACKET STREAMING ENGINE
|
||||
M: Clemens Ladisch <clemens@ladisch.de>
|
||||
M: Takashi Sakamoto <o-takashi@sakamocchi.jp>
|
||||
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
|
||||
S: Maintained
|
||||
F: sound/firewire/
|
||||
F: include/uapi/sound/firewire.h
|
||||
|
||||
FIREWIRE MEDIA DRIVERS (firedtv)
|
||||
M: Stefan Richter <stefanr@s5r6.in-berlin.de>
|
||||
|
|
|
@ -246,8 +246,8 @@ static struct platform_device latch2_gpio_device = {
|
|||
#define LATCH2_PIN_SCARD_CMDVCC 11
|
||||
#define LATCH2_PIN_MODEM_NRESET 12
|
||||
#define LATCH2_PIN_MODEM_CODEC 13
|
||||
#define LATCH2_PIN_AUDIO_MUTE 14
|
||||
#define LATCH2_PIN_HOOKFLASH 15
|
||||
#define LATCH2_PIN_HANDSFREE_MUTE 14
|
||||
#define LATCH2_PIN_HANDSET_MUTE 15
|
||||
|
||||
static struct regulator_consumer_supply modem_nreset_consumers[] = {
|
||||
REGULATOR_SUPPLY("RESET#", "serial8250.1"),
|
||||
|
@ -476,6 +476,10 @@ static struct gpiod_lookup_table ams_delta_audio_gpio_table = {
|
|||
"hook_switch", 0),
|
||||
GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_MODEM_CODEC,
|
||||
"modem_codec", 0),
|
||||
GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_HANDSFREE_MUTE,
|
||||
"handsfree_mute", 0),
|
||||
GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_HANDSET_MUTE,
|
||||
"handset_mute", 0),
|
||||
{ },
|
||||
},
|
||||
};
|
||||
|
@ -590,8 +594,6 @@ static int gpiochip_match_by_label(struct gpio_chip *chip, void *data)
|
|||
static struct gpiod_hog ams_delta_gpio_hogs[] = {
|
||||
GPIO_HOG(LATCH2_LABEL, LATCH2_PIN_KEYBRD_DATAOUT, "keybrd_dataout",
|
||||
GPIO_ACTIVE_HIGH, GPIOD_OUT_LOW),
|
||||
GPIO_HOG(LATCH2_LABEL, LATCH2_PIN_AUDIO_MUTE, "audio_mute",
|
||||
GPIO_ACTIVE_HIGH, GPIOD_OUT_LOW),
|
||||
{},
|
||||
};
|
||||
|
||||
|
|
|
@ -1951,6 +1951,8 @@
|
|||
#define PCI_VENDOR_ID_DIGIGRAM 0x1369
|
||||
#define PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_SERIAL_SUBSYSTEM 0xc001
|
||||
#define PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_CAE_SERIAL_SUBSYSTEM 0xc002
|
||||
#define PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ESE_SERIAL_SUBSYSTEM 0xc021
|
||||
#define PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ESE_CAE_SERIAL_SUBSYSTEM 0xc022
|
||||
|
||||
#define PCI_VENDOR_ID_KAWASAKI 0x136b
|
||||
#define PCI_DEVICE_ID_MCHIP_KL5A72002 0xff01
|
||||
|
|
|
@ -59,6 +59,7 @@ struct hda_bus {
|
|||
unsigned int in_reset:1; /* during reset operation */
|
||||
unsigned int no_response_fallback:1; /* don't fallback at RIRB error */
|
||||
unsigned int bus_probing :1; /* during probing process */
|
||||
unsigned int keep_power:1; /* keep power up for notification */
|
||||
|
||||
int primary_dig_out_type; /* primary digital out PCM type */
|
||||
unsigned int mixer_assigned; /* codec addr for mixer name */
|
||||
|
|
|
@ -122,10 +122,6 @@ int snd_hdac_codec_modalias(struct hdac_device *hdac, char *buf, size_t size);
|
|||
|
||||
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);
|
||||
int snd_hdac_exec_verb(struct hdac_device *codec, unsigned int cmd,
|
||||
unsigned int flags, unsigned int *res);
|
||||
int snd_hdac_read(struct hdac_device *codec, hda_nid_t nid,
|
||||
unsigned int verb, unsigned int parm, unsigned int *res);
|
||||
int _snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm,
|
||||
|
@ -253,24 +249,6 @@ struct hdac_ext_bus_ops {
|
|||
int (*hdev_detach)(struct hdac_device *hdev);
|
||||
};
|
||||
|
||||
/*
|
||||
* Lowlevel I/O operators
|
||||
*/
|
||||
struct hdac_io_ops {
|
||||
/* mapped register accesses */
|
||||
void (*reg_writel)(u32 value, u32 __iomem *addr);
|
||||
u32 (*reg_readl)(u32 __iomem *addr);
|
||||
void (*reg_writew)(u16 value, u16 __iomem *addr);
|
||||
u16 (*reg_readw)(u16 __iomem *addr);
|
||||
void (*reg_writeb)(u8 value, u8 __iomem *addr);
|
||||
u8 (*reg_readb)(u8 __iomem *addr);
|
||||
/* Allocation ops */
|
||||
int (*dma_alloc_pages)(struct hdac_bus *bus, int type, size_t size,
|
||||
struct snd_dma_buffer *buf);
|
||||
void (*dma_free_pages)(struct hdac_bus *bus,
|
||||
struct snd_dma_buffer *buf);
|
||||
};
|
||||
|
||||
#define HDA_UNSOL_QUEUE_SIZE 64
|
||||
#define HDA_MAX_CODECS 8 /* limit by controller side */
|
||||
|
||||
|
@ -304,7 +282,6 @@ struct hdac_rb {
|
|||
struct hdac_bus {
|
||||
struct device *dev;
|
||||
const struct hdac_bus_ops *ops;
|
||||
const struct hdac_io_ops *io_ops;
|
||||
const struct hdac_ext_bus_ops *ext_ops;
|
||||
|
||||
/* h/w resources */
|
||||
|
@ -344,6 +321,7 @@ struct hdac_bus {
|
|||
/* CORB/RIRB and position buffers */
|
||||
struct snd_dma_buffer rb;
|
||||
struct snd_dma_buffer posbuf;
|
||||
int dma_type; /* SNDRV_DMA_TYPE_XXX for CORB/RIRB */
|
||||
|
||||
/* hdac_stream linked list */
|
||||
struct list_head stream_list;
|
||||
|
@ -384,8 +362,7 @@ struct hdac_bus {
|
|||
};
|
||||
|
||||
int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
|
||||
const struct hdac_bus_ops *ops,
|
||||
const struct hdac_io_ops *io_ops);
|
||||
const struct hdac_bus_ops *ops);
|
||||
void snd_hdac_bus_exit(struct hdac_bus *bus);
|
||||
int snd_hdac_bus_exec_verb(struct hdac_bus *bus, unsigned int addr,
|
||||
unsigned int cmd, unsigned int *res);
|
||||
|
@ -393,11 +370,6 @@ int snd_hdac_bus_exec_verb_unlocked(struct hdac_bus *bus, unsigned int addr,
|
|||
unsigned int cmd, unsigned int *res);
|
||||
void snd_hdac_bus_queue_event(struct hdac_bus *bus, u32 res, u32 res_ex);
|
||||
|
||||
int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec);
|
||||
void snd_hdac_bus_remove_device(struct hdac_bus *bus,
|
||||
struct hdac_device *codec);
|
||||
void snd_hdac_bus_process_unsol_events(struct work_struct *work);
|
||||
|
||||
static inline void snd_hdac_codec_link_up(struct hdac_device *codec)
|
||||
{
|
||||
set_bit(codec->addr, &codec->bus->codec_powered);
|
||||
|
@ -429,21 +401,38 @@ int snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status,
|
|||
int snd_hdac_bus_alloc_stream_pages(struct hdac_bus *bus);
|
||||
void snd_hdac_bus_free_stream_pages(struct hdac_bus *bus);
|
||||
|
||||
#ifdef CONFIG_SND_HDA_ALIGNED_MMIO
|
||||
unsigned int snd_hdac_aligned_read(void __iomem *addr, unsigned int mask);
|
||||
void snd_hdac_aligned_write(unsigned int val, void __iomem *addr,
|
||||
unsigned int mask);
|
||||
#define snd_hdac_reg_writeb(v, addr) snd_hdac_aligned_write(v, addr, 0xff)
|
||||
#define snd_hdac_reg_writew(v, addr) snd_hdac_aligned_write(v, addr, 0xffff)
|
||||
#define snd_hdac_reg_readb(addr) snd_hdac_aligned_read(addr, 0xff)
|
||||
#define snd_hdac_reg_readw(addr) snd_hdac_aligned_read(addr, 0xffff)
|
||||
#else /* CONFIG_SND_HDA_ALIGNED_MMIO */
|
||||
#define snd_hdac_reg_writeb(val, addr) writeb(val, addr)
|
||||
#define snd_hdac_reg_writew(val, addr) writew(val, addr)
|
||||
#define snd_hdac_reg_readb(addr) readb(addr)
|
||||
#define snd_hdac_reg_readw(addr) readw(addr)
|
||||
#endif /* CONFIG_SND_HDA_ALIGNED_MMIO */
|
||||
#define snd_hdac_reg_writel(val, addr) writel(val, addr)
|
||||
#define snd_hdac_reg_readl(addr) readl(addr)
|
||||
|
||||
/*
|
||||
* macros for easy use
|
||||
*/
|
||||
#define _snd_hdac_chip_writeb(chip, reg, value) \
|
||||
((chip)->io_ops->reg_writeb(value, (chip)->remap_addr + (reg)))
|
||||
snd_hdac_reg_writeb(value, (chip)->remap_addr + (reg))
|
||||
#define _snd_hdac_chip_readb(chip, reg) \
|
||||
((chip)->io_ops->reg_readb((chip)->remap_addr + (reg)))
|
||||
snd_hdac_reg_readb((chip)->remap_addr + (reg))
|
||||
#define _snd_hdac_chip_writew(chip, reg, value) \
|
||||
((chip)->io_ops->reg_writew(value, (chip)->remap_addr + (reg)))
|
||||
snd_hdac_reg_writew(value, (chip)->remap_addr + (reg))
|
||||
#define _snd_hdac_chip_readw(chip, reg) \
|
||||
((chip)->io_ops->reg_readw((chip)->remap_addr + (reg)))
|
||||
snd_hdac_reg_readw((chip)->remap_addr + (reg))
|
||||
#define _snd_hdac_chip_writel(chip, reg, value) \
|
||||
((chip)->io_ops->reg_writel(value, (chip)->remap_addr + (reg)))
|
||||
snd_hdac_reg_writel(value, (chip)->remap_addr + (reg))
|
||||
#define _snd_hdac_chip_readl(chip, reg) \
|
||||
((chip)->io_ops->reg_readl((chip)->remap_addr + (reg)))
|
||||
snd_hdac_reg_readl((chip)->remap_addr + (reg))
|
||||
|
||||
/* read/write a register, pass without AZX_REG_ prefix */
|
||||
#define snd_hdac_chip_writel(chip, reg, value) \
|
||||
|
@ -548,24 +537,19 @@ int snd_hdac_get_stream_stripe_ctl(struct hdac_bus *bus,
|
|||
/*
|
||||
* macros for easy use
|
||||
*/
|
||||
#define _snd_hdac_stream_write(type, dev, reg, value) \
|
||||
((dev)->bus->io_ops->reg_write ## type(value, (dev)->sd_addr + (reg)))
|
||||
#define _snd_hdac_stream_read(type, dev, reg) \
|
||||
((dev)->bus->io_ops->reg_read ## type((dev)->sd_addr + (reg)))
|
||||
|
||||
/* read/write a register, pass without AZX_REG_ prefix */
|
||||
#define snd_hdac_stream_writel(dev, reg, value) \
|
||||
_snd_hdac_stream_write(l, dev, AZX_REG_ ## reg, value)
|
||||
snd_hdac_reg_writel(value, (dev)->sd_addr + AZX_REG_ ## reg)
|
||||
#define snd_hdac_stream_writew(dev, reg, value) \
|
||||
_snd_hdac_stream_write(w, dev, AZX_REG_ ## reg, value)
|
||||
snd_hdac_reg_writew(value, (dev)->sd_addr + AZX_REG_ ## reg)
|
||||
#define snd_hdac_stream_writeb(dev, reg, value) \
|
||||
_snd_hdac_stream_write(b, dev, AZX_REG_ ## reg, value)
|
||||
snd_hdac_reg_writeb(value, (dev)->sd_addr + AZX_REG_ ## reg)
|
||||
#define snd_hdac_stream_readl(dev, reg) \
|
||||
_snd_hdac_stream_read(l, dev, AZX_REG_ ## reg)
|
||||
snd_hdac_reg_readl((dev)->sd_addr + AZX_REG_ ## reg)
|
||||
#define snd_hdac_stream_readw(dev, reg) \
|
||||
_snd_hdac_stream_read(w, dev, AZX_REG_ ## reg)
|
||||
snd_hdac_reg_readw((dev)->sd_addr + AZX_REG_ ## reg)
|
||||
#define snd_hdac_stream_readb(dev, reg) \
|
||||
_snd_hdac_stream_read(b, dev, AZX_REG_ ## reg)
|
||||
snd_hdac_reg_readb((dev)->sd_addr + AZX_REG_ ## reg)
|
||||
|
||||
/* update a register, pass without AZX_REG_ prefix */
|
||||
#define snd_hdac_stream_updatel(dev, reg, mask, val) \
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev,
|
||||
const struct hdac_bus_ops *ops,
|
||||
const struct hdac_io_ops *io_ops,
|
||||
const struct hdac_ext_bus_ops *ext_ops);
|
||||
|
||||
void snd_hdac_ext_bus_exit(struct hdac_bus *bus);
|
||||
|
|
|
@ -47,6 +47,9 @@ struct hdmi_codec_params {
|
|||
int channels;
|
||||
};
|
||||
|
||||
typedef void (*hdmi_codec_plugged_cb)(struct device *dev,
|
||||
bool plugged);
|
||||
|
||||
struct hdmi_codec_pdata;
|
||||
struct hdmi_codec_ops {
|
||||
/*
|
||||
|
@ -88,6 +91,14 @@ struct hdmi_codec_ops {
|
|||
*/
|
||||
int (*get_dai_id)(struct snd_soc_component *comment,
|
||||
struct device_node *endpoint);
|
||||
|
||||
/*
|
||||
* Hook callback function to handle connector plug event.
|
||||
* Optional
|
||||
*/
|
||||
int (*hook_plugged_cb)(struct device *dev, void *data,
|
||||
hdmi_codec_plugged_cb fn,
|
||||
struct device *codec_dev);
|
||||
};
|
||||
|
||||
/* HDMI codec initalization data */
|
||||
|
@ -99,6 +110,12 @@ struct hdmi_codec_pdata {
|
|||
void *data;
|
||||
};
|
||||
|
||||
struct snd_soc_component;
|
||||
struct snd_soc_jack;
|
||||
|
||||
int hdmi_codec_set_jack_detect(struct snd_soc_component *component,
|
||||
struct snd_soc_jack *jack);
|
||||
|
||||
#define HDMI_CODEC_DRV_NAME "hdmi-audio-codec"
|
||||
|
||||
#endif /* __HDMI_CODEC_H__ */
|
||||
|
|
|
@ -1,18 +1,17 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* skl-nhlt.h - Intel HDA Platform NHLT header
|
||||
* intel-nhlt.h - Intel HDA Platform NHLT header
|
||||
*
|
||||
* Copyright (C) 2015 Intel Corp
|
||||
* Author: Sanjiv Kumar <sanjiv.kumar@intel.com>
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
* Copyright (c) 2015-2019 Intel Corporation
|
||||
*/
|
||||
#ifndef __SKL_NHLT_H__
|
||||
#define __SKL_NHLT_H__
|
||||
|
||||
#ifndef __INTEL_NHLT_H__
|
||||
#define __INTEL_NHLT_H__
|
||||
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#if IS_ENABLED(CONFIG_ACPI) && IS_ENABLED(CONFIG_SND_INTEL_NHLT)
|
||||
|
||||
struct wav_fmt {
|
||||
u16 fmt_tag;
|
||||
u16 channels;
|
||||
|
@ -97,16 +96,22 @@ struct nhlt_resource_desc {
|
|||
#define MIC_ARRAY_2CH 2
|
||||
#define MIC_ARRAY_4CH 4
|
||||
|
||||
struct nhlt_tdm_config {
|
||||
struct nhlt_device_specific_config {
|
||||
u8 virtual_slot;
|
||||
u8 config_type;
|
||||
} __packed;
|
||||
|
||||
struct nhlt_dmic_array_config {
|
||||
struct nhlt_tdm_config tdm_config;
|
||||
struct nhlt_device_specific_config device_config;
|
||||
u8 array_type;
|
||||
} __packed;
|
||||
|
||||
struct nhlt_vendor_dmic_array_config {
|
||||
struct nhlt_dmic_array_config dmic_config;
|
||||
u8 nb_mics;
|
||||
/* TODO add vendor mic config */
|
||||
} __packed;
|
||||
|
||||
enum {
|
||||
NHLT_MIC_ARRAY_2CH_SMALL = 0xa,
|
||||
NHLT_MIC_ARRAY_2CH_BIG = 0xb,
|
||||
|
@ -116,4 +121,30 @@ enum {
|
|||
NHLT_MIC_ARRAY_VENDOR_DEFINED = 0xf,
|
||||
};
|
||||
|
||||
struct nhlt_acpi_table *intel_nhlt_init(struct device *dev);
|
||||
|
||||
void intel_nhlt_free(struct nhlt_acpi_table *addr);
|
||||
|
||||
int intel_nhlt_get_dmic_geo(struct device *dev, struct nhlt_acpi_table *nhlt);
|
||||
|
||||
#else
|
||||
|
||||
struct nhlt_acpi_table;
|
||||
|
||||
static inline struct nhlt_acpi_table *intel_nhlt_init(struct device *dev)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void intel_nhlt_free(struct nhlt_acpi_table *addr)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int intel_nhlt_get_dmic_geo(struct device *dev,
|
||||
struct nhlt_acpi_table *nhlt)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -117,6 +117,8 @@ struct snd_pcm_ops {
|
|||
#define SNDRV_PCM_RATE_96000 (1<<10) /* 96000Hz */
|
||||
#define SNDRV_PCM_RATE_176400 (1<<11) /* 176400Hz */
|
||||
#define SNDRV_PCM_RATE_192000 (1<<12) /* 192000Hz */
|
||||
#define SNDRV_PCM_RATE_352800 (1<<13) /* 352800Hz */
|
||||
#define SNDRV_PCM_RATE_384000 (1<<14) /* 384000Hz */
|
||||
|
||||
#define SNDRV_PCM_RATE_CONTINUOUS (1<<30) /* continuous range */
|
||||
#define SNDRV_PCM_RATE_KNOT (1<<31) /* supports more non-continuos rates */
|
||||
|
@ -129,6 +131,9 @@ struct snd_pcm_ops {
|
|||
SNDRV_PCM_RATE_88200|SNDRV_PCM_RATE_96000)
|
||||
#define SNDRV_PCM_RATE_8000_192000 (SNDRV_PCM_RATE_8000_96000|SNDRV_PCM_RATE_176400|\
|
||||
SNDRV_PCM_RATE_192000)
|
||||
#define SNDRV_PCM_RATE_8000_384000 (SNDRV_PCM_RATE_8000_192000|\
|
||||
SNDRV_PCM_RATE_352800|\
|
||||
SNDRV_PCM_RATE_384000)
|
||||
#define _SNDRV_PCM_FMTBIT(fmt) (1ULL << (__force int)SNDRV_PCM_FORMAT_##fmt)
|
||||
#define SNDRV_PCM_FMTBIT_S8 _SNDRV_PCM_FMTBIT(S8)
|
||||
#define SNDRV_PCM_FMTBIT_U8 _SNDRV_PCM_FMTBIT(U8)
|
||||
|
|
|
@ -25,6 +25,8 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[];
|
|||
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[];
|
||||
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[];
|
||||
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_machines[];
|
||||
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_machines[];
|
||||
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_ehl_machines[];
|
||||
|
||||
/*
|
||||
* generic table used for HDA codec-based platforms, possibly with
|
||||
|
|
|
@ -0,0 +1,387 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* soc-component.h
|
||||
*
|
||||
* Copyright (c) 2019 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#ifndef __SOC_COMPONENT_H
|
||||
#define __SOC_COMPONENT_H
|
||||
|
||||
#include <sound/soc.h>
|
||||
|
||||
/*
|
||||
* Component probe and remove ordering levels for components with runtime
|
||||
* dependencies.
|
||||
*/
|
||||
#define SND_SOC_COMP_ORDER_FIRST -2
|
||||
#define SND_SOC_COMP_ORDER_EARLY -1
|
||||
#define SND_SOC_COMP_ORDER_NORMAL 0
|
||||
#define SND_SOC_COMP_ORDER_LATE 1
|
||||
#define SND_SOC_COMP_ORDER_LAST 2
|
||||
|
||||
#define for_each_comp_order(order) \
|
||||
for (order = SND_SOC_COMP_ORDER_FIRST; \
|
||||
order <= SND_SOC_COMP_ORDER_LAST; \
|
||||
order++)
|
||||
|
||||
/* component interface */
|
||||
struct snd_soc_component_driver {
|
||||
const char *name;
|
||||
|
||||
/* Default control and setup, added after probe() is run */
|
||||
const struct snd_kcontrol_new *controls;
|
||||
unsigned int num_controls;
|
||||
const struct snd_soc_dapm_widget *dapm_widgets;
|
||||
unsigned int num_dapm_widgets;
|
||||
const struct snd_soc_dapm_route *dapm_routes;
|
||||
unsigned int num_dapm_routes;
|
||||
|
||||
int (*probe)(struct snd_soc_component *component);
|
||||
void (*remove)(struct snd_soc_component *component);
|
||||
int (*suspend)(struct snd_soc_component *component);
|
||||
int (*resume)(struct snd_soc_component *component);
|
||||
|
||||
unsigned int (*read)(struct snd_soc_component *component,
|
||||
unsigned int reg);
|
||||
int (*write)(struct snd_soc_component *component,
|
||||
unsigned int reg, unsigned int val);
|
||||
|
||||
/* pcm creation and destruction */
|
||||
int (*pcm_new)(struct snd_soc_pcm_runtime *rtd);
|
||||
void (*pcm_free)(struct snd_pcm *pcm);
|
||||
|
||||
/* component wide operations */
|
||||
int (*set_sysclk)(struct snd_soc_component *component,
|
||||
int clk_id, int source, unsigned int freq, int dir);
|
||||
int (*set_pll)(struct snd_soc_component *component, int pll_id,
|
||||
int source, unsigned int freq_in, unsigned int freq_out);
|
||||
int (*set_jack)(struct snd_soc_component *component,
|
||||
struct snd_soc_jack *jack, void *data);
|
||||
|
||||
/* DT */
|
||||
int (*of_xlate_dai_name)(struct snd_soc_component *component,
|
||||
struct of_phandle_args *args,
|
||||
const char **dai_name);
|
||||
int (*of_xlate_dai_id)(struct snd_soc_component *comment,
|
||||
struct device_node *endpoint);
|
||||
void (*seq_notifier)(struct snd_soc_component *component,
|
||||
enum snd_soc_dapm_type type, int subseq);
|
||||
int (*stream_event)(struct snd_soc_component *component, int event);
|
||||
int (*set_bias_level)(struct snd_soc_component *component,
|
||||
enum snd_soc_bias_level level);
|
||||
|
||||
const struct snd_pcm_ops *ops;
|
||||
const struct snd_compr_ops *compr_ops;
|
||||
|
||||
/* probe ordering - for components with runtime dependencies */
|
||||
int probe_order;
|
||||
int remove_order;
|
||||
|
||||
/*
|
||||
* signal if the module handling the component should not be removed
|
||||
* if a pcm is open. Setting this would prevent the module
|
||||
* refcount being incremented in probe() but allow it be incremented
|
||||
* when a pcm is opened and decremented when it is closed.
|
||||
*/
|
||||
unsigned int module_get_upon_open:1;
|
||||
|
||||
/* bits */
|
||||
unsigned int idle_bias_on:1;
|
||||
unsigned int suspend_bias_off:1;
|
||||
unsigned int use_pmdown_time:1; /* care pmdown_time at stop */
|
||||
unsigned int endianness:1;
|
||||
unsigned int non_legacy_dai_naming:1;
|
||||
|
||||
/* this component uses topology and ignore machine driver FEs */
|
||||
const char *ignore_machine;
|
||||
const char *topology_name_prefix;
|
||||
int (*be_hw_params_fixup)(struct snd_soc_pcm_runtime *rtd,
|
||||
struct snd_pcm_hw_params *params);
|
||||
bool use_dai_pcm_id; /* use DAI link PCM ID as PCM device number */
|
||||
int be_pcm_base; /* base device ID for all BE PCMs */
|
||||
};
|
||||
|
||||
struct snd_soc_component {
|
||||
const char *name;
|
||||
int id;
|
||||
const char *name_prefix;
|
||||
struct device *dev;
|
||||
struct snd_soc_card *card;
|
||||
|
||||
unsigned int active;
|
||||
|
||||
unsigned int suspended:1; /* is in suspend PM state */
|
||||
|
||||
struct list_head list;
|
||||
struct list_head card_aux_list; /* for auxiliary bound components */
|
||||
struct list_head card_list;
|
||||
|
||||
const struct snd_soc_component_driver *driver;
|
||||
|
||||
struct list_head dai_list;
|
||||
int num_dai;
|
||||
|
||||
struct regmap *regmap;
|
||||
int val_bytes;
|
||||
|
||||
struct mutex io_mutex;
|
||||
|
||||
/* attached dynamic objects */
|
||||
struct list_head dobj_list;
|
||||
|
||||
/*
|
||||
* DO NOT use any of the fields below in drivers, they are temporary and
|
||||
* are going to be removed again soon. If you use them in driver code
|
||||
* the driver will be marked as BROKEN when these fields are removed.
|
||||
*/
|
||||
|
||||
/* Don't use these, use snd_soc_component_get_dapm() */
|
||||
struct snd_soc_dapm_context dapm;
|
||||
|
||||
/* machine specific init */
|
||||
int (*init)(struct snd_soc_component *component);
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *debugfs_root;
|
||||
const char *debugfs_prefix;
|
||||
#endif
|
||||
};
|
||||
|
||||
#define for_each_component_dais(component, dai)\
|
||||
list_for_each_entry(dai, &(component)->dai_list, list)
|
||||
#define for_each_component_dais_safe(component, dai, _dai)\
|
||||
list_for_each_entry_safe(dai, _dai, &(component)->dai_list, list)
|
||||
|
||||
/**
|
||||
* snd_soc_dapm_to_component() - Casts a DAPM context to the component it is
|
||||
* embedded in
|
||||
* @dapm: The DAPM context to cast to the component
|
||||
*
|
||||
* This function must only be used on DAPM contexts that are known to be part of
|
||||
* a component (e.g. in a component driver). Otherwise the behavior is
|
||||
* undefined.
|
||||
*/
|
||||
static inline struct snd_soc_component *snd_soc_dapm_to_component(
|
||||
struct snd_soc_dapm_context *dapm)
|
||||
{
|
||||
return container_of(dapm, struct snd_soc_component, dapm);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_soc_component_get_dapm() - Returns the DAPM context associated with a
|
||||
* component
|
||||
* @component: The component for which to get the DAPM context
|
||||
*/
|
||||
static inline struct snd_soc_dapm_context *snd_soc_component_get_dapm(
|
||||
struct snd_soc_component *component)
|
||||
{
|
||||
return &component->dapm;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_soc_component_init_bias_level() - Initialize COMPONENT DAPM bias level
|
||||
* @component: The COMPONENT for which to initialize the DAPM bias level
|
||||
* @level: The DAPM level to initialize to
|
||||
*
|
||||
* Initializes the COMPONENT DAPM bias level. See snd_soc_dapm_init_bias_level()
|
||||
*/
|
||||
static inline void
|
||||
snd_soc_component_init_bias_level(struct snd_soc_component *component,
|
||||
enum snd_soc_bias_level level)
|
||||
{
|
||||
snd_soc_dapm_init_bias_level(
|
||||
snd_soc_component_get_dapm(component), level);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_soc_component_get_bias_level() - Get current COMPONENT DAPM bias level
|
||||
* @component: The COMPONENT for which to get the DAPM bias level
|
||||
*
|
||||
* Returns: The current DAPM bias level of the COMPONENT.
|
||||
*/
|
||||
static inline enum snd_soc_bias_level
|
||||
snd_soc_component_get_bias_level(struct snd_soc_component *component)
|
||||
{
|
||||
return snd_soc_dapm_get_bias_level(
|
||||
snd_soc_component_get_dapm(component));
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_soc_component_force_bias_level() - Set the COMPONENT DAPM bias level
|
||||
* @component: The COMPONENT for which to set the level
|
||||
* @level: The level to set to
|
||||
*
|
||||
* Forces the COMPONENT bias level to a specific state. See
|
||||
* snd_soc_dapm_force_bias_level().
|
||||
*/
|
||||
static inline int
|
||||
snd_soc_component_force_bias_level(struct snd_soc_component *component,
|
||||
enum snd_soc_bias_level level)
|
||||
{
|
||||
return snd_soc_dapm_force_bias_level(
|
||||
snd_soc_component_get_dapm(component),
|
||||
level);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_soc_dapm_kcontrol_component() - Returns the component associated to a
|
||||
* kcontrol
|
||||
* @kcontrol: The kcontrol
|
||||
*
|
||||
* This function must only be used on DAPM contexts that are known to be part of
|
||||
* a COMPONENT (e.g. in a COMPONENT driver). Otherwise the behavior is undefined
|
||||
*/
|
||||
static inline struct snd_soc_component *snd_soc_dapm_kcontrol_component(
|
||||
struct snd_kcontrol *kcontrol)
|
||||
{
|
||||
return snd_soc_dapm_to_component(snd_soc_dapm_kcontrol_dapm(kcontrol));
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_soc_component_cache_sync() - Sync the register cache with the hardware
|
||||
* @component: COMPONENT to sync
|
||||
*
|
||||
* Note: This function will call regcache_sync()
|
||||
*/
|
||||
static inline int snd_soc_component_cache_sync(
|
||||
struct snd_soc_component *component)
|
||||
{
|
||||
return regcache_sync(component->regmap);
|
||||
}
|
||||
|
||||
/* component IO */
|
||||
int snd_soc_component_read(struct snd_soc_component *component,
|
||||
unsigned int reg, unsigned int *val);
|
||||
unsigned int snd_soc_component_read32(struct snd_soc_component *component,
|
||||
unsigned int reg);
|
||||
int snd_soc_component_write(struct snd_soc_component *component,
|
||||
unsigned int reg, unsigned int val);
|
||||
int snd_soc_component_update_bits(struct snd_soc_component *component,
|
||||
unsigned int reg, unsigned int mask,
|
||||
unsigned int val);
|
||||
int snd_soc_component_update_bits_async(struct snd_soc_component *component,
|
||||
unsigned int reg, unsigned int mask,
|
||||
unsigned int val);
|
||||
void snd_soc_component_async_complete(struct snd_soc_component *component);
|
||||
int snd_soc_component_test_bits(struct snd_soc_component *component,
|
||||
unsigned int reg, unsigned int mask,
|
||||
unsigned int value);
|
||||
|
||||
/* component wide operations */
|
||||
int snd_soc_component_set_sysclk(struct snd_soc_component *component,
|
||||
int clk_id, int source,
|
||||
unsigned int freq, int dir);
|
||||
int snd_soc_component_set_pll(struct snd_soc_component *component, int pll_id,
|
||||
int source, unsigned int freq_in,
|
||||
unsigned int freq_out);
|
||||
int snd_soc_component_set_jack(struct snd_soc_component *component,
|
||||
struct snd_soc_jack *jack, void *data);
|
||||
|
||||
void snd_soc_component_seq_notifier(struct snd_soc_component *component,
|
||||
enum snd_soc_dapm_type type, int subseq);
|
||||
int snd_soc_component_stream_event(struct snd_soc_component *component,
|
||||
int event);
|
||||
int snd_soc_component_set_bias_level(struct snd_soc_component *component,
|
||||
enum snd_soc_bias_level level);
|
||||
|
||||
#ifdef CONFIG_REGMAP
|
||||
void snd_soc_component_init_regmap(struct snd_soc_component *component,
|
||||
struct regmap *regmap);
|
||||
void snd_soc_component_exit_regmap(struct snd_soc_component *component);
|
||||
#endif
|
||||
|
||||
#define snd_soc_component_module_get_when_probe(component)\
|
||||
snd_soc_component_module_get(component, 0)
|
||||
#define snd_soc_component_module_get_when_open(component) \
|
||||
snd_soc_component_module_get(component, 1)
|
||||
int snd_soc_component_module_get(struct snd_soc_component *component,
|
||||
int upon_open);
|
||||
#define snd_soc_component_module_put_when_remove(component) \
|
||||
snd_soc_component_module_put(component, 0)
|
||||
#define snd_soc_component_module_put_when_close(component) \
|
||||
snd_soc_component_module_put(component, 1)
|
||||
void snd_soc_component_module_put(struct snd_soc_component *component,
|
||||
int upon_open);
|
||||
|
||||
static inline void snd_soc_component_set_drvdata(struct snd_soc_component *c,
|
||||
void *data)
|
||||
{
|
||||
dev_set_drvdata(c->dev, data);
|
||||
}
|
||||
|
||||
static inline void *snd_soc_component_get_drvdata(struct snd_soc_component *c)
|
||||
{
|
||||
return dev_get_drvdata(c->dev);
|
||||
}
|
||||
|
||||
static inline bool snd_soc_component_is_active(
|
||||
struct snd_soc_component *component)
|
||||
{
|
||||
return component->active != 0;
|
||||
}
|
||||
|
||||
/* component pin */
|
||||
int snd_soc_component_enable_pin(struct snd_soc_component *component,
|
||||
const char *pin);
|
||||
int snd_soc_component_enable_pin_unlocked(struct snd_soc_component *component,
|
||||
const char *pin);
|
||||
int snd_soc_component_disable_pin(struct snd_soc_component *component,
|
||||
const char *pin);
|
||||
int snd_soc_component_disable_pin_unlocked(struct snd_soc_component *component,
|
||||
const char *pin);
|
||||
int snd_soc_component_nc_pin(struct snd_soc_component *component,
|
||||
const char *pin);
|
||||
int snd_soc_component_nc_pin_unlocked(struct snd_soc_component *component,
|
||||
const char *pin);
|
||||
int snd_soc_component_get_pin_status(struct snd_soc_component *component,
|
||||
const char *pin);
|
||||
int snd_soc_component_force_enable_pin(struct snd_soc_component *component,
|
||||
const char *pin);
|
||||
int snd_soc_component_force_enable_pin_unlocked(
|
||||
struct snd_soc_component *component,
|
||||
const char *pin);
|
||||
|
||||
/* component driver ops */
|
||||
int snd_soc_component_open(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream);
|
||||
int snd_soc_component_close(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream);
|
||||
int snd_soc_component_prepare(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream);
|
||||
int snd_soc_component_hw_params(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params);
|
||||
int snd_soc_component_hw_free(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream);
|
||||
int snd_soc_component_trigger(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
int cmd);
|
||||
void snd_soc_component_suspend(struct snd_soc_component *component);
|
||||
void snd_soc_component_resume(struct snd_soc_component *component);
|
||||
int snd_soc_component_is_suspended(struct snd_soc_component *component);
|
||||
int snd_soc_component_probe(struct snd_soc_component *component);
|
||||
void snd_soc_component_remove(struct snd_soc_component *component);
|
||||
int snd_soc_component_of_xlate_dai_id(struct snd_soc_component *component,
|
||||
struct device_node *ep);
|
||||
int snd_soc_component_of_xlate_dai_name(struct snd_soc_component *component,
|
||||
struct of_phandle_args *args,
|
||||
const char **dai_name);
|
||||
|
||||
int snd_soc_pcm_component_pointer(struct snd_pcm_substream *substream);
|
||||
int snd_soc_pcm_component_ioctl(struct snd_pcm_substream *substream,
|
||||
unsigned int cmd, void *arg);
|
||||
int snd_soc_pcm_component_copy_user(struct snd_pcm_substream *substream,
|
||||
int channel, unsigned long pos,
|
||||
void __user *buf, unsigned long bytes);
|
||||
struct page *snd_soc_pcm_component_page(struct snd_pcm_substream *substream,
|
||||
unsigned long offset);
|
||||
int snd_soc_pcm_component_mmap(struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *vma);
|
||||
int snd_soc_pcm_component_new(struct snd_pcm *pcm);
|
||||
void snd_soc_pcm_component_free(struct snd_pcm *pcm);
|
||||
|
||||
#endif /* __SOC_COMPONENT_H */
|
|
@ -145,6 +145,31 @@ int snd_soc_dai_get_channel_map(struct snd_soc_dai *dai,
|
|||
|
||||
int snd_soc_dai_is_dummy(struct snd_soc_dai *dai);
|
||||
|
||||
int snd_soc_dai_hw_params(struct snd_soc_dai *dai,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params);
|
||||
void snd_soc_dai_hw_free(struct snd_soc_dai *dai,
|
||||
struct snd_pcm_substream *substream);
|
||||
int snd_soc_dai_startup(struct snd_soc_dai *dai,
|
||||
struct snd_pcm_substream *substream);
|
||||
void snd_soc_dai_shutdown(struct snd_soc_dai *dai,
|
||||
struct snd_pcm_substream *substream);
|
||||
int snd_soc_dai_prepare(struct snd_soc_dai *dai,
|
||||
struct snd_pcm_substream *substream);
|
||||
int snd_soc_dai_trigger(struct snd_soc_dai *dai,
|
||||
struct snd_pcm_substream *substream, int cmd);
|
||||
int snd_soc_dai_bespoke_trigger(struct snd_soc_dai *dai,
|
||||
struct snd_pcm_substream *substream, int cmd);
|
||||
snd_pcm_sframes_t snd_soc_dai_delay(struct snd_soc_dai *dai,
|
||||
struct snd_pcm_substream *substream);
|
||||
void snd_soc_dai_suspend(struct snd_soc_dai *dai);
|
||||
void snd_soc_dai_resume(struct snd_soc_dai *dai);
|
||||
int snd_soc_dai_probe(struct snd_soc_dai *dai);
|
||||
int snd_soc_dai_remove(struct snd_soc_dai *dai);
|
||||
int snd_soc_dai_compress_new(struct snd_soc_dai *dai,
|
||||
struct snd_soc_pcm_runtime *rtd, int num);
|
||||
bool snd_soc_dai_stream_valid(struct snd_soc_dai *dai, int stream);
|
||||
|
||||
struct snd_soc_dai_ops {
|
||||
/*
|
||||
* DAI clocking configuration, all optional.
|
||||
|
@ -268,8 +293,6 @@ struct snd_soc_dai_driver {
|
|||
/* Optional Callback used at pcm creation*/
|
||||
int (*pcm_new)(struct snd_soc_pcm_runtime *rtd,
|
||||
struct snd_soc_dai *dai);
|
||||
/* DAI is also used for the control bus */
|
||||
bool bus_control;
|
||||
|
||||
/* ops */
|
||||
const struct snd_soc_dai_ops *ops;
|
||||
|
@ -281,6 +304,7 @@ struct snd_soc_dai_driver {
|
|||
unsigned int symmetric_rates:1;
|
||||
unsigned int symmetric_channels:1;
|
||||
unsigned int symmetric_samplebits:1;
|
||||
unsigned int bus_control:1; /* DAI is also used for the control bus */
|
||||
|
||||
/* probe ordering - for components with runtime dependencies */
|
||||
int probe_order;
|
||||
|
|
|
@ -353,6 +353,8 @@ struct device;
|
|||
#define SND_SOC_DAPM_WILL_PMD 0x80 /* called at start of sequence */
|
||||
#define SND_SOC_DAPM_PRE_POST_PMD \
|
||||
(SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD)
|
||||
#define SND_SOC_DAPM_PRE_POST_PMU \
|
||||
(SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU)
|
||||
|
||||
/* convenience event type detection */
|
||||
#define SND_SOC_DAPM_EVENT_ON(e) \
|
||||
|
@ -402,6 +404,9 @@ int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm,
|
|||
struct snd_soc_dapm_widget *snd_soc_dapm_new_control(
|
||||
struct snd_soc_dapm_context *dapm,
|
||||
const struct snd_soc_dapm_widget *widget);
|
||||
struct snd_soc_dapm_widget *snd_soc_dapm_new_control_unlocked(
|
||||
struct snd_soc_dapm_context *dapm,
|
||||
const struct snd_soc_dapm_widget *widget);
|
||||
int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
|
||||
struct snd_soc_dai *dai);
|
||||
int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card);
|
||||
|
@ -414,6 +419,9 @@ int snd_soc_dapm_update_dai(struct snd_pcm_substream *substream,
|
|||
/* dapm path setup */
|
||||
int snd_soc_dapm_new_widgets(struct snd_soc_card *card);
|
||||
void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm);
|
||||
void snd_soc_dapm_init(struct snd_soc_dapm_context *dapm,
|
||||
struct snd_soc_card *card,
|
||||
struct snd_soc_component *component);
|
||||
int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm,
|
||||
const struct snd_soc_dapm_route *route, int num);
|
||||
int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm,
|
||||
|
@ -659,8 +667,6 @@ struct snd_soc_dapm_context {
|
|||
unsigned int idle_bias_off:1; /* Use BIAS_OFF instead of STANDBY */
|
||||
/* Go to BIAS_OFF in suspend if the DAPM context is idle */
|
||||
unsigned int suspend_bias_off:1;
|
||||
void (*seq_notifier)(struct snd_soc_dapm_context *,
|
||||
enum snd_soc_dapm_type, int);
|
||||
|
||||
struct device *dev; /* from parent - for debug */
|
||||
struct snd_soc_component *component; /* parent component */
|
||||
|
@ -670,10 +676,6 @@ struct snd_soc_dapm_context {
|
|||
enum snd_soc_bias_level target_bias_level;
|
||||
struct list_head list;
|
||||
|
||||
int (*stream_event)(struct snd_soc_dapm_context *dapm, int event);
|
||||
int (*set_bias_level)(struct snd_soc_dapm_context *dapm,
|
||||
enum snd_soc_bias_level level);
|
||||
|
||||
struct snd_soc_dapm_wcache path_sink_cache;
|
||||
struct snd_soc_dapm_wcache path_source_cache;
|
||||
|
||||
|
|
|
@ -142,9 +142,16 @@ void snd_soc_dpcm_be_set_state(struct snd_soc_pcm_runtime *be, int stream,
|
|||
|
||||
/* internal use only */
|
||||
int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute);
|
||||
void soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd);
|
||||
int soc_dpcm_runtime_update(struct snd_soc_card *);
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
void soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd);
|
||||
#else
|
||||
static inline void soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
int dpcm_path_get(struct snd_soc_pcm_runtime *fe,
|
||||
int stream, struct snd_soc_dapm_widget_list **list_);
|
||||
int dpcm_process_paths(struct snd_soc_pcm_runtime *fe,
|
||||
|
|
|
@ -362,21 +362,6 @@
|
|||
#define SOC_ENUM_SINGLE_VIRT_DECL(name, xtexts) \
|
||||
const struct soc_enum name = SOC_ENUM_SINGLE_VIRT(ARRAY_SIZE(xtexts), xtexts)
|
||||
|
||||
/*
|
||||
* Component probe and remove ordering levels for components with runtime
|
||||
* dependencies.
|
||||
*/
|
||||
#define SND_SOC_COMP_ORDER_FIRST -2
|
||||
#define SND_SOC_COMP_ORDER_EARLY -1
|
||||
#define SND_SOC_COMP_ORDER_NORMAL 0
|
||||
#define SND_SOC_COMP_ORDER_LATE 1
|
||||
#define SND_SOC_COMP_ORDER_LAST 2
|
||||
|
||||
#define for_each_comp_order(order) \
|
||||
for (order = SND_SOC_COMP_ORDER_FIRST; \
|
||||
order <= SND_SOC_COMP_ORDER_LAST; \
|
||||
order++)
|
||||
|
||||
/*
|
||||
* Bias levels
|
||||
*
|
||||
|
@ -505,10 +490,6 @@ int snd_soc_params_to_bclk(struct snd_pcm_hw_params *parms);
|
|||
int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
|
||||
const struct snd_pcm_hardware *hw);
|
||||
|
||||
int soc_dai_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai);
|
||||
|
||||
/* Jack reporting */
|
||||
int snd_soc_card_jack_new(struct snd_soc_card *card, const char *id, int type,
|
||||
struct snd_soc_jack *jack, struct snd_soc_jack_pin *pins,
|
||||
|
@ -751,132 +732,6 @@ struct snd_soc_compr_ops {
|
|||
int (*trigger)(struct snd_compr_stream *);
|
||||
};
|
||||
|
||||
/* component interface */
|
||||
struct snd_soc_component_driver {
|
||||
const char *name;
|
||||
|
||||
/* Default control and setup, added after probe() is run */
|
||||
const struct snd_kcontrol_new *controls;
|
||||
unsigned int num_controls;
|
||||
const struct snd_soc_dapm_widget *dapm_widgets;
|
||||
unsigned int num_dapm_widgets;
|
||||
const struct snd_soc_dapm_route *dapm_routes;
|
||||
unsigned int num_dapm_routes;
|
||||
|
||||
int (*probe)(struct snd_soc_component *);
|
||||
void (*remove)(struct snd_soc_component *);
|
||||
int (*suspend)(struct snd_soc_component *);
|
||||
int (*resume)(struct snd_soc_component *);
|
||||
|
||||
unsigned int (*read)(struct snd_soc_component *, unsigned int);
|
||||
int (*write)(struct snd_soc_component *, unsigned int, unsigned int);
|
||||
|
||||
/* pcm creation and destruction */
|
||||
int (*pcm_new)(struct snd_soc_pcm_runtime *);
|
||||
void (*pcm_free)(struct snd_pcm *);
|
||||
|
||||
/* component wide operations */
|
||||
int (*set_sysclk)(struct snd_soc_component *component,
|
||||
int clk_id, int source, unsigned int freq, int dir);
|
||||
int (*set_pll)(struct snd_soc_component *component, int pll_id,
|
||||
int source, unsigned int freq_in, unsigned int freq_out);
|
||||
int (*set_jack)(struct snd_soc_component *component,
|
||||
struct snd_soc_jack *jack, void *data);
|
||||
|
||||
/* DT */
|
||||
int (*of_xlate_dai_name)(struct snd_soc_component *component,
|
||||
struct of_phandle_args *args,
|
||||
const char **dai_name);
|
||||
int (*of_xlate_dai_id)(struct snd_soc_component *comment,
|
||||
struct device_node *endpoint);
|
||||
void (*seq_notifier)(struct snd_soc_component *, enum snd_soc_dapm_type,
|
||||
int subseq);
|
||||
int (*stream_event)(struct snd_soc_component *, int event);
|
||||
int (*set_bias_level)(struct snd_soc_component *component,
|
||||
enum snd_soc_bias_level level);
|
||||
|
||||
const struct snd_pcm_ops *ops;
|
||||
const struct snd_compr_ops *compr_ops;
|
||||
|
||||
/* probe ordering - for components with runtime dependencies */
|
||||
int probe_order;
|
||||
int remove_order;
|
||||
|
||||
/*
|
||||
* signal if the module handling the component should not be removed
|
||||
* if a pcm is open. Setting this would prevent the module
|
||||
* refcount being incremented in probe() but allow it be incremented
|
||||
* when a pcm is opened and decremented when it is closed.
|
||||
*/
|
||||
unsigned int module_get_upon_open:1;
|
||||
|
||||
/* bits */
|
||||
unsigned int idle_bias_on:1;
|
||||
unsigned int suspend_bias_off:1;
|
||||
unsigned int use_pmdown_time:1; /* care pmdown_time at stop */
|
||||
unsigned int endianness:1;
|
||||
unsigned int non_legacy_dai_naming:1;
|
||||
|
||||
/* this component uses topology and ignore machine driver FEs */
|
||||
const char *ignore_machine;
|
||||
const char *topology_name_prefix;
|
||||
int (*be_hw_params_fixup)(struct snd_soc_pcm_runtime *rtd,
|
||||
struct snd_pcm_hw_params *params);
|
||||
bool use_dai_pcm_id; /* use the DAI link PCM ID as PCM device number */
|
||||
int be_pcm_base; /* base device ID for all BE PCMs */
|
||||
};
|
||||
|
||||
struct snd_soc_component {
|
||||
const char *name;
|
||||
int id;
|
||||
const char *name_prefix;
|
||||
struct device *dev;
|
||||
struct snd_soc_card *card;
|
||||
|
||||
unsigned int active;
|
||||
|
||||
unsigned int suspended:1; /* is in suspend PM state */
|
||||
|
||||
struct list_head list;
|
||||
struct list_head card_aux_list; /* for auxiliary bound components */
|
||||
struct list_head card_list;
|
||||
|
||||
const struct snd_soc_component_driver *driver;
|
||||
|
||||
struct list_head dai_list;
|
||||
int num_dai;
|
||||
|
||||
struct regmap *regmap;
|
||||
int val_bytes;
|
||||
|
||||
struct mutex io_mutex;
|
||||
|
||||
/* attached dynamic objects */
|
||||
struct list_head dobj_list;
|
||||
|
||||
/*
|
||||
* DO NOT use any of the fields below in drivers, they are temporary and
|
||||
* are going to be removed again soon. If you use them in driver code the
|
||||
* driver will be marked as BROKEN when these fields are removed.
|
||||
*/
|
||||
|
||||
/* Don't use these, use snd_soc_component_get_dapm() */
|
||||
struct snd_soc_dapm_context dapm;
|
||||
|
||||
/* machine specific init */
|
||||
int (*init)(struct snd_soc_component *component);
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *debugfs_root;
|
||||
const char *debugfs_prefix;
|
||||
#endif
|
||||
};
|
||||
|
||||
#define for_each_component_dais(component, dai)\
|
||||
list_for_each_entry(dai, &(component)->dai_list, list)
|
||||
#define for_each_component_dais_safe(component, dai, _dai)\
|
||||
list_for_each_entry_safe(dai, _dai, &(component)->dai_list, list)
|
||||
|
||||
struct snd_soc_rtdcom_list {
|
||||
struct snd_soc_component *component;
|
||||
struct list_head list; /* rtd::component_list */
|
||||
|
@ -1086,6 +941,7 @@ struct snd_soc_dai_link {
|
|||
#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_AUX(_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];
|
||||
|
@ -1107,14 +963,11 @@ struct snd_soc_codec_conf {
|
|||
};
|
||||
|
||||
struct snd_soc_aux_dev {
|
||||
const char *name; /* Codec name */
|
||||
|
||||
/*
|
||||
* specify multi-codec either by device name, or by
|
||||
* DT/OF node, but not both.
|
||||
*/
|
||||
const char *codec_name;
|
||||
struct device_node *codec_of_node;
|
||||
struct snd_soc_dai_link_component dlc;
|
||||
|
||||
/* codec/machine specific init - e.g. add machine controls */
|
||||
int (*init)(struct snd_soc_component *component);
|
||||
|
@ -1135,6 +988,10 @@ struct snd_soc_card {
|
|||
struct mutex mutex;
|
||||
struct mutex dapm_mutex;
|
||||
|
||||
/* Mutex for PCM operations */
|
||||
struct mutex pcm_mutex;
|
||||
enum snd_soc_pcm_subclass pcm_subclass;
|
||||
|
||||
spinlock_t dpcm_lock;
|
||||
|
||||
bool instantiated;
|
||||
|
@ -1203,8 +1060,6 @@ struct snd_soc_card {
|
|||
int num_of_dapm_routes;
|
||||
bool fully_routed;
|
||||
|
||||
struct work_struct deferred_resume_work;
|
||||
|
||||
/* lists of probed devices belonging to this card */
|
||||
struct list_head component_dev_list;
|
||||
struct list_head list;
|
||||
|
@ -1224,7 +1079,9 @@ struct snd_soc_card {
|
|||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *debugfs_card_root;
|
||||
struct dentry *debugfs_pop_time;
|
||||
#endif
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
struct work_struct deferred_resume_work;
|
||||
#endif
|
||||
u32 pop_time;
|
||||
|
||||
|
@ -1234,6 +1091,10 @@ struct snd_soc_card {
|
|||
for ((i) = 0; \
|
||||
((i) < (card)->num_links) && ((link) = &(card)->dai_link[i]); \
|
||||
(i)++)
|
||||
#define for_each_card_pre_auxs(card, i, aux) \
|
||||
for ((i) = 0; \
|
||||
((i) < (card)->num_aux_devs) && ((aux) = &(card)->aux_dev[i]); \
|
||||
(i)++)
|
||||
|
||||
#define for_each_card_links(card, link) \
|
||||
list_for_each_entry(link, &(card)->dai_link_list, list)
|
||||
|
@ -1245,6 +1106,12 @@ struct snd_soc_card {
|
|||
#define for_each_card_rtds_safe(card, rtd, _rtd) \
|
||||
list_for_each_entry_safe(rtd, _rtd, &(card)->rtd_list, list)
|
||||
|
||||
#define for_each_card_auxs(card, component) \
|
||||
list_for_each_entry(component, &card->aux_comp_list, card_aux_list)
|
||||
#define for_each_card_auxs_safe(card, component, _comp) \
|
||||
list_for_each_entry_safe(component, _comp, \
|
||||
&card->aux_comp_list, card_aux_list)
|
||||
|
||||
#define for_each_card_components(card, component) \
|
||||
list_for_each_entry(component, &(card)->component_dev_list, card_list)
|
||||
|
||||
|
@ -1253,8 +1120,6 @@ struct snd_soc_pcm_runtime {
|
|||
struct device *dev;
|
||||
struct snd_soc_card *card;
|
||||
struct snd_soc_dai_link *dai_link;
|
||||
struct mutex pcm_mutex;
|
||||
enum snd_soc_pcm_subclass pcm_subclass;
|
||||
struct snd_pcm_ops ops;
|
||||
|
||||
unsigned int params_select; /* currently selected param for dai link */
|
||||
|
@ -1342,134 +1207,6 @@ struct soc_enum {
|
|||
struct snd_soc_dobj dobj;
|
||||
};
|
||||
|
||||
/**
|
||||
* snd_soc_dapm_to_component() - Casts a DAPM context to the component it is
|
||||
* embedded in
|
||||
* @dapm: The DAPM context to cast to the component
|
||||
*
|
||||
* This function must only be used on DAPM contexts that are known to be part of
|
||||
* a component (e.g. in a component driver). Otherwise the behavior is
|
||||
* undefined.
|
||||
*/
|
||||
static inline struct snd_soc_component *snd_soc_dapm_to_component(
|
||||
struct snd_soc_dapm_context *dapm)
|
||||
{
|
||||
return container_of(dapm, struct snd_soc_component, dapm);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_soc_component_get_dapm() - Returns the DAPM context associated with a
|
||||
* component
|
||||
* @component: The component for which to get the DAPM context
|
||||
*/
|
||||
static inline struct snd_soc_dapm_context *snd_soc_component_get_dapm(
|
||||
struct snd_soc_component *component)
|
||||
{
|
||||
return &component->dapm;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_soc_component_init_bias_level() - Initialize COMPONENT DAPM bias level
|
||||
* @component: The COMPONENT for which to initialize the DAPM bias level
|
||||
* @level: The DAPM level to initialize to
|
||||
*
|
||||
* Initializes the COMPONENT DAPM bias level. See snd_soc_dapm_init_bias_level().
|
||||
*/
|
||||
static inline void
|
||||
snd_soc_component_init_bias_level(struct snd_soc_component *component,
|
||||
enum snd_soc_bias_level level)
|
||||
{
|
||||
snd_soc_dapm_init_bias_level(
|
||||
snd_soc_component_get_dapm(component), level);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_soc_component_get_bias_level() - Get current COMPONENT DAPM bias level
|
||||
* @component: The COMPONENT for which to get the DAPM bias level
|
||||
*
|
||||
* Returns: The current DAPM bias level of the COMPONENT.
|
||||
*/
|
||||
static inline enum snd_soc_bias_level
|
||||
snd_soc_component_get_bias_level(struct snd_soc_component *component)
|
||||
{
|
||||
return snd_soc_dapm_get_bias_level(
|
||||
snd_soc_component_get_dapm(component));
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_soc_component_force_bias_level() - Set the COMPONENT DAPM bias level
|
||||
* @component: The COMPONENT for which to set the level
|
||||
* @level: The level to set to
|
||||
*
|
||||
* Forces the COMPONENT bias level to a specific state. See
|
||||
* snd_soc_dapm_force_bias_level().
|
||||
*/
|
||||
static inline int
|
||||
snd_soc_component_force_bias_level(struct snd_soc_component *component,
|
||||
enum snd_soc_bias_level level)
|
||||
{
|
||||
return snd_soc_dapm_force_bias_level(
|
||||
snd_soc_component_get_dapm(component),
|
||||
level);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_soc_dapm_kcontrol_component() - Returns the component associated to a kcontrol
|
||||
* @kcontrol: The kcontrol
|
||||
*
|
||||
* This function must only be used on DAPM contexts that are known to be part of
|
||||
* a COMPONENT (e.g. in a COMPONENT driver). Otherwise the behavior is undefined.
|
||||
*/
|
||||
static inline struct snd_soc_component *snd_soc_dapm_kcontrol_component(
|
||||
struct snd_kcontrol *kcontrol)
|
||||
{
|
||||
return snd_soc_dapm_to_component(snd_soc_dapm_kcontrol_dapm(kcontrol));
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_soc_component_cache_sync() - Sync the register cache with the hardware
|
||||
* @component: COMPONENT to sync
|
||||
*
|
||||
* Note: This function will call regcache_sync()
|
||||
*/
|
||||
static inline int snd_soc_component_cache_sync(
|
||||
struct snd_soc_component *component)
|
||||
{
|
||||
return regcache_sync(component->regmap);
|
||||
}
|
||||
|
||||
/* component IO */
|
||||
int snd_soc_component_read(struct snd_soc_component *component,
|
||||
unsigned int reg, unsigned int *val);
|
||||
unsigned int snd_soc_component_read32(struct snd_soc_component *component,
|
||||
unsigned int reg);
|
||||
int snd_soc_component_write(struct snd_soc_component *component,
|
||||
unsigned int reg, unsigned int val);
|
||||
int snd_soc_component_update_bits(struct snd_soc_component *component,
|
||||
unsigned int reg, unsigned int mask, unsigned int val);
|
||||
int snd_soc_component_update_bits_async(struct snd_soc_component *component,
|
||||
unsigned int reg, unsigned int mask, unsigned int val);
|
||||
void snd_soc_component_async_complete(struct snd_soc_component *component);
|
||||
int snd_soc_component_test_bits(struct snd_soc_component *component,
|
||||
unsigned int reg, unsigned int mask, unsigned int value);
|
||||
|
||||
/* component wide operations */
|
||||
int snd_soc_component_set_sysclk(struct snd_soc_component *component,
|
||||
int clk_id, int source, unsigned int freq, int dir);
|
||||
int snd_soc_component_set_pll(struct snd_soc_component *component, int pll_id,
|
||||
int source, unsigned int freq_in,
|
||||
unsigned int freq_out);
|
||||
int snd_soc_component_set_jack(struct snd_soc_component *component,
|
||||
struct snd_soc_jack *jack, void *data);
|
||||
|
||||
#ifdef CONFIG_REGMAP
|
||||
|
||||
void snd_soc_component_init_regmap(struct snd_soc_component *component,
|
||||
struct regmap *regmap);
|
||||
void snd_soc_component_exit_regmap(struct snd_soc_component *component);
|
||||
|
||||
#endif
|
||||
|
||||
/* device driver data */
|
||||
|
||||
static inline void snd_soc_card_set_drvdata(struct snd_soc_card *card,
|
||||
|
@ -1483,27 +1220,6 @@ static inline void *snd_soc_card_get_drvdata(struct snd_soc_card *card)
|
|||
return card->drvdata;
|
||||
}
|
||||
|
||||
static inline void snd_soc_component_set_drvdata(struct snd_soc_component *c,
|
||||
void *data)
|
||||
{
|
||||
dev_set_drvdata(c->dev, data);
|
||||
}
|
||||
|
||||
static inline void *snd_soc_component_get_drvdata(struct snd_soc_component *c)
|
||||
{
|
||||
return dev_get_drvdata(c->dev);
|
||||
}
|
||||
|
||||
static inline void snd_soc_initialize_card_lists(struct snd_soc_card *card)
|
||||
{
|
||||
INIT_LIST_HEAD(&card->widgets);
|
||||
INIT_LIST_HEAD(&card->paths);
|
||||
INIT_LIST_HEAD(&card->dapm_list);
|
||||
INIT_LIST_HEAD(&card->aux_comp_list);
|
||||
INIT_LIST_HEAD(&card->component_dev_list);
|
||||
INIT_LIST_HEAD(&card->list);
|
||||
}
|
||||
|
||||
static inline bool snd_soc_volsw_is_stereo(struct soc_mixer_control *mc)
|
||||
{
|
||||
if (mc->reg == mc->rreg && mc->shift == mc->rshift)
|
||||
|
@ -1540,12 +1256,6 @@ static inline unsigned int snd_soc_enum_item_to_val(struct soc_enum *e,
|
|||
return e->values[item];
|
||||
}
|
||||
|
||||
static inline bool snd_soc_component_is_active(
|
||||
struct snd_soc_component *component)
|
||||
{
|
||||
return component->active != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_soc_kcontrol_component() - Returns the component that registered the
|
||||
* control
|
||||
|
@ -1681,24 +1391,6 @@ static inline void snd_soc_dapm_mutex_unlock(struct snd_soc_dapm_context *dapm)
|
|||
mutex_unlock(&dapm->card->dapm_mutex);
|
||||
}
|
||||
|
||||
int snd_soc_component_enable_pin(struct snd_soc_component *component,
|
||||
const char *pin);
|
||||
int snd_soc_component_enable_pin_unlocked(struct snd_soc_component *component,
|
||||
const char *pin);
|
||||
int snd_soc_component_disable_pin(struct snd_soc_component *component,
|
||||
const char *pin);
|
||||
int snd_soc_component_disable_pin_unlocked(struct snd_soc_component *component,
|
||||
const char *pin);
|
||||
int snd_soc_component_nc_pin(struct snd_soc_component *component,
|
||||
const char *pin);
|
||||
int snd_soc_component_nc_pin_unlocked(struct snd_soc_component *component,
|
||||
const char *pin);
|
||||
int snd_soc_component_get_pin_status(struct snd_soc_component *component,
|
||||
const char *pin);
|
||||
int snd_soc_component_force_enable_pin(struct snd_soc_component *component,
|
||||
const char *pin);
|
||||
int snd_soc_component_force_enable_pin_unlocked(
|
||||
struct snd_soc_component *component,
|
||||
const char *pin);
|
||||
#include <sound/soc-component.h>
|
||||
|
||||
#endif
|
||||
|
|
|
@ -76,6 +76,9 @@ struct sof_ipc_dai_ssp_params {
|
|||
uint16_t tdm_per_slot_padding_flag;
|
||||
uint32_t clks_control;
|
||||
uint32_t quirks;
|
||||
uint32_t bclk_delay; /* guaranteed time (ms) for which BCLK
|
||||
* will be driven, before sending data
|
||||
*/
|
||||
} __packed;
|
||||
|
||||
/* HDA Configuration Request - SOF_IPC_DAI_HDA_CONFIG */
|
||||
|
@ -176,4 +179,13 @@ struct sof_ipc_dai_dmic_params {
|
|||
struct sof_ipc_dai_dmic_pdm_ctrl pdm[0];
|
||||
} __packed;
|
||||
|
||||
/* ALH Configuration Request - SOF_IPC_DAI_ALH_CONFIG */
|
||||
struct sof_ipc_dai_alh_params {
|
||||
struct sof_ipc_hdr hdr;
|
||||
uint32_t stream_id;
|
||||
|
||||
/* reserved for future use */
|
||||
uint32_t reserved[15];
|
||||
} __packed;
|
||||
|
||||
#endif
|
||||
|
|
|
@ -49,7 +49,9 @@ enum sof_ipc_dai_type {
|
|||
SOF_DAI_INTEL_SSP, /**< Intel SSP */
|
||||
SOF_DAI_INTEL_DMIC, /**< Intel DMIC */
|
||||
SOF_DAI_INTEL_HDA, /**< Intel HD/A */
|
||||
SOF_DAI_INTEL_SOUNDWIRE, /**< Intel SoundWire */
|
||||
SOF_DAI_INTEL_ALH, /**< Intel ALH */
|
||||
SOF_DAI_IMX_SAI, /**< i.MX SAI */
|
||||
SOF_DAI_IMX_ESAI, /**< i.MX ESAI */
|
||||
};
|
||||
|
||||
/* general purpose DAI configuration */
|
||||
|
@ -70,6 +72,7 @@ struct sof_ipc_dai_config {
|
|||
struct sof_ipc_dai_ssp_params ssp;
|
||||
struct sof_ipc_dai_dmic_params dmic;
|
||||
struct sof_ipc_dai_hda_params hda;
|
||||
struct sof_ipc_dai_alh_params alh;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
/* SOF ABI version major, minor and patch numbers */
|
||||
#define SOF_ABI_MAJOR 3
|
||||
#define SOF_ABI_MINOR 8
|
||||
#define SOF_ABI_MINOR 10
|
||||
#define SOF_ABI_PATCH 0
|
||||
|
||||
/* SOF ABI version number. Format within 32bit word is MMmmmppp */
|
||||
|
|
|
@ -75,6 +75,7 @@
|
|||
#define SOF_TKN_INTEL_SSP_FRAME_PULSE_WIDTH 503
|
||||
#define SOF_TKN_INTEL_SSP_QUIRKS 504
|
||||
#define SOF_TKN_INTEL_SSP_TDM_PADDING_PER_SLOT 505
|
||||
#define SOF_TKN_INTEL_SSP_BCLK_DELAY 506
|
||||
|
||||
/* DMIC */
|
||||
#define SOF_TKN_INTEL_DMIC_DRIVER_VERSION 600
|
||||
|
@ -105,4 +106,12 @@
|
|||
/* for backward compatibility */
|
||||
#define SOF_TKN_EFFECT_TYPE SOF_TKN_PROCESS_TYPE
|
||||
|
||||
/* SAI */
|
||||
#define SOF_TKN_IMX_SAI_FIRST_TOKEN 1000
|
||||
/* TODO: Add SAI tokens */
|
||||
|
||||
/* ESAI */
|
||||
#define SOF_TKN_IMX_ESAI_FIRST_TOKEN 1100
|
||||
/* TODO: Add ESAI tokens */
|
||||
|
||||
#endif
|
||||
|
|
|
@ -71,8 +71,10 @@ static int onyx_read_register(struct onyx *onyx, u8 reg, u8 *value)
|
|||
return 0;
|
||||
}
|
||||
v = i2c_smbus_read_byte_data(onyx->i2c, reg);
|
||||
if (v < 0)
|
||||
if (v < 0) {
|
||||
*value = 0;
|
||||
return -1;
|
||||
}
|
||||
*value = (u8)v;
|
||||
onyx->cache[ONYX_REG_CONTROL-FIRSTREGISTER] = *value;
|
||||
return 0;
|
||||
|
|
|
@ -2170,7 +2170,7 @@ static int snd_pcm_hw_rule_sample_bits(struct snd_pcm_hw_params *params,
|
|||
|
||||
static const unsigned int rates[] = {
|
||||
5512, 8000, 11025, 16000, 22050, 32000, 44100,
|
||||
48000, 64000, 88200, 96000, 176400, 192000
|
||||
48000, 64000, 88200, 96000, 176400, 192000, 352800, 384000
|
||||
};
|
||||
|
||||
const struct snd_pcm_hw_constraint_list snd_pcm_known_rates = {
|
||||
|
|
|
@ -146,19 +146,24 @@ void amdtp_am824_set_midi_position(struct amdtp_stream *s,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(amdtp_am824_set_midi_position);
|
||||
|
||||
static void write_pcm_s32(struct amdtp_stream *s,
|
||||
struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int frames)
|
||||
static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int frames,
|
||||
unsigned int pcm_frames)
|
||||
{
|
||||
struct amdtp_am824 *p = s->protocol;
|
||||
unsigned int channels = p->pcm_channels;
|
||||
struct snd_pcm_runtime *runtime = pcm->runtime;
|
||||
unsigned int channels, remaining_frames, i, c;
|
||||
unsigned int pcm_buffer_pointer;
|
||||
int remaining_frames;
|
||||
const u32 *src;
|
||||
int i, c;
|
||||
|
||||
pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
|
||||
pcm_buffer_pointer %= runtime->buffer_size;
|
||||
|
||||
channels = p->pcm_channels;
|
||||
src = (void *)runtime->dma_area +
|
||||
frames_to_bytes(runtime, s->pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
|
||||
frames_to_bytes(runtime, pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
|
||||
|
||||
for (i = 0; i < frames; ++i) {
|
||||
for (c = 0; c < channels; ++c) {
|
||||
|
@ -172,19 +177,24 @@ static void write_pcm_s32(struct amdtp_stream *s,
|
|||
}
|
||||
}
|
||||
|
||||
static void read_pcm_s32(struct amdtp_stream *s,
|
||||
struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int frames)
|
||||
static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int frames,
|
||||
unsigned int pcm_frames)
|
||||
{
|
||||
struct amdtp_am824 *p = s->protocol;
|
||||
unsigned int channels = p->pcm_channels;
|
||||
struct snd_pcm_runtime *runtime = pcm->runtime;
|
||||
unsigned int channels, remaining_frames, i, c;
|
||||
unsigned int pcm_buffer_pointer;
|
||||
int remaining_frames;
|
||||
u32 *dst;
|
||||
int i, c;
|
||||
|
||||
pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
|
||||
pcm_buffer_pointer %= runtime->buffer_size;
|
||||
|
||||
channels = p->pcm_channels;
|
||||
dst = (void *)runtime->dma_area +
|
||||
frames_to_bytes(runtime, s->pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
|
||||
frames_to_bytes(runtime, pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
|
||||
|
||||
for (i = 0; i < frames; ++i) {
|
||||
for (c = 0; c < channels; ++c) {
|
||||
|
@ -284,7 +294,7 @@ static void midi_rate_use_one_byte(struct amdtp_stream *s, unsigned int port)
|
|||
}
|
||||
|
||||
static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
|
||||
unsigned int frames)
|
||||
unsigned int frames, unsigned int data_block_counter)
|
||||
{
|
||||
struct amdtp_am824 *p = s->protocol;
|
||||
unsigned int f, port;
|
||||
|
@ -293,7 +303,7 @@ static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
|
|||
for (f = 0; f < frames; f++) {
|
||||
b = (u8 *)&buffer[p->midi_position];
|
||||
|
||||
port = (s->data_block_counter + f) % 8;
|
||||
port = (data_block_counter + f) % 8;
|
||||
if (f < MAX_MIDI_RX_BLOCKS &&
|
||||
midi_ratelimit_per_packet(s, port) &&
|
||||
p->midi[port] != NULL &&
|
||||
|
@ -311,16 +321,20 @@ static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
|
|||
}
|
||||
}
|
||||
|
||||
static void read_midi_messages(struct amdtp_stream *s,
|
||||
__be32 *buffer, unsigned int frames)
|
||||
static void read_midi_messages(struct amdtp_stream *s, __be32 *buffer,
|
||||
unsigned int frames, unsigned int data_block_counter)
|
||||
{
|
||||
struct amdtp_am824 *p = s->protocol;
|
||||
unsigned int f, port;
|
||||
int len;
|
||||
u8 *b;
|
||||
int f;
|
||||
|
||||
for (f = 0; f < frames; f++) {
|
||||
port = (8 - s->ctx_data.tx.first_dbc + s->data_block_counter + f) % 8;
|
||||
unsigned int port = f;
|
||||
|
||||
if (!(s->flags & CIP_UNALIGHED_DBC))
|
||||
port += data_block_counter;
|
||||
port %= 8;
|
||||
b = (u8 *)&buffer[p->midi_position];
|
||||
|
||||
len = b[0] - 0x80;
|
||||
|
@ -331,44 +345,61 @@ static void read_midi_messages(struct amdtp_stream *s,
|
|||
}
|
||||
}
|
||||
|
||||
static unsigned int process_rx_data_blocks(struct amdtp_stream *s, __be32 *buffer,
|
||||
unsigned int data_blocks, unsigned int *syt)
|
||||
static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
|
||||
const struct pkt_desc *descs,
|
||||
unsigned int packets,
|
||||
struct snd_pcm_substream *pcm)
|
||||
{
|
||||
struct amdtp_am824 *p = s->protocol;
|
||||
struct snd_pcm_substream *pcm = READ_ONCE(s->pcm);
|
||||
unsigned int pcm_frames;
|
||||
unsigned int pcm_frames = 0;
|
||||
int i;
|
||||
|
||||
if (pcm) {
|
||||
write_pcm_s32(s, pcm, buffer, data_blocks);
|
||||
pcm_frames = data_blocks * p->frame_multiplier;
|
||||
} else {
|
||||
write_pcm_silence(s, buffer, data_blocks);
|
||||
pcm_frames = 0;
|
||||
for (i = 0; i < packets; ++i) {
|
||||
const struct pkt_desc *desc = descs + i;
|
||||
__be32 *buf = desc->ctx_payload;
|
||||
unsigned int data_blocks = desc->data_blocks;
|
||||
|
||||
if (pcm) {
|
||||
write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
|
||||
pcm_frames += data_blocks * p->frame_multiplier;
|
||||
} else {
|
||||
write_pcm_silence(s, buf, data_blocks);
|
||||
}
|
||||
|
||||
if (p->midi_ports) {
|
||||
write_midi_messages(s, buf, data_blocks,
|
||||
desc->data_block_counter);
|
||||
}
|
||||
}
|
||||
|
||||
if (p->midi_ports)
|
||||
write_midi_messages(s, buffer, data_blocks);
|
||||
|
||||
return pcm_frames;
|
||||
}
|
||||
|
||||
static unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffer,
|
||||
unsigned int data_blocks, unsigned int *syt)
|
||||
static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
|
||||
const struct pkt_desc *descs,
|
||||
unsigned int packets,
|
||||
struct snd_pcm_substream *pcm)
|
||||
{
|
||||
struct amdtp_am824 *p = s->protocol;
|
||||
struct snd_pcm_substream *pcm = READ_ONCE(s->pcm);
|
||||
unsigned int pcm_frames;
|
||||
unsigned int pcm_frames = 0;
|
||||
int i;
|
||||
|
||||
if (pcm) {
|
||||
read_pcm_s32(s, pcm, buffer, data_blocks);
|
||||
pcm_frames = data_blocks * p->frame_multiplier;
|
||||
} else {
|
||||
pcm_frames = 0;
|
||||
for (i = 0; i < packets; ++i) {
|
||||
const struct pkt_desc *desc = descs + i;
|
||||
__be32 *buf = desc->ctx_payload;
|
||||
unsigned int data_blocks = desc->data_blocks;
|
||||
|
||||
if (pcm) {
|
||||
read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
|
||||
pcm_frames += data_blocks * p->frame_multiplier;
|
||||
}
|
||||
|
||||
if (p->midi_ports) {
|
||||
read_midi_messages(s, buf, data_blocks,
|
||||
desc->data_block_counter);
|
||||
}
|
||||
}
|
||||
|
||||
if (p->midi_ports)
|
||||
read_midi_messages(s, buffer, data_blocks);
|
||||
|
||||
return pcm_frames;
|
||||
}
|
||||
|
||||
|
@ -383,15 +414,14 @@ static unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffe
|
|||
int amdtp_am824_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
enum amdtp_stream_direction dir, enum cip_flags flags)
|
||||
{
|
||||
amdtp_stream_process_data_blocks_t process_data_blocks;
|
||||
amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
|
||||
|
||||
if (dir == AMDTP_IN_STREAM)
|
||||
process_data_blocks = process_tx_data_blocks;
|
||||
process_ctx_payloads = process_ir_ctx_payloads;
|
||||
else
|
||||
process_data_blocks = process_rx_data_blocks;
|
||||
process_ctx_payloads = process_it_ctx_payloads;
|
||||
|
||||
return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM,
|
||||
process_data_blocks,
|
||||
sizeof(struct amdtp_am824));
|
||||
process_ctx_payloads, sizeof(struct amdtp_am824));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(amdtp_am824_init);
|
||||
|
|
|
@ -14,8 +14,8 @@
|
|||
#include <linux/tracepoint.h>
|
||||
|
||||
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_PROTO(const struct amdtp_stream *s, u32 cycles, const __be32 *cip_header, unsigned int payload_length, unsigned int data_blocks, unsigned int data_block_counter, unsigned int index),
|
||||
TP_ARGS(s, cycles, cip_header, payload_length, data_blocks, data_block_counter, index),
|
||||
TP_STRUCT__entry(
|
||||
__field(unsigned int, second)
|
||||
__field(unsigned int, cycle)
|
||||
|
@ -47,7 +47,7 @@ TRACE_EVENT(amdtp_packet,
|
|||
}
|
||||
__entry->payload_quadlets = payload_length / sizeof(__be32);
|
||||
__entry->data_blocks = data_blocks;
|
||||
__entry->data_block_counter = s->data_block_counter,
|
||||
__entry->data_block_counter = data_block_counter,
|
||||
__entry->packet_index = s->packet_index;
|
||||
__entry->irq = !!in_interrupt();
|
||||
__entry->index = index;
|
||||
|
|
|
@ -74,16 +74,16 @@ static void pcm_period_tasklet(unsigned long data);
|
|||
* @dir: the direction of stream
|
||||
* @flags: the packet transmission method to use
|
||||
* @fmt: the value of fmt field in CIP header
|
||||
* @process_data_blocks: callback handler to process data blocks
|
||||
* @process_ctx_payloads: callback handler to process payloads of isoc context
|
||||
* @protocol_size: the size to allocate newly for protocol
|
||||
*/
|
||||
int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
enum amdtp_stream_direction dir, enum cip_flags flags,
|
||||
unsigned int fmt,
|
||||
amdtp_stream_process_data_blocks_t process_data_blocks,
|
||||
amdtp_stream_process_ctx_payloads_t process_ctx_payloads,
|
||||
unsigned int protocol_size)
|
||||
{
|
||||
if (process_data_blocks == NULL)
|
||||
if (process_ctx_payloads == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
s->protocol = kzalloc(protocol_size, GFP_KERNEL);
|
||||
|
@ -102,7 +102,10 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
|
|||
s->callbacked = false;
|
||||
|
||||
s->fmt = fmt;
|
||||
s->process_data_blocks = process_data_blocks;
|
||||
s->process_ctx_payloads = process_ctx_payloads;
|
||||
|
||||
if (dir == AMDTP_OUT_STREAM)
|
||||
s->ctx_data.rx.syt_override = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -473,12 +476,12 @@ static inline int queue_in_packet(struct amdtp_stream *s,
|
|||
}
|
||||
|
||||
static void generate_cip_header(struct amdtp_stream *s, __be32 cip_header[2],
|
||||
unsigned int syt)
|
||||
unsigned int data_block_counter, unsigned int syt)
|
||||
{
|
||||
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);
|
||||
data_block_counter);
|
||||
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) |
|
||||
|
@ -487,8 +490,9 @@ static void generate_cip_header(struct amdtp_stream *s, __be32 cip_header[2],
|
|||
|
||||
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 data_blocks,
|
||||
unsigned int data_block_counter,
|
||||
unsigned int syt, unsigned int index)
|
||||
{
|
||||
unsigned int payload_length;
|
||||
__be32 *cip_header;
|
||||
|
@ -496,14 +500,9 @@ static void build_it_pkt_header(struct amdtp_stream *s, unsigned int cycle,
|
|||
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;
|
||||
}
|
||||
|
||||
if (!(s->flags & CIP_NO_HEADER)) {
|
||||
cip_header = (__be32 *)params->header;
|
||||
generate_cip_header(s, cip_header, syt);
|
||||
generate_cip_header(s, cip_header, data_block_counter, syt);
|
||||
params->header_length = 2 * sizeof(__be32);
|
||||
payload_length += params->header_length;
|
||||
} else {
|
||||
|
@ -511,23 +510,19 @@ static void build_it_pkt_header(struct amdtp_stream *s, unsigned int cycle,
|
|||
}
|
||||
|
||||
trace_amdtp_packet(s, cycle, cip_header, payload_length, data_blocks,
|
||||
index);
|
||||
|
||||
if (!(s->flags & CIP_DBC_IS_END_EVENT)) {
|
||||
s->data_block_counter =
|
||||
(s->data_block_counter + data_blocks) & 0xff;
|
||||
}
|
||||
data_block_counter, 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)
|
||||
unsigned int *data_blocks,
|
||||
unsigned int *data_block_counter, unsigned int *syt)
|
||||
{
|
||||
u32 cip_header[2];
|
||||
unsigned int sph;
|
||||
unsigned int fmt;
|
||||
unsigned int fdf;
|
||||
unsigned int dbc;
|
||||
bool lost;
|
||||
|
||||
cip_header[0] = be32_to_cpu(buf[0]);
|
||||
|
@ -579,17 +574,16 @@ static int check_cip_header(struct amdtp_stream *s, const __be32 *buf,
|
|||
}
|
||||
|
||||
/* Check data block counter continuity */
|
||||
*dbc = cip_header[0] & CIP_DBC_MASK;
|
||||
dbc = cip_header[0] & CIP_DBC_MASK;
|
||||
if (*data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) &&
|
||||
s->data_block_counter != UINT_MAX)
|
||||
*dbc = s->data_block_counter;
|
||||
*data_block_counter != UINT_MAX)
|
||||
dbc = *data_block_counter;
|
||||
|
||||
if (((s->flags & CIP_SKIP_DBC_ZERO_CHECK) &&
|
||||
*dbc == s->ctx_data.tx.first_dbc) ||
|
||||
s->data_block_counter == UINT_MAX) {
|
||||
if ((dbc == 0x00 && (s->flags & CIP_SKIP_DBC_ZERO_CHECK)) ||
|
||||
*data_block_counter == UINT_MAX) {
|
||||
lost = false;
|
||||
} else if (!(s->flags & CIP_DBC_IS_END_EVENT)) {
|
||||
lost = *dbc != s->data_block_counter;
|
||||
lost = dbc != *data_block_counter;
|
||||
} else {
|
||||
unsigned int dbc_interval;
|
||||
|
||||
|
@ -598,16 +592,18 @@ static int check_cip_header(struct amdtp_stream *s, const __be32 *buf,
|
|||
else
|
||||
dbc_interval = *data_blocks;
|
||||
|
||||
lost = *dbc != ((s->data_block_counter + dbc_interval) & 0xff);
|
||||
lost = dbc != ((*data_block_counter + dbc_interval) & 0xff);
|
||||
}
|
||||
|
||||
if (lost) {
|
||||
dev_err(&s->unit->device,
|
||||
"Detect discontinuity of CIP: %02X %02X\n",
|
||||
s->data_block_counter, *dbc);
|
||||
*data_block_counter, dbc);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
*data_block_counter = dbc;
|
||||
|
||||
*syt = cip_header[1] & CIP_SYT_MASK;
|
||||
|
||||
return 0;
|
||||
|
@ -616,10 +612,10 @@ static int check_cip_header(struct amdtp_stream *s, const __be32 *buf,
|
|||
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)
|
||||
unsigned int *data_blocks,
|
||||
unsigned int *data_block_counter,
|
||||
unsigned int *syt, unsigned int index)
|
||||
{
|
||||
unsigned int dbc;
|
||||
const __be32 *cip_header;
|
||||
int err;
|
||||
|
||||
|
@ -635,7 +631,7 @@ static int parse_ir_ctx_header(struct amdtp_stream *s, unsigned int cycle,
|
|||
if (!(s->flags & CIP_NO_HEADER)) {
|
||||
cip_header = ctx_header + 2;
|
||||
err = check_cip_header(s, cip_header, *payload_length,
|
||||
data_blocks, &dbc, syt);
|
||||
data_blocks, data_block_counter, syt);
|
||||
if (err < 0)
|
||||
return err;
|
||||
} else {
|
||||
|
@ -645,16 +641,12 @@ static int parse_ir_ctx_header(struct amdtp_stream *s, unsigned int cycle,
|
|||
s->data_block_quadlets;
|
||||
*syt = 0;
|
||||
|
||||
if (s->data_block_counter != UINT_MAX)
|
||||
dbc = s->data_block_counter;
|
||||
else
|
||||
dbc = 0;
|
||||
if (*data_block_counter == UINT_MAX)
|
||||
*data_block_counter = 0;
|
||||
}
|
||||
|
||||
s->data_block_counter = dbc;
|
||||
|
||||
trace_amdtp_packet(s, cycle, cip_header, *payload_length, *data_blocks,
|
||||
index);
|
||||
*data_block_counter, index);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -686,6 +678,80 @@ static inline u32 compute_it_cycle(const __be32 ctx_header_tstamp)
|
|||
return increment_cycle_count(cycle, QUEUE_LENGTH);
|
||||
}
|
||||
|
||||
static int generate_device_pkt_descs(struct amdtp_stream *s,
|
||||
struct pkt_desc *descs,
|
||||
const __be32 *ctx_header,
|
||||
unsigned int packets)
|
||||
{
|
||||
unsigned int dbc = s->data_block_counter;
|
||||
int i;
|
||||
int err;
|
||||
|
||||
for (i = 0; i < packets; ++i) {
|
||||
struct pkt_desc *desc = descs + i;
|
||||
unsigned int index = (s->packet_index + i) % QUEUE_LENGTH;
|
||||
unsigned int cycle;
|
||||
unsigned int payload_length;
|
||||
unsigned int data_blocks;
|
||||
unsigned int syt;
|
||||
|
||||
cycle = compute_cycle_count(ctx_header[1]);
|
||||
|
||||
err = parse_ir_ctx_header(s, cycle, ctx_header, &payload_length,
|
||||
&data_blocks, &dbc, &syt, i);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
desc->cycle = cycle;
|
||||
desc->syt = syt;
|
||||
desc->data_blocks = data_blocks;
|
||||
desc->data_block_counter = dbc;
|
||||
desc->ctx_payload = s->buffer.packets[index].buffer;
|
||||
|
||||
if (!(s->flags & CIP_DBC_IS_END_EVENT))
|
||||
dbc = (dbc + desc->data_blocks) & 0xff;
|
||||
|
||||
ctx_header +=
|
||||
s->ctx_data.tx.ctx_header_size / sizeof(*ctx_header);
|
||||
}
|
||||
|
||||
s->data_block_counter = dbc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void generate_ideal_pkt_descs(struct amdtp_stream *s,
|
||||
struct pkt_desc *descs,
|
||||
const __be32 *ctx_header,
|
||||
unsigned int packets)
|
||||
{
|
||||
unsigned int dbc = s->data_block_counter;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < packets; ++i) {
|
||||
struct pkt_desc *desc = descs + i;
|
||||
unsigned int index = (s->packet_index + i) % QUEUE_LENGTH;
|
||||
|
||||
desc->cycle = compute_it_cycle(*ctx_header);
|
||||
desc->syt = calculate_syt(s, desc->cycle);
|
||||
desc->data_blocks = calculate_data_blocks(s, desc->syt);
|
||||
|
||||
if (s->flags & CIP_DBC_IS_END_EVENT)
|
||||
dbc = (dbc + desc->data_blocks) & 0xff;
|
||||
|
||||
desc->data_block_counter = dbc;
|
||||
|
||||
if (!(s->flags & CIP_DBC_IS_END_EVENT))
|
||||
dbc = (dbc + desc->data_blocks) & 0xff;
|
||||
|
||||
desc->ctx_payload = s->buffer.packets[index].buffer;
|
||||
|
||||
++ctx_header;
|
||||
}
|
||||
|
||||
s->data_block_counter = dbc;
|
||||
}
|
||||
|
||||
static inline void cancel_stream(struct amdtp_stream *s)
|
||||
{
|
||||
s->packet_index = -1;
|
||||
|
@ -694,6 +760,19 @@ static inline void cancel_stream(struct amdtp_stream *s)
|
|||
WRITE_ONCE(s->pcm_buffer_pointer, SNDRV_PCM_POS_XRUN);
|
||||
}
|
||||
|
||||
static void process_ctx_payloads(struct amdtp_stream *s,
|
||||
const struct pkt_desc *descs,
|
||||
unsigned int packets)
|
||||
{
|
||||
struct snd_pcm_substream *pcm;
|
||||
unsigned int pcm_frames;
|
||||
|
||||
pcm = READ_ONCE(s->pcm);
|
||||
pcm_frames = s->process_ctx_payloads(s, descs, packets, pcm);
|
||||
if (pcm)
|
||||
update_pcm_pointers(s, pcm, pcm_frames);
|
||||
}
|
||||
|
||||
static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
|
||||
size_t header_length, void *header,
|
||||
void *private_data)
|
||||
|
@ -706,38 +785,31 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
|
|||
if (s->packet_index < 0)
|
||||
return;
|
||||
|
||||
generate_ideal_pkt_descs(s, s->pkt_descs, ctx_header, packets);
|
||||
|
||||
process_ctx_payloads(s, s->pkt_descs, packets);
|
||||
|
||||
for (i = 0; i < packets; ++i) {
|
||||
u32 cycle;
|
||||
const struct pkt_desc *desc = s->pkt_descs + i;
|
||||
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);
|
||||
if (s->ctx_data.rx.syt_override < 0)
|
||||
syt = desc->syt;
|
||||
else
|
||||
syt = s->ctx_data.rx.syt_override;
|
||||
|
||||
build_it_pkt_header(s, cycle, &template.params, data_blocks,
|
||||
build_it_pkt_header(s, desc->cycle, &template.params,
|
||||
desc->data_blocks, desc->data_block_counter,
|
||||
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);
|
||||
|
@ -748,8 +820,10 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
|
|||
void *private_data)
|
||||
{
|
||||
struct amdtp_stream *s = private_data;
|
||||
unsigned int i, packets;
|
||||
unsigned int packets;
|
||||
__be32 *ctx_header = header;
|
||||
int i;
|
||||
int err;
|
||||
|
||||
if (s->packet_index < 0)
|
||||
return;
|
||||
|
@ -757,48 +831,23 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
|
|||
// The number of packets in buffer.
|
||||
packets = header_length / s->ctx_data.tx.ctx_header_size;
|
||||
|
||||
for (i = 0; i < packets; i++) {
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
err = generate_device_pkt_descs(s, s->pkt_descs, ctx_header, packets);
|
||||
if (err < 0) {
|
||||
if (err != -EAGAIN) {
|
||||
cancel_stream(s);
|
||||
return;
|
||||
}
|
||||
|
||||
if (queue_in_packet(s, ¶ms) < 0)
|
||||
break;
|
||||
|
||||
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);
|
||||
} else {
|
||||
process_ctx_payloads(s, s->pkt_descs, packets);
|
||||
}
|
||||
|
||||
/* Queueing error or detecting invalid payload. */
|
||||
if (i < packets) {
|
||||
cancel_stream(s);
|
||||
return;
|
||||
for (i = 0; i < packets; ++i) {
|
||||
struct fw_iso_packet params = {0};
|
||||
|
||||
if (queue_in_packet(s, ¶ms) < 0) {
|
||||
cancel_stream(s);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fw_iso_context_queue_flush(s->context);
|
||||
|
@ -845,7 +894,7 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
|
|||
* amdtp_stream_set_parameters() and it must be started before any PCM or MIDI
|
||||
* device can be started.
|
||||
*/
|
||||
int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
|
||||
static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
|
||||
{
|
||||
static const struct {
|
||||
unsigned int data_block;
|
||||
|
@ -932,6 +981,13 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
|
|||
else
|
||||
s->tag = TAG_CIP;
|
||||
|
||||
s->pkt_descs = kcalloc(INTERRUPT_INTERVAL, sizeof(*s->pkt_descs),
|
||||
GFP_KERNEL);
|
||||
if (!s->pkt_descs) {
|
||||
err = -ENOMEM;
|
||||
goto err_context;
|
||||
}
|
||||
|
||||
s->packet_index = 0;
|
||||
do {
|
||||
struct fw_iso_packet params;
|
||||
|
@ -943,7 +999,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
|
|||
err = queue_out_packet(s, ¶ms);
|
||||
}
|
||||
if (err < 0)
|
||||
goto err_context;
|
||||
goto err_pkt_descs;
|
||||
} while (s->packet_index > 0);
|
||||
|
||||
/* NOTE: TAG1 matches CIP. This just affects in stream. */
|
||||
|
@ -954,12 +1010,13 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
|
|||
s->callbacked = false;
|
||||
err = fw_iso_context_start(s->context, -1, 0, tag);
|
||||
if (err < 0)
|
||||
goto err_context;
|
||||
goto err_pkt_descs;
|
||||
|
||||
mutex_unlock(&s->mutex);
|
||||
|
||||
return 0;
|
||||
|
||||
err_pkt_descs:
|
||||
kfree(s->pkt_descs);
|
||||
err_context:
|
||||
fw_iso_context_destroy(s->context);
|
||||
s->context = ERR_PTR(-1);
|
||||
|
@ -970,7 +1027,6 @@ err_unlock:
|
|||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(amdtp_stream_start);
|
||||
|
||||
/**
|
||||
* amdtp_stream_pcm_pointer - get the PCM buffer position
|
||||
|
@ -1041,7 +1097,7 @@ EXPORT_SYMBOL(amdtp_stream_update);
|
|||
* All PCM and MIDI devices of the stream must be stopped before the stream
|
||||
* itself can be stopped.
|
||||
*/
|
||||
void amdtp_stream_stop(struct amdtp_stream *s)
|
||||
static void amdtp_stream_stop(struct amdtp_stream *s)
|
||||
{
|
||||
mutex_lock(&s->mutex);
|
||||
|
||||
|
@ -1055,12 +1111,12 @@ void amdtp_stream_stop(struct amdtp_stream *s)
|
|||
fw_iso_context_destroy(s->context);
|
||||
s->context = ERR_PTR(-1);
|
||||
iso_packets_buffer_destroy(&s->buffer, s->unit);
|
||||
kfree(s->pkt_descs);
|
||||
|
||||
s->callbacked = false;
|
||||
|
||||
mutex_unlock(&s->mutex);
|
||||
}
|
||||
EXPORT_SYMBOL(amdtp_stream_stop);
|
||||
|
||||
/**
|
||||
* amdtp_stream_pcm_abort - abort the running PCM device
|
||||
|
@ -1078,3 +1134,92 @@ void amdtp_stream_pcm_abort(struct amdtp_stream *s)
|
|||
snd_pcm_stop_xrun(pcm);
|
||||
}
|
||||
EXPORT_SYMBOL(amdtp_stream_pcm_abort);
|
||||
|
||||
/**
|
||||
* amdtp_domain_init - initialize an AMDTP domain structure
|
||||
* @d: the AMDTP domain to initialize.
|
||||
*/
|
||||
int amdtp_domain_init(struct amdtp_domain *d)
|
||||
{
|
||||
INIT_LIST_HEAD(&d->streams);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(amdtp_domain_init);
|
||||
|
||||
/**
|
||||
* amdtp_domain_destroy - destroy an AMDTP domain structure
|
||||
* @d: the AMDTP domain to destroy.
|
||||
*/
|
||||
void amdtp_domain_destroy(struct amdtp_domain *d)
|
||||
{
|
||||
// At present nothing to do.
|
||||
return;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(amdtp_domain_destroy);
|
||||
|
||||
/**
|
||||
* amdtp_domain_add_stream - register isoc context into the domain.
|
||||
* @d: the AMDTP domain.
|
||||
* @s: the AMDTP stream.
|
||||
* @channel: the isochronous channel on the bus.
|
||||
* @speed: firewire speed code.
|
||||
*/
|
||||
int amdtp_domain_add_stream(struct amdtp_domain *d, struct amdtp_stream *s,
|
||||
int channel, int speed)
|
||||
{
|
||||
struct amdtp_stream *tmp;
|
||||
|
||||
list_for_each_entry(tmp, &d->streams, list) {
|
||||
if (s == tmp)
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
list_add(&s->list, &d->streams);
|
||||
|
||||
s->channel = channel;
|
||||
s->speed = speed;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(amdtp_domain_add_stream);
|
||||
|
||||
/**
|
||||
* amdtp_domain_start - start sending packets for isoc context in the domain.
|
||||
* @d: the AMDTP domain.
|
||||
*/
|
||||
int amdtp_domain_start(struct amdtp_domain *d)
|
||||
{
|
||||
struct amdtp_stream *s;
|
||||
int err = 0;
|
||||
|
||||
list_for_each_entry(s, &d->streams, list) {
|
||||
err = amdtp_stream_start(s, s->channel, s->speed);
|
||||
if (err < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (err < 0) {
|
||||
list_for_each_entry(s, &d->streams, list)
|
||||
amdtp_stream_stop(s);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(amdtp_domain_start);
|
||||
|
||||
/**
|
||||
* amdtp_domain_stop - stop sending packets for isoc context in the same domain.
|
||||
* @d: the AMDTP domain to which the isoc contexts belong.
|
||||
*/
|
||||
void amdtp_domain_stop(struct amdtp_domain *d)
|
||||
{
|
||||
struct amdtp_stream *s, *next;
|
||||
|
||||
list_for_each_entry_safe(s, next, &d->streams, list) {
|
||||
list_del(&s->list);
|
||||
|
||||
amdtp_stream_stop(s);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(amdtp_domain_stop);
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
* @CIP_HEADER_WITHOUT_EOH: Only for in-stream. CIP Header doesn't include
|
||||
* valid EOH.
|
||||
* @CIP_NO_HEADERS: a lack of headers in packets
|
||||
* @CIP_UNALIGHED_DBC: Only for in-stream. The value of dbc is not alighed to
|
||||
* the value of current SYT_INTERVAL; e.g. initial value is not zero.
|
||||
*/
|
||||
enum cip_flags {
|
||||
CIP_NONBLOCKING = 0x00,
|
||||
|
@ -45,6 +47,7 @@ enum cip_flags {
|
|||
CIP_JUMBO_PAYLOAD = 0x40,
|
||||
CIP_HEADER_WITHOUT_EOH = 0x80,
|
||||
CIP_NO_HEADER = 0x100,
|
||||
CIP_UNALIGHED_DBC = 0x200,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -91,12 +94,20 @@ enum amdtp_stream_direction {
|
|||
AMDTP_IN_STREAM
|
||||
};
|
||||
|
||||
struct pkt_desc {
|
||||
u32 cycle;
|
||||
u32 syt;
|
||||
unsigned int data_blocks;
|
||||
unsigned int data_block_counter;
|
||||
__be32 *ctx_payload;
|
||||
};
|
||||
|
||||
struct amdtp_stream;
|
||||
typedef unsigned int (*amdtp_stream_process_data_blocks_t)(
|
||||
typedef unsigned int (*amdtp_stream_process_ctx_payloads_t)(
|
||||
struct amdtp_stream *s,
|
||||
__be32 *buffer,
|
||||
unsigned int data_blocks,
|
||||
unsigned int *syt);
|
||||
const struct pkt_desc *desc,
|
||||
unsigned int packets,
|
||||
struct snd_pcm_substream *pcm);
|
||||
struct amdtp_stream {
|
||||
struct fw_unit *unit;
|
||||
enum cip_flags flags;
|
||||
|
@ -107,6 +118,7 @@ struct amdtp_stream {
|
|||
struct fw_iso_context *context;
|
||||
struct iso_packets_buffer buffer;
|
||||
int packet_index;
|
||||
struct pkt_desc *pkt_descs;
|
||||
int tag;
|
||||
union {
|
||||
struct {
|
||||
|
@ -119,8 +131,6 @@ struct amdtp_stream {
|
|||
// 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.
|
||||
|
@ -131,6 +141,7 @@ struct amdtp_stream {
|
|||
|
||||
// To generate CIP header.
|
||||
unsigned int fdf;
|
||||
int syt_override;
|
||||
} rx;
|
||||
} ctx_data;
|
||||
|
||||
|
@ -158,13 +169,18 @@ struct amdtp_stream {
|
|||
|
||||
/* For backends to process data blocks. */
|
||||
void *protocol;
|
||||
amdtp_stream_process_data_blocks_t process_data_blocks;
|
||||
amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
|
||||
|
||||
// For domain.
|
||||
int channel;
|
||||
int speed;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
enum amdtp_stream_direction dir, enum cip_flags flags,
|
||||
unsigned int fmt,
|
||||
amdtp_stream_process_data_blocks_t process_data_blocks,
|
||||
amdtp_stream_process_ctx_payloads_t process_ctx_payloads,
|
||||
unsigned int protocol_size);
|
||||
void amdtp_stream_destroy(struct amdtp_stream *s);
|
||||
|
||||
|
@ -172,9 +188,7 @@ int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate,
|
|||
unsigned int data_block_quadlets);
|
||||
unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s);
|
||||
|
||||
int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed);
|
||||
void amdtp_stream_update(struct amdtp_stream *s);
|
||||
void amdtp_stream_stop(struct amdtp_stream *s);
|
||||
|
||||
int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
|
||||
struct snd_pcm_runtime *runtime);
|
||||
|
@ -256,4 +270,17 @@ static inline bool amdtp_stream_wait_callback(struct amdtp_stream *s,
|
|||
msecs_to_jiffies(timeout)) > 0;
|
||||
}
|
||||
|
||||
struct amdtp_domain {
|
||||
struct list_head streams;
|
||||
};
|
||||
|
||||
int amdtp_domain_init(struct amdtp_domain *d);
|
||||
void amdtp_domain_destroy(struct amdtp_domain *d);
|
||||
|
||||
int amdtp_domain_add_stream(struct amdtp_domain *d, struct amdtp_stream *s,
|
||||
int channel, int speed);
|
||||
|
||||
int amdtp_domain_start(struct amdtp_domain *d);
|
||||
void amdtp_domain_stop(struct amdtp_domain *d);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -115,6 +115,8 @@ struct snd_bebob {
|
|||
|
||||
/* For BeBoB version quirk. */
|
||||
unsigned int version;
|
||||
|
||||
struct amdtp_domain domain;
|
||||
};
|
||||
|
||||
static inline int
|
||||
|
|
|
@ -445,10 +445,9 @@ start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream)
|
|||
goto end;
|
||||
}
|
||||
|
||||
/* start amdtp stream */
|
||||
err = amdtp_stream_start(stream,
|
||||
conn->resources.channel,
|
||||
conn->speed);
|
||||
// start amdtp stream.
|
||||
err = amdtp_domain_add_stream(&bebob->domain, stream,
|
||||
conn->resources.channel, conn->speed);
|
||||
end:
|
||||
return err;
|
||||
}
|
||||
|
@ -523,7 +522,13 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
|
|||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err = amdtp_domain_init(&bebob->domain);
|
||||
if (err < 0) {
|
||||
destroy_stream(bebob, &bebob->tx_stream);
|
||||
destroy_stream(bebob, &bebob->rx_stream);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int keep_resources(struct snd_bebob *bebob, struct amdtp_stream *stream,
|
||||
|
@ -566,9 +571,7 @@ int snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate)
|
|||
if (rate == 0)
|
||||
rate = curr_rate;
|
||||
if (curr_rate != rate) {
|
||||
amdtp_stream_stop(&bebob->tx_stream);
|
||||
amdtp_stream_stop(&bebob->rx_stream);
|
||||
|
||||
amdtp_domain_stop(&bebob->domain);
|
||||
break_both_connections(bebob);
|
||||
|
||||
cmp_connection_release(&bebob->out_conn);
|
||||
|
@ -620,9 +623,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob)
|
|||
// 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);
|
||||
|
||||
amdtp_domain_stop(&bebob->domain);
|
||||
break_both_connections(bebob);
|
||||
}
|
||||
|
||||
|
@ -640,11 +641,16 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob)
|
|||
return err;
|
||||
|
||||
err = start_stream(bebob, &bebob->rx_stream);
|
||||
if (err < 0) {
|
||||
dev_err(&bebob->unit->device,
|
||||
"fail to run AMDTP master stream:%d\n", err);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = start_stream(bebob, &bebob->tx_stream);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = amdtp_domain_start(&bebob->domain);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
// NOTE:
|
||||
// The firmware customized by M-Audio uses these commands to
|
||||
|
@ -660,21 +666,8 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob)
|
|||
}
|
||||
|
||||
if (!amdtp_stream_wait_callback(&bebob->rx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (!amdtp_stream_running(&bebob->tx_stream)) {
|
||||
err = start_stream(bebob, &bebob->tx_stream);
|
||||
if (err < 0) {
|
||||
dev_err(&bebob->unit->device,
|
||||
"fail to run AMDTP slave stream:%d\n", err);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!amdtp_stream_wait_callback(&bebob->tx_stream,
|
||||
CALLBACK_TIMEOUT) ||
|
||||
!amdtp_stream_wait_callback(&bebob->tx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
|
@ -683,8 +676,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob)
|
|||
|
||||
return 0;
|
||||
error:
|
||||
amdtp_stream_stop(&bebob->tx_stream);
|
||||
amdtp_stream_stop(&bebob->rx_stream);
|
||||
amdtp_domain_stop(&bebob->domain);
|
||||
break_both_connections(bebob);
|
||||
return err;
|
||||
}
|
||||
|
@ -692,9 +684,7 @@ error:
|
|||
void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob)
|
||||
{
|
||||
if (bebob->substreams_counter == 0) {
|
||||
amdtp_stream_stop(&bebob->rx_stream);
|
||||
amdtp_stream_stop(&bebob->tx_stream);
|
||||
|
||||
amdtp_domain_stop(&bebob->domain);
|
||||
break_both_connections(bebob);
|
||||
|
||||
cmp_connection_release(&bebob->out_conn);
|
||||
|
@ -708,6 +698,8 @@ void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob)
|
|||
*/
|
||||
void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob)
|
||||
{
|
||||
amdtp_domain_destroy(&bebob->domain);
|
||||
|
||||
destroy_stream(bebob, &bebob->tx_stream);
|
||||
destroy_stream(bebob, &bebob->rx_stream);
|
||||
}
|
||||
|
|
|
@ -154,14 +154,10 @@ 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));
|
||||
|
@ -297,10 +293,11 @@ int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate)
|
|||
if (dice->substreams_counter == 0 || curr_rate != rate) {
|
||||
struct reg_params tx_params, rx_params;
|
||||
|
||||
amdtp_domain_stop(&dice->domain);
|
||||
|
||||
err = get_register_params(dice, &tx_params, &rx_params);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
finish_session(dice, &tx_params, &rx_params);
|
||||
|
||||
release_resources(dice);
|
||||
|
@ -377,7 +374,8 @@ static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
|
|||
return err;
|
||||
}
|
||||
|
||||
err = amdtp_stream_start(stream, resources->channel, max_speed);
|
||||
err = amdtp_domain_add_stream(&dice->domain, stream,
|
||||
resources->channel, max_speed);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
@ -410,6 +408,7 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice)
|
|||
for (i = 0; i < MAX_STREAMS; ++i) {
|
||||
if (amdtp_streaming_error(&dice->tx_stream[i]) ||
|
||||
amdtp_streaming_error(&dice->rx_stream[i])) {
|
||||
amdtp_domain_stop(&dice->domain);
|
||||
finish_session(dice, &tx_params, &rx_params);
|
||||
break;
|
||||
}
|
||||
|
@ -456,6 +455,10 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice)
|
|||
goto error;
|
||||
}
|
||||
|
||||
err = amdtp_domain_start(&dice->domain);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
for (i = 0; i < MAX_STREAMS; i++) {
|
||||
if ((i < tx_params.count &&
|
||||
!amdtp_stream_wait_callback(&dice->tx_stream[i],
|
||||
|
@ -471,6 +474,7 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice)
|
|||
|
||||
return 0;
|
||||
error:
|
||||
amdtp_domain_stop(&dice->domain);
|
||||
finish_session(dice, &tx_params, &rx_params);
|
||||
return err;
|
||||
}
|
||||
|
@ -485,8 +489,10 @@ void snd_dice_stream_stop_duplex(struct snd_dice *dice)
|
|||
struct reg_params tx_params, rx_params;
|
||||
|
||||
if (dice->substreams_counter == 0) {
|
||||
if (get_register_params(dice, &tx_params, &rx_params) >= 0)
|
||||
if (get_register_params(dice, &tx_params, &rx_params) >= 0) {
|
||||
amdtp_domain_stop(&dice->domain);
|
||||
finish_session(dice, &tx_params, &rx_params);
|
||||
}
|
||||
|
||||
release_resources(dice);
|
||||
}
|
||||
|
@ -564,7 +570,15 @@ int snd_dice_stream_init_duplex(struct snd_dice *dice)
|
|||
destroy_stream(dice, AMDTP_OUT_STREAM, i);
|
||||
for (i = 0; i < MAX_STREAMS; i++)
|
||||
destroy_stream(dice, AMDTP_IN_STREAM, i);
|
||||
break;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
err = amdtp_domain_init(&dice->domain);
|
||||
if (err < 0) {
|
||||
for (i = 0; i < MAX_STREAMS; ++i) {
|
||||
destroy_stream(dice, AMDTP_OUT_STREAM, i);
|
||||
destroy_stream(dice, AMDTP_IN_STREAM, i);
|
||||
}
|
||||
}
|
||||
end:
|
||||
|
@ -579,6 +593,8 @@ void snd_dice_stream_destroy_duplex(struct snd_dice *dice)
|
|||
destroy_stream(dice, AMDTP_IN_STREAM, i);
|
||||
destroy_stream(dice, AMDTP_OUT_STREAM, i);
|
||||
}
|
||||
|
||||
amdtp_domain_destroy(&dice->domain);
|
||||
}
|
||||
|
||||
void snd_dice_stream_update_duplex(struct snd_dice *dice)
|
||||
|
@ -596,6 +612,8 @@ void snd_dice_stream_update_duplex(struct snd_dice *dice)
|
|||
dice->global_enabled = false;
|
||||
|
||||
if (get_register_params(dice, &tx_params, &rx_params) == 0) {
|
||||
amdtp_domain_stop(&dice->domain);
|
||||
|
||||
stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
|
||||
stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
|
||||
}
|
||||
|
|
|
@ -112,6 +112,8 @@ struct snd_dice {
|
|||
bool global_enabled;
|
||||
struct completion clock_accepted;
|
||||
unsigned int substreams_counter;
|
||||
|
||||
struct amdtp_domain domain;
|
||||
};
|
||||
|
||||
enum snd_dice_addr_type {
|
||||
|
|
|
@ -143,17 +143,23 @@ int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate,
|
|||
}
|
||||
|
||||
static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int frames)
|
||||
__be32 *buffer, unsigned int frames,
|
||||
unsigned int pcm_frames)
|
||||
{
|
||||
struct amdtp_dot *p = s->protocol;
|
||||
unsigned int channels = p->pcm_channels;
|
||||
struct snd_pcm_runtime *runtime = pcm->runtime;
|
||||
unsigned int channels, remaining_frames, i, c;
|
||||
unsigned int pcm_buffer_pointer;
|
||||
int remaining_frames;
|
||||
const u32 *src;
|
||||
int i, c;
|
||||
|
||||
pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
|
||||
pcm_buffer_pointer %= runtime->buffer_size;
|
||||
|
||||
channels = p->pcm_channels;
|
||||
src = (void *)runtime->dma_area +
|
||||
frames_to_bytes(runtime, s->pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
|
||||
frames_to_bytes(runtime, pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
|
||||
|
||||
buffer++;
|
||||
for (i = 0; i < frames; ++i) {
|
||||
|
@ -169,17 +175,23 @@ static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
|
|||
}
|
||||
|
||||
static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int frames)
|
||||
__be32 *buffer, unsigned int frames,
|
||||
unsigned int pcm_frames)
|
||||
{
|
||||
struct amdtp_dot *p = s->protocol;
|
||||
unsigned int channels = p->pcm_channels;
|
||||
struct snd_pcm_runtime *runtime = pcm->runtime;
|
||||
unsigned int channels, remaining_frames, i, c;
|
||||
unsigned int pcm_buffer_pointer;
|
||||
int remaining_frames;
|
||||
u32 *dst;
|
||||
int i, c;
|
||||
|
||||
pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
|
||||
pcm_buffer_pointer %= runtime->buffer_size;
|
||||
|
||||
channels = p->pcm_channels;
|
||||
dst = (void *)runtime->dma_area +
|
||||
frames_to_bytes(runtime, s->pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
|
||||
frames_to_bytes(runtime, pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
|
||||
|
||||
buffer++;
|
||||
for (i = 0; i < frames; ++i) {
|
||||
|
@ -234,7 +246,7 @@ static inline void midi_use_bytes(struct amdtp_stream *s,
|
|||
}
|
||||
|
||||
static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
|
||||
unsigned int data_blocks)
|
||||
unsigned int data_blocks, unsigned int data_block_counter)
|
||||
{
|
||||
struct amdtp_dot *p = s->protocol;
|
||||
unsigned int f, port;
|
||||
|
@ -242,7 +254,7 @@ static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
|
|||
u8 *b;
|
||||
|
||||
for (f = 0; f < data_blocks; f++) {
|
||||
port = (s->data_block_counter + f) % 8;
|
||||
port = (data_block_counter + f) % 8;
|
||||
b = (u8 *)&buffer[0];
|
||||
|
||||
len = 0;
|
||||
|
@ -329,66 +341,74 @@ void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port,
|
|||
WRITE_ONCE(p->midi[port], midi);
|
||||
}
|
||||
|
||||
static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
|
||||
__be32 *buffer,
|
||||
unsigned int data_blocks,
|
||||
unsigned int *syt)
|
||||
static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
|
||||
const struct pkt_desc *descs,
|
||||
unsigned int packets,
|
||||
struct snd_pcm_substream *pcm)
|
||||
{
|
||||
struct snd_pcm_substream *pcm;
|
||||
unsigned int pcm_frames;
|
||||
unsigned int pcm_frames = 0;
|
||||
int i;
|
||||
|
||||
pcm = READ_ONCE(s->pcm);
|
||||
if (pcm) {
|
||||
read_pcm_s32(s, pcm, buffer, data_blocks);
|
||||
pcm_frames = data_blocks;
|
||||
} else {
|
||||
pcm_frames = 0;
|
||||
for (i = 0; i < packets; ++i) {
|
||||
const struct pkt_desc *desc = descs + i;
|
||||
__be32 *buf = desc->ctx_payload;
|
||||
unsigned int data_blocks = desc->data_blocks;
|
||||
|
||||
if (pcm) {
|
||||
read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
|
||||
pcm_frames += data_blocks;
|
||||
}
|
||||
|
||||
read_midi_messages(s, buf, data_blocks);
|
||||
}
|
||||
|
||||
read_midi_messages(s, buffer, data_blocks);
|
||||
|
||||
return pcm_frames;
|
||||
}
|
||||
|
||||
static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
|
||||
__be32 *buffer,
|
||||
unsigned int data_blocks,
|
||||
unsigned int *syt)
|
||||
static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
|
||||
const struct pkt_desc *descs,
|
||||
unsigned int packets,
|
||||
struct snd_pcm_substream *pcm)
|
||||
{
|
||||
struct snd_pcm_substream *pcm;
|
||||
unsigned int pcm_frames;
|
||||
unsigned int pcm_frames = 0;
|
||||
int i;
|
||||
|
||||
pcm = READ_ONCE(s->pcm);
|
||||
if (pcm) {
|
||||
write_pcm_s32(s, pcm, buffer, data_blocks);
|
||||
pcm_frames = data_blocks;
|
||||
} else {
|
||||
write_pcm_silence(s, buffer, data_blocks);
|
||||
pcm_frames = 0;
|
||||
for (i = 0; i < packets; ++i) {
|
||||
const struct pkt_desc *desc = descs + i;
|
||||
__be32 *buf = desc->ctx_payload;
|
||||
unsigned int data_blocks = desc->data_blocks;
|
||||
|
||||
if (pcm) {
|
||||
write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
|
||||
pcm_frames += data_blocks;
|
||||
} else {
|
||||
write_pcm_silence(s, buf, data_blocks);
|
||||
}
|
||||
|
||||
write_midi_messages(s, buf, data_blocks,
|
||||
desc->data_block_counter);
|
||||
}
|
||||
|
||||
write_midi_messages(s, buffer, data_blocks);
|
||||
|
||||
return pcm_frames;
|
||||
}
|
||||
|
||||
int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
enum amdtp_stream_direction dir)
|
||||
{
|
||||
amdtp_stream_process_data_blocks_t process_data_blocks;
|
||||
amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
|
||||
enum cip_flags flags;
|
||||
|
||||
/* Use different mode between incoming/outgoing. */
|
||||
// Use different mode between incoming/outgoing.
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
flags = CIP_NONBLOCKING;
|
||||
process_data_blocks = process_tx_data_blocks;
|
||||
process_ctx_payloads = process_ir_ctx_payloads;
|
||||
} else {
|
||||
flags = CIP_BLOCKING;
|
||||
process_data_blocks = process_rx_data_blocks;
|
||||
process_ctx_payloads = process_it_ctx_payloads;
|
||||
}
|
||||
|
||||
return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM,
|
||||
process_data_blocks, sizeof(struct amdtp_dot));
|
||||
process_ctx_payloads, sizeof(struct amdtp_dot));
|
||||
}
|
||||
|
||||
void amdtp_dot_reset(struct amdtp_stream *s)
|
||||
|
|
|
@ -126,9 +126,6 @@ static void finish_session(struct snd_dg00x *dg00x)
|
|||
{
|
||||
__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,
|
||||
|
@ -218,29 +215,59 @@ static int keep_resources(struct snd_dg00x *dg00x, struct amdtp_stream *stream,
|
|||
fw_parent_device(dg00x->unit)->max_speed);
|
||||
}
|
||||
|
||||
static int init_stream(struct snd_dg00x *dg00x, struct amdtp_stream *s)
|
||||
{
|
||||
struct fw_iso_resources *resources;
|
||||
enum amdtp_stream_direction dir;
|
||||
int err;
|
||||
|
||||
if (s == &dg00x->tx_stream) {
|
||||
resources = &dg00x->tx_resources;
|
||||
dir = AMDTP_IN_STREAM;
|
||||
} else {
|
||||
resources = &dg00x->rx_resources;
|
||||
dir = AMDTP_OUT_STREAM;
|
||||
}
|
||||
|
||||
err = fw_iso_resources_init(resources, dg00x->unit);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = amdtp_dot_init(s, dg00x->unit, dir);
|
||||
if (err < 0)
|
||||
fw_iso_resources_destroy(resources);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void destroy_stream(struct snd_dg00x *dg00x, struct amdtp_stream *s)
|
||||
{
|
||||
amdtp_stream_destroy(s);
|
||||
|
||||
if (s == &dg00x->tx_stream)
|
||||
fw_iso_resources_destroy(&dg00x->tx_resources);
|
||||
else
|
||||
fw_iso_resources_destroy(&dg00x->rx_resources);
|
||||
}
|
||||
|
||||
int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* For out-stream. */
|
||||
err = fw_iso_resources_init(&dg00x->rx_resources, dg00x->unit);
|
||||
err = init_stream(dg00x, &dg00x->rx_stream);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
err = amdtp_dot_init(&dg00x->rx_stream, dg00x->unit, AMDTP_OUT_STREAM);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
return err;
|
||||
|
||||
/* For in-stream. */
|
||||
err = fw_iso_resources_init(&dg00x->tx_resources, dg00x->unit);
|
||||
err = init_stream(dg00x, &dg00x->tx_stream);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
err = amdtp_dot_init(&dg00x->tx_stream, dg00x->unit, AMDTP_IN_STREAM);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
destroy_stream(dg00x, &dg00x->rx_stream);
|
||||
|
||||
err = amdtp_domain_init(&dg00x->domain);
|
||||
if (err < 0) {
|
||||
destroy_stream(dg00x, &dg00x->rx_stream);
|
||||
destroy_stream(dg00x, &dg00x->tx_stream);
|
||||
}
|
||||
|
||||
return 0;
|
||||
error:
|
||||
snd_dg00x_stream_destroy_duplex(dg00x);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -250,11 +277,10 @@ error:
|
|||
*/
|
||||
void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x)
|
||||
{
|
||||
amdtp_stream_destroy(&dg00x->rx_stream);
|
||||
fw_iso_resources_destroy(&dg00x->rx_resources);
|
||||
amdtp_domain_destroy(&dg00x->domain);
|
||||
|
||||
amdtp_stream_destroy(&dg00x->tx_stream);
|
||||
fw_iso_resources_destroy(&dg00x->tx_resources);
|
||||
destroy_stream(dg00x, &dg00x->rx_stream);
|
||||
destroy_stream(dg00x, &dg00x->tx_stream);
|
||||
}
|
||||
|
||||
int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate)
|
||||
|
@ -269,6 +295,8 @@ int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate)
|
|||
rate = curr_rate;
|
||||
|
||||
if (dg00x->substreams_counter == 0 || curr_rate != rate) {
|
||||
amdtp_domain_stop(&dg00x->domain);
|
||||
|
||||
finish_session(dg00x);
|
||||
|
||||
fw_iso_resources_free(&dg00x->tx_resources);
|
||||
|
@ -301,8 +329,10 @@ int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x)
|
|||
return 0;
|
||||
|
||||
if (amdtp_streaming_error(&dg00x->tx_stream) ||
|
||||
amdtp_streaming_error(&dg00x->rx_stream))
|
||||
amdtp_streaming_error(&dg00x->rx_stream)) {
|
||||
amdtp_domain_stop(&dg00x->domain);
|
||||
finish_session(dg00x);
|
||||
}
|
||||
|
||||
if (generation != fw_parent_device(dg00x->unit)->card->generation) {
|
||||
err = fw_iso_resources_update(&dg00x->tx_resources);
|
||||
|
@ -319,36 +349,30 @@ int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x)
|
|||
* which source of clock is used.
|
||||
*/
|
||||
if (!amdtp_stream_running(&dg00x->rx_stream)) {
|
||||
int spd = fw_parent_device(dg00x->unit)->max_speed;
|
||||
|
||||
err = begin_session(dg00x);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = amdtp_stream_start(&dg00x->rx_stream,
|
||||
dg00x->rx_resources.channel,
|
||||
fw_parent_device(dg00x->unit)->max_speed);
|
||||
err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->rx_stream,
|
||||
dg00x->rx_resources.channel, spd);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->tx_stream,
|
||||
dg00x->tx_resources.channel, spd);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = amdtp_domain_start(&dg00x->domain);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
if (!amdtp_stream_wait_callback(&dg00x->rx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The value of SYT field in transmitted packets is always 0x0000. Thus,
|
||||
* duplex streams with timestamp synchronization cannot be built.
|
||||
*/
|
||||
if (!amdtp_stream_running(&dg00x->tx_stream)) {
|
||||
err = amdtp_stream_start(&dg00x->tx_stream,
|
||||
dg00x->tx_resources.channel,
|
||||
fw_parent_device(dg00x->unit)->max_speed);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
if (!amdtp_stream_wait_callback(&dg00x->tx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
CALLBACK_TIMEOUT) ||
|
||||
!amdtp_stream_wait_callback(&dg00x->tx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
|
@ -356,6 +380,7 @@ int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x)
|
|||
|
||||
return 0;
|
||||
error:
|
||||
amdtp_domain_stop(&dg00x->domain);
|
||||
finish_session(dg00x);
|
||||
|
||||
return err;
|
||||
|
@ -364,6 +389,7 @@ error:
|
|||
void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x)
|
||||
{
|
||||
if (dg00x->substreams_counter == 0) {
|
||||
amdtp_domain_stop(&dg00x->domain);
|
||||
finish_session(dg00x);
|
||||
|
||||
fw_iso_resources_free(&dg00x->tx_resources);
|
||||
|
|
|
@ -59,6 +59,8 @@ struct snd_dg00x {
|
|||
|
||||
/* Console models have additional MIDI ports for control surface. */
|
||||
bool is_console;
|
||||
|
||||
struct amdtp_domain domain;
|
||||
};
|
||||
|
||||
#define DG00X_ADDR_BASE 0xffffe0000000ull
|
||||
|
|
|
@ -27,19 +27,24 @@ int amdtp_ff_set_parameters(struct amdtp_stream *s, unsigned int rate,
|
|||
return amdtp_stream_set_parameters(s, rate, data_channels);
|
||||
}
|
||||
|
||||
static void write_pcm_s32(struct amdtp_stream *s,
|
||||
struct snd_pcm_substream *pcm,
|
||||
__le32 *buffer, unsigned int frames)
|
||||
static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
|
||||
__le32 *buffer, unsigned int frames,
|
||||
unsigned int pcm_frames)
|
||||
{
|
||||
struct amdtp_ff *p = s->protocol;
|
||||
unsigned int channels = p->pcm_channels;
|
||||
struct snd_pcm_runtime *runtime = pcm->runtime;
|
||||
unsigned int channels, remaining_frames, i, c;
|
||||
unsigned int pcm_buffer_pointer;
|
||||
int remaining_frames;
|
||||
const u32 *src;
|
||||
int i, c;
|
||||
|
||||
pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
|
||||
pcm_buffer_pointer %= runtime->buffer_size;
|
||||
|
||||
channels = p->pcm_channels;
|
||||
src = (void *)runtime->dma_area +
|
||||
frames_to_bytes(runtime, s->pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
|
||||
frames_to_bytes(runtime, pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
|
||||
|
||||
for (i = 0; i < frames; ++i) {
|
||||
for (c = 0; c < channels; ++c) {
|
||||
|
@ -52,19 +57,24 @@ static void write_pcm_s32(struct amdtp_stream *s,
|
|||
}
|
||||
}
|
||||
|
||||
static void read_pcm_s32(struct amdtp_stream *s,
|
||||
struct snd_pcm_substream *pcm,
|
||||
__le32 *buffer, unsigned int frames)
|
||||
static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
|
||||
__le32 *buffer, unsigned int frames,
|
||||
unsigned int pcm_frames)
|
||||
{
|
||||
struct amdtp_ff *p = s->protocol;
|
||||
unsigned int channels = p->pcm_channels;
|
||||
struct snd_pcm_runtime *runtime = pcm->runtime;
|
||||
unsigned int channels, remaining_frames, i, c;
|
||||
unsigned int pcm_buffer_pointer;
|
||||
int remaining_frames;
|
||||
u32 *dst;
|
||||
int i, c;
|
||||
|
||||
pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
|
||||
pcm_buffer_pointer %= runtime->buffer_size;
|
||||
|
||||
channels = p->pcm_channels;
|
||||
dst = (void *)runtime->dma_area +
|
||||
frames_to_bytes(runtime, s->pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
|
||||
frames_to_bytes(runtime, pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
|
||||
|
||||
for (i = 0; i < frames; ++i) {
|
||||
for (c = 0; c < channels; ++c) {
|
||||
|
@ -102,38 +112,47 @@ int amdtp_ff_add_pcm_hw_constraints(struct amdtp_stream *s,
|
|||
return amdtp_stream_add_pcm_hw_constraints(s, runtime);
|
||||
}
|
||||
|
||||
static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
|
||||
__be32 *buffer,
|
||||
unsigned int data_blocks,
|
||||
unsigned int *syt)
|
||||
static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
|
||||
const struct pkt_desc *descs,
|
||||
unsigned int packets,
|
||||
struct snd_pcm_substream *pcm)
|
||||
{
|
||||
struct snd_pcm_substream *pcm = READ_ONCE(s->pcm);
|
||||
unsigned int pcm_frames;
|
||||
unsigned int pcm_frames = 0;
|
||||
int i;
|
||||
|
||||
if (pcm) {
|
||||
write_pcm_s32(s, pcm, (__le32 *)buffer, data_blocks);
|
||||
pcm_frames = data_blocks;
|
||||
} else {
|
||||
write_pcm_silence(s, (__le32 *)buffer, data_blocks);
|
||||
pcm_frames = 0;
|
||||
for (i = 0; i < packets; ++i) {
|
||||
const struct pkt_desc *desc = descs + i;
|
||||
__le32 *buf = (__le32 *)desc->ctx_payload;
|
||||
unsigned int data_blocks = desc->data_blocks;
|
||||
|
||||
if (pcm) {
|
||||
write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
|
||||
pcm_frames += data_blocks;
|
||||
} else {
|
||||
write_pcm_silence(s, buf, data_blocks);
|
||||
}
|
||||
}
|
||||
|
||||
return pcm_frames;
|
||||
}
|
||||
|
||||
static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
|
||||
__be32 *buffer,
|
||||
unsigned int data_blocks,
|
||||
unsigned int *syt)
|
||||
static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
|
||||
const struct pkt_desc *descs,
|
||||
unsigned int packets,
|
||||
struct snd_pcm_substream *pcm)
|
||||
{
|
||||
struct snd_pcm_substream *pcm = READ_ONCE(s->pcm);
|
||||
unsigned int pcm_frames;
|
||||
unsigned int pcm_frames = 0;
|
||||
int i;
|
||||
|
||||
if (pcm) {
|
||||
read_pcm_s32(s, pcm, (__le32 *)buffer, data_blocks);
|
||||
pcm_frames = data_blocks;
|
||||
} else {
|
||||
pcm_frames = 0;
|
||||
for (i = 0; i < packets; ++i) {
|
||||
const struct pkt_desc *desc = descs + i;
|
||||
__le32 *buf = (__le32 *)desc->ctx_payload;
|
||||
unsigned int data_blocks = desc->data_blocks;
|
||||
|
||||
if (pcm) {
|
||||
read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
|
||||
pcm_frames += data_blocks;
|
||||
}
|
||||
}
|
||||
|
||||
return pcm_frames;
|
||||
|
@ -142,13 +161,13 @@ static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
|
|||
int amdtp_ff_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
enum amdtp_stream_direction dir)
|
||||
{
|
||||
amdtp_stream_process_data_blocks_t process_data_blocks;
|
||||
amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
|
||||
|
||||
if (dir == AMDTP_IN_STREAM)
|
||||
process_data_blocks = process_tx_data_blocks;
|
||||
process_ctx_payloads = process_ir_ctx_payloads;
|
||||
else
|
||||
process_data_blocks = process_rx_data_blocks;
|
||||
process_ctx_payloads = process_it_ctx_payloads;
|
||||
|
||||
return amdtp_stream_init(s, unit, dir, CIP_NO_HEADER, 0,
|
||||
process_data_blocks, sizeof(struct amdtp_ff));
|
||||
process_ctx_payloads, sizeof(struct amdtp_ff));
|
||||
}
|
||||
|
|
|
@ -32,61 +32,65 @@ int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc,
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
static int init_stream(struct snd_ff *ff, enum amdtp_stream_direction dir)
|
||||
static int init_stream(struct snd_ff *ff, struct amdtp_stream *s)
|
||||
{
|
||||
int err;
|
||||
struct fw_iso_resources *resources;
|
||||
struct amdtp_stream *stream;
|
||||
enum amdtp_stream_direction dir;
|
||||
int err;
|
||||
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
if (s == &ff->tx_stream) {
|
||||
resources = &ff->tx_resources;
|
||||
stream = &ff->tx_stream;
|
||||
dir = AMDTP_IN_STREAM;
|
||||
} else {
|
||||
resources = &ff->rx_resources;
|
||||
stream = &ff->rx_stream;
|
||||
dir = AMDTP_OUT_STREAM;
|
||||
}
|
||||
|
||||
err = fw_iso_resources_init(resources, ff->unit);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = amdtp_ff_init(stream, ff->unit, dir);
|
||||
err = amdtp_ff_init(s, ff->unit, dir);
|
||||
if (err < 0)
|
||||
fw_iso_resources_destroy(resources);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void destroy_stream(struct snd_ff *ff, enum amdtp_stream_direction dir)
|
||||
static void destroy_stream(struct snd_ff *ff, struct amdtp_stream *s)
|
||||
{
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
amdtp_stream_destroy(&ff->tx_stream);
|
||||
amdtp_stream_destroy(s);
|
||||
|
||||
if (s == &ff->tx_stream)
|
||||
fw_iso_resources_destroy(&ff->tx_resources);
|
||||
} else {
|
||||
amdtp_stream_destroy(&ff->rx_stream);
|
||||
else
|
||||
fw_iso_resources_destroy(&ff->rx_resources);
|
||||
}
|
||||
}
|
||||
|
||||
int snd_ff_stream_init_duplex(struct snd_ff *ff)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = init_stream(ff, AMDTP_OUT_STREAM);
|
||||
err = init_stream(ff, &ff->rx_stream);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
return err;
|
||||
|
||||
err = init_stream(ff, &ff->tx_stream);
|
||||
if (err < 0) {
|
||||
destroy_stream(ff, &ff->rx_stream);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = amdtp_domain_init(&ff->domain);
|
||||
if (err < 0) {
|
||||
destroy_stream(ff, &ff->rx_stream);
|
||||
destroy_stream(ff, &ff->tx_stream);
|
||||
}
|
||||
|
||||
err = init_stream(ff, AMDTP_IN_STREAM);
|
||||
if (err < 0)
|
||||
destroy_stream(ff, AMDTP_OUT_STREAM);
|
||||
end:
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -96,8 +100,10 @@ end:
|
|||
*/
|
||||
void snd_ff_stream_destroy_duplex(struct snd_ff *ff)
|
||||
{
|
||||
destroy_stream(ff, AMDTP_IN_STREAM);
|
||||
destroy_stream(ff, AMDTP_OUT_STREAM);
|
||||
amdtp_domain_destroy(&ff->domain);
|
||||
|
||||
destroy_stream(ff, &ff->rx_stream);
|
||||
destroy_stream(ff, &ff->tx_stream);
|
||||
}
|
||||
|
||||
int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate)
|
||||
|
@ -114,6 +120,7 @@ int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate)
|
|||
enum snd_ff_stream_mode mode;
|
||||
int i;
|
||||
|
||||
amdtp_domain_stop(&ff->domain);
|
||||
finish_session(ff);
|
||||
|
||||
fw_iso_resources_free(&ff->tx_resources);
|
||||
|
@ -156,25 +163,39 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
|
|||
return 0;
|
||||
|
||||
if (amdtp_streaming_error(&ff->tx_stream) ||
|
||||
amdtp_streaming_error(&ff->rx_stream))
|
||||
amdtp_streaming_error(&ff->rx_stream)) {
|
||||
amdtp_domain_stop(&ff->domain);
|
||||
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)) {
|
||||
int spd = fw_parent_device(ff->unit)->max_speed;
|
||||
|
||||
err = ff->spec->protocol->begin_session(ff, rate);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = amdtp_stream_start(&ff->rx_stream,
|
||||
ff->rx_resources.channel,
|
||||
fw_parent_device(ff->unit)->max_speed);
|
||||
err = amdtp_domain_add_stream(&ff->domain, &ff->rx_stream,
|
||||
ff->rx_resources.channel, spd);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = amdtp_domain_add_stream(&ff->domain, &ff->tx_stream,
|
||||
ff->tx_resources.channel, spd);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = amdtp_domain_start(&ff->domain);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
if (!amdtp_stream_wait_callback(&ff->rx_stream,
|
||||
CALLBACK_TIMEOUT_MS) ||
|
||||
!amdtp_stream_wait_callback(&ff->tx_stream,
|
||||
CALLBACK_TIMEOUT_MS)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
|
@ -185,22 +206,9 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
|
|||
goto error;
|
||||
}
|
||||
|
||||
if (!amdtp_stream_running(&ff->tx_stream)) {
|
||||
err = amdtp_stream_start(&ff->tx_stream,
|
||||
ff->tx_resources.channel,
|
||||
fw_parent_device(ff->unit)->max_speed);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
if (!amdtp_stream_wait_callback(&ff->tx_stream,
|
||||
CALLBACK_TIMEOUT_MS)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
error:
|
||||
amdtp_domain_stop(&ff->domain);
|
||||
finish_session(ff);
|
||||
|
||||
return err;
|
||||
|
@ -209,6 +217,7 @@ error:
|
|||
void snd_ff_stream_stop_duplex(struct snd_ff *ff)
|
||||
{
|
||||
if (ff->substreams_counter == 0) {
|
||||
amdtp_domain_stop(&ff->domain);
|
||||
finish_session(ff);
|
||||
|
||||
fw_iso_resources_free(&ff->tx_resources);
|
||||
|
@ -218,12 +227,11 @@ void snd_ff_stream_stop_duplex(struct snd_ff *ff)
|
|||
|
||||
void snd_ff_stream_update_duplex(struct snd_ff *ff)
|
||||
{
|
||||
amdtp_domain_stop(&ff->domain);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
void snd_ff_stream_lock_changed(struct snd_ff *ff)
|
||||
|
|
|
@ -91,6 +91,8 @@ struct snd_ff {
|
|||
int dev_lock_count;
|
||||
bool dev_lock_changed;
|
||||
wait_queue_head_t hwdep_wait;
|
||||
|
||||
struct amdtp_domain domain;
|
||||
};
|
||||
|
||||
enum snd_ff_clock_src {
|
||||
|
|
|
@ -107,6 +107,8 @@ struct snd_efw {
|
|||
u8 *resp_buf;
|
||||
u8 *pull_ptr;
|
||||
u8 *push_ptr;
|
||||
|
||||
struct amdtp_domain domain;
|
||||
};
|
||||
|
||||
int snd_efw_transaction_cmd(struct fw_unit *unit,
|
||||
|
|
|
@ -8,8 +8,7 @@
|
|||
|
||||
#define CALLBACK_TIMEOUT 100
|
||||
|
||||
static int
|
||||
init_stream(struct snd_efw *efw, struct amdtp_stream *stream)
|
||||
static int init_stream(struct snd_efw *efw, struct amdtp_stream *stream)
|
||||
{
|
||||
struct cmp_connection *conn;
|
||||
enum cmp_direction c_dir;
|
||||
|
@ -28,28 +27,40 @@ init_stream(struct snd_efw *efw, struct amdtp_stream *stream)
|
|||
|
||||
err = cmp_connection_init(conn, efw->unit, c_dir, 0);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
return err;
|
||||
|
||||
err = amdtp_am824_init(stream, efw->unit, s_dir, CIP_BLOCKING);
|
||||
if (err < 0) {
|
||||
amdtp_stream_destroy(stream);
|
||||
cmp_connection_destroy(conn);
|
||||
return err;
|
||||
}
|
||||
end:
|
||||
|
||||
if (stream == &efw->tx_stream) {
|
||||
// Fireworks transmits NODATA packets with TAG0.
|
||||
efw->tx_stream.flags |= CIP_EMPTY_WITH_TAG0;
|
||||
// Fireworks has its own meaning for dbc.
|
||||
efw->tx_stream.flags |= CIP_DBC_IS_END_EVENT;
|
||||
// Fireworks reset dbc at bus reset.
|
||||
efw->tx_stream.flags |= CIP_SKIP_DBC_ZERO_CHECK;
|
||||
// But Recent firmwares starts packets with non-zero dbc.
|
||||
// Driver version 5.7.6 installs firmware version 5.7.3.
|
||||
if (efw->is_fireworks3 &&
|
||||
(efw->firmware_version == 0x5070000 ||
|
||||
efw->firmware_version == 0x5070300 ||
|
||||
efw->firmware_version == 0x5080000))
|
||||
efw->tx_stream.flags |= CIP_UNALIGHED_DBC;
|
||||
// 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.ctx_data.tx.dbc_interval = 8;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void
|
||||
stop_stream(struct snd_efw *efw, struct amdtp_stream *stream)
|
||||
{
|
||||
amdtp_stream_stop(stream);
|
||||
|
||||
if (stream == &efw->tx_stream)
|
||||
cmp_connection_break(&efw->out_conn);
|
||||
else
|
||||
cmp_connection_break(&efw->in_conn);
|
||||
}
|
||||
|
||||
static int start_stream(struct snd_efw *efw, struct amdtp_stream *stream,
|
||||
unsigned int rate)
|
||||
{
|
||||
|
@ -67,38 +78,26 @@ static int start_stream(struct snd_efw *efw, struct amdtp_stream *stream,
|
|||
return err;
|
||||
|
||||
// Start amdtp stream.
|
||||
err = amdtp_stream_start(stream, conn->resources.channel, conn->speed);
|
||||
err = amdtp_domain_add_stream(&efw->domain, stream,
|
||||
conn->resources.channel, conn->speed);
|
||||
if (err < 0) {
|
||||
cmp_connection_break(conn);
|
||||
return err;
|
||||
}
|
||||
|
||||
// Wait first callback.
|
||||
if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) {
|
||||
amdtp_stream_stop(stream);
|
||||
cmp_connection_break(conn);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function should be called before starting the stream or after stopping
|
||||
* the streams.
|
||||
*/
|
||||
static void
|
||||
destroy_stream(struct snd_efw *efw, struct amdtp_stream *stream)
|
||||
// This function should be called before starting the stream or after stopping
|
||||
// the streams.
|
||||
static void destroy_stream(struct snd_efw *efw, struct amdtp_stream *stream)
|
||||
{
|
||||
struct cmp_connection *conn;
|
||||
amdtp_stream_destroy(stream);
|
||||
|
||||
if (stream == &efw->tx_stream)
|
||||
conn = &efw->out_conn;
|
||||
cmp_connection_destroy(&efw->out_conn);
|
||||
else
|
||||
conn = &efw->in_conn;
|
||||
|
||||
amdtp_stream_destroy(stream);
|
||||
cmp_connection_destroy(conn);
|
||||
cmp_connection_destroy(&efw->in_conn);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -131,42 +130,28 @@ int snd_efw_stream_init_duplex(struct snd_efw *efw)
|
|||
|
||||
err = init_stream(efw, &efw->tx_stream);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
/* Fireworks transmits NODATA packets with TAG0. */
|
||||
efw->tx_stream.flags |= CIP_EMPTY_WITH_TAG0;
|
||||
/* Fireworks has its own meaning for dbc. */
|
||||
efw->tx_stream.flags |= CIP_DBC_IS_END_EVENT;
|
||||
/* Fireworks reset dbc at bus reset. */
|
||||
efw->tx_stream.flags |= CIP_SKIP_DBC_ZERO_CHECK;
|
||||
/*
|
||||
* But Recent firmwares starts packets with non-zero dbc.
|
||||
* Driver version 5.7.6 installs firmware version 5.7.3.
|
||||
*/
|
||||
if (efw->is_fireworks3 &&
|
||||
(efw->firmware_version == 0x5070000 ||
|
||||
efw->firmware_version == 0x5070300 ||
|
||||
efw->firmware_version == 0x5080000))
|
||||
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.ctx_data.tx.dbc_interval = 8;
|
||||
return err;
|
||||
|
||||
err = init_stream(efw, &efw->rx_stream);
|
||||
if (err < 0) {
|
||||
destroy_stream(efw, &efw->tx_stream);
|
||||
goto end;
|
||||
return err;
|
||||
}
|
||||
|
||||
/* set IEC61883 compliant mode (actually not fully compliant...) */
|
||||
err = amdtp_domain_init(&efw->domain);
|
||||
if (err < 0) {
|
||||
destroy_stream(efw, &efw->tx_stream);
|
||||
destroy_stream(efw, &efw->rx_stream);
|
||||
return err;
|
||||
}
|
||||
|
||||
// set IEC61883 compliant mode (actually not fully compliant...).
|
||||
err = snd_efw_command_set_tx_mode(efw, SND_EFW_TRANSPORT_MODE_IEC61883);
|
||||
if (err < 0) {
|
||||
destroy_stream(efw, &efw->tx_stream);
|
||||
destroy_stream(efw, &efw->rx_stream);
|
||||
}
|
||||
end:
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -214,8 +199,10 @@ int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate)
|
|||
if (rate == 0)
|
||||
rate = curr_rate;
|
||||
if (rate != curr_rate) {
|
||||
stop_stream(efw, &efw->tx_stream);
|
||||
stop_stream(efw, &efw->rx_stream);
|
||||
amdtp_domain_stop(&efw->domain);
|
||||
|
||||
cmp_connection_break(&efw->out_conn);
|
||||
cmp_connection_break(&efw->in_conn);
|
||||
|
||||
cmp_connection_release(&efw->out_conn);
|
||||
cmp_connection_release(&efw->in_conn);
|
||||
|
@ -255,47 +242,57 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw)
|
|||
if (efw->substreams_counter == 0)
|
||||
return -EIO;
|
||||
|
||||
if (amdtp_streaming_error(&efw->rx_stream) ||
|
||||
amdtp_streaming_error(&efw->tx_stream)) {
|
||||
amdtp_domain_stop(&efw->domain);
|
||||
cmp_connection_break(&efw->out_conn);
|
||||
cmp_connection_break(&efw->in_conn);
|
||||
}
|
||||
|
||||
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 = start_stream(efw, &efw->rx_stream, rate);
|
||||
if (err < 0) {
|
||||
dev_err(&efw->unit->device,
|
||||
"fail to start AMDTP master stream:%d\n", err);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = amdtp_domain_start(&efw->domain);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
// Wait first callback.
|
||||
if (!amdtp_stream_wait_callback(&efw->rx_stream,
|
||||
CALLBACK_TIMEOUT) ||
|
||||
!amdtp_stream_wait_callback(&efw->tx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
error:
|
||||
stop_stream(efw, &efw->rx_stream);
|
||||
stop_stream(efw, &efw->tx_stream);
|
||||
amdtp_domain_stop(&efw->domain);
|
||||
|
||||
cmp_connection_break(&efw->out_conn);
|
||||
cmp_connection_break(&efw->in_conn);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void snd_efw_stream_stop_duplex(struct snd_efw *efw)
|
||||
{
|
||||
if (efw->substreams_counter == 0) {
|
||||
stop_stream(efw, &efw->tx_stream);
|
||||
stop_stream(efw, &efw->rx_stream);
|
||||
amdtp_domain_stop(&efw->domain);
|
||||
|
||||
cmp_connection_break(&efw->out_conn);
|
||||
cmp_connection_break(&efw->in_conn);
|
||||
|
||||
cmp_connection_release(&efw->out_conn);
|
||||
cmp_connection_release(&efw->in_conn);
|
||||
|
@ -304,18 +301,19 @@ void snd_efw_stream_stop_duplex(struct snd_efw *efw)
|
|||
|
||||
void snd_efw_stream_update_duplex(struct snd_efw *efw)
|
||||
{
|
||||
if (cmp_connection_update(&efw->out_conn) < 0 ||
|
||||
cmp_connection_update(&efw->in_conn) < 0) {
|
||||
stop_stream(efw, &efw->rx_stream);
|
||||
stop_stream(efw, &efw->tx_stream);
|
||||
} else {
|
||||
amdtp_stream_update(&efw->rx_stream);
|
||||
amdtp_stream_update(&efw->tx_stream);
|
||||
}
|
||||
amdtp_domain_stop(&efw->domain);
|
||||
|
||||
cmp_connection_break(&efw->out_conn);
|
||||
cmp_connection_break(&efw->in_conn);
|
||||
|
||||
amdtp_stream_pcm_abort(&efw->rx_stream);
|
||||
amdtp_stream_pcm_abort(&efw->tx_stream);
|
||||
}
|
||||
|
||||
void snd_efw_stream_destroy_duplex(struct snd_efw *efw)
|
||||
{
|
||||
amdtp_domain_destroy(&efw->domain);
|
||||
|
||||
destroy_stream(efw, &efw->rx_stream);
|
||||
destroy_stream(efw, &efw->tx_stream);
|
||||
}
|
||||
|
|
|
@ -117,19 +117,25 @@ int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void read_pcm_s32(struct amdtp_stream *s,
|
||||
struct snd_pcm_runtime *runtime,
|
||||
__be32 *buffer, unsigned int data_blocks)
|
||||
static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int data_blocks,
|
||||
unsigned int pcm_frames)
|
||||
{
|
||||
struct amdtp_motu *p = s->protocol;
|
||||
unsigned int channels, remaining_frames, i, c;
|
||||
unsigned int channels = p->pcm_chunks;
|
||||
struct snd_pcm_runtime *runtime = pcm->runtime;
|
||||
unsigned int pcm_buffer_pointer;
|
||||
int remaining_frames;
|
||||
u8 *byte;
|
||||
u32 *dst;
|
||||
int i, c;
|
||||
|
||||
pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
|
||||
pcm_buffer_pointer %= runtime->buffer_size;
|
||||
|
||||
channels = p->pcm_chunks;
|
||||
dst = (void *)runtime->dma_area +
|
||||
frames_to_bytes(runtime, s->pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
|
||||
frames_to_bytes(runtime, pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
|
||||
|
||||
for (i = 0; i < data_blocks; ++i) {
|
||||
byte = (u8 *)buffer + p->pcm_byte_offset;
|
||||
|
@ -147,19 +153,25 @@ static void read_pcm_s32(struct amdtp_stream *s,
|
|||
}
|
||||
}
|
||||
|
||||
static void write_pcm_s32(struct amdtp_stream *s,
|
||||
struct snd_pcm_runtime *runtime,
|
||||
__be32 *buffer, unsigned int data_blocks)
|
||||
static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int data_blocks,
|
||||
unsigned int pcm_frames)
|
||||
{
|
||||
struct amdtp_motu *p = s->protocol;
|
||||
unsigned int channels, remaining_frames, i, c;
|
||||
unsigned int channels = p->pcm_chunks;
|
||||
struct snd_pcm_runtime *runtime = pcm->runtime;
|
||||
unsigned int pcm_buffer_pointer;
|
||||
int remaining_frames;
|
||||
u8 *byte;
|
||||
const u32 *src;
|
||||
int i, c;
|
||||
|
||||
pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
|
||||
pcm_buffer_pointer %= runtime->buffer_size;
|
||||
|
||||
channels = p->pcm_chunks;
|
||||
src = (void *)runtime->dma_area +
|
||||
frames_to_bytes(runtime, s->pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
|
||||
frames_to_bytes(runtime, pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
|
||||
|
||||
for (i = 0; i < data_blocks; ++i) {
|
||||
byte = (u8 *)buffer + p->pcm_byte_offset;
|
||||
|
@ -298,24 +310,52 @@ static void __maybe_unused copy_message(u64 *frames, __be32 *buffer,
|
|||
}
|
||||
}
|
||||
|
||||
static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
|
||||
__be32 *buffer, unsigned int data_blocks,
|
||||
unsigned int *syt)
|
||||
static void probe_tracepoints_events(struct amdtp_stream *s,
|
||||
const struct pkt_desc *descs,
|
||||
unsigned int packets)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < packets; ++i) {
|
||||
const struct pkt_desc *desc = descs + i;
|
||||
__be32 *buf = desc->ctx_payload;
|
||||
unsigned int data_blocks = desc->data_blocks;
|
||||
|
||||
trace_data_block_sph(s, data_blocks, buf);
|
||||
trace_data_block_message(s, data_blocks, buf);
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
|
||||
const struct pkt_desc *descs,
|
||||
unsigned int packets,
|
||||
struct snd_pcm_substream *pcm)
|
||||
{
|
||||
struct amdtp_motu *p = s->protocol;
|
||||
struct snd_pcm_substream *pcm;
|
||||
unsigned int pcm_frames = 0;
|
||||
int i;
|
||||
|
||||
trace_data_block_sph(s, data_blocks, buffer);
|
||||
trace_data_block_message(s, data_blocks, buffer);
|
||||
// For data block processing.
|
||||
for (i = 0; i < packets; ++i) {
|
||||
const struct pkt_desc *desc = descs + i;
|
||||
__be32 *buf = desc->ctx_payload;
|
||||
unsigned int data_blocks = desc->data_blocks;
|
||||
|
||||
if (p->midi_ports)
|
||||
read_midi_messages(s, buffer, data_blocks);
|
||||
if (pcm) {
|
||||
read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
|
||||
pcm_frames += data_blocks;
|
||||
}
|
||||
|
||||
pcm = READ_ONCE(s->pcm);
|
||||
if (data_blocks > 0 && pcm)
|
||||
read_pcm_s32(s, pcm->runtime, buffer, data_blocks);
|
||||
if (p->midi_ports)
|
||||
read_midi_messages(s, buf, data_blocks);
|
||||
}
|
||||
|
||||
return data_blocks;
|
||||
// For tracepoints.
|
||||
if (trace_data_block_sph_enabled() ||
|
||||
trace_data_block_message_enabled())
|
||||
probe_tracepoints_events(s, descs, packets);
|
||||
|
||||
return pcm_frames;
|
||||
}
|
||||
|
||||
static inline void compute_next_elapse_from_start(struct amdtp_motu *p)
|
||||
|
@ -360,46 +400,55 @@ static void write_sph(struct amdtp_stream *s, __be32 *buffer,
|
|||
}
|
||||
}
|
||||
|
||||
static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
|
||||
__be32 *buffer, unsigned int data_blocks,
|
||||
unsigned int *syt)
|
||||
static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
|
||||
const struct pkt_desc *descs,
|
||||
unsigned int packets,
|
||||
struct snd_pcm_substream *pcm)
|
||||
{
|
||||
struct amdtp_motu *p = (struct amdtp_motu *)s->protocol;
|
||||
struct snd_pcm_substream *pcm;
|
||||
struct amdtp_motu *p = s->protocol;
|
||||
unsigned int pcm_frames = 0;
|
||||
int i;
|
||||
|
||||
/* Not used. */
|
||||
*syt = 0xffff;
|
||||
// For data block processing.
|
||||
for (i = 0; i < packets; ++i) {
|
||||
const struct pkt_desc *desc = descs + i;
|
||||
__be32 *buf = desc->ctx_payload;
|
||||
unsigned int data_blocks = desc->data_blocks;
|
||||
|
||||
/* TODO: how to interact control messages between userspace? */
|
||||
if (pcm) {
|
||||
write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
|
||||
pcm_frames += data_blocks;
|
||||
} else {
|
||||
write_pcm_silence(s, buf, data_blocks);
|
||||
}
|
||||
|
||||
if (p->midi_ports)
|
||||
write_midi_messages(s, buffer, data_blocks);
|
||||
if (p->midi_ports)
|
||||
write_midi_messages(s, buf, data_blocks);
|
||||
|
||||
pcm = READ_ONCE(s->pcm);
|
||||
if (pcm)
|
||||
write_pcm_s32(s, pcm->runtime, buffer, data_blocks);
|
||||
else
|
||||
write_pcm_silence(s, buffer, data_blocks);
|
||||
// TODO: how to interact control messages between userspace?
|
||||
|
||||
write_sph(s, buffer, data_blocks);
|
||||
write_sph(s, buf, data_blocks);
|
||||
}
|
||||
|
||||
trace_data_block_sph(s, data_blocks, buffer);
|
||||
trace_data_block_message(s, data_blocks, buffer);
|
||||
// For tracepoints.
|
||||
if (trace_data_block_sph_enabled() ||
|
||||
trace_data_block_message_enabled())
|
||||
probe_tracepoints_events(s, descs, packets);
|
||||
|
||||
return data_blocks;
|
||||
return pcm_frames;
|
||||
}
|
||||
|
||||
int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
enum amdtp_stream_direction dir,
|
||||
const struct snd_motu_protocol *const protocol)
|
||||
{
|
||||
amdtp_stream_process_data_blocks_t process_data_blocks;
|
||||
amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
|
||||
int fmt = CIP_FMT_MOTU;
|
||||
int flags = CIP_BLOCKING;
|
||||
int err;
|
||||
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
process_data_blocks = process_tx_data_blocks;
|
||||
process_ctx_payloads = process_ir_ctx_payloads;
|
||||
|
||||
/*
|
||||
* Units of version 3 transmits packets with invalid CIP header
|
||||
|
@ -418,17 +467,23 @@ int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
|
|||
CIP_SKIP_DBC_ZERO_CHECK;
|
||||
}
|
||||
} else {
|
||||
process_data_blocks = process_rx_data_blocks;
|
||||
process_ctx_payloads = process_it_ctx_payloads;
|
||||
flags |= CIP_DBC_IS_END_EVENT;
|
||||
}
|
||||
|
||||
err = amdtp_stream_init(s, unit, dir, flags, fmt, process_data_blocks,
|
||||
err = amdtp_stream_init(s, unit, dir, flags, fmt, process_ctx_payloads,
|
||||
sizeof(struct amdtp_motu));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
s->sph = 1;
|
||||
s->ctx_data.rx.fdf = MOTU_FDF_AM824;
|
||||
|
||||
if (dir == AMDTP_OUT_STREAM) {
|
||||
// Use fixed value for FDF field.
|
||||
s->ctx_data.rx.fdf = MOTU_FDF_AM824;
|
||||
// Not used.
|
||||
s->ctx_data.rx.syt_override = 0xffff;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -92,9 +92,6 @@ static void finish_session(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)
|
||||
|
@ -109,27 +106,6 @@ static void finish_session(struct snd_motu *motu)
|
|||
sizeof(reg));
|
||||
}
|
||||
|
||||
static int start_isoc_ctx(struct snd_motu *motu, struct amdtp_stream *stream)
|
||||
{
|
||||
struct fw_iso_resources *resources;
|
||||
int err;
|
||||
|
||||
if (stream == &motu->rx_stream)
|
||||
resources = &motu->rx_resources;
|
||||
else
|
||||
resources = &motu->tx_resources;
|
||||
|
||||
err = amdtp_stream_start(stream, resources->channel,
|
||||
fw_parent_device(motu->unit)->max_speed);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_motu_stream_cache_packet_formats(struct snd_motu *motu)
|
||||
{
|
||||
int err;
|
||||
|
@ -169,6 +145,7 @@ int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate)
|
|||
rate = curr_rate;
|
||||
|
||||
if (motu->substreams_counter == 0 || curr_rate != rate) {
|
||||
amdtp_domain_stop(&motu->domain);
|
||||
finish_session(motu);
|
||||
|
||||
fw_iso_resources_free(&motu->tx_resources);
|
||||
|
@ -234,8 +211,10 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu)
|
|||
return 0;
|
||||
|
||||
if (amdtp_streaming_error(&motu->rx_stream) ||
|
||||
amdtp_streaming_error(&motu->tx_stream))
|
||||
amdtp_streaming_error(&motu->tx_stream)) {
|
||||
amdtp_domain_stop(&motu->domain);
|
||||
finish_session(motu);
|
||||
}
|
||||
|
||||
if (generation != fw_parent_device(motu->unit)->card->generation) {
|
||||
err = fw_iso_resources_update(&motu->rx_resources);
|
||||
|
@ -248,6 +227,8 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu)
|
|||
}
|
||||
|
||||
if (!amdtp_stream_running(&motu->rx_stream)) {
|
||||
int spd = fw_parent_device(motu->unit)->max_speed;
|
||||
|
||||
err = ensure_packet_formats(motu);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
@ -259,10 +240,25 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu)
|
|||
goto stop_streams;
|
||||
}
|
||||
|
||||
err = start_isoc_ctx(motu, &motu->rx_stream);
|
||||
if (err < 0) {
|
||||
dev_err(&motu->unit->device,
|
||||
"fail to start IT context: %d\n", err);
|
||||
err = amdtp_domain_add_stream(&motu->domain, &motu->tx_stream,
|
||||
motu->tx_resources.channel, spd);
|
||||
if (err < 0)
|
||||
goto stop_streams;
|
||||
|
||||
err = amdtp_domain_add_stream(&motu->domain, &motu->rx_stream,
|
||||
motu->rx_resources.channel, spd);
|
||||
if (err < 0)
|
||||
goto stop_streams;
|
||||
|
||||
err = amdtp_domain_start(&motu->domain);
|
||||
if (err < 0)
|
||||
goto stop_streams;
|
||||
|
||||
if (!amdtp_stream_wait_callback(&motu->tx_stream,
|
||||
CALLBACK_TIMEOUT) ||
|
||||
!amdtp_stream_wait_callback(&motu->rx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto stop_streams;
|
||||
}
|
||||
|
||||
|
@ -274,18 +270,10 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu)
|
|||
}
|
||||
}
|
||||
|
||||
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);
|
||||
goto stop_streams;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
stop_streams:
|
||||
amdtp_domain_stop(&motu->domain);
|
||||
finish_session(motu);
|
||||
return err;
|
||||
}
|
||||
|
@ -293,6 +281,7 @@ stop_streams:
|
|||
void snd_motu_stream_stop_duplex(struct snd_motu *motu)
|
||||
{
|
||||
if (motu->substreams_counter == 0) {
|
||||
amdtp_domain_stop(&motu->domain);
|
||||
finish_session(motu);
|
||||
|
||||
fw_iso_resources_free(&motu->tx_resources);
|
||||
|
@ -300,74 +289,72 @@ void snd_motu_stream_stop_duplex(struct snd_motu *motu)
|
|||
}
|
||||
}
|
||||
|
||||
static int init_stream(struct snd_motu *motu, enum amdtp_stream_direction dir)
|
||||
static int init_stream(struct snd_motu *motu, struct amdtp_stream *s)
|
||||
{
|
||||
int err;
|
||||
struct amdtp_stream *stream;
|
||||
struct fw_iso_resources *resources;
|
||||
enum amdtp_stream_direction dir;
|
||||
int err;
|
||||
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
stream = &motu->tx_stream;
|
||||
if (s == &motu->tx_stream) {
|
||||
resources = &motu->tx_resources;
|
||||
dir = AMDTP_IN_STREAM;
|
||||
} else {
|
||||
stream = &motu->rx_stream;
|
||||
resources = &motu->rx_resources;
|
||||
dir = AMDTP_OUT_STREAM;
|
||||
}
|
||||
|
||||
err = fw_iso_resources_init(resources, motu->unit);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = amdtp_motu_init(stream, motu->unit, dir, motu->spec->protocol);
|
||||
if (err < 0) {
|
||||
amdtp_stream_destroy(stream);
|
||||
err = amdtp_motu_init(s, motu->unit, dir, motu->spec->protocol);
|
||||
if (err < 0)
|
||||
fw_iso_resources_destroy(resources);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void destroy_stream(struct snd_motu *motu,
|
||||
enum amdtp_stream_direction dir)
|
||||
static void destroy_stream(struct snd_motu *motu, struct amdtp_stream *s)
|
||||
{
|
||||
struct amdtp_stream *stream;
|
||||
struct fw_iso_resources *resources;
|
||||
amdtp_stream_destroy(s);
|
||||
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
stream = &motu->tx_stream;
|
||||
resources = &motu->tx_resources;
|
||||
} else {
|
||||
stream = &motu->rx_stream;
|
||||
resources = &motu->rx_resources;
|
||||
}
|
||||
|
||||
amdtp_stream_destroy(stream);
|
||||
fw_iso_resources_destroy(resources);
|
||||
if (s == &motu->tx_stream)
|
||||
fw_iso_resources_destroy(&motu->tx_resources);
|
||||
else
|
||||
fw_iso_resources_destroy(&motu->rx_resources);
|
||||
}
|
||||
|
||||
int snd_motu_stream_init_duplex(struct snd_motu *motu)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = init_stream(motu, AMDTP_IN_STREAM);
|
||||
err = init_stream(motu, &motu->tx_stream);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = init_stream(motu, AMDTP_OUT_STREAM);
|
||||
if (err < 0)
|
||||
destroy_stream(motu, AMDTP_IN_STREAM);
|
||||
err = init_stream(motu, &motu->rx_stream);
|
||||
if (err < 0) {
|
||||
destroy_stream(motu, &motu->tx_stream);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = amdtp_domain_init(&motu->domain);
|
||||
if (err < 0) {
|
||||
destroy_stream(motu, &motu->tx_stream);
|
||||
destroy_stream(motu, &motu->rx_stream);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function should be called before starting streams or after stopping
|
||||
* streams.
|
||||
*/
|
||||
// This function should be called before starting streams or after stopping
|
||||
// streams.
|
||||
void snd_motu_stream_destroy_duplex(struct snd_motu *motu)
|
||||
{
|
||||
destroy_stream(motu, AMDTP_IN_STREAM);
|
||||
destroy_stream(motu, AMDTP_OUT_STREAM);
|
||||
amdtp_domain_destroy(&motu->domain);
|
||||
|
||||
destroy_stream(motu, &motu->rx_stream);
|
||||
destroy_stream(motu, &motu->tx_stream);
|
||||
|
||||
motu->substreams_counter = 0;
|
||||
}
|
||||
|
|
|
@ -247,6 +247,17 @@ static const struct snd_motu_spec motu_audio_express = {
|
|||
.analog_out_ports = 4,
|
||||
};
|
||||
|
||||
static const struct snd_motu_spec motu_4pre = {
|
||||
.name = "4pre",
|
||||
.protocol = &snd_motu_protocol_v3,
|
||||
.flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
|
||||
SND_MOTU_SPEC_TX_MICINST_CHUNK |
|
||||
SND_MOTU_SPEC_TX_RETURN_CHUNK |
|
||||
SND_MOTU_SPEC_RX_SEPARETED_MAIN,
|
||||
.analog_in_ports = 2,
|
||||
.analog_out_ports = 2,
|
||||
};
|
||||
|
||||
#define SND_MOTU_DEV_ENTRY(model, data) \
|
||||
{ \
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID | \
|
||||
|
@ -265,6 +276,7 @@ static const struct ieee1394_device_id motu_id_table[] = {
|
|||
SND_MOTU_DEV_ENTRY(0x000015, &motu_828mk3), /* FireWire only. */
|
||||
SND_MOTU_DEV_ENTRY(0x000035, &motu_828mk3), /* Hybrid. */
|
||||
SND_MOTU_DEV_ENTRY(0x000033, &motu_audio_express),
|
||||
SND_MOTU_DEV_ENTRY(0x000045, &motu_4pre),
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(ieee1394, motu_id_table);
|
||||
|
|
|
@ -69,6 +69,8 @@ struct snd_motu {
|
|||
int dev_lock_count;
|
||||
bool dev_lock_changed;
|
||||
wait_queue_head_t hwdep_wait;
|
||||
|
||||
struct amdtp_domain domain;
|
||||
};
|
||||
|
||||
enum snd_motu_spec_flags {
|
||||
|
|
|
@ -114,19 +114,13 @@ static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = amdtp_stream_start(stream, conn->resources.channel, conn->speed);
|
||||
err = amdtp_domain_add_stream(&oxfw->domain, stream,
|
||||
conn->resources.channel, conn->speed);
|
||||
if (err < 0) {
|
||||
cmp_connection_break(conn);
|
||||
return err;
|
||||
}
|
||||
|
||||
// Wait first packet.
|
||||
if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) {
|
||||
amdtp_stream_stop(stream);
|
||||
cmp_connection_break(conn);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -280,12 +274,12 @@ int snd_oxfw_stream_reserve_duplex(struct snd_oxfw *oxfw,
|
|||
pcm_channels = formation.pcm;
|
||||
}
|
||||
if (formation.rate != rate || formation.pcm != pcm_channels) {
|
||||
amdtp_stream_stop(&oxfw->rx_stream);
|
||||
amdtp_domain_stop(&oxfw->domain);
|
||||
|
||||
cmp_connection_break(&oxfw->in_conn);
|
||||
cmp_connection_release(&oxfw->in_conn);
|
||||
|
||||
if (oxfw->has_output) {
|
||||
amdtp_stream_stop(&oxfw->tx_stream);
|
||||
cmp_connection_break(&oxfw->out_conn);
|
||||
cmp_connection_release(&oxfw->out_conn);
|
||||
}
|
||||
|
@ -325,30 +319,46 @@ int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw)
|
|||
|
||||
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);
|
||||
amdtp_domain_stop(&oxfw->domain);
|
||||
|
||||
if (oxfw->has_output) {
|
||||
amdtp_stream_stop(&oxfw->tx_stream);
|
||||
cmp_connection_break(&oxfw->in_conn);
|
||||
if (oxfw->has_output)
|
||||
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);
|
||||
"fail to prepare rx stream: %d\n", err);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (oxfw->has_output) {
|
||||
if (!amdtp_stream_running(&oxfw->tx_stream)) {
|
||||
if (oxfw->has_output &&
|
||||
!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);
|
||||
"fail to prepare tx stream: %d\n", err);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
err = amdtp_domain_start(&oxfw->domain);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
// Wait first packet.
|
||||
if (!amdtp_stream_wait_callback(&oxfw->rx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (oxfw->has_output) {
|
||||
if (!amdtp_stream_wait_callback(&oxfw->tx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
@ -356,24 +366,24 @@ int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw)
|
|||
|
||||
return 0;
|
||||
error:
|
||||
amdtp_stream_stop(&oxfw->rx_stream);
|
||||
amdtp_domain_stop(&oxfw->domain);
|
||||
|
||||
cmp_connection_break(&oxfw->in_conn);
|
||||
if (oxfw->has_output) {
|
||||
amdtp_stream_stop(&oxfw->tx_stream);
|
||||
if (oxfw->has_output)
|
||||
cmp_connection_break(&oxfw->out_conn);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void snd_oxfw_stream_stop_duplex(struct snd_oxfw *oxfw)
|
||||
{
|
||||
if (oxfw->substreams_count == 0) {
|
||||
amdtp_stream_stop(&oxfw->rx_stream);
|
||||
amdtp_domain_stop(&oxfw->domain);
|
||||
|
||||
cmp_connection_break(&oxfw->in_conn);
|
||||
cmp_connection_release(&oxfw->in_conn);
|
||||
|
||||
if (oxfw->has_output) {
|
||||
amdtp_stream_stop(&oxfw->tx_stream);
|
||||
cmp_connection_break(&oxfw->out_conn);
|
||||
cmp_connection_release(&oxfw->out_conn);
|
||||
}
|
||||
|
@ -409,13 +419,22 @@ int snd_oxfw_stream_init_duplex(struct snd_oxfw *oxfw)
|
|||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
err = amdtp_domain_init(&oxfw->domain);
|
||||
if (err < 0) {
|
||||
destroy_stream(oxfw, &oxfw->rx_stream);
|
||||
if (oxfw->has_output)
|
||||
destroy_stream(oxfw, &oxfw->tx_stream);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
// This function should be called before starting the stream or after stopping
|
||||
// the streams.
|
||||
void snd_oxfw_stream_destroy_duplex(struct snd_oxfw *oxfw)
|
||||
{
|
||||
amdtp_domain_destroy(&oxfw->domain);
|
||||
|
||||
destroy_stream(oxfw, &oxfw->rx_stream);
|
||||
|
||||
if (oxfw->has_output)
|
||||
|
@ -424,13 +443,13 @@ void snd_oxfw_stream_destroy_duplex(struct snd_oxfw *oxfw)
|
|||
|
||||
void snd_oxfw_stream_update_duplex(struct snd_oxfw *oxfw)
|
||||
{
|
||||
amdtp_stream_stop(&oxfw->rx_stream);
|
||||
amdtp_domain_stop(&oxfw->domain);
|
||||
|
||||
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);
|
||||
|
|
|
@ -63,6 +63,8 @@ struct snd_oxfw {
|
|||
|
||||
const struct ieee1394_device_id *entry;
|
||||
void *spec;
|
||||
|
||||
struct amdtp_domain domain;
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -32,19 +32,24 @@ int amdtp_tscm_set_parameters(struct amdtp_stream *s, unsigned int rate)
|
|||
return amdtp_stream_set_parameters(s, rate, data_channels);
|
||||
}
|
||||
|
||||
static void write_pcm_s32(struct amdtp_stream *s,
|
||||
struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int frames)
|
||||
static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int frames,
|
||||
unsigned int pcm_frames)
|
||||
{
|
||||
struct amdtp_tscm *p = s->protocol;
|
||||
unsigned int channels = p->pcm_channels;
|
||||
struct snd_pcm_runtime *runtime = pcm->runtime;
|
||||
unsigned int channels, remaining_frames, i, c;
|
||||
unsigned int pcm_buffer_pointer;
|
||||
int remaining_frames;
|
||||
const u32 *src;
|
||||
int i, c;
|
||||
|
||||
pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
|
||||
pcm_buffer_pointer %= runtime->buffer_size;
|
||||
|
||||
channels = p->pcm_channels;
|
||||
src = (void *)runtime->dma_area +
|
||||
frames_to_bytes(runtime, s->pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
|
||||
frames_to_bytes(runtime, pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
|
||||
|
||||
for (i = 0; i < frames; ++i) {
|
||||
for (c = 0; c < channels; ++c) {
|
||||
|
@ -57,19 +62,24 @@ static void write_pcm_s32(struct amdtp_stream *s,
|
|||
}
|
||||
}
|
||||
|
||||
static void read_pcm_s32(struct amdtp_stream *s,
|
||||
struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int frames)
|
||||
static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int frames,
|
||||
unsigned int pcm_frames)
|
||||
{
|
||||
struct amdtp_tscm *p = s->protocol;
|
||||
unsigned int channels = p->pcm_channels;
|
||||
struct snd_pcm_runtime *runtime = pcm->runtime;
|
||||
unsigned int channels, remaining_frames, i, c;
|
||||
unsigned int pcm_buffer_pointer;
|
||||
int remaining_frames;
|
||||
u32 *dst;
|
||||
int i, c;
|
||||
|
||||
pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
|
||||
pcm_buffer_pointer %= runtime->buffer_size;
|
||||
|
||||
channels = p->pcm_channels;
|
||||
dst = (void *)runtime->dma_area +
|
||||
frames_to_bytes(runtime, s->pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
|
||||
frames_to_bytes(runtime, pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
|
||||
|
||||
/* The first data channel is for event counter. */
|
||||
buffer += 1;
|
||||
|
@ -165,65 +175,82 @@ static void read_status_messages(struct amdtp_stream *s,
|
|||
}
|
||||
}
|
||||
|
||||
static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
|
||||
__be32 *buffer,
|
||||
unsigned int data_blocks,
|
||||
unsigned int *syt)
|
||||
static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
|
||||
const struct pkt_desc *descs,
|
||||
unsigned int packets,
|
||||
struct snd_pcm_substream *pcm)
|
||||
{
|
||||
struct snd_pcm_substream *pcm;
|
||||
unsigned int pcm_frames = 0;
|
||||
int i;
|
||||
|
||||
pcm = READ_ONCE(s->pcm);
|
||||
if (data_blocks > 0 && pcm)
|
||||
read_pcm_s32(s, pcm, buffer, data_blocks);
|
||||
for (i = 0; i < packets; ++i) {
|
||||
const struct pkt_desc *desc = descs + i;
|
||||
__be32 *buf = desc->ctx_payload;
|
||||
unsigned int data_blocks = desc->data_blocks;
|
||||
|
||||
read_status_messages(s, buffer, data_blocks);
|
||||
if (pcm) {
|
||||
read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
|
||||
pcm_frames += data_blocks;
|
||||
}
|
||||
|
||||
return data_blocks;
|
||||
read_status_messages(s, buf, data_blocks);
|
||||
}
|
||||
|
||||
return pcm_frames;
|
||||
}
|
||||
|
||||
static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
|
||||
__be32 *buffer,
|
||||
unsigned int data_blocks,
|
||||
unsigned int *syt)
|
||||
static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
|
||||
const struct pkt_desc *descs,
|
||||
unsigned int packets,
|
||||
struct snd_pcm_substream *pcm)
|
||||
{
|
||||
struct snd_pcm_substream *pcm;
|
||||
unsigned int pcm_frames = 0;
|
||||
int i;
|
||||
|
||||
/* This field is not used. */
|
||||
*syt = 0x0000;
|
||||
for (i = 0; i < packets; ++i) {
|
||||
const struct pkt_desc *desc = descs + i;
|
||||
__be32 *buf = desc->ctx_payload;
|
||||
unsigned int data_blocks = desc->data_blocks;
|
||||
|
||||
pcm = READ_ONCE(s->pcm);
|
||||
if (pcm)
|
||||
write_pcm_s32(s, pcm, buffer, data_blocks);
|
||||
else
|
||||
write_pcm_silence(s, buffer, data_blocks);
|
||||
if (pcm) {
|
||||
write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
|
||||
pcm_frames += data_blocks;
|
||||
} else {
|
||||
write_pcm_silence(s, buf, data_blocks);
|
||||
}
|
||||
}
|
||||
|
||||
return data_blocks;
|
||||
return pcm_frames;
|
||||
}
|
||||
|
||||
int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
enum amdtp_stream_direction dir, unsigned int pcm_channels)
|
||||
{
|
||||
amdtp_stream_process_data_blocks_t process_data_blocks;
|
||||
amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
|
||||
struct amdtp_tscm *p;
|
||||
unsigned int fmt;
|
||||
int err;
|
||||
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
fmt = AMDTP_FMT_TSCM_TX;
|
||||
process_data_blocks = process_tx_data_blocks;
|
||||
process_ctx_payloads = process_ir_ctx_payloads;
|
||||
} else {
|
||||
fmt = AMDTP_FMT_TSCM_RX;
|
||||
process_data_blocks = process_rx_data_blocks;
|
||||
process_ctx_payloads = process_it_ctx_payloads;
|
||||
}
|
||||
|
||||
err = amdtp_stream_init(s, unit, dir,
|
||||
CIP_NONBLOCKING | CIP_SKIP_DBC_ZERO_CHECK, fmt,
|
||||
process_data_blocks, sizeof(struct amdtp_tscm));
|
||||
CIP_NONBLOCKING | CIP_SKIP_DBC_ZERO_CHECK, fmt,
|
||||
process_ctx_payloads, sizeof(struct amdtp_tscm));
|
||||
if (err < 0)
|
||||
return 0;
|
||||
|
||||
/* Use fixed value for FDF field. */
|
||||
s->ctx_data.rx.fdf = 0x00;
|
||||
if (dir == AMDTP_OUT_STREAM) {
|
||||
// Use fixed value for FDF field.
|
||||
s->ctx_data.rx.fdf = 0x00;
|
||||
// Not used.
|
||||
s->ctx_data.rx.syt_override = 0x0000;
|
||||
}
|
||||
|
||||
/* This protocol uses fixed number of data channels for PCM samples. */
|
||||
p = s->protocol;
|
||||
|
|
|
@ -56,6 +56,9 @@ static int pcm_open(struct snd_pcm_substream *substream)
|
|||
goto err_locked;
|
||||
|
||||
err = snd_tscm_stream_get_clock(tscm, &clock);
|
||||
if (err < 0)
|
||||
goto err_locked;
|
||||
|
||||
if (clock != SND_TSCM_CLOCK_INTERNAL ||
|
||||
amdtp_stream_pcm_running(&tscm->rx_stream) ||
|
||||
amdtp_stream_pcm_running(&tscm->tx_stream)) {
|
||||
|
|
|
@ -8,20 +8,37 @@
|
|||
#include <linux/delay.h>
|
||||
#include "tascam.h"
|
||||
|
||||
#define CLOCK_STATUS_MASK 0xffff0000
|
||||
#define CLOCK_CONFIG_MASK 0x0000ffff
|
||||
|
||||
#define CALLBACK_TIMEOUT 500
|
||||
|
||||
static int get_clock(struct snd_tscm *tscm, u32 *data)
|
||||
{
|
||||
int trial = 0;
|
||||
__be32 reg;
|
||||
int err;
|
||||
|
||||
err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS,
|
||||
®, sizeof(reg), 0);
|
||||
if (err >= 0)
|
||||
*data = be32_to_cpu(reg);
|
||||
while (trial++ < 5) {
|
||||
err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS,
|
||||
®, sizeof(reg), 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return err;
|
||||
*data = be32_to_cpu(reg);
|
||||
if (*data & CLOCK_STATUS_MASK)
|
||||
break;
|
||||
|
||||
// In intermediate state after changing clock status.
|
||||
msleep(50);
|
||||
}
|
||||
|
||||
// Still in the intermediate state.
|
||||
if (trial >= 5)
|
||||
return -EAGAIN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_clock(struct snd_tscm *tscm, unsigned int rate,
|
||||
|
@ -34,7 +51,7 @@ static int set_clock(struct snd_tscm *tscm, unsigned int rate,
|
|||
err = get_clock(tscm, &data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
data &= 0x0000ffff;
|
||||
data &= CLOCK_CONFIG_MASK;
|
||||
|
||||
if (rate > 0) {
|
||||
data &= 0x000000ff;
|
||||
|
@ -79,17 +96,14 @@ static int set_clock(struct snd_tscm *tscm, unsigned int rate,
|
|||
|
||||
int snd_tscm_stream_get_rate(struct snd_tscm *tscm, unsigned int *rate)
|
||||
{
|
||||
u32 data = 0x0;
|
||||
unsigned int trials = 0;
|
||||
u32 data;
|
||||
int err;
|
||||
|
||||
while (data == 0x0 || trials++ < 5) {
|
||||
err = get_clock(tscm, &data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = get_clock(tscm, &data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
data = (data & 0xff000000) >> 24;
|
||||
}
|
||||
data = (data & 0xff000000) >> 24;
|
||||
|
||||
/* Check base rate. */
|
||||
if ((data & 0x0f) == 0x01)
|
||||
|
@ -180,9 +194,6 @@ 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,
|
||||
|
@ -287,38 +298,68 @@ static int keep_resources(struct snd_tscm *tscm, unsigned int rate,
|
|||
fw_parent_device(tscm->unit)->max_speed);
|
||||
}
|
||||
|
||||
int snd_tscm_stream_init_duplex(struct snd_tscm *tscm)
|
||||
static int init_stream(struct snd_tscm *tscm, struct amdtp_stream *s)
|
||||
{
|
||||
struct fw_iso_resources *resources;
|
||||
enum amdtp_stream_direction dir;
|
||||
unsigned int pcm_channels;
|
||||
int err;
|
||||
|
||||
/* For out-stream. */
|
||||
err = fw_iso_resources_init(&tscm->rx_resources, tscm->unit);
|
||||
if (err < 0)
|
||||
return err;
|
||||
pcm_channels = tscm->spec->pcm_playback_analog_channels;
|
||||
if (s == &tscm->tx_stream) {
|
||||
resources = &tscm->tx_resources;
|
||||
dir = AMDTP_IN_STREAM;
|
||||
pcm_channels = tscm->spec->pcm_capture_analog_channels;
|
||||
} else {
|
||||
resources = &tscm->rx_resources;
|
||||
dir = AMDTP_OUT_STREAM;
|
||||
pcm_channels = tscm->spec->pcm_playback_analog_channels;
|
||||
}
|
||||
|
||||
if (tscm->spec->has_adat)
|
||||
pcm_channels += 8;
|
||||
if (tscm->spec->has_spdif)
|
||||
pcm_channels += 2;
|
||||
err = amdtp_tscm_init(&tscm->rx_stream, tscm->unit, AMDTP_OUT_STREAM,
|
||||
pcm_channels);
|
||||
|
||||
err = fw_iso_resources_init(resources, tscm->unit);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* For in-stream. */
|
||||
err = fw_iso_resources_init(&tscm->tx_resources, tscm->unit);
|
||||
err = amdtp_tscm_init(s, tscm->unit, dir, pcm_channels);
|
||||
if (err < 0)
|
||||
fw_iso_resources_free(resources);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void destroy_stream(struct snd_tscm *tscm, struct amdtp_stream *s)
|
||||
{
|
||||
amdtp_stream_destroy(s);
|
||||
|
||||
if (s == &tscm->tx_stream)
|
||||
fw_iso_resources_destroy(&tscm->tx_resources);
|
||||
else
|
||||
fw_iso_resources_destroy(&tscm->rx_resources);
|
||||
}
|
||||
|
||||
int snd_tscm_stream_init_duplex(struct snd_tscm *tscm)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = init_stream(tscm, &tscm->tx_stream);
|
||||
if (err < 0)
|
||||
return err;
|
||||
pcm_channels = tscm->spec->pcm_capture_analog_channels;
|
||||
if (tscm->spec->has_adat)
|
||||
pcm_channels += 8;
|
||||
if (tscm->spec->has_spdif)
|
||||
pcm_channels += 2;
|
||||
err = amdtp_tscm_init(&tscm->tx_stream, tscm->unit, AMDTP_IN_STREAM,
|
||||
pcm_channels);
|
||||
if (err < 0)
|
||||
amdtp_stream_destroy(&tscm->rx_stream);
|
||||
|
||||
err = init_stream(tscm, &tscm->rx_stream);
|
||||
if (err < 0) {
|
||||
destroy_stream(tscm, &tscm->tx_stream);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = amdtp_domain_init(&tscm->domain);
|
||||
if (err < 0) {
|
||||
destroy_stream(tscm, &tscm->tx_stream);
|
||||
destroy_stream(tscm, &tscm->rx_stream);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -326,24 +367,20 @@ int snd_tscm_stream_init_duplex(struct snd_tscm *tscm)
|
|||
// 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);
|
||||
amdtp_stream_stop(&tscm->tx_stream);
|
||||
amdtp_domain_stop(&tscm->domain);
|
||||
|
||||
amdtp_stream_pcm_abort(&tscm->tx_stream);
|
||||
amdtp_stream_pcm_abort(&tscm->rx_stream);
|
||||
amdtp_stream_stop(&tscm->rx_stream);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function should be called before starting streams or after stopping
|
||||
* streams.
|
||||
*/
|
||||
// This function should be called before starting streams or after stopping
|
||||
// streams.
|
||||
void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm)
|
||||
{
|
||||
amdtp_stream_destroy(&tscm->rx_stream);
|
||||
amdtp_stream_destroy(&tscm->tx_stream);
|
||||
amdtp_domain_destroy(&tscm->domain);
|
||||
|
||||
fw_iso_resources_destroy(&tscm->rx_resources);
|
||||
fw_iso_resources_destroy(&tscm->tx_resources);
|
||||
destroy_stream(tscm, &tscm->rx_stream);
|
||||
destroy_stream(tscm, &tscm->tx_stream);
|
||||
}
|
||||
|
||||
int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate)
|
||||
|
@ -356,6 +393,8 @@ int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate)
|
|||
return err;
|
||||
|
||||
if (tscm->substreams_counter == 0 || rate != curr_rate) {
|
||||
amdtp_domain_stop(&tscm->domain);
|
||||
|
||||
finish_session(tscm);
|
||||
|
||||
fw_iso_resources_free(&tscm->tx_resources);
|
||||
|
@ -388,8 +427,10 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
|
|||
return 0;
|
||||
|
||||
if (amdtp_streaming_error(&tscm->rx_stream) ||
|
||||
amdtp_streaming_error(&tscm->tx_stream))
|
||||
amdtp_streaming_error(&tscm->tx_stream)) {
|
||||
amdtp_domain_stop(&tscm->domain);
|
||||
finish_session(tscm);
|
||||
}
|
||||
|
||||
if (generation != fw_parent_device(tscm->unit)->card->generation) {
|
||||
err = fw_iso_resources_update(&tscm->tx_resources);
|
||||
|
@ -402,6 +443,8 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
|
|||
}
|
||||
|
||||
if (!amdtp_stream_running(&tscm->rx_stream)) {
|
||||
int spd = fw_parent_device(tscm->unit)->max_speed;
|
||||
|
||||
err = set_stream_formats(tscm, rate);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
@ -410,27 +453,23 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
|
|||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = amdtp_stream_start(&tscm->rx_stream,
|
||||
tscm->rx_resources.channel,
|
||||
fw_parent_device(tscm->unit)->max_speed);
|
||||
err = amdtp_domain_add_stream(&tscm->domain, &tscm->rx_stream,
|
||||
tscm->rx_resources.channel, spd);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = amdtp_domain_add_stream(&tscm->domain, &tscm->tx_stream,
|
||||
tscm->tx_resources.channel, spd);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = amdtp_domain_start(&tscm->domain);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (!amdtp_stream_wait_callback(&tscm->rx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (!amdtp_stream_running(&tscm->tx_stream)) {
|
||||
err = amdtp_stream_start(&tscm->tx_stream,
|
||||
tscm->tx_resources.channel,
|
||||
fw_parent_device(tscm->unit)->max_speed);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
if (!amdtp_stream_wait_callback(&tscm->tx_stream,
|
||||
CALLBACK_TIMEOUT) ||
|
||||
!amdtp_stream_wait_callback(&tscm->tx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
|
@ -439,6 +478,7 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
|
|||
|
||||
return 0;
|
||||
error:
|
||||
amdtp_domain_stop(&tscm->domain);
|
||||
finish_session(tscm);
|
||||
|
||||
return err;
|
||||
|
@ -447,6 +487,7 @@ error:
|
|||
void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm)
|
||||
{
|
||||
if (tscm->substreams_counter == 0) {
|
||||
amdtp_domain_stop(&tscm->domain);
|
||||
finish_session(tscm);
|
||||
|
||||
fw_iso_resources_free(&tscm->tx_resources);
|
||||
|
|
|
@ -39,6 +39,9 @@ static const struct snd_tscm_spec model_specs[] = {
|
|||
.midi_capture_ports = 2,
|
||||
.midi_playback_ports = 4,
|
||||
},
|
||||
// This kernel module doesn't support FE-8 because the most of features
|
||||
// can be implemented in userspace without any specific support of this
|
||||
// module.
|
||||
};
|
||||
|
||||
static int identify_model(struct snd_tscm *tscm)
|
||||
|
@ -214,7 +217,6 @@ static const struct ieee1394_device_id snd_tscm_id_table[] = {
|
|||
.vendor_id = 0x00022e,
|
||||
.specifier_id = 0x00022e,
|
||||
},
|
||||
/* FE-08 requires reverse-engineering because it just has faders. */
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(ieee1394, snd_tscm_id_table);
|
||||
|
|
|
@ -97,6 +97,8 @@ struct snd_tscm {
|
|||
struct snd_firewire_tascam_change queue[SND_TSCM_QUEUE_COUNT];
|
||||
unsigned int pull_pos;
|
||||
unsigned int push_pos;
|
||||
|
||||
struct amdtp_domain domain;
|
||||
};
|
||||
|
||||
#define TSCM_ADDR_BASE 0xffff00000000ull
|
||||
|
@ -127,6 +129,26 @@ struct snd_tscm {
|
|||
|
||||
#define TSCM_OFFSET_MIDI_RX_QUAD 0x4000
|
||||
|
||||
// Although FE-8 supports the above registers, it has no I/O interfaces for
|
||||
// audio samples and music messages. Otherwise it supports another notification
|
||||
// for status and control message as well as LED brightening. The message
|
||||
// consists of quadlet-aligned data up to 32 quadlets. The first byte of message
|
||||
// is fixed to 0x40. The second byte is between 0x00 to 0x1f and represent each
|
||||
// control:
|
||||
// fader: 0x00-0x07
|
||||
// button: 0x0d, 0x0e
|
||||
// knob: 0x14-0x1b
|
||||
// sensing: 0x0b
|
||||
//
|
||||
// The rest two bytes represent state of the controls; e.g. current value for
|
||||
// fader and knob, bitmasks for button and sensing.
|
||||
// Just after turning on, 32 quadlets messages with 0x00-0x1f are immediately
|
||||
// sent in one transaction. After, several quadlets are sent in one transaction.
|
||||
//
|
||||
// TSCM_OFFSET_FE8_CTL_TX_ON 0x0310
|
||||
// TSCM_OFFSET_FE8_CTL_TX_ADDR_HI 0x0314
|
||||
// TSCM_OFFSET_FE8_CTL_TX_ADDR_LO 0x0318
|
||||
|
||||
enum snd_tscm_clock {
|
||||
SND_TSCM_CLOCK_INTERNAL = 0,
|
||||
SND_TSCM_CLOCK_WORD = 1,
|
||||
|
|
|
@ -6,6 +6,9 @@ config SND_HDA_CORE
|
|||
config SND_HDA_DSP_LOADER
|
||||
bool
|
||||
|
||||
config SND_HDA_ALIGNED_MMIO
|
||||
bool
|
||||
|
||||
config SND_HDA_COMPONENT
|
||||
bool
|
||||
|
||||
|
@ -29,3 +32,8 @@ config SND_HDA_PREALLOC_SIZE
|
|||
|
||||
Note that the pre-allocation size can be changed dynamically
|
||||
via a proc file (/proc/asound/card*/pcm*/sub*/prealloc), too.
|
||||
|
||||
config SND_INTEL_NHLT
|
||||
tristate
|
||||
# this config should be selected only for Intel ACPI platforms.
|
||||
# A fallback is provided so that the code compiles in all cases.
|
|
@ -13,3 +13,6 @@ obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o
|
|||
|
||||
#extended hda
|
||||
obj-$(CONFIG_SND_HDA_EXT_CORE) += ext/
|
||||
|
||||
snd-intel-nhlt-objs := intel-nhlt.o
|
||||
obj-$(CONFIG_SND_INTEL_NHLT) += snd-intel-nhlt.o
|
||||
|
|
|
@ -17,80 +17,22 @@
|
|||
MODULE_DESCRIPTION("HDA extended core");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
static void hdac_ext_writel(u32 value, u32 __iomem *addr)
|
||||
{
|
||||
writel(value, addr);
|
||||
}
|
||||
|
||||
static u32 hdac_ext_readl(u32 __iomem *addr)
|
||||
{
|
||||
return readl(addr);
|
||||
}
|
||||
|
||||
static void hdac_ext_writew(u16 value, u16 __iomem *addr)
|
||||
{
|
||||
writew(value, addr);
|
||||
}
|
||||
|
||||
static u16 hdac_ext_readw(u16 __iomem *addr)
|
||||
{
|
||||
return readw(addr);
|
||||
}
|
||||
|
||||
static void hdac_ext_writeb(u8 value, u8 __iomem *addr)
|
||||
{
|
||||
writeb(value, addr);
|
||||
}
|
||||
|
||||
static u8 hdac_ext_readb(u8 __iomem *addr)
|
||||
{
|
||||
return readb(addr);
|
||||
}
|
||||
|
||||
static int hdac_ext_dma_alloc_pages(struct hdac_bus *bus, int type,
|
||||
size_t size, struct snd_dma_buffer *buf)
|
||||
{
|
||||
return snd_dma_alloc_pages(type, bus->dev, size, buf);
|
||||
}
|
||||
|
||||
static void hdac_ext_dma_free_pages(struct hdac_bus *bus, struct snd_dma_buffer *buf)
|
||||
{
|
||||
snd_dma_free_pages(buf);
|
||||
}
|
||||
|
||||
static const struct hdac_io_ops hdac_ext_default_io = {
|
||||
.reg_writel = hdac_ext_writel,
|
||||
.reg_readl = hdac_ext_readl,
|
||||
.reg_writew = hdac_ext_writew,
|
||||
.reg_readw = hdac_ext_readw,
|
||||
.reg_writeb = hdac_ext_writeb,
|
||||
.reg_readb = hdac_ext_readb,
|
||||
.dma_alloc_pages = hdac_ext_dma_alloc_pages,
|
||||
.dma_free_pages = hdac_ext_dma_free_pages,
|
||||
};
|
||||
|
||||
/**
|
||||
* snd_hdac_ext_bus_init - initialize a HD-audio extended bus
|
||||
* @ebus: the pointer to extended bus object
|
||||
* @dev: device pointer
|
||||
* @ops: bus verb operators
|
||||
* @io_ops: lowlevel I/O operators, can be NULL. If NULL core will use
|
||||
* default ops
|
||||
*
|
||||
* Returns 0 if successful, or a negative error code.
|
||||
*/
|
||||
int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev,
|
||||
const struct hdac_bus_ops *ops,
|
||||
const struct hdac_io_ops *io_ops,
|
||||
const struct hdac_ext_bus_ops *ext_ops)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* check if io ops are provided, if not load the defaults */
|
||||
if (io_ops == NULL)
|
||||
io_ops = &hdac_ext_default_io;
|
||||
|
||||
ret = snd_hdac_bus_init(bus, dev, ops, io_ops);
|
||||
ret = snd_hdac_bus_init(bus, dev, ops);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -4,12 +4,16 @@
|
|||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/export.h>
|
||||
#include <sound/hdaudio.h>
|
||||
#include "local.h"
|
||||
#include "trace.h"
|
||||
|
||||
static void snd_hdac_bus_process_unsol_events(struct work_struct *work);
|
||||
|
||||
static const struct hdac_bus_ops default_ops = {
|
||||
.command = snd_hdac_bus_send_cmd,
|
||||
.get_response = snd_hdac_bus_get_response,
|
||||
|
@ -19,13 +23,11 @@ static const struct hdac_bus_ops default_ops = {
|
|||
* snd_hdac_bus_init - initialize a HD-audio bas bus
|
||||
* @bus: the pointer to bus object
|
||||
* @ops: bus verb operators
|
||||
* @io_ops: lowlevel I/O operators
|
||||
*
|
||||
* Returns 0 if successful, or a negative error code.
|
||||
*/
|
||||
int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
|
||||
const struct hdac_bus_ops *ops,
|
||||
const struct hdac_io_ops *io_ops)
|
||||
const struct hdac_bus_ops *ops)
|
||||
{
|
||||
memset(bus, 0, sizeof(*bus));
|
||||
bus->dev = dev;
|
||||
|
@ -33,7 +35,7 @@ int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
|
|||
bus->ops = ops;
|
||||
else
|
||||
bus->ops = &default_ops;
|
||||
bus->io_ops = io_ops;
|
||||
bus->dma_type = SNDRV_DMA_TYPE_DEV;
|
||||
INIT_LIST_HEAD(&bus->stream_list);
|
||||
INIT_LIST_HEAD(&bus->codec_list);
|
||||
INIT_WORK(&bus->unsol_work, snd_hdac_bus_process_unsol_events);
|
||||
|
@ -149,7 +151,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_bus_queue_event);
|
|||
/*
|
||||
* process queued unsolicited events
|
||||
*/
|
||||
void snd_hdac_bus_process_unsol_events(struct work_struct *work)
|
||||
static void snd_hdac_bus_process_unsol_events(struct work_struct *work)
|
||||
{
|
||||
struct hdac_bus *bus = container_of(work, struct hdac_bus, unsol_work);
|
||||
struct hdac_device *codec;
|
||||
|
@ -172,7 +174,6 @@ void snd_hdac_bus_process_unsol_events(struct work_struct *work)
|
|||
drv->unsol_event(codec, res);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_bus_process_unsol_events);
|
||||
|
||||
/**
|
||||
* snd_hdac_bus_add_device - Add a codec to bus
|
||||
|
@ -197,7 +198,6 @@ int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec)
|
|||
bus->num_codecs++;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_bus_add_device);
|
||||
|
||||
/**
|
||||
* snd_hdac_bus_remove_device - Remove a codec from bus
|
||||
|
@ -216,4 +216,33 @@ void snd_hdac_bus_remove_device(struct hdac_bus *bus,
|
|||
bus->num_codecs--;
|
||||
flush_work(&bus->unsol_work);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_bus_remove_device);
|
||||
|
||||
#ifdef CONFIG_SND_HDA_ALIGNED_MMIO
|
||||
/* Helpers for aligned read/write of mmio space, for Tegra */
|
||||
unsigned int snd_hdac_aligned_read(void __iomem *addr, unsigned int mask)
|
||||
{
|
||||
void __iomem *aligned_addr =
|
||||
(void __iomem *)((unsigned long)(addr) & ~0x3);
|
||||
unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
|
||||
unsigned int v;
|
||||
|
||||
v = readl(aligned_addr);
|
||||
return (v >> shift) & mask;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_aligned_read);
|
||||
|
||||
void snd_hdac_aligned_write(unsigned int val, void __iomem *addr,
|
||||
unsigned int mask)
|
||||
{
|
||||
void __iomem *aligned_addr =
|
||||
(void __iomem *)((unsigned long)(addr) & ~0x3);
|
||||
unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
|
||||
unsigned int v;
|
||||
|
||||
v = readl(aligned_addr);
|
||||
v &= ~(mask << shift);
|
||||
v |= val << shift;
|
||||
writel(v, aligned_addr);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_aligned_write);
|
||||
#endif /* CONFIG_SND_HDA_ALIGNED_MMIO */
|
||||
|
|
|
@ -447,6 +447,8 @@ static void azx_int_disable(struct hdac_bus *bus)
|
|||
list_for_each_entry(azx_dev, &bus->stream_list, list)
|
||||
snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_INT_MASK, 0);
|
||||
|
||||
synchronize_irq(bus->irq);
|
||||
|
||||
/* disable SIE for all streams */
|
||||
snd_hdac_chip_writeb(bus, INTCTL, 0);
|
||||
|
||||
|
@ -575,12 +577,13 @@ int snd_hdac_bus_alloc_stream_pages(struct hdac_bus *bus)
|
|||
{
|
||||
struct hdac_stream *s;
|
||||
int num_streams = 0;
|
||||
int dma_type = bus->dma_type ? bus->dma_type : SNDRV_DMA_TYPE_DEV;
|
||||
int err;
|
||||
|
||||
list_for_each_entry(s, &bus->stream_list, list) {
|
||||
/* allocate memory for the BDL for each stream */
|
||||
err = bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV,
|
||||
BDL_SIZE, &s->bdl);
|
||||
err = snd_dma_alloc_pages(dma_type, bus->dev,
|
||||
BDL_SIZE, &s->bdl);
|
||||
num_streams++;
|
||||
if (err < 0)
|
||||
return -ENOMEM;
|
||||
|
@ -589,16 +592,15 @@ int snd_hdac_bus_alloc_stream_pages(struct hdac_bus *bus)
|
|||
if (WARN_ON(!num_streams))
|
||||
return -EINVAL;
|
||||
/* allocate memory for the position buffer */
|
||||
err = bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV,
|
||||
num_streams * 8, &bus->posbuf);
|
||||
err = snd_dma_alloc_pages(dma_type, bus->dev,
|
||||
num_streams * 8, &bus->posbuf);
|
||||
if (err < 0)
|
||||
return -ENOMEM;
|
||||
list_for_each_entry(s, &bus->stream_list, list)
|
||||
s->posbuf = (__le32 *)(bus->posbuf.area + s->index * 8);
|
||||
|
||||
/* single page (at least 4096 bytes) must suffice for both ringbuffes */
|
||||
return bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV,
|
||||
PAGE_SIZE, &bus->rb);
|
||||
return snd_dma_alloc_pages(dma_type, bus->dev, PAGE_SIZE, &bus->rb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_bus_alloc_stream_pages);
|
||||
|
||||
|
@ -612,12 +614,12 @@ void snd_hdac_bus_free_stream_pages(struct hdac_bus *bus)
|
|||
|
||||
list_for_each_entry(s, &bus->stream_list, list) {
|
||||
if (s->bdl.area)
|
||||
bus->io_ops->dma_free_pages(bus, &s->bdl);
|
||||
snd_dma_free_pages(&s->bdl);
|
||||
}
|
||||
|
||||
if (bus->rb.area)
|
||||
bus->io_ops->dma_free_pages(bus, &bus->rb);
|
||||
snd_dma_free_pages(&bus->rb);
|
||||
if (bus->posbuf.area)
|
||||
bus->io_ops->dma_free_pages(bus, &bus->posbuf);
|
||||
snd_dma_free_pages(&bus->posbuf);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_bus_free_stream_pages);
|
||||
|
|
|
@ -218,8 +218,8 @@ EXPORT_SYMBOL_GPL(snd_hdac_codec_modalias);
|
|||
*
|
||||
* Return an encoded command verb or -1 for error.
|
||||
*/
|
||||
unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid,
|
||||
unsigned int verb, unsigned int parm)
|
||||
static unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid,
|
||||
unsigned int verb, unsigned int parm)
|
||||
{
|
||||
u32 val, addr;
|
||||
|
||||
|
@ -237,7 +237,6 @@ unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid,
|
|||
val |= parm;
|
||||
return val;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_make_cmd);
|
||||
|
||||
/**
|
||||
* snd_hdac_exec_verb - execute an encoded verb
|
||||
|
@ -258,7 +257,6 @@ int snd_hdac_exec_verb(struct hdac_device *codec, unsigned int cmd,
|
|||
return codec->exec_verb(codec, cmd, flags, res);
|
||||
return snd_hdac_bus_exec_verb(codec->bus, codec->addr, cmd, res);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_exec_verb);
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <sound/core.h>
|
||||
#include <sound/hdaudio.h>
|
||||
#include <sound/hda_regmap.h>
|
||||
#include "local.h"
|
||||
|
||||
static int codec_pm_lock(struct hdac_device *codec)
|
||||
{
|
||||
|
|
|
@ -229,11 +229,7 @@ int snd_hdac_stream_setup(struct hdac_stream *azx_dev)
|
|||
/* set the interrupt enable bits in the descriptor control register */
|
||||
snd_hdac_stream_updatel(azx_dev, SD_CTL, 0, SD_INT_MASK);
|
||||
|
||||
if (azx_dev->direction == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
azx_dev->fifo_size =
|
||||
snd_hdac_stream_readw(azx_dev, SD_FIFOSIZE) + 1;
|
||||
else
|
||||
azx_dev->fifo_size = 0;
|
||||
azx_dev->fifo_size = snd_hdac_stream_readw(azx_dev, SD_FIFOSIZE) + 1;
|
||||
|
||||
/* when LPIB delay correction gives a small negative value,
|
||||
* we ignore it; currently set the threshold statically to
|
||||
|
@ -680,8 +676,8 @@ int snd_hdac_dsp_prepare(struct hdac_stream *azx_dev, unsigned int format,
|
|||
azx_dev->locked = true;
|
||||
spin_unlock_irq(&bus->reg_lock);
|
||||
|
||||
err = bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV_SG,
|
||||
byte_size, bufp);
|
||||
err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, bus->dev,
|
||||
byte_size, bufp);
|
||||
if (err < 0)
|
||||
goto err_alloc;
|
||||
|
||||
|
@ -707,7 +703,7 @@ int snd_hdac_dsp_prepare(struct hdac_stream *azx_dev, unsigned int format,
|
|||
return azx_dev->stream_tag;
|
||||
|
||||
error:
|
||||
bus->io_ops->dma_free_pages(bus, bufp);
|
||||
snd_dma_free_pages(bufp);
|
||||
err_alloc:
|
||||
spin_lock_irq(&bus->reg_lock);
|
||||
azx_dev->locked = false;
|
||||
|
@ -754,7 +750,7 @@ void snd_hdac_dsp_cleanup(struct hdac_stream *azx_dev,
|
|||
azx_dev->period_bytes = 0;
|
||||
azx_dev->format_val = 0;
|
||||
|
||||
bus->io_ops->dma_free_pages(bus, dmab);
|
||||
snd_dma_free_pages(dmab);
|
||||
dmab->area = NULL;
|
||||
|
||||
spin_lock_irq(&bus->reg_lock);
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2015-2019 Intel Corporation
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <sound/intel-nhlt.h>
|
||||
|
||||
#define NHLT_ACPI_HEADER_SIG "NHLT"
|
||||
|
||||
/* Unique identification for getting NHLT blobs */
|
||||
static guid_t osc_guid =
|
||||
GUID_INIT(0xA69F886E, 0x6CEB, 0x4594,
|
||||
0xA4, 0x1F, 0x7B, 0x5D, 0xCE, 0x24, 0xC5, 0x53);
|
||||
|
||||
struct nhlt_acpi_table *intel_nhlt_init(struct device *dev)
|
||||
{
|
||||
acpi_handle handle;
|
||||
union acpi_object *obj;
|
||||
struct nhlt_resource_desc *nhlt_ptr;
|
||||
struct nhlt_acpi_table *nhlt_table = NULL;
|
||||
|
||||
handle = ACPI_HANDLE(dev);
|
||||
if (!handle) {
|
||||
dev_err(dev, "Didn't find ACPI_HANDLE\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
obj = acpi_evaluate_dsm(handle, &osc_guid, 1, 1, NULL);
|
||||
|
||||
if (!obj)
|
||||
return NULL;
|
||||
|
||||
if (obj->type != ACPI_TYPE_BUFFER) {
|
||||
dev_dbg(dev, "No NHLT table found\n");
|
||||
ACPI_FREE(obj);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
nhlt_ptr = (struct nhlt_resource_desc *)obj->buffer.pointer;
|
||||
if (nhlt_ptr->length)
|
||||
nhlt_table = (struct nhlt_acpi_table *)
|
||||
memremap(nhlt_ptr->min_addr, nhlt_ptr->length,
|
||||
MEMREMAP_WB);
|
||||
ACPI_FREE(obj);
|
||||
if (nhlt_table &&
|
||||
(strncmp(nhlt_table->header.signature,
|
||||
NHLT_ACPI_HEADER_SIG,
|
||||
strlen(NHLT_ACPI_HEADER_SIG)) != 0)) {
|
||||
memunmap(nhlt_table);
|
||||
dev_err(dev, "NHLT ACPI header signature incorrect\n");
|
||||
return NULL;
|
||||
}
|
||||
return nhlt_table;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_nhlt_init);
|
||||
|
||||
void intel_nhlt_free(struct nhlt_acpi_table *nhlt)
|
||||
{
|
||||
memunmap((void *)nhlt);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_nhlt_free);
|
||||
|
||||
int intel_nhlt_get_dmic_geo(struct device *dev, struct nhlt_acpi_table *nhlt)
|
||||
{
|
||||
struct nhlt_endpoint *epnt;
|
||||
struct nhlt_dmic_array_config *cfg;
|
||||
struct nhlt_vendor_dmic_array_config *cfg_vendor;
|
||||
unsigned int dmic_geo = 0;
|
||||
u8 j;
|
||||
|
||||
if (!nhlt)
|
||||
return 0;
|
||||
|
||||
epnt = (struct nhlt_endpoint *)nhlt->desc;
|
||||
|
||||
for (j = 0; j < nhlt->endpoint_count; j++) {
|
||||
if (epnt->linktype == NHLT_LINK_DMIC) {
|
||||
cfg = (struct nhlt_dmic_array_config *)
|
||||
(epnt->config.caps);
|
||||
switch (cfg->array_type) {
|
||||
case NHLT_MIC_ARRAY_2CH_SMALL:
|
||||
case NHLT_MIC_ARRAY_2CH_BIG:
|
||||
dmic_geo = MIC_ARRAY_2CH;
|
||||
break;
|
||||
|
||||
case NHLT_MIC_ARRAY_4CH_1ST_GEOM:
|
||||
case NHLT_MIC_ARRAY_4CH_L_SHAPED:
|
||||
case NHLT_MIC_ARRAY_4CH_2ND_GEOM:
|
||||
dmic_geo = MIC_ARRAY_4CH;
|
||||
break;
|
||||
case NHLT_MIC_ARRAY_VENDOR_DEFINED:
|
||||
cfg_vendor = (struct nhlt_vendor_dmic_array_config *)cfg;
|
||||
dmic_geo = cfg_vendor->nb_mics;
|
||||
break;
|
||||
default:
|
||||
dev_warn(dev, "undefined DMIC array_type 0x%0x\n",
|
||||
cfg->array_type);
|
||||
}
|
||||
}
|
||||
epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
|
||||
}
|
||||
|
||||
return dmic_geo;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_nhlt_get_dmic_geo);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("Intel NHLT driver");
|
|
@ -33,4 +33,11 @@ int hda_widget_sysfs_reinit(struct hdac_device *codec, hda_nid_t start_nid,
|
|||
int num_nodes);
|
||||
void hda_widget_sysfs_exit(struct hdac_device *codec);
|
||||
|
||||
int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec);
|
||||
void snd_hdac_bus_remove_device(struct hdac_bus *bus,
|
||||
struct hdac_device *codec);
|
||||
|
||||
int snd_hdac_exec_verb(struct hdac_device *codec, unsigned int cmd,
|
||||
unsigned int flags, unsigned int *res);
|
||||
|
||||
#endif /* __HDAC_LOCAL_H */
|
||||
|
|
|
@ -775,11 +775,12 @@ static int build_adc_controls(struct snd_akm4xxx *ak)
|
|||
return err;
|
||||
|
||||
memset(&knew, 0, sizeof(knew));
|
||||
knew.name = ak->adc_info[mixer_ch].selector_name;
|
||||
if (!knew.name) {
|
||||
if (!ak->adc_info ||
|
||||
!ak->adc_info[mixer_ch].selector_name) {
|
||||
knew.name = "Capture Channel";
|
||||
knew.index = mixer_ch + ak->idx_offset * 2;
|
||||
}
|
||||
} else
|
||||
knew.name = ak->adc_info[mixer_ch].selector_name;
|
||||
|
||||
knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
||||
knew.info = ak4xxx_capture_source_info;
|
||||
|
|
|
@ -80,7 +80,7 @@ int snd_sbdsp_reset(struct snd_sb *chip)
|
|||
|
||||
static int snd_sbdsp_version(struct snd_sb * chip)
|
||||
{
|
||||
unsigned int result = -ENODEV;
|
||||
unsigned int result;
|
||||
|
||||
snd_sbdsp_command(chip, SB_DSP_GET_VERSION);
|
||||
result = (short) snd_sbdsp_get_byte(chip) << 8;
|
||||
|
|
|
@ -788,7 +788,6 @@ wavefront_send_patch (snd_wavefront_t *dev, wavefront_patch_info *header)
|
|||
|
||||
dev->patch_status[header->number] |= WF_SLOT_FILLED;
|
||||
|
||||
bptr = buf;
|
||||
bptr = munge_int32 (header->number, buf, 2);
|
||||
munge_buf ((unsigned char *)&header->hdr.p, bptr, WF_PATCH_BYTES);
|
||||
|
||||
|
|
|
@ -1432,25 +1432,25 @@ static int FalconMixerIoctl(u_int cmd, u_long arg)
|
|||
{
|
||||
int data;
|
||||
switch (cmd) {
|
||||
case SOUND_MIXER_READ_RECMASK:
|
||||
case SOUND_MIXER_READ_RECMASK:
|
||||
return IOCTL_OUT(arg, SOUND_MASK_MIC);
|
||||
case SOUND_MIXER_READ_DEVMASK:
|
||||
case SOUND_MIXER_READ_DEVMASK:
|
||||
return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC | SOUND_MASK_SPEAKER);
|
||||
case SOUND_MIXER_READ_STEREODEVS:
|
||||
case SOUND_MIXER_READ_STEREODEVS:
|
||||
return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC);
|
||||
case SOUND_MIXER_READ_VOLUME:
|
||||
case SOUND_MIXER_READ_VOLUME:
|
||||
return IOCTL_OUT(arg,
|
||||
VOLUME_ATT_TO_VOXWARE(dmasound.volume_left) |
|
||||
VOLUME_ATT_TO_VOXWARE(dmasound.volume_right) << 8);
|
||||
case SOUND_MIXER_READ_CAPS:
|
||||
case SOUND_MIXER_READ_CAPS:
|
||||
return IOCTL_OUT(arg, SOUND_CAP_EXCL_INPUT);
|
||||
case SOUND_MIXER_WRITE_MIC:
|
||||
case SOUND_MIXER_WRITE_MIC:
|
||||
IOCTL_IN(arg, data);
|
||||
tt_dmasnd.input_gain =
|
||||
RECLEVEL_VOXWARE_TO_GAIN(data & 0xff) << 4 |
|
||||
RECLEVEL_VOXWARE_TO_GAIN(data >> 8 & 0xff);
|
||||
/* fall thru, return set value */
|
||||
case SOUND_MIXER_READ_MIC:
|
||||
/* fall through - return set value */
|
||||
case SOUND_MIXER_READ_MIC:
|
||||
return IOCTL_OUT(arg,
|
||||
RECLEVEL_GAIN_TO_VOXWARE(tt_dmasnd.input_gain >> 4 & 0xf) |
|
||||
RECLEVEL_GAIN_TO_VOXWARE(tt_dmasnd.input_gain & 0xf) << 8);
|
||||
|
|
|
@ -596,11 +596,6 @@ static int snd_ac97_put_volsw(struct snd_kcontrol *kcontrol,
|
|||
return err;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new snd_ac97_controls_master_mono[2] = {
|
||||
AC97_SINGLE("Master Mono Playback Switch", AC97_MASTER_MONO, 15, 1, 1),
|
||||
AC97_SINGLE("Master Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 1)
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new snd_ac97_controls_tone[2] = {
|
||||
AC97_SINGLE("Tone Control - Bass", AC97_MASTER_TONE, 8, 15, 1),
|
||||
AC97_SINGLE("Tone Control - Treble", AC97_MASTER_TONE, 0, 15, 1)
|
||||
|
|
|
@ -2189,11 +2189,10 @@ static int snd_echo_resume(struct device *dev)
|
|||
u32 pipe_alloc_mask;
|
||||
int err;
|
||||
|
||||
commpage_bak = kmalloc(sizeof(*commpage), GFP_KERNEL);
|
||||
commpage = chip->comm_page;
|
||||
commpage_bak = kmemdup(commpage, sizeof(*commpage), GFP_KERNEL);
|
||||
if (commpage_bak == NULL)
|
||||
return -ENOMEM;
|
||||
commpage = chip->comm_page;
|
||||
memcpy(commpage_bak, commpage, sizeof(*commpage));
|
||||
|
||||
err = init_hw(chip, chip->pci->device, chip->pci->subsystem_device);
|
||||
if (err < 0) {
|
||||
|
|
|
@ -12,6 +12,7 @@ config SND_HDA_INTEL
|
|||
tristate "HD Audio PCI"
|
||||
depends on SND_PCI
|
||||
select SND_HDA
|
||||
select SND_INTEL_NHLT if ACPI
|
||||
help
|
||||
Say Y here to include support for Intel "High Definition
|
||||
Audio" (Azalia) and its compatible devices.
|
||||
|
@ -22,10 +23,20 @@ config SND_HDA_INTEL
|
|||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-hda-intel.
|
||||
|
||||
config SND_HDA_INTEL_DETECT_DMIC
|
||||
bool "DMIC detection and probe abort"
|
||||
depends on SND_HDA_INTEL
|
||||
help
|
||||
Say Y to detect digital microphones on SKL+ devices. DMICs
|
||||
cannot be handled by the HDaudio legacy driver and are
|
||||
currently only supported by the SOF driver.
|
||||
If unsure say N.
|
||||
|
||||
config SND_HDA_TEGRA
|
||||
tristate "NVIDIA Tegra HD Audio"
|
||||
depends on ARCH_TEGRA
|
||||
select SND_HDA
|
||||
select SND_HDA_ALIGNED_MMIO
|
||||
help
|
||||
Say Y here to support the HDA controller present in NVIDIA
|
||||
Tegra SoCs
|
||||
|
|
|
@ -884,7 +884,8 @@ EXPORT_SYMBOL_GPL(snd_hda_apply_fixup);
|
|||
#define IGNORE_SEQ_ASSOC (~(AC_DEFCFG_SEQUENCE | AC_DEFCFG_DEF_ASSOC))
|
||||
|
||||
static bool pin_config_match(struct hda_codec *codec,
|
||||
const struct hda_pintbl *pins)
|
||||
const struct hda_pintbl *pins,
|
||||
bool match_all_pins)
|
||||
{
|
||||
const struct hda_pincfg *pin;
|
||||
int i;
|
||||
|
@ -908,7 +909,8 @@ static bool pin_config_match(struct hda_codec *codec,
|
|||
return false;
|
||||
}
|
||||
}
|
||||
if (!found && (cfg & 0xf0000000) != 0x40000000)
|
||||
if (match_all_pins &&
|
||||
!found && (cfg & 0xf0000000) != 0x40000000)
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -920,10 +922,12 @@ static bool pin_config_match(struct hda_codec *codec,
|
|||
* @codec: the HDA codec
|
||||
* @pin_quirk: zero-terminated pin quirk list
|
||||
* @fixlist: the fixup list
|
||||
* @match_all_pins: all valid pins must match with the table entries
|
||||
*/
|
||||
void snd_hda_pick_pin_fixup(struct hda_codec *codec,
|
||||
const struct snd_hda_pin_quirk *pin_quirk,
|
||||
const struct hda_fixup *fixlist)
|
||||
const struct hda_fixup *fixlist,
|
||||
bool match_all_pins)
|
||||
{
|
||||
const struct snd_hda_pin_quirk *pq;
|
||||
|
||||
|
@ -935,7 +939,7 @@ void snd_hda_pick_pin_fixup(struct hda_codec *codec,
|
|||
continue;
|
||||
if (codec->core.vendor_id != pq->codec)
|
||||
continue;
|
||||
if (pin_config_match(codec, pq->pins)) {
|
||||
if (pin_config_match(codec, pq->pins, match_all_pins)) {
|
||||
codec->fixup_id = pq->value;
|
||||
#ifdef CONFIG_SND_DEBUG_VERBOSE
|
||||
codec->fixup_name = pq->name;
|
||||
|
|
|
@ -846,7 +846,13 @@ static void snd_hda_codec_dev_release(struct device *dev)
|
|||
snd_hda_sysfs_clear(codec);
|
||||
kfree(codec->modelname);
|
||||
kfree(codec->wcaps);
|
||||
kfree(codec);
|
||||
|
||||
/*
|
||||
* In the case of ASoC HD-audio, hda_codec is device managed.
|
||||
* It will be freed when the ASoC device is removed.
|
||||
*/
|
||||
if (codec->core.type == HDA_DEV_LEGACY)
|
||||
kfree(codec);
|
||||
}
|
||||
|
||||
#define DEV_NAME_LEN 31
|
||||
|
|
|
@ -794,6 +794,7 @@ static int azx_rirb_get_response(struct hdac_bus *bus, unsigned int addr,
|
|||
unsigned long timeout;
|
||||
unsigned long loopcounter;
|
||||
int do_poll = 0;
|
||||
bool warned = false;
|
||||
|
||||
again:
|
||||
timeout = jiffies + msecs_to_jiffies(1000);
|
||||
|
@ -813,9 +814,17 @@ static int azx_rirb_get_response(struct hdac_bus *bus, unsigned int addr,
|
|||
spin_unlock_irq(&bus->reg_lock);
|
||||
if (time_after(jiffies, timeout))
|
||||
break;
|
||||
if (hbus->needs_damn_long_delay || loopcounter > 3000)
|
||||
#define LOOP_COUNT_MAX 3000
|
||||
if (hbus->needs_damn_long_delay ||
|
||||
loopcounter > LOOP_COUNT_MAX) {
|
||||
if (loopcounter > LOOP_COUNT_MAX && !warned) {
|
||||
dev_dbg_ratelimited(chip->card->dev,
|
||||
"too slow response, last cmd=%#08x\n",
|
||||
bus->last_cmd[addr]);
|
||||
warned = true;
|
||||
}
|
||||
msleep(2); /* temporary workaround */
|
||||
else {
|
||||
} else {
|
||||
udelay(10);
|
||||
cond_resched();
|
||||
}
|
||||
|
@ -869,10 +878,13 @@ static int azx_rirb_get_response(struct hdac_bus *bus, unsigned int addr,
|
|||
*/
|
||||
if (hbus->allow_bus_reset && !hbus->response_reset && !hbus->in_reset) {
|
||||
hbus->response_reset = 1;
|
||||
dev_err(chip->card->dev,
|
||||
"No response from codec, resetting bus: last cmd=0x%08x\n",
|
||||
bus->last_cmd[addr]);
|
||||
return -EAGAIN; /* give a chance to retry */
|
||||
}
|
||||
|
||||
dev_err(chip->card->dev,
|
||||
dev_WARN(chip->card->dev,
|
||||
"azx_get_response timeout, switching to single_cmd mode: last cmd=0x%08x\n",
|
||||
bus->last_cmd[addr]);
|
||||
chip->single_cmd = 1;
|
||||
|
@ -1207,14 +1219,12 @@ void snd_hda_bus_reset(struct hda_bus *bus)
|
|||
}
|
||||
|
||||
/* HD-audio bus initialization */
|
||||
int azx_bus_init(struct azx *chip, const char *model,
|
||||
const struct hdac_io_ops *io_ops)
|
||||
int azx_bus_init(struct azx *chip, const char *model)
|
||||
{
|
||||
struct hda_bus *bus = &chip->bus;
|
||||
int err;
|
||||
|
||||
err = snd_hdac_bus_init(&bus->core, chip->card->dev, &bus_core_ops,
|
||||
io_ops);
|
||||
err = snd_hdac_bus_init(&bus->core, chip->card->dev, &bus_core_ops);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
|
|
@ -206,8 +206,7 @@ void azx_stop_chip(struct azx *chip);
|
|||
irqreturn_t azx_interrupt(int irq, void *dev_id);
|
||||
|
||||
/* Codec interface */
|
||||
int azx_bus_init(struct azx *chip, const char *model,
|
||||
const struct hdac_io_ops *io_ops);
|
||||
int azx_bus_init(struct azx *chip, const char *model);
|
||||
int azx_probe_codecs(struct azx *chip, unsigned int max_slots);
|
||||
int azx_codec_configure(struct azx *chip);
|
||||
int azx_init_streams(struct azx *chip);
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include <sound/initval.h>
|
||||
#include <sound/hdaudio.h>
|
||||
#include <sound/hda_i915.h>
|
||||
#include <sound/intel-nhlt.h>
|
||||
#include <linux/vgaarb.h>
|
||||
#include <linux/vga_switcheroo.h>
|
||||
#include <linux/firmware.h>
|
||||
|
@ -84,8 +85,6 @@ enum {
|
|||
#define INTEL_SCH_HDA_DEVC 0x78
|
||||
#define INTEL_SCH_HDA_DEVC_NOSNOOP (0x1<<11)
|
||||
|
||||
/* Define IN stream 0 FIFO size offset in VIA controller */
|
||||
#define VIA_IN_STREAM0_FIFO_SIZE_OFFSET 0x90
|
||||
/* Define VIA HD Audio Device ID*/
|
||||
#define VIA_HDAC_DEVICE_ID 0x3288
|
||||
|
||||
|
@ -125,6 +124,7 @@ static char *patch[SNDRV_CARDS];
|
|||
static bool beep_mode[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] =
|
||||
CONFIG_SND_HDA_INPUT_BEEP_MODE};
|
||||
#endif
|
||||
static bool dmic_detect = IS_ENABLED(CONFIG_SND_HDA_INTEL_DETECT_DMIC);
|
||||
|
||||
module_param_array(index, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(index, "Index value for Intel HD audio interface.");
|
||||
|
@ -159,6 +159,8 @@ module_param_array(beep_mode, bool, NULL, 0444);
|
|||
MODULE_PARM_DESC(beep_mode, "Select HDA Beep registration mode "
|
||||
"(0=off, 1=on) (default=1).");
|
||||
#endif
|
||||
module_param(dmic_detect, bool, 0444);
|
||||
MODULE_PARM_DESC(dmic_detect, "DMIC detect on SKL+ platforms");
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int param_set_xint(const char *val, const struct kernel_param *kp);
|
||||
|
@ -267,6 +269,7 @@ enum {
|
|||
AZX_DRIVER_CTX,
|
||||
AZX_DRIVER_CTHDA,
|
||||
AZX_DRIVER_CMEDIA,
|
||||
AZX_DRIVER_ZHAOXIN,
|
||||
AZX_DRIVER_GENERIC,
|
||||
AZX_NUM_DRIVERS, /* keep this as last entry */
|
||||
};
|
||||
|
@ -353,7 +356,7 @@ enum {
|
|||
*/
|
||||
#ifdef SUPPORT_VGA_SWITCHEROO
|
||||
#define use_vga_switcheroo(chip) ((chip)->use_vga_switcheroo)
|
||||
#define needs_eld_notify_link(chip) ((chip)->need_eld_notify_link)
|
||||
#define needs_eld_notify_link(chip) ((chip)->bus.keep_power)
|
||||
#else
|
||||
#define use_vga_switcheroo(chip) 0
|
||||
#define needs_eld_notify_link(chip) false
|
||||
|
@ -385,6 +388,7 @@ static char *driver_short_names[] = {
|
|||
[AZX_DRIVER_CTX] = "HDA Creative",
|
||||
[AZX_DRIVER_CTHDA] = "HDA Creative",
|
||||
[AZX_DRIVER_CMEDIA] = "HDA C-Media",
|
||||
[AZX_DRIVER_ZHAOXIN] = "HDA Zhaoxin",
|
||||
[AZX_DRIVER_GENERIC] = "HD-Audio Generic",
|
||||
};
|
||||
|
||||
|
@ -811,11 +815,7 @@ static unsigned int azx_via_get_position(struct azx *chip,
|
|||
mod_dma_pos = le32_to_cpu(*azx_dev->core.posbuf);
|
||||
mod_dma_pos %= azx_dev->core.period_bytes;
|
||||
|
||||
/* azx_dev->fifo_size can't get FIFO size of in stream.
|
||||
* Get from base address + offset.
|
||||
*/
|
||||
fifo_size = readw(azx_bus(chip)->remap_addr +
|
||||
VIA_IN_STREAM0_FIFO_SIZE_OFFSET);
|
||||
fifo_size = azx_stream(azx_dev)->fifo_size - 1;
|
||||
|
||||
if (azx_dev->insufficient) {
|
||||
/* Link position never gather than FIFO size */
|
||||
|
@ -1145,7 +1145,7 @@ static int azx_runtime_idle(struct device *dev)
|
|||
return -EBUSY;
|
||||
|
||||
/* ELD notification gets broken when HD-audio bus is off */
|
||||
if (needs_eld_notify_link(hda))
|
||||
if (needs_eld_notify_link(chip))
|
||||
return -EBUSY;
|
||||
|
||||
return 0;
|
||||
|
@ -1256,7 +1256,7 @@ static void setup_vga_switcheroo_runtime_pm(struct azx *chip)
|
|||
struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
|
||||
struct hda_codec *codec;
|
||||
|
||||
if (hda->use_vga_switcheroo && !hda->need_eld_notify_link) {
|
||||
if (hda->use_vga_switcheroo && !needs_eld_notify_link(chip)) {
|
||||
list_for_each_codec(codec, &chip->bus)
|
||||
codec->auto_runtime_pm = 1;
|
||||
/* reset the power save setup */
|
||||
|
@ -1270,10 +1270,9 @@ static void azx_vs_gpu_bound(struct pci_dev *pci,
|
|||
{
|
||||
struct snd_card *card = pci_get_drvdata(pci);
|
||||
struct azx *chip = card->private_data;
|
||||
struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
|
||||
|
||||
if (client_id == VGA_SWITCHEROO_DIS)
|
||||
hda->need_eld_notify_link = 0;
|
||||
chip->bus.keep_power = 0;
|
||||
setup_vga_switcheroo_runtime_pm(chip);
|
||||
}
|
||||
|
||||
|
@ -1285,7 +1284,7 @@ static void init_vga_switcheroo(struct azx *chip)
|
|||
dev_info(chip->card->dev,
|
||||
"Handle vga_switcheroo audio client\n");
|
||||
hda->use_vga_switcheroo = 1;
|
||||
hda->need_eld_notify_link = 1; /* cleared in gpu_bound op */
|
||||
chip->bus.keep_power = 1; /* cleared in either gpu_bound op or codec probe */
|
||||
chip->driver_caps |= AZX_DCAPS_PM_RUNTIME;
|
||||
pci_dev_put(p);
|
||||
}
|
||||
|
@ -1349,9 +1348,9 @@ static int azx_free(struct azx *chip)
|
|||
}
|
||||
|
||||
if (bus->chip_init) {
|
||||
azx_stop_chip(chip);
|
||||
azx_clear_irq_pending(chip);
|
||||
azx_stop_all_streams(chip);
|
||||
azx_stop_chip(chip);
|
||||
}
|
||||
|
||||
if (bus->irq >= 0)
|
||||
|
@ -1684,7 +1683,6 @@ static int default_bdl_pos_adj(struct azx *chip)
|
|||
/*
|
||||
* constructor
|
||||
*/
|
||||
static const struct hdac_io_ops pci_hda_io_ops;
|
||||
static const struct hda_controller_ops pci_hda_ops;
|
||||
|
||||
static int azx_create(struct snd_card *card, struct pci_dev *pci,
|
||||
|
@ -1744,13 +1742,17 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci,
|
|||
else
|
||||
chip->bdl_pos_adj = bdl_pos_adj[dev];
|
||||
|
||||
err = azx_bus_init(chip, model[dev], &pci_hda_io_ops);
|
||||
err = azx_bus_init(chip, model[dev]);
|
||||
if (err < 0) {
|
||||
kfree(hda);
|
||||
pci_disable_device(pci);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* use the non-cached pages in non-snoop mode */
|
||||
if (!azx_snoop(chip))
|
||||
azx_bus(chip)->dma_type = SNDRV_DMA_TYPE_DEV_UC;
|
||||
|
||||
/* Workaround for a communication error on CFL (bko#199007) and CNL */
|
||||
if (IS_CFL(pci) || IS_CNL(pci))
|
||||
azx_bus(chip)->polling_mode = 1;
|
||||
|
@ -1985,41 +1987,6 @@ static void azx_firmware_cb(const struct firmware *fw, void *context)
|
|||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* HDA controller ops.
|
||||
*/
|
||||
|
||||
/* PCI register access. */
|
||||
static void pci_azx_writel(u32 value, u32 __iomem *addr)
|
||||
{
|
||||
writel(value, addr);
|
||||
}
|
||||
|
||||
static u32 pci_azx_readl(u32 __iomem *addr)
|
||||
{
|
||||
return readl(addr);
|
||||
}
|
||||
|
||||
static void pci_azx_writew(u16 value, u16 __iomem *addr)
|
||||
{
|
||||
writew(value, addr);
|
||||
}
|
||||
|
||||
static u16 pci_azx_readw(u16 __iomem *addr)
|
||||
{
|
||||
return readw(addr);
|
||||
}
|
||||
|
||||
static void pci_azx_writeb(u8 value, u8 __iomem *addr)
|
||||
{
|
||||
writeb(value, addr);
|
||||
}
|
||||
|
||||
static u8 pci_azx_readb(u8 __iomem *addr)
|
||||
{
|
||||
return readb(addr);
|
||||
}
|
||||
|
||||
static int disable_msi_reset_irq(struct azx *chip)
|
||||
{
|
||||
struct hdac_bus *bus = azx_bus(chip);
|
||||
|
@ -2036,24 +2003,6 @@ static int disable_msi_reset_irq(struct azx *chip)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* DMA page allocation helpers. */
|
||||
static int dma_alloc_pages(struct hdac_bus *bus,
|
||||
int type,
|
||||
size_t size,
|
||||
struct snd_dma_buffer *buf)
|
||||
{
|
||||
struct azx *chip = bus_to_azx(bus);
|
||||
|
||||
if (!azx_snoop(chip) && type == SNDRV_DMA_TYPE_DEV)
|
||||
type = SNDRV_DMA_TYPE_DEV_UC;
|
||||
return snd_dma_alloc_pages(type, bus->dev, size, buf);
|
||||
}
|
||||
|
||||
static void dma_free_pages(struct hdac_bus *bus, struct snd_dma_buffer *buf)
|
||||
{
|
||||
snd_dma_free_pages(buf);
|
||||
}
|
||||
|
||||
static void pcm_mmap_prepare(struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *area)
|
||||
{
|
||||
|
@ -2065,23 +2014,31 @@ static void pcm_mmap_prepare(struct snd_pcm_substream *substream,
|
|||
#endif
|
||||
}
|
||||
|
||||
static const struct hdac_io_ops pci_hda_io_ops = {
|
||||
.reg_writel = pci_azx_writel,
|
||||
.reg_readl = pci_azx_readl,
|
||||
.reg_writew = pci_azx_writew,
|
||||
.reg_readw = pci_azx_readw,
|
||||
.reg_writeb = pci_azx_writeb,
|
||||
.reg_readb = pci_azx_readb,
|
||||
.dma_alloc_pages = dma_alloc_pages,
|
||||
.dma_free_pages = dma_free_pages,
|
||||
};
|
||||
|
||||
static const struct hda_controller_ops pci_hda_ops = {
|
||||
.disable_msi_reset_irq = disable_msi_reset_irq,
|
||||
.pcm_mmap_prepare = pcm_mmap_prepare,
|
||||
.position_check = azx_position_check,
|
||||
};
|
||||
|
||||
static int azx_check_dmic(struct pci_dev *pci, struct azx *chip)
|
||||
{
|
||||
struct nhlt_acpi_table *nhlt;
|
||||
int ret = 0;
|
||||
|
||||
if (chip->driver_type == AZX_DRIVER_SKL &&
|
||||
pci->class != 0x040300) {
|
||||
nhlt = intel_nhlt_init(&pci->dev);
|
||||
if (nhlt) {
|
||||
if (intel_nhlt_get_dmic_geo(&pci->dev, nhlt)) {
|
||||
ret = -ENODEV;
|
||||
dev_info(&pci->dev, "Digital mics found on Skylake+ platform, aborting probe\n");
|
||||
}
|
||||
intel_nhlt_free(nhlt);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int azx_probe(struct pci_dev *pci,
|
||||
const struct pci_device_id *pci_id)
|
||||
{
|
||||
|
@ -2112,6 +2069,17 @@ static int azx_probe(struct pci_dev *pci,
|
|||
card->private_data = chip;
|
||||
hda = container_of(chip, struct hda_intel, chip);
|
||||
|
||||
/*
|
||||
* stop probe if digital microphones detected on Skylake+ platform
|
||||
* with the DSP enabled. This is an opt-in behavior defined at build
|
||||
* time or at run-time with a module parameter
|
||||
*/
|
||||
if (dmic_detect) {
|
||||
err = azx_check_dmic(pci, chip);
|
||||
if (err < 0)
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
pci_set_drvdata(pci, card);
|
||||
|
||||
err = register_vga_switcheroo(chip);
|
||||
|
@ -2653,6 +2621,8 @@ static const struct pci_device_id azx_ids[] = {
|
|||
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
|
||||
.class_mask = 0xffffff,
|
||||
.driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_HDMI },
|
||||
/* Zhaoxin */
|
||||
{ PCI_DEVICE(0x1d17, 0x3288), .driver_data = AZX_DRIVER_ZHAOXIN },
|
||||
{ 0, }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, azx_ids);
|
||||
|
|
|
@ -25,7 +25,6 @@ struct hda_intel {
|
|||
|
||||
/* vga_switcheroo setup */
|
||||
unsigned int use_vga_switcheroo:1;
|
||||
unsigned int need_eld_notify_link:1;
|
||||
unsigned int vga_switcheroo_registered:1;
|
||||
unsigned int init_failed:1; /* delayed init failed */
|
||||
|
||||
|
|
|
@ -361,7 +361,8 @@ void snd_hda_pick_fixup(struct hda_codec *codec,
|
|||
const struct hda_fixup *fixlist);
|
||||
void snd_hda_pick_pin_fixup(struct hda_codec *codec,
|
||||
const struct snd_hda_pin_quirk *pin_quirk,
|
||||
const struct hda_fixup *fixlist);
|
||||
const struct hda_fixup *fixlist,
|
||||
bool match_all_pins);
|
||||
|
||||
/* helper macros to retrieve pin default-config values */
|
||||
#define get_defcfg_connect(cfg) \
|
||||
|
|
|
@ -75,88 +75,6 @@ MODULE_PARM_DESC(power_save,
|
|||
#define power_save 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
* DMA page allocation ops.
|
||||
*/
|
||||
static int dma_alloc_pages(struct hdac_bus *bus, int type, size_t size,
|
||||
struct snd_dma_buffer *buf)
|
||||
{
|
||||
return snd_dma_alloc_pages(type, bus->dev, size, buf);
|
||||
}
|
||||
|
||||
static void dma_free_pages(struct hdac_bus *bus, struct snd_dma_buffer *buf)
|
||||
{
|
||||
snd_dma_free_pages(buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Register access ops. Tegra HDA register access is DWORD only.
|
||||
*/
|
||||
static void hda_tegra_writel(u32 value, u32 __iomem *addr)
|
||||
{
|
||||
writel(value, addr);
|
||||
}
|
||||
|
||||
static u32 hda_tegra_readl(u32 __iomem *addr)
|
||||
{
|
||||
return readl(addr);
|
||||
}
|
||||
|
||||
static void hda_tegra_writew(u16 value, u16 __iomem *addr)
|
||||
{
|
||||
unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
|
||||
void __iomem *dword_addr = (void __iomem *)((unsigned long)(addr) & ~0x3);
|
||||
u32 v;
|
||||
|
||||
v = readl(dword_addr);
|
||||
v &= ~(0xffff << shift);
|
||||
v |= value << shift;
|
||||
writel(v, dword_addr);
|
||||
}
|
||||
|
||||
static u16 hda_tegra_readw(u16 __iomem *addr)
|
||||
{
|
||||
unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
|
||||
void __iomem *dword_addr = (void __iomem *)((unsigned long)(addr) & ~0x3);
|
||||
u32 v;
|
||||
|
||||
v = readl(dword_addr);
|
||||
return (v >> shift) & 0xffff;
|
||||
}
|
||||
|
||||
static void hda_tegra_writeb(u8 value, u8 __iomem *addr)
|
||||
{
|
||||
unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
|
||||
void __iomem *dword_addr = (void __iomem *)((unsigned long)(addr) & ~0x3);
|
||||
u32 v;
|
||||
|
||||
v = readl(dword_addr);
|
||||
v &= ~(0xff << shift);
|
||||
v |= value << shift;
|
||||
writel(v, dword_addr);
|
||||
}
|
||||
|
||||
static u8 hda_tegra_readb(u8 __iomem *addr)
|
||||
{
|
||||
unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
|
||||
void __iomem *dword_addr = (void __iomem *)((unsigned long)(addr) & ~0x3);
|
||||
u32 v;
|
||||
|
||||
v = readl(dword_addr);
|
||||
return (v >> shift) & 0xff;
|
||||
}
|
||||
|
||||
static const struct hdac_io_ops hda_tegra_io_ops = {
|
||||
.reg_writel = hda_tegra_writel,
|
||||
.reg_readl = hda_tegra_readl,
|
||||
.reg_writew = hda_tegra_writew,
|
||||
.reg_readw = hda_tegra_readw,
|
||||
.reg_writeb = hda_tegra_writeb,
|
||||
.reg_readb = hda_tegra_readb,
|
||||
.dma_alloc_pages = dma_alloc_pages,
|
||||
.dma_free_pages = dma_free_pages,
|
||||
};
|
||||
|
||||
static const struct hda_controller_ops hda_tegra_ops; /* nothing special */
|
||||
|
||||
static void hda_tegra_init(struct hda_tegra *hda)
|
||||
|
@ -475,7 +393,7 @@ static int hda_tegra_create(struct snd_card *card,
|
|||
|
||||
INIT_WORK(&hda->probe_work, hda_tegra_probe_work);
|
||||
|
||||
err = azx_bus_init(chip, NULL, &hda_tegra_io_ops);
|
||||
err = azx_bus_init(chip, NULL);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
@ -119,6 +120,7 @@ struct hdmi_pcm {
|
|||
};
|
||||
|
||||
struct hdmi_spec {
|
||||
struct hda_codec *codec;
|
||||
int num_cvts;
|
||||
struct snd_array cvts; /* struct hdmi_spec_per_cvt */
|
||||
hda_nid_t cvt_nids[4]; /* only for haswell fix */
|
||||
|
@ -163,9 +165,11 @@ struct hdmi_spec {
|
|||
struct hda_multi_out multiout;
|
||||
struct hda_pcm_stream pcm_playback;
|
||||
|
||||
/* i915/powerwell (Haswell+/Valleyview+) specific */
|
||||
bool use_acomp_notifier; /* use i915 eld_notify callback for hotplug */
|
||||
bool use_jack_detect; /* jack detection enabled */
|
||||
bool use_acomp_notifier; /* use eld_notify callback for hotplug */
|
||||
bool acomp_registered; /* audio component registered in this driver */
|
||||
struct drm_audio_component_audio_ops drm_audio_ops;
|
||||
int (*port2pin)(struct hda_codec *, int); /* reverse port/pin mapping */
|
||||
|
||||
struct hdac_chmap chmap;
|
||||
hda_nid_t vendor_nid;
|
||||
|
@ -765,6 +769,10 @@ static void check_presence_and_report(struct hda_codec *codec, hda_nid_t nid,
|
|||
static void jack_callback(struct hda_codec *codec,
|
||||
struct hda_jack_callback *jack)
|
||||
{
|
||||
/* stop polling when notification is enabled */
|
||||
if (codec_has_acomp(codec))
|
||||
return;
|
||||
|
||||
/* hda_jack don't support DP MST */
|
||||
check_presence_and_report(codec, jack->nid, 0);
|
||||
}
|
||||
|
@ -823,6 +831,9 @@ static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
|
|||
int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
|
||||
int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
|
||||
|
||||
if (codec_has_acomp(codec))
|
||||
return;
|
||||
|
||||
if (!snd_hda_jack_tbl_get_from_tag(codec, tag)) {
|
||||
codec_dbg(codec, "Unexpected HDMI event tag 0x%x\n", tag);
|
||||
return;
|
||||
|
@ -1421,7 +1432,7 @@ static void hdmi_pcm_reset_pin(struct hdmi_spec *spec,
|
|||
/* update per_pin ELD from the given new ELD;
|
||||
* setup info frame and notification accordingly
|
||||
*/
|
||||
static void update_eld(struct hda_codec *codec,
|
||||
static bool update_eld(struct hda_codec *codec,
|
||||
struct hdmi_spec_per_pin *per_pin,
|
||||
struct hdmi_eld *eld)
|
||||
{
|
||||
|
@ -1429,7 +1440,7 @@ static void update_eld(struct hda_codec *codec,
|
|||
struct hdmi_spec *spec = codec->spec;
|
||||
bool old_eld_valid = pin_eld->eld_valid;
|
||||
bool eld_changed;
|
||||
int pcm_idx = -1;
|
||||
int pcm_idx;
|
||||
|
||||
/* for monitor disconnection, save pcm_idx firstly */
|
||||
pcm_idx = per_pin->pcm_idx;
|
||||
|
@ -1452,18 +1463,22 @@ static void update_eld(struct hda_codec *codec,
|
|||
snd_hdmi_show_eld(codec, &eld->info);
|
||||
|
||||
eld_changed = (pin_eld->eld_valid != eld->eld_valid);
|
||||
if (eld->eld_valid && pin_eld->eld_valid)
|
||||
eld_changed |= (pin_eld->monitor_present != eld->monitor_present);
|
||||
if (!eld_changed && eld->eld_valid && pin_eld->eld_valid)
|
||||
if (pin_eld->eld_size != eld->eld_size ||
|
||||
memcmp(pin_eld->eld_buffer, eld->eld_buffer,
|
||||
eld->eld_size) != 0)
|
||||
eld_changed = true;
|
||||
|
||||
pin_eld->monitor_present = eld->monitor_present;
|
||||
pin_eld->eld_valid = eld->eld_valid;
|
||||
pin_eld->eld_size = eld->eld_size;
|
||||
if (eld->eld_valid)
|
||||
memcpy(pin_eld->eld_buffer, eld->eld_buffer, eld->eld_size);
|
||||
pin_eld->info = eld->info;
|
||||
if (eld_changed) {
|
||||
pin_eld->monitor_present = eld->monitor_present;
|
||||
pin_eld->eld_valid = eld->eld_valid;
|
||||
pin_eld->eld_size = eld->eld_size;
|
||||
if (eld->eld_valid)
|
||||
memcpy(pin_eld->eld_buffer, eld->eld_buffer,
|
||||
eld->eld_size);
|
||||
pin_eld->info = eld->info;
|
||||
}
|
||||
|
||||
/*
|
||||
* Re-setup pin and infoframe. This is needed e.g. when
|
||||
|
@ -1481,6 +1496,7 @@ static void update_eld(struct hda_codec *codec,
|
|||
SNDRV_CTL_EVENT_MASK_VALUE |
|
||||
SNDRV_CTL_EVENT_MASK_INFO,
|
||||
&get_hdmi_pcm(spec, pcm_idx)->eld_ctl->id);
|
||||
return eld_changed;
|
||||
}
|
||||
|
||||
/* update ELD and jack state via HD-audio verbs */
|
||||
|
@ -1582,6 +1598,7 @@ static void sync_eld_via_acomp(struct hda_codec *codec,
|
|||
struct hdmi_spec *spec = codec->spec;
|
||||
struct hdmi_eld *eld = &spec->temp_eld;
|
||||
struct snd_jack *jack = NULL;
|
||||
bool changed;
|
||||
int size;
|
||||
|
||||
mutex_lock(&per_pin->lock);
|
||||
|
@ -1608,15 +1625,13 @@ static void sync_eld_via_acomp(struct hda_codec *codec,
|
|||
* disconnected event. Jack must be fetched before update_eld()
|
||||
*/
|
||||
jack = pin_idx_to_jack(codec, per_pin);
|
||||
update_eld(codec, per_pin, eld);
|
||||
changed = update_eld(codec, per_pin, eld);
|
||||
if (jack == NULL)
|
||||
jack = pin_idx_to_jack(codec, per_pin);
|
||||
if (jack == NULL)
|
||||
goto unlock;
|
||||
snd_jack_report(jack,
|
||||
(eld->monitor_present && eld->eld_valid) ?
|
||||
if (changed && jack)
|
||||
snd_jack_report(jack,
|
||||
(eld->monitor_present && eld->eld_valid) ?
|
||||
SND_JACK_AVOUT : 0);
|
||||
unlock:
|
||||
mutex_unlock(&per_pin->lock);
|
||||
}
|
||||
|
||||
|
@ -1632,18 +1647,13 @@ static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
|
|||
snd_hda_power_down_pm(codec);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (codec_has_acomp(codec)) {
|
||||
ret = hdmi_present_sense_via_verbs(per_pin, repoll);
|
||||
snd_hda_power_down_pm(codec);
|
||||
} else {
|
||||
sync_eld_via_acomp(codec, per_pin);
|
||||
ret = false; /* don't call snd_hda_jack_report_sync() */
|
||||
} else {
|
||||
ret = hdmi_present_sense_via_verbs(per_pin, repoll);
|
||||
}
|
||||
|
||||
if (!codec_has_acomp(codec))
|
||||
snd_hda_power_down_pm(codec);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -2248,6 +2258,8 @@ static int generic_hdmi_init(struct hda_codec *codec)
|
|||
struct hdmi_spec *spec = codec->spec;
|
||||
int pin_idx;
|
||||
|
||||
mutex_lock(&spec->pcm_lock);
|
||||
spec->use_jack_detect = !codec->jackpoll_interval;
|
||||
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
|
||||
struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
|
||||
hda_nid_t pin_nid = per_pin->pin_nid;
|
||||
|
@ -2255,11 +2267,15 @@ static int generic_hdmi_init(struct hda_codec *codec)
|
|||
|
||||
snd_hda_set_dev_select(codec, pin_nid, dev_id);
|
||||
hdmi_init_pin(codec, pin_nid);
|
||||
if (!codec_has_acomp(codec))
|
||||
if (codec_has_acomp(codec))
|
||||
continue;
|
||||
if (spec->use_jack_detect)
|
||||
snd_hda_jack_detect_enable(codec, pin_nid);
|
||||
else
|
||||
snd_hda_jack_detect_enable_callback(codec, pin_nid,
|
||||
codec->jackpoll_interval > 0 ?
|
||||
jack_callback : NULL);
|
||||
jack_callback);
|
||||
}
|
||||
mutex_unlock(&spec->pcm_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2292,7 +2308,9 @@ static void generic_hdmi_free(struct hda_codec *codec)
|
|||
struct hdmi_spec *spec = codec->spec;
|
||||
int pin_idx, pcm_idx;
|
||||
|
||||
if (codec_has_acomp(codec)) {
|
||||
if (spec->acomp_registered) {
|
||||
snd_hdac_acomp_exit(&codec->bus->core);
|
||||
} else if (codec_has_acomp(codec)) {
|
||||
snd_hdac_acomp_register_notifier(&codec->bus->core, NULL);
|
||||
codec->relaxed_resume = 0;
|
||||
}
|
||||
|
@ -2360,6 +2378,7 @@ static int alloc_generic_hdmi(struct hda_codec *codec)
|
|||
if (!spec)
|
||||
return -ENOMEM;
|
||||
|
||||
spec->codec = codec;
|
||||
spec->ops = generic_standard_hdmi_ops;
|
||||
spec->dev_num = 1; /* initialize to 1 */
|
||||
mutex_init(&spec->pcm_lock);
|
||||
|
@ -2397,6 +2416,138 @@ static int patch_generic_hdmi(struct hda_codec *codec)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* generic audio component binding
|
||||
*/
|
||||
|
||||
/* turn on / off the unsol event jack detection dynamically */
|
||||
static void reprogram_jack_detect(struct hda_codec *codec, hda_nid_t nid,
|
||||
bool use_acomp)
|
||||
{
|
||||
struct hda_jack_tbl *tbl;
|
||||
|
||||
tbl = snd_hda_jack_tbl_get(codec, nid);
|
||||
if (tbl) {
|
||||
/* clear unsol even if component notifier is used, or re-enable
|
||||
* if notifier is cleared
|
||||
*/
|
||||
unsigned int val = use_acomp ? 0 : (AC_USRSP_EN | tbl->tag);
|
||||
snd_hda_codec_write_cache(codec, nid, 0,
|
||||
AC_VERB_SET_UNSOLICITED_ENABLE, val);
|
||||
} else {
|
||||
/* if no jack entry was defined beforehand, create a new one
|
||||
* at need (i.e. only when notifier is cleared)
|
||||
*/
|
||||
if (!use_acomp)
|
||||
snd_hda_jack_detect_enable(codec, nid);
|
||||
}
|
||||
}
|
||||
|
||||
/* set up / clear component notifier dynamically */
|
||||
static void generic_acomp_notifier_set(struct drm_audio_component *acomp,
|
||||
bool use_acomp)
|
||||
{
|
||||
struct hdmi_spec *spec;
|
||||
int i;
|
||||
|
||||
spec = container_of(acomp->audio_ops, struct hdmi_spec, drm_audio_ops);
|
||||
mutex_lock(&spec->pcm_lock);
|
||||
spec->use_acomp_notifier = use_acomp;
|
||||
spec->codec->relaxed_resume = use_acomp;
|
||||
/* reprogram each jack detection logic depending on the notifier */
|
||||
if (spec->use_jack_detect) {
|
||||
for (i = 0; i < spec->num_pins; i++)
|
||||
reprogram_jack_detect(spec->codec,
|
||||
get_pin(spec, i)->pin_nid,
|
||||
use_acomp);
|
||||
}
|
||||
mutex_unlock(&spec->pcm_lock);
|
||||
}
|
||||
|
||||
/* enable / disable the notifier via master bind / unbind */
|
||||
static int generic_acomp_master_bind(struct device *dev,
|
||||
struct drm_audio_component *acomp)
|
||||
{
|
||||
generic_acomp_notifier_set(acomp, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void generic_acomp_master_unbind(struct device *dev,
|
||||
struct drm_audio_component *acomp)
|
||||
{
|
||||
generic_acomp_notifier_set(acomp, false);
|
||||
}
|
||||
|
||||
/* check whether both HD-audio and DRM PCI devices belong to the same bus */
|
||||
static int match_bound_vga(struct device *dev, int subtype, void *data)
|
||||
{
|
||||
struct hdac_bus *bus = data;
|
||||
struct pci_dev *pci, *master;
|
||||
|
||||
if (!dev_is_pci(dev) || !dev_is_pci(bus->dev))
|
||||
return 0;
|
||||
master = to_pci_dev(bus->dev);
|
||||
pci = to_pci_dev(dev);
|
||||
return master->bus == pci->bus;
|
||||
}
|
||||
|
||||
/* audio component notifier for AMD/Nvidia HDMI codecs */
|
||||
static void generic_acomp_pin_eld_notify(void *audio_ptr, int port, int dev_id)
|
||||
{
|
||||
struct hda_codec *codec = audio_ptr;
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
hda_nid_t pin_nid = spec->port2pin(codec, port);
|
||||
|
||||
if (!pin_nid)
|
||||
return;
|
||||
if (get_wcaps_type(get_wcaps(codec, pin_nid)) != AC_WID_PIN)
|
||||
return;
|
||||
/* skip notification during system suspend (but not in runtime PM);
|
||||
* the state will be updated at resume
|
||||
*/
|
||||
if (snd_power_get_state(codec->card) != SNDRV_CTL_POWER_D0)
|
||||
return;
|
||||
/* ditto during suspend/resume process itself */
|
||||
if (snd_hdac_is_in_pm(&codec->core))
|
||||
return;
|
||||
|
||||
check_presence_and_report(codec, pin_nid, dev_id);
|
||||
}
|
||||
|
||||
/* set up the private drm_audio_ops from the template */
|
||||
static void setup_drm_audio_ops(struct hda_codec *codec,
|
||||
const struct drm_audio_component_audio_ops *ops)
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
|
||||
spec->drm_audio_ops.audio_ptr = codec;
|
||||
/* intel_audio_codec_enable() or intel_audio_codec_disable()
|
||||
* will call pin_eld_notify with using audio_ptr pointer
|
||||
* We need make sure audio_ptr is really setup
|
||||
*/
|
||||
wmb();
|
||||
spec->drm_audio_ops.pin2port = ops->pin2port;
|
||||
spec->drm_audio_ops.pin_eld_notify = ops->pin_eld_notify;
|
||||
spec->drm_audio_ops.master_bind = ops->master_bind;
|
||||
spec->drm_audio_ops.master_unbind = ops->master_unbind;
|
||||
}
|
||||
|
||||
/* initialize the generic HDMI audio component */
|
||||
static void generic_acomp_init(struct hda_codec *codec,
|
||||
const struct drm_audio_component_audio_ops *ops,
|
||||
int (*port2pin)(struct hda_codec *, int))
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
|
||||
spec->port2pin = port2pin;
|
||||
setup_drm_audio_ops(codec, ops);
|
||||
if (!snd_hdac_acomp_init(&codec->bus->core, &spec->drm_audio_ops,
|
||||
match_bound_vga, 0)) {
|
||||
spec->acomp_registered = true;
|
||||
codec->bus->keep_power = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Intel codec parsers and helpers
|
||||
*/
|
||||
|
@ -2565,20 +2716,19 @@ static void intel_pin_eld_notify(void *audio_ptr, int port, int pipe)
|
|||
check_presence_and_report(codec, pin_nid, dev_id);
|
||||
}
|
||||
|
||||
static const struct drm_audio_component_audio_ops intel_audio_ops = {
|
||||
.pin2port = intel_pin2port,
|
||||
.pin_eld_notify = intel_pin_eld_notify,
|
||||
};
|
||||
|
||||
/* register i915 component pin_eld_notify callback */
|
||||
static void register_i915_notifier(struct hda_codec *codec)
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
|
||||
spec->use_acomp_notifier = true;
|
||||
spec->drm_audio_ops.audio_ptr = codec;
|
||||
/* intel_audio_codec_enable() or intel_audio_codec_disable()
|
||||
* will call pin_eld_notify with using audio_ptr pointer
|
||||
* We need make sure audio_ptr is really setup
|
||||
*/
|
||||
wmb();
|
||||
spec->drm_audio_ops.pin2port = intel_pin2port;
|
||||
spec->drm_audio_ops.pin_eld_notify = intel_pin_eld_notify;
|
||||
spec->port2pin = intel_port2pin;
|
||||
setup_drm_audio_ops(codec, &intel_audio_ops);
|
||||
snd_hdac_acomp_register_notifier(&codec->bus->core,
|
||||
&spec->drm_audio_ops);
|
||||
/* no need for forcible resume for jack check thanks to notifier */
|
||||
|
@ -2612,6 +2762,8 @@ static void i915_pin_cvt_fixup(struct hda_codec *codec,
|
|||
/* precondition and allocation for Intel codecs */
|
||||
static int alloc_intel_hdmi(struct hda_codec *codec)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* requires i915 binding */
|
||||
if (!codec->bus->core.audio_component) {
|
||||
codec_info(codec, "No i915 binding for Intel HDMI/DP codec\n");
|
||||
|
@ -2620,7 +2772,12 @@ static int alloc_intel_hdmi(struct hda_codec *codec)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
return alloc_generic_hdmi(codec);
|
||||
err = alloc_generic_hdmi(codec);
|
||||
if (err < 0)
|
||||
return err;
|
||||
/* no need to handle unsol events */
|
||||
codec->patch_ops.unsol_event = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* parse and post-process for Intel codecs */
|
||||
|
@ -2976,6 +3133,7 @@ static int patch_simple_hdmi(struct hda_codec *codec,
|
|||
if (!spec)
|
||||
return -ENOMEM;
|
||||
|
||||
spec->codec = codec;
|
||||
codec->spec = spec;
|
||||
hdmi_array_init(spec, 1);
|
||||
|
||||
|
@ -3280,6 +3438,26 @@ static int nvhdmi_chmap_validate(struct hdac_chmap *chmap,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* map from pin NID to port; port is 0-based */
|
||||
/* for Nvidia: assume widget NID starting from 4, with step 1 (4, 5, 6, ...) */
|
||||
static int nvhdmi_pin2port(void *audio_ptr, int pin_nid)
|
||||
{
|
||||
return pin_nid - 4;
|
||||
}
|
||||
|
||||
/* reverse-map from port to pin NID: see above */
|
||||
static int nvhdmi_port2pin(struct hda_codec *codec, int port)
|
||||
{
|
||||
return port + 4;
|
||||
}
|
||||
|
||||
static const struct drm_audio_component_audio_ops nvhdmi_audio_ops = {
|
||||
.pin2port = nvhdmi_pin2port,
|
||||
.pin_eld_notify = generic_acomp_pin_eld_notify,
|
||||
.master_bind = generic_acomp_master_bind,
|
||||
.master_unbind = generic_acomp_master_unbind,
|
||||
};
|
||||
|
||||
static int patch_nvhdmi(struct hda_codec *codec)
|
||||
{
|
||||
struct hdmi_spec *spec;
|
||||
|
@ -3296,6 +3474,8 @@ static int patch_nvhdmi(struct hda_codec *codec)
|
|||
nvhdmi_chmap_cea_alloc_validate_get_type;
|
||||
spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate;
|
||||
|
||||
generic_acomp_init(codec, &nvhdmi_audio_ops, nvhdmi_port2pin);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -3783,6 +3963,26 @@ static int atihdmi_init(struct hda_codec *codec)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* map from pin NID to port; port is 0-based */
|
||||
/* for AMD: assume widget NID starting from 3, with step 2 (3, 5, 7, ...) */
|
||||
static int atihdmi_pin2port(void *audio_ptr, int pin_nid)
|
||||
{
|
||||
return pin_nid / 2 - 1;
|
||||
}
|
||||
|
||||
/* reverse-map from port to pin NID: see above */
|
||||
static int atihdmi_port2pin(struct hda_codec *codec, int port)
|
||||
{
|
||||
return port * 2 + 3;
|
||||
}
|
||||
|
||||
static const struct drm_audio_component_audio_ops atihdmi_audio_ops = {
|
||||
.pin2port = atihdmi_pin2port,
|
||||
.pin_eld_notify = generic_acomp_pin_eld_notify,
|
||||
.master_bind = generic_acomp_master_bind,
|
||||
.master_unbind = generic_acomp_master_unbind,
|
||||
};
|
||||
|
||||
static int patch_atihdmi(struct hda_codec *codec)
|
||||
{
|
||||
struct hdmi_spec *spec;
|
||||
|
@ -3831,6 +4031,8 @@ static int patch_atihdmi(struct hda_codec *codec)
|
|||
*/
|
||||
codec->link_down_at_suspend = 1;
|
||||
|
||||
generic_acomp_init(codec, &atihdmi_audio_ops, atihdmi_port2pin);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -1058,6 +1058,9 @@ static const struct snd_pci_quirk beep_white_list[] = {
|
|||
SND_PCI_QUIRK(0x1043, 0x834a, "EeePC", 1),
|
||||
SND_PCI_QUIRK(0x1458, 0xa002, "GA-MA790X", 1),
|
||||
SND_PCI_QUIRK(0x8086, 0xd613, "Intel", 1),
|
||||
/* blacklist -- no beep available */
|
||||
SND_PCI_QUIRK(0x17aa, 0x309e, "Lenovo ThinkCentre M73", 0),
|
||||
SND_PCI_QUIRK(0x17aa, 0x30a3, "Lenovo ThinkCentre M93", 0),
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -2841,7 +2844,8 @@ static int patch_alc268(struct hda_codec *codec)
|
|||
return err;
|
||||
|
||||
spec = codec->spec;
|
||||
spec->gen.beep_nid = 0x01;
|
||||
if (has_cdefine_beep(codec))
|
||||
spec->gen.beep_nid = 0x01;
|
||||
|
||||
spec->shutup = alc_eapd_shutup;
|
||||
|
||||
|
@ -3755,6 +3759,72 @@ static void alc269_x101_hp_automute_hook(struct hda_codec *codec,
|
|||
vref);
|
||||
}
|
||||
|
||||
/*
|
||||
* Magic sequence to make Huawei Matebook X right speaker working (bko#197801)
|
||||
*/
|
||||
struct hda_alc298_mbxinit {
|
||||
unsigned char value_0x23;
|
||||
unsigned char value_0x25;
|
||||
};
|
||||
|
||||
static void alc298_huawei_mbx_stereo_seq(struct hda_codec *codec,
|
||||
const struct hda_alc298_mbxinit *initval,
|
||||
bool first)
|
||||
{
|
||||
snd_hda_codec_write(codec, 0x06, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x0);
|
||||
alc_write_coef_idx(codec, 0x26, 0xb000);
|
||||
|
||||
if (first)
|
||||
snd_hda_codec_write(codec, 0x21, 0, AC_VERB_GET_PIN_SENSE, 0x0);
|
||||
|
||||
snd_hda_codec_write(codec, 0x6, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x80);
|
||||
alc_write_coef_idx(codec, 0x26, 0xf000);
|
||||
alc_write_coef_idx(codec, 0x23, initval->value_0x23);
|
||||
|
||||
if (initval->value_0x23 != 0x1e)
|
||||
alc_write_coef_idx(codec, 0x25, initval->value_0x25);
|
||||
|
||||
snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0x26);
|
||||
snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, 0xb010);
|
||||
}
|
||||
|
||||
static void alc298_fixup_huawei_mbx_stereo(struct hda_codec *codec,
|
||||
const struct hda_fixup *fix,
|
||||
int action)
|
||||
{
|
||||
/* Initialization magic */
|
||||
static const struct hda_alc298_mbxinit dac_init[] = {
|
||||
{0x0c, 0x00}, {0x0d, 0x00}, {0x0e, 0x00}, {0x0f, 0x00},
|
||||
{0x10, 0x00}, {0x1a, 0x40}, {0x1b, 0x82}, {0x1c, 0x00},
|
||||
{0x1d, 0x00}, {0x1e, 0x00}, {0x1f, 0x00},
|
||||
{0x20, 0xc2}, {0x21, 0xc8}, {0x22, 0x26}, {0x23, 0x24},
|
||||
{0x27, 0xff}, {0x28, 0xff}, {0x29, 0xff}, {0x2a, 0x8f},
|
||||
{0x2b, 0x02}, {0x2c, 0x48}, {0x2d, 0x34}, {0x2e, 0x00},
|
||||
{0x2f, 0x00},
|
||||
{0x30, 0x00}, {0x31, 0x00}, {0x32, 0x00}, {0x33, 0x00},
|
||||
{0x34, 0x00}, {0x35, 0x01}, {0x36, 0x93}, {0x37, 0x0c},
|
||||
{0x38, 0x00}, {0x39, 0x00}, {0x3a, 0xf8}, {0x38, 0x80},
|
||||
{}
|
||||
};
|
||||
const struct hda_alc298_mbxinit *seq;
|
||||
|
||||
if (action != HDA_FIXUP_ACT_INIT)
|
||||
return;
|
||||
|
||||
/* Start */
|
||||
snd_hda_codec_write(codec, 0x06, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x00);
|
||||
snd_hda_codec_write(codec, 0x06, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x80);
|
||||
alc_write_coef_idx(codec, 0x26, 0xf000);
|
||||
alc_write_coef_idx(codec, 0x22, 0x31);
|
||||
alc_write_coef_idx(codec, 0x23, 0x0b);
|
||||
alc_write_coef_idx(codec, 0x25, 0x00);
|
||||
snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0x26);
|
||||
snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, 0xb010);
|
||||
|
||||
for (seq = dac_init; seq->value_0x23; seq++)
|
||||
alc298_huawei_mbx_stereo_seq(codec, seq, seq == dac_init);
|
||||
}
|
||||
|
||||
static void alc269_fixup_x101_headset_mic(struct hda_codec *codec,
|
||||
const struct hda_fixup *fix, int action)
|
||||
{
|
||||
|
@ -5780,6 +5850,7 @@ enum {
|
|||
ALC255_FIXUP_DUMMY_LINEOUT_VERB,
|
||||
ALC255_FIXUP_DELL_HEADSET_MIC,
|
||||
ALC256_FIXUP_HUAWEI_MACH_WX9_PINS,
|
||||
ALC298_FIXUP_HUAWEI_MBX_STEREO,
|
||||
ALC295_FIXUP_HP_X360,
|
||||
ALC221_FIXUP_HP_HEADSET_MIC,
|
||||
ALC285_FIXUP_LENOVO_HEADPHONE_NOISE,
|
||||
|
@ -6089,6 +6160,12 @@ static const struct hda_fixup alc269_fixups[] = {
|
|||
.chained = true,
|
||||
.chain_id = ALC255_FIXUP_MIC_MUTE_LED
|
||||
},
|
||||
[ALC298_FIXUP_HUAWEI_MBX_STEREO] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = alc298_fixup_huawei_mbx_stereo,
|
||||
.chained = true,
|
||||
.chain_id = ALC255_FIXUP_MIC_MUTE_LED
|
||||
},
|
||||
[ALC269_FIXUP_ASUS_X101_FUNC] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = alc269_fixup_x101_headset_mic,
|
||||
|
@ -7280,6 +7357,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
|
|||
{.id = ALC225_FIXUP_HEADSET_JACK, .name = "alc-headset-jack"},
|
||||
{.id = ALC295_FIXUP_CHROME_BOOK, .name = "alc-chrome-book"},
|
||||
{.id = ALC299_FIXUP_PREDATOR_SPK, .name = "predator-spk"},
|
||||
{.id = ALC298_FIXUP_HUAWEI_MBX_STEREO, .name = "huawei-mbx-stereo"},
|
||||
{}
|
||||
};
|
||||
#define ALC225_STANDARD_PINS \
|
||||
|
@ -7590,10 +7668,6 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
|
|||
{0x12, 0x90a60120},
|
||||
{0x14, 0x90170110},
|
||||
{0x21, 0x0321101f}),
|
||||
SND_HDA_PIN_QUIRK(0x10ec0289, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE,
|
||||
{0x12, 0xb7a60130},
|
||||
{0x14, 0x90170110},
|
||||
{0x21, 0x04211020}),
|
||||
SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
|
||||
ALC290_STANDARD_PINS,
|
||||
{0x15, 0x04211040},
|
||||
|
@ -7703,6 +7777,19 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
|
|||
{}
|
||||
};
|
||||
|
||||
/* This is the fallback pin_fixup_tbl for alc269 family, to make the tbl match
|
||||
* more machines, don't need to match all valid pins, just need to match
|
||||
* all the pins defined in the tbl. Just because of this reason, it is possible
|
||||
* that a single machine matches multiple tbls, so there is one limitation:
|
||||
* at most one tbl is allowed to define for the same vendor and same codec
|
||||
*/
|
||||
static const struct snd_hda_pin_quirk alc269_fallback_pin_fixup_tbl[] = {
|
||||
SND_HDA_PIN_QUIRK(0x10ec0289, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE,
|
||||
{0x19, 0x40000000},
|
||||
{0x1b, 0x40000000}),
|
||||
{}
|
||||
};
|
||||
|
||||
static void alc269_fill_coef(struct hda_codec *codec)
|
||||
{
|
||||
struct alc_spec *spec = codec->spec;
|
||||
|
@ -7892,7 +7979,8 @@ static int patch_alc269(struct hda_codec *codec)
|
|||
|
||||
snd_hda_pick_fixup(codec, alc269_fixup_models,
|
||||
alc269_fixup_tbl, alc269_fixups);
|
||||
snd_hda_pick_pin_fixup(codec, alc269_pin_fixup_tbl, alc269_fixups);
|
||||
snd_hda_pick_pin_fixup(codec, alc269_pin_fixup_tbl, alc269_fixups, true);
|
||||
snd_hda_pick_pin_fixup(codec, alc269_fallback_pin_fixup_tbl, alc269_fixups, false);
|
||||
snd_hda_pick_fixup(codec, NULL, alc269_fixup_vendor_tbl,
|
||||
alc269_fixups);
|
||||
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
|
||||
|
@ -8026,7 +8114,8 @@ static int patch_alc861(struct hda_codec *codec)
|
|||
return err;
|
||||
|
||||
spec = codec->spec;
|
||||
spec->gen.beep_nid = 0x23;
|
||||
if (has_cdefine_beep(codec))
|
||||
spec->gen.beep_nid = 0x23;
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
spec->power_hook = alc_power_eapd;
|
||||
|
@ -8127,7 +8216,8 @@ static int patch_alc861vd(struct hda_codec *codec)
|
|||
return err;
|
||||
|
||||
spec = codec->spec;
|
||||
spec->gen.beep_nid = 0x23;
|
||||
if (has_cdefine_beep(codec))
|
||||
spec->gen.beep_nid = 0x23;
|
||||
|
||||
spec->shutup = alc_eapd_shutup;
|
||||
|
||||
|
@ -8267,6 +8357,45 @@ static void alc662_fixup_usi_headset_mic(struct hda_codec *codec,
|
|||
}
|
||||
}
|
||||
|
||||
static void alc662_aspire_ethos_mute_speakers(struct hda_codec *codec,
|
||||
struct hda_jack_callback *cb)
|
||||
{
|
||||
/* surround speakers at 0x1b already get muted automatically when
|
||||
* headphones are plugged in, but we have to mute/unmute the remaining
|
||||
* channels manually:
|
||||
* 0x15 - front left/front right
|
||||
* 0x18 - front center/ LFE
|
||||
*/
|
||||
if (snd_hda_jack_detect_state(codec, 0x1b) == HDA_JACK_PRESENT) {
|
||||
snd_hda_set_pin_ctl_cache(codec, 0x15, 0);
|
||||
snd_hda_set_pin_ctl_cache(codec, 0x18, 0);
|
||||
} else {
|
||||
snd_hda_set_pin_ctl_cache(codec, 0x15, PIN_OUT);
|
||||
snd_hda_set_pin_ctl_cache(codec, 0x18, PIN_OUT);
|
||||
}
|
||||
}
|
||||
|
||||
static void alc662_fixup_aspire_ethos_hp(struct hda_codec *codec,
|
||||
const struct hda_fixup *fix, int action)
|
||||
{
|
||||
/* Pin 0x1b: shared headphones jack and surround speakers */
|
||||
if (!is_jack_detectable(codec, 0x1b))
|
||||
return;
|
||||
|
||||
switch (action) {
|
||||
case HDA_FIXUP_ACT_PRE_PROBE:
|
||||
snd_hda_jack_detect_enable_callback(codec, 0x1b,
|
||||
alc662_aspire_ethos_mute_speakers);
|
||||
break;
|
||||
case HDA_FIXUP_ACT_INIT:
|
||||
/* Make sure to start in a correct state, i.e. if
|
||||
* headphones have been plugged in before powering up the system
|
||||
*/
|
||||
alc662_aspire_ethos_mute_speakers(codec, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static struct coef_fw alc668_coefs[] = {
|
||||
WRITE_COEF(0x01, 0xbebe), WRITE_COEF(0x02, 0xaaaa), WRITE_COEF(0x03, 0x0),
|
||||
WRITE_COEF(0x04, 0x0180), WRITE_COEF(0x06, 0x0), WRITE_COEF(0x07, 0x0f80),
|
||||
|
@ -8338,6 +8467,9 @@ enum {
|
|||
ALC662_FIXUP_USI_FUNC,
|
||||
ALC662_FIXUP_USI_HEADSET_MODE,
|
||||
ALC662_FIXUP_LENOVO_MULTI_CODECS,
|
||||
ALC669_FIXUP_ACER_ASPIRE_ETHOS,
|
||||
ALC669_FIXUP_ACER_ASPIRE_ETHOS_SUBWOOFER,
|
||||
ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET,
|
||||
};
|
||||
|
||||
static const struct hda_fixup alc662_fixups[] = {
|
||||
|
@ -8664,6 +8796,33 @@ static const struct hda_fixup alc662_fixups[] = {
|
|||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = alc233_alc662_fixup_lenovo_dual_codecs,
|
||||
},
|
||||
[ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = alc662_fixup_aspire_ethos_hp,
|
||||
},
|
||||
[ALC669_FIXUP_ACER_ASPIRE_ETHOS_SUBWOOFER] = {
|
||||
.type = HDA_FIXUP_VERBS,
|
||||
/* subwoofer needs an extra GPIO setting to become audible */
|
||||
.v.verbs = (const struct hda_verb[]) {
|
||||
{0x01, AC_VERB_SET_GPIO_MASK, 0x02},
|
||||
{0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
|
||||
{0x01, AC_VERB_SET_GPIO_DATA, 0x00},
|
||||
{ }
|
||||
},
|
||||
.chained = true,
|
||||
.chain_id = ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET
|
||||
},
|
||||
[ALC669_FIXUP_ACER_ASPIRE_ETHOS] = {
|
||||
.type = HDA_FIXUP_PINS,
|
||||
.v.pins = (const struct hda_pintbl[]) {
|
||||
{ 0x15, 0x92130110 }, /* front speakers */
|
||||
{ 0x18, 0x99130111 }, /* center/subwoofer */
|
||||
{ 0x1b, 0x11130012 }, /* surround plus jack for HP */
|
||||
{ }
|
||||
},
|
||||
.chained = true,
|
||||
.chain_id = ALC669_FIXUP_ACER_ASPIRE_ETHOS_SUBWOOFER
|
||||
},
|
||||
};
|
||||
|
||||
static const struct snd_pci_quirk alc662_fixup_tbl[] = {
|
||||
|
@ -8709,6 +8868,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = {
|
|||
SND_PCI_QUIRK(0x19da, 0xa130, "Zotac Z68", ALC662_FIXUP_ZOTAC_Z68),
|
||||
SND_PCI_QUIRK(0x1b0a, 0x01b8, "ACER Veriton", ALC662_FIXUP_ACER_VERITON),
|
||||
SND_PCI_QUIRK(0x1b35, 0x2206, "CZC P10T", ALC662_FIXUP_CZC_P10T),
|
||||
SND_PCI_QUIRK(0x1025, 0x0566, "Acer Aspire Ethos 8951G", ALC669_FIXUP_ACER_ASPIRE_ETHOS),
|
||||
|
||||
#if 0
|
||||
/* Below is a quirk table taken from the old code.
|
||||
|
@ -8802,6 +8962,7 @@ static const struct hda_model_fixup alc662_fixup_models[] = {
|
|||
{.id = ALC892_FIXUP_ASROCK_MOBO, .name = "asrock-mobo"},
|
||||
{.id = ALC662_FIXUP_USI_HEADSET_MODE, .name = "usi-headset"},
|
||||
{.id = ALC662_FIXUP_LENOVO_MULTI_CODECS, .name = "dual-codecs"},
|
||||
{.id = ALC669_FIXUP_ACER_ASPIRE_ETHOS, .name = "aspire-ethos"},
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -8877,7 +9038,7 @@ static int patch_alc662(struct hda_codec *codec)
|
|||
|
||||
snd_hda_pick_fixup(codec, alc662_fixup_models,
|
||||
alc662_fixup_tbl, alc662_fixups);
|
||||
snd_hda_pick_pin_fixup(codec, alc662_pin_fixup_tbl, alc662_fixups);
|
||||
snd_hda_pick_pin_fixup(codec, alc662_pin_fixup_tbl, alc662_fixups, true);
|
||||
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
|
||||
|
||||
alc_auto_parse_customize_define(codec);
|
||||
|
|
|
@ -975,15 +975,6 @@ static int stac_create_spdif_mux_ctls(struct hda_codec *codec)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
static const struct hda_verb stac9200_core_init[] = {
|
||||
/* set dac0mux for dac converter */
|
||||
{ 0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct hda_verb stac9200_eapd_init[] = {
|
||||
/* set dac0mux for dac converter */
|
||||
{0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
|
||||
|
|
|
@ -49,6 +49,14 @@ static const struct pci_device_id snd_lx6464es_ids[] = {
|
|||
PCI_VENDOR_ID_DIGIGRAM,
|
||||
PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_CAE_SERIAL_SUBSYSTEM),
|
||||
}, /* LX6464ES-CAE */
|
||||
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES,
|
||||
PCI_VENDOR_ID_DIGIGRAM,
|
||||
PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ESE_SERIAL_SUBSYSTEM),
|
||||
}, /* LX6464ESe */
|
||||
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES,
|
||||
PCI_VENDOR_ID_DIGIGRAM,
|
||||
PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ESE_CAE_SERIAL_SUBSYSTEM),
|
||||
}, /* LX6464ESe-CAE */
|
||||
{ 0, },
|
||||
};
|
||||
|
||||
|
|
|
@ -51,7 +51,6 @@ source "sound/soc/dwc/Kconfig"
|
|||
source "sound/soc/fsl/Kconfig"
|
||||
source "sound/soc/hisilicon/Kconfig"
|
||||
source "sound/soc/jz4740/Kconfig"
|
||||
source "sound/soc/nuc900/Kconfig"
|
||||
source "sound/soc/kirkwood/Kconfig"
|
||||
source "sound/soc/img/Kconfig"
|
||||
source "sound/soc/intel/Kconfig"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-utils.o
|
||||
snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-utils.o soc-dai.o soc-component.o
|
||||
snd-soc-core-objs += soc-pcm.o soc-io.o soc-devres.o soc-ops.o
|
||||
snd-soc-core-$(CONFIG_SND_SOC_COMPRESS) += soc-compress.o
|
||||
|
||||
|
@ -39,7 +39,6 @@ obj-$(CONFIG_SND_SOC) += intel/
|
|||
obj-$(CONFIG_SND_SOC) += mediatek/
|
||||
obj-$(CONFIG_SND_SOC) += meson/
|
||||
obj-$(CONFIG_SND_SOC) += mxs/
|
||||
obj-$(CONFIG_SND_SOC) += nuc900/
|
||||
obj-$(CONFIG_SND_SOC) += kirkwood/
|
||||
obj-$(CONFIG_SND_SOC) += pxa/
|
||||
obj-$(CONFIG_SND_SOC) += qcom/
|
||||
|
|
|
@ -10,7 +10,7 @@ config SND_SOC_AMD_CZ_DA7219MX98357_MACH
|
|||
select SND_SOC_MAX98357A
|
||||
select SND_SOC_ADAU7002
|
||||
select REGULATOR
|
||||
depends on SND_SOC_AMD_ACP && I2C
|
||||
depends on SND_SOC_AMD_ACP && I2C && GPIOLIB
|
||||
help
|
||||
This option enables machine driver for DA7219 and MAX9835.
|
||||
|
||||
|
|
|
@ -1251,8 +1251,7 @@ static int acp_audio_probe(struct platform_device *pdev)
|
|||
if (!audio_drv_data)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
audio_drv_data->acp_mmio = devm_ioremap_resource(&pdev->dev, res);
|
||||
audio_drv_data->acp_mmio = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(audio_drv_data->acp_mmio))
|
||||
return PTR_ERR(audio_drv_data->acp_mmio);
|
||||
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче