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

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

Pull sound updates from Takashi Iwai:
 "There are no too intrusive changes in this update batch.  The biggest
  LOC is found in the new DICE driver, and other small changes are
  scattered over the whole sound subtree (which is a common pattern).

  Below are highlights:

   - ALSA core:
     * Memory allocation support with genpool
     * Fix blocking in drain ioctl of compress_offload

   - HD-audio:
     * Improved AMD HDMI supports
     * Intel HDMI detection improvements
     * thinkpad_acpi mute-key integration
     * New PCI ID, New ALC255,285,293 codecs, CX20952

   - USB-audio:
     * New buffer size management
     * Clean up endpoint handling codes

   - ASoC:
     * Further work on the dmaengine helpers, including support for
       configuring the parameters for DMA by reading the capabilities of
       the DMA controller which removes some guesswork and magic numbers
       from drivers.
     * A refresh of the documentation.
     * Conversions of many drivers to direct regmap API usage in order
       to allow the ASoC level register I/O code to be removed, this
       will hopefully be completed by v3.14.
     * Support for using async register I/O in DAPM, reducing the time
       taken to implement power transitions on systems that support it.

   - Firewire: DICE driver

   - Lots of small fixes for bugs reported by Coverity"

* tag 'sound-3.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (382 commits)
  ALSA: hda/realtek - Add new codec ALC255/ALC3234 UAJ supported
  ALSA: hda - Apply MacBook fixups for CS4208 correctly
  ASoC: fsl: imx-wm8962: remove an unneeded check
  ASoC: fsl: imx-pcm-fiq: Remove unused 'runtime' variable
  ALSA: hda/realtek - Make fixup regs persist after resume
  ALSA: hda_intel: ratelimit "spurious response" message
  ASoC: generic-dmaengine-pcm: Use SNDRV_DMA_TYPE_DEV_IRAM as default
  ASoC: dapm: Use WARN_ON() instead of BUG_ON()
  ASoC: wm_adsp: Fix BUG_ON() and WARN_ON() usages
  ASoC: Replace BUG() with WARN()
  ASoC: wm_hubs: Replace BUG() with WARN()
  ASoC: wm8996: Replace BUG() with WARN()
  ASoC: wm8962: Replace BUG() with WARN()
  ASoC: wm8958: Replace BUG() with WARN()
  ASoC: wm8904: Replace BUG() with WARN()
  ASoC: wm8900: Replace BUG() with WARN()
  ASoC: wm8350: Replace BUG() with WARN()
  ASoC: txx9: Use WARN_ON() instead of BUG_ON()
  ASoC: sh: Use WARN_ON() instead of BUG_ON()
  ASoC: rcar: Use WARN_ON() instead of BUG_ON()
  ...
CS42L73 audio CODEC
Required properties:
- compatible : "cirrus,cs42l73"
- reg : the I2C address of the device for I2C
Optional properties:
- reset_gpio : a GPIO spec for the reset pin.
- chgfreq : Charge Pump Frequency values 0x00-0x0F
Example:
codec: cs42l73@4a {
compatible = "cirrus,cs42l73";
reg = <0x4a>;
reset_gpio = <&gpio 10 0>;
chgfreq = <0x05>;
};
\ No newline at end of file
* Texas Instruments SoC audio setups with TLV320AIC3X Codec
Required properties:
- compatible : "ti,da830-evm-audio" : forDM365/DA8xx/OMAPL1x/AM33xx
- ti,model : The user-visible name of this sound complex.
- ti,audio-codec : The phandle of the TLV320AIC3x audio codec
- ti,mcasp-controller : The phandle of the McASP controller
- ti,codec-clock-rate : The Codec Clock rate (in Hz) applied to the Codec
- ti,audio-routing : A list of the connections between audio components.
Each entry is a pair of strings, the first being the connection's sink,
the second being the connection's source. Valid names for sources and
sinks are the codec's pins, and the jacks on the board:
Board connectors:
* Headphone Jack
* Line Out
* Mic Jack
* Line In
Example:
sound {
compatible = "ti,da830-evm-audio";
ti,model = "DA830 EVM";
ti,audio-codec = <&tlv320aic3x>;
ti,mcasp-controller = <&mcasp1>;
ti,codec-clock-rate = <12000000>;
ti,audio-routing =
"Headphone Jack", "HPLOUT",
"Headphone Jack", "HPROUT",
"Line Out", "LLOUT",
"Line Out", "RLOUT",
"MIC3L", "Mic Bias 2V",
"MIC3R", "Mic Bias 2V",
"Mic Bias 2V", "Mic Jack",
"LINE1L", "Line In",
"LINE2L", "Line In",
"LINE1R", "Line In",
"LINE2R", "Line In";
};
...@@ -4,17 +4,25 @@ Required properties: ...@@ -4,17 +4,25 @@ Required properties:
- compatible : - compatible :
"ti,dm646x-mcasp-audio" : for DM646x platforms "ti,dm646x-mcasp-audio" : for DM646x platforms
"ti,da830-mcasp-audio" : for both DA830 & DA850 platforms "ti,da830-mcasp-audio" : for both DA830 & DA850 platforms
"ti,omap2-mcasp-audio" : for OMAP2 platforms (TI81xx, AM33xx) "ti,am33xx-mcasp-audio" : for AM33xx platforms (AM33xx, TI81xx)
- reg : Should contain McASP registers offset and length
- interrupts : Interrupt number for McASP
- op-mode : I2S/DIT ops mode.
- tdm-slots : Slots for TDM operation.
- num-serializer : Serializers used by McASP.
- serial-dir : A list of serializer pin mode. The list number should be equal
to "num-serializer" parameter. Each entry is a number indication
serializer pin direction. (0 - INACTIVE, 1 - TX, 2 - RX)
- reg : Should contain reg specifiers for the entries in the reg-names property.
- reg-names : Should contain:
* "mpu" for the main registers (required). For compatibility with
existing software, it is recommended this is the first entry.
* "dat" for separate data port register access (optional).
- op-mode : I2S/DIT ops mode. 0 for I2S mode. 1 for DIT mode used for S/PDIF,
IEC60958-1, and AES-3 formats.
- tdm-slots : Slots for TDM operation. Indicates number of channels transmitted
or received over one serializer.
- serial-dir : A list of serializer configuration. Each entry is a number
indication for serializer pin direction.
(0 - INACTIVE, 1 - TX, 2 - RX)
- dmas: two element list of DMA controller phandles and DMA request line
ordered pairs.
- dma-names: identifier string for each DMA request line in the dmas property.
These strings correspond 1:1 with the ordered pairs in dmas. The dma
identifiers must be "rx" and "tx".
Optional properties: Optional properties:
...@@ -23,18 +31,23 @@ Optional properties: ...@@ -23,18 +31,23 @@ Optional properties:
- rx-num-evt : FIFO levels. - rx-num-evt : FIFO levels.
- sram-size-playback : size of sram to be allocated during playback - sram-size-playback : size of sram to be allocated during playback
- sram-size-capture : size of sram to be allocated during capture - sram-size-capture : size of sram to be allocated during capture
- interrupts : Interrupt numbers for McASP, currently not used by the driver
- interrupt-names : Known interrupt names are "tx" and "rx"
- pinctrl-0: Should specify pin control group used for this controller.
- pinctrl-names: Should contain only one value - "default", for more details
please refer to pinctrl-bindings.txt
Example: Example:
mcasp0: mcasp0@1d00000 { mcasp0: mcasp0@1d00000 {
compatible = "ti,da830-mcasp-audio"; compatible = "ti,da830-mcasp-audio";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x100000 0x3000>; reg = <0x100000 0x3000>;
interrupts = <82 83>; reg-names "mpu";
interrupts = <82>, <83>;
interrupts-names = "tx", "rx";
op-mode = <0>; /* MCASP_IIS_MODE */ op-mode = <0>; /* MCASP_IIS_MODE */
tdm-slots = <2>; tdm-slots = <2>;
num-serializer = <16>;
serial-dir = < serial-dir = <
0 0 0 0 /* 0: INACTIVE, 1: TX, 2: RX */ 0 0 0 0 /* 0: INACTIVE, 1: TX, 2: RX */
0 0 0 0 0 0 0 0
......
...@@ -24,10 +24,36 @@ Optional properties: ...@@ -24,10 +24,36 @@ Optional properties:
3 - MICBIAS output is connected to AVDD, 3 - MICBIAS output is connected to AVDD,
If this node is not mentioned or if the value is incorrect, then MicBias If this node is not mentioned or if the value is incorrect, then MicBias
is powered down. is powered down.
- AVDD-supply, IOVDD-supply, DRVDD-supply, DVDD-supply : power supplies for the
device as covered in Documentation/devicetree/bindings/regulator/regulator.txt
CODEC output pins:
* LLOUT
* RLOUT
* MONO_LOUT
* HPLOUT
* HPROUT
* HPLCOM
* HPRCOM
CODEC input pins:
* MIC3L
* MIC3R
* LINE1L
* LINE2L
* LINE1R
* LINE2R
The pins can be used in referring sound node's audio-routing property.
Example: Example:
tlv320aic3x: tlv320aic3x@1b { tlv320aic3x: tlv320aic3x@1b {
compatible = "ti,tlv320aic3x"; compatible = "ti,tlv320aic3x";
reg = <0x1b>; reg = <0x1b>;
AVDD-supply = <&regulator>;
IOVDD-supply = <&regulator>;
DRVDD-supply = <&regulator>;
DVDD-supply = <&regulator>;
}; };
Texas Instruments - tpa6130a2 Codec module
The tpa6130a2 serial control bus communicates through I2C protocols
Required properties:
- compatible - "string" - One of:
"ti,tpa6130a2" - TPA6130A2
"ti,tpa6140a2" - TPA6140A2
- reg - <int> - I2C slave address
- Vdd-supply - <phandle> - power supply regulator
Optional properties:
- power-gpio - gpio pin to power the device
Example:
tpa6130a2: tpa6130a2@60 {
compatible = "ti,tpa6130a2";
reg = <0x60>;
Vdd-supply = <&vmmc2>;
power-gpio = <&gpio4 2 GPIO_ACTIVE_HIGH>;
};
...@@ -138,6 +138,7 @@ Code Seq#(hex) Include File Comments ...@@ -138,6 +138,7 @@ Code Seq#(hex) Include File Comments
'H' C0-DF net/bluetooth/cmtp/cmtp.h conflict! 'H' C0-DF net/bluetooth/cmtp/cmtp.h conflict!
'H' C0-DF net/bluetooth/bnep/bnep.h conflict! 'H' C0-DF net/bluetooth/bnep/bnep.h conflict!
'H' F1 linux/hid-roccat.h <mailto:erazor_de@users.sourceforge.net> 'H' F1 linux/hid-roccat.h <mailto:erazor_de@users.sourceforge.net>
'H' F8-FA sound/firewire.h
'I' all linux/isdn.h conflict! 'I' all linux/isdn.h conflict!
'I' 00-0F drivers/isdn/divert/isdn_divert.h conflict! 'I' 00-0F drivers/isdn/divert/isdn_divert.h conflict!
'I' 40-4F linux/mISDNif.h conflict! 'I' 40-4F linux/mISDNif.h conflict!
......
ThinkPad ACPI Extras Driver ThinkPad ACPI Extras Driver
Version 0.24 Version 0.25
December 11th, 2009 October 16th, 2013
Borislav Deianov <borislav@users.sf.net> Borislav Deianov <borislav@users.sf.net>
Henrique de Moraes Holschuh <hmh@hmh.eng.br> Henrique de Moraes Holschuh <hmh@hmh.eng.br>
...@@ -741,6 +741,9 @@ compiled with the CONFIG_THINKPAD_ACPI_UNSAFE_LEDS option enabled. ...@@ -741,6 +741,9 @@ compiled with the CONFIG_THINKPAD_ACPI_UNSAFE_LEDS option enabled.
Distributions must never enable this option. Individual users that Distributions must never enable this option. Individual users that
are aware of the consequences are welcome to enabling it. are aware of the consequences are welcome to enabling it.
Audio mute and microphone mute LEDs are supported, but currently not
visible to userspace. They are used by the snd-hda-intel audio driver.
procfs notes: procfs notes:
The available commands are: The available commands are:
......
...@@ -616,7 +616,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. ...@@ -616,7 +616,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
As default, snd-dummy drivers doesn't allocate the real buffers As default, snd-dummy drivers doesn't allocate the real buffers
but either ignores read/write or mmap a single dummy page to all but either ignores read/write or mmap a single dummy page to all
buffer pages, in order to save the resouces. If your apps need buffer pages, in order to save the resources. If your apps need
the read/ written buffer data to be consistent, pass fake_buffer=0 the read/ written buffer data to be consistent, pass fake_buffer=0
option. option.
......
...@@ -232,7 +232,7 @@ The parameter can be given: ...@@ -232,7 +232,7 @@ The parameter can be given:
# modprobe snd-usb-audio index=1 device_setup=0x09 # modprobe snd-usb-audio index=1 device_setup=0x09
* Or while configuring the modules options in your modules configuration file * Or while configuring the modules options in your modules configuration file
(tipically a .conf file in /etc/modprobe.d/ directory: (typically a .conf file in /etc/modprobe.d/ directory:
alias snd-card-1 snd-usb-audio alias snd-card-1 snd-usb-audio
options snd-usb-audio index=1 device_setup=0x09 options snd-usb-audio index=1 device_setup=0x09
......
...@@ -87,7 +87,7 @@ with 4 channels, ...@@ -87,7 +87,7 @@ with 4 channels,
and use the interleaved 4 channel data. and use the interleaved 4 channel data.
There are some control switchs affecting to the speaker connections: There are some control switches affecting to the speaker connections:
"Line-In Mode" - an enum control to change the behavior of line-in "Line-In Mode" - an enum control to change the behavior of line-in
jack. Either "Line-In", "Rear Output" or "Bass Output" can jack. Either "Line-In", "Rear Output" or "Bass Output" can
......
...@@ -217,12 +217,12 @@ Not supported: ...@@ -217,12 +217,12 @@ Not supported:
would be enabled with ALSA kcontrols. would be enabled with ALSA kcontrols.
- Audio policy/resource management. This API does not provide any - Audio policy/resource management. This API does not provide any
hooks to query the utilization of the audio DSP, nor any premption hooks to query the utilization of the audio DSP, nor any preemption
mechanisms. mechanisms.
- No notion of underun/overrun. Since the bytes written are compressed - No notion of underrun/overrun. Since the bytes written are compressed
in nature and data written/read doesn't translate directly to in nature and data written/read doesn't translate directly to
rendered output in time, this does not deal with underrun/overun and rendered output in time, this does not deal with underrun/overrun and
maybe dealt in user-library maybe dealt in user-library
Credits: Credits:
......
Dynamic PCM
===========
1. Description
==============
Dynamic PCM allows an ALSA PCM device to digitally route its PCM audio to
various digital endpoints during the PCM stream runtime. e.g. PCM0 can route
digital audio to I2S DAI0, I2S DAI1 or PDM DAI2. This is useful for on SoC DSP
drivers that expose several ALSA PCMs and can route to multiple DAIs.
The DPCM runtime routing is determined by the ALSA mixer settings in the same
way as the analog signal is routed in an ASoC codec driver. DPCM uses a DAPM
graph representing the DSP internal audio paths and uses the mixer settings to
determine the patch used by each ALSA PCM.
DPCM re-uses all the existing component codec, platform and DAI drivers without
any modifications.
Phone Audio System with SoC based DSP
-------------------------------------
Consider the following phone audio subsystem. This will be used in this
document for all examples :-
| Front End PCMs | SoC DSP | Back End DAIs | Audio devices |
*************
PCM0 <------------> * * <----DAI0-----> Codec Headset
* *
PCM1 <------------> * * <----DAI1-----> Codec Speakers
* DSP *
PCM2 <------------> * * <----DAI2-----> MODEM
* *
PCM3 <------------> * * <----DAI3-----> BT
* *
* * <----DAI4-----> DMIC
* *
* * <----DAI5-----> FM
*************
This diagram shows a simple smart phone audio subsystem. It supports Bluetooth,
FM digital radio, Speakers, Headset Jack, digital microphones and cellular
modem. This sound card exposes 4 DSP front end (FE) ALSA PCM devices and
supports 6 back end (BE) DAIs. Each FE PCM can digitally route audio data to any
of the BE DAIs. The FE PCM devices can also route audio to more than 1 BE DAI.
Example - DPCM Switching playback from DAI0 to DAI1
---------------------------------------------------
Audio is being played to the Headset. After a while the user removes the headset
and audio continues playing on the speakers.
Playback on PCM0 to Headset would look like :-
*************
PCM0 <============> * * <====DAI0=====> Codec Headset
* *
PCM1 <------------> * * <----DAI1-----> Codec Speakers
* DSP *
PCM2 <------------> * * <----DAI2-----> MODEM
* *
PCM3 <------------> * * <----DAI3-----> BT
* *
* * <----DAI4-----> DMIC
* *
* * <----DAI5-----> FM
*************
The headset is removed from the jack by user so the speakers must now be used :-
*************
PCM0 <============> * * <----DAI0-----> Codec Headset
* *
PCM1 <------------> * * <====DAI1=====> Codec Speakers
* DSP *
PCM2 <------------> * * <----DAI2-----> MODEM
* *
PCM3 <------------> * * <----DAI3-----> BT
* *
* * <----DAI4-----> DMIC
* *
* * <----DAI5-----> FM
*************
The audio driver processes this as follows :-
1) Machine driver receives Jack removal event.
2) Machine driver OR audio HAL disables the Headset path.
3) DPCM runs the PCM trigger(stop), hw_free(), shutdown() operations on DAI0
for headset since the path is now disabled.
4) Machine driver or audio HAL enables the speaker path.
5) DPCM runs the PCM ops for startup(), hw_params(), prepapre() and
trigger(start) for DAI1 Speakers since the path is enabled.
In this example, the machine driver or userspace audio HAL can alter the routing
and then DPCM will take care of managing the DAI PCM operations to either bring
the link up or down. Audio playback does not stop during this transition.
DPCM machine driver
===================
The DPCM enabled ASoC machine driver is similar to normal machine drivers
except that we also have to :-
1) Define the FE and BE DAI links.
2) Define any FE/BE PCM operations.
3) Define widget graph connections.
1 FE and BE DAI links
---------------------
| Front End PCMs | SoC DSP | Back End DAIs | Audio devices |
*************
PCM0 <------------> * * <----DAI0-----> Codec Headset
* *
PCM1 <------------> * * <----DAI1-----> Codec Speakers
* DSP *
PCM2 <------------> * * <----DAI2-----> MODEM
* *
PCM3 <------------> * * <----DAI3-----> BT
* *
* * <----DAI4-----> DMIC
* *
* * <----DAI5-----> FM
*************
For the example above we have to define 4 FE DAI links and 6 BE DAI links. The
FE DAI links are defined as follows :-
static struct snd_soc_dai_link machine_dais[] = {
{
.name = "PCM0 System",
.stream_name = "System Playback",
.cpu_dai_name = "System Pin",
.platform_name = "dsp-audio",
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.dynamic = 1,
.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
.dpcm_playback = 1,
},
.....< other FE and BE DAI links here >
};
This FE DAI link is pretty similar to a regular DAI link except that we also
set the DAI link to a DPCM FE with the "dynamic = 1". The supported FE stream
directions should also be set with the "dpcm_playback" and "dpcm_capture"
flags. There is also an option to specify the ordering of the trigger call for
each FE. This allows the ASoC core to trigger the DSP before or after the other
components (as some DSPs have strong requirements for the ordering DAI/DSP
start and stop sequences).
The FE DAI above sets the codec and code DAIs to dummy devices since the BE is
dynamic and will change depending on runtime config.
The BE DAIs are configured as follows :-
static struct snd_soc_dai_link machine_dais[] = {
.....< FE DAI links here >
{
.name = "Codec Headset",
.cpu_dai_name = "ssp-dai.0",
.platform_name = "snd-soc-dummy",
.no_pcm = 1,
.codec_name = "rt5640.0-001c",
.codec_dai_name = "rt5640-aif1",
.ignore_suspend = 1,
.ignore_pmdown_time = 1,
.be_hw_params_fixup = hswult_ssp0_fixup,
.ops = &haswell_ops,
.dpcm_playback = 1,
.dpcm_capture = 1,
},
.....< other BE DAI links here >
};
This BE DAI link connects DAI0 to the codec (in this case RT5460 AIF1). It sets
the "no_pcm" flag to mark it has a BE and sets flags for supported stream
directions using "dpcm_playback" and "dpcm_capture" above.
The BE has also flags set for ignoring suspend and PM down time. This allows
the BE to work in a hostless mode where the host CPU is not transferring data
like a BT phone call :-
*************
PCM0 <------------> * * <----DAI0-----> Codec Headset
* *
PCM1 <------------> * * <----DAI1-----> Codec Speakers
* DSP *
PCM2 <------------> * * <====DAI2=====> MODEM
* *
PCM3 <------------> * * <====DAI3=====> BT
* *
* * <----DAI4-----> DMIC
* *
* * <----DAI5-----> FM
*************
This allows the host CPU to sleep whilst the DSP, MODEM DAI and the BT DAI are
still in operation.
A BE DAI link can also set the codec to a dummy device if the code is a device
that is managed externally.
Likewise a BE DAI can also set a dummy cpu DAI if the CPU DAI is managed by the
DSP firmware.
2 FE/BE PCM operations
----------------------
The BE above also exports some PCM operations and a "fixup" callback. The fixup
callback is used by the machine driver to (re)configure the DAI based upon the
FE hw params. i.e. the DSP may perform SRC or ASRC from the FE to BE.
e.g. DSP converts all FE hw params to run at fixed rate of 48k, 16bit, stereo for
DAI0. This means all FE hw_params have to be fixed in the machine driver for
DAI0 so that the DAI is running at desired configuration regardless of the FE
configuration.
static int dai0_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
struct snd_interval *rate = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_RATE);
struct snd_interval *channels = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_CHANNELS);
/* The DSP will covert the FE rate to 48k, stereo */
rate->min = rate->max = 48000;
channels->min = channels->max = 2;
/* set DAI0 to 16 bit */
snd_mask_set(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT -
SNDRV_PCM_HW_PARAM_FIRST_MASK],
SNDRV_PCM_FORMAT_S16_LE);
return 0;
}
The other PCM operation are the same as for regular DAI links. Use as necessary.
3 Widget graph connections
--------------------------
The BE DAI links will normally be connected to the graph at initialisation time
by the ASoC DAPM core. However, if the BE codec or BE DAI is a dummy then this
has to be set explicitly in the driver :-
/* BE for codec Headset - DAI0 is dummy and managed by DSP FW */
{"DAI0 CODEC IN", NULL, "AIF1 Capture"},
{"AIF1 Playback", NULL, "DAI0 CODEC OUT"},
Writing a DPCM DSP driver
=========================
The DPCM DSP driver looks much like a standard platform class ASoC driver
combined with elements from a codec class driver. A DSP platform driver must
implement :-
1) Front End PCM DAIs - i.e. struct snd_soc_dai_driver.
2) DAPM graph showing DSP audio routing from FE DAIs to BEs.
3) DAPM widgets from DSP graph.
4) Mixers for gains, routing, etc.
5) DMA configuration.
6) BE AIF widgets.
Items 6 is important for routing the audio outside of the DSP. AIF need to be
defined for each BE and each stream direction. e.g for BE DAI0 above we would
have :-
SND_SOC_DAPM_AIF_IN("DAI0 RX", NULL, 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("DAI0 TX", NULL, 0, SND_SOC_NOPM, 0, 0),
The BE AIF are used to connect the DSP graph to the graphs for the other
component drivers (e.g. codec graph).
Hostless PCM streams
====================
A hostless PCM stream is a stream that is not routed through the host CPU. An
example of this would be a phone call from handset to modem.
*************
PCM0 <------------> * * <----DAI0-----> Codec Headset
* *
PCM1 <------------> * * <====DAI1=====> Codec Speakers/Mic
* DSP *
PCM2 <------------> * * <====DAI2=====> MODEM
* *
PCM3 <------------> * * <----DAI3-----> BT
* *
* * <----DAI4-----> DMIC
* *
* * <----DAI5-----> FM
*************
In this case the PCM data is routed via the DSP. The host CPU in this use case
is only used for control and can sleep during the runtime of the stream.
The host can control the hostless link either by :-
1) Configuring the link as a CODEC <-> CODEC style link. In this case the link
is enabled or disabled by the state of the DAPM graph. This usually means
there is a mixer control that can be used to connect or disconnect the path
between both DAIs.
2) Hostless FE. This FE has a virtual connection to the BE DAI links on the DAPM
graph. Control is then carried out by the FE as regular PCM operations.
This method gives more control over the DAI links, but requires much more
userspace code to control the link. Its recommended to use CODEC<->CODEC
unless your HW needs more fine grained sequencing of the PCM ops.
CODEC <-> CODEC link
--------------------
This DAI link is enabled when DAPM detects a valid path within the DAPM graph.
The machine driver sets some additional parameters to the DAI link i.e.
static const struct snd_soc_pcm_stream dai_params = {
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rate_min = 8000,
.rate_max = 8000,
.channels_min = 2,
.channels_max = 2,
};
static struct snd_soc_dai_link dais[] = {
< ... more DAI links above ... >
{
.name = "MODEM",
.stream_name = "MODEM",
.cpu_dai_name = "dai2",
.codec_dai_name = "modem-aif1",
.codec_name = "modem",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBM_CFM,
.params = &dai_params,
}
< ... more DAI links here ... >
These parameters are used to configure the DAI hw_params() when DAPM detects a
valid path and then calls the PCM operations to start the link. DAPM will also
call the appropriate PCM operations to disable the DAI when the path is no
longer valid.
Hostless FE
-----------
The DAI link(s) are enabled by a FE that does not read or write any PCM data.
This means creating a new FE that is connected with a virtual path to both
DAI links. The DAI links will be started when the FE PCM is started and stopped
when the FE PCM is stopped. Note that the FE PCM cannot read or write data in
this configuration.
ASoC Codec Driver ASoC Codec Class Driver
================= =======================
The codec driver is generic and hardware independent code that configures the The codec class driver is generic and hardware independent code that configures
codec to provide audio capture and playback. It should contain no code that is the codec, FM, MODEM, BT or external DSP to provide audio capture and playback.
specific to the target platform or machine. All platform and machine specific It should contain no code that is specific to the target platform or machine.
code should be added to the platform and machine drivers respectively. All platform and machine specific code should be added to the platform and
machine drivers respectively.
Each codec driver *must* provide the following features:- Each codec class driver *must* provide the following features:-
1) Codec DAI and PCM configuration 1) Codec DAI and PCM configuration
2) Codec control IO - using I2C, 3 Wire(SPI) or both APIs 2) Codec control IO - using RegMap API
3) Mixers and audio controls 3) Mixers and audio controls
4) Codec audio operations 4) Codec audio operations
5) DAPM description.
6) DAPM event handler.
Optionally, codec drivers can also provide:- Optionally, codec drivers can also provide:-
5) DAPM description.
6) DAPM event handler.
7) DAC Digital mute control. 7) DAC Digital mute control.
Its probably best to use this guide in conjunction with the existing codec Its probably best to use this guide in conjunction with the existing codec
...@@ -64,26 +65,9 @@ struct snd_soc_dai_driver wm8731_dai = { ...@@ -64,26 +65,9 @@ struct snd_soc_dai_driver wm8731_dai = {
2 - Codec control IO 2 - Codec control IO
-------------------- --------------------
The codec can usually be controlled via an I2C or SPI style interface The codec can usually be controlled via an I2C or SPI style interface
(AC97 combines control with data in the DAI). The codec drivers provide (AC97 combines control with data in the DAI). The codec driver should use the
functions to read and write the codec registers along with supplying a Regmap API for all codec IO. Please see include/linux/regmap.h and existing
register cache:- codec drivers for example regmap usage.
/* IO control data and register cache */
void *control_data; /* codec control (i2c/3wire) data */
void *reg_cache;
Codec read/write should do any data formatting and call the hardware
read write below to perform the IO. These functions are called by the
core and ALSA when performing DAPM or changing the mixer:-
unsigned int (*read)(struct snd_soc_codec *, unsigned int);
int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);
Codec hardware IO functions - usually points to either the I2C, SPI or AC97
read/write:-
hw_write_t hw_write;
hw_read_t hw_read;
3 - Mixers and audio controls 3 - Mixers and audio controls
...@@ -127,7 +111,7 @@ Defines a stereo enumerated control ...@@ -127,7 +111,7 @@ Defines a stereo enumerated control
4 - Codec Audio Operations 4 - Codec Audio Operations
-------------------------- --------------------------
The codec driver also supports the following ALSA operations:- The codec driver also supports the following ALSA PCM operations:-
/* SoC audio ops */ /* SoC audio ops */
struct snd_soc_ops { struct snd_soc_ops {
......
...@@ -21,7 +21,7 @@ level power systems. ...@@ -21,7 +21,7 @@ level power systems.
There are 4 power domains within DAPM There are 4 power domains within DAPM
1. Codec domain - VREF, VMID (core codec and audio power) 1. Codec bias domain - VREF, VMID (core codec and audio power)
Usually controlled at codec probe/remove and suspend/resume, although Usually controlled at codec probe/remove and suspend/resume, although
can be set at stream time if power is not needed for sidetone, etc. can be set at stream time if power is not needed for sidetone, etc.
...@@ -30,7 +30,7 @@ There are 4 power domains within DAPM ...@@ -30,7 +30,7 @@ There are 4 power domains within DAPM
machine driver and responds to asynchronous events e.g when HP machine driver and responds to asynchronous events e.g when HP
are inserted are inserted
3. Path domain - audio susbsystem signal paths 3. Path domain - audio subsystem signal paths
Automatically set when mixer and mux settings are changed by the user. Automatically set when mixer and mux settings are changed by the user.
e.g. alsamixer, amixer. e.g. alsamixer, amixer.
...@@ -63,14 +63,22 @@ Audio DAPM widgets fall into a number of types:- ...@@ -63,14 +63,22 @@ Audio DAPM widgets fall into a number of types:-
o Line - Line Input/Output (and optional Jack) o Line - Line Input/Output (and optional Jack)
o Speaker - Speaker o Speaker - Speaker
o Supply - Power or clock supply widget used by other widgets. o Supply - Power or clock supply widget used by other widgets.
o Regulator - External regulator that supplies power to audio components.
o Clock - External clock that supplies clock to audio components.
o AIF IN - Audio Interface Input (with TDM slot mask).
o AIF OUT - Audio Interface Output (with TDM slot mask).
o Siggen - Signal Generator.
o DAI IN - Digital Audio Interface Input.
o DAI OUT - Digital Audio Interface Output.
o DAI Link - DAI Link between two DAI structures */
o Pre - Special PRE widget (exec before all others) o Pre - Special PRE widget (exec before all others)
o Post - Special POST widget (exec after all others) o Post - Special POST widget (exec after all others)
(Widgets are defined in include/sound/soc-dapm.h) (Widgets are defined in include/sound/soc-dapm.h)
Widgets are usually added in the codec driver and the machine driver. There are Widgets can be added to the sound card by any of the component driver types.
convenience macros defined in soc-dapm.h that can be used to quickly build a There are convenience macros defined in soc-dapm.h that can be used to quickly
list of widgets of the codecs and machines DAPM widgets. build a list of widgets of the codecs and machines DAPM widgets.
Most widgets have a name, register, shift and invert. Some widgets have extra Most widgets have a name, register, shift and invert. Some widgets have extra
parameters for stream name and kcontrols. parameters for stream name and kcontrols.
...@@ -80,11 +88,13 @@ parameters for stream name and kcontrols. ...@@ -80,11 +88,13 @@ parameters for stream name and kcontrols.
------------------------- -------------------------
Stream Widgets relate to the stream power domain and only consist of ADCs Stream Widgets relate to the stream power domain and only consist of ADCs
(analog to digital converters) and DACs (digital to analog converters). (analog to digital converters), DACs (digital to analog converters),
AIF IN and AIF OUT.
Stream widgets have the following format:- Stream widgets have the following format:-
SND_SOC_DAPM_DAC(name, stream name, reg, shift, invert), SND_SOC_DAPM_DAC(name, stream name, reg, shift, invert),
SND_SOC_DAPM_AIF_IN(name, stream, slot, reg, shift, invert)
NOTE: the stream name must match the corresponding stream name in your codec NOTE: the stream name must match the corresponding stream name in your codec
snd_soc_codec_dai. snd_soc_codec_dai.
...@@ -94,6 +104,11 @@ e.g. stream widgets for HiFi playback and capture ...@@ -94,6 +104,11 @@ e.g. stream widgets for HiFi playback and capture
SND_SOC_DAPM_DAC("HiFi DAC", "HiFi Playback", REG, 3, 1), SND_SOC_DAPM_DAC("HiFi DAC", "HiFi Playback", REG, 3, 1),
SND_SOC_DAPM_ADC("HiFi ADC", "HiFi Capture", REG, 2, 1), SND_SOC_DAPM_ADC("HiFi ADC", "HiFi Capture", REG, 2, 1),
e.g. stream widgets for AIF
SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
2.2 Path Domain Widgets 2.2 Path Domain Widgets
----------------------- -----------------------
...@@ -121,12 +136,14 @@ If you dont want the mixer elements prefixed with the name of the mixer widget, ...@@ -121,12 +136,14 @@ If you dont want the mixer elements prefixed with the name of the mixer widget,
you can use SND_SOC_DAPM_MIXER_NAMED_CTL instead. the parameters are the same you can use SND_SOC_DAPM_MIXER_NAMED_CTL instead. the parameters are the same
as for SND_SOC_DAPM_MIXER. as for SND_SOC_DAPM_MIXER.
2.3 Platform/Machine domain Widgets
----------------------------------- 2.3 Machine domain Widgets
--------------------------
Machine widgets are different from codec widgets in that they don't have a Machine widgets are different from codec widgets in that they don't have a
codec register bit associated with them. A machine widget is assigned to each codec register bit associated with them. A machine widget is assigned to each
machine audio component (non codec) that can be independently powered. e.g. machine audio component (non codec or DSP) that can be independently
powered. e.g.
o Speaker Amp o Speaker Amp
o Microphone Bias o Microphone Bias
...@@ -146,12 +163,12 @@ static int spitz_mic_bias(struct snd_soc_dapm_widget* w, int event) ...@@ -146,12 +163,12 @@ static int spitz_mic_bias(struct snd_soc_dapm_widget* w, int event)
SND_SOC_DAPM_MIC("Mic Jack", spitz_mic_bias), SND_SOC_DAPM_MIC("Mic Jack", spitz_mic_bias),
2.4 Codec Domain 2.4 Codec (BIAS) Domain
---------------- -----------------------
The codec power domain has no widgets and is handled by the codecs DAPM event The codec bias power domain has no widgets and is handled by the codecs DAPM
handler. This handler is called when the codec powerstate is changed wrt to any event handler. This handler is called when the codec powerstate is changed wrt
stream event or by kernel PM events. to any stream event or by kernel PM events.
2.5 Virtual Widgets 2.5 Virtual Widgets
...@@ -169,15 +186,16 @@ After all the widgets have been defined, they can then be added to the DAPM ...@@ -169,15 +186,16 @@ After all the widgets have been defined, they can then be added to the DAPM
subsystem individually with a call to snd_soc_dapm_new_control(). subsystem individually with a call to snd_soc_dapm_new_control().
3. Codec Widget Interconnections 3. Codec/DSP Widget Interconnections
================================ ====================================
Widgets are connected to each other within the codec and machine by audio paths Widgets are connected to each other within the codec, platform and machine by
(called interconnections). Each interconnection must be defined in order to audio paths (called interconnections). Each interconnection must be defined in
create a map of all audio paths between widgets. order to create a map of all audio paths between widgets.
This is easiest with a diagram of the codec (and schematic of the machine audio This is easiest with a diagram of the codec or DSP (and schematic of the machine
system), as it requires joining widgets together via their audio signal paths. audio system), as it requires joining widgets together via their audio signal
paths.
e.g., from the WM8731 output mixer (wm8731.c) e.g., from the WM8731 output mixer (wm8731.c)
...@@ -247,16 +265,9 @@ machine and includes the codec. e.g. ...@@ -247,16 +265,9 @@ machine and includes the codec. e.g.
o Mic Jack o Mic Jack
o Codec Pins o Codec Pins
When a codec pin is NC it can be marked as not used with a call to Endpoints are added to the DAPM graph so that their usage can be determined in
order to save power. e.g. NC codecs pins will be switched OFF, unconnected
snd_soc_dapm_set_endpoint(codec, "Widget Name", 0); jacks can also be switched OFF.
The last argument is 0 for inactive and 1 for active. This way the pin and its
input widget will never be powered up and consume power.
This also applies to machine widgets. e.g. if a headphone is connected to a
jack then the jack can be marked active. If the headphone is removed, then
the headphone jack can be marked inactive.
5 DAPM Widget Events 5 DAPM Widget Events
......
ASoC Machine Driver ASoC Machine Driver
=================== ===================
The ASoC machine (or board) driver is the code that glues together the platform The ASoC machine (or board) driver is the code that glues together all the
and codec drivers. component drivers (e.g. codecs, platforms and DAIs). It also describes the
relationships between each componnent which include audio paths, GPIOs,
interrupts, clocking, jacks and voltage regulators.
The machine driver can contain codec and platform specific code. It registers The machine driver can contain codec and platform specific code. It registers
the audio subsystem with the kernel as a platform device and is represented by the audio subsystem with the kernel as a platform device and is represented by
......
ASoC Platform Driver ASoC Platform Driver
==================== ====================
An ASoC platform driver can be divided into audio DMA and SoC DAI configuration An ASoC platform driver class can be divided into audio DMA drivers, SoC DAI
and control. The platform drivers only target the SoC CPU and must have no board drivers and DSP drivers. The platform drivers only target the SoC CPU and must
specific code. have no board specific code.
Audio DMA Audio DMA
========= =========
...@@ -64,3 +64,16 @@ Each SoC DAI driver must provide the following features:- ...@@ -64,3 +64,16 @@ Each SoC DAI driver must provide the following features:-
5) Suspend and resume (optional) 5) Suspend and resume (optional)
Please see codec.txt for a description of items 1 - 4. Please see codec.txt for a description of items 1 - 4.
SoC DSP Drivers
===============
Each SoC DSP driver usually supplies the following features :-
1) DAPM graph
2) Mixer controls
3) DMA IO to/from DSP buffers (if applicable)
4) Definition of DSP front end (FE) PCM devices.
Please see DPCM.txt for a description of item 4.
...@@ -158,8 +158,6 @@ int mc13xxx_reg_read(struct mc13xxx *mc13xxx, unsigned int offset, u32 *val) ...@@ -158,8 +158,6 @@ int mc13xxx_reg_read(struct mc13xxx *mc13xxx, unsigned int offset, u32 *val)
{ {
int ret; int ret;
BUG_ON(!mutex_is_locked(&mc13xxx->lock));
if (offset > MC13XXX_NUMREGS) if (offset > MC13XXX_NUMREGS)
return -EINVAL; return -EINVAL;
...@@ -172,8 +170,6 @@ EXPORT_SYMBOL(mc13xxx_reg_read); ...@@ -172,8 +170,6 @@ EXPORT_SYMBOL(mc13xxx_reg_read);
int mc13xxx_reg_write(struct mc13xxx *mc13xxx, unsigned int offset, u32 val) int mc13xxx_reg_write(struct mc13xxx *mc13xxx, unsigned int offset, u32 val)
{ {
BUG_ON(!mutex_is_locked(&mc13xxx->lock));
dev_vdbg(mc13xxx->dev, "[0x%02x] <- 0x%06x\n", offset, val); dev_vdbg(mc13xxx->dev, "[0x%02x] <- 0x%06x\n", offset, val);
if (offset > MC13XXX_NUMREGS || val > 0xffffff) if (offset > MC13XXX_NUMREGS || val > 0xffffff)
...@@ -186,7 +182,6 @@ EXPORT_SYMBOL(mc13xxx_reg_write); ...@@ -186,7 +182,6 @@ EXPORT_SYMBOL(mc13xxx_reg_write);
int mc13xxx_reg_rmw(struct mc13xxx *mc13xxx, unsigned int offset, int mc13xxx_reg_rmw(struct mc13xxx *mc13xxx, unsigned int offset,
u32 mask, u32 val) u32 mask, u32 val)
{ {
BUG_ON(!mutex_is_locked(&mc13xxx->lock));
BUG_ON(val & ~mask); BUG_ON(val & ~mask);
dev_vdbg(mc13xxx->dev, "[0x%02x] <- 0x%06x (mask: 0x%06x)\n", dev_vdbg(mc13xxx->dev, "[0x%02x] <- 0x%06x (mask: 0x%06x)\n",
offset, val, mask); offset, val, mask);
......
...@@ -94,10 +94,15 @@ static int mc13xxx_spi_write(void *context, const void *data, size_t count) ...@@ -94,10 +94,15 @@ static int mc13xxx_spi_write(void *context, const void *data, size_t count)
{ {
struct device *dev = context; struct device *dev = context;
struct spi_device *spi = to_spi_device(dev); struct spi_device *spi = to_spi_device(dev);
const char *reg = data;
if (count != 4) if (count != 4)
return -ENOTSUPP; return -ENOTSUPP;
/* include errata fix for spi audio problems */
if (*reg == MC13783_AUDIO_CODEC || *reg == MC13783_AUDIO_DAC)
spi_write(spi, data, count);
return spi_write(spi, data, count); return spi_write(spi, data, count);
} }
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#define TPACPI_VERSION "0.24" #define TPACPI_VERSION "0.25"
#define TPACPI_SYSFS_VERSION 0x020700 #define TPACPI_SYSFS_VERSION 0x020700
/* /*
...@@ -88,6 +88,7 @@ ...@@ -88,6 +88,7 @@
#include <linux/pci_ids.h> #include <linux/pci_ids.h>
#include <linux/thinkpad_acpi.h>
/* ThinkPad CMOS commands */ /* ThinkPad CMOS commands */
#define TP_CMOS_VOLUME_DOWN 0 #define TP_CMOS_VOLUME_DOWN 0
...@@ -8350,6 +8351,91 @@ static struct ibm_struct fan_driver_data = { ...@@ -8350,6 +8351,91 @@ static struct ibm_struct fan_driver_data = {
.resume = fan_resume, .resume = fan_resume,
}; };
/*************************************************************************
* Mute LED subdriver
*/
struct tp_led_table {
acpi_string name;
int on_value;
int off_value;
int state;
};
static struct tp_led_table led_tables[] = {
[TPACPI_LED_MUTE] = {
.name = "SSMS",
.on_value = 1,
.off_value = 0,
},
[TPACPI_LED_MICMUTE] = {
.name = "MMTS",
.on_value = 2,
.off_value = 0,
},
};
static int mute_led_on_off(struct tp_led_table *t, bool state)
{
acpi_handle temp;
int output;
if (!ACPI_SUCCESS(acpi_get_handle(hkey_handle, t->name, &temp))) {
pr_warn("Thinkpad ACPI has no %s interface.\n", t->name);
return -EIO;
}
if (!acpi_evalf(hkey_handle, &output, t->name, "dd",
state ? t->on_value : t->off_value))
return -EIO;
t->state = state;
return state;
}
int tpacpi_led_set(int whichled, bool on)
{
struct tp_led_table *t;
if (whichled < 0 || whichled >= TPACPI_LED_MAX)
return -EINVAL;
t = &led_tables[whichled];
if (t->state < 0 || t->state == on)
return t->state;
return mute_led_on_off(t, on);
}
EXPORT_SYMBOL_GPL(tpacpi_led_set);
static int mute_led_init(struct ibm_init_struct *iibm)
{
acpi_handle temp;
int i;
for (i = 0; i < TPACPI_LED_MAX; i++) {
struct tp_led_table *t = &led_tables[i];
if (ACPI_SUCCESS(acpi_get_handle(hkey_handle, t->name, &temp)))
mute_led_on_off(t, false);
else
t->state = -ENODEV;
}
return 0;
}
static void mute_led_exit(void)
{
int i;
for (i = 0; i < TPACPI_LED_MAX; i++)
tpacpi_led_set(i, false);
}
static struct ibm_struct mute_led_driver_data = {
.name = "mute_led",
.exit = mute_led_exit,
};
/**************************************************************************** /****************************************************************************
**************************************************************************** ****************************************************************************
* *
...@@ -8768,6 +8854,10 @@ static struct ibm_init_struct ibms_init[] __initdata = { ...@@ -8768,6 +8854,10 @@ static struct ibm_init_struct ibms_init[] __initdata = {
.init = fan_init, .init = fan_init,
.data = &fan_driver_data, .data = &fan_driver_data,
}, },
{
.init = mute_led_init,
.data = &mute_led_driver_data,
},
}; };
static int __init set_ibm_param(const char *val, struct kernel_param *kp) static int __init set_ibm_param(const char *val, struct kernel_param *kp)
......
...@@ -41,6 +41,13 @@ int mc13xxx_adc_do_conversion(struct mc13xxx *mc13xxx, ...@@ -41,6 +41,13 @@ int mc13xxx_adc_do_conversion(struct mc13xxx *mc13xxx,
unsigned int mode, unsigned int channel, unsigned int mode, unsigned int channel,
u8 ato, bool atox, unsigned int *sample); u8 ato, bool atox, unsigned int *sample);
#define MC13783_AUDIO_RX0 36
#define MC13783_AUDIO_RX1 37
#define MC13783_AUDIO_TX 38
#define MC13783_SSI_NETWORK 39
#define MC13783_AUDIO_CODEC 40
#define MC13783_AUDIO_DAC 41
#define MC13XXX_IRQ_ADCDONE 0 #define MC13XXX_IRQ_ADCDONE 0
#define MC13XXX_IRQ_ADCBISDONE 1 #define MC13XXX_IRQ_ADCBISDONE 1
#define MC13XXX_IRQ_TS 2 #define MC13XXX_IRQ_TS 2
......
...@@ -84,6 +84,8 @@ struct snd_platform_data { ...@@ -84,6 +84,8 @@ struct snd_platform_data {
u8 version; u8 version;
u8 txnumevt; u8 txnumevt;
u8 rxnumevt; u8 rxnumevt;
int tx_dma_channel;
int rx_dma_channel;
}; };
enum { enum {
......
#ifndef __THINKPAD_ACPI_H__
#define __THINKPAD_ACPI_H__
/* These two functions return 0 if success, or negative error code
(e g -ENODEV if no led present) */
enum {
TPACPI_LED_MUTE,
TPACPI_LED_MICMUTE,
TPACPI_LED_MAX,
};
int tpacpi_led_set(int whichled, bool on);
#endif
...@@ -170,7 +170,7 @@ struct ak4114 { ...@@ -170,7 +170,7 @@ struct ak4114 {
void * private_data; void * private_data;
unsigned int init: 1; unsigned int init: 1;
spinlock_t lock; spinlock_t lock;
unsigned char regmap[7]; unsigned char regmap[6];
unsigned char txcsb[5]; unsigned char txcsb[5];
struct snd_kcontrol *kctls[AK4114_CONTROLS]; struct snd_kcontrol *kctls[AK4114_CONTROLS];
struct snd_pcm_substream *playback_substream; struct snd_pcm_substream *playback_substream;
...@@ -189,7 +189,7 @@ struct ak4114 { ...@@ -189,7 +189,7 @@ struct ak4114 {
int snd_ak4114_create(struct snd_card *card, int snd_ak4114_create(struct snd_card *card,
ak4114_read_t *read, ak4114_write_t *write, ak4114_read_t *read, ak4114_write_t *write,
const unsigned char pgm[7], const unsigned char txcsb[5], const unsigned char pgm[6], const unsigned char txcsb[5],
void *private_data, struct ak4114 **r_ak4114); void *private_data, struct ak4114 **r_ak4114);
void snd_ak4114_reg_write(struct ak4114 *ak4114, unsigned char reg, unsigned char mask, unsigned char val); void snd_ak4114_reg_write(struct ak4114 *ak4114, unsigned char reg, unsigned char mask, unsigned char val);
void snd_ak4114_reinit(struct ak4114 *ak4114); void snd_ak4114_reinit(struct ak4114 *ak4114);
......
...@@ -171,4 +171,13 @@ static inline void snd_compr_fragment_elapsed(struct snd_compr_stream *stream) ...@@ -171,4 +171,13 @@ static inline void snd_compr_fragment_elapsed(struct snd_compr_stream *stream)
wake_up(&stream->runtime->sleep); wake_up(&stream->runtime->sleep);
} }
static inline void snd_compr_drain_notify(struct snd_compr_stream *stream)
{
if (snd_BUG_ON(!stream))
return;
stream->runtime->state = SNDRV_PCM_STATE_SETUP;
wake_up(&stream->runtime->sleep);
}
#endif #endif
...@@ -31,6 +31,8 @@ struct cs42l52_platform_data { ...@@ -31,6 +31,8 @@ struct cs42l52_platform_data {
/* Charge Pump Freq. Check datasheet Pg73 */ /* Charge Pump Freq. Check datasheet Pg73 */
unsigned int chgfreq; unsigned int chgfreq;
/* Reset GPIO */
unsigned int reset_gpio;
}; };
#endif /* __CS42L52_H */ #endif /* __CS42L52_H */
/*
* linux/sound/cs42l73.h -- Platform data for CS42L73
*
* Copyright (c) 2012 Cirrus Logic Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __CS42L73_H
#define __CS42L73_H
struct cs42l73_platform_data {
/* RST GPIO */
unsigned int reset_gpio;
unsigned int chgfreq;
int jack_detection;
unsigned int mclk_freq;
};
#endif /* __CS42L73_H */
...@@ -61,6 +61,8 @@ struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream) ...@@ -61,6 +61,8 @@ struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream)
* @slave_id: Slave requester id for the DMA channel. * @slave_id: Slave requester id for the DMA channel.
* @filter_data: Custom DMA channel filter data, this will usually be used when * @filter_data: Custom DMA channel filter data, this will usually be used when
* requesting the DMA channel. * requesting the DMA channel.
* @chan_name: Custom channel name to use when requesting DMA channel.
* @fifo_size: FIFO size of the DAI controller in bytes
*/ */
struct snd_dmaengine_dai_dma_data { struct snd_dmaengine_dai_dma_data {
dma_addr_t addr; dma_addr_t addr;
...@@ -68,6 +70,8 @@ struct snd_dmaengine_dai_dma_data { ...@@ -68,6 +70,8 @@ struct snd_dmaengine_dai_dma_data {
u32 maxburst; u32 maxburst;
unsigned int slave_id; unsigned int slave_id;
void *filter_data; void *filter_data;
const char *chan_name;
unsigned int fifo_size;
}; };
void snd_dmaengine_pcm_set_config_from_dai_data( void snd_dmaengine_pcm_set_config_from_dai_data(
...@@ -96,6 +100,10 @@ void snd_dmaengine_pcm_set_config_from_dai_data( ...@@ -96,6 +100,10 @@ void snd_dmaengine_pcm_set_config_from_dai_data(
* playback. * playback.
*/ */
#define SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX BIT(3) #define SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX BIT(3)
/*
* The PCM streams have custom channel names specified.
*/
#define SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME BIT(4)
/** /**
* struct snd_dmaengine_pcm_config - Configuration data for dmaengine based PCM * struct snd_dmaengine_pcm_config - Configuration data for dmaengine based PCM
......
...@@ -52,6 +52,11 @@ struct snd_dma_device { ...@@ -52,6 +52,11 @@ struct snd_dma_device {
#else #else
#define SNDRV_DMA_TYPE_DEV_SG SNDRV_DMA_TYPE_DEV /* no SG-buf support */ #define SNDRV_DMA_TYPE_DEV_SG SNDRV_DMA_TYPE_DEV /* no SG-buf support */
#endif #endif
#ifdef CONFIG_GENERIC_ALLOCATOR
#define SNDRV_DMA_TYPE_DEV_IRAM 4 /* generic device iram-buffer */
#else
#define SNDRV_DMA_TYPE_DEV_IRAM SNDRV_DMA_TYPE_DEV
#endif
/* /*
* info for buffer allocation * info for buffer allocation
......
...@@ -36,7 +36,6 @@ ...@@ -36,7 +36,6 @@
#define RSND_SSI_CLK_PIN_SHARE (1 << 31) #define RSND_SSI_CLK_PIN_SHARE (1 << 31)
#define RSND_SSI_CLK_FROM_ADG (1 << 30) /* clock parent is master */ #define RSND_SSI_CLK_FROM_ADG (1 << 30) /* clock parent is master */
#define RSND_SSI_SYNC (1 << 29) /* SSI34_sync etc */ #define RSND_SSI_SYNC (1 << 29) /* SSI34_sync etc */
#define RSND_SSI_DEPENDENT (1 << 28) /* SSI needs SRU/SCU */
#define RSND_SSI_PLAY (1 << 24) #define RSND_SSI_PLAY (1 << 24)
......
...@@ -105,6 +105,8 @@ int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai, ...@@ -105,6 +105,8 @@ int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
int pll_id, int source, unsigned int freq_in, unsigned int freq_out); int pll_id, int source, unsigned int freq_in, unsigned int freq_out);
int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio);
/* Digital Audio interface formatting */ /* Digital Audio interface formatting */
int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt); int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
...@@ -131,6 +133,7 @@ struct snd_soc_dai_ops { ...@@ -131,6 +133,7 @@ struct snd_soc_dai_ops {
int (*set_pll)(struct snd_soc_dai *dai, int pll_id, int source, int (*set_pll)(struct snd_soc_dai *dai, int pll_id, int source,
unsigned int freq_in, unsigned int freq_out); unsigned int freq_in, unsigned int freq_out);
int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div); int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div);
int (*set_bclk_ratio)(struct snd_soc_dai *dai, unsigned int ratio);
/* /*
* DAI format configuration * DAI format configuration
...@@ -166,6 +169,13 @@ struct snd_soc_dai_ops { ...@@ -166,6 +169,13 @@ struct snd_soc_dai_ops {
struct snd_soc_dai *); struct snd_soc_dai *);
int (*prepare)(struct snd_pcm_substream *, int (*prepare)(struct snd_pcm_substream *,
struct snd_soc_dai *); struct snd_soc_dai *);
/*
* NOTE: Commands passed to the trigger function are not necessarily
* compatible with the current state of the dai. For example this
* sequence of commands is possible: START STOP STOP.
* So do not unconditionally use refcounting functions in the trigger
* function, e.g. clk_enable/disable.
*/
int (*trigger)(struct snd_pcm_substream *, int, int (*trigger)(struct snd_pcm_substream *, int,
struct snd_soc_dai *); struct snd_soc_dai *);
int (*bespoke_trigger)(struct snd_pcm_substream *, int, int (*bespoke_trigger)(struct snd_pcm_substream *, int,
...@@ -276,6 +286,13 @@ static inline void snd_soc_dai_set_dma_data(struct snd_soc_dai *dai, ...@@ -276,6 +286,13 @@ static inline void snd_soc_dai_set_dma_data(struct snd_soc_dai *dai,
dai->capture_dma_data = data; dai->capture_dma_data = data;
} }
static inline void snd_soc_dai_init_dma_data(struct snd_soc_dai *dai,
void *playback, void *capture)
{
dai->playback_dma_data = playback;
dai->capture_dma_data = capture;
}
static inline void snd_soc_dai_set_drvdata(struct snd_soc_dai *dai, static inline void snd_soc_dai_set_drvdata(struct snd_soc_dai *dai,
void *data) void *data)
{ {
......
...@@ -286,6 +286,8 @@ struct device; ...@@ -286,6 +286,8 @@ struct device;
.info = snd_soc_info_volsw, \ .info = snd_soc_info_volsw, \
.get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \ .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
.private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 1) } .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 1) }
#define SOC_DAPM_SINGLE_VIRT(xname, max) \
SOC_DAPM_SINGLE(xname, SND_SOC_NOPM, 0, max, 0)
#define SOC_DAPM_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \ #define SOC_DAPM_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.info = snd_soc_info_volsw, \ .info = snd_soc_info_volsw, \
...@@ -300,6 +302,8 @@ struct device; ...@@ -300,6 +302,8 @@ struct device;
.tlv.p = (tlv_array), \ .tlv.p = (tlv_array), \
.get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \ .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
.private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) } .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) }
#define SOC_DAPM_SINGLE_TLV_VIRT(xname, max, tlv_array) \
SOC_DAPM_SINGLE(xname, SND_SOC_NOPM, 0, max, 0, tlv_array)
#define SOC_DAPM_ENUM(xname, xenum) \ #define SOC_DAPM_ENUM(xname, xenum) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.info = snd_soc_info_enum_double, \ .info = snd_soc_info_enum_double, \
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#ifndef __LINUX_SND_SOC_H #ifndef __LINUX_SND_SOC_H
#define __LINUX_SND_SOC_H #define __LINUX_SND_SOC_H
#include <linux/of.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/notifier.h> #include <linux/notifier.h>
...@@ -330,7 +331,6 @@ struct soc_enum; ...@@ -330,7 +331,6 @@ struct soc_enum;
struct snd_soc_jack; struct snd_soc_jack;
struct snd_soc_jack_zone; struct snd_soc_jack_zone;
struct snd_soc_jack_pin; struct snd_soc_jack_pin;
struct snd_soc_cache_ops;
#include <sound/soc-dapm.h> #include <sound/soc-dapm.h>
#include <sound/soc-dpcm.h> #include <sound/soc-dpcm.h>
...@@ -348,10 +348,6 @@ enum snd_soc_control_type { ...@@ -348,10 +348,6 @@ enum snd_soc_control_type {
SND_SOC_REGMAP, SND_SOC_REGMAP,
}; };
enum snd_soc_compress_type {
SND_SOC_FLAT_COMPRESSION = 1,
};
enum snd_soc_pcm_subclass { enum snd_soc_pcm_subclass {
SND_SOC_PCM_CLASS_PCM = 0, SND_SOC_PCM_CLASS_PCM = 0,
SND_SOC_PCM_CLASS_BE = 1, SND_SOC_PCM_CLASS_BE = 1,
...@@ -369,6 +365,7 @@ int snd_soc_codec_set_pll(struct snd_soc_codec *codec, int pll_id, int source, ...@@ -369,6 +365,7 @@ int snd_soc_codec_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
int snd_soc_register_card(struct snd_soc_card *card); int snd_soc_register_card(struct snd_soc_card *card);
int snd_soc_unregister_card(struct snd_soc_card *card); int snd_soc_unregister_card(struct snd_soc_card *card);
int devm_snd_soc_register_card(struct device *dev, struct snd_soc_card *card);
int snd_soc_suspend(struct device *dev); int snd_soc_suspend(struct device *dev);
int snd_soc_resume(struct device *dev); int snd_soc_resume(struct device *dev);
int snd_soc_poweroff(struct device *dev); int snd_soc_poweroff(struct device *dev);
...@@ -386,6 +383,9 @@ void snd_soc_unregister_codec(struct device *dev); ...@@ -386,6 +383,9 @@ void snd_soc_unregister_codec(struct device *dev);
int snd_soc_register_component(struct device *dev, int snd_soc_register_component(struct device *dev,
const struct snd_soc_component_driver *cmpnt_drv, const struct snd_soc_component_driver *cmpnt_drv,
struct snd_soc_dai_driver *dai_drv, int num_dai); struct snd_soc_dai_driver *dai_drv, int num_dai);
int devm_snd_soc_register_component(struct device *dev,
const struct snd_soc_component_driver *cmpnt_drv,
struct snd_soc_dai_driver *dai_drv, int num_dai);
void snd_soc_unregister_component(struct device *dev); void snd_soc_unregister_component(struct device *dev);
int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int snd_soc_codec_volatile_register(struct snd_soc_codec *codec,
unsigned int reg); unsigned int reg);
...@@ -403,12 +403,6 @@ int snd_soc_cache_write(struct snd_soc_codec *codec, ...@@ -403,12 +403,6 @@ int snd_soc_cache_write(struct snd_soc_codec *codec,
unsigned int reg, unsigned int value); unsigned int reg, unsigned int value);
int snd_soc_cache_read(struct snd_soc_codec *codec, int snd_soc_cache_read(struct snd_soc_codec *codec,
unsigned int reg, unsigned int *value); unsigned int reg, unsigned int *value);
int snd_soc_default_volatile_register(struct snd_soc_codec *codec,
unsigned int reg);
int snd_soc_default_readable_register(struct snd_soc_codec *codec,
unsigned int reg);
int snd_soc_default_writable_register(struct snd_soc_codec *codec,
unsigned int reg);
int snd_soc_platform_read(struct snd_soc_platform *platform, int snd_soc_platform_read(struct snd_soc_platform *platform,
unsigned int reg); unsigned int reg);
int snd_soc_platform_write(struct snd_soc_platform *platform, int snd_soc_platform_write(struct snd_soc_platform *platform,
...@@ -541,22 +535,6 @@ int snd_soc_get_strobe(struct snd_kcontrol *kcontrol, ...@@ -541,22 +535,6 @@ int snd_soc_get_strobe(struct snd_kcontrol *kcontrol,
int snd_soc_put_strobe(struct snd_kcontrol *kcontrol, int snd_soc_put_strobe(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol); struct snd_ctl_elem_value *ucontrol);
/**
* struct snd_soc_reg_access - Describes whether a given register is
* readable, writable or volatile.
*
* @reg: the register number
* @read: whether this register is readable
* @write: whether this register is writable
* @vol: whether this register is volatile
*/
struct snd_soc_reg_access {
u16 reg;
u16 read;
u16 write;
u16 vol;
};
/** /**
* struct snd_soc_jack_pin - Describes a pin to update based on jack detection * struct snd_soc_jack_pin - Describes a pin to update based on jack detection
* *
...@@ -657,17 +635,26 @@ struct snd_soc_compr_ops { ...@@ -657,17 +635,26 @@ struct snd_soc_compr_ops {
int (*trigger)(struct snd_compr_stream *); int (*trigger)(struct snd_compr_stream *);
}; };
/* SoC cache ops */ /* component interface */
struct snd_soc_cache_ops { struct snd_soc_component_driver {
const char *name;
/* DT */
int (*of_xlate_dai_name)(struct snd_soc_component *component,
struct of_phandle_args *args,
const char **dai_name);
};
struct snd_soc_component {
const char *name; const char *name;
enum snd_soc_compress_type id; int id;
int (*init)(struct snd_soc_codec *codec); struct device *dev;
int (*exit)(struct snd_soc_codec *codec); struct list_head list;
int (*read)(struct snd_soc_codec *codec, unsigned int reg,
unsigned int *value); struct snd_soc_dai_driver *dai_drv;
int (*write)(struct snd_soc_codec *codec, unsigned int reg, int num_dai;
unsigned int value);
int (*sync)(struct snd_soc_codec *codec); const struct snd_soc_component_driver *driver;
}; };
/* SoC Audio Codec device */ /* SoC Audio Codec device */
...@@ -683,8 +670,6 @@ struct snd_soc_codec { ...@@ -683,8 +670,6 @@ struct snd_soc_codec {
struct list_head list; struct list_head list;
struct list_head card_list; struct list_head card_list;
int num_dai; int num_dai;
enum snd_soc_compress_type compress_type;
size_t reg_size; /* reg_cache_size * reg_word_size */
int (*volatile_register)(struct snd_soc_codec *, unsigned int); int (*volatile_register)(struct snd_soc_codec *, unsigned int);
int (*readable_register)(struct snd_soc_codec *, unsigned int); int (*readable_register)(struct snd_soc_codec *, unsigned int);
int (*writable_register)(struct snd_soc_codec *, unsigned int); int (*writable_register)(struct snd_soc_codec *, unsigned int);
...@@ -708,13 +693,13 @@ struct snd_soc_codec { ...@@ -708,13 +693,13 @@ struct snd_soc_codec {
unsigned int (*hw_read)(struct snd_soc_codec *, unsigned int); unsigned int (*hw_read)(struct snd_soc_codec *, unsigned int);
unsigned int (*read)(struct snd_soc_codec *, unsigned int); unsigned int (*read)(struct snd_soc_codec *, unsigned int);
int (*write)(struct snd_soc_codec *, unsigned int, unsigned int); int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);
int (*bulk_write_raw)(struct snd_soc_codec *, unsigned int, const void *, size_t);
void *reg_cache; void *reg_cache;
const void *reg_def_copy;
const struct snd_soc_cache_ops *cache_ops;
struct mutex cache_rw_mutex; struct mutex cache_rw_mutex;
int val_bytes; int val_bytes;
/* component */
struct snd_soc_component component;
/* dapm */ /* dapm */
struct snd_soc_dapm_context dapm; struct snd_soc_dapm_context dapm;
unsigned int ignore_pmdown_time:1; /* pmdown_time is ignored at stop */ unsigned int ignore_pmdown_time:1; /* pmdown_time is ignored at stop */
...@@ -733,6 +718,7 @@ struct snd_soc_codec_driver { ...@@ -733,6 +718,7 @@ struct snd_soc_codec_driver {
int (*remove)(struct snd_soc_codec *); int (*remove)(struct snd_soc_codec *);
int (*suspend)(struct snd_soc_codec *); int (*suspend)(struct snd_soc_codec *);
int (*resume)(struct snd_soc_codec *); int (*resume)(struct snd_soc_codec *);
struct snd_soc_component_driver component_driver;
/* Default control and setup, added after probe() is run */ /* Default control and setup, added after probe() is run */
const struct snd_kcontrol_new *controls; const struct snd_kcontrol_new *controls;
...@@ -760,9 +746,6 @@ struct snd_soc_codec_driver { ...@@ -760,9 +746,6 @@ struct snd_soc_codec_driver {
short reg_cache_step; short reg_cache_step;
short reg_word_size; short reg_word_size;
const void *reg_cache_default; const void *reg_cache_default;
short reg_access_size;
const struct snd_soc_reg_access *reg_access_default;
enum snd_soc_compress_type compress_type;
/* codec bias level */ /* codec bias level */
int (*set_bias_level)(struct snd_soc_codec *, int (*set_bias_level)(struct snd_soc_codec *,
...@@ -849,20 +832,6 @@ struct snd_soc_platform { ...@@ -849,20 +832,6 @@ struct snd_soc_platform {
#endif #endif
}; };
struct snd_soc_component_driver {
const char *name;
};
struct snd_soc_component {
const char *name;
int id;
int num_dai;
struct device *dev;
struct list_head list;
const struct snd_soc_component_driver *driver;
};
struct snd_soc_dai_link { struct snd_soc_dai_link {
/* config - must be set by machine driver */ /* config - must be set by machine driver */
const char *name; /* Codec name */ const char *name; /* Codec name */
...@@ -944,12 +913,6 @@ struct snd_soc_codec_conf { ...@@ -944,12 +913,6 @@ struct snd_soc_codec_conf {
* associated per device * associated per device
*/ */
const char *name_prefix; const char *name_prefix;
/*
* set this to the desired compression type if you want to
* override the one supplied in codec->driver->compress_type
*/
enum snd_soc_compress_type compress_type;
}; };
struct snd_soc_aux_dev { struct snd_soc_aux_dev {
...@@ -1088,7 +1051,8 @@ struct snd_soc_pcm_runtime { ...@@ -1088,7 +1051,8 @@ struct snd_soc_pcm_runtime {
/* mixer control */ /* mixer control */
struct soc_mixer_control { struct soc_mixer_control {
int min, max, platform_max; int min, max, platform_max;
unsigned int reg, rreg, shift, rshift; int reg, rreg;
unsigned int shift, rshift;
unsigned int invert:1; unsigned int invert:1;
unsigned int autodisable:1; unsigned int autodisable:1;
}; };
...@@ -1121,8 +1085,6 @@ struct soc_enum { ...@@ -1121,8 +1085,6 @@ struct soc_enum {
unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg); unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg);
unsigned int snd_soc_write(struct snd_soc_codec *codec, unsigned int snd_soc_write(struct snd_soc_codec *codec,
unsigned int reg, unsigned int val); unsigned int reg, unsigned int val);
unsigned int snd_soc_bulk_write_raw(struct snd_soc_codec *codec,
unsigned int reg, const void *data, size_t len);
/* device driver data */ /* device driver data */
...@@ -1201,6 +1163,8 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, ...@@ -1201,6 +1163,8 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
const char *propname); const char *propname);
unsigned int snd_soc_of_parse_daifmt(struct device_node *np, unsigned int snd_soc_of_parse_daifmt(struct device_node *np,
const char *prefix); const char *prefix);
int snd_soc_of_get_dai_name(struct device_node *of_node,
const char **dai_name);
#include <sound/soc-dai.h> #include <sound/soc-dai.h>
......
...@@ -14,6 +14,7 @@ struct snd_soc_codec; ...@@ -14,6 +14,7 @@ struct snd_soc_codec;
struct snd_soc_platform; struct snd_soc_platform;
struct snd_soc_card; struct snd_soc_card;
struct snd_soc_dapm_widget; struct snd_soc_dapm_widget;
struct snd_soc_dapm_path;
/* /*
* Log register events * Log register events
......
...@@ -5,6 +5,7 @@ header-y += asound_fm.h ...@@ -5,6 +5,7 @@ header-y += asound_fm.h
header-y += compress_offload.h header-y += compress_offload.h
header-y += compress_params.h header-y += compress_params.h
header-y += emu10k1.h header-y += emu10k1.h
header-y += firewire.h
header-y += hdsp.h header-y += hdsp.h
header-y += hdspm.h header-y += hdspm.h
header-y += sb16_csp.h header-y += sb16_csp.h
......
...@@ -93,9 +93,10 @@ enum { ...@@ -93,9 +93,10 @@ enum {
SNDRV_HWDEP_IFACE_SB_RC, /* SB Extigy/Audigy2NX remote control */ SNDRV_HWDEP_IFACE_SB_RC, /* SB Extigy/Audigy2NX remote control */
SNDRV_HWDEP_IFACE_HDA, /* HD-audio */ SNDRV_HWDEP_IFACE_HDA, /* HD-audio */
SNDRV_HWDEP_IFACE_USB_STREAM, /* direct access to usb stream */ SNDRV_HWDEP_IFACE_USB_STREAM, /* direct access to usb stream */
SNDRV_HWDEP_IFACE_FW_DICE, /* TC DICE FireWire device */
/* Don't forget to change the following: */ /* Don't forget to change the following: */
SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_USB_STREAM SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_DICE
}; };
struct snd_hwdep_info { struct snd_hwdep_info {
......
#ifndef _UAPI_SOUND_FIREWIRE_H_INCLUDED
#define _UAPI_SOUND_FIREWIRE_H_INCLUDED
#include <linux/ioctl.h>
/* events can be read() from the hwdep device */
#define SNDRV_FIREWIRE_EVENT_LOCK_STATUS 0x000010cc
#define SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION 0xd1ce004e
struct snd_firewire_event_common {
unsigned int type; /* SNDRV_FIREWIRE_EVENT_xxx */
};
struct snd_firewire_event_lock_status {
unsigned int type;
unsigned int status; /* 0/1 = unlocked/locked */
};
struct snd_firewire_event_dice_notification {
unsigned int type;
unsigned int notification; /* DICE-specific bits */
};
union snd_firewire_event {
struct snd_firewire_event_common common;
struct snd_firewire_event_lock_status lock_status;
struct snd_firewire_event_dice_notification dice_notification;
};
#define SNDRV_FIREWIRE_IOCTL_GET_INFO _IOR('H', 0xf8, struct snd_firewire_get_info)
#define SNDRV_FIREWIRE_IOCTL_LOCK _IO('H', 0xf9)
#define SNDRV_FIREWIRE_IOCTL_UNLOCK _IO('H', 0xfa)
#define SNDRV_FIREWIRE_TYPE_DICE 1
/* Fireworks, AV/C, RME, MOTU, ... */
struct snd_firewire_get_info {
unsigned int type; /* SNDRV_FIREWIRE_TYPE_xxx */
unsigned int card; /* same as fw_cdev_get_info.card */
unsigned char guid[8];
char device_name[16]; /* device node in /dev */
};
/*
* SNDRV_FIREWIRE_IOCTL_LOCK prevents the driver from streaming.
* Returns -EBUSY if the driver is already streaming.
*/
#endif /* _UAPI_SOUND_FIREWIRE_H_INCLUDED */
...@@ -117,8 +117,7 @@ static inline void pxa_ac97_warm_pxa25x(void) ...@@ -117,8 +117,7 @@ static inline void pxa_ac97_warm_pxa25x(void)
{ {
gsr_bits = 0; gsr_bits = 0;
GCR |= GCR_WARM_RST | GCR_PRIRDY_IEN | GCR_SECRDY_IEN; GCR |= GCR_WARM_RST;
wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1);
} }
static inline void pxa_ac97_cold_pxa25x(void) static inline void pxa_ac97_cold_pxa25x(void)
...@@ -129,8 +128,6 @@ static inline void pxa_ac97_cold_pxa25x(void) ...@@ -129,8 +128,6 @@ static inline void pxa_ac97_cold_pxa25x(void)
gsr_bits = 0; gsr_bits = 0;
GCR = GCR_COLD_RST; GCR = GCR_COLD_RST;
GCR |= GCR_CDONE_IE|GCR_SDONE_IE;
wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1);
} }
#endif #endif
...@@ -149,8 +146,6 @@ static inline void pxa_ac97_warm_pxa27x(void) ...@@ -149,8 +146,6 @@ static inline void pxa_ac97_warm_pxa27x(void)
static inline void pxa_ac97_cold_pxa27x(void) static inline void pxa_ac97_cold_pxa27x(void)
{ {
unsigned int timeout;
GCR &= GCR_COLD_RST; /* clear everything but nCRST */ GCR &= GCR_COLD_RST; /* clear everything but nCRST */
GCR &= ~GCR_COLD_RST; /* then assert nCRST */ GCR &= ~GCR_COLD_RST; /* then assert nCRST */
...@@ -161,29 +156,20 @@ static inline void pxa_ac97_cold_pxa27x(void) ...@@ -161,29 +156,20 @@ static inline void pxa_ac97_cold_pxa27x(void)
udelay(5); udelay(5);
clk_disable(ac97conf_clk); clk_disable(ac97conf_clk);
GCR = GCR_COLD_RST | GCR_WARM_RST; GCR = GCR_COLD_RST | GCR_WARM_RST;
timeout = 100; /* wait for the codec-ready bit to be set */
while (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--)
mdelay(1);
} }
#endif #endif
#ifdef CONFIG_PXA3xx #ifdef CONFIG_PXA3xx
static inline void pxa_ac97_warm_pxa3xx(void) static inline void pxa_ac97_warm_pxa3xx(void)
{ {
int timeout = 100;
gsr_bits = 0; gsr_bits = 0;
/* Can't use interrupts */ /* Can't use interrupts */
GCR |= GCR_WARM_RST; GCR |= GCR_WARM_RST;
while (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--)
mdelay(1);
} }
static inline void pxa_ac97_cold_pxa3xx(void) static inline void pxa_ac97_cold_pxa3xx(void)
{ {
int timeout = 1000;
/* Hold CLKBPB for 100us */ /* Hold CLKBPB for 100us */
GCR = 0; GCR = 0;
GCR = GCR_CLKBPB; GCR = GCR_CLKBPB;
...@@ -199,14 +185,13 @@ static inline void pxa_ac97_cold_pxa3xx(void) ...@@ -199,14 +185,13 @@ static inline void pxa_ac97_cold_pxa3xx(void)
GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN); GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
GCR = GCR_WARM_RST | GCR_COLD_RST; GCR = GCR_WARM_RST | GCR_COLD_RST;
while (!(GSR & (GSR_PCR | GSR_SCR)) && timeout--)
mdelay(10);
} }
#endif #endif
bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97) bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97)
{ {
unsigned long gsr; unsigned long gsr;
unsigned int timeout = 100;
#ifdef CONFIG_PXA25x #ifdef CONFIG_PXA25x
if (cpu_is_pxa25x()) if (cpu_is_pxa25x())
...@@ -223,7 +208,11 @@ bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97) ...@@ -223,7 +208,11 @@ bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97)
pxa_ac97_warm_pxa3xx(); pxa_ac97_warm_pxa3xx();
else else
#endif #endif
BUG(); snd_BUG();
while (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--)
mdelay(1);
gsr = GSR | gsr_bits; gsr = GSR | gsr_bits;
if (!(gsr & (GSR_PCR | GSR_SCR))) { if (!(gsr & (GSR_PCR | GSR_SCR))) {
printk(KERN_INFO "%s: warm reset timeout (GSR=%#lx)\n", printk(KERN_INFO "%s: warm reset timeout (GSR=%#lx)\n",
...@@ -239,6 +228,7 @@ EXPORT_SYMBOL_GPL(pxa2xx_ac97_try_warm_reset); ...@@ -239,6 +228,7 @@ EXPORT_SYMBOL_GPL(pxa2xx_ac97_try_warm_reset);
bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97) bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97)
{ {
unsigned long gsr; unsigned long gsr;
unsigned int timeout = 1000;
#ifdef CONFIG_PXA25x #ifdef CONFIG_PXA25x
if (cpu_is_pxa25x()) if (cpu_is_pxa25x())
...@@ -255,7 +245,10 @@ bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97) ...@@ -255,7 +245,10 @@ bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97)
pxa_ac97_cold_pxa3xx(); pxa_ac97_cold_pxa3xx();
else else
#endif #endif
BUG(); snd_BUG();
while (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--)
mdelay(1);
gsr = GSR | gsr_bits; gsr = GSR | gsr_bits;
if (!(gsr & (GSR_PCR | GSR_SCR))) { if (!(gsr & (GSR_PCR | GSR_SCR))) {
......
...@@ -185,7 +185,7 @@ static int pxa2xx_ac97_probe(struct platform_device *dev) ...@@ -185,7 +185,7 @@ static int pxa2xx_ac97_probe(struct platform_device *dev)
goto err; goto err;
card->dev = &dev->dev; card->dev = &dev->dev;
strncpy(card->driver, dev->dev.driver->name, sizeof(card->driver)); strlcpy(card->driver, dev->dev.driver->name, sizeof(card->driver));
ret = pxa2xx_pcm_new(card, &pxa2xx_ac97_pcm_client, &pxa2xx_ac97_pcm); ret = pxa2xx_pcm_new(card, &pxa2xx_ac97_pcm_client, &pxa2xx_ac97_pcm);
if (ret) if (ret)
......
...@@ -34,7 +34,6 @@ ...@@ -34,7 +34,6 @@
#include <linux/dw_dmac.h> #include <linux/dw_dmac.h>
#include <mach/cpu.h> #include <mach/cpu.h>
#include <mach/gpio.h>
#ifdef CONFIG_ARCH_AT91 #ifdef CONFIG_ARCH_AT91
#include <mach/hardware.h> #include <mach/hardware.h>
......
...@@ -680,14 +680,48 @@ static int snd_compr_stop(struct snd_compr_stream *stream) ...@@ -680,14 +680,48 @@ static int snd_compr_stop(struct snd_compr_stream *stream)
return -EPERM; return -EPERM;
retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP); retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP);
if (!retval) { if (!retval) {
stream->runtime->state = SNDRV_PCM_STATE_SETUP; snd_compr_drain_notify(stream);
wake_up(&stream->runtime->sleep);
stream->runtime->total_bytes_available = 0; stream->runtime->total_bytes_available = 0;
stream->runtime->total_bytes_transferred = 0; stream->runtime->total_bytes_transferred = 0;
} }
return retval; return retval;
} }
static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
{
int ret;
/*
* We are called with lock held. So drop the lock while we wait for
* drain complete notfication from the driver
*
* It is expected that driver will notify the drain completion and then
* stream will be moved to SETUP state, even if draining resulted in an
* error. We can trigger next track after this.
*/
stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
mutex_unlock(&stream->device->lock);
/* we wait for drain to complete here, drain can return when
* interruption occurred, wait returned error or success.
* For the first two cases we don't do anything different here and
* return after waking up
*/
ret = wait_event_interruptible(stream->runtime->sleep,
(stream->runtime->state != SNDRV_PCM_STATE_DRAINING));
if (ret == -ERESTARTSYS)
pr_debug("wait aborted by a signal");
else if (ret)
pr_debug("wait for drain failed with %d\n", ret);
wake_up(&stream->runtime->sleep);
mutex_lock(&stream->device->lock);
return ret;
}
static int snd_compr_drain(struct snd_compr_stream *stream) static int snd_compr_drain(struct snd_compr_stream *stream)
{ {
int retval; int retval;
...@@ -695,12 +729,15 @@ static int snd_compr_drain(struct snd_compr_stream *stream) ...@@ -695,12 +729,15 @@ static int snd_compr_drain(struct snd_compr_stream *stream)
if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
stream->runtime->state == SNDRV_PCM_STATE_SETUP) stream->runtime->state == SNDRV_PCM_STATE_SETUP)
return -EPERM; return -EPERM;
retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN); retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN);
if (!retval) { if (retval) {
stream->runtime->state = SNDRV_PCM_STATE_DRAINING; pr_debug("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval);
wake_up(&stream->runtime->sleep); wake_up(&stream->runtime->sleep);
return retval;
} }
return retval;
return snd_compress_wait_for_drain(stream);
} }
static int snd_compr_next_track(struct snd_compr_stream *stream) static int snd_compr_next_track(struct snd_compr_stream *stream)
...@@ -736,9 +773,14 @@ static int snd_compr_partial_drain(struct snd_compr_stream *stream) ...@@ -736,9 +773,14 @@ static int snd_compr_partial_drain(struct snd_compr_stream *stream)
return -EPERM; return -EPERM;
retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN); retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN);
if (retval) {
pr_debug("Partial drain returned failure\n");
wake_up(&stream->runtime->sleep);
return retval;
}
stream->next_track = false; stream->next_track = false;
return retval; return snd_compress_wait_for_drain(stream);
} }
static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg) static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
......
...@@ -66,7 +66,7 @@ static int module_slot_match(struct module *module, int idx) ...@@ -66,7 +66,7 @@ static int module_slot_match(struct module *module, int idx)
#ifdef MODULE #ifdef MODULE
const char *s1, *s2; const char *s1, *s2;
if (!module || !module->name || !slots[idx]) if (!module || !*module->name || !slots[idx])
return 0; return 0;
s1 = module->name; s1 = module->name;
...@@ -597,7 +597,7 @@ static void snd_card_set_id_no_lock(struct snd_card *card, const char *src, ...@@ -597,7 +597,7 @@ static void snd_card_set_id_no_lock(struct snd_card *card, const char *src,
/* last resort... */ /* last resort... */
snd_printk(KERN_ERR "unable to set card id (%s)\n", id); snd_printk(KERN_ERR "unable to set card id (%s)\n", id);
if (card->proc_root->name) if (card->proc_root->name)
strcpy(card->id, card->proc_root->name); strlcpy(card->id, card->proc_root->name, sizeof(card->id));
} }
/** /**
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/genalloc.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <sound/memalloc.h> #include <sound/memalloc.h>
...@@ -157,6 +158,51 @@ static void snd_free_dev_pages(struct device *dev, size_t size, void *ptr, ...@@ -157,6 +158,51 @@ static void snd_free_dev_pages(struct device *dev, size_t size, void *ptr,
dec_snd_pages(pg); dec_snd_pages(pg);
dma_free_coherent(dev, PAGE_SIZE << pg, ptr, dma); dma_free_coherent(dev, PAGE_SIZE << pg, ptr, dma);
} }
#ifdef CONFIG_GENERIC_ALLOCATOR
/**
* snd_malloc_dev_iram - allocate memory from on-chip internal ram
* @dmab: buffer allocation record to store the allocated data
* @size: number of bytes to allocate from the iram
*
* This function requires iram phandle provided via of_node
*/
static void snd_malloc_dev_iram(struct snd_dma_buffer *dmab, size_t size)
{
struct device *dev = dmab->dev.dev;
struct gen_pool *pool = NULL;
dmab->area = NULL;
dmab->addr = 0;
if (dev->of_node)
pool = of_get_named_gen_pool(dev->of_node, "iram", 0);
if (!pool)
return;
/* Assign the pool into private_data field */
dmab->private_data = pool;
dmab->area = (void *)gen_pool_alloc(pool, size);
if (!dmab->area)
return;
dmab->addr = gen_pool_virt_to_phys(pool, (unsigned long)dmab->area);
}
/**
* snd_free_dev_iram - free allocated specific memory from on-chip internal ram
* @dmab: buffer allocation record to store the allocated data
*/
static void snd_free_dev_iram(struct snd_dma_buffer *dmab)
{
struct gen_pool *pool = dmab->private_data;
if (pool && dmab->area)
gen_pool_free(pool, (unsigned long)dmab->area, dmab->bytes);
}
#endif /* CONFIG_GENERIC_ALLOCATOR */
#endif /* CONFIG_HAS_DMA */ #endif /* CONFIG_HAS_DMA */
/* /*
...@@ -197,6 +243,16 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size, ...@@ -197,6 +243,16 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size,
dmab->addr = 0; dmab->addr = 0;
break; break;
#ifdef CONFIG_HAS_DMA #ifdef CONFIG_HAS_DMA
#ifdef CONFIG_GENERIC_ALLOCATOR
case SNDRV_DMA_TYPE_DEV_IRAM:
snd_malloc_dev_iram(dmab, size);
if (dmab->area)
break;
/* Internal memory might have limited size and no enough space,
* so if we fail to malloc, try to fetch memory traditionally.
*/
dmab->dev.type = SNDRV_DMA_TYPE_DEV;
#endif /* CONFIG_GENERIC_ALLOCATOR */
case SNDRV_DMA_TYPE_DEV: case SNDRV_DMA_TYPE_DEV:
dmab->area = snd_malloc_dev_pages(device, size, &dmab->addr); dmab->area = snd_malloc_dev_pages(device, size, &dmab->addr);
break; break;
...@@ -269,6 +325,11 @@ void snd_dma_free_pages(struct snd_dma_buffer *dmab) ...@@ -269,6 +325,11 @@ void snd_dma_free_pages(struct snd_dma_buffer *dmab)
snd_free_pages(dmab->area, dmab->bytes); snd_free_pages(dmab->area, dmab->bytes);
break; break;
#ifdef CONFIG_HAS_DMA #ifdef CONFIG_HAS_DMA
#ifdef CONFIG_GENERIC_ALLOCATOR
case SNDRV_DMA_TYPE_DEV_IRAM:
snd_free_dev_iram(dmab);
break;
#endif /* CONFIG_GENERIC_ALLOCATOR */
case SNDRV_DMA_TYPE_DEV: case SNDRV_DMA_TYPE_DEV:
snd_free_dev_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr); snd_free_dev_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr);
break; break;
......
...@@ -63,23 +63,19 @@ int snd_hwparams_to_dma_slave_config(const struct snd_pcm_substream *substream, ...@@ -63,23 +63,19 @@ int snd_hwparams_to_dma_slave_config(const struct snd_pcm_substream *substream,
struct dma_slave_config *slave_config) struct dma_slave_config *slave_config)
{ {
enum dma_slave_buswidth buswidth; enum dma_slave_buswidth buswidth;
int bits;
switch (params_format(params)) { bits = snd_pcm_format_physical_width(params_format(params));
case SNDRV_PCM_FORMAT_S8: if (bits < 8 || bits > 64)
return -EINVAL;
else if (bits == 8)
buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE; buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE;
break; else if (bits == 16)
case SNDRV_PCM_FORMAT_S16_LE:
buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES; buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
break; else if (bits <= 32)
case SNDRV_PCM_FORMAT_S18_3LE:
case SNDRV_PCM_FORMAT_S20_3LE:
case SNDRV_PCM_FORMAT_S24_LE:
case SNDRV_PCM_FORMAT_S32_LE:
buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES; buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
break; else
default: buswidth = DMA_SLAVE_BUSWIDTH_8_BYTES;
return -EINVAL;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
slave_config->direction = DMA_MEM_TO_DEV; slave_config->direction = DMA_MEM_TO_DEV;
......
...@@ -2428,6 +2428,7 @@ static int snd_pcm_hwsync(struct snd_pcm_substream *substream) ...@@ -2428,6 +2428,7 @@ static int snd_pcm_hwsync(struct snd_pcm_substream *substream)
case SNDRV_PCM_STATE_DRAINING: case SNDRV_PCM_STATE_DRAINING:
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
goto __badfd; goto __badfd;
/* Fall through */
case SNDRV_PCM_STATE_RUNNING: case SNDRV_PCM_STATE_RUNNING:
if ((err = snd_pcm_update_hw_ptr(substream)) < 0) if ((err = snd_pcm_update_hw_ptr(substream)) < 0)
break; break;
...@@ -2460,6 +2461,7 @@ static int snd_pcm_delay(struct snd_pcm_substream *substream, ...@@ -2460,6 +2461,7 @@ static int snd_pcm_delay(struct snd_pcm_substream *substream,
case SNDRV_PCM_STATE_DRAINING: case SNDRV_PCM_STATE_DRAINING:
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
goto __badfd; goto __badfd;
/* Fall through */
case SNDRV_PCM_STATE_RUNNING: case SNDRV_PCM_STATE_RUNNING:
if ((err = snd_pcm_update_hw_ptr(substream)) < 0) if ((err = snd_pcm_update_hw_ptr(substream)) < 0)
break; break;
...@@ -3199,6 +3201,14 @@ int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream, ...@@ -3199,6 +3201,14 @@ int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *area) struct vm_area_struct *area)
{ {
area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
#ifdef CONFIG_GENERIC_ALLOCATOR
if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV_IRAM) {
area->vm_page_prot = pgprot_writecombine(area->vm_page_prot);
return remap_pfn_range(area, area->vm_start,
substream->dma_buffer.addr >> PAGE_SHIFT,
area->vm_end - area->vm_start, area->vm_page_prot);
}
#endif /* CONFIG_GENERIC_ALLOCATOR */
#ifdef ARCH_HAS_DMA_MMAP_COHERENT #ifdef ARCH_HAS_DMA_MMAP_COHERENT
if (!substream->ops->page && if (!substream->ops->page &&
substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV)
......
...@@ -390,6 +390,11 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan) ...@@ -390,6 +390,11 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
voice = snd_opl3_oss_map[chan->number]; voice = snd_opl3_oss_map[chan->number];
} }
if (voice < 0) {
spin_unlock_irqrestore(&opl3->voice_lock, flags);
return;
}
if (voice < MAX_OPL2_VOICES) { if (voice < MAX_OPL2_VOICES) {
/* Left register block for voices 0 .. 8 */ /* Left register block for voices 0 .. 8 */
reg_side = OPL3_LEFT; reg_side = OPL3_LEFT;
......
...@@ -46,8 +46,9 @@ static int snd_pcsp_create(struct snd_card *card) ...@@ -46,8 +46,9 @@ static int snd_pcsp_create(struct snd_card *card)
int err; int err;
int div, min_div, order; int div, min_div, order;
hrtimer_get_res(CLOCK_MONOTONIC, &tp);
if (!nopcm) { if (!nopcm) {
hrtimer_get_res(CLOCK_MONOTONIC, &tp);
if (tp.tv_sec || tp.tv_nsec > PCSP_MAX_PERIOD_NS) { if (tp.tv_sec || tp.tv_nsec > PCSP_MAX_PERIOD_NS) {
printk(KERN_ERR "PCSP: Timer resolution is not sufficient " printk(KERN_ERR "PCSP: Timer resolution is not sufficient "
"(%linS)\n", tp.tv_nsec); "(%linS)\n", tp.tv_nsec);
......
...@@ -11,6 +11,21 @@ config SND_FIREWIRE_LIB ...@@ -11,6 +11,21 @@ config SND_FIREWIRE_LIB
tristate tristate
depends on SND_PCM depends on SND_PCM
config SND_DICE
tristate "DICE-based DACs (EXPERIMENTAL)"
select SND_HWDEP
select SND_PCM
select SND_FIREWIRE_LIB
help
Say Y here to include support for many DACs based on the DICE
chip family (DICE-II/Jr/Mini) from TC Applied Technologies.
At the moment, this driver supports playback only. If you
want to use devices that support capturing, use FFADO instead.
To compile this driver as a module, choose M here: the module
will be called snd-dice.
config SND_FIREWIRE_SPEAKERS config SND_FIREWIRE_SPEAKERS
tristate "FireWire speakers" tristate "FireWire speakers"
select SND_PCM select SND_PCM
......
snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \ snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \
fcp.o cmp.o amdtp.o fcp.o cmp.o amdtp.o
snd-dice-objs := dice.o
snd-firewire-speakers-objs := speakers.o snd-firewire-speakers-objs := speakers.o
snd-isight-objs := isight.o snd-isight-objs := isight.o
snd-scs1x-objs := scs1x.o snd-scs1x-objs := scs1x.o
obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o
obj-$(CONFIG_SND_DICE) += snd-dice.o
obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o
obj-$(CONFIG_SND_ISIGHT) += snd-isight.o obj-$(CONFIG_SND_ISIGHT) += snd-isight.o
obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o
...@@ -42,9 +42,6 @@ static void pcm_period_tasklet(unsigned long data); ...@@ -42,9 +42,6 @@ static void pcm_period_tasklet(unsigned long data);
int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit, int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit,
enum cip_out_flags flags) enum cip_out_flags flags)
{ {
if (flags != CIP_NONBLOCKING)
return -EINVAL;
s->unit = fw_unit_get(unit); s->unit = fw_unit_get(unit);
s->flags = flags; s->flags = flags;
s->context = ERR_PTR(-1); s->context = ERR_PTR(-1);
...@@ -62,73 +59,91 @@ EXPORT_SYMBOL(amdtp_out_stream_init); ...@@ -62,73 +59,91 @@ EXPORT_SYMBOL(amdtp_out_stream_init);
*/ */
void amdtp_out_stream_destroy(struct amdtp_out_stream *s) void amdtp_out_stream_destroy(struct amdtp_out_stream *s)
{ {
WARN_ON(!IS_ERR(s->context)); WARN_ON(amdtp_out_stream_running(s));
mutex_destroy(&s->mutex); mutex_destroy(&s->mutex);
fw_unit_put(s->unit); fw_unit_put(s->unit);
} }
EXPORT_SYMBOL(amdtp_out_stream_destroy); EXPORT_SYMBOL(amdtp_out_stream_destroy);
const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = {
[CIP_SFC_32000] = 8,
[CIP_SFC_44100] = 8,
[CIP_SFC_48000] = 8,
[CIP_SFC_88200] = 16,
[CIP_SFC_96000] = 16,
[CIP_SFC_176400] = 32,
[CIP_SFC_192000] = 32,
};
EXPORT_SYMBOL(amdtp_syt_intervals);
/** /**
* amdtp_out_stream_set_rate - set the sample rate * amdtp_out_stream_set_parameters - set stream parameters
* @s: the AMDTP output stream to configure * @s: the AMDTP output stream to configure
* @rate: the sample rate * @rate: the sample rate
* @pcm_channels: the number of PCM samples in each data block, to be encoded
* as AM824 multi-bit linear audio
* @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels)
* *
* The sample rate must be set before the stream is started, and must not be * The parameters must be set before the stream is started, and must not be
* changed while the stream is running. * changed while the stream is running.
*/ */
void amdtp_out_stream_set_rate(struct amdtp_out_stream *s, unsigned int rate) void amdtp_out_stream_set_parameters(struct amdtp_out_stream *s,
unsigned int rate,
unsigned int pcm_channels,
unsigned int midi_ports)
{ {
static const struct { static const unsigned int rates[] = {
unsigned int rate; [CIP_SFC_32000] = 32000,
unsigned int syt_interval; [CIP_SFC_44100] = 44100,
} rate_info[] = { [CIP_SFC_48000] = 48000,
[CIP_SFC_32000] = { 32000, 8, }, [CIP_SFC_88200] = 88200,
[CIP_SFC_44100] = { 44100, 8, }, [CIP_SFC_96000] = 96000,
[CIP_SFC_48000] = { 48000, 8, }, [CIP_SFC_176400] = 176400,
[CIP_SFC_88200] = { 88200, 16, }, [CIP_SFC_192000] = 192000,
[CIP_SFC_96000] = { 96000, 16, },
[CIP_SFC_176400] = { 176400, 32, },
[CIP_SFC_192000] = { 192000, 32, },
}; };
unsigned int sfc; unsigned int sfc;
if (WARN_ON(!IS_ERR(s->context))) if (WARN_ON(amdtp_out_stream_running(s)))
return; return;
for (sfc = 0; sfc < ARRAY_SIZE(rate_info); ++sfc) for (sfc = 0; sfc < CIP_SFC_COUNT; ++sfc)
if (rate_info[sfc].rate == rate) { if (rates[sfc] == rate)
s->sfc = sfc; goto sfc_found;
s->syt_interval = rate_info[sfc].syt_interval;
return;
}
WARN_ON(1); WARN_ON(1);
return;
sfc_found:
s->dual_wire = (s->flags & CIP_HI_DUALWIRE) && sfc > CIP_SFC_96000;
if (s->dual_wire) {
sfc -= 2;
rate /= 2;
pcm_channels *= 2;
}
s->sfc = sfc;
s->data_block_quadlets = pcm_channels + DIV_ROUND_UP(midi_ports, 8);
s->pcm_channels = pcm_channels;
s->midi_ports = midi_ports;
s->syt_interval = amdtp_syt_intervals[sfc];
/* default buffering in the device */
s->transfer_delay = TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE;
if (s->flags & CIP_BLOCKING)
/* additional buffering needed to adjust for no-data packets */
s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate;
} }
EXPORT_SYMBOL(amdtp_out_stream_set_rate); EXPORT_SYMBOL(amdtp_out_stream_set_parameters);
/** /**
* amdtp_out_stream_get_max_payload - get the stream's packet size * amdtp_out_stream_get_max_payload - get the stream's packet size
* @s: the AMDTP output stream * @s: the AMDTP output stream
* *
* This function must not be called before the stream has been configured * This function must not be called before the stream has been configured
* with amdtp_out_stream_set_hw_params(), amdtp_out_stream_set_pcm(), and * with amdtp_out_stream_set_parameters().
* amdtp_out_stream_set_midi().
*/ */
unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s) unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s)
{ {
static const unsigned int max_data_blocks[] = { return 8 + s->syt_interval * s->data_block_quadlets * 4;
[CIP_SFC_32000] = 4,
[CIP_SFC_44100] = 6,
[CIP_SFC_48000] = 6,
[CIP_SFC_88200] = 12,
[CIP_SFC_96000] = 12,
[CIP_SFC_176400] = 23,
[CIP_SFC_192000] = 24,
};
s->data_block_quadlets = s->pcm_channels;
s->data_block_quadlets += DIV_ROUND_UP(s->midi_ports, 8);
return 8 + max_data_blocks[s->sfc] * 4 * s->data_block_quadlets;
} }
EXPORT_SYMBOL(amdtp_out_stream_get_max_payload); EXPORT_SYMBOL(amdtp_out_stream_get_max_payload);
...@@ -138,19 +153,26 @@ static void amdtp_write_s16(struct amdtp_out_stream *s, ...@@ -138,19 +153,26 @@ static void amdtp_write_s16(struct amdtp_out_stream *s,
static void amdtp_write_s32(struct amdtp_out_stream *s, static void amdtp_write_s32(struct amdtp_out_stream *s,
struct snd_pcm_substream *pcm, struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames); __be32 *buffer, unsigned int frames);
static void amdtp_write_s16_dualwire(struct amdtp_out_stream *s,
struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames);
static void amdtp_write_s32_dualwire(struct amdtp_out_stream *s,
struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames);
/** /**
* amdtp_out_stream_set_pcm_format - set the PCM format * amdtp_out_stream_set_pcm_format - set the PCM format
* @s: the AMDTP output stream to configure * @s: the AMDTP output stream to configure
* @format: the format of the ALSA PCM device * @format: the format of the ALSA PCM device
* *
* The sample format must be set before the stream is started, and must not be * The sample format must be set after the other paramters (rate/PCM channels/
* changed while the stream is running. * MIDI) and before the stream is started, and must not be changed while the
* stream is running.
*/ */
void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s, void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s,
snd_pcm_format_t format) snd_pcm_format_t format)
{ {
if (WARN_ON(!IS_ERR(s->context))) if (WARN_ON(amdtp_out_stream_running(s)))
return; return;
switch (format) { switch (format) {
...@@ -158,10 +180,16 @@ void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s, ...@@ -158,10 +180,16 @@ void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s,
WARN_ON(1); WARN_ON(1);
/* fall through */ /* fall through */
case SNDRV_PCM_FORMAT_S16: case SNDRV_PCM_FORMAT_S16:
s->transfer_samples = amdtp_write_s16; if (s->dual_wire)
s->transfer_samples = amdtp_write_s16_dualwire;
else
s->transfer_samples = amdtp_write_s16;
break; break;
case SNDRV_PCM_FORMAT_S32: case SNDRV_PCM_FORMAT_S32:
s->transfer_samples = amdtp_write_s32; if (s->dual_wire)
s->transfer_samples = amdtp_write_s32_dualwire;
else
s->transfer_samples = amdtp_write_s32;
break; break;
} }
} }
...@@ -248,7 +276,7 @@ static unsigned int calculate_syt(struct amdtp_out_stream *s, ...@@ -248,7 +276,7 @@ static unsigned int calculate_syt(struct amdtp_out_stream *s,
s->last_syt_offset = syt_offset; s->last_syt_offset = syt_offset;
if (syt_offset < TICKS_PER_CYCLE) { if (syt_offset < TICKS_PER_CYCLE) {
syt_offset += TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE; syt_offset += s->transfer_delay;
syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12; syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12;
syt += syt_offset % TICKS_PER_CYCLE; syt += syt_offset % TICKS_PER_CYCLE;
...@@ -268,7 +296,7 @@ static void amdtp_write_s32(struct amdtp_out_stream *s, ...@@ -268,7 +296,7 @@ static void amdtp_write_s32(struct amdtp_out_stream *s,
channels = s->pcm_channels; channels = s->pcm_channels;
src = (void *)runtime->dma_area + src = (void *)runtime->dma_area +
s->pcm_buffer_pointer * (runtime->frame_bits / 8); frames_to_bytes(runtime, s->pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
frame_step = s->data_block_quadlets - channels; frame_step = s->data_block_quadlets - channels;
...@@ -294,7 +322,7 @@ static void amdtp_write_s16(struct amdtp_out_stream *s, ...@@ -294,7 +322,7 @@ static void amdtp_write_s16(struct amdtp_out_stream *s,
channels = s->pcm_channels; channels = s->pcm_channels;
src = (void *)runtime->dma_area + src = (void *)runtime->dma_area +
s->pcm_buffer_pointer * (runtime->frame_bits / 8); frames_to_bytes(runtime, s->pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
frame_step = s->data_block_quadlets - channels; frame_step = s->data_block_quadlets - channels;
...@@ -310,6 +338,68 @@ static void amdtp_write_s16(struct amdtp_out_stream *s, ...@@ -310,6 +338,68 @@ static void amdtp_write_s16(struct amdtp_out_stream *s,
} }
} }
static void amdtp_write_s32_dualwire(struct amdtp_out_stream *s,
struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames)
{
struct snd_pcm_runtime *runtime = pcm->runtime;
unsigned int channels, frame_adjust_1, frame_adjust_2, i, c;
const u32 *src;
channels = s->pcm_channels;
src = (void *)runtime->dma_area +
s->pcm_buffer_pointer * (runtime->frame_bits / 8);
frame_adjust_1 = channels - 1;
frame_adjust_2 = 1 - (s->data_block_quadlets - channels);
channels /= 2;
for (i = 0; i < frames; ++i) {
for (c = 0; c < channels; ++c) {
*buffer = cpu_to_be32((*src >> 8) | 0x40000000);
src++;
buffer += 2;
}
buffer -= frame_adjust_1;
for (c = 0; c < channels; ++c) {
*buffer = cpu_to_be32((*src >> 8) | 0x40000000);
src++;
buffer += 2;
}
buffer -= frame_adjust_2;
}
}
static void amdtp_write_s16_dualwire(struct amdtp_out_stream *s,
struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames)
{
struct snd_pcm_runtime *runtime = pcm->runtime;
unsigned int channels, frame_adjust_1, frame_adjust_2, i, c;
const u16 *src;
channels = s->pcm_channels;
src = (void *)runtime->dma_area +
s->pcm_buffer_pointer * (runtime->frame_bits / 8);
frame_adjust_1 = channels - 1;
frame_adjust_2 = 1 - (s->data_block_quadlets - channels);
channels /= 2;
for (i = 0; i < frames; ++i) {
for (c = 0; c < channels; ++c) {
*buffer = cpu_to_be32((*src << 8) | 0x40000000);
src++;
buffer += 2;
}
buffer -= frame_adjust_1;
for (c = 0; c < channels; ++c) {
*buffer = cpu_to_be32((*src << 8) | 0x40000000);
src++;
buffer += 2;
}
buffer -= frame_adjust_2;
}
}
static void amdtp_fill_pcm_silence(struct amdtp_out_stream *s, static void amdtp_fill_pcm_silence(struct amdtp_out_stream *s,
__be32 *buffer, unsigned int frames) __be32 *buffer, unsigned int frames)
{ {
...@@ -344,8 +434,17 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle) ...@@ -344,8 +434,17 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle)
return; return;
index = s->packet_index; index = s->packet_index;
data_blocks = calculate_data_blocks(s);
syt = calculate_syt(s, cycle); syt = calculate_syt(s, cycle);
if (!(s->flags & CIP_BLOCKING)) {
data_blocks = calculate_data_blocks(s);
} else {
if (syt != 0xffff) {
data_blocks = s->syt_interval;
} else {
data_blocks = 0;
syt = 0xffffff;
}
}
buffer = s->buffer.packets[index].buffer; buffer = s->buffer.packets[index].buffer;
buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) | buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) |
...@@ -386,6 +485,9 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle) ...@@ -386,6 +485,9 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle)
s->packet_index = index; s->packet_index = index;
if (pcm) { if (pcm) {
if (s->dual_wire)
data_blocks *= 2;
ptr = s->pcm_buffer_pointer + data_blocks; ptr = s->pcm_buffer_pointer + data_blocks;
if (ptr >= pcm->runtime->buffer_size) if (ptr >= pcm->runtime->buffer_size)
ptr -= pcm->runtime->buffer_size; ptr -= pcm->runtime->buffer_size;
...@@ -455,9 +557,8 @@ static int queue_initial_skip_packets(struct amdtp_out_stream *s) ...@@ -455,9 +557,8 @@ static int queue_initial_skip_packets(struct amdtp_out_stream *s)
* @speed: firewire speed code * @speed: firewire speed code
* *
* The stream cannot be started until it has been configured with * The stream cannot be started until it has been configured with
* amdtp_out_stream_set_hw_params(), amdtp_out_stream_set_pcm(), and * amdtp_out_stream_set_parameters() and amdtp_out_stream_set_pcm_format(),
* amdtp_out_stream_set_midi(); and it must be started before any * and it must be started before any PCM or MIDI device can be started.
* PCM or MIDI device can be started.
*/ */
int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed) int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed)
{ {
...@@ -477,7 +578,7 @@ int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed) ...@@ -477,7 +578,7 @@ int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed)
mutex_lock(&s->mutex); mutex_lock(&s->mutex);
if (WARN_ON(!IS_ERR(s->context) || if (WARN_ON(amdtp_out_stream_running(s) ||
(!s->pcm_channels && !s->midi_ports))) { (!s->pcm_channels && !s->midi_ports))) {
err = -EBADFD; err = -EBADFD;
goto err_unlock; goto err_unlock;
...@@ -573,7 +674,7 @@ void amdtp_out_stream_stop(struct amdtp_out_stream *s) ...@@ -573,7 +674,7 @@ void amdtp_out_stream_stop(struct amdtp_out_stream *s)
{ {
mutex_lock(&s->mutex); mutex_lock(&s->mutex);
if (IS_ERR(s->context)) { if (!amdtp_out_stream_running(s)) {
mutex_unlock(&s->mutex); mutex_unlock(&s->mutex);
return; return;
} }
......
#ifndef SOUND_FIREWIRE_AMDTP_H_INCLUDED #ifndef SOUND_FIREWIRE_AMDTP_H_INCLUDED
#define SOUND_FIREWIRE_AMDTP_H_INCLUDED #define SOUND_FIREWIRE_AMDTP_H_INCLUDED
#include <linux/err.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include "packets-buffer.h" #include "packets-buffer.h"
...@@ -11,9 +12,18 @@ ...@@ -11,9 +12,18 @@
* sample_rate/8000 samples, with rounding up or down to adjust * sample_rate/8000 samples, with rounding up or down to adjust
* for clock skew and left-over fractional samples. This should * for clock skew and left-over fractional samples. This should
* be used if supported by the device. * be used if supported by the device.
* @CIP_BLOCKING: In blocking mode, each packet contains either zero or
* SYT_INTERVAL samples, with these two types alternating so that
* the overall sample rate comes out right.
* @CIP_HI_DUALWIRE: At rates above 96 kHz, pretend that the stream runs
* at half the actual sample rate with twice the number of channels;
* two samples of a channel are stored consecutively in the packet.
* Requires blocking mode and SYT_INTERVAL-aligned PCM buffer size.
*/ */
enum cip_out_flags { enum cip_out_flags {
CIP_NONBLOCKING = 0, CIP_NONBLOCKING = 0x00,
CIP_BLOCKING = 0x01,
CIP_HI_DUALWIRE = 0x02,
}; };
/** /**
...@@ -27,6 +37,7 @@ enum cip_sfc { ...@@ -27,6 +37,7 @@ enum cip_sfc {
CIP_SFC_96000 = 4, CIP_SFC_96000 = 4,
CIP_SFC_176400 = 5, CIP_SFC_176400 = 5,
CIP_SFC_192000 = 6, CIP_SFC_192000 = 6,
CIP_SFC_COUNT
}; };
#define AMDTP_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \ #define AMDTP_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \
...@@ -43,6 +54,7 @@ struct amdtp_out_stream { ...@@ -43,6 +54,7 @@ struct amdtp_out_stream {
struct mutex mutex; struct mutex mutex;
enum cip_sfc sfc; enum cip_sfc sfc;
bool dual_wire;
unsigned int data_block_quadlets; unsigned int data_block_quadlets;
unsigned int pcm_channels; unsigned int pcm_channels;
unsigned int midi_ports; unsigned int midi_ports;
...@@ -51,6 +63,7 @@ struct amdtp_out_stream { ...@@ -51,6 +63,7 @@ struct amdtp_out_stream {
__be32 *buffer, unsigned int frames); __be32 *buffer, unsigned int frames);
unsigned int syt_interval; unsigned int syt_interval;
unsigned int transfer_delay;
unsigned int source_node_id_field; unsigned int source_node_id_field;
struct iso_packets_buffer buffer; struct iso_packets_buffer buffer;
...@@ -74,7 +87,10 @@ int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit, ...@@ -74,7 +87,10 @@ int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit,
enum cip_out_flags flags); enum cip_out_flags flags);
void amdtp_out_stream_destroy(struct amdtp_out_stream *s); void amdtp_out_stream_destroy(struct amdtp_out_stream *s);
void amdtp_out_stream_set_rate(struct amdtp_out_stream *s, unsigned int rate); void amdtp_out_stream_set_parameters(struct amdtp_out_stream *s,
unsigned int rate,
unsigned int pcm_channels,
unsigned int midi_ports);
unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s); unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s);
int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed); int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed);
...@@ -87,31 +103,11 @@ void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s); ...@@ -87,31 +103,11 @@ void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s);
unsigned long amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s); unsigned long amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s);
void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s); void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s);
/** extern const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT];
* amdtp_out_stream_set_pcm - configure format of PCM samples
* @s: the AMDTP output stream to be configured
* @pcm_channels: the number of PCM samples in each data block, to be encoded
* as AM824 multi-bit linear audio
*
* This function must not be called while the stream is running.
*/
static inline void amdtp_out_stream_set_pcm(struct amdtp_out_stream *s,
unsigned int pcm_channels)
{
s->pcm_channels = pcm_channels;
}
/** static inline bool amdtp_out_stream_running(struct amdtp_out_stream *s)
* amdtp_out_stream_set_midi - configure format of MIDI data
* @s: the AMDTP output stream to be configured
* @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels)
*
* This function must not be called while the stream is running.
*/
static inline void amdtp_out_stream_set_midi(struct amdtp_out_stream *s,
unsigned int midi_ports)
{ {
s->midi_ports = midi_ports; return !IS_ERR(s->context);
} }
/** /**
......
...@@ -48,9 +48,6 @@ static int pcr_modify(struct cmp_connection *c, ...@@ -48,9 +48,6 @@ static int pcr_modify(struct cmp_connection *c,
int (*check)(struct cmp_connection *c, __be32 pcr), int (*check)(struct cmp_connection *c, __be32 pcr),
enum bus_reset_handling bus_reset_handling) enum bus_reset_handling bus_reset_handling)
{ {
struct fw_device *device = fw_parent_device(c->resources.unit);
int generation = c->resources.generation;
int rcode, errors = 0;
__be32 old_arg, buffer[2]; __be32 old_arg, buffer[2];
int err; int err;
...@@ -59,36 +56,31 @@ static int pcr_modify(struct cmp_connection *c, ...@@ -59,36 +56,31 @@ static int pcr_modify(struct cmp_connection *c,
old_arg = buffer[0]; old_arg = buffer[0];
buffer[1] = modify(c, buffer[0]); buffer[1] = modify(c, buffer[0]);
rcode = fw_run_transaction( err = snd_fw_transaction(
device->card, TCODE_LOCK_COMPARE_SWAP, c->resources.unit, TCODE_LOCK_COMPARE_SWAP,
device->node_id, generation, device->max_speed,
CSR_REGISTER_BASE + CSR_IPCR(c->pcr_index), CSR_REGISTER_BASE + CSR_IPCR(c->pcr_index),
buffer, 8); buffer, 8,
FW_FIXED_GENERATION | c->resources.generation);
if (rcode == RCODE_COMPLETE) {
if (buffer[0] == old_arg) /* success? */ if (err < 0) {
break; if (err == -EAGAIN &&
bus_reset_handling == SUCCEED_ON_BUS_RESET)
if (check) { err = 0;
err = check(c, buffer[0]); return err;
if (err < 0) }
return err;
} if (buffer[0] == old_arg) /* success? */
} else if (rcode == RCODE_GENERATION) break;
goto bus_reset;
else if (rcode_is_permanent_error(rcode) || ++errors >= 3) if (check) {
goto io_error; err = check(c, buffer[0]);
if (err < 0)
return err;
}
} }
c->last_pcr_value = buffer[1]; c->last_pcr_value = buffer[1];
return 0; return 0;
io_error:
cmp_error(c, "transaction failed: %s\n", fw_rcode_string(rcode));
return -EIO;
bus_reset:
return bus_reset_handling == ABORT_ON_BUS_RESET ? -EAGAIN : 0;
} }
...@@ -108,7 +100,7 @@ int cmp_connection_init(struct cmp_connection *c, ...@@ -108,7 +100,7 @@ int cmp_connection_init(struct cmp_connection *c,
err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
CSR_REGISTER_BASE + CSR_IMPR, CSR_REGISTER_BASE + CSR_IMPR,
&impr_be, 4); &impr_be, 4, 0);
if (err < 0) if (err < 0)
return err; return err;
impr = be32_to_cpu(impr_be); impr = be32_to_cpu(impr_be);
......
#ifndef SOUND_FIREWIRE_DICE_INTERFACE_H_INCLUDED
#define SOUND_FIREWIRE_DICE_INTERFACE_H_INCLUDED
/*
* DICE device interface definitions
*/
/*
* Generally, all registers can be read like memory, i.e., with quadlet read or
* block read transactions with at least quadlet-aligned offset and length.
* Writes are not allowed except where noted; quadlet-sized registers must be
* written with a quadlet write transaction.
*
* All values are in big endian. The DICE firmware runs on a little-endian CPU
* and just byte-swaps _all_ quadlets on the bus, so values without endianness
* (e.g. strings) get scrambled and must be byte-swapped again by the driver.
*/
/*
* Streaming is handled by the "DICE driver" interface. Its registers are
* located in this private address space.
*/
#define DICE_PRIVATE_SPACE 0xffffe0000000uLL
/*
* The registers are organized in several sections, which are organized
* separately to allow them to be extended individually. Whether a register is
* supported can be detected by checking its offset against its section's size.
*
* The section offset values are relative to DICE_PRIVATE_SPACE; the offset/
* size values are measured in quadlets. Read-only.
*/
#define DICE_GLOBAL_OFFSET 0x00
#define DICE_GLOBAL_SIZE 0x04
#define DICE_TX_OFFSET 0x08
#define DICE_TX_SIZE 0x0c
#define DICE_RX_OFFSET 0x10
#define DICE_RX_SIZE 0x14
#define DICE_EXT_SYNC_OFFSET 0x18
#define DICE_EXT_SYNC_SIZE 0x1c
#define DICE_UNUSED2_OFFSET 0x20
#define DICE_UNUSED2_SIZE 0x24
/*
* Global settings.
*/
/*
* Stores the full 64-bit address (node ID and offset in the node's address
* space) where the device will send notifications. Must be changed with
* a compare/swap transaction by the owner. This register is automatically
* cleared on a bus reset.
*/
#define GLOBAL_OWNER 0x000
#define OWNER_NO_OWNER 0xffff000000000000uLL
#define OWNER_NODE_SHIFT 48
/*
* A bitmask with asynchronous events; read-only. When any event(s) happen,
* the bits of previous events are cleared, and the value of this register is
* also written to the address stored in the owner register.
*/
#define GLOBAL_NOTIFICATION 0x008
/* Some registers in the Rx/Tx sections may have changed. */
#define NOTIFY_RX_CFG_CHG 0x00000001
#define NOTIFY_TX_CFG_CHG 0x00000002
/* Lock status of the current clock source may have changed. */
#define NOTIFY_LOCK_CHG 0x00000010
/* Write to the clock select register has been finished. */
#define NOTIFY_CLOCK_ACCEPTED 0x00000020
/* Lock status of some clock source has changed. */
#define NOTIFY_EXT_STATUS 0x00000040
/* Other bits may be used for device-specific events. */
/*
* A name that can be customized for each device; read/write. Padded with zero
* bytes. Quadlets are byte-swapped. The encoding is whatever the host driver
* happens to be using.
*/
#define GLOBAL_NICK_NAME 0x00c
#define NICK_NAME_SIZE 64
/*
* The current sample rate and clock source; read/write. Whether a clock
* source or sample rate is supported is device-specific; the internal clock
* source is always available. Low/mid/high = up to 48/96/192 kHz. This
* register can be changed even while streams are running.
*/
#define GLOBAL_CLOCK_SELECT 0x04c
#define CLOCK_SOURCE_MASK 0x000000ff
#define CLOCK_SOURCE_AES1 0x00000000
#define CLOCK_SOURCE_AES2 0x00000001
#define CLOCK_SOURCE_AES3 0x00000002
#define CLOCK_SOURCE_AES4 0x00000003
#define CLOCK_SOURCE_AES_ANY 0x00000004
#define CLOCK_SOURCE_ADAT 0x00000005
#define CLOCK_SOURCE_TDIF 0x00000006
#define CLOCK_SOURCE_WC 0x00000007
#define CLOCK_SOURCE_ARX1 0x00000008
#define CLOCK_SOURCE_ARX2 0x00000009
#define CLOCK_SOURCE_ARX3 0x0000000a
#define CLOCK_SOURCE_ARX4 0x0000000b
#define CLOCK_SOURCE_INTERNAL 0x0000000c
#define CLOCK_RATE_MASK 0x0000ff00
#define CLOCK_RATE_32000 0x00000000
#define CLOCK_RATE_44100 0x00000100
#define CLOCK_RATE_48000 0x00000200
#define CLOCK_RATE_88200 0x00000300
#define CLOCK_RATE_96000 0x00000400
#define CLOCK_RATE_176400 0x00000500
#define CLOCK_RATE_192000 0x00000600
#define CLOCK_RATE_ANY_LOW 0x00000700
#define CLOCK_RATE_ANY_MID 0x00000800
#define CLOCK_RATE_ANY_HIGH 0x00000900
#define CLOCK_RATE_NONE 0x00000a00
#define CLOCK_RATE_SHIFT 8
/*
* Enable streaming; read/write. Writing a non-zero value (re)starts all
* streams that have a valid iso channel set; zero stops all streams. The
* streams' parameters must be configured before starting. This register is
* automatically cleared on a bus reset.
*/
#define GLOBAL_ENABLE 0x050
/*
* Status of the sample clock; read-only.
*/
#define GLOBAL_STATUS 0x054
/* The current clock source is locked. */
#define STATUS_SOURCE_LOCKED 0x00000001
/* The actual sample rate; CLOCK_RATE_32000-_192000 or _NONE. */
#define STATUS_NOMINAL_RATE_MASK 0x0000ff00
/*
* Status of all clock sources; read-only.
*/
#define GLOBAL_EXTENDED_STATUS 0x058
/*
* The _LOCKED bits always show the current status; any change generates
* a notification.
*/
#define EXT_STATUS_AES1_LOCKED 0x00000001
#define EXT_STATUS_AES2_LOCKED 0x00000002
#define EXT_STATUS_AES3_LOCKED 0x00000004
#define EXT_STATUS_AES4_LOCKED 0x00000008
#define EXT_STATUS_ADAT_LOCKED 0x00000010
#define EXT_STATUS_TDIF_LOCKED 0x00000020
#define EXT_STATUS_ARX1_LOCKED 0x00000040
#define EXT_STATUS_ARX2_LOCKED 0x00000080
#define EXT_STATUS_ARX3_LOCKED 0x00000100
#define EXT_STATUS_ARX4_LOCKED 0x00000200
#define EXT_STATUS_WC_LOCKED 0x00000400
/*
* The _SLIP bits do not generate notifications; a set bit indicates that an
* error occurred since the last time when this register was read with
* a quadlet read transaction.
*/
#define EXT_STATUS_AES1_SLIP 0x00010000
#define EXT_STATUS_AES2_SLIP 0x00020000
#define EXT_STATUS_AES3_SLIP 0x00040000
#define EXT_STATUS_AES4_SLIP 0x00080000
#define EXT_STATUS_ADAT_SLIP 0x00100000
#define EXT_STATUS_TDIF_SLIP 0x00200000
#define EXT_STATUS_ARX1_SLIP 0x00400000
#define EXT_STATUS_ARX2_SLIP 0x00800000
#define EXT_STATUS_ARX3_SLIP 0x01000000
#define EXT_STATUS_ARX4_SLIP 0x02000000
#define EXT_STATUS_WC_SLIP 0x04000000
/*
* The measured rate of the current clock source, in Hz; read-only.
*/
#define GLOBAL_SAMPLE_RATE 0x05c
/*
* The version of the DICE driver specification that this device conforms to;
* read-only.
*/
#define GLOBAL_VERSION 0x060
/* Some old firmware versions do not have the following global registers: */
/*
* Supported sample rates and clock sources; read-only.
*/
#define GLOBAL_CLOCK_CAPABILITIES 0x064
#define CLOCK_CAP_RATE_32000 0x00000001
#define CLOCK_CAP_RATE_44100 0x00000002
#define CLOCK_CAP_RATE_48000 0x00000004
#define CLOCK_CAP_RATE_88200 0x00000008
#define CLOCK_CAP_RATE_96000 0x00000010
#define CLOCK_CAP_RATE_176400 0x00000020
#define CLOCK_CAP_RATE_192000 0x00000040
#define CLOCK_CAP_SOURCE_AES1 0x00010000
#define CLOCK_CAP_SOURCE_AES2 0x00020000
#define CLOCK_CAP_SOURCE_AES3 0x00040000
#define CLOCK_CAP_SOURCE_AES4 0x00080000
#define CLOCK_CAP_SOURCE_AES_ANY 0x00100000
#define CLOCK_CAP_SOURCE_ADAT 0x00200000
#define CLOCK_CAP_SOURCE_TDIF 0x00400000
#define CLOCK_CAP_SOURCE_WC 0x00800000
#define CLOCK_CAP_SOURCE_ARX1 0x01000000
#define CLOCK_CAP_SOURCE_ARX2 0x02000000
#define CLOCK_CAP_SOURCE_ARX3 0x04000000
#define CLOCK_CAP_SOURCE_ARX4 0x08000000
#define CLOCK_CAP_SOURCE_INTERNAL 0x10000000
/*
* Names of all clock sources; read-only. Quadlets are byte-swapped. Names
* are separated with one backslash, the list is terminated with two
* backslashes. Unused clock sources are included.
*/
#define GLOBAL_CLOCK_SOURCE_NAMES 0x068
#define CLOCK_SOURCE_NAMES_SIZE 256
/*
* Capture stream settings. This section includes the number/size registers
* and the registers of all streams.
*/
/*
* The number of supported capture streams; read-only.
*/
#define TX_NUMBER 0x000
/*
* The size of one stream's register block, in quadlets; read-only. The
* registers of the first stream follow immediately afterwards; the registers
* of the following streams are offset by this register's value.
*/
#define TX_SIZE 0x004
/*
* The isochronous channel number on which packets are sent, or -1 if the
* stream is not to be used; read/write.
*/
#define TX_ISOCHRONOUS 0x008
/*
* The number of audio channels; read-only. There will be one quadlet per
* channel; the first channel is the first quadlet in a data block.
*/
#define TX_NUMBER_AUDIO 0x00c
/*
* The number of MIDI ports, 0-8; read-only. If > 0, there will be one
* additional quadlet in each data block, following the audio quadlets.
*/
#define TX_NUMBER_MIDI 0x010
/*
* The speed at which the packets are sent, SCODE_100-_400; read/write.
*/
#define TX_SPEED 0x014
/*
* Names of all audio channels; read-only. Quadlets are byte-swapped. Names
* are separated with one backslash, the list is terminated with two
* backslashes.
*/
#define TX_NAMES 0x018
#define TX_NAMES_SIZE 256
/*
* Audio IEC60958 capabilities; read-only. Bitmask with one bit per audio
* channel.
*/
#define TX_AC3_CAPABILITIES 0x118
/*
* Send audio data with IEC60958 label; read/write. Bitmask with one bit per
* audio channel. This register can be changed even while the stream is
* running.
*/
#define TX_AC3_ENABLE 0x11c
/*
* Playback stream settings. This section includes the number/size registers
* and the registers of all streams.
*/
/*
* The number of supported playback streams; read-only.
*/
#define RX_NUMBER 0x000
/*
* The size of one stream's register block, in quadlets; read-only. The
* registers of the first stream follow immediately afterwards; the registers
* of the following streams are offset by this register's value.
*/
#define RX_SIZE 0x004
/*
* The isochronous channel number on which packets are received, or -1 if the
* stream is not to be used; read/write.
*/
#define RX_ISOCHRONOUS 0x008
/*
* Index of first quadlet to be interpreted; read/write. If > 0, that many
* quadlets at the beginning of each data block will be ignored, and all the
* audio and MIDI quadlets will follow.
*/
#define RX_SEQ_START 0x00c
/*
* The number of audio channels; read-only. There will be one quadlet per
* channel.
*/
#define RX_NUMBER_AUDIO 0x010
/*
* The number of MIDI ports, 0-8; read-only. If > 0, there will be one
* additional quadlet in each data block, following the audio quadlets.
*/
#define RX_NUMBER_MIDI 0x014
/*
* Names of all audio channels; read-only. Quadlets are byte-swapped. Names
* are separated with one backslash, the list is terminated with two
* backslashes.
*/
#define RX_NAMES 0x018
#define RX_NAMES_SIZE 256
/*
* Audio IEC60958 capabilities; read-only. Bitmask with one bit per audio
* channel.
*/
#define RX_AC3_CAPABILITIES 0x118
/*
* Receive audio data with IEC60958 label; read/write. Bitmask with one bit
* per audio channel. This register can be changed even while the stream is
* running.
*/
#define RX_AC3_ENABLE 0x11c
/*
* Extended synchronization information.
* This section can be read completely with a block read request.
*/
/*
* Current clock source; read-only.
*/
#define EXT_SYNC_CLOCK_SOURCE 0x000
/*
* Clock source is locked (boolean); read-only.
*/
#define EXT_SYNC_LOCKED 0x004
/*
* Current sample rate (CLOCK_RATE_* >> CLOCK_RATE_SHIFT), _32000-_192000 or
* _NONE; read-only.
*/
#define EXT_SYNC_RATE 0x008
/*
* ADAT user data bits; read-only.
*/
#define EXT_SYNC_ADAT_USER_DATA 0x00c
/* The data bits, if available. */
#define ADAT_USER_DATA_MASK 0x0f
/* The data bits are not available. */
#define ADAT_USER_DATA_NO_DATA 0x10
#endif
此差异已折叠。
...@@ -90,7 +90,7 @@ int fcp_avc_transaction(struct fw_unit *unit, ...@@ -90,7 +90,7 @@ int fcp_avc_transaction(struct fw_unit *unit,
: TCODE_WRITE_BLOCK_REQUEST; : TCODE_WRITE_BLOCK_REQUEST;
ret = snd_fw_transaction(t.unit, tcode, ret = snd_fw_transaction(t.unit, tcode,
CSR_REGISTER_BASE + CSR_FCP_COMMAND, CSR_REGISTER_BASE + CSR_FCP_COMMAND,
(void *)command, command_size); (void *)command, command_size, 0);
if (ret < 0) if (ret < 0)
break; break;
......
...@@ -217,7 +217,7 @@ static void isight_packet(struct fw_iso_context *context, u32 cycle, ...@@ -217,7 +217,7 @@ static void isight_packet(struct fw_iso_context *context, u32 cycle,
static int isight_connect(struct isight *isight) static int isight_connect(struct isight *isight)
{ {
int ch, err, rcode, errors = 0; int ch, err;
__be32 value; __be32 value;
retry_after_bus_reset: retry_after_bus_reset:
...@@ -230,27 +230,19 @@ static int isight_connect(struct isight *isight) ...@@ -230,27 +230,19 @@ static int isight_connect(struct isight *isight)
} }
value = cpu_to_be32(ch | (isight->device->max_speed << SPEED_SHIFT)); value = cpu_to_be32(ch | (isight->device->max_speed << SPEED_SHIFT));
for (;;) { err = snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST,
rcode = fw_run_transaction( isight->audio_base + REG_ISO_TX_CONFIG,
isight->device->card, &value, 4, FW_FIXED_GENERATION |
TCODE_WRITE_QUADLET_REQUEST, isight->resources.generation);
isight->device->node_id, if (err == -EAGAIN) {
isight->resources.generation, fw_iso_resources_free(&isight->resources);
isight->device->max_speed, goto retry_after_bus_reset;
isight->audio_base + REG_ISO_TX_CONFIG, } else if (err < 0) {
&value, 4); goto err_resources;
if (rcode == RCODE_COMPLETE) {
return 0;
} else if (rcode == RCODE_GENERATION) {
fw_iso_resources_free(&isight->resources);
goto retry_after_bus_reset;
} else if (rcode_is_permanent_error(rcode) || ++errors >= 3) {
err = -EIO;
goto err_resources;
}
msleep(5);
} }
return 0;
err_resources: err_resources:
fw_iso_resources_free(&isight->resources); fw_iso_resources_free(&isight->resources);
error: error:
...@@ -315,17 +307,19 @@ static int isight_hw_params(struct snd_pcm_substream *substream, ...@@ -315,17 +307,19 @@ static int isight_hw_params(struct snd_pcm_substream *substream,
static int reg_read(struct isight *isight, int offset, __be32 *value) static int reg_read(struct isight *isight, int offset, __be32 *value)
{ {
return snd_fw_transaction(isight->unit, TCODE_READ_QUADLET_REQUEST, return snd_fw_transaction(isight->unit, TCODE_READ_QUADLET_REQUEST,
isight->audio_base + offset, value, 4); isight->audio_base + offset, value, 4, 0);
} }
static int reg_write(struct isight *isight, int offset, __be32 value) static int reg_write(struct isight *isight, int offset, __be32 value)
{ {
return snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST, return snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST,
isight->audio_base + offset, &value, 4); isight->audio_base + offset, &value, 4, 0);
} }
static void isight_stop_streaming(struct isight *isight) static void isight_stop_streaming(struct isight *isight)
{ {
__be32 value;
if (!isight->context) if (!isight->context)
return; return;
...@@ -333,7 +327,10 @@ static void isight_stop_streaming(struct isight *isight) ...@@ -333,7 +327,10 @@ static void isight_stop_streaming(struct isight *isight)
fw_iso_context_destroy(isight->context); fw_iso_context_destroy(isight->context);
isight->context = NULL; isight->context = NULL;
fw_iso_resources_free(&isight->resources); fw_iso_resources_free(&isight->resources);
reg_write(isight, REG_AUDIO_ENABLE, 0); value = 0;
snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST,
isight->audio_base + REG_AUDIO_ENABLE,
&value, 4, FW_QUIET);
} }
static int isight_hw_free(struct snd_pcm_substream *substream) static int isight_hw_free(struct snd_pcm_substream *substream)
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
#include <linux/module.h> #include <linux/module.h>
#include "lib.h" #include "lib.h"
#define ERROR_RETRY_DELAY_MS 5 #define ERROR_RETRY_DELAY_MS 20
/** /**
* snd_fw_transaction - send a request and wait for its completion * snd_fw_transaction - send a request and wait for its completion
...@@ -20,6 +20,9 @@ ...@@ -20,6 +20,9 @@
* @offset: the address in the target's address space * @offset: the address in the target's address space
* @buffer: input/output data * @buffer: input/output data
* @length: length of @buffer * @length: length of @buffer
* @flags: use %FW_FIXED_GENERATION and add the generation value to attempt the
* request only in that generation; use %FW_QUIET to suppress error
* messages
* *
* Submits an asynchronous request to the target device, and waits for the * Submits an asynchronous request to the target device, and waits for the
* response. The node ID and the current generation are derived from @unit. * response. The node ID and the current generation are derived from @unit.
...@@ -27,14 +30,18 @@ ...@@ -27,14 +30,18 @@
* Returns zero on success, or a negative error code. * Returns zero on success, or a negative error code.
*/ */
int snd_fw_transaction(struct fw_unit *unit, int tcode, int snd_fw_transaction(struct fw_unit *unit, int tcode,
u64 offset, void *buffer, size_t length) u64 offset, void *buffer, size_t length,
unsigned int flags)
{ {
struct fw_device *device = fw_parent_device(unit); struct fw_device *device = fw_parent_device(unit);
int generation, rcode, tries = 0; int generation, rcode, tries = 0;
generation = flags & FW_GENERATION_MASK;
for (;;) { for (;;) {
generation = device->generation; if (!(flags & FW_FIXED_GENERATION)) {
smp_rmb(); /* node_id vs. generation */ generation = device->generation;
smp_rmb(); /* node_id vs. generation */
}
rcode = fw_run_transaction(device->card, tcode, rcode = fw_run_transaction(device->card, tcode,
device->node_id, generation, device->node_id, generation,
device->max_speed, offset, device->max_speed, offset,
...@@ -43,9 +50,14 @@ int snd_fw_transaction(struct fw_unit *unit, int tcode, ...@@ -43,9 +50,14 @@ int snd_fw_transaction(struct fw_unit *unit, int tcode,
if (rcode == RCODE_COMPLETE) if (rcode == RCODE_COMPLETE)
return 0; return 0;
if (rcode == RCODE_GENERATION && (flags & FW_FIXED_GENERATION))
return -EAGAIN;
if (rcode_is_permanent_error(rcode) || ++tries >= 3) { if (rcode_is_permanent_error(rcode) || ++tries >= 3) {
dev_err(&unit->device, "transaction failed: %s\n", if (!(flags & FW_QUIET))
fw_rcode_string(rcode)); dev_err(&unit->device,
"transaction failed: %s\n",
fw_rcode_string(rcode));
return -EIO; return -EIO;
} }
......
...@@ -6,8 +6,13 @@ ...@@ -6,8 +6,13 @@
struct fw_unit; struct fw_unit;
#define FW_GENERATION_MASK 0x00ff
#define FW_FIXED_GENERATION 0x0100
#define FW_QUIET 0x0200
int snd_fw_transaction(struct fw_unit *unit, int tcode, int snd_fw_transaction(struct fw_unit *unit, int tcode,
u64 offset, void *buffer, size_t length); u64 offset, void *buffer, size_t length,
unsigned int flags);
/* returns true if retrying the transaction would not make sense */ /* returns true if retrying the transaction would not make sense */
static inline bool rcode_is_permanent_error(int rcode) static inline bool rcode_is_permanent_error(int rcode)
......
...@@ -369,7 +369,7 @@ static int scs_init_hss_address(struct scs *scs) ...@@ -369,7 +369,7 @@ static int scs_init_hss_address(struct scs *scs)
data = cpu_to_be64(((u64)HSS1394_TAG_CHANGE_ADDRESS << 56) | data = cpu_to_be64(((u64)HSS1394_TAG_CHANGE_ADDRESS << 56) |
scs->hss_handler.offset); scs->hss_handler.offset);
err = snd_fw_transaction(scs->unit, TCODE_WRITE_BLOCK_REQUEST, err = snd_fw_transaction(scs->unit, TCODE_WRITE_BLOCK_REQUEST,
HSS1394_ADDRESS, &data, 8); HSS1394_ADDRESS, &data, 8, 0);
if (err < 0) if (err < 0)
dev_err(&scs->unit->device, "HSS1394 communication failed\n"); dev_err(&scs->unit->device, "HSS1394 communication failed\n");
...@@ -455,12 +455,16 @@ static int scs_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) ...@@ -455,12 +455,16 @@ static int scs_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
static void scs_update(struct fw_unit *unit) static void scs_update(struct fw_unit *unit)
{ {
struct scs *scs = dev_get_drvdata(&unit->device); struct scs *scs = dev_get_drvdata(&unit->device);
int generation;
__be64 data; __be64 data;
data = cpu_to_be64(((u64)HSS1394_TAG_CHANGE_ADDRESS << 56) | data = cpu_to_be64(((u64)HSS1394_TAG_CHANGE_ADDRESS << 56) |
scs->hss_handler.offset); scs->hss_handler.offset);
generation = fw_parent_device(unit)->generation;
smp_rmb(); /* node_id vs. generation */
snd_fw_transaction(scs->unit, TCODE_WRITE_BLOCK_REQUEST, snd_fw_transaction(scs->unit, TCODE_WRITE_BLOCK_REQUEST,
HSS1394_ADDRESS, &data, 8); HSS1394_ADDRESS, &data, 8,
FW_FIXED_GENERATION | generation);
} }
static void scs_remove(struct fw_unit *unit) static void scs_remove(struct fw_unit *unit)
......
...@@ -52,7 +52,6 @@ struct fwspk { ...@@ -52,7 +52,6 @@ struct fwspk {
struct mutex mutex; struct mutex mutex;
struct cmp_connection connection; struct cmp_connection connection;
struct amdtp_out_stream stream; struct amdtp_out_stream stream;
bool stream_running;
bool mute; bool mute;
s16 volume[6]; s16 volume[6];
s16 volume_min; s16 volume_min;
...@@ -188,10 +187,9 @@ static int fwspk_close(struct snd_pcm_substream *substream) ...@@ -188,10 +187,9 @@ static int fwspk_close(struct snd_pcm_substream *substream)
static void fwspk_stop_stream(struct fwspk *fwspk) static void fwspk_stop_stream(struct fwspk *fwspk)
{ {
if (fwspk->stream_running) { if (amdtp_out_stream_running(&fwspk->stream)) {
amdtp_out_stream_stop(&fwspk->stream); amdtp_out_stream_stop(&fwspk->stream);
cmp_connection_break(&fwspk->connection); cmp_connection_break(&fwspk->connection);
fwspk->stream_running = false;
} }
} }
...@@ -246,8 +244,10 @@ static int fwspk_hw_params(struct snd_pcm_substream *substream, ...@@ -246,8 +244,10 @@ static int fwspk_hw_params(struct snd_pcm_substream *substream,
if (err < 0) if (err < 0)
goto error; goto error;
amdtp_out_stream_set_rate(&fwspk->stream, params_rate(hw_params)); amdtp_out_stream_set_parameters(&fwspk->stream,
amdtp_out_stream_set_pcm(&fwspk->stream, params_channels(hw_params)); params_rate(hw_params),
params_channels(hw_params),
0);
amdtp_out_stream_set_pcm_format(&fwspk->stream, amdtp_out_stream_set_pcm_format(&fwspk->stream,
params_format(hw_params)); params_format(hw_params));
...@@ -285,7 +285,7 @@ static int fwspk_prepare(struct snd_pcm_substream *substream) ...@@ -285,7 +285,7 @@ static int fwspk_prepare(struct snd_pcm_substream *substream)
if (amdtp_out_streaming_error(&fwspk->stream)) if (amdtp_out_streaming_error(&fwspk->stream))
fwspk_stop_stream(fwspk); fwspk_stop_stream(fwspk);
if (!fwspk->stream_running) { if (!amdtp_out_stream_running(&fwspk->stream)) {
err = cmp_connection_establish(&fwspk->connection, err = cmp_connection_establish(&fwspk->connection,
amdtp_out_stream_get_max_payload(&fwspk->stream)); amdtp_out_stream_get_max_payload(&fwspk->stream));
if (err < 0) if (err < 0)
...@@ -296,8 +296,6 @@ static int fwspk_prepare(struct snd_pcm_substream *substream) ...@@ -296,8 +296,6 @@ static int fwspk_prepare(struct snd_pcm_substream *substream)
fwspk->connection.speed); fwspk->connection.speed);
if (err < 0) if (err < 0)
goto err_connection; goto err_connection;
fwspk->stream_running = true;
} }
mutex_unlock(&fwspk->mutex); mutex_unlock(&fwspk->mutex);
...@@ -647,7 +645,7 @@ static u32 fwspk_read_firmware_version(struct fw_unit *unit) ...@@ -647,7 +645,7 @@ static u32 fwspk_read_firmware_version(struct fw_unit *unit)
int err; int err;
err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
OXFORD_FIRMWARE_ID_ADDRESS, &data, 4); OXFORD_FIRMWARE_ID_ADDRESS, &data, 4, 0);
return err >= 0 ? be32_to_cpu(data) : 0; return err >= 0 ? be32_to_cpu(data) : 0;
} }
......
...@@ -60,7 +60,7 @@ static void reg_dump(struct ak4114 *ak4114) ...@@ -60,7 +60,7 @@ static void reg_dump(struct ak4114 *ak4114)
printk(KERN_DEBUG "AK4114 REG DUMP:\n"); printk(KERN_DEBUG "AK4114 REG DUMP:\n");
for (i = 0; i < 0x20; i++) for (i = 0; i < 0x20; i++)
printk(KERN_DEBUG "reg[%02x] = %02x (%02x)\n", i, reg_read(ak4114, i), i < sizeof(ak4114->regmap) ? ak4114->regmap[i] : 0); printk(KERN_DEBUG "reg[%02x] = %02x (%02x)\n", i, reg_read(ak4114, i), i < ARRAY_SIZE(ak4114->regmap) ? ak4114->regmap[i] : 0);
} }
#endif #endif
...@@ -81,7 +81,7 @@ static int snd_ak4114_dev_free(struct snd_device *device) ...@@ -81,7 +81,7 @@ static int snd_ak4114_dev_free(struct snd_device *device)
int snd_ak4114_create(struct snd_card *card, int snd_ak4114_create(struct snd_card *card,
ak4114_read_t *read, ak4114_write_t *write, ak4114_read_t *read, ak4114_write_t *write,
const unsigned char pgm[7], const unsigned char txcsb[5], const unsigned char pgm[6], const unsigned char txcsb[5],
void *private_data, struct ak4114 **r_ak4114) void *private_data, struct ak4114 **r_ak4114)
{ {
struct ak4114 *chip; struct ak4114 *chip;
...@@ -101,7 +101,7 @@ int snd_ak4114_create(struct snd_card *card, ...@@ -101,7 +101,7 @@ int snd_ak4114_create(struct snd_card *card,
chip->private_data = private_data; chip->private_data = private_data;
INIT_DELAYED_WORK(&chip->work, ak4114_stats); INIT_DELAYED_WORK(&chip->work, ak4114_stats);
for (reg = 0; reg < 7; reg++) for (reg = 0; reg < 6; reg++)
chip->regmap[reg] = pgm[reg]; chip->regmap[reg] = pgm[reg];
for (reg = 0; reg < 5; reg++) for (reg = 0; reg < 5; reg++)
chip->txcsb[reg] = txcsb[reg]; chip->txcsb[reg] = txcsb[reg];
...@@ -142,7 +142,7 @@ static void ak4114_init_regs(struct ak4114 *chip) ...@@ -142,7 +142,7 @@ static void ak4114_init_regs(struct ak4114 *chip)
/* release reset, but leave powerdown */ /* release reset, but leave powerdown */
reg_write(chip, AK4114_REG_PWRDN, (old | AK4114_RST) & ~AK4114_PWN); reg_write(chip, AK4114_REG_PWRDN, (old | AK4114_RST) & ~AK4114_PWN);
udelay(200); udelay(200);
for (reg = 1; reg < 7; reg++) for (reg = 1; reg < 6; reg++)
reg_write(chip, reg, chip->regmap[reg]); reg_write(chip, reg, chip->regmap[reg]);
for (reg = 0; reg < 5; reg++) for (reg = 0; reg < 5; reg++)
reg_write(chip, reg + AK4114_REG_TXCSB0, chip->txcsb[reg]); reg_write(chip, reg + AK4114_REG_TXCSB0, chip->txcsb[reg]);
......
...@@ -583,7 +583,7 @@ static int ak4xxx_capture_source_info(struct snd_kcontrol *kcontrol, ...@@ -583,7 +583,7 @@ static int ak4xxx_capture_source_info(struct snd_kcontrol *kcontrol,
if (idx >= num_names) if (idx >= num_names)
return -EINVAL; return -EINVAL;
input_names = ak->adc_info[mixer_ch].input_names; input_names = ak->adc_info[mixer_ch].input_names;
strncpy(uinfo->value.enumerated.name, input_names[idx], strlcpy(uinfo->value.enumerated.name, input_names[idx],
sizeof(uinfo->value.enumerated.name)); sizeof(uinfo->value.enumerated.name));
return 0; return 0;
} }
......
...@@ -126,6 +126,7 @@ static void snd_cmi8328_cfg_write(u16 port, u8 reg, u8 val) ...@@ -126,6 +126,7 @@ static void snd_cmi8328_cfg_write(u16 port, u8 reg, u8 val)
outb(val, port + 3); /* yes, value goes to the same port as index */ outb(val, port + 3); /* yes, value goes to the same port as index */
} }
#ifdef CONFIG_PM
static void snd_cmi8328_cfg_save(u16 port, u8 cfg[]) static void snd_cmi8328_cfg_save(u16 port, u8 cfg[])
{ {
cfg[0] = snd_cmi8328_cfg_read(port, CFG1); cfg[0] = snd_cmi8328_cfg_read(port, CFG1);
...@@ -139,6 +140,7 @@ static void snd_cmi8328_cfg_restore(u16 port, u8 cfg[]) ...@@ -139,6 +140,7 @@ static void snd_cmi8328_cfg_restore(u16 port, u8 cfg[])
snd_cmi8328_cfg_write(port, CFG2, cfg[1]); snd_cmi8328_cfg_write(port, CFG2, cfg[1]);
snd_cmi8328_cfg_write(port, CFG3, cfg[2]); snd_cmi8328_cfg_write(port, CFG3, cfg[2]);
} }
#endif /* CONFIG_PM */
static int snd_cmi8328_mixer(struct snd_wss *chip) static int snd_cmi8328_mixer(struct snd_wss *chip)
{ {
......
...@@ -208,6 +208,7 @@ static int snd_sb_csp_ioctl(struct snd_hwdep * hw, struct file *file, unsigned i ...@@ -208,6 +208,7 @@ static int snd_sb_csp_ioctl(struct snd_hwdep * hw, struct file *file, unsigned i
switch (cmd) { switch (cmd) {
/* get information */ /* get information */
case SNDRV_SB_CSP_IOCTL_INFO: case SNDRV_SB_CSP_IOCTL_INFO:
memset(&info, 0, sizeof(info));
*info.codec_name = *p->codec_name; *info.codec_name = *p->codec_name;
info.func_nr = p->func_nr; info.func_nr = p->func_nr;
info.acc_format = p->acc_format; info.acc_format = p->acc_format;
......
...@@ -276,7 +276,7 @@ static void ad1843_write_multi(struct snd_ad1843 *ad1843, int argcount, ...) ...@@ -276,7 +276,7 @@ static void ad1843_write_multi(struct snd_ad1843 *ad1843, int argcount, ...)
if (reg == -1) if (reg == -1)
reg = fp->reg; reg = fp->reg;
else else
BUG_ON(reg != fp->reg); WARN_ON(reg != fp->reg);
m = ((1 << fp->nbits) - 1) << fp->lo_bit; m = ((1 << fp->nbits) - 1) << fp->lo_bit;
mask |= m; mask |= m;
bits |= (value << fp->lo_bit) & m; bits |= (value << fp->lo_bit) & m;
......
...@@ -1544,7 +1544,7 @@ static int ess_has_rec_mixer (int submodel) ...@@ -1544,7 +1544,7 @@ static int ess_has_rec_mixer (int submodel)
return 1; return 1;
default: default:
return 0; return 0;
}; }
}; };
#ifdef FKS_LOGGING #ifdef FKS_LOGGING
......
...@@ -739,7 +739,7 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe ...@@ -739,7 +739,7 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe
reg = ad1889_readw(chip, AD_DS_WADA); reg = ad1889_readw(chip, AD_DS_WADA);
snd_iprintf(buffer, "Right: %s, -%d dB\n", snd_iprintf(buffer, "Right: %s, -%d dB\n",
(reg & AD_DS_WADA_RWAM) ? "mute" : "unmute", (reg & AD_DS_WADA_RWAM) ? "mute" : "unmute",
((reg & AD_DS_WADA_RWAA) >> 8) * 3); (reg & AD_DS_WADA_RWAA) * 3);
reg = ad1889_readw(chip, AD_DS_WAS); reg = ad1889_readw(chip, AD_DS_WAS);
snd_iprintf(buffer, "Wave samplerate: %u Hz\n", reg); snd_iprintf(buffer, "Wave samplerate: %u Hz\n", reg);
......
...@@ -855,7 +855,6 @@ static void snd_ali_disable_spdif_out(struct snd_ali *codec) ...@@ -855,7 +855,6 @@ static void snd_ali_disable_spdif_out(struct snd_ali *codec)
static void snd_ali_update_ptr(struct snd_ali *codec, int channel) static void snd_ali_update_ptr(struct snd_ali *codec, int channel)
{ {
struct snd_ali_voice *pvoice; struct snd_ali_voice *pvoice;
struct snd_pcm_runtime *runtime;
struct snd_ali_channel_control *pchregs; struct snd_ali_channel_control *pchregs;
unsigned int old, mask; unsigned int old, mask;
#ifdef ALI_DEBUG #ifdef ALI_DEBUG
...@@ -872,7 +871,6 @@ static void snd_ali_update_ptr(struct snd_ali *codec, int channel) ...@@ -872,7 +871,6 @@ static void snd_ali_update_ptr(struct snd_ali *codec, int channel)
return; return;
pvoice = &codec->synth.voices[channel]; pvoice = &codec->synth.voices[channel];
runtime = pvoice->substream->runtime;
udelay(100); udelay(100);
spin_lock(&codec->reg_lock); spin_lock(&codec->reg_lock);
......
...@@ -1913,6 +1913,7 @@ static int snd_asihpi_tuner_band_put(struct snd_kcontrol *kcontrol, ...@@ -1913,6 +1913,7 @@ static int snd_asihpi_tuner_band_put(struct snd_kcontrol *kcontrol,
struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol); struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol);
*/ */
u32 h_control = kcontrol->private_value; u32 h_control = kcontrol->private_value;
unsigned int idx;
u16 band; u16 band;
u16 tuner_bands[HPI_TUNER_BAND_LAST]; u16 tuner_bands[HPI_TUNER_BAND_LAST];
u32 num_bands = 0; u32 num_bands = 0;
...@@ -1920,7 +1921,10 @@ static int snd_asihpi_tuner_band_put(struct snd_kcontrol *kcontrol, ...@@ -1920,7 +1921,10 @@ static int snd_asihpi_tuner_band_put(struct snd_kcontrol *kcontrol,
num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands, num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands,
HPI_TUNER_BAND_LAST); HPI_TUNER_BAND_LAST);
band = tuner_bands[ucontrol->value.enumerated.item[0]]; idx = ucontrol->value.enumerated.item[0];
if (idx >= ARRAY_SIZE(tuner_bands))
idx = ARRAY_SIZE(tuner_bands) - 1;
band = tuner_bands[idx];
hpi_handle_error(hpi_tuner_set_band(h_control, band)); hpi_handle_error(hpi_tuner_set_band(h_control, band));
return 1; return 1;
...@@ -2383,7 +2387,8 @@ static int snd_asihpi_clksrc_put(struct snd_kcontrol *kcontrol, ...@@ -2383,7 +2387,8 @@ static int snd_asihpi_clksrc_put(struct snd_kcontrol *kcontrol,
struct snd_card_asihpi *asihpi = struct snd_card_asihpi *asihpi =
(struct snd_card_asihpi *)(kcontrol->private_data); (struct snd_card_asihpi *)(kcontrol->private_data);
struct clk_cache *clkcache = &asihpi->cc; struct clk_cache *clkcache = &asihpi->cc;
int change, item; unsigned int item;
int change;
u32 h_control = kcontrol->private_value; u32 h_control = kcontrol->private_value;
change = 1; change = 1;
......
...@@ -671,7 +671,7 @@ static int snd_vortex_new_pcm(vortex_t *chip, int idx, int nr) ...@@ -671,7 +671,7 @@ static int snd_vortex_new_pcm(vortex_t *chip, int idx, int nr)
return err; return err;
break; break;
#endif #endif
}; }
if (VORTEX_PCM_TYPE(pcm) == VORTEX_PCM_SPDIF) { if (VORTEX_PCM_TYPE(pcm) == VORTEX_PCM_SPDIF) {
for (i = 0; i < ARRAY_SIZE(snd_vortex_mixer_spdif); i++) { for (i = 0; i < ARRAY_SIZE(snd_vortex_mixer_spdif); i++) {
......
...@@ -219,7 +219,6 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt, ...@@ -219,7 +219,6 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt,
*/ */
hwwrite(vortex->mmio, WT_RUN(wt), val); hwwrite(vortex->mmio, WT_RUN(wt), val);
return 0xc; return 0xc;
break;
case 1: /* param 0 */ case 1: /* param 0 */
/* /*
printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
...@@ -227,7 +226,6 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt, ...@@ -227,7 +226,6 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt,
*/ */
hwwrite(vortex->mmio, WT_PARM(wt, 0), val); hwwrite(vortex->mmio, WT_PARM(wt, 0), val);
return 0xc; return 0xc;
break;
case 2: /* param 1 */ case 2: /* param 1 */
/* /*
printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
...@@ -235,7 +233,6 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt, ...@@ -235,7 +233,6 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt,
*/ */
hwwrite(vortex->mmio, WT_PARM(wt, 1), val); hwwrite(vortex->mmio, WT_PARM(wt, 1), val);
return 0xc; return 0xc;
break;
case 3: /* param 2 */ case 3: /* param 2 */
/* /*
printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
...@@ -243,7 +240,6 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt, ...@@ -243,7 +240,6 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt,
*/ */
hwwrite(vortex->mmio, WT_PARM(wt, 2), val); hwwrite(vortex->mmio, WT_PARM(wt, 2), val);
return 0xc; return 0xc;
break;
case 4: /* param 3 */ case 4: /* param 3 */
/* /*
printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
...@@ -251,7 +247,6 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt, ...@@ -251,7 +247,6 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt,
*/ */
hwwrite(vortex->mmio, WT_PARM(wt, 3), val); hwwrite(vortex->mmio, WT_PARM(wt, 3), val);
return 0xc; return 0xc;
break;
case 6: /* mute */ case 6: /* mute */
/* /*
printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
...@@ -259,20 +254,17 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt, ...@@ -259,20 +254,17 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt,
*/ */
hwwrite(vortex->mmio, WT_MUTE(wt), val); hwwrite(vortex->mmio, WT_MUTE(wt), val);
return 0xc; return 0xc;
break;
case 0xb: case 0xb:
{ /* delay */ /* delay */
/* /*
printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
WT_DELAY(wt,0), (int)val); WT_DELAY(wt,0), (int)val);
*/ */
hwwrite(vortex->mmio, WT_DELAY(wt, 3), val); hwwrite(vortex->mmio, WT_DELAY(wt, 3), val);
hwwrite(vortex->mmio, WT_DELAY(wt, 2), val); hwwrite(vortex->mmio, WT_DELAY(wt, 2), val);
hwwrite(vortex->mmio, WT_DELAY(wt, 1), val); hwwrite(vortex->mmio, WT_DELAY(wt, 1), val);
hwwrite(vortex->mmio, WT_DELAY(wt, 0), val); hwwrite(vortex->mmio, WT_DELAY(wt, 0), val);
return 0xc; return 0xc;
}
break;
/* Global WT block parameters */ /* Global WT block parameters */
case 5: /* sramp */ case 5: /* sramp */
ecx = WT_SRAMP(wt); ecx = WT_SRAMP(wt);
...@@ -291,7 +283,6 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt, ...@@ -291,7 +283,6 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt,
break; break;
default: default:
return 0; return 0;
break;
} }
/* /*
printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", ecx, (int)val); printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", ecx, (int)val);
......
...@@ -715,14 +715,14 @@ snd_azf3328_mixer_ac97_read(struct snd_ac97 *ac97, unsigned short reg_ac97) ...@@ -715,14 +715,14 @@ snd_azf3328_mixer_ac97_read(struct snd_ac97 *ac97, unsigned short reg_ac97)
const struct snd_azf3328 *chip = ac97->private_data; const struct snd_azf3328 *chip = ac97->private_data;
unsigned short reg_azf = snd_azf3328_mixer_ac97_map_reg_idx(reg_ac97); unsigned short reg_azf = snd_azf3328_mixer_ac97_map_reg_idx(reg_ac97);
unsigned short reg_val = 0; unsigned short reg_val = 0;
bool unsupported = 0; bool unsupported = false;
snd_azf3328_dbgmixer( snd_azf3328_dbgmixer(
"snd_azf3328_mixer_ac97_read reg_ac97 %u\n", "snd_azf3328_mixer_ac97_read reg_ac97 %u\n",
reg_ac97 reg_ac97
); );
if (reg_azf & AZF_AC97_REG_UNSUPPORTED) if (reg_azf & AZF_AC97_REG_UNSUPPORTED)
unsupported = 1; unsupported = true;
else { else {
if (reg_azf & AZF_AC97_REG_REAL_IO_READ) if (reg_azf & AZF_AC97_REG_REAL_IO_READ)
reg_val = snd_azf3328_mixer_inw(chip, reg_val = snd_azf3328_mixer_inw(chip,
...@@ -759,7 +759,7 @@ snd_azf3328_mixer_ac97_read(struct snd_ac97 *ac97, unsigned short reg_ac97) ...@@ -759,7 +759,7 @@ snd_azf3328_mixer_ac97_read(struct snd_ac97 *ac97, unsigned short reg_ac97)
reg_val = azf_emulated_ac97_vendor_id & 0xffff; reg_val = azf_emulated_ac97_vendor_id & 0xffff;
break; break;
default: default:
unsupported = 1; unsupported = true;
break; break;
} }
} }
...@@ -776,14 +776,14 @@ snd_azf3328_mixer_ac97_write(struct snd_ac97 *ac97, ...@@ -776,14 +776,14 @@ snd_azf3328_mixer_ac97_write(struct snd_ac97 *ac97,
{ {
const struct snd_azf3328 *chip = ac97->private_data; const struct snd_azf3328 *chip = ac97->private_data;
unsigned short reg_azf = snd_azf3328_mixer_ac97_map_reg_idx(reg_ac97); unsigned short reg_azf = snd_azf3328_mixer_ac97_map_reg_idx(reg_ac97);
bool unsupported = 0; bool unsupported = false;
snd_azf3328_dbgmixer( snd_azf3328_dbgmixer(
"snd_azf3328_mixer_ac97_write reg_ac97 %u val %u\n", "snd_azf3328_mixer_ac97_write reg_ac97 %u val %u\n",
reg_ac97, val reg_ac97, val
); );
if (reg_azf & AZF_AC97_REG_UNSUPPORTED) if (reg_azf & AZF_AC97_REG_UNSUPPORTED)
unsupported = 1; unsupported = true;
else { else {
if (reg_azf & AZF_AC97_REG_REAL_IO_WRITE) if (reg_azf & AZF_AC97_REG_REAL_IO_WRITE)
snd_azf3328_mixer_outw( snd_azf3328_mixer_outw(
...@@ -808,7 +808,7 @@ snd_azf3328_mixer_ac97_write(struct snd_ac97 *ac97, ...@@ -808,7 +808,7 @@ snd_azf3328_mixer_ac97_write(struct snd_ac97 *ac97,
*/ */
break; break;
default: default:
unsupported = 1; unsupported = true;
break; break;
} }
} }
...@@ -1559,7 +1559,7 @@ snd_azf3328_pcm_trigger(struct snd_pcm_substream *substream, int cmd) ...@@ -1559,7 +1559,7 @@ snd_azf3328_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
struct snd_azf3328_codec_data *codec = runtime->private_data; struct snd_azf3328_codec_data *codec = runtime->private_data;
int result = 0; int result = 0;
u16 flags1; u16 flags1;
bool previously_muted = 0; bool previously_muted = false;
bool is_main_mixer_playback_codec = (AZF_CODEC_PLAYBACK == codec->type); bool is_main_mixer_playback_codec = (AZF_CODEC_PLAYBACK == codec->type);
snd_azf3328_dbgcalls("snd_azf3328_pcm_trigger cmd %d\n", cmd); snd_azf3328_dbgcalls("snd_azf3328_pcm_trigger cmd %d\n", cmd);
......
...@@ -161,13 +161,13 @@ int olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97) ...@@ -161,13 +161,13 @@ int olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97)
/* drop the original AD1888 HPF control */ /* drop the original AD1888 HPF control */
memset(&elem, 0, sizeof(elem)); memset(&elem, 0, sizeof(elem));
elem.iface = SNDRV_CTL_ELEM_IFACE_MIXER; elem.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
strncpy(elem.name, "High Pass Filter Enable", sizeof(elem.name)); strlcpy(elem.name, "High Pass Filter Enable", sizeof(elem.name));
snd_ctl_remove_id(card, &elem); snd_ctl_remove_id(card, &elem);
/* drop the original V_REFOUT control */ /* drop the original V_REFOUT control */
memset(&elem, 0, sizeof(elem)); memset(&elem, 0, sizeof(elem));
elem.iface = SNDRV_CTL_ELEM_IFACE_MIXER; elem.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
strncpy(elem.name, "V_REFOUT Enable", sizeof(elem.name)); strlcpy(elem.name, "V_REFOUT Enable", sizeof(elem.name));
snd_ctl_remove_id(card, &elem); snd_ctl_remove_id(card, &elem);
/* add the OLPC-specific controls */ /* add the OLPC-specific controls */
......
...@@ -33,7 +33,7 @@ struct daio_rsc_idx { ...@@ -33,7 +33,7 @@ struct daio_rsc_idx {
unsigned short right; unsigned short right;
}; };
struct daio_rsc_idx idx_20k1[NUM_DAIOTYP] = { static struct daio_rsc_idx idx_20k1[NUM_DAIOTYP] = {
[LINEO1] = {.left = 0x00, .right = 0x01}, [LINEO1] = {.left = 0x00, .right = 0x01},
[LINEO2] = {.left = 0x18, .right = 0x19}, [LINEO2] = {.left = 0x18, .right = 0x19},
[LINEO3] = {.left = 0x08, .right = 0x09}, [LINEO3] = {.left = 0x08, .right = 0x09},
...@@ -44,7 +44,7 @@ struct daio_rsc_idx idx_20k1[NUM_DAIOTYP] = { ...@@ -44,7 +44,7 @@ struct daio_rsc_idx idx_20k1[NUM_DAIOTYP] = {
[SPDIFI1] = {.left = 0x95, .right = 0x9d}, [SPDIFI1] = {.left = 0x95, .right = 0x9d},
}; };
struct daio_rsc_idx idx_20k2[NUM_DAIOTYP] = { static struct daio_rsc_idx idx_20k2[NUM_DAIOTYP] = {
[LINEO1] = {.left = 0x40, .right = 0x41}, [LINEO1] = {.left = 0x40, .right = 0x41},
[LINEO2] = {.left = 0x60, .right = 0x61}, [LINEO2] = {.left = 0x60, .right = 0x61},
[LINEO3] = {.left = 0x50, .right = 0x51}, [LINEO3] = {.left = 0x50, .right = 0x51},
......
...@@ -69,7 +69,8 @@ unsigned int get_field(unsigned int data, unsigned int field) ...@@ -69,7 +69,8 @@ unsigned int get_field(unsigned int data, unsigned int field)
{ {
int i; int i;
BUG_ON(!field); if (WARN_ON(!field))
return 0;
/* @field should always be greater than 0 */ /* @field should always be greater than 0 */
for (i = 0; !(field & (1 << i)); ) for (i = 0; !(field & (1 << i)); )
i++; i++;
...@@ -81,7 +82,8 @@ void set_field(unsigned int *data, unsigned int field, unsigned int value) ...@@ -81,7 +82,8 @@ void set_field(unsigned int *data, unsigned int field, unsigned int value)
{ {
int i; int i;
BUG_ON(!field); if (WARN_ON(!field))
return;
/* @field should always be greater than 0 */ /* @field should always be greater than 0 */
for (i = 0; !(field & (1 << i)); ) for (i = 0; !(field & (1 << i)); )
i++; i++;
......
...@@ -1182,15 +1182,20 @@ static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu) ...@@ -1182,15 +1182,20 @@ static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu)
u32 *gpr_map; u32 *gpr_map;
mm_segment_t seg; mm_segment_t seg;
if ((icode = kzalloc(sizeof(*icode), GFP_KERNEL)) == NULL || err = -ENOMEM;
(icode->gpr_map = (u_int32_t __user *) icode = kzalloc(sizeof(*icode), GFP_KERNEL);
kcalloc(512 + 256 + 256 + 2 * 1024, sizeof(u_int32_t), if (!icode)
GFP_KERNEL)) == NULL || return err;
(controls = kcalloc(SND_EMU10K1_GPR_CONTROLS,
sizeof(*controls), GFP_KERNEL)) == NULL) { icode->gpr_map = (u_int32_t __user *) kcalloc(512 + 256 + 256 + 2 * 1024,
err = -ENOMEM; sizeof(u_int32_t), GFP_KERNEL);
goto __err; if (!icode->gpr_map)
} goto __err_gpr;
controls = kcalloc(SND_EMU10K1_GPR_CONTROLS,
sizeof(*controls), GFP_KERNEL);
if (!controls)
goto __err_ctrls;
gpr_map = (u32 __force *)icode->gpr_map; gpr_map = (u32 __force *)icode->gpr_map;
icode->tram_data_map = icode->gpr_map + 512; icode->tram_data_map = icode->gpr_map + 512;
...@@ -1741,12 +1746,12 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input)) ...@@ -1741,12 +1746,12 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
emu->support_tlv = 0; /* clear again */ emu->support_tlv = 0; /* clear again */
snd_leave_user(seg); snd_leave_user(seg);
__err: __err:
kfree(controls); kfree(controls);
if (icode != NULL) { __err_ctrls:
kfree((void __force *)icode->gpr_map); kfree((void __force *)icode->gpr_map);
kfree(icode); __err_gpr:
} kfree(icode);
return err; return err;
} }
...@@ -1813,18 +1818,26 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu) ...@@ -1813,18 +1818,26 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
u32 *gpr_map; u32 *gpr_map;
mm_segment_t seg; mm_segment_t seg;
if ((icode = kzalloc(sizeof(*icode), GFP_KERNEL)) == NULL) err = -ENOMEM;
return -ENOMEM; icode = kzalloc(sizeof(*icode), GFP_KERNEL);
if ((icode->gpr_map = (u_int32_t __user *) if (!icode)
kcalloc(256 + 160 + 160 + 2 * 512, sizeof(u_int32_t), return err;
GFP_KERNEL)) == NULL ||
(controls = kcalloc(SND_EMU10K1_GPR_CONTROLS, icode->gpr_map = (u_int32_t __user *) kcalloc(256 + 160 + 160 + 2 * 512,
sizeof(struct snd_emu10k1_fx8010_control_gpr), sizeof(u_int32_t), GFP_KERNEL);
GFP_KERNEL)) == NULL || if (!icode->gpr_map)
(ipcm = kzalloc(sizeof(*ipcm), GFP_KERNEL)) == NULL) { goto __err_gpr;
err = -ENOMEM;
goto __err; controls = kcalloc(SND_EMU10K1_GPR_CONTROLS,
} sizeof(struct snd_emu10k1_fx8010_control_gpr),
GFP_KERNEL);
if (!controls)
goto __err_ctrls;
ipcm = kzalloc(sizeof(*ipcm), GFP_KERNEL);
if (!ipcm)
goto __err_ipcm;
gpr_map = (u32 __force *)icode->gpr_map; gpr_map = (u32 __force *)icode->gpr_map;
icode->tram_data_map = icode->gpr_map + 256; icode->tram_data_map = icode->gpr_map + 256;
...@@ -2363,13 +2376,14 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu) ...@@ -2363,13 +2376,14 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
snd_leave_user(seg); snd_leave_user(seg);
if (err >= 0) if (err >= 0)
err = snd_emu10k1_ipcm_poke(emu, ipcm); err = snd_emu10k1_ipcm_poke(emu, ipcm);
__err: __err:
kfree(ipcm); kfree(ipcm);
__err_ipcm:
kfree(controls); kfree(controls);
if (icode != NULL) { __err_ctrls:
kfree((void __force *)icode->gpr_map); kfree((void __force *)icode->gpr_map);
kfree(icode); __err_gpr:
} kfree(icode);
return err; return err;
} }
......
...@@ -638,7 +638,7 @@ static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid, ...@@ -638,7 +638,7 @@ static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid,
/* don't add channel suffix for Headphone controls */ /* don't add channel suffix for Headphone controls */
int idx = get_hp_label_index(codec, nid, cfg->hp_pins, int idx = get_hp_label_index(codec, nid, cfg->hp_pins,
cfg->hp_outs); cfg->hp_outs);
if (idx >= 0) if (idx >= 0 && indexp)
*indexp = idx; *indexp = idx;
sfx = ""; sfx = "";
} }
......
...@@ -110,6 +110,7 @@ static int snd_hda_beep_event(struct input_dev *dev, unsigned int type, ...@@ -110,6 +110,7 @@ static int snd_hda_beep_event(struct input_dev *dev, unsigned int type,
case SND_BELL: case SND_BELL:
if (hz) if (hz)
hz = 1000; hz = 1000;
/* fallthru */
case SND_TONE: case SND_TONE:
if (beep->linear_tone) if (beep->linear_tone)
beep->tone = beep_linear_tone(beep, hz); beep->tone = beep_linear_tone(beep, hz);
...@@ -151,10 +152,8 @@ static int snd_hda_do_attach(struct hda_beep *beep) ...@@ -151,10 +152,8 @@ static int snd_hda_do_attach(struct hda_beep *beep)
int err; int err;
input_dev = input_allocate_device(); input_dev = input_allocate_device();
if (!input_dev) { if (!input_dev)
printk(KERN_INFO "hda_beep: unable to allocate input device\n");
return -ENOMEM; return -ENOMEM;
}
/* setup digital beep device */ /* setup digital beep device */
input_dev->name = "HDA Digital PCBeep"; input_dev->name = "HDA Digital PCBeep";
......
此差异已折叠。
...@@ -698,6 +698,7 @@ struct hda_bus { ...@@ -698,6 +698,7 @@ struct hda_bus {
unsigned int in_reset:1; /* during reset operation */ unsigned int in_reset:1; /* during reset operation */
unsigned int power_keep_link_on:1; /* don't power off HDA link */ unsigned int power_keep_link_on:1; /* don't power off HDA link */
unsigned int no_response_fallback:1; /* don't fallback at RIRB error */ unsigned int no_response_fallback:1; /* don't fallback at RIRB error */
unsigned int avoid_link_reset:1; /* don't reset link at runtime PM */
int primary_dig_out_type; /* primary digital out PCM type */ int primary_dig_out_type; /* primary digital out PCM type */
}; };
......
此差异已折叠。
...@@ -549,11 +549,15 @@ static hda_nid_t look_for_out_mute_nid(struct hda_codec *codec, ...@@ -549,11 +549,15 @@ static hda_nid_t look_for_out_mute_nid(struct hda_codec *codec,
static hda_nid_t look_for_out_vol_nid(struct hda_codec *codec, static hda_nid_t look_for_out_vol_nid(struct hda_codec *codec,
struct nid_path *path) struct nid_path *path)
{ {
struct hda_gen_spec *spec = codec->spec;
int i; int i;
for (i = path->depth - 1; i >= 0; i--) { for (i = path->depth - 1; i >= 0; i--) {
if (nid_has_volume(codec, path->path[i], HDA_OUTPUT)) hda_nid_t nid = path->path[i];
return path->path[i]; if ((spec->out_vol_mask >> nid) & 1)
continue;
if (nid_has_volume(codec, nid, HDA_OUTPUT))
return nid;
} }
return 0; return 0;
} }
......
...@@ -242,6 +242,9 @@ struct hda_gen_spec { ...@@ -242,6 +242,9 @@ struct hda_gen_spec {
/* additional mute flags (only effective with auto_mute_via_amp=1) */ /* additional mute flags (only effective with auto_mute_via_amp=1) */
u64 mute_bits; u64 mute_bits;
/* bitmask for skipping volume controls */
u64 out_vol_mask;
/* badness tables for output path evaluations */ /* badness tables for output path evaluations */
const struct badness_table *main_out_badness; const struct badness_table *main_out_badness;
const struct badness_table *extra_out_badness; const struct badness_table *extra_out_badness;
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册