sound updates for 5.2-rc1
The most significant changes at this cycle are the Sound Open Firmware support from Intel for the common DSP framework along with its support for Intel platforms. It's a door opened to a real "free" firmware (in the sense of FOSS), and other parties show interests in it. In addition to SOF, we've got a bunch of updates and fixes as usual. Some highlights are below. ALSA core: - Cleanups and fixes in ALSA timer code to cover some races spotted by syzkaller - Cleanups and fixes in ALSA sequencer code to cover some races, again unsurprisingly, spotted by syzkaller - Optimize the common page allocation helper with alloc_pages_exact() ASoC: - Add SOF core support, as well as Intel SOF platform support - Generic card driver improvements: support for MCLK/sample rate ratio and pin switches - A big set of improvements to TLV320AIC32x4 drivers - New drivers for Freescale audio mixers, several Intel machines, several Mediatek machines, Meson G12A, Spreadtrum compressed audio and DMA devices HD-audio: - A few Realtek codec fixes for reducing pop noises - Quirks for Chromebooks - Workaround for faulty connection report on AMD/Nvidia HDMI Others: - A quirk for Focusrite Scarlett Solo USB-audio - Add support for MOTU 8pre FireWire - 24bit sample format support in aloop - GUS patch format support (finally, over a decade) in native emux synth code -----BEGIN PGP SIGNATURE----- iQJCBAABCAAsFiEEIXTw5fNLNI7mMiVaLtJE4w1nLE8FAlzUKVAOHHRpd2FpQHN1 c2UuZGUACgkQLtJE4w1nLE/FtQ/+LWWV9wIXeKYCSxKUdUF21Cpdo9eh3P55MTIR ooxbasNywsllYC1l2IXYi/HYKUlpIwg7kA2itfxaPSW7pveWKPt4mufJsvWpRZws XUre1MPR9vzTwDqhiG+8rH3osS0YVu9kOipRej043hFHsnXxbOfsnSQ2I+tst6tS 12s5Hmhx/YFyR6fJMCIm3CoR59spz/SWXftna6bnkKbEYkeDmI3TtM1s9jum3U5F KReBigp1qffRzSjUYhw8YRujkJPqB65cLGguRyPDkuUoDbLzhu2QBNXwlQvk2+rH bgreE7hqmm2ZwRnjSwJFtrFSuOfULjBjXqv2QMG+Lby5e/VzrzV0wdZJd2YXk9VX VzhjiHmAGI2l30JClGuz9wN2MXuvtQFr2dJF56Nuw535mwuBUzxQwXY5qkX6uJ1z KXADrUz7atA2VVYRgPEj8D30c1RlAzmiPLTxLTKouk0LdcOih90z/SaT/qlUHfsA 7TUaYBJt6PKbmrGK+PxUTpw/M88w+frbcV+PZNAynLeJfiRROl1Yg/6Tc0QC9nts cHs9Migp2tf37gEvYcXFEPHI/A+sfIb5RZuTQySpUN09vODb37cZXl+lI4FuLGcJ F4d45b05gNcybt+SFj63CGpW+k4QO8M6TUzy7gvQ0DdUmhd0mEvAmGfEPBoaBsau 5uJEfOQ= =0tyg -----END PGP SIGNATURE----- Merge tag 'sound-5.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound Pull sound updates from Takashi Iwai: "The most significant changes at this cycle are the Sound Open Firmware support from Intel for the common DSP framework along with its support for Intel platforms. It's a door opened to a real "free" firmware (in the sense of FOSS), and other parties show interests in it. In addition to SOF, we've got a bunch of updates and fixes as usual. Some highlights are below. ALSA core: - Cleanups and fixes in ALSA timer code to cover some races spotted by syzkaller - Cleanups and fixes in ALSA sequencer code to cover some races, again unsurprisingly, spotted by syzkaller - Optimize the common page allocation helper with alloc_pages_exact() ASoC: - Add SOF core support, as well as Intel SOF platform support - Generic card driver improvements: support for MCLK/sample rate ratio and pin switches - A big set of improvements to TLV320AIC32x4 drivers - New drivers for Freescale audio mixers, several Intel machines, several Mediatek machines, Meson G12A, Spreadtrum compressed audio and DMA devices HD-audio: - A few Realtek codec fixes for reducing pop noises - Quirks for Chromebooks - Workaround for faulty connection report on AMD/Nvidia HDMI Others: - A quirk for Focusrite Scarlett Solo USB-audio - Add support for MOTU 8pre FireWire - 24bit sample format support in aloop - GUS patch format support (finally, over a decade) in native emux synth code" * tag 'sound-5.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (375 commits) ASoC: SOF: Fix unused variable warnings ALSA: line6: toneport: Fix broken usage of timer for delayed execution ALSA: aica: Fix a long-time build breakage ALSA: hda/realtek - Support low power consumption for ALC256 ASoC: stm32: i2s: update pcm hardware constraints ASoC: codec: hdac_hdmi: no checking monitor in hw_params ASoC: mediatek: mt6358: save PGA for mixer control ASoC: mediatek: mt6358: save output volume for mixer controls ASoC: mediatek: mt6358: initialize setting when ramping volume ASoC: SOF: core: fix undefined nocodec reference ASoC: SOF: xtensa: fix undefined references ASoC: SOF: Propagate sof_get_ctrl_copy_params() error properly ALSA: hdea/realtek - Headset fixup for System76 Gazelle (gaze14) ALSA: hda/intel: add CometLake PCI IDs ALSA: hda/realtek - Support low power consumption for ALC295 ASoC: rockchip: Fix an uninitialized variable compile warning ASoC: SOF: Fix a compile warning with CONFIG_PCI=n ASoC: da7219: Fix a compile warning at CONFIG_COMMON_CLK=n ASoC: sound/soc/sof/: fix kconfig dependency warning ASoC: stm32: spdifrx: change trace level on iec control ...
This commit is contained in:
commit
e57ccca1ba
|
@ -1,5 +1,8 @@
|
|||
ADI AXI-I2S controller
|
||||
|
||||
The core can be generated with transmit (playback), only receive
|
||||
(capture) or both directions enabled.
|
||||
|
||||
Required properties:
|
||||
- compatible : Must be "adi,axi-i2s-1.00.a"
|
||||
- reg : Must contain I2S core's registers location and length
|
||||
|
@ -9,8 +12,8 @@ Required properties:
|
|||
- clock-names : "axi" for the clock to the AXI interface, "ref" for the sample
|
||||
rate reference clock.
|
||||
- dmas: Pairs of phandle and specifier for the DMA channels that are used by
|
||||
the core. The core expects two dma channels, one for transmit and one for
|
||||
receive.
|
||||
the core. The core expects two dma channels if both transmit and receive are
|
||||
enabled, one channel otherwise.
|
||||
- dma-names : "tx" for the transmit channel, "rx" for the receive channel.
|
||||
|
||||
For more details on the 'dma', 'dma-names', 'clock' and 'clock-names' properties
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
|
||||
Required properties:
|
||||
- compatible: 'amlogic,axg-toddr' or
|
||||
'amlogic,axg-frddr'
|
||||
'amlogic,axg-toddr' or
|
||||
'amlogic,g12a-frddr' or
|
||||
'amlogic,g12a-toddr'
|
||||
- reg: physical base address of the controller and length of memory
|
||||
mapped region.
|
||||
- interrupts: interrupt specifier for the fifo.
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
* Amlogic Audio PDM input
|
||||
|
||||
Required properties:
|
||||
- compatible: 'amlogic,axg-pdm'
|
||||
- compatible: 'amlogic,axg-pdm' or
|
||||
'amlogic,g12a-pdm'
|
||||
- reg: physical base address of the controller and length of memory
|
||||
mapped region.
|
||||
- clocks: list of clock phandle, one for each entry clock-names.
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
* Amlogic Audio SPDIF Input
|
||||
|
||||
Required properties:
|
||||
- compatible: 'amlogic,axg-spdifin'
|
||||
- compatible: 'amlogic,axg-spdifin' or
|
||||
'amlogic,g12a-spdifin'
|
||||
- interrupts: interrupt specifier for the spdif input.
|
||||
- clocks: list of clock phandle, one for each entry clock-names.
|
||||
- clock-names: should contain the following:
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
* Amlogic Audio SPDIF Output
|
||||
|
||||
Required properties:
|
||||
- compatible: 'amlogic,axg-spdifout'
|
||||
- compatible: 'amlogic,axg-spdifout' or
|
||||
'amlogic,g12a-spdifout'
|
||||
- clocks: list of clock phandle, one for each entry clock-names.
|
||||
- clock-names: should contain the following:
|
||||
* "pclk" : peripheral clock.
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
|
||||
Required properties:
|
||||
- compatible: 'amlogic,axg-tdmin' or
|
||||
'amlogic,axg-tdmout'
|
||||
'amlogic,axg-tdmout' or
|
||||
'amlogic,g12a-tdmin' or
|
||||
'amlogic,g12a-tdmout'
|
||||
- reg: physical base address of the controller and length of memory
|
||||
mapped region.
|
||||
- clocks: list of clock phandle, one for each entry clock-names.
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
Cirrus Logic Lochnagar Audio Development Board
|
||||
|
||||
Lochnagar is an evaluation and development board for Cirrus Logic
|
||||
Smart CODEC and Amp devices. It allows the connection of most Cirrus
|
||||
Logic devices on mini-cards, as well as allowing connection of
|
||||
various application processor systems to provide a full evaluation
|
||||
platform. Audio system topology, clocking and power can all be
|
||||
controlled through the Lochnagar, allowing the device under test
|
||||
to be used in a variety of possible use cases.
|
||||
|
||||
This binding document describes the binding for the audio portion
|
||||
of the driver.
|
||||
|
||||
This binding must be part of the Lochnagar MFD binding:
|
||||
[4] ../mfd/cirrus,lochnagar.txt
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : One of the following strings:
|
||||
"cirrus,lochnagar2-soundcard"
|
||||
|
||||
- #sound-dai-cells : Must be set to 1.
|
||||
|
||||
- clocks : Contains an entry for each entry in clock-names.
|
||||
- clock-names : Must include the following clocks:
|
||||
"mclk" Master clock source for the sound card, should normally
|
||||
be set to LOCHNAGAR_SOUNDCARD_MCLK provided by the Lochnagar
|
||||
clock driver.
|
||||
|
||||
Example:
|
||||
|
||||
lochnagar-sc {
|
||||
compatible = "cirrus,lochnagar2-soundcard";
|
||||
|
||||
#sound-dai-cells = <1>;
|
||||
|
||||
clocks = <&lochnagar_clk LOCHNAGAR_SOUNDCARD_MCLK>;
|
||||
clock-names = "mclk";
|
||||
};
|
|
@ -1,6 +1,17 @@
|
|||
CS42L51 audio CODEC
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "cirrus,cs42l51"
|
||||
|
||||
- reg : the I2C address of the device for I2C.
|
||||
|
||||
Optional properties:
|
||||
- VL-supply, VD-supply, VA-supply, VAHP-supply: power supplies for the device,
|
||||
as covered in Documentation/devicetree/bindings/regulator/regulator.txt.
|
||||
|
||||
- reset-gpios : GPIO specification for the reset pin. If specified, it will be
|
||||
deasserted before starting the communication with the codec.
|
||||
|
||||
- clocks : a list of phandles + clock-specifiers, one for each entry in
|
||||
clock-names
|
||||
|
@ -14,4 +25,9 @@ cs42l51: cs42l51@4a {
|
|||
reg = <0x4a>;
|
||||
clocks = <&mclk_prov>;
|
||||
clock-names = "MCLK";
|
||||
VL-supply = <®_audio>;
|
||||
VD-supply = <®_audio>;
|
||||
VA-supply = <®_audio>;
|
||||
VAHP-supply = <®_audio>;
|
||||
reset-gpios = <&gpiog 9 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
|
|
|
@ -23,8 +23,8 @@ Optional properties:
|
|||
interrupt is to be used to wake system, otherwise "irq" should be used.
|
||||
- wakeup-source: Flag to indicate this device can wake system (suspend/resume).
|
||||
|
||||
- #clock-cells : Should be set to '<0>', only one clock source provided;
|
||||
- clock-output-names : Name given for DAI clocks output;
|
||||
- #clock-cells : Should be set to '<1>', two clock sources provided;
|
||||
- clock-output-names : Names given for DAI clock outputs (WCLK & BCLK);
|
||||
|
||||
- clocks : phandle and clock specifier for codec MCLK.
|
||||
- clock-names : Clock name string for 'clocks' attribute, should be "mclk".
|
||||
|
@ -84,8 +84,8 @@ Example:
|
|||
VDDMIC-supply = <®_audio>;
|
||||
VDDIO-supply = <®_audio>;
|
||||
|
||||
#clock-cells = <0>;
|
||||
clock-output-names = "dai-clks";
|
||||
#clock-cells = <1>;
|
||||
clock-output-names = "dai-wclk", "dai-bclk";
|
||||
|
||||
clocks = <&clks 201>;
|
||||
clock-names = "mclk";
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
NXP Audio Mixer (AUDMIX).
|
||||
|
||||
The Audio Mixer is a on-chip functional module that allows mixing of two
|
||||
audio streams into a single audio stream. Audio Mixer has two input serial
|
||||
audio interfaces. These are driven by two Synchronous Audio interface
|
||||
modules (SAI). Each input serial interface carries 8 audio channels in its
|
||||
frame in TDM manner. Mixer mixes audio samples of corresponding channels
|
||||
from two interfaces into a single sample. Before mixing, audio samples of
|
||||
two inputs can be attenuated based on configuration. The output of the
|
||||
Audio Mixer is also a serial audio interface. Like input interfaces it has
|
||||
the same TDM frame format. This output is used to drive the serial DAC TDM
|
||||
interface of audio codec and also sent to the external pins along with the
|
||||
receive path of normal audio SAI module for readback by the CPU.
|
||||
|
||||
The output of Audio Mixer can be selected from any of the three streams
|
||||
- serial audio input 1
|
||||
- serial audio input 2
|
||||
- mixed audio
|
||||
|
||||
Mixing operation is independent of audio sample rate but the two audio
|
||||
input streams must have same audio sample rate with same number of channels
|
||||
in TDM frame to be eligible for mixing.
|
||||
|
||||
Device driver required properties:
|
||||
=================================
|
||||
- compatible : Compatible list, contains "fsl,imx8qm-audmix"
|
||||
|
||||
- reg : Offset and length of the register set for the device.
|
||||
|
||||
- clocks : Must contain an entry for each entry in clock-names.
|
||||
|
||||
- clock-names : Must include the "ipg" for register access.
|
||||
|
||||
- power-domains : Must contain the phandle to AUDMIX power domain node
|
||||
|
||||
- dais : Must contain a list of phandles to AUDMIX connected
|
||||
DAIs. The current implementation requires two phandles
|
||||
to SAI interfaces to be provided, the first SAI in the
|
||||
list being used to route the AUDMIX output.
|
||||
|
||||
Device driver configuration example:
|
||||
======================================
|
||||
audmix: audmix@59840000 {
|
||||
compatible = "fsl,imx8qm-audmix";
|
||||
reg = <0x0 0x59840000 0x0 0x10000>;
|
||||
clocks = <&clk IMX8QXP_AUD_AUDMIX_IPG>;
|
||||
clock-names = "ipg";
|
||||
power-domains = <&pd_audmix>;
|
||||
dais = <&sai4>, <&sai5>;
|
||||
};
|
|
@ -0,0 +1,43 @@
|
|||
* Microchip I2S Multi-Channel Controller
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "microchip,sam9x60-i2smcc".
|
||||
- reg: Should be the physical base address of the controller and the
|
||||
length of memory mapped region.
|
||||
- interrupts: Should contain the interrupt for the controller.
|
||||
- dmas: Should be one per channel name listed in the dma-names property,
|
||||
as described in atmel-dma.txt and dma.txt files.
|
||||
- dma-names: Identifier string for each DMA request line in the dmas property.
|
||||
Two dmas have to be defined, "tx" and "rx".
|
||||
- clocks: Must contain an entry for each entry in clock-names.
|
||||
Please refer to clock-bindings.txt.
|
||||
- clock-names: Should be one of each entry matching the clocks phandles list:
|
||||
- "pclk" (peripheral clock) Required.
|
||||
- "gclk" (generated clock) Optional (1).
|
||||
|
||||
Optional properties:
|
||||
- pinctrl-0: Should specify pin control groups used for this controller.
|
||||
- princtrl-names: Should contain only one value - "default".
|
||||
|
||||
|
||||
(1) : Only the peripheral clock is required. The generated clock is optional
|
||||
and should be set mostly when Master Mode is required.
|
||||
|
||||
Example:
|
||||
|
||||
i2s@f001c000 {
|
||||
compatible = "microchip,sam9x60-i2smcc";
|
||||
reg = <0xf001c000 0x100>;
|
||||
interrupts = <34 IRQ_TYPE_LEVEL_HIGH 7>;
|
||||
dmas = <&dma0
|
||||
(AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) |
|
||||
AT91_XDMAC_DT_PERID(36))>,
|
||||
<&dma0
|
||||
(AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) |
|
||||
AT91_XDMAC_DT_PERID(37))>;
|
||||
dma-names = "tx", "rx";
|
||||
clocks = <&i2s_clk>, <&i2s_gclk>;
|
||||
clock-names = "pclk", "gclk";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_i2s_default>;
|
||||
};
|
|
@ -0,0 +1,15 @@
|
|||
MT8183 with MT6358, DA7219 and MAX98357 CODECS
|
||||
|
||||
Required properties:
|
||||
- compatible : "mediatek,mt8183_da7219_max98357"
|
||||
- mediatek,headset-codec: the phandles of da7219 codecs
|
||||
- mediatek,platform: the phandle of MT8183 ASoC platform
|
||||
|
||||
Example:
|
||||
|
||||
sound {
|
||||
compatible = "mediatek,mt8183_da7219_max98357";
|
||||
mediatek,headset-codec = <&da7219>;
|
||||
mediatek,platform = <&afe>;
|
||||
};
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
MT8183 with MT6358, TS3A227 and MAX98357 CODECS
|
||||
|
||||
Required properties:
|
||||
- compatible : "mediatek,mt8183_mt6358_ts3a227_max98357"
|
||||
- mediatek,headset-codec: the phandles of ts3a227 codecs
|
||||
- mediatek,platform: the phandle of MT8183 ASoC platform
|
||||
|
||||
Example:
|
||||
|
||||
sound {
|
||||
compatible = "mediatek,mt8183_mt6358_ts3a227_max98357";
|
||||
mediatek,headset-codec = <&ts3a227>;
|
||||
mediatek,platform = <&afe>;
|
||||
};
|
||||
|
|
@ -266,6 +266,7 @@ Required properties:
|
|||
- "renesas,rcar_sound-r8a7743" (RZ/G1M)
|
||||
- "renesas,rcar_sound-r8a7744" (RZ/G1N)
|
||||
- "renesas,rcar_sound-r8a7745" (RZ/G1E)
|
||||
- "renesas,rcar_sound-r8a77470" (RZ/G1C)
|
||||
- "renesas,rcar_sound-r8a774a1" (RZ/G2M)
|
||||
- "renesas,rcar_sound-r8a774c0" (RZ/G2E)
|
||||
- "renesas,rcar_sound-r8a7778" (R-Car M1A)
|
||||
|
@ -282,7 +283,12 @@ Required properties:
|
|||
- reg : Should contain the register physical address.
|
||||
required register is
|
||||
SRU/ADG/SSI if generation1
|
||||
SRU/ADG/SSIU/SSI if generation2
|
||||
SRU/ADG/SSIU/SSI/AUDIO-DMAC-periperi if generation2/generation3
|
||||
Select extended AUDIO-DMAC-periperi address if SoC has it,
|
||||
otherwise select normal AUDIO-DMAC-periperi address.
|
||||
- reg-names : Should contain the register names.
|
||||
scu/adg/ssi if generation1
|
||||
scu/adg/ssiu/ssi/audmapp if generation2/generation3
|
||||
- rcar_sound,ssi : Should contain SSI feature.
|
||||
The number of SSI subnode should be same as HW.
|
||||
see below for detail.
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
Required properties:
|
||||
|
||||
- compatible: "rockchip,pdm"
|
||||
- "rockchip,px30-pdm"
|
||||
- "rockchip,rk1808-pdm"
|
||||
- "rockchip,rk3308-pdm"
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
- dmas: DMA specifiers for rx dma. See the DMA client binding,
|
||||
|
@ -12,6 +15,8 @@ Required properties:
|
|||
- clock-names: should contain following:
|
||||
- "pdm_hclk": clock for PDM BUS
|
||||
- "pdm_clk" : clock for PDM controller
|
||||
- resets: a list of phandle + reset-specifer paris, one for each entry in reset-names.
|
||||
- reset-names: reset names, should include "pdm-m".
|
||||
- pinctrl-names: Must contain a "default" entry.
|
||||
- pinctrl-N: One property must exist for each entry in
|
||||
pinctrl-names. See ../pinctrl/pinctrl-bindings.txt
|
||||
|
|
|
@ -22,6 +22,11 @@ Optional properties:
|
|||
2: Use JD1_2 pin for jack-detect
|
||||
3: Use JD2 pin for jack-detect
|
||||
|
||||
- realtek,jack-detect-not-inverted
|
||||
bool. Normal jack-detect switches give an inverted (active-low) signal,
|
||||
set this bool in the rare case you've a jack-detect switch which is not
|
||||
inverted.
|
||||
|
||||
- realtek,over-current-threshold-microamp
|
||||
u32, micbias over-current detection threshold in µA, valid values are
|
||||
600, 1500 and 2000µA.
|
||||
|
|
|
@ -2,9 +2,9 @@ Simple Amplifier Audio Driver
|
|||
|
||||
Required properties:
|
||||
- compatible : "dioo,dio2125" or "simple-audio-amplifier"
|
||||
- enable-gpios : the gpio connected to the enable pin of the simple amplifier
|
||||
|
||||
Optional properties:
|
||||
- enable-gpios : the gpio connected to the enable pin of the simple amplifier
|
||||
- VCC-supply : power supply for the device, as covered
|
||||
in Documentation/devicetree/bindings/regulator/regulator.txt
|
||||
|
||||
|
|
|
@ -24,6 +24,8 @@ Optional properties:
|
|||
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:
|
||||
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
Spreadtrum Multi-Channel Data Transfer Binding
|
||||
|
||||
The Multi-channel data transfer controller is used for sound stream
|
||||
transmission between audio subsystem and other AP/CP subsystem. It
|
||||
supports 10 DAC channel and 10 ADC channel, and each channel can be
|
||||
configured with DMA mode or interrupt mode.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "sprd,sc9860-mcdt".
|
||||
- reg: Should contain registers address and length.
|
||||
- interrupts: Should contain one interrupt shared by all channel.
|
||||
|
||||
Example:
|
||||
|
||||
mcdt@41490000 {
|
||||
compatible = "sprd,sc9860-mcdt";
|
||||
reg = <0 0x41490000 0 0x170>;
|
||||
interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
|
@ -324,7 +324,7 @@ to details explained in the following section.
|
|||
strcpy(card->driver, "My Chip");
|
||||
strcpy(card->shortname, "My Own Chip 123");
|
||||
sprintf(card->longname, "%s at 0x%lx irq %i",
|
||||
card->shortname, chip->ioport, chip->irq);
|
||||
card->shortname, chip->port, chip->irq);
|
||||
|
||||
/* (5) */
|
||||
.... /* implemented later */
|
||||
|
@ -437,7 +437,7 @@ Since each component can be properly freed, the single
|
|||
strcpy(card->driver, "My Chip");
|
||||
strcpy(card->shortname, "My Own Chip 123");
|
||||
sprintf(card->longname, "%s at 0x%lx irq %i",
|
||||
card->shortname, chip->ioport, chip->irq);
|
||||
card->shortname, chip->port, chip->irq);
|
||||
|
||||
The driver field holds the minimal ID string of the chip. This is used
|
||||
by alsa-lib's configurator, so keep it simple but unique. Even the
|
||||
|
|
15
MAINTAINERS
15
MAINTAINERS
|
@ -3369,7 +3369,7 @@ F: include/uapi/linux/bsg.h
|
|||
BT87X AUDIO DRIVER
|
||||
M: Clemens Ladisch <clemens@ladisch.de>
|
||||
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
|
||||
T: git git://git.alsa-project.org/alsa-kernel.git
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
|
||||
S: Maintained
|
||||
F: Documentation/sound/cards/bt87x.rst
|
||||
F: sound/pci/bt87x.c
|
||||
|
@ -3422,7 +3422,7 @@ F: drivers/scsi/FlashPoint.*
|
|||
C-MEDIA CMI8788 DRIVER
|
||||
M: Clemens Ladisch <clemens@ladisch.de>
|
||||
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
|
||||
T: git git://git.alsa-project.org/alsa-kernel.git
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
|
||||
S: Maintained
|
||||
F: sound/pci/oxygen/
|
||||
|
||||
|
@ -3818,6 +3818,7 @@ F: drivers/hwmon/lochnagar-hwmon.c
|
|||
F: drivers/mfd/lochnagar-i2c.c
|
||||
F: drivers/pinctrl/cirrus/pinctrl-lochnagar.c
|
||||
F: drivers/regulator/lochnagar-regulator.c
|
||||
F: sound/soc/codecs/lochnagar-sc.c
|
||||
F: include/dt-bindings/clk/lochnagar.h
|
||||
F: include/dt-bindings/pinctrl/lochnagar.h
|
||||
F: include/linux/mfd/lochnagar*
|
||||
|
@ -3826,6 +3827,7 @@ F: Documentation/devicetree/bindings/clock/cirrus,lochnagar.txt
|
|||
F: Documentation/devicetree/bindings/hwmon/cirrus,lochnagar.txt
|
||||
F: Documentation/devicetree/bindings/pinctrl/cirrus,lochnagar.txt
|
||||
F: Documentation/devicetree/bindings/regulator/cirrus,lochnagar.txt
|
||||
F: Documentation/devicetree/bindings/sound/cirrus,lochnagar.txt
|
||||
F: Documentation/hwmon/lochnagar
|
||||
|
||||
CISCO FCOE HBA DRIVER
|
||||
|
@ -5770,7 +5772,7 @@ F: drivers/edac/qcom_edac.c
|
|||
EDIROL UA-101/UA-1000 DRIVER
|
||||
M: Clemens Ladisch <clemens@ladisch.de>
|
||||
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
|
||||
T: git git://git.alsa-project.org/alsa-kernel.git
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
|
||||
S: Maintained
|
||||
F: sound/usb/misc/ua101.c
|
||||
|
||||
|
@ -6110,7 +6112,7 @@ F: include/linux/f75375s.h
|
|||
FIREWIRE AUDIO DRIVERS
|
||||
M: Clemens Ladisch <clemens@ladisch.de>
|
||||
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
|
||||
T: git git://git.alsa-project.org/alsa-kernel.git
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
|
||||
S: Maintained
|
||||
F: sound/firewire/
|
||||
|
||||
|
@ -11700,7 +11702,7 @@ F: Documentation/devicetree/bindings/opp/
|
|||
OPL4 DRIVER
|
||||
M: Clemens Ladisch <clemens@ladisch.de>
|
||||
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
|
||||
T: git git://git.alsa-project.org/alsa-kernel.git
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
|
||||
S: Maintained
|
||||
F: sound/drivers/opl4/
|
||||
|
||||
|
@ -14610,7 +14612,6 @@ M: Takashi Iwai <tiwai@suse.com>
|
|||
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
|
||||
W: http://www.alsa-project.org/
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
|
||||
T: git git://git.alsa-project.org/alsa-kernel.git
|
||||
Q: http://patchwork.kernel.org/project/alsa-devel/list/
|
||||
S: Maintained
|
||||
F: Documentation/sound/
|
||||
|
@ -16256,7 +16257,7 @@ F: drivers/usb/storage/
|
|||
USB MIDI DRIVER
|
||||
M: Clemens Ladisch <clemens@ladisch.de>
|
||||
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
|
||||
T: git git://git.alsa-project.org/alsa-kernel.git
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
|
||||
S: Maintained
|
||||
F: sound/usb/midi.*
|
||||
|
||||
|
|
|
@ -226,7 +226,6 @@ int copy_from_user_toio(volatile void __iomem *dst, const void __user *src, size
|
|||
|
||||
/* init.c */
|
||||
|
||||
extern struct snd_card *snd_cards[SNDRV_CARDS];
|
||||
int snd_card_locked(int card);
|
||||
#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
|
||||
#define SND_MIXER_OSS_NOTIFY_REGISTER 0
|
||||
|
@ -251,7 +250,20 @@ int snd_card_add_dev_attr(struct snd_card *card,
|
|||
int snd_component_add(struct snd_card *card, const char *component);
|
||||
int snd_card_file_add(struct snd_card *card, struct file *file);
|
||||
int snd_card_file_remove(struct snd_card *card, struct file *file);
|
||||
#define snd_card_unref(card) put_device(&(card)->card_dev)
|
||||
|
||||
struct snd_card *snd_card_ref(int card);
|
||||
|
||||
/**
|
||||
* snd_card_unref - Unreference the card object
|
||||
* @card: the card object to unreference
|
||||
*
|
||||
* Call this function for the card object that was obtained via snd_card_ref()
|
||||
* or snd_lookup_minor_data().
|
||||
*/
|
||||
static inline void snd_card_unref(struct snd_card *card)
|
||||
{
|
||||
put_device(&card->card_dev);
|
||||
}
|
||||
|
||||
#define snd_card_set_dev(card, devptr) ((card)->dev = (devptr))
|
||||
|
||||
|
|
|
@ -33,10 +33,16 @@ enum da7219_mic_amp_in_sel {
|
|||
|
||||
struct da7219_aad_pdata;
|
||||
|
||||
enum da7219_dai_clks {
|
||||
DA7219_DAI_WCLK_IDX = 0,
|
||||
DA7219_DAI_BCLK_IDX,
|
||||
DA7219_DAI_NUM_CLKS,
|
||||
};
|
||||
|
||||
struct da7219_pdata {
|
||||
bool wakeup_source;
|
||||
|
||||
const char *dai_clks_name;
|
||||
const char *dai_clk_names[DA7219_DAI_NUM_CLKS];
|
||||
|
||||
/* Mic */
|
||||
enum da7219_micbias_voltage micbias_lvl;
|
||||
|
|
|
@ -297,7 +297,7 @@ struct hdac_rb {
|
|||
* @num_streams: streams supported
|
||||
* @idx: HDA link index
|
||||
* @hlink_list: link list of HDA links
|
||||
* @lock: lock for link mgmt
|
||||
* @lock: lock for link and display power mgmt
|
||||
* @cmd_dma_state: state of cmd DMAs: CORB and RIRB
|
||||
*/
|
||||
struct hdac_bus {
|
||||
|
@ -363,6 +363,7 @@ struct hdac_bus {
|
|||
/* locks */
|
||||
spinlock_t reg_lock;
|
||||
struct mutex cmd_mutex;
|
||||
struct mutex lock;
|
||||
|
||||
/* DRM component interface */
|
||||
struct drm_audio_component *audio_component;
|
||||
|
@ -373,11 +374,9 @@ struct hdac_bus {
|
|||
int num_streams;
|
||||
int idx;
|
||||
|
||||
/* link management */
|
||||
struct list_head hlink_list;
|
||||
|
||||
struct mutex lock;
|
||||
bool cmd_dma_state;
|
||||
|
||||
};
|
||||
|
||||
int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
|
||||
|
|
|
@ -151,9 +151,5 @@ int snd_dma_alloc_pages_fallback(int type, struct device *dev, size_t size,
|
|||
struct snd_dma_buffer *dmab);
|
||||
void snd_dma_free_pages(struct snd_dma_buffer *dmab);
|
||||
|
||||
/* basic memory allocation functions */
|
||||
void *snd_malloc_pages(size_t size, gfp_t gfp_flags);
|
||||
void snd_free_pages(void *ptr, size_t size);
|
||||
|
||||
#endif /* __SOUND_MEMALLOC_H */
|
||||
|
||||
|
|
|
@ -73,7 +73,8 @@ __printf(3, 4)
|
|||
int snd_seq_create_kernel_client(struct snd_card *card, int client_index,
|
||||
const char *name_fmt, ...);
|
||||
int snd_seq_delete_kernel_client(int client);
|
||||
int snd_seq_kernel_client_enqueue(int client, struct snd_seq_event *ev, int atomic, int hop);
|
||||
int snd_seq_kernel_client_enqueue(int client, struct snd_seq_event *ev,
|
||||
struct file *file, bool blocking);
|
||||
int snd_seq_kernel_client_dispatch(int client, struct snd_seq_event *ev, int atomic, int hop);
|
||||
int snd_seq_kernel_client_ctl(int client, unsigned int cmd, void *arg);
|
||||
|
||||
|
|
|
@ -10,10 +10,10 @@
|
|||
|
||||
#include <sound/soc.h>
|
||||
|
||||
#define asoc_simple_card_init_hp(card, sjack, prefix) \
|
||||
asoc_simple_card_init_jack(card, sjack, 1, prefix)
|
||||
#define asoc_simple_card_init_mic(card, sjack, prefix) \
|
||||
asoc_simple_card_init_jack(card, sjack, 0, prefix)
|
||||
#define asoc_simple_init_hp(card, sjack, prefix) \
|
||||
asoc_simple_init_jack(card, sjack, 1, prefix)
|
||||
#define asoc_simple_init_mic(card, sjack, prefix) \
|
||||
asoc_simple_init_jack(card, sjack, 0, prefix)
|
||||
|
||||
struct asoc_simple_dai {
|
||||
const char *name;
|
||||
|
@ -26,7 +26,7 @@ struct asoc_simple_dai {
|
|||
struct clk *clk;
|
||||
};
|
||||
|
||||
struct asoc_simple_card_data {
|
||||
struct asoc_simple_data {
|
||||
u32 convert_rate;
|
||||
u32 convert_channels;
|
||||
};
|
||||
|
@ -37,96 +37,180 @@ struct asoc_simple_jack {
|
|||
struct snd_soc_jack_gpio gpio;
|
||||
};
|
||||
|
||||
int asoc_simple_card_parse_daifmt(struct device *dev,
|
||||
struct device_node *node,
|
||||
struct device_node *codec,
|
||||
char *prefix,
|
||||
unsigned int *retfmt);
|
||||
struct asoc_simple_priv {
|
||||
struct snd_soc_card snd_card;
|
||||
struct simple_dai_props {
|
||||
struct asoc_simple_dai *cpu_dai;
|
||||
struct asoc_simple_dai *codec_dai;
|
||||
struct snd_soc_dai_link_component codecs; /* single codec */
|
||||
struct snd_soc_dai_link_component platforms;
|
||||
struct asoc_simple_data adata;
|
||||
struct snd_soc_codec_conf *codec_conf;
|
||||
unsigned int mclk_fs;
|
||||
} *dai_props;
|
||||
struct asoc_simple_jack hp_jack;
|
||||
struct asoc_simple_jack mic_jack;
|
||||
struct snd_soc_dai_link *dai_link;
|
||||
struct asoc_simple_dai *dais;
|
||||
struct snd_soc_codec_conf *codec_conf;
|
||||
struct gpio_desc *pa_gpio;
|
||||
};
|
||||
#define simple_priv_to_card(priv) (&(priv)->snd_card)
|
||||
#define simple_priv_to_props(priv, i) ((priv)->dai_props + (i))
|
||||
#define simple_priv_to_dev(priv) (simple_priv_to_card(priv)->dev)
|
||||
#define simple_priv_to_link(priv, i) (simple_priv_to_card(priv)->dai_link + (i))
|
||||
|
||||
struct link_info {
|
||||
int dais; /* number of dai */
|
||||
int link; /* number of link */
|
||||
int conf; /* number of codec_conf */
|
||||
int cpu; /* turn for CPU / Codec */
|
||||
};
|
||||
|
||||
int asoc_simple_parse_daifmt(struct device *dev,
|
||||
struct device_node *node,
|
||||
struct device_node *codec,
|
||||
char *prefix,
|
||||
unsigned int *retfmt);
|
||||
__printf(3, 4)
|
||||
int asoc_simple_card_set_dailink_name(struct device *dev,
|
||||
struct snd_soc_dai_link *dai_link,
|
||||
const char *fmt, ...);
|
||||
int asoc_simple_card_parse_card_name(struct snd_soc_card *card,
|
||||
char *prefix);
|
||||
int asoc_simple_set_dailink_name(struct device *dev,
|
||||
struct snd_soc_dai_link *dai_link,
|
||||
const char *fmt, ...);
|
||||
int asoc_simple_parse_card_name(struct snd_soc_card *card,
|
||||
char *prefix);
|
||||
|
||||
#define asoc_simple_card_parse_clk_cpu(dev, node, dai_link, simple_dai) \
|
||||
asoc_simple_card_parse_clk(dev, node, dai_link->cpu_of_node, simple_dai, \
|
||||
#define asoc_simple_parse_clk_cpu(dev, node, dai_link, simple_dai) \
|
||||
asoc_simple_parse_clk(dev, node, dai_link->cpu_of_node, simple_dai, \
|
||||
dai_link->cpu_dai_name, NULL)
|
||||
#define asoc_simple_card_parse_clk_codec(dev, node, dai_link, simple_dai) \
|
||||
asoc_simple_card_parse_clk(dev, node, dai_link->codec_of_node, simple_dai,\
|
||||
#define asoc_simple_parse_clk_codec(dev, node, dai_link, simple_dai) \
|
||||
asoc_simple_parse_clk(dev, node, dai_link->codec_of_node, simple_dai,\
|
||||
dai_link->codec_dai_name, dai_link->codecs)
|
||||
int asoc_simple_card_parse_clk(struct device *dev,
|
||||
struct device_node *node,
|
||||
struct device_node *dai_of_node,
|
||||
struct asoc_simple_dai *simple_dai,
|
||||
const char *dai_name,
|
||||
struct snd_soc_dai_link_component *dlc);
|
||||
int asoc_simple_card_clk_enable(struct asoc_simple_dai *dai);
|
||||
void asoc_simple_card_clk_disable(struct asoc_simple_dai *dai);
|
||||
int asoc_simple_parse_clk(struct device *dev,
|
||||
struct device_node *node,
|
||||
struct device_node *dai_of_node,
|
||||
struct asoc_simple_dai *simple_dai,
|
||||
const char *dai_name,
|
||||
struct snd_soc_dai_link_component *dlc);
|
||||
int asoc_simple_startup(struct snd_pcm_substream *substream);
|
||||
void asoc_simple_shutdown(struct snd_pcm_substream *substream);
|
||||
int asoc_simple_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params);
|
||||
int asoc_simple_dai_init(struct snd_soc_pcm_runtime *rtd);
|
||||
int asoc_simple_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||
struct snd_pcm_hw_params *params);
|
||||
|
||||
#define asoc_simple_card_parse_cpu(node, dai_link, \
|
||||
list_name, cells_name, is_single_link) \
|
||||
asoc_simple_card_parse_dai(node, NULL, \
|
||||
&dai_link->cpu_of_node, \
|
||||
&dai_link->cpu_dai_name, list_name, cells_name, is_single_link)
|
||||
#define asoc_simple_card_parse_codec(node, dai_link, list_name, cells_name) \
|
||||
asoc_simple_card_parse_dai(node, dai_link->codecs, \
|
||||
#define asoc_simple_parse_cpu(node, dai_link, is_single_link) \
|
||||
asoc_simple_parse_dai(node, NULL, \
|
||||
&dai_link->cpu_of_node, \
|
||||
&dai_link->cpu_dai_name, is_single_link)
|
||||
#define asoc_simple_parse_codec(node, dai_link) \
|
||||
asoc_simple_parse_dai(node, dai_link->codecs, \
|
||||
&dai_link->codec_of_node, \
|
||||
&dai_link->codec_dai_name, \
|
||||
list_name, cells_name, NULL)
|
||||
#define asoc_simple_card_parse_platform(node, dai_link, list_name, cells_name) \
|
||||
asoc_simple_card_parse_dai(node, dai_link->platforms, \
|
||||
&dai_link->platform_of_node, \
|
||||
NULL, list_name, cells_name, NULL)
|
||||
int asoc_simple_card_parse_dai(struct device_node *node,
|
||||
struct snd_soc_dai_link_component *dlc,
|
||||
struct device_node **endpoint_np,
|
||||
const char **dai_name,
|
||||
const char *list_name,
|
||||
const char *cells_name,
|
||||
int *is_single_links);
|
||||
&dai_link->codec_dai_name, NULL)
|
||||
#define asoc_simple_parse_platform(node, dai_link) \
|
||||
asoc_simple_parse_dai(node, dai_link->platforms, \
|
||||
&dai_link->platform_of_node, NULL, NULL)
|
||||
|
||||
#define asoc_simple_card_parse_graph_cpu(ep, dai_link) \
|
||||
asoc_simple_card_parse_graph_dai(ep, NULL, \
|
||||
&dai_link->cpu_of_node, \
|
||||
&dai_link->cpu_dai_name)
|
||||
#define asoc_simple_card_parse_graph_codec(ep, dai_link) \
|
||||
asoc_simple_card_parse_graph_dai(ep, dai_link->codecs, \
|
||||
&dai_link->codec_of_node, \
|
||||
&dai_link->codec_dai_name)
|
||||
int asoc_simple_card_parse_graph_dai(struct device_node *ep,
|
||||
struct snd_soc_dai_link_component *dlc,
|
||||
struct device_node **endpoint_np,
|
||||
const char **dai_name);
|
||||
|
||||
#define asoc_simple_card_of_parse_tdm(np, dai) \
|
||||
#define asoc_simple_parse_tdm(np, dai) \
|
||||
snd_soc_of_parse_tdm_slot(np, &(dai)->tx_slot_mask, \
|
||||
&(dai)->rx_slot_mask, \
|
||||
&(dai)->slots, \
|
||||
&(dai)->slot_width);
|
||||
|
||||
int asoc_simple_card_init_dai(struct snd_soc_dai *dai,
|
||||
struct asoc_simple_dai *simple_dai);
|
||||
|
||||
void asoc_simple_card_canonicalize_platform(struct snd_soc_dai_link *dai_link);
|
||||
void asoc_simple_card_canonicalize_cpu(struct snd_soc_dai_link *dai_link,
|
||||
void asoc_simple_canonicalize_platform(struct snd_soc_dai_link *dai_link);
|
||||
void asoc_simple_canonicalize_cpu(struct snd_soc_dai_link *dai_link,
|
||||
int is_single_links);
|
||||
|
||||
int asoc_simple_card_clean_reference(struct snd_soc_card *card);
|
||||
int asoc_simple_clean_reference(struct snd_soc_card *card);
|
||||
|
||||
void asoc_simple_card_convert_fixup(struct asoc_simple_card_data *data,
|
||||
void asoc_simple_convert_fixup(struct asoc_simple_data *data,
|
||||
struct snd_pcm_hw_params *params);
|
||||
void asoc_simple_card_parse_convert(struct device *dev,
|
||||
struct device_node *np, char *prefix,
|
||||
struct asoc_simple_card_data *data);
|
||||
void asoc_simple_parse_convert(struct device *dev,
|
||||
struct device_node *np, char *prefix,
|
||||
struct asoc_simple_data *data);
|
||||
|
||||
int asoc_simple_card_of_parse_routing(struct snd_soc_card *card,
|
||||
int asoc_simple_parse_routing(struct snd_soc_card *card,
|
||||
char *prefix);
|
||||
int asoc_simple_card_of_parse_widgets(struct snd_soc_card *card,
|
||||
int asoc_simple_parse_widgets(struct snd_soc_card *card,
|
||||
char *prefix);
|
||||
int asoc_simple_parse_pin_switches(struct snd_soc_card *card,
|
||||
char *prefix);
|
||||
|
||||
int asoc_simple_card_init_jack(struct snd_soc_card *card,
|
||||
int asoc_simple_init_jack(struct snd_soc_card *card,
|
||||
struct asoc_simple_jack *sjack,
|
||||
int is_hp, char *prefix);
|
||||
int asoc_simple_init_priv(struct asoc_simple_priv *priv,
|
||||
struct link_info *li);
|
||||
|
||||
#ifdef DEBUG
|
||||
inline void asoc_simple_debug_dai(struct asoc_simple_priv *priv,
|
||||
char *name,
|
||||
struct asoc_simple_dai *dai)
|
||||
{
|
||||
struct device *dev = simple_priv_to_dev(priv);
|
||||
|
||||
if (dai->name)
|
||||
dev_dbg(dev, "%s dai name = %s\n",
|
||||
name, dai->name);
|
||||
if (dai->sysclk)
|
||||
dev_dbg(dev, "%s sysclk = %d\n",
|
||||
name, dai->sysclk);
|
||||
|
||||
dev_dbg(dev, "%s direction = %s\n",
|
||||
name, dai->clk_direction ? "OUT" : "IN");
|
||||
|
||||
if (dai->slots)
|
||||
dev_dbg(dev, "%s slots = %d\n", name, dai->slots);
|
||||
if (dai->slot_width)
|
||||
dev_dbg(dev, "%s slot width = %d\n", name, dai->slot_width);
|
||||
if (dai->tx_slot_mask)
|
||||
dev_dbg(dev, "%s tx slot mask = %d\n", name, dai->tx_slot_mask);
|
||||
if (dai->rx_slot_mask)
|
||||
dev_dbg(dev, "%s rx slot mask = %d\n", name, dai->rx_slot_mask);
|
||||
if (dai->clk)
|
||||
dev_dbg(dev, "%s clk %luHz\n", name, clk_get_rate(dai->clk));
|
||||
}
|
||||
|
||||
inline void asoc_simple_debug_info(struct asoc_simple_priv *priv)
|
||||
{
|
||||
struct snd_soc_card *card = simple_priv_to_card(priv);
|
||||
struct device *dev = simple_priv_to_dev(priv);
|
||||
|
||||
int i;
|
||||
|
||||
if (card->name)
|
||||
dev_dbg(dev, "Card Name: %s\n", card->name);
|
||||
|
||||
for (i = 0; i < card->num_links; i++) {
|
||||
struct simple_dai_props *props = simple_priv_to_props(priv, i);
|
||||
struct snd_soc_dai_link *link = simple_priv_to_link(priv, i);
|
||||
|
||||
dev_dbg(dev, "DAI%d\n", i);
|
||||
|
||||
asoc_simple_debug_dai(priv, "cpu", props->cpu_dai);
|
||||
asoc_simple_debug_dai(priv, "codec", props->codec_dai);
|
||||
|
||||
if (link->name)
|
||||
dev_dbg(dev, "dai name = %s\n", link->name);
|
||||
|
||||
dev_dbg(dev, "dai format = %04x\n", link->dai_fmt);
|
||||
|
||||
if (props->adata.convert_rate)
|
||||
dev_dbg(dev, "convert_rate = %d\n",
|
||||
props->adata.convert_rate);
|
||||
if (props->adata.convert_channels)
|
||||
dev_dbg(dev, "convert_channels = %d\n",
|
||||
props->adata.convert_channels);
|
||||
if (props->codec_conf && props->codec_conf->name_prefix)
|
||||
dev_dbg(dev, "name prefix = %s\n",
|
||||
props->codec_conf->name_prefix);
|
||||
if (props->mclk_fs)
|
||||
dev_dbg(dev, "mclk-fs = %d\n",
|
||||
props->mclk_fs);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define asoc_simple_debug_info(priv)
|
||||
#endif /* DEBUG */
|
||||
|
||||
#endif /* __SIMPLE_CARD_UTILS_H */
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_SOUND_SOF_H
|
||||
#define __INCLUDE_SOUND_SOF_H
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-acpi.h>
|
||||
|
||||
struct snd_sof_dsp_ops;
|
||||
|
||||
/*
|
||||
* SOF Platform data.
|
||||
*/
|
||||
struct snd_sof_pdata {
|
||||
const struct firmware *fw;
|
||||
const char *drv_name;
|
||||
const char *name;
|
||||
const char *platform;
|
||||
|
||||
struct device *dev;
|
||||
|
||||
/*
|
||||
* notification callback used if the hardware initialization
|
||||
* can take time or is handled in a workqueue. This callback
|
||||
* can be used by the caller to e.g. enable runtime_pm
|
||||
* or limit functionality until all low-level inits are
|
||||
* complete.
|
||||
*/
|
||||
void (*sof_probe_complete)(struct device *dev);
|
||||
|
||||
/* descriptor */
|
||||
const struct sof_dev_desc *desc;
|
||||
|
||||
/* firmware and topology filenames */
|
||||
const char *fw_filename_prefix;
|
||||
const char *fw_filename;
|
||||
const char *tplg_filename_prefix;
|
||||
const char *tplg_filename;
|
||||
|
||||
/* machine */
|
||||
struct platform_device *pdev_mach;
|
||||
const struct snd_soc_acpi_mach *machine;
|
||||
|
||||
void *hw_pdata;
|
||||
};
|
||||
|
||||
/*
|
||||
* Descriptor used for setting up SOF platform data. This is used when
|
||||
* ACPI/PCI data is missing or mapped differently.
|
||||
*/
|
||||
struct sof_dev_desc {
|
||||
/* list of machines using this configuration */
|
||||
struct snd_soc_acpi_mach *machines;
|
||||
|
||||
/* Platform resource indexes in BAR / ACPI resources. */
|
||||
/* Must set to -1 if not used - add new items to end */
|
||||
int resindex_lpe_base;
|
||||
int resindex_pcicfg_base;
|
||||
int resindex_imr_base;
|
||||
int irqindex_host_ipc;
|
||||
int resindex_dma_base;
|
||||
|
||||
/* DMA only valid when resindex_dma_base != -1*/
|
||||
int dma_engine;
|
||||
int dma_size;
|
||||
|
||||
/* IPC timeouts in ms */
|
||||
int ipc_timeout;
|
||||
int boot_timeout;
|
||||
|
||||
/* chip information for dsp */
|
||||
const void *chip_info;
|
||||
|
||||
/* defaults for no codec mode */
|
||||
const char *nocodec_fw_filename;
|
||||
const char *nocodec_tplg_filename;
|
||||
|
||||
/* defaults paths for firmware and topology files */
|
||||
const char *default_fw_path;
|
||||
const char *default_tplg_path;
|
||||
|
||||
const struct snd_sof_dsp_ops *ops;
|
||||
const struct sof_arch_ops *arch_ops;
|
||||
};
|
||||
|
||||
int sof_nocodec_setup(struct device *dev,
|
||||
struct snd_sof_pdata *sof_pdata,
|
||||
struct snd_soc_acpi_mach *mach,
|
||||
const struct sof_dev_desc *desc,
|
||||
const struct snd_sof_dsp_ops *ops);
|
||||
#endif
|
|
@ -0,0 +1,158 @@
|
|||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_SOUND_SOF_CONTROL_H__
|
||||
#define __INCLUDE_SOUND_SOF_CONTROL_H__
|
||||
|
||||
#include <uapi/sound/sof/header.h>
|
||||
#include <sound/sof/header.h>
|
||||
|
||||
/*
|
||||
* Component Mixers and Controls
|
||||
*/
|
||||
|
||||
/* channel positions - uses same values as ALSA */
|
||||
enum sof_ipc_chmap {
|
||||
SOF_CHMAP_UNKNOWN = 0,
|
||||
SOF_CHMAP_NA, /**< N/A, silent */
|
||||
SOF_CHMAP_MONO, /**< mono stream */
|
||||
SOF_CHMAP_FL, /**< front left */
|
||||
SOF_CHMAP_FR, /**< front right */
|
||||
SOF_CHMAP_RL, /**< rear left */
|
||||
SOF_CHMAP_RR, /**< rear right */
|
||||
SOF_CHMAP_FC, /**< front centre */
|
||||
SOF_CHMAP_LFE, /**< LFE */
|
||||
SOF_CHMAP_SL, /**< side left */
|
||||
SOF_CHMAP_SR, /**< side right */
|
||||
SOF_CHMAP_RC, /**< rear centre */
|
||||
SOF_CHMAP_FLC, /**< front left centre */
|
||||
SOF_CHMAP_FRC, /**< front right centre */
|
||||
SOF_CHMAP_RLC, /**< rear left centre */
|
||||
SOF_CHMAP_RRC, /**< rear right centre */
|
||||
SOF_CHMAP_FLW, /**< front left wide */
|
||||
SOF_CHMAP_FRW, /**< front right wide */
|
||||
SOF_CHMAP_FLH, /**< front left high */
|
||||
SOF_CHMAP_FCH, /**< front centre high */
|
||||
SOF_CHMAP_FRH, /**< front right high */
|
||||
SOF_CHMAP_TC, /**< top centre */
|
||||
SOF_CHMAP_TFL, /**< top front left */
|
||||
SOF_CHMAP_TFR, /**< top front right */
|
||||
SOF_CHMAP_TFC, /**< top front centre */
|
||||
SOF_CHMAP_TRL, /**< top rear left */
|
||||
SOF_CHMAP_TRR, /**< top rear right */
|
||||
SOF_CHMAP_TRC, /**< top rear centre */
|
||||
SOF_CHMAP_TFLC, /**< top front left centre */
|
||||
SOF_CHMAP_TFRC, /**< top front right centre */
|
||||
SOF_CHMAP_TSL, /**< top side left */
|
||||
SOF_CHMAP_TSR, /**< top side right */
|
||||
SOF_CHMAP_LLFE, /**< left LFE */
|
||||
SOF_CHMAP_RLFE, /**< right LFE */
|
||||
SOF_CHMAP_BC, /**< bottom centre */
|
||||
SOF_CHMAP_BLC, /**< bottom left centre */
|
||||
SOF_CHMAP_BRC, /**< bottom right centre */
|
||||
SOF_CHMAP_LAST = SOF_CHMAP_BRC,
|
||||
};
|
||||
|
||||
/* control data type and direction */
|
||||
enum sof_ipc_ctrl_type {
|
||||
/* per channel data - uses struct sof_ipc_ctrl_value_chan */
|
||||
SOF_CTRL_TYPE_VALUE_CHAN_GET = 0,
|
||||
SOF_CTRL_TYPE_VALUE_CHAN_SET,
|
||||
/* component data - uses struct sof_ipc_ctrl_value_comp */
|
||||
SOF_CTRL_TYPE_VALUE_COMP_GET,
|
||||
SOF_CTRL_TYPE_VALUE_COMP_SET,
|
||||
/* bespoke data - uses struct sof_abi_hdr */
|
||||
SOF_CTRL_TYPE_DATA_GET,
|
||||
SOF_CTRL_TYPE_DATA_SET,
|
||||
};
|
||||
|
||||
/* control command type */
|
||||
enum sof_ipc_ctrl_cmd {
|
||||
SOF_CTRL_CMD_VOLUME = 0, /**< maps to ALSA volume style controls */
|
||||
SOF_CTRL_CMD_ENUM, /**< maps to ALSA enum style controls */
|
||||
SOF_CTRL_CMD_SWITCH, /**< maps to ALSA switch style controls */
|
||||
SOF_CTRL_CMD_BINARY, /**< maps to ALSA binary style controls */
|
||||
};
|
||||
|
||||
/* generic channel mapped value data */
|
||||
struct sof_ipc_ctrl_value_chan {
|
||||
uint32_t channel; /**< channel map - enum sof_ipc_chmap */
|
||||
uint32_t value;
|
||||
} __packed;
|
||||
|
||||
/* generic component mapped value data */
|
||||
struct sof_ipc_ctrl_value_comp {
|
||||
uint32_t index; /**< component source/sink/control index in control */
|
||||
union {
|
||||
uint32_t uvalue;
|
||||
int32_t svalue;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* generic control data */
|
||||
struct sof_ipc_ctrl_data {
|
||||
struct sof_ipc_reply rhdr;
|
||||
uint32_t comp_id;
|
||||
|
||||
/* control access and data type */
|
||||
uint32_t type; /**< enum sof_ipc_ctrl_type */
|
||||
uint32_t cmd; /**< enum sof_ipc_ctrl_cmd */
|
||||
uint32_t index; /**< control index for comps > 1 control */
|
||||
|
||||
/* control data - can either be appended or DMAed from host */
|
||||
struct sof_ipc_host_buffer buffer;
|
||||
uint32_t num_elems; /**< in array elems or bytes for data type */
|
||||
uint32_t elems_remaining; /**< elems remaining if sent in parts */
|
||||
|
||||
uint32_t msg_index; /**< for large messages sent in parts */
|
||||
|
||||
/* reserved for future use */
|
||||
uint32_t reserved[6];
|
||||
|
||||
/* control data - add new types if needed */
|
||||
union {
|
||||
/* channel values can be used by volume type controls */
|
||||
struct sof_ipc_ctrl_value_chan chanv[0];
|
||||
/* component values used by routing controls like mux, mixer */
|
||||
struct sof_ipc_ctrl_value_comp compv[0];
|
||||
/* data can be used by binary controls */
|
||||
struct sof_abi_hdr data[0];
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/** Event type */
|
||||
enum sof_ipc_ctrl_event_type {
|
||||
SOF_CTRL_EVENT_GENERIC = 0, /**< generic event */
|
||||
SOF_CTRL_EVENT_GENERIC_METADATA, /**< generic event with metadata */
|
||||
SOF_CTRL_EVENT_KD, /**< keyword detection event */
|
||||
SOF_CTRL_EVENT_VAD, /**< voice activity detection event */
|
||||
};
|
||||
|
||||
/**
|
||||
* Generic notification data.
|
||||
*/
|
||||
struct sof_ipc_comp_event {
|
||||
struct sof_ipc_reply rhdr;
|
||||
uint16_t src_comp_type; /**< COMP_TYPE_ */
|
||||
uint32_t src_comp_id; /**< source component id */
|
||||
uint32_t event_type; /**< event type - SOF_CTRL_EVENT_* */
|
||||
uint32_t num_elems; /**< in array elems or bytes for data type */
|
||||
|
||||
/* reserved for future use */
|
||||
uint32_t reserved[8];
|
||||
|
||||
/* control data - add new types if needed */
|
||||
union {
|
||||
/* data can be used by binary controls */
|
||||
struct sof_abi_hdr data[0];
|
||||
/* event specific values */
|
||||
uint32_t event_value;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,178 @@
|
|||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_SOUND_SOF_DAI_INTEL_H__
|
||||
#define __INCLUDE_SOUND_SOF_DAI_INTEL_H__
|
||||
|
||||
#include <sound/sof/header.h>
|
||||
|
||||
/* ssc1: TINTE */
|
||||
#define SOF_DAI_INTEL_SSP_QUIRK_TINTE (1 << 0)
|
||||
/* ssc1: PINTE */
|
||||
#define SOF_DAI_INTEL_SSP_QUIRK_PINTE (1 << 1)
|
||||
/* ssc2: SMTATF */
|
||||
#define SOF_DAI_INTEL_SSP_QUIRK_SMTATF (1 << 2)
|
||||
/* ssc2: MMRATF */
|
||||
#define SOF_DAI_INTEL_SSP_QUIRK_MMRATF (1 << 3)
|
||||
/* ssc2: PSPSTWFDFD */
|
||||
#define SOF_DAI_INTEL_SSP_QUIRK_PSPSTWFDFD (1 << 4)
|
||||
/* ssc2: PSPSRWFDFD */
|
||||
#define SOF_DAI_INTEL_SSP_QUIRK_PSPSRWFDFD (1 << 5)
|
||||
/* ssc1: LBM */
|
||||
#define SOF_DAI_INTEL_SSP_QUIRK_LBM (1 << 6)
|
||||
|
||||
/* here is the possibility to define others aux macros */
|
||||
|
||||
#define SOF_DAI_INTEL_SSP_FRAME_PULSE_WIDTH_MAX 38
|
||||
#define SOF_DAI_INTEL_SSP_SLOT_PADDING_MAX 31
|
||||
|
||||
/* SSP clocks control settings
|
||||
*
|
||||
* Macros for clks_control field in sof_ipc_dai_ssp_params struct.
|
||||
*/
|
||||
|
||||
/* mclk 0 disable */
|
||||
#define SOF_DAI_INTEL_SSP_MCLK_0_DISABLE BIT(0)
|
||||
/* mclk 1 disable */
|
||||
#define SOF_DAI_INTEL_SSP_MCLK_1_DISABLE BIT(1)
|
||||
/* mclk keep active */
|
||||
#define SOF_DAI_INTEL_SSP_CLKCTRL_MCLK_KA BIT(2)
|
||||
/* bclk keep active */
|
||||
#define SOF_DAI_INTEL_SSP_CLKCTRL_BCLK_KA BIT(3)
|
||||
/* fs keep active */
|
||||
#define SOF_DAI_INTEL_SSP_CLKCTRL_FS_KA BIT(4)
|
||||
/* bclk idle */
|
||||
#define SOF_DAI_INTEL_SSP_CLKCTRL_BCLK_IDLE_HIGH BIT(5)
|
||||
|
||||
/* SSP Configuration Request - SOF_IPC_DAI_SSP_CONFIG */
|
||||
struct sof_ipc_dai_ssp_params {
|
||||
struct sof_ipc_hdr hdr;
|
||||
uint16_t reserved1;
|
||||
uint16_t mclk_id;
|
||||
|
||||
uint32_t mclk_rate; /* mclk frequency in Hz */
|
||||
uint32_t fsync_rate; /* fsync frequency in Hz */
|
||||
uint32_t bclk_rate; /* bclk frequency in Hz */
|
||||
|
||||
/* TDM */
|
||||
uint32_t tdm_slots;
|
||||
uint32_t rx_slots;
|
||||
uint32_t tx_slots;
|
||||
|
||||
/* data */
|
||||
uint32_t sample_valid_bits;
|
||||
uint16_t tdm_slot_width;
|
||||
uint16_t reserved2; /* alignment */
|
||||
|
||||
/* MCLK */
|
||||
uint32_t mclk_direction;
|
||||
|
||||
uint16_t frame_pulse_width;
|
||||
uint16_t tdm_per_slot_padding_flag;
|
||||
uint32_t clks_control;
|
||||
uint32_t quirks;
|
||||
} __packed;
|
||||
|
||||
/* HDA Configuration Request - SOF_IPC_DAI_HDA_CONFIG */
|
||||
struct sof_ipc_dai_hda_params {
|
||||
struct sof_ipc_hdr hdr;
|
||||
uint32_t link_dma_ch;
|
||||
} __packed;
|
||||
|
||||
/* DMIC Configuration Request - SOF_IPC_DAI_DMIC_CONFIG */
|
||||
|
||||
/* This struct is defined per 2ch PDM controller available in the platform.
|
||||
* Normally it is sufficient to set the used microphone specific enables to 1
|
||||
* and keep other parameters as zero. The customizations are:
|
||||
*
|
||||
* 1. If a device mixes different microphones types with different polarity
|
||||
* and/or the absolute polarity matters the PCM signal from a microphone
|
||||
* can be inverted with the controls.
|
||||
*
|
||||
* 2. If the microphones in a stereo pair do not appear in captured stream
|
||||
* in desired order due to board schematics choises they can be swapped with
|
||||
* the clk_edge parameter.
|
||||
*
|
||||
* 3. If PDM bit errors are seen in capture (poor quality) the skew parameter
|
||||
* that delays the sampling time of data by half cycles of DMIC source clock
|
||||
* can be tried for improvement. However there is no guarantee for this to fix
|
||||
* data integrity problems.
|
||||
*/
|
||||
struct sof_ipc_dai_dmic_pdm_ctrl {
|
||||
struct sof_ipc_hdr hdr;
|
||||
uint16_t id; /**< PDM controller ID */
|
||||
|
||||
uint16_t enable_mic_a; /**< Use A (left) channel mic (0 or 1)*/
|
||||
uint16_t enable_mic_b; /**< Use B (right) channel mic (0 or 1)*/
|
||||
|
||||
uint16_t polarity_mic_a; /**< Optionally invert mic A signal (0 or 1) */
|
||||
uint16_t polarity_mic_b; /**< Optionally invert mic B signal (0 or 1) */
|
||||
|
||||
uint16_t clk_edge; /**< Optionally swap data clock edge (0 or 1) */
|
||||
uint16_t skew; /**< Adjust PDM data sampling vs. clock (0..15) */
|
||||
|
||||
uint16_t reserved[3]; /**< Make sure the total size is 4 bytes aligned */
|
||||
} __packed;
|
||||
|
||||
/* This struct contains the global settings for all 2ch PDM controllers. The
|
||||
* 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.
|
||||
*
|
||||
* 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
|
||||
* range 1.0 - 3.2 MHz is usually supported microphones. Some microphones are
|
||||
* multi-mode capable and there may be denied mic clock frequencies between
|
||||
* the modes. In such case set the clock range limits of the desired mode to
|
||||
* avoid the driver to set clock to an illegal rate.
|
||||
*
|
||||
* The duty cycle could be set to 48-52% if not known. Generally these
|
||||
* parameters can be altered within data sheet specified limits to match
|
||||
* required audio application performance power.
|
||||
*
|
||||
* The microphone clock needs to be usually about 50-80 times the used audio
|
||||
* sample rate. With highest sample rates above 48 kHz this can relaxed
|
||||
* somewhat.
|
||||
*
|
||||
* The parameter wake_up_time describes how long time the microphone needs
|
||||
* for the data line to produce valid output from mic clock start. The driver
|
||||
* will mute the captured audio for the given time. The min_clock_on_time
|
||||
* parameter is used to prevent too short clock bursts to happen. The driver
|
||||
* will keep the clock active after capture stop if this time is not yet
|
||||
* met. The unit for both is microseconds (us). Exceed of 100 ms will be
|
||||
* treated as an error.
|
||||
*/
|
||||
struct sof_ipc_dai_dmic_params {
|
||||
struct sof_ipc_hdr hdr;
|
||||
uint32_t driver_ipc_version; /**< Version (1..N) */
|
||||
|
||||
uint32_t pdmclk_min; /**< Minimum microphone clock in Hz (100000..N) */
|
||||
uint32_t pdmclk_max; /**< Maximum microphone clock in Hz (min...N) */
|
||||
|
||||
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 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 wake_up_time; /**< Time from clock start to data (us) */
|
||||
uint32_t min_clock_on_time; /**< Min. time that clk is kept on (us) */
|
||||
|
||||
/* reserved for future use */
|
||||
uint32_t reserved[6];
|
||||
|
||||
/**< variable number of pdm controller config */
|
||||
struct sof_ipc_dai_dmic_pdm_ctrl pdm[0];
|
||||
} __packed;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,75 @@
|
|||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_SOUND_SOF_DAI_H__
|
||||
#define __INCLUDE_SOUND_SOF_DAI_H__
|
||||
|
||||
#include <sound/sof/header.h>
|
||||
#include <sound/sof/dai-intel.h>
|
||||
|
||||
/*
|
||||
* DAI Configuration.
|
||||
*
|
||||
* Each different DAI type will have it's own structure and IPC cmd.
|
||||
*/
|
||||
|
||||
#define SOF_DAI_FMT_I2S 1 /**< I2S mode */
|
||||
#define SOF_DAI_FMT_RIGHT_J 2 /**< Right Justified mode */
|
||||
#define SOF_DAI_FMT_LEFT_J 3 /**< Left Justified mode */
|
||||
#define SOF_DAI_FMT_DSP_A 4 /**< L data MSB after FRM LRC */
|
||||
#define SOF_DAI_FMT_DSP_B 5 /**< L data MSB during FRM LRC */
|
||||
#define SOF_DAI_FMT_PDM 6 /**< Pulse density modulation */
|
||||
|
||||
#define SOF_DAI_FMT_CONT (1 << 4) /**< continuous clock */
|
||||
#define SOF_DAI_FMT_GATED (0 << 4) /**< clock is gated */
|
||||
|
||||
#define SOF_DAI_FMT_NB_NF (0 << 8) /**< normal bit clock + frame */
|
||||
#define SOF_DAI_FMT_NB_IF (2 << 8) /**< normal BCLK + inv FRM */
|
||||
#define SOF_DAI_FMT_IB_NF (3 << 8) /**< invert BCLK + nor FRM */
|
||||
#define SOF_DAI_FMT_IB_IF (4 << 8) /**< invert BCLK + FRM */
|
||||
|
||||
#define SOF_DAI_FMT_CBM_CFM (0 << 12) /**< codec clk & FRM master */
|
||||
#define SOF_DAI_FMT_CBS_CFM (2 << 12) /**< codec clk slave & FRM master */
|
||||
#define SOF_DAI_FMT_CBM_CFS (3 << 12) /**< codec clk master & frame slave */
|
||||
#define SOF_DAI_FMT_CBS_CFS (4 << 12) /**< codec clk & FRM slave */
|
||||
|
||||
#define SOF_DAI_FMT_FORMAT_MASK 0x000f
|
||||
#define SOF_DAI_FMT_CLOCK_MASK 0x00f0
|
||||
#define SOF_DAI_FMT_INV_MASK 0x0f00
|
||||
#define SOF_DAI_FMT_MASTER_MASK 0xf000
|
||||
|
||||
/** \brief Types of DAI */
|
||||
enum sof_ipc_dai_type {
|
||||
SOF_DAI_INTEL_NONE = 0, /**< None */
|
||||
SOF_DAI_INTEL_SSP, /**< Intel SSP */
|
||||
SOF_DAI_INTEL_DMIC, /**< Intel DMIC */
|
||||
SOF_DAI_INTEL_HDA, /**< Intel HD/A */
|
||||
};
|
||||
|
||||
/* general purpose DAI configuration */
|
||||
struct sof_ipc_dai_config {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
uint32_t type; /**< DAI type - enum sof_ipc_dai_type */
|
||||
uint32_t dai_index; /**< index of this type dai */
|
||||
|
||||
/* physical protocol and clocking */
|
||||
uint16_t format; /**< SOF_DAI_FMT_ */
|
||||
uint16_t reserved16; /**< alignment */
|
||||
|
||||
/* reserved for future use */
|
||||
uint32_t reserved[8];
|
||||
|
||||
/* HW specific data */
|
||||
union {
|
||||
struct sof_ipc_dai_ssp_params ssp;
|
||||
struct sof_ipc_dai_dmic_params dmic;
|
||||
struct sof_ipc_dai_hda_params hda;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,158 @@
|
|||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_SOUND_SOF_HEADER_H__
|
||||
#define __INCLUDE_SOUND_SOF_HEADER_H__
|
||||
|
||||
#include <uapi/sound/sof/abi.h>
|
||||
|
||||
/** \addtogroup sof_uapi uAPI
|
||||
* SOF uAPI specification.
|
||||
* @{
|
||||
*/
|
||||
|
||||
/*
|
||||
* IPC messages have a prefixed 32 bit identifier made up as follows :-
|
||||
*
|
||||
* 0xGCCCNNNN where
|
||||
* G is global cmd type (4 bits)
|
||||
* C is command type (12 bits)
|
||||
* I is the ID number (16 bits) - monotonic and overflows
|
||||
*
|
||||
* This is sent at the start of the IPM message in the mailbox. Messages should
|
||||
* not be sent in the doorbell (special exceptions for firmware .
|
||||
*/
|
||||
|
||||
/* Global Message - Generic */
|
||||
#define SOF_GLB_TYPE_SHIFT 28
|
||||
#define SOF_GLB_TYPE_MASK (0xf << SOF_GLB_TYPE_SHIFT)
|
||||
#define SOF_GLB_TYPE(x) ((x) << SOF_GLB_TYPE_SHIFT)
|
||||
|
||||
/* Command Message - Generic */
|
||||
#define SOF_CMD_TYPE_SHIFT 16
|
||||
#define SOF_CMD_TYPE_MASK (0xfff << SOF_CMD_TYPE_SHIFT)
|
||||
#define SOF_CMD_TYPE(x) ((x) << SOF_CMD_TYPE_SHIFT)
|
||||
|
||||
/* Global Message Types */
|
||||
#define SOF_IPC_GLB_REPLY SOF_GLB_TYPE(0x1U)
|
||||
#define SOF_IPC_GLB_COMPOUND SOF_GLB_TYPE(0x2U)
|
||||
#define SOF_IPC_GLB_TPLG_MSG SOF_GLB_TYPE(0x3U)
|
||||
#define SOF_IPC_GLB_PM_MSG SOF_GLB_TYPE(0x4U)
|
||||
#define SOF_IPC_GLB_COMP_MSG SOF_GLB_TYPE(0x5U)
|
||||
#define SOF_IPC_GLB_STREAM_MSG SOF_GLB_TYPE(0x6U)
|
||||
#define SOF_IPC_FW_READY SOF_GLB_TYPE(0x7U)
|
||||
#define SOF_IPC_GLB_DAI_MSG SOF_GLB_TYPE(0x8U)
|
||||
#define SOF_IPC_GLB_TRACE_MSG SOF_GLB_TYPE(0x9U)
|
||||
|
||||
/*
|
||||
* DSP Command Message Types
|
||||
*/
|
||||
|
||||
/* topology */
|
||||
#define SOF_IPC_TPLG_COMP_NEW SOF_CMD_TYPE(0x001)
|
||||
#define SOF_IPC_TPLG_COMP_FREE SOF_CMD_TYPE(0x002)
|
||||
#define SOF_IPC_TPLG_COMP_CONNECT SOF_CMD_TYPE(0x003)
|
||||
#define SOF_IPC_TPLG_PIPE_NEW SOF_CMD_TYPE(0x010)
|
||||
#define SOF_IPC_TPLG_PIPE_FREE SOF_CMD_TYPE(0x011)
|
||||
#define SOF_IPC_TPLG_PIPE_CONNECT SOF_CMD_TYPE(0x012)
|
||||
#define SOF_IPC_TPLG_PIPE_COMPLETE SOF_CMD_TYPE(0x013)
|
||||
#define SOF_IPC_TPLG_BUFFER_NEW SOF_CMD_TYPE(0x020)
|
||||
#define SOF_IPC_TPLG_BUFFER_FREE SOF_CMD_TYPE(0x021)
|
||||
|
||||
/* PM */
|
||||
#define SOF_IPC_PM_CTX_SAVE SOF_CMD_TYPE(0x001)
|
||||
#define SOF_IPC_PM_CTX_RESTORE SOF_CMD_TYPE(0x002)
|
||||
#define SOF_IPC_PM_CTX_SIZE SOF_CMD_TYPE(0x003)
|
||||
#define SOF_IPC_PM_CLK_SET SOF_CMD_TYPE(0x004)
|
||||
#define SOF_IPC_PM_CLK_GET SOF_CMD_TYPE(0x005)
|
||||
#define SOF_IPC_PM_CLK_REQ SOF_CMD_TYPE(0x006)
|
||||
#define SOF_IPC_PM_CORE_ENABLE SOF_CMD_TYPE(0x007)
|
||||
|
||||
/* component runtime config - multiple different types */
|
||||
#define SOF_IPC_COMP_SET_VALUE SOF_CMD_TYPE(0x001)
|
||||
#define SOF_IPC_COMP_GET_VALUE SOF_CMD_TYPE(0x002)
|
||||
#define SOF_IPC_COMP_SET_DATA SOF_CMD_TYPE(0x003)
|
||||
#define SOF_IPC_COMP_GET_DATA SOF_CMD_TYPE(0x004)
|
||||
|
||||
/* DAI messages */
|
||||
#define SOF_IPC_DAI_CONFIG SOF_CMD_TYPE(0x001)
|
||||
#define SOF_IPC_DAI_LOOPBACK SOF_CMD_TYPE(0x002)
|
||||
|
||||
/* stream */
|
||||
#define SOF_IPC_STREAM_PCM_PARAMS SOF_CMD_TYPE(0x001)
|
||||
#define SOF_IPC_STREAM_PCM_PARAMS_REPLY SOF_CMD_TYPE(0x002)
|
||||
#define SOF_IPC_STREAM_PCM_FREE SOF_CMD_TYPE(0x003)
|
||||
#define SOF_IPC_STREAM_TRIG_START SOF_CMD_TYPE(0x004)
|
||||
#define SOF_IPC_STREAM_TRIG_STOP SOF_CMD_TYPE(0x005)
|
||||
#define SOF_IPC_STREAM_TRIG_PAUSE SOF_CMD_TYPE(0x006)
|
||||
#define SOF_IPC_STREAM_TRIG_RELEASE SOF_CMD_TYPE(0x007)
|
||||
#define SOF_IPC_STREAM_TRIG_DRAIN SOF_CMD_TYPE(0x008)
|
||||
#define SOF_IPC_STREAM_TRIG_XRUN SOF_CMD_TYPE(0x009)
|
||||
#define SOF_IPC_STREAM_POSITION SOF_CMD_TYPE(0x00a)
|
||||
#define SOF_IPC_STREAM_VORBIS_PARAMS SOF_CMD_TYPE(0x010)
|
||||
#define SOF_IPC_STREAM_VORBIS_FREE SOF_CMD_TYPE(0x011)
|
||||
|
||||
/* trace and debug */
|
||||
#define SOF_IPC_TRACE_DMA_PARAMS SOF_CMD_TYPE(0x001)
|
||||
#define SOF_IPC_TRACE_DMA_POSITION SOF_CMD_TYPE(0x002)
|
||||
|
||||
/* Get message component id */
|
||||
#define SOF_IPC_MESSAGE_ID(x) ((x) & 0xffff)
|
||||
|
||||
/* maximum message size for mailbox Tx/Rx */
|
||||
#define SOF_IPC_MSG_MAX_SIZE 384
|
||||
|
||||
/*
|
||||
* Structure Header - Header for all IPC structures except command structs.
|
||||
* The size can be greater than the structure size and that means there is
|
||||
* extended bespoke data beyond the end of the structure including variable
|
||||
* arrays.
|
||||
*/
|
||||
|
||||
struct sof_ipc_hdr {
|
||||
uint32_t size; /**< size of structure */
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Command Header - Header for all IPC commands. Identifies IPC message.
|
||||
* The size can be greater than the structure size and that means there is
|
||||
* extended bespoke data beyond the end of the structure including variable
|
||||
* arrays.
|
||||
*/
|
||||
|
||||
struct sof_ipc_cmd_hdr {
|
||||
uint32_t size; /**< size of structure */
|
||||
uint32_t cmd; /**< SOF_IPC_GLB_ + cmd */
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Generic reply message. Some commands override this with their own reply
|
||||
* types that must include this at start.
|
||||
*/
|
||||
struct sof_ipc_reply {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
int32_t error; /**< negative error numbers */
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Compound commands - SOF_IPC_GLB_COMPOUND.
|
||||
*
|
||||
* Compound commands are sent to the DSP as a single IPC operation. The
|
||||
* commands are split into blocks and each block has a header. This header
|
||||
* identifies the command type and the number of commands before the next
|
||||
* header.
|
||||
*/
|
||||
|
||||
struct sof_ipc_compound_hdr {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
uint32_t count; /**< count of 0 means end of compound sequence */
|
||||
} __packed;
|
||||
|
||||
/** @}*/
|
||||
|
||||
#endif
|
|
@ -0,0 +1,118 @@
|
|||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_SOUND_SOF_INFO_H__
|
||||
#define __INCLUDE_SOUND_SOF_INFO_H__
|
||||
|
||||
#include <sound/sof/header.h>
|
||||
#include <sound/sof/stream.h>
|
||||
|
||||
/*
|
||||
* Firmware boot and version
|
||||
*/
|
||||
|
||||
#define SOF_IPC_MAX_ELEMS 16
|
||||
|
||||
/* extended data types that can be appended onto end of sof_ipc_fw_ready */
|
||||
enum sof_ipc_ext_data {
|
||||
SOF_IPC_EXT_DMA_BUFFER = 0,
|
||||
SOF_IPC_EXT_WINDOW,
|
||||
};
|
||||
|
||||
/* FW version - SOF_IPC_GLB_VERSION */
|
||||
struct sof_ipc_fw_version {
|
||||
struct sof_ipc_hdr hdr;
|
||||
uint16_t major;
|
||||
uint16_t minor;
|
||||
uint16_t micro;
|
||||
uint16_t build;
|
||||
uint8_t date[12];
|
||||
uint8_t time[10];
|
||||
uint8_t tag[6];
|
||||
uint32_t abi_version;
|
||||
|
||||
/* reserved for future use */
|
||||
uint32_t reserved[4];
|
||||
} __packed;
|
||||
|
||||
/* FW ready Message - sent by firmware when boot has completed */
|
||||
struct sof_ipc_fw_ready {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
uint32_t dspbox_offset; /* dsp initiated IPC mailbox */
|
||||
uint32_t hostbox_offset; /* host initiated IPC mailbox */
|
||||
uint32_t dspbox_size;
|
||||
uint32_t hostbox_size;
|
||||
struct sof_ipc_fw_version version;
|
||||
|
||||
/* Miscellaneous debug flags showing build/debug features enabled */
|
||||
union {
|
||||
uint64_t reserved;
|
||||
struct {
|
||||
uint64_t build:1;
|
||||
uint64_t locks:1;
|
||||
uint64_t locks_verbose:1;
|
||||
uint64_t gdb:1;
|
||||
} bits;
|
||||
} debug;
|
||||
|
||||
/* reserved for future use */
|
||||
uint32_t reserved[4];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Extended Firmware data. All optional, depends on platform/arch.
|
||||
*/
|
||||
enum sof_ipc_region {
|
||||
SOF_IPC_REGION_DOWNBOX = 0,
|
||||
SOF_IPC_REGION_UPBOX,
|
||||
SOF_IPC_REGION_TRACE,
|
||||
SOF_IPC_REGION_DEBUG,
|
||||
SOF_IPC_REGION_STREAM,
|
||||
SOF_IPC_REGION_REGS,
|
||||
SOF_IPC_REGION_EXCEPTION,
|
||||
};
|
||||
|
||||
struct sof_ipc_ext_data_hdr {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
uint32_t type; /**< SOF_IPC_EXT_ */
|
||||
} __packed;
|
||||
|
||||
struct sof_ipc_dma_buffer_elem {
|
||||
struct sof_ipc_hdr hdr;
|
||||
uint32_t type; /**< SOF_IPC_REGION_ */
|
||||
uint32_t id; /**< platform specific - used to map to host memory */
|
||||
struct sof_ipc_host_buffer buffer;
|
||||
} __packed;
|
||||
|
||||
/* extended data DMA buffers for IPC, trace and debug */
|
||||
struct sof_ipc_dma_buffer_data {
|
||||
struct sof_ipc_ext_data_hdr ext_hdr;
|
||||
uint32_t num_buffers;
|
||||
|
||||
/* host files in buffer[n].buffer */
|
||||
struct sof_ipc_dma_buffer_elem buffer[];
|
||||
} __packed;
|
||||
|
||||
struct sof_ipc_window_elem {
|
||||
struct sof_ipc_hdr hdr;
|
||||
uint32_t type; /**< SOF_IPC_REGION_ */
|
||||
uint32_t id; /**< platform specific - used to map to host memory */
|
||||
uint32_t flags; /**< R, W, RW, etc - to define */
|
||||
uint32_t size; /**< size of region in bytes */
|
||||
/* offset in window region as windows can be partitioned */
|
||||
uint32_t offset;
|
||||
} __packed;
|
||||
|
||||
/* extended data memory windows for IPC, trace and debug */
|
||||
struct sof_ipc_window {
|
||||
struct sof_ipc_ext_data_hdr ext_hdr;
|
||||
uint32_t num_windows;
|
||||
struct sof_ipc_window_elem window[];
|
||||
} __packed;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,48 @@
|
|||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_SOUND_SOF_PM_H__
|
||||
#define __INCLUDE_SOUND_SOF_PM_H__
|
||||
|
||||
#include <sound/sof/header.h>
|
||||
|
||||
/*
|
||||
* PM
|
||||
*/
|
||||
|
||||
/* PM context element */
|
||||
struct sof_ipc_pm_ctx_elem {
|
||||
struct sof_ipc_hdr hdr;
|
||||
uint32_t type;
|
||||
uint32_t size;
|
||||
uint64_t addr;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* PM context - SOF_IPC_PM_CTX_SAVE, SOF_IPC_PM_CTX_RESTORE,
|
||||
* SOF_IPC_PM_CTX_SIZE
|
||||
*/
|
||||
struct sof_ipc_pm_ctx {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
struct sof_ipc_host_buffer buffer;
|
||||
uint32_t num_elems;
|
||||
uint32_t size;
|
||||
|
||||
/* reserved for future use */
|
||||
uint32_t reserved[8];
|
||||
|
||||
struct sof_ipc_pm_ctx_elem elems[];
|
||||
} __packed;
|
||||
|
||||
/* enable or disable cores - SOF_IPC_PM_CORE_ENABLE */
|
||||
struct sof_ipc_pm_core_config {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
uint32_t enable_mask;
|
||||
} __packed;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,148 @@
|
|||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_SOUND_SOF_STREAM_H__
|
||||
#define __INCLUDE_SOUND_SOF_STREAM_H__
|
||||
|
||||
#include <sound/sof/header.h>
|
||||
|
||||
/*
|
||||
* Stream configuration.
|
||||
*/
|
||||
|
||||
#define SOF_IPC_MAX_CHANNELS 8
|
||||
|
||||
/* common sample rates for use in masks */
|
||||
#define SOF_RATE_8000 (1 << 0) /**< 8000Hz */
|
||||
#define SOF_RATE_11025 (1 << 1) /**< 11025Hz */
|
||||
#define SOF_RATE_12000 (1 << 2) /**< 12000Hz */
|
||||
#define SOF_RATE_16000 (1 << 3) /**< 16000Hz */
|
||||
#define SOF_RATE_22050 (1 << 4) /**< 22050Hz */
|
||||
#define SOF_RATE_24000 (1 << 5) /**< 24000Hz */
|
||||
#define SOF_RATE_32000 (1 << 6) /**< 32000Hz */
|
||||
#define SOF_RATE_44100 (1 << 7) /**< 44100Hz */
|
||||
#define SOF_RATE_48000 (1 << 8) /**< 48000Hz */
|
||||
#define SOF_RATE_64000 (1 << 9) /**< 64000Hz */
|
||||
#define SOF_RATE_88200 (1 << 10) /**< 88200Hz */
|
||||
#define SOF_RATE_96000 (1 << 11) /**< 96000Hz */
|
||||
#define SOF_RATE_176400 (1 << 12) /**< 176400Hz */
|
||||
#define SOF_RATE_192000 (1 << 13) /**< 192000Hz */
|
||||
|
||||
/* continuous and non-standard rates for flexibility */
|
||||
#define SOF_RATE_CONTINUOUS (1 << 30) /**< range */
|
||||
#define SOF_RATE_KNOT (1 << 31) /**< non-continuous */
|
||||
|
||||
/* generic PCM flags for runtime settings */
|
||||
#define SOF_PCM_FLAG_XRUN_STOP (1 << 0) /**< Stop on any XRUN */
|
||||
|
||||
/* stream PCM frame format */
|
||||
enum sof_ipc_frame {
|
||||
SOF_IPC_FRAME_S16_LE = 0,
|
||||
SOF_IPC_FRAME_S24_4LE,
|
||||
SOF_IPC_FRAME_S32_LE,
|
||||
SOF_IPC_FRAME_FLOAT,
|
||||
/* other formats here */
|
||||
};
|
||||
|
||||
/* stream buffer format */
|
||||
enum sof_ipc_buffer_format {
|
||||
SOF_IPC_BUFFER_INTERLEAVED,
|
||||
SOF_IPC_BUFFER_NONINTERLEAVED,
|
||||
/* other formats here */
|
||||
};
|
||||
|
||||
/* stream direction */
|
||||
enum sof_ipc_stream_direction {
|
||||
SOF_IPC_STREAM_PLAYBACK = 0,
|
||||
SOF_IPC_STREAM_CAPTURE,
|
||||
};
|
||||
|
||||
/* stream ring info */
|
||||
struct sof_ipc_host_buffer {
|
||||
struct sof_ipc_hdr hdr;
|
||||
uint32_t phy_addr;
|
||||
uint32_t pages;
|
||||
uint32_t size;
|
||||
uint32_t reserved[3];
|
||||
} __packed;
|
||||
|
||||
struct sof_ipc_stream_params {
|
||||
struct sof_ipc_hdr hdr;
|
||||
struct sof_ipc_host_buffer buffer;
|
||||
uint32_t direction; /**< enum sof_ipc_stream_direction */
|
||||
uint32_t frame_fmt; /**< enum sof_ipc_frame */
|
||||
uint32_t buffer_fmt; /**< enum sof_ipc_buffer_format */
|
||||
uint32_t rate;
|
||||
uint16_t stream_tag;
|
||||
uint16_t channels;
|
||||
uint16_t sample_valid_bytes;
|
||||
uint16_t sample_container_bytes;
|
||||
|
||||
/* for notifying host period has completed - 0 means no period IRQ */
|
||||
uint32_t host_period_bytes;
|
||||
|
||||
uint32_t reserved[2];
|
||||
uint16_t chmap[SOF_IPC_MAX_CHANNELS]; /**< channel map - SOF_CHMAP_ */
|
||||
} __packed;
|
||||
|
||||
/* PCM params info - SOF_IPC_STREAM_PCM_PARAMS */
|
||||
struct sof_ipc_pcm_params {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
uint32_t comp_id;
|
||||
uint32_t flags; /**< generic PCM flags - SOF_PCM_FLAG_ */
|
||||
uint32_t reserved[2];
|
||||
struct sof_ipc_stream_params params;
|
||||
} __packed;
|
||||
|
||||
/* PCM params info reply - SOF_IPC_STREAM_PCM_PARAMS_REPLY */
|
||||
struct sof_ipc_pcm_params_reply {
|
||||
struct sof_ipc_reply rhdr;
|
||||
uint32_t comp_id;
|
||||
uint32_t posn_offset;
|
||||
} __packed;
|
||||
|
||||
/* free stream - SOF_IPC_STREAM_PCM_PARAMS */
|
||||
struct sof_ipc_stream {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
uint32_t comp_id;
|
||||
} __packed;
|
||||
|
||||
/* flags indicating which time stamps are in sync with each other */
|
||||
#define SOF_TIME_HOST_SYNC (1 << 0)
|
||||
#define SOF_TIME_DAI_SYNC (1 << 1)
|
||||
#define SOF_TIME_WALL_SYNC (1 << 2)
|
||||
#define SOF_TIME_STAMP_SYNC (1 << 3)
|
||||
|
||||
/* flags indicating which time stamps are valid */
|
||||
#define SOF_TIME_HOST_VALID (1 << 8)
|
||||
#define SOF_TIME_DAI_VALID (1 << 9)
|
||||
#define SOF_TIME_WALL_VALID (1 << 10)
|
||||
#define SOF_TIME_STAMP_VALID (1 << 11)
|
||||
|
||||
/* flags indicating time stamps are 64bit else 3use low 32bit */
|
||||
#define SOF_TIME_HOST_64 (1 << 16)
|
||||
#define SOF_TIME_DAI_64 (1 << 17)
|
||||
#define SOF_TIME_WALL_64 (1 << 18)
|
||||
#define SOF_TIME_STAMP_64 (1 << 19)
|
||||
|
||||
struct sof_ipc_stream_posn {
|
||||
struct sof_ipc_reply rhdr;
|
||||
uint32_t comp_id; /**< host component ID */
|
||||
uint32_t flags; /**< SOF_TIME_ */
|
||||
uint32_t wallclock_hz; /**< frequency of wallclock in Hz */
|
||||
uint32_t timestamp_ns; /**< resolution of timestamp in ns */
|
||||
uint64_t host_posn; /**< host DMA position in bytes */
|
||||
uint64_t dai_posn; /**< DAI DMA position in bytes */
|
||||
uint64_t comp_posn; /**< comp position in bytes */
|
||||
uint64_t wallclock; /**< audio wall clock */
|
||||
uint64_t timestamp; /**< system time stamp */
|
||||
uint32_t xrun_comp_id; /**< comp ID of XRUN component */
|
||||
int32_t xrun_size; /**< XRUN size in bytes */
|
||||
} __packed;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,256 @@
|
|||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_SOUND_SOF_TOPOLOGY_H__
|
||||
#define __INCLUDE_SOUND_SOF_TOPOLOGY_H__
|
||||
|
||||
#include <sound/sof/header.h>
|
||||
|
||||
/*
|
||||
* Component
|
||||
*/
|
||||
|
||||
/* types of component */
|
||||
enum sof_comp_type {
|
||||
SOF_COMP_NONE = 0,
|
||||
SOF_COMP_HOST,
|
||||
SOF_COMP_DAI,
|
||||
SOF_COMP_SG_HOST, /**< scatter gather variant */
|
||||
SOF_COMP_SG_DAI, /**< scatter gather variant */
|
||||
SOF_COMP_VOLUME,
|
||||
SOF_COMP_MIXER,
|
||||
SOF_COMP_MUX,
|
||||
SOF_COMP_SRC,
|
||||
SOF_COMP_SPLITTER,
|
||||
SOF_COMP_TONE,
|
||||
SOF_COMP_SWITCH,
|
||||
SOF_COMP_BUFFER,
|
||||
SOF_COMP_EQ_IIR,
|
||||
SOF_COMP_EQ_FIR,
|
||||
SOF_COMP_KEYWORD_DETECT,
|
||||
SOF_COMP_KPB, /* A key phrase buffer component */
|
||||
SOF_COMP_SELECTOR, /**< channel selector 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 */
|
||||
};
|
||||
|
||||
/* XRUN action for component */
|
||||
#define SOF_XRUN_STOP 1 /**< stop stream */
|
||||
#define SOF_XRUN_UNDER_ZERO 2 /**< send 0s to sink */
|
||||
#define SOF_XRUN_OVER_NULL 4 /**< send data to NULL */
|
||||
|
||||
/* create new generic component - SOF_IPC_TPLG_COMP_NEW */
|
||||
struct sof_ipc_comp {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
uint32_t id;
|
||||
enum sof_comp_type type;
|
||||
uint32_t pipeline_id;
|
||||
|
||||
/* reserved for future use */
|
||||
uint32_t reserved[2];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Component Buffers
|
||||
*/
|
||||
|
||||
/*
|
||||
* SOF memory capabilities, add new ones at the end
|
||||
*/
|
||||
#define SOF_MEM_CAPS_RAM (1 << 0)
|
||||
#define SOF_MEM_CAPS_ROM (1 << 1)
|
||||
#define SOF_MEM_CAPS_EXT (1 << 2) /**< external */
|
||||
#define SOF_MEM_CAPS_LP (1 << 3) /**< low power */
|
||||
#define SOF_MEM_CAPS_HP (1 << 4) /**< high performance */
|
||||
#define SOF_MEM_CAPS_DMA (1 << 5) /**< DMA'able */
|
||||
#define SOF_MEM_CAPS_CACHE (1 << 6) /**< cacheable */
|
||||
#define SOF_MEM_CAPS_EXEC (1 << 7) /**< executable */
|
||||
|
||||
/* 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_ */
|
||||
} __packed;
|
||||
|
||||
/* generic component config data - must always be after struct sof_ipc_comp */
|
||||
struct sof_ipc_comp_config {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
uint32_t periods_sink; /**< 0 means variable */
|
||||
uint32_t periods_source; /**< 0 means variable */
|
||||
uint32_t reserved1; /**< reserved */
|
||||
uint32_t frame_fmt; /**< SOF_IPC_FRAME_ */
|
||||
uint32_t xrun_action;
|
||||
|
||||
/* reserved for future use */
|
||||
uint32_t reserved[2];
|
||||
} __packed;
|
||||
|
||||
/* generic host component */
|
||||
struct sof_ipc_comp_host {
|
||||
struct sof_ipc_comp comp;
|
||||
struct sof_ipc_comp_config config;
|
||||
uint32_t direction; /**< SOF_IPC_STREAM_ */
|
||||
uint32_t no_irq; /**< don't send periodic IRQ to host/DSP */
|
||||
uint32_t dmac_config; /**< DMA engine specific */
|
||||
} __packed;
|
||||
|
||||
/* generic DAI component */
|
||||
struct sof_ipc_comp_dai {
|
||||
struct sof_ipc_comp comp;
|
||||
struct sof_ipc_comp_config config;
|
||||
uint32_t direction; /**< SOF_IPC_STREAM_ */
|
||||
uint32_t dai_index; /**< index of this type dai */
|
||||
uint32_t type; /**< DAI type - SOF_DAI_ */
|
||||
uint32_t reserved; /**< reserved */
|
||||
} __packed;
|
||||
|
||||
/* generic mixer component */
|
||||
struct sof_ipc_comp_mixer {
|
||||
struct sof_ipc_comp comp;
|
||||
struct sof_ipc_comp_config config;
|
||||
} __packed;
|
||||
|
||||
/* volume ramping types */
|
||||
enum sof_volume_ramp {
|
||||
SOF_VOLUME_LINEAR = 0,
|
||||
SOF_VOLUME_LOG,
|
||||
SOF_VOLUME_LINEAR_ZC,
|
||||
SOF_VOLUME_LOG_ZC,
|
||||
};
|
||||
|
||||
/* generic volume component */
|
||||
struct sof_ipc_comp_volume {
|
||||
struct sof_ipc_comp comp;
|
||||
struct sof_ipc_comp_config config;
|
||||
uint32_t channels;
|
||||
uint32_t min_value;
|
||||
uint32_t max_value;
|
||||
uint32_t ramp; /**< SOF_VOLUME_ */
|
||||
uint32_t initial_ramp; /**< ramp space in ms */
|
||||
} __packed;
|
||||
|
||||
/* generic SRC component */
|
||||
struct sof_ipc_comp_src {
|
||||
struct sof_ipc_comp comp;
|
||||
struct sof_ipc_comp_config config;
|
||||
/* either source or sink rate must be non zero */
|
||||
uint32_t source_rate; /**< source rate or 0 for variable */
|
||||
uint32_t sink_rate; /**< sink rate or 0 for variable */
|
||||
uint32_t rate_mask; /**< SOF_RATE_ supported rates */
|
||||
} __packed;
|
||||
|
||||
/* generic MUX component */
|
||||
struct sof_ipc_comp_mux {
|
||||
struct sof_ipc_comp comp;
|
||||
struct sof_ipc_comp_config config;
|
||||
} __packed;
|
||||
|
||||
/* generic tone generator component */
|
||||
struct sof_ipc_comp_tone {
|
||||
struct sof_ipc_comp comp;
|
||||
struct sof_ipc_comp_config config;
|
||||
int32_t sample_rate;
|
||||
int32_t frequency;
|
||||
int32_t amplitude;
|
||||
int32_t freq_mult;
|
||||
int32_t ampl_mult;
|
||||
int32_t length;
|
||||
int32_t period;
|
||||
int32_t repeats;
|
||||
int32_t ramp_step;
|
||||
} __packed;
|
||||
|
||||
/** \brief Types of processing components */
|
||||
enum sof_ipc_process_type {
|
||||
SOF_PROCESS_NONE = 0, /**< None */
|
||||
SOF_PROCESS_EQFIR, /**< Intel FIR */
|
||||
SOF_PROCESS_EQIIR, /**< Intel IIR */
|
||||
SOF_PROCESS_KEYWORD_DETECT, /**< Keyword Detection */
|
||||
SOF_PROCESS_KPB, /**< KeyPhrase Buffer Manager */
|
||||
SOF_PROCESS_CHAN_SELECTOR, /**< Channel Selector */
|
||||
};
|
||||
|
||||
/* generic "effect", "codec" or proprietary processing component */
|
||||
struct sof_ipc_comp_process {
|
||||
struct sof_ipc_comp comp;
|
||||
struct sof_ipc_comp_config config;
|
||||
uint32_t size; /**< size of bespoke data section in bytes */
|
||||
uint32_t type; /**< sof_ipc_process_type */
|
||||
|
||||
/* reserved for future use */
|
||||
uint32_t reserved[7];
|
||||
|
||||
unsigned char data[0];
|
||||
} __packed;
|
||||
|
||||
/* frees components, buffers and pipelines
|
||||
* SOF_IPC_TPLG_COMP_FREE, SOF_IPC_TPLG_PIPE_FREE, SOF_IPC_TPLG_BUFFER_FREE
|
||||
*/
|
||||
struct sof_ipc_free {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
uint32_t id;
|
||||
} __packed;
|
||||
|
||||
struct sof_ipc_comp_reply {
|
||||
struct sof_ipc_reply rhdr;
|
||||
uint32_t id;
|
||||
uint32_t offset;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Pipeline
|
||||
*/
|
||||
|
||||
/** \brief Types of pipeline scheduling time domains */
|
||||
enum sof_ipc_pipe_sched_time_domain {
|
||||
SOF_TIME_DOMAIN_DMA = 0, /**< DMA interrupt */
|
||||
SOF_TIME_DOMAIN_TIMER, /**< Timer interrupt */
|
||||
};
|
||||
|
||||
/* new pipeline - SOF_IPC_TPLG_PIPE_NEW */
|
||||
struct sof_ipc_pipe_new {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
uint32_t comp_id; /**< component id for pipeline */
|
||||
uint32_t pipeline_id; /**< pipeline id */
|
||||
uint32_t sched_id; /**< Scheduling component id */
|
||||
uint32_t core; /**< core we run on */
|
||||
uint32_t period; /**< execution period in us*/
|
||||
uint32_t priority; /**< priority level 0 (low) to 10 (max) */
|
||||
uint32_t period_mips; /**< worst case instruction count per period */
|
||||
uint32_t frames_per_sched;/**< output frames of pipeline, 0 is variable */
|
||||
uint32_t xrun_limit_usecs; /**< report xruns greater than limit */
|
||||
uint32_t time_domain; /**< scheduling time domain */
|
||||
} __packed;
|
||||
|
||||
/* pipeline construction complete - SOF_IPC_TPLG_PIPE_COMPLETE */
|
||||
struct sof_ipc_pipe_ready {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
uint32_t comp_id;
|
||||
} __packed;
|
||||
|
||||
struct sof_ipc_pipe_free {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
uint32_t comp_id;
|
||||
} __packed;
|
||||
|
||||
/* connect two components in pipeline - SOF_IPC_TPLG_COMP_CONNECT */
|
||||
struct sof_ipc_pipe_comp_connect {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
uint32_t source_id;
|
||||
uint32_t sink_id;
|
||||
} __packed;
|
||||
|
||||
/* external events */
|
||||
enum sof_event_types {
|
||||
SOF_EVENT_NONE = 0,
|
||||
SOF_KEYWORD_DETECT_DAPM_EVENT,
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,67 @@
|
|||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_SOUND_SOF_TRACE_H__
|
||||
#define __INCLUDE_SOUND_SOF_TRACE_H__
|
||||
|
||||
#include <sound/sof/header.h>
|
||||
#include <sound/sof/stream.h>
|
||||
|
||||
/*
|
||||
* DMA for Trace
|
||||
*/
|
||||
|
||||
#define SOF_TRACE_FILENAME_SIZE 32
|
||||
|
||||
/* DMA for Trace params info - SOF_IPC_DEBUG_DMA_PARAMS */
|
||||
struct sof_ipc_dma_trace_params {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
struct sof_ipc_host_buffer buffer;
|
||||
uint32_t stream_tag;
|
||||
} __packed;
|
||||
|
||||
/* DMA for Trace params info - SOF_IPC_DEBUG_DMA_PARAMS */
|
||||
struct sof_ipc_dma_trace_posn {
|
||||
struct sof_ipc_reply rhdr;
|
||||
uint32_t host_offset; /* Offset of DMA host buffer */
|
||||
uint32_t overflow; /* overflow bytes if any */
|
||||
uint32_t messages; /* total trace messages */
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Commom debug
|
||||
*/
|
||||
|
||||
/*
|
||||
* SOF panic codes
|
||||
*/
|
||||
#define SOF_IPC_PANIC_MAGIC 0x0dead000
|
||||
#define SOF_IPC_PANIC_MAGIC_MASK 0x0ffff000
|
||||
#define SOF_IPC_PANIC_CODE_MASK 0x00000fff
|
||||
#define SOF_IPC_PANIC_MEM (SOF_IPC_PANIC_MAGIC | 0x0)
|
||||
#define SOF_IPC_PANIC_WORK (SOF_IPC_PANIC_MAGIC | 0x1)
|
||||
#define SOF_IPC_PANIC_IPC (SOF_IPC_PANIC_MAGIC | 0x2)
|
||||
#define SOF_IPC_PANIC_ARCH (SOF_IPC_PANIC_MAGIC | 0x3)
|
||||
#define SOF_IPC_PANIC_PLATFORM (SOF_IPC_PANIC_MAGIC | 0x4)
|
||||
#define SOF_IPC_PANIC_TASK (SOF_IPC_PANIC_MAGIC | 0x5)
|
||||
#define SOF_IPC_PANIC_EXCEPTION (SOF_IPC_PANIC_MAGIC | 0x6)
|
||||
#define SOF_IPC_PANIC_DEADLOCK (SOF_IPC_PANIC_MAGIC | 0x7)
|
||||
#define SOF_IPC_PANIC_STACK (SOF_IPC_PANIC_MAGIC | 0x8)
|
||||
#define SOF_IPC_PANIC_IDLE (SOF_IPC_PANIC_MAGIC | 0x9)
|
||||
#define SOF_IPC_PANIC_WFI (SOF_IPC_PANIC_MAGIC | 0xa)
|
||||
#define SOF_IPC_PANIC_ASSERT (SOF_IPC_PANIC_MAGIC | 0xb)
|
||||
|
||||
/* panic info include filename and line number */
|
||||
struct sof_ipc_panic_info {
|
||||
struct sof_ipc_hdr hdr;
|
||||
uint32_t code; /* SOF_IPC_PANIC_ */
|
||||
char filename[SOF_TRACE_FILENAME_SIZE];
|
||||
uint32_t linenum;
|
||||
} __packed;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,44 @@
|
|||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_SOUND_SOF_XTENSA_H__
|
||||
#define __INCLUDE_SOUND_SOF_XTENSA_H__
|
||||
|
||||
#include <sound/sof/header.h>
|
||||
|
||||
/*
|
||||
* Architecture specific debug
|
||||
*/
|
||||
|
||||
/* Xtensa Firmware Oops data */
|
||||
struct sof_ipc_dsp_oops_xtensa {
|
||||
struct sof_ipc_hdr hdr;
|
||||
uint32_t exccause;
|
||||
uint32_t excvaddr;
|
||||
uint32_t ps;
|
||||
uint32_t epc1;
|
||||
uint32_t epc2;
|
||||
uint32_t epc3;
|
||||
uint32_t epc4;
|
||||
uint32_t epc5;
|
||||
uint32_t epc6;
|
||||
uint32_t epc7;
|
||||
uint32_t eps2;
|
||||
uint32_t eps3;
|
||||
uint32_t eps4;
|
||||
uint32_t eps5;
|
||||
uint32_t eps6;
|
||||
uint32_t eps7;
|
||||
uint32_t depc;
|
||||
uint32_t intenable;
|
||||
uint32_t interrupt;
|
||||
uint32_t sar;
|
||||
uint32_t stack;
|
||||
} __packed;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,62 @@
|
|||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
/**
|
||||
* SOF ABI versioning is based on Semantic Versioning where we have a given
|
||||
* MAJOR.MINOR.PATCH version number. See https://semver.org/
|
||||
*
|
||||
* Rules for incrementing or changing version :-
|
||||
*
|
||||
* 1) Increment MAJOR version if you make incompatible API changes. MINOR and
|
||||
* PATCH should be reset to 0.
|
||||
*
|
||||
* 2) Increment MINOR version if you add backwards compatible features or
|
||||
* changes. PATCH should be reset to 0.
|
||||
*
|
||||
* 3) Increment PATCH version if you add backwards compatible bug fixes.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_UAPI_SOUND_SOF_ABI_H__
|
||||
#define __INCLUDE_UAPI_SOUND_SOF_ABI_H__
|
||||
|
||||
/* SOF ABI version major, minor and patch numbers */
|
||||
#define SOF_ABI_MAJOR 3
|
||||
#define SOF_ABI_MINOR 4
|
||||
#define SOF_ABI_PATCH 0
|
||||
|
||||
/* SOF ABI version number. Format within 32bit word is MMmmmppp */
|
||||
#define SOF_ABI_MAJOR_SHIFT 24
|
||||
#define SOF_ABI_MAJOR_MASK 0xff
|
||||
#define SOF_ABI_MINOR_SHIFT 12
|
||||
#define SOF_ABI_MINOR_MASK 0xfff
|
||||
#define SOF_ABI_PATCH_SHIFT 0
|
||||
#define SOF_ABI_PATCH_MASK 0xfff
|
||||
|
||||
#define SOF_ABI_VER(major, minor, patch) \
|
||||
(((major) << SOF_ABI_MAJOR_SHIFT) | \
|
||||
((minor) << SOF_ABI_MINOR_SHIFT) | \
|
||||
((patch) << SOF_ABI_PATCH_SHIFT))
|
||||
|
||||
#define SOF_ABI_VERSION_MAJOR(version) \
|
||||
(((version) >> SOF_ABI_MAJOR_SHIFT) & SOF_ABI_MAJOR_MASK)
|
||||
#define SOF_ABI_VERSION_MINOR(version) \
|
||||
(((version) >> SOF_ABI_MINOR_SHIFT) & SOF_ABI_MINOR_MASK)
|
||||
#define SOF_ABI_VERSION_PATCH(version) \
|
||||
(((version) >> SOF_ABI_PATCH_SHIFT) & SOF_ABI_PATCH_MASK)
|
||||
|
||||
#define SOF_ABI_VERSION_INCOMPATIBLE(sof_ver, client_ver) \
|
||||
(SOF_ABI_VERSION_MAJOR((sof_ver)) != \
|
||||
SOF_ABI_VERSION_MAJOR((client_ver)) \
|
||||
)
|
||||
|
||||
#define SOF_ABI_VERSION SOF_ABI_VER(SOF_ABI_MAJOR, SOF_ABI_MINOR, SOF_ABI_PATCH)
|
||||
|
||||
/* SOF ABI magic number "SOF\0". */
|
||||
#define SOF_ABI_MAGIC 0x00464F53
|
||||
|
||||
#endif
|
|
@ -0,0 +1,172 @@
|
|||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_UAPI_SOUND_SOF_USER_EQ_H__
|
||||
#define __INCLUDE_UAPI_SOUND_SOF_USER_EQ_H__
|
||||
|
||||
/* FIR EQ type */
|
||||
|
||||
#define SOF_EQ_FIR_IDX_SWITCH 0
|
||||
|
||||
#define SOF_EQ_FIR_MAX_SIZE 4096 /* Max size allowed for coef data in bytes */
|
||||
|
||||
#define SOF_EQ_FIR_MAX_LENGTH 192 /* Max length for individual filter */
|
||||
|
||||
#define SOF_EQ_FIR_MAX_RESPONSES 8 /* A blob can define max 8 FIR EQs */
|
||||
|
||||
/*
|
||||
* eq_fir_configuration data structure contains this information
|
||||
* uint32_t size
|
||||
* This is the number of bytes need to store the received EQ
|
||||
* configuration.
|
||||
* uint16_t channels_in_config
|
||||
* This describes the number of channels in this EQ config data. It
|
||||
* can be different from PLATFORM_MAX_CHANNELS.
|
||||
* uint16_t number_of_responses
|
||||
* 0=no responses, 1=one response defined, 2=two responses defined, etc.
|
||||
* int16_t data[]
|
||||
* assign_response[channels_in_config]
|
||||
* 0 = use first response, 1 = use 2nd response, etc.
|
||||
* E.g. {0, 0, 0, 0, 1, 1, 1, 1} would apply to channels 0-3 the
|
||||
* same first defined response and for to channels 4-7 the second.
|
||||
* coef_data[]
|
||||
* Repeated data
|
||||
* { filter_length, output_shift, h[] }
|
||||
* for every EQ response defined where vector h has filter_length
|
||||
* number of coefficients. Coefficients in h[] are in Q1.15 format.
|
||||
* E.g. 16384 (Q1.15) = 0.5. The shifts are number of right shifts.
|
||||
*
|
||||
* NOTE: The channels_in_config must be even to have coef_data aligned to
|
||||
* 32 bit word in RAM. Therefore a mono EQ assign must be duplicated to 2ch
|
||||
* even if it would never used. Similarly a 5ch EQ assign must be increased
|
||||
* to 6ch. EQ init will return an error if this is not met.
|
||||
*
|
||||
* NOTE: The filter_length must be multiple of four. Therefore the filter must
|
||||
* be padded from the end with zeros have this condition met.
|
||||
*/
|
||||
|
||||
struct sof_eq_fir_config {
|
||||
uint32_t size;
|
||||
uint16_t channels_in_config;
|
||||
uint16_t number_of_responses;
|
||||
|
||||
/* reserved */
|
||||
uint32_t reserved[4];
|
||||
|
||||
int16_t data[];
|
||||
} __packed;
|
||||
|
||||
struct sof_eq_fir_coef_data {
|
||||
int16_t length; /* Number of FIR taps */
|
||||
int16_t out_shift; /* Amount of right shifts at output */
|
||||
|
||||
/* reserved */
|
||||
uint32_t reserved[4];
|
||||
|
||||
int16_t coef[]; /* FIR coefficients */
|
||||
} __packed;
|
||||
|
||||
/* In the struct above there's two 16 bit words (length, shift) and four
|
||||
* reserved 32 bit words before the actual FIR coefficients. This information
|
||||
* is used in parsing of the configuration blob.
|
||||
*/
|
||||
#define SOF_EQ_FIR_COEF_NHEADER \
|
||||
(sizeof(struct sof_eq_fir_coef_data) / sizeof(int16_t))
|
||||
|
||||
/* IIR EQ type */
|
||||
|
||||
#define SOF_EQ_IIR_IDX_SWITCH 0
|
||||
|
||||
#define SOF_EQ_IIR_MAX_SIZE 1024 /* Max size allowed for coef data in bytes */
|
||||
|
||||
#define SOF_EQ_IIR_MAX_RESPONSES 8 /* A blob can define max 8 IIR EQs */
|
||||
|
||||
/* eq_iir_configuration
|
||||
* uint32_t channels_in_config
|
||||
* This describes the number of channels in this EQ config data. It
|
||||
* can be different from PLATFORM_MAX_CHANNELS.
|
||||
* uint32_t number_of_responses_defined
|
||||
* 0=no responses, 1=one response defined, 2=two responses defined, etc.
|
||||
* int32_t data[]
|
||||
* Data consist of two parts. First is the response assign vector that
|
||||
* has length of channels_in_config. The latter part is coefficient
|
||||
* data.
|
||||
* uint32_t assign_response[channels_in_config]
|
||||
* -1 = not defined, 0 = use first response, 1 = use 2nd, etc.
|
||||
* E.g. {0, 0, 0, 0, -1, -1, -1, -1} would apply to channels 0-3 the
|
||||
* same first defined response and leave channels 4-7 unequalized.
|
||||
* coefficient_data[]
|
||||
* <1st EQ>
|
||||
* uint32_t num_biquads
|
||||
* uint32_t num_biquads_in_series
|
||||
* <1st biquad>
|
||||
* int32_t coef_a2 Q2.30 format
|
||||
* int32_t coef_a1 Q2.30 format
|
||||
* int32_t coef_b2 Q2.30 format
|
||||
* int32_t coef_b1 Q2.30 format
|
||||
* int32_t coef_b0 Q2.30 format
|
||||
* int32_t output_shift number of shifts right, shift left is negative
|
||||
* int32_t output_gain Q2.14 format
|
||||
* <2nd biquad>
|
||||
* ...
|
||||
* <2nd EQ>
|
||||
*
|
||||
* Note: A flat response biquad can be made with a section set to
|
||||
* b0 = 1.0, gain = 1.0, and other parameters set to 0
|
||||
* {0, 0, 0, 0, 1073741824, 0, 16484}
|
||||
*/
|
||||
|
||||
struct sof_eq_iir_config {
|
||||
uint32_t size;
|
||||
uint32_t channels_in_config;
|
||||
uint32_t number_of_responses;
|
||||
|
||||
/* reserved */
|
||||
uint32_t reserved[4];
|
||||
|
||||
int32_t data[]; /* eq_assign[channels], eq 0, eq 1, ... */
|
||||
} __packed;
|
||||
|
||||
struct sof_eq_iir_header_df2t {
|
||||
uint32_t num_sections;
|
||||
uint32_t num_sections_in_series;
|
||||
|
||||
/* reserved */
|
||||
uint32_t reserved[4];
|
||||
|
||||
int32_t biquads[]; /* Repeated biquad coefficients */
|
||||
} __packed;
|
||||
|
||||
struct sof_eq_iir_biquad_df2t {
|
||||
int32_t a2; /* Q2.30 */
|
||||
int32_t a1; /* Q2.30 */
|
||||
int32_t b2; /* Q2.30 */
|
||||
int32_t b1; /* Q2.30 */
|
||||
int32_t b0; /* Q2.30 */
|
||||
int32_t output_shift; /* Number of right shifts */
|
||||
int32_t output_gain; /* Q2.14 */
|
||||
} __packed;
|
||||
|
||||
/* A full 22th order equalizer with 11 biquads cover octave bands 1-11 in
|
||||
* in the 0 - 20 kHz bandwidth.
|
||||
*/
|
||||
#define SOF_EQ_IIR_DF2T_BIQUADS_MAX 11
|
||||
|
||||
/* The number of int32_t words in sof_eq_iir_header_df2t:
|
||||
* num_sections, num_sections_in_series, reserved[4]
|
||||
*/
|
||||
#define SOF_EQ_IIR_NHEADER_DF2T \
|
||||
(sizeof(struct sof_eq_iir_header_df2t) / sizeof(int32_t))
|
||||
|
||||
/* The number of int32_t words in sof_eq_iir_biquad_df2t:
|
||||
* a2, a1, b2, b1, b0, output_shift, output_gain
|
||||
*/
|
||||
#define SOF_EQ_IIR_NBIQUAD_DF2T \
|
||||
(sizeof(struct sof_eq_iir_biquad_df2t) / sizeof(int32_t))
|
||||
|
||||
#endif
|
|
@ -0,0 +1,78 @@
|
|||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Firmware file format .
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_UAPI_SOF_FW_H__
|
||||
#define __INCLUDE_UAPI_SOF_FW_H__
|
||||
|
||||
#define SND_SOF_FW_SIG_SIZE 4
|
||||
#define SND_SOF_FW_ABI 1
|
||||
#define SND_SOF_FW_SIG "Reef"
|
||||
|
||||
/*
|
||||
* Firmware module is made up of 1 . N blocks of different types. The
|
||||
* Block header is used to determine where and how block is to be copied in the
|
||||
* DSP/host memory space.
|
||||
*/
|
||||
enum snd_sof_fw_blk_type {
|
||||
SOF_FW_BLK_TYPE_INVALID = -1,
|
||||
SOF_FW_BLK_TYPE_START = 0,
|
||||
SOF_FW_BLK_TYPE_RSRVD0 = SOF_FW_BLK_TYPE_START,
|
||||
SOF_FW_BLK_TYPE_IRAM = 1, /* local instruction RAM */
|
||||
SOF_FW_BLK_TYPE_DRAM = 2, /* local data RAM */
|
||||
SOF_FW_BLK_TYPE_SRAM = 3, /* system RAM */
|
||||
SOF_FW_BLK_TYPE_ROM = 4,
|
||||
SOF_FW_BLK_TYPE_IMR = 5,
|
||||
SOF_FW_BLK_TYPE_RSRVD6 = 6,
|
||||
SOF_FW_BLK_TYPE_RSRVD7 = 7,
|
||||
SOF_FW_BLK_TYPE_RSRVD8 = 8,
|
||||
SOF_FW_BLK_TYPE_RSRVD9 = 9,
|
||||
SOF_FW_BLK_TYPE_RSRVD10 = 10,
|
||||
SOF_FW_BLK_TYPE_RSRVD11 = 11,
|
||||
SOF_FW_BLK_TYPE_RSRVD12 = 12,
|
||||
SOF_FW_BLK_TYPE_RSRVD13 = 13,
|
||||
SOF_FW_BLK_TYPE_RSRVD14 = 14,
|
||||
/* use SOF_FW_BLK_TYPE_RSVRDX for new block types */
|
||||
SOF_FW_BLK_TYPE_NUM
|
||||
};
|
||||
|
||||
struct snd_sof_blk_hdr {
|
||||
enum snd_sof_fw_blk_type type;
|
||||
uint32_t size; /* bytes minus this header */
|
||||
uint32_t offset; /* offset from base */
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Firmware file is made up of 1 .. N different modules types. The module
|
||||
* type is used to determine how to load and parse the module.
|
||||
*/
|
||||
enum snd_sof_fw_mod_type {
|
||||
SOF_FW_BASE = 0, /* base firmware image */
|
||||
SOF_FW_MODULE = 1, /* firmware module */
|
||||
};
|
||||
|
||||
struct snd_sof_mod_hdr {
|
||||
enum snd_sof_fw_mod_type type;
|
||||
uint32_t size; /* bytes minus this header */
|
||||
uint32_t num_blocks; /* number of blocks */
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Firmware file header.
|
||||
*/
|
||||
struct snd_sof_fw_header {
|
||||
unsigned char sig[SND_SOF_FW_SIG_SIZE]; /* "Reef" */
|
||||
uint32_t file_size; /* size of file minus this header */
|
||||
uint32_t num_modules; /* number of modules */
|
||||
uint32_t abi; /* version of header format */
|
||||
} __packed;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,27 @@
|
|||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_UAPI_SOUND_SOF_USER_HEADER_H__
|
||||
#define __INCLUDE_UAPI_SOUND_SOF_USER_HEADER_H__
|
||||
|
||||
/*
|
||||
* Header for all non IPC ABI data.
|
||||
*
|
||||
* Identifies data type, size and ABI.
|
||||
* Used by any bespoke component data structures or binary blobs.
|
||||
*/
|
||||
struct sof_abi_hdr {
|
||||
uint32_t magic; /**< 'S', 'O', 'F', '\0' */
|
||||
uint32_t type; /**< component specific type */
|
||||
uint32_t size; /**< size in bytes of data excl. this struct */
|
||||
uint32_t abi; /**< SOF ABI version */
|
||||
uint32_t reserved[4]; /**< reserved for future use */
|
||||
uint32_t data[0]; /**< Component data - opaque to core */
|
||||
} __packed;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,188 @@
|
|||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_UAPI_SOUND_SOF_USER_MANIFEST_H__
|
||||
#define __INCLUDE_UAPI_SOUND_SOF_USER_MANIFEST_H__
|
||||
|
||||
/* start offset for base FW module */
|
||||
#define SOF_MAN_ELF_TEXT_OFFSET 0x2000
|
||||
|
||||
/* FW Extended Manifest Header id = $AE1 */
|
||||
#define SOF_MAN_EXT_HEADER_MAGIC 0x31454124
|
||||
|
||||
/* module type load type */
|
||||
#define SOF_MAN_MOD_TYPE_BUILTIN 0
|
||||
#define SOF_MAN_MOD_TYPE_MODULE 1
|
||||
|
||||
struct sof_man_module_type {
|
||||
uint32_t load_type:4; /* SOF_MAN_MOD_TYPE_ */
|
||||
uint32_t auto_start:1;
|
||||
uint32_t domain_ll:1;
|
||||
uint32_t domain_dp:1;
|
||||
uint32_t rsvd_:25;
|
||||
};
|
||||
|
||||
/* segment flags.type */
|
||||
#define SOF_MAN_SEGMENT_TEXT 0
|
||||
#define SOF_MAN_SEGMENT_RODATA 1
|
||||
#define SOF_MAN_SEGMENT_DATA 1
|
||||
#define SOF_MAN_SEGMENT_BSS 2
|
||||
#define SOF_MAN_SEGMENT_EMPTY 15
|
||||
|
||||
union sof_man_segment_flags {
|
||||
uint32_t ul;
|
||||
struct {
|
||||
uint32_t contents:1;
|
||||
uint32_t alloc:1;
|
||||
uint32_t load:1;
|
||||
uint32_t readonly:1;
|
||||
uint32_t code:1;
|
||||
uint32_t data:1;
|
||||
uint32_t _rsvd0:2;
|
||||
uint32_t type:4; /* MAN_SEGMENT_ */
|
||||
uint32_t _rsvd1:4;
|
||||
uint32_t length:16; /* of segment in pages */
|
||||
} r;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Module segment descriptor. Used by ROM - Immutable.
|
||||
*/
|
||||
struct sof_man_segment_desc {
|
||||
union sof_man_segment_flags flags;
|
||||
uint32_t v_base_addr;
|
||||
uint32_t file_offset;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* The firmware binary can be split into several modules.
|
||||
*/
|
||||
|
||||
#define SOF_MAN_MOD_ID_LEN 4
|
||||
#define SOF_MAN_MOD_NAME_LEN 8
|
||||
#define SOF_MAN_MOD_SHA256_LEN 32
|
||||
#define SOF_MAN_MOD_ID {'$', 'A', 'M', 'E'}
|
||||
|
||||
/*
|
||||
* Each module has an entry in the FW header. Used by ROM - Immutable.
|
||||
*/
|
||||
struct sof_man_module {
|
||||
uint8_t struct_id[SOF_MAN_MOD_ID_LEN]; /* SOF_MAN_MOD_ID */
|
||||
uint8_t name[SOF_MAN_MOD_NAME_LEN];
|
||||
uint8_t uuid[16];
|
||||
struct sof_man_module_type type;
|
||||
uint8_t hash[SOF_MAN_MOD_SHA256_LEN];
|
||||
uint32_t entry_point;
|
||||
uint16_t cfg_offset;
|
||||
uint16_t cfg_count;
|
||||
uint32_t affinity_mask;
|
||||
uint16_t instance_max_count; /* max number of instances */
|
||||
uint16_t instance_bss_size; /* instance (pages) */
|
||||
struct sof_man_segment_desc segment[3];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Each module has a configuration in the FW header. Used by ROM - Immutable.
|
||||
*/
|
||||
struct sof_man_mod_config {
|
||||
uint32_t par[4]; /* module parameters */
|
||||
uint32_t is_pages; /* actual size of instance .bss (pages) */
|
||||
uint32_t cps; /* cycles per second */
|
||||
uint32_t ibs; /* input buffer size (bytes) */
|
||||
uint32_t obs; /* output buffer size (bytes) */
|
||||
uint32_t module_flags; /* flags, reserved for future use */
|
||||
uint32_t cpc; /* cycles per single run */
|
||||
uint32_t obls; /* output block size, reserved for future use */
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* FW Manifest Header
|
||||
*/
|
||||
|
||||
#define SOF_MAN_FW_HDR_FW_NAME_LEN 8
|
||||
#define SOF_MAN_FW_HDR_ID {'$', 'A', 'M', '1'}
|
||||
#define SOF_MAN_FW_HDR_NAME "ADSPFW"
|
||||
#define SOF_MAN_FW_HDR_FLAGS 0x0
|
||||
#define SOF_MAN_FW_HDR_FEATURES 0xff
|
||||
|
||||
/*
|
||||
* The firmware has a standard header that is checked by the ROM on firmware
|
||||
* loading. preload_page_count is used by DMA code loader and is entire
|
||||
* image size on CNL. i.e. CNL: total size of the binary’s .text and .rodata
|
||||
* Used by ROM - Immutable.
|
||||
*/
|
||||
struct sof_man_fw_header {
|
||||
uint8_t header_id[4];
|
||||
uint32_t header_len;
|
||||
uint8_t name[SOF_MAN_FW_HDR_FW_NAME_LEN];
|
||||
/* number of pages of preloaded image loaded by driver */
|
||||
uint32_t preload_page_count;
|
||||
uint32_t fw_image_flags;
|
||||
uint32_t feature_mask;
|
||||
uint16_t major_version;
|
||||
uint16_t minor_version;
|
||||
uint16_t hotfix_version;
|
||||
uint16_t build_version;
|
||||
uint32_t num_module_entries;
|
||||
uint32_t hw_buf_base_addr;
|
||||
uint32_t hw_buf_length;
|
||||
/* target address for binary loading as offset in IMR - must be == base offset */
|
||||
uint32_t load_offset;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Firmware manifest descriptor. This can contain N modules and N module
|
||||
* configs. Used by ROM - Immutable.
|
||||
*/
|
||||
struct sof_man_fw_desc {
|
||||
struct sof_man_fw_header header;
|
||||
|
||||
/* Warning - hack for module arrays. For some unknown reason the we
|
||||
* have a variable size array of struct man_module followed by a
|
||||
* variable size array of struct mod_config. These should have been
|
||||
* merged into a variable array of a parent structure. We have to hack
|
||||
* around this in many places....
|
||||
*
|
||||
* struct sof_man_module man_module[];
|
||||
* struct sof_man_mod_config mod_config[];
|
||||
*/
|
||||
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Component Descriptor. Used by ROM - Immutable.
|
||||
*/
|
||||
struct sof_man_component_desc {
|
||||
uint32_t reserved[2]; /* all 0 */
|
||||
uint32_t version;
|
||||
uint8_t hash[SOF_MAN_MOD_SHA256_LEN];
|
||||
uint32_t base_offset;
|
||||
uint32_t limit_offset;
|
||||
uint32_t attributes[4];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Audio DSP extended metadata. Used by ROM - Immutable.
|
||||
*/
|
||||
struct sof_man_adsp_meta_file_ext {
|
||||
uint32_t ext_type; /* always 17 for ADSP extension */
|
||||
uint32_t ext_len;
|
||||
uint32_t imr_type;
|
||||
uint8_t reserved[16]; /* all 0 */
|
||||
struct sof_man_component_desc comp_desc[1];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Module Manifest for rimage module metadata. Not used by ROM.
|
||||
*/
|
||||
struct sof_man_module_manifest {
|
||||
struct sof_man_module module;
|
||||
uint32_t text_size;
|
||||
} __packed;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,107 @@
|
|||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
* Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
|
||||
* Keyon Jie <yang.jie@linux.intel.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
* Topology IDs and tokens.
|
||||
*
|
||||
* ** MUST BE ALIGNED WITH TOPOLOGY CONFIGURATION TOKEN VALUES **
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_UAPI_SOF_TOPOLOGY_H__
|
||||
#define __INCLUDE_UAPI_SOF_TOPOLOGY_H__
|
||||
|
||||
/*
|
||||
* Kcontrol IDs
|
||||
*/
|
||||
#define SOF_TPLG_KCTL_VOL_ID 256
|
||||
#define SOF_TPLG_KCTL_ENUM_ID 257
|
||||
#define SOF_TPLG_KCTL_BYTES_ID 258
|
||||
#define SOF_TPLG_KCTL_SWITCH_ID 259
|
||||
|
||||
/*
|
||||
* Tokens - must match values in topology configurations
|
||||
*/
|
||||
|
||||
/* buffers */
|
||||
#define SOF_TKN_BUF_SIZE 100
|
||||
#define SOF_TKN_BUF_CAPS 101
|
||||
|
||||
/* DAI */
|
||||
/* Token retired with ABI 3.2, do not use for new capabilities
|
||||
* #define SOF_TKN_DAI_DMAC_CONFIG 153
|
||||
*/
|
||||
#define SOF_TKN_DAI_TYPE 154
|
||||
#define SOF_TKN_DAI_INDEX 155
|
||||
#define SOF_TKN_DAI_DIRECTION 156
|
||||
|
||||
/* scheduling */
|
||||
#define SOF_TKN_SCHED_PERIOD 200
|
||||
#define SOF_TKN_SCHED_PRIORITY 201
|
||||
#define SOF_TKN_SCHED_MIPS 202
|
||||
#define SOF_TKN_SCHED_CORE 203
|
||||
#define SOF_TKN_SCHED_FRAMES 204
|
||||
#define SOF_TKN_SCHED_TIME_DOMAIN 205
|
||||
|
||||
/* volume */
|
||||
#define SOF_TKN_VOLUME_RAMP_STEP_TYPE 250
|
||||
#define SOF_TKN_VOLUME_RAMP_STEP_MS 251
|
||||
|
||||
/* SRC */
|
||||
#define SOF_TKN_SRC_RATE_IN 300
|
||||
#define SOF_TKN_SRC_RATE_OUT 301
|
||||
|
||||
/* PCM */
|
||||
#define SOF_TKN_PCM_DMAC_CONFIG 353
|
||||
|
||||
/* Generic components */
|
||||
#define SOF_TKN_COMP_PERIOD_SINK_COUNT 400
|
||||
#define SOF_TKN_COMP_PERIOD_SOURCE_COUNT 401
|
||||
#define SOF_TKN_COMP_FORMAT 402
|
||||
/* Token retired with ABI 3.2, do not use for new capabilities
|
||||
* #define SOF_TKN_COMP_PRELOAD_COUNT 403
|
||||
*/
|
||||
|
||||
/* SSP */
|
||||
#define SOF_TKN_INTEL_SSP_CLKS_CONTROL 500
|
||||
#define SOF_TKN_INTEL_SSP_MCLK_ID 501
|
||||
#define SOF_TKN_INTEL_SSP_SAMPLE_BITS 502
|
||||
#define SOF_TKN_INTEL_SSP_FRAME_PULSE_WIDTH 503
|
||||
#define SOF_TKN_INTEL_SSP_QUIRKS 504
|
||||
#define SOF_TKN_INTEL_SSP_TDM_PADDING_PER_SLOT 505
|
||||
|
||||
/* DMIC */
|
||||
#define SOF_TKN_INTEL_DMIC_DRIVER_VERSION 600
|
||||
#define SOF_TKN_INTEL_DMIC_CLK_MIN 601
|
||||
#define SOF_TKN_INTEL_DMIC_CLK_MAX 602
|
||||
#define SOF_TKN_INTEL_DMIC_DUTY_MIN 603
|
||||
#define SOF_TKN_INTEL_DMIC_DUTY_MAX 604
|
||||
#define SOF_TKN_INTEL_DMIC_NUM_PDM_ACTIVE 605
|
||||
#define SOF_TKN_INTEL_DMIC_SAMPLE_RATE 608
|
||||
#define SOF_TKN_INTEL_DMIC_FIFO_WORD_LENGTH 609
|
||||
|
||||
/* DMIC PDM */
|
||||
#define SOF_TKN_INTEL_DMIC_PDM_CTRL_ID 700
|
||||
#define SOF_TKN_INTEL_DMIC_PDM_MIC_A_Enable 701
|
||||
#define SOF_TKN_INTEL_DMIC_PDM_MIC_B_Enable 702
|
||||
#define SOF_TKN_INTEL_DMIC_PDM_POLARITY_A 703
|
||||
#define SOF_TKN_INTEL_DMIC_PDM_POLARITY_B 704
|
||||
#define SOF_TKN_INTEL_DMIC_PDM_CLK_EDGE 705
|
||||
#define SOF_TKN_INTEL_DMIC_PDM_SKEW 706
|
||||
|
||||
/* Tone */
|
||||
#define SOF_TKN_TONE_SAMPLE_RATE 800
|
||||
|
||||
/* Processing Components */
|
||||
#define SOF_TKN_PROCESS_TYPE 900
|
||||
|
||||
/* for backward compatibility */
|
||||
#define SOF_TKN_EFFECT_TYPE SOF_TKN_PROCESS_TYPE
|
||||
|
||||
#endif
|
|
@ -0,0 +1,21 @@
|
|||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_UAPI_SOUND_SOF_USER_TONE_H__
|
||||
#define __INCLUDE_UAPI_SOUND_SOF_USER_TONE_H__
|
||||
|
||||
#define SOF_TONE_IDX_FREQUENCY 0
|
||||
#define SOF_TONE_IDX_AMPLITUDE 1
|
||||
#define SOF_TONE_IDX_FREQ_MULT 2
|
||||
#define SOF_TONE_IDX_AMPL_MULT 3
|
||||
#define SOF_TONE_IDX_LENGTH 4
|
||||
#define SOF_TONE_IDX_PERIOD 5
|
||||
#define SOF_TONE_IDX_REPEATS 6
|
||||
#define SOF_TONE_IDX_LIN_RAMP_STEP 7
|
||||
|
||||
#endif
|
|
@ -0,0 +1,66 @@
|
|||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_UAPI_SOUND_SOF_USER_TRACE_H__
|
||||
#define __INCLUDE_UAPI_SOUND_SOF_USER_TRACE_H__
|
||||
|
||||
/*
|
||||
* Host system time.
|
||||
*
|
||||
* This property is used by the driver to pass down information about
|
||||
* current system time. It is expressed in us.
|
||||
* FW translates timestamps (in log entries, probe pockets) to this time
|
||||
* domain.
|
||||
*
|
||||
* (cavs: SystemTime).
|
||||
*/
|
||||
struct system_time {
|
||||
uint32_t val_l; /* Lower dword of current host time value */
|
||||
uint32_t val_u; /* Upper dword of current host time value */
|
||||
} __packed;
|
||||
|
||||
#define LOG_ENABLE 1 /* Enable logging */
|
||||
#define LOG_DISABLE 0 /* Disable logging */
|
||||
|
||||
#define LOG_LEVEL_CRITICAL 1 /* (FDK fatal) */
|
||||
#define LOG_LEVEL_VERBOSE 2
|
||||
|
||||
/*
|
||||
* Layout of a log fifo.
|
||||
*/
|
||||
struct log_buffer_layout {
|
||||
uint32_t read_ptr; /*read pointer */
|
||||
uint32_t write_ptr; /* write pointer */
|
||||
uint32_t buffer[0]; /* buffer */
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Log buffer status reported by FW.
|
||||
*/
|
||||
struct log_buffer_status {
|
||||
uint32_t core_id; /* ID of core that logged to other half */
|
||||
} __packed;
|
||||
|
||||
#define TRACE_ID_LENGTH 12
|
||||
|
||||
/*
|
||||
* Log entry header.
|
||||
*
|
||||
* The header is followed by an array of arguments (uint32_t[]).
|
||||
* Number of arguments is specified by the params_num field of log_entry
|
||||
*/
|
||||
struct log_entry_header {
|
||||
uint32_t id_0 : TRACE_ID_LENGTH; /* e.g. Pipeline ID */
|
||||
uint32_t id_1 : TRACE_ID_LENGTH; /* e.g. Component ID */
|
||||
uint32_t core_id : 8; /* Reporting core's id */
|
||||
|
||||
uint64_t timestamp; /* Timestamp (in dsp ticks) */
|
||||
uint32_t log_entry_address; /* Address of log entry in ELF */
|
||||
} __packed;
|
||||
|
||||
#endif
|
|
@ -49,8 +49,7 @@ static const struct file_operations snd_shutdown_f_ops;
|
|||
|
||||
/* locked for registering/using */
|
||||
static DECLARE_BITMAP(snd_cards_lock, SNDRV_CARDS);
|
||||
struct snd_card *snd_cards[SNDRV_CARDS];
|
||||
EXPORT_SYMBOL(snd_cards);
|
||||
static struct snd_card *snd_cards[SNDRV_CARDS];
|
||||
|
||||
static DEFINE_MUTEX(snd_card_mutex);
|
||||
|
||||
|
@ -268,6 +267,26 @@ int snd_card_new(struct device *parent, int idx, const char *xid,
|
|||
}
|
||||
EXPORT_SYMBOL(snd_card_new);
|
||||
|
||||
/**
|
||||
* snd_card_ref - Get the card object from the index
|
||||
* @idx: the card index
|
||||
*
|
||||
* Returns a card object corresponding to the given index or NULL if not found.
|
||||
* Release the object via snd_card_unref().
|
||||
*/
|
||||
struct snd_card *snd_card_ref(int idx)
|
||||
{
|
||||
struct snd_card *card;
|
||||
|
||||
mutex_lock(&snd_card_mutex);
|
||||
card = snd_cards[idx];
|
||||
if (card)
|
||||
get_device(&card->card_dev);
|
||||
mutex_unlock(&snd_card_mutex);
|
||||
return card;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_card_ref);
|
||||
|
||||
/* return non-zero if a card is already locked */
|
||||
int snd_card_locked(int card)
|
||||
{
|
||||
|
|
|
@ -30,53 +30,6 @@
|
|||
#endif
|
||||
#include <sound/memalloc.h>
|
||||
|
||||
/*
|
||||
*
|
||||
* Generic memory allocators
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* snd_malloc_pages - allocate pages with the given size
|
||||
* @size: the size to allocate in bytes
|
||||
* @gfp_flags: the allocation conditions, GFP_XXX
|
||||
*
|
||||
* Allocates the physically contiguous pages with the given size.
|
||||
*
|
||||
* Return: The pointer of the buffer, or %NULL if no enough memory.
|
||||
*/
|
||||
void *snd_malloc_pages(size_t size, gfp_t gfp_flags)
|
||||
{
|
||||
int pg;
|
||||
|
||||
if (WARN_ON(!size))
|
||||
return NULL;
|
||||
if (WARN_ON(!gfp_flags))
|
||||
return NULL;
|
||||
gfp_flags |= __GFP_COMP; /* compound page lets parts be mapped */
|
||||
pg = get_order(size);
|
||||
return (void *) __get_free_pages(gfp_flags, pg);
|
||||
}
|
||||
EXPORT_SYMBOL(snd_malloc_pages);
|
||||
|
||||
/**
|
||||
* snd_free_pages - release the pages
|
||||
* @ptr: the buffer pointer to release
|
||||
* @size: the allocated buffer size
|
||||
*
|
||||
* Releases the buffer allocated via snd_malloc_pages().
|
||||
*/
|
||||
void snd_free_pages(void *ptr, size_t size)
|
||||
{
|
||||
int pg;
|
||||
|
||||
if (ptr == NULL)
|
||||
return;
|
||||
pg = get_order(size);
|
||||
free_pages((unsigned long) ptr, pg);
|
||||
}
|
||||
EXPORT_SYMBOL(snd_free_pages);
|
||||
|
||||
/*
|
||||
*
|
||||
* Bus-specific memory allocators
|
||||
|
@ -190,8 +143,8 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size,
|
|||
dmab->bytes = 0;
|
||||
switch (type) {
|
||||
case SNDRV_DMA_TYPE_CONTINUOUS:
|
||||
dmab->area = snd_malloc_pages(size,
|
||||
(__force gfp_t)(unsigned long)device);
|
||||
dmab->area = alloc_pages_exact(size,
|
||||
(__force gfp_t)(unsigned long)device);
|
||||
dmab->addr = 0;
|
||||
break;
|
||||
#ifdef CONFIG_HAS_DMA
|
||||
|
@ -275,7 +228,7 @@ void snd_dma_free_pages(struct snd_dma_buffer *dmab)
|
|||
{
|
||||
switch (dmab->dev.type) {
|
||||
case SNDRV_DMA_TYPE_CONTINUOUS:
|
||||
snd_free_pages(dmab->area, dmab->bytes);
|
||||
free_pages_exact(dmab->area, dmab->bytes);
|
||||
break;
|
||||
#ifdef CONFIG_HAS_DMA
|
||||
#ifdef CONFIG_GENERIC_ALLOCATOR
|
||||
|
|
|
@ -1403,24 +1403,32 @@ static int snd_mixer_oss_notify_handler(struct snd_card *card, int cmd)
|
|||
|
||||
static int __init alsa_mixer_oss_init(void)
|
||||
{
|
||||
struct snd_card *card;
|
||||
int idx;
|
||||
|
||||
snd_mixer_oss_notify_callback = snd_mixer_oss_notify_handler;
|
||||
for (idx = 0; idx < SNDRV_CARDS; idx++) {
|
||||
if (snd_cards[idx])
|
||||
snd_mixer_oss_notify_handler(snd_cards[idx], SND_MIXER_OSS_NOTIFY_REGISTER);
|
||||
card = snd_card_ref(idx);
|
||||
if (card) {
|
||||
snd_mixer_oss_notify_handler(card, SND_MIXER_OSS_NOTIFY_REGISTER);
|
||||
snd_card_unref(card);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit alsa_mixer_oss_exit(void)
|
||||
{
|
||||
struct snd_card *card;
|
||||
int idx;
|
||||
|
||||
snd_mixer_oss_notify_callback = NULL;
|
||||
for (idx = 0; idx < SNDRV_CARDS; idx++) {
|
||||
if (snd_cards[idx])
|
||||
snd_mixer_oss_notify_handler(snd_cards[idx], SND_MIXER_OSS_NOTIFY_FREE);
|
||||
card = snd_card_ref(idx);
|
||||
if (card) {
|
||||
snd_mixer_oss_notify_handler(card, SND_MIXER_OSS_NOTIFY_FREE);
|
||||
snd_card_unref(card);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -959,22 +959,22 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
|
|||
return -ENOMEM;
|
||||
|
||||
size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status));
|
||||
runtime->status = snd_malloc_pages(size, GFP_KERNEL);
|
||||
runtime->status = alloc_pages_exact(size, GFP_KERNEL);
|
||||
if (runtime->status == NULL) {
|
||||
kfree(runtime);
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset((void*)runtime->status, 0, size);
|
||||
memset(runtime->status, 0, size);
|
||||
|
||||
size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control));
|
||||
runtime->control = snd_malloc_pages(size, GFP_KERNEL);
|
||||
runtime->control = alloc_pages_exact(size, GFP_KERNEL);
|
||||
if (runtime->control == NULL) {
|
||||
snd_free_pages((void*)runtime->status,
|
||||
free_pages_exact(runtime->status,
|
||||
PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)));
|
||||
kfree(runtime);
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset((void*)runtime->control, 0, size);
|
||||
memset(runtime->control, 0, size);
|
||||
|
||||
init_waitqueue_head(&runtime->sleep);
|
||||
init_waitqueue_head(&runtime->tsleep);
|
||||
|
@ -1000,9 +1000,9 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
|
|||
runtime = substream->runtime;
|
||||
if (runtime->private_free != NULL)
|
||||
runtime->private_free(runtime);
|
||||
snd_free_pages((void*)runtime->status,
|
||||
free_pages_exact(runtime->status,
|
||||
PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)));
|
||||
snd_free_pages((void*)runtime->control,
|
||||
free_pages_exact(runtime->control,
|
||||
PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)));
|
||||
kfree(runtime->hw_constraints.rules);
|
||||
/* Avoid concurrent access to runtime via PCM timer interface */
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include <sound/rawmidi.h>
|
||||
#include <sound/seq_kernel.h>
|
||||
#include <sound/info.h>
|
||||
#include "../seq_clientmgr.h"
|
||||
|
||||
/* max. applications */
|
||||
#define SNDRV_SEQ_OSS_MAX_CLIENTS 16
|
||||
|
@ -150,11 +151,16 @@ snd_seq_oss_dispatch(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, int a
|
|||
return snd_seq_kernel_client_dispatch(dp->cseq, ev, atomic, hop);
|
||||
}
|
||||
|
||||
/* ioctl */
|
||||
/* ioctl for writeq */
|
||||
static inline int
|
||||
snd_seq_oss_control(struct seq_oss_devinfo *dp, unsigned int type, void *arg)
|
||||
{
|
||||
return snd_seq_kernel_client_ctl(dp->cseq, type, arg);
|
||||
int err;
|
||||
|
||||
snd_seq_client_ioctl_lock(dp->cseq);
|
||||
err = snd_seq_kernel_client_ctl(dp->cseq, type, arg);
|
||||
snd_seq_client_ioctl_unlock(dp->cseq);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* fill the addresses in header */
|
||||
|
|
|
@ -180,14 +180,11 @@ insert_queue(struct seq_oss_devinfo *dp, union evrec *rec, struct file *opt)
|
|||
return 0; /* invalid event - no need to insert queue */
|
||||
|
||||
event.time.tick = snd_seq_oss_timer_cur_tick(dp->timer);
|
||||
if (dp->timer->realtime || !dp->timer->running) {
|
||||
if (dp->timer->realtime || !dp->timer->running)
|
||||
snd_seq_oss_dispatch(dp, &event, 0, 0);
|
||||
} else {
|
||||
if (is_nonblock_mode(dp->file_mode))
|
||||
rc = snd_seq_kernel_client_enqueue(dp->cseq, &event, 0, 0);
|
||||
else
|
||||
rc = snd_seq_kernel_client_enqueue_blocking(dp->cseq, &event, opt, 0, 0);
|
||||
}
|
||||
else
|
||||
rc = snd_seq_kernel_client_enqueue(dp->cseq, &event, opt,
|
||||
!is_nonblock_mode(dp->file_mode));
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
@ -116,7 +116,7 @@ snd_seq_oss_writeq_sync(struct seq_oss_writeq *q)
|
|||
rec->t.code = SEQ_SYNCTIMER;
|
||||
rec->t.time = time;
|
||||
q->sync_event_put = 1;
|
||||
snd_seq_kernel_client_enqueue_blocking(dp->cseq, &ev, NULL, 0, 0);
|
||||
snd_seq_kernel_client_enqueue(dp->cseq, &ev, NULL, true);
|
||||
}
|
||||
|
||||
wait_event_interruptible_timeout(q->sync_sleep, ! q->sync_event_put, HZ);
|
||||
|
|
|
@ -179,6 +179,41 @@ struct snd_seq_client *snd_seq_client_use_ptr(int clientid)
|
|||
return client;
|
||||
}
|
||||
|
||||
/* Take refcount and perform ioctl_mutex lock on the given client;
|
||||
* used only for OSS sequencer
|
||||
* Unlock via snd_seq_client_ioctl_unlock() below
|
||||
*/
|
||||
bool snd_seq_client_ioctl_lock(int clientid)
|
||||
{
|
||||
struct snd_seq_client *client;
|
||||
|
||||
client = snd_seq_client_use_ptr(clientid);
|
||||
if (!client)
|
||||
return false;
|
||||
mutex_lock(&client->ioctl_mutex);
|
||||
/* The client isn't unrefed here; see snd_seq_client_ioctl_unlock() */
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_seq_client_ioctl_lock);
|
||||
|
||||
/* Unlock and unref the given client; for OSS sequencer use only */
|
||||
void snd_seq_client_ioctl_unlock(int clientid)
|
||||
{
|
||||
struct snd_seq_client *client;
|
||||
|
||||
client = snd_seq_client_use_ptr(clientid);
|
||||
if (WARN_ON(!client))
|
||||
return;
|
||||
mutex_unlock(&client->ioctl_mutex);
|
||||
/* The doubly unrefs below are intentional; the first one releases the
|
||||
* leftover from snd_seq_client_ioctl_lock() above, and the second one
|
||||
* is for releasing snd_seq_client_use_ptr() in this function
|
||||
*/
|
||||
snd_seq_client_unlock(client);
|
||||
snd_seq_client_unlock(client);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_seq_client_ioctl_unlock);
|
||||
|
||||
static void usage_alloc(struct snd_seq_usage *res, int num)
|
||||
{
|
||||
res->cur += num;
|
||||
|
@ -203,7 +238,6 @@ int __init client_init_data(void)
|
|||
|
||||
static struct snd_seq_client *seq_create_client1(int client_index, int poolsize)
|
||||
{
|
||||
unsigned long flags;
|
||||
int c;
|
||||
struct snd_seq_client *client;
|
||||
|
||||
|
@ -224,7 +258,7 @@ static struct snd_seq_client *seq_create_client1(int client_index, int poolsize)
|
|||
mutex_init(&client->ioctl_mutex);
|
||||
|
||||
/* find free slot in the client table */
|
||||
spin_lock_irqsave(&clients_lock, flags);
|
||||
spin_lock_irq(&clients_lock);
|
||||
if (client_index < 0) {
|
||||
for (c = SNDRV_SEQ_DYNAMIC_CLIENTS_BEGIN;
|
||||
c < SNDRV_SEQ_MAX_CLIENTS;
|
||||
|
@ -232,17 +266,17 @@ static struct snd_seq_client *seq_create_client1(int client_index, int poolsize)
|
|||
if (clienttab[c] || clienttablock[c])
|
||||
continue;
|
||||
clienttab[client->number = c] = client;
|
||||
spin_unlock_irqrestore(&clients_lock, flags);
|
||||
spin_unlock_irq(&clients_lock);
|
||||
return client;
|
||||
}
|
||||
} else {
|
||||
if (clienttab[client_index] == NULL && !clienttablock[client_index]) {
|
||||
clienttab[client->number = client_index] = client;
|
||||
spin_unlock_irqrestore(&clients_lock, flags);
|
||||
spin_unlock_irq(&clients_lock);
|
||||
return client;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&clients_lock, flags);
|
||||
spin_unlock_irq(&clients_lock);
|
||||
snd_seq_pool_delete(&client->pool);
|
||||
kfree(client);
|
||||
return NULL; /* no free slot found or busy, return failure code */
|
||||
|
@ -251,23 +285,21 @@ static struct snd_seq_client *seq_create_client1(int client_index, int poolsize)
|
|||
|
||||
static int seq_free_client1(struct snd_seq_client *client)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (!client)
|
||||
return 0;
|
||||
spin_lock_irqsave(&clients_lock, flags);
|
||||
spin_lock_irq(&clients_lock);
|
||||
clienttablock[client->number] = 1;
|
||||
clienttab[client->number] = NULL;
|
||||
spin_unlock_irqrestore(&clients_lock, flags);
|
||||
spin_unlock_irq(&clients_lock);
|
||||
snd_seq_delete_all_ports(client);
|
||||
snd_seq_queue_client_leave(client->number);
|
||||
snd_use_lock_sync(&client->use_lock);
|
||||
snd_seq_queue_client_termination(client->number);
|
||||
if (client->pool)
|
||||
snd_seq_pool_delete(&client->pool);
|
||||
spin_lock_irqsave(&clients_lock, flags);
|
||||
spin_lock_irq(&clients_lock);
|
||||
clienttablock[client->number] = 0;
|
||||
spin_unlock_irqrestore(&clients_lock, flags);
|
||||
spin_unlock_irq(&clients_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1900,20 +1932,14 @@ static int snd_seq_ioctl_get_subscription(struct snd_seq_client *client,
|
|||
int result;
|
||||
struct snd_seq_client *sender = NULL;
|
||||
struct snd_seq_client_port *sport = NULL;
|
||||
struct snd_seq_subscribers *p;
|
||||
|
||||
result = -EINVAL;
|
||||
if ((sender = snd_seq_client_use_ptr(subs->sender.client)) == NULL)
|
||||
goto __end;
|
||||
if ((sport = snd_seq_port_use_ptr(sender, subs->sender.port)) == NULL)
|
||||
goto __end;
|
||||
p = snd_seq_port_get_subscription(&sport->c_src, &subs->dest);
|
||||
if (p) {
|
||||
result = 0;
|
||||
*subs = p->info;
|
||||
} else
|
||||
result = -ENOENT;
|
||||
|
||||
result = snd_seq_port_get_subscription(&sport->c_src, &subs->dest,
|
||||
subs);
|
||||
__end:
|
||||
if (sport)
|
||||
snd_seq_port_unlock(sport);
|
||||
|
@ -2227,12 +2253,13 @@ int snd_seq_delete_kernel_client(int client)
|
|||
}
|
||||
EXPORT_SYMBOL(snd_seq_delete_kernel_client);
|
||||
|
||||
/* skeleton to enqueue event, called from snd_seq_kernel_client_enqueue
|
||||
* and snd_seq_kernel_client_enqueue_blocking
|
||||
/*
|
||||
* exported, called by kernel clients to enqueue events (w/o blocking)
|
||||
*
|
||||
* RETURN VALUE: zero if succeed, negative if error
|
||||
*/
|
||||
static int kernel_client_enqueue(int client, struct snd_seq_event *ev,
|
||||
struct file *file, int blocking,
|
||||
int atomic, int hop)
|
||||
int snd_seq_kernel_client_enqueue(int client, struct snd_seq_event *ev,
|
||||
struct file *file, bool blocking)
|
||||
{
|
||||
struct snd_seq_client *cptr;
|
||||
int result;
|
||||
|
@ -2255,41 +2282,21 @@ static int kernel_client_enqueue(int client, struct snd_seq_event *ev,
|
|||
if (cptr == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (! cptr->accept_output)
|
||||
if (!cptr->accept_output) {
|
||||
result = -EPERM;
|
||||
else /* send it */
|
||||
} else { /* send it */
|
||||
mutex_lock(&cptr->ioctl_mutex);
|
||||
result = snd_seq_client_enqueue_event(cptr, ev, file, blocking,
|
||||
atomic, hop, NULL);
|
||||
false, 0,
|
||||
&cptr->ioctl_mutex);
|
||||
mutex_unlock(&cptr->ioctl_mutex);
|
||||
}
|
||||
|
||||
snd_seq_client_unlock(cptr);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* exported, called by kernel clients to enqueue events (w/o blocking)
|
||||
*
|
||||
* RETURN VALUE: zero if succeed, negative if error
|
||||
*/
|
||||
int snd_seq_kernel_client_enqueue(int client, struct snd_seq_event * ev,
|
||||
int atomic, int hop)
|
||||
{
|
||||
return kernel_client_enqueue(client, ev, NULL, 0, atomic, hop);
|
||||
}
|
||||
EXPORT_SYMBOL(snd_seq_kernel_client_enqueue);
|
||||
|
||||
/*
|
||||
* exported, called by kernel clients to enqueue events (with blocking)
|
||||
*
|
||||
* RETURN VALUE: zero if succeed, negative if error
|
||||
*/
|
||||
int snd_seq_kernel_client_enqueue_blocking(int client, struct snd_seq_event * ev,
|
||||
struct file *file,
|
||||
int atomic, int hop)
|
||||
{
|
||||
return kernel_client_enqueue(client, ev, file, 1, atomic, hop);
|
||||
}
|
||||
EXPORT_SYMBOL(snd_seq_kernel_client_enqueue_blocking);
|
||||
|
||||
/*
|
||||
* exported, called by kernel clients to dispatch events directly to other
|
||||
* clients, bypassing the queues. Event time-stamp will be updated.
|
||||
|
|
|
@ -93,14 +93,14 @@ struct snd_seq_client *snd_seq_client_use_ptr(int clientid);
|
|||
/* dispatch event to client(s) */
|
||||
int snd_seq_dispatch_event(struct snd_seq_event_cell *cell, int atomic, int hop);
|
||||
|
||||
/* exported to other modules */
|
||||
int snd_seq_kernel_client_enqueue(int client, struct snd_seq_event *ev, int atomic, int hop);
|
||||
int snd_seq_kernel_client_enqueue_blocking(int client, struct snd_seq_event * ev,
|
||||
struct file *file, int atomic, int hop);
|
||||
int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table *wait);
|
||||
int snd_seq_client_notify_subscription(int client, int port,
|
||||
struct snd_seq_port_subscribe *info, int evtype);
|
||||
|
||||
/* only for OSS sequencer */
|
||||
bool snd_seq_client_ioctl_lock(int clientid);
|
||||
void snd_seq_client_ioctl_unlock(int clientid);
|
||||
|
||||
extern int seq_client_load[15];
|
||||
|
||||
#endif
|
||||
|
|
|
@ -98,18 +98,17 @@ static struct snd_seq_event_cell *fifo_cell_out(struct snd_seq_fifo *f);
|
|||
void snd_seq_fifo_clear(struct snd_seq_fifo *f)
|
||||
{
|
||||
struct snd_seq_event_cell *cell;
|
||||
unsigned long flags;
|
||||
|
||||
/* clear overflow flag */
|
||||
atomic_set(&f->overflow, 0);
|
||||
|
||||
snd_use_lock_sync(&f->use_lock);
|
||||
spin_lock_irqsave(&f->lock, flags);
|
||||
spin_lock_irq(&f->lock);
|
||||
/* drain the fifo */
|
||||
while ((cell = fifo_cell_out(f)) != NULL) {
|
||||
snd_seq_cell_free(cell);
|
||||
}
|
||||
spin_unlock_irqrestore(&f->lock, flags);
|
||||
spin_unlock_irq(&f->lock);
|
||||
}
|
||||
|
||||
|
||||
|
@ -195,9 +194,9 @@ int snd_seq_fifo_cell_out(struct snd_seq_fifo *f,
|
|||
}
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
add_wait_queue(&f->input_sleep, &wait);
|
||||
spin_unlock_irq(&f->lock);
|
||||
spin_unlock_irqrestore(&f->lock, flags);
|
||||
schedule();
|
||||
spin_lock_irq(&f->lock);
|
||||
spin_lock_irqsave(&f->lock, flags);
|
||||
remove_wait_queue(&f->input_sleep, &wait);
|
||||
if (signal_pending(current)) {
|
||||
spin_unlock_irqrestore(&f->lock, flags);
|
||||
|
@ -239,7 +238,6 @@ int snd_seq_fifo_poll_wait(struct snd_seq_fifo *f, struct file *file,
|
|||
/* change the size of pool; all old events are removed */
|
||||
int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct snd_seq_pool *newpool, *oldpool;
|
||||
struct snd_seq_event_cell *cell, *next, *oldhead;
|
||||
|
||||
|
@ -255,7 +253,7 @@ int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize)
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&f->lock, flags);
|
||||
spin_lock_irq(&f->lock);
|
||||
/* remember old pool */
|
||||
oldpool = f->pool;
|
||||
oldhead = f->head;
|
||||
|
@ -265,7 +263,7 @@ int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize)
|
|||
f->tail = NULL;
|
||||
f->cells = 0;
|
||||
/* NOTE: overflow flag is not cleared */
|
||||
spin_unlock_irqrestore(&f->lock, flags);
|
||||
spin_unlock_irq(&f->lock);
|
||||
|
||||
/* close the old pool and wait until all users are gone */
|
||||
snd_seq_pool_mark_closing(oldpool);
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
#include <linux/export.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched/signal.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/mm.h>
|
||||
#include <sound/core.h>
|
||||
|
||||
#include <sound/seq_kernel.h>
|
||||
|
@ -244,13 +244,13 @@ static int snd_seq_cell_alloc(struct snd_seq_pool *pool,
|
|||
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
add_wait_queue(&pool->output_sleep, &wait);
|
||||
spin_unlock_irq(&pool->lock);
|
||||
spin_unlock_irqrestore(&pool->lock, flags);
|
||||
if (mutexp)
|
||||
mutex_unlock(mutexp);
|
||||
schedule();
|
||||
if (mutexp)
|
||||
mutex_lock(mutexp);
|
||||
spin_lock_irq(&pool->lock);
|
||||
spin_lock_irqsave(&pool->lock, flags);
|
||||
remove_wait_queue(&pool->output_sleep, &wait);
|
||||
/* interrupted? */
|
||||
if (signal_pending(current)) {
|
||||
|
@ -384,21 +384,20 @@ int snd_seq_pool_init(struct snd_seq_pool *pool)
|
|||
{
|
||||
int cell;
|
||||
struct snd_seq_event_cell *cellptr;
|
||||
unsigned long flags;
|
||||
|
||||
if (snd_BUG_ON(!pool))
|
||||
return -EINVAL;
|
||||
|
||||
cellptr = vmalloc(array_size(sizeof(struct snd_seq_event_cell),
|
||||
pool->size));
|
||||
cellptr = kvmalloc_array(sizeof(struct snd_seq_event_cell), pool->size,
|
||||
GFP_KERNEL);
|
||||
if (!cellptr)
|
||||
return -ENOMEM;
|
||||
|
||||
/* add new cells to the free cell list */
|
||||
spin_lock_irqsave(&pool->lock, flags);
|
||||
spin_lock_irq(&pool->lock);
|
||||
if (pool->ptr) {
|
||||
spin_unlock_irqrestore(&pool->lock, flags);
|
||||
vfree(cellptr);
|
||||
spin_unlock_irq(&pool->lock);
|
||||
kvfree(cellptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -416,7 +415,7 @@ int snd_seq_pool_init(struct snd_seq_pool *pool)
|
|||
/* init statistics */
|
||||
pool->max_used = 0;
|
||||
pool->total_elements = pool->size;
|
||||
spin_unlock_irqrestore(&pool->lock, flags);
|
||||
spin_unlock_irq(&pool->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -435,7 +434,6 @@ void snd_seq_pool_mark_closing(struct snd_seq_pool *pool)
|
|||
/* remove events */
|
||||
int snd_seq_pool_done(struct snd_seq_pool *pool)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct snd_seq_event_cell *ptr;
|
||||
|
||||
if (snd_BUG_ON(!pool))
|
||||
|
@ -449,18 +447,18 @@ int snd_seq_pool_done(struct snd_seq_pool *pool)
|
|||
schedule_timeout_uninterruptible(1);
|
||||
|
||||
/* release all resources */
|
||||
spin_lock_irqsave(&pool->lock, flags);
|
||||
spin_lock_irq(&pool->lock);
|
||||
ptr = pool->ptr;
|
||||
pool->ptr = NULL;
|
||||
pool->free = NULL;
|
||||
pool->total_elements = 0;
|
||||
spin_unlock_irqrestore(&pool->lock, flags);
|
||||
spin_unlock_irq(&pool->lock);
|
||||
|
||||
vfree(ptr);
|
||||
kvfree(ptr);
|
||||
|
||||
spin_lock_irqsave(&pool->lock, flags);
|
||||
spin_lock_irq(&pool->lock);
|
||||
pool->closing = 0;
|
||||
spin_unlock_irqrestore(&pool->lock, flags);
|
||||
spin_unlock_irq(&pool->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -128,7 +128,6 @@ static void port_subs_info_init(struct snd_seq_port_subs_info *grp)
|
|||
struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
|
||||
int port)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct snd_seq_client_port *new_port, *p;
|
||||
int num = -1;
|
||||
|
||||
|
@ -157,7 +156,7 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
|
|||
|
||||
num = port >= 0 ? port : 0;
|
||||
mutex_lock(&client->ports_mutex);
|
||||
write_lock_irqsave(&client->ports_lock, flags);
|
||||
write_lock_irq(&client->ports_lock);
|
||||
list_for_each_entry(p, &client->ports_list_head, list) {
|
||||
if (p->addr.port > num)
|
||||
break;
|
||||
|
@ -169,7 +168,7 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
|
|||
client->num_ports++;
|
||||
new_port->addr.port = num; /* store the port number in the port */
|
||||
sprintf(new_port->name, "port-%d", num);
|
||||
write_unlock_irqrestore(&client->ports_lock, flags);
|
||||
write_unlock_irq(&client->ports_lock);
|
||||
mutex_unlock(&client->ports_mutex);
|
||||
|
||||
return new_port;
|
||||
|
@ -283,11 +282,10 @@ static int port_delete(struct snd_seq_client *client,
|
|||
/* delete a port with the given port id */
|
||||
int snd_seq_delete_port(struct snd_seq_client *client, int port)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct snd_seq_client_port *found = NULL, *p;
|
||||
|
||||
mutex_lock(&client->ports_mutex);
|
||||
write_lock_irqsave(&client->ports_lock, flags);
|
||||
write_lock_irq(&client->ports_lock);
|
||||
list_for_each_entry(p, &client->ports_list_head, list) {
|
||||
if (p->addr.port == port) {
|
||||
/* ok found. delete from the list at first */
|
||||
|
@ -297,7 +295,7 @@ int snd_seq_delete_port(struct snd_seq_client *client, int port)
|
|||
break;
|
||||
}
|
||||
}
|
||||
write_unlock_irqrestore(&client->ports_lock, flags);
|
||||
write_unlock_irq(&client->ports_lock);
|
||||
mutex_unlock(&client->ports_mutex);
|
||||
if (found)
|
||||
return port_delete(client, found);
|
||||
|
@ -308,7 +306,6 @@ int snd_seq_delete_port(struct snd_seq_client *client, int port)
|
|||
/* delete the all ports belonging to the given client */
|
||||
int snd_seq_delete_all_ports(struct snd_seq_client *client)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct list_head deleted_list;
|
||||
struct snd_seq_client_port *port, *tmp;
|
||||
|
||||
|
@ -316,7 +313,7 @@ int snd_seq_delete_all_ports(struct snd_seq_client *client)
|
|||
* clear the port list in the client data.
|
||||
*/
|
||||
mutex_lock(&client->ports_mutex);
|
||||
write_lock_irqsave(&client->ports_lock, flags);
|
||||
write_lock_irq(&client->ports_lock);
|
||||
if (! list_empty(&client->ports_list_head)) {
|
||||
list_add(&deleted_list, &client->ports_list_head);
|
||||
list_del_init(&client->ports_list_head);
|
||||
|
@ -324,7 +321,7 @@ int snd_seq_delete_all_ports(struct snd_seq_client *client)
|
|||
INIT_LIST_HEAD(&deleted_list);
|
||||
}
|
||||
client->num_ports = 0;
|
||||
write_unlock_irqrestore(&client->ports_lock, flags);
|
||||
write_unlock_irq(&client->ports_lock);
|
||||
|
||||
/* remove each port in deleted_list */
|
||||
list_for_each_entry_safe(port, tmp, &deleted_list, list) {
|
||||
|
@ -550,10 +547,10 @@ static void delete_and_unsubscribe_port(struct snd_seq_client *client,
|
|||
list_del_init(list);
|
||||
grp->exclusive = 0;
|
||||
write_unlock_irq(&grp->list_lock);
|
||||
up_write(&grp->list_mutex);
|
||||
|
||||
if (!empty)
|
||||
unsubscribe_port(client, port, grp, &subs->info, ack);
|
||||
up_write(&grp->list_mutex);
|
||||
}
|
||||
|
||||
/* connect two ports */
|
||||
|
@ -635,20 +632,23 @@ int snd_seq_port_disconnect(struct snd_seq_client *connector,
|
|||
|
||||
|
||||
/* get matched subscriber */
|
||||
struct snd_seq_subscribers *snd_seq_port_get_subscription(struct snd_seq_port_subs_info *src_grp,
|
||||
struct snd_seq_addr *dest_addr)
|
||||
int snd_seq_port_get_subscription(struct snd_seq_port_subs_info *src_grp,
|
||||
struct snd_seq_addr *dest_addr,
|
||||
struct snd_seq_port_subscribe *subs)
|
||||
{
|
||||
struct snd_seq_subscribers *s, *found = NULL;
|
||||
struct snd_seq_subscribers *s;
|
||||
int err = -ENOENT;
|
||||
|
||||
down_read(&src_grp->list_mutex);
|
||||
list_for_each_entry(s, &src_grp->list_head, src_list) {
|
||||
if (addr_match(dest_addr, &s->info.dest)) {
|
||||
found = s;
|
||||
*subs = s->info;
|
||||
err = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
up_read(&src_grp->list_mutex);
|
||||
return found;
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -135,7 +135,8 @@ int snd_seq_port_subscribe(struct snd_seq_client_port *port,
|
|||
struct snd_seq_port_subscribe *info);
|
||||
|
||||
/* get matched subscriber */
|
||||
struct snd_seq_subscribers *snd_seq_port_get_subscription(struct snd_seq_port_subs_info *src_grp,
|
||||
struct snd_seq_addr *dest_addr);
|
||||
int snd_seq_port_get_subscription(struct snd_seq_port_subs_info *src_grp,
|
||||
struct snd_seq_addr *dest_addr,
|
||||
struct snd_seq_port_subscribe *subs);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -134,8 +134,11 @@ static struct snd_minor *autoload_device(unsigned int minor)
|
|||
if (dev == SNDRV_MINOR_CONTROL) {
|
||||
/* /dev/aloadC? */
|
||||
int card = SNDRV_MINOR_CARD(minor);
|
||||
if (snd_cards[card] == NULL)
|
||||
struct snd_card *ref = snd_card_ref(card);
|
||||
if (!ref)
|
||||
snd_request_card(card);
|
||||
else
|
||||
snd_card_unref(ref);
|
||||
} else if (dev == SNDRV_MINOR_GLOBAL) {
|
||||
/* /dev/aloadSEQ */
|
||||
snd_request_other(minor);
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
|
||||
/* internal flags */
|
||||
#define SNDRV_TIMER_IFLG_PAUSED 0x00010000
|
||||
#define SNDRV_TIMER_IFLG_DEAD 0x00020000
|
||||
|
||||
#if IS_ENABLED(CONFIG_SND_HRTIMER)
|
||||
#define DEFAULT_TIMER_LIMIT 4
|
||||
|
@ -254,19 +255,20 @@ int snd_timer_open(struct snd_timer_instance **ti,
|
|||
struct snd_timer_instance *timeri = NULL;
|
||||
int err;
|
||||
|
||||
mutex_lock(®ister_mutex);
|
||||
if (tid->dev_class == SNDRV_TIMER_CLASS_SLAVE) {
|
||||
/* open a slave instance */
|
||||
if (tid->dev_sclass <= SNDRV_TIMER_SCLASS_NONE ||
|
||||
tid->dev_sclass > SNDRV_TIMER_SCLASS_OSS_SEQUENCER) {
|
||||
pr_debug("ALSA: timer: invalid slave class %i\n",
|
||||
tid->dev_sclass);
|
||||
return -EINVAL;
|
||||
err = -EINVAL;
|
||||
goto unlock;
|
||||
}
|
||||
mutex_lock(®ister_mutex);
|
||||
timeri = snd_timer_instance_new(owner, NULL);
|
||||
if (!timeri) {
|
||||
mutex_unlock(®ister_mutex);
|
||||
return -ENOMEM;
|
||||
err = -ENOMEM;
|
||||
goto unlock;
|
||||
}
|
||||
timeri->slave_class = tid->dev_sclass;
|
||||
timeri->slave_id = tid->device;
|
||||
|
@ -277,13 +279,10 @@ int snd_timer_open(struct snd_timer_instance **ti,
|
|||
snd_timer_close_locked(timeri);
|
||||
timeri = NULL;
|
||||
}
|
||||
mutex_unlock(®ister_mutex);
|
||||
*ti = timeri;
|
||||
return err;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* open a master instance */
|
||||
mutex_lock(®ister_mutex);
|
||||
timer = snd_timer_find(tid);
|
||||
#ifdef CONFIG_MODULES
|
||||
if (!timer) {
|
||||
|
@ -294,25 +293,26 @@ int snd_timer_open(struct snd_timer_instance **ti,
|
|||
}
|
||||
#endif
|
||||
if (!timer) {
|
||||
mutex_unlock(®ister_mutex);
|
||||
return -ENODEV;
|
||||
err = -ENODEV;
|
||||
goto unlock;
|
||||
}
|
||||
if (!list_empty(&timer->open_list_head)) {
|
||||
timeri = list_entry(timer->open_list_head.next,
|
||||
struct snd_timer_instance, open_list);
|
||||
if (timeri->flags & SNDRV_TIMER_IFLG_EXCLUSIVE) {
|
||||
mutex_unlock(®ister_mutex);
|
||||
return -EBUSY;
|
||||
err = -EBUSY;
|
||||
timeri = NULL;
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
if (timer->num_instances >= timer->max_instances) {
|
||||
mutex_unlock(®ister_mutex);
|
||||
return -EBUSY;
|
||||
err = -EBUSY;
|
||||
goto unlock;
|
||||
}
|
||||
timeri = snd_timer_instance_new(owner, timer);
|
||||
if (!timeri) {
|
||||
mutex_unlock(®ister_mutex);
|
||||
return -ENOMEM;
|
||||
err = -ENOMEM;
|
||||
goto unlock;
|
||||
}
|
||||
/* take a card refcount for safe disconnection */
|
||||
if (timer->card)
|
||||
|
@ -321,16 +321,16 @@ int snd_timer_open(struct snd_timer_instance **ti,
|
|||
timeri->slave_id = slave_id;
|
||||
|
||||
if (list_empty(&timer->open_list_head) && timer->hw.open) {
|
||||
int err = timer->hw.open(timer);
|
||||
err = timer->hw.open(timer);
|
||||
if (err) {
|
||||
kfree(timeri->owner);
|
||||
kfree(timeri);
|
||||
timeri = NULL;
|
||||
|
||||
if (timer->card)
|
||||
put_device(&timer->card->card_dev);
|
||||
module_put(timer->module);
|
||||
mutex_unlock(®ister_mutex);
|
||||
return err;
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -341,6 +341,8 @@ int snd_timer_open(struct snd_timer_instance **ti,
|
|||
snd_timer_close_locked(timeri);
|
||||
timeri = NULL;
|
||||
}
|
||||
|
||||
unlock:
|
||||
mutex_unlock(®ister_mutex);
|
||||
*ti = timeri;
|
||||
return err;
|
||||
|
@ -353,15 +355,20 @@ EXPORT_SYMBOL(snd_timer_open);
|
|||
*/
|
||||
static int snd_timer_close_locked(struct snd_timer_instance *timeri)
|
||||
{
|
||||
struct snd_timer *timer = NULL;
|
||||
struct snd_timer *timer = timeri->timer;
|
||||
struct snd_timer_instance *slave, *tmp;
|
||||
|
||||
if (timer) {
|
||||
spin_lock_irq(&timer->lock);
|
||||
timeri->flags |= SNDRV_TIMER_IFLG_DEAD;
|
||||
spin_unlock_irq(&timer->lock);
|
||||
}
|
||||
|
||||
list_del(&timeri->open_list);
|
||||
|
||||
/* force to stop the timer */
|
||||
snd_timer_stop(timeri);
|
||||
|
||||
timer = timeri->timer;
|
||||
if (timer) {
|
||||
timer->num_instances--;
|
||||
/* wait, until the active callback is finished */
|
||||
|
@ -497,6 +504,10 @@ static int snd_timer_start1(struct snd_timer_instance *timeri,
|
|||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&timer->lock, flags);
|
||||
if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) {
|
||||
result = -EINVAL;
|
||||
goto unlock;
|
||||
}
|
||||
if (timer->card && timer->card->shutdown) {
|
||||
result = -ENODEV;
|
||||
goto unlock;
|
||||
|
@ -541,11 +552,16 @@ static int snd_timer_start_slave(struct snd_timer_instance *timeri,
|
|||
bool start)
|
||||
{
|
||||
unsigned long flags;
|
||||
int err;
|
||||
|
||||
spin_lock_irqsave(&slave_active_lock, flags);
|
||||
if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) {
|
||||
err = -EINVAL;
|
||||
goto unlock;
|
||||
}
|
||||
if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) {
|
||||
spin_unlock_irqrestore(&slave_active_lock, flags);
|
||||
return -EBUSY;
|
||||
err = -EBUSY;
|
||||
goto unlock;
|
||||
}
|
||||
timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
|
||||
if (timeri->master && timeri->timer) {
|
||||
|
@ -556,8 +572,10 @@ static int snd_timer_start_slave(struct snd_timer_instance *timeri,
|
|||
SNDRV_TIMER_EVENT_CONTINUE);
|
||||
spin_unlock(&timeri->timer->lock);
|
||||
}
|
||||
err = 1; /* delayed start */
|
||||
unlock:
|
||||
spin_unlock_irqrestore(&slave_active_lock, flags);
|
||||
return 1; /* delayed start */
|
||||
return err;
|
||||
}
|
||||
|
||||
/* stop/pause a master timer */
|
||||
|
@ -720,6 +738,46 @@ static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_l
|
|||
timer->sticks = ticks;
|
||||
}
|
||||
|
||||
/* call callbacks in timer ack list */
|
||||
static void snd_timer_process_callbacks(struct snd_timer *timer,
|
||||
struct list_head *head)
|
||||
{
|
||||
struct snd_timer_instance *ti;
|
||||
unsigned long resolution, ticks;
|
||||
|
||||
while (!list_empty(head)) {
|
||||
ti = list_first_entry(head, struct snd_timer_instance,
|
||||
ack_list);
|
||||
|
||||
/* remove from ack_list and make empty */
|
||||
list_del_init(&ti->ack_list);
|
||||
|
||||
if (!(ti->flags & SNDRV_TIMER_IFLG_DEAD)) {
|
||||
ticks = ti->pticks;
|
||||
ti->pticks = 0;
|
||||
resolution = ti->resolution;
|
||||
ti->flags |= SNDRV_TIMER_IFLG_CALLBACK;
|
||||
spin_unlock(&timer->lock);
|
||||
if (ti->callback)
|
||||
ti->callback(ti, resolution, ticks);
|
||||
spin_lock(&timer->lock);
|
||||
ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* clear pending instances from ack list */
|
||||
static void snd_timer_clear_callbacks(struct snd_timer *timer,
|
||||
struct list_head *head)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&timer->lock, flags);
|
||||
while (!list_empty(head))
|
||||
list_del_init(head->next);
|
||||
spin_unlock_irqrestore(&timer->lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* timer tasklet
|
||||
*
|
||||
|
@ -727,34 +785,15 @@ static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_l
|
|||
static void snd_timer_tasklet(unsigned long arg)
|
||||
{
|
||||
struct snd_timer *timer = (struct snd_timer *) arg;
|
||||
struct snd_timer_instance *ti;
|
||||
struct list_head *p;
|
||||
unsigned long resolution, ticks;
|
||||
unsigned long flags;
|
||||
|
||||
if (timer->card && timer->card->shutdown)
|
||||
if (timer->card && timer->card->shutdown) {
|
||||
snd_timer_clear_callbacks(timer, &timer->sack_list_head);
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&timer->lock, flags);
|
||||
/* now process all callbacks */
|
||||
while (!list_empty(&timer->sack_list_head)) {
|
||||
p = timer->sack_list_head.next; /* get first item */
|
||||
ti = list_entry(p, struct snd_timer_instance, ack_list);
|
||||
|
||||
/* remove from ack_list and make empty */
|
||||
list_del_init(p);
|
||||
|
||||
ticks = ti->pticks;
|
||||
ti->pticks = 0;
|
||||
resolution = ti->resolution;
|
||||
|
||||
ti->flags |= SNDRV_TIMER_IFLG_CALLBACK;
|
||||
spin_unlock(&timer->lock);
|
||||
if (ti->callback)
|
||||
ti->callback(ti, resolution, ticks);
|
||||
spin_lock(&timer->lock);
|
||||
ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK;
|
||||
}
|
||||
snd_timer_process_callbacks(timer, &timer->sack_list_head);
|
||||
spin_unlock_irqrestore(&timer->lock, flags);
|
||||
}
|
||||
|
||||
|
@ -767,16 +806,18 @@ static void snd_timer_tasklet(unsigned long arg)
|
|||
void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
|
||||
{
|
||||
struct snd_timer_instance *ti, *ts, *tmp;
|
||||
unsigned long resolution, ticks;
|
||||
struct list_head *p, *ack_list_head;
|
||||
unsigned long resolution;
|
||||
struct list_head *ack_list_head;
|
||||
unsigned long flags;
|
||||
int use_tasklet = 0;
|
||||
|
||||
if (timer == NULL)
|
||||
return;
|
||||
|
||||
if (timer->card && timer->card->shutdown)
|
||||
if (timer->card && timer->card->shutdown) {
|
||||
snd_timer_clear_callbacks(timer, &timer->ack_list_head);
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&timer->lock, flags);
|
||||
|
||||
|
@ -790,6 +831,8 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
|
|||
*/
|
||||
list_for_each_entry_safe(ti, tmp, &timer->active_list_head,
|
||||
active_list) {
|
||||
if (ti->flags & SNDRV_TIMER_IFLG_DEAD)
|
||||
continue;
|
||||
if (!(ti->flags & SNDRV_TIMER_IFLG_RUNNING))
|
||||
continue;
|
||||
ti->pticks += ticks_left;
|
||||
|
@ -839,23 +882,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
|
|||
}
|
||||
|
||||
/* now process all fast callbacks */
|
||||
while (!list_empty(&timer->ack_list_head)) {
|
||||
p = timer->ack_list_head.next; /* get first item */
|
||||
ti = list_entry(p, struct snd_timer_instance, ack_list);
|
||||
|
||||
/* remove from ack_list and make empty */
|
||||
list_del_init(p);
|
||||
|
||||
ticks = ti->pticks;
|
||||
ti->pticks = 0;
|
||||
|
||||
ti->flags |= SNDRV_TIMER_IFLG_CALLBACK;
|
||||
spin_unlock(&timer->lock);
|
||||
if (ti->callback)
|
||||
ti->callback(ti, resolution, ticks);
|
||||
spin_lock(&timer->lock);
|
||||
ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK;
|
||||
}
|
||||
snd_timer_process_callbacks(timer, &timer->ack_list_head);
|
||||
|
||||
/* do we have any slow callbacks? */
|
||||
use_tasklet = !list_empty(&timer->sack_list_head);
|
||||
|
@ -1882,7 +1909,10 @@ static int snd_timer_user_start(struct file *file)
|
|||
snd_timer_stop(tu->timeri);
|
||||
tu->timeri->lost = 0;
|
||||
tu->last_resolution = 0;
|
||||
return (err = snd_timer_start(tu->timeri, tu->ticks)) < 0 ? err : 0;
|
||||
err = snd_timer_start(tu->timeri, tu->ticks);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_timer_user_stop(struct file *file)
|
||||
|
@ -1893,7 +1923,10 @@ static int snd_timer_user_stop(struct file *file)
|
|||
tu = file->private_data;
|
||||
if (!tu->timeri)
|
||||
return -EBADFD;
|
||||
return (err = snd_timer_stop(tu->timeri)) < 0 ? err : 0;
|
||||
err = snd_timer_stop(tu->timeri);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_timer_user_continue(struct file *file)
|
||||
|
@ -1908,7 +1941,10 @@ static int snd_timer_user_continue(struct file *file)
|
|||
if (!(tu->timeri->flags & SNDRV_TIMER_IFLG_PAUSED))
|
||||
return snd_timer_user_start(file);
|
||||
tu->timeri->lost = 0;
|
||||
return (err = snd_timer_continue(tu->timeri)) < 0 ? err : 0;
|
||||
err = snd_timer_continue(tu->timeri);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_timer_user_pause(struct file *file)
|
||||
|
@ -1919,7 +1955,10 @@ static int snd_timer_user_pause(struct file *file)
|
|||
tu = file->private_data;
|
||||
if (!tu->timeri)
|
||||
return -EBADFD;
|
||||
return (err = snd_timer_pause(tu->timeri)) < 0 ? err : 0;
|
||||
err = snd_timer_pause(tu->timeri);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum {
|
||||
|
|
|
@ -337,7 +337,7 @@ static int loopback_prepare(struct snd_pcm_substream *substream)
|
|||
|
||||
loopback_timer_stop_sync(dpcm);
|
||||
|
||||
salign = (snd_pcm_format_width(runtime->format) *
|
||||
salign = (snd_pcm_format_physical_width(runtime->format) *
|
||||
runtime->channels) / 8;
|
||||
bps = salign * runtime->rate;
|
||||
if (bps <= 0 || salign <= 0)
|
||||
|
@ -562,6 +562,8 @@ static const struct snd_pcm_hardware loopback_pcm_hardware =
|
|||
SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE |
|
||||
SNDRV_PCM_INFO_RESUME),
|
||||
.formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
|
||||
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |
|
||||
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |
|
||||
SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE |
|
||||
SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE),
|
||||
.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_192000,
|
||||
|
|
|
@ -56,8 +56,9 @@
|
|||
#define INTERRUPT_INTERVAL 16
|
||||
#define QUEUE_LENGTH 48
|
||||
|
||||
#define IN_PACKET_HEADER_SIZE 4
|
||||
#define IR_HEADER_SIZE 8 // For header and timestamp.
|
||||
#define OUT_PACKET_HEADER_SIZE 0
|
||||
#define HEADER_TSTAMP_MASK 0x0000ffff
|
||||
|
||||
static void pcm_period_tasklet(unsigned long data);
|
||||
|
||||
|
@ -456,7 +457,7 @@ static inline int queue_out_packet(struct amdtp_stream *s,
|
|||
|
||||
static inline int queue_in_packet(struct amdtp_stream *s)
|
||||
{
|
||||
return queue_packet(s, IN_PACKET_HEADER_SIZE, s->max_payload_length);
|
||||
return queue_packet(s, IR_HEADER_SIZE, s->max_payload_length);
|
||||
}
|
||||
|
||||
static int handle_out_packet(struct amdtp_stream *s,
|
||||
|
@ -701,13 +702,6 @@ static inline u32 increment_cycle_count(u32 cycle, unsigned int addend)
|
|||
return cycle;
|
||||
}
|
||||
|
||||
static inline u32 decrement_cycle_count(u32 cycle, unsigned int subtrahend)
|
||||
{
|
||||
if (cycle < subtrahend)
|
||||
cycle += 8 * CYCLES_PER_SECOND;
|
||||
return cycle - subtrahend;
|
||||
}
|
||||
|
||||
static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
|
||||
size_t header_length, void *header,
|
||||
void *private_data)
|
||||
|
@ -745,29 +739,26 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
|
|||
struct amdtp_stream *s = private_data;
|
||||
unsigned int i, packets;
|
||||
unsigned int payload_length, max_payload_length;
|
||||
__be32 *headers = header;
|
||||
u32 cycle;
|
||||
__be32 *ctx_header = header;
|
||||
|
||||
if (s->packet_index < 0)
|
||||
return;
|
||||
|
||||
/* The number of packets in buffer */
|
||||
packets = header_length / IN_PACKET_HEADER_SIZE;
|
||||
|
||||
cycle = compute_cycle_count(tstamp);
|
||||
|
||||
/* Align to actual cycle count for the last packet. */
|
||||
cycle = decrement_cycle_count(cycle, packets);
|
||||
packets = header_length / IR_HEADER_SIZE;
|
||||
|
||||
/* For buffer-over-run prevention. */
|
||||
max_payload_length = s->max_payload_length;
|
||||
|
||||
for (i = 0; i < packets; i++) {
|
||||
cycle = increment_cycle_count(cycle, 1);
|
||||
u32 iso_header = be32_to_cpu(ctx_header[0]);
|
||||
unsigned int cycle;
|
||||
|
||||
tstamp = be32_to_cpu(ctx_header[1]) & HEADER_TSTAMP_MASK;
|
||||
cycle = compute_cycle_count(tstamp);
|
||||
|
||||
/* The number of bytes in this packet */
|
||||
payload_length =
|
||||
(be32_to_cpu(headers[i]) >> ISO_DATA_LENGTH_SHIFT);
|
||||
payload_length = iso_header >> ISO_DATA_LENGTH_SHIFT;
|
||||
if (payload_length > max_payload_length) {
|
||||
dev_err(&s->unit->device,
|
||||
"Detect jumbo payload: %04x %04x\n",
|
||||
|
@ -777,6 +768,8 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
|
|||
|
||||
if (s->handle_packet(s, payload_length, cycle, i) < 0)
|
||||
break;
|
||||
|
||||
ctx_header += IR_HEADER_SIZE / sizeof(__be32);
|
||||
}
|
||||
|
||||
/* Queueing error or detecting invalid payload. */
|
||||
|
@ -797,6 +790,7 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
|
|||
void *header, void *private_data)
|
||||
{
|
||||
struct amdtp_stream *s = private_data;
|
||||
__be32 *ctx_header = header;
|
||||
u32 cycle;
|
||||
unsigned int packets;
|
||||
|
||||
|
@ -807,11 +801,10 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
|
|||
s->callbacked = true;
|
||||
wake_up(&s->callback_wait);
|
||||
|
||||
cycle = compute_cycle_count(tstamp);
|
||||
|
||||
if (s->direction == AMDTP_IN_STREAM) {
|
||||
packets = header_length / IN_PACKET_HEADER_SIZE;
|
||||
cycle = decrement_cycle_count(cycle, packets);
|
||||
tstamp = be32_to_cpu(ctx_header[1]) & HEADER_TSTAMP_MASK;
|
||||
cycle = compute_cycle_count(tstamp);
|
||||
|
||||
context->callback.sc = in_stream_callback;
|
||||
if (s->flags & CIP_NO_HEADER)
|
||||
s->handle_packet = handle_in_packet_without_header;
|
||||
|
@ -819,6 +812,7 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
|
|||
s->handle_packet = handle_in_packet;
|
||||
} else {
|
||||
packets = header_length / 4;
|
||||
cycle = compute_cycle_count(tstamp);
|
||||
cycle = increment_cycle_count(cycle, QUEUE_LENGTH - packets);
|
||||
context->callback.sc = out_stream_callback;
|
||||
if (s->flags & CIP_NO_HEADER)
|
||||
|
@ -880,7 +874,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
|
|||
if (s->direction == AMDTP_IN_STREAM) {
|
||||
dir = DMA_FROM_DEVICE;
|
||||
type = FW_ISO_CONTEXT_RECEIVE;
|
||||
header_size = IN_PACKET_HEADER_SIZE;
|
||||
header_size = IR_HEADER_SIZE;
|
||||
} else {
|
||||
dir = DMA_TO_DEVICE;
|
||||
type = FW_ISO_CONTEXT_TRANSMIT;
|
||||
|
|
|
@ -412,6 +412,12 @@ int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
|
|||
CIP_HEADER_WITHOUT_EOH;
|
||||
fmt = CIP_FMT_MOTU_TX_V3;
|
||||
}
|
||||
|
||||
if (protocol == &snd_motu_protocol_v2) {
|
||||
// 8pre has some quirks.
|
||||
flags |= CIP_WRONG_DBS |
|
||||
CIP_SKIP_DBC_ZERO_CHECK;
|
||||
}
|
||||
} else {
|
||||
process_data_blocks = process_rx_data_blocks;
|
||||
flags |= CIP_DBC_IS_END_EVENT;
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#define V2_CLOCK_SRC_SHIFT 0
|
||||
#define V2_CLOCK_TRAVELER_FETCH_DISABLE 0x04000000
|
||||
#define V2_CLOCK_TRAVELER_FETCH_ENABLE 0x03000000
|
||||
#define V2_CLOCK_8PRE_FETCH_DISABLE 0x02000000
|
||||
#define V2_CLOCK_8PRE_FETCH_ENABLE 0x00000000
|
||||
|
||||
#define V2_IN_OUT_CONF_OFFSET 0x0c04
|
||||
#define V2_OPT_OUT_IFACE_MASK 0x00000c00
|
||||
|
@ -132,20 +134,31 @@ static int v2_switch_fetching_mode(struct snd_motu *motu, bool enable)
|
|||
u32 data;
|
||||
int err = 0;
|
||||
|
||||
if (motu->spec == &snd_motu_spec_traveler) {
|
||||
if (motu->spec == &snd_motu_spec_traveler ||
|
||||
motu->spec == &snd_motu_spec_8pre) {
|
||||
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_TRAVELER_FETCH_DISABLE |
|
||||
V2_CLOCK_TRAVELER_FETCH_ENABLE);
|
||||
if (motu->spec == &snd_motu_spec_traveler) {
|
||||
data &= ~(V2_CLOCK_TRAVELER_FETCH_DISABLE |
|
||||
V2_CLOCK_TRAVELER_FETCH_ENABLE);
|
||||
|
||||
if (enable)
|
||||
data |= V2_CLOCK_TRAVELER_FETCH_ENABLE;
|
||||
else
|
||||
data |= V2_CLOCK_TRAVELER_FETCH_DISABLE;
|
||||
if (enable)
|
||||
data |= V2_CLOCK_TRAVELER_FETCH_ENABLE;
|
||||
else
|
||||
data |= V2_CLOCK_TRAVELER_FETCH_DISABLE;
|
||||
} else if (motu->spec == &snd_motu_spec_8pre) {
|
||||
data &= ~(V2_CLOCK_8PRE_FETCH_DISABLE |
|
||||
V2_CLOCK_8PRE_FETCH_ENABLE);
|
||||
|
||||
if (enable)
|
||||
data |= V2_CLOCK_8PRE_FETCH_DISABLE;
|
||||
else
|
||||
data |= V2_CLOCK_8PRE_FETCH_ENABLE;
|
||||
}
|
||||
|
||||
reg = cpu_to_be32(data);
|
||||
err = snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET,
|
||||
|
@ -220,10 +233,16 @@ static void calculate_differed_part(struct snd_motu_packet_format *formats,
|
|||
* interfaces.
|
||||
*/
|
||||
data = (data & mask) >> shift;
|
||||
if ((flags & SND_MOTU_SPEC_HAS_OPT_IFACE_A) &&
|
||||
data == V2_OPT_IFACE_MODE_ADAT) {
|
||||
pcm_chunks[0] += 8;
|
||||
pcm_chunks[1] += 4;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/* At mode x4, no data chunks are supported in this part. */
|
||||
|
|
|
@ -203,6 +203,20 @@ const struct snd_motu_spec snd_motu_spec_traveler = {
|
|||
.analog_out_ports = 8,
|
||||
};
|
||||
|
||||
const struct snd_motu_spec snd_motu_spec_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,
|
||||
|
@ -248,6 +262,7 @@ static const struct snd_motu_spec motu_audio_express = {
|
|||
static const struct ieee1394_device_id motu_id_table[] = {
|
||||
SND_MOTU_DEV_ENTRY(0x000003, &motu_828mk2),
|
||||
SND_MOTU_DEV_ENTRY(0x000009, &snd_motu_spec_traveler),
|
||||
SND_MOTU_DEV_ENTRY(0x00000f, &snd_motu_spec_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),
|
||||
|
|
|
@ -130,6 +130,7 @@ 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_traveler;
|
||||
extern const struct snd_motu_spec snd_motu_spec_8pre;
|
||||
|
||||
int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
enum amdtp_stream_direction dir,
|
||||
|
|
|
@ -104,9 +104,7 @@ int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev,
|
|||
return ret;
|
||||
|
||||
bus->ext_ops = ext_ops;
|
||||
INIT_LIST_HEAD(&bus->hlink_list);
|
||||
bus->idx = idx++;
|
||||
|
||||
bus->cmd_dma_state = true;
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -39,6 +39,7 @@ int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
|
|||
spin_lock_init(&bus->reg_lock);
|
||||
mutex_init(&bus->cmd_mutex);
|
||||
mutex_init(&bus->lock);
|
||||
INIT_LIST_HEAD(&bus->hlink_list);
|
||||
bus->irq = -1;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -306,7 +306,7 @@ static void snd_gf1_mem_info_read(struct snd_info_entry *entry,
|
|||
used = 0;
|
||||
for (block = alloc->first, i = 0; block; block = block->next, i++) {
|
||||
used += block->size;
|
||||
snd_iprintf(buffer, "Block %i at 0x%lx onboard 0x%x size %i (0x%x):\n", i, (long) block, block->ptr, block->size, block->size);
|
||||
snd_iprintf(buffer, "Block %i onboard 0x%x size %i (0x%x):\n", i, block->ptr, block->size, block->size);
|
||||
if (block->share ||
|
||||
block->share_id[0] || block->share_id[1] ||
|
||||
block->share_id[2] || block->share_id[3])
|
||||
|
|
10
sound/last.c
10
sound/last.c
|
@ -24,14 +24,18 @@
|
|||
|
||||
static int __init alsa_sound_last_init(void)
|
||||
{
|
||||
struct snd_card *card;
|
||||
int idx, ok = 0;
|
||||
|
||||
printk(KERN_INFO "ALSA device list:\n");
|
||||
for (idx = 0; idx < SNDRV_CARDS; idx++)
|
||||
if (snd_cards[idx] != NULL) {
|
||||
printk(KERN_INFO " #%i: %s\n", idx, snd_cards[idx]->longname);
|
||||
for (idx = 0; idx < SNDRV_CARDS; idx++) {
|
||||
card = snd_card_ref(idx);
|
||||
if (card) {
|
||||
printk(KERN_INFO " #%i: %s\n", idx, card->longname);
|
||||
snd_card_unref(card);
|
||||
ok++;
|
||||
}
|
||||
}
|
||||
if (ok == 0)
|
||||
printk(KERN_INFO " No soundcards found.\n");
|
||||
return 0;
|
||||
|
|
|
@ -1882,22 +1882,8 @@ int snd_emu10k1_create(struct snd_card *card,
|
|||
c->name, pci->vendor, pci->device,
|
||||
emu->serial);
|
||||
|
||||
if (!*card->id && c->id) {
|
||||
int i, n = 0;
|
||||
if (!*card->id && c->id)
|
||||
strlcpy(card->id, c->id, sizeof(card->id));
|
||||
for (;;) {
|
||||
for (i = 0; i < snd_ecards_limit; i++) {
|
||||
if (snd_cards[i] && !strcmp(snd_cards[i]->id, card->id))
|
||||
break;
|
||||
}
|
||||
if (i >= snd_ecards_limit)
|
||||
break;
|
||||
n++;
|
||||
if (n >= SNDRV_CARDS)
|
||||
break;
|
||||
snprintf(card->id, sizeof(card->id), "%s_%d", c->id, n);
|
||||
}
|
||||
}
|
||||
|
||||
is_audigy = emu->audigy = c->emu10k2_chip;
|
||||
|
||||
|
|
|
@ -832,7 +832,13 @@ static int snd_hda_codec_dev_free(struct snd_device *device)
|
|||
struct hda_codec *codec = device->device_data;
|
||||
|
||||
codec->in_freeing = 1;
|
||||
snd_hdac_device_unregister(&codec->core);
|
||||
/*
|
||||
* snd_hda_codec_device_new() is used by legacy HDA and ASoC driver.
|
||||
* We can't unregister ASoC device since it will be unregistered in
|
||||
* snd_hdac_ext_bus_device_remove().
|
||||
*/
|
||||
if (codec->core.type == HDA_DEV_LEGACY)
|
||||
snd_hdac_device_unregister(&codec->core);
|
||||
codec_display_power(codec, false);
|
||||
put_device(hda_codec_dev(codec));
|
||||
return 0;
|
||||
|
|
|
@ -1788,9 +1788,6 @@ static int azx_first_init(struct azx *chip)
|
|||
chip->msi = 0;
|
||||
}
|
||||
|
||||
if (azx_acquire_irq(chip, 0) < 0)
|
||||
return -EBUSY;
|
||||
|
||||
pci_set_master(pci);
|
||||
synchronize_irq(bus->irq);
|
||||
|
||||
|
@ -1904,6 +1901,9 @@ static int azx_first_init(struct azx *chip)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (azx_acquire_irq(chip, 0) < 0)
|
||||
return -EBUSY;
|
||||
|
||||
strcpy(card->driver, "HDA-Intel");
|
||||
strlcpy(card->shortname, driver_short_names[chip->driver_type],
|
||||
sizeof(card->shortname));
|
||||
|
@ -2378,6 +2378,12 @@ static const struct pci_device_id azx_ids[] = {
|
|||
/* Cannonlake */
|
||||
{ PCI_DEVICE(0x8086, 0x9dc8),
|
||||
.driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
|
||||
/* CometLake-LP */
|
||||
{ PCI_DEVICE(0x8086, 0x02C8),
|
||||
.driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
|
||||
/* CometLake-H */
|
||||
{ PCI_DEVICE(0x8086, 0x06C8),
|
||||
.driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
|
||||
/* Icelake */
|
||||
{ PCI_DEVICE(0x8086, 0x34c8),
|
||||
.driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
|
||||
|
|
|
@ -1551,9 +1551,11 @@ static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
|
|||
ret = !repoll || !eld->monitor_present || eld->eld_valid;
|
||||
|
||||
jack = snd_hda_jack_tbl_get(codec, pin_nid);
|
||||
if (jack)
|
||||
if (jack) {
|
||||
jack->block_report = !ret;
|
||||
|
||||
jack->pin_sense = (eld->monitor_present && eld->eld_valid) ?
|
||||
AC_PINSENSE_PRESENCE : 0;
|
||||
}
|
||||
mutex_unlock(&per_pin->lock);
|
||||
return ret;
|
||||
}
|
||||
|
@ -1663,6 +1665,11 @@ static void hdmi_repoll_eld(struct work_struct *work)
|
|||
container_of(to_delayed_work(work), struct hdmi_spec_per_pin, work);
|
||||
struct hda_codec *codec = per_pin->codec;
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
struct hda_jack_tbl *jack;
|
||||
|
||||
jack = snd_hda_jack_tbl_get(codec, per_pin->pin_nid);
|
||||
if (jack)
|
||||
jack->jack_dirty = 1;
|
||||
|
||||
if (per_pin->repoll_count++ > 6)
|
||||
per_pin->repoll_count = 0;
|
||||
|
|
|
@ -119,6 +119,7 @@ struct alc_spec {
|
|||
unsigned int no_depop_delay:1;
|
||||
unsigned int done_hp_init:1;
|
||||
unsigned int no_shutup_pins:1;
|
||||
unsigned int ultra_low_power:1;
|
||||
|
||||
/* for PLL fix */
|
||||
hda_nid_t pll_nid;
|
||||
|
@ -803,11 +804,10 @@ static int alc_init(struct hda_codec *codec)
|
|||
if (spec->init_hook)
|
||||
spec->init_hook(codec);
|
||||
|
||||
snd_hda_gen_init(codec);
|
||||
alc_fix_pll(codec);
|
||||
alc_auto_init_amp(codec, spec->init_amp);
|
||||
|
||||
snd_hda_gen_init(codec);
|
||||
|
||||
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT);
|
||||
|
||||
return 0;
|
||||
|
@ -3197,7 +3197,7 @@ static void alc256_init(struct hda_codec *codec)
|
|||
bool hp_pin_sense;
|
||||
|
||||
if (!hp_pin)
|
||||
return;
|
||||
hp_pin = 0x21;
|
||||
|
||||
msleep(30);
|
||||
|
||||
|
@ -3207,17 +3207,25 @@ static void alc256_init(struct hda_codec *codec)
|
|||
msleep(2);
|
||||
|
||||
alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */
|
||||
if (spec->ultra_low_power) {
|
||||
alc_update_coef_idx(codec, 0x03, 1<<1, 1<<1);
|
||||
alc_update_coef_idx(codec, 0x08, 3<<2, 3<<2);
|
||||
alc_update_coef_idx(codec, 0x08, 7<<4, 0);
|
||||
alc_update_coef_idx(codec, 0x3b, 1<<15, 0);
|
||||
alc_update_coef_idx(codec, 0x0e, 7<<6, 7<<6);
|
||||
msleep(30);
|
||||
}
|
||||
|
||||
snd_hda_codec_write(codec, hp_pin, 0,
|
||||
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
|
||||
|
||||
if (hp_pin_sense)
|
||||
if (hp_pin_sense || spec->ultra_low_power)
|
||||
msleep(85);
|
||||
|
||||
snd_hda_codec_write(codec, hp_pin, 0,
|
||||
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
|
||||
|
||||
if (hp_pin_sense)
|
||||
if (hp_pin_sense || spec->ultra_low_power)
|
||||
msleep(100);
|
||||
|
||||
alc_update_coef_idx(codec, 0x46, 3 << 12, 0);
|
||||
|
@ -3232,10 +3240,8 @@ static void alc256_shutup(struct hda_codec *codec)
|
|||
hda_nid_t hp_pin = alc_get_hp_pin(spec);
|
||||
bool hp_pin_sense;
|
||||
|
||||
if (!hp_pin) {
|
||||
alc269_shutup(codec);
|
||||
return;
|
||||
}
|
||||
if (!hp_pin)
|
||||
hp_pin = 0x21;
|
||||
|
||||
hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
|
||||
|
||||
|
@ -3245,7 +3251,7 @@ static void alc256_shutup(struct hda_codec *codec)
|
|||
snd_hda_codec_write(codec, hp_pin, 0,
|
||||
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
|
||||
|
||||
if (hp_pin_sense)
|
||||
if (hp_pin_sense || spec->ultra_low_power)
|
||||
msleep(85);
|
||||
|
||||
/* 3k pull low control for Headset jack. */
|
||||
|
@ -3256,11 +3262,20 @@ static void alc256_shutup(struct hda_codec *codec)
|
|||
snd_hda_codec_write(codec, hp_pin, 0,
|
||||
AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
|
||||
|
||||
if (hp_pin_sense)
|
||||
if (hp_pin_sense || spec->ultra_low_power)
|
||||
msleep(100);
|
||||
|
||||
alc_auto_setup_eapd(codec, false);
|
||||
alc_shutup_pins(codec);
|
||||
if (spec->ultra_low_power) {
|
||||
msleep(50);
|
||||
alc_update_coef_idx(codec, 0x03, 1<<1, 0);
|
||||
alc_update_coef_idx(codec, 0x08, 7<<4, 7<<4);
|
||||
alc_update_coef_idx(codec, 0x08, 3<<2, 0);
|
||||
alc_update_coef_idx(codec, 0x3b, 1<<15, 1<<15);
|
||||
alc_update_coef_idx(codec, 0x0e, 7<<6, 0);
|
||||
msleep(30);
|
||||
}
|
||||
}
|
||||
|
||||
static void alc225_init(struct hda_codec *codec)
|
||||
|
@ -3270,8 +3285,7 @@ static void alc225_init(struct hda_codec *codec)
|
|||
bool hp1_pin_sense, hp2_pin_sense;
|
||||
|
||||
if (!hp_pin)
|
||||
return;
|
||||
|
||||
hp_pin = 0x21;
|
||||
msleep(30);
|
||||
|
||||
hp1_pin_sense = snd_hda_jack_detect(codec, hp_pin);
|
||||
|
@ -3281,25 +3295,31 @@ static void alc225_init(struct hda_codec *codec)
|
|||
msleep(2);
|
||||
|
||||
alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */
|
||||
if (spec->ultra_low_power) {
|
||||
alc_update_coef_idx(codec, 0x08, 0x0f << 2, 3<<2);
|
||||
alc_update_coef_idx(codec, 0x0e, 7<<6, 7<<6);
|
||||
alc_update_coef_idx(codec, 0x33, 1<<11, 0);
|
||||
msleep(30);
|
||||
}
|
||||
|
||||
if (hp1_pin_sense)
|
||||
if (hp1_pin_sense || spec->ultra_low_power)
|
||||
snd_hda_codec_write(codec, hp_pin, 0,
|
||||
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
|
||||
if (hp2_pin_sense)
|
||||
snd_hda_codec_write(codec, 0x16, 0,
|
||||
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
|
||||
|
||||
if (hp1_pin_sense || hp2_pin_sense)
|
||||
if (hp1_pin_sense || hp2_pin_sense || spec->ultra_low_power)
|
||||
msleep(85);
|
||||
|
||||
if (hp1_pin_sense)
|
||||
if (hp1_pin_sense || spec->ultra_low_power)
|
||||
snd_hda_codec_write(codec, hp_pin, 0,
|
||||
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
|
||||
if (hp2_pin_sense)
|
||||
snd_hda_codec_write(codec, 0x16, 0,
|
||||
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
|
||||
|
||||
if (hp1_pin_sense || hp2_pin_sense)
|
||||
if (hp1_pin_sense || hp2_pin_sense || spec->ultra_low_power)
|
||||
msleep(100);
|
||||
|
||||
alc_update_coef_idx(codec, 0x4a, 3 << 10, 0);
|
||||
|
@ -3312,11 +3332,8 @@ static void alc225_shutup(struct hda_codec *codec)
|
|||
hda_nid_t hp_pin = alc_get_hp_pin(spec);
|
||||
bool hp1_pin_sense, hp2_pin_sense;
|
||||
|
||||
if (!hp_pin) {
|
||||
alc269_shutup(codec);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hp_pin)
|
||||
hp_pin = 0x21;
|
||||
/* 3k pull low control for Headset jack. */
|
||||
alc_update_coef_idx(codec, 0x4a, 0, 3 << 10);
|
||||
|
||||
|
@ -3326,28 +3343,36 @@ static void alc225_shutup(struct hda_codec *codec)
|
|||
if (hp1_pin_sense || hp2_pin_sense)
|
||||
msleep(2);
|
||||
|
||||
if (hp1_pin_sense)
|
||||
if (hp1_pin_sense || spec->ultra_low_power)
|
||||
snd_hda_codec_write(codec, hp_pin, 0,
|
||||
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
|
||||
if (hp2_pin_sense)
|
||||
snd_hda_codec_write(codec, 0x16, 0,
|
||||
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
|
||||
|
||||
if (hp1_pin_sense || hp2_pin_sense)
|
||||
if (hp1_pin_sense || hp2_pin_sense || spec->ultra_low_power)
|
||||
msleep(85);
|
||||
|
||||
if (hp1_pin_sense)
|
||||
if (hp1_pin_sense || spec->ultra_low_power)
|
||||
snd_hda_codec_write(codec, hp_pin, 0,
|
||||
AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
|
||||
if (hp2_pin_sense)
|
||||
snd_hda_codec_write(codec, 0x16, 0,
|
||||
AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
|
||||
|
||||
if (hp1_pin_sense || hp2_pin_sense)
|
||||
if (hp1_pin_sense || hp2_pin_sense || spec->ultra_low_power)
|
||||
msleep(100);
|
||||
|
||||
alc_auto_setup_eapd(codec, false);
|
||||
alc_shutup_pins(codec);
|
||||
if (spec->ultra_low_power) {
|
||||
msleep(50);
|
||||
alc_update_coef_idx(codec, 0x08, 0x0f << 2, 0x0c << 2);
|
||||
alc_update_coef_idx(codec, 0x0e, 7<<6, 0);
|
||||
alc_update_coef_idx(codec, 0x33, 1<<11, 1<<11);
|
||||
alc_update_coef_idx(codec, 0x4a, 3<<4, 2<<4);
|
||||
msleep(30);
|
||||
}
|
||||
}
|
||||
|
||||
static void alc_default_init(struct hda_codec *codec)
|
||||
|
@ -5527,7 +5552,12 @@ static void alc_fixup_headset_jack(struct hda_codec *codec,
|
|||
static void alc295_fixup_chromebook(struct hda_codec *codec,
|
||||
const struct hda_fixup *fix, int action)
|
||||
{
|
||||
struct alc_spec *spec = codec->spec;
|
||||
|
||||
switch (action) {
|
||||
case HDA_FIXUP_ACT_PRE_PROBE:
|
||||
spec->ultra_low_power = true;
|
||||
break;
|
||||
case HDA_FIXUP_ACT_INIT:
|
||||
switch (codec->core.vendor_id) {
|
||||
case 0x10ec0295:
|
||||
|
@ -6933,6 +6963,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
|||
SND_PCI_QUIRK(0x1462, 0xb120, "MSI Cubi MS-B120", ALC283_FIXUP_HEADSET_MIC),
|
||||
SND_PCI_QUIRK(0x1462, 0xb171, "Cubi N 8GL (MS-B171)", ALC283_FIXUP_HEADSET_MIC),
|
||||
SND_PCI_QUIRK(0x1558, 0x1325, "System76 Darter Pro (darp5)", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
|
||||
SND_PCI_QUIRK(0x1558, 0x8550, "System76 Gazelle (gaze14)", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
|
||||
SND_PCI_QUIRK(0x1558, 0x8560, "System76 Gazelle (gaze14)", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
|
||||
SND_PCI_QUIRK(0x17aa, 0x1036, "Lenovo P520", ALC233_FIXUP_LENOVO_MULTI_CODECS),
|
||||
SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE),
|
||||
SND_PCI_QUIRK(0x17aa, 0x215e, "Thinkpad L512", ALC269_FIXUP_SKU_IGNORE),
|
||||
|
|
|
@ -233,7 +233,6 @@ static int snd_ps3_program_dma(struct snd_ps3_card_info *card,
|
|||
int fill_stages, dma_ch, stage;
|
||||
enum snd_ps3_ch ch;
|
||||
uint32_t ch0_kick_event = 0; /* initialize to mute gcc */
|
||||
void *start_vaddr;
|
||||
unsigned long irqsave;
|
||||
int silent = 0;
|
||||
|
||||
|
@ -257,7 +256,6 @@ static int snd_ps3_program_dma(struct snd_ps3_card_info *card,
|
|||
fill_stages = 4;
|
||||
spin_lock_irqsave(&card->dma_lock, irqsave);
|
||||
for (ch = 0; ch < 2; ch++) {
|
||||
start_vaddr = card->dma_next_transfer_vaddr[0];
|
||||
for (stage = 0; stage < fill_stages; stage++) {
|
||||
dma_ch = stage * 2 + ch;
|
||||
if (silent)
|
||||
|
@ -526,9 +524,7 @@ static int snd_ps3_pcm_open(struct snd_pcm_substream *substream)
|
|||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream);
|
||||
int pcm_index;
|
||||
|
||||
pcm_index = substream->pcm->device;
|
||||
/* to retrieve substream/runtime in interrupt handler */
|
||||
card->substream = substream;
|
||||
|
||||
|
|
|
@ -303,7 +303,7 @@ static void aica_period_elapsed(struct timer_list *t)
|
|||
{
|
||||
struct snd_card_aica *dreamcastcard = from_timer(dreamcastcard,
|
||||
t, timer);
|
||||
struct snd_pcm_substream *substream = dreamcastcard->timer_substream;
|
||||
struct snd_pcm_substream *substream = dreamcastcard->substream;
|
||||
/*timer function - so cannot sleep */
|
||||
int play_period;
|
||||
struct snd_pcm_runtime *runtime;
|
||||
|
@ -335,13 +335,6 @@ static void spu_begin_dma(struct snd_pcm_substream *substream)
|
|||
dreamcastcard = substream->pcm->private_data;
|
||||
/*get the queue to do the work */
|
||||
schedule_work(&(dreamcastcard->spu_dma_work));
|
||||
/* Timer may already be running */
|
||||
if (unlikely(dreamcastcard->timer_substream)) {
|
||||
mod_timer(&dreamcastcard->timer, jiffies + 4);
|
||||
return;
|
||||
}
|
||||
timer_setup(&dreamcastcard->timer, aica_period_elapsed, 0);
|
||||
dreamcastcard->timer_substream = substream;
|
||||
mod_timer(&dreamcastcard->timer, jiffies + 4);
|
||||
}
|
||||
|
||||
|
@ -379,8 +372,8 @@ static int snd_aicapcm_pcm_close(struct snd_pcm_substream
|
|||
{
|
||||
struct snd_card_aica *dreamcastcard = substream->pcm->private_data;
|
||||
flush_work(&(dreamcastcard->spu_dma_work));
|
||||
if (dreamcastcard->timer_substream)
|
||||
del_timer(&dreamcastcard->timer);
|
||||
del_timer(&dreamcastcard->timer);
|
||||
dreamcastcard->substream = NULL;
|
||||
kfree(dreamcastcard->channel);
|
||||
spu_disable();
|
||||
return 0;
|
||||
|
@ -613,6 +606,7 @@ static int snd_aica_probe(struct platform_device *devptr)
|
|||
"Yamaha AICA Super Intelligent Sound Processor for SEGA Dreamcast");
|
||||
/* Prepare to use the queue */
|
||||
INIT_WORK(&(dreamcastcard->spu_dma_work), run_spu_dma);
|
||||
timer_setup(&dreamcastcard->timer, aica_period_elapsed, 0);
|
||||
/* Load the PCM 'chip' */
|
||||
err = snd_aicapcmchip(dreamcastcard, 0);
|
||||
if (unlikely(err < 0))
|
||||
|
|
|
@ -63,6 +63,7 @@ source "sound/soc/rockchip/Kconfig"
|
|||
source "sound/soc/samsung/Kconfig"
|
||||
source "sound/soc/sh/Kconfig"
|
||||
source "sound/soc/sirf/Kconfig"
|
||||
source "sound/soc/sof/Kconfig"
|
||||
source "sound/soc/spear/Kconfig"
|
||||
source "sound/soc/sprd/Kconfig"
|
||||
source "sound/soc/sti/Kconfig"
|
||||
|
|
|
@ -47,6 +47,7 @@ obj-$(CONFIG_SND_SOC) += rockchip/
|
|||
obj-$(CONFIG_SND_SOC) += samsung/
|
||||
obj-$(CONFIG_SND_SOC) += sh/
|
||||
obj-$(CONFIG_SND_SOC) += sirf/
|
||||
obj-$(CONFIG_SND_SOC) += sof/
|
||||
obj-$(CONFIG_SND_SOC) += spear/
|
||||
obj-$(CONFIG_SND_SOC) += sprd/
|
||||
obj-$(CONFIG_SND_SOC) += sti/
|
||||
|
|
|
@ -43,6 +43,9 @@ struct axi_i2s {
|
|||
struct clk *clk;
|
||||
struct clk *clk_ref;
|
||||
|
||||
bool has_capture;
|
||||
bool has_playback;
|
||||
|
||||
struct snd_soc_dai_driver dai_driver;
|
||||
|
||||
struct snd_dmaengine_dai_dma_data capture_dma_data;
|
||||
|
@ -136,8 +139,10 @@ static int axi_i2s_dai_probe(struct snd_soc_dai *dai)
|
|||
{
|
||||
struct axi_i2s *i2s = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data,
|
||||
&i2s->capture_dma_data);
|
||||
snd_soc_dai_init_dma_data(
|
||||
dai,
|
||||
i2s->has_playback ? &i2s->playback_dma_data : NULL,
|
||||
i2s->has_capture ? &i2s->capture_dma_data : NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -151,18 +156,6 @@ static const struct snd_soc_dai_ops axi_i2s_dai_ops = {
|
|||
|
||||
static struct snd_soc_dai_driver axi_i2s_dai = {
|
||||
.probe = axi_i2s_dai_probe,
|
||||
.playback = {
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_KNOT,
|
||||
.formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE,
|
||||
},
|
||||
.capture = {
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_KNOT,
|
||||
.formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE,
|
||||
},
|
||||
.ops = &axi_i2s_dai_ops,
|
||||
.symmetric_rates = 1,
|
||||
};
|
||||
|
@ -178,6 +171,19 @@ static const struct regmap_config axi_i2s_regmap_config = {
|
|||
.max_register = AXI_I2S_REG_STATUS,
|
||||
};
|
||||
|
||||
static void axi_i2s_parse_of(struct axi_i2s *i2s, const struct device_node *np)
|
||||
{
|
||||
struct property *dma_names;
|
||||
const char *dma_name;
|
||||
|
||||
of_property_for_each_string(np, "dma-names", dma_names, dma_name) {
|
||||
if (strcmp(dma_name, "rx") == 0)
|
||||
i2s->has_capture = true;
|
||||
if (strcmp(dma_name, "tx") == 0)
|
||||
i2s->has_playback = true;
|
||||
}
|
||||
}
|
||||
|
||||
static int axi_i2s_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
|
@ -191,6 +197,8 @@ static int axi_i2s_probe(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, i2s);
|
||||
|
||||
axi_i2s_parse_of(i2s, pdev->dev.of_node);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(base))
|
||||
|
@ -213,13 +221,29 @@ static int axi_i2s_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
i2s->playback_dma_data.addr = res->start + AXI_I2S_REG_TX_FIFO;
|
||||
i2s->playback_dma_data.addr_width = 4;
|
||||
i2s->playback_dma_data.maxburst = 1;
|
||||
if (i2s->has_playback) {
|
||||
axi_i2s_dai.playback.channels_min = 2;
|
||||
axi_i2s_dai.playback.channels_max = 2;
|
||||
axi_i2s_dai.playback.rates = SNDRV_PCM_RATE_KNOT;
|
||||
axi_i2s_dai.playback.formats =
|
||||
SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE;
|
||||
|
||||
i2s->capture_dma_data.addr = res->start + AXI_I2S_REG_RX_FIFO;
|
||||
i2s->capture_dma_data.addr_width = 4;
|
||||
i2s->capture_dma_data.maxburst = 1;
|
||||
i2s->playback_dma_data.addr = res->start + AXI_I2S_REG_TX_FIFO;
|
||||
i2s->playback_dma_data.addr_width = 4;
|
||||
i2s->playback_dma_data.maxburst = 1;
|
||||
}
|
||||
|
||||
if (i2s->has_capture) {
|
||||
axi_i2s_dai.capture.channels_min = 2;
|
||||
axi_i2s_dai.capture.channels_max = 2;
|
||||
axi_i2s_dai.capture.rates = SNDRV_PCM_RATE_KNOT;
|
||||
axi_i2s_dai.capture.formats =
|
||||
SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE;
|
||||
|
||||
i2s->capture_dma_data.addr = res->start + AXI_I2S_REG_RX_FIFO;
|
||||
i2s->capture_dma_data.addr_width = 4;
|
||||
i2s->capture_dma_data.maxburst = 1;
|
||||
}
|
||||
|
||||
i2s->ratnum.num = clk_get_rate(i2s->clk_ref) / 2 / AXI_I2S_BITS_PER_FRAME;
|
||||
i2s->ratnum.den_step = 1;
|
||||
|
@ -240,6 +264,10 @@ static int axi_i2s_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
goto err_clk_disable;
|
||||
|
||||
dev_info(&pdev->dev, "probed, capture %s, playback %s\n",
|
||||
i2s->has_capture ? "enabled" : "disabled",
|
||||
i2s->has_playback ? "enabled" : "disabled");
|
||||
|
||||
return 0;
|
||||
|
||||
err_clk_disable:
|
||||
|
|
|
@ -46,8 +46,9 @@
|
|||
#define DUAL_CHANNEL 2
|
||||
|
||||
static struct snd_soc_jack cz_jack;
|
||||
static struct clk *da7219_dai_clk;
|
||||
extern int bt_uart_enable;
|
||||
static struct clk *da7219_dai_wclk;
|
||||
static struct clk *da7219_dai_bclk;
|
||||
extern bool bt_uart_enable;
|
||||
|
||||
static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
|
@ -72,7 +73,8 @@ static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd)
|
|||
return ret;
|
||||
}
|
||||
|
||||
da7219_dai_clk = clk_get(component->dev, "da7219-dai-clks");
|
||||
da7219_dai_wclk = clk_get(component->dev, "da7219-dai-wclk");
|
||||
da7219_dai_bclk = clk_get(component->dev, "da7219-dai-bclk");
|
||||
|
||||
ret = snd_soc_card_jack_new(card, "Headset Jack",
|
||||
SND_JACK_HEADSET | SND_JACK_LINEOUT |
|
||||
|
@ -94,12 +96,15 @@ static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int da7219_clk_enable(struct snd_pcm_substream *substream)
|
||||
static int da7219_clk_enable(struct snd_pcm_substream *substream,
|
||||
int wclk_rate, int bclk_rate)
|
||||
{
|
||||
int ret = 0;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
|
||||
ret = clk_prepare_enable(da7219_dai_clk);
|
||||
clk_set_rate(da7219_dai_wclk, wclk_rate);
|
||||
clk_set_rate(da7219_dai_bclk, bclk_rate);
|
||||
ret = clk_prepare_enable(da7219_dai_bclk);
|
||||
if (ret < 0) {
|
||||
dev_err(rtd->dev, "can't enable master clock %d\n", ret);
|
||||
return ret;
|
||||
|
@ -110,7 +115,7 @@ static int da7219_clk_enable(struct snd_pcm_substream *substream)
|
|||
|
||||
static void da7219_clk_disable(void)
|
||||
{
|
||||
clk_disable_unprepare(da7219_dai_clk);
|
||||
clk_disable_unprepare(da7219_dai_bclk);
|
||||
}
|
||||
|
||||
static const unsigned int channels[] = {
|
||||
|
@ -151,7 +156,7 @@ static int cz_da7219_play_startup(struct snd_pcm_substream *substream)
|
|||
&constraints_rates);
|
||||
|
||||
machine->play_i2s_instance = I2S_SP_INSTANCE;
|
||||
return da7219_clk_enable(substream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cz_da7219_cap_startup(struct snd_pcm_substream *substream)
|
||||
|
@ -173,12 +178,7 @@ static int cz_da7219_cap_startup(struct snd_pcm_substream *substream)
|
|||
|
||||
machine->cap_i2s_instance = I2S_SP_INSTANCE;
|
||||
machine->capture_channel = CAP_CHANNEL1;
|
||||
return da7219_clk_enable(substream);
|
||||
}
|
||||
|
||||
static void cz_da7219_shutdown(struct snd_pcm_substream *substream)
|
||||
{
|
||||
da7219_clk_disable();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cz_max_startup(struct snd_pcm_substream *substream)
|
||||
|
@ -199,12 +199,7 @@ static int cz_max_startup(struct snd_pcm_substream *substream)
|
|||
&constraints_rates);
|
||||
|
||||
machine->play_i2s_instance = I2S_BT_INSTANCE;
|
||||
return da7219_clk_enable(substream);
|
||||
}
|
||||
|
||||
static void cz_max_shutdown(struct snd_pcm_substream *substream)
|
||||
{
|
||||
da7219_clk_disable();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cz_dmic0_startup(struct snd_pcm_substream *substream)
|
||||
|
@ -225,7 +220,7 @@ static int cz_dmic0_startup(struct snd_pcm_substream *substream)
|
|||
&constraints_rates);
|
||||
|
||||
machine->cap_i2s_instance = I2S_BT_INSTANCE;
|
||||
return da7219_clk_enable(substream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cz_dmic1_startup(struct snd_pcm_substream *substream)
|
||||
|
@ -247,10 +242,28 @@ static int cz_dmic1_startup(struct snd_pcm_substream *substream)
|
|||
|
||||
machine->cap_i2s_instance = I2S_SP_INSTANCE;
|
||||
machine->capture_channel = CAP_CHANNEL0;
|
||||
return da7219_clk_enable(substream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cz_dmic_shutdown(struct snd_pcm_substream *substream)
|
||||
static int cz_da7219_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
int wclk, bclk;
|
||||
|
||||
wclk = params_rate(params);
|
||||
bclk = wclk * params_channels(params) *
|
||||
snd_pcm_format_width(params_format(params));
|
||||
/* ADAU7002 spec: "The ADAU7002 requires a BCLK rate
|
||||
* that is minimum of 64x the LRCLK sample rate."
|
||||
* DA7219 is the only clk source so for all codecs
|
||||
* we have to limit bclk to 64X lrclk.
|
||||
*/
|
||||
if (bclk < (wclk * 64))
|
||||
bclk = wclk * 64;
|
||||
return da7219_clk_enable(substream, wclk, bclk);
|
||||
}
|
||||
|
||||
static void cz_da7219_shutdown(struct snd_pcm_substream *substream)
|
||||
{
|
||||
da7219_clk_disable();
|
||||
}
|
||||
|
@ -258,26 +271,31 @@ static void cz_dmic_shutdown(struct snd_pcm_substream *substream)
|
|||
static const struct snd_soc_ops cz_da7219_play_ops = {
|
||||
.startup = cz_da7219_play_startup,
|
||||
.shutdown = cz_da7219_shutdown,
|
||||
.hw_params = cz_da7219_params,
|
||||
};
|
||||
|
||||
static const struct snd_soc_ops cz_da7219_cap_ops = {
|
||||
.startup = cz_da7219_cap_startup,
|
||||
.shutdown = cz_da7219_shutdown,
|
||||
.hw_params = cz_da7219_params,
|
||||
};
|
||||
|
||||
static const struct snd_soc_ops cz_max_play_ops = {
|
||||
.startup = cz_max_startup,
|
||||
.shutdown = cz_max_shutdown,
|
||||
.shutdown = cz_da7219_shutdown,
|
||||
.hw_params = cz_da7219_params,
|
||||
};
|
||||
|
||||
static const struct snd_soc_ops cz_dmic0_cap_ops = {
|
||||
.startup = cz_dmic0_startup,
|
||||
.shutdown = cz_dmic_shutdown,
|
||||
.shutdown = cz_da7219_shutdown,
|
||||
.hw_params = cz_da7219_params,
|
||||
};
|
||||
|
||||
static const struct snd_soc_ops cz_dmic1_cap_ops = {
|
||||
.startup = cz_dmic1_startup,
|
||||
.shutdown = cz_dmic_shutdown,
|
||||
.shutdown = cz_da7219_shutdown,
|
||||
.hw_params = cz_da7219_params,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_link cz_dai_7219_98357[] = {
|
||||
|
|
|
@ -558,7 +558,7 @@ static int acp3x_dai_i2s_trigger(struct snd_pcm_substream *substream,
|
|||
return ret;
|
||||
}
|
||||
|
||||
struct snd_soc_dai_ops acp3x_dai_i2s_ops = {
|
||||
static struct snd_soc_dai_ops acp3x_dai_i2s_ops = {
|
||||
.hw_params = acp3x_dai_i2s_hwparams,
|
||||
.trigger = acp3x_dai_i2s_trigger,
|
||||
.set_fmt = acp3x_dai_i2s_set_fmt,
|
||||
|
|
|
@ -109,4 +109,18 @@ config SND_SOC_MIKROE_PROTO
|
|||
using I2C over SDA (MPU Data Input) and SCL (MPU Clock Input) pins.
|
||||
Both playback and capture are supported.
|
||||
|
||||
config SND_MCHP_SOC_I2S_MCC
|
||||
tristate "Microchip ASoC driver for boards using I2S MCC"
|
||||
depends on OF && (ARCH_AT91 || COMPILE_TEST)
|
||||
select SND_SOC_GENERIC_DMAENGINE_PCM
|
||||
select REGMAP_MMIO
|
||||
help
|
||||
Say Y or M if you want to add support for I2S Multi-Channel ASoC
|
||||
driver on the following Microchip platforms:
|
||||
- sam9x60
|
||||
|
||||
The I2SMCC complies with the Inter-IC Sound (I2S) bus specification
|
||||
and supports a Time Division Multiplexed (TDM) interface with
|
||||
external multi-channel audio codecs.
|
||||
|
||||
endif
|
||||
|
|
|
@ -4,11 +4,13 @@ snd-soc-atmel-pcm-pdc-objs := atmel-pcm-pdc.o
|
|||
snd-soc-atmel-pcm-dma-objs := atmel-pcm-dma.o
|
||||
snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o
|
||||
snd-soc-atmel-i2s-objs := atmel-i2s.o
|
||||
snd-soc-mchp-i2s-mcc-objs := mchp-i2s-mcc.o
|
||||
|
||||
obj-$(CONFIG_SND_ATMEL_SOC_PDC) += snd-soc-atmel-pcm-pdc.o
|
||||
obj-$(CONFIG_SND_ATMEL_SOC_DMA) += snd-soc-atmel-pcm-dma.o
|
||||
obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o
|
||||
obj-$(CONFIG_SND_ATMEL_SOC_I2S) += snd-soc-atmel-i2s.o
|
||||
obj-$(CONFIG_SND_MCHP_SOC_I2S_MCC) += snd-soc-mchp-i2s-mcc.o
|
||||
|
||||
# AT91 Machine Support
|
||||
snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o
|
||||
|
|
|
@ -0,0 +1,974 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Driver for Microchip I2S Multi-channel controller
|
||||
//
|
||||
// Copyright (C) 2018 Microchip Technology Inc. and its subsidiaries
|
||||
//
|
||||
// Author: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/lcm.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
|
||||
/*
|
||||
* ---- I2S Controller Register map ----
|
||||
*/
|
||||
#define MCHP_I2SMCC_CR 0x0000 /* Control Register */
|
||||
#define MCHP_I2SMCC_MRA 0x0004 /* Mode Register A */
|
||||
#define MCHP_I2SMCC_MRB 0x0008 /* Mode Register B */
|
||||
#define MCHP_I2SMCC_SR 0x000C /* Status Register */
|
||||
#define MCHP_I2SMCC_IERA 0x0010 /* Interrupt Enable Register A */
|
||||
#define MCHP_I2SMCC_IDRA 0x0014 /* Interrupt Disable Register A */
|
||||
#define MCHP_I2SMCC_IMRA 0x0018 /* Interrupt Mask Register A */
|
||||
#define MCHP_I2SMCC_ISRA 0X001C /* Interrupt Status Register A */
|
||||
|
||||
#define MCHP_I2SMCC_IERB 0x0020 /* Interrupt Enable Register B */
|
||||
#define MCHP_I2SMCC_IDRB 0x0024 /* Interrupt Disable Register B */
|
||||
#define MCHP_I2SMCC_IMRB 0x0028 /* Interrupt Mask Register B */
|
||||
#define MCHP_I2SMCC_ISRB 0X002C /* Interrupt Status Register B */
|
||||
|
||||
#define MCHP_I2SMCC_RHR 0x0030 /* Receiver Holding Register */
|
||||
#define MCHP_I2SMCC_THR 0x0034 /* Transmitter Holding Register */
|
||||
|
||||
#define MCHP_I2SMCC_RHL0R 0x0040 /* Receiver Holding Left 0 Register */
|
||||
#define MCHP_I2SMCC_RHR0R 0x0044 /* Receiver Holding Right 0 Register */
|
||||
|
||||
#define MCHP_I2SMCC_RHL1R 0x0048 /* Receiver Holding Left 1 Register */
|
||||
#define MCHP_I2SMCC_RHR1R 0x004C /* Receiver Holding Right 1 Register */
|
||||
|
||||
#define MCHP_I2SMCC_RHL2R 0x0050 /* Receiver Holding Left 2 Register */
|
||||
#define MCHP_I2SMCC_RHR2R 0x0054 /* Receiver Holding Right 2 Register */
|
||||
|
||||
#define MCHP_I2SMCC_RHL3R 0x0058 /* Receiver Holding Left 3 Register */
|
||||
#define MCHP_I2SMCC_RHR3R 0x005C /* Receiver Holding Right 3 Register */
|
||||
|
||||
#define MCHP_I2SMCC_THL0R 0x0060 /* Transmitter Holding Left 0 Register */
|
||||
#define MCHP_I2SMCC_THR0R 0x0064 /* Transmitter Holding Right 0 Register */
|
||||
|
||||
#define MCHP_I2SMCC_THL1R 0x0068 /* Transmitter Holding Left 1 Register */
|
||||
#define MCHP_I2SMCC_THR1R 0x006C /* Transmitter Holding Right 1 Register */
|
||||
|
||||
#define MCHP_I2SMCC_THL2R 0x0070 /* Transmitter Holding Left 2 Register */
|
||||
#define MCHP_I2SMCC_THR2R 0x0074 /* Transmitter Holding Right 2 Register */
|
||||
|
||||
#define MCHP_I2SMCC_THL3R 0x0078 /* Transmitter Holding Left 3 Register */
|
||||
#define MCHP_I2SMCC_THR3R 0x007C /* Transmitter Holding Right 3 Register */
|
||||
|
||||
#define MCHP_I2SMCC_VERSION 0x00FC /* Version Register */
|
||||
|
||||
/*
|
||||
* ---- Control Register (Write-only) ----
|
||||
*/
|
||||
#define MCHP_I2SMCC_CR_RXEN BIT(0) /* Receiver Enable */
|
||||
#define MCHP_I2SMCC_CR_RXDIS BIT(1) /* Receiver Disable */
|
||||
#define MCHP_I2SMCC_CR_CKEN BIT(2) /* Clock Enable */
|
||||
#define MCHP_I2SMCC_CR_CKDIS BIT(3) /* Clock Disable */
|
||||
#define MCHP_I2SMCC_CR_TXEN BIT(4) /* Transmitter Enable */
|
||||
#define MCHP_I2SMCC_CR_TXDIS BIT(5) /* Transmitter Disable */
|
||||
#define MCHP_I2SMCC_CR_SWRST BIT(7) /* Software Reset */
|
||||
|
||||
/*
|
||||
* ---- Mode Register A (Read/Write) ----
|
||||
*/
|
||||
#define MCHP_I2SMCC_MRA_MODE_MASK GENMASK(0, 0)
|
||||
#define MCHP_I2SMCC_MRA_MODE_SLAVE (0 << 0)
|
||||
#define MCHP_I2SMCC_MRA_MODE_MASTER (1 << 0)
|
||||
|
||||
#define MCHP_I2SMCC_MRA_DATALENGTH_MASK GENMASK(3, 1)
|
||||
#define MCHP_I2SMCC_MRA_DATALENGTH_32_BITS (0 << 1)
|
||||
#define MCHP_I2SMCC_MRA_DATALENGTH_24_BITS (1 << 1)
|
||||
#define MCHP_I2SMCC_MRA_DATALENGTH_20_BITS (2 << 1)
|
||||
#define MCHP_I2SMCC_MRA_DATALENGTH_18_BITS (3 << 1)
|
||||
#define MCHP_I2SMCC_MRA_DATALENGTH_16_BITS (4 << 1)
|
||||
#define MCHP_I2SMCC_MRA_DATALENGTH_16_BITS_COMPACT (5 << 1)
|
||||
#define MCHP_I2SMCC_MRA_DATALENGTH_8_BITS (6 << 1)
|
||||
#define MCHP_I2SMCC_MRA_DATALENGTH_8_BITS_COMPACT (7 << 1)
|
||||
|
||||
#define MCHP_I2SMCC_MRA_WIRECFG_MASK GENMASK(5, 4)
|
||||
#define MCHP_I2SMCC_MRA_WIRECFG_I2S_1_TDM_0 (0 << 4)
|
||||
#define MCHP_I2SMCC_MRA_WIRECFG_I2S_2_TDM_1 (1 << 4)
|
||||
#define MCHP_I2SMCC_MRA_WIRECFG_I2S_4_TDM_2 (2 << 4)
|
||||
#define MCHP_I2SMCC_MRA_WIRECFG_TDM_3 (3 << 4)
|
||||
|
||||
#define MCHP_I2SMCC_MRA_FORMAT_MASK GENMASK(7, 6)
|
||||
#define MCHP_I2SMCC_MRA_FORMAT_I2S (0 << 6)
|
||||
#define MCHP_I2SMCC_MRA_FORMAT_LJ (1 << 6) /* Left Justified */
|
||||
#define MCHP_I2SMCC_MRA_FORMAT_TDM (2 << 6)
|
||||
#define MCHP_I2SMCC_MRA_FORMAT_TDMLJ (3 << 6)
|
||||
|
||||
/* Transmitter uses one DMA channel ... */
|
||||
/* Left audio samples duplicated to right audio channel */
|
||||
#define MCHP_I2SMCC_MRA_RXMONO BIT(8)
|
||||
|
||||
/* I2SDO output of I2SC is internally connected to I2SDI input */
|
||||
#define MCHP_I2SMCC_MRA_RXLOOP BIT(9)
|
||||
|
||||
/* Receiver uses one DMA channel ... */
|
||||
/* Left audio samples duplicated to right audio channel */
|
||||
#define MCHP_I2SMCC_MRA_TXMONO BIT(10)
|
||||
|
||||
/* x sample transmitted when underrun */
|
||||
#define MCHP_I2SMCC_MRA_TXSAME_ZERO (0 << 11) /* Zero sample */
|
||||
#define MCHP_I2SMCC_MRA_TXSAME_PREVIOUS (1 << 11) /* Previous sample */
|
||||
|
||||
/* select between peripheral clock and generated clock */
|
||||
#define MCHP_I2SMCC_MRA_SRCCLK_PCLK (0 << 12)
|
||||
#define MCHP_I2SMCC_MRA_SRCCLK_GCLK (1 << 12)
|
||||
|
||||
/* Number of TDM Channels - 1 */
|
||||
#define MCHP_I2SMCC_MRA_NBCHAN_MASK GENMASK(15, 13)
|
||||
#define MCHP_I2SMCC_MRA_NBCHAN(ch) \
|
||||
((((ch) - 1) << 13) & MCHP_I2SMCC_MRA_NBCHAN_MASK)
|
||||
|
||||
/* Selected Clock to I2SMCC Master Clock ratio */
|
||||
#define MCHP_I2SMCC_MRA_IMCKDIV_MASK GENMASK(21, 16)
|
||||
#define MCHP_I2SMCC_MRA_IMCKDIV(div) \
|
||||
(((div) << 16) & MCHP_I2SMCC_MRA_IMCKDIV_MASK)
|
||||
|
||||
/* TDM Frame Synchronization */
|
||||
#define MCHP_I2SMCC_MRA_TDMFS_MASK GENMASK(23, 22)
|
||||
#define MCHP_I2SMCC_MRA_TDMFS_SLOT (0 << 22)
|
||||
#define MCHP_I2SMCC_MRA_TDMFS_HALF (1 << 22)
|
||||
#define MCHP_I2SMCC_MRA_TDMFS_BIT (2 << 22)
|
||||
|
||||
/* Selected Clock to I2SMC Serial Clock ratio */
|
||||
#define MCHP_I2SMCC_MRA_ISCKDIV_MASK GENMASK(29, 24)
|
||||
#define MCHP_I2SMCC_MRA_ISCKDIV(div) \
|
||||
(((div) << 24) & MCHP_I2SMCC_MRA_ISCKDIV_MASK)
|
||||
|
||||
/* Master Clock mode */
|
||||
#define MCHP_I2SMCC_MRA_IMCKMODE_MASK GENMASK(30, 30)
|
||||
/* 0: No master clock generated*/
|
||||
#define MCHP_I2SMCC_MRA_IMCKMODE_NONE (0 << 30)
|
||||
/* 1: master clock generated (internally generated clock drives I2SMCK pin) */
|
||||
#define MCHP_I2SMCC_MRA_IMCKMODE_GEN (1 << 30)
|
||||
|
||||
/* Slot Width */
|
||||
/* 0: slot is 32 bits wide for DATALENGTH = 18/20/24 bits. */
|
||||
/* 1: slot is 24 bits wide for DATALENGTH = 18/20/24 bits. */
|
||||
#define MCHP_I2SMCC_MRA_IWS BIT(31)
|
||||
|
||||
/*
|
||||
* ---- Mode Register B (Read/Write) ----
|
||||
*/
|
||||
/* all enabled I2S left channels are filled first, then I2S right channels */
|
||||
#define MCHP_I2SMCC_MRB_CRAMODE_LEFT_FIRST (0 << 0)
|
||||
/*
|
||||
* an enabled I2S left channel is filled, then the corresponding right
|
||||
* channel, until all channels are filled
|
||||
*/
|
||||
#define MCHP_I2SMCC_MRB_CRAMODE_REGULAR (1 << 0)
|
||||
|
||||
#define MCHP_I2SMCC_MRB_FIFOEN BIT(1)
|
||||
|
||||
#define MCHP_I2SMCC_MRB_DMACHUNK_MASK GENMASK(9, 8)
|
||||
#define MCHP_I2SMCC_MRB_DMACHUNK(no_words) \
|
||||
(((fls(no_words) - 1) << 8) & MCHP_I2SMCC_MRB_DMACHUNK_MASK)
|
||||
|
||||
#define MCHP_I2SMCC_MRB_CLKSEL_MASK GENMASK(16, 16)
|
||||
#define MCHP_I2SMCC_MRB_CLKSEL_EXT (0 << 16)
|
||||
#define MCHP_I2SMCC_MRB_CLKSEL_INT (1 << 16)
|
||||
|
||||
/*
|
||||
* ---- Status Registers (Read-only) ----
|
||||
*/
|
||||
#define MCHP_I2SMCC_SR_RXEN BIT(0) /* Receiver Enabled */
|
||||
#define MCHP_I2SMCC_SR_TXEN BIT(4) /* Transmitter Enabled */
|
||||
|
||||
/*
|
||||
* ---- Interrupt Enable/Disable/Mask/Status Registers A ----
|
||||
*/
|
||||
#define MCHP_I2SMCC_INT_TXRDY_MASK(ch) GENMASK((ch) - 1, 0)
|
||||
#define MCHP_I2SMCC_INT_TXRDYCH(ch) BIT(ch)
|
||||
#define MCHP_I2SMCC_INT_TXUNF_MASK(ch) GENMASK((ch) + 7, 8)
|
||||
#define MCHP_I2SMCC_INT_TXUNFCH(ch) BIT((ch) + 8)
|
||||
#define MCHP_I2SMCC_INT_RXRDY_MASK(ch) GENMASK((ch) + 15, 16)
|
||||
#define MCHP_I2SMCC_INT_RXRDYCH(ch) BIT((ch) + 16)
|
||||
#define MCHP_I2SMCC_INT_RXOVF_MASK(ch) GENMASK((ch) + 23, 24)
|
||||
#define MCHP_I2SMCC_INT_RXOVFCH(ch) BIT((ch) + 24)
|
||||
|
||||
/*
|
||||
* ---- Interrupt Enable/Disable/Mask/Status Registers B ----
|
||||
*/
|
||||
#define MCHP_I2SMCC_INT_WERR BIT(0)
|
||||
#define MCHP_I2SMCC_INT_TXFFRDY BIT(8)
|
||||
#define MCHP_I2SMCC_INT_TXFFEMP BIT(9)
|
||||
#define MCHP_I2SMCC_INT_RXFFRDY BIT(12)
|
||||
#define MCHP_I2SMCC_INT_RXFFFUL BIT(13)
|
||||
|
||||
/*
|
||||
* ---- Version Register (Read-only) ----
|
||||
*/
|
||||
#define MCHP_I2SMCC_VERSION_MASK GENMASK(11, 0)
|
||||
|
||||
#define MCHP_I2SMCC_MAX_CHANNELS 8
|
||||
#define MCHP_I2MCC_TDM_SLOT_WIDTH 32
|
||||
|
||||
static const struct regmap_config mchp_i2s_mcc_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
.max_register = MCHP_I2SMCC_VERSION,
|
||||
};
|
||||
|
||||
struct mchp_i2s_mcc_dev {
|
||||
struct wait_queue_head wq_txrdy;
|
||||
struct wait_queue_head wq_rxrdy;
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct clk *pclk;
|
||||
struct clk *gclk;
|
||||
struct snd_dmaengine_dai_dma_data playback;
|
||||
struct snd_dmaengine_dai_dma_data capture;
|
||||
unsigned int fmt;
|
||||
unsigned int sysclk;
|
||||
unsigned int frame_length;
|
||||
int tdm_slots;
|
||||
int channels;
|
||||
int gclk_use:1;
|
||||
int gclk_running:1;
|
||||
int tx_rdy:1;
|
||||
int rx_rdy:1;
|
||||
};
|
||||
|
||||
static irqreturn_t mchp_i2s_mcc_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct mchp_i2s_mcc_dev *dev = dev_id;
|
||||
u32 sra, imra, srb, imrb, pendinga, pendingb, idra = 0;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
|
||||
regmap_read(dev->regmap, MCHP_I2SMCC_IMRA, &imra);
|
||||
regmap_read(dev->regmap, MCHP_I2SMCC_ISRA, &sra);
|
||||
pendinga = imra & sra;
|
||||
|
||||
regmap_read(dev->regmap, MCHP_I2SMCC_IMRB, &imrb);
|
||||
regmap_read(dev->regmap, MCHP_I2SMCC_ISRB, &srb);
|
||||
pendingb = imrb & srb;
|
||||
|
||||
if (!pendinga && !pendingb)
|
||||
return IRQ_NONE;
|
||||
|
||||
/*
|
||||
* Tx/Rx ready interrupts are enabled when stopping only, to assure
|
||||
* availability and to disable clocks if necessary
|
||||
*/
|
||||
idra |= pendinga & (MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels) |
|
||||
MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels));
|
||||
if (idra)
|
||||
ret = IRQ_HANDLED;
|
||||
|
||||
if ((imra & MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels)) &&
|
||||
(imra & MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels)) ==
|
||||
(idra & MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels))) {
|
||||
dev->tx_rdy = 1;
|
||||
wake_up_interruptible(&dev->wq_txrdy);
|
||||
}
|
||||
if ((imra & MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels)) &&
|
||||
(imra & MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels)) ==
|
||||
(idra & MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels))) {
|
||||
dev->rx_rdy = 1;
|
||||
wake_up_interruptible(&dev->wq_rxrdy);
|
||||
}
|
||||
regmap_write(dev->regmap, MCHP_I2SMCC_IDRA, idra);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mchp_i2s_mcc_set_sysclk(struct snd_soc_dai *dai,
|
||||
int clk_id, unsigned int freq, int dir)
|
||||
{
|
||||
struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
dev_dbg(dev->dev, "%s() clk_id=%d freq=%u dir=%d\n",
|
||||
__func__, clk_id, freq, dir);
|
||||
|
||||
/* We do not need SYSCLK */
|
||||
if (dir == SND_SOC_CLOCK_IN)
|
||||
return 0;
|
||||
|
||||
dev->sysclk = freq;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mchp_i2s_mcc_set_bclk_ratio(struct snd_soc_dai *dai,
|
||||
unsigned int ratio)
|
||||
{
|
||||
struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
dev_dbg(dev->dev, "%s() ratio=%u\n", __func__, ratio);
|
||||
|
||||
dev->frame_length = ratio;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mchp_i2s_mcc_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
{
|
||||
struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
dev_dbg(dev->dev, "%s() fmt=%#x\n", __func__, fmt);
|
||||
|
||||
/* We don't support any kind of clock inversion */
|
||||
if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF)
|
||||
return -EINVAL;
|
||||
|
||||
/* We can't generate only FSYNC */
|
||||
if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBM_CFS)
|
||||
return -EINVAL;
|
||||
|
||||
/* We can only reconfigure the IP when it's stopped */
|
||||
if (fmt & SND_SOC_DAIFMT_CONT)
|
||||
return -EINVAL;
|
||||
|
||||
dev->fmt = fmt;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mchp_i2s_mcc_set_dai_tdm_slot(struct snd_soc_dai *dai,
|
||||
unsigned int tx_mask,
|
||||
unsigned int rx_mask,
|
||||
int slots, int slot_width)
|
||||
{
|
||||
struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
dev_dbg(dev->dev,
|
||||
"%s() tx_mask=0x%08x rx_mask=0x%08x slots=%d width=%d\n",
|
||||
__func__, tx_mask, rx_mask, slots, slot_width);
|
||||
|
||||
if (slots < 0 || slots > MCHP_I2SMCC_MAX_CHANNELS ||
|
||||
slot_width != MCHP_I2MCC_TDM_SLOT_WIDTH)
|
||||
return -EINVAL;
|
||||
|
||||
if (slots) {
|
||||
/* We do not support daisy chain */
|
||||
if (rx_mask != GENMASK(slots - 1, 0) ||
|
||||
rx_mask != tx_mask)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev->tdm_slots = slots;
|
||||
dev->frame_length = slots * MCHP_I2MCC_TDM_SLOT_WIDTH;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mchp_i2s_mcc_clk_get_rate_diff(struct clk *clk,
|
||||
unsigned long rate,
|
||||
struct clk **best_clk,
|
||||
unsigned long *best_rate,
|
||||
unsigned long *best_diff_rate)
|
||||
{
|
||||
long round_rate;
|
||||
unsigned int diff_rate;
|
||||
|
||||
round_rate = clk_round_rate(clk, rate);
|
||||
if (round_rate < 0)
|
||||
return (int)round_rate;
|
||||
|
||||
diff_rate = abs(rate - round_rate);
|
||||
if (diff_rate < *best_diff_rate) {
|
||||
*best_clk = clk;
|
||||
*best_diff_rate = diff_rate;
|
||||
*best_rate = rate;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mchp_i2s_mcc_config_divs(struct mchp_i2s_mcc_dev *dev,
|
||||
unsigned int bclk, unsigned int *mra)
|
||||
{
|
||||
unsigned long clk_rate;
|
||||
unsigned long lcm_rate;
|
||||
unsigned long best_rate = 0;
|
||||
unsigned long best_diff_rate = ~0;
|
||||
unsigned int sysclk;
|
||||
struct clk *best_clk = NULL;
|
||||
int ret;
|
||||
|
||||
/* For code simplification */
|
||||
if (!dev->sysclk)
|
||||
sysclk = bclk;
|
||||
else
|
||||
sysclk = dev->sysclk;
|
||||
|
||||
/*
|
||||
* MCLK is Selected CLK / (2 * IMCKDIV),
|
||||
* BCLK is Selected CLK / (2 * ISCKDIV);
|
||||
* if IMCKDIV or ISCKDIV are 0, MCLK or BCLK = Selected CLK
|
||||
*/
|
||||
lcm_rate = lcm(sysclk, bclk);
|
||||
if ((lcm_rate / sysclk % 2 == 1 && lcm_rate / sysclk > 2) ||
|
||||
(lcm_rate / bclk % 2 == 1 && lcm_rate / bclk > 2))
|
||||
lcm_rate *= 2;
|
||||
|
||||
for (clk_rate = lcm_rate;
|
||||
(clk_rate == sysclk || clk_rate / (sysclk * 2) <= GENMASK(5, 0)) &&
|
||||
(clk_rate == bclk || clk_rate / (bclk * 2) <= GENMASK(5, 0));
|
||||
clk_rate += lcm_rate) {
|
||||
ret = mchp_i2s_mcc_clk_get_rate_diff(dev->gclk, clk_rate,
|
||||
&best_clk, &best_rate,
|
||||
&best_diff_rate);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "gclk error for rate %lu: %d",
|
||||
clk_rate, ret);
|
||||
} else {
|
||||
if (!best_diff_rate) {
|
||||
dev_dbg(dev->dev, "found perfect rate on gclk: %lu\n",
|
||||
clk_rate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret = mchp_i2s_mcc_clk_get_rate_diff(dev->pclk, clk_rate,
|
||||
&best_clk, &best_rate,
|
||||
&best_diff_rate);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "pclk error for rate %lu: %d",
|
||||
clk_rate, ret);
|
||||
} else {
|
||||
if (!best_diff_rate) {
|
||||
dev_dbg(dev->dev, "found perfect rate on pclk: %lu\n",
|
||||
clk_rate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* check if clocks returned only errors */
|
||||
if (!best_clk) {
|
||||
dev_err(dev->dev, "unable to change rate to clocks\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(dev->dev, "source CLK is %s with rate %lu, diff %lu\n",
|
||||
best_clk == dev->pclk ? "pclk" : "gclk",
|
||||
best_rate, best_diff_rate);
|
||||
|
||||
/* set the rate */
|
||||
ret = clk_set_rate(best_clk, best_rate);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "unable to set rate %lu to %s: %d\n",
|
||||
best_rate, best_clk == dev->pclk ? "PCLK" : "GCLK",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Configure divisors */
|
||||
if (dev->sysclk)
|
||||
*mra |= MCHP_I2SMCC_MRA_IMCKDIV(best_rate / (2 * sysclk));
|
||||
*mra |= MCHP_I2SMCC_MRA_ISCKDIV(best_rate / (2 * bclk));
|
||||
|
||||
if (best_clk == dev->gclk) {
|
||||
*mra |= MCHP_I2SMCC_MRA_SRCCLK_GCLK;
|
||||
ret = clk_prepare(dev->gclk);
|
||||
if (ret < 0)
|
||||
dev_err(dev->dev, "unable to prepare GCLK: %d\n", ret);
|
||||
else
|
||||
dev->gclk_use = 1;
|
||||
} else {
|
||||
*mra |= MCHP_I2SMCC_MRA_SRCCLK_PCLK;
|
||||
dev->gclk_use = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mchp_i2s_mcc_is_running(struct mchp_i2s_mcc_dev *dev)
|
||||
{
|
||||
u32 sr;
|
||||
|
||||
regmap_read(dev->regmap, MCHP_I2SMCC_SR, &sr);
|
||||
return !!(sr & (MCHP_I2SMCC_SR_TXEN | MCHP_I2SMCC_SR_RXEN));
|
||||
}
|
||||
|
||||
static int mchp_i2s_mcc_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
|
||||
u32 mra = 0;
|
||||
u32 mrb = 0;
|
||||
unsigned int channels = params_channels(params);
|
||||
unsigned int frame_length = dev->frame_length;
|
||||
unsigned int bclk_rate;
|
||||
int set_divs = 0;
|
||||
int ret;
|
||||
bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
|
||||
|
||||
dev_dbg(dev->dev, "%s() rate=%u format=%#x width=%u channels=%u\n",
|
||||
__func__, params_rate(params), params_format(params),
|
||||
params_width(params), params_channels(params));
|
||||
|
||||
switch (dev->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
if (dev->tdm_slots) {
|
||||
dev_err(dev->dev, "I2S with TDM is not supported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
mra |= MCHP_I2SMCC_MRA_FORMAT_I2S;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
if (dev->tdm_slots) {
|
||||
dev_err(dev->dev, "Left-Justified with TDM is not supported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
mra |= MCHP_I2SMCC_MRA_FORMAT_LJ;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_DSP_A:
|
||||
mra |= MCHP_I2SMCC_MRA_FORMAT_TDM;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev->dev, "unsupported bus format\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
/* cpu is BCLK and LRC master */
|
||||
mra |= MCHP_I2SMCC_MRA_MODE_MASTER;
|
||||
if (dev->sysclk)
|
||||
mra |= MCHP_I2SMCC_MRA_IMCKMODE_GEN;
|
||||
set_divs = 1;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBS_CFM:
|
||||
/* cpu is BCLK master */
|
||||
mrb |= MCHP_I2SMCC_MRB_CLKSEL_INT;
|
||||
set_divs = 1;
|
||||
/* fall through */
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
/* cpu is slave */
|
||||
mra |= MCHP_I2SMCC_MRA_MODE_SLAVE;
|
||||
if (dev->sysclk)
|
||||
dev_warn(dev->dev, "Unable to generate MCLK in Slave mode\n");
|
||||
break;
|
||||
default:
|
||||
dev_err(dev->dev, "unsupported master/slave mode\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (dev->fmt & (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J)) {
|
||||
switch (channels) {
|
||||
case 1:
|
||||
if (is_playback)
|
||||
mra |= MCHP_I2SMCC_MRA_TXMONO;
|
||||
else
|
||||
mra |= MCHP_I2SMCC_MRA_RXMONO;
|
||||
break;
|
||||
case 2:
|
||||
break;
|
||||
default:
|
||||
dev_err(dev->dev, "unsupported number of audio channels\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!frame_length)
|
||||
frame_length = 2 * params_physical_width(params);
|
||||
} else if (dev->fmt & SND_SOC_DAIFMT_DSP_A) {
|
||||
if (dev->tdm_slots) {
|
||||
if (channels % 2 && channels * 2 <= dev->tdm_slots) {
|
||||
/*
|
||||
* Duplicate data for even-numbered channels
|
||||
* to odd-numbered channels
|
||||
*/
|
||||
if (is_playback)
|
||||
mra |= MCHP_I2SMCC_MRA_TXMONO;
|
||||
else
|
||||
mra |= MCHP_I2SMCC_MRA_RXMONO;
|
||||
}
|
||||
channels = dev->tdm_slots;
|
||||
}
|
||||
|
||||
mra |= MCHP_I2SMCC_MRA_NBCHAN(channels);
|
||||
if (!frame_length)
|
||||
frame_length = channels * MCHP_I2MCC_TDM_SLOT_WIDTH;
|
||||
}
|
||||
|
||||
/*
|
||||
* We must have the same burst size configured
|
||||
* in the DMA transfer and in out IP
|
||||
*/
|
||||
mrb |= MCHP_I2SMCC_MRB_DMACHUNK(channels);
|
||||
if (is_playback)
|
||||
dev->playback.maxburst = 1 << (fls(channels) - 1);
|
||||
else
|
||||
dev->capture.maxburst = 1 << (fls(channels) - 1);
|
||||
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S8:
|
||||
mra |= MCHP_I2SMCC_MRA_DATALENGTH_8_BITS;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
mra |= MCHP_I2SMCC_MRA_DATALENGTH_16_BITS;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S18_3LE:
|
||||
mra |= MCHP_I2SMCC_MRA_DATALENGTH_18_BITS |
|
||||
MCHP_I2SMCC_MRA_IWS;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
||||
mra |= MCHP_I2SMCC_MRA_DATALENGTH_20_BITS |
|
||||
MCHP_I2SMCC_MRA_IWS;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_3LE:
|
||||
mra |= MCHP_I2SMCC_MRA_DATALENGTH_24_BITS |
|
||||
MCHP_I2SMCC_MRA_IWS;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
mra |= MCHP_I2SMCC_MRA_DATALENGTH_24_BITS;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
mra |= MCHP_I2SMCC_MRA_DATALENGTH_32_BITS;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev->dev, "unsupported size/endianness for audio samples\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we are already running, the wanted setup must be
|
||||
* the same with the one that's currently ongoing
|
||||
*/
|
||||
if (mchp_i2s_mcc_is_running(dev)) {
|
||||
u32 mra_cur;
|
||||
u32 mrb_cur;
|
||||
|
||||
regmap_read(dev->regmap, MCHP_I2SMCC_MRA, &mra_cur);
|
||||
regmap_read(dev->regmap, MCHP_I2SMCC_MRB, &mrb_cur);
|
||||
if (mra != mra_cur || mrb != mrb_cur)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Save the number of channels to know what interrupts to enable */
|
||||
dev->channels = channels;
|
||||
|
||||
if (set_divs) {
|
||||
bclk_rate = frame_length * params_rate(params);
|
||||
ret = mchp_i2s_mcc_config_divs(dev, bclk_rate, &mra);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "unable to configure the divisors: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = regmap_write(dev->regmap, MCHP_I2SMCC_MRA, mra);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return regmap_write(dev->regmap, MCHP_I2SMCC_MRB, mrb);
|
||||
}
|
||||
|
||||
static int mchp_i2s_mcc_hw_free(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
|
||||
bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
|
||||
long err;
|
||||
|
||||
if (is_playback) {
|
||||
err = wait_event_interruptible_timeout(dev->wq_txrdy,
|
||||
dev->tx_rdy,
|
||||
msecs_to_jiffies(500));
|
||||
} else {
|
||||
err = wait_event_interruptible_timeout(dev->wq_rxrdy,
|
||||
dev->rx_rdy,
|
||||
msecs_to_jiffies(500));
|
||||
}
|
||||
|
||||
if (err == 0) {
|
||||
u32 idra;
|
||||
|
||||
dev_warn_once(dev->dev, "Timeout waiting for %s\n",
|
||||
is_playback ? "Tx ready" : "Rx ready");
|
||||
if (is_playback)
|
||||
idra = MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels);
|
||||
else
|
||||
idra = MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels);
|
||||
regmap_write(dev->regmap, MCHP_I2SMCC_IDRA, idra);
|
||||
}
|
||||
|
||||
if (!mchp_i2s_mcc_is_running(dev)) {
|
||||
regmap_write(dev->regmap, MCHP_I2SMCC_CR, MCHP_I2SMCC_CR_CKDIS);
|
||||
|
||||
if (dev->gclk_running) {
|
||||
clk_disable_unprepare(dev->gclk);
|
||||
dev->gclk_running = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mchp_i2s_mcc_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
|
||||
bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
|
||||
u32 cr = 0;
|
||||
u32 iera = 0;
|
||||
u32 sr;
|
||||
int err;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
if (is_playback)
|
||||
cr = MCHP_I2SMCC_CR_TXEN | MCHP_I2SMCC_CR_CKEN;
|
||||
else
|
||||
cr = MCHP_I2SMCC_CR_RXEN | MCHP_I2SMCC_CR_CKEN;
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
regmap_read(dev->regmap, MCHP_I2SMCC_SR, &sr);
|
||||
if (is_playback && (sr & MCHP_I2SMCC_SR_TXEN)) {
|
||||
cr = MCHP_I2SMCC_CR_TXDIS;
|
||||
dev->tx_rdy = 0;
|
||||
/*
|
||||
* Enable Tx Ready interrupts on all channels
|
||||
* to assure all data is sent
|
||||
*/
|
||||
iera = MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels);
|
||||
} else if (!is_playback && (sr & MCHP_I2SMCC_SR_RXEN)) {
|
||||
cr = MCHP_I2SMCC_CR_RXDIS;
|
||||
dev->rx_rdy = 0;
|
||||
/*
|
||||
* Enable Rx Ready interrupts on all channels
|
||||
* to assure all data is received
|
||||
*/
|
||||
iera = MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((cr & MCHP_I2SMCC_CR_CKEN) && dev->gclk_use &&
|
||||
!dev->gclk_running) {
|
||||
err = clk_enable(dev->gclk);
|
||||
if (err) {
|
||||
dev_err_once(dev->dev, "failed to enable GCLK: %d\n",
|
||||
err);
|
||||
} else {
|
||||
dev->gclk_running = 1;
|
||||
}
|
||||
}
|
||||
|
||||
regmap_write(dev->regmap, MCHP_I2SMCC_IERA, iera);
|
||||
regmap_write(dev->regmap, MCHP_I2SMCC_CR, cr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mchp_i2s_mcc_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
/* Software reset the IP if it's not running */
|
||||
if (!mchp_i2s_mcc_is_running(dev)) {
|
||||
return regmap_write(dev->regmap, MCHP_I2SMCC_CR,
|
||||
MCHP_I2SMCC_CR_SWRST);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops mchp_i2s_mcc_dai_ops = {
|
||||
.set_sysclk = mchp_i2s_mcc_set_sysclk,
|
||||
.set_bclk_ratio = mchp_i2s_mcc_set_bclk_ratio,
|
||||
.startup = mchp_i2s_mcc_startup,
|
||||
.trigger = mchp_i2s_mcc_trigger,
|
||||
.hw_params = mchp_i2s_mcc_hw_params,
|
||||
.hw_free = mchp_i2s_mcc_hw_free,
|
||||
.set_fmt = mchp_i2s_mcc_set_dai_fmt,
|
||||
.set_tdm_slot = mchp_i2s_mcc_set_dai_tdm_slot,
|
||||
};
|
||||
|
||||
static int mchp_i2s_mcc_dai_probe(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
init_waitqueue_head(&dev->wq_txrdy);
|
||||
init_waitqueue_head(&dev->wq_rxrdy);
|
||||
|
||||
snd_soc_dai_init_dma_data(dai, &dev->playback, &dev->capture);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define MCHP_I2SMCC_RATES SNDRV_PCM_RATE_8000_192000
|
||||
|
||||
#define MCHP_I2SMCC_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
|
||||
SNDRV_PCM_FMTBIT_S16_LE | \
|
||||
SNDRV_PCM_FMTBIT_S18_3LE | \
|
||||
SNDRV_PCM_FMTBIT_S20_3LE | \
|
||||
SNDRV_PCM_FMTBIT_S24_3LE | \
|
||||
SNDRV_PCM_FMTBIT_S24_LE | \
|
||||
SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
||||
static struct snd_soc_dai_driver mchp_i2s_mcc_dai = {
|
||||
.probe = mchp_i2s_mcc_dai_probe,
|
||||
.playback = {
|
||||
.stream_name = "I2SMCC-Playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = 8,
|
||||
.rates = MCHP_I2SMCC_RATES,
|
||||
.formats = MCHP_I2SMCC_FORMATS,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "I2SMCC-Capture",
|
||||
.channels_min = 1,
|
||||
.channels_max = 8,
|
||||
.rates = MCHP_I2SMCC_RATES,
|
||||
.formats = MCHP_I2SMCC_FORMATS,
|
||||
},
|
||||
.ops = &mchp_i2s_mcc_dai_ops,
|
||||
.symmetric_rates = 1,
|
||||
.symmetric_samplebits = 1,
|
||||
.symmetric_channels = 1,
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver mchp_i2s_mcc_component = {
|
||||
.name = "mchp-i2s-mcc",
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id mchp_i2s_mcc_dt_ids[] = {
|
||||
{
|
||||
.compatible = "microchip,sam9x60-i2smcc",
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mchp_i2s_mcc_dt_ids);
|
||||
#endif
|
||||
|
||||
static int mchp_i2s_mcc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mchp_i2s_mcc_dev *dev;
|
||||
struct resource *mem;
|
||||
struct regmap *regmap;
|
||||
void __iomem *base;
|
||||
u32 version;
|
||||
int irq;
|
||||
int err;
|
||||
|
||||
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(&pdev->dev, mem);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
regmap = devm_regmap_init_mmio(&pdev->dev, base,
|
||||
&mchp_i2s_mcc_regmap_config);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
err = devm_request_irq(&pdev->dev, irq, mchp_i2s_mcc_interrupt, 0,
|
||||
dev_name(&pdev->dev), dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
dev->pclk = devm_clk_get(&pdev->dev, "pclk");
|
||||
if (IS_ERR(dev->pclk)) {
|
||||
err = PTR_ERR(dev->pclk);
|
||||
dev_err(&pdev->dev,
|
||||
"failed to get the peripheral clock: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Get the optional generated clock */
|
||||
dev->gclk = devm_clk_get(&pdev->dev, "gclk");
|
||||
if (IS_ERR(dev->gclk)) {
|
||||
if (PTR_ERR(dev->gclk) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
dev_warn(&pdev->dev,
|
||||
"generated clock not found: %d\n", err);
|
||||
dev->gclk = NULL;
|
||||
}
|
||||
|
||||
dev->dev = &pdev->dev;
|
||||
dev->regmap = regmap;
|
||||
platform_set_drvdata(pdev, dev);
|
||||
|
||||
err = clk_prepare_enable(dev->pclk);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to enable the peripheral clock: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = devm_snd_soc_register_component(&pdev->dev,
|
||||
&mchp_i2s_mcc_component,
|
||||
&mchp_i2s_mcc_dai, 1);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to register DAI: %d\n", err);
|
||||
clk_disable_unprepare(dev->pclk);
|
||||
return err;
|
||||
}
|
||||
|
||||
dev->playback.addr = (dma_addr_t)mem->start + MCHP_I2SMCC_THR;
|
||||
dev->capture.addr = (dma_addr_t)mem->start + MCHP_I2SMCC_RHR;
|
||||
|
||||
err = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to register PCM: %d\n", err);
|
||||
clk_disable_unprepare(dev->pclk);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Get IP version. */
|
||||
regmap_read(dev->regmap, MCHP_I2SMCC_VERSION, &version);
|
||||
dev_info(&pdev->dev, "hw version: %#lx\n",
|
||||
version & MCHP_I2SMCC_VERSION_MASK);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mchp_i2s_mcc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mchp_i2s_mcc_dev *dev = platform_get_drvdata(pdev);
|
||||
|
||||
clk_disable_unprepare(dev->pclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver mchp_i2s_mcc_driver = {
|
||||
.driver = {
|
||||
.name = "mchp_i2s_mcc",
|
||||
.of_match_table = of_match_ptr(mchp_i2s_mcc_dt_ids),
|
||||
},
|
||||
.probe = mchp_i2s_mcc_probe,
|
||||
.remove = mchp_i2s_mcc_remove,
|
||||
};
|
||||
module_platform_driver(mchp_i2s_mcc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Microchip I2S Multi-Channel Controller driver");
|
||||
MODULE_AUTHOR("Codrin Ciubotariu <codrin.ciubotariu@microchip.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -117,8 +117,8 @@ static int tse850_put_mux2(struct snd_kcontrol *kctrl,
|
|||
return snd_soc_dapm_put_enum_double(kctrl, ucontrol);
|
||||
}
|
||||
|
||||
int tse850_get_mix(struct snd_kcontrol *kctrl,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
static int tse850_get_mix(struct snd_kcontrol *kctrl,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
|
||||
struct snd_soc_card *card = dapm->card;
|
||||
|
@ -129,8 +129,8 @@ int tse850_get_mix(struct snd_kcontrol *kctrl,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int tse850_put_mix(struct snd_kcontrol *kctrl,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
static int tse850_put_mix(struct snd_kcontrol *kctrl,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
|
||||
struct snd_soc_card *card = dapm->card;
|
||||
|
@ -151,8 +151,8 @@ int tse850_put_mix(struct snd_kcontrol *kctrl,
|
|||
return 1;
|
||||
}
|
||||
|
||||
int tse850_get_ana(struct snd_kcontrol *kctrl,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
static int tse850_get_ana(struct snd_kcontrol *kctrl,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
|
||||
struct snd_soc_card *card = dapm->card;
|
||||
|
@ -184,8 +184,8 @@ int tse850_get_ana(struct snd_kcontrol *kctrl,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int tse850_put_ana(struct snd_kcontrol *kctrl,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
static int tse850_put_ana(struct snd_kcontrol *kctrl,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
|
||||
struct snd_soc_card *card = dapm->card;
|
||||
|
|
|
@ -94,6 +94,7 @@ config SND_SOC_ALL_CODECS
|
|||
select SND_SOC_JZ4725B_CODEC
|
||||
select SND_SOC_LM4857 if I2C
|
||||
select SND_SOC_LM49453 if I2C
|
||||
select SND_SOC_LOCHNAGAR_SC if MFD_LOCHNAGAR
|
||||
select SND_SOC_MAX98088 if I2C
|
||||
select SND_SOC_MAX98090 if I2C
|
||||
select SND_SOC_MAX98095 if I2C
|
||||
|
@ -179,8 +180,8 @@ config SND_SOC_ALL_CODECS
|
|||
select SND_SOC_TLV320AIC23_SPI if SPI_MASTER
|
||||
select SND_SOC_TLV320AIC26 if SPI_MASTER
|
||||
select SND_SOC_TLV320AIC31XX if I2C
|
||||
select SND_SOC_TLV320AIC32X4_I2C if I2C
|
||||
select SND_SOC_TLV320AIC32X4_SPI if SPI_MASTER
|
||||
select SND_SOC_TLV320AIC32X4_I2C if I2C && COMMON_CLK
|
||||
select SND_SOC_TLV320AIC32X4_SPI if SPI_MASTER && COMMON_CLK
|
||||
select SND_SOC_TLV320AIC3X if I2C
|
||||
select SND_SOC_TPA6130A2 if I2C
|
||||
select SND_SOC_TLV320DAC33 if I2C
|
||||
|
@ -688,6 +689,13 @@ config SND_SOC_ISABELLE
|
|||
config SND_SOC_LM49453
|
||||
tristate
|
||||
|
||||
config SND_SOC_LOCHNAGAR_SC
|
||||
tristate "Lochnagar Sound Card"
|
||||
depends on MFD_LOCHNAGAR
|
||||
help
|
||||
This driver support the sound card functionality of the Cirrus
|
||||
Logic Lochnagar audio development board.
|
||||
|
||||
config SND_SOC_MAX98088
|
||||
tristate "Maxim MAX98088/9 Low-Power, Stereo Audio Codec"
|
||||
depends on I2C
|
||||
|
@ -1097,15 +1105,18 @@ config SND_SOC_TLV320AIC31XX
|
|||
|
||||
config SND_SOC_TLV320AIC32X4
|
||||
tristate
|
||||
depends on COMMON_CLK
|
||||
|
||||
config SND_SOC_TLV320AIC32X4_I2C
|
||||
tristate "Texas Instruments TLV320AIC32x4 audio CODECs - I2C"
|
||||
depends on I2C
|
||||
depends on COMMON_CLK
|
||||
select SND_SOC_TLV320AIC32X4
|
||||
|
||||
config SND_SOC_TLV320AIC32X4_SPI
|
||||
tristate "Texas Instruments TLV320AIC32x4 audio CODECs - SPI"
|
||||
depends on SPI_MASTER
|
||||
depends on COMMON_CLK
|
||||
select SND_SOC_TLV320AIC32X4
|
||||
|
||||
config SND_SOC_TLV320AIC3X
|
||||
|
|
|
@ -91,6 +91,7 @@ snd-soc-jz4725b-codec-objs := jz4725b.o
|
|||
snd-soc-l3-objs := l3.o
|
||||
snd-soc-lm4857-objs := lm4857.o
|
||||
snd-soc-lm49453-objs := lm49453.o
|
||||
snd-soc-lochnagar-sc-objs := lochnagar-sc.o
|
||||
snd-soc-max9759-objs := max9759.o
|
||||
snd-soc-max9768-objs := max9768.o
|
||||
snd-soc-max98088-objs := max98088.o
|
||||
|
@ -192,7 +193,7 @@ snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o
|
|||
snd-soc-tlv320aic23-spi-objs := tlv320aic23-spi.o
|
||||
snd-soc-tlv320aic26-objs := tlv320aic26.o
|
||||
snd-soc-tlv320aic31xx-objs := tlv320aic31xx.o
|
||||
snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o
|
||||
snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o tlv320aic32x4-clk.o
|
||||
snd-soc-tlv320aic32x4-i2c-objs := tlv320aic32x4-i2c.o
|
||||
snd-soc-tlv320aic32x4-spi-objs := tlv320aic32x4-spi.o
|
||||
snd-soc-tlv320aic3x-objs := tlv320aic3x.o
|
||||
|
@ -364,6 +365,7 @@ obj-$(CONFIG_SND_SOC_JZ4725B_CODEC) += snd-soc-jz4725b-codec.o
|
|||
obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
|
||||
obj-$(CONFIG_SND_SOC_LM4857) += snd-soc-lm4857.o
|
||||
obj-$(CONFIG_SND_SOC_LM49453) += snd-soc-lm49453.o
|
||||
obj-$(CONFIG_SND_SOC_LOCHNAGAR_SC) += snd-soc-lochnagar-sc.o
|
||||
obj-$(CONFIG_SND_SOC_MAX9759) += snd-soc-max9759.o
|
||||
obj-$(CONFIG_SND_SOC_MAX9768) += snd-soc-max9768.o
|
||||
obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o
|
||||
|
|
|
@ -29,18 +29,27 @@ static int cs42l51_i2c_probe(struct i2c_client *i2c,
|
|||
struct regmap_config config;
|
||||
|
||||
config = cs42l51_regmap;
|
||||
config.val_bits = 8;
|
||||
config.reg_bits = 8;
|
||||
|
||||
return cs42l51_probe(&i2c->dev, devm_regmap_init_i2c(i2c, &config));
|
||||
}
|
||||
|
||||
static int cs42l51_i2c_remove(struct i2c_client *i2c)
|
||||
{
|
||||
return cs42l51_remove(&i2c->dev);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops cs42l51_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(cs42l51_suspend, cs42l51_resume)
|
||||
};
|
||||
|
||||
static struct i2c_driver cs42l51_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "cs42l51",
|
||||
.of_match_table = cs42l51_of_match,
|
||||
.pm = &cs42l51_pm_ops,
|
||||
},
|
||||
.probe = cs42l51_i2c_probe,
|
||||
.remove = cs42l51_i2c_remove,
|
||||
.id_table = cs42l51_i2c_id,
|
||||
};
|
||||
|
||||
|
|
|
@ -30,7 +30,9 @@
|
|||
#include <sound/initval.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include "cs42l51.h"
|
||||
|
||||
|
@ -40,11 +42,21 @@ enum master_slave_mode {
|
|||
MODE_MASTER,
|
||||
};
|
||||
|
||||
static const char * const cs42l51_supply_names[] = {
|
||||
"VL",
|
||||
"VD",
|
||||
"VA",
|
||||
"VAHP",
|
||||
};
|
||||
|
||||
struct cs42l51_private {
|
||||
unsigned int mclk;
|
||||
struct clk *mclk_handle;
|
||||
unsigned int audio_mode; /* The mode (I2S or left-justified) */
|
||||
enum master_slave_mode func;
|
||||
struct regulator_bulk_data supplies[ARRAY_SIZE(cs42l51_supply_names)];
|
||||
struct gpio_desc *reset_gpio;
|
||||
struct regmap *regmap;
|
||||
};
|
||||
|
||||
#define CS42L51_FORMATS ( \
|
||||
|
@ -111,6 +123,7 @@ static const DECLARE_TLV_DB_SCALE(tone_tlv, -1050, 150, 0);
|
|||
static const DECLARE_TLV_DB_SCALE(aout_tlv, -10200, 50, 0);
|
||||
|
||||
static const DECLARE_TLV_DB_SCALE(boost_tlv, 1600, 1600, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(adc_boost_tlv, 2000, 2000, 0);
|
||||
static const char *chan_mix[] = {
|
||||
"L R",
|
||||
"L+R",
|
||||
|
@ -139,6 +152,8 @@ static const struct snd_kcontrol_new cs42l51_snd_controls[] = {
|
|||
SOC_SINGLE("Zero Cross Switch", CS42L51_DAC_CTL, 0, 0, 0),
|
||||
SOC_DOUBLE_TLV("Mic Boost Volume",
|
||||
CS42L51_MIC_CTL, 0, 1, 1, 0, boost_tlv),
|
||||
SOC_DOUBLE_TLV("ADC Boost Volume",
|
||||
CS42L51_MIC_CTL, 5, 6, 1, 0, adc_boost_tlv),
|
||||
SOC_SINGLE_TLV("Bass Volume", CS42L51_TONE_CTL, 0, 0xf, 1, tone_tlv),
|
||||
SOC_SINGLE_TLV("Treble Volume", CS42L51_TONE_CTL, 4, 0xf, 1, tone_tlv),
|
||||
SOC_ENUM_EXT("PCM channel mixer",
|
||||
|
@ -195,7 +210,8 @@ static const struct snd_kcontrol_new cs42l51_adcr_mux_controls =
|
|||
SOC_DAPM_ENUM("Route", cs42l51_adcr_mux_enum);
|
||||
|
||||
static const struct snd_soc_dapm_widget cs42l51_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_MICBIAS("Mic Bias", CS42L51_MIC_POWER_CTL, 1, 1),
|
||||
SND_SOC_DAPM_SUPPLY("Mic Bias", CS42L51_MIC_POWER_CTL, 1, 1, NULL,
|
||||
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
||||
SND_SOC_DAPM_PGA_E("Left PGA", CS42L51_POWER_CTL1, 3, 1, NULL, 0,
|
||||
cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
|
||||
SND_SOC_DAPM_PGA_E("Right PGA", CS42L51_POWER_CTL1, 4, 1, NULL, 0,
|
||||
|
@ -329,6 +345,19 @@ static struct cs42l51_ratios slave_auto_ratios[] = {
|
|||
{ 256, CS42L51_DSM_MODE, 1 }, { 384, CS42L51_DSM_MODE, 1 },
|
||||
};
|
||||
|
||||
/*
|
||||
* Master mode mclk/fs ratios.
|
||||
* Recommended configurations are SSM for 4-50khz and DSM for 50-100kHz ranges
|
||||
* The table below provides support of following ratios:
|
||||
* 128: SSM (%128) with div2 disabled
|
||||
* 256: SSM (%128) with div2 enabled
|
||||
* In both cases, if sampling rate is above 50kHz, SSM is overridden
|
||||
* with DSM (%128) configuration
|
||||
*/
|
||||
static struct cs42l51_ratios master_ratios[] = {
|
||||
{ 128, CS42L51_SSM_MODE, 0 }, { 256, CS42L51_SSM_MODE, 1 },
|
||||
};
|
||||
|
||||
static int cs42l51_set_dai_sysclk(struct snd_soc_dai *codec_dai,
|
||||
int clk_id, unsigned int freq, int dir)
|
||||
{
|
||||
|
@ -351,11 +380,13 @@ static int cs42l51_hw_params(struct snd_pcm_substream *substream,
|
|||
unsigned int ratio;
|
||||
struct cs42l51_ratios *ratios = NULL;
|
||||
int nr_ratios = 0;
|
||||
int intf_ctl, power_ctl, fmt;
|
||||
int intf_ctl, power_ctl, fmt, mode;
|
||||
|
||||
switch (cs42l51->func) {
|
||||
case MODE_MASTER:
|
||||
return -EINVAL;
|
||||
ratios = master_ratios;
|
||||
nr_ratios = ARRAY_SIZE(master_ratios);
|
||||
break;
|
||||
case MODE_SLAVE:
|
||||
ratios = slave_ratios;
|
||||
nr_ratios = ARRAY_SIZE(slave_ratios);
|
||||
|
@ -391,7 +422,16 @@ static int cs42l51_hw_params(struct snd_pcm_substream *substream,
|
|||
switch (cs42l51->func) {
|
||||
case MODE_MASTER:
|
||||
intf_ctl |= CS42L51_INTF_CTL_MASTER;
|
||||
power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode);
|
||||
mode = ratios[i].speed_mode;
|
||||
/* Force DSM mode if sampling rate is above 50kHz */
|
||||
if (rate > 50000)
|
||||
mode = CS42L51_DSM_MODE;
|
||||
power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(mode);
|
||||
/*
|
||||
* Auto detect mode is not applicable for master mode and has to
|
||||
* be disabled. Otherwise SPEED[1:0] bits will be ignored.
|
||||
*/
|
||||
power_ctl &= ~CS42L51_MIC_POWER_CTL_AUTO;
|
||||
break;
|
||||
case MODE_SLAVE:
|
||||
power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode);
|
||||
|
@ -464,6 +504,13 @@ static int cs42l51_dai_mute(struct snd_soc_dai *dai, int mute)
|
|||
return snd_soc_component_write(component, CS42L51_DAC_OUT_CTL, reg);
|
||||
}
|
||||
|
||||
static int cs42l51_of_xlate_dai_id(struct snd_soc_component *component,
|
||||
struct device_node *endpoint)
|
||||
{
|
||||
/* return dai id 0, whatever the endpoint index */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops cs42l51_dai_ops = {
|
||||
.hw_params = cs42l51_hw_params,
|
||||
.set_sysclk = cs42l51_set_dai_sysclk,
|
||||
|
@ -526,13 +573,113 @@ static const struct snd_soc_component_driver soc_component_device_cs42l51 = {
|
|||
.num_dapm_widgets = ARRAY_SIZE(cs42l51_dapm_widgets),
|
||||
.dapm_routes = cs42l51_routes,
|
||||
.num_dapm_routes = ARRAY_SIZE(cs42l51_routes),
|
||||
.of_xlate_dai_id = cs42l51_of_xlate_dai_id,
|
||||
.idle_bias_on = 1,
|
||||
.use_pmdown_time = 1,
|
||||
.endianness = 1,
|
||||
.non_legacy_dai_naming = 1,
|
||||
};
|
||||
|
||||
static bool cs42l51_writeable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case CS42L51_POWER_CTL1:
|
||||
case CS42L51_MIC_POWER_CTL:
|
||||
case CS42L51_INTF_CTL:
|
||||
case CS42L51_MIC_CTL:
|
||||
case CS42L51_ADC_CTL:
|
||||
case CS42L51_ADC_INPUT:
|
||||
case CS42L51_DAC_OUT_CTL:
|
||||
case CS42L51_DAC_CTL:
|
||||
case CS42L51_ALC_PGA_CTL:
|
||||
case CS42L51_ALC_PGB_CTL:
|
||||
case CS42L51_ADCA_ATT:
|
||||
case CS42L51_ADCB_ATT:
|
||||
case CS42L51_ADCA_VOL:
|
||||
case CS42L51_ADCB_VOL:
|
||||
case CS42L51_PCMA_VOL:
|
||||
case CS42L51_PCMB_VOL:
|
||||
case CS42L51_BEEP_FREQ:
|
||||
case CS42L51_BEEP_VOL:
|
||||
case CS42L51_BEEP_CONF:
|
||||
case CS42L51_TONE_CTL:
|
||||
case CS42L51_AOUTA_VOL:
|
||||
case CS42L51_AOUTB_VOL:
|
||||
case CS42L51_PCM_MIXER:
|
||||
case CS42L51_LIMIT_THRES_DIS:
|
||||
case CS42L51_LIMIT_REL:
|
||||
case CS42L51_LIMIT_ATT:
|
||||
case CS42L51_ALC_EN:
|
||||
case CS42L51_ALC_REL:
|
||||
case CS42L51_ALC_THRES:
|
||||
case CS42L51_NOISE_CONF:
|
||||
case CS42L51_CHARGE_FREQ:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool cs42l51_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case CS42L51_STATUS:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool cs42l51_readable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case CS42L51_CHIP_REV_ID:
|
||||
case CS42L51_POWER_CTL1:
|
||||
case CS42L51_MIC_POWER_CTL:
|
||||
case CS42L51_INTF_CTL:
|
||||
case CS42L51_MIC_CTL:
|
||||
case CS42L51_ADC_CTL:
|
||||
case CS42L51_ADC_INPUT:
|
||||
case CS42L51_DAC_OUT_CTL:
|
||||
case CS42L51_DAC_CTL:
|
||||
case CS42L51_ALC_PGA_CTL:
|
||||
case CS42L51_ALC_PGB_CTL:
|
||||
case CS42L51_ADCA_ATT:
|
||||
case CS42L51_ADCB_ATT:
|
||||
case CS42L51_ADCA_VOL:
|
||||
case CS42L51_ADCB_VOL:
|
||||
case CS42L51_PCMA_VOL:
|
||||
case CS42L51_PCMB_VOL:
|
||||
case CS42L51_BEEP_FREQ:
|
||||
case CS42L51_BEEP_VOL:
|
||||
case CS42L51_BEEP_CONF:
|
||||
case CS42L51_TONE_CTL:
|
||||
case CS42L51_AOUTA_VOL:
|
||||
case CS42L51_AOUTB_VOL:
|
||||
case CS42L51_PCM_MIXER:
|
||||
case CS42L51_LIMIT_THRES_DIS:
|
||||
case CS42L51_LIMIT_REL:
|
||||
case CS42L51_LIMIT_ATT:
|
||||
case CS42L51_ALC_EN:
|
||||
case CS42L51_ALC_REL:
|
||||
case CS42L51_ALC_THRES:
|
||||
case CS42L51_NOISE_CONF:
|
||||
case CS42L51_STATUS:
|
||||
case CS42L51_CHARGE_FREQ:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const struct regmap_config cs42l51_regmap = {
|
||||
.reg_bits = 8,
|
||||
.reg_stride = 1,
|
||||
.val_bits = 8,
|
||||
.use_single_write = true,
|
||||
.readable_reg = cs42l51_readable_reg,
|
||||
.volatile_reg = cs42l51_volatile_reg,
|
||||
.writeable_reg = cs42l51_writeable_reg,
|
||||
.max_register = CS42L51_CHARGE_FREQ,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
@ -542,7 +689,7 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap)
|
|||
{
|
||||
struct cs42l51_private *cs42l51;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
int ret, i;
|
||||
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
@ -553,6 +700,7 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap)
|
|||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(dev, cs42l51);
|
||||
cs42l51->regmap = regmap;
|
||||
|
||||
cs42l51->mclk_handle = devm_clk_get(dev, "MCLK");
|
||||
if (IS_ERR(cs42l51->mclk_handle)) {
|
||||
|
@ -561,6 +709,34 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap)
|
|||
cs42l51->mclk_handle = NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(cs42l51->supplies); i++)
|
||||
cs42l51->supplies[i].supply = cs42l51_supply_names[i];
|
||||
|
||||
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs42l51->supplies),
|
||||
cs42l51->supplies);
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "Failed to request supplies: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(cs42l51->supplies),
|
||||
cs42l51->supplies);
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "Failed to enable supplies: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
cs42l51->reset_gpio = devm_gpiod_get_optional(dev, "reset",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(cs42l51->reset_gpio))
|
||||
return PTR_ERR(cs42l51->reset_gpio);
|
||||
|
||||
if (cs42l51->reset_gpio) {
|
||||
dev_dbg(dev, "Release reset gpio\n");
|
||||
gpiod_set_value_cansleep(cs42l51->reset_gpio, 0);
|
||||
mdelay(2);
|
||||
}
|
||||
|
||||
/* Verify that we have a CS42L51 */
|
||||
ret = regmap_read(regmap, CS42L51_CHIP_REV_ID, &val);
|
||||
if (ret < 0) {
|
||||
|
@ -579,11 +755,50 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap)
|
|||
|
||||
ret = devm_snd_soc_register_component(dev,
|
||||
&soc_component_device_cs42l51, &cs42l51_dai, 1);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
regulator_bulk_disable(ARRAY_SIZE(cs42l51->supplies),
|
||||
cs42l51->supplies);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cs42l51_probe);
|
||||
|
||||
int cs42l51_remove(struct device *dev)
|
||||
{
|
||||
struct cs42l51_private *cs42l51 = dev_get_drvdata(dev);
|
||||
|
||||
gpiod_set_value_cansleep(cs42l51->reset_gpio, 1);
|
||||
|
||||
return regulator_bulk_disable(ARRAY_SIZE(cs42l51->supplies),
|
||||
cs42l51->supplies);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cs42l51_remove);
|
||||
|
||||
int __maybe_unused cs42l51_suspend(struct device *dev)
|
||||
{
|
||||
struct cs42l51_private *cs42l51 = dev_get_drvdata(dev);
|
||||
|
||||
regcache_cache_only(cs42l51->regmap, true);
|
||||
regcache_mark_dirty(cs42l51->regmap);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cs42l51_suspend);
|
||||
|
||||
int __maybe_unused cs42l51_resume(struct device *dev)
|
||||
{
|
||||
struct cs42l51_private *cs42l51 = dev_get_drvdata(dev);
|
||||
|
||||
regcache_cache_only(cs42l51->regmap, false);
|
||||
|
||||
return regcache_sync(cs42l51->regmap);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cs42l51_resume);
|
||||
|
||||
const struct of_device_id cs42l51_of_match[] = {
|
||||
{ .compatible = "cirrus,cs42l51", },
|
||||
{ }
|
||||
|
|
|
@ -22,6 +22,9 @@ struct device;
|
|||
|
||||
extern const struct regmap_config cs42l51_regmap;
|
||||
int cs42l51_probe(struct device *dev, struct regmap *regmap);
|
||||
int cs42l51_remove(struct device *dev);
|
||||
int __maybe_unused cs42l51_suspend(struct device *dev);
|
||||
int __maybe_unused cs42l51_resume(struct device *dev);
|
||||
extern const struct of_device_id cs42l51_of_match[];
|
||||
|
||||
#define CS42L51_CHIP_ID 0x1B
|
||||
|
|
|
@ -2322,6 +2322,8 @@ static int cs43130_probe(struct snd_soc_component *component)
|
|||
return ret;
|
||||
|
||||
cs43130->wq = create_singlethread_workqueue("cs43130_hp");
|
||||
if (!cs43130->wq)
|
||||
return -ENOMEM;
|
||||
INIT_WORK(&cs43130->work, cs43130_imp_meas);
|
||||
}
|
||||
|
||||
|
|
|
@ -75,7 +75,9 @@ static int cs47l24_adsp_power_ev(struct snd_soc_dapm_widget *w,
|
|||
|
||||
v = (v & ARIZONA_SYSCLK_FREQ_MASK) >> ARIZONA_SYSCLK_FREQ_SHIFT;
|
||||
|
||||
return wm_adsp2_early_event(w, kcontrol, event, v);
|
||||
wm_adsp2_set_dspclk(w, v);
|
||||
|
||||
return wm_adsp_early_event(w, kcontrol, event);
|
||||
}
|
||||
|
||||
static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
|
||||
|
|
|
@ -1305,7 +1305,10 @@ static int da7213_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
|
|||
/* By default only 64 BCLK per WCLK is supported */
|
||||
dai_clk_mode |= DA7213_DAI_BCLKS_PER_WCLK_64;
|
||||
|
||||
snd_soc_component_write(component, DA7213_DAI_CLK_MODE, dai_clk_mode);
|
||||
snd_soc_component_update_bits(component, DA7213_DAI_CLK_MODE,
|
||||
DA7213_DAI_BCLKS_PER_WCLK_MASK |
|
||||
DA7213_DAI_CLK_POL_MASK | DA7213_DAI_WCLK_POL_MASK,
|
||||
dai_clk_mode);
|
||||
snd_soc_component_update_bits(component, DA7213_DAI_CTRL, DA7213_DAI_FORMAT_MASK,
|
||||
dai_ctrl);
|
||||
snd_soc_component_write(component, DA7213_DAI_OFFSET, dai_offset);
|
||||
|
|
|
@ -181,7 +181,9 @@
|
|||
#define DA7213_DAI_BCLKS_PER_WCLK_256 (0x3 << 0)
|
||||
#define DA7213_DAI_BCLKS_PER_WCLK_MASK (0x3 << 0)
|
||||
#define DA7213_DAI_CLK_POL_INV (0x1 << 2)
|
||||
#define DA7213_DAI_CLK_POL_MASK (0x1 << 2)
|
||||
#define DA7213_DAI_WCLK_POL_INV (0x1 << 3)
|
||||
#define DA7213_DAI_WCLK_POL_MASK (0x1 << 3)
|
||||
#define DA7213_DAI_CLK_EN_MASK (0x1 << 7)
|
||||
|
||||
/* DA7213_DAI_CTRL = 0x29 */
|
||||
|
|
|
@ -797,6 +797,7 @@ static int da7219_dai_event(struct snd_soc_dapm_widget *w,
|
|||
{
|
||||
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
|
||||
struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
|
||||
struct clk *bclk = da7219->dai_clks[DA7219_DAI_BCLK_IDX];
|
||||
u8 pll_ctrl, pll_status;
|
||||
int i = 0, ret;
|
||||
bool srm_lock = false;
|
||||
|
@ -805,11 +806,11 @@ static int da7219_dai_event(struct snd_soc_dapm_widget *w,
|
|||
case SND_SOC_DAPM_PRE_PMU:
|
||||
if (da7219->master) {
|
||||
/* Enable DAI clks for master mode */
|
||||
if (da7219->dai_clks) {
|
||||
ret = clk_prepare_enable(da7219->dai_clks);
|
||||
if (bclk) {
|
||||
ret = clk_prepare_enable(bclk);
|
||||
if (ret) {
|
||||
dev_err(component->dev,
|
||||
"Failed to enable dai_clks\n");
|
||||
"Failed to enable DAI clks\n");
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
|
@ -852,8 +853,8 @@ static int da7219_dai_event(struct snd_soc_dapm_widget *w,
|
|||
|
||||
/* Disable DAI clks if in master mode */
|
||||
if (da7219->master) {
|
||||
if (da7219->dai_clks)
|
||||
clk_disable_unprepare(da7219->dai_clks);
|
||||
if (bclk)
|
||||
clk_disable_unprepare(bclk);
|
||||
else
|
||||
snd_soc_component_update_bits(component,
|
||||
DA7219_DAI_CLK_MODE,
|
||||
|
@ -1385,17 +1386,50 @@ static int da7219_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int da7219_set_bclks_per_wclk(struct snd_soc_component *component,
|
||||
unsigned long factor)
|
||||
{
|
||||
u8 bclks_per_wclk;
|
||||
|
||||
switch (factor) {
|
||||
case 32:
|
||||
bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32;
|
||||
break;
|
||||
case 64:
|
||||
bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64;
|
||||
break;
|
||||
case 128:
|
||||
bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_128;
|
||||
break;
|
||||
case 256:
|
||||
bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_256;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
|
||||
DA7219_DAI_BCLKS_PER_WCLK_MASK,
|
||||
bclks_per_wclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int da7219_set_dai_tdm_slot(struct snd_soc_dai *dai,
|
||||
unsigned int tx_mask, unsigned int rx_mask,
|
||||
int slots, int slot_width)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
|
||||
struct clk *wclk = da7219->dai_clks[DA7219_DAI_WCLK_IDX];
|
||||
struct clk *bclk = da7219->dai_clks[DA7219_DAI_BCLK_IDX];
|
||||
unsigned int ch_mask;
|
||||
u8 dai_bclks_per_wclk, slot_offset;
|
||||
unsigned long sr, bclk_rate;
|
||||
u8 slot_offset;
|
||||
u16 offset;
|
||||
__le16 dai_offset;
|
||||
u32 frame_size;
|
||||
int ret;
|
||||
|
||||
/* No channels enabled so disable TDM */
|
||||
if (!tx_mask) {
|
||||
|
@ -1432,28 +1466,26 @@ static int da7219_set_dai_tdm_slot(struct snd_soc_dai *dai,
|
|||
*/
|
||||
if (da7219->master) {
|
||||
frame_size = slots * slot_width;
|
||||
switch (frame_size) {
|
||||
case 32:
|
||||
dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32;
|
||||
break;
|
||||
case 64:
|
||||
dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64;
|
||||
break;
|
||||
case 128:
|
||||
dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_128;
|
||||
break;
|
||||
case 256:
|
||||
dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_256;
|
||||
break;
|
||||
default:
|
||||
dev_err(component->dev, "Invalid frame size %d\n",
|
||||
frame_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
|
||||
DA7219_DAI_BCLKS_PER_WCLK_MASK,
|
||||
dai_bclks_per_wclk);
|
||||
if (bclk) {
|
||||
sr = clk_get_rate(wclk);
|
||||
bclk_rate = sr * frame_size;
|
||||
ret = clk_set_rate(bclk, bclk_rate);
|
||||
if (ret) {
|
||||
dev_err(component->dev,
|
||||
"Failed to set TDM BCLK rate %lu: %d\n",
|
||||
bclk_rate, ret);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
ret = da7219_set_bclks_per_wclk(component, frame_size);
|
||||
if (ret) {
|
||||
dev_err(component->dev,
|
||||
"Failed to set TDM BCLKs per WCLK %d: %d\n",
|
||||
frame_size, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dai_offset = cpu_to_le16(offset);
|
||||
|
@ -1471,44 +1503,12 @@ static int da7219_set_dai_tdm_slot(struct snd_soc_dai *dai,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int da7219_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
static int da7219_set_sr(struct snd_soc_component *component,
|
||||
unsigned long rate)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
|
||||
u8 dai_ctrl = 0, dai_bclks_per_wclk = 0, fs;
|
||||
unsigned int channels;
|
||||
int word_len = params_width(params);
|
||||
int frame_size;
|
||||
u8 fs;
|
||||
|
||||
switch (word_len) {
|
||||
case 16:
|
||||
dai_ctrl |= DA7219_DAI_WORD_LENGTH_S16_LE;
|
||||
break;
|
||||
case 20:
|
||||
dai_ctrl |= DA7219_DAI_WORD_LENGTH_S20_LE;
|
||||
break;
|
||||
case 24:
|
||||
dai_ctrl |= DA7219_DAI_WORD_LENGTH_S24_LE;
|
||||
break;
|
||||
case 32:
|
||||
dai_ctrl |= DA7219_DAI_WORD_LENGTH_S32_LE;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
channels = params_channels(params);
|
||||
if ((channels < 1) || (channels > DA7219_DAI_CH_NUM_MAX)) {
|
||||
dev_err(component->dev,
|
||||
"Invalid number of channels, only 1 to %d supported\n",
|
||||
DA7219_DAI_CH_NUM_MAX);
|
||||
return -EINVAL;
|
||||
}
|
||||
dai_ctrl |= channels << DA7219_DAI_CH_NUM_SHIFT;
|
||||
|
||||
switch (params_rate(params)) {
|
||||
switch (rate) {
|
||||
case 8000:
|
||||
fs = DA7219_SR_8000;
|
||||
break;
|
||||
|
@ -1546,28 +1546,118 @@ static int da7219_hw_params(struct snd_pcm_substream *substream,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
snd_soc_component_write(component, DA7219_SR, fs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int da7219_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
|
||||
struct clk *wclk = da7219->dai_clks[DA7219_DAI_WCLK_IDX];
|
||||
struct clk *bclk = da7219->dai_clks[DA7219_DAI_BCLK_IDX];
|
||||
u8 dai_ctrl = 0;
|
||||
unsigned int channels;
|
||||
unsigned long sr, bclk_rate;
|
||||
int word_len = params_width(params);
|
||||
int frame_size, ret;
|
||||
|
||||
switch (word_len) {
|
||||
case 16:
|
||||
dai_ctrl |= DA7219_DAI_WORD_LENGTH_S16_LE;
|
||||
break;
|
||||
case 20:
|
||||
dai_ctrl |= DA7219_DAI_WORD_LENGTH_S20_LE;
|
||||
break;
|
||||
case 24:
|
||||
dai_ctrl |= DA7219_DAI_WORD_LENGTH_S24_LE;
|
||||
break;
|
||||
case 32:
|
||||
dai_ctrl |= DA7219_DAI_WORD_LENGTH_S32_LE;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
channels = params_channels(params);
|
||||
if ((channels < 1) || (channels > DA7219_DAI_CH_NUM_MAX)) {
|
||||
dev_err(component->dev,
|
||||
"Invalid number of channels, only 1 to %d supported\n",
|
||||
DA7219_DAI_CH_NUM_MAX);
|
||||
return -EINVAL;
|
||||
}
|
||||
dai_ctrl |= channels << DA7219_DAI_CH_NUM_SHIFT;
|
||||
|
||||
sr = params_rate(params);
|
||||
if (da7219->master && wclk) {
|
||||
ret = clk_set_rate(wclk, sr);
|
||||
if (ret) {
|
||||
dev_err(component->dev,
|
||||
"Failed to set WCLK SR %lu: %d\n", sr, ret);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
ret = da7219_set_sr(component, sr);
|
||||
if (ret) {
|
||||
dev_err(component->dev,
|
||||
"Failed to set SR %lu: %d\n", sr, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If we're master, then we have a limited set of BCLK rates we
|
||||
* support. For slave mode this isn't the case and the codec can detect
|
||||
* the BCLK rate automatically.
|
||||
*/
|
||||
if (da7219->master && !da7219->tdm_en) {
|
||||
frame_size = word_len * 2;
|
||||
if (frame_size <= 32)
|
||||
dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32;
|
||||
if ((word_len * DA7219_DAI_CH_NUM_MAX) <= 32)
|
||||
frame_size = 32;
|
||||
else
|
||||
dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64;
|
||||
frame_size = 64;
|
||||
|
||||
snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
|
||||
DA7219_DAI_BCLKS_PER_WCLK_MASK,
|
||||
dai_bclks_per_wclk);
|
||||
if (bclk) {
|
||||
bclk_rate = frame_size * sr;
|
||||
/*
|
||||
* Rounding the rate here avoids failure trying to set a
|
||||
* new rate on an already enabled bclk. In that
|
||||
* instance this will just set the same rate as is
|
||||
* currently in use, and so should continue without
|
||||
* problem, as long as the BCLK rate is suitable for the
|
||||
* desired frame size.
|
||||
*/
|
||||
bclk_rate = clk_round_rate(bclk, bclk_rate);
|
||||
if ((bclk_rate / sr) < frame_size) {
|
||||
dev_err(component->dev,
|
||||
"BCLK rate mismatch against frame size");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = clk_set_rate(bclk, bclk_rate);
|
||||
if (ret) {
|
||||
dev_err(component->dev,
|
||||
"Failed to set BCLK rate %lu: %d\n",
|
||||
bclk_rate, ret);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
ret = da7219_set_bclks_per_wclk(component, frame_size);
|
||||
if (ret) {
|
||||
dev_err(component->dev,
|
||||
"Failed to set BCLKs per WCLK %d: %d\n",
|
||||
frame_size, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
snd_soc_component_update_bits(component, DA7219_DAI_CTRL,
|
||||
DA7219_DAI_WORD_LENGTH_MASK |
|
||||
DA7219_DAI_CH_NUM_MASK,
|
||||
dai_ctrl);
|
||||
snd_soc_component_write(component, DA7219_SR, fs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1583,20 +1673,26 @@ static const struct snd_soc_dai_ops da7219_dai_ops = {
|
|||
#define DA7219_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
|
||||
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
||||
#define DA7219_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
|
||||
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
|
||||
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
|
||||
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
|
||||
SNDRV_PCM_RATE_96000)
|
||||
|
||||
static struct snd_soc_dai_driver da7219_dai = {
|
||||
.name = "da7219-hifi",
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = DA7219_DAI_CH_NUM_MAX,
|
||||
.rates = SNDRV_PCM_RATE_8000_96000,
|
||||
.rates = DA7219_RATES,
|
||||
.formats = DA7219_FORMATS,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "Capture",
|
||||
.channels_min = 1,
|
||||
.channels_max = DA7219_DAI_CH_NUM_MAX,
|
||||
.rates = SNDRV_PCM_RATE_8000_96000,
|
||||
.rates = DA7219_RATES,
|
||||
.formats = DA7219_FORMATS,
|
||||
},
|
||||
.ops = &da7219_dai_ops,
|
||||
|
@ -1672,11 +1768,14 @@ static struct da7219_pdata *da7219_fw_to_pdata(struct snd_soc_component *compone
|
|||
|
||||
pdata->wakeup_source = device_property_read_bool(dev, "wakeup-source");
|
||||
|
||||
pdata->dai_clks_name = "da7219-dai-clks";
|
||||
if (device_property_read_string(dev, "clock-output-names",
|
||||
&pdata->dai_clks_name))
|
||||
dev_warn(dev, "Using default clk name: %s\n",
|
||||
pdata->dai_clks_name);
|
||||
pdata->dai_clk_names[DA7219_DAI_WCLK_IDX] = "da7219-dai-wclk";
|
||||
pdata->dai_clk_names[DA7219_DAI_BCLK_IDX] = "da7219-dai-bclk";
|
||||
if (device_property_read_string_array(dev, "clock-output-names",
|
||||
pdata->dai_clk_names,
|
||||
DA7219_DAI_NUM_CLKS) < 0)
|
||||
dev_warn(dev, "Using default DAI clk names: %s, %s\n",
|
||||
pdata->dai_clk_names[DA7219_DAI_WCLK_IDX],
|
||||
pdata->dai_clk_names[DA7219_DAI_BCLK_IDX]);
|
||||
|
||||
if (device_property_read_u32(dev, "dlg,micbias-lvl", &of_val32) >= 0)
|
||||
pdata->micbias_lvl = da7219_fw_micbias_lvl(dev, of_val32);
|
||||
|
@ -1793,12 +1892,16 @@ static int da7219_handle_supplies(struct snd_soc_component *component)
|
|||
}
|
||||
|
||||
#ifdef CONFIG_COMMON_CLK
|
||||
static int da7219_dai_clks_prepare(struct clk_hw *hw)
|
||||
static int da7219_wclk_prepare(struct clk_hw *hw)
|
||||
{
|
||||
struct da7219_priv *da7219 =
|
||||
container_of(hw, struct da7219_priv, dai_clks_hw);
|
||||
container_of(hw, struct da7219_priv,
|
||||
dai_clks_hw[DA7219_DAI_WCLK_IDX]);
|
||||
struct snd_soc_component *component = da7219->component;
|
||||
|
||||
if (!da7219->master)
|
||||
return -EINVAL;
|
||||
|
||||
snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
|
||||
DA7219_DAI_CLK_EN_MASK,
|
||||
DA7219_DAI_CLK_EN_MASK);
|
||||
|
@ -1806,33 +1909,42 @@ static int da7219_dai_clks_prepare(struct clk_hw *hw)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void da7219_dai_clks_unprepare(struct clk_hw *hw)
|
||||
static void da7219_wclk_unprepare(struct clk_hw *hw)
|
||||
{
|
||||
struct da7219_priv *da7219 =
|
||||
container_of(hw, struct da7219_priv, dai_clks_hw);
|
||||
container_of(hw, struct da7219_priv,
|
||||
dai_clks_hw[DA7219_DAI_WCLK_IDX]);
|
||||
struct snd_soc_component *component = da7219->component;
|
||||
|
||||
if (!da7219->master)
|
||||
return;
|
||||
|
||||
snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
|
||||
DA7219_DAI_CLK_EN_MASK, 0);
|
||||
}
|
||||
|
||||
static int da7219_dai_clks_is_prepared(struct clk_hw *hw)
|
||||
static int da7219_wclk_is_prepared(struct clk_hw *hw)
|
||||
{
|
||||
struct da7219_priv *da7219 =
|
||||
container_of(hw, struct da7219_priv, dai_clks_hw);
|
||||
container_of(hw, struct da7219_priv,
|
||||
dai_clks_hw[DA7219_DAI_WCLK_IDX]);
|
||||
struct snd_soc_component *component = da7219->component;
|
||||
u8 clk_reg;
|
||||
|
||||
if (!da7219->master)
|
||||
return -EINVAL;
|
||||
|
||||
clk_reg = snd_soc_component_read32(component, DA7219_DAI_CLK_MODE);
|
||||
|
||||
return !!(clk_reg & DA7219_DAI_CLK_EN_MASK);
|
||||
}
|
||||
|
||||
static unsigned long da7219_dai_clks_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
static unsigned long da7219_wclk_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct da7219_priv *da7219 =
|
||||
container_of(hw, struct da7219_priv, dai_clks_hw);
|
||||
container_of(hw, struct da7219_priv,
|
||||
dai_clks_hw[DA7219_DAI_WCLK_IDX]);
|
||||
struct snd_soc_component *component = da7219->component;
|
||||
u8 fs = snd_soc_component_read32(component, DA7219_SR);
|
||||
|
||||
|
@ -1864,11 +1976,148 @@ static unsigned long da7219_dai_clks_recalc_rate(struct clk_hw *hw,
|
|||
}
|
||||
}
|
||||
|
||||
static const struct clk_ops da7219_dai_clks_ops = {
|
||||
.prepare = da7219_dai_clks_prepare,
|
||||
.unprepare = da7219_dai_clks_unprepare,
|
||||
.is_prepared = da7219_dai_clks_is_prepared,
|
||||
.recalc_rate = da7219_dai_clks_recalc_rate,
|
||||
static long da7219_wclk_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
struct da7219_priv *da7219 =
|
||||
container_of(hw, struct da7219_priv,
|
||||
dai_clks_hw[DA7219_DAI_WCLK_IDX]);
|
||||
|
||||
if (!da7219->master)
|
||||
return -EINVAL;
|
||||
|
||||
if (rate < 11025)
|
||||
return 8000;
|
||||
else if (rate < 12000)
|
||||
return 11025;
|
||||
else if (rate < 16000)
|
||||
return 12000;
|
||||
else if (rate < 22050)
|
||||
return 16000;
|
||||
else if (rate < 24000)
|
||||
return 22050;
|
||||
else if (rate < 32000)
|
||||
return 24000;
|
||||
else if (rate < 44100)
|
||||
return 32000;
|
||||
else if (rate < 48000)
|
||||
return 44100;
|
||||
else if (rate < 88200)
|
||||
return 48000;
|
||||
else if (rate < 96000)
|
||||
return 88200;
|
||||
else
|
||||
return 96000;
|
||||
}
|
||||
|
||||
static int da7219_wclk_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct da7219_priv *da7219 =
|
||||
container_of(hw, struct da7219_priv,
|
||||
dai_clks_hw[DA7219_DAI_WCLK_IDX]);
|
||||
struct snd_soc_component *component = da7219->component;
|
||||
|
||||
if (!da7219->master)
|
||||
return -EINVAL;
|
||||
|
||||
return da7219_set_sr(component, rate);
|
||||
}
|
||||
|
||||
static unsigned long da7219_bclk_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct da7219_priv *da7219 =
|
||||
container_of(hw, struct da7219_priv,
|
||||
dai_clks_hw[DA7219_DAI_BCLK_IDX]);
|
||||
struct snd_soc_component *component = da7219->component;
|
||||
u8 bclks_per_wclk = snd_soc_component_read32(component,
|
||||
DA7219_DAI_CLK_MODE);
|
||||
|
||||
switch (bclks_per_wclk & DA7219_DAI_BCLKS_PER_WCLK_MASK) {
|
||||
case DA7219_DAI_BCLKS_PER_WCLK_32:
|
||||
return parent_rate * 32;
|
||||
case DA7219_DAI_BCLKS_PER_WCLK_64:
|
||||
return parent_rate * 64;
|
||||
case DA7219_DAI_BCLKS_PER_WCLK_128:
|
||||
return parent_rate * 128;
|
||||
case DA7219_DAI_BCLKS_PER_WCLK_256:
|
||||
return parent_rate * 256;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned long da7219_bclk_get_factor(unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
unsigned long factor;
|
||||
|
||||
factor = rate / parent_rate;
|
||||
if (factor < 64)
|
||||
return 32;
|
||||
else if (factor < 128)
|
||||
return 64;
|
||||
else if (factor < 256)
|
||||
return 128;
|
||||
else
|
||||
return 256;
|
||||
}
|
||||
|
||||
static long da7219_bclk_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
struct da7219_priv *da7219 =
|
||||
container_of(hw, struct da7219_priv,
|
||||
dai_clks_hw[DA7219_DAI_BCLK_IDX]);
|
||||
unsigned long factor;
|
||||
|
||||
if (!*parent_rate || !da7219->master)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* We don't allow changing the parent rate as some BCLK rates can be
|
||||
* derived from multiple parent WCLK rates (BCLK rates are set as a
|
||||
* multiplier of WCLK in HW). We just do some rounding down based on the
|
||||
* parent WCLK rate set and find the appropriate multiplier of BCLK to
|
||||
* get the rounded down BCLK value.
|
||||
*/
|
||||
factor = da7219_bclk_get_factor(rate, *parent_rate);
|
||||
|
||||
return *parent_rate * factor;
|
||||
}
|
||||
|
||||
static int da7219_bclk_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct da7219_priv *da7219 =
|
||||
container_of(hw, struct da7219_priv,
|
||||
dai_clks_hw[DA7219_DAI_BCLK_IDX]);
|
||||
struct snd_soc_component *component = da7219->component;
|
||||
unsigned long factor;
|
||||
|
||||
if (!da7219->master)
|
||||
return -EINVAL;
|
||||
|
||||
factor = da7219_bclk_get_factor(rate, parent_rate);
|
||||
|
||||
return da7219_set_bclks_per_wclk(component, factor);
|
||||
}
|
||||
|
||||
static const struct clk_ops da7219_dai_clk_ops[DA7219_DAI_NUM_CLKS] = {
|
||||
[DA7219_DAI_WCLK_IDX] = {
|
||||
.prepare = da7219_wclk_prepare,
|
||||
.unprepare = da7219_wclk_unprepare,
|
||||
.is_prepared = da7219_wclk_is_prepared,
|
||||
.recalc_rate = da7219_wclk_recalc_rate,
|
||||
.round_rate = da7219_wclk_round_rate,
|
||||
.set_rate = da7219_wclk_set_rate,
|
||||
},
|
||||
[DA7219_DAI_BCLK_IDX] = {
|
||||
.recalc_rate = da7219_bclk_recalc_rate,
|
||||
.round_rate = da7219_bclk_round_rate,
|
||||
.set_rate = da7219_bclk_set_rate,
|
||||
},
|
||||
};
|
||||
|
||||
static int da7219_register_dai_clks(struct snd_soc_component *component)
|
||||
|
@ -1876,47 +2125,81 @@ static int da7219_register_dai_clks(struct snd_soc_component *component)
|
|||
struct device *dev = component->dev;
|
||||
struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
|
||||
struct da7219_pdata *pdata = da7219->pdata;
|
||||
struct clk_init_data init = {};
|
||||
struct clk *dai_clks;
|
||||
struct clk_lookup *dai_clks_lookup;
|
||||
const char *parent_name;
|
||||
int i, ret;
|
||||
|
||||
if (da7219->mclk) {
|
||||
parent_name = __clk_get_name(da7219->mclk);
|
||||
init.parent_names = &parent_name;
|
||||
init.num_parents = 1;
|
||||
} else {
|
||||
init.parent_names = NULL;
|
||||
init.num_parents = 0;
|
||||
}
|
||||
for (i = 0; i < DA7219_DAI_NUM_CLKS; ++i) {
|
||||
struct clk_init_data init = {};
|
||||
struct clk *dai_clk;
|
||||
struct clk_lookup *dai_clk_lookup;
|
||||
struct clk_hw *dai_clk_hw = &da7219->dai_clks_hw[i];
|
||||
|
||||
init.name = pdata->dai_clks_name;
|
||||
init.ops = &da7219_dai_clks_ops;
|
||||
init.flags = CLK_GET_RATE_NOCACHE;
|
||||
da7219->dai_clks_hw.init = &init;
|
||||
switch (i) {
|
||||
case DA7219_DAI_WCLK_IDX:
|
||||
/*
|
||||
* If we can, make MCLK the parent of WCLK to ensure
|
||||
* it's enabled as required.
|
||||
*/
|
||||
if (da7219->mclk) {
|
||||
parent_name = __clk_get_name(da7219->mclk);
|
||||
init.parent_names = &parent_name;
|
||||
init.num_parents = 1;
|
||||
} else {
|
||||
init.parent_names = NULL;
|
||||
init.num_parents = 0;
|
||||
}
|
||||
break;
|
||||
case DA7219_DAI_BCLK_IDX:
|
||||
/* Make WCLK the parent of BCLK */
|
||||
parent_name = __clk_get_name(da7219->dai_clks[DA7219_DAI_WCLK_IDX]);
|
||||
init.parent_names = &parent_name;
|
||||
init.num_parents = 1;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "Invalid clock index\n");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
dai_clks = devm_clk_register(dev, &da7219->dai_clks_hw);
|
||||
if (IS_ERR(dai_clks)) {
|
||||
dev_warn(dev, "Failed to register DAI clocks: %ld\n",
|
||||
PTR_ERR(dai_clks));
|
||||
return PTR_ERR(dai_clks);
|
||||
}
|
||||
da7219->dai_clks = dai_clks;
|
||||
init.name = pdata->dai_clk_names[i];
|
||||
init.ops = &da7219_dai_clk_ops[i];
|
||||
init.flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_GATE;
|
||||
dai_clk_hw->init = &init;
|
||||
|
||||
/* If we're using DT, then register as provider accordingly */
|
||||
if (dev->of_node) {
|
||||
devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
|
||||
&da7219->dai_clks_hw);
|
||||
} else {
|
||||
dai_clks_lookup = clkdev_create(dai_clks, pdata->dai_clks_name,
|
||||
"%s", dev_name(dev));
|
||||
if (!dai_clks_lookup)
|
||||
return -ENOMEM;
|
||||
else
|
||||
da7219->dai_clks_lookup = dai_clks_lookup;
|
||||
dai_clk = devm_clk_register(dev, dai_clk_hw);
|
||||
if (IS_ERR(dai_clk)) {
|
||||
dev_warn(dev, "Failed to register %s: %ld\n",
|
||||
init.name, PTR_ERR(dai_clk));
|
||||
ret = PTR_ERR(dai_clk);
|
||||
goto err;
|
||||
}
|
||||
da7219->dai_clks[i] = dai_clk;
|
||||
|
||||
/* If we're using DT, then register as provider accordingly */
|
||||
if (dev->of_node) {
|
||||
devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
|
||||
dai_clk_hw);
|
||||
} else {
|
||||
dai_clk_lookup = clkdev_create(dai_clk, init.name,
|
||||
"%s", dev_name(dev));
|
||||
if (!dai_clk_lookup) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
} else {
|
||||
da7219->dai_clks_lookup[i] = dai_clk_lookup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
do {
|
||||
if (da7219->dai_clks_lookup[i])
|
||||
clkdev_drop(da7219->dai_clks_lookup[i]);
|
||||
} while (i-- > 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
static inline int da7219_register_dai_clks(struct snd_soc_component *component)
|
||||
|
@ -2080,12 +2363,17 @@ static int da7219_probe(struct snd_soc_component *component)
|
|||
static void da7219_remove(struct snd_soc_component *component)
|
||||
{
|
||||
struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
|
||||
#ifdef CONFIG_COMMON_CLK
|
||||
int i;
|
||||
#endif
|
||||
|
||||
da7219_aad_exit(component);
|
||||
|
||||
#ifdef CONFIG_COMMON_CLK
|
||||
if (da7219->dai_clks_lookup)
|
||||
clkdev_drop(da7219->dai_clks_lookup);
|
||||
for (i = DA7219_DAI_NUM_CLKS - 1; i >= 0; --i) {
|
||||
if (da7219->dai_clks_lookup[i])
|
||||
clkdev_drop(da7219->dai_clks_lookup[i]);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Supplies */
|
||||
|
|
|
@ -820,10 +820,10 @@ struct da7219_priv {
|
|||
struct mutex pll_lock;
|
||||
|
||||
#ifdef CONFIG_COMMON_CLK
|
||||
struct clk_hw dai_clks_hw;
|
||||
struct clk_hw dai_clks_hw[DA7219_DAI_NUM_CLKS];
|
||||
#endif
|
||||
struct clk_lookup *dai_clks_lookup;
|
||||
struct clk *dai_clks;
|
||||
struct clk_lookup *dai_clks_lookup[DA7219_DAI_NUM_CLKS];
|
||||
struct clk *dai_clks[DA7219_DAI_NUM_CLKS];
|
||||
|
||||
struct clk *mclk;
|
||||
unsigned int mclk_rate;
|
||||
|
|
|
@ -43,6 +43,7 @@ struct es8316_priv {
|
|||
unsigned int sysclk;
|
||||
unsigned int allowed_rates[NR_SUPPORTED_MCLK_LRCK_RATIOS];
|
||||
struct snd_pcm_hw_constraint_list sysclk_constraints;
|
||||
bool jd_inverted;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -577,6 +578,9 @@ static irqreturn_t es8316_irq(int irq, void *data)
|
|||
if (!es8316->jack)
|
||||
goto out;
|
||||
|
||||
if (es8316->jd_inverted)
|
||||
flags ^= ES8316_GPIO_FLAG_HP_NOT_INSERTED;
|
||||
|
||||
dev_dbg(comp->dev, "gpio flags %#04x\n", flags);
|
||||
if (flags & ES8316_GPIO_FLAG_HP_NOT_INSERTED) {
|
||||
/* Jack removed, or spurious IRQ? */
|
||||
|
@ -592,6 +596,8 @@ static irqreturn_t es8316_irq(int irq, void *data)
|
|||
/* Jack inserted, determine type */
|
||||
es8316_enable_micbias_for_mic_gnd_short_detect(comp);
|
||||
regmap_read(es8316->regmap, ES8316_GPIO_FLAG, &flags);
|
||||
if (es8316->jd_inverted)
|
||||
flags ^= ES8316_GPIO_FLAG_HP_NOT_INSERTED;
|
||||
dev_dbg(comp->dev, "gpio flags %#04x\n", flags);
|
||||
if (flags & ES8316_GPIO_FLAG_HP_NOT_INSERTED) {
|
||||
/* Jack unplugged underneath us */
|
||||
|
@ -633,6 +639,14 @@ static void es8316_enable_jack_detect(struct snd_soc_component *component,
|
|||
{
|
||||
struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
|
||||
|
||||
/*
|
||||
* Init es8316->jd_inverted here and not in the probe, as we cannot
|
||||
* guarantee that the bytchr-es8316 driver, which might set this
|
||||
* property, will probe before us.
|
||||
*/
|
||||
es8316->jd_inverted = device_property_read_bool(component->dev,
|
||||
"everest,jack-detect-inverted");
|
||||
|
||||
mutex_lock(&es8316->lock);
|
||||
|
||||
es8316->jack = jack;
|
||||
|
|
|
@ -328,6 +328,12 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component)
|
|||
dev_err(&hdev->dev, "failed to create hda codec %d\n", ret);
|
||||
goto error_no_pm;
|
||||
}
|
||||
/*
|
||||
* Overwrite type to HDA_DEV_ASOC since it is a ASoC driver
|
||||
* hda_codec.c will check this flag to determine if unregister
|
||||
* device is needed.
|
||||
*/
|
||||
hdev->type = HDA_DEV_ASOC;
|
||||
|
||||
/*
|
||||
* snd_hda_codec_device_new decrements the usage count so call get pm
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue