sound updates for 4.2-rc1

It was a busy development cycle at this time, as you can see a wide
 range of changes in diffstat.  There are no big changes but many
 refactoring and improvements.  Here we go some highlights:
 
 * ALSA core:
 - Procfs codes were cleaned up to use seq_file
 - Procfs can be opt out via Kconfig (only for EXPERT)
 - Two types of jack API were unified finally; now both kctl and input
   jack devs are handled via a single function call.
 
 * HD-audio
 - Continued code restructuring for the future ASoC driver; now HDA
   controller driver is split to a core helper module.
 - Preliminary codes for Skylake audio support in HDA core.
 - Proper i915 gfx power well management for SKL & co
 - Enabled runtime PM as default for Intel HDMI/DP codecs
 - Newer Tegra chip supports
 - More quirks for Dell headsets, Alienware (with CA0132), etc.
 - A couple of DRM ELD helper API functions
 
 * ASoC
 - Support for loading ASoC topology maps from firmware, intended to be
   used to allow self-describing DSP firmware images to be built which
   can map controls added by the DSP to userspace without the kernel
   needing to know about individual DSP firmwares
 - Lots of refactoring to avoid direct access to snd_soc_codec where
   it's not needed supporting future refactoring
 - Big refactoring, cleanup and enhancement for the Wolfson ADSP driver
 - Cleanup series for TI TAS2552 and R-CAR drivers
 - Fixes and improvements on RT56xx codecs
 - Support for TI TAS571x power amplifiers
 - Support for Qualcomm APQ8016 and ZTE ZX296702 SoCs
 - Support for x86 systems with RT5650 and Qualcomm Storm
 - Support for Mediatek AFE (Audio Front End) unit
 - Other various small fixes to ASoC codec drivers
 
 * Firewire
 - Enhanced to allow non-blocking streams to use timestamp
   synchronization
 - Improve support for DM1500 and BeBoBv3
 
 * Misc
 - Cleanup of old pci API functions over all PCI sound drivers
 - Fix long-standing regression of the old powermac i2c setup
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iQIcBAABCAAGBQJVitjmAAoJEGwxgFQ9KSmksW8P/2ngNzNpo/bmmGh6xjB7GWU9
 RDAkqhKd6yvcClQojGS9n4a9CJ8nk5tdqTr9rMp58N7DRv6GYCPdq0A+lLOih+yC
 UPcTkTMBKm6UtvJjEcaasMxhvs5xno345oo5KrBdvlfv1rXe83dTtzEsybWYkaVD
 dJbbr5QFaiyj5cTp9nanK5kyTyDDXCdP+vjBGv5u9+GbVxQ6Eenyts89uSqEZs1F
 ltoBrl4VotXyqHKneJ0ttUKEimcVIgu8rCXH0sTtCg0SZVJFi+UXzI/VkkS+expL
 x9bNN6bw5UT9LA8+qybFRETx+8qchFsffzeUEle4wkIpVKXt/VqjP3GIvp6umlF5
 RhU5Wumf2KuIVjgVsYxd7bUkmHr4ywpqS3vSWMWU90FApJay7exatzLPyUVN0AxH
 pdAizc8NWFk1kVtWq8jr9agEdxDt2l+E9UXij+ViGyouMZL1oSvOo9NgovfwvfC6
 qKUisUkq53p1uPOW/U5gvF7bee2enEXMI9YUY1Z8MHx7nloq+25Nqma8P0gYthB8
 6Qk+t1oqC2p7ZMSkyVHH9nySQmoLITZHZmsHqqpLW+jFtanhuckDI75AvmrScs+r
 3+2YZXxPI0caZZ1qxMCd7Clmh7ZcSeRe73HXSXmF0xrLffISM3Yg3ZN10cbWQRj2
 D6TiHCspLpn+pcYLcWJ2
 =D78E
 -----END PGP SIGNATURE-----

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

Pull sound updates from Takashi Iwai:
 "It was a busy development cycle at this time, as you can see a wide
  range of changes in diffstat.  There are no big changes but many
  refactoring and improvements.  Here we go some highlights:

  ALSA core:
   - Procfs codes were cleaned up to use seq_file
   - Procfs can be opt out via Kconfig (only for EXPERT)
   - Two types of jack API were unified finally; now both kctl and input
     jack devs are handled via a single function call.

  HD-audio:
   - Continued code restructuring for the future ASoC driver; now HDA
     controller driver is split to a core helper module.
   - Preliminary codes for Skylake audio support in HDA core.
   - Proper i915 gfx power well management for SKL & co
   - Enabled runtime PM as default for Intel HDMI/DP codecs
   - Newer Tegra chip supports
   - More quirks for Dell headsets, Alienware (with CA0132), etc.
   - A couple of DRM ELD helper API functions

  ASoC:
   - Support for loading ASoC topology maps from firmware, intended to
     be used to allow self-describing DSP firmware images to be built
     which can map controls added by the DSP to userspace without the
     kernel needing to know about individual DSP firmwares
   - Lots of refactoring to avoid direct access to snd_soc_codec where
     it's not needed supporting future refactoring
   - Big refactoring, cleanup and enhancement for the Wolfson ADSP
     driver
   - Cleanup series for TI TAS2552 and R-CAR drivers
   - Fixes and improvements on RT56xx codecs
   - Support for TI TAS571x power amplifiers
   - Support for Qualcomm APQ8016 and ZTE ZX296702 SoCs
   - Support for x86 systems with RT5650 and Qualcomm Storm
   - Support for Mediatek AFE (Audio Front End) unit
   - Other various small fixes to ASoC codec drivers

  Firewire:
   - Enhanced to allow non-blocking streams to use timestamp
     synchronization
   - Improve support for DM1500 and BeBoBv3

  Misc:
   - Cleanup of old pci API functions over all PCI sound drivers
   - Fix long-standing regression of the old powermac i2c setup"

* tag 'sound-4.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (533 commits)
  ALSA: pcm: Fix pcm_class sysfs output
  ALSA: hda-beep: Update authors dead email address
  ASoC: wm_adsp: Move DSP Rate controls into the codec
  ASoC: wm8995: Fix setting sysclk for WM8995_SYSCLK_MCLK2 case
  ALSA: hda: provide default bus io ops extended hdac
  ALSA: hda: add hda link cleanup routine
  ALSA: hda: add hdac_ext stream creation and cleanup routines
  ASoC: rsrc-card: remove unused ret
  ALSA: HDAC: move SND_HDA_PREALLOC_SIZE to core
  ASoC: mediatek: Add machine driver for rt5650 rt5676 codec
  ASoC: mediatek: Add machine driver for MAX98090 codec
  ASoC: mediatek: Add AFE platform driver
  ASoC: rsnd: remove io from rsnd_mod
  ASoC: rsnd: move rsnd_mod_is_working() to rsnd_io_is_working()
  ASoC: rsnd: don't use rsnd_mod_to_io() on snd_kcontrol
  ASoC: rsnd: don't use rsnd_mod_to_io() on rsnd_src_xxx()
  ASoC: rsnd: don't use rsnd_mod_to_io() on rsnd_ssi_xxx()
  ASoC: rsnd: don't use rsnd_mod_to_io() on rsnd_dma_xxx()
  ASoC: rsnd: don't use rsnd_mod_to_io() on rsnd_get_adinr()
  ASoC: rsnd: add common interrupt handler for SSI/SRC/DMA
  ...
This commit is contained in:
Linus Torvalds 2015-06-25 17:15:18 -07:00
commit 4570a37169
415 changed files with 19361 additions and 6752 deletions

View File

@ -20,6 +20,8 @@ Optional properties:
pin configurations as described in the datasheet,
table 53. Note that the value of this property has
to be prefixed with '/bits/ 8'.
- avdd-supply: Power supply for AVDD, providing 3.3V
- dvdd-supply: Power supply for DVDD, providing 3.3V
Examples:
@ -28,6 +30,8 @@ Examples:
compatible = "adi,adau1701";
reg = <0x34>;
reset-gpio = <&gpio 23 0>;
avdd-supply = <&vdd_3v3_reg>;
dvdd-supply = <&vdd_3v3_reg>;
adi,pll-mode-gpios = <&gpio 24 0 &gpio 25 0>;
adi,pin-config = /bits/ 8 <0x4 0x7 0x5 0x5 0x4 0x4
0x4 0x4 0x4 0x4 0x4 0x4>;

View File

@ -0,0 +1,13 @@
Bluetooth-SCO audio CODEC
This device support generic Bluetooth SCO link.
Required properties:
- compatible : "delta,dfbmcs320"
Example:
codec: bt_sco {
compatible = "delta,dfbmcs320";
};

View File

@ -0,0 +1,13 @@
GTM601 UMTS modem audio interface CODEC
This device has no configuration interface. Sample rate is fixed - 8kHz.
Required properties:
- compatible : "option,gtm601"
Example:
codec: gtm601_codec {
compatible = "option,gtm601";
};

View File

@ -18,6 +18,12 @@ Optional properties:
- maxim,dmic-freq: Frequency at which to clock DMIC
- maxim,micbias: Micbias voltage applies to the analog mic, valid voltages value are:
0 - 2.2v
1 - 2.55v
2 - 2.4v
3 - 2.8v
Pins on the device (for linking into audio routes):
* MIC1

View File

@ -0,0 +1,13 @@
MT8173 with MAX98090 CODEC
Required properties:
- compatible : "mediatek,mt8173-max98090"
- mediatek,audio-codec: the phandle of the MAX98090 audio codec
Example:
sound {
compatible = "mediatek,mt8173-max98090";
mediatek,audio-codec = <&max98090>;
};

View File

@ -0,0 +1,13 @@
MT8173 with RT5650 RT5676 CODECS
Required properties:
- compatible : "mediatek,mt8173-rt5650-rt5676"
- mediatek,audio-codec: the phandles of rt5650 and rt5676 codecs
Example:
sound {
compatible = "mediatek,mt8173-rt5650-rt5676";
mediatek,audio-codec = <&rt5650 &rt5676>;
};

View File

@ -0,0 +1,45 @@
Mediatek AFE PCM controller
Required properties:
- compatible = "mediatek,mt8173-afe-pcm";
- reg: register location and size
- interrupts: Should contain AFE interrupt
- clock-names: should have these clock names:
"infra_sys_audio_clk",
"top_pdn_audio",
"top_pdn_aud_intbus",
"bck0",
"bck1",
"i2s0_m",
"i2s1_m",
"i2s2_m",
"i2s3_m",
"i2s3_b";
Example:
afe: mt8173-afe-pcm@11220000 {
compatible = "mediatek,mt8173-afe-pcm";
reg = <0 0x11220000 0 0x1000>;
interrupts = <GIC_SPI 134 IRQ_TYPE_EDGE_FALLING>;
clocks = <&infracfg INFRA_AUDIO>,
<&topckgen TOP_AUDIO_SEL>,
<&topckgen TOP_AUD_INTBUS_SEL>,
<&topckgen TOP_APLL1_DIV0>,
<&topckgen TOP_APLL2_DIV0>,
<&topckgen TOP_I2S0_M_CK_SEL>,
<&topckgen TOP_I2S1_M_CK_SEL>,
<&topckgen TOP_I2S2_M_CK_SEL>,
<&topckgen TOP_I2S3_M_CK_SEL>,
<&topckgen TOP_I2S3_B_CK_SEL>;
clock-names = "infra_sys_audio_clk",
"top_pdn_audio",
"top_pdn_aud_intbus",
"bck0",
"bck1",
"i2s0_m",
"i2s1_m",
"i2s2_m",
"i2s3_m",
"i2s3_b";
};

View File

@ -0,0 +1,60 @@
* Qualcomm Technologies APQ8016 SBC ASoC machine driver
This node models the Qualcomm Technologies APQ8016 SBC ASoC machine driver
Required properties:
- compatible : "qcom,apq8016-sbc-sndcard"
- pinctrl-N : One property must exist for each entry in
pinctrl-names. See ../pinctrl/pinctrl-bindings.txt
for details of the property values.
- pinctrl-names : Must contain a "default" entry.
- reg : Must contain an address for each entry in reg-names.
- reg-names : A list which must include the following entries:
* "mic-iomux"
* "spkr-iomux"
- qcom,model : Name of the sound card.
Dai-link subnode properties and subnodes:
Required dai-link subnodes:
- cpu : CPU sub-node
- codec : CODEC sub-node
Required CPU/CODEC subnodes properties:
-link-name : Name of the dai link.
-sound-dai : phandle and port of CPU/CODEC
-capture-dai : phandle and port of CPU/CODEC
Example:
sound: sound {
compatible = "qcom,apq8016-sbc-sndcard";
reg = <0x07702000 0x4>, <0x07702004 0x4>;
reg-names = "mic-iomux", "spkr-iomux";
qcom,model = "DB410c";
/* I2S - Internal codec */
internal-dai-link@0 {
cpu { /* PRIMARY */
sound-dai = <&lpass MI2S_PRIMARY>;
};
codec {
sound-dai = <&wcd_codec 0>;
};
};
/* External Primary or External Secondary -ADV7533 HDMI */
external-dai-link@0 {
link-name = "ADV7533";
cpu { /* QUAT */
sound-dai = <&lpass MI2S_QUATERNARY>;
};
codec {
sound-dai = <&adv_bridge 0>;
};
};
};

View File

@ -4,12 +4,21 @@ This node models the Qualcomm Technologies Low-Power Audio SubSystem (LPASS).
Required properties:
- compatible : "qcom,lpass-cpu"
- compatible : "qcom,lpass-cpu" or "qcom,apq8016-lpass-cpu"
- clocks : Must contain an entry for each entry in clock-names.
- clock-names : A list which must include the following entries:
* "ahbix-clk"
* "mi2s-osr-clk"
* "mi2s-bit-clk"
: required clocks for "qcom,lpass-cpu-apq8016"
* "ahbix-clk"
* "mi2s-bit-clk0"
* "mi2s-bit-clk1"
* "mi2s-bit-clk2"
* "mi2s-bit-clk3"
* "pcnoc-mport-clk"
* "pcnoc-sway-clk"
- interrupts : Must contain an entry for each entry in
interrupt-names.
- interrupt-names : A list which must include the following entries:
@ -22,6 +31,8 @@ Required properties:
- reg-names : A list which must include the following entries:
* "lpass-lpaif"
Optional properties:
- qcom,adsp : Phandle for the audio DSP node

View File

@ -5,6 +5,7 @@ Required properties:
"renesas,rcar_sound-gen1" if generation1, and
"renesas,rcar_sound-gen2" if generation2
Examples with soctypes are:
- "renesas,rcar_sound-r8a7778" (R-Car M1A)
- "renesas,rcar_sound-r8a7790" (R-Car H2)
- "renesas,rcar_sound-r8a7791" (R-Car M2-W)
- reg : Should contain the register physical address.
@ -47,7 +48,7 @@ DAI subnode properties:
Example:
rcar_sound: rcar_sound@ec500000 {
rcar_sound: sound@ec500000 {
#sound-dai-cells = <1>;
compatible = "renesas,rcar_sound-r8a7791", "renesas,rcar_sound-gen2";
reg = <0 0xec500000 0 0x1000>, /* SCU */

View File

@ -0,0 +1,72 @@
RT5650/RT5645 audio CODEC
This device supports I2C only.
Required properties:
- compatible : One of "realtek,rt5645" or "realtek,rt5650".
- reg : The I2C address of the device.
- interrupts : The CODEC's interrupt output.
Optional properties:
- hp-detect-gpios:
a GPIO spec for the external headphone detect pin. If jd-mode = 0,
we will get the JD status by getting the value of hp-detect-gpios.
- realtek,in2-differential
Boolean. Indicate MIC2 input are differential, rather than single-ended.
- realtek,dmic1-data-pin
0: dmic1 is not used
1: using IN2P pin as dmic1 data pin
2: using GPIO6 pin as dmic1 data pin
3: using GPIO10 pin as dmic1 data pin
4: using GPIO12 pin as dmic1 data pin
- realtek,dmic2-data-pin
0: dmic2 is not used
1: using IN2N pin as dmic2 data pin
2: using GPIO5 pin as dmic2 data pin
3: using GPIO11 pin as dmic2 data pin
-- realtek,jd-mode : The JD mode of rt5645/rt5650
0 : rt5645/rt5650 JD function is not used
1 : Mode-0 (VDD=3.3V), two port jack detection
2 : Mode-1 (VDD=3.3V), one port jack detection
3 : Mode-2 (VDD=1.8V), one port jack detection
Pins on the device (for linking into audio routes) for RT5645/RT5650:
* DMIC L1
* DMIC R1
* DMIC L2
* DMIC R2
* IN1P
* IN1N
* IN2P
* IN2N
* Haptic Generator
* HPOL
* HPOR
* LOUTL
* LOUTR
* PDM1L
* PDM1R
* SPOL
* SPOR
Example:
codec: rt5650@1a {
compatible = "realtek,rt5650";
reg = <0x1a>;
hp-detect-gpios = <&gpio 19 0>;
interrupt-parent = <&gpio>;
interrupts = <7 IRQ_TYPE_EDGE_FALLING>;
realtek,dmic-en = "true";
realtek,en-jd-func = "true";
realtek,jd-mode = <3>;
};

View File

@ -18,6 +18,7 @@ Required properties:
Optional properties:
- realtek,pow-ldo2-gpio : The GPIO that controls the CODEC's POW_LDO2 pin.
- realtek,reset-gpio : The GPIO that controls the CODEC's RESET pin.
- realtek,in1-differential
- realtek,in2-differential
@ -70,6 +71,7 @@ rt5677 {
realtek,pow-ldo2-gpio =
<&gpio TEGRA_GPIO(V, 3) GPIO_ACTIVE_HIGH>;
realtek,reset-gpio = <&gpio TEGRA_GPIO(BB, 3) GPIO_ACTIVE_LOW>;
realtek,in1-differential = "true";
realtek,gpio-config = /bits/ 8 <0 0 0 0 0 2>; /* pull up GPIO6 */
realtek,jd2-gpio = <3>; /* Enables Jack detection for GPIO6 */

View File

@ -16,7 +16,8 @@ Optional properties:
connection's sink, the second being the connection's
source.
- simple-audio-card,mclk-fs : Multiplication factor between stream rate and codec
mclk.
mclk. When defined, mclk-fs property defined in
dai-link sub nodes are ignored.
- simple-audio-card,hp-det-gpio : Reference to GPIO that signals when
headphones are attached.
- simple-audio-card,mic-det-gpio : Reference to GPIO that signals when
@ -55,6 +56,9 @@ Optional dai-link subnode properties:
dai-link uses bit clock inversion.
- frame-inversion : bool property. Add this if the
dai-link uses frame clock inversion.
- mclk-fs : Multiplication factor between stream
rate and codec mclk, applied only for
the dai-link.
For backward compatibility the frame-master and bitclock-master
properties can be used as booleans in codec subnode to indicate if the

View File

@ -14,6 +14,12 @@ Required properties:
Optional properties:
- enable-gpio - gpio pin to enable/disable the device
tas2552 can receive it's reference clock via MCLK, BCLK, IVCLKIN pin or use the
internal 1.8MHz. This CLKIN is used by the PLL. In addition to PLL, the PDM
reference clock is also selectable: PLL, IVCLKIN, BCLK or MCLK.
For system integration the dt-bindings/sound/tas2552.h header file provides
defined values to selct and configure the PLL and PDM reference clocks.
Example:
tas2552: tas2552@41 {

View File

@ -0,0 +1,41 @@
Texas Instruments TAS5711/TAS5717/TAS5719 stereo power amplifiers
The codec is controlled through an I2C interface. It also has two other
signals that can be wired up to GPIOs: reset (strongly recommended), and
powerdown (optional).
Required properties:
- compatible: "ti,tas5711", "ti,tas5717", or "ti,tas5719"
- reg: The I2C address of the device
- #sound-dai-cells: must be equal to 0
Optional properties:
- reset-gpios: GPIO specifier for the TAS571x's active low reset line
- pdn-gpios: GPIO specifier for the TAS571x's active low powerdown line
- clocks: clock phandle for the MCLK input
- clock-names: should be "mclk"
- AVDD-supply: regulator phandle for the AVDD supply (all chips)
- DVDD-supply: regulator phandle for the DVDD supply (all chips)
- HPVDD-supply: regulator phandle for the HPVDD supply (5717/5719)
- PVDD_AB-supply: regulator phandle for the PVDD_AB supply (5717/5719)
- PVDD_CD-supply: regulator phandle for the PVDD_CD supply (5717/5719)
- PVDD_A-supply: regulator phandle for the PVDD_A supply (5711)
- PVDD_B-supply: regulator phandle for the PVDD_B supply (5711)
- PVDD_C-supply: regulator phandle for the PVDD_C supply (5711)
- PVDD_D-supply: regulator phandle for the PVDD_D supply (5711)
Example:
tas5717: audio-codec@2a {
compatible = "ti,tas5717";
reg = <0x2a>;
#sound-dai-cells = <0>;
reset-gpios = <&gpio5 1 GPIO_ACTIVE_LOW>;
pdn-gpios = <&gpio5 2 GPIO_ACTIVE_LOW>;
clocks = <&clk_core CLK_I2S>;
clock-names = "mclk";
};

View File

@ -10,9 +10,20 @@ Required properties:
- reg : the I2C address of the device for I2C, the chip select
number for SPI.
Optional properties:
- diff-mode: Differential output mode configuration. Default value for field
DIFF in register R8 (MODE_CONTROL_2). If absent, the default is 0, shall be:
0 = stereo
1 = mono left
2 = stereo reversed
3 = mono right
Example:
codec: wm8741@1a {
compatible = "wlf,wm8741";
reg = <0x1a>;
diff-mode = <3>;
};

View File

@ -0,0 +1,44 @@
ZTE ZX296702 I2S controller
Required properties:
- compatible : Must be "zte,zx296702-i2s"
- reg : Must contain I2S core's registers location and length
- clocks : Pairs of phandle and specifier referencing the controller's clocks.
- clock-names: "tx" for the clock to the I2S interface.
- dmas: Pairs of phandle and specifier for the DMA channel that is used by
the core. The core expects two dma channels for transmit.
- dma-names : Must be "tx" and "rx"
For more details on the 'dma', 'dma-names', 'clock' and 'clock-names' properties
please check:
* resource-names.txt
* clock/clock-bindings.txt
* dma/dma.txt
Example:
i2s0: i2s0@0b005000 {
#sound-dai-cells = <0>;
compatible = "zte,zx296702-i2s";
reg = <0x0b005000 0x1000>;
clocks = <&lsp0clk ZX296702_I2S0_DIV>;
clock-names = "tx";
interrupts = <GIC_SPI 22 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&dma 5>, <&dma 6>;
dma-names = "tx", "rx";
status = "okay";
};
sound {
compatible = "simple-audio-card";
simple-audio-card,name = "zx296702_snd";
simple-audio-card,format = "left_j";
simple-audio-card,bitclock-master = <&sndcodec>;
simple-audio-card,frame-master = <&sndcodec>;
sndcpu: simple-audio-card,cpu {
sound-dai = <&i2s0>;
};
sndcodec: simple-audio-card,codec {
sound-dai = <&acodec>;
};
};

View File

@ -0,0 +1,28 @@
ZTE ZX296702 SPDIF controller
Required properties:
- compatible : Must be "zte,zx296702-spdif"
- reg : Must contain SPDIF core's registers location and length
- clocks : Pairs of phandle and specifier referencing the controller's clocks.
- clock-names: "tx" for the clock to the SPDIF interface.
- dmas: Pairs of phandle and specifier for the DMA channel that is used by
the core. The core expects one dma channel for transmit.
- dma-names : Must be "tx"
For more details on the 'dma', 'dma-names', 'clock' and 'clock-names' properties
please check:
* resource-names.txt
* clock/clock-bindings.txt
* dma/dma.txt
Example:
spdif0: spdif0@0b004000 {
compatible = "zte,zx296702-spdif";
reg = <0x0b004000 0x1000>;
clocks = <&lsp0clk ZX296702_SPDIF0_DIV>;
clock-names = "tx";
interrupts = <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&dma 4>;
dma-names = "tx";
status = "okay";
};

View File

@ -54,6 +54,7 @@ cosmic Cosmic Circuits
crystalfontz Crystalfontz America, Inc.
dallas Maxim Integrated Products (formerly Dallas Semiconductor)
davicom DAVICOM Semiconductor, Inc.
delta Delta Electronics, Inc.
denx Denx Software Engineering
digi Digi International Inc.
digilent Diglent, Inc.

View File

@ -11,7 +11,10 @@ ALC880
ALC260
======
N/A
gpio1 Enable GPIO1
coef Enable EAPD via COEF table
fujitsu Quirk for FSC S7020
fujitsu-jwse Quirk for FSC S7020 with jack modes and HP mic support
ALC262
======
@ -20,8 +23,9 @@ ALC262
ALC267/268
==========
inv-dmic Inverted internal mic workaround
hp-eapd Disable HP EAPD on NID 0x15
ALC269/270/275/276/28x/29x
ALC22x/23x/25x/269/27x/28x/29x (and vendor-specific ALC3xxx models)
======
laptop-amic Laptops with analog-mic input
laptop-dmic Laptops with digital-mic input
@ -29,9 +33,15 @@ ALC269/270/275/276/28x/29x
alc271-dmic Enable ALC271X digital mic workaround
inv-dmic Inverted internal mic workaround
headset-mic Indicates a combined headset (headphone+mic) jack
headset-mode More comprehensive headset support for ALC269 & co
headset-mode-no-hp-mic Headset mode support without headphone mic
lenovo-dock Enables docking station I/O for some Lenovos
hp-gpio-led GPIO LED support on HP laptops
dell-headset-multi Headset jack, which can also be used as mic-in
dell-headset-dock Headset jack (without mic-in), and also dock I/O
alc283-dac-wcaps Fixups for Chromebook with ALC283
alc283-sense-combo Combo jack sensing on ALC283
tpt440-dock Pin configs for Lenovo Thinkpad Dock support
ALC66x/67x/892
==============

View File

@ -0,0 +1,43 @@
Why we need Jack kcontrols
==========================
ALSA uses kcontrols to export audio controls(switch, volume, Mux, ...)
to user space. This means userspace applications like pulseaudio can
switch off headphones and switch on speakers when no headphones are
pluged in.
The old ALSA jack code only created input devices for each registered
jack. These jack input devices are not readable by userspace devices
that run as non root.
The new jack code creates embedded jack kcontrols for each jack that
can be read by any process.
This can be combined with UCM to allow userspace to route audio more
intelligently based on jack insertion or removal events.
Jack Kcontrol Internals
=======================
Each jack will have a kcontrol list, so that we can create a kcontrol
and attach it to the jack, at jack creation stage. We can also add a
kcontrol to an existing jack, at anytime when required.
Those kcontrols will be freed automatically when the Jack is freed.
How to use jack kcontrols
=========================
In order to keep compatibility, snd_jack_new() has been modified by
adding two params :-
- @initial_kctl: if true, create a kcontrol and add it to the jack
list.
- @phantom_jack: Don't create a input device for phantom jacks.
HDA jacks can set phantom_jack to true in order to create a phantom
jack and set initial_kctl to true to create an initial kcontrol with
the correct id.
ASoC jacks should set initial_kctl as false. The pin name will be
assigned as the jack kcontrol name.

View File

@ -41,7 +41,7 @@ pss_no_sound
This module parameter is a flag that can be used to tell the driver to
just configure non-sound components. 0 configures all components, a non-0
value will only attept to configure the CDROM and joystick ports. This
value will only attempt to configure the CDROM and joystick ports. This
parameter can be used by a user who only wished to use the builtin joystick
and/or CDROM port(s) of his PSS sound card. If this driver is loaded with this
parameter and with the parameter below set to true then a user can safely unload

View File

@ -1346,7 +1346,7 @@ implement nice real-time signal processing audio effect software and
network telephones. The ACI mixer has to be switched into the "solo"
mode for duplex operation in order to avoid feedback caused by the
mixer (input hears output signal). You can de-/activate this mode
through toggleing the record button for the wave controller with an
through toggling the record button for the wave controller with an
OSS-mixer.
The PCM20 contains a radio tuner, which is also controlled by

View File

@ -29,7 +29,7 @@ Driver Status
Still somewhat experimental. The driver should work stable, i.e. it
should'nt crash your box. It might not work as expected, have bugs,
not being fully OSS API compilant, ...
not being fully OSS API compliant, ...
Latest versions are available from http://bytesex.org/bttv/, the
driver is in the bttv tarball. Kernel patches might be available too,

View File

@ -10037,6 +10037,12 @@ L: netdev@vger.kernel.org
S: Maintained
F: drivers/net/ethernet/ti/netcp*
TI TAS571X FAMILY ASoC CODEC DRIVER
M: Kevin Cernekee <cernekee@chromium.org>
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
S: Odd Fixes
F: sound/soc/codecs/tas571x*
TI TWL4030 SERIES SOC CODEC DRIVER
M: Peter Ujfalusi <peter.ujfalusi@ti.com>
L: alsa-devel@alsa-project.org (moderated for non-subscribers)

View File

@ -465,6 +465,7 @@ static dma_cookie_t rcar_dmac_tx_submit(struct dma_async_tx_descriptor *tx)
static int rcar_dmac_desc_alloc(struct rcar_dmac_chan *chan, gfp_t gfp)
{
struct rcar_dmac_desc_page *page;
unsigned long flags;
LIST_HEAD(list);
unsigned int i;
@ -482,10 +483,10 @@ static int rcar_dmac_desc_alloc(struct rcar_dmac_chan *chan, gfp_t gfp)
list_add_tail(&desc->node, &list);
}
spin_lock_irq(&chan->lock);
spin_lock_irqsave(&chan->lock, flags);
list_splice_tail(&list, &chan->desc.free);
list_add_tail(&page->node, &chan->desc.pages);
spin_unlock_irq(&chan->lock);
spin_unlock_irqrestore(&chan->lock, flags);
return 0;
}
@ -516,6 +517,7 @@ static void rcar_dmac_desc_put(struct rcar_dmac_chan *chan,
static void rcar_dmac_desc_recycle_acked(struct rcar_dmac_chan *chan)
{
struct rcar_dmac_desc *desc, *_desc;
unsigned long flags;
LIST_HEAD(list);
/*
@ -524,9 +526,9 @@ static void rcar_dmac_desc_recycle_acked(struct rcar_dmac_chan *chan)
* list_for_each_entry_safe, isn't safe if we release the channel lock
* around the rcar_dmac_desc_put() call.
*/
spin_lock_irq(&chan->lock);
spin_lock_irqsave(&chan->lock, flags);
list_splice_init(&chan->desc.wait, &list);
spin_unlock_irq(&chan->lock);
spin_unlock_irqrestore(&chan->lock, flags);
list_for_each_entry_safe(desc, _desc, &list, node) {
if (async_tx_test_ack(&desc->async_tx)) {
@ -539,9 +541,9 @@ static void rcar_dmac_desc_recycle_acked(struct rcar_dmac_chan *chan)
return;
/* Put the remaining descriptors back in the wait list. */
spin_lock_irq(&chan->lock);
spin_lock_irqsave(&chan->lock, flags);
list_splice(&list, &chan->desc.wait);
spin_unlock_irq(&chan->lock);
spin_unlock_irqrestore(&chan->lock, flags);
}
/*
@ -556,12 +558,13 @@ static void rcar_dmac_desc_recycle_acked(struct rcar_dmac_chan *chan)
static struct rcar_dmac_desc *rcar_dmac_desc_get(struct rcar_dmac_chan *chan)
{
struct rcar_dmac_desc *desc;
unsigned long flags;
int ret;
/* Recycle acked descriptors before attempting allocation. */
rcar_dmac_desc_recycle_acked(chan);
spin_lock_irq(&chan->lock);
spin_lock_irqsave(&chan->lock, flags);
while (list_empty(&chan->desc.free)) {
/*
@ -570,17 +573,17 @@ static struct rcar_dmac_desc *rcar_dmac_desc_get(struct rcar_dmac_chan *chan)
* allocated descriptors. If the allocation fails return an
* error.
*/
spin_unlock_irq(&chan->lock);
spin_unlock_irqrestore(&chan->lock, flags);
ret = rcar_dmac_desc_alloc(chan, GFP_NOWAIT);
if (ret < 0)
return NULL;
spin_lock_irq(&chan->lock);
spin_lock_irqsave(&chan->lock, flags);
}
desc = list_first_entry(&chan->desc.free, struct rcar_dmac_desc, node);
list_del(&desc->node);
spin_unlock_irq(&chan->lock);
spin_unlock_irqrestore(&chan->lock, flags);
return desc;
}
@ -593,6 +596,7 @@ static struct rcar_dmac_desc *rcar_dmac_desc_get(struct rcar_dmac_chan *chan)
static int rcar_dmac_xfer_chunk_alloc(struct rcar_dmac_chan *chan, gfp_t gfp)
{
struct rcar_dmac_desc_page *page;
unsigned long flags;
LIST_HEAD(list);
unsigned int i;
@ -606,10 +610,10 @@ static int rcar_dmac_xfer_chunk_alloc(struct rcar_dmac_chan *chan, gfp_t gfp)
list_add_tail(&chunk->node, &list);
}
spin_lock_irq(&chan->lock);
spin_lock_irqsave(&chan->lock, flags);
list_splice_tail(&list, &chan->desc.chunks_free);
list_add_tail(&page->node, &chan->desc.pages);
spin_unlock_irq(&chan->lock);
spin_unlock_irqrestore(&chan->lock, flags);
return 0;
}
@ -627,9 +631,10 @@ static struct rcar_dmac_xfer_chunk *
rcar_dmac_xfer_chunk_get(struct rcar_dmac_chan *chan)
{
struct rcar_dmac_xfer_chunk *chunk;
unsigned long flags;
int ret;
spin_lock_irq(&chan->lock);
spin_lock_irqsave(&chan->lock, flags);
while (list_empty(&chan->desc.chunks_free)) {
/*
@ -638,18 +643,18 @@ rcar_dmac_xfer_chunk_get(struct rcar_dmac_chan *chan)
* allocated descriptors. If the allocation fails return an
* error.
*/
spin_unlock_irq(&chan->lock);
spin_unlock_irqrestore(&chan->lock, flags);
ret = rcar_dmac_xfer_chunk_alloc(chan, GFP_NOWAIT);
if (ret < 0)
return NULL;
spin_lock_irq(&chan->lock);
spin_lock_irqsave(&chan->lock, flags);
}
chunk = list_first_entry(&chan->desc.chunks_free,
struct rcar_dmac_xfer_chunk, node);
list_del(&chunk->node);
spin_unlock_irq(&chan->lock);
spin_unlock_irqrestore(&chan->lock, flags);
return chunk;
}

View File

@ -6479,6 +6479,9 @@ enum skl_disp_power_wells {
#define AUDIO_CP_READY(trans) ((1 << 1) << ((trans) * 4))
#define AUDIO_ELD_VALID(trans) ((1 << 0) << ((trans) * 4))
#define HSW_AUD_CHICKENBIT 0x65f10
#define SKL_AUD_CODEC_WAKE_SIGNAL (1 << 15)
/* HSW Power Wells */
#define HSW_PWR_WELL_BIOS 0x45400 /* CTL1 */
#define HSW_PWR_WELL_DRIVER 0x45404 /* CTL2 */

View File

@ -475,6 +475,32 @@ static void i915_audio_component_put_power(struct device *dev)
intel_display_power_put(dev_to_i915(dev), POWER_DOMAIN_AUDIO);
}
static void i915_audio_component_codec_wake_override(struct device *dev,
bool enable)
{
struct drm_i915_private *dev_priv = dev_to_i915(dev);
u32 tmp;
if (!IS_SKYLAKE(dev_priv))
return;
/*
* Enable/disable generating the codec wake signal, overriding the
* internal logic to generate the codec wake to controller.
*/
tmp = I915_READ(HSW_AUD_CHICKENBIT);
tmp &= ~SKL_AUD_CODEC_WAKE_SIGNAL;
I915_WRITE(HSW_AUD_CHICKENBIT, tmp);
usleep_range(1000, 1500);
if (enable) {
tmp = I915_READ(HSW_AUD_CHICKENBIT);
tmp |= SKL_AUD_CODEC_WAKE_SIGNAL;
I915_WRITE(HSW_AUD_CHICKENBIT, tmp);
usleep_range(1000, 1500);
}
}
/* Get CDCLK in kHz */
static int i915_audio_component_get_cdclk_freq(struct device *dev)
{
@ -495,6 +521,7 @@ static const struct i915_audio_component_ops i915_audio_component_ops = {
.owner = THIS_MODULE,
.get_power = i915_audio_component_get_power,
.put_power = i915_audio_component_put_power,
.codec_wake_override = i915_audio_component_codec_wake_override,
.get_cdclk_freq = i915_audio_component_get_cdclk_freq,
};

View File

@ -78,11 +78,6 @@ static int arizona_ldo1_hc_set_voltage_sel(struct regulator_dev *rdev,
if (ret != 0)
return ret;
ret = regmap_update_bits(regmap, ARIZONA_DYNAMIC_FREQUENCY_SCALING_1,
ARIZONA_SUBSYS_MAX_FREQ, val);
if (ret != 0)
return ret;
if (val)
return 0;

View File

@ -31,6 +31,7 @@ struct i915_audio_component {
struct module *owner;
void (*get_power)(struct device *);
void (*put_power)(struct device *);
void (*codec_wake_override)(struct device *, bool enable);
int (*get_cdclk_freq)(struct device *);
} *ops;
};

View File

@ -0,0 +1,9 @@
#ifndef __DT_APQ8016_LPASS_H
#define __DT_APQ8016_LPASS_H
#define MI2S_PRIMARY 0
#define MI2S_SECONDARY 1
#define MI2S_TERTIARY 2
#define MI2S_QUATERNARY 3
#endif /* __DT_APQ8016_LPASS_H */

View File

@ -0,0 +1,9 @@
#ifndef __AUDIO_JACK_EVENTS_H
#define __AUDIO_JACK_EVENTS_H
#define JACK_HEADPHONE 1
#define JACK_MICROPHONE 2
#define JACK_LINEOUT 3
#define JACK_LINEIN 4
#endif /* __AUDIO_JACK_EVENTS_H */

View File

@ -0,0 +1,18 @@
#ifndef __DT_TAS2552_H
#define __DT_TAS2552_H
#define TAS2552_PLL_CLKIN (0)
#define TAS2552_PDM_CLK (1)
#define TAS2552_CLK_TARGET_MASK (1)
#define TAS2552_PLL_CLKIN_MCLK ((0 << 1) | TAS2552_PLL_CLKIN)
#define TAS2552_PLL_CLKIN_BCLK ((1 << 1) | TAS2552_PLL_CLKIN)
#define TAS2552_PLL_CLKIN_IVCLKIN ((2 << 1) | TAS2552_PLL_CLKIN)
#define TAS2552_PLL_CLKIN_1_8_FIXED ((3 << 1) | TAS2552_PLL_CLKIN)
#define TAS2552_PDM_CLK_PLL ((0 << 1) | TAS2552_PDM_CLK)
#define TAS2552_PDM_CLK_IVCLKIN ((1 << 1) | TAS2552_PDM_CLK)
#define TAS2552_PDM_CLK_BCLK ((2 << 1) | TAS2552_PDM_CLK)
#define TAS2552_PDM_CLK_MCLK ((3 << 1) | TAS2552_PDM_CLK)
#endif /* __DT_TAS2552_H */

View File

@ -252,7 +252,7 @@ void snd_ctl_sync_vmaster(struct snd_kcontrol *kctl, bool hook_only);
* Helper functions for jack-detection controls
*/
struct snd_kcontrol *
snd_kctl_jack_new(const char *name, int idx, void *private_data);
snd_kctl_jack_new(const char *name, struct snd_card *card);
void snd_kctl_jack_report(struct snd_card *card,
struct snd_kcontrol *kctl, bool status);

View File

@ -224,16 +224,13 @@ void *snd_lookup_oss_minor_data(unsigned int minor, int type);
#endif
int snd_minor_info_init(void);
int snd_minor_info_done(void);
/* sound_oss.c */
#ifdef CONFIG_SND_OSSEMUL
int snd_minor_info_oss_init(void);
int snd_minor_info_oss_done(void);
#else
static inline int snd_minor_info_oss_init(void) { return 0; }
static inline int snd_minor_info_oss_done(void) { return 0; }
#endif
/* memory.c */
@ -262,7 +259,6 @@ int snd_card_free_when_closed(struct snd_card *card);
void snd_card_set_id(struct snd_card *card, const char *id);
int snd_card_register(struct snd_card *card);
int snd_card_info_init(void);
int snd_card_info_done(void);
int snd_card_add_dev_attr(struct snd_card *card,
const struct attribute_group *group);
int snd_component_add(struct snd_card *card, const char *component);

View File

@ -90,11 +90,6 @@ void snd_dmaengine_pcm_set_config_from_dai_data(
* makes sense if SND_DMAENGINE_PCM_FLAG_COMPAT is set as well.
*/
#define SND_DMAENGINE_PCM_FLAG_NO_DT BIT(1)
/*
* The platforms dmaengine driver does not support reporting the amount of
* bytes that are still left to transfer.
*/
#define SND_DMAENGINE_PCM_FLAG_NO_RESIDUE BIT(2)
/*
* The PCM is half duplex and the DMA channel is shared between capture and
* playback.

View File

@ -125,7 +125,7 @@ struct snd_emux {
struct snd_util_memhdr *memhdr; /* memory chunk information */
#ifdef CONFIG_PROC_FS
#ifdef CONFIG_SND_PROC_FS
struct snd_info_entry *proc;
#endif

36
include/sound/hda_i915.h Normal file
View File

@ -0,0 +1,36 @@
/*
* HD-Audio helpers to sync with i915 driver
*/
#ifndef __SOUND_HDA_I915_H
#define __SOUND_HDA_I915_H
#ifdef CONFIG_SND_HDA_I915
int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable);
int snd_hdac_display_power(struct hdac_bus *bus, bool enable);
int snd_hdac_get_display_clk(struct hdac_bus *bus);
int snd_hdac_i915_init(struct hdac_bus *bus);
int snd_hdac_i915_exit(struct hdac_bus *bus);
#else
static int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable)
{
return 0;
}
static inline int snd_hdac_display_power(struct hdac_bus *bus, bool enable)
{
return 0;
}
static inline int snd_hdac_get_display_clk(struct hdac_bus *bus)
{
return 0;
}
static inline int snd_hdac_i915_init(struct hdac_bus *bus)
{
return -ENODEV;
}
static inline int snd_hdac_i915_exit(struct hdac_bus *bus)
{
return 0;
}
#endif
#endif /* __SOUND_HDA_I915_H */

View File

@ -0,0 +1,244 @@
/*
* HD-audio controller (Azalia) registers and helpers
*
* For traditional reasons, we still use azx_ prefix here
*/
#ifndef __SOUND_HDA_REGISTER_H
#define __SOUND_HDA_REGISTER_H
#include <linux/io.h>
#include <sound/hdaudio.h>
#define AZX_REG_GCAP 0x00
#define AZX_GCAP_64OK (1 << 0) /* 64bit address support */
#define AZX_GCAP_NSDO (3 << 1) /* # of serial data out signals */
#define AZX_GCAP_BSS (31 << 3) /* # of bidirectional streams */
#define AZX_GCAP_ISS (15 << 8) /* # of input streams */
#define AZX_GCAP_OSS (15 << 12) /* # of output streams */
#define AZX_REG_VMIN 0x02
#define AZX_REG_VMAJ 0x03
#define AZX_REG_OUTPAY 0x04
#define AZX_REG_INPAY 0x06
#define AZX_REG_GCTL 0x08
#define AZX_GCTL_RESET (1 << 0) /* controller reset */
#define AZX_GCTL_FCNTRL (1 << 1) /* flush control */
#define AZX_GCTL_UNSOL (1 << 8) /* accept unsol. response enable */
#define AZX_REG_WAKEEN 0x0c
#define AZX_REG_STATESTS 0x0e
#define AZX_REG_GSTS 0x10
#define AZX_GSTS_FSTS (1 << 1) /* flush status */
#define AZX_REG_GCAP2 0x12
#define AZX_REG_LLCH 0x14
#define AZX_REG_OUTSTRMPAY 0x18
#define AZX_REG_INSTRMPAY 0x1A
#define AZX_REG_INTCTL 0x20
#define AZX_REG_INTSTS 0x24
#define AZX_REG_WALLCLK 0x30 /* 24Mhz source */
#define AZX_REG_OLD_SSYNC 0x34 /* SSYNC for old ICH */
#define AZX_REG_SSYNC 0x38
#define AZX_REG_CORBLBASE 0x40
#define AZX_REG_CORBUBASE 0x44
#define AZX_REG_CORBWP 0x48
#define AZX_REG_CORBRP 0x4a
#define AZX_CORBRP_RST (1 << 15) /* read pointer reset */
#define AZX_REG_CORBCTL 0x4c
#define AZX_CORBCTL_RUN (1 << 1) /* enable DMA */
#define AZX_CORBCTL_CMEIE (1 << 0) /* enable memory error irq */
#define AZX_REG_CORBSTS 0x4d
#define AZX_CORBSTS_CMEI (1 << 0) /* memory error indication */
#define AZX_REG_CORBSIZE 0x4e
#define AZX_REG_RIRBLBASE 0x50
#define AZX_REG_RIRBUBASE 0x54
#define AZX_REG_RIRBWP 0x58
#define AZX_RIRBWP_RST (1 << 15) /* write pointer reset */
#define AZX_REG_RINTCNT 0x5a
#define AZX_REG_RIRBCTL 0x5c
#define AZX_RBCTL_IRQ_EN (1 << 0) /* enable IRQ */
#define AZX_RBCTL_DMA_EN (1 << 1) /* enable DMA */
#define AZX_RBCTL_OVERRUN_EN (1 << 2) /* enable overrun irq */
#define AZX_REG_RIRBSTS 0x5d
#define AZX_RBSTS_IRQ (1 << 0) /* response irq */
#define AZX_RBSTS_OVERRUN (1 << 2) /* overrun irq */
#define AZX_REG_RIRBSIZE 0x5e
#define AZX_REG_IC 0x60
#define AZX_REG_IR 0x64
#define AZX_REG_IRS 0x68
#define AZX_IRS_VALID (1<<1)
#define AZX_IRS_BUSY (1<<0)
#define AZX_REG_DPLBASE 0x70
#define AZX_REG_DPUBASE 0x74
#define AZX_DPLBASE_ENABLE 0x1 /* Enable position buffer */
/* SD offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
/* stream register offsets from stream base */
#define AZX_REG_SD_CTL 0x00
#define AZX_REG_SD_STS 0x03
#define AZX_REG_SD_LPIB 0x04
#define AZX_REG_SD_CBL 0x08
#define AZX_REG_SD_LVI 0x0c
#define AZX_REG_SD_FIFOW 0x0e
#define AZX_REG_SD_FIFOSIZE 0x10
#define AZX_REG_SD_FORMAT 0x12
#define AZX_REG_SD_FIFOL 0x14
#define AZX_REG_SD_BDLPL 0x18
#define AZX_REG_SD_BDLPU 0x1c
/* Haswell/Broadwell display HD-A controller Extended Mode registers */
#define AZX_REG_HSW_EM4 0x100c
#define AZX_REG_HSW_EM5 0x1010
/* PCI space */
#define AZX_PCIREG_TCSEL 0x44
/*
* other constants
*/
/* max number of fragments - we may use more if allocating more pages for BDL */
#define BDL_SIZE 4096
#define AZX_MAX_BDL_ENTRIES (BDL_SIZE / 16)
#define AZX_MAX_FRAG 32
/* max buffer size - no h/w limit, you can increase as you like */
#define AZX_MAX_BUF_SIZE (1024*1024*1024)
/* RIRB int mask: overrun[2], response[0] */
#define RIRB_INT_RESPONSE 0x01
#define RIRB_INT_OVERRUN 0x04
#define RIRB_INT_MASK 0x05
/* STATESTS int mask: S3,SD2,SD1,SD0 */
#define STATESTS_INT_MASK ((1 << HDA_MAX_CODECS) - 1)
/* SD_CTL bits */
#define SD_CTL_STREAM_RESET 0x01 /* stream reset bit */
#define SD_CTL_DMA_START 0x02 /* stream DMA start bit */
#define SD_CTL_STRIPE (3 << 16) /* stripe control */
#define SD_CTL_TRAFFIC_PRIO (1 << 18) /* traffic priority */
#define SD_CTL_DIR (1 << 19) /* bi-directional stream */
#define SD_CTL_STREAM_TAG_MASK (0xf << 20)
#define SD_CTL_STREAM_TAG_SHIFT 20
/* SD_CTL and SD_STS */
#define SD_INT_DESC_ERR 0x10 /* descriptor error interrupt */
#define SD_INT_FIFO_ERR 0x08 /* FIFO error interrupt */
#define SD_INT_COMPLETE 0x04 /* completion interrupt */
#define SD_INT_MASK (SD_INT_DESC_ERR|SD_INT_FIFO_ERR|\
SD_INT_COMPLETE)
/* SD_STS */
#define SD_STS_FIFO_READY 0x20 /* FIFO ready */
/* INTCTL and INTSTS */
#define AZX_INT_ALL_STREAM 0xff /* all stream interrupts */
#define AZX_INT_CTRL_EN 0x40000000 /* controller interrupt enable bit */
#define AZX_INT_GLOBAL_EN 0x80000000 /* global interrupt enable bit */
/* below are so far hardcoded - should read registers in future */
#define AZX_MAX_CORB_ENTRIES 256
#define AZX_MAX_RIRB_ENTRIES 256
/* Capability header Structure */
#define AZX_REG_CAP_HDR 0x0
#define AZX_CAP_HDR_VER_OFF 28
#define AZX_CAP_HDR_VER_MASK (0xF << AZX_CAP_HDR_VER_OFF)
#define AZX_CAP_HDR_ID_OFF 16
#define AZX_CAP_HDR_ID_MASK (0xFFF << AZX_CAP_HDR_ID_OFF)
#define AZX_CAP_HDR_NXT_PTR_MASK 0xFFFF
/* registers of Software Position Based FIFO Capability Structure */
#define AZX_SPB_CAP_ID 0x4
#define AZX_REG_SPB_BASE_ADDR 0x700
#define AZX_REG_SPB_SPBFCH 0x00
#define AZX_REG_SPB_SPBFCCTL 0x04
/* Base used to calculate the iterating register offset */
#define AZX_SPB_BASE 0x08
/* Interval used to calculate the iterating register offset */
#define AZX_SPB_INTERVAL 0x08
/* registers of Global Time Synchronization Capability Structure */
#define AZX_GTS_CAP_ID 0x1
#define AZX_REG_GTS_GTSCH 0x00
#define AZX_REG_GTS_GTSCD 0x04
#define AZX_REG_GTS_GTSCTLAC 0x0C
#define AZX_GTS_BASE 0x20
#define AZX_GTS_INTERVAL 0x20
/* registers for Processing Pipe Capability Structure */
#define AZX_PP_CAP_ID 0x3
#define AZX_REG_PP_PPCH 0x10
#define AZX_REG_PP_PPCTL 0x04
#define AZX_PPCTL_PIE (1<<31)
#define AZX_PPCTL_GPROCEN (1<<30)
/* _X_ = dma engine # and cannot * exceed 29 (per spec max 30 dma engines) */
#define AZX_PPCTL_PROCEN(_X_) (1<<(_X_))
#define AZX_REG_PP_PPSTS 0x08
#define AZX_PPHC_BASE 0x10
#define AZX_PPHC_INTERVAL 0x10
#define AZX_REG_PPHCLLPL 0x0
#define AZX_REG_PPHCLLPU 0x4
#define AZX_REG_PPHCLDPL 0x8
#define AZX_REG_PPHCLDPU 0xC
#define AZX_PPLC_BASE 0x10
#define AZX_PPLC_MULTI 0x10
#define AZX_PPLC_INTERVAL 0x10
#define AZX_REG_PPLCCTL 0x0
#define AZX_PPLCCTL_STRM_BITS 4
#define AZX_PPLCCTL_STRM_SHIFT 20
#define AZX_REG_MASK(bit_num, offset) \
(((1 << (bit_num)) - 1) << (offset))
#define AZX_PPLCCTL_STRM_MASK \
AZX_REG_MASK(AZX_PPLCCTL_STRM_BITS, AZX_PPLCCTL_STRM_SHIFT)
#define AZX_PPLCCTL_RUN (1<<1)
#define AZX_PPLCCTL_STRST (1<<0)
#define AZX_REG_PPLCFMT 0x4
#define AZX_REG_PPLCLLPL 0x8
#define AZX_REG_PPLCLLPU 0xC
/* registers for Multiple Links Capability Structure */
#define AZX_ML_CAP_ID 0x2
#define AZX_REG_ML_MLCH 0x00
#define AZX_REG_ML_MLCD 0x04
#define AZX_ML_BASE 0x40
#define AZX_ML_INTERVAL 0x40
#define AZX_REG_ML_LCAP 0x00
#define AZX_REG_ML_LCTL 0x04
#define AZX_REG_ML_LOSIDV 0x08
#define AZX_REG_ML_LSDIID 0x0C
#define AZX_REG_ML_LPSOO 0x10
#define AZX_REG_ML_LPSIO 0x12
#define AZX_REG_ML_LWALFC 0x18
#define AZX_REG_ML_LOUTPAY 0x20
#define AZX_REG_ML_LINPAY 0x30
#define AZX_MLCTL_SPA (1<<16)
#define AZX_MLCTL_CPA 23
/*
* helpers to read the stream position
*/
static inline unsigned int
snd_hdac_stream_get_pos_lpib(struct hdac_stream *stream)
{
return snd_hdac_stream_readl(stream, SD_LPIB);
}
static inline unsigned int
snd_hdac_stream_get_pos_posbuf(struct hdac_stream *stream)
{
return le32_to_cpu(*stream->posbuf);
}
#endif /* __SOUND_HDA_REGISTER_H */

View File

@ -6,12 +6,18 @@
#define __SOUND_HDAUDIO_H
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/timecounter.h>
#include <sound/core.h>
#include <sound/memalloc.h>
#include <sound/hda_verbs.h>
#include <drm/i915_component.h>
/* codec node id */
typedef u16 hda_nid_t;
struct hdac_bus;
struct hdac_stream;
struct hdac_device;
struct hdac_driver;
struct hdac_widget_tree;
@ -21,6 +27,16 @@ struct hdac_widget_tree;
*/
extern struct bus_type snd_hda_bus_type;
/*
* HDA device table
*/
struct hda_device_id {
__u32 vendor_id;
__u32 rev_id;
const char *name;
unsigned long driver_data;
};
/*
* generic arrays
*/
@ -69,6 +85,7 @@ struct hdac_device {
/* misc flags */
atomic_t in_pm; /* suspend/resume being performed */
bool link_power_control:1;
/* sysfs */
struct hdac_widget_tree *widgets;
@ -85,6 +102,7 @@ struct hdac_device {
enum {
HDA_DEV_CORE,
HDA_DEV_LEGACY,
HDA_DEV_ASOC,
};
/* direction */
@ -118,6 +136,15 @@ int snd_hdac_get_connections(struct hdac_device *codec, hda_nid_t nid,
hda_nid_t *conn_list, int max_conns);
int snd_hdac_get_sub_nodes(struct hdac_device *codec, hda_nid_t nid,
hda_nid_t *start_id);
unsigned int snd_hdac_calc_stream_format(unsigned int rate,
unsigned int channels,
unsigned int format,
unsigned int maxbps,
unsigned short spdif_ctls);
int snd_hdac_query_supported_pcm(struct hdac_device *codec, hda_nid_t nid,
u32 *ratesp, u64 *formatsp, unsigned int *bpsp);
bool snd_hdac_is_supported_format(struct hdac_device *codec, hda_nid_t nid,
unsigned int format);
/**
* snd_hdac_read_parm - read a codec parameter
@ -154,14 +181,18 @@ static inline void snd_hdac_power_down_pm(struct hdac_device *codec) {}
struct hdac_driver {
struct device_driver driver;
int type;
const struct hda_device_id *id_table;
int (*match)(struct hdac_device *dev, struct hdac_driver *drv);
void (*unsol_event)(struct hdac_device *dev, unsigned int event);
};
#define drv_to_hdac_driver(_drv) container_of(_drv, struct hdac_driver, driver)
const struct hda_device_id *
hdac_get_device_id(struct hdac_device *hdev, struct hdac_driver *drv);
/*
* HD-audio bus base driver
* Bus verb operators
*/
struct hdac_bus_ops {
/* send a single command */
@ -169,13 +200,59 @@ struct hdac_bus_ops {
/* get a response from the last command */
int (*get_response)(struct hdac_bus *bus, unsigned int addr,
unsigned int *res);
/* control the link power */
int (*link_power)(struct hdac_bus *bus, bool enable);
};
/*
* Lowlevel I/O operators
*/
struct hdac_io_ops {
/* mapped register accesses */
void (*reg_writel)(u32 value, u32 __iomem *addr);
u32 (*reg_readl)(u32 __iomem *addr);
void (*reg_writew)(u16 value, u16 __iomem *addr);
u16 (*reg_readw)(u16 __iomem *addr);
void (*reg_writeb)(u8 value, u8 __iomem *addr);
u8 (*reg_readb)(u8 __iomem *addr);
/* Allocation ops */
int (*dma_alloc_pages)(struct hdac_bus *bus, int type, size_t size,
struct snd_dma_buffer *buf);
void (*dma_free_pages)(struct hdac_bus *bus,
struct snd_dma_buffer *buf);
};
#define HDA_UNSOL_QUEUE_SIZE 64
#define HDA_MAX_CODECS 8 /* limit by controller side */
/* HD Audio class code */
#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403
/*
* CORB/RIRB
*
* Each CORB entry is 4byte, RIRB is 8byte
*/
struct hdac_rb {
__le32 *buf; /* virtual address of CORB/RIRB buffer */
dma_addr_t addr; /* physical address of CORB/RIRB buffer */
unsigned short rp, wp; /* RIRB read/write pointers */
int cmds[HDA_MAX_CODECS]; /* number of pending requests */
u32 res[HDA_MAX_CODECS]; /* last read value */
};
/*
* HD-audio bus base driver
*/
struct hdac_bus {
struct device *dev;
const struct hdac_bus_ops *ops;
const struct hdac_io_ops *io_ops;
/* h/w resources */
unsigned long addr;
void __iomem *remap_addr;
int irq;
/* codec linked list */
struct list_head codec_list;
@ -189,18 +266,49 @@ struct hdac_bus {
unsigned int unsol_rp, unsol_wp;
struct work_struct unsol_work;
/* bit flags of detected codecs */
unsigned long codec_mask;
/* bit flags of powered codecs */
unsigned long codec_powered;
/* flags */
/* CORB/RIRB */
struct hdac_rb corb;
struct hdac_rb rirb;
unsigned int last_cmd[HDA_MAX_CODECS]; /* last sent command */
/* CORB/RIRB and position buffers */
struct snd_dma_buffer rb;
struct snd_dma_buffer posbuf;
/* hdac_stream linked list */
struct list_head stream_list;
/* operation state */
bool chip_init:1; /* h/w initialized */
/* behavior flags */
bool sync_write:1; /* sync after verb write */
bool use_posbuf:1; /* use position buffer */
bool snoop:1; /* enable snooping */
bool align_bdle_4k:1; /* BDLE align 4K boundary */
bool reverse_assign:1; /* assign devices in reverse order */
bool corbrp_self_clear:1; /* CORBRP clears itself after reset */
int bdl_pos_adj; /* BDL position adjustment */
/* locks */
spinlock_t reg_lock;
struct mutex cmd_mutex;
/* i915 component interface */
struct i915_audio_component *audio_component;
int i915_power_refcount;
};
int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
const struct hdac_bus_ops *ops);
const struct hdac_bus_ops *ops,
const struct hdac_io_ops *io_ops);
void snd_hdac_bus_exit(struct hdac_bus *bus);
int snd_hdac_bus_exec_verb(struct hdac_bus *bus, unsigned int addr,
unsigned int cmd, unsigned int *res);
@ -222,6 +330,201 @@ static inline void snd_hdac_codec_link_down(struct hdac_device *codec)
clear_bit(codec->addr, &codec->bus->codec_powered);
}
int snd_hdac_bus_send_cmd(struct hdac_bus *bus, unsigned int val);
int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr,
unsigned int *res);
int snd_hdac_link_power(struct hdac_device *codec, bool enable);
bool snd_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset);
void snd_hdac_bus_stop_chip(struct hdac_bus *bus);
void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus);
void snd_hdac_bus_stop_cmd_io(struct hdac_bus *bus);
void snd_hdac_bus_enter_link_reset(struct hdac_bus *bus);
void snd_hdac_bus_exit_link_reset(struct hdac_bus *bus);
void snd_hdac_bus_update_rirb(struct hdac_bus *bus);
void snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status,
void (*ack)(struct hdac_bus *,
struct hdac_stream *));
int snd_hdac_bus_alloc_stream_pages(struct hdac_bus *bus);
void snd_hdac_bus_free_stream_pages(struct hdac_bus *bus);
/*
* macros for easy use
*/
#define _snd_hdac_chip_write(type, chip, reg, value) \
((chip)->io_ops->reg_write ## type(value, (chip)->remap_addr + (reg)))
#define _snd_hdac_chip_read(type, chip, reg) \
((chip)->io_ops->reg_read ## type((chip)->remap_addr + (reg)))
/* read/write a register, pass without AZX_REG_ prefix */
#define snd_hdac_chip_writel(chip, reg, value) \
_snd_hdac_chip_write(l, chip, AZX_REG_ ## reg, value)
#define snd_hdac_chip_writew(chip, reg, value) \
_snd_hdac_chip_write(w, chip, AZX_REG_ ## reg, value)
#define snd_hdac_chip_writeb(chip, reg, value) \
_snd_hdac_chip_write(b, chip, AZX_REG_ ## reg, value)
#define snd_hdac_chip_readl(chip, reg) \
_snd_hdac_chip_read(l, chip, AZX_REG_ ## reg)
#define snd_hdac_chip_readw(chip, reg) \
_snd_hdac_chip_read(w, chip, AZX_REG_ ## reg)
#define snd_hdac_chip_readb(chip, reg) \
_snd_hdac_chip_read(b, chip, AZX_REG_ ## reg)
/* update a register, pass without AZX_REG_ prefix */
#define snd_hdac_chip_updatel(chip, reg, mask, val) \
snd_hdac_chip_writel(chip, reg, \
(snd_hdac_chip_readl(chip, reg) & ~(mask)) | (val))
#define snd_hdac_chip_updatew(chip, reg, mask, val) \
snd_hdac_chip_writew(chip, reg, \
(snd_hdac_chip_readw(chip, reg) & ~(mask)) | (val))
#define snd_hdac_chip_updateb(chip, reg, mask, val) \
snd_hdac_chip_writeb(chip, reg, \
(snd_hdac_chip_readb(chip, reg) & ~(mask)) | (val))
/*
* HD-audio stream
*/
struct hdac_stream {
struct hdac_bus *bus;
struct snd_dma_buffer bdl; /* BDL buffer */
__le32 *posbuf; /* position buffer pointer */
int direction; /* playback / capture (SNDRV_PCM_STREAM_*) */
unsigned int bufsize; /* size of the play buffer in bytes */
unsigned int period_bytes; /* size of the period in bytes */
unsigned int frags; /* number for period in the play buffer */
unsigned int fifo_size; /* FIFO size */
void __iomem *sd_addr; /* stream descriptor pointer */
u32 sd_int_sta_mask; /* stream int status mask */
/* pcm support */
struct snd_pcm_substream *substream; /* assigned substream,
* set in PCM open
*/
unsigned int format_val; /* format value to be set in the
* controller and the codec
*/
unsigned char stream_tag; /* assigned stream */
unsigned char index; /* stream index */
int assigned_key; /* last device# key assigned to */
bool opened:1;
bool running:1;
bool prepared:1;
bool no_period_wakeup:1;
bool locked:1;
/* timestamp */
unsigned long start_wallclk; /* start + minimum wallclk */
unsigned long period_wallclk; /* wallclk for period */
struct timecounter tc;
struct cyclecounter cc;
int delay_negative_threshold;
struct list_head list;
#ifdef CONFIG_SND_HDA_DSP_LOADER
/* DSP access mutex */
struct mutex dsp_mutex;
#endif
};
void snd_hdac_stream_init(struct hdac_bus *bus, struct hdac_stream *azx_dev,
int idx, int direction, int tag);
struct hdac_stream *snd_hdac_stream_assign(struct hdac_bus *bus,
struct snd_pcm_substream *substream);
void snd_hdac_stream_release(struct hdac_stream *azx_dev);
int snd_hdac_stream_setup(struct hdac_stream *azx_dev);
void snd_hdac_stream_cleanup(struct hdac_stream *azx_dev);
int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev);
int snd_hdac_stream_set_params(struct hdac_stream *azx_dev,
unsigned int format_val);
void snd_hdac_stream_start(struct hdac_stream *azx_dev, bool fresh_start);
void snd_hdac_stream_clear(struct hdac_stream *azx_dev);
void snd_hdac_stream_stop(struct hdac_stream *azx_dev);
void snd_hdac_stream_reset(struct hdac_stream *azx_dev);
void snd_hdac_stream_sync_trigger(struct hdac_stream *azx_dev, bool set,
unsigned int streams, unsigned int reg);
void snd_hdac_stream_sync(struct hdac_stream *azx_dev, bool start,
unsigned int streams);
void snd_hdac_stream_timecounter_init(struct hdac_stream *azx_dev,
unsigned int streams);
/*
* macros for easy use
*/
#define _snd_hdac_stream_write(type, dev, reg, value) \
((dev)->bus->io_ops->reg_write ## type(value, (dev)->sd_addr + (reg)))
#define _snd_hdac_stream_read(type, dev, reg) \
((dev)->bus->io_ops->reg_read ## type((dev)->sd_addr + (reg)))
/* read/write a register, pass without AZX_REG_ prefix */
#define snd_hdac_stream_writel(dev, reg, value) \
_snd_hdac_stream_write(l, dev, AZX_REG_ ## reg, value)
#define snd_hdac_stream_writew(dev, reg, value) \
_snd_hdac_stream_write(w, dev, AZX_REG_ ## reg, value)
#define snd_hdac_stream_writeb(dev, reg, value) \
_snd_hdac_stream_write(b, dev, AZX_REG_ ## reg, value)
#define snd_hdac_stream_readl(dev, reg) \
_snd_hdac_stream_read(l, dev, AZX_REG_ ## reg)
#define snd_hdac_stream_readw(dev, reg) \
_snd_hdac_stream_read(w, dev, AZX_REG_ ## reg)
#define snd_hdac_stream_readb(dev, reg) \
_snd_hdac_stream_read(b, dev, AZX_REG_ ## reg)
/* update a register, pass without AZX_REG_ prefix */
#define snd_hdac_stream_updatel(dev, reg, mask, val) \
snd_hdac_stream_writel(dev, reg, \
(snd_hdac_stream_readl(dev, reg) & \
~(mask)) | (val))
#define snd_hdac_stream_updatew(dev, reg, mask, val) \
snd_hdac_stream_writew(dev, reg, \
(snd_hdac_stream_readw(dev, reg) & \
~(mask)) | (val))
#define snd_hdac_stream_updateb(dev, reg, mask, val) \
snd_hdac_stream_writeb(dev, reg, \
(snd_hdac_stream_readb(dev, reg) & \
~(mask)) | (val))
#ifdef CONFIG_SND_HDA_DSP_LOADER
/* DSP lock helpers */
#define snd_hdac_dsp_lock_init(dev) mutex_init(&(dev)->dsp_mutex)
#define snd_hdac_dsp_lock(dev) mutex_lock(&(dev)->dsp_mutex)
#define snd_hdac_dsp_unlock(dev) mutex_unlock(&(dev)->dsp_mutex)
#define snd_hdac_stream_is_locked(dev) ((dev)->locked)
/* DSP loader helpers */
int snd_hdac_dsp_prepare(struct hdac_stream *azx_dev, unsigned int format,
unsigned int byte_size, struct snd_dma_buffer *bufp);
void snd_hdac_dsp_trigger(struct hdac_stream *azx_dev, bool start);
void snd_hdac_dsp_cleanup(struct hdac_stream *azx_dev,
struct snd_dma_buffer *dmab);
#else /* CONFIG_SND_HDA_DSP_LOADER */
#define snd_hdac_dsp_lock_init(dev) do {} while (0)
#define snd_hdac_dsp_lock(dev) do {} while (0)
#define snd_hdac_dsp_unlock(dev) do {} while (0)
#define snd_hdac_stream_is_locked(dev) 0
static inline int
snd_hdac_dsp_prepare(struct hdac_stream *azx_dev, unsigned int format,
unsigned int byte_size, struct snd_dma_buffer *bufp)
{
return 0;
}
static inline void snd_hdac_dsp_trigger(struct hdac_stream *azx_dev, bool start)
{
}
static inline void snd_hdac_dsp_cleanup(struct hdac_stream *azx_dev,
struct snd_dma_buffer *dmab)
{
}
#endif /* CONFIG_SND_HDA_DSP_LOADER */
/*
* generic array helpers
*/

132
include/sound/hdaudio_ext.h Normal file
View File

@ -0,0 +1,132 @@
#ifndef __SOUND_HDAUDIO_EXT_H
#define __SOUND_HDAUDIO_EXT_H
#include <sound/hdaudio.h>
/**
* hdac_ext_bus: HDAC extended bus for extended HDA caps
*
* @bus: hdac bus
* @num_streams: streams supported
* @ppcap: pp capabilities pointer
* @spbcap: SPIB capabilities pointer
* @mlcap: MultiLink capabilities pointer
* @gtscap: gts capabilities pointer
* @hlink_list: link list of HDA links
*/
struct hdac_ext_bus {
struct hdac_bus bus;
int num_streams;
int idx;
void __iomem *ppcap;
void __iomem *spbcap;
void __iomem *mlcap;
void __iomem *gtscap;
struct list_head hlink_list;
};
int snd_hdac_ext_bus_init(struct hdac_ext_bus *sbus, struct device *dev,
const struct hdac_bus_ops *ops,
const struct hdac_io_ops *io_ops);
void snd_hdac_ext_bus_exit(struct hdac_ext_bus *sbus);
int snd_hdac_ext_bus_device_init(struct hdac_ext_bus *sbus, int addr);
void snd_hdac_ext_bus_device_exit(struct hdac_device *hdev);
#define ebus_to_hbus(ebus) (&(ebus)->bus)
#define hbus_to_ebus(_bus) \
container_of(_bus, struct hdac_ext_bus, bus)
int snd_hdac_ext_bus_parse_capabilities(struct hdac_ext_bus *sbus);
void snd_hdac_ext_bus_ppcap_enable(struct hdac_ext_bus *chip, bool enable);
void snd_hdac_ext_bus_ppcap_int_enable(struct hdac_ext_bus *chip, bool enable);
void snd_hdac_ext_stream_spbcap_enable(struct hdac_ext_bus *chip,
bool enable, int index);
int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_ext_bus *bus);
struct hdac_ext_link *snd_hdac_ext_bus_get_link(struct hdac_ext_bus *bus,
const char *codec_name);
enum hdac_ext_stream_type {
HDAC_EXT_STREAM_TYPE_COUPLED = 0,
HDAC_EXT_STREAM_TYPE_HOST,
HDAC_EXT_STREAM_TYPE_LINK
};
/**
* hdac_ext_stream: HDAC extended stream for extended HDA caps
*
* @hstream: hdac_stream
* @pphc_addr: processing pipe host stream pointer
* @pplc_addr: processing pipe link stream pointer
* @decoupled: stream host and link is decoupled
* @link_locked: link is locked
* @link_prepared: link is prepared
* link_substream: link substream
*/
struct hdac_ext_stream {
struct hdac_stream hstream;
void __iomem *pphc_addr;
void __iomem *pplc_addr;
bool decoupled:1;
bool link_locked:1;
bool link_prepared;
struct snd_pcm_substream *link_substream;
};
#define hdac_stream(s) (&(s)->hstream)
#define stream_to_hdac_ext_stream(s) \
container_of(s, struct hdac_ext_stream, hstream)
void snd_hdac_ext_stream_init(struct hdac_ext_bus *bus,
struct hdac_ext_stream *stream, int idx,
int direction, int tag);
int snd_hdac_ext_stream_init_all(struct hdac_ext_bus *ebus, int start_idx,
int num_stream, int dir);
void snd_hdac_stream_free_all(struct hdac_ext_bus *ebus);
void snd_hdac_link_free_all(struct hdac_ext_bus *ebus);
struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_ext_bus *bus,
struct snd_pcm_substream *substream,
int type);
void snd_hdac_ext_stream_release(struct hdac_ext_stream *azx_dev, int type);
void snd_hdac_ext_stream_decouple(struct hdac_ext_bus *bus,
struct hdac_ext_stream *azx_dev, bool decouple);
void snd_hdac_ext_stop_streams(struct hdac_ext_bus *sbus);
void snd_hdac_ext_link_stream_start(struct hdac_ext_stream *hstream);
void snd_hdac_ext_link_stream_clear(struct hdac_ext_stream *hstream);
void snd_hdac_ext_link_stream_reset(struct hdac_ext_stream *hstream);
int snd_hdac_ext_link_stream_setup(struct hdac_ext_stream *stream, int fmt);
struct hdac_ext_link {
struct hdac_bus *bus;
int index;
void __iomem *ml_addr; /* link output stream reg pointer */
u32 lcaps; /* link capablities */
u16 lsdiid; /* link sdi identifier */
struct list_head list;
};
int snd_hdac_ext_bus_link_power_up(struct hdac_ext_link *link);
int snd_hdac_ext_bus_link_power_down(struct hdac_ext_link *link);
void snd_hdac_ext_link_set_stream_id(struct hdac_ext_link *link,
int stream);
void snd_hdac_ext_link_clear_stream_id(struct hdac_ext_link *link,
int stream);
/* update register macro */
#define snd_hdac_updatel(addr, reg, mask, val) \
writel(((readl(addr + reg) & ~(mask)) | (val)), \
addr + reg)
#define snd_hdac_updatew(addr, reg, mask, val) \
writew(((readw(addr + reg) & ~(mask)) | (val)), \
addr + reg)
#endif /* __SOUND_HDAUDIO_EXT_H */

View File

@ -23,6 +23,8 @@
*/
#include <linux/poll.h>
#include <linux/seq_file.h>
#include <sound/core.h>
/* buffer for information */
struct snd_info_buffer {
@ -90,16 +92,14 @@ struct snd_info_entry {
struct list_head list;
};
#if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS)
#if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_SND_PROC_FS)
int snd_info_minor_register(void);
int snd_info_minor_unregister(void);
#else
#define snd_info_minor_register() /* NOP */
#define snd_info_minor_unregister() /* NOP */
#define snd_info_minor_register() 0
#endif
#ifdef CONFIG_PROC_FS
#ifdef CONFIG_SND_PROC_FS
extern struct snd_info_entry *snd_seq_root;
#ifdef CONFIG_SND_OSSEMUL
@ -110,8 +110,18 @@ void snd_card_info_read_oss(struct snd_info_buffer *buffer);
static inline void snd_card_info_read_oss(struct snd_info_buffer *buffer) {}
#endif
__printf(2, 3)
int snd_iprintf(struct snd_info_buffer *buffer, const char *fmt, ...);
/**
* snd_iprintf - printf on the procfs buffer
* @buf: the procfs buffer
* @fmt: the printf format
*
* Outputs the string on the procfs buffer just like printf().
*
* Return: zero for success, or a negative error code.
*/
#define snd_iprintf(buf, fmt, args...) \
seq_printf((struct seq_file *)(buf)->buffer, fmt, ##args)
int snd_info_init(void);
int snd_info_done(void);
@ -135,8 +145,12 @@ void snd_info_card_id_change(struct snd_card *card);
int snd_info_register(struct snd_info_entry *entry);
/* for card drivers */
int snd_card_proc_new(struct snd_card *card, const char *name,
struct snd_info_entry **entryp);
static inline int snd_card_proc_new(struct snd_card *card, const char *name,
struct snd_info_entry **entryp)
{
*entryp = snd_info_create_card_entry(card, name, card->proc_root);
return *entryp ? 0 : -ENOMEM;
}
static inline void snd_info_set_text_ops(struct snd_info_entry *entry,
void *private_data,
@ -175,7 +189,6 @@ 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)),
void *private_data,
void (*read)(struct snd_info_entry *, struct snd_info_buffer *)) {}
static inline int snd_info_check_reserved_words(const char *str) { return 1; }
#endif
@ -184,7 +197,7 @@ static inline int snd_info_check_reserved_words(const char *str) { return 1; }
* OSS info part
*/
#if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS)
#if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_SND_PROC_FS)
#define SNDRV_OSS_INFO_DEV_AUDIO 0
#define SNDRV_OSS_INFO_DEV_SYNTH 1
@ -197,6 +210,6 @@ static inline int snd_info_check_reserved_words(const char *str) { return 1; }
int snd_oss_info_register(int dev, int num, char *string);
#define snd_oss_info_unregister(dev, num) snd_oss_info_register(dev, num, NULL)
#endif /* CONFIG_SND_OSSEMUL && CONFIG_PROC_FS */
#endif /* CONFIG_SND_OSSEMUL && CONFIG_SND_PROC_FS */
#endif /* __SOUND_INFO_H */

View File

@ -73,6 +73,8 @@ enum snd_jack_types {
struct snd_jack {
struct input_dev *input_dev;
struct list_head kctl_list;
struct snd_card *card;
int registered;
int type;
const char *id;
@ -85,7 +87,8 @@ struct snd_jack {
#ifdef CONFIG_SND_JACK
int snd_jack_new(struct snd_card *card, const char *id, int type,
struct snd_jack **jack);
struct snd_jack **jack, bool initial_kctl, bool phantom_jack);
int snd_jack_add_new_kctl(struct snd_jack *jack, const char * name, int mask);
void snd_jack_set_parent(struct snd_jack *jack, struct device *parent);
int snd_jack_set_key(struct snd_jack *jack, enum snd_jack_types type,
int keytype);
@ -93,9 +96,13 @@ int snd_jack_set_key(struct snd_jack *jack, enum snd_jack_types type,
void snd_jack_report(struct snd_jack *jack, int status);
#else
static inline int snd_jack_new(struct snd_card *card, const char *id, int type,
struct snd_jack **jack)
struct snd_jack **jack, bool initial_kctl, bool phantom_jack)
{
return 0;
}
static inline int snd_jack_add_new_kctl(struct snd_jack *jack, const char * name, int mask)
{
return 0;
}

View File

@ -224,9 +224,10 @@ typedef int (*snd_pcm_hw_rule_func_t)(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule {
unsigned int cond;
snd_pcm_hw_rule_func_t func;
int var;
int deps[4];
snd_pcm_hw_rule_func_t func;
void *private;
};
@ -273,8 +274,8 @@ struct snd_pcm_hw_constraint_ratdens {
};
struct snd_pcm_hw_constraint_list {
unsigned int count;
const unsigned int *list;
unsigned int count;
unsigned int mask;
};

View File

@ -0,0 +1,6 @@
#ifndef __SOUND_PCM_DRM_ELD_H
#define __SOUND_PCM_DRM_ELD_H
int snd_pcm_hw_constraint_eld(struct snd_pcm_runtime *runtime, void *eld);
#endif

View File

@ -0,0 +1,9 @@
#ifndef __SOUND_PCM_IEC958_H
#define __SOUND_PCM_IEC958_H
#include <linux/types.h>
int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
size_t len);
#endif

View File

@ -15,17 +15,11 @@ struct rt5645_platform_data {
/* IN2 can optionally be differential */
bool in2_diff;
bool dmic_en;
unsigned int dmic1_data_pin;
/* 0 = IN2N; 1 = GPIO5; 2 = GPIO11 */
unsigned int dmic2_data_pin;
/* 0 = IN2P; 1 = GPIO6; 2 = GPIO10; 3 = GPIO12 */
unsigned int hp_det_gpio;
bool gpio_hp_det_active_high;
/* true if codec's jd function is used */
bool en_jd_func;
unsigned int jd_mode;
};

View File

@ -15,6 +15,8 @@
#include <linux/types.h>
#include <sound/control.h>
#include <sound/soc-topology.h>
#include <sound/asoc.h>
struct device;
@ -107,6 +109,10 @@ struct device;
{ .id = snd_soc_dapm_mux, .name = wname, \
SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
.kcontrol_news = wcontrols, .num_kcontrols = 1}
#define SND_SOC_DAPM_DEMUX(wname, wreg, wshift, winvert, wcontrols) \
{ .id = snd_soc_dapm_demux, .name = wname, \
SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
.kcontrol_news = wcontrols, .num_kcontrols = 1}
/* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */
#define SOC_PGA_ARRAY(wname, wreg, wshift, winvert,\
@ -444,11 +450,15 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm(
struct snd_kcontrol *kcontrol);
int snd_soc_dapm_force_bias_level(struct snd_soc_dapm_context *dapm,
enum snd_soc_bias_level level);
/* dapm widget types */
enum snd_soc_dapm_type {
snd_soc_dapm_input = 0, /* input pin */
snd_soc_dapm_output, /* output pin */
snd_soc_dapm_mux, /* selects 1 analog signal from many inputs */
snd_soc_dapm_demux, /* connects the input to one of multiple outputs */
snd_soc_dapm_mixer, /* mixes several analog signals together */
snd_soc_dapm_mixer_named_ctl, /* mixer with named controls */
snd_soc_dapm_pga, /* programmable gain/attenuation (volume) */
@ -563,6 +573,7 @@ struct snd_soc_dapm_widget {
int num_kcontrols;
const struct snd_kcontrol_new *kcontrol_news;
struct snd_kcontrol **kcontrols;
struct snd_soc_dobj dobj;
/* widget input and outputs */
struct list_head sources;
@ -585,6 +596,10 @@ struct snd_soc_dapm_update {
int val;
};
struct snd_soc_dapm_wcache {
struct snd_soc_dapm_widget *widget;
};
/* DAPM context */
struct snd_soc_dapm_context {
enum snd_soc_bias_level bias_level;
@ -606,6 +621,9 @@ struct snd_soc_dapm_context {
int (*set_bias_level)(struct snd_soc_dapm_context *dapm,
enum snd_soc_bias_level level);
struct snd_soc_dapm_wcache path_sink_cache;
struct snd_soc_dapm_wcache path_source_cache;
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs_dapm;
#endif
@ -623,4 +641,35 @@ struct snd_soc_dapm_stats {
int neighbour_checks;
};
/**
* snd_soc_dapm_init_bias_level() - Initialize DAPM bias level
* @dapm: The DAPM context to initialize
* @level: The DAPM level to initialize to
*
* This function only sets the driver internal state of the DAPM level and will
* not modify the state of the device. Hence it should not be used during normal
* operation, but only to synchronize the internal state to the device state.
* E.g. during driver probe to set the DAPM level to the one corresponding with
* the power-on reset state of the device.
*
* To change the DAPM state of the device use snd_soc_dapm_set_bias_level().
*/
static inline void snd_soc_dapm_init_bias_level(
struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level)
{
dapm->bias_level = level;
}
/**
* snd_soc_dapm_get_bias_level() - Get current DAPM bias level
* @dapm: The context for which to get the bias level
*
* Returns: The current bias level of the passed DAPM context.
*/
static inline enum snd_soc_bias_level snd_soc_dapm_get_bias_level(
struct snd_soc_dapm_context *dapm)
{
return dapm->bias_level;
}
#endif

View File

@ -0,0 +1,168 @@
/*
* linux/sound/soc-topology.h -- ALSA SoC Firmware Controls and DAPM
*
* Copyright (C) 2012 Texas Instruments Inc.
* Copyright (C) 2015 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Simple file API to load FW that includes mixers, coefficients, DAPM graphs,
* algorithms, equalisers, DAIs, widgets, FE caps, BE caps, codec link caps etc.
*/
#ifndef __LINUX_SND_SOC_TPLG_H
#define __LINUX_SND_SOC_TPLG_H
#include <sound/asoc.h>
#include <linux/list.h>
struct firmware;
struct snd_kcontrol;
struct snd_soc_tplg_pcm_be;
struct snd_ctl_elem_value;
struct snd_ctl_elem_info;
struct snd_soc_dapm_widget;
struct snd_soc_component;
struct snd_soc_tplg_pcm_fe;
struct snd_soc_dapm_context;
struct snd_soc_card;
/* object scan be loaded and unloaded in groups with identfying indexes */
#define SND_SOC_TPLG_INDEX_ALL 0 /* ID that matches all FW objects */
/* dynamic object type */
enum snd_soc_dobj_type {
SND_SOC_DOBJ_NONE = 0, /* object is not dynamic */
SND_SOC_DOBJ_MIXER,
SND_SOC_DOBJ_ENUM,
SND_SOC_DOBJ_BYTES,
SND_SOC_DOBJ_PCM,
SND_SOC_DOBJ_DAI_LINK,
SND_SOC_DOBJ_CODEC_LINK,
SND_SOC_DOBJ_WIDGET,
};
/* dynamic control object */
struct snd_soc_dobj_control {
struct snd_kcontrol *kcontrol;
char **dtexts;
unsigned long *dvalues;
};
/* dynamic widget object */
struct snd_soc_dobj_widget {
unsigned int kcontrol_enum:1; /* this widget is an enum kcontrol */
};
/* dynamic PCM DAI object */
struct snd_soc_dobj_pcm_dai {
struct snd_soc_tplg_pcm_dai *pd;
unsigned int count;
};
/* generic dynamic object - all dynamic objects belong to this struct */
struct snd_soc_dobj {
enum snd_soc_dobj_type type;
unsigned int index; /* objects can belong in different groups */
struct list_head list;
struct snd_soc_tplg_ops *ops;
union {
struct snd_soc_dobj_control control;
struct snd_soc_dobj_widget widget;
struct snd_soc_dobj_pcm_dai pcm_dai;
};
void *private; /* core does not touch this */
};
/*
* Kcontrol operations - used to map handlers onto firmware based controls.
*/
struct snd_soc_tplg_kcontrol_ops {
u32 id;
int (*get)(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int (*put)(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int (*info)(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo);
};
/*
* DAPM widget event handlers - used to map handlers onto widgets.
*/
struct snd_soc_tplg_widget_events {
u16 type;
int (*event_handler)(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event);
};
/*
* Public API - Used by component drivers to load and unload dynamic objects
* and their resources.
*/
struct snd_soc_tplg_ops {
/* external kcontrol init - used for any driver specific init */
int (*control_load)(struct snd_soc_component *,
struct snd_kcontrol_new *, struct snd_soc_tplg_ctl_hdr *);
int (*control_unload)(struct snd_soc_component *,
struct snd_soc_dobj *);
/* external widget init - used for any driver specific init */
int (*widget_load)(struct snd_soc_component *,
struct snd_soc_dapm_widget *,
struct snd_soc_tplg_dapm_widget *);
int (*widget_unload)(struct snd_soc_component *,
struct snd_soc_dobj *);
/* FE - used for any driver specific init */
int (*pcm_dai_load)(struct snd_soc_component *,
struct snd_soc_tplg_pcm_dai *pcm_dai, int num_fe);
int (*pcm_dai_unload)(struct snd_soc_component *,
struct snd_soc_dobj *);
/* callback to handle vendor bespoke data */
int (*vendor_load)(struct snd_soc_component *,
struct snd_soc_tplg_hdr *);
int (*vendor_unload)(struct snd_soc_component *,
struct snd_soc_tplg_hdr *);
/* completion - called at completion of firmware loading */
void (*complete)(struct snd_soc_component *);
/* manifest - optional to inform component of manifest */
int (*manifest)(struct snd_soc_component *,
struct snd_soc_tplg_manifest *);
/* bespoke kcontrol handlers available for binding */
const struct snd_soc_tplg_kcontrol_ops *io_ops;
int io_ops_count;
};
/* gets a pointer to data from the firmware block header */
static inline const void *snd_soc_tplg_get_data(struct snd_soc_tplg_hdr *hdr)
{
const void *ptr = hdr;
return ptr + sizeof(*hdr);
}
/* Dynamic Object loading and removal for component drivers */
int snd_soc_tplg_component_load(struct snd_soc_component *comp,
struct snd_soc_tplg_ops *ops, const struct firmware *fw,
u32 index);
int snd_soc_tplg_component_remove(struct snd_soc_component *comp, u32 index);
/* Widget removal - widgets also removed wth component API */
void snd_soc_tplg_widget_remove(struct snd_soc_dapm_widget *w);
void snd_soc_tplg_widget_remove_all(struct snd_soc_dapm_context *dapm,
u32 index);
/* Binds event handlers to dynamic widgets */
int snd_soc_tplg_widget_bind_event(struct snd_soc_dapm_widget *w,
const struct snd_soc_tplg_widget_events *events, int num_events,
u16 event_type);
#endif

View File

@ -27,6 +27,7 @@
#include <sound/compress_driver.h>
#include <sound/control.h>
#include <sound/ac97_codec.h>
#include <sound/soc-topology.h>
/*
* Convenience kcontrol builders
@ -190,8 +191,12 @@
#define SOC_VALUE_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xitems, xtexts, xvalues) \
{ .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \
.mask = xmask, .items = xitems, .texts = xtexts, .values = xvalues}
#define SOC_VALUE_ENUM_SINGLE(xreg, xshift, xmask, xnitmes, xtexts, xvalues) \
SOC_VALUE_ENUM_DOUBLE(xreg, xshift, xshift, xmask, xnitmes, xtexts, xvalues)
#define SOC_VALUE_ENUM_SINGLE(xreg, xshift, xmask, xitems, xtexts, xvalues) \
SOC_VALUE_ENUM_DOUBLE(xreg, xshift, xshift, xmask, xitems, xtexts, xvalues)
#define SOC_VALUE_ENUM_SINGLE_AUTODISABLE(xreg, xshift, xmask, xitems, xtexts, xvalues) \
{ .reg = xreg, .shift_l = xshift, .shift_r = xshift, \
.mask = xmask, .items = xitems, .texts = xtexts, \
.values = xvalues, .autodisable = 1}
#define SOC_ENUM_SINGLE_VIRT(xitems, xtexts) \
SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, xitems, xtexts)
#define SOC_ENUM(xname, xenum) \
@ -312,6 +317,11 @@
ARRAY_SIZE(xtexts), xtexts, xvalues)
#define SOC_VALUE_ENUM_SINGLE_DECL(name, xreg, xshift, xmask, xtexts, xvalues) \
SOC_VALUE_ENUM_DOUBLE_DECL(name, xreg, xshift, xshift, xmask, xtexts, xvalues)
#define SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(name, xreg, xshift, xmask, xtexts, xvalues) \
const struct soc_enum name = SOC_VALUE_ENUM_SINGLE_AUTODISABLE(xreg, \
xshift, xmask, ARRAY_SIZE(xtexts), xtexts, xvalues)
#define SOC_ENUM_SINGLE_VIRT_DECL(name, xtexts) \
const struct soc_enum name = SOC_ENUM_SINGLE_VIRT(ARRAY_SIZE(xtexts), xtexts)
@ -767,6 +777,9 @@ struct snd_soc_component {
struct mutex io_mutex;
/* attached dynamic objects */
struct list_head dobj_list;
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs_root;
#endif
@ -819,7 +832,7 @@ struct snd_soc_codec {
/* component */
struct snd_soc_component component;
/* dapm */
/* Don't access this directly, use snd_soc_codec_get_dapm() */
struct snd_soc_dapm_context dapm;
#ifdef CONFIG_DEBUG_FS
@ -961,30 +974,6 @@ struct snd_soc_dai_link {
enum snd_soc_dpcm_trigger trigger[2]; /* trigger type for DPCM */
/* Keep DAI active over suspend */
unsigned int ignore_suspend:1;
/* Symmetry requirements */
unsigned int symmetric_rates:1;
unsigned int symmetric_channels:1;
unsigned int symmetric_samplebits:1;
/* Mark this pcm with non atomic ops */
bool nonatomic;
/* Do not create a PCM for this DAI link (Backend link) */
unsigned int no_pcm:1;
/* This DAI link can route to other DAI links at runtime (Frontend)*/
unsigned int dynamic:1;
/* DPCM capture and Playback support */
unsigned int dpcm_capture:1;
unsigned int dpcm_playback:1;
/* pmdown_time is ignored at stop */
unsigned int ignore_pmdown_time:1;
/* codec/machine specific init - e.g. add machine controls */
int (*init)(struct snd_soc_pcm_runtime *rtd);
@ -999,6 +988,33 @@ struct snd_soc_dai_link {
/* For unidirectional dai links */
bool playback_only;
bool capture_only;
/* Mark this pcm with non atomic ops */
bool nonatomic;
/* Keep DAI active over suspend */
unsigned int ignore_suspend:1;
/* Symmetry requirements */
unsigned int symmetric_rates:1;
unsigned int symmetric_channels:1;
unsigned int symmetric_samplebits:1;
/* Do not create a PCM for this DAI link (Backend link) */
unsigned int no_pcm:1;
/* This DAI link can route to other DAI links at runtime (Frontend)*/
unsigned int dynamic:1;
/* DPCM capture and Playback support */
unsigned int dpcm_capture:1;
unsigned int dpcm_playback:1;
/* DPCM used FE & BE merged format */
unsigned int dpcm_merged_format:1;
/* pmdown_time is ignored at stop */
unsigned int ignore_pmdown_time:1;
};
struct snd_soc_codec_conf {
@ -1111,6 +1127,9 @@ struct snd_soc_card {
struct list_head dapm_list;
struct list_head dapm_dirty;
/* attached dynamic objects */
struct list_head dobj_list;
/* Generic DAPM context for the card */
struct snd_soc_dapm_context dapm;
struct snd_soc_dapm_stats dapm_stats;
@ -1170,6 +1189,7 @@ struct soc_mixer_control {
unsigned int sign_bit;
unsigned int invert:1;
unsigned int autodisable:1;
struct snd_soc_dobj dobj;
};
struct soc_bytes {
@ -1180,6 +1200,8 @@ struct soc_bytes {
struct soc_bytes_ext {
int max;
struct snd_soc_dobj dobj;
/* used for TLV byte control */
int (*get)(unsigned int __user *bytes, unsigned int size);
int (*put)(const unsigned int __user *bytes, unsigned int size);
@ -1200,6 +1222,8 @@ struct soc_enum {
unsigned int mask;
const char * const *texts;
const unsigned int *values;
unsigned int autodisable:1;
struct snd_soc_dobj dobj;
};
/**
@ -1281,6 +1305,58 @@ static inline struct snd_soc_dapm_context *snd_soc_component_get_dapm(
return component->dapm_ptr;
}
/**
* snd_soc_codec_get_dapm() - Returns the DAPM context for the CODEC
* @codec: The CODEC for which to get the DAPM context
*
* Note: Use this function instead of directly accessing the CODEC's dapm field
*/
static inline struct snd_soc_dapm_context *snd_soc_codec_get_dapm(
struct snd_soc_codec *codec)
{
return &codec->dapm;
}
/**
* snd_soc_dapm_init_bias_level() - Initialize CODEC DAPM bias level
* @dapm: The CODEC for which to initialize the DAPM bias level
* @level: The DAPM level to initialize to
*
* Initializes the CODEC DAPM bias level. See snd_soc_dapm_init_bias_level().
*/
static inline void snd_soc_codec_init_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
snd_soc_dapm_init_bias_level(snd_soc_codec_get_dapm(codec), level);
}
/**
* snd_soc_dapm_get_bias_level() - Get current CODEC DAPM bias level
* @codec: The CODEC for which to get the DAPM bias level
*
* Returns: The current DAPM bias level of the CODEC.
*/
static inline enum snd_soc_bias_level snd_soc_codec_get_bias_level(
struct snd_soc_codec *codec)
{
return snd_soc_dapm_get_bias_level(snd_soc_codec_get_dapm(codec));
}
/**
* snd_soc_codec_force_bias_level() - Set the CODEC DAPM bias level
* @codec: The CODEC for which to set the level
* @level: The level to set to
*
* Forces the CODEC bias level to a specific state. See
* snd_soc_dapm_force_bias_level().
*/
static inline int snd_soc_codec_force_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
return snd_soc_dapm_force_bias_level(snd_soc_codec_get_dapm(codec),
level);
}
/**
* snd_soc_dapm_kcontrol_codec() - Returns the codec associated to a kcontrol
* @kcontrol: The kcontrol

View File

@ -31,12 +31,7 @@
* ~(sizeof(unsigned int) - 1)) ....
*/
#define SNDRV_CTL_TLVT_CONTAINER 0 /* one level down - group of TLVs */
#define SNDRV_CTL_TLVT_DB_SCALE 1 /* dB scale */
#define SNDRV_CTL_TLVT_DB_LINEAR 2 /* linear volume */
#define SNDRV_CTL_TLVT_DB_RANGE 3 /* dB range container */
#define SNDRV_CTL_TLVT_DB_MINMAX 4 /* dB scale with min/max */
#define SNDRV_CTL_TLVT_DB_MINMAX_MUTE 5 /* dB scale with min/max with mute */
#include <uapi/sound/tlv.h>
#define TLV_ITEM(type, ...) \
(type), TLV_LENGTH(__VA_ARGS__), __VA_ARGS__
@ -90,12 +85,4 @@
#define TLV_DB_GAIN_MUTE -9999999
/*
* channel-mapping TLV items
* TLV length must match with num_channels
*/
#define SNDRV_CTL_TLVT_CHMAP_FIXED 0x101 /* fixed channel position */
#define SNDRV_CTL_TLVT_CHMAP_VAR 0x102 /* channels freely swappable */
#define SNDRV_CTL_TLVT_CHMAP_PAIRED 0x103 /* pair-wise swappable */
#endif /* __SOUND_TLV_H */

388
include/uapi/sound/asoc.h Normal file
View File

@ -0,0 +1,388 @@
/*
* uapi/sound/asoc.h -- ALSA SoC Firmware Controls and DAPM
*
* Copyright (C) 2012 Texas Instruments Inc.
* Copyright (C) 2015 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Simple file API to load FW that includes mixers, coefficients, DAPM graphs,
* algorithms, equalisers, DAIs, widgets etc.
*/
#ifndef __LINUX_UAPI_SND_ASOC_H
#define __LINUX_UAPI_SND_ASOC_H
#include <linux/types.h>
#include <sound/asound.h>
/*
* Maximum number of channels topology kcontrol can represent.
*/
#define SND_SOC_TPLG_MAX_CHAN 8
/*
* Maximum number of PCM formats capability
*/
#define SND_SOC_TPLG_MAX_FORMATS 16
/*
* Maximum number of PCM stream configs
*/
#define SND_SOC_TPLG_STREAM_CONFIG_MAX 8
/* individual kcontrol info types - can be mixed with other types */
#define SND_SOC_TPLG_CTL_VOLSW 1
#define SND_SOC_TPLG_CTL_VOLSW_SX 2
#define SND_SOC_TPLG_CTL_VOLSW_XR_SX 3
#define SND_SOC_TPLG_CTL_ENUM 4
#define SND_SOC_TPLG_CTL_BYTES 5
#define SND_SOC_TPLG_CTL_ENUM_VALUE 6
#define SND_SOC_TPLG_CTL_RANGE 7
#define SND_SOC_TPLG_CTL_STROBE 8
/* individual widget kcontrol info types - can be mixed with other types */
#define SND_SOC_TPLG_DAPM_CTL_VOLSW 64
#define SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE 65
#define SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT 66
#define SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE 67
#define SND_SOC_TPLG_DAPM_CTL_PIN 68
/* DAPM widget types - add new items to the end */
#define SND_SOC_TPLG_DAPM_INPUT 0
#define SND_SOC_TPLG_DAPM_OUTPUT 1
#define SND_SOC_TPLG_DAPM_MUX 2
#define SND_SOC_TPLG_DAPM_MIXER 3
#define SND_SOC_TPLG_DAPM_PGA 4
#define SND_SOC_TPLG_DAPM_OUT_DRV 5
#define SND_SOC_TPLG_DAPM_ADC 6
#define SND_SOC_TPLG_DAPM_DAC 7
#define SND_SOC_TPLG_DAPM_SWITCH 8
#define SND_SOC_TPLG_DAPM_PRE 9
#define SND_SOC_TPLG_DAPM_POST 10
#define SND_SOC_TPLG_DAPM_AIF_IN 11
#define SND_SOC_TPLG_DAPM_AIF_OUT 12
#define SND_SOC_TPLG_DAPM_DAI_IN 13
#define SND_SOC_TPLG_DAPM_DAI_OUT 14
#define SND_SOC_TPLG_DAPM_DAI_LINK 15
#define SND_SOC_TPLG_DAPM_LAST SND_SOC_TPLG_DAPM_DAI_LINK
/* Header magic number and string sizes */
#define SND_SOC_TPLG_MAGIC 0x41536F43 /* ASoC */
/* string sizes */
#define SND_SOC_TPLG_NUM_TEXTS 16
/* ABI version */
#define SND_SOC_TPLG_ABI_VERSION 0x2
/* Max size of TLV data */
#define SND_SOC_TPLG_TLV_SIZE 32
/*
* File and Block header data types.
* Add new generic and vendor types to end of list.
* Generic types are handled by the core whilst vendors types are passed
* to the component drivers for handling.
*/
#define SND_SOC_TPLG_TYPE_MIXER 1
#define SND_SOC_TPLG_TYPE_BYTES 2
#define SND_SOC_TPLG_TYPE_ENUM 3
#define SND_SOC_TPLG_TYPE_DAPM_GRAPH 4
#define SND_SOC_TPLG_TYPE_DAPM_WIDGET 5
#define SND_SOC_TPLG_TYPE_DAI_LINK 6
#define SND_SOC_TPLG_TYPE_PCM 7
#define SND_SOC_TPLG_TYPE_MANIFEST 8
#define SND_SOC_TPLG_TYPE_CODEC_LINK 9
#define SND_SOC_TPLG_TYPE_MAX SND_SOC_TPLG_TYPE_CODEC_LINK
/* vendor block IDs - please add new vendor types to end */
#define SND_SOC_TPLG_TYPE_VENDOR_FW 1000
#define SND_SOC_TPLG_TYPE_VENDOR_CONFIG 1001
#define SND_SOC_TPLG_TYPE_VENDOR_COEFF 1002
#define SND_SOC_TPLG_TYPEVENDOR_CODEC 1003
#define SND_SOC_TPLG_STREAM_PLAYBACK 0
#define SND_SOC_TPLG_STREAM_CAPTURE 1
/*
* Block Header.
* This header preceeds all object and object arrays below.
*/
struct snd_soc_tplg_hdr {
__le32 magic; /* magic number */
__le32 abi; /* ABI version */
__le32 version; /* optional vendor specific version details */
__le32 type; /* SND_SOC_TPLG_TYPE_ */
__le32 size; /* size of this structure */
__le32 vendor_type; /* optional vendor specific type info */
__le32 payload_size; /* data bytes, excluding this header */
__le32 index; /* identifier for block */
__le32 count; /* number of elements in block */
} __attribute__((packed));
/*
* Private data.
* All topology objects may have private data that can be used by the driver or
* firmware. Core will ignore this data.
*/
struct snd_soc_tplg_private {
__le32 size; /* in bytes of private data */
char data[0];
} __attribute__((packed));
/*
* Kcontrol TLV data.
*/
struct snd_soc_tplg_ctl_tlv {
__le32 size; /* in bytes aligned to 4 */
__le32 numid; /* control element numeric identification */
__le32 count; /* number of elem in data array */
__le32 data[SND_SOC_TPLG_TLV_SIZE];
} __attribute__((packed));
/*
* Kcontrol channel data
*/
struct snd_soc_tplg_channel {
__le32 size; /* in bytes of this structure */
__le32 reg;
__le32 shift;
__le32 id; /* ID maps to Left, Right, LFE etc */
} __attribute__((packed));
/*
* Kcontrol Operations IDs
*/
struct snd_soc_tplg_kcontrol_ops_id {
__le32 get;
__le32 put;
__le32 info;
} __attribute__((packed));
/*
* kcontrol header
*/
struct snd_soc_tplg_ctl_hdr {
__le32 size; /* in bytes of this structure */
__le32 type;
char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
__le32 access;
struct snd_soc_tplg_kcontrol_ops_id ops;
__le32 tlv_size; /* non zero means control has TLV data */
} __attribute__((packed));
/*
* Stream Capabilities
*/
struct snd_soc_tplg_stream_caps {
__le32 size; /* in bytes of this structure */
char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
__le64 formats[SND_SOC_TPLG_MAX_FORMATS]; /* supported formats SNDRV_PCM_FMTBIT_* */
__le32 rates; /* supported rates SNDRV_PCM_RATE_* */
__le32 rate_min; /* min rate */
__le32 rate_max; /* max rate */
__le32 channels_min; /* min channels */
__le32 channels_max; /* max channels */
__le32 periods_min; /* min number of periods */
__le32 periods_max; /* max number of periods */
__le32 period_size_min; /* min period size bytes */
__le32 period_size_max; /* max period size bytes */
__le32 buffer_size_min; /* min buffer size bytes */
__le32 buffer_size_max; /* max buffer size bytes */
} __attribute__((packed));
/*
* FE or BE Stream configuration supported by SW/FW
*/
struct snd_soc_tplg_stream {
__le32 size; /* in bytes of this structure */
__le64 format; /* SNDRV_PCM_FMTBIT_* */
__le32 rate; /* SNDRV_PCM_RATE_* */
__le32 period_bytes; /* size of period in bytes */
__le32 buffer_bytes; /* size of buffer in bytes */
__le32 channels; /* channels */
__le32 tdm_slot; /* optional BE bitmask of supported TDM slots */
__le32 dai_fmt; /* SND_SOC_DAIFMT_ */
} __attribute__((packed));
/*
* Duplex stream configuration supported by SW/FW.
*/
struct snd_soc_tplg_stream_config {
__le32 size; /* in bytes of this structure */
char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
struct snd_soc_tplg_stream playback;
struct snd_soc_tplg_stream capture;
} __attribute__((packed));
/*
* Manifest. List totals for each payload type. Not used in parsing, but will
* be passed to the component driver before any other objects in order for any
* global componnent resource allocations.
*
* File block representation for manifest :-
* +-----------------------------------+----+
* | struct snd_soc_tplg_hdr | 1 |
* +-----------------------------------+----+
* | struct snd_soc_tplg_manifest | 1 |
* +-----------------------------------+----+
*/
struct snd_soc_tplg_manifest {
__le32 size; /* in bytes of this structure */
__le32 control_elems; /* number of control elements */
__le32 widget_elems; /* number of widget elements */
__le32 graph_elems; /* number of graph elements */
__le32 dai_elems; /* number of DAI elements */
__le32 dai_link_elems; /* number of DAI link elements */
} __attribute__((packed));
/*
* Mixer kcontrol.
*
* File block representation for mixer kcontrol :-
* +-----------------------------------+----+
* | struct snd_soc_tplg_hdr | 1 |
* +-----------------------------------+----+
* | struct snd_soc_tplg_mixer_control | N |
* +-----------------------------------+----+
*/
struct snd_soc_tplg_mixer_control {
struct snd_soc_tplg_ctl_hdr hdr;
__le32 size; /* in bytes of this structure */
__le32 min;
__le32 max;
__le32 platform_max;
__le32 invert;
__le32 num_channels;
struct snd_soc_tplg_channel channel[SND_SOC_TPLG_MAX_CHAN];
struct snd_soc_tplg_ctl_tlv tlv;
struct snd_soc_tplg_private priv;
} __attribute__((packed));
/*
* Enumerated kcontrol
*
* File block representation for enum kcontrol :-
* +-----------------------------------+----+
* | struct snd_soc_tplg_hdr | 1 |
* +-----------------------------------+----+
* | struct snd_soc_tplg_enum_control | N |
* +-----------------------------------+----+
*/
struct snd_soc_tplg_enum_control {
struct snd_soc_tplg_ctl_hdr hdr;
__le32 size; /* in bytes of this structure */
__le32 num_channels;
struct snd_soc_tplg_channel channel[SND_SOC_TPLG_MAX_CHAN];
__le32 items;
__le32 mask;
__le32 count;
char texts[SND_SOC_TPLG_NUM_TEXTS][SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
__le32 values[SND_SOC_TPLG_NUM_TEXTS * SNDRV_CTL_ELEM_ID_NAME_MAXLEN / 4];
struct snd_soc_tplg_private priv;
} __attribute__((packed));
/*
* Bytes kcontrol
*
* File block representation for bytes kcontrol :-
* +-----------------------------------+----+
* | struct snd_soc_tplg_hdr | 1 |
* +-----------------------------------+----+
* | struct snd_soc_tplg_bytes_control | N |
* +-----------------------------------+----+
*/
struct snd_soc_tplg_bytes_control {
struct snd_soc_tplg_ctl_hdr hdr;
__le32 size; /* in bytes of this structure */
__le32 max;
__le32 mask;
__le32 base;
__le32 num_regs;
struct snd_soc_tplg_private priv;
} __attribute__((packed));
/*
* DAPM Graph Element
*
* File block representation for DAPM graph elements :-
* +-------------------------------------+----+
* | struct snd_soc_tplg_hdr | 1 |
* +-------------------------------------+----+
* | struct snd_soc_tplg_dapm_graph_elem | N |
* +-------------------------------------+----+
*/
struct snd_soc_tplg_dapm_graph_elem {
char sink[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
char control[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
char source[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
} __attribute__((packed));
/*
* DAPM Widget.
*
* File block representation for DAPM widget :-
* +-------------------------------------+-----+
* | struct snd_soc_tplg_hdr | 1 |
* +-------------------------------------+-----+
* | struct snd_soc_tplg_dapm_widget | N |
* +-------------------------------------+-----+
* | struct snd_soc_tplg_enum_control | 0|1 |
* | struct snd_soc_tplg_mixer_control | 0|N |
* +-------------------------------------+-----+
*
* Optional enum or mixer control can be appended to the end of each widget
* in the block.
*/
struct snd_soc_tplg_dapm_widget {
__le32 size; /* in bytes of this structure */
__le32 id; /* SND_SOC_DAPM_CTL */
char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
char sname[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
__le32 reg; /* negative reg = no direct dapm */
__le32 shift; /* bits to shift */
__le32 mask; /* non-shifted mask */
__u32 invert; /* invert the power bit */
__u32 ignore_suspend; /* kept enabled over suspend */
__u16 event_flags;
__u16 event_type;
__u16 num_kcontrols;
struct snd_soc_tplg_private priv;
/*
* kcontrols that relate to this widget
* follow here after widget private data
*/
} __attribute__((packed));
struct snd_soc_tplg_pcm_cfg_caps {
struct snd_soc_tplg_stream_caps caps;
struct snd_soc_tplg_stream_config configs[SND_SOC_TPLG_STREAM_CONFIG_MAX];
__le32 num_configs; /* number of configs */
} __attribute__((packed));
/*
* Describes SW/FW specific features of PCM or DAI link.
*
* File block representation for PCM/DAI-Link :-
* +-----------------------------------+-----+
* | struct snd_soc_tplg_hdr | 1 |
* +-----------------------------------+-----+
* | struct snd_soc_tplg_dapm_pcm_dai | N |
* +-----------------------------------+-----+
*/
struct snd_soc_tplg_pcm_dai {
__le32 size; /* in bytes of this structure */
char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
__le32 id; /* unique ID - used to match */
__le32 playback; /* supports playback mode */
__le32 capture; /* supports capture mode */
__le32 compress; /* 1 = compressed; 0 = PCM */
struct snd_soc_tplg_pcm_cfg_caps capconf[2]; /* capabilities and configs */
} __attribute__((packed));
#endif

31
include/uapi/sound/tlv.h Normal file
View File

@ -0,0 +1,31 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __UAPI_SOUND_TLV_H
#define __UAPI_SOUND_TLV_H
#define SNDRV_CTL_TLVT_CONTAINER 0 /* one level down - group of TLVs */
#define SNDRV_CTL_TLVT_DB_SCALE 1 /* dB scale */
#define SNDRV_CTL_TLVT_DB_LINEAR 2 /* linear volume */
#define SNDRV_CTL_TLVT_DB_RANGE 3 /* dB range container */
#define SNDRV_CTL_TLVT_DB_MINMAX 4 /* dB scale with min/max */
#define SNDRV_CTL_TLVT_DB_MINMAX_MUTE 5 /* dB scale with min/max with mute */
/*
* channel-mapping TLV items
* TLV length must match with num_channels
*/
#define SNDRV_CTL_TLVT_CHMAP_FIXED 0x101 /* fixed channel position */
#define SNDRV_CTL_TLVT_CHMAP_VAR 0x102 /* channels freely swappable */
#define SNDRV_CTL_TLVT_CHMAP_PAIRED 0x103 /* pair-wise swappable */
#endif

View File

@ -150,6 +150,8 @@ static int soundbus_device_resume(struct device * dev)
#endif /* CONFIG_PM */
/* soundbus_dev_attrs is declared in sysfs.c */
ATTRIBUTE_GROUPS(soundbus_dev);
static struct bus_type soundbus_bus_type = {
.name = "aoa-soundbus",
.probe = soundbus_probe,
@ -160,7 +162,7 @@ static struct bus_type soundbus_bus_type = {
.suspend = soundbus_device_suspend,
.resume = soundbus_device_resume,
#endif
.dev_attrs = soundbus_dev_attrs,
.dev_groups = soundbus_dev_groups,
};
int soundbus_add_one(struct soundbus_dev *dev)

View File

@ -199,6 +199,6 @@ struct soundbus_driver {
extern int soundbus_register_driver(struct soundbus_driver *drv);
extern void soundbus_unregister_driver(struct soundbus_driver *drv);
extern struct device_attribute soundbus_dev_attrs[];
extern struct attribute *soundbus_dev_attrs[];
#endif /* __SOUNDBUS_H */

View File

@ -30,13 +30,16 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
return length;
}
static DEVICE_ATTR_RO(modalias);
soundbus_config_of_attr (name, "%s\n");
static DEVICE_ATTR_RO(name);
soundbus_config_of_attr (type, "%s\n");
static DEVICE_ATTR_RO(type);
struct device_attribute soundbus_dev_attrs[] = {
__ATTR_RO(name),
__ATTR_RO(type),
__ATTR_RO(modalias),
__ATTR_NULL
struct attribute *soundbus_dev_attrs[] = {
&dev_attr_name.attr,
&dev_attr_type.attr,
&dev_attr_modalias.attr,
NULL,
};

View File

@ -6,6 +6,12 @@ config SND_PCM
tristate
select SND_TIMER
config SND_PCM_ELD
bool
config SND_PCM_IEC958
bool
config SND_DMAENGINE_PCM
tristate
@ -176,9 +182,18 @@ config SND_SUPPORT_OLD_API
Say Y here to support the obsolete ALSA PCM API (ver.0.9.0 rc3
or older).
config SND_PROC_FS
bool "Sound Proc FS Support" if EXPERT
depends on PROC_FS
default y
help
Say 'N' to disable Sound proc FS, which may reduce code size about
9KB on x86_64 platform.
If unsure say Y.
config SND_VERBOSE_PROCFS
bool "Verbose procfs contents"
depends on PROC_FS
depends on SND_PROC_FS
default y
help
Say Y here to include code for verbose procfs contents (provides
@ -221,9 +236,6 @@ config SND_PCM_XRUN_DEBUG
config SND_VMASTER
bool
config SND_KCTL_JACK
bool
config SND_DMA_SGBUF
def_bool y
depends on X86

View File

@ -3,16 +3,21 @@
# Copyright (c) 1999,2001 by Jaroslav Kysela <perex@perex.cz>
#
snd-y := sound.o init.o memory.o info.o control.o misc.o device.o
snd-y := sound.o init.o memory.o control.o misc.o device.o
ifneq ($(CONFIG_SND_PROC_FS),)
snd-y += info.o
snd-$(CONFIG_SND_OSSEMUL) += info_oss.o
endif
snd-$(CONFIG_ISA_DMA_API) += isadma.o
snd-$(CONFIG_SND_OSSEMUL) += sound_oss.o info_oss.o
snd-$(CONFIG_SND_OSSEMUL) += sound_oss.o
snd-$(CONFIG_SND_VMASTER) += vmaster.o
snd-$(CONFIG_SND_KCTL_JACK) += ctljack.o
snd-$(CONFIG_SND_JACK) += jack.o
snd-$(CONFIG_SND_JACK) += ctljack.o jack.o
snd-pcm-y := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \
pcm_memory.o memalloc.o
snd-pcm-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o
snd-pcm-$(CONFIG_SND_PCM_ELD) += pcm_drm_eld.o
snd-pcm-$(CONFIG_SND_PCM_IEC958) += pcm_iec958.o
# for trace-points
CFLAGS_pcm_lib.o := -I$(src)

View File

@ -31,19 +31,49 @@ static struct snd_kcontrol_new jack_detect_kctl = {
.get = jack_detect_kctl_get,
};
static int get_available_index(struct snd_card *card, const char *name)
{
struct snd_ctl_elem_id sid;
memset(&sid, 0, sizeof(sid));
sid.index = 0;
sid.iface = SNDRV_CTL_ELEM_IFACE_CARD;
strlcpy(sid.name, name, sizeof(sid.name));
while (snd_ctl_find_id(card, &sid))
sid.index++;
return sid.index;
}
static void jack_kctl_name_gen(char *name, const char *src_name, int size)
{
size_t count = strlen(src_name);
bool need_cat = true;
/* remove redundant " Jack" from src_name */
if (count >= 5)
need_cat = strncmp(&src_name[count - 5], " Jack", 5) ? true : false;
snprintf(name, size, need_cat ? "%s Jack" : "%s", src_name);
}
struct snd_kcontrol *
snd_kctl_jack_new(const char *name, int idx, void *private_data)
snd_kctl_jack_new(const char *name, struct snd_card *card)
{
struct snd_kcontrol *kctl;
kctl = snd_ctl_new1(&jack_detect_kctl, private_data);
kctl = snd_ctl_new1(&jack_detect_kctl, NULL);
if (!kctl)
return NULL;
snprintf(kctl->id.name, sizeof(kctl->id.name), "%s Jack", name);
kctl->id.index = idx;
jack_kctl_name_gen(kctl->id.name, name, sizeof(kctl->id.name));
kctl->id.index = get_available_index(card, kctl->id.name);
kctl->private_value = 0;
return kctl;
}
EXPORT_SYMBOL_GPL(snd_kctl_jack_new);
void snd_kctl_jack_report(struct snd_card *card,
struct snd_kcontrol *kctl, bool status)
@ -53,4 +83,3 @@ void snd_kctl_jack_report(struct snd_card *card,
kctl->private_value = status;
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
}
EXPORT_SYMBOL_GPL(snd_kctl_jack_report);

View File

@ -484,7 +484,7 @@ static int snd_hwdep_dev_disconnect(struct snd_device *device)
return 0;
}
#ifdef CONFIG_PROC_FS
#ifdef CONFIG_SND_PROC_FS
/*
* Info interface
*/
@ -521,10 +521,10 @@ static void __exit snd_hwdep_proc_done(void)
{
snd_info_free_entry(snd_hwdep_proc_entry);
}
#else /* !CONFIG_PROC_FS */
#else /* !CONFIG_SND_PROC_FS */
#define snd_hwdep_proc_init()
#define snd_hwdep_proc_done()
#endif /* CONFIG_PROC_FS */
#endif /* CONFIG_SND_PROC_FS */
/*

File diff suppressed because it is too large Load Diff

View File

@ -29,15 +29,12 @@
#include <linux/utsname.h>
#include <linux/mutex.h>
#if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS)
/*
* OSS compatible part
*/
static DEFINE_MUTEX(strings);
static char *snd_sndstat_strings[SNDRV_CARDS][SNDRV_OSS_INFO_DEV_COUNT];
static struct snd_info_entry *snd_sndstat_proc_entry;
int snd_oss_info_register(int dev, int num, char *string)
{
@ -112,27 +109,15 @@ static void snd_sndstat_proc_read(struct snd_info_entry *entry,
snd_sndstat_show_strings(buffer, "Mixers", SNDRV_OSS_INFO_DEV_MIXERS);
}
int snd_info_minor_register(void)
int __init snd_info_minor_register(void)
{
struct snd_info_entry *entry;
memset(snd_sndstat_strings, 0, sizeof(snd_sndstat_strings));
if ((entry = snd_info_create_module_entry(THIS_MODULE, "sndstat", snd_oss_root)) != NULL) {
entry->c.text.read = snd_sndstat_proc_read;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
entry = NULL;
}
}
snd_sndstat_proc_entry = entry;
return 0;
entry = snd_info_create_module_entry(THIS_MODULE, "sndstat",
snd_oss_root);
if (!entry)
return -ENOMEM;
entry->c.text.read = snd_sndstat_proc_read;
return snd_info_register(entry); /* freed in error path */
}
int snd_info_minor_unregister(void)
{
snd_info_free_entry(snd_sndstat_proc_entry);
snd_sndstat_proc_entry = NULL;
return 0;
}
#endif /* CONFIG_SND_OSSEMUL */

View File

@ -100,35 +100,29 @@ int (*snd_mixer_oss_notify_callback)(struct snd_card *card, int free_flag);
EXPORT_SYMBOL(snd_mixer_oss_notify_callback);
#endif
#ifdef CONFIG_PROC_FS
#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 inline int init_info_for_card(struct snd_card *card)
static int init_info_for_card(struct snd_card *card)
{
int err;
struct snd_info_entry *entry;
if ((err = snd_info_card_register(card)) < 0) {
dev_dbg(card->dev, "unable to create card info\n");
return err;
}
if ((entry = snd_info_create_card_entry(card, "id", card->proc_root)) == NULL) {
entry = snd_info_create_card_entry(card, "id", card->proc_root);
if (!entry) {
dev_dbg(card->dev, "unable to create card entry\n");
return err;
}
entry->c.text.read = snd_card_id_read;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
entry = NULL;
}
card->proc_id = entry;
return 0;
return snd_info_card_register(card);
}
#else /* !CONFIG_PROC_FS */
#else /* !CONFIG_SND_PROC_FS */
#define init_info_for_card(card)
#endif
@ -756,7 +750,7 @@ int snd_card_register(struct snd_card *card)
if (snd_cards[card->number]) {
/* already registered */
mutex_unlock(&snd_card_mutex);
return 0;
return snd_info_card_register(card); /* register pending info */
}
if (*card->id) {
/* make a unique id name from the given string */
@ -782,9 +776,7 @@ int snd_card_register(struct snd_card *card)
EXPORT_SYMBOL(snd_card_register);
#ifdef CONFIG_PROC_FS
static struct snd_info_entry *snd_card_info_entry;
#ifdef CONFIG_SND_PROC_FS
static void snd_card_info_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
@ -810,7 +802,6 @@ static void snd_card_info_read(struct snd_info_entry *entry,
}
#ifdef CONFIG_SND_OSSEMUL
void snd_card_info_read_oss(struct snd_info_buffer *buffer)
{
int idx, count;
@ -832,7 +823,6 @@ void snd_card_info_read_oss(struct snd_info_buffer *buffer)
#endif
#ifdef MODULE
static struct snd_info_entry *snd_card_module_info_entry;
static void snd_card_module_info_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
@ -857,36 +847,21 @@ int __init snd_card_info_init(void)
if (! entry)
return -ENOMEM;
entry->c.text.read = snd_card_info_read;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
return -ENOMEM;
}
snd_card_info_entry = entry;
if (snd_info_register(entry) < 0)
return -ENOMEM; /* freed in error path */
#ifdef MODULE
entry = snd_info_create_module_entry(THIS_MODULE, "modules", NULL);
if (entry) {
entry->c.text.read = snd_card_module_info_read;
if (snd_info_register(entry) < 0)
snd_info_free_entry(entry);
else
snd_card_module_info_entry = entry;
}
if (!entry)
return -ENOMEM;
entry->c.text.read = snd_card_module_info_read;
if (snd_info_register(entry) < 0)
return -ENOMEM; /* freed in error path */
#endif
return 0;
}
int __exit snd_card_info_done(void)
{
snd_info_free_entry(snd_card_info_entry);
#ifdef MODULE
snd_info_free_entry(snd_card_module_info_entry);
#endif
return 0;
}
#endif /* CONFIG_PROC_FS */
#endif /* CONFIG_SND_PROC_FS */
/**
* snd_component_add - add a component string

View File

@ -24,6 +24,13 @@
#include <linux/module.h>
#include <sound/jack.h>
#include <sound/core.h>
#include <sound/control.h>
struct snd_jack_kctl {
struct snd_kcontrol *kctl;
struct list_head list; /* list of controls belong to the same jack */
unsigned int mask_bits; /* only masked status bits are reported via kctl */
};
static int jack_switch_types[SND_JACK_SWITCH_TYPES] = {
SW_HEADPHONE_INSERT,
@ -54,7 +61,13 @@ static int snd_jack_dev_disconnect(struct snd_device *device)
static int snd_jack_dev_free(struct snd_device *device)
{
struct snd_jack *jack = device->device_data;
struct snd_card *card = device->card;
struct snd_jack_kctl *jack_kctl, *tmp_jack_kctl;
list_for_each_entry_safe(jack_kctl, tmp_jack_kctl, &jack->kctl_list, list) {
list_del_init(&jack_kctl->list);
snd_ctl_remove(card, jack_kctl->kctl);
}
if (jack->private_free)
jack->private_free(jack);
@ -74,6 +87,10 @@ static int snd_jack_dev_register(struct snd_device *device)
snprintf(jack->name, sizeof(jack->name), "%s %s",
card->shortname, jack->id);
if (!jack->input_dev)
return 0;
jack->input_dev->name = jack->name;
/* Default to the sound card device. */
@ -100,6 +117,77 @@ static int snd_jack_dev_register(struct snd_device *device)
return err;
}
static void snd_jack_kctl_private_free(struct snd_kcontrol *kctl)
{
struct snd_jack_kctl *jack_kctl;
jack_kctl = kctl->private_data;
if (jack_kctl) {
list_del(&jack_kctl->list);
kfree(jack_kctl);
}
}
static void snd_jack_kctl_add(struct snd_jack *jack, struct snd_jack_kctl *jack_kctl)
{
list_add_tail(&jack_kctl->list, &jack->kctl_list);
}
static struct snd_jack_kctl * snd_jack_kctl_new(struct snd_card *card, const char *name, unsigned int mask)
{
struct snd_kcontrol *kctl;
struct snd_jack_kctl *jack_kctl;
int err;
kctl = snd_kctl_jack_new(name, card);
if (!kctl)
return NULL;
err = snd_ctl_add(card, kctl);
if (err < 0)
return NULL;
jack_kctl = kzalloc(sizeof(*jack_kctl), GFP_KERNEL);
if (!jack_kctl)
goto error;
jack_kctl->kctl = kctl;
jack_kctl->mask_bits = mask;
kctl->private_data = jack_kctl;
kctl->private_free = snd_jack_kctl_private_free;
return jack_kctl;
error:
snd_ctl_free_one(kctl);
return NULL;
}
/**
* snd_jack_add_new_kctl - Create a new snd_jack_kctl and add it to jack
* @jack: the jack instance which the kctl will attaching to
* @name: the name for the snd_kcontrol object
* @mask: a bitmask of enum snd_jack_type values that can be detected
* by this snd_jack_kctl object.
*
* Creates a new snd_kcontrol object and adds it to the jack kctl_list.
*
* Return: Zero if successful, or a negative error code on failure.
*/
int snd_jack_add_new_kctl(struct snd_jack *jack, const char * name, int mask)
{
struct snd_jack_kctl *jack_kctl;
jack_kctl = snd_jack_kctl_new(jack->card, name, mask);
if (!jack_kctl)
return -ENOMEM;
snd_jack_kctl_add(jack, jack_kctl);
return 0;
}
EXPORT_SYMBOL(snd_jack_add_new_kctl);
/**
* snd_jack_new - Create a new jack
* @card: the card instance
@ -107,6 +195,8 @@ static int snd_jack_dev_register(struct snd_device *device)
* @type: a bitmask of enum snd_jack_type values that can be detected by
* this jack
* @jjack: Used to provide the allocated jack object to the caller.
* @initial_kctl: if true, create a kcontrol and add it to the jack list.
* @phantom_jack: Don't create a input device for phantom jacks.
*
* Creates a new jack object.
*
@ -114,9 +204,10 @@ static int snd_jack_dev_register(struct snd_device *device)
* On success @jjack will be initialised.
*/
int snd_jack_new(struct snd_card *card, const char *id, int type,
struct snd_jack **jjack)
struct snd_jack **jjack, bool initial_kctl, bool phantom_jack)
{
struct snd_jack *jack;
struct snd_jack_kctl *jack_kctl = NULL;
int err;
int i;
static struct snd_device_ops ops = {
@ -125,31 +216,47 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
.dev_disconnect = snd_jack_dev_disconnect,
};
if (initial_kctl) {
jack_kctl = snd_jack_kctl_new(card, id, type);
if (!jack_kctl)
return -ENOMEM;
}
jack = kzalloc(sizeof(struct snd_jack), GFP_KERNEL);
if (jack == NULL)
return -ENOMEM;
jack->id = kstrdup(id, GFP_KERNEL);
jack->input_dev = input_allocate_device();
if (jack->input_dev == NULL) {
err = -ENOMEM;
goto fail_input;
/* don't creat input device for phantom jack */
if (!phantom_jack) {
jack->input_dev = input_allocate_device();
if (jack->input_dev == NULL) {
err = -ENOMEM;
goto fail_input;
}
jack->input_dev->phys = "ALSA";
jack->type = type;
for (i = 0; i < SND_JACK_SWITCH_TYPES; i++)
if (type & (1 << i))
input_set_capability(jack->input_dev, EV_SW,
jack_switch_types[i]);
}
jack->input_dev->phys = "ALSA";
jack->type = type;
for (i = 0; i < SND_JACK_SWITCH_TYPES; i++)
if (type & (1 << i))
input_set_capability(jack->input_dev, EV_SW,
jack_switch_types[i]);
err = snd_device_new(card, SNDRV_DEV_JACK, jack, &ops);
if (err < 0)
goto fail_input;
jack->card = card;
INIT_LIST_HEAD(&jack->kctl_list);
if (initial_kctl)
snd_jack_kctl_add(jack, jack_kctl);
*jjack = jack;
return 0;
@ -175,6 +282,8 @@ EXPORT_SYMBOL(snd_jack_new);
void snd_jack_set_parent(struct snd_jack *jack, struct device *parent)
{
WARN_ON(jack->registered);
if (!jack->input_dev)
return;
jack->input_dev->dev.parent = parent;
}
@ -230,11 +339,19 @@ EXPORT_SYMBOL(snd_jack_set_key);
*/
void snd_jack_report(struct snd_jack *jack, int status)
{
struct snd_jack_kctl *jack_kctl;
int i;
if (!jack)
return;
list_for_each_entry(jack_kctl, &jack->kctl_list, list)
snd_kctl_jack_report(jack->card, jack_kctl->kctl,
status & jack_kctl->mask_bits);
if (!jack->input_dev)
return;
for (i = 0; i < ARRAY_SIZE(jack->key); i++) {
int testbit = SND_JACK_BTN_0 >> i;
@ -252,9 +369,6 @@ void snd_jack_report(struct snd_jack *jack, int status)
}
input_sync(jack->input_dev);
}
EXPORT_SYMBOL(snd_jack_report);
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
MODULE_DESCRIPTION("Jack detection support for ALSA");
MODULE_LICENSE("GPL");

View File

@ -1111,7 +1111,7 @@ static int snd_mixer_oss_build_input(struct snd_mixer_oss *mixer, struct snd_mix
return 0;
}
#ifdef CONFIG_PROC_FS
#ifdef CONFIG_SND_PROC_FS
/*
*/
#define MIXER_VOL(name) [SOUND_MIXER_##name] = #name
@ -1255,10 +1255,10 @@ static void snd_mixer_oss_proc_done(struct snd_mixer_oss *mixer)
snd_info_free_entry(mixer->proc_entry);
mixer->proc_entry = NULL;
}
#else /* !CONFIG_PROC_FS */
#else /* !CONFIG_SND_PROC_FS */
#define snd_mixer_oss_proc_init(mix)
#define snd_mixer_oss_proc_done(mix)
#endif /* CONFIG_PROC_FS */
#endif /* CONFIG_SND_PROC_FS */
static void snd_mixer_oss_build(struct snd_mixer_oss *mixer)
{

View File

@ -1027,7 +1027,8 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
static ssize_t show_pcm_class(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct snd_pcm *pcm;
struct snd_pcm_str *pstr = container_of(dev, struct snd_pcm_str, dev);
struct snd_pcm *pcm = pstr->pcm;
const char *str;
static const char *strs[SNDRV_PCM_CLASS_LAST + 1] = {
[SNDRV_PCM_CLASS_GENERIC] = "generic",
@ -1036,8 +1037,7 @@ static ssize_t show_pcm_class(struct device *dev,
[SNDRV_PCM_CLASS_DIGITIZER] = "digitizer",
};
if (! (pcm = dev_get_drvdata(dev)) ||
pcm->dev_class > SNDRV_PCM_CLASS_LAST)
if (pcm->dev_class > SNDRV_PCM_CLASS_LAST)
str = "none";
else
str = strs[pcm->dev_class];
@ -1181,7 +1181,7 @@ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree)
}
EXPORT_SYMBOL(snd_pcm_notify);
#ifdef CONFIG_PROC_FS
#ifdef CONFIG_SND_PROC_FS
/*
* Info interface
*/
@ -1227,10 +1227,10 @@ static void snd_pcm_proc_done(void)
snd_info_free_entry(snd_pcm_proc_entry);
}
#else /* !CONFIG_PROC_FS */
#else /* !CONFIG_SND_PROC_FS */
#define snd_pcm_proc_init()
#define snd_pcm_proc_done()
#endif /* CONFIG_PROC_FS */
#endif /* CONFIG_SND_PROC_FS */
/*

99
sound/core/pcm_drm_eld.c Normal file
View File

@ -0,0 +1,99 @@
/*
* PCM DRM helpers
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/export.h>
#include <drm/drm_edid.h>
#include <sound/pcm.h>
#include <sound/pcm_drm_eld.h>
static const unsigned int eld_rates[] = {
32000,
44100,
48000,
88200,
96000,
176400,
192000,
};
static unsigned int sad_max_channels(const u8 *sad)
{
return 1 + (sad[0] & 7);
}
static int eld_limit_rates(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
struct snd_interval *r = hw_param_interval(params, rule->var);
struct snd_interval *c;
unsigned int rate_mask = 7, i;
const u8 *sad, *eld = rule->private;
sad = drm_eld_sad(eld);
if (sad) {
c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
for (i = drm_eld_sad_count(eld); i > 0; i--, sad += 3) {
unsigned max_channels = sad_max_channels(sad);
/*
* Exclude SADs which do not include the
* requested number of channels.
*/
if (c->min <= max_channels)
rate_mask |= sad[1];
}
}
return snd_interval_list(r, ARRAY_SIZE(eld_rates), eld_rates,
rate_mask);
}
static int eld_limit_channels(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
struct snd_interval *c = hw_param_interval(params, rule->var);
struct snd_interval *r;
struct snd_interval t = { .min = 1, .max = 2, .integer = 1, };
unsigned int i;
const u8 *sad, *eld = rule->private;
sad = drm_eld_sad(eld);
if (sad) {
unsigned int rate_mask = 0;
/* Convert the rate interval to a mask */
r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
for (i = 0; i < ARRAY_SIZE(eld_rates); i++)
if (r->min <= eld_rates[i] && r->max >= eld_rates[i])
rate_mask |= BIT(i);
for (i = drm_eld_sad_count(eld); i > 0; i--, sad += 3)
if (rate_mask & sad[1])
t.max = max(t.max, sad_max_channels(sad));
}
return snd_interval_refine(c, &t);
}
int snd_pcm_hw_constraint_eld(struct snd_pcm_runtime *runtime, void *eld)
{
int ret;
ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
eld_limit_rates, eld,
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
if (ret < 0)
return ret;
ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
eld_limit_channels, eld,
SNDRV_PCM_HW_PARAM_RATE, -1);
return ret;
}
EXPORT_SYMBOL_GPL(snd_pcm_hw_constraint_eld);

95
sound/core/pcm_iec958.c Normal file
View File

@ -0,0 +1,95 @@
/*
* PCM DRM helpers
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/export.h>
#include <linux/types.h>
#include <sound/asoundef.h>
#include <sound/pcm.h>
#include <sound/pcm_iec958.h>
/**
* snd_pcm_create_iec958_consumer - create consumer format IEC958 channel status
* @runtime: pcm runtime structure with ->rate filled in
* @cs: channel status buffer, at least four bytes
* @len: length of channel status buffer
*
* Create the consumer format channel status data in @cs of maximum size
* @len corresponding to the parameters of the PCM runtime @runtime.
*
* Drivers may wish to tweak the contents of the buffer after creation.
*
* Returns: length of buffer, or negative error code if something failed.
*/
int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
size_t len)
{
unsigned int fs, ws;
if (len < 4)
return -EINVAL;
switch (runtime->rate) {
case 32000:
fs = IEC958_AES3_CON_FS_32000;
break;
case 44100:
fs = IEC958_AES3_CON_FS_44100;
break;
case 48000:
fs = IEC958_AES3_CON_FS_48000;
break;
case 88200:
fs = IEC958_AES3_CON_FS_88200;
break;
case 96000:
fs = IEC958_AES3_CON_FS_96000;
break;
case 176400:
fs = IEC958_AES3_CON_FS_176400;
break;
case 192000:
fs = IEC958_AES3_CON_FS_192000;
break;
default:
return -EINVAL;
}
if (len > 4) {
switch (snd_pcm_format_width(runtime->format)) {
case 16:
ws = IEC958_AES4_CON_WORDLEN_20_16;
break;
case 18:
ws = IEC958_AES4_CON_WORDLEN_22_18;
break;
case 20:
ws = IEC958_AES4_CON_WORDLEN_20_16 |
IEC958_AES4_CON_MAX_WORDLEN_24;
break;
case 24:
ws = IEC958_AES4_CON_WORDLEN_24_20 |
IEC958_AES4_CON_MAX_WORDLEN_24;
break;
default:
return -EINVAL;
}
}
memset(cs, 0, len);
cs[0] = IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS_NONE;
cs[1] = IEC958_AES1_CON_GENERAL;
cs[2] = IEC958_AES2_CON_SOURCE_UNSPEC | IEC958_AES2_CON_CHANNEL_UNSPEC;
cs[3] = IEC958_AES3_CON_CLOCK_1000PPM | fs;
if (len > 4)
cs[4] = ws;
return len;
}
EXPORT_SYMBOL(snd_pcm_create_iec958_consumer);

View File

@ -6,7 +6,8 @@
snd-seq-device-objs := seq_device.o
snd-seq-objs := seq.o seq_lock.o seq_clientmgr.o seq_memory.o seq_queue.o \
seq_fifo.o seq_prioq.o seq_timer.o \
seq_system.o seq_ports.o seq_info.o
seq_system.o seq_ports.o
snd-seq-$(CONFIG_SND_PROC_FS) += seq_info.o
snd-seq-midi-objs := seq_midi.o
snd-seq-midi-emul-objs := seq_midi_emul.o
snd-seq-midi-event-objs := seq_midi_event.o

View File

@ -45,7 +45,7 @@ MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_MUSIC);
*/
static int register_device(void);
static void unregister_device(void);
#ifdef CONFIG_PROC_FS
#ifdef CONFIG_SND_PROC_FS
static int register_proc(void);
static void unregister_proc(void);
#else
@ -261,7 +261,7 @@ unregister_device(void)
* /proc interface
*/
#ifdef CONFIG_PROC_FS
#ifdef CONFIG_SND_PROC_FS
static struct snd_info_entry *info_entry;
@ -303,4 +303,4 @@ unregister_proc(void)
snd_info_free_entry(info_entry);
info_entry = NULL;
}
#endif /* CONFIG_PROC_FS */
#endif /* CONFIG_SND_PROC_FS */

View File

@ -479,8 +479,7 @@ snd_seq_oss_reset(struct seq_oss_devinfo *dp)
snd_seq_oss_timer_stop(dp->timer);
}
#ifdef CONFIG_PROC_FS
#ifdef CONFIG_SND_PROC_FS
/*
* misc. functions for proc interface
*/
@ -531,4 +530,4 @@ snd_seq_oss_system_info_read(struct snd_info_buffer *buf)
snd_seq_oss_readq_info_read(dp->readq, buf);
}
}
#endif /* CONFIG_PROC_FS */
#endif /* CONFIG_SND_PROC_FS */

View File

@ -665,7 +665,7 @@ snd_seq_oss_midi_make_info(struct seq_oss_devinfo *dp, int dev, struct midi_info
}
#ifdef CONFIG_PROC_FS
#ifdef CONFIG_SND_PROC_FS
/*
* proc interface
*/
@ -705,4 +705,4 @@ snd_seq_oss_midi_info_read(struct snd_info_buffer *buf)
snd_use_lock_free(&mdev->use_lock);
}
}
#endif /* CONFIG_PROC_FS */
#endif /* CONFIG_SND_PROC_FS */

View File

@ -222,7 +222,7 @@ snd_seq_oss_readq_put_timestamp(struct seq_oss_readq *q, unsigned long curt, int
}
#ifdef CONFIG_PROC_FS
#ifdef CONFIG_SND_PROC_FS
/*
* proc interface
*/
@ -233,4 +233,4 @@ snd_seq_oss_readq_info_read(struct seq_oss_readq *q, struct snd_info_buffer *buf
(waitqueue_active(&q->midi_sleep) ? "sleeping":"running"),
q->qlen, q->input_time);
}
#endif /* CONFIG_PROC_FS */
#endif /* CONFIG_SND_PROC_FS */

View File

@ -630,7 +630,7 @@ snd_seq_oss_synth_make_info(struct seq_oss_devinfo *dp, int dev, struct synth_in
}
#ifdef CONFIG_PROC_FS
#ifdef CONFIG_SND_PROC_FS
/*
* proc interface
*/
@ -658,4 +658,4 @@ snd_seq_oss_synth_info_read(struct snd_info_buffer *buf)
snd_use_lock_free(&rec->use_lock);
}
}
#endif /* CONFIG_PROC_FS */
#endif /* CONFIG_SND_PROC_FS */

View File

@ -2447,7 +2447,7 @@ EXPORT_SYMBOL(snd_seq_kernel_client_write_poll);
/*---------------------------------------------------------------------------*/
#ifdef CONFIG_PROC_FS
#ifdef CONFIG_SND_PROC_FS
/*
* /proc interface
*/
@ -2549,7 +2549,7 @@ void snd_seq_info_clients_read(struct snd_info_entry *entry,
snd_seq_client_unlock(client);
}
}
#endif /* CONFIG_PROC_FS */
#endif /* CONFIG_SND_PROC_FS */
/*---------------------------------------------------------------------------*/

View File

@ -72,7 +72,7 @@ static struct bus_type snd_seq_bus_type = {
/*
* proc interface -- just for compatibility
*/
#ifdef CONFIG_PROC_FS
#ifdef CONFIG_SND_PROC_FS
static struct snd_info_entry *info_entry;
static int print_dev_info(struct device *dev, void *data)
@ -272,7 +272,7 @@ EXPORT_SYMBOL_GPL(snd_seq_driver_unregister);
static int __init seq_dev_proc_init(void)
{
#ifdef CONFIG_PROC_FS
#ifdef CONFIG_SND_PROC_FS
info_entry = snd_info_create_module_entry(THIS_MODULE, "drivers",
snd_seq_root);
if (info_entry == NULL)
@ -305,7 +305,7 @@ static void __exit alsa_seq_device_exit(void)
#ifdef CONFIG_MODULES
cancel_work_sync(&autoload_work);
#endif
#ifdef CONFIG_PROC_FS
#ifdef CONFIG_SND_PROC_FS
snd_info_free_entry(info_entry);
#endif
bus_unregister(&snd_seq_bus_type);

View File

@ -27,7 +27,6 @@
#include "seq_clientmgr.h"
#include "seq_timer.h"
#ifdef CONFIG_PROC_FS
static struct snd_info_entry *queues_entry;
static struct snd_info_entry *clients_entry;
static struct snd_info_entry *timer_entry;
@ -51,6 +50,13 @@ create_info_entry(char *name, void (*read)(struct snd_info_entry *,
return entry;
}
static void free_info_entries(void)
{
snd_info_free_entry(queues_entry);
snd_info_free_entry(clients_entry);
snd_info_free_entry(timer_entry);
}
/* create all our /proc entries */
int __init snd_seq_info_init(void)
{
@ -59,14 +65,17 @@ int __init snd_seq_info_init(void)
clients_entry = create_info_entry("clients",
snd_seq_info_clients_read);
timer_entry = create_info_entry("timer", snd_seq_info_timer_read);
if (!queues_entry || !clients_entry || !timer_entry)
goto error;
return 0;
error:
free_info_entries();
return -ENOMEM;
}
int __exit snd_seq_info_done(void)
{
snd_info_free_entry(queues_entry);
snd_info_free_entry(clients_entry);
snd_info_free_entry(timer_entry);
free_info_entries();
return 0;
}
#endif

View File

@ -29,7 +29,7 @@ void snd_seq_info_timer_read(struct snd_info_entry *entry, struct snd_info_buffe
void snd_seq_info_queues_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer);
#ifdef CONFIG_PROC_FS
#ifdef CONFIG_SND_PROC_FS
int snd_seq_info_init( void );
int snd_seq_info_done( void );
#else

View File

@ -753,7 +753,7 @@ int snd_seq_control_queue(struct snd_seq_event *ev, int atomic, int hop)
/*----------------------------------------------------------------*/
#ifdef CONFIG_PROC_FS
#ifdef CONFIG_SND_PROC_FS
/* exported to seq_info.c */
void snd_seq_info_queues_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
@ -787,5 +787,5 @@ void snd_seq_info_queues_read(struct snd_info_entry *entry,
queuefree(q);
}
}
#endif /* CONFIG_PROC_FS */
#endif /* CONFIG_SND_PROC_FS */

View File

@ -422,7 +422,7 @@ snd_seq_tick_time_t snd_seq_timer_get_cur_tick(struct snd_seq_timer *tmr)
}
#ifdef CONFIG_PROC_FS
#ifdef CONFIG_SND_PROC_FS
/* exported to seq_info.c */
void snd_seq_info_timer_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
@ -449,5 +449,5 @@ void snd_seq_info_timer_read(struct snd_info_entry *entry,
queuefree(q);
}
}
#endif /* CONFIG_PROC_FS */
#endif /* CONFIG_SND_PROC_FS */

View File

@ -330,13 +330,10 @@ int snd_unregister_device(struct device *dev)
}
EXPORT_SYMBOL(snd_unregister_device);
#ifdef CONFIG_PROC_FS
#ifdef CONFIG_SND_PROC_FS
/*
* INFO PART
*/
static struct snd_info_entry *snd_minor_info_entry;
static const char *snd_device_type_name(int type)
{
switch (type) {
@ -389,23 +386,12 @@ int __init snd_minor_info_init(void)
struct snd_info_entry *entry;
entry = snd_info_create_module_entry(THIS_MODULE, "devices", NULL);
if (entry) {
entry->c.text.read = snd_minor_info_read;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
entry = NULL;
}
}
snd_minor_info_entry = entry;
return 0;
if (!entry)
return -ENOMEM;
entry->c.text.read = snd_minor_info_read;
return snd_info_register(entry); /* freed in error path */
}
int __exit snd_minor_info_done(void)
{
snd_info_free_entry(snd_minor_info_entry);
return 0;
}
#endif /* CONFIG_PROC_FS */
#endif /* CONFIG_SND_PROC_FS */
/*
* INIT PART
@ -423,7 +409,6 @@ static int __init alsa_sound_init(void)
unregister_chrdev(major, "alsa");
return -ENOMEM;
}
snd_info_minor_register();
#ifndef MODULE
pr_info("Advanced Linux Sound Architecture Driver Initialized.\n");
#endif
@ -432,7 +417,6 @@ static int __init alsa_sound_init(void)
static void __exit alsa_sound_exit(void)
{
snd_info_minor_unregister();
snd_info_done();
unregister_chrdev(major, "alsa");
}

View File

@ -19,12 +19,6 @@
*
*/
#ifdef CONFIG_SND_OSSEMUL
#if !IS_ENABLED(CONFIG_SOUND)
#error "Enable the OSS soundcore multiplexer (CONFIG_SOUND) in the kernel."
#endif
#include <linux/init.h>
#include <linux/export.h>
#include <linux/slab.h>
@ -213,10 +207,7 @@ EXPORT_SYMBOL(snd_unregister_oss_device);
* INFO PART
*/
#ifdef CONFIG_PROC_FS
static struct snd_info_entry *snd_minor_info_oss_entry;
#ifdef CONFIG_SND_PROC_FS
static const char *snd_oss_device_type_name(int type)
{
switch (type) {
@ -263,22 +254,9 @@ int __init snd_minor_info_oss_init(void)
struct snd_info_entry *entry;
entry = snd_info_create_module_entry(THIS_MODULE, "devices", snd_oss_root);
if (entry) {
entry->c.text.read = snd_minor_info_oss_read;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
entry = NULL;
}
}
snd_minor_info_oss_entry = entry;
return 0;
if (!entry)
return -ENOMEM;
entry->c.text.read = snd_minor_info_oss_read;
return snd_info_register(entry); /* freed in error path */
}
int __exit snd_minor_info_oss_done(void)
{
snd_info_free_entry(snd_minor_info_oss_entry);
return 0;
}
#endif /* CONFIG_PROC_FS */
#endif /* CONFIG_SND_OSSEMUL */
#endif /* CONFIG_SND_PROC_FS */

View File

@ -1034,7 +1034,7 @@ static int snd_timer_register_system(void)
return snd_timer_global_register(timer);
}
#ifdef CONFIG_PROC_FS
#ifdef CONFIG_SND_PROC_FS
/*
* Info interface
*/
@ -1104,7 +1104,7 @@ static void __exit snd_timer_proc_done(void)
{
snd_info_free_entry(snd_timer_proc_entry);
}
#else /* !CONFIG_PROC_FS */
#else /* !CONFIG_SND_PROC_FS */
#define snd_timer_proc_init()
#define snd_timer_proc_done()
#endif

View File

@ -1053,8 +1053,6 @@ static int loopback_mixer_new(struct loopback *loopback, int notify)
return 0;
}
#ifdef CONFIG_PROC_FS
static void print_dpcm_info(struct snd_info_buffer *buffer,
struct loopback_pcm *dpcm,
const char *id)
@ -1128,12 +1126,6 @@ static int loopback_proc_new(struct loopback *loopback, int cidx)
return 0;
}
#else /* !CONFIG_PROC_FS */
#define loopback_proc_new(loopback, cidx) do { } while (0)
#endif
static int loopback_probe(struct platform_device *devptr)
{
struct snd_card *card;

View File

@ -156,13 +156,13 @@ static int emu10k1_playback_constraints(struct snd_pcm_runtime *runtime)
return 0;
}
struct dummy_model model_emu10k1 = {
static struct dummy_model model_emu10k1 = {
.name = "emu10k1",
.playback_constraints = emu10k1_playback_constraints,
.buffer_bytes_max = 128 * 1024,
};
struct dummy_model model_rme9652 = {
static struct dummy_model model_rme9652 = {
.name = "rme9652",
.buffer_bytes_max = 26 * 64 * 1024,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
@ -172,7 +172,7 @@ struct dummy_model model_rme9652 = {
.periods_max = 2,
};
struct dummy_model model_ice1712 = {
static struct dummy_model model_ice1712 = {
.name = "ice1712",
.buffer_bytes_max = 256 * 1024,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
@ -182,7 +182,7 @@ struct dummy_model model_ice1712 = {
.periods_max = 1024,
};
struct dummy_model model_uda1341 = {
static struct dummy_model model_uda1341 = {
.name = "uda1341",
.buffer_bytes_max = 16380,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
@ -192,7 +192,7 @@ struct dummy_model model_uda1341 = {
.periods_max = 255,
};
struct dummy_model model_ac97 = {
static struct dummy_model model_ac97 = {
.name = "ac97",
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels_min = 2,
@ -202,7 +202,7 @@ struct dummy_model model_ac97 = {
.rate_max = 48000,
};
struct dummy_model model_ca0106 = {
static struct dummy_model model_ca0106 = {
.name = "ca0106",
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.buffer_bytes_max = ((65536-64)*8),
@ -216,7 +216,7 @@ struct dummy_model model_ca0106 = {
.rate_max = 192000,
};
struct dummy_model *dummy_models[] = {
static struct dummy_model *dummy_models[] = {
&model_emu10k1,
&model_rme9652,
&model_ice1712,
@ -914,7 +914,7 @@ static int snd_card_dummy_new_mixer(struct snd_dummy *dummy)
return 0;
}
#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_PROC_FS)
#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_SND_PROC_FS)
/*
* proc interface
*/
@ -1042,7 +1042,7 @@ static void dummy_proc_init(struct snd_dummy *chip)
}
#else
#define dummy_proc_init(x)
#endif /* CONFIG_SND_DEBUG && CONFIG_PROC_FS */
#endif /* CONFIG_SND_DEBUG && CONFIG_SND_PROC_FS */
static int snd_dummy_probe(struct platform_device *devptr)
{

View File

@ -3,7 +3,8 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
snd-opl4-lib-objs := opl4_lib.o opl4_mixer.o opl4_proc.o
snd-opl4-lib-objs := opl4_lib.o opl4_mixer.o
snd-opl4-lib-$(CONFIG_SND_PROC_FS) += opl4_proc.o
snd-opl4-synth-objs := opl4_seq.o opl4_synth.o yrw801.o
obj-$(CONFIG_SND_OPL4_LIB) += snd-opl4-lib.o

View File

@ -176,9 +176,7 @@ static int snd_opl4_create_seq_dev(struct snd_opl4 *opl4, int seq_device)
static void snd_opl4_free(struct snd_opl4 *opl4)
{
#ifdef CONFIG_PROC_FS
snd_opl4_free_proc(opl4);
#endif
release_and_free_resource(opl4->res_fm_port);
release_and_free_resource(opl4->res_pcm_port);
kfree(opl4);
@ -249,9 +247,7 @@ int snd_opl4_create(struct snd_card *card,
snd_opl4_enable_opl4(opl4);
snd_opl4_create_mixer(opl4);
#ifdef CONFIG_PROC_FS
snd_opl4_create_proc(opl4);
#endif
#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
opl4->seq_client = -1;

View File

@ -178,7 +178,7 @@ struct snd_opl4 {
spinlock_t reg_lock;
struct snd_card *card;
#ifdef CONFIG_PROC_FS
#ifdef CONFIG_SND_PROC_FS
struct snd_info_entry *proc_entry;
int memory_access;
#endif
@ -207,10 +207,13 @@ void snd_opl4_write_memory(struct snd_opl4 *opl4, const char *buf, int offset, i
/* opl4_mixer.c */
int snd_opl4_create_mixer(struct snd_opl4 *opl4);
#ifdef CONFIG_PROC_FS
#ifdef CONFIG_SND_PROC_FS
/* opl4_proc.c */
int snd_opl4_create_proc(struct snd_opl4 *opl4);
void snd_opl4_free_proc(struct snd_opl4 *opl4);
#else
static inline int snd_opl4_create_proc(struct snd_opl4 *opl4) { return 0; }
static inline void snd_opl4_free_proc(struct snd_opl4 *opl4) {}
#endif
/* opl4_seq.c */

View File

@ -22,8 +22,6 @@
#include <linux/export.h>
#include <sound/info.h>
#ifdef CONFIG_PROC_FS
static int snd_opl4_mem_proc_open(struct snd_info_entry *entry,
unsigned short mode, void **file_private_data)
{
@ -129,5 +127,3 @@ void snd_opl4_free_proc(struct snd_opl4 *opl4)
{
snd_info_free_entry(opl4->proc_entry);
}
#endif /* CONFIG_PROC_FS */

View File

@ -95,6 +95,7 @@ config SND_BEBOB
* Tascam IF-FW/DM
* Behringer XENIX UFX 1204/1604
* Behringer Digital Mixer X32 series (X-UF Card)
* Behringer FCA610/1616
* Apogee Rosetta 200/400 (X-FireWire card)
* Apogee DA/AD/DD-16X (X-FireWire card)
* Apogee Ensemble
@ -114,6 +115,7 @@ config SND_BEBOB
* M-Audio FireWire410/AudioPhile/Solo
* M-Audio Ozonic/NRV10/ProfireLightBridge
* M-Audio FireWire 1814/ProjectMix IO
* Digidesign Mbox 2 Pro
To compile this driver as a module, choose M here: the module
will be called snd-bebob.

View File

@ -40,24 +40,28 @@
#define TAG_CIP 1
/* common isochronous packet header parameters */
#define CIP_EOH (1u << 31)
#define CIP_EOH_SHIFT 31
#define CIP_EOH (1u << CIP_EOH_SHIFT)
#define CIP_EOH_MASK 0x80000000
#define CIP_FMT_AM (0x10 << 24)
#define CIP_SID_SHIFT 24
#define CIP_SID_MASK 0x3f000000
#define CIP_DBS_MASK 0x00ff0000
#define CIP_DBS_SHIFT 16
#define CIP_DBC_MASK 0x000000ff
#define CIP_FMT_SHIFT 24
#define CIP_FMT_MASK 0x3f000000
#define CIP_FDF_MASK 0x00ff0000
#define CIP_FDF_SHIFT 16
#define CIP_SYT_MASK 0x0000ffff
#define CIP_SYT_NO_INFO 0xffff
#define CIP_FDF_MASK 0x00ff0000
#define CIP_FDF_SFC_SHIFT 16
/*
* Audio and Music transfer protocol specific parameters
* only "Clock-based rate control mode" is supported
*/
#define AMDTP_FDF_AM824 (0 << (CIP_FDF_SFC_SHIFT + 3))
#define CIP_FMT_AM (0x10 << CIP_FMT_SHIFT)
#define AMDTP_FDF_AM824 (0 << (CIP_FDF_SHIFT + 3))
#define AMDTP_FDF_NO_DATA 0xff
#define AMDTP_DBS_MASK 0x00ff0000
#define AMDTP_DBS_SHIFT 16
#define AMDTP_DBC_MASK 0x000000ff
/* TODO: make these configurable */
#define INTERRUPT_INTERVAL 16
@ -251,19 +255,24 @@ EXPORT_SYMBOL(amdtp_stream_set_parameters);
*/
unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s)
{
return 8 + s->syt_interval * s->data_block_quadlets * 4;
unsigned int multiplier = 1;
if (s->flags & CIP_JUMBO_PAYLOAD)
multiplier = 5;
return 8 + s->syt_interval * s->data_block_quadlets * 4 * multiplier;
}
EXPORT_SYMBOL(amdtp_stream_get_max_payload);
static void amdtp_write_s16(struct amdtp_stream *s,
struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames);
static void amdtp_write_s32(struct amdtp_stream *s,
struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames);
static void amdtp_read_s32(struct amdtp_stream *s,
struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames);
static void write_pcm_s16(struct amdtp_stream *s,
struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames);
static void write_pcm_s32(struct amdtp_stream *s,
struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames);
static void read_pcm_s32(struct amdtp_stream *s,
struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames);
/**
* amdtp_stream_set_pcm_format - set the PCM format
@ -286,16 +295,16 @@ void amdtp_stream_set_pcm_format(struct amdtp_stream *s,
/* fall through */
case SNDRV_PCM_FORMAT_S16:
if (s->direction == AMDTP_OUT_STREAM) {
s->transfer_samples = amdtp_write_s16;
s->transfer_samples = write_pcm_s16;
break;
}
WARN_ON(1);
/* fall through */
case SNDRV_PCM_FORMAT_S32:
if (s->direction == AMDTP_OUT_STREAM)
s->transfer_samples = amdtp_write_s32;
s->transfer_samples = write_pcm_s32;
else
s->transfer_samples = amdtp_read_s32;
s->transfer_samples = read_pcm_s32;
break;
}
}
@ -316,17 +325,25 @@ void amdtp_stream_pcm_prepare(struct amdtp_stream *s)
}
EXPORT_SYMBOL(amdtp_stream_pcm_prepare);
static unsigned int calculate_data_blocks(struct amdtp_stream *s)
static unsigned int calculate_data_blocks(struct amdtp_stream *s,
unsigned int syt)
{
unsigned int phase, data_blocks;
if (s->flags & CIP_BLOCKING)
data_blocks = s->syt_interval;
else if (!cip_sfc_is_base_44100(s->sfc)) {
/* Sample_rate / 8000 is an integer, and precomputed. */
data_blocks = s->data_block_state;
/* Blocking mode. */
if (s->flags & CIP_BLOCKING) {
/* This module generate empty packet for 'no data'. */
if (syt == CIP_SYT_NO_INFO)
data_blocks = 0;
else
data_blocks = s->syt_interval;
/* Non-blocking mode. */
} else {
phase = s->data_block_state;
if (!cip_sfc_is_base_44100(s->sfc)) {
/* Sample_rate / 8000 is an integer, and precomputed. */
data_blocks = s->data_block_state;
} else {
phase = s->data_block_state;
/*
* This calculates the number of data blocks per packet so that
@ -336,16 +353,17 @@ static unsigned int calculate_data_blocks(struct amdtp_stream *s)
* as possible in the sequence (to prevent underruns of the
* device's buffer).
*/
if (s->sfc == CIP_SFC_44100)
/* 6 6 5 6 5 6 5 ... */
data_blocks = 5 + ((phase & 1) ^
(phase == 0 || phase >= 40));
else
/* 12 11 11 11 11 ... or 23 22 22 22 22 ... */
data_blocks = 11 * (s->sfc >> 1) + (phase == 0);
if (++phase >= (80 >> (s->sfc >> 1)))
phase = 0;
s->data_block_state = phase;
if (s->sfc == CIP_SFC_44100)
/* 6 6 5 6 5 6 5 ... */
data_blocks = 5 + ((phase & 1) ^
(phase == 0 || phase >= 40));
else
/* 12 11 11 11 11 ... or 23 22 22 22 22 ... */
data_blocks = 11 * (s->sfc >> 1) + (phase == 0);
if (++phase >= (80 >> (s->sfc >> 1)))
phase = 0;
s->data_block_state = phase;
}
}
return data_blocks;
@ -394,9 +412,9 @@ static unsigned int calculate_syt(struct amdtp_stream *s,
}
}
static void amdtp_write_s32(struct amdtp_stream *s,
struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames)
static void write_pcm_s32(struct amdtp_stream *s,
struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames)
{
struct snd_pcm_runtime *runtime = pcm->runtime;
unsigned int channels, remaining_frames, i, c;
@ -419,9 +437,9 @@ static void amdtp_write_s32(struct amdtp_stream *s,
}
}
static void amdtp_write_s16(struct amdtp_stream *s,
struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames)
static void write_pcm_s16(struct amdtp_stream *s,
struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames)
{
struct snd_pcm_runtime *runtime = pcm->runtime;
unsigned int channels, remaining_frames, i, c;
@ -444,9 +462,9 @@ static void amdtp_write_s16(struct amdtp_stream *s,
}
}
static void amdtp_read_s32(struct amdtp_stream *s,
struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames)
static void read_pcm_s32(struct amdtp_stream *s,
struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames)
{
struct snd_pcm_runtime *runtime = pcm->runtime;
unsigned int channels, remaining_frames, i, c;
@ -468,8 +486,8 @@ static void amdtp_read_s32(struct amdtp_stream *s,
}
}
static void amdtp_fill_pcm_silence(struct amdtp_stream *s,
__be32 *buffer, unsigned int frames)
static void write_pcm_silence(struct amdtp_stream *s,
__be32 *buffer, unsigned int frames)
{
unsigned int i, c;
@ -510,8 +528,8 @@ static void midi_rate_use_one_byte(struct amdtp_stream *s, unsigned int port)
s->midi_fifo_used[port] += amdtp_rate_table[s->sfc];
}
static void amdtp_fill_midi(struct amdtp_stream *s,
__be32 *buffer, unsigned int frames)
static void write_midi_messages(struct amdtp_stream *s,
__be32 *buffer, unsigned int frames)
{
unsigned int f, port;
u8 *b;
@ -537,8 +555,8 @@ static void amdtp_fill_midi(struct amdtp_stream *s,
}
}
static void amdtp_pull_midi(struct amdtp_stream *s,
__be32 *buffer, unsigned int frames)
static void read_midi_messages(struct amdtp_stream *s,
__be32 *buffer, unsigned int frames)
{
unsigned int f, port;
int len;
@ -633,57 +651,48 @@ static inline int queue_in_packet(struct amdtp_stream *s)
amdtp_stream_get_max_payload(s), false);
}
static void handle_out_packet(struct amdtp_stream *s, unsigned int syt)
static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks,
unsigned int syt)
{
__be32 *buffer;
unsigned int data_blocks, payload_length;
unsigned int payload_length;
struct snd_pcm_substream *pcm;
if (s->packet_index < 0)
return;
/* this module generate empty packet for 'no data' */
if (!(s->flags & CIP_BLOCKING) || (syt != CIP_SYT_NO_INFO))
data_blocks = calculate_data_blocks(s);
else
data_blocks = 0;
buffer = s->buffer.packets[s->packet_index].buffer;
buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) |
(s->data_block_quadlets << AMDTP_DBS_SHIFT) |
(s->data_block_quadlets << CIP_DBS_SHIFT) |
s->data_block_counter);
buffer[1] = cpu_to_be32(CIP_EOH | CIP_FMT_AM | AMDTP_FDF_AM824 |
(s->sfc << CIP_FDF_SFC_SHIFT) | syt);
(s->sfc << CIP_FDF_SHIFT) | syt);
buffer += 2;
pcm = ACCESS_ONCE(s->pcm);
if (pcm)
s->transfer_samples(s, pcm, buffer, data_blocks);
else
amdtp_fill_pcm_silence(s, buffer, data_blocks);
write_pcm_silence(s, buffer, data_blocks);
if (s->midi_ports)
amdtp_fill_midi(s, buffer, data_blocks);
write_midi_messages(s, buffer, data_blocks);
s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
payload_length = 8 + data_blocks * 4 * s->data_block_quadlets;
if (queue_out_packet(s, payload_length, false) < 0) {
s->packet_index = -1;
amdtp_stream_pcm_abort(s);
return;
}
if (queue_out_packet(s, payload_length, false) < 0)
return -EIO;
if (pcm)
update_pcm_pointers(s, pcm, data_blocks);
/* No need to return the number of handled data blocks. */
return 0;
}
static void handle_in_packet(struct amdtp_stream *s,
unsigned int payload_quadlets,
__be32 *buffer)
static int handle_in_packet(struct amdtp_stream *s,
unsigned int payload_quadlets, __be32 *buffer,
unsigned int *data_blocks)
{
u32 cip_header[2];
unsigned int data_blocks, data_block_quadlets, data_block_counter,
dbc_interval;
unsigned int data_block_quadlets, data_block_counter, dbc_interval;
struct snd_pcm_substream *pcm = NULL;
bool lost;
@ -700,33 +709,34 @@ static void handle_in_packet(struct amdtp_stream *s,
dev_info_ratelimited(&s->unit->device,
"Invalid CIP header for AMDTP: %08X:%08X\n",
cip_header[0], cip_header[1]);
*data_blocks = 0;
goto end;
}
/* Calculate data blocks */
if (payload_quadlets < 3 ||
((cip_header[1] & CIP_FDF_MASK) ==
(AMDTP_FDF_NO_DATA << CIP_FDF_SFC_SHIFT))) {
data_blocks = 0;
(AMDTP_FDF_NO_DATA << CIP_FDF_SHIFT))) {
*data_blocks = 0;
} else {
data_block_quadlets =
(cip_header[0] & AMDTP_DBS_MASK) >> AMDTP_DBS_SHIFT;
(cip_header[0] & CIP_DBS_MASK) >> CIP_DBS_SHIFT;
/* avoid division by zero */
if (data_block_quadlets == 0) {
dev_info_ratelimited(&s->unit->device,
dev_err(&s->unit->device,
"Detect invalid value in dbs field: %08X\n",
cip_header[0]);
goto err;
return -EPROTO;
}
if (s->flags & CIP_WRONG_DBS)
data_block_quadlets = s->data_block_quadlets;
data_blocks = (payload_quadlets - 2) / data_block_quadlets;
*data_blocks = (payload_quadlets - 2) / data_block_quadlets;
}
/* Check data block counter continuity */
data_block_counter = cip_header[0] & AMDTP_DBC_MASK;
if (data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) &&
data_block_counter = cip_header[0] & CIP_DBC_MASK;
if (*data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) &&
s->data_block_counter != UINT_MAX)
data_block_counter = s->data_block_counter;
@ -736,49 +746,46 @@ static void handle_in_packet(struct amdtp_stream *s,
} else if (!(s->flags & CIP_DBC_IS_END_EVENT)) {
lost = data_block_counter != s->data_block_counter;
} else {
if ((data_blocks > 0) && (s->tx_dbc_interval > 0))
if ((*data_blocks > 0) && (s->tx_dbc_interval > 0))
dbc_interval = s->tx_dbc_interval;
else
dbc_interval = data_blocks;
dbc_interval = *data_blocks;
lost = data_block_counter !=
((s->data_block_counter + dbc_interval) & 0xff);
}
if (lost) {
dev_info(&s->unit->device,
"Detect discontinuity of CIP: %02X %02X\n",
s->data_block_counter, data_block_counter);
goto err;
dev_err(&s->unit->device,
"Detect discontinuity of CIP: %02X %02X\n",
s->data_block_counter, data_block_counter);
return -EIO;
}
if (data_blocks > 0) {
if (*data_blocks > 0) {
buffer += 2;
pcm = ACCESS_ONCE(s->pcm);
if (pcm)
s->transfer_samples(s, pcm, buffer, data_blocks);
s->transfer_samples(s, pcm, buffer, *data_blocks);
if (s->midi_ports)
amdtp_pull_midi(s, buffer, data_blocks);
read_midi_messages(s, buffer, *data_blocks);
}
if (s->flags & CIP_DBC_IS_END_EVENT)
s->data_block_counter = data_block_counter;
else
s->data_block_counter =
(data_block_counter + data_blocks) & 0xff;
(data_block_counter + *data_blocks) & 0xff;
end:
if (queue_in_packet(s) < 0)
goto err;
return -EIO;
if (pcm)
update_pcm_pointers(s, pcm, data_blocks);
update_pcm_pointers(s, pcm, *data_blocks);
return;
err:
s->packet_index = -1;
amdtp_stream_pcm_abort(s);
return 0;
}
static void out_stream_callback(struct fw_iso_context *context, u32 cycle,
@ -787,6 +794,10 @@ static void out_stream_callback(struct fw_iso_context *context, u32 cycle,
{
struct amdtp_stream *s = private_data;
unsigned int i, syt, packets = header_length / 4;
unsigned int data_blocks;
if (s->packet_index < 0)
return;
/*
* Compute the cycle of the last queued packet.
@ -797,8 +808,15 @@ static void out_stream_callback(struct fw_iso_context *context, u32 cycle,
for (i = 0; i < packets; ++i) {
syt = calculate_syt(s, ++cycle);
handle_out_packet(s, syt);
data_blocks = calculate_data_blocks(s, syt);
if (handle_out_packet(s, data_blocks, syt) < 0) {
s->packet_index = -1;
amdtp_stream_pcm_abort(s);
return;
}
}
fw_iso_context_queue_flush(s->context);
}
@ -807,32 +825,55 @@ static void in_stream_callback(struct fw_iso_context *context, u32 cycle,
void *private_data)
{
struct amdtp_stream *s = private_data;
unsigned int p, syt, packets, payload_quadlets;
unsigned int p, syt, packets;
unsigned int payload_quadlets, max_payload_quadlets;
unsigned int data_blocks;
__be32 *buffer, *headers = header;
if (s->packet_index < 0)
return;
/* The number of packets in buffer */
packets = header_length / IN_PACKET_HEADER_SIZE;
/* For buffer-over-run prevention. */
max_payload_quadlets = amdtp_stream_get_max_payload(s) / 4;
for (p = 0; p < packets; p++) {
if (s->packet_index < 0)
break;
buffer = s->buffer.packets[s->packet_index].buffer;
/* Process sync slave stream */
if (s->sync_slave && s->sync_slave->callbacked) {
syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK;
handle_out_packet(s->sync_slave, syt);
}
/* The number of quadlets in this packet */
payload_quadlets =
(be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4;
handle_in_packet(s, payload_quadlets, buffer);
if (payload_quadlets > max_payload_quadlets) {
dev_err(&s->unit->device,
"Detect jumbo payload: %02x %02x\n",
payload_quadlets, max_payload_quadlets);
s->packet_index = -1;
break;
}
if (handle_in_packet(s, payload_quadlets, buffer,
&data_blocks) < 0) {
s->packet_index = -1;
break;
}
/* Process sync slave stream */
if (s->sync_slave && s->sync_slave->callbacked) {
syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK;
if (handle_out_packet(s->sync_slave,
data_blocks, syt) < 0) {
s->packet_index = -1;
break;
}
}
}
/* Queueing error or detecting discontinuity */
if (s->packet_index < 0) {
amdtp_stream_pcm_abort(s);
/* Abort sync slave. */
if (s->sync_slave) {
s->sync_slave->packet_index = -1;
@ -872,7 +913,7 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
if (s->direction == AMDTP_IN_STREAM)
context->callback.sc = in_stream_callback;
else if ((s->flags & CIP_BLOCKING) && (s->flags & CIP_SYNC_TO_DEVICE))
else if (s->flags & CIP_SYNC_TO_DEVICE)
context->callback.sc = slave_stream_callback;
else
context->callback.sc = out_stream_callback;
@ -1013,8 +1054,10 @@ EXPORT_SYMBOL(amdtp_stream_pcm_pointer);
*/
void amdtp_stream_update(struct amdtp_stream *s)
{
/* Precomputing. */
ACCESS_ONCE(s->source_node_id_field) =
(fw_parent_device(s->unit)->card->node_id & 0x3f) << 24;
(fw_parent_device(s->unit)->card->node_id << CIP_SID_SHIFT) &
CIP_SID_MASK;
}
EXPORT_SYMBOL(amdtp_stream_update);

View File

@ -29,6 +29,9 @@
* packet is not continuous from an initial value.
* @CIP_EMPTY_HAS_WRONG_DBC: Only for in-stream. The value of dbc in empty
* packet is wrong but the others are correct.
* @CIP_JUMBO_PAYLOAD: Only for in-stream. The number of data blocks in an
* packet is larger than IEC 61883-6 defines. Current implementation
* allows 5 times as large as IEC 61883-6 defines.
*/
enum cip_flags {
CIP_NONBLOCKING = 0x00,
@ -40,6 +43,7 @@ enum cip_flags {
CIP_SKIP_DBC_ZERO_CHECK = 0x20,
CIP_SKIP_INIT_DBC_CHECK = 0x40,
CIP_EMPTY_HAS_WRONG_DBC = 0x80,
CIP_JUMBO_PAYLOAD = 0x100,
};
/**

View File

@ -33,6 +33,7 @@ static DEFINE_MUTEX(devices_mutex);
static DECLARE_BITMAP(devices_used, SNDRV_CARDS);
/* Offsets from information register. */
#define INFO_OFFSET_BEBOB_VERSION 0x08
#define INFO_OFFSET_GUID 0x10
#define INFO_OFFSET_HW_MODEL_ID 0x18
#define INFO_OFFSET_HW_MODEL_REVISION 0x1c
@ -57,6 +58,7 @@ static DECLARE_BITMAP(devices_used, SNDRV_CARDS);
#define VEN_FOCUSRITE 0x0000130e
#define VEN_MAUDIO1 0x00000d6c
#define VEN_MAUDIO2 0x000007f5
#define VEN_DIGIDESIGN 0x00a07e
#define MODEL_FOCUSRITE_SAFFIRE_BOTH 0x00000000
#define MODEL_MAUDIO_AUDIOPHILE_BOTH 0x00010060
@ -72,6 +74,7 @@ name_device(struct snd_bebob *bebob, unsigned int vendor_id)
u32 hw_id;
u32 data[2] = {0};
u32 revision;
u32 version;
int err;
/* get vendor name from root directory */
@ -104,6 +107,12 @@ name_device(struct snd_bebob *bebob, unsigned int vendor_id)
if (err < 0)
goto end;
err = snd_bebob_read_quad(bebob->unit, INFO_OFFSET_BEBOB_VERSION,
&version);
if (err < 0)
goto end;
bebob->version = version;
strcpy(bebob->card->driver, "BeBoB");
strcpy(bebob->card->shortname, model);
strcpy(bebob->card->mixername, model);
@ -364,6 +373,10 @@ static const struct ieee1394_device_id bebob_id_table[] = {
SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00001604, &spec_normal),
/* Behringer, Digital Mixer X32 series (X-UF Card) */
SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00000006, &spec_normal),
/* Behringer, F-Control Audio 1616 */
SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x001616, &spec_normal),
/* Behringer, F-Control Audio 610 */
SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x000610, &spec_normal),
/* Apogee Electronics, Rosetta 200/400 (X-FireWire card) */
/* Apogee Electronics, DA/AD/DD-16X (X-FireWire card) */
SND_BEBOB_DEV_ENTRY(VEN_APOGEE, 0x00010048, &spec_normal),
@ -433,11 +446,11 @@ static const struct ieee1394_device_id bebob_id_table[] = {
/* M-Audio ProjectMix */
SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, MODEL_MAUDIO_PROJECTMIX,
&maudio_special_spec),
/* Digidesign Mbox 2 Pro */
SND_BEBOB_DEV_ENTRY(VEN_DIGIDESIGN, 0x0000a9, &spec_normal),
/* IDs are unknown but able to be supported */
/* Apogee, Mini-ME Firewire */
/* Apogee, Mini-DAC Firewire */
/* Behringer, F-Control Audio 1616 */
/* Behringer, F-Control Audio 610 */
/* Cakawalk, Sonar Power Studio 66 */
/* CME, UF400e */
/* ESI, Quotafire XL */

View File

@ -49,10 +49,15 @@ struct snd_bebob_stream_formation {
extern const unsigned int snd_bebob_rate_table[SND_BEBOB_STRM_FMT_ENTRIES];
/* device specific operations */
#define SND_BEBOB_CLOCK_INTERNAL "Internal"
enum snd_bebob_clock_type {
SND_BEBOB_CLOCK_TYPE_INTERNAL = 0,
SND_BEBOB_CLOCK_TYPE_EXTERNAL,
SND_BEBOB_CLOCK_TYPE_SYT,
};
struct snd_bebob_clock_spec {
unsigned int num;
const char *const *labels;
enum snd_bebob_clock_type *types;
int (*get)(struct snd_bebob *bebob, unsigned int *id);
};
struct snd_bebob_rate_spec {
@ -92,8 +97,7 @@ struct snd_bebob {
struct amdtp_stream rx_stream;
struct cmp_connection out_conn;
struct cmp_connection in_conn;
atomic_t capture_substreams;
atomic_t playback_substreams;
atomic_t substreams_counter;
struct snd_bebob_stream_formation
tx_stream_formations[SND_BEBOB_STRM_FMT_ENTRIES];
@ -110,6 +114,9 @@ struct snd_bebob {
/* for M-Audio special devices */
void *maudio_special_quirk;
bool deferred_registration;
/* For BeBoB version quirk. */
unsigned int version;
};
static inline int
@ -159,7 +166,8 @@ enum avc_bridgeco_plug_type {
AVC_BRIDGECO_PLUG_TYPE_MIDI = 0x02,
AVC_BRIDGECO_PLUG_TYPE_SYNC = 0x03,
AVC_BRIDGECO_PLUG_TYPE_ANA = 0x04,
AVC_BRIDGECO_PLUG_TYPE_DIG = 0x05
AVC_BRIDGECO_PLUG_TYPE_DIG = 0x05,
AVC_BRIDGECO_PLUG_TYPE_ADDITION = 0x06
};
static inline void
avc_bridgeco_fill_unit_addr(u8 buf[AVC_BRIDGECO_ADDR_BYTES],
@ -205,8 +213,8 @@ int avc_bridgeco_get_plug_strm_fmt(struct fw_unit *unit,
/* for AMDTP streaming */
int snd_bebob_stream_get_rate(struct snd_bebob *bebob, unsigned int *rate);
int snd_bebob_stream_set_rate(struct snd_bebob *bebob, unsigned int rate);
int snd_bebob_stream_check_internal_clock(struct snd_bebob *bebob,
bool *internal);
int snd_bebob_stream_get_clock_src(struct snd_bebob *bebob,
enum snd_bebob_clock_type *src);
int snd_bebob_stream_discover(struct snd_bebob *bebob);
int snd_bebob_stream_init_duplex(struct snd_bebob *bebob);
int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate);

View File

@ -103,11 +103,17 @@ saffire_write_quad(struct snd_bebob *bebob, u64 offset, u32 value)
&data, sizeof(__be32), 0);
}
static const char *const saffirepro_10_clk_src_labels[] = {
SND_BEBOB_CLOCK_INTERNAL, "S/PDIF", "Word Clock"
static enum snd_bebob_clock_type saffirepro_10_clk_src_types[] = {
SND_BEBOB_CLOCK_TYPE_INTERNAL,
SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* S/PDIF */
SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* Word Clock */
};
static const char *const saffirepro_26_clk_src_labels[] = {
SND_BEBOB_CLOCK_INTERNAL, "S/PDIF", "ADAT1", "ADAT2", "Word Clock"
static enum snd_bebob_clock_type saffirepro_26_clk_src_types[] = {
SND_BEBOB_CLOCK_TYPE_INTERNAL,
SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* S/PDIF */
SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* ADAT1 */
SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* ADAT2 */
SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* Word Clock */
};
/* Value maps between registers and labels for SaffirePro 10/26. */
static const signed char saffirepro_clk_maps[][SAFFIREPRO_CLOCK_SOURCE_COUNT] = {
@ -178,7 +184,7 @@ saffirepro_both_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
goto end;
/* depending on hardware, use a different mapping */
if (bebob->spec->clock->labels == saffirepro_10_clk_src_labels)
if (bebob->spec->clock->types == saffirepro_10_clk_src_types)
map = saffirepro_clk_maps[0];
else
map = saffirepro_clk_maps[1];
@ -195,8 +201,9 @@ end:
}
struct snd_bebob_spec saffire_le_spec;
static const char *const saffire_both_clk_src_labels[] = {
SND_BEBOB_CLOCK_INTERNAL, "S/PDIF"
static enum snd_bebob_clock_type saffire_both_clk_src_types[] = {
SND_BEBOB_CLOCK_TYPE_INTERNAL,
SND_BEBOB_CLOCK_TYPE_EXTERNAL,
};
static int
saffire_both_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
@ -259,8 +266,8 @@ static struct snd_bebob_rate_spec saffirepro_both_rate_spec = {
};
/* Saffire Pro 26 I/O */
static struct snd_bebob_clock_spec saffirepro_26_clk_spec = {
.num = ARRAY_SIZE(saffirepro_26_clk_src_labels),
.labels = saffirepro_26_clk_src_labels,
.num = ARRAY_SIZE(saffirepro_26_clk_src_types),
.types = saffirepro_26_clk_src_types,
.get = &saffirepro_both_clk_src_get,
};
struct snd_bebob_spec saffirepro_26_spec = {
@ -270,8 +277,8 @@ struct snd_bebob_spec saffirepro_26_spec = {
};
/* Saffire Pro 10 I/O */
static struct snd_bebob_clock_spec saffirepro_10_clk_spec = {
.num = ARRAY_SIZE(saffirepro_10_clk_src_labels),
.labels = saffirepro_10_clk_src_labels,
.num = ARRAY_SIZE(saffirepro_10_clk_src_types),
.types = saffirepro_10_clk_src_types,
.get = &saffirepro_both_clk_src_get,
};
struct snd_bebob_spec saffirepro_10_spec = {
@ -285,8 +292,8 @@ static struct snd_bebob_rate_spec saffire_both_rate_spec = {
.set = &snd_bebob_stream_set_rate,
};
static struct snd_bebob_clock_spec saffire_both_clk_spec = {
.num = ARRAY_SIZE(saffire_both_clk_src_labels),
.labels = saffire_both_clk_src_labels,
.num = ARRAY_SIZE(saffire_both_clk_src_types),
.types = saffire_both_clk_src_types,
.get = &saffire_both_clk_src_get,
};
/* Saffire LE */

View File

@ -340,9 +340,12 @@ end:
}
/* Clock source control for special firmware */
static const char *const special_clk_labels[] = {
SND_BEBOB_CLOCK_INTERNAL " with Digital Mute", "Digital",
"Word Clock", SND_BEBOB_CLOCK_INTERNAL};
static enum snd_bebob_clock_type special_clk_types[] = {
SND_BEBOB_CLOCK_TYPE_INTERNAL, /* With digital mute */
SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* SPDIF/ADAT */
SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* Word Clock */
SND_BEBOB_CLOCK_TYPE_INTERNAL,
};
static int special_clk_get(struct snd_bebob *bebob, unsigned int *id)
{
struct special_params *params = bebob->maudio_special_quirk;
@ -352,7 +355,13 @@ static int special_clk_get(struct snd_bebob *bebob, unsigned int *id)
static int special_clk_ctl_info(struct snd_kcontrol *kctl,
struct snd_ctl_elem_info *einf)
{
return snd_ctl_enum_info(einf, 1, ARRAY_SIZE(special_clk_labels),
static const char *const special_clk_labels[] = {
"Internal with Digital Mute",
"Digital",
"Word Clock",
"Internal"
};
return snd_ctl_enum_info(einf, 1, ARRAY_SIZE(special_clk_types),
special_clk_labels);
}
static int special_clk_ctl_get(struct snd_kcontrol *kctl,
@ -371,7 +380,7 @@ static int special_clk_ctl_put(struct snd_kcontrol *kctl,
int err, id;
id = uval->value.enumerated.item[0];
if (id >= ARRAY_SIZE(special_clk_labels))
if (id >= ARRAY_SIZE(special_clk_types))
return -EINVAL;
mutex_lock(&bebob->mutex);
@ -708,8 +717,8 @@ static struct snd_bebob_rate_spec special_rate_spec = {
.set = &special_set_rate,
};
static struct snd_bebob_clock_spec special_clk_spec = {
.num = ARRAY_SIZE(special_clk_labels),
.labels = special_clk_labels,
.num = ARRAY_SIZE(special_clk_types),
.types = special_clk_types,
.get = &special_clk_get,
};
static struct snd_bebob_meter_spec special_meter_spec = {

View File

@ -17,7 +17,7 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream)
if (err < 0)
goto end;
atomic_inc(&bebob->capture_substreams);
atomic_inc(&bebob->substreams_counter);
err = snd_bebob_stream_start_duplex(bebob, 0);
if (err < 0)
snd_bebob_stream_lock_release(bebob);
@ -34,7 +34,7 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream)
if (err < 0)
goto end;
atomic_inc(&bebob->playback_substreams);
atomic_inc(&bebob->substreams_counter);
err = snd_bebob_stream_start_duplex(bebob, 0);
if (err < 0)
snd_bebob_stream_lock_release(bebob);
@ -46,7 +46,7 @@ static int midi_capture_close(struct snd_rawmidi_substream *substream)
{
struct snd_bebob *bebob = substream->rmidi->private_data;
atomic_dec(&bebob->capture_substreams);
atomic_dec(&bebob->substreams_counter);
snd_bebob_stream_stop_duplex(bebob);
snd_bebob_stream_lock_release(bebob);
@ -57,7 +57,7 @@ static int midi_playback_close(struct snd_rawmidi_substream *substream)
{
struct snd_bebob *bebob = substream->rmidi->private_data;
atomic_dec(&bebob->playback_substreams);
atomic_dec(&bebob->substreams_counter);
snd_bebob_stream_stop_duplex(bebob);
snd_bebob_stream_lock_release(bebob);

View File

@ -157,7 +157,7 @@ pcm_open(struct snd_pcm_substream *substream)
struct snd_bebob *bebob = substream->private_data;
struct snd_bebob_rate_spec *spec = bebob->spec->rate;
unsigned int sampling_rate;
bool internal;
enum snd_bebob_clock_type src;
int err;
err = snd_bebob_stream_lock_try(bebob);
@ -168,7 +168,7 @@ pcm_open(struct snd_pcm_substream *substream)
if (err < 0)
goto err_locked;
err = snd_bebob_stream_check_internal_clock(bebob, &internal);
err = snd_bebob_stream_get_clock_src(bebob, &src);
if (err < 0)
goto err_locked;
@ -176,7 +176,7 @@ pcm_open(struct snd_pcm_substream *substream)
* When source of clock is internal or any PCM stream are running,
* the available sampling rate is limited at current sampling rate.
*/
if (!internal ||
if (src == SND_BEBOB_CLOCK_TYPE_EXTERNAL ||
amdtp_stream_pcm_running(&bebob->tx_stream) ||
amdtp_stream_pcm_running(&bebob->rx_stream)) {
err = spec->get(bebob, &sampling_rate);
@ -213,7 +213,7 @@ pcm_capture_hw_params(struct snd_pcm_substream *substream,
struct snd_bebob *bebob = substream->private_data;
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
atomic_inc(&bebob->capture_substreams);
atomic_inc(&bebob->substreams_counter);
amdtp_stream_set_pcm_format(&bebob->tx_stream,
params_format(hw_params));
return snd_pcm_lib_alloc_vmalloc_buffer(substream,
@ -226,7 +226,7 @@ pcm_playback_hw_params(struct snd_pcm_substream *substream,
struct snd_bebob *bebob = substream->private_data;
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
atomic_inc(&bebob->playback_substreams);
atomic_inc(&bebob->substreams_counter);
amdtp_stream_set_pcm_format(&bebob->rx_stream,
params_format(hw_params));
return snd_pcm_lib_alloc_vmalloc_buffer(substream,
@ -239,7 +239,7 @@ pcm_capture_hw_free(struct snd_pcm_substream *substream)
struct snd_bebob *bebob = substream->private_data;
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
atomic_dec(&bebob->capture_substreams);
atomic_dec(&bebob->substreams_counter);
snd_bebob_stream_stop_duplex(bebob);
@ -251,7 +251,7 @@ pcm_playback_hw_free(struct snd_pcm_substream *substream)
struct snd_bebob *bebob = substream->private_data;
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
atomic_dec(&bebob->playback_substreams);
atomic_dec(&bebob->substreams_counter);
snd_bebob_stream_stop_duplex(bebob);

View File

@ -132,25 +132,27 @@ static void
proc_read_clock(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
static const char *const clk_labels[] = {
"Internal",
"External",
"SYT-Match",
};
struct snd_bebob *bebob = entry->private_data;
struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
unsigned int rate, id;
bool internal;
enum snd_bebob_clock_type src;
unsigned int rate;
if (rate_spec->get(bebob, &rate) >= 0)
snd_iprintf(buffer, "Sampling rate: %d\n", rate);
if (clk_spec) {
if (clk_spec->get(bebob, &id) >= 0)
if (snd_bebob_stream_get_clock_src(bebob, &src) >= 0) {
if (clk_spec)
snd_iprintf(buffer, "Clock Source: %s\n",
clk_spec->labels[id]);
} else {
if (snd_bebob_stream_check_internal_clock(bebob,
&internal) >= 0)
clk_labels[src]);
else
snd_iprintf(buffer, "Clock Source: %s (MSU-dest: %d)\n",
(internal) ? "Internal" : "External",
bebob->sync_input_plug);
clk_labels[src], bebob->sync_input_plug);
}
}

View File

@ -8,7 +8,7 @@
#include "./bebob.h"
#define CALLBACK_TIMEOUT 1000
#define CALLBACK_TIMEOUT 2000
#define FW_ISO_RESOURCE_DELAY 1000
/*
@ -116,16 +116,15 @@ end:
return err;
}
int
snd_bebob_stream_check_internal_clock(struct snd_bebob *bebob, bool *internal)
int snd_bebob_stream_get_clock_src(struct snd_bebob *bebob,
enum snd_bebob_clock_type *src)
{
struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
u8 addr[AVC_BRIDGECO_ADDR_BYTES], input[7];
unsigned int id;
enum avc_bridgeco_plug_type type;
int err = 0;
*internal = false;
/* 1.The device has its own operation to switch source of clock */
if (clk_spec) {
err = clk_spec->get(bebob, &id);
@ -143,10 +142,7 @@ snd_bebob_stream_check_internal_clock(struct snd_bebob *bebob, bool *internal)
goto end;
}
if (strncmp(clk_spec->labels[id], SND_BEBOB_CLOCK_INTERNAL,
strlen(SND_BEBOB_CLOCK_INTERNAL)) == 0)
*internal = true;
*src = clk_spec->types[id];
goto end;
}
@ -155,7 +151,7 @@ snd_bebob_stream_check_internal_clock(struct snd_bebob *bebob, bool *internal)
* to use internal clock always
*/
if (bebob->sync_input_plug < 0) {
*internal = true;
*src = SND_BEBOB_CLOCK_TYPE_INTERNAL;
goto end;
}
@ -178,18 +174,79 @@ snd_bebob_stream_check_internal_clock(struct snd_bebob *bebob, bool *internal)
* Here check the first field. This field is used for direction.
*/
if (input[0] == 0xff) {
*internal = true;
*src = SND_BEBOB_CLOCK_TYPE_INTERNAL;
goto end;
}
/*
* If source of clock is internal CSR, Music Sub Unit Sync Input is
* a destination of Music Sub Unit Sync Output.
*/
*internal = ((input[0] == AVC_BRIDGECO_PLUG_DIR_OUT) &&
(input[1] == AVC_BRIDGECO_PLUG_MODE_SUBUNIT) &&
(input[2] == 0x0c) &&
(input[3] == 0x00));
/* The source from any output plugs is for one purpose only. */
if (input[0] == AVC_BRIDGECO_PLUG_DIR_OUT) {
/*
* In BeBoB architecture, the source from music subunit may
* bypass from oPCR[0]. This means that this source gives
* synchronization to IEEE 1394 cycle start packet.
*/
if (input[1] == AVC_BRIDGECO_PLUG_MODE_SUBUNIT &&
input[2] == 0x0c) {
*src = SND_BEBOB_CLOCK_TYPE_INTERNAL;
goto end;
}
/* The source from any input units is for several purposes. */
} else if (input[1] == AVC_BRIDGECO_PLUG_MODE_UNIT) {
if (input[2] == AVC_BRIDGECO_PLUG_UNIT_ISOC) {
if (input[3] == 0x00) {
/*
* This source comes from iPCR[0]. This means
* that presentation timestamp calculated by
* SYT series of the received packets. In
* short, this driver is the master of
* synchronization.
*/
*src = SND_BEBOB_CLOCK_TYPE_SYT;
goto end;
} else {
/*
* This source comes from iPCR[1-29]. This
* means that the synchronization stream is not
* the Audio/MIDI compound stream.
*/
*src = SND_BEBOB_CLOCK_TYPE_EXTERNAL;
goto end;
}
} else if (input[2] == AVC_BRIDGECO_PLUG_UNIT_EXT) {
/* Check type of this plug. */
avc_bridgeco_fill_unit_addr(addr,
AVC_BRIDGECO_PLUG_DIR_IN,
AVC_BRIDGECO_PLUG_UNIT_EXT,
input[3]);
err = avc_bridgeco_get_plug_type(bebob->unit, addr,
&type);
if (err < 0)
goto end;
if (type == AVC_BRIDGECO_PLUG_TYPE_DIG) {
/*
* SPDIF/ADAT or sometimes (not always) word
* clock.
*/
*src = SND_BEBOB_CLOCK_TYPE_EXTERNAL;
goto end;
} else if (type == AVC_BRIDGECO_PLUG_TYPE_SYNC) {
/* Often word clock. */
*src = SND_BEBOB_CLOCK_TYPE_EXTERNAL;
goto end;
} else if (type == AVC_BRIDGECO_PLUG_TYPE_ADDITION) {
/*
* Not standard.
* Mostly, additional internal clock.
*/
*src = SND_BEBOB_CLOCK_TYPE_INTERNAL;
goto end;
}
}
}
/* Not supported. */
err = -EIO;
end:
return err;
}
@ -417,8 +474,24 @@ destroy_both_connections(struct snd_bebob *bebob)
static int
get_sync_mode(struct snd_bebob *bebob, enum cip_flags *sync_mode)
{
/* currently this module doesn't support SYT-Match mode */
*sync_mode = CIP_SYNC_TO_DEVICE;
enum snd_bebob_clock_type src;
int err;
err = snd_bebob_stream_get_clock_src(bebob, &src);
if (err < 0)
return err;
switch (src) {
case SND_BEBOB_CLOCK_TYPE_INTERNAL:
case SND_BEBOB_CLOCK_TYPE_EXTERNAL:
*sync_mode = CIP_SYNC_TO_DEVICE;
break;
default:
case SND_BEBOB_CLOCK_TYPE_SYT:
*sync_mode = 0;
break;
}
return 0;
}
@ -467,6 +540,17 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
/* See comments in next function */
init_completion(&bebob->bus_reset);
bebob->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK;
/*
* BeBoB v3 transfers packets with these qurks:
* - In the beginning of streaming, the value of dbc is incremented
* even if no data blocks are transferred.
* - The value of dbc is reset suddenly.
*/
if (bebob->version > 2)
bebob->tx_stream.flags |= CIP_EMPTY_HAS_WRONG_DBC |
CIP_SKIP_DBC_ZERO_CHECK;
/*
* At high sampling rate, M-Audio special firmware transmits empty
* packet with the value of dbc incremented by 8 but the others are
@ -490,7 +574,6 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
{
struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
struct amdtp_stream *master, *slave;
atomic_t *slave_substreams;
enum cip_flags sync_mode;
unsigned int curr_rate;
bool updated = false;
@ -515,8 +598,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
mutex_lock(&bebob->mutex);
/* Need no substreams */
if (atomic_read(&bebob->playback_substreams) == 0 &&
atomic_read(&bebob->capture_substreams) == 0)
if (atomic_read(&bebob->substreams_counter) == 0)
goto end;
err = get_sync_mode(bebob, &sync_mode);
@ -525,11 +607,9 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
if (sync_mode == CIP_SYNC_TO_DEVICE) {
master = &bebob->tx_stream;
slave = &bebob->rx_stream;
slave_substreams = &bebob->playback_substreams;
} else {
master = &bebob->rx_stream;
slave = &bebob->tx_stream;
slave_substreams = &bebob->capture_substreams;
}
/*
@ -630,7 +710,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
}
/* start slave if needed */
if (atomic_read(slave_substreams) > 0 && !amdtp_stream_running(slave)) {
if (!amdtp_stream_running(slave)) {
err = start_stream(bebob, slave, rate);
if (err < 0) {
dev_err(&bebob->unit->device,
@ -656,31 +736,25 @@ end:
void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob)
{
struct amdtp_stream *master, *slave;
atomic_t *master_substreams, *slave_substreams;
if (bebob->master == &bebob->rx_stream) {
slave = &bebob->tx_stream;
master = &bebob->rx_stream;
slave_substreams = &bebob->capture_substreams;
master_substreams = &bebob->playback_substreams;
} else {
slave = &bebob->rx_stream;
master = &bebob->tx_stream;
slave_substreams = &bebob->playback_substreams;
master_substreams = &bebob->capture_substreams;
}
mutex_lock(&bebob->mutex);
if (atomic_read(slave_substreams) == 0) {
if (atomic_read(&bebob->substreams_counter) == 0) {
amdtp_stream_pcm_abort(master);
amdtp_stream_stop(master);
amdtp_stream_pcm_abort(slave);
amdtp_stream_stop(slave);
if (atomic_read(master_substreams) == 0) {
amdtp_stream_pcm_abort(master);
amdtp_stream_stop(master);
break_both_connections(bebob);
}
break_both_connections(bebob);
}
mutex_unlock(&bebob->mutex);

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