sound updates for 5.8-rc1
It was another busy development cycle, and the majority of changes are found in ASoC side. Below are Some highlights. ASoC core: - Lots of core cleanups and refactorings, still on-going work by Morimoto-san ASoC drivers: - Continued work on cleaning up and improving the Intel SOF stuff, along with new platform support including SoundWire - Fixes to make the Marvell SSPA driver work upstream - Support for AMD Renoir ACP, Dialog DA7212, Freescale EASRC and i.MX8M, Intel Elkhard Lake, Maxim MAX98390, Nuvoton NAU8812 and NAU8814 and Realtek RT1016. USB-audio: - Improvement for sync and implicit feedback streams with the more accurate frame size calculation and full-duplex support - Support for RME Babyface Pro and Prioneer DJ DJM HD-audio: - Fixes for Mic mute LED on HP machines - Re-enable support of Intel SST driver for SKL/KBL platforms FireWire: - Lots of refactoring, add support for RME FireFace and MOTU UltraLite-mk3 -----BEGIN PGP SIGNATURE----- iQJCBAABCAAsFiEEIXTw5fNLNI7mMiVaLtJE4w1nLE8FAl7Y3HAOHHRpd2FpQHN1 c2UuZGUACgkQLtJE4w1nLE95NA//YzYnCmwyir4tY5M9uiN2Mqu/JZkqmJmQH+MB xt0J7RYeYcxDasCKU1ibi8U3qGmSyF58DuFmkWsaVvDFqGKOpcXh6HZRhzXt4qng 14c7zUW1T/7HWdGoRqySIP27Xtwb72o1g8ZriG+8vop/I4OsG2qj5KaVIdRHVvoL Nbciyiauv7/Ad1U/pULTjP/tDR7VtbNXrFcAMFXAdAo9swvo9mcRDzkMdwaOz2Ex z+qMhK/y07Lf1IcPH8ruFmth7nCVLQ3YTp6vGCJKDU8/d8yW+NfEnHZVYq1rJwvT iUC2NmNPl/R4kWIOGK+QuWQzOv1/pme2A4VWSznEQDZ97BYL5+TzvXMu0XbAr1SV T9eo82EJM1AYiBEIbxdTUQhidbGz7mmP4iTAykGNKlrwjk6n7TXmOcUE8TqAK2qm CWEeNJKqjUEQxmB9ad4MMSs02zhzM2inyPNREZwr5d/isJEPrtOC+djG+0yBtmiv KgcVYUFfZ1WXLd5Q20S0KVYCTB5f20ZfMrtExbMqdeHoXWQRZpMmSFFnKslfFKX6 /jHqHfsmLVV83QRL1Ldr/UNP7HKPrlSSulUFqZ4BKjgIkz1DpRFtqIoseb8geJj3 YSpV8i6sxM3OgKPZTaOWXYGED+/5V3w+HDLSN3kGECGy6ex/Y0Hpd8U3W95IxmOs y1x9fbo= =E+d+ -----END PGP SIGNATURE----- Merge tag 'sound-5.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound Pull sound updates from Takashi Iwai: "It was another busy development cycle, and the majority of changes are found in ASoC side. Below are Some highlights. ASoC core: - Lots of core cleanups and refactorings, still on-going work by Morimoto-san ASoC drivers: - Continued work on cleaning up and improving the Intel SOF stuff, along with new platform support including SoundWire - Fixes to make the Marvell SSPA driver work upstream - Support for AMD Renoir ACP, Dialog DA7212, Freescale EASRC and i.MX8M, Intel Elkhard Lake, Maxim MAX98390, Nuvoton NAU8812 and NAU8814 and Realtek RT1016. USB-audio: - Improvement for sync and implicit feedback streams with the more accurate frame size calculation and full-duplex support - Support for RME Babyface Pro and Prioneer DJ DJM HD-audio: - Fixes for Mic mute LED on HP machines - Re-enable support of Intel SST driver for SKL/KBL platforms FireWire: - Lots of refactoring, add support for RME FireFace and MOTU UltraLite-mk3" * tag 'sound-5.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (428 commits) ALSA: es1688: Add the missed snd_card_free() ALSA: hda: add sienna_cichlid audio asic id for sienna_cichlid up ALSA: usb-audio: Add Pioneer DJ DJM-900NXS2 support ASoC: qcom: q6asm-dai: kCFI fix ASoC: soc-card: add snd_soc_card_remove_dai_link() ASoC: soc-card: add snd_soc_card_add_dai_link() ASoC: soc-card: add snd_soc_card_set_bias_level_post() ASoC: soc-card: add snd_soc_card_set_bias_level() ASoC: soc-card: add snd_soc_card_remove() ASoC: soc-card: add snd_soc_card_late_probe() ASoC: soc-card: add snd_soc_card_probe() ASoC: soc-card: add probed bit field to snd_soc_card ASoC: soc-card: add snd_soc_card_resume_post() ASoC: soc-card: add snd_soc_card_resume_pre() ASoC: soc-card: add snd_soc_card_suspend_post() ASoC: soc-card: add snd_soc_card_suspend_pre() ASoC: soc-card: move snd_soc_card_subclass to soc-card ASoC: soc-card: move snd_soc_card_get_codec_dai() to soc-card ASoC: soc-card: move snd_soc_card_set/get_drvdata() to soc-card ASoC: soc-card: move snd_soc_card_jack_new() to soc-card ...
This commit is contained in:
commit
631d691408
|
@ -17,6 +17,8 @@ properties:
|
|||
compatible:
|
||||
enum:
|
||||
- fsl,imx8qxp-dsp
|
||||
- fsl,imx8qm-dsp
|
||||
- fsl,imx8mp-dsp
|
||||
|
||||
reg:
|
||||
description: Should contain register location and length
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
Dialog Semiconductor DA7213 Audio Codec bindings
|
||||
Dialog Semiconductor DA7212/DA7213 Audio Codec bindings
|
||||
|
||||
======
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "dlg,da7213"
|
||||
- compatible : Should be "dlg,da7212" or "dlg,da7213"
|
||||
- reg: Specifies the I2C slave address
|
||||
|
||||
Optional properties:
|
||||
|
@ -21,6 +21,10 @@ Optional properties:
|
|||
- dlg,dmic-clkrate : DMIC clock frequency (Hz).
|
||||
[<1500000>, <3000000>]
|
||||
|
||||
- VDDA-supply : Regulator phandle for Analogue power supply
|
||||
- VDDMIC-supply : Regulator phandle for Mic Bias
|
||||
- VDDIO-supply : Regulator phandle for I/O power supply
|
||||
|
||||
======
|
||||
|
||||
Example:
|
||||
|
|
|
@ -51,6 +51,10 @@ Optional properties:
|
|||
will be in use as default. Otherwise, the big endian
|
||||
mode will be in use for all the device registers.
|
||||
|
||||
- fsl,asrc-format : Defines a mutual sample format used by DPCM Back
|
||||
Ends, which can replace the fsl,asrc-width.
|
||||
The value is 2 (S16_LE), or 6 (S24_LE).
|
||||
|
||||
Example:
|
||||
|
||||
asrc: asrc@2034000 {
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/sound/fsl,easrc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: NXP Asynchronous Sample Rate Converter (ASRC) Controller
|
||||
|
||||
maintainers:
|
||||
- Shengjiu Wang <shengjiu.wang@nxp.com>
|
||||
|
||||
properties:
|
||||
$nodename:
|
||||
pattern: "^easrc@.*"
|
||||
|
||||
compatible:
|
||||
const: fsl,imx8mn-easrc
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: Peripheral clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: mem
|
||||
|
||||
dmas:
|
||||
maxItems: 8
|
||||
|
||||
dma-names:
|
||||
items:
|
||||
- const: ctx0_rx
|
||||
- const: ctx0_tx
|
||||
- const: ctx1_rx
|
||||
- const: ctx1_tx
|
||||
- const: ctx2_rx
|
||||
- const: ctx2_tx
|
||||
- const: ctx3_rx
|
||||
- const: ctx3_tx
|
||||
|
||||
firmware-name:
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/string
|
||||
- const: imx/easrc/easrc-imx8mn.bin
|
||||
description: The coefficient table for the filters
|
||||
|
||||
fsl,asrc-rate:
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32
|
||||
- minimum: 8000
|
||||
- maximum: 192000
|
||||
description: Defines a mutual sample rate used by DPCM Back Ends
|
||||
|
||||
fsl,asrc-format:
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32
|
||||
- enum: [2, 6, 10, 32, 36]
|
||||
default: 2
|
||||
description:
|
||||
Defines a mutual sample format used by DPCM Back Ends
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
- dmas
|
||||
- dma-names
|
||||
- firmware-name
|
||||
- fsl,asrc-rate
|
||||
- fsl,asrc-format
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/imx8mn-clock.h>
|
||||
|
||||
easrc: easrc@300c0000 {
|
||||
compatible = "fsl,imx8mn-easrc";
|
||||
reg = <0x0 0x300c0000 0x0 0x10000>;
|
||||
interrupts = <0x0 122 0x4>;
|
||||
clocks = <&clk IMX8MN_CLK_ASRC_ROOT>;
|
||||
clock-names = "mem";
|
||||
dmas = <&sdma2 16 23 0> , <&sdma2 17 23 0>,
|
||||
<&sdma2 18 23 0> , <&sdma2 19 23 0>,
|
||||
<&sdma2 20 23 0> , <&sdma2 21 23 0>,
|
||||
<&sdma2 22 23 0> , <&sdma2 23 23 0>;
|
||||
dma-names = "ctx0_rx", "ctx0_tx",
|
||||
"ctx1_rx", "ctx1_tx",
|
||||
"ctx2_rx", "ctx2_tx",
|
||||
"ctx3_rx", "ctx3_tx";
|
||||
firmware-name = "imx/easrc/easrc-imx8mn.bin";
|
||||
fsl,asrc-rate = <8000>;
|
||||
fsl,asrc-format = <2>;
|
||||
};
|
|
@ -12,6 +12,7 @@ Required properties:
|
|||
"fsl,imx35-esai",
|
||||
"fsl,vf610-esai",
|
||||
"fsl,imx6ull-esai",
|
||||
"fsl,imx8qm-esai",
|
||||
|
||||
- reg : Offset and length of the register set for the device.
|
||||
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0+ OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/sound/marvell,mmp-sspa.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Marvel SSPA Digital Audio Interface Bindings
|
||||
|
||||
maintainers:
|
||||
- Lubomir Rintel <lkundrak@v3.sk>
|
||||
|
||||
properties:
|
||||
$nodename:
|
||||
pattern: "^audio-controller(@.*)?$"
|
||||
|
||||
compatible:
|
||||
const: marvell,mmp-sspa
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description: RX block
|
||||
- description: TX block
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: Clock for the Audio block
|
||||
- description: I2S bit clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: audio
|
||||
- const: bitclk
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
'#sound-dai-cells':
|
||||
const: 0
|
||||
|
||||
dmas:
|
||||
items:
|
||||
- description: TX DMA Channel
|
||||
- description: RX DMA Channel
|
||||
|
||||
dma-names:
|
||||
items:
|
||||
- const: tx
|
||||
- const: rx
|
||||
|
||||
port:
|
||||
type: object
|
||||
|
||||
properties:
|
||||
endpoint:
|
||||
type: object
|
||||
|
||||
properties:
|
||||
remote-endpoint: true
|
||||
|
||||
frame-master:
|
||||
type: boolean
|
||||
description: SoC generates the frame clock
|
||||
|
||||
bitclock-master:
|
||||
type: boolean
|
||||
description: SoC generates the bit clock
|
||||
|
||||
dai-format:
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
description: The digital audio format
|
||||
const: i2s
|
||||
|
||||
required:
|
||||
- remote-endpoint
|
||||
|
||||
required:
|
||||
- endpoint
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- "#sound-dai-cells"
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
- dmas
|
||||
- dma-names
|
||||
- port
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/marvell,mmp2.h>
|
||||
|
||||
audio-controller@d42a0c00 {
|
||||
compatible = "marvell,mmp-sspa";
|
||||
reg = <0xd42a0c00 0x30>,
|
||||
<0xd42a0c80 0x30>;
|
||||
interrupts = <2>;
|
||||
clock-names = "audio", "bitclk";
|
||||
clocks = <&soc_clocks 127>,
|
||||
<&audio_clk 1>;
|
||||
#sound-dai-cells = <0>;
|
||||
dmas = <&adma0 0>, <&adma0 1>;
|
||||
dma-names = "tx", "rx";
|
||||
port {
|
||||
endpoint {
|
||||
remote-endpoint = <&rt5631_0>;
|
||||
frame-master;
|
||||
bitclock-master;
|
||||
dai-format = "i2s";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
|
@ -1,10 +1,11 @@
|
|||
NAU8810 audio CODEC
|
||||
NAU8810/NAU8812/NAU8814 audio CODEC
|
||||
|
||||
This device supports I2C only.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "nuvoton,nau8810"
|
||||
- compatible : One of "nuvoton,nau8810" or "nuvoton,nau8812" or
|
||||
"nuvoton,nau8814"
|
||||
|
||||
- reg : the I2C address of the device.
|
||||
|
||||
|
|
|
@ -101,5 +101,5 @@ Example:
|
|||
nuvoton,crosstalk-enable;
|
||||
|
||||
clock-names = "mclk";
|
||||
clocks = <&tegra_car TEGRA210_CLK_CLK_OUT_2>;
|
||||
clocks = <&tegra_pmc TEGRA_PMC_CLK_OUT_2>;
|
||||
};
|
||||
|
|
|
@ -29,6 +29,7 @@ Optional properties:
|
|||
- nvidia,hp-det-gpios : The GPIO that detect headphones are plugged in
|
||||
- nvidia,int-mic-en-gpios : The GPIO that enables the internal microphone
|
||||
- nvidia,ext-mic-en-gpios : The GPIO that enables the external microphone
|
||||
- nvidia,headset : The Mic Jack represents state of the headset microphone pin
|
||||
|
||||
Example:
|
||||
|
||||
|
|
|
@ -30,6 +30,8 @@ Required properties:
|
|||
- reg : Must contain an address for each entry in reg-names.
|
||||
- reg-names : A list which must include the following entries:
|
||||
* "lpass-lpaif"
|
||||
- #address-cells : Must be 1
|
||||
- #size-cells : Must be 0
|
||||
|
||||
|
||||
|
||||
|
@ -37,6 +39,20 @@ Optional properties:
|
|||
|
||||
- qcom,adsp : Phandle for the audio DSP node
|
||||
|
||||
By default, the driver uses up to 4 MI2S SD lines, for a total of 8 channels.
|
||||
The SD lines to use can be configured by adding subnodes for each of the DAIs.
|
||||
|
||||
Required properties for each DAI (represented by a subnode):
|
||||
- reg : Must be one of the DAI IDs
|
||||
(usually part of dt-bindings header)
|
||||
- qcom,playback-sd-lines: List of serial data lines to use for playback
|
||||
Each SD line should be represented by a number from 0-3.
|
||||
- qcom,capture-sd-lines : List of serial data lines to use for capture
|
||||
Each SD line should be represented by a number from 0-3.
|
||||
|
||||
Note that adding a subnode changes the default to "no lines configured",
|
||||
so both playback and capture lines should be configured when a subnode is added.
|
||||
|
||||
Example:
|
||||
|
||||
lpass@28100000 {
|
||||
|
@ -51,4 +67,13 @@ lpass@28100000 {
|
|||
reg = <0x28100000 0x10000>;
|
||||
reg-names = "lpass-lpaif";
|
||||
qcom,adsp = <&adsp>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
/* Optional to set different MI2S SD lines */
|
||||
dai@3 {
|
||||
reg = <MI2S_QUATERNARY>;
|
||||
qcom,playback-sd-lines = <0 1>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -29,7 +29,7 @@ used by the apr service device.
|
|||
Definition: Must be 0
|
||||
|
||||
= EXAMPLE
|
||||
q6adm@8 {
|
||||
apr-service@8 {
|
||||
compatible = "qcom,q6adm";
|
||||
reg = <APR_SVC_ADM>;
|
||||
q6routing: routing {
|
||||
|
|
|
@ -100,7 +100,7 @@ configuration of each dai. Must contain the following properties.
|
|||
|
||||
= EXAMPLE
|
||||
|
||||
q6afe@4 {
|
||||
apr-service@4 {
|
||||
compatible = "qcom,q6afe";
|
||||
reg = <APR_SVC_AFE>;
|
||||
|
||||
|
@ -110,12 +110,12 @@ q6afe@4 {
|
|||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
hdmi@1 {
|
||||
reg = <1>;
|
||||
dai@1 {
|
||||
reg = <HDMI_RX>;
|
||||
};
|
||||
|
||||
tdm@24 {
|
||||
reg = <24>;
|
||||
dai@24 {
|
||||
reg = <PRIMARY_TDM_RX_0>;
|
||||
qcom,tdm-sync-mode = <1>:
|
||||
qcom,tdm-sync-src = <1>;
|
||||
qcom,tdm-data-out = <0>;
|
||||
|
@ -125,8 +125,8 @@ q6afe@4 {
|
|||
|
||||
};
|
||||
|
||||
tdm@25 {
|
||||
reg = <25>;
|
||||
dai@25 {
|
||||
reg = <PRIMARY_TDM_TX_0>;
|
||||
qcom,tdm-sync-mode = <1>:
|
||||
qcom,tdm-sync-src = <1>;
|
||||
qcom,tdm-data-out = <0>;
|
||||
|
@ -135,43 +135,43 @@ q6afe@4 {
|
|||
qcom,tdm-data-align = <0>;
|
||||
};
|
||||
|
||||
prim-mi2s-rx@16 {
|
||||
reg = <16>;
|
||||
dai@16 {
|
||||
reg = <PRIMARY_MI2S_RX>;
|
||||
qcom,sd-lines = <0 2>;
|
||||
};
|
||||
|
||||
prim-mi2s-tx@17 {
|
||||
reg = <17>;
|
||||
dai@17 {
|
||||
reg = <PRIMARY_MI2S_TX>;
|
||||
qcom,sd-lines = <1>;
|
||||
};
|
||||
|
||||
sec-mi2s-rx@18 {
|
||||
reg = <18>;
|
||||
dai@18 {
|
||||
reg = <SECONDARY_MI2S_RX>;
|
||||
qcom,sd-lines = <0 3>;
|
||||
};
|
||||
|
||||
sec-mi2s-tx@19 {
|
||||
reg = <19>;
|
||||
dai@19 {
|
||||
reg = <SECONDARY_MI2S_TX>;
|
||||
qcom,sd-lines = <1>;
|
||||
};
|
||||
|
||||
tert-mi2s-rx@20 {
|
||||
reg = <20>;
|
||||
dai@20 {
|
||||
reg = <TERTIARY_MI2S_RX>;
|
||||
qcom,sd-lines = <1 3>;
|
||||
};
|
||||
|
||||
tert-mi2s-tx@21 {
|
||||
reg = <21>;
|
||||
dai@21 {
|
||||
reg = <TERTIARY_MI2S_TX>;
|
||||
qcom,sd-lines = <0>;
|
||||
};
|
||||
|
||||
quat-mi2s-rx@22 {
|
||||
reg = <22>;
|
||||
dai@22 {
|
||||
reg = <QUATERNARY_MI2S_RX>;
|
||||
qcom,sd-lines = <0>;
|
||||
};
|
||||
|
||||
quat-mi2s-tx@23 {
|
||||
reg = <23>;
|
||||
dai@23 {
|
||||
reg = <QUATERNARY_MI2S_TX>;
|
||||
qcom,sd-lines = <1>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -51,13 +51,16 @@ configuration of each dai. Must contain the following properties.
|
|||
|
||||
= EXAMPLE
|
||||
|
||||
q6asm@7 {
|
||||
apr-service@7 {
|
||||
compatible = "qcom,q6asm";
|
||||
reg = <APR_SVC_ASM>;
|
||||
q6asmdai: dais {
|
||||
compatible = "qcom,q6asm-dais";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
#sound-dai-cells = <1>;
|
||||
mm@0 {
|
||||
|
||||
dai@0 {
|
||||
reg = <0>;
|
||||
direction = <2>;
|
||||
is-compress-dai;
|
||||
|
|
|
@ -15,7 +15,7 @@ used by the apr service device.
|
|||
example "qcom,q6core-v2.0"
|
||||
|
||||
= EXAMPLE
|
||||
q6core@3 {
|
||||
apr-service@3 {
|
||||
compatible = "qcom,q6core";
|
||||
reg = <APR_SVC_ADSP_CORE>;
|
||||
};
|
||||
|
|
|
@ -263,6 +263,7 @@ Required properties:
|
|||
"renesas,rcar_sound-gen2" if generation2 (or RZ/G1)
|
||||
"renesas,rcar_sound-gen3" if generation3 (or RZ/G2)
|
||||
Examples with soctypes are:
|
||||
- "renesas,rcar_sound-r8a7742" (RZ/G1H)
|
||||
- "renesas,rcar_sound-r8a7743" (RZ/G1M)
|
||||
- "renesas,rcar_sound-r8a7744" (RZ/G1N)
|
||||
- "renesas,rcar_sound-r8a7745" (RZ/G1E)
|
||||
|
|
|
@ -24,6 +24,7 @@ properties:
|
|||
- rockchip,rk3188-i2s
|
||||
- rockchip,rk3228-i2s
|
||||
- rockchip,rk3288-i2s
|
||||
- rockchip,rk3308-i2s
|
||||
- rockchip,rk3328-i2s
|
||||
- rockchip,rk3366-i2s
|
||||
- rockchip,rk3368-i2s
|
||||
|
@ -47,14 +48,15 @@ properties:
|
|||
- const: i2s_hclk
|
||||
|
||||
dmas:
|
||||
items:
|
||||
- description: TX DMA Channel
|
||||
- description: RX DMA Channel
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
dma-names:
|
||||
items:
|
||||
- const: tx
|
||||
oneOf:
|
||||
- const: rx
|
||||
- items:
|
||||
- const: tx
|
||||
- const: rx
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
RT1016 Stereo Class D Audio Amplifier
|
||||
|
||||
This device supports I2C only.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "realtek,rt1016".
|
||||
|
||||
- reg : The I2C address of the device.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
rt1016: codec@1a {
|
||||
compatible = "realtek,rt1016";
|
||||
reg = <0x1a>;
|
||||
};
|
|
@ -1,351 +0,0 @@
|
|||
Simple-Card:
|
||||
|
||||
Simple-Card specifies audio DAI connections of SoC <-> codec.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "simple-audio-card"
|
||||
|
||||
Optional properties:
|
||||
|
||||
- simple-audio-card,name : User specified audio sound card name, one string
|
||||
property.
|
||||
- simple-audio-card,widgets : Please refer to widgets.txt.
|
||||
- simple-audio-card,routing : A list of the connections between audio components.
|
||||
Each entry is a pair of strings, the first being the
|
||||
connection's sink, the second being the connection's
|
||||
source.
|
||||
- simple-audio-card,mclk-fs : Multiplication factor between stream rate and codec
|
||||
mclk. When defined, mclk-fs property defined in
|
||||
dai-link sub nodes are ignored.
|
||||
- simple-audio-card,hp-det-gpio : Reference to GPIO that signals when
|
||||
headphones are attached.
|
||||
- simple-audio-card,mic-det-gpio : Reference to GPIO that signals when
|
||||
a microphone is attached.
|
||||
- simple-audio-card,aux-devs : List of phandles pointing to auxiliary devices, such
|
||||
as amplifiers, to be added to the sound card.
|
||||
- simple-audio-card,pin-switches : List of strings containing the widget names for
|
||||
which pin switches must be created.
|
||||
|
||||
Optional subnodes:
|
||||
|
||||
- simple-audio-card,dai-link : Container for dai-link level
|
||||
properties and the CPU and CODEC
|
||||
sub-nodes. This container may be
|
||||
omitted when the card has only one
|
||||
DAI link. See the examples and the
|
||||
section below.
|
||||
|
||||
Dai-link subnode properties and subnodes:
|
||||
|
||||
If dai-link subnode is omitted and the subnode properties are directly
|
||||
under "sound"-node the subnode property and subnode names have to be
|
||||
prefixed with "simple-audio-card,"-prefix.
|
||||
|
||||
Required dai-link subnodes:
|
||||
|
||||
- cpu : CPU sub-node
|
||||
- codec : CODEC sub-node
|
||||
|
||||
Optional dai-link subnode properties:
|
||||
|
||||
- format : CPU/CODEC common audio format.
|
||||
"i2s", "right_j", "left_j" , "dsp_a"
|
||||
"dsp_b", "ac97", "pdm", "msb", "lsb"
|
||||
- frame-master : Indicates dai-link frame master.
|
||||
phandle to a cpu or codec subnode.
|
||||
- bitclock-master : Indicates dai-link bit clock master.
|
||||
phandle to a cpu or codec subnode.
|
||||
- bitclock-inversion : bool property. Add this if the
|
||||
dai-link uses bit clock inversion.
|
||||
- frame-inversion : bool property. Add this if the
|
||||
dai-link uses frame clock inversion.
|
||||
- mclk-fs : Multiplication factor between stream
|
||||
rate and codec mclk, applied only for
|
||||
the dai-link.
|
||||
|
||||
For backward compatibility the frame-master and bitclock-master
|
||||
properties can be used as booleans in codec subnode to indicate if the
|
||||
codec is the dai-link frame or bit clock master. In this case there
|
||||
should be no dai-link node, the same properties should not be present
|
||||
at sound-node level, and the bitclock-inversion and frame-inversion
|
||||
properties should also be placed in the codec node if needed.
|
||||
|
||||
Required CPU/CODEC subnodes properties:
|
||||
|
||||
- sound-dai : phandle and port of CPU/CODEC
|
||||
|
||||
Optional CPU/CODEC subnodes properties:
|
||||
|
||||
- dai-tdm-slot-num : Please refer to tdm-slot.txt.
|
||||
- dai-tdm-slot-width : Please refer to tdm-slot.txt.
|
||||
- clocks / system-clock-frequency : specify subnode's clock if needed.
|
||||
it can be specified via "clocks" if system has
|
||||
clock node (= common clock), or "system-clock-frequency"
|
||||
(if system doens't support common clock)
|
||||
If a clock is specified, it is
|
||||
enabled with clk_prepare_enable()
|
||||
in dai startup() and disabled with
|
||||
clk_disable_unprepare() in dai
|
||||
shutdown().
|
||||
If a clock is specified and a
|
||||
multiplication factor is given with
|
||||
mclk-fs, the clock will be set to the
|
||||
calculated mclk frequency when the
|
||||
stream starts.
|
||||
- system-clock-direction-out : specifies clock direction as 'out' on
|
||||
initialization. It is useful for some aCPUs with
|
||||
fixed clocks.
|
||||
|
||||
-------------------------------------------
|
||||
Example 1 - single DAI link:
|
||||
-------------------------------------------
|
||||
|
||||
sound {
|
||||
compatible = "simple-audio-card";
|
||||
simple-audio-card,name = "VF610-Tower-Sound-Card";
|
||||
simple-audio-card,format = "left_j";
|
||||
simple-audio-card,bitclock-master = <&dailink0_master>;
|
||||
simple-audio-card,frame-master = <&dailink0_master>;
|
||||
simple-audio-card,widgets =
|
||||
"Microphone", "Microphone Jack",
|
||||
"Headphone", "Headphone Jack",
|
||||
"Speaker", "External Speaker";
|
||||
simple-audio-card,routing =
|
||||
"MIC_IN", "Microphone Jack",
|
||||
"Headphone Jack", "HP_OUT",
|
||||
"External Speaker", "LINE_OUT";
|
||||
|
||||
simple-audio-card,cpu {
|
||||
sound-dai = <&sh_fsi2 0>;
|
||||
};
|
||||
|
||||
dailink0_master: simple-audio-card,codec {
|
||||
sound-dai = <&ak4648>;
|
||||
clocks = <&osc>;
|
||||
};
|
||||
};
|
||||
|
||||
&i2c0 {
|
||||
ak4648: ak4648@12 {
|
||||
#sound-dai-cells = <0>;
|
||||
compatible = "asahi-kasei,ak4648";
|
||||
reg = <0x12>;
|
||||
};
|
||||
};
|
||||
|
||||
sh_fsi2: sh_fsi2@ec230000 {
|
||||
#sound-dai-cells = <1>;
|
||||
compatible = "renesas,sh_fsi2";
|
||||
reg = <0xec230000 0x400>;
|
||||
interrupt-parent = <&gic>;
|
||||
interrupts = <0 146 0x4>;
|
||||
};
|
||||
|
||||
-------------------------------------------
|
||||
Example 2 - many DAI links:
|
||||
-------------------------------------------
|
||||
|
||||
sound {
|
||||
compatible = "simple-audio-card";
|
||||
simple-audio-card,name = "Cubox Audio";
|
||||
|
||||
simple-audio-card,dai-link@0 { /* I2S - HDMI */
|
||||
reg = <0>;
|
||||
format = "i2s";
|
||||
cpu {
|
||||
sound-dai = <&audio1 0>;
|
||||
};
|
||||
codec {
|
||||
sound-dai = <&tda998x 0>;
|
||||
};
|
||||
};
|
||||
|
||||
simple-audio-card,dai-link@1 { /* S/PDIF - HDMI */
|
||||
reg = <1>;
|
||||
cpu {
|
||||
sound-dai = <&audio1 1>;
|
||||
};
|
||||
codec {
|
||||
sound-dai = <&tda998x 1>;
|
||||
};
|
||||
};
|
||||
|
||||
simple-audio-card,dai-link@2 { /* S/PDIF - S/PDIF */
|
||||
reg = <2>;
|
||||
cpu {
|
||||
sound-dai = <&audio1 1>;
|
||||
};
|
||||
codec {
|
||||
sound-dai = <&spdif_codec>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
-------------------------------------------
|
||||
Example 3 - route audio from IMX6 SSI2 through TLV320DAC3100 codec
|
||||
through TPA6130A2 amplifier to headphones:
|
||||
-------------------------------------------
|
||||
|
||||
&i2c0 {
|
||||
codec: tlv320dac3100@18 {
|
||||
compatible = "ti,tlv320dac3100";
|
||||
...
|
||||
}
|
||||
|
||||
amp: tpa6130a2@60 {
|
||||
compatible = "ti,tpa6130a2";
|
||||
...
|
||||
}
|
||||
}
|
||||
|
||||
sound {
|
||||
compatible = "simple-audio-card";
|
||||
...
|
||||
simple-audio-card,widgets =
|
||||
"Headphone", "Headphone Jack";
|
||||
simple-audio-card,routing =
|
||||
"Headphone Jack", "HPLEFT",
|
||||
"Headphone Jack", "HPRIGHT",
|
||||
"LEFTIN", "HPL",
|
||||
"RIGHTIN", "HPR";
|
||||
simple-audio-card,aux-devs = <&>;
|
||||
simple-audio-card,cpu {
|
||||
sound-dai = <&ssi2>;
|
||||
};
|
||||
simple-audio-card,codec {
|
||||
sound-dai = <&codec>;
|
||||
clocks = ...
|
||||
};
|
||||
};
|
||||
|
||||
-------------------------------------------
|
||||
Example 4. Sampling Rate Conversion
|
||||
-------------------------------------------
|
||||
|
||||
sound {
|
||||
compatible = "simple-audio-card";
|
||||
|
||||
simple-audio-card,name = "rsnd-ak4643";
|
||||
simple-audio-card,format = "left_j";
|
||||
simple-audio-card,bitclock-master = <&sndcodec>;
|
||||
simple-audio-card,frame-master = <&sndcodec>;
|
||||
|
||||
simple-audio-card,convert-rate = <48000>;
|
||||
|
||||
simple-audio-card,prefix = "ak4642";
|
||||
simple-audio-card,routing = "ak4642 Playback", "DAI0 Playback",
|
||||
"DAI0 Capture", "ak4642 Capture";
|
||||
|
||||
sndcpu: simple-audio-card,cpu {
|
||||
sound-dai = <&rcar_sound>;
|
||||
};
|
||||
|
||||
sndcodec: simple-audio-card,codec {
|
||||
sound-dai = <&ak4643>;
|
||||
system-clock-frequency = <11289600>;
|
||||
};
|
||||
};
|
||||
|
||||
-------------------------------------------
|
||||
Example 5. 2 CPU 1 Codec (Mixing)
|
||||
-------------------------------------------
|
||||
sound {
|
||||
compatible = "simple-audio-card";
|
||||
|
||||
simple-audio-card,name = "rsnd-ak4643";
|
||||
simple-audio-card,format = "left_j";
|
||||
simple-audio-card,bitclock-master = <&dpcmcpu>;
|
||||
simple-audio-card,frame-master = <&dpcmcpu>;
|
||||
|
||||
simple-audio-card,routing = "ak4642 Playback", "DAI0 Playback",
|
||||
"ak4642 Playback", "DAI1 Playback";
|
||||
|
||||
dpcmcpu: cpu@0 {
|
||||
sound-dai = <&rcar_sound 0>;
|
||||
};
|
||||
|
||||
cpu@1 {
|
||||
sound-dai = <&rcar_sound 1>;
|
||||
};
|
||||
|
||||
codec {
|
||||
prefix = "ak4642";
|
||||
sound-dai = <&ak4643>;
|
||||
clocks = <&audio_clock>;
|
||||
};
|
||||
};
|
||||
|
||||
-------------------------------------------
|
||||
Example 6 - many DAI links with DPCM:
|
||||
-------------------------------------------
|
||||
|
||||
CPU0 ------ ak4613
|
||||
CPU1 ------ PCM3168A-p /* DPCM 1ch/2ch */
|
||||
CPU2 --/ /* DPCM 3ch/4ch */
|
||||
CPU3 --/ /* DPCM 5ch/6ch */
|
||||
CPU4 --/ /* DPCM 7ch/8ch */
|
||||
CPU5 ------ PCM3168A-c
|
||||
|
||||
sound {
|
||||
compatible = "simple-audio-card";
|
||||
|
||||
simple-audio-card,routing =
|
||||
"pcm3168a Playback", "DAI1 Playback",
|
||||
"pcm3168a Playback", "DAI2 Playback",
|
||||
"pcm3168a Playback", "DAI3 Playback",
|
||||
"pcm3168a Playback", "DAI4 Playback";
|
||||
|
||||
simple-audio-card,dai-link@0 {
|
||||
format = "left_j";
|
||||
bitclock-master = <&sndcpu0>;
|
||||
frame-master = <&sndcpu0>;
|
||||
|
||||
sndcpu0: cpu {
|
||||
sound-dai = <&rcar_sound 0>;
|
||||
};
|
||||
codec {
|
||||
sound-dai = <&ak4613>;
|
||||
};
|
||||
};
|
||||
simple-audio-card,dai-link@1 {
|
||||
format = "i2s";
|
||||
bitclock-master = <&sndcpu1>;
|
||||
frame-master = <&sndcpu1>;
|
||||
|
||||
convert-channels = <8>; /* TDM Split */
|
||||
|
||||
sndcpu1: cpu@0 {
|
||||
sound-dai = <&rcar_sound 1>;
|
||||
};
|
||||
cpu@1 {
|
||||
sound-dai = <&rcar_sound 2>;
|
||||
};
|
||||
cpu@2 {
|
||||
sound-dai = <&rcar_sound 3>;
|
||||
};
|
||||
cpu@3 {
|
||||
sound-dai = <&rcar_sound 4>;
|
||||
};
|
||||
codec {
|
||||
mclk-fs = <512>;
|
||||
prefix = "pcm3168a";
|
||||
dai-tdm-slot-num = <8>;
|
||||
sound-dai = <&pcm3168a 0>;
|
||||
};
|
||||
};
|
||||
simple-audio-card,dai-link@2 {
|
||||
format = "i2s";
|
||||
bitclock-master = <&sndcpu2>;
|
||||
frame-master = <&sndcpu2>;
|
||||
|
||||
sndcpu2: cpu {
|
||||
sound-dai = <&rcar_sound 5>;
|
||||
};
|
||||
codec {
|
||||
mclk-fs = <512>;
|
||||
prefix = "pcm3168a";
|
||||
sound-dai = <&pcm3168a 1>;
|
||||
};
|
||||
};
|
||||
};
|
|
@ -0,0 +1,484 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/sound/simple-card.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Simple Audio Card Driver Device Tree Bindings
|
||||
|
||||
maintainers:
|
||||
- Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
|
||||
|
||||
definitions:
|
||||
|
||||
frame-master:
|
||||
description: Indicates dai-link frame master.
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
- maxItems: 1
|
||||
|
||||
bitclock-master:
|
||||
description: Indicates dai-link bit clock master
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
- maxItems: 1
|
||||
|
||||
frame-inversion:
|
||||
description: dai-link uses frame clock inversion
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
|
||||
bitclock-inversion:
|
||||
description: dai-link uses bit clock inversion
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
|
||||
dai-tdm-slot-num:
|
||||
description: see tdm-slot.txt.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
dai-tdm-slot-width:
|
||||
description: see tdm-slot.txt.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
system-clock-frequency:
|
||||
description: |
|
||||
If a clock is specified and a multiplication factor is given with
|
||||
mclk-fs, the clock will be set to the calculated mclk frequency
|
||||
when the stream starts.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
system-clock-direction-out:
|
||||
description: |
|
||||
specifies clock direction as 'out' on initialization.
|
||||
It is useful for some aCPUs with fixed clocks.
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
|
||||
mclk-fs:
|
||||
description: |
|
||||
Multiplication factor between stream rate and codec mclk.
|
||||
When defined, mclk-fs property defined in dai-link sub nodes are ignored.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
aux-devs:
|
||||
description: |
|
||||
List of phandles pointing to auxiliary devices, such
|
||||
as amplifiers, to be added to the sound card.
|
||||
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
|
||||
convert-rate:
|
||||
description: CPU to Codec rate convert.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
convert-channels:
|
||||
description: CPU to Codec rate channels.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
prefix:
|
||||
description: "device name prefix"
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
|
||||
label:
|
||||
maxItems: 1
|
||||
|
||||
routing:
|
||||
description: |
|
||||
A list of the connections between audio components.
|
||||
Each entry is a pair of strings, the first being the
|
||||
connection's sink, the second being the connection's source.
|
||||
$ref: /schemas/types.yaml#/definitions/non-unique-string-array
|
||||
|
||||
widgets:
|
||||
description: User specified audio sound widgets.
|
||||
$ref: /schemas/types.yaml#/definitions/non-unique-string-array
|
||||
|
||||
pin-switches:
|
||||
description: the widget names for which pin switches must be created.
|
||||
$ref: /schemas/types.yaml#/definitions/string-array
|
||||
|
||||
format:
|
||||
description: audio format.
|
||||
items:
|
||||
enum:
|
||||
- i2s
|
||||
- right_j
|
||||
- left_j
|
||||
- dsp_a
|
||||
- dsp_b
|
||||
- ac97
|
||||
- pdm
|
||||
- msb
|
||||
- lsb
|
||||
|
||||
dai:
|
||||
type: object
|
||||
properties:
|
||||
sound-dai:
|
||||
maxItems: 1
|
||||
|
||||
# common properties
|
||||
mclk-fs:
|
||||
$ref: "#/definitions/mclk-fs"
|
||||
prefix:
|
||||
$ref: "#/definitions/prefix"
|
||||
frame-inversion:
|
||||
$ref: "#/definitions/frame-inversion"
|
||||
bitclock-inversion:
|
||||
$ref: "#/definitions/bitclock-inversion"
|
||||
frame-master:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
bitclock-master:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
|
||||
dai-tdm-slot-num:
|
||||
$ref: "#/definitions/dai-tdm-slot-num"
|
||||
dai-tdm-slot-width:
|
||||
$ref: "#/definitions/dai-tdm-slot-width"
|
||||
clocks:
|
||||
maxItems: 1
|
||||
system-clock-frequency:
|
||||
$ref: "#/definitions/system-clock-frequency"
|
||||
system-clock-direction-out:
|
||||
$ref: "#/definitions/system-clock-direction-out"
|
||||
required:
|
||||
- sound-dai
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- simple-audio-card
|
||||
- simple-scu-audio-card
|
||||
|
||||
"#address-cells":
|
||||
const: 1
|
||||
"#size-cells":
|
||||
const: 0
|
||||
|
||||
label:
|
||||
$ref: "#/definitions/label"
|
||||
|
||||
simple-audio-card,name:
|
||||
description: User specified audio sound card name.
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
|
||||
# use patternProperties to avoid naming "xxx,yyy" issue
|
||||
patternProperties:
|
||||
"^simple-audio-card,widgets$":
|
||||
$ref: "#/definitions/widgets"
|
||||
"^simple-audio-card,routing$":
|
||||
$ref: "#/definitions/routing"
|
||||
"^simple-audio-card,cpu(@[0-9a-f]+)?":
|
||||
$ref: "#/definitions/dai"
|
||||
"^simple-audio-card,codec(@[0-9a-f]+)?":
|
||||
$ref: "#/definitions/dai"
|
||||
|
||||
# common properties
|
||||
"^simple-audio-card,frame-master$":
|
||||
$ref: "#/definitions/frame-master"
|
||||
"^simple-audio-card,bitclock-master$":
|
||||
$ref: "#/definitions/bitclock-master"
|
||||
"^simple-audio-card,frame-inversion$":
|
||||
$ref: "#/definitions/frame-inversion"
|
||||
"^simple-audio-card,bitclock-inversion$":
|
||||
$ref: "#/definitions/bitclock-inversion"
|
||||
"^simple-audio-card,format$":
|
||||
$ref: "#/definitions/format"
|
||||
"^simple-audio-card,mclk-fs$":
|
||||
$ref: "#/definitions/mclk-fs"
|
||||
"^simple-audio-card,aux-devs$":
|
||||
$ref: "#/definitions/aux-devs"
|
||||
"^simple-audio-card,convert-rate$":
|
||||
$ref: "#/definitions/convert-rate"
|
||||
"^simple-audio-card,convert-channels$":
|
||||
$ref: "#/definitions/convert-channels"
|
||||
"^simple-audio-card,prefix$":
|
||||
$ref: "#/definitions/prefix"
|
||||
"^simple-audio-card,pin-switches$":
|
||||
$ref: "#/definitions/pin-switches"
|
||||
"^simple-audio-card,hp-det-gpio$":
|
||||
maxItems: 1
|
||||
"^simple-audio-card,mic-det-gpio$":
|
||||
maxItems: 1
|
||||
|
||||
"^simple-audio-card,dai-link(@[0-9a-f]+)?$":
|
||||
description: |
|
||||
Container for dai-link level properties and the CPU and CODEC sub-nodes.
|
||||
This container may be omitted when the card has only one DAI link.
|
||||
type: object
|
||||
properties:
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
# common properties
|
||||
frame-master:
|
||||
$ref: "#/definitions/frame-master"
|
||||
bitclock-master:
|
||||
$ref: "#/definitions/bitclock-master"
|
||||
frame-inversion:
|
||||
$ref: "#/definitions/frame-inversion"
|
||||
bitclock-inversion:
|
||||
$ref: "#/definitions/bitclock-inversion"
|
||||
format:
|
||||
$ref: "#/definitions/format"
|
||||
mclk-fs:
|
||||
$ref: "#/definitions/mclk-fs"
|
||||
aux-devs:
|
||||
$ref: "#/definitions/aux-devs"
|
||||
convert-rate:
|
||||
$ref: "#/definitions/convert-rate"
|
||||
convert-channels:
|
||||
$ref: "#/definitions/convert-channels"
|
||||
prefix:
|
||||
$ref: "#/definitions/prefix"
|
||||
pin-switches:
|
||||
$ref: "#/definitions/pin-switches"
|
||||
hp-det-gpio:
|
||||
maxItems: 1
|
||||
mic-det-gpio:
|
||||
maxItems: 1
|
||||
|
||||
patternProperties:
|
||||
"^cpu(@[0-9a-f]+)?":
|
||||
$ref: "#/definitions/dai"
|
||||
"^codec(@[0-9a-f]+)?":
|
||||
$ref: "#/definitions/dai"
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
#--------------------
|
||||
# single DAI link
|
||||
#--------------------
|
||||
- |
|
||||
sound {
|
||||
compatible = "simple-audio-card";
|
||||
simple-audio-card,name = "VF610-Tower-Sound-Card";
|
||||
simple-audio-card,format = "left_j";
|
||||
simple-audio-card,bitclock-master = <&dailink0_master>;
|
||||
simple-audio-card,frame-master = <&dailink0_master>;
|
||||
simple-audio-card,widgets =
|
||||
"Microphone", "Microphone Jack",
|
||||
"Headphone", "Headphone Jack",
|
||||
"Speaker", "External Speaker";
|
||||
simple-audio-card,routing =
|
||||
"MIC_IN", "Microphone Jack",
|
||||
"Headphone Jack", "HP_OUT",
|
||||
"External Speaker", "LINE_OUT";
|
||||
|
||||
simple-audio-card,cpu {
|
||||
sound-dai = <&sh_fsi2 0>;
|
||||
};
|
||||
|
||||
dailink0_master: simple-audio-card,codec {
|
||||
sound-dai = <&ak4648>;
|
||||
clocks = <&osc>;
|
||||
};
|
||||
};
|
||||
|
||||
#--------------------
|
||||
# Multi DAI links
|
||||
#--------------------
|
||||
- |
|
||||
sound {
|
||||
compatible = "simple-audio-card";
|
||||
simple-audio-card,name = "Cubox Audio";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
simple-audio-card,dai-link@0 { /* I2S - HDMI */
|
||||
reg = <0>;
|
||||
format = "i2s";
|
||||
cpu {
|
||||
sound-dai = <&audio0>;
|
||||
};
|
||||
codec {
|
||||
sound-dai = <&tda998x0>;
|
||||
};
|
||||
};
|
||||
|
||||
simple-audio-card,dai-link@1 { /* S/PDIF - HDMI */
|
||||
reg = <1>;
|
||||
cpu {
|
||||
sound-dai = <&audio1>;
|
||||
};
|
||||
codec {
|
||||
sound-dai = <&tda998x1>;
|
||||
};
|
||||
};
|
||||
|
||||
simple-audio-card,dai-link@2 { /* S/PDIF - S/PDIF */
|
||||
reg = <2>;
|
||||
cpu {
|
||||
sound-dai = <&audio2>;
|
||||
};
|
||||
codec {
|
||||
sound-dai = <&spdif_codec>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
#--------------------
|
||||
# route audio from IMX6 SSI2 through TLV320DAC3100 codec
|
||||
# through TPA6130A2 amplifier to headphones:
|
||||
#--------------------
|
||||
- |
|
||||
sound {
|
||||
compatible = "simple-audio-card";
|
||||
|
||||
simple-audio-card,widgets =
|
||||
"Headphone", "Headphone Jack";
|
||||
simple-audio-card,routing =
|
||||
"Headphone Jack", "HPLEFT",
|
||||
"Headphone Jack", "HPRIGHT",
|
||||
"LEFTIN", "HPL",
|
||||
"RIGHTIN", "HPR";
|
||||
simple-audio-card,aux-devs = <&>;
|
||||
simple-audio-card,cpu {
|
||||
sound-dai = <&ssi2>;
|
||||
};
|
||||
simple-audio-card,codec {
|
||||
sound-dai = <&codec>;
|
||||
clocks = <&clocks>;
|
||||
};
|
||||
};
|
||||
|
||||
#--------------------
|
||||
# Sampling Rate Conversion
|
||||
#--------------------
|
||||
- |
|
||||
sound {
|
||||
compatible = "simple-audio-card";
|
||||
|
||||
simple-audio-card,name = "rsnd-ak4643";
|
||||
simple-audio-card,format = "left_j";
|
||||
simple-audio-card,bitclock-master = <&sndcodec>;
|
||||
simple-audio-card,frame-master = <&sndcodec>;
|
||||
|
||||
simple-audio-card,convert-rate = <48000>;
|
||||
|
||||
simple-audio-card,prefix = "ak4642";
|
||||
simple-audio-card,routing = "ak4642 Playback", "DAI0 Playback",
|
||||
"DAI0 Capture", "ak4642 Capture";
|
||||
|
||||
sndcpu: simple-audio-card,cpu {
|
||||
sound-dai = <&rcar_sound>;
|
||||
};
|
||||
|
||||
sndcodec: simple-audio-card,codec {
|
||||
sound-dai = <&ak4643>;
|
||||
system-clock-frequency = <11289600>;
|
||||
};
|
||||
};
|
||||
|
||||
#--------------------
|
||||
# 2 CPU 1 Codec (Mixing)
|
||||
#--------------------
|
||||
- |
|
||||
sound {
|
||||
compatible = "simple-audio-card";
|
||||
|
||||
simple-audio-card,name = "rsnd-ak4643";
|
||||
simple-audio-card,format = "left_j";
|
||||
simple-audio-card,bitclock-master = <&dpcmcpu>;
|
||||
simple-audio-card,frame-master = <&dpcmcpu>;
|
||||
|
||||
simple-audio-card,convert-rate = <48000>;
|
||||
simple-audio-card,convert-channels = <2>;
|
||||
|
||||
simple-audio-card,routing = "ak4642 Playback", "DAI0 Playback",
|
||||
"ak4642 Playback", "DAI1 Playback";
|
||||
|
||||
dpcmcpu: simple-audio-card,cpu@0 {
|
||||
sound-dai = <&rcar_sound 0>;
|
||||
};
|
||||
|
||||
simple-audio-card,cpu@1 {
|
||||
sound-dai = <&rcar_sound 1>;
|
||||
};
|
||||
|
||||
simple-audio-card,codec {
|
||||
prefix = "ak4642";
|
||||
sound-dai = <&ak4643>;
|
||||
clocks = <&audio_clock>;
|
||||
};
|
||||
};
|
||||
|
||||
#--------------------
|
||||
# Multi DAI links with DPCM:
|
||||
#
|
||||
# CPU0 ------ ak4613
|
||||
# CPU1 ------ PCM3168A-p /* DPCM 1ch/2ch */
|
||||
# CPU2 --/ /* DPCM 3ch/4ch */
|
||||
# CPU3 --/ /* DPCM 5ch/6ch */
|
||||
# CPU4 --/ /* DPCM 7ch/8ch */
|
||||
# CPU5 ------ PCM3168A-c
|
||||
#--------------------
|
||||
- |
|
||||
sound {
|
||||
compatible = "simple-audio-card";
|
||||
|
||||
simple-audio-card,routing =
|
||||
"pcm3168a Playback", "DAI1 Playback",
|
||||
"pcm3168a Playback", "DAI2 Playback",
|
||||
"pcm3168a Playback", "DAI3 Playback",
|
||||
"pcm3168a Playback", "DAI4 Playback";
|
||||
|
||||
simple-audio-card,dai-link@0 {
|
||||
format = "left_j";
|
||||
bitclock-master = <&sndcpu0>;
|
||||
frame-master = <&sndcpu0>;
|
||||
|
||||
sndcpu0: cpu {
|
||||
sound-dai = <&rcar_sound 0>;
|
||||
};
|
||||
codec {
|
||||
sound-dai = <&ak4613>;
|
||||
};
|
||||
};
|
||||
|
||||
simple-audio-card,dai-link@1 {
|
||||
format = "i2s";
|
||||
bitclock-master = <&sndcpu1>;
|
||||
frame-master = <&sndcpu1>;
|
||||
|
||||
convert-channels = <8>; /* TDM Split */
|
||||
|
||||
sndcpu1: cpu@0 {
|
||||
sound-dai = <&rcar_sound 1>;
|
||||
};
|
||||
cpu@1 {
|
||||
sound-dai = <&rcar_sound 2>;
|
||||
};
|
||||
cpu@2 {
|
||||
sound-dai = <&rcar_sound 3>;
|
||||
};
|
||||
cpu@3 {
|
||||
sound-dai = <&rcar_sound 4>;
|
||||
};
|
||||
codec {
|
||||
mclk-fs = <512>;
|
||||
prefix = "pcm3168a";
|
||||
dai-tdm-slot-num = <8>;
|
||||
sound-dai = <&pcm3168a 0>;
|
||||
};
|
||||
};
|
||||
|
||||
simple-audio-card,dai-link@2 {
|
||||
format = "i2s";
|
||||
bitclock-master = <&sndcpu2>;
|
||||
frame-master = <&sndcpu2>;
|
||||
|
||||
sndcpu2: cpu {
|
||||
sound-dai = <&rcar_sound 5>;
|
||||
};
|
||||
codec {
|
||||
mclk-fs = <512>;
|
||||
prefix = "pcm3168a";
|
||||
sound-dai = <&pcm3168a 1>;
|
||||
};
|
||||
};
|
||||
};
|
|
@ -63,6 +63,55 @@ properties:
|
|||
- $ref: /schemas/types.yaml#/definitions/uint32
|
||||
- enum: [0, 1, 2]
|
||||
|
||||
ti,pdm-edge-select:
|
||||
description: |
|
||||
Defines the PDMCLK sampling edge configuration for the PDM inputs. This
|
||||
array is defined as <PDMIN1 PDMIN2 PDMIN3 PDMIN4>.
|
||||
|
||||
0 - (default) Odd channel is latched on the negative edge and even
|
||||
channel is latched on the the positive edge.
|
||||
1 - Odd channel is latched on the positive edge and even channel is
|
||||
latched on the the negative edge.
|
||||
|
||||
PDMIN1 - PDMCLK latching edge used for channel 1 and 2 data
|
||||
PDMIN2 - PDMCLK latching edge used for channel 3 and 4 data
|
||||
PDMIN3 - PDMCLK latching edge used for channel 5 and 6 data
|
||||
PDMIN4 - PDMCLK latching edge used for channel 7 and 8 data
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
- minItems: 1
|
||||
maxItems: 4
|
||||
items:
|
||||
maximum: 1
|
||||
default: [0, 0, 0, 0]
|
||||
|
||||
ti,gpi-config:
|
||||
description: |
|
||||
Defines the configuration for the general purpose input pins (GPI).
|
||||
The array is defined as <GPI1 GPI2 GPI3 GPI4>.
|
||||
|
||||
0 - (default) disabled
|
||||
1 - GPIX is configured as a general-purpose input (GPI)
|
||||
2 - GPIX is configured as a master clock input (MCLK)
|
||||
3 - GPIX is configured as an ASI input for daisy-chain (SDIN)
|
||||
4 - GPIX is configured as a PDM data input for channel 1 and channel
|
||||
(PDMDIN1)
|
||||
5 - GPIX is configured as a PDM data input for channel 3 and channel
|
||||
(PDMDIN2)
|
||||
6 - GPIX is configured as a PDM data input for channel 5 and channel
|
||||
(PDMDIN3)
|
||||
7 - GPIX is configured as a PDM data input for channel 7 and channel
|
||||
(PDMDIN4)
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
- minItems: 1
|
||||
maxItems: 4
|
||||
items:
|
||||
maximum: 7
|
||||
default: [0, 0, 0, 0]
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
@ -77,6 +126,8 @@ examples:
|
|||
compatible = "ti,tlv320adc5140";
|
||||
reg = <0x4c>;
|
||||
ti,mic-bias-source = <6>;
|
||||
ti,pdm-edge-select = <0 1 0 1>;
|
||||
ti,gpi-config = <4 5 6 7>;
|
||||
reset-gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -14,9 +14,15 @@ Required properties:
|
|||
- #gpio-cells : Must be 2. The first cell is the pin number and the
|
||||
second cell is used to specify optional parameters (currently unused).
|
||||
|
||||
- AVDD2-supply, DBVDD1-supply, DBVDD2-supply, DBVDD3-supply, CPVDD-supply,
|
||||
SPKVDD1-supply, SPKVDD2-supply : power supplies for the device, as covered
|
||||
in Documentation/devicetree/bindings/regulator/regulator.txt
|
||||
- power supplies for the device, as covered in
|
||||
Documentation/devicetree/bindings/regulator/regulator.txt, depending
|
||||
on compatible:
|
||||
- for wlf,wm1811 and wlf,wm8958:
|
||||
AVDD1-supply, AVDD2-supply, DBVDD1-supply, DBVDD2-supply, DBVDD3-supply,
|
||||
DCVDD-supply, CPVDD-supply, SPKVDD1-supply, SPKVDD2-supply
|
||||
- for wlf,wm8994:
|
||||
AVDD1-supply, AVDD2-supply, DBVDD-supply, DCVDD-supply, CPVDD-supply,
|
||||
SPKVDD1-supply, SPKVDD2-supply
|
||||
|
||||
Optional properties:
|
||||
|
||||
|
@ -73,11 +79,11 @@ wm8994: codec@1a {
|
|||
|
||||
lineout1-se;
|
||||
|
||||
AVDD1-supply = <®ulator>;
|
||||
AVDD2-supply = <®ulator>;
|
||||
CPVDD-supply = <®ulator>;
|
||||
DBVDD1-supply = <®ulator>;
|
||||
DBVDD2-supply = <®ulator>;
|
||||
DBVDD3-supply = <®ulator>;
|
||||
DBVDD-supply = <®ulator>;
|
||||
DCVDD-supply = <®ulator>;
|
||||
SPKVDD1-supply = <®ulator>;
|
||||
SPKVDD2-supply = <®ulator>;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/sound/zl38060.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: ZL38060 Connected Home Audio Processor from Microsemi.
|
||||
|
||||
description: |
|
||||
The ZL38060 is a "Connected Home Audio Processor" from Microsemi,
|
||||
which consists of a Digital Signal Processor (DSP), several Digital
|
||||
Audio Interfaces (DAIs), analog outputs, and a block of 14 GPIOs.
|
||||
|
||||
maintainers:
|
||||
- Jaroslav Kysela <perex@perex.cz>
|
||||
- Takashi Iwai <tiwai@suse.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: mscc,zl38060
|
||||
|
||||
reg:
|
||||
description:
|
||||
SPI device address.
|
||||
maxItems: 1
|
||||
|
||||
spi-max-frequency:
|
||||
maximum: 24000000
|
||||
|
||||
reset-gpios:
|
||||
description:
|
||||
A GPIO line handling reset of the chip. As the line is active low,
|
||||
it should be marked GPIO_ACTIVE_LOW (see ../gpio/gpio.txt)
|
||||
maxItems: 1
|
||||
|
||||
'#gpio-cells':
|
||||
const: 2
|
||||
|
||||
gpio-controller: true
|
||||
|
||||
'#sound-dai-cells':
|
||||
const: 0
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- '#gpio-cells'
|
||||
- gpio-controller
|
||||
- '#sound-dai-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
spi0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
codec: zl38060@0 {
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
#sound-dai-cells = <0>;
|
||||
compatible = "mscc,zl38060";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <12000000>;
|
||||
reset-gpios = <&gpio1 0 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
};
|
|
@ -669,11 +669,11 @@ static int sdw_stream_setup(struct snd_pcm_substream *substream,
|
|||
|
||||
/* Set stream pointer on all CODEC DAIs */
|
||||
for (i = 0; i < rtd->num_codecs; i++) {
|
||||
ret = snd_soc_dai_set_sdw_stream(rtd->codec_dais[i], sdw_stream,
|
||||
ret = snd_soc_dai_set_sdw_stream(asoc_rtd_to_codec(rtd, i), sdw_stream,
|
||||
substream->stream);
|
||||
if (ret < 0) {
|
||||
dev_err(dai->dev, "failed to set stream pointer on codec dai %s",
|
||||
rtd->codec_dais[i]->name);
|
||||
asoc_rtd_to_codec(rtd, i)->name);
|
||||
goto release_stream;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,7 +75,7 @@ struct snd_kcontrol {
|
|||
unsigned long private_value;
|
||||
void *private_data;
|
||||
void (*private_free)(struct snd_kcontrol *kcontrol);
|
||||
struct snd_kcontrol_volatile vd[0]; /* volatile data */
|
||||
struct snd_kcontrol_volatile vd[]; /* volatile data */
|
||||
};
|
||||
|
||||
#define snd_kcontrol(n) list_entry(n, struct snd_kcontrol, list)
|
||||
|
|
|
@ -288,6 +288,10 @@ struct hda_codec {
|
|||
#define dev_to_hda_codec(_dev) container_of(_dev, struct hda_codec, core.dev)
|
||||
#define hda_codec_dev(_dev) (&(_dev)->core.dev)
|
||||
|
||||
#define hdac_to_hda_priv(_hdac) \
|
||||
container_of(_hdac, struct hdac_hda_priv, codec.core)
|
||||
#define hdac_to_hda_codec(_hdac) container_of(_hdac, struct hda_codec, core)
|
||||
|
||||
#define list_for_each_codec(c, bus) \
|
||||
list_for_each_entry(c, &(bus)->core.codec_list, core.list)
|
||||
#define list_for_each_codec_safe(c, n, bus) \
|
||||
|
@ -362,13 +366,6 @@ struct hda_verb {
|
|||
void snd_hda_sequence_write(struct hda_codec *codec,
|
||||
const struct hda_verb *seq);
|
||||
|
||||
/* unsolicited event */
|
||||
static inline void
|
||||
snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex)
|
||||
{
|
||||
snd_hdac_bus_queue_event(&bus->core, res, res_ex);
|
||||
}
|
||||
|
||||
/* cached write */
|
||||
static inline int
|
||||
snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
|
||||
|
|
|
@ -207,8 +207,8 @@ static inline int snd_hdac_power_down_pm(struct hdac_device *codec) { return 0;
|
|||
static inline int snd_hdac_keep_power_up(struct hdac_device *codec) { return 0; }
|
||||
static inline void snd_hdac_enter_pm(struct hdac_device *codec) {}
|
||||
static inline void snd_hdac_leave_pm(struct hdac_device *codec) {}
|
||||
static inline bool snd_hdac_is_in_pm(struct hdac_device *codec) { return 0; }
|
||||
static inline bool snd_hdac_is_power_on(struct hdac_device *codec) { return 1; }
|
||||
static inline bool snd_hdac_is_in_pm(struct hdac_device *codec) { return false; }
|
||||
static inline bool snd_hdac_is_power_on(struct hdac_device *codec) { return true; }
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -364,16 +364,16 @@ struct hdac_bus {
|
|||
/* link management */
|
||||
struct list_head hlink_list;
|
||||
bool cmd_dma_state;
|
||||
|
||||
/* factor used to derive STRIPE control value */
|
||||
unsigned int sdo_limit;
|
||||
};
|
||||
|
||||
int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
|
||||
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);
|
||||
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);
|
||||
|
||||
static inline void snd_hdac_codec_link_up(struct hdac_device *codec)
|
||||
{
|
||||
|
|
|
@ -50,7 +50,7 @@ enum nhlt_device_type {
|
|||
|
||||
struct nhlt_specific_cfg {
|
||||
u32 size;
|
||||
u8 caps[0];
|
||||
u8 caps[];
|
||||
} __packed;
|
||||
|
||||
struct nhlt_fmt_cfg {
|
||||
|
@ -60,7 +60,7 @@ struct nhlt_fmt_cfg {
|
|||
|
||||
struct nhlt_fmt {
|
||||
u8 fmt_count;
|
||||
struct nhlt_fmt_cfg fmt_config[0];
|
||||
struct nhlt_fmt_cfg fmt_config[];
|
||||
} __packed;
|
||||
|
||||
struct nhlt_endpoint {
|
||||
|
@ -80,7 +80,7 @@ struct nhlt_endpoint {
|
|||
struct nhlt_acpi_table {
|
||||
struct acpi_table_header header;
|
||||
u8 endpoint_count;
|
||||
struct nhlt_endpoint desc[0];
|
||||
struct nhlt_endpoint desc[];
|
||||
} __packed;
|
||||
|
||||
struct nhlt_resource_desc {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0-only
|
||||
*
|
||||
* Copyright (C) 2013-15, Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* soc-card.h
|
||||
*
|
||||
* Copyright (C) 2019 Renesas Electronics Corp.
|
||||
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
|
||||
*/
|
||||
#ifndef __SOC_CARD_H
|
||||
#define __SOC_CARD_H
|
||||
|
||||
enum snd_soc_card_subclass {
|
||||
SND_SOC_CARD_CLASS_INIT = 0,
|
||||
SND_SOC_CARD_CLASS_RUNTIME = 1,
|
||||
};
|
||||
|
||||
struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card,
|
||||
const char *name);
|
||||
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, unsigned int num_pins);
|
||||
|
||||
int snd_soc_card_suspend_pre(struct snd_soc_card *card);
|
||||
int snd_soc_card_suspend_post(struct snd_soc_card *card);
|
||||
int snd_soc_card_resume_pre(struct snd_soc_card *card);
|
||||
int snd_soc_card_resume_post(struct snd_soc_card *card);
|
||||
|
||||
int snd_soc_card_probe(struct snd_soc_card *card);
|
||||
int snd_soc_card_late_probe(struct snd_soc_card *card);
|
||||
int snd_soc_card_remove(struct snd_soc_card *card);
|
||||
|
||||
int snd_soc_card_set_bias_level(struct snd_soc_card *card,
|
||||
struct snd_soc_dapm_context *dapm,
|
||||
enum snd_soc_bias_level level);
|
||||
int snd_soc_card_set_bias_level_post(struct snd_soc_card *card,
|
||||
struct snd_soc_dapm_context *dapm,
|
||||
enum snd_soc_bias_level level);
|
||||
|
||||
int snd_soc_card_add_dai_link(struct snd_soc_card *card,
|
||||
struct snd_soc_dai_link *dai_link);
|
||||
void snd_soc_card_remove_dai_link(struct snd_soc_card *card,
|
||||
struct snd_soc_dai_link *dai_link);
|
||||
|
||||
/* device driver data */
|
||||
static inline void snd_soc_card_set_drvdata(struct snd_soc_card *card,
|
||||
void *data)
|
||||
{
|
||||
card->drvdata = data;
|
||||
}
|
||||
|
||||
static inline void *snd_soc_card_get_drvdata(struct snd_soc_card *card)
|
||||
{
|
||||
return card->drvdata;
|
||||
}
|
||||
|
||||
static inline
|
||||
struct snd_soc_dai *snd_soc_card_get_codec_dai(struct snd_soc_card *card,
|
||||
const char *dai_name)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
|
||||
for_each_card_rtds(card, rtd) {
|
||||
if (!strcmp(asoc_rtd_to_codec(rtd, 0)->name, dai_name))
|
||||
return asoc_rtd_to_codec(rtd, 0);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* __SOC_CARD_H */
|
|
@ -25,6 +25,44 @@
|
|||
order++)
|
||||
|
||||
/* component interface */
|
||||
struct snd_compress_ops {
|
||||
int (*open)(struct snd_soc_component *component,
|
||||
struct snd_compr_stream *stream);
|
||||
int (*free)(struct snd_soc_component *component,
|
||||
struct snd_compr_stream *stream);
|
||||
int (*set_params)(struct snd_soc_component *component,
|
||||
struct snd_compr_stream *stream,
|
||||
struct snd_compr_params *params);
|
||||
int (*get_params)(struct snd_soc_component *component,
|
||||
struct snd_compr_stream *stream,
|
||||
struct snd_codec *params);
|
||||
int (*set_metadata)(struct snd_soc_component *component,
|
||||
struct snd_compr_stream *stream,
|
||||
struct snd_compr_metadata *metadata);
|
||||
int (*get_metadata)(struct snd_soc_component *component,
|
||||
struct snd_compr_stream *stream,
|
||||
struct snd_compr_metadata *metadata);
|
||||
int (*trigger)(struct snd_soc_component *component,
|
||||
struct snd_compr_stream *stream, int cmd);
|
||||
int (*pointer)(struct snd_soc_component *component,
|
||||
struct snd_compr_stream *stream,
|
||||
struct snd_compr_tstamp *tstamp);
|
||||
int (*copy)(struct snd_soc_component *component,
|
||||
struct snd_compr_stream *stream, char __user *buf,
|
||||
size_t count);
|
||||
int (*mmap)(struct snd_soc_component *component,
|
||||
struct snd_compr_stream *stream,
|
||||
struct vm_area_struct *vma);
|
||||
int (*ack)(struct snd_soc_component *component,
|
||||
struct snd_compr_stream *stream, size_t bytes);
|
||||
int (*get_caps)(struct snd_soc_component *component,
|
||||
struct snd_compr_stream *stream,
|
||||
struct snd_compr_caps *caps);
|
||||
int (*get_codec_caps)(struct snd_soc_component *component,
|
||||
struct snd_compr_stream *stream,
|
||||
struct snd_compr_codec_caps *codec);
|
||||
};
|
||||
|
||||
struct snd_soc_component_driver {
|
||||
const char *name;
|
||||
|
||||
|
@ -108,7 +146,7 @@ struct snd_soc_component_driver {
|
|||
struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *vma);
|
||||
|
||||
const struct snd_compr_ops *compr_ops;
|
||||
const struct snd_compress_ops *compress_ops;
|
||||
|
||||
/* probe ordering - for components with runtime dependencies */
|
||||
int probe_order;
|
||||
|
@ -351,10 +389,10 @@ 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)
|
||||
static inline unsigned int
|
||||
snd_soc_component_active(struct snd_soc_component *component)
|
||||
{
|
||||
return component->active != 0;
|
||||
return component->active;
|
||||
}
|
||||
|
||||
/* component pin */
|
||||
|
|
|
@ -154,21 +154,59 @@ 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);
|
||||
void snd_soc_dai_action(struct snd_soc_dai *dai,
|
||||
int stream, int action);
|
||||
static inline void snd_soc_dai_activate(struct snd_soc_dai *dai,
|
||||
int stream)
|
||||
{
|
||||
snd_soc_dai_action(dai, stream, 1);
|
||||
}
|
||||
static inline void snd_soc_dai_deactivate(struct snd_soc_dai *dai,
|
||||
int stream)
|
||||
{
|
||||
snd_soc_dai_action(dai, stream, -1);
|
||||
}
|
||||
int snd_soc_dai_active(struct snd_soc_dai *dai);
|
||||
|
||||
int snd_soc_pcm_dai_probe(struct snd_soc_pcm_runtime *rtd, int order);
|
||||
int snd_soc_pcm_dai_remove(struct snd_soc_pcm_runtime *rtd, int order);
|
||||
int snd_soc_pcm_dai_new(struct snd_soc_pcm_runtime *rtd);
|
||||
int snd_soc_pcm_dai_prepare(struct snd_pcm_substream *substream);
|
||||
int snd_soc_pcm_dai_trigger(struct snd_pcm_substream *substream, int cmd);
|
||||
int snd_soc_pcm_dai_bespoke_trigger(struct snd_pcm_substream *substream,
|
||||
int cmd);
|
||||
|
||||
int snd_soc_dai_compr_startup(struct snd_soc_dai *dai,
|
||||
struct snd_compr_stream *cstream);
|
||||
void snd_soc_dai_compr_shutdown(struct snd_soc_dai *dai,
|
||||
struct snd_compr_stream *cstream);
|
||||
int snd_soc_dai_compr_trigger(struct snd_soc_dai *dai,
|
||||
struct snd_compr_stream *cstream, int cmd);
|
||||
int snd_soc_dai_compr_set_params(struct snd_soc_dai *dai,
|
||||
struct snd_compr_stream *cstream,
|
||||
struct snd_compr_params *params);
|
||||
int snd_soc_dai_compr_get_params(struct snd_soc_dai *dai,
|
||||
struct snd_compr_stream *cstream,
|
||||
struct snd_codec *params);
|
||||
int snd_soc_dai_compr_ack(struct snd_soc_dai *dai,
|
||||
struct snd_compr_stream *cstream,
|
||||
size_t bytes);
|
||||
int snd_soc_dai_compr_pointer(struct snd_soc_dai *dai,
|
||||
struct snd_compr_stream *cstream,
|
||||
struct snd_compr_tstamp *tstamp);
|
||||
int snd_soc_dai_compr_set_metadata(struct snd_soc_dai *dai,
|
||||
struct snd_compr_stream *cstream,
|
||||
struct snd_compr_metadata *metadata);
|
||||
int snd_soc_dai_compr_get_metadata(struct snd_soc_dai *dai,
|
||||
struct snd_compr_stream *cstream,
|
||||
struct snd_compr_metadata *metadata);
|
||||
|
||||
struct snd_soc_dai_ops {
|
||||
/*
|
||||
|
@ -326,8 +364,6 @@ struct snd_soc_dai {
|
|||
/* DAI runtime info */
|
||||
unsigned int stream_active[SNDRV_PCM_STREAM_LAST + 1]; /* usage count */
|
||||
|
||||
unsigned int active;
|
||||
|
||||
struct snd_soc_dapm_widget *playback_widget;
|
||||
struct snd_soc_dapm_widget *capture_widget;
|
||||
|
||||
|
@ -443,4 +479,10 @@ static inline void *snd_soc_dai_get_sdw_stream(struct snd_soc_dai *dai,
|
|||
return ERR_PTR(-ENOTSUPP);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
snd_soc_dai_stream_active(struct snd_soc_dai *dai, int stream)
|
||||
{
|
||||
return dai->stream_active[stream];
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -689,7 +689,7 @@ struct snd_soc_dapm_context {
|
|||
/* A list of widgets associated with an object, typically a snd_kcontrol */
|
||||
struct snd_soc_dapm_widget_list {
|
||||
int num_widgets;
|
||||
struct snd_soc_dapm_widget *widgets[0];
|
||||
struct snd_soc_dapm_widget *widgets[];
|
||||
};
|
||||
|
||||
#define for_each_dapm_widgets(list, i, widget) \
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* soc-link.h
|
||||
*
|
||||
* Copyright (C) 2019 Renesas Electronics Corp.
|
||||
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
|
||||
*/
|
||||
#ifndef __SOC_LINK_H
|
||||
#define __SOC_LINK_H
|
||||
|
||||
int snd_soc_link_init(struct snd_soc_pcm_runtime *rtd);
|
||||
int snd_soc_link_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||
struct snd_pcm_hw_params *params);
|
||||
|
||||
int snd_soc_link_startup(struct snd_pcm_substream *substream);
|
||||
void snd_soc_link_shutdown(struct snd_pcm_substream *substream);
|
||||
int snd_soc_link_prepare(struct snd_pcm_substream *substream);
|
||||
int snd_soc_link_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params);
|
||||
void snd_soc_link_hw_free(struct snd_pcm_substream *substream);
|
||||
int snd_soc_link_trigger(struct snd_pcm_substream *substream, int cmd);
|
||||
|
||||
int snd_soc_link_compr_startup(struct snd_compr_stream *cstream);
|
||||
void snd_soc_link_compr_shutdown(struct snd_compr_stream *cstream);
|
||||
int snd_soc_link_compr_set_params(struct snd_compr_stream *cstream);
|
||||
|
||||
#endif /* __SOC_LINK_H */
|
|
@ -414,11 +414,6 @@ enum snd_soc_pcm_subclass {
|
|||
SND_SOC_PCM_CLASS_BE = 1,
|
||||
};
|
||||
|
||||
enum snd_soc_card_subclass {
|
||||
SND_SOC_CARD_CLASS_INIT = 0,
|
||||
SND_SOC_CARD_CLASS_RUNTIME = 1,
|
||||
};
|
||||
|
||||
int snd_soc_register_card(struct snd_soc_card *card);
|
||||
int snd_soc_unregister_card(struct snd_soc_card *card);
|
||||
int devm_snd_soc_register_card(struct device *dev, struct snd_soc_card *card);
|
||||
|
@ -468,8 +463,19 @@ struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card,
|
|||
struct snd_soc_dai_link *dai_link);
|
||||
|
||||
bool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd);
|
||||
void snd_soc_runtime_activate(struct snd_soc_pcm_runtime *rtd, int stream);
|
||||
void snd_soc_runtime_deactivate(struct snd_soc_pcm_runtime *rtd, int stream);
|
||||
|
||||
void snd_soc_runtime_action(struct snd_soc_pcm_runtime *rtd,
|
||||
int stream, int action);
|
||||
static inline void snd_soc_runtime_activate(struct snd_soc_pcm_runtime *rtd,
|
||||
int stream)
|
||||
{
|
||||
snd_soc_runtime_action(rtd, stream, 1);
|
||||
}
|
||||
static inline void snd_soc_runtime_deactivate(struct snd_soc_pcm_runtime *rtd,
|
||||
int stream)
|
||||
{
|
||||
snd_soc_runtime_action(rtd, stream, -1);
|
||||
}
|
||||
|
||||
int snd_soc_runtime_calc_hw(struct snd_soc_pcm_runtime *rtd,
|
||||
struct snd_pcm_hardware *hw, int stream);
|
||||
|
@ -498,10 +504,6 @@ int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
|
|||
const struct snd_pcm_hardware *hw);
|
||||
|
||||
/* 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,
|
||||
unsigned int num_pins);
|
||||
|
||||
void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask);
|
||||
int snd_soc_jack_add_pins(struct snd_soc_jack *jack, int count,
|
||||
struct snd_soc_jack_pin *pins);
|
||||
|
@ -571,8 +573,6 @@ static inline int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops)
|
|||
struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template,
|
||||
void *data, const char *long_name,
|
||||
const char *prefix);
|
||||
struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card,
|
||||
const char *name);
|
||||
int snd_soc_add_component_controls(struct snd_soc_component *component,
|
||||
const struct snd_kcontrol_new *controls, unsigned int num_controls);
|
||||
int snd_soc_add_card_controls(struct snd_soc_card *soc_card,
|
||||
|
@ -790,9 +790,6 @@ struct snd_soc_dai_link {
|
|||
const struct snd_soc_pcm_stream *params;
|
||||
unsigned int num_params;
|
||||
|
||||
struct snd_soc_dapm_widget *playback_widget;
|
||||
struct snd_soc_dapm_widget *capture_widget;
|
||||
|
||||
unsigned int dai_fmt; /* format to set on init */
|
||||
|
||||
enum snd_soc_dpcm_trigger trigger[2]; /* trigger type for DPCM */
|
||||
|
@ -809,7 +806,7 @@ struct snd_soc_dai_link {
|
|||
const struct snd_soc_compr_ops *compr_ops;
|
||||
|
||||
/* Mark this pcm with non atomic ops */
|
||||
bool nonatomic;
|
||||
unsigned int nonatomic:1;
|
||||
|
||||
/* For unidirectional dai links */
|
||||
unsigned int playback_only:1;
|
||||
|
@ -1005,9 +1002,6 @@ struct snd_soc_card {
|
|||
|
||||
spinlock_t dpcm_lock;
|
||||
|
||||
bool instantiated;
|
||||
bool topology_shortname_created;
|
||||
|
||||
int (*probe)(struct snd_soc_card *card);
|
||||
int (*late_probe)(struct snd_soc_card *card);
|
||||
int (*remove)(struct snd_soc_card *card);
|
||||
|
@ -1068,8 +1062,6 @@ struct snd_soc_card {
|
|||
int num_of_dapm_widgets;
|
||||
const struct snd_soc_dapm_route *of_dapm_routes;
|
||||
int num_of_dapm_routes;
|
||||
bool fully_routed;
|
||||
bool disable_route_checks;
|
||||
|
||||
/* lists of probed devices belonging to this card */
|
||||
struct list_head component_dev_list;
|
||||
|
@ -1096,6 +1088,13 @@ struct snd_soc_card {
|
|||
#endif
|
||||
u32 pop_time;
|
||||
|
||||
/* bit field */
|
||||
unsigned int instantiated:1;
|
||||
unsigned int topology_shortname_created:1;
|
||||
unsigned int fully_routed:1;
|
||||
unsigned int disable_route_checks:1;
|
||||
unsigned int probed:1;
|
||||
|
||||
void *drvdata;
|
||||
};
|
||||
#define for_each_card_prelinks(card, i, link) \
|
||||
|
@ -1146,16 +1145,21 @@ struct snd_soc_pcm_runtime {
|
|||
/* runtime devices */
|
||||
struct snd_pcm *pcm;
|
||||
struct snd_compr *compr;
|
||||
struct snd_soc_dai *codec_dai;
|
||||
struct snd_soc_dai *cpu_dai;
|
||||
|
||||
/*
|
||||
* dais = cpu_dai + codec_dai
|
||||
* see
|
||||
* soc_new_pcm_runtime()
|
||||
* asoc_rtd_to_cpu()
|
||||
* asoc_rtd_to_codec()
|
||||
*/
|
||||
struct snd_soc_dai **dais;
|
||||
|
||||
struct snd_soc_dai **codec_dais;
|
||||
unsigned int num_codecs;
|
||||
|
||||
struct snd_soc_dai **cpu_dais;
|
||||
unsigned int num_cpus;
|
||||
|
||||
struct snd_soc_dapm_widget *playback_widget;
|
||||
struct snd_soc_dapm_widget *capture_widget;
|
||||
|
||||
struct delayed_work delayed_work;
|
||||
void (*close_delayed_work_func)(struct snd_soc_pcm_runtime *rtd);
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
@ -1170,28 +1174,28 @@ struct snd_soc_pcm_runtime {
|
|||
unsigned int fe_compr:1; /* for Dynamic PCM */
|
||||
|
||||
int num_components;
|
||||
struct snd_soc_component *components[0]; /* CPU/Codec/Platform */
|
||||
struct snd_soc_component *components[]; /* CPU/Codec/Platform */
|
||||
};
|
||||
/* see soc_new_pcm_runtime() */
|
||||
#define asoc_rtd_to_cpu(rtd, n) (rtd)->dais[n]
|
||||
#define asoc_rtd_to_codec(rtd, n) (rtd)->dais[n + (rtd)->num_cpus]
|
||||
|
||||
#define for_each_rtd_components(rtd, i, component) \
|
||||
for ((i) = 0; \
|
||||
for ((i) = 0, component = NULL; \
|
||||
((i) < rtd->num_components) && ((component) = rtd->components[i]);\
|
||||
(i)++)
|
||||
#define for_each_rtd_cpu_dais(rtd, i, dai) \
|
||||
for ((i) = 0; \
|
||||
((i) < rtd->num_cpus) && ((dai) = rtd->cpu_dais[i]); \
|
||||
((i) < rtd->num_cpus) && ((dai) = asoc_rtd_to_cpu(rtd, i)); \
|
||||
(i)++)
|
||||
#define for_each_rtd_cpu_dais_rollback(rtd, i, dai) \
|
||||
for (; (--(i) >= 0) && ((dai) = rtd->cpu_dais[i]);)
|
||||
for (; (--(i) >= 0) && ((dai) = asoc_rtd_to_cpu(rtd, i));)
|
||||
#define for_each_rtd_codec_dais(rtd, i, dai) \
|
||||
for ((i) = 0; \
|
||||
((i) < rtd->num_codecs) && ((dai) = rtd->codec_dais[i]); \
|
||||
((i) < rtd->num_codecs) && ((dai) = asoc_rtd_to_codec(rtd, i)); \
|
||||
(i)++)
|
||||
#define for_each_rtd_codec_dais_rollback(rtd, i, dai) \
|
||||
for (; (--(i) >= 0) && ((dai) = rtd->codec_dais[i]);)
|
||||
for (; (--(i) >= 0) && ((dai) = asoc_rtd_to_codec(rtd, i));)
|
||||
#define for_each_rtd_dais(rtd, i, dai) \
|
||||
for ((i) = 0; \
|
||||
((i) < (rtd)->num_cpus + (rtd)->num_codecs) && \
|
||||
|
@ -1252,29 +1256,16 @@ struct soc_enum {
|
|||
#endif
|
||||
};
|
||||
|
||||
/* device driver data */
|
||||
|
||||
static inline void snd_soc_card_set_drvdata(struct snd_soc_card *card,
|
||||
void *data)
|
||||
{
|
||||
card->drvdata = data;
|
||||
}
|
||||
|
||||
static inline void *snd_soc_card_get_drvdata(struct snd_soc_card *card)
|
||||
{
|
||||
return card->drvdata;
|
||||
}
|
||||
|
||||
static inline bool snd_soc_volsw_is_stereo(struct soc_mixer_control *mc)
|
||||
{
|
||||
if (mc->reg == mc->rreg && mc->shift == mc->rshift)
|
||||
return 0;
|
||||
return false;
|
||||
/*
|
||||
* mc->reg == mc->rreg && mc->shift != mc->rshift, or
|
||||
* mc->reg != mc->rreg means that the control is
|
||||
* stereo (bits in one register or in two registers)
|
||||
*/
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline unsigned int snd_soc_enum_val_to_item(struct soc_enum *e,
|
||||
|
@ -1377,20 +1368,6 @@ struct snd_soc_dai *snd_soc_find_dai(
|
|||
|
||||
#include <sound/soc-dai.h>
|
||||
|
||||
static inline
|
||||
struct snd_soc_dai *snd_soc_card_get_codec_dai(struct snd_soc_card *card,
|
||||
const char *dai_name)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
|
||||
list_for_each_entry(rtd, &card->rtd_list, list) {
|
||||
if (!strcmp(rtd->codec_dai->name, dai_name))
|
||||
return rtd->codec_dai;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline
|
||||
int snd_soc_fixup_dai_links_platform_name(struct snd_soc_card *card,
|
||||
const char *platform_name)
|
||||
|
@ -1436,5 +1413,6 @@ static inline void snd_soc_dapm_mutex_unlock(struct snd_soc_dapm_context *dapm)
|
|||
}
|
||||
|
||||
#include <sound/soc-component.h>
|
||||
#include <sound/soc-card.h>
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
|
||||
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
|
@ -27,6 +27,9 @@ struct snd_sof_pdata {
|
|||
|
||||
struct device *dev;
|
||||
|
||||
/* indicate how many first bytes shouldn't be loaded into DSP memory. */
|
||||
size_t fw_offset;
|
||||
|
||||
/*
|
||||
* notification callback used if the hardware initialization
|
||||
* can take time or is handled in a workqueue. This callback
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
|
||||
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
|
||||
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
|
||||
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
|
||||
/*
|
||||
* Copyright 2019 NXP
|
||||
*
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
|
||||
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
|
@ -49,6 +49,9 @@
|
|||
/* bclk idle */
|
||||
#define SOF_DAI_INTEL_SSP_CLKCTRL_BCLK_IDLE_HIGH BIT(5)
|
||||
|
||||
/* DMIC max. four controllers for eight microphone channels */
|
||||
#define SOF_DAI_INTEL_DMIC_NUM_CTRL 4
|
||||
|
||||
/* SSP Configuration Request - SOF_IPC_DAI_SSP_CONFIG */
|
||||
struct sof_ipc_dai_ssp_params {
|
||||
struct sof_ipc_hdr hdr;
|
||||
|
@ -85,15 +88,19 @@ struct sof_ipc_dai_ssp_params {
|
|||
struct sof_ipc_dai_hda_params {
|
||||
struct sof_ipc_hdr hdr;
|
||||
uint32_t link_dma_ch;
|
||||
uint32_t rate;
|
||||
uint32_t channels;
|
||||
} __packed;
|
||||
|
||||
/* ALH Configuration Request - SOF_IPC_DAI_ALH_CONFIG */
|
||||
struct sof_ipc_dai_alh_params {
|
||||
struct sof_ipc_hdr hdr;
|
||||
uint32_t stream_id;
|
||||
uint32_t rate;
|
||||
uint32_t channels;
|
||||
|
||||
/* reserved for future use */
|
||||
uint32_t reserved[15];
|
||||
uint32_t reserved[13];
|
||||
} __packed;
|
||||
|
||||
/* DMIC Configuration Request - SOF_IPC_DAI_DMIC_CONFIG */
|
||||
|
@ -135,7 +142,7 @@ struct sof_ipc_dai_dmic_pdm_ctrl {
|
|||
* version number used in configuration data is checked vs. version used by
|
||||
* device driver src/drivers/dmic.c need to match. It is incremented from
|
||||
* initial value 1 if updates done for the to driver would alter the operation
|
||||
* of the microhone.
|
||||
* of the microphone.
|
||||
*
|
||||
* Note: The microphone clock (pdmclk_min, pdmclk_max, duty_min, duty_max)
|
||||
* parameters need to be set as defined in microphone data sheet. E.g. clock
|
||||
|
@ -170,12 +177,13 @@ struct sof_ipc_dai_dmic_params {
|
|||
uint32_t fifo_fs; /**< FIFO sample rate in Hz (8000..96000) */
|
||||
uint32_t reserved_1; /**< Reserved */
|
||||
uint16_t fifo_bits; /**< FIFO word length (16 or 32) */
|
||||
uint16_t reserved_2; /**< Reserved */
|
||||
uint16_t fifo_bits_b; /**< Deprecated since firmware ABI 3.0.1 */
|
||||
|
||||
uint16_t duty_min; /**< Min. mic clock duty cycle in % (20..80) */
|
||||
uint16_t duty_max; /**< Max. mic clock duty cycle in % (min..80) */
|
||||
|
||||
uint32_t num_pdm_active; /**< Number of active pdm controllers */
|
||||
uint32_t num_pdm_active; /**< Number of active pdm controllers. */
|
||||
/**< Range is 1..SOF_DAI_INTEL_DMIC_NUM_CTRL */
|
||||
|
||||
uint32_t wake_up_time; /**< Time from clock start to data (us) */
|
||||
uint32_t min_clock_on_time; /**< Min. time that clk is kept on (us) */
|
||||
|
@ -184,8 +192,8 @@ struct sof_ipc_dai_dmic_params {
|
|||
/* reserved for future use */
|
||||
uint32_t reserved[5];
|
||||
|
||||
/**< variable number of pdm controller config */
|
||||
struct sof_ipc_dai_dmic_pdm_ctrl pdm[0];
|
||||
/**< PDM controllers configuration */
|
||||
struct sof_ipc_dai_dmic_pdm_ctrl pdm[SOF_DAI_INTEL_DMIC_NUM_CTRL];
|
||||
} __packed;
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
|
||||
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2020 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Extended manifest is a place to store metadata about firmware, known during
|
||||
* compilation time - for example firmware version or used compiler.
|
||||
* Given information are read on host side before firmware startup.
|
||||
* This part of output binary is not signed.
|
||||
*/
|
||||
|
||||
#ifndef __SOF_FIRMWARE_EXT_MANIFEST_H__
|
||||
#define __SOF_FIRMWARE_EXT_MANIFEST_H__
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/types.h>
|
||||
#include <sound/sof/info.h>
|
||||
|
||||
/* In ASCII `XMan` */
|
||||
#define SOF_EXT_MAN_MAGIC_NUMBER 0x6e614d58
|
||||
|
||||
/* Build u32 number in format MMmmmppp */
|
||||
#define SOF_EXT_MAN_BUILD_VERSION(MAJOR, MINOR, PATH) ((uint32_t)( \
|
||||
((MAJOR) << 24) | \
|
||||
((MINOR) << 12) | \
|
||||
(PATH)))
|
||||
|
||||
/* check extended manifest version consistency */
|
||||
#define SOF_EXT_MAN_VERSION_INCOMPATIBLE(host_ver, cli_ver) ( \
|
||||
((host_ver) & GENMASK(31, 24)) != \
|
||||
((cli_ver) & GENMASK(31, 24)))
|
||||
|
||||
/* used extended manifest header version */
|
||||
#define SOF_EXT_MAN_VERSION SOF_EXT_MAN_BUILD_VERSION(1, 0, 0)
|
||||
|
||||
/* extended manifest header, deleting any field breaks backward compatibility */
|
||||
struct sof_ext_man_header {
|
||||
uint32_t magic; /*< identification number, */
|
||||
/*< EXT_MAN_MAGIC_NUMBER */
|
||||
uint32_t full_size; /*< [bytes] full size of ext_man, */
|
||||
/*< (header + content + padding) */
|
||||
uint32_t header_size; /*< [bytes] makes header extensionable, */
|
||||
/*< after append new field to ext_man header */
|
||||
/*< then backward compatible won't be lost */
|
||||
uint32_t header_version; /*< value of EXT_MAN_VERSION */
|
||||
/*< not related with following content */
|
||||
|
||||
/* just after this header should be list of ext_man_elem_* elements */
|
||||
} __packed;
|
||||
|
||||
/* Now define extended manifest elements */
|
||||
|
||||
/* Extended manifest elements types */
|
||||
enum sof_ext_man_elem_type {
|
||||
SOF_EXT_MAN_ELEM_FW_VERSION = 0,
|
||||
SOF_EXT_MAN_ELEM_WINDOW = SOF_IPC_EXT_WINDOW,
|
||||
SOF_EXT_MAN_ELEM_CC_VERSION = SOF_IPC_EXT_CC_INFO,
|
||||
};
|
||||
|
||||
/* extended manifest element header */
|
||||
struct sof_ext_man_elem_header {
|
||||
uint32_t type; /*< SOF_EXT_MAN_ELEM_ */
|
||||
uint32_t size; /*< in bytes, including header size */
|
||||
|
||||
/* just after this header should be type dependent content */
|
||||
} __packed;
|
||||
|
||||
/* FW version */
|
||||
struct sof_ext_man_fw_version {
|
||||
struct sof_ext_man_elem_header hdr;
|
||||
/* use sof_ipc struct because of code re-use */
|
||||
struct sof_ipc_fw_version version;
|
||||
uint32_t flags;
|
||||
} __packed;
|
||||
|
||||
/* extended data memory windows for IPC, trace and debug */
|
||||
struct sof_ext_man_window {
|
||||
struct sof_ext_man_elem_header hdr;
|
||||
/* use sof_ipc struct because of code re-use */
|
||||
struct sof_ipc_window ipc_window;
|
||||
} __packed;
|
||||
|
||||
/* Used C compiler description */
|
||||
struct sof_ext_man_cc_version {
|
||||
struct sof_ext_man_elem_header hdr;
|
||||
/* use sof_ipc struct because of code re-use */
|
||||
struct sof_ipc_cc_version cc_version;
|
||||
} __packed;
|
||||
|
||||
#endif /* __SOF_FIRMWARE_EXT_MANIFEST_H__ */
|
|
@ -1,4 +1,4 @@
|
|||
/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
|
||||
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
|
||||
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
|
@ -31,6 +31,8 @@ enum sof_ipc_ext_data {
|
|||
SOF_IPC_EXT_UNUSED = 0,
|
||||
SOF_IPC_EXT_WINDOW = 1,
|
||||
SOF_IPC_EXT_CC_INFO = 2,
|
||||
SOF_IPC_EXT_PROBE_INFO = 3,
|
||||
SOF_IPC_EXT_USER_ABI_INFO = 4,
|
||||
};
|
||||
|
||||
/* FW version - SOF_IPC_GLB_VERSION */
|
||||
|
@ -109,9 +111,27 @@ struct sof_ipc_cc_version {
|
|||
/* reserved for future use */
|
||||
uint32_t reserved[4];
|
||||
|
||||
char name[16]; /* null terminated compiler name */
|
||||
char optim[4]; /* null terminated compiler -O flag value */
|
||||
char desc[]; /* null terminated compiler description */
|
||||
uint8_t name[16]; /* null terminated compiler name */
|
||||
uint8_t optim[4]; /* null terminated compiler -O flag value */
|
||||
uint8_t desc[32]; /* null terminated compiler description */
|
||||
} __packed;
|
||||
|
||||
/* extended data: Probe setup */
|
||||
struct sof_ipc_probe_support {
|
||||
struct sof_ipc_ext_data_hdr ext_hdr;
|
||||
|
||||
uint32_t probe_points_max;
|
||||
uint32_t injection_dmas_max;
|
||||
|
||||
/* reserved for future use */
|
||||
uint32_t reserved[2];
|
||||
} __packed;
|
||||
|
||||
/* extended data: user abi version(s) */
|
||||
struct sof_ipc_user_abi_version {
|
||||
struct sof_ipc_ext_data_hdr ext_hdr;
|
||||
|
||||
uint32_t abi_dbg_version;
|
||||
} __packed;
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
|
||||
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
|
||||
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
|
||||
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
|
@ -37,6 +37,8 @@ enum sof_comp_type {
|
|||
SOF_COMP_SELECTOR, /**< channel selector component */
|
||||
SOF_COMP_DEMUX,
|
||||
SOF_COMP_ASRC, /**< Asynchronous sample rate converter */
|
||||
SOF_COMP_DCBLOCK,
|
||||
SOF_COMP_SMART_AMP, /**< smart amplifier component */
|
||||
/* keep FILEREAD/FILEWRITE as the last ones */
|
||||
SOF_COMP_FILEREAD = 10000, /**< host test based file IO */
|
||||
SOF_COMP_FILEWRITE = 10001, /**< host test based file IO */
|
||||
|
@ -75,11 +77,23 @@ struct sof_ipc_comp {
|
|||
#define SOF_MEM_CAPS_CACHE (1 << 6) /**< cacheable */
|
||||
#define SOF_MEM_CAPS_EXEC (1 << 7) /**< executable */
|
||||
|
||||
/*
|
||||
* overrun will cause ring buffer overwrite, instead of XRUN.
|
||||
*/
|
||||
#define SOF_BUF_OVERRUN_PERMITTED BIT(0)
|
||||
|
||||
/*
|
||||
* underrun will cause readback of 0s, instead of XRUN.
|
||||
*/
|
||||
#define SOF_BUF_UNDERRUN_PERMITTED BIT(1)
|
||||
|
||||
/* create new component buffer - SOF_IPC_TPLG_BUFFER_NEW */
|
||||
struct sof_ipc_buffer {
|
||||
struct sof_ipc_comp comp;
|
||||
uint32_t size; /**< buffer size in bytes */
|
||||
uint32_t caps; /**< SOF_MEM_CAPS_ */
|
||||
uint32_t flags; /**< SOF_BUF_ flags defined above */
|
||||
uint32_t reserved; /**< reserved for future use */
|
||||
} __packed;
|
||||
|
||||
/* generic component config data - must always be after struct sof_ipc_comp */
|
||||
|
@ -206,6 +220,8 @@ enum sof_ipc_process_type {
|
|||
SOF_PROCESS_CHAN_SELECTOR, /**< Channel Selector */
|
||||
SOF_PROCESS_MUX,
|
||||
SOF_PROCESS_DEMUX,
|
||||
SOF_PROCESS_DCBLOCK,
|
||||
SOF_PROCESS_SMART_AMP, /**< Smart Amplifier */
|
||||
};
|
||||
|
||||
/* generic "effect", "codec" or proprietary processing component */
|
||||
|
@ -218,7 +234,7 @@ struct sof_ipc_comp_process {
|
|||
/* reserved for future use */
|
||||
uint32_t reserved[7];
|
||||
|
||||
unsigned char data[0];
|
||||
uint8_t data[0];
|
||||
} __packed;
|
||||
|
||||
/* frees components, buffers and pipelines
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
|
||||
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
|
@ -72,7 +72,7 @@ struct sof_ipc_dma_trace_posn {
|
|||
struct sof_ipc_panic_info {
|
||||
struct sof_ipc_hdr hdr;
|
||||
uint32_t code; /* SOF_IPC_PANIC_ */
|
||||
char filename[SOF_TRACE_FILENAME_SIZE];
|
||||
uint8_t filename[SOF_TRACE_FILENAME_SIZE];
|
||||
uint32_t linenum;
|
||||
} __packed;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
|
||||
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
*/
|
||||
#define SKL_CONTROL_TYPE_BYTE_TLV 0x100
|
||||
#define SKL_CONTROL_TYPE_MIC_SELECT 0x102
|
||||
#define SKL_CONTROL_TYPE_MULTI_IO_SELECT 0x103
|
||||
#define SKL_CONTROL_TYPE_MULTI_IO_SELECT_DMIC 0x104
|
||||
|
||||
#define HDA_SST_CFG_MAX 900 /* size of copier cfg*/
|
||||
#define MAX_IN_QUEUE 8
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
/* SOF ABI version major, minor and patch numbers */
|
||||
#define SOF_ABI_MAJOR 3
|
||||
#define SOF_ABI_MINOR 13
|
||||
#define SOF_ABI_MINOR 16
|
||||
#define SOF_ABI_PATCH 0
|
||||
|
||||
/* SOF ABI version number. Format within 32bit word is MMmmmppp */
|
||||
|
|
|
@ -126,4 +126,12 @@
|
|||
#define SOF_TKN_MUTE_LED_USE 1300
|
||||
#define SOF_TKN_MUTE_LED_DIRECTION 1301
|
||||
|
||||
/* ALH */
|
||||
#define SOF_TKN_INTEL_ALH_RATE 1400
|
||||
#define SOF_TKN_INTEL_ALH_CH 1401
|
||||
|
||||
/* HDA */
|
||||
#define SOF_TKN_INTEL_HDA_RATE 1500
|
||||
#define SOF_TKN_INTEL_HDA_CH 1501
|
||||
|
||||
#endif
|
||||
|
|
|
@ -64,7 +64,7 @@ struct snd_pcm_plugin {
|
|||
char *buf;
|
||||
snd_pcm_uframes_t buf_frames;
|
||||
struct snd_pcm_plugin_channel *buf_channels;
|
||||
char extra_data[0];
|
||||
char extra_data[];
|
||||
};
|
||||
|
||||
int snd_pcm_plugin_build(struct snd_pcm_substream *handle,
|
||||
|
|
|
@ -44,14 +44,4 @@ snd_seq_oss_timer_cur_tick(struct seq_oss_timer *timer)
|
|||
return timer->cur_tick;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* is realtime event?
|
||||
*/
|
||||
static inline int
|
||||
snd_seq_oss_timer_is_realtime(struct seq_oss_timer *timer)
|
||||
{
|
||||
return timer->realtime;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -457,7 +457,7 @@ static int portman_probe(struct parport *p)
|
|||
|
||||
/* Set for RXDATA0 where no damage will be done. */
|
||||
/* 5 */
|
||||
parport_write_control(p, RXDATA0 + STROBE); /* Write Strobe=1 to command reg. */
|
||||
parport_write_control(p, RXDATA0 | STROBE); /* Write Strobe=1 to command reg. */
|
||||
|
||||
/* 6 */
|
||||
if ((parport_read_status(p) & ESTB) != ESTB)
|
||||
|
|
|
@ -150,8 +150,12 @@ config SND_FIREWIRE_MOTU
|
|||
Say Y here to enable support for FireWire devices which MOTU produced:
|
||||
* 828mk2
|
||||
* Traveler
|
||||
* 828mk3
|
||||
* Ultralite
|
||||
* 8pre
|
||||
* 828mk3 (FireWire only)
|
||||
* 828mk3 (Hybrid)
|
||||
* Audio Express
|
||||
* 4pre
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-firewire-motu.
|
||||
|
@ -164,6 +168,8 @@ config SND_FIREFACE
|
|||
Say Y here to include support for RME fireface series.
|
||||
* Fireface 400
|
||||
* Fireface 800
|
||||
* Fireface UFX
|
||||
* Fireface UCX
|
||||
* Fireface 802
|
||||
|
||||
endif # SND_FIREWIRE
|
||||
|
|
|
@ -82,7 +82,8 @@ int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate,
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
s->ctx_data.rx.fdf = AMDTP_FDF_AM824 | s->sfc;
|
||||
if (s->direction == AMDTP_OUT_STREAM)
|
||||
s->ctx_data.rx.fdf = AMDTP_FDF_AM824 | s->sfc;
|
||||
|
||||
p->pcm_channels = pcm_channels;
|
||||
p->midi_ports = midi_ports;
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#define CYCLES_PER_SECOND 8000
|
||||
#define TICKS_PER_SECOND (TICKS_PER_CYCLE * CYCLES_PER_SECOND)
|
||||
|
||||
#define OHCI_MAX_SECOND 8
|
||||
|
||||
/* Always support Linux tracing subsystem. */
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include "amdtp-stream-trace.h"
|
||||
|
@ -337,25 +339,26 @@ void amdtp_stream_pcm_prepare(struct amdtp_stream *s)
|
|||
}
|
||||
EXPORT_SYMBOL(amdtp_stream_pcm_prepare);
|
||||
|
||||
static unsigned int calculate_data_blocks(struct amdtp_stream *s,
|
||||
unsigned int syt)
|
||||
static unsigned int calculate_data_blocks(unsigned int *data_block_state,
|
||||
bool is_blocking, bool is_no_info,
|
||||
unsigned int syt_interval, enum cip_sfc sfc)
|
||||
{
|
||||
unsigned int phase, data_blocks;
|
||||
unsigned int data_blocks;
|
||||
|
||||
/* Blocking mode. */
|
||||
if (s->flags & CIP_BLOCKING) {
|
||||
if (is_blocking) {
|
||||
/* This module generate empty packet for 'no data'. */
|
||||
if (syt == CIP_SYT_NO_INFO)
|
||||
if (is_no_info)
|
||||
data_blocks = 0;
|
||||
else
|
||||
data_blocks = s->syt_interval;
|
||||
data_blocks = syt_interval;
|
||||
/* Non-blocking mode. */
|
||||
} else {
|
||||
if (!cip_sfc_is_base_44100(s->sfc)) {
|
||||
if (!cip_sfc_is_base_44100(sfc)) {
|
||||
// Sample_rate / 8000 is an integer, and precomputed.
|
||||
data_blocks = s->ctx_data.rx.data_block_state;
|
||||
data_blocks = *data_block_state;
|
||||
} else {
|
||||
phase = s->ctx_data.rx.data_block_state;
|
||||
unsigned int phase = *data_block_state;
|
||||
|
||||
/*
|
||||
* This calculates the number of data blocks per packet so that
|
||||
|
@ -365,31 +368,30 @@ static unsigned int calculate_data_blocks(struct amdtp_stream *s,
|
|||
* as possible in the sequence (to prevent underruns of the
|
||||
* device's buffer).
|
||||
*/
|
||||
if (s->sfc == CIP_SFC_44100)
|
||||
if (sfc == CIP_SFC_44100)
|
||||
/* 6 6 5 6 5 6 5 ... */
|
||||
data_blocks = 5 + ((phase & 1) ^
|
||||
(phase == 0 || phase >= 40));
|
||||
else
|
||||
/* 12 11 11 11 11 ... or 23 22 22 22 22 ... */
|
||||
data_blocks = 11 * (s->sfc >> 1) + (phase == 0);
|
||||
if (++phase >= (80 >> (s->sfc >> 1)))
|
||||
data_blocks = 11 * (sfc >> 1) + (phase == 0);
|
||||
if (++phase >= (80 >> (sfc >> 1)))
|
||||
phase = 0;
|
||||
s->ctx_data.rx.data_block_state = phase;
|
||||
*data_block_state = phase;
|
||||
}
|
||||
}
|
||||
|
||||
return data_blocks;
|
||||
}
|
||||
|
||||
static unsigned int calculate_syt(struct amdtp_stream *s,
|
||||
unsigned int cycle)
|
||||
static unsigned int calculate_syt_offset(unsigned int *last_syt_offset,
|
||||
unsigned int *syt_offset_state, enum cip_sfc sfc)
|
||||
{
|
||||
unsigned int syt_offset, phase, index, syt;
|
||||
unsigned int syt_offset;
|
||||
|
||||
if (s->ctx_data.rx.last_syt_offset < TICKS_PER_CYCLE) {
|
||||
if (!cip_sfc_is_base_44100(s->sfc))
|
||||
syt_offset = s->ctx_data.rx.last_syt_offset +
|
||||
s->ctx_data.rx.syt_offset_state;
|
||||
if (*last_syt_offset < TICKS_PER_CYCLE) {
|
||||
if (!cip_sfc_is_base_44100(sfc))
|
||||
syt_offset = *last_syt_offset + *syt_offset_state;
|
||||
else {
|
||||
/*
|
||||
* The time, in ticks, of the n'th SYT_INTERVAL sample is:
|
||||
|
@ -401,28 +403,24 @@ static unsigned int calculate_syt(struct amdtp_stream *s,
|
|||
* 1386 1386 1387 1386 1386 1386 1387 1386 1386 1386 1387 ...
|
||||
* This code generates _exactly_ the same sequence.
|
||||
*/
|
||||
phase = s->ctx_data.rx.syt_offset_state;
|
||||
index = phase % 13;
|
||||
syt_offset = s->ctx_data.rx.last_syt_offset;
|
||||
unsigned int phase = *syt_offset_state;
|
||||
unsigned int index = phase % 13;
|
||||
|
||||
syt_offset = *last_syt_offset;
|
||||
syt_offset += 1386 + ((index && !(index & 3)) ||
|
||||
phase == 146);
|
||||
if (++phase >= 147)
|
||||
phase = 0;
|
||||
s->ctx_data.rx.syt_offset_state = phase;
|
||||
*syt_offset_state = phase;
|
||||
}
|
||||
} else
|
||||
syt_offset = s->ctx_data.rx.last_syt_offset - TICKS_PER_CYCLE;
|
||||
s->ctx_data.rx.last_syt_offset = syt_offset;
|
||||
syt_offset = *last_syt_offset - TICKS_PER_CYCLE;
|
||||
*last_syt_offset = syt_offset;
|
||||
|
||||
if (syt_offset < TICKS_PER_CYCLE) {
|
||||
syt_offset += s->ctx_data.rx.transfer_delay;
|
||||
syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12;
|
||||
syt += syt_offset % TICKS_PER_CYCLE;
|
||||
if (syt_offset >= TICKS_PER_CYCLE)
|
||||
syt_offset = CIP_SYT_NO_INFO;
|
||||
|
||||
return syt & CIP_SYT_MASK;
|
||||
} else {
|
||||
return CIP_SYT_NO_INFO;
|
||||
}
|
||||
return syt_offset;
|
||||
}
|
||||
|
||||
static void update_pcm_pointers(struct amdtp_stream *s,
|
||||
|
@ -680,8 +678,8 @@ static inline u32 compute_cycle_count(__be32 ctx_header_tstamp)
|
|||
static inline u32 increment_cycle_count(u32 cycle, unsigned int addend)
|
||||
{
|
||||
cycle += addend;
|
||||
if (cycle >= 8 * CYCLES_PER_SECOND)
|
||||
cycle -= 8 * CYCLES_PER_SECOND;
|
||||
if (cycle >= OHCI_MAX_SECOND * CYCLES_PER_SECOND)
|
||||
cycle -= OHCI_MAX_SECOND * CYCLES_PER_SECOND;
|
||||
return cycle;
|
||||
}
|
||||
|
||||
|
@ -738,21 +736,41 @@ static int generate_device_pkt_descs(struct amdtp_stream *s,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void generate_ideal_pkt_descs(struct amdtp_stream *s,
|
||||
struct pkt_desc *descs,
|
||||
const __be32 *ctx_header,
|
||||
unsigned int packets)
|
||||
static unsigned int compute_syt(unsigned int syt_offset, unsigned int cycle,
|
||||
unsigned int transfer_delay)
|
||||
{
|
||||
unsigned int syt;
|
||||
|
||||
syt_offset += transfer_delay;
|
||||
syt = ((cycle + syt_offset / TICKS_PER_CYCLE) << 12) |
|
||||
(syt_offset % TICKS_PER_CYCLE);
|
||||
return syt & CIP_SYT_MASK;
|
||||
}
|
||||
|
||||
static void generate_pkt_descs(struct amdtp_stream *s, struct pkt_desc *descs,
|
||||
const __be32 *ctx_header, unsigned int packets,
|
||||
const struct seq_desc *seq_descs,
|
||||
unsigned int seq_size)
|
||||
{
|
||||
unsigned int dbc = s->data_block_counter;
|
||||
unsigned int seq_index = s->ctx_data.rx.seq_index;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < packets; ++i) {
|
||||
struct pkt_desc *desc = descs + i;
|
||||
unsigned int index = (s->packet_index + i) % s->queue_size;
|
||||
const struct seq_desc *seq = seq_descs + seq_index;
|
||||
unsigned int syt;
|
||||
|
||||
desc->cycle = compute_it_cycle(*ctx_header, s->queue_size);
|
||||
desc->syt = calculate_syt(s, desc->cycle);
|
||||
desc->data_blocks = calculate_data_blocks(s, desc->syt);
|
||||
|
||||
syt = seq->syt_offset;
|
||||
if (syt != CIP_SYT_NO_INFO) {
|
||||
syt = compute_syt(syt, desc->cycle,
|
||||
s->ctx_data.rx.transfer_delay);
|
||||
}
|
||||
desc->syt = syt;
|
||||
desc->data_blocks = seq->data_blocks;
|
||||
|
||||
if (s->flags & CIP_DBC_IS_END_EVENT)
|
||||
dbc = (dbc + desc->data_blocks) & 0xff;
|
||||
|
@ -764,10 +782,13 @@ static void generate_ideal_pkt_descs(struct amdtp_stream *s,
|
|||
|
||||
desc->ctx_payload = s->buffer.packets[index].buffer;
|
||||
|
||||
seq_index = (seq_index + 1) % seq_size;
|
||||
|
||||
++ctx_header;
|
||||
}
|
||||
|
||||
s->data_block_counter = dbc;
|
||||
s->ctx_data.rx.seq_index = seq_index;
|
||||
}
|
||||
|
||||
static inline void cancel_stream(struct amdtp_stream *s)
|
||||
|
@ -791,24 +812,16 @@ static void process_ctx_payloads(struct amdtp_stream *s,
|
|||
update_pcm_pointers(s, pcm, pcm_frames);
|
||||
}
|
||||
|
||||
static void amdtp_stream_master_callback(struct fw_iso_context *context,
|
||||
u32 tstamp, size_t header_length,
|
||||
void *header, void *private_data);
|
||||
|
||||
static void amdtp_stream_master_first_callback(struct fw_iso_context *context,
|
||||
u32 tstamp, size_t header_length,
|
||||
void *header, void *private_data);
|
||||
|
||||
static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
|
||||
size_t header_length, void *header,
|
||||
void *private_data)
|
||||
{
|
||||
struct amdtp_stream *s = private_data;
|
||||
const struct amdtp_domain *d = s->domain;
|
||||
const __be32 *ctx_header = header;
|
||||
unsigned int events_per_period = s->ctx_data.rx.events_per_period;
|
||||
unsigned int event_count = s->ctx_data.rx.event_count;
|
||||
unsigned int packets;
|
||||
bool is_irq_target;
|
||||
int i;
|
||||
|
||||
if (s->packet_index < 0)
|
||||
|
@ -817,14 +830,11 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
|
|||
// Calculate the number of packets in buffer and check XRUN.
|
||||
packets = header_length / sizeof(*ctx_header);
|
||||
|
||||
generate_ideal_pkt_descs(s, s->pkt_descs, ctx_header, packets);
|
||||
generate_pkt_descs(s, s->pkt_descs, ctx_header, packets, d->seq_descs,
|
||||
d->seq_size);
|
||||
|
||||
process_ctx_payloads(s, s->pkt_descs, packets);
|
||||
|
||||
is_irq_target =
|
||||
!!(context->callback.sc == amdtp_stream_master_callback ||
|
||||
context->callback.sc == amdtp_stream_master_first_callback);
|
||||
|
||||
for (i = 0; i < packets; ++i) {
|
||||
const struct pkt_desc *desc = s->pkt_descs + i;
|
||||
unsigned int syt;
|
||||
|
@ -843,7 +853,7 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
|
|||
desc->data_blocks, desc->data_block_counter,
|
||||
syt, i);
|
||||
|
||||
if (is_irq_target) {
|
||||
if (s == s->domain->irq_target) {
|
||||
event_count += desc->data_blocks;
|
||||
if (event_count >= events_per_period) {
|
||||
event_count -= events_per_period;
|
||||
|
@ -896,14 +906,63 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
|
|||
}
|
||||
}
|
||||
|
||||
static void amdtp_stream_master_callback(struct fw_iso_context *context,
|
||||
u32 tstamp, size_t header_length,
|
||||
void *header, void *private_data)
|
||||
static void pool_ideal_seq_descs(struct amdtp_domain *d, unsigned int packets)
|
||||
{
|
||||
struct amdtp_domain *d = private_data;
|
||||
struct amdtp_stream *irq_target = d->irq_target;
|
||||
unsigned int seq_tail = d->seq_tail;
|
||||
unsigned int seq_size = d->seq_size;
|
||||
unsigned int min_avail;
|
||||
struct amdtp_stream *s;
|
||||
|
||||
min_avail = d->seq_size;
|
||||
list_for_each_entry(s, &d->streams, list) {
|
||||
unsigned int seq_index;
|
||||
unsigned int avail;
|
||||
|
||||
if (s->direction == AMDTP_IN_STREAM)
|
||||
continue;
|
||||
|
||||
seq_index = s->ctx_data.rx.seq_index;
|
||||
avail = d->seq_tail;
|
||||
if (seq_index > avail)
|
||||
avail += d->seq_size;
|
||||
avail -= seq_index;
|
||||
|
||||
if (avail < min_avail)
|
||||
min_avail = avail;
|
||||
}
|
||||
|
||||
while (min_avail < packets) {
|
||||
struct seq_desc *desc = d->seq_descs + seq_tail;
|
||||
|
||||
desc->syt_offset = calculate_syt_offset(&d->last_syt_offset,
|
||||
&d->syt_offset_state, irq_target->sfc);
|
||||
desc->data_blocks = calculate_data_blocks(&d->data_block_state,
|
||||
!!(irq_target->flags & CIP_BLOCKING),
|
||||
desc->syt_offset == CIP_SYT_NO_INFO,
|
||||
irq_target->syt_interval, irq_target->sfc);
|
||||
|
||||
++seq_tail;
|
||||
seq_tail %= seq_size;
|
||||
|
||||
++min_avail;
|
||||
}
|
||||
|
||||
d->seq_tail = seq_tail;
|
||||
}
|
||||
|
||||
static void irq_target_callback(struct fw_iso_context *context, u32 tstamp,
|
||||
size_t header_length, void *header,
|
||||
void *private_data)
|
||||
{
|
||||
struct amdtp_stream *irq_target = private_data;
|
||||
struct amdtp_domain *d = irq_target->domain;
|
||||
unsigned int packets = header_length / sizeof(__be32);
|
||||
struct amdtp_stream *s;
|
||||
|
||||
// Record enough entries with extra 3 cycles at least.
|
||||
pool_ideal_seq_descs(d, packets + 3);
|
||||
|
||||
out_stream_callback(context, tstamp, header_length, header, irq_target);
|
||||
if (amdtp_streaming_error(irq_target))
|
||||
goto error;
|
||||
|
@ -950,7 +1009,10 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
|
|||
} else {
|
||||
cycle = compute_it_cycle(*ctx_header, s->queue_size);
|
||||
|
||||
context->callback.sc = out_stream_callback;
|
||||
if (s == s->domain->irq_target)
|
||||
context->callback.sc = irq_target_callback;
|
||||
else
|
||||
context->callback.sc = out_stream_callback;
|
||||
}
|
||||
|
||||
s->start_cycle = cycle;
|
||||
|
@ -958,64 +1020,29 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
|
|||
context->callback.sc(context, tstamp, header_length, header, s);
|
||||
}
|
||||
|
||||
static void amdtp_stream_master_first_callback(struct fw_iso_context *context,
|
||||
u32 tstamp, size_t header_length,
|
||||
void *header, void *private_data)
|
||||
{
|
||||
struct amdtp_domain *d = private_data;
|
||||
struct amdtp_stream *s = d->irq_target;
|
||||
const __be32 *ctx_header = header;
|
||||
|
||||
s->callbacked = true;
|
||||
wake_up(&s->callback_wait);
|
||||
|
||||
s->start_cycle = compute_it_cycle(*ctx_header, s->queue_size);
|
||||
|
||||
context->callback.sc = amdtp_stream_master_callback;
|
||||
|
||||
context->callback.sc(context, tstamp, header_length, header, d);
|
||||
}
|
||||
|
||||
/**
|
||||
* amdtp_stream_start - start transferring packets
|
||||
* @s: the AMDTP stream to start
|
||||
* @channel: the isochronous channel on the bus
|
||||
* @speed: firewire speed code
|
||||
* @d: the AMDTP domain to which the AMDTP stream belongs
|
||||
* @is_irq_target: whether isoc context for the AMDTP stream is used to generate
|
||||
* hardware IRQ.
|
||||
* @start_cycle: the isochronous cycle to start the context. Start immediately
|
||||
* if negative value is given.
|
||||
* @queue_size: The number of packets in the queue.
|
||||
* @idle_irq_interval: the interval to queue packet during initial state.
|
||||
*
|
||||
* The stream cannot be started until it has been configured with
|
||||
* amdtp_stream_set_parameters() and it must be started before any PCM or MIDI
|
||||
* device can be started.
|
||||
*/
|
||||
static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed,
|
||||
struct amdtp_domain *d, bool is_irq_target,
|
||||
int start_cycle)
|
||||
int start_cycle, unsigned int queue_size,
|
||||
unsigned int idle_irq_interval)
|
||||
{
|
||||
static const struct {
|
||||
unsigned int data_block;
|
||||
unsigned int syt_offset;
|
||||
} *entry, initial_state[] = {
|
||||
[CIP_SFC_32000] = { 4, 3072 },
|
||||
[CIP_SFC_48000] = { 6, 1024 },
|
||||
[CIP_SFC_96000] = { 12, 1024 },
|
||||
[CIP_SFC_192000] = { 24, 1024 },
|
||||
[CIP_SFC_44100] = { 0, 67 },
|
||||
[CIP_SFC_88200] = { 0, 67 },
|
||||
[CIP_SFC_176400] = { 0, 67 },
|
||||
};
|
||||
unsigned int events_per_buffer = d->events_per_buffer;
|
||||
unsigned int events_per_period = d->events_per_period;
|
||||
unsigned int idle_irq_interval;
|
||||
bool is_irq_target = (s == s->domain->irq_target);
|
||||
unsigned int ctx_header_size;
|
||||
unsigned int max_ctx_payload_size;
|
||||
enum dma_data_direction dir;
|
||||
int type, tag, err;
|
||||
fw_iso_callback_t ctx_cb;
|
||||
void *ctx_data;
|
||||
|
||||
mutex_lock(&s->mutex);
|
||||
|
||||
|
@ -1034,12 +1061,7 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed,
|
|||
|
||||
s->data_block_counter = UINT_MAX;
|
||||
} else {
|
||||
entry = &initial_state[s->sfc];
|
||||
|
||||
s->data_block_counter = 0;
|
||||
s->ctx_data.rx.data_block_state = entry->data_block;
|
||||
s->ctx_data.rx.syt_offset_state = entry->syt_offset;
|
||||
s->ctx_data.rx.last_syt_offset = TICKS_PER_CYCLE;
|
||||
}
|
||||
|
||||
/* initialize packet buffer */
|
||||
|
@ -1063,37 +1085,15 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed,
|
|||
max_ctx_payload_size -= IT_PKT_HEADER_SIZE_CIP;
|
||||
}
|
||||
|
||||
// This is a case that AMDTP streams in domain run just for MIDI
|
||||
// substream. Use the number of events equivalent to 10 msec as
|
||||
// interval of hardware IRQ.
|
||||
if (events_per_period == 0)
|
||||
events_per_period = amdtp_rate_table[s->sfc] / 100;
|
||||
if (events_per_buffer == 0)
|
||||
events_per_buffer = events_per_period * 3;
|
||||
|
||||
idle_irq_interval = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_period,
|
||||
amdtp_rate_table[s->sfc]);
|
||||
s->queue_size = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_buffer,
|
||||
amdtp_rate_table[s->sfc]);
|
||||
|
||||
err = iso_packets_buffer_init(&s->buffer, s->unit, s->queue_size,
|
||||
err = iso_packets_buffer_init(&s->buffer, s->unit, queue_size,
|
||||
max_ctx_payload_size, dir);
|
||||
if (err < 0)
|
||||
goto err_unlock;
|
||||
|
||||
if (is_irq_target) {
|
||||
s->ctx_data.rx.events_per_period = events_per_period;
|
||||
s->ctx_data.rx.event_count = 0;
|
||||
ctx_cb = amdtp_stream_master_first_callback;
|
||||
ctx_data = d;
|
||||
} else {
|
||||
ctx_cb = amdtp_stream_first_callback;
|
||||
ctx_data = s;
|
||||
}
|
||||
s->queue_size = queue_size;
|
||||
|
||||
s->context = fw_iso_context_create(fw_parent_device(s->unit)->card,
|
||||
type, channel, speed, ctx_header_size,
|
||||
ctx_cb, ctx_data);
|
||||
amdtp_stream_first_callback, s);
|
||||
if (IS_ERR(s->context)) {
|
||||
err = PTR_ERR(s->context);
|
||||
if (err == -EBUSY)
|
||||
|
@ -1302,6 +1302,8 @@ int amdtp_domain_init(struct amdtp_domain *d)
|
|||
|
||||
d->events_per_period = 0;
|
||||
|
||||
d->seq_descs = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(amdtp_domain_init);
|
||||
|
@ -1338,6 +1340,7 @@ int amdtp_domain_add_stream(struct amdtp_domain *d, struct amdtp_stream *s,
|
|||
|
||||
s->channel = channel;
|
||||
s->speed = speed;
|
||||
s->domain = d;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1374,6 +1377,22 @@ static int get_current_cycle_time(struct fw_card *fw_card, int *cur_cycle)
|
|||
*/
|
||||
int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle)
|
||||
{
|
||||
static const struct {
|
||||
unsigned int data_block;
|
||||
unsigned int syt_offset;
|
||||
} *entry, initial_state[] = {
|
||||
[CIP_SFC_32000] = { 4, 3072 },
|
||||
[CIP_SFC_48000] = { 6, 1024 },
|
||||
[CIP_SFC_96000] = { 12, 1024 },
|
||||
[CIP_SFC_192000] = { 24, 1024 },
|
||||
[CIP_SFC_44100] = { 0, 67 },
|
||||
[CIP_SFC_88200] = { 0, 67 },
|
||||
[CIP_SFC_176400] = { 0, 67 },
|
||||
};
|
||||
unsigned int events_per_buffer = d->events_per_buffer;
|
||||
unsigned int events_per_period = d->events_per_period;
|
||||
unsigned int idle_irq_interval;
|
||||
unsigned int queue_size;
|
||||
struct amdtp_stream *s;
|
||||
int cycle;
|
||||
int err;
|
||||
|
@ -1387,12 +1406,34 @@ int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle)
|
|||
return -ENXIO;
|
||||
d->irq_target = s;
|
||||
|
||||
// This is a case that AMDTP streams in domain run just for MIDI
|
||||
// substream. Use the number of events equivalent to 10 msec as
|
||||
// interval of hardware IRQ.
|
||||
if (events_per_period == 0)
|
||||
events_per_period = amdtp_rate_table[d->irq_target->sfc] / 100;
|
||||
if (events_per_buffer == 0)
|
||||
events_per_buffer = events_per_period * 3;
|
||||
|
||||
queue_size = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_buffer,
|
||||
amdtp_rate_table[d->irq_target->sfc]);
|
||||
|
||||
d->seq_descs = kcalloc(queue_size, sizeof(*d->seq_descs), GFP_KERNEL);
|
||||
if (!d->seq_descs)
|
||||
return -ENOMEM;
|
||||
d->seq_size = queue_size;
|
||||
d->seq_tail = 0;
|
||||
|
||||
entry = &initial_state[s->sfc];
|
||||
d->data_block_state = entry->data_block;
|
||||
d->syt_offset_state = entry->syt_offset;
|
||||
d->last_syt_offset = TICKS_PER_CYCLE;
|
||||
|
||||
if (ir_delay_cycle > 0) {
|
||||
struct fw_card *fw_card = fw_parent_device(s->unit)->card;
|
||||
|
||||
err = get_current_cycle_time(fw_card, &cycle);
|
||||
if (err < 0)
|
||||
return err;
|
||||
goto error;
|
||||
|
||||
// No need to care overflow in cycle field because of enough
|
||||
// width.
|
||||
|
@ -1423,18 +1464,26 @@ int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle)
|
|||
} else {
|
||||
// IT context starts immediately.
|
||||
cycle_match = -1;
|
||||
s->ctx_data.rx.seq_index = 0;
|
||||
}
|
||||
|
||||
if (s != d->irq_target) {
|
||||
err = amdtp_stream_start(s, s->channel, s->speed, d,
|
||||
false, cycle_match);
|
||||
err = amdtp_stream_start(s, s->channel, s->speed,
|
||||
cycle_match, queue_size, 0);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
s = d->irq_target;
|
||||
err = amdtp_stream_start(s, s->channel, s->speed, d, true, -1);
|
||||
s->ctx_data.rx.events_per_period = events_per_period;
|
||||
s->ctx_data.rx.event_count = 0;
|
||||
s->ctx_data.rx.seq_index = 0;
|
||||
|
||||
idle_irq_interval = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_period,
|
||||
amdtp_rate_table[d->irq_target->sfc]);
|
||||
err = amdtp_stream_start(s, s->channel, s->speed, -1, queue_size,
|
||||
idle_irq_interval);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
|
@ -1442,6 +1491,8 @@ int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle)
|
|||
error:
|
||||
list_for_each_entry(s, &d->streams, list)
|
||||
amdtp_stream_stop(s);
|
||||
kfree(d->seq_descs);
|
||||
d->seq_descs = NULL;
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(amdtp_domain_start);
|
||||
|
@ -1466,5 +1517,8 @@ void amdtp_domain_stop(struct amdtp_domain *d)
|
|||
|
||||
d->events_per_period = 0;
|
||||
d->irq_target = NULL;
|
||||
|
||||
kfree(d->seq_descs);
|
||||
d->seq_descs = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(amdtp_domain_stop);
|
||||
|
|
|
@ -108,6 +108,8 @@ typedef unsigned int (*amdtp_stream_process_ctx_payloads_t)(
|
|||
const struct pkt_desc *desc,
|
||||
unsigned int packets,
|
||||
struct snd_pcm_substream *pcm);
|
||||
|
||||
struct amdtp_domain;
|
||||
struct amdtp_stream {
|
||||
struct fw_unit *unit;
|
||||
enum cip_flags flags;
|
||||
|
@ -136,9 +138,7 @@ struct amdtp_stream {
|
|||
struct {
|
||||
// To calculate CIP data blocks and tstamp.
|
||||
unsigned int transfer_delay;
|
||||
unsigned int data_block_state;
|
||||
unsigned int last_syt_offset;
|
||||
unsigned int syt_offset_state;
|
||||
unsigned int seq_index;
|
||||
|
||||
// To generate CIP header.
|
||||
unsigned int fdf;
|
||||
|
@ -180,6 +180,7 @@ struct amdtp_stream {
|
|||
int channel;
|
||||
int speed;
|
||||
struct list_head list;
|
||||
struct amdtp_domain *domain;
|
||||
};
|
||||
|
||||
int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
|
@ -273,6 +274,11 @@ static inline bool amdtp_stream_wait_callback(struct amdtp_stream *s,
|
|||
msecs_to_jiffies(timeout)) > 0;
|
||||
}
|
||||
|
||||
struct seq_desc {
|
||||
unsigned int syt_offset;
|
||||
unsigned int data_blocks;
|
||||
};
|
||||
|
||||
struct amdtp_domain {
|
||||
struct list_head streams;
|
||||
|
||||
|
@ -280,6 +286,14 @@ struct amdtp_domain {
|
|||
unsigned int events_per_buffer;
|
||||
|
||||
struct amdtp_stream *irq_target;
|
||||
|
||||
struct seq_desc *seq_descs;
|
||||
unsigned int seq_size;
|
||||
unsigned int seq_tail;
|
||||
|
||||
unsigned int data_block_state;
|
||||
unsigned int syt_offset_state;
|
||||
unsigned int last_syt_offset;
|
||||
};
|
||||
|
||||
int amdtp_domain_init(struct amdtp_domain *d);
|
||||
|
|
|
@ -16,7 +16,8 @@
|
|||
#define LATTER_SYNC_STATUS 0x0000801c0000ULL
|
||||
|
||||
static int parse_clock_bits(u32 data, unsigned int *rate,
|
||||
enum snd_ff_clock_src *src)
|
||||
enum snd_ff_clock_src *src,
|
||||
enum snd_ff_unit_version unit_version)
|
||||
{
|
||||
static const struct {
|
||||
unsigned int rate;
|
||||
|
@ -43,6 +44,11 @@ static int parse_clock_bits(u32 data, unsigned int *rate,
|
|||
};
|
||||
int i;
|
||||
|
||||
if (unit_version != SND_FF_UNIT_VERSION_UCX) {
|
||||
// e.g. 0x00fe0f20 but expected 0x00eff002.
|
||||
data = ((data & 0xf0f0f0f0) >> 4) | ((data & 0x0f0f0f0f) << 4);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(rate_entries); ++i) {
|
||||
rate_entry = rate_entries + i;
|
||||
if ((data & 0x0f000000) == rate_entry->flag) {
|
||||
|
@ -79,7 +85,7 @@ static int latter_get_clock(struct snd_ff *ff, unsigned int *rate,
|
|||
return err;
|
||||
data = le32_to_cpu(reg);
|
||||
|
||||
return parse_clock_bits(data, rate, src);
|
||||
return parse_clock_bits(data, rate, src, ff->unit_version);
|
||||
}
|
||||
|
||||
static int latter_switch_fetching_mode(struct snd_ff *ff, bool enable)
|
||||
|
@ -107,18 +113,18 @@ static int latter_allocate_resources(struct snd_ff *ff, unsigned int rate)
|
|||
int err;
|
||||
|
||||
// Set the number of data blocks transferred in a second.
|
||||
if (rate % 32000 == 0)
|
||||
code = 0x00;
|
||||
if (rate % 48000 == 0)
|
||||
code = 0x04;
|
||||
else if (rate % 44100 == 0)
|
||||
code = 0x02;
|
||||
else if (rate % 48000 == 0)
|
||||
code = 0x04;
|
||||
else if (rate % 32000 == 0)
|
||||
code = 0x00;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
if (rate >= 64000 && rate < 128000)
|
||||
code |= 0x08;
|
||||
else if (rate >= 128000 && rate < 192000)
|
||||
else if (rate >= 128000)
|
||||
code |= 0x10;
|
||||
|
||||
reg = cpu_to_le32(code);
|
||||
|
@ -140,7 +146,7 @@ static int latter_allocate_resources(struct snd_ff *ff, unsigned int rate)
|
|||
if (curr_rate == rate)
|
||||
break;
|
||||
}
|
||||
if (count == 10)
|
||||
if (count > 10)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); ++i) {
|
||||
|
@ -181,14 +187,30 @@ static int latter_begin_session(struct snd_ff *ff, unsigned int rate)
|
|||
__le32 reg;
|
||||
int err;
|
||||
|
||||
if (rate >= 32000 && rate <= 48000)
|
||||
flag = 0x92;
|
||||
else if (rate >= 64000 && rate <= 96000)
|
||||
flag = 0x8e;
|
||||
else if (rate >= 128000 && rate <= 192000)
|
||||
flag = 0x8c;
|
||||
else
|
||||
return -EINVAL;
|
||||
if (ff->unit_version == SND_FF_UNIT_VERSION_UCX) {
|
||||
// For Fireface UCX. Always use the maximum number of data
|
||||
// channels in data block of packet.
|
||||
if (rate >= 32000 && rate <= 48000)
|
||||
flag = 0x92;
|
||||
else if (rate >= 64000 && rate <= 96000)
|
||||
flag = 0x8e;
|
||||
else if (rate >= 128000 && rate <= 192000)
|
||||
flag = 0x8c;
|
||||
else
|
||||
return -EINVAL;
|
||||
} else {
|
||||
// For Fireface UFX and 802. Due to bandwidth limitation on
|
||||
// IEEE 1394a (400 Mbps), Analog 1-12 and AES are available
|
||||
// without any ADAT at quadruple speed.
|
||||
if (rate >= 32000 && rate <= 48000)
|
||||
flag = 0x9e;
|
||||
else if (rate >= 64000 && rate <= 96000)
|
||||
flag = 0x96;
|
||||
else if (rate >= 128000 && rate <= 192000)
|
||||
flag = 0x8e;
|
||||
else
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (generation != fw_parent_device(ff->unit)->card->generation) {
|
||||
err = fw_iso_resources_update(&ff->tx_resources);
|
||||
|
@ -207,8 +229,6 @@ static int latter_begin_session(struct snd_ff *ff, unsigned int rate)
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
// Always use the maximum number of data channels in data block of
|
||||
// packet.
|
||||
reg = cpu_to_le32(flag);
|
||||
return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
LATTER_ISOC_START, ®, sizeof(reg), 0);
|
||||
|
@ -263,7 +283,7 @@ static void latter_dump_status(struct snd_ff *ff, struct snd_info_buffer *buffer
|
|||
}
|
||||
}
|
||||
|
||||
err = parse_clock_bits(data, &rate, &src);
|
||||
err = parse_clock_bits(data, &rate, &src, ff->unit_version);
|
||||
if (err < 0)
|
||||
return;
|
||||
label = snd_ff_proc_get_clk_label(src);
|
||||
|
|
|
@ -184,7 +184,6 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
|
|||
*/
|
||||
if (!amdtp_stream_running(&ff->rx_stream)) {
|
||||
int spd = fw_parent_device(ff->unit)->max_speed;
|
||||
unsigned int ir_delay_cycle;
|
||||
|
||||
err = ff->spec->protocol->begin_session(ff, rate);
|
||||
if (err < 0)
|
||||
|
@ -200,14 +199,7 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
|
|||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
// The device postpones start of transmission mostly for several
|
||||
// cycles after receiving packets firstly.
|
||||
if (ff->spec->protocol == &snd_ff_protocol_ff800)
|
||||
ir_delay_cycle = 800; // = 100 msec
|
||||
else
|
||||
ir_delay_cycle = 16; // = 2 msec
|
||||
|
||||
err = amdtp_domain_start(&ff->domain, ir_delay_cycle);
|
||||
err = amdtp_domain_start(&ff->domain, 0);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
|
|
|
@ -16,12 +16,22 @@ MODULE_LICENSE("GPL v2");
|
|||
static void name_card(struct snd_ff *ff)
|
||||
{
|
||||
struct fw_device *fw_dev = fw_parent_device(ff->unit);
|
||||
const char *const names[] = {
|
||||
[SND_FF_UNIT_VERSION_FF800] = "Fireface800",
|
||||
[SND_FF_UNIT_VERSION_FF400] = "Fireface400",
|
||||
[SND_FF_UNIT_VERSION_UFX] = "FirefaceUFX",
|
||||
[SND_FF_UNIT_VERSION_UCX] = "FirefaceUCX",
|
||||
[SND_FF_UNIT_VERSION_802] = "Fireface802",
|
||||
};
|
||||
const char *name;
|
||||
|
||||
name = names[ff->unit_version];
|
||||
|
||||
strcpy(ff->card->driver, "Fireface");
|
||||
strcpy(ff->card->shortname, ff->spec->name);
|
||||
strcpy(ff->card->mixername, ff->spec->name);
|
||||
strcpy(ff->card->shortname, name);
|
||||
strcpy(ff->card->mixername, name);
|
||||
snprintf(ff->card->longname, sizeof(ff->card->longname),
|
||||
"RME %s, GUID %08x%08x at %s, S%d", ff->spec->name,
|
||||
"RME %s, GUID %08x%08x at %s, S%d", name,
|
||||
fw_dev->config_rom[3], fw_dev->config_rom[4],
|
||||
dev_name(&ff->unit->device), 100 << fw_dev->max_speed);
|
||||
}
|
||||
|
@ -101,6 +111,7 @@ static int snd_ff_probe(struct fw_unit *unit,
|
|||
spin_lock_init(&ff->lock);
|
||||
init_waitqueue_head(&ff->hwdep_wait);
|
||||
|
||||
ff->unit_version = entry->version;
|
||||
ff->spec = (const struct snd_ff_spec *)entry->driver_data;
|
||||
|
||||
/* Register this sound card later. */
|
||||
|
@ -145,7 +156,6 @@ static void snd_ff_remove(struct fw_unit *unit)
|
|||
}
|
||||
|
||||
static const struct snd_ff_spec spec_ff800 = {
|
||||
.name = "Fireface800",
|
||||
.pcm_capture_channels = {28, 20, 12},
|
||||
.pcm_playback_channels = {28, 20, 12},
|
||||
.midi_in_ports = 1,
|
||||
|
@ -157,7 +167,6 @@ static const struct snd_ff_spec spec_ff800 = {
|
|||
};
|
||||
|
||||
static const struct snd_ff_spec spec_ff400 = {
|
||||
.name = "Fireface400",
|
||||
.pcm_capture_channels = {18, 14, 10},
|
||||
.pcm_playback_channels = {18, 14, 10},
|
||||
.midi_in_ports = 2,
|
||||
|
@ -169,7 +178,6 @@ static const struct snd_ff_spec spec_ff400 = {
|
|||
};
|
||||
|
||||
static const struct snd_ff_spec spec_ucx = {
|
||||
.name = "FirefaceUCX",
|
||||
.pcm_capture_channels = {18, 14, 12},
|
||||
.pcm_playback_channels = {18, 14, 12},
|
||||
.midi_in_ports = 2,
|
||||
|
@ -180,6 +188,17 @@ static const struct snd_ff_spec spec_ucx = {
|
|||
.midi_rx_addrs = {0xffff00000030ull, 0xffff00000030ull},
|
||||
};
|
||||
|
||||
static const struct snd_ff_spec spec_ufx_802 = {
|
||||
.pcm_capture_channels = {30, 22, 14},
|
||||
.pcm_playback_channels = {30, 22, 14},
|
||||
.midi_in_ports = 1,
|
||||
.midi_out_ports = 1,
|
||||
.protocol = &snd_ff_protocol_latter,
|
||||
.midi_high_addr = 0xffff00000034ull,
|
||||
.midi_addr_range = 0x80,
|
||||
.midi_rx_addrs = {0xffff00000030ull, 0xffff00000030ull},
|
||||
};
|
||||
|
||||
static const struct ieee1394_device_id snd_ff_id_table[] = {
|
||||
/* Fireface 800 */
|
||||
{
|
||||
|
@ -189,7 +208,7 @@ static const struct ieee1394_device_id snd_ff_id_table[] = {
|
|||
IEEE1394_MATCH_MODEL_ID,
|
||||
.vendor_id = OUI_RME,
|
||||
.specifier_id = OUI_RME,
|
||||
.version = 0x000001,
|
||||
.version = SND_FF_UNIT_VERSION_FF800,
|
||||
.model_id = 0x101800,
|
||||
.driver_data = (kernel_ulong_t)&spec_ff800,
|
||||
},
|
||||
|
@ -201,10 +220,22 @@ static const struct ieee1394_device_id snd_ff_id_table[] = {
|
|||
IEEE1394_MATCH_MODEL_ID,
|
||||
.vendor_id = OUI_RME,
|
||||
.specifier_id = OUI_RME,
|
||||
.version = 0x000002,
|
||||
.version = SND_FF_UNIT_VERSION_FF400,
|
||||
.model_id = 0x101800,
|
||||
.driver_data = (kernel_ulong_t)&spec_ff400,
|
||||
},
|
||||
// Fireface UFX.
|
||||
{
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID |
|
||||
IEEE1394_MATCH_SPECIFIER_ID |
|
||||
IEEE1394_MATCH_VERSION |
|
||||
IEEE1394_MATCH_MODEL_ID,
|
||||
.vendor_id = OUI_RME,
|
||||
.specifier_id = OUI_RME,
|
||||
.version = SND_FF_UNIT_VERSION_UFX,
|
||||
.model_id = 0x101800,
|
||||
.driver_data = (kernel_ulong_t)&spec_ufx_802,
|
||||
},
|
||||
// Fireface UCX.
|
||||
{
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID |
|
||||
|
@ -213,10 +244,22 @@ static const struct ieee1394_device_id snd_ff_id_table[] = {
|
|||
IEEE1394_MATCH_MODEL_ID,
|
||||
.vendor_id = OUI_RME,
|
||||
.specifier_id = OUI_RME,
|
||||
.version = 0x000004,
|
||||
.version = SND_FF_UNIT_VERSION_UCX,
|
||||
.model_id = 0x101800,
|
||||
.driver_data = (kernel_ulong_t)&spec_ucx,
|
||||
},
|
||||
// Fireface 802.
|
||||
{
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID |
|
||||
IEEE1394_MATCH_SPECIFIER_ID |
|
||||
IEEE1394_MATCH_VERSION |
|
||||
IEEE1394_MATCH_MODEL_ID,
|
||||
.vendor_id = OUI_RME,
|
||||
.specifier_id = OUI_RME,
|
||||
.version = SND_FF_UNIT_VERSION_802,
|
||||
.model_id = 0x101800,
|
||||
.driver_data = (kernel_ulong_t)&spec_ufx_802,
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(ieee1394, snd_ff_id_table);
|
||||
|
|
|
@ -34,6 +34,14 @@
|
|||
#define SND_FF_IN_MIDI_PORTS 2
|
||||
#define SND_FF_OUT_MIDI_PORTS 2
|
||||
|
||||
enum snd_ff_unit_version {
|
||||
SND_FF_UNIT_VERSION_FF800 = 0x000001,
|
||||
SND_FF_UNIT_VERSION_FF400 = 0x000002,
|
||||
SND_FF_UNIT_VERSION_UFX = 0x000003,
|
||||
SND_FF_UNIT_VERSION_UCX = 0x000004,
|
||||
SND_FF_UNIT_VERSION_802 = 0x000005,
|
||||
};
|
||||
|
||||
enum snd_ff_stream_mode {
|
||||
SND_FF_STREAM_MODE_LOW = 0,
|
||||
SND_FF_STREAM_MODE_MID,
|
||||
|
@ -43,8 +51,6 @@ enum snd_ff_stream_mode {
|
|||
|
||||
struct snd_ff_protocol;
|
||||
struct snd_ff_spec {
|
||||
const char *const name;
|
||||
|
||||
const unsigned int pcm_capture_channels[SND_FF_STREAM_MODE_COUNT];
|
||||
const unsigned int pcm_playback_channels[SND_FF_STREAM_MODE_COUNT];
|
||||
|
||||
|
@ -66,6 +72,7 @@ struct snd_ff {
|
|||
bool registered;
|
||||
struct delayed_work dwork;
|
||||
|
||||
enum snd_ff_unit_version unit_version;
|
||||
const struct snd_ff_spec *spec;
|
||||
|
||||
/* To handle MIDI tx. */
|
||||
|
|
|
@ -177,7 +177,7 @@ struct snd_efw_phys_meters {
|
|||
u32 in_meters;
|
||||
u32 reserved4;
|
||||
u32 reserved5;
|
||||
u32 values[0];
|
||||
u32 values[];
|
||||
} __packed;
|
||||
enum snd_efw_clock_source {
|
||||
SND_EFW_CLOCK_SOURCE_INTERNAL = 0,
|
||||
|
|
|
@ -76,15 +76,11 @@ int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
|
|||
if (i == ARRAY_SIZE(snd_motu_clock_rates))
|
||||
return -EINVAL;
|
||||
|
||||
pcm_chunks = formats->fixed_part_pcm_chunks[mode] +
|
||||
formats->differed_part_pcm_chunks[mode];
|
||||
// Each data block includes SPH in its head. Data chunks follow with
|
||||
// 3 byte alignment. Padding follows with zero to conform to quadlet
|
||||
// alignment.
|
||||
pcm_chunks = formats->pcm_chunks[mode];
|
||||
data_chunks = formats->msg_chunks + pcm_chunks;
|
||||
|
||||
/*
|
||||
* Each data block includes SPH in its head. Data chunks follow with
|
||||
* 3 byte alignment. Padding follows with zero to conform to quadlet
|
||||
* alignment.
|
||||
*/
|
||||
data_block_quadlets = 1 + DIV_ROUND_UP(data_chunks * 3, 4);
|
||||
|
||||
err = amdtp_stream_set_parameters(s, rate, data_block_quadlets);
|
||||
|
@ -440,7 +436,7 @@ static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
|
|||
|
||||
int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
enum amdtp_stream_direction dir,
|
||||
const struct snd_motu_protocol *const protocol)
|
||||
const struct snd_motu_spec *spec)
|
||||
{
|
||||
amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
|
||||
int fmt = CIP_FMT_MOTU;
|
||||
|
@ -454,14 +450,15 @@ int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
|
|||
* Units of version 3 transmits packets with invalid CIP header
|
||||
* against IEC 61883-1.
|
||||
*/
|
||||
if (protocol == &snd_motu_protocol_v3) {
|
||||
if (spec->protocol_version == SND_MOTU_PROTOCOL_V3) {
|
||||
flags |= CIP_WRONG_DBS |
|
||||
CIP_SKIP_DBC_ZERO_CHECK |
|
||||
CIP_HEADER_WITHOUT_EOH;
|
||||
fmt = CIP_FMT_MOTU_TX_V3;
|
||||
}
|
||||
|
||||
if (protocol == &snd_motu_protocol_v2) {
|
||||
if (spec == &snd_motu_spec_8pre ||
|
||||
spec == &snd_motu_spec_ultralite) {
|
||||
// 8pre has some quirks.
|
||||
flags |= CIP_WRONG_DBS |
|
||||
CIP_SKIP_DBC_ZERO_CHECK;
|
||||
|
|
|
@ -26,8 +26,7 @@ static int motu_rate_constraint(struct snd_pcm_hw_params *params,
|
|||
rate = snd_motu_clock_rates[i];
|
||||
mode = i / 2;
|
||||
|
||||
pcm_channels = formats->fixed_part_pcm_chunks[mode] +
|
||||
formats->differed_part_pcm_chunks[mode];
|
||||
pcm_channels = formats->pcm_chunks[mode];
|
||||
if (!snd_interval_test(c, pcm_channels))
|
||||
continue;
|
||||
|
||||
|
@ -59,8 +58,7 @@ static int motu_channels_constraint(struct snd_pcm_hw_params *params,
|
|||
if (!snd_interval_test(r, rate))
|
||||
continue;
|
||||
|
||||
pcm_channels = formats->fixed_part_pcm_chunks[mode] +
|
||||
formats->differed_part_pcm_chunks[mode];
|
||||
pcm_channels = formats->pcm_chunks[mode];
|
||||
channels.min = min(channels.min, pcm_channels);
|
||||
channels.max = max(channels.max, pcm_channels);
|
||||
}
|
||||
|
@ -82,8 +80,7 @@ static void limit_channels_and_rates(struct snd_motu *motu,
|
|||
rate = snd_motu_clock_rates[i];
|
||||
mode = i / 2;
|
||||
|
||||
pcm_channels = formats->fixed_part_pcm_chunks[mode] +
|
||||
formats->differed_part_pcm_chunks[mode];
|
||||
pcm_channels = formats->pcm_chunks[mode];
|
||||
if (pcm_channels == 0)
|
||||
continue;
|
||||
|
||||
|
@ -133,7 +130,6 @@ static int init_hw_info(struct snd_motu *motu,
|
|||
static int pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_motu *motu = substream->private_data;
|
||||
const struct snd_motu_protocol *const protocol = motu->spec->protocol;
|
||||
struct amdtp_domain *d = &motu->domain;
|
||||
enum snd_motu_clock_source src;
|
||||
int err;
|
||||
|
@ -152,7 +148,7 @@ static int pcm_open(struct snd_pcm_substream *substream)
|
|||
if (err < 0)
|
||||
goto err_locked;
|
||||
|
||||
err = protocol->get_clock_source(motu, &src);
|
||||
err = snd_motu_protocol_get_clock_source(motu, &src);
|
||||
if (err < 0)
|
||||
goto err_locked;
|
||||
|
||||
|
@ -166,7 +162,7 @@ static int pcm_open(struct snd_pcm_substream *substream)
|
|||
unsigned int frames_per_buffer = d->events_per_buffer;
|
||||
unsigned int rate;
|
||||
|
||||
err = protocol->get_clock_rate(motu, &rate);
|
||||
err = snd_motu_protocol_get_clock_rate(motu, &rate);
|
||||
if (err < 0)
|
||||
goto err_locked;
|
||||
|
||||
|
|
|
@ -28,13 +28,12 @@ static void proc_read_clock(struct snd_info_entry *entry,
|
|||
{
|
||||
|
||||
struct snd_motu *motu = entry->private_data;
|
||||
const struct snd_motu_protocol *const protocol = motu->spec->protocol;
|
||||
unsigned int rate;
|
||||
enum snd_motu_clock_source source;
|
||||
|
||||
if (protocol->get_clock_rate(motu, &rate) < 0)
|
||||
if (snd_motu_protocol_get_clock_rate(motu, &rate) < 0)
|
||||
return;
|
||||
if (protocol->get_clock_source(motu, &source) < 0)
|
||||
if (snd_motu_protocol_get_clock_source(motu, &source) < 0)
|
||||
return;
|
||||
|
||||
snd_iprintf(buffer, "Rate:\t%d\n", rate);
|
||||
|
@ -45,15 +44,14 @@ static void proc_read_format(struct snd_info_entry *entry,
|
|||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
struct snd_motu *motu = entry->private_data;
|
||||
const struct snd_motu_protocol *const protocol = motu->spec->protocol;
|
||||
unsigned int mode;
|
||||
struct snd_motu_packet_format *formats;
|
||||
int i;
|
||||
|
||||
if (protocol->cache_packet_formats(motu) < 0)
|
||||
if (snd_motu_protocol_cache_packet_formats(motu) < 0)
|
||||
return;
|
||||
|
||||
snd_iprintf(buffer, "tx:\tmsg\tfixed\tdiffered\n");
|
||||
snd_iprintf(buffer, "tx:\tmsg\tfixed\ttotal\n");
|
||||
for (i = 0; i < SND_MOTU_CLOCK_RATE_COUNT; ++i) {
|
||||
mode = i >> 1;
|
||||
|
||||
|
@ -62,11 +60,11 @@ static void proc_read_format(struct snd_info_entry *entry,
|
|||
"%u:\t%u\t%u\t%u\n",
|
||||
snd_motu_clock_rates[i],
|
||||
formats->msg_chunks,
|
||||
formats->fixed_part_pcm_chunks[mode],
|
||||
formats->differed_part_pcm_chunks[mode]);
|
||||
motu->spec->tx_fixed_pcm_chunks[mode],
|
||||
formats->pcm_chunks[mode]);
|
||||
}
|
||||
|
||||
snd_iprintf(buffer, "rx:\tmsg\tfixed\tdiffered\n");
|
||||
snd_iprintf(buffer, "rx:\tmsg\tfixed\ttotal\n");
|
||||
for (i = 0; i < SND_MOTU_CLOCK_RATE_COUNT; ++i) {
|
||||
mode = i >> 1;
|
||||
|
||||
|
@ -75,8 +73,8 @@ static void proc_read_format(struct snd_info_entry *entry,
|
|||
"%u:\t%u\t%u\t%u\n",
|
||||
snd_motu_clock_rates[i],
|
||||
formats->msg_chunks,
|
||||
formats->fixed_part_pcm_chunks[mode],
|
||||
formats->differed_part_pcm_chunks[mode]);
|
||||
motu->spec->rx_fixed_pcm_chunks[mode],
|
||||
formats->pcm_chunks[mode]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,8 @@ static int get_clock_rate(u32 data, unsigned int *rate)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int v2_get_clock_rate(struct snd_motu *motu, unsigned int *rate)
|
||||
int snd_motu_protocol_v2_get_clock_rate(struct snd_motu *motu,
|
||||
unsigned int *rate)
|
||||
{
|
||||
__be32 reg;
|
||||
int err;
|
||||
|
@ -48,7 +49,8 @@ static int v2_get_clock_rate(struct snd_motu *motu, unsigned int *rate)
|
|||
return get_clock_rate(be32_to_cpu(reg), rate);
|
||||
}
|
||||
|
||||
static int v2_set_clock_rate(struct snd_motu *motu, unsigned int rate)
|
||||
int snd_motu_protocol_v2_set_clock_rate(struct snd_motu *motu,
|
||||
unsigned int rate)
|
||||
{
|
||||
__be32 reg;
|
||||
u32 data;
|
||||
|
@ -76,14 +78,10 @@ static int v2_set_clock_rate(struct snd_motu *motu, unsigned int rate)
|
|||
sizeof(reg));
|
||||
}
|
||||
|
||||
static int get_clock_source(struct snd_motu *motu, u32 data,
|
||||
enum snd_motu_clock_source *src)
|
||||
static int detect_clock_source_optical_model(struct snd_motu *motu, u32 data,
|
||||
enum snd_motu_clock_source *src)
|
||||
{
|
||||
unsigned int index = data & V2_CLOCK_SRC_MASK;
|
||||
if (index > 5)
|
||||
return -EIO;
|
||||
|
||||
switch (index) {
|
||||
switch (data) {
|
||||
case 0:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
|
||||
break;
|
||||
|
@ -116,14 +114,50 @@ static int get_clock_source(struct snd_motu *motu, u32 data,
|
|||
*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB;
|
||||
break;
|
||||
default:
|
||||
return -EIO;
|
||||
*src = SND_MOTU_CLOCK_SOURCE_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int v2_get_clock_source(struct snd_motu *motu,
|
||||
enum snd_motu_clock_source *src)
|
||||
static int v2_detect_clock_source(struct snd_motu *motu, u32 data,
|
||||
enum snd_motu_clock_source *src)
|
||||
{
|
||||
switch (data) {
|
||||
case 0:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
|
||||
break;
|
||||
case 2:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
|
||||
break;
|
||||
case 3:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_SPH;
|
||||
break;
|
||||
case 4:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
|
||||
break;
|
||||
default:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_clock_source(struct snd_motu *motu, u32 data,
|
||||
enum snd_motu_clock_source *src)
|
||||
{
|
||||
data &= V2_CLOCK_SRC_MASK;
|
||||
if (motu->spec == &snd_motu_spec_828mk2 ||
|
||||
motu->spec == &snd_motu_spec_traveler)
|
||||
return detect_clock_source_optical_model(motu, data, src);
|
||||
else
|
||||
return v2_detect_clock_source(motu, data, src);
|
||||
}
|
||||
|
||||
int snd_motu_protocol_v2_get_clock_source(struct snd_motu *motu,
|
||||
enum snd_motu_clock_source *src)
|
||||
{
|
||||
__be32 reg;
|
||||
int err;
|
||||
|
@ -136,167 +170,189 @@ static int v2_get_clock_source(struct snd_motu *motu,
|
|||
return get_clock_source(motu, be32_to_cpu(reg), src);
|
||||
}
|
||||
|
||||
static int v2_switch_fetching_mode(struct snd_motu *motu, bool enable)
|
||||
// Expected for Traveler and 896HD, which implements Altera Cyclone EP1C3.
|
||||
static int switch_fetching_mode_cyclone(struct snd_motu *motu, u32 *data,
|
||||
bool enable)
|
||||
{
|
||||
*data |= V2_CLOCK_MODEL_SPECIFIC;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// For UltraLite and 8pre, which implements Xilinx Spartan XC3S200.
|
||||
static int switch_fetching_mode_spartan(struct snd_motu *motu, u32 *data,
|
||||
bool enable)
|
||||
{
|
||||
unsigned int rate;
|
||||
enum snd_motu_clock_source src;
|
||||
__be32 reg;
|
||||
u32 data;
|
||||
int err = 0;
|
||||
int err;
|
||||
|
||||
// 828mkII implements Altera ACEX 1K EP1K30. Nothing to do.
|
||||
if (motu->spec == &snd_motu_spec_828mk2)
|
||||
err = get_clock_source(motu, *data, &src);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = get_clock_rate(*data, &rate);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (src == SND_MOTU_CLOCK_SOURCE_SPH && rate > 48000)
|
||||
*data |= V2_CLOCK_MODEL_SPECIFIC;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_motu_protocol_v2_switch_fetching_mode(struct snd_motu *motu,
|
||||
bool enable)
|
||||
{
|
||||
if (motu->spec == &snd_motu_spec_828mk2) {
|
||||
// 828mkII implements Altera ACEX 1K EP1K30. Nothing to do.
|
||||
return 0;
|
||||
|
||||
err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, ®,
|
||||
sizeof(reg));
|
||||
if (err < 0)
|
||||
return err;
|
||||
data = be32_to_cpu(reg);
|
||||
|
||||
err = get_clock_source(motu, data, &src);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
data &= ~(V2_CLOCK_FETCH_ENABLE | V2_CLOCK_MODEL_SPECIFIC);
|
||||
if (enable)
|
||||
data |= V2_CLOCK_FETCH_ENABLE;
|
||||
|
||||
if (motu->spec->flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4) {
|
||||
// Expected for Traveler and 896HD, which implements Altera
|
||||
// Cyclone EP1C3.
|
||||
data |= V2_CLOCK_MODEL_SPECIFIC;
|
||||
} else {
|
||||
// For UltraLite and 8pre, which implements Xilinx Spartan
|
||||
// XC3S200.
|
||||
unsigned int rate;
|
||||
__be32 reg;
|
||||
u32 data;
|
||||
int err;
|
||||
|
||||
err = get_clock_rate(data, &rate);
|
||||
err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET,
|
||||
®, sizeof(reg));
|
||||
if (err < 0)
|
||||
return err;
|
||||
data = be32_to_cpu(reg);
|
||||
|
||||
data &= ~(V2_CLOCK_FETCH_ENABLE | V2_CLOCK_MODEL_SPECIFIC);
|
||||
if (enable)
|
||||
data |= V2_CLOCK_FETCH_ENABLE;
|
||||
|
||||
if (motu->spec == &snd_motu_spec_traveler)
|
||||
err = switch_fetching_mode_cyclone(motu, &data, enable);
|
||||
else
|
||||
err = switch_fetching_mode_spartan(motu, &data, enable);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (src == SND_MOTU_CLOCK_SOURCE_SPH && rate > 48000)
|
||||
data |= V2_CLOCK_MODEL_SPECIFIC;
|
||||
reg = cpu_to_be32(data);
|
||||
return snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET,
|
||||
®, sizeof(reg));
|
||||
}
|
||||
|
||||
reg = cpu_to_be32(data);
|
||||
return snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET, ®,
|
||||
sizeof(reg));
|
||||
}
|
||||
|
||||
static void calculate_fixed_part(struct snd_motu_packet_format *formats,
|
||||
enum amdtp_stream_direction dir,
|
||||
enum snd_motu_spec_flags flags,
|
||||
unsigned char analog_ports)
|
||||
static int detect_packet_formats_828mk2(struct snd_motu *motu, u32 data)
|
||||
{
|
||||
unsigned char pcm_chunks[3] = {0, 0, 0};
|
||||
|
||||
formats->msg_chunks = 2;
|
||||
|
||||
pcm_chunks[0] = analog_ports;
|
||||
pcm_chunks[1] = analog_ports;
|
||||
if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
|
||||
pcm_chunks[2] = analog_ports;
|
||||
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
if (flags & SND_MOTU_SPEC_TX_MICINST_CHUNK) {
|
||||
pcm_chunks[0] += 2;
|
||||
pcm_chunks[1] += 2;
|
||||
}
|
||||
if (flags & SND_MOTU_SPEC_TX_RETURN_CHUNK) {
|
||||
pcm_chunks[0] += 2;
|
||||
pcm_chunks[1] += 2;
|
||||
}
|
||||
} else {
|
||||
if (flags & SND_MOTU_SPEC_RX_SEPARATED_MAIN) {
|
||||
pcm_chunks[0] += 2;
|
||||
pcm_chunks[1] += 2;
|
||||
}
|
||||
|
||||
// Packets to v2 units include 2 chunks for phone 1/2, except
|
||||
// for 176.4/192.0 kHz.
|
||||
pcm_chunks[0] += 2;
|
||||
pcm_chunks[1] += 2;
|
||||
if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) ==
|
||||
V2_OPT_IFACE_MODE_ADAT) {
|
||||
motu->tx_packet_formats.pcm_chunks[0] += 8;
|
||||
motu->tx_packet_formats.pcm_chunks[1] += 4;
|
||||
}
|
||||
|
||||
if (flags & SND_MOTU_SPEC_HAS_AESEBU_IFACE) {
|
||||
pcm_chunks[0] += 2;
|
||||
pcm_chunks[1] += 2;
|
||||
if (((data & V2_OPT_OUT_IFACE_MASK) >> V2_OPT_OUT_IFACE_SHIFT) ==
|
||||
V2_OPT_IFACE_MODE_ADAT) {
|
||||
motu->rx_packet_formats.pcm_chunks[0] += 8;
|
||||
motu->rx_packet_formats.pcm_chunks[1] += 4;
|
||||
}
|
||||
|
||||
/*
|
||||
* All of v2 models have a pair of coaxial interfaces for digital in/out
|
||||
* port. At 44.1/48.0/88.2/96.0 kHz, packets includes PCM from these
|
||||
* ports.
|
||||
*/
|
||||
pcm_chunks[0] += 2;
|
||||
pcm_chunks[1] += 2;
|
||||
|
||||
formats->fixed_part_pcm_chunks[0] = pcm_chunks[0];
|
||||
formats->fixed_part_pcm_chunks[1] = pcm_chunks[1];
|
||||
formats->fixed_part_pcm_chunks[2] = pcm_chunks[2];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void calculate_differed_part(struct snd_motu_packet_format *formats,
|
||||
enum snd_motu_spec_flags flags,
|
||||
u32 data, u32 mask, u32 shift)
|
||||
static int detect_packet_formats_traveler(struct snd_motu *motu, u32 data)
|
||||
{
|
||||
unsigned char pcm_chunks[2] = {0, 0};
|
||||
|
||||
/*
|
||||
* When optical interfaces are configured for S/PDIF (TOSLINK),
|
||||
* the above PCM frames come from them, instead of coaxial
|
||||
* interfaces.
|
||||
*/
|
||||
data = (data & mask) >> shift;
|
||||
if (data == V2_OPT_IFACE_MODE_ADAT) {
|
||||
if (flags & SND_MOTU_SPEC_HAS_OPT_IFACE_A) {
|
||||
pcm_chunks[0] += 8;
|
||||
pcm_chunks[1] += 4;
|
||||
}
|
||||
// 8pre has two sets of optical interface and doesn't reduce
|
||||
// chunks for ADAT signals.
|
||||
if (flags & SND_MOTU_SPEC_HAS_OPT_IFACE_B) {
|
||||
pcm_chunks[1] += 4;
|
||||
}
|
||||
if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) ==
|
||||
V2_OPT_IFACE_MODE_ADAT) {
|
||||
motu->tx_packet_formats.pcm_chunks[0] += 8;
|
||||
motu->tx_packet_formats.pcm_chunks[1] += 4;
|
||||
}
|
||||
|
||||
/* At mode x4, no data chunks are supported in this part. */
|
||||
formats->differed_part_pcm_chunks[0] = pcm_chunks[0];
|
||||
formats->differed_part_pcm_chunks[1] = pcm_chunks[1];
|
||||
if (((data & V2_OPT_OUT_IFACE_MASK) >> V2_OPT_OUT_IFACE_SHIFT) ==
|
||||
V2_OPT_IFACE_MODE_ADAT) {
|
||||
motu->rx_packet_formats.pcm_chunks[0] += 8;
|
||||
motu->rx_packet_formats.pcm_chunks[1] += 4;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int v2_cache_packet_formats(struct snd_motu *motu)
|
||||
static int detect_packet_formats_8pre(struct snd_motu *motu, u32 data)
|
||||
{
|
||||
if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) ==
|
||||
V2_OPT_IFACE_MODE_ADAT) {
|
||||
motu->tx_packet_formats.pcm_chunks[0] += 8;
|
||||
motu->tx_packet_formats.pcm_chunks[1] += 8;
|
||||
}
|
||||
|
||||
if (((data & V2_OPT_OUT_IFACE_MASK) >> V2_OPT_OUT_IFACE_SHIFT) ==
|
||||
V2_OPT_IFACE_MODE_ADAT) {
|
||||
motu->rx_packet_formats.pcm_chunks[0] += 8;
|
||||
motu->rx_packet_formats.pcm_chunks[1] += 8;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_motu_protocol_v2_cache_packet_formats(struct snd_motu *motu)
|
||||
{
|
||||
__be32 reg;
|
||||
u32 data;
|
||||
int err;
|
||||
|
||||
motu->tx_packet_formats.pcm_byte_offset = 10;
|
||||
motu->rx_packet_formats.pcm_byte_offset = 10;
|
||||
|
||||
motu->tx_packet_formats.msg_chunks = 2;
|
||||
motu->rx_packet_formats.msg_chunks = 2;
|
||||
|
||||
err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, ®,
|
||||
sizeof(reg));
|
||||
if (err < 0)
|
||||
return err;
|
||||
data = be32_to_cpu(reg);
|
||||
|
||||
calculate_fixed_part(&motu->tx_packet_formats, AMDTP_IN_STREAM,
|
||||
motu->spec->flags, motu->spec->analog_in_ports);
|
||||
calculate_differed_part(&motu->tx_packet_formats, motu->spec->flags,
|
||||
data, V2_OPT_IN_IFACE_MASK, V2_OPT_IN_IFACE_SHIFT);
|
||||
memcpy(motu->tx_packet_formats.pcm_chunks,
|
||||
motu->spec->tx_fixed_pcm_chunks,
|
||||
sizeof(motu->tx_packet_formats.pcm_chunks));
|
||||
memcpy(motu->rx_packet_formats.pcm_chunks,
|
||||
motu->spec->rx_fixed_pcm_chunks,
|
||||
sizeof(motu->rx_packet_formats.pcm_chunks));
|
||||
|
||||
calculate_fixed_part(&motu->rx_packet_formats, AMDTP_OUT_STREAM,
|
||||
motu->spec->flags, motu->spec->analog_out_ports);
|
||||
calculate_differed_part(&motu->rx_packet_formats, motu->spec->flags,
|
||||
data, V2_OPT_OUT_IFACE_MASK, V2_OPT_OUT_IFACE_SHIFT);
|
||||
|
||||
motu->tx_packet_formats.pcm_byte_offset = 10;
|
||||
motu->rx_packet_formats.pcm_byte_offset = 10;
|
||||
|
||||
return 0;
|
||||
if (motu->spec == &snd_motu_spec_828mk2)
|
||||
return detect_packet_formats_828mk2(motu, data);
|
||||
else if (motu->spec == &snd_motu_spec_traveler)
|
||||
return detect_packet_formats_traveler(motu, data);
|
||||
else if (motu->spec == &snd_motu_spec_8pre)
|
||||
return detect_packet_formats_8pre(motu, data);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct snd_motu_protocol snd_motu_protocol_v2 = {
|
||||
.get_clock_rate = v2_get_clock_rate,
|
||||
.set_clock_rate = v2_set_clock_rate,
|
||||
.get_clock_source = v2_get_clock_source,
|
||||
.switch_fetching_mode = v2_switch_fetching_mode,
|
||||
.cache_packet_formats = v2_cache_packet_formats,
|
||||
const struct snd_motu_spec snd_motu_spec_828mk2 = {
|
||||
.name = "828mk2",
|
||||
.protocol_version = SND_MOTU_PROTOCOL_V2,
|
||||
.flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
|
||||
SND_MOTU_SPEC_TX_MIDI_2ND_Q,
|
||||
.tx_fixed_pcm_chunks = {14, 14, 0},
|
||||
.rx_fixed_pcm_chunks = {14, 14, 0},
|
||||
};
|
||||
|
||||
const struct snd_motu_spec snd_motu_spec_traveler = {
|
||||
.name = "Traveler",
|
||||
.protocol_version = SND_MOTU_PROTOCOL_V2,
|
||||
.flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
|
||||
SND_MOTU_SPEC_TX_MIDI_2ND_Q,
|
||||
.tx_fixed_pcm_chunks = {14, 14, 8},
|
||||
.rx_fixed_pcm_chunks = {14, 14, 8},
|
||||
};
|
||||
|
||||
const struct snd_motu_spec snd_motu_spec_ultralite = {
|
||||
.name = "UltraLite",
|
||||
.protocol_version = SND_MOTU_PROTOCOL_V2,
|
||||
.flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
|
||||
SND_MOTU_SPEC_TX_MIDI_2ND_Q,
|
||||
.tx_fixed_pcm_chunks = {14, 14, 0},
|
||||
.rx_fixed_pcm_chunks = {14, 14, 0},
|
||||
};
|
||||
|
||||
const struct snd_motu_spec snd_motu_spec_8pre = {
|
||||
.name = "8pre",
|
||||
.protocol_version = SND_MOTU_PROTOCOL_V2,
|
||||
.flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
|
||||
SND_MOTU_SPEC_TX_MIDI_2ND_Q,
|
||||
.tx_fixed_pcm_chunks = {10, 6, 0},
|
||||
.rx_fixed_pcm_chunks = {10, 6, 0},
|
||||
};
|
||||
|
|
|
@ -24,7 +24,8 @@
|
|||
#define V3_NO_ADAT_OPT_OUT_IFACE_A 0x00040000
|
||||
#define V3_NO_ADAT_OPT_OUT_IFACE_B 0x00400000
|
||||
|
||||
static int v3_get_clock_rate(struct snd_motu *motu, unsigned int *rate)
|
||||
int snd_motu_protocol_v3_get_clock_rate(struct snd_motu *motu,
|
||||
unsigned int *rate)
|
||||
{
|
||||
__be32 reg;
|
||||
u32 data;
|
||||
|
@ -45,7 +46,8 @@ static int v3_get_clock_rate(struct snd_motu *motu, unsigned int *rate)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int v3_set_clock_rate(struct snd_motu *motu, unsigned int rate)
|
||||
int snd_motu_protocol_v3_set_clock_rate(struct snd_motu *motu,
|
||||
unsigned int rate)
|
||||
{
|
||||
__be32 reg;
|
||||
u32 data;
|
||||
|
@ -85,55 +87,102 @@ static int v3_set_clock_rate(struct snd_motu *motu, unsigned int rate)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int v3_get_clock_source(struct snd_motu *motu,
|
||||
enum snd_motu_clock_source *src)
|
||||
static int detect_clock_source_828mk3(struct snd_motu *motu, u32 data,
|
||||
enum snd_motu_clock_source *src)
|
||||
{
|
||||
switch (data) {
|
||||
case 0x00:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
|
||||
break;
|
||||
case 0x01:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
|
||||
break;
|
||||
case 0x02:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_SPH;
|
||||
break;
|
||||
case 0x10:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
|
||||
break;
|
||||
case 0x18:
|
||||
case 0x19:
|
||||
{
|
||||
__be32 reg;
|
||||
u32 options;
|
||||
int err;
|
||||
|
||||
err = snd_motu_transaction_read(motu,
|
||||
V3_OPT_IFACE_MODE_OFFSET, ®, sizeof(reg));
|
||||
if (err < 0)
|
||||
return err;
|
||||
options = be32_to_cpu(reg);
|
||||
|
||||
if (data == 0x18) {
|
||||
if (options & V3_NO_ADAT_OPT_IN_IFACE_A)
|
||||
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_A;
|
||||
else
|
||||
*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_A;
|
||||
} else {
|
||||
if (options & V3_NO_ADAT_OPT_IN_IFACE_B)
|
||||
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_B;
|
||||
else
|
||||
*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_B;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int v3_detect_clock_source(struct snd_motu *motu, u32 data,
|
||||
enum snd_motu_clock_source *src)
|
||||
{
|
||||
switch (data) {
|
||||
case 0x00:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
|
||||
break;
|
||||
case 0x01:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
|
||||
break;
|
||||
case 0x02:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_SPH;
|
||||
break;
|
||||
case 0x10:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
|
||||
break;
|
||||
default:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_motu_protocol_v3_get_clock_source(struct snd_motu *motu,
|
||||
enum snd_motu_clock_source *src)
|
||||
{
|
||||
__be32 reg;
|
||||
u32 data;
|
||||
unsigned int val;
|
||||
int err;
|
||||
|
||||
err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, ®,
|
||||
sizeof(reg));
|
||||
if (err < 0)
|
||||
return err;
|
||||
data = be32_to_cpu(reg);
|
||||
data = be32_to_cpu(reg) & V3_CLOCK_SOURCE_MASK;
|
||||
|
||||
val = data & V3_CLOCK_SOURCE_MASK;
|
||||
if (val == 0x00) {
|
||||
*src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
|
||||
} else if (val == 0x01) {
|
||||
*src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
|
||||
} else if (val == 0x02) {
|
||||
*src = SND_MOTU_CLOCK_SOURCE_SPH;
|
||||
} else if (val == 0x10) {
|
||||
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
|
||||
} else if (val == 0x18 || val == 0x19) {
|
||||
err = snd_motu_transaction_read(motu, V3_OPT_IFACE_MODE_OFFSET,
|
||||
®, sizeof(reg));
|
||||
if (err < 0)
|
||||
return err;
|
||||
data = be32_to_cpu(reg);
|
||||
|
||||
if (val == 0x18) {
|
||||
if (data & V3_NO_ADAT_OPT_IN_IFACE_A)
|
||||
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_A;
|
||||
else
|
||||
*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_A;
|
||||
} else {
|
||||
if (data & V3_NO_ADAT_OPT_IN_IFACE_B)
|
||||
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_B;
|
||||
else
|
||||
*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_B;
|
||||
}
|
||||
} else {
|
||||
*src = SND_MOTU_CLOCK_SOURCE_UNKNOWN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
if (motu->spec == &snd_motu_spec_828mk3)
|
||||
return detect_clock_source_828mk3(motu, data, src);
|
||||
else
|
||||
return v3_detect_clock_source(motu, data, src);
|
||||
}
|
||||
|
||||
static int v3_switch_fetching_mode(struct snd_motu *motu, bool enable)
|
||||
int snd_motu_protocol_v3_switch_fetching_mode(struct snd_motu *motu,
|
||||
bool enable)
|
||||
{
|
||||
__be32 reg;
|
||||
u32 data;
|
||||
|
@ -155,162 +204,113 @@ static int v3_switch_fetching_mode(struct snd_motu *motu, bool enable)
|
|||
sizeof(reg));
|
||||
}
|
||||
|
||||
static void calculate_fixed_part(struct snd_motu_packet_format *formats,
|
||||
enum amdtp_stream_direction dir,
|
||||
enum snd_motu_spec_flags flags,
|
||||
unsigned char analog_ports)
|
||||
static int detect_packet_formats_828mk3(struct snd_motu *motu, u32 data)
|
||||
{
|
||||
unsigned char pcm_chunks[3] = {0, 0, 0};
|
||||
|
||||
formats->msg_chunks = 2;
|
||||
|
||||
pcm_chunks[0] = analog_ports;
|
||||
pcm_chunks[1] = analog_ports;
|
||||
if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
|
||||
pcm_chunks[2] = analog_ports;
|
||||
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
if (flags & SND_MOTU_SPEC_TX_MICINST_CHUNK) {
|
||||
pcm_chunks[0] += 2;
|
||||
pcm_chunks[1] += 2;
|
||||
if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
|
||||
pcm_chunks[2] += 2;
|
||||
if (data & V3_ENABLE_OPT_IN_IFACE_A) {
|
||||
if (data & V3_NO_ADAT_OPT_IN_IFACE_A) {
|
||||
motu->tx_packet_formats.pcm_chunks[0] += 4;
|
||||
motu->tx_packet_formats.pcm_chunks[1] += 4;
|
||||
} else {
|
||||
motu->tx_packet_formats.pcm_chunks[0] += 8;
|
||||
motu->tx_packet_formats.pcm_chunks[1] += 4;
|
||||
}
|
||||
|
||||
if (flags & SND_MOTU_SPEC_TX_RETURN_CHUNK) {
|
||||
pcm_chunks[0] += 2;
|
||||
pcm_chunks[1] += 2;
|
||||
if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
|
||||
pcm_chunks[2] += 2;
|
||||
}
|
||||
|
||||
if (flags & SND_MOTU_SPEC_TX_REVERB_CHUNK) {
|
||||
pcm_chunks[0] += 2;
|
||||
pcm_chunks[1] += 2;
|
||||
}
|
||||
} else {
|
||||
if (flags & SND_MOTU_SPEC_RX_SEPARATED_MAIN) {
|
||||
pcm_chunks[0] += 2;
|
||||
pcm_chunks[1] += 2;
|
||||
}
|
||||
|
||||
// Packets to v3 units include 2 chunks for phone 1/2, except
|
||||
// for 176.4/192.0 kHz.
|
||||
pcm_chunks[0] += 2;
|
||||
pcm_chunks[1] += 2;
|
||||
}
|
||||
|
||||
if (flags & SND_MOTU_SPEC_HAS_AESEBU_IFACE) {
|
||||
pcm_chunks[0] += 2;
|
||||
pcm_chunks[1] += 2;
|
||||
if (data & V3_ENABLE_OPT_IN_IFACE_B) {
|
||||
if (data & V3_NO_ADAT_OPT_IN_IFACE_B) {
|
||||
motu->tx_packet_formats.pcm_chunks[0] += 4;
|
||||
motu->tx_packet_formats.pcm_chunks[1] += 4;
|
||||
} else {
|
||||
motu->tx_packet_formats.pcm_chunks[0] += 8;
|
||||
motu->tx_packet_formats.pcm_chunks[1] += 4;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* At least, packets have two data chunks for S/PDIF on coaxial
|
||||
* interface.
|
||||
*/
|
||||
pcm_chunks[0] += 2;
|
||||
pcm_chunks[1] += 2;
|
||||
if (data & V3_ENABLE_OPT_OUT_IFACE_A) {
|
||||
if (data & V3_NO_ADAT_OPT_OUT_IFACE_A) {
|
||||
motu->rx_packet_formats.pcm_chunks[0] += 4;
|
||||
motu->rx_packet_formats.pcm_chunks[1] += 4;
|
||||
} else {
|
||||
motu->rx_packet_formats.pcm_chunks[0] += 8;
|
||||
motu->rx_packet_formats.pcm_chunks[1] += 4;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Fixed part consists of PCM chunks multiple of 4, with msg chunks. As
|
||||
* a result, this part can includes empty data chunks.
|
||||
*/
|
||||
formats->fixed_part_pcm_chunks[0] = round_up(2 + pcm_chunks[0], 4) - 2;
|
||||
formats->fixed_part_pcm_chunks[1] = round_up(2 + pcm_chunks[1], 4) - 2;
|
||||
if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
|
||||
formats->fixed_part_pcm_chunks[2] =
|
||||
round_up(2 + pcm_chunks[2], 4) - 2;
|
||||
if (data & V3_ENABLE_OPT_OUT_IFACE_B) {
|
||||
if (data & V3_NO_ADAT_OPT_OUT_IFACE_B) {
|
||||
motu->rx_packet_formats.pcm_chunks[0] += 4;
|
||||
motu->rx_packet_formats.pcm_chunks[1] += 4;
|
||||
} else {
|
||||
motu->rx_packet_formats.pcm_chunks[0] += 8;
|
||||
motu->rx_packet_formats.pcm_chunks[1] += 4;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void calculate_differed_part(struct snd_motu_packet_format *formats,
|
||||
enum snd_motu_spec_flags flags, u32 data,
|
||||
u32 a_enable_mask, u32 a_no_adat_mask,
|
||||
u32 b_enable_mask, u32 b_no_adat_mask)
|
||||
{
|
||||
unsigned char pcm_chunks[3] = {0, 0, 0};
|
||||
int i;
|
||||
|
||||
if ((flags & SND_MOTU_SPEC_HAS_OPT_IFACE_A) && (data & a_enable_mask)) {
|
||||
if (data & a_no_adat_mask) {
|
||||
/*
|
||||
* Additional two data chunks for S/PDIF on optical
|
||||
* interface A. This includes empty data chunks.
|
||||
*/
|
||||
pcm_chunks[0] += 4;
|
||||
pcm_chunks[1] += 4;
|
||||
} else {
|
||||
/*
|
||||
* Additional data chunks for ADAT on optical interface
|
||||
* A.
|
||||
*/
|
||||
pcm_chunks[0] += 8;
|
||||
pcm_chunks[1] += 4;
|
||||
}
|
||||
}
|
||||
|
||||
if ((flags & SND_MOTU_SPEC_HAS_OPT_IFACE_B) && (data & b_enable_mask)) {
|
||||
if (data & b_no_adat_mask) {
|
||||
/*
|
||||
* Additional two data chunks for S/PDIF on optical
|
||||
* interface B. This includes empty data chunks.
|
||||
*/
|
||||
pcm_chunks[0] += 4;
|
||||
pcm_chunks[1] += 4;
|
||||
} else {
|
||||
/*
|
||||
* Additional data chunks for ADAT on optical interface
|
||||
* B.
|
||||
*/
|
||||
pcm_chunks[0] += 8;
|
||||
pcm_chunks[1] += 4;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 3; ++i) {
|
||||
if (pcm_chunks[i] > 0)
|
||||
pcm_chunks[i] = round_up(pcm_chunks[i], 4);
|
||||
|
||||
formats->differed_part_pcm_chunks[i] = pcm_chunks[i];
|
||||
}
|
||||
}
|
||||
|
||||
static int v3_cache_packet_formats(struct snd_motu *motu)
|
||||
int snd_motu_protocol_v3_cache_packet_formats(struct snd_motu *motu)
|
||||
{
|
||||
__be32 reg;
|
||||
u32 data;
|
||||
int err;
|
||||
|
||||
motu->tx_packet_formats.pcm_byte_offset = 10;
|
||||
motu->rx_packet_formats.pcm_byte_offset = 10;
|
||||
|
||||
motu->tx_packet_formats.msg_chunks = 2;
|
||||
motu->rx_packet_formats.msg_chunks = 2;
|
||||
|
||||
err = snd_motu_transaction_read(motu, V3_OPT_IFACE_MODE_OFFSET, ®,
|
||||
sizeof(reg));
|
||||
if (err < 0)
|
||||
return err;
|
||||
data = be32_to_cpu(reg);
|
||||
|
||||
calculate_fixed_part(&motu->tx_packet_formats, AMDTP_IN_STREAM,
|
||||
motu->spec->flags, motu->spec->analog_in_ports);
|
||||
calculate_differed_part(&motu->tx_packet_formats,
|
||||
motu->spec->flags, data,
|
||||
V3_ENABLE_OPT_IN_IFACE_A, V3_NO_ADAT_OPT_IN_IFACE_A,
|
||||
V3_ENABLE_OPT_IN_IFACE_B, V3_NO_ADAT_OPT_IN_IFACE_B);
|
||||
memcpy(motu->tx_packet_formats.pcm_chunks,
|
||||
motu->spec->tx_fixed_pcm_chunks,
|
||||
sizeof(motu->tx_packet_formats.pcm_chunks));
|
||||
memcpy(motu->rx_packet_formats.pcm_chunks,
|
||||
motu->spec->rx_fixed_pcm_chunks,
|
||||
sizeof(motu->rx_packet_formats.pcm_chunks));
|
||||
|
||||
calculate_fixed_part(&motu->rx_packet_formats, AMDTP_OUT_STREAM,
|
||||
motu->spec->flags, motu->spec->analog_out_ports);
|
||||
calculate_differed_part(&motu->rx_packet_formats,
|
||||
motu->spec->flags, data,
|
||||
V3_ENABLE_OPT_OUT_IFACE_A, V3_NO_ADAT_OPT_OUT_IFACE_A,
|
||||
V3_ENABLE_OPT_OUT_IFACE_B, V3_NO_ADAT_OPT_OUT_IFACE_B);
|
||||
|
||||
motu->tx_packet_formats.pcm_byte_offset = 10;
|
||||
motu->rx_packet_formats.pcm_byte_offset = 10;
|
||||
|
||||
return 0;
|
||||
if (motu->spec == &snd_motu_spec_828mk3)
|
||||
return detect_packet_formats_828mk3(motu, data);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct snd_motu_protocol snd_motu_protocol_v3 = {
|
||||
.get_clock_rate = v3_get_clock_rate,
|
||||
.set_clock_rate = v3_set_clock_rate,
|
||||
.get_clock_source = v3_get_clock_source,
|
||||
.switch_fetching_mode = v3_switch_fetching_mode,
|
||||
.cache_packet_formats = v3_cache_packet_formats,
|
||||
|
||||
const struct snd_motu_spec snd_motu_spec_828mk3 = {
|
||||
.name = "828mk3",
|
||||
.protocol_version = SND_MOTU_PROTOCOL_V3,
|
||||
.flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q |
|
||||
SND_MOTU_SPEC_TX_MIDI_3RD_Q,
|
||||
.tx_fixed_pcm_chunks = {18, 18, 14},
|
||||
.rx_fixed_pcm_chunks = {14, 14, 10},
|
||||
};
|
||||
|
||||
const struct snd_motu_spec snd_motu_spec_ultralite_mk3 = {
|
||||
.name = "UltraLiteMk3",
|
||||
.protocol_version = SND_MOTU_PROTOCOL_V3,
|
||||
.flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q |
|
||||
SND_MOTU_SPEC_TX_MIDI_3RD_Q,
|
||||
.tx_fixed_pcm_chunks = {18, 14, 10},
|
||||
.rx_fixed_pcm_chunks = {14, 14, 14},
|
||||
};
|
||||
|
||||
const struct snd_motu_spec snd_motu_spec_audio_express = {
|
||||
.name = "AudioExpress",
|
||||
.protocol_version = SND_MOTU_PROTOCOL_V3,
|
||||
.flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
|
||||
SND_MOTU_SPEC_TX_MIDI_3RD_Q,
|
||||
.tx_fixed_pcm_chunks = {10, 10, 0},
|
||||
.rx_fixed_pcm_chunks = {10, 10, 0},
|
||||
};
|
||||
|
||||
const struct snd_motu_spec snd_motu_spec_4pre = {
|
||||
.name = "4pre",
|
||||
.protocol_version = SND_MOTU_PROTOCOL_V3,
|
||||
.tx_fixed_pcm_chunks = {10, 10, 0},
|
||||
.rx_fixed_pcm_chunks = {10, 10, 0},
|
||||
};
|
||||
|
|
|
@ -88,7 +88,7 @@ static void finish_session(struct snd_motu *motu)
|
|||
u32 data;
|
||||
int err;
|
||||
|
||||
err = motu->spec->protocol->switch_fetching_mode(motu, false);
|
||||
err = snd_motu_protocol_switch_fetching_mode(motu, false);
|
||||
if (err < 0)
|
||||
return;
|
||||
|
||||
|
@ -110,7 +110,7 @@ int snd_motu_stream_cache_packet_formats(struct snd_motu *motu)
|
|||
{
|
||||
int err;
|
||||
|
||||
err = motu->spec->protocol->cache_packet_formats(motu);
|
||||
err = snd_motu_protocol_cache_packet_formats(motu);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@ -140,7 +140,7 @@ int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate,
|
|||
unsigned int curr_rate;
|
||||
int err;
|
||||
|
||||
err = motu->spec->protocol->get_clock_rate(motu, &curr_rate);
|
||||
err = snd_motu_protocol_get_clock_rate(motu, &curr_rate);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (rate == 0)
|
||||
|
@ -153,7 +153,7 @@ int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate,
|
|||
fw_iso_resources_free(&motu->tx_resources);
|
||||
fw_iso_resources_free(&motu->rx_resources);
|
||||
|
||||
err = motu->spec->protocol->set_clock_rate(motu, rate);
|
||||
err = snd_motu_protocol_set_clock_rate(motu, rate);
|
||||
if (err < 0) {
|
||||
dev_err(&motu->unit->device,
|
||||
"fail to set sampling rate: %d\n", err);
|
||||
|
@ -201,9 +201,9 @@ static int ensure_packet_formats(struct snd_motu *motu)
|
|||
data &= ~(TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS |
|
||||
RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS|
|
||||
TX_PACKET_TRANSMISSION_SPEED_MASK);
|
||||
if (motu->tx_packet_formats.differed_part_pcm_chunks[0] == 0)
|
||||
if (motu->spec->tx_fixed_pcm_chunks[0] == motu->tx_packet_formats.pcm_chunks[0])
|
||||
data |= TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS;
|
||||
if (motu->rx_packet_formats.differed_part_pcm_chunks[0] == 0)
|
||||
if (motu->spec->rx_fixed_pcm_chunks[0] == motu->rx_packet_formats.pcm_chunks[0])
|
||||
data |= RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS;
|
||||
data |= fw_parent_device(motu->unit)->max_speed;
|
||||
|
||||
|
@ -272,7 +272,7 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu)
|
|||
goto stop_streams;
|
||||
}
|
||||
|
||||
err = motu->spec->protocol->switch_fetching_mode(motu, true);
|
||||
err = snd_motu_protocol_switch_fetching_mode(motu, true);
|
||||
if (err < 0) {
|
||||
dev_err(&motu->unit->device,
|
||||
"fail to enable frame fetching: %d\n", err);
|
||||
|
@ -317,7 +317,7 @@ static int init_stream(struct snd_motu *motu, struct amdtp_stream *s)
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = amdtp_motu_init(s, motu->unit, dir, motu->spec->protocol);
|
||||
err = amdtp_motu_init(s, motu->unit, dir, motu->spec);
|
||||
if (err < 0)
|
||||
fw_iso_resources_destroy(resources);
|
||||
|
||||
|
|
|
@ -172,105 +172,6 @@ static void motu_bus_update(struct fw_unit *unit)
|
|||
snd_motu_transaction_reregister(motu);
|
||||
}
|
||||
|
||||
const struct snd_motu_spec snd_motu_spec_828mk2 = {
|
||||
.name = "828mk2",
|
||||
.protocol = &snd_motu_protocol_v2,
|
||||
.flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
|
||||
SND_MOTU_SPEC_TX_MICINST_CHUNK |
|
||||
SND_MOTU_SPEC_TX_RETURN_CHUNK |
|
||||
SND_MOTU_SPEC_RX_SEPARATED_MAIN |
|
||||
SND_MOTU_SPEC_HAS_OPT_IFACE_A |
|
||||
SND_MOTU_SPEC_RX_MIDI_2ND_Q |
|
||||
SND_MOTU_SPEC_TX_MIDI_2ND_Q,
|
||||
|
||||
.analog_in_ports = 8,
|
||||
.analog_out_ports = 8,
|
||||
};
|
||||
|
||||
static const struct snd_motu_spec motu_traveler = {
|
||||
.name = "Traveler",
|
||||
.protocol = &snd_motu_protocol_v2,
|
||||
.flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
|
||||
SND_MOTU_SPEC_SUPPORT_CLOCK_X4 |
|
||||
SND_MOTU_SPEC_TX_RETURN_CHUNK |
|
||||
SND_MOTU_SPEC_HAS_AESEBU_IFACE |
|
||||
SND_MOTU_SPEC_HAS_OPT_IFACE_A |
|
||||
SND_MOTU_SPEC_RX_MIDI_2ND_Q |
|
||||
SND_MOTU_SPEC_TX_MIDI_2ND_Q,
|
||||
|
||||
.analog_in_ports = 8,
|
||||
.analog_out_ports = 8,
|
||||
};
|
||||
|
||||
static const struct snd_motu_spec motu_ultralite = {
|
||||
.name = "UltraLite",
|
||||
.protocol = &snd_motu_protocol_v2,
|
||||
.flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
|
||||
SND_MOTU_SPEC_TX_MICINST_CHUNK | // padding.
|
||||
SND_MOTU_SPEC_TX_RETURN_CHUNK |
|
||||
SND_MOTU_SPEC_RX_MIDI_2ND_Q |
|
||||
SND_MOTU_SPEC_TX_MIDI_2ND_Q |
|
||||
SND_MOTU_SPEC_RX_SEPARATED_MAIN,
|
||||
.analog_in_ports = 8,
|
||||
.analog_out_ports = 8,
|
||||
};
|
||||
|
||||
static const struct snd_motu_spec motu_8pre = {
|
||||
.name = "8pre",
|
||||
.protocol = &snd_motu_protocol_v2,
|
||||
// In tx, use coax chunks for mix-return 1/2. In rx, use coax chunks for
|
||||
// dummy 1/2.
|
||||
.flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
|
||||
SND_MOTU_SPEC_HAS_OPT_IFACE_A |
|
||||
SND_MOTU_SPEC_HAS_OPT_IFACE_B |
|
||||
SND_MOTU_SPEC_RX_MIDI_2ND_Q |
|
||||
SND_MOTU_SPEC_TX_MIDI_2ND_Q,
|
||||
.analog_in_ports = 8,
|
||||
.analog_out_ports = 2,
|
||||
};
|
||||
|
||||
static const struct snd_motu_spec motu_828mk3 = {
|
||||
.name = "828mk3",
|
||||
.protocol = &snd_motu_protocol_v3,
|
||||
.flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
|
||||
SND_MOTU_SPEC_SUPPORT_CLOCK_X4 |
|
||||
SND_MOTU_SPEC_TX_MICINST_CHUNK |
|
||||
SND_MOTU_SPEC_TX_RETURN_CHUNK |
|
||||
SND_MOTU_SPEC_TX_REVERB_CHUNK |
|
||||
SND_MOTU_SPEC_RX_SEPARATED_MAIN |
|
||||
SND_MOTU_SPEC_HAS_OPT_IFACE_A |
|
||||
SND_MOTU_SPEC_HAS_OPT_IFACE_B |
|
||||
SND_MOTU_SPEC_RX_MIDI_3RD_Q |
|
||||
SND_MOTU_SPEC_TX_MIDI_3RD_Q,
|
||||
|
||||
.analog_in_ports = 8,
|
||||
.analog_out_ports = 8,
|
||||
};
|
||||
|
||||
static const struct snd_motu_spec motu_audio_express = {
|
||||
.name = "AudioExpress",
|
||||
.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_SEPARATED_MAIN |
|
||||
SND_MOTU_SPEC_RX_MIDI_2ND_Q |
|
||||
SND_MOTU_SPEC_TX_MIDI_3RD_Q,
|
||||
.analog_in_ports = 2,
|
||||
.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_SEPARATED_MAIN,
|
||||
.analog_in_ports = 2,
|
||||
.analog_out_ports = 2,
|
||||
};
|
||||
|
||||
#define SND_MOTU_DEV_ENTRY(model, data) \
|
||||
{ \
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID | \
|
||||
|
@ -284,13 +185,14 @@ static const struct snd_motu_spec motu_4pre = {
|
|||
|
||||
static const struct ieee1394_device_id motu_id_table[] = {
|
||||
SND_MOTU_DEV_ENTRY(0x000003, &snd_motu_spec_828mk2),
|
||||
SND_MOTU_DEV_ENTRY(0x000009, &motu_traveler),
|
||||
SND_MOTU_DEV_ENTRY(0x00000d, &motu_ultralite),
|
||||
SND_MOTU_DEV_ENTRY(0x00000f, &motu_8pre),
|
||||
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),
|
||||
SND_MOTU_DEV_ENTRY(0x000009, &snd_motu_spec_traveler),
|
||||
SND_MOTU_DEV_ENTRY(0x00000d, &snd_motu_spec_ultralite),
|
||||
SND_MOTU_DEV_ENTRY(0x00000f, &snd_motu_spec_8pre),
|
||||
SND_MOTU_DEV_ENTRY(0x000015, &snd_motu_spec_828mk3), // FireWire only.
|
||||
SND_MOTU_DEV_ENTRY(0x000019, &snd_motu_spec_ultralite_mk3), // FireWire only.
|
||||
SND_MOTU_DEV_ENTRY(0x000035, &snd_motu_spec_828mk3), // Hybrid.
|
||||
SND_MOTU_DEV_ENTRY(0x000033, &snd_motu_spec_audio_express),
|
||||
SND_MOTU_DEV_ENTRY(0x000045, &snd_motu_spec_4pre),
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(ieee1394, motu_id_table);
|
||||
|
|
|
@ -36,8 +36,7 @@ struct snd_motu_packet_format {
|
|||
unsigned char pcm_byte_offset;
|
||||
|
||||
unsigned char msg_chunks;
|
||||
unsigned char fixed_part_pcm_chunks[3];
|
||||
unsigned char differed_part_pcm_chunks[3];
|
||||
unsigned char pcm_chunks[3];
|
||||
};
|
||||
|
||||
struct snd_motu {
|
||||
|
@ -74,19 +73,10 @@ struct snd_motu {
|
|||
};
|
||||
|
||||
enum snd_motu_spec_flags {
|
||||
SND_MOTU_SPEC_SUPPORT_CLOCK_X2 = 0x0001,
|
||||
SND_MOTU_SPEC_SUPPORT_CLOCK_X4 = 0x0002,
|
||||
SND_MOTU_SPEC_TX_MICINST_CHUNK = 0x0004,
|
||||
SND_MOTU_SPEC_TX_RETURN_CHUNK = 0x0008,
|
||||
SND_MOTU_SPEC_TX_REVERB_CHUNK = 0x0010,
|
||||
SND_MOTU_SPEC_HAS_AESEBU_IFACE = 0x0020,
|
||||
SND_MOTU_SPEC_HAS_OPT_IFACE_A = 0x0040,
|
||||
SND_MOTU_SPEC_HAS_OPT_IFACE_B = 0x0080,
|
||||
SND_MOTU_SPEC_RX_MIDI_2ND_Q = 0x0100,
|
||||
SND_MOTU_SPEC_RX_MIDI_3RD_Q = 0x0200,
|
||||
SND_MOTU_SPEC_TX_MIDI_2ND_Q = 0x0400,
|
||||
SND_MOTU_SPEC_TX_MIDI_3RD_Q = 0x0800,
|
||||
SND_MOTU_SPEC_RX_SEPARATED_MAIN = 0x1000,
|
||||
SND_MOTU_SPEC_RX_MIDI_2ND_Q = 0x0001,
|
||||
SND_MOTU_SPEC_RX_MIDI_3RD_Q = 0x0002,
|
||||
SND_MOTU_SPEC_TX_MIDI_2ND_Q = 0x0004,
|
||||
SND_MOTU_SPEC_TX_MIDI_3RD_Q = 0x0008,
|
||||
};
|
||||
|
||||
#define SND_MOTU_CLOCK_RATE_COUNT 6
|
||||
|
@ -108,33 +98,33 @@ enum snd_motu_clock_source {
|
|||
SND_MOTU_CLOCK_SOURCE_UNKNOWN,
|
||||
};
|
||||
|
||||
struct snd_motu_protocol {
|
||||
int (*get_clock_rate)(struct snd_motu *motu, unsigned int *rate);
|
||||
int (*set_clock_rate)(struct snd_motu *motu, unsigned int rate);
|
||||
int (*get_clock_source)(struct snd_motu *motu,
|
||||
enum snd_motu_clock_source *source);
|
||||
int (*switch_fetching_mode)(struct snd_motu *motu, bool enable);
|
||||
int (*cache_packet_formats)(struct snd_motu *motu);
|
||||
enum snd_motu_protocol_version {
|
||||
SND_MOTU_PROTOCOL_V2,
|
||||
SND_MOTU_PROTOCOL_V3,
|
||||
};
|
||||
|
||||
struct snd_motu_spec {
|
||||
const char *const name;
|
||||
enum snd_motu_protocol_version protocol_version;
|
||||
enum snd_motu_spec_flags flags;
|
||||
|
||||
unsigned char analog_in_ports;
|
||||
unsigned char analog_out_ports;
|
||||
|
||||
const struct snd_motu_protocol *const protocol;
|
||||
unsigned char tx_fixed_pcm_chunks[3];
|
||||
unsigned char rx_fixed_pcm_chunks[3];
|
||||
};
|
||||
|
||||
extern const struct snd_motu_protocol snd_motu_protocol_v2;
|
||||
extern const struct snd_motu_protocol snd_motu_protocol_v3;
|
||||
|
||||
extern const struct snd_motu_spec snd_motu_spec_828mk2;
|
||||
extern const struct snd_motu_spec snd_motu_spec_traveler;
|
||||
extern const struct snd_motu_spec snd_motu_spec_ultralite;
|
||||
extern const struct snd_motu_spec snd_motu_spec_8pre;
|
||||
|
||||
extern const struct snd_motu_spec snd_motu_spec_828mk3;
|
||||
extern const struct snd_motu_spec snd_motu_spec_ultralite_mk3;
|
||||
extern const struct snd_motu_spec snd_motu_spec_audio_express;
|
||||
extern const struct snd_motu_spec snd_motu_spec_4pre;
|
||||
|
||||
int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
enum amdtp_stream_direction dir,
|
||||
const struct snd_motu_protocol *const protocol);
|
||||
const struct snd_motu_spec *spec);
|
||||
int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
|
||||
unsigned int midi_ports,
|
||||
struct snd_motu_packet_format *formats);
|
||||
|
@ -169,4 +159,79 @@ int snd_motu_create_pcm_devices(struct snd_motu *motu);
|
|||
int snd_motu_create_midi_devices(struct snd_motu *motu);
|
||||
|
||||
int snd_motu_create_hwdep_device(struct snd_motu *motu);
|
||||
|
||||
int snd_motu_protocol_v2_get_clock_rate(struct snd_motu *motu,
|
||||
unsigned int *rate);
|
||||
int snd_motu_protocol_v2_set_clock_rate(struct snd_motu *motu,
|
||||
unsigned int rate);
|
||||
int snd_motu_protocol_v2_get_clock_source(struct snd_motu *motu,
|
||||
enum snd_motu_clock_source *src);
|
||||
int snd_motu_protocol_v2_switch_fetching_mode(struct snd_motu *motu,
|
||||
bool enable);
|
||||
int snd_motu_protocol_v2_cache_packet_formats(struct snd_motu *motu);
|
||||
|
||||
int snd_motu_protocol_v3_get_clock_rate(struct snd_motu *motu,
|
||||
unsigned int *rate);
|
||||
int snd_motu_protocol_v3_set_clock_rate(struct snd_motu *motu,
|
||||
unsigned int rate);
|
||||
int snd_motu_protocol_v3_get_clock_source(struct snd_motu *motu,
|
||||
enum snd_motu_clock_source *src);
|
||||
int snd_motu_protocol_v3_switch_fetching_mode(struct snd_motu *motu,
|
||||
bool enable);
|
||||
int snd_motu_protocol_v3_cache_packet_formats(struct snd_motu *motu);
|
||||
|
||||
static inline int snd_motu_protocol_get_clock_rate(struct snd_motu *motu,
|
||||
unsigned int *rate)
|
||||
{
|
||||
if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V2)
|
||||
return snd_motu_protocol_v2_get_clock_rate(motu, rate);
|
||||
else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3)
|
||||
return snd_motu_protocol_v3_get_clock_rate(motu, rate);
|
||||
else
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static inline int snd_motu_protocol_set_clock_rate(struct snd_motu *motu,
|
||||
unsigned int rate)
|
||||
{
|
||||
if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V2)
|
||||
return snd_motu_protocol_v2_set_clock_rate(motu, rate);
|
||||
else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3)
|
||||
return snd_motu_protocol_v3_set_clock_rate(motu, rate);
|
||||
else
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static inline int snd_motu_protocol_get_clock_source(struct snd_motu *motu,
|
||||
enum snd_motu_clock_source *source)
|
||||
{
|
||||
if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V2)
|
||||
return snd_motu_protocol_v2_get_clock_source(motu, source);
|
||||
else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3)
|
||||
return snd_motu_protocol_v3_get_clock_source(motu, source);
|
||||
else
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static inline int snd_motu_protocol_switch_fetching_mode(struct snd_motu *motu,
|
||||
bool enable)
|
||||
{
|
||||
if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V2)
|
||||
return snd_motu_protocol_v2_switch_fetching_mode(motu, enable);
|
||||
else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3)
|
||||
return snd_motu_protocol_v3_switch_fetching_mode(motu, enable);
|
||||
else
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static inline int snd_motu_protocol_cache_packet_formats(struct snd_motu *motu)
|
||||
{
|
||||
if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V2)
|
||||
return snd_motu_protocol_v2_cache_packet_formats(motu);
|
||||
else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3)
|
||||
return snd_motu_protocol_v3_cache_packet_formats(motu);
|
||||
else
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -62,7 +62,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_exit);
|
|||
|
||||
static void default_release(struct device *dev)
|
||||
{
|
||||
snd_hdac_ext_bus_device_exit(container_of(dev, struct hdac_device, dev));
|
||||
snd_hdac_ext_bus_device_exit(dev_to_hdac_dev(dev));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -81,7 +81,6 @@ int snd_hdac_bus_exec_verb(struct hdac_bus *bus, unsigned int addr,
|
|||
mutex_unlock(&bus->cmd_mutex);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_bus_exec_verb);
|
||||
|
||||
/**
|
||||
* snd_hdac_bus_exec_verb_unlocked - unlocked version
|
||||
|
@ -150,7 +149,6 @@ void snd_hdac_bus_queue_event(struct hdac_bus *bus, u32 res, u32 res_ex)
|
|||
|
||||
schedule_work(&bus->unsol_work);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_bus_queue_event);
|
||||
|
||||
/*
|
||||
* process queued unsolicited events
|
||||
|
@ -162,6 +160,7 @@ static void snd_hdac_bus_process_unsol_events(struct work_struct *work)
|
|||
struct hdac_driver *drv;
|
||||
unsigned int rp, caddr, res;
|
||||
|
||||
spin_lock_irq(&bus->reg_lock);
|
||||
while (bus->unsol_rp != bus->unsol_wp) {
|
||||
rp = (bus->unsol_rp + 1) % HDA_UNSOL_QUEUE_SIZE;
|
||||
bus->unsol_rp = rp;
|
||||
|
@ -173,10 +172,13 @@ static void snd_hdac_bus_process_unsol_events(struct work_struct *work)
|
|||
codec = bus->caddr_tbl[caddr & 0x0f];
|
||||
if (!codec || !codec->dev.driver)
|
||||
continue;
|
||||
spin_unlock_irq(&bus->reg_lock);
|
||||
drv = drv_to_hdac_driver(codec->dev.driver);
|
||||
if (drv->unsol_event)
|
||||
drv->unsol_event(codec, res);
|
||||
spin_lock_irq(&bus->reg_lock);
|
||||
}
|
||||
spin_unlock_irq(&bus->reg_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <sound/core.h>
|
||||
#include <sound/hdaudio.h>
|
||||
#include <sound/hda_register.h>
|
||||
#include "local.h"
|
||||
|
||||
/* clear CORB read pointer properly */
|
||||
static void azx_clear_corbrp(struct hdac_bus *bus)
|
||||
|
@ -527,6 +528,18 @@ bool snd_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset)
|
|||
}
|
||||
|
||||
bus->chip_init = true;
|
||||
|
||||
/*
|
||||
* Default value of '8' is as per the HD audio specification (Rev 1.0a).
|
||||
* Following relation is used to derive STRIPE control value.
|
||||
* For sample rate <= 48K:
|
||||
* { ((num_channels * bits_per_sample) / number of SDOs) >= 8 }
|
||||
* For sample rate > 48K:
|
||||
* { ((num_channels * bits_per_sample * rate/48000) /
|
||||
* number of SDOs) >= 8 }
|
||||
*/
|
||||
bus->sdo_limit = 8;
|
||||
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_bus_init_chip);
|
||||
|
|
|
@ -20,7 +20,7 @@ static int get_codec_vendor_name(struct hdac_device *codec);
|
|||
|
||||
static void default_release(struct device *dev)
|
||||
{
|
||||
snd_hdac_device_exit(container_of(dev, struct hdac_device, dev));
|
||||
snd_hdac_device_exit(dev_to_hdac_dev(dev));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -38,7 +38,7 @@ int snd_hdac_get_stream_stripe_ctl(struct hdac_bus *bus,
|
|||
else
|
||||
value = (channels * bits_per_sample) / sdo_line;
|
||||
|
||||
if (value >= 8)
|
||||
if (value >= bus->sdo_limit)
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2019 Jaroslav Kysela <perex@perex.cz>
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/soundwire/sdw.h>
|
||||
#include <linux/soundwire/sdw_intel.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/intel-dsp-config.h>
|
||||
#include <sound/intel-nhlt.h>
|
||||
|
@ -14,9 +17,14 @@ static int dsp_driver;
|
|||
module_param(dsp_driver, int, 0444);
|
||||
MODULE_PARM_DESC(dsp_driver, "Force the DSP driver for Intel DSP (0=auto, 1=legacy, 2=SST, 3=SOF)");
|
||||
|
||||
#define FLAG_SST BIT(0)
|
||||
#define FLAG_SOF BIT(1)
|
||||
#define FLAG_SOF_ONLY_IF_DMIC BIT(16)
|
||||
#define FLAG_SST BIT(0)
|
||||
#define FLAG_SOF BIT(1)
|
||||
#define FLAG_SST_ONLY_IF_DMIC BIT(15)
|
||||
#define FLAG_SOF_ONLY_IF_DMIC BIT(16)
|
||||
#define FLAG_SOF_ONLY_IF_SOUNDWIRE BIT(17)
|
||||
|
||||
#define FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE (FLAG_SOF_ONLY_IF_DMIC | \
|
||||
FLAG_SOF_ONLY_IF_SOUNDWIRE)
|
||||
|
||||
struct config_entry {
|
||||
u32 flags;
|
||||
|
@ -100,6 +108,10 @@ static const struct config_entry config_table[] = {
|
|||
{}
|
||||
}
|
||||
},
|
||||
{
|
||||
.flags = FLAG_SST | FLAG_SST_ONLY_IF_DMIC,
|
||||
.device = 0x9d70,
|
||||
},
|
||||
#endif
|
||||
/* Kabylake-LP */
|
||||
#if IS_ENABLED(CONFIG_SND_SOC_INTEL_KBL)
|
||||
|
@ -116,6 +128,10 @@ static const struct config_entry config_table[] = {
|
|||
{}
|
||||
}
|
||||
},
|
||||
{
|
||||
.flags = FLAG_SST | FLAG_SST_ONLY_IF_DMIC,
|
||||
.device = 0x9d71,
|
||||
},
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -166,7 +182,7 @@ static const struct config_entry config_table[] = {
|
|||
}
|
||||
},
|
||||
{
|
||||
.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
|
||||
.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
|
||||
.device = 0x9dc8,
|
||||
},
|
||||
#endif
|
||||
|
@ -187,7 +203,7 @@ static const struct config_entry config_table[] = {
|
|||
}
|
||||
},
|
||||
{
|
||||
.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
|
||||
.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
|
||||
.device = 0xa348,
|
||||
},
|
||||
#endif
|
||||
|
@ -204,18 +220,50 @@ static const struct config_entry config_table[] = {
|
|||
DMI_MATCH(DMI_SYS_VENDOR, "Google"),
|
||||
}
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "09C6")
|
||||
},
|
||||
},
|
||||
{
|
||||
/* early version of SKU 09C6 */
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0983")
|
||||
},
|
||||
},
|
||||
{}
|
||||
}
|
||||
},
|
||||
{
|
||||
.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
|
||||
.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
|
||||
.device = 0x02c8,
|
||||
},
|
||||
#endif
|
||||
/* Cometlake-H */
|
||||
#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMETLAKE_H)
|
||||
{
|
||||
.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
|
||||
.flags = FLAG_SOF,
|
||||
.device = 0x06c8,
|
||||
.dmi_table = (const struct dmi_system_id []) {
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "098F"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0990"),
|
||||
},
|
||||
},
|
||||
{}
|
||||
}
|
||||
},
|
||||
{
|
||||
.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
|
||||
.device = 0x06c8,
|
||||
},
|
||||
#endif
|
||||
|
@ -236,7 +284,7 @@ static const struct config_entry config_table[] = {
|
|||
}
|
||||
},
|
||||
{
|
||||
.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
|
||||
.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
|
||||
.device = 0x34c8,
|
||||
},
|
||||
#endif
|
||||
|
@ -256,9 +304,8 @@ static const struct config_entry config_table[] = {
|
|||
{}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
|
||||
.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
|
||||
.device = 0xa0c8,
|
||||
},
|
||||
#endif
|
||||
|
@ -303,6 +350,28 @@ static int snd_intel_dsp_check_dmic(struct pci_dev *pci)
|
|||
return ret;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)
|
||||
static int snd_intel_dsp_check_soundwire(struct pci_dev *pci)
|
||||
{
|
||||
struct sdw_intel_acpi_info info;
|
||||
acpi_handle handle;
|
||||
int ret;
|
||||
|
||||
handle = ACPI_HANDLE(&pci->dev);
|
||||
|
||||
ret = sdw_intel_acpi_scan(handle, &info);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return info.link_mask;
|
||||
}
|
||||
#else
|
||||
static int snd_intel_dsp_check_soundwire(struct pci_dev *pci)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int snd_intel_dsp_driver_probe(struct pci_dev *pci)
|
||||
{
|
||||
const struct config_entry *cfg;
|
||||
|
@ -336,18 +405,31 @@ int snd_intel_dsp_driver_probe(struct pci_dev *pci)
|
|||
return SND_INTEL_DSP_DRIVER_ANY;
|
||||
|
||||
if (cfg->flags & FLAG_SOF) {
|
||||
if (cfg->flags & FLAG_SOF_ONLY_IF_DMIC) {
|
||||
if (snd_intel_dsp_check_dmic(pci)) {
|
||||
dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SOF driver\n");
|
||||
return SND_INTEL_DSP_DRIVER_SOF;
|
||||
}
|
||||
} else {
|
||||
if (cfg->flags & FLAG_SOF_ONLY_IF_SOUNDWIRE &&
|
||||
snd_intel_dsp_check_soundwire(pci) > 0) {
|
||||
dev_info(&pci->dev, "SoundWire enabled on CannonLake+ platform, using SOF driver\n");
|
||||
return SND_INTEL_DSP_DRIVER_SOF;
|
||||
}
|
||||
if (cfg->flags & FLAG_SOF_ONLY_IF_DMIC &&
|
||||
snd_intel_dsp_check_dmic(pci)) {
|
||||
dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SOF driver\n");
|
||||
return SND_INTEL_DSP_DRIVER_SOF;
|
||||
}
|
||||
if (!(cfg->flags & FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE))
|
||||
return SND_INTEL_DSP_DRIVER_SOF;
|
||||
}
|
||||
|
||||
if (cfg->flags & FLAG_SST)
|
||||
return SND_INTEL_DSP_DRIVER_SST;
|
||||
|
||||
if (cfg->flags & FLAG_SST) {
|
||||
if (cfg->flags & FLAG_SST_ONLY_IF_DMIC) {
|
||||
if (snd_intel_dsp_check_dmic(pci)) {
|
||||
dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SST driver\n");
|
||||
return SND_INTEL_DSP_DRIVER_SST;
|
||||
}
|
||||
} else {
|
||||
return SND_INTEL_DSP_DRIVER_SST;
|
||||
}
|
||||
}
|
||||
|
||||
return SND_INTEL_DSP_DRIVER_LEGACY;
|
||||
}
|
||||
|
@ -355,3 +437,4 @@ EXPORT_SYMBOL_GPL(snd_intel_dsp_driver_probe);
|
|||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("Intel DSP config driver");
|
||||
MODULE_IMPORT_NS(SOUNDWIRE_INTEL_INIT);
|
||||
|
|
|
@ -1,61 +1,28 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
// 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 const 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;
|
||||
struct nhlt_acpi_table *nhlt;
|
||||
acpi_status status;
|
||||
|
||||
handle = ACPI_HANDLE(dev);
|
||||
if (!handle) {
|
||||
dev_err(dev, "Didn't find ACPI_HANDLE\n");
|
||||
status = acpi_get_table(ACPI_SIG_NHLT, 0,
|
||||
(struct acpi_table_header **)&nhlt);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
dev_warn(dev, "NHLT table not found\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;
|
||||
return nhlt;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_nhlt_init);
|
||||
|
||||
void intel_nhlt_free(struct nhlt_acpi_table *nhlt)
|
||||
{
|
||||
memunmap((void *)nhlt);
|
||||
acpi_put_table((struct acpi_table_header *)nhlt);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_nhlt_free);
|
||||
|
||||
|
|
|
@ -36,6 +36,9 @@ 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);
|
||||
void snd_hdac_bus_queue_event(struct hdac_bus *bus, u32 res, u32 res_ex);
|
||||
int snd_hdac_bus_exec_verb(struct hdac_bus *bus, unsigned int addr,
|
||||
unsigned int cmd, unsigned int *res);
|
||||
|
||||
int snd_hdac_exec_verb(struct hdac_device *codec, unsigned int cmd,
|
||||
unsigned int flags, unsigned int *res);
|
||||
|
|
|
@ -54,7 +54,7 @@ MODULE_PARM_DESC(clockfreq, "Clock frequency for ad1816a driver (default = 0).")
|
|||
static const struct pnp_card_device_id snd_ad1816a_pnpids[] = {
|
||||
/* Analog Devices AD1815 */
|
||||
{ .id = "ADS7150", .devs = { { .id = "ADS7150" }, { .id = "ADS7151" } } },
|
||||
/* Analog Device AD1816? */
|
||||
/* Analog Devices AD1816? */
|
||||
{ .id = "ADS7180", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
|
||||
/* Analog Devices AD1816A - added by Kenneth Platz <kxp@atl.hp.com> */
|
||||
{ .id = "ADS7181", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
|
||||
|
|
|
@ -267,8 +267,10 @@ static int snd_es968_pnp_detect(struct pnp_card_link *pcard,
|
|||
return error;
|
||||
}
|
||||
error = snd_es1688_probe(card, dev);
|
||||
if (error < 0)
|
||||
if (error < 0) {
|
||||
snd_card_free(card);
|
||||
return error;
|
||||
}
|
||||
pnp_set_card_drvdata(pcard, card);
|
||||
snd_es968_pnp_is_probed = 1;
|
||||
return 0;
|
||||
|
|
|
@ -1171,7 +1171,10 @@ wavefront_send_alias (snd_wavefront_t *dev, wavefront_patch_info *header)
|
|||
"alias for %d\n",
|
||||
header->number,
|
||||
header->hdr.a.OriginalSample);
|
||||
|
||||
|
||||
if (header->number >= WF_MAX_SAMPLE)
|
||||
return -EINVAL;
|
||||
|
||||
munge_int32 (header->number, &alias_hdr[0], 2);
|
||||
munge_int32 (header->hdr.a.OriginalSample, &alias_hdr[2], 2);
|
||||
munge_int32 (*((unsigned int *)&header->hdr.a.sampleStartOffset),
|
||||
|
@ -1202,6 +1205,9 @@ wavefront_send_multisample (snd_wavefront_t *dev, wavefront_patch_info *header)
|
|||
int num_samples;
|
||||
unsigned char *msample_hdr;
|
||||
|
||||
if (header->number >= WF_MAX_SAMPLE)
|
||||
return -EINVAL;
|
||||
|
||||
msample_hdr = kmalloc(WF_MSAMPLE_BYTES, GFP_KERNEL);
|
||||
if (! msample_hdr)
|
||||
return -ENOMEM;
|
||||
|
|
|
@ -1356,7 +1356,7 @@ static int patch_cx20551(struct snd_ac97 *ac97)
|
|||
}
|
||||
|
||||
/*
|
||||
* Analog Device AD18xx, AD19xx codecs
|
||||
* Analog Devices AD18xx, AD19xx codecs
|
||||
*/
|
||||
#ifdef CONFIG_PM
|
||||
static void ad18xx_resume(struct snd_ac97 *ac97)
|
||||
|
|
|
@ -99,10 +99,10 @@ comment "Set to Y if you want auto-loading the codec driver"
|
|||
depends on SND_HDA=y && SND_HDA_CODEC_REALTEK=m
|
||||
|
||||
config SND_HDA_CODEC_ANALOG
|
||||
tristate "Build Analog Device HD-audio codec support"
|
||||
tristate "Build Analog Devices HD-audio codec support"
|
||||
select SND_HDA_GENERIC
|
||||
help
|
||||
Say Y or M here to include Analog Device HD-audio codec support in
|
||||
Say Y or M here to include Analog Devices HD-audio codec support in
|
||||
snd-hda-intel driver, such as AD1986A.
|
||||
|
||||
comment "Set to Y if you want auto-loading the codec driver"
|
||||
|
|
|
@ -2662,6 +2662,9 @@ static const struct pci_device_id azx_ids[] = {
|
|||
{ PCI_DEVICE(0x1002, 0xab20),
|
||||
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
|
||||
AZX_DCAPS_PM_RUNTIME },
|
||||
{ PCI_DEVICE(0x1002, 0xab28),
|
||||
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
|
||||
AZX_DCAPS_PM_RUNTIME },
|
||||
{ PCI_DEVICE(0x1002, 0xab38),
|
||||
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
|
||||
AZX_DCAPS_PM_RUNTIME },
|
||||
|
|
|
@ -52,10 +52,21 @@
|
|||
#define HDA_IPFS_INTR_MASK 0x188
|
||||
#define HDA_IPFS_EN_INTR (1 << 16)
|
||||
|
||||
/* FPCI */
|
||||
#define FPCI_DBG_CFG_2 0x10F4
|
||||
#define FPCI_GCAP_NSDO_SHIFT 18
|
||||
#define FPCI_GCAP_NSDO_MASK (0x3 << FPCI_GCAP_NSDO_SHIFT)
|
||||
|
||||
/* max number of SDs */
|
||||
#define NUM_CAPTURE_SD 1
|
||||
#define NUM_PLAYBACK_SD 1
|
||||
|
||||
/*
|
||||
* Tegra194 does not reflect correct number of SDO lines. Below macro
|
||||
* is used to update the GCAP register to workaround the issue.
|
||||
*/
|
||||
#define TEGRA194_NUM_SDO_LINES 4
|
||||
|
||||
struct hda_tegra {
|
||||
struct azx chip;
|
||||
struct device *dev;
|
||||
|
@ -275,6 +286,7 @@ static int hda_tegra_init_clk(struct hda_tegra *hda)
|
|||
|
||||
static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev)
|
||||
{
|
||||
struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
|
||||
struct hdac_bus *bus = azx_bus(chip);
|
||||
struct snd_card *card = chip->card;
|
||||
int err;
|
||||
|
@ -298,6 +310,26 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev)
|
|||
bus->irq = irq_id;
|
||||
card->sync_irq = bus->irq;
|
||||
|
||||
/*
|
||||
* Tegra194 has 4 SDO lines and the STRIPE can be used to
|
||||
* indicate how many of the SDO lines the stream should be
|
||||
* striped. But GCAP register does not reflect the true
|
||||
* capability of HW. Below workaround helps to fix this.
|
||||
*
|
||||
* GCAP_NSDO is bits 19:18 in T_AZA_DBG_CFG_2,
|
||||
* 0 for 1 SDO, 1 for 2 SDO, 2 for 4 SDO lines.
|
||||
*/
|
||||
if (of_device_is_compatible(np, "nvidia,tegra194-hda")) {
|
||||
u32 val;
|
||||
|
||||
dev_info(card->dev, "Override SDO lines to %u\n",
|
||||
TEGRA194_NUM_SDO_LINES);
|
||||
|
||||
val = readl(hda->regs + FPCI_DBG_CFG_2) & ~FPCI_GCAP_NSDO_MASK;
|
||||
val |= (TEGRA194_NUM_SDO_LINES >> 1) << FPCI_GCAP_NSDO_SHIFT;
|
||||
writel(val, hda->regs + FPCI_DBG_CFG_2);
|
||||
}
|
||||
|
||||
gcap = azx_readw(chip, GCAP);
|
||||
dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap);
|
||||
|
||||
|
@ -332,6 +364,23 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev)
|
|||
/* initialize chip */
|
||||
azx_init_chip(chip, 1);
|
||||
|
||||
/*
|
||||
* Playback (for 44.1K/48K, 2-channel, 16-bps) fails with
|
||||
* 4 SDO lines due to legacy design limitation. Following
|
||||
* is, from HD Audio Specification (Revision 1.0a), used to
|
||||
* control striping of the stream across multiple SDO lines
|
||||
* for sample rates <= 48K.
|
||||
*
|
||||
* { ((num_channels * bits_per_sample) / number of SDOs) >= 8 }
|
||||
*
|
||||
* Due to legacy design issue it is recommended that above
|
||||
* ratio must be greater than 8. Since number of SDO lines is
|
||||
* in powers of 2, next available ratio is 16 which can be
|
||||
* used as a limiting factor here.
|
||||
*/
|
||||
if (of_device_is_compatible(np, "nvidia,tegra194-hda"))
|
||||
chip->bus.core.sdo_limit = 16;
|
||||
|
||||
/* codec detection */
|
||||
if (!bus->codec_mask) {
|
||||
dev_err(card->dev, "no codecs found!\n");
|
||||
|
@ -408,6 +457,7 @@ static int hda_tegra_create(struct snd_card *card,
|
|||
|
||||
static const struct of_device_id hda_tegra_match[] = {
|
||||
{ .compatible = "nvidia,tegra30-hda" },
|
||||
{ .compatible = "nvidia,tegra194-hda" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, hda_tegra_match);
|
||||
|
|
|
@ -2024,7 +2024,7 @@ static const struct hda_pcm_ops generic_ops = {
|
|||
|
||||
static int hdmi_get_spk_alloc(struct hdac_device *hdac, int pcm_idx)
|
||||
{
|
||||
struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
|
||||
struct hda_codec *codec = hdac_to_hda_codec(hdac);
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
|
||||
|
||||
|
@ -2037,7 +2037,7 @@ static int hdmi_get_spk_alloc(struct hdac_device *hdac, int pcm_idx)
|
|||
static void hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx,
|
||||
unsigned char *chmap)
|
||||
{
|
||||
struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
|
||||
struct hda_codec *codec = hdac_to_hda_codec(hdac);
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
|
||||
|
||||
|
@ -2051,7 +2051,7 @@ static void hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx,
|
|||
static void hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx,
|
||||
unsigned char *chmap, int prepared)
|
||||
{
|
||||
struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
|
||||
struct hda_codec *codec = hdac_to_hda_codec(hdac);
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
|
||||
|
||||
|
@ -2067,7 +2067,7 @@ static void hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx,
|
|||
|
||||
static bool is_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx)
|
||||
{
|
||||
struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
|
||||
struct hda_codec *codec = hdac_to_hda_codec(hdac);
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
|
||||
|
||||
|
@ -3787,7 +3787,7 @@ static int atihdmi_paired_chmap_validate(struct hdac_chmap *chmap,
|
|||
static int atihdmi_pin_set_slot_channel(struct hdac_device *hdac,
|
||||
hda_nid_t pin_nid, int hdmi_slot, int stream_channel)
|
||||
{
|
||||
struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
|
||||
struct hda_codec *codec = hdac_to_hda_codec(hdac);
|
||||
int verb;
|
||||
int ati_channel_setup = 0;
|
||||
|
||||
|
@ -3823,7 +3823,7 @@ static int atihdmi_pin_set_slot_channel(struct hdac_device *hdac,
|
|||
static int atihdmi_pin_get_slot_channel(struct hdac_device *hdac,
|
||||
hda_nid_t pin_nid, int asp_slot)
|
||||
{
|
||||
struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
|
||||
struct hda_codec *codec = hdac_to_hda_codec(hdac);
|
||||
bool was_odd = false;
|
||||
int ati_asp_slot = asp_slot;
|
||||
int verb;
|
||||
|
@ -4169,6 +4169,7 @@ HDA_CODEC_ENTRY(0x8086280d, "Geminilake HDMI", patch_i915_glk_hdmi),
|
|||
HDA_CODEC_ENTRY(0x8086280f, "Icelake HDMI", patch_i915_icl_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862812, "Tigerlake HDMI", patch_i915_tgl_hdmi),
|
||||
HDA_CODEC_ENTRY(0x8086281a, "Jasperlake HDMI", patch_i915_icl_hdmi),
|
||||
HDA_CODEC_ENTRY(0x8086281b, "Elkhartlake HDMI", patch_i915_icl_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI", patch_i915_byt_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI", patch_i915_byt_hdmi),
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <linux/dmi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/leds.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/jack.h>
|
||||
#include <sound/hda_codec.h>
|
||||
|
@ -81,6 +82,7 @@ struct alc_spec {
|
|||
|
||||
/* mute LED for HP laptops, see alc269_fixup_mic_mute_hook() */
|
||||
int mute_led_polarity;
|
||||
int micmute_led_polarity;
|
||||
hda_nid_t mute_led_nid;
|
||||
hda_nid_t cap_mute_led_nid;
|
||||
|
||||
|
@ -4080,11 +4082,9 @@ static void alc269_fixup_hp_mute_led_mic3(struct hda_codec *codec,
|
|||
|
||||
/* update LED status via GPIO */
|
||||
static void alc_update_gpio_led(struct hda_codec *codec, unsigned int mask,
|
||||
bool enabled)
|
||||
int polarity, bool enabled)
|
||||
{
|
||||
struct alc_spec *spec = codec->spec;
|
||||
|
||||
if (spec->mute_led_polarity)
|
||||
if (polarity)
|
||||
enabled = !enabled;
|
||||
alc_update_gpio_data(codec, mask, !enabled); /* muted -> LED on */
|
||||
}
|
||||
|
@ -4095,7 +4095,8 @@ static void alc_fixup_gpio_mute_hook(void *private_data, int enabled)
|
|||
struct hda_codec *codec = private_data;
|
||||
struct alc_spec *spec = codec->spec;
|
||||
|
||||
alc_update_gpio_led(codec, spec->gpio_mute_led_mask, enabled);
|
||||
alc_update_gpio_led(codec, spec->gpio_mute_led_mask,
|
||||
spec->mute_led_polarity, enabled);
|
||||
}
|
||||
|
||||
/* turn on/off mic-mute LED via GPIO per capture hook */
|
||||
|
@ -4104,9 +4105,30 @@ static void alc_gpio_micmute_update(struct hda_codec *codec)
|
|||
struct alc_spec *spec = codec->spec;
|
||||
|
||||
alc_update_gpio_led(codec, spec->gpio_mic_led_mask,
|
||||
spec->micmute_led_polarity,
|
||||
spec->gen.micmute_led.led_value);
|
||||
}
|
||||
|
||||
#if IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO)
|
||||
static int micmute_led_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent);
|
||||
struct alc_spec *spec = codec->spec;
|
||||
|
||||
alc_update_gpio_led(codec, spec->gpio_mic_led_mask,
|
||||
spec->micmute_led_polarity, !!brightness);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct led_classdev micmute_led_cdev = {
|
||||
.name = "hda::micmute",
|
||||
.max_brightness = 1,
|
||||
.brightness_set_blocking = micmute_led_set,
|
||||
.default_trigger = "audio-micmute",
|
||||
};
|
||||
#endif
|
||||
|
||||
/* setup mute and mic-mute GPIO bits, add hooks appropriately */
|
||||
static void alc_fixup_hp_gpio_led(struct hda_codec *codec,
|
||||
int action,
|
||||
|
@ -4114,6 +4136,9 @@ static void alc_fixup_hp_gpio_led(struct hda_codec *codec,
|
|||
unsigned int micmute_mask)
|
||||
{
|
||||
struct alc_spec *spec = codec->spec;
|
||||
#if IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO)
|
||||
int err;
|
||||
#endif
|
||||
|
||||
alc_fixup_gpio(codec, action, mute_mask | micmute_mask);
|
||||
|
||||
|
@ -4126,6 +4151,13 @@ static void alc_fixup_hp_gpio_led(struct hda_codec *codec,
|
|||
if (micmute_mask) {
|
||||
spec->gpio_mic_led_mask = micmute_mask;
|
||||
snd_hda_gen_add_micmute_led(codec, alc_gpio_micmute_update);
|
||||
|
||||
#if IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO)
|
||||
micmute_led_cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
|
||||
err = devm_led_classdev_register(&codec->core.dev, &micmute_led_cdev);
|
||||
if (err)
|
||||
codec_warn(codec, "failed to register micmute LED\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4138,7 +4170,11 @@ static void alc269_fixup_hp_gpio_led(struct hda_codec *codec,
|
|||
static void alc285_fixup_hp_gpio_led(struct hda_codec *codec,
|
||||
const struct hda_fixup *fix, int action)
|
||||
{
|
||||
alc_fixup_hp_gpio_led(codec, action, 0x04, 0x00);
|
||||
struct alc_spec *spec = codec->spec;
|
||||
|
||||
spec->micmute_led_polarity = 1;
|
||||
|
||||
alc_fixup_hp_gpio_led(codec, action, 0x04, 0x01);
|
||||
}
|
||||
|
||||
static void alc286_fixup_hp_gpio_led(struct hda_codec *codec,
|
||||
|
@ -5808,7 +5844,8 @@ static void alc280_hp_gpio4_automute_hook(struct hda_codec *codec,
|
|||
|
||||
snd_hda_gen_hp_automute(codec, jack);
|
||||
/* mute_led_polarity is set to 0, so we pass inverted value here */
|
||||
alc_update_gpio_led(codec, 0x10, !spec->gen.hp_jack_present);
|
||||
alc_update_gpio_led(codec, 0x10, spec->mute_led_polarity,
|
||||
!spec->gen.hp_jack_present);
|
||||
}
|
||||
|
||||
/* Manage GPIOs for HP EliteBook Folio 9480m.
|
||||
|
|
|
@ -460,7 +460,7 @@ static void xonar_st_init(struct oxygen *chip)
|
|||
|
||||
data->generic.anti_pop_delay = 100;
|
||||
data->h6 = chip->model.dac_channels_mixer > 2;
|
||||
data->has_cs2000 = 1;
|
||||
data->has_cs2000 = true;
|
||||
data->cs2000_regs[CS2000_FUN_CFG_1] = CS2000_REF_CLK_DIV_1;
|
||||
data->broken_i2c = true;
|
||||
|
||||
|
@ -502,7 +502,7 @@ static void xonar_xense_init(struct oxygen *chip)
|
|||
xonar_init_ext_power(chip);
|
||||
|
||||
data->generic.anti_pop_delay = 100;
|
||||
data->has_cs2000 = 1;
|
||||
data->has_cs2000 = true;
|
||||
data->cs2000_regs[CS2000_FUN_CFG_1] = CS2000_REF_CLK_DIV_1;
|
||||
|
||||
oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
|
||||
|
|
|
@ -226,7 +226,7 @@ static int snd_pmac_pcm_prepare(struct snd_pmac *chip, struct pmac_stream *rec,
|
|||
offset += rec->period_size;
|
||||
}
|
||||
/* make loop */
|
||||
cp->command = cpu_to_le16(DBDMA_NOP + BR_ALWAYS);
|
||||
cp->command = cpu_to_le16(DBDMA_NOP | BR_ALWAYS);
|
||||
cp->cmd_dep = cpu_to_le32(rec->cmd.addr);
|
||||
|
||||
snd_pmac_dma_stop(rec);
|
||||
|
@ -726,7 +726,7 @@ void snd_pmac_beep_dma_start(struct snd_pmac *chip, int bytes, unsigned long add
|
|||
chip->extra_dma.cmds->xfer_status = cpu_to_le16(0);
|
||||
chip->extra_dma.cmds->cmd_dep = cpu_to_le32(chip->extra_dma.addr);
|
||||
chip->extra_dma.cmds->phy_addr = cpu_to_le32(addr);
|
||||
chip->extra_dma.cmds->command = cpu_to_le16(OUTPUT_MORE + BR_ALWAYS);
|
||||
chip->extra_dma.cmds->command = cpu_to_le16(OUTPUT_MORE | BR_ALWAYS);
|
||||
out_le32(&chip->awacs->control,
|
||||
(in_le32(&chip->awacs->control) & ~0x1f00)
|
||||
| (speed << 8));
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-utils.o soc-dai.o soc-component.o
|
||||
snd-soc-core-objs += soc-pcm.o soc-io.o soc-devres.o soc-ops.o
|
||||
snd-soc-core-objs += soc-pcm.o soc-io.o soc-devres.o soc-ops.o soc-link.o soc-card.o
|
||||
snd-soc-core-$(CONFIG_SND_SOC_COMPRESS) += soc-compress.o
|
||||
|
||||
ifneq ($(CONFIG_SND_SOC_TOPOLOGY),)
|
||||
|
|
|
@ -29,10 +29,23 @@ config SND_SOC_AMD_ACP3x
|
|||
|
||||
config SND_SOC_AMD_RV_RT5682_MACH
|
||||
tristate "AMD RV support for RT5682"
|
||||
select SND_SOC_RT5682
|
||||
select SND_SOC_RT5682_I2C
|
||||
select SND_SOC_MAX98357A
|
||||
select SND_SOC_CROS_EC_CODEC
|
||||
select I2C_CROS_EC_TUNNEL
|
||||
depends on SND_SOC_AMD_ACP3x && I2C && CROS_EC
|
||||
help
|
||||
This option enables machine driver for RT5682 and MAX9835.
|
||||
|
||||
config SND_SOC_AMD_RENOIR
|
||||
tristate "AMD Audio Coprocessor - Renoir support"
|
||||
depends on X86 && PCI
|
||||
help
|
||||
This option enables ACP support for Renoir platform
|
||||
|
||||
config SND_SOC_AMD_RENOIR_MACH
|
||||
tristate "AMD Renoir support for DMIC"
|
||||
select SND_SOC_DMIC
|
||||
depends on SND_SOC_AMD_RENOIR
|
||||
help
|
||||
This option enables machine driver for DMIC
|
||||
|
|
|
@ -9,3 +9,4 @@ obj-$(CONFIG_SND_SOC_AMD_CZ_DA7219MX98357_MACH) += snd-soc-acp-da7219mx98357-mac
|
|||
obj-$(CONFIG_SND_SOC_AMD_CZ_RT5645_MACH) += snd-soc-acp-rt5645-mach.o
|
||||
obj-$(CONFIG_SND_SOC_AMD_ACP3x) += raven/
|
||||
obj-$(CONFIG_SND_SOC_AMD_RV_RT5682_MACH) += snd-soc-acp-rt5682-mach.o
|
||||
obj-$(CONFIG_SND_SOC_AMD_RENOIR) += renoir/
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
#include "acp3x.h"
|
||||
|
||||
#define DRV_NAME "acp3x-i2s"
|
||||
#define DRV_NAME "acp3x_i2s_playcap"
|
||||
|
||||
static int acp3x_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
|
||||
unsigned int fmt)
|
||||
|
@ -269,7 +269,7 @@ static struct snd_soc_dai_ops acp3x_i2s_dai_ops = {
|
|||
};
|
||||
|
||||
static const struct snd_soc_component_driver acp3x_dai_component = {
|
||||
.name = "acp3x-i2s",
|
||||
.name = DRV_NAME,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver acp3x_i2s_dai = {
|
||||
|
@ -348,4 +348,4 @@ module_platform_driver(acp3x_dai_driver);
|
|||
MODULE_AUTHOR("Vishnuvardhanrao.Ravulapati@amd.com");
|
||||
MODULE_DESCRIPTION("AMD ACP 3.x PCM Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
||||
MODULE_ALIAS("platform:"DRV_NAME);
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
#include "acp3x.h"
|
||||
|
||||
#define DRV_NAME "acp3x-i2s-audio"
|
||||
#define DRV_NAME "acp3x_rv_i2s_dma"
|
||||
|
||||
static const struct snd_pcm_hardware acp3x_pcm_hardware_playback = {
|
||||
.info = SNDRV_PCM_INFO_INTERLEAVED |
|
||||
|
@ -241,14 +241,6 @@ static int acp3x_dma_open(struct snd_soc_component *component,
|
|||
adata->i2ssp_play_stream && !adata->i2ssp_capture_stream)
|
||||
rv_writel(1, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
adata->play_stream = substream;
|
||||
adata->i2ssp_play_stream = substream;
|
||||
} else {
|
||||
adata->capture_stream = substream;
|
||||
adata->i2ssp_capture_stream = substream;
|
||||
}
|
||||
|
||||
i2s_data->acp3x_base = adata->acp3x_base;
|
||||
runtime->private_data = i2s_data;
|
||||
return ret;
|
||||
|
@ -263,23 +255,42 @@ static int acp3x_dma_hw_params(struct snd_soc_component *component,
|
|||
struct snd_soc_pcm_runtime *prtd;
|
||||
struct snd_soc_card *card;
|
||||
struct acp3x_platform_info *pinfo;
|
||||
struct i2s_dev_data *adata;
|
||||
u64 size;
|
||||
|
||||
prtd = substream->private_data;
|
||||
card = prtd->card;
|
||||
pinfo = snd_soc_card_get_drvdata(card);
|
||||
adata = dev_get_drvdata(component->dev);
|
||||
rtd = substream->runtime->private_data;
|
||||
if (!rtd)
|
||||
return -EINVAL;
|
||||
|
||||
if (pinfo)
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
if (pinfo) {
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
rtd->i2s_instance = pinfo->play_i2s_instance;
|
||||
else
|
||||
switch (rtd->i2s_instance) {
|
||||
case I2S_BT_INSTANCE:
|
||||
adata->play_stream = substream;
|
||||
break;
|
||||
case I2S_SP_INSTANCE:
|
||||
default:
|
||||
adata->i2ssp_play_stream = substream;
|
||||
}
|
||||
} else {
|
||||
rtd->i2s_instance = pinfo->cap_i2s_instance;
|
||||
else
|
||||
switch (rtd->i2s_instance) {
|
||||
case I2S_BT_INSTANCE:
|
||||
adata->capture_stream = substream;
|
||||
break;
|
||||
case I2S_SP_INSTANCE:
|
||||
default:
|
||||
adata->i2ssp_capture_stream = substream;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pr_err("pinfo failed\n");
|
||||
|
||||
}
|
||||
size = params_buffer_bytes(params);
|
||||
rtd->dma_addr = substream->dma_buffer.addr;
|
||||
rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
|
||||
|
@ -292,7 +303,6 @@ static snd_pcm_uframes_t acp3x_dma_pointer(struct snd_soc_component *component,
|
|||
{
|
||||
struct snd_soc_pcm_runtime *prtd;
|
||||
struct snd_soc_card *card;
|
||||
struct acp3x_platform_info *pinfo;
|
||||
struct i2s_stream_instance *rtd;
|
||||
u32 pos;
|
||||
u32 buffersize;
|
||||
|
@ -301,13 +311,6 @@ static snd_pcm_uframes_t acp3x_dma_pointer(struct snd_soc_component *component,
|
|||
prtd = substream->private_data;
|
||||
card = prtd->card;
|
||||
rtd = substream->runtime->private_data;
|
||||
pinfo = snd_soc_card_get_drvdata(card);
|
||||
if (pinfo) {
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
rtd->i2s_instance = pinfo->play_i2s_instance;
|
||||
else
|
||||
rtd->i2s_instance = pinfo->cap_i2s_instance;
|
||||
}
|
||||
|
||||
buffersize = frames_to_bytes(substream->runtime,
|
||||
substream->runtime->buffer_size);
|
||||
|
@ -531,4 +534,4 @@ MODULE_AUTHOR("Maruthi.Bayyavarapu@amd.com");
|
|||
MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
|
||||
MODULE_DESCRIPTION("AMD ACP 3.x PCM Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
||||
MODULE_ALIAS("platform:"DRV_NAME);
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
# SPDX-License-Identifier: GPL-2.0+
|
||||
# Renoir platform Support
|
||||
snd-rn-pci-acp3x-objs := rn-pci-acp3x.o
|
||||
snd-acp3x-pdm-dma-objs := acp3x-pdm-dma.o
|
||||
obj-$(CONFIG_SND_SOC_AMD_RENOIR) += snd-rn-pci-acp3x.o
|
||||
obj-$(CONFIG_SND_SOC_AMD_RENOIR) += snd-acp3x-pdm-dma.o
|
||||
obj-$(CONFIG_SND_SOC_AMD_RENOIR_MACH) += acp3x-rn.o
|
|
@ -0,0 +1,524 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
//
|
||||
// AMD ALSA SoC PDM Driver
|
||||
//
|
||||
//Copyright 2020 Advanced Micro Devices, Inc.
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dai.h>
|
||||
|
||||
#include "rn_acp3x.h"
|
||||
|
||||
#define DRV_NAME "acp_rn_pdm_dma"
|
||||
|
||||
static const struct snd_pcm_hardware acp_pdm_hardware_capture = {
|
||||
.info = SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
|
||||
.formats = SNDRV_PCM_FMTBIT_S32_LE,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.rate_min = 48000,
|
||||
.rate_max = 48000,
|
||||
.buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,
|
||||
.period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
|
||||
.period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
|
||||
.periods_min = CAPTURE_MIN_NUM_PERIODS,
|
||||
.periods_max = CAPTURE_MAX_NUM_PERIODS,
|
||||
};
|
||||
|
||||
static irqreturn_t pdm_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct pdm_dev_data *rn_pdm_data;
|
||||
u16 cap_flag;
|
||||
u32 val;
|
||||
|
||||
rn_pdm_data = dev_id;
|
||||
if (!rn_pdm_data)
|
||||
return IRQ_NONE;
|
||||
|
||||
cap_flag = 0;
|
||||
val = rn_readl(rn_pdm_data->acp_base + ACP_EXTERNAL_INTR_STAT);
|
||||
if ((val & BIT(PDM_DMA_STAT)) && rn_pdm_data->capture_stream) {
|
||||
rn_writel(BIT(PDM_DMA_STAT), rn_pdm_data->acp_base +
|
||||
ACP_EXTERNAL_INTR_STAT);
|
||||
snd_pcm_period_elapsed(rn_pdm_data->capture_stream);
|
||||
cap_flag = 1;
|
||||
}
|
||||
|
||||
if (cap_flag)
|
||||
return IRQ_HANDLED;
|
||||
else
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static void init_pdm_ring_buffer(u32 physical_addr,
|
||||
u32 buffer_size,
|
||||
u32 watermark_size,
|
||||
void __iomem *acp_base)
|
||||
{
|
||||
rn_writel(physical_addr, acp_base + ACP_WOV_RX_RINGBUFADDR);
|
||||
rn_writel(buffer_size, acp_base + ACP_WOV_RX_RINGBUFSIZE);
|
||||
rn_writel(watermark_size, acp_base + ACP_WOV_RX_INTR_WATERMARK_SIZE);
|
||||
rn_writel(0x01, acp_base + ACPAXI2AXI_ATU_CTRL);
|
||||
}
|
||||
|
||||
static void enable_pdm_clock(void __iomem *acp_base)
|
||||
{
|
||||
u32 pdm_clk_enable, pdm_ctrl;
|
||||
|
||||
pdm_clk_enable = ACP_PDM_CLK_FREQ_MASK;
|
||||
pdm_ctrl = 0x00;
|
||||
|
||||
rn_writel(pdm_clk_enable, acp_base + ACP_WOV_CLK_CTRL);
|
||||
pdm_ctrl = rn_readl(acp_base + ACP_WOV_MISC_CTRL);
|
||||
pdm_ctrl |= ACP_WOV_MISC_CTRL_MASK;
|
||||
rn_writel(pdm_ctrl, acp_base + ACP_WOV_MISC_CTRL);
|
||||
}
|
||||
|
||||
static void enable_pdm_interrupts(void __iomem *acp_base)
|
||||
{
|
||||
u32 ext_int_ctrl;
|
||||
|
||||
ext_int_ctrl = rn_readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
|
||||
ext_int_ctrl |= PDM_DMA_INTR_MASK;
|
||||
rn_writel(ext_int_ctrl, acp_base + ACP_EXTERNAL_INTR_CNTL);
|
||||
}
|
||||
|
||||
static void disable_pdm_interrupts(void __iomem *acp_base)
|
||||
{
|
||||
u32 ext_int_ctrl;
|
||||
|
||||
ext_int_ctrl = rn_readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
|
||||
ext_int_ctrl |= ~PDM_DMA_INTR_MASK;
|
||||
rn_writel(ext_int_ctrl, acp_base + ACP_EXTERNAL_INTR_CNTL);
|
||||
}
|
||||
|
||||
static bool check_pdm_dma_status(void __iomem *acp_base)
|
||||
{
|
||||
bool pdm_dma_status;
|
||||
u32 pdm_enable, pdm_dma_enable;
|
||||
|
||||
pdm_dma_status = false;
|
||||
pdm_enable = rn_readl(acp_base + ACP_WOV_PDM_ENABLE);
|
||||
pdm_dma_enable = rn_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
|
||||
if ((pdm_enable & ACP_PDM_ENABLE) && (pdm_dma_enable &
|
||||
ACP_PDM_DMA_EN_STATUS))
|
||||
pdm_dma_status = true;
|
||||
return pdm_dma_status;
|
||||
}
|
||||
|
||||
static int start_pdm_dma(void __iomem *acp_base)
|
||||
{
|
||||
u32 pdm_enable;
|
||||
u32 pdm_dma_enable;
|
||||
int timeout;
|
||||
|
||||
pdm_enable = 0x01;
|
||||
pdm_dma_enable = 0x01;
|
||||
|
||||
enable_pdm_clock(acp_base);
|
||||
rn_writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
|
||||
rn_writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
|
||||
pdm_dma_enable = 0x00;
|
||||
timeout = 0;
|
||||
while (++timeout < ACP_COUNTER) {
|
||||
pdm_dma_enable = rn_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
|
||||
if ((pdm_dma_enable & 0x02) == ACP_PDM_DMA_EN_STATUS)
|
||||
return 0;
|
||||
udelay(DELAY_US);
|
||||
}
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int stop_pdm_dma(void __iomem *acp_base)
|
||||
{
|
||||
u32 pdm_enable, pdm_dma_enable;
|
||||
int timeout;
|
||||
|
||||
pdm_enable = 0x00;
|
||||
pdm_dma_enable = 0x00;
|
||||
|
||||
pdm_enable = rn_readl(acp_base + ACP_WOV_PDM_ENABLE);
|
||||
pdm_dma_enable = rn_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
|
||||
if (pdm_dma_enable & 0x01) {
|
||||
pdm_dma_enable = 0x02;
|
||||
rn_writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
|
||||
pdm_dma_enable = 0x00;
|
||||
timeout = 0;
|
||||
while (++timeout < ACP_COUNTER) {
|
||||
pdm_dma_enable = rn_readl(acp_base +
|
||||
ACP_WOV_PDM_DMA_ENABLE);
|
||||
if ((pdm_dma_enable & 0x02) == 0x00)
|
||||
break;
|
||||
udelay(DELAY_US);
|
||||
}
|
||||
if (timeout == ACP_COUNTER)
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
if (pdm_enable == ACP_PDM_ENABLE) {
|
||||
pdm_enable = ACP_PDM_DISABLE;
|
||||
rn_writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
|
||||
}
|
||||
rn_writel(0x01, acp_base + ACP_WOV_PDM_FIFO_FLUSH);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void config_acp_dma(struct pdm_stream_instance *rtd, int direction)
|
||||
{
|
||||
u16 page_idx;
|
||||
u32 low, high, val;
|
||||
dma_addr_t addr;
|
||||
|
||||
addr = rtd->dma_addr;
|
||||
val = 0;
|
||||
|
||||
/* Group Enable */
|
||||
rn_writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp_base +
|
||||
ACPAXI2AXI_ATU_BASE_ADDR_GRP_1);
|
||||
rn_writel(PAGE_SIZE_4K_ENABLE, rtd->acp_base +
|
||||
ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1);
|
||||
|
||||
for (page_idx = 0; page_idx < rtd->num_pages; page_idx++) {
|
||||
/* Load the low address of page int ACP SRAM through SRBM */
|
||||
low = lower_32_bits(addr);
|
||||
high = upper_32_bits(addr);
|
||||
|
||||
rn_writel(low, rtd->acp_base + ACP_SCRATCH_REG_0 + val);
|
||||
high |= BIT(31);
|
||||
rn_writel(high, rtd->acp_base + ACP_SCRATCH_REG_0 + val + 4);
|
||||
val += 8;
|
||||
addr += PAGE_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
static int acp_pdm_dma_open(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime;
|
||||
struct pdm_dev_data *adata;
|
||||
struct pdm_stream_instance *pdm_data;
|
||||
int ret;
|
||||
|
||||
runtime = substream->runtime;
|
||||
adata = dev_get_drvdata(component->dev);
|
||||
pdm_data = kzalloc(sizeof(*pdm_data), GFP_KERNEL);
|
||||
if (!pdm_data)
|
||||
return -EINVAL;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
runtime->hw = acp_pdm_hardware_capture;
|
||||
|
||||
ret = snd_pcm_hw_constraint_integer(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
if (ret < 0) {
|
||||
dev_err(component->dev, "set integer constraint failed\n");
|
||||
kfree(pdm_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
enable_pdm_interrupts(adata->acp_base);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
adata->capture_stream = substream;
|
||||
|
||||
pdm_data->acp_base = adata->acp_base;
|
||||
runtime->private_data = pdm_data;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int acp_pdm_dma_hw_params(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct pdm_stream_instance *rtd;
|
||||
size_t size, period_bytes;
|
||||
|
||||
rtd = substream->runtime->private_data;
|
||||
if (!rtd)
|
||||
return -EINVAL;
|
||||
size = params_buffer_bytes(params);
|
||||
period_bytes = params_period_bytes(params);
|
||||
rtd->dma_addr = substream->dma_buffer.addr;
|
||||
rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
|
||||
config_acp_dma(rtd, substream->stream);
|
||||
init_pdm_ring_buffer(MEM_WINDOW_START, size, period_bytes,
|
||||
rtd->acp_base);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u64 acp_pdm_get_byte_count(struct pdm_stream_instance *rtd,
|
||||
int direction)
|
||||
{
|
||||
union acp_pdm_dma_count byte_count;
|
||||
|
||||
byte_count.bcount.high =
|
||||
rn_readl(rtd->acp_base +
|
||||
ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH);
|
||||
byte_count.bcount.low =
|
||||
rn_readl(rtd->acp_base +
|
||||
ACP_WOV_RX_LINEARPOSITIONCNTR_LOW);
|
||||
return byte_count.bytescount;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t acp_pdm_dma_pointer(struct snd_soc_component *comp,
|
||||
struct snd_pcm_substream *stream)
|
||||
{
|
||||
struct pdm_stream_instance *rtd;
|
||||
u32 pos, buffersize;
|
||||
u64 bytescount;
|
||||
|
||||
rtd = stream->runtime->private_data;
|
||||
buffersize = frames_to_bytes(stream->runtime,
|
||||
stream->runtime->buffer_size);
|
||||
bytescount = acp_pdm_get_byte_count(rtd, stream->stream);
|
||||
if (bytescount > rtd->bytescount)
|
||||
bytescount -= rtd->bytescount;
|
||||
pos = do_div(bytescount, buffersize);
|
||||
return bytes_to_frames(stream->runtime, pos);
|
||||
}
|
||||
|
||||
static int acp_pdm_dma_new(struct snd_soc_component *component,
|
||||
struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct device *parent = component->dev->parent;
|
||||
|
||||
snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
|
||||
parent, MIN_BUFFER, MAX_BUFFER);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acp_pdm_dma_mmap(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
return snd_pcm_lib_default_mmap(substream, vma);
|
||||
}
|
||||
|
||||
static int acp_pdm_dma_close(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct pdm_dev_data *adata = dev_get_drvdata(component->dev);
|
||||
|
||||
disable_pdm_interrupts(adata->acp_base);
|
||||
adata->capture_stream = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acp_pdm_dai_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct pdm_stream_instance *rtd;
|
||||
unsigned int ch_mask;
|
||||
|
||||
rtd = substream->runtime->private_data;
|
||||
switch (params_channels(params)) {
|
||||
case TWO_CH:
|
||||
ch_mask = 0x00;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
rn_writel(ch_mask, rtd->acp_base + ACP_WOV_PDM_NO_OF_CHANNELS);
|
||||
rn_writel(PDM_DECIMATION_FACTOR, rtd->acp_base +
|
||||
ACP_WOV_PDM_DECIMATION_FACTOR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acp_pdm_dai_trigger(struct snd_pcm_substream *substream,
|
||||
int cmd, struct snd_soc_dai *dai)
|
||||
{
|
||||
struct pdm_stream_instance *rtd;
|
||||
int ret;
|
||||
bool pdm_status;
|
||||
|
||||
rtd = substream->runtime->private_data;
|
||||
ret = 0;
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
rtd->bytescount = acp_pdm_get_byte_count(rtd,
|
||||
substream->stream);
|
||||
pdm_status = check_pdm_dma_status(rtd->acp_base);
|
||||
if (!pdm_status)
|
||||
ret = start_pdm_dma(rtd->acp_base);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
pdm_status = check_pdm_dma_status(rtd->acp_base);
|
||||
if (pdm_status)
|
||||
ret = stop_pdm_dma(rtd->acp_base);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_ops acp_pdm_dai_ops = {
|
||||
.hw_params = acp_pdm_dai_hw_params,
|
||||
.trigger = acp_pdm_dai_trigger,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver acp_pdm_dai_driver = {
|
||||
.capture = {
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S24_LE |
|
||||
SNDRV_PCM_FMTBIT_S32_LE,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rate_min = 48000,
|
||||
.rate_max = 48000,
|
||||
},
|
||||
.ops = &acp_pdm_dai_ops,
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver acp_pdm_component = {
|
||||
.name = DRV_NAME,
|
||||
.open = acp_pdm_dma_open,
|
||||
.close = acp_pdm_dma_close,
|
||||
.hw_params = acp_pdm_dma_hw_params,
|
||||
.pointer = acp_pdm_dma_pointer,
|
||||
.mmap = acp_pdm_dma_mmap,
|
||||
.pcm_construct = acp_pdm_dma_new,
|
||||
};
|
||||
|
||||
static int acp_pdm_audio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
struct pdm_dev_data *adata;
|
||||
unsigned int irqflags;
|
||||
int status;
|
||||
|
||||
if (!pdev->dev.platform_data) {
|
||||
dev_err(&pdev->dev, "platform_data not retrieved\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
irqflags = *((unsigned int *)(pdev->dev.platform_data));
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
adata = devm_kzalloc(&pdev->dev, sizeof(*adata), GFP_KERNEL);
|
||||
if (!adata)
|
||||
return -ENOMEM;
|
||||
|
||||
adata->acp_base = devm_ioremap(&pdev->dev, res->start,
|
||||
resource_size(res));
|
||||
if (!adata->acp_base)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
adata->pdm_irq = res->start;
|
||||
adata->capture_stream = NULL;
|
||||
|
||||
dev_set_drvdata(&pdev->dev, adata);
|
||||
status = devm_snd_soc_register_component(&pdev->dev,
|
||||
&acp_pdm_component,
|
||||
&acp_pdm_dai_driver, 1);
|
||||
if (status) {
|
||||
dev_err(&pdev->dev, "Fail to register acp pdm dai\n");
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
status = devm_request_irq(&pdev->dev, adata->pdm_irq, pdm_irq_handler,
|
||||
irqflags, "ACP_PDM_IRQ", adata);
|
||||
if (status) {
|
||||
dev_err(&pdev->dev, "ACP PDM IRQ request failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_allow(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acp_pdm_audio_remove(struct platform_device *pdev)
|
||||
{
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acp_pdm_resume(struct device *dev)
|
||||
{
|
||||
struct pdm_dev_data *adata;
|
||||
struct snd_pcm_runtime *runtime;
|
||||
struct pdm_stream_instance *rtd;
|
||||
u32 period_bytes, buffer_len;
|
||||
|
||||
adata = dev_get_drvdata(dev);
|
||||
if (adata->capture_stream && adata->capture_stream->runtime) {
|
||||
runtime = adata->capture_stream->runtime;
|
||||
rtd = runtime->private_data;
|
||||
period_bytes = frames_to_bytes(runtime, runtime->period_size);
|
||||
buffer_len = frames_to_bytes(runtime, runtime->buffer_size);
|
||||
config_acp_dma(rtd, SNDRV_PCM_STREAM_CAPTURE);
|
||||
init_pdm_ring_buffer(MEM_WINDOW_START, buffer_len, period_bytes,
|
||||
adata->acp_base);
|
||||
}
|
||||
enable_pdm_interrupts(adata->acp_base);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acp_pdm_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct pdm_dev_data *adata;
|
||||
|
||||
adata = dev_get_drvdata(dev);
|
||||
disable_pdm_interrupts(adata->acp_base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acp_pdm_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct pdm_dev_data *adata;
|
||||
|
||||
adata = dev_get_drvdata(dev);
|
||||
enable_pdm_interrupts(adata->acp_base);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops acp_pdm_pm_ops = {
|
||||
.runtime_suspend = acp_pdm_runtime_suspend,
|
||||
.runtime_resume = acp_pdm_runtime_resume,
|
||||
.resume = acp_pdm_resume,
|
||||
};
|
||||
|
||||
static struct platform_driver acp_pdm_dma_driver = {
|
||||
.probe = acp_pdm_audio_probe,
|
||||
.remove = acp_pdm_audio_remove,
|
||||
.driver = {
|
||||
.name = "acp_rn_pdm_dma",
|
||||
.pm = &acp_pdm_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(acp_pdm_dma_driver);
|
||||
|
||||
MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
|
||||
MODULE_DESCRIPTION("AMD ACP3x Renior PDM Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
|
@ -0,0 +1,77 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
//
|
||||
// Machine driver for AMD Renoir platform using DMIC
|
||||
//
|
||||
//Copyright 2020 Advanced Micro Devices, Inc.
|
||||
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <linux/module.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include "rn_acp3x.h"
|
||||
|
||||
#define DRV_NAME "acp_pdm_mach"
|
||||
|
||||
SND_SOC_DAILINK_DEF(acp_pdm,
|
||||
DAILINK_COMP_ARRAY(COMP_CPU("acp_rn_pdm_dma.0")));
|
||||
|
||||
SND_SOC_DAILINK_DEF(dmic_codec,
|
||||
DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec.0",
|
||||
"dmic-hifi")));
|
||||
|
||||
SND_SOC_DAILINK_DEF(platform,
|
||||
DAILINK_COMP_ARRAY(COMP_PLATFORM("acp_rn_pdm_dma.0")));
|
||||
|
||||
static struct snd_soc_dai_link acp_dai_pdm[] = {
|
||||
{
|
||||
.name = "acp3x-dmic-capture",
|
||||
.stream_name = "DMIC capture",
|
||||
.capture_only = 1,
|
||||
SND_SOC_DAILINK_REG(acp_pdm, dmic_codec, platform),
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_card acp_card = {
|
||||
.name = "acp",
|
||||
.owner = THIS_MODULE,
|
||||
.dai_link = acp_dai_pdm,
|
||||
.num_links = 1,
|
||||
};
|
||||
|
||||
static int acp_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct acp_pdm *machine = NULL;
|
||||
struct snd_soc_card *card;
|
||||
|
||||
card = &acp_card;
|
||||
acp_card.dev = &pdev->dev;
|
||||
|
||||
platform_set_drvdata(pdev, card);
|
||||
snd_soc_card_set_drvdata(card, machine);
|
||||
ret = devm_snd_soc_register_card(&pdev->dev, card);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"snd_soc_register_card(%s) failed: %d\n",
|
||||
acp_card.name, ret);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver acp_mach_driver = {
|
||||
.driver = {
|
||||
.name = "acp_pdm_mach",
|
||||
.pm = &snd_soc_pm_ops,
|
||||
},
|
||||
.probe = acp_probe,
|
||||
};
|
||||
|
||||
module_platform_driver(acp_mach_driver);
|
||||
|
||||
MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
|
@ -0,0 +1,344 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
//
|
||||
// AMD Renoir ACP PCI Driver
|
||||
//
|
||||
//Copyright 2020 Advanced Micro Devices, Inc.
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include "rn_acp3x.h"
|
||||
|
||||
static int acp_power_gating;
|
||||
module_param(acp_power_gating, int, 0644);
|
||||
MODULE_PARM_DESC(acp_power_gating, "Enable acp power gating");
|
||||
|
||||
struct acp_dev_data {
|
||||
void __iomem *acp_base;
|
||||
struct resource *res;
|
||||
struct platform_device *pdev[ACP_DEVS];
|
||||
};
|
||||
|
||||
static int rn_acp_power_on(void __iomem *acp_base)
|
||||
{
|
||||
u32 val;
|
||||
int timeout;
|
||||
|
||||
val = rn_readl(acp_base + ACP_PGFSM_STATUS);
|
||||
|
||||
if (val == 0)
|
||||
return val;
|
||||
|
||||
if ((val & ACP_PGFSM_STATUS_MASK) !=
|
||||
ACP_POWER_ON_IN_PROGRESS)
|
||||
rn_writel(ACP_PGFSM_CNTL_POWER_ON_MASK,
|
||||
acp_base + ACP_PGFSM_CONTROL);
|
||||
timeout = 0;
|
||||
while (++timeout < 500) {
|
||||
val = rn_readl(acp_base + ACP_PGFSM_STATUS);
|
||||
if (!val)
|
||||
return 0;
|
||||
udelay(1);
|
||||
}
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int rn_acp_power_off(void __iomem *acp_base)
|
||||
{
|
||||
u32 val;
|
||||
int timeout;
|
||||
|
||||
rn_writel(ACP_PGFSM_CNTL_POWER_OFF_MASK,
|
||||
acp_base + ACP_PGFSM_CONTROL);
|
||||
timeout = 0;
|
||||
while (++timeout < 500) {
|
||||
val = rn_readl(acp_base + ACP_PGFSM_STATUS);
|
||||
if ((val & ACP_PGFSM_STATUS_MASK) == ACP_POWERED_OFF)
|
||||
return 0;
|
||||
udelay(1);
|
||||
}
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int rn_acp_reset(void __iomem *acp_base)
|
||||
{
|
||||
u32 val;
|
||||
int timeout;
|
||||
|
||||
rn_writel(1, acp_base + ACP_SOFT_RESET);
|
||||
timeout = 0;
|
||||
while (++timeout < 500) {
|
||||
val = rn_readl(acp_base + ACP_SOFT_RESET);
|
||||
if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK)
|
||||
break;
|
||||
cpu_relax();
|
||||
}
|
||||
rn_writel(0, acp_base + ACP_SOFT_RESET);
|
||||
timeout = 0;
|
||||
while (++timeout < 500) {
|
||||
val = rn_readl(acp_base + ACP_SOFT_RESET);
|
||||
if (!val)
|
||||
return 0;
|
||||
cpu_relax();
|
||||
}
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static void rn_acp_enable_interrupts(void __iomem *acp_base)
|
||||
{
|
||||
u32 ext_intr_ctrl;
|
||||
|
||||
rn_writel(0x01, acp_base + ACP_EXTERNAL_INTR_ENB);
|
||||
ext_intr_ctrl = rn_readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
|
||||
ext_intr_ctrl |= ACP_ERROR_MASK;
|
||||
rn_writel(ext_intr_ctrl, acp_base + ACP_EXTERNAL_INTR_CNTL);
|
||||
}
|
||||
|
||||
static void rn_acp_disable_interrupts(void __iomem *acp_base)
|
||||
{
|
||||
rn_writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base +
|
||||
ACP_EXTERNAL_INTR_STAT);
|
||||
rn_writel(0x00, acp_base + ACP_EXTERNAL_INTR_ENB);
|
||||
}
|
||||
|
||||
static int rn_acp_init(void __iomem *acp_base)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* power on */
|
||||
ret = rn_acp_power_on(acp_base);
|
||||
if (ret) {
|
||||
pr_err("ACP power on failed\n");
|
||||
return ret;
|
||||
}
|
||||
rn_writel(0x01, acp_base + ACP_CONTROL);
|
||||
/* Reset */
|
||||
ret = rn_acp_reset(acp_base);
|
||||
if (ret) {
|
||||
pr_err("ACP reset failed\n");
|
||||
return ret;
|
||||
}
|
||||
rn_writel(0x03, acp_base + ACP_CLKMUX_SEL);
|
||||
rn_acp_enable_interrupts(acp_base);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rn_acp_deinit(void __iomem *acp_base)
|
||||
{
|
||||
int ret;
|
||||
|
||||
rn_acp_disable_interrupts(acp_base);
|
||||
/* Reset */
|
||||
ret = rn_acp_reset(acp_base);
|
||||
if (ret) {
|
||||
pr_err("ACP reset failed\n");
|
||||
return ret;
|
||||
}
|
||||
rn_writel(0x00, acp_base + ACP_CLKMUX_SEL);
|
||||
rn_writel(0x00, acp_base + ACP_CONTROL);
|
||||
/* power off */
|
||||
if (acp_power_gating) {
|
||||
ret = rn_acp_power_off(acp_base);
|
||||
if (ret) {
|
||||
pr_err("ACP power off failed\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_rn_acp_probe(struct pci_dev *pci,
|
||||
const struct pci_device_id *pci_id)
|
||||
{
|
||||
struct acp_dev_data *adata;
|
||||
struct platform_device_info pdevinfo[ACP_DEVS];
|
||||
unsigned int irqflags;
|
||||
int ret, index;
|
||||
u32 addr;
|
||||
|
||||
if (pci_enable_device(pci)) {
|
||||
dev_err(&pci->dev, "pci_enable_device failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = pci_request_regions(pci, "AMD ACP3x audio");
|
||||
if (ret < 0) {
|
||||
dev_err(&pci->dev, "pci_request_regions failed\n");
|
||||
goto disable_pci;
|
||||
}
|
||||
|
||||
adata = devm_kzalloc(&pci->dev, sizeof(struct acp_dev_data),
|
||||
GFP_KERNEL);
|
||||
if (!adata) {
|
||||
ret = -ENOMEM;
|
||||
goto release_regions;
|
||||
}
|
||||
|
||||
/* check for msi interrupt support */
|
||||
ret = pci_enable_msi(pci);
|
||||
if (ret)
|
||||
/* msi is not enabled */
|
||||
irqflags = IRQF_SHARED;
|
||||
else
|
||||
/* msi is enabled */
|
||||
irqflags = 0;
|
||||
|
||||
addr = pci_resource_start(pci, 0);
|
||||
adata->acp_base = devm_ioremap(&pci->dev, addr,
|
||||
pci_resource_len(pci, 0));
|
||||
if (!adata->acp_base) {
|
||||
ret = -ENOMEM;
|
||||
goto disable_msi;
|
||||
}
|
||||
pci_set_master(pci);
|
||||
pci_set_drvdata(pci, adata);
|
||||
ret = rn_acp_init(adata->acp_base);
|
||||
if (ret)
|
||||
goto disable_msi;
|
||||
|
||||
adata->res = devm_kzalloc(&pci->dev,
|
||||
sizeof(struct resource) * 2,
|
||||
GFP_KERNEL);
|
||||
if (!adata->res) {
|
||||
ret = -ENOMEM;
|
||||
goto de_init;
|
||||
}
|
||||
|
||||
adata->res[0].name = "acp_pdm_iomem";
|
||||
adata->res[0].flags = IORESOURCE_MEM;
|
||||
adata->res[0].start = addr;
|
||||
adata->res[0].end = addr + (ACP_REG_END - ACP_REG_START);
|
||||
adata->res[1].name = "acp_pdm_irq";
|
||||
adata->res[1].flags = IORESOURCE_IRQ;
|
||||
adata->res[1].start = pci->irq;
|
||||
adata->res[1].end = pci->irq;
|
||||
|
||||
memset(&pdevinfo, 0, sizeof(pdevinfo));
|
||||
pdevinfo[0].name = "acp_rn_pdm_dma";
|
||||
pdevinfo[0].id = 0;
|
||||
pdevinfo[0].parent = &pci->dev;
|
||||
pdevinfo[0].num_res = 2;
|
||||
pdevinfo[0].res = adata->res;
|
||||
pdevinfo[0].data = &irqflags;
|
||||
pdevinfo[0].size_data = sizeof(irqflags);
|
||||
|
||||
pdevinfo[1].name = "dmic-codec";
|
||||
pdevinfo[1].id = 0;
|
||||
pdevinfo[1].parent = &pci->dev;
|
||||
pdevinfo[2].name = "acp_pdm_mach";
|
||||
pdevinfo[2].id = 0;
|
||||
pdevinfo[2].parent = &pci->dev;
|
||||
for (index = 0; index < ACP_DEVS; index++) {
|
||||
adata->pdev[index] =
|
||||
platform_device_register_full(&pdevinfo[index]);
|
||||
if (IS_ERR(adata->pdev[index])) {
|
||||
dev_err(&pci->dev, "cannot register %s device\n",
|
||||
pdevinfo[index].name);
|
||||
ret = PTR_ERR(adata->pdev[index]);
|
||||
goto unregister_devs;
|
||||
}
|
||||
}
|
||||
pm_runtime_set_autosuspend_delay(&pci->dev, ACP_SUSPEND_DELAY_MS);
|
||||
pm_runtime_use_autosuspend(&pci->dev);
|
||||
pm_runtime_put_noidle(&pci->dev);
|
||||
pm_runtime_allow(&pci->dev);
|
||||
return 0;
|
||||
|
||||
unregister_devs:
|
||||
for (index = 0; index < ACP_DEVS; index++)
|
||||
platform_device_unregister(adata->pdev[index]);
|
||||
de_init:
|
||||
if (rn_acp_deinit(adata->acp_base))
|
||||
dev_err(&pci->dev, "ACP de-init failed\n");
|
||||
disable_msi:
|
||||
pci_disable_msi(pci);
|
||||
release_regions:
|
||||
pci_release_regions(pci);
|
||||
disable_pci:
|
||||
pci_disable_device(pci);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int snd_rn_acp_suspend(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct acp_dev_data *adata;
|
||||
|
||||
adata = dev_get_drvdata(dev);
|
||||
ret = rn_acp_deinit(adata->acp_base);
|
||||
if (ret)
|
||||
dev_err(dev, "ACP de-init failed\n");
|
||||
else
|
||||
dev_dbg(dev, "ACP de-initialized\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int snd_rn_acp_resume(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct acp_dev_data *adata;
|
||||
|
||||
adata = dev_get_drvdata(dev);
|
||||
ret = rn_acp_init(adata->acp_base);
|
||||
if (ret) {
|
||||
dev_err(dev, "ACP init failed\n");
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops rn_acp_pm = {
|
||||
.runtime_suspend = snd_rn_acp_suspend,
|
||||
.runtime_resume = snd_rn_acp_resume,
|
||||
.suspend = snd_rn_acp_suspend,
|
||||
.resume = snd_rn_acp_resume,
|
||||
};
|
||||
|
||||
static void snd_rn_acp_remove(struct pci_dev *pci)
|
||||
{
|
||||
struct acp_dev_data *adata;
|
||||
int ret, index;
|
||||
|
||||
adata = pci_get_drvdata(pci);
|
||||
for (index = 0; index < ACP_DEVS; index++)
|
||||
platform_device_unregister(adata->pdev[index]);
|
||||
ret = rn_acp_deinit(adata->acp_base);
|
||||
if (ret)
|
||||
dev_err(&pci->dev, "ACP de-init failed\n");
|
||||
pm_runtime_forbid(&pci->dev);
|
||||
pm_runtime_get_noresume(&pci->dev);
|
||||
pci_disable_msi(pci);
|
||||
pci_release_regions(pci);
|
||||
pci_disable_device(pci);
|
||||
}
|
||||
|
||||
static const struct pci_device_id snd_rn_acp_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_DEVICE_ID),
|
||||
.class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
|
||||
.class_mask = 0xffffff },
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, snd_rn_acp_ids);
|
||||
|
||||
static struct pci_driver rn_acp_driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.id_table = snd_rn_acp_ids,
|
||||
.probe = snd_rn_acp_probe,
|
||||
.remove = snd_rn_acp_remove,
|
||||
.driver = {
|
||||
.pm = &rn_acp_pm,
|
||||
}
|
||||
};
|
||||
|
||||
module_pci_driver(rn_acp_driver);
|
||||
|
||||
MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
|
||||
MODULE_DESCRIPTION("AMD ACP Renoir PCI driver");
|
||||
MODULE_LICENSE("GPL v2");
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue