sound updates for 4.1-rc1

There have been major modernization with the standard bus: in ALSA
 sequencer core and HD-audio.  Also, HD-audio receives the regmap
 support replacing the in-house cache register cache code.  These
 changes shouldn't impact the existing behavior, but rather
 refactoring.
 
 In addition, HD-audio got the code split to a core library part and
 the "legacy" driver parts.  This is a preliminary work for adapting
 the upcoming ASoC HD-audio driver, and the whole transition is still
 work in progress, likely finished in 4.1.
 
 Along with them, there are many updates in ASoC area as usual, too:
 lots of cleanups, Intel code shuffling, etc.
 
 Here are some highlights:
 
 ALSA core:
 - PCM: the audio timestamp / wallclock enhancement
 - PCM: fixes in DPCM management
 - Fixes / cleanups of user-space control element management
 - Sequencer: modernization using the standard bus
 
 HD-audio:
 - Modernization using the standard bus
 - Regmap support
 - Use standard runtime PM for codec power saving
 - Widget-path based power-saving for IDT, VIA and Realtek codecs
 - Reorganized sysfs entries for each codec object
 - More Dell headset support
 
 ASoC:
 - Move of jack registration to the card level
 - Lots of ASoC cleanups, mainly moving things from the CODEC level
   to the card level
 - Support for DAPM routes specified by both the machine driver and DT
 - Continuing improvements to rcar
 - pcm512x enhacements
 - Intel platforms updates
 - rt5670 updates / fixes
 - New platforms / devices: some non-DSP Qualcomm platforms, Google's
   Storm platform, Maxmim MAX98925 CODECs and the Ingenic JZ4780 SoC
 
 Misc:
 - ice1724: Improved ESI W192M support
 - emu10k1: Emu 1010 fixes/enhancement
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iQIcBAABCAAGBQJVLnwvAAoJEGwxgFQ9KSmks3QP/31ObznJTUHLakrUNTk5X09Q
 WJfXjbmQxzhzZ4KmPcc5iQz6sN1eId6lJLFRtX3djGqXHPCv0O8R4i9LaXqHgQHd
 XZaH5KSbpnWUi1n7UD21GuXC+IwnvaUzYDsMBN1CYA3FWX3DjQ/t2fZh+M1ljwr0
 P29ZDNb3zcj1fGuewcWmFk0nDhPoXrtwR+cVf2Z7XxDMlaqn/bWSKcM5Ke5dndjL
 WV8xlmac/HSb+cofgNco21vgJNPGEGuP5X25ESqpzWbtQan+VnAw2kRV7XSULM6j
 MJfJPQvKUb4DxlXDld//VrpBK1vlvdJJNjEQco5V8woA8vHm0AnvmerHnOzv6ISn
 dPQV0FvyH7C2DpIDI+VnvxPWr/d+RulNnWJO2t2HYwcS8BWee3RdeEQ9gzrmMwA2
 NWVSebpqzmn2dX6DyspgrFvpRTQsDlFHKCqzc3dbgELuWN8otIrDQFL2B67MYyjS
 89oOTZOywIH74joNFuh9CflQ1gIXbwjKeXZW/B/NifWckds5rbR9cW2xL/Y0zcoZ
 NZwn/IK/YP/NaOMct4emYDNCTlZ2Q/wFXcOflIvX8yYe5KoNHcahWsb4OiT5bMLc
 9T7APCSSBwy30fDRL22SaLeh9WCGVgJZI8AiqMPYWAXzaDVwbhkeGg9utTb6g+42
 BnDSB1GOtYodtuRLVEG3
 =4z5j
 -----END PGP SIGNATURE-----

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

Pull sound updates from Takashi Iwai:
 "There have been major modernization with the standard bus: in ALSA
  sequencer core and HD-audio.  Also, HD-audio receives the regmap
  support replacing the in-house cache register cache code.  These
  changes shouldn't impact the existing behavior, but rather
  refactoring.

  In addition, HD-audio got the code split to a core library part and
  the "legacy" driver parts.  This is a preliminary work for adapting
  the upcoming ASoC HD-audio driver, and the whole transition is still
  work in progress, likely finished in 4.1.

  Along with them, there are many updates in ASoC area as usual, too:
  lots of cleanups, Intel code shuffling, etc.

  Here are some highlights:

  ALSA core:
   - PCM: the audio timestamp / wallclock enhancement
   - PCM: fixes in DPCM management
   - Fixes / cleanups of user-space control element management
   - Sequencer: modernization using the standard bus

  HD-audio:
   - Modernization using the standard bus
   - Regmap support
   - Use standard runtime PM for codec power saving
   - Widget-path based power-saving for IDT, VIA and Realtek codecs
   - Reorganized sysfs entries for each codec object
   - More Dell headset support

  ASoC:
   - Move of jack registration to the card level
   - Lots of ASoC cleanups, mainly moving things from the CODEC level to
     the card level
   - Support for DAPM routes specified by both the machine driver and DT
   - Continuing improvements to rcar
   - pcm512x enhacements
   - Intel platforms updates
   - rt5670 updates / fixes
   - New platforms / devices: some non-DSP Qualcomm platforms, Google's
     Storm platform, Maxmim MAX98925 CODECs and the Ingenic JZ4780 SoC

  Misc:
   - ice1724: Improved ESI W192M support
   - emu10k1: Emu 1010 fixes/enhancement"

* tag 'sound-4.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (411 commits)
  ALSA: hda - set GET bit when adding a vendor verb to the codec regmap
  ALSA: hda/realtek - Enable the ALC292 dock fixup on the Thinkpad T450
  ALSA: hda - Fix another race in runtime PM refcounting
  ALSA: hda - Expose codec type sysfs
  ALSA: ctl: fix to handle several elements added by one operation for userspace element
  ASoC: Intel: fix array_size.cocci warnings
  ASoC: n810: Automatically disconnect non-connected pins
  ASoC: n810: Consistently pass the card DAPM context to n810_ext_control()
  ASoC: davinci-evm: Use card DAPM context to access widgets
  ASoC: mop500_ab8500: Use card DAPM context to access widgets
  ASoC: wm1133-ev1: Use card DAPM context to access widgets
  ASoC: atmel: Improve machine driver compile test coverage
  ASoC: atmel: Add dependency to SND_SOC_I2C_AND_SPI where necessary
  ALSA: control: Fix a typo of SNDRV_CTL_ELEM_ACCESS_TLV_* with SNDRV_CTL_TLV_OP_*
  ALSA: usb-audio: Don't attempt to get Microsoft Lifecam Cinema sample rate
  ASoC: rnsd: fix build regression without CONFIG_OF
  ALSA: emu10k1: add toggles for E-mu 1010 optical ports
  ALSA: ctl: fill identical information to return value when adding userspace elements
  ALSA: ctl: fix a bug to return no identical information in info operation for userspace controls
  ALSA: ctl: confirm to return all identical information in 'activate' event
  ...
This commit is contained in:
Linus Torvalds 2015-04-15 15:41:41 -07:00
commit d0a3997c0c
315 changed files with 15198 additions and 9045 deletions

View File

@ -1,7 +1,7 @@
Ingenic JZ4740 I2S controller
Required properties:
- compatible : "ingenic,jz4740-i2s"
- compatible : "ingenic,jz4740-i2s" or "ingenic,jz4780-i2s"
- reg : I2S registers location and length
- clocks : AIC and I2S PLL clock specifiers.
- clock-names: "aic" and "i2s"

View File

@ -0,0 +1,22 @@
max98925 audio CODEC
This device supports I2C.
Required properties:
- compatible : "maxim,max98925"
- vmon-slot-no : slot number used to send voltage information
- imon-slot-no : slot number used to send current information
- reg : the I2C address of the device for I2C
Example:
codec: max98925@1a {
compatible = "maxim,max98925";
vmon-slot-no = <0>;
imon-slot-no = <2>;
reg = <0x1a>;
};

View File

@ -18,6 +18,7 @@ Required properties:
* Headphones
* Speakers
* Mic Jack
* Int Mic
- nvidia,i2s-controller : The phandle of the Tegra I2S controller that's
connected to the CODEC.

View File

@ -0,0 +1,43 @@
* Qualcomm Technologies LPASS CPU DAI
This node models the Qualcomm Technologies Low-Power Audio SubSystem (LPASS).
Required properties:
- compatible : "qcom,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"
- interrupts : Must contain an entry for each entry in
interrupt-names.
- interrupt-names : A list which must include the following entries:
* "lpass-irq-lpaif"
- 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:
* "lpass-lpaif"
Optional properties:
- qcom,adsp : Phandle for the audio DSP node
Example:
lpass@28100000 {
compatible = "qcom,lpass-cpu";
clocks = <&lcc AHBIX_CLK>, <&lcc MI2S_OSR_CLK>, <&lcc MI2S_BIT_CLK>;
clock-names = "ahbix-clk", "mi2s-osr-clk", "mi2s-bit-clk";
interrupts = <0 85 1>;
interrupt-names = "lpass-irq-lpaif";
pinctrl-names = "default", "idle";
pinctrl-0 = <&mi2s_default>;
pinctrl-1 = <&mi2s_idle>;
reg = <0x28100000 0x10000>;
reg-names = "lpass-lpaif";
qcom,adsp = <&adsp>;
};

View File

@ -29,9 +29,17 @@ SSI subnode properties:
- shared-pin : if shared clock pin
- pio-transfer : use PIO transfer mode
- no-busif : BUSIF is not ussed when [mem -> SSI] via DMA case
- dma : Should contain Audio DMAC entry
- dma-names : SSI case "rx" (=playback), "tx" (=capture)
SSIU case "rxu" (=playback), "txu" (=capture)
SRC subnode properties:
no properties at this point
- dma : Should contain Audio DMAC entry
- dma-names : "rx" (=playback), "tx" (=capture)
DVC subnode properties:
- dma : Should contain Audio DMAC entry
- dma-names : "tx" (=playback/capture)
DAI subnode properties:
- playback : list of playback modules
@ -45,56 +53,145 @@ rcar_sound: rcar_sound@ec500000 {
reg = <0 0xec500000 0 0x1000>, /* SCU */
<0 0xec5a0000 0 0x100>, /* ADG */
<0 0xec540000 0 0x1000>, /* SSIU */
<0 0xec541000 0 0x1280>; /* SSI */
<0 0xec541000 0 0x1280>, /* SSI */
<0 0xec740000 0 0x200>; /* Audio DMAC peri peri*/
reg-names = "scu", "adg", "ssiu", "ssi", "audmapp";
clocks = <&mstp10_clks R8A7790_CLK_SSI_ALL>,
<&mstp10_clks R8A7790_CLK_SSI9>, <&mstp10_clks R8A7790_CLK_SSI8>,
<&mstp10_clks R8A7790_CLK_SSI7>, <&mstp10_clks R8A7790_CLK_SSI6>,
<&mstp10_clks R8A7790_CLK_SSI5>, <&mstp10_clks R8A7790_CLK_SSI4>,
<&mstp10_clks R8A7790_CLK_SSI3>, <&mstp10_clks R8A7790_CLK_SSI2>,
<&mstp10_clks R8A7790_CLK_SSI1>, <&mstp10_clks R8A7790_CLK_SSI0>,
<&mstp10_clks R8A7790_CLK_SCU_SRC9>, <&mstp10_clks R8A7790_CLK_SCU_SRC8>,
<&mstp10_clks R8A7790_CLK_SCU_SRC7>, <&mstp10_clks R8A7790_CLK_SCU_SRC6>,
<&mstp10_clks R8A7790_CLK_SCU_SRC5>, <&mstp10_clks R8A7790_CLK_SCU_SRC4>,
<&mstp10_clks R8A7790_CLK_SCU_SRC3>, <&mstp10_clks R8A7790_CLK_SCU_SRC2>,
<&mstp10_clks R8A7790_CLK_SCU_SRC1>, <&mstp10_clks R8A7790_CLK_SCU_SRC0>,
<&mstp10_clks R8A7790_CLK_SCU_DVC0>, <&mstp10_clks R8A7790_CLK_SCU_DVC1>,
<&audio_clk_a>, <&audio_clk_b>, <&audio_clk_c>, <&m2_clk>;
clock-names = "ssi-all",
"ssi.9", "ssi.8", "ssi.7", "ssi.6", "ssi.5",
"ssi.4", "ssi.3", "ssi.2", "ssi.1", "ssi.0",
"src.9", "src.8", "src.7", "src.6", "src.5",
"src.4", "src.3", "src.2", "src.1", "src.0",
"dvc.0", "dvc.1",
"clk_a", "clk_b", "clk_c", "clk_i";
rcar_sound,dvc {
dvc0: dvc@0 { };
dvc1: dvc@1 { };
dvc0: dvc@0 {
dmas = <&audma0 0xbc>;
dma-names = "tx";
};
dvc1: dvc@1 {
dmas = <&audma0 0xbe>;
dma-names = "tx";
};
};
rcar_sound,src {
src0: src@0 { };
src1: src@1 { };
src2: src@2 { };
src3: src@3 { };
src4: src@4 { };
src5: src@5 { };
src6: src@6 { };
src7: src@7 { };
src8: src@8 { };
src9: src@9 { };
src0: src@0 {
interrupts = <0 352 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&audma0 0x85>, <&audma1 0x9a>;
dma-names = "rx", "tx";
};
src1: src@1 {
interrupts = <0 353 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&audma0 0x87>, <&audma1 0x9c>;
dma-names = "rx", "tx";
};
src2: src@2 {
interrupts = <0 354 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&audma0 0x89>, <&audma1 0x9e>;
dma-names = "rx", "tx";
};
src3: src@3 {
interrupts = <0 355 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&audma0 0x8b>, <&audma1 0xa0>;
dma-names = "rx", "tx";
};
src4: src@4 {
interrupts = <0 356 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&audma0 0x8d>, <&audma1 0xb0>;
dma-names = "rx", "tx";
};
src5: src@5 {
interrupts = <0 357 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&audma0 0x8f>, <&audma1 0xb2>;
dma-names = "rx", "tx";
};
src6: src@6 {
interrupts = <0 358 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&audma0 0x91>, <&audma1 0xb4>;
dma-names = "rx", "tx";
};
src7: src@7 {
interrupts = <0 359 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&audma0 0x93>, <&audma1 0xb6>;
dma-names = "rx", "tx";
};
src8: src@8 {
interrupts = <0 360 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&audma0 0x95>, <&audma1 0xb8>;
dma-names = "rx", "tx";
};
src9: src@9 {
interrupts = <0 361 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&audma0 0x97>, <&audma1 0xba>;
dma-names = "rx", "tx";
};
};
rcar_sound,ssi {
ssi0: ssi@0 {
interrupts = <0 370 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&audma0 0x01>, <&audma1 0x02>, <&audma0 0x15>, <&audma1 0x16>;
dma-names = "rx", "tx", "rxu", "txu";
};
ssi1: ssi@1 {
interrupts = <0 371 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&audma0 0x03>, <&audma1 0x04>, <&audma0 0x49>, <&audma1 0x4a>;
dma-names = "rx", "tx", "rxu", "txu";
};
ssi2: ssi@2 {
interrupts = <0 372 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&audma0 0x05>, <&audma1 0x06>, <&audma0 0x63>, <&audma1 0x64>;
dma-names = "rx", "tx", "rxu", "txu";
};
ssi3: ssi@3 {
interrupts = <0 373 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&audma0 0x07>, <&audma1 0x08>, <&audma0 0x6f>, <&audma1 0x70>;
dma-names = "rx", "tx", "rxu", "txu";
};
ssi4: ssi@4 {
interrupts = <0 374 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&audma0 0x09>, <&audma1 0x0a>, <&audma0 0x71>, <&audma1 0x72>;
dma-names = "rx", "tx", "rxu", "txu";
};
ssi5: ssi@5 {
interrupts = <0 375 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&audma0 0x0b>, <&audma1 0x0c>, <&audma0 0x73>, <&audma1 0x74>;
dma-names = "rx", "tx", "rxu", "txu";
};
ssi6: ssi@6 {
interrupts = <0 376 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&audma0 0x0d>, <&audma1 0x0e>, <&audma0 0x75>, <&audma1 0x76>;
dma-names = "rx", "tx", "rxu", "txu";
};
ssi7: ssi@7 {
interrupts = <0 377 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&audma0 0x0f>, <&audma1 0x10>, <&audma0 0x79>, <&audma1 0x7a>;
dma-names = "rx", "tx", "rxu", "txu";
};
ssi8: ssi@8 {
interrupts = <0 378 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&audma0 0x11>, <&audma1 0x12>, <&audma0 0x7b>, <&audma1 0x7c>;
dma-names = "rx", "tx", "rxu", "txu";
};
ssi9: ssi@9 {
interrupts = <0 379 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&audma0 0x13>, <&audma1 0x14>, <&audma0 0x7d>, <&audma1 0x7e>;
dma-names = "rx", "tx", "rxu", "txu";
};
};

View File

@ -0,0 +1,67 @@
Renesas Sampling Rate Convert Sound Card:
Renesas Sampling Rate Convert Sound Card specifies audio DAI connections of SoC <-> codec.
Required properties:
- compatible : "renesas,rsrc-card,<board>"
Examples with soctypes are:
- "renesas,rsrc-card,lager"
- "renesas,rsrc-card,koelsch"
Optional properties:
- card_name : User specified audio sound card name, one string
property.
- cpu : CPU sub-node
- codec : CODEC sub-node
Optional subnode properties:
- format : CPU/CODEC common audio format.
"i2s", "right_j", "left_j" , "dsp_a"
"dsp_b", "ac97", "pdm", "msb", "lsb"
- frame-master : Indicates dai-link frame master.
phandle to a cpu or codec subnode.
- bitclock-master : Indicates dai-link bit clock master.
phandle to a cpu or codec subnode.
- bitclock-inversion : bool property. Add this if the
dai-link uses bit clock inversion.
- frame-inversion : bool property. Add this if the
dai-link uses frame clock inversion.
- convert-rate : platform specified sampling rate convert
Required CPU/CODEC subnodes properties:
- sound-dai : phandle and port of CPU/CODEC
Optional CPU/CODEC subnodes properties:
- clocks / system-clock-frequency : specify subnode's clock if needed.
it can be specified via "clocks" if system has
clock node (= common clock), or "system-clock-frequency"
(if system doens't support common clock)
If a clock is specified, it is
enabled with clk_prepare_enable()
in dai startup() and disabled with
clk_disable_unprepare() in dai
shutdown().
Example
sound {
compatible = "renesas,rsrc-card,lager";
card-name = "rsnd-ak4643";
format = "left_j";
bitclock-master = <&sndcodec>;
frame-master = <&sndcodec>;
sndcpu: cpu {
sound-dai = <&rcar_sound>;
};
sndcodec: codec {
sound-dai = <&ak4643>;
system-clock-frequency = <11289600>;
};
};

View File

@ -0,0 +1,23 @@
* Sound complex for Storm boards
Models a soundcard for Storm boards with the Qualcomm Technologies IPQ806x SOC
connected to a MAX98357A DAC via I2S.
Required properties:
- compatible : "google,storm-audio"
- cpu : Phandle of the CPU DAI
- codec : Phandle of the codec DAI
Optional properties:
- qcom,model : The user-visible name of this sound card.
Example:
sound {
compatible = "google,storm-audio";
qcom,model = "ipq806x-storm";
cpu = <&lpass_cpu>;
codec = <&max98357a>;
};

View File

@ -10,6 +10,13 @@ Required properties:
- reg : the I2C address of the device for I2C, the chip select
number for SPI.
- PVDD-supply, DVDD-supply : Power supplies for the device, as covered
in Documentation/devicetree/bindings/regulator/regulator.txt
Optional properties:
- wlf,reset-gpio: A GPIO specifier for the GPIO controlling the reset pin
Example:
codec: wm8804@1a {

View File

@ -71,11 +71,11 @@ SOURCE:
HDMI/DP (either HDMI or DisplayPort)
Exceptions (deprecated):
[Digital] Capture Source
[Digital] Capture Switch (aka input gain switch)
[Digital] Capture Volume (aka input gain volume)
[Digital] Playback Switch (aka output gain switch)
[Digital] Playback Volume (aka output gain volume)
[Analogue|Digital] Capture Source
[Analogue|Digital] Capture Switch (aka input gain switch)
[Analogue|Digital] Capture Volume (aka input gain volume)
[Analogue|Digital] Playback Switch (aka output gain switch)
[Analogue|Digital] Playback Volume (aka output gain volume)
Tone Control - Switch
Tone Control - Bass
Tone Control - Treble

View File

@ -466,7 +466,11 @@ The generic parser supports the following hints:
- add_jack_modes (bool): add "xxx Jack Mode" enum controls to each
I/O jack for allowing to change the headphone amp and mic bias VREF
capabilities
- power_down_unused (bool): power down the unused widgets
- power_save_node (bool): advanced power management for each widget,
controlling the power sate (D0/D3) of each widget node depending on
the actual pin and stream states
- power_down_unused (bool): power down the unused widgets, a subset of
power_save_node, and will be dropped in future
- add_hp_mic (bool): add the headphone to capture source if possible
- hp_mic_detect (bool): enable/disable the hp/mic shared input for a
single built-in mic case; default true

View File

@ -0,0 +1,200 @@
The ALSA API can provide two different system timestamps:
- Trigger_tstamp is the system time snapshot taken when the .trigger
callback is invoked. This snapshot is taken by the ALSA core in the
general case, but specific hardware may have synchronization
capabilities or conversely may only be able to provide a correct
estimate with a delay. In the latter two cases, the low-level driver
is responsible for updating the trigger_tstamp at the most appropriate
and precise moment. Applications should not rely solely on the first
trigger_tstamp but update their internal calculations if the driver
provides a refined estimate with a delay.
- tstamp is the current system timestamp updated during the last
event or application query.
The difference (tstamp - trigger_tstamp) defines the elapsed time.
The ALSA API provides reports two basic pieces of information, avail
and delay, which combined with the trigger and current system
timestamps allow for applications to keep track of the 'fullness' of
the ring buffer and the amount of queued samples.
The use of these different pointers and time information depends on
the application needs:
- 'avail' reports how much can be written in the ring buffer
- 'delay' reports the time it will take to hear a new sample after all
queued samples have been played out.
When timestamps are enabled, the avail/delay information is reported
along with a snapshot of system time. Applications can select from
CLOCK_REALTIME (NTP corrections including going backwards),
CLOCK_MONOTONIC (NTP corrections but never going backwards),
CLOCK_MONOTIC_RAW (without NTP corrections) and change the mode
dynamically with sw_params
The ALSA API also provide an audio_tstamp which reflects the passage
of time as measured by different components of audio hardware. In
ascii-art, this could be represented as follows (for the playback
case):
--------------------------------------------------------------> time
^ ^ ^ ^ ^
| | | | |
analog link dma app FullBuffer
time time time time time
| | | | |
|< codec delay >|<--hw delay-->|<queued samples>|<---avail->|
|<----------------- delay---------------------->| |
|<----ring buffer length---->|
The analog time is taken at the last stage of the playback, as close
as possible to the actual transducer
The link time is taken at the output of the SOC/chipset as the samples
are pushed on a link. The link time can be directly measured if
supported in hardware by sample counters or wallclocks (e.g. with
HDAudio 24MHz or PTP clock for networked solutions) or indirectly
estimated (e.g. with the frame counter in USB).
The DMA time is measured using counters - typically the least reliable
of all measurements due to the bursty natured of DMA transfers.
The app time corresponds to the time tracked by an application after
writing in the ring buffer.
The application can query what the hardware supports, define which
audio time it wants reported by selecting the relevant settings in
audio_tstamp_config fields, get an estimate of the timestamp
accuracy. It can also request the delay-to-analog be included in the
measurement. Direct access to the link time is very interesting on
platforms that provide an embedded DSP; measuring directly the link
time with dedicated hardware, possibly synchronized with system time,
removes the need to keep track of internal DSP processing times and
latency.
In case the application requests an audio tstamp that is not supported
in hardware/low-level driver, the type is overridden as DEFAULT and the
timestamp will report the DMA time based on the hw_pointer value.
For backwards compatibility with previous implementations that did not
provide timestamp selection, with a zero-valued COMPAT timestamp type
the results will default to the HDAudio wall clock for playback
streams and to the DMA time (hw_ptr) in all other cases.
The audio timestamp accuracy can be returned to user-space, so that
appropriate decisions are made:
- for dma time (default), the granularity of the transfers can be
inferred from the steps between updates and in turn provide
information on how much the application pointer can be rewound
safely.
- the link time can be used to track long-term drifts between audio
and system time using the (tstamp-trigger_tstamp)/audio_tstamp
ratio, the precision helps define how much smoothing/low-pass
filtering is required. The link time can be either reset on startup
or reported as is (the latter being useful to compare progress of
different streams - but may require the wallclock to be always
running and not wrap-around during idle periods). If supported in
hardware, the absolute link time could also be used to define a
precise start time (patches WIP)
- including the delay in the audio timestamp may
counter-intuitively not increase the precision of timestamps, e.g. if a
codec includes variable-latency DSP processing or a chain of
hardware components the delay is typically not known with precision.
The accuracy is reported in nanosecond units (using an unsigned 32-bit
word), which gives a max precision of 4.29s, more than enough for
audio applications...
Due to the varied nature of timestamping needs, even for a single
application, the audio_tstamp_config can be changed dynamically. In
the STATUS ioctl, the parameters are read-only and do not allow for
any application selection. To work around this limitation without
impacting legacy applications, a new STATUS_EXT ioctl is introduced
with read/write parameters. ALSA-lib will be modified to make use of
STATUS_EXT and effectively deprecate STATUS.
The ALSA API only allows for a single audio timestamp to be reported
at a time. This is a conscious design decision, reading the audio
timestamps from hardware registers or from IPC takes time, the more
timestamps are read the more imprecise the combined measurements
are. To avoid any interpretation issues, a single (system, audio)
timestamp is reported. Applications that need different timestamps
will be required to issue multiple queries and perform an
interpolation of the results
In some hardware-specific configuration, the system timestamp is
latched by a low-level audio subsytem, and the information provided
back to the driver. Due to potential delays in the communication with
the hardware, there is a risk of misalignment with the avail and delay
information. To make sure applications are not confused, a
driver_timestamp field is added in the snd_pcm_status structure; this
timestamp shows when the information is put together by the driver
before returning from the STATUS and STATUS_EXT ioctl. in most cases
this driver_timestamp will be identical to the regular system tstamp.
Examples of typestamping with HDaudio:
1. DMA timestamp, no compensation for DMA+analog delay
$ ./audio_time -p --ts_type=1
playback: systime: 341121338 nsec, audio time 342000000 nsec, systime delta -878662
playback: systime: 426236663 nsec, audio time 427187500 nsec, systime delta -950837
playback: systime: 597080580 nsec, audio time 598000000 nsec, systime delta -919420
playback: systime: 682059782 nsec, audio time 683020833 nsec, systime delta -961051
playback: systime: 852896415 nsec, audio time 853854166 nsec, systime delta -957751
playback: systime: 937903344 nsec, audio time 938854166 nsec, systime delta -950822
2. DMA timestamp, compensation for DMA+analog delay
$ ./audio_time -p --ts_type=1 -d
playback: systime: 341053347 nsec, audio time 341062500 nsec, systime delta -9153
playback: systime: 426072447 nsec, audio time 426062500 nsec, systime delta 9947
playback: systime: 596899518 nsec, audio time 596895833 nsec, systime delta 3685
playback: systime: 681915317 nsec, audio time 681916666 nsec, systime delta -1349
playback: systime: 852741306 nsec, audio time 852750000 nsec, systime delta -8694
3. link timestamp, compensation for DMA+analog delay
$ ./audio_time -p --ts_type=2 -d
playback: systime: 341060004 nsec, audio time 341062791 nsec, systime delta -2787
playback: systime: 426242074 nsec, audio time 426244875 nsec, systime delta -2801
playback: systime: 597080992 nsec, audio time 597084583 nsec, systime delta -3591
playback: systime: 682084512 nsec, audio time 682088291 nsec, systime delta -3779
playback: systime: 852936229 nsec, audio time 852940916 nsec, systime delta -4687
playback: systime: 938107562 nsec, audio time 938112708 nsec, systime delta -5146
Example 1 shows that the timestamp at the DMA level is close to 1ms
ahead of the actual playback time (as a side time this sort of
measurement can help define rewind safeguards). Compensating for the
DMA-link delay in example 2 helps remove the hardware buffering abut
the information is still very jittery, with up to one sample of
error. In example 3 where the timestamps are measured with the link
wallclock, the timestamps show a monotonic behavior and a lower
dispersion.
Example 3 and 4 are with USB audio class. Example 3 shows a high
offset between audio time and system time due to buffering. Example 4
shows how compensating for the delay exposes a 1ms accuracy (due to
the use of the frame counter by the driver)
Example 3: DMA timestamp, no compensation for delay, delta of ~5ms
$ ./audio_time -p -Dhw:1 -t1
playback: systime: 120174019 nsec, audio time 125000000 nsec, systime delta -4825981
playback: systime: 245041136 nsec, audio time 250000000 nsec, systime delta -4958864
playback: systime: 370106088 nsec, audio time 375000000 nsec, systime delta -4893912
playback: systime: 495040065 nsec, audio time 500000000 nsec, systime delta -4959935
playback: systime: 620038179 nsec, audio time 625000000 nsec, systime delta -4961821
playback: systime: 745087741 nsec, audio time 750000000 nsec, systime delta -4912259
playback: systime: 870037336 nsec, audio time 875000000 nsec, systime delta -4962664
Example 4: DMA timestamp, compensation for delay, delay of ~1ms
$ ./audio_time -p -Dhw:1 -t1 -d
playback: systime: 120190520 nsec, audio time 120000000 nsec, systime delta 190520
playback: systime: 245036740 nsec, audio time 244000000 nsec, systime delta 1036740
playback: systime: 370034081 nsec, audio time 369000000 nsec, systime delta 1034081
playback: systime: 495159907 nsec, audio time 494000000 nsec, systime delta 1159907
playback: systime: 620098824 nsec, audio time 619000000 nsec, systime delta 1098824
playback: systime: 745031847 nsec, audio time 744000000 nsec, systime delta 1031847

View File

@ -1764,7 +1764,7 @@ S: Supported
F: drivers/tty/serial/atmel_serial.c
ATMEL Audio ALSA driver
M: Bo Shen <voice.shen@atmel.com>
M: Nicolas Ferre <nicolas.ferre@atmel.com>
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
S: Supported
F: sound/soc/atmel
@ -5301,6 +5301,13 @@ F: drivers/char/ipmi/
F: include/linux/ipmi*
F: include/uapi/linux/ipmi*
QCOM AUDIO (ASoC) DRIVERS
M: Patrick Lai <plai@codeaurora.org>
M: Banajit Goswami <bgoswami@codeaurora.org>
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
S: Supported
F: sound/soc/qcom/
IPS SCSI RAID DRIVER
M: Adaptec OEM Raid Solutions <aacraid@adaptec.com>
L: linux-scsi@vger.kernel.org

View File

@ -1015,7 +1015,6 @@ static struct asoc_simple_card_info fsi_wm8978_info = {
.platform = "sh_fsi2",
.daifmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM,
.cpu_dai = {
.fmt = SND_SOC_DAIFMT_IB_NF,
.name = "fsia-dai",
},
.codec_dai = {
@ -1040,9 +1039,9 @@ static struct asoc_simple_card_info fsi2_hdmi_info = {
.card = "FSI2B-HDMI",
.codec = "sh-mobile-hdmi",
.platform = "sh_fsi2",
.daifmt = SND_SOC_DAIFMT_CBS_CFS,
.cpu_dai = {
.name = "fsib-dai",
.fmt = SND_SOC_DAIFMT_CBS_CFS,
},
.codec_dai = {
.name = "sh_mobile_hdmi-hifi",

View File

@ -194,6 +194,7 @@ struct dma_chan *of_dma_request_slave_channel(struct device_node *np,
return ERR_PTR(ret_no_channel);
}
EXPORT_SYMBOL_GPL(of_dma_request_slave_channel);
/**
* of_dma_simple_xlate - Simple DMA engine translation function

View File

@ -608,7 +608,9 @@ struct ac97_quirk {
int type; /* quirk type above */
};
int snd_ac97_tune_hardware(struct snd_ac97 *ac97, struct ac97_quirk *quirk, const char *override);
int snd_ac97_tune_hardware(struct snd_ac97 *ac97,
const struct ac97_quirk *quirk,
const char *override);
int snd_ac97_set_rate(struct snd_ac97 *ac97, int reg, unsigned int rate);
/*

View File

@ -70,7 +70,7 @@ struct snd_compr_runtime {
* @device: device pointer
* @direction: stream direction, playback/recording
* @metadata_set: metadata set flag, true when set
* @next_track: has userspace signall next track transistion, true when set
* @next_track: has userspace signal next track transition, true when set
* @private_data: pointer to DSP private data
*/
struct snd_compr_stream {
@ -95,7 +95,7 @@ struct snd_compr_stream {
* and the stream properties
* @get_params: retrieve the codec parameters, mandatory
* @set_metadata: Set the metadata values for a stream
* @get_metadata: retreives the requested metadata values from stream
* @get_metadata: retrieves the requested metadata values from stream
* @trigger: Trigger operations like start, pause, resume, drain, stop.
* This callback is mandatory
* @pointer: Retrieve current h/w pointer information. Mandatory

View File

@ -227,7 +227,7 @@ snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave)
* Add a virtual slave control to the given master.
* Unlike snd_ctl_add_slave(), the element added via this function
* is supposed to have volatile values, and get callback is called
* at each time quried from the master.
* at each time queried from the master.
*
* When the control peeks the hardware values directly and the value
* can be changed by other means than the put callback of the element,

View File

@ -278,7 +278,8 @@ int snd_device_new(struct snd_card *card, enum snd_device_type type,
void *device_data, struct snd_device_ops *ops);
int snd_device_register(struct snd_card *card, void *device_data);
int snd_device_register_all(struct snd_card *card);
int snd_device_disconnect_all(struct snd_card *card);
void snd_device_disconnect(struct snd_card *card, void *device_data);
void snd_device_disconnect_all(struct snd_card *card);
void snd_device_free(struct snd_card *card, void *device_data);
void snd_device_free_all(struct snd_card *card);

217
include/sound/hda_regmap.h Normal file
View File

@ -0,0 +1,217 @@
/*
* HD-audio regmap helpers
*/
#ifndef __SOUND_HDA_REGMAP_H
#define __SOUND_HDA_REGMAP_H
#include <linux/regmap.h>
#include <sound/core.h>
#include <sound/hdaudio.h>
int snd_hdac_regmap_init(struct hdac_device *codec);
void snd_hdac_regmap_exit(struct hdac_device *codec);
int snd_hdac_regmap_add_vendor_verb(struct hdac_device *codec,
unsigned int verb);
int snd_hdac_regmap_read_raw(struct hdac_device *codec, unsigned int reg,
unsigned int *val);
int snd_hdac_regmap_write_raw(struct hdac_device *codec, unsigned int reg,
unsigned int val);
int snd_hdac_regmap_update_raw(struct hdac_device *codec, unsigned int reg,
unsigned int mask, unsigned int val);
/**
* snd_hdac_regmap_encode_verb - encode the verb to a pseudo register
* @nid: widget NID
* @verb: codec verb
*
* Returns an encoded pseudo register.
*/
#define snd_hdac_regmap_encode_verb(nid, verb) \
(((verb) << 8) | 0x80000 | ((unsigned int)(nid) << 20))
/**
* snd_hdac_regmap_encode_amp - encode the AMP verb to a pseudo register
* @nid: widget NID
* @ch: channel (left = 0, right = 1)
* @dir: direction (#HDA_INPUT, #HDA_OUTPUT)
* @idx: input index value
*
* Returns an encoded pseudo register.
*/
#define snd_hdac_regmap_encode_amp(nid, ch, dir, idx) \
(snd_hdac_regmap_encode_verb(nid, AC_VERB_GET_AMP_GAIN_MUTE) | \
((ch) ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT) | \
((dir) == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT) | \
(idx))
/**
* snd_hdac_regmap_encode_amp_stereo - encode a pseudo register for stereo AMPs
* @nid: widget NID
* @dir: direction (#HDA_INPUT, #HDA_OUTPUT)
* @idx: input index value
*
* Returns an encoded pseudo register.
*/
#define snd_hdac_regmap_encode_amp_stereo(nid, dir, idx) \
(snd_hdac_regmap_encode_verb(nid, AC_VERB_GET_AMP_GAIN_MUTE) | \
AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT | /* both bits set! */ \
((dir) == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT) | \
(idx))
/**
* snd_hdac_regmap_write - Write a verb with caching
* @nid: codec NID
* @reg: verb to write
* @val: value to write
*
* For writing an amp value, use snd_hda_regmap_amp_update().
*/
static inline int
snd_hdac_regmap_write(struct hdac_device *codec, hda_nid_t nid,
unsigned int verb, unsigned int val)
{
unsigned int cmd = snd_hdac_regmap_encode_verb(nid, verb);
return snd_hdac_regmap_write_raw(codec, cmd, val);
}
/**
* snd_hda_regmap_update - Update a verb value with caching
* @nid: codec NID
* @verb: verb to update
* @mask: bit mask to update
* @val: value to update
*
* For updating an amp value, use snd_hda_regmap_amp_update().
*/
static inline int
snd_hdac_regmap_update(struct hdac_device *codec, hda_nid_t nid,
unsigned int verb, unsigned int mask,
unsigned int val)
{
unsigned int cmd = snd_hdac_regmap_encode_verb(nid, verb);
return snd_hdac_regmap_update_raw(codec, cmd, mask, val);
}
/**
* snd_hda_regmap_read - Read a verb with caching
* @nid: codec NID
* @verb: verb to read
* @val: pointer to store the value
*
* For reading an amp value, use snd_hda_regmap_get_amp().
*/
static inline int
snd_hdac_regmap_read(struct hdac_device *codec, hda_nid_t nid,
unsigned int verb, unsigned int *val)
{
unsigned int cmd = snd_hdac_regmap_encode_verb(nid, verb);
return snd_hdac_regmap_read_raw(codec, cmd, val);
}
/**
* snd_hdac_regmap_get_amp - Read AMP value
* @codec: HD-audio codec
* @nid: NID to read the AMP value
* @ch: channel (left=0 or right=1)
* @direction: #HDA_INPUT or #HDA_OUTPUT
* @index: the index value (only for input direction)
* @val: the pointer to store the value
*
* Read AMP value. The volume is between 0 to 0x7f, 0x80 = mute bit.
* Returns the value or a negative error.
*/
static inline int
snd_hdac_regmap_get_amp(struct hdac_device *codec, hda_nid_t nid,
int ch, int dir, int idx)
{
unsigned int cmd = snd_hdac_regmap_encode_amp(nid, ch, dir, idx);
int err, val;
err = snd_hdac_regmap_read_raw(codec, cmd, &val);
return err < 0 ? err : val;
}
/**
* snd_hdac_regmap_update_amp - update the AMP value
* @codec: HD-audio codec
* @nid: NID to read the AMP value
* @ch: channel (left=0 or right=1)
* @direction: #HDA_INPUT or #HDA_OUTPUT
* @idx: the index value (only for input direction)
* @mask: bit mask to set
* @val: the bits value to set
*
* Update the AMP value with a bit mask.
* Returns 0 if the value is unchanged, 1 if changed, or a negative error.
*/
static inline int
snd_hdac_regmap_update_amp(struct hdac_device *codec, hda_nid_t nid,
int ch, int dir, int idx, int mask, int val)
{
unsigned int cmd = snd_hdac_regmap_encode_amp(nid, ch, dir, idx);
return snd_hdac_regmap_update_raw(codec, cmd, mask, val);
}
/**
* snd_hdac_regmap_get_amp_stereo - Read stereo AMP values
* @codec: HD-audio codec
* @nid: NID to read the AMP value
* @ch: channel (left=0 or right=1)
* @direction: #HDA_INPUT or #HDA_OUTPUT
* @index: the index value (only for input direction)
* @val: the pointer to store the value
*
* Read stereo AMP values. The lower byte is left, the upper byte is right.
* Returns the value or a negative error.
*/
static inline int
snd_hdac_regmap_get_amp_stereo(struct hdac_device *codec, hda_nid_t nid,
int dir, int idx)
{
unsigned int cmd = snd_hdac_regmap_encode_amp_stereo(nid, dir, idx);
int err, val;
err = snd_hdac_regmap_read_raw(codec, cmd, &val);
return err < 0 ? err : val;
}
/**
* snd_hdac_regmap_update_amp_stereo - update the stereo AMP value
* @codec: HD-audio codec
* @nid: NID to read the AMP value
* @direction: #HDA_INPUT or #HDA_OUTPUT
* @idx: the index value (only for input direction)
* @mask: bit mask to set
* @val: the bits value to set
*
* Update the stereo AMP value with a bit mask.
* The lower byte is left, the upper byte is right.
* Returns 0 if the value is unchanged, 1 if changed, or a negative error.
*/
static inline int
snd_hdac_regmap_update_amp_stereo(struct hdac_device *codec, hda_nid_t nid,
int dir, int idx, int mask, int val)
{
unsigned int cmd = snd_hdac_regmap_encode_amp_stereo(nid, dir, idx);
return snd_hdac_regmap_update_raw(codec, cmd, mask, val);
}
/**
* snd_hdac_regmap_sync_node - sync the widget node attributes
* @codec: HD-audio codec
* @nid: NID to sync
*/
static inline void
snd_hdac_regmap_sync_node(struct hdac_device *codec, hda_nid_t nid)
{
regcache_mark_dirty(codec->regmap);
regcache_sync_region(codec->regmap, nid << 20, ((nid + 1) << 20) - 1);
}
#endif /* __SOUND_HDA_REGMAP_H */

247
include/sound/hdaudio.h Normal file
View File

@ -0,0 +1,247 @@
/*
* HD-audio core stuff
*/
#ifndef __SOUND_HDAUDIO_H
#define __SOUND_HDAUDIO_H
#include <linux/device.h>
#include <sound/hda_verbs.h>
/* codec node id */
typedef u16 hda_nid_t;
struct hdac_bus;
struct hdac_device;
struct hdac_driver;
struct hdac_widget_tree;
/*
* exported bus type
*/
extern struct bus_type snd_hda_bus_type;
/*
* generic arrays
*/
struct snd_array {
unsigned int used;
unsigned int alloced;
unsigned int elem_size;
unsigned int alloc_align;
void *list;
};
/*
* HD-audio codec base device
*/
struct hdac_device {
struct device dev;
int type;
struct hdac_bus *bus;
unsigned int addr; /* codec address */
struct list_head list; /* list point for bus codec_list */
hda_nid_t afg; /* AFG node id */
hda_nid_t mfg; /* MFG node id */
/* ids */
unsigned int vendor_id;
unsigned int subsystem_id;
unsigned int revision_id;
unsigned int afg_function_id;
unsigned int mfg_function_id;
unsigned int afg_unsol:1;
unsigned int mfg_unsol:1;
unsigned int power_caps; /* FG power caps */
const char *vendor_name; /* codec vendor name */
const char *chip_name; /* codec chip name */
/* verb exec op override */
int (*exec_verb)(struct hdac_device *dev, unsigned int cmd,
unsigned int flags, unsigned int *res);
/* widgets */
unsigned int num_nodes;
hda_nid_t start_nid, end_nid;
/* misc flags */
atomic_t in_pm; /* suspend/resume being performed */
/* sysfs */
struct hdac_widget_tree *widgets;
/* regmap */
struct regmap *regmap;
struct snd_array vendor_verbs;
bool lazy_cache:1; /* don't wake up for writes */
bool caps_overwriting:1; /* caps overwrite being in process */
bool cache_coef:1; /* cache COEF read/write too */
};
/* device/driver type used for matching */
enum {
HDA_DEV_CORE,
HDA_DEV_LEGACY,
};
/* direction */
enum {
HDA_INPUT, HDA_OUTPUT
};
#define dev_to_hdac_dev(_dev) container_of(_dev, struct hdac_device, dev)
int snd_hdac_device_init(struct hdac_device *dev, struct hdac_bus *bus,
const char *name, unsigned int addr);
void snd_hdac_device_exit(struct hdac_device *dev);
int snd_hdac_device_register(struct hdac_device *codec);
void snd_hdac_device_unregister(struct hdac_device *codec);
int snd_hdac_refresh_widgets(struct hdac_device *codec);
unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid,
unsigned int verb, unsigned int parm);
int snd_hdac_exec_verb(struct hdac_device *codec, unsigned int cmd,
unsigned int flags, unsigned int *res);
int snd_hdac_read(struct hdac_device *codec, hda_nid_t nid,
unsigned int verb, unsigned int parm, unsigned int *res);
int _snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm,
unsigned int *res);
int snd_hdac_read_parm_uncached(struct hdac_device *codec, hda_nid_t nid,
int parm);
int snd_hdac_override_parm(struct hdac_device *codec, hda_nid_t nid,
unsigned int parm, unsigned int val);
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);
/**
* snd_hdac_read_parm - read a codec parameter
* @codec: the codec object
* @nid: NID to read a parameter
* @parm: parameter to read
*
* Returns -1 for error. If you need to distinguish the error more
* strictly, use _snd_hdac_read_parm() directly.
*/
static inline int snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid,
int parm)
{
unsigned int val;
return _snd_hdac_read_parm(codec, nid, parm, &val) < 0 ? -1 : val;
}
#ifdef CONFIG_PM
void snd_hdac_power_up(struct hdac_device *codec);
void snd_hdac_power_down(struct hdac_device *codec);
void snd_hdac_power_up_pm(struct hdac_device *codec);
void snd_hdac_power_down_pm(struct hdac_device *codec);
#else
static inline void snd_hdac_power_up(struct hdac_device *codec) {}
static inline void snd_hdac_power_down(struct hdac_device *codec) {}
static inline void snd_hdac_power_up_pm(struct hdac_device *codec) {}
static inline void snd_hdac_power_down_pm(struct hdac_device *codec) {}
#endif
/*
* HD-audio codec base driver
*/
struct hdac_driver {
struct device_driver driver;
int type;
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)
/*
* HD-audio bus base driver
*/
struct hdac_bus_ops {
/* send a single command */
int (*command)(struct hdac_bus *bus, unsigned int cmd);
/* get a response from the last command */
int (*get_response)(struct hdac_bus *bus, unsigned int addr,
unsigned int *res);
};
#define HDA_UNSOL_QUEUE_SIZE 64
struct hdac_bus {
struct device *dev;
const struct hdac_bus_ops *ops;
/* codec linked list */
struct list_head codec_list;
unsigned int num_codecs;
/* link caddr -> codec */
struct hdac_device *caddr_tbl[HDA_MAX_CODEC_ADDRESS + 1];
/* unsolicited event queue */
u32 unsol_queue[HDA_UNSOL_QUEUE_SIZE * 2]; /* ring buffer */
unsigned int unsol_rp, unsol_wp;
struct work_struct unsol_work;
/* bit flags of powered codecs */
unsigned long codec_powered;
/* flags */
bool sync_write:1; /* sync after verb write */
/* locks */
struct mutex cmd_mutex;
};
int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
const struct hdac_bus_ops *ops);
void snd_hdac_bus_exit(struct hdac_bus *bus);
int snd_hdac_bus_exec_verb(struct hdac_bus *bus, unsigned int addr,
unsigned int cmd, unsigned int *res);
int snd_hdac_bus_exec_verb_unlocked(struct hdac_bus *bus, unsigned int addr,
unsigned int cmd, unsigned int *res);
void snd_hdac_bus_queue_event(struct hdac_bus *bus, u32 res, u32 res_ex);
int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec);
void snd_hdac_bus_remove_device(struct hdac_bus *bus,
struct hdac_device *codec);
static inline void snd_hdac_codec_link_up(struct hdac_device *codec)
{
set_bit(codec->addr, &codec->bus->codec_powered);
}
static inline void snd_hdac_codec_link_down(struct hdac_device *codec)
{
clear_bit(codec->addr, &codec->bus->codec_powered);
}
/*
* generic array helpers
*/
void *snd_array_new(struct snd_array *array);
void snd_array_free(struct snd_array *array);
static inline void snd_array_init(struct snd_array *array, unsigned int size,
unsigned int align)
{
array->elem_size = size;
array->alloc_align = align;
}
static inline void *snd_array_elem(struct snd_array *array, unsigned int idx)
{
return array->list + idx * array->elem_size;
}
static inline unsigned int snd_array_index(struct snd_array *array, void *ptr)
{
return (unsigned long)(ptr - array->list) / array->elem_size;
}
#endif /* __SOUND_HDAUDIO_H */

View File

@ -60,6 +60,9 @@ struct snd_pcm_hardware {
struct snd_pcm_substream;
struct snd_pcm_audio_tstamp_config; /* definitions further down */
struct snd_pcm_audio_tstamp_report;
struct snd_pcm_ops {
int (*open)(struct snd_pcm_substream *substream);
int (*close)(struct snd_pcm_substream *substream);
@ -71,8 +74,10 @@ struct snd_pcm_ops {
int (*prepare)(struct snd_pcm_substream *substream);
int (*trigger)(struct snd_pcm_substream *substream, int cmd);
snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *substream);
int (*wall_clock)(struct snd_pcm_substream *substream,
struct timespec *audio_ts);
int (*get_time_info)(struct snd_pcm_substream *substream,
struct timespec *system_ts, struct timespec *audio_ts,
struct snd_pcm_audio_tstamp_config *audio_tstamp_config,
struct snd_pcm_audio_tstamp_report *audio_tstamp_report);
int (*copy)(struct snd_pcm_substream *substream, int channel,
snd_pcm_uframes_t pos,
void __user *buf, snd_pcm_uframes_t count);
@ -281,6 +286,58 @@ struct snd_pcm_hw_constraint_ranges {
struct snd_pcm_hwptr_log;
/*
* userspace-provided audio timestamp config to kernel,
* structure is for internal use only and filled with dedicated unpack routine
*/
struct snd_pcm_audio_tstamp_config {
/* 5 of max 16 bits used */
u32 type_requested:4;
u32 report_delay:1; /* add total delay to A/D or D/A */
};
static inline void snd_pcm_unpack_audio_tstamp_config(__u32 data,
struct snd_pcm_audio_tstamp_config *config)
{
config->type_requested = data & 0xF;
config->report_delay = (data >> 4) & 1;
}
/*
* kernel-provided audio timestamp report to user-space
* structure is for internal use only and read by dedicated pack routine
*/
struct snd_pcm_audio_tstamp_report {
/* 6 of max 16 bits used for bit-fields */
/* for backwards compatibility */
u32 valid:1;
/* actual type if hardware could not support requested timestamp */
u32 actual_type:4;
/* accuracy represented in ns units */
u32 accuracy_report:1; /* 0 if accuracy unknown, 1 if accuracy field is valid */
u32 accuracy; /* up to 4.29s, will be packed in separate field */
};
static inline void snd_pcm_pack_audio_tstamp_report(__u32 *data, __u32 *accuracy,
const struct snd_pcm_audio_tstamp_report *report)
{
u32 tmp;
tmp = report->accuracy_report;
tmp <<= 4;
tmp |= report->actual_type;
tmp <<= 1;
tmp |= report->valid;
*data &= 0xffff; /* zero-clear MSBs */
*data |= (tmp << 16);
*accuracy = report->accuracy;
}
struct snd_pcm_runtime {
/* -- Status -- */
struct snd_pcm_substream *trigger_master;
@ -361,6 +418,11 @@ struct snd_pcm_runtime {
struct snd_dma_buffer *dma_buffer_p; /* allocated buffer */
/* -- audio timestamp config -- */
struct snd_pcm_audio_tstamp_config audio_tstamp_config;
struct snd_pcm_audio_tstamp_report audio_tstamp_report;
struct timespec driver_tstamp;
#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
/* -- OSS things -- */
struct snd_pcm_oss_runtime oss;

View File

@ -366,4 +366,11 @@ static inline int params_physical_width(const struct snd_pcm_hw_params *p)
return snd_pcm_format_physical_width(params_format(p));
}
static inline void
params_set_format(struct snd_pcm_hw_params *p, snd_pcm_format_t fmt)
{
snd_mask_set(hw_param_mask(p, SNDRV_PCM_HW_PARAM_FORMAT),
(__force int)fmt);
}
#endif /* __SOUND_PCM_PARAMS_H */

View File

@ -14,6 +14,7 @@
struct rt5670_platform_data {
int jd_mode;
bool in2_diff;
bool dev_gpio;
bool dmic_en;
unsigned int dmic1_data_pin;

View File

@ -25,29 +25,26 @@
* registered device information
*/
#define ID_LEN 32
/* status flag */
#define SNDRV_SEQ_DEVICE_FREE 0
#define SNDRV_SEQ_DEVICE_REGISTERED 1
struct snd_seq_device {
/* device info */
struct snd_card *card; /* sound card */
int device; /* device number */
char id[ID_LEN]; /* driver id */
const char *id; /* driver id */
char name[80]; /* device name */
int argsize; /* size of the argument */
void *driver_data; /* private data for driver */
int status; /* flag - read only */
void *private_data; /* private data for the caller */
void (*private_free)(struct snd_seq_device *device);
struct list_head list; /* link to next device */
struct device dev;
};
#define to_seq_dev(_dev) \
container_of(_dev, struct snd_seq_device, dev)
/* sequencer driver */
/* driver operators
* init_device:
* probe:
* Initialize the device with given parameters.
* Typically,
* 1. call snd_hwdep_new
@ -55,25 +52,40 @@ struct snd_seq_device {
* 3. call snd_hwdep_register
* 4. store the instance to dev->driver_data pointer.
*
* free_device:
* remove:
* Release the private data.
* Typically, call snd_device_free(dev->card, dev->driver_data)
*/
struct snd_seq_dev_ops {
int (*init_device)(struct snd_seq_device *dev);
int (*free_device)(struct snd_seq_device *dev);
struct snd_seq_driver {
struct device_driver driver;
char *id;
int argsize;
};
#define to_seq_drv(_drv) \
container_of(_drv, struct snd_seq_driver, driver)
/*
* prototypes
*/
#ifdef CONFIG_MODULES
void snd_seq_device_load_drivers(void);
int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize, struct snd_seq_device **result);
int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry, int argsize);
int snd_seq_device_unregister_driver(char *id);
#else
#define snd_seq_device_load_drivers()
#endif
int snd_seq_device_new(struct snd_card *card, int device, const char *id,
int argsize, struct snd_seq_device **result);
#define SNDRV_SEQ_DEVICE_ARGPTR(dev) (void *)((char *)(dev) + sizeof(struct snd_seq_device))
int __must_check __snd_seq_driver_register(struct snd_seq_driver *drv,
struct module *mod);
#define snd_seq_driver_register(drv) \
__snd_seq_driver_register(drv, THIS_MODULE)
void snd_seq_driver_unregister(struct snd_seq_driver *drv);
#define module_snd_seq_driver(drv) \
module_driver(drv, snd_seq_driver_register, snd_seq_driver_unregister)
/*
* id strings for generic devices

View File

@ -99,13 +99,9 @@ int snd_seq_event_port_attach(int client, struct snd_seq_port_callback *pcbp,
int snd_seq_event_port_detach(int client, int port);
#ifdef CONFIG_MODULES
void snd_seq_autoload_lock(void);
void snd_seq_autoload_unlock(void);
void snd_seq_autoload_init(void);
#define snd_seq_autoload_exit() snd_seq_autoload_lock()
void snd_seq_autoload_exit(void);
#else
#define snd_seq_autoload_lock()
#define snd_seq_autoload_unlock()
#define snd_seq_autoload_init()
#define snd_seq_autoload_exit()
#endif

View File

@ -16,7 +16,6 @@
struct asoc_simple_dai {
const char *name;
unsigned int fmt;
unsigned int sysclk;
int slots;
int slot_width;

View File

@ -378,6 +378,7 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card);
void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card);
int snd_soc_dapm_new_pcm(struct snd_soc_card *card,
const struct snd_soc_pcm_stream *params,
unsigned int num_params,
struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink);
@ -440,7 +441,6 @@ void dapm_mark_endpoints_dirty(struct snd_soc_card *card);
int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
struct snd_soc_dapm_widget_list **list);
struct snd_soc_codec *snd_soc_dapm_kcontrol_codec(struct snd_kcontrol *kcontrol);
struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm(
struct snd_kcontrol *kcontrol);
@ -531,6 +531,8 @@ struct snd_soc_dapm_widget {
void *priv; /* widget specific data */
struct regulator *regulator; /* attached regulator */
const struct snd_soc_pcm_stream *params; /* params for dai links */
unsigned int num_params; /* number of params for dai links */
unsigned int params_select; /* currently selected param for dai link */
/* dapm control */
int reg; /* negative reg = no direct dapm */
@ -586,8 +588,6 @@ struct snd_soc_dapm_update {
/* DAPM context */
struct snd_soc_dapm_context {
enum snd_soc_bias_level bias_level;
enum snd_soc_bias_level suspend_bias_level;
struct delayed_work delayed_work;
unsigned int idle_bias_off:1; /* Use BIAS_OFF instead of STANDBY */
/* Go to BIAS_OFF in suspend if the DAPM context is idle */
unsigned int suspend_bias_off:1;

View File

@ -135,7 +135,7 @@ void snd_soc_dpcm_be_set_state(struct snd_soc_pcm_runtime *be, int stream,
/* internal use only */
int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute);
int soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd);
void soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd);
int soc_dpcm_runtime_update(struct snd_soc_card *);
int dpcm_path_get(struct snd_soc_pcm_runtime *fe,

View File

@ -450,8 +450,10 @@ int soc_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai);
/* Jack reporting */
int snd_soc_jack_new(struct snd_soc_codec *codec, const char *id, int type,
struct snd_soc_jack *jack);
int snd_soc_card_jack_new(struct snd_soc_card *card, const char *id, int type,
struct snd_soc_jack *jack, struct snd_soc_jack_pin *pins,
unsigned int num_pins);
void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask);
int snd_soc_jack_add_pins(struct snd_soc_jack *jack, int count,
struct snd_soc_jack_pin *pins);
@ -659,7 +661,7 @@ struct snd_soc_jack_gpio {
struct snd_soc_jack {
struct mutex mutex;
struct snd_jack *jack;
struct snd_soc_codec *codec;
struct snd_soc_card *card;
struct list_head pins;
int status;
struct blocking_notifier_head notifier;
@ -941,6 +943,7 @@ struct snd_soc_dai_link {
int be_id; /* optional ID for machine driver BE identification */
const struct snd_soc_pcm_stream *params;
unsigned int num_params;
unsigned int dai_fmt; /* format to set on init */
@ -954,6 +957,9 @@ struct snd_soc_dai_link {
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;
@ -1071,11 +1077,16 @@ struct snd_soc_card {
/*
* Card-specific routes and widgets.
* Note: of_dapm_xxx for Device Tree; Otherwise for driver build-in.
*/
const struct snd_soc_dapm_widget *dapm_widgets;
int num_dapm_widgets;
const struct snd_soc_dapm_route *dapm_routes;
int num_dapm_routes;
const struct snd_soc_dapm_widget *of_dapm_widgets;
int num_of_dapm_widgets;
const struct snd_soc_dapm_route *of_dapm_routes;
int num_of_dapm_routes;
bool fully_routed;
struct work_struct deferred_resume_work;
@ -1258,6 +1269,19 @@ static inline struct snd_soc_dapm_context *snd_soc_component_get_dapm(
return component->dapm_ptr;
}
/**
* snd_soc_dapm_kcontrol_codec() - Returns the codec associated to a kcontrol
* @kcontrol: The kcontrol
*
* This function must only be used on DAPM contexts that are known to be part of
* a CODEC (e.g. in a CODEC driver). Otherwise the behavior is undefined.
*/
static inline struct snd_soc_codec *snd_soc_dapm_kcontrol_codec(
struct snd_kcontrol *kcontrol)
{
return snd_soc_dapm_to_codec(snd_soc_dapm_kcontrol_dapm(kcontrol));
}
/* codec IO */
unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg);
int snd_soc_write(struct snd_soc_codec *codec, unsigned int reg,
@ -1469,7 +1493,7 @@ static inline struct snd_soc_codec *snd_soc_kcontrol_codec(
}
/**
* snd_soc_kcontrol_platform() - Returns the platform that registerd the control
* snd_soc_kcontrol_platform() - Returns the platform that registered the control
* @kcontrol: The control for which to get the platform
*
* Note: This function will only work correctly if the control has been

View File

@ -22,6 +22,7 @@
#ifndef _UAPI__SOUND_ASEQUENCER_H
#define _UAPI__SOUND_ASEQUENCER_H
#include <sound/asound.h>
/** version of the sequencer */
#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION (1, 0, 1)

View File

@ -25,6 +25,9 @@
#include <linux/types.h>
#ifndef __KERNEL__
#include <stdlib.h>
#endif
/*
* protocol version
@ -140,7 +143,7 @@ struct snd_hwdep_dsp_image {
* *
*****************************************************************************/
#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 12)
#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 13)
typedef unsigned long snd_pcm_uframes_t;
typedef signed long snd_pcm_sframes_t;
@ -267,10 +270,17 @@ typedef int __bitwise snd_pcm_subformat_t;
#define SNDRV_PCM_INFO_JOINT_DUPLEX 0x00200000 /* playback and capture stream are somewhat correlated */
#define SNDRV_PCM_INFO_SYNC_START 0x00400000 /* pcm support some kind of sync go */
#define SNDRV_PCM_INFO_NO_PERIOD_WAKEUP 0x00800000 /* period wakeup can be disabled */
#define SNDRV_PCM_INFO_HAS_WALL_CLOCK 0x01000000 /* has audio wall clock for audio/system time sync */
#define SNDRV_PCM_INFO_HAS_WALL_CLOCK 0x01000000 /* (Deprecated)has audio wall clock for audio/system time sync */
#define SNDRV_PCM_INFO_HAS_LINK_ATIME 0x01000000 /* report hardware link audio time, reset on startup */
#define SNDRV_PCM_INFO_HAS_LINK_ABSOLUTE_ATIME 0x02000000 /* report absolute hardware link audio time, not reset on startup */
#define SNDRV_PCM_INFO_HAS_LINK_ESTIMATED_ATIME 0x04000000 /* report estimated link audio time */
#define SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME 0x08000000 /* report synchronized audio/system time */
#define SNDRV_PCM_INFO_DRAIN_TRIGGER 0x40000000 /* internal kernel flag - trigger in drain */
#define SNDRV_PCM_INFO_FIFO_IN_FRAMES 0x80000000 /* internal kernel flag - FIFO size is in frames */
typedef int __bitwise snd_pcm_state_t;
#define SNDRV_PCM_STATE_OPEN ((__force snd_pcm_state_t) 0) /* stream is open */
#define SNDRV_PCM_STATE_SETUP ((__force snd_pcm_state_t) 1) /* stream has a setup */
@ -408,6 +418,22 @@ struct snd_pcm_channel_info {
unsigned int step; /* samples distance in bits */
};
enum {
/*
* first definition for backwards compatibility only,
* maps to wallclock/link time for HDAudio playback and DEFAULT/DMA time for everything else
*/
SNDRV_PCM_AUDIO_TSTAMP_TYPE_COMPAT = 0,
/* timestamp definitions */
SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT = 1, /* DMA time, reported as per hw_ptr */
SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK = 2, /* link time reported by sample or wallclock counter, reset on startup */
SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_ABSOLUTE = 3, /* link time reported by sample or wallclock counter, not reset on startup */
SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_ESTIMATED = 4, /* link time estimated indirectly */
SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED = 5, /* link time synchronized with system time */
SNDRV_PCM_AUDIO_TSTAMP_TYPE_LAST = SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED
};
struct snd_pcm_status {
snd_pcm_state_t state; /* stream state */
struct timespec trigger_tstamp; /* time when stream was started/stopped/paused */
@ -419,9 +445,11 @@ struct snd_pcm_status {
snd_pcm_uframes_t avail_max; /* max frames available on hw since last status */
snd_pcm_uframes_t overrange; /* count of ADC (capture) overrange detections from last status */
snd_pcm_state_t suspended_state; /* suspended stream state */
__u32 reserved_alignment; /* must be filled with zero */
struct timespec audio_tstamp; /* from sample counter or wall clock */
unsigned char reserved[56-sizeof(struct timespec)]; /* must be filled with zero */
__u32 audio_tstamp_data; /* needed for 64-bit alignment, used for configs/report to/from userspace */
struct timespec audio_tstamp; /* sample counter, wall clock, PHC or on-demand sync'ed */
struct timespec driver_tstamp; /* useful in case reference system tstamp is reported with delay */
__u32 audio_tstamp_accuracy; /* in ns units, only valid if indicated in audio_tstamp_data */
unsigned char reserved[52-2*sizeof(struct timespec)]; /* must be filled with zero */
};
struct snd_pcm_mmap_status {
@ -534,6 +562,7 @@ enum {
#define SNDRV_PCM_IOCTL_DELAY _IOR('A', 0x21, snd_pcm_sframes_t)
#define SNDRV_PCM_IOCTL_HWSYNC _IO('A', 0x22)
#define SNDRV_PCM_IOCTL_SYNC_PTR _IOWR('A', 0x23, struct snd_pcm_sync_ptr)
#define SNDRV_PCM_IOCTL_STATUS_EXT _IOWR('A', 0x24, struct snd_pcm_status)
#define SNDRV_PCM_IOCTL_CHANNEL_INFO _IOR('A', 0x32, struct snd_pcm_channel_info)
#define SNDRV_PCM_IOCTL_PREPARE _IO('A', 0x40)
#define SNDRV_PCM_IOCTL_RESET _IO('A', 0x41)

View File

@ -75,7 +75,7 @@ struct snd_compr_tstamp {
/**
* struct snd_compr_avail - avail descriptor
* @avail: Number of bytes available in ring buffer for writing/reading
* @tstamp: timestamp infomation
* @tstamp: timestamp information
*/
struct snd_compr_avail {
__u64 avail;

View File

@ -23,8 +23,7 @@
#define _UAPI__SOUND_EMU10K1_H
#include <linux/types.h>
#include <sound/asound.h>
/*
* ---- FX8010 ----

View File

@ -20,6 +20,12 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifdef __KERNEL__
#include <linux/types.h>
#else
#include <stdint.h>
#endif
/* Maximum channels is 64 even on 56Mode you have 64playbacks to matrix */
#define HDSPM_MAX_CHANNELS 64

View File

@ -76,6 +76,8 @@ source "sound/isa/Kconfig"
source "sound/pci/Kconfig"
source "sound/hda/Kconfig"
source "sound/ppc/Kconfig"
source "sound/aoa/Kconfig"

View File

@ -6,7 +6,7 @@ obj-$(CONFIG_SOUND_PRIME) += sound_firmware.o
obj-$(CONFIG_SOUND_PRIME) += oss/
obj-$(CONFIG_DMASOUND) += oss/
obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ \
firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/
firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/
obj-$(CONFIG_SND_AOA) += aoa/
# This one must be compilable even if sound is configured out

View File

@ -31,7 +31,7 @@ module_param(force, int, 0444);
MODULE_PARM_DESC(force, "Force loading i2sbus even when"
" no layout-id property is present");
static struct of_device_id i2sbus_match[] = {
static const struct of_device_id i2sbus_match[] = {
{ .name = "i2s" },
{ }
};

View File

@ -192,36 +192,41 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask,
EXPORT_SYMBOL(snd_ctl_notify);
/**
* snd_ctl_new - create a control instance from the template
* @control: the control template
* @access: the default control access
* snd_ctl_new - create a new control instance with some elements
* @kctl: the pointer to store new control instance
* @count: the number of elements in this control
* @access: the default access flags for elements in this control
* @file: given when locking these elements
*
* Allocates a new struct snd_kcontrol instance and copies the given template
* to the new instance. It does not copy volatile data (access).
* Allocates a memory object for a new control instance. The instance has
* elements as many as the given number (@count). Each element has given
* access permissions (@access). Each element is locked when @file is given.
*
* Return: The pointer of the new instance, or %NULL on failure.
* Return: 0 on success, error code on failure
*/
static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control,
unsigned int access)
static int snd_ctl_new(struct snd_kcontrol **kctl, unsigned int count,
unsigned int access, struct snd_ctl_file *file)
{
struct snd_kcontrol *kctl;
unsigned int size;
unsigned int idx;
if (snd_BUG_ON(!control || !control->count))
return NULL;
if (count == 0 || count > MAX_CONTROL_COUNT)
return -EINVAL;
if (control->count > MAX_CONTROL_COUNT)
return NULL;
size = sizeof(struct snd_kcontrol);
size += sizeof(struct snd_kcontrol_volatile) * count;
kctl = kzalloc(sizeof(*kctl) + sizeof(struct snd_kcontrol_volatile) * control->count, GFP_KERNEL);
if (kctl == NULL) {
pr_err("ALSA: Cannot allocate control instance\n");
return NULL;
*kctl = kzalloc(size, GFP_KERNEL);
if (!*kctl)
return -ENOMEM;
for (idx = 0; idx < count; idx++) {
(*kctl)->vd[idx].access = access;
(*kctl)->vd[idx].owner = file;
}
*kctl = *control;
for (idx = 0; idx < kctl->count; idx++)
kctl->vd[idx].access = access;
return kctl;
(*kctl)->count = count;
return 0;
}
/**
@ -238,37 +243,53 @@ static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control,
struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol,
void *private_data)
{
struct snd_kcontrol kctl;
struct snd_kcontrol *kctl;
unsigned int count;
unsigned int access;
int err;
if (snd_BUG_ON(!ncontrol || !ncontrol->info))
return NULL;
memset(&kctl, 0, sizeof(kctl));
kctl.id.iface = ncontrol->iface;
kctl.id.device = ncontrol->device;
kctl.id.subdevice = ncontrol->subdevice;
count = ncontrol->count;
if (count == 0)
count = 1;
access = ncontrol->access;
if (access == 0)
access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
access &= (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_VOLATILE |
SNDRV_CTL_ELEM_ACCESS_INACTIVE |
SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND |
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK);
err = snd_ctl_new(&kctl, count, access, NULL);
if (err < 0)
return NULL;
/* The 'numid' member is decided when calling snd_ctl_add(). */
kctl->id.iface = ncontrol->iface;
kctl->id.device = ncontrol->device;
kctl->id.subdevice = ncontrol->subdevice;
if (ncontrol->name) {
strlcpy(kctl.id.name, ncontrol->name, sizeof(kctl.id.name));
if (strcmp(ncontrol->name, kctl.id.name) != 0)
strlcpy(kctl->id.name, ncontrol->name, sizeof(kctl->id.name));
if (strcmp(ncontrol->name, kctl->id.name) != 0)
pr_warn("ALSA: Control name '%s' truncated to '%s'\n",
ncontrol->name, kctl.id.name);
ncontrol->name, kctl->id.name);
}
kctl.id.index = ncontrol->index;
kctl.count = ncontrol->count ? ncontrol->count : 1;
access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
(ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
SNDRV_CTL_ELEM_ACCESS_VOLATILE|
SNDRV_CTL_ELEM_ACCESS_INACTIVE|
SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE|
SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND|
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK));
kctl.info = ncontrol->info;
kctl.get = ncontrol->get;
kctl.put = ncontrol->put;
kctl.tlv.p = ncontrol->tlv.p;
kctl.private_value = ncontrol->private_value;
kctl.private_data = private_data;
return snd_ctl_new(&kctl, access);
kctl->id.index = ncontrol->index;
kctl->info = ncontrol->info;
kctl->get = ncontrol->get;
kctl->put = ncontrol->put;
kctl->tlv.p = ncontrol->tlv.p;
kctl->private_value = ncontrol->private_value;
kctl->private_data = private_data;
return kctl;
}
EXPORT_SYMBOL(snd_ctl_new1);
@ -557,6 +578,7 @@ error:
*
* Finds the control instance with the given id, and activate or
* inactivate the control together with notification, if changed.
* The given ID data is filled with full information.
*
* Return: 0 if unchanged, 1 if changed, or a negative error code on failure.
*/
@ -586,6 +608,7 @@ int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id,
goto unlock;
vd->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
}
snd_ctl_build_ioff(id, kctl, index_offset);
ret = 1;
unlock:
up_write(&card->controls_rwsem);
@ -1006,7 +1029,7 @@ static int snd_ctl_elem_unlock(struct snd_ctl_file *file,
struct user_element {
struct snd_ctl_elem_info info;
struct snd_card *card;
void *elem_data; /* element data */
char *elem_data; /* element data */
unsigned long elem_data_size; /* size of element data in bytes */
void *tlv_data; /* TLV data */
unsigned long tlv_data_size; /* TLV data size */
@ -1017,8 +1040,12 @@ static int snd_ctl_elem_user_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct user_element *ue = kcontrol->private_data;
unsigned int offset;
offset = snd_ctl_get_ioff(kcontrol, &uinfo->id);
*uinfo = ue->info;
snd_ctl_build_ioff(&uinfo->id, kcontrol, offset);
return 0;
}
@ -1028,10 +1055,13 @@ static int snd_ctl_elem_user_enum_info(struct snd_kcontrol *kcontrol,
struct user_element *ue = kcontrol->private_data;
const char *names;
unsigned int item;
unsigned int offset;
item = uinfo->value.enumerated.item;
offset = snd_ctl_get_ioff(kcontrol, &uinfo->id);
*uinfo = ue->info;
snd_ctl_build_ioff(&uinfo->id, kcontrol, offset);
item = min(item, uinfo->value.enumerated.items - 1);
uinfo->value.enumerated.item = item;
@ -1048,9 +1078,12 @@ static int snd_ctl_elem_user_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct user_element *ue = kcontrol->private_data;
unsigned int size = ue->elem_data_size;
char *src = ue->elem_data +
snd_ctl_get_ioff(kcontrol, &ucontrol->id) * size;
mutex_lock(&ue->card->user_ctl_lock);
memcpy(&ucontrol->value, ue->elem_data, ue->elem_data_size);
memcpy(&ucontrol->value, src, size);
mutex_unlock(&ue->card->user_ctl_lock);
return 0;
}
@ -1060,11 +1093,14 @@ static int snd_ctl_elem_user_put(struct snd_kcontrol *kcontrol,
{
int change;
struct user_element *ue = kcontrol->private_data;
unsigned int size = ue->elem_data_size;
char *dst = ue->elem_data +
snd_ctl_get_ioff(kcontrol, &ucontrol->id) * size;
mutex_lock(&ue->card->user_ctl_lock);
change = memcmp(&ucontrol->value, ue->elem_data, ue->elem_data_size) != 0;
change = memcmp(&ucontrol->value, dst, size) != 0;
if (change)
memcpy(ue->elem_data, &ucontrol->value, ue->elem_data_size);
memcpy(dst, &ucontrol->value, size);
mutex_unlock(&ue->card->user_ctl_lock);
return change;
}
@ -1078,7 +1114,7 @@ static int snd_ctl_elem_user_tlv(struct snd_kcontrol *kcontrol,
int change = 0;
void *new_data;
if (op_flag > 0) {
if (op_flag == SNDRV_CTL_TLV_OP_WRITE) {
if (size > 1024 * 128) /* sane value */
return -EINVAL;
@ -1161,84 +1197,103 @@ static void snd_ctl_elem_user_free(struct snd_kcontrol *kcontrol)
static int snd_ctl_elem_add(struct snd_ctl_file *file,
struct snd_ctl_elem_info *info, int replace)
{
/* The capacity of struct snd_ctl_elem_value.value.*/
static const unsigned int value_sizes[] = {
[SNDRV_CTL_ELEM_TYPE_BOOLEAN] = sizeof(long),
[SNDRV_CTL_ELEM_TYPE_INTEGER] = sizeof(long),
[SNDRV_CTL_ELEM_TYPE_ENUMERATED] = sizeof(unsigned int),
[SNDRV_CTL_ELEM_TYPE_BYTES] = sizeof(unsigned char),
[SNDRV_CTL_ELEM_TYPE_IEC958] = sizeof(struct snd_aes_iec958),
[SNDRV_CTL_ELEM_TYPE_INTEGER64] = sizeof(long long),
};
static const unsigned int max_value_counts[] = {
[SNDRV_CTL_ELEM_TYPE_BOOLEAN] = 128,
[SNDRV_CTL_ELEM_TYPE_INTEGER] = 128,
[SNDRV_CTL_ELEM_TYPE_ENUMERATED] = 128,
[SNDRV_CTL_ELEM_TYPE_BYTES] = 512,
[SNDRV_CTL_ELEM_TYPE_IEC958] = 1,
[SNDRV_CTL_ELEM_TYPE_INTEGER64] = 64,
};
struct snd_card *card = file->card;
struct snd_kcontrol kctl, *_kctl;
struct snd_kcontrol *kctl;
unsigned int count;
unsigned int access;
long private_size;
struct user_element *ue;
int idx, err;
unsigned int offset;
int err;
if (info->count < 1)
return -EINVAL;
if (!*info->id.name)
return -EINVAL;
if (strnlen(info->id.name, sizeof(info->id.name)) >= sizeof(info->id.name))
return -EINVAL;
access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
(info->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
SNDRV_CTL_ELEM_ACCESS_INACTIVE|
SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE));
info->id.numid = 0;
memset(&kctl, 0, sizeof(kctl));
/* Delete a control to replace them if needed. */
if (replace) {
info->id.numid = 0;
err = snd_ctl_remove_user_ctl(file, &info->id);
if (err)
return err;
}
if (card->user_ctl_count >= MAX_USER_CONTROLS)
/*
* The number of userspace controls are counted control by control,
* not element by element.
*/
if (card->user_ctl_count + 1 > MAX_USER_CONTROLS)
return -ENOMEM;
memcpy(&kctl.id, &info->id, sizeof(info->id));
kctl.count = info->owner ? info->owner : 1;
access |= SNDRV_CTL_ELEM_ACCESS_USER;
if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED)
kctl.info = snd_ctl_elem_user_enum_info;
else
kctl.info = snd_ctl_elem_user_info;
if (access & SNDRV_CTL_ELEM_ACCESS_READ)
kctl.get = snd_ctl_elem_user_get;
if (access & SNDRV_CTL_ELEM_ACCESS_WRITE)
kctl.put = snd_ctl_elem_user_put;
if (access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) {
kctl.tlv.c = snd_ctl_elem_user_tlv;
/* Check the number of elements for this userspace control. */
count = info->owner;
if (count == 0)
count = 1;
/* Arrange access permissions if needed. */
access = info->access;
if (access == 0)
access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
access &= (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_INACTIVE |
SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE);
if (access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE)
access |= SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
}
switch (info->type) {
case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
case SNDRV_CTL_ELEM_TYPE_INTEGER:
private_size = sizeof(long);
if (info->count > 128)
return -EINVAL;
break;
case SNDRV_CTL_ELEM_TYPE_INTEGER64:
private_size = sizeof(long long);
if (info->count > 64)
return -EINVAL;
break;
case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
private_size = sizeof(unsigned int);
if (info->count > 128 || info->value.enumerated.items == 0)
return -EINVAL;
break;
case SNDRV_CTL_ELEM_TYPE_BYTES:
private_size = sizeof(unsigned char);
if (info->count > 512)
return -EINVAL;
break;
case SNDRV_CTL_ELEM_TYPE_IEC958:
private_size = sizeof(struct snd_aes_iec958);
if (info->count != 1)
return -EINVAL;
break;
default:
access |= SNDRV_CTL_ELEM_ACCESS_USER;
/*
* Check information and calculate the size of data specific to
* this userspace control.
*/
if (info->type < SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
info->type > SNDRV_CTL_ELEM_TYPE_INTEGER64)
return -EINVAL;
}
private_size *= info->count;
ue = kzalloc(sizeof(struct user_element) + private_size, GFP_KERNEL);
if (ue == NULL)
if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED &&
info->value.enumerated.items == 0)
return -EINVAL;
if (info->count < 1 ||
info->count > max_value_counts[info->type])
return -EINVAL;
private_size = value_sizes[info->type] * info->count;
/*
* Keep memory object for this userspace control. After passing this
* code block, the instance should be freed by snd_ctl_free_one().
*
* Note that these elements in this control are locked.
*/
err = snd_ctl_new(&kctl, count, access, file);
if (err < 0)
return err;
memcpy(&kctl->id, &info->id, sizeof(kctl->id));
kctl->private_data = kzalloc(sizeof(struct user_element) + private_size * count,
GFP_KERNEL);
if (kctl->private_data == NULL) {
kfree(kctl);
return -ENOMEM;
}
kctl->private_free = snd_ctl_elem_user_free;
/* Set private data for this userspace control. */
ue = (struct user_element *)kctl->private_data;
ue->card = card;
ue->info = *info;
ue->info.access = 0;
@ -1247,23 +1302,36 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
if (ue->info.type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
err = snd_ctl_elem_init_enum_names(ue);
if (err < 0) {
kfree(ue);
snd_ctl_free_one(kctl);
return err;
}
}
kctl.private_free = snd_ctl_elem_user_free;
_kctl = snd_ctl_new(&kctl, access);
if (_kctl == NULL) {
kfree(ue->priv_data);
kfree(ue);
return -ENOMEM;
}
_kctl->private_data = ue;
for (idx = 0; idx < _kctl->count; idx++)
_kctl->vd[idx].owner = file;
err = snd_ctl_add(card, _kctl);
/* Set callback functions. */
if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED)
kctl->info = snd_ctl_elem_user_enum_info;
else
kctl->info = snd_ctl_elem_user_info;
if (access & SNDRV_CTL_ELEM_ACCESS_READ)
kctl->get = snd_ctl_elem_user_get;
if (access & SNDRV_CTL_ELEM_ACCESS_WRITE)
kctl->put = snd_ctl_elem_user_put;
if (access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE)
kctl->tlv.c = snd_ctl_elem_user_tlv;
/* This function manage to free the instance on failure. */
err = snd_ctl_add(card, kctl);
if (err < 0)
return err;
offset = snd_ctl_get_ioff(kctl, &info->id);
snd_ctl_build_ioff(&info->id, kctl, offset);
/*
* Here we cannot fill any field for the number of elements added by
* this operation because there're no specific fields. The usage of
* 'owner' field for this purpose may cause any bugs to userspace
* applications because the field originally means PID of a process
* which locks the element.
*/
down_write(&card->controls_rwsem);
card->user_ctl_count++;
@ -1276,9 +1344,19 @@ static int snd_ctl_elem_add_user(struct snd_ctl_file *file,
struct snd_ctl_elem_info __user *_info, int replace)
{
struct snd_ctl_elem_info info;
int err;
if (copy_from_user(&info, _info, sizeof(info)))
return -EFAULT;
return snd_ctl_elem_add(file, &info, replace);
err = snd_ctl_elem_add(file, &info, replace);
if (err < 0)
return err;
if (copy_to_user(_info, &info, sizeof(info))) {
snd_ctl_remove_user_ctl(file, &info.id);
return -EFAULT;
}
return 0;
}
static int snd_ctl_elem_remove(struct snd_ctl_file *file,
@ -1338,9 +1416,12 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file,
goto __kctl_end;
}
vd = &kctl->vd[tlv.numid - kctl->id.numid];
if ((op_flag == 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) == 0) ||
(op_flag > 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) == 0) ||
(op_flag < 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND) == 0)) {
if ((op_flag == SNDRV_CTL_TLV_OP_READ &&
(vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) == 0) ||
(op_flag == SNDRV_CTL_TLV_OP_WRITE &&
(vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) == 0) ||
(op_flag == SNDRV_CTL_TLV_OP_CMD &&
(vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND) == 0)) {
err = -ENXIO;
goto __kctl_end;
}
@ -1357,7 +1438,7 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file,
return 0;
}
} else {
if (op_flag) {
if (op_flag != SNDRV_CTL_TLV_OP_READ) {
err = -ENXIO;
goto __kctl_end;
}

View File

@ -50,10 +50,8 @@ int snd_device_new(struct snd_card *card, enum snd_device_type type,
if (snd_BUG_ON(!card || !device_data || !ops))
return -ENXIO;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev == NULL) {
dev_err(card->dev, "Cannot allocate device, type=%d\n", type);
if (!dev)
return -ENOMEM;
}
INIT_LIST_HEAD(&dev->list);
dev->card = card;
dev->type = type;
@ -73,7 +71,7 @@ int snd_device_new(struct snd_card *card, enum snd_device_type type,
}
EXPORT_SYMBOL(snd_device_new);
static int __snd_device_disconnect(struct snd_device *dev)
static void __snd_device_disconnect(struct snd_device *dev)
{
if (dev->state == SNDRV_DEV_REGISTERED) {
if (dev->ops->dev_disconnect &&
@ -81,7 +79,6 @@ static int __snd_device_disconnect(struct snd_device *dev)
dev_err(dev->card->dev, "device disconnect failure\n");
dev->state = SNDRV_DEV_DISCONNECTED;
}
return 0;
}
static void __snd_device_free(struct snd_device *dev)
@ -108,6 +105,34 @@ static struct snd_device *look_for_dev(struct snd_card *card, void *device_data)
return NULL;
}
/**
* snd_device_disconnect - disconnect the device
* @card: the card instance
* @device_data: the data pointer to disconnect
*
* Turns the device into the disconnection state, invoking
* dev_disconnect callback, if the device was already registered.
*
* Usually called from snd_card_disconnect().
*
* Return: Zero if successful, or a negative error code on failure or if the
* device not found.
*/
void snd_device_disconnect(struct snd_card *card, void *device_data)
{
struct snd_device *dev;
if (snd_BUG_ON(!card || !device_data))
return;
dev = look_for_dev(card, device_data);
if (dev)
__snd_device_disconnect(dev);
else
dev_dbg(card->dev, "device disconnect %p (from %pF), not found\n",
device_data, __builtin_return_address(0));
}
EXPORT_SYMBOL_GPL(snd_device_disconnect);
/**
* snd_device_free - release the device from the card
* @card: the card instance
@ -195,18 +220,14 @@ int snd_device_register_all(struct snd_card *card)
* disconnect all the devices on the card.
* called from init.c
*/
int snd_device_disconnect_all(struct snd_card *card)
void snd_device_disconnect_all(struct snd_card *card)
{
struct snd_device *dev;
int err = 0;
if (snd_BUG_ON(!card))
return -ENXIO;
list_for_each_entry_reverse(dev, &card->devices, list) {
if (__snd_device_disconnect(dev) < 0)
err = -ENXIO;
}
return err;
return;
list_for_each_entry_reverse(dev, &card->devices, list)
__snd_device_disconnect(dev);
}
/*

View File

@ -378,10 +378,8 @@ int snd_hwdep_new(struct snd_card *card, char *id, int device,
if (rhwdep)
*rhwdep = NULL;
hwdep = kzalloc(sizeof(*hwdep), GFP_KERNEL);
if (hwdep == NULL) {
dev_err(card->dev, "hwdep: cannot allocate\n");
if (!hwdep)
return -ENOMEM;
}
init_waitqueue_head(&hwdep->open_wait);
mutex_init(&hwdep->open_mutex);

View File

@ -400,7 +400,6 @@ static const struct file_operations snd_shutdown_f_ops =
int snd_card_disconnect(struct snd_card *card)
{
struct snd_monitor_file *mfile;
int err;
if (!card)
return -EINVAL;
@ -445,9 +444,7 @@ int snd_card_disconnect(struct snd_card *card)
#endif
/* notify all devices that we are disconnected */
err = snd_device_disconnect_all(card);
if (err < 0)
dev_err(card->dev, "not all devices for card %i can be disconnected\n", card->number);
snd_device_disconnect_all(card);
snd_info_card_disconnect(card);
if (card->registered) {

View File

@ -1212,10 +1212,8 @@ static void snd_mixer_oss_proc_write(struct snd_info_entry *entry,
/* not changed */
goto __unlock;
tbl = kmalloc(sizeof(*tbl), GFP_KERNEL);
if (! tbl) {
pr_err("ALSA: mixer_oss: no memory\n");
if (!tbl)
goto __unlock;
}
tbl->oss_id = ch;
tbl->name = kstrdup(str, GFP_KERNEL);
if (! tbl->name) {

View File

@ -854,7 +854,6 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
params = kmalloc(sizeof(*params), GFP_KERNEL);
sparams = kmalloc(sizeof(*sparams), GFP_KERNEL);
if (!sw_params || !params || !sparams) {
pcm_dbg(substream->pcm, "No memory\n");
err = -ENOMEM;
goto failure;
}

View File

@ -49,8 +49,6 @@ static struct snd_pcm *snd_pcm_get(struct snd_card *card, int device)
struct snd_pcm *pcm;
list_for_each_entry(pcm, &snd_pcm_devices, list) {
if (pcm->internal)
continue;
if (pcm->card == card && pcm->device == device)
return pcm;
}
@ -62,8 +60,6 @@ static int snd_pcm_next(struct snd_card *card, int device)
struct snd_pcm *pcm;
list_for_each_entry(pcm, &snd_pcm_devices, list) {
if (pcm->internal)
continue;
if (pcm->card == card && pcm->device > device)
return pcm->device;
else if (pcm->card->number > card->number)
@ -76,6 +72,9 @@ static int snd_pcm_add(struct snd_pcm *newpcm)
{
struct snd_pcm *pcm;
if (newpcm->internal)
return 0;
list_for_each_entry(pcm, &snd_pcm_devices, list) {
if (pcm->card == newpcm->card && pcm->device == newpcm->device)
return -EBUSY;
@ -344,11 +343,8 @@ static void snd_pcm_proc_info_read(struct snd_pcm_substream *substream,
return;
info = kmalloc(sizeof(*info), GFP_KERNEL);
if (! info) {
pcm_dbg(substream->pcm,
"snd_pcm_proc_info_read: cannot malloc\n");
if (!info)
return;
}
err = snd_pcm_info(substream, info);
if (err < 0) {
@ -718,10 +714,8 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
prev = NULL;
for (idx = 0, prev = NULL; idx < substream_count; idx++) {
substream = kzalloc(sizeof(*substream), GFP_KERNEL);
if (substream == NULL) {
pcm_err(pcm, "Cannot allocate PCM substream\n");
if (!substream)
return -ENOMEM;
}
substream->pcm = pcm;
substream->pstr = pstr;
substream->number = idx;
@ -775,13 +769,14 @@ static int _snd_pcm_new(struct snd_card *card, const char *id, int device,
if (rpcm)
*rpcm = NULL;
pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
if (pcm == NULL) {
dev_err(card->dev, "Cannot allocate PCM\n");
if (!pcm)
return -ENOMEM;
}
pcm->card = card;
pcm->device = device;
pcm->internal = internal;
mutex_init(&pcm->open_mutex);
init_waitqueue_head(&pcm->open_wait);
INIT_LIST_HEAD(&pcm->list);
if (id)
strlcpy(pcm->id, id, sizeof(pcm->id));
if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)) < 0) {
@ -792,8 +787,6 @@ static int _snd_pcm_new(struct snd_card *card, const char *id, int device,
snd_pcm_free(pcm);
return err;
}
mutex_init(&pcm->open_mutex);
init_waitqueue_head(&pcm->open_wait);
if ((err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)) < 0) {
snd_pcm_free(pcm);
return err;
@ -888,8 +881,9 @@ static int snd_pcm_free(struct snd_pcm *pcm)
if (!pcm)
return 0;
list_for_each_entry(notify, &snd_pcm_notify_list, list) {
notify->n_unregister(pcm);
if (!pcm->internal) {
list_for_each_entry(notify, &snd_pcm_notify_list, list)
notify->n_unregister(pcm);
}
if (pcm->private_free)
pcm->private_free(pcm);
@ -919,6 +913,9 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
if (snd_BUG_ON(!pcm || !rsubstream))
return -ENXIO;
if (snd_BUG_ON(stream != SNDRV_PCM_STREAM_PLAYBACK &&
stream != SNDRV_PCM_STREAM_CAPTURE))
return -EINVAL;
*rsubstream = NULL;
pstr = &pcm->streams[stream];
if (pstr->substream == NULL || pstr->substream_count == 0)
@ -927,25 +924,14 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
card = pcm->card;
prefer_subdevice = snd_ctl_get_preferred_subdevice(card, SND_CTL_SUBDEV_PCM);
switch (stream) {
case SNDRV_PCM_STREAM_PLAYBACK:
if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) {
for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream; substream = substream->next) {
if (SUBSTREAM_BUSY(substream))
return -EAGAIN;
}
if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) {
int opposite = !stream;
for (substream = pcm->streams[opposite].substream; substream;
substream = substream->next) {
if (SUBSTREAM_BUSY(substream))
return -EAGAIN;
}
break;
case SNDRV_PCM_STREAM_CAPTURE:
if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) {
for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) {
if (SUBSTREAM_BUSY(substream))
return -EAGAIN;
}
}
break;
default:
return -EINVAL;
}
if (file->f_flags & O_APPEND) {
@ -968,15 +954,12 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
return 0;
}
if (prefer_subdevice >= 0) {
for (substream = pstr->substream; substream; substream = substream->next)
if (!SUBSTREAM_BUSY(substream) && substream->number == prefer_subdevice)
goto __ok;
}
for (substream = pstr->substream; substream; substream = substream->next)
if (!SUBSTREAM_BUSY(substream))
for (substream = pstr->substream; substream; substream = substream->next) {
if (!SUBSTREAM_BUSY(substream) &&
(prefer_subdevice == -1 ||
substream->number == prefer_subdevice))
break;
__ok:
}
if (substream == NULL)
return -EAGAIN;
@ -1086,15 +1069,16 @@ static int snd_pcm_dev_register(struct snd_device *device)
if (snd_BUG_ON(!device || !device->device_data))
return -ENXIO;
pcm = device->device_data;
if (pcm->internal)
return 0;
mutex_lock(&register_mutex);
err = snd_pcm_add(pcm);
if (err) {
mutex_unlock(&register_mutex);
return err;
}
if (err)
goto unlock;
for (cidx = 0; cidx < 2; cidx++) {
int devtype = -1;
if (pcm->streams[cidx].substream == NULL || pcm->internal)
if (pcm->streams[cidx].substream == NULL)
continue;
switch (cidx) {
case SNDRV_PCM_STREAM_PLAYBACK:
@ -1109,9 +1093,8 @@ static int snd_pcm_dev_register(struct snd_device *device)
&snd_pcm_f_ops[cidx], pcm,
&pcm->streams[cidx].dev);
if (err < 0) {
list_del(&pcm->list);
mutex_unlock(&register_mutex);
return err;
list_del_init(&pcm->list);
goto unlock;
}
for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
@ -1121,8 +1104,9 @@ static int snd_pcm_dev_register(struct snd_device *device)
list_for_each_entry(notify, &snd_pcm_notify_list, list)
notify->n_register(pcm);
unlock:
mutex_unlock(&register_mutex);
return 0;
return err;
}
static int snd_pcm_dev_disconnect(struct snd_device *device)
@ -1133,13 +1117,10 @@ static int snd_pcm_dev_disconnect(struct snd_device *device)
int cidx;
mutex_lock(&register_mutex);
if (list_empty(&pcm->list))
goto unlock;
mutex_lock(&pcm->open_mutex);
wake_up(&pcm->open_wait);
list_del_init(&pcm->list);
for (cidx = 0; cidx < 2; cidx++)
for (cidx = 0; cidx < 2; cidx++) {
for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) {
snd_pcm_stream_lock_irq(substream);
if (substream->runtime) {
@ -1149,18 +1130,20 @@ static int snd_pcm_dev_disconnect(struct snd_device *device)
}
snd_pcm_stream_unlock_irq(substream);
}
list_for_each_entry(notify, &snd_pcm_notify_list, list) {
notify->n_disconnect(pcm);
}
if (!pcm->internal) {
list_for_each_entry(notify, &snd_pcm_notify_list, list)
notify->n_disconnect(pcm);
}
for (cidx = 0; cidx < 2; cidx++) {
snd_unregister_device(&pcm->streams[cidx].dev);
if (!pcm->internal)
snd_unregister_device(&pcm->streams[cidx].dev);
if (pcm->streams[cidx].chmap_kctl) {
snd_ctl_remove(pcm->card, pcm->streams[cidx].chmap_kctl);
pcm->streams[cidx].chmap_kctl = NULL;
}
}
mutex_unlock(&pcm->open_mutex);
unlock:
mutex_unlock(&register_mutex);
return 0;
}

View File

@ -194,18 +194,30 @@ struct snd_pcm_status32 {
u32 avail_max;
u32 overrange;
s32 suspended_state;
u32 reserved_alignment;
u32 audio_tstamp_data;
struct compat_timespec audio_tstamp;
unsigned char reserved[56-sizeof(struct compat_timespec)];
struct compat_timespec driver_tstamp;
u32 audio_tstamp_accuracy;
unsigned char reserved[52-2*sizeof(struct compat_timespec)];
} __attribute__((packed));
static int snd_pcm_status_user_compat(struct snd_pcm_substream *substream,
struct snd_pcm_status32 __user *src)
struct snd_pcm_status32 __user *src,
bool ext)
{
struct snd_pcm_status status;
int err;
memset(&status, 0, sizeof(status));
/*
* with extension, parameters are read/write,
* get audio_tstamp_data from user,
* ignore rest of status structure
*/
if (ext && get_user(status.audio_tstamp_data,
(u32 __user *)(&src->audio_tstamp_data)))
return -EFAULT;
err = snd_pcm_status(substream, &status);
if (err < 0)
return err;
@ -222,7 +234,10 @@ static int snd_pcm_status_user_compat(struct snd_pcm_substream *substream,
put_user(status.avail_max, &src->avail_max) ||
put_user(status.overrange, &src->overrange) ||
put_user(status.suspended_state, &src->suspended_state) ||
compat_put_timespec(&status.audio_tstamp, &src->audio_tstamp))
put_user(status.audio_tstamp_data, &src->audio_tstamp_data) ||
compat_put_timespec(&status.audio_tstamp, &src->audio_tstamp) ||
compat_put_timespec(&status.driver_tstamp, &src->driver_tstamp) ||
put_user(status.audio_tstamp_accuracy, &src->audio_tstamp_accuracy))
return -EFAULT;
return err;
@ -457,6 +472,7 @@ enum {
SNDRV_PCM_IOCTL_HW_PARAMS32 = _IOWR('A', 0x11, struct snd_pcm_hw_params32),
SNDRV_PCM_IOCTL_SW_PARAMS32 = _IOWR('A', 0x13, struct snd_pcm_sw_params32),
SNDRV_PCM_IOCTL_STATUS32 = _IOR('A', 0x20, struct snd_pcm_status32),
SNDRV_PCM_IOCTL_STATUS_EXT32 = _IOWR('A', 0x24, struct snd_pcm_status32),
SNDRV_PCM_IOCTL_DELAY32 = _IOR('A', 0x21, s32),
SNDRV_PCM_IOCTL_CHANNEL_INFO32 = _IOR('A', 0x32, struct snd_pcm_channel_info32),
SNDRV_PCM_IOCTL_REWIND32 = _IOW('A', 0x46, u32),
@ -517,7 +533,9 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
case SNDRV_PCM_IOCTL_SW_PARAMS32:
return snd_pcm_ioctl_sw_params_compat(substream, argp);
case SNDRV_PCM_IOCTL_STATUS32:
return snd_pcm_status_user_compat(substream, argp);
return snd_pcm_status_user_compat(substream, argp, false);
case SNDRV_PCM_IOCTL_STATUS_EXT32:
return snd_pcm_status_user_compat(substream, argp, true);
case SNDRV_PCM_IOCTL_SYNC_PTR32:
return snd_pcm_ioctl_sync_ptr_compat(substream, argp);
case SNDRV_PCM_IOCTL_CHANNEL_INFO32:

View File

@ -289,7 +289,7 @@ EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_request_channel);
*
* The function should usually be called from the pcm open callback. Note that
* this function will use private_data field of the substream's runtime. So it
* is not availabe to your pcm driver implementation.
* is not available to your pcm driver implementation.
*/
int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream,
struct dma_chan *chan)
@ -328,7 +328,7 @@ EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open);
* This function will request a DMA channel using the passed filter function and
* data. The function should usually be called from the pcm open callback. Note
* that this function will use private_data field of the substream's runtime. So
* it is not availabe to your pcm driver implementation.
* it is not available to your pcm driver implementation.
*/
int snd_dmaengine_pcm_open_request_chan(struct snd_pcm_substream *substream,
dma_filter_fn filter_fn, void *filter_data)

View File

@ -232,6 +232,49 @@ int snd_pcm_update_state(struct snd_pcm_substream *substream,
return 0;
}
static void update_audio_tstamp(struct snd_pcm_substream *substream,
struct timespec *curr_tstamp,
struct timespec *audio_tstamp)
{
struct snd_pcm_runtime *runtime = substream->runtime;
u64 audio_frames, audio_nsecs;
struct timespec driver_tstamp;
if (runtime->tstamp_mode != SNDRV_PCM_TSTAMP_ENABLE)
return;
if (!(substream->ops->get_time_info) ||
(runtime->audio_tstamp_report.actual_type ==
SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT)) {
/*
* provide audio timestamp derived from pointer position
* add delay only if requested
*/
audio_frames = runtime->hw_ptr_wrap + runtime->status->hw_ptr;
if (runtime->audio_tstamp_config.report_delay) {
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
audio_frames -= runtime->delay;
else
audio_frames += runtime->delay;
}
audio_nsecs = div_u64(audio_frames * 1000000000LL,
runtime->rate);
*audio_tstamp = ns_to_timespec(audio_nsecs);
}
runtime->status->audio_tstamp = *audio_tstamp;
runtime->status->tstamp = *curr_tstamp;
/*
* re-take a driver timestamp to let apps detect if the reference tstamp
* read by low-level hardware was provided with a delay
*/
snd_pcm_gettime(substream->runtime, (struct timespec *)&driver_tstamp);
runtime->driver_tstamp = driver_tstamp;
}
static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
unsigned int in_interrupt)
{
@ -256,11 +299,18 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
pos = substream->ops->pointer(substream);
curr_jiffies = jiffies;
if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp);
if ((substream->ops->get_time_info) &&
(runtime->audio_tstamp_config.type_requested != SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT)) {
substream->ops->get_time_info(substream, &curr_tstamp,
&audio_tstamp,
&runtime->audio_tstamp_config,
&runtime->audio_tstamp_report);
if ((runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK) &&
(substream->ops->wall_clock))
substream->ops->wall_clock(substream, &audio_tstamp);
/* re-test in case tstamp type is not supported in hardware and was demoted to DEFAULT */
if (runtime->audio_tstamp_report.actual_type == SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT)
snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp);
} else
snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp);
}
if (pos == SNDRV_PCM_POS_XRUN) {
@ -403,8 +453,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
}
no_delta_check:
if (runtime->status->hw_ptr == new_hw_ptr)
if (runtime->status->hw_ptr == new_hw_ptr) {
update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp);
return 0;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
runtime->silence_size > 0)
@ -426,30 +478,8 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
snd_BUG_ON(crossed_boundary != 1);
runtime->hw_ptr_wrap += runtime->boundary;
}
if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
runtime->status->tstamp = curr_tstamp;
if (!(runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK)) {
/*
* no wall clock available, provide audio timestamp
* derived from pointer position+delay
*/
u64 audio_frames, audio_nsecs;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
audio_frames = runtime->hw_ptr_wrap
+ runtime->status->hw_ptr
- runtime->delay;
else
audio_frames = runtime->hw_ptr_wrap
+ runtime->status->hw_ptr
+ runtime->delay;
audio_nsecs = div_u64(audio_frames * 1000000000LL,
runtime->rate);
audio_tstamp = ns_to_timespec(audio_nsecs);
}
runtime->status->audio_tstamp = audio_tstamp;
}
update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp);
return snd_pcm_update_state(substream, runtime);
}

View File

@ -707,6 +707,23 @@ int snd_pcm_status(struct snd_pcm_substream *substream,
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_stream_lock_irq(substream);
snd_pcm_unpack_audio_tstamp_config(status->audio_tstamp_data,
&runtime->audio_tstamp_config);
/* backwards compatible behavior */
if (runtime->audio_tstamp_config.type_requested ==
SNDRV_PCM_AUDIO_TSTAMP_TYPE_COMPAT) {
if (runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK)
runtime->audio_tstamp_config.type_requested =
SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK;
else
runtime->audio_tstamp_config.type_requested =
SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT;
runtime->audio_tstamp_report.valid = 0;
} else
runtime->audio_tstamp_report.valid = 1;
status->state = runtime->status->state;
status->suspended_state = runtime->status->suspended_state;
if (status->state == SNDRV_PCM_STATE_OPEN)
@ -716,8 +733,15 @@ int snd_pcm_status(struct snd_pcm_substream *substream,
snd_pcm_update_hw_ptr(substream);
if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
status->tstamp = runtime->status->tstamp;
status->driver_tstamp = runtime->driver_tstamp;
status->audio_tstamp =
runtime->status->audio_tstamp;
if (runtime->audio_tstamp_report.valid == 1)
/* backwards compatibility, no report provided in COMPAT mode */
snd_pcm_pack_audio_tstamp_report(&status->audio_tstamp_data,
&status->audio_tstamp_accuracy,
&runtime->audio_tstamp_report);
goto _tstamp_end;
}
} else {
@ -753,12 +777,21 @@ int snd_pcm_status(struct snd_pcm_substream *substream,
}
static int snd_pcm_status_user(struct snd_pcm_substream *substream,
struct snd_pcm_status __user * _status)
struct snd_pcm_status __user * _status,
bool ext)
{
struct snd_pcm_status status;
int res;
memset(&status, 0, sizeof(status));
/*
* with extension, parameters are read/write,
* get audio_tstamp_data from user,
* ignore rest of status structure
*/
if (ext && get_user(status.audio_tstamp_data,
(u32 __user *)(&_status->audio_tstamp_data)))
return -EFAULT;
res = snd_pcm_status(substream, &status);
if (res < 0)
return res;
@ -2725,7 +2758,9 @@ static int snd_pcm_common_ioctl1(struct file *file,
case SNDRV_PCM_IOCTL_SW_PARAMS:
return snd_pcm_sw_params_user(substream, arg);
case SNDRV_PCM_IOCTL_STATUS:
return snd_pcm_status_user(substream, arg);
return snd_pcm_status_user(substream, arg, false);
case SNDRV_PCM_IOCTL_STATUS_EXT:
return snd_pcm_status_user(substream, arg, true);
case SNDRV_PCM_IOCTL_CHANNEL_INFO:
return snd_pcm_channel_info_user(substream, arg);
case SNDRV_PCM_IOCTL_PREPARE:

View File

@ -1429,10 +1429,8 @@ static int snd_rawmidi_alloc_substreams(struct snd_rawmidi *rmidi,
for (idx = 0; idx < count; idx++) {
substream = kzalloc(sizeof(*substream), GFP_KERNEL);
if (substream == NULL) {
rmidi_err(rmidi, "rawmidi: cannot allocate substream\n");
if (!substream)
return -ENOMEM;
}
substream->stream = direction;
substream->number = idx;
substream->rmidi = rmidi;
@ -1479,10 +1477,8 @@ int snd_rawmidi_new(struct snd_card *card, char *id, int device,
if (rrawmidi)
*rrawmidi = NULL;
rmidi = kzalloc(sizeof(*rmidi), GFP_KERNEL);
if (rmidi == NULL) {
dev_err(card->dev, "rawmidi: cannot allocate\n");
if (!rmidi)
return -ENOMEM;
}
rmidi->card = card;
rmidi->device = device;
mutex_init(&rmidi->open_mutex);

View File

@ -65,15 +65,20 @@ static unsigned int odev_poll(struct file *file, poll_table * wait);
* module interface
*/
static struct snd_seq_driver seq_oss_synth_driver = {
.driver = {
.name = KBUILD_MODNAME,
.probe = snd_seq_oss_synth_probe,
.remove = snd_seq_oss_synth_remove,
},
.id = SNDRV_SEQ_DEV_ID_OSS,
.argsize = sizeof(struct snd_seq_oss_reg),
};
static int __init alsa_seq_oss_init(void)
{
int rc;
static struct snd_seq_dev_ops ops = {
snd_seq_oss_synth_register,
snd_seq_oss_synth_unregister,
};
snd_seq_autoload_lock();
if ((rc = register_device()) < 0)
goto error;
if ((rc = register_proc()) < 0) {
@ -86,8 +91,8 @@ static int __init alsa_seq_oss_init(void)
goto error;
}
if ((rc = snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OSS, &ops,
sizeof(struct snd_seq_oss_reg))) < 0) {
rc = snd_seq_driver_register(&seq_oss_synth_driver);
if (rc < 0) {
snd_seq_oss_delete_client();
unregister_proc();
unregister_device();
@ -98,13 +103,12 @@ static int __init alsa_seq_oss_init(void)
snd_seq_oss_synth_init();
error:
snd_seq_autoload_unlock();
return rc;
}
static void __exit alsa_seq_oss_exit(void)
{
snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OSS);
snd_seq_driver_unregister(&seq_oss_synth_driver);
snd_seq_oss_delete_client();
unregister_proc();
unregister_device();

View File

@ -188,10 +188,8 @@ snd_seq_oss_open(struct file *file, int level)
struct seq_oss_devinfo *dp;
dp = kzalloc(sizeof(*dp), GFP_KERNEL);
if (!dp) {
pr_err("ALSA: seq_oss: can't malloc device info\n");
if (!dp)
return -ENOMEM;
}
dp->cseq = system_client;
dp->port = -1;

View File

@ -173,10 +173,9 @@ snd_seq_oss_midi_check_new_port(struct snd_seq_port_info *pinfo)
/*
* allocate midi info record
*/
if ((mdev = kzalloc(sizeof(*mdev), GFP_KERNEL)) == NULL) {
pr_err("ALSA: seq_oss: can't malloc midi info\n");
mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
if (!mdev)
return -ENOMEM;
}
/* copy the port information */
mdev->client = pinfo->addr.client;

View File

@ -47,13 +47,12 @@ snd_seq_oss_readq_new(struct seq_oss_devinfo *dp, int maxlen)
{
struct seq_oss_readq *q;
if ((q = kzalloc(sizeof(*q), GFP_KERNEL)) == NULL) {
pr_err("ALSA: seq_oss: can't malloc read queue\n");
q = kzalloc(sizeof(*q), GFP_KERNEL);
if (!q)
return NULL;
}
if ((q->q = kcalloc(maxlen, sizeof(union evrec), GFP_KERNEL)) == NULL) {
pr_err("ALSA: seq_oss: can't malloc read queue buffer\n");
q->q = kcalloc(maxlen, sizeof(union evrec), GFP_KERNEL);
if (!q->q) {
kfree(q);
return NULL;
}

View File

@ -98,17 +98,17 @@ snd_seq_oss_synth_init(void)
* registration of the synth device
*/
int
snd_seq_oss_synth_register(struct snd_seq_device *dev)
snd_seq_oss_synth_probe(struct device *_dev)
{
struct snd_seq_device *dev = to_seq_dev(_dev);
int i;
struct seq_oss_synth *rec;
struct snd_seq_oss_reg *reg = SNDRV_SEQ_DEVICE_ARGPTR(dev);
unsigned long flags;
if ((rec = kzalloc(sizeof(*rec), GFP_KERNEL)) == NULL) {
pr_err("ALSA: seq_oss: can't malloc synth info\n");
rec = kzalloc(sizeof(*rec), GFP_KERNEL);
if (!rec)
return -ENOMEM;
}
rec->seq_device = -1;
rec->synth_type = reg->type;
rec->synth_subtype = reg->subtype;
@ -149,8 +149,9 @@ snd_seq_oss_synth_register(struct snd_seq_device *dev)
int
snd_seq_oss_synth_unregister(struct snd_seq_device *dev)
snd_seq_oss_synth_remove(struct device *_dev)
{
struct snd_seq_device *dev = to_seq_dev(_dev);
int index;
struct seq_oss_synth *rec = dev->driver_data;
unsigned long flags;
@ -247,7 +248,6 @@ snd_seq_oss_synth_setup(struct seq_oss_devinfo *dp)
if (info->nr_voices > 0) {
info->ch = kcalloc(info->nr_voices, sizeof(struct seq_oss_chinfo), GFP_KERNEL);
if (!info->ch) {
pr_err("ALSA: seq_oss: Cannot malloc voices\n");
rec->oper.close(&info->arg);
module_put(rec->oper.owner);
snd_use_lock_free(&rec->use_lock);

View File

@ -28,8 +28,8 @@
#include <sound/seq_device.h>
void snd_seq_oss_synth_init(void);
int snd_seq_oss_synth_register(struct snd_seq_device *dev);
int snd_seq_oss_synth_unregister(struct snd_seq_device *dev);
int snd_seq_oss_synth_probe(struct device *dev);
int snd_seq_oss_synth_remove(struct device *dev);
void snd_seq_oss_synth_setup(struct seq_oss_devinfo *dp);
void snd_seq_oss_synth_setup_midi(struct seq_oss_devinfo *dp);
void snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp);

View File

@ -1879,6 +1879,7 @@ static int snd_seq_ioctl_get_client_pool(struct snd_seq_client *client,
if (cptr == NULL)
return -ENOENT;
memset(&info, 0, sizeof(info));
info.client = cptr->number;
info.output_pool = cptr->pool->size;
info.output_room = cptr->pool->room;
info.output_free = info.output_pool;

View File

@ -36,6 +36,7 @@
*
*/
#include <linux/device.h>
#include <linux/init.h>
#include <linux/module.h>
#include <sound/core.h>
@ -51,140 +52,78 @@ MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
MODULE_DESCRIPTION("ALSA sequencer device management");
MODULE_LICENSE("GPL");
/* driver state */
#define DRIVER_EMPTY 0
#define DRIVER_LOADED (1<<0)
#define DRIVER_REQUESTED (1<<1)
#define DRIVER_LOCKED (1<<2)
#define DRIVER_REQUESTING (1<<3)
/*
* bus definition
*/
static int snd_seq_bus_match(struct device *dev, struct device_driver *drv)
{
struct snd_seq_device *sdev = to_seq_dev(dev);
struct snd_seq_driver *sdrv = to_seq_drv(drv);
struct ops_list {
char id[ID_LEN]; /* driver id */
int driver; /* driver state */
int used; /* reference counter */
int argsize; /* argument size */
return strcmp(sdrv->id, sdev->id) == 0 &&
sdrv->argsize == sdev->argsize;
}
/* operators */
struct snd_seq_dev_ops ops;
/* registered devices */
struct list_head dev_list; /* list of devices */
int num_devices; /* number of associated devices */
int num_init_devices; /* number of initialized devices */
struct mutex reg_mutex;
struct list_head list; /* next driver */
static struct bus_type snd_seq_bus_type = {
.name = "snd_seq",
.match = snd_seq_bus_match,
};
static LIST_HEAD(opslist);
static int num_ops;
static DEFINE_MUTEX(ops_mutex);
/*
* proc interface -- just for compatibility
*/
#ifdef CONFIG_PROC_FS
static struct snd_info_entry *info_entry;
#endif
/*
* prototypes
*/
static int snd_seq_device_free(struct snd_seq_device *dev);
static int snd_seq_device_dev_free(struct snd_device *device);
static int snd_seq_device_dev_register(struct snd_device *device);
static int snd_seq_device_dev_disconnect(struct snd_device *device);
static int print_dev_info(struct device *dev, void *data)
{
struct snd_seq_device *sdev = to_seq_dev(dev);
struct snd_info_buffer *buffer = data;
static int init_device(struct snd_seq_device *dev, struct ops_list *ops);
static int free_device(struct snd_seq_device *dev, struct ops_list *ops);
static struct ops_list *find_driver(char *id, int create_if_empty);
static struct ops_list *create_driver(char *id);
static void unlock_driver(struct ops_list *ops);
static void remove_drivers(void);
snd_iprintf(buffer, "snd-%s,%s,%d\n", sdev->id,
dev->driver ? "loaded" : "empty",
dev->driver ? 1 : 0);
return 0;
}
/*
* show all drivers and their status
*/
#ifdef CONFIG_PROC_FS
static void snd_seq_device_info(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct ops_list *ops;
mutex_lock(&ops_mutex);
list_for_each_entry(ops, &opslist, list) {
snd_iprintf(buffer, "snd-%s%s%s%s,%d\n",
ops->id,
ops->driver & DRIVER_LOADED ? ",loaded" : (ops->driver == DRIVER_EMPTY ? ",empty" : ""),
ops->driver & DRIVER_REQUESTED ? ",requested" : "",
ops->driver & DRIVER_LOCKED ? ",locked" : "",
ops->num_devices);
}
mutex_unlock(&ops_mutex);
bus_for_each_dev(&snd_seq_bus_type, NULL, buffer, print_dev_info);
}
#endif
/*
* load all registered drivers (called from seq_clientmgr.c)
*/
#ifdef CONFIG_MODULES
/* avoid auto-loading during module_init() */
/* flag to block auto-loading */
static atomic_t snd_seq_in_init = ATOMIC_INIT(1); /* blocked as default */
void snd_seq_autoload_lock(void)
static int request_seq_drv(struct device *dev, void *data)
{
atomic_inc(&snd_seq_in_init);
struct snd_seq_device *sdev = to_seq_dev(dev);
if (!dev->driver)
request_module("snd-%s", sdev->id);
return 0;
}
void snd_seq_autoload_unlock(void)
{
atomic_dec(&snd_seq_in_init);
}
static void autoload_drivers(void)
static void autoload_drivers(struct work_struct *work)
{
/* avoid reentrance */
if (atomic_inc_return(&snd_seq_in_init) == 1) {
struct ops_list *ops;
mutex_lock(&ops_mutex);
list_for_each_entry(ops, &opslist, list) {
if ((ops->driver & DRIVER_REQUESTING) &&
!(ops->driver & DRIVER_REQUESTED)) {
ops->used++;
mutex_unlock(&ops_mutex);
ops->driver |= DRIVER_REQUESTED;
request_module("snd-%s", ops->id);
mutex_lock(&ops_mutex);
ops->used--;
}
}
mutex_unlock(&ops_mutex);
}
if (atomic_inc_return(&snd_seq_in_init) == 1)
bus_for_each_dev(&snd_seq_bus_type, NULL, NULL,
request_seq_drv);
atomic_dec(&snd_seq_in_init);
}
static void call_autoload(struct work_struct *work)
{
autoload_drivers();
}
static DECLARE_WORK(autoload_work, call_autoload);
static void try_autoload(struct ops_list *ops)
{
if (!ops->driver) {
ops->driver |= DRIVER_REQUESTING;
schedule_work(&autoload_work);
}
}
static DECLARE_WORK(autoload_work, autoload_drivers);
static void queue_autoload_drivers(void)
{
struct ops_list *ops;
mutex_lock(&ops_mutex);
list_for_each_entry(ops, &opslist, list)
try_autoload(ops);
mutex_unlock(&ops_mutex);
schedule_work(&autoload_work);
}
void snd_seq_autoload_init(void)
@ -195,16 +134,63 @@ void snd_seq_autoload_init(void)
queue_autoload_drivers();
#endif
}
#else
#define try_autoload(ops) /* NOP */
#endif
EXPORT_SYMBOL(snd_seq_autoload_init);
void snd_seq_autoload_exit(void)
{
atomic_inc(&snd_seq_in_init);
}
EXPORT_SYMBOL(snd_seq_autoload_exit);
void snd_seq_device_load_drivers(void)
{
#ifdef CONFIG_MODULES
queue_autoload_drivers();
flush_work(&autoload_work);
}
EXPORT_SYMBOL(snd_seq_device_load_drivers);
#else
#define queue_autoload_drivers() /* NOP */
#endif
/*
* device management
*/
static int snd_seq_device_dev_free(struct snd_device *device)
{
struct snd_seq_device *dev = device->device_data;
put_device(&dev->dev);
return 0;
}
static int snd_seq_device_dev_register(struct snd_device *device)
{
struct snd_seq_device *dev = device->device_data;
int err;
err = device_add(&dev->dev);
if (err < 0)
return err;
if (!dev->dev.driver)
queue_autoload_drivers();
return 0;
}
static int snd_seq_device_dev_disconnect(struct snd_device *device)
{
struct snd_seq_device *dev = device->device_data;
device_del(&dev->dev);
return 0;
}
static void snd_seq_dev_release(struct device *dev)
{
struct snd_seq_device *sdev = to_seq_dev(dev);
if (sdev->private_free)
sdev->private_free(sdev);
kfree(sdev);
}
/*
@ -214,11 +200,10 @@ void snd_seq_device_load_drivers(void)
* id = id of driver
* result = return pointer (NULL allowed if unnecessary)
*/
int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize,
struct snd_seq_device **result)
int snd_seq_device_new(struct snd_card *card, int device, const char *id,
int argsize, struct snd_seq_device **result)
{
struct snd_seq_device *dev;
struct ops_list *ops;
int err;
static struct snd_device_ops dops = {
.dev_free = snd_seq_device_dev_free,
@ -232,347 +217,60 @@ int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize,
if (snd_BUG_ON(!id))
return -EINVAL;
ops = find_driver(id, 1);
if (ops == NULL)
dev = kzalloc(sizeof(*dev) + argsize, GFP_KERNEL);
if (!dev)
return -ENOMEM;
dev = kzalloc(sizeof(*dev)*2 + argsize, GFP_KERNEL);
if (dev == NULL) {
unlock_driver(ops);
return -ENOMEM;
}
/* set up device info */
dev->card = card;
dev->device = device;
strlcpy(dev->id, id, sizeof(dev->id));
dev->id = id;
dev->argsize = argsize;
dev->status = SNDRV_SEQ_DEVICE_FREE;
device_initialize(&dev->dev);
dev->dev.parent = &card->card_dev;
dev->dev.bus = &snd_seq_bus_type;
dev->dev.release = snd_seq_dev_release;
dev_set_name(&dev->dev, "%s-%d-%d", dev->id, card->number, device);
/* add this device to the list */
mutex_lock(&ops->reg_mutex);
list_add_tail(&dev->list, &ops->dev_list);
ops->num_devices++;
mutex_unlock(&ops->reg_mutex);
if ((err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops)) < 0) {
snd_seq_device_free(dev);
err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops);
if (err < 0) {
put_device(&dev->dev);
return err;
}
try_autoload(ops);
unlock_driver(ops);
if (result)
*result = dev;
return 0;
}
EXPORT_SYMBOL(snd_seq_device_new);
/*
* free the existing device
* driver registration
*/
static int snd_seq_device_free(struct snd_seq_device *dev)
int __snd_seq_driver_register(struct snd_seq_driver *drv, struct module *mod)
{
struct ops_list *ops;
if (snd_BUG_ON(!dev))
if (WARN_ON(!drv->driver.name || !drv->id))
return -EINVAL;
ops = find_driver(dev->id, 0);
if (ops == NULL)
return -ENXIO;
/* remove the device from the list */
mutex_lock(&ops->reg_mutex);
list_del(&dev->list);
ops->num_devices--;
mutex_unlock(&ops->reg_mutex);
free_device(dev, ops);
if (dev->private_free)
dev->private_free(dev);
kfree(dev);
unlock_driver(ops);
return 0;
drv->driver.bus = &snd_seq_bus_type;
drv->driver.owner = mod;
return driver_register(&drv->driver);
}
EXPORT_SYMBOL_GPL(__snd_seq_driver_register);
static int snd_seq_device_dev_free(struct snd_device *device)
void snd_seq_driver_unregister(struct snd_seq_driver *drv)
{
struct snd_seq_device *dev = device->device_data;
return snd_seq_device_free(dev);
driver_unregister(&drv->driver);
}
/*
* register the device
*/
static int snd_seq_device_dev_register(struct snd_device *device)
{
struct snd_seq_device *dev = device->device_data;
struct ops_list *ops;
ops = find_driver(dev->id, 0);
if (ops == NULL)
return -ENOENT;
/* initialize this device if the corresponding driver was
* already loaded
*/
if (ops->driver & DRIVER_LOADED)
init_device(dev, ops);
unlock_driver(ops);
return 0;
}
/*
* disconnect the device
*/
static int snd_seq_device_dev_disconnect(struct snd_device *device)
{
struct snd_seq_device *dev = device->device_data;
struct ops_list *ops;
ops = find_driver(dev->id, 0);
if (ops == NULL)
return -ENOENT;
free_device(dev, ops);
unlock_driver(ops);
return 0;
}
/*
* register device driver
* id = driver id
* entry = driver operators - duplicated to each instance
*/
int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry,
int argsize)
{
struct ops_list *ops;
struct snd_seq_device *dev;
if (id == NULL || entry == NULL ||
entry->init_device == NULL || entry->free_device == NULL)
return -EINVAL;
ops = find_driver(id, 1);
if (ops == NULL)
return -ENOMEM;
if (ops->driver & DRIVER_LOADED) {
pr_warn("ALSA: seq: driver_register: driver '%s' already exists\n", id);
unlock_driver(ops);
return -EBUSY;
}
mutex_lock(&ops->reg_mutex);
/* copy driver operators */
ops->ops = *entry;
ops->driver |= DRIVER_LOADED;
ops->argsize = argsize;
/* initialize existing devices if necessary */
list_for_each_entry(dev, &ops->dev_list, list) {
init_device(dev, ops);
}
mutex_unlock(&ops->reg_mutex);
unlock_driver(ops);
return 0;
}
/*
* create driver record
*/
static struct ops_list * create_driver(char *id)
{
struct ops_list *ops;
ops = kzalloc(sizeof(*ops), GFP_KERNEL);
if (ops == NULL)
return ops;
/* set up driver entry */
strlcpy(ops->id, id, sizeof(ops->id));
mutex_init(&ops->reg_mutex);
/*
* The ->reg_mutex locking rules are per-driver, so we create
* separate per-driver lock classes:
*/
lockdep_set_class(&ops->reg_mutex, (struct lock_class_key *)id);
ops->driver = DRIVER_EMPTY;
INIT_LIST_HEAD(&ops->dev_list);
/* lock this instance */
ops->used = 1;
/* register driver entry */
mutex_lock(&ops_mutex);
list_add_tail(&ops->list, &opslist);
num_ops++;
mutex_unlock(&ops_mutex);
return ops;
}
/*
* unregister the specified driver
*/
int snd_seq_device_unregister_driver(char *id)
{
struct ops_list *ops;
struct snd_seq_device *dev;
ops = find_driver(id, 0);
if (ops == NULL)
return -ENXIO;
if (! (ops->driver & DRIVER_LOADED) ||
(ops->driver & DRIVER_LOCKED)) {
pr_err("ALSA: seq: driver_unregister: cannot unload driver '%s': status=%x\n",
id, ops->driver);
unlock_driver(ops);
return -EBUSY;
}
/* close and release all devices associated with this driver */
mutex_lock(&ops->reg_mutex);
ops->driver |= DRIVER_LOCKED; /* do not remove this driver recursively */
list_for_each_entry(dev, &ops->dev_list, list) {
free_device(dev, ops);
}
ops->driver = 0;
if (ops->num_init_devices > 0)
pr_err("ALSA: seq: free_driver: init_devices > 0!! (%d)\n",
ops->num_init_devices);
mutex_unlock(&ops->reg_mutex);
unlock_driver(ops);
/* remove empty driver entries */
remove_drivers();
return 0;
}
/*
* remove empty driver entries
*/
static void remove_drivers(void)
{
struct list_head *head;
mutex_lock(&ops_mutex);
head = opslist.next;
while (head != &opslist) {
struct ops_list *ops = list_entry(head, struct ops_list, list);
if (! (ops->driver & DRIVER_LOADED) &&
ops->used == 0 && ops->num_devices == 0) {
head = head->next;
list_del(&ops->list);
kfree(ops);
num_ops--;
} else
head = head->next;
}
mutex_unlock(&ops_mutex);
}
/*
* initialize the device - call init_device operator
*/
static int init_device(struct snd_seq_device *dev, struct ops_list *ops)
{
if (! (ops->driver & DRIVER_LOADED))
return 0; /* driver is not loaded yet */
if (dev->status != SNDRV_SEQ_DEVICE_FREE)
return 0; /* already initialized */
if (ops->argsize != dev->argsize) {
pr_err("ALSA: seq: incompatible device '%s' for plug-in '%s' (%d %d)\n",
dev->name, ops->id, ops->argsize, dev->argsize);
return -EINVAL;
}
if (ops->ops.init_device(dev) >= 0) {
dev->status = SNDRV_SEQ_DEVICE_REGISTERED;
ops->num_init_devices++;
} else {
pr_err("ALSA: seq: init_device failed: %s: %s\n",
dev->name, dev->id);
}
return 0;
}
/*
* release the device - call free_device operator
*/
static int free_device(struct snd_seq_device *dev, struct ops_list *ops)
{
int result;
if (! (ops->driver & DRIVER_LOADED))
return 0; /* driver is not loaded yet */
if (dev->status != SNDRV_SEQ_DEVICE_REGISTERED)
return 0; /* not registered */
if (ops->argsize != dev->argsize) {
pr_err("ALSA: seq: incompatible device '%s' for plug-in '%s' (%d %d)\n",
dev->name, ops->id, ops->argsize, dev->argsize);
return -EINVAL;
}
if ((result = ops->ops.free_device(dev)) >= 0 || result == -ENXIO) {
dev->status = SNDRV_SEQ_DEVICE_FREE;
dev->driver_data = NULL;
ops->num_init_devices--;
} else {
pr_err("ALSA: seq: free_device failed: %s: %s\n",
dev->name, dev->id);
}
return 0;
}
/*
* find the matching driver with given id
*/
static struct ops_list * find_driver(char *id, int create_if_empty)
{
struct ops_list *ops;
mutex_lock(&ops_mutex);
list_for_each_entry(ops, &opslist, list) {
if (strcmp(ops->id, id) == 0) {
ops->used++;
mutex_unlock(&ops_mutex);
return ops;
}
}
mutex_unlock(&ops_mutex);
if (create_if_empty)
return create_driver(id);
return NULL;
}
static void unlock_driver(struct ops_list *ops)
{
mutex_lock(&ops_mutex);
ops->used--;
mutex_unlock(&ops_mutex);
}
EXPORT_SYMBOL_GPL(snd_seq_driver_unregister);
/*
* module part
*/
static int __init alsa_seq_device_init(void)
static int __init seq_dev_proc_init(void)
{
#ifdef CONFIG_PROC_FS
info_entry = snd_info_create_module_entry(THIS_MODULE, "drivers",
@ -589,28 +287,29 @@ static int __init alsa_seq_device_init(void)
return 0;
}
static int __init alsa_seq_device_init(void)
{
int err;
err = bus_register(&snd_seq_bus_type);
if (err < 0)
return err;
err = seq_dev_proc_init();
if (err < 0)
bus_unregister(&snd_seq_bus_type);
return err;
}
static void __exit alsa_seq_device_exit(void)
{
#ifdef CONFIG_MODULES
cancel_work_sync(&autoload_work);
#endif
remove_drivers();
#ifdef CONFIG_PROC_FS
snd_info_free_entry(info_entry);
#endif
if (num_ops)
pr_err("ALSA: seq: drivers not released (%d)\n", num_ops);
bus_unregister(&snd_seq_bus_type);
}
module_init(alsa_seq_device_init)
subsys_initcall(alsa_seq_device_init)
module_exit(alsa_seq_device_exit)
EXPORT_SYMBOL(snd_seq_device_load_drivers);
EXPORT_SYMBOL(snd_seq_device_new);
EXPORT_SYMBOL(snd_seq_device_register_driver);
EXPORT_SYMBOL(snd_seq_device_unregister_driver);
#ifdef CONFIG_MODULES
EXPORT_SYMBOL(snd_seq_autoload_init);
EXPORT_SYMBOL(snd_seq_autoload_lock);
EXPORT_SYMBOL(snd_seq_autoload_unlock);
#endif

View File

@ -214,11 +214,7 @@ delete_client(void)
static int __init alsa_seq_dummy_init(void)
{
int err;
snd_seq_autoload_lock();
err = register_client();
snd_seq_autoload_unlock();
return err;
return register_client();
}
static void __exit alsa_seq_dummy_exit(void)

View File

@ -33,10 +33,8 @@ struct snd_seq_fifo *snd_seq_fifo_new(int poolsize)
struct snd_seq_fifo *f;
f = kzalloc(sizeof(*f), GFP_KERNEL);
if (f == NULL) {
pr_debug("ALSA: seq: malloc failed for snd_seq_fifo_new() \n");
if (!f)
return NULL;
}
f->pool = snd_seq_pool_new(poolsize);
if (f->pool == NULL) {

View File

@ -387,10 +387,8 @@ int snd_seq_pool_init(struct snd_seq_pool *pool)
return 0;
pool->ptr = vmalloc(sizeof(struct snd_seq_event_cell) * pool->size);
if (pool->ptr == NULL) {
pr_debug("ALSA: seq: malloc for sequencer events failed\n");
if (!pool->ptr)
return -ENOMEM;
}
/* add new cells to the free cell list */
spin_lock_irqsave(&pool->lock, flags);
@ -463,10 +461,8 @@ struct snd_seq_pool *snd_seq_pool_new(int poolsize)
/* create pool block */
pool = kzalloc(sizeof(*pool), GFP_KERNEL);
if (pool == NULL) {
pr_debug("ALSA: seq: malloc failed for pool\n");
if (!pool)
return NULL;
}
spin_lock_init(&pool->lock);
pool->ptr = NULL;
pool->free = NULL;

View File

@ -273,8 +273,9 @@ static void snd_seq_midisynth_delete(struct seq_midisynth *msynth)
/* register new midi synth port */
static int
snd_seq_midisynth_register_port(struct snd_seq_device *dev)
snd_seq_midisynth_probe(struct device *_dev)
{
struct snd_seq_device *dev = to_seq_dev(_dev);
struct seq_midisynth_client *client;
struct seq_midisynth *msynth, *ms;
struct snd_seq_port_info *port;
@ -427,8 +428,9 @@ snd_seq_midisynth_register_port(struct snd_seq_device *dev)
/* release midi synth port */
static int
snd_seq_midisynth_unregister_port(struct snd_seq_device *dev)
snd_seq_midisynth_remove(struct device *_dev)
{
struct snd_seq_device *dev = to_seq_dev(_dev);
struct seq_midisynth_client *client;
struct seq_midisynth *msynth;
struct snd_card *card = dev->card;
@ -457,24 +459,14 @@ snd_seq_midisynth_unregister_port(struct snd_seq_device *dev)
return 0;
}
static struct snd_seq_driver seq_midisynth_driver = {
.driver = {
.name = KBUILD_MODNAME,
.probe = snd_seq_midisynth_probe,
.remove = snd_seq_midisynth_remove,
},
.id = SNDRV_SEQ_DEV_ID_MIDISYNTH,
.argsize = 0,
};
static int __init alsa_seq_midi_init(void)
{
static struct snd_seq_dev_ops ops = {
snd_seq_midisynth_register_port,
snd_seq_midisynth_unregister_port,
};
memset(&synths, 0, sizeof(synths));
snd_seq_autoload_lock();
snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH, &ops, 0);
snd_seq_autoload_unlock();
return 0;
}
static void __exit alsa_seq_midi_exit(void)
{
snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH);
}
module_init(alsa_seq_midi_init)
module_exit(alsa_seq_midi_exit)
module_snd_seq_driver(seq_midisynth_driver);

View File

@ -141,10 +141,8 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
/* create a new port */
new_port = kzalloc(sizeof(*new_port), GFP_KERNEL);
if (! new_port) {
pr_debug("ALSA: seq: malloc failed for registering client port\n");
if (!new_port)
return NULL; /* failure, out of memory */
}
/* init port data */
new_port->addr.client = client->number;
new_port->addr.port = -1;

View File

@ -59,10 +59,8 @@ struct snd_seq_prioq *snd_seq_prioq_new(void)
struct snd_seq_prioq *f;
f = kzalloc(sizeof(*f), GFP_KERNEL);
if (f == NULL) {
pr_debug("ALSA: seq: malloc failed for snd_seq_prioq_new()\n");
if (!f)
return NULL;
}
spin_lock_init(&f->lock);
f->head = NULL;

View File

@ -111,10 +111,8 @@ static struct snd_seq_queue *queue_new(int owner, int locked)
struct snd_seq_queue *q;
q = kzalloc(sizeof(*q), GFP_KERNEL);
if (q == NULL) {
pr_debug("ALSA: seq: malloc failed for snd_seq_queue_new()\n");
if (!q)
return NULL;
}
spin_lock_init(&q->owner_lock);
spin_lock_init(&q->check_lock);

View File

@ -56,10 +56,8 @@ struct snd_seq_timer *snd_seq_timer_new(void)
struct snd_seq_timer *tmr;
tmr = kzalloc(sizeof(*tmr), GFP_KERNEL);
if (tmr == NULL) {
pr_debug("ALSA: seq: malloc failed for snd_seq_timer_new() \n");
if (!tmr)
return NULL;
}
spin_lock_init(&tmr->lock);
/* reset setup to defaults */

View File

@ -186,7 +186,7 @@ static const struct file_operations snd_fops =
};
#ifdef CONFIG_SND_DYNAMIC_MINORS
static int snd_find_free_minor(int type)
static int snd_find_free_minor(int type, struct snd_card *card, int dev)
{
int minor;
@ -209,7 +209,7 @@ static int snd_find_free_minor(int type)
return -EBUSY;
}
#else
static int snd_kernel_minor(int type, struct snd_card *card, int dev)
static int snd_find_free_minor(int type, struct snd_card *card, int dev)
{
int minor;
@ -237,6 +237,8 @@ static int snd_kernel_minor(int type, struct snd_card *card, int dev)
}
if (snd_BUG_ON(minor < 0 || minor >= SNDRV_OS_MINORS))
return -EINVAL;
if (snd_minors[minor])
return -EBUSY;
return minor;
}
#endif
@ -276,13 +278,7 @@ int snd_register_device(int type, struct snd_card *card, int dev,
preg->private_data = private_data;
preg->card_ptr = card;
mutex_lock(&sound_mutex);
#ifdef CONFIG_SND_DYNAMIC_MINORS
minor = snd_find_free_minor(type);
#else
minor = snd_kernel_minor(type, card, dev);
if (minor >= 0 && snd_minors[minor])
minor = -EBUSY;
#endif
minor = snd_find_free_minor(type, card, dev);
if (minor < 0) {
err = minor;
goto error;

View File

@ -774,10 +774,8 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid,
if (rtimer)
*rtimer = NULL;
timer = kzalloc(sizeof(*timer), GFP_KERNEL);
if (timer == NULL) {
pr_err("ALSA: timer: cannot allocate\n");
if (!timer)
return -ENOMEM;
}
timer->tmr_class = tid->dev_class;
timer->card = card;
timer->tmr_device = tid->device;

View File

@ -216,8 +216,9 @@ static int snd_opl3_synth_create_port(struct snd_opl3 * opl3)
/* ------------------------------ */
static int snd_opl3_seq_new_device(struct snd_seq_device *dev)
static int snd_opl3_seq_probe(struct device *_dev)
{
struct snd_seq_device *dev = to_seq_dev(_dev);
struct snd_opl3 *opl3;
int client, err;
char name[32];
@ -257,8 +258,9 @@ static int snd_opl3_seq_new_device(struct snd_seq_device *dev)
return 0;
}
static int snd_opl3_seq_delete_device(struct snd_seq_device *dev)
static int snd_opl3_seq_remove(struct device *_dev)
{
struct snd_seq_device *dev = to_seq_dev(_dev);
struct snd_opl3 *opl3;
opl3 = *(struct snd_opl3 **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
@ -275,22 +277,14 @@ static int snd_opl3_seq_delete_device(struct snd_seq_device *dev)
return 0;
}
static int __init alsa_opl3_seq_init(void)
{
static struct snd_seq_dev_ops ops =
{
snd_opl3_seq_new_device,
snd_opl3_seq_delete_device
};
static struct snd_seq_driver opl3_seq_driver = {
.driver = {
.name = KBUILD_MODNAME,
.probe = snd_opl3_seq_probe,
.remove = snd_opl3_seq_remove,
},
.id = SNDRV_SEQ_DEV_ID_OPL3,
.argsize = sizeof(struct snd_opl3 *),
};
return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OPL3, &ops,
sizeof(struct snd_opl3 *));
}
static void __exit alsa_opl3_seq_exit(void)
{
snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OPL3);
}
module_init(alsa_opl3_seq_init)
module_exit(alsa_opl3_seq_exit)
module_snd_seq_driver(opl3_seq_driver);

View File

@ -124,8 +124,9 @@ static void snd_opl4_seq_free_port(void *private_data)
snd_midi_channel_free_set(opl4->chset);
}
static int snd_opl4_seq_new_device(struct snd_seq_device *dev)
static int snd_opl4_seq_probe(struct device *_dev)
{
struct snd_seq_device *dev = to_seq_dev(_dev);
struct snd_opl4 *opl4;
int client;
struct snd_seq_port_callback pcallbacks;
@ -180,8 +181,9 @@ static int snd_opl4_seq_new_device(struct snd_seq_device *dev)
return 0;
}
static int snd_opl4_seq_delete_device(struct snd_seq_device *dev)
static int snd_opl4_seq_remove(struct device *_dev)
{
struct snd_seq_device *dev = to_seq_dev(_dev);
struct snd_opl4 *opl4;
opl4 = *(struct snd_opl4 **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
@ -195,21 +197,14 @@ static int snd_opl4_seq_delete_device(struct snd_seq_device *dev)
return 0;
}
static int __init alsa_opl4_synth_init(void)
{
static struct snd_seq_dev_ops ops = {
snd_opl4_seq_new_device,
snd_opl4_seq_delete_device
};
static struct snd_seq_driver opl4_seq_driver = {
.driver = {
.name = KBUILD_MODNAME,
.probe = snd_opl4_seq_probe,
.remove = snd_opl4_seq_remove,
},
.id = SNDRV_SEQ_DEV_ID_OPL4,
.argsize = sizeof(struct snd_opl4 *),
};
return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OPL4, &ops,
sizeof(struct snd_opl4 *));
}
static void __exit alsa_opl4_synth_exit(void)
{
snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OPL4);
}
module_init(alsa_opl4_synth_init)
module_exit(alsa_opl4_synth_exit)
module_snd_seq_driver(opl4_seq_driver);

View File

@ -166,10 +166,10 @@ int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
* One AMDTP packet can include some frames. In blocking mode, the
* number equals to SYT_INTERVAL. So the number is 8, 16 or 32,
* depending on its sampling rate. For accurate period interrupt, it's
* preferrable to aligh period/buffer sizes to current SYT_INTERVAL.
* preferrable to align period/buffer sizes to current SYT_INTERVAL.
*
* TODO: These constraints can be improved with propper rules.
* Currently apply LCM of SYT_INTEVALs.
* TODO: These constraints can be improved with proper rules.
* Currently apply LCM of SYT_INTERVALs.
*/
err = snd_pcm_hw_constraint_step(runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 32);
@ -270,7 +270,7 @@ static void amdtp_read_s32(struct amdtp_stream *s,
* @s: the AMDTP stream to configure
* @format: the format of the ALSA PCM device
*
* The sample format must be set after the other paramters (rate/PCM channels/
* The sample format must be set after the other parameters (rate/PCM channels/
* MIDI) and before the stream is started, and must not be changed while the
* stream is running.
*/

View File

@ -13,7 +13,7 @@
*
* Transaction substance:
* At first, 6 data exist. Following to the data, parameters for each command
* exist. All of the parameters are 32 bit alighed to big endian.
* exist. All of the parameters are 32 bit aligned to big endian.
* data[0]: Length of transaction substance
* data[1]: Transaction version
* data[2]: Sequence number. This is incremented by the device

3
sound/hda/Kconfig Normal file
View File

@ -0,0 +1,3 @@
config SND_HDA_CORE
tristate
select REGMAP

7
sound/hda/Makefile Normal file
View File

@ -0,0 +1,7 @@
snd-hda-core-objs := hda_bus_type.o hdac_bus.o hdac_device.o hdac_sysfs.o \
hdac_regmap.o array.o
snd-hda-core-objs += trace.o
CFLAGS_trace.o := -I$(src)
obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o

49
sound/hda/array.c Normal file
View File

@ -0,0 +1,49 @@
/*
* generic arrays
*/
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/hdaudio.h>
/**
* snd_array_new - get a new element from the given array
* @array: the array object
*
* Get a new element from the given array. If it exceeds the
* pre-allocated array size, re-allocate the array.
*
* Returns NULL if allocation failed.
*/
void *snd_array_new(struct snd_array *array)
{
if (snd_BUG_ON(!array->elem_size))
return NULL;
if (array->used >= array->alloced) {
int num = array->alloced + array->alloc_align;
int size = (num + 1) * array->elem_size;
void *nlist;
if (snd_BUG_ON(num >= 4096))
return NULL;
nlist = krealloc(array->list, size, GFP_KERNEL | __GFP_ZERO);
if (!nlist)
return NULL;
array->list = nlist;
array->alloced = num;
}
return snd_array_elem(array, array->used++);
}
EXPORT_SYMBOL_GPL(snd_array_new);
/**
* snd_array_free - free the given array elements
* @array: the array object
*/
void snd_array_free(struct snd_array *array)
{
kfree(array->list);
array->used = 0;
array->alloced = 0;
array->list = NULL;
}
EXPORT_SYMBOL_GPL(snd_array_free);

42
sound/hda/hda_bus_type.c Normal file
View File

@ -0,0 +1,42 @@
/*
* HD-audio bus
*/
#include <linux/init.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/export.h>
#include <sound/hdaudio.h>
MODULE_DESCRIPTION("HD-audio bus");
MODULE_LICENSE("GPL");
static int hda_bus_match(struct device *dev, struct device_driver *drv)
{
struct hdac_device *hdev = dev_to_hdac_dev(dev);
struct hdac_driver *hdrv = drv_to_hdac_driver(drv);
if (hdev->type != hdrv->type)
return 0;
if (hdrv->match)
return hdrv->match(hdev, hdrv);
return 1;
}
struct bus_type snd_hda_bus_type = {
.name = "hdaudio",
.match = hda_bus_match,
};
EXPORT_SYMBOL_GPL(snd_hda_bus_type);
static int __init hda_bus_init(void)
{
return bus_register(&snd_hda_bus_type);
}
static void __exit hda_bus_exit(void)
{
bus_unregister(&snd_hda_bus_type);
}
subsys_initcall(hda_bus_init);
module_exit(hda_bus_exit);

186
sound/hda/hdac_bus.c Normal file
View File

@ -0,0 +1,186 @@
/*
* HD-audio core bus driver
*/
#include <linux/init.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/export.h>
#include <sound/hdaudio.h>
#include "trace.h"
static void process_unsol_events(struct work_struct *work);
/**
* snd_hdac_bus_init - initialize a HD-audio bas bus
* @bus: the pointer to bus object
*
* Returns 0 if successful, or a negative error code.
*/
int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
const struct hdac_bus_ops *ops)
{
memset(bus, 0, sizeof(*bus));
bus->dev = dev;
bus->ops = ops;
INIT_LIST_HEAD(&bus->codec_list);
INIT_WORK(&bus->unsol_work, process_unsol_events);
mutex_init(&bus->cmd_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(snd_hdac_bus_init);
/**
* snd_hdac_bus_exit - clean up a HD-audio bas bus
* @bus: the pointer to bus object
*/
void snd_hdac_bus_exit(struct hdac_bus *bus)
{
WARN_ON(!list_empty(&bus->codec_list));
cancel_work_sync(&bus->unsol_work);
}
EXPORT_SYMBOL_GPL(snd_hdac_bus_exit);
/**
* snd_hdac_bus_exec_verb - execute a HD-audio verb on the given bus
* @bus: bus object
* @cmd: HD-audio encoded verb
* @res: pointer to store the response, NULL if performing asynchronously
*
* Returns 0 if successful, or a negative error code.
*/
int snd_hdac_bus_exec_verb(struct hdac_bus *bus, unsigned int addr,
unsigned int cmd, unsigned int *res)
{
int err;
mutex_lock(&bus->cmd_mutex);
err = snd_hdac_bus_exec_verb_unlocked(bus, addr, cmd, res);
mutex_unlock(&bus->cmd_mutex);
return err;
}
EXPORT_SYMBOL_GPL(snd_hdac_bus_exec_verb);
/**
* snd_hdac_bus_exec_verb_unlocked - unlocked version
* @bus: bus object
* @cmd: HD-audio encoded verb
* @res: pointer to store the response, NULL if performing asynchronously
*
* Returns 0 if successful, or a negative error code.
*/
int snd_hdac_bus_exec_verb_unlocked(struct hdac_bus *bus, unsigned int addr,
unsigned int cmd, unsigned int *res)
{
unsigned int tmp;
int err;
if (cmd == ~0)
return -EINVAL;
if (res)
*res = -1;
else if (bus->sync_write)
res = &tmp;
for (;;) {
trace_hda_send_cmd(bus, cmd);
err = bus->ops->command(bus, cmd);
if (err != -EAGAIN)
break;
/* process pending verbs */
err = bus->ops->get_response(bus, addr, &tmp);
if (err)
break;
}
if (!err && res) {
err = bus->ops->get_response(bus, addr, res);
trace_hda_get_response(bus, addr, *res);
}
return err;
}
EXPORT_SYMBOL_GPL(snd_hdac_bus_exec_verb_unlocked);
/**
* snd_hdac_bus_queue_event - add an unsolicited event to queue
* @bus: the BUS
* @res: unsolicited event (lower 32bit of RIRB entry)
* @res_ex: codec addr and flags (upper 32bit or RIRB entry)
*
* Adds the given event to the queue. The events are processed in
* the workqueue asynchronously. Call this function in the interrupt
* hanlder when RIRB receives an unsolicited event.
*/
void snd_hdac_bus_queue_event(struct hdac_bus *bus, u32 res, u32 res_ex)
{
unsigned int wp;
if (!bus)
return;
trace_hda_unsol_event(bus, res, res_ex);
wp = (bus->unsol_wp + 1) % HDA_UNSOL_QUEUE_SIZE;
bus->unsol_wp = wp;
wp <<= 1;
bus->unsol_queue[wp] = res;
bus->unsol_queue[wp + 1] = res_ex;
schedule_work(&bus->unsol_work);
}
EXPORT_SYMBOL_GPL(snd_hdac_bus_queue_event);
/*
* process queued unsolicited events
*/
static void process_unsol_events(struct work_struct *work)
{
struct hdac_bus *bus = container_of(work, struct hdac_bus, unsol_work);
struct hdac_device *codec;
struct hdac_driver *drv;
unsigned int rp, caddr, res;
while (bus->unsol_rp != bus->unsol_wp) {
rp = (bus->unsol_rp + 1) % HDA_UNSOL_QUEUE_SIZE;
bus->unsol_rp = rp;
rp <<= 1;
res = bus->unsol_queue[rp];
caddr = bus->unsol_queue[rp + 1];
if (!(caddr & (1 << 4))) /* no unsolicited event? */
continue;
codec = bus->caddr_tbl[caddr & 0x0f];
if (!codec || !codec->dev.driver)
continue;
drv = drv_to_hdac_driver(codec->dev.driver);
if (drv->unsol_event)
drv->unsol_event(codec, res);
}
}
int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec)
{
if (bus->caddr_tbl[codec->addr]) {
dev_err(bus->dev, "address 0x%x is already occupied\n",
codec->addr);
return -EBUSY;
}
list_add_tail(&codec->list, &bus->codec_list);
bus->caddr_tbl[codec->addr] = codec;
set_bit(codec->addr, &bus->codec_powered);
bus->num_codecs++;
return 0;
}
EXPORT_SYMBOL_GPL(snd_hdac_bus_add_device);
void snd_hdac_bus_remove_device(struct hdac_bus *bus,
struct hdac_device *codec)
{
WARN_ON(bus != codec->bus);
if (list_empty(&codec->list))
return;
list_del_init(&codec->list);
bus->caddr_tbl[codec->addr] = NULL;
clear_bit(codec->addr, &bus->codec_powered);
bus->num_codecs--;
}
EXPORT_SYMBOL_GPL(snd_hdac_bus_remove_device);

599
sound/hda/hdac_device.c Normal file
View File

@ -0,0 +1,599 @@
/*
* HD-audio codec core device
*/
#include <linux/init.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/export.h>
#include <linux/pm_runtime.h>
#include <sound/hdaudio.h>
#include <sound/hda_regmap.h>
#include "local.h"
static void setup_fg_nodes(struct hdac_device *codec);
static int get_codec_vendor_name(struct hdac_device *codec);
static void default_release(struct device *dev)
{
snd_hdac_device_exit(container_of(dev, struct hdac_device, dev));
}
/**
* snd_hdac_device_init - initialize the HD-audio codec base device
* @codec: device to initialize
* @bus: but to attach
* @name: device name string
* @addr: codec address
*
* Returns zero for success or a negative error code.
*
* This function increments the runtime PM counter and marks it active.
* The caller needs to turn it off appropriately later.
*
* The caller needs to set the device's release op properly by itself.
*/
int snd_hdac_device_init(struct hdac_device *codec, struct hdac_bus *bus,
const char *name, unsigned int addr)
{
struct device *dev;
hda_nid_t fg;
int err;
dev = &codec->dev;
device_initialize(dev);
dev->parent = bus->dev;
dev->bus = &snd_hda_bus_type;
dev->release = default_release;
dev->groups = hdac_dev_attr_groups;
dev_set_name(dev, "%s", name);
device_enable_async_suspend(dev);
codec->bus = bus;
codec->addr = addr;
codec->type = HDA_DEV_CORE;
pm_runtime_set_active(&codec->dev);
pm_runtime_get_noresume(&codec->dev);
atomic_set(&codec->in_pm, 0);
err = snd_hdac_bus_add_device(bus, codec);
if (err < 0)
goto error;
/* fill parameters */
codec->vendor_id = snd_hdac_read_parm(codec, AC_NODE_ROOT,
AC_PAR_VENDOR_ID);
if (codec->vendor_id == -1) {
/* read again, hopefully the access method was corrected
* in the last read...
*/
codec->vendor_id = snd_hdac_read_parm(codec, AC_NODE_ROOT,
AC_PAR_VENDOR_ID);
}
codec->subsystem_id = snd_hdac_read_parm(codec, AC_NODE_ROOT,
AC_PAR_SUBSYSTEM_ID);
codec->revision_id = snd_hdac_read_parm(codec, AC_NODE_ROOT,
AC_PAR_REV_ID);
setup_fg_nodes(codec);
if (!codec->afg && !codec->mfg) {
dev_err(dev, "no AFG or MFG node found\n");
err = -ENODEV;
goto error;
}
fg = codec->afg ? codec->afg : codec->mfg;
err = snd_hdac_refresh_widgets(codec);
if (err < 0)
goto error;
codec->power_caps = snd_hdac_read_parm(codec, fg, AC_PAR_POWER_STATE);
/* reread ssid if not set by parameter */
if (codec->subsystem_id == -1 || codec->subsystem_id == 0)
snd_hdac_read(codec, fg, AC_VERB_GET_SUBSYSTEM_ID, 0,
&codec->subsystem_id);
err = get_codec_vendor_name(codec);
if (err < 0)
goto error;
codec->chip_name = kasprintf(GFP_KERNEL, "ID %x",
codec->vendor_id & 0xffff);
if (!codec->chip_name) {
err = -ENOMEM;
goto error;
}
return 0;
error:
put_device(&codec->dev);
return err;
}
EXPORT_SYMBOL_GPL(snd_hdac_device_init);
/**
* snd_hdac_device_exit - clean up the HD-audio codec base device
* @codec: device to clean up
*/
void snd_hdac_device_exit(struct hdac_device *codec)
{
pm_runtime_put_noidle(&codec->dev);
snd_hdac_bus_remove_device(codec->bus, codec);
kfree(codec->vendor_name);
kfree(codec->chip_name);
}
EXPORT_SYMBOL_GPL(snd_hdac_device_exit);
/**
* snd_hdac_device_register - register the hd-audio codec base device
* codec: the device to register
*/
int snd_hdac_device_register(struct hdac_device *codec)
{
int err;
err = device_add(&codec->dev);
if (err < 0)
return err;
err = hda_widget_sysfs_init(codec);
if (err < 0) {
device_del(&codec->dev);
return err;
}
return 0;
}
EXPORT_SYMBOL_GPL(snd_hdac_device_register);
/**
* snd_hdac_device_unregister - unregister the hd-audio codec base device
* codec: the device to unregister
*/
void snd_hdac_device_unregister(struct hdac_device *codec)
{
if (device_is_registered(&codec->dev)) {
hda_widget_sysfs_exit(codec);
device_del(&codec->dev);
}
}
EXPORT_SYMBOL_GPL(snd_hdac_device_unregister);
/**
* snd_hdac_make_cmd - compose a 32bit command word to be sent to the
* HD-audio controller
* @codec: the codec object
* @nid: NID to encode
* @verb: verb to encode
* @parm: parameter to encode
*
* Return an encoded command verb or -1 for error.
*/
unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid,
unsigned int verb, unsigned int parm)
{
u32 val, addr;
addr = codec->addr;
if ((addr & ~0xf) || (nid & ~0x7f) ||
(verb & ~0xfff) || (parm & ~0xffff)) {
dev_err(&codec->dev, "out of range cmd %x:%x:%x:%x\n",
addr, nid, verb, parm);
return -1;
}
val = addr << 28;
val |= (u32)nid << 20;
val |= verb << 8;
val |= parm;
return val;
}
EXPORT_SYMBOL_GPL(snd_hdac_make_cmd);
/**
* snd_hdac_exec_verb - execute an encoded verb
* @codec: the codec object
* @cmd: encoded verb to execute
* @flags: optional flags, pass zero for default
* @res: the pointer to store the result, NULL if running async
*
* Returns zero if successful, or a negative error code.
*
* This calls the exec_verb op when set in hdac_codec. If not,
* call the default snd_hdac_bus_exec_verb().
*/
int snd_hdac_exec_verb(struct hdac_device *codec, unsigned int cmd,
unsigned int flags, unsigned int *res)
{
if (codec->exec_verb)
return codec->exec_verb(codec, cmd, flags, res);
return snd_hdac_bus_exec_verb(codec->bus, codec->addr, cmd, res);
}
EXPORT_SYMBOL_GPL(snd_hdac_exec_verb);
/**
* snd_hdac_read - execute a verb
* @codec: the codec object
* @nid: NID to execute a verb
* @verb: verb to execute
* @parm: parameter for a verb
* @res: the pointer to store the result, NULL if running async
*
* Returns zero if successful, or a negative error code.
*/
int snd_hdac_read(struct hdac_device *codec, hda_nid_t nid,
unsigned int verb, unsigned int parm, unsigned int *res)
{
unsigned int cmd = snd_hdac_make_cmd(codec, nid, verb, parm);
return snd_hdac_exec_verb(codec, cmd, 0, res);
}
EXPORT_SYMBOL_GPL(snd_hdac_read);
/**
* _snd_hdac_read_parm - read a parmeter
*
* This function returns zero or an error unlike snd_hdac_read_parm().
*/
int _snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm,
unsigned int *res)
{
unsigned int cmd;
cmd = snd_hdac_regmap_encode_verb(nid, AC_VERB_PARAMETERS) | parm;
return snd_hdac_regmap_read_raw(codec, cmd, res);
}
EXPORT_SYMBOL_GPL(_snd_hdac_read_parm);
/**
* snd_hdac_read_parm_uncached - read a codec parameter without caching
* @codec: the codec object
* @nid: NID to read a parameter
* @parm: parameter to read
*
* Returns -1 for error. If you need to distinguish the error more
* strictly, use snd_hdac_read() directly.
*/
int snd_hdac_read_parm_uncached(struct hdac_device *codec, hda_nid_t nid,
int parm)
{
int val;
if (codec->regmap)
regcache_cache_bypass(codec->regmap, true);
val = snd_hdac_read_parm(codec, nid, parm);
if (codec->regmap)
regcache_cache_bypass(codec->regmap, false);
return val;
}
EXPORT_SYMBOL_GPL(snd_hdac_read_parm_uncached);
/**
* snd_hdac_override_parm - override read-only parameters
* @codec: the codec object
* @nid: NID for the parameter
* @parm: the parameter to change
* @val: the parameter value to overwrite
*/
int snd_hdac_override_parm(struct hdac_device *codec, hda_nid_t nid,
unsigned int parm, unsigned int val)
{
unsigned int verb = (AC_VERB_PARAMETERS << 8) | (nid << 20) | parm;
int err;
if (!codec->regmap)
return -EINVAL;
codec->caps_overwriting = true;
err = snd_hdac_regmap_write_raw(codec, verb, val);
codec->caps_overwriting = false;
return err;
}
EXPORT_SYMBOL_GPL(snd_hdac_override_parm);
/**
* snd_hdac_get_sub_nodes - get start NID and number of subtree nodes
* @codec: the codec object
* @nid: NID to inspect
* @start_id: the pointer to store the starting NID
*
* Returns the number of subtree nodes or zero if not found.
* This function reads parameters always without caching.
*/
int snd_hdac_get_sub_nodes(struct hdac_device *codec, hda_nid_t nid,
hda_nid_t *start_id)
{
unsigned int parm;
parm = snd_hdac_read_parm_uncached(codec, nid, AC_PAR_NODE_COUNT);
if (parm == -1) {
*start_id = 0;
return 0;
}
*start_id = (parm >> 16) & 0x7fff;
return (int)(parm & 0x7fff);
}
EXPORT_SYMBOL_GPL(snd_hdac_get_sub_nodes);
/*
* look for an AFG and MFG nodes
*/
static void setup_fg_nodes(struct hdac_device *codec)
{
int i, total_nodes, function_id;
hda_nid_t nid;
total_nodes = snd_hdac_get_sub_nodes(codec, AC_NODE_ROOT, &nid);
for (i = 0; i < total_nodes; i++, nid++) {
function_id = snd_hdac_read_parm(codec, nid,
AC_PAR_FUNCTION_TYPE);
switch (function_id & 0xff) {
case AC_GRP_AUDIO_FUNCTION:
codec->afg = nid;
codec->afg_function_id = function_id & 0xff;
codec->afg_unsol = (function_id >> 8) & 1;
break;
case AC_GRP_MODEM_FUNCTION:
codec->mfg = nid;
codec->mfg_function_id = function_id & 0xff;
codec->mfg_unsol = (function_id >> 8) & 1;
break;
default:
break;
}
}
}
/**
* snd_hdac_refresh_widgets - Reset the widget start/end nodes
* @codec: the codec object
*/
int snd_hdac_refresh_widgets(struct hdac_device *codec)
{
hda_nid_t start_nid;
int nums;
nums = snd_hdac_get_sub_nodes(codec, codec->afg, &start_nid);
if (!start_nid || nums <= 0 || nums >= 0xff) {
dev_err(&codec->dev, "cannot read sub nodes for FG 0x%02x\n",
codec->afg);
return -EINVAL;
}
codec->num_nodes = nums;
codec->start_nid = start_nid;
codec->end_nid = start_nid + nums;
return 0;
}
EXPORT_SYMBOL_GPL(snd_hdac_refresh_widgets);
/* return CONNLIST_LEN parameter of the given widget */
static unsigned int get_num_conns(struct hdac_device *codec, hda_nid_t nid)
{
unsigned int wcaps = get_wcaps(codec, nid);
unsigned int parm;
if (!(wcaps & AC_WCAP_CONN_LIST) &&
get_wcaps_type(wcaps) != AC_WID_VOL_KNB)
return 0;
parm = snd_hdac_read_parm(codec, nid, AC_PAR_CONNLIST_LEN);
if (parm == -1)
parm = 0;
return parm;
}
/**
* snd_hdac_get_connections - get a widget connection list
* @codec: the codec object
* @nid: NID
* @conn_list: the array to store the results, can be NULL
* @max_conns: the max size of the given array
*
* Returns the number of connected widgets, zero for no connection, or a
* negative error code. When the number of elements don't fit with the
* given array size, it returns -ENOSPC.
*
* When @conn_list is NULL, it just checks the number of connections.
*/
int snd_hdac_get_connections(struct hdac_device *codec, hda_nid_t nid,
hda_nid_t *conn_list, int max_conns)
{
unsigned int parm;
int i, conn_len, conns, err;
unsigned int shift, num_elems, mask;
hda_nid_t prev_nid;
int null_count = 0;
parm = get_num_conns(codec, nid);
if (!parm)
return 0;
if (parm & AC_CLIST_LONG) {
/* long form */
shift = 16;
num_elems = 2;
} else {
/* short form */
shift = 8;
num_elems = 4;
}
conn_len = parm & AC_CLIST_LENGTH;
mask = (1 << (shift-1)) - 1;
if (!conn_len)
return 0; /* no connection */
if (conn_len == 1) {
/* single connection */
err = snd_hdac_read(codec, nid, AC_VERB_GET_CONNECT_LIST, 0,
&parm);
if (err < 0)
return err;
if (conn_list)
conn_list[0] = parm & mask;
return 1;
}
/* multi connection */
conns = 0;
prev_nid = 0;
for (i = 0; i < conn_len; i++) {
int range_val;
hda_nid_t val, n;
if (i % num_elems == 0) {
err = snd_hdac_read(codec, nid,
AC_VERB_GET_CONNECT_LIST, i,
&parm);
if (err < 0)
return -EIO;
}
range_val = !!(parm & (1 << (shift-1))); /* ranges */
val = parm & mask;
if (val == 0 && null_count++) { /* no second chance */
dev_dbg(&codec->dev,
"invalid CONNECT_LIST verb %x[%i]:%x\n",
nid, i, parm);
return 0;
}
parm >>= shift;
if (range_val) {
/* ranges between the previous and this one */
if (!prev_nid || prev_nid >= val) {
dev_warn(&codec->dev,
"invalid dep_range_val %x:%x\n",
prev_nid, val);
continue;
}
for (n = prev_nid + 1; n <= val; n++) {
if (conn_list) {
if (conns >= max_conns)
return -ENOSPC;
conn_list[conns] = n;
}
conns++;
}
} else {
if (conn_list) {
if (conns >= max_conns)
return -ENOSPC;
conn_list[conns] = val;
}
conns++;
}
prev_nid = val;
}
return conns;
}
EXPORT_SYMBOL_GPL(snd_hdac_get_connections);
#ifdef CONFIG_PM
/**
* snd_hdac_power_up - power up the codec
* @codec: the codec object
*
* This function calls the runtime PM helper to power up the given codec.
* Unlike snd_hdac_power_up_pm(), you should call this only for the code
* path that isn't included in PM path. Otherwise it gets stuck.
*/
void snd_hdac_power_up(struct hdac_device *codec)
{
pm_runtime_get_sync(&codec->dev);
}
EXPORT_SYMBOL_GPL(snd_hdac_power_up);
/**
* snd_hdac_power_down - power down the codec
* @codec: the codec object
*/
void snd_hdac_power_down(struct hdac_device *codec)
{
struct device *dev = &codec->dev;
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
}
EXPORT_SYMBOL_GPL(snd_hdac_power_down);
/**
* snd_hdac_power_up_pm - power up the codec
* @codec: the codec object
*
* This function can be called in a recursive code path like init code
* which may be called by PM suspend/resume again. OTOH, if a power-up
* call must wake up the sleeper (e.g. in a kctl callback), use
* snd_hdac_power_up() instead.
*/
void snd_hdac_power_up_pm(struct hdac_device *codec)
{
if (!atomic_inc_not_zero(&codec->in_pm))
snd_hdac_power_up(codec);
}
EXPORT_SYMBOL_GPL(snd_hdac_power_up_pm);
/**
* snd_hdac_power_down_pm - power down the codec
* @codec: the codec object
*
* Like snd_hdac_power_up_pm(), this function is used in a recursive
* code path like init code which may be called by PM suspend/resume again.
*/
void snd_hdac_power_down_pm(struct hdac_device *codec)
{
if (atomic_dec_if_positive(&codec->in_pm) < 0)
snd_hdac_power_down(codec);
}
EXPORT_SYMBOL_GPL(snd_hdac_power_down_pm);
#endif
/* codec vendor labels */
struct hda_vendor_id {
unsigned int id;
const char *name;
};
static struct hda_vendor_id hda_vendor_ids[] = {
{ 0x1002, "ATI" },
{ 0x1013, "Cirrus Logic" },
{ 0x1057, "Motorola" },
{ 0x1095, "Silicon Image" },
{ 0x10de, "Nvidia" },
{ 0x10ec, "Realtek" },
{ 0x1102, "Creative" },
{ 0x1106, "VIA" },
{ 0x111d, "IDT" },
{ 0x11c1, "LSI" },
{ 0x11d4, "Analog Devices" },
{ 0x13f6, "C-Media" },
{ 0x14f1, "Conexant" },
{ 0x17e8, "Chrontel" },
{ 0x1854, "LG" },
{ 0x1aec, "Wolfson Microelectronics" },
{ 0x1af4, "QEMU" },
{ 0x434d, "C-Media" },
{ 0x8086, "Intel" },
{ 0x8384, "SigmaTel" },
{} /* terminator */
};
/* store the codec vendor name */
static int get_codec_vendor_name(struct hdac_device *codec)
{
const struct hda_vendor_id *c;
u16 vendor_id = codec->vendor_id >> 16;
for (c = hda_vendor_ids; c->id; c++) {
if (c->id == vendor_id) {
codec->vendor_name = kstrdup(c->name, GFP_KERNEL);
return codec->vendor_name ? 0 : -ENOMEM;
}
}
codec->vendor_name = kasprintf(GFP_KERNEL, "Generic %04x", vendor_id);
return codec->vendor_name ? 0 : -ENOMEM;
}

472
sound/hda/hdac_regmap.c Normal file
View File

@ -0,0 +1,472 @@
/*
* Regmap support for HD-audio verbs
*
* A virtual register is translated to one or more hda verbs for write,
* vice versa for read.
*
* A few limitations:
* - Provided for not all verbs but only subset standard non-volatile verbs.
* - For reading, only AC_VERB_GET_* variants can be used.
* - For writing, mapped to the *corresponding* AC_VERB_SET_* variants,
* so can't handle asymmetric verbs for read and write
*/
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/regmap.h>
#include <linux/export.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <sound/core.h>
#include <sound/hdaudio.h>
#include <sound/hda_regmap.h>
#ifdef CONFIG_PM
#define codec_is_running(codec) \
(atomic_read(&(codec)->in_pm) || \
!pm_runtime_suspended(&(codec)->dev))
#else
#define codec_is_running(codec) true
#endif
#define get_verb(reg) (((reg) >> 8) & 0xfff)
static bool hda_volatile_reg(struct device *dev, unsigned int reg)
{
struct hdac_device *codec = dev_to_hdac_dev(dev);
unsigned int verb = get_verb(reg);
switch (verb) {
case AC_VERB_GET_PROC_COEF:
return !codec->cache_coef;
case AC_VERB_GET_COEF_INDEX:
case AC_VERB_GET_PROC_STATE:
case AC_VERB_GET_POWER_STATE:
case AC_VERB_GET_PIN_SENSE:
case AC_VERB_GET_HDMI_DIP_SIZE:
case AC_VERB_GET_HDMI_ELDD:
case AC_VERB_GET_HDMI_DIP_INDEX:
case AC_VERB_GET_HDMI_DIP_DATA:
case AC_VERB_GET_HDMI_DIP_XMIT:
case AC_VERB_GET_HDMI_CP_CTRL:
case AC_VERB_GET_HDMI_CHAN_SLOT:
case AC_VERB_GET_DEVICE_SEL:
case AC_VERB_GET_DEVICE_LIST: /* read-only volatile */
return true;
}
return false;
}
static bool hda_writeable_reg(struct device *dev, unsigned int reg)
{
struct hdac_device *codec = dev_to_hdac_dev(dev);
unsigned int verb = get_verb(reg);
int i;
for (i = 0; i < codec->vendor_verbs.used; i++) {
unsigned int *v = snd_array_elem(&codec->vendor_verbs, i);
if (verb == *v)
return true;
}
if (codec->caps_overwriting)
return true;
switch (verb & 0xf00) {
case AC_VERB_GET_STREAM_FORMAT:
case AC_VERB_GET_AMP_GAIN_MUTE:
return true;
case AC_VERB_GET_PROC_COEF:
return codec->cache_coef;
case 0xf00:
break;
default:
return false;
}
switch (verb) {
case AC_VERB_GET_CONNECT_SEL:
case AC_VERB_GET_SDI_SELECT:
case AC_VERB_GET_PIN_WIDGET_CONTROL:
case AC_VERB_GET_UNSOLICITED_RESPONSE: /* only as SET_UNSOLICITED_ENABLE */
case AC_VERB_GET_BEEP_CONTROL:
case AC_VERB_GET_EAPD_BTLENABLE:
case AC_VERB_GET_DIGI_CONVERT_1:
case AC_VERB_GET_DIGI_CONVERT_2: /* only for beep control */
case AC_VERB_GET_VOLUME_KNOB_CONTROL:
case AC_VERB_GET_GPIO_MASK:
case AC_VERB_GET_GPIO_DIRECTION:
case AC_VERB_GET_GPIO_DATA: /* not for volatile read */
case AC_VERB_GET_GPIO_WAKE_MASK:
case AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK:
case AC_VERB_GET_GPIO_STICKY_MASK:
return true;
}
return false;
}
static bool hda_readable_reg(struct device *dev, unsigned int reg)
{
struct hdac_device *codec = dev_to_hdac_dev(dev);
unsigned int verb = get_verb(reg);
if (codec->caps_overwriting)
return true;
switch (verb) {
case AC_VERB_PARAMETERS:
case AC_VERB_GET_CONNECT_LIST:
case AC_VERB_GET_SUBSYSTEM_ID:
return true;
/* below are basically writable, but disabled for reducing unnecessary
* writes at sync
*/
case AC_VERB_GET_CONFIG_DEFAULT: /* usually just read */
case AC_VERB_GET_CONV: /* managed in PCM code */
case AC_VERB_GET_CVT_CHAN_COUNT: /* managed in HDMI CA code */
return true;
}
return hda_writeable_reg(dev, reg);
}
/*
* Stereo amp pseudo register:
* for making easier to handle the stereo volume control, we provide a
* fake register to deal both left and right channels by a single
* (pseudo) register access. A verb consisting of SET_AMP_GAIN with
* *both* SET_LEFT and SET_RIGHT bits takes a 16bit value, the lower 8bit
* for the left and the upper 8bit for the right channel.
*/
static bool is_stereo_amp_verb(unsigned int reg)
{
if (((reg >> 8) & 0x700) != AC_VERB_SET_AMP_GAIN_MUTE)
return false;
return (reg & (AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT)) ==
(AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT);
}
/* read a pseudo stereo amp register (16bit left+right) */
static int hda_reg_read_stereo_amp(struct hdac_device *codec,
unsigned int reg, unsigned int *val)
{
unsigned int left, right;
int err;
reg &= ~(AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT);
err = snd_hdac_exec_verb(codec, reg | AC_AMP_GET_LEFT, 0, &left);
if (err < 0)
return err;
err = snd_hdac_exec_verb(codec, reg | AC_AMP_GET_RIGHT, 0, &right);
if (err < 0)
return err;
*val = left | (right << 8);
return 0;
}
/* write a pseudo stereo amp register (16bit left+right) */
static int hda_reg_write_stereo_amp(struct hdac_device *codec,
unsigned int reg, unsigned int val)
{
int err;
unsigned int verb, left, right;
verb = AC_VERB_SET_AMP_GAIN_MUTE << 8;
if (reg & AC_AMP_GET_OUTPUT)
verb |= AC_AMP_SET_OUTPUT;
else
verb |= AC_AMP_SET_INPUT | ((reg & 0xf) << 8);
reg = (reg & ~0xfffff) | verb;
left = val & 0xff;
right = (val >> 8) & 0xff;
if (left == right) {
reg |= AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT;
return snd_hdac_exec_verb(codec, reg | left, 0, NULL);
}
err = snd_hdac_exec_verb(codec, reg | AC_AMP_SET_LEFT | left, 0, NULL);
if (err < 0)
return err;
err = snd_hdac_exec_verb(codec, reg | AC_AMP_SET_RIGHT | right, 0, NULL);
if (err < 0)
return err;
return 0;
}
/* read a pseudo coef register (16bit) */
static int hda_reg_read_coef(struct hdac_device *codec, unsigned int reg,
unsigned int *val)
{
unsigned int verb;
int err;
if (!codec->cache_coef)
return -EINVAL;
/* LSB 8bit = coef index */
verb = (reg & ~0xfff00) | (AC_VERB_SET_COEF_INDEX << 8);
err = snd_hdac_exec_verb(codec, verb, 0, NULL);
if (err < 0)
return err;
verb = (reg & ~0xfffff) | (AC_VERB_GET_COEF_INDEX << 8);
return snd_hdac_exec_verb(codec, verb, 0, val);
}
/* write a pseudo coef register (16bit) */
static int hda_reg_write_coef(struct hdac_device *codec, unsigned int reg,
unsigned int val)
{
unsigned int verb;
int err;
if (!codec->cache_coef)
return -EINVAL;
/* LSB 8bit = coef index */
verb = (reg & ~0xfff00) | (AC_VERB_SET_COEF_INDEX << 8);
err = snd_hdac_exec_verb(codec, verb, 0, NULL);
if (err < 0)
return err;
verb = (reg & ~0xfffff) | (AC_VERB_GET_COEF_INDEX << 8) |
(val & 0xffff);
return snd_hdac_exec_verb(codec, verb, 0, NULL);
}
static int hda_reg_read(void *context, unsigned int reg, unsigned int *val)
{
struct hdac_device *codec = context;
int verb = get_verb(reg);
int err;
if (!codec_is_running(codec) && verb != AC_VERB_GET_POWER_STATE)
return -EAGAIN;
reg |= (codec->addr << 28);
if (is_stereo_amp_verb(reg))
return hda_reg_read_stereo_amp(codec, reg, val);
if (verb == AC_VERB_GET_PROC_COEF)
return hda_reg_read_coef(codec, reg, val);
err = snd_hdac_exec_verb(codec, reg, 0, val);
if (err < 0)
return err;
/* special handling for asymmetric reads */
if (verb == AC_VERB_GET_POWER_STATE) {
if (*val & AC_PWRST_ERROR)
*val = -1;
else /* take only the actual state */
*val = (*val >> 4) & 0x0f;
}
return 0;
}
static int hda_reg_write(void *context, unsigned int reg, unsigned int val)
{
struct hdac_device *codec = context;
unsigned int verb;
int i, bytes, err;
reg &= ~0x00080000U; /* drop GET bit */
reg |= (codec->addr << 28);
verb = get_verb(reg);
if (!codec_is_running(codec) && verb != AC_VERB_SET_POWER_STATE)
return codec->lazy_cache ? 0 : -EAGAIN;
if (is_stereo_amp_verb(reg))
return hda_reg_write_stereo_amp(codec, reg, val);
if (verb == AC_VERB_SET_PROC_COEF)
return hda_reg_write_coef(codec, reg, val);
switch (verb & 0xf00) {
case AC_VERB_SET_AMP_GAIN_MUTE:
verb = AC_VERB_SET_AMP_GAIN_MUTE;
if (reg & AC_AMP_GET_LEFT)
verb |= AC_AMP_SET_LEFT >> 8;
else
verb |= AC_AMP_SET_RIGHT >> 8;
if (reg & AC_AMP_GET_OUTPUT) {
verb |= AC_AMP_SET_OUTPUT >> 8;
} else {
verb |= AC_AMP_SET_INPUT >> 8;
verb |= reg & 0xf;
}
break;
}
switch (verb) {
case AC_VERB_SET_DIGI_CONVERT_1:
bytes = 2;
break;
case AC_VERB_SET_CONFIG_DEFAULT_BYTES_0:
bytes = 4;
break;
default:
bytes = 1;
break;
}
for (i = 0; i < bytes; i++) {
reg &= ~0xfffff;
reg |= (verb + i) << 8 | ((val >> (8 * i)) & 0xff);
err = snd_hdac_exec_verb(codec, reg, 0, NULL);
if (err < 0)
return err;
}
return 0;
}
static const struct regmap_config hda_regmap_cfg = {
.name = "hdaudio",
.reg_bits = 32,
.val_bits = 32,
.max_register = 0xfffffff,
.writeable_reg = hda_writeable_reg,
.readable_reg = hda_readable_reg,
.volatile_reg = hda_volatile_reg,
.cache_type = REGCACHE_RBTREE,
.reg_read = hda_reg_read,
.reg_write = hda_reg_write,
.use_single_rw = true,
};
int snd_hdac_regmap_init(struct hdac_device *codec)
{
struct regmap *regmap;
regmap = regmap_init(&codec->dev, NULL, codec, &hda_regmap_cfg);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
codec->regmap = regmap;
snd_array_init(&codec->vendor_verbs, sizeof(unsigned int), 8);
return 0;
}
EXPORT_SYMBOL_GPL(snd_hdac_regmap_init);
void snd_hdac_regmap_exit(struct hdac_device *codec)
{
if (codec->regmap) {
regmap_exit(codec->regmap);
codec->regmap = NULL;
snd_array_free(&codec->vendor_verbs);
}
}
EXPORT_SYMBOL_GPL(snd_hdac_regmap_exit);
/**
* snd_hdac_regmap_add_vendor_verb - add a vendor-specific verb to regmap
* @codec: the codec object
* @verb: verb to allow accessing via regmap
*
* Returns zero for success or a negative error code.
*/
int snd_hdac_regmap_add_vendor_verb(struct hdac_device *codec,
unsigned int verb)
{
unsigned int *p = snd_array_new(&codec->vendor_verbs);
if (!p)
return -ENOMEM;
*p = verb | 0x800; /* set GET bit */
return 0;
}
EXPORT_SYMBOL_GPL(snd_hdac_regmap_add_vendor_verb);
/*
* helper functions
*/
/* write a pseudo-register value (w/o power sequence) */
static int reg_raw_write(struct hdac_device *codec, unsigned int reg,
unsigned int val)
{
if (!codec->regmap)
return hda_reg_write(codec, reg, val);
else
return regmap_write(codec->regmap, reg, val);
}
/**
* snd_hdac_regmap_write_raw - write a pseudo register with power mgmt
* @codec: the codec object
* @reg: pseudo register
* @val: value to write
*
* Returns zero if successful or a negative error code.
*/
int snd_hdac_regmap_write_raw(struct hdac_device *codec, unsigned int reg,
unsigned int val)
{
int err;
err = reg_raw_write(codec, reg, val);
if (err == -EAGAIN) {
snd_hdac_power_up_pm(codec);
err = reg_raw_write(codec, reg, val);
snd_hdac_power_down_pm(codec);
}
return err;
}
EXPORT_SYMBOL_GPL(snd_hdac_regmap_write_raw);
static int reg_raw_read(struct hdac_device *codec, unsigned int reg,
unsigned int *val)
{
if (!codec->regmap)
return hda_reg_read(codec, reg, val);
else
return regmap_read(codec->regmap, reg, val);
}
/**
* snd_hdac_regmap_read_raw - read a pseudo register with power mgmt
* @codec: the codec object
* @reg: pseudo register
* @val: pointer to store the read value
*
* Returns zero if successful or a negative error code.
*/
int snd_hdac_regmap_read_raw(struct hdac_device *codec, unsigned int reg,
unsigned int *val)
{
int err;
err = reg_raw_read(codec, reg, val);
if (err == -EAGAIN) {
snd_hdac_power_up_pm(codec);
err = reg_raw_read(codec, reg, val);
snd_hdac_power_down_pm(codec);
}
return err;
}
EXPORT_SYMBOL_GPL(snd_hdac_regmap_read_raw);
/**
* snd_hdac_regmap_update_raw - update a pseudo register with power mgmt
* @codec: the codec object
* @reg: pseudo register
* @mask: bit mask to udpate
* @val: value to update
*
* Returns zero if successful or a negative error code.
*/
int snd_hdac_regmap_update_raw(struct hdac_device *codec, unsigned int reg,
unsigned int mask, unsigned int val)
{
unsigned int orig;
int err;
val &= mask;
err = snd_hdac_regmap_read_raw(codec, reg, &orig);
if (err < 0)
return err;
val |= orig & ~mask;
if (val == orig)
return 0;
err = snd_hdac_regmap_write_raw(codec, reg, val);
if (err < 0)
return err;
return 1;
}
EXPORT_SYMBOL_GPL(snd_hdac_regmap_update_raw);

406
sound/hda/hdac_sysfs.c Normal file
View File

@ -0,0 +1,406 @@
/*
* sysfs support for HD-audio core device
*/
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/device.h>
#include <sound/core.h>
#include <sound/hdaudio.h>
#include "local.h"
struct hdac_widget_tree {
struct kobject *root;
struct kobject *afg;
struct kobject **nodes;
};
#define CODEC_ATTR(type) \
static ssize_t type##_show(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
struct hdac_device *codec = dev_to_hdac_dev(dev); \
return sprintf(buf, "0x%x\n", codec->type); \
} \
static DEVICE_ATTR_RO(type)
#define CODEC_ATTR_STR(type) \
static ssize_t type##_show(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
struct hdac_device *codec = dev_to_hdac_dev(dev); \
return sprintf(buf, "%s\n", \
codec->type ? codec->type : ""); \
} \
static DEVICE_ATTR_RO(type)
CODEC_ATTR(type);
CODEC_ATTR(vendor_id);
CODEC_ATTR(subsystem_id);
CODEC_ATTR(revision_id);
CODEC_ATTR(afg);
CODEC_ATTR(mfg);
CODEC_ATTR_STR(vendor_name);
CODEC_ATTR_STR(chip_name);
static struct attribute *hdac_dev_attrs[] = {
&dev_attr_type.attr,
&dev_attr_vendor_id.attr,
&dev_attr_subsystem_id.attr,
&dev_attr_revision_id.attr,
&dev_attr_afg.attr,
&dev_attr_mfg.attr,
&dev_attr_vendor_name.attr,
&dev_attr_chip_name.attr,
NULL
};
static struct attribute_group hdac_dev_attr_group = {
.attrs = hdac_dev_attrs,
};
const struct attribute_group *hdac_dev_attr_groups[] = {
&hdac_dev_attr_group,
NULL
};
/*
* Widget tree sysfs
*
* This is a tree showing the attributes of each widget. It appears like
* /sys/bus/hdaudioC0D0/widgets/04/caps
*/
struct widget_attribute;
struct widget_attribute {
struct attribute attr;
ssize_t (*show)(struct hdac_device *codec, hda_nid_t nid,
struct widget_attribute *attr, char *buf);
ssize_t (*store)(struct hdac_device *codec, hda_nid_t nid,
struct widget_attribute *attr,
const char *buf, size_t count);
};
static int get_codec_nid(struct kobject *kobj, struct hdac_device **codecp)
{
struct device *dev = kobj_to_dev(kobj->parent->parent);
int nid;
ssize_t ret;
ret = kstrtoint(kobj->name, 16, &nid);
if (ret < 0)
return ret;
*codecp = dev_to_hdac_dev(dev);
return nid;
}
static ssize_t widget_attr_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
struct widget_attribute *wid_attr =
container_of(attr, struct widget_attribute, attr);
struct hdac_device *codec;
int nid;
if (!wid_attr->show)
return -EIO;
nid = get_codec_nid(kobj, &codec);
if (nid < 0)
return nid;
return wid_attr->show(codec, nid, wid_attr, buf);
}
static ssize_t widget_attr_store(struct kobject *kobj, struct attribute *attr,
const char *buf, size_t count)
{
struct widget_attribute *wid_attr =
container_of(attr, struct widget_attribute, attr);
struct hdac_device *codec;
int nid;
if (!wid_attr->store)
return -EIO;
nid = get_codec_nid(kobj, &codec);
if (nid < 0)
return nid;
return wid_attr->store(codec, nid, wid_attr, buf, count);
}
static const struct sysfs_ops widget_sysfs_ops = {
.show = widget_attr_show,
.store = widget_attr_store,
};
static void widget_release(struct kobject *kobj)
{
kfree(kobj);
}
static struct kobj_type widget_ktype = {
.release = widget_release,
.sysfs_ops = &widget_sysfs_ops,
};
#define WIDGET_ATTR_RO(_name) \
struct widget_attribute wid_attr_##_name = __ATTR_RO(_name)
#define WIDGET_ATTR_RW(_name) \
struct widget_attribute wid_attr_##_name = __ATTR_RW(_name)
static ssize_t caps_show(struct hdac_device *codec, hda_nid_t nid,
struct widget_attribute *attr, char *buf)
{
return sprintf(buf, "0x%08x\n", get_wcaps(codec, nid));
}
static ssize_t pin_caps_show(struct hdac_device *codec, hda_nid_t nid,
struct widget_attribute *attr, char *buf)
{
if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
return 0;
return sprintf(buf, "0x%08x\n",
snd_hdac_read_parm(codec, nid, AC_PAR_PIN_CAP));
}
static ssize_t pin_cfg_show(struct hdac_device *codec, hda_nid_t nid,
struct widget_attribute *attr, char *buf)
{
unsigned int val;
if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
return 0;
if (snd_hdac_read(codec, nid, AC_VERB_GET_CONFIG_DEFAULT, 0, &val))
return 0;
return sprintf(buf, "0x%08x\n", val);
}
static bool has_pcm_cap(struct hdac_device *codec, hda_nid_t nid)
{
if (nid == codec->afg || nid == codec->mfg)
return true;
switch (get_wcaps_type(get_wcaps(codec, nid))) {
case AC_WID_AUD_OUT:
case AC_WID_AUD_IN:
return true;
default:
return false;
}
}
static ssize_t pcm_caps_show(struct hdac_device *codec, hda_nid_t nid,
struct widget_attribute *attr, char *buf)
{
if (!has_pcm_cap(codec, nid))
return 0;
return sprintf(buf, "0x%08x\n",
snd_hdac_read_parm(codec, nid, AC_PAR_PCM));
}
static ssize_t pcm_formats_show(struct hdac_device *codec, hda_nid_t nid,
struct widget_attribute *attr, char *buf)
{
if (!has_pcm_cap(codec, nid))
return 0;
return sprintf(buf, "0x%08x\n",
snd_hdac_read_parm(codec, nid, AC_PAR_STREAM));
}
static ssize_t amp_in_caps_show(struct hdac_device *codec, hda_nid_t nid,
struct widget_attribute *attr, char *buf)
{
if (nid != codec->afg && !(get_wcaps(codec, nid) & AC_WCAP_IN_AMP))
return 0;
return sprintf(buf, "0x%08x\n",
snd_hdac_read_parm(codec, nid, AC_PAR_AMP_IN_CAP));
}
static ssize_t amp_out_caps_show(struct hdac_device *codec, hda_nid_t nid,
struct widget_attribute *attr, char *buf)
{
if (nid != codec->afg && !(get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
return 0;
return sprintf(buf, "0x%08x\n",
snd_hdac_read_parm(codec, nid, AC_PAR_AMP_OUT_CAP));
}
static ssize_t power_caps_show(struct hdac_device *codec, hda_nid_t nid,
struct widget_attribute *attr, char *buf)
{
if (nid != codec->afg && !(get_wcaps(codec, nid) & AC_WCAP_POWER))
return 0;
return sprintf(buf, "0x%08x\n",
snd_hdac_read_parm(codec, nid, AC_PAR_POWER_STATE));
}
static ssize_t gpio_caps_show(struct hdac_device *codec, hda_nid_t nid,
struct widget_attribute *attr, char *buf)
{
return sprintf(buf, "0x%08x\n",
snd_hdac_read_parm(codec, nid, AC_PAR_GPIO_CAP));
}
static ssize_t connections_show(struct hdac_device *codec, hda_nid_t nid,
struct widget_attribute *attr, char *buf)
{
hda_nid_t list[32];
int i, nconns;
ssize_t ret = 0;
nconns = snd_hdac_get_connections(codec, nid, list, ARRAY_SIZE(list));
if (nconns <= 0)
return nconns;
for (i = 0; i < nconns; i++)
ret += sprintf(buf + ret, "%s0x%02x", i ? " " : "", list[i]);
ret += sprintf(buf + ret, "\n");
return ret;
}
static WIDGET_ATTR_RO(caps);
static WIDGET_ATTR_RO(pin_caps);
static WIDGET_ATTR_RO(pin_cfg);
static WIDGET_ATTR_RO(pcm_caps);
static WIDGET_ATTR_RO(pcm_formats);
static WIDGET_ATTR_RO(amp_in_caps);
static WIDGET_ATTR_RO(amp_out_caps);
static WIDGET_ATTR_RO(power_caps);
static WIDGET_ATTR_RO(gpio_caps);
static WIDGET_ATTR_RO(connections);
static struct attribute *widget_node_attrs[] = {
&wid_attr_caps.attr,
&wid_attr_pin_caps.attr,
&wid_attr_pin_cfg.attr,
&wid_attr_pcm_caps.attr,
&wid_attr_pcm_formats.attr,
&wid_attr_amp_in_caps.attr,
&wid_attr_amp_out_caps.attr,
&wid_attr_power_caps.attr,
&wid_attr_connections.attr,
NULL,
};
static struct attribute *widget_afg_attrs[] = {
&wid_attr_pcm_caps.attr,
&wid_attr_pcm_formats.attr,
&wid_attr_amp_in_caps.attr,
&wid_attr_amp_out_caps.attr,
&wid_attr_power_caps.attr,
&wid_attr_gpio_caps.attr,
NULL,
};
static const struct attribute_group widget_node_group = {
.attrs = widget_node_attrs,
};
static const struct attribute_group widget_afg_group = {
.attrs = widget_afg_attrs,
};
static void free_widget_node(struct kobject *kobj,
const struct attribute_group *group)
{
if (kobj) {
sysfs_remove_group(kobj, group);
kobject_put(kobj);
}
}
static void widget_tree_free(struct hdac_device *codec)
{
struct hdac_widget_tree *tree = codec->widgets;
struct kobject **p;
if (!tree)
return;
free_widget_node(tree->afg, &widget_afg_group);
if (tree->nodes) {
for (p = tree->nodes; *p; p++)
free_widget_node(*p, &widget_node_group);
kfree(tree->nodes);
}
if (tree->root)
kobject_put(tree->root);
kfree(tree);
codec->widgets = NULL;
}
static int add_widget_node(struct kobject *parent, hda_nid_t nid,
const struct attribute_group *group,
struct kobject **res)
{
struct kobject *kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
int err;
if (!kobj)
return -ENOMEM;
kobject_init(kobj, &widget_ktype);
err = kobject_add(kobj, parent, "%02x", nid);
if (err < 0)
return err;
err = sysfs_create_group(kobj, group);
if (err < 0) {
kobject_put(kobj);
return err;
}
*res = kobj;
return 0;
}
static int widget_tree_create(struct hdac_device *codec)
{
struct hdac_widget_tree *tree;
int i, err;
hda_nid_t nid;
tree = codec->widgets = kzalloc(sizeof(*tree), GFP_KERNEL);
if (!tree)
return -ENOMEM;
tree->root = kobject_create_and_add("widgets", &codec->dev.kobj);
if (!tree->root)
return -ENOMEM;
tree->nodes = kcalloc(codec->num_nodes + 1, sizeof(*tree->nodes),
GFP_KERNEL);
if (!tree->nodes)
return -ENOMEM;
for (i = 0, nid = codec->start_nid; i < codec->num_nodes; i++, nid++) {
err = add_widget_node(tree->root, nid, &widget_node_group,
&tree->nodes[i]);
if (err < 0)
return err;
}
if (codec->afg) {
err = add_widget_node(tree->root, codec->afg,
&widget_afg_group, &tree->afg);
if (err < 0)
return err;
}
kobject_uevent(tree->root, KOBJ_CHANGE);
return 0;
}
int hda_widget_sysfs_init(struct hdac_device *codec)
{
int err;
err = widget_tree_create(codec);
if (err < 0) {
widget_tree_free(codec);
return err;
}
return 0;
}
void hda_widget_sysfs_exit(struct hdac_device *codec)
{
widget_tree_free(codec);
}

23
sound/hda/local.h Normal file
View File

@ -0,0 +1,23 @@
/*
* Local helper macros and functions for HD-audio core drivers
*/
#ifndef __HDAC_LOCAL_H
#define __HDAC_LOCAL_H
#define get_wcaps(codec, nid) \
snd_hdac_read_parm(codec, nid, AC_PAR_AUDIO_WIDGET_CAP)
/* get the widget type from widget capability bits */
static inline int get_wcaps_type(unsigned int wcaps)
{
if (!wcaps)
return -1; /* invalid type */
return (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
}
extern const struct attribute_group *hdac_dev_attr_groups[];
int hda_widget_sysfs_init(struct hdac_device *codec);
void hda_widget_sysfs_exit(struct hdac_device *codec);
#endif /* __HDAC_LOCAL_H */

6
sound/hda/trace.c Normal file
View File

@ -0,0 +1,6 @@
/*
* tracepoint definitions for HD-audio core drivers
*/
#define CREATE_TRACE_POINTS
#include "trace.h"

62
sound/hda/trace.h Normal file
View File

@ -0,0 +1,62 @@
#undef TRACE_SYSTEM
#define TRACE_SYSTEM hda
#if !defined(__HDAC_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
#define __HDAC_TRACE_H
#include <linux/tracepoint.h>
#include <linux/device.h>
#include <sound/hdaudio.h>
#ifndef HDAC_MSG_MAX
#define HDAC_MSG_MAX 500
#endif
struct hdac_bus;
struct hdac_codec;
TRACE_EVENT(hda_send_cmd,
TP_PROTO(struct hdac_bus *bus, unsigned int cmd),
TP_ARGS(bus, cmd),
TP_STRUCT__entry(__dynamic_array(char, msg, HDAC_MSG_MAX)),
TP_fast_assign(
snprintf(__get_str(msg), HDAC_MSG_MAX,
"[%s:%d] val=0x%08x",
dev_name((bus)->dev), (cmd) >> 28, cmd);
),
TP_printk("%s", __get_str(msg))
);
TRACE_EVENT(hda_get_response,
TP_PROTO(struct hdac_bus *bus, unsigned int addr, unsigned int res),
TP_ARGS(bus, addr, res),
TP_STRUCT__entry(__dynamic_array(char, msg, HDAC_MSG_MAX)),
TP_fast_assign(
snprintf(__get_str(msg), HDAC_MSG_MAX,
"[%s:%d] val=0x%08x",
dev_name((bus)->dev), addr, res);
),
TP_printk("%s", __get_str(msg))
);
TRACE_EVENT(hda_unsol_event,
TP_PROTO(struct hdac_bus *bus, u32 res, u32 res_ex),
TP_ARGS(bus, res, res_ex),
TP_STRUCT__entry(__dynamic_array(char, msg, HDAC_MSG_MAX)),
TP_fast_assign(
snprintf(__get_str(msg), HDAC_MSG_MAX,
"[%s:%d] res=0x%08x, res_ex=0x%08x",
dev_name((bus)->dev), res_ex & 0x0f, res, res_ex);
),
TP_printk("%s", __get_str(msg))
);
#endif /* __HDAC_TRACE_H */
/* This part must be outside protection */
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE trace
#include <trace/define_trace.h>

View File

@ -73,7 +73,7 @@ int snd_ak4113_create(struct snd_card *card, ak4113_read_t *read,
void *private_data, struct ak4113 **r_ak4113)
{
struct ak4113 *chip;
int err = 0;
int err;
unsigned char reg;
static struct snd_device_ops ops = {
.dev_free = snd_ak4113_dev_free,
@ -109,7 +109,7 @@ int snd_ak4113_create(struct snd_card *card, ak4113_read_t *read,
__fail:
snd_ak4113_free(chip);
return err < 0 ? err : -EIO;
return err;
}
EXPORT_SYMBOL_GPL(snd_ak4113_create);

View File

@ -34,8 +34,9 @@ MODULE_LICENSE("GPL");
/*
* create a new hardware dependent device for Emu8000
*/
static int snd_emu8000_new_device(struct snd_seq_device *dev)
static int snd_emu8000_probe(struct device *_dev)
{
struct snd_seq_device *dev = to_seq_dev(_dev);
struct snd_emu8000 *hw;
struct snd_emux *emu;
@ -93,8 +94,9 @@ static int snd_emu8000_new_device(struct snd_seq_device *dev)
/*
* free all resources
*/
static int snd_emu8000_delete_device(struct snd_seq_device *dev)
static int snd_emu8000_remove(struct device *_dev)
{
struct snd_seq_device *dev = to_seq_dev(_dev);
struct snd_emu8000 *hw;
if (dev->driver_data == NULL)
@ -114,21 +116,14 @@ static int snd_emu8000_delete_device(struct snd_seq_device *dev)
* INIT part
*/
static int __init alsa_emu8000_init(void)
{
static struct snd_seq_dev_ops ops = {
snd_emu8000_new_device,
snd_emu8000_delete_device,
};
return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_EMU8000, &ops,
sizeof(struct snd_emu8000*));
}
static struct snd_seq_driver emu8000_driver = {
.driver = {
.name = KBUILD_MODNAME,
.probe = snd_emu8000_probe,
.remove = snd_emu8000_remove,
},
.id = SNDRV_SEQ_DEV_ID_EMU8000,
.argsize = sizeof(struct snd_emu8000 *),
};
static void __exit alsa_emu8000_exit(void)
{
snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_EMU8000);
}
module_init(alsa_emu8000_init)
module_exit(alsa_emu8000_exit)
module_snd_seq_driver(emu8000_driver);

View File

@ -79,13 +79,13 @@ wavefront_fx_memset (snd_wavefront_t *dev,
if (page < 0 || page > 7) {
snd_printk ("FX memset: "
"page must be >= 0 and <= 7\n");
return -(EINVAL);
return -EINVAL;
}
if (addr < 0 || addr > 0x7f) {
snd_printk ("FX memset: "
"addr must be >= 0 and <= 7f\n");
return -(EINVAL);
return -EINVAL;
}
if (cnt == 1) {
@ -118,7 +118,7 @@ wavefront_fx_memset (snd_wavefront_t *dev,
snd_printk ("FX memset "
"(0x%x, 0x%x, 0x%lx, %d) incomplete\n",
page, addr, (unsigned long) data, cnt);
return -(EIO);
return -EIO;
}
}

View File

@ -793,7 +793,7 @@ wavefront_send_patch (snd_wavefront_t *dev, wavefront_patch_info *header)
if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_PATCH, NULL, buf)) {
snd_printk ("download patch failed\n");
return -(EIO);
return -EIO;
}
return (0);
@ -831,7 +831,7 @@ wavefront_send_program (snd_wavefront_t *dev, wavefront_patch_info *header)
if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_PROGRAM, NULL, buf)) {
snd_printk ("download patch failed\n");
return -(EIO);
return -EIO;
}
return (0);
@ -952,7 +952,7 @@ wavefront_send_sample (snd_wavefront_t *dev,
if (skip > 0 && header->hdr.s.SampleResolution != LINEAR_16BIT) {
snd_printk ("channel selection only "
"possible on 16-bit samples");
return -(EINVAL);
return -EINVAL;
}
switch (skip) {
@ -1049,7 +1049,7 @@ wavefront_send_sample (snd_wavefront_t *dev,
NULL, sample_hdr)) {
snd_printk ("sample %sdownload refused.\n",
header->size ? "" : "header ");
return -(EIO);
return -EIO;
}
if (header->size == 0) {
@ -1075,7 +1075,7 @@ wavefront_send_sample (snd_wavefront_t *dev,
if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_BLOCK, NULL, NULL)) {
snd_printk ("download block "
"request refused.\n");
return -(EIO);
return -EIO;
}
for (i = 0; i < blocksize; i++) {
@ -1135,12 +1135,12 @@ wavefront_send_sample (snd_wavefront_t *dev,
if (dma_ack == -1) {
snd_printk ("upload sample "
"DMA ack timeout\n");
return -(EIO);
return -EIO;
} else {
snd_printk ("upload sample "
"DMA ack error 0x%x\n",
dma_ack);
return -(EIO);
return -EIO;
}
}
}
@ -1181,7 +1181,7 @@ wavefront_send_alias (snd_wavefront_t *dev, wavefront_patch_info *header)
if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_SAMPLE_ALIAS, NULL, alias_hdr)) {
snd_printk ("download alias failed.\n");
return -(EIO);
return -EIO;
}
dev->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_ALIAS);
@ -1232,7 +1232,7 @@ wavefront_send_multisample (snd_wavefront_t *dev, wavefront_patch_info *header)
msample_hdr)) {
snd_printk ("download of multisample failed.\n");
kfree(msample_hdr);
return -(EIO);
return -EIO;
}
dev->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_MULTISAMPLE);
@ -1254,7 +1254,7 @@ wavefront_fetch_multisample (snd_wavefront_t *dev,
if (snd_wavefront_cmd (dev, WFC_UPLOAD_MULTISAMPLE, log_ns, number)) {
snd_printk ("upload multisample failed.\n");
return -(EIO);
return -EIO;
}
DPRINT (WF_DEBUG_DATA, "msample %d has %d samples\n",
@ -1273,14 +1273,14 @@ wavefront_fetch_multisample (snd_wavefront_t *dev,
if ((val = wavefront_read (dev)) == -1) {
snd_printk ("upload multisample failed "
"during sample loop.\n");
return -(EIO);
return -EIO;
}
d[0] = val;
if ((val = wavefront_read (dev)) == -1) {
snd_printk ("upload multisample failed "
"during sample loop.\n");
return -(EIO);
return -EIO;
}
d[1] = val;
@ -1315,7 +1315,7 @@ wavefront_send_drum (snd_wavefront_t *dev, wavefront_patch_info *header)
if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_EDRUM_PROGRAM, NULL, drumbuf)) {
snd_printk ("download drum failed.\n");
return -(EIO);
return -EIO;
}
return (0);

View File

@ -633,19 +633,25 @@ static int au1000_ac97_probe(struct platform_device *pdev)
au1000->stream[PLAYBACK] = kmalloc(sizeof(struct audio_stream),
GFP_KERNEL);
if (!au1000->stream[PLAYBACK])
if (!au1000->stream[PLAYBACK]) {
err = -ENOMEM;
goto out;
}
au1000->stream[PLAYBACK]->dma = -1;
au1000->stream[CAPTURE] = kmalloc(sizeof(struct audio_stream),
GFP_KERNEL);
if (!au1000->stream[CAPTURE])
if (!au1000->stream[CAPTURE]) {
err = -ENOMEM;
goto out;
}
au1000->stream[CAPTURE]->dma = -1;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r)
if (!r) {
err = -ENODEV;
goto out;
}
err = -EBUSY;
au1000->ac97_res_port = request_mem_region(r->start, resource_size(r),

View File

@ -58,13 +58,13 @@ int sound_install_audiodrv(int vers, char *name, struct audio_driver *driver,
if (vers != AUDIO_DRIVER_VERSION || driver_size > sizeof(struct audio_driver)) {
printk(KERN_ERR "Sound: Incompatible audio driver for %s\n", name);
return -(EINVAL);
return -EINVAL;
}
num = sound_alloc_audiodev();
if (num == -1) {
printk(KERN_ERR "sound: Too many audio drivers\n");
return -(EBUSY);
return -EBUSY;
}
d = (struct audio_driver *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct audio_driver)));
sound_nblocks++;
@ -79,7 +79,7 @@ int sound_install_audiodrv(int vers, char *name, struct audio_driver *driver,
if (d == NULL || op == NULL) {
printk(KERN_ERR "Sound: Can't allocate driver for (%s)\n", name);
sound_unload_audiodev(num);
return -(ENOMEM);
return -ENOMEM;
}
init_waitqueue_head(&op->in_sleeper);
init_waitqueue_head(&op->out_sleeper);

View File

@ -666,7 +666,7 @@ static int opl3_start_note (int dev, int voice, int note, int volume)
opl3_command(map->ioaddr, FNUM_LOW + map->voice_num, data);
data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3);
devc->voc[voice].keyon_byte = data;
devc->voc[voice].keyon_byte = data;
opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, data);
if (voice_mode == 4)
opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num + 3, data);
@ -717,7 +717,7 @@ static void freq_to_fnum (int freq, int *block, int *fnum)
static void opl3_command (int io_addr, unsigned int addr, unsigned int val)
{
int i;
int i;
/*
* The original 2-OP synth requires a quite long delay after writing to a

View File

@ -604,7 +604,7 @@ static void ess_audio_output_block_audio2
ess_chgmixer (devc, 0x78, 0x03, 0x03); /* Go */
devc->irq_mode_16 = IMODE_OUTPUT;
devc->intr_active_16 = 1;
devc->intr_active_16 = 1;
}
static void ess_audio_output_block
@ -1183,17 +1183,12 @@ FKS_test (devc);
chip = "ES1688";
}
printk ( KERN_INFO "ESS chip %s %s%s\n"
, chip
, ( devc->sbmo.esstype == ESSTYPE_DETECT || devc->sbmo.esstype == ESSTYPE_LIKE20
? "detected"
: "specified"
)
, ( devc->sbmo.esstype == ESSTYPE_LIKE20
? " (kernel 2.0 compatible)"
: ""
)
);
printk(KERN_INFO "ESS chip %s %s%s\n", chip,
(devc->sbmo.esstype == ESSTYPE_DETECT ||
devc->sbmo.esstype == ESSTYPE_LIKE20) ?
"detected" : "specified",
devc->sbmo.esstype == ESSTYPE_LIKE20 ?
" (kernel 2.0 compatible)" : "");
sprintf(name,"ESS %s AudioDrive (rev %d)", chip, ess_minor & 0x0f);
} else {

View File

@ -179,14 +179,14 @@ void sb_dsp_midi_init(sb_devc * devc, struct module *owner)
{
printk(KERN_WARNING "Sound Blaster: failed to allocate MIDI memory.\n");
sound_unload_mididev(dev);
return;
return;
}
memcpy((char *) midi_devs[dev], (char *) &sb_midi_operations,
sizeof(struct midi_operations));
if (owner)
midi_devs[dev]->owner = owner;
midi_devs[dev]->owner = owner;
midi_devs[dev]->devc = devc;

View File

@ -50,29 +50,24 @@ tmr2ticks(int tmr_value)
static void
poll_def_tmr(unsigned long dummy)
{
if (!opened)
return;
def_tmr.expires = (1) + jiffies;
add_timer(&def_tmr);
if (opened)
{
if (!tmr_running)
return;
{
def_tmr.expires = (1) + jiffies;
add_timer(&def_tmr);
}
spin_lock(&lock);
tmr_ctr++;
curr_ticks = ticks_offs + tmr2ticks(tmr_ctr);
if (tmr_running)
{
spin_lock(&lock);
tmr_ctr++;
curr_ticks = ticks_offs + tmr2ticks(tmr_ctr);
if (curr_ticks >= next_event_time) {
next_event_time = (unsigned long) -1;
sequencer_timer(0);
}
if (curr_ticks >= next_event_time)
{
next_event_time = (unsigned long) -1;
sequencer_timer(0);
}
spin_unlock(&lock);
}
}
spin_unlock(&lock);
}
static void

View File

@ -49,13 +49,13 @@ static int v_midi_open (int dev, int mode,
unsigned long flags;
if (devc == NULL)
return -(ENXIO);
return -ENXIO;
spin_lock_irqsave(&devc->lock,flags);
if (devc->opened)
{
spin_unlock_irqrestore(&devc->lock,flags);
return -(EBUSY);
return -EBUSY;
}
devc->opened = 1;
spin_unlock_irqrestore(&devc->lock,flags);

View File

@ -177,6 +177,7 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = {
{ 0x54524123, 0xffffffff, "TR28602", NULL, NULL }, // only guess --jk [TR28023 = eMicro EM28023 (new CT1297)]
{ 0x54584e03, 0xffffffff, "TLV320AIC27", NULL, NULL },
{ 0x54584e20, 0xffffffff, "TLC320AD9xC", NULL, NULL },
{ 0x56494120, 0xfffffff0, "VIA1613", patch_vt1613, NULL },
{ 0x56494161, 0xffffffff, "VIA1612A", NULL, NULL }, // modified ICE1232 with S/PDIF
{ 0x56494170, 0xffffffff, "VIA1617A", patch_vt1617a, NULL }, // modified VT1616 with S/PDIF
{ 0x56494182, 0xffffffff, "VIA1618", patch_vt1618, NULL },
@ -2901,7 +2902,8 @@ static int apply_quirk_str(struct snd_ac97 *ac97, const char *typestr)
* Return: Zero if successful, or a negative error code on failure.
*/
int snd_ac97_tune_hardware(struct snd_ac97 *ac97, struct ac97_quirk *quirk, const char *override)
int snd_ac97_tune_hardware(struct snd_ac97 *ac97,
const struct ac97_quirk *quirk, const char *override)
{
int result;

View File

@ -3351,6 +3351,33 @@ static int patch_cm9780(struct snd_ac97 *ac97)
return 0;
}
/*
* VIA VT1613 codec
*/
static const struct snd_kcontrol_new snd_ac97_controls_vt1613[] = {
AC97_SINGLE("DC Offset removal", 0x5a, 10, 1, 0),
};
static int patch_vt1613_specific(struct snd_ac97 *ac97)
{
return patch_build_controls(ac97, &snd_ac97_controls_vt1613[0],
ARRAY_SIZE(snd_ac97_controls_vt1613));
};
static const struct snd_ac97_build_ops patch_vt1613_ops = {
.build_specific = patch_vt1613_specific
};
static int patch_vt1613(struct snd_ac97 *ac97)
{
ac97->build_ops = &patch_vt1613_ops;
ac97->flags |= AC97_HAS_NO_VIDEO;
ac97->caps |= AC97_BC_HEADPHONE;
return 0;
}
/*
* VIA VT1616 codec
*/

View File

@ -747,7 +747,7 @@ snd_ad1889_proc_init(struct snd_ad1889 *chip)
snd_info_set_text_ops(entry, chip, snd_ad1889_proc_read);
}
static struct ac97_quirk ac97_quirks[] = {
static const struct ac97_quirk ac97_quirks[] = {
{
.subvendor = 0x11d4, /* AD */
.subdevice = 0x1889, /* AD1889 */

View File

@ -2376,7 +2376,7 @@ static int snd_asihpi_cmode_add(struct snd_card_asihpi *asihpi,
/*------------------------------------------------------------
Sampleclock source controls
------------------------------------------------------------*/
static const char const *sampleclock_sources[] = {
static const char * const sampleclock_sources[] = {
"N/A", "Local PLL", "Digital Sync", "Word External", "Word Header",
"SMPTE", "Digital1", "Auto", "Network", "Invalid",
"Prev Module", "BLU-Link",

View File

@ -1390,7 +1390,7 @@ static irqreturn_t snd_atiixp_interrupt(int irq, void *dev_id)
* ac97 mixer section
*/
static struct ac97_quirk ac97_quirks[] = {
static const struct ac97_quirk ac97_quirks[] = {
{
.subvendor = 0x103c,
.subdevice = 0x006b,

View File

@ -1385,8 +1385,8 @@ snd_azf3328_ctrl_codec_activity(struct snd_azf3328 *chip,
.running)
&& (!chip->codecs[peer_codecs[codec_type].other2]
.running));
}
if (call_function)
}
if (call_function)
snd_azf3328_ctrl_enable_codecs(chip, enable);
/* ...and adjust clock, too
@ -2126,7 +2126,8 @@ static struct snd_pcm_ops snd_azf3328_i2s_out_ops = {
static int
snd_azf3328_pcm(struct snd_azf3328 *chip)
{
enum { AZF_PCMDEV_STD, AZF_PCMDEV_I2S_OUT, NUM_AZF_PCMDEVS }; /* pcm devices */
/* pcm devices */
enum { AZF_PCMDEV_STD, AZF_PCMDEV_I2S_OUT, NUM_AZF_PCMDEVS };
struct snd_pcm *pcm;
int err;

View File

@ -2062,7 +2062,7 @@ static int snd_cmipci_get_volume(struct snd_kcontrol *kcontrol,
val = (snd_cmipci_mixer_read(cm, reg.right_reg) >> reg.right_shift) & reg.mask;
if (reg.invert)
val = reg.mask - val;
ucontrol->value.integer.value[1] = val;
ucontrol->value.integer.value[1] = val;
}
spin_unlock_irq(&cm->reg_lock);
return 0;

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