sound updates for 5.4

As shown in diffstat and logs, it was again a busy development
 cycle at this time, too.  The most significant changes are still
 on-going refactoring / modernization works for ASoC core and
 drivers, but there are lots of other changes as well.  Here we go,
 some highlights below:
 
 ASoC:
 - Quite lots of cleanup / refactoring of ASoC core and APIs;
   most of them are systematic, but also including cleanups and
   modernization
 
 - A bulk of updates for some ASoC platforms, Freescale, sunxi and
   Intel SST/SOF
 
 - Initial support for Sound Open Firmware on i.MX8
 
 - Removal of deprecated w90x900 and nuc900 drivers
 
 - New support for Cirrus Logic CS47L15 and CS47L92, Freescale i.MX
   7ULP and 8MQ, Meson G12A and NXP UDA1334
 
 USB-audio:
 - More validations of descriptor units for hardening against bugs
   reported by fuzzers
 
 - PCM device assignment workaround for a past call-order change
 
 - Scarlett Gen2 mixer interface, a few more more quirks
 
 HD-audio:
 - Support for audio component with AMD/ATI and Nvidia HDMI codecs
 
 - Clean up HD-audio core and remove indirect access ops for Intel SOF
 
 - DMIC detection at probe; it would make systems automatically falling
   back to SST/SOF driver on devices that need DMIC handling.
   Needs a new Kconfig to set, and beware that it's still new and a bit
   experimental
 
 FireWire:
 - Lots of code refactoring and cleanups
 -----BEGIN PGP SIGNATURE-----
 
 iQJCBAABCAAsFiEEIXTw5fNLNI7mMiVaLtJE4w1nLE8FAl1/dEsOHHRpd2FpQHN1
 c2UuZGUACgkQLtJE4w1nLE+2WA//YoEoxXgx/Aok5eJfKPSn0Pd7HccvU53Q9Syx
 z8DaUSUbd6S7g9P8zzXm1l5bIfAur1FQDkvfYrA23iDhixna/w+jnkeUbRBCFbnB
 tdUX4mMBMO/wfJZ5I1Amw9OaKsEaWoSaw8EezgWLkQUkVjfAVLitz/oH4qxlitxv
 ssOP2pLNaNKMqJjgV49DJ6wXjAFnndB+nSoEGzDkoDYv17VeahDG6XRPRGvmf6ZM
 06w2KfGJqstNniH5Cw2us0GQf1UD6Ra1aZJRekoOjih5PeX8/Mjl+6HgQVwEZwZY
 ZnwAgJaL/ujUPywJO9cBlMv2yiePMqgXA8b5sNO1HJdzwIP7wAjD/bbY6V9+8IDZ
 zQpqHWen7B/PySLym7y19QvVZYmc+y43vJJev9M6kc0LofOltWmeeAUr5x3gktWx
 WuwN6KEHP5NA2cwkc8mW1LwiUavon1hl7PGOVqx7R3k4upPAKY1ZXBHNXgN2+M5N
 lX9fbAMLB2W0cPkD/Ob29JP04qDxmLT+nhHNh2lzSQBlHHpsuVUyGvJH4PwOWnTb
 tk1xTPxF+zVaVTV3TUGHziiK98PHY/Sj1hlyvKGuBiD+efvvnXAVkivjL9Egj4aq
 QGGGshTdOi7l7Anon3TEbHA8cFNkttRukKq1bpskZHydlaQtTOW0CEotRmvEJfpR
 ghQn5mY=
 =/+1Q
 -----END PGP SIGNATURE-----

Merge tag 'sound-5.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound

Pull sound updates from Takashi Iwai:
 "As shown in diffstat and logs, it was again a busy development cycle
  at this time, too. The most significant changes are still on-going
  refactoring / modernization works for ASoC core and drivers, but there
  are lots of other changes as well. Here we go, some highlights below:

  ASoC:

   - Quite a lot of cleanup / refactoring of ASoC core and APIs; most of
     them are systematic, but also including cleanups and modernization

   - A bulk of updates for some ASoC platforms, Freescale, sunxi and
     Intel SST/SOF

   - Initial support for Sound Open Firmware on i.MX8

   - Removal of deprecated w90x900 and nuc900 drivers

   - New support for Cirrus Logic CS47L15 and CS47L92, Freescale i.MX
     7ULP and 8MQ, Meson G12A and NXP UDA1334

  USB-audio:

   - More validations of descriptor units for hardening against bugs
     reported by fuzzers

   - PCM device assignment workaround for a past call-order change

   - Scarlett Gen2 mixer interface, a few more more quirks

  HD-audio:

   - Support for audio component with AMD/ATI and Nvidia HDMI codecs

   - Clean up HD-audio core and remove indirect access ops for Intel SOF

   - DMIC detection at probe; it would make systems automatically
     falling back to SST/SOF driver on devices that need DMIC handling.
     Needs a new Kconfig to set, and beware that it's still new and a
     bit experimental

  FireWire:

   - Lots of code refactoring and cleanups"

* tag 'sound-5.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (521 commits)
  ASoC: sdm845: remove unneeded semicolon
  ASoC: fsl_sai: Implement set_bclk_ratio
  ASoC: dmaengine: Replace strncpy() with strscpy_pad() for pcm->name
  ASoC: wcd9335: remove redundant use of ret variable
  ALSA: firewire-tascam: check intermediate state of clock status and retry
  ALSA: firewire-tascam: handle error code when getting current source of clock
  ASoC: hdmi-codec: Add an op to set callback function for plug event
  ASoC: rt5677: keep analog power register at SND_SOC_BIAS_OFF
  ASoC: rt5677: Remove magic number register writes
  ASoC: soc-core: self contained soc_unbind_aux_dev()
  ASoC: soc-core: add soc_unbind_aux_dev()
  ASoC: soc-core: self contained soc_bind_aux_dev()
  ASoC: soc-core: move soc_probe_link_dais() next to soc_remove_link_dais()
  ASoC: soc-core: self contained soc_probe_link_dais()
  ASoC: soc-core: add new soc_link_init()
  ASoC: soc-core: move soc_probe_dai() next to soc_remove_dai()
  ASoC: soc-core: self contained soc_remove_link_dais()
  ASoC: soc-core: self contained soc_remove_link_components()
  ASoC: soc-core: self contained soc_probe_link_components()
  ASoC: rt1308: make array pd static const, makes object smaller
  ...
This commit is contained in:
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:
compatible:
contains:
const: allwinner,sun8i-h3-spdif
enum:
- allwinner,sun8i-h3-spdif
- allwinner,sun50i-h6-spdif
then:
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
'amlogic,axg-toddr' or
'amlogic,g12a-frddr' or
'amlogic,g12a-toddr'
'amlogic,g12a-toddr' or
'amlogic,sm1-frddr' or
'amlogic,sm1-toddr'
- reg: physical base address of the controller and length of memory
mapped region.
- interrupts: interrupt specifier for the fifo.
- clocks: phandle to the fifo peripheral clock provided by the audio
clock controller.
- resets: phandle to memory ARB line provided by the arb reset controller.
- resets: list of reset phandle, one for each entry reset-names.
- reset-names: should contain the following:
* "arb" : memory ARB line (required)
* "rst" : dedicated device reset line (optional)
- #sound-dai-cells: must be 0.
Example of FRDDR A on the A113 SoC:

View File

@ -2,7 +2,8 @@
Required properties:
- compatible: 'amlogic,axg-pdm' or
'amlogic,g12a-pdm'
'amlogic,g12a-pdm' or
'amlogic,sm1-pdm'
- reg: physical base address of the controller and length of memory
mapped region.
- clocks: list of clock phandle, one for each entry clock-names.
@ -12,6 +13,9 @@ Required properties:
* "sysclk" : dsp system clock
- #sound-dai-cells: must be 0.
Optional property:
- resets: phandle to the dedicated reset line of the pdm input.
Example of PDM on the A113 SoC:
pdm: audio-controller@ff632000 {

View File

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

View File

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

View File

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

View File

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

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:
- compatible : Compatible list, must contain "fsl,imx35-esai" or
"fsl,vf610-esai"
- compatible : Compatible list, should contain one of the following
compatibles:
"fsl,imx35-esai",
"fsl,vf610-esai",
"fsl,imx6ull-esai",
- reg : Offset and length of the register set for the device.

View File

@ -8,7 +8,9 @@ codec/DSP interfaces.
Required properties:
- compatible : Compatible list, contains "fsl,vf610-sai",
"fsl,imx6sx-sai" or "fsl,imx6ul-sai"
"fsl,imx6sx-sai", "fsl,imx6ul-sai",
"fsl,imx7ulp-sai", "fsl,imx8mq-sai" or
"fsl,imx8qm-sai".
- reg : Offset and length of the register set for the device.

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,
3 = VIACOMBO: VIA-specific workaround for capture,
4 = COMBO: use LPIB for playback, auto for capture stream
5 = SKL+: apply the delay calculation available on recent Intel chips
6 = FIFO: correct the position with the fixed FIFO size, for recent AMD chips
probe_mask
Bitmask to probe codecs (default = -1, meaning all slots);
When the bit 8 (0x100) is set, the lower 8 bits are used

View File

@ -260,6 +260,9 @@ alc295-hp-x360
HP Spectre X360 fixups
alc-sense-combo
Headset button support for Chrome platform
huawei-mbx-stereo
Enable initialization verbs for Huawei MBX stereo speakers;
might be risky, try this at your own risk
ALC66x/67x/892
==============

View File

@ -66,6 +66,11 @@ by comparing both LPIB and position-buffer values.
``position_fix=4`` is another combination available for all controllers,
and uses LPIB for the playback and the position-buffer for the capture
streams.
``position_fix=5`` is specific to Intel platforms, so far, for Skylake
and onward. It applies the delay calculation for the precise position
reporting.
``position_fix=6`` is to correct the position with the fixed FIFO
size, mainly targeted for the recent AMD controllers.
0 is the default value for all other
controllers, the automatic check and fallback to LPIB as described in
the above. If you get a problem of repeated sounds, this option might

View File

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

View File

@ -246,8 +246,8 @@ static struct platform_device latch2_gpio_device = {
#define LATCH2_PIN_SCARD_CMDVCC 11
#define LATCH2_PIN_MODEM_NRESET 12
#define LATCH2_PIN_MODEM_CODEC 13
#define LATCH2_PIN_AUDIO_MUTE 14
#define LATCH2_PIN_HOOKFLASH 15
#define LATCH2_PIN_HANDSFREE_MUTE 14
#define LATCH2_PIN_HANDSET_MUTE 15
static struct regulator_consumer_supply modem_nreset_consumers[] = {
REGULATOR_SUPPLY("RESET#", "serial8250.1"),
@ -476,6 +476,10 @@ static struct gpiod_lookup_table ams_delta_audio_gpio_table = {
"hook_switch", 0),
GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_MODEM_CODEC,
"modem_codec", 0),
GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_HANDSFREE_MUTE,
"handsfree_mute", 0),
GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_HANDSET_MUTE,
"handset_mute", 0),
{ },
},
};
@ -590,8 +594,6 @@ static int gpiochip_match_by_label(struct gpio_chip *chip, void *data)
static struct gpiod_hog ams_delta_gpio_hogs[] = {
GPIO_HOG(LATCH2_LABEL, LATCH2_PIN_KEYBRD_DATAOUT, "keybrd_dataout",
GPIO_ACTIVE_HIGH, GPIOD_OUT_LOW),
GPIO_HOG(LATCH2_LABEL, LATCH2_PIN_AUDIO_MUTE, "audio_mute",
GPIO_ACTIVE_HIGH, GPIOD_OUT_LOW),
{},
};

View File

@ -1951,6 +1951,8 @@
#define PCI_VENDOR_ID_DIGIGRAM 0x1369
#define PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_SERIAL_SUBSYSTEM 0xc001
#define PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_CAE_SERIAL_SUBSYSTEM 0xc002
#define PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ESE_SERIAL_SUBSYSTEM 0xc021
#define PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ESE_CAE_SERIAL_SUBSYSTEM 0xc022
#define PCI_VENDOR_ID_KAWASAKI 0x136b
#define PCI_DEVICE_ID_MCHIP_KL5A72002 0xff01

View File

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

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);
unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid,
unsigned int verb, unsigned int parm);
int snd_hdac_exec_verb(struct hdac_device *codec, unsigned int cmd,
unsigned int flags, unsigned int *res);
int snd_hdac_read(struct hdac_device *codec, hda_nid_t nid,
unsigned int verb, unsigned int parm, unsigned int *res);
int _snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm,
@ -253,24 +249,6 @@ struct hdac_ext_bus_ops {
int (*hdev_detach)(struct hdac_device *hdev);
};
/*
* Lowlevel I/O operators
*/
struct hdac_io_ops {
/* mapped register accesses */
void (*reg_writel)(u32 value, u32 __iomem *addr);
u32 (*reg_readl)(u32 __iomem *addr);
void (*reg_writew)(u16 value, u16 __iomem *addr);
u16 (*reg_readw)(u16 __iomem *addr);
void (*reg_writeb)(u8 value, u8 __iomem *addr);
u8 (*reg_readb)(u8 __iomem *addr);
/* Allocation ops */
int (*dma_alloc_pages)(struct hdac_bus *bus, int type, size_t size,
struct snd_dma_buffer *buf);
void (*dma_free_pages)(struct hdac_bus *bus,
struct snd_dma_buffer *buf);
};
#define HDA_UNSOL_QUEUE_SIZE 64
#define HDA_MAX_CODECS 8 /* limit by controller side */
@ -304,7 +282,6 @@ struct hdac_rb {
struct hdac_bus {
struct device *dev;
const struct hdac_bus_ops *ops;
const struct hdac_io_ops *io_ops;
const struct hdac_ext_bus_ops *ext_ops;
/* h/w resources */
@ -344,6 +321,7 @@ struct hdac_bus {
/* CORB/RIRB and position buffers */
struct snd_dma_buffer rb;
struct snd_dma_buffer posbuf;
int dma_type; /* SNDRV_DMA_TYPE_XXX for CORB/RIRB */
/* hdac_stream linked list */
struct list_head stream_list;
@ -384,8 +362,7 @@ struct hdac_bus {
};
int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
const struct hdac_bus_ops *ops,
const struct hdac_io_ops *io_ops);
const struct hdac_bus_ops *ops);
void snd_hdac_bus_exit(struct hdac_bus *bus);
int snd_hdac_bus_exec_verb(struct hdac_bus *bus, unsigned int addr,
unsigned int cmd, unsigned int *res);
@ -393,11 +370,6 @@ int snd_hdac_bus_exec_verb_unlocked(struct hdac_bus *bus, unsigned int addr,
unsigned int cmd, unsigned int *res);
void snd_hdac_bus_queue_event(struct hdac_bus *bus, u32 res, u32 res_ex);
int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec);
void snd_hdac_bus_remove_device(struct hdac_bus *bus,
struct hdac_device *codec);
void snd_hdac_bus_process_unsol_events(struct work_struct *work);
static inline void snd_hdac_codec_link_up(struct hdac_device *codec)
{
set_bit(codec->addr, &codec->bus->codec_powered);
@ -429,21 +401,38 @@ int snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status,
int snd_hdac_bus_alloc_stream_pages(struct hdac_bus *bus);
void snd_hdac_bus_free_stream_pages(struct hdac_bus *bus);
#ifdef CONFIG_SND_HDA_ALIGNED_MMIO
unsigned int snd_hdac_aligned_read(void __iomem *addr, unsigned int mask);
void snd_hdac_aligned_write(unsigned int val, void __iomem *addr,
unsigned int mask);
#define snd_hdac_reg_writeb(v, addr) snd_hdac_aligned_write(v, addr, 0xff)
#define snd_hdac_reg_writew(v, addr) snd_hdac_aligned_write(v, addr, 0xffff)
#define snd_hdac_reg_readb(addr) snd_hdac_aligned_read(addr, 0xff)
#define snd_hdac_reg_readw(addr) snd_hdac_aligned_read(addr, 0xffff)
#else /* CONFIG_SND_HDA_ALIGNED_MMIO */
#define snd_hdac_reg_writeb(val, addr) writeb(val, addr)
#define snd_hdac_reg_writew(val, addr) writew(val, addr)
#define snd_hdac_reg_readb(addr) readb(addr)
#define snd_hdac_reg_readw(addr) readw(addr)
#endif /* CONFIG_SND_HDA_ALIGNED_MMIO */
#define snd_hdac_reg_writel(val, addr) writel(val, addr)
#define snd_hdac_reg_readl(addr) readl(addr)
/*
* macros for easy use
*/
#define _snd_hdac_chip_writeb(chip, reg, value) \
((chip)->io_ops->reg_writeb(value, (chip)->remap_addr + (reg)))
snd_hdac_reg_writeb(value, (chip)->remap_addr + (reg))
#define _snd_hdac_chip_readb(chip, reg) \
((chip)->io_ops->reg_readb((chip)->remap_addr + (reg)))
snd_hdac_reg_readb((chip)->remap_addr + (reg))
#define _snd_hdac_chip_writew(chip, reg, value) \
((chip)->io_ops->reg_writew(value, (chip)->remap_addr + (reg)))
snd_hdac_reg_writew(value, (chip)->remap_addr + (reg))
#define _snd_hdac_chip_readw(chip, reg) \
((chip)->io_ops->reg_readw((chip)->remap_addr + (reg)))
snd_hdac_reg_readw((chip)->remap_addr + (reg))
#define _snd_hdac_chip_writel(chip, reg, value) \
((chip)->io_ops->reg_writel(value, (chip)->remap_addr + (reg)))
snd_hdac_reg_writel(value, (chip)->remap_addr + (reg))
#define _snd_hdac_chip_readl(chip, reg) \
((chip)->io_ops->reg_readl((chip)->remap_addr + (reg)))
snd_hdac_reg_readl((chip)->remap_addr + (reg))
/* read/write a register, pass without AZX_REG_ prefix */
#define snd_hdac_chip_writel(chip, reg, value) \
@ -548,24 +537,19 @@ int snd_hdac_get_stream_stripe_ctl(struct hdac_bus *bus,
/*
* macros for easy use
*/
#define _snd_hdac_stream_write(type, dev, reg, value) \
((dev)->bus->io_ops->reg_write ## type(value, (dev)->sd_addr + (reg)))
#define _snd_hdac_stream_read(type, dev, reg) \
((dev)->bus->io_ops->reg_read ## type((dev)->sd_addr + (reg)))
/* read/write a register, pass without AZX_REG_ prefix */
#define snd_hdac_stream_writel(dev, reg, value) \
_snd_hdac_stream_write(l, dev, AZX_REG_ ## reg, value)
snd_hdac_reg_writel(value, (dev)->sd_addr + AZX_REG_ ## reg)
#define snd_hdac_stream_writew(dev, reg, value) \
_snd_hdac_stream_write(w, dev, AZX_REG_ ## reg, value)
snd_hdac_reg_writew(value, (dev)->sd_addr + AZX_REG_ ## reg)
#define snd_hdac_stream_writeb(dev, reg, value) \
_snd_hdac_stream_write(b, dev, AZX_REG_ ## reg, value)
snd_hdac_reg_writeb(value, (dev)->sd_addr + AZX_REG_ ## reg)
#define snd_hdac_stream_readl(dev, reg) \
_snd_hdac_stream_read(l, dev, AZX_REG_ ## reg)
snd_hdac_reg_readl((dev)->sd_addr + AZX_REG_ ## reg)
#define snd_hdac_stream_readw(dev, reg) \
_snd_hdac_stream_read(w, dev, AZX_REG_ ## reg)
snd_hdac_reg_readw((dev)->sd_addr + AZX_REG_ ## reg)
#define snd_hdac_stream_readb(dev, reg) \
_snd_hdac_stream_read(b, dev, AZX_REG_ ## reg)
snd_hdac_reg_readb((dev)->sd_addr + AZX_REG_ ## reg)
/* update a register, pass without AZX_REG_ prefix */
#define snd_hdac_stream_updatel(dev, reg, mask, val) \

View File

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

View File

@ -47,6 +47,9 @@ struct hdmi_codec_params {
int channels;
};
typedef void (*hdmi_codec_plugged_cb)(struct device *dev,
bool plugged);
struct hdmi_codec_pdata;
struct hdmi_codec_ops {
/*
@ -88,6 +91,14 @@ struct hdmi_codec_ops {
*/
int (*get_dai_id)(struct snd_soc_component *comment,
struct device_node *endpoint);
/*
* Hook callback function to handle connector plug event.
* Optional
*/
int (*hook_plugged_cb)(struct device *dev, void *data,
hdmi_codec_plugged_cb fn,
struct device *codec_dev);
};
/* HDMI codec initalization data */
@ -99,6 +110,12 @@ struct hdmi_codec_pdata {
void *data;
};
struct snd_soc_component;
struct snd_soc_jack;
int hdmi_codec_set_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *jack);
#define HDMI_CODEC_DRV_NAME "hdmi-audio-codec"
#endif /* __HDMI_CODEC_H__ */

View File

@ -1,18 +1,17 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* skl-nhlt.h - Intel HDA Platform NHLT header
* intel-nhlt.h - Intel HDA Platform NHLT header
*
* Copyright (C) 2015 Intel Corp
* Author: Sanjiv Kumar <sanjiv.kumar@intel.com>
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Copyright (c) 2015-2019 Intel Corporation
*/
#ifndef __SKL_NHLT_H__
#define __SKL_NHLT_H__
#ifndef __INTEL_NHLT_H__
#define __INTEL_NHLT_H__
#include <linux/acpi.h>
#if IS_ENABLED(CONFIG_ACPI) && IS_ENABLED(CONFIG_SND_INTEL_NHLT)
struct wav_fmt {
u16 fmt_tag;
u16 channels;
@ -97,16 +96,22 @@ struct nhlt_resource_desc {
#define MIC_ARRAY_2CH 2
#define MIC_ARRAY_4CH 4
struct nhlt_tdm_config {
struct nhlt_device_specific_config {
u8 virtual_slot;
u8 config_type;
} __packed;
struct nhlt_dmic_array_config {
struct nhlt_tdm_config tdm_config;
struct nhlt_device_specific_config device_config;
u8 array_type;
} __packed;
struct nhlt_vendor_dmic_array_config {
struct nhlt_dmic_array_config dmic_config;
u8 nb_mics;
/* TODO add vendor mic config */
} __packed;
enum {
NHLT_MIC_ARRAY_2CH_SMALL = 0xa,
NHLT_MIC_ARRAY_2CH_BIG = 0xb,
@ -116,4 +121,30 @@ enum {
NHLT_MIC_ARRAY_VENDOR_DEFINED = 0xf,
};
struct nhlt_acpi_table *intel_nhlt_init(struct device *dev);
void intel_nhlt_free(struct nhlt_acpi_table *addr);
int intel_nhlt_get_dmic_geo(struct device *dev, struct nhlt_acpi_table *nhlt);
#else
struct nhlt_acpi_table;
static inline struct nhlt_acpi_table *intel_nhlt_init(struct device *dev)
{
return NULL;
}
static inline void intel_nhlt_free(struct nhlt_acpi_table *addr)
{
}
static inline int intel_nhlt_get_dmic_geo(struct device *dev,
struct nhlt_acpi_table *nhlt)
{
return 0;
}
#endif
#endif

View File

@ -117,6 +117,8 @@ struct snd_pcm_ops {
#define SNDRV_PCM_RATE_96000 (1<<10) /* 96000Hz */
#define SNDRV_PCM_RATE_176400 (1<<11) /* 176400Hz */
#define SNDRV_PCM_RATE_192000 (1<<12) /* 192000Hz */
#define SNDRV_PCM_RATE_352800 (1<<13) /* 352800Hz */
#define SNDRV_PCM_RATE_384000 (1<<14) /* 384000Hz */
#define SNDRV_PCM_RATE_CONTINUOUS (1<<30) /* continuous range */
#define SNDRV_PCM_RATE_KNOT (1<<31) /* supports more non-continuos rates */
@ -129,6 +131,9 @@ struct snd_pcm_ops {
SNDRV_PCM_RATE_88200|SNDRV_PCM_RATE_96000)
#define SNDRV_PCM_RATE_8000_192000 (SNDRV_PCM_RATE_8000_96000|SNDRV_PCM_RATE_176400|\
SNDRV_PCM_RATE_192000)
#define SNDRV_PCM_RATE_8000_384000 (SNDRV_PCM_RATE_8000_192000|\
SNDRV_PCM_RATE_352800|\
SNDRV_PCM_RATE_384000)
#define _SNDRV_PCM_FMTBIT(fmt) (1ULL << (__force int)SNDRV_PCM_FORMAT_##fmt)
#define SNDRV_PCM_FMTBIT_S8 _SNDRV_PCM_FMTBIT(S8)
#define SNDRV_PCM_FMTBIT_U8 _SNDRV_PCM_FMTBIT(U8)

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_cnl_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_ehl_machines[];
/*
* generic table used for HDA codec-based platforms, possibly with

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_hw_params(struct snd_soc_dai *dai,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params);
void snd_soc_dai_hw_free(struct snd_soc_dai *dai,
struct snd_pcm_substream *substream);
int snd_soc_dai_startup(struct snd_soc_dai *dai,
struct snd_pcm_substream *substream);
void snd_soc_dai_shutdown(struct snd_soc_dai *dai,
struct snd_pcm_substream *substream);
int snd_soc_dai_prepare(struct snd_soc_dai *dai,
struct snd_pcm_substream *substream);
int snd_soc_dai_trigger(struct snd_soc_dai *dai,
struct snd_pcm_substream *substream, int cmd);
int snd_soc_dai_bespoke_trigger(struct snd_soc_dai *dai,
struct snd_pcm_substream *substream, int cmd);
snd_pcm_sframes_t snd_soc_dai_delay(struct snd_soc_dai *dai,
struct snd_pcm_substream *substream);
void snd_soc_dai_suspend(struct snd_soc_dai *dai);
void snd_soc_dai_resume(struct snd_soc_dai *dai);
int snd_soc_dai_probe(struct snd_soc_dai *dai);
int snd_soc_dai_remove(struct snd_soc_dai *dai);
int snd_soc_dai_compress_new(struct snd_soc_dai *dai,
struct snd_soc_pcm_runtime *rtd, int num);
bool snd_soc_dai_stream_valid(struct snd_soc_dai *dai, int stream);
struct snd_soc_dai_ops {
/*
* DAI clocking configuration, all optional.
@ -268,8 +293,6 @@ struct snd_soc_dai_driver {
/* Optional Callback used at pcm creation*/
int (*pcm_new)(struct snd_soc_pcm_runtime *rtd,
struct snd_soc_dai *dai);
/* DAI is also used for the control bus */
bool bus_control;
/* ops */
const struct snd_soc_dai_ops *ops;
@ -281,6 +304,7 @@ struct snd_soc_dai_driver {
unsigned int symmetric_rates:1;
unsigned int symmetric_channels:1;
unsigned int symmetric_samplebits:1;
unsigned int bus_control:1; /* DAI is also used for the control bus */
/* probe ordering - for components with runtime dependencies */
int probe_order;

View File

@ -353,6 +353,8 @@ struct device;
#define SND_SOC_DAPM_WILL_PMD 0x80 /* called at start of sequence */
#define SND_SOC_DAPM_PRE_POST_PMD \
(SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD)
#define SND_SOC_DAPM_PRE_POST_PMU \
(SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU)
/* convenience event type detection */
#define SND_SOC_DAPM_EVENT_ON(e) \
@ -402,6 +404,9 @@ int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm,
struct snd_soc_dapm_widget *snd_soc_dapm_new_control(
struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_widget *widget);
struct snd_soc_dapm_widget *snd_soc_dapm_new_control_unlocked(
struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_widget *widget);
int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
struct snd_soc_dai *dai);
int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card);
@ -414,6 +419,9 @@ int snd_soc_dapm_update_dai(struct snd_pcm_substream *substream,
/* dapm path setup */
int snd_soc_dapm_new_widgets(struct snd_soc_card *card);
void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm);
void snd_soc_dapm_init(struct snd_soc_dapm_context *dapm,
struct snd_soc_card *card,
struct snd_soc_component *component);
int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_route *route, int num);
int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm,
@ -659,8 +667,6 @@ struct snd_soc_dapm_context {
unsigned int idle_bias_off:1; /* Use BIAS_OFF instead of STANDBY */
/* Go to BIAS_OFF in suspend if the DAPM context is idle */
unsigned int suspend_bias_off:1;
void (*seq_notifier)(struct snd_soc_dapm_context *,
enum snd_soc_dapm_type, int);
struct device *dev; /* from parent - for debug */
struct snd_soc_component *component; /* parent component */
@ -670,10 +676,6 @@ struct snd_soc_dapm_context {
enum snd_soc_bias_level target_bias_level;
struct list_head list;
int (*stream_event)(struct snd_soc_dapm_context *dapm, int event);
int (*set_bias_level)(struct snd_soc_dapm_context *dapm,
enum snd_soc_bias_level level);
struct snd_soc_dapm_wcache path_sink_cache;
struct snd_soc_dapm_wcache path_source_cache;

View File

@ -142,9 +142,16 @@ void snd_soc_dpcm_be_set_state(struct snd_soc_pcm_runtime *be, int stream,
/* internal use only */
int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute);
void soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd);
int soc_dpcm_runtime_update(struct snd_soc_card *);
#ifdef CONFIG_DEBUG_FS
void soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd);
#else
static inline void soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd)
{
}
#endif
int dpcm_path_get(struct snd_soc_pcm_runtime *fe,
int stream, struct snd_soc_dapm_widget_list **list_);
int dpcm_process_paths(struct snd_soc_pcm_runtime *fe,

View File

@ -362,21 +362,6 @@
#define SOC_ENUM_SINGLE_VIRT_DECL(name, xtexts) \
const struct soc_enum name = SOC_ENUM_SINGLE_VIRT(ARRAY_SIZE(xtexts), xtexts)
/*
* Component probe and remove ordering levels for components with runtime
* dependencies.
*/
#define SND_SOC_COMP_ORDER_FIRST -2
#define SND_SOC_COMP_ORDER_EARLY -1
#define SND_SOC_COMP_ORDER_NORMAL 0
#define SND_SOC_COMP_ORDER_LATE 1
#define SND_SOC_COMP_ORDER_LAST 2
#define for_each_comp_order(order) \
for (order = SND_SOC_COMP_ORDER_FIRST; \
order <= SND_SOC_COMP_ORDER_LAST; \
order++)
/*
* Bias levels
*
@ -505,10 +490,6 @@ int snd_soc_params_to_bclk(struct snd_pcm_hw_params *parms);
int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
const struct snd_pcm_hardware *hw);
int soc_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai);
/* Jack reporting */
int snd_soc_card_jack_new(struct snd_soc_card *card, const char *id, int type,
struct snd_soc_jack *jack, struct snd_soc_jack_pin *pins,
@ -751,132 +732,6 @@ struct snd_soc_compr_ops {
int (*trigger)(struct snd_compr_stream *);
};
/* component interface */
struct snd_soc_component_driver {
const char *name;
/* Default control and setup, added after probe() is run */
const struct snd_kcontrol_new *controls;
unsigned int num_controls;
const struct snd_soc_dapm_widget *dapm_widgets;
unsigned int num_dapm_widgets;
const struct snd_soc_dapm_route *dapm_routes;
unsigned int num_dapm_routes;
int (*probe)(struct snd_soc_component *);
void (*remove)(struct snd_soc_component *);
int (*suspend)(struct snd_soc_component *);
int (*resume)(struct snd_soc_component *);
unsigned int (*read)(struct snd_soc_component *, unsigned int);
int (*write)(struct snd_soc_component *, unsigned int, unsigned int);
/* pcm creation and destruction */
int (*pcm_new)(struct snd_soc_pcm_runtime *);
void (*pcm_free)(struct snd_pcm *);
/* component wide operations */
int (*set_sysclk)(struct snd_soc_component *component,
int clk_id, int source, unsigned int freq, int dir);
int (*set_pll)(struct snd_soc_component *component, int pll_id,
int source, unsigned int freq_in, unsigned int freq_out);
int (*set_jack)(struct snd_soc_component *component,
struct snd_soc_jack *jack, void *data);
/* DT */
int (*of_xlate_dai_name)(struct snd_soc_component *component,
struct of_phandle_args *args,
const char **dai_name);
int (*of_xlate_dai_id)(struct snd_soc_component *comment,
struct device_node *endpoint);
void (*seq_notifier)(struct snd_soc_component *, enum snd_soc_dapm_type,
int subseq);
int (*stream_event)(struct snd_soc_component *, int event);
int (*set_bias_level)(struct snd_soc_component *component,
enum snd_soc_bias_level level);
const struct snd_pcm_ops *ops;
const struct snd_compr_ops *compr_ops;
/* probe ordering - for components with runtime dependencies */
int probe_order;
int remove_order;
/*
* signal if the module handling the component should not be removed
* if a pcm is open. Setting this would prevent the module
* refcount being incremented in probe() but allow it be incremented
* when a pcm is opened and decremented when it is closed.
*/
unsigned int module_get_upon_open:1;
/* bits */
unsigned int idle_bias_on:1;
unsigned int suspend_bias_off:1;
unsigned int use_pmdown_time:1; /* care pmdown_time at stop */
unsigned int endianness:1;
unsigned int non_legacy_dai_naming:1;
/* this component uses topology and ignore machine driver FEs */
const char *ignore_machine;
const char *topology_name_prefix;
int (*be_hw_params_fixup)(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params);
bool use_dai_pcm_id; /* use the DAI link PCM ID as PCM device number */
int be_pcm_base; /* base device ID for all BE PCMs */
};
struct snd_soc_component {
const char *name;
int id;
const char *name_prefix;
struct device *dev;
struct snd_soc_card *card;
unsigned int active;
unsigned int suspended:1; /* is in suspend PM state */
struct list_head list;
struct list_head card_aux_list; /* for auxiliary bound components */
struct list_head card_list;
const struct snd_soc_component_driver *driver;
struct list_head dai_list;
int num_dai;
struct regmap *regmap;
int val_bytes;
struct mutex io_mutex;
/* attached dynamic objects */
struct list_head dobj_list;
/*
* DO NOT use any of the fields below in drivers, they are temporary and
* are going to be removed again soon. If you use them in driver code the
* driver will be marked as BROKEN when these fields are removed.
*/
/* Don't use these, use snd_soc_component_get_dapm() */
struct snd_soc_dapm_context dapm;
/* machine specific init */
int (*init)(struct snd_soc_component *component);
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs_root;
const char *debugfs_prefix;
#endif
};
#define for_each_component_dais(component, dai)\
list_for_each_entry(dai, &(component)->dai_list, list)
#define for_each_component_dais_safe(component, dai, _dai)\
list_for_each_entry_safe(dai, _dai, &(component)->dai_list, list)
struct snd_soc_rtdcom_list {
struct snd_soc_component *component;
struct list_head list; /* rtd::component_list */
@ -1086,6 +941,7 @@ struct snd_soc_dai_link {
#define COMP_CPU(_dai) { .dai_name = _dai, }
#define COMP_CODEC(_name, _dai) { .name = _name, .dai_name = _dai, }
#define COMP_PLATFORM(_name) { .name = _name }
#define COMP_AUX(_name) { .name = _name }
#define COMP_DUMMY() { .name = "snd-soc-dummy", .dai_name = "snd-soc-dummy-dai", }
extern struct snd_soc_dai_link_component null_dailink_component[0];
@ -1107,14 +963,11 @@ struct snd_soc_codec_conf {
};
struct snd_soc_aux_dev {
const char *name; /* Codec name */
/*
* specify multi-codec either by device name, or by
* DT/OF node, but not both.
*/
const char *codec_name;
struct device_node *codec_of_node;
struct snd_soc_dai_link_component dlc;
/* codec/machine specific init - e.g. add machine controls */
int (*init)(struct snd_soc_component *component);
@ -1135,6 +988,10 @@ struct snd_soc_card {
struct mutex mutex;
struct mutex dapm_mutex;
/* Mutex for PCM operations */
struct mutex pcm_mutex;
enum snd_soc_pcm_subclass pcm_subclass;
spinlock_t dpcm_lock;
bool instantiated;
@ -1203,8 +1060,6 @@ struct snd_soc_card {
int num_of_dapm_routes;
bool fully_routed;
struct work_struct deferred_resume_work;
/* lists of probed devices belonging to this card */
struct list_head component_dev_list;
struct list_head list;
@ -1224,7 +1079,9 @@ struct snd_soc_card {
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs_card_root;
struct dentry *debugfs_pop_time;
#endif
#ifdef CONFIG_PM_SLEEP
struct work_struct deferred_resume_work;
#endif
u32 pop_time;
@ -1234,6 +1091,10 @@ struct snd_soc_card {
for ((i) = 0; \
((i) < (card)->num_links) && ((link) = &(card)->dai_link[i]); \
(i)++)
#define for_each_card_pre_auxs(card, i, aux) \
for ((i) = 0; \
((i) < (card)->num_aux_devs) && ((aux) = &(card)->aux_dev[i]); \
(i)++)
#define for_each_card_links(card, link) \
list_for_each_entry(link, &(card)->dai_link_list, list)
@ -1245,6 +1106,12 @@ struct snd_soc_card {
#define for_each_card_rtds_safe(card, rtd, _rtd) \
list_for_each_entry_safe(rtd, _rtd, &(card)->rtd_list, list)
#define for_each_card_auxs(card, component) \
list_for_each_entry(component, &card->aux_comp_list, card_aux_list)
#define for_each_card_auxs_safe(card, component, _comp) \
list_for_each_entry_safe(component, _comp, \
&card->aux_comp_list, card_aux_list)
#define for_each_card_components(card, component) \
list_for_each_entry(component, &(card)->component_dev_list, card_list)
@ -1253,8 +1120,6 @@ struct snd_soc_pcm_runtime {
struct device *dev;
struct snd_soc_card *card;
struct snd_soc_dai_link *dai_link;
struct mutex pcm_mutex;
enum snd_soc_pcm_subclass pcm_subclass;
struct snd_pcm_ops ops;
unsigned int params_select; /* currently selected param for dai link */
@ -1342,134 +1207,6 @@ struct soc_enum {
struct snd_soc_dobj dobj;
};
/**
* snd_soc_dapm_to_component() - Casts a DAPM context to the component it is
* embedded in
* @dapm: The DAPM context to cast to the component
*
* This function must only be used on DAPM contexts that are known to be part of
* a component (e.g. in a component driver). Otherwise the behavior is
* undefined.
*/
static inline struct snd_soc_component *snd_soc_dapm_to_component(
struct snd_soc_dapm_context *dapm)
{
return container_of(dapm, struct snd_soc_component, dapm);
}
/**
* snd_soc_component_get_dapm() - Returns the DAPM context associated with a
* component
* @component: The component for which to get the DAPM context
*/
static inline struct snd_soc_dapm_context *snd_soc_component_get_dapm(
struct snd_soc_component *component)
{
return &component->dapm;
}
/**
* snd_soc_component_init_bias_level() - Initialize COMPONENT DAPM bias level
* @component: The COMPONENT for which to initialize the DAPM bias level
* @level: The DAPM level to initialize to
*
* Initializes the COMPONENT DAPM bias level. See snd_soc_dapm_init_bias_level().
*/
static inline void
snd_soc_component_init_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
snd_soc_dapm_init_bias_level(
snd_soc_component_get_dapm(component), level);
}
/**
* snd_soc_component_get_bias_level() - Get current COMPONENT DAPM bias level
* @component: The COMPONENT for which to get the DAPM bias level
*
* Returns: The current DAPM bias level of the COMPONENT.
*/
static inline enum snd_soc_bias_level
snd_soc_component_get_bias_level(struct snd_soc_component *component)
{
return snd_soc_dapm_get_bias_level(
snd_soc_component_get_dapm(component));
}
/**
* snd_soc_component_force_bias_level() - Set the COMPONENT DAPM bias level
* @component: The COMPONENT for which to set the level
* @level: The level to set to
*
* Forces the COMPONENT bias level to a specific state. See
* snd_soc_dapm_force_bias_level().
*/
static inline int
snd_soc_component_force_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
return snd_soc_dapm_force_bias_level(
snd_soc_component_get_dapm(component),
level);
}
/**
* snd_soc_dapm_kcontrol_component() - Returns the component associated to a kcontrol
* @kcontrol: The kcontrol
*
* This function must only be used on DAPM contexts that are known to be part of
* a COMPONENT (e.g. in a COMPONENT driver). Otherwise the behavior is undefined.
*/
static inline struct snd_soc_component *snd_soc_dapm_kcontrol_component(
struct snd_kcontrol *kcontrol)
{
return snd_soc_dapm_to_component(snd_soc_dapm_kcontrol_dapm(kcontrol));
}
/**
* snd_soc_component_cache_sync() - Sync the register cache with the hardware
* @component: COMPONENT to sync
*
* Note: This function will call regcache_sync()
*/
static inline int snd_soc_component_cache_sync(
struct snd_soc_component *component)
{
return regcache_sync(component->regmap);
}
/* component IO */
int snd_soc_component_read(struct snd_soc_component *component,
unsigned int reg, unsigned int *val);
unsigned int snd_soc_component_read32(struct snd_soc_component *component,
unsigned int reg);
int snd_soc_component_write(struct snd_soc_component *component,
unsigned int reg, unsigned int val);
int snd_soc_component_update_bits(struct snd_soc_component *component,
unsigned int reg, unsigned int mask, unsigned int val);
int snd_soc_component_update_bits_async(struct snd_soc_component *component,
unsigned int reg, unsigned int mask, unsigned int val);
void snd_soc_component_async_complete(struct snd_soc_component *component);
int snd_soc_component_test_bits(struct snd_soc_component *component,
unsigned int reg, unsigned int mask, unsigned int value);
/* component wide operations */
int snd_soc_component_set_sysclk(struct snd_soc_component *component,
int clk_id, int source, unsigned int freq, int dir);
int snd_soc_component_set_pll(struct snd_soc_component *component, int pll_id,
int source, unsigned int freq_in,
unsigned int freq_out);
int snd_soc_component_set_jack(struct snd_soc_component *component,
struct snd_soc_jack *jack, void *data);
#ifdef CONFIG_REGMAP
void snd_soc_component_init_regmap(struct snd_soc_component *component,
struct regmap *regmap);
void snd_soc_component_exit_regmap(struct snd_soc_component *component);
#endif
/* device driver data */
static inline void snd_soc_card_set_drvdata(struct snd_soc_card *card,
@ -1483,27 +1220,6 @@ static inline void *snd_soc_card_get_drvdata(struct snd_soc_card *card)
return card->drvdata;
}
static inline void snd_soc_component_set_drvdata(struct snd_soc_component *c,
void *data)
{
dev_set_drvdata(c->dev, data);
}
static inline void *snd_soc_component_get_drvdata(struct snd_soc_component *c)
{
return dev_get_drvdata(c->dev);
}
static inline void snd_soc_initialize_card_lists(struct snd_soc_card *card)
{
INIT_LIST_HEAD(&card->widgets);
INIT_LIST_HEAD(&card->paths);
INIT_LIST_HEAD(&card->dapm_list);
INIT_LIST_HEAD(&card->aux_comp_list);
INIT_LIST_HEAD(&card->component_dev_list);
INIT_LIST_HEAD(&card->list);
}
static inline bool snd_soc_volsw_is_stereo(struct soc_mixer_control *mc)
{
if (mc->reg == mc->rreg && mc->shift == mc->rshift)
@ -1540,12 +1256,6 @@ static inline unsigned int snd_soc_enum_item_to_val(struct soc_enum *e,
return e->values[item];
}
static inline bool snd_soc_component_is_active(
struct snd_soc_component *component)
{
return component->active != 0;
}
/**
* snd_soc_kcontrol_component() - Returns the component that registered the
* control
@ -1681,24 +1391,6 @@ static inline void snd_soc_dapm_mutex_unlock(struct snd_soc_dapm_context *dapm)
mutex_unlock(&dapm->card->dapm_mutex);
}
int snd_soc_component_enable_pin(struct snd_soc_component *component,
const char *pin);
int snd_soc_component_enable_pin_unlocked(struct snd_soc_component *component,
const char *pin);
int snd_soc_component_disable_pin(struct snd_soc_component *component,
const char *pin);
int snd_soc_component_disable_pin_unlocked(struct snd_soc_component *component,
const char *pin);
int snd_soc_component_nc_pin(struct snd_soc_component *component,
const char *pin);
int snd_soc_component_nc_pin_unlocked(struct snd_soc_component *component,
const char *pin);
int snd_soc_component_get_pin_status(struct snd_soc_component *component,
const char *pin);
int snd_soc_component_force_enable_pin(struct snd_soc_component *component,
const char *pin);
int snd_soc_component_force_enable_pin_unlocked(
struct snd_soc_component *component,
const char *pin);
#include <sound/soc-component.h>
#endif

View File

@ -76,6 +76,9 @@ struct sof_ipc_dai_ssp_params {
uint16_t tdm_per_slot_padding_flag;
uint32_t clks_control;
uint32_t quirks;
uint32_t bclk_delay; /* guaranteed time (ms) for which BCLK
* will be driven, before sending data
*/
} __packed;
/* HDA Configuration Request - SOF_IPC_DAI_HDA_CONFIG */
@ -176,4 +179,13 @@ struct sof_ipc_dai_dmic_params {
struct sof_ipc_dai_dmic_pdm_ctrl pdm[0];
} __packed;
/* ALH Configuration Request - SOF_IPC_DAI_ALH_CONFIG */
struct sof_ipc_dai_alh_params {
struct sof_ipc_hdr hdr;
uint32_t stream_id;
/* reserved for future use */
uint32_t reserved[15];
} __packed;
#endif

View File

@ -49,7 +49,9 @@ enum sof_ipc_dai_type {
SOF_DAI_INTEL_SSP, /**< Intel SSP */
SOF_DAI_INTEL_DMIC, /**< Intel DMIC */
SOF_DAI_INTEL_HDA, /**< Intel HD/A */
SOF_DAI_INTEL_SOUNDWIRE, /**< Intel SoundWire */
SOF_DAI_INTEL_ALH, /**< Intel ALH */
SOF_DAI_IMX_SAI, /**< i.MX SAI */
SOF_DAI_IMX_ESAI, /**< i.MX ESAI */
};
/* general purpose DAI configuration */
@ -70,6 +72,7 @@ struct sof_ipc_dai_config {
struct sof_ipc_dai_ssp_params ssp;
struct sof_ipc_dai_dmic_params dmic;
struct sof_ipc_dai_hda_params hda;
struct sof_ipc_dai_alh_params alh;
};
} __packed;

View File

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

View File

@ -75,6 +75,7 @@
#define SOF_TKN_INTEL_SSP_FRAME_PULSE_WIDTH 503
#define SOF_TKN_INTEL_SSP_QUIRKS 504
#define SOF_TKN_INTEL_SSP_TDM_PADDING_PER_SLOT 505
#define SOF_TKN_INTEL_SSP_BCLK_DELAY 506
/* DMIC */
#define SOF_TKN_INTEL_DMIC_DRIVER_VERSION 600
@ -105,4 +106,12 @@
/* for backward compatibility */
#define SOF_TKN_EFFECT_TYPE SOF_TKN_PROCESS_TYPE
/* SAI */
#define SOF_TKN_IMX_SAI_FIRST_TOKEN 1000
/* TODO: Add SAI tokens */
/* ESAI */
#define SOF_TKN_IMX_ESAI_FIRST_TOKEN 1100
/* TODO: Add ESAI tokens */
#endif

View File

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

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[] = {
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 = {

View File

@ -146,19 +146,24 @@ void amdtp_am824_set_midi_position(struct amdtp_stream *s,
}
EXPORT_SYMBOL_GPL(amdtp_am824_set_midi_position);
static void write_pcm_s32(struct amdtp_stream *s,
struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames)
static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames,
unsigned int pcm_frames)
{
struct amdtp_am824 *p = s->protocol;
unsigned int channels = p->pcm_channels;
struct snd_pcm_runtime *runtime = pcm->runtime;
unsigned int channels, remaining_frames, i, c;
unsigned int pcm_buffer_pointer;
int remaining_frames;
const u32 *src;
int i, c;
pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
pcm_buffer_pointer %= runtime->buffer_size;
channels = p->pcm_channels;
src = (void *)runtime->dma_area +
frames_to_bytes(runtime, s->pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
frames_to_bytes(runtime, pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
for (i = 0; i < frames; ++i) {
for (c = 0; c < channels; ++c) {
@ -172,19 +177,24 @@ static void write_pcm_s32(struct amdtp_stream *s,
}
}
static void read_pcm_s32(struct amdtp_stream *s,
struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames)
static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames,
unsigned int pcm_frames)
{
struct amdtp_am824 *p = s->protocol;
unsigned int channels = p->pcm_channels;
struct snd_pcm_runtime *runtime = pcm->runtime;
unsigned int channels, remaining_frames, i, c;
unsigned int pcm_buffer_pointer;
int remaining_frames;
u32 *dst;
int i, c;
pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
pcm_buffer_pointer %= runtime->buffer_size;
channels = p->pcm_channels;
dst = (void *)runtime->dma_area +
frames_to_bytes(runtime, s->pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
frames_to_bytes(runtime, pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
for (i = 0; i < frames; ++i) {
for (c = 0; c < channels; ++c) {
@ -284,7 +294,7 @@ static void midi_rate_use_one_byte(struct amdtp_stream *s, unsigned int port)
}
static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
unsigned int frames)
unsigned int frames, unsigned int data_block_counter)
{
struct amdtp_am824 *p = s->protocol;
unsigned int f, port;
@ -293,7 +303,7 @@ static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
for (f = 0; f < frames; f++) {
b = (u8 *)&buffer[p->midi_position];
port = (s->data_block_counter + f) % 8;
port = (data_block_counter + f) % 8;
if (f < MAX_MIDI_RX_BLOCKS &&
midi_ratelimit_per_packet(s, port) &&
p->midi[port] != NULL &&
@ -311,16 +321,20 @@ static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
}
}
static void read_midi_messages(struct amdtp_stream *s,
__be32 *buffer, unsigned int frames)
static void read_midi_messages(struct amdtp_stream *s, __be32 *buffer,
unsigned int frames, unsigned int data_block_counter)
{
struct amdtp_am824 *p = s->protocol;
unsigned int f, port;
int len;
u8 *b;
int f;
for (f = 0; f < frames; f++) {
port = (8 - s->ctx_data.tx.first_dbc + s->data_block_counter + f) % 8;
unsigned int port = f;
if (!(s->flags & CIP_UNALIGHED_DBC))
port += data_block_counter;
port %= 8;
b = (u8 *)&buffer[p->midi_position];
len = b[0] - 0x80;
@ -331,44 +345,61 @@ static void read_midi_messages(struct amdtp_stream *s,
}
}
static unsigned int process_rx_data_blocks(struct amdtp_stream *s, __be32 *buffer,
unsigned int data_blocks, unsigned int *syt)
static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
const struct pkt_desc *descs,
unsigned int packets,
struct snd_pcm_substream *pcm)
{
struct amdtp_am824 *p = s->protocol;
struct snd_pcm_substream *pcm = READ_ONCE(s->pcm);
unsigned int pcm_frames;
unsigned int pcm_frames = 0;
int i;
if (pcm) {
write_pcm_s32(s, pcm, buffer, data_blocks);
pcm_frames = data_blocks * p->frame_multiplier;
} else {
write_pcm_silence(s, buffer, data_blocks);
pcm_frames = 0;
for (i = 0; i < packets; ++i) {
const struct pkt_desc *desc = descs + i;
__be32 *buf = desc->ctx_payload;
unsigned int data_blocks = desc->data_blocks;
if (pcm) {
write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
pcm_frames += data_blocks * p->frame_multiplier;
} else {
write_pcm_silence(s, buf, data_blocks);
}
if (p->midi_ports) {
write_midi_messages(s, buf, data_blocks,
desc->data_block_counter);
}
}
if (p->midi_ports)
write_midi_messages(s, buffer, data_blocks);
return pcm_frames;
}
static unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffer,
unsigned int data_blocks, unsigned int *syt)
static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
const struct pkt_desc *descs,
unsigned int packets,
struct snd_pcm_substream *pcm)
{
struct amdtp_am824 *p = s->protocol;
struct snd_pcm_substream *pcm = READ_ONCE(s->pcm);
unsigned int pcm_frames;
unsigned int pcm_frames = 0;
int i;
if (pcm) {
read_pcm_s32(s, pcm, buffer, data_blocks);
pcm_frames = data_blocks * p->frame_multiplier;
} else {
pcm_frames = 0;
for (i = 0; i < packets; ++i) {
const struct pkt_desc *desc = descs + i;
__be32 *buf = desc->ctx_payload;
unsigned int data_blocks = desc->data_blocks;
if (pcm) {
read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
pcm_frames += data_blocks * p->frame_multiplier;
}
if (p->midi_ports) {
read_midi_messages(s, buf, data_blocks,
desc->data_block_counter);
}
}
if (p->midi_ports)
read_midi_messages(s, buffer, data_blocks);
return pcm_frames;
}
@ -383,15 +414,14 @@ static unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffe
int amdtp_am824_init(struct amdtp_stream *s, struct fw_unit *unit,
enum amdtp_stream_direction dir, enum cip_flags flags)
{
amdtp_stream_process_data_blocks_t process_data_blocks;
amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
if (dir == AMDTP_IN_STREAM)
process_data_blocks = process_tx_data_blocks;
process_ctx_payloads = process_ir_ctx_payloads;
else
process_data_blocks = process_rx_data_blocks;
process_ctx_payloads = process_it_ctx_payloads;
return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM,
process_data_blocks,
sizeof(struct amdtp_am824));
process_ctx_payloads, sizeof(struct amdtp_am824));
}
EXPORT_SYMBOL_GPL(amdtp_am824_init);

View File

@ -14,8 +14,8 @@
#include <linux/tracepoint.h>
TRACE_EVENT(amdtp_packet,
TP_PROTO(const struct amdtp_stream *s, u32 cycles, const __be32 *cip_header, unsigned int payload_length, unsigned int data_blocks, unsigned int index),
TP_ARGS(s, cycles, cip_header, payload_length, data_blocks, index),
TP_PROTO(const struct amdtp_stream *s, u32 cycles, const __be32 *cip_header, unsigned int payload_length, unsigned int data_blocks, unsigned int data_block_counter, unsigned int index),
TP_ARGS(s, cycles, cip_header, payload_length, data_blocks, data_block_counter, index),
TP_STRUCT__entry(
__field(unsigned int, second)
__field(unsigned int, cycle)
@ -47,7 +47,7 @@ TRACE_EVENT(amdtp_packet,
}
__entry->payload_quadlets = payload_length / sizeof(__be32);
__entry->data_blocks = data_blocks;
__entry->data_block_counter = s->data_block_counter,
__entry->data_block_counter = data_block_counter,
__entry->packet_index = s->packet_index;
__entry->irq = !!in_interrupt();
__entry->index = index;

View File

@ -74,16 +74,16 @@ static void pcm_period_tasklet(unsigned long data);
* @dir: the direction of stream
* @flags: the packet transmission method to use
* @fmt: the value of fmt field in CIP header
* @process_data_blocks: callback handler to process data blocks
* @process_ctx_payloads: callback handler to process payloads of isoc context
* @protocol_size: the size to allocate newly for protocol
*/
int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
enum amdtp_stream_direction dir, enum cip_flags flags,
unsigned int fmt,
amdtp_stream_process_data_blocks_t process_data_blocks,
amdtp_stream_process_ctx_payloads_t process_ctx_payloads,
unsigned int protocol_size)
{
if (process_data_blocks == NULL)
if (process_ctx_payloads == NULL)
return -EINVAL;
s->protocol = kzalloc(protocol_size, GFP_KERNEL);
@ -102,7 +102,10 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
s->callbacked = false;
s->fmt = fmt;
s->process_data_blocks = process_data_blocks;
s->process_ctx_payloads = process_ctx_payloads;
if (dir == AMDTP_OUT_STREAM)
s->ctx_data.rx.syt_override = -1;
return 0;
}
@ -473,12 +476,12 @@ static inline int queue_in_packet(struct amdtp_stream *s,
}
static void generate_cip_header(struct amdtp_stream *s, __be32 cip_header[2],
unsigned int syt)
unsigned int data_block_counter, unsigned int syt)
{
cip_header[0] = cpu_to_be32(READ_ONCE(s->source_node_id_field) |
(s->data_block_quadlets << CIP_DBS_SHIFT) |
((s->sph << CIP_SPH_SHIFT) & CIP_SPH_MASK) |
s->data_block_counter);
data_block_counter);
cip_header[1] = cpu_to_be32(CIP_EOH |
((s->fmt << CIP_FMT_SHIFT) & CIP_FMT_MASK) |
((s->ctx_data.rx.fdf << CIP_FDF_SHIFT) & CIP_FDF_MASK) |
@ -487,8 +490,9 @@ static void generate_cip_header(struct amdtp_stream *s, __be32 cip_header[2],
static void build_it_pkt_header(struct amdtp_stream *s, unsigned int cycle,
struct fw_iso_packet *params,
unsigned int data_blocks, unsigned int syt,
unsigned int index)
unsigned int data_blocks,
unsigned int data_block_counter,
unsigned int syt, unsigned int index)
{
unsigned int payload_length;
__be32 *cip_header;
@ -496,14 +500,9 @@ static void build_it_pkt_header(struct amdtp_stream *s, unsigned int cycle,
payload_length = data_blocks * sizeof(__be32) * s->data_block_quadlets;
params->payload_length = payload_length;
if (s->flags & CIP_DBC_IS_END_EVENT) {
s->data_block_counter =
(s->data_block_counter + data_blocks) & 0xff;
}
if (!(s->flags & CIP_NO_HEADER)) {
cip_header = (__be32 *)params->header;
generate_cip_header(s, cip_header, syt);
generate_cip_header(s, cip_header, data_block_counter, syt);
params->header_length = 2 * sizeof(__be32);
payload_length += params->header_length;
} else {
@ -511,23 +510,19 @@ static void build_it_pkt_header(struct amdtp_stream *s, unsigned int cycle,
}
trace_amdtp_packet(s, cycle, cip_header, payload_length, data_blocks,
index);
if (!(s->flags & CIP_DBC_IS_END_EVENT)) {
s->data_block_counter =
(s->data_block_counter + data_blocks) & 0xff;
}
data_block_counter, index);
}
static int check_cip_header(struct amdtp_stream *s, const __be32 *buf,
unsigned int payload_length,
unsigned int *data_blocks, unsigned int *dbc,
unsigned int *syt)
unsigned int *data_blocks,
unsigned int *data_block_counter, unsigned int *syt)
{
u32 cip_header[2];
unsigned int sph;
unsigned int fmt;
unsigned int fdf;
unsigned int dbc;
bool lost;
cip_header[0] = be32_to_cpu(buf[0]);
@ -579,17 +574,16 @@ static int check_cip_header(struct amdtp_stream *s, const __be32 *buf,
}
/* Check data block counter continuity */
*dbc = cip_header[0] & CIP_DBC_MASK;
dbc = cip_header[0] & CIP_DBC_MASK;
if (*data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) &&
s->data_block_counter != UINT_MAX)
*dbc = s->data_block_counter;
*data_block_counter != UINT_MAX)
dbc = *data_block_counter;
if (((s->flags & CIP_SKIP_DBC_ZERO_CHECK) &&
*dbc == s->ctx_data.tx.first_dbc) ||
s->data_block_counter == UINT_MAX) {
if ((dbc == 0x00 && (s->flags & CIP_SKIP_DBC_ZERO_CHECK)) ||
*data_block_counter == UINT_MAX) {
lost = false;
} else if (!(s->flags & CIP_DBC_IS_END_EVENT)) {
lost = *dbc != s->data_block_counter;
lost = dbc != *data_block_counter;
} else {
unsigned int dbc_interval;
@ -598,16 +592,18 @@ static int check_cip_header(struct amdtp_stream *s, const __be32 *buf,
else
dbc_interval = *data_blocks;
lost = *dbc != ((s->data_block_counter + dbc_interval) & 0xff);
lost = dbc != ((*data_block_counter + dbc_interval) & 0xff);
}
if (lost) {
dev_err(&s->unit->device,
"Detect discontinuity of CIP: %02X %02X\n",
s->data_block_counter, *dbc);
*data_block_counter, dbc);
return -EIO;
}
*data_block_counter = dbc;
*syt = cip_header[1] & CIP_SYT_MASK;
return 0;
@ -616,10 +612,10 @@ static int check_cip_header(struct amdtp_stream *s, const __be32 *buf,
static int parse_ir_ctx_header(struct amdtp_stream *s, unsigned int cycle,
const __be32 *ctx_header,
unsigned int *payload_length,
unsigned int *data_blocks, unsigned int *syt,
unsigned int index)
unsigned int *data_blocks,
unsigned int *data_block_counter,
unsigned int *syt, unsigned int index)
{
unsigned int dbc;
const __be32 *cip_header;
int err;
@ -635,7 +631,7 @@ static int parse_ir_ctx_header(struct amdtp_stream *s, unsigned int cycle,
if (!(s->flags & CIP_NO_HEADER)) {
cip_header = ctx_header + 2;
err = check_cip_header(s, cip_header, *payload_length,
data_blocks, &dbc, syt);
data_blocks, data_block_counter, syt);
if (err < 0)
return err;
} else {
@ -645,16 +641,12 @@ static int parse_ir_ctx_header(struct amdtp_stream *s, unsigned int cycle,
s->data_block_quadlets;
*syt = 0;
if (s->data_block_counter != UINT_MAX)
dbc = s->data_block_counter;
else
dbc = 0;
if (*data_block_counter == UINT_MAX)
*data_block_counter = 0;
}
s->data_block_counter = dbc;
trace_amdtp_packet(s, cycle, cip_header, *payload_length, *data_blocks,
index);
*data_block_counter, index);
return err;
}
@ -686,6 +678,80 @@ static inline u32 compute_it_cycle(const __be32 ctx_header_tstamp)
return increment_cycle_count(cycle, QUEUE_LENGTH);
}
static int generate_device_pkt_descs(struct amdtp_stream *s,
struct pkt_desc *descs,
const __be32 *ctx_header,
unsigned int packets)
{
unsigned int dbc = s->data_block_counter;
int i;
int err;
for (i = 0; i < packets; ++i) {
struct pkt_desc *desc = descs + i;
unsigned int index = (s->packet_index + i) % QUEUE_LENGTH;
unsigned int cycle;
unsigned int payload_length;
unsigned int data_blocks;
unsigned int syt;
cycle = compute_cycle_count(ctx_header[1]);
err = parse_ir_ctx_header(s, cycle, ctx_header, &payload_length,
&data_blocks, &dbc, &syt, i);
if (err < 0)
return err;
desc->cycle = cycle;
desc->syt = syt;
desc->data_blocks = data_blocks;
desc->data_block_counter = dbc;
desc->ctx_payload = s->buffer.packets[index].buffer;
if (!(s->flags & CIP_DBC_IS_END_EVENT))
dbc = (dbc + desc->data_blocks) & 0xff;
ctx_header +=
s->ctx_data.tx.ctx_header_size / sizeof(*ctx_header);
}
s->data_block_counter = dbc;
return 0;
}
static void generate_ideal_pkt_descs(struct amdtp_stream *s,
struct pkt_desc *descs,
const __be32 *ctx_header,
unsigned int packets)
{
unsigned int dbc = s->data_block_counter;
int i;
for (i = 0; i < packets; ++i) {
struct pkt_desc *desc = descs + i;
unsigned int index = (s->packet_index + i) % QUEUE_LENGTH;
desc->cycle = compute_it_cycle(*ctx_header);
desc->syt = calculate_syt(s, desc->cycle);
desc->data_blocks = calculate_data_blocks(s, desc->syt);
if (s->flags & CIP_DBC_IS_END_EVENT)
dbc = (dbc + desc->data_blocks) & 0xff;
desc->data_block_counter = dbc;
if (!(s->flags & CIP_DBC_IS_END_EVENT))
dbc = (dbc + desc->data_blocks) & 0xff;
desc->ctx_payload = s->buffer.packets[index].buffer;
++ctx_header;
}
s->data_block_counter = dbc;
}
static inline void cancel_stream(struct amdtp_stream *s)
{
s->packet_index = -1;
@ -694,6 +760,19 @@ static inline void cancel_stream(struct amdtp_stream *s)
WRITE_ONCE(s->pcm_buffer_pointer, SNDRV_PCM_POS_XRUN);
}
static void process_ctx_payloads(struct amdtp_stream *s,
const struct pkt_desc *descs,
unsigned int packets)
{
struct snd_pcm_substream *pcm;
unsigned int pcm_frames;
pcm = READ_ONCE(s->pcm);
pcm_frames = s->process_ctx_payloads(s, descs, packets, pcm);
if (pcm)
update_pcm_pointers(s, pcm, pcm_frames);
}
static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
size_t header_length, void *header,
void *private_data)
@ -706,38 +785,31 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
if (s->packet_index < 0)
return;
generate_ideal_pkt_descs(s, s->pkt_descs, ctx_header, packets);
process_ctx_payloads(s, s->pkt_descs, packets);
for (i = 0; i < packets; ++i) {
u32 cycle;
const struct pkt_desc *desc = s->pkt_descs + i;
unsigned int syt;
unsigned int data_blocks;
__be32 *buffer;
unsigned int pcm_frames;
struct {
struct fw_iso_packet params;
__be32 header[IT_PKT_HEADER_SIZE_CIP / sizeof(__be32)];
} template = { {0}, {0} };
struct snd_pcm_substream *pcm;
cycle = compute_it_cycle(*ctx_header);
syt = calculate_syt(s, cycle);
data_blocks = calculate_data_blocks(s, syt);
buffer = s->buffer.packets[s->packet_index].buffer;
pcm_frames = s->process_data_blocks(s, buffer, data_blocks,
&syt);
if (s->ctx_data.rx.syt_override < 0)
syt = desc->syt;
else
syt = s->ctx_data.rx.syt_override;
build_it_pkt_header(s, cycle, &template.params, data_blocks,
build_it_pkt_header(s, desc->cycle, &template.params,
desc->data_blocks, desc->data_block_counter,
syt, i);
if (queue_out_packet(s, &template.params) < 0) {
cancel_stream(s);
return;
}
pcm = READ_ONCE(s->pcm);
if (pcm && pcm_frames > 0)
update_pcm_pointers(s, pcm, pcm_frames);
++ctx_header;
}
fw_iso_context_queue_flush(s->context);
@ -748,8 +820,10 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
void *private_data)
{
struct amdtp_stream *s = private_data;
unsigned int i, packets;
unsigned int packets;
__be32 *ctx_header = header;
int i;
int err;
if (s->packet_index < 0)
return;
@ -757,48 +831,23 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
// The number of packets in buffer.
packets = header_length / s->ctx_data.tx.ctx_header_size;
for (i = 0; i < packets; i++) {
u32 cycle;
unsigned int payload_length;
unsigned int data_blocks;
unsigned int syt;
__be32 *buffer;
unsigned int pcm_frames = 0;
struct fw_iso_packet params = {0};
struct snd_pcm_substream *pcm;
int err;
cycle = compute_cycle_count(ctx_header[1]);
err = parse_ir_ctx_header(s, cycle, ctx_header, &payload_length,
&data_blocks, &syt, i);
if (err < 0 && err != -EAGAIN)
break;
if (err >= 0) {
buffer = s->buffer.packets[s->packet_index].buffer;
pcm_frames = s->process_data_blocks(s, buffer,
data_blocks, &syt);
if (!(s->flags & CIP_DBC_IS_END_EVENT)) {
s->data_block_counter += data_blocks;
s->data_block_counter &= 0xff;
}
err = generate_device_pkt_descs(s, s->pkt_descs, ctx_header, packets);
if (err < 0) {
if (err != -EAGAIN) {
cancel_stream(s);
return;
}
if (queue_in_packet(s, &params) < 0)
break;
pcm = READ_ONCE(s->pcm);
if (pcm && pcm_frames > 0)
update_pcm_pointers(s, pcm, pcm_frames);
ctx_header += s->ctx_data.tx.ctx_header_size / sizeof(*ctx_header);
} else {
process_ctx_payloads(s, s->pkt_descs, packets);
}
/* Queueing error or detecting invalid payload. */
if (i < packets) {
cancel_stream(s);
return;
for (i = 0; i < packets; ++i) {
struct fw_iso_packet params = {0};
if (queue_in_packet(s, &params) < 0) {
cancel_stream(s);
return;
}
}
fw_iso_context_queue_flush(s->context);
@ -845,7 +894,7 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
* amdtp_stream_set_parameters() and it must be started before any PCM or MIDI
* device can be started.
*/
int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
{
static const struct {
unsigned int data_block;
@ -932,6 +981,13 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
else
s->tag = TAG_CIP;
s->pkt_descs = kcalloc(INTERRUPT_INTERVAL, sizeof(*s->pkt_descs),
GFP_KERNEL);
if (!s->pkt_descs) {
err = -ENOMEM;
goto err_context;
}
s->packet_index = 0;
do {
struct fw_iso_packet params;
@ -943,7 +999,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
err = queue_out_packet(s, &params);
}
if (err < 0)
goto err_context;
goto err_pkt_descs;
} while (s->packet_index > 0);
/* NOTE: TAG1 matches CIP. This just affects in stream. */
@ -954,12 +1010,13 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
s->callbacked = false;
err = fw_iso_context_start(s->context, -1, 0, tag);
if (err < 0)
goto err_context;
goto err_pkt_descs;
mutex_unlock(&s->mutex);
return 0;
err_pkt_descs:
kfree(s->pkt_descs);
err_context:
fw_iso_context_destroy(s->context);
s->context = ERR_PTR(-1);
@ -970,7 +1027,6 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
return err;
}
EXPORT_SYMBOL(amdtp_stream_start);
/**
* amdtp_stream_pcm_pointer - get the PCM buffer position
@ -1041,7 +1097,7 @@ EXPORT_SYMBOL(amdtp_stream_update);
* All PCM and MIDI devices of the stream must be stopped before the stream
* itself can be stopped.
*/
void amdtp_stream_stop(struct amdtp_stream *s)
static void amdtp_stream_stop(struct amdtp_stream *s)
{
mutex_lock(&s->mutex);
@ -1055,12 +1111,12 @@ void amdtp_stream_stop(struct amdtp_stream *s)
fw_iso_context_destroy(s->context);
s->context = ERR_PTR(-1);
iso_packets_buffer_destroy(&s->buffer, s->unit);
kfree(s->pkt_descs);
s->callbacked = false;
mutex_unlock(&s->mutex);
}
EXPORT_SYMBOL(amdtp_stream_stop);
/**
* amdtp_stream_pcm_abort - abort the running PCM device
@ -1078,3 +1134,92 @@ void amdtp_stream_pcm_abort(struct amdtp_stream *s)
snd_pcm_stop_xrun(pcm);
}
EXPORT_SYMBOL(amdtp_stream_pcm_abort);
/**
* amdtp_domain_init - initialize an AMDTP domain structure
* @d: the AMDTP domain to initialize.
*/
int amdtp_domain_init(struct amdtp_domain *d)
{
INIT_LIST_HEAD(&d->streams);
return 0;
}
EXPORT_SYMBOL_GPL(amdtp_domain_init);
/**
* amdtp_domain_destroy - destroy an AMDTP domain structure
* @d: the AMDTP domain to destroy.
*/
void amdtp_domain_destroy(struct amdtp_domain *d)
{
// At present nothing to do.
return;
}
EXPORT_SYMBOL_GPL(amdtp_domain_destroy);
/**
* amdtp_domain_add_stream - register isoc context into the domain.
* @d: the AMDTP domain.
* @s: the AMDTP stream.
* @channel: the isochronous channel on the bus.
* @speed: firewire speed code.
*/
int amdtp_domain_add_stream(struct amdtp_domain *d, struct amdtp_stream *s,
int channel, int speed)
{
struct amdtp_stream *tmp;
list_for_each_entry(tmp, &d->streams, list) {
if (s == tmp)
return -EBUSY;
}
list_add(&s->list, &d->streams);
s->channel = channel;
s->speed = speed;
return 0;
}
EXPORT_SYMBOL_GPL(amdtp_domain_add_stream);
/**
* amdtp_domain_start - start sending packets for isoc context in the domain.
* @d: the AMDTP domain.
*/
int amdtp_domain_start(struct amdtp_domain *d)
{
struct amdtp_stream *s;
int err = 0;
list_for_each_entry(s, &d->streams, list) {
err = amdtp_stream_start(s, s->channel, s->speed);
if (err < 0)
break;
}
if (err < 0) {
list_for_each_entry(s, &d->streams, list)
amdtp_stream_stop(s);
}
return err;
}
EXPORT_SYMBOL_GPL(amdtp_domain_start);
/**
* amdtp_domain_stop - stop sending packets for isoc context in the same domain.
* @d: the AMDTP domain to which the isoc contexts belong.
*/
void amdtp_domain_stop(struct amdtp_domain *d)
{
struct amdtp_stream *s, *next;
list_for_each_entry_safe(s, next, &d->streams, list) {
list_del(&s->list);
amdtp_stream_stop(s);
}
}
EXPORT_SYMBOL_GPL(amdtp_domain_stop);

View File

@ -33,6 +33,8 @@
* @CIP_HEADER_WITHOUT_EOH: Only for in-stream. CIP Header doesn't include
* valid EOH.
* @CIP_NO_HEADERS: a lack of headers in packets
* @CIP_UNALIGHED_DBC: Only for in-stream. The value of dbc is not alighed to
* the value of current SYT_INTERVAL; e.g. initial value is not zero.
*/
enum cip_flags {
CIP_NONBLOCKING = 0x00,
@ -45,6 +47,7 @@ enum cip_flags {
CIP_JUMBO_PAYLOAD = 0x40,
CIP_HEADER_WITHOUT_EOH = 0x80,
CIP_NO_HEADER = 0x100,
CIP_UNALIGHED_DBC = 0x200,
};
/**
@ -91,12 +94,20 @@ enum amdtp_stream_direction {
AMDTP_IN_STREAM
};
struct pkt_desc {
u32 cycle;
u32 syt;
unsigned int data_blocks;
unsigned int data_block_counter;
__be32 *ctx_payload;
};
struct amdtp_stream;
typedef unsigned int (*amdtp_stream_process_data_blocks_t)(
typedef unsigned int (*amdtp_stream_process_ctx_payloads_t)(
struct amdtp_stream *s,
__be32 *buffer,
unsigned int data_blocks,
unsigned int *syt);
const struct pkt_desc *desc,
unsigned int packets,
struct snd_pcm_substream *pcm);
struct amdtp_stream {
struct fw_unit *unit;
enum cip_flags flags;
@ -107,6 +118,7 @@ struct amdtp_stream {
struct fw_iso_context *context;
struct iso_packets_buffer buffer;
int packet_index;
struct pkt_desc *pkt_descs;
int tag;
union {
struct {
@ -119,8 +131,6 @@ struct amdtp_stream {
// Fixed interval of dbc between previos/current
// packets.
unsigned int dbc_interval;
// Indicate the value of dbc field in a first packet.
unsigned int first_dbc;
} tx;
struct {
// To calculate CIP data blocks and tstamp.
@ -131,6 +141,7 @@ struct amdtp_stream {
// To generate CIP header.
unsigned int fdf;
int syt_override;
} rx;
} ctx_data;
@ -158,13 +169,18 @@ struct amdtp_stream {
/* For backends to process data blocks. */
void *protocol;
amdtp_stream_process_data_blocks_t process_data_blocks;
amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
// For domain.
int channel;
int speed;
struct list_head list;
};
int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
enum amdtp_stream_direction dir, enum cip_flags flags,
unsigned int fmt,
amdtp_stream_process_data_blocks_t process_data_blocks,
amdtp_stream_process_ctx_payloads_t process_ctx_payloads,
unsigned int protocol_size);
void amdtp_stream_destroy(struct amdtp_stream *s);
@ -172,9 +188,7 @@ int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate,
unsigned int data_block_quadlets);
unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s);
int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed);
void amdtp_stream_update(struct amdtp_stream *s);
void amdtp_stream_stop(struct amdtp_stream *s);
int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
struct snd_pcm_runtime *runtime);
@ -256,4 +270,17 @@ static inline bool amdtp_stream_wait_callback(struct amdtp_stream *s,
msecs_to_jiffies(timeout)) > 0;
}
struct amdtp_domain {
struct list_head streams;
};
int amdtp_domain_init(struct amdtp_domain *d);
void amdtp_domain_destroy(struct amdtp_domain *d);
int amdtp_domain_add_stream(struct amdtp_domain *d, struct amdtp_stream *s,
int channel, int speed);
int amdtp_domain_start(struct amdtp_domain *d);
void amdtp_domain_stop(struct amdtp_domain *d);
#endif

View File

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

View File

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

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

View File

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

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

View File

@ -126,9 +126,6 @@ static void finish_session(struct snd_dg00x *dg00x)
{
__be32 data;
amdtp_stream_stop(&dg00x->tx_stream);
amdtp_stream_stop(&dg00x->rx_stream);
data = cpu_to_be32(0x00000003);
snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_SET,
@ -218,29 +215,59 @@ static int keep_resources(struct snd_dg00x *dg00x, struct amdtp_stream *stream,
fw_parent_device(dg00x->unit)->max_speed);
}
static int init_stream(struct snd_dg00x *dg00x, struct amdtp_stream *s)
{
struct fw_iso_resources *resources;
enum amdtp_stream_direction dir;
int err;
if (s == &dg00x->tx_stream) {
resources = &dg00x->tx_resources;
dir = AMDTP_IN_STREAM;
} else {
resources = &dg00x->rx_resources;
dir = AMDTP_OUT_STREAM;
}
err = fw_iso_resources_init(resources, dg00x->unit);
if (err < 0)
return err;
err = amdtp_dot_init(s, dg00x->unit, dir);
if (err < 0)
fw_iso_resources_destroy(resources);
return err;
}
static void destroy_stream(struct snd_dg00x *dg00x, struct amdtp_stream *s)
{
amdtp_stream_destroy(s);
if (s == &dg00x->tx_stream)
fw_iso_resources_destroy(&dg00x->tx_resources);
else
fw_iso_resources_destroy(&dg00x->rx_resources);
}
int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x)
{
int err;
/* For out-stream. */
err = fw_iso_resources_init(&dg00x->rx_resources, dg00x->unit);
err = init_stream(dg00x, &dg00x->rx_stream);
if (err < 0)
goto error;
err = amdtp_dot_init(&dg00x->rx_stream, dg00x->unit, AMDTP_OUT_STREAM);
if (err < 0)
goto error;
return err;
/* For in-stream. */
err = fw_iso_resources_init(&dg00x->tx_resources, dg00x->unit);
err = init_stream(dg00x, &dg00x->tx_stream);
if (err < 0)
goto error;
err = amdtp_dot_init(&dg00x->tx_stream, dg00x->unit, AMDTP_IN_STREAM);
if (err < 0)
goto error;
destroy_stream(dg00x, &dg00x->rx_stream);
err = amdtp_domain_init(&dg00x->domain);
if (err < 0) {
destroy_stream(dg00x, &dg00x->rx_stream);
destroy_stream(dg00x, &dg00x->tx_stream);
}
return 0;
error:
snd_dg00x_stream_destroy_duplex(dg00x);
return err;
}
@ -250,11 +277,10 @@ int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x)
*/
void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x)
{
amdtp_stream_destroy(&dg00x->rx_stream);
fw_iso_resources_destroy(&dg00x->rx_resources);
amdtp_domain_destroy(&dg00x->domain);
amdtp_stream_destroy(&dg00x->tx_stream);
fw_iso_resources_destroy(&dg00x->tx_resources);
destroy_stream(dg00x, &dg00x->rx_stream);
destroy_stream(dg00x, &dg00x->tx_stream);
}
int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate)
@ -269,6 +295,8 @@ int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate)
rate = curr_rate;
if (dg00x->substreams_counter == 0 || curr_rate != rate) {
amdtp_domain_stop(&dg00x->domain);
finish_session(dg00x);
fw_iso_resources_free(&dg00x->tx_resources);
@ -301,8 +329,10 @@ int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x)
return 0;
if (amdtp_streaming_error(&dg00x->tx_stream) ||
amdtp_streaming_error(&dg00x->rx_stream))
amdtp_streaming_error(&dg00x->rx_stream)) {
amdtp_domain_stop(&dg00x->domain);
finish_session(dg00x);
}
if (generation != fw_parent_device(dg00x->unit)->card->generation) {
err = fw_iso_resources_update(&dg00x->tx_resources);
@ -319,36 +349,30 @@ int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x)
* which source of clock is used.
*/
if (!amdtp_stream_running(&dg00x->rx_stream)) {
int spd = fw_parent_device(dg00x->unit)->max_speed;
err = begin_session(dg00x);
if (err < 0)
goto error;
err = amdtp_stream_start(&dg00x->rx_stream,
dg00x->rx_resources.channel,
fw_parent_device(dg00x->unit)->max_speed);
err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->rx_stream,
dg00x->rx_resources.channel, spd);
if (err < 0)
goto error;
err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->tx_stream,
dg00x->tx_resources.channel, spd);
if (err < 0)
goto error;
err = amdtp_domain_start(&dg00x->domain);
if (err < 0)
goto error;
if (!amdtp_stream_wait_callback(&dg00x->rx_stream,
CALLBACK_TIMEOUT)) {
err = -ETIMEDOUT;
goto error;
}
}
/*
* The value of SYT field in transmitted packets is always 0x0000. Thus,
* duplex streams with timestamp synchronization cannot be built.
*/
if (!amdtp_stream_running(&dg00x->tx_stream)) {
err = amdtp_stream_start(&dg00x->tx_stream,
dg00x->tx_resources.channel,
fw_parent_device(dg00x->unit)->max_speed);
if (err < 0)
goto error;
if (!amdtp_stream_wait_callback(&dg00x->tx_stream,
CALLBACK_TIMEOUT)) {
CALLBACK_TIMEOUT) ||
!amdtp_stream_wait_callback(&dg00x->tx_stream,
CALLBACK_TIMEOUT)) {
err = -ETIMEDOUT;
goto error;
}
@ -356,6 +380,7 @@ int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x)
return 0;
error:
amdtp_domain_stop(&dg00x->domain);
finish_session(dg00x);
return err;
@ -364,6 +389,7 @@ int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x)
void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x)
{
if (dg00x->substreams_counter == 0) {
amdtp_domain_stop(&dg00x->domain);
finish_session(dg00x);
fw_iso_resources_free(&dg00x->tx_resources);

View File

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

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);
}
static void write_pcm_s32(struct amdtp_stream *s,
struct snd_pcm_substream *pcm,
__le32 *buffer, unsigned int frames)
static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
__le32 *buffer, unsigned int frames,
unsigned int pcm_frames)
{
struct amdtp_ff *p = s->protocol;
unsigned int channels = p->pcm_channels;
struct snd_pcm_runtime *runtime = pcm->runtime;
unsigned int channels, remaining_frames, i, c;
unsigned int pcm_buffer_pointer;
int remaining_frames;
const u32 *src;
int i, c;
pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
pcm_buffer_pointer %= runtime->buffer_size;
channels = p->pcm_channels;
src = (void *)runtime->dma_area +
frames_to_bytes(runtime, s->pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
frames_to_bytes(runtime, pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
for (i = 0; i < frames; ++i) {
for (c = 0; c < channels; ++c) {
@ -52,19 +57,24 @@ static void write_pcm_s32(struct amdtp_stream *s,
}
}
static void read_pcm_s32(struct amdtp_stream *s,
struct snd_pcm_substream *pcm,
__le32 *buffer, unsigned int frames)
static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
__le32 *buffer, unsigned int frames,
unsigned int pcm_frames)
{
struct amdtp_ff *p = s->protocol;
unsigned int channels = p->pcm_channels;
struct snd_pcm_runtime *runtime = pcm->runtime;
unsigned int channels, remaining_frames, i, c;
unsigned int pcm_buffer_pointer;
int remaining_frames;
u32 *dst;
int i, c;
pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
pcm_buffer_pointer %= runtime->buffer_size;
channels = p->pcm_channels;
dst = (void *)runtime->dma_area +
frames_to_bytes(runtime, s->pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
frames_to_bytes(runtime, pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
for (i = 0; i < frames; ++i) {
for (c = 0; c < channels; ++c) {
@ -102,38 +112,47 @@ int amdtp_ff_add_pcm_hw_constraints(struct amdtp_stream *s,
return amdtp_stream_add_pcm_hw_constraints(s, runtime);
}
static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
__be32 *buffer,
unsigned int data_blocks,
unsigned int *syt)
static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
const struct pkt_desc *descs,
unsigned int packets,
struct snd_pcm_substream *pcm)
{
struct snd_pcm_substream *pcm = READ_ONCE(s->pcm);
unsigned int pcm_frames;
unsigned int pcm_frames = 0;
int i;
if (pcm) {
write_pcm_s32(s, pcm, (__le32 *)buffer, data_blocks);
pcm_frames = data_blocks;
} else {
write_pcm_silence(s, (__le32 *)buffer, data_blocks);
pcm_frames = 0;
for (i = 0; i < packets; ++i) {
const struct pkt_desc *desc = descs + i;
__le32 *buf = (__le32 *)desc->ctx_payload;
unsigned int data_blocks = desc->data_blocks;
if (pcm) {
write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
pcm_frames += data_blocks;
} else {
write_pcm_silence(s, buf, data_blocks);
}
}
return pcm_frames;
}
static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
__be32 *buffer,
unsigned int data_blocks,
unsigned int *syt)
static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
const struct pkt_desc *descs,
unsigned int packets,
struct snd_pcm_substream *pcm)
{
struct snd_pcm_substream *pcm = READ_ONCE(s->pcm);
unsigned int pcm_frames;
unsigned int pcm_frames = 0;
int i;
if (pcm) {
read_pcm_s32(s, pcm, (__le32 *)buffer, data_blocks);
pcm_frames = data_blocks;
} else {
pcm_frames = 0;
for (i = 0; i < packets; ++i) {
const struct pkt_desc *desc = descs + i;
__le32 *buf = (__le32 *)desc->ctx_payload;
unsigned int data_blocks = desc->data_blocks;
if (pcm) {
read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
pcm_frames += data_blocks;
}
}
return pcm_frames;
@ -142,13 +161,13 @@ static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
int amdtp_ff_init(struct amdtp_stream *s, struct fw_unit *unit,
enum amdtp_stream_direction dir)
{
amdtp_stream_process_data_blocks_t process_data_blocks;
amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
if (dir == AMDTP_IN_STREAM)
process_data_blocks = process_tx_data_blocks;
process_ctx_payloads = process_ir_ctx_payloads;
else
process_data_blocks = process_rx_data_blocks;
process_ctx_payloads = process_it_ctx_payloads;
return amdtp_stream_init(s, unit, dir, CIP_NO_HEADER, 0,
process_data_blocks, sizeof(struct amdtp_ff));
process_ctx_payloads, sizeof(struct amdtp_ff));
}

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

View File

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

View File

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

View File

@ -8,8 +8,7 @@
#define CALLBACK_TIMEOUT 100
static int
init_stream(struct snd_efw *efw, struct amdtp_stream *stream)
static int init_stream(struct snd_efw *efw, struct amdtp_stream *stream)
{
struct cmp_connection *conn;
enum cmp_direction c_dir;
@ -28,28 +27,40 @@ init_stream(struct snd_efw *efw, struct amdtp_stream *stream)
err = cmp_connection_init(conn, efw->unit, c_dir, 0);
if (err < 0)
goto end;
return err;
err = amdtp_am824_init(stream, efw->unit, s_dir, CIP_BLOCKING);
if (err < 0) {
amdtp_stream_destroy(stream);
cmp_connection_destroy(conn);
return err;
}
end:
if (stream == &efw->tx_stream) {
// Fireworks transmits NODATA packets with TAG0.
efw->tx_stream.flags |= CIP_EMPTY_WITH_TAG0;
// Fireworks has its own meaning for dbc.
efw->tx_stream.flags |= CIP_DBC_IS_END_EVENT;
// Fireworks reset dbc at bus reset.
efw->tx_stream.flags |= CIP_SKIP_DBC_ZERO_CHECK;
// But Recent firmwares starts packets with non-zero dbc.
// Driver version 5.7.6 installs firmware version 5.7.3.
if (efw->is_fireworks3 &&
(efw->firmware_version == 0x5070000 ||
efw->firmware_version == 0x5070300 ||
efw->firmware_version == 0x5080000))
efw->tx_stream.flags |= CIP_UNALIGHED_DBC;
// AudioFire9 always reports wrong dbs.
if (efw->is_af9)
efw->tx_stream.flags |= CIP_WRONG_DBS;
// Firmware version 5.5 reports fixed interval for dbc.
if (efw->firmware_version == 0x5050000)
efw->tx_stream.ctx_data.tx.dbc_interval = 8;
}
return err;
}
static void
stop_stream(struct snd_efw *efw, struct amdtp_stream *stream)
{
amdtp_stream_stop(stream);
if (stream == &efw->tx_stream)
cmp_connection_break(&efw->out_conn);
else
cmp_connection_break(&efw->in_conn);
}
static int start_stream(struct snd_efw *efw, struct amdtp_stream *stream,
unsigned int rate)
{
@ -67,38 +78,26 @@ static int start_stream(struct snd_efw *efw, struct amdtp_stream *stream,
return err;
// Start amdtp stream.
err = amdtp_stream_start(stream, conn->resources.channel, conn->speed);
err = amdtp_domain_add_stream(&efw->domain, stream,
conn->resources.channel, conn->speed);
if (err < 0) {
cmp_connection_break(conn);
return err;
}
// Wait first callback.
if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) {
amdtp_stream_stop(stream);
cmp_connection_break(conn);
return -ETIMEDOUT;
}
return 0;
}
/*
* This function should be called before starting the stream or after stopping
* the streams.
*/
static void
destroy_stream(struct snd_efw *efw, struct amdtp_stream *stream)
// This function should be called before starting the stream or after stopping
// the streams.
static void destroy_stream(struct snd_efw *efw, struct amdtp_stream *stream)
{
struct cmp_connection *conn;
amdtp_stream_destroy(stream);
if (stream == &efw->tx_stream)
conn = &efw->out_conn;
cmp_connection_destroy(&efw->out_conn);
else
conn = &efw->in_conn;
amdtp_stream_destroy(stream);
cmp_connection_destroy(conn);
cmp_connection_destroy(&efw->in_conn);
}
static int
@ -131,42 +130,28 @@ int snd_efw_stream_init_duplex(struct snd_efw *efw)
err = init_stream(efw, &efw->tx_stream);
if (err < 0)
goto end;
/* Fireworks transmits NODATA packets with TAG0. */
efw->tx_stream.flags |= CIP_EMPTY_WITH_TAG0;
/* Fireworks has its own meaning for dbc. */
efw->tx_stream.flags |= CIP_DBC_IS_END_EVENT;
/* Fireworks reset dbc at bus reset. */
efw->tx_stream.flags |= CIP_SKIP_DBC_ZERO_CHECK;
/*
* But Recent firmwares starts packets with non-zero dbc.
* Driver version 5.7.6 installs firmware version 5.7.3.
*/
if (efw->is_fireworks3 &&
(efw->firmware_version == 0x5070000 ||
efw->firmware_version == 0x5070300 ||
efw->firmware_version == 0x5080000))
efw->tx_stream.ctx_data.tx.first_dbc = 0x02;
/* AudioFire9 always reports wrong dbs. */
if (efw->is_af9)
efw->tx_stream.flags |= CIP_WRONG_DBS;
/* Firmware version 5.5 reports fixed interval for dbc. */
if (efw->firmware_version == 0x5050000)
efw->tx_stream.ctx_data.tx.dbc_interval = 8;
return err;
err = init_stream(efw, &efw->rx_stream);
if (err < 0) {
destroy_stream(efw, &efw->tx_stream);
goto end;
return err;
}
/* set IEC61883 compliant mode (actually not fully compliant...) */
err = amdtp_domain_init(&efw->domain);
if (err < 0) {
destroy_stream(efw, &efw->tx_stream);
destroy_stream(efw, &efw->rx_stream);
return err;
}
// set IEC61883 compliant mode (actually not fully compliant...).
err = snd_efw_command_set_tx_mode(efw, SND_EFW_TRANSPORT_MODE_IEC61883);
if (err < 0) {
destroy_stream(efw, &efw->tx_stream);
destroy_stream(efw, &efw->rx_stream);
}
end:
return err;
}
@ -214,8 +199,10 @@ int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate)
if (rate == 0)
rate = curr_rate;
if (rate != curr_rate) {
stop_stream(efw, &efw->tx_stream);
stop_stream(efw, &efw->rx_stream);
amdtp_domain_stop(&efw->domain);
cmp_connection_break(&efw->out_conn);
cmp_connection_break(&efw->in_conn);
cmp_connection_release(&efw->out_conn);
cmp_connection_release(&efw->in_conn);
@ -255,47 +242,57 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw)
if (efw->substreams_counter == 0)
return -EIO;
if (amdtp_streaming_error(&efw->rx_stream) ||
amdtp_streaming_error(&efw->tx_stream)) {
amdtp_domain_stop(&efw->domain);
cmp_connection_break(&efw->out_conn);
cmp_connection_break(&efw->in_conn);
}
err = snd_efw_command_get_sampling_rate(efw, &rate);
if (err < 0)
return err;
if (amdtp_streaming_error(&efw->rx_stream) ||
amdtp_streaming_error(&efw->tx_stream)) {
stop_stream(efw, &efw->rx_stream);
stop_stream(efw, &efw->tx_stream);
}
/* master should be always running */
if (!amdtp_stream_running(&efw->rx_stream)) {
err = start_stream(efw, &efw->rx_stream, rate);
if (err < 0) {
dev_err(&efw->unit->device,
"fail to start AMDTP master stream:%d\n", err);
if (err < 0)
goto error;
}
}
if (!amdtp_stream_running(&efw->tx_stream)) {
err = start_stream(efw, &efw->tx_stream, rate);
if (err < 0) {
dev_err(&efw->unit->device,
"fail to start AMDTP slave stream:%d\n", err);
if (err < 0)
goto error;
err = amdtp_domain_start(&efw->domain);
if (err < 0)
goto error;
// Wait first callback.
if (!amdtp_stream_wait_callback(&efw->rx_stream,
CALLBACK_TIMEOUT) ||
!amdtp_stream_wait_callback(&efw->tx_stream,
CALLBACK_TIMEOUT)) {
err = -ETIMEDOUT;
goto error;
}
}
return 0;
error:
stop_stream(efw, &efw->rx_stream);
stop_stream(efw, &efw->tx_stream);
amdtp_domain_stop(&efw->domain);
cmp_connection_break(&efw->out_conn);
cmp_connection_break(&efw->in_conn);
return err;
}
void snd_efw_stream_stop_duplex(struct snd_efw *efw)
{
if (efw->substreams_counter == 0) {
stop_stream(efw, &efw->tx_stream);
stop_stream(efw, &efw->rx_stream);
amdtp_domain_stop(&efw->domain);
cmp_connection_break(&efw->out_conn);
cmp_connection_break(&efw->in_conn);
cmp_connection_release(&efw->out_conn);
cmp_connection_release(&efw->in_conn);
@ -304,18 +301,19 @@ void snd_efw_stream_stop_duplex(struct snd_efw *efw)
void snd_efw_stream_update_duplex(struct snd_efw *efw)
{
if (cmp_connection_update(&efw->out_conn) < 0 ||
cmp_connection_update(&efw->in_conn) < 0) {
stop_stream(efw, &efw->rx_stream);
stop_stream(efw, &efw->tx_stream);
} else {
amdtp_stream_update(&efw->rx_stream);
amdtp_stream_update(&efw->tx_stream);
}
amdtp_domain_stop(&efw->domain);
cmp_connection_break(&efw->out_conn);
cmp_connection_break(&efw->in_conn);
amdtp_stream_pcm_abort(&efw->rx_stream);
amdtp_stream_pcm_abort(&efw->tx_stream);
}
void snd_efw_stream_destroy_duplex(struct snd_efw *efw)
{
amdtp_domain_destroy(&efw->domain);
destroy_stream(efw, &efw->rx_stream);
destroy_stream(efw, &efw->tx_stream);
}

View File

@ -117,19 +117,25 @@ int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
return 0;
}
static void read_pcm_s32(struct amdtp_stream *s,
struct snd_pcm_runtime *runtime,
__be32 *buffer, unsigned int data_blocks)
static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int data_blocks,
unsigned int pcm_frames)
{
struct amdtp_motu *p = s->protocol;
unsigned int channels, remaining_frames, i, c;
unsigned int channels = p->pcm_chunks;
struct snd_pcm_runtime *runtime = pcm->runtime;
unsigned int pcm_buffer_pointer;
int remaining_frames;
u8 *byte;
u32 *dst;
int i, c;
pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
pcm_buffer_pointer %= runtime->buffer_size;
channels = p->pcm_chunks;
dst = (void *)runtime->dma_area +
frames_to_bytes(runtime, s->pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
frames_to_bytes(runtime, pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
for (i = 0; i < data_blocks; ++i) {
byte = (u8 *)buffer + p->pcm_byte_offset;
@ -147,19 +153,25 @@ static void read_pcm_s32(struct amdtp_stream *s,
}
}
static void write_pcm_s32(struct amdtp_stream *s,
struct snd_pcm_runtime *runtime,
__be32 *buffer, unsigned int data_blocks)
static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int data_blocks,
unsigned int pcm_frames)
{
struct amdtp_motu *p = s->protocol;
unsigned int channels, remaining_frames, i, c;
unsigned int channels = p->pcm_chunks;
struct snd_pcm_runtime *runtime = pcm->runtime;
unsigned int pcm_buffer_pointer;
int remaining_frames;
u8 *byte;
const u32 *src;
int i, c;
pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
pcm_buffer_pointer %= runtime->buffer_size;
channels = p->pcm_chunks;
src = (void *)runtime->dma_area +
frames_to_bytes(runtime, s->pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
frames_to_bytes(runtime, pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
for (i = 0; i < data_blocks; ++i) {
byte = (u8 *)buffer + p->pcm_byte_offset;
@ -298,24 +310,52 @@ static void __maybe_unused copy_message(u64 *frames, __be32 *buffer,
}
}
static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
__be32 *buffer, unsigned int data_blocks,
unsigned int *syt)
static void probe_tracepoints_events(struct amdtp_stream *s,
const struct pkt_desc *descs,
unsigned int packets)
{
int i;
for (i = 0; i < packets; ++i) {
const struct pkt_desc *desc = descs + i;
__be32 *buf = desc->ctx_payload;
unsigned int data_blocks = desc->data_blocks;
trace_data_block_sph(s, data_blocks, buf);
trace_data_block_message(s, data_blocks, buf);
}
}
static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
const struct pkt_desc *descs,
unsigned int packets,
struct snd_pcm_substream *pcm)
{
struct amdtp_motu *p = s->protocol;
struct snd_pcm_substream *pcm;
unsigned int pcm_frames = 0;
int i;
trace_data_block_sph(s, data_blocks, buffer);
trace_data_block_message(s, data_blocks, buffer);
// For data block processing.
for (i = 0; i < packets; ++i) {
const struct pkt_desc *desc = descs + i;
__be32 *buf = desc->ctx_payload;
unsigned int data_blocks = desc->data_blocks;
if (p->midi_ports)
read_midi_messages(s, buffer, data_blocks);
if (pcm) {
read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
pcm_frames += data_blocks;
}
pcm = READ_ONCE(s->pcm);
if (data_blocks > 0 && pcm)
read_pcm_s32(s, pcm->runtime, buffer, data_blocks);
if (p->midi_ports)
read_midi_messages(s, buf, data_blocks);
}
return data_blocks;
// For tracepoints.
if (trace_data_block_sph_enabled() ||
trace_data_block_message_enabled())
probe_tracepoints_events(s, descs, packets);
return pcm_frames;
}
static inline void compute_next_elapse_from_start(struct amdtp_motu *p)
@ -360,46 +400,55 @@ static void write_sph(struct amdtp_stream *s, __be32 *buffer,
}
}
static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
__be32 *buffer, unsigned int data_blocks,
unsigned int *syt)
static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
const struct pkt_desc *descs,
unsigned int packets,
struct snd_pcm_substream *pcm)
{
struct amdtp_motu *p = (struct amdtp_motu *)s->protocol;
struct snd_pcm_substream *pcm;
struct amdtp_motu *p = s->protocol;
unsigned int pcm_frames = 0;
int i;
/* Not used. */
*syt = 0xffff;
// For data block processing.
for (i = 0; i < packets; ++i) {
const struct pkt_desc *desc = descs + i;
__be32 *buf = desc->ctx_payload;
unsigned int data_blocks = desc->data_blocks;
/* TODO: how to interact control messages between userspace? */
if (pcm) {
write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
pcm_frames += data_blocks;
} else {
write_pcm_silence(s, buf, data_blocks);
}
if (p->midi_ports)
write_midi_messages(s, buffer, data_blocks);
if (p->midi_ports)
write_midi_messages(s, buf, data_blocks);
pcm = READ_ONCE(s->pcm);
if (pcm)
write_pcm_s32(s, pcm->runtime, buffer, data_blocks);
else
write_pcm_silence(s, buffer, data_blocks);
// TODO: how to interact control messages between userspace?
write_sph(s, buffer, data_blocks);
write_sph(s, buf, data_blocks);
}
trace_data_block_sph(s, data_blocks, buffer);
trace_data_block_message(s, data_blocks, buffer);
// For tracepoints.
if (trace_data_block_sph_enabled() ||
trace_data_block_message_enabled())
probe_tracepoints_events(s, descs, packets);
return data_blocks;
return pcm_frames;
}
int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
enum amdtp_stream_direction dir,
const struct snd_motu_protocol *const protocol)
{
amdtp_stream_process_data_blocks_t process_data_blocks;
amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
int fmt = CIP_FMT_MOTU;
int flags = CIP_BLOCKING;
int err;
if (dir == AMDTP_IN_STREAM) {
process_data_blocks = process_tx_data_blocks;
process_ctx_payloads = process_ir_ctx_payloads;
/*
* Units of version 3 transmits packets with invalid CIP header
@ -418,17 +467,23 @@ int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
CIP_SKIP_DBC_ZERO_CHECK;
}
} else {
process_data_blocks = process_rx_data_blocks;
process_ctx_payloads = process_it_ctx_payloads;
flags |= CIP_DBC_IS_END_EVENT;
}
err = amdtp_stream_init(s, unit, dir, flags, fmt, process_data_blocks,
err = amdtp_stream_init(s, unit, dir, flags, fmt, process_ctx_payloads,
sizeof(struct amdtp_motu));
if (err < 0)
return err;
s->sph = 1;
s->ctx_data.rx.fdf = MOTU_FDF_AM824;
if (dir == AMDTP_OUT_STREAM) {
// Use fixed value for FDF field.
s->ctx_data.rx.fdf = MOTU_FDF_AM824;
// Not used.
s->ctx_data.rx.syt_override = 0xffff;
}
return 0;
}

View File

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

View File

@ -247,6 +247,17 @@ static const struct snd_motu_spec motu_audio_express = {
.analog_out_ports = 4,
};
static const struct snd_motu_spec motu_4pre = {
.name = "4pre",
.protocol = &snd_motu_protocol_v3,
.flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
SND_MOTU_SPEC_TX_MICINST_CHUNK |
SND_MOTU_SPEC_TX_RETURN_CHUNK |
SND_MOTU_SPEC_RX_SEPARETED_MAIN,
.analog_in_ports = 2,
.analog_out_ports = 2,
};
#define SND_MOTU_DEV_ENTRY(model, data) \
{ \
.match_flags = IEEE1394_MATCH_VENDOR_ID | \
@ -265,6 +276,7 @@ static const struct ieee1394_device_id motu_id_table[] = {
SND_MOTU_DEV_ENTRY(0x000015, &motu_828mk3), /* FireWire only. */
SND_MOTU_DEV_ENTRY(0x000035, &motu_828mk3), /* Hybrid. */
SND_MOTU_DEV_ENTRY(0x000033, &motu_audio_express),
SND_MOTU_DEV_ENTRY(0x000045, &motu_4pre),
{ }
};
MODULE_DEVICE_TABLE(ieee1394, motu_id_table);

View File

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

View File

@ -114,19 +114,13 @@ static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
if (err < 0)
return err;
err = amdtp_stream_start(stream, conn->resources.channel, conn->speed);
err = amdtp_domain_add_stream(&oxfw->domain, stream,
conn->resources.channel, conn->speed);
if (err < 0) {
cmp_connection_break(conn);
return err;
}
// Wait first packet.
if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) {
amdtp_stream_stop(stream);
cmp_connection_break(conn);
return -ETIMEDOUT;
}
return 0;
}
@ -280,12 +274,12 @@ int snd_oxfw_stream_reserve_duplex(struct snd_oxfw *oxfw,
pcm_channels = formation.pcm;
}
if (formation.rate != rate || formation.pcm != pcm_channels) {
amdtp_stream_stop(&oxfw->rx_stream);
amdtp_domain_stop(&oxfw->domain);
cmp_connection_break(&oxfw->in_conn);
cmp_connection_release(&oxfw->in_conn);
if (oxfw->has_output) {
amdtp_stream_stop(&oxfw->tx_stream);
cmp_connection_break(&oxfw->out_conn);
cmp_connection_release(&oxfw->out_conn);
}
@ -325,30 +319,46 @@ int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw)
if (amdtp_streaming_error(&oxfw->rx_stream) ||
amdtp_streaming_error(&oxfw->tx_stream)) {
amdtp_stream_stop(&oxfw->rx_stream);
cmp_connection_break(&oxfw->in_conn);
amdtp_domain_stop(&oxfw->domain);
if (oxfw->has_output) {
amdtp_stream_stop(&oxfw->tx_stream);
cmp_connection_break(&oxfw->in_conn);
if (oxfw->has_output)
cmp_connection_break(&oxfw->out_conn);
}
}
if (!amdtp_stream_running(&oxfw->rx_stream)) {
err = start_stream(oxfw, &oxfw->rx_stream);
if (err < 0) {
dev_err(&oxfw->unit->device,
"fail to start rx stream: %d\n", err);
"fail to prepare rx stream: %d\n", err);
goto error;
}
}
if (oxfw->has_output) {
if (!amdtp_stream_running(&oxfw->tx_stream)) {
if (oxfw->has_output &&
!amdtp_stream_running(&oxfw->tx_stream)) {
err = start_stream(oxfw, &oxfw->tx_stream);
if (err < 0) {
dev_err(&oxfw->unit->device,
"fail to start tx stream: %d\n", err);
"fail to prepare tx stream: %d\n", err);
goto error;
}
}
err = amdtp_domain_start(&oxfw->domain);
if (err < 0)
goto error;
// Wait first packet.
if (!amdtp_stream_wait_callback(&oxfw->rx_stream,
CALLBACK_TIMEOUT)) {
err = -ETIMEDOUT;
goto error;
}
if (oxfw->has_output) {
if (!amdtp_stream_wait_callback(&oxfw->tx_stream,
CALLBACK_TIMEOUT)) {
err = -ETIMEDOUT;
goto error;
}
}
@ -356,24 +366,24 @@ int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw)
return 0;
error:
amdtp_stream_stop(&oxfw->rx_stream);
amdtp_domain_stop(&oxfw->domain);
cmp_connection_break(&oxfw->in_conn);
if (oxfw->has_output) {
amdtp_stream_stop(&oxfw->tx_stream);
if (oxfw->has_output)
cmp_connection_break(&oxfw->out_conn);
}
return err;
}
void snd_oxfw_stream_stop_duplex(struct snd_oxfw *oxfw)
{
if (oxfw->substreams_count == 0) {
amdtp_stream_stop(&oxfw->rx_stream);
amdtp_domain_stop(&oxfw->domain);
cmp_connection_break(&oxfw->in_conn);
cmp_connection_release(&oxfw->in_conn);
if (oxfw->has_output) {
amdtp_stream_stop(&oxfw->tx_stream);
cmp_connection_break(&oxfw->out_conn);
cmp_connection_release(&oxfw->out_conn);
}
@ -409,13 +419,22 @@ int snd_oxfw_stream_init_duplex(struct snd_oxfw *oxfw)
}
}
return 0;
err = amdtp_domain_init(&oxfw->domain);
if (err < 0) {
destroy_stream(oxfw, &oxfw->rx_stream);
if (oxfw->has_output)
destroy_stream(oxfw, &oxfw->tx_stream);
}
return err;
}
// This function should be called before starting the stream or after stopping
// the streams.
void snd_oxfw_stream_destroy_duplex(struct snd_oxfw *oxfw)
{
amdtp_domain_destroy(&oxfw->domain);
destroy_stream(oxfw, &oxfw->rx_stream);
if (oxfw->has_output)
@ -424,13 +443,13 @@ void snd_oxfw_stream_destroy_duplex(struct snd_oxfw *oxfw)
void snd_oxfw_stream_update_duplex(struct snd_oxfw *oxfw)
{
amdtp_stream_stop(&oxfw->rx_stream);
amdtp_domain_stop(&oxfw->domain);
cmp_connection_break(&oxfw->in_conn);
amdtp_stream_pcm_abort(&oxfw->rx_stream);
if (oxfw->has_output) {
amdtp_stream_stop(&oxfw->tx_stream);
cmp_connection_break(&oxfw->out_conn);
amdtp_stream_pcm_abort(&oxfw->tx_stream);

View File

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

View File

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

View File

@ -8,20 +8,37 @@
#include <linux/delay.h>
#include "tascam.h"
#define CLOCK_STATUS_MASK 0xffff0000
#define CLOCK_CONFIG_MASK 0x0000ffff
#define CALLBACK_TIMEOUT 500
static int get_clock(struct snd_tscm *tscm, u32 *data)
{
int trial = 0;
__be32 reg;
int err;
err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS,
&reg, sizeof(reg), 0);
if (err >= 0)
*data = be32_to_cpu(reg);
while (trial++ < 5) {
err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS,
&reg, sizeof(reg), 0);
if (err < 0)
return err;
return err;
*data = be32_to_cpu(reg);
if (*data & CLOCK_STATUS_MASK)
break;
// In intermediate state after changing clock status.
msleep(50);
}
// Still in the intermediate state.
if (trial >= 5)
return -EAGAIN;
return 0;
}
static int set_clock(struct snd_tscm *tscm, unsigned int rate,
@ -34,7 +51,7 @@ static int set_clock(struct snd_tscm *tscm, unsigned int rate,
err = get_clock(tscm, &data);
if (err < 0)
return err;
data &= 0x0000ffff;
data &= CLOCK_CONFIG_MASK;
if (rate > 0) {
data &= 0x000000ff;
@ -79,17 +96,14 @@ static int set_clock(struct snd_tscm *tscm, unsigned int rate,
int snd_tscm_stream_get_rate(struct snd_tscm *tscm, unsigned int *rate)
{
u32 data = 0x0;
unsigned int trials = 0;
u32 data;
int err;
while (data == 0x0 || trials++ < 5) {
err = get_clock(tscm, &data);
if (err < 0)
return err;
err = get_clock(tscm, &data);
if (err < 0)
return err;
data = (data & 0xff000000) >> 24;
}
data = (data & 0xff000000) >> 24;
/* Check base rate. */
if ((data & 0x0f) == 0x01)
@ -180,9 +194,6 @@ static void finish_session(struct snd_tscm *tscm)
{
__be32 reg;
amdtp_stream_stop(&tscm->rx_stream);
amdtp_stream_stop(&tscm->tx_stream);
reg = 0;
snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING,
@ -287,38 +298,68 @@ static int keep_resources(struct snd_tscm *tscm, unsigned int rate,
fw_parent_device(tscm->unit)->max_speed);
}
int snd_tscm_stream_init_duplex(struct snd_tscm *tscm)
static int init_stream(struct snd_tscm *tscm, struct amdtp_stream *s)
{
struct fw_iso_resources *resources;
enum amdtp_stream_direction dir;
unsigned int pcm_channels;
int err;
/* For out-stream. */
err = fw_iso_resources_init(&tscm->rx_resources, tscm->unit);
if (err < 0)
return err;
pcm_channels = tscm->spec->pcm_playback_analog_channels;
if (s == &tscm->tx_stream) {
resources = &tscm->tx_resources;
dir = AMDTP_IN_STREAM;
pcm_channels = tscm->spec->pcm_capture_analog_channels;
} else {
resources = &tscm->rx_resources;
dir = AMDTP_OUT_STREAM;
pcm_channels = tscm->spec->pcm_playback_analog_channels;
}
if (tscm->spec->has_adat)
pcm_channels += 8;
if (tscm->spec->has_spdif)
pcm_channels += 2;
err = amdtp_tscm_init(&tscm->rx_stream, tscm->unit, AMDTP_OUT_STREAM,
pcm_channels);
err = fw_iso_resources_init(resources, tscm->unit);
if (err < 0)
return err;
/* For in-stream. */
err = fw_iso_resources_init(&tscm->tx_resources, tscm->unit);
err = amdtp_tscm_init(s, tscm->unit, dir, pcm_channels);
if (err < 0)
fw_iso_resources_free(resources);
return err;
}
static void destroy_stream(struct snd_tscm *tscm, struct amdtp_stream *s)
{
amdtp_stream_destroy(s);
if (s == &tscm->tx_stream)
fw_iso_resources_destroy(&tscm->tx_resources);
else
fw_iso_resources_destroy(&tscm->rx_resources);
}
int snd_tscm_stream_init_duplex(struct snd_tscm *tscm)
{
int err;
err = init_stream(tscm, &tscm->tx_stream);
if (err < 0)
return err;
pcm_channels = tscm->spec->pcm_capture_analog_channels;
if (tscm->spec->has_adat)
pcm_channels += 8;
if (tscm->spec->has_spdif)
pcm_channels += 2;
err = amdtp_tscm_init(&tscm->tx_stream, tscm->unit, AMDTP_IN_STREAM,
pcm_channels);
if (err < 0)
amdtp_stream_destroy(&tscm->rx_stream);
err = init_stream(tscm, &tscm->rx_stream);
if (err < 0) {
destroy_stream(tscm, &tscm->tx_stream);
return err;
}
err = amdtp_domain_init(&tscm->domain);
if (err < 0) {
destroy_stream(tscm, &tscm->tx_stream);
destroy_stream(tscm, &tscm->rx_stream);
}
return err;
}
@ -326,24 +367,20 @@ int snd_tscm_stream_init_duplex(struct snd_tscm *tscm)
// At bus reset, streaming is stopped and some registers are clear.
void snd_tscm_stream_update_duplex(struct snd_tscm *tscm)
{
amdtp_stream_pcm_abort(&tscm->tx_stream);
amdtp_stream_stop(&tscm->tx_stream);
amdtp_domain_stop(&tscm->domain);
amdtp_stream_pcm_abort(&tscm->tx_stream);
amdtp_stream_pcm_abort(&tscm->rx_stream);
amdtp_stream_stop(&tscm->rx_stream);
}
/*
* This function should be called before starting streams or after stopping
* streams.
*/
// This function should be called before starting streams or after stopping
// streams.
void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm)
{
amdtp_stream_destroy(&tscm->rx_stream);
amdtp_stream_destroy(&tscm->tx_stream);
amdtp_domain_destroy(&tscm->domain);
fw_iso_resources_destroy(&tscm->rx_resources);
fw_iso_resources_destroy(&tscm->tx_resources);
destroy_stream(tscm, &tscm->rx_stream);
destroy_stream(tscm, &tscm->tx_stream);
}
int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate)
@ -356,6 +393,8 @@ int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate)
return err;
if (tscm->substreams_counter == 0 || rate != curr_rate) {
amdtp_domain_stop(&tscm->domain);
finish_session(tscm);
fw_iso_resources_free(&tscm->tx_resources);
@ -388,8 +427,10 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
return 0;
if (amdtp_streaming_error(&tscm->rx_stream) ||
amdtp_streaming_error(&tscm->tx_stream))
amdtp_streaming_error(&tscm->tx_stream)) {
amdtp_domain_stop(&tscm->domain);
finish_session(tscm);
}
if (generation != fw_parent_device(tscm->unit)->card->generation) {
err = fw_iso_resources_update(&tscm->tx_resources);
@ -402,6 +443,8 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
}
if (!amdtp_stream_running(&tscm->rx_stream)) {
int spd = fw_parent_device(tscm->unit)->max_speed;
err = set_stream_formats(tscm, rate);
if (err < 0)
goto error;
@ -410,27 +453,23 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
if (err < 0)
goto error;
err = amdtp_stream_start(&tscm->rx_stream,
tscm->rx_resources.channel,
fw_parent_device(tscm->unit)->max_speed);
err = amdtp_domain_add_stream(&tscm->domain, &tscm->rx_stream,
tscm->rx_resources.channel, spd);
if (err < 0)
goto error;
err = amdtp_domain_add_stream(&tscm->domain, &tscm->tx_stream,
tscm->tx_resources.channel, spd);
if (err < 0)
goto error;
err = amdtp_domain_start(&tscm->domain);
if (err < 0)
return err;
if (!amdtp_stream_wait_callback(&tscm->rx_stream,
CALLBACK_TIMEOUT)) {
err = -ETIMEDOUT;
goto error;
}
}
if (!amdtp_stream_running(&tscm->tx_stream)) {
err = amdtp_stream_start(&tscm->tx_stream,
tscm->tx_resources.channel,
fw_parent_device(tscm->unit)->max_speed);
if (err < 0)
goto error;
if (!amdtp_stream_wait_callback(&tscm->tx_stream,
CALLBACK_TIMEOUT) ||
!amdtp_stream_wait_callback(&tscm->tx_stream,
CALLBACK_TIMEOUT)) {
err = -ETIMEDOUT;
goto error;
@ -439,6 +478,7 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
return 0;
error:
amdtp_domain_stop(&tscm->domain);
finish_session(tscm);
return err;
@ -447,6 +487,7 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm)
{
if (tscm->substreams_counter == 0) {
amdtp_domain_stop(&tscm->domain);
finish_session(tscm);
fw_iso_resources_free(&tscm->tx_resources);

View File

@ -39,6 +39,9 @@ static const struct snd_tscm_spec model_specs[] = {
.midi_capture_ports = 2,
.midi_playback_ports = 4,
},
// This kernel module doesn't support FE-8 because the most of features
// can be implemented in userspace without any specific support of this
// module.
};
static int identify_model(struct snd_tscm *tscm)
@ -214,7 +217,6 @@ static const struct ieee1394_device_id snd_tscm_id_table[] = {
.vendor_id = 0x00022e,
.specifier_id = 0x00022e,
},
/* FE-08 requires reverse-engineering because it just has faders. */
{}
};
MODULE_DEVICE_TABLE(ieee1394, snd_tscm_id_table);

View File

@ -97,6 +97,8 @@ struct snd_tscm {
struct snd_firewire_tascam_change queue[SND_TSCM_QUEUE_COUNT];
unsigned int pull_pos;
unsigned int push_pos;
struct amdtp_domain domain;
};
#define TSCM_ADDR_BASE 0xffff00000000ull
@ -127,6 +129,26 @@ struct snd_tscm {
#define TSCM_OFFSET_MIDI_RX_QUAD 0x4000
// Although FE-8 supports the above registers, it has no I/O interfaces for
// audio samples and music messages. Otherwise it supports another notification
// for status and control message as well as LED brightening. The message
// consists of quadlet-aligned data up to 32 quadlets. The first byte of message
// is fixed to 0x40. The second byte is between 0x00 to 0x1f and represent each
// control:
// fader: 0x00-0x07
// button: 0x0d, 0x0e
// knob: 0x14-0x1b
// sensing: 0x0b
//
// The rest two bytes represent state of the controls; e.g. current value for
// fader and knob, bitmasks for button and sensing.
// Just after turning on, 32 quadlets messages with 0x00-0x1f are immediately
// sent in one transaction. After, several quadlets are sent in one transaction.
//
// TSCM_OFFSET_FE8_CTL_TX_ON 0x0310
// TSCM_OFFSET_FE8_CTL_TX_ADDR_HI 0x0314
// TSCM_OFFSET_FE8_CTL_TX_ADDR_LO 0x0318
enum snd_tscm_clock {
SND_TSCM_CLOCK_INTERNAL = 0,
SND_TSCM_CLOCK_WORD = 1,

View File

@ -6,6 +6,9 @@ config SND_HDA_CORE
config SND_HDA_DSP_LOADER
bool
config SND_HDA_ALIGNED_MMIO
bool
config SND_HDA_COMPONENT
bool
@ -29,3 +32,8 @@ config SND_HDA_PREALLOC_SIZE
Note that the pre-allocation size can be changed dynamically
via a proc file (/proc/asound/card*/pcm*/sub*/prealloc), too.
config SND_INTEL_NHLT
tristate
# this config should be selected only for Intel ACPI platforms.
# A fallback is provided so that the code compiles in all cases.

View File

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

View File

@ -17,80 +17,22 @@
MODULE_DESCRIPTION("HDA extended core");
MODULE_LICENSE("GPL v2");
static void hdac_ext_writel(u32 value, u32 __iomem *addr)
{
writel(value, addr);
}
static u32 hdac_ext_readl(u32 __iomem *addr)
{
return readl(addr);
}
static void hdac_ext_writew(u16 value, u16 __iomem *addr)
{
writew(value, addr);
}
static u16 hdac_ext_readw(u16 __iomem *addr)
{
return readw(addr);
}
static void hdac_ext_writeb(u8 value, u8 __iomem *addr)
{
writeb(value, addr);
}
static u8 hdac_ext_readb(u8 __iomem *addr)
{
return readb(addr);
}
static int hdac_ext_dma_alloc_pages(struct hdac_bus *bus, int type,
size_t size, struct snd_dma_buffer *buf)
{
return snd_dma_alloc_pages(type, bus->dev, size, buf);
}
static void hdac_ext_dma_free_pages(struct hdac_bus *bus, struct snd_dma_buffer *buf)
{
snd_dma_free_pages(buf);
}
static const struct hdac_io_ops hdac_ext_default_io = {
.reg_writel = hdac_ext_writel,
.reg_readl = hdac_ext_readl,
.reg_writew = hdac_ext_writew,
.reg_readw = hdac_ext_readw,
.reg_writeb = hdac_ext_writeb,
.reg_readb = hdac_ext_readb,
.dma_alloc_pages = hdac_ext_dma_alloc_pages,
.dma_free_pages = hdac_ext_dma_free_pages,
};
/**
* snd_hdac_ext_bus_init - initialize a HD-audio extended bus
* @ebus: the pointer to extended bus object
* @dev: device pointer
* @ops: bus verb operators
* @io_ops: lowlevel I/O operators, can be NULL. If NULL core will use
* default ops
*
* Returns 0 if successful, or a negative error code.
*/
int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev,
const struct hdac_bus_ops *ops,
const struct hdac_io_ops *io_ops,
const struct hdac_ext_bus_ops *ext_ops)
{
int ret;
/* check if io ops are provided, if not load the defaults */
if (io_ops == NULL)
io_ops = &hdac_ext_default_io;
ret = snd_hdac_bus_init(bus, dev, ops, io_ops);
ret = snd_hdac_bus_init(bus, dev, ops);
if (ret < 0)
return ret;

View File

@ -4,12 +4,16 @@
*/
#include <linux/init.h>
#include <linux/io.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/export.h>
#include <sound/hdaudio.h>
#include "local.h"
#include "trace.h"
static void snd_hdac_bus_process_unsol_events(struct work_struct *work);
static const struct hdac_bus_ops default_ops = {
.command = snd_hdac_bus_send_cmd,
.get_response = snd_hdac_bus_get_response,
@ -19,13 +23,11 @@ static const struct hdac_bus_ops default_ops = {
* snd_hdac_bus_init - initialize a HD-audio bas bus
* @bus: the pointer to bus object
* @ops: bus verb operators
* @io_ops: lowlevel I/O operators
*
* Returns 0 if successful, or a negative error code.
*/
int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
const struct hdac_bus_ops *ops,
const struct hdac_io_ops *io_ops)
const struct hdac_bus_ops *ops)
{
memset(bus, 0, sizeof(*bus));
bus->dev = dev;
@ -33,7 +35,7 @@ int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
bus->ops = ops;
else
bus->ops = &default_ops;
bus->io_ops = io_ops;
bus->dma_type = SNDRV_DMA_TYPE_DEV;
INIT_LIST_HEAD(&bus->stream_list);
INIT_LIST_HEAD(&bus->codec_list);
INIT_WORK(&bus->unsol_work, snd_hdac_bus_process_unsol_events);
@ -149,7 +151,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_bus_queue_event);
/*
* process queued unsolicited events
*/
void snd_hdac_bus_process_unsol_events(struct work_struct *work)
static void snd_hdac_bus_process_unsol_events(struct work_struct *work)
{
struct hdac_bus *bus = container_of(work, struct hdac_bus, unsol_work);
struct hdac_device *codec;
@ -172,7 +174,6 @@ void snd_hdac_bus_process_unsol_events(struct work_struct *work)
drv->unsol_event(codec, res);
}
}
EXPORT_SYMBOL_GPL(snd_hdac_bus_process_unsol_events);
/**
* snd_hdac_bus_add_device - Add a codec to bus
@ -197,7 +198,6 @@ int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec)
bus->num_codecs++;
return 0;
}
EXPORT_SYMBOL_GPL(snd_hdac_bus_add_device);
/**
* snd_hdac_bus_remove_device - Remove a codec from bus
@ -216,4 +216,33 @@ void snd_hdac_bus_remove_device(struct hdac_bus *bus,
bus->num_codecs--;
flush_work(&bus->unsol_work);
}
EXPORT_SYMBOL_GPL(snd_hdac_bus_remove_device);
#ifdef CONFIG_SND_HDA_ALIGNED_MMIO
/* Helpers for aligned read/write of mmio space, for Tegra */
unsigned int snd_hdac_aligned_read(void __iomem *addr, unsigned int mask)
{
void __iomem *aligned_addr =
(void __iomem *)((unsigned long)(addr) & ~0x3);
unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
unsigned int v;
v = readl(aligned_addr);
return (v >> shift) & mask;
}
EXPORT_SYMBOL_GPL(snd_hdac_aligned_read);
void snd_hdac_aligned_write(unsigned int val, void __iomem *addr,
unsigned int mask)
{
void __iomem *aligned_addr =
(void __iomem *)((unsigned long)(addr) & ~0x3);
unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
unsigned int v;
v = readl(aligned_addr);
v &= ~(mask << shift);
v |= val << shift;
writel(v, aligned_addr);
}
EXPORT_SYMBOL_GPL(snd_hdac_aligned_write);
#endif /* CONFIG_SND_HDA_ALIGNED_MMIO */

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

View File

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

View File

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

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 */
snd_hdac_stream_updatel(azx_dev, SD_CTL, 0, SD_INT_MASK);
if (azx_dev->direction == SNDRV_PCM_STREAM_PLAYBACK)
azx_dev->fifo_size =
snd_hdac_stream_readw(azx_dev, SD_FIFOSIZE) + 1;
else
azx_dev->fifo_size = 0;
azx_dev->fifo_size = snd_hdac_stream_readw(azx_dev, SD_FIFOSIZE) + 1;
/* when LPIB delay correction gives a small negative value,
* we ignore it; currently set the threshold statically to
@ -680,8 +676,8 @@ int snd_hdac_dsp_prepare(struct hdac_stream *azx_dev, unsigned int format,
azx_dev->locked = true;
spin_unlock_irq(&bus->reg_lock);
err = bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV_SG,
byte_size, bufp);
err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, bus->dev,
byte_size, bufp);
if (err < 0)
goto err_alloc;
@ -707,7 +703,7 @@ int snd_hdac_dsp_prepare(struct hdac_stream *azx_dev, unsigned int format,
return azx_dev->stream_tag;
error:
bus->io_ops->dma_free_pages(bus, bufp);
snd_dma_free_pages(bufp);
err_alloc:
spin_lock_irq(&bus->reg_lock);
azx_dev->locked = false;
@ -754,7 +750,7 @@ void snd_hdac_dsp_cleanup(struct hdac_stream *azx_dev,
azx_dev->period_bytes = 0;
azx_dev->format_val = 0;
bus->io_ops->dma_free_pages(bus, dmab);
snd_dma_free_pages(dmab);
dmab->area = NULL;
spin_lock_irq(&bus->reg_lock);

107
sound/hda/intel-nhlt.c Normal file
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);
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 */

View File

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

View File

@ -80,7 +80,7 @@ int snd_sbdsp_reset(struct snd_sb *chip)
static int snd_sbdsp_version(struct snd_sb * chip)
{
unsigned int result = -ENODEV;
unsigned int result;
snd_sbdsp_command(chip, SB_DSP_GET_VERSION);
result = (short) snd_sbdsp_get_byte(chip) << 8;

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;
bptr = buf;
bptr = munge_int32 (header->number, buf, 2);
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;
switch (cmd) {
case SOUND_MIXER_READ_RECMASK:
case SOUND_MIXER_READ_RECMASK:
return IOCTL_OUT(arg, SOUND_MASK_MIC);
case SOUND_MIXER_READ_DEVMASK:
case SOUND_MIXER_READ_DEVMASK:
return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC | SOUND_MASK_SPEAKER);
case SOUND_MIXER_READ_STEREODEVS:
case SOUND_MIXER_READ_STEREODEVS:
return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC);
case SOUND_MIXER_READ_VOLUME:
case SOUND_MIXER_READ_VOLUME:
return IOCTL_OUT(arg,
VOLUME_ATT_TO_VOXWARE(dmasound.volume_left) |
VOLUME_ATT_TO_VOXWARE(dmasound.volume_right) << 8);
case SOUND_MIXER_READ_CAPS:
case SOUND_MIXER_READ_CAPS:
return IOCTL_OUT(arg, SOUND_CAP_EXCL_INPUT);
case SOUND_MIXER_WRITE_MIC:
case SOUND_MIXER_WRITE_MIC:
IOCTL_IN(arg, data);
tt_dmasnd.input_gain =
RECLEVEL_VOXWARE_TO_GAIN(data & 0xff) << 4 |
RECLEVEL_VOXWARE_TO_GAIN(data >> 8 & 0xff);
/* fall thru, return set value */
case SOUND_MIXER_READ_MIC:
/* fall through - return set value */
case SOUND_MIXER_READ_MIC:
return IOCTL_OUT(arg,
RECLEVEL_GAIN_TO_VOXWARE(tt_dmasnd.input_gain >> 4 & 0xf) |
RECLEVEL_GAIN_TO_VOXWARE(tt_dmasnd.input_gain & 0xf) << 8);

View File

@ -596,11 +596,6 @@ static int snd_ac97_put_volsw(struct snd_kcontrol *kcontrol,
return err;
}
static const struct snd_kcontrol_new snd_ac97_controls_master_mono[2] = {
AC97_SINGLE("Master Mono Playback Switch", AC97_MASTER_MONO, 15, 1, 1),
AC97_SINGLE("Master Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 1)
};
static const struct snd_kcontrol_new snd_ac97_controls_tone[2] = {
AC97_SINGLE("Tone Control - Bass", AC97_MASTER_TONE, 8, 15, 1),
AC97_SINGLE("Tone Control - Treble", AC97_MASTER_TONE, 0, 15, 1)

View File

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

View File

@ -12,6 +12,7 @@ config SND_HDA_INTEL
tristate "HD Audio PCI"
depends on SND_PCI
select SND_HDA
select SND_INTEL_NHLT if ACPI
help
Say Y here to include support for Intel "High Definition
Audio" (Azalia) and its compatible devices.
@ -22,10 +23,20 @@ config SND_HDA_INTEL
To compile this driver as a module, choose M here: the module
will be called snd-hda-intel.
config SND_HDA_INTEL_DETECT_DMIC
bool "DMIC detection and probe abort"
depends on SND_HDA_INTEL
help
Say Y to detect digital microphones on SKL+ devices. DMICs
cannot be handled by the HDaudio legacy driver and are
currently only supported by the SOF driver.
If unsure say N.
config SND_HDA_TEGRA
tristate "NVIDIA Tegra HD Audio"
depends on ARCH_TEGRA
select SND_HDA
select SND_HDA_ALIGNED_MMIO
help
Say Y here to support the HDA controller present in NVIDIA
Tegra SoCs

View File

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

View File

@ -846,7 +846,13 @@ static void snd_hda_codec_dev_release(struct device *dev)
snd_hda_sysfs_clear(codec);
kfree(codec->modelname);
kfree(codec->wcaps);
kfree(codec);
/*
* In the case of ASoC HD-audio, hda_codec is device managed.
* It will be freed when the ASoC device is removed.
*/
if (codec->core.type == HDA_DEV_LEGACY)
kfree(codec);
}
#define DEV_NAME_LEN 31

View File

@ -794,6 +794,7 @@ static int azx_rirb_get_response(struct hdac_bus *bus, unsigned int addr,
unsigned long timeout;
unsigned long loopcounter;
int do_poll = 0;
bool warned = false;
again:
timeout = jiffies + msecs_to_jiffies(1000);
@ -813,9 +814,17 @@ static int azx_rirb_get_response(struct hdac_bus *bus, unsigned int addr,
spin_unlock_irq(&bus->reg_lock);
if (time_after(jiffies, timeout))
break;
if (hbus->needs_damn_long_delay || loopcounter > 3000)
#define LOOP_COUNT_MAX 3000
if (hbus->needs_damn_long_delay ||
loopcounter > LOOP_COUNT_MAX) {
if (loopcounter > LOOP_COUNT_MAX && !warned) {
dev_dbg_ratelimited(chip->card->dev,
"too slow response, last cmd=%#08x\n",
bus->last_cmd[addr]);
warned = true;
}
msleep(2); /* temporary workaround */
else {
} else {
udelay(10);
cond_resched();
}
@ -869,10 +878,13 @@ static int azx_rirb_get_response(struct hdac_bus *bus, unsigned int addr,
*/
if (hbus->allow_bus_reset && !hbus->response_reset && !hbus->in_reset) {
hbus->response_reset = 1;
dev_err(chip->card->dev,
"No response from codec, resetting bus: last cmd=0x%08x\n",
bus->last_cmd[addr]);
return -EAGAIN; /* give a chance to retry */
}
dev_err(chip->card->dev,
dev_WARN(chip->card->dev,
"azx_get_response timeout, switching to single_cmd mode: last cmd=0x%08x\n",
bus->last_cmd[addr]);
chip->single_cmd = 1;
@ -1207,14 +1219,12 @@ void snd_hda_bus_reset(struct hda_bus *bus)
}
/* HD-audio bus initialization */
int azx_bus_init(struct azx *chip, const char *model,
const struct hdac_io_ops *io_ops)
int azx_bus_init(struct azx *chip, const char *model)
{
struct hda_bus *bus = &chip->bus;
int err;
err = snd_hdac_bus_init(&bus->core, chip->card->dev, &bus_core_ops,
io_ops);
err = snd_hdac_bus_init(&bus->core, chip->card->dev, &bus_core_ops);
if (err < 0)
return err;

View File

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

View File

@ -46,6 +46,7 @@
#include <sound/initval.h>
#include <sound/hdaudio.h>
#include <sound/hda_i915.h>
#include <sound/intel-nhlt.h>
#include <linux/vgaarb.h>
#include <linux/vga_switcheroo.h>
#include <linux/firmware.h>
@ -84,8 +85,6 @@ enum {
#define INTEL_SCH_HDA_DEVC 0x78
#define INTEL_SCH_HDA_DEVC_NOSNOOP (0x1<<11)
/* Define IN stream 0 FIFO size offset in VIA controller */
#define VIA_IN_STREAM0_FIFO_SIZE_OFFSET 0x90
/* Define VIA HD Audio Device ID*/
#define VIA_HDAC_DEVICE_ID 0x3288
@ -125,6 +124,7 @@ static char *patch[SNDRV_CARDS];
static bool beep_mode[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] =
CONFIG_SND_HDA_INPUT_BEEP_MODE};
#endif
static bool dmic_detect = IS_ENABLED(CONFIG_SND_HDA_INTEL_DETECT_DMIC);
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for Intel HD audio interface.");
@ -159,6 +159,8 @@ module_param_array(beep_mode, bool, NULL, 0444);
MODULE_PARM_DESC(beep_mode, "Select HDA Beep registration mode "
"(0=off, 1=on) (default=1).");
#endif
module_param(dmic_detect, bool, 0444);
MODULE_PARM_DESC(dmic_detect, "DMIC detect on SKL+ platforms");
#ifdef CONFIG_PM
static int param_set_xint(const char *val, const struct kernel_param *kp);
@ -267,6 +269,7 @@ enum {
AZX_DRIVER_CTX,
AZX_DRIVER_CTHDA,
AZX_DRIVER_CMEDIA,
AZX_DRIVER_ZHAOXIN,
AZX_DRIVER_GENERIC,
AZX_NUM_DRIVERS, /* keep this as last entry */
};
@ -353,7 +356,7 @@ enum {
*/
#ifdef SUPPORT_VGA_SWITCHEROO
#define use_vga_switcheroo(chip) ((chip)->use_vga_switcheroo)
#define needs_eld_notify_link(chip) ((chip)->need_eld_notify_link)
#define needs_eld_notify_link(chip) ((chip)->bus.keep_power)
#else
#define use_vga_switcheroo(chip) 0
#define needs_eld_notify_link(chip) false
@ -385,6 +388,7 @@ static char *driver_short_names[] = {
[AZX_DRIVER_CTX] = "HDA Creative",
[AZX_DRIVER_CTHDA] = "HDA Creative",
[AZX_DRIVER_CMEDIA] = "HDA C-Media",
[AZX_DRIVER_ZHAOXIN] = "HDA Zhaoxin",
[AZX_DRIVER_GENERIC] = "HD-Audio Generic",
};
@ -811,11 +815,7 @@ static unsigned int azx_via_get_position(struct azx *chip,
mod_dma_pos = le32_to_cpu(*azx_dev->core.posbuf);
mod_dma_pos %= azx_dev->core.period_bytes;
/* azx_dev->fifo_size can't get FIFO size of in stream.
* Get from base address + offset.
*/
fifo_size = readw(azx_bus(chip)->remap_addr +
VIA_IN_STREAM0_FIFO_SIZE_OFFSET);
fifo_size = azx_stream(azx_dev)->fifo_size - 1;
if (azx_dev->insufficient) {
/* Link position never gather than FIFO size */
@ -1145,7 +1145,7 @@ static int azx_runtime_idle(struct device *dev)
return -EBUSY;
/* ELD notification gets broken when HD-audio bus is off */
if (needs_eld_notify_link(hda))
if (needs_eld_notify_link(chip))
return -EBUSY;
return 0;
@ -1256,7 +1256,7 @@ static void setup_vga_switcheroo_runtime_pm(struct azx *chip)
struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
struct hda_codec *codec;
if (hda->use_vga_switcheroo && !hda->need_eld_notify_link) {
if (hda->use_vga_switcheroo && !needs_eld_notify_link(chip)) {
list_for_each_codec(codec, &chip->bus)
codec->auto_runtime_pm = 1;
/* reset the power save setup */
@ -1270,10 +1270,9 @@ static void azx_vs_gpu_bound(struct pci_dev *pci,
{
struct snd_card *card = pci_get_drvdata(pci);
struct azx *chip = card->private_data;
struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
if (client_id == VGA_SWITCHEROO_DIS)
hda->need_eld_notify_link = 0;
chip->bus.keep_power = 0;
setup_vga_switcheroo_runtime_pm(chip);
}
@ -1285,7 +1284,7 @@ static void init_vga_switcheroo(struct azx *chip)
dev_info(chip->card->dev,
"Handle vga_switcheroo audio client\n");
hda->use_vga_switcheroo = 1;
hda->need_eld_notify_link = 1; /* cleared in gpu_bound op */
chip->bus.keep_power = 1; /* cleared in either gpu_bound op or codec probe */
chip->driver_caps |= AZX_DCAPS_PM_RUNTIME;
pci_dev_put(p);
}
@ -1349,9 +1348,9 @@ static int azx_free(struct azx *chip)
}
if (bus->chip_init) {
azx_stop_chip(chip);
azx_clear_irq_pending(chip);
azx_stop_all_streams(chip);
azx_stop_chip(chip);
}
if (bus->irq >= 0)
@ -1684,7 +1683,6 @@ static int default_bdl_pos_adj(struct azx *chip)
/*
* constructor
*/
static const struct hdac_io_ops pci_hda_io_ops;
static const struct hda_controller_ops pci_hda_ops;
static int azx_create(struct snd_card *card, struct pci_dev *pci,
@ -1744,13 +1742,17 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci,
else
chip->bdl_pos_adj = bdl_pos_adj[dev];
err = azx_bus_init(chip, model[dev], &pci_hda_io_ops);
err = azx_bus_init(chip, model[dev]);
if (err < 0) {
kfree(hda);
pci_disable_device(pci);
return err;
}
/* use the non-cached pages in non-snoop mode */
if (!azx_snoop(chip))
azx_bus(chip)->dma_type = SNDRV_DMA_TYPE_DEV_UC;
/* Workaround for a communication error on CFL (bko#199007) and CNL */
if (IS_CFL(pci) || IS_CNL(pci))
azx_bus(chip)->polling_mode = 1;
@ -1985,41 +1987,6 @@ static void azx_firmware_cb(const struct firmware *fw, void *context)
}
#endif
/*
* HDA controller ops.
*/
/* PCI register access. */
static void pci_azx_writel(u32 value, u32 __iomem *addr)
{
writel(value, addr);
}
static u32 pci_azx_readl(u32 __iomem *addr)
{
return readl(addr);
}
static void pci_azx_writew(u16 value, u16 __iomem *addr)
{
writew(value, addr);
}
static u16 pci_azx_readw(u16 __iomem *addr)
{
return readw(addr);
}
static void pci_azx_writeb(u8 value, u8 __iomem *addr)
{
writeb(value, addr);
}
static u8 pci_azx_readb(u8 __iomem *addr)
{
return readb(addr);
}
static int disable_msi_reset_irq(struct azx *chip)
{
struct hdac_bus *bus = azx_bus(chip);
@ -2036,24 +2003,6 @@ static int disable_msi_reset_irq(struct azx *chip)
return 0;
}
/* DMA page allocation helpers. */
static int dma_alloc_pages(struct hdac_bus *bus,
int type,
size_t size,
struct snd_dma_buffer *buf)
{
struct azx *chip = bus_to_azx(bus);
if (!azx_snoop(chip) && type == SNDRV_DMA_TYPE_DEV)
type = SNDRV_DMA_TYPE_DEV_UC;
return snd_dma_alloc_pages(type, bus->dev, size, buf);
}
static void dma_free_pages(struct hdac_bus *bus, struct snd_dma_buffer *buf)
{
snd_dma_free_pages(buf);
}
static void pcm_mmap_prepare(struct snd_pcm_substream *substream,
struct vm_area_struct *area)
{
@ -2065,23 +2014,31 @@ static void pcm_mmap_prepare(struct snd_pcm_substream *substream,
#endif
}
static const struct hdac_io_ops pci_hda_io_ops = {
.reg_writel = pci_azx_writel,
.reg_readl = pci_azx_readl,
.reg_writew = pci_azx_writew,
.reg_readw = pci_azx_readw,
.reg_writeb = pci_azx_writeb,
.reg_readb = pci_azx_readb,
.dma_alloc_pages = dma_alloc_pages,
.dma_free_pages = dma_free_pages,
};
static const struct hda_controller_ops pci_hda_ops = {
.disable_msi_reset_irq = disable_msi_reset_irq,
.pcm_mmap_prepare = pcm_mmap_prepare,
.position_check = azx_position_check,
};
static int azx_check_dmic(struct pci_dev *pci, struct azx *chip)
{
struct nhlt_acpi_table *nhlt;
int ret = 0;
if (chip->driver_type == AZX_DRIVER_SKL &&
pci->class != 0x040300) {
nhlt = intel_nhlt_init(&pci->dev);
if (nhlt) {
if (intel_nhlt_get_dmic_geo(&pci->dev, nhlt)) {
ret = -ENODEV;
dev_info(&pci->dev, "Digital mics found on Skylake+ platform, aborting probe\n");
}
intel_nhlt_free(nhlt);
}
}
return ret;
}
static int azx_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
@ -2112,6 +2069,17 @@ static int azx_probe(struct pci_dev *pci,
card->private_data = chip;
hda = container_of(chip, struct hda_intel, chip);
/*
* stop probe if digital microphones detected on Skylake+ platform
* with the DSP enabled. This is an opt-in behavior defined at build
* time or at run-time with a module parameter
*/
if (dmic_detect) {
err = azx_check_dmic(pci, chip);
if (err < 0)
goto out_free;
}
pci_set_drvdata(pci, card);
err = register_vga_switcheroo(chip);
@ -2653,6 +2621,8 @@ static const struct pci_device_id azx_ids[] = {
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
.class_mask = 0xffffff,
.driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_HDMI },
/* Zhaoxin */
{ PCI_DEVICE(0x1d17, 0x3288), .driver_data = AZX_DRIVER_ZHAOXIN },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, azx_ids);

View File

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

View File

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

View File

@ -75,88 +75,6 @@ MODULE_PARM_DESC(power_save,
#define power_save 0
#endif
/*
* DMA page allocation ops.
*/
static int dma_alloc_pages(struct hdac_bus *bus, int type, size_t size,
struct snd_dma_buffer *buf)
{
return snd_dma_alloc_pages(type, bus->dev, size, buf);
}
static void dma_free_pages(struct hdac_bus *bus, struct snd_dma_buffer *buf)
{
snd_dma_free_pages(buf);
}
/*
* Register access ops. Tegra HDA register access is DWORD only.
*/
static void hda_tegra_writel(u32 value, u32 __iomem *addr)
{
writel(value, addr);
}
static u32 hda_tegra_readl(u32 __iomem *addr)
{
return readl(addr);
}
static void hda_tegra_writew(u16 value, u16 __iomem *addr)
{
unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
void __iomem *dword_addr = (void __iomem *)((unsigned long)(addr) & ~0x3);
u32 v;
v = readl(dword_addr);
v &= ~(0xffff << shift);
v |= value << shift;
writel(v, dword_addr);
}
static u16 hda_tegra_readw(u16 __iomem *addr)
{
unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
void __iomem *dword_addr = (void __iomem *)((unsigned long)(addr) & ~0x3);
u32 v;
v = readl(dword_addr);
return (v >> shift) & 0xffff;
}
static void hda_tegra_writeb(u8 value, u8 __iomem *addr)
{
unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
void __iomem *dword_addr = (void __iomem *)((unsigned long)(addr) & ~0x3);
u32 v;
v = readl(dword_addr);
v &= ~(0xff << shift);
v |= value << shift;
writel(v, dword_addr);
}
static u8 hda_tegra_readb(u8 __iomem *addr)
{
unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
void __iomem *dword_addr = (void __iomem *)((unsigned long)(addr) & ~0x3);
u32 v;
v = readl(dword_addr);
return (v >> shift) & 0xff;
}
static const struct hdac_io_ops hda_tegra_io_ops = {
.reg_writel = hda_tegra_writel,
.reg_readl = hda_tegra_readl,
.reg_writew = hda_tegra_writew,
.reg_readw = hda_tegra_readw,
.reg_writeb = hda_tegra_writeb,
.reg_readb = hda_tegra_readb,
.dma_alloc_pages = dma_alloc_pages,
.dma_free_pages = dma_free_pages,
};
static const struct hda_controller_ops hda_tegra_ops; /* nothing special */
static void hda_tegra_init(struct hda_tegra *hda)
@ -475,7 +393,7 @@ static int hda_tegra_create(struct snd_card *card,
INIT_WORK(&hda->probe_work, hda_tegra_probe_work);
err = azx_bus_init(chip, NULL, &hda_tegra_io_ops);
err = azx_bus_init(chip, NULL);
if (err < 0)
return err;

View File

@ -18,6 +18,7 @@
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
@ -119,6 +120,7 @@ struct hdmi_pcm {
};
struct hdmi_spec {
struct hda_codec *codec;
int num_cvts;
struct snd_array cvts; /* struct hdmi_spec_per_cvt */
hda_nid_t cvt_nids[4]; /* only for haswell fix */
@ -163,9 +165,11 @@ struct hdmi_spec {
struct hda_multi_out multiout;
struct hda_pcm_stream pcm_playback;
/* i915/powerwell (Haswell+/Valleyview+) specific */
bool use_acomp_notifier; /* use i915 eld_notify callback for hotplug */
bool use_jack_detect; /* jack detection enabled */
bool use_acomp_notifier; /* use eld_notify callback for hotplug */
bool acomp_registered; /* audio component registered in this driver */
struct drm_audio_component_audio_ops drm_audio_ops;
int (*port2pin)(struct hda_codec *, int); /* reverse port/pin mapping */
struct hdac_chmap chmap;
hda_nid_t vendor_nid;
@ -765,6 +769,10 @@ static void check_presence_and_report(struct hda_codec *codec, hda_nid_t nid,
static void jack_callback(struct hda_codec *codec,
struct hda_jack_callback *jack)
{
/* stop polling when notification is enabled */
if (codec_has_acomp(codec))
return;
/* hda_jack don't support DP MST */
check_presence_and_report(codec, jack->nid, 0);
}
@ -823,6 +831,9 @@ static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
if (codec_has_acomp(codec))
return;
if (!snd_hda_jack_tbl_get_from_tag(codec, tag)) {
codec_dbg(codec, "Unexpected HDMI event tag 0x%x\n", tag);
return;
@ -1421,7 +1432,7 @@ static void hdmi_pcm_reset_pin(struct hdmi_spec *spec,
/* update per_pin ELD from the given new ELD;
* setup info frame and notification accordingly
*/
static void update_eld(struct hda_codec *codec,
static bool update_eld(struct hda_codec *codec,
struct hdmi_spec_per_pin *per_pin,
struct hdmi_eld *eld)
{
@ -1429,7 +1440,7 @@ static void update_eld(struct hda_codec *codec,
struct hdmi_spec *spec = codec->spec;
bool old_eld_valid = pin_eld->eld_valid;
bool eld_changed;
int pcm_idx = -1;
int pcm_idx;
/* for monitor disconnection, save pcm_idx firstly */
pcm_idx = per_pin->pcm_idx;
@ -1452,18 +1463,22 @@ static void update_eld(struct hda_codec *codec,
snd_hdmi_show_eld(codec, &eld->info);
eld_changed = (pin_eld->eld_valid != eld->eld_valid);
if (eld->eld_valid && pin_eld->eld_valid)
eld_changed |= (pin_eld->monitor_present != eld->monitor_present);
if (!eld_changed && eld->eld_valid && pin_eld->eld_valid)
if (pin_eld->eld_size != eld->eld_size ||
memcmp(pin_eld->eld_buffer, eld->eld_buffer,
eld->eld_size) != 0)
eld_changed = true;
pin_eld->monitor_present = eld->monitor_present;
pin_eld->eld_valid = eld->eld_valid;
pin_eld->eld_size = eld->eld_size;
if (eld->eld_valid)
memcpy(pin_eld->eld_buffer, eld->eld_buffer, eld->eld_size);
pin_eld->info = eld->info;
if (eld_changed) {
pin_eld->monitor_present = eld->monitor_present;
pin_eld->eld_valid = eld->eld_valid;
pin_eld->eld_size = eld->eld_size;
if (eld->eld_valid)
memcpy(pin_eld->eld_buffer, eld->eld_buffer,
eld->eld_size);
pin_eld->info = eld->info;
}
/*
* Re-setup pin and infoframe. This is needed e.g. when
@ -1481,6 +1496,7 @@ static void update_eld(struct hda_codec *codec,
SNDRV_CTL_EVENT_MASK_VALUE |
SNDRV_CTL_EVENT_MASK_INFO,
&get_hdmi_pcm(spec, pcm_idx)->eld_ctl->id);
return eld_changed;
}
/* update ELD and jack state via HD-audio verbs */
@ -1582,6 +1598,7 @@ static void sync_eld_via_acomp(struct hda_codec *codec,
struct hdmi_spec *spec = codec->spec;
struct hdmi_eld *eld = &spec->temp_eld;
struct snd_jack *jack = NULL;
bool changed;
int size;
mutex_lock(&per_pin->lock);
@ -1608,15 +1625,13 @@ static void sync_eld_via_acomp(struct hda_codec *codec,
* disconnected event. Jack must be fetched before update_eld()
*/
jack = pin_idx_to_jack(codec, per_pin);
update_eld(codec, per_pin, eld);
changed = update_eld(codec, per_pin, eld);
if (jack == NULL)
jack = pin_idx_to_jack(codec, per_pin);
if (jack == NULL)
goto unlock;
snd_jack_report(jack,
(eld->monitor_present && eld->eld_valid) ?
if (changed && jack)
snd_jack_report(jack,
(eld->monitor_present && eld->eld_valid) ?
SND_JACK_AVOUT : 0);
unlock:
mutex_unlock(&per_pin->lock);
}
@ -1632,18 +1647,13 @@ static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
snd_hda_power_down_pm(codec);
return false;
}
}
if (codec_has_acomp(codec)) {
ret = hdmi_present_sense_via_verbs(per_pin, repoll);
snd_hda_power_down_pm(codec);
} else {
sync_eld_via_acomp(codec, per_pin);
ret = false; /* don't call snd_hda_jack_report_sync() */
} else {
ret = hdmi_present_sense_via_verbs(per_pin, repoll);
}
if (!codec_has_acomp(codec))
snd_hda_power_down_pm(codec);
return ret;
}
@ -2248,6 +2258,8 @@ static int generic_hdmi_init(struct hda_codec *codec)
struct hdmi_spec *spec = codec->spec;
int pin_idx;
mutex_lock(&spec->pcm_lock);
spec->use_jack_detect = !codec->jackpoll_interval;
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
hda_nid_t pin_nid = per_pin->pin_nid;
@ -2255,11 +2267,15 @@ static int generic_hdmi_init(struct hda_codec *codec)
snd_hda_set_dev_select(codec, pin_nid, dev_id);
hdmi_init_pin(codec, pin_nid);
if (!codec_has_acomp(codec))
if (codec_has_acomp(codec))
continue;
if (spec->use_jack_detect)
snd_hda_jack_detect_enable(codec, pin_nid);
else
snd_hda_jack_detect_enable_callback(codec, pin_nid,
codec->jackpoll_interval > 0 ?
jack_callback : NULL);
jack_callback);
}
mutex_unlock(&spec->pcm_lock);
return 0;
}
@ -2292,7 +2308,9 @@ static void generic_hdmi_free(struct hda_codec *codec)
struct hdmi_spec *spec = codec->spec;
int pin_idx, pcm_idx;
if (codec_has_acomp(codec)) {
if (spec->acomp_registered) {
snd_hdac_acomp_exit(&codec->bus->core);
} else if (codec_has_acomp(codec)) {
snd_hdac_acomp_register_notifier(&codec->bus->core, NULL);
codec->relaxed_resume = 0;
}
@ -2360,6 +2378,7 @@ static int alloc_generic_hdmi(struct hda_codec *codec)
if (!spec)
return -ENOMEM;
spec->codec = codec;
spec->ops = generic_standard_hdmi_ops;
spec->dev_num = 1; /* initialize to 1 */
mutex_init(&spec->pcm_lock);
@ -2397,6 +2416,138 @@ static int patch_generic_hdmi(struct hda_codec *codec)
return 0;
}
/*
* generic audio component binding
*/
/* turn on / off the unsol event jack detection dynamically */
static void reprogram_jack_detect(struct hda_codec *codec, hda_nid_t nid,
bool use_acomp)
{
struct hda_jack_tbl *tbl;
tbl = snd_hda_jack_tbl_get(codec, nid);
if (tbl) {
/* clear unsol even if component notifier is used, or re-enable
* if notifier is cleared
*/
unsigned int val = use_acomp ? 0 : (AC_USRSP_EN | tbl->tag);
snd_hda_codec_write_cache(codec, nid, 0,
AC_VERB_SET_UNSOLICITED_ENABLE, val);
} else {
/* if no jack entry was defined beforehand, create a new one
* at need (i.e. only when notifier is cleared)
*/
if (!use_acomp)
snd_hda_jack_detect_enable(codec, nid);
}
}
/* set up / clear component notifier dynamically */
static void generic_acomp_notifier_set(struct drm_audio_component *acomp,
bool use_acomp)
{
struct hdmi_spec *spec;
int i;
spec = container_of(acomp->audio_ops, struct hdmi_spec, drm_audio_ops);
mutex_lock(&spec->pcm_lock);
spec->use_acomp_notifier = use_acomp;
spec->codec->relaxed_resume = use_acomp;
/* reprogram each jack detection logic depending on the notifier */
if (spec->use_jack_detect) {
for (i = 0; i < spec->num_pins; i++)
reprogram_jack_detect(spec->codec,
get_pin(spec, i)->pin_nid,
use_acomp);
}
mutex_unlock(&spec->pcm_lock);
}
/* enable / disable the notifier via master bind / unbind */
static int generic_acomp_master_bind(struct device *dev,
struct drm_audio_component *acomp)
{
generic_acomp_notifier_set(acomp, true);
return 0;
}
static void generic_acomp_master_unbind(struct device *dev,
struct drm_audio_component *acomp)
{
generic_acomp_notifier_set(acomp, false);
}
/* check whether both HD-audio and DRM PCI devices belong to the same bus */
static int match_bound_vga(struct device *dev, int subtype, void *data)
{
struct hdac_bus *bus = data;
struct pci_dev *pci, *master;
if (!dev_is_pci(dev) || !dev_is_pci(bus->dev))
return 0;
master = to_pci_dev(bus->dev);
pci = to_pci_dev(dev);
return master->bus == pci->bus;
}
/* audio component notifier for AMD/Nvidia HDMI codecs */
static void generic_acomp_pin_eld_notify(void *audio_ptr, int port, int dev_id)
{
struct hda_codec *codec = audio_ptr;
struct hdmi_spec *spec = codec->spec;
hda_nid_t pin_nid = spec->port2pin(codec, port);
if (!pin_nid)
return;
if (get_wcaps_type(get_wcaps(codec, pin_nid)) != AC_WID_PIN)
return;
/* skip notification during system suspend (but not in runtime PM);
* the state will be updated at resume
*/
if (snd_power_get_state(codec->card) != SNDRV_CTL_POWER_D0)
return;
/* ditto during suspend/resume process itself */
if (snd_hdac_is_in_pm(&codec->core))
return;
check_presence_and_report(codec, pin_nid, dev_id);
}
/* set up the private drm_audio_ops from the template */
static void setup_drm_audio_ops(struct hda_codec *codec,
const struct drm_audio_component_audio_ops *ops)
{
struct hdmi_spec *spec = codec->spec;
spec->drm_audio_ops.audio_ptr = codec;
/* intel_audio_codec_enable() or intel_audio_codec_disable()
* will call pin_eld_notify with using audio_ptr pointer
* We need make sure audio_ptr is really setup
*/
wmb();
spec->drm_audio_ops.pin2port = ops->pin2port;
spec->drm_audio_ops.pin_eld_notify = ops->pin_eld_notify;
spec->drm_audio_ops.master_bind = ops->master_bind;
spec->drm_audio_ops.master_unbind = ops->master_unbind;
}
/* initialize the generic HDMI audio component */
static void generic_acomp_init(struct hda_codec *codec,
const struct drm_audio_component_audio_ops *ops,
int (*port2pin)(struct hda_codec *, int))
{
struct hdmi_spec *spec = codec->spec;
spec->port2pin = port2pin;
setup_drm_audio_ops(codec, ops);
if (!snd_hdac_acomp_init(&codec->bus->core, &spec->drm_audio_ops,
match_bound_vga, 0)) {
spec->acomp_registered = true;
codec->bus->keep_power = 0;
}
}
/*
* Intel codec parsers and helpers
*/
@ -2565,20 +2716,19 @@ static void intel_pin_eld_notify(void *audio_ptr, int port, int pipe)
check_presence_and_report(codec, pin_nid, dev_id);
}
static const struct drm_audio_component_audio_ops intel_audio_ops = {
.pin2port = intel_pin2port,
.pin_eld_notify = intel_pin_eld_notify,
};
/* register i915 component pin_eld_notify callback */
static void register_i915_notifier(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
spec->use_acomp_notifier = true;
spec->drm_audio_ops.audio_ptr = codec;
/* intel_audio_codec_enable() or intel_audio_codec_disable()
* will call pin_eld_notify with using audio_ptr pointer
* We need make sure audio_ptr is really setup
*/
wmb();
spec->drm_audio_ops.pin2port = intel_pin2port;
spec->drm_audio_ops.pin_eld_notify = intel_pin_eld_notify;
spec->port2pin = intel_port2pin;
setup_drm_audio_ops(codec, &intel_audio_ops);
snd_hdac_acomp_register_notifier(&codec->bus->core,
&spec->drm_audio_ops);
/* no need for forcible resume for jack check thanks to notifier */
@ -2612,6 +2762,8 @@ static void i915_pin_cvt_fixup(struct hda_codec *codec,
/* precondition and allocation for Intel codecs */
static int alloc_intel_hdmi(struct hda_codec *codec)
{
int err;
/* requires i915 binding */
if (!codec->bus->core.audio_component) {
codec_info(codec, "No i915 binding for Intel HDMI/DP codec\n");
@ -2620,7 +2772,12 @@ static int alloc_intel_hdmi(struct hda_codec *codec)
return -ENODEV;
}
return alloc_generic_hdmi(codec);
err = alloc_generic_hdmi(codec);
if (err < 0)
return err;
/* no need to handle unsol events */
codec->patch_ops.unsol_event = NULL;
return 0;
}
/* parse and post-process for Intel codecs */
@ -2976,6 +3133,7 @@ static int patch_simple_hdmi(struct hda_codec *codec,
if (!spec)
return -ENOMEM;
spec->codec = codec;
codec->spec = spec;
hdmi_array_init(spec, 1);
@ -3280,6 +3438,26 @@ static int nvhdmi_chmap_validate(struct hdac_chmap *chmap,
return 0;
}
/* map from pin NID to port; port is 0-based */
/* for Nvidia: assume widget NID starting from 4, with step 1 (4, 5, 6, ...) */
static int nvhdmi_pin2port(void *audio_ptr, int pin_nid)
{
return pin_nid - 4;
}
/* reverse-map from port to pin NID: see above */
static int nvhdmi_port2pin(struct hda_codec *codec, int port)
{
return port + 4;
}
static const struct drm_audio_component_audio_ops nvhdmi_audio_ops = {
.pin2port = nvhdmi_pin2port,
.pin_eld_notify = generic_acomp_pin_eld_notify,
.master_bind = generic_acomp_master_bind,
.master_unbind = generic_acomp_master_unbind,
};
static int patch_nvhdmi(struct hda_codec *codec)
{
struct hdmi_spec *spec;
@ -3296,6 +3474,8 @@ static int patch_nvhdmi(struct hda_codec *codec)
nvhdmi_chmap_cea_alloc_validate_get_type;
spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate;
generic_acomp_init(codec, &nvhdmi_audio_ops, nvhdmi_port2pin);
return 0;
}
@ -3783,6 +3963,26 @@ static int atihdmi_init(struct hda_codec *codec)
return 0;
}
/* map from pin NID to port; port is 0-based */
/* for AMD: assume widget NID starting from 3, with step 2 (3, 5, 7, ...) */
static int atihdmi_pin2port(void *audio_ptr, int pin_nid)
{
return pin_nid / 2 - 1;
}
/* reverse-map from port to pin NID: see above */
static int atihdmi_port2pin(struct hda_codec *codec, int port)
{
return port * 2 + 3;
}
static const struct drm_audio_component_audio_ops atihdmi_audio_ops = {
.pin2port = atihdmi_pin2port,
.pin_eld_notify = generic_acomp_pin_eld_notify,
.master_bind = generic_acomp_master_bind,
.master_unbind = generic_acomp_master_unbind,
};
static int patch_atihdmi(struct hda_codec *codec)
{
struct hdmi_spec *spec;
@ -3831,6 +4031,8 @@ static int patch_atihdmi(struct hda_codec *codec)
*/
codec->link_down_at_suspend = 1;
generic_acomp_init(codec, &atihdmi_audio_ops, atihdmi_port2pin);
return 0;
}

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(0x1458, 0xa002, "GA-MA790X", 1),
SND_PCI_QUIRK(0x8086, 0xd613, "Intel", 1),
/* blacklist -- no beep available */
SND_PCI_QUIRK(0x17aa, 0x309e, "Lenovo ThinkCentre M73", 0),
SND_PCI_QUIRK(0x17aa, 0x30a3, "Lenovo ThinkCentre M93", 0),
{}
};
@ -2841,7 +2844,8 @@ static int patch_alc268(struct hda_codec *codec)
return err;
spec = codec->spec;
spec->gen.beep_nid = 0x01;
if (has_cdefine_beep(codec))
spec->gen.beep_nid = 0x01;
spec->shutup = alc_eapd_shutup;
@ -3755,6 +3759,72 @@ static void alc269_x101_hp_automute_hook(struct hda_codec *codec,
vref);
}
/*
* Magic sequence to make Huawei Matebook X right speaker working (bko#197801)
*/
struct hda_alc298_mbxinit {
unsigned char value_0x23;
unsigned char value_0x25;
};
static void alc298_huawei_mbx_stereo_seq(struct hda_codec *codec,
const struct hda_alc298_mbxinit *initval,
bool first)
{
snd_hda_codec_write(codec, 0x06, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x0);
alc_write_coef_idx(codec, 0x26, 0xb000);
if (first)
snd_hda_codec_write(codec, 0x21, 0, AC_VERB_GET_PIN_SENSE, 0x0);
snd_hda_codec_write(codec, 0x6, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x80);
alc_write_coef_idx(codec, 0x26, 0xf000);
alc_write_coef_idx(codec, 0x23, initval->value_0x23);
if (initval->value_0x23 != 0x1e)
alc_write_coef_idx(codec, 0x25, initval->value_0x25);
snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0x26);
snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, 0xb010);
}
static void alc298_fixup_huawei_mbx_stereo(struct hda_codec *codec,
const struct hda_fixup *fix,
int action)
{
/* Initialization magic */
static const struct hda_alc298_mbxinit dac_init[] = {
{0x0c, 0x00}, {0x0d, 0x00}, {0x0e, 0x00}, {0x0f, 0x00},
{0x10, 0x00}, {0x1a, 0x40}, {0x1b, 0x82}, {0x1c, 0x00},
{0x1d, 0x00}, {0x1e, 0x00}, {0x1f, 0x00},
{0x20, 0xc2}, {0x21, 0xc8}, {0x22, 0x26}, {0x23, 0x24},
{0x27, 0xff}, {0x28, 0xff}, {0x29, 0xff}, {0x2a, 0x8f},
{0x2b, 0x02}, {0x2c, 0x48}, {0x2d, 0x34}, {0x2e, 0x00},
{0x2f, 0x00},
{0x30, 0x00}, {0x31, 0x00}, {0x32, 0x00}, {0x33, 0x00},
{0x34, 0x00}, {0x35, 0x01}, {0x36, 0x93}, {0x37, 0x0c},
{0x38, 0x00}, {0x39, 0x00}, {0x3a, 0xf8}, {0x38, 0x80},
{}
};
const struct hda_alc298_mbxinit *seq;
if (action != HDA_FIXUP_ACT_INIT)
return;
/* Start */
snd_hda_codec_write(codec, 0x06, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x00);
snd_hda_codec_write(codec, 0x06, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x80);
alc_write_coef_idx(codec, 0x26, 0xf000);
alc_write_coef_idx(codec, 0x22, 0x31);
alc_write_coef_idx(codec, 0x23, 0x0b);
alc_write_coef_idx(codec, 0x25, 0x00);
snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0x26);
snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, 0xb010);
for (seq = dac_init; seq->value_0x23; seq++)
alc298_huawei_mbx_stereo_seq(codec, seq, seq == dac_init);
}
static void alc269_fixup_x101_headset_mic(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
@ -5780,6 +5850,7 @@ enum {
ALC255_FIXUP_DUMMY_LINEOUT_VERB,
ALC255_FIXUP_DELL_HEADSET_MIC,
ALC256_FIXUP_HUAWEI_MACH_WX9_PINS,
ALC298_FIXUP_HUAWEI_MBX_STEREO,
ALC295_FIXUP_HP_X360,
ALC221_FIXUP_HP_HEADSET_MIC,
ALC285_FIXUP_LENOVO_HEADPHONE_NOISE,
@ -6089,6 +6160,12 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC255_FIXUP_MIC_MUTE_LED
},
[ALC298_FIXUP_HUAWEI_MBX_STEREO] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc298_fixup_huawei_mbx_stereo,
.chained = true,
.chain_id = ALC255_FIXUP_MIC_MUTE_LED
},
[ALC269_FIXUP_ASUS_X101_FUNC] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc269_fixup_x101_headset_mic,
@ -7280,6 +7357,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
{.id = ALC225_FIXUP_HEADSET_JACK, .name = "alc-headset-jack"},
{.id = ALC295_FIXUP_CHROME_BOOK, .name = "alc-chrome-book"},
{.id = ALC299_FIXUP_PREDATOR_SPK, .name = "predator-spk"},
{.id = ALC298_FIXUP_HUAWEI_MBX_STEREO, .name = "huawei-mbx-stereo"},
{}
};
#define ALC225_STANDARD_PINS \
@ -7590,10 +7668,6 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
{0x12, 0x90a60120},
{0x14, 0x90170110},
{0x21, 0x0321101f}),
SND_HDA_PIN_QUIRK(0x10ec0289, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE,
{0x12, 0xb7a60130},
{0x14, 0x90170110},
{0x21, 0x04211020}),
SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
ALC290_STANDARD_PINS,
{0x15, 0x04211040},
@ -7703,6 +7777,19 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
{}
};
/* This is the fallback pin_fixup_tbl for alc269 family, to make the tbl match
* more machines, don't need to match all valid pins, just need to match
* all the pins defined in the tbl. Just because of this reason, it is possible
* that a single machine matches multiple tbls, so there is one limitation:
* at most one tbl is allowed to define for the same vendor and same codec
*/
static const struct snd_hda_pin_quirk alc269_fallback_pin_fixup_tbl[] = {
SND_HDA_PIN_QUIRK(0x10ec0289, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE,
{0x19, 0x40000000},
{0x1b, 0x40000000}),
{}
};
static void alc269_fill_coef(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
@ -7892,7 +7979,8 @@ static int patch_alc269(struct hda_codec *codec)
snd_hda_pick_fixup(codec, alc269_fixup_models,
alc269_fixup_tbl, alc269_fixups);
snd_hda_pick_pin_fixup(codec, alc269_pin_fixup_tbl, alc269_fixups);
snd_hda_pick_pin_fixup(codec, alc269_pin_fixup_tbl, alc269_fixups, true);
snd_hda_pick_pin_fixup(codec, alc269_fallback_pin_fixup_tbl, alc269_fixups, false);
snd_hda_pick_fixup(codec, NULL, alc269_fixup_vendor_tbl,
alc269_fixups);
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
@ -8026,7 +8114,8 @@ static int patch_alc861(struct hda_codec *codec)
return err;
spec = codec->spec;
spec->gen.beep_nid = 0x23;
if (has_cdefine_beep(codec))
spec->gen.beep_nid = 0x23;
#ifdef CONFIG_PM
spec->power_hook = alc_power_eapd;
@ -8127,7 +8216,8 @@ static int patch_alc861vd(struct hda_codec *codec)
return err;
spec = codec->spec;
spec->gen.beep_nid = 0x23;
if (has_cdefine_beep(codec))
spec->gen.beep_nid = 0x23;
spec->shutup = alc_eapd_shutup;
@ -8267,6 +8357,45 @@ static void alc662_fixup_usi_headset_mic(struct hda_codec *codec,
}
}
static void alc662_aspire_ethos_mute_speakers(struct hda_codec *codec,
struct hda_jack_callback *cb)
{
/* surround speakers at 0x1b already get muted automatically when
* headphones are plugged in, but we have to mute/unmute the remaining
* channels manually:
* 0x15 - front left/front right
* 0x18 - front center/ LFE
*/
if (snd_hda_jack_detect_state(codec, 0x1b) == HDA_JACK_PRESENT) {
snd_hda_set_pin_ctl_cache(codec, 0x15, 0);
snd_hda_set_pin_ctl_cache(codec, 0x18, 0);
} else {
snd_hda_set_pin_ctl_cache(codec, 0x15, PIN_OUT);
snd_hda_set_pin_ctl_cache(codec, 0x18, PIN_OUT);
}
}
static void alc662_fixup_aspire_ethos_hp(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
/* Pin 0x1b: shared headphones jack and surround speakers */
if (!is_jack_detectable(codec, 0x1b))
return;
switch (action) {
case HDA_FIXUP_ACT_PRE_PROBE:
snd_hda_jack_detect_enable_callback(codec, 0x1b,
alc662_aspire_ethos_mute_speakers);
break;
case HDA_FIXUP_ACT_INIT:
/* Make sure to start in a correct state, i.e. if
* headphones have been plugged in before powering up the system
*/
alc662_aspire_ethos_mute_speakers(codec, NULL);
break;
}
}
static struct coef_fw alc668_coefs[] = {
WRITE_COEF(0x01, 0xbebe), WRITE_COEF(0x02, 0xaaaa), WRITE_COEF(0x03, 0x0),
WRITE_COEF(0x04, 0x0180), WRITE_COEF(0x06, 0x0), WRITE_COEF(0x07, 0x0f80),
@ -8338,6 +8467,9 @@ enum {
ALC662_FIXUP_USI_FUNC,
ALC662_FIXUP_USI_HEADSET_MODE,
ALC662_FIXUP_LENOVO_MULTI_CODECS,
ALC669_FIXUP_ACER_ASPIRE_ETHOS,
ALC669_FIXUP_ACER_ASPIRE_ETHOS_SUBWOOFER,
ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET,
};
static const struct hda_fixup alc662_fixups[] = {
@ -8664,6 +8796,33 @@ static const struct hda_fixup alc662_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc233_alc662_fixup_lenovo_dual_codecs,
},
[ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc662_fixup_aspire_ethos_hp,
},
[ALC669_FIXUP_ACER_ASPIRE_ETHOS_SUBWOOFER] = {
.type = HDA_FIXUP_VERBS,
/* subwoofer needs an extra GPIO setting to become audible */
.v.verbs = (const struct hda_verb[]) {
{0x01, AC_VERB_SET_GPIO_MASK, 0x02},
{0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
{0x01, AC_VERB_SET_GPIO_DATA, 0x00},
{ }
},
.chained = true,
.chain_id = ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET
},
[ALC669_FIXUP_ACER_ASPIRE_ETHOS] = {
.type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) {
{ 0x15, 0x92130110 }, /* front speakers */
{ 0x18, 0x99130111 }, /* center/subwoofer */
{ 0x1b, 0x11130012 }, /* surround plus jack for HP */
{ }
},
.chained = true,
.chain_id = ALC669_FIXUP_ACER_ASPIRE_ETHOS_SUBWOOFER
},
};
static const struct snd_pci_quirk alc662_fixup_tbl[] = {
@ -8709,6 +8868,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = {
SND_PCI_QUIRK(0x19da, 0xa130, "Zotac Z68", ALC662_FIXUP_ZOTAC_Z68),
SND_PCI_QUIRK(0x1b0a, 0x01b8, "ACER Veriton", ALC662_FIXUP_ACER_VERITON),
SND_PCI_QUIRK(0x1b35, 0x2206, "CZC P10T", ALC662_FIXUP_CZC_P10T),
SND_PCI_QUIRK(0x1025, 0x0566, "Acer Aspire Ethos 8951G", ALC669_FIXUP_ACER_ASPIRE_ETHOS),
#if 0
/* Below is a quirk table taken from the old code.
@ -8802,6 +8962,7 @@ static const struct hda_model_fixup alc662_fixup_models[] = {
{.id = ALC892_FIXUP_ASROCK_MOBO, .name = "asrock-mobo"},
{.id = ALC662_FIXUP_USI_HEADSET_MODE, .name = "usi-headset"},
{.id = ALC662_FIXUP_LENOVO_MULTI_CODECS, .name = "dual-codecs"},
{.id = ALC669_FIXUP_ACER_ASPIRE_ETHOS, .name = "aspire-ethos"},
{}
};
@ -8877,7 +9038,7 @@ static int patch_alc662(struct hda_codec *codec)
snd_hda_pick_fixup(codec, alc662_fixup_models,
alc662_fixup_tbl, alc662_fixups);
snd_hda_pick_pin_fixup(codec, alc662_pin_fixup_tbl, alc662_fixups);
snd_hda_pick_pin_fixup(codec, alc662_pin_fixup_tbl, alc662_fixups, true);
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
alc_auto_parse_customize_define(codec);

View File

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

View File

@ -49,6 +49,14 @@ static const struct pci_device_id snd_lx6464es_ids[] = {
PCI_VENDOR_ID_DIGIGRAM,
PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_CAE_SERIAL_SUBSYSTEM),
}, /* LX6464ES-CAE */
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES,
PCI_VENDOR_ID_DIGIGRAM,
PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ESE_SERIAL_SUBSYSTEM),
}, /* LX6464ESe */
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES,
PCI_VENDOR_ID_DIGIGRAM,
PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ESE_CAE_SERIAL_SUBSYSTEM),
}, /* LX6464ESe-CAE */
{ 0, },
};

View File

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

View File

@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-utils.o
snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-utils.o soc-dai.o soc-component.o
snd-soc-core-objs += soc-pcm.o soc-io.o soc-devres.o soc-ops.o
snd-soc-core-$(CONFIG_SND_SOC_COMPRESS) += soc-compress.o
@ -39,7 +39,6 @@ obj-$(CONFIG_SND_SOC) += intel/
obj-$(CONFIG_SND_SOC) += mediatek/
obj-$(CONFIG_SND_SOC) += meson/
obj-$(CONFIG_SND_SOC) += mxs/
obj-$(CONFIG_SND_SOC) += nuc900/
obj-$(CONFIG_SND_SOC) += kirkwood/
obj-$(CONFIG_SND_SOC) += pxa/
obj-$(CONFIG_SND_SOC) += qcom/

View File

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

View File

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

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