提交 d0a3997c 编写于 作者: L Linus Torvalds

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
  ...
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"
......
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>;
};
......@@ -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.
......
* 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>;
};
......@@ -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";
};
};
......
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>;
};
};
* 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>;
};
......@@ -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 {
......
......@@ -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
......
......@@ -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
......
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
......@@ -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
......
......@@ -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",
......
......@@ -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
......
......@@ -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);
/*
......
......@@ -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
......
......@@ -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,
......
......@@ -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);
......
/*
* 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 */
/*
* 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 */
......@@ -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;
......
......@@ -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 */
......@@ -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;
......
......@@ -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
......
......@@ -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
......
......@@ -16,7 +16,6 @@
struct asoc_simple_dai {
const char *name;
unsigned int fmt;
unsigned int sysclk;
int slots;
int slot_width;
......
......@@ -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;
......
......@@ -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,
......
......@@ -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
......
......@@ -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)
......
......@@ -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)
......
......@@ -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;
......
......@@ -23,8 +23,7 @@
#define _UAPI__SOUND_EMU10K1_H
#include <linux/types.h>
#include <sound/asound.h>
/*
* ---- FX8010 ----
......
......@@ -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
......
......@@ -76,6 +76,8 @@ source "sound/isa/Kconfig"
source "sound/pci/Kconfig"
source "sound/hda/Kconfig"
source "sound/ppc/Kconfig"
source "sound/aoa/Kconfig"
......
......@@ -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
......
......@@ -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" },
{ }
};
......
......@@ -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 @@ static int snd_ctl_remove_user_ctl(struct snd_ctl_file * file,
*
* 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;
}
......
......@@ -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);
}
/*
......
......@@ -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);
......
......@@ -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) {
......
......@@ -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) {
......
......@@ -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;
}
......
......@@ -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;
}
}
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;
}
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;
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;
}
......
......@@ -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:
......
......@@ -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)
......
......@@ -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 ((runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK) &&
(substream->ops->wall_clock))
substream->ops->wall_clock(substream, &audio_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);
/* 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);
}
......
......@@ -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:
......
......@@ -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);
......
......@@ -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();
......
......@@ -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;
......
......@@ -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;
......
......@@ -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;
}
......
......@@ -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);
......
......@@ -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);
......
......@@ -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;
......
......@@ -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)
struct ops_list {
char id[ID_LEN]; /* driver id */
int driver; /* driver state */
int used; /* reference counter */
int argsize; /* argument size */
/* 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 */
};
/*
* 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);
return strcmp(sdrv->id, sdev->id) == 0 &&
sdrv->argsize == sdev->argsize;
}
static LIST_HEAD(opslist);
static int num_ops;
static DEFINE_MUTEX(ops_mutex);
#ifdef CONFIG_PROC_FS
static struct snd_info_entry *info_entry;
#endif
static struct bus_type snd_seq_bus_type = {
.name = "snd_seq",
.match = snd_seq_bus_match,
};
/*
* prototypes
* proc interface -- just for compatibility
*/
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 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);
#ifdef CONFIG_PROC_FS
static struct snd_info_entry *info_entry;
/*
* show all drivers and their status
*/
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;
snd_iprintf(buffer, "snd-%s,%s,%d\n", sdev->id,
dev->driver ? "loaded" : "empty",
dev->driver ? 1 : 0);
return 0;
}
#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)
{
atomic_inc(&snd_seq_in_init);
}
void snd_seq_autoload_unlock(void)
static int request_seq_drv(struct device *dev, void *data)
{
atomic_dec(&snd_seq_in_init);
struct snd_seq_device *sdev = to_seq_dev(dev);
if (!dev->driver)
request_module("snd-%s", sdev->id);
return 0;
}
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,384 +134,143 @@ 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_device_load_drivers(void)
void snd_seq_autoload_exit(void)
{
#ifdef CONFIG_MODULES
queue_autoload_drivers();
flush_work(&autoload_work);
#endif
atomic_inc(&snd_seq_in_init);
}
EXPORT_SYMBOL(snd_seq_autoload_exit);
/*
* register a sequencer device
* card = card info
* device = device number (if any)
* 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)
void snd_seq_device_load_drivers(void)
{
struct snd_seq_device *dev;
struct ops_list *ops;
int err;
static struct snd_device_ops dops = {
.dev_free = snd_seq_device_dev_free,
.dev_register = snd_seq_device_dev_register,
.dev_disconnect = snd_seq_device_dev_disconnect,
};
if (result)
*result = NULL;
if (snd_BUG_ON(!id))
return -EINVAL;
ops = find_driver(id, 1);
if (ops == NULL)
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->argsize = argsize;
dev->status = SNDRV_SEQ_DEVICE_FREE;
/* 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);
return err;
}
try_autoload(ops);
unlock_driver(ops);
if (result)
*result = dev;
return 0;
queue_autoload_drivers();
flush_work(&autoload_work);
}
EXPORT_SYMBOL(snd_seq_device_load_drivers);
#else
#define queue_autoload_drivers() /* NOP */
#endif
/*
* free the existing device
* device management
*/
static int snd_seq_device_free(struct snd_seq_device *dev)
{
struct ops_list *ops;
if (snd_BUG_ON(!dev))
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;
}
static int snd_seq_device_dev_free(struct snd_device *device)
{
struct snd_seq_device *dev = device->device_data;
return snd_seq_device_free(dev);
put_device(&dev->dev);
return 0;
}
/*
* 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);
int err;
unlock_driver(ops);
err = device_add(&dev->dev);
if (err < 0)
return err;
if (!dev->dev.driver)
queue_autoload_drivers();
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);
device_del(&dev->dev);
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)
static void snd_seq_dev_release(struct device *dev)
{
struct ops_list *ops;
struct snd_seq_device *dev;
struct snd_seq_device *sdev = to_seq_dev(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;
if (sdev->private_free)
sdev->private_free(sdev);
kfree(sdev);
}
/*
* 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
* register a sequencer device
* card = card info
* device = device number (if any)
* id = id of driver
* result = return pointer (NULL allowed if unnecessary)
*/
int snd_seq_device_unregister_driver(char *id)
int snd_seq_device_new(struct snd_card *card, int device, const char *id,
int argsize, struct snd_seq_device **result)
{
struct ops_list *ops;
struct snd_seq_device *dev;
int err;
static struct snd_device_ops dops = {
.dev_free = snd_seq_device_dev_free,
.dev_register = snd_seq_device_dev_register,
.dev_disconnect = snd_seq_device_dev_disconnect,
};
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);
if (result)
*result = NULL;
/* remove empty driver entries */
remove_drivers();
if (snd_BUG_ON(!id))
return -EINVAL;
return 0;
}
dev = kzalloc(sizeof(*dev) + argsize, GFP_KERNEL);
if (!dev)
return -ENOMEM;
/* set up device info */
dev->card = card;
dev->device = device;
dev->id = id;
dev->argsize = argsize;
/*
* 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);
}
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);
/*
* 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);
/* add this device to the list */
err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops);
if (err < 0) {
put_device(&dev->dev);
return err;
}
if (result)
*result = dev;
return 0;
}
EXPORT_SYMBOL(snd_seq_device_new);
/*
* release the device - call free_device operator
* driver registration
*/
static int free_device(struct snd_seq_device *dev, struct ops_list *ops)
int __snd_seq_driver_register(struct snd_seq_driver *drv, struct module *mod)
{
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);
if (WARN_ON(!drv->driver.name || !drv->id))
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;
drv->driver.bus = &snd_seq_bus_type;
drv->driver.owner = mod;
return driver_register(&drv->driver);
}
EXPORT_SYMBOL_GPL(__snd_seq_driver_register);
/*
* find the matching driver with given id
*/
static struct ops_list * find_driver(char *id, int create_if_empty)
void snd_seq_driver_unregister(struct snd_seq_driver *drv)
{
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;
driver_unregister(&drv->driver);
}
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
......@@ -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)
......
......@@ -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) {
......
......@@ -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;
......
......@@ -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);
......@@ -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;
......
......@@ -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;
......
......@@ -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);
......
......@@ -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 */
......
......@@ -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;
......
......@@ -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;
......
......@@ -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
};
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);
}
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 *),
};
module_init(alsa_opl3_seq_init)
module_exit(alsa_opl3_seq_exit)
module_snd_seq_driver(opl3_seq_driver);
......@@ -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
};
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);
}
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 *),
};
module_init(alsa_opl4_synth_init)
module_exit(alsa_opl4_synth_exit)
module_snd_seq_driver(opl4_seq_driver);
......@@ -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.
*/
......
......@@ -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
......
config SND_HDA_CORE
tristate
select REGMAP
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
/*
* 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);
/*
* 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);
/*
* 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);
/*
* 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;
}
此差异已折叠。
此差异已折叠。
/*
* 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 */
/*
* tracepoint definitions for HD-audio core drivers
*/
#define CREATE_TRACE_POINTS
#include "trace.h"
#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>
......@@ -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);
......
......@@ -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 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)
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 *),
};
module_snd_seq_driver(emu8000_driver);
......@@ -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;
}
}
......
......@@ -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),
......
......@@ -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);
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册