1
0
Fork 0

sound updates for 3.13-rc1

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.
 
 - Fireiwre: DICE driver
 
 - Lots of small fixes for bugs reported by Coverity
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.22 (GNU/Linux)
 
 iQIcBAABAgAGBQJSf2ycAAoJEGwxgFQ9KSmkVPcQAIenO8wxmHFyxHStQEt4GkM/
 1BNk3V9MqAVv+ecjNPWrak+IUFY48gelUISfL1qIvlSl5pZ+FS+UEVSObczeI5Fp
 aY1WDCypC3nfsIm4JCIF/Mv3CpE3eY0Gcxqy6OO87mEVs14rLl/Q0NUw2UVrxRQp
 tu0dh6/C3Bjh8+qSnVnPVcLQG6tQsl7Wv71TyowL4ywom9yrx3uBT1qmqLftG8AH
 Wjm2mpxj0dCGAqTcgiu4DMyTJw7kuTmLduDbhExqIApiaeB2o5ilZny/uQBrP32z
 rdUiJm6cSmQ1jv7L0C0xR3vXv73rS73jXMYh2Qt/9iEZIZkwAhTy0Z7Jr5bMfPjP
 I9hICYRGhfa0S2UJa7yd6Jy3qlnUSyCAU9StQlLIiA+e3Xg0a8yoTZFQ/qWSWzwL
 UK584Wst/lCG8QWUwKV/3n/75ALcKZ1cVrBlcCvcKJwv6OKua7DK0XtDfGpsM5sz
 tiXjyY6T8nh87x62z3/IGMHD43xRp6zmadgwvCzYLkcBbsDNQSQHqzvly0XXtLYb
 4N0cEJjHjHDbiQXkWEreDZ/y9cUSv129GZWsnUQAsO1OoHQaf8hUQt5PxBeYGu9B
 E60pERBNVvicajitdwL+GJ1WeqTkl3VnU8s/ucLXGoGb92Z0aWhqtrMAHCj9MybP
 S2aL7q6otZ4k+Wgh3VKj
 =lxuj
 -----END PGP SIGNATURE-----

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()
  ...
hifive-unleashed-5.1
Linus Torvalds 2013-11-12 15:29:53 +09:00
commit eeab517b68
253 changed files with 8606 additions and 3990 deletions

View File

@ -0,0 +1,22 @@
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>;
};

View File

@ -0,0 +1,42 @@
* 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";
};

View File

@ -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:
- 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

View File

@ -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>;
}; };

View File

@ -0,0 +1,27 @@
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>;
};

View File

@ -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!

View File

@ -1,7 +1,7 @@
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.
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:

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -0,0 +1,380 @@
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.

View File

@ -1,22 +1,23 @@
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 = {
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
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 {

View File

@ -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
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:-
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.
------------------------- -------------------------
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
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,
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)
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
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.
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

View File

@ -1,8 +1,10 @@
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

View File

@ -1,9 +1,9 @@
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:-
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.

View File

@ -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);
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);
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);

View File

@ -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);
} }

View File

@ -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 @@
#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 = {
.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 = {
.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)

View File

@ -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

View File

@ -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 {

View File

@ -0,0 +1,15 @@
#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

View File

@ -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 {
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);

View File

@ -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

View File

@ -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 */

View File

@ -0,0 +1,22 @@
/*
* 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 */

View File

@ -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 {
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(
* playback. * playback.
*/ */
#define SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX BIT(3) #define SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX BIT(3)
/*
* The PCM streams have custom channel names specified.
*/
#define SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME BIT(4)
/** /**
* struct snd_dmaengine_pcm_config - Configuration data for dmaengine based PCM * struct snd_dmaengine_pcm_config - Configuration data for dmaengine based PCM

View File

@ -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

View File

@ -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)

View File

@ -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 {
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 {
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,
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)
{ {

View File

@ -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;
.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, \

View File

@ -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;
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 {
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,
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);
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,
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,
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 {
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; const char *name;
enum snd_soc_compress_type id;
int (*init)(struct snd_soc_codec *codec); /* DT */
int (*exit)(struct snd_soc_codec *codec); int (*of_xlate_dai_name)(struct snd_soc_component *component,
int (*read)(struct snd_soc_codec *codec, unsigned int reg, struct of_phandle_args *args,
unsigned int *value); const char **dai_name);
int (*write)(struct snd_soc_codec *codec, unsigned int reg, };
unsigned int value);
int (*sync)(struct snd_soc_codec *codec); struct snd_soc_component {
const char *name;
int id;
struct device *dev;
struct list_head list;
struct snd_soc_dai_driver *dai_drv;
int num_dai;
const struct snd_soc_component_driver *driver;
}; };
/* SoC Audio Codec device */ /* SoC Audio Codec device */
@ -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 {
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 {
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 {
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 {
#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 {
* 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 {
/* 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 {
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,
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>

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -0,0 +1,51 @@
#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 */

View File

@ -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)
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)
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)
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)
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)
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);
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)
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))) {

View File

@ -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)

View File

@ -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>

View File

@ -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)
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)
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)

View File

@ -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,
/* 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));
} }
/** /**

View File

@ -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,
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,
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)
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;

View File

@ -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)
buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE;
break;
case SNDRV_PCM_FORMAT_S16_LE:
buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
break;
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;
break;
default:
return -EINVAL; return -EINVAL;
} else if (bits == 8)
buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE;
else if (bits == 16)
buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
else if (bits <= 32)
buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
else
buswidth = DMA_SLAVE_BUSWIDTH_8_BYTES;
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;

View File

@ -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,
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,
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)

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -1,10 +1,12 @@
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

View File

@ -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);
*/ */
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,
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,
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,
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,
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,
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,
} }
} }
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)
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)
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)
* @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)
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)
{ {
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;
} }

View File

@ -1,6 +1,7 @@
#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 @@
* 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 {
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 {
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 {
__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,
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);
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);
} }
/** /**

View File

@ -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,
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 (err < 0) {
if (buffer[0] == old_arg) /* success? */ if (err == -EAGAIN &&
break; bus_reset_handling == SUCCEED_ON_BUS_RESET)
err = 0;
return err;
}
if (check) { if (buffer[0] == old_arg) /* success? */
err = check(c, buffer[0]); break;
if (err < 0)
return err; if (check) {
} err = check(c, buffer[0]);
} else if (rcode == RCODE_GENERATION) if (err < 0)
goto bus_reset; return err;
else if (rcode_is_permanent_error(rcode) || ++errors >= 3) }
goto io_error;
} }
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,
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);

View File

@ -0,0 +1,371 @@
#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

1494
sound/firewire/dice.c 100644

File diff suppressed because it is too large Load Diff

View File

@ -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;

View File

@ -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 @@ retry_after_bus_reset:
} }
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,
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)
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)

View File

@ -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 @@
* @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 @@
* 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,
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;
} }

View File

@ -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)

View File

@ -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 @@ err_card:
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)

View File

@ -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)
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,
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)
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)
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)
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;
} }

View File

@ -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)
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,
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)
/* 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]);

View File

@ -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;
} }

View File

@ -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[])
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)
{ {

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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)
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);

View File

@ -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,
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,
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;

View File

@ -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++) {

View File

@ -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,
*/ */
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,
*/ */
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,
*/ */
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,
*/ */
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,
*/ */
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,
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);

View File

@ -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)
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,
{ {
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,
*/ */
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)
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);

View File

@ -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 */

View File

@ -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] = {
[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},

View File

@ -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)
{ {
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++;

View File

@ -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))
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)
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)
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;
} }

View File

@ -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 = "";
} }

View File

@ -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)
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";

View File

@ -565,7 +565,7 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid,
range_val = !!(parm & (1 << (shift-1))); /* ranges */ range_val = !!(parm & (1 << (shift-1))); /* ranges */
val = parm & mask; val = parm & mask;
if (val == 0 && null_count++) { /* no second chance */ if (val == 0 && null_count++) { /* no second chance */
snd_printk(KERN_WARNING "hda_codec: " snd_printdd("hda_codec: "
"invalid CONNECT_LIST verb %x[%i]:%x\n", "invalid CONNECT_LIST verb %x[%i]:%x\n",
nid, i, parm); nid, i, parm);
return 0; return 0;
@ -2634,8 +2634,7 @@ static int map_slaves(struct hda_codec *codec, const char * const *slaves,
items = codec->mixers.list; items = codec->mixers.list;
for (i = 0; i < codec->mixers.used; i++) { for (i = 0; i < codec->mixers.used; i++) {
struct snd_kcontrol *sctl = items[i].kctl; struct snd_kcontrol *sctl = items[i].kctl;
if (!sctl || !sctl->id.name || if (!sctl || sctl->id.iface != SNDRV_CTL_ELEM_IFACE_MIXER)
sctl->id.iface != SNDRV_CTL_ELEM_IFACE_MIXER)
continue; continue;
for (s = slaves; *s; s++) { for (s = slaves; *s; s++) {
char tmpname[sizeof(sctl->id.name)]; char tmpname[sizeof(sctl->id.name)];
@ -2662,7 +2661,7 @@ static int check_slave_present(void *data, struct snd_kcontrol *sctl)
} }
/* guess the value corresponding to 0dB */ /* guess the value corresponding to 0dB */
static int get_kctl_0dB_offset(struct snd_kcontrol *kctl) static int get_kctl_0dB_offset(struct snd_kcontrol *kctl, int *step_to_check)
{ {
int _tlv[4]; int _tlv[4];
const int *tlv = NULL; const int *tlv = NULL;
@ -2677,8 +2676,19 @@ static int get_kctl_0dB_offset(struct snd_kcontrol *kctl)
set_fs(fs); set_fs(fs);
} else if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) } else if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_READ)
tlv = kctl->tlv.p; tlv = kctl->tlv.p;
if (tlv && tlv[0] == SNDRV_CTL_TLVT_DB_SCALE) if (tlv && tlv[0] == SNDRV_CTL_TLVT_DB_SCALE) {
val = -tlv[2] / tlv[3]; int step = tlv[3];
step &= ~TLV_DB_SCALE_MUTE;
if (!step)
return -1;
if (*step_to_check && *step_to_check != step) {
snd_printk(KERN_ERR "hda_codec: Mismatching dB step for vmaster slave (%d!=%d)\n",
*step_to_check, step);
return -1;
}
*step_to_check = step;
val = -tlv[2] / step;
}
return val; return val;
} }
@ -2699,7 +2709,7 @@ static int put_kctl_with_value(struct snd_kcontrol *kctl, int val)
/* initialize the slave volume with 0dB */ /* initialize the slave volume with 0dB */
static int init_slave_0dB(void *data, struct snd_kcontrol *slave) static int init_slave_0dB(void *data, struct snd_kcontrol *slave)
{ {
int offset = get_kctl_0dB_offset(slave); int offset = get_kctl_0dB_offset(slave, data);
if (offset > 0) if (offset > 0)
put_kctl_with_value(slave, offset); put_kctl_with_value(slave, offset);
return 0; return 0;
@ -2760,9 +2770,11 @@ int __snd_hda_add_vmaster(struct hda_codec *codec, char *name,
/* init with master mute & zero volume */ /* init with master mute & zero volume */
put_kctl_with_value(kctl, 0); put_kctl_with_value(kctl, 0);
if (init_slave_vol) if (init_slave_vol) {
int step = 0;
map_slaves(codec, slaves, suffix, map_slaves(codec, slaves, suffix,
tlv ? init_slave_0dB : init_slave_unmute, kctl); tlv ? init_slave_0dB : init_slave_unmute, &step);
}
if (ctl_ret) if (ctl_ret)
*ctl_ret = kctl; *ctl_ret = kctl;
@ -5395,11 +5407,6 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
snd_hda_codec_setup_stream(codec, snd_hda_codec_setup_stream(codec,
mout->hp_out_nid[i], mout->hp_out_nid[i],
stream_tag, 0, format); stream_tag, 0, format);
for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
if (!mout->no_share_stream && mout->extra_out_nid[i])
snd_hda_codec_setup_stream(codec,
mout->extra_out_nid[i],
stream_tag, 0, format);
/* surrounds */ /* surrounds */
for (i = 1; i < mout->num_dacs; i++) { for (i = 1; i < mout->num_dacs; i++) {
@ -5410,6 +5417,20 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
snd_hda_codec_setup_stream(codec, nids[i], stream_tag, snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
0, format); 0, format);
} }
/* extra surrounds */
for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) {
int ch = 0;
if (!mout->extra_out_nid[i])
break;
if (chs >= (i + 1) * 2)
ch = i * 2;
else if (!mout->no_share_stream)
break;
snd_hda_codec_setup_stream(codec, mout->extra_out_nid[i],
stream_tag, ch, format);
}
return 0; return 0;
} }
EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_prepare); EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_prepare);

View File

@ -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 */
}; };

View File

@ -2,6 +2,7 @@
* Generic routines and proc interface for ELD(EDID Like Data) information * Generic routines and proc interface for ELD(EDID Like Data) information
* *
* Copyright(c) 2008 Intel Corporation. * Copyright(c) 2008 Intel Corporation.
* Copyright (c) 2013 Anssi Hannula <anssi.hannula@iki.fi>
* *
* Authors: * Authors:
* Wu Fengguang <wfg@linux.intel.com> * Wu Fengguang <wfg@linux.intel.com>
@ -478,10 +479,9 @@ static void hdmi_print_sad_info(int i, struct cea_sad *a,
snd_iprintf(buffer, "sad%d_profile\t\t%d\n", i, a->profile); snd_iprintf(buffer, "sad%d_profile\t\t%d\n", i, a->profile);
} }
static void hdmi_print_eld_info(struct snd_info_entry *entry, void snd_hdmi_print_eld_info(struct hdmi_eld *eld,
struct snd_info_buffer *buffer) struct snd_info_buffer *buffer)
{ {
struct hdmi_eld *eld = entry->private_data;
struct parsed_hdmi_eld *e = &eld->info; struct parsed_hdmi_eld *e = &eld->info;
char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
int i; int i;
@ -500,13 +500,10 @@ static void hdmi_print_eld_info(struct snd_info_entry *entry,
[4 ... 7] = "reserved" [4 ... 7] = "reserved"
}; };
mutex_lock(&eld->lock);
snd_iprintf(buffer, "monitor_present\t\t%d\n", eld->monitor_present); snd_iprintf(buffer, "monitor_present\t\t%d\n", eld->monitor_present);
snd_iprintf(buffer, "eld_valid\t\t%d\n", eld->eld_valid); snd_iprintf(buffer, "eld_valid\t\t%d\n", eld->eld_valid);
if (!eld->eld_valid) { if (!eld->eld_valid)
mutex_unlock(&eld->lock);
return; return;
}
snd_iprintf(buffer, "monitor_name\t\t%s\n", e->monitor_name); snd_iprintf(buffer, "monitor_name\t\t%s\n", e->monitor_name);
snd_iprintf(buffer, "connection_type\t\t%s\n", snd_iprintf(buffer, "connection_type\t\t%s\n",
eld_connection_type_names[e->conn_type]); eld_connection_type_names[e->conn_type]);
@ -528,13 +525,11 @@ static void hdmi_print_eld_info(struct snd_info_entry *entry,
for (i = 0; i < e->sad_count; i++) for (i = 0; i < e->sad_count; i++)
hdmi_print_sad_info(i, e->sad + i, buffer); hdmi_print_sad_info(i, e->sad + i, buffer);
mutex_unlock(&eld->lock);
} }
static void hdmi_write_eld_info(struct snd_info_entry *entry, void snd_hdmi_write_eld_info(struct hdmi_eld *eld,
struct snd_info_buffer *buffer) struct snd_info_buffer *buffer)
{ {
struct hdmi_eld *eld = entry->private_data;
struct parsed_hdmi_eld *e = &eld->info; struct parsed_hdmi_eld *e = &eld->info;
char line[64]; char line[64];
char name[64]; char name[64];
@ -542,7 +537,6 @@ static void hdmi_write_eld_info(struct snd_info_entry *entry,
long long val; long long val;
unsigned int n; unsigned int n;
mutex_lock(&eld->lock);
while (!snd_info_get_line(buffer, line, sizeof(line))) { while (!snd_info_get_line(buffer, line, sizeof(line))) {
if (sscanf(line, "%s %llx", name, &val) != 2) if (sscanf(line, "%s %llx", name, &val) != 2)
continue; continue;
@ -594,38 +588,7 @@ static void hdmi_write_eld_info(struct snd_info_entry *entry,
e->sad_count = n + 1; e->sad_count = n + 1;
} }
} }
mutex_unlock(&eld->lock);
} }
int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld,
int index)
{
char name[32];
struct snd_info_entry *entry;
int err;
snprintf(name, sizeof(name), "eld#%d.%d", codec->addr, index);
err = snd_card_proc_new(codec->bus->card, name, &entry);
if (err < 0)
return err;
snd_info_set_text_ops(entry, eld, hdmi_print_eld_info);
entry->c.text.write = hdmi_write_eld_info;
entry->mode |= S_IWUSR;
eld->proc_entry = entry;
return 0;
}
void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld)
{
if (!codec->bus->shutdown && eld->proc_entry) {
snd_device_free(codec->bus->card, eld->proc_entry);
eld->proc_entry = NULL;
}
}
#endif /* CONFIG_PROC_FS */ #endif /* CONFIG_PROC_FS */
/* update PCM info based on ELD */ /* update PCM info based on ELD */
@ -671,3 +634,153 @@ void snd_hdmi_eld_update_pcm_info(struct parsed_hdmi_eld *e,
hinfo->maxbps = min(hinfo->maxbps, maxbps); hinfo->maxbps = min(hinfo->maxbps, maxbps);
hinfo->channels_max = min(hinfo->channels_max, channels_max); hinfo->channels_max = min(hinfo->channels_max, channels_max);
} }
/* ATI/AMD specific stuff (ELD emulation) */
#define ATI_VERB_SET_AUDIO_DESCRIPTOR 0x776
#define ATI_VERB_SET_SINK_INFO_INDEX 0x780
#define ATI_VERB_GET_SPEAKER_ALLOCATION 0xf70
#define ATI_VERB_GET_AUDIO_DESCRIPTOR 0xf76
#define ATI_VERB_GET_AUDIO_VIDEO_DELAY 0xf7b
#define ATI_VERB_GET_SINK_INFO_INDEX 0xf80
#define ATI_VERB_GET_SINK_INFO_DATA 0xf81
#define ATI_SPKALLOC_SPKALLOC 0x007f
#define ATI_SPKALLOC_TYPE_HDMI 0x0100
#define ATI_SPKALLOC_TYPE_DISPLAYPORT 0x0200
/* first three bytes are just standard SAD */
#define ATI_AUDIODESC_CHANNELS 0x00000007
#define ATI_AUDIODESC_RATES 0x0000ff00
#define ATI_AUDIODESC_LPCM_STEREO_RATES 0xff000000
/* in standard HDMI VSDB format */
#define ATI_DELAY_VIDEO_LATENCY 0x000000ff
#define ATI_DELAY_AUDIO_LATENCY 0x0000ff00
enum ati_sink_info_idx {
ATI_INFO_IDX_MANUFACTURER_ID = 0,
ATI_INFO_IDX_PRODUCT_ID = 1,
ATI_INFO_IDX_SINK_DESC_LEN = 2,
ATI_INFO_IDX_PORT_ID_LOW = 3,
ATI_INFO_IDX_PORT_ID_HIGH = 4,
ATI_INFO_IDX_SINK_DESC_FIRST = 5,
ATI_INFO_IDX_SINK_DESC_LAST = 22, /* max len 18 bytes */
};
int snd_hdmi_get_eld_ati(struct hda_codec *codec, hda_nid_t nid,
unsigned char *buf, int *eld_size, bool rev3_or_later)
{
int spkalloc, ati_sad, aud_synch;
int sink_desc_len = 0;
int pos, i;
/* ATI/AMD does not have ELD, emulate it */
spkalloc = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SPEAKER_ALLOCATION, 0);
if (!spkalloc) {
snd_printd(KERN_INFO "HDMI ATI/AMD: no speaker allocation for ELD\n");
return -EINVAL;
}
memset(buf, 0, ELD_FIXED_BYTES + ELD_MAX_MNL + ELD_MAX_SAD * 3);
/* version */
buf[0] = ELD_VER_CEA_861D << 3;
/* speaker allocation from EDID */
buf[7] = spkalloc & ATI_SPKALLOC_SPKALLOC;
/* is DisplayPort? */
if (spkalloc & ATI_SPKALLOC_TYPE_DISPLAYPORT)
buf[5] |= 0x04;
pos = ELD_FIXED_BYTES;
if (rev3_or_later) {
int sink_info;
snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PORT_ID_LOW);
sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
put_unaligned_le32(sink_info, buf + 8);
snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PORT_ID_HIGH);
sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
put_unaligned_le32(sink_info, buf + 12);
snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_MANUFACTURER_ID);
sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
put_unaligned_le16(sink_info, buf + 16);
snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PRODUCT_ID);
sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
put_unaligned_le16(sink_info, buf + 18);
snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_SINK_DESC_LEN);
sink_desc_len = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
if (sink_desc_len > ELD_MAX_MNL) {
snd_printd(KERN_INFO "HDMI ATI/AMD: Truncating HDMI sink description with length %d\n",
sink_desc_len);
sink_desc_len = ELD_MAX_MNL;
}
buf[4] |= sink_desc_len;
for (i = 0; i < sink_desc_len; i++) {
snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_SINK_DESC_FIRST + i);
buf[pos++] = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
}
}
for (i = AUDIO_CODING_TYPE_LPCM; i <= AUDIO_CODING_TYPE_WMAPRO; i++) {
if (i == AUDIO_CODING_TYPE_SACD || i == AUDIO_CODING_TYPE_DST)
continue; /* not handled by ATI/AMD */
snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_AUDIO_DESCRIPTOR, i << 3);
ati_sad = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_AUDIO_DESCRIPTOR, 0);
if (ati_sad & ATI_AUDIODESC_RATES) {
/* format is supported, copy SAD as-is */
buf[pos++] = (ati_sad & 0x0000ff) >> 0;
buf[pos++] = (ati_sad & 0x00ff00) >> 8;
buf[pos++] = (ati_sad & 0xff0000) >> 16;
}
if (i == AUDIO_CODING_TYPE_LPCM
&& (ati_sad & ATI_AUDIODESC_LPCM_STEREO_RATES)
&& (ati_sad & ATI_AUDIODESC_LPCM_STEREO_RATES) >> 16 != (ati_sad & ATI_AUDIODESC_RATES)) {
/* for PCM there is a separate stereo rate mask */
buf[pos++] = ((ati_sad & 0x000000ff) & ~ATI_AUDIODESC_CHANNELS) | 0x1;
/* rates from the extra byte */
buf[pos++] = (ati_sad & 0xff000000) >> 24;
buf[pos++] = (ati_sad & 0x00ff0000) >> 16;
}
}
if (pos == ELD_FIXED_BYTES + sink_desc_len) {
snd_printd(KERN_INFO "HDMI ATI/AMD: no audio descriptors for ELD\n");
return -EINVAL;
}
aud_synch = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_AUDIO_VIDEO_DELAY, 0);
if ((aud_synch & ATI_DELAY_VIDEO_LATENCY) && (aud_synch & ATI_DELAY_AUDIO_LATENCY)) {
int video_latency = (aud_synch & ATI_DELAY_VIDEO_LATENCY) - 1;
int audio_latency = ((aud_synch & ATI_DELAY_AUDIO_LATENCY) >> 8) - 1;
if (video_latency > audio_latency)
buf[6] = min(video_latency - audio_latency, 0xfa);
}
/* Baseline length */
buf[2] = pos - 4;
/* SAD count */
buf[5] |= ((pos - ELD_FIXED_BYTES - sink_desc_len) / 3) << 4;
*eld_size = pos;
return 0;
}

View File

@ -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;
} }

View File

@ -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;

View File

@ -169,6 +169,7 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
"{Intel, PPT}," "{Intel, PPT},"
"{Intel, LPT}," "{Intel, LPT},"
"{Intel, LPT_LP}," "{Intel, LPT_LP},"
"{Intel, WPT_LP},"
"{Intel, HPT}," "{Intel, HPT},"
"{Intel, PBG}," "{Intel, PBG},"
"{Intel, SCH}," "{Intel, SCH},"
@ -568,6 +569,7 @@ enum {
AZX_DRIVER_ICH, AZX_DRIVER_ICH,
AZX_DRIVER_PCH, AZX_DRIVER_PCH,
AZX_DRIVER_SCH, AZX_DRIVER_SCH,
AZX_DRIVER_HDMI,
AZX_DRIVER_ATI, AZX_DRIVER_ATI,
AZX_DRIVER_ATIHDMI, AZX_DRIVER_ATIHDMI,
AZX_DRIVER_ATIHDMI_NS, AZX_DRIVER_ATIHDMI_NS,
@ -612,6 +614,11 @@ enum {
#define AZX_DCAPS_INTEL_PCH \ #define AZX_DCAPS_INTEL_PCH \
(AZX_DCAPS_INTEL_PCH_NOPM | AZX_DCAPS_PM_RUNTIME) (AZX_DCAPS_INTEL_PCH_NOPM | AZX_DCAPS_PM_RUNTIME)
#define AZX_DCAPS_INTEL_HASWELL \
(AZX_DCAPS_SCH_SNOOP | AZX_DCAPS_ALIGN_BUFSIZE | \
AZX_DCAPS_COUNT_LPIB_DELAY | AZX_DCAPS_PM_RUNTIME | \
AZX_DCAPS_I915_POWERWELL)
/* quirks for ATI SB / AMD Hudson */ /* quirks for ATI SB / AMD Hudson */
#define AZX_DCAPS_PRESET_ATI_SB \ #define AZX_DCAPS_PRESET_ATI_SB \
(AZX_DCAPS_ATI_SNOOP | AZX_DCAPS_NO_TCSEL | \ (AZX_DCAPS_ATI_SNOOP | AZX_DCAPS_NO_TCSEL | \
@ -642,6 +649,7 @@ static char *driver_short_names[] = {
[AZX_DRIVER_ICH] = "HDA Intel", [AZX_DRIVER_ICH] = "HDA Intel",
[AZX_DRIVER_PCH] = "HDA Intel PCH", [AZX_DRIVER_PCH] = "HDA Intel PCH",
[AZX_DRIVER_SCH] = "HDA Intel MID", [AZX_DRIVER_SCH] = "HDA Intel MID",
[AZX_DRIVER_HDMI] = "HDA Intel HDMI",
[AZX_DRIVER_ATI] = "HDA ATI SB", [AZX_DRIVER_ATI] = "HDA ATI SB",
[AZX_DRIVER_ATIHDMI] = "HDA ATI HDMI", [AZX_DRIVER_ATIHDMI] = "HDA ATI HDMI",
[AZX_DRIVER_ATIHDMI_NS] = "HDA ATI HDMI", [AZX_DRIVER_ATIHDMI_NS] = "HDA ATI HDMI",
@ -906,12 +914,12 @@ static void azx_update_rirb(struct azx *chip)
chip->rirb.res[addr] = res; chip->rirb.res[addr] = res;
smp_wmb(); smp_wmb();
chip->rirb.cmds[addr]--; chip->rirb.cmds[addr]--;
} else } else if (printk_ratelimit()) {
snd_printk(KERN_ERR SFX "%s: spurious response %#x:%#x, " snd_printk(KERN_ERR SFX "%s: spurious response %#x:%#x, last cmd=%#08x\n",
"last cmd=%#08x\n",
pci_name(chip->pci), pci_name(chip->pci),
res, res_ex, res, res_ex,
chip->last_cmd[addr]); chip->last_cmd[addr]);
}
} }
} }
@ -2986,7 +2994,8 @@ static int azx_runtime_suspend(struct device *dev)
STATESTS_INT_MASK); STATESTS_INT_MASK);
azx_stop_chip(chip); azx_stop_chip(chip);
azx_enter_link_reset(chip); if (!chip->bus->avoid_link_reset)
azx_enter_link_reset(chip);
azx_clear_irq_pending(chip); azx_clear_irq_pending(chip);
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
hda_display_power(false); hda_display_power(false);
@ -3985,16 +3994,16 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
/* Lynx Point-LP */ /* Lynx Point-LP */
{ PCI_DEVICE(0x8086, 0x9c21), { PCI_DEVICE(0x8086, 0x9c21),
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
/* Wildcat Point-LP */
{ PCI_DEVICE(0x8086, 0x9ca0),
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
/* Haswell */ /* Haswell */
{ PCI_DEVICE(0x8086, 0x0a0c), { PCI_DEVICE(0x8086, 0x0a0c),
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH | .driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL },
AZX_DCAPS_I915_POWERWELL },
{ PCI_DEVICE(0x8086, 0x0c0c), { PCI_DEVICE(0x8086, 0x0c0c),
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH | .driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL },
AZX_DCAPS_I915_POWERWELL },
{ PCI_DEVICE(0x8086, 0x0d0c), { PCI_DEVICE(0x8086, 0x0d0c),
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH | .driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL },
AZX_DCAPS_I915_POWERWELL },
/* 5 Series/3400 */ /* 5 Series/3400 */
{ PCI_DEVICE(0x8086, 0x3b56), { PCI_DEVICE(0x8086, 0x3b56),
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM }, .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM },
@ -4074,6 +4083,22 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
.driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
{ PCI_DEVICE(0x1002, 0xaa48), { PCI_DEVICE(0x1002, 0xaa48),
.driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
{ PCI_DEVICE(0x1002, 0xaa50),
.driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
{ PCI_DEVICE(0x1002, 0xaa58),
.driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
{ PCI_DEVICE(0x1002, 0xaa60),
.driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
{ PCI_DEVICE(0x1002, 0xaa68),
.driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
{ PCI_DEVICE(0x1002, 0xaa80),
.driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
{ PCI_DEVICE(0x1002, 0xaa88),
.driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
{ PCI_DEVICE(0x1002, 0xaa90),
.driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
{ PCI_DEVICE(0x1002, 0xaa98),
.driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
{ PCI_DEVICE(0x1002, 0x9902), { PCI_DEVICE(0x1002, 0x9902),
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI }, .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI },
{ PCI_DEVICE(0x1002, 0xaaa0), { PCI_DEVICE(0x1002, 0xaaa0),

View File

@ -286,7 +286,7 @@ void snd_hda_jack_report_sync(struct hda_codec *codec)
jack = codec->jacktbl.list; jack = codec->jacktbl.list;
for (i = 0; i < codec->jacktbl.used; i++, jack++) for (i = 0; i < codec->jacktbl.used; i++, jack++)
if (jack->nid) { if (jack->nid) {
if (!jack->kctl) if (!jack->kctl || jack->block_report)
continue; continue;
state = get_jack_plug_state(jack->pin_sense); state = get_jack_plug_state(jack->pin_sense);
snd_kctl_jack_report(codec->bus->card, jack->kctl, state); snd_kctl_jack_report(codec->bus->card, jack->kctl, state);

View File

@ -28,6 +28,7 @@ struct hda_jack_tbl {
unsigned int jack_detect:1; /* capable of jack-detection? */ unsigned int jack_detect:1; /* capable of jack-detection? */
unsigned int jack_dirty:1; /* needs to update? */ unsigned int jack_dirty:1; /* needs to update? */
unsigned int phantom_jack:1; /* a fixed, always present port? */ unsigned int phantom_jack:1; /* a fixed, always present port? */
unsigned int block_report:1; /* in a transitional state - do not report to userspace */
hda_nid_t gating_jack; /* valid when gating jack plugged */ hda_nid_t gating_jack; /* valid when gating jack plugged */
hda_nid_t gated_jack; /* gated is dependent on this jack */ hda_nid_t gated_jack; /* gated is dependent on this jack */
struct snd_kcontrol *kctl; /* assigned kctl for jack-detection */ struct snd_kcontrol *kctl; /* assigned kctl for jack-detection */

View File

@ -428,6 +428,7 @@ enum {
HDA_FIXUP_ACT_PROBE, HDA_FIXUP_ACT_PROBE,
HDA_FIXUP_ACT_INIT, HDA_FIXUP_ACT_INIT,
HDA_FIXUP_ACT_BUILD, HDA_FIXUP_ACT_BUILD,
HDA_FIXUP_ACT_FREE,
}; };
int snd_hda_add_verbs(struct hda_codec *codec, const struct hda_verb *list); int snd_hda_add_verbs(struct hda_codec *codec, const struct hda_verb *list);
@ -751,10 +752,6 @@ struct hdmi_eld {
int eld_size; int eld_size;
char eld_buffer[ELD_MAX_SIZE]; char eld_buffer[ELD_MAX_SIZE];
struct parsed_hdmi_eld info; struct parsed_hdmi_eld info;
struct mutex lock;
#ifdef CONFIG_PROC_FS
struct snd_info_entry *proc_entry;
#endif
}; };
int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid); int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid);
@ -766,21 +763,15 @@ void snd_hdmi_show_eld(struct parsed_hdmi_eld *e);
void snd_hdmi_eld_update_pcm_info(struct parsed_hdmi_eld *e, void snd_hdmi_eld_update_pcm_info(struct parsed_hdmi_eld *e,
struct hda_pcm_stream *hinfo); struct hda_pcm_stream *hinfo);
int snd_hdmi_get_eld_ati(struct hda_codec *codec, hda_nid_t nid,
unsigned char *buf, int *eld_size,
bool rev3_or_later);
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld, void snd_hdmi_print_eld_info(struct hdmi_eld *eld,
int index); struct snd_info_buffer *buffer);
void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld); void snd_hdmi_write_eld_info(struct hdmi_eld *eld,
#else struct snd_info_buffer *buffer);
static inline int snd_hda_eld_proc_new(struct hda_codec *codec,
struct hdmi_eld *eld,
int index)
{
return 0;
}
static inline void snd_hda_eld_proc_free(struct hda_codec *codec,
struct hdmi_eld *eld)
{
}
#endif #endif
#define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80 #define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80

View File

@ -973,8 +973,11 @@ static void ad1884_fixup_thinkpad(struct hda_codec *codec,
{ {
struct ad198x_spec *spec = codec->spec; struct ad198x_spec *spec = codec->spec;
if (action == HDA_FIXUP_ACT_PRE_PROBE) if (action == HDA_FIXUP_ACT_PRE_PROBE) {
spec->gen.keep_eapd_on = 1; spec->gen.keep_eapd_on = 1;
spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook;
spec->eapd_nid = 0x12;
}
} }
/* set magic COEFs for dmic */ /* set magic COEFs for dmic */

View File

@ -759,7 +759,7 @@ struct ca0132_spec {
/* /*
* CA0132 codec access * CA0132 codec access
*/ */
unsigned int codec_send_command(struct hda_codec *codec, hda_nid_t nid, static unsigned int codec_send_command(struct hda_codec *codec, hda_nid_t nid,
unsigned int verb, unsigned int parm, unsigned int *res) unsigned int verb, unsigned int parm, unsigned int *res)
{ {
unsigned int response; unsigned int response;

View File

@ -597,6 +597,7 @@ static int patch_cs420x(struct hda_codec *codec)
* Its layout is no longer compatible with CS4206/CS4207 * Its layout is no longer compatible with CS4206/CS4207
*/ */
enum { enum {
CS4208_MAC_AUTO,
CS4208_MBA6, CS4208_MBA6,
CS4208_GPIO0, CS4208_GPIO0,
}; };
@ -608,7 +609,12 @@ static const struct hda_model_fixup cs4208_models[] = {
}; };
static const struct snd_pci_quirk cs4208_fixup_tbl[] = { static const struct snd_pci_quirk cs4208_fixup_tbl[] = {
/* codec SSID */ SND_PCI_QUIRK_VENDOR(0x106b, "Apple", CS4208_MAC_AUTO),
{} /* terminator */
};
/* codec SSID matching */
static const struct snd_pci_quirk cs4208_mac_fixup_tbl[] = {
SND_PCI_QUIRK(0x106b, 0x7100, "MacBookAir 6,1", CS4208_MBA6), SND_PCI_QUIRK(0x106b, 0x7100, "MacBookAir 6,1", CS4208_MBA6),
SND_PCI_QUIRK(0x106b, 0x7200, "MacBookAir 6,2", CS4208_MBA6), SND_PCI_QUIRK(0x106b, 0x7200, "MacBookAir 6,2", CS4208_MBA6),
{} /* terminator */ {} /* terminator */
@ -626,6 +632,20 @@ static void cs4208_fixup_gpio0(struct hda_codec *codec,
} }
} }
static const struct hda_fixup cs4208_fixups[];
/* remap the fixup from codec SSID and apply it */
static void cs4208_fixup_mac(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
if (action != HDA_FIXUP_ACT_PRE_PROBE)
return;
snd_hda_pick_fixup(codec, NULL, cs4208_mac_fixup_tbl, cs4208_fixups);
if (codec->fixup_id < 0 || codec->fixup_id == CS4208_MAC_AUTO)
codec->fixup_id = CS4208_GPIO0; /* default fixup */
snd_hda_apply_fixup(codec, action);
}
static const struct hda_fixup cs4208_fixups[] = { static const struct hda_fixup cs4208_fixups[] = {
[CS4208_MBA6] = { [CS4208_MBA6] = {
.type = HDA_FIXUP_PINS, .type = HDA_FIXUP_PINS,
@ -637,6 +657,10 @@ static const struct hda_fixup cs4208_fixups[] = {
.type = HDA_FIXUP_FUNC, .type = HDA_FIXUP_FUNC,
.v.func = cs4208_fixup_gpio0, .v.func = cs4208_fixup_gpio0,
}, },
[CS4208_MAC_AUTO] = {
.type = HDA_FIXUP_FUNC,
.v.func = cs4208_fixup_mac,
},
}; };
/* correct the 0dB offset of input pins */ /* correct the 0dB offset of input pins */
@ -660,6 +684,8 @@ static int patch_cs4208(struct hda_codec *codec)
return -ENOMEM; return -ENOMEM;
spec->gen.automute_hook = cs_automute; spec->gen.automute_hook = cs_automute;
/* exclude NID 0x10 (HP) from output volumes due to different steps */
spec->gen.out_vol_mask = 1ULL << 0x10;
snd_hda_pick_fixup(codec, cs4208_models, cs4208_fixup_tbl, snd_hda_pick_fixup(codec, cs4208_models, cs4208_fixup_tbl,
cs4208_fixups); cs4208_fixups);

View File

@ -3208,11 +3208,17 @@ static int cx_auto_init(struct hda_codec *codec)
return 0; return 0;
} }
static void cx_auto_free(struct hda_codec *codec)
{
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_FREE);
snd_hda_gen_free(codec);
}
static const struct hda_codec_ops cx_auto_patch_ops = { static const struct hda_codec_ops cx_auto_patch_ops = {
.build_controls = cx_auto_build_controls, .build_controls = cx_auto_build_controls,
.build_pcms = snd_hda_gen_build_pcms, .build_pcms = snd_hda_gen_build_pcms,
.init = cx_auto_init, .init = cx_auto_init,
.free = snd_hda_gen_free, .free = cx_auto_free,
.unsol_event = snd_hda_jack_unsol_event, .unsol_event = snd_hda_jack_unsol_event,
#ifdef CONFIG_PM #ifdef CONFIG_PM
.check_power_status = snd_hda_gen_check_power_status, .check_power_status = snd_hda_gen_check_power_status,
@ -3232,8 +3238,84 @@ enum {
CXT_FIXUP_HEADPHONE_MIC_PIN, CXT_FIXUP_HEADPHONE_MIC_PIN,
CXT_FIXUP_HEADPHONE_MIC, CXT_FIXUP_HEADPHONE_MIC,
CXT_FIXUP_GPIO1, CXT_FIXUP_GPIO1,
CXT_FIXUP_THINKPAD_ACPI,
}; };
#if IS_ENABLED(CONFIG_THINKPAD_ACPI)
#include <linux/thinkpad_acpi.h>
static int (*led_set_func)(int, bool);
static void update_tpacpi_mute_led(void *private_data, int enabled)
{
struct hda_codec *codec = private_data;
struct conexant_spec *spec = codec->spec;
if (spec->dynamic_eapd)
cx_auto_vmaster_hook(private_data, enabled);
if (led_set_func)
led_set_func(TPACPI_LED_MUTE, !enabled);
}
static void update_tpacpi_micmute_led(struct hda_codec *codec,
struct snd_ctl_elem_value *ucontrol)
{
if (!ucontrol || !led_set_func)
return;
if (strcmp("Capture Switch", ucontrol->id.name) == 0 && ucontrol->id.index == 0) {
/* TODO: How do I verify if it's a mono or stereo here? */
bool val = ucontrol->value.integer.value[0] || ucontrol->value.integer.value[1];
led_set_func(TPACPI_LED_MICMUTE, !val);
}
}
static void cxt_fixup_thinkpad_acpi(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
struct conexant_spec *spec = codec->spec;
bool removefunc = false;
if (action == HDA_FIXUP_ACT_PROBE) {
if (!led_set_func)
led_set_func = symbol_request(tpacpi_led_set);
if (!led_set_func) {
snd_printk(KERN_WARNING "Failed to find thinkpad-acpi symbol tpacpi_led_set\n");
return;
}
removefunc = true;
if (led_set_func(TPACPI_LED_MUTE, false) >= 0) {
spec->gen.vmaster_mute.hook = update_tpacpi_mute_led;
removefunc = false;
}
if (led_set_func(TPACPI_LED_MICMUTE, false) >= 0) {
if (spec->gen.num_adc_nids > 1)
snd_printdd("Skipping micmute LED control due to several ADCs");
else {
spec->gen.cap_sync_hook = update_tpacpi_micmute_led;
removefunc = false;
}
}
}
if (led_set_func && (action == HDA_FIXUP_ACT_FREE || removefunc)) {
symbol_put(tpacpi_led_set);
led_set_func = NULL;
}
}
#else
static void cxt_fixup_thinkpad_acpi(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
}
#endif
static void cxt_fixup_stereo_dmic(struct hda_codec *codec, static void cxt_fixup_stereo_dmic(struct hda_codec *codec,
const struct hda_fixup *fix, int action) const struct hda_fixup *fix, int action)
{ {
@ -3344,6 +3426,8 @@ static const struct hda_fixup cxt_fixups[] = {
[CXT_PINCFG_LENOVO_TP410] = { [CXT_PINCFG_LENOVO_TP410] = {
.type = HDA_FIXUP_PINS, .type = HDA_FIXUP_PINS,
.v.pins = cxt_pincfg_lenovo_tp410, .v.pins = cxt_pincfg_lenovo_tp410,
.chained = true,
.chain_id = CXT_FIXUP_THINKPAD_ACPI,
}, },
[CXT_PINCFG_LEMOTE_A1004] = { [CXT_PINCFG_LEMOTE_A1004] = {
.type = HDA_FIXUP_PINS, .type = HDA_FIXUP_PINS,
@ -3385,6 +3469,10 @@ static const struct hda_fixup cxt_fixups[] = {
{ } { }
}, },
}, },
[CXT_FIXUP_THINKPAD_ACPI] = {
.type = HDA_FIXUP_FUNC,
.v.func = cxt_fixup_thinkpad_acpi,
},
}; };
static const struct snd_pci_quirk cxt5051_fixups[] = { static const struct snd_pci_quirk cxt5051_fixups[] = {
@ -3507,7 +3595,7 @@ static int patch_conexant_auto(struct hda_codec *codec)
return 0; return 0;
error: error:
snd_hda_gen_free(codec); cx_auto_free(codec);
return err; return err;
} }
@ -3568,6 +3656,8 @@ static const struct hda_codec_preset snd_hda_preset_conexant[] = {
.patch = patch_conexant_auto }, .patch = patch_conexant_auto },
{ .id = 0x14f15115, .name = "CX20757", { .id = 0x14f15115, .name = "CX20757",
.patch = patch_conexant_auto }, .patch = patch_conexant_auto },
{ .id = 0x14f151d7, .name = "CX20952",
.patch = patch_conexant_auto },
{} /* terminator */ {} /* terminator */
}; };
@ -3594,6 +3684,7 @@ MODULE_ALIAS("snd-hda-codec-id:14f15111");
MODULE_ALIAS("snd-hda-codec-id:14f15113"); MODULE_ALIAS("snd-hda-codec-id:14f15113");
MODULE_ALIAS("snd-hda-codec-id:14f15114"); MODULE_ALIAS("snd-hda-codec-id:14f15114");
MODULE_ALIAS("snd-hda-codec-id:14f15115"); MODULE_ALIAS("snd-hda-codec-id:14f15115");
MODULE_ALIAS("snd-hda-codec-id:14f151d7");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Conexant HD-audio codec"); MODULE_DESCRIPTION("Conexant HD-audio codec");

File diff suppressed because it is too large Load Diff

View File

@ -554,8 +554,6 @@ do_sku:
nid = portd; nid = portd;
else if (tmp == 3) else if (tmp == 3)
nid = porti; nid = porti;
else
return 1;
if (found_in_nid_list(nid, spec->gen.autocfg.line_out_pins, if (found_in_nid_list(nid, spec->gen.autocfg.line_out_pins,
spec->gen.autocfg.line_outs)) spec->gen.autocfg.line_outs))
return 1; return 1;
@ -579,26 +577,35 @@ static void alc_ssid_check(struct hda_codec *codec, const hda_nid_t *ports)
/* /*
* COEF access helper functions * COEF access helper functions
*/ */
static int alc_read_coef_idx(struct hda_codec *codec,
unsigned int coef_idx) static int alc_read_coefex_idx(struct hda_codec *codec,
hda_nid_t nid,
unsigned int coef_idx)
{ {
unsigned int val; unsigned int val;
snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX,
coef_idx); coef_idx);
val = snd_hda_codec_read(codec, 0x20, 0, val = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_PROC_COEF, 0); AC_VERB_GET_PROC_COEF, 0);
return val; return val;
} }
static void alc_write_coef_idx(struct hda_codec *codec, unsigned int coef_idx, #define alc_read_coef_idx(codec, coef_idx) \
alc_read_coefex_idx(codec, 0x20, coef_idx)
static void alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
unsigned int coef_idx,
unsigned int coef_val) unsigned int coef_val)
{ {
snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX,
coef_idx); coef_idx);
snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PROC_COEF,
coef_val); coef_val);
} }
#define alc_write_coef_idx(codec, coef_idx, coef_val) \
alc_write_coefex_idx(codec, 0x20, coef_idx, coef_val)
/* a special bypass for COEF 0; read the cached value at the second time */ /* a special bypass for COEF 0; read the cached value at the second time */
static unsigned int alc_get_coef0(struct hda_codec *codec) static unsigned int alc_get_coef0(struct hda_codec *codec)
{ {
@ -831,7 +838,11 @@ static inline void alc_shutup(struct hda_codec *codec)
snd_hda_shutup_pins(codec); snd_hda_shutup_pins(codec);
} }
#define alc_free snd_hda_gen_free static void alc_free(struct hda_codec *codec)
{
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_FREE);
snd_hda_gen_free(codec);
}
#ifdef CONFIG_PM #ifdef CONFIG_PM
static void alc_power_eapd(struct hda_codec *codec) static void alc_power_eapd(struct hda_codec *codec)
@ -1043,6 +1054,7 @@ enum {
ALC880_FIXUP_UNIWILL, ALC880_FIXUP_UNIWILL,
ALC880_FIXUP_UNIWILL_DIG, ALC880_FIXUP_UNIWILL_DIG,
ALC880_FIXUP_Z71V, ALC880_FIXUP_Z71V,
ALC880_FIXUP_ASUS_W5A,
ALC880_FIXUP_3ST_BASE, ALC880_FIXUP_3ST_BASE,
ALC880_FIXUP_3ST, ALC880_FIXUP_3ST,
ALC880_FIXUP_3ST_DIG, ALC880_FIXUP_3ST_DIG,
@ -1213,6 +1225,26 @@ static const struct hda_fixup alc880_fixups[] = {
{ } { }
} }
}, },
[ALC880_FIXUP_ASUS_W5A] = {
.type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) {
/* set up the whole pins as BIOS is utterly broken */
{ 0x14, 0x0121411f }, /* HP */
{ 0x15, 0x411111f0 }, /* N/A */
{ 0x16, 0x411111f0 }, /* N/A */
{ 0x17, 0x411111f0 }, /* N/A */
{ 0x18, 0x90a60160 }, /* mic */
{ 0x19, 0x411111f0 }, /* N/A */
{ 0x1a, 0x411111f0 }, /* N/A */
{ 0x1b, 0x411111f0 }, /* N/A */
{ 0x1c, 0x411111f0 }, /* N/A */
{ 0x1d, 0x411111f0 }, /* N/A */
{ 0x1e, 0xb743111e }, /* SPDIF out */
{ }
},
.chained = true,
.chain_id = ALC880_FIXUP_GPIO1,
},
[ALC880_FIXUP_3ST_BASE] = { [ALC880_FIXUP_3ST_BASE] = {
.type = HDA_FIXUP_PINS, .type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) { .v.pins = (const struct hda_pintbl[]) {
@ -1334,6 +1366,7 @@ static const struct hda_fixup alc880_fixups[] = {
static const struct snd_pci_quirk alc880_fixup_tbl[] = { static const struct snd_pci_quirk alc880_fixup_tbl[] = {
SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_FIXUP_W810), SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_FIXUP_W810),
SND_PCI_QUIRK(0x1043, 0x10c3, "ASUS W5A", ALC880_FIXUP_ASUS_W5A),
SND_PCI_QUIRK(0x1043, 0x1964, "ASUS Z71V", ALC880_FIXUP_Z71V), SND_PCI_QUIRK(0x1043, 0x1964, "ASUS Z71V", ALC880_FIXUP_Z71V),
SND_PCI_QUIRK_VENDOR(0x1043, "ASUS", ALC880_FIXUP_GPIO1), SND_PCI_QUIRK_VENDOR(0x1043, "ASUS", ALC880_FIXUP_GPIO1),
SND_PCI_QUIRK(0x1558, 0x5401, "Clevo GPIO2", ALC880_FIXUP_GPIO2), SND_PCI_QUIRK(0x1558, 0x5401, "Clevo GPIO2", ALC880_FIXUP_GPIO2),
@ -2388,6 +2421,7 @@ static const struct hda_verb alc268_beep_init_verbs[] = {
enum { enum {
ALC268_FIXUP_INV_DMIC, ALC268_FIXUP_INV_DMIC,
ALC268_FIXUP_HP_EAPD, ALC268_FIXUP_HP_EAPD,
ALC268_FIXUP_SPDIF,
}; };
static const struct hda_fixup alc268_fixups[] = { static const struct hda_fixup alc268_fixups[] = {
@ -2402,6 +2436,13 @@ static const struct hda_fixup alc268_fixups[] = {
{} {}
} }
}, },
[ALC268_FIXUP_SPDIF] = {
.type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) {
{ 0x1e, 0x014b1180 }, /* enable SPDIF out */
{}
}
},
}; };
static const struct hda_model_fixup alc268_fixup_models[] = { static const struct hda_model_fixup alc268_fixup_models[] = {
@ -2411,6 +2452,7 @@ static const struct hda_model_fixup alc268_fixup_models[] = {
}; };
static const struct snd_pci_quirk alc268_fixup_tbl[] = { static const struct snd_pci_quirk alc268_fixup_tbl[] = {
SND_PCI_QUIRK(0x1025, 0x0139, "Acer TravelMate 6293", ALC268_FIXUP_SPDIF),
SND_PCI_QUIRK(0x1025, 0x015b, "Acer AOA 150 (ZG5)", ALC268_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x1025, 0x015b, "Acer AOA 150 (ZG5)", ALC268_FIXUP_INV_DMIC),
/* below is codec SSID since multiple Toshiba laptops have the /* below is codec SSID since multiple Toshiba laptops have the
* same PCI SSID 1179:ff00 * same PCI SSID 1179:ff00
@ -2539,7 +2581,9 @@ enum {
ALC269_TYPE_ALC282, ALC269_TYPE_ALC282,
ALC269_TYPE_ALC283, ALC269_TYPE_ALC283,
ALC269_TYPE_ALC284, ALC269_TYPE_ALC284,
ALC269_TYPE_ALC285,
ALC269_TYPE_ALC286, ALC269_TYPE_ALC286,
ALC269_TYPE_ALC255,
}; };
/* /*
@ -2558,6 +2602,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
case ALC269_TYPE_ALC269VC: case ALC269_TYPE_ALC269VC:
case ALC269_TYPE_ALC280: case ALC269_TYPE_ALC280:
case ALC269_TYPE_ALC284: case ALC269_TYPE_ALC284:
case ALC269_TYPE_ALC285:
ssids = alc269va_ssids; ssids = alc269va_ssids;
break; break;
case ALC269_TYPE_ALC269VB: case ALC269_TYPE_ALC269VB:
@ -2565,6 +2610,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
case ALC269_TYPE_ALC282: case ALC269_TYPE_ALC282:
case ALC269_TYPE_ALC283: case ALC269_TYPE_ALC283:
case ALC269_TYPE_ALC286: case ALC269_TYPE_ALC286:
case ALC269_TYPE_ALC255:
ssids = alc269_ssids; ssids = alc269_ssids;
break; break;
default: default:
@ -2652,7 +2698,7 @@ static void alc283_shutup(struct hda_codec *codec)
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
if (hp_pin_sense) if (hp_pin_sense)
msleep(85); msleep(100);
snd_hda_codec_write(codec, hp_pin, 0, snd_hda_codec_write(codec, hp_pin, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
@ -2661,7 +2707,7 @@ static void alc283_shutup(struct hda_codec *codec)
alc_write_coef_idx(codec, 0x46, val | (3 << 12)); alc_write_coef_idx(codec, 0x46, val | (3 << 12));
if (hp_pin_sense) if (hp_pin_sense)
msleep(85); msleep(100);
snd_hda_shutup_pins(codec); snd_hda_shutup_pins(codec);
alc_write_coef_idx(codec, 0x43, 0x9614); alc_write_coef_idx(codec, 0x43, 0x9614);
} }
@ -2944,6 +2990,23 @@ static void alc269_fixup_mic_mute_hook(void *private_data, int enabled)
snd_hda_set_pin_ctl_cache(codec, spec->mute_led_nid, pinval); snd_hda_set_pin_ctl_cache(codec, spec->mute_led_nid, pinval);
} }
/* Make sure the led works even in runtime suspend */
static unsigned int led_power_filter(struct hda_codec *codec,
hda_nid_t nid,
unsigned int power_state)
{
struct alc_spec *spec = codec->spec;
if (power_state != AC_PWRST_D3 || nid != spec->mute_led_nid)
return power_state;
/* Set pin ctl again, it might have just been set to 0 */
snd_hda_set_pin_ctl(codec, nid,
snd_hda_codec_get_pin_target(codec, nid));
return AC_PWRST_D0;
}
static void alc269_fixup_hp_mute_led(struct hda_codec *codec, static void alc269_fixup_hp_mute_led(struct hda_codec *codec,
const struct hda_fixup *fix, int action) const struct hda_fixup *fix, int action)
{ {
@ -2963,6 +3026,7 @@ static void alc269_fixup_hp_mute_led(struct hda_codec *codec,
spec->mute_led_nid = pin - 0x0a + 0x18; spec->mute_led_nid = pin - 0x0a + 0x18;
spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook; spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook;
spec->gen.vmaster_mute_enum = 1; spec->gen.vmaster_mute_enum = 1;
codec->power_filter = led_power_filter;
snd_printd("Detected mute LED for %x:%d\n", spec->mute_led_nid, snd_printd("Detected mute LED for %x:%d\n", spec->mute_led_nid,
spec->mute_led_polarity); spec->mute_led_polarity);
break; break;
@ -2978,6 +3042,7 @@ static void alc269_fixup_hp_mute_led_mic1(struct hda_codec *codec,
spec->mute_led_nid = 0x18; spec->mute_led_nid = 0x18;
spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook; spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook;
spec->gen.vmaster_mute_enum = 1; spec->gen.vmaster_mute_enum = 1;
codec->power_filter = led_power_filter;
} }
} }
@ -2990,6 +3055,7 @@ static void alc269_fixup_hp_mute_led_mic2(struct hda_codec *codec,
spec->mute_led_nid = 0x19; spec->mute_led_nid = 0x19;
spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook; spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook;
spec->gen.vmaster_mute_enum = 1; spec->gen.vmaster_mute_enum = 1;
codec->power_filter = led_power_filter;
} }
} }
@ -3052,6 +3118,19 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec)
int val; int val;
switch (codec->vendor_id) { switch (codec->vendor_id) {
case 0x10ec0255:
/* LDO and MISC control */
alc_write_coef_idx(codec, 0x1b, 0x0c0b);
/* UAJ function set to menual mode */
alc_write_coef_idx(codec, 0x45, 0xd089);
/* Direct Drive HP Amp control(Set to verb control)*/
val = alc_read_coefex_idx(codec, 0x57, 0x05);
alc_write_coefex_idx(codec, 0x57, 0x05, val & ~(1<<14));
/* Set MIC2 Vref gate with HP */
alc_write_coef_idx(codec, 0x06, 0x6104);
/* Direct Drive HP Amp control */
alc_write_coefex_idx(codec, 0x57, 0x03, 0x8aa6);
break;
case 0x10ec0283: case 0x10ec0283:
alc_write_coef_idx(codec, 0x1b, 0x0c0b); alc_write_coef_idx(codec, 0x1b, 0x0c0b);
alc_write_coef_idx(codec, 0x45, 0xc429); alc_write_coef_idx(codec, 0x45, 0xc429);
@ -3083,6 +3162,14 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin,
int val; int val;
switch (codec->vendor_id) { switch (codec->vendor_id) {
case 0x10ec0255:
alc_write_coef_idx(codec, 0x45, 0xc489);
snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
alc_write_coefex_idx(codec, 0x57, 0x03, 0x8aa6);
/* Set MIC2 Vref gate to normal */
alc_write_coef_idx(codec, 0x06, 0x6100);
snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
break;
case 0x10ec0283: case 0x10ec0283:
alc_write_coef_idx(codec, 0x45, 0xc429); alc_write_coef_idx(codec, 0x45, 0xc429);
snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
@ -3114,6 +3201,12 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin,
static void alc_headset_mode_default(struct hda_codec *codec) static void alc_headset_mode_default(struct hda_codec *codec)
{ {
switch (codec->vendor_id) { switch (codec->vendor_id) {
case 0x10ec0255:
alc_write_coef_idx(codec, 0x45, 0xc089);
alc_write_coef_idx(codec, 0x45, 0xc489);
alc_write_coefex_idx(codec, 0x57, 0x03, 0x8ea6);
alc_write_coef_idx(codec, 0x49, 0x0049);
break;
case 0x10ec0283: case 0x10ec0283:
alc_write_coef_idx(codec, 0x06, 0x2100); alc_write_coef_idx(codec, 0x06, 0x2100);
alc_write_coef_idx(codec, 0x32, 0x4ea3); alc_write_coef_idx(codec, 0x32, 0x4ea3);
@ -3137,6 +3230,12 @@ static void alc_headset_mode_default(struct hda_codec *codec)
static void alc_headset_mode_ctia(struct hda_codec *codec) static void alc_headset_mode_ctia(struct hda_codec *codec)
{ {
switch (codec->vendor_id) { switch (codec->vendor_id) {
case 0x10ec0255:
/* Set to CTIA type */
alc_write_coef_idx(codec, 0x45, 0xd489);
alc_write_coef_idx(codec, 0x1b, 0x0c2b);
alc_write_coefex_idx(codec, 0x57, 0x03, 0x8ea6);
break;
case 0x10ec0283: case 0x10ec0283:
alc_write_coef_idx(codec, 0x45, 0xd429); alc_write_coef_idx(codec, 0x45, 0xd429);
alc_write_coef_idx(codec, 0x1b, 0x0c2b); alc_write_coef_idx(codec, 0x1b, 0x0c2b);
@ -3159,6 +3258,12 @@ static void alc_headset_mode_ctia(struct hda_codec *codec)
static void alc_headset_mode_omtp(struct hda_codec *codec) static void alc_headset_mode_omtp(struct hda_codec *codec)
{ {
switch (codec->vendor_id) { switch (codec->vendor_id) {
case 0x10ec0255:
/* Set to OMTP Type */
alc_write_coef_idx(codec, 0x45, 0xe489);
alc_write_coef_idx(codec, 0x1b, 0x0c2b);
alc_write_coefex_idx(codec, 0x57, 0x03, 0x8ea6);
break;
case 0x10ec0283: case 0x10ec0283:
alc_write_coef_idx(codec, 0x45, 0xe429); alc_write_coef_idx(codec, 0x45, 0xe429);
alc_write_coef_idx(codec, 0x1b, 0x0c2b); alc_write_coef_idx(codec, 0x1b, 0x0c2b);
@ -3184,6 +3289,15 @@ static void alc_determine_headset_type(struct hda_codec *codec)
struct alc_spec *spec = codec->spec; struct alc_spec *spec = codec->spec;
switch (codec->vendor_id) { switch (codec->vendor_id) {
case 0x10ec0255:
/* combo jack auto switch control(Check type)*/
alc_write_coef_idx(codec, 0x45, 0xd089);
/* combo jack auto switch control(Vref conteol) */
alc_write_coef_idx(codec, 0x49, 0x0149);
msleep(300);
val = alc_read_coef_idx(codec, 0x46);
is_ctia = (val & 0x0070) == 0x0070;
break;
case 0x10ec0283: case 0x10ec0283:
alc_write_coef_idx(codec, 0x45, 0xd029); alc_write_coef_idx(codec, 0x45, 0xd029);
msleep(300); msleep(300);
@ -3330,6 +3444,21 @@ static void alc_fixup_headset_mode_no_hp_mic(struct hda_codec *codec,
alc_fixup_headset_mode(codec, fix, action); alc_fixup_headset_mode(codec, fix, action);
} }
static void alc_fixup_headset_mode_alc255(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
if (action == HDA_FIXUP_ACT_PRE_PROBE) {
/* Set to iphone type */
alc_write_coef_idx(codec, 0x1b, 0x880b);
alc_write_coef_idx(codec, 0x45, 0xd089);
alc_write_coef_idx(codec, 0x1b, 0x080b);
alc_write_coef_idx(codec, 0x46, 0x0004);
alc_write_coef_idx(codec, 0x1b, 0x0c0b);
msleep(30);
}
alc_fixup_headset_mode(codec, fix, action);
}
static void alc_fixup_headset_mode_alc668(struct hda_codec *codec, static void alc_fixup_headset_mode_alc668(struct hda_codec *codec,
const struct hda_fixup *fix, int action) const struct hda_fixup *fix, int action)
{ {
@ -3443,7 +3572,11 @@ static void alc283_fixup_chromebook(struct hda_codec *codec,
switch (action) { switch (action) {
case HDA_FIXUP_ACT_PRE_PROBE: case HDA_FIXUP_ACT_PRE_PROBE:
alc283_chromebook_caps(codec); alc283_chromebook_caps(codec);
/* Disable AA-loopback as it causes white noise */
spec->gen.mixer_nid = 0;
spec->gen.hp_automute_hook = alc283_hp_automute_hook; spec->gen.hp_automute_hook = alc283_hp_automute_hook;
break;
case HDA_FIXUP_ACT_INIT:
/* MIC2-VREF control */ /* MIC2-VREF control */
/* Set to manual mode */ /* Set to manual mode */
val = alc_read_coef_idx(codec, 0x06); val = alc_read_coef_idx(codec, 0x06);
@ -3514,6 +3647,74 @@ static void alc290_fixup_mono_speakers(struct hda_codec *codec,
snd_hda_override_wcaps(codec, 0x03, 0); snd_hda_override_wcaps(codec, 0x03, 0);
} }
#if IS_ENABLED(CONFIG_THINKPAD_ACPI)
#include <linux/thinkpad_acpi.h>
static int (*led_set_func)(int, bool);
static void update_tpacpi_mute_led(void *private_data, int enabled)
{
if (led_set_func)
led_set_func(TPACPI_LED_MUTE, !enabled);
}
static void update_tpacpi_micmute_led(struct hda_codec *codec,
struct snd_ctl_elem_value *ucontrol)
{
if (!ucontrol || !led_set_func)
return;
if (strcmp("Capture Switch", ucontrol->id.name) == 0 && ucontrol->id.index == 0) {
/* TODO: How do I verify if it's a mono or stereo here? */
bool val = ucontrol->value.integer.value[0] || ucontrol->value.integer.value[1];
led_set_func(TPACPI_LED_MICMUTE, !val);
}
}
static void alc_fixup_thinkpad_acpi(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
bool removefunc = false;
if (action == HDA_FIXUP_ACT_PROBE) {
if (!led_set_func)
led_set_func = symbol_request(tpacpi_led_set);
if (!led_set_func) {
snd_printk(KERN_WARNING "Failed to find thinkpad-acpi symbol tpacpi_led_set\n");
return;
}
removefunc = true;
if (led_set_func(TPACPI_LED_MUTE, false) >= 0) {
spec->gen.vmaster_mute.hook = update_tpacpi_mute_led;
removefunc = false;
}
if (led_set_func(TPACPI_LED_MICMUTE, false) >= 0) {
if (spec->gen.num_adc_nids > 1)
snd_printdd("Skipping micmute LED control due to several ADCs");
else {
spec->gen.cap_sync_hook = update_tpacpi_micmute_led;
removefunc = false;
}
}
}
if (led_set_func && (action == HDA_FIXUP_ACT_FREE || removefunc)) {
symbol_put(tpacpi_led_set);
led_set_func = NULL;
}
}
#else
static void alc_fixup_thinkpad_acpi(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
}
#endif
enum { enum {
ALC269_FIXUP_SONY_VAIO, ALC269_FIXUP_SONY_VAIO,
ALC275_FIXUP_SONY_VAIO_GPIO2, ALC275_FIXUP_SONY_VAIO_GPIO2,
@ -3552,11 +3753,15 @@ enum {
ALC271_FIXUP_HP_GATE_MIC_JACK, ALC271_FIXUP_HP_GATE_MIC_JACK,
ALC269_FIXUP_ACER_AC700, ALC269_FIXUP_ACER_AC700,
ALC269_FIXUP_LIMIT_INT_MIC_BOOST, ALC269_FIXUP_LIMIT_INT_MIC_BOOST,
ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED,
ALC269VB_FIXUP_ORDISSIMO_EVE2, ALC269VB_FIXUP_ORDISSIMO_EVE2,
ALC283_FIXUP_CHROME_BOOK, ALC283_FIXUP_CHROME_BOOK,
ALC282_FIXUP_ASUS_TX300, ALC282_FIXUP_ASUS_TX300,
ALC283_FIXUP_INT_MIC, ALC283_FIXUP_INT_MIC,
ALC290_FIXUP_MONO_SPEAKERS, ALC290_FIXUP_MONO_SPEAKERS,
ALC269_FIXUP_THINKPAD_ACPI,
ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC255_FIXUP_HEADSET_MODE,
}; };
static const struct hda_fixup alc269_fixups[] = { static const struct hda_fixup alc269_fixups[] = {
@ -3821,6 +4026,12 @@ static const struct hda_fixup alc269_fixups[] = {
.type = HDA_FIXUP_FUNC, .type = HDA_FIXUP_FUNC,
.v.func = alc269_fixup_limit_int_mic_boost, .v.func = alc269_fixup_limit_int_mic_boost,
}, },
[ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc269_fixup_limit_int_mic_boost,
.chained = true,
.chain_id = ALC269_FIXUP_HP_MUTE_LED_MIC1,
},
[ALC269VB_FIXUP_ORDISSIMO_EVE2] = { [ALC269VB_FIXUP_ORDISSIMO_EVE2] = {
.type = HDA_FIXUP_PINS, .type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) { .v.pins = (const struct hda_pintbl[]) {
@ -3854,6 +4065,26 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true, .chained = true,
.chain_id = ALC269_FIXUP_DELL3_MIC_NO_PRESENCE, .chain_id = ALC269_FIXUP_DELL3_MIC_NO_PRESENCE,
}, },
[ALC269_FIXUP_THINKPAD_ACPI] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_thinkpad_acpi,
.chained = true,
.chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST
},
[ALC255_FIXUP_DELL1_MIC_NO_PRESENCE] = {
.type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) {
{ 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
{ 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */
{ }
},
.chained = true,
.chain_id = ALC255_FIXUP_HEADSET_MODE
},
[ALC255_FIXUP_HEADSET_MODE] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_headset_mode_alc255,
},
}; };
static const struct snd_pci_quirk alc269_fixup_tbl[] = { static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@ -3896,12 +4127,15 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x0609, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0609, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0613, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0613, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0616, "Dell Vostro 5470", ALC290_FIXUP_MONO_SPEAKERS), SND_PCI_QUIRK(0x1028, 0x0616, "Dell Vostro 5470", ALC290_FIXUP_MONO_SPEAKERS),
SND_PCI_QUIRK(0x1028, 0x061f, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x063f, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x15cc, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x15cc, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x15cd, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x15cd, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2), SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2),
SND_PCI_QUIRK(0x103c, 0x18e6, "HP", ALC269_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x18e6, "HP", ALC269_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x1973, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x1973, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1),
SND_PCI_QUIRK(0x103c, 0x1983, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x1983, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1),
SND_PCI_QUIRK(0x103c, 0x218b, "HP", ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x21ed, "HP Falco Chromebook", ALC283_FIXUP_CHROME_BOOK), SND_PCI_QUIRK(0x103c, 0x21ed, "HP Falco Chromebook", ALC283_FIXUP_CHROME_BOOK),
SND_PCI_QUIRK_VENDOR(0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK_VENDOR(0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED),
SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300), SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300),
@ -3937,7 +4171,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x2208, "Thinkpad T431s", ALC269_FIXUP_LENOVO_DOCK), SND_PCI_QUIRK(0x17aa, 0x2208, "Thinkpad T431s", ALC269_FIXUP_LENOVO_DOCK),
SND_PCI_QUIRK(0x17aa, 0x220c, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x220c, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad", ALC269_FIXUP_THINKPAD_ACPI),
SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x17aa, 0x501a, "Thinkpad", ALC283_FIXUP_INT_MIC), SND_PCI_QUIRK(0x17aa, 0x501a, "Thinkpad", ALC283_FIXUP_INT_MIC),
@ -4125,9 +4359,16 @@ static int patch_alc269(struct hda_codec *codec)
case 0x10ec0292: case 0x10ec0292:
spec->codec_variant = ALC269_TYPE_ALC284; spec->codec_variant = ALC269_TYPE_ALC284;
break; break;
case 0x10ec0285:
case 0x10ec0293:
spec->codec_variant = ALC269_TYPE_ALC285;
break;
case 0x10ec0286: case 0x10ec0286:
spec->codec_variant = ALC269_TYPE_ALC286; spec->codec_variant = ALC269_TYPE_ALC286;
break; break;
case 0x10ec0255:
spec->codec_variant = ALC269_TYPE_ALC255;
break;
} }
if (snd_hda_codec_read(codec, 0x51, 0, AC_VERB_PARAMETERS, 0) == 0x10ec5505) { if (snd_hda_codec_read(codec, 0x51, 0, AC_VERB_PARAMETERS, 0) == 0x10ec5505) {
@ -4415,6 +4656,25 @@ static void alc272_fixup_mario(struct hda_codec *codec,
"hda_codec: failed to override amp caps for NID 0x2\n"); "hda_codec: failed to override amp caps for NID 0x2\n");
} }
static const struct snd_pcm_chmap_elem asus_pcm_2_1_chmaps[] = {
{ .channels = 2,
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
{ .channels = 4,
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
SNDRV_CHMAP_NA, SNDRV_CHMAP_LFE } }, /* LFE only on right */
{ }
};
/* override the 2.1 chmap */
static void alc662_fixup_bass_chmap(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
if (action == HDA_FIXUP_ACT_BUILD) {
struct alc_spec *spec = codec->spec;
spec->gen.pcm_rec[0].stream[0].chmap = asus_pcm_2_1_chmaps;
}
}
enum { enum {
ALC662_FIXUP_ASPIRE, ALC662_FIXUP_ASPIRE,
ALC662_FIXUP_IDEAPAD, ALC662_FIXUP_IDEAPAD,
@ -4435,6 +4695,7 @@ enum {
ALC662_FIXUP_INV_DMIC, ALC662_FIXUP_INV_DMIC,
ALC668_FIXUP_DELL_MIC_NO_PRESENCE, ALC668_FIXUP_DELL_MIC_NO_PRESENCE,
ALC668_FIXUP_HEADSET_MODE, ALC668_FIXUP_HEADSET_MODE,
ALC662_FIXUP_BASS_CHMAP,
}; };
static const struct hda_fixup alc662_fixups[] = { static const struct hda_fixup alc662_fixups[] = {
@ -4609,6 +4870,12 @@ static const struct hda_fixup alc662_fixups[] = {
.type = HDA_FIXUP_FUNC, .type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_headset_mode_alc668, .v.func = alc_fixup_headset_mode_alc668,
}, },
[ALC662_FIXUP_BASS_CHMAP] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc662_fixup_bass_chmap,
.chained = true,
.chain_id = ALC662_FIXUP_ASUS_MODE4
},
}; };
static const struct snd_pci_quirk alc662_fixup_tbl[] = { static const struct snd_pci_quirk alc662_fixup_tbl[] = {
@ -4621,9 +4888,10 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = {
SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE), SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE),
SND_PCI_QUIRK(0x1028, 0x05d8, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x05d8, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x05db, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x05db, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0626, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800), SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800),
SND_PCI_QUIRK(0x1043, 0x1477, "ASUS N56VZ", ALC662_FIXUP_ASUS_MODE4), SND_PCI_QUIRK(0x1043, 0x1477, "ASUS N56VZ", ALC662_FIXUP_BASS_CHMAP),
SND_PCI_QUIRK(0x1043, 0x1bf3, "ASUS N76VZ", ALC662_FIXUP_ASUS_MODE4), SND_PCI_QUIRK(0x1043, 0x1bf3, "ASUS N76VZ", ALC662_FIXUP_BASS_CHMAP),
SND_PCI_QUIRK(0x1043, 0x8469, "ASUS mobo", ALC662_FIXUP_NO_JACK_DETECT), SND_PCI_QUIRK(0x1043, 0x8469, "ASUS mobo", ALC662_FIXUP_NO_JACK_DETECT),
SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_FIXUP_ASUS_MODE2), SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_FIXUP_ASUS_MODE2),
SND_PCI_QUIRK(0x144d, 0xc051, "Samsung R720", ALC662_FIXUP_IDEAPAD), SND_PCI_QUIRK(0x144d, 0xc051, "Samsung R720", ALC662_FIXUP_IDEAPAD),
@ -4842,6 +5110,7 @@ static int patch_alc680(struct hda_codec *codec)
static const struct hda_codec_preset snd_hda_preset_realtek[] = { static const struct hda_codec_preset snd_hda_preset_realtek[] = {
{ .id = 0x10ec0221, .name = "ALC221", .patch = patch_alc269 }, { .id = 0x10ec0221, .name = "ALC221", .patch = patch_alc269 },
{ .id = 0x10ec0233, .name = "ALC233", .patch = patch_alc269 }, { .id = 0x10ec0233, .name = "ALC233", .patch = patch_alc269 },
{ .id = 0x10ec0255, .name = "ALC255", .patch = patch_alc269 },
{ .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 }, { .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 },
{ .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 }, { .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 },
{ .id = 0x10ec0267, .name = "ALC267", .patch = patch_alc268 }, { .id = 0x10ec0267, .name = "ALC267", .patch = patch_alc268 },
@ -4855,9 +5124,11 @@ static const struct hda_codec_preset snd_hda_preset_realtek[] = {
{ .id = 0x10ec0282, .name = "ALC282", .patch = patch_alc269 }, { .id = 0x10ec0282, .name = "ALC282", .patch = patch_alc269 },
{ .id = 0x10ec0283, .name = "ALC283", .patch = patch_alc269 }, { .id = 0x10ec0283, .name = "ALC283", .patch = patch_alc269 },
{ .id = 0x10ec0284, .name = "ALC284", .patch = patch_alc269 }, { .id = 0x10ec0284, .name = "ALC284", .patch = patch_alc269 },
{ .id = 0x10ec0285, .name = "ALC285", .patch = patch_alc269 },
{ .id = 0x10ec0286, .name = "ALC286", .patch = patch_alc269 }, { .id = 0x10ec0286, .name = "ALC286", .patch = patch_alc269 },
{ .id = 0x10ec0290, .name = "ALC290", .patch = patch_alc269 }, { .id = 0x10ec0290, .name = "ALC290", .patch = patch_alc269 },
{ .id = 0x10ec0292, .name = "ALC292", .patch = patch_alc269 }, { .id = 0x10ec0292, .name = "ALC292", .patch = patch_alc269 },
{ .id = 0x10ec0293, .name = "ALC293", .patch = patch_alc269 },
{ .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660", { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660",
.patch = patch_alc861 }, .patch = patch_alc861 },
{ .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd }, { .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd },

View File

@ -2091,8 +2091,10 @@ static void stac92hd83xxx_fixup_hp_mic_led(struct hda_codec *codec,
{ {
struct sigmatel_spec *spec = codec->spec; struct sigmatel_spec *spec = codec->spec;
if (action == HDA_FIXUP_ACT_PRE_PROBE) if (action == HDA_FIXUP_ACT_PRE_PROBE) {
spec->mic_mute_led_gpio = 0x08; /* GPIO3 */ spec->mic_mute_led_gpio = 0x08; /* GPIO3 */
codec->bus->avoid_link_reset = 1;
}
} }
static void stac92hd83xxx_fixup_headset_jack(struct hda_codec *codec, static void stac92hd83xxx_fixup_headset_jack(struct hda_codec *codec,

View File

@ -203,12 +203,12 @@ static void psc724_set_jack_state(struct snd_ice1712 *ice, bool hp_connected)
/* notify about master speaker mute change */ /* notify about master speaker mute change */
memset(&elem_id, 0, sizeof(elem_id)); memset(&elem_id, 0, sizeof(elem_id));
elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
strncpy(elem_id.name, "Master Speakers Playback Switch", strlcpy(elem_id.name, "Master Speakers Playback Switch",
sizeof(elem_id.name)); sizeof(elem_id.name));
kctl = snd_ctl_find_id(ice->card, &elem_id); kctl = snd_ctl_find_id(ice->card, &elem_id);
snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
/* and headphone mute change */ /* and headphone mute change */
strncpy(elem_id.name, spec->wm8776.ctl[WM8776_CTL_HP_SW].name, strlcpy(elem_id.name, spec->wm8776.ctl[WM8776_CTL_HP_SW].name,
sizeof(elem_id.name)); sizeof(elem_id.name));
kctl = snd_ctl_find_id(ice->card, &elem_id); kctl = snd_ctl_find_id(ice->card, &elem_id);
snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);

View File

@ -203,6 +203,7 @@ static const char * const ext_clock_names[3] = {"IEC958 In", "Word Clock 1xFS",
#define AK4620_DEEMVOL_REG 0x03 #define AK4620_DEEMVOL_REG 0x03
#define AK4620_SMUTE (1<<7) #define AK4620_SMUTE (1<<7)
#ifdef CONFIG_PROC_FS
/* /*
* Conversion from int value to its binary form. Used for debugging. * Conversion from int value to its binary form. Used for debugging.
* The output buffer must be allocated prior to calling the function. * The output buffer must be allocated prior to calling the function.
@ -227,6 +228,7 @@ static char *get_binary(char *buffer, int value)
buffer[pos] = '\0'; buffer[pos] = '\0';
return buffer; return buffer;
} }
#endif /* CONFIG_PROC_FS */
/* /*
* Initial setup of the conversion array GPIO <-> rate * Initial setup of the conversion array GPIO <-> rate

View File

@ -253,7 +253,8 @@ static int snd_wm8766_ctl_get(struct snd_kcontrol *kcontrol,
} }
if (wm->ctl[n].flags & WM8766_FLAG_INVERT) { if (wm->ctl[n].flags & WM8766_FLAG_INVERT) {
val1 = wm->ctl[n].max - (val1 - wm->ctl[n].min); val1 = wm->ctl[n].max - (val1 - wm->ctl[n].min);
val2 = wm->ctl[n].max - (val2 - wm->ctl[n].min); if (wm->ctl[n].flags & WM8766_FLAG_STEREO)
val2 = wm->ctl[n].max - (val2 - wm->ctl[n].min);
} }
ucontrol->value.integer.value[0] = val1; ucontrol->value.integer.value[0] = val1;
if (wm->ctl[n].flags & WM8766_FLAG_STEREO) if (wm->ctl[n].flags & WM8766_FLAG_STEREO)

View File

@ -52,7 +52,7 @@ static void snd_wm8776_activate_ctl(struct snd_wm8776 *wm,
unsigned int index_offset; unsigned int index_offset;
memset(&elem_id, 0, sizeof(elem_id)); memset(&elem_id, 0, sizeof(elem_id));
strncpy(elem_id.name, ctl_name, sizeof(elem_id.name)); strlcpy(elem_id.name, ctl_name, sizeof(elem_id.name));
elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
kctl = snd_ctl_find_id(card, &elem_id); kctl = snd_ctl_find_id(card, &elem_id);
if (!kctl) if (!kctl)
@ -526,7 +526,8 @@ static int snd_wm8776_ctl_get(struct snd_kcontrol *kcontrol,
} }
if (wm->ctl[n].flags & WM8776_FLAG_INVERT) { if (wm->ctl[n].flags & WM8776_FLAG_INVERT) {
val1 = wm->ctl[n].max - (val1 - wm->ctl[n].min); val1 = wm->ctl[n].max - (val1 - wm->ctl[n].min);
val2 = wm->ctl[n].max - (val2 - wm->ctl[n].min); if (wm->ctl[n].flags & WM8776_FLAG_STEREO)
val2 = wm->ctl[n].max - (val2 - wm->ctl[n].min);
} }
ucontrol->value.integer.value[0] = val1; ucontrol->value.integer.value[0] = val1;
if (wm->ctl[n].flags & WM8776_FLAG_STEREO) if (wm->ctl[n].flags & WM8776_FLAG_STEREO)

View File

@ -1541,17 +1541,16 @@ static int snd_intel8x0_pcm1(struct intel8x0 *chip, int device,
snd_dma_pci_data(chip->pci), snd_dma_pci_data(chip->pci),
rec->prealloc_size, rec->prealloc_max_size); rec->prealloc_size, rec->prealloc_max_size);
if (rec->ac97_idx == ICHD_PCMOUT && rec->playback_ops) { if (rec->playback_ops &&
rec->playback_ops->open == snd_intel8x0_playback_open) {
struct snd_pcm_chmap *chmap; struct snd_pcm_chmap *chmap;
int chs = 2; int chs = 2;
if (rec->ac97_idx == ICHD_PCMOUT) { if (chip->multi8)
if (chip->multi8) chs = 8;
chs = 8; else if (chip->multi6)
else if (chip->multi6) chs = 6;
chs = 6; else if (chip->multi4)
else if (chip->multi4) chs = 4;
chs = 4;
}
err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
snd_pcm_alt_chmaps, chs, 0, snd_pcm_alt_chmaps, chs, 0,
&chmap); &chmap);

View File

@ -463,7 +463,7 @@ static int lola_parse_tree(struct lola *chip)
err = lola_read_param(chip, 1, LOLA_PAR_FUNCTION_TYPE, &val); err = lola_read_param(chip, 1, LOLA_PAR_FUNCTION_TYPE, &val);
if (err < 0) { if (err < 0) {
printk(KERN_ERR SFX "Can't read FUNCTION_TYPE for 0x%x\n", nid); printk(KERN_ERR SFX "Can't read FUNCTION_TYPE\n");
return err; return err;
} }
if (val != 1) { if (val != 1) {

View File

@ -453,8 +453,8 @@ static void lx_trigger_start(struct lx6464es *chip, struct lx_stream *lx_stream)
lower_32_bits(buf), upper_32_bits(buf), lower_32_bits(buf), upper_32_bits(buf),
&buffer_index); &buffer_index);
snd_printdd(LXP "starting: buffer index %x on %p (%d bytes)\n", snd_printdd(LXP "starting: buffer index %x on 0x%lx (%d bytes)\n",
buffer_index, (void *)buf, period_bytes); buffer_index, (unsigned long)buf, period_bytes);
buf += period_bytes; buf += period_bytes;
} }

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