1
0
Fork 0

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
  ...
alistair/sunxi64-5.4-dsi
Linus Torvalds 2019-09-17 17:43:33 -07:00
commit 6ab8ad3160
354 changed files with 17192 additions and 8318 deletions

View File

@ -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>;
};

View File

@ -70,7 +70,9 @@ allOf:
properties: properties:
compatible: compatible:
contains: contains:
const: allwinner,sun8i-h3-spdif enum:
- allwinner,sun8i-h3-spdif
- allwinner,sun50i-h6-spdif
then: then:
properties: properties:

View File

@ -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 = <&reg_eldo1>;
};
...

View File

@ -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";
};
...

View File

@ -4,13 +4,18 @@ Required properties:
- compatible: 'amlogic,axg-toddr' or - compatible: 'amlogic,axg-toddr' or
'amlogic,axg-toddr' or 'amlogic,axg-toddr' or
'amlogic,g12a-frddr' 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 - reg: physical base address of the controller and length of memory
mapped region. mapped region.
- interrupts: interrupt specifier for the fifo. - interrupts: interrupt specifier for the fifo.
- clocks: phandle to the fifo peripheral clock provided by the audio - clocks: phandle to the fifo peripheral clock provided by the audio
clock controller. 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. - #sound-dai-cells: must be 0.
Example of FRDDR A on the A113 SoC: Example of FRDDR A on the A113 SoC:

View File

@ -2,7 +2,8 @@
Required properties: Required properties:
- compatible: 'amlogic,axg-pdm' or - 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 - reg: physical base address of the controller and length of memory
mapped region. mapped region.
- clocks: list of clock phandle, one for each entry clock-names. - clocks: list of clock phandle, one for each entry clock-names.
@ -12,6 +13,9 @@ Required properties:
* "sysclk" : dsp system clock * "sysclk" : dsp system clock
- #sound-dai-cells: must be 0. - #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: Example of PDM on the A113 SoC:
pdm: audio-controller@ff632000 { pdm: audio-controller@ff632000 {

View File

@ -2,7 +2,8 @@
Required properties: Required properties:
- compatible: 'amlogic,axg-spdifin' or - compatible: 'amlogic,axg-spdifin' or
'amlogic,g12a-spdifin' 'amlogic,g12a-spdifin' or
'amlogic,sm1-spdifin'
- interrupts: interrupt specifier for the spdif input. - interrupts: interrupt specifier for the spdif input.
- clocks: list of clock phandle, one for each entry clock-names. - clocks: list of clock phandle, one for each entry clock-names.
- clock-names: should contain the following: - clock-names: should contain the following:
@ -10,6 +11,9 @@ Required properties:
* "refclk" : spdif input reference clock * "refclk" : spdif input reference clock
- #sound-dai-cells: must be 0. - #sound-dai-cells: must be 0.
Optional property:
- resets: phandle to the dedicated reset line of the spdif input.
Example on the A113 SoC: Example on the A113 SoC:
spdifin: audio-controller@400 { spdifin: audio-controller@400 {

View File

@ -2,13 +2,17 @@
Required properties: Required properties:
- compatible: 'amlogic,axg-spdifout' or - 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. - clocks: list of clock phandle, one for each entry clock-names.
- clock-names: should contain the following: - clock-names: should contain the following:
* "pclk" : peripheral clock. * "pclk" : peripheral clock.
* "mclk" : master clock * "mclk" : master clock
- #sound-dai-cells: must be 0. - #sound-dai-cells: must be 0.
Optional property:
- resets: phandle to the dedicated reset line of the spdif output.
Example on the A113 SoC: Example on the A113 SoC:
spdifout: audio-controller@480 { spdifout: audio-controller@480 {

View File

@ -4,7 +4,9 @@ Required properties:
- compatible: 'amlogic,axg-tdmin' or - compatible: 'amlogic,axg-tdmin' or
'amlogic,axg-tdmout' or 'amlogic,axg-tdmout' or
'amlogic,g12a-tdmin' 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 - reg: physical base address of the controller and length of memory
mapped region. mapped region.
- clocks: list of clock phandle, one for each entry clock-names. - clocks: list of clock phandle, one for each entry clock-names.

View File

@ -1,10 +1,12 @@
* Amlogic HDMI Tx control glue * Amlogic HDMI Tx control glue
Required properties: 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 - reg: physical base address of the controller and length of memory
mapped region. mapped region.
- #sound-dai-cells: should be 1. - #sound-dai-cells: should be 1.
- resets: phandle to the dedicated reset line of the hdmitx glue.
Example on the S905X2 SoC: Example on the S905X2 SoC:
@ -12,6 +14,7 @@ tohdmitx: audio-controller@744 {
compatible = "amlogic,g12a-tohdmitx"; compatible = "amlogic,g12a-tohdmitx";
reg = <0x0 0x744 0x0 0x4>; reg = <0x0 0x744 0x0 0x4>;
#sound-dai-cells = <1>; #sound-dai-cells = <1>;
resets = <&clkc_audio AUD_RESET_TOHDMITX>;
}; };
Example of an 'amlogic,axg-sound-card': Example of an 'amlogic,axg-sound-card':

View File

@ -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";
};

View File

@ -7,8 +7,11 @@ other DSPs. It has up to six transmitters and four receivers.
Required properties: Required properties:
- compatible : Compatible list, must contain "fsl,imx35-esai" or - compatible : Compatible list, should contain one of the following
"fsl,vf610-esai" compatibles:
"fsl,imx35-esai",
"fsl,vf610-esai",
"fsl,imx6ull-esai",
- reg : Offset and length of the register set for the device. - reg : Offset and length of the register set for the device.

View File

@ -8,7 +8,9 @@ codec/DSP interfaces.
Required properties: Required properties:
- compatible : Compatible list, contains "fsl,vf610-sai", - 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. - reg : Offset and length of the register set for the device.

View File

@ -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 = <&reg_eldo1>;
};

View File

@ -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";
};
};

View File

@ -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>;
};

View File

@ -1001,6 +1001,8 @@ position_fix
2 = POSBUF: use position buffer, 2 = POSBUF: use position buffer,
3 = VIACOMBO: VIA-specific workaround for capture, 3 = VIACOMBO: VIA-specific workaround for capture,
4 = COMBO: use LPIB for playback, auto for capture stream 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 probe_mask
Bitmask to probe codecs (default = -1, meaning all slots); Bitmask to probe codecs (default = -1, meaning all slots);
When the bit 8 (0x100) is set, the lower 8 bits are used When the bit 8 (0x100) is set, the lower 8 bits are used

View File

@ -260,6 +260,9 @@ alc295-hp-x360
HP Spectre X360 fixups HP Spectre X360 fixups
alc-sense-combo alc-sense-combo
Headset button support for Chrome platform 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 ALC66x/67x/892
============== ==============

View File

@ -66,6 +66,11 @@ by comparing both LPIB and position-buffer values.
``position_fix=4`` is another combination available for all controllers, ``position_fix=4`` is another combination available for all controllers,
and uses LPIB for the playback and the position-buffer for the capture and uses LPIB for the playback and the position-buffer for the capture
streams. 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 0 is the default value for all other
controllers, the automatic check and fallback to LPIB as described in controllers, the automatic check and fallback to LPIB as described in
the above. If you get a problem of repeated sounds, this option might the above. If you get a problem of repeated sounds, this option might

View File

@ -6270,12 +6270,14 @@ S: Maintained
F: drivers/hwmon/f75375s.c F: drivers/hwmon/f75375s.c
F: include/linux/f75375s.h 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: Clemens Ladisch <clemens@ladisch.de>
M: Takashi Sakamoto <o-takashi@sakamocchi.jp>
L: alsa-devel@alsa-project.org (moderated for non-subscribers) L: alsa-devel@alsa-project.org (moderated for non-subscribers)
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
S: Maintained S: Maintained
F: sound/firewire/ F: sound/firewire/
F: include/uapi/sound/firewire.h
FIREWIRE MEDIA DRIVERS (firedtv) FIREWIRE MEDIA DRIVERS (firedtv)
M: Stefan Richter <stefanr@s5r6.in-berlin.de> M: Stefan Richter <stefanr@s5r6.in-berlin.de>

View File

@ -246,8 +246,8 @@ static struct platform_device latch2_gpio_device = {
#define LATCH2_PIN_SCARD_CMDVCC 11 #define LATCH2_PIN_SCARD_CMDVCC 11
#define LATCH2_PIN_MODEM_NRESET 12 #define LATCH2_PIN_MODEM_NRESET 12
#define LATCH2_PIN_MODEM_CODEC 13 #define LATCH2_PIN_MODEM_CODEC 13
#define LATCH2_PIN_AUDIO_MUTE 14 #define LATCH2_PIN_HANDSFREE_MUTE 14
#define LATCH2_PIN_HOOKFLASH 15 #define LATCH2_PIN_HANDSET_MUTE 15
static struct regulator_consumer_supply modem_nreset_consumers[] = { static struct regulator_consumer_supply modem_nreset_consumers[] = {
REGULATOR_SUPPLY("RESET#", "serial8250.1"), REGULATOR_SUPPLY("RESET#", "serial8250.1"),
@ -476,6 +476,10 @@ static struct gpiod_lookup_table ams_delta_audio_gpio_table = {
"hook_switch", 0), "hook_switch", 0),
GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_MODEM_CODEC, GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_MODEM_CODEC,
"modem_codec", 0), "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[] = { static struct gpiod_hog ams_delta_gpio_hogs[] = {
GPIO_HOG(LATCH2_LABEL, LATCH2_PIN_KEYBRD_DATAOUT, "keybrd_dataout", GPIO_HOG(LATCH2_LABEL, LATCH2_PIN_KEYBRD_DATAOUT, "keybrd_dataout",
GPIO_ACTIVE_HIGH, GPIOD_OUT_LOW), GPIO_ACTIVE_HIGH, GPIOD_OUT_LOW),
GPIO_HOG(LATCH2_LABEL, LATCH2_PIN_AUDIO_MUTE, "audio_mute",
GPIO_ACTIVE_HIGH, GPIOD_OUT_LOW),
{}, {},
}; };

View File

@ -1951,6 +1951,8 @@
#define PCI_VENDOR_ID_DIGIGRAM 0x1369 #define PCI_VENDOR_ID_DIGIGRAM 0x1369
#define PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_SERIAL_SUBSYSTEM 0xc001 #define PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_SERIAL_SUBSYSTEM 0xc001
#define PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_CAE_SERIAL_SUBSYSTEM 0xc002 #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_VENDOR_ID_KAWASAKI 0x136b
#define PCI_DEVICE_ID_MCHIP_KL5A72002 0xff01 #define PCI_DEVICE_ID_MCHIP_KL5A72002 0xff01

View File

@ -59,6 +59,7 @@ struct hda_bus {
unsigned int in_reset:1; /* during reset operation */ unsigned int in_reset:1; /* during reset operation */
unsigned int no_response_fallback:1; /* don't fallback at RIRB error */ unsigned int no_response_fallback:1; /* don't fallback at RIRB error */
unsigned int bus_probing :1; /* during probing process */ 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 */ int primary_dig_out_type; /* primary digital out PCM type */
unsigned int mixer_assigned; /* codec addr for mixer name */ unsigned int mixer_assigned; /* codec addr for mixer name */

View File

@ -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); 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, int snd_hdac_read(struct hdac_device *codec, hda_nid_t nid,
unsigned int verb, unsigned int parm, unsigned int *res); unsigned int verb, unsigned int parm, unsigned int *res);
int _snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm, 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); 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_UNSOL_QUEUE_SIZE 64
#define HDA_MAX_CODECS 8 /* limit by controller side */ #define HDA_MAX_CODECS 8 /* limit by controller side */
@ -304,7 +282,6 @@ struct hdac_rb {
struct hdac_bus { struct hdac_bus {
struct device *dev; struct device *dev;
const struct hdac_bus_ops *ops; const struct hdac_bus_ops *ops;
const struct hdac_io_ops *io_ops;
const struct hdac_ext_bus_ops *ext_ops; const struct hdac_ext_bus_ops *ext_ops;
/* h/w resources */ /* h/w resources */
@ -344,6 +321,7 @@ struct hdac_bus {
/* CORB/RIRB and position buffers */ /* CORB/RIRB and position buffers */
struct snd_dma_buffer rb; struct snd_dma_buffer rb;
struct snd_dma_buffer posbuf; struct snd_dma_buffer posbuf;
int dma_type; /* SNDRV_DMA_TYPE_XXX for CORB/RIRB */
/* hdac_stream linked list */ /* hdac_stream linked list */
struct list_head stream_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, int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
const struct hdac_bus_ops *ops, const struct hdac_bus_ops *ops);
const struct hdac_io_ops *io_ops);
void snd_hdac_bus_exit(struct hdac_bus *bus); void snd_hdac_bus_exit(struct hdac_bus *bus);
int snd_hdac_bus_exec_verb(struct hdac_bus *bus, unsigned int addr, int snd_hdac_bus_exec_verb(struct hdac_bus *bus, unsigned int addr,
unsigned int cmd, unsigned int *res); 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); unsigned int cmd, unsigned int *res);
void snd_hdac_bus_queue_event(struct hdac_bus *bus, u32 res, u32 res_ex); 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) static inline void snd_hdac_codec_link_up(struct hdac_device *codec)
{ {
set_bit(codec->addr, &codec->bus->codec_powered); 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); int snd_hdac_bus_alloc_stream_pages(struct hdac_bus *bus);
void snd_hdac_bus_free_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 * macros for easy use
*/ */
#define _snd_hdac_chip_writeb(chip, reg, value) \ #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) \ #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) \ #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) \ #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) \ #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) \ #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 */ /* read/write a register, pass without AZX_REG_ prefix */
#define snd_hdac_chip_writel(chip, reg, value) \ #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 * 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 */ /* read/write a register, pass without AZX_REG_ prefix */
#define snd_hdac_stream_writel(dev, reg, value) \ #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) \ #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) \ #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) \ #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) \ #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) \ #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 */ /* update a register, pass without AZX_REG_ prefix */
#define snd_hdac_stream_updatel(dev, reg, mask, val) \ #define snd_hdac_stream_updatel(dev, reg, mask, val) \

View File

@ -6,7 +6,6 @@
int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev, int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev,
const struct hdac_bus_ops *ops, const struct hdac_bus_ops *ops,
const struct hdac_io_ops *io_ops,
const struct hdac_ext_bus_ops *ext_ops); const struct hdac_ext_bus_ops *ext_ops);
void snd_hdac_ext_bus_exit(struct hdac_bus *bus); void snd_hdac_ext_bus_exit(struct hdac_bus *bus);

View File

@ -47,6 +47,9 @@ struct hdmi_codec_params {
int channels; int channels;
}; };
typedef void (*hdmi_codec_plugged_cb)(struct device *dev,
bool plugged);
struct hdmi_codec_pdata; struct hdmi_codec_pdata;
struct hdmi_codec_ops { struct hdmi_codec_ops {
/* /*
@ -88,6 +91,14 @@ struct hdmi_codec_ops {
*/ */
int (*get_dai_id)(struct snd_soc_component *comment, int (*get_dai_id)(struct snd_soc_component *comment,
struct device_node *endpoint); 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 */ /* HDMI codec initalization data */
@ -99,6 +110,12 @@ struct hdmi_codec_pdata {
void *data; 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" #define HDMI_CODEC_DRV_NAME "hdmi-audio-codec"
#endif /* __HDMI_CODEC_H__ */ #endif /* __HDMI_CODEC_H__ */

View File

@ -1,18 +1,17 @@
/* SPDX-License-Identifier: GPL-2.0-only */ /* 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 * Copyright (c) 2015-2019 Intel Corporation
* Author: Sanjiv Kumar <sanjiv.kumar@intel.com>
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/ */
#ifndef __SKL_NHLT_H__
#define __SKL_NHLT_H__ #ifndef __INTEL_NHLT_H__
#define __INTEL_NHLT_H__
#include <linux/acpi.h> #include <linux/acpi.h>
#if IS_ENABLED(CONFIG_ACPI) && IS_ENABLED(CONFIG_SND_INTEL_NHLT)
struct wav_fmt { struct wav_fmt {
u16 fmt_tag; u16 fmt_tag;
u16 channels; u16 channels;
@ -97,16 +96,22 @@ struct nhlt_resource_desc {
#define MIC_ARRAY_2CH 2 #define MIC_ARRAY_2CH 2
#define MIC_ARRAY_4CH 4 #define MIC_ARRAY_4CH 4
struct nhlt_tdm_config { struct nhlt_device_specific_config {
u8 virtual_slot; u8 virtual_slot;
u8 config_type; u8 config_type;
} __packed; } __packed;
struct nhlt_dmic_array_config { struct nhlt_dmic_array_config {
struct nhlt_tdm_config tdm_config; struct nhlt_device_specific_config device_config;
u8 array_type; u8 array_type;
} __packed; } __packed;
struct nhlt_vendor_dmic_array_config {
struct nhlt_dmic_array_config dmic_config;
u8 nb_mics;
/* TODO add vendor mic config */
} __packed;
enum { enum {
NHLT_MIC_ARRAY_2CH_SMALL = 0xa, NHLT_MIC_ARRAY_2CH_SMALL = 0xa,
NHLT_MIC_ARRAY_2CH_BIG = 0xb, NHLT_MIC_ARRAY_2CH_BIG = 0xb,
@ -116,4 +121,30 @@ enum {
NHLT_MIC_ARRAY_VENDOR_DEFINED = 0xf, 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 #endif

View File

@ -117,6 +117,8 @@ struct snd_pcm_ops {
#define SNDRV_PCM_RATE_96000 (1<<10) /* 96000Hz */ #define SNDRV_PCM_RATE_96000 (1<<10) /* 96000Hz */
#define SNDRV_PCM_RATE_176400 (1<<11) /* 176400Hz */ #define SNDRV_PCM_RATE_176400 (1<<11) /* 176400Hz */
#define SNDRV_PCM_RATE_192000 (1<<12) /* 192000Hz */ #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_CONTINUOUS (1<<30) /* continuous range */
#define SNDRV_PCM_RATE_KNOT (1<<31) /* supports more non-continuos rates */ #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) SNDRV_PCM_RATE_88200|SNDRV_PCM_RATE_96000)
#define SNDRV_PCM_RATE_8000_192000 (SNDRV_PCM_RATE_8000_96000|SNDRV_PCM_RATE_176400|\ #define SNDRV_PCM_RATE_8000_192000 (SNDRV_PCM_RATE_8000_96000|SNDRV_PCM_RATE_176400|\
SNDRV_PCM_RATE_192000) 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(fmt) (1ULL << (__force int)SNDRV_PCM_FORMAT_##fmt)
#define SNDRV_PCM_FMTBIT_S8 _SNDRV_PCM_FMTBIT(S8) #define SNDRV_PCM_FMTBIT_S8 _SNDRV_PCM_FMTBIT(S8)
#define SNDRV_PCM_FMTBIT_U8 _SNDRV_PCM_FMTBIT(U8) #define SNDRV_PCM_FMTBIT_U8 _SNDRV_PCM_FMTBIT(U8)

View File

@ -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_glk_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_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_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 * generic table used for HDA codec-based platforms, possibly with

View File

@ -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 */

View File

@ -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_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 { struct snd_soc_dai_ops {
/* /*
* DAI clocking configuration, all optional. * DAI clocking configuration, all optional.
@ -268,8 +293,6 @@ struct snd_soc_dai_driver {
/* Optional Callback used at pcm creation*/ /* Optional Callback used at pcm creation*/
int (*pcm_new)(struct snd_soc_pcm_runtime *rtd, int (*pcm_new)(struct snd_soc_pcm_runtime *rtd,
struct snd_soc_dai *dai); struct snd_soc_dai *dai);
/* DAI is also used for the control bus */
bool bus_control;
/* ops */ /* ops */
const struct snd_soc_dai_ops *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_rates:1;
unsigned int symmetric_channels:1; unsigned int symmetric_channels:1;
unsigned int symmetric_samplebits: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 */ /* probe ordering - for components with runtime dependencies */
int probe_order; int probe_order;

View File

@ -353,6 +353,8 @@ struct device;
#define SND_SOC_DAPM_WILL_PMD 0x80 /* called at start of sequence */ #define SND_SOC_DAPM_WILL_PMD 0x80 /* called at start of sequence */
#define SND_SOC_DAPM_PRE_POST_PMD \ #define SND_SOC_DAPM_PRE_POST_PMD \
(SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_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 */ /* convenience event type detection */
#define SND_SOC_DAPM_EVENT_ON(e) \ #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_widget *snd_soc_dapm_new_control(
struct snd_soc_dapm_context *dapm, struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_widget *widget); 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, int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
struct snd_soc_dai *dai); struct snd_soc_dai *dai);
int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card); 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 */ /* dapm path setup */
int snd_soc_dapm_new_widgets(struct snd_soc_card *card); 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_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, int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_route *route, int num); const struct snd_soc_dapm_route *route, int num);
int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm, 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 */ unsigned int idle_bias_off:1; /* Use BIAS_OFF instead of STANDBY */
/* Go to BIAS_OFF in suspend if the DAPM context is idle */ /* Go to BIAS_OFF in suspend if the DAPM context is idle */
unsigned int suspend_bias_off:1; 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 device *dev; /* from parent - for debug */
struct snd_soc_component *component; /* parent component */ struct snd_soc_component *component; /* parent component */
@ -670,10 +676,6 @@ struct snd_soc_dapm_context {
enum snd_soc_bias_level target_bias_level; enum snd_soc_bias_level target_bias_level;
struct list_head list; 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_sink_cache;
struct snd_soc_dapm_wcache path_source_cache; struct snd_soc_dapm_wcache path_source_cache;

View File

@ -142,9 +142,16 @@ void snd_soc_dpcm_be_set_state(struct snd_soc_pcm_runtime *be, int stream,
/* internal use only */ /* internal use only */
int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute); 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 *); 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 dpcm_path_get(struct snd_soc_pcm_runtime *fe,
int stream, struct snd_soc_dapm_widget_list **list_); int stream, struct snd_soc_dapm_widget_list **list_);
int dpcm_process_paths(struct snd_soc_pcm_runtime *fe, int dpcm_process_paths(struct snd_soc_pcm_runtime *fe,

View File

@ -362,21 +362,6 @@
#define SOC_ENUM_SINGLE_VIRT_DECL(name, xtexts) \ #define SOC_ENUM_SINGLE_VIRT_DECL(name, xtexts) \
const struct soc_enum name = SOC_ENUM_SINGLE_VIRT(ARRAY_SIZE(xtexts), 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 * 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, int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
const struct snd_pcm_hardware *hw); 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 */ /* Jack reporting */
int snd_soc_card_jack_new(struct snd_soc_card *card, const char *id, int type, 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, 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 *); 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_rtdcom_list {
struct snd_soc_component *component; struct snd_soc_component *component;
struct list_head list; /* rtd::component_list */ 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_CPU(_dai) { .dai_name = _dai, }
#define COMP_CODEC(_name, _dai) { .name = _name, .dai_name = _dai, } #define COMP_CODEC(_name, _dai) { .name = _name, .dai_name = _dai, }
#define COMP_PLATFORM(_name) { .name = _name } #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", } #define COMP_DUMMY() { .name = "snd-soc-dummy", .dai_name = "snd-soc-dummy-dai", }
extern struct snd_soc_dai_link_component null_dailink_component[0]; 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 { struct snd_soc_aux_dev {
const char *name; /* Codec name */
/* /*
* specify multi-codec either by device name, or by * specify multi-codec either by device name, or by
* DT/OF node, but not both. * DT/OF node, but not both.
*/ */
const char *codec_name; struct snd_soc_dai_link_component dlc;
struct device_node *codec_of_node;
/* codec/machine specific init - e.g. add machine controls */ /* codec/machine specific init - e.g. add machine controls */
int (*init)(struct snd_soc_component *component); int (*init)(struct snd_soc_component *component);
@ -1135,6 +988,10 @@ struct snd_soc_card {
struct mutex mutex; struct mutex mutex;
struct mutex dapm_mutex; struct mutex dapm_mutex;
/* Mutex for PCM operations */
struct mutex pcm_mutex;
enum snd_soc_pcm_subclass pcm_subclass;
spinlock_t dpcm_lock; spinlock_t dpcm_lock;
bool instantiated; bool instantiated;
@ -1203,8 +1060,6 @@ struct snd_soc_card {
int num_of_dapm_routes; int num_of_dapm_routes;
bool fully_routed; bool fully_routed;
struct work_struct deferred_resume_work;
/* lists of probed devices belonging to this card */ /* lists of probed devices belonging to this card */
struct list_head component_dev_list; struct list_head component_dev_list;
struct list_head list; struct list_head list;
@ -1224,7 +1079,9 @@ struct snd_soc_card {
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
struct dentry *debugfs_card_root; struct dentry *debugfs_card_root;
struct dentry *debugfs_pop_time; #endif
#ifdef CONFIG_PM_SLEEP
struct work_struct deferred_resume_work;
#endif #endif
u32 pop_time; u32 pop_time;
@ -1234,6 +1091,10 @@ struct snd_soc_card {
for ((i) = 0; \ for ((i) = 0; \
((i) < (card)->num_links) && ((link) = &(card)->dai_link[i]); \ ((i) < (card)->num_links) && ((link) = &(card)->dai_link[i]); \
(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) \ #define for_each_card_links(card, link) \
list_for_each_entry(link, &(card)->dai_link_list, list) 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) \ #define for_each_card_rtds_safe(card, rtd, _rtd) \
list_for_each_entry_safe(rtd, _rtd, &(card)->rtd_list, list) 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) \ #define for_each_card_components(card, component) \
list_for_each_entry(component, &(card)->component_dev_list, card_list) list_for_each_entry(component, &(card)->component_dev_list, card_list)
@ -1253,8 +1120,6 @@ struct snd_soc_pcm_runtime {
struct device *dev; struct device *dev;
struct snd_soc_card *card; struct snd_soc_card *card;
struct snd_soc_dai_link *dai_link; struct snd_soc_dai_link *dai_link;
struct mutex pcm_mutex;
enum snd_soc_pcm_subclass pcm_subclass;
struct snd_pcm_ops ops; struct snd_pcm_ops ops;
unsigned int params_select; /* currently selected param for dai link */ unsigned int params_select; /* currently selected param for dai link */
@ -1342,134 +1207,6 @@ struct soc_enum {
struct snd_soc_dobj dobj; 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 */ /* device driver data */
static inline void snd_soc_card_set_drvdata(struct snd_soc_card *card, 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; 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) static inline bool snd_soc_volsw_is_stereo(struct soc_mixer_control *mc)
{ {
if (mc->reg == mc->rreg && mc->shift == mc->rshift) 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]; 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 * snd_soc_kcontrol_component() - Returns the component that registered the
* control * 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); mutex_unlock(&dapm->card->dapm_mutex);
} }
int snd_soc_component_enable_pin(struct snd_soc_component *component, #include <sound/soc-component.h>
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);
#endif #endif

View File

@ -76,6 +76,9 @@ struct sof_ipc_dai_ssp_params {
uint16_t tdm_per_slot_padding_flag; uint16_t tdm_per_slot_padding_flag;
uint32_t clks_control; uint32_t clks_control;
uint32_t quirks; uint32_t quirks;
uint32_t bclk_delay; /* guaranteed time (ms) for which BCLK
* will be driven, before sending data
*/
} __packed; } __packed;
/* HDA Configuration Request - SOF_IPC_DAI_HDA_CONFIG */ /* 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]; struct sof_ipc_dai_dmic_pdm_ctrl pdm[0];
} __packed; } __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 #endif

View File

@ -49,7 +49,9 @@ enum sof_ipc_dai_type {
SOF_DAI_INTEL_SSP, /**< Intel SSP */ SOF_DAI_INTEL_SSP, /**< Intel SSP */
SOF_DAI_INTEL_DMIC, /**< Intel DMIC */ SOF_DAI_INTEL_DMIC, /**< Intel DMIC */
SOF_DAI_INTEL_HDA, /**< Intel HD/A */ 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 */ /* general purpose DAI configuration */
@ -70,6 +72,7 @@ struct sof_ipc_dai_config {
struct sof_ipc_dai_ssp_params ssp; struct sof_ipc_dai_ssp_params ssp;
struct sof_ipc_dai_dmic_params dmic; struct sof_ipc_dai_dmic_params dmic;
struct sof_ipc_dai_hda_params hda; struct sof_ipc_dai_hda_params hda;
struct sof_ipc_dai_alh_params alh;
}; };
} __packed; } __packed;

View File

@ -26,7 +26,7 @@
/* SOF ABI version major, minor and patch numbers */ /* SOF ABI version major, minor and patch numbers */
#define SOF_ABI_MAJOR 3 #define SOF_ABI_MAJOR 3
#define SOF_ABI_MINOR 8 #define SOF_ABI_MINOR 10
#define SOF_ABI_PATCH 0 #define SOF_ABI_PATCH 0
/* SOF ABI version number. Format within 32bit word is MMmmmppp */ /* SOF ABI version number. Format within 32bit word is MMmmmppp */

View File

@ -75,6 +75,7 @@
#define SOF_TKN_INTEL_SSP_FRAME_PULSE_WIDTH 503 #define SOF_TKN_INTEL_SSP_FRAME_PULSE_WIDTH 503
#define SOF_TKN_INTEL_SSP_QUIRKS 504 #define SOF_TKN_INTEL_SSP_QUIRKS 504
#define SOF_TKN_INTEL_SSP_TDM_PADDING_PER_SLOT 505 #define SOF_TKN_INTEL_SSP_TDM_PADDING_PER_SLOT 505
#define SOF_TKN_INTEL_SSP_BCLK_DELAY 506
/* DMIC */ /* DMIC */
#define SOF_TKN_INTEL_DMIC_DRIVER_VERSION 600 #define SOF_TKN_INTEL_DMIC_DRIVER_VERSION 600
@ -105,4 +106,12 @@
/* for backward compatibility */ /* for backward compatibility */
#define SOF_TKN_EFFECT_TYPE SOF_TKN_PROCESS_TYPE #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 #endif

View File

@ -71,8 +71,10 @@ static int onyx_read_register(struct onyx *onyx, u8 reg, u8 *value)
return 0; return 0;
} }
v = i2c_smbus_read_byte_data(onyx->i2c, reg); v = i2c_smbus_read_byte_data(onyx->i2c, reg);
if (v < 0) if (v < 0) {
*value = 0;
return -1; return -1;
}
*value = (u8)v; *value = (u8)v;
onyx->cache[ONYX_REG_CONTROL-FIRSTREGISTER] = *value; onyx->cache[ONYX_REG_CONTROL-FIRSTREGISTER] = *value;
return 0; return 0;

View File

@ -2170,7 +2170,7 @@ static int snd_pcm_hw_rule_sample_bits(struct snd_pcm_hw_params *params,
static const unsigned int rates[] = { static const unsigned int rates[] = {
5512, 8000, 11025, 16000, 22050, 32000, 44100, 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 = { const struct snd_pcm_hw_constraint_list snd_pcm_known_rates = {

View File

@ -146,19 +146,24 @@ void amdtp_am824_set_midi_position(struct amdtp_stream *s,
} }
EXPORT_SYMBOL_GPL(amdtp_am824_set_midi_position); EXPORT_SYMBOL_GPL(amdtp_am824_set_midi_position);
static void write_pcm_s32(struct amdtp_stream *s, static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
struct snd_pcm_substream *pcm, __be32 *buffer, unsigned int frames,
__be32 *buffer, unsigned int frames) unsigned int pcm_frames)
{ {
struct amdtp_am824 *p = s->protocol; struct amdtp_am824 *p = s->protocol;
unsigned int channels = p->pcm_channels;
struct snd_pcm_runtime *runtime = pcm->runtime; 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; 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 + src = (void *)runtime->dma_area +
frames_to_bytes(runtime, s->pcm_buffer_pointer); frames_to_bytes(runtime, pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
for (i = 0; i < frames; ++i) { for (i = 0; i < frames; ++i) {
for (c = 0; c < channels; ++c) { 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, static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
struct snd_pcm_substream *pcm, __be32 *buffer, unsigned int frames,
__be32 *buffer, unsigned int frames) unsigned int pcm_frames)
{ {
struct amdtp_am824 *p = s->protocol; struct amdtp_am824 *p = s->protocol;
unsigned int channels = p->pcm_channels;
struct snd_pcm_runtime *runtime = pcm->runtime; struct snd_pcm_runtime *runtime = pcm->runtime;
unsigned int channels, remaining_frames, i, c; unsigned int pcm_buffer_pointer;
int remaining_frames;
u32 *dst; 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 + dst = (void *)runtime->dma_area +
frames_to_bytes(runtime, s->pcm_buffer_pointer); frames_to_bytes(runtime, pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
for (i = 0; i < frames; ++i) { for (i = 0; i < frames; ++i) {
for (c = 0; c < channels; ++c) { 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, 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; struct amdtp_am824 *p = s->protocol;
unsigned int f, port; 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++) { for (f = 0; f < frames; f++) {
b = (u8 *)&buffer[p->midi_position]; 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 && if (f < MAX_MIDI_RX_BLOCKS &&
midi_ratelimit_per_packet(s, port) && midi_ratelimit_per_packet(s, port) &&
p->midi[port] != NULL && 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, static void read_midi_messages(struct amdtp_stream *s, __be32 *buffer,
__be32 *buffer, unsigned int frames) unsigned int frames, unsigned int data_block_counter)
{ {
struct amdtp_am824 *p = s->protocol; struct amdtp_am824 *p = s->protocol;
unsigned int f, port;
int len; int len;
u8 *b; u8 *b;
int f;
for (f = 0; f < frames; 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]; b = (u8 *)&buffer[p->midi_position];
len = b[0] - 0x80; 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, static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
unsigned int data_blocks, unsigned int *syt) const struct pkt_desc *descs,
unsigned int packets,
struct snd_pcm_substream *pcm)
{ {
struct amdtp_am824 *p = s->protocol; struct amdtp_am824 *p = s->protocol;
struct snd_pcm_substream *pcm = READ_ONCE(s->pcm); unsigned int pcm_frames = 0;
unsigned int pcm_frames; int i;
if (pcm) { for (i = 0; i < packets; ++i) {
write_pcm_s32(s, pcm, buffer, data_blocks); const struct pkt_desc *desc = descs + i;
pcm_frames = data_blocks * p->frame_multiplier; __be32 *buf = desc->ctx_payload;
} else { unsigned int data_blocks = desc->data_blocks;
write_pcm_silence(s, buffer, data_blocks);
pcm_frames = 0; 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; return pcm_frames;
} }
static unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffer, static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
unsigned int data_blocks, unsigned int *syt) const struct pkt_desc *descs,
unsigned int packets,
struct snd_pcm_substream *pcm)
{ {
struct amdtp_am824 *p = s->protocol; struct amdtp_am824 *p = s->protocol;
struct snd_pcm_substream *pcm = READ_ONCE(s->pcm); unsigned int pcm_frames = 0;
unsigned int pcm_frames; int i;
if (pcm) { for (i = 0; i < packets; ++i) {
read_pcm_s32(s, pcm, buffer, data_blocks); const struct pkt_desc *desc = descs + i;
pcm_frames = data_blocks * p->frame_multiplier; __be32 *buf = desc->ctx_payload;
} else { unsigned int data_blocks = desc->data_blocks;
pcm_frames = 0;
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; 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, int amdtp_am824_init(struct amdtp_stream *s, struct fw_unit *unit,
enum amdtp_stream_direction dir, enum cip_flags flags) 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) if (dir == AMDTP_IN_STREAM)
process_data_blocks = process_tx_data_blocks; process_ctx_payloads = process_ir_ctx_payloads;
else 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, return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM,
process_data_blocks, process_ctx_payloads, sizeof(struct amdtp_am824));
sizeof(struct amdtp_am824));
} }
EXPORT_SYMBOL_GPL(amdtp_am824_init); EXPORT_SYMBOL_GPL(amdtp_am824_init);

View File

@ -14,8 +14,8 @@
#include <linux/tracepoint.h> #include <linux/tracepoint.h>
TRACE_EVENT(amdtp_packet, 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_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, index), TP_ARGS(s, cycles, cip_header, payload_length, data_blocks, data_block_counter, index),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(unsigned int, second) __field(unsigned int, second)
__field(unsigned int, cycle) __field(unsigned int, cycle)
@ -47,7 +47,7 @@ TRACE_EVENT(amdtp_packet,
} }
__entry->payload_quadlets = payload_length / sizeof(__be32); __entry->payload_quadlets = payload_length / sizeof(__be32);
__entry->data_blocks = data_blocks; __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->packet_index = s->packet_index;
__entry->irq = !!in_interrupt(); __entry->irq = !!in_interrupt();
__entry->index = index; __entry->index = index;

View File

@ -74,16 +74,16 @@ static void pcm_period_tasklet(unsigned long data);
* @dir: the direction of stream * @dir: the direction of stream
* @flags: the packet transmission method to use * @flags: the packet transmission method to use
* @fmt: the value of fmt field in CIP header * @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 * @protocol_size: the size to allocate newly for protocol
*/ */
int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
enum amdtp_stream_direction dir, enum cip_flags flags, enum amdtp_stream_direction dir, enum cip_flags flags,
unsigned int fmt, 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) unsigned int protocol_size)
{ {
if (process_data_blocks == NULL) if (process_ctx_payloads == NULL)
return -EINVAL; return -EINVAL;
s->protocol = kzalloc(protocol_size, GFP_KERNEL); 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->callbacked = false;
s->fmt = fmt; 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; 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], 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) | cip_header[0] = cpu_to_be32(READ_ONCE(s->source_node_id_field) |
(s->data_block_quadlets << CIP_DBS_SHIFT) | (s->data_block_quadlets << CIP_DBS_SHIFT) |
((s->sph << CIP_SPH_SHIFT) & CIP_SPH_MASK) | ((s->sph << CIP_SPH_SHIFT) & CIP_SPH_MASK) |
s->data_block_counter); data_block_counter);
cip_header[1] = cpu_to_be32(CIP_EOH | cip_header[1] = cpu_to_be32(CIP_EOH |
((s->fmt << CIP_FMT_SHIFT) & CIP_FMT_MASK) | ((s->fmt << CIP_FMT_SHIFT) & CIP_FMT_MASK) |
((s->ctx_data.rx.fdf << CIP_FDF_SHIFT) & CIP_FDF_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, static void build_it_pkt_header(struct amdtp_stream *s, unsigned int cycle,
struct fw_iso_packet *params, struct fw_iso_packet *params,
unsigned int data_blocks, unsigned int syt, unsigned int data_blocks,
unsigned int index) unsigned int data_block_counter,
unsigned int syt, unsigned int index)
{ {
unsigned int payload_length; unsigned int payload_length;
__be32 *cip_header; __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; payload_length = data_blocks * sizeof(__be32) * s->data_block_quadlets;
params->payload_length = payload_length; 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)) { if (!(s->flags & CIP_NO_HEADER)) {
cip_header = (__be32 *)params->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); params->header_length = 2 * sizeof(__be32);
payload_length += params->header_length; payload_length += params->header_length;
} else { } 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, trace_amdtp_packet(s, cycle, cip_header, payload_length, data_blocks,
index); data_block_counter, index);
if (!(s->flags & CIP_DBC_IS_END_EVENT)) {
s->data_block_counter =
(s->data_block_counter + data_blocks) & 0xff;
}
} }
static int check_cip_header(struct amdtp_stream *s, const __be32 *buf, static int check_cip_header(struct amdtp_stream *s, const __be32 *buf,
unsigned int payload_length, unsigned int payload_length,
unsigned int *data_blocks, unsigned int *dbc, unsigned int *data_blocks,
unsigned int *syt) unsigned int *data_block_counter, unsigned int *syt)
{ {
u32 cip_header[2]; u32 cip_header[2];
unsigned int sph; unsigned int sph;
unsigned int fmt; unsigned int fmt;
unsigned int fdf; unsigned int fdf;
unsigned int dbc;
bool lost; bool lost;
cip_header[0] = be32_to_cpu(buf[0]); 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 */ /* 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) && if (*data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) &&
s->data_block_counter != UINT_MAX) *data_block_counter != UINT_MAX)
*dbc = s->data_block_counter; dbc = *data_block_counter;
if (((s->flags & CIP_SKIP_DBC_ZERO_CHECK) && if ((dbc == 0x00 && (s->flags & CIP_SKIP_DBC_ZERO_CHECK)) ||
*dbc == s->ctx_data.tx.first_dbc) || *data_block_counter == UINT_MAX) {
s->data_block_counter == UINT_MAX) {
lost = false; lost = false;
} else if (!(s->flags & CIP_DBC_IS_END_EVENT)) { } else if (!(s->flags & CIP_DBC_IS_END_EVENT)) {
lost = *dbc != s->data_block_counter; lost = dbc != *data_block_counter;
} else { } else {
unsigned int dbc_interval; unsigned int dbc_interval;
@ -598,16 +592,18 @@ static int check_cip_header(struct amdtp_stream *s, const __be32 *buf,
else else
dbc_interval = *data_blocks; dbc_interval = *data_blocks;
lost = *dbc != ((s->data_block_counter + dbc_interval) & 0xff); lost = dbc != ((*data_block_counter + dbc_interval) & 0xff);
} }
if (lost) { if (lost) {
dev_err(&s->unit->device, dev_err(&s->unit->device,
"Detect discontinuity of CIP: %02X %02X\n", "Detect discontinuity of CIP: %02X %02X\n",
s->data_block_counter, *dbc); *data_block_counter, dbc);
return -EIO; return -EIO;
} }
*data_block_counter = dbc;
*syt = cip_header[1] & CIP_SYT_MASK; *syt = cip_header[1] & CIP_SYT_MASK;
return 0; 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, static int parse_ir_ctx_header(struct amdtp_stream *s, unsigned int cycle,
const __be32 *ctx_header, const __be32 *ctx_header,
unsigned int *payload_length, unsigned int *payload_length,
unsigned int *data_blocks, unsigned int *syt, unsigned int *data_blocks,
unsigned int index) unsigned int *data_block_counter,
unsigned int *syt, unsigned int index)
{ {
unsigned int dbc;
const __be32 *cip_header; const __be32 *cip_header;
int err; 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)) { if (!(s->flags & CIP_NO_HEADER)) {
cip_header = ctx_header + 2; cip_header = ctx_header + 2;
err = check_cip_header(s, cip_header, *payload_length, err = check_cip_header(s, cip_header, *payload_length,
data_blocks, &dbc, syt); data_blocks, data_block_counter, syt);
if (err < 0) if (err < 0)
return err; return err;
} else { } else {
@ -645,16 +641,12 @@ static int parse_ir_ctx_header(struct amdtp_stream *s, unsigned int cycle,
s->data_block_quadlets; s->data_block_quadlets;
*syt = 0; *syt = 0;
if (s->data_block_counter != UINT_MAX) if (*data_block_counter == UINT_MAX)
dbc = s->data_block_counter; *data_block_counter = 0;
else
dbc = 0;
} }
s->data_block_counter = dbc;
trace_amdtp_packet(s, cycle, cip_header, *payload_length, *data_blocks, trace_amdtp_packet(s, cycle, cip_header, *payload_length, *data_blocks,
index); *data_block_counter, index);
return err; return err;
} }
@ -686,6 +678,80 @@ static inline u32 compute_it_cycle(const __be32 ctx_header_tstamp)
return increment_cycle_count(cycle, QUEUE_LENGTH); 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) static inline void cancel_stream(struct amdtp_stream *s)
{ {
s->packet_index = -1; 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); 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, static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
size_t header_length, void *header, size_t header_length, void *header,
void *private_data) void *private_data)
@ -706,38 +785,31 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
if (s->packet_index < 0) if (s->packet_index < 0)
return; 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) { for (i = 0; i < packets; ++i) {
u32 cycle; const struct pkt_desc *desc = s->pkt_descs + i;
unsigned int syt; unsigned int syt;
unsigned int data_blocks;
__be32 *buffer;
unsigned int pcm_frames;
struct { struct {
struct fw_iso_packet params; struct fw_iso_packet params;
__be32 header[IT_PKT_HEADER_SIZE_CIP / sizeof(__be32)]; __be32 header[IT_PKT_HEADER_SIZE_CIP / sizeof(__be32)];
} template = { {0}, {0} }; } template = { {0}, {0} };
struct snd_pcm_substream *pcm;
cycle = compute_it_cycle(*ctx_header); if (s->ctx_data.rx.syt_override < 0)
syt = calculate_syt(s, cycle); syt = desc->syt;
data_blocks = calculate_data_blocks(s, syt); else
buffer = s->buffer.packets[s->packet_index].buffer; syt = s->ctx_data.rx.syt_override;
pcm_frames = s->process_data_blocks(s, buffer, data_blocks,
&syt);
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); syt, i);
if (queue_out_packet(s, &template.params) < 0) { if (queue_out_packet(s, &template.params) < 0) {
cancel_stream(s); cancel_stream(s);
return; 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); 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) void *private_data)
{ {
struct amdtp_stream *s = private_data; struct amdtp_stream *s = private_data;
unsigned int i, packets; unsigned int packets;
__be32 *ctx_header = header; __be32 *ctx_header = header;
int i;
int err;
if (s->packet_index < 0) if (s->packet_index < 0)
return; return;
@ -757,48 +831,23 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
// The number of packets in buffer. // The number of packets in buffer.
packets = header_length / s->ctx_data.tx.ctx_header_size; packets = header_length / s->ctx_data.tx.ctx_header_size;
for (i = 0; i < packets; i++) { err = generate_device_pkt_descs(s, s->pkt_descs, ctx_header, packets);
u32 cycle; if (err < 0) {
unsigned int payload_length; if (err != -EAGAIN) {
unsigned int data_blocks; cancel_stream(s);
unsigned int syt; return;
__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;
}
} }
} else {
if (queue_in_packet(s, &params) < 0) process_ctx_payloads(s, s->pkt_descs, packets);
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);
} }
/* Queueing error or detecting invalid payload. */ for (i = 0; i < packets; ++i) {
if (i < packets) { struct fw_iso_packet params = {0};
cancel_stream(s);
return; if (queue_in_packet(s, &params) < 0) {
cancel_stream(s);
return;
}
} }
fw_iso_context_queue_flush(s->context); 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 * amdtp_stream_set_parameters() and it must be started before any PCM or MIDI
* device can be started. * 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 { static const struct {
unsigned int data_block; unsigned int data_block;
@ -932,6 +981,13 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
else else
s->tag = TAG_CIP; 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; s->packet_index = 0;
do { do {
struct fw_iso_packet params; 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, &params); err = queue_out_packet(s, &params);
} }
if (err < 0) if (err < 0)
goto err_context; goto err_pkt_descs;
} while (s->packet_index > 0); } while (s->packet_index > 0);
/* NOTE: TAG1 matches CIP. This just affects in stream. */ /* 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; s->callbacked = false;
err = fw_iso_context_start(s->context, -1, 0, tag); err = fw_iso_context_start(s->context, -1, 0, tag);
if (err < 0) if (err < 0)
goto err_context; goto err_pkt_descs;
mutex_unlock(&s->mutex); mutex_unlock(&s->mutex);
return 0; return 0;
err_pkt_descs:
kfree(s->pkt_descs);
err_context: err_context:
fw_iso_context_destroy(s->context); fw_iso_context_destroy(s->context);
s->context = ERR_PTR(-1); s->context = ERR_PTR(-1);
@ -970,7 +1027,6 @@ err_unlock:
return err; return err;
} }
EXPORT_SYMBOL(amdtp_stream_start);
/** /**
* amdtp_stream_pcm_pointer - get the PCM buffer position * 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 * All PCM and MIDI devices of the stream must be stopped before the stream
* itself can be stopped. * 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); mutex_lock(&s->mutex);
@ -1055,12 +1111,12 @@ void amdtp_stream_stop(struct amdtp_stream *s)
fw_iso_context_destroy(s->context); fw_iso_context_destroy(s->context);
s->context = ERR_PTR(-1); s->context = ERR_PTR(-1);
iso_packets_buffer_destroy(&s->buffer, s->unit); iso_packets_buffer_destroy(&s->buffer, s->unit);
kfree(s->pkt_descs);
s->callbacked = false; s->callbacked = false;
mutex_unlock(&s->mutex); mutex_unlock(&s->mutex);
} }
EXPORT_SYMBOL(amdtp_stream_stop);
/** /**
* amdtp_stream_pcm_abort - abort the running PCM device * 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); snd_pcm_stop_xrun(pcm);
} }
EXPORT_SYMBOL(amdtp_stream_pcm_abort); 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);

View File

@ -33,6 +33,8 @@
* @CIP_HEADER_WITHOUT_EOH: Only for in-stream. CIP Header doesn't include * @CIP_HEADER_WITHOUT_EOH: Only for in-stream. CIP Header doesn't include
* valid EOH. * valid EOH.
* @CIP_NO_HEADERS: a lack of headers in packets * @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 { enum cip_flags {
CIP_NONBLOCKING = 0x00, CIP_NONBLOCKING = 0x00,
@ -45,6 +47,7 @@ enum cip_flags {
CIP_JUMBO_PAYLOAD = 0x40, CIP_JUMBO_PAYLOAD = 0x40,
CIP_HEADER_WITHOUT_EOH = 0x80, CIP_HEADER_WITHOUT_EOH = 0x80,
CIP_NO_HEADER = 0x100, CIP_NO_HEADER = 0x100,
CIP_UNALIGHED_DBC = 0x200,
}; };
/** /**
@ -91,12 +94,20 @@ enum amdtp_stream_direction {
AMDTP_IN_STREAM 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; 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, struct amdtp_stream *s,
__be32 *buffer, const struct pkt_desc *desc,
unsigned int data_blocks, unsigned int packets,
unsigned int *syt); struct snd_pcm_substream *pcm);
struct amdtp_stream { struct amdtp_stream {
struct fw_unit *unit; struct fw_unit *unit;
enum cip_flags flags; enum cip_flags flags;
@ -107,6 +118,7 @@ struct amdtp_stream {
struct fw_iso_context *context; struct fw_iso_context *context;
struct iso_packets_buffer buffer; struct iso_packets_buffer buffer;
int packet_index; int packet_index;
struct pkt_desc *pkt_descs;
int tag; int tag;
union { union {
struct { struct {
@ -119,8 +131,6 @@ struct amdtp_stream {
// Fixed interval of dbc between previos/current // Fixed interval of dbc between previos/current
// packets. // packets.
unsigned int dbc_interval; unsigned int dbc_interval;
// Indicate the value of dbc field in a first packet.
unsigned int first_dbc;
} tx; } tx;
struct { struct {
// To calculate CIP data blocks and tstamp. // To calculate CIP data blocks and tstamp.
@ -131,6 +141,7 @@ struct amdtp_stream {
// To generate CIP header. // To generate CIP header.
unsigned int fdf; unsigned int fdf;
int syt_override;
} rx; } rx;
} ctx_data; } ctx_data;
@ -158,13 +169,18 @@ struct amdtp_stream {
/* For backends to process data blocks. */ /* For backends to process data blocks. */
void *protocol; 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, int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
enum amdtp_stream_direction dir, enum cip_flags flags, enum amdtp_stream_direction dir, enum cip_flags flags,
unsigned int fmt, 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); unsigned int protocol_size);
void amdtp_stream_destroy(struct amdtp_stream *s); 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 data_block_quadlets);
unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s); 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_update(struct amdtp_stream *s);
void amdtp_stream_stop(struct amdtp_stream *s);
int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s, int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
struct snd_pcm_runtime *runtime); 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; 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 #endif

View File

@ -115,6 +115,8 @@ struct snd_bebob {
/* For BeBoB version quirk. */ /* For BeBoB version quirk. */
unsigned int version; unsigned int version;
struct amdtp_domain domain;
}; };
static inline int static inline int

View File

@ -445,10 +445,9 @@ start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream)
goto end; goto end;
} }
/* start amdtp stream */ // start amdtp stream.
err = amdtp_stream_start(stream, err = amdtp_domain_add_stream(&bebob->domain, stream,
conn->resources.channel, conn->resources.channel, conn->speed);
conn->speed);
end: end:
return err; return err;
} }
@ -523,7 +522,13 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
return err; 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, 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) if (rate == 0)
rate = curr_rate; rate = curr_rate;
if (curr_rate != rate) { if (curr_rate != rate) {
amdtp_stream_stop(&bebob->tx_stream); amdtp_domain_stop(&bebob->domain);
amdtp_stream_stop(&bebob->rx_stream);
break_both_connections(bebob); break_both_connections(bebob);
cmp_connection_release(&bebob->out_conn); 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 // packet queueing error or detecting discontinuity
if (amdtp_streaming_error(&bebob->rx_stream) || if (amdtp_streaming_error(&bebob->rx_stream) ||
amdtp_streaming_error(&bebob->tx_stream)) { amdtp_streaming_error(&bebob->tx_stream)) {
amdtp_stream_stop(&bebob->rx_stream); amdtp_domain_stop(&bebob->domain);
amdtp_stream_stop(&bebob->tx_stream);
break_both_connections(bebob); break_both_connections(bebob);
} }
@ -640,11 +641,16 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob)
return err; return err;
err = start_stream(bebob, &bebob->rx_stream); err = start_stream(bebob, &bebob->rx_stream);
if (err < 0) { if (err < 0)
dev_err(&bebob->unit->device, goto error;
"fail to run AMDTP master stream:%d\n", err);
err = start_stream(bebob, &bebob->tx_stream);
if (err < 0)
goto error;
err = amdtp_domain_start(&bebob->domain);
if (err < 0)
goto error; goto error;
}
// NOTE: // NOTE:
// The firmware customized by M-Audio uses these commands to // 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, if (!amdtp_stream_wait_callback(&bebob->rx_stream,
CALLBACK_TIMEOUT)) { CALLBACK_TIMEOUT) ||
err = -ETIMEDOUT; !amdtp_stream_wait_callback(&bebob->tx_stream,
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)) { CALLBACK_TIMEOUT)) {
err = -ETIMEDOUT; err = -ETIMEDOUT;
goto error; goto error;
@ -683,8 +676,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob)
return 0; return 0;
error: error:
amdtp_stream_stop(&bebob->tx_stream); amdtp_domain_stop(&bebob->domain);
amdtp_stream_stop(&bebob->rx_stream);
break_both_connections(bebob); break_both_connections(bebob);
return err; return err;
} }
@ -692,9 +684,7 @@ error:
void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob) void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob)
{ {
if (bebob->substreams_counter == 0) { if (bebob->substreams_counter == 0) {
amdtp_stream_stop(&bebob->rx_stream); amdtp_domain_stop(&bebob->domain);
amdtp_stream_stop(&bebob->tx_stream);
break_both_connections(bebob); break_both_connections(bebob);
cmp_connection_release(&bebob->out_conn); 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) 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->tx_stream);
destroy_stream(bebob, &bebob->rx_stream); destroy_stream(bebob, &bebob->rx_stream);
} }

View File

@ -154,14 +154,10 @@ static void stop_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
for (i = 0; i < params->count; i++) { for (i = 0; i < params->count; i++) {
reg = cpu_to_be32((u32)-1); reg = cpu_to_be32((u32)-1);
if (dir == AMDTP_IN_STREAM) { if (dir == AMDTP_IN_STREAM) {
amdtp_stream_stop(&dice->tx_stream[i]);
snd_dice_transaction_write_tx(dice, snd_dice_transaction_write_tx(dice,
params->size * i + TX_ISOCHRONOUS, params->size * i + TX_ISOCHRONOUS,
&reg, sizeof(reg)); &reg, sizeof(reg));
} else { } else {
amdtp_stream_stop(&dice->rx_stream[i]);
snd_dice_transaction_write_rx(dice, snd_dice_transaction_write_rx(dice,
params->size * i + RX_ISOCHRONOUS, params->size * i + RX_ISOCHRONOUS,
&reg, sizeof(reg)); &reg, 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) { if (dice->substreams_counter == 0 || curr_rate != rate) {
struct reg_params tx_params, rx_params; struct reg_params tx_params, rx_params;
amdtp_domain_stop(&dice->domain);
err = get_register_params(dice, &tx_params, &rx_params); err = get_register_params(dice, &tx_params, &rx_params);
if (err < 0) if (err < 0)
return err; return err;
finish_session(dice, &tx_params, &rx_params); finish_session(dice, &tx_params, &rx_params);
release_resources(dice); release_resources(dice);
@ -377,7 +374,8 @@ static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
return err; 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) if (err < 0)
return err; return err;
} }
@ -410,6 +408,7 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice)
for (i = 0; i < MAX_STREAMS; ++i) { for (i = 0; i < MAX_STREAMS; ++i) {
if (amdtp_streaming_error(&dice->tx_stream[i]) || if (amdtp_streaming_error(&dice->tx_stream[i]) ||
amdtp_streaming_error(&dice->rx_stream[i])) { amdtp_streaming_error(&dice->rx_stream[i])) {
amdtp_domain_stop(&dice->domain);
finish_session(dice, &tx_params, &rx_params); finish_session(dice, &tx_params, &rx_params);
break; break;
} }
@ -456,6 +455,10 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice)
goto error; goto error;
} }
err = amdtp_domain_start(&dice->domain);
if (err < 0)
goto error;
for (i = 0; i < MAX_STREAMS; i++) { for (i = 0; i < MAX_STREAMS; i++) {
if ((i < tx_params.count && if ((i < tx_params.count &&
!amdtp_stream_wait_callback(&dice->tx_stream[i], !amdtp_stream_wait_callback(&dice->tx_stream[i],
@ -471,6 +474,7 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice)
return 0; return 0;
error: error:
amdtp_domain_stop(&dice->domain);
finish_session(dice, &tx_params, &rx_params); finish_session(dice, &tx_params, &rx_params);
return err; return err;
} }
@ -485,8 +489,10 @@ void snd_dice_stream_stop_duplex(struct snd_dice *dice)
struct reg_params tx_params, rx_params; struct reg_params tx_params, rx_params;
if (dice->substreams_counter == 0) { 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); finish_session(dice, &tx_params, &rx_params);
}
release_resources(dice); release_resources(dice);
} }
@ -564,7 +570,15 @@ int snd_dice_stream_init_duplex(struct snd_dice *dice)
destroy_stream(dice, AMDTP_OUT_STREAM, i); destroy_stream(dice, AMDTP_OUT_STREAM, i);
for (i = 0; i < MAX_STREAMS; i++) for (i = 0; i < MAX_STREAMS; i++)
destroy_stream(dice, AMDTP_IN_STREAM, 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: 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_IN_STREAM, i);
destroy_stream(dice, AMDTP_OUT_STREAM, i); destroy_stream(dice, AMDTP_OUT_STREAM, i);
} }
amdtp_domain_destroy(&dice->domain);
} }
void snd_dice_stream_update_duplex(struct snd_dice *dice) 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; dice->global_enabled = false;
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);
stop_streams(dice, AMDTP_IN_STREAM, &tx_params); stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
stop_streams(dice, AMDTP_OUT_STREAM, &rx_params); stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
} }

View File

@ -112,6 +112,8 @@ struct snd_dice {
bool global_enabled; bool global_enabled;
struct completion clock_accepted; struct completion clock_accepted;
unsigned int substreams_counter; unsigned int substreams_counter;
struct amdtp_domain domain;
}; };
enum snd_dice_addr_type { enum snd_dice_addr_type {

View File

@ -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, 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; struct amdtp_dot *p = s->protocol;
unsigned int channels = p->pcm_channels;
struct snd_pcm_runtime *runtime = pcm->runtime; 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; 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 + src = (void *)runtime->dma_area +
frames_to_bytes(runtime, s->pcm_buffer_pointer); frames_to_bytes(runtime, pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
buffer++; buffer++;
for (i = 0; i < frames; ++i) { 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, 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; struct amdtp_dot *p = s->protocol;
unsigned int channels = p->pcm_channels;
struct snd_pcm_runtime *runtime = pcm->runtime; struct snd_pcm_runtime *runtime = pcm->runtime;
unsigned int channels, remaining_frames, i, c; unsigned int pcm_buffer_pointer;
int remaining_frames;
u32 *dst; 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 + dst = (void *)runtime->dma_area +
frames_to_bytes(runtime, s->pcm_buffer_pointer); frames_to_bytes(runtime, pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
buffer++; buffer++;
for (i = 0; i < frames; ++i) { 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, 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; struct amdtp_dot *p = s->protocol;
unsigned int f, port; unsigned int f, port;
@ -242,7 +254,7 @@ static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
u8 *b; u8 *b;
for (f = 0; f < data_blocks; f++) { for (f = 0; f < data_blocks; f++) {
port = (s->data_block_counter + f) % 8; port = (data_block_counter + f) % 8;
b = (u8 *)&buffer[0]; b = (u8 *)&buffer[0];
len = 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); WRITE_ONCE(p->midi[port], midi);
} }
static unsigned int process_tx_data_blocks(struct amdtp_stream *s, static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
__be32 *buffer, const struct pkt_desc *descs,
unsigned int data_blocks, unsigned int packets,
unsigned int *syt) struct snd_pcm_substream *pcm)
{ {
struct snd_pcm_substream *pcm; unsigned int pcm_frames = 0;
unsigned int pcm_frames; int i;
pcm = READ_ONCE(s->pcm); for (i = 0; i < packets; ++i) {
if (pcm) { const struct pkt_desc *desc = descs + i;
read_pcm_s32(s, pcm, buffer, data_blocks); __be32 *buf = desc->ctx_payload;
pcm_frames = data_blocks; unsigned int data_blocks = desc->data_blocks;
} else {
pcm_frames = 0; 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; return pcm_frames;
} }
static unsigned int process_rx_data_blocks(struct amdtp_stream *s, static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
__be32 *buffer, const struct pkt_desc *descs,
unsigned int data_blocks, unsigned int packets,
unsigned int *syt) struct snd_pcm_substream *pcm)
{ {
struct snd_pcm_substream *pcm; unsigned int pcm_frames = 0;
unsigned int pcm_frames; int i;
pcm = READ_ONCE(s->pcm); for (i = 0; i < packets; ++i) {
if (pcm) { const struct pkt_desc *desc = descs + i;
write_pcm_s32(s, pcm, buffer, data_blocks); __be32 *buf = desc->ctx_payload;
pcm_frames = data_blocks; unsigned int data_blocks = desc->data_blocks;
} else {
write_pcm_silence(s, buffer, data_blocks); if (pcm) {
pcm_frames = 0; 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; return pcm_frames;
} }
int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit, int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit,
enum amdtp_stream_direction dir) 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; enum cip_flags flags;
/* Use different mode between incoming/outgoing. */ // Use different mode between incoming/outgoing.
if (dir == AMDTP_IN_STREAM) { if (dir == AMDTP_IN_STREAM) {
flags = CIP_NONBLOCKING; flags = CIP_NONBLOCKING;
process_data_blocks = process_tx_data_blocks; process_ctx_payloads = process_ir_ctx_payloads;
} else { } else {
flags = CIP_BLOCKING; 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, 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) void amdtp_dot_reset(struct amdtp_stream *s)

View File

@ -126,9 +126,6 @@ static void finish_session(struct snd_dg00x *dg00x)
{ {
__be32 data; __be32 data;
amdtp_stream_stop(&dg00x->tx_stream);
amdtp_stream_stop(&dg00x->rx_stream);
data = cpu_to_be32(0x00000003); data = cpu_to_be32(0x00000003);
snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST, snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_SET, 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); 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 snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x)
{ {
int err; int err;
/* For out-stream. */ err = init_stream(dg00x, &dg00x->rx_stream);
err = fw_iso_resources_init(&dg00x->rx_resources, dg00x->unit);
if (err < 0) if (err < 0)
goto error; return err;
err = amdtp_dot_init(&dg00x->rx_stream, dg00x->unit, AMDTP_OUT_STREAM);
if (err < 0)
goto error;
/* For in-stream. */ err = init_stream(dg00x, &dg00x->tx_stream);
err = fw_iso_resources_init(&dg00x->tx_resources, dg00x->unit);
if (err < 0) if (err < 0)
goto error; destroy_stream(dg00x, &dg00x->rx_stream);
err = amdtp_dot_init(&dg00x->tx_stream, dg00x->unit, AMDTP_IN_STREAM);
if (err < 0) err = amdtp_domain_init(&dg00x->domain);
goto error; 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; return err;
} }
@ -250,11 +277,10 @@ error:
*/ */
void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x) void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x)
{ {
amdtp_stream_destroy(&dg00x->rx_stream); amdtp_domain_destroy(&dg00x->domain);
fw_iso_resources_destroy(&dg00x->rx_resources);
amdtp_stream_destroy(&dg00x->tx_stream); destroy_stream(dg00x, &dg00x->rx_stream);
fw_iso_resources_destroy(&dg00x->tx_resources); destroy_stream(dg00x, &dg00x->tx_stream);
} }
int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate) 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; rate = curr_rate;
if (dg00x->substreams_counter == 0 || curr_rate != rate) { if (dg00x->substreams_counter == 0 || curr_rate != rate) {
amdtp_domain_stop(&dg00x->domain);
finish_session(dg00x); finish_session(dg00x);
fw_iso_resources_free(&dg00x->tx_resources); fw_iso_resources_free(&dg00x->tx_resources);
@ -301,8 +329,10 @@ int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x)
return 0; return 0;
if (amdtp_streaming_error(&dg00x->tx_stream) || 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); finish_session(dg00x);
}
if (generation != fw_parent_device(dg00x->unit)->card->generation) { if (generation != fw_parent_device(dg00x->unit)->card->generation) {
err = fw_iso_resources_update(&dg00x->tx_resources); 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. * which source of clock is used.
*/ */
if (!amdtp_stream_running(&dg00x->rx_stream)) { if (!amdtp_stream_running(&dg00x->rx_stream)) {
int spd = fw_parent_device(dg00x->unit)->max_speed;
err = begin_session(dg00x); err = begin_session(dg00x);
if (err < 0) if (err < 0)
goto error; goto error;
err = amdtp_stream_start(&dg00x->rx_stream, err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->rx_stream,
dg00x->rx_resources.channel, dg00x->rx_resources.channel, spd);
fw_parent_device(dg00x->unit)->max_speed); 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) if (err < 0)
goto error; goto error;
if (!amdtp_stream_wait_callback(&dg00x->rx_stream, if (!amdtp_stream_wait_callback(&dg00x->rx_stream,
CALLBACK_TIMEOUT)) { CALLBACK_TIMEOUT) ||
err = -ETIMEDOUT; !amdtp_stream_wait_callback(&dg00x->tx_stream,
goto error; CALLBACK_TIMEOUT)) {
}
}
/*
* 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)) {
err = -ETIMEDOUT; err = -ETIMEDOUT;
goto error; goto error;
} }
@ -356,6 +380,7 @@ int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x)
return 0; return 0;
error: error:
amdtp_domain_stop(&dg00x->domain);
finish_session(dg00x); finish_session(dg00x);
return err; return err;
@ -364,6 +389,7 @@ error:
void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x) void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x)
{ {
if (dg00x->substreams_counter == 0) { if (dg00x->substreams_counter == 0) {
amdtp_domain_stop(&dg00x->domain);
finish_session(dg00x); finish_session(dg00x);
fw_iso_resources_free(&dg00x->tx_resources); fw_iso_resources_free(&dg00x->tx_resources);

View File

@ -59,6 +59,8 @@ struct snd_dg00x {
/* Console models have additional MIDI ports for control surface. */ /* Console models have additional MIDI ports for control surface. */
bool is_console; bool is_console;
struct amdtp_domain domain;
}; };
#define DG00X_ADDR_BASE 0xffffe0000000ull #define DG00X_ADDR_BASE 0xffffe0000000ull

View File

@ -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); return amdtp_stream_set_parameters(s, rate, data_channels);
} }
static void write_pcm_s32(struct amdtp_stream *s, static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
struct snd_pcm_substream *pcm, __le32 *buffer, unsigned int frames,
__le32 *buffer, unsigned int frames) unsigned int pcm_frames)
{ {
struct amdtp_ff *p = s->protocol; struct amdtp_ff *p = s->protocol;
unsigned int channels = p->pcm_channels;
struct snd_pcm_runtime *runtime = pcm->runtime; 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; 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 + src = (void *)runtime->dma_area +
frames_to_bytes(runtime, s->pcm_buffer_pointer); frames_to_bytes(runtime, pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
for (i = 0; i < frames; ++i) { for (i = 0; i < frames; ++i) {
for (c = 0; c < channels; ++c) { 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, static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
struct snd_pcm_substream *pcm, __le32 *buffer, unsigned int frames,
__le32 *buffer, unsigned int frames) unsigned int pcm_frames)
{ {
struct amdtp_ff *p = s->protocol; struct amdtp_ff *p = s->protocol;
unsigned int channels = p->pcm_channels;
struct snd_pcm_runtime *runtime = pcm->runtime; struct snd_pcm_runtime *runtime = pcm->runtime;
unsigned int channels, remaining_frames, i, c; unsigned int pcm_buffer_pointer;
int remaining_frames;
u32 *dst; 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 + dst = (void *)runtime->dma_area +
frames_to_bytes(runtime, s->pcm_buffer_pointer); frames_to_bytes(runtime, pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
for (i = 0; i < frames; ++i) { for (i = 0; i < frames; ++i) {
for (c = 0; c < channels; ++c) { 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); return amdtp_stream_add_pcm_hw_constraints(s, runtime);
} }
static unsigned int process_rx_data_blocks(struct amdtp_stream *s, static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
__be32 *buffer, const struct pkt_desc *descs,
unsigned int data_blocks, unsigned int packets,
unsigned int *syt) struct snd_pcm_substream *pcm)
{ {
struct snd_pcm_substream *pcm = READ_ONCE(s->pcm); unsigned int pcm_frames = 0;
unsigned int pcm_frames; int i;
if (pcm) { for (i = 0; i < packets; ++i) {
write_pcm_s32(s, pcm, (__le32 *)buffer, data_blocks); const struct pkt_desc *desc = descs + i;
pcm_frames = data_blocks; __le32 *buf = (__le32 *)desc->ctx_payload;
} else { unsigned int data_blocks = desc->data_blocks;
write_pcm_silence(s, (__le32 *)buffer, data_blocks);
pcm_frames = 0; 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; return pcm_frames;
} }
static unsigned int process_tx_data_blocks(struct amdtp_stream *s, static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
__be32 *buffer, const struct pkt_desc *descs,
unsigned int data_blocks, unsigned int packets,
unsigned int *syt) struct snd_pcm_substream *pcm)
{ {
struct snd_pcm_substream *pcm = READ_ONCE(s->pcm); unsigned int pcm_frames = 0;
unsigned int pcm_frames; int i;
if (pcm) { for (i = 0; i < packets; ++i) {
read_pcm_s32(s, pcm, (__le32 *)buffer, data_blocks); const struct pkt_desc *desc = descs + i;
pcm_frames = data_blocks; __le32 *buf = (__le32 *)desc->ctx_payload;
} else { unsigned int data_blocks = desc->data_blocks;
pcm_frames = 0;
if (pcm) {
read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
pcm_frames += data_blocks;
}
} }
return pcm_frames; 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, int amdtp_ff_init(struct amdtp_stream *s, struct fw_unit *unit,
enum amdtp_stream_direction dir) 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) if (dir == AMDTP_IN_STREAM)
process_data_blocks = process_tx_data_blocks; process_ctx_payloads = process_ir_ctx_payloads;
else 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, 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));
} }

View File

@ -32,61 +32,65 @@ int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc,
static inline void finish_session(struct snd_ff *ff) 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->finish_session(ff);
ff->spec->protocol->switch_fetching_mode(ff, false); 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 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; resources = &ff->tx_resources;
stream = &ff->tx_stream; dir = AMDTP_IN_STREAM;
} else { } else {
resources = &ff->rx_resources; resources = &ff->rx_resources;
stream = &ff->rx_stream; dir = AMDTP_OUT_STREAM;
} }
err = fw_iso_resources_init(resources, ff->unit); err = fw_iso_resources_init(resources, ff->unit);
if (err < 0) if (err < 0)
return err; return err;
err = amdtp_ff_init(stream, ff->unit, dir); err = amdtp_ff_init(s, ff->unit, dir);
if (err < 0) if (err < 0)
fw_iso_resources_destroy(resources); fw_iso_resources_destroy(resources);
return err; 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(s);
amdtp_stream_destroy(&ff->tx_stream);
if (s == &ff->tx_stream)
fw_iso_resources_destroy(&ff->tx_resources); fw_iso_resources_destroy(&ff->tx_resources);
} else { else
amdtp_stream_destroy(&ff->rx_stream);
fw_iso_resources_destroy(&ff->rx_resources); fw_iso_resources_destroy(&ff->rx_resources);
}
} }
int snd_ff_stream_init_duplex(struct snd_ff *ff) int snd_ff_stream_init_duplex(struct snd_ff *ff)
{ {
int err; int err;
err = init_stream(ff, AMDTP_OUT_STREAM); err = init_stream(ff, &ff->rx_stream);
if (err < 0) 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; return err;
} }
@ -96,8 +100,10 @@ end:
*/ */
void snd_ff_stream_destroy_duplex(struct snd_ff *ff) void snd_ff_stream_destroy_duplex(struct snd_ff *ff)
{ {
destroy_stream(ff, AMDTP_IN_STREAM); amdtp_domain_destroy(&ff->domain);
destroy_stream(ff, AMDTP_OUT_STREAM);
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) 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; enum snd_ff_stream_mode mode;
int i; int i;
amdtp_domain_stop(&ff->domain);
finish_session(ff); finish_session(ff);
fw_iso_resources_free(&ff->tx_resources); 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; return 0;
if (amdtp_streaming_error(&ff->tx_stream) || 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); finish_session(ff);
}
/* /*
* Regardless of current source of clock signal, drivers transfer some * Regardless of current source of clock signal, drivers transfer some
* packets. Then, the device transfers packets. * packets. Then, the device transfers packets.
*/ */
if (!amdtp_stream_running(&ff->rx_stream)) { if (!amdtp_stream_running(&ff->rx_stream)) {
int spd = fw_parent_device(ff->unit)->max_speed;
err = ff->spec->protocol->begin_session(ff, rate); err = ff->spec->protocol->begin_session(ff, rate);
if (err < 0) if (err < 0)
goto error; goto error;
err = amdtp_stream_start(&ff->rx_stream, err = amdtp_domain_add_stream(&ff->domain, &ff->rx_stream,
ff->rx_resources.channel, ff->rx_resources.channel, spd);
fw_parent_device(ff->unit)->max_speed); 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) if (err < 0)
goto error; goto error;
if (!amdtp_stream_wait_callback(&ff->rx_stream, if (!amdtp_stream_wait_callback(&ff->rx_stream,
CALLBACK_TIMEOUT_MS) ||
!amdtp_stream_wait_callback(&ff->tx_stream,
CALLBACK_TIMEOUT_MS)) { CALLBACK_TIMEOUT_MS)) {
err = -ETIMEDOUT; err = -ETIMEDOUT;
goto error; goto error;
@ -185,22 +206,9 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
goto error; 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; return 0;
error: error:
amdtp_domain_stop(&ff->domain);
finish_session(ff); finish_session(ff);
return err; return err;
@ -209,6 +217,7 @@ error:
void snd_ff_stream_stop_duplex(struct snd_ff *ff) void snd_ff_stream_stop_duplex(struct snd_ff *ff)
{ {
if (ff->substreams_counter == 0) { if (ff->substreams_counter == 0) {
amdtp_domain_stop(&ff->domain);
finish_session(ff); finish_session(ff);
fw_iso_resources_free(&ff->tx_resources); 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) void snd_ff_stream_update_duplex(struct snd_ff *ff)
{ {
amdtp_domain_stop(&ff->domain);
// The device discontinue to transfer packets. // The device discontinue to transfer packets.
amdtp_stream_pcm_abort(&ff->tx_stream); amdtp_stream_pcm_abort(&ff->tx_stream);
amdtp_stream_stop(&ff->tx_stream);
amdtp_stream_pcm_abort(&ff->rx_stream); amdtp_stream_pcm_abort(&ff->rx_stream);
amdtp_stream_stop(&ff->rx_stream);
} }
void snd_ff_stream_lock_changed(struct snd_ff *ff) void snd_ff_stream_lock_changed(struct snd_ff *ff)

View File

@ -91,6 +91,8 @@ struct snd_ff {
int dev_lock_count; int dev_lock_count;
bool dev_lock_changed; bool dev_lock_changed;
wait_queue_head_t hwdep_wait; wait_queue_head_t hwdep_wait;
struct amdtp_domain domain;
}; };
enum snd_ff_clock_src { enum snd_ff_clock_src {

View File

@ -107,6 +107,8 @@ struct snd_efw {
u8 *resp_buf; u8 *resp_buf;
u8 *pull_ptr; u8 *pull_ptr;
u8 *push_ptr; u8 *push_ptr;
struct amdtp_domain domain;
}; };
int snd_efw_transaction_cmd(struct fw_unit *unit, int snd_efw_transaction_cmd(struct fw_unit *unit,

View File

@ -8,8 +8,7 @@
#define CALLBACK_TIMEOUT 100 #define CALLBACK_TIMEOUT 100
static int static int init_stream(struct snd_efw *efw, struct amdtp_stream *stream)
init_stream(struct snd_efw *efw, struct amdtp_stream *stream)
{ {
struct cmp_connection *conn; struct cmp_connection *conn;
enum cmp_direction c_dir; 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); err = cmp_connection_init(conn, efw->unit, c_dir, 0);
if (err < 0) if (err < 0)
goto end; return err;
err = amdtp_am824_init(stream, efw->unit, s_dir, CIP_BLOCKING); err = amdtp_am824_init(stream, efw->unit, s_dir, CIP_BLOCKING);
if (err < 0) { if (err < 0) {
amdtp_stream_destroy(stream); amdtp_stream_destroy(stream);
cmp_connection_destroy(conn); 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; 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, static int start_stream(struct snd_efw *efw, struct amdtp_stream *stream,
unsigned int rate) unsigned int rate)
{ {
@ -67,38 +78,26 @@ static int start_stream(struct snd_efw *efw, struct amdtp_stream *stream,
return err; return err;
// Start amdtp stream. // 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) { if (err < 0) {
cmp_connection_break(conn); cmp_connection_break(conn);
return err; 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; return 0;
} }
/* // This function should be called before starting the stream or after stopping
* This function should be called before starting the stream or after stopping // the streams.
* the streams. static void destroy_stream(struct snd_efw *efw, struct amdtp_stream *stream)
*/
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) if (stream == &efw->tx_stream)
conn = &efw->out_conn; cmp_connection_destroy(&efw->out_conn);
else else
conn = &efw->in_conn; cmp_connection_destroy(&efw->in_conn);
amdtp_stream_destroy(stream);
cmp_connection_destroy(conn);
} }
static int static int
@ -131,42 +130,28 @@ int snd_efw_stream_init_duplex(struct snd_efw *efw)
err = init_stream(efw, &efw->tx_stream); err = init_stream(efw, &efw->tx_stream);
if (err < 0) if (err < 0)
goto end; return err;
/* 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;
err = init_stream(efw, &efw->rx_stream); err = init_stream(efw, &efw->rx_stream);
if (err < 0) { if (err < 0) {
destroy_stream(efw, &efw->tx_stream); 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); err = snd_efw_command_set_tx_mode(efw, SND_EFW_TRANSPORT_MODE_IEC61883);
if (err < 0) { if (err < 0) {
destroy_stream(efw, &efw->tx_stream); destroy_stream(efw, &efw->tx_stream);
destroy_stream(efw, &efw->rx_stream); destroy_stream(efw, &efw->rx_stream);
} }
end:
return err; return err;
} }
@ -214,8 +199,10 @@ int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate)
if (rate == 0) if (rate == 0)
rate = curr_rate; rate = curr_rate;
if (rate != curr_rate) { if (rate != curr_rate) {
stop_stream(efw, &efw->tx_stream); amdtp_domain_stop(&efw->domain);
stop_stream(efw, &efw->rx_stream);
cmp_connection_break(&efw->out_conn);
cmp_connection_break(&efw->in_conn);
cmp_connection_release(&efw->out_conn); cmp_connection_release(&efw->out_conn);
cmp_connection_release(&efw->in_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) if (efw->substreams_counter == 0)
return -EIO; 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); err = snd_efw_command_get_sampling_rate(efw, &rate);
if (err < 0) if (err < 0)
return err; 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)) { if (!amdtp_stream_running(&efw->rx_stream)) {
err = start_stream(efw, &efw->rx_stream, rate); err = start_stream(efw, &efw->rx_stream, rate);
if (err < 0) { if (err < 0)
dev_err(&efw->unit->device,
"fail to start AMDTP master stream:%d\n", err);
goto error; goto error;
}
}
if (!amdtp_stream_running(&efw->tx_stream)) {
err = start_stream(efw, &efw->tx_stream, rate); err = start_stream(efw, &efw->tx_stream, rate);
if (err < 0) { if (err < 0)
dev_err(&efw->unit->device, goto error;
"fail to start AMDTP slave stream:%d\n", err);
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; goto error;
} }
} }
return 0; return 0;
error: error:
stop_stream(efw, &efw->rx_stream); amdtp_domain_stop(&efw->domain);
stop_stream(efw, &efw->tx_stream);
cmp_connection_break(&efw->out_conn);
cmp_connection_break(&efw->in_conn);
return err; return err;
} }
void snd_efw_stream_stop_duplex(struct snd_efw *efw) void snd_efw_stream_stop_duplex(struct snd_efw *efw)
{ {
if (efw->substreams_counter == 0) { if (efw->substreams_counter == 0) {
stop_stream(efw, &efw->tx_stream); amdtp_domain_stop(&efw->domain);
stop_stream(efw, &efw->rx_stream);
cmp_connection_break(&efw->out_conn);
cmp_connection_break(&efw->in_conn);
cmp_connection_release(&efw->out_conn); cmp_connection_release(&efw->out_conn);
cmp_connection_release(&efw->in_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) void snd_efw_stream_update_duplex(struct snd_efw *efw)
{ {
if (cmp_connection_update(&efw->out_conn) < 0 || amdtp_domain_stop(&efw->domain);
cmp_connection_update(&efw->in_conn) < 0) {
stop_stream(efw, &efw->rx_stream); cmp_connection_break(&efw->out_conn);
stop_stream(efw, &efw->tx_stream); cmp_connection_break(&efw->in_conn);
} else {
amdtp_stream_update(&efw->rx_stream); amdtp_stream_pcm_abort(&efw->rx_stream);
amdtp_stream_update(&efw->tx_stream); amdtp_stream_pcm_abort(&efw->tx_stream);
}
} }
void snd_efw_stream_destroy_duplex(struct snd_efw *efw) 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->rx_stream);
destroy_stream(efw, &efw->tx_stream); destroy_stream(efw, &efw->tx_stream);
} }

View File

@ -117,19 +117,25 @@ int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
return 0; return 0;
} }
static void read_pcm_s32(struct amdtp_stream *s, static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
struct snd_pcm_runtime *runtime, __be32 *buffer, unsigned int data_blocks,
__be32 *buffer, unsigned int data_blocks) unsigned int pcm_frames)
{ {
struct amdtp_motu *p = s->protocol; 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; u8 *byte;
u32 *dst; 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 + dst = (void *)runtime->dma_area +
frames_to_bytes(runtime, s->pcm_buffer_pointer); frames_to_bytes(runtime, pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
for (i = 0; i < data_blocks; ++i) { for (i = 0; i < data_blocks; ++i) {
byte = (u8 *)buffer + p->pcm_byte_offset; 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, static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
struct snd_pcm_runtime *runtime, __be32 *buffer, unsigned int data_blocks,
__be32 *buffer, unsigned int data_blocks) unsigned int pcm_frames)
{ {
struct amdtp_motu *p = s->protocol; 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; u8 *byte;
const u32 *src; 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 + src = (void *)runtime->dma_area +
frames_to_bytes(runtime, s->pcm_buffer_pointer); frames_to_bytes(runtime, pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
for (i = 0; i < data_blocks; ++i) { for (i = 0; i < data_blocks; ++i) {
byte = (u8 *)buffer + p->pcm_byte_offset; 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, static void probe_tracepoints_events(struct amdtp_stream *s,
__be32 *buffer, unsigned int data_blocks, const struct pkt_desc *descs,
unsigned int *syt) 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 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); // For data block processing.
trace_data_block_message(s, data_blocks, buffer); 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) if (pcm) {
read_midi_messages(s, buffer, data_blocks); read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
pcm_frames += data_blocks;
}
pcm = READ_ONCE(s->pcm); if (p->midi_ports)
if (data_blocks > 0 && pcm) read_midi_messages(s, buf, data_blocks);
read_pcm_s32(s, pcm->runtime, buffer, 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) 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, static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
__be32 *buffer, unsigned int data_blocks, const struct pkt_desc *descs,
unsigned int *syt) unsigned int packets,
struct snd_pcm_substream *pcm)
{ {
struct amdtp_motu *p = (struct amdtp_motu *)s->protocol; struct amdtp_motu *p = s->protocol;
struct snd_pcm_substream *pcm; unsigned int pcm_frames = 0;
int i;
/* Not used. */ // For data block processing.
*syt = 0xffff; 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) if (p->midi_ports)
write_midi_messages(s, buffer, data_blocks); write_midi_messages(s, buf, data_blocks);
pcm = READ_ONCE(s->pcm); // TODO: how to interact control messages between userspace?
if (pcm)
write_pcm_s32(s, pcm->runtime, buffer, data_blocks);
else
write_pcm_silence(s, buffer, data_blocks);
write_sph(s, buffer, data_blocks); write_sph(s, buf, data_blocks);
}
trace_data_block_sph(s, data_blocks, buffer); // For tracepoints.
trace_data_block_message(s, data_blocks, buffer); 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, int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
enum amdtp_stream_direction dir, enum amdtp_stream_direction dir,
const struct snd_motu_protocol *const protocol) 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 fmt = CIP_FMT_MOTU;
int flags = CIP_BLOCKING; int flags = CIP_BLOCKING;
int err; int err;
if (dir == AMDTP_IN_STREAM) { 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 * 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; CIP_SKIP_DBC_ZERO_CHECK;
} }
} else { } else {
process_data_blocks = process_rx_data_blocks; process_ctx_payloads = process_it_ctx_payloads;
flags |= CIP_DBC_IS_END_EVENT; 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)); sizeof(struct amdtp_motu));
if (err < 0) if (err < 0)
return err; return err;
s->sph = 1; 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; return 0;
} }

View File

@ -92,9 +92,6 @@ static void finish_session(struct snd_motu *motu)
if (err < 0) if (err < 0)
return; return;
amdtp_stream_stop(&motu->tx_stream);
amdtp_stream_stop(&motu->rx_stream);
err = snd_motu_transaction_read(motu, ISOC_COMM_CONTROL_OFFSET, &reg, err = snd_motu_transaction_read(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
sizeof(reg)); sizeof(reg));
if (err < 0) if (err < 0)
@ -109,27 +106,6 @@ static void finish_session(struct snd_motu *motu)
sizeof(reg)); 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 snd_motu_stream_cache_packet_formats(struct snd_motu *motu)
{ {
int err; int err;
@ -169,6 +145,7 @@ int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate)
rate = curr_rate; rate = curr_rate;
if (motu->substreams_counter == 0 || curr_rate != rate) { if (motu->substreams_counter == 0 || curr_rate != rate) {
amdtp_domain_stop(&motu->domain);
finish_session(motu); finish_session(motu);
fw_iso_resources_free(&motu->tx_resources); fw_iso_resources_free(&motu->tx_resources);
@ -234,8 +211,10 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu)
return 0; return 0;
if (amdtp_streaming_error(&motu->rx_stream) || 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); finish_session(motu);
}
if (generation != fw_parent_device(motu->unit)->card->generation) { if (generation != fw_parent_device(motu->unit)->card->generation) {
err = fw_iso_resources_update(&motu->rx_resources); 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)) { if (!amdtp_stream_running(&motu->rx_stream)) {
int spd = fw_parent_device(motu->unit)->max_speed;
err = ensure_packet_formats(motu); err = ensure_packet_formats(motu);
if (err < 0) if (err < 0)
return err; return err;
@ -259,10 +240,25 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu)
goto stop_streams; goto stop_streams;
} }
err = start_isoc_ctx(motu, &motu->rx_stream); err = amdtp_domain_add_stream(&motu->domain, &motu->tx_stream,
if (err < 0) { motu->tx_resources.channel, spd);
dev_err(&motu->unit->device, if (err < 0)
"fail to start IT context: %d\n", err); 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; 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; return 0;
stop_streams: stop_streams:
amdtp_domain_stop(&motu->domain);
finish_session(motu); finish_session(motu);
return err; return err;
} }
@ -293,6 +281,7 @@ stop_streams:
void snd_motu_stream_stop_duplex(struct snd_motu *motu) void snd_motu_stream_stop_duplex(struct snd_motu *motu)
{ {
if (motu->substreams_counter == 0) { if (motu->substreams_counter == 0) {
amdtp_domain_stop(&motu->domain);
finish_session(motu); finish_session(motu);
fw_iso_resources_free(&motu->tx_resources); 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; struct fw_iso_resources *resources;
enum amdtp_stream_direction dir;
int err;
if (dir == AMDTP_IN_STREAM) { if (s == &motu->tx_stream) {
stream = &motu->tx_stream;
resources = &motu->tx_resources; resources = &motu->tx_resources;
dir = AMDTP_IN_STREAM;
} else { } else {
stream = &motu->rx_stream;
resources = &motu->rx_resources; resources = &motu->rx_resources;
dir = AMDTP_OUT_STREAM;
} }
err = fw_iso_resources_init(resources, motu->unit); err = fw_iso_resources_init(resources, motu->unit);
if (err < 0) if (err < 0)
return err; return err;
err = amdtp_motu_init(stream, motu->unit, dir, motu->spec->protocol); err = amdtp_motu_init(s, motu->unit, dir, motu->spec->protocol);
if (err < 0) { if (err < 0)
amdtp_stream_destroy(stream);
fw_iso_resources_destroy(resources); fw_iso_resources_destroy(resources);
}
return err; return err;
} }
static void destroy_stream(struct snd_motu *motu, static void destroy_stream(struct snd_motu *motu, struct amdtp_stream *s)
enum amdtp_stream_direction dir)
{ {
struct amdtp_stream *stream; amdtp_stream_destroy(s);
struct fw_iso_resources *resources;
if (dir == AMDTP_IN_STREAM) { if (s == &motu->tx_stream)
stream = &motu->tx_stream; fw_iso_resources_destroy(&motu->tx_resources);
resources = &motu->tx_resources; else
} else { fw_iso_resources_destroy(&motu->rx_resources);
stream = &motu->rx_stream;
resources = &motu->rx_resources;
}
amdtp_stream_destroy(stream);
fw_iso_resources_destroy(resources);
} }
int snd_motu_stream_init_duplex(struct snd_motu *motu) int snd_motu_stream_init_duplex(struct snd_motu *motu)
{ {
int err; int err;
err = init_stream(motu, AMDTP_IN_STREAM); err = init_stream(motu, &motu->tx_stream);
if (err < 0) if (err < 0)
return err; return err;
err = init_stream(motu, AMDTP_OUT_STREAM); err = init_stream(motu, &motu->rx_stream);
if (err < 0) if (err < 0) {
destroy_stream(motu, AMDTP_IN_STREAM); 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; return err;
} }
/* // This function should be called before starting streams or after stopping
* This function should be called before starting streams or after stopping // streams.
* streams.
*/
void snd_motu_stream_destroy_duplex(struct snd_motu *motu) void snd_motu_stream_destroy_duplex(struct snd_motu *motu)
{ {
destroy_stream(motu, AMDTP_IN_STREAM); amdtp_domain_destroy(&motu->domain);
destroy_stream(motu, AMDTP_OUT_STREAM);
destroy_stream(motu, &motu->rx_stream);
destroy_stream(motu, &motu->tx_stream);
motu->substreams_counter = 0; motu->substreams_counter = 0;
} }

View File

@ -247,6 +247,17 @@ static const struct snd_motu_spec motu_audio_express = {
.analog_out_ports = 4, .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) \ #define SND_MOTU_DEV_ENTRY(model, data) \
{ \ { \
.match_flags = IEEE1394_MATCH_VENDOR_ID | \ .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(0x000015, &motu_828mk3), /* FireWire only. */
SND_MOTU_DEV_ENTRY(0x000035, &motu_828mk3), /* Hybrid. */ SND_MOTU_DEV_ENTRY(0x000035, &motu_828mk3), /* Hybrid. */
SND_MOTU_DEV_ENTRY(0x000033, &motu_audio_express), SND_MOTU_DEV_ENTRY(0x000033, &motu_audio_express),
SND_MOTU_DEV_ENTRY(0x000045, &motu_4pre),
{ } { }
}; };
MODULE_DEVICE_TABLE(ieee1394, motu_id_table); MODULE_DEVICE_TABLE(ieee1394, motu_id_table);

View File

@ -69,6 +69,8 @@ struct snd_motu {
int dev_lock_count; int dev_lock_count;
bool dev_lock_changed; bool dev_lock_changed;
wait_queue_head_t hwdep_wait; wait_queue_head_t hwdep_wait;
struct amdtp_domain domain;
}; };
enum snd_motu_spec_flags { enum snd_motu_spec_flags {

View File

@ -114,19 +114,13 @@ static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
if (err < 0) if (err < 0)
return err; 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) { if (err < 0) {
cmp_connection_break(conn); cmp_connection_break(conn);
return err; 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; return 0;
} }
@ -280,12 +274,12 @@ int snd_oxfw_stream_reserve_duplex(struct snd_oxfw *oxfw,
pcm_channels = formation.pcm; pcm_channels = formation.pcm;
} }
if (formation.rate != rate || formation.pcm != pcm_channels) { 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_break(&oxfw->in_conn);
cmp_connection_release(&oxfw->in_conn); cmp_connection_release(&oxfw->in_conn);
if (oxfw->has_output) { if (oxfw->has_output) {
amdtp_stream_stop(&oxfw->tx_stream);
cmp_connection_break(&oxfw->out_conn); cmp_connection_break(&oxfw->out_conn);
cmp_connection_release(&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) || if (amdtp_streaming_error(&oxfw->rx_stream) ||
amdtp_streaming_error(&oxfw->tx_stream)) { amdtp_streaming_error(&oxfw->tx_stream)) {
amdtp_stream_stop(&oxfw->rx_stream); amdtp_domain_stop(&oxfw->domain);
cmp_connection_break(&oxfw->in_conn);
if (oxfw->has_output) { cmp_connection_break(&oxfw->in_conn);
amdtp_stream_stop(&oxfw->tx_stream); if (oxfw->has_output)
cmp_connection_break(&oxfw->out_conn); cmp_connection_break(&oxfw->out_conn);
}
} }
if (!amdtp_stream_running(&oxfw->rx_stream)) { if (!amdtp_stream_running(&oxfw->rx_stream)) {
err = start_stream(oxfw, &oxfw->rx_stream); err = start_stream(oxfw, &oxfw->rx_stream);
if (err < 0) { if (err < 0) {
dev_err(&oxfw->unit->device, dev_err(&oxfw->unit->device,
"fail to start rx stream: %d\n", err); "fail to prepare rx stream: %d\n", err);
goto error; goto error;
} }
}
if (oxfw->has_output) { if (oxfw->has_output &&
if (!amdtp_stream_running(&oxfw->tx_stream)) { !amdtp_stream_running(&oxfw->tx_stream)) {
err = start_stream(oxfw, &oxfw->tx_stream); err = start_stream(oxfw, &oxfw->tx_stream);
if (err < 0) { if (err < 0) {
dev_err(&oxfw->unit->device, 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; goto error;
} }
} }
@ -356,24 +366,24 @@ int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw)
return 0; return 0;
error: error:
amdtp_stream_stop(&oxfw->rx_stream); amdtp_domain_stop(&oxfw->domain);
cmp_connection_break(&oxfw->in_conn); cmp_connection_break(&oxfw->in_conn);
if (oxfw->has_output) { if (oxfw->has_output)
amdtp_stream_stop(&oxfw->tx_stream);
cmp_connection_break(&oxfw->out_conn); cmp_connection_break(&oxfw->out_conn);
}
return err; return err;
} }
void snd_oxfw_stream_stop_duplex(struct snd_oxfw *oxfw) void snd_oxfw_stream_stop_duplex(struct snd_oxfw *oxfw)
{ {
if (oxfw->substreams_count == 0) { if (oxfw->substreams_count == 0) {
amdtp_stream_stop(&oxfw->rx_stream); amdtp_domain_stop(&oxfw->domain);
cmp_connection_break(&oxfw->in_conn); cmp_connection_break(&oxfw->in_conn);
cmp_connection_release(&oxfw->in_conn); cmp_connection_release(&oxfw->in_conn);
if (oxfw->has_output) { if (oxfw->has_output) {
amdtp_stream_stop(&oxfw->tx_stream);
cmp_connection_break(&oxfw->out_conn); cmp_connection_break(&oxfw->out_conn);
cmp_connection_release(&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 // This function should be called before starting the stream or after stopping
// the streams. // the streams.
void snd_oxfw_stream_destroy_duplex(struct snd_oxfw *oxfw) void snd_oxfw_stream_destroy_duplex(struct snd_oxfw *oxfw)
{ {
amdtp_domain_destroy(&oxfw->domain);
destroy_stream(oxfw, &oxfw->rx_stream); destroy_stream(oxfw, &oxfw->rx_stream);
if (oxfw->has_output) 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) 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); cmp_connection_break(&oxfw->in_conn);
amdtp_stream_pcm_abort(&oxfw->rx_stream); amdtp_stream_pcm_abort(&oxfw->rx_stream);
if (oxfw->has_output) { if (oxfw->has_output) {
amdtp_stream_stop(&oxfw->tx_stream);
cmp_connection_break(&oxfw->out_conn); cmp_connection_break(&oxfw->out_conn);
amdtp_stream_pcm_abort(&oxfw->tx_stream); amdtp_stream_pcm_abort(&oxfw->tx_stream);

View File

@ -63,6 +63,8 @@ struct snd_oxfw {
const struct ieee1394_device_id *entry; const struct ieee1394_device_id *entry;
void *spec; void *spec;
struct amdtp_domain domain;
}; };
/* /*

View File

@ -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); return amdtp_stream_set_parameters(s, rate, data_channels);
} }
static void write_pcm_s32(struct amdtp_stream *s, static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
struct snd_pcm_substream *pcm, __be32 *buffer, unsigned int frames,
__be32 *buffer, unsigned int frames) unsigned int pcm_frames)
{ {
struct amdtp_tscm *p = s->protocol; struct amdtp_tscm *p = s->protocol;
unsigned int channels = p->pcm_channels;
struct snd_pcm_runtime *runtime = pcm->runtime; 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; 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 + src = (void *)runtime->dma_area +
frames_to_bytes(runtime, s->pcm_buffer_pointer); frames_to_bytes(runtime, pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
for (i = 0; i < frames; ++i) { for (i = 0; i < frames; ++i) {
for (c = 0; c < channels; ++c) { 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, static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
struct snd_pcm_substream *pcm, __be32 *buffer, unsigned int frames,
__be32 *buffer, unsigned int frames) unsigned int pcm_frames)
{ {
struct amdtp_tscm *p = s->protocol; struct amdtp_tscm *p = s->protocol;
unsigned int channels = p->pcm_channels;
struct snd_pcm_runtime *runtime = pcm->runtime; struct snd_pcm_runtime *runtime = pcm->runtime;
unsigned int channels, remaining_frames, i, c; unsigned int pcm_buffer_pointer;
int remaining_frames;
u32 *dst; 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 + dst = (void *)runtime->dma_area +
frames_to_bytes(runtime, s->pcm_buffer_pointer); frames_to_bytes(runtime, pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
/* The first data channel is for event counter. */ /* The first data channel is for event counter. */
buffer += 1; 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, static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
__be32 *buffer, const struct pkt_desc *descs,
unsigned int data_blocks, unsigned int packets,
unsigned int *syt) struct snd_pcm_substream *pcm)
{ {
struct snd_pcm_substream *pcm; unsigned int pcm_frames = 0;
int i;
pcm = READ_ONCE(s->pcm); for (i = 0; i < packets; ++i) {
if (data_blocks > 0 && pcm) const struct pkt_desc *desc = descs + i;
read_pcm_s32(s, pcm, buffer, data_blocks); __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, static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
__be32 *buffer, const struct pkt_desc *descs,
unsigned int data_blocks, unsigned int packets,
unsigned int *syt) struct snd_pcm_substream *pcm)
{ {
struct snd_pcm_substream *pcm; unsigned int pcm_frames = 0;
int i;
/* This field is not used. */ for (i = 0; i < packets; ++i) {
*syt = 0x0000; 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) {
if (pcm) write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
write_pcm_s32(s, pcm, buffer, data_blocks); pcm_frames += data_blocks;
else } else {
write_pcm_silence(s, buffer, data_blocks); 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, int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit,
enum amdtp_stream_direction dir, unsigned int pcm_channels) 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; struct amdtp_tscm *p;
unsigned int fmt; unsigned int fmt;
int err; int err;
if (dir == AMDTP_IN_STREAM) { if (dir == AMDTP_IN_STREAM) {
fmt = AMDTP_FMT_TSCM_TX; fmt = AMDTP_FMT_TSCM_TX;
process_data_blocks = process_tx_data_blocks; process_ctx_payloads = process_ir_ctx_payloads;
} else { } else {
fmt = AMDTP_FMT_TSCM_RX; 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, err = amdtp_stream_init(s, unit, dir,
CIP_NONBLOCKING | CIP_SKIP_DBC_ZERO_CHECK, fmt, CIP_NONBLOCKING | CIP_SKIP_DBC_ZERO_CHECK, fmt,
process_data_blocks, sizeof(struct amdtp_tscm)); process_ctx_payloads, sizeof(struct amdtp_tscm));
if (err < 0) if (err < 0)
return 0; return 0;
/* Use fixed value for FDF field. */ if (dir == AMDTP_OUT_STREAM) {
s->ctx_data.rx.fdf = 0x00; // 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. */ /* This protocol uses fixed number of data channels for PCM samples. */
p = s->protocol; p = s->protocol;

View File

@ -56,6 +56,9 @@ static int pcm_open(struct snd_pcm_substream *substream)
goto err_locked; goto err_locked;
err = snd_tscm_stream_get_clock(tscm, &clock); err = snd_tscm_stream_get_clock(tscm, &clock);
if (err < 0)
goto err_locked;
if (clock != SND_TSCM_CLOCK_INTERNAL || if (clock != SND_TSCM_CLOCK_INTERNAL ||
amdtp_stream_pcm_running(&tscm->rx_stream) || amdtp_stream_pcm_running(&tscm->rx_stream) ||
amdtp_stream_pcm_running(&tscm->tx_stream)) { amdtp_stream_pcm_running(&tscm->tx_stream)) {

View File

@ -8,20 +8,37 @@
#include <linux/delay.h> #include <linux/delay.h>
#include "tascam.h" #include "tascam.h"
#define CLOCK_STATUS_MASK 0xffff0000
#define CLOCK_CONFIG_MASK 0x0000ffff
#define CALLBACK_TIMEOUT 500 #define CALLBACK_TIMEOUT 500
static int get_clock(struct snd_tscm *tscm, u32 *data) static int get_clock(struct snd_tscm *tscm, u32 *data)
{ {
int trial = 0;
__be32 reg; __be32 reg;
int err; int err;
err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST, while (trial++ < 5) {
TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS, err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
&reg, sizeof(reg), 0); TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS,
if (err >= 0) &reg, sizeof(reg), 0);
*data = be32_to_cpu(reg); 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, 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); err = get_clock(tscm, &data);
if (err < 0) if (err < 0)
return err; return err;
data &= 0x0000ffff; data &= CLOCK_CONFIG_MASK;
if (rate > 0) { if (rate > 0) {
data &= 0x000000ff; 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) int snd_tscm_stream_get_rate(struct snd_tscm *tscm, unsigned int *rate)
{ {
u32 data = 0x0; u32 data;
unsigned int trials = 0;
int err; int err;
while (data == 0x0 || trials++ < 5) { err = get_clock(tscm, &data);
err = get_clock(tscm, &data); if (err < 0)
if (err < 0) return err;
return err;
data = (data & 0xff000000) >> 24; data = (data & 0xff000000) >> 24;
}
/* Check base rate. */ /* Check base rate. */
if ((data & 0x0f) == 0x01) if ((data & 0x0f) == 0x01)
@ -180,9 +194,6 @@ static void finish_session(struct snd_tscm *tscm)
{ {
__be32 reg; __be32 reg;
amdtp_stream_stop(&tscm->rx_stream);
amdtp_stream_stop(&tscm->tx_stream);
reg = 0; reg = 0;
snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING, 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); 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; unsigned int pcm_channels;
int err; int err;
/* For out-stream. */ if (s == &tscm->tx_stream) {
err = fw_iso_resources_init(&tscm->rx_resources, tscm->unit); resources = &tscm->tx_resources;
if (err < 0) dir = AMDTP_IN_STREAM;
return err; pcm_channels = tscm->spec->pcm_capture_analog_channels;
pcm_channels = tscm->spec->pcm_playback_analog_channels; } else {
resources = &tscm->rx_resources;
dir = AMDTP_OUT_STREAM;
pcm_channels = tscm->spec->pcm_playback_analog_channels;
}
if (tscm->spec->has_adat) if (tscm->spec->has_adat)
pcm_channels += 8; pcm_channels += 8;
if (tscm->spec->has_spdif) if (tscm->spec->has_spdif)
pcm_channels += 2; 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) if (err < 0)
return err; return err;
/* For in-stream. */ err = amdtp_tscm_init(s, tscm->unit, dir, pcm_channels);
err = fw_iso_resources_init(&tscm->tx_resources, tscm->unit); 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) if (err < 0)
return err; return err;
pcm_channels = tscm->spec->pcm_capture_analog_channels;
if (tscm->spec->has_adat) err = init_stream(tscm, &tscm->rx_stream);
pcm_channels += 8; if (err < 0) {
if (tscm->spec->has_spdif) destroy_stream(tscm, &tscm->tx_stream);
pcm_channels += 2; return err;
err = amdtp_tscm_init(&tscm->tx_stream, tscm->unit, AMDTP_IN_STREAM, }
pcm_channels);
if (err < 0) err = amdtp_domain_init(&tscm->domain);
amdtp_stream_destroy(&tscm->rx_stream); if (err < 0) {
destroy_stream(tscm, &tscm->tx_stream);
destroy_stream(tscm, &tscm->rx_stream);
}
return err; 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. // At bus reset, streaming is stopped and some registers are clear.
void snd_tscm_stream_update_duplex(struct snd_tscm *tscm) void snd_tscm_stream_update_duplex(struct snd_tscm *tscm)
{ {
amdtp_stream_pcm_abort(&tscm->tx_stream); amdtp_domain_stop(&tscm->domain);
amdtp_stream_stop(&tscm->tx_stream);
amdtp_stream_pcm_abort(&tscm->tx_stream);
amdtp_stream_pcm_abort(&tscm->rx_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
* This function should be called before starting streams or after stopping // streams.
* streams.
*/
void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm) void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm)
{ {
amdtp_stream_destroy(&tscm->rx_stream); amdtp_domain_destroy(&tscm->domain);
amdtp_stream_destroy(&tscm->tx_stream);
fw_iso_resources_destroy(&tscm->rx_resources); destroy_stream(tscm, &tscm->rx_stream);
fw_iso_resources_destroy(&tscm->tx_resources); destroy_stream(tscm, &tscm->tx_stream);
} }
int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate) 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; return err;
if (tscm->substreams_counter == 0 || rate != curr_rate) { if (tscm->substreams_counter == 0 || rate != curr_rate) {
amdtp_domain_stop(&tscm->domain);
finish_session(tscm); finish_session(tscm);
fw_iso_resources_free(&tscm->tx_resources); 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; return 0;
if (amdtp_streaming_error(&tscm->rx_stream) || 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); finish_session(tscm);
}
if (generation != fw_parent_device(tscm->unit)->card->generation) { if (generation != fw_parent_device(tscm->unit)->card->generation) {
err = fw_iso_resources_update(&tscm->tx_resources); 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)) { if (!amdtp_stream_running(&tscm->rx_stream)) {
int spd = fw_parent_device(tscm->unit)->max_speed;
err = set_stream_formats(tscm, rate); err = set_stream_formats(tscm, rate);
if (err < 0) if (err < 0)
goto error; goto error;
@ -410,27 +453,23 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
if (err < 0) if (err < 0)
goto error; goto error;
err = amdtp_stream_start(&tscm->rx_stream, err = amdtp_domain_add_stream(&tscm->domain, &tscm->rx_stream,
tscm->rx_resources.channel, tscm->rx_resources.channel, spd);
fw_parent_device(tscm->unit)->max_speed);
if (err < 0) if (err < 0)
goto error; 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, if (!amdtp_stream_wait_callback(&tscm->rx_stream,
CALLBACK_TIMEOUT)) { CALLBACK_TIMEOUT) ||
err = -ETIMEDOUT; !amdtp_stream_wait_callback(&tscm->tx_stream,
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)) { CALLBACK_TIMEOUT)) {
err = -ETIMEDOUT; err = -ETIMEDOUT;
goto error; goto error;
@ -439,6 +478,7 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
return 0; return 0;
error: error:
amdtp_domain_stop(&tscm->domain);
finish_session(tscm); finish_session(tscm);
return err; return err;
@ -447,6 +487,7 @@ error:
void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm) void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm)
{ {
if (tscm->substreams_counter == 0) { if (tscm->substreams_counter == 0) {
amdtp_domain_stop(&tscm->domain);
finish_session(tscm); finish_session(tscm);
fw_iso_resources_free(&tscm->tx_resources); fw_iso_resources_free(&tscm->tx_resources);

View File

@ -39,6 +39,9 @@ static const struct snd_tscm_spec model_specs[] = {
.midi_capture_ports = 2, .midi_capture_ports = 2,
.midi_playback_ports = 4, .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) 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, .vendor_id = 0x00022e,
.specifier_id = 0x00022e, .specifier_id = 0x00022e,
}, },
/* FE-08 requires reverse-engineering because it just has faders. */
{} {}
}; };
MODULE_DEVICE_TABLE(ieee1394, snd_tscm_id_table); MODULE_DEVICE_TABLE(ieee1394, snd_tscm_id_table);

View File

@ -97,6 +97,8 @@ struct snd_tscm {
struct snd_firewire_tascam_change queue[SND_TSCM_QUEUE_COUNT]; struct snd_firewire_tascam_change queue[SND_TSCM_QUEUE_COUNT];
unsigned int pull_pos; unsigned int pull_pos;
unsigned int push_pos; unsigned int push_pos;
struct amdtp_domain domain;
}; };
#define TSCM_ADDR_BASE 0xffff00000000ull #define TSCM_ADDR_BASE 0xffff00000000ull
@ -127,6 +129,26 @@ struct snd_tscm {
#define TSCM_OFFSET_MIDI_RX_QUAD 0x4000 #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 { enum snd_tscm_clock {
SND_TSCM_CLOCK_INTERNAL = 0, SND_TSCM_CLOCK_INTERNAL = 0,
SND_TSCM_CLOCK_WORD = 1, SND_TSCM_CLOCK_WORD = 1,

View File

@ -6,6 +6,9 @@ config SND_HDA_CORE
config SND_HDA_DSP_LOADER config SND_HDA_DSP_LOADER
bool bool
config SND_HDA_ALIGNED_MMIO
bool
config SND_HDA_COMPONENT config SND_HDA_COMPONENT
bool bool
@ -29,3 +32,8 @@ config SND_HDA_PREALLOC_SIZE
Note that the pre-allocation size can be changed dynamically Note that the pre-allocation size can be changed dynamically
via a proc file (/proc/asound/card*/pcm*/sub*/prealloc), too. 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.

View File

@ -13,3 +13,6 @@ obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o
#extended hda #extended hda
obj-$(CONFIG_SND_HDA_EXT_CORE) += ext/ obj-$(CONFIG_SND_HDA_EXT_CORE) += ext/
snd-intel-nhlt-objs := intel-nhlt.o
obj-$(CONFIG_SND_INTEL_NHLT) += snd-intel-nhlt.o

View File

@ -17,80 +17,22 @@
MODULE_DESCRIPTION("HDA extended core"); MODULE_DESCRIPTION("HDA extended core");
MODULE_LICENSE("GPL v2"); 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 * snd_hdac_ext_bus_init - initialize a HD-audio extended bus
* @ebus: the pointer to extended bus object * @ebus: the pointer to extended bus object
* @dev: device pointer * @dev: device pointer
* @ops: bus verb operators * @ops: bus verb operators
* @io_ops: lowlevel I/O operators, can be NULL. If NULL core will use
* default ops * default ops
* *
* Returns 0 if successful, or a negative error code. * Returns 0 if successful, or a negative error code.
*/ */
int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev, int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev,
const struct hdac_bus_ops *ops, const struct hdac_bus_ops *ops,
const struct hdac_io_ops *io_ops,
const struct hdac_ext_bus_ops *ext_ops) const struct hdac_ext_bus_ops *ext_ops)
{ {
int ret; int ret;
/* check if io ops are provided, if not load the defaults */ ret = snd_hdac_bus_init(bus, dev, ops);
if (io_ops == NULL)
io_ops = &hdac_ext_default_io;
ret = snd_hdac_bus_init(bus, dev, ops, io_ops);
if (ret < 0) if (ret < 0)
return ret; return ret;

View File

@ -4,12 +4,16 @@
*/ */
#include <linux/init.h> #include <linux/init.h>
#include <linux/io.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/export.h> #include <linux/export.h>
#include <sound/hdaudio.h> #include <sound/hdaudio.h>
#include "local.h"
#include "trace.h" #include "trace.h"
static void snd_hdac_bus_process_unsol_events(struct work_struct *work);
static const struct hdac_bus_ops default_ops = { static const struct hdac_bus_ops default_ops = {
.command = snd_hdac_bus_send_cmd, .command = snd_hdac_bus_send_cmd,
.get_response = snd_hdac_bus_get_response, .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 * snd_hdac_bus_init - initialize a HD-audio bas bus
* @bus: the pointer to bus object * @bus: the pointer to bus object
* @ops: bus verb operators * @ops: bus verb operators
* @io_ops: lowlevel I/O operators
* *
* Returns 0 if successful, or a negative error code. * Returns 0 if successful, or a negative error code.
*/ */
int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev, int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
const struct hdac_bus_ops *ops, const struct hdac_bus_ops *ops)
const struct hdac_io_ops *io_ops)
{ {
memset(bus, 0, sizeof(*bus)); memset(bus, 0, sizeof(*bus));
bus->dev = dev; bus->dev = dev;
@ -33,7 +35,7 @@ int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
bus->ops = ops; bus->ops = ops;
else else
bus->ops = &default_ops; 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->stream_list);
INIT_LIST_HEAD(&bus->codec_list); INIT_LIST_HEAD(&bus->codec_list);
INIT_WORK(&bus->unsol_work, snd_hdac_bus_process_unsol_events); 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 * 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_bus *bus = container_of(work, struct hdac_bus, unsol_work);
struct hdac_device *codec; struct hdac_device *codec;
@ -172,7 +174,6 @@ void snd_hdac_bus_process_unsol_events(struct work_struct *work)
drv->unsol_event(codec, res); drv->unsol_event(codec, res);
} }
} }
EXPORT_SYMBOL_GPL(snd_hdac_bus_process_unsol_events);
/** /**
* snd_hdac_bus_add_device - Add a codec to bus * 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++; bus->num_codecs++;
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(snd_hdac_bus_add_device);
/** /**
* snd_hdac_bus_remove_device - Remove a codec from bus * 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--; bus->num_codecs--;
flush_work(&bus->unsol_work); 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 */

View File

@ -447,6 +447,8 @@ static void azx_int_disable(struct hdac_bus *bus)
list_for_each_entry(azx_dev, &bus->stream_list, list) list_for_each_entry(azx_dev, &bus->stream_list, list)
snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_INT_MASK, 0); snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_INT_MASK, 0);
synchronize_irq(bus->irq);
/* disable SIE for all streams */ /* disable SIE for all streams */
snd_hdac_chip_writeb(bus, INTCTL, 0); 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; struct hdac_stream *s;
int num_streams = 0; int num_streams = 0;
int dma_type = bus->dma_type ? bus->dma_type : SNDRV_DMA_TYPE_DEV;
int err; int err;
list_for_each_entry(s, &bus->stream_list, list) { list_for_each_entry(s, &bus->stream_list, list) {
/* allocate memory for the BDL for each stream */ /* allocate memory for the BDL for each stream */
err = bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV, err = snd_dma_alloc_pages(dma_type, bus->dev,
BDL_SIZE, &s->bdl); BDL_SIZE, &s->bdl);
num_streams++; num_streams++;
if (err < 0) if (err < 0)
return -ENOMEM; return -ENOMEM;
@ -589,16 +592,15 @@ int snd_hdac_bus_alloc_stream_pages(struct hdac_bus *bus)
if (WARN_ON(!num_streams)) if (WARN_ON(!num_streams))
return -EINVAL; return -EINVAL;
/* allocate memory for the position buffer */ /* allocate memory for the position buffer */
err = bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV, err = snd_dma_alloc_pages(dma_type, bus->dev,
num_streams * 8, &bus->posbuf); num_streams * 8, &bus->posbuf);
if (err < 0) if (err < 0)
return -ENOMEM; return -ENOMEM;
list_for_each_entry(s, &bus->stream_list, list) list_for_each_entry(s, &bus->stream_list, list)
s->posbuf = (__le32 *)(bus->posbuf.area + s->index * 8); s->posbuf = (__le32 *)(bus->posbuf.area + s->index * 8);
/* single page (at least 4096 bytes) must suffice for both ringbuffes */ /* single page (at least 4096 bytes) must suffice for both ringbuffes */
return bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV, return snd_dma_alloc_pages(dma_type, bus->dev, PAGE_SIZE, &bus->rb);
PAGE_SIZE, &bus->rb);
} }
EXPORT_SYMBOL_GPL(snd_hdac_bus_alloc_stream_pages); 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) { list_for_each_entry(s, &bus->stream_list, list) {
if (s->bdl.area) if (s->bdl.area)
bus->io_ops->dma_free_pages(bus, &s->bdl); snd_dma_free_pages(&s->bdl);
} }
if (bus->rb.area) if (bus->rb.area)
bus->io_ops->dma_free_pages(bus, &bus->rb); snd_dma_free_pages(&bus->rb);
if (bus->posbuf.area) 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); EXPORT_SYMBOL_GPL(snd_hdac_bus_free_stream_pages);

View File

@ -218,8 +218,8 @@ EXPORT_SYMBOL_GPL(snd_hdac_codec_modalias);
* *
* Return an encoded command verb or -1 for error. * Return an encoded command verb or -1 for error.
*/ */
unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid, static unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid,
unsigned int verb, unsigned int parm) unsigned int verb, unsigned int parm)
{ {
u32 val, addr; u32 val, addr;
@ -237,7 +237,6 @@ unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid,
val |= parm; val |= parm;
return val; return val;
} }
EXPORT_SYMBOL_GPL(snd_hdac_make_cmd);
/** /**
* snd_hdac_exec_verb - execute an encoded verb * 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 codec->exec_verb(codec, cmd, flags, res);
return snd_hdac_bus_exec_verb(codec->bus, codec->addr, cmd, res); return snd_hdac_bus_exec_verb(codec->bus, codec->addr, cmd, res);
} }
EXPORT_SYMBOL_GPL(snd_hdac_exec_verb);
/** /**

View File

@ -21,6 +21,7 @@
#include <sound/core.h> #include <sound/core.h>
#include <sound/hdaudio.h> #include <sound/hdaudio.h>
#include <sound/hda_regmap.h> #include <sound/hda_regmap.h>
#include "local.h"
static int codec_pm_lock(struct hdac_device *codec) static int codec_pm_lock(struct hdac_device *codec)
{ {

View File

@ -229,11 +229,7 @@ int snd_hdac_stream_setup(struct hdac_stream *azx_dev)
/* set the interrupt enable bits in the descriptor control register */ /* set the interrupt enable bits in the descriptor control register */
snd_hdac_stream_updatel(azx_dev, SD_CTL, 0, SD_INT_MASK); 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;
azx_dev->fifo_size =
snd_hdac_stream_readw(azx_dev, SD_FIFOSIZE) + 1;
else
azx_dev->fifo_size = 0;
/* when LPIB delay correction gives a small negative value, /* when LPIB delay correction gives a small negative value,
* we ignore it; currently set the threshold statically to * 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; azx_dev->locked = true;
spin_unlock_irq(&bus->reg_lock); spin_unlock_irq(&bus->reg_lock);
err = bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV_SG, err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, bus->dev,
byte_size, bufp); byte_size, bufp);
if (err < 0) if (err < 0)
goto err_alloc; 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; return azx_dev->stream_tag;
error: error:
bus->io_ops->dma_free_pages(bus, bufp); snd_dma_free_pages(bufp);
err_alloc: err_alloc:
spin_lock_irq(&bus->reg_lock); spin_lock_irq(&bus->reg_lock);
azx_dev->locked = false; 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->period_bytes = 0;
azx_dev->format_val = 0; azx_dev->format_val = 0;
bus->io_ops->dma_free_pages(bus, dmab); snd_dma_free_pages(dmab);
dmab->area = NULL; dmab->area = NULL;
spin_lock_irq(&bus->reg_lock); spin_lock_irq(&bus->reg_lock);

View File

@ -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");

View File

@ -33,4 +33,11 @@ int hda_widget_sysfs_reinit(struct hdac_device *codec, hda_nid_t start_nid,
int num_nodes); int num_nodes);
void hda_widget_sysfs_exit(struct hdac_device *codec); 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 */ #endif /* __HDAC_LOCAL_H */

View File

@ -775,11 +775,12 @@ static int build_adc_controls(struct snd_akm4xxx *ak)
return err; return err;
memset(&knew, 0, sizeof(knew)); memset(&knew, 0, sizeof(knew));
knew.name = ak->adc_info[mixer_ch].selector_name; if (!ak->adc_info ||
if (!knew.name) { !ak->adc_info[mixer_ch].selector_name) {
knew.name = "Capture Channel"; knew.name = "Capture Channel";
knew.index = mixer_ch + ak->idx_offset * 2; 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.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
knew.info = ak4xxx_capture_source_info; knew.info = ak4xxx_capture_source_info;

View File

@ -80,7 +80,7 @@ int snd_sbdsp_reset(struct snd_sb *chip)
static int snd_sbdsp_version(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); snd_sbdsp_command(chip, SB_DSP_GET_VERSION);
result = (short) snd_sbdsp_get_byte(chip) << 8; result = (short) snd_sbdsp_get_byte(chip) << 8;

View File

@ -788,7 +788,6 @@ wavefront_send_patch (snd_wavefront_t *dev, wavefront_patch_info *header)
dev->patch_status[header->number] |= WF_SLOT_FILLED; dev->patch_status[header->number] |= WF_SLOT_FILLED;
bptr = buf;
bptr = munge_int32 (header->number, buf, 2); bptr = munge_int32 (header->number, buf, 2);
munge_buf ((unsigned char *)&header->hdr.p, bptr, WF_PATCH_BYTES); munge_buf ((unsigned char *)&header->hdr.p, bptr, WF_PATCH_BYTES);

View File

@ -1432,25 +1432,25 @@ static int FalconMixerIoctl(u_int cmd, u_long arg)
{ {
int data; int data;
switch (cmd) { switch (cmd) {
case SOUND_MIXER_READ_RECMASK: case SOUND_MIXER_READ_RECMASK:
return IOCTL_OUT(arg, SOUND_MASK_MIC); 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); 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); return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC);
case SOUND_MIXER_READ_VOLUME: case SOUND_MIXER_READ_VOLUME:
return IOCTL_OUT(arg, return IOCTL_OUT(arg,
VOLUME_ATT_TO_VOXWARE(dmasound.volume_left) | VOLUME_ATT_TO_VOXWARE(dmasound.volume_left) |
VOLUME_ATT_TO_VOXWARE(dmasound.volume_right) << 8); 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); return IOCTL_OUT(arg, SOUND_CAP_EXCL_INPUT);
case SOUND_MIXER_WRITE_MIC: case SOUND_MIXER_WRITE_MIC:
IOCTL_IN(arg, data); IOCTL_IN(arg, data);
tt_dmasnd.input_gain = tt_dmasnd.input_gain =
RECLEVEL_VOXWARE_TO_GAIN(data & 0xff) << 4 | RECLEVEL_VOXWARE_TO_GAIN(data & 0xff) << 4 |
RECLEVEL_VOXWARE_TO_GAIN(data >> 8 & 0xff); RECLEVEL_VOXWARE_TO_GAIN(data >> 8 & 0xff);
/* fall thru, return set value */ /* fall through - return set value */
case SOUND_MIXER_READ_MIC: case SOUND_MIXER_READ_MIC:
return IOCTL_OUT(arg, return IOCTL_OUT(arg,
RECLEVEL_GAIN_TO_VOXWARE(tt_dmasnd.input_gain >> 4 & 0xf) | RECLEVEL_GAIN_TO_VOXWARE(tt_dmasnd.input_gain >> 4 & 0xf) |
RECLEVEL_GAIN_TO_VOXWARE(tt_dmasnd.input_gain & 0xf) << 8); RECLEVEL_GAIN_TO_VOXWARE(tt_dmasnd.input_gain & 0xf) << 8);

View File

@ -596,11 +596,6 @@ static int snd_ac97_put_volsw(struct snd_kcontrol *kcontrol,
return err; 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] = { 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 - Bass", AC97_MASTER_TONE, 8, 15, 1),
AC97_SINGLE("Tone Control - Treble", AC97_MASTER_TONE, 0, 15, 1) AC97_SINGLE("Tone Control - Treble", AC97_MASTER_TONE, 0, 15, 1)

View File

@ -2189,11 +2189,10 @@ static int snd_echo_resume(struct device *dev)
u32 pipe_alloc_mask; u32 pipe_alloc_mask;
int err; 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) if (commpage_bak == NULL)
return -ENOMEM; return -ENOMEM;
commpage = chip->comm_page;
memcpy(commpage_bak, commpage, sizeof(*commpage));
err = init_hw(chip, chip->pci->device, chip->pci->subsystem_device); err = init_hw(chip, chip->pci->device, chip->pci->subsystem_device);
if (err < 0) { if (err < 0) {

View File

@ -12,6 +12,7 @@ config SND_HDA_INTEL
tristate "HD Audio PCI" tristate "HD Audio PCI"
depends on SND_PCI depends on SND_PCI
select SND_HDA select SND_HDA
select SND_INTEL_NHLT if ACPI
help help
Say Y here to include support for Intel "High Definition Say Y here to include support for Intel "High Definition
Audio" (Azalia) and its compatible devices. 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 To compile this driver as a module, choose M here: the module
will be called snd-hda-intel. 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 config SND_HDA_TEGRA
tristate "NVIDIA Tegra HD Audio" tristate "NVIDIA Tegra HD Audio"
depends on ARCH_TEGRA depends on ARCH_TEGRA
select SND_HDA select SND_HDA
select SND_HDA_ALIGNED_MMIO
help help
Say Y here to support the HDA controller present in NVIDIA Say Y here to support the HDA controller present in NVIDIA
Tegra SoCs Tegra SoCs

View File

@ -884,7 +884,8 @@ EXPORT_SYMBOL_GPL(snd_hda_apply_fixup);
#define IGNORE_SEQ_ASSOC (~(AC_DEFCFG_SEQUENCE | AC_DEFCFG_DEF_ASSOC)) #define IGNORE_SEQ_ASSOC (~(AC_DEFCFG_SEQUENCE | AC_DEFCFG_DEF_ASSOC))
static bool pin_config_match(struct hda_codec *codec, 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; const struct hda_pincfg *pin;
int i; int i;
@ -908,7 +909,8 @@ static bool pin_config_match(struct hda_codec *codec,
return false; return false;
} }
} }
if (!found && (cfg & 0xf0000000) != 0x40000000) if (match_all_pins &&
!found && (cfg & 0xf0000000) != 0x40000000)
return false; return false;
} }
@ -920,10 +922,12 @@ static bool pin_config_match(struct hda_codec *codec,
* @codec: the HDA codec * @codec: the HDA codec
* @pin_quirk: zero-terminated pin quirk list * @pin_quirk: zero-terminated pin quirk list
* @fixlist: the fixup 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, void snd_hda_pick_pin_fixup(struct hda_codec *codec,
const struct snd_hda_pin_quirk *pin_quirk, 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; const struct snd_hda_pin_quirk *pq;
@ -935,7 +939,7 @@ void snd_hda_pick_pin_fixup(struct hda_codec *codec,
continue; continue;
if (codec->core.vendor_id != pq->codec) if (codec->core.vendor_id != pq->codec)
continue; continue;
if (pin_config_match(codec, pq->pins)) { if (pin_config_match(codec, pq->pins, match_all_pins)) {
codec->fixup_id = pq->value; codec->fixup_id = pq->value;
#ifdef CONFIG_SND_DEBUG_VERBOSE #ifdef CONFIG_SND_DEBUG_VERBOSE
codec->fixup_name = pq->name; codec->fixup_name = pq->name;

View File

@ -846,7 +846,13 @@ static void snd_hda_codec_dev_release(struct device *dev)
snd_hda_sysfs_clear(codec); snd_hda_sysfs_clear(codec);
kfree(codec->modelname); kfree(codec->modelname);
kfree(codec->wcaps); 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 #define DEV_NAME_LEN 31

View File

@ -794,6 +794,7 @@ static int azx_rirb_get_response(struct hdac_bus *bus, unsigned int addr,
unsigned long timeout; unsigned long timeout;
unsigned long loopcounter; unsigned long loopcounter;
int do_poll = 0; int do_poll = 0;
bool warned = false;
again: again:
timeout = jiffies + msecs_to_jiffies(1000); 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); spin_unlock_irq(&bus->reg_lock);
if (time_after(jiffies, timeout)) if (time_after(jiffies, timeout))
break; 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 */ msleep(2); /* temporary workaround */
else { } else {
udelay(10); udelay(10);
cond_resched(); 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) { if (hbus->allow_bus_reset && !hbus->response_reset && !hbus->in_reset) {
hbus->response_reset = 1; 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 */ 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", "azx_get_response timeout, switching to single_cmd mode: last cmd=0x%08x\n",
bus->last_cmd[addr]); bus->last_cmd[addr]);
chip->single_cmd = 1; chip->single_cmd = 1;
@ -1207,14 +1219,12 @@ void snd_hda_bus_reset(struct hda_bus *bus)
} }
/* HD-audio bus initialization */ /* HD-audio bus initialization */
int azx_bus_init(struct azx *chip, const char *model, int azx_bus_init(struct azx *chip, const char *model)
const struct hdac_io_ops *io_ops)
{ {
struct hda_bus *bus = &chip->bus; struct hda_bus *bus = &chip->bus;
int err; int err;
err = snd_hdac_bus_init(&bus->core, chip->card->dev, &bus_core_ops, err = snd_hdac_bus_init(&bus->core, chip->card->dev, &bus_core_ops);
io_ops);
if (err < 0) if (err < 0)
return err; return err;

View File

@ -206,8 +206,7 @@ void azx_stop_chip(struct azx *chip);
irqreturn_t azx_interrupt(int irq, void *dev_id); irqreturn_t azx_interrupt(int irq, void *dev_id);
/* Codec interface */ /* Codec interface */
int azx_bus_init(struct azx *chip, const char *model, int azx_bus_init(struct azx *chip, const char *model);
const struct hdac_io_ops *io_ops);
int azx_probe_codecs(struct azx *chip, unsigned int max_slots); int azx_probe_codecs(struct azx *chip, unsigned int max_slots);
int azx_codec_configure(struct azx *chip); int azx_codec_configure(struct azx *chip);
int azx_init_streams(struct azx *chip); int azx_init_streams(struct azx *chip);

View File

@ -46,6 +46,7 @@
#include <sound/initval.h> #include <sound/initval.h>
#include <sound/hdaudio.h> #include <sound/hdaudio.h>
#include <sound/hda_i915.h> #include <sound/hda_i915.h>
#include <sound/intel-nhlt.h>
#include <linux/vgaarb.h> #include <linux/vgaarb.h>
#include <linux/vga_switcheroo.h> #include <linux/vga_switcheroo.h>
#include <linux/firmware.h> #include <linux/firmware.h>
@ -84,8 +85,6 @@ enum {
#define INTEL_SCH_HDA_DEVC 0x78 #define INTEL_SCH_HDA_DEVC 0x78
#define INTEL_SCH_HDA_DEVC_NOSNOOP (0x1<<11) #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 HD Audio Device ID*/
#define VIA_HDAC_DEVICE_ID 0x3288 #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)] = static bool beep_mode[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] =
CONFIG_SND_HDA_INPUT_BEEP_MODE}; CONFIG_SND_HDA_INPUT_BEEP_MODE};
#endif #endif
static bool dmic_detect = IS_ENABLED(CONFIG_SND_HDA_INTEL_DETECT_DMIC);
module_param_array(index, int, NULL, 0444); module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for Intel HD audio interface."); 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 " MODULE_PARM_DESC(beep_mode, "Select HDA Beep registration mode "
"(0=off, 1=on) (default=1)."); "(0=off, 1=on) (default=1).");
#endif #endif
module_param(dmic_detect, bool, 0444);
MODULE_PARM_DESC(dmic_detect, "DMIC detect on SKL+ platforms");
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int param_set_xint(const char *val, const struct kernel_param *kp); static int param_set_xint(const char *val, const struct kernel_param *kp);
@ -267,6 +269,7 @@ enum {
AZX_DRIVER_CTX, AZX_DRIVER_CTX,
AZX_DRIVER_CTHDA, AZX_DRIVER_CTHDA,
AZX_DRIVER_CMEDIA, AZX_DRIVER_CMEDIA,
AZX_DRIVER_ZHAOXIN,
AZX_DRIVER_GENERIC, AZX_DRIVER_GENERIC,
AZX_NUM_DRIVERS, /* keep this as last entry */ AZX_NUM_DRIVERS, /* keep this as last entry */
}; };
@ -353,7 +356,7 @@ enum {
*/ */
#ifdef SUPPORT_VGA_SWITCHEROO #ifdef SUPPORT_VGA_SWITCHEROO
#define use_vga_switcheroo(chip) ((chip)->use_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 #else
#define use_vga_switcheroo(chip) 0 #define use_vga_switcheroo(chip) 0
#define needs_eld_notify_link(chip) false #define needs_eld_notify_link(chip) false
@ -385,6 +388,7 @@ static char *driver_short_names[] = {
[AZX_DRIVER_CTX] = "HDA Creative", [AZX_DRIVER_CTX] = "HDA Creative",
[AZX_DRIVER_CTHDA] = "HDA Creative", [AZX_DRIVER_CTHDA] = "HDA Creative",
[AZX_DRIVER_CMEDIA] = "HDA C-Media", [AZX_DRIVER_CMEDIA] = "HDA C-Media",
[AZX_DRIVER_ZHAOXIN] = "HDA Zhaoxin",
[AZX_DRIVER_GENERIC] = "HD-Audio Generic", [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 = le32_to_cpu(*azx_dev->core.posbuf);
mod_dma_pos %= azx_dev->core.period_bytes; mod_dma_pos %= azx_dev->core.period_bytes;
/* azx_dev->fifo_size can't get FIFO size of in stream. fifo_size = azx_stream(azx_dev)->fifo_size - 1;
* Get from base address + offset.
*/
fifo_size = readw(azx_bus(chip)->remap_addr +
VIA_IN_STREAM0_FIFO_SIZE_OFFSET);
if (azx_dev->insufficient) { if (azx_dev->insufficient) {
/* Link position never gather than FIFO size */ /* Link position never gather than FIFO size */
@ -1145,7 +1145,7 @@ static int azx_runtime_idle(struct device *dev)
return -EBUSY; return -EBUSY;
/* ELD notification gets broken when HD-audio bus is off */ /* 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 -EBUSY;
return 0; 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_intel *hda = container_of(chip, struct hda_intel, chip);
struct hda_codec *codec; 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) list_for_each_codec(codec, &chip->bus)
codec->auto_runtime_pm = 1; codec->auto_runtime_pm = 1;
/* reset the power save setup */ /* 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 snd_card *card = pci_get_drvdata(pci);
struct azx *chip = card->private_data; struct azx *chip = card->private_data;
struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
if (client_id == VGA_SWITCHEROO_DIS) if (client_id == VGA_SWITCHEROO_DIS)
hda->need_eld_notify_link = 0; chip->bus.keep_power = 0;
setup_vga_switcheroo_runtime_pm(chip); setup_vga_switcheroo_runtime_pm(chip);
} }
@ -1285,7 +1284,7 @@ static void init_vga_switcheroo(struct azx *chip)
dev_info(chip->card->dev, dev_info(chip->card->dev,
"Handle vga_switcheroo audio client\n"); "Handle vga_switcheroo audio client\n");
hda->use_vga_switcheroo = 1; 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; chip->driver_caps |= AZX_DCAPS_PM_RUNTIME;
pci_dev_put(p); pci_dev_put(p);
} }
@ -1349,9 +1348,9 @@ static int azx_free(struct azx *chip)
} }
if (bus->chip_init) { if (bus->chip_init) {
azx_stop_chip(chip);
azx_clear_irq_pending(chip); azx_clear_irq_pending(chip);
azx_stop_all_streams(chip); azx_stop_all_streams(chip);
azx_stop_chip(chip);
} }
if (bus->irq >= 0) if (bus->irq >= 0)
@ -1684,7 +1683,6 @@ static int default_bdl_pos_adj(struct azx *chip)
/* /*
* constructor * constructor
*/ */
static const struct hdac_io_ops pci_hda_io_ops;
static const struct hda_controller_ops pci_hda_ops; static const struct hda_controller_ops pci_hda_ops;
static int azx_create(struct snd_card *card, struct pci_dev *pci, 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 else
chip->bdl_pos_adj = bdl_pos_adj[dev]; 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) { if (err < 0) {
kfree(hda); kfree(hda);
pci_disable_device(pci); pci_disable_device(pci);
return err; 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 */ /* Workaround for a communication error on CFL (bko#199007) and CNL */
if (IS_CFL(pci) || IS_CNL(pci)) if (IS_CFL(pci) || IS_CNL(pci))
azx_bus(chip)->polling_mode = 1; azx_bus(chip)->polling_mode = 1;
@ -1985,41 +1987,6 @@ static void azx_firmware_cb(const struct firmware *fw, void *context)
} }
#endif #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) static int disable_msi_reset_irq(struct azx *chip)
{ {
struct hdac_bus *bus = azx_bus(chip); struct hdac_bus *bus = azx_bus(chip);
@ -2036,24 +2003,6 @@ static int disable_msi_reset_irq(struct azx *chip)
return 0; 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, static void pcm_mmap_prepare(struct snd_pcm_substream *substream,
struct vm_area_struct *area) struct vm_area_struct *area)
{ {
@ -2065,23 +2014,31 @@ static void pcm_mmap_prepare(struct snd_pcm_substream *substream,
#endif #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 = { static const struct hda_controller_ops pci_hda_ops = {
.disable_msi_reset_irq = disable_msi_reset_irq, .disable_msi_reset_irq = disable_msi_reset_irq,
.pcm_mmap_prepare = pcm_mmap_prepare, .pcm_mmap_prepare = pcm_mmap_prepare,
.position_check = azx_position_check, .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, static int azx_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id) const struct pci_device_id *pci_id)
{ {
@ -2112,6 +2069,17 @@ static int azx_probe(struct pci_dev *pci,
card->private_data = chip; card->private_data = chip;
hda = container_of(chip, struct hda_intel, 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); pci_set_drvdata(pci, card);
err = register_vga_switcheroo(chip); 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 = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
.class_mask = 0xffffff, .class_mask = 0xffffff,
.driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_HDMI }, .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_HDMI },
/* Zhaoxin */
{ PCI_DEVICE(0x1d17, 0x3288), .driver_data = AZX_DRIVER_ZHAOXIN },
{ 0, } { 0, }
}; };
MODULE_DEVICE_TABLE(pci, azx_ids); MODULE_DEVICE_TABLE(pci, azx_ids);

View File

@ -25,7 +25,6 @@ struct hda_intel {
/* vga_switcheroo setup */ /* vga_switcheroo setup */
unsigned int use_vga_switcheroo:1; unsigned int use_vga_switcheroo:1;
unsigned int need_eld_notify_link:1;
unsigned int vga_switcheroo_registered:1; unsigned int vga_switcheroo_registered:1;
unsigned int init_failed:1; /* delayed init failed */ unsigned int init_failed:1; /* delayed init failed */

View File

@ -361,7 +361,8 @@ void snd_hda_pick_fixup(struct hda_codec *codec,
const struct hda_fixup *fixlist); const struct hda_fixup *fixlist);
void snd_hda_pick_pin_fixup(struct hda_codec *codec, void snd_hda_pick_pin_fixup(struct hda_codec *codec,
const struct snd_hda_pin_quirk *pin_quirk, 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 */ /* helper macros to retrieve pin default-config values */
#define get_defcfg_connect(cfg) \ #define get_defcfg_connect(cfg) \

View File

@ -75,88 +75,6 @@ MODULE_PARM_DESC(power_save,
#define power_save 0 #define power_save 0
#endif #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 const struct hda_controller_ops hda_tegra_ops; /* nothing special */
static void hda_tegra_init(struct hda_tegra *hda) 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); 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) if (err < 0)
return err; return err;

View File

@ -18,6 +18,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/pci.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
@ -119,6 +120,7 @@ struct hdmi_pcm {
}; };
struct hdmi_spec { struct hdmi_spec {
struct hda_codec *codec;
int num_cvts; int num_cvts;
struct snd_array cvts; /* struct hdmi_spec_per_cvt */ struct snd_array cvts; /* struct hdmi_spec_per_cvt */
hda_nid_t cvt_nids[4]; /* only for haswell fix */ hda_nid_t cvt_nids[4]; /* only for haswell fix */
@ -163,9 +165,11 @@ struct hdmi_spec {
struct hda_multi_out multiout; struct hda_multi_out multiout;
struct hda_pcm_stream pcm_playback; struct hda_pcm_stream pcm_playback;
/* i915/powerwell (Haswell+/Valleyview+) specific */ bool use_jack_detect; /* jack detection enabled */
bool use_acomp_notifier; /* use i915 eld_notify callback for hotplug */ 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; struct drm_audio_component_audio_ops drm_audio_ops;
int (*port2pin)(struct hda_codec *, int); /* reverse port/pin mapping */
struct hdac_chmap chmap; struct hdac_chmap chmap;
hda_nid_t vendor_nid; 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, static void jack_callback(struct hda_codec *codec,
struct hda_jack_callback *jack) struct hda_jack_callback *jack)
{ {
/* stop polling when notification is enabled */
if (codec_has_acomp(codec))
return;
/* hda_jack don't support DP MST */ /* hda_jack don't support DP MST */
check_presence_and_report(codec, jack->nid, 0); 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 tag = res >> AC_UNSOL_RES_TAG_SHIFT;
int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_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)) { if (!snd_hda_jack_tbl_get_from_tag(codec, tag)) {
codec_dbg(codec, "Unexpected HDMI event tag 0x%x\n", tag); codec_dbg(codec, "Unexpected HDMI event tag 0x%x\n", tag);
return; return;
@ -1421,7 +1432,7 @@ static void hdmi_pcm_reset_pin(struct hdmi_spec *spec,
/* update per_pin ELD from the given new ELD; /* update per_pin ELD from the given new ELD;
* setup info frame and notification accordingly * 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_spec_per_pin *per_pin,
struct hdmi_eld *eld) struct hdmi_eld *eld)
{ {
@ -1429,7 +1440,7 @@ static void update_eld(struct hda_codec *codec,
struct hdmi_spec *spec = codec->spec; struct hdmi_spec *spec = codec->spec;
bool old_eld_valid = pin_eld->eld_valid; bool old_eld_valid = pin_eld->eld_valid;
bool eld_changed; bool eld_changed;
int pcm_idx = -1; int pcm_idx;
/* for monitor disconnection, save pcm_idx firstly */ /* for monitor disconnection, save pcm_idx firstly */
pcm_idx = per_pin->pcm_idx; 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); snd_hdmi_show_eld(codec, &eld->info);
eld_changed = (pin_eld->eld_valid != eld->eld_valid); 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 || if (pin_eld->eld_size != eld->eld_size ||
memcmp(pin_eld->eld_buffer, eld->eld_buffer, memcmp(pin_eld->eld_buffer, eld->eld_buffer,
eld->eld_size) != 0) eld->eld_size) != 0)
eld_changed = true; eld_changed = true;
pin_eld->monitor_present = eld->monitor_present; if (eld_changed) {
pin_eld->eld_valid = eld->eld_valid; pin_eld->monitor_present = eld->monitor_present;
pin_eld->eld_size = eld->eld_size; pin_eld->eld_valid = eld->eld_valid;
if (eld->eld_valid) pin_eld->eld_size = eld->eld_size;
memcpy(pin_eld->eld_buffer, eld->eld_buffer, eld->eld_size); if (eld->eld_valid)
pin_eld->info = eld->info; 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 * 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_VALUE |
SNDRV_CTL_EVENT_MASK_INFO, SNDRV_CTL_EVENT_MASK_INFO,
&get_hdmi_pcm(spec, pcm_idx)->eld_ctl->id); &get_hdmi_pcm(spec, pcm_idx)->eld_ctl->id);
return eld_changed;
} }
/* update ELD and jack state via HD-audio verbs */ /* 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_spec *spec = codec->spec;
struct hdmi_eld *eld = &spec->temp_eld; struct hdmi_eld *eld = &spec->temp_eld;
struct snd_jack *jack = NULL; struct snd_jack *jack = NULL;
bool changed;
int size; int size;
mutex_lock(&per_pin->lock); 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() * disconnected event. Jack must be fetched before update_eld()
*/ */
jack = pin_idx_to_jack(codec, per_pin); jack = pin_idx_to_jack(codec, per_pin);
update_eld(codec, per_pin, eld); changed = update_eld(codec, per_pin, eld);
if (jack == NULL) if (jack == NULL)
jack = pin_idx_to_jack(codec, per_pin); jack = pin_idx_to_jack(codec, per_pin);
if (jack == NULL) if (changed && jack)
goto unlock; snd_jack_report(jack,
snd_jack_report(jack, (eld->monitor_present && eld->eld_valid) ?
(eld->monitor_present && eld->eld_valid) ?
SND_JACK_AVOUT : 0); SND_JACK_AVOUT : 0);
unlock:
mutex_unlock(&per_pin->lock); 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); snd_hda_power_down_pm(codec);
return false; return false;
} }
} ret = hdmi_present_sense_via_verbs(per_pin, repoll);
snd_hda_power_down_pm(codec);
if (codec_has_acomp(codec)) { } else {
sync_eld_via_acomp(codec, per_pin); sync_eld_via_acomp(codec, per_pin);
ret = false; /* don't call snd_hda_jack_report_sync() */ 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; return ret;
} }
@ -2248,6 +2258,8 @@ static int generic_hdmi_init(struct hda_codec *codec)
struct hdmi_spec *spec = codec->spec; struct hdmi_spec *spec = codec->spec;
int pin_idx; 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++) { for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
hda_nid_t pin_nid = per_pin->pin_nid; 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); snd_hda_set_dev_select(codec, pin_nid, dev_id);
hdmi_init_pin(codec, pin_nid); 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, snd_hda_jack_detect_enable_callback(codec, pin_nid,
codec->jackpoll_interval > 0 ? jack_callback);
jack_callback : NULL);
} }
mutex_unlock(&spec->pcm_lock);
return 0; return 0;
} }
@ -2292,7 +2308,9 @@ static void generic_hdmi_free(struct hda_codec *codec)
struct hdmi_spec *spec = codec->spec; struct hdmi_spec *spec = codec->spec;
int pin_idx, pcm_idx; 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); snd_hdac_acomp_register_notifier(&codec->bus->core, NULL);
codec->relaxed_resume = 0; codec->relaxed_resume = 0;
} }
@ -2360,6 +2378,7 @@ static int alloc_generic_hdmi(struct hda_codec *codec)
if (!spec) if (!spec)
return -ENOMEM; return -ENOMEM;
spec->codec = codec;
spec->ops = generic_standard_hdmi_ops; spec->ops = generic_standard_hdmi_ops;
spec->dev_num = 1; /* initialize to 1 */ spec->dev_num = 1; /* initialize to 1 */
mutex_init(&spec->pcm_lock); mutex_init(&spec->pcm_lock);
@ -2397,6 +2416,138 @@ static int patch_generic_hdmi(struct hda_codec *codec)
return 0; 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 * 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); 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 */ /* register i915 component pin_eld_notify callback */
static void register_i915_notifier(struct hda_codec *codec) static void register_i915_notifier(struct hda_codec *codec)
{ {
struct hdmi_spec *spec = codec->spec; struct hdmi_spec *spec = codec->spec;
spec->use_acomp_notifier = true; spec->use_acomp_notifier = true;
spec->drm_audio_ops.audio_ptr = codec; spec->port2pin = intel_port2pin;
/* intel_audio_codec_enable() or intel_audio_codec_disable() setup_drm_audio_ops(codec, &intel_audio_ops);
* 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;
snd_hdac_acomp_register_notifier(&codec->bus->core, snd_hdac_acomp_register_notifier(&codec->bus->core,
&spec->drm_audio_ops); &spec->drm_audio_ops);
/* no need for forcible resume for jack check thanks to notifier */ /* 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 */ /* precondition and allocation for Intel codecs */
static int alloc_intel_hdmi(struct hda_codec *codec) static int alloc_intel_hdmi(struct hda_codec *codec)
{ {
int err;
/* requires i915 binding */ /* requires i915 binding */
if (!codec->bus->core.audio_component) { if (!codec->bus->core.audio_component) {
codec_info(codec, "No i915 binding for Intel HDMI/DP codec\n"); 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 -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 */ /* parse and post-process for Intel codecs */
@ -2976,6 +3133,7 @@ static int patch_simple_hdmi(struct hda_codec *codec,
if (!spec) if (!spec)
return -ENOMEM; return -ENOMEM;
spec->codec = codec;
codec->spec = spec; codec->spec = spec;
hdmi_array_init(spec, 1); hdmi_array_init(spec, 1);
@ -3280,6 +3438,26 @@ static int nvhdmi_chmap_validate(struct hdac_chmap *chmap,
return 0; 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) static int patch_nvhdmi(struct hda_codec *codec)
{ {
struct hdmi_spec *spec; struct hdmi_spec *spec;
@ -3296,6 +3474,8 @@ static int patch_nvhdmi(struct hda_codec *codec)
nvhdmi_chmap_cea_alloc_validate_get_type; nvhdmi_chmap_cea_alloc_validate_get_type;
spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate; spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate;
generic_acomp_init(codec, &nvhdmi_audio_ops, nvhdmi_port2pin);
return 0; return 0;
} }
@ -3783,6 +3963,26 @@ static int atihdmi_init(struct hda_codec *codec)
return 0; 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) static int patch_atihdmi(struct hda_codec *codec)
{ {
struct hdmi_spec *spec; struct hdmi_spec *spec;
@ -3831,6 +4031,8 @@ static int patch_atihdmi(struct hda_codec *codec)
*/ */
codec->link_down_at_suspend = 1; codec->link_down_at_suspend = 1;
generic_acomp_init(codec, &atihdmi_audio_ops, atihdmi_port2pin);
return 0; return 0;
} }

View File

@ -1058,6 +1058,9 @@ static const struct snd_pci_quirk beep_white_list[] = {
SND_PCI_QUIRK(0x1043, 0x834a, "EeePC", 1), SND_PCI_QUIRK(0x1043, 0x834a, "EeePC", 1),
SND_PCI_QUIRK(0x1458, 0xa002, "GA-MA790X", 1), SND_PCI_QUIRK(0x1458, 0xa002, "GA-MA790X", 1),
SND_PCI_QUIRK(0x8086, 0xd613, "Intel", 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; return err;
spec = codec->spec; spec = codec->spec;
spec->gen.beep_nid = 0x01; if (has_cdefine_beep(codec))
spec->gen.beep_nid = 0x01;
spec->shutup = alc_eapd_shutup; spec->shutup = alc_eapd_shutup;
@ -3755,6 +3759,72 @@ static void alc269_x101_hp_automute_hook(struct hda_codec *codec,
vref); 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, static void alc269_fixup_x101_headset_mic(struct hda_codec *codec,
const struct hda_fixup *fix, int action) const struct hda_fixup *fix, int action)
{ {
@ -5780,6 +5850,7 @@ enum {
ALC255_FIXUP_DUMMY_LINEOUT_VERB, ALC255_FIXUP_DUMMY_LINEOUT_VERB,
ALC255_FIXUP_DELL_HEADSET_MIC, ALC255_FIXUP_DELL_HEADSET_MIC,
ALC256_FIXUP_HUAWEI_MACH_WX9_PINS, ALC256_FIXUP_HUAWEI_MACH_WX9_PINS,
ALC298_FIXUP_HUAWEI_MBX_STEREO,
ALC295_FIXUP_HP_X360, ALC295_FIXUP_HP_X360,
ALC221_FIXUP_HP_HEADSET_MIC, ALC221_FIXUP_HP_HEADSET_MIC,
ALC285_FIXUP_LENOVO_HEADPHONE_NOISE, ALC285_FIXUP_LENOVO_HEADPHONE_NOISE,
@ -6089,6 +6160,12 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true, .chained = true,
.chain_id = ALC255_FIXUP_MIC_MUTE_LED .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] = { [ALC269_FIXUP_ASUS_X101_FUNC] = {
.type = HDA_FIXUP_FUNC, .type = HDA_FIXUP_FUNC,
.v.func = alc269_fixup_x101_headset_mic, .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 = ALC225_FIXUP_HEADSET_JACK, .name = "alc-headset-jack"},
{.id = ALC295_FIXUP_CHROME_BOOK, .name = "alc-chrome-book"}, {.id = ALC295_FIXUP_CHROME_BOOK, .name = "alc-chrome-book"},
{.id = ALC299_FIXUP_PREDATOR_SPK, .name = "predator-spk"}, {.id = ALC299_FIXUP_PREDATOR_SPK, .name = "predator-spk"},
{.id = ALC298_FIXUP_HUAWEI_MBX_STEREO, .name = "huawei-mbx-stereo"},
{} {}
}; };
#define ALC225_STANDARD_PINS \ #define ALC225_STANDARD_PINS \
@ -7590,10 +7668,6 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
{0x12, 0x90a60120}, {0x12, 0x90a60120},
{0x14, 0x90170110}, {0x14, 0x90170110},
{0x21, 0x0321101f}), {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, SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
ALC290_STANDARD_PINS, ALC290_STANDARD_PINS,
{0x15, 0x04211040}, {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) static void alc269_fill_coef(struct hda_codec *codec)
{ {
struct alc_spec *spec = codec->spec; 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, snd_hda_pick_fixup(codec, alc269_fixup_models,
alc269_fixup_tbl, alc269_fixups); 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, snd_hda_pick_fixup(codec, NULL, alc269_fixup_vendor_tbl,
alc269_fixups); alc269_fixups);
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
@ -8026,7 +8114,8 @@ static int patch_alc861(struct hda_codec *codec)
return err; return err;
spec = codec->spec; spec = codec->spec;
spec->gen.beep_nid = 0x23; if (has_cdefine_beep(codec))
spec->gen.beep_nid = 0x23;
#ifdef CONFIG_PM #ifdef CONFIG_PM
spec->power_hook = alc_power_eapd; spec->power_hook = alc_power_eapd;
@ -8127,7 +8216,8 @@ static int patch_alc861vd(struct hda_codec *codec)
return err; return err;
spec = codec->spec; spec = codec->spec;
spec->gen.beep_nid = 0x23; if (has_cdefine_beep(codec))
spec->gen.beep_nid = 0x23;
spec->shutup = alc_eapd_shutup; 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[] = { static struct coef_fw alc668_coefs[] = {
WRITE_COEF(0x01, 0xbebe), WRITE_COEF(0x02, 0xaaaa), WRITE_COEF(0x03, 0x0), WRITE_COEF(0x01, 0xbebe), WRITE_COEF(0x02, 0xaaaa), WRITE_COEF(0x03, 0x0),
WRITE_COEF(0x04, 0x0180), WRITE_COEF(0x06, 0x0), WRITE_COEF(0x07, 0x0f80), WRITE_COEF(0x04, 0x0180), WRITE_COEF(0x06, 0x0), WRITE_COEF(0x07, 0x0f80),
@ -8338,6 +8467,9 @@ enum {
ALC662_FIXUP_USI_FUNC, ALC662_FIXUP_USI_FUNC,
ALC662_FIXUP_USI_HEADSET_MODE, ALC662_FIXUP_USI_HEADSET_MODE,
ALC662_FIXUP_LENOVO_MULTI_CODECS, 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[] = { static const struct hda_fixup alc662_fixups[] = {
@ -8664,6 +8796,33 @@ static const struct hda_fixup alc662_fixups[] = {
.type = HDA_FIXUP_FUNC, .type = HDA_FIXUP_FUNC,
.v.func = alc233_alc662_fixup_lenovo_dual_codecs, .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[] = { 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(0x19da, 0xa130, "Zotac Z68", ALC662_FIXUP_ZOTAC_Z68),
SND_PCI_QUIRK(0x1b0a, 0x01b8, "ACER Veriton", ALC662_FIXUP_ACER_VERITON), 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(0x1b35, 0x2206, "CZC P10T", ALC662_FIXUP_CZC_P10T),
SND_PCI_QUIRK(0x1025, 0x0566, "Acer Aspire Ethos 8951G", ALC669_FIXUP_ACER_ASPIRE_ETHOS),
#if 0 #if 0
/* Below is a quirk table taken from the old code. /* 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 = ALC892_FIXUP_ASROCK_MOBO, .name = "asrock-mobo"},
{.id = ALC662_FIXUP_USI_HEADSET_MODE, .name = "usi-headset"}, {.id = ALC662_FIXUP_USI_HEADSET_MODE, .name = "usi-headset"},
{.id = ALC662_FIXUP_LENOVO_MULTI_CODECS, .name = "dual-codecs"}, {.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, snd_hda_pick_fixup(codec, alc662_fixup_models,
alc662_fixup_tbl, alc662_fixups); 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); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
alc_auto_parse_customize_define(codec); alc_auto_parse_customize_define(codec);

View File

@ -975,15 +975,6 @@ static int stac_create_spdif_mux_ctls(struct hda_codec *codec)
return 0; 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[] = { static const struct hda_verb stac9200_eapd_init[] = {
/* set dac0mux for dac converter */ /* set dac0mux for dac converter */
{0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},

View File

@ -49,6 +49,14 @@ static const struct pci_device_id snd_lx6464es_ids[] = {
PCI_VENDOR_ID_DIGIGRAM, PCI_VENDOR_ID_DIGIGRAM,
PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_CAE_SERIAL_SUBSYSTEM), PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_CAE_SERIAL_SUBSYSTEM),
}, /* LX6464ES-CAE */ }, /* 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, }, { 0, },
}; };

View File

@ -51,7 +51,6 @@ source "sound/soc/dwc/Kconfig"
source "sound/soc/fsl/Kconfig" source "sound/soc/fsl/Kconfig"
source "sound/soc/hisilicon/Kconfig" source "sound/soc/hisilicon/Kconfig"
source "sound/soc/jz4740/Kconfig" source "sound/soc/jz4740/Kconfig"
source "sound/soc/nuc900/Kconfig"
source "sound/soc/kirkwood/Kconfig" source "sound/soc/kirkwood/Kconfig"
source "sound/soc/img/Kconfig" source "sound/soc/img/Kconfig"
source "sound/soc/intel/Kconfig" source "sound/soc/intel/Kconfig"

View File

@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0 # 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-objs += soc-pcm.o soc-io.o soc-devres.o soc-ops.o
snd-soc-core-$(CONFIG_SND_SOC_COMPRESS) += soc-compress.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) += mediatek/
obj-$(CONFIG_SND_SOC) += meson/ obj-$(CONFIG_SND_SOC) += meson/
obj-$(CONFIG_SND_SOC) += mxs/ obj-$(CONFIG_SND_SOC) += mxs/
obj-$(CONFIG_SND_SOC) += nuc900/
obj-$(CONFIG_SND_SOC) += kirkwood/ obj-$(CONFIG_SND_SOC) += kirkwood/
obj-$(CONFIG_SND_SOC) += pxa/ obj-$(CONFIG_SND_SOC) += pxa/
obj-$(CONFIG_SND_SOC) += qcom/ obj-$(CONFIG_SND_SOC) += qcom/

View File

@ -10,7 +10,7 @@ config SND_SOC_AMD_CZ_DA7219MX98357_MACH
select SND_SOC_MAX98357A select SND_SOC_MAX98357A
select SND_SOC_ADAU7002 select SND_SOC_ADAU7002
select REGULATOR select REGULATOR
depends on SND_SOC_AMD_ACP && I2C depends on SND_SOC_AMD_ACP && I2C && GPIOLIB
help help
This option enables machine driver for DA7219 and MAX9835. This option enables machine driver for DA7219 and MAX9835.

View File

@ -1251,8 +1251,7 @@ static int acp_audio_probe(struct platform_device *pdev)
if (!audio_drv_data) if (!audio_drv_data)
return -ENOMEM; return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); audio_drv_data->acp_mmio = devm_platform_ioremap_resource(pdev, 0);
audio_drv_data->acp_mmio = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(audio_drv_data->acp_mmio)) if (IS_ERR(audio_drv_data->acp_mmio))
return PTR_ERR(audio_drv_data->acp_mmio); return PTR_ERR(audio_drv_data->acp_mmio);

Some files were not shown because too many files have changed in this diff Show More