sound updates for 5.1

We had again a busy development cycle with many new drivers as well as
 lots of core improvements / cleanups.  Let's go for highlights:
 
 ALSA core:
 - PCM locking scheme was refactored for reducing a global rwlock
 - PCM suspend is handled in the device type PM ops now; lots of
   explicit calls were reduced by this action
 - Cleanups about PCM buffer preallocation calls
 - Kill NULL device object in memory allocations
 - Lots of procfs API cleanups
 
 ASoC core:
 - Support for only powering up channels that are actively being used
 - Cleanups / fixes of topology API
 
 ASoC drivers:
 - MediaTek BTCVSD for a Bluetooth radio chip, which is the first such
   driver we've had upstream!
 - Quite a few improvements to simplify the generic card drivers,
   especially the merge of the SCU cards into the main generic drivers
 - Lots of fixes for probing on Intel systems to follow more standard
   styles
 - A big refresh and cleanup of the Samsung drivers
 - New drivers: Asahi Kasei Microdevices AK4497, Cirrus Logic CS4341
   and CS35L26, Google ChromeOS embedded controllers, Ingenic JZ4725B,
   MediaTek BTCVSD, MT8183 and MT6358, NXP MICFIL, Rockchip RK3328,
   Spreadtrum DMA controllers, Qualcomm WCD9335, Xilinx S/PDIF and PCM
   formatters
 
 ALSA drivers:
 - Improvements of Tegra HD-audio controller driver for supporting new
   chips
 - HD-audio codec quirks for ALC294 S4 resume, ASUS laptop, Chrome
   headset button support and Dell workstations
 - Improved DSD support on USB-audio
 - Quirk for MOTU MicroBook II USB-audio
 - Support for Fireface UCX support and Solid State Logic Duende
   Classic/Mini
 -----BEGIN PGP SIGNATURE-----
 
 iQJCBAABCAAsFiEEIXTw5fNLNI7mMiVaLtJE4w1nLE8FAlx5GAUOHHRpd2FpQHN1
 c2UuZGUACgkQLtJE4w1nLE9yVA/+LYkM9fkd8BkRvnUxDPQTupBtr5tXjVPC7NHX
 WEZU+My5bxFvmF0tkV0IeBjzTNhR+ND1HHa4EcJPaV26UX+BBSyM2q8EhPxXJ3Ly
 o2d6bRYIvyDMEShyzZAlDegZ45LiP9Re7xwfVtA1gFVwCxXeWRXyz8Jj9FjUACVx
 1dd74wsVvPwt3uvGnJTEDbtpxA7lLCnVmco0PcXmkBIE0f99EzF/9xGs56UUKrlJ
 UM+KRH6H5fskESSUdR9MSoOi6Vw794rMNrFQIx9yb6/JHfq1Q5cyxQSx/E+7AMaU
 0QrQbKYDRVhrw8P2mVYXvbaZe2SdLMJdOKGXQMdYXpcUgSD0MBnc/O4HCn9HnT89
 M4QjoqSWYgipnLHf+O6JfrojF39+TrQ8LQWQZSNDYijOcQvaXAgK+vXmYOSQSQs2
 H8fljEHXAIjDOhlBd0J4b9gAn21ijjZUSRBAWkNS0Sp0qBu0WcbBWLph/zHqjd7J
 UvTILjpKegd28yrsudLXRL73v3fJoWXM7wwrjnGdmi93FhqCNdJ9co/03JNRSlQ9
 sFM6YBj6pyg4QkNTpPwZw5nn48uBsbHZRU8la7Vfp0c+XYPkHKR5EwQQ0YoajZhW
 e2SSeMUVCHGmIAoqweAxakU4zCDvv4wLbnjJRi/BXIh17XrhG4AAiRrYZOYaOCwO
 Ku/x7dI=
 =Vh8O
 -----END PGP SIGNATURE-----

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

Pull sound updates from Takashi Iwai:
 "We had again a busy development cycle with many new drivers as well as
  lots of core improvements / cleanups. Let's go for highlights:

  ALSA core:

   - PCM locking scheme was refactored for reducing a global rwlock

   - PCM suspend is handled in the device type PM ops now; lots of
     explicit calls were reduced by this action

   - Cleanups about PCM buffer preallocation calls

   - Kill NULL device object in memory allocations

   - Lots of procfs API cleanups

  ASoC core:

   - Support for only powering up channels that are actively being used

   - Cleanups / fixes of topology API

  ASoC drivers:

   - MediaTek BTCVSD for a Bluetooth radio chip, which is the first such
     driver we've had upstream!

   - Quite a few improvements to simplify the generic card drivers,
     especially the merge of the SCU cards into the main generic drivers

   - Lots of fixes for probing on Intel systems to follow more standard
     styles

   - A big refresh and cleanup of the Samsung drivers

   - New drivers: Asahi Kasei Microdevices AK4497, Cirrus Logic CS4341
     and CS35L26, Google ChromeOS embedded controllers, Ingenic JZ4725B,
     MediaTek BTCVSD, MT8183 and MT6358, NXP MICFIL, Rockchip RK3328,
     Spreadtrum DMA controllers, Qualcomm WCD9335, Xilinx S/PDIF and PCM
     formatters

  ALSA drivers:

   - Improvements of Tegra HD-audio controller driver for supporting new
     chips

   - HD-audio codec quirks for ALC294 S4 resume, ASUS laptop, Chrome
     headset button support and Dell workstations

   - Improved DSD support on USB-audio

   - Quirk for MOTU MicroBook II USB-audio

   - Support for Fireface UCX support and Solid State Logic Duende
     Classic/Mini"

* tag 'sound-5.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (461 commits)
  ALSA: usb-audio: Add quirk for MOTU MicroBook II
  ASoC: stm32: i2s: skip useless write in slave mode
  ASoC: stm32: i2s: fix race condition in irq handler
  ASoC: stm32: i2s: remove useless callback
  ASoC: stm32: i2s: fix dma configuration
  ASoC: stm32: i2s: fix stream count management
  ASoC: stm32: i2s: fix 16 bit format support
  ASoC: stm32: i2s: fix IRQ clearing
  ASoC: qcom: Kconfig: fix dependency for sdm845
  ASoC: Intel: Boards: Add Maxim98373 support
  ASoC: rsnd: gen: fix SSI9 4/5/6/7 busif related register address
  ALSA: firewire-motu: fix construction of PCM frame for capture direction
  ALSA: bebob: use more identical mod_alias for Saffire Pro 10 I/O against Liquid Saffire 56
  ALSA: hda: Extend i915 component bind timeout
  ASoC: wm_adsp: Improve logging messages
  ASoC: wm_adsp: Add support for multiple compressed buffers
  ASoC: wm_adsp: Refactor compress stream initialisation
  ASoC: wm_adsp: Reorder some functions for improved clarity
  ASoC: wm_adsp: Factor out stripping padding from ADSP data
  ASoC: cs35l36: Fix an IS_ERR() vs NULL checking bug
  ...
This commit is contained in:
Linus Torvalds 2019-03-06 14:10:46 -08:00
commit da2577fe63
412 changed files with 34407 additions and 6021 deletions

View File

@ -17,12 +17,18 @@ Required properties:
Documentation/devicetree/bindings/regulator/regulator.txt Documentation/devicetree/bindings/regulator/regulator.txt
Optional properties: Optional properties:
- reset-gpio: the reset pin for the chip, for more details consult - reset-gpios: the reset pin for the chip, for more details consult
Documentation/devicetree/bindings/gpio/gpio.txt Documentation/devicetree/bindings/gpio/gpio.txt
- DVDD-supply: supply voltage for the digital core, please consult - DVDD-supply: supply voltage for the digital core, please consult
Documentation/devicetree/bindings/regulator/regulator.txt Documentation/devicetree/bindings/regulator/regulator.txt
- adi,micbias: configures the voltage setting for the MICBIAS pin.
Select 0/1/2/3/4/5/6/7/8 to specify MICBIAS voltage
5V/5.5V/6V/6.5V/7V/7.5V/8V/8.5V/9V
If not specified the default value will be "7" meaning 8.5 Volts.
This property is only valid for the ADAU1977
For required properties on SPI, please consult For required properties on SPI, please consult
Documentation/devicetree/bindings/spi/spi-bus.txt Documentation/devicetree/bindings/spi/spi-bus.txt
@ -40,7 +46,8 @@ Examples:
AVDD-supply = <&regulator>; AVDD-supply = <&regulator>;
DVDD-supply = <&regulator_digital>; DVDD-supply = <&regulator_digital>;
reset_gpio = <&gpio 10 GPIO_ACTIVE_LOW>; adi,micbias = <3>;
reset-gpios = <&gpio 10 GPIO_ACTIVE_LOW>;
}; };
adau1977_i2c: adau1977@11 { adau1977_i2c: adau1977@11 {
@ -50,5 +57,5 @@ Examples:
AVDD-supply = <&regulator>; AVDD-supply = <&regulator>;
DVDD-supply = <&regulator_digital>; DVDD-supply = <&regulator_digital>;
reset_gpio = <&gpio 10 GPIO_ACTIVE_LOW>; reset-gpios = <&gpio 10 GPIO_ACTIVE_LOW>;
}; };

View File

@ -4,7 +4,7 @@ This device supports I2C mode.
Required properties: Required properties:
- compatible : "asahi-kasei,ak4458" - compatible : "asahi-kasei,ak4458" or "asahi-kasei,ak4497"
- reg : The I2C address of the device for I2C - reg : The I2C address of the device for I2C
Optional properties: Optional properties:

View File

@ -1,123 +0,0 @@
Audio-Graph-SCU-Card:
Audio-Graph-SCU-Card is "Audio-Graph-Card" + "ALSA DPCM".
It is based on common bindings for device graphs.
see ${LINUX}/Documentation/devicetree/bindings/graph.txt
Basically, Audio-Graph-SCU-Card property is same as
Simple-Card / Simple-SCU-Card / Audio-Graph-Card.
see ${LINUX}/Documentation/devicetree/bindings/sound/simple-card.txt
${LINUX}/Documentation/devicetree/bindings/sound/simple-scu-card.txt
${LINUX}/Documentation/devicetree/bindings/sound/audio-graph-card.txt
Below are same as Simple-Card / Audio-Graph-Card.
- label
- dai-format
- frame-master
- bitclock-master
- bitclock-inversion
- frame-inversion
- dai-tdm-slot-num
- dai-tdm-slot-width
- clocks / system-clock-frequency
Below are same as Simple-SCU-Card.
- convert-rate
- convert-channels
- prefix
- routing
Required properties:
- compatible : "audio-graph-scu-card";
- dais : list of CPU DAI port{s}
Example 1. Sampling Rate Conversion
sound_card {
compatible = "audio-graph-scu-card";
label = "sound-card";
prefix = "codec";
routing = "codec Playback", "DAI0 Playback",
"DAI0 Capture", "codec Capture";
convert-rate = <48000>;
dais = <&cpu_port>;
};
audio-codec {
...
port {
codec_endpoint: endpoint {
remote-endpoint = <&cpu_endpoint>;
};
};
};
dai-controller {
...
cpu_port: port {
cpu_endpoint: endpoint {
remote-endpoint = <&codec_endpoint>;
dai-format = "left_j";
...
};
};
};
Example 2. 2 CPU 1 Codec (Mixing)
sound_card {
compatible = "audio-graph-scu-card";
label = "sound-card";
routing = "codec Playback", "DAI0 Playback",
"codec Playback", "DAI1 Playback",
"DAI0 Capture", "codec Capture";
dais = <&cpu_port0
&cpu_port1>;
};
audio-codec {
...
audio-graph-card,prefix = "codec";
audio-graph-card,convert-rate = <48000>;
port {
codec_endpoint0: endpoint {
remote-endpoint = <&cpu_endpoint0>;
};
codec_endpoint1: endpoint {
remote-endpoint = <&cpu_endpoint1>;
};
};
};
dai-controller {
...
ports {
cpu_port0: port {
cpu_endpoint0: endpoint {
remote-endpoint = <&codec_endpoint0>;
dai-format = "left_j";
...
};
};
cpu_port1: port {
cpu_endpoint1: endpoint {
remote-endpoint = <&codec_endpoint1>;
dai-format = "left_j";
...
};
};
};
};

View File

@ -0,0 +1,168 @@
CS35L36 Speaker Amplifier
Required properties:
- compatible : "cirrus,cs35l36"
- reg : the I2C address of the device for I2C
- VA-supply, VP-supply : power supplies for the device,
as covered in
Documentation/devicetree/bindings/regulator/regulator.txt.
- cirrus,boost-ctl-millivolt : Boost Voltage Value. Configures the boost
converter's output voltage in mV. The range is from 2550mV to 12000mV with
increments of 50mV.
(Default) VP
- cirrus,boost-peak-milliamp : Boost-converter peak current limit in mA.
Configures the peak current by monitoring the current through the boost FET.
Range starts at 1600mA and goes to a maximum of 4500mA with increments of
50mA.
(Default) 4.50 Amps
- cirrus,boost-ind-nanohenry : Inductor estimation LBST reference value.
Seeds the digital boost converter's inductor estimation block with the initial
inductance value to reference.
1000 = 1uH (Default)
1200 = 1.2uH
Optional properties:
- cirrus,multi-amp-mode : Boolean to determine if there are more than
one amplifier in the system. If more than one it is best to Hi-Z the ASP
port to prevent bus contention on the output signal
- cirrus,boost-ctl-select : Boost conerter control source selection.
Selects the source of the BST_CTL target VBST voltage for the boost
converter to generate.
0x00 - Control Port Value
0x01 - Class H Tracking (Default)
0x10 - MultiDevice Sync Value
- cirrus,amp-pcm-inv : Boolean to determine Amplifier will invert incoming
PCM data
- cirrus,imon-pol-inv : Boolean to determine Amplifier will invert the
polarity of outbound IMON feedback data
- cirrus,vmon-pol-inv : Boolean to determine Amplifier will invert the
polarity of outbound VMON feedback data
- cirrus,dcm-mode-enable : Boost converter automatic DCM Mode enable.
This enables the digital boost converter to operate in a low power
(Discontinuous Conduction) mode during low loading conditions.
- cirrus,weak-fet-disable : Boolean : The strength of the output drivers is
reduced when operating in a Weak-FET Drive Mode and must not be used to drive
a large load.
- cirrus,classh-wk-fet-delay : Weak-FET entry delay. Controls the delay
(in ms) before the Class H algorithm switches to the weak-FET voltage
(after the audio falls and remains below the value specified in WKFET_AMP_THLD).
0 = 0ms
1 = 5ms
2 = 10ms
3 = 50ms
4 = 100ms (Default)
5 = 200ms
6 = 500ms
7 = 1000ms
- cirrus,classh-weak-fet-thld-millivolt : Weak-FET amplifier drive threshold.
Configures the signal threshold at which the PWM output stage enters
weak-FET operation. The range is 50mV to 700mV in 50mV increments.
- cirrus,temp-warn-threshold : Amplifier overtemperature warning threshold.
Configures the threshold at which the overtemperature warning condition occurs.
When the threshold is met, the overtemperature warning attenuation is applied
and the TEMP_WARN_EINT interrupt status bit is set.
If TEMP_WARN_MASK = 0, INTb is asserted.
0 = 105C
1 = 115C
2 = 125C (Default)
3 = 135C
- cirrus,irq-drive-select : Selects the driver type of the selected interrupt
output.
0 = Open-drain
1 = Push-pull (Default)
- cirrus,irq-gpio-select : Selects the pin to serve as the programmable
interrupt output.
0 = PDM_DATA / SWIRE_SD / INT (Default)
1 = GPIO
Optional properties for the "cirrus,vpbr-config" Sub-node
- cirrus,vpbr-en : VBST brownout prevention enable. Configures whether the
VBST brownout prevention algorithm is enabled or disabled.
0 = VBST brownout prevention disabled (default)
1 = VBST brownout prevention enabled
See Section 7.31.1 VPBR Config for configuration options & further details
- cirrus,vpbr-thld : Initial VPBR threshold. Configures the VP brownout
threshold voltage
- cirrus,cirrus,vpbr-atk-rate : Attenuation attack step rate. Configures the
amount delay between consecutive volume attenuation steps when a brownout
condition is present and the VP brownout condition is in an attacking state.
- cirrus,vpbr-atk-vol : VP brownout prevention step size. Configures the VP
brownout prevention attacking attenuation step size when operating in either
digital volume or analog gain modes.
- cirrus,vpbr-max-attn : Maximum attenuation that the VP brownout prevention
can apply to the audio signal.
- cirrus,vpbr-wait : Configures the delay time between a brownout condition
no longer being present and the VP brownout prevention entering an attenuation
release state.
- cirrus,vpbr-rel-rate : Attenuation release step rate. Configures the delay
between consecutive volume attenuation release steps when a brownout condition
is not longer present and the VP brownout is in an attenuation release state.
- cirrus,vpbr-mute-en : During the attack state, if the vpbr-max-attn value
is reached, the error condition still remains, and this bit is set, the audio
is muted.
Example:
cs35l36: cs35l36@40 {
compatible = "cirrus,cs35l36";
reg = <0x40>;
VA-supply = <&dummy_vreg>;
VP-supply = <&dummy_vreg>;
reset-gpios = <&gpio0 54 0>;
interrupt-parent = <&gpio8>;
interrupts = <3 IRQ_TYPE_LEVEL_LOW>;
cirrus,boost-ind-nanohenry = <1000>;
cirrus,boost-ctl-millivolt = <10000>;
cirrus,boost-peak-milliamp = <4500>;
cirrus,boost-ctl-select = <0x00>;
cirrus,weak-fet-delay = <0x04>;
cirrus,weak-fet-thld = <0x01>;
cirrus,temp-warn-threshold = <0x01>;
cirrus,multi-amp-mode;
cirrus,irq-drive-select = <0x01>;
cirrus,irq-gpio-select = <0x01>;
cirrus,vpbr-config {
cirrus,vpbr-en = <0x00>;
cirrus,vpbr-thld = <0x05>;
cirrus,vpbr-atk-rate = <0x02>;
cirrus,vpbr-atk-vol = <0x01>;
cirrus,vpbr-max-attn = <0x09>;
cirrus,vpbr-wait = <0x01>;
cirrus,vpbr-rel-rate = <0x05>;
cirrus,vpbr-mute-en = <0x00>;
};
};

View File

@ -0,0 +1,22 @@
Cirrus Logic CS4341 audio DAC
This device supports both I2C and SPI (configured with pin strapping
on the board).
Required properties:
- compatible: "cirrus,cs4341a"
- reg : the I2C address of the device for I2C, the chip select
number for SPI.
For required properties on I2C-bus, please consult
Documentation/devicetree/bindings/i2c/i2c.txt
For required properties on SPI-bus, please consult
Documentation/devicetree/bindings/spi/spi-bus.txt
Example:
codec: cs4341@0 {
#sound-dai-cells = <0>;
compatible = "cirrus,cs4341a";
reg = <0>;
spi-max-frequency = <6000000>;
};

View File

@ -45,6 +45,23 @@ Optional properties:
- fck_parent : Should contain a valid clock name which will be used as parent - fck_parent : Should contain a valid clock name which will be used as parent
for the McASP fck for the McASP fck
Optional GPIO support:
If any McASP pin need to be used as GPIO then the McASP node must have:
...
gpio-controller
#gpio-cells = <2>;
...
When requesting a GPIO, the first parameter is the PIN index in McASP_P*
registers.
For example to request the AXR2 pin of mcasp8:
function-gpios = <&mcasp8 2 0>;
Or to request the ACLKR pin of mcasp8:
function-gpios = <&mcasp8 29 0>;
For generic gpio information, please refer to bindings/gpio/gpio.txt
Example: Example:
mcasp0: mcasp0@1d00000 { mcasp0: mcasp0@1d00000 {

View File

@ -0,0 +1,32 @@
NXP MICFIL Digital Audio Interface (MICFIL).
The MICFIL digital interface provides a 16-bit audio signal from a PDM
microphone bitstream in a configurable output sampling rate.
Required properties:
- compatible : Compatible list, contains "fsl,imx8mm-micfil"
- reg : Offset and length of the register set for the device.
- interrupts : Contains the micfil interrupts.
- clocks : Must contain an entry for each entry in clock-names.
- clock-names : Must include the "ipg_clk" for register access and
"ipg_clk_app" for internal micfil clock.
- dmas : Generic dma devicetree binding as described in
Documentation/devicetree/bindings/dma/dma.txt.
Example:
micfil: micfil@30080000 {
compatible = "fsl,imx8mm-micfil";
reg = <0x0 0x30080000 0x0 0x10000>;
interrupts = <GIC_SPI 109 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk IMX8MM_CLK_PDM_IPG>,
<&clk IMX8MM_CLK_PDM_ROOT>;
clock-names = "ipg_clk", "ipg_clk_app";
dmas = <&sdma2 24 26 0x80000000>;
};

View File

@ -0,0 +1,26 @@
* Audio codec controlled by ChromeOS EC
Google's ChromeOS EC codec is a digital mic codec provided by the
Embedded Controller (EC) and is controlled via a host-command interface.
An EC codec node should only be found as a sub-node of the EC node (see
Documentation/devicetree/bindings/mfd/cros-ec.txt).
Required properties:
- compatible: Must contain "google,cros-ec-codec"
- #sound-dai-cells: Should be 1. The cell specifies number of DAIs.
- max-dmic-gain: A number for maximum gain in dB on digital microphone.
Example:
cros-ec@0 {
compatible = "google,cros-ec-spi";
...
cros_ec_codec: ec-codec {
compatible = "google,cros-ec-codec";
#sound-dai-cells = <1>;
max-dmic-gain = <43>;
};
};

View File

@ -0,0 +1,20 @@
Ingenic JZ4725B codec controller
Required properties:
- compatible : "ingenic,jz4725b-codec"
- reg : codec registers location and length
- clocks : phandle to the AIC clock.
- clock-names: must be set to "aic".
- #sound-dai-cells: Must be set to 0.
Example:
codec: audio-codec@100200a4 {
compatible = "ingenic,jz4725b-codec";
reg = <0x100200a4 0x8>;
#sound-dai-cells = <0>;
clocks = <&cgu JZ4725B_CLK_AIC>;
clock-names = "aic";
};

View File

@ -0,0 +1,20 @@
Ingenic JZ4740 codec controller
Required properties:
- compatible : "ingenic,jz4740-codec"
- reg : codec registers location and length
- clocks : phandle to the AIC clock.
- clock-names: must be set to "aic".
- #sound-dai-cells: Must be set to 0.
Example:
codec: audio-codec@10020080 {
compatible = "ingenic,jz4740-codec";
reg = <0x10020080 0x8>;
#sound-dai-cells = <0>;
clocks = <&cgu JZ4740_CLK_AIC>;
clock-names = "aic";
};

View File

@ -0,0 +1,18 @@
Mediatek MT6358 Audio Codec
The communication between MT6358 and SoC is through Mediatek PMIC wrapper.
For more detail, please visit Mediatek PMIC wrapper documentation.
Must be a child node of PMIC wrapper.
Required properties:
- compatible : "mediatek,mt6358-sound".
- Avdd-supply : power source of AVDD
Example:
mt6358_snd {
compatible = "mediatek,mt6358-sound";
Avdd-supply = <&mt6358_vaud28_reg>;
};

View File

@ -0,0 +1,36 @@
Mediatek AFE PCM controller for mt8183
Required properties:
- compatible = "mediatek,mt68183-audio";
- reg: register location and size
- interrupts: should contain AFE interrupt
- power-domains: should define the power domain
- clocks: Must contain an entry for each entry in clock-names
- clock-names: should have these clock names:
"infra_sys_audio_clk",
"mtkaif_26m_clk",
"top_mux_audio",
"top_mux_aud_intbus",
"top_sys_pll3_d4",
"top_clk26m_clk";
Example:
afe: mt8183-afe-pcm@11220000 {
compatible = "mediatek,mt8183-audio";
reg = <0 0x11220000 0 0x1000>;
interrupts = <GIC_SPI 161 IRQ_TYPE_LEVEL_LOW>;
power-domains = <&scpsys MT8183_POWER_DOMAIN_AUDIO>;
clocks = <&infrasys CLK_INFRA_AUDIO>,
<&infrasys CLK_INFRA_AUDIO_26M_BCLK>,
<&topckgen CLK_TOP_MUX_AUDIO>,
<&topckgen CLK_TOP_MUX_AUD_INTBUS>,
<&topckgen CLK_TOP_SYSPLL_D2_D4>,
<&clk26m>;
clock-names = "infra_sys_audio_clk",
"mtkaif_26m_clk",
"top_mux_audio",
"top_mux_aud_intbus",
"top_sys_pll_d2_d4",
"top_clk26m_clk";
};

View File

@ -0,0 +1,24 @@
Mediatek ALSA BT SCO CVSD/MSBC Driver
Required properties:
- compatible = "mediatek,mtk-btcvsd-snd";
- reg: register location and size of PKV and SRAM_BANK2
- interrupts: should contain BTSCO interrupt
- mediatek,infracfg: the phandles of INFRASYS
- mediatek,offset: Array contains of register offset and mask
infra_misc_offset,
infra_conn_bt_cvsd_mask,
cvsd_mcu_read_offset,
cvsd_mcu_write_offset,
cvsd_packet_indicator_offset
Example:
mtk-btcvsd-snd@18000000 {
compatible = "mediatek,mtk-btcvsd-snd";
reg=<0 0x18000000 0 0x1000>,
<0 0x18080000 0 0x8000>;
interrupts = <GIC_SPI 286 IRQ_TYPE_LEVEL_LOW>;
mediatek,infracfg = <&infrasys>;
mediatek,offset = <0xf00 0x800 0xfd0 0xfd4 0xfd8>;
};

View File

@ -13,6 +13,10 @@ Required properties:
See ../reset/reset.txt for details. See ../reset/reset.txt for details.
- reset-names : Must include the following entries: hda, hda2hdmi, hda2codec_2x - reset-names : Must include the following entries: hda, hda2hdmi, hda2codec_2x
Optional properties:
- nvidia,model : The user-visible name of this sound complex. Since the property
is optional, legacy boards can use default name provided in hda driver.
Example: Example:
hda@70030000 { hda@70030000 {
@ -27,4 +31,5 @@ hda@70030000 {
<&tegra_car 128>, /* hda2hdmi */ <&tegra_car 128>, /* hda2hdmi */
<&tegra_car 111>; /* hda2codec_2x */ <&tegra_car 111>; /* hda2codec_2x */
reset-names = "hda", "hda2hdmi", "hda2codec_2x"; reset-names = "hda", "hda2hdmi", "hda2codec_2x";
nvidia,model = "jetson-tk1-hda";
}; };

View File

@ -30,6 +30,7 @@ Required properties
- vdd-cdc-io-supply: phandle to VDD_CDC_IO regulator DT node. - vdd-cdc-io-supply: phandle to VDD_CDC_IO regulator DT node.
- vdd-cdc-tx-rx-cx-supply: phandle to VDD_CDC_TX/RX/CX regulator DT node. - vdd-cdc-tx-rx-cx-supply: phandle to VDD_CDC_TX/RX/CX regulator DT node.
- vdd-micbias-supply: phandle of VDD_MICBIAS supply's regulator DT node. - vdd-micbias-supply: phandle of VDD_MICBIAS supply's regulator DT node.
Optional Properties: Optional Properties:
- qcom,mbhc-vthreshold-low: Array of 5 threshold voltages in mV for 5 buttons - qcom,mbhc-vthreshold-low: Array of 5 threshold voltages in mV for 5 buttons
detection on headset when the mbhc is powered up detection on headset when the mbhc is powered up
@ -92,9 +93,9 @@ spmi_bus {
"cdc_ear_cnp_int", "cdc_ear_cnp_int",
"cdc_hphr_cnp_int", "cdc_hphr_cnp_int",
"cdc_hphl_cnp_int"; "cdc_hphl_cnp_int";
VDD-CDC-IO-supply = <&pm8916_l5>; vdd-cdc-io-supply = <&pm8916_l5>;
VDD-CDC-TX-RX-CX-supply = <&pm8916_l5>; vdd-cdc-tx-rx-cx-supply = <&pm8916_l5>;
VDD-MICBIAS-supply = <&pm8916_l13>; vdd-micbias-supply = <&pm8916_l13>;
#sound-dai-cells = <1>; #sound-dai-cells = <1>;
}; };
}; };

View File

@ -34,12 +34,12 @@ Required properties with SLIMbus Interface:
Definition: Interrupt names of WCD INTR1 and INTR2 Definition: Interrupt names of WCD INTR1 and INTR2
Should be: "intr1", "intr2" Should be: "intr1", "intr2"
- reset-gpio: - reset-gpios:
Usage: required Usage: required
Value type: <String Array> Value type: <String Array>
Definition: Reset gpio line Definition: Reset gpio line
- qcom,ifd: - slim-ifc-dev:
Usage: required Usage: required
Value type: <phandle> Value type: <phandle>
Definition: SLIM interface device Definition: SLIM interface device
@ -104,13 +104,13 @@ Required properties with SLIMbus Interface:
Value type: <u32> Value type: <u32>
Definition: Must be 1 Definition: Must be 1
codec@1{ audio-codec@1{
compatible = "slim217,1a0"; compatible = "slim217,1a0";
reg = <1 0>; reg = <1 0>;
interrupts = <&msmgpio 54 IRQ_TYPE_LEVEL_HIGH>; interrupts = <&msmgpio 54 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "intr2" interrupt-names = "intr2"
reset-gpio = <&msmgpio 64 0>; reset-gpios = <&msmgpio 64 0>;
qcom,ifd = <&wc9335_ifd>; slim-ifc-dev = <&wc9335_ifd>;
clock-names = "mclk", "native"; clock-names = "mclk", "native";
clocks = <&rpmcc RPM_SMD_DIV_CLK1>, clocks = <&rpmcc RPM_SMD_DIV_CLK1>,
<&rpmcc RPM_SMD_BB_CLK1>; <&rpmcc RPM_SMD_BB_CLK1>;

View File

@ -0,0 +1,23 @@
* Rockchip Rk3328 internal codec
Required properties:
- compatible: "rockchip,rk3328-codec"
- reg: physical base address of the controller and length of memory mapped
region.
- rockchip,grf: the phandle of the syscon node for GRF register.
- clocks: a list of phandle + clock-specifer pairs, one for each entry in clock-names.
- clock-names: should be "pclk".
- spk-depop-time-ms: speak depop time msec.
Example for rk3328 internal codec:
codec: codec@ff410000 {
compatible = "rockchip,rk3328-codec";
reg = <0x0 0xff410000 0x0 0x1000>;
rockchip,grf = <&grf>;
clocks = <&cru PCLK_ACODEC>;
clock-names = "pclk";
spk-depop-time-ms = 100;
status = "disabled";
};

View File

@ -37,6 +37,15 @@ VDDIO 1.8V 2.5V 3.3V
2 = 3.33 mA 5.74 mA 8.03 mA 2 = 3.33 mA 5.74 mA 8.03 mA
3 = 4.99 mA 8.61 mA 12.05 mA 3 = 4.99 mA 8.61 mA 12.05 mA
- sclk-strength: the SCLK pad strength. Possible values are:
0, 1, 2 and 3 as per the table below:
VDDIO 1.8V 2.5V 3.3V
0 = Disable
1 = 1.66 mA 2.87 mA 4.02 mA
2 = 3.33 mA 5.74 mA 8.03 mA
3 = 4.99 mA 8.61 mA 12.05 mA
Example: Example:
sgtl5000: codec@a { sgtl5000: codec@a {

View File

@ -1,94 +0,0 @@
ASoC Simple SCU Sound Card
Simple SCU Sound Card is "Simple Sound Card" + "ALSA DPCM".
For example, you can use this driver if you want to exchange sampling rate convert,
Mixing, etc...
Required properties:
- compatible : "simple-scu-audio-card"
"renesas,rsrc-card"
Optional properties:
- simple-audio-card,name : see simple-audio-card.txt
- simple-audio-card,cpu : see simple-audio-card.txt
- simple-audio-card,codec : see simple-audio-card.txt
Optional subnode properties:
- simple-audio-card,format : see simple-audio-card.txt
- simple-audio-card,frame-master : see simple-audio-card.txt
- simple-audio-card,bitclock-master : see simple-audio-card.txt
- simple-audio-card,bitclock-inversion : see simple-audio-card.txt
- simple-audio-card,frame-inversion : see simple-audio-card.txt
- simple-audio-card,convert-rate : platform specified sampling rate convert
- simple-audio-card,convert-channels : platform specified converted channel size (2 - 8 ch)
- simple-audio-card,prefix : see routing
- simple-audio-card,widgets : Please refer to widgets.txt.
- simple-audio-card,routing : A list of the connections between audio components.
Each entry is a pair of strings, the first being the connection's sink,
the second being the connection's source. Valid names for sources.
use audio-prefix if some components is using same sink/sources naming.
it can be used if compatible was "renesas,rsrc-card";
Required CPU/CODEC subnodes properties:
- sound-dai : see simple-audio-card.txt
Optional CPU/CODEC subnodes properties:
- clocks / system-clock-frequency : see simple-audio-card.txt
Example 1. Sampling Rate Conversion
sound {
compatible = "simple-scu-audio-card";
simple-audio-card,name = "rsnd-ak4643";
simple-audio-card,format = "left_j";
simple-audio-card,bitclock-master = <&sndcodec>;
simple-audio-card,frame-master = <&sndcodec>;
simple-audio-card,convert-rate = <48000>;
simple-audio-card,prefix = "ak4642";
simple-audio-card,routing = "ak4642 Playback", "DAI0 Playback",
"DAI0 Capture", "ak4642 Capture";
sndcpu: simple-audio-card,cpu {
sound-dai = <&rcar_sound>;
};
sndcodec: simple-audio-card,codec {
sound-dai = <&ak4643>;
system-clock-frequency = <11289600>;
};
};
Example 2. 2 CPU 1 Codec (Mixing)
sound {
compatible = "simple-scu-audio-card";
simple-audio-card,name = "rsnd-ak4643";
simple-audio-card,format = "left_j";
simple-audio-card,bitclock-master = <&dpcmcpu>;
simple-audio-card,frame-master = <&dpcmcpu>;
simple-audio-card,routing = "ak4642 Playback", "DAI0 Playback",
"ak4642 Playback", "DAI1 Playback";
dpcmcpu: cpu@0 {
sound-dai = <&rcar_sound 0>;
};
cpu@1 {
sound-dai = <&rcar_sound 1>;
};
codec {
prefix = "ak4642";
sound-dai = <&ak4643>;
clocks = <&audio_clock>;
};
};

View File

@ -0,0 +1,23 @@
* Spreadtrum DMA platfrom bindings
Required properties:
- compatible: Should be "sprd,pcm-platform".
- dmas: Specify the list of DMA controller phandle and DMA request line ordered pairs.
- dma-names: Identifier string for each DMA request line in the dmas property.
These strings correspond 1:1 with the ordered pairs in dmas.
Example:
audio_platform:platform@0 {
compatible = "sprd,pcm-platform";
dmas = <&agcp_dma 1 1>, <&agcp_dma 2 2>,
<&agcp_dma 3 3>, <&agcp_dma 4 4>,
<&agcp_dma 5 5>, <&agcp_dma 6 6>,
<&agcp_dma 7 7>, <&agcp_dma 8 8>,
<&agcp_dma 9 9>, <&agcp_dma 10 10>;
dma-names = "normal_p_l", "normal_p_r",
"normal_c_l", "normal_c_r",
"voice_c", "fast_p",
"loop_c", "loop_p",
"voip_c", "voip_p";
};

View File

@ -0,0 +1,29 @@
Device-Tree bindings for Xilinx PL audio formatter
The IP core supports DMA, data formatting(AES<->PCM conversion)
of audio samples.
Required properties:
- compatible: "xlnx,audio-formatter-1.0"
- interrupt-names: Names specified to list of interrupts in same
order mentioned under "interrupts".
List of supported interrupt names are:
"irq_mm2s" : interrupt from MM2S block
"irq_s2mm" : interrupt from S2MM block
- interrupts-parent: Phandle for interrupt controller.
- interrupts: List of Interrupt numbers.
- reg: Base address and size of the IP core instance.
- clock-names: List of input clocks.
Required elements: "s_axi_lite_aclk", "aud_mclk"
- clocks: Input clock specifier. Refer to common clock bindings.
Example:
audio_ss_0_audio_formatter_0: audio_formatter@80010000 {
compatible = "xlnx,audio-formatter-1.0";
interrupt-names = "irq_mm2s", "irq_s2mm";
interrupt-parent = <&gic>;
interrupts = <0 104 4>, <0 105 4>;
reg = <0x0 0x80010000 0x0 0x1000>;
clock-names = "s_axi_lite_aclk", "aud_mclk";
clocks = <&clk 71>, <&clk_wiz_1 0>;
};

View File

@ -0,0 +1,28 @@
Device-Tree bindings for Xilinx SPDIF IP
The IP supports playback and capture of SPDIF audio
Required properties:
- compatible: "xlnx,spdif-2.0"
- clock-names: List of input clocks.
Required elements: "s_axi_aclk", "aud_clk_i"
- clocks: Input clock specifier. Refer to common clock bindings.
- reg: Base address and address length of the IP core instance.
- interrupts-parent: Phandle for interrupt controller.
- interrupts: List of Interrupt numbers.
- xlnx,spdif-mode: 0 :- receiver mode
1 :- transmitter mode
- xlnx,aud_clk_i: input audio clock value.
Example:
spdif_0: spdif@80010000 {
clock-names = "aud_clk_i", "s_axi_aclk";
clocks = <&misc_clk_0>, <&clk 71>;
compatible = "xlnx,spdif-2.0";
interrupt-names = "spdif_interrupt";
interrupt-parent = <&gic>;
interrupts = <0 91 4>;
reg = <0x0 0x80010000 0x0 0x10000>;
xlnx,spdif-mode = <1>;
xlnx,aud_clk_i = <49152913>;
};

View File

@ -254,10 +254,12 @@ alc274-dell-aio
ALC274 fixups on Dell AIO machines ALC274 fixups on Dell AIO machines
alc255-dummy-lineout alc255-dummy-lineout
Dell Precision 3930 fixups Dell Precision 3930 fixups
alc255-dell-headset"}, alc255-dell-headset
Dell Precision 3630 fixups Dell Precision 3630 fixups
alc295-hp-x360 alc295-hp-x360
HP Spectre X360 fixups HP Spectre X360 fixups
alc-sense-combo
Headset button support for Chrome platform
ALC66x/67x/892 ALC66x/67x/892
============== ==============

View File

@ -3520,14 +3520,14 @@ allocator will try to get an area as large as possible within the
given size. given size.
The second argument (type) and the third argument (device pointer) are The second argument (type) and the third argument (device pointer) are
dependent on the bus. In the case of the ISA bus, pass dependent on the bus. For normal devices, pass the device pointer
:c:func:`snd_dma_isa_data()` as the third argument with (typically identical as ``card->dev``) to the third argument with
``SNDRV_DMA_TYPE_DEV`` type. For the continuous buffer unrelated to the ``SNDRV_DMA_TYPE_DEV`` type. For the continuous buffer unrelated to the
bus can be pre-allocated with ``SNDRV_DMA_TYPE_CONTINUOUS`` type and the bus can be pre-allocated with ``SNDRV_DMA_TYPE_CONTINUOUS`` type and the
``snd_dma_continuous_data(GFP_KERNEL)`` device pointer, where ``snd_dma_continuous_data(GFP_KERNEL)`` device pointer, where
``GFP_KERNEL`` is the kernel allocation flag to use. For the PCI ``GFP_KERNEL`` is the kernel allocation flag to use. For the
scatter-gather buffers, use ``SNDRV_DMA_TYPE_DEV_SG`` with scatter-gather buffers, use ``SNDRV_DMA_TYPE_DEV_SG`` with the device
``snd_dma_pci_data(pci)`` (see the `Non-Contiguous Buffers`_ pointer (see the `Non-Contiguous Buffers`_
section). section).
Once the buffer is pre-allocated, you can use the allocator in the Once the buffer is pre-allocated, you can use the allocator in the
@ -3924,15 +3924,12 @@ The scheme of the real suspend job is as follows.
2. Call :c:func:`snd_power_change_state()` with 2. Call :c:func:`snd_power_change_state()` with
``SNDRV_CTL_POWER_D3hot`` to change the power status. ``SNDRV_CTL_POWER_D3hot`` to change the power status.
3. Call :c:func:`snd_pcm_suspend_all()` to suspend the running 3. If AC97 codecs are used, call :c:func:`snd_ac97_suspend()` for
PCM streams.
4. If AC97 codecs are used, call :c:func:`snd_ac97_suspend()` for
each codec. each codec.
5. Save the register values if necessary. 4. Save the register values if necessary.
6. Stop the hardware if necessary. 5. Stop the hardware if necessary.
A typical code would be like: A typical code would be like:
@ -3946,12 +3943,10 @@ A typical code would be like:
/* (2) */ /* (2) */
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
/* (3) */ /* (3) */
snd_pcm_suspend_all(chip->pcm);
/* (4) */
snd_ac97_suspend(chip->ac97); snd_ac97_suspend(chip->ac97);
/* (5) */ /* (4) */
snd_mychip_save_registers(chip); snd_mychip_save_registers(chip);
/* (6) */ /* (5) */
snd_mychip_stop_hardware(chip); snd_mychip_stop_hardware(chip);
return 0; return 0;
} }
@ -3994,13 +3989,9 @@ A typical code would be like:
return 0; return 0;
} }
As shown in the above, it's better to save registers after suspending Note that, at the time this callback gets called, the PCM stream has
the PCM operations via :c:func:`snd_pcm_suspend_all()` or been already suspended via its own PM ops calling
:c:func:`snd_pcm_suspend()`. It means that the PCM streams are :c:func:`snd_pcm_suspend_all()` internally.
already stopped when the register snapshot is taken. But, remember that
you don't have to restart the PCM stream in the resume callback. It'll
be restarted via trigger call with ``SNDRV_PCM_TRIGGER_RESUME`` when
necessary.
OK, we have all callbacks now. Let's set them up. In the initialization OK, we have all callbacks now. Let's set them up. In the initialization
of the card, make sure that you can get the chip data from the card of the card, make sure that you can get the chip data from the card

View File

@ -13,7 +13,7 @@ drivers that expose several ALSA PCMs and can route to multiple DAIs.
The DPCM runtime routing is determined by the ALSA mixer settings in the same The DPCM runtime routing is determined by the ALSA mixer settings in the same
way as the analog signal is routed in an ASoC codec driver. DPCM uses a DAPM way as the analog signal is routed in an ASoC codec driver. DPCM uses a DAPM
graph representing the DSP internal audio paths and uses the mixer settings to graph representing the DSP internal audio paths and uses the mixer settings to
determine the patch used by each ALSA PCM. determine the path used by each ALSA PCM.
DPCM re-uses all the existing component codec, platform and DAI drivers without DPCM re-uses all the existing component codec, platform and DAI drivers without
any modifications. any modifications.
@ -101,7 +101,7 @@ The audio driver processes this as follows :-
4. Machine driver or audio HAL enables the speaker path. 4. Machine driver or audio HAL enables the speaker path.
5. DPCM runs the PCM ops for startup(), hw_params(), prepapre() and 5. DPCM runs the PCM ops for startup(), hw_params(), prepare() and
trigger(start) for DAI1 Speakers since the path is enabled. trigger(start) for DAI1 Speakers since the path is enabled.
In this example, the machine driver or userspace audio HAL can alter the routing In this example, the machine driver or userspace audio HAL can alter the routing
@ -221,7 +221,7 @@ like a BT phone call :-
This allows the host CPU to sleep while the DSP, MODEM DAI and the BT DAI are This allows the host CPU to sleep while the DSP, MODEM DAI and the BT DAI are
still in operation. still in operation.
A BE DAI link can also set the codec to a dummy device if the code is a device A BE DAI link can also set the codec to a dummy device if the codec is a device
that is managed externally. that is managed externally.
Likewise a BE DAI can also set a dummy cpu DAI if the CPU DAI is managed by the Likewise a BE DAI can also set a dummy cpu DAI if the CPU DAI is managed by the
@ -249,7 +249,7 @@ configuration.
struct snd_interval *channels = hw_param_interval(params, struct snd_interval *channels = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_CHANNELS); SNDRV_PCM_HW_PARAM_CHANNELS);
/* The DSP will covert the FE rate to 48k, stereo */ /* The DSP will convert the FE rate to 48k, stereo */
rate->min = rate->max = 48000; rate->min = rate->max = 48000;
channels->min = channels->max = 2; channels->min = channels->max = 2;
@ -386,5 +386,3 @@ This means creating a new FE that is connected with a virtual path to both
DAI links. The DAI links will be started when the FE PCM is started and stopped DAI links. The DAI links will be started when the FE PCM is started and stopped
when the FE PCM is stopped. Note that the FE PCM cannot read or write data in when the FE PCM is stopped. Note that the FE PCM cannot read or write data in
this configuration. this configuration.

View File

@ -3727,6 +3727,14 @@ N: cros_ec
N: cros-ec N: cros-ec
F: drivers/power/supply/cros_usbpd-charger.c F: drivers/power/supply/cros_usbpd-charger.c
CHROMEOS EC CODEC DRIVER
M: Cheng-Yi Chiang <cychiang@chromium.org>
S: Maintained
R: Enric Balletbo i Serra <enric.balletbo@collabora.com>
R: Guenter Roeck <groeck@chromium.org>
F: Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt
F: sound/soc/codecs/cros_ec_codec.*
CIRRUS LOGIC AUDIO CODEC DRIVERS CIRRUS LOGIC AUDIO CODEC DRIVERS
M: Brian Austin <brian.austin@cirrus.com> M: Brian Austin <brian.austin@cirrus.com>
M: Paul Handrigan <Paul.Handrigan@cirrus.com> M: Paul Handrigan <Paul.Handrigan@cirrus.com>

View File

@ -54,6 +54,7 @@
}; };
hda@3510000 { hda@3510000 {
nvidia,model = "jetson-tx2-hda";
status = "okay"; status = "okay";
}; };

View File

@ -21,6 +21,7 @@
}; };
hda@3510000 { hda@3510000 {
nvidia,model = "jetson-xavier-hda";
status = "okay"; status = "okay";
}; };

View File

@ -1323,6 +1323,7 @@
}; };
hda@70030000 { hda@70030000 {
nvidia,model = "jetson-tx1-hda";
status = "okay"; status = "okay";
}; };

View File

@ -530,17 +530,24 @@ static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data)
if (ares->type != ACPI_RESOURCE_TYPE_GPIO) if (ares->type != ACPI_RESOURCE_TYPE_GPIO)
return 1; return 1;
if (lookup->n++ == lookup->index && !lookup->desc) { if (!lookup->desc) {
const struct acpi_resource_gpio *agpio = &ares->data.gpio; const struct acpi_resource_gpio *agpio = &ares->data.gpio;
int pin_index = lookup->pin_index; bool gpioint = agpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT;
int pin_index;
if (lookup->info.quirks & ACPI_GPIO_QUIRK_ONLY_GPIOIO && gpioint)
lookup->index++;
if (lookup->n++ != lookup->index)
return 1;
pin_index = lookup->pin_index;
if (pin_index >= agpio->pin_table_length) if (pin_index >= agpio->pin_table_length)
return 1; return 1;
lookup->desc = acpi_get_gpiod(agpio->resource_source.string_ptr, lookup->desc = acpi_get_gpiod(agpio->resource_source.string_ptr,
agpio->pin_table[pin_index]); agpio->pin_table[pin_index]);
lookup->info.gpioint = lookup->info.gpioint = gpioint;
agpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT;
/* /*
* Polarity and triggering are only specified for GpioInt * Polarity and triggering are only specified for GpioInt

View File

@ -614,7 +614,6 @@ static int snd_dw_hdmi_suspend(struct device *dev)
struct snd_dw_hdmi *dw = dev_get_drvdata(dev); struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
snd_power_change_state(dw->card, SNDRV_CTL_POWER_D3cold); snd_power_change_state(dw->card, SNDRV_CTL_POWER_D3cold);
snd_pcm_suspend_all(dw->pcm);
return 0; return 0;
} }

View File

@ -360,13 +360,11 @@ static int solo_snd_pcm_init(struct solo_dev *solo_dev)
ss; ss = ss->next, i++) ss; ss = ss->next, i++)
sprintf(ss->name, "Camera #%d Audio", i); sprintf(ss->name, "Camera #%d Audio", i);
ret = snd_pcm_lib_preallocate_pages_for_all(pcm, snd_pcm_lib_preallocate_pages_for_all(pcm,
SNDRV_DMA_TYPE_CONTINUOUS, SNDRV_DMA_TYPE_CONTINUOUS,
snd_dma_continuous_data(GFP_KERNEL), snd_dma_continuous_data(GFP_KERNEL),
G723_PERIOD_BYTES * PERIODS, G723_PERIOD_BYTES * PERIODS,
G723_PERIOD_BYTES * PERIODS); G723_PERIOD_BYTES * PERIODS);
if (ret < 0)
return ret;
solo_dev->snd_pcm = pcm; solo_dev->snd_pcm = pcm;

View File

@ -301,11 +301,12 @@ static int tw686x_snd_pcm_init(struct tw686x_dev *dev)
ss; ss = ss->next, i++) ss; ss = ss->next, i++)
snprintf(ss->name, sizeof(ss->name), "vch%u audio", i); snprintf(ss->name, sizeof(ss->name), "vch%u audio", i);
return snd_pcm_lib_preallocate_pages_for_all(pcm, snd_pcm_lib_preallocate_pages_for_all(pcm,
SNDRV_DMA_TYPE_DEV, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(dev->pci_dev), snd_dma_pci_data(dev->pci_dev),
TW686X_AUDIO_PAGE_MAX * AUDIO_DMA_SIZE_MAX, TW686X_AUDIO_PAGE_MAX * AUDIO_DMA_SIZE_MAX,
TW686X_AUDIO_PAGE_MAX * AUDIO_DMA_SIZE_MAX); TW686X_AUDIO_PAGE_MAX * AUDIO_DMA_SIZE_MAX);
return 0;
} }
static void tw686x_audio_dma_free(struct tw686x_dev *dev, static void tw686x_audio_dma_free(struct tw686x_dev *dev,

View File

@ -1014,6 +1014,13 @@ struct acpi_gpio_mapping {
/* Ignore IoRestriction field */ /* Ignore IoRestriction field */
#define ACPI_GPIO_QUIRK_NO_IO_RESTRICTION BIT(0) #define ACPI_GPIO_QUIRK_NO_IO_RESTRICTION BIT(0)
/*
* When ACPI GPIO mapping table is in use the index parameter inside it
* refers to the GPIO resource in _CRS method. That index has no
* distinction of actual type of the resource. When consumer wants to
* get GpioIo type explicitly, this quirk may be used.
*/
#define ACPI_GPIO_QUIRK_ONLY_GPIOIO BIT(1)
unsigned int quirks; unsigned int quirks;
}; };

View File

@ -120,7 +120,6 @@ struct snd_card {
struct list_head ctl_files; /* active control files */ struct list_head ctl_files; /* active control files */
struct snd_info_entry *proc_root; /* root for soundcard specific files */ struct snd_info_entry *proc_root; /* root for soundcard specific files */
struct snd_info_entry *proc_id; /* the card id */
struct proc_dir_entry *proc_root_link; /* number link to real id */ struct proc_dir_entry *proc_root_link; /* number link to real id */
struct list_head files_list; /* all files associated to this card */ struct list_head files_list; /* all files associated to this card */

43
include/sound/cs35l36.h Normal file
View File

@ -0,0 +1,43 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* linux/sound/cs35l36.h -- Platform data for CS35L36
*
* Copyright 2018 Cirrus Logic, Inc.
*
* Author: James Schulman <james.schulman@cirrus.com>
*
*/
#ifndef __CS35L36_H
#define __CS35L36_H
struct cs35l36_vpbr_cfg {
bool is_present;
bool vpbr_en;
int vpbr_thld;
int vpbr_atk_rate;
int vpbr_atk_vol;
int vpbr_max_attn;
int vpbr_wait;
int vpbr_rel_rate;
int vpbr_mute_en;
};
struct cs35l36_platform_data {
bool multi_amp_mode;
bool dcm_mode;
bool amp_pcm_inv;
bool imon_pol_inv;
bool vmon_pol_inv;
int boost_ind;
int bst_vctl;
int bst_vctl_sel;
int bst_ipk;
bool extern_boost;
int temp_warn_thld;
int irq_drv_sel;
int irq_gpio_sel;
struct cs35l36_vpbr_cfg vpbr_config;
};
#endif /* __CS35L36_H */

View File

@ -99,10 +99,6 @@ void snd_dmaengine_pcm_set_config_from_dai_data(
* playback. * playback.
*/ */
#define SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX BIT(3) #define SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX BIT(3)
/*
* The PCM streams have custom channel names specified.
*/
#define SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME BIT(4)
/** /**
* struct snd_dmaengine_pcm_config - Configuration data for dmaengine based PCM * struct snd_dmaengine_pcm_config - Configuration data for dmaengine based PCM

View File

@ -79,6 +79,7 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
/* stream register offsets from stream base */ /* stream register offsets from stream base */
#define AZX_REG_SD_CTL 0x00 #define AZX_REG_SD_CTL 0x00
#define AZX_REG_SD_CTL_3B 0x02 /* 3rd byte of SD_CTL register */
#define AZX_REG_SD_STS 0x03 #define AZX_REG_SD_STS 0x03
#define AZX_REG_SD_LPIB 0x04 #define AZX_REG_SD_LPIB 0x04
#define AZX_REG_SD_CBL 0x08 #define AZX_REG_SD_CBL 0x08
@ -165,6 +166,7 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
#define SD_INT_COMPLETE 0x04 /* completion interrupt */ #define SD_INT_COMPLETE 0x04 /* completion interrupt */
#define SD_INT_MASK (SD_INT_DESC_ERR|SD_INT_FIFO_ERR|\ #define SD_INT_MASK (SD_INT_DESC_ERR|SD_INT_FIFO_ERR|\
SD_INT_COMPLETE) SD_INT_COMPLETE)
#define SD_CTL_STRIPE_MASK 0x3 /* stripe control mask */
/* SD_STS */ /* SD_STS */
#define SD_STS_FIFO_READY 0x20 /* FIFO ready */ #define SD_STS_FIFO_READY 0x20 /* FIFO ready */

View File

@ -66,6 +66,7 @@ enum {
#define AC_VERB_GET_CONFIG_DEFAULT 0x0f1c #define AC_VERB_GET_CONFIG_DEFAULT 0x0f1c
/* f20: AFG/MFG */ /* f20: AFG/MFG */
#define AC_VERB_GET_SUBSYSTEM_ID 0x0f20 #define AC_VERB_GET_SUBSYSTEM_ID 0x0f20
#define AC_VERB_GET_STRIPE_CONTROL 0x0f24
#define AC_VERB_GET_CVT_CHAN_COUNT 0x0f2d #define AC_VERB_GET_CVT_CHAN_COUNT 0x0f2d
#define AC_VERB_GET_HDMI_DIP_SIZE 0x0f2e #define AC_VERB_GET_HDMI_DIP_SIZE 0x0f2e
#define AC_VERB_GET_HDMI_ELDD 0x0f2f #define AC_VERB_GET_HDMI_ELDD 0x0f2f
@ -110,6 +111,7 @@ enum {
#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_3 0x71f #define AC_VERB_SET_CONFIG_DEFAULT_BYTES_3 0x71f
#define AC_VERB_SET_EAPD 0x788 #define AC_VERB_SET_EAPD 0x788
#define AC_VERB_SET_CODEC_RESET 0x7ff #define AC_VERB_SET_CODEC_RESET 0x7ff
#define AC_VERB_SET_STRIPE_CONTROL 0x724
#define AC_VERB_SET_CVT_CHAN_COUNT 0x72d #define AC_VERB_SET_CVT_CHAN_COUNT 0x72d
#define AC_VERB_SET_HDMI_DIP_INDEX 0x730 #define AC_VERB_SET_HDMI_DIP_INDEX 0x730
#define AC_VERB_SET_HDMI_DIP_DATA 0x731 #define AC_VERB_SET_HDMI_DIP_DATA 0x731

View File

@ -539,6 +539,9 @@ void snd_hdac_stream_sync(struct hdac_stream *azx_dev, bool start,
unsigned int streams); unsigned int streams);
void snd_hdac_stream_timecounter_init(struct hdac_stream *azx_dev, void snd_hdac_stream_timecounter_init(struct hdac_stream *azx_dev,
unsigned int streams); unsigned int streams);
int snd_hdac_get_stream_stripe_ctl(struct hdac_bus *bus,
struct snd_pcm_substream *substream);
/* /*
* macros for easy use * macros for easy use
*/ */

View File

@ -82,7 +82,6 @@ struct snd_info_entry {
struct snd_info_entry_ops *ops; struct snd_info_entry_ops *ops;
} c; } c;
struct snd_info_entry *parent; struct snd_info_entry *parent;
struct snd_card *card;
struct module *module; struct module *module;
void *private_data; void *private_data;
void (*private_free)(struct snd_info_entry *entry); void (*private_free)(struct snd_info_entry *entry);
@ -160,6 +159,13 @@ static inline void snd_info_set_text_ops(struct snd_info_entry *entry,
entry->c.text.read = read; entry->c.text.read = read;
} }
int snd_card_rw_proc_new(struct snd_card *card, const char *name,
void *private_data,
void (*read)(struct snd_info_entry *,
struct snd_info_buffer *),
void (*write)(struct snd_info_entry *entry,
struct snd_info_buffer *buffer));
int snd_info_check_reserved_words(const char *str); int snd_info_check_reserved_words(const char *str);
#else #else
@ -189,10 +195,38 @@ static inline int snd_card_proc_new(struct snd_card *card, const char *name,
static inline void snd_info_set_text_ops(struct snd_info_entry *entry __attribute__((unused)), static inline void snd_info_set_text_ops(struct snd_info_entry *entry __attribute__((unused)),
void *private_data, void *private_data,
void (*read)(struct snd_info_entry *, struct snd_info_buffer *)) {} void (*read)(struct snd_info_entry *, struct snd_info_buffer *)) {}
static inline int snd_card_rw_proc_new(struct snd_card *card, const char *name,
void *private_data,
void (*read)(struct snd_info_entry *,
struct snd_info_buffer *),
void (*write)(struct snd_info_entry *entry,
struct snd_info_buffer *buffer))
{
return 0;
}
static inline int snd_info_check_reserved_words(const char *str) { return 1; } static inline int snd_info_check_reserved_words(const char *str) { return 1; }
#endif #endif
/**
* snd_card_ro_proc_new - Create a read-only text proc file entry for the card
* @card: the card instance
* @name: the file name
* @private_data: the arbitrary private data
* @read: the read callback
*
* This proc file entry will be registered via snd_card_register() call, and
* it will be removed automatically at the card removal, too.
*/
static inline int
snd_card_ro_proc_new(struct snd_card *card, const char *name,
void *private_data,
void (*read)(struct snd_info_entry *,
struct snd_info_buffer *))
{
return snd_card_rw_proc_new(card, name, private_data, read, NULL);
}
/* /*
* OSS info part * OSS info part
*/ */

View File

@ -37,7 +37,6 @@ struct snd_dma_device {
}; };
#define snd_dma_pci_data(pci) (&(pci)->dev) #define snd_dma_pci_data(pci) (&(pci)->dev)
#define snd_dma_isa_data() NULL
#define snd_dma_continuous_data(x) ((struct device *)(__force unsigned long)(x)) #define snd_dma_continuous_data(x) ((struct device *)(__force unsigned long)(x))

View File

@ -30,6 +30,7 @@
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/pm_qos.h> #include <linux/pm_qos.h>
#include <linux/refcount.h>
#define snd_pcm_substream_chip(substream) ((substream)->private_data) #define snd_pcm_substream_chip(substream) ((substream)->private_data)
#define snd_pcm_chip(pcm) ((pcm)->private_data) #define snd_pcm_chip(pcm) ((pcm)->private_data)
@ -439,7 +440,7 @@ struct snd_pcm_group { /* keep linked substreams */
spinlock_t lock; spinlock_t lock;
struct mutex mutex; struct mutex mutex;
struct list_head substreams; struct list_head substreams;
int count; refcount_t refs;
}; };
struct pid; struct pid;
@ -470,7 +471,6 @@ struct snd_pcm_substream {
struct snd_pcm_group self_group; /* fake group for non linked substream (with substream lock inside) */ struct snd_pcm_group self_group; /* fake group for non linked substream (with substream lock inside) */
struct snd_pcm_group *group; /* pointer to current group */ struct snd_pcm_group *group; /* pointer to current group */
/* -- assigned files -- */ /* -- assigned files -- */
void *file;
int ref_count; int ref_count;
atomic_t mmap_count; atomic_t mmap_count;
unsigned int f_flags; unsigned int f_flags;
@ -482,15 +482,6 @@ struct snd_pcm_substream {
#endif #endif
#ifdef CONFIG_SND_VERBOSE_PROCFS #ifdef CONFIG_SND_VERBOSE_PROCFS
struct snd_info_entry *proc_root; struct snd_info_entry *proc_root;
struct snd_info_entry *proc_info_entry;
struct snd_info_entry *proc_hw_params_entry;
struct snd_info_entry *proc_sw_params_entry;
struct snd_info_entry *proc_status_entry;
struct snd_info_entry *proc_prealloc_entry;
struct snd_info_entry *proc_prealloc_max_entry;
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
struct snd_info_entry *proc_xrun_injection_entry;
#endif
#endif /* CONFIG_SND_VERBOSE_PROCFS */ #endif /* CONFIG_SND_VERBOSE_PROCFS */
/* misc flags */ /* misc flags */
unsigned int hw_opened: 1; unsigned int hw_opened: 1;
@ -512,10 +503,8 @@ struct snd_pcm_str {
#endif #endif
#ifdef CONFIG_SND_VERBOSE_PROCFS #ifdef CONFIG_SND_VERBOSE_PROCFS
struct snd_info_entry *proc_root; struct snd_info_entry *proc_root;
struct snd_info_entry *proc_info_entry;
#ifdef CONFIG_SND_PCM_XRUN_DEBUG #ifdef CONFIG_SND_PCM_XRUN_DEBUG
unsigned int xrun_debug; /* 0 = disabled, 1 = verbose, 2 = stacktrace */ unsigned int xrun_debug; /* 0 = disabled, 1 = verbose, 2 = stacktrace */
struct snd_info_entry *proc_xrun_debug_entry;
#endif #endif
#endif #endif
struct snd_kcontrol *chmap_kctl; /* channel-mapping controls */ struct snd_kcontrol *chmap_kctl; /* channel-mapping controls */
@ -538,6 +527,7 @@ struct snd_pcm {
void (*private_free) (struct snd_pcm *pcm); void (*private_free) (struct snd_pcm *pcm);
bool internal; /* pcm is for internal use only */ bool internal; /* pcm is for internal use only */
bool nonatomic; /* whole PCM operations are in non-atomic context */ bool nonatomic; /* whole PCM operations are in non-atomic context */
bool no_device_suspend; /* don't invoke device PM suspend */
#if IS_ENABLED(CONFIG_SND_PCM_OSS) #if IS_ENABLED(CONFIG_SND_PCM_OSS)
struct snd_pcm_oss oss; struct snd_pcm_oss oss;
#endif #endif
@ -581,13 +571,8 @@ int snd_pcm_stop(struct snd_pcm_substream *substream, snd_pcm_state_t status);
int snd_pcm_drain_done(struct snd_pcm_substream *substream); int snd_pcm_drain_done(struct snd_pcm_substream *substream);
int snd_pcm_stop_xrun(struct snd_pcm_substream *substream); int snd_pcm_stop_xrun(struct snd_pcm_substream *substream);
#ifdef CONFIG_PM #ifdef CONFIG_PM
int snd_pcm_suspend(struct snd_pcm_substream *substream);
int snd_pcm_suspend_all(struct snd_pcm *pcm); int snd_pcm_suspend_all(struct snd_pcm *pcm);
#else #else
static inline int snd_pcm_suspend(struct snd_pcm_substream *substream)
{
return 0;
}
static inline int snd_pcm_suspend_all(struct snd_pcm *pcm) static inline int snd_pcm_suspend_all(struct snd_pcm *pcm)
{ {
return 0; return 0;
@ -1200,12 +1185,12 @@ static inline void snd_pcm_gettime(struct snd_pcm_runtime *runtime,
* Memory * Memory
*/ */
int snd_pcm_lib_preallocate_free(struct snd_pcm_substream *substream); void snd_pcm_lib_preallocate_free(struct snd_pcm_substream *substream);
int snd_pcm_lib_preallocate_free_for_all(struct snd_pcm *pcm); void snd_pcm_lib_preallocate_free_for_all(struct snd_pcm *pcm);
int snd_pcm_lib_preallocate_pages(struct snd_pcm_substream *substream, void snd_pcm_lib_preallocate_pages(struct snd_pcm_substream *substream,
int type, struct device *data, int type, struct device *data,
size_t size, size_t max); size_t size, size_t max);
int snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm, void snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm,
int type, void *data, int type, void *data,
size_t size, size_t max); size_t size, size_t max);
int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size); int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size);

View File

@ -75,7 +75,7 @@ void asoc_simple_card_clk_disable(struct asoc_simple_dai *dai);
&dai_link->codec_dai_name, \ &dai_link->codec_dai_name, \
list_name, cells_name, NULL) list_name, cells_name, NULL)
#define asoc_simple_card_parse_platform(node, dai_link, list_name, cells_name) \ #define asoc_simple_card_parse_platform(node, dai_link, list_name, cells_name) \
asoc_simple_card_parse_dai(node, dai_link->platform, \ asoc_simple_card_parse_dai(node, dai_link->platforms, \
&dai_link->platform_of_node, \ &dai_link->platform_of_node, \
NULL, list_name, cells_name, NULL) NULL, list_name, cells_name, NULL)
int asoc_simple_card_parse_dai(struct device_node *node, int asoc_simple_card_parse_dai(struct device_node *node,
@ -108,7 +108,7 @@ int asoc_simple_card_parse_graph_dai(struct device_node *ep,
int asoc_simple_card_init_dai(struct snd_soc_dai *dai, int asoc_simple_card_init_dai(struct snd_soc_dai *dai,
struct asoc_simple_dai *simple_dai); struct asoc_simple_dai *simple_dai);
int asoc_simple_card_canonicalize_dailink(struct snd_soc_dai_link *dai_link); 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_card_canonicalize_cpu(struct snd_soc_dai_link *dai_link,
int is_single_links); int is_single_links);

View File

@ -22,20 +22,37 @@ struct snd_soc_acpi_package_context {
#define SND_ACPI_I2C_ID_LEN (4 + ACPI_ID_LEN + 3 + 1) #define SND_ACPI_I2C_ID_LEN (4 + ACPI_ID_LEN + 3 + 1)
#if IS_ENABLED(CONFIG_ACPI) #if IS_ENABLED(CONFIG_ACPI)
/* acpi match */
struct snd_soc_acpi_mach *
snd_soc_acpi_find_machine(struct snd_soc_acpi_mach *machines);
bool snd_soc_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN], bool snd_soc_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN],
struct snd_soc_acpi_package_context *ctx); struct snd_soc_acpi_package_context *ctx);
/* check all codecs */
struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg);
#else #else
/* acpi match */
static inline struct snd_soc_acpi_mach *
snd_soc_acpi_find_machine(struct snd_soc_acpi_mach *machines)
{
return NULL;
}
static inline bool static inline bool
snd_soc_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN], snd_soc_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN],
struct snd_soc_acpi_package_context *ctx) struct snd_soc_acpi_package_context *ctx)
{ {
return false; return false;
} }
#endif
/* acpi match */ /* check all codecs */
struct snd_soc_acpi_mach * static inline struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg)
snd_soc_acpi_find_machine(struct snd_soc_acpi_mach *machines); {
return NULL;
}
#endif
/** /**
* snd_soc_acpi_mach_params: interface for machine driver configuration * snd_soc_acpi_mach_params: interface for machine driver configuration
@ -69,9 +86,6 @@ struct snd_soc_acpi_mach_params {
* is not constant since this field may be updated at run-time * is not constant since this field may be updated at run-time
* @sof_fw_filename: Sound Open Firmware file name, if enabled * @sof_fw_filename: Sound Open Firmware file name, if enabled
* @sof_tplg_filename: Sound Open Firmware topology file name, if enabled * @sof_tplg_filename: Sound Open Firmware topology file name, if enabled
* @asoc_plat_name: ASoC platform name, used for binding machine drivers
* if non NULL
* @new_mach_data: machine driver private data fixup
*/ */
/* Descriptor for SST ASoC machine driver */ /* Descriptor for SST ASoC machine driver */
struct snd_soc_acpi_mach { struct snd_soc_acpi_mach {
@ -85,8 +99,6 @@ struct snd_soc_acpi_mach {
struct snd_soc_acpi_mach_params mach_params; struct snd_soc_acpi_mach_params mach_params;
const char *sof_fw_filename; const char *sof_fw_filename;
const char *sof_tplg_filename; const char *sof_tplg_filename;
const char *asoc_plat_name;
struct platform_device * (*new_mach_data)(void *pdata);
}; };
#define SND_SOC_ACPI_MAX_CODECS 3 #define SND_SOC_ACPI_MAX_CODECS 3
@ -105,7 +117,4 @@ struct snd_soc_acpi_codecs {
u8 codecs[SND_SOC_ACPI_MAX_CODECS][ACPI_ID_LEN]; u8 codecs[SND_SOC_ACPI_MAX_CODECS][ACPI_ID_LEN];
}; };
/* check all codecs */
struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg);
#endif #endif

View File

@ -214,21 +214,21 @@ struct device;
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD} .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD}
/* stream domain */ /* stream domain */
#define SND_SOC_DAPM_AIF_IN(wname, stname, wslot, wreg, wshift, winvert) \ #define SND_SOC_DAPM_AIF_IN(wname, stname, wchan, wreg, wshift, winvert) \
{ .id = snd_soc_dapm_aif_in, .name = wname, .sname = stname, \ { .id = snd_soc_dapm_aif_in, .name = wname, .sname = stname, \
SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), } .channel = wchan, SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), }
#define SND_SOC_DAPM_AIF_IN_E(wname, stname, wslot, wreg, wshift, winvert, \ #define SND_SOC_DAPM_AIF_IN_E(wname, stname, wchan, wreg, wshift, winvert, \
wevent, wflags) \ wevent, wflags) \
{ .id = snd_soc_dapm_aif_in, .name = wname, .sname = stname, \ { .id = snd_soc_dapm_aif_in, .name = wname, .sname = stname, \
SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ .channel = wchan, SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
.event = wevent, .event_flags = wflags } .event = wevent, .event_flags = wflags }
#define SND_SOC_DAPM_AIF_OUT(wname, stname, wslot, wreg, wshift, winvert) \ #define SND_SOC_DAPM_AIF_OUT(wname, stname, wchan, wreg, wshift, winvert) \
{ .id = snd_soc_dapm_aif_out, .name = wname, .sname = stname, \ { .id = snd_soc_dapm_aif_out, .name = wname, .sname = stname, \
SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), } .channel = wchan, SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), }
#define SND_SOC_DAPM_AIF_OUT_E(wname, stname, wslot, wreg, wshift, winvert, \ #define SND_SOC_DAPM_AIF_OUT_E(wname, stname, wchan, wreg, wshift, winvert, \
wevent, wflags) \ wevent, wflags) \
{ .id = snd_soc_dapm_aif_out, .name = wname, .sname = stname, \ { .id = snd_soc_dapm_aif_out, .name = wname, .sname = stname, \
SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ .channel = wchan, SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
.event = wevent, .event_flags = wflags } .event = wevent, .event_flags = wflags }
#define SND_SOC_DAPM_DAC(wname, stname, wreg, wshift, winvert) \ #define SND_SOC_DAPM_DAC(wname, stname, wreg, wshift, winvert) \
{ .id = snd_soc_dapm_dac, .name = wname, .sname = stname, \ { .id = snd_soc_dapm_dac, .name = wname, .sname = stname, \
@ -407,6 +407,10 @@ int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card); int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card);
void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card); void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card);
int snd_soc_dapm_update_dai(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai);
/* dapm path setup */ /* dapm path setup */
int snd_soc_dapm_new_widgets(struct snd_soc_card *card); int snd_soc_dapm_new_widgets(struct snd_soc_card *card);
void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm); void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm);
@ -519,6 +523,9 @@ enum snd_soc_dapm_type {
snd_soc_dapm_asrc, /* DSP/CODEC ASRC component */ snd_soc_dapm_asrc, /* DSP/CODEC ASRC component */
snd_soc_dapm_encoder, /* FW/SW audio encoder component */ snd_soc_dapm_encoder, /* FW/SW audio encoder component */
snd_soc_dapm_decoder, /* FW/SW audio decoder component */ snd_soc_dapm_decoder, /* FW/SW audio decoder component */
/* Don't edit below this line */
SND_SOC_DAPM_TYPE_COUNT
}; };
enum snd_soc_dapm_subclass { enum snd_soc_dapm_subclass {
@ -540,6 +547,8 @@ struct snd_soc_dapm_route {
/* Note: currently only supported for links where source is a supply */ /* Note: currently only supported for links where source is a supply */
int (*connected)(struct snd_soc_dapm_widget *source, int (*connected)(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink); struct snd_soc_dapm_widget *sink);
struct snd_soc_dobj dobj;
}; };
/* dapm audio path between two widgets */ /* dapm audio path between two widgets */
@ -625,6 +634,8 @@ struct snd_soc_dapm_widget {
int endpoints[2]; int endpoints[2];
struct clk *clk; struct clk *clk;
int channel;
}; };
struct snd_soc_dapm_update { struct snd_soc_dapm_update {

View File

@ -38,12 +38,14 @@ struct snd_soc_dapm_route;
enum snd_soc_dobj_type { enum snd_soc_dobj_type {
SND_SOC_DOBJ_NONE = 0, /* object is not dynamic */ SND_SOC_DOBJ_NONE = 0, /* object is not dynamic */
SND_SOC_DOBJ_MIXER, SND_SOC_DOBJ_MIXER,
SND_SOC_DOBJ_ENUM,
SND_SOC_DOBJ_BYTES, SND_SOC_DOBJ_BYTES,
SND_SOC_DOBJ_PCM, SND_SOC_DOBJ_ENUM,
SND_SOC_DOBJ_DAI_LINK, SND_SOC_DOBJ_GRAPH,
SND_SOC_DOBJ_CODEC_LINK,
SND_SOC_DOBJ_WIDGET, SND_SOC_DOBJ_WIDGET,
SND_SOC_DOBJ_DAI_LINK,
SND_SOC_DOBJ_PCM,
SND_SOC_DOBJ_CODEC_LINK,
SND_SOC_DOBJ_BACKEND_LINK,
}; };
/* dynamic control object */ /* dynamic control object */

View File

@ -802,6 +802,9 @@ struct snd_soc_component_driver {
int probe_order; int probe_order;
int remove_order; int remove_order;
/* signal if the module handling the component cannot be removed */
unsigned int ignore_module_refcount:1;
/* bits */ /* bits */
unsigned int idle_bias_on:1; unsigned int idle_bias_on:1;
unsigned int suspend_bias_off:1; unsigned int suspend_bias_off:1;
@ -891,6 +894,18 @@ struct snd_soc_dai_link {
/* config - must be set by machine driver */ /* config - must be set by machine driver */
const char *name; /* Codec name */ const char *name; /* Codec name */
const char *stream_name; /* Stream name */ const char *stream_name; /* Stream name */
/*
* cpu_name
* cpu_of_node
* cpu_dai_name
*
* These are legacy style, and will be replaced to
* modern style (= snd_soc_dai_link_component) in the future,
* but, not yet supported so far.
* If modern style was supported for CPU, all driver will switch
* to use it, and, legacy style code will be removed from ALSA SoC.
*/
/* /*
* You MAY specify the link's CPU-side device, either by device name, * You MAY specify the link's CPU-side device, either by device name,
* or by DT/OF node, but not both. If this information is omitted, * or by DT/OF node, but not both. If this information is omitted,
@ -906,6 +921,19 @@ struct snd_soc_dai_link {
* only, which only works well when that device exposes a single DAI. * only, which only works well when that device exposes a single DAI.
*/ */
const char *cpu_dai_name; const char *cpu_dai_name;
/*
* codec_name
* codec_of_node
* codec_dai_name
*
* These are legacy style, it will be converted to modern style
* (= snd_soc_dai_link_component) automatically in soc-core
* if driver is using legacy style.
* Driver shouldn't use both legacy and modern style in the same time.
* If modern style was supported for CPU, all driver will switch
* to use it, and, legacy style code will be removed from ALSA SoC.
*/
/* /*
* You MUST specify the link's codec, either by device name, or by * You MUST specify the link's codec, either by device name, or by
* DT/OF node, but not both. * DT/OF node, but not both.
@ -918,6 +946,17 @@ struct snd_soc_dai_link {
struct snd_soc_dai_link_component *codecs; struct snd_soc_dai_link_component *codecs;
unsigned int num_codecs; unsigned int num_codecs;
/*
* platform_name
* platform_of_node
*
* These are legacy style, it will be converted to modern style
* (= snd_soc_dai_link_component) automatically in soc-core
* if driver is using legacy style.
* Driver shouldn't use both legacy and modern style in the same time.
* If modern style was supported for CPU, all driver will switch
* to use it, and, legacy style code will be removed from ALSA SoC.
*/
/* /*
* You MAY specify the link's platform/PCM/DMA driver, either by * You MAY specify the link's platform/PCM/DMA driver, either by
* device name, or by DT/OF node, but not both. Some forms of link * device name, or by DT/OF node, but not both. Some forms of link
@ -925,7 +964,8 @@ struct snd_soc_dai_link {
*/ */
const char *platform_name; const char *platform_name;
struct device_node *platform_of_node; struct device_node *platform_of_node;
struct snd_soc_dai_link_component *platform; struct snd_soc_dai_link_component *platforms;
unsigned int num_platforms;
int id; /* optional ID for machine driver link identification */ int id; /* optional ID for machine driver link identification */
@ -1543,6 +1583,37 @@ struct snd_soc_dai *snd_soc_card_get_codec_dai(struct snd_soc_card *card,
return NULL; return NULL;
} }
static inline
int snd_soc_fixup_dai_links_platform_name(struct snd_soc_card *card,
const char *platform_name)
{
struct snd_soc_dai_link *dai_link;
const char *name;
int i;
if (!platform_name) /* nothing to do */
return 0;
/* set platform name for each dailink */
for_each_card_prelinks(card, i, dai_link) {
name = devm_kstrdup(card->dev, platform_name, GFP_KERNEL);
if (!name)
return -ENOMEM;
if (dai_link->platforms)
/* only single platform is supported for now */
dai_link->platforms->name = name;
else
/*
* legacy mode, this case will be removed when all
* derivers are switched to modern style dai_link.
*/
dai_link->platform_name = name;
}
return 0;
}
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
extern struct dentry *snd_soc_debugfs_root; extern struct dentry *snd_soc_debugfs_root;
#endif #endif

View File

@ -84,7 +84,7 @@ ac97_of_get_child_device(struct ac97_controller *ac97_ctrl, int idx,
if ((idx != of_property_read_u32(node, "reg", &reg)) || if ((idx != of_property_read_u32(node, "reg", &reg)) ||
!of_device_is_compatible(node, compat)) !of_device_is_compatible(node, compat))
continue; continue;
return of_node_get(node); return node;
} }
return NULL; return NULL;

View File

@ -82,6 +82,7 @@ static struct device_node *get_gpio(char *name,
if (altname && (strcmp(audio_gpio, altname) == 0)) if (altname && (strcmp(audio_gpio, altname) == 0))
break; break;
} }
of_node_put(gpio);
/* still not found, assume not there */ /* still not found, assume not there */
if (!np) if (!np)
return NULL; return NULL;

View File

@ -380,10 +380,6 @@ static int i2sbus_suspend(struct macio_dev* dev, pm_message_t state)
int err, ret = 0; int err, ret = 0;
list_for_each_entry(i2sdev, &control->list, item) { list_for_each_entry(i2sdev, &control->list, item) {
/* Notify Alsa */
/* Suspend PCM streams */
snd_pcm_suspend_all(i2sdev->sound.pcm);
/* Notify codecs */ /* Notify codecs */
list_for_each_entry(cii, &i2sdev->sound.codec_list, list) { list_for_each_entry(cii, &i2sdev->sound.codec_list, list) {
err = 0; err = 0;

View File

@ -757,7 +757,6 @@ static int aaci_do_suspend(struct snd_card *card)
{ {
struct aaci *aaci = card->private_data; struct aaci *aaci = card->private_data;
snd_power_change_state(card, SNDRV_CTL_POWER_D3cold); snd_power_change_state(card, SNDRV_CTL_POWER_D3cold);
snd_pcm_suspend_all(aaci->pcm);
return 0; return 0;
} }
@ -942,7 +941,8 @@ static int aaci_init_pcm(struct aaci *aaci)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &aaci_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &aaci_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &aaci_capture_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &aaci_capture_ops);
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
NULL, 0, 64 * 1024); aaci->card->dev,
0, 64 * 1024);
} }
return ret; return ret;

View File

@ -124,7 +124,6 @@ static int pxa2xx_ac97_do_suspend(struct snd_card *card)
pxa2xx_audio_ops_t *platform_ops = card->dev->platform_data; pxa2xx_audio_ops_t *platform_ops = card->dev->platform_data;
snd_power_change_state(card, SNDRV_CTL_POWER_D3cold); snd_power_change_state(card, SNDRV_CTL_POWER_D3cold);
snd_pcm_suspend_all(pxa2xx_ac97_pcm);
snd_ac97_suspend(pxa2xx_ac97_ac97); snd_ac97_suspend(pxa2xx_ac97_ac97);
if (platform_ops && platform_ops->suspend) if (platform_ops && platform_ops->suspend)
platform_ops->suspend(platform_ops->priv); platform_ops->suspend(platform_ops->priv);

View File

@ -603,11 +603,9 @@ static int atmel_ac97c_pcm_new(struct atmel_ac97c *chip)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &atmel_ac97_capture_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &atmel_ac97_capture_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &atmel_ac97_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &atmel_ac97_playback_ops);
retval = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
&chip->pdev->dev, hw.periods_min * hw.period_bytes_min, &chip->pdev->dev, hw.periods_min * hw.period_bytes_min,
hw.buffer_bytes_max); hw.buffer_bytes_max);
if (retval)
return retval;
pcm->private_data = chip; pcm->private_data = chip;
pcm->info_flags = 0; pcm->info_flags = 0;

View File

@ -1015,22 +1015,13 @@ static int snd_compress_proc_init(struct snd_compr *compr)
if (!entry) if (!entry)
return -ENOMEM; return -ENOMEM;
entry->mode = S_IFDIR | 0555; entry->mode = S_IFDIR | 0555;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
return -ENOMEM;
}
compr->proc_root = entry; compr->proc_root = entry;
entry = snd_info_create_card_entry(compr->card, "info", entry = snd_info_create_card_entry(compr->card, "info",
compr->proc_root); compr->proc_root);
if (entry) { if (entry)
snd_info_set_text_ops(entry, compr, snd_info_set_text_ops(entry, compr,
snd_compress_proc_info_read); snd_compress_proc_info_read);
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
entry = NULL;
}
}
compr->proc_info_entry = entry; compr->proc_info_entry = entry;
return 0; return 0;

View File

@ -463,11 +463,12 @@ static struct snd_info_entry *create_subdir(struct module *mod,
} }
static struct snd_info_entry * static struct snd_info_entry *
snd_info_create_entry(const char *name, struct snd_info_entry *parent); snd_info_create_entry(const char *name, struct snd_info_entry *parent,
struct module *module);
int __init snd_info_init(void) int __init snd_info_init(void)
{ {
snd_proc_root = snd_info_create_entry("asound", NULL); snd_proc_root = snd_info_create_entry("asound", NULL, THIS_MODULE);
if (!snd_proc_root) if (!snd_proc_root)
return -ENOMEM; return -ENOMEM;
snd_proc_root->mode = S_IFDIR | 0555; snd_proc_root->mode = S_IFDIR | 0555;
@ -503,6 +504,14 @@ int __exit snd_info_done(void)
return 0; return 0;
} }
static void snd_card_id_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_card *card = entry->private_data;
snd_iprintf(buffer, "%s\n", card->id);
}
/* /*
* create a card proc file * create a card proc file
* called from init.c * called from init.c
@ -520,28 +529,8 @@ int snd_info_card_create(struct snd_card *card)
if (!entry) if (!entry)
return -ENOMEM; return -ENOMEM;
card->proc_root = entry; card->proc_root = entry;
return 0;
}
/* register all pending info entries */ return snd_card_ro_proc_new(card, "id", card, snd_card_id_read);
static int snd_info_register_recursive(struct snd_info_entry *entry)
{
struct snd_info_entry *p;
int err;
if (!entry->p) {
err = snd_info_register(entry);
if (err < 0)
return err;
}
list_for_each_entry(p, &entry->children, list) {
err = snd_info_register_recursive(p);
if (err < 0)
return err;
}
return 0;
} }
/* /*
@ -557,7 +546,7 @@ int snd_info_card_register(struct snd_card *card)
if (snd_BUG_ON(!card)) if (snd_BUG_ON(!card))
return -ENXIO; return -ENXIO;
err = snd_info_register_recursive(card->proc_root); err = snd_info_register(card->proc_root);
if (err < 0) if (err < 0)
return err; return err;
@ -705,7 +694,8 @@ EXPORT_SYMBOL(snd_info_get_str);
* Return: The pointer of the new instance, or %NULL on failure. * Return: The pointer of the new instance, or %NULL on failure.
*/ */
static struct snd_info_entry * static struct snd_info_entry *
snd_info_create_entry(const char *name, struct snd_info_entry *parent) snd_info_create_entry(const char *name, struct snd_info_entry *parent,
struct module *module)
{ {
struct snd_info_entry *entry; struct snd_info_entry *entry;
entry = kzalloc(sizeof(*entry), GFP_KERNEL); entry = kzalloc(sizeof(*entry), GFP_KERNEL);
@ -722,6 +712,7 @@ snd_info_create_entry(const char *name, struct snd_info_entry *parent)
INIT_LIST_HEAD(&entry->children); INIT_LIST_HEAD(&entry->children);
INIT_LIST_HEAD(&entry->list); INIT_LIST_HEAD(&entry->list);
entry->parent = parent; entry->parent = parent;
entry->module = module;
if (parent) if (parent)
list_add_tail(&entry->list, &parent->children); list_add_tail(&entry->list, &parent->children);
return entry; return entry;
@ -741,10 +732,9 @@ struct snd_info_entry *snd_info_create_module_entry(struct module * module,
const char *name, const char *name,
struct snd_info_entry *parent) struct snd_info_entry *parent)
{ {
struct snd_info_entry *entry = snd_info_create_entry(name, parent); if (!parent)
if (entry) parent = snd_proc_root;
entry->module = module; return snd_info_create_entry(name, parent, module);
return entry;
} }
EXPORT_SYMBOL(snd_info_create_module_entry); EXPORT_SYMBOL(snd_info_create_module_entry);
@ -762,12 +752,9 @@ struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card,
const char *name, const char *name,
struct snd_info_entry * parent) struct snd_info_entry * parent)
{ {
struct snd_info_entry *entry = snd_info_create_entry(name, parent); if (!parent)
if (entry) { parent = card->proc_root;
entry->module = card->module; return snd_info_create_entry(name, parent, card->module);
entry->card = card;
}
return entry;
} }
EXPORT_SYMBOL(snd_info_create_card_entry); EXPORT_SYMBOL(snd_info_create_card_entry);
@ -813,15 +800,7 @@ void snd_info_free_entry(struct snd_info_entry * entry)
} }
EXPORT_SYMBOL(snd_info_free_entry); EXPORT_SYMBOL(snd_info_free_entry);
/** static int __snd_info_register(struct snd_info_entry *entry)
* snd_info_register - register the info entry
* @entry: the info entry
*
* Registers the proc info entry.
*
* Return: Zero if successful, or a negative error code on failure.
*/
int snd_info_register(struct snd_info_entry * entry)
{ {
struct proc_dir_entry *root, *p = NULL; struct proc_dir_entry *root, *p = NULL;
@ -829,6 +808,8 @@ int snd_info_register(struct snd_info_entry * entry)
return -ENXIO; return -ENXIO;
root = entry->parent == NULL ? snd_proc_root->p : entry->parent->p; root = entry->parent == NULL ? snd_proc_root->p : entry->parent->p;
mutex_lock(&info_mutex); mutex_lock(&info_mutex);
if (entry->p || !root)
goto unlock;
if (S_ISDIR(entry->mode)) { if (S_ISDIR(entry->mode)) {
p = proc_mkdir_mode(entry->name, entry->mode, root); p = proc_mkdir_mode(entry->name, entry->mode, root);
if (!p) { if (!p) {
@ -850,11 +831,73 @@ int snd_info_register(struct snd_info_entry * entry)
proc_set_size(p, entry->size); proc_set_size(p, entry->size);
} }
entry->p = p; entry->p = p;
unlock:
mutex_unlock(&info_mutex); mutex_unlock(&info_mutex);
return 0; return 0;
} }
/**
* snd_info_register - register the info entry
* @entry: the info entry
*
* Registers the proc info entry.
* The all children entries are registered recursively.
*
* Return: Zero if successful, or a negative error code on failure.
*/
int snd_info_register(struct snd_info_entry *entry)
{
struct snd_info_entry *p;
int err;
if (!entry->p) {
err = __snd_info_register(entry);
if (err < 0)
return err;
}
list_for_each_entry(p, &entry->children, list) {
err = snd_info_register(p);
if (err < 0)
return err;
}
return 0;
}
EXPORT_SYMBOL(snd_info_register); EXPORT_SYMBOL(snd_info_register);
/**
* snd_card_rw_proc_new - Create a read/write text proc file entry for the card
* @card: the card instance
* @name: the file name
* @private_data: the arbitrary private data
* @read: the read callback
* @write: the write callback, NULL for read-only
*
* This proc file entry will be registered via snd_card_register() call, and
* it will be removed automatically at the card removal, too.
*/
int snd_card_rw_proc_new(struct snd_card *card, const char *name,
void *private_data,
void (*read)(struct snd_info_entry *,
struct snd_info_buffer *),
void (*write)(struct snd_info_entry *entry,
struct snd_info_buffer *buffer))
{
struct snd_info_entry *entry;
entry = snd_info_create_card_entry(card, name, card->proc_root);
if (!entry)
return -ENOMEM;
snd_info_set_text_ops(entry, private_data, read);
if (write) {
entry->mode |= 0200;
entry->c.text.write = write;
}
return 0;
}
EXPORT_SYMBOL_GPL(snd_card_rw_proc_new);
/* /*
*/ */

View File

@ -100,31 +100,6 @@ int (*snd_mixer_oss_notify_callback)(struct snd_card *card, int free_flag);
EXPORT_SYMBOL(snd_mixer_oss_notify_callback); EXPORT_SYMBOL(snd_mixer_oss_notify_callback);
#endif #endif
#ifdef CONFIG_SND_PROC_FS
static void snd_card_id_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
snd_iprintf(buffer, "%s\n", entry->card->id);
}
static int init_info_for_card(struct snd_card *card)
{
struct snd_info_entry *entry;
entry = snd_info_create_card_entry(card, "id", card->proc_root);
if (!entry) {
dev_dbg(card->dev, "unable to create card entry\n");
return -ENOMEM;
}
entry->c.text.read = snd_card_id_read;
card->proc_id = entry;
return snd_info_card_register(card);
}
#else /* !CONFIG_SND_PROC_FS */
#define init_info_for_card(card)
#endif
static int check_empty_slot(struct module *module, int slot) static int check_empty_slot(struct module *module, int slot)
{ {
return !slots[slot] || !*slots[slot]; return !slots[slot] || !*slots[slot];
@ -491,7 +466,6 @@ static int snd_card_do_free(struct snd_card *card)
snd_device_free_all(card); snd_device_free_all(card);
if (card->private_free) if (card->private_free)
card->private_free(card); card->private_free(card);
snd_info_free_entry(card->proc_id);
if (snd_info_card_free(card) < 0) { if (snd_info_card_free(card) < 0) {
dev_warn(card->dev, "unable to free card info\n"); dev_warn(card->dev, "unable to free card info\n");
/* Not fatal error */ /* Not fatal error */
@ -795,7 +769,10 @@ int snd_card_register(struct snd_card *card)
} }
snd_cards[card->number] = card; snd_cards[card->number] = card;
mutex_unlock(&snd_card_mutex); mutex_unlock(&snd_card_mutex);
init_info_for_card(card); err = snd_info_card_register(card);
if (err < 0)
return err;
#if IS_ENABLED(CONFIG_SND_MIXER_OSS) #if IS_ENABLED(CONFIG_SND_MIXER_OSS)
if (snd_mixer_oss_notify_callback) if (snd_mixer_oss_notify_callback)
snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER); snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER);

View File

@ -182,6 +182,8 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size,
return -ENXIO; return -ENXIO;
if (WARN_ON(!dmab)) if (WARN_ON(!dmab))
return -ENXIO; return -ENXIO;
if (WARN_ON(!device))
return -EINVAL;
dmab->dev.type = type; dmab->dev.type = type;
dmab->dev.dev = device; dmab->dev.dev = device;

View File

@ -2427,7 +2427,6 @@ static int snd_pcm_oss_open_file(struct file *file,
} }
pcm_oss_file->streams[idx] = substream; pcm_oss_file->streams[idx] = substream;
substream->file = pcm_oss_file;
snd_pcm_oss_init_substream(substream, &setup[idx], minor); snd_pcm_oss_init_substream(substream, &setup[idx], minor);
} }

View File

@ -528,52 +528,44 @@ static int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr)
if (!entry) if (!entry)
return -ENOMEM; return -ENOMEM;
entry->mode = S_IFDIR | 0555; entry->mode = S_IFDIR | 0555;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
return -ENOMEM;
}
pstr->proc_root = entry; pstr->proc_root = entry;
entry = snd_info_create_card_entry(pcm->card, "info", pstr->proc_root); entry = snd_info_create_card_entry(pcm->card, "info", pstr->proc_root);
if (entry) { if (entry)
snd_info_set_text_ops(entry, pstr, snd_pcm_stream_proc_info_read); snd_info_set_text_ops(entry, pstr, snd_pcm_stream_proc_info_read);
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
entry = NULL;
}
}
pstr->proc_info_entry = entry;
#ifdef CONFIG_SND_PCM_XRUN_DEBUG #ifdef CONFIG_SND_PCM_XRUN_DEBUG
entry = snd_info_create_card_entry(pcm->card, "xrun_debug", entry = snd_info_create_card_entry(pcm->card, "xrun_debug",
pstr->proc_root); pstr->proc_root);
if (entry) { if (entry) {
entry->c.text.read = snd_pcm_xrun_debug_read; snd_info_set_text_ops(entry, pstr, snd_pcm_xrun_debug_read);
entry->c.text.write = snd_pcm_xrun_debug_write; entry->c.text.write = snd_pcm_xrun_debug_write;
entry->mode |= 0200; entry->mode |= 0200;
entry->private_data = pstr;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
entry = NULL;
} }
}
pstr->proc_xrun_debug_entry = entry;
#endif #endif
return 0; return 0;
} }
static int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) static int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr)
{ {
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
snd_info_free_entry(pstr->proc_xrun_debug_entry);
pstr->proc_xrun_debug_entry = NULL;
#endif
snd_info_free_entry(pstr->proc_info_entry);
pstr->proc_info_entry = NULL;
snd_info_free_entry(pstr->proc_root); snd_info_free_entry(pstr->proc_root);
pstr->proc_root = NULL; pstr->proc_root = NULL;
return 0; return 0;
} }
static struct snd_info_entry *
create_substream_info_entry(struct snd_pcm_substream *substream,
const char *name,
void (*read)(struct snd_info_entry *,
struct snd_info_buffer *))
{
struct snd_info_entry *entry;
entry = snd_info_create_card_entry(substream->pcm->card, name,
substream->proc_root);
if (entry)
snd_info_set_text_ops(entry, substream, read);
return entry;
}
static int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) static int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream)
{ {
struct snd_info_entry *entry; struct snd_info_entry *entry;
@ -588,101 +580,61 @@ static int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream)
if (!entry) if (!entry)
return -ENOMEM; return -ENOMEM;
entry->mode = S_IFDIR | 0555; entry->mode = S_IFDIR | 0555;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
return -ENOMEM;
}
substream->proc_root = entry; substream->proc_root = entry;
entry = snd_info_create_card_entry(card, "info", substream->proc_root);
if (entry) { create_substream_info_entry(substream, "info",
snd_info_set_text_ops(entry, substream,
snd_pcm_substream_proc_info_read); snd_pcm_substream_proc_info_read);
if (snd_info_register(entry) < 0) { create_substream_info_entry(substream, "hw_params",
snd_info_free_entry(entry);
entry = NULL;
}
}
substream->proc_info_entry = entry;
entry = snd_info_create_card_entry(card, "hw_params",
substream->proc_root);
if (entry) {
snd_info_set_text_ops(entry, substream,
snd_pcm_substream_proc_hw_params_read); snd_pcm_substream_proc_hw_params_read);
if (snd_info_register(entry) < 0) { create_substream_info_entry(substream, "sw_params",
snd_info_free_entry(entry);
entry = NULL;
}
}
substream->proc_hw_params_entry = entry;
entry = snd_info_create_card_entry(card, "sw_params",
substream->proc_root);
if (entry) {
snd_info_set_text_ops(entry, substream,
snd_pcm_substream_proc_sw_params_read); snd_pcm_substream_proc_sw_params_read);
if (snd_info_register(entry) < 0) { create_substream_info_entry(substream, "status",
snd_info_free_entry(entry);
entry = NULL;
}
}
substream->proc_sw_params_entry = entry;
entry = snd_info_create_card_entry(card, "status",
substream->proc_root);
if (entry) {
snd_info_set_text_ops(entry, substream,
snd_pcm_substream_proc_status_read); snd_pcm_substream_proc_status_read);
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
entry = NULL;
}
}
substream->proc_status_entry = entry;
#ifdef CONFIG_SND_PCM_XRUN_DEBUG #ifdef CONFIG_SND_PCM_XRUN_DEBUG
entry = snd_info_create_card_entry(card, "xrun_injection", entry = create_substream_info_entry(substream, "xrun_injection", NULL);
substream->proc_root);
if (entry) { if (entry) {
entry->private_data = substream;
entry->c.text.read = NULL;
entry->c.text.write = snd_pcm_xrun_injection_write; entry->c.text.write = snd_pcm_xrun_injection_write;
entry->mode = S_IFREG | 0200; entry->mode = S_IFREG | 0200;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
entry = NULL;
} }
}
substream->proc_xrun_injection_entry = entry;
#endif /* CONFIG_SND_PCM_XRUN_DEBUG */ #endif /* CONFIG_SND_PCM_XRUN_DEBUG */
return 0; return 0;
} }
static int snd_pcm_substream_proc_done(struct snd_pcm_substream *substream)
{
snd_info_free_entry(substream->proc_info_entry);
substream->proc_info_entry = NULL;
snd_info_free_entry(substream->proc_hw_params_entry);
substream->proc_hw_params_entry = NULL;
snd_info_free_entry(substream->proc_sw_params_entry);
substream->proc_sw_params_entry = NULL;
snd_info_free_entry(substream->proc_status_entry);
substream->proc_status_entry = NULL;
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
snd_info_free_entry(substream->proc_xrun_injection_entry);
substream->proc_xrun_injection_entry = NULL;
#endif
snd_info_free_entry(substream->proc_root);
substream->proc_root = NULL;
return 0;
}
#else /* !CONFIG_SND_VERBOSE_PROCFS */ #else /* !CONFIG_SND_VERBOSE_PROCFS */
static inline int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr) { return 0; } static inline int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr) { return 0; }
static inline int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) { return 0; } static inline int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) { return 0; }
static inline int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) { return 0; } static inline int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) { return 0; }
static inline int snd_pcm_substream_proc_done(struct snd_pcm_substream *substream) { return 0; }
#endif /* CONFIG_SND_VERBOSE_PROCFS */ #endif /* CONFIG_SND_VERBOSE_PROCFS */
static const struct attribute_group *pcm_dev_attr_groups[]; static const struct attribute_group *pcm_dev_attr_groups[];
/*
* PM callbacks: we need to deal only with suspend here, as the resume is
* triggered either from user-space or the driver's resume callback
*/
#ifdef CONFIG_PM_SLEEP
static int do_pcm_suspend(struct device *dev)
{
struct snd_pcm_str *pstr = container_of(dev, struct snd_pcm_str, dev);
if (!pstr->pcm->no_device_suspend)
snd_pcm_suspend_all(pstr->pcm);
return 0;
}
#endif
static const struct dev_pm_ops pcm_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(do_pcm_suspend, NULL)
};
/* device type for PCM -- basically only for passing PM callbacks */
static const struct device_type pcm_dev_type = {
.name = "pcm",
.pm = &pcm_dev_pm_ops,
};
/** /**
* snd_pcm_new_stream - create a new PCM stream * snd_pcm_new_stream - create a new PCM stream
* @pcm: the pcm instance * @pcm: the pcm instance
@ -713,6 +665,7 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
snd_device_initialize(&pstr->dev, pcm->card); snd_device_initialize(&pstr->dev, pcm->card);
pstr->dev.groups = pcm_dev_attr_groups; pstr->dev.groups = pcm_dev_attr_groups;
pstr->dev.type = &pcm_dev_type;
dev_set_name(&pstr->dev, "pcmC%iD%i%c", pcm->card->number, pcm->device, dev_set_name(&pstr->dev, "pcmC%iD%i%c", pcm->card->number, pcm->device,
stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c'); stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c');
@ -753,9 +706,7 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
} }
} }
substream->group = &substream->self_group; substream->group = &substream->self_group;
spin_lock_init(&substream->self_group.lock); snd_pcm_group_init(&substream->self_group);
mutex_init(&substream->self_group.mutex);
INIT_LIST_HEAD(&substream->self_group.substreams);
list_add_tail(&substream->link_list, &substream->self_group.substreams); list_add_tail(&substream->link_list, &substream->self_group.substreams);
atomic_set(&substream->mmap_count, 0); atomic_set(&substream->mmap_count, 0);
prev = substream; prev = substream;
@ -885,15 +836,17 @@ static void snd_pcm_free_stream(struct snd_pcm_str * pstr)
#if IS_ENABLED(CONFIG_SND_PCM_OSS) #if IS_ENABLED(CONFIG_SND_PCM_OSS)
struct snd_pcm_oss_setup *setup, *setupn; struct snd_pcm_oss_setup *setup, *setupn;
#endif #endif
/* free all proc files under the stream */
snd_pcm_stream_proc_done(pstr);
substream = pstr->substream; substream = pstr->substream;
while (substream) { while (substream) {
substream_next = substream->next; substream_next = substream->next;
snd_pcm_timer_done(substream); snd_pcm_timer_done(substream);
snd_pcm_substream_proc_done(substream);
kfree(substream); kfree(substream);
substream = substream_next; substream = substream_next;
} }
snd_pcm_stream_proc_done(pstr);
#if IS_ENABLED(CONFIG_SND_PCM_OSS) #if IS_ENABLED(CONFIG_SND_PCM_OSS)
for (setup = pstr->oss.setup_list; setup; setup = setupn) { for (setup = pstr->oss.setup_list; setup; setup = setupn) {
setupn = setup->next; setupn = setup->next;

View File

@ -2176,6 +2176,10 @@ snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream,
if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
snd_pcm_update_hw_ptr(substream); snd_pcm_update_hw_ptr(substream);
/*
* If size < start_threshold, wait indefinitely. Another
* thread may start capture
*/
if (!is_playback && if (!is_playback &&
runtime->status->state == SNDRV_PCM_STATE_PREPARED && runtime->status->state == SNDRV_PCM_STATE_PREPARED &&
size >= runtime->start_threshold) { size >= runtime->start_threshold) {
@ -2214,9 +2218,8 @@ snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream,
if (frames > cont) if (frames > cont)
frames = cont; frames = cont;
if (snd_BUG_ON(!frames)) { if (snd_BUG_ON(!frames)) {
runtime->twake = 0; err = -EINVAL;
snd_pcm_stream_unlock_irq(substream); goto _end_unlock;
return -EINVAL;
} }
snd_pcm_stream_unlock_irq(substream); snd_pcm_stream_unlock_irq(substream);
err = writer(substream, appl_ofs, data, offset, frames, err = writer(substream, appl_ofs, data, offset, frames,

View File

@ -66,5 +66,6 @@ static inline void snd_pcm_timer_done(struct snd_pcm_substream *substream) {}
#endif #endif
void __snd_pcm_xrun(struct snd_pcm_substream *substream); void __snd_pcm_xrun(struct snd_pcm_substream *substream);
void snd_pcm_group_init(struct snd_pcm_group *group);
#endif /* __SOUND_CORE_PCM_LOCAL_H */ #endif /* __SOUND_CORE_PCM_LOCAL_H */

View File

@ -87,19 +87,10 @@ static void snd_pcm_lib_preallocate_dma_free(struct snd_pcm_substream *substream
* @substream: the pcm substream instance * @substream: the pcm substream instance
* *
* Releases the pre-allocated buffer of the given substream. * Releases the pre-allocated buffer of the given substream.
*
* Return: Zero if successful, or a negative error code on failure.
*/ */
int snd_pcm_lib_preallocate_free(struct snd_pcm_substream *substream) void snd_pcm_lib_preallocate_free(struct snd_pcm_substream *substream)
{ {
snd_pcm_lib_preallocate_dma_free(substream); snd_pcm_lib_preallocate_dma_free(substream);
#ifdef CONFIG_SND_VERBOSE_PROCFS
snd_info_free_entry(substream->proc_prealloc_max_entry);
substream->proc_prealloc_max_entry = NULL;
snd_info_free_entry(substream->proc_prealloc_entry);
substream->proc_prealloc_entry = NULL;
#endif
return 0;
} }
/** /**
@ -107,10 +98,8 @@ int snd_pcm_lib_preallocate_free(struct snd_pcm_substream *substream)
* @pcm: the pcm instance * @pcm: the pcm instance
* *
* Releases all the pre-allocated buffers on the given pcm. * Releases all the pre-allocated buffers on the given pcm.
*
* Return: Zero if successful, or a negative error code on failure.
*/ */
int snd_pcm_lib_preallocate_free_for_all(struct snd_pcm *pcm) void snd_pcm_lib_preallocate_free_for_all(struct snd_pcm *pcm)
{ {
struct snd_pcm_substream *substream; struct snd_pcm_substream *substream;
int stream; int stream;
@ -118,7 +107,6 @@ int snd_pcm_lib_preallocate_free_for_all(struct snd_pcm *pcm)
for (stream = 0; stream < 2; stream++) for (stream = 0; stream < 2; stream++)
for (substream = pcm->streams[stream].substream; substream; substream = substream->next) for (substream = pcm->streams[stream].substream; substream; substream = substream->next)
snd_pcm_lib_preallocate_free(substream); snd_pcm_lib_preallocate_free(substream);
return 0;
} }
EXPORT_SYMBOL(snd_pcm_lib_preallocate_free_for_all); EXPORT_SYMBOL(snd_pcm_lib_preallocate_free_for_all);
@ -198,26 +186,19 @@ static inline void preallocate_info_init(struct snd_pcm_substream *substream)
{ {
struct snd_info_entry *entry; struct snd_info_entry *entry;
if ((entry = snd_info_create_card_entry(substream->pcm->card, "prealloc", substream->proc_root)) != NULL) { entry = snd_info_create_card_entry(substream->pcm->card, "prealloc",
entry->c.text.read = snd_pcm_lib_preallocate_proc_read; substream->proc_root);
if (entry) {
snd_info_set_text_ops(entry, substream,
snd_pcm_lib_preallocate_proc_read);
entry->c.text.write = snd_pcm_lib_preallocate_proc_write; entry->c.text.write = snd_pcm_lib_preallocate_proc_write;
entry->mode |= 0200; entry->mode |= 0200;
entry->private_data = substream;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
entry = NULL;
} }
} entry = snd_info_create_card_entry(substream->pcm->card, "prealloc_max",
substream->proc_prealloc_entry = entry; substream->proc_root);
if ((entry = snd_info_create_card_entry(substream->pcm->card, "prealloc_max", substream->proc_root)) != NULL) { if (entry)
entry->c.text.read = snd_pcm_lib_preallocate_max_proc_read; snd_info_set_text_ops(entry, substream,
entry->private_data = substream; snd_pcm_lib_preallocate_max_proc_read);
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
entry = NULL;
}
}
substream->proc_prealloc_max_entry = entry;
} }
#else /* !CONFIG_SND_VERBOSE_PROCFS */ #else /* !CONFIG_SND_VERBOSE_PROCFS */
@ -227,7 +208,7 @@ static inline void preallocate_info_init(struct snd_pcm_substream *substream)
/* /*
* pre-allocate the buffer and create a proc file for the substream * pre-allocate the buffer and create a proc file for the substream
*/ */
static int snd_pcm_lib_preallocate_pages1(struct snd_pcm_substream *substream, static void snd_pcm_lib_preallocate_pages1(struct snd_pcm_substream *substream,
size_t size, size_t max) size_t size, size_t max)
{ {
@ -238,7 +219,6 @@ static int snd_pcm_lib_preallocate_pages1(struct snd_pcm_substream *substream,
substream->buffer_bytes_max = substream->dma_buffer.bytes; substream->buffer_bytes_max = substream->dma_buffer.bytes;
substream->dma_max = max; substream->dma_max = max;
preallocate_info_init(substream); preallocate_info_init(substream);
return 0;
} }
@ -251,16 +231,14 @@ static int snd_pcm_lib_preallocate_pages1(struct snd_pcm_substream *substream,
* @max: the max. allowed pre-allocation size * @max: the max. allowed pre-allocation size
* *
* Do pre-allocation for the given DMA buffer type. * Do pre-allocation for the given DMA buffer type.
*
* Return: Zero if successful, or a negative error code on failure.
*/ */
int snd_pcm_lib_preallocate_pages(struct snd_pcm_substream *substream, void snd_pcm_lib_preallocate_pages(struct snd_pcm_substream *substream,
int type, struct device *data, int type, struct device *data,
size_t size, size_t max) size_t size, size_t max)
{ {
substream->dma_buffer.dev.type = type; substream->dma_buffer.dev.type = type;
substream->dma_buffer.dev.dev = data; substream->dma_buffer.dev.dev = data;
return snd_pcm_lib_preallocate_pages1(substream, size, max); snd_pcm_lib_preallocate_pages1(substream, size, max);
} }
EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages); EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages);
@ -274,21 +252,17 @@ EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages);
* *
* Do pre-allocation to all substreams of the given pcm for the * Do pre-allocation to all substreams of the given pcm for the
* specified DMA type. * specified DMA type.
*
* Return: Zero if successful, or a negative error code on failure.
*/ */
int snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm, void snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm,
int type, void *data, int type, void *data,
size_t size, size_t max) size_t size, size_t max)
{ {
struct snd_pcm_substream *substream; struct snd_pcm_substream *substream;
int stream, err; int stream;
for (stream = 0; stream < 2; stream++) for (stream = 0; stream < 2; stream++)
for (substream = pcm->streams[stream].substream; substream; substream = substream->next) for (substream = pcm->streams[stream].substream; substream; substream = substream->next)
if ((err = snd_pcm_lib_preallocate_pages(substream, type, data, size, max)) < 0) snd_pcm_lib_preallocate_pages(substream, type, data, size, max);
return err;
return 0;
} }
EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all); EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all);

View File

@ -85,71 +85,30 @@ static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream);
* *
*/ */
static DEFINE_RWLOCK(snd_pcm_link_rwlock);
static DECLARE_RWSEM(snd_pcm_link_rwsem); static DECLARE_RWSEM(snd_pcm_link_rwsem);
/* Writer in rwsem may block readers even during its waiting in queue, void snd_pcm_group_init(struct snd_pcm_group *group)
* and this may lead to a deadlock when the code path takes read sem
* twice (e.g. one in snd_pcm_action_nonatomic() and another in
* snd_pcm_stream_lock()). As a (suboptimal) workaround, let writer to
* sleep until all the readers are completed without blocking by writer.
*/
static inline void down_write_nonfifo(struct rw_semaphore *lock)
{ {
while (!down_write_trylock(lock)) spin_lock_init(&group->lock);
msleep(1); mutex_init(&group->mutex);
INIT_LIST_HEAD(&group->substreams);
refcount_set(&group->refs, 0);
} }
#define PCM_LOCK_DEFAULT 0 /* define group lock helpers */
#define PCM_LOCK_IRQ 1 #define DEFINE_PCM_GROUP_LOCK(action, mutex_action) \
#define PCM_LOCK_IRQSAVE 2 static void snd_pcm_group_ ## action(struct snd_pcm_group *group, bool nonatomic) \
{ \
static unsigned long __snd_pcm_stream_lock_mode(struct snd_pcm_substream *substream, if (nonatomic) \
unsigned int mode) mutex_ ## mutex_action(&group->mutex); \
{ else \
unsigned long flags = 0; spin_ ## action(&group->lock); \
if (substream->pcm->nonatomic) {
down_read_nested(&snd_pcm_link_rwsem, SINGLE_DEPTH_NESTING);
mutex_lock(&substream->self_group.mutex);
} else {
switch (mode) {
case PCM_LOCK_DEFAULT:
read_lock(&snd_pcm_link_rwlock);
break;
case PCM_LOCK_IRQ:
read_lock_irq(&snd_pcm_link_rwlock);
break;
case PCM_LOCK_IRQSAVE:
read_lock_irqsave(&snd_pcm_link_rwlock, flags);
break;
}
spin_lock(&substream->self_group.lock);
}
return flags;
} }
static void __snd_pcm_stream_unlock_mode(struct snd_pcm_substream *substream, DEFINE_PCM_GROUP_LOCK(lock, lock);
unsigned int mode, unsigned long flags) DEFINE_PCM_GROUP_LOCK(unlock, unlock);
{ DEFINE_PCM_GROUP_LOCK(lock_irq, lock);
if (substream->pcm->nonatomic) { DEFINE_PCM_GROUP_LOCK(unlock_irq, unlock);
mutex_unlock(&substream->self_group.mutex);
up_read(&snd_pcm_link_rwsem);
} else {
spin_unlock(&substream->self_group.lock);
switch (mode) {
case PCM_LOCK_DEFAULT:
read_unlock(&snd_pcm_link_rwlock);
break;
case PCM_LOCK_IRQ:
read_unlock_irq(&snd_pcm_link_rwlock);
break;
case PCM_LOCK_IRQSAVE:
read_unlock_irqrestore(&snd_pcm_link_rwlock, flags);
break;
}
}
}
/** /**
* snd_pcm_stream_lock - Lock the PCM stream * snd_pcm_stream_lock - Lock the PCM stream
@ -161,7 +120,7 @@ static void __snd_pcm_stream_unlock_mode(struct snd_pcm_substream *substream,
*/ */
void snd_pcm_stream_lock(struct snd_pcm_substream *substream) void snd_pcm_stream_lock(struct snd_pcm_substream *substream)
{ {
__snd_pcm_stream_lock_mode(substream, PCM_LOCK_DEFAULT); snd_pcm_group_lock(&substream->self_group, substream->pcm->nonatomic);
} }
EXPORT_SYMBOL_GPL(snd_pcm_stream_lock); EXPORT_SYMBOL_GPL(snd_pcm_stream_lock);
@ -173,7 +132,7 @@ EXPORT_SYMBOL_GPL(snd_pcm_stream_lock);
*/ */
void snd_pcm_stream_unlock(struct snd_pcm_substream *substream) void snd_pcm_stream_unlock(struct snd_pcm_substream *substream)
{ {
__snd_pcm_stream_unlock_mode(substream, PCM_LOCK_DEFAULT, 0); snd_pcm_group_unlock(&substream->self_group, substream->pcm->nonatomic);
} }
EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock); EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock);
@ -187,7 +146,8 @@ EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock);
*/ */
void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream) void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream)
{ {
__snd_pcm_stream_lock_mode(substream, PCM_LOCK_IRQ); snd_pcm_group_lock_irq(&substream->self_group,
substream->pcm->nonatomic);
} }
EXPORT_SYMBOL_GPL(snd_pcm_stream_lock_irq); EXPORT_SYMBOL_GPL(snd_pcm_stream_lock_irq);
@ -199,13 +159,19 @@ EXPORT_SYMBOL_GPL(snd_pcm_stream_lock_irq);
*/ */
void snd_pcm_stream_unlock_irq(struct snd_pcm_substream *substream) void snd_pcm_stream_unlock_irq(struct snd_pcm_substream *substream)
{ {
__snd_pcm_stream_unlock_mode(substream, PCM_LOCK_IRQ, 0); snd_pcm_group_unlock_irq(&substream->self_group,
substream->pcm->nonatomic);
} }
EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irq); EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irq);
unsigned long _snd_pcm_stream_lock_irqsave(struct snd_pcm_substream *substream) unsigned long _snd_pcm_stream_lock_irqsave(struct snd_pcm_substream *substream)
{ {
return __snd_pcm_stream_lock_mode(substream, PCM_LOCK_IRQSAVE); unsigned long flags = 0;
if (substream->pcm->nonatomic)
mutex_lock(&substream->self_group.mutex);
else
spin_lock_irqsave(&substream->self_group.lock, flags);
return flags;
} }
EXPORT_SYMBOL_GPL(_snd_pcm_stream_lock_irqsave); EXPORT_SYMBOL_GPL(_snd_pcm_stream_lock_irqsave);
@ -219,7 +185,10 @@ EXPORT_SYMBOL_GPL(_snd_pcm_stream_lock_irqsave);
void snd_pcm_stream_unlock_irqrestore(struct snd_pcm_substream *substream, void snd_pcm_stream_unlock_irqrestore(struct snd_pcm_substream *substream,
unsigned long flags) unsigned long flags)
{ {
__snd_pcm_stream_unlock_mode(substream, PCM_LOCK_IRQSAVE, flags); if (substream->pcm->nonatomic)
mutex_unlock(&substream->self_group.mutex);
else
spin_unlock_irqrestore(&substream->self_group.lock, flags);
} }
EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irqrestore); EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irqrestore);
@ -1124,6 +1093,68 @@ static int snd_pcm_action_single(const struct action_ops *ops,
return res; return res;
} }
static void snd_pcm_group_assign(struct snd_pcm_substream *substream,
struct snd_pcm_group *new_group)
{
substream->group = new_group;
list_move(&substream->link_list, &new_group->substreams);
}
/*
* Unref and unlock the group, but keep the stream lock;
* when the group becomes empty and no longer referred, destroy itself
*/
static void snd_pcm_group_unref(struct snd_pcm_group *group,
struct snd_pcm_substream *substream)
{
bool do_free;
if (!group)
return;
do_free = refcount_dec_and_test(&group->refs) &&
list_empty(&group->substreams);
snd_pcm_group_unlock(group, substream->pcm->nonatomic);
if (do_free)
kfree(group);
}
/*
* Lock the group inside a stream lock and reference it;
* return the locked group object, or NULL if not linked
*/
static struct snd_pcm_group *
snd_pcm_stream_group_ref(struct snd_pcm_substream *substream)
{
bool nonatomic = substream->pcm->nonatomic;
struct snd_pcm_group *group;
bool trylock;
for (;;) {
if (!snd_pcm_stream_linked(substream))
return NULL;
group = substream->group;
/* block freeing the group object */
refcount_inc(&group->refs);
trylock = nonatomic ? mutex_trylock(&group->mutex) :
spin_trylock(&group->lock);
if (trylock)
break; /* OK */
/* re-lock for avoiding ABBA deadlock */
snd_pcm_stream_unlock(substream);
snd_pcm_group_lock(group, nonatomic);
snd_pcm_stream_lock(substream);
/* check the group again; the above opens a small race window */
if (substream->group == group)
break; /* OK */
/* group changed, try again */
snd_pcm_group_unref(group, substream);
}
return group;
}
/* /*
* Note: call with stream lock * Note: call with stream lock
*/ */
@ -1131,28 +1162,15 @@ static int snd_pcm_action(const struct action_ops *ops,
struct snd_pcm_substream *substream, struct snd_pcm_substream *substream,
int state) int state)
{ {
struct snd_pcm_group *group;
int res; int res;
if (!snd_pcm_stream_linked(substream)) group = snd_pcm_stream_group_ref(substream);
return snd_pcm_action_single(ops, substream, state); if (group)
if (substream->pcm->nonatomic) {
if (!mutex_trylock(&substream->group->mutex)) {
mutex_unlock(&substream->self_group.mutex);
mutex_lock(&substream->group->mutex);
mutex_lock(&substream->self_group.mutex);
}
res = snd_pcm_action_group(ops, substream, state, 1); res = snd_pcm_action_group(ops, substream, state, 1);
mutex_unlock(&substream->group->mutex); else
} else { res = snd_pcm_action_single(ops, substream, state);
if (!spin_trylock(&substream->group->lock)) { snd_pcm_group_unref(group, substream);
spin_unlock(&substream->self_group.lock);
spin_lock(&substream->group->lock);
spin_lock(&substream->self_group.lock);
}
res = snd_pcm_action_group(ops, substream, state, 1);
spin_unlock(&substream->group->lock);
}
return res; return res;
} }
@ -1179,6 +1197,7 @@ static int snd_pcm_action_nonatomic(const struct action_ops *ops,
{ {
int res; int res;
/* Guarantee the group members won't change during non-atomic action */
down_read(&snd_pcm_link_rwsem); down_read(&snd_pcm_link_rwsem);
if (snd_pcm_stream_linked(substream)) if (snd_pcm_stream_linked(substream))
res = snd_pcm_action_group(ops, substream, state, 0); res = snd_pcm_action_group(ops, substream, state, 0);
@ -1460,29 +1479,24 @@ static const struct action_ops snd_pcm_action_suspend = {
.post_action = snd_pcm_post_suspend .post_action = snd_pcm_post_suspend
}; };
/** /*
* snd_pcm_suspend - trigger SUSPEND to all linked streams * snd_pcm_suspend - trigger SUSPEND to all linked streams
* @substream: the PCM substream * @substream: the PCM substream
* *
* After this call, all streams are changed to SUSPENDED state. * After this call, all streams are changed to SUSPENDED state.
* *
* Return: Zero if successful (or @substream is %NULL), or a negative error * Return: Zero if successful, or a negative error code.
* code.
*/ */
int snd_pcm_suspend(struct snd_pcm_substream *substream) static int snd_pcm_suspend(struct snd_pcm_substream *substream)
{ {
int err; int err;
unsigned long flags; unsigned long flags;
if (! substream)
return 0;
snd_pcm_stream_lock_irqsave(substream, flags); snd_pcm_stream_lock_irqsave(substream, flags);
err = snd_pcm_action(&snd_pcm_action_suspend, substream, 0); err = snd_pcm_action(&snd_pcm_action_suspend, substream, 0);
snd_pcm_stream_unlock_irqrestore(substream, flags); snd_pcm_stream_unlock_irqrestore(substream, flags);
return err; return err;
} }
EXPORT_SYMBOL(snd_pcm_suspend);
/** /**
* snd_pcm_suspend_all - trigger SUSPEND to all substreams in the given pcm * snd_pcm_suspend_all - trigger SUSPEND to all substreams in the given pcm
@ -1506,6 +1520,14 @@ int snd_pcm_suspend_all(struct snd_pcm *pcm)
/* FIXME: the open/close code should lock this as well */ /* FIXME: the open/close code should lock this as well */
if (substream->runtime == NULL) if (substream->runtime == NULL)
continue; continue;
/*
* Skip BE dai link PCM's that are internal and may
* not have their substream ops set.
*/
if (!substream->ops)
continue;
err = snd_pcm_suspend(substream); err = snd_pcm_suspend(substream);
if (err < 0 && err != -EBUSY) if (err < 0 && err != -EBUSY)
return err; return err;
@ -1792,8 +1814,6 @@ static const struct action_ops snd_pcm_action_drain_init = {
.post_action = snd_pcm_post_drain_init .post_action = snd_pcm_post_drain_init
}; };
static int snd_pcm_drop(struct snd_pcm_substream *substream);
/* /*
* Drain the stream(s). * Drain the stream(s).
* When the substream is linked, sync until the draining of all playback streams * When the substream is linked, sync until the draining of all playback streams
@ -1807,6 +1827,7 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
struct snd_card *card; struct snd_card *card;
struct snd_pcm_runtime *runtime; struct snd_pcm_runtime *runtime;
struct snd_pcm_substream *s; struct snd_pcm_substream *s;
struct snd_pcm_group *group;
wait_queue_entry_t wait; wait_queue_entry_t wait;
int result = 0; int result = 0;
int nonblock = 0; int nonblock = 0;
@ -1823,7 +1844,6 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
} else if (substream->f_flags & O_NONBLOCK) } else if (substream->f_flags & O_NONBLOCK)
nonblock = 1; nonblock = 1;
down_read(&snd_pcm_link_rwsem);
snd_pcm_stream_lock_irq(substream); snd_pcm_stream_lock_irq(substream);
/* resume pause */ /* resume pause */
if (runtime->status->state == SNDRV_PCM_STATE_PAUSED) if (runtime->status->state == SNDRV_PCM_STATE_PAUSED)
@ -1848,6 +1868,7 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
} }
/* find a substream to drain */ /* find a substream to drain */
to_check = NULL; to_check = NULL;
group = snd_pcm_stream_group_ref(substream);
snd_pcm_group_for_each_entry(s, substream) { snd_pcm_group_for_each_entry(s, substream) {
if (s->stream != SNDRV_PCM_STREAM_PLAYBACK) if (s->stream != SNDRV_PCM_STREAM_PLAYBACK)
continue; continue;
@ -1857,12 +1878,12 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
break; break;
} }
} }
snd_pcm_group_unref(group, substream);
if (!to_check) if (!to_check)
break; /* all drained */ break; /* all drained */
init_waitqueue_entry(&wait, current); init_waitqueue_entry(&wait, current);
add_wait_queue(&to_check->sleep, &wait); add_wait_queue(&to_check->sleep, &wait);
snd_pcm_stream_unlock_irq(substream); snd_pcm_stream_unlock_irq(substream);
up_read(&snd_pcm_link_rwsem);
if (runtime->no_period_wakeup) if (runtime->no_period_wakeup)
tout = MAX_SCHEDULE_TIMEOUT; tout = MAX_SCHEDULE_TIMEOUT;
else { else {
@ -1874,9 +1895,17 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
tout = msecs_to_jiffies(tout * 1000); tout = msecs_to_jiffies(tout * 1000);
} }
tout = schedule_timeout_interruptible(tout); tout = schedule_timeout_interruptible(tout);
down_read(&snd_pcm_link_rwsem);
snd_pcm_stream_lock_irq(substream); snd_pcm_stream_lock_irq(substream);
group = snd_pcm_stream_group_ref(substream);
snd_pcm_group_for_each_entry(s, substream) {
if (s->runtime == to_check) {
remove_wait_queue(&to_check->sleep, &wait); remove_wait_queue(&to_check->sleep, &wait);
break;
}
}
snd_pcm_group_unref(group, substream);
if (card->shutdown) { if (card->shutdown) {
result = -ENODEV; result = -ENODEV;
break; break;
@ -1896,7 +1925,6 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
unlock: unlock:
snd_pcm_stream_unlock_irq(substream); snd_pcm_stream_unlock_irq(substream);
up_read(&snd_pcm_link_rwsem);
return result; return result;
} }
@ -1935,13 +1963,19 @@ static int snd_pcm_drop(struct snd_pcm_substream *substream)
static bool is_pcm_file(struct file *file) static bool is_pcm_file(struct file *file)
{ {
struct inode *inode = file_inode(file); struct inode *inode = file_inode(file);
struct snd_pcm *pcm;
unsigned int minor; unsigned int minor;
if (!S_ISCHR(inode->i_mode) || imajor(inode) != snd_major) if (!S_ISCHR(inode->i_mode) || imajor(inode) != snd_major)
return false; return false;
minor = iminor(inode); minor = iminor(inode);
return snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_PLAYBACK) || pcm = snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_PLAYBACK);
snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_CAPTURE); if (!pcm)
pcm = snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_CAPTURE);
if (!pcm)
return false;
snd_card_unref(pcm->card);
return true;
} }
/* /*
@ -1952,7 +1986,8 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
int res = 0; int res = 0;
struct snd_pcm_file *pcm_file; struct snd_pcm_file *pcm_file;
struct snd_pcm_substream *substream1; struct snd_pcm_substream *substream1;
struct snd_pcm_group *group; struct snd_pcm_group *group, *target_group;
bool nonatomic = substream->pcm->nonatomic;
struct fd f = fdget(fd); struct fd f = fdget(fd);
if (!f.file) if (!f.file)
@ -1963,13 +1998,14 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
} }
pcm_file = f.file->private_data; pcm_file = f.file->private_data;
substream1 = pcm_file->substream; substream1 = pcm_file->substream;
group = kmalloc(sizeof(*group), GFP_KERNEL); group = kzalloc(sizeof(*group), GFP_KERNEL);
if (!group) { if (!group) {
res = -ENOMEM; res = -ENOMEM;
goto _nolock; goto _nolock;
} }
down_write_nonfifo(&snd_pcm_link_rwsem); snd_pcm_group_init(group);
write_lock_irq(&snd_pcm_link_rwlock);
down_write(&snd_pcm_link_rwsem);
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN || if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN ||
substream->runtime->status->state != substream1->runtime->status->state || substream->runtime->status->state != substream1->runtime->status->state ||
substream->pcm->nonatomic != substream1->pcm->nonatomic) { substream->pcm->nonatomic != substream1->pcm->nonatomic) {
@ -1980,23 +2016,23 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
res = -EALREADY; res = -EALREADY;
goto _end; goto _end;
} }
snd_pcm_stream_lock_irq(substream);
if (!snd_pcm_stream_linked(substream)) { if (!snd_pcm_stream_linked(substream)) {
substream->group = group; snd_pcm_group_assign(substream, group);
group = NULL; group = NULL; /* assigned, don't free this one below */
spin_lock_init(&substream->group->lock);
mutex_init(&substream->group->mutex);
INIT_LIST_HEAD(&substream->group->substreams);
list_add_tail(&substream->link_list, &substream->group->substreams);
substream->group->count = 1;
} }
list_add_tail(&substream1->link_list, &substream->group->substreams); target_group = substream->group;
substream->group->count++; snd_pcm_stream_unlock_irq(substream);
substream1->group = substream->group;
snd_pcm_group_lock_irq(target_group, nonatomic);
snd_pcm_stream_lock(substream1);
snd_pcm_group_assign(substream1, target_group);
snd_pcm_stream_unlock(substream1);
snd_pcm_group_unlock_irq(target_group, nonatomic);
_end: _end:
write_unlock_irq(&snd_pcm_link_rwlock);
up_write(&snd_pcm_link_rwsem); up_write(&snd_pcm_link_rwsem);
_nolock: _nolock:
snd_card_unref(substream1->pcm->card);
kfree(group); kfree(group);
_badf: _badf:
fdput(f); fdput(f);
@ -2005,34 +2041,43 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
static void relink_to_local(struct snd_pcm_substream *substream) static void relink_to_local(struct snd_pcm_substream *substream)
{ {
substream->group = &substream->self_group; snd_pcm_stream_lock(substream);
INIT_LIST_HEAD(&substream->self_group.substreams); snd_pcm_group_assign(substream, &substream->self_group);
list_add_tail(&substream->link_list, &substream->self_group.substreams); snd_pcm_stream_unlock(substream);
} }
static int snd_pcm_unlink(struct snd_pcm_substream *substream) static int snd_pcm_unlink(struct snd_pcm_substream *substream)
{ {
struct snd_pcm_substream *s; struct snd_pcm_group *group;
bool nonatomic = substream->pcm->nonatomic;
bool do_free = false;
int res = 0; int res = 0;
down_write_nonfifo(&snd_pcm_link_rwsem); down_write(&snd_pcm_link_rwsem);
write_lock_irq(&snd_pcm_link_rwlock);
if (!snd_pcm_stream_linked(substream)) { if (!snd_pcm_stream_linked(substream)) {
res = -EALREADY; res = -EALREADY;
goto _end; goto _end;
} }
list_del(&substream->link_list);
substream->group->count--; group = substream->group;
if (substream->group->count == 1) { /* detach the last stream, too */ snd_pcm_group_lock_irq(group, nonatomic);
snd_pcm_group_for_each_entry(s, substream) {
relink_to_local(s);
break;
}
kfree(substream->group);
}
relink_to_local(substream); relink_to_local(substream);
/* detach the last stream, too */
if (list_is_singular(&group->substreams)) {
relink_to_local(list_first_entry(&group->substreams,
struct snd_pcm_substream,
link_list));
do_free = !refcount_read(&group->refs);
}
snd_pcm_group_unlock_irq(group, nonatomic);
if (do_free)
kfree(group);
_end: _end:
write_unlock_irq(&snd_pcm_link_rwlock);
up_write(&snd_pcm_link_rwsem); up_write(&snd_pcm_link_rwsem);
return res; return res;
} }
@ -2457,10 +2502,8 @@ static int snd_pcm_open_file(struct file *file,
return -ENOMEM; return -ENOMEM;
} }
pcm_file->substream = substream; pcm_file->substream = substream;
if (substream->ref_count == 1) { if (substream->ref_count == 1)
substream->file = pcm_file;
substream->pcm_release = pcm_release_private; substream->pcm_release = pcm_release_private;
}
file->private_data = pcm_file; file->private_data = pcm_file;
return 0; return 0;

View File

@ -1133,16 +1133,10 @@ static void print_cable_info(struct snd_info_entry *entry,
static int loopback_proc_new(struct loopback *loopback, int cidx) static int loopback_proc_new(struct loopback *loopback, int cidx)
{ {
char name[32]; char name[32];
struct snd_info_entry *entry;
int err;
snprintf(name, sizeof(name), "cable#%d", cidx); snprintf(name, sizeof(name), "cable#%d", cidx);
err = snd_card_proc_new(loopback->card, name, &entry); return snd_card_ro_proc_new(loopback->card, name, loopback,
if (err < 0) print_cable_info);
return err;
snd_info_set_text_ops(entry, loopback, print_cable_info);
return 0;
} }
static int loopback_probe(struct platform_device *devptr) static int loopback_probe(struct platform_device *devptr)
@ -1200,12 +1194,8 @@ static int loopback_remove(struct platform_device *devptr)
static int loopback_suspend(struct device *pdev) static int loopback_suspend(struct device *pdev)
{ {
struct snd_card *card = dev_get_drvdata(pdev); struct snd_card *card = dev_get_drvdata(pdev);
struct loopback *loopback = card->private_data;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
snd_pcm_suspend_all(loopback->pcm[0]);
snd_pcm_suspend_all(loopback->pcm[1]);
return 0; return 0;
} }

View File

@ -1037,14 +1037,8 @@ static void dummy_proc_write(struct snd_info_entry *entry,
static void dummy_proc_init(struct snd_dummy *chip) static void dummy_proc_init(struct snd_dummy *chip)
{ {
struct snd_info_entry *entry; snd_card_rw_proc_new(chip->card, "dummy_pcm", chip,
dummy_proc_read, dummy_proc_write);
if (!snd_card_proc_new(chip->card, "dummy_pcm", &entry)) {
snd_info_set_text_ops(entry, chip, dummy_proc_read);
entry->c.text.write = dummy_proc_write;
entry->mode |= 0200;
entry->private_data = chip;
}
} }
#else #else
#define dummy_proc_init(x) #define dummy_proc_init(x)
@ -1138,10 +1132,8 @@ static int snd_dummy_remove(struct platform_device *devptr)
static int snd_dummy_suspend(struct device *pdev) static int snd_dummy_suspend(struct device *pdev)
{ {
struct snd_card *card = dev_get_drvdata(pdev); struct snd_card *card = dev_get_drvdata(pdev);
struct snd_dummy *dummy = card->private_data;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
snd_pcm_suspend_all(dummy->pcm);
return 0; return 0;
} }

View File

@ -114,10 +114,6 @@ int snd_opl4_create_proc(struct snd_opl4 *opl4)
entry->c.ops = &snd_opl4_mem_proc_ops; entry->c.ops = &snd_opl4_mem_proc_ops;
entry->module = THIS_MODULE; entry->module = THIS_MODULE;
entry->private_data = opl4; entry->private_data = opl4;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
entry = NULL;
}
} }
opl4->proc_entry = entry; opl4->proc_entry = entry;
return 0; return 0;

View File

@ -197,7 +197,6 @@ static int pcsp_suspend(struct device *dev)
{ {
struct snd_pcsp *chip = dev_get_drvdata(dev); struct snd_pcsp *chip = dev_get_drvdata(dev);
pcsp_stop_beep(chip); pcsp_stop_beep(chip);
snd_pcm_suspend_all(chip->pcm);
return 0; return 0;
} }

View File

@ -643,10 +643,7 @@ static void vx_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *b
static void vx_proc_init(struct vx_core *chip) static void vx_proc_init(struct vx_core *chip)
{ {
struct snd_info_entry *entry; snd_card_ro_proc_new(chip->card, "vx-status", chip, vx_proc_read);
if (! snd_card_proc_new(chip->card, "vx-status", &entry))
snd_info_set_text_ops(entry, chip, vx_proc_read);
} }
@ -732,12 +729,8 @@ EXPORT_SYMBOL(snd_vx_dsp_load);
*/ */
int snd_vx_suspend(struct vx_core *chip) int snd_vx_suspend(struct vx_core *chip)
{ {
unsigned int i;
snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot); snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
chip->chip_status |= VX_STAT_IN_SUSPEND; chip->chip_status |= VX_STAT_IN_SUSPEND;
for (i = 0; i < chip->hw->num_codecs; i++)
snd_pcm_suspend_all(chip->pcm[i]);
return 0; return 0;
} }

View File

@ -163,5 +163,6 @@ config SND_FIREFACE
Say Y here to include support for RME fireface series. Say Y here to include support for RME fireface series.
* Fireface 400 * Fireface 400
* Fireface 800 * Fireface 800
* Fireface UCX
endif # SND_FIREWIRE endif # SND_FIREWIRE

View File

@ -448,7 +448,19 @@ static const struct ieee1394_device_id bebob_id_table[] = {
/* Focusrite, SaffirePro 26 I/O */ /* Focusrite, SaffirePro 26 I/O */
SND_BEBOB_DEV_ENTRY(VEN_FOCUSRITE, 0x00000003, &saffirepro_26_spec), SND_BEBOB_DEV_ENTRY(VEN_FOCUSRITE, 0x00000003, &saffirepro_26_spec),
/* Focusrite, SaffirePro 10 I/O */ /* Focusrite, SaffirePro 10 I/O */
SND_BEBOB_DEV_ENTRY(VEN_FOCUSRITE, 0x00000006, &saffirepro_10_spec), {
// The combination of vendor_id and model_id is the same as the
// same as the one of Liquid Saffire 56.
.match_flags = IEEE1394_MATCH_VENDOR_ID |
IEEE1394_MATCH_MODEL_ID |
IEEE1394_MATCH_SPECIFIER_ID |
IEEE1394_MATCH_VERSION,
.vendor_id = VEN_FOCUSRITE,
.model_id = 0x000006,
.specifier_id = 0x00a02d,
.version = 0x010001,
.driver_data = (kernel_ulong_t)&saffirepro_10_spec,
},
/* Focusrite, Saffire(no label and LE) */ /* Focusrite, Saffire(no label and LE) */
SND_BEBOB_DEV_ENTRY(VEN_FOCUSRITE, MODEL_FOCUSRITE_SAFFIRE_BOTH, SND_BEBOB_DEV_ENTRY(VEN_FOCUSRITE, MODEL_FOCUSRITE_SAFFIRE_BOTH,
&saffire_spec), &saffire_spec),

View File

@ -163,12 +163,8 @@ add_node(struct snd_bebob *bebob, struct snd_info_entry *root, const char *name,
struct snd_info_entry *entry; struct snd_info_entry *entry;
entry = snd_info_create_card_entry(bebob->card, name, root); entry = snd_info_create_card_entry(bebob->card, name, root);
if (entry == NULL) if (entry)
return;
snd_info_set_text_ops(entry, bebob, op); snd_info_set_text_ops(entry, bebob, op);
if (snd_info_register(entry) < 0)
snd_info_free_entry(entry);
} }
void snd_bebob_proc_init(struct snd_bebob *bebob) void snd_bebob_proc_init(struct snd_bebob *bebob)
@ -184,10 +180,6 @@ void snd_bebob_proc_init(struct snd_bebob *bebob)
if (root == NULL) if (root == NULL)
return; return;
root->mode = S_IFDIR | 0555; root->mode = S_IFDIR | 0555;
if (snd_info_register(root) < 0) {
snd_info_free_entry(root);
return;
}
add_node(bebob, root, "clock", proc_read_clock); add_node(bebob, root, "clock", proc_read_clock);
add_node(bebob, root, "firmware", proc_read_hw_info); add_node(bebob, root, "firmware", proc_read_hw_info);

View File

@ -285,12 +285,8 @@ static void add_node(struct snd_dice *dice, struct snd_info_entry *root,
struct snd_info_entry *entry; struct snd_info_entry *entry;
entry = snd_info_create_card_entry(dice->card, name, root); entry = snd_info_create_card_entry(dice->card, name, root);
if (!entry) if (entry)
return;
snd_info_set_text_ops(entry, dice, op); snd_info_set_text_ops(entry, dice, op);
if (snd_info_register(entry) < 0)
snd_info_free_entry(entry);
} }
void snd_dice_create_proc(struct snd_dice *dice) void snd_dice_create_proc(struct snd_dice *dice)
@ -306,10 +302,6 @@ void snd_dice_create_proc(struct snd_dice *dice)
if (!root) if (!root)
return; return;
root->mode = S_IFDIR | 0555; root->mode = S_IFDIR | 0555;
if (snd_info_register(root) < 0) {
snd_info_free_entry(root);
return;
}
add_node(dice, root, "dice", dice_proc_read); add_node(dice, root, "dice", dice_proc_read);
add_node(dice, root, "formation", dice_proc_read_formation); add_node(dice, root, "formation", dice_proc_read_formation);

View File

@ -18,6 +18,7 @@ MODULE_LICENSE("GPL v2");
#define OUI_ALESIS 0x000595 #define OUI_ALESIS 0x000595
#define OUI_MAUDIO 0x000d6c #define OUI_MAUDIO 0x000d6c
#define OUI_MYTEK 0x001ee8 #define OUI_MYTEK 0x001ee8
#define OUI_SSL 0x0050c2 // Actually ID reserved by IEEE.
#define DICE_CATEGORY_ID 0x04 #define DICE_CATEGORY_ID 0x04
#define WEISS_CATEGORY_ID 0x00 #define WEISS_CATEGORY_ID 0x00
@ -196,7 +197,7 @@ static int dice_probe(struct fw_unit *unit,
struct snd_dice *dice; struct snd_dice *dice;
int err; int err;
if (!entry->driver_data) { if (!entry->driver_data && entry->vendor_id != OUI_SSL) {
err = check_dice_category(unit); err = check_dice_category(unit);
if (err < 0) if (err < 0)
return -ENODEV; return -ENODEV;
@ -361,6 +362,15 @@ static const struct ieee1394_device_id dice_id_table[] = {
.model_id = 0x000002, .model_id = 0x000002,
.driver_data = (kernel_ulong_t)snd_dice_detect_mytek_formats, .driver_data = (kernel_ulong_t)snd_dice_detect_mytek_formats,
}, },
// Solid State Logic, Duende Classic and Mini.
// NOTE: each field of GUID in config ROM is not compliant to standard
// DICE scheme.
{
.match_flags = IEEE1394_MATCH_VENDOR_ID |
IEEE1394_MATCH_MODEL_ID,
.vendor_id = OUI_SSL,
.model_id = 0x000070,
},
{ {
.match_flags = IEEE1394_MATCH_VERSION, .match_flags = IEEE1394_MATCH_VERSION,
.version = DICE_INTERFACE, .version = DICE_INTERFACE,

View File

@ -80,20 +80,8 @@ void snd_dg00x_proc_init(struct snd_dg00x *dg00x)
return; return;
root->mode = S_IFDIR | 0555; root->mode = S_IFDIR | 0555;
if (snd_info_register(root) < 0) {
snd_info_free_entry(root);
return;
}
entry = snd_info_create_card_entry(dg00x->card, "clock", root); entry = snd_info_create_card_entry(dg00x->card, "clock", root);
if (entry == NULL) { if (entry)
snd_info_free_entry(root);
return;
}
snd_info_set_text_ops(entry, dg00x, proc_read_clock); snd_info_set_text_ops(entry, dg00x, proc_read_clock);
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
snd_info_free_entry(root);
}
} }

View File

@ -1,4 +1,4 @@
snd-fireface-objs := ff.o ff-transaction.o ff-midi.o ff-proc.o amdtp-ff.o \ snd-fireface-objs := ff.o ff-transaction.o ff-midi.o ff-proc.o amdtp-ff.o \
ff-stream.o ff-pcm.o ff-hwdep.o ff-protocol-ff400.o \ ff-stream.o ff-pcm.o ff-hwdep.o ff-protocol-former.o \
ff-protocol-ff800.o ff-protocol-latter.o
obj-$(CONFIG_SND_FIREFACE) += snd-fireface.o obj-$(CONFIG_SND_FIREFACE) += snd-fireface.o

View File

@ -19,7 +19,7 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream)
struct snd_ff *ff = substream->rmidi->private_data; struct snd_ff *ff = substream->rmidi->private_data;
/* Initialize internal status. */ /* Initialize internal status. */
ff->running_status[substream->number] = 0; ff->on_sysex[substream->number] = 0;
ff->rx_midi_error[substream->number] = false; ff->rx_midi_error[substream->number] = false;
WRITE_ONCE(ff->rx_midi_substreams[substream->number], substream); WRITE_ONCE(ff->rx_midi_substreams[substream->number], substream);

View File

@ -152,7 +152,7 @@ static int pcm_open(struct snd_pcm_substream *substream)
if (err < 0) if (err < 0)
goto release_lock; goto release_lock;
err = snd_ff_transaction_get_clock(ff, &rate, &src); err = ff->spec->protocol->get_clock(ff, &rate, &src);
if (err < 0) if (err < 0)
goto release_lock; goto release_lock;

View File

@ -8,209 +8,29 @@
#include "./ff.h" #include "./ff.h"
static void proc_dump_clock_config(struct snd_info_entry *entry, const char *snd_ff_proc_get_clk_label(enum snd_ff_clock_src src)
{
static const char *const labels[] = {
"Internal",
"S/PDIF",
"ADAT1",
"ADAT2",
"Word",
"LTC",
};
if (src >= ARRAY_SIZE(labels))
return NULL;
return labels[src];
}
static void proc_dump_status(struct snd_info_entry *entry,
struct snd_info_buffer *buffer) struct snd_info_buffer *buffer)
{ {
struct snd_ff *ff = entry->private_data; struct snd_ff *ff = entry->private_data;
__le32 reg;
u32 data;
unsigned int rate;
const char *src;
int err;
err = snd_fw_transaction(ff->unit, TCODE_READ_BLOCK_REQUEST, ff->spec->protocol->dump_status(ff, buffer);
SND_FF_REG_CLOCK_CONFIG, &reg, sizeof(reg), 0);
if (err < 0)
return;
data = le32_to_cpu(reg);
snd_iprintf(buffer, "Output S/PDIF format: %s (Emphasis: %s)\n",
(data & 0x20) ? "Professional" : "Consumer",
(data & 0x40) ? "on" : "off");
snd_iprintf(buffer, "Optical output interface format: %s\n",
((data >> 8) & 0x01) ? "S/PDIF" : "ADAT");
snd_iprintf(buffer, "Word output single speed: %s\n",
((data >> 8) & 0x20) ? "on" : "off");
snd_iprintf(buffer, "S/PDIF input interface: %s\n",
((data >> 8) & 0x02) ? "Optical" : "Coaxial");
switch ((data >> 1) & 0x03) {
case 0x01:
rate = 32000;
break;
case 0x00:
rate = 44100;
break;
case 0x03:
rate = 48000;
break;
case 0x02:
default:
return;
}
if (data & 0x08)
rate *= 2;
else if (data & 0x10)
rate *= 4;
snd_iprintf(buffer, "Sampling rate: %d\n", rate);
if (data & 0x01) {
src = "Internal";
} else {
switch ((data >> 10) & 0x07) {
case 0x00:
src = "ADAT1";
break;
case 0x01:
src = "ADAT2";
break;
case 0x03:
src = "S/PDIF";
break;
case 0x04:
src = "Word";
break;
case 0x05:
src = "LTC";
break;
default:
return;
}
}
snd_iprintf(buffer, "Sync to clock source: %s\n", src);
}
static void proc_dump_sync_status(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_ff *ff = entry->private_data;
__le32 reg;
u32 data;
int err;
err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST,
SND_FF_REG_SYNC_STATUS, &reg, sizeof(reg), 0);
if (err < 0)
return;
data = le32_to_cpu(reg);
snd_iprintf(buffer, "External source detection:\n");
snd_iprintf(buffer, "Word Clock:");
if ((data >> 24) & 0x20) {
if ((data >> 24) & 0x40)
snd_iprintf(buffer, "sync\n");
else
snd_iprintf(buffer, "lock\n");
} else {
snd_iprintf(buffer, "none\n");
}
snd_iprintf(buffer, "S/PDIF:");
if ((data >> 16) & 0x10) {
if ((data >> 16) & 0x04)
snd_iprintf(buffer, "sync\n");
else
snd_iprintf(buffer, "lock\n");
} else {
snd_iprintf(buffer, "none\n");
}
snd_iprintf(buffer, "ADAT1:");
if ((data >> 8) & 0x04) {
if ((data >> 8) & 0x10)
snd_iprintf(buffer, "sync\n");
else
snd_iprintf(buffer, "lock\n");
} else {
snd_iprintf(buffer, "none\n");
}
snd_iprintf(buffer, "ADAT2:");
if ((data >> 8) & 0x08) {
if ((data >> 8) & 0x20)
snd_iprintf(buffer, "sync\n");
else
snd_iprintf(buffer, "lock\n");
} else {
snd_iprintf(buffer, "none\n");
}
snd_iprintf(buffer, "\nUsed external source:\n");
if (((data >> 22) & 0x07) == 0x07) {
snd_iprintf(buffer, "None\n");
} else {
switch ((data >> 22) & 0x07) {
case 0x00:
snd_iprintf(buffer, "ADAT1:");
break;
case 0x01:
snd_iprintf(buffer, "ADAT2:");
break;
case 0x03:
snd_iprintf(buffer, "S/PDIF:");
break;
case 0x04:
snd_iprintf(buffer, "Word:");
break;
case 0x07:
snd_iprintf(buffer, "Nothing:");
break;
case 0x02:
case 0x05:
case 0x06:
default:
snd_iprintf(buffer, "unknown:");
break;
}
if ((data >> 25) & 0x07) {
switch ((data >> 25) & 0x07) {
case 0x01:
snd_iprintf(buffer, "32000\n");
break;
case 0x02:
snd_iprintf(buffer, "44100\n");
break;
case 0x03:
snd_iprintf(buffer, "48000\n");
break;
case 0x04:
snd_iprintf(buffer, "64000\n");
break;
case 0x05:
snd_iprintf(buffer, "88200\n");
break;
case 0x06:
snd_iprintf(buffer, "96000\n");
break;
case 0x07:
snd_iprintf(buffer, "128000\n");
break;
case 0x08:
snd_iprintf(buffer, "176400\n");
break;
case 0x09:
snd_iprintf(buffer, "192000\n");
break;
case 0x00:
snd_iprintf(buffer, "unknown\n");
break;
}
}
}
snd_iprintf(buffer, "Multiplied:");
snd_iprintf(buffer, "%d\n", (data & 0x3ff) * 250);
} }
static void add_node(struct snd_ff *ff, struct snd_info_entry *root, static void add_node(struct snd_ff *ff, struct snd_info_entry *root,
@ -221,12 +41,8 @@ static void add_node(struct snd_ff *ff, struct snd_info_entry *root,
struct snd_info_entry *entry; struct snd_info_entry *entry;
entry = snd_info_create_card_entry(ff->card, name, root); entry = snd_info_create_card_entry(ff->card, name, root);
if (entry == NULL) if (entry)
return;
snd_info_set_text_ops(entry, ff, op); snd_info_set_text_ops(entry, ff, op);
if (snd_info_register(entry) < 0)
snd_info_free_entry(entry);
} }
void snd_ff_proc_init(struct snd_ff *ff) void snd_ff_proc_init(struct snd_ff *ff)
@ -242,11 +58,6 @@ void snd_ff_proc_init(struct snd_ff *ff)
if (root == NULL) if (root == NULL)
return; return;
root->mode = S_IFDIR | 0555; root->mode = S_IFDIR | 0555;
if (snd_info_register(root) < 0) {
snd_info_free_entry(root);
return;
}
add_node(ff, root, "clock-config", proc_dump_clock_config); add_node(ff, root, "status", proc_dump_status);
add_node(ff, root, "sync-status", proc_dump_sync_status);
} }

View File

@ -1,161 +0,0 @@
/*
* ff-protocol-ff400.c - a part of driver for RME Fireface series
*
* Copyright (c) 2015-2017 Takashi Sakamoto
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#include <linux/delay.h>
#include "ff.h"
#define FF400_STF 0x000080100500ull
#define FF400_RX_PACKET_FORMAT 0x000080100504ull
#define FF400_ISOC_COMM_START 0x000080100508ull
#define FF400_TX_PACKET_FORMAT 0x00008010050cull
#define FF400_ISOC_COMM_STOP 0x000080100510ull
/*
* Fireface 400 manages isochronous channel number in 3 bit field. Therefore,
* we can allocate between 0 and 7 channel.
*/
static int keep_resources(struct snd_ff *ff, unsigned int rate)
{
enum snd_ff_stream_mode mode;
int i;
int err;
// Check whether the given value is supported or not.
for (i = 0; i < CIP_SFC_COUNT; i++) {
if (amdtp_rate_table[i] == rate)
break;
}
if (i >= CIP_SFC_COUNT)
return -EINVAL;
err = snd_ff_stream_get_multiplier_mode(i, &mode);
if (err < 0)
return err;
/* Keep resources for in-stream. */
ff->tx_resources.channels_mask = 0x00000000000000ffuLL;
err = fw_iso_resources_allocate(&ff->tx_resources,
amdtp_stream_get_max_payload(&ff->tx_stream),
fw_parent_device(ff->unit)->max_speed);
if (err < 0)
return err;
/* Keep resources for out-stream. */
err = amdtp_ff_set_parameters(&ff->rx_stream, rate,
ff->spec->pcm_playback_channels[mode]);
if (err < 0)
return err;
ff->rx_resources.channels_mask = 0x00000000000000ffuLL;
err = fw_iso_resources_allocate(&ff->rx_resources,
amdtp_stream_get_max_payload(&ff->rx_stream),
fw_parent_device(ff->unit)->max_speed);
if (err < 0)
fw_iso_resources_free(&ff->tx_resources);
return err;
}
static int ff400_begin_session(struct snd_ff *ff, unsigned int rate)
{
__le32 reg;
int err;
err = keep_resources(ff, rate);
if (err < 0)
return err;
/* Set the number of data blocks transferred in a second. */
reg = cpu_to_le32(rate);
err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
FF400_STF, &reg, sizeof(reg), 0);
if (err < 0)
return err;
msleep(100);
/*
* Set isochronous channel and the number of quadlets of received
* packets.
*/
reg = cpu_to_le32(((ff->rx_stream.data_block_quadlets << 3) << 8) |
ff->rx_resources.channel);
err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
FF400_RX_PACKET_FORMAT, &reg, sizeof(reg), 0);
if (err < 0)
return err;
/*
* Set isochronous channel and the number of quadlets of transmitted
* packet.
*/
/* TODO: investigate the purpose of this 0x80. */
reg = cpu_to_le32((0x80 << 24) |
(ff->tx_resources.channel << 5) |
(ff->tx_stream.data_block_quadlets));
err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
FF400_TX_PACKET_FORMAT, &reg, sizeof(reg), 0);
if (err < 0)
return err;
/* Allow to transmit packets. */
reg = cpu_to_le32(0x00000001);
return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
FF400_ISOC_COMM_START, &reg, sizeof(reg), 0);
}
static void ff400_finish_session(struct snd_ff *ff)
{
__le32 reg;
reg = cpu_to_le32(0x80000000);
snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
FF400_ISOC_COMM_STOP, &reg, sizeof(reg), 0);
}
static void ff400_handle_midi_msg(struct snd_ff *ff, __le32 *buf, size_t length)
{
int i;
for (i = 0; i < length / 4; i++) {
u32 quad = le32_to_cpu(buf[i]);
u8 byte;
unsigned int index;
struct snd_rawmidi_substream *substream;
/* Message in first port. */
/*
* This value may represent the index of this unit when the same
* units are on the same IEEE 1394 bus. This driver doesn't use
* it.
*/
index = (quad >> 8) & 0xff;
if (index > 0) {
substream = READ_ONCE(ff->tx_midi_substreams[0]);
if (substream != NULL) {
byte = quad & 0xff;
snd_rawmidi_receive(substream, &byte, 1);
}
}
/* Message in second port. */
index = (quad >> 24) & 0xff;
if (index > 0) {
substream = READ_ONCE(ff->tx_midi_substreams[1]);
if (substream != NULL) {
byte = (quad >> 16) & 0xff;
snd_rawmidi_receive(substream, &byte, 1);
}
}
}
}
const struct snd_ff_protocol snd_ff_protocol_ff400 = {
.handle_midi_msg = ff400_handle_midi_msg,
.begin_session = ff400_begin_session,
.finish_session = ff400_finish_session,
};

View File

@ -1,143 +0,0 @@
/*
* ff-protocol-ff800.c - a part of driver for RME Fireface series
*
* Copyright (c) 2018 Takashi Sakamoto
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#include <linux/delay.h>
#include "ff.h"
#define FF800_STF 0x0000fc88f000
#define FF800_RX_PACKET_FORMAT 0x0000fc88f004
#define FF800_ALLOC_TX_STREAM 0x0000fc88f008
#define FF800_ISOC_COMM_START 0x0000fc88f00c
#define FF800_TX_S800_FLAG 0x00000800
#define FF800_ISOC_COMM_STOP 0x0000fc88f010
#define FF800_TX_PACKET_ISOC_CH 0x0000801c0008
static int allocate_rx_resources(struct snd_ff *ff)
{
u32 data;
__le32 reg;
int err;
// Controllers should allocate isochronous resources for rx stream.
err = fw_iso_resources_allocate(&ff->rx_resources,
amdtp_stream_get_max_payload(&ff->rx_stream),
fw_parent_device(ff->unit)->max_speed);
if (err < 0)
return err;
// Set isochronous channel and the number of quadlets of rx packets.
data = ff->rx_stream.data_block_quadlets << 3;
data = (data << 8) | ff->rx_resources.channel;
reg = cpu_to_le32(data);
return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
FF800_RX_PACKET_FORMAT, &reg, sizeof(reg), 0);
}
static int allocate_tx_resources(struct snd_ff *ff)
{
__le32 reg;
unsigned int count;
unsigned int tx_isoc_channel;
int err;
reg = cpu_to_le32(ff->tx_stream.data_block_quadlets);
err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
FF800_ALLOC_TX_STREAM, &reg, sizeof(reg), 0);
if (err < 0)
return err;
// Wait till the format of tx packet is available.
count = 0;
while (count++ < 10) {
u32 data;
err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST,
FF800_TX_PACKET_ISOC_CH, &reg, sizeof(reg), 0);
if (err < 0)
return err;
data = le32_to_cpu(reg);
if (data != 0xffffffff) {
tx_isoc_channel = data;
break;
}
msleep(50);
}
if (count >= 10)
return -ETIMEDOUT;
// NOTE: this is a makeshift to start OHCI 1394 IR context in the
// channel. On the other hand, 'struct fw_iso_resources.allocated' is
// not true and it's not deallocated at stop.
ff->tx_resources.channel = tx_isoc_channel;
return 0;
}
static int ff800_begin_session(struct snd_ff *ff, unsigned int rate)
{
__le32 reg;
int err;
reg = cpu_to_le32(rate);
err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
FF800_STF, &reg, sizeof(reg), 0);
if (err < 0)
return err;
// If starting isochronous communication immediately, change of STF has
// no effect. In this case, the communication runs based on former STF.
// Let's sleep for a bit.
msleep(100);
err = allocate_rx_resources(ff);
if (err < 0)
return err;
err = allocate_tx_resources(ff);
if (err < 0)
return err;
reg = cpu_to_le32(0x80000000);
reg |= cpu_to_le32(ff->tx_stream.data_block_quadlets);
if (fw_parent_device(ff->unit)->max_speed == SCODE_800)
reg |= cpu_to_le32(FF800_TX_S800_FLAG);
return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
FF800_ISOC_COMM_START, &reg, sizeof(reg), 0);
}
static void ff800_finish_session(struct snd_ff *ff)
{
__le32 reg;
reg = cpu_to_le32(0x80000000);
snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
FF800_ISOC_COMM_STOP, &reg, sizeof(reg), 0);
}
static void ff800_handle_midi_msg(struct snd_ff *ff, __le32 *buf, size_t length)
{
int i;
for (i = 0; i < length / 4; i++) {
u8 byte = le32_to_cpu(buf[i]) & 0xff;
struct snd_rawmidi_substream *substream;
substream = READ_ONCE(ff->tx_midi_substreams[0]);
if (substream)
snd_rawmidi_receive(substream, &byte, 1);
}
}
const struct snd_ff_protocol snd_ff_protocol_ff800 = {
.handle_midi_msg = ff800_handle_midi_msg,
.begin_session = ff800_begin_session,
.finish_session = ff800_finish_session,
};

View File

@ -0,0 +1,597 @@
// SPDX-License-Identifier: GPL-2.0
// ff-protocol-former.c - a part of driver for RME Fireface series
//
// Copyright (c) 2019 Takashi Sakamoto
//
// Licensed under the terms of the GNU General Public License, version 2.
#include <linux/delay.h>
#include "ff.h"
#define FORMER_REG_SYNC_STATUS 0x0000801c0000ull
/* For block write request. */
#define FORMER_REG_FETCH_PCM_FRAMES 0x0000801c0000ull
#define FORMER_REG_CLOCK_CONFIG 0x0000801c0004ull
static int parse_clock_bits(u32 data, unsigned int *rate,
enum snd_ff_clock_src *src)
{
static const struct {
unsigned int rate;
u32 mask;
} *rate_entry, rate_entries[] = {
{ 32000, 0x00000002, },
{ 44100, 0x00000000, },
{ 48000, 0x00000006, },
{ 64000, 0x0000000a, },
{ 88200, 0x00000008, },
{ 96000, 0x0000000e, },
{ 128000, 0x00000012, },
{ 176400, 0x00000010, },
{ 192000, 0x00000016, },
};
static const struct {
enum snd_ff_clock_src src;
u32 mask;
} *clk_entry, clk_entries[] = {
{ SND_FF_CLOCK_SRC_ADAT1, 0x00000000, },
{ SND_FF_CLOCK_SRC_ADAT2, 0x00000400, },
{ SND_FF_CLOCK_SRC_SPDIF, 0x00000c00, },
{ SND_FF_CLOCK_SRC_WORD, 0x00001000, },
{ SND_FF_CLOCK_SRC_LTC, 0x00001800, },
};
int i;
for (i = 0; i < ARRAY_SIZE(rate_entries); ++i) {
rate_entry = rate_entries + i;
if ((data & 0x0000001e) == rate_entry->mask) {
*rate = rate_entry->rate;
break;
}
}
if (i == ARRAY_SIZE(rate_entries))
return -EIO;
if (data & 0x00000001) {
*src = SND_FF_CLOCK_SRC_INTERNAL;
} else {
for (i = 0; i < ARRAY_SIZE(clk_entries); ++i) {
clk_entry = clk_entries + i;
if ((data & 0x00001c00) == clk_entry->mask) {
*src = clk_entry->src;
break;
}
}
if (i == ARRAY_SIZE(clk_entries))
return -EIO;
}
return 0;
}
static int former_get_clock(struct snd_ff *ff, unsigned int *rate,
enum snd_ff_clock_src *src)
{
__le32 reg;
u32 data;
int err;
err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST,
FORMER_REG_CLOCK_CONFIG, &reg, sizeof(reg), 0);
if (err < 0)
return err;
data = le32_to_cpu(reg);
return parse_clock_bits(data, rate, src);
}
static int former_switch_fetching_mode(struct snd_ff *ff, bool enable)
{
unsigned int count;
__le32 *reg;
int i;
int err;
count = 0;
for (i = 0; i < SND_FF_STREAM_MODE_COUNT; ++i)
count = max(count, ff->spec->pcm_playback_channels[i]);
reg = kcalloc(count, sizeof(__le32), GFP_KERNEL);
if (!reg)
return -ENOMEM;
if (!enable) {
/*
* Each quadlet is corresponding to data channels in a data
* blocks in reverse order. Precisely, quadlets for available
* data channels should be enabled. Here, I take second best
* to fetch PCM frames from all of data channels regardless of
* stf.
*/
for (i = 0; i < count; ++i)
reg[i] = cpu_to_le32(0x00000001);
}
err = snd_fw_transaction(ff->unit, TCODE_WRITE_BLOCK_REQUEST,
FORMER_REG_FETCH_PCM_FRAMES, reg,
sizeof(__le32) * count, 0);
kfree(reg);
return err;
}
static void dump_clock_config(struct snd_ff *ff, struct snd_info_buffer *buffer)
{
__le32 reg;
u32 data;
unsigned int rate;
enum snd_ff_clock_src src;
const char *label;
int err;
err = snd_fw_transaction(ff->unit, TCODE_READ_BLOCK_REQUEST,
FORMER_REG_CLOCK_CONFIG, &reg, sizeof(reg), 0);
if (err < 0)
return;
data = le32_to_cpu(reg);
snd_iprintf(buffer, "Output S/PDIF format: %s (Emphasis: %s)\n",
(data & 0x00000020) ? "Professional" : "Consumer",
(data & 0x00000040) ? "on" : "off");
snd_iprintf(buffer, "Optical output interface format: %s\n",
(data & 0x00000100) ? "S/PDIF" : "ADAT");
snd_iprintf(buffer, "Word output single speed: %s\n",
(data & 0x00002000) ? "on" : "off");
snd_iprintf(buffer, "S/PDIF input interface: %s\n",
(data & 0x00000200) ? "Optical" : "Coaxial");
err = parse_clock_bits(data, &rate, &src);
if (err < 0)
return;
label = snd_ff_proc_get_clk_label(src);
if (!label)
return;
snd_iprintf(buffer, "Clock configuration: %d %s\n", rate, label);
}
static void dump_sync_status(struct snd_ff *ff, struct snd_info_buffer *buffer)
{
static const struct {
char *const label;
u32 locked_mask;
u32 synced_mask;
} *clk_entry, clk_entries[] = {
{ "WDClk", 0x40000000, 0x20000000, },
{ "S/PDIF", 0x00080000, 0x00040000, },
{ "ADAT1", 0x00000400, 0x00001000, },
{ "ADAT2", 0x00000800, 0x00002000, },
};
static const struct {
char *const label;
u32 mask;
} *referred_entry, referred_entries[] = {
{ "ADAT1", 0x00000000, },
{ "ADAT2", 0x00400000, },
{ "S/PDIF", 0x00c00000, },
{ "WDclk", 0x01000000, },
{ "TCO", 0x01400000, },
};
static const struct {
unsigned int rate;
u32 mask;
} *rate_entry, rate_entries[] = {
{ 32000, 0x02000000, },
{ 44100, 0x04000000, },
{ 48000, 0x06000000, },
{ 64000, 0x08000000, },
{ 88200, 0x0a000000, },
{ 96000, 0x0c000000, },
{ 128000, 0x0e000000, },
{ 176400, 0x10000000, },
{ 192000, 0x12000000, },
};
__le32 reg[2];
u32 data[2];
int i;
int err;
err = snd_fw_transaction(ff->unit, TCODE_READ_BLOCK_REQUEST,
FORMER_REG_SYNC_STATUS, reg, sizeof(reg), 0);
if (err < 0)
return;
data[0] = le32_to_cpu(reg[0]);
data[1] = le32_to_cpu(reg[1]);
snd_iprintf(buffer, "External source detection:\n");
for (i = 0; i < ARRAY_SIZE(clk_entries); ++i) {
const char *state;
clk_entry = clk_entries + i;
if (data[0] & clk_entry->locked_mask) {
if (data[0] & clk_entry->synced_mask)
state = "sync";
else
state = "lock";
} else {
state = "none";
}
snd_iprintf(buffer, "%s: %s\n", clk_entry->label, state);
}
snd_iprintf(buffer, "Referred clock:\n");
if (data[1] & 0x00000001) {
snd_iprintf(buffer, "Internal\n");
} else {
unsigned int rate;
const char *label;
for (i = 0; i < ARRAY_SIZE(referred_entries); ++i) {
referred_entry = referred_entries + i;
if ((data[0] & 0x1e0000) == referred_entry->mask) {
label = referred_entry->label;
break;
}
}
if (i == ARRAY_SIZE(referred_entries))
label = "none";
for (i = 0; i < ARRAY_SIZE(rate_entries); ++i) {
rate_entry = rate_entries + i;
if ((data[0] & 0x1e000000) == rate_entry->mask) {
rate = rate_entry->rate;
break;
}
}
if (i == ARRAY_SIZE(rate_entries))
rate = 0;
snd_iprintf(buffer, "%s %d\n", label, rate);
}
}
static void former_dump_status(struct snd_ff *ff,
struct snd_info_buffer *buffer)
{
dump_clock_config(ff, buffer);
dump_sync_status(ff, buffer);
}
static int former_fill_midi_msg(struct snd_ff *ff,
struct snd_rawmidi_substream *substream,
unsigned int port)
{
u8 *buf = (u8 *)ff->msg_buf[port];
int len;
int i;
len = snd_rawmidi_transmit_peek(substream, buf,
SND_FF_MAXIMIM_MIDI_QUADS);
if (len <= 0)
return len;
// One quadlet includes one byte.
for (i = len - 1; i >= 0; --i)
ff->msg_buf[port][i] = cpu_to_le32(buf[i]);
ff->rx_bytes[port] = len;
return len;
}
#define FF800_STF 0x0000fc88f000
#define FF800_RX_PACKET_FORMAT 0x0000fc88f004
#define FF800_ALLOC_TX_STREAM 0x0000fc88f008
#define FF800_ISOC_COMM_START 0x0000fc88f00c
#define FF800_TX_S800_FLAG 0x00000800
#define FF800_ISOC_COMM_STOP 0x0000fc88f010
#define FF800_TX_PACKET_ISOC_CH 0x0000801c0008
static int allocate_rx_resources(struct snd_ff *ff)
{
u32 data;
__le32 reg;
int err;
// Controllers should allocate isochronous resources for rx stream.
err = fw_iso_resources_allocate(&ff->rx_resources,
amdtp_stream_get_max_payload(&ff->rx_stream),
fw_parent_device(ff->unit)->max_speed);
if (err < 0)
return err;
// Set isochronous channel and the number of quadlets of rx packets.
data = ff->rx_stream.data_block_quadlets << 3;
data = (data << 8) | ff->rx_resources.channel;
reg = cpu_to_le32(data);
return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
FF800_RX_PACKET_FORMAT, &reg, sizeof(reg), 0);
}
static int allocate_tx_resources(struct snd_ff *ff)
{
__le32 reg;
unsigned int count;
unsigned int tx_isoc_channel;
int err;
reg = cpu_to_le32(ff->tx_stream.data_block_quadlets);
err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
FF800_ALLOC_TX_STREAM, &reg, sizeof(reg), 0);
if (err < 0)
return err;
// Wait till the format of tx packet is available.
count = 0;
while (count++ < 10) {
u32 data;
err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST,
FF800_TX_PACKET_ISOC_CH, &reg, sizeof(reg), 0);
if (err < 0)
return err;
data = le32_to_cpu(reg);
if (data != 0xffffffff) {
tx_isoc_channel = data;
break;
}
msleep(50);
}
if (count >= 10)
return -ETIMEDOUT;
// NOTE: this is a makeshift to start OHCI 1394 IR context in the
// channel. On the other hand, 'struct fw_iso_resources.allocated' is
// not true and it's not deallocated at stop.
ff->tx_resources.channel = tx_isoc_channel;
return 0;
}
static int ff800_begin_session(struct snd_ff *ff, unsigned int rate)
{
__le32 reg;
int err;
reg = cpu_to_le32(rate);
err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
FF800_STF, &reg, sizeof(reg), 0);
if (err < 0)
return err;
// If starting isochronous communication immediately, change of STF has
// no effect. In this case, the communication runs based on former STF.
// Let's sleep for a bit.
msleep(100);
err = allocate_rx_resources(ff);
if (err < 0)
return err;
err = allocate_tx_resources(ff);
if (err < 0)
return err;
reg = cpu_to_le32(0x80000000);
reg |= cpu_to_le32(ff->tx_stream.data_block_quadlets);
if (fw_parent_device(ff->unit)->max_speed == SCODE_800)
reg |= cpu_to_le32(FF800_TX_S800_FLAG);
return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
FF800_ISOC_COMM_START, &reg, sizeof(reg), 0);
}
static void ff800_finish_session(struct snd_ff *ff)
{
__le32 reg;
reg = cpu_to_le32(0x80000000);
snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
FF800_ISOC_COMM_STOP, &reg, sizeof(reg), 0);
}
// Fireface 800 doesn't allow drivers to register lower 4 bytes of destination
// address.
// A write transaction to clear registered higher 4 bytes of destination address
// has an effect to suppress asynchronous transaction from device.
static void ff800_handle_midi_msg(struct snd_ff *ff, unsigned int offset,
__le32 *buf, size_t length)
{
int i;
for (i = 0; i < length / 4; i++) {
u8 byte = le32_to_cpu(buf[i]) & 0xff;
struct snd_rawmidi_substream *substream;
substream = READ_ONCE(ff->tx_midi_substreams[0]);
if (substream)
snd_rawmidi_receive(substream, &byte, 1);
}
}
const struct snd_ff_protocol snd_ff_protocol_ff800 = {
.handle_midi_msg = ff800_handle_midi_msg,
.fill_midi_msg = former_fill_midi_msg,
.get_clock = former_get_clock,
.switch_fetching_mode = former_switch_fetching_mode,
.begin_session = ff800_begin_session,
.finish_session = ff800_finish_session,
.dump_status = former_dump_status,
};
#define FF400_STF 0x000080100500ull
#define FF400_RX_PACKET_FORMAT 0x000080100504ull
#define FF400_ISOC_COMM_START 0x000080100508ull
#define FF400_TX_PACKET_FORMAT 0x00008010050cull
#define FF400_ISOC_COMM_STOP 0x000080100510ull
/*
* Fireface 400 manages isochronous channel number in 3 bit field. Therefore,
* we can allocate between 0 and 7 channel.
*/
static int keep_resources(struct snd_ff *ff, unsigned int rate)
{
enum snd_ff_stream_mode mode;
int i;
int err;
// Check whether the given value is supported or not.
for (i = 0; i < CIP_SFC_COUNT; i++) {
if (amdtp_rate_table[i] == rate)
break;
}
if (i >= CIP_SFC_COUNT)
return -EINVAL;
err = snd_ff_stream_get_multiplier_mode(i, &mode);
if (err < 0)
return err;
/* Keep resources for in-stream. */
ff->tx_resources.channels_mask = 0x00000000000000ffuLL;
err = fw_iso_resources_allocate(&ff->tx_resources,
amdtp_stream_get_max_payload(&ff->tx_stream),
fw_parent_device(ff->unit)->max_speed);
if (err < 0)
return err;
/* Keep resources for out-stream. */
ff->rx_resources.channels_mask = 0x00000000000000ffuLL;
err = fw_iso_resources_allocate(&ff->rx_resources,
amdtp_stream_get_max_payload(&ff->rx_stream),
fw_parent_device(ff->unit)->max_speed);
if (err < 0)
fw_iso_resources_free(&ff->tx_resources);
return err;
}
static int ff400_begin_session(struct snd_ff *ff, unsigned int rate)
{
__le32 reg;
int err;
err = keep_resources(ff, rate);
if (err < 0)
return err;
/* Set the number of data blocks transferred in a second. */
reg = cpu_to_le32(rate);
err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
FF400_STF, &reg, sizeof(reg), 0);
if (err < 0)
return err;
msleep(100);
/*
* Set isochronous channel and the number of quadlets of received
* packets.
*/
reg = cpu_to_le32(((ff->rx_stream.data_block_quadlets << 3) << 8) |
ff->rx_resources.channel);
err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
FF400_RX_PACKET_FORMAT, &reg, sizeof(reg), 0);
if (err < 0)
return err;
/*
* Set isochronous channel and the number of quadlets of transmitted
* packet.
*/
/* TODO: investigate the purpose of this 0x80. */
reg = cpu_to_le32((0x80 << 24) |
(ff->tx_resources.channel << 5) |
(ff->tx_stream.data_block_quadlets));
err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
FF400_TX_PACKET_FORMAT, &reg, sizeof(reg), 0);
if (err < 0)
return err;
/* Allow to transmit packets. */
reg = cpu_to_le32(0x00000001);
return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
FF400_ISOC_COMM_START, &reg, sizeof(reg), 0);
}
static void ff400_finish_session(struct snd_ff *ff)
{
__le32 reg;
reg = cpu_to_le32(0x80000000);
snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
FF400_ISOC_COMM_STOP, &reg, sizeof(reg), 0);
}
// For Fireface 400, lower 4 bytes of destination address is configured by bit
// flag in quadlet register (little endian) at 0x'0000'801'0051c. Drivers can
// select one of 4 options:
//
// bit flags: offset of destination address
// - 0x04000000: 0x'....'....'0000'0000
// - 0x08000000: 0x'....'....'0000'0080
// - 0x10000000: 0x'....'....'0000'0100
// - 0x20000000: 0x'....'....'0000'0180
//
// Drivers can suppress the device to transfer asynchronous transactions by
// using below 2 bits.
// - 0x01000000: suppress transmission
// - 0x02000000: suppress transmission
//
// Actually, the register is write-only and includes the other options such as
// input attenuation. This driver allocates destination address with '0000'0000
// in its lower offset and expects userspace application to configure the
// register for it.
static void ff400_handle_midi_msg(struct snd_ff *ff, unsigned int offset,
__le32 *buf, size_t length)
{
int i;
for (i = 0; i < length / 4; i++) {
u32 quad = le32_to_cpu(buf[i]);
u8 byte;
unsigned int index;
struct snd_rawmidi_substream *substream;
/* Message in first port. */
/*
* This value may represent the index of this unit when the same
* units are on the same IEEE 1394 bus. This driver doesn't use
* it.
*/
index = (quad >> 8) & 0xff;
if (index > 0) {
substream = READ_ONCE(ff->tx_midi_substreams[0]);
if (substream != NULL) {
byte = quad & 0xff;
snd_rawmidi_receive(substream, &byte, 1);
}
}
/* Message in second port. */
index = (quad >> 24) & 0xff;
if (index > 0) {
substream = READ_ONCE(ff->tx_midi_substreams[1]);
if (substream != NULL) {
byte = (quad >> 16) & 0xff;
snd_rawmidi_receive(substream, &byte, 1);
}
}
}
}
const struct snd_ff_protocol snd_ff_protocol_ff400 = {
.handle_midi_msg = ff400_handle_midi_msg,
.fill_midi_msg = former_fill_midi_msg,
.get_clock = former_get_clock,
.switch_fetching_mode = former_switch_fetching_mode,
.begin_session = ff400_begin_session,
.finish_session = ff400_finish_session,
.dump_status = former_dump_status,
};

View File

@ -0,0 +1,430 @@
// SPDX-License-Identifier: GPL-2.0
// ff-protocol-latter - a part of driver for RME Fireface series
//
// Copyright (c) 2019 Takashi Sakamoto
//
// Licensed under the terms of the GNU General Public License, version 2.
#include <linux/delay.h>
#include "ff.h"
#define LATTER_STF 0xffff00000004
#define LATTER_ISOC_CHANNELS 0xffff00000008
#define LATTER_ISOC_START 0xffff0000000c
#define LATTER_FETCH_MODE 0xffff00000010
#define LATTER_SYNC_STATUS 0x0000801c0000
static int parse_clock_bits(u32 data, unsigned int *rate,
enum snd_ff_clock_src *src)
{
static const struct {
unsigned int rate;
u32 flag;
} *rate_entry, rate_entries[] = {
{ 32000, 0x00000000, },
{ 44100, 0x01000000, },
{ 48000, 0x02000000, },
{ 64000, 0x04000000, },
{ 88200, 0x05000000, },
{ 96000, 0x06000000, },
{ 128000, 0x08000000, },
{ 176400, 0x09000000, },
{ 192000, 0x0a000000, },
};
static const struct {
enum snd_ff_clock_src src;
u32 flag;
} *clk_entry, clk_entries[] = {
{ SND_FF_CLOCK_SRC_SPDIF, 0x00000200, },
{ SND_FF_CLOCK_SRC_ADAT1, 0x00000400, },
{ SND_FF_CLOCK_SRC_WORD, 0x00000600, },
{ SND_FF_CLOCK_SRC_INTERNAL, 0x00000e00, },
};
int i;
for (i = 0; i < ARRAY_SIZE(rate_entries); ++i) {
rate_entry = rate_entries + i;
if ((data & 0x0f000000) == rate_entry->flag) {
*rate = rate_entry->rate;
break;
}
}
if (i == ARRAY_SIZE(rate_entries))
return -EIO;
for (i = 0; i < ARRAY_SIZE(clk_entries); ++i) {
clk_entry = clk_entries + i;
if ((data & 0x000e00) == clk_entry->flag) {
*src = clk_entry->src;
break;
}
}
if (i == ARRAY_SIZE(clk_entries))
return -EIO;
return 0;
}
static int latter_get_clock(struct snd_ff *ff, unsigned int *rate,
enum snd_ff_clock_src *src)
{
__le32 reg;
u32 data;
int err;
err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST,
LATTER_SYNC_STATUS, &reg, sizeof(reg), 0);
if (err < 0)
return err;
data = le32_to_cpu(reg);
return parse_clock_bits(data, rate, src);
}
static int latter_switch_fetching_mode(struct snd_ff *ff, bool enable)
{
u32 data;
__le32 reg;
if (enable)
data = 0x00000000;
else
data = 0xffffffff;
reg = cpu_to_le32(data);
return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
LATTER_FETCH_MODE, &reg, sizeof(reg), 0);
}
static int keep_resources(struct snd_ff *ff, unsigned int rate)
{
enum snd_ff_stream_mode mode;
int i;
int err;
// Check whether the given value is supported or not.
for (i = 0; i < CIP_SFC_COUNT; i++) {
if (amdtp_rate_table[i] == rate)
break;
}
if (i >= CIP_SFC_COUNT)
return -EINVAL;
err = snd_ff_stream_get_multiplier_mode(i, &mode);
if (err < 0)
return err;
/* Keep resources for in-stream. */
ff->tx_resources.channels_mask = 0x00000000000000ffuLL;
err = fw_iso_resources_allocate(&ff->tx_resources,
amdtp_stream_get_max_payload(&ff->tx_stream),
fw_parent_device(ff->unit)->max_speed);
if (err < 0)
return err;
/* Keep resources for out-stream. */
ff->rx_resources.channels_mask = 0x00000000000000ffuLL;
err = fw_iso_resources_allocate(&ff->rx_resources,
amdtp_stream_get_max_payload(&ff->rx_stream),
fw_parent_device(ff->unit)->max_speed);
if (err < 0)
fw_iso_resources_free(&ff->tx_resources);
return err;
}
static int latter_begin_session(struct snd_ff *ff, unsigned int rate)
{
static const struct {
unsigned int stf;
unsigned int code;
unsigned int flag;
} *entry, rate_table[] = {
{ 32000, 0x00, 0x92, },
{ 44100, 0x02, 0x92, },
{ 48000, 0x04, 0x92, },
{ 64000, 0x08, 0x8e, },
{ 88200, 0x0a, 0x8e, },
{ 96000, 0x0c, 0x8e, },
{ 128000, 0x10, 0x8c, },
{ 176400, 0x12, 0x8c, },
{ 192000, 0x14, 0x8c, },
};
u32 data;
__le32 reg;
unsigned int count;
int i;
int err;
for (i = 0; i < ARRAY_SIZE(rate_table); ++i) {
entry = rate_table + i;
if (entry->stf == rate)
break;
}
if (i == ARRAY_SIZE(rate_table))
return -EINVAL;
reg = cpu_to_le32(entry->code);
err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
LATTER_STF, &reg, sizeof(reg), 0);
if (err < 0)
return err;
// Confirm to shift transmission clock.
count = 0;
while (count++ < 10) {
unsigned int curr_rate;
enum snd_ff_clock_src src;
err = latter_get_clock(ff, &curr_rate, &src);
if (err < 0)
return err;
if (curr_rate == rate)
break;
}
if (count == 10)
return -ETIMEDOUT;
err = keep_resources(ff, rate);
if (err < 0)
return err;
data = (ff->tx_resources.channel << 8) | ff->rx_resources.channel;
reg = cpu_to_le32(data);
err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
LATTER_ISOC_CHANNELS, &reg, sizeof(reg), 0);
if (err < 0)
return err;
// Always use the maximum number of data channels in data block of
// packet.
reg = cpu_to_le32(entry->flag);
return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
LATTER_ISOC_START, &reg, sizeof(reg), 0);
}
static void latter_finish_session(struct snd_ff *ff)
{
__le32 reg;
reg = cpu_to_le32(0x00000000);
snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
LATTER_ISOC_START, &reg, sizeof(reg), 0);
}
static void latter_dump_status(struct snd_ff *ff, struct snd_info_buffer *buffer)
{
static const struct {
char *const label;
u32 locked_mask;
u32 synced_mask;
} *clk_entry, clk_entries[] = {
{ "S/PDIF", 0x00000001, 0x00000010, },
{ "ADAT", 0x00000002, 0x00000020, },
{ "WDClk", 0x00000004, 0x00000040, },
};
__le32 reg;
u32 data;
unsigned int rate;
enum snd_ff_clock_src src;
const char *label;
int i;
int err;
err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST,
LATTER_SYNC_STATUS, &reg, sizeof(reg), 0);
if (err < 0)
return;
data = le32_to_cpu(reg);
snd_iprintf(buffer, "External source detection:\n");
for (i = 0; i < ARRAY_SIZE(clk_entries); ++i) {
clk_entry = clk_entries + i;
snd_iprintf(buffer, "%s: ", clk_entry->label);
if (data & clk_entry->locked_mask) {
if (data & clk_entry->synced_mask)
snd_iprintf(buffer, "sync\n");
else
snd_iprintf(buffer, "lock\n");
} else {
snd_iprintf(buffer, "none\n");
}
}
err = parse_clock_bits(data, &rate, &src);
if (err < 0)
return;
label = snd_ff_proc_get_clk_label(src);
if (!label)
return;
snd_iprintf(buffer, "Referred clock: %s %d\n", label, rate);
}
// NOTE: transactions are transferred within 0x00-0x7f in allocated range of
// address. This seems to be for check of discontinuity in receiver side.
//
// Like Fireface 400, drivers can select one of 4 options for lower 4 bytes of
// destination address by bit flags in quadlet register (little endian) at
// 0x'ffff'0000'0014:
//
// bit flags: offset of destination address
// - 0x00002000: 0x'....'....'0000'0000
// - 0x00004000: 0x'....'....'0000'0080
// - 0x00008000: 0x'....'....'0000'0100
// - 0x00010000: 0x'....'....'0000'0180
//
// Drivers can suppress the device to transfer asynchronous transactions by
// clear these bit flags.
//
// Actually, the register is write-only and includes the other settings such as
// input attenuation. This driver allocates for the first option
// (0x'....'....'0000'0000) and expects userspace application to configure the
// register for it.
static void latter_handle_midi_msg(struct snd_ff *ff, unsigned int offset,
__le32 *buf, size_t length)
{
u32 data = le32_to_cpu(*buf);
unsigned int index = (data & 0x000000f0) >> 4;
u8 byte[3];
struct snd_rawmidi_substream *substream;
unsigned int len;
if (index >= ff->spec->midi_in_ports)
return;
switch (data & 0x0000000f) {
case 0x00000008:
case 0x00000009:
case 0x0000000a:
case 0x0000000b:
case 0x0000000e:
len = 3;
break;
case 0x0000000c:
case 0x0000000d:
len = 2;
break;
default:
len = data & 0x00000003;
if (len == 0)
len = 3;
break;
}
byte[0] = (data & 0x0000ff00) >> 8;
byte[1] = (data & 0x00ff0000) >> 16;
byte[2] = (data & 0xff000000) >> 24;
substream = READ_ONCE(ff->tx_midi_substreams[index]);
if (substream)
snd_rawmidi_receive(substream, byte, len);
}
/*
* When return minus value, given argument is not MIDI status.
* When return 0, given argument is a beginning of system exclusive.
* When return the others, given argument is MIDI data.
*/
static inline int calculate_message_bytes(u8 status)
{
switch (status) {
case 0xf6: /* Tune request. */
case 0xf8: /* Timing clock. */
case 0xfa: /* Start. */
case 0xfb: /* Continue. */
case 0xfc: /* Stop. */
case 0xfe: /* Active sensing. */
case 0xff: /* System reset. */
return 1;
case 0xf1: /* MIDI time code quarter frame. */
case 0xf3: /* Song select. */
return 2;
case 0xf2: /* Song position pointer. */
return 3;
case 0xf0: /* Exclusive. */
return 0;
case 0xf7: /* End of exclusive. */
break;
case 0xf4: /* Undefined. */
case 0xf5: /* Undefined. */
case 0xf9: /* Undefined. */
case 0xfd: /* Undefined. */
break;
default:
switch (status & 0xf0) {
case 0x80: /* Note on. */
case 0x90: /* Note off. */
case 0xa0: /* Polyphonic key pressure. */
case 0xb0: /* Control change and Mode change. */
case 0xe0: /* Pitch bend change. */
return 3;
case 0xc0: /* Program change. */
case 0xd0: /* Channel pressure. */
return 2;
default:
break;
}
break;
}
return -EINVAL;
}
static int latter_fill_midi_msg(struct snd_ff *ff,
struct snd_rawmidi_substream *substream,
unsigned int port)
{
u32 data = {0};
u8 *buf = (u8 *)&data;
int consumed;
buf[0] = port << 4;
consumed = snd_rawmidi_transmit_peek(substream, buf + 1, 3);
if (consumed <= 0)
return consumed;
if (!ff->on_sysex[port]) {
if (buf[1] != 0xf0) {
if (consumed < calculate_message_bytes(buf[1]))
return 0;
} else {
// The beginning of exclusives.
ff->on_sysex[port] = true;
}
buf[0] |= consumed;
} else {
if (buf[1] != 0xf7) {
if (buf[2] == 0xf7 || buf[3] == 0xf7) {
// Transfer end code at next time.
consumed -= 1;
}
buf[0] |= consumed;
} else {
// The end of exclusives.
ff->on_sysex[port] = false;
consumed = 1;
buf[0] |= 0x0f;
}
}
ff->msg_buf[port][0] = cpu_to_le32(data);
ff->rx_bytes[port] = consumed;
return 1;
}
const struct snd_ff_protocol snd_ff_protocol_latter = {
.handle_midi_msg = latter_handle_midi_msg,
.fill_midi_msg = latter_fill_midi_msg,
.get_clock = latter_get_clock,
.switch_fetching_mode = latter_switch_fetching_mode,
.begin_session = latter_begin_session,
.finish_session = latter_finish_session,
.dump_status = latter_dump_status,
};

View File

@ -37,44 +37,10 @@ static void release_resources(struct snd_ff *ff)
fw_iso_resources_free(&ff->rx_resources); fw_iso_resources_free(&ff->rx_resources);
} }
static int switch_fetching_mode(struct snd_ff *ff, bool enable)
{
unsigned int count;
__le32 *reg;
int i;
int err;
count = 0;
for (i = 0; i < SND_FF_STREAM_MODE_COUNT; ++i)
count = max(count, ff->spec->pcm_playback_channels[i]);
reg = kcalloc(count, sizeof(__le32), GFP_KERNEL);
if (!reg)
return -ENOMEM;
if (!enable) {
/*
* Each quadlet is corresponding to data channels in a data
* blocks in reverse order. Precisely, quadlets for available
* data channels should be enabled. Here, I take second best
* to fetch PCM frames from all of data channels regardless of
* stf.
*/
for (i = 0; i < count; ++i)
reg[i] = cpu_to_le32(0x00000001);
}
err = snd_fw_transaction(ff->unit, TCODE_WRITE_BLOCK_REQUEST,
SND_FF_REG_FETCH_PCM_FRAMES, reg,
sizeof(__le32) * count, 0);
kfree(reg);
return err;
}
static inline void finish_session(struct snd_ff *ff) static inline void finish_session(struct snd_ff *ff)
{ {
ff->spec->protocol->finish_session(ff); ff->spec->protocol->finish_session(ff);
switch_fetching_mode(ff, false); ff->spec->protocol->switch_fetching_mode(ff, false);
} }
static int init_stream(struct snd_ff *ff, enum amdtp_stream_direction dir) static int init_stream(struct snd_ff *ff, enum amdtp_stream_direction dir)
@ -147,7 +113,7 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
if (ff->substreams_counter == 0) if (ff->substreams_counter == 0)
return 0; return 0;
err = snd_ff_transaction_get_clock(ff, &curr_rate, &src); err = ff->spec->protocol->get_clock(ff, &curr_rate, &src);
if (err < 0) if (err < 0)
return err; return err;
if (curr_rate != rate || if (curr_rate != rate ||
@ -206,7 +172,7 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
goto error; goto error;
} }
err = switch_fetching_mode(ff, true); err = ff->spec->protocol->switch_fetching_mode(ff, true);
if (err < 0) if (err < 0)
goto error; goto error;
} }

View File

@ -8,72 +8,6 @@
#include "ff.h" #include "ff.h"
#define SND_FF_REG_MIDI_RX_PORT_0 0x000080180000ull
#define SND_FF_REG_MIDI_RX_PORT_1 0x000080190000ull
int snd_ff_transaction_get_clock(struct snd_ff *ff, unsigned int *rate,
enum snd_ff_clock_src *src)
{
__le32 reg;
u32 data;
int err;
err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST,
SND_FF_REG_CLOCK_CONFIG, &reg, sizeof(reg), 0);
if (err < 0)
return err;
data = le32_to_cpu(reg);
/* Calculate sampling rate. */
switch ((data >> 1) & 0x03) {
case 0x01:
*rate = 32000;
break;
case 0x00:
*rate = 44100;
break;
case 0x03:
*rate = 48000;
break;
case 0x02:
default:
return -EIO;
}
if (data & 0x08)
*rate *= 2;
else if (data & 0x10)
*rate *= 4;
/* Calculate source of clock. */
if (data & 0x01) {
*src = SND_FF_CLOCK_SRC_INTERNAL;
} else {
/* TODO: 0x02, 0x06, 0x07? */
switch ((data >> 10) & 0x07) {
case 0x00:
*src = SND_FF_CLOCK_SRC_ADAT1;
break;
case 0x01:
*src = SND_FF_CLOCK_SRC_ADAT2;
break;
case 0x03:
*src = SND_FF_CLOCK_SRC_SPDIF;
break;
case 0x04:
*src = SND_FF_CLOCK_SRC_WORD;
break;
case 0x05:
*src = SND_FF_CLOCK_SRC_LTC;
break;
default:
return -EIO;
}
}
return 0;
}
static void finish_transmit_midi_msg(struct snd_ff *ff, unsigned int port, static void finish_transmit_midi_msg(struct snd_ff *ff, unsigned int port,
int rcode) int rcode)
{ {
@ -117,23 +51,17 @@ static void finish_transmit_midi1_msg(struct fw_card *card, int rcode,
finish_transmit_midi_msg(ff, 1, rcode); finish_transmit_midi_msg(ff, 1, rcode);
} }
static inline void fill_midi_buf(struct snd_ff *ff, unsigned int port,
unsigned int index, u8 byte)
{
ff->msg_buf[port][index] = cpu_to_le32(byte);
}
static void transmit_midi_msg(struct snd_ff *ff, unsigned int port) static void transmit_midi_msg(struct snd_ff *ff, unsigned int port)
{ {
struct snd_rawmidi_substream *substream = struct snd_rawmidi_substream *substream =
READ_ONCE(ff->rx_midi_substreams[port]); READ_ONCE(ff->rx_midi_substreams[port]);
u8 *buf = (u8 *)ff->msg_buf[port]; int quad_count;
int i, len;
struct fw_device *fw_dev = fw_parent_device(ff->unit); struct fw_device *fw_dev = fw_parent_device(ff->unit);
unsigned long long addr; unsigned long long addr;
int generation; int generation;
fw_transaction_callback_t callback; fw_transaction_callback_t callback;
int tcode;
if (substream == NULL || snd_rawmidi_transmit_empty(substream)) if (substream == NULL || snd_rawmidi_transmit_empty(substream))
return; return;
@ -147,26 +75,26 @@ static void transmit_midi_msg(struct snd_ff *ff, unsigned int port)
return; return;
} }
len = snd_rawmidi_transmit_peek(substream, buf, quad_count = ff->spec->protocol->fill_midi_msg(ff, substream, port);
SND_FF_MAXIMIM_MIDI_QUADS); if (quad_count <= 0)
if (len <= 0)
return; return;
for (i = len - 1; i >= 0; i--)
fill_midi_buf(ff, port, i, buf[i]);
if (port == 0) { if (port == 0) {
addr = SND_FF_REG_MIDI_RX_PORT_0; addr = ff->spec->midi_rx_addrs[0];
callback = finish_transmit_midi0_msg; callback = finish_transmit_midi0_msg;
} else { } else {
addr = SND_FF_REG_MIDI_RX_PORT_1; addr = ff->spec->midi_rx_addrs[1];
callback = finish_transmit_midi1_msg; callback = finish_transmit_midi1_msg;
} }
/* Set interval to next transaction. */ /* Set interval to next transaction. */
ff->next_ktime[port] = ktime_add_ns(ktime_get(), ff->next_ktime[port] = ktime_add_ns(ktime_get(),
len * 8 * NSEC_PER_SEC / 31250); ff->rx_bytes[port] * 8 * NSEC_PER_SEC / 31250);
ff->rx_bytes[port] = len;
if (quad_count == 1)
tcode = TCODE_WRITE_QUADLET_REQUEST;
else
tcode = TCODE_WRITE_BLOCK_REQUEST;
/* /*
* In Linux FireWire core, when generation is updated with memory * In Linux FireWire core, when generation is updated with memory
@ -178,10 +106,9 @@ static void transmit_midi_msg(struct snd_ff *ff, unsigned int port)
*/ */
generation = fw_dev->generation; generation = fw_dev->generation;
smp_rmb(); smp_rmb();
fw_send_request(fw_dev->card, &ff->transactions[port], fw_send_request(fw_dev->card, &ff->transactions[port], tcode,
TCODE_WRITE_BLOCK_REQUEST,
fw_dev->node_id, generation, fw_dev->max_speed, fw_dev->node_id, generation, fw_dev->max_speed,
addr, &ff->msg_buf[port], len * 4, addr, &ff->msg_buf[port], quad_count * 4,
callback, &ff->transactions[port]); callback, &ff->transactions[port]);
} }
@ -209,7 +136,9 @@ static void handle_midi_msg(struct fw_card *card, struct fw_request *request,
fw_send_response(card, request, RCODE_COMPLETE); fw_send_response(card, request, RCODE_COMPLETE);
ff->spec->protocol->handle_midi_msg(ff, buf, length); offset -= ff->async_handler.offset;
ff->spec->protocol->handle_midi_msg(ff, (unsigned int)offset, buf,
length);
} }
static int allocate_own_address(struct snd_ff *ff, int i) static int allocate_own_address(struct snd_ff *ff, int i)
@ -217,7 +146,7 @@ static int allocate_own_address(struct snd_ff *ff, int i)
struct fw_address_region midi_msg_region; struct fw_address_region midi_msg_region;
int err; int err;
ff->async_handler.length = SND_FF_MAXIMIM_MIDI_QUADS * 4; ff->async_handler.length = ff->spec->midi_addr_range;
ff->async_handler.address_callback = handle_midi_msg; ff->async_handler.address_callback = handle_midi_msg;
ff->async_handler.callback_data = ff; ff->async_handler.callback_data = ff;
@ -236,35 +165,13 @@ static int allocate_own_address(struct snd_ff *ff, int i)
return err; return err;
} }
/* // Controllers are allowed to register higher 4 bytes of destination address to
* Controllers are allowed to register higher 4 bytes of address to receive // receive asynchronous transactions for MIDI messages, while the way to
* the transactions. Different models have different registers for this purpose; // register lower 4 bytes of address is different depending on protocols. For
* e.g. 0x'0000'8010'03f4 for Fireface 400. // details, please refer to comments in protocol implementations.
* The controllers are not allowed to register lower 4 bytes of the address. //
* They are forced to select one of 4 options for the part of address by writing // This driver expects userspace applications to configure registers for the
* corresponding bits to 0x'0000'8010'051f. // lower address because in most cases such registers has the other settings.
*
* The 3rd-6th bits of this register are flags to indicate lower 4 bytes of
* address to which the device transferrs the transactions. In short:
* - 0x20: 0x'....'....'0000'0180
* - 0x10: 0x'....'....'0000'0100
* - 0x08: 0x'....'....'0000'0080
* - 0x04: 0x'....'....'0000'0000
*
* This driver configure 0x'....'....'0000'0000 to receive MIDI messages from
* units. The 3rd bit of the register should be configured, however this driver
* deligates this task to userspace applications due to a restriction that this
* register is write-only and the other bits have own effects.
*
* Unlike Fireface 800, Fireface 400 cancels transferring asynchronous
* transactions when the 1st and 2nd of the register stand. These two bits have
* the same effect.
* - 0x02, 0x01: cancel transferring
*
* On the other hand, the bits have no effect on Fireface 800. This model
* cancels asynchronous transactions when the higher 4 bytes of address is
* overwritten with zero.
*/
int snd_ff_transaction_reregister(struct snd_ff *ff) int snd_ff_transaction_reregister(struct snd_ff *ff)
{ {
struct fw_card *fw_card = fw_parent_device(ff->unit)->card; struct fw_card *fw_card = fw_parent_device(ff->unit)->card;

View File

@ -153,6 +153,8 @@ static const struct snd_ff_spec spec_ff800 = {
.midi_out_ports = 1, .midi_out_ports = 1,
.protocol = &snd_ff_protocol_ff800, .protocol = &snd_ff_protocol_ff800,
.midi_high_addr = 0x000200000320ull, .midi_high_addr = 0x000200000320ull,
.midi_addr_range = 12,
.midi_rx_addrs = {0x000080180000ull, 0},
}; };
static const struct snd_ff_spec spec_ff400 = { static const struct snd_ff_spec spec_ff400 = {
@ -163,6 +165,20 @@ static const struct snd_ff_spec spec_ff400 = {
.midi_out_ports = 2, .midi_out_ports = 2,
.protocol = &snd_ff_protocol_ff400, .protocol = &snd_ff_protocol_ff400,
.midi_high_addr = 0x0000801003f4ull, .midi_high_addr = 0x0000801003f4ull,
.midi_addr_range = SND_FF_MAXIMIM_MIDI_QUADS * 4,
.midi_rx_addrs = {0x000080180000ull, 0x000080190000ull},
};
static const struct snd_ff_spec spec_ucx = {
.name = "FirefaceUCX",
.pcm_capture_channels = {18, 14, 12},
.pcm_playback_channels = {18, 14, 12},
.midi_in_ports = 2,
.midi_out_ports = 2,
.protocol = &snd_ff_protocol_latter,
.midi_high_addr = 0xffff00000034ull,
.midi_addr_range = 0x80,
.midi_rx_addrs = {0xffff00000030ull, 0xffff00000030ull},
}; };
static const struct ieee1394_device_id snd_ff_id_table[] = { static const struct ieee1394_device_id snd_ff_id_table[] = {
@ -190,6 +206,18 @@ static const struct ieee1394_device_id snd_ff_id_table[] = {
.model_id = 0x101800, .model_id = 0x101800,
.driver_data = (kernel_ulong_t)&spec_ff400, .driver_data = (kernel_ulong_t)&spec_ff400,
}, },
// Fireface UCX.
{
.match_flags = IEEE1394_MATCH_VENDOR_ID |
IEEE1394_MATCH_SPECIFIER_ID |
IEEE1394_MATCH_VERSION |
IEEE1394_MATCH_MODEL_ID,
.vendor_id = OUI_RME,
.specifier_id = OUI_RME,
.version = 0x000004,
.model_id = 0x101800,
.driver_data = (kernel_ulong_t)&spec_ucx,
},
{} {}
}; };
MODULE_DEVICE_TABLE(ieee1394, snd_ff_id_table); MODULE_DEVICE_TABLE(ieee1394, snd_ff_id_table);

View File

@ -35,11 +35,6 @@
#define SND_FF_IN_MIDI_PORTS 2 #define SND_FF_IN_MIDI_PORTS 2
#define SND_FF_OUT_MIDI_PORTS 2 #define SND_FF_OUT_MIDI_PORTS 2
#define SND_FF_REG_SYNC_STATUS 0x0000801c0000ull
/* For block write request. */
#define SND_FF_REG_FETCH_PCM_FRAMES 0x0000801c0000ull
#define SND_FF_REG_CLOCK_CONFIG 0x0000801c0004ull
enum snd_ff_stream_mode { enum snd_ff_stream_mode {
SND_FF_STREAM_MODE_LOW = 0, SND_FF_STREAM_MODE_LOW = 0,
SND_FF_STREAM_MODE_MID, SND_FF_STREAM_MODE_MID,
@ -59,6 +54,8 @@ struct snd_ff_spec {
const struct snd_ff_protocol *protocol; const struct snd_ff_protocol *protocol;
u64 midi_high_addr; u64 midi_high_addr;
u8 midi_addr_range;
u64 midi_rx_addrs[SND_FF_OUT_MIDI_PORTS];
}; };
struct snd_ff { struct snd_ff {
@ -78,7 +75,7 @@ struct snd_ff {
/* TO handle MIDI rx. */ /* TO handle MIDI rx. */
struct snd_rawmidi_substream *rx_midi_substreams[SND_FF_OUT_MIDI_PORTS]; struct snd_rawmidi_substream *rx_midi_substreams[SND_FF_OUT_MIDI_PORTS];
u8 running_status[SND_FF_OUT_MIDI_PORTS]; bool on_sysex[SND_FF_OUT_MIDI_PORTS];
__le32 msg_buf[SND_FF_OUT_MIDI_PORTS][SND_FF_MAXIMIM_MIDI_QUADS]; __le32 msg_buf[SND_FF_OUT_MIDI_PORTS][SND_FF_MAXIMIM_MIDI_QUADS];
struct work_struct rx_midi_work[SND_FF_OUT_MIDI_PORTS]; struct work_struct rx_midi_work[SND_FF_OUT_MIDI_PORTS];
struct fw_transaction transactions[SND_FF_OUT_MIDI_PORTS]; struct fw_transaction transactions[SND_FF_OUT_MIDI_PORTS];
@ -108,16 +105,23 @@ enum snd_ff_clock_src {
}; };
struct snd_ff_protocol { struct snd_ff_protocol {
void (*handle_midi_msg)(struct snd_ff *ff, __le32 *buf, size_t length); void (*handle_midi_msg)(struct snd_ff *ff, unsigned int offset,
__le32 *buf, size_t length);
int (*fill_midi_msg)(struct snd_ff *ff,
struct snd_rawmidi_substream *substream,
unsigned int port);
int (*get_clock)(struct snd_ff *ff, unsigned int *rate,
enum snd_ff_clock_src *src);
int (*switch_fetching_mode)(struct snd_ff *ff, bool enable);
int (*begin_session)(struct snd_ff *ff, unsigned int rate); int (*begin_session)(struct snd_ff *ff, unsigned int rate);
void (*finish_session)(struct snd_ff *ff); void (*finish_session)(struct snd_ff *ff);
void (*dump_status)(struct snd_ff *ff, struct snd_info_buffer *buffer);
}; };
extern const struct snd_ff_protocol snd_ff_protocol_ff800; extern const struct snd_ff_protocol snd_ff_protocol_ff800;
extern const struct snd_ff_protocol snd_ff_protocol_ff400; extern const struct snd_ff_protocol snd_ff_protocol_ff400;
extern const struct snd_ff_protocol snd_ff_protocol_latter;
int snd_ff_transaction_get_clock(struct snd_ff *ff, unsigned int *rate,
enum snd_ff_clock_src *src);
int snd_ff_transaction_register(struct snd_ff *ff); int snd_ff_transaction_register(struct snd_ff *ff);
int snd_ff_transaction_reregister(struct snd_ff *ff); int snd_ff_transaction_reregister(struct snd_ff *ff);
void snd_ff_transaction_unregister(struct snd_ff *ff); void snd_ff_transaction_unregister(struct snd_ff *ff);
@ -142,6 +146,7 @@ int snd_ff_stream_lock_try(struct snd_ff *ff);
void snd_ff_stream_lock_release(struct snd_ff *ff); void snd_ff_stream_lock_release(struct snd_ff *ff);
void snd_ff_proc_init(struct snd_ff *ff); void snd_ff_proc_init(struct snd_ff *ff);
const char *snd_ff_proc_get_clk_label(enum snd_ff_clock_src src);
int snd_ff_create_midi_devices(struct snd_ff *ff); int snd_ff_create_midi_devices(struct snd_ff *ff);

View File

@ -199,12 +199,8 @@ add_node(struct snd_efw *efw, struct snd_info_entry *root, const char *name,
struct snd_info_entry *entry; struct snd_info_entry *entry;
entry = snd_info_create_card_entry(efw->card, name, root); entry = snd_info_create_card_entry(efw->card, name, root);
if (entry == NULL) if (entry)
return;
snd_info_set_text_ops(entry, efw, op); snd_info_set_text_ops(entry, efw, op);
if (snd_info_register(entry) < 0)
snd_info_free_entry(entry);
} }
void snd_efw_proc_init(struct snd_efw *efw) void snd_efw_proc_init(struct snd_efw *efw)
@ -220,10 +216,6 @@ void snd_efw_proc_init(struct snd_efw *efw)
if (root == NULL) if (root == NULL)
return; return;
root->mode = S_IFDIR | 0555; root->mode = S_IFDIR | 0555;
if (snd_info_register(root) < 0) {
snd_info_free_entry(root);
return;
}
add_node(efw, root, "clock", proc_read_clock); add_node(efw, root, "clock", proc_read_clock);
add_node(efw, root, "firmware", proc_read_hwinfo); add_node(efw, root, "firmware", proc_read_hwinfo);

View File

@ -136,7 +136,9 @@ static void read_pcm_s32(struct amdtp_stream *s,
byte = (u8 *)buffer + p->pcm_byte_offset; byte = (u8 *)buffer + p->pcm_byte_offset;
for (c = 0; c < channels; ++c) { for (c = 0; c < channels; ++c) {
*dst = (byte[0] << 24) | (byte[1] << 16) | byte[2]; *dst = (byte[0] << 24) |
(byte[1] << 16) |
(byte[2] << 8);
byte += 3; byte += 3;
dst++; dst++;
} }

View File

@ -87,12 +87,8 @@ static void add_node(struct snd_motu *motu, struct snd_info_entry *root,
struct snd_info_entry *entry; struct snd_info_entry *entry;
entry = snd_info_create_card_entry(motu->card, name, root); entry = snd_info_create_card_entry(motu->card, name, root);
if (entry == NULL) if (entry)
return;
snd_info_set_text_ops(entry, motu, op); snd_info_set_text_ops(entry, motu, op);
if (snd_info_register(entry) < 0)
snd_info_free_entry(entry);
} }
void snd_motu_proc_init(struct snd_motu *motu) void snd_motu_proc_init(struct snd_motu *motu)
@ -108,10 +104,6 @@ void snd_motu_proc_init(struct snd_motu *motu)
if (root == NULL) if (root == NULL)
return; return;
root->mode = S_IFDIR | 0555; root->mode = S_IFDIR | 0555;
if (snd_info_register(root) < 0) {
snd_info_free_entry(root);
return;
}
add_node(motu, root, "clock", proc_read_clock); add_node(motu, root, "clock", proc_read_clock);
add_node(motu, root, "format", proc_read_format); add_node(motu, root, "format", proc_read_format);

View File

@ -83,12 +83,8 @@ static void add_node(struct snd_oxfw *oxfw, struct snd_info_entry *root,
struct snd_info_entry *entry; struct snd_info_entry *entry;
entry = snd_info_create_card_entry(oxfw->card, name, root); entry = snd_info_create_card_entry(oxfw->card, name, root);
if (entry == NULL) if (entry)
return;
snd_info_set_text_ops(entry, oxfw, op); snd_info_set_text_ops(entry, oxfw, op);
if (snd_info_register(entry) < 0)
snd_info_free_entry(entry);
} }
void snd_oxfw_proc_init(struct snd_oxfw *oxfw) void snd_oxfw_proc_init(struct snd_oxfw *oxfw)
@ -104,10 +100,6 @@ void snd_oxfw_proc_init(struct snd_oxfw *oxfw)
if (root == NULL) if (root == NULL)
return; return;
root->mode = S_IFDIR | 0555; root->mode = S_IFDIR | 0555;
if (snd_info_register(root) < 0) {
snd_info_free_entry(root);
return;
}
add_node(oxfw, root, "formation", proc_read_formation); add_node(oxfw, root, "formation", proc_read_formation);
} }

View File

@ -58,12 +58,8 @@ static void add_node(struct snd_tscm *tscm, struct snd_info_entry *root,
struct snd_info_entry *entry; struct snd_info_entry *entry;
entry = snd_info_create_card_entry(tscm->card, name, root); entry = snd_info_create_card_entry(tscm->card, name, root);
if (entry == NULL) if (entry)
return;
snd_info_set_text_ops(entry, tscm, op); snd_info_set_text_ops(entry, tscm, op);
if (snd_info_register(entry) < 0)
snd_info_free_entry(entry);
} }
void snd_tscm_proc_init(struct snd_tscm *tscm) void snd_tscm_proc_init(struct snd_tscm *tscm)
@ -79,10 +75,6 @@ void snd_tscm_proc_init(struct snd_tscm *tscm)
if (root == NULL) if (root == NULL)
return; return;
root->mode = S_IFDIR | 0555; root->mode = S_IFDIR | 0555;
if (snd_info_register(root) < 0) {
snd_info_free_entry(root);
return;
}
add_node(tscm, root, "firmware", proc_read_firmware); add_node(tscm, root, "firmware", proc_read_firmware);
} }

View File

@ -376,7 +376,7 @@ void snd_hdac_bus_exit_link_reset(struct hdac_bus *bus)
{ {
unsigned long timeout; unsigned long timeout;
snd_hdac_chip_updateb(bus, GCTL, 0, AZX_GCTL_RESET); snd_hdac_chip_updateb(bus, GCTL, AZX_GCTL_RESET, AZX_GCTL_RESET);
timeout = jiffies + msecs_to_jiffies(100); timeout = jiffies + msecs_to_jiffies(100);
while (!snd_hdac_chip_readb(bus, GCTL) && time_before(jiffies, timeout)) while (!snd_hdac_chip_readb(bus, GCTL) && time_before(jiffies, timeout))
@ -415,7 +415,7 @@ int snd_hdac_bus_reset_link(struct hdac_bus *bus, bool full_reset)
} }
/* Accept unsolicited responses */ /* Accept unsolicited responses */
snd_hdac_chip_updatel(bus, GCTL, 0, AZX_GCTL_UNSOL); snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, AZX_GCTL_UNSOL);
/* detect codecs */ /* detect codecs */
if (!bus->codec_mask) { if (!bus->codec_mask) {
@ -431,7 +431,9 @@ EXPORT_SYMBOL_GPL(snd_hdac_bus_reset_link);
static void azx_int_enable(struct hdac_bus *bus) static void azx_int_enable(struct hdac_bus *bus)
{ {
/* enable controller CIE and GIE */ /* enable controller CIE and GIE */
snd_hdac_chip_updatel(bus, INTCTL, 0, AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN); snd_hdac_chip_updatel(bus, INTCTL,
AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN,
AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN);
} }
/* disable interrupts */ /* disable interrupts */

View File

@ -144,9 +144,9 @@ int snd_hdac_i915_init(struct hdac_bus *bus)
return -ENODEV; return -ENODEV;
if (!acomp->ops) { if (!acomp->ops) {
request_module("i915"); request_module("i915");
/* 10s timeout */ /* 60s timeout */
wait_for_completion_timeout(&bind_complete, wait_for_completion_timeout(&bind_complete,
msecs_to_jiffies(10 * 1000)); msecs_to_jiffies(60 * 1000));
} }
if (!acomp->ops) { if (!acomp->ops) {
dev_info(bus->dev, "couldn't bind with audio component\n"); dev_info(bus->dev, "couldn't bind with audio component\n");

View File

@ -12,6 +12,40 @@
#include <sound/hda_register.h> #include <sound/hda_register.h>
#include "trace.h" #include "trace.h"
/**
* snd_hdac_get_stream_stripe_ctl - get stripe control value
* @bus: HD-audio core bus
* @substream: PCM substream
*/
int snd_hdac_get_stream_stripe_ctl(struct hdac_bus *bus,
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int channels = runtime->channels,
rate = runtime->rate,
bits_per_sample = runtime->sample_bits,
max_sdo_lines, value, sdo_line;
/* T_AZA_GCAP_NSDO is 1:2 bitfields in GCAP */
max_sdo_lines = snd_hdac_chip_readl(bus, GCAP) & AZX_GCAP_NSDO;
/* following is from HD audio spec */
for (sdo_line = max_sdo_lines; sdo_line > 0; sdo_line >>= 1) {
if (rate > 48000)
value = (channels * bits_per_sample *
(rate / 48000)) / sdo_line;
else
value = (channels * bits_per_sample) / sdo_line;
if (value >= 8)
break;
}
/* stripe value: 0 for 1SDO, 1 for 2SDO, 2 for 4SDO lines */
return sdo_line >> 1;
}
EXPORT_SYMBOL_GPL(snd_hdac_get_stream_stripe_ctl);
/** /**
* snd_hdac_stream_init - initialize each stream (aka device) * snd_hdac_stream_init - initialize each stream (aka device)
* @bus: HD-audio core bus * @bus: HD-audio core bus
@ -48,6 +82,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_stream_init);
void snd_hdac_stream_start(struct hdac_stream *azx_dev, bool fresh_start) void snd_hdac_stream_start(struct hdac_stream *azx_dev, bool fresh_start)
{ {
struct hdac_bus *bus = azx_dev->bus; struct hdac_bus *bus = azx_dev->bus;
int stripe_ctl;
trace_snd_hdac_stream_start(bus, azx_dev); trace_snd_hdac_stream_start(bus, azx_dev);
@ -56,7 +91,13 @@ void snd_hdac_stream_start(struct hdac_stream *azx_dev, bool fresh_start)
azx_dev->start_wallclk -= azx_dev->period_wallclk; azx_dev->start_wallclk -= azx_dev->period_wallclk;
/* enable SIE */ /* enable SIE */
snd_hdac_chip_updatel(bus, INTCTL, 0, 1 << azx_dev->index); snd_hdac_chip_updatel(bus, INTCTL,
1 << azx_dev->index,
1 << azx_dev->index);
/* set stripe control */
stripe_ctl = snd_hdac_get_stream_stripe_ctl(bus, azx_dev->substream);
snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK,
stripe_ctl);
/* set DMA start and interrupt mask */ /* set DMA start and interrupt mask */
snd_hdac_stream_updateb(azx_dev, SD_CTL, snd_hdac_stream_updateb(azx_dev, SD_CTL,
0, SD_CTL_DMA_START | SD_INT_MASK); 0, SD_CTL_DMA_START | SD_INT_MASK);
@ -73,6 +114,7 @@ void snd_hdac_stream_clear(struct hdac_stream *azx_dev)
snd_hdac_stream_updateb(azx_dev, SD_CTL, snd_hdac_stream_updateb(azx_dev, SD_CTL,
SD_CTL_DMA_START | SD_INT_MASK, 0); SD_CTL_DMA_START | SD_INT_MASK, 0);
snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */ snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */
snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK, 0);
azx_dev->running = false; azx_dev->running = false;
} }
EXPORT_SYMBOL_GPL(snd_hdac_stream_clear); EXPORT_SYMBOL_GPL(snd_hdac_stream_clear);

View File

@ -492,9 +492,8 @@ static void snd_ak4113_proc_regs_read(struct snd_info_entry *entry,
static void snd_ak4113_proc_init(struct ak4113 *ak4113) static void snd_ak4113_proc_init(struct ak4113 *ak4113)
{ {
struct snd_info_entry *entry; snd_card_ro_proc_new(ak4113->card, "ak4113", ak4113,
if (!snd_card_proc_new(ak4113->card, "ak4113", &entry)) snd_ak4113_proc_regs_read);
snd_info_set_text_ops(entry, ak4113, snd_ak4113_proc_regs_read);
} }
int snd_ak4113_build(struct ak4113 *ak4113, int snd_ak4113_build(struct ak4113 *ak4113,

View File

@ -465,9 +465,8 @@ static void snd_ak4114_proc_regs_read(struct snd_info_entry *entry,
static void snd_ak4114_proc_init(struct ak4114 *ak4114) static void snd_ak4114_proc_init(struct ak4114 *ak4114)
{ {
struct snd_info_entry *entry; snd_card_ro_proc_new(ak4114->card, "ak4114", ak4114,
if (!snd_card_proc_new(ak4114->card, "ak4114", &entry)) snd_ak4114_proc_regs_read);
snd_info_set_text_ops(entry, ak4114, snd_ak4114_proc_regs_read);
} }
int snd_ak4114_build(struct ak4114 *ak4114, int snd_ak4114_build(struct ak4114 *ak4114,

View File

@ -875,13 +875,7 @@ static void proc_regs_read(struct snd_info_entry *entry,
static int proc_init(struct snd_akm4xxx *ak) static int proc_init(struct snd_akm4xxx *ak)
{ {
struct snd_info_entry *entry; return snd_card_ro_proc_new(ak->card, ak->name, ak, proc_regs_read);
int err;
err = snd_card_proc_new(ak->card, ak->name, &entry);
if (err < 0)
return err;
snd_info_set_text_ops(entry, ak, proc_regs_read);
return 0;
} }
int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak) int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak)

View File

@ -518,7 +518,6 @@ void snd_ad1816a_suspend(struct snd_ad1816a *chip)
int reg; int reg;
unsigned long flags; unsigned long flags;
snd_pcm_suspend_all(chip->pcm);
spin_lock_irqsave(&chip->lock, flags); spin_lock_irqsave(&chip->lock, flags);
for (reg = 0; reg < 48; reg++) for (reg = 0; reg < 48; reg++)
chip->image[reg] = snd_ad1816a_read(chip, reg); chip->image[reg] = snd_ad1816a_read(chip, reg);
@ -694,7 +693,7 @@ int snd_ad1816a_pcm(struct snd_ad1816a *chip, int device)
snd_ad1816a_init(chip); snd_ad1816a_init(chip);
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_isa_data(), chip->card->dev,
64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024);
chip->pcm = pcm; chip->pcm = pcm;

View File

@ -322,7 +322,6 @@ static int snd_als100_pnp_suspend(struct pnp_card_link *pcard, pm_message_t stat
struct snd_sb *chip = acard->chip; struct snd_sb *chip = acard->chip;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
snd_pcm_suspend_all(chip->pcm);
snd_sbmixer_suspend(chip); snd_sbmixer_suspend(chip);
return 0; return 0;
} }

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