sound updates for 3.16-rc1

At this time, majority of changes come from ASoC world while we got a
 few new drivers in other places for FireWire and USB.  There have been
 lots of ASoC core cleanups / refactoring, but very little visible to
 external users.
 
 ASoC
 - Support for specifying aux CODECs in DT
 - Removal of the deprecated mux and enum macros
 - More moves towards full componentisation
 - Removal of some unused I/O code
 - Lots of cleanups, fixes and enhancements to the davinci, Freescale,
   Haswell and Realtek drivers
 - Several drivers exposed directly in Kconfig for use with simple-card
 - GPIO descriptor support for jacks
 - More updates and fixes to the Freescale SSI, Intel and rsnd drivers
 - New drivers for Cirrus CS42L56, Realtek RT5639, RT5642 and RT5651 and
   ST STA350, Analog Devices ADAU1361, ADAU1381, ADAU1761 and ADAU1781,
   and Realtek RT5677
 
 HD-audio:
 - Clean up Dell headset quirks
 - Noise fixes for Dell and Sony laptops
 - Thinkpad T440 dock fix
 - Realtek codec updates (ALC293,ALC233,ALC3235)
 - Tegra HD-audio HDMI support
 
 FireWire-audio:
 - FireWire audio stack enhancement (AMDTP, MIDI), support for incoming
   isochronous stream and duplex streams with timestamp synchronization
 - BeBoB-based devices support
 - Fireworks-based device support
 
 USB-audio:
 - Behringer BCD2000 USB device support
 
 Misc:
 - Clean up of a few old drivers, atmel, fm801, etc
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.22 (GNU/Linux)
 
 iQIcBAABAgAGBQJTjzW4AAoJEGwxgFQ9KSmkUrMP/1z43Kp+F9Y0v0VBH6oR/d4N
 l9IyxBno/ABxfWloGFnRLEyzZyj2yG8A7inT0alVXJifHJN4iPOKBb5dPE9LMRvc
 qLhJjMwznAirkuE8Wsk+IAoKuyXEI4m+KKEIXt5WJ3UyAo/j1lySZVMChzcTFFk/
 oc2C6CciYrQLziaaL/K5zD9v9XdDr9koOaSHK/xjUOCbDlEBJu6T2IvRI/tkqJmy
 8oRRhRteXZ9D959+ftntKrFVf10APQ4ZQbsX/pHboduaoozYAJSJGFhQNbh/UZnb
 zwwwanNZvLwzn+rRXJJuzHF4jra34CuQFL2awsDP9Wck9E3YLmt4audNQ6LM6J8z
 IVZs5IjMIL1ey1T2oRczLnv7EoDp0xdP38GqXnQ88j3zd+Ifi77idNw1ssU1aZ5B
 LzEFEytT1UbEUkqom9qtIG+GId9hSmVmHQuLsc6Ayg7md0oBeJnBC05Xt5FATdrp
 HseHYfSrNNDBFKyj8+j0TVtHc9Xf4SKziSVWz/PT0gaROzOsR2e46HC2Hvut+OFZ
 rLLPXn9up5viQFxOTbO7sdYGCYa/iVH7IwB2oCP6Z5/I8+fhsU7aA4Hl+0wBikin
 PDSwuchmRlNpHJ18YDonjzFtWA51wG4IlcNbQY4ywO/jFae06KYxQPTwvmJI0+oV
 GXyKtjdBnQg8nnWJlS8J
 =nxFA
 -----END PGP SIGNATURE-----

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

Pull sound updates from Takashi Iwai:
 "At this time, majority of changes come from ASoC world while we got a
  few new drivers in other places for FireWire and USB.  There have been
  lots of ASoC core cleanups / refactoring, but very little visible to
  external users.

  ASoC:
   - Support for specifying aux CODECs in DT
   - Removal of the deprecated mux and enum macros
   - More moves towards full componentisation
   - Removal of some unused I/O code
   - Lots of cleanups, fixes and enhancements to the davinci, Freescale,
     Haswell and Realtek drivers
   - Several drivers exposed directly in Kconfig for use with
     simple-card
   - GPIO descriptor support for jacks
   - More updates and fixes to the Freescale SSI, Intel and rsnd drivers
   - New drivers for Cirrus CS42L56, Realtek RT5639, RT5642 and RT5651
     and ST STA350, Analog Devices ADAU1361, ADAU1381, ADAU1761 and
     ADAU1781, and Realtek RT5677

  HD-audio:
   - Clean up Dell headset quirks
   - Noise fixes for Dell and Sony laptops
   - Thinkpad T440 dock fix
   - Realtek codec updates (ALC293,ALC233,ALC3235)
   - Tegra HD-audio HDMI support

  FireWire-audio:
   - FireWire audio stack enhancement (AMDTP, MIDI), support for
     incoming isochronous stream and duplex streams with timestamp
     synchronization
   - BeBoB-based devices support
   - Fireworks-based device support

  USB-audio:
   - Behringer BCD2000 USB device support

  Misc:
   - Clean up of a few old drivers, atmel, fm801, etc"

* tag 'sound-3.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (480 commits)
  ASoC: Fix wrong argument for card remove callbacks
  ASoC: free jack GPIOs before the sound card is freed
  ALSA: firewire-lib: Remove a comment about restriction of asynchronous operation
  ASoC: cache: Fix error code when not using ASoC level cache
  ALSA: hda/realtek - Fix COEF widget NID for ALC260 replacer fixup
  ALSA: hda/realtek - Correction of fixup codes for PB V7900 laptop
  ALSA: firewire-lib: Use IEC 61883-6 compliant labels for Raw Audio data
  ASoC: add RT5677 CODEC driver
  ASoC: intel: The Baytrail/MAX98090 driver depends on I2C
  ASoC: rt5640: Add the function "get_clk_info" to RL6231 shared support
  ASoC: rt5640: Add the function of the PLL clock calculation to RL6231 shared support
  ASoC: rt5640: Add RL6231 class device shared support for RT5640, RT5645 and RT5651
  ASoC: cache: Fix possible ZERO_SIZE_PTR pointer dereferencing error.
  ASoC: Add helper functions to cast from DAPM context to CODEC/platform
  ALSA: bebob: sizeof() vs ARRAY_SIZE() typo
  ASoC: wm9713: correct mono out PGA sources
  ALSA: synth: emux: soundfont.c: Cleaning up memory leak
  ASoC: fsl: Remove dependencies of boards for SND_SOC_EUKREA_TLV320
  ASoC: fsl-ssi: Use regmap
  ASoC: fsl-ssi: reorder and document fsl_ssi_private
  ...
This commit is contained in:
Linus Torvalds 2014-06-04 09:08:25 -07:00
commit b77279bc2e
337 changed files with 36801 additions and 5502 deletions

View File

@ -10,6 +10,9 @@ Optional properties:
- fsl,mc13xxx-uses-touch : Indicate the touchscreen controller is being used - fsl,mc13xxx-uses-touch : Indicate the touchscreen controller is being used
Sub-nodes: Sub-nodes:
- codec: Contain the Audio Codec node.
- adc-port: Contain PMIC SSI port number used for ADC.
- dac-port: Contain PMIC SSI port number used for DAC.
- leds : Contain the led nodes and initial register values in property - leds : Contain the led nodes and initial register values in property
"led-control". Number of register depends of used IC, for MC13783 is 6, "led-control". Number of register depends of used IC, for MC13783 is 6,
for MC13892 is 4, for MC34708 is 1. See datasheet for bits definitions of for MC13892 is 4, for MC34708 is 1. See datasheet for bits definitions of

View File

@ -8,6 +8,8 @@ Required properties:
- reg : The chip select number on the SPI bus - reg : The chip select number on the SPI bus
- vdd-supply : A regulator node, providing 2.7V - 3.6V
Optional properties: Optional properties:
- reset-gpio : a GPIO spec for the reset pin. If specified, it will be - reset-gpio : a GPIO spec for the reset pin. If specified, it will be
@ -19,4 +21,5 @@ spdif: ak4104@0 {
compatible = "asahi-kasei,ak4104"; compatible = "asahi-kasei,ak4104";
reg = <0>; reg = <0>;
spi-max-frequency = <5000000>; spi-max-frequency = <5000000>;
vdd-supply = <&vdd_3v3_reg>;
}; };

View File

@ -0,0 +1,25 @@
ALC5621/ALC5622/ALC5623 audio Codec
Required properties:
- compatible: "realtek,alc5623"
- reg: the I2C address of the device.
Optional properties:
- add-ctrl: Default register value for Reg-40h, Additional Control
Register. If absent or has the value of 0, the
register is untouched.
- jack-det-ctrl: Default register value for Reg-5Ah, Jack Detect
Control Register. If absent or has value 0, the
register is untouched.
Example:
alc5621: alc5621@1a {
compatible = "alc5621";
reg = <0x1a>;
add-ctrl = <0x3700>;
jack-det-ctrl = <0x4810>;
};

View File

@ -0,0 +1,63 @@
CS42L52 audio CODEC
Required properties:
- compatible : "cirrus,cs42l56"
- reg : the I2C address of the device for I2C
- VA-supply, VCP-supply, VLDO-supply : power supplies for the device,
as covered in Documentation/devicetree/bindings/regulator/regulator.txt.
Optional properties:
- cirrus,gpio-nreset : GPIO controller's phandle and the number
of the GPIO used to reset the codec.
- cirrus,chgfreq-divisor : Values used to set the Charge Pump Frequency.
Allowable values of 0x00 through 0x0F. These are raw values written to the
register, not the actual frequency. The frequency is determined by the following.
Frequency = MCLK / 4 * (N+2)
N = chgfreq_val
MCLK = Where MCLK is the frequency of the mclk signal after the MCLKDIV2 circuit.
- cirrus,ain1a-ref-cfg, ain1b-ref-cfg : boolean, If present, AIN1A or AIN1B are configured
as a pseudo-differential input referenced to AIN1REF/AIN3A.
- cirrus,ain2a-ref-cfg, ain2b-ref-cfg : boolean, If present, AIN2A or AIN2B are configured
as a pseudo-differential input referenced to AIN2REF/AIN3B.
- cirrus,micbias-lvl: Set the output voltage level on the MICBIAS Pin.
0 = 0.5 x VA
1 = 0.6 x VA
2 = 0.7 x VA
3 = 0.8 x VA
4 = 0.83 x VA
5 = 0.91 x VA
- cirrus,adaptive-pwr-cfg : Configures how the power to the Headphone and Lineout
Amplifiers adapt to the output signal levels.
0 = Adapt to Volume Mode. Voltage level determined by the sum of the relevant volume settings.
1 = Fixed - Headphone and Line Amp supply = + or - VCP/2.
2 = Fixed - Headphone and Line Amp supply = + or - VCP.
3 = Adapted to Signal; Voltage level is dynamically determined by the output signal.
- cirrus,hpf-left-freq, hpf-right-freq : Sets the corner frequency (-3dB point) for the internal High-Pass
Filter.
0 = 1.8Hz
1 = 119Hz
2 = 236Hz
3 = 464Hz
Example:
codec: codec@4b {
compatible = "cirrus,cs42l56";
reg = <0x4b>;
gpio-reset = <&gpio 10 0>;
cirrus,chgfreq-divisor = <0x05>;
cirrus.ain1_ref_cfg;
cirrus,micbias-lvl = <5>;
VA-supply = <&reg_audio>;
};

View File

@ -7,10 +7,11 @@ codec/DSP interfaces.
Required properties: Required properties:
- compatible: Compatible list, contains "fsl,vf610-sai". - compatible: Compatible list, contains "fsl,vf610-sai" or "fsl,imx6sx-sai".
- reg: Offset and length of the register set for the device. - reg: Offset and length of the register set for the device.
- clocks: Must contain an entry for each entry in clock-names. - clocks: Must contain an entry for each entry in clock-names.
- clock-names : Must include the "sai" entry. - clock-names : Must include the "bus" for register access and "mclk1" "mclk2"
"mclk3" for bit clock and frame clock providing.
- dmas : Generic dma devicetree binding as described in - dmas : Generic dma devicetree binding as described in
Documentation/devicetree/bindings/dma/dma.txt. Documentation/devicetree/bindings/dma/dma.txt.
- dma-names : Two dmas have to be defined, "tx" and "rx". - dma-names : Two dmas have to be defined, "tx" and "rx".
@ -30,8 +31,10 @@ sai2: sai@40031000 {
reg = <0x40031000 0x1000>; reg = <0x40031000 0x1000>;
pinctrl-names = "default"; pinctrl-names = "default";
pinctrl-0 = <&pinctrl_sai2_1>; pinctrl-0 = <&pinctrl_sai2_1>;
clocks = <&clks VF610_CLK_SAI2>; clocks = <&clks VF610_CLK_PLATFORM_BUS>,
clock-names = "sai"; <&clks VF610_CLK_SAI2>,
<&clks 0>, <&clks 0>;
clock-names = "bus", "mclk1", "mclk2", "mclk3";
dma-names = "tx", "rx"; dma-names = "tx", "rx";
dmas = <&edma0 0 VF610_EDMA_MUXID0_SAI2_TX>, dmas = <&edma0 0 VF610_EDMA_MUXID0_SAI2_TX>,
<&edma0 0 VF610_EDMA_MUXID0_SAI2_RX>; <&edma0 0 VF610_EDMA_MUXID0_SAI2_RX>;

View File

@ -10,6 +10,12 @@ Required properties:
- interrupts : The CODEC's interrupt output. - interrupts : The CODEC's interrupt output.
Optional properties:
- clocks: The phandle of the master clock to the CODEC
- clock-names: Should be "mclk"
Pins on the device (for linking into audio routes): Pins on the device (for linking into audio routes):
* MIC1 * MIC1

View File

@ -0,0 +1,22 @@
MAX98095 audio CODEC
This device supports I2C only.
Required properties:
- compatible : "maxim,max98095".
- reg : The I2C address of the device.
Optional properties:
- clocks: The phandle of the master clock to the CODEC
- clock-names: Should be "mclk"
Example:
max98095: codec@11 {
compatible = "maxim,max98095";
reg = <0x11>;
};

View File

@ -0,0 +1,27 @@
* Nokia N900 audio setup
Required properties:
- compatible: Should contain "nokia,n900-audio"
- nokia,cpu-dai: phandle for the McBSP node
- nokia,audio-codec: phandles for the main TLV320AIC3X node and the
auxiliary TLV320AIC3X node (in this order)
- nokia,headphone-amplifier: phandle for the TPA6130A2 node
- tvout-selection-gpios: GPIO for tvout selection
- jack-detection-gpios: GPIO for jack detection
- eci-switch-gpios: GPIO for ECI (Enhancement Control Interface) switch
- speaker-amplifier-gpios: GPIO for speaker amplifier
Example:
sound {
compatible = "nokia,n900-audio";
nokia,cpu-dai = <&mcbsp2>;
nokia,audio-codec = <&tlv320aic3x>, <&tlv320aic3x_aux>;
nokia,headphone-amplifier = <&tpa6130a2>;
tvout-selection-gpios = <&gpio2 8 GPIO_ACTIVE_HIGH>; /* 40 */
jack-detection-gpios = <&gpio6 17 GPIO_ACTIVE_HIGH>; /* 177 */
eci-switch-gpios = <&gpio6 22 GPIO_ACTIVE_HIGH>; /* 182 */
speaker-amplifier-gpios = <&twl_gpio 7 GPIO_ACTIVE_HIGH>;
};

View File

@ -0,0 +1,28 @@
NVIDIA Tegra30 HDA controller
Required properties:
- compatible : "nvidia,tegra30-hda"
- reg : Should contain the HDA registers location and length.
- interrupts : The interrupt from the HDA controller.
- clocks : Must contain an entry for each required entry in clock-names.
See ../clocks/clock-bindings.txt for details.
- clock-names : Must include the following entries: hda, hdacodec_2x, hda2hdmi
- resets : Must contain an entry for each entry in reset-names.
See ../reset/reset.txt for details.
- reset-names : Must include the following entries: hda, hdacodec_2x, hda2hdmi
Example:
hda@0,70030000 {
compatible = "nvidia,tegra124-hda", "nvidia,tegra30-hda";
reg = <0x0 0x70030000 0x0 0x10000>;
interrupts = <GIC_SPI 81 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&tegra_car TEGRA124_CLK_HDA>,
<&tegra_car TEGRA124_CLK_HDA2HDMI>,
<&tegra_car TEGRA124_CLK_HDA2CODEC_2X>;
clock-names = "hda", "hda2hdmi", "hda2codec_2x";
resets = <&tegra_car 125>, /* hda */
<&tegra_car 128>; /* hda2hdmi */
<&tegra_car 111>, /* hda2codec_2x */
reset-names = "hda", "hda2hdmi", "hda2codec_2x";
};

View File

@ -20,6 +20,7 @@ Required properties:
SSI subnode properties: SSI subnode properties:
- interrupts : Should contain SSI interrupt for PIO transfer - interrupts : Should contain SSI interrupt for PIO transfer
- shared-pin : if shared clock pin - shared-pin : if shared clock pin
- pio-transfer : use PIO transfer mode
SRC subnode properties: SRC subnode properties:
no properties at this point no properties at this point

View File

@ -1,10 +1,10 @@
RT5640 audio CODEC RT5640/RT5639 audio CODEC
This device supports I2C only. This device supports I2C only.
Required properties: Required properties:
- compatible : "realtek,rt5640". - compatible : One of "realtek,rt5640" or "realtek,rt5639".
- reg : The I2C address of the device. - reg : The I2C address of the device.
@ -18,7 +18,7 @@ Optional properties:
- realtek,ldo1-en-gpios : The GPIO that controls the CODEC's LDO1_EN pin. - realtek,ldo1-en-gpios : The GPIO that controls the CODEC's LDO1_EN pin.
Pins on the device (for linking into audio routes): Pins on the device (for linking into audio routes) for RT5639/RT5640:
* DMIC1 * DMIC1
* DMIC2 * DMIC2
@ -31,13 +31,16 @@ Pins on the device (for linking into audio routes):
* HPOR * HPOR
* LOUTL * LOUTL
* LOUTR * LOUTR
* MONOP
* MONON
* SPOLP * SPOLP
* SPOLN * SPOLN
* SPORP * SPORP
* SPORN * SPORN
Additional pins on the device for RT5640:
* MONOP
* MONON
Example: Example:
rt5640 { rt5640 {

View File

@ -1,6 +1,6 @@
Simple-Card: Simple-Card:
Simple-Card specifies audio DAI connection of SoC <-> codec. Simple-Card specifies audio DAI connections of SoC <-> codec.
Required properties: Required properties:
@ -10,26 +10,54 @@ Optional properties:
- simple-audio-card,name : User specified audio sound card name, one string - simple-audio-card,name : User specified audio sound card name, one string
property. property.
- simple-audio-card,format : CPU/CODEC common audio format.
"i2s", "right_j", "left_j" , "dsp_a"
"dsp_b", "ac97", "pdm", "msb", "lsb"
- simple-audio-card,widgets : Please refer to widgets.txt. - simple-audio-card,widgets : Please refer to widgets.txt.
- simple-audio-card,routing : A list of the connections between audio components. - simple-audio-card,routing : A list of the connections between audio components.
Each entry is a pair of strings, the first being the Each entry is a pair of strings, the first being the
connection's sink, the second being the connection's connection's sink, the second being the connection's
source. source.
- dai-tdm-slot-num : Please refer to tdm-slot.txt. - simple-audio-card,mclk-fs : Multiplication factor between stream rate and codec
- dai-tdm-slot-width : Please refer to tdm-slot.txt. mclk.
Required subnodes: Optional subnodes:
- simple-audio-card,dai-link : container for the CPU and CODEC sub-nodes - simple-audio-card,dai-link : Container for dai-link level
This container may be omitted when the properties and the CPU and CODEC
card has only one DAI link. sub-nodes. This container may be
See the examples. omitted when the card has only one
DAI link. See the examples and the
section bellow.
- simple-audio-card,cpu : CPU sub-node Dai-link subnode properties and subnodes:
- simple-audio-card,codec : CODEC sub-node
If dai-link subnode is omitted and the subnode properties are directly
under "sound"-node the subnode property and subnode names have to be
prefixed with "simple-audio-card,"-prefix.
Required dai-link subnodes:
- cpu : CPU sub-node
- codec : CODEC sub-node
Optional dai-link subnode properties:
- format : CPU/CODEC common audio format.
"i2s", "right_j", "left_j" , "dsp_a"
"dsp_b", "ac97", "pdm", "msb", "lsb"
- frame-master : Indicates dai-link frame master.
phandle to a cpu or codec subnode.
- bitclock-master : Indicates dai-link bit clock master.
phandle to a cpu or codec subnode.
- bitclock-inversion : bool property. Add this if the
dai-link uses bit clock inversion.
- frame-inversion : bool property. Add this if the
dai-link uses frame clock inversion.
For backward compatibility the frame-master and bitclock-master
properties can be used as booleans in codec subnode to indicate if the
codec is the dai-link frame or bit clock master. In this case there
should be no dai-link node, the same properties should not be present
at sound-node level, and the bitclock-inversion and frame-inversion
properties should also be placed in the codec node if needed.
Required CPU/CODEC subnodes properties: Required CPU/CODEC subnodes properties:
@ -37,29 +65,21 @@ Required CPU/CODEC subnodes properties:
Optional CPU/CODEC subnodes properties: Optional CPU/CODEC subnodes properties:
- format : CPU/CODEC specific audio format if needed. - dai-tdm-slot-num : Please refer to tdm-slot.txt.
see simple-audio-card,format - dai-tdm-slot-width : Please refer to tdm-slot.txt.
- frame-master : bool property. add this if subnode is frame master
- bitclock-master : bool property. add this if subnode is bitclock master
- bitclock-inversion : bool property. add this if subnode has clock inversion
- frame-inversion : bool property. add this if subnode has frame inversion
- clocks / system-clock-frequency : specify subnode's clock if needed. - clocks / system-clock-frequency : specify subnode's clock if needed.
it can be specified via "clocks" if system has it can be specified via "clocks" if system has
clock node (= common clock), or "system-clock-frequency" clock node (= common clock), or "system-clock-frequency"
(if system doens't support common clock) (if system doens't support common clock)
Note:
* For 'format', 'frame-master', 'bitclock-master', 'bitclock-inversion' and
'frame-inversion', the simple card will use the settings of CODEC for both
CPU and CODEC sides as we need to keep the settings identical for both ends
of the link.
Example 1 - single DAI link: Example 1 - single DAI link:
sound { sound {
compatible = "simple-audio-card"; compatible = "simple-audio-card";
simple-audio-card,name = "VF610-Tower-Sound-Card"; simple-audio-card,name = "VF610-Tower-Sound-Card";
simple-audio-card,format = "left_j"; simple-audio-card,format = "left_j";
simple-audio-card,bitclock-master = <&dailink0_master>;
simple-audio-card,frame-master = <&dailink0_master>;
simple-audio-card,widgets = simple-audio-card,widgets =
"Microphone", "Microphone Jack", "Microphone", "Microphone Jack",
"Headphone", "Headphone Jack", "Headphone", "Headphone Jack",
@ -69,17 +89,12 @@ sound {
"Headphone Jack", "HP_OUT", "Headphone Jack", "HP_OUT",
"External Speaker", "LINE_OUT"; "External Speaker", "LINE_OUT";
dai-tdm-slot-num = <2>;
dai-tdm-slot-width = <8>;
simple-audio-card,cpu { simple-audio-card,cpu {
sound-dai = <&sh_fsi2 0>; sound-dai = <&sh_fsi2 0>;
}; };
simple-audio-card,codec { dailink0_master: simple-audio-card,codec {
sound-dai = <&ak4648>; sound-dai = <&ak4648>;
bitclock-master;
frame-master;
clocks = <&osc>; clocks = <&osc>;
}; };
}; };
@ -105,31 +120,31 @@ Example 2 - many DAI links:
sound { sound {
compatible = "simple-audio-card"; compatible = "simple-audio-card";
simple-audio-card,name = "Cubox Audio"; simple-audio-card,name = "Cubox Audio";
simple-audio-card,format = "i2s";
simple-audio-card,dai-link@0 { /* I2S - HDMI */ simple-audio-card,dai-link@0 { /* I2S - HDMI */
simple-audio-card,cpu { format = "i2s";
cpu {
sound-dai = <&audio1 0>; sound-dai = <&audio1 0>;
}; };
simple-audio-card,codec { codec {
sound-dai = <&tda998x 0>; sound-dai = <&tda998x 0>;
}; };
}; };
simple-audio-card,dai-link@1 { /* S/PDIF - HDMI */ simple-audio-card,dai-link@1 { /* S/PDIF - HDMI */
simple-audio-card,cpu { cpu {
sound-dai = <&audio1 1>; sound-dai = <&audio1 1>;
}; };
simple-audio-card,codec { codec {
sound-dai = <&tda998x 1>; sound-dai = <&tda998x 1>;
}; };
}; };
simple-audio-card,dai-link@2 { /* S/PDIF - S/PDIF */ simple-audio-card,dai-link@2 { /* S/PDIF - S/PDIF */
simple-audio-card,cpu { cpu {
sound-dai = <&audio1 1>; sound-dai = <&audio1 1>;
}; };
simple-audio-card,codec { codec {
sound-dai = <&spdif_codec>; sound-dai = <&spdif_codec>;
}; };
}; };

View File

@ -0,0 +1,17 @@
Audio Binding for Snow boards
Required properties:
- compatible : Can be one of the following,
"google,snow-audio-max98090" or
"google,snow-audio-max98095"
- samsung,i2s-controller: The phandle of the Samsung I2S controller
- samsung,audio-codec: The phandle of the audio codec
Example:
sound {
compatible = "google,snow-audio-max98095";
samsung,i2s-controller = <&i2s0>;
samsung,audio-codec = <&max98095>;
};

View File

@ -0,0 +1,131 @@
STA350 audio CODEC
The driver for this device only supports I2C.
Required properties:
- compatible: "st,sta350"
- reg: the I2C address of the device for I2C
- reset-gpios: a GPIO spec for the reset pin. If specified, it will be
deasserted before communication to the codec starts.
- power-down-gpios: a GPIO spec for the power down pin. If specified,
it will be deasserted before communication to the codec
starts.
- vdd-dig-supply: regulator spec, providing 3.3V
- vdd-pll-supply: regulator spec, providing 3.3V
- vcc-supply: regulator spec, providing 5V - 26V
Optional properties:
- st,output-conf: number, Selects the output configuration:
0: 2-channel (full-bridge) power, 2-channel data-out
1: 2 (half-bridge). 1 (full-bridge) on-board power
2: 2 Channel (Full-Bridge) Power, 1 Channel FFX
3: 1 Channel Mono-Parallel
If parameter is missing, mode 0 will be enabled.
This property has to be specified as '/bits/ 8' value.
- st,ch1-output-mapping: Channel 1 output mapping
- st,ch2-output-mapping: Channel 2 output mapping
- st,ch3-output-mapping: Channel 3 output mapping
0: Channel 1
1: Channel 2
2: Channel 3
If parameter is missing, channel 1 is choosen.
This properties have to be specified as '/bits/ 8' values.
- st,thermal-warning-recover:
If present, thermal warning recovery is enabled.
- st,thermal-warning-adjustment:
If present, thermal warning adjustment is enabled.
- st,fault-detect-recovery:
If present, then fault recovery will be enabled.
- st,ffx-power-output-mode: string
The FFX power output mode selects how the FFX output timing is
configured. Must be one of these values:
- "drop-compensation"
- "tapered-compensation"
- "full-power-mode"
- "variable-drop-compensation" (default)
- st,drop-compensation-ns: number
Only required for "st,ffx-power-output-mode" ==
"variable-drop-compensation".
Specifies the drop compensation in nanoseconds.
The value must be in the range of 0..300, and only
multiples of 20 are allowed. Default is 140ns.
- st,overcurrent-warning-adjustment:
If present, overcurrent warning adjustment is enabled.
- st,max-power-use-mpcc:
If present, then MPCC bits are used for MPC coefficients,
otherwise standard MPC coefficients are used.
- st,max-power-corr:
If present, power bridge correction for THD reduction near maximum
power output is enabled.
- st,am-reduction-mode:
If present, FFX mode runs in AM reduction mode, otherwise normal
FFX mode is used.
- st,odd-pwm-speed-mode:
If present, PWM speed mode run on odd speed mode (341.3 kHz) on all
channels. If not present, normal PWM spped mode (384 kHz) will be used.
- st,distortion-compensation:
If present, distortion compensation variable uses DCC coefficient.
If not present, preset DC coefficient is used.
- st,invalid-input-detect-mute:
If present, automatic invalid input detect mute is enabled.
- st,activate-mute-output:
If present, a mute output will be activated in ase the volume will
reach a value lower than -76 dBFS.
- st,bridge-immediate-off:
If present, the bridge will be switched off immediately after the
power-down-gpio goes low. Otherwise, the bridge will wait for 13
million clock cycles to pass before shutting down.
- st,noise-shape-dc-cut:
If present, the noise-shaping technique on the DC cutoff filter are
enabled.
- st,powerdown-master-volume:
If present, the power-down pin and I2C power-down functions will
act on the master volume. Otherwise, the functions will act on the
mute commands.
- st,powerdown-delay-divider:
If present, the bridge power-down time will be divided by the provided
value. If not specified, a divider of 1 will be used. Allowed values
are 1, 2, 4, 8, 16, 32, 64 and 128.
This property has to be specified as '/bits/ 8' value.
Example:
codec: sta350@38 {
compatible = "st,sta350";
reg = <0x1c>;
reset-gpios = <&gpio1 19 0>;
power-down-gpios = <&gpio1 16 0>;
st,output-conf = /bits/ 8 <0x3>; // set output to 2-channel
// (full-bridge) power,
// 2-channel data-out
st,ch1-output-mapping = /bits/ 8 <0>; // set channel 1 output ch 1
st,ch2-output-mapping = /bits/ 8 <0>; // set channel 2 output ch 1
st,ch3-output-mapping = /bits/ 8 <0>; // set channel 3 output ch 1
st,max-power-correction; // enables power bridge
// correction for THD reduction
// near maximum power output
st,invalid-input-detect-mute; // mute if no valid digital
// audio signal is provided.
};

View File

@ -8270,6 +8270,7 @@ L: alsa-devel@alsa-project.org (moderated for non-subscribers)
W: http://www.alsa-project.org/ W: http://www.alsa-project.org/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
T: git git://git.alsa-project.org/alsa-kernel.git T: git git://git.alsa-project.org/alsa-kernel.git
Q: http://patchwork.kernel.org/project/alsa-devel/list/
S: Maintained S: Maintained
F: Documentation/sound/ F: Documentation/sound/
F: include/sound/ F: include/sound/

View File

@ -22,8 +22,6 @@ enum jz4740_dma_request_type {
JZ4740_DMA_TYPE_UART_RECEIVE = 21, JZ4740_DMA_TYPE_UART_RECEIVE = 21,
JZ4740_DMA_TYPE_SPI_TRANSMIT = 22, JZ4740_DMA_TYPE_SPI_TRANSMIT = 22,
JZ4740_DMA_TYPE_SPI_RECEIVE = 23, JZ4740_DMA_TYPE_SPI_RECEIVE = 23,
JZ4740_DMA_TYPE_AIC_TRANSMIT = 24,
JZ4740_DMA_TYPE_AIC_RECEIVE = 25,
JZ4740_DMA_TYPE_MMC_TRANSMIT = 26, JZ4740_DMA_TYPE_MMC_TRANSMIT = 26,
JZ4740_DMA_TYPE_MMC_RECEIVE = 27, JZ4740_DMA_TYPE_MMC_RECEIVE = 27,
JZ4740_DMA_TYPE_TCU = 28, JZ4740_DMA_TYPE_TCU = 28,

View File

@ -425,6 +425,15 @@ static struct platform_device qi_lb60_audio_device = {
.id = -1, .id = -1,
}; };
static struct gpiod_lookup_table qi_lb60_audio_gpio_table = {
.dev_id = "qi-lb60-audio",
.table = {
GPIO_LOOKUP("Bank B", 29, "snd", 0),
GPIO_LOOKUP("Bank D", 4, "amp", 0),
{ },
},
};
static struct platform_device *jz_platform_devices[] __initdata = { static struct platform_device *jz_platform_devices[] __initdata = {
&jz4740_udc_device, &jz4740_udc_device,
&jz4740_udc_xceiv_device, &jz4740_udc_xceiv_device,
@ -461,6 +470,8 @@ static int __init qi_lb60_init_platform_devices(void)
jz4740_adc_device.dev.platform_data = &qi_lb60_battery_pdata; jz4740_adc_device.dev.platform_data = &qi_lb60_battery_pdata;
jz4740_mmc_device.dev.platform_data = &qi_lb60_mmc_pdata; jz4740_mmc_device.dev.platform_data = &qi_lb60_mmc_pdata;
gpiod_add_lookup_table(&qi_lb60_audio_gpio_table);
jz4740_serial_device_register(); jz4740_serial_device_register();
spi_register_board_info(qi_lb60_spi_board_info, spi_register_board_info(qi_lb60_spi_board_info,

View File

@ -118,7 +118,6 @@ int fw_card_add(struct fw_card *card,
u32 max_receive, u32 link_speed, u64 guid); u32 max_receive, u32 link_speed, u64 guid);
void fw_core_remove_card(struct fw_card *card); void fw_core_remove_card(struct fw_card *card);
int fw_compute_block_crc(__be32 *block); int fw_compute_block_crc(__be32 *block);
void fw_schedule_bus_reset(struct fw_card *card, bool delayed, bool short_reset);
void fw_schedule_bm_work(struct fw_card *card, unsigned long delay); void fw_schedule_bm_work(struct fw_card *card, unsigned long delay);
/* -cdev */ /* -cdev */

View File

@ -673,9 +673,13 @@ int mc13xxx_common_init(struct device *dev)
if (mc13xxx->flags & MC13XXX_USE_ADC) if (mc13xxx->flags & MC13XXX_USE_ADC)
mc13xxx_add_subdevice(mc13xxx, "%s-adc"); mc13xxx_add_subdevice(mc13xxx, "%s-adc");
if (mc13xxx->flags & MC13XXX_USE_CODEC) if (mc13xxx->flags & MC13XXX_USE_CODEC) {
mc13xxx_add_subdevice_pdata(mc13xxx, "%s-codec", if (pdata)
pdata->codec, sizeof(*pdata->codec)); mc13xxx_add_subdevice_pdata(mc13xxx, "%s-codec",
pdata->codec, sizeof(*pdata->codec));
else
mc13xxx_add_subdevice(mc13xxx, "%s-codec");
}
if (mc13xxx->flags & MC13XXX_USE_RTC) if (mc13xxx->flags & MC13XXX_USE_RTC)
mc13xxx_add_subdevice(mc13xxx, "%s-rtc"); mc13xxx_add_subdevice(mc13xxx, "%s-rtc");

View File

@ -367,6 +367,9 @@ static inline int fw_stream_packet_destination_id(int tag, int channel, int sy)
return tag << 14 | channel << 8 | sy; return tag << 14 | channel << 8 | sy;
} }
void fw_schedule_bus_reset(struct fw_card *card, bool delayed,
bool short_reset);
struct fw_descriptor { struct fw_descriptor {
struct list_head link; struct list_head link;
size_t length; size_t length;

View File

@ -0,0 +1,109 @@
/*
* Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961/ADAU1781/ADAU1781 codecs
*
* Copyright 2011-2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2 or later.
*/
#ifndef __LINUX_PLATFORM_DATA_ADAU17X1_H__
#define __LINUX_PLATFORM_DATA_ADAU17X1_H__
/**
* enum adau17x1_micbias_voltage - Microphone bias voltage
* @ADAU17X1_MICBIAS_0_90_AVDD: 0.9 * AVDD
* @ADAU17X1_MICBIAS_0_65_AVDD: 0.65 * AVDD
*/
enum adau17x1_micbias_voltage {
ADAU17X1_MICBIAS_0_90_AVDD = 0,
ADAU17X1_MICBIAS_0_65_AVDD = 1,
};
/**
* enum adau1761_digmic_jackdet_pin_mode - Configuration of the JACKDET/MICIN pin
* @ADAU1761_DIGMIC_JACKDET_PIN_MODE_NONE: Disable the pin
* @ADAU1761_DIGMIC_JACKDET_PIN_MODE_DIGMIC: Configure the pin for usage as
* digital microphone input.
* @ADAU1761_DIGMIC_JACKDET_PIN_MODE_JACKDETECT: Configure the pin for jack
* insertion detection.
*/
enum adau1761_digmic_jackdet_pin_mode {
ADAU1761_DIGMIC_JACKDET_PIN_MODE_NONE,
ADAU1761_DIGMIC_JACKDET_PIN_MODE_DIGMIC,
ADAU1761_DIGMIC_JACKDET_PIN_MODE_JACKDETECT,
};
/**
* adau1761_jackdetect_debounce_time - Jack insertion detection debounce time
* @ADAU1761_JACKDETECT_DEBOUNCE_5MS: 5 milliseconds
* @ADAU1761_JACKDETECT_DEBOUNCE_10MS: 10 milliseconds
* @ADAU1761_JACKDETECT_DEBOUNCE_20MS: 20 milliseconds
* @ADAU1761_JACKDETECT_DEBOUNCE_40MS: 40 milliseconds
*/
enum adau1761_jackdetect_debounce_time {
ADAU1761_JACKDETECT_DEBOUNCE_5MS = 0,
ADAU1761_JACKDETECT_DEBOUNCE_10MS = 1,
ADAU1761_JACKDETECT_DEBOUNCE_20MS = 2,
ADAU1761_JACKDETECT_DEBOUNCE_40MS = 3,
};
/**
* enum adau1761_output_mode - Output mode configuration
* @ADAU1761_OUTPUT_MODE_HEADPHONE: Headphone output
* @ADAU1761_OUTPUT_MODE_HEADPHONE_CAPLESS: Capless headphone output
* @ADAU1761_OUTPUT_MODE_LINE: Line output
*/
enum adau1761_output_mode {
ADAU1761_OUTPUT_MODE_HEADPHONE,
ADAU1761_OUTPUT_MODE_HEADPHONE_CAPLESS,
ADAU1761_OUTPUT_MODE_LINE,
};
/**
* struct adau1761_platform_data - ADAU1761 Codec driver platform data
* @input_differential: If true the input pins will be configured in
* differential mode.
* @lineout_mode: Output mode for the LOUT/ROUT pins
* @headphone_mode: Output mode for the LHP/RHP pins
* @digmic_jackdetect_pin_mode: JACKDET/MICIN pin configuration
* @jackdetect_debounce_time: Jack insertion detection debounce time.
* Note: This value will only be used, if the JACKDET/MICIN pin is configured
* for jack insertion detection.
* @jackdetect_active_low: If true the jack insertion detection is active low.
* Othwise it will be active high.
* @micbias_voltage: Microphone voltage bias
*/
struct adau1761_platform_data {
bool input_differential;
enum adau1761_output_mode lineout_mode;
enum adau1761_output_mode headphone_mode;
enum adau1761_digmic_jackdet_pin_mode digmic_jackdetect_pin_mode;
enum adau1761_jackdetect_debounce_time jackdetect_debounce_time;
bool jackdetect_active_low;
enum adau17x1_micbias_voltage micbias_voltage;
};
/**
* struct adau1781_platform_data - ADAU1781 Codec driver platform data
* @left_input_differential: If true configure the left input as
* differential input.
* @right_input_differential: If true configure the right input as differntial
* input.
* @use_dmic: If true configure the MIC pins as digital microphone pins instead
* of analog microphone pins.
* @micbias_voltage: Microphone voltage bias
*/
struct adau1781_platform_data {
bool left_input_differential;
bool right_input_differential;
bool use_dmic;
enum adau17x1_micbias_voltage micbias_voltage;
};
#endif

View File

@ -23,7 +23,6 @@
* @reset_pin: GPIO pin wired to the reset input on the external AC97 codec, * @reset_pin: GPIO pin wired to the reset input on the external AC97 codec,
* optional to use, set to -ENODEV if not in use. AC97 layer will * optional to use, set to -ENODEV if not in use. AC97 layer will
* try to do a software reset of the external codec anyway. * try to do a software reset of the external codec anyway.
* @flags: Flags for which directions should be enabled.
* *
* If the user do not want to use a DMA channel for playback or capture, i.e. * If the user do not want to use a DMA channel for playback or capture, i.e.
* only one feature is required on the board. The slave for playback or capture * only one feature is required on the board. The slave for playback or capture
@ -33,7 +32,6 @@
struct ac97c_platform_data { struct ac97c_platform_data {
struct dw_dma_slave rx_dws; struct dw_dma_slave rx_dws;
struct dw_dma_slave tx_dws; struct dw_dma_slave tx_dws;
unsigned int flags;
int reset_pin; int reset_pin;
}; };

View File

@ -282,13 +282,6 @@ int snd_card_new(struct device *parent, int idx, const char *xid,
struct module *module, int extra_size, struct module *module, int extra_size,
struct snd_card **card_ret); struct snd_card **card_ret);
static inline int __deprecated
snd_card_create(int idx, const char *id, struct module *module, int extra_size,
struct snd_card **ret)
{
return snd_card_new(NULL, idx, id, module, extra_size, ret);
}
int snd_card_disconnect(struct snd_card *card); int snd_card_disconnect(struct snd_card *card);
int snd_card_free(struct snd_card *card); int snd_card_free(struct snd_card *card);
int snd_card_free_when_closed(struct snd_card *card); int snd_card_free_when_closed(struct snd_card *card);

48
include/sound/cs42l56.h Normal file
View File

@ -0,0 +1,48 @@
/*
* linux/sound/cs42l56.h -- Platform data for CS42L56
*
* Copyright (c) 2014 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 __CS42L56_H
#define __CS42L56_H
struct cs42l56_platform_data {
/* GPIO for Reset */
unsigned int gpio_nreset;
/* MICBIAS Level. Check datasheet Pg48 */
unsigned int micbias_lvl;
/* Analog Input 1A Reference 0=Single 1=Pseudo-Differential */
unsigned int ain1a_ref_cfg;
/* Analog Input 2A Reference 0=Single 1=Pseudo-Differential */
unsigned int ain2a_ref_cfg;
/* Analog Input 1B Reference 0=Single 1=Pseudo-Differential */
unsigned int ain1b_ref_cfg;
/* Analog Input 2B Reference 0=Single 1=Pseudo-Differential */
unsigned int ain2b_ref_cfg;
/* Charge Pump Freq. Check datasheet Pg62 */
unsigned int chgfreq;
/* HighPass Filter Right Channel Corner Frequency */
unsigned int hpfb_freq;
/* HighPass Filter Left Channel Corner Frequency */
unsigned int hpfa_freq;
/* Adaptive Power Control for LO/HP */
unsigned int adaptive_pwr;
};
#endif /* __CS42L56_H */

30
include/sound/omap-pcm.h Normal file
View File

@ -0,0 +1,30 @@
/*
* omap-pcm.h - OMAP PCM driver
*
* Copyright (C) 2014 Texas Instruments, Inc.
*
* Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* 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.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
#ifndef __OMAP_PCM_H__
#define __OMAP_PCM_H__
#if IS_ENABLED(CONFIG_SND_OMAP_SOC)
int omap_pcm_platform_register(struct device *dev);
#else
static inline int omap_pcm_platform_register(struct device *dev)
{
return 0;
}
#endif /* CONFIG_SND_OMAP_SOC */
#endif /* __OMAP_PCM_H__ */

View File

@ -34,47 +34,39 @@
* B : SSI direction * B : SSI direction
*/ */
#define RSND_SSI_CLK_PIN_SHARE (1 << 31) #define RSND_SSI_CLK_PIN_SHARE (1 << 31)
#define RSND_SSI_PLAY (1 << 24)
#define RSND_SSI(_dma_id, _pio_irq, _flags) \ #define RSND_SSI(_dma_id, _pio_irq, _flags) \
{ .dma_id = _dma_id, .pio_irq = _pio_irq, .flags = _flags } { .dma_id = _dma_id, .pio_irq = _pio_irq, .flags = _flags }
#define RSND_SSI_SET(_dai_id, _dma_id, _pio_irq, _flags) \
{ .dai_id = _dai_id, .dma_id = _dma_id, .pio_irq = _pio_irq, .flags = _flags }
#define RSND_SSI_UNUSED \ #define RSND_SSI_UNUSED \
{ .dai_id = -1, .dma_id = -1, .pio_irq = -1, .flags = 0 } { .dma_id = -1, .pio_irq = -1, .flags = 0 }
struct rsnd_ssi_platform_info { struct rsnd_ssi_platform_info {
int dai_id; /* will be removed */
int dma_id; int dma_id;
int pio_irq; int pio_irq;
u32 flags; u32 flags;
}; };
#define RSND_SRC(rate, _dma_id) \
{ .convert_rate = rate, .dma_id = _dma_id, }
#define RSND_SRC_UNUSED \
{ .convert_rate = 0, .dma_id = -1, }
struct rsnd_src_platform_info {
u32 convert_rate; /* sampling rate convert */
int dma_id; /* for Gen2 SCU */
};
/* /*
* flags * flags
*/ */
#define RSND_SCU_USE_HPBIF (1 << 31) /* it needs RSND_SSI_DEPENDENT */ struct rsnd_dvc_platform_info {
#define RSND_SRC(rate, _dma_id) \
{ .flags = RSND_SCU_USE_HPBIF, .convert_rate = rate, .dma_id = _dma_id, }
#define RSND_SRC_SET(rate, _dma_id) \
{ .flags = RSND_SCU_USE_HPBIF, .convert_rate = rate, .dma_id = _dma_id, }
#define RSND_SRC_UNUSED \
{ .flags = 0, .convert_rate = 0, .dma_id = 0, }
#define rsnd_scu_platform_info rsnd_src_platform_info
#define src_info scu_info
#define src_info_nr scu_info_nr
struct rsnd_src_platform_info {
u32 flags; u32 flags;
u32 convert_rate; /* sampling rate convert */
int dma_id; /* for Gen2 SCU */
}; };
struct rsnd_dai_path_info { struct rsnd_dai_path_info {
struct rsnd_ssi_platform_info *ssi; struct rsnd_ssi_platform_info *ssi;
struct rsnd_src_platform_info *src; struct rsnd_src_platform_info *src;
struct rsnd_dvc_platform_info *dvc;
}; };
struct rsnd_dai_platform_info { struct rsnd_dai_platform_info {
@ -99,6 +91,8 @@ struct rcar_snd_info {
int ssi_info_nr; int ssi_info_nr;
struct rsnd_src_platform_info *src_info; struct rsnd_src_platform_info *src_info;
int src_info_nr; int src_info_nr;
struct rsnd_dvc_platform_info *dvc_info;
int dvc_info_nr;
struct rsnd_dai_platform_info *dai_info; struct rsnd_dai_platform_info *dai_info;
int dai_info_nr; int dai_info_nr;
int (*start)(int id); int (*start)(int id);

View File

@ -16,6 +16,10 @@ struct rt5640_platform_data {
bool in1_diff; bool in1_diff;
bool in2_diff; bool in2_diff;
bool dmic_en;
bool dmic1_data_pin; /* 0 = IN1P; 1 = GPIO3 */
bool dmic2_data_pin; /* 0 = IN1N; 1 = GPIO4 */
int ldo1_en; /* GPIO for LDO1_EN */ int ldo1_en; /* GPIO for LDO1_EN */
}; };

25
include/sound/rt5645.h Normal file
View File

@ -0,0 +1,25 @@
/*
* linux/sound/rt5645.h -- Platform data for RT5645
*
* Copyright 2013 Realtek Microelectronics
*
* 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 __LINUX_SND_RT5645_H
#define __LINUX_SND_RT5645_H
struct rt5645_platform_data {
/* IN2 can optionally be differential */
bool in2_diff;
bool dmic_en;
unsigned int dmic1_data_pin;
/* 0 = IN2N; 1 = GPIO5; 2 = GPIO11 */
unsigned int dmic2_data_pin;
/* 0 = IN2P; 1 = GPIO6; 2 = GPIO10; 3 = GPIO12 */
};
#endif

21
include/sound/rt5651.h Normal file
View File

@ -0,0 +1,21 @@
/*
* linux/sound/rt286.h -- Platform data for RT286
*
* Copyright 2013 Realtek Microelectronics
*
* 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 __LINUX_SND_RT5651_H
#define __LINUX_SND_RT5651_H
struct rt5651_platform_data {
/* IN2 can optionally be differential */
bool in2_diff;
bool dmic_en;
};
#endif

21
include/sound/rt5677.h Normal file
View File

@ -0,0 +1,21 @@
/*
* linux/sound/rt5677.h -- Platform data for RT5677
*
* Copyright 2013 Realtek Semiconductor Corp.
* Author: Oder Chiou <oder_chiou@realtek.com>
*
* 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 __LINUX_SND_RT5677_H
#define __LINUX_SND_RT5677_H
struct rt5677_platform_data {
/* IN1 IN2 can optionally be differential */
bool in1_diff;
bool in2_diff;
};
#endif

View File

@ -252,7 +252,6 @@ struct snd_soc_dai {
unsigned int symmetric_rates:1; unsigned int symmetric_rates:1;
unsigned int symmetric_channels:1; unsigned int symmetric_channels:1;
unsigned int symmetric_samplebits:1; unsigned int symmetric_samplebits:1;
struct snd_pcm_runtime *runtime;
unsigned int active; unsigned int active;
unsigned char probed:1; unsigned char probed:1;
@ -277,7 +276,6 @@ struct snd_soc_dai {
struct snd_soc_card *card; struct snd_soc_card *card;
struct list_head list; struct list_head list;
struct list_head card_list;
}; };
static inline void *snd_soc_dai_get_dma_data(const struct snd_soc_dai *dai, static inline void *snd_soc_dai_get_dma_data(const struct snd_soc_dai *dai,

View File

@ -107,10 +107,6 @@ struct device;
{ .id = snd_soc_dapm_mux, .name = wname, \ { .id = snd_soc_dapm_mux, .name = wname, \
SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
.kcontrol_news = wcontrols, .num_kcontrols = 1} .kcontrol_news = wcontrols, .num_kcontrols = 1}
#define SND_SOC_DAPM_VIRT_MUX(wname, wreg, wshift, winvert, wcontrols) \
SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols)
#define SND_SOC_DAPM_VALUE_MUX(wname, wreg, wshift, winvert, wcontrols) \
SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols)
/* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */ /* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */
#define SOC_PGA_ARRAY(wname, wreg, wshift, winvert,\ #define SOC_PGA_ARRAY(wname, wreg, wshift, winvert,\
@ -166,10 +162,6 @@ struct device;
SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
.kcontrol_news = wcontrols, .num_kcontrols = 1, \ .kcontrol_news = wcontrols, .num_kcontrols = 1, \
.event = wevent, .event_flags = wflags} .event = wevent, .event_flags = wflags}
#define SND_SOC_DAPM_VIRT_MUX_E(wname, wreg, wshift, winvert, wcontrols, \
wevent, wflags) \
SND_SOC_DAPM_MUX_E(wname, wreg, wshift, winvert, wcontrols, wevent, \
wflags)
/* additional sequencing control within an event type */ /* additional sequencing control within an event type */
#define SND_SOC_DAPM_PGA_S(wname, wsubseq, wreg, wshift, winvert, \ #define SND_SOC_DAPM_PGA_S(wname, wsubseq, wreg, wshift, winvert, \
@ -256,9 +248,8 @@ struct device;
/* generic widgets */ /* generic widgets */
#define SND_SOC_DAPM_REG(wid, wname, wreg, wshift, wmask, won_val, woff_val) \ #define SND_SOC_DAPM_REG(wid, wname, wreg, wshift, wmask, won_val, woff_val) \
{ .id = wid, .name = wname, .kcontrol_news = NULL, .num_kcontrols = 0, \ { .id = wid, .name = wname, .kcontrol_news = NULL, .num_kcontrols = 0, \
.reg = -((wreg) + 1), .shift = wshift, .mask = wmask, \ .reg = wreg, .shift = wshift, .mask = wmask, \
.on_val = won_val, .off_val = woff_val, .event = dapm_reg_event, \ .on_val = won_val, .off_val = woff_val, }
.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD}
#define SND_SOC_DAPM_SUPPLY(wname, wreg, wshift, winvert, wevent, wflags) \ #define SND_SOC_DAPM_SUPPLY(wname, wreg, wshift, winvert, wevent, wflags) \
{ .id = snd_soc_dapm_supply, .name = wname, \ { .id = snd_soc_dapm_supply, .name = wname, \
SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
@ -305,16 +296,12 @@ struct device;
.get = snd_soc_dapm_get_enum_double, \ .get = snd_soc_dapm_get_enum_double, \
.put = snd_soc_dapm_put_enum_double, \ .put = snd_soc_dapm_put_enum_double, \
.private_value = (unsigned long)&xenum } .private_value = (unsigned long)&xenum }
#define SOC_DAPM_ENUM_VIRT(xname, xenum) \
SOC_DAPM_ENUM(xname, xenum)
#define SOC_DAPM_ENUM_EXT(xname, xenum, xget, xput) \ #define SOC_DAPM_ENUM_EXT(xname, xenum, xget, xput) \
{ .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, \
.get = xget, \ .get = xget, \
.put = xput, \ .put = xput, \
.private_value = (unsigned long)&xenum } .private_value = (unsigned long)&xenum }
#define SOC_DAPM_VALUE_ENUM(xname, xenum) \
SOC_DAPM_ENUM(xname, xenum)
#define SOC_DAPM_PIN_SWITCH(xname) \ #define SOC_DAPM_PIN_SWITCH(xname) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname " Switch", \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname " Switch", \
.info = snd_soc_dapm_info_pin_switch, \ .info = snd_soc_dapm_info_pin_switch, \
@ -362,8 +349,6 @@ struct regulator;
struct snd_soc_dapm_widget_list; struct snd_soc_dapm_widget_list;
struct snd_soc_dapm_update; struct snd_soc_dapm_update;
int dapm_reg_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event);
int dapm_regulator_event(struct snd_soc_dapm_widget *w, int dapm_regulator_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event); struct snd_kcontrol *kcontrol, int event);
int dapm_clock_event(struct snd_soc_dapm_widget *w, int dapm_clock_event(struct snd_soc_dapm_widget *w,
@ -606,6 +591,7 @@ struct snd_soc_dapm_context {
enum snd_soc_dapm_type, int); enum snd_soc_dapm_type, int);
struct device *dev; /* from parent - for debug */ struct device *dev; /* from parent - for debug */
struct snd_soc_component *component; /* parent component */
struct snd_soc_codec *codec; /* parent codec */ struct snd_soc_codec *codec; /* parent codec */
struct snd_soc_platform *platform; /* parent platform */ struct snd_soc_platform *platform; /* parent platform */
struct snd_soc_card *card; /* parent card */ struct snd_soc_card *card; /* parent card */

View File

@ -196,8 +196,6 @@
.info = snd_soc_info_enum_double, \ .info = snd_soc_info_enum_double, \
.get = snd_soc_get_enum_double, .put = snd_soc_put_enum_double, \ .get = snd_soc_get_enum_double, .put = snd_soc_put_enum_double, \
.private_value = (unsigned long)&xenum } .private_value = (unsigned long)&xenum }
#define SOC_VALUE_ENUM(xname, xenum) \
SOC_ENUM(xname, xenum)
#define SOC_SINGLE_EXT(xname, xreg, xshift, xmax, xinvert,\ #define SOC_SINGLE_EXT(xname, xreg, xshift, xmax, xinvert,\
xhandler_get, xhandler_put) \ xhandler_get, xhandler_put) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
@ -266,6 +264,13 @@
{.base = xbase, .num_regs = xregs, \ {.base = xbase, .num_regs = xregs, \
.mask = xmask }) } .mask = xmask }) }
#define SND_SOC_BYTES_EXT(xname, xcount, xhandler_get, xhandler_put) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.info = snd_soc_bytes_info_ext, \
.get = xhandler_get, .put = xhandler_put, \
.private_value = (unsigned long)&(struct soc_bytes_ext) \
{.max = xcount} }
#define SOC_SINGLE_XR_SX(xname, xregbase, xregcount, xnbits, \ #define SOC_SINGLE_XR_SX(xname, xregbase, xregcount, xnbits, \
xmin, xmax, xinvert) \ xmin, xmax, xinvert) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
@ -377,6 +382,8 @@ int snd_soc_resume(struct device *dev);
int snd_soc_poweroff(struct device *dev); int snd_soc_poweroff(struct device *dev);
int snd_soc_register_platform(struct device *dev, int snd_soc_register_platform(struct device *dev,
const struct snd_soc_platform_driver *platform_drv); const struct snd_soc_platform_driver *platform_drv);
int devm_snd_soc_register_platform(struct device *dev,
const struct snd_soc_platform_driver *platform_drv);
void snd_soc_unregister_platform(struct device *dev); void snd_soc_unregister_platform(struct device *dev);
int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform, int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform,
const struct snd_soc_platform_driver *platform_drv); const struct snd_soc_platform_driver *platform_drv);
@ -393,14 +400,6 @@ int devm_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);
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,
unsigned int reg);
int snd_soc_codec_readable_register(struct snd_soc_codec *codec,
unsigned int reg);
int snd_soc_codec_writable_register(struct snd_soc_codec *codec,
unsigned int reg);
int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
struct regmap *regmap);
int snd_soc_cache_sync(struct snd_soc_codec *codec); int snd_soc_cache_sync(struct snd_soc_codec *codec);
int snd_soc_cache_init(struct snd_soc_codec *codec); int snd_soc_cache_init(struct snd_soc_codec *codec);
int snd_soc_cache_exit(struct snd_soc_codec *codec); int snd_soc_cache_exit(struct snd_soc_codec *codec);
@ -453,6 +452,9 @@ int snd_soc_jack_get_type(struct snd_soc_jack *jack, int micbias_voltage);
#ifdef CONFIG_GPIOLIB #ifdef CONFIG_GPIOLIB
int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
struct snd_soc_jack_gpio *gpios); struct snd_soc_jack_gpio *gpios);
int snd_soc_jack_add_gpiods(struct device *gpiod_dev,
struct snd_soc_jack *jack,
int count, struct snd_soc_jack_gpio *gpios);
void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count, void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count,
struct snd_soc_jack_gpio *gpios); struct snd_soc_jack_gpio *gpios);
#else #else
@ -462,6 +464,14 @@ static inline int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
return 0; return 0;
} }
static inline int snd_soc_jack_add_gpiods(struct device *gpiod_dev,
struct snd_soc_jack *jack,
int count,
struct snd_soc_jack_gpio *gpios)
{
return 0;
}
static inline void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count, static inline void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count,
struct snd_soc_jack_gpio *gpios) struct snd_soc_jack_gpio *gpios)
{ {
@ -469,12 +479,12 @@ static inline void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count,
#endif #endif
/* codec register bit access */ /* codec register bit access */
int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg, int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned int reg,
unsigned int mask, unsigned int value); unsigned int mask, unsigned int value);
int snd_soc_update_bits_locked(struct snd_soc_codec *codec, int snd_soc_update_bits_locked(struct snd_soc_codec *codec,
unsigned short reg, unsigned int mask, unsigned int reg, unsigned int mask,
unsigned int value); unsigned int value);
int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg, int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned int reg,
unsigned int mask, unsigned int value); unsigned int mask, unsigned int value);
int snd_soc_new_ac97_codec(struct snd_soc_codec *codec, int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
@ -540,6 +550,8 @@ int snd_soc_bytes_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol); struct snd_ctl_elem_value *ucontrol);
int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol); struct snd_ctl_elem_value *ucontrol);
int snd_soc_bytes_info_ext(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *ucontrol);
int snd_soc_info_xr_sx(struct snd_kcontrol *kcontrol, int snd_soc_info_xr_sx(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo); struct snd_ctl_elem_info *uinfo);
int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol, int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol,
@ -586,8 +598,12 @@ struct snd_soc_jack_zone {
/** /**
* struct snd_soc_jack_gpio - Describes a gpio pin for jack detection * struct snd_soc_jack_gpio - Describes a gpio pin for jack detection
* *
* @gpio: gpio number * @gpio: legacy gpio number
* @name: gpio name * @idx: gpio descriptor index within the function of the GPIO
* consumer device
* @gpiod_dev GPIO consumer device
* @name: gpio name. Also as connection ID for the GPIO consumer
* device function name lookup
* @report: value to report when jack detected * @report: value to report when jack detected
* @invert: report presence in low state * @invert: report presence in low state
* @debouce_time: debouce time in ms * @debouce_time: debouce time in ms
@ -598,6 +614,8 @@ struct snd_soc_jack_zone {
*/ */
struct snd_soc_jack_gpio { struct snd_soc_jack_gpio {
unsigned int gpio; unsigned int gpio;
unsigned int idx;
struct device *gpiod_dev;
const char *name; const char *name;
int report; int report;
int invert; int invert;
@ -606,6 +624,7 @@ struct snd_soc_jack_gpio {
struct snd_soc_jack *jack; struct snd_soc_jack *jack;
struct delayed_work work; struct delayed_work work;
struct gpio_desc *desc;
void *data; void *data;
int (*jack_status_check)(void *data); int (*jack_status_check)(void *data);
@ -668,6 +687,7 @@ struct snd_soc_component {
unsigned int active; unsigned int active;
unsigned int ignore_pmdown_time:1; /* pmdown_time is ignored at stop */ unsigned int ignore_pmdown_time:1; /* pmdown_time is ignored at stop */
unsigned int registered_as_component:1;
struct list_head list; struct list_head list;
@ -677,6 +697,14 @@ struct snd_soc_component {
const struct snd_soc_component_driver *driver; const struct snd_soc_component_driver *driver;
struct list_head dai_list; struct list_head dai_list;
int (*read)(struct snd_soc_component *, unsigned int, unsigned int *);
int (*write)(struct snd_soc_component *, unsigned int, unsigned int);
struct regmap *regmap;
int val_bytes;
struct mutex io_mutex;
}; };
/* SoC Audio Codec device */ /* SoC Audio Codec device */
@ -691,10 +719,6 @@ struct snd_soc_codec {
struct snd_soc_card *card; struct snd_soc_card *card;
struct list_head list; struct list_head list;
struct list_head card_list; struct list_head card_list;
int num_dai;
int (*volatile_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);
/* runtime */ /* runtime */
struct snd_ac97 *ac97; /* for ad-hoc ac97 devices */ struct snd_ac97 *ac97; /* for ad-hoc ac97 devices */
@ -704,18 +728,14 @@ struct snd_soc_codec {
unsigned int ac97_registered:1; /* Codec has been AC97 registered */ unsigned int ac97_registered:1; /* Codec has been AC97 registered */
unsigned int ac97_created:1; /* Codec has been created by SoC */ unsigned int ac97_created:1; /* Codec has been created by SoC */
unsigned int cache_init:1; /* codec cache has been initialized */ unsigned int cache_init:1; /* codec cache has been initialized */
unsigned int using_regmap:1; /* using regmap access */
u32 cache_only; /* Suppress writes to hardware */ u32 cache_only; /* Suppress writes to hardware */
u32 cache_sync; /* Cache needs to be synced to hardware */ u32 cache_sync; /* Cache needs to be synced to hardware */
/* codec IO */ /* codec IO */
void *control_data; /* codec control (i2c/3wire) data */ void *control_data; /* codec control (i2c/3wire) data */
hw_write_t hw_write; hw_write_t hw_write;
unsigned int (*read)(struct snd_soc_codec *, unsigned int);
int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);
void *reg_cache; void *reg_cache;
struct mutex cache_rw_mutex; struct mutex cache_rw_mutex;
int val_bytes;
/* component */ /* component */
struct snd_soc_component component; struct snd_soc_component component;
@ -754,13 +774,9 @@ struct snd_soc_codec_driver {
unsigned int freq_in, unsigned int freq_out); unsigned int freq_in, unsigned int freq_out);
/* codec IO */ /* codec IO */
struct regmap *(*get_regmap)(struct device *);
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 (*display_register)(struct snd_soc_codec *, char *,
size_t, unsigned int);
int (*volatile_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);
unsigned int reg_cache_size; unsigned int reg_cache_size;
short reg_cache_step; short reg_cache_step;
short reg_word_size; short reg_word_size;
@ -791,6 +807,7 @@ struct snd_soc_platform_driver {
int (*remove)(struct snd_soc_platform *); int (*remove)(struct snd_soc_platform *);
int (*suspend)(struct snd_soc_dai *dai); int (*suspend)(struct snd_soc_dai *dai);
int (*resume)(struct snd_soc_dai *dai); int (*resume)(struct snd_soc_dai *dai);
struct snd_soc_component_driver component_driver;
/* pcm creation and destruction */ /* pcm creation and destruction */
int (*pcm_new)(struct snd_soc_pcm_runtime *); int (*pcm_new)(struct snd_soc_pcm_runtime *);
@ -835,7 +852,6 @@ struct snd_soc_platform {
int id; int id;
struct device *dev; struct device *dev;
const struct snd_soc_platform_driver *driver; const struct snd_soc_platform_driver *driver;
struct mutex mutex;
unsigned int suspended:1; /* platform is suspended */ unsigned int suspended:1; /* platform is suspended */
unsigned int probed:1; unsigned int probed:1;
@ -844,6 +860,8 @@ struct snd_soc_platform {
struct list_head list; struct list_head list;
struct list_head card_list; struct list_head card_list;
struct snd_soc_component component;
struct snd_soc_dapm_context dapm; struct snd_soc_dapm_context dapm;
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
@ -931,7 +949,12 @@ struct snd_soc_dai_link {
}; };
struct snd_soc_codec_conf { struct snd_soc_codec_conf {
/*
* specify device either by device name, or by
* DT/OF node, but not both.
*/
const char *dev_name; const char *dev_name;
const struct device_node *of_node;
/* /*
* optional map of kcontrol, widget and path name prefixes that are * optional map of kcontrol, widget and path name prefixes that are
@ -942,7 +965,13 @@ struct snd_soc_codec_conf {
struct snd_soc_aux_dev { struct snd_soc_aux_dev {
const char *name; /* Codec name */ const char *name; /* Codec name */
const char *codec_name; /* for multi-codec */
/*
* specify multi-codec either by device name, or by
* DT/OF node, but not both.
*/
const char *codec_name;
const struct device_node *codec_of_node;
/* codec/machine specific init - e.g. add machine controls */ /* codec/machine specific init - e.g. add machine controls */
int (*init)(struct snd_soc_dapm_context *dapm); int (*init)(struct snd_soc_dapm_context *dapm);
@ -957,7 +986,6 @@ struct snd_soc_card {
struct snd_card *snd_card; struct snd_card *snd_card;
struct module *owner; struct module *owner;
struct list_head list;
struct mutex mutex; struct mutex mutex;
struct mutex dapm_mutex; struct mutex dapm_mutex;
@ -1020,7 +1048,6 @@ struct snd_soc_card {
/* lists of probed devices belonging to this card */ /* lists of probed devices belonging to this card */
struct list_head codec_dev_list; struct list_head codec_dev_list;
struct list_head platform_dev_list; struct list_head platform_dev_list;
struct list_head dai_dev_list;
struct list_head widgets; struct list_head widgets;
struct list_head paths; struct list_head paths;
@ -1090,6 +1117,10 @@ struct soc_bytes {
u32 mask; u32 mask;
}; };
struct soc_bytes_ext {
int max;
};
/* multi register control */ /* multi register control */
struct soc_mreg_control { struct soc_mreg_control {
long min, max; long min, max;
@ -1120,10 +1151,66 @@ static inline struct snd_soc_codec *snd_soc_component_to_codec(
return container_of(component, struct snd_soc_codec, component); return container_of(component, struct snd_soc_codec, component);
} }
/**
* snd_soc_component_to_platform() - Casts a component to the platform it is embedded in
* @component: The component to cast to a platform
*
* This function must only be used on components that are known to be platforms.
* Otherwise the behavior is undefined.
*/
static inline struct snd_soc_platform *snd_soc_component_to_platform(
struct snd_soc_component *component)
{
return container_of(component, struct snd_soc_platform, component);
}
/**
* snd_soc_dapm_to_codec() - Casts a DAPM context to the CODEC it is embedded in
* @dapm: The DAPM context to cast to the CODEC
*
* This function must only be used on DAPM contexts that are known to be part of
* a CODEC (e.g. in a CODEC driver). Otherwise the behavior is undefined.
*/
static inline struct snd_soc_codec *snd_soc_dapm_to_codec(
struct snd_soc_dapm_context *dapm)
{
return container_of(dapm, struct snd_soc_codec, dapm);
}
/**
* snd_soc_dapm_to_platform() - Casts a DAPM context to the platform it is
* embedded in
* @dapm: The DAPM context to cast to the platform.
*
* This function must only be used on DAPM contexts that are known to be part of
* a platform (e.g. in a platform driver). Otherwise the behavior is undefined.
*/
static inline struct snd_soc_platform *snd_soc_dapm_to_platform(
struct snd_soc_dapm_context *dapm)
{
return container_of(dapm, struct snd_soc_platform, dapm);
}
/* codec IO */ /* codec IO */
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, int snd_soc_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int reg, unsigned int val); unsigned int val);
/* component IO */
int snd_soc_component_read(struct snd_soc_component *component,
unsigned int reg, unsigned int *val);
int snd_soc_component_write(struct snd_soc_component *component,
unsigned int reg, unsigned int val);
int snd_soc_component_update_bits(struct snd_soc_component *component,
unsigned int reg, unsigned int mask, unsigned int val);
int snd_soc_component_update_bits_async(struct snd_soc_component *component,
unsigned int reg, unsigned int mask, unsigned int val);
void snd_soc_component_async_complete(struct snd_soc_component *component);
int snd_soc_component_test_bits(struct snd_soc_component *component,
unsigned int reg, unsigned int mask, unsigned int value);
int snd_soc_component_init_io(struct snd_soc_component *component,
struct regmap *regmap);
/* device driver data */ /* device driver data */
@ -1173,7 +1260,6 @@ static inline void *snd_soc_pcm_get_drvdata(struct snd_soc_pcm_runtime *rtd)
static inline void snd_soc_initialize_card_lists(struct snd_soc_card *card) static inline void snd_soc_initialize_card_lists(struct snd_soc_card *card)
{ {
INIT_LIST_HEAD(&card->dai_dev_list);
INIT_LIST_HEAD(&card->codec_dev_list); INIT_LIST_HEAD(&card->codec_dev_list);
INIT_LIST_HEAD(&card->platform_dev_list); INIT_LIST_HEAD(&card->platform_dev_list);
INIT_LIST_HEAD(&card->widgets); INIT_LIST_HEAD(&card->widgets);
@ -1228,6 +1314,50 @@ static inline bool snd_soc_codec_is_active(struct snd_soc_codec *codec)
return snd_soc_component_is_active(&codec->component); return snd_soc_component_is_active(&codec->component);
} }
/**
* snd_soc_kcontrol_component() - Returns the component that registered the
* control
* @kcontrol: The control for which to get the component
*
* Note: This function will work correctly if the control has been registered
* for a component. Either with snd_soc_add_codec_controls() or
* snd_soc_add_platform_controls() or via table based setup for either a
* CODEC, a platform or component driver. Otherwise the behavior is undefined.
*/
static inline struct snd_soc_component *snd_soc_kcontrol_component(
struct snd_kcontrol *kcontrol)
{
return snd_kcontrol_chip(kcontrol);
}
/**
* snd_soc_kcontrol_codec() - Returns the CODEC that registered the control
* @kcontrol: The control for which to get the CODEC
*
* Note: This function will only work correctly if the control has been
* registered with snd_soc_add_codec_controls() or via table based setup of
* snd_soc_codec_driver. Otherwise the behavior is undefined.
*/
static inline struct snd_soc_codec *snd_soc_kcontrol_codec(
struct snd_kcontrol *kcontrol)
{
return snd_soc_component_to_codec(snd_soc_kcontrol_component(kcontrol));
}
/**
* snd_soc_kcontrol_platform() - Returns the platform that registerd the control
* @kcontrol: The control for which to get the platform
*
* Note: This function will only work correctly if the control has been
* registered with snd_soc_add_platform_controls() or via table based setup of
* a snd_soc_platform_driver. Otherwise the behavior is undefined.
*/
static inline struct snd_soc_platform *snd_soc_kcontrol_platform(
struct snd_kcontrol *kcontrol)
{
return snd_soc_component_to_platform(snd_soc_kcontrol_component(kcontrol));
}
int snd_soc_util_init(void); int snd_soc_util_init(void);
void snd_soc_util_exit(void); void snd_soc_util_exit(void);
@ -1241,7 +1371,9 @@ int snd_soc_of_parse_tdm_slot(struct device_node *np,
int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, 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,
struct device_node **bitclkmaster,
struct device_node **framemaster);
int snd_soc_of_get_dai_name(struct device_node *of_node, int snd_soc_of_get_dai_name(struct device_node *of_node,
const char **dai_name); const char **dai_name);

57
include/sound/sta350.h Normal file
View File

@ -0,0 +1,57 @@
/*
* Platform data for ST STA350 ASoC codec driver.
*
* Copyright: 2014 Raumfeld GmbH
* Author: Sven Brandau <info@brandau.biz>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#ifndef __LINUX_SND__STA350_H
#define __LINUX_SND__STA350_H
#define STA350_OCFG_2CH 0
#define STA350_OCFG_2_1CH 1
#define STA350_OCFG_1CH 3
#define STA350_OM_CH1 0
#define STA350_OM_CH2 1
#define STA350_OM_CH3 2
#define STA350_THERMAL_ADJUSTMENT_ENABLE 1
#define STA350_THERMAL_RECOVERY_ENABLE 2
#define STA350_FAULT_DETECT_RECOVERY_BYPASS 1
#define STA350_FFX_PM_DROP_COMP 0
#define STA350_FFX_PM_TAPERED_COMP 1
#define STA350_FFX_PM_FULL_POWER 2
#define STA350_FFX_PM_VARIABLE_DROP_COMP 3
struct sta350_platform_data {
u8 output_conf;
u8 ch1_output_mapping;
u8 ch2_output_mapping;
u8 ch3_output_mapping;
u8 ffx_power_output_mode;
u8 drop_compensation_ns;
u8 powerdown_delay_divider;
unsigned int thermal_warning_recovery:1;
unsigned int thermal_warning_adjustment:1;
unsigned int fault_detect_recovery:1;
unsigned int oc_warning_adjustment:1;
unsigned int max_power_use_mpcc:1;
unsigned int max_power_correction:1;
unsigned int am_reduction_mode:1;
unsigned int odd_pwm_speed_mode:1;
unsigned int distortion_compensation:1;
unsigned int invalid_input_detect_mute:1;
unsigned int activate_mute_output:1;
unsigned int bridge_immediate_off:1;
unsigned int noise_shape_dc_cut:1;
unsigned int powerdown_master_vol:1;
};
#endif /* __LINUX_SND__STA350_H */

View File

@ -11,102 +11,10 @@
struct snd_soc_jack; struct snd_soc_jack;
struct snd_soc_codec; struct snd_soc_codec;
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; struct snd_soc_dapm_path;
/*
* Log register events
*/
DECLARE_EVENT_CLASS(snd_soc_reg,
TP_PROTO(struct snd_soc_codec *codec, unsigned int reg,
unsigned int val),
TP_ARGS(codec, reg, val),
TP_STRUCT__entry(
__string( name, codec->name )
__field( int, id )
__field( unsigned int, reg )
__field( unsigned int, val )
),
TP_fast_assign(
__assign_str(name, codec->name);
__entry->id = codec->id;
__entry->reg = reg;
__entry->val = val;
),
TP_printk("codec=%s.%d reg=%x val=%x", __get_str(name),
(int)__entry->id, (unsigned int)__entry->reg,
(unsigned int)__entry->val)
);
DEFINE_EVENT(snd_soc_reg, snd_soc_reg_write,
TP_PROTO(struct snd_soc_codec *codec, unsigned int reg,
unsigned int val),
TP_ARGS(codec, reg, val)
);
DEFINE_EVENT(snd_soc_reg, snd_soc_reg_read,
TP_PROTO(struct snd_soc_codec *codec, unsigned int reg,
unsigned int val),
TP_ARGS(codec, reg, val)
);
DECLARE_EVENT_CLASS(snd_soc_preg,
TP_PROTO(struct snd_soc_platform *platform, unsigned int reg,
unsigned int val),
TP_ARGS(platform, reg, val),
TP_STRUCT__entry(
__string( name, platform->name )
__field( int, id )
__field( unsigned int, reg )
__field( unsigned int, val )
),
TP_fast_assign(
__assign_str(name, platform->name);
__entry->id = platform->id;
__entry->reg = reg;
__entry->val = val;
),
TP_printk("platform=%s.%d reg=%x val=%x", __get_str(name),
(int)__entry->id, (unsigned int)__entry->reg,
(unsigned int)__entry->val)
);
DEFINE_EVENT(snd_soc_preg, snd_soc_preg_write,
TP_PROTO(struct snd_soc_platform *platform, unsigned int reg,
unsigned int val),
TP_ARGS(platform, reg, val)
);
DEFINE_EVENT(snd_soc_preg, snd_soc_preg_read,
TP_PROTO(struct snd_soc_platform *platform, unsigned int reg,
unsigned int val),
TP_ARGS(platform, reg, val)
);
DECLARE_EVENT_CLASS(snd_soc_card, DECLARE_EVENT_CLASS(snd_soc_card,
TP_PROTO(struct snd_soc_card *card, int val), TP_PROTO(struct snd_soc_card *card, int val),

View File

@ -94,9 +94,11 @@ enum {
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 */ SNDRV_HWDEP_IFACE_FW_DICE, /* TC DICE FireWire device */
SNDRV_HWDEP_IFACE_FW_FIREWORKS, /* Echo Audio Fireworks based device */
SNDRV_HWDEP_IFACE_FW_BEBOB, /* BridgeCo BeBoB based device */
/* Don't forget to change the following: */ /* Don't forget to change the following: */
SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_DICE SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_BEBOB
}; };
struct snd_hwdep_info { struct snd_hwdep_info {

View File

@ -2,11 +2,13 @@
#define _UAPI_SOUND_FIREWIRE_H_INCLUDED #define _UAPI_SOUND_FIREWIRE_H_INCLUDED
#include <linux/ioctl.h> #include <linux/ioctl.h>
#include <linux/types.h>
/* events can be read() from the hwdep device */ /* events can be read() from the hwdep device */
#define SNDRV_FIREWIRE_EVENT_LOCK_STATUS 0x000010cc #define SNDRV_FIREWIRE_EVENT_LOCK_STATUS 0x000010cc
#define SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION 0xd1ce004e #define SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION 0xd1ce004e
#define SNDRV_FIREWIRE_EVENT_EFW_RESPONSE 0x4e617475
struct snd_firewire_event_common { struct snd_firewire_event_common {
unsigned int type; /* SNDRV_FIREWIRE_EVENT_xxx */ unsigned int type; /* SNDRV_FIREWIRE_EVENT_xxx */
@ -22,10 +24,27 @@ struct snd_firewire_event_dice_notification {
unsigned int notification; /* DICE-specific bits */ unsigned int notification; /* DICE-specific bits */
}; };
#define SND_EFW_TRANSACTION_USER_SEQNUM_MAX ((__u32)((__u16)~0) - 1)
/* each field should be in big endian */
struct snd_efw_transaction {
__be32 length;
__be32 version;
__be32 seqnum;
__be32 category;
__be32 command;
__be32 status;
__be32 params[0];
};
struct snd_firewire_event_efw_response {
unsigned int type;
__be32 response[0]; /* some responses */
};
union snd_firewire_event { union snd_firewire_event {
struct snd_firewire_event_common common; struct snd_firewire_event_common common;
struct snd_firewire_event_lock_status lock_status; struct snd_firewire_event_lock_status lock_status;
struct snd_firewire_event_dice_notification dice_notification; struct snd_firewire_event_dice_notification dice_notification;
struct snd_firewire_event_efw_response efw_response;
}; };
@ -34,7 +53,9 @@ union snd_firewire_event {
#define SNDRV_FIREWIRE_IOCTL_UNLOCK _IO('H', 0xfa) #define SNDRV_FIREWIRE_IOCTL_UNLOCK _IO('H', 0xfa)
#define SNDRV_FIREWIRE_TYPE_DICE 1 #define SNDRV_FIREWIRE_TYPE_DICE 1
/* Fireworks, AV/C, RME, MOTU, ... */ #define SNDRV_FIREWIRE_TYPE_FIREWORKS 2
#define SNDRV_FIREWIRE_TYPE_BEBOB 3
/* AV/C, RME, MOTU, ... */
struct snd_firewire_get_info { struct snd_firewire_get_info {
unsigned int type; /* SNDRV_FIREWIRE_TYPE_xxx */ unsigned int type; /* SNDRV_FIREWIRE_TYPE_xxx */

View File

@ -241,7 +241,7 @@ static struct snd_kcontrol_new inputgain_control = {
static int onyx_snd_capture_source_info(struct snd_kcontrol *kcontrol, static int onyx_snd_capture_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo) struct snd_ctl_elem_info *uinfo)
{ {
static char *texts[] = { "Line-In", "Microphone" }; static const char * const texts[] = { "Line-In", "Microphone" };
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1; uinfo->count = 1;

View File

@ -14,6 +14,8 @@
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/dmaengine.h> #include <linux/dmaengine.h>
#include <mach/dma.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/pxa2xx-lib.h> #include <sound/pxa2xx-lib.h>
#include <sound/dmaengine_pcm.h> #include <sound/dmaengine_pcm.h>

View File

@ -9,12 +9,11 @@
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <mach/dma.h>
struct pxa2xx_runtime_data { struct pxa2xx_runtime_data {
int dma_ch; int dma_ch;
struct snd_dmaengine_dai_dma_data *params; struct snd_dmaengine_dai_dma_data *params;
pxa_dma_desc *dma_desc_array; struct pxa_dma_desc *dma_desc_array;
dma_addr_t dma_desc_array_phys; dma_addr_t dma_desc_array_phys;
}; };

View File

@ -1198,6 +1198,7 @@ static int atmel_ac97c_remove(struct platform_device *pdev)
} }
static struct platform_driver atmel_ac97c_driver = { static struct platform_driver atmel_ac97c_driver = {
.probe = atmel_ac97c_probe,
.remove = atmel_ac97c_remove, .remove = atmel_ac97c_remove,
.driver = { .driver = {
.name = "atmel_ac97c", .name = "atmel_ac97c",
@ -1205,19 +1206,7 @@ static struct platform_driver atmel_ac97c_driver = {
.pm = ATMEL_AC97C_PM_OPS, .pm = ATMEL_AC97C_PM_OPS,
}, },
}; };
module_platform_driver(atmel_ac97c_driver);
static int __init atmel_ac97c_init(void)
{
return platform_driver_probe(&atmel_ac97c_driver,
atmel_ac97c_probe);
}
module_init(atmel_ac97c_init);
static void __exit atmel_ac97c_exit(void)
{
platform_driver_unregister(&atmel_ac97c_driver);
}
module_exit(atmel_ac97c_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Driver for Atmel AC97 controller"); MODULE_DESCRIPTION("Driver for Atmel AC97 controller");

View File

@ -345,7 +345,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
snd_pcm_debug_name(substream, name, sizeof(name)); snd_pcm_debug_name(substream, name, sizeof(name));
xrun_log_show(substream); xrun_log_show(substream);
pcm_err(substream->pcm, pcm_err(substream->pcm,
"BUG: %s, pos = %ld, buffer size = %ld, period size = %ld\n", "XRUN: %s, pos = %ld, buffer size = %ld, period size = %ld\n",
name, pos, runtime->buffer_size, name, pos, runtime->buffer_size,
runtime->period_size); runtime->period_size);
} }

View File

@ -362,13 +362,13 @@ snd_seq_midisynth_register_port(struct snd_seq_device *dev)
if (! port->name[0]) { if (! port->name[0]) {
if (info->name[0]) { if (info->name[0]) {
if (ports > 1) if (ports > 1)
snprintf(port->name, sizeof(port->name), "%s-%d", info->name, p); snprintf(port->name, sizeof(port->name), "%s-%u", info->name, p);
else else
snprintf(port->name, sizeof(port->name), "%s", info->name); snprintf(port->name, sizeof(port->name), "%s", info->name);
} else { } else {
/* last resort */ /* last resort */
if (ports > 1) if (ports > 1)
sprintf(port->name, "MIDI %d-%d-%d", card->number, device, p); sprintf(port->name, "MIDI %d-%d-%u", card->number, device, p);
else else
sprintf(port->name, "MIDI %d-%d", card->number, device); sprintf(port->name, "MIDI %d-%d", card->number, device);
} }

View File

@ -9,12 +9,12 @@ if SND_FIREWIRE && FIREWIRE
config SND_FIREWIRE_LIB config SND_FIREWIRE_LIB
tristate tristate
depends on SND_PCM select SND_PCM
select SND_RAWMIDI
config SND_DICE config SND_DICE
tristate "DICE-based DACs (EXPERIMENTAL)" tristate "DICE-based DACs (EXPERIMENTAL)"
select SND_HWDEP select SND_HWDEP
select SND_PCM
select SND_FIREWIRE_LIB select SND_FIREWIRE_LIB
help help
Say Y here to include support for many DACs based on the DICE Say Y here to include support for many DACs based on the DICE
@ -28,7 +28,6 @@ config SND_DICE
config SND_FIREWIRE_SPEAKERS config SND_FIREWIRE_SPEAKERS
tristate "FireWire speakers" tristate "FireWire speakers"
select SND_PCM
select SND_FIREWIRE_LIB select SND_FIREWIRE_LIB
help help
Say Y here to include support for the Griffin FireWave Surround Say Y here to include support for the Griffin FireWave Surround
@ -39,7 +38,6 @@ config SND_FIREWIRE_SPEAKERS
config SND_ISIGHT config SND_ISIGHT
tristate "Apple iSight microphone" tristate "Apple iSight microphone"
select SND_PCM
select SND_FIREWIRE_LIB select SND_FIREWIRE_LIB
help help
Say Y here to include support for the front and rear microphones Say Y here to include support for the front and rear microphones
@ -50,8 +48,6 @@ config SND_ISIGHT
config SND_SCS1X config SND_SCS1X
tristate "Stanton Control System 1 MIDI" tristate "Stanton Control System 1 MIDI"
select SND_PCM
select SND_RAWMIDI
select SND_FIREWIRE_LIB select SND_FIREWIRE_LIB
help help
Say Y here to include support for the MIDI ports of the Stanton Say Y here to include support for the MIDI ports of the Stanton
@ -61,4 +57,59 @@ config SND_SCS1X
To compile this driver as a module, choose M here: the module To compile this driver as a module, choose M here: the module
will be called snd-scs1x. will be called snd-scs1x.
config SND_FIREWORKS
tristate "Echo Fireworks board module support"
select SND_FIREWIRE_LIB
select SND_HWDEP
help
Say Y here to include support for FireWire devices based
on Echo Digital Audio Fireworks board:
* Mackie Onyx 400F/1200F
* Echo AudioFire12/8(until 2009 July)
* Echo AudioFire2/4/Pre8/8(since 2009 July)
* Echo Fireworks 8/HDMI
* Gibson Robot Interface Pack/GoldTop
To compile this driver as a module, choose M here: the module
will be called snd-fireworks.
config SND_BEBOB
tristate "BridgeCo DM1000/DM1100/DM1500 with BeBoB firmware"
select SND_FIREWIRE_LIB
select SND_HWDEP
help
Say Y here to include support for FireWire devices based
on BridgeCo DM1000/DM1100/DM1500 with BeBoB firmware:
* Edirol FA-66/FA-101
* PreSonus FIREBOX/FIREPOD/FP10/Inspire1394
* BridgeCo RDAudio1/Audio5
* Mackie Onyx 1220/1620/1640 (Firewire I/O Card)
* Mackie d.2 (Firewire Option)
* Stanton FinalScratch 2 (ScratchAmp)
* Tascam IF-FW/DM
* Behringer XENIX UFX 1204/1604
* Behringer Digital Mixer X32 series (X-UF Card)
* Apogee Rosetta 200/400 (X-FireWire card)
* Apogee DA/AD/DD-16X (X-FireWire card)
* Apogee Ensemble
* ESI Quotafire610
* AcousticReality eARMasterOne
* CME MatrixKFW
* Phonic Helix Board 12 MkII/18 MkII/24 MkII
* Phonic Helix Board 12 Universal/18 Universal/24 Universal
* Lynx Aurora 8/16 (LT-FW)
* ICON FireXon
* PrismSound Orpheus/ADA-8XR
* TerraTec PHASE 24 FW/PHASE X24 FW/PHASE 88 Rack FW
* Terratec EWS MIC2/EWS MIC4
* Terratec Aureon 7.1 Firewire
* Yamaha GO44/GO46
* Focusrite Saffire/Saffire LE/SaffirePro10 IO/SaffirePro26 IO
* M-Audio Firewire410/AudioPhile/Solo
* M-Audio Ozonic/NRV10/ProfireLightBridge
* M-Audio Firewire 1814/ProjectMix IO
To compile this driver as a module, choose M here: the module
will be called snd-bebob.
endif # SND_FIREWIRE endif # SND_FIREWIRE

View File

@ -10,3 +10,5 @@ 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
obj-$(CONFIG_SND_FIREWORKS) += fireworks/
obj-$(CONFIG_SND_BEBOB) += bebob/

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@
#include "packets-buffer.h" #include "packets-buffer.h"
/** /**
* enum cip_out_flags - describes details of the streaming protocol * enum cip_flags - describes details of the streaming protocol
* @CIP_NONBLOCKING: In non-blocking mode, each packet contains * @CIP_NONBLOCKING: In non-blocking mode, each packet contains
* 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
@ -16,15 +16,30 @@
* @CIP_BLOCKING: In blocking mode, each packet contains either zero or * @CIP_BLOCKING: In blocking mode, each packet contains either zero or
* SYT_INTERVAL samples, with these two types alternating so that * SYT_INTERVAL samples, with these two types alternating so that
* the overall sample rate comes out right. * the overall sample rate comes out right.
* @CIP_HI_DUALWIRE: At rates above 96 kHz, pretend that the stream runs * @CIP_SYNC_TO_DEVICE: In sync to device mode, time stamp in out packets is
* at half the actual sample rate with twice the number of channels; * generated by in packets. Defaultly this driver generates timestamp.
* two samples of a channel are stored consecutively in the packet. * @CIP_EMPTY_WITH_TAG0: Only for in-stream. Empty in-packets have TAG0.
* Requires blocking mode and SYT_INTERVAL-aligned PCM buffer size. * @CIP_DBC_IS_END_EVENT: Only for in-stream. The value of dbc in an in-packet
* corresponds to the end of event in the packet. Out of IEC 61883.
* @CIP_WRONG_DBS: Only for in-stream. The value of dbs is wrong in in-packets.
* The value of data_block_quadlets is used instead of reported value.
* @SKIP_DBC_ZERO_CHECK: Only for in-stream. Packets with zero in dbc is
* skipped for detecting discontinuity.
* @CIP_SKIP_INIT_DBC_CHECK: Only for in-stream. The value of dbc in first
* packet is not continuous from an initial value.
* @CIP_EMPTY_HAS_WRONG_DBC: Only for in-stream. The value of dbc in empty
* packet is wrong but the others are correct.
*/ */
enum cip_out_flags { enum cip_flags {
CIP_NONBLOCKING = 0x00, CIP_NONBLOCKING = 0x00,
CIP_BLOCKING = 0x01, CIP_BLOCKING = 0x01,
CIP_HI_DUALWIRE = 0x02, CIP_SYNC_TO_DEVICE = 0x02,
CIP_EMPTY_WITH_TAG0 = 0x04,
CIP_DBC_IS_END_EVENT = 0x08,
CIP_WRONG_DBS = 0x10,
CIP_SKIP_DBC_ZERO_CHECK = 0x20,
CIP_SKIP_INIT_DBC_CHECK = 0x40,
CIP_EMPTY_HAS_WRONG_DBC = 0x80,
}; };
/** /**
@ -41,27 +56,55 @@ enum cip_sfc {
CIP_SFC_COUNT CIP_SFC_COUNT
}; };
#define AMDTP_IN_PCM_FORMAT_BITS SNDRV_PCM_FMTBIT_S32
#define AMDTP_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \ #define AMDTP_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \
SNDRV_PCM_FMTBIT_S32) SNDRV_PCM_FMTBIT_S32)
/*
* This module supports maximum 64 PCM channels for one PCM stream
* This is for our convenience.
*/
#define AMDTP_MAX_CHANNELS_FOR_PCM 64
/*
* AMDTP packet can include channels for MIDI conformant data.
* Each MIDI conformant data channel includes 8 MPX-MIDI data stream.
* Each MPX-MIDI data stream includes one data stream from/to MIDI ports.
*
* This module supports maximum 1 MIDI conformant data channels.
* Then this AMDTP packets can transfer maximum 8 MIDI data streams.
*/
#define AMDTP_MAX_CHANNELS_FOR_MIDI 1
struct fw_unit; struct fw_unit;
struct fw_iso_context; struct fw_iso_context;
struct snd_pcm_substream; struct snd_pcm_substream;
struct snd_pcm_runtime;
struct snd_rawmidi_substream;
struct amdtp_out_stream { enum amdtp_stream_direction {
AMDTP_OUT_STREAM = 0,
AMDTP_IN_STREAM
};
struct amdtp_stream {
struct fw_unit *unit; struct fw_unit *unit;
enum cip_out_flags flags; enum cip_flags flags;
enum amdtp_stream_direction direction;
struct fw_iso_context *context; struct fw_iso_context *context;
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;
void (*transfer_samples)(struct amdtp_out_stream *s, void (*transfer_samples)(struct amdtp_stream *s,
struct snd_pcm_substream *pcm, struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames); __be32 *buffer, unsigned int frames);
u8 pcm_positions[AMDTP_MAX_CHANNELS_FOR_PCM];
u8 midi_position;
unsigned int syt_interval; unsigned int syt_interval;
unsigned int transfer_delay; unsigned int transfer_delay;
@ -82,65 +125,148 @@ struct amdtp_out_stream {
unsigned int pcm_buffer_pointer; unsigned int pcm_buffer_pointer;
unsigned int pcm_period_pointer; unsigned int pcm_period_pointer;
bool pointer_flush; bool pointer_flush;
struct snd_rawmidi_substream *midi[AMDTP_MAX_CHANNELS_FOR_MIDI * 8];
/* quirk: fixed interval of dbc between previos/current packets. */
unsigned int tx_dbc_interval;
/* quirk: the first count of data blocks in an rx packet for MIDI */
unsigned int rx_blocks_for_midi;
bool callbacked;
wait_queue_head_t callback_wait;
struct amdtp_stream *sync_slave;
}; };
int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit, int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
enum cip_out_flags flags); enum amdtp_stream_direction dir,
void amdtp_out_stream_destroy(struct amdtp_out_stream *s); enum cip_flags flags);
void amdtp_stream_destroy(struct amdtp_stream *s);
void amdtp_out_stream_set_parameters(struct amdtp_out_stream *s, void amdtp_stream_set_parameters(struct amdtp_stream *s,
unsigned int rate, unsigned int rate,
unsigned int pcm_channels, unsigned int pcm_channels,
unsigned int midi_ports); unsigned int midi_ports);
unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s); unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s);
int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed); int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed);
void amdtp_out_stream_update(struct amdtp_out_stream *s); void amdtp_stream_update(struct amdtp_stream *s);
void amdtp_out_stream_stop(struct amdtp_out_stream *s); void amdtp_stream_stop(struct amdtp_stream *s);
void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s, int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
snd_pcm_format_t format); struct snd_pcm_runtime *runtime);
void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s); void amdtp_stream_set_pcm_format(struct amdtp_stream *s,
unsigned long amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s); snd_pcm_format_t format);
void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s); void amdtp_stream_pcm_prepare(struct amdtp_stream *s);
unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s);
void amdtp_stream_pcm_abort(struct amdtp_stream *s);
extern const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT]; extern const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT];
extern const unsigned int amdtp_rate_table[CIP_SFC_COUNT];
static inline bool amdtp_out_stream_running(struct amdtp_out_stream *s) /**
* amdtp_stream_running - check stream is running or not
* @s: the AMDTP stream
*
* If this function returns true, the stream is running.
*/
static inline bool amdtp_stream_running(struct amdtp_stream *s)
{ {
return !IS_ERR(s->context); return !IS_ERR(s->context);
} }
/** /**
* amdtp_out_streaming_error - check for streaming error * amdtp_streaming_error - check for streaming error
* @s: the AMDTP output stream * @s: the AMDTP stream
* *
* If this function returns true, the stream's packet queue has stopped due to * If this function returns true, the stream's packet queue has stopped due to
* an asynchronous error. * an asynchronous error.
*/ */
static inline bool amdtp_out_streaming_error(struct amdtp_out_stream *s) static inline bool amdtp_streaming_error(struct amdtp_stream *s)
{ {
return s->packet_index < 0; return s->packet_index < 0;
} }
/** /**
* amdtp_out_stream_pcm_trigger - start/stop playback from a PCM device * amdtp_stream_pcm_running - check PCM substream is running or not
* @s: the AMDTP output stream * @s: the AMDTP stream
*
* If this function returns true, PCM substream in the AMDTP stream is running.
*/
static inline bool amdtp_stream_pcm_running(struct amdtp_stream *s)
{
return !!s->pcm;
}
/**
* amdtp_stream_pcm_trigger - start/stop playback from a PCM device
* @s: the AMDTP stream
* @pcm: the PCM device to be started, or %NULL to stop the current device * @pcm: the PCM device to be started, or %NULL to stop the current device
* *
* Call this function on a running isochronous stream to enable the actual * Call this function on a running isochronous stream to enable the actual
* transmission of PCM data. This function should be called from the PCM * transmission of PCM data. This function should be called from the PCM
* device's .trigger callback. * device's .trigger callback.
*/ */
static inline void amdtp_out_stream_pcm_trigger(struct amdtp_out_stream *s, static inline void amdtp_stream_pcm_trigger(struct amdtp_stream *s,
struct snd_pcm_substream *pcm) struct snd_pcm_substream *pcm)
{ {
ACCESS_ONCE(s->pcm) = pcm; ACCESS_ONCE(s->pcm) = pcm;
} }
/**
* amdtp_stream_midi_trigger - start/stop playback/capture with a MIDI device
* @s: the AMDTP stream
* @port: index of MIDI port
* @midi: the MIDI device to be started, or %NULL to stop the current device
*
* Call this function on a running isochronous stream to enable the actual
* transmission of MIDI data. This function should be called from the MIDI
* device's .trigger callback.
*/
static inline void amdtp_stream_midi_trigger(struct amdtp_stream *s,
unsigned int port,
struct snd_rawmidi_substream *midi)
{
if (port < s->midi_ports)
ACCESS_ONCE(s->midi[port]) = midi;
}
static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc) static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc)
{ {
return sfc & 1; return sfc & 1;
} }
static inline void amdtp_stream_set_sync(enum cip_flags sync_mode,
struct amdtp_stream *master,
struct amdtp_stream *slave)
{
if (sync_mode == CIP_SYNC_TO_DEVICE) {
master->flags |= CIP_SYNC_TO_DEVICE;
slave->flags |= CIP_SYNC_TO_DEVICE;
master->sync_slave = slave;
} else {
master->flags &= ~CIP_SYNC_TO_DEVICE;
slave->flags &= ~CIP_SYNC_TO_DEVICE;
master->sync_slave = NULL;
}
slave->sync_slave = NULL;
}
/**
* amdtp_stream_wait_callback - sleep till callbacked or timeout
* @s: the AMDTP stream
* @timeout: msec till timeout
*
* If this function return false, the AMDTP stream should be stopped.
*/
static inline bool amdtp_stream_wait_callback(struct amdtp_stream *s,
unsigned int timeout)
{
return wait_event_timeout(s->callback_wait,
s->callbacked == true,
msecs_to_jiffies(timeout)) > 0;
}
#endif #endif

View File

@ -0,0 +1,4 @@
snd-bebob-objs := bebob_command.o bebob_stream.o bebob_proc.o bebob_midi.o \
bebob_pcm.o bebob_hwdep.o bebob_terratec.o bebob_yamaha.o \
bebob_focusrite.o bebob_maudio.o bebob.o
obj-m += snd-bebob.o

View File

@ -0,0 +1,471 @@
/*
* bebob.c - a part of driver for BeBoB based devices
*
* Copyright (c) 2013-2014 Takashi Sakamoto
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
/*
* BeBoB is 'BridgeCo enhanced Breakout Box'. This is installed to firewire
* devices with DM1000/DM1100/DM1500 chipset. It gives common way for host
* system to handle BeBoB based devices.
*/
#include "bebob.h"
MODULE_DESCRIPTION("BridgeCo BeBoB driver");
MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
MODULE_LICENSE("GPL v2");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "card index");
module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "enable BeBoB sound card");
static DEFINE_MUTEX(devices_mutex);
static DECLARE_BITMAP(devices_used, SNDRV_CARDS);
/* Offsets from information register. */
#define INFO_OFFSET_GUID 0x10
#define INFO_OFFSET_HW_MODEL_ID 0x18
#define INFO_OFFSET_HW_MODEL_REVISION 0x1c
#define VEN_EDIROL 0x000040ab
#define VEN_PRESONUS 0x00000a92
#define VEN_BRIDGECO 0x000007f5
#define VEN_MACKIE 0x0000000f
#define VEN_STANTON 0x00001260
#define VEN_TASCAM 0x0000022e
#define VEN_BEHRINGER 0x00001564
#define VEN_APOGEE 0x000003db
#define VEN_ESI 0x00000f1b
#define VEN_ACOUSTIC 0x00000002
#define VEN_CME 0x0000000a
#define VEN_PHONIC 0x00001496
#define VEN_LYNX 0x000019e5
#define VEN_ICON 0x00001a9e
#define VEN_PRISMSOUND 0x00001198
#define VEN_TERRATEC 0x00000aac
#define VEN_YAMAHA 0x0000a0de
#define VEN_FOCUSRITE 0x0000130e
#define VEN_MAUDIO1 0x00000d6c
#define VEN_MAUDIO2 0x000007f5
#define MODEL_FOCUSRITE_SAFFIRE_BOTH 0x00000000
#define MODEL_MAUDIO_AUDIOPHILE_BOTH 0x00010060
#define MODEL_MAUDIO_FW1814 0x00010071
#define MODEL_MAUDIO_PROJECTMIX 0x00010091
static int
name_device(struct snd_bebob *bebob, unsigned int vendor_id)
{
struct fw_device *fw_dev = fw_parent_device(bebob->unit);
char vendor[24] = {0};
char model[32] = {0};
u32 hw_id;
u32 data[2] = {0};
u32 revision;
int err;
/* get vendor name from root directory */
err = fw_csr_string(fw_dev->config_rom + 5, CSR_VENDOR,
vendor, sizeof(vendor));
if (err < 0)
goto end;
/* get model name from unit directory */
err = fw_csr_string(bebob->unit->directory, CSR_MODEL,
model, sizeof(model));
if (err < 0)
goto end;
/* get hardware id */
err = snd_bebob_read_quad(bebob->unit, INFO_OFFSET_HW_MODEL_ID,
&hw_id);
if (err < 0)
goto end;
/* get hardware revision */
err = snd_bebob_read_quad(bebob->unit, INFO_OFFSET_HW_MODEL_REVISION,
&revision);
if (err < 0)
goto end;
/* get GUID */
err = snd_bebob_read_block(bebob->unit, INFO_OFFSET_GUID,
data, sizeof(data));
if (err < 0)
goto end;
strcpy(bebob->card->driver, "BeBoB");
strcpy(bebob->card->shortname, model);
strcpy(bebob->card->mixername, model);
snprintf(bebob->card->longname, sizeof(bebob->card->longname),
"%s %s (id:%d, rev:%d), GUID %08x%08x at %s, S%d",
vendor, model, hw_id, revision,
data[0], data[1], dev_name(&bebob->unit->device),
100 << fw_dev->max_speed);
end:
return err;
}
static void
bebob_card_free(struct snd_card *card)
{
struct snd_bebob *bebob = card->private_data;
if (bebob->card_index >= 0) {
mutex_lock(&devices_mutex);
clear_bit(bebob->card_index, devices_used);
mutex_unlock(&devices_mutex);
}
mutex_destroy(&bebob->mutex);
}
static const struct snd_bebob_spec *
get_saffire_spec(struct fw_unit *unit)
{
char name[24] = {0};
if (fw_csr_string(unit->directory, CSR_MODEL, name, sizeof(name)) < 0)
return NULL;
if (strcmp(name, "SaffireLE") == 0)
return &saffire_le_spec;
else
return &saffire_spec;
}
static bool
check_audiophile_booted(struct fw_unit *unit)
{
char name[24] = {0};
if (fw_csr_string(unit->directory, CSR_MODEL, name, sizeof(name)) < 0)
return false;
return strncmp(name, "FW Audiophile Bootloader", 15) != 0;
}
static int
bebob_probe(struct fw_unit *unit,
const struct ieee1394_device_id *entry)
{
struct snd_card *card;
struct snd_bebob *bebob;
const struct snd_bebob_spec *spec;
unsigned int card_index;
int err;
mutex_lock(&devices_mutex);
for (card_index = 0; card_index < SNDRV_CARDS; card_index++) {
if (!test_bit(card_index, devices_used) && enable[card_index])
break;
}
if (card_index >= SNDRV_CARDS) {
err = -ENOENT;
goto end;
}
if ((entry->vendor_id == VEN_FOCUSRITE) &&
(entry->model_id == MODEL_FOCUSRITE_SAFFIRE_BOTH))
spec = get_saffire_spec(unit);
else if ((entry->vendor_id == VEN_MAUDIO1) &&
(entry->model_id == MODEL_MAUDIO_AUDIOPHILE_BOTH) &&
!check_audiophile_booted(unit))
spec = NULL;
else
spec = (const struct snd_bebob_spec *)entry->driver_data;
if (spec == NULL) {
if ((entry->vendor_id == VEN_MAUDIO1) ||
(entry->vendor_id == VEN_MAUDIO2))
err = snd_bebob_maudio_load_firmware(unit);
else
err = -ENOSYS;
goto end;
}
err = snd_card_new(&unit->device, index[card_index], id[card_index],
THIS_MODULE, sizeof(struct snd_bebob), &card);
if (err < 0)
goto end;
bebob = card->private_data;
bebob->card_index = card_index;
set_bit(card_index, devices_used);
card->private_free = bebob_card_free;
bebob->card = card;
bebob->unit = unit;
bebob->spec = spec;
mutex_init(&bebob->mutex);
spin_lock_init(&bebob->lock);
init_waitqueue_head(&bebob->hwdep_wait);
err = name_device(bebob, entry->vendor_id);
if (err < 0)
goto error;
if ((entry->vendor_id == VEN_MAUDIO1) &&
(entry->model_id == MODEL_MAUDIO_FW1814))
err = snd_bebob_maudio_special_discover(bebob, true);
else if ((entry->vendor_id == VEN_MAUDIO1) &&
(entry->model_id == MODEL_MAUDIO_PROJECTMIX))
err = snd_bebob_maudio_special_discover(bebob, false);
else
err = snd_bebob_stream_discover(bebob);
if (err < 0)
goto error;
snd_bebob_proc_init(bebob);
if ((bebob->midi_input_ports > 0) ||
(bebob->midi_output_ports > 0)) {
err = snd_bebob_create_midi_devices(bebob);
if (err < 0)
goto error;
}
err = snd_bebob_create_pcm_devices(bebob);
if (err < 0)
goto error;
err = snd_bebob_create_hwdep_device(bebob);
if (err < 0)
goto error;
err = snd_bebob_stream_init_duplex(bebob);
if (err < 0)
goto error;
if (!bebob->maudio_special_quirk) {
err = snd_card_register(card);
if (err < 0) {
snd_bebob_stream_destroy_duplex(bebob);
goto error;
}
} else {
/*
* This is a workaround. This bus reset seems to have an effect
* to make devices correctly handling transactions. Without
* this, the devices have gap_count mismatch. This causes much
* failure of transaction.
*
* Just after registration, user-land application receive
* signals from dbus and starts I/Os. To avoid I/Os till the
* future bus reset, registration is done in next update().
*/
bebob->deferred_registration = true;
fw_schedule_bus_reset(fw_parent_device(bebob->unit)->card,
false, true);
}
dev_set_drvdata(&unit->device, bebob);
end:
mutex_unlock(&devices_mutex);
return err;
error:
mutex_unlock(&devices_mutex);
snd_card_free(card);
return err;
}
static void
bebob_update(struct fw_unit *unit)
{
struct snd_bebob *bebob = dev_get_drvdata(&unit->device);
if (bebob == NULL)
return;
fcp_bus_reset(bebob->unit);
snd_bebob_stream_update_duplex(bebob);
if (bebob->deferred_registration) {
if (snd_card_register(bebob->card) < 0) {
snd_bebob_stream_destroy_duplex(bebob);
snd_card_free(bebob->card);
}
bebob->deferred_registration = false;
}
}
static void bebob_remove(struct fw_unit *unit)
{
struct snd_bebob *bebob = dev_get_drvdata(&unit->device);
if (bebob == NULL)
return;
kfree(bebob->maudio_special_quirk);
snd_bebob_stream_destroy_duplex(bebob);
snd_card_disconnect(bebob->card);
snd_card_free_when_closed(bebob->card);
}
static struct snd_bebob_rate_spec normal_rate_spec = {
.get = &snd_bebob_stream_get_rate,
.set = &snd_bebob_stream_set_rate
};
static const struct snd_bebob_spec spec_normal = {
.clock = NULL,
.rate = &normal_rate_spec,
.meter = NULL
};
static const struct ieee1394_device_id bebob_id_table[] = {
/* Edirol, FA-66 */
SND_BEBOB_DEV_ENTRY(VEN_EDIROL, 0x00010049, &spec_normal),
/* Edirol, FA-101 */
SND_BEBOB_DEV_ENTRY(VEN_EDIROL, 0x00010048, &spec_normal),
/* Presonus, FIREBOX */
SND_BEBOB_DEV_ENTRY(VEN_PRESONUS, 0x00010000, &spec_normal),
/* PreSonus, FIREPOD/FP10 */
SND_BEBOB_DEV_ENTRY(VEN_PRESONUS, 0x00010066, &spec_normal),
/* PreSonus, Inspire1394 */
SND_BEBOB_DEV_ENTRY(VEN_PRESONUS, 0x00010001, &spec_normal),
/* BridgeCo, RDAudio1 */
SND_BEBOB_DEV_ENTRY(VEN_BRIDGECO, 0x00010048, &spec_normal),
/* BridgeCo, Audio5 */
SND_BEBOB_DEV_ENTRY(VEN_BRIDGECO, 0x00010049, &spec_normal),
/* Mackie, Onyx 1220/1620/1640 (Firewire I/O Card) */
SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010065, &spec_normal),
/* Mackie, d.2 (Firewire Option) */
SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010067, &spec_normal),
/* Stanton, ScratchAmp */
SND_BEBOB_DEV_ENTRY(VEN_STANTON, 0x00000001, &spec_normal),
/* Tascam, IF-FW DM */
SND_BEBOB_DEV_ENTRY(VEN_TASCAM, 0x00010067, &spec_normal),
/* Behringer, XENIX UFX 1204 */
SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00001204, &spec_normal),
/* Behringer, XENIX UFX 1604 */
SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00001604, &spec_normal),
/* Behringer, Digital Mixer X32 series (X-UF Card) */
SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00000006, &spec_normal),
/* Apogee Electronics, Rosetta 200/400 (X-FireWire card) */
/* Apogee Electronics, DA/AD/DD-16X (X-FireWire card) */
SND_BEBOB_DEV_ENTRY(VEN_APOGEE, 0x00010048, &spec_normal),
/* Apogee Electronics, Ensemble */
SND_BEBOB_DEV_ENTRY(VEN_APOGEE, 0x00001eee, &spec_normal),
/* ESI, Quatafire610 */
SND_BEBOB_DEV_ENTRY(VEN_ESI, 0x00010064, &spec_normal),
/* AcousticReality, eARMasterOne */
SND_BEBOB_DEV_ENTRY(VEN_ACOUSTIC, 0x00000002, &spec_normal),
/* CME, MatrixKFW */
SND_BEBOB_DEV_ENTRY(VEN_CME, 0x00030000, &spec_normal),
/* Phonic, Helix Board 12 MkII */
SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00050000, &spec_normal),
/* Phonic, Helix Board 18 MkII */
SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00060000, &spec_normal),
/* Phonic, Helix Board 24 MkII */
SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00070000, &spec_normal),
/* Phonic, Helix Board 12 Universal/18 Universal/24 Universal */
SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00000000, &spec_normal),
/* Lynx, Aurora 8/16 (LT-FW) */
SND_BEBOB_DEV_ENTRY(VEN_LYNX, 0x00000001, &spec_normal),
/* ICON, FireXon */
SND_BEBOB_DEV_ENTRY(VEN_ICON, 0x00000001, &spec_normal),
/* PrismSound, Orpheus */
SND_BEBOB_DEV_ENTRY(VEN_PRISMSOUND, 0x00010048, &spec_normal),
/* PrismSound, ADA-8XR */
SND_BEBOB_DEV_ENTRY(VEN_PRISMSOUND, 0x0000ada8, &spec_normal),
/* TerraTec Electronic GmbH, PHASE 88 Rack FW */
SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000003, &phase88_rack_spec),
/* TerraTec Electronic GmbH, PHASE 24 FW */
SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000004, &phase24_series_spec),
/* TerraTec Electronic GmbH, Phase X24 FW */
SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000007, &phase24_series_spec),
/* TerraTec Electronic GmbH, EWS MIC2/MIC8 */
SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000005, &spec_normal),
/* Terratec Electronic GmbH, Aureon 7.1 Firewire */
SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000002, &spec_normal),
/* Yamaha, GO44 */
SND_BEBOB_DEV_ENTRY(VEN_YAMAHA, 0x0010000b, &yamaha_go_spec),
/* YAMAHA, GO46 */
SND_BEBOB_DEV_ENTRY(VEN_YAMAHA, 0x0010000c, &yamaha_go_spec),
/* Focusrite, SaffirePro 26 I/O */
SND_BEBOB_DEV_ENTRY(VEN_FOCUSRITE, 0x00000003, &saffirepro_26_spec),
/* Focusrite, SaffirePro 10 I/O */
SND_BEBOB_DEV_ENTRY(VEN_FOCUSRITE, 0x00000006, &saffirepro_10_spec),
/* Focusrite, Saffire(no label and LE) */
SND_BEBOB_DEV_ENTRY(VEN_FOCUSRITE, MODEL_FOCUSRITE_SAFFIRE_BOTH,
&saffire_spec),
/* M-Audio, Firewire 410 */
SND_BEBOB_DEV_ENTRY(VEN_MAUDIO2, 0x00010058, NULL), /* bootloader */
SND_BEBOB_DEV_ENTRY(VEN_MAUDIO2, 0x00010046, &maudio_fw410_spec),
/* M-Audio, Firewire Audiophile */
SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, MODEL_MAUDIO_AUDIOPHILE_BOTH,
&maudio_audiophile_spec),
/* M-Audio, Firewire Solo */
SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, 0x00010062, &maudio_solo_spec),
/* M-Audio, Ozonic */
SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, 0x0000000a, &maudio_ozonic_spec),
/* M-Audio NRV10 */
SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, 0x00010081, &maudio_nrv10_spec),
/* M-Audio, ProFireLightbridge */
SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, 0x000100a1, &spec_normal),
/* Firewire 1814 */
SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, 0x00010070, NULL), /* bootloader */
SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, MODEL_MAUDIO_FW1814,
&maudio_special_spec),
/* M-Audio ProjectMix */
SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, MODEL_MAUDIO_PROJECTMIX,
&maudio_special_spec),
/* IDs are unknown but able to be supported */
/* Apogee, Mini-ME Firewire */
/* Apogee, Mini-DAC Firewire */
/* Behringer, F-Control Audio 1616 */
/* Behringer, F-Control Audio 610 */
/* Cakawalk, Sonar Power Studio 66 */
/* CME, UF400e */
/* ESI, Quotafire XL */
/* Infrasonic, DewX */
/* Infrasonic, Windy6 */
/* Mackie, Digital X Bus x.200 */
/* Mackie, Digital X Bus x.400 */
/* Phonic, HB 12 */
/* Phonic, HB 24 */
/* Phonic, HB 18 */
/* Phonic, FireFly 202 */
/* Phonic, FireFly 302 */
/* Rolf Spuler, Firewire Guitar */
{}
};
MODULE_DEVICE_TABLE(ieee1394, bebob_id_table);
static struct fw_driver bebob_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "snd-bebob",
.bus = &fw_bus_type,
},
.probe = bebob_probe,
.update = bebob_update,
.remove = bebob_remove,
.id_table = bebob_id_table,
};
static int __init
snd_bebob_init(void)
{
return driver_register(&bebob_driver.driver);
}
static void __exit
snd_bebob_exit(void)
{
driver_unregister(&bebob_driver.driver);
}
module_init(snd_bebob_init);
module_exit(snd_bebob_exit);

View File

@ -0,0 +1,257 @@
/*
* bebob.h - a part of driver for BeBoB based devices
*
* Copyright (c) 2013-2014 Takashi Sakamoto
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#ifndef SOUND_BEBOB_H_INCLUDED
#define SOUND_BEBOB_H_INCLUDED
#include <linux/compat.h>
#include <linux/device.h>
#include <linux/firewire.h>
#include <linux/firewire-constants.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/info.h>
#include <sound/rawmidi.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/firewire.h>
#include <sound/hwdep.h>
#include "../lib.h"
#include "../fcp.h"
#include "../packets-buffer.h"
#include "../iso-resources.h"
#include "../amdtp.h"
#include "../cmp.h"
/* basic register addresses on DM1000/DM1100/DM1500 */
#define BEBOB_ADDR_REG_INFO 0xffffc8020000ULL
#define BEBOB_ADDR_REG_REQ 0xffffc8021000ULL
struct snd_bebob;
#define SND_BEBOB_STRM_FMT_ENTRIES 7
struct snd_bebob_stream_formation {
unsigned int pcm;
unsigned int midi;
};
/* this is a lookup table for index of stream formations */
extern const unsigned int snd_bebob_rate_table[SND_BEBOB_STRM_FMT_ENTRIES];
/* device specific operations */
#define SND_BEBOB_CLOCK_INTERNAL "Internal"
struct snd_bebob_clock_spec {
unsigned int num;
char *const *labels;
int (*get)(struct snd_bebob *bebob, unsigned int *id);
};
struct snd_bebob_rate_spec {
int (*get)(struct snd_bebob *bebob, unsigned int *rate);
int (*set)(struct snd_bebob *bebob, unsigned int rate);
};
struct snd_bebob_meter_spec {
unsigned int num;
char *const *labels;
int (*get)(struct snd_bebob *bebob, u32 *target, unsigned int size);
};
struct snd_bebob_spec {
struct snd_bebob_clock_spec *clock;
struct snd_bebob_rate_spec *rate;
struct snd_bebob_meter_spec *meter;
};
struct snd_bebob {
struct snd_card *card;
struct fw_unit *unit;
int card_index;
struct mutex mutex;
spinlock_t lock;
const struct snd_bebob_spec *spec;
unsigned int midi_input_ports;
unsigned int midi_output_ports;
/* for bus reset quirk */
struct completion bus_reset;
bool connected;
struct amdtp_stream *master;
struct amdtp_stream tx_stream;
struct amdtp_stream rx_stream;
struct cmp_connection out_conn;
struct cmp_connection in_conn;
atomic_t capture_substreams;
atomic_t playback_substreams;
struct snd_bebob_stream_formation
tx_stream_formations[SND_BEBOB_STRM_FMT_ENTRIES];
struct snd_bebob_stream_formation
rx_stream_formations[SND_BEBOB_STRM_FMT_ENTRIES];
int sync_input_plug;
/* for uapi */
int dev_lock_count;
bool dev_lock_changed;
wait_queue_head_t hwdep_wait;
/* for M-Audio special devices */
void *maudio_special_quirk;
bool deferred_registration;
};
static inline int
snd_bebob_read_block(struct fw_unit *unit, u64 addr, void *buf, int size)
{
return snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST,
BEBOB_ADDR_REG_INFO + addr,
buf, size, 0);
}
static inline int
snd_bebob_read_quad(struct fw_unit *unit, u64 addr, u32 *buf)
{
return snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
BEBOB_ADDR_REG_INFO + addr,
(void *)buf, sizeof(u32), 0);
}
/* AV/C Audio Subunit Specification 1.0 (Oct 2000, 1394TA) */
int avc_audio_set_selector(struct fw_unit *unit, unsigned int subunit_id,
unsigned int fb_id, unsigned int num);
int avc_audio_get_selector(struct fw_unit *unit, unsigned int subunit_id,
unsigned int fb_id, unsigned int *num);
/*
* AVC command extensions, AV/C Unit and Subunit, Revision 17
* (Nov 2003, BridgeCo)
*/
#define AVC_BRIDGECO_ADDR_BYTES 6
enum avc_bridgeco_plug_dir {
AVC_BRIDGECO_PLUG_DIR_IN = 0x00,
AVC_BRIDGECO_PLUG_DIR_OUT = 0x01
};
enum avc_bridgeco_plug_mode {
AVC_BRIDGECO_PLUG_MODE_UNIT = 0x00,
AVC_BRIDGECO_PLUG_MODE_SUBUNIT = 0x01,
AVC_BRIDGECO_PLUG_MODE_FUNCTION_BLOCK = 0x02
};
enum avc_bridgeco_plug_unit {
AVC_BRIDGECO_PLUG_UNIT_ISOC = 0x00,
AVC_BRIDGECO_PLUG_UNIT_EXT = 0x01,
AVC_BRIDGECO_PLUG_UNIT_ASYNC = 0x02
};
enum avc_bridgeco_plug_type {
AVC_BRIDGECO_PLUG_TYPE_ISOC = 0x00,
AVC_BRIDGECO_PLUG_TYPE_ASYNC = 0x01,
AVC_BRIDGECO_PLUG_TYPE_MIDI = 0x02,
AVC_BRIDGECO_PLUG_TYPE_SYNC = 0x03,
AVC_BRIDGECO_PLUG_TYPE_ANA = 0x04,
AVC_BRIDGECO_PLUG_TYPE_DIG = 0x05
};
static inline void
avc_bridgeco_fill_unit_addr(u8 buf[AVC_BRIDGECO_ADDR_BYTES],
enum avc_bridgeco_plug_dir dir,
enum avc_bridgeco_plug_unit unit,
unsigned int pid)
{
buf[0] = 0xff; /* Unit */
buf[1] = dir;
buf[2] = AVC_BRIDGECO_PLUG_MODE_UNIT;
buf[3] = unit;
buf[4] = 0xff & pid;
buf[5] = 0xff; /* reserved */
}
static inline void
avc_bridgeco_fill_msu_addr(u8 buf[AVC_BRIDGECO_ADDR_BYTES],
enum avc_bridgeco_plug_dir dir,
unsigned int pid)
{
buf[0] = 0x60; /* Music subunit */
buf[1] = dir;
buf[2] = AVC_BRIDGECO_PLUG_MODE_SUBUNIT;
buf[3] = 0xff & pid;
buf[4] = 0xff; /* reserved */
buf[5] = 0xff; /* reserved */
}
int avc_bridgeco_get_plug_ch_pos(struct fw_unit *unit,
u8 addr[AVC_BRIDGECO_ADDR_BYTES],
u8 *buf, unsigned int len);
int avc_bridgeco_get_plug_type(struct fw_unit *unit,
u8 addr[AVC_BRIDGECO_ADDR_BYTES],
enum avc_bridgeco_plug_type *type);
int avc_bridgeco_get_plug_section_type(struct fw_unit *unit,
u8 addr[AVC_BRIDGECO_ADDR_BYTES],
unsigned int id, u8 *type);
int avc_bridgeco_get_plug_input(struct fw_unit *unit,
u8 addr[AVC_BRIDGECO_ADDR_BYTES],
u8 input[7]);
int avc_bridgeco_get_plug_strm_fmt(struct fw_unit *unit,
u8 addr[AVC_BRIDGECO_ADDR_BYTES], u8 *buf,
unsigned int *len, unsigned int eid);
/* for AMDTP streaming */
int snd_bebob_stream_get_rate(struct snd_bebob *bebob, unsigned int *rate);
int snd_bebob_stream_set_rate(struct snd_bebob *bebob, unsigned int rate);
int snd_bebob_stream_check_internal_clock(struct snd_bebob *bebob,
bool *internal);
int snd_bebob_stream_discover(struct snd_bebob *bebob);
int snd_bebob_stream_map(struct snd_bebob *bebob,
struct amdtp_stream *stream);
int snd_bebob_stream_init_duplex(struct snd_bebob *bebob);
int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate);
void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob);
void snd_bebob_stream_update_duplex(struct snd_bebob *bebob);
void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob);
void snd_bebob_stream_lock_changed(struct snd_bebob *bebob);
int snd_bebob_stream_lock_try(struct snd_bebob *bebob);
void snd_bebob_stream_lock_release(struct snd_bebob *bebob);
void snd_bebob_proc_init(struct snd_bebob *bebob);
int snd_bebob_create_midi_devices(struct snd_bebob *bebob);
int snd_bebob_create_pcm_devices(struct snd_bebob *bebob);
int snd_bebob_create_hwdep_device(struct snd_bebob *bebob);
/* model specific operations */
extern struct snd_bebob_spec phase88_rack_spec;
extern struct snd_bebob_spec phase24_series_spec;
extern struct snd_bebob_spec yamaha_go_spec;
extern struct snd_bebob_spec saffirepro_26_spec;
extern struct snd_bebob_spec saffirepro_10_spec;
extern struct snd_bebob_spec saffire_le_spec;
extern struct snd_bebob_spec saffire_spec;
extern struct snd_bebob_spec maudio_fw410_spec;
extern struct snd_bebob_spec maudio_audiophile_spec;
extern struct snd_bebob_spec maudio_solo_spec;
extern struct snd_bebob_spec maudio_ozonic_spec;
extern struct snd_bebob_spec maudio_nrv10_spec;
extern struct snd_bebob_spec maudio_special_spec;
int snd_bebob_maudio_special_discover(struct snd_bebob *bebob, bool is1814);
int snd_bebob_maudio_load_firmware(struct fw_unit *unit);
#define SND_BEBOB_DEV_ENTRY(vendor, model, data) \
{ \
.match_flags = IEEE1394_MATCH_VENDOR_ID | \
IEEE1394_MATCH_MODEL_ID, \
.vendor_id = vendor, \
.model_id = model, \
.driver_data = (kernel_ulong_t)data \
}
#endif

View File

@ -0,0 +1,282 @@
/*
* bebob_command.c - driver for BeBoB based devices
*
* Copyright (c) 2013-2014 Takashi Sakamoto
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#include "./bebob.h"
int avc_audio_set_selector(struct fw_unit *unit, unsigned int subunit_id,
unsigned int fb_id, unsigned int num)
{
u8 *buf;
int err;
buf = kzalloc(12, GFP_KERNEL);
if (buf == NULL)
return -ENOMEM;
buf[0] = 0x00; /* AV/C CONTROL */
buf[1] = 0x08 | (0x07 & subunit_id); /* AUDIO SUBUNIT ID */
buf[2] = 0xb8; /* FUNCTION BLOCK */
buf[3] = 0x80; /* type is 'selector'*/
buf[4] = 0xff & fb_id; /* function block id */
buf[5] = 0x10; /* control attribute is CURRENT */
buf[6] = 0x02; /* selector length is 2 */
buf[7] = 0xff & num; /* input function block plug number */
buf[8] = 0x01; /* control selector is SELECTOR_CONTROL */
err = fcp_avc_transaction(unit, buf, 12, buf, 12,
BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
BIT(6) | BIT(7) | BIT(8));
if (err > 0 && err < 9)
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
else if (buf[0] == 0x0a) /* REJECTED */
err = -EINVAL;
else if (err > 0)
err = 0;
kfree(buf);
return err;
}
int avc_audio_get_selector(struct fw_unit *unit, unsigned int subunit_id,
unsigned int fb_id, unsigned int *num)
{
u8 *buf;
int err;
buf = kzalloc(12, GFP_KERNEL);
if (buf == NULL)
return -ENOMEM;
buf[0] = 0x01; /* AV/C STATUS */
buf[1] = 0x08 | (0x07 & subunit_id); /* AUDIO SUBUNIT ID */
buf[2] = 0xb8; /* FUNCTION BLOCK */
buf[3] = 0x80; /* type is 'selector'*/
buf[4] = 0xff & fb_id; /* function block id */
buf[5] = 0x10; /* control attribute is CURRENT */
buf[6] = 0x02; /* selector length is 2 */
buf[7] = 0xff; /* input function block plug number */
buf[8] = 0x01; /* control selector is SELECTOR_CONTROL */
err = fcp_avc_transaction(unit, buf, 12, buf, 12,
BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
BIT(6) | BIT(8));
if (err > 0 && err < 9)
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
else if (buf[0] == 0x0a) /* REJECTED */
err = -EINVAL;
else if (buf[0] == 0x0b) /* IN TRANSITION */
err = -EAGAIN;
if (err < 0)
goto end;
*num = buf[7];
err = 0;
end:
kfree(buf);
return err;
}
static inline void
avc_bridgeco_fill_extension_addr(u8 *buf, u8 *addr)
{
buf[1] = addr[0];
memcpy(buf + 4, addr + 1, 5);
}
static inline void
avc_bridgeco_fill_plug_info_extension_command(u8 *buf, u8 *addr,
unsigned int itype)
{
buf[0] = 0x01; /* AV/C STATUS */
buf[2] = 0x02; /* AV/C GENERAL PLUG INFO */
buf[3] = 0xc0; /* BridgeCo extension */
avc_bridgeco_fill_extension_addr(buf, addr);
buf[9] = itype; /* info type */
}
int avc_bridgeco_get_plug_type(struct fw_unit *unit,
u8 addr[AVC_BRIDGECO_ADDR_BYTES],
enum avc_bridgeco_plug_type *type)
{
u8 *buf;
int err;
buf = kzalloc(12, GFP_KERNEL);
if (buf == NULL)
return -ENOMEM;
/* Info type is 'plug type'. */
avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x00);
err = fcp_avc_transaction(unit, buf, 12, buf, 12,
BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
BIT(6) | BIT(7) | BIT(9));
if ((err >= 0) && (err < 8))
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
else if (buf[0] == 0x0a) /* REJECTED */
err = -EINVAL;
else if (buf[0] == 0x0b) /* IN TRANSITION */
err = -EAGAIN;
if (err < 0)
goto end;
*type = buf[10];
err = 0;
end:
kfree(buf);
return err;
}
int avc_bridgeco_get_plug_ch_pos(struct fw_unit *unit,
u8 addr[AVC_BRIDGECO_ADDR_BYTES],
u8 *buf, unsigned int len)
{
int err;
/* Info type is 'channel position'. */
avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x03);
err = fcp_avc_transaction(unit, buf, 12, buf, 256,
BIT(1) | BIT(2) | BIT(3) | BIT(4) |
BIT(5) | BIT(6) | BIT(7) | BIT(9));
if ((err >= 0) && (err < 8))
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
else if (buf[0] == 0x0a) /* REJECTED */
err = -EINVAL;
else if (buf[0] == 0x0b) /* IN TRANSITION */
err = -EAGAIN;
if (err < 0)
goto end;
/* Pick up specific data. */
memmove(buf, buf + 10, err - 10);
err = 0;
end:
return err;
}
int avc_bridgeco_get_plug_section_type(struct fw_unit *unit,
u8 addr[AVC_BRIDGECO_ADDR_BYTES],
unsigned int id, u8 *type)
{
u8 *buf;
int err;
/* section info includes charactors but this module don't need it */
buf = kzalloc(12, GFP_KERNEL);
if (buf == NULL)
return -ENOMEM;
/* Info type is 'section info'. */
avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x07);
buf[10] = 0xff & ++id; /* section id */
err = fcp_avc_transaction(unit, buf, 12, buf, 12,
BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
BIT(6) | BIT(7) | BIT(9) | BIT(10));
if ((err >= 0) && (err < 8))
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
else if (buf[0] == 0x0a) /* REJECTED */
err = -EINVAL;
else if (buf[0] == 0x0b) /* IN TRANSITION */
err = -EAGAIN;
if (err < 0)
goto end;
*type = buf[11];
err = 0;
end:
kfree(buf);
return err;
}
int avc_bridgeco_get_plug_input(struct fw_unit *unit,
u8 addr[AVC_BRIDGECO_ADDR_BYTES], u8 input[7])
{
int err;
u8 *buf;
buf = kzalloc(18, GFP_KERNEL);
if (buf == NULL)
return -ENOMEM;
/* Info type is 'plug input'. */
avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x05);
err = fcp_avc_transaction(unit, buf, 16, buf, 16,
BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
BIT(6) | BIT(7));
if ((err >= 0) && (err < 8))
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
else if (buf[0] == 0x0a) /* REJECTED */
err = -EINVAL;
else if (buf[0] == 0x0b) /* IN TRANSITION */
err = -EAGAIN;
if (err < 0)
goto end;
memcpy(input, buf + 10, 5);
err = 0;
end:
kfree(buf);
return err;
}
int avc_bridgeco_get_plug_strm_fmt(struct fw_unit *unit,
u8 addr[AVC_BRIDGECO_ADDR_BYTES], u8 *buf,
unsigned int *len, unsigned int eid)
{
int err;
/* check given buffer */
if ((buf == NULL) || (*len < 12)) {
err = -EINVAL;
goto end;
}
buf[0] = 0x01; /* AV/C STATUS */
buf[2] = 0x2f; /* AV/C STREAM FORMAT SUPPORT */
buf[3] = 0xc1; /* Bridgeco extension - List Request */
avc_bridgeco_fill_extension_addr(buf, addr);
buf[10] = 0xff & eid; /* Entry ID */
err = fcp_avc_transaction(unit, buf, 12, buf, *len,
BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
BIT(6) | BIT(7) | BIT(10));
if ((err >= 0) && (err < 12))
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
else if (buf[0] == 0x0a) /* REJECTED */
err = -EINVAL;
else if (buf[0] == 0x0b) /* IN TRANSITION */
err = -EAGAIN;
else if (buf[10] != eid)
err = -EIO;
if (err < 0)
goto end;
/* Pick up 'stream format info'. */
memmove(buf, buf + 11, err - 11);
*len = err - 11;
err = 0;
end:
return err;
}

View File

@ -0,0 +1,279 @@
/*
* bebob_focusrite.c - a part of driver for BeBoB based devices
*
* Copyright (c) 2013-2014 Takashi Sakamoto
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#include "./bebob.h"
#define ANA_IN "Analog In"
#define DIG_IN "Digital In"
#define ANA_OUT "Analog Out"
#define DIG_OUT "Digital Out"
#define STM_IN "Stream In"
#define SAFFIRE_ADDRESS_BASE 0x000100000000ULL
#define SAFFIRE_OFFSET_CLOCK_SOURCE 0x00f8
#define SAFFIREPRO_OFFSET_CLOCK_SOURCE 0x0174
/* whether sync to external device or not */
#define SAFFIRE_OFFSET_CLOCK_SYNC_EXT 0x013c
#define SAFFIRE_LE_OFFSET_CLOCK_SYNC_EXT 0x0432
#define SAFFIREPRO_OFFSET_CLOCK_SYNC_EXT 0x0164
#define SAFFIRE_CLOCK_SOURCE_INTERNAL 0
#define SAFFIRE_CLOCK_SOURCE_SPDIF 1
/* '1' is absent, why... */
#define SAFFIREPRO_CLOCK_SOURCE_INTERNAL 0
#define SAFFIREPRO_CLOCK_SOURCE_SPDIF 2
#define SAFFIREPRO_CLOCK_SOURCE_ADAT1 3
#define SAFFIREPRO_CLOCK_SOURCE_ADAT2 4
#define SAFFIREPRO_CLOCK_SOURCE_WORDCLOCK 5
/* S/PDIF, ADAT1, ADAT2 is enabled or not. three quadlets */
#define SAFFIREPRO_ENABLE_DIG_IFACES 0x01a4
/* saffirepro has its own parameter for sampling frequency */
#define SAFFIREPRO_RATE_NOREBOOT 0x01cc
/* index is the value for this register */
static const unsigned int rates[] = {
[0] = 0,
[1] = 44100,
[2] = 48000,
[3] = 88200,
[4] = 96000,
[5] = 176400,
[6] = 192000
};
/* saffire(no label)/saffire LE has metering */
#define SAFFIRE_OFFSET_METER 0x0100
#define SAFFIRE_LE_OFFSET_METER 0x0168
static inline int
saffire_read_block(struct snd_bebob *bebob, u64 offset,
u32 *buf, unsigned int size)
{
unsigned int i;
int err;
__be32 *tmp = (__be32 *)buf;
err = snd_fw_transaction(bebob->unit, TCODE_READ_BLOCK_REQUEST,
SAFFIRE_ADDRESS_BASE + offset,
tmp, size, 0);
if (err < 0)
goto end;
for (i = 0; i < size / sizeof(u32); i++)
buf[i] = be32_to_cpu(tmp[i]);
end:
return err;
}
static inline int
saffire_read_quad(struct snd_bebob *bebob, u64 offset, u32 *value)
{
int err;
__be32 tmp;
err = snd_fw_transaction(bebob->unit, TCODE_READ_QUADLET_REQUEST,
SAFFIRE_ADDRESS_BASE + offset,
&tmp, sizeof(__be32), 0);
if (err < 0)
goto end;
*value = be32_to_cpu(tmp);
end:
return err;
}
static inline int
saffire_write_quad(struct snd_bebob *bebob, u64 offset, u32 value)
{
__be32 data = cpu_to_be32(value);
return snd_fw_transaction(bebob->unit, TCODE_WRITE_QUADLET_REQUEST,
SAFFIRE_ADDRESS_BASE + offset,
&data, sizeof(__be32), 0);
}
static char *const saffirepro_26_clk_src_labels[] = {
SND_BEBOB_CLOCK_INTERNAL, "S/PDIF", "ADAT1", "ADAT2", "Word Clock"
};
static char *const saffirepro_10_clk_src_labels[] = {
SND_BEBOB_CLOCK_INTERNAL, "S/PDIF", "Word Clock"
};
static int
saffirepro_both_clk_freq_get(struct snd_bebob *bebob, unsigned int *rate)
{
u32 id;
int err;
err = saffire_read_quad(bebob, SAFFIREPRO_RATE_NOREBOOT, &id);
if (err < 0)
goto end;
if (id >= ARRAY_SIZE(rates))
err = -EIO;
else
*rate = rates[id];
end:
return err;
}
static int
saffirepro_both_clk_freq_set(struct snd_bebob *bebob, unsigned int rate)
{
u32 id;
for (id = 0; id < ARRAY_SIZE(rates); id++) {
if (rates[id] == rate)
break;
}
if (id == ARRAY_SIZE(rates))
return -EINVAL;
return saffire_write_quad(bebob, SAFFIREPRO_RATE_NOREBOOT, id);
}
static int
saffirepro_both_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
{
int err;
u32 value;
err = saffire_read_quad(bebob, SAFFIREPRO_OFFSET_CLOCK_SOURCE, &value);
if (err < 0)
goto end;
if (bebob->spec->clock->labels == saffirepro_10_clk_src_labels) {
if (value == SAFFIREPRO_CLOCK_SOURCE_WORDCLOCK)
*id = 2;
else if (value == SAFFIREPRO_CLOCK_SOURCE_SPDIF)
*id = 1;
} else if (value > 1) {
*id = value - 1;
}
end:
return err;
}
struct snd_bebob_spec saffire_le_spec;
static char *const saffire_both_clk_src_labels[] = {
SND_BEBOB_CLOCK_INTERNAL, "S/PDIF"
};
static int
saffire_both_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
{
int err;
u32 value;
err = saffire_read_quad(bebob, SAFFIRE_OFFSET_CLOCK_SOURCE, &value);
if (err >= 0)
*id = 0xff & value;
return err;
};
static char *const saffire_le_meter_labels[] = {
ANA_IN, ANA_IN, DIG_IN,
ANA_OUT, ANA_OUT, ANA_OUT, ANA_OUT,
STM_IN, STM_IN
};
static char *const saffire_meter_labels[] = {
ANA_IN, ANA_IN,
STM_IN, STM_IN, STM_IN, STM_IN, STM_IN,
};
static int
saffire_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
{
struct snd_bebob_meter_spec *spec = bebob->spec->meter;
unsigned int channels;
u64 offset;
int err;
if (spec->labels == saffire_le_meter_labels)
offset = SAFFIRE_LE_OFFSET_METER;
else
offset = SAFFIRE_OFFSET_METER;
channels = spec->num * 2;
if (size < channels * sizeof(u32))
return -EIO;
err = saffire_read_block(bebob, offset, buf, size);
if (err >= 0 && spec->labels == saffire_le_meter_labels) {
swap(buf[1], buf[3]);
swap(buf[2], buf[3]);
swap(buf[3], buf[4]);
swap(buf[7], buf[10]);
swap(buf[8], buf[10]);
swap(buf[9], buf[11]);
swap(buf[11], buf[12]);
swap(buf[15], buf[16]);
}
return err;
}
static struct snd_bebob_rate_spec saffirepro_both_rate_spec = {
.get = &saffirepro_both_clk_freq_get,
.set = &saffirepro_both_clk_freq_set,
};
/* Saffire Pro 26 I/O */
static struct snd_bebob_clock_spec saffirepro_26_clk_spec = {
.num = ARRAY_SIZE(saffirepro_26_clk_src_labels),
.labels = saffirepro_26_clk_src_labels,
.get = &saffirepro_both_clk_src_get,
};
struct snd_bebob_spec saffirepro_26_spec = {
.clock = &saffirepro_26_clk_spec,
.rate = &saffirepro_both_rate_spec,
.meter = NULL
};
/* Saffire Pro 10 I/O */
static struct snd_bebob_clock_spec saffirepro_10_clk_spec = {
.num = ARRAY_SIZE(saffirepro_10_clk_src_labels),
.labels = saffirepro_10_clk_src_labels,
.get = &saffirepro_both_clk_src_get,
};
struct snd_bebob_spec saffirepro_10_spec = {
.clock = &saffirepro_10_clk_spec,
.rate = &saffirepro_both_rate_spec,
.meter = NULL
};
static struct snd_bebob_rate_spec saffire_both_rate_spec = {
.get = &snd_bebob_stream_get_rate,
.set = &snd_bebob_stream_set_rate,
};
static struct snd_bebob_clock_spec saffire_both_clk_spec = {
.num = ARRAY_SIZE(saffire_both_clk_src_labels),
.labels = saffire_both_clk_src_labels,
.get = &saffire_both_clk_src_get,
};
/* Saffire LE */
static struct snd_bebob_meter_spec saffire_le_meter_spec = {
.num = ARRAY_SIZE(saffire_le_meter_labels),
.labels = saffire_le_meter_labels,
.get = &saffire_meter_get,
};
struct snd_bebob_spec saffire_le_spec = {
.clock = &saffire_both_clk_spec,
.rate = &saffire_both_rate_spec,
.meter = &saffire_le_meter_spec
};
/* Saffire */
static struct snd_bebob_meter_spec saffire_meter_spec = {
.num = ARRAY_SIZE(saffire_meter_labels),
.labels = saffire_meter_labels,
.get = &saffire_meter_get,
};
struct snd_bebob_spec saffire_spec = {
.clock = &saffire_both_clk_spec,
.rate = &saffire_both_rate_spec,
.meter = &saffire_meter_spec
};

View File

@ -0,0 +1,199 @@
/*
* bebob_hwdep.c - a part of driver for BeBoB based devices
*
* Copyright (c) 2013-2014 Takashi Sakamoto
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
/*
* This codes give three functionality.
*
* 1.get firewire node infomation
* 2.get notification about starting/stopping stream
* 3.lock/unlock stream
*/
#include "bebob.h"
static long
hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
loff_t *offset)
{
struct snd_bebob *bebob = hwdep->private_data;
DEFINE_WAIT(wait);
union snd_firewire_event event;
spin_lock_irq(&bebob->lock);
while (!bebob->dev_lock_changed) {
prepare_to_wait(&bebob->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
spin_unlock_irq(&bebob->lock);
schedule();
finish_wait(&bebob->hwdep_wait, &wait);
if (signal_pending(current))
return -ERESTARTSYS;
spin_lock_irq(&bebob->lock);
}
memset(&event, 0, sizeof(event));
if (bebob->dev_lock_changed) {
event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
event.lock_status.status = (bebob->dev_lock_count > 0);
bebob->dev_lock_changed = false;
count = min_t(long, count, sizeof(event.lock_status));
}
spin_unlock_irq(&bebob->lock);
if (copy_to_user(buf, &event, count))
return -EFAULT;
return count;
}
static unsigned int
hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait)
{
struct snd_bebob *bebob = hwdep->private_data;
unsigned int events;
poll_wait(file, &bebob->hwdep_wait, wait);
spin_lock_irq(&bebob->lock);
if (bebob->dev_lock_changed)
events = POLLIN | POLLRDNORM;
else
events = 0;
spin_unlock_irq(&bebob->lock);
return events;
}
static int
hwdep_get_info(struct snd_bebob *bebob, void __user *arg)
{
struct fw_device *dev = fw_parent_device(bebob->unit);
struct snd_firewire_get_info info;
memset(&info, 0, sizeof(info));
info.type = SNDRV_FIREWIRE_TYPE_BEBOB;
info.card = dev->card->index;
*(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
*(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
strlcpy(info.device_name, dev_name(&dev->device),
sizeof(info.device_name));
if (copy_to_user(arg, &info, sizeof(info)))
return -EFAULT;
return 0;
}
static int
hwdep_lock(struct snd_bebob *bebob)
{
int err;
spin_lock_irq(&bebob->lock);
if (bebob->dev_lock_count == 0) {
bebob->dev_lock_count = -1;
err = 0;
} else {
err = -EBUSY;
}
spin_unlock_irq(&bebob->lock);
return err;
}
static int
hwdep_unlock(struct snd_bebob *bebob)
{
int err;
spin_lock_irq(&bebob->lock);
if (bebob->dev_lock_count == -1) {
bebob->dev_lock_count = 0;
err = 0;
} else {
err = -EBADFD;
}
spin_unlock_irq(&bebob->lock);
return err;
}
static int
hwdep_release(struct snd_hwdep *hwdep, struct file *file)
{
struct snd_bebob *bebob = hwdep->private_data;
spin_lock_irq(&bebob->lock);
if (bebob->dev_lock_count == -1)
bebob->dev_lock_count = 0;
spin_unlock_irq(&bebob->lock);
return 0;
}
static int
hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
unsigned int cmd, unsigned long arg)
{
struct snd_bebob *bebob = hwdep->private_data;
switch (cmd) {
case SNDRV_FIREWIRE_IOCTL_GET_INFO:
return hwdep_get_info(bebob, (void __user *)arg);
case SNDRV_FIREWIRE_IOCTL_LOCK:
return hwdep_lock(bebob);
case SNDRV_FIREWIRE_IOCTL_UNLOCK:
return hwdep_unlock(bebob);
default:
return -ENOIOCTLCMD;
}
}
#ifdef CONFIG_COMPAT
static int
hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
unsigned int cmd, unsigned long arg)
{
return hwdep_ioctl(hwdep, file, cmd,
(unsigned long)compat_ptr(arg));
}
#else
#define hwdep_compat_ioctl NULL
#endif
static const struct snd_hwdep_ops hwdep_ops = {
.read = hwdep_read,
.release = hwdep_release,
.poll = hwdep_poll,
.ioctl = hwdep_ioctl,
.ioctl_compat = hwdep_compat_ioctl,
};
int snd_bebob_create_hwdep_device(struct snd_bebob *bebob)
{
struct snd_hwdep *hwdep;
int err;
err = snd_hwdep_new(bebob->card, "BeBoB", 0, &hwdep);
if (err < 0)
goto end;
strcpy(hwdep->name, "BeBoB");
hwdep->iface = SNDRV_HWDEP_IFACE_FW_BEBOB;
hwdep->ops = hwdep_ops;
hwdep->private_data = bebob;
hwdep->exclusive = true;
end:
return err;
}

View File

@ -0,0 +1,792 @@
/*
* bebob_maudio.c - a part of driver for BeBoB based devices
*
* Copyright (c) 2013-2014 Takashi Sakamoto
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#include "./bebob.h"
#include <sound/control.h>
/*
* Just powering on, Firewire 410/Audiophile/1814 and ProjectMix I/O wait to
* download firmware blob. To enable these devices, drivers should upload
* firmware blob and send a command to initialize configuration to factory
* settings when completing uploading. Then these devices generate bus reset
* and are recognized as new devices with the firmware.
*
* But with firmware version 5058 or later, the firmware is stored to flash
* memory in the device and drivers can tell bootloader to load the firmware
* by sending a cue. This cue must be sent one time.
*
* For streaming, both of output and input streams are needed for Firewire 410
* and Ozonic. The single stream is OK for the other devices even if the clock
* source is not SYT-Match (I note no devices use SYT-Match).
*
* Without streaming, the devices except for Firewire Audiophile can mix any
* input and output. For this reason, Audiophile cannot be used as standalone
* mixer.
*
* Firewire 1814 and ProjectMix I/O uses special firmware. It will be freezed
* when receiving any commands which the firmware can't understand. These
* devices utilize completely different system to control. It is some
* write-transaction directly into a certain address. All of addresses for mixer
* functionality is between 0xffc700700000 to 0xffc70070009c.
*/
/* Offset from information register */
#define INFO_OFFSET_SW_DATE 0x20
/* Bootloader Protocol Version 1 */
#define MAUDIO_BOOTLOADER_CUE1 0x00000001
/*
* Initializing configuration to factory settings (= 0x1101), (swapped in line),
* Command code is zero (= 0x00),
* the number of operands is zero (= 0x00)(at least significant byte)
*/
#define MAUDIO_BOOTLOADER_CUE2 0x01110000
/* padding */
#define MAUDIO_BOOTLOADER_CUE3 0x00000000
#define MAUDIO_SPECIFIC_ADDRESS 0xffc700000000ULL
#define METER_OFFSET 0x00600000
/* some device has sync info after metering data */
#define METER_SIZE_SPECIAL 84 /* with sync info */
#define METER_SIZE_FW410 76 /* with sync info */
#define METER_SIZE_AUDIOPHILE 60 /* with sync info */
#define METER_SIZE_SOLO 52 /* with sync info */
#define METER_SIZE_OZONIC 48
#define METER_SIZE_NRV10 80
/* labels for metering */
#define ANA_IN "Analog In"
#define ANA_OUT "Analog Out"
#define DIG_IN "Digital In"
#define SPDIF_IN "S/PDIF In"
#define ADAT_IN "ADAT In"
#define DIG_OUT "Digital Out"
#define SPDIF_OUT "S/PDIF Out"
#define ADAT_OUT "ADAT Out"
#define STRM_IN "Stream In"
#define AUX_OUT "Aux Out"
#define HP_OUT "HP Out"
/* for NRV */
#define UNKNOWN_METER "Unknown"
struct special_params {
bool is1814;
unsigned int clk_src;
unsigned int dig_in_fmt;
unsigned int dig_out_fmt;
unsigned int clk_lock;
struct snd_ctl_elem_id *ctl_id_sync;
};
/*
* For some M-Audio devices, this module just send cue to load firmware. After
* loading, the device generates bus reset and newly detected.
*
* If we make any transactions to load firmware, the operation may failed.
*/
int snd_bebob_maudio_load_firmware(struct fw_unit *unit)
{
struct fw_device *device = fw_parent_device(unit);
int err, rcode;
u64 date;
__be32 cues[3] = {
MAUDIO_BOOTLOADER_CUE1,
MAUDIO_BOOTLOADER_CUE2,
MAUDIO_BOOTLOADER_CUE3
};
/* check date of software used to build */
err = snd_bebob_read_block(unit, INFO_OFFSET_SW_DATE,
&date, sizeof(u64));
if (err < 0)
goto end;
/*
* firmware version 5058 or later has date later than "20070401", but
* 'date' is not null-terminated.
*/
if (date < 0x3230303730343031LL) {
dev_err(&unit->device,
"Use firmware version 5058 or later\n");
err = -ENOSYS;
goto end;
}
rcode = fw_run_transaction(device->card, TCODE_WRITE_BLOCK_REQUEST,
device->node_id, device->generation,
device->max_speed, BEBOB_ADDR_REG_REQ,
cues, sizeof(cues));
if (rcode != RCODE_COMPLETE) {
dev_err(&unit->device,
"Failed to send a cue to load firmware\n");
err = -EIO;
}
end:
return err;
}
static inline int
get_meter(struct snd_bebob *bebob, void *buf, unsigned int size)
{
return snd_fw_transaction(bebob->unit, TCODE_READ_BLOCK_REQUEST,
MAUDIO_SPECIFIC_ADDRESS + METER_OFFSET,
buf, size, 0);
}
static int
check_clk_sync(struct snd_bebob *bebob, unsigned int size, bool *sync)
{
int err;
u8 *buf;
buf = kmalloc(size, GFP_KERNEL);
if (buf == NULL)
return -ENOMEM;
err = get_meter(bebob, buf, size);
if (err < 0)
goto end;
/* if synced, this value is the same as SFC of FDF in CIP header */
*sync = (buf[size - 2] != 0xff);
end:
kfree(buf);
return err;
}
/*
* dig_fmt: 0x00:S/PDIF, 0x01:ADAT
* clk_lock: 0x00:unlock, 0x01:lock
*/
static int
avc_maudio_set_special_clk(struct snd_bebob *bebob, unsigned int clk_src,
unsigned int dig_in_fmt, unsigned int dig_out_fmt,
unsigned int clk_lock)
{
struct special_params *params = bebob->maudio_special_quirk;
int err;
u8 *buf;
if (amdtp_stream_running(&bebob->rx_stream) ||
amdtp_stream_running(&bebob->tx_stream))
return -EBUSY;
buf = kmalloc(12, GFP_KERNEL);
if (buf == NULL)
return -ENOMEM;
buf[0] = 0x00; /* CONTROL */
buf[1] = 0xff; /* UNIT */
buf[2] = 0x00; /* vendor dependent */
buf[3] = 0x04; /* company ID high */
buf[4] = 0x00; /* company ID middle */
buf[5] = 0x04; /* company ID low */
buf[6] = 0xff & clk_src; /* clock source */
buf[7] = 0xff & dig_in_fmt; /* input digital format */
buf[8] = 0xff & dig_out_fmt; /* output digital format */
buf[9] = 0xff & clk_lock; /* lock these settings */
buf[10] = 0x00; /* padding */
buf[11] = 0x00; /* padding */
err = fcp_avc_transaction(bebob->unit, buf, 12, buf, 12,
BIT(1) | BIT(2) | BIT(3) | BIT(4) |
BIT(5) | BIT(6) | BIT(7) | BIT(8) |
BIT(9));
if ((err > 0) && (err < 10))
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
else if (buf[0] == 0x0a) /* REJECTED */
err = -EINVAL;
if (err < 0)
goto end;
params->clk_src = buf[6];
params->dig_in_fmt = buf[7];
params->dig_out_fmt = buf[8];
params->clk_lock = buf[9];
if (params->ctl_id_sync)
snd_ctl_notify(bebob->card, SNDRV_CTL_EVENT_MASK_VALUE,
params->ctl_id_sync);
err = 0;
end:
kfree(buf);
return err;
}
static void
special_stream_formation_set(struct snd_bebob *bebob)
{
static const unsigned int ch_table[2][2][3] = {
/* AMDTP_OUT_STREAM */
{ { 6, 6, 4 }, /* SPDIF */
{ 12, 8, 4 } }, /* ADAT */
/* AMDTP_IN_STREAM */
{ { 10, 10, 2 }, /* SPDIF */
{ 16, 12, 2 } } /* ADAT */
};
struct special_params *params = bebob->maudio_special_quirk;
unsigned int i, max;
max = SND_BEBOB_STRM_FMT_ENTRIES - 1;
if (!params->is1814)
max -= 2;
for (i = 0; i < max; i++) {
bebob->tx_stream_formations[i + 1].pcm =
ch_table[AMDTP_IN_STREAM][params->dig_in_fmt][i / 2];
bebob->tx_stream_formations[i + 1].midi = 1;
bebob->rx_stream_formations[i + 1].pcm =
ch_table[AMDTP_OUT_STREAM][params->dig_out_fmt][i / 2];
bebob->rx_stream_formations[i + 1].midi = 1;
}
}
static int add_special_controls(struct snd_bebob *bebob);
int
snd_bebob_maudio_special_discover(struct snd_bebob *bebob, bool is1814)
{
struct special_params *params;
int err;
params = kzalloc(sizeof(struct special_params), GFP_KERNEL);
if (params == NULL)
return -ENOMEM;
mutex_lock(&bebob->mutex);
bebob->maudio_special_quirk = (void *)params;
params->is1814 = is1814;
/* initialize these parameters because driver is not allowed to ask */
bebob->rx_stream.context = ERR_PTR(-1);
bebob->tx_stream.context = ERR_PTR(-1);
err = avc_maudio_set_special_clk(bebob, 0x03, 0x00, 0x00, 0x00);
if (err < 0) {
dev_err(&bebob->unit->device,
"fail to initialize clock params: %d\n", err);
goto end;
}
err = add_special_controls(bebob);
if (err < 0)
goto end;
special_stream_formation_set(bebob);
if (params->is1814) {
bebob->midi_input_ports = 1;
bebob->midi_output_ports = 1;
} else {
bebob->midi_input_ports = 2;
bebob->midi_output_ports = 2;
}
end:
if (err < 0) {
kfree(params);
bebob->maudio_special_quirk = NULL;
}
mutex_unlock(&bebob->mutex);
return err;
}
/* Input plug shows actual rate. Output plug is needless for this purpose. */
static int special_get_rate(struct snd_bebob *bebob, unsigned int *rate)
{
int err, trials;
trials = 0;
do {
err = avc_general_get_sig_fmt(bebob->unit, rate,
AVC_GENERAL_PLUG_DIR_IN, 0);
} while (err == -EAGAIN && ++trials < 3);
return err;
}
static int special_set_rate(struct snd_bebob *bebob, unsigned int rate)
{
struct special_params *params = bebob->maudio_special_quirk;
int err;
err = avc_general_set_sig_fmt(bebob->unit, rate,
AVC_GENERAL_PLUG_DIR_OUT, 0);
if (err < 0)
goto end;
/*
* Just after changing sampling rate for output, a followed command
* for input is easy to fail. This is a workaround fot this issue.
*/
msleep(100);
err = avc_general_set_sig_fmt(bebob->unit, rate,
AVC_GENERAL_PLUG_DIR_IN, 0);
if (err < 0)
goto end;
if (params->ctl_id_sync)
snd_ctl_notify(bebob->card, SNDRV_CTL_EVENT_MASK_VALUE,
params->ctl_id_sync);
end:
return err;
}
/* Clock source control for special firmware */
static char *const special_clk_labels[] = {
SND_BEBOB_CLOCK_INTERNAL " with Digital Mute", "Digital",
"Word Clock", SND_BEBOB_CLOCK_INTERNAL};
static int special_clk_get(struct snd_bebob *bebob, unsigned int *id)
{
struct special_params *params = bebob->maudio_special_quirk;
*id = params->clk_src;
return 0;
}
static int special_clk_ctl_info(struct snd_kcontrol *kctl,
struct snd_ctl_elem_info *einf)
{
einf->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
einf->count = 1;
einf->value.enumerated.items = ARRAY_SIZE(special_clk_labels);
if (einf->value.enumerated.item >= einf->value.enumerated.items)
einf->value.enumerated.item = einf->value.enumerated.items - 1;
strcpy(einf->value.enumerated.name,
special_clk_labels[einf->value.enumerated.item]);
return 0;
}
static int special_clk_ctl_get(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *uval)
{
struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
struct special_params *params = bebob->maudio_special_quirk;
uval->value.enumerated.item[0] = params->clk_src;
return 0;
}
static int special_clk_ctl_put(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *uval)
{
struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
struct special_params *params = bebob->maudio_special_quirk;
int err, id;
mutex_lock(&bebob->mutex);
id = uval->value.enumerated.item[0];
if (id >= ARRAY_SIZE(special_clk_labels))
return 0;
err = avc_maudio_set_special_clk(bebob, id,
params->dig_in_fmt,
params->dig_out_fmt,
params->clk_lock);
mutex_unlock(&bebob->mutex);
return err >= 0;
}
static struct snd_kcontrol_new special_clk_ctl = {
.name = "Clock Source",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = special_clk_ctl_info,
.get = special_clk_ctl_get,
.put = special_clk_ctl_put
};
/* Clock synchronization control for special firmware */
static int special_sync_ctl_info(struct snd_kcontrol *kctl,
struct snd_ctl_elem_info *einf)
{
einf->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
einf->count = 1;
einf->value.integer.min = 0;
einf->value.integer.max = 1;
return 0;
}
static int special_sync_ctl_get(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *uval)
{
struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
int err;
bool synced = 0;
err = check_clk_sync(bebob, METER_SIZE_SPECIAL, &synced);
if (err >= 0)
uval->value.integer.value[0] = synced;
return 0;
}
static struct snd_kcontrol_new special_sync_ctl = {
.name = "Sync Status",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.info = special_sync_ctl_info,
.get = special_sync_ctl_get,
};
/* Digital interface control for special firmware */
static char *const special_dig_iface_labels[] = {
"S/PDIF Optical", "S/PDIF Coaxial", "ADAT Optical"
};
static int special_dig_in_iface_ctl_info(struct snd_kcontrol *kctl,
struct snd_ctl_elem_info *einf)
{
einf->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
einf->count = 1;
einf->value.enumerated.items = ARRAY_SIZE(special_dig_iface_labels);
if (einf->value.enumerated.item >= einf->value.enumerated.items)
einf->value.enumerated.item = einf->value.enumerated.items - 1;
strcpy(einf->value.enumerated.name,
special_dig_iface_labels[einf->value.enumerated.item]);
return 0;
}
static int special_dig_in_iface_ctl_get(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *uval)
{
struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
struct special_params *params = bebob->maudio_special_quirk;
unsigned int dig_in_iface;
int err, val;
mutex_lock(&bebob->mutex);
err = avc_audio_get_selector(bebob->unit, 0x00, 0x04,
&dig_in_iface);
if (err < 0) {
dev_err(&bebob->unit->device,
"fail to get digital input interface: %d\n", err);
goto end;
}
/* encoded id for user value */
val = (params->dig_in_fmt << 1) | (dig_in_iface & 0x01);
/* for ADAT Optical */
if (val > 2)
val = 2;
uval->value.enumerated.item[0] = val;
end:
mutex_unlock(&bebob->mutex);
return err;
}
static int special_dig_in_iface_ctl_set(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *uval)
{
struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
struct special_params *params = bebob->maudio_special_quirk;
unsigned int id, dig_in_fmt, dig_in_iface;
int err;
mutex_lock(&bebob->mutex);
id = uval->value.enumerated.item[0];
/* decode user value */
dig_in_fmt = (id >> 1) & 0x01;
dig_in_iface = id & 0x01;
err = avc_maudio_set_special_clk(bebob,
params->clk_src,
dig_in_fmt,
params->dig_out_fmt,
params->clk_lock);
if ((err < 0) || (params->dig_in_fmt > 0)) /* ADAT */
goto end;
err = avc_audio_set_selector(bebob->unit, 0x00, 0x04, dig_in_iface);
if (err < 0)
dev_err(&bebob->unit->device,
"fail to set digital input interface: %d\n", err);
end:
special_stream_formation_set(bebob);
mutex_unlock(&bebob->mutex);
return err;
}
static struct snd_kcontrol_new special_dig_in_iface_ctl = {
.name = "Digital Input Interface",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = special_dig_in_iface_ctl_info,
.get = special_dig_in_iface_ctl_get,
.put = special_dig_in_iface_ctl_set
};
static int special_dig_out_iface_ctl_info(struct snd_kcontrol *kctl,
struct snd_ctl_elem_info *einf)
{
einf->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
einf->count = 1;
einf->value.enumerated.items = ARRAY_SIZE(special_dig_iface_labels) - 1;
if (einf->value.enumerated.item >= einf->value.enumerated.items)
einf->value.enumerated.item = einf->value.enumerated.items - 1;
strcpy(einf->value.enumerated.name,
special_dig_iface_labels[einf->value.enumerated.item + 1]);
return 0;
}
static int special_dig_out_iface_ctl_get(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *uval)
{
struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
struct special_params *params = bebob->maudio_special_quirk;
mutex_lock(&bebob->mutex);
uval->value.enumerated.item[0] = params->dig_out_fmt;
mutex_unlock(&bebob->mutex);
return 0;
}
static int special_dig_out_iface_ctl_set(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *uval)
{
struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
struct special_params *params = bebob->maudio_special_quirk;
unsigned int id;
int err;
mutex_lock(&bebob->mutex);
id = uval->value.enumerated.item[0];
err = avc_maudio_set_special_clk(bebob,
params->clk_src,
params->dig_in_fmt,
id, params->clk_lock);
if (err >= 0)
special_stream_formation_set(bebob);
mutex_unlock(&bebob->mutex);
return err;
}
static struct snd_kcontrol_new special_dig_out_iface_ctl = {
.name = "Digital Output Interface",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = special_dig_out_iface_ctl_info,
.get = special_dig_out_iface_ctl_get,
.put = special_dig_out_iface_ctl_set
};
static int add_special_controls(struct snd_bebob *bebob)
{
struct snd_kcontrol *kctl;
struct special_params *params = bebob->maudio_special_quirk;
int err;
kctl = snd_ctl_new1(&special_clk_ctl, bebob);
err = snd_ctl_add(bebob->card, kctl);
if (err < 0)
goto end;
kctl = snd_ctl_new1(&special_sync_ctl, bebob);
err = snd_ctl_add(bebob->card, kctl);
if (err < 0)
goto end;
params->ctl_id_sync = &kctl->id;
kctl = snd_ctl_new1(&special_dig_in_iface_ctl, bebob);
err = snd_ctl_add(bebob->card, kctl);
if (err < 0)
goto end;
kctl = snd_ctl_new1(&special_dig_out_iface_ctl, bebob);
err = snd_ctl_add(bebob->card, kctl);
end:
return err;
}
/* Hardware metering for special firmware */
static char *const special_meter_labels[] = {
ANA_IN, ANA_IN, ANA_IN, ANA_IN,
SPDIF_IN,
ADAT_IN, ADAT_IN, ADAT_IN, ADAT_IN,
ANA_OUT, ANA_OUT,
SPDIF_OUT,
ADAT_OUT, ADAT_OUT, ADAT_OUT, ADAT_OUT,
HP_OUT, HP_OUT,
AUX_OUT
};
static int
special_meter_get(struct snd_bebob *bebob, u32 *target, unsigned int size)
{
u16 *buf;
unsigned int i, c, channels;
int err;
channels = ARRAY_SIZE(special_meter_labels) * 2;
if (size < channels * sizeof(u32))
return -EINVAL;
/* omit last 4 bytes because it's clock info. */
buf = kmalloc(METER_SIZE_SPECIAL - 4, GFP_KERNEL);
if (buf == NULL)
return -ENOMEM;
err = get_meter(bebob, (void *)buf, METER_SIZE_SPECIAL - 4);
if (err < 0)
goto end;
/* Its format is u16 and some channels are unknown. */
i = 0;
for (c = 2; c < channels + 2; c++)
target[i++] = be16_to_cpu(buf[c]) << 16;
end:
kfree(buf);
return err;
}
/* last 4 bytes are omitted because it's clock info. */
static char *const fw410_meter_labels[] = {
ANA_IN, DIG_IN,
ANA_OUT, ANA_OUT, ANA_OUT, ANA_OUT, DIG_OUT,
HP_OUT
};
static char *const audiophile_meter_labels[] = {
ANA_IN, DIG_IN,
ANA_OUT, ANA_OUT, DIG_OUT,
HP_OUT, AUX_OUT,
};
static char *const solo_meter_labels[] = {
ANA_IN, DIG_IN,
STRM_IN, STRM_IN,
ANA_OUT, DIG_OUT
};
/* no clock info */
static char *const ozonic_meter_labels[] = {
ANA_IN, ANA_IN,
STRM_IN, STRM_IN,
ANA_OUT, ANA_OUT
};
/* TODO: need testers. these positions are based on authour's assumption */
static char *const nrv10_meter_labels[] = {
ANA_IN, ANA_IN, ANA_IN, ANA_IN,
DIG_IN,
ANA_OUT, ANA_OUT, ANA_OUT, ANA_OUT,
DIG_IN
};
static int
normal_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
{
struct snd_bebob_meter_spec *spec = bebob->spec->meter;
unsigned int c, channels;
int err;
channels = spec->num * 2;
if (size < channels * sizeof(u32))
return -EINVAL;
err = get_meter(bebob, (void *)buf, size);
if (err < 0)
goto end;
for (c = 0; c < channels; c++)
be32_to_cpus(&buf[c]);
/* swap stream channels because inverted */
if (spec->labels == solo_meter_labels) {
swap(buf[4], buf[6]);
swap(buf[5], buf[7]);
}
end:
return err;
}
/* for special customized devices */
static struct snd_bebob_rate_spec special_rate_spec = {
.get = &special_get_rate,
.set = &special_set_rate,
};
static struct snd_bebob_clock_spec special_clk_spec = {
.num = ARRAY_SIZE(special_clk_labels),
.labels = special_clk_labels,
.get = &special_clk_get,
};
static struct snd_bebob_meter_spec special_meter_spec = {
.num = ARRAY_SIZE(special_meter_labels),
.labels = special_meter_labels,
.get = &special_meter_get
};
struct snd_bebob_spec maudio_special_spec = {
.clock = &special_clk_spec,
.rate = &special_rate_spec,
.meter = &special_meter_spec
};
/* Firewire 410 specification */
static struct snd_bebob_rate_spec usual_rate_spec = {
.get = &snd_bebob_stream_get_rate,
.set = &snd_bebob_stream_set_rate,
};
static struct snd_bebob_meter_spec fw410_meter_spec = {
.num = ARRAY_SIZE(fw410_meter_labels),
.labels = fw410_meter_labels,
.get = &normal_meter_get
};
struct snd_bebob_spec maudio_fw410_spec = {
.clock = NULL,
.rate = &usual_rate_spec,
.meter = &fw410_meter_spec
};
/* Firewire Audiophile specification */
static struct snd_bebob_meter_spec audiophile_meter_spec = {
.num = ARRAY_SIZE(audiophile_meter_labels),
.labels = audiophile_meter_labels,
.get = &normal_meter_get
};
struct snd_bebob_spec maudio_audiophile_spec = {
.clock = NULL,
.rate = &usual_rate_spec,
.meter = &audiophile_meter_spec
};
/* Firewire Solo specification */
static struct snd_bebob_meter_spec solo_meter_spec = {
.num = ARRAY_SIZE(solo_meter_labels),
.labels = solo_meter_labels,
.get = &normal_meter_get
};
struct snd_bebob_spec maudio_solo_spec = {
.clock = NULL,
.rate = &usual_rate_spec,
.meter = &solo_meter_spec
};
/* Ozonic specification */
static struct snd_bebob_meter_spec ozonic_meter_spec = {
.num = ARRAY_SIZE(ozonic_meter_labels),
.labels = ozonic_meter_labels,
.get = &normal_meter_get
};
struct snd_bebob_spec maudio_ozonic_spec = {
.clock = NULL,
.rate = &usual_rate_spec,
.meter = &ozonic_meter_spec
};
/* NRV10 specification */
static struct snd_bebob_meter_spec nrv10_meter_spec = {
.num = ARRAY_SIZE(nrv10_meter_labels),
.labels = nrv10_meter_labels,
.get = &normal_meter_get
};
struct snd_bebob_spec maudio_nrv10_spec = {
.clock = NULL,
.rate = &usual_rate_spec,
.meter = &nrv10_meter_spec
};

View File

@ -0,0 +1,168 @@
/*
* bebob_midi.c - a part of driver for BeBoB based devices
*
* Copyright (c) 2013-2014 Takashi Sakamoto
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#include "bebob.h"
static int midi_capture_open(struct snd_rawmidi_substream *substream)
{
struct snd_bebob *bebob = substream->rmidi->private_data;
int err;
err = snd_bebob_stream_lock_try(bebob);
if (err < 0)
goto end;
atomic_inc(&bebob->capture_substreams);
err = snd_bebob_stream_start_duplex(bebob, 0);
if (err < 0)
snd_bebob_stream_lock_release(bebob);
end:
return err;
}
static int midi_playback_open(struct snd_rawmidi_substream *substream)
{
struct snd_bebob *bebob = substream->rmidi->private_data;
int err;
err = snd_bebob_stream_lock_try(bebob);
if (err < 0)
goto end;
atomic_inc(&bebob->playback_substreams);
err = snd_bebob_stream_start_duplex(bebob, 0);
if (err < 0)
snd_bebob_stream_lock_release(bebob);
end:
return err;
}
static int midi_capture_close(struct snd_rawmidi_substream *substream)
{
struct snd_bebob *bebob = substream->rmidi->private_data;
atomic_dec(&bebob->capture_substreams);
snd_bebob_stream_stop_duplex(bebob);
snd_bebob_stream_lock_release(bebob);
return 0;
}
static int midi_playback_close(struct snd_rawmidi_substream *substream)
{
struct snd_bebob *bebob = substream->rmidi->private_data;
atomic_dec(&bebob->playback_substreams);
snd_bebob_stream_stop_duplex(bebob);
snd_bebob_stream_lock_release(bebob);
return 0;
}
static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
{
struct snd_bebob *bebob = substrm->rmidi->private_data;
unsigned long flags;
spin_lock_irqsave(&bebob->lock, flags);
if (up)
amdtp_stream_midi_trigger(&bebob->tx_stream,
substrm->number, substrm);
else
amdtp_stream_midi_trigger(&bebob->tx_stream,
substrm->number, NULL);
spin_unlock_irqrestore(&bebob->lock, flags);
}
static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
{
struct snd_bebob *bebob = substrm->rmidi->private_data;
unsigned long flags;
spin_lock_irqsave(&bebob->lock, flags);
if (up)
amdtp_stream_midi_trigger(&bebob->rx_stream,
substrm->number, substrm);
else
amdtp_stream_midi_trigger(&bebob->rx_stream,
substrm->number, NULL);
spin_unlock_irqrestore(&bebob->lock, flags);
}
static struct snd_rawmidi_ops midi_capture_ops = {
.open = midi_capture_open,
.close = midi_capture_close,
.trigger = midi_capture_trigger,
};
static struct snd_rawmidi_ops midi_playback_ops = {
.open = midi_playback_open,
.close = midi_playback_close,
.trigger = midi_playback_trigger,
};
static void set_midi_substream_names(struct snd_bebob *bebob,
struct snd_rawmidi_str *str)
{
struct snd_rawmidi_substream *subs;
list_for_each_entry(subs, &str->substreams, list) {
snprintf(subs->name, sizeof(subs->name),
"%s MIDI %d",
bebob->card->shortname, subs->number + 1);
}
}
int snd_bebob_create_midi_devices(struct snd_bebob *bebob)
{
struct snd_rawmidi *rmidi;
struct snd_rawmidi_str *str;
int err;
/* create midi ports */
err = snd_rawmidi_new(bebob->card, bebob->card->driver, 0,
bebob->midi_output_ports, bebob->midi_input_ports,
&rmidi);
if (err < 0)
return err;
snprintf(rmidi->name, sizeof(rmidi->name),
"%s MIDI", bebob->card->shortname);
rmidi->private_data = bebob;
if (bebob->midi_input_ports > 0) {
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
&midi_capture_ops);
str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
set_midi_substream_names(bebob, str);
}
if (bebob->midi_output_ports > 0) {
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
&midi_playback_ops);
str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
set_midi_substream_names(bebob, str);
}
if ((bebob->midi_output_ports > 0) && (bebob->midi_input_ports > 0))
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
return 0;
}

View File

@ -0,0 +1,378 @@
/*
* bebob_pcm.c - a part of driver for BeBoB based devices
*
* Copyright (c) 2013-2014 Takashi Sakamoto
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#include "./bebob.h"
static int
hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
{
struct snd_bebob_stream_formation *formations = rule->private;
struct snd_interval *r =
hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
const struct snd_interval *c =
hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
struct snd_interval t = {
.min = UINT_MAX, .max = 0, .integer = 1
};
unsigned int i;
for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
/* entry is invalid */
if (formations[i].pcm == 0)
continue;
if (!snd_interval_test(c, formations[i].pcm))
continue;
t.min = min(t.min, snd_bebob_rate_table[i]);
t.max = max(t.max, snd_bebob_rate_table[i]);
}
return snd_interval_refine(r, &t);
}
static int
hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
{
struct snd_bebob_stream_formation *formations = rule->private;
struct snd_interval *c =
hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
const struct snd_interval *r =
hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
struct snd_interval t = {
.min = UINT_MAX, .max = 0, .integer = 1
};
unsigned int i;
for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
/* entry is invalid */
if (formations[i].pcm == 0)
continue;
if (!snd_interval_test(r, snd_bebob_rate_table[i]))
continue;
t.min = min(t.min, formations[i].pcm);
t.max = max(t.max, formations[i].pcm);
}
return snd_interval_refine(c, &t);
}
static void
limit_channels_and_rates(struct snd_pcm_hardware *hw,
struct snd_bebob_stream_formation *formations)
{
unsigned int i;
hw->channels_min = UINT_MAX;
hw->channels_max = 0;
hw->rate_min = UINT_MAX;
hw->rate_max = 0;
hw->rates = 0;
for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
/* entry has no PCM channels */
if (formations[i].pcm == 0)
continue;
hw->channels_min = min(hw->channels_min, formations[i].pcm);
hw->channels_max = max(hw->channels_max, formations[i].pcm);
hw->rate_min = min(hw->rate_min, snd_bebob_rate_table[i]);
hw->rate_max = max(hw->rate_max, snd_bebob_rate_table[i]);
hw->rates |= snd_pcm_rate_to_rate_bit(snd_bebob_rate_table[i]);
}
}
static void
limit_period_and_buffer(struct snd_pcm_hardware *hw)
{
hw->periods_min = 2; /* SNDRV_PCM_INFO_BATCH */
hw->periods_max = UINT_MAX;
hw->period_bytes_min = 4 * hw->channels_max; /* bytes for a frame */
/* Just to prevent from allocating much pages. */
hw->period_bytes_max = hw->period_bytes_min * 2048;
hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min;
}
static int
pcm_init_hw_params(struct snd_bebob *bebob,
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct amdtp_stream *s;
struct snd_bebob_stream_formation *formations;
int err;
runtime->hw.info = SNDRV_PCM_INFO_BATCH |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_JOINT_DUPLEX |
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID;
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
runtime->hw.formats = AMDTP_IN_PCM_FORMAT_BITS;
s = &bebob->tx_stream;
formations = bebob->tx_stream_formations;
} else {
runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS;
s = &bebob->rx_stream;
formations = bebob->rx_stream_formations;
}
limit_channels_and_rates(&runtime->hw, formations);
limit_period_and_buffer(&runtime->hw);
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
hw_rule_channels, formations,
SNDRV_PCM_HW_PARAM_RATE, -1);
if (err < 0)
goto end;
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
hw_rule_rate, formations,
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
if (err < 0)
goto end;
err = amdtp_stream_add_pcm_hw_constraints(s, runtime);
end:
return err;
}
static int
pcm_open(struct snd_pcm_substream *substream)
{
struct snd_bebob *bebob = substream->private_data;
struct snd_bebob_rate_spec *spec = bebob->spec->rate;
unsigned int sampling_rate;
bool internal;
int err;
err = snd_bebob_stream_lock_try(bebob);
if (err < 0)
goto end;
err = pcm_init_hw_params(bebob, substream);
if (err < 0)
goto err_locked;
err = snd_bebob_stream_check_internal_clock(bebob, &internal);
if (err < 0)
goto err_locked;
/*
* When source of clock is internal or any PCM stream are running,
* the available sampling rate is limited at current sampling rate.
*/
if (!internal ||
amdtp_stream_pcm_running(&bebob->tx_stream) ||
amdtp_stream_pcm_running(&bebob->rx_stream)) {
err = spec->get(bebob, &sampling_rate);
if (err < 0) {
dev_err(&bebob->unit->device,
"fail to get sampling rate: %d\n", err);
goto err_locked;
}
substream->runtime->hw.rate_min = sampling_rate;
substream->runtime->hw.rate_max = sampling_rate;
}
snd_pcm_set_sync(substream);
end:
return err;
err_locked:
snd_bebob_stream_lock_release(bebob);
return err;
}
static int
pcm_close(struct snd_pcm_substream *substream)
{
struct snd_bebob *bebob = substream->private_data;
snd_bebob_stream_lock_release(bebob);
return 0;
}
static int
pcm_capture_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_bebob *bebob = substream->private_data;
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
atomic_inc(&bebob->capture_substreams);
amdtp_stream_set_pcm_format(&bebob->tx_stream,
params_format(hw_params));
return snd_pcm_lib_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
}
static int
pcm_playback_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_bebob *bebob = substream->private_data;
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
atomic_inc(&bebob->playback_substreams);
amdtp_stream_set_pcm_format(&bebob->rx_stream,
params_format(hw_params));
return snd_pcm_lib_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
}
static int
pcm_capture_hw_free(struct snd_pcm_substream *substream)
{
struct snd_bebob *bebob = substream->private_data;
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
atomic_dec(&bebob->capture_substreams);
snd_bebob_stream_stop_duplex(bebob);
return snd_pcm_lib_free_vmalloc_buffer(substream);
}
static int
pcm_playback_hw_free(struct snd_pcm_substream *substream)
{
struct snd_bebob *bebob = substream->private_data;
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
atomic_dec(&bebob->playback_substreams);
snd_bebob_stream_stop_duplex(bebob);
return snd_pcm_lib_free_vmalloc_buffer(substream);
}
static int
pcm_capture_prepare(struct snd_pcm_substream *substream)
{
struct snd_bebob *bebob = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
err = snd_bebob_stream_start_duplex(bebob, runtime->rate);
if (err >= 0)
amdtp_stream_pcm_prepare(&bebob->tx_stream);
return err;
}
static int
pcm_playback_prepare(struct snd_pcm_substream *substream)
{
struct snd_bebob *bebob = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
err = snd_bebob_stream_start_duplex(bebob, runtime->rate);
if (err >= 0)
amdtp_stream_pcm_prepare(&bebob->rx_stream);
return err;
}
static int
pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_bebob *bebob = substream->private_data;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
amdtp_stream_pcm_trigger(&bebob->tx_stream, substream);
break;
case SNDRV_PCM_TRIGGER_STOP:
amdtp_stream_pcm_trigger(&bebob->tx_stream, NULL);
break;
default:
return -EINVAL;
}
return 0;
}
static int
pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_bebob *bebob = substream->private_data;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
amdtp_stream_pcm_trigger(&bebob->rx_stream, substream);
break;
case SNDRV_PCM_TRIGGER_STOP:
amdtp_stream_pcm_trigger(&bebob->rx_stream, NULL);
break;
default:
return -EINVAL;
}
return 0;
}
static snd_pcm_uframes_t
pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
{
struct snd_bebob *bebob = sbstrm->private_data;
return amdtp_stream_pcm_pointer(&bebob->tx_stream);
}
static snd_pcm_uframes_t
pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
{
struct snd_bebob *bebob = sbstrm->private_data;
return amdtp_stream_pcm_pointer(&bebob->rx_stream);
}
static const struct snd_pcm_ops pcm_capture_ops = {
.open = pcm_open,
.close = pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = pcm_capture_hw_params,
.hw_free = pcm_capture_hw_free,
.prepare = pcm_capture_prepare,
.trigger = pcm_capture_trigger,
.pointer = pcm_capture_pointer,
.page = snd_pcm_lib_get_vmalloc_page,
};
static const struct snd_pcm_ops pcm_playback_ops = {
.open = pcm_open,
.close = pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = pcm_playback_hw_params,
.hw_free = pcm_playback_hw_free,
.prepare = pcm_playback_prepare,
.trigger = pcm_playback_trigger,
.pointer = pcm_playback_pointer,
.page = snd_pcm_lib_get_vmalloc_page,
.mmap = snd_pcm_lib_mmap_vmalloc,
};
int snd_bebob_create_pcm_devices(struct snd_bebob *bebob)
{
struct snd_pcm *pcm;
int err;
err = snd_pcm_new(bebob->card, bebob->card->driver, 0, 1, 1, &pcm);
if (err < 0)
goto end;
pcm->private_data = bebob;
snprintf(pcm->name, sizeof(pcm->name),
"%s PCM", bebob->card->shortname);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
end:
return err;
}

View File

@ -0,0 +1,196 @@
/*
* bebob_proc.c - a part of driver for BeBoB based devices
*
* Copyright (c) 2013-2014 Takashi Sakamoto
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#include "./bebob.h"
/* contents of information register */
struct hw_info {
u64 manufacturer;
u32 protocol_ver;
u32 bld_ver;
u32 guid[2];
u32 model_id;
u32 model_rev;
u64 fw_date;
u64 fw_time;
u32 fw_id;
u32 fw_ver;
u32 base_addr;
u32 max_size;
u64 bld_date;
u64 bld_time;
/* may not used in product
u64 dbg_date;
u64 dbg_time;
u32 dbg_id;
u32 dbg_version;
*/
} __packed;
static void
proc_read_hw_info(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_bebob *bebob = entry->private_data;
struct hw_info *info;
info = kzalloc(sizeof(struct hw_info), GFP_KERNEL);
if (info == NULL)
return;
if (snd_bebob_read_block(bebob->unit, 0,
info, sizeof(struct hw_info)) < 0)
goto end;
snd_iprintf(buffer, "Manufacturer:\t%.8s\n",
(char *)&info->manufacturer);
snd_iprintf(buffer, "Protocol Ver:\t%d\n", info->protocol_ver);
snd_iprintf(buffer, "Build Ver:\t%d\n", info->bld_ver);
snd_iprintf(buffer, "GUID:\t\t0x%.8X%.8X\n",
info->guid[0], info->guid[1]);
snd_iprintf(buffer, "Model ID:\t0x%02X\n", info->model_id);
snd_iprintf(buffer, "Model Rev:\t%d\n", info->model_rev);
snd_iprintf(buffer, "Firmware Date:\t%.8s\n", (char *)&info->fw_date);
snd_iprintf(buffer, "Firmware Time:\t%.8s\n", (char *)&info->fw_time);
snd_iprintf(buffer, "Firmware ID:\t0x%X\n", info->fw_id);
snd_iprintf(buffer, "Firmware Ver:\t%d\n", info->fw_ver);
snd_iprintf(buffer, "Base Addr:\t0x%X\n", info->base_addr);
snd_iprintf(buffer, "Max Size:\t%d\n", info->max_size);
snd_iprintf(buffer, "Loader Date:\t%.8s\n", (char *)&info->bld_date);
snd_iprintf(buffer, "Loader Time:\t%.8s\n", (char *)&info->bld_time);
end:
kfree(info);
}
static void
proc_read_meters(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_bebob *bebob = entry->private_data;
struct snd_bebob_meter_spec *spec = bebob->spec->meter;
u32 *buf;
unsigned int i, c, channels, size;
if (spec == NULL)
return;
channels = spec->num * 2;
size = channels * sizeof(u32);
buf = kmalloc(size, GFP_KERNEL);
if (buf == NULL)
return;
if (spec->get(bebob, buf, size) < 0)
goto end;
for (i = 0, c = 1; i < channels; i++) {
snd_iprintf(buffer, "%s %d:\t%d\n",
spec->labels[i / 2], c++, buf[i]);
if ((i + 1 < channels - 1) &&
(strcmp(spec->labels[i / 2],
spec->labels[(i + 1) / 2]) != 0))
c = 1;
}
end:
kfree(buf);
}
static void
proc_read_formation(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_bebob *bebob = entry->private_data;
struct snd_bebob_stream_formation *formation;
unsigned int i;
snd_iprintf(buffer, "Output Stream from device:\n");
snd_iprintf(buffer, "\tRate\tPCM\tMIDI\n");
formation = bebob->tx_stream_formations;
for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
snd_iprintf(buffer,
"\t%d\t%d\t%d\n", snd_bebob_rate_table[i],
formation[i].pcm, formation[i].midi);
}
snd_iprintf(buffer, "Input Stream to device:\n");
snd_iprintf(buffer, "\tRate\tPCM\tMIDI\n");
formation = bebob->rx_stream_formations;
for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
snd_iprintf(buffer,
"\t%d\t%d\t%d\n", snd_bebob_rate_table[i],
formation[i].pcm, formation[i].midi);
}
}
static void
proc_read_clock(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_bebob *bebob = entry->private_data;
struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
unsigned int rate, id;
bool internal;
if (rate_spec->get(bebob, &rate) >= 0)
snd_iprintf(buffer, "Sampling rate: %d\n", rate);
if (clk_spec) {
if (clk_spec->get(bebob, &id) >= 0)
snd_iprintf(buffer, "Clock Source: %s\n",
clk_spec->labels[id]);
} else {
if (snd_bebob_stream_check_internal_clock(bebob,
&internal) >= 0)
snd_iprintf(buffer, "Clock Source: %s (MSU-dest: %d)\n",
(internal) ? "Internal" : "External",
bebob->sync_input_plug);
}
}
static void
add_node(struct snd_bebob *bebob, struct snd_info_entry *root, const char *name,
void (*op)(struct snd_info_entry *e, struct snd_info_buffer *b))
{
struct snd_info_entry *entry;
entry = snd_info_create_card_entry(bebob->card, name, root);
if (entry == NULL)
return;
snd_info_set_text_ops(entry, bebob, op);
if (snd_info_register(entry) < 0)
snd_info_free_entry(entry);
}
void snd_bebob_proc_init(struct snd_bebob *bebob)
{
struct snd_info_entry *root;
/*
* All nodes are automatically removed at snd_card_disconnect(),
* by following to link list.
*/
root = snd_info_create_card_entry(bebob->card, "firewire",
bebob->card->proc_root);
if (root == NULL)
return;
root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
if (snd_info_register(root) < 0) {
snd_info_free_entry(root);
return;
}
add_node(bebob, root, "clock", proc_read_clock);
add_node(bebob, root, "firmware", proc_read_hw_info);
add_node(bebob, root, "formation", proc_read_formation);
if (bebob->spec->meter != NULL)
add_node(bebob, root, "meter", proc_read_meters);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,68 @@
/*
* bebob_terratec.c - a part of driver for BeBoB based devices
*
* Copyright (c) 2013-2014 Takashi Sakamoto
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#include "./bebob.h"
static char *const phase88_rack_clk_src_labels[] = {
SND_BEBOB_CLOCK_INTERNAL, "Digital In", "Word Clock"
};
static int
phase88_rack_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
{
unsigned int enable_ext, enable_word;
int err;
err = avc_audio_get_selector(bebob->unit, 0, 0, &enable_ext);
if (err < 0)
goto end;
err = avc_audio_get_selector(bebob->unit, 0, 0, &enable_word);
if (err < 0)
goto end;
*id = (enable_ext & 0x01) | ((enable_word & 0x01) << 1);
end:
return err;
}
static char *const phase24_series_clk_src_labels[] = {
SND_BEBOB_CLOCK_INTERNAL, "Digital In"
};
static int
phase24_series_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
{
return avc_audio_get_selector(bebob->unit, 0, 4, id);
}
static struct snd_bebob_rate_spec phase_series_rate_spec = {
.get = &snd_bebob_stream_get_rate,
.set = &snd_bebob_stream_set_rate,
};
/* PHASE 88 Rack FW */
static struct snd_bebob_clock_spec phase88_rack_clk = {
.num = ARRAY_SIZE(phase88_rack_clk_src_labels),
.labels = phase88_rack_clk_src_labels,
.get = &phase88_rack_clk_src_get,
};
struct snd_bebob_spec phase88_rack_spec = {
.clock = &phase88_rack_clk,
.rate = &phase_series_rate_spec,
.meter = NULL
};
/* 'PHASE 24 FW' and 'PHASE X24 FW' */
static struct snd_bebob_clock_spec phase24_series_clk = {
.num = ARRAY_SIZE(phase24_series_clk_src_labels),
.labels = phase24_series_clk_src_labels,
.get = &phase24_series_clk_src_get,
};
struct snd_bebob_spec phase24_series_spec = {
.clock = &phase24_series_clk,
.rate = &phase_series_rate_spec,
.meter = NULL
};

View File

@ -0,0 +1,50 @@
/*
* bebob_yamaha.c - a part of driver for BeBoB based devices
*
* Copyright (c) 2013-2014 Takashi Sakamoto
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#include "./bebob.h"
/*
* NOTE:
* Yamaha GO44 is not designed to be used as stand-alone mixer. So any streams
* must be accompanied. If changing the state, a LED on the device starts to
* blink and its sync status is false. In this state, the device sounds nothing
* even if streaming. To start streaming at the current sampling rate is only
* way to revocer this state. GO46 is better for stand-alone mixer.
*
* Both of them have a capability to change its sampling rate up to 192.0kHz.
* At 192.0kHz, the device reports 4 PCM-in, 1 MIDI-in, 6 PCM-out, 1 MIDI-out.
* But Yamaha's driver reduce 2 PCM-in, 1 MIDI-in, 2 PCM-out, 1 MIDI-out to use
* 'Extended Stream Format Information Command - Single Request' in 'Additional
* AVC commands' defined by BridgeCo.
* This ALSA driver don't do this because a bit tiresome. Then isochronous
* streaming with many asynchronous transactions brings sounds with noises.
* Unfortunately current 'ffado-mixer' generated many asynchronous transaction
* to observe device's state, mainly check cmp connection and signal format. I
* reccomend users to close ffado-mixer at 192.0kHz if mixer is needless.
*/
static char *const clk_src_labels[] = {SND_BEBOB_CLOCK_INTERNAL, "SPDIF"};
static int
clk_src_get(struct snd_bebob *bebob, unsigned int *id)
{
return avc_audio_get_selector(bebob->unit, 0, 4, id);
}
static struct snd_bebob_clock_spec clock_spec = {
.num = ARRAY_SIZE(clk_src_labels),
.labels = clk_src_labels,
.get = &clk_src_get,
};
static struct snd_bebob_rate_spec rate_spec = {
.get = &snd_bebob_stream_get_rate,
.set = &snd_bebob_stream_set_rate,
};
struct snd_bebob_spec yamaha_go_spec = {
.clock = &clock_spec,
.rate = &rate_spec,
.meter = NULL
};

View File

@ -14,18 +14,28 @@
#include "iso-resources.h" #include "iso-resources.h"
#include "cmp.h" #include "cmp.h"
#define IMPR_SPEED_MASK 0xc0000000 /* MPR common fields */
#define IMPR_SPEED_SHIFT 30 #define MPR_SPEED_MASK 0xc0000000
#define IMPR_XSPEED_MASK 0x00000060 #define MPR_SPEED_SHIFT 30
#define IMPR_XSPEED_SHIFT 5 #define MPR_XSPEED_MASK 0x00000060
#define IMPR_PLUGS_MASK 0x0000001f #define MPR_XSPEED_SHIFT 5
#define MPR_PLUGS_MASK 0x0000001f
#define IPCR_ONLINE 0x80000000 /* PCR common fields */
#define IPCR_BCAST_CONN 0x40000000 #define PCR_ONLINE 0x80000000
#define IPCR_P2P_CONN_MASK 0x3f000000 #define PCR_BCAST_CONN 0x40000000
#define IPCR_P2P_CONN_SHIFT 24 #define PCR_P2P_CONN_MASK 0x3f000000
#define IPCR_CHANNEL_MASK 0x003f0000 #define PCR_P2P_CONN_SHIFT 24
#define IPCR_CHANNEL_SHIFT 16 #define PCR_CHANNEL_MASK 0x003f0000
#define PCR_CHANNEL_SHIFT 16
/* oPCR specific fields */
#define OPCR_XSPEED_MASK 0x00C00000
#define OPCR_XSPEED_SHIFT 22
#define OPCR_SPEED_MASK 0x0000C000
#define OPCR_SPEED_SHIFT 14
#define OPCR_OVERHEAD_ID_MASK 0x00003C00
#define OPCR_OVERHEAD_ID_SHIFT 10
enum bus_reset_handling { enum bus_reset_handling {
ABORT_ON_BUS_RESET, ABORT_ON_BUS_RESET,
@ -39,10 +49,27 @@ void cmp_error(struct cmp_connection *c, const char *fmt, ...)
va_start(va, fmt); va_start(va, fmt);
dev_err(&c->resources.unit->device, "%cPCR%u: %pV", dev_err(&c->resources.unit->device, "%cPCR%u: %pV",
'i', c->pcr_index, &(struct va_format){ fmt, &va }); (c->direction == CMP_INPUT) ? 'i' : 'o',
c->pcr_index, &(struct va_format){ fmt, &va });
va_end(va); va_end(va);
} }
static u64 mpr_address(struct cmp_connection *c)
{
if (c->direction == CMP_INPUT)
return CSR_REGISTER_BASE + CSR_IMPR;
else
return CSR_REGISTER_BASE + CSR_OMPR;
}
static u64 pcr_address(struct cmp_connection *c)
{
if (c->direction == CMP_INPUT)
return CSR_REGISTER_BASE + CSR_IPCR(c->pcr_index);
else
return CSR_REGISTER_BASE + CSR_OPCR(c->pcr_index);
}
static int pcr_modify(struct cmp_connection *c, static int pcr_modify(struct cmp_connection *c,
__be32 (*modify)(struct cmp_connection *c, __be32 old), __be32 (*modify)(struct cmp_connection *c, __be32 old),
int (*check)(struct cmp_connection *c, __be32 pcr), int (*check)(struct cmp_connection *c, __be32 pcr),
@ -58,8 +85,7 @@ static int pcr_modify(struct cmp_connection *c,
err = snd_fw_transaction( err = snd_fw_transaction(
c->resources.unit, TCODE_LOCK_COMPARE_SWAP, c->resources.unit, TCODE_LOCK_COMPARE_SWAP,
CSR_REGISTER_BASE + CSR_IPCR(c->pcr_index), pcr_address(c), buffer, 8,
buffer, 8,
FW_FIXED_GENERATION | c->resources.generation); FW_FIXED_GENERATION | c->resources.generation);
if (err < 0) { if (err < 0) {
@ -88,24 +114,25 @@ static int pcr_modify(struct cmp_connection *c,
* cmp_connection_init - initializes a connection manager * cmp_connection_init - initializes a connection manager
* @c: the connection manager to initialize * @c: the connection manager to initialize
* @unit: a unit of the target device * @unit: a unit of the target device
* @ipcr_index: the index of the iPCR on the target device * @pcr_index: the index of the iPCR/oPCR on the target device
*/ */
int cmp_connection_init(struct cmp_connection *c, int cmp_connection_init(struct cmp_connection *c,
struct fw_unit *unit, struct fw_unit *unit,
unsigned int ipcr_index) enum cmp_direction direction,
unsigned int pcr_index)
{ {
__be32 impr_be; __be32 mpr_be;
u32 impr; u32 mpr;
int err; int err;
c->direction = direction;
err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
CSR_REGISTER_BASE + CSR_IMPR, mpr_address(c), &mpr_be, 4, 0);
&impr_be, 4, 0);
if (err < 0) if (err < 0)
return err; return err;
impr = be32_to_cpu(impr_be); mpr = be32_to_cpu(mpr_be);
if (ipcr_index >= (impr & IMPR_PLUGS_MASK)) if (pcr_index >= (mpr & MPR_PLUGS_MASK))
return -EINVAL; return -EINVAL;
err = fw_iso_resources_init(&c->resources, unit); err = fw_iso_resources_init(&c->resources, unit);
@ -115,15 +142,35 @@ int cmp_connection_init(struct cmp_connection *c,
c->connected = false; c->connected = false;
mutex_init(&c->mutex); mutex_init(&c->mutex);
c->last_pcr_value = cpu_to_be32(0x80000000); c->last_pcr_value = cpu_to_be32(0x80000000);
c->pcr_index = ipcr_index; c->pcr_index = pcr_index;
c->max_speed = (impr & IMPR_SPEED_MASK) >> IMPR_SPEED_SHIFT; c->max_speed = (mpr & MPR_SPEED_MASK) >> MPR_SPEED_SHIFT;
if (c->max_speed == SCODE_BETA) if (c->max_speed == SCODE_BETA)
c->max_speed += (impr & IMPR_XSPEED_MASK) >> IMPR_XSPEED_SHIFT; c->max_speed += (mpr & MPR_XSPEED_MASK) >> MPR_XSPEED_SHIFT;
return 0; return 0;
} }
EXPORT_SYMBOL(cmp_connection_init); EXPORT_SYMBOL(cmp_connection_init);
/**
* cmp_connection_check_used - check connection is already esablished or not
* @c: the connection manager to be checked
*/
int cmp_connection_check_used(struct cmp_connection *c, bool *used)
{
__be32 pcr;
int err;
err = snd_fw_transaction(
c->resources.unit, TCODE_READ_QUADLET_REQUEST,
pcr_address(c), &pcr, 4, 0);
if (err >= 0)
*used = !!(pcr & cpu_to_be32(PCR_BCAST_CONN |
PCR_P2P_CONN_MASK));
return err;
}
EXPORT_SYMBOL(cmp_connection_check_used);
/** /**
* cmp_connection_destroy - free connection manager resources * cmp_connection_destroy - free connection manager resources
* @c: the connection manager * @c: the connection manager
@ -139,23 +186,70 @@ EXPORT_SYMBOL(cmp_connection_destroy);
static __be32 ipcr_set_modify(struct cmp_connection *c, __be32 ipcr) static __be32 ipcr_set_modify(struct cmp_connection *c, __be32 ipcr)
{ {
ipcr &= ~cpu_to_be32(IPCR_BCAST_CONN | ipcr &= ~cpu_to_be32(PCR_BCAST_CONN |
IPCR_P2P_CONN_MASK | PCR_P2P_CONN_MASK |
IPCR_CHANNEL_MASK); PCR_CHANNEL_MASK);
ipcr |= cpu_to_be32(1 << IPCR_P2P_CONN_SHIFT); ipcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT);
ipcr |= cpu_to_be32(c->resources.channel << IPCR_CHANNEL_SHIFT); ipcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT);
return ipcr; return ipcr;
} }
static int ipcr_set_check(struct cmp_connection *c, __be32 ipcr) static int get_overhead_id(struct cmp_connection *c)
{ {
if (ipcr & cpu_to_be32(IPCR_BCAST_CONN | int id;
IPCR_P2P_CONN_MASK)) {
/*
* apply "oPCR overhead ID encoding"
* the encoding table can convert up to 512.
* here the value over 512 is converted as the same way as 512.
*/
for (id = 1; id < 16; id++) {
if (c->resources.bandwidth_overhead < (id << 5))
break;
}
if (id == 16)
id = 0;
return id;
}
static __be32 opcr_set_modify(struct cmp_connection *c, __be32 opcr)
{
unsigned int spd, xspd;
/* generate speed and extended speed field value */
if (c->speed > SCODE_400) {
spd = SCODE_800;
xspd = c->speed - SCODE_800;
} else {
spd = c->speed;
xspd = 0;
}
opcr &= ~cpu_to_be32(PCR_BCAST_CONN |
PCR_P2P_CONN_MASK |
OPCR_XSPEED_MASK |
PCR_CHANNEL_MASK |
OPCR_SPEED_MASK |
OPCR_OVERHEAD_ID_MASK);
opcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT);
opcr |= cpu_to_be32(xspd << OPCR_XSPEED_SHIFT);
opcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT);
opcr |= cpu_to_be32(spd << OPCR_SPEED_SHIFT);
opcr |= cpu_to_be32(get_overhead_id(c) << OPCR_OVERHEAD_ID_SHIFT);
return opcr;
}
static int pcr_set_check(struct cmp_connection *c, __be32 pcr)
{
if (pcr & cpu_to_be32(PCR_BCAST_CONN |
PCR_P2P_CONN_MASK)) {
cmp_error(c, "plug is already in use\n"); cmp_error(c, "plug is already in use\n");
return -EBUSY; return -EBUSY;
} }
if (!(ipcr & cpu_to_be32(IPCR_ONLINE))) { if (!(pcr & cpu_to_be32(PCR_ONLINE))) {
cmp_error(c, "plug is not on-line\n"); cmp_error(c, "plug is not on-line\n");
return -ECONNREFUSED; return -ECONNREFUSED;
} }
@ -170,9 +264,9 @@ static int ipcr_set_check(struct cmp_connection *c, __be32 ipcr)
* *
* This function establishes a point-to-point connection from the local * This function establishes a point-to-point connection from the local
* computer to the target by allocating isochronous resources (channel and * computer to the target by allocating isochronous resources (channel and
* bandwidth) and setting the target's input plug control register. When this * bandwidth) and setting the target's input/output plug control register.
* function succeeds, the caller is responsible for starting transmitting * When this function succeeds, the caller is responsible for starting
* packets. * transmitting packets.
*/ */
int cmp_connection_establish(struct cmp_connection *c, int cmp_connection_establish(struct cmp_connection *c,
unsigned int max_payload_bytes) unsigned int max_payload_bytes)
@ -193,8 +287,13 @@ int cmp_connection_establish(struct cmp_connection *c,
if (err < 0) if (err < 0)
goto err_mutex; goto err_mutex;
err = pcr_modify(c, ipcr_set_modify, ipcr_set_check, if (c->direction == CMP_OUTPUT)
ABORT_ON_BUS_RESET); err = pcr_modify(c, opcr_set_modify, pcr_set_check,
ABORT_ON_BUS_RESET);
else
err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
ABORT_ON_BUS_RESET);
if (err == -EAGAIN) { if (err == -EAGAIN) {
fw_iso_resources_free(&c->resources); fw_iso_resources_free(&c->resources);
goto retry_after_bus_reset; goto retry_after_bus_reset;
@ -221,8 +320,8 @@ EXPORT_SYMBOL(cmp_connection_establish);
* cmp_connection_update - update the connection after a bus reset * cmp_connection_update - update the connection after a bus reset
* @c: the connection manager * @c: the connection manager
* *
* This function must be called from the driver's .update handler to reestablish * This function must be called from the driver's .update handler to
* any connection that might have been active. * reestablish any connection that might have been active.
* *
* Returns zero on success, or a negative error code. On an error, the * Returns zero on success, or a negative error code. On an error, the
* connection is broken and the caller must stop transmitting iso packets. * connection is broken and the caller must stop transmitting iso packets.
@ -242,8 +341,13 @@ int cmp_connection_update(struct cmp_connection *c)
if (err < 0) if (err < 0)
goto err_unconnect; goto err_unconnect;
err = pcr_modify(c, ipcr_set_modify, ipcr_set_check, if (c->direction == CMP_OUTPUT)
SUCCEED_ON_BUS_RESET); err = pcr_modify(c, opcr_set_modify, pcr_set_check,
SUCCEED_ON_BUS_RESET);
else
err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
SUCCEED_ON_BUS_RESET);
if (err < 0) if (err < 0)
goto err_resources; goto err_resources;
@ -261,19 +365,18 @@ int cmp_connection_update(struct cmp_connection *c)
} }
EXPORT_SYMBOL(cmp_connection_update); EXPORT_SYMBOL(cmp_connection_update);
static __be32 pcr_break_modify(struct cmp_connection *c, __be32 pcr)
static __be32 ipcr_break_modify(struct cmp_connection *c, __be32 ipcr)
{ {
return ipcr & ~cpu_to_be32(IPCR_BCAST_CONN | IPCR_P2P_CONN_MASK); return pcr & ~cpu_to_be32(PCR_BCAST_CONN | PCR_P2P_CONN_MASK);
} }
/** /**
* cmp_connection_break - break the connection to the target * cmp_connection_break - break the connection to the target
* @c: the connection manager * @c: the connection manager
* *
* This function deactives the connection in the target's input plug control * This function deactives the connection in the target's input/output plug
* register, and frees the isochronous resources of the connection. Before * control register, and frees the isochronous resources of the connection.
* calling this function, the caller should cease transmitting packets. * Before calling this function, the caller should cease transmitting packets.
*/ */
void cmp_connection_break(struct cmp_connection *c) void cmp_connection_break(struct cmp_connection *c)
{ {
@ -286,7 +389,7 @@ void cmp_connection_break(struct cmp_connection *c)
return; return;
} }
err = pcr_modify(c, ipcr_break_modify, NULL, SUCCEED_ON_BUS_RESET); err = pcr_modify(c, pcr_break_modify, NULL, SUCCEED_ON_BUS_RESET);
if (err < 0) if (err < 0)
cmp_error(c, "plug is still connected\n"); cmp_error(c, "plug is still connected\n");

View File

@ -7,12 +7,17 @@
struct fw_unit; struct fw_unit;
enum cmp_direction {
CMP_INPUT = 0,
CMP_OUTPUT,
};
/** /**
* struct cmp_connection - manages an isochronous connection to a device * struct cmp_connection - manages an isochronous connection to a device
* @speed: the connection's actual speed * @speed: the connection's actual speed
* *
* This structure manages (using CMP) an isochronous stream from the local * This structure manages (using CMP) an isochronous stream between the local
* computer to a device's input plug (iPCR). * computer and a device's input plug (iPCR) and output plug (oPCR).
* *
* There is no corresponding oPCR created on the local computer, so it is not * There is no corresponding oPCR created on the local computer, so it is not
* possible to overlay connections on top of this one. * possible to overlay connections on top of this one.
@ -26,11 +31,14 @@ struct cmp_connection {
__be32 last_pcr_value; __be32 last_pcr_value;
unsigned int pcr_index; unsigned int pcr_index;
unsigned int max_speed; unsigned int max_speed;
enum cmp_direction direction;
}; };
int cmp_connection_init(struct cmp_connection *connection, int cmp_connection_init(struct cmp_connection *connection,
struct fw_unit *unit, struct fw_unit *unit,
unsigned int ipcr_index); enum cmp_direction direction,
unsigned int pcr_index);
int cmp_connection_check_used(struct cmp_connection *connection, bool *used);
void cmp_connection_destroy(struct cmp_connection *connection); void cmp_connection_destroy(struct cmp_connection *connection);
int cmp_connection_establish(struct cmp_connection *connection, int cmp_connection_establish(struct cmp_connection *connection,

View File

@ -51,7 +51,7 @@ struct dice {
wait_queue_head_t hwdep_wait; wait_queue_head_t hwdep_wait;
u32 notification_bits; u32 notification_bits;
struct fw_iso_resources resources; struct fw_iso_resources resources;
struct amdtp_out_stream stream; struct amdtp_stream stream;
}; };
MODULE_DESCRIPTION("DICE driver"); MODULE_DESCRIPTION("DICE driver");
@ -420,22 +420,7 @@ static int dice_open(struct snd_pcm_substream *substream)
if (err < 0) if (err < 0)
goto err_lock; goto err_lock;
err = snd_pcm_hw_constraint_step(runtime, 0, err = amdtp_stream_add_pcm_hw_constraints(&dice->stream, runtime);
SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 32);
if (err < 0)
goto err_lock;
err = snd_pcm_hw_constraint_step(runtime, 0,
SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32);
if (err < 0)
goto err_lock;
err = snd_pcm_hw_constraint_minmax(runtime,
SNDRV_PCM_HW_PARAM_PERIOD_TIME,
5000, UINT_MAX);
if (err < 0)
goto err_lock;
err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
if (err < 0) if (err < 0)
goto err_lock; goto err_lock;
@ -460,17 +445,17 @@ static int dice_stream_start_packets(struct dice *dice)
{ {
int err; int err;
if (amdtp_out_stream_running(&dice->stream)) if (amdtp_stream_running(&dice->stream))
return 0; return 0;
err = amdtp_out_stream_start(&dice->stream, dice->resources.channel, err = amdtp_stream_start(&dice->stream, dice->resources.channel,
fw_parent_device(dice->unit)->max_speed); fw_parent_device(dice->unit)->max_speed);
if (err < 0) if (err < 0)
return err; return err;
err = dice_enable_set(dice); err = dice_enable_set(dice);
if (err < 0) { if (err < 0) {
amdtp_out_stream_stop(&dice->stream); amdtp_stream_stop(&dice->stream);
return err; return err;
} }
@ -484,7 +469,7 @@ static int dice_stream_start(struct dice *dice)
if (!dice->resources.allocated) { if (!dice->resources.allocated) {
err = fw_iso_resources_allocate(&dice->resources, err = fw_iso_resources_allocate(&dice->resources,
amdtp_out_stream_get_max_payload(&dice->stream), amdtp_stream_get_max_payload(&dice->stream),
fw_parent_device(dice->unit)->max_speed); fw_parent_device(dice->unit)->max_speed);
if (err < 0) if (err < 0)
goto error; goto error;
@ -516,9 +501,9 @@ static int dice_stream_start(struct dice *dice)
static void dice_stream_stop_packets(struct dice *dice) static void dice_stream_stop_packets(struct dice *dice)
{ {
if (amdtp_out_stream_running(&dice->stream)) { if (amdtp_stream_running(&dice->stream)) {
dice_enable_clear(dice); dice_enable_clear(dice);
amdtp_out_stream_stop(&dice->stream); amdtp_stream_stop(&dice->stream);
} }
} }
@ -563,7 +548,7 @@ static int dice_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params) struct snd_pcm_hw_params *hw_params)
{ {
struct dice *dice = substream->private_data; struct dice *dice = substream->private_data;
unsigned int rate_index, mode; unsigned int rate_index, mode, rate, channels, i;
int err; int err;
mutex_lock(&dice->mutex); mutex_lock(&dice->mutex);
@ -575,18 +560,39 @@ static int dice_hw_params(struct snd_pcm_substream *substream,
if (err < 0) if (err < 0)
return err; return err;
rate_index = rate_to_index(params_rate(hw_params)); rate = params_rate(hw_params);
rate_index = rate_to_index(rate);
err = dice_change_rate(dice, rate_index << CLOCK_RATE_SHIFT); err = dice_change_rate(dice, rate_index << CLOCK_RATE_SHIFT);
if (err < 0) if (err < 0)
return err; return err;
/*
* 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 PCM buffer size should be aligned to SYT_INTERVAL.
*/
channels = params_channels(hw_params);
if (rate_index > 4) {
if (channels > AMDTP_MAX_CHANNELS_FOR_PCM / 2) {
err = -ENOSYS;
return err;
}
for (i = 0; i < channels; i++) {
dice->stream.pcm_positions[i * 2] = i;
dice->stream.pcm_positions[i * 2 + 1] = i + channels;
}
rate /= 2;
channels *= 2;
}
mode = rate_index_to_mode(rate_index); mode = rate_index_to_mode(rate_index);
amdtp_out_stream_set_parameters(&dice->stream, amdtp_stream_set_parameters(&dice->stream, rate, channels,
params_rate(hw_params), dice->rx_midi_ports[mode]);
params_channels(hw_params), amdtp_stream_set_pcm_format(&dice->stream,
dice->rx_midi_ports[mode]); params_format(hw_params));
amdtp_out_stream_set_pcm_format(&dice->stream,
params_format(hw_params));
return 0; return 0;
} }
@ -609,7 +615,7 @@ static int dice_prepare(struct snd_pcm_substream *substream)
mutex_lock(&dice->mutex); mutex_lock(&dice->mutex);
if (amdtp_out_streaming_error(&dice->stream)) if (amdtp_streaming_error(&dice->stream))
dice_stream_stop_packets(dice); dice_stream_stop_packets(dice);
err = dice_stream_start(dice); err = dice_stream_start(dice);
@ -620,7 +626,7 @@ static int dice_prepare(struct snd_pcm_substream *substream)
mutex_unlock(&dice->mutex); mutex_unlock(&dice->mutex);
amdtp_out_stream_pcm_prepare(&dice->stream); amdtp_stream_pcm_prepare(&dice->stream);
return 0; return 0;
} }
@ -640,7 +646,7 @@ static int dice_trigger(struct snd_pcm_substream *substream, int cmd)
default: default:
return -EINVAL; return -EINVAL;
} }
amdtp_out_stream_pcm_trigger(&dice->stream, pcm); amdtp_stream_pcm_trigger(&dice->stream, pcm);
return 0; return 0;
} }
@ -649,7 +655,7 @@ static snd_pcm_uframes_t dice_pointer(struct snd_pcm_substream *substream)
{ {
struct dice *dice = substream->private_data; struct dice *dice = substream->private_data;
return amdtp_out_stream_pcm_pointer(&dice->stream); return amdtp_stream_pcm_pointer(&dice->stream);
} }
static int dice_create_pcm(struct dice *dice) static int dice_create_pcm(struct dice *dice)
@ -1104,7 +1110,7 @@ static void dice_card_free(struct snd_card *card)
{ {
struct dice *dice = card->private_data; struct dice *dice = card->private_data;
amdtp_out_stream_destroy(&dice->stream); amdtp_stream_destroy(&dice->stream);
fw_core_remove_address_handler(&dice->notification_handler); fw_core_remove_address_handler(&dice->notification_handler);
mutex_destroy(&dice->mutex); mutex_destroy(&dice->mutex);
} }
@ -1360,8 +1366,8 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
goto err_owner; goto err_owner;
dice->resources.channels_mask = 0x00000000ffffffffuLL; dice->resources.channels_mask = 0x00000000ffffffffuLL;
err = amdtp_out_stream_init(&dice->stream, unit, err = amdtp_stream_init(&dice->stream, unit, AMDTP_OUT_STREAM,
CIP_BLOCKING | CIP_HI_DUALWIRE); CIP_BLOCKING);
if (err < 0) if (err < 0)
goto err_resources; goto err_resources;
@ -1417,7 +1423,7 @@ static void dice_remove(struct fw_unit *unit)
{ {
struct dice *dice = dev_get_drvdata(&unit->device); struct dice *dice = dev_get_drvdata(&unit->device);
amdtp_out_stream_pcm_abort(&dice->stream); amdtp_stream_pcm_abort(&dice->stream);
snd_card_disconnect(dice->card); snd_card_disconnect(dice->card);
@ -1443,7 +1449,7 @@ static void dice_bus_reset(struct fw_unit *unit)
* to stop so that the application can restart them in an orderly * to stop so that the application can restart them in an orderly
* manner. * manner.
*/ */
amdtp_out_stream_pcm_abort(&dice->stream); amdtp_stream_pcm_abort(&dice->stream);
mutex_lock(&dice->mutex); mutex_lock(&dice->mutex);

View File

@ -10,12 +10,14 @@
#include <linux/firewire-constants.h> #include <linux/firewire-constants.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/delay.h> #include <linux/delay.h>
#include "fcp.h" #include "fcp.h"
#include "lib.h" #include "lib.h"
#include "amdtp.h"
#define CTS_AVC 0x00 #define CTS_AVC 0x00
@ -23,6 +25,158 @@
#define ERROR_DELAY_MS 5 #define ERROR_DELAY_MS 5
#define FCP_TIMEOUT_MS 125 #define FCP_TIMEOUT_MS 125
int avc_general_set_sig_fmt(struct fw_unit *unit, unsigned int rate,
enum avc_general_plug_dir dir,
unsigned short pid)
{
unsigned int sfc;
u8 *buf;
bool flag;
int err;
flag = false;
for (sfc = 0; sfc < CIP_SFC_COUNT; sfc++) {
if (amdtp_rate_table[sfc] == rate) {
flag = true;
break;
}
}
if (!flag)
return -EINVAL;
buf = kzalloc(8, GFP_KERNEL);
if (buf == NULL)
return -ENOMEM;
buf[0] = 0x00; /* AV/C CONTROL */
buf[1] = 0xff; /* UNIT */
if (dir == AVC_GENERAL_PLUG_DIR_IN)
buf[2] = 0x19; /* INPUT PLUG SIGNAL FORMAT */
else
buf[2] = 0x18; /* OUTPUT PLUG SIGNAL FORMAT */
buf[3] = 0xff & pid; /* plug id */
buf[4] = 0x90; /* EOH_1, Form_1, FMT. AM824 */
buf[5] = 0x07 & sfc; /* FDF-hi. AM824, frequency */
buf[6] = 0xff; /* FDF-mid. AM824, SYT hi (not used)*/
buf[7] = 0xff; /* FDF-low. AM824, SYT lo (not used) */
/* do transaction and check buf[1-5] are the same against command */
err = fcp_avc_transaction(unit, buf, 8, buf, 8,
BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5));
if (err >= 0 && err < 8)
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
else if (buf[0] == 0x0a) /* REJECTED */
err = -EINVAL;
if (err < 0)
goto end;
err = 0;
end:
kfree(buf);
return err;
}
EXPORT_SYMBOL(avc_general_set_sig_fmt);
int avc_general_get_sig_fmt(struct fw_unit *unit, unsigned int *rate,
enum avc_general_plug_dir dir,
unsigned short pid)
{
unsigned int sfc;
u8 *buf;
int err;
buf = kzalloc(8, GFP_KERNEL);
if (buf == NULL)
return -ENOMEM;
buf[0] = 0x01; /* AV/C STATUS */
buf[1] = 0xff; /* Unit */
if (dir == AVC_GENERAL_PLUG_DIR_IN)
buf[2] = 0x19; /* INPUT PLUG SIGNAL FORMAT */
else
buf[2] = 0x18; /* OUTPUT PLUG SIGNAL FORMAT */
buf[3] = 0xff & pid; /* plug id */
buf[4] = 0x90; /* EOH_1, Form_1, FMT. AM824 */
buf[5] = 0xff; /* FDF-hi. AM824, frequency */
buf[6] = 0xff; /* FDF-mid. AM824, SYT hi (not used) */
buf[7] = 0xff; /* FDF-low. AM824, SYT lo (not used) */
/* do transaction and check buf[1-4] are the same against command */
err = fcp_avc_transaction(unit, buf, 8, buf, 8,
BIT(1) | BIT(2) | BIT(3) | BIT(4));
if (err >= 0 && err < 8)
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
else if (buf[0] == 0x0a) /* REJECTED */
err = -EINVAL;
else if (buf[0] == 0x0b) /* IN TRANSITION */
err = -EAGAIN;
if (err < 0)
goto end;
/* check sfc field and pick up rate */
sfc = 0x07 & buf[5];
if (sfc >= CIP_SFC_COUNT) {
err = -EAGAIN; /* also in transition */
goto end;
}
*rate = amdtp_rate_table[sfc];
err = 0;
end:
kfree(buf);
return err;
}
EXPORT_SYMBOL(avc_general_get_sig_fmt);
int avc_general_get_plug_info(struct fw_unit *unit, unsigned int subunit_type,
unsigned int subunit_id, unsigned int subfunction,
u8 info[AVC_PLUG_INFO_BUF_BYTES])
{
u8 *buf;
int err;
/* extended subunit in spec.4.2 is not supported */
if ((subunit_type == 0x1E) || (subunit_id == 5))
return -EINVAL;
buf = kzalloc(8, GFP_KERNEL);
if (buf == NULL)
return -ENOMEM;
buf[0] = 0x01; /* AV/C STATUS */
/* UNIT or Subunit, Functionblock */
buf[1] = ((subunit_type & 0x1f) << 3) | (subunit_id & 0x7);
buf[2] = 0x02; /* PLUG INFO */
buf[3] = 0xff & subfunction;
err = fcp_avc_transaction(unit, buf, 8, buf, 8, BIT(1) | BIT(2));
if (err >= 0 && err < 8)
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
else if (buf[0] == 0x0a) /* REJECTED */
err = -EINVAL;
else if (buf[0] == 0x0b) /* IN TRANSITION */
err = -EAGAIN;
if (err < 0)
goto end;
info[0] = buf[4];
info[1] = buf[5];
info[2] = buf[6];
info[3] = buf[7];
err = 0;
end:
kfree(buf);
return err;
}
EXPORT_SYMBOL(avc_general_get_plug_info);
static DEFINE_SPINLOCK(transactions_lock); static DEFINE_SPINLOCK(transactions_lock);
static LIST_HEAD(transactions); static LIST_HEAD(transactions);
@ -30,6 +184,7 @@ enum fcp_state {
STATE_PENDING, STATE_PENDING,
STATE_BUS_RESET, STATE_BUS_RESET,
STATE_COMPLETE, STATE_COMPLETE,
STATE_DEFERRED,
}; };
struct fcp_transaction { struct fcp_transaction {
@ -40,6 +195,7 @@ struct fcp_transaction {
unsigned int response_match_bytes; unsigned int response_match_bytes;
enum fcp_state state; enum fcp_state state;
wait_queue_head_t wait; wait_queue_head_t wait;
bool deferrable;
}; };
/** /**
@ -62,8 +218,6 @@ struct fcp_transaction {
* *
* @command and @response can point to the same buffer. * @command and @response can point to the same buffer.
* *
* Asynchronous operation (INTERIM, NOTIFY) is not supported at the moment.
*
* Returns the actual size of the response frame, or a negative error code. * Returns the actual size of the response frame, or a negative error code.
*/ */
int fcp_avc_transaction(struct fw_unit *unit, int fcp_avc_transaction(struct fw_unit *unit,
@ -81,6 +235,9 @@ int fcp_avc_transaction(struct fw_unit *unit,
t.state = STATE_PENDING; t.state = STATE_PENDING;
init_waitqueue_head(&t.wait); init_waitqueue_head(&t.wait);
if (*(const u8 *)command == 0x00 || *(const u8 *)command == 0x03)
t.deferrable = true;
spin_lock_irq(&transactions_lock); spin_lock_irq(&transactions_lock);
list_add_tail(&t.list, &transactions); list_add_tail(&t.list, &transactions);
spin_unlock_irq(&transactions_lock); spin_unlock_irq(&transactions_lock);
@ -93,11 +250,21 @@ int fcp_avc_transaction(struct fw_unit *unit,
(void *)command, command_size, 0); (void *)command, command_size, 0);
if (ret < 0) if (ret < 0)
break; break;
deferred:
wait_event_timeout(t.wait, t.state != STATE_PENDING, wait_event_timeout(t.wait, t.state != STATE_PENDING,
msecs_to_jiffies(FCP_TIMEOUT_MS)); msecs_to_jiffies(FCP_TIMEOUT_MS));
if (t.state == STATE_COMPLETE) { if (t.state == STATE_DEFERRED) {
/*
* 'AV/C General Specification' define no time limit
* on command completion once an INTERIM response has
* been sent. but we promise to finish this function
* for a caller. Here we use FCP_TIMEOUT_MS for next
* interval. This is not in the specification.
*/
t.state = STATE_PENDING;
goto deferred;
} else if (t.state == STATE_COMPLETE) {
ret = t.response_size; ret = t.response_size;
break; break;
} else if (t.state == STATE_BUS_RESET) { } else if (t.state == STATE_BUS_RESET) {
@ -132,7 +299,8 @@ void fcp_bus_reset(struct fw_unit *unit)
spin_lock_irq(&transactions_lock); spin_lock_irq(&transactions_lock);
list_for_each_entry(t, &transactions, list) { list_for_each_entry(t, &transactions, list) {
if (t->unit == unit && if (t->unit == unit &&
t->state == STATE_PENDING) { (t->state == STATE_PENDING ||
t->state == STATE_DEFERRED)) {
t->state = STATE_BUS_RESET; t->state = STATE_BUS_RESET;
wake_up(&t->wait); wake_up(&t->wait);
} }
@ -186,10 +354,15 @@ static void fcp_response(struct fw_card *card, struct fw_request *request,
if (t->state == STATE_PENDING && if (t->state == STATE_PENDING &&
is_matching_response(t, data, length)) { is_matching_response(t, data, length)) {
t->state = STATE_COMPLETE; if (t->deferrable && *(const u8 *)data == 0x0f) {
t->response_size = min((unsigned int)length, t->state = STATE_DEFERRED;
t->response_size); } else {
memcpy(t->response_buffer, data, t->response_size); t->state = STATE_COMPLETE;
t->response_size = min_t(unsigned int, length,
t->response_size);
memcpy(t->response_buffer, data,
t->response_size);
}
wake_up(&t->wait); wake_up(&t->wait);
} }
} }

View File

@ -1,8 +1,29 @@
#ifndef SOUND_FIREWIRE_FCP_H_INCLUDED #ifndef SOUND_FIREWIRE_FCP_H_INCLUDED
#define SOUND_FIREWIRE_FCP_H_INCLUDED #define SOUND_FIREWIRE_FCP_H_INCLUDED
#define AVC_PLUG_INFO_BUF_BYTES 4
struct fw_unit; struct fw_unit;
/*
* AV/C Digital Interface Command Set General Specification 4.2
* (Sep 2004, 1394TA)
*/
enum avc_general_plug_dir {
AVC_GENERAL_PLUG_DIR_IN = 0,
AVC_GENERAL_PLUG_DIR_OUT = 1,
AVC_GENERAL_PLUG_DIR_COUNT
};
int avc_general_set_sig_fmt(struct fw_unit *unit, unsigned int rate,
enum avc_general_plug_dir dir,
unsigned short plug);
int avc_general_get_sig_fmt(struct fw_unit *unit, unsigned int *rate,
enum avc_general_plug_dir dir,
unsigned short plug);
int avc_general_get_plug_info(struct fw_unit *unit, unsigned int subunit_type,
unsigned int subunit_id, unsigned int subfunction,
u8 info[AVC_PLUG_INFO_BUF_BYTES]);
int fcp_avc_transaction(struct fw_unit *unit, int fcp_avc_transaction(struct fw_unit *unit,
const void *command, unsigned int command_size, const void *command, unsigned int command_size,
void *response, unsigned int response_size, void *response, unsigned int response_size,

View File

@ -0,0 +1,4 @@
snd-fireworks-objs := fireworks_transaction.o fireworks_command.o \
fireworks_stream.o fireworks_proc.o fireworks_midi.o \
fireworks_pcm.o fireworks_hwdep.o fireworks.o
obj-m += snd-fireworks.o

View File

@ -0,0 +1,353 @@
/*
* fireworks.c - a part of driver for Fireworks based devices
*
* Copyright (c) 2009-2010 Clemens Ladisch
* Copyright (c) 2013-2014 Takashi Sakamoto
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
/*
* Fireworks is a board module which Echo Audio produced. This module consists
* of three chipsets:
* - Communication chipset for IEEE1394 PHY/Link and IEC 61883-1/6
* - DSP or/and FPGA for signal processing
* - Flash Memory to store firmwares
*/
#include "fireworks.h"
MODULE_DESCRIPTION("Echo Fireworks driver");
MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
MODULE_LICENSE("GPL v2");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
unsigned int snd_efw_resp_buf_size = 1024;
bool snd_efw_resp_buf_debug = false;
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "card index");
module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "enable Fireworks sound card");
module_param_named(resp_buf_size, snd_efw_resp_buf_size, uint, 0444);
MODULE_PARM_DESC(resp_buf_size,
"response buffer size (max 4096, default 1024)");
module_param_named(resp_buf_debug, snd_efw_resp_buf_debug, bool, 0444);
MODULE_PARM_DESC(resp_buf_debug, "store all responses to buffer");
static DEFINE_MUTEX(devices_mutex);
static DECLARE_BITMAP(devices_used, SNDRV_CARDS);
#define VENDOR_LOUD 0x000ff2
#define MODEL_MACKIE_400F 0x00400f
#define MODEL_MACKIE_1200F 0x01200f
#define VENDOR_ECHO 0x001486
#define MODEL_ECHO_AUDIOFIRE_12 0x00af12
#define MODEL_ECHO_AUDIOFIRE_12HD 0x0af12d
#define MODEL_ECHO_AUDIOFIRE_12_APPLE 0x0af12a
/* This is applied for AudioFire8 (until 2009 July) */
#define MODEL_ECHO_AUDIOFIRE_8 0x000af8
#define MODEL_ECHO_AUDIOFIRE_2 0x000af2
#define MODEL_ECHO_AUDIOFIRE_4 0x000af4
/* AudioFire9 is applied for AudioFire8(since 2009 July) and AudioFirePre8 */
#define MODEL_ECHO_AUDIOFIRE_9 0x000af9
/* unknown as product */
#define MODEL_ECHO_FIREWORKS_8 0x0000f8
#define MODEL_ECHO_FIREWORKS_HDMI 0x00afd1
#define VENDOR_GIBSON 0x00075b
/* for Robot Interface Pack of Dark Fire, Dusk Tiger, Les Paul Standard 2010 */
#define MODEL_GIBSON_RIP 0x00afb2
/* unknown as product */
#define MODEL_GIBSON_GOLDTOP 0x00afb9
/* part of hardware capability flags */
#define FLAG_RESP_ADDR_CHANGABLE 0
static int
get_hardware_info(struct snd_efw *efw)
{
struct fw_device *fw_dev = fw_parent_device(efw->unit);
struct snd_efw_hwinfo *hwinfo;
char version[12] = {0};
int err;
hwinfo = kzalloc(sizeof(struct snd_efw_hwinfo), GFP_KERNEL);
if (hwinfo == NULL)
return -ENOMEM;
err = snd_efw_command_get_hwinfo(efw, hwinfo);
if (err < 0)
goto end;
/* firmware version for communication chipset */
snprintf(version, sizeof(version), "%u.%u",
(hwinfo->arm_version >> 24) & 0xff,
(hwinfo->arm_version >> 16) & 0xff);
efw->firmware_version = hwinfo->arm_version;
strcpy(efw->card->driver, "Fireworks");
strcpy(efw->card->shortname, hwinfo->model_name);
strcpy(efw->card->mixername, hwinfo->model_name);
snprintf(efw->card->longname, sizeof(efw->card->longname),
"%s %s v%s, GUID %08x%08x at %s, S%d",
hwinfo->vendor_name, hwinfo->model_name, version,
hwinfo->guid_hi, hwinfo->guid_lo,
dev_name(&efw->unit->device), 100 << fw_dev->max_speed);
if (hwinfo->flags & BIT(FLAG_RESP_ADDR_CHANGABLE))
efw->resp_addr_changable = true;
efw->supported_sampling_rate = 0;
if ((hwinfo->min_sample_rate <= 22050)
&& (22050 <= hwinfo->max_sample_rate))
efw->supported_sampling_rate |= SNDRV_PCM_RATE_22050;
if ((hwinfo->min_sample_rate <= 32000)
&& (32000 <= hwinfo->max_sample_rate))
efw->supported_sampling_rate |= SNDRV_PCM_RATE_32000;
if ((hwinfo->min_sample_rate <= 44100)
&& (44100 <= hwinfo->max_sample_rate))
efw->supported_sampling_rate |= SNDRV_PCM_RATE_44100;
if ((hwinfo->min_sample_rate <= 48000)
&& (48000 <= hwinfo->max_sample_rate))
efw->supported_sampling_rate |= SNDRV_PCM_RATE_48000;
if ((hwinfo->min_sample_rate <= 88200)
&& (88200 <= hwinfo->max_sample_rate))
efw->supported_sampling_rate |= SNDRV_PCM_RATE_88200;
if ((hwinfo->min_sample_rate <= 96000)
&& (96000 <= hwinfo->max_sample_rate))
efw->supported_sampling_rate |= SNDRV_PCM_RATE_96000;
if ((hwinfo->min_sample_rate <= 176400)
&& (176400 <= hwinfo->max_sample_rate))
efw->supported_sampling_rate |= SNDRV_PCM_RATE_176400;
if ((hwinfo->min_sample_rate <= 192000)
&& (192000 <= hwinfo->max_sample_rate))
efw->supported_sampling_rate |= SNDRV_PCM_RATE_192000;
/* the number of MIDI ports, not of MIDI conformant data channels */
if (hwinfo->midi_out_ports > SND_EFW_MAX_MIDI_OUT_PORTS ||
hwinfo->midi_in_ports > SND_EFW_MAX_MIDI_IN_PORTS) {
err = -EIO;
goto end;
}
efw->midi_out_ports = hwinfo->midi_out_ports;
efw->midi_in_ports = hwinfo->midi_in_ports;
if (hwinfo->amdtp_tx_pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM ||
hwinfo->amdtp_tx_pcm_channels_2x > AMDTP_MAX_CHANNELS_FOR_PCM ||
hwinfo->amdtp_tx_pcm_channels_4x > AMDTP_MAX_CHANNELS_FOR_PCM ||
hwinfo->amdtp_rx_pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM ||
hwinfo->amdtp_rx_pcm_channels_2x > AMDTP_MAX_CHANNELS_FOR_PCM ||
hwinfo->amdtp_rx_pcm_channels_4x > AMDTP_MAX_CHANNELS_FOR_PCM) {
err = -ENOSYS;
goto end;
}
efw->pcm_capture_channels[0] = hwinfo->amdtp_tx_pcm_channels;
efw->pcm_capture_channels[1] = hwinfo->amdtp_tx_pcm_channels_2x;
efw->pcm_capture_channels[2] = hwinfo->amdtp_tx_pcm_channels_4x;
efw->pcm_playback_channels[0] = hwinfo->amdtp_rx_pcm_channels;
efw->pcm_playback_channels[1] = hwinfo->amdtp_rx_pcm_channels_2x;
efw->pcm_playback_channels[2] = hwinfo->amdtp_rx_pcm_channels_4x;
/* Hardware metering. */
if (hwinfo->phys_in_grp_count > HWINFO_MAX_CAPS_GROUPS ||
hwinfo->phys_out_grp_count > HWINFO_MAX_CAPS_GROUPS) {
err = -EIO;
goto end;
}
efw->phys_in = hwinfo->phys_in;
efw->phys_out = hwinfo->phys_out;
efw->phys_in_grp_count = hwinfo->phys_in_grp_count;
efw->phys_out_grp_count = hwinfo->phys_out_grp_count;
memcpy(&efw->phys_in_grps, hwinfo->phys_in_grps,
sizeof(struct snd_efw_phys_grp) * hwinfo->phys_in_grp_count);
memcpy(&efw->phys_out_grps, hwinfo->phys_out_grps,
sizeof(struct snd_efw_phys_grp) * hwinfo->phys_out_grp_count);
end:
kfree(hwinfo);
return err;
}
static void
efw_card_free(struct snd_card *card)
{
struct snd_efw *efw = card->private_data;
if (efw->card_index >= 0) {
mutex_lock(&devices_mutex);
clear_bit(efw->card_index, devices_used);
mutex_unlock(&devices_mutex);
}
mutex_destroy(&efw->mutex);
kfree(efw->resp_buf);
}
static int
efw_probe(struct fw_unit *unit,
const struct ieee1394_device_id *entry)
{
struct snd_card *card;
struct snd_efw *efw;
int card_index, err;
mutex_lock(&devices_mutex);
/* check registered cards */
for (card_index = 0; card_index < SNDRV_CARDS; ++card_index) {
if (!test_bit(card_index, devices_used) && enable[card_index])
break;
}
if (card_index >= SNDRV_CARDS) {
err = -ENOENT;
goto end;
}
err = snd_card_new(&unit->device, index[card_index], id[card_index],
THIS_MODULE, sizeof(struct snd_efw), &card);
if (err < 0)
goto end;
efw = card->private_data;
efw->card_index = card_index;
set_bit(card_index, devices_used);
card->private_free = efw_card_free;
efw->card = card;
efw->unit = unit;
mutex_init(&efw->mutex);
spin_lock_init(&efw->lock);
init_waitqueue_head(&efw->hwdep_wait);
/* prepare response buffer */
snd_efw_resp_buf_size = clamp(snd_efw_resp_buf_size,
SND_EFW_RESPONSE_MAXIMUM_BYTES, 4096U);
efw->resp_buf = kzalloc(snd_efw_resp_buf_size, GFP_KERNEL);
if (efw->resp_buf == NULL) {
err = -ENOMEM;
goto error;
}
efw->pull_ptr = efw->push_ptr = efw->resp_buf;
snd_efw_transaction_add_instance(efw);
err = get_hardware_info(efw);
if (err < 0)
goto error;
if (entry->model_id == MODEL_ECHO_AUDIOFIRE_9)
efw->is_af9 = true;
snd_efw_proc_init(efw);
if (efw->midi_out_ports || efw->midi_in_ports) {
err = snd_efw_create_midi_devices(efw);
if (err < 0)
goto error;
}
err = snd_efw_create_pcm_devices(efw);
if (err < 0)
goto error;
err = snd_efw_create_hwdep_device(efw);
if (err < 0)
goto error;
err = snd_efw_stream_init_duplex(efw);
if (err < 0)
goto error;
err = snd_card_register(card);
if (err < 0) {
snd_efw_stream_destroy_duplex(efw);
goto error;
}
dev_set_drvdata(&unit->device, efw);
end:
mutex_unlock(&devices_mutex);
return err;
error:
snd_efw_transaction_remove_instance(efw);
mutex_unlock(&devices_mutex);
snd_card_free(card);
return err;
}
static void efw_update(struct fw_unit *unit)
{
struct snd_efw *efw = dev_get_drvdata(&unit->device);
snd_efw_transaction_bus_reset(efw->unit);
snd_efw_stream_update_duplex(efw);
}
static void efw_remove(struct fw_unit *unit)
{
struct snd_efw *efw = dev_get_drvdata(&unit->device);
snd_efw_stream_destroy_duplex(efw);
snd_efw_transaction_remove_instance(efw);
snd_card_disconnect(efw->card);
snd_card_free_when_closed(efw->card);
}
static const struct ieee1394_device_id efw_id_table[] = {
SND_EFW_DEV_ENTRY(VENDOR_LOUD, MODEL_MACKIE_400F),
SND_EFW_DEV_ENTRY(VENDOR_LOUD, MODEL_MACKIE_1200F),
SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_8),
SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_12),
SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_12HD),
SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_12_APPLE),
SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_2),
SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_4),
SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_9),
SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_FIREWORKS_8),
SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_FIREWORKS_HDMI),
SND_EFW_DEV_ENTRY(VENDOR_GIBSON, MODEL_GIBSON_RIP),
SND_EFW_DEV_ENTRY(VENDOR_GIBSON, MODEL_GIBSON_GOLDTOP),
{}
};
MODULE_DEVICE_TABLE(ieee1394, efw_id_table);
static struct fw_driver efw_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "snd-fireworks",
.bus = &fw_bus_type,
},
.probe = efw_probe,
.update = efw_update,
.remove = efw_remove,
.id_table = efw_id_table,
};
static int __init snd_efw_init(void)
{
int err;
err = snd_efw_transaction_register();
if (err < 0)
goto end;
err = driver_register(&efw_driver.driver);
if (err < 0)
snd_efw_transaction_unregister();
end:
return err;
}
static void __exit snd_efw_exit(void)
{
snd_efw_transaction_unregister();
driver_unregister(&efw_driver.driver);
mutex_destroy(&devices_mutex);
}
module_init(snd_efw_init);
module_exit(snd_efw_exit);

View File

@ -0,0 +1,233 @@
/*
* fireworks.h - a part of driver for Fireworks based devices
*
* Copyright (c) 2009-2010 Clemens Ladisch
* Copyright (c) 2013-2014 Takashi Sakamoto
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#ifndef SOUND_FIREWORKS_H_INCLUDED
#define SOUND_FIREWORKS_H_INCLUDED
#include <linux/compat.h>
#include <linux/device.h>
#include <linux/firewire.h>
#include <linux/firewire-constants.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/info.h>
#include <sound/rawmidi.h>
#include <sound/pcm_params.h>
#include <sound/firewire.h>
#include <sound/hwdep.h>
#include "../packets-buffer.h"
#include "../iso-resources.h"
#include "../amdtp.h"
#include "../cmp.h"
#include "../lib.h"
#define SND_EFW_MAX_MIDI_OUT_PORTS 2
#define SND_EFW_MAX_MIDI_IN_PORTS 2
#define SND_EFW_MULTIPLIER_MODES 3
#define HWINFO_NAME_SIZE_BYTES 32
#define HWINFO_MAX_CAPS_GROUPS 8
/*
* This should be greater than maximum bytes for EFW response content.
* Currently response against command for isochronous channel mapping is
* confirmed to be the maximum one. But for flexibility, use maximum data
* payload for asynchronous primary packets at S100 (Cable base rate) in
* IEEE Std 1394-1995.
*/
#define SND_EFW_RESPONSE_MAXIMUM_BYTES 0x200U
extern unsigned int snd_efw_resp_buf_size;
extern bool snd_efw_resp_buf_debug;
struct snd_efw_phys_grp {
u8 type; /* see enum snd_efw_grp_type */
u8 count;
} __packed;
struct snd_efw {
struct snd_card *card;
struct fw_unit *unit;
int card_index;
struct mutex mutex;
spinlock_t lock;
/* for transaction */
u32 seqnum;
bool resp_addr_changable;
/* for quirks */
bool is_af9;
u32 firmware_version;
unsigned int midi_in_ports;
unsigned int midi_out_ports;
unsigned int supported_sampling_rate;
unsigned int pcm_capture_channels[SND_EFW_MULTIPLIER_MODES];
unsigned int pcm_playback_channels[SND_EFW_MULTIPLIER_MODES];
struct amdtp_stream *master;
struct amdtp_stream tx_stream;
struct amdtp_stream rx_stream;
struct cmp_connection out_conn;
struct cmp_connection in_conn;
atomic_t capture_substreams;
atomic_t playback_substreams;
/* hardware metering parameters */
unsigned int phys_out;
unsigned int phys_in;
unsigned int phys_out_grp_count;
unsigned int phys_in_grp_count;
struct snd_efw_phys_grp phys_out_grps[HWINFO_MAX_CAPS_GROUPS];
struct snd_efw_phys_grp phys_in_grps[HWINFO_MAX_CAPS_GROUPS];
/* for uapi */
int dev_lock_count;
bool dev_lock_changed;
wait_queue_head_t hwdep_wait;
/* response queue */
u8 *resp_buf;
u8 *pull_ptr;
u8 *push_ptr;
unsigned int resp_queues;
};
int snd_efw_transaction_cmd(struct fw_unit *unit,
const void *cmd, unsigned int size);
int snd_efw_transaction_run(struct fw_unit *unit,
const void *cmd, unsigned int cmd_size,
void *resp, unsigned int resp_size);
int snd_efw_transaction_register(void);
void snd_efw_transaction_unregister(void);
void snd_efw_transaction_bus_reset(struct fw_unit *unit);
void snd_efw_transaction_add_instance(struct snd_efw *efw);
void snd_efw_transaction_remove_instance(struct snd_efw *efw);
struct snd_efw_hwinfo {
u32 flags;
u32 guid_hi;
u32 guid_lo;
u32 type;
u32 version;
char vendor_name[HWINFO_NAME_SIZE_BYTES];
char model_name[HWINFO_NAME_SIZE_BYTES];
u32 supported_clocks;
u32 amdtp_rx_pcm_channels;
u32 amdtp_tx_pcm_channels;
u32 phys_out;
u32 phys_in;
u32 phys_out_grp_count;
struct snd_efw_phys_grp phys_out_grps[HWINFO_MAX_CAPS_GROUPS];
u32 phys_in_grp_count;
struct snd_efw_phys_grp phys_in_grps[HWINFO_MAX_CAPS_GROUPS];
u32 midi_out_ports;
u32 midi_in_ports;
u32 max_sample_rate;
u32 min_sample_rate;
u32 dsp_version;
u32 arm_version;
u32 mixer_playback_channels;
u32 mixer_capture_channels;
u32 fpga_version;
u32 amdtp_rx_pcm_channels_2x;
u32 amdtp_tx_pcm_channels_2x;
u32 amdtp_rx_pcm_channels_4x;
u32 amdtp_tx_pcm_channels_4x;
u32 reserved[16];
} __packed;
enum snd_efw_grp_type {
SND_EFW_CH_TYPE_ANALOG = 0,
SND_EFW_CH_TYPE_SPDIF = 1,
SND_EFW_CH_TYPE_ADAT = 2,
SND_EFW_CH_TYPE_SPDIF_OR_ADAT = 3,
SND_EFW_CH_TYPE_ANALOG_MIRRORING = 4,
SND_EFW_CH_TYPE_HEADPHONES = 5,
SND_EFW_CH_TYPE_I2S = 6,
SND_EFW_CH_TYPE_GUITAR = 7,
SND_EFW_CH_TYPE_PIEZO_GUITAR = 8,
SND_EFW_CH_TYPE_GUITAR_STRING = 9,
SND_EFW_CH_TYPE_VIRTUAL = 0x10000,
SND_EFW_CH_TYPE_DUMMY
};
struct snd_efw_phys_meters {
u32 status; /* guitar state/midi signal/clock input detect */
u32 reserved0;
u32 reserved1;
u32 reserved2;
u32 reserved3;
u32 out_meters;
u32 in_meters;
u32 reserved4;
u32 reserved5;
u32 values[0];
} __packed;
enum snd_efw_clock_source {
SND_EFW_CLOCK_SOURCE_INTERNAL = 0,
SND_EFW_CLOCK_SOURCE_SYTMATCH = 1,
SND_EFW_CLOCK_SOURCE_WORDCLOCK = 2,
SND_EFW_CLOCK_SOURCE_SPDIF = 3,
SND_EFW_CLOCK_SOURCE_ADAT_1 = 4,
SND_EFW_CLOCK_SOURCE_ADAT_2 = 5,
SND_EFW_CLOCK_SOURCE_CONTINUOUS = 6 /* internal variable clock */
};
enum snd_efw_transport_mode {
SND_EFW_TRANSPORT_MODE_WINDOWS = 0,
SND_EFW_TRANSPORT_MODE_IEC61883 = 1,
};
int snd_efw_command_set_resp_addr(struct snd_efw *efw,
u16 addr_high, u32 addr_low);
int snd_efw_command_set_tx_mode(struct snd_efw *efw,
enum snd_efw_transport_mode mode);
int snd_efw_command_get_hwinfo(struct snd_efw *efw,
struct snd_efw_hwinfo *hwinfo);
int snd_efw_command_get_phys_meters(struct snd_efw *efw,
struct snd_efw_phys_meters *meters,
unsigned int len);
int snd_efw_command_get_clock_source(struct snd_efw *efw,
enum snd_efw_clock_source *source);
int snd_efw_command_get_sampling_rate(struct snd_efw *efw, unsigned int *rate);
int snd_efw_command_set_sampling_rate(struct snd_efw *efw, unsigned int rate);
int snd_efw_stream_init_duplex(struct snd_efw *efw);
int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate);
void snd_efw_stream_stop_duplex(struct snd_efw *efw);
void snd_efw_stream_update_duplex(struct snd_efw *efw);
void snd_efw_stream_destroy_duplex(struct snd_efw *efw);
void snd_efw_stream_lock_changed(struct snd_efw *efw);
int snd_efw_stream_lock_try(struct snd_efw *efw);
void snd_efw_stream_lock_release(struct snd_efw *efw);
void snd_efw_proc_init(struct snd_efw *efw);
int snd_efw_create_midi_devices(struct snd_efw *efw);
int snd_efw_create_pcm_devices(struct snd_efw *efw);
int snd_efw_get_multiplier_mode(unsigned int sampling_rate, unsigned int *mode);
int snd_efw_create_hwdep_device(struct snd_efw *efw);
#define SND_EFW_DEV_ENTRY(vendor, model) \
{ \
.match_flags = IEEE1394_MATCH_VENDOR_ID | \
IEEE1394_MATCH_MODEL_ID, \
.vendor_id = vendor,\
.model_id = model \
}
#endif

View File

@ -0,0 +1,372 @@
/*
* fireworks_command.c - a part of driver for Fireworks based devices
*
* Copyright (c) 2013-2014 Takashi Sakamoto
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#include "./fireworks.h"
/*
* This driver uses transaction version 1 or later to use extended hardware
* information. Then too old devices are not available.
*
* Each commands are not required to have continuous sequence numbers. This
* number is just used to match command and response.
*
* This module support a part of commands. Please see FFADO if you want to see
* whole commands. But there are some commands which FFADO don't implement.
*
* Fireworks also supports AV/C general commands and AV/C Stream Format
* Information commands. But this module don't use them.
*/
#define KERNEL_SEQNUM_MIN (SND_EFW_TRANSACTION_USER_SEQNUM_MAX + 2)
#define KERNEL_SEQNUM_MAX ((u32)~0)
/* for clock source and sampling rate */
struct efc_clock {
u32 source;
u32 sampling_rate;
u32 index;
};
/* command categories */
enum efc_category {
EFC_CAT_HWINFO = 0,
EFC_CAT_TRANSPORT = 2,
EFC_CAT_HWCTL = 3,
};
/* hardware info category commands */
enum efc_cmd_hwinfo {
EFC_CMD_HWINFO_GET_CAPS = 0,
EFC_CMD_HWINFO_GET_POLLED = 1,
EFC_CMD_HWINFO_SET_RESP_ADDR = 2
};
enum efc_cmd_transport {
EFC_CMD_TRANSPORT_SET_TX_MODE = 0
};
/* hardware control category commands */
enum efc_cmd_hwctl {
EFC_CMD_HWCTL_SET_CLOCK = 0,
EFC_CMD_HWCTL_GET_CLOCK = 1,
EFC_CMD_HWCTL_IDENTIFY = 5
};
/* return values in response */
enum efr_status {
EFR_STATUS_OK = 0,
EFR_STATUS_BAD = 1,
EFR_STATUS_BAD_COMMAND = 2,
EFR_STATUS_COMM_ERR = 3,
EFR_STATUS_BAD_QUAD_COUNT = 4,
EFR_STATUS_UNSUPPORTED = 5,
EFR_STATUS_1394_TIMEOUT = 6,
EFR_STATUS_DSP_TIMEOUT = 7,
EFR_STATUS_BAD_RATE = 8,
EFR_STATUS_BAD_CLOCK = 9,
EFR_STATUS_BAD_CHANNEL = 10,
EFR_STATUS_BAD_PAN = 11,
EFR_STATUS_FLASH_BUSY = 12,
EFR_STATUS_BAD_MIRROR = 13,
EFR_STATUS_BAD_LED = 14,
EFR_STATUS_BAD_PARAMETER = 15,
EFR_STATUS_INCOMPLETE = 0x80000000
};
static const char *const efr_status_names[] = {
[EFR_STATUS_OK] = "OK",
[EFR_STATUS_BAD] = "bad",
[EFR_STATUS_BAD_COMMAND] = "bad command",
[EFR_STATUS_COMM_ERR] = "comm err",
[EFR_STATUS_BAD_QUAD_COUNT] = "bad quad count",
[EFR_STATUS_UNSUPPORTED] = "unsupported",
[EFR_STATUS_1394_TIMEOUT] = "1394 timeout",
[EFR_STATUS_DSP_TIMEOUT] = "DSP timeout",
[EFR_STATUS_BAD_RATE] = "bad rate",
[EFR_STATUS_BAD_CLOCK] = "bad clock",
[EFR_STATUS_BAD_CHANNEL] = "bad channel",
[EFR_STATUS_BAD_PAN] = "bad pan",
[EFR_STATUS_FLASH_BUSY] = "flash busy",
[EFR_STATUS_BAD_MIRROR] = "bad mirror",
[EFR_STATUS_BAD_LED] = "bad LED",
[EFR_STATUS_BAD_PARAMETER] = "bad parameter",
[EFR_STATUS_BAD_PARAMETER + 1] = "incomplete"
};
static int
efw_transaction(struct snd_efw *efw, unsigned int category,
unsigned int command,
const __be32 *params, unsigned int param_bytes,
const __be32 *resp, unsigned int resp_bytes)
{
struct snd_efw_transaction *header;
__be32 *buf;
u32 seqnum;
unsigned int buf_bytes, cmd_bytes;
int err;
/* calculate buffer size*/
buf_bytes = sizeof(struct snd_efw_transaction) +
max(param_bytes, resp_bytes);
/* keep buffer */
buf = kzalloc(buf_bytes, GFP_KERNEL);
if (buf == NULL)
return -ENOMEM;
/* to keep consistency of sequence number */
spin_lock(&efw->lock);
if ((efw->seqnum < KERNEL_SEQNUM_MIN) ||
(efw->seqnum >= KERNEL_SEQNUM_MAX - 2))
efw->seqnum = KERNEL_SEQNUM_MIN;
else
efw->seqnum += 2;
seqnum = efw->seqnum;
spin_unlock(&efw->lock);
/* fill transaction header fields */
cmd_bytes = sizeof(struct snd_efw_transaction) + param_bytes;
header = (struct snd_efw_transaction *)buf;
header->length = cpu_to_be32(cmd_bytes / sizeof(__be32));
header->version = cpu_to_be32(1);
header->seqnum = cpu_to_be32(seqnum);
header->category = cpu_to_be32(category);
header->command = cpu_to_be32(command);
header->status = 0;
/* fill transaction command parameters */
memcpy(header->params, params, param_bytes);
err = snd_efw_transaction_run(efw->unit, buf, cmd_bytes,
buf, buf_bytes);
if (err < 0)
goto end;
/* check transaction header fields */
if ((be32_to_cpu(header->version) < 1) ||
(be32_to_cpu(header->category) != category) ||
(be32_to_cpu(header->command) != command) ||
(be32_to_cpu(header->status) != EFR_STATUS_OK)) {
dev_err(&efw->unit->device, "EFW command failed [%u/%u]: %s\n",
be32_to_cpu(header->category),
be32_to_cpu(header->command),
efr_status_names[be32_to_cpu(header->status)]);
err = -EIO;
goto end;
}
if (resp == NULL)
goto end;
/* fill transaction response parameters */
memset((void *)resp, 0, resp_bytes);
resp_bytes = min_t(unsigned int, resp_bytes,
be32_to_cpu(header->length) * sizeof(__be32) -
sizeof(struct snd_efw_transaction));
memcpy((void *)resp, &buf[6], resp_bytes);
end:
kfree(buf);
return err;
}
/*
* The address in host system for transaction response is changable when the
* device supports. struct hwinfo.flags includes its flag. The default is
* MEMORY_SPACE_EFW_RESPONSE.
*/
int snd_efw_command_set_resp_addr(struct snd_efw *efw,
u16 addr_high, u32 addr_low)
{
__be32 addr[2];
addr[0] = cpu_to_be32(addr_high);
addr[1] = cpu_to_be32(addr_low);
if (!efw->resp_addr_changable)
return -ENOSYS;
return efw_transaction(efw, EFC_CAT_HWCTL,
EFC_CMD_HWINFO_SET_RESP_ADDR,
addr, sizeof(addr), NULL, 0);
}
/*
* This is for timestamp processing. In Windows mode, all 32bit fields of second
* CIP header in AMDTP transmit packet is used for 'presentation timestamp'. In
* 'no data' packet the value of this field is 0x90ffffff.
*/
int snd_efw_command_set_tx_mode(struct snd_efw *efw,
enum snd_efw_transport_mode mode)
{
__be32 param = cpu_to_be32(mode);
return efw_transaction(efw, EFC_CAT_TRANSPORT,
EFC_CMD_TRANSPORT_SET_TX_MODE,
&param, sizeof(param), NULL, 0);
}
int snd_efw_command_get_hwinfo(struct snd_efw *efw,
struct snd_efw_hwinfo *hwinfo)
{
int err;
err = efw_transaction(efw, EFC_CAT_HWINFO,
EFC_CMD_HWINFO_GET_CAPS,
NULL, 0, (__be32 *)hwinfo, sizeof(*hwinfo));
if (err < 0)
goto end;
be32_to_cpus(&hwinfo->flags);
be32_to_cpus(&hwinfo->guid_hi);
be32_to_cpus(&hwinfo->guid_lo);
be32_to_cpus(&hwinfo->type);
be32_to_cpus(&hwinfo->version);
be32_to_cpus(&hwinfo->supported_clocks);
be32_to_cpus(&hwinfo->amdtp_rx_pcm_channels);
be32_to_cpus(&hwinfo->amdtp_tx_pcm_channels);
be32_to_cpus(&hwinfo->phys_out);
be32_to_cpus(&hwinfo->phys_in);
be32_to_cpus(&hwinfo->phys_out_grp_count);
be32_to_cpus(&hwinfo->phys_in_grp_count);
be32_to_cpus(&hwinfo->midi_out_ports);
be32_to_cpus(&hwinfo->midi_in_ports);
be32_to_cpus(&hwinfo->max_sample_rate);
be32_to_cpus(&hwinfo->min_sample_rate);
be32_to_cpus(&hwinfo->dsp_version);
be32_to_cpus(&hwinfo->arm_version);
be32_to_cpus(&hwinfo->mixer_playback_channels);
be32_to_cpus(&hwinfo->mixer_capture_channels);
be32_to_cpus(&hwinfo->fpga_version);
be32_to_cpus(&hwinfo->amdtp_rx_pcm_channels_2x);
be32_to_cpus(&hwinfo->amdtp_tx_pcm_channels_2x);
be32_to_cpus(&hwinfo->amdtp_rx_pcm_channels_4x);
be32_to_cpus(&hwinfo->amdtp_tx_pcm_channels_4x);
/* ensure terminated */
hwinfo->vendor_name[HWINFO_NAME_SIZE_BYTES - 1] = '\0';
hwinfo->model_name[HWINFO_NAME_SIZE_BYTES - 1] = '\0';
end:
return err;
}
int snd_efw_command_get_phys_meters(struct snd_efw *efw,
struct snd_efw_phys_meters *meters,
unsigned int len)
{
__be32 *buf = (__be32 *)meters;
unsigned int i;
int err;
err = efw_transaction(efw, EFC_CAT_HWINFO,
EFC_CMD_HWINFO_GET_POLLED,
NULL, 0, (__be32 *)meters, len);
if (err >= 0)
for (i = 0; i < len / sizeof(u32); i++)
be32_to_cpus(&buf[i]);
return err;
}
static int
command_get_clock(struct snd_efw *efw, struct efc_clock *clock)
{
int err;
err = efw_transaction(efw, EFC_CAT_HWCTL,
EFC_CMD_HWCTL_GET_CLOCK,
NULL, 0,
(__be32 *)clock, sizeof(struct efc_clock));
if (err >= 0) {
be32_to_cpus(&clock->source);
be32_to_cpus(&clock->sampling_rate);
be32_to_cpus(&clock->index);
}
return err;
}
/* give UINT_MAX if set nothing */
static int
command_set_clock(struct snd_efw *efw,
unsigned int source, unsigned int rate)
{
struct efc_clock clock = {0};
int err;
/* check arguments */
if ((source == UINT_MAX) && (rate == UINT_MAX)) {
err = -EINVAL;
goto end;
}
/* get current status */
err = command_get_clock(efw, &clock);
if (err < 0)
goto end;
/* no need */
if ((clock.source == source) && (clock.sampling_rate == rate))
goto end;
/* set params */
if ((source != UINT_MAX) && (clock.source != source))
clock.source = source;
if ((rate != UINT_MAX) && (clock.sampling_rate != rate))
clock.sampling_rate = rate;
clock.index = 0;
cpu_to_be32s(&clock.source);
cpu_to_be32s(&clock.sampling_rate);
cpu_to_be32s(&clock.index);
err = efw_transaction(efw, EFC_CAT_HWCTL,
EFC_CMD_HWCTL_SET_CLOCK,
(__be32 *)&clock, sizeof(struct efc_clock),
NULL, 0);
if (err < 0)
goto end;
/*
* With firmware version 5.8, just after changing clock state, these
* parameters are not immediately retrieved by get command. In my
* trial, there needs to be 100msec to get changed parameters.
*/
msleep(150);
end:
return err;
}
int snd_efw_command_get_clock_source(struct snd_efw *efw,
enum snd_efw_clock_source *source)
{
int err;
struct efc_clock clock = {0};
err = command_get_clock(efw, &clock);
if (err >= 0)
*source = clock.source;
return err;
}
int snd_efw_command_get_sampling_rate(struct snd_efw *efw, unsigned int *rate)
{
int err;
struct efc_clock clock = {0};
err = command_get_clock(efw, &clock);
if (err >= 0)
*rate = clock.sampling_rate;
return err;
}
int snd_efw_command_set_sampling_rate(struct snd_efw *efw, unsigned int rate)
{
return command_set_clock(efw, UINT_MAX, rate);
}

View File

@ -0,0 +1,298 @@
/*
* fireworks_hwdep.c - a part of driver for Fireworks based devices
*
* Copyright (c) 2013-2014 Takashi Sakamoto
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
/*
* This codes have five functionalities.
*
* 1.get information about firewire node
* 2.get notification about starting/stopping stream
* 3.lock/unlock streaming
* 4.transmit command of EFW transaction
* 5.receive response of EFW transaction
*
*/
#include "fireworks.h"
static long
hwdep_read_resp_buf(struct snd_efw *efw, char __user *buf, long remained,
loff_t *offset)
{
unsigned int length, till_end, type;
struct snd_efw_transaction *t;
long count = 0;
if (remained < sizeof(type) + sizeof(struct snd_efw_transaction))
return -ENOSPC;
/* data type is SNDRV_FIREWIRE_EVENT_EFW_RESPONSE */
type = SNDRV_FIREWIRE_EVENT_EFW_RESPONSE;
if (copy_to_user(buf, &type, sizeof(type)))
return -EFAULT;
remained -= sizeof(type);
buf += sizeof(type);
/* write into buffer as many responses as possible */
while (efw->resp_queues > 0) {
t = (struct snd_efw_transaction *)(efw->pull_ptr);
length = be32_to_cpu(t->length) * sizeof(__be32);
/* confirm enough space for this response */
if (remained < length)
break;
/* copy from ring buffer to user buffer */
while (length > 0) {
till_end = snd_efw_resp_buf_size -
(unsigned int)(efw->pull_ptr - efw->resp_buf);
till_end = min_t(unsigned int, length, till_end);
if (copy_to_user(buf, efw->pull_ptr, till_end))
return -EFAULT;
efw->pull_ptr += till_end;
if (efw->pull_ptr >= efw->resp_buf +
snd_efw_resp_buf_size)
efw->pull_ptr = efw->resp_buf;
length -= till_end;
buf += till_end;
count += till_end;
remained -= till_end;
}
efw->resp_queues--;
}
return count;
}
static long
hwdep_read_locked(struct snd_efw *efw, char __user *buf, long count,
loff_t *offset)
{
union snd_firewire_event event;
memset(&event, 0, sizeof(event));
event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
event.lock_status.status = (efw->dev_lock_count > 0);
efw->dev_lock_changed = false;
count = min_t(long, count, sizeof(event.lock_status));
if (copy_to_user(buf, &event, count))
return -EFAULT;
return count;
}
static long
hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
loff_t *offset)
{
struct snd_efw *efw = hwdep->private_data;
DEFINE_WAIT(wait);
spin_lock_irq(&efw->lock);
while ((!efw->dev_lock_changed) && (efw->resp_queues == 0)) {
prepare_to_wait(&efw->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
spin_unlock_irq(&efw->lock);
schedule();
finish_wait(&efw->hwdep_wait, &wait);
if (signal_pending(current))
return -ERESTARTSYS;
spin_lock_irq(&efw->lock);
}
if (efw->dev_lock_changed)
count = hwdep_read_locked(efw, buf, count, offset);
else if (efw->resp_queues > 0)
count = hwdep_read_resp_buf(efw, buf, count, offset);
spin_unlock_irq(&efw->lock);
return count;
}
static long
hwdep_write(struct snd_hwdep *hwdep, const char __user *data, long count,
loff_t *offset)
{
struct snd_efw *efw = hwdep->private_data;
u32 seqnum;
u8 *buf;
if (count < sizeof(struct snd_efw_transaction) ||
SND_EFW_RESPONSE_MAXIMUM_BYTES < count)
return -EINVAL;
buf = memdup_user(data, count);
if (IS_ERR(buf))
return PTR_ERR(buf);
/* check seqnum is not for kernel-land */
seqnum = be32_to_cpu(((struct snd_efw_transaction *)buf)->seqnum);
if (seqnum > SND_EFW_TRANSACTION_USER_SEQNUM_MAX) {
count = -EINVAL;
goto end;
}
if (snd_efw_transaction_cmd(efw->unit, buf, count) < 0)
count = -EIO;
end:
kfree(buf);
return count;
}
static unsigned int
hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait)
{
struct snd_efw *efw = hwdep->private_data;
unsigned int events;
poll_wait(file, &efw->hwdep_wait, wait);
spin_lock_irq(&efw->lock);
if (efw->dev_lock_changed || (efw->resp_queues > 0))
events = POLLIN | POLLRDNORM;
else
events = 0;
spin_unlock_irq(&efw->lock);
return events | POLLOUT;
}
static int
hwdep_get_info(struct snd_efw *efw, void __user *arg)
{
struct fw_device *dev = fw_parent_device(efw->unit);
struct snd_firewire_get_info info;
memset(&info, 0, sizeof(info));
info.type = SNDRV_FIREWIRE_TYPE_FIREWORKS;
info.card = dev->card->index;
*(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
*(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
strlcpy(info.device_name, dev_name(&dev->device),
sizeof(info.device_name));
if (copy_to_user(arg, &info, sizeof(info)))
return -EFAULT;
return 0;
}
static int
hwdep_lock(struct snd_efw *efw)
{
int err;
spin_lock_irq(&efw->lock);
if (efw->dev_lock_count == 0) {
efw->dev_lock_count = -1;
err = 0;
} else {
err = -EBUSY;
}
spin_unlock_irq(&efw->lock);
return err;
}
static int
hwdep_unlock(struct snd_efw *efw)
{
int err;
spin_lock_irq(&efw->lock);
if (efw->dev_lock_count == -1) {
efw->dev_lock_count = 0;
err = 0;
} else {
err = -EBADFD;
}
spin_unlock_irq(&efw->lock);
return err;
}
static int
hwdep_release(struct snd_hwdep *hwdep, struct file *file)
{
struct snd_efw *efw = hwdep->private_data;
spin_lock_irq(&efw->lock);
if (efw->dev_lock_count == -1)
efw->dev_lock_count = 0;
spin_unlock_irq(&efw->lock);
return 0;
}
static int
hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
unsigned int cmd, unsigned long arg)
{
struct snd_efw *efw = hwdep->private_data;
switch (cmd) {
case SNDRV_FIREWIRE_IOCTL_GET_INFO:
return hwdep_get_info(efw, (void __user *)arg);
case SNDRV_FIREWIRE_IOCTL_LOCK:
return hwdep_lock(efw);
case SNDRV_FIREWIRE_IOCTL_UNLOCK:
return hwdep_unlock(efw);
default:
return -ENOIOCTLCMD;
}
}
#ifdef CONFIG_COMPAT
static int
hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
unsigned int cmd, unsigned long arg)
{
return hwdep_ioctl(hwdep, file, cmd,
(unsigned long)compat_ptr(arg));
}
#else
#define hwdep_compat_ioctl NULL
#endif
static const struct snd_hwdep_ops hwdep_ops = {
.read = hwdep_read,
.write = hwdep_write,
.release = hwdep_release,
.poll = hwdep_poll,
.ioctl = hwdep_ioctl,
.ioctl_compat = hwdep_compat_ioctl,
};
int snd_efw_create_hwdep_device(struct snd_efw *efw)
{
struct snd_hwdep *hwdep;
int err;
err = snd_hwdep_new(efw->card, "Fireworks", 0, &hwdep);
if (err < 0)
goto end;
strcpy(hwdep->name, "Fireworks");
hwdep->iface = SNDRV_HWDEP_IFACE_FW_FIREWORKS;
hwdep->ops = hwdep_ops;
hwdep->private_data = efw;
hwdep->exclusive = true;
end:
return err;
}

View File

@ -0,0 +1,168 @@
/*
* fireworks_midi.c - a part of driver for Fireworks based devices
*
* Copyright (c) 2009-2010 Clemens Ladisch
* Copyright (c) 2013-2014 Takashi Sakamoto
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#include "fireworks.h"
static int midi_capture_open(struct snd_rawmidi_substream *substream)
{
struct snd_efw *efw = substream->rmidi->private_data;
int err;
err = snd_efw_stream_lock_try(efw);
if (err < 0)
goto end;
atomic_inc(&efw->capture_substreams);
err = snd_efw_stream_start_duplex(efw, 0);
if (err < 0)
snd_efw_stream_lock_release(efw);
end:
return err;
}
static int midi_playback_open(struct snd_rawmidi_substream *substream)
{
struct snd_efw *efw = substream->rmidi->private_data;
int err;
err = snd_efw_stream_lock_try(efw);
if (err < 0)
goto end;
atomic_inc(&efw->playback_substreams);
err = snd_efw_stream_start_duplex(efw, 0);
if (err < 0)
snd_efw_stream_lock_release(efw);
end:
return err;
}
static int midi_capture_close(struct snd_rawmidi_substream *substream)
{
struct snd_efw *efw = substream->rmidi->private_data;
atomic_dec(&efw->capture_substreams);
snd_efw_stream_stop_duplex(efw);
snd_efw_stream_lock_release(efw);
return 0;
}
static int midi_playback_close(struct snd_rawmidi_substream *substream)
{
struct snd_efw *efw = substream->rmidi->private_data;
atomic_dec(&efw->playback_substreams);
snd_efw_stream_stop_duplex(efw);
snd_efw_stream_lock_release(efw);
return 0;
}
static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
{
struct snd_efw *efw = substrm->rmidi->private_data;
unsigned long flags;
spin_lock_irqsave(&efw->lock, flags);
if (up)
amdtp_stream_midi_trigger(&efw->tx_stream,
substrm->number, substrm);
else
amdtp_stream_midi_trigger(&efw->tx_stream,
substrm->number, NULL);
spin_unlock_irqrestore(&efw->lock, flags);
}
static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
{
struct snd_efw *efw = substrm->rmidi->private_data;
unsigned long flags;
spin_lock_irqsave(&efw->lock, flags);
if (up)
amdtp_stream_midi_trigger(&efw->rx_stream,
substrm->number, substrm);
else
amdtp_stream_midi_trigger(&efw->rx_stream,
substrm->number, NULL);
spin_unlock_irqrestore(&efw->lock, flags);
}
static struct snd_rawmidi_ops midi_capture_ops = {
.open = midi_capture_open,
.close = midi_capture_close,
.trigger = midi_capture_trigger,
};
static struct snd_rawmidi_ops midi_playback_ops = {
.open = midi_playback_open,
.close = midi_playback_close,
.trigger = midi_playback_trigger,
};
static void set_midi_substream_names(struct snd_efw *efw,
struct snd_rawmidi_str *str)
{
struct snd_rawmidi_substream *subs;
list_for_each_entry(subs, &str->substreams, list) {
snprintf(subs->name, sizeof(subs->name),
"%s MIDI %d", efw->card->shortname, subs->number + 1);
}
}
int snd_efw_create_midi_devices(struct snd_efw *efw)
{
struct snd_rawmidi *rmidi;
struct snd_rawmidi_str *str;
int err;
/* create midi ports */
err = snd_rawmidi_new(efw->card, efw->card->driver, 0,
efw->midi_out_ports, efw->midi_in_ports,
&rmidi);
if (err < 0)
return err;
snprintf(rmidi->name, sizeof(rmidi->name),
"%s MIDI", efw->card->shortname);
rmidi->private_data = efw;
if (efw->midi_in_ports > 0) {
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
&midi_capture_ops);
str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
set_midi_substream_names(efw, str);
}
if (efw->midi_out_ports > 0) {
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
&midi_playback_ops);
str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
set_midi_substream_names(efw, str);
}
if ((efw->midi_out_ports > 0) && (efw->midi_in_ports > 0))
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
return 0;
}

View File

@ -0,0 +1,403 @@
/*
* fireworks_pcm.c - a part of driver for Fireworks based devices
*
* Copyright (c) 2009-2010 Clemens Ladisch
* Copyright (c) 2013-2014 Takashi Sakamoto
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#include "./fireworks.h"
/*
* NOTE:
* Fireworks changes its AMDTP channels for PCM data according to its sampling
* rate. There are three modes. Here _XX is either _rx or _tx.
* 0: 32.0- 48.0 kHz then snd_efw_hwinfo.amdtp_XX_pcm_channels applied
* 1: 88.2- 96.0 kHz then snd_efw_hwinfo.amdtp_XX_pcm_channels_2x applied
* 2: 176.4-192.0 kHz then snd_efw_hwinfo.amdtp_XX_pcm_channels_4x applied
*
* The number of PCM channels for analog input and output are always fixed but
* the number of PCM channels for digital input and output are differed.
*
* Additionally, according to "AudioFire Owner's Manual Version 2.2", in some
* model, the number of PCM channels for digital input has more restriction
* depending on which digital interface is selected.
* - S/PDIF coaxial and optical : use input 1-2
* - ADAT optical at 32.0-48.0 kHz : use input 1-8
* - ADAT optical at 88.2-96.0 kHz : use input 1-4 (S/MUX format)
*
* The data in AMDTP channels for blank PCM channels are zero.
*/
static const unsigned int freq_table[] = {
/* multiplier mode 0 */
[0] = 32000,
[1] = 44100,
[2] = 48000,
/* multiplier mode 1 */
[3] = 88200,
[4] = 96000,
/* multiplier mode 2 */
[5] = 176400,
[6] = 192000,
};
static inline unsigned int
get_multiplier_mode_with_index(unsigned int index)
{
return ((int)index - 1) / 2;
}
int snd_efw_get_multiplier_mode(unsigned int sampling_rate, unsigned int *mode)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(freq_table); i++) {
if (freq_table[i] == sampling_rate) {
*mode = get_multiplier_mode_with_index(i);
return 0;
}
}
return -EINVAL;
}
static int
hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
{
unsigned int *pcm_channels = rule->private;
struct snd_interval *r =
hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
const struct snd_interval *c =
hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
struct snd_interval t = {
.min = UINT_MAX, .max = 0, .integer = 1
};
unsigned int i, mode;
for (i = 0; i < ARRAY_SIZE(freq_table); i++) {
mode = get_multiplier_mode_with_index(i);
if (!snd_interval_test(c, pcm_channels[mode]))
continue;
t.min = min(t.min, freq_table[i]);
t.max = max(t.max, freq_table[i]);
}
return snd_interval_refine(r, &t);
}
static int
hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
{
unsigned int *pcm_channels = rule->private;
struct snd_interval *c =
hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
const struct snd_interval *r =
hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
struct snd_interval t = {
.min = UINT_MAX, .max = 0, .integer = 1
};
unsigned int i, mode;
for (i = 0; i < ARRAY_SIZE(freq_table); i++) {
mode = get_multiplier_mode_with_index(i);
if (!snd_interval_test(r, freq_table[i]))
continue;
t.min = min(t.min, pcm_channels[mode]);
t.max = max(t.max, pcm_channels[mode]);
}
return snd_interval_refine(c, &t);
}
static void
limit_channels(struct snd_pcm_hardware *hw, unsigned int *pcm_channels)
{
unsigned int i, mode;
hw->channels_min = UINT_MAX;
hw->channels_max = 0;
for (i = 0; i < ARRAY_SIZE(freq_table); i++) {
mode = get_multiplier_mode_with_index(i);
if (pcm_channels[mode] == 0)
continue;
hw->channels_min = min(hw->channels_min, pcm_channels[mode]);
hw->channels_max = max(hw->channels_max, pcm_channels[mode]);
}
}
static void
limit_period_and_buffer(struct snd_pcm_hardware *hw)
{
hw->periods_min = 2; /* SNDRV_PCM_INFO_BATCH */
hw->periods_max = UINT_MAX;
hw->period_bytes_min = 4 * hw->channels_max; /* bytes for a frame */
/* Just to prevent from allocating much pages. */
hw->period_bytes_max = hw->period_bytes_min * 2048;
hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min;
}
static int
pcm_init_hw_params(struct snd_efw *efw,
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct amdtp_stream *s;
unsigned int *pcm_channels;
int err;
runtime->hw.info = SNDRV_PCM_INFO_BATCH |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_JOINT_DUPLEX |
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID;
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
runtime->hw.formats = AMDTP_IN_PCM_FORMAT_BITS;
s = &efw->tx_stream;
pcm_channels = efw->pcm_capture_channels;
} else {
runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS;
s = &efw->rx_stream;
pcm_channels = efw->pcm_playback_channels;
}
/* limit rates */
runtime->hw.rates = efw->supported_sampling_rate,
snd_pcm_limit_hw_rates(runtime);
limit_channels(&runtime->hw, pcm_channels);
limit_period_and_buffer(&runtime->hw);
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
hw_rule_channels, pcm_channels,
SNDRV_PCM_HW_PARAM_RATE, -1);
if (err < 0)
goto end;
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
hw_rule_rate, pcm_channels,
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
if (err < 0)
goto end;
err = amdtp_stream_add_pcm_hw_constraints(s, runtime);
end:
return err;
}
static int pcm_open(struct snd_pcm_substream *substream)
{
struct snd_efw *efw = substream->private_data;
unsigned int sampling_rate;
enum snd_efw_clock_source clock_source;
int err;
err = snd_efw_stream_lock_try(efw);
if (err < 0)
goto end;
err = pcm_init_hw_params(efw, substream);
if (err < 0)
goto err_locked;
err = snd_efw_command_get_clock_source(efw, &clock_source);
if (err < 0)
goto err_locked;
/*
* When source of clock is not internal or any PCM streams are running,
* available sampling rate is limited at current sampling rate.
*/
if ((clock_source != SND_EFW_CLOCK_SOURCE_INTERNAL) ||
amdtp_stream_pcm_running(&efw->tx_stream) ||
amdtp_stream_pcm_running(&efw->rx_stream)) {
err = snd_efw_command_get_sampling_rate(efw, &sampling_rate);
if (err < 0)
goto err_locked;
substream->runtime->hw.rate_min = sampling_rate;
substream->runtime->hw.rate_max = sampling_rate;
}
snd_pcm_set_sync(substream);
end:
return err;
err_locked:
snd_efw_stream_lock_release(efw);
return err;
}
static int pcm_close(struct snd_pcm_substream *substream)
{
struct snd_efw *efw = substream->private_data;
snd_efw_stream_lock_release(efw);
return 0;
}
static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_efw *efw = substream->private_data;
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
atomic_inc(&efw->capture_substreams);
amdtp_stream_set_pcm_format(&efw->tx_stream, params_format(hw_params));
return snd_pcm_lib_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
}
static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_efw *efw = substream->private_data;
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
atomic_inc(&efw->playback_substreams);
amdtp_stream_set_pcm_format(&efw->rx_stream, params_format(hw_params));
return snd_pcm_lib_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
}
static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
{
struct snd_efw *efw = substream->private_data;
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
atomic_dec(&efw->capture_substreams);
snd_efw_stream_stop_duplex(efw);
return snd_pcm_lib_free_vmalloc_buffer(substream);
}
static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
{
struct snd_efw *efw = substream->private_data;
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
atomic_dec(&efw->playback_substreams);
snd_efw_stream_stop_duplex(efw);
return snd_pcm_lib_free_vmalloc_buffer(substream);
}
static int pcm_capture_prepare(struct snd_pcm_substream *substream)
{
struct snd_efw *efw = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
err = snd_efw_stream_start_duplex(efw, runtime->rate);
if (err >= 0)
amdtp_stream_pcm_prepare(&efw->tx_stream);
return err;
}
static int pcm_playback_prepare(struct snd_pcm_substream *substream)
{
struct snd_efw *efw = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
err = snd_efw_stream_start_duplex(efw, runtime->rate);
if (err >= 0)
amdtp_stream_pcm_prepare(&efw->rx_stream);
return err;
}
static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_efw *efw = substream->private_data;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
amdtp_stream_pcm_trigger(&efw->tx_stream, substream);
break;
case SNDRV_PCM_TRIGGER_STOP:
amdtp_stream_pcm_trigger(&efw->tx_stream, NULL);
break;
default:
return -EINVAL;
}
return 0;
}
static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_efw *efw = substream->private_data;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
amdtp_stream_pcm_trigger(&efw->rx_stream, substream);
break;
case SNDRV_PCM_TRIGGER_STOP:
amdtp_stream_pcm_trigger(&efw->rx_stream, NULL);
break;
default:
return -EINVAL;
}
return 0;
}
static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
{
struct snd_efw *efw = sbstrm->private_data;
return amdtp_stream_pcm_pointer(&efw->tx_stream);
}
static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
{
struct snd_efw *efw = sbstrm->private_data;
return amdtp_stream_pcm_pointer(&efw->rx_stream);
}
static const struct snd_pcm_ops pcm_capture_ops = {
.open = pcm_open,
.close = pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = pcm_capture_hw_params,
.hw_free = pcm_capture_hw_free,
.prepare = pcm_capture_prepare,
.trigger = pcm_capture_trigger,
.pointer = pcm_capture_pointer,
.page = snd_pcm_lib_get_vmalloc_page,
};
static const struct snd_pcm_ops pcm_playback_ops = {
.open = pcm_open,
.close = pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = pcm_playback_hw_params,
.hw_free = pcm_playback_hw_free,
.prepare = pcm_playback_prepare,
.trigger = pcm_playback_trigger,
.pointer = pcm_playback_pointer,
.page = snd_pcm_lib_get_vmalloc_page,
.mmap = snd_pcm_lib_mmap_vmalloc,
};
int snd_efw_create_pcm_devices(struct snd_efw *efw)
{
struct snd_pcm *pcm;
int err;
err = snd_pcm_new(efw->card, efw->card->driver, 0, 1, 1, &pcm);
if (err < 0)
goto end;
pcm->private_data = efw;
snprintf(pcm->name, sizeof(pcm->name), "%s PCM", efw->card->shortname);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
end:
return err;
}

View File

@ -0,0 +1,232 @@
/*
* fireworks_proc.c - a part of driver for Fireworks based devices
*
* Copyright (c) 2009-2010 Clemens Ladisch
* Copyright (c) 2013-2014 Takashi Sakamoto
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#include "./fireworks.h"
static inline const char*
get_phys_name(struct snd_efw_phys_grp *grp, bool input)
{
const char *const ch_type[] = {
"Analog", "S/PDIF", "ADAT", "S/PDIF or ADAT", "Mirroring",
"Headphones", "I2S", "Guitar", "Pirzo Guitar", "Guitar String",
};
if (grp->type < ARRAY_SIZE(ch_type))
return ch_type[grp->type];
else if (input)
return "Input";
else
return "Output";
}
static void
proc_read_hwinfo(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
{
struct snd_efw *efw = entry->private_data;
unsigned short i;
struct snd_efw_hwinfo *hwinfo;
hwinfo = kmalloc(sizeof(struct snd_efw_hwinfo), GFP_KERNEL);
if (hwinfo == NULL)
return;
if (snd_efw_command_get_hwinfo(efw, hwinfo) < 0)
goto end;
snd_iprintf(buffer, "guid_hi: 0x%X\n", hwinfo->guid_hi);
snd_iprintf(buffer, "guid_lo: 0x%X\n", hwinfo->guid_lo);
snd_iprintf(buffer, "type: 0x%X\n", hwinfo->type);
snd_iprintf(buffer, "version: 0x%X\n", hwinfo->version);
snd_iprintf(buffer, "vendor_name: %s\n", hwinfo->vendor_name);
snd_iprintf(buffer, "model_name: %s\n", hwinfo->model_name);
snd_iprintf(buffer, "dsp_version: 0x%X\n", hwinfo->dsp_version);
snd_iprintf(buffer, "arm_version: 0x%X\n", hwinfo->arm_version);
snd_iprintf(buffer, "fpga_version: 0x%X\n", hwinfo->fpga_version);
snd_iprintf(buffer, "flags: 0x%X\n", hwinfo->flags);
snd_iprintf(buffer, "max_sample_rate: 0x%X\n", hwinfo->max_sample_rate);
snd_iprintf(buffer, "min_sample_rate: 0x%X\n", hwinfo->min_sample_rate);
snd_iprintf(buffer, "supported_clock: 0x%X\n",
hwinfo->supported_clocks);
snd_iprintf(buffer, "phys out: 0x%X\n", hwinfo->phys_out);
snd_iprintf(buffer, "phys in: 0x%X\n", hwinfo->phys_in);
snd_iprintf(buffer, "phys in grps: 0x%X\n",
hwinfo->phys_in_grp_count);
for (i = 0; i < hwinfo->phys_in_grp_count; i++) {
snd_iprintf(buffer,
"phys in grp[0x%d]: type 0x%d, count 0x%d\n",
i, hwinfo->phys_out_grps[i].type,
hwinfo->phys_out_grps[i].count);
}
snd_iprintf(buffer, "phys out grps: 0x%X\n",
hwinfo->phys_out_grp_count);
for (i = 0; i < hwinfo->phys_out_grp_count; i++) {
snd_iprintf(buffer,
"phys out grps[0x%d]: type 0x%d, count 0x%d\n",
i, hwinfo->phys_out_grps[i].type,
hwinfo->phys_out_grps[i].count);
}
snd_iprintf(buffer, "amdtp rx pcm channels 1x: 0x%X\n",
hwinfo->amdtp_rx_pcm_channels);
snd_iprintf(buffer, "amdtp tx pcm channels 1x: 0x%X\n",
hwinfo->amdtp_tx_pcm_channels);
snd_iprintf(buffer, "amdtp rx pcm channels 2x: 0x%X\n",
hwinfo->amdtp_rx_pcm_channels_2x);
snd_iprintf(buffer, "amdtp tx pcm channels 2x: 0x%X\n",
hwinfo->amdtp_tx_pcm_channels_2x);
snd_iprintf(buffer, "amdtp rx pcm channels 4x: 0x%X\n",
hwinfo->amdtp_rx_pcm_channels_4x);
snd_iprintf(buffer, "amdtp tx pcm channels 4x: 0x%X\n",
hwinfo->amdtp_tx_pcm_channels_4x);
snd_iprintf(buffer, "midi out ports: 0x%X\n", hwinfo->midi_out_ports);
snd_iprintf(buffer, "midi in ports: 0x%X\n", hwinfo->midi_in_ports);
snd_iprintf(buffer, "mixer playback channels: 0x%X\n",
hwinfo->mixer_playback_channels);
snd_iprintf(buffer, "mixer capture channels: 0x%X\n",
hwinfo->mixer_capture_channels);
end:
kfree(hwinfo);
}
static void
proc_read_clock(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
{
struct snd_efw *efw = entry->private_data;
enum snd_efw_clock_source clock_source;
unsigned int sampling_rate;
if (snd_efw_command_get_clock_source(efw, &clock_source) < 0)
return;
if (snd_efw_command_get_sampling_rate(efw, &sampling_rate) < 0)
return;
snd_iprintf(buffer, "Clock Source: %d\n", clock_source);
snd_iprintf(buffer, "Sampling Rate: %d\n", sampling_rate);
}
/*
* NOTE:
* dB = 20 * log10(linear / 0x01000000)
* -144.0 dB when linear is 0
*/
static void
proc_read_phys_meters(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_efw *efw = entry->private_data;
struct snd_efw_phys_meters *meters;
unsigned int g, c, m, max, size;
const char *name;
u32 *linear;
int err;
size = sizeof(struct snd_efw_phys_meters) +
(efw->phys_in + efw->phys_out) * sizeof(u32);
meters = kzalloc(size, GFP_KERNEL);
if (meters == NULL)
return;
err = snd_efw_command_get_phys_meters(efw, meters, size);
if (err < 0)
goto end;
snd_iprintf(buffer, "Physical Meters:\n");
m = 0;
max = min(efw->phys_out, meters->out_meters);
linear = meters->values;
snd_iprintf(buffer, " %d Outputs:\n", max);
for (g = 0; g < efw->phys_out_grp_count; g++) {
name = get_phys_name(&efw->phys_out_grps[g], false);
for (c = 0; c < efw->phys_out_grps[g].count; c++) {
if (m < max)
snd_iprintf(buffer, "\t%s [%d]: %d\n",
name, c, linear[m++]);
}
}
m = 0;
max = min(efw->phys_in, meters->in_meters);
linear = meters->values + meters->out_meters;
snd_iprintf(buffer, " %d Inputs:\n", max);
for (g = 0; g < efw->phys_in_grp_count; g++) {
name = get_phys_name(&efw->phys_in_grps[g], true);
for (c = 0; c < efw->phys_in_grps[g].count; c++)
if (m < max)
snd_iprintf(buffer, "\t%s [%d]: %d\n",
name, c, linear[m++]);
}
end:
kfree(meters);
}
static void
proc_read_queues_state(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_efw *efw = entry->private_data;
unsigned int consumed;
if (efw->pull_ptr > efw->push_ptr)
consumed = snd_efw_resp_buf_size -
(unsigned int)(efw->pull_ptr - efw->push_ptr);
else
consumed = (unsigned int)(efw->push_ptr - efw->pull_ptr);
snd_iprintf(buffer, "%d %d/%d\n",
efw->resp_queues, consumed, snd_efw_resp_buf_size);
}
static void
add_node(struct snd_efw *efw, struct snd_info_entry *root, const char *name,
void (*op)(struct snd_info_entry *e, struct snd_info_buffer *b))
{
struct snd_info_entry *entry;
entry = snd_info_create_card_entry(efw->card, name, root);
if (entry == NULL)
return;
snd_info_set_text_ops(entry, efw, op);
if (snd_info_register(entry) < 0)
snd_info_free_entry(entry);
}
void snd_efw_proc_init(struct snd_efw *efw)
{
struct snd_info_entry *root;
/*
* All nodes are automatically removed at snd_card_disconnect(),
* by following to link list.
*/
root = snd_info_create_card_entry(efw->card, "firewire",
efw->card->proc_root);
if (root == NULL)
return;
root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
if (snd_info_register(root) < 0) {
snd_info_free_entry(root);
return;
}
add_node(efw, root, "clock", proc_read_clock);
add_node(efw, root, "firmware", proc_read_hwinfo);
add_node(efw, root, "meters", proc_read_phys_meters);
add_node(efw, root, "queues", proc_read_queues_state);
}

View File

@ -0,0 +1,372 @@
/*
* fireworks_stream.c - a part of driver for Fireworks based devices
*
* Copyright (c) 2013-2014 Takashi Sakamoto
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#include "./fireworks.h"
#define CALLBACK_TIMEOUT 100
static int
init_stream(struct snd_efw *efw, struct amdtp_stream *stream)
{
struct cmp_connection *conn;
enum cmp_direction c_dir;
enum amdtp_stream_direction s_dir;
int err;
if (stream == &efw->tx_stream) {
conn = &efw->out_conn;
c_dir = CMP_OUTPUT;
s_dir = AMDTP_IN_STREAM;
} else {
conn = &efw->in_conn;
c_dir = CMP_INPUT;
s_dir = AMDTP_OUT_STREAM;
}
err = cmp_connection_init(conn, efw->unit, c_dir, 0);
if (err < 0)
goto end;
err = amdtp_stream_init(stream, efw->unit, s_dir, CIP_BLOCKING);
if (err < 0) {
amdtp_stream_destroy(stream);
cmp_connection_destroy(conn);
}
end:
return err;
}
static void
stop_stream(struct snd_efw *efw, struct amdtp_stream *stream)
{
amdtp_stream_pcm_abort(stream);
amdtp_stream_stop(stream);
if (stream == &efw->tx_stream)
cmp_connection_break(&efw->out_conn);
else
cmp_connection_break(&efw->in_conn);
}
static int
start_stream(struct snd_efw *efw, struct amdtp_stream *stream,
unsigned int sampling_rate)
{
struct cmp_connection *conn;
unsigned int mode, pcm_channels, midi_ports;
int err;
err = snd_efw_get_multiplier_mode(sampling_rate, &mode);
if (err < 0)
goto end;
if (stream == &efw->tx_stream) {
conn = &efw->out_conn;
pcm_channels = efw->pcm_capture_channels[mode];
midi_ports = efw->midi_out_ports;
} else {
conn = &efw->in_conn;
pcm_channels = efw->pcm_playback_channels[mode];
midi_ports = efw->midi_in_ports;
}
amdtp_stream_set_parameters(stream, sampling_rate,
pcm_channels, midi_ports);
/* establish connection via CMP */
err = cmp_connection_establish(conn,
amdtp_stream_get_max_payload(stream));
if (err < 0)
goto end;
/* start amdtp stream */
err = amdtp_stream_start(stream,
conn->resources.channel,
conn->speed);
if (err < 0) {
stop_stream(efw, stream);
goto end;
}
/* wait first callback */
if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) {
stop_stream(efw, stream);
err = -ETIMEDOUT;
}
end:
return err;
}
static void
destroy_stream(struct snd_efw *efw, struct amdtp_stream *stream)
{
stop_stream(efw, stream);
amdtp_stream_destroy(stream);
if (stream == &efw->tx_stream)
cmp_connection_destroy(&efw->out_conn);
else
cmp_connection_destroy(&efw->in_conn);
}
static int
get_sync_mode(struct snd_efw *efw, enum cip_flags *sync_mode)
{
enum snd_efw_clock_source clock_source;
int err;
err = snd_efw_command_get_clock_source(efw, &clock_source);
if (err < 0)
return err;
if (clock_source == SND_EFW_CLOCK_SOURCE_SYTMATCH)
return -ENOSYS;
*sync_mode = CIP_SYNC_TO_DEVICE;
return 0;
}
static int
check_connection_used_by_others(struct snd_efw *efw, struct amdtp_stream *s)
{
struct cmp_connection *conn;
bool used;
int err;
if (s == &efw->tx_stream)
conn = &efw->out_conn;
else
conn = &efw->in_conn;
err = cmp_connection_check_used(conn, &used);
if ((err >= 0) && used && !amdtp_stream_running(s)) {
dev_err(&efw->unit->device,
"Connection established by others: %cPCR[%d]\n",
(conn->direction == CMP_OUTPUT) ? 'o' : 'i',
conn->pcr_index);
err = -EBUSY;
}
return err;
}
int snd_efw_stream_init_duplex(struct snd_efw *efw)
{
int err;
err = init_stream(efw, &efw->tx_stream);
if (err < 0)
goto end;
/* Fireworks transmits NODATA packets with TAG0. */
efw->tx_stream.flags |= CIP_EMPTY_WITH_TAG0;
/* Fireworks has its own meaning for dbc. */
efw->tx_stream.flags |= CIP_DBC_IS_END_EVENT;
/* Fireworks reset dbc at bus reset. */
efw->tx_stream.flags |= CIP_SKIP_DBC_ZERO_CHECK;
/* AudioFire9 always reports wrong dbs. */
if (efw->is_af9)
efw->tx_stream.flags |= CIP_WRONG_DBS;
/* Firmware version 5.5 reports fixed interval for dbc. */
if (efw->firmware_version == 0x5050000)
efw->tx_stream.tx_dbc_interval = 8;
err = init_stream(efw, &efw->rx_stream);
if (err < 0) {
destroy_stream(efw, &efw->tx_stream);
goto end;
}
/*
* Fireworks ignores MIDI messages in more than first 8 data
* blocks of an received AMDTP packet.
*/
efw->rx_stream.rx_blocks_for_midi = 8;
/* set IEC61883 compliant mode (actually not fully compliant...) */
err = snd_efw_command_set_tx_mode(efw, SND_EFW_TRANSPORT_MODE_IEC61883);
if (err < 0) {
destroy_stream(efw, &efw->tx_stream);
destroy_stream(efw, &efw->rx_stream);
}
end:
return err;
}
int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate)
{
struct amdtp_stream *master, *slave;
atomic_t *slave_substreams;
enum cip_flags sync_mode;
unsigned int curr_rate;
int err = 0;
mutex_lock(&efw->mutex);
/* Need no substreams */
if ((atomic_read(&efw->playback_substreams) == 0) &&
(atomic_read(&efw->capture_substreams) == 0))
goto end;
err = get_sync_mode(efw, &sync_mode);
if (err < 0)
goto end;
if (sync_mode == CIP_SYNC_TO_DEVICE) {
master = &efw->tx_stream;
slave = &efw->rx_stream;
slave_substreams = &efw->playback_substreams;
} else {
master = &efw->rx_stream;
slave = &efw->tx_stream;
slave_substreams = &efw->capture_substreams;
}
/*
* Considering JACK/FFADO streaming:
* TODO: This can be removed hwdep functionality becomes popular.
*/
err = check_connection_used_by_others(efw, master);
if (err < 0)
goto end;
/* packet queueing error */
if (amdtp_streaming_error(slave))
stop_stream(efw, slave);
if (amdtp_streaming_error(master))
stop_stream(efw, master);
/* stop streams if rate is different */
err = snd_efw_command_get_sampling_rate(efw, &curr_rate);
if (err < 0)
goto end;
if (rate == 0)
rate = curr_rate;
if (rate != curr_rate) {
stop_stream(efw, slave);
stop_stream(efw, master);
}
/* master should be always running */
if (!amdtp_stream_running(master)) {
amdtp_stream_set_sync(sync_mode, master, slave);
efw->master = master;
err = snd_efw_command_set_sampling_rate(efw, rate);
if (err < 0)
goto end;
err = start_stream(efw, master, rate);
if (err < 0) {
dev_err(&efw->unit->device,
"fail to start AMDTP master stream:%d\n", err);
goto end;
}
}
/* start slave if needed */
if (atomic_read(slave_substreams) > 0 && !amdtp_stream_running(slave)) {
err = start_stream(efw, slave, rate);
if (err < 0) {
dev_err(&efw->unit->device,
"fail to start AMDTP slave stream:%d\n", err);
stop_stream(efw, master);
}
}
end:
mutex_unlock(&efw->mutex);
return err;
}
void snd_efw_stream_stop_duplex(struct snd_efw *efw)
{
struct amdtp_stream *master, *slave;
atomic_t *master_substreams, *slave_substreams;
mutex_lock(&efw->mutex);
if (efw->master == &efw->rx_stream) {
slave = &efw->tx_stream;
master = &efw->rx_stream;
slave_substreams = &efw->capture_substreams;
master_substreams = &efw->playback_substreams;
} else {
slave = &efw->rx_stream;
master = &efw->tx_stream;
slave_substreams = &efw->playback_substreams;
master_substreams = &efw->capture_substreams;
}
if (atomic_read(slave_substreams) == 0) {
stop_stream(efw, slave);
if (atomic_read(master_substreams) == 0)
stop_stream(efw, master);
}
mutex_unlock(&efw->mutex);
}
void snd_efw_stream_update_duplex(struct snd_efw *efw)
{
if ((cmp_connection_update(&efw->out_conn) < 0) ||
(cmp_connection_update(&efw->in_conn) < 0)) {
mutex_lock(&efw->mutex);
stop_stream(efw, &efw->rx_stream);
stop_stream(efw, &efw->tx_stream);
mutex_unlock(&efw->mutex);
} else {
amdtp_stream_update(&efw->rx_stream);
amdtp_stream_update(&efw->tx_stream);
}
}
void snd_efw_stream_destroy_duplex(struct snd_efw *efw)
{
mutex_lock(&efw->mutex);
destroy_stream(efw, &efw->rx_stream);
destroy_stream(efw, &efw->tx_stream);
mutex_unlock(&efw->mutex);
}
void snd_efw_stream_lock_changed(struct snd_efw *efw)
{
efw->dev_lock_changed = true;
wake_up(&efw->hwdep_wait);
}
int snd_efw_stream_lock_try(struct snd_efw *efw)
{
int err;
spin_lock_irq(&efw->lock);
/* user land lock this */
if (efw->dev_lock_count < 0) {
err = -EBUSY;
goto end;
}
/* this is the first time */
if (efw->dev_lock_count++ == 0)
snd_efw_stream_lock_changed(efw);
err = 0;
end:
spin_unlock_irq(&efw->lock);
return err;
}
void snd_efw_stream_lock_release(struct snd_efw *efw)
{
spin_lock_irq(&efw->lock);
if (WARN_ON(efw->dev_lock_count <= 0))
goto end;
if (--efw->dev_lock_count == 0)
snd_efw_stream_lock_changed(efw);
end:
spin_unlock_irq(&efw->lock);
}

View File

@ -0,0 +1,326 @@
/*
* fireworks_transaction.c - a part of driver for Fireworks based devices
*
* Copyright (c) 2013-2014 Takashi Sakamoto
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
/*
* Fireworks have its own transaction. The transaction can be delivered by AV/C
* Vendor Specific command. But at least Windows driver and firmware version 5.5
* or later don't use it.
*
* Transaction substance:
* At first, 6 data exist. Following to the 6 data, parameters for each
* commands exists. All of parameters are 32 bit alighed to big endian.
* data[0]: Length of transaction substance
* data[1]: Transaction version
* data[2]: Sequence number. This is incremented by the device
* data[3]: transaction category
* data[4]: transaction command
* data[5]: return value in response.
* data[6-]: parameters
*
* Transaction address:
* command: 0xecc000000000
* response: 0xecc080000000 (default)
*
* I note that the address for response can be changed by command. But this
* module uses the default address.
*/
#include "./fireworks.h"
#define MEMORY_SPACE_EFW_COMMAND 0xecc000000000ULL
#define MEMORY_SPACE_EFW_RESPONSE 0xecc080000000ULL
#define ERROR_RETRIES 3
#define ERROR_DELAY_MS 5
#define EFC_TIMEOUT_MS 125
static DEFINE_SPINLOCK(instances_lock);
static struct snd_efw *instances[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
static DEFINE_SPINLOCK(transaction_queues_lock);
static LIST_HEAD(transaction_queues);
enum transaction_queue_state {
STATE_PENDING,
STATE_BUS_RESET,
STATE_COMPLETE
};
struct transaction_queue {
struct list_head list;
struct fw_unit *unit;
void *buf;
unsigned int size;
u32 seqnum;
enum transaction_queue_state state;
wait_queue_head_t wait;
};
int snd_efw_transaction_cmd(struct fw_unit *unit,
const void *cmd, unsigned int size)
{
return snd_fw_transaction(unit, TCODE_WRITE_BLOCK_REQUEST,
MEMORY_SPACE_EFW_COMMAND,
(void *)cmd, size, 0);
}
int snd_efw_transaction_run(struct fw_unit *unit,
const void *cmd, unsigned int cmd_size,
void *resp, unsigned int resp_size)
{
struct transaction_queue t;
unsigned int tries;
int ret;
t.unit = unit;
t.buf = resp;
t.size = resp_size;
t.seqnum = be32_to_cpu(((struct snd_efw_transaction *)cmd)->seqnum) + 1;
t.state = STATE_PENDING;
init_waitqueue_head(&t.wait);
spin_lock_irq(&transaction_queues_lock);
list_add_tail(&t.list, &transaction_queues);
spin_unlock_irq(&transaction_queues_lock);
tries = 0;
do {
ret = snd_efw_transaction_cmd(t.unit, (void *)cmd, cmd_size);
if (ret < 0)
break;
wait_event_timeout(t.wait, t.state != STATE_PENDING,
msecs_to_jiffies(EFC_TIMEOUT_MS));
if (t.state == STATE_COMPLETE) {
ret = t.size;
break;
} else if (t.state == STATE_BUS_RESET) {
msleep(ERROR_DELAY_MS);
} else if (++tries >= ERROR_RETRIES) {
dev_err(&t.unit->device, "EFW transaction timed out\n");
ret = -EIO;
break;
}
} while (1);
spin_lock_irq(&transaction_queues_lock);
list_del(&t.list);
spin_unlock_irq(&transaction_queues_lock);
return ret;
}
static void
copy_resp_to_buf(struct snd_efw *efw, void *data, size_t length, int *rcode)
{
size_t capacity, till_end;
struct snd_efw_transaction *t;
spin_lock_irq(&efw->lock);
t = (struct snd_efw_transaction *)data;
length = min_t(size_t, t->length * sizeof(t->length), length);
if (efw->push_ptr < efw->pull_ptr)
capacity = (unsigned int)(efw->pull_ptr - efw->push_ptr);
else
capacity = snd_efw_resp_buf_size -
(unsigned int)(efw->push_ptr - efw->pull_ptr);
/* confirm enough space for this response */
if (capacity < length) {
*rcode = RCODE_CONFLICT_ERROR;
goto end;
}
/* copy to ring buffer */
while (length > 0) {
till_end = snd_efw_resp_buf_size -
(unsigned int)(efw->push_ptr - efw->resp_buf);
till_end = min_t(unsigned int, length, till_end);
memcpy(efw->push_ptr, data, till_end);
efw->push_ptr += till_end;
if (efw->push_ptr >= efw->resp_buf + snd_efw_resp_buf_size)
efw->push_ptr = efw->resp_buf;
length -= till_end;
data += till_end;
}
/* for hwdep */
efw->resp_queues++;
wake_up(&efw->hwdep_wait);
*rcode = RCODE_COMPLETE;
end:
spin_unlock_irq(&efw->lock);
}
static void
handle_resp_for_user(struct fw_card *card, int generation, int source,
void *data, size_t length, int *rcode)
{
struct fw_device *device;
struct snd_efw *efw;
unsigned int i;
spin_lock_irq(&instances_lock);
for (i = 0; i < SNDRV_CARDS; i++) {
efw = instances[i];
if (efw == NULL)
continue;
device = fw_parent_device(efw->unit);
if ((device->card != card) ||
(device->generation != generation))
continue;
smp_rmb(); /* node id vs. generation */
if (device->node_id != source)
continue;
break;
}
if (i == SNDRV_CARDS)
goto end;
copy_resp_to_buf(efw, data, length, rcode);
end:
spin_unlock_irq(&instances_lock);
}
static void
handle_resp_for_kernel(struct fw_card *card, int generation, int source,
void *data, size_t length, int *rcode, u32 seqnum)
{
struct fw_device *device;
struct transaction_queue *t;
unsigned long flags;
spin_lock_irqsave(&transaction_queues_lock, flags);
list_for_each_entry(t, &transaction_queues, list) {
device = fw_parent_device(t->unit);
if ((device->card != card) ||
(device->generation != generation))
continue;
smp_rmb(); /* node_id vs. generation */
if (device->node_id != source)
continue;
if ((t->state == STATE_PENDING) && (t->seqnum == seqnum)) {
t->state = STATE_COMPLETE;
t->size = min_t(unsigned int, length, t->size);
memcpy(t->buf, data, t->size);
wake_up(&t->wait);
*rcode = RCODE_COMPLETE;
}
}
spin_unlock_irqrestore(&transaction_queues_lock, flags);
}
static void
efw_response(struct fw_card *card, struct fw_request *request,
int tcode, int destination, int source,
int generation, unsigned long long offset,
void *data, size_t length, void *callback_data)
{
int rcode, dummy;
u32 seqnum;
rcode = RCODE_TYPE_ERROR;
if (length < sizeof(struct snd_efw_transaction)) {
rcode = RCODE_DATA_ERROR;
goto end;
} else if (offset != MEMORY_SPACE_EFW_RESPONSE) {
rcode = RCODE_ADDRESS_ERROR;
goto end;
}
seqnum = be32_to_cpu(((struct snd_efw_transaction *)data)->seqnum);
if (seqnum > SND_EFW_TRANSACTION_USER_SEQNUM_MAX + 1) {
handle_resp_for_kernel(card, generation, source,
data, length, &rcode, seqnum);
if (snd_efw_resp_buf_debug)
handle_resp_for_user(card, generation, source,
data, length, &dummy);
} else {
handle_resp_for_user(card, generation, source,
data, length, &rcode);
}
end:
fw_send_response(card, request, rcode);
}
void snd_efw_transaction_add_instance(struct snd_efw *efw)
{
unsigned int i;
spin_lock_irq(&instances_lock);
for (i = 0; i < SNDRV_CARDS; i++) {
if (instances[i] != NULL)
continue;
instances[i] = efw;
break;
}
spin_unlock_irq(&instances_lock);
}
void snd_efw_transaction_remove_instance(struct snd_efw *efw)
{
unsigned int i;
spin_lock_irq(&instances_lock);
for (i = 0; i < SNDRV_CARDS; i++) {
if (instances[i] != efw)
continue;
instances[i] = NULL;
}
spin_unlock_irq(&instances_lock);
}
void snd_efw_transaction_bus_reset(struct fw_unit *unit)
{
struct transaction_queue *t;
spin_lock_irq(&transaction_queues_lock);
list_for_each_entry(t, &transaction_queues, list) {
if ((t->unit == unit) &&
(t->state == STATE_PENDING)) {
t->state = STATE_BUS_RESET;
wake_up(&t->wait);
}
}
spin_unlock_irq(&transaction_queues_lock);
}
static struct fw_address_handler resp_register_handler = {
.length = SND_EFW_RESPONSE_MAXIMUM_BYTES,
.address_callback = efw_response
};
int snd_efw_transaction_register(void)
{
static const struct fw_address_region resp_register_region = {
.start = MEMORY_SPACE_EFW_RESPONSE,
.end = MEMORY_SPACE_EFW_RESPONSE +
SND_EFW_RESPONSE_MAXIMUM_BYTES
};
return fw_core_add_address_handler(&resp_register_handler,
&resp_register_region);
}
void snd_efw_transaction_unregister(void)
{
WARN_ON(!list_empty(&transaction_queues));
fw_core_remove_address_handler(&resp_register_handler);
}

View File

@ -51,7 +51,7 @@ struct fwspk {
const struct device_info *device_info; const struct device_info *device_info;
struct mutex mutex; struct mutex mutex;
struct cmp_connection connection; struct cmp_connection connection;
struct amdtp_out_stream stream; struct amdtp_stream stream;
bool mute; bool mute;
s16 volume[6]; s16 volume[6];
s16 volume_min; s16 volume_min;
@ -167,13 +167,7 @@ static int fwspk_open(struct snd_pcm_substream *substream)
if (err < 0) if (err < 0)
return err; return err;
err = snd_pcm_hw_constraint_minmax(runtime, err = amdtp_stream_add_pcm_hw_constraints(&fwspk->stream, runtime);
SNDRV_PCM_HW_PARAM_PERIOD_TIME,
5000, UINT_MAX);
if (err < 0)
return err;
err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
if (err < 0) if (err < 0)
return err; return err;
@ -187,48 +181,12 @@ 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 (amdtp_out_stream_running(&fwspk->stream)) { if (amdtp_stream_running(&fwspk->stream)) {
amdtp_out_stream_stop(&fwspk->stream); amdtp_stream_stop(&fwspk->stream);
cmp_connection_break(&fwspk->connection); cmp_connection_break(&fwspk->connection);
} }
} }
static int fwspk_set_rate(struct fwspk *fwspk, unsigned int sfc)
{
u8 *buf;
int err;
buf = kmalloc(8, GFP_KERNEL);
if (!buf)
return -ENOMEM;
buf[0] = 0x00; /* AV/C, CONTROL */
buf[1] = 0xff; /* unit */
buf[2] = 0x19; /* INPUT PLUG SIGNAL FORMAT */
buf[3] = 0x00; /* plug 0 */
buf[4] = 0x90; /* format: audio */
buf[5] = 0x00 | sfc; /* AM824, frequency */
buf[6] = 0xff; /* SYT (not used) */
buf[7] = 0xff;
err = fcp_avc_transaction(fwspk->unit, buf, 8, buf, 8,
BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5));
if (err < 0)
goto error;
if (err < 6 || buf[0] != 0x09 /* ACCEPTED */) {
dev_err(&fwspk->unit->device, "failed to set sample rate\n");
err = -EIO;
goto error;
}
err = 0;
error:
kfree(buf);
return err;
}
static int fwspk_hw_params(struct snd_pcm_substream *substream, static int fwspk_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params) struct snd_pcm_hw_params *hw_params)
{ {
@ -244,17 +202,20 @@ static int fwspk_hw_params(struct snd_pcm_substream *substream,
if (err < 0) if (err < 0)
goto error; goto error;
amdtp_out_stream_set_parameters(&fwspk->stream, amdtp_stream_set_parameters(&fwspk->stream,
params_rate(hw_params), params_rate(hw_params),
params_channels(hw_params), params_channels(hw_params),
0); 0);
amdtp_out_stream_set_pcm_format(&fwspk->stream, amdtp_stream_set_pcm_format(&fwspk->stream,
params_format(hw_params)); params_format(hw_params));
err = fwspk_set_rate(fwspk, fwspk->stream.sfc); err = avc_general_set_sig_fmt(fwspk->unit, params_rate(hw_params),
if (err < 0) AVC_GENERAL_PLUG_DIR_IN, 0);
if (err < 0) {
dev_err(&fwspk->unit->device, "failed to set sample rate\n");
goto err_buffer; goto err_buffer;
}
return 0; return 0;
@ -282,25 +243,25 @@ static int fwspk_prepare(struct snd_pcm_substream *substream)
mutex_lock(&fwspk->mutex); mutex_lock(&fwspk->mutex);
if (amdtp_out_streaming_error(&fwspk->stream)) if (amdtp_streaming_error(&fwspk->stream))
fwspk_stop_stream(fwspk); fwspk_stop_stream(fwspk);
if (!amdtp_out_stream_running(&fwspk->stream)) { if (!amdtp_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_stream_get_max_payload(&fwspk->stream));
if (err < 0) if (err < 0)
goto err_mutex; goto err_mutex;
err = amdtp_out_stream_start(&fwspk->stream, err = amdtp_stream_start(&fwspk->stream,
fwspk->connection.resources.channel, fwspk->connection.resources.channel,
fwspk->connection.speed); fwspk->connection.speed);
if (err < 0) if (err < 0)
goto err_connection; goto err_connection;
} }
mutex_unlock(&fwspk->mutex); mutex_unlock(&fwspk->mutex);
amdtp_out_stream_pcm_prepare(&fwspk->stream); amdtp_stream_pcm_prepare(&fwspk->stream);
return 0; return 0;
@ -327,7 +288,7 @@ static int fwspk_trigger(struct snd_pcm_substream *substream, int cmd)
default: default:
return -EINVAL; return -EINVAL;
} }
amdtp_out_stream_pcm_trigger(&fwspk->stream, pcm); amdtp_stream_pcm_trigger(&fwspk->stream, pcm);
return 0; return 0;
} }
@ -335,7 +296,7 @@ static snd_pcm_uframes_t fwspk_pointer(struct snd_pcm_substream *substream)
{ {
struct fwspk *fwspk = substream->private_data; struct fwspk *fwspk = substream->private_data;
return amdtp_out_stream_pcm_pointer(&fwspk->stream); return amdtp_stream_pcm_pointer(&fwspk->stream);
} }
static int fwspk_create_pcm(struct fwspk *fwspk) static int fwspk_create_pcm(struct fwspk *fwspk)
@ -653,7 +614,7 @@ static void fwspk_card_free(struct snd_card *card)
{ {
struct fwspk *fwspk = card->private_data; struct fwspk *fwspk = card->private_data;
amdtp_out_stream_destroy(&fwspk->stream); amdtp_stream_destroy(&fwspk->stream);
cmp_connection_destroy(&fwspk->connection); cmp_connection_destroy(&fwspk->connection);
fw_unit_put(fwspk->unit); fw_unit_put(fwspk->unit);
mutex_destroy(&fwspk->mutex); mutex_destroy(&fwspk->mutex);
@ -679,11 +640,12 @@ static int fwspk_probe(struct fw_unit *unit,
fwspk->unit = fw_unit_get(unit); fwspk->unit = fw_unit_get(unit);
fwspk->device_info = (const struct device_info *)id->driver_data; fwspk->device_info = (const struct device_info *)id->driver_data;
err = cmp_connection_init(&fwspk->connection, unit, 0); err = cmp_connection_init(&fwspk->connection, unit, CMP_INPUT, 0);
if (err < 0) if (err < 0)
goto err_unit; goto err_unit;
err = amdtp_out_stream_init(&fwspk->stream, unit, CIP_NONBLOCKING); err = amdtp_stream_init(&fwspk->stream, unit, AMDTP_OUT_STREAM,
CIP_NONBLOCKING);
if (err < 0) if (err < 0)
goto err_connection; goto err_connection;
@ -733,21 +695,21 @@ static void fwspk_bus_reset(struct fw_unit *unit)
fcp_bus_reset(fwspk->unit); fcp_bus_reset(fwspk->unit);
if (cmp_connection_update(&fwspk->connection) < 0) { if (cmp_connection_update(&fwspk->connection) < 0) {
amdtp_out_stream_pcm_abort(&fwspk->stream); amdtp_stream_pcm_abort(&fwspk->stream);
mutex_lock(&fwspk->mutex); mutex_lock(&fwspk->mutex);
fwspk_stop_stream(fwspk); fwspk_stop_stream(fwspk);
mutex_unlock(&fwspk->mutex); mutex_unlock(&fwspk->mutex);
return; return;
} }
amdtp_out_stream_update(&fwspk->stream); amdtp_stream_update(&fwspk->stream);
} }
static void fwspk_remove(struct fw_unit *unit) static void fwspk_remove(struct fw_unit *unit)
{ {
struct fwspk *fwspk = dev_get_drvdata(&unit->device); struct fwspk *fwspk = dev_get_drvdata(&unit->device);
amdtp_out_stream_pcm_abort(&fwspk->stream); amdtp_stream_pcm_abort(&fwspk->stream);
snd_card_disconnect(fwspk->card); snd_card_disconnect(fwspk->card);
mutex_lock(&fwspk->mutex); mutex_lock(&fwspk->mutex);

View File

@ -442,17 +442,11 @@ static void snd_interwave_detect_memory(struct snd_gus_card *gus)
for (bank_pos = 0; bank_pos < 16L * 1024L * 1024L; bank_pos += 4L * 1024L * 1024L) { for (bank_pos = 0; bank_pos < 16L * 1024L * 1024L; bank_pos += 4L * 1024L * 1024L) {
for (i = 0; i < 8; ++i) for (i = 0; i < 8; ++i)
iwave[i] = snd_gf1_peek(gus, bank_pos + i); iwave[i] = snd_gf1_peek(gus, bank_pos + i);
#ifdef CONFIG_SND_DEBUG_ROM
printk(KERN_DEBUG "ROM at 0x%06x = %8phC\n", bank_pos, iwave);
#endif
if (strncmp(iwave, "INTRWAVE", 8)) if (strncmp(iwave, "INTRWAVE", 8))
continue; /* first check */ continue; /* first check */
csum = 0; csum = 0;
for (i = 0; i < sizeof(struct rom_hdr); i++) for (i = 0; i < sizeof(struct rom_hdr); i++)
csum += snd_gf1_peek(gus, bank_pos + i); csum += snd_gf1_peek(gus, bank_pos + i);
#ifdef CONFIG_SND_DEBUG_ROM
printk(KERN_DEBUG "ROM checksum = 0x%x (computed)\n", csum);
#endif
if (csum != 0) if (csum != 0)
continue; /* not valid rom */ continue; /* not valid rom */
gus->gf1.rom_banks++; gus->gf1.rom_banks++;

View File

@ -648,14 +648,14 @@ static int au1000_ac97_probe(struct platform_device *pdev)
goto out; goto out;
err = -EBUSY; err = -EBUSY;
au1000->ac97_res_port = request_mem_region(r->start, au1000->ac97_res_port = request_mem_region(r->start, resource_size(r),
r->end - r->start + 1, pdev->name); pdev->name);
if (!au1000->ac97_res_port) { if (!au1000->ac97_res_port) {
snd_printk(KERN_ERR "ALSA AC97: can't grab AC97 port\n"); snd_printk(KERN_ERR "ALSA AC97: can't grab AC97 port\n");
goto out; goto out;
} }
io = ioremap(r->start, r->end - r->start + 1); io = ioremap(r->start, resource_size(r));
if (!io) if (!io)
goto out; goto out;

View File

@ -567,7 +567,6 @@ static int mpu401_out(int dev, unsigned char midi_byte)
static int mpu401_command(int dev, mpu_command_rec * cmd) static int mpu401_command(int dev, mpu_command_rec * cmd)
{ {
int i, timeout, ok; int i, timeout, ok;
int ret = 0;
unsigned long flags; unsigned long flags;
struct mpu_config *devc; struct mpu_config *devc;
@ -644,7 +643,6 @@ static int mpu401_command(int dev, mpu_command_rec * cmd)
} }
} }
} }
ret = 0;
cmd->data[0] = 0; cmd->data[0] = 0;
if (cmd->nr_returns) if (cmd->nr_returns)
@ -666,7 +664,7 @@ static int mpu401_command(int dev, mpu_command_rec * cmd)
} }
} }
spin_unlock_irqrestore(&devc->lock,flags); spin_unlock_irqrestore(&devc->lock,flags);
return ret; return 0;
} }
static int mpu_cmd(int dev, int cmd, int data) static int mpu_cmd(int dev, int cmd, int data)

View File

@ -2625,15 +2625,12 @@ static int __init cs4297a_init(void)
u32 pwr, id; u32 pwr, id;
mm_segment_t fs; mm_segment_t fs;
int rval; int rval;
#ifndef CONFIG_BCM_CS4297A_CSWARM
u64 cfg; u64 cfg;
int mdio_val; int mdio_val;
#endif
CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO
"cs4297a: cs4297a_init_module()+ \n")); "cs4297a: cs4297a_init_module()+ \n"));
#ifndef CONFIG_BCM_CS4297A_CSWARM
mdio_val = __raw_readq(KSEG1 + A_MAC_REGISTER(2, R_MAC_MDIO)) & mdio_val = __raw_readq(KSEG1 + A_MAC_REGISTER(2, R_MAC_MDIO)) &
(M_MAC_MDIO_DIR|M_MAC_MDIO_OUT); (M_MAC_MDIO_DIR|M_MAC_MDIO_OUT);
@ -2659,7 +2656,6 @@ static int __init cs4297a_init(void)
__raw_writeq(mdio_val | M_MAC_GENC, KSEG1+A_MAC_REGISTER(2, R_MAC_MDIO)); __raw_writeq(mdio_val | M_MAC_GENC, KSEG1+A_MAC_REGISTER(2, R_MAC_MDIO));
/* Give the codec some time to finish resetting (start the bit clock) */ /* Give the codec some time to finish resetting (start the bit clock) */
udelay(100); udelay(100);
#endif
if (!(s = kzalloc(sizeof(struct cs4297a_state), GFP_KERNEL))) { if (!(s = kzalloc(sizeof(struct cs4297a_state), GFP_KERNEL))) {
CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR

View File

@ -23,6 +23,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/module.h> #include <linux/module.h>
@ -34,8 +35,6 @@
#include <sound/opl3.h> #include <sound/opl3.h>
#include <sound/initval.h> #include <sound/initval.h>
#include <asm/io.h>
#ifdef CONFIG_SND_FM801_TEA575X_BOOL #ifdef CONFIG_SND_FM801_TEA575X_BOOL
#include <media/tea575x.h> #include <media/tea575x.h>
#endif #endif
@ -80,7 +79,10 @@ MODULE_PARM_DESC(radio_nr, "Radio device numbers");
* Direct registers * Direct registers
*/ */
#define FM801_REG(chip, reg) (chip->port + FM801_##reg) #define fm801_writew(chip,reg,value) outw((value), chip->port + FM801_##reg)
#define fm801_readw(chip,reg) inw(chip->port + FM801_##reg)
#define fm801_writel(chip,reg,value) outl((value), chip->port + FM801_##reg)
#define FM801_PCM_VOL 0x00 /* PCM Output Volume */ #define FM801_PCM_VOL 0x00 /* PCM Output Volume */
#define FM801_FM_VOL 0x02 /* FM Output Volume */ #define FM801_FM_VOL 0x02 /* FM Output Volume */
@ -156,21 +158,27 @@ MODULE_PARM_DESC(radio_nr, "Radio device numbers");
#define FM801_GPIO_GS3 (1<<15) #define FM801_GPIO_GS3 (1<<15)
#define FM801_GPIO_GS(x) (1<<(12+(x))) #define FM801_GPIO_GS(x) (1<<(12+(x)))
/* /**
* struct fm801 - describes FM801 chip
* @port: I/O port number
* @multichannel: multichannel support
* @secondary: secondary codec
* @secondary_addr: address of the secondary codec
* @tea575x_tuner: tuner access method & flags
* @ply_ctrl: playback control
* @cap_ctrl: capture control
*/ */
struct fm801 { struct fm801 {
int irq; int irq;
unsigned long port; /* I/O port number */ unsigned long port;
unsigned int multichannel: 1, /* multichannel support */ unsigned int multichannel: 1,
secondary: 1; /* secondary codec */ secondary: 1;
unsigned char secondary_addr; /* address of the secondary codec */ unsigned char secondary_addr;
unsigned int tea575x_tuner; /* tuner access method & flags */ unsigned int tea575x_tuner;
unsigned short ply_ctrl; /* playback control */ unsigned short ply_ctrl;
unsigned short cap_ctrl; /* capture control */ unsigned short cap_ctrl;
unsigned long ply_buffer; unsigned long ply_buffer;
unsigned int ply_buf; unsigned int ply_buf;
@ -222,6 +230,30 @@ MODULE_DEVICE_TABLE(pci, snd_fm801_ids);
* common I/O routines * common I/O routines
*/ */
static bool fm801_ac97_is_ready(struct fm801 *chip, unsigned int iterations)
{
unsigned int idx;
for (idx = 0; idx < iterations; idx++) {
if (!(fm801_readw(chip, AC97_CMD) & FM801_AC97_BUSY))
return true;
udelay(10);
}
return false;
}
static bool fm801_ac97_is_valid(struct fm801 *chip, unsigned int iterations)
{
unsigned int idx;
for (idx = 0; idx < iterations; idx++) {
if (fm801_readw(chip, AC97_CMD) & FM801_AC97_VALID)
return true;
udelay(10);
}
return false;
}
static int snd_fm801_update_bits(struct fm801 *chip, unsigned short reg, static int snd_fm801_update_bits(struct fm801 *chip, unsigned short reg,
unsigned short mask, unsigned short value) unsigned short mask, unsigned short value)
{ {
@ -244,73 +276,54 @@ static void snd_fm801_codec_write(struct snd_ac97 *ac97,
unsigned short val) unsigned short val)
{ {
struct fm801 *chip = ac97->private_data; struct fm801 *chip = ac97->private_data;
int idx;
/* /*
* Wait until the codec interface is not ready.. * Wait until the codec interface is not ready..
*/ */
for (idx = 0; idx < 100; idx++) { if (!fm801_ac97_is_ready(chip, 100)) {
if (!(inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_BUSY)) dev_err(chip->card->dev, "AC'97 interface is busy (1)\n");
goto ok1; return;
udelay(10);
} }
dev_err(chip->card->dev, "AC'97 interface is busy (1)\n");
return;
ok1:
/* write data and address */ /* write data and address */
outw(val, FM801_REG(chip, AC97_DATA)); fm801_writew(chip, AC97_DATA, val);
outw(reg | (ac97->addr << FM801_AC97_ADDR_SHIFT), FM801_REG(chip, AC97_CMD)); fm801_writew(chip, AC97_CMD, reg | (ac97->addr << FM801_AC97_ADDR_SHIFT));
/* /*
* Wait until the write command is not completed.. * Wait until the write command is not completed..
*/ */
for (idx = 0; idx < 1000; idx++) { if (!fm801_ac97_is_ready(chip, 1000))
if (!(inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_BUSY)) dev_err(chip->card->dev, "AC'97 interface #%d is busy (2)\n",
return; ac97->num);
udelay(10);
}
dev_err(chip->card->dev, "AC'97 interface #%d is busy (2)\n", ac97->num);
} }
static unsigned short snd_fm801_codec_read(struct snd_ac97 *ac97, unsigned short reg) static unsigned short snd_fm801_codec_read(struct snd_ac97 *ac97, unsigned short reg)
{ {
struct fm801 *chip = ac97->private_data; struct fm801 *chip = ac97->private_data;
int idx;
/* /*
* Wait until the codec interface is not ready.. * Wait until the codec interface is not ready..
*/ */
for (idx = 0; idx < 100; idx++) { if (!fm801_ac97_is_ready(chip, 100)) {
if (!(inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_BUSY)) dev_err(chip->card->dev, "AC'97 interface is busy (1)\n");
goto ok1; return 0;
udelay(10);
} }
dev_err(chip->card->dev, "AC'97 interface is busy (1)\n");
return 0;
ok1:
/* read command */ /* read command */
outw(reg | (ac97->addr << FM801_AC97_ADDR_SHIFT) | FM801_AC97_READ, fm801_writew(chip, AC97_CMD,
FM801_REG(chip, AC97_CMD)); reg | (ac97->addr << FM801_AC97_ADDR_SHIFT) | FM801_AC97_READ);
for (idx = 0; idx < 100; idx++) { if (!fm801_ac97_is_ready(chip, 100)) {
if (!(inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_BUSY)) dev_err(chip->card->dev, "AC'97 interface #%d is busy (2)\n",
goto ok2; ac97->num);
udelay(10); return 0;
} }
dev_err(chip->card->dev, "AC'97 interface #%d is busy (2)\n", ac97->num);
return 0;
ok2: if (!fm801_ac97_is_valid(chip, 1000)) {
for (idx = 0; idx < 1000; idx++) { dev_err(chip->card->dev,
if (inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_VALID) "AC'97 interface #%d is not valid (2)\n", ac97->num);
goto ok3; return 0;
udelay(10);
} }
dev_err(chip->card->dev, "AC'97 interface #%d is not valid (2)\n", ac97->num);
return 0;
ok3: return fm801_readw(chip, AC97_DATA);
return inw(FM801_REG(chip, AC97_DATA));
} }
static unsigned int rates[] = { static unsigned int rates[] = {
@ -384,7 +397,7 @@ static int snd_fm801_playback_trigger(struct snd_pcm_substream *substream,
snd_BUG(); snd_BUG();
return -EINVAL; return -EINVAL;
} }
outw(chip->ply_ctrl, FM801_REG(chip, PLY_CTRL)); fm801_writew(chip, PLY_CTRL, chip->ply_ctrl);
spin_unlock(&chip->reg_lock); spin_unlock(&chip->reg_lock);
return 0; return 0;
} }
@ -419,7 +432,7 @@ static int snd_fm801_capture_trigger(struct snd_pcm_substream *substream,
snd_BUG(); snd_BUG();
return -EINVAL; return -EINVAL;
} }
outw(chip->cap_ctrl, FM801_REG(chip, CAP_CTRL)); fm801_writew(chip, CAP_CTRL, chip->cap_ctrl);
spin_unlock(&chip->reg_lock); spin_unlock(&chip->reg_lock);
return 0; return 0;
} }
@ -457,12 +470,13 @@ static int snd_fm801_playback_prepare(struct snd_pcm_substream *substream)
} }
chip->ply_ctrl |= snd_fm801_rate_bits(runtime->rate) << FM801_RATE_SHIFT; chip->ply_ctrl |= snd_fm801_rate_bits(runtime->rate) << FM801_RATE_SHIFT;
chip->ply_buf = 0; chip->ply_buf = 0;
outw(chip->ply_ctrl, FM801_REG(chip, PLY_CTRL)); fm801_writew(chip, PLY_CTRL, chip->ply_ctrl);
outw(chip->ply_count - 1, FM801_REG(chip, PLY_COUNT)); fm801_writew(chip, PLY_COUNT, chip->ply_count - 1);
chip->ply_buffer = runtime->dma_addr; chip->ply_buffer = runtime->dma_addr;
chip->ply_pos = 0; chip->ply_pos = 0;
outl(chip->ply_buffer, FM801_REG(chip, PLY_BUF1)); fm801_writel(chip, PLY_BUF1, chip->ply_buffer);
outl(chip->ply_buffer + (chip->ply_count % chip->ply_size), FM801_REG(chip, PLY_BUF2)); fm801_writel(chip, PLY_BUF2,
chip->ply_buffer + (chip->ply_count % chip->ply_size));
spin_unlock_irq(&chip->reg_lock); spin_unlock_irq(&chip->reg_lock);
return 0; return 0;
} }
@ -483,12 +497,13 @@ static int snd_fm801_capture_prepare(struct snd_pcm_substream *substream)
chip->cap_ctrl |= FM801_STEREO; chip->cap_ctrl |= FM801_STEREO;
chip->cap_ctrl |= snd_fm801_rate_bits(runtime->rate) << FM801_RATE_SHIFT; chip->cap_ctrl |= snd_fm801_rate_bits(runtime->rate) << FM801_RATE_SHIFT;
chip->cap_buf = 0; chip->cap_buf = 0;
outw(chip->cap_ctrl, FM801_REG(chip, CAP_CTRL)); fm801_writew(chip, CAP_CTRL, chip->cap_ctrl);
outw(chip->cap_count - 1, FM801_REG(chip, CAP_COUNT)); fm801_writew(chip, CAP_COUNT, chip->cap_count - 1);
chip->cap_buffer = runtime->dma_addr; chip->cap_buffer = runtime->dma_addr;
chip->cap_pos = 0; chip->cap_pos = 0;
outl(chip->cap_buffer, FM801_REG(chip, CAP_BUF1)); fm801_writel(chip, CAP_BUF1, chip->cap_buffer);
outl(chip->cap_buffer + (chip->cap_count % chip->cap_size), FM801_REG(chip, CAP_BUF2)); fm801_writel(chip, CAP_BUF2,
chip->cap_buffer + (chip->cap_count % chip->cap_size));
spin_unlock_irq(&chip->reg_lock); spin_unlock_irq(&chip->reg_lock);
return 0; return 0;
} }
@ -501,8 +516,8 @@ static snd_pcm_uframes_t snd_fm801_playback_pointer(struct snd_pcm_substream *su
if (!(chip->ply_ctrl & FM801_START)) if (!(chip->ply_ctrl & FM801_START))
return 0; return 0;
spin_lock(&chip->reg_lock); spin_lock(&chip->reg_lock);
ptr = chip->ply_pos + (chip->ply_count - 1) - inw(FM801_REG(chip, PLY_COUNT)); ptr = chip->ply_pos + (chip->ply_count - 1) - fm801_readw(chip, PLY_COUNT);
if (inw(FM801_REG(chip, IRQ_STATUS)) & FM801_IRQ_PLAYBACK) { if (fm801_readw(chip, IRQ_STATUS) & FM801_IRQ_PLAYBACK) {
ptr += chip->ply_count; ptr += chip->ply_count;
ptr %= chip->ply_size; ptr %= chip->ply_size;
} }
@ -518,8 +533,8 @@ static snd_pcm_uframes_t snd_fm801_capture_pointer(struct snd_pcm_substream *sub
if (!(chip->cap_ctrl & FM801_START)) if (!(chip->cap_ctrl & FM801_START))
return 0; return 0;
spin_lock(&chip->reg_lock); spin_lock(&chip->reg_lock);
ptr = chip->cap_pos + (chip->cap_count - 1) - inw(FM801_REG(chip, CAP_COUNT)); ptr = chip->cap_pos + (chip->cap_count - 1) - fm801_readw(chip, CAP_COUNT);
if (inw(FM801_REG(chip, IRQ_STATUS)) & FM801_IRQ_CAPTURE) { if (fm801_readw(chip, IRQ_STATUS) & FM801_IRQ_CAPTURE) {
ptr += chip->cap_count; ptr += chip->cap_count;
ptr %= chip->cap_size; ptr %= chip->cap_size;
} }
@ -533,12 +548,12 @@ static irqreturn_t snd_fm801_interrupt(int irq, void *dev_id)
unsigned short status; unsigned short status;
unsigned int tmp; unsigned int tmp;
status = inw(FM801_REG(chip, IRQ_STATUS)); status = fm801_readw(chip, IRQ_STATUS);
status &= FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU|FM801_IRQ_VOLUME; status &= FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU|FM801_IRQ_VOLUME;
if (! status) if (! status)
return IRQ_NONE; return IRQ_NONE;
/* ack first */ /* ack first */
outw(status, FM801_REG(chip, IRQ_STATUS)); fm801_writew(chip, IRQ_STATUS, status);
if (chip->pcm && (status & FM801_IRQ_PLAYBACK) && chip->playback_substream) { if (chip->pcm && (status & FM801_IRQ_PLAYBACK) && chip->playback_substream) {
spin_lock(&chip->reg_lock); spin_lock(&chip->reg_lock);
chip->ply_buf++; chip->ply_buf++;
@ -546,10 +561,10 @@ static irqreturn_t snd_fm801_interrupt(int irq, void *dev_id)
chip->ply_pos %= chip->ply_size; chip->ply_pos %= chip->ply_size;
tmp = chip->ply_pos + chip->ply_count; tmp = chip->ply_pos + chip->ply_count;
tmp %= chip->ply_size; tmp %= chip->ply_size;
outl(chip->ply_buffer + tmp, if (chip->ply_buf & 1)
(chip->ply_buf & 1) ? fm801_writel(chip, PLY_BUF1, chip->ply_buffer + tmp);
FM801_REG(chip, PLY_BUF1) : else
FM801_REG(chip, PLY_BUF2)); fm801_writel(chip, PLY_BUF2, chip->ply_buffer + tmp);
spin_unlock(&chip->reg_lock); spin_unlock(&chip->reg_lock);
snd_pcm_period_elapsed(chip->playback_substream); snd_pcm_period_elapsed(chip->playback_substream);
} }
@ -560,10 +575,10 @@ static irqreturn_t snd_fm801_interrupt(int irq, void *dev_id)
chip->cap_pos %= chip->cap_size; chip->cap_pos %= chip->cap_size;
tmp = chip->cap_pos + chip->cap_count; tmp = chip->cap_pos + chip->cap_count;
tmp %= chip->cap_size; tmp %= chip->cap_size;
outl(chip->cap_buffer + tmp, if (chip->cap_buf & 1)
(chip->cap_buf & 1) ? fm801_writel(chip, CAP_BUF1, chip->cap_buffer + tmp);
FM801_REG(chip, CAP_BUF1) : else
FM801_REG(chip, CAP_BUF2)); fm801_writel(chip, CAP_BUF2, chip->cap_buffer + tmp);
spin_unlock(&chip->reg_lock); spin_unlock(&chip->reg_lock);
snd_pcm_period_elapsed(chip->capture_substream); snd_pcm_period_elapsed(chip->capture_substream);
} }
@ -747,7 +762,7 @@ static struct snd_fm801_tea575x_gpio snd_fm801_tea575x_gpios[] = {
static void snd_fm801_tea575x_set_pins(struct snd_tea575x *tea, u8 pins) static void snd_fm801_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
{ {
struct fm801 *chip = tea->private_data; struct fm801 *chip = tea->private_data;
unsigned short reg = inw(FM801_REG(chip, GPIO_CTRL)); unsigned short reg = fm801_readw(chip, GPIO_CTRL);
struct snd_fm801_tea575x_gpio gpio = *get_tea575x_gpio(chip); struct snd_fm801_tea575x_gpio gpio = *get_tea575x_gpio(chip);
reg &= ~(FM801_GPIO_GP(gpio.data) | reg &= ~(FM801_GPIO_GP(gpio.data) |
@ -759,13 +774,13 @@ static void snd_fm801_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
/* WRITE_ENABLE is inverted */ /* WRITE_ENABLE is inverted */
reg |= (pins & TEA575X_WREN) ? 0 : FM801_GPIO_GP(gpio.wren); reg |= (pins & TEA575X_WREN) ? 0 : FM801_GPIO_GP(gpio.wren);
outw(reg, FM801_REG(chip, GPIO_CTRL)); fm801_writew(chip, GPIO_CTRL, reg);
} }
static u8 snd_fm801_tea575x_get_pins(struct snd_tea575x *tea) static u8 snd_fm801_tea575x_get_pins(struct snd_tea575x *tea)
{ {
struct fm801 *chip = tea->private_data; struct fm801 *chip = tea->private_data;
unsigned short reg = inw(FM801_REG(chip, GPIO_CTRL)); unsigned short reg = fm801_readw(chip, GPIO_CTRL);
struct snd_fm801_tea575x_gpio gpio = *get_tea575x_gpio(chip); struct snd_fm801_tea575x_gpio gpio = *get_tea575x_gpio(chip);
u8 ret; u8 ret;
@ -780,7 +795,7 @@ static u8 snd_fm801_tea575x_get_pins(struct snd_tea575x *tea)
static void snd_fm801_tea575x_set_direction(struct snd_tea575x *tea, bool output) static void snd_fm801_tea575x_set_direction(struct snd_tea575x *tea, bool output)
{ {
struct fm801 *chip = tea->private_data; struct fm801 *chip = tea->private_data;
unsigned short reg = inw(FM801_REG(chip, GPIO_CTRL)); unsigned short reg = fm801_readw(chip, GPIO_CTRL);
struct snd_fm801_tea575x_gpio gpio = *get_tea575x_gpio(chip); struct snd_fm801_tea575x_gpio gpio = *get_tea575x_gpio(chip);
/* use GPIO lines and set write enable bit */ /* use GPIO lines and set write enable bit */
@ -811,7 +826,7 @@ static void snd_fm801_tea575x_set_direction(struct snd_tea575x *tea, bool output
FM801_GPIO_GP(gpio.clk)); FM801_GPIO_GP(gpio.clk));
} }
outw(reg, FM801_REG(chip, GPIO_CTRL)); fm801_writew(chip, GPIO_CTRL, reg);
} }
static struct snd_tea575x_ops snd_fm801_tea_ops = { static struct snd_tea575x_ops snd_fm801_tea_ops = {
@ -962,7 +977,7 @@ static int snd_fm801_get_mux(struct snd_kcontrol *kcontrol,
struct fm801 *chip = snd_kcontrol_chip(kcontrol); struct fm801 *chip = snd_kcontrol_chip(kcontrol);
unsigned short val; unsigned short val;
val = inw(FM801_REG(chip, REC_SRC)) & 7; val = fm801_readw(chip, REC_SRC) & 7;
if (val > 4) if (val > 4)
val = 4; val = 4;
ucontrol->value.enumerated.item[0] = val; ucontrol->value.enumerated.item[0] = val;
@ -1073,12 +1088,12 @@ static int wait_for_codec(struct fm801 *chip, unsigned int codec_id,
{ {
unsigned long timeout = jiffies + waits; unsigned long timeout = jiffies + waits;
outw(FM801_AC97_READ | (codec_id << FM801_AC97_ADDR_SHIFT) | reg, fm801_writew(chip, AC97_CMD,
FM801_REG(chip, AC97_CMD)); reg | (codec_id << FM801_AC97_ADDR_SHIFT) | FM801_AC97_READ);
udelay(5); udelay(5);
do { do {
if ((inw(FM801_REG(chip, AC97_CMD)) & (FM801_AC97_VALID|FM801_AC97_BUSY)) if ((fm801_readw(chip, AC97_CMD) &
== FM801_AC97_VALID) (FM801_AC97_VALID | FM801_AC97_BUSY)) == FM801_AC97_VALID)
return 0; return 0;
schedule_timeout_uninterruptible(1); schedule_timeout_uninterruptible(1);
} while (time_after(timeout, jiffies)); } while (time_after(timeout, jiffies));
@ -1093,10 +1108,10 @@ static int snd_fm801_chip_init(struct fm801 *chip, int resume)
goto __ac97_ok; goto __ac97_ok;
/* codec cold reset + AC'97 warm reset */ /* codec cold reset + AC'97 warm reset */
outw((1<<5) | (1<<6), FM801_REG(chip, CODEC_CTRL)); fm801_writew(chip, CODEC_CTRL, (1 << 5) | (1 << 6));
inw(FM801_REG(chip, CODEC_CTRL)); /* flush posting data */ fm801_readw(chip, CODEC_CTRL); /* flush posting data */
udelay(100); udelay(100);
outw(0, FM801_REG(chip, CODEC_CTRL)); fm801_writew(chip, CODEC_CTRL, 0);
if (wait_for_codec(chip, 0, AC97_RESET, msecs_to_jiffies(750)) < 0) if (wait_for_codec(chip, 0, AC97_RESET, msecs_to_jiffies(750)) < 0)
if (!resume) { if (!resume) {
@ -1117,7 +1132,7 @@ static int snd_fm801_chip_init(struct fm801 *chip, int resume)
for (i = 3; i > 0; i--) { for (i = 3; i > 0; i--) {
if (!wait_for_codec(chip, i, AC97_VENDOR_ID1, if (!wait_for_codec(chip, i, AC97_VENDOR_ID1,
msecs_to_jiffies(50))) { msecs_to_jiffies(50))) {
cmdw = inw(FM801_REG(chip, AC97_DATA)); cmdw = fm801_readw(chip, AC97_DATA);
if (cmdw != 0xffff && cmdw != 0) { if (cmdw != 0xffff && cmdw != 0) {
chip->secondary = 1; chip->secondary = 1;
chip->secondary_addr = i; chip->secondary_addr = i;
@ -1135,23 +1150,24 @@ static int snd_fm801_chip_init(struct fm801 *chip, int resume)
__ac97_ok: __ac97_ok:
/* init volume */ /* init volume */
outw(0x0808, FM801_REG(chip, PCM_VOL)); fm801_writew(chip, PCM_VOL, 0x0808);
outw(0x9f1f, FM801_REG(chip, FM_VOL)); fm801_writew(chip, FM_VOL, 0x9f1f);
outw(0x8808, FM801_REG(chip, I2S_VOL)); fm801_writew(chip, I2S_VOL, 0x8808);
/* I2S control - I2S mode */ /* I2S control - I2S mode */
outw(0x0003, FM801_REG(chip, I2S_MODE)); fm801_writew(chip, I2S_MODE, 0x0003);
/* interrupt setup */ /* interrupt setup */
cmdw = inw(FM801_REG(chip, IRQ_MASK)); cmdw = fm801_readw(chip, IRQ_MASK);
if (chip->irq < 0) if (chip->irq < 0)
cmdw |= 0x00c3; /* mask everything, no PCM nor MPU */ cmdw |= 0x00c3; /* mask everything, no PCM nor MPU */
else else
cmdw &= ~0x0083; /* unmask MPU, PLAYBACK & CAPTURE */ cmdw &= ~0x0083; /* unmask MPU, PLAYBACK & CAPTURE */
outw(cmdw, FM801_REG(chip, IRQ_MASK)); fm801_writew(chip, IRQ_MASK, cmdw);
/* interrupt clear */ /* interrupt clear */
outw(FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU, FM801_REG(chip, IRQ_STATUS)); fm801_writew(chip, IRQ_STATUS,
FM801_IRQ_PLAYBACK | FM801_IRQ_CAPTURE | FM801_IRQ_MPU);
return 0; return 0;
} }
@ -1165,9 +1181,9 @@ static int snd_fm801_free(struct fm801 *chip)
goto __end_hw; goto __end_hw;
/* interrupt setup - mask everything */ /* interrupt setup - mask everything */
cmdw = inw(FM801_REG(chip, IRQ_MASK)); cmdw = fm801_readw(chip, IRQ_MASK);
cmdw |= 0x00c3; cmdw |= 0x00c3;
outw(cmdw, FM801_REG(chip, IRQ_MASK)); fm801_writew(chip, IRQ_MASK, cmdw);
__end_hw: __end_hw:
#ifdef CONFIG_SND_FM801_TEA575X_BOOL #ifdef CONFIG_SND_FM801_TEA575X_BOOL
@ -1339,15 +1355,15 @@ static int snd_card_fm801_probe(struct pci_dev *pci,
return err; return err;
} }
if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_FM801, if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_FM801,
FM801_REG(chip, MPU401_DATA), chip->port + FM801_MPU401_DATA,
MPU401_INFO_INTEGRATED | MPU401_INFO_INTEGRATED |
MPU401_INFO_IRQ_HOOK, MPU401_INFO_IRQ_HOOK,
-1, &chip->rmidi)) < 0) { -1, &chip->rmidi)) < 0) {
snd_card_free(card); snd_card_free(card);
return err; return err;
} }
if ((err = snd_opl3_create(card, FM801_REG(chip, OPL3_BANK0), if ((err = snd_opl3_create(card, chip->port + FM801_OPL3_BANK0,
FM801_REG(chip, OPL3_BANK1), chip->port + FM801_OPL3_BANK1,
OPL3_HW_OPL3_FM801, 1, &opl3)) < 0) { OPL3_HW_OPL3_FM801, 1, &opl3)) < 0) {
snd_card_free(card); snd_card_free(card);
return err; return err;

View File

@ -20,6 +20,21 @@ config SND_HDA_INTEL
To compile this driver as a module, choose M here: the module To compile this driver as a module, choose M here: the module
will be called snd-hda-intel. will be called snd-hda-intel.
config SND_HDA_TEGRA
tristate "NVIDIA Tegra HD Audio"
depends on ARCH_TEGRA
select SND_HDA
help
Say Y here to support the HDA controller present in NVIDIA
Tegra SoCs
This options enables support for the HD Audio controller
present in some NVIDIA Tegra SoCs, used to communicate audio
to the HDMI output.
To compile this driver as a module, choose M here: the module
will be called snd-hda-tegra.
if SND_HDA if SND_HDA
config SND_HDA_DSP_LOADER config SND_HDA_DSP_LOADER

View File

@ -1,5 +1,6 @@
snd-hda-intel-objs := hda_intel.o snd-hda-intel-objs := hda_intel.o
snd-hda-controller-objs := hda_controller.o snd-hda-controller-objs := hda_controller.o
snd-hda-tegra-objs := hda_tegra.o
# for haswell power well # for haswell power well
snd-hda-intel-$(CONFIG_SND_HDA_I915) += hda_i915.o snd-hda-intel-$(CONFIG_SND_HDA_I915) += hda_i915.o
@ -47,3 +48,4 @@ obj-$(CONFIG_SND_HDA_CODEC_HDMI) += snd-hda-codec-hdmi.o
# otherwise the codec patches won't be hooked before the PCI probe # otherwise the codec patches won't be hooked before the PCI probe
# when built in kernel # when built in kernel
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o
obj-$(CONFIG_SND_HDA_TEGRA) += snd-hda-tegra.o

View File

@ -839,6 +839,43 @@ void snd_hda_apply_fixup(struct hda_codec *codec, int action)
} }
EXPORT_SYMBOL_GPL(snd_hda_apply_fixup); EXPORT_SYMBOL_GPL(snd_hda_apply_fixup);
static bool pin_config_match(struct hda_codec *codec,
const struct hda_pintbl *pins)
{
for (; pins->nid; pins++) {
u32 def_conf = snd_hda_codec_get_pincfg(codec, pins->nid);
if (pins->val != def_conf)
return false;
}
return true;
}
void snd_hda_pick_pin_fixup(struct hda_codec *codec,
const struct snd_hda_pin_quirk *pin_quirk,
const struct hda_fixup *fixlist)
{
const struct snd_hda_pin_quirk *pq;
if (codec->fixup_forced)
return;
for (pq = pin_quirk; pq->subvendor; pq++) {
if ((codec->subsystem_id & 0xffff0000) != (pq->subvendor << 16))
continue;
if (codec->vendor_id != pq->codec)
continue;
if (pin_config_match(codec, pq->pins)) {
codec->fixup_id = pq->value;
#ifdef CONFIG_SND_DEBUG_VERBOSE
codec->fixup_name = pq->name;
#endif
codec->fixup_list = fixlist;
return;
}
}
}
EXPORT_SYMBOL_GPL(snd_hda_pick_pin_fixup);
void snd_hda_pick_fixup(struct hda_codec *codec, void snd_hda_pick_fixup(struct hda_codec *codec,
const struct hda_model_fixup *models, const struct hda_model_fixup *models,
const struct snd_pci_quirk *quirk, const struct snd_pci_quirk *quirk,
@ -852,15 +889,17 @@ void snd_hda_pick_fixup(struct hda_codec *codec,
if (codec->modelname && !strcmp(codec->modelname, "nofixup")) { if (codec->modelname && !strcmp(codec->modelname, "nofixup")) {
codec->fixup_list = NULL; codec->fixup_list = NULL;
codec->fixup_id = -1; codec->fixup_id = -1;
codec->fixup_forced = 1;
return; return;
} }
if (codec->modelname && models) { if (codec->modelname && models) {
while (models->name) { while (models->name) {
if (!strcmp(codec->modelname, models->name)) { if (!strcmp(codec->modelname, models->name)) {
id = models->id; codec->fixup_id = models->id;
name = models->name; codec->fixup_name = models->name;
break; codec->fixup_forced = 1;
return;
} }
models++; models++;
} }
@ -889,6 +928,7 @@ void snd_hda_pick_fixup(struct hda_codec *codec,
} }
} }
codec->fixup_forced = 0;
codec->fixup_id = id; codec->fixup_id = id;
if (id >= 0) { if (id >= 0) {
codec->fixup_list = fixlist; codec->fixup_list = fixlist;

View File

@ -402,6 +402,7 @@ struct hda_codec {
/* fix-up list */ /* fix-up list */
int fixup_id; int fixup_id;
unsigned int fixup_forced:1; /* fixup explicitly set by user */
const struct hda_fixup *fixup_list; const struct hda_fixup *fixup_list;
const char *fixup_name; const char *fixup_name;

View File

@ -3722,7 +3722,7 @@ static void parse_digital(struct hda_codec *codec)
} else { } else {
spec->multiout.slave_dig_outs = spec->slave_dig_outs; spec->multiout.slave_dig_outs = spec->slave_dig_outs;
if (nums >= ARRAY_SIZE(spec->slave_dig_outs) - 1) if (nums >= ARRAY_SIZE(spec->slave_dig_outs) - 1)
break; break;
spec->slave_dig_outs[nums - 1] = dig_nid; spec->slave_dig_outs[nums - 1] = dig_nid;
} }
nums++; nums++;

View File

@ -1730,7 +1730,7 @@ static void azx_remove(struct pci_dev *pci)
} }
/* PCI IDs */ /* PCI IDs */
static DEFINE_PCI_DEVICE_TABLE(azx_ids) = { static const struct pci_device_id azx_ids[] = {
/* CPT */ /* CPT */
{ PCI_DEVICE(0x8086, 0x1c20), { PCI_DEVICE(0x8086, 0x1c20),
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM }, .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM },

View File

@ -407,6 +407,16 @@ struct hda_fixup {
} v; } v;
}; };
struct snd_hda_pin_quirk {
unsigned int codec; /* Codec vendor/device ID */
unsigned short subvendor; /* PCI subvendor ID */
const struct hda_pintbl *pins; /* list of matching pins */
#ifdef CONFIG_SND_DEBUG_VERBOSE
const char *name;
#endif
int value; /* quirk value */
};
/* fixup types */ /* fixup types */
enum { enum {
HDA_FIXUP_INVALID, HDA_FIXUP_INVALID,
@ -434,6 +444,10 @@ void snd_hda_pick_fixup(struct hda_codec *codec,
const struct hda_model_fixup *models, const struct hda_model_fixup *models,
const struct snd_pci_quirk *quirk, const struct snd_pci_quirk *quirk,
const struct hda_fixup *fixlist); const struct hda_fixup *fixlist);
void snd_hda_pick_pin_fixup(struct hda_codec *codec,
const struct snd_hda_pin_quirk *pin_quirk,
const struct hda_fixup *fixlist);
/* /*
* unsolicited event handler * unsolicited event handler

588
sound/pci/hda/hda_tegra.c Normal file
View File

@ -0,0 +1,588 @@
/*
*
* Implementation of primary ALSA driver code base for NVIDIA Tegra HDA.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <linux/clk.h>
#include <linux/clocksource.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/mutex.h>
#include <linux/of_device.h>
#include <linux/reboot.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <sound/core.h>
#include <sound/initval.h>
#include "hda_codec.h"
#include "hda_controller.h"
#include "hda_priv.h"
/* Defines for Nvidia Tegra HDA support */
#define HDA_BAR0 0x8000
#define HDA_CFG_CMD 0x1004
#define HDA_CFG_BAR0 0x1010
#define HDA_ENABLE_IO_SPACE (1 << 0)
#define HDA_ENABLE_MEM_SPACE (1 << 1)
#define HDA_ENABLE_BUS_MASTER (1 << 2)
#define HDA_ENABLE_SERR (1 << 8)
#define HDA_DISABLE_INTR (1 << 10)
#define HDA_BAR0_INIT_PROGRAM 0xFFFFFFFF
#define HDA_BAR0_FINAL_PROGRAM (1 << 14)
/* IPFS */
#define HDA_IPFS_CONFIG 0x180
#define HDA_IPFS_EN_FPCI 0x1
#define HDA_IPFS_FPCI_BAR0 0x80
#define HDA_FPCI_BAR0_START 0x40
#define HDA_IPFS_INTR_MASK 0x188
#define HDA_IPFS_EN_INTR (1 << 16)
/* max number of SDs */
#define NUM_CAPTURE_SD 1
#define NUM_PLAYBACK_SD 1
struct hda_tegra {
struct azx chip;
struct device *dev;
struct clk *hda_clk;
struct clk *hda2codec_2x_clk;
struct clk *hda2hdmi_clk;
void __iomem *regs;
};
#ifdef CONFIG_PM
static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT;
module_param(power_save, bint, 0644);
MODULE_PARM_DESC(power_save,
"Automatic power-saving timeout (in seconds, 0 = disable).");
#else
static int power_save = 0;
#endif
/*
* DMA page allocation ops.
*/
static int dma_alloc_pages(struct azx *chip, int type, size_t size,
struct snd_dma_buffer *buf)
{
return snd_dma_alloc_pages(type, chip->card->dev, size, buf);
}
static void dma_free_pages(struct azx *chip, struct snd_dma_buffer *buf)
{
snd_dma_free_pages(buf);
}
static int substream_alloc_pages(struct azx *chip,
struct snd_pcm_substream *substream,
size_t size)
{
struct azx_dev *azx_dev = get_azx_dev(substream);
azx_dev->bufsize = 0;
azx_dev->period_bytes = 0;
azx_dev->format_val = 0;
return snd_pcm_lib_malloc_pages(substream, size);
}
static int substream_free_pages(struct azx *chip,
struct snd_pcm_substream *substream)
{
return snd_pcm_lib_free_pages(substream);
}
/*
* Register access ops. Tegra HDA register access is DWORD only.
*/
static void hda_tegra_writel(u32 value, u32 *addr)
{
writel(value, addr);
}
static u32 hda_tegra_readl(u32 *addr)
{
return readl(addr);
}
static void hda_tegra_writew(u16 value, u16 *addr)
{
unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
void *dword_addr = (void *)((unsigned long)(addr) & ~0x3);
u32 v;
v = readl(dword_addr);
v &= ~(0xffff << shift);
v |= value << shift;
writel(v, dword_addr);
}
static u16 hda_tegra_readw(u16 *addr)
{
unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
void *dword_addr = (void *)((unsigned long)(addr) & ~0x3);
u32 v;
v = readl(dword_addr);
return (v >> shift) & 0xffff;
}
static void hda_tegra_writeb(u8 value, u8 *addr)
{
unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
void *dword_addr = (void *)((unsigned long)(addr) & ~0x3);
u32 v;
v = readl(dword_addr);
v &= ~(0xff << shift);
v |= value << shift;
writel(v, dword_addr);
}
static u8 hda_tegra_readb(u8 *addr)
{
unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
void *dword_addr = (void *)((unsigned long)(addr) & ~0x3);
u32 v;
v = readl(dword_addr);
return (v >> shift) & 0xff;
}
static const struct hda_controller_ops hda_tegra_ops = {
.reg_writel = hda_tegra_writel,
.reg_readl = hda_tegra_readl,
.reg_writew = hda_tegra_writew,
.reg_readw = hda_tegra_readw,
.reg_writeb = hda_tegra_writeb,
.reg_readb = hda_tegra_readb,
.dma_alloc_pages = dma_alloc_pages,
.dma_free_pages = dma_free_pages,
.substream_alloc_pages = substream_alloc_pages,
.substream_free_pages = substream_free_pages,
};
static void hda_tegra_init(struct hda_tegra *hda)
{
u32 v;
/* Enable PCI access */
v = readl(hda->regs + HDA_IPFS_CONFIG);
v |= HDA_IPFS_EN_FPCI;
writel(v, hda->regs + HDA_IPFS_CONFIG);
/* Enable MEM/IO space and bus master */
v = readl(hda->regs + HDA_CFG_CMD);
v &= ~HDA_DISABLE_INTR;
v |= HDA_ENABLE_MEM_SPACE | HDA_ENABLE_IO_SPACE |
HDA_ENABLE_BUS_MASTER | HDA_ENABLE_SERR;
writel(v, hda->regs + HDA_CFG_CMD);
writel(HDA_BAR0_INIT_PROGRAM, hda->regs + HDA_CFG_BAR0);
writel(HDA_BAR0_FINAL_PROGRAM, hda->regs + HDA_CFG_BAR0);
writel(HDA_FPCI_BAR0_START, hda->regs + HDA_IPFS_FPCI_BAR0);
v = readl(hda->regs + HDA_IPFS_INTR_MASK);
v |= HDA_IPFS_EN_INTR;
writel(v, hda->regs + HDA_IPFS_INTR_MASK);
}
static int hda_tegra_enable_clocks(struct hda_tegra *data)
{
int rc;
rc = clk_prepare_enable(data->hda_clk);
if (rc)
return rc;
rc = clk_prepare_enable(data->hda2codec_2x_clk);
if (rc)
goto disable_hda;
rc = clk_prepare_enable(data->hda2hdmi_clk);
if (rc)
goto disable_codec_2x;
return 0;
disable_codec_2x:
clk_disable_unprepare(data->hda2codec_2x_clk);
disable_hda:
clk_disable_unprepare(data->hda_clk);
return rc;
}
static void hda_tegra_disable_clocks(struct hda_tegra *data)
{
clk_disable_unprepare(data->hda2hdmi_clk);
clk_disable_unprepare(data->hda2codec_2x_clk);
clk_disable_unprepare(data->hda_clk);
}
#ifdef CONFIG_PM_SLEEP
/*
* power management
*/
static int hda_tegra_suspend(struct device *dev)
{
struct snd_card *card = dev_get_drvdata(dev);
struct azx *chip = card->private_data;
struct azx_pcm *p;
struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
list_for_each_entry(p, &chip->pcm_list, list)
snd_pcm_suspend_all(p->pcm);
if (chip->initialized)
snd_hda_suspend(chip->bus);
azx_stop_chip(chip);
azx_enter_link_reset(chip);
hda_tegra_disable_clocks(hda);
return 0;
}
static int hda_tegra_resume(struct device *dev)
{
struct snd_card *card = dev_get_drvdata(dev);
struct azx *chip = card->private_data;
struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
int status;
hda_tegra_enable_clocks(hda);
/* Read STATESTS before controller reset */
status = azx_readw(chip, STATESTS);
hda_tegra_init(hda);
azx_init_chip(chip, 1);
snd_hda_resume(chip->bus);
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
#endif /* CONFIG_PM_SLEEP */
static const struct dev_pm_ops hda_tegra_pm = {
SET_SYSTEM_SLEEP_PM_OPS(hda_tegra_suspend, hda_tegra_resume)
};
/*
* reboot notifier for hang-up problem at power-down
*/
static int hda_tegra_halt(struct notifier_block *nb, unsigned long event,
void *buf)
{
struct azx *chip = container_of(nb, struct azx, reboot_notifier);
snd_hda_bus_reboot_notify(chip->bus);
azx_stop_chip(chip);
return NOTIFY_OK;
}
static void hda_tegra_notifier_register(struct azx *chip)
{
chip->reboot_notifier.notifier_call = hda_tegra_halt;
register_reboot_notifier(&chip->reboot_notifier);
}
static void hda_tegra_notifier_unregister(struct azx *chip)
{
if (chip->reboot_notifier.notifier_call)
unregister_reboot_notifier(&chip->reboot_notifier);
}
/*
* destructor
*/
static int hda_tegra_dev_free(struct snd_device *device)
{
int i;
struct azx *chip = device->device_data;
hda_tegra_notifier_unregister(chip);
if (chip->initialized) {
for (i = 0; i < chip->num_streams; i++)
azx_stream_stop(chip, &chip->azx_dev[i]);
azx_stop_chip(chip);
}
azx_free_stream_pages(chip);
return 0;
}
static int hda_tegra_init_chip(struct azx *chip, struct platform_device *pdev)
{
struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
struct device *dev = hda->dev;
struct resource *res;
int err;
hda->hda_clk = devm_clk_get(dev, "hda");
if (IS_ERR(hda->hda_clk))
return PTR_ERR(hda->hda_clk);
hda->hda2codec_2x_clk = devm_clk_get(dev, "hda2codec_2x");
if (IS_ERR(hda->hda2codec_2x_clk))
return PTR_ERR(hda->hda2codec_2x_clk);
hda->hda2hdmi_clk = devm_clk_get(dev, "hda2hdmi");
if (IS_ERR(hda->hda2hdmi_clk))
return PTR_ERR(hda->hda2hdmi_clk);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
hda->regs = devm_ioremap_resource(dev, res);
if (IS_ERR(chip->remap_addr))
return PTR_ERR(chip->remap_addr);
chip->remap_addr = hda->regs + HDA_BAR0;
chip->addr = res->start + HDA_BAR0;
err = hda_tegra_enable_clocks(hda);
if (err)
return err;
hda_tegra_init(hda);
return 0;
}
/*
* The codecs were powered up in snd_hda_codec_new().
* Now all initialization done, so turn them down if possible
*/
static void power_down_all_codecs(struct azx *chip)
{
struct hda_codec *codec;
list_for_each_entry(codec, &chip->bus->codec_list, list)
snd_hda_power_down(codec);
}
static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev)
{
struct snd_card *card = chip->card;
int err;
unsigned short gcap;
int irq_id = platform_get_irq(pdev, 0);
err = hda_tegra_init_chip(chip, pdev);
if (err)
return err;
err = devm_request_irq(chip->card->dev, irq_id, azx_interrupt,
IRQF_SHARED, KBUILD_MODNAME, chip);
if (err) {
dev_err(chip->card->dev,
"unable to request IRQ %d, disabling device\n",
irq_id);
return err;
}
chip->irq = irq_id;
synchronize_irq(chip->irq);
gcap = azx_readw(chip, GCAP);
dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap);
/* read number of streams from GCAP register instead of using
* hardcoded value
*/
chip->capture_streams = (gcap >> 8) & 0x0f;
chip->playback_streams = (gcap >> 12) & 0x0f;
if (!chip->playback_streams && !chip->capture_streams) {
/* gcap didn't give any info, switching to old method */
chip->playback_streams = NUM_PLAYBACK_SD;
chip->capture_streams = NUM_CAPTURE_SD;
}
chip->capture_index_offset = 0;
chip->playback_index_offset = chip->capture_streams;
chip->num_streams = chip->playback_streams + chip->capture_streams;
chip->azx_dev = devm_kcalloc(card->dev, chip->num_streams,
sizeof(*chip->azx_dev), GFP_KERNEL);
if (!chip->azx_dev)
return -ENOMEM;
err = azx_alloc_stream_pages(chip);
if (err < 0)
return err;
/* initialize streams */
azx_init_stream(chip);
/* initialize chip */
azx_init_chip(chip, 1);
/* codec detection */
if (!chip->codec_mask) {
dev_err(card->dev, "no codecs found!\n");
return -ENODEV;
}
strcpy(card->driver, "tegra-hda");
strcpy(card->shortname, "tegra-hda");
snprintf(card->longname, sizeof(card->longname),
"%s at 0x%lx irq %i",
card->shortname, chip->addr, chip->irq);
return 0;
}
/*
* constructor
*/
static int hda_tegra_create(struct snd_card *card,
unsigned int driver_caps,
const struct hda_controller_ops *hda_ops,
struct hda_tegra *hda)
{
static struct snd_device_ops ops = {
.dev_free = hda_tegra_dev_free,
};
struct azx *chip;
int err;
chip = &hda->chip;
spin_lock_init(&chip->reg_lock);
mutex_init(&chip->open_mutex);
chip->card = card;
chip->ops = hda_ops;
chip->irq = -1;
chip->driver_caps = driver_caps;
chip->driver_type = driver_caps & 0xff;
chip->dev_index = 0;
INIT_LIST_HEAD(&chip->pcm_list);
INIT_LIST_HEAD(&chip->list);
chip->position_fix[0] = POS_FIX_AUTO;
chip->position_fix[1] = POS_FIX_AUTO;
chip->codec_probe_mask = -1;
chip->single_cmd = false;
chip->snoop = true;
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
if (err < 0) {
dev_err(card->dev, "Error creating device\n");
return err;
}
return 0;
}
static const struct of_device_id hda_tegra_match[] = {
{ .compatible = "nvidia,tegra30-hda" },
{},
};
MODULE_DEVICE_TABLE(of, hda_tegra_match);
static int hda_tegra_probe(struct platform_device *pdev)
{
struct snd_card *card;
struct azx *chip;
struct hda_tegra *hda;
int err;
const unsigned int driver_flags = AZX_DCAPS_RIRB_DELAY;
hda = devm_kzalloc(&pdev->dev, sizeof(*hda), GFP_KERNEL);
if (!hda)
return -ENOMEM;
hda->dev = &pdev->dev;
chip = &hda->chip;
err = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
THIS_MODULE, 0, &card);
if (err < 0) {
dev_err(&pdev->dev, "Error creating card!\n");
return err;
}
err = hda_tegra_create(card, driver_flags, &hda_tegra_ops, hda);
if (err < 0)
goto out_free;
card->private_data = chip;
dev_set_drvdata(&pdev->dev, card);
err = hda_tegra_first_init(chip, pdev);
if (err < 0)
goto out_free;
/* create codec instances */
err = azx_codec_create(chip, NULL, 0, &power_save);
if (err < 0)
goto out_free;
err = azx_codec_configure(chip);
if (err < 0)
goto out_free;
/* create PCM streams */
err = snd_hda_build_pcms(chip->bus);
if (err < 0)
goto out_free;
/* create mixer controls */
err = azx_mixer_create(chip);
if (err < 0)
goto out_free;
err = snd_card_register(chip->card);
if (err < 0)
goto out_free;
chip->running = 1;
power_down_all_codecs(chip);
hda_tegra_notifier_register(chip);
return 0;
out_free:
snd_card_free(card);
return err;
}
static int hda_tegra_remove(struct platform_device *pdev)
{
return snd_card_free(dev_get_drvdata(&pdev->dev));
}
static struct platform_driver tegra_platform_hda = {
.driver = {
.name = "tegra-hda",
.pm = &hda_tegra_pm,
.of_match_table = hda_tegra_match,
},
.probe = hda_tegra_probe,
.remove = hda_tegra_remove,
};
module_platform_driver(tegra_platform_hda);
MODULE_DESCRIPTION("Tegra HDA bus driver");
MODULE_LICENSE("GPL v2");

View File

@ -332,6 +332,7 @@ static const struct hda_fixup ad1986a_fixups[] = {
static const struct snd_pci_quirk ad1986a_fixup_tbl[] = { static const struct snd_pci_quirk ad1986a_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_FIXUP_LAPTOP_IMIC), SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_FIXUP_LAPTOP_IMIC),
SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8JN", AD1986A_FIXUP_EAPD),
SND_PCI_QUIRK_MASK(0x1043, 0xff00, 0x8100, "ASUS P5", AD1986A_FIXUP_3STACK), SND_PCI_QUIRK_MASK(0x1043, 0xff00, 0x8100, "ASUS P5", AD1986A_FIXUP_3STACK),
SND_PCI_QUIRK_MASK(0x1043, 0xff00, 0x8200, "ASUS M2", AD1986A_FIXUP_3STACK), SND_PCI_QUIRK_MASK(0x1043, 0xff00, 0x8200, "ASUS M2", AD1986A_FIXUP_3STACK),
SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_FIXUP_3STACK), SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_FIXUP_3STACK),

View File

@ -1127,10 +1127,6 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
AMP_OUT_UNMUTE); AMP_OUT_UNMUTE);
eld = &per_pin->sink_eld; eld = &per_pin->sink_eld;
if (!eld->monitor_present) {
hdmi_set_channel_count(codec, per_pin->cvt_nid, channels);
return;
}
if (!non_pcm && per_pin->chmap_set) if (!non_pcm && per_pin->chmap_set)
ca = hdmi_manual_channel_allocation(channels, per_pin->chmap); ca = hdmi_manual_channel_allocation(channels, per_pin->chmap);
@ -3324,6 +3320,7 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = {
{ .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_nvhdmi }, { .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_nvhdmi },
{ .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_nvhdmi }, { .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_nvhdmi },
{ .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_nvhdmi }, { .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_nvhdmi },
{ .id = 0x10de0028, .name = "Tegra12x HDMI", .patch = patch_nvhdmi },
{ .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_nvhdmi }, { .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_nvhdmi },
{ .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_nvhdmi }, { .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_nvhdmi },
{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_nvhdmi }, { .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_nvhdmi },
@ -3380,6 +3377,7 @@ MODULE_ALIAS("snd-hda-codec-id:10de0019");
MODULE_ALIAS("snd-hda-codec-id:10de001a"); MODULE_ALIAS("snd-hda-codec-id:10de001a");
MODULE_ALIAS("snd-hda-codec-id:10de001b"); MODULE_ALIAS("snd-hda-codec-id:10de001b");
MODULE_ALIAS("snd-hda-codec-id:10de001c"); MODULE_ALIAS("snd-hda-codec-id:10de001c");
MODULE_ALIAS("snd-hda-codec-id:10de0028");
MODULE_ALIAS("snd-hda-codec-id:10de0040"); MODULE_ALIAS("snd-hda-codec-id:10de0040");
MODULE_ALIAS("snd-hda-codec-id:10de0041"); MODULE_ALIAS("snd-hda-codec-id:10de0041");
MODULE_ALIAS("snd-hda-codec-id:10de0042"); MODULE_ALIAS("snd-hda-codec-id:10de0042");

View File

@ -951,7 +951,9 @@ static struct alc_codec_rename_pci_table rename_pci_tbl[] = {
{ 0x10ec0280, 0x1028, 0, "ALC3220" }, { 0x10ec0280, 0x1028, 0, "ALC3220" },
{ 0x10ec0282, 0x1028, 0, "ALC3221" }, { 0x10ec0282, 0x1028, 0, "ALC3221" },
{ 0x10ec0283, 0x1028, 0, "ALC3223" }, { 0x10ec0283, 0x1028, 0, "ALC3223" },
{ 0x10ec0288, 0x1028, 0, "ALC3263" },
{ 0x10ec0292, 0x1028, 0, "ALC3226" }, { 0x10ec0292, 0x1028, 0, "ALC3226" },
{ 0x10ec0293, 0x1028, 0, "ALC3235" },
{ 0x10ec0255, 0x1028, 0, "ALC3234" }, { 0x10ec0255, 0x1028, 0, "ALC3234" },
{ 0x10ec0668, 0x1028, 0, "ALC3661" }, { 0x10ec0668, 0x1028, 0, "ALC3661" },
{ } /* terminator */ { } /* terminator */
@ -1647,12 +1649,10 @@ static const struct hda_fixup alc260_fixups[] = {
[ALC260_FIXUP_COEF] = { [ALC260_FIXUP_COEF] = {
.type = HDA_FIXUP_VERBS, .type = HDA_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) { .v.verbs = (const struct hda_verb[]) {
{ 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, { 0x1a, AC_VERB_SET_COEF_INDEX, 0x07 },
{ 0x20, AC_VERB_SET_PROC_COEF, 0x3040 }, { 0x1a, AC_VERB_SET_PROC_COEF, 0x3040 },
{ } { }
}, },
.chained = true,
.chain_id = ALC260_FIXUP_HP_PIN_0F,
}, },
[ALC260_FIXUP_GPIO1] = { [ALC260_FIXUP_GPIO1] = {
.type = HDA_FIXUP_VERBS, .type = HDA_FIXUP_VERBS,
@ -1667,8 +1667,8 @@ static const struct hda_fixup alc260_fixups[] = {
[ALC260_FIXUP_REPLACER] = { [ALC260_FIXUP_REPLACER] = {
.type = HDA_FIXUP_VERBS, .type = HDA_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) { .v.verbs = (const struct hda_verb[]) {
{ 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, { 0x1a, AC_VERB_SET_COEF_INDEX, 0x07 },
{ 0x20, AC_VERB_SET_PROC_COEF, 0x3050 }, { 0x1a, AC_VERB_SET_PROC_COEF, 0x3050 },
{ } { }
}, },
.chained = true, .chained = true,
@ -3522,6 +3522,7 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec)
/* Direct Drive HP Amp control */ /* Direct Drive HP Amp control */
alc_write_coefex_idx(codec, 0x57, 0x03, 0x8aa6); alc_write_coefex_idx(codec, 0x57, 0x03, 0x8aa6);
break; break;
case 0x10ec0233:
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);
@ -3538,6 +3539,25 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec)
alc_write_coef_idx(codec, 0x18, 0x7308); alc_write_coef_idx(codec, 0x18, 0x7308);
alc_write_coef_idx(codec, 0x6b, 0xc429); alc_write_coef_idx(codec, 0x6b, 0xc429);
break; break;
case 0x10ec0293:
/* SET Line1 JD to 0 */
val = alc_read_coef_idx(codec, 0x10);
alc_write_coef_idx(codec, 0x10, (val & ~(7<<8)) | 6<<8);
/* SET charge pump by verb */
val = alc_read_coefex_idx(codec, 0x57, 0x05);
alc_write_coefex_idx(codec, 0x57, 0x05, (val & ~(1<<15|1<<13)) | 0x0);
/* SET EN_OSW to 1 */
val = alc_read_coefex_idx(codec, 0x57, 0x03);
alc_write_coefex_idx(codec, 0x57, 0x03, (val & ~(1<<10)) | (1<<10) );
/* Combo JD gating with LINE1-VREFO */
val = alc_read_coef_idx(codec, 0x1a);
alc_write_coef_idx(codec, 0x1a, (val & ~(1<<3)) | (1<<3));
/* Set to TRS type */
alc_write_coef_idx(codec, 0x45, 0xc429);
/* Combo Jack auto detect */
val = alc_read_coef_idx(codec, 0x4a);
alc_write_coef_idx(codec, 0x4a, (val & 0xfff0) | 0x000e);
break;
case 0x10ec0668: case 0x10ec0668:
alc_write_coef_idx(codec, 0x15, 0x0d40); alc_write_coef_idx(codec, 0x15, 0x0d40);
alc_write_coef_idx(codec, 0xb7, 0x802b); alc_write_coef_idx(codec, 0xb7, 0x802b);
@ -3561,6 +3581,7 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin,
alc_write_coef_idx(codec, 0x06, 0x6100); alc_write_coef_idx(codec, 0x06, 0x6100);
snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
break; break;
case 0x10ec0233:
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);
@ -3576,6 +3597,21 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin,
alc_write_coef_idx(codec, 0x19, 0xa208); alc_write_coef_idx(codec, 0x19, 0xa208);
alc_write_coef_idx(codec, 0x2e, 0xacf0); alc_write_coef_idx(codec, 0x2e, 0xacf0);
break; break;
case 0x10ec0293:
/* Set to TRS mode */
alc_write_coef_idx(codec, 0x45, 0xc429);
snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
/* SET charge pump by verb */
val = alc_read_coefex_idx(codec, 0x57, 0x05);
alc_write_coefex_idx(codec, 0x57, 0x05, (val & ~(1<<15|1<<13)) | (1<<15|1<<13));
/* SET EN_OSW to 0 */
val = alc_read_coefex_idx(codec, 0x57, 0x03);
alc_write_coefex_idx(codec, 0x57, 0x03, (val & ~(1<<10)) | 0x0);
/* Combo JD gating without LINE1-VREFO */
val = alc_read_coef_idx(codec, 0x1a);
alc_write_coef_idx(codec, 0x1a, (val & ~(1<<3)) | 0x0);
snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
break;
case 0x10ec0668: case 0x10ec0668:
alc_write_coef_idx(codec, 0x11, 0x0001); alc_write_coef_idx(codec, 0x11, 0x0001);
snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
@ -3591,6 +3627,8 @@ 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)
{ {
int val;
switch (codec->vendor_id) { switch (codec->vendor_id) {
case 0x10ec0255: case 0x10ec0255:
alc_write_coef_idx(codec, 0x45, 0xc089); alc_write_coef_idx(codec, 0x45, 0xc089);
@ -3598,6 +3636,7 @@ static void alc_headset_mode_default(struct hda_codec *codec)
alc_write_coefex_idx(codec, 0x57, 0x03, 0x8ea6); alc_write_coefex_idx(codec, 0x57, 0x03, 0x8ea6);
alc_write_coef_idx(codec, 0x49, 0x0049); alc_write_coef_idx(codec, 0x49, 0x0049);
break; break;
case 0x10ec0233:
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);
@ -3608,6 +3647,16 @@ static void alc_headset_mode_default(struct hda_codec *codec)
alc_write_coef_idx(codec, 0x6b, 0xc429); alc_write_coef_idx(codec, 0x6b, 0xc429);
alc_write_coef_idx(codec, 0x18, 0x7308); alc_write_coef_idx(codec, 0x18, 0x7308);
break; break;
case 0x10ec0293:
/* Combo Jack auto detect */
val = alc_read_coef_idx(codec, 0x4a);
alc_write_coef_idx(codec, 0x4a, (val & 0xfff0) | 0x000e);
/* Set to TRS type */
alc_write_coef_idx(codec, 0x45, 0xC429);
/* Combo JD gating without LINE1-VREFO */
val = alc_read_coef_idx(codec, 0x1a);
alc_write_coef_idx(codec, 0x1a, (val & ~(1<<3)) | 0x0);
break;
case 0x10ec0668: case 0x10ec0668:
alc_write_coef_idx(codec, 0x11, 0x0041); alc_write_coef_idx(codec, 0x11, 0x0041);
alc_write_coef_idx(codec, 0x15, 0x0d40); alc_write_coef_idx(codec, 0x15, 0x0d40);
@ -3620,6 +3669,8 @@ static void alc_headset_mode_default(struct hda_codec *codec)
/* Iphone type */ /* Iphone type */
static void alc_headset_mode_ctia(struct hda_codec *codec) static void alc_headset_mode_ctia(struct hda_codec *codec)
{ {
int val;
switch (codec->vendor_id) { switch (codec->vendor_id) {
case 0x10ec0255: case 0x10ec0255:
/* Set to CTIA type */ /* Set to CTIA type */
@ -3627,6 +3678,7 @@ static void alc_headset_mode_ctia(struct hda_codec *codec)
alc_write_coef_idx(codec, 0x1b, 0x0c2b); alc_write_coef_idx(codec, 0x1b, 0x0c2b);
alc_write_coefex_idx(codec, 0x57, 0x03, 0x8ea6); alc_write_coefex_idx(codec, 0x57, 0x03, 0x8ea6);
break; break;
case 0x10ec0233:
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);
@ -3637,6 +3689,13 @@ static void alc_headset_mode_ctia(struct hda_codec *codec)
alc_write_coef_idx(codec, 0x76, 0x0008); alc_write_coef_idx(codec, 0x76, 0x0008);
alc_write_coef_idx(codec, 0x18, 0x7388); alc_write_coef_idx(codec, 0x18, 0x7388);
break; break;
case 0x10ec0293:
/* Set to ctia type */
alc_write_coef_idx(codec, 0x45, 0xd429);
/* SET Line1 JD to 1 */
val = alc_read_coef_idx(codec, 0x10);
alc_write_coef_idx(codec, 0x10, (val & ~(7<<8)) | 7<<8);
break;
case 0x10ec0668: case 0x10ec0668:
alc_write_coef_idx(codec, 0x11, 0x0001); alc_write_coef_idx(codec, 0x11, 0x0001);
alc_write_coef_idx(codec, 0x15, 0x0d60); alc_write_coef_idx(codec, 0x15, 0x0d60);
@ -3649,6 +3708,8 @@ static void alc_headset_mode_ctia(struct hda_codec *codec)
/* Nokia type */ /* Nokia type */
static void alc_headset_mode_omtp(struct hda_codec *codec) static void alc_headset_mode_omtp(struct hda_codec *codec)
{ {
int val;
switch (codec->vendor_id) { switch (codec->vendor_id) {
case 0x10ec0255: case 0x10ec0255:
/* Set to OMTP Type */ /* Set to OMTP Type */
@ -3656,6 +3717,7 @@ static void alc_headset_mode_omtp(struct hda_codec *codec)
alc_write_coef_idx(codec, 0x1b, 0x0c2b); alc_write_coef_idx(codec, 0x1b, 0x0c2b);
alc_write_coefex_idx(codec, 0x57, 0x03, 0x8ea6); alc_write_coefex_idx(codec, 0x57, 0x03, 0x8ea6);
break; break;
case 0x10ec0233:
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);
@ -3666,6 +3728,13 @@ static void alc_headset_mode_omtp(struct hda_codec *codec)
alc_write_coef_idx(codec, 0x76, 0x0008); alc_write_coef_idx(codec, 0x76, 0x0008);
alc_write_coef_idx(codec, 0x18, 0x7388); alc_write_coef_idx(codec, 0x18, 0x7388);
break; break;
case 0x10ec0293:
/* Set to omtp type */
alc_write_coef_idx(codec, 0x45, 0xe429);
/* SET Line1 JD to 1 */
val = alc_read_coef_idx(codec, 0x10);
alc_write_coef_idx(codec, 0x10, (val & ~(7<<8)) | 7<<8);
break;
case 0x10ec0668: case 0x10ec0668:
alc_write_coef_idx(codec, 0x11, 0x0001); alc_write_coef_idx(codec, 0x11, 0x0001);
alc_write_coef_idx(codec, 0x15, 0x0d50); alc_write_coef_idx(codec, 0x15, 0x0d50);
@ -3691,6 +3760,7 @@ static void alc_determine_headset_type(struct hda_codec *codec)
val = alc_read_coef_idx(codec, 0x46); val = alc_read_coef_idx(codec, 0x46);
is_ctia = (val & 0x0070) == 0x0070; is_ctia = (val & 0x0070) == 0x0070;
break; break;
case 0x10ec0233:
case 0x10ec0283: case 0x10ec0283:
alc_write_coef_idx(codec, 0x45, 0xd029); alc_write_coef_idx(codec, 0x45, 0xd029);
msleep(300); msleep(300);
@ -3703,6 +3773,16 @@ static void alc_determine_headset_type(struct hda_codec *codec)
val = alc_read_coef_idx(codec, 0x6c); val = alc_read_coef_idx(codec, 0x6c);
is_ctia = (val & 0x001c) == 0x001c; is_ctia = (val & 0x001c) == 0x001c;
break; break;
case 0x10ec0293:
/* Combo Jack auto detect */
val = alc_read_coef_idx(codec, 0x4a);
alc_write_coef_idx(codec, 0x4a, (val & 0xfff0) | 0x0008);
/* Set to ctia type */
alc_write_coef_idx(codec, 0x45, 0xD429);
msleep(300);
val = alc_read_coef_idx(codec, 0x46);
is_ctia = (val & 0x0070) == 0x0070;
break;
case 0x10ec0668: case 0x10ec0668:
alc_write_coef_idx(codec, 0x11, 0x0001); alc_write_coef_idx(codec, 0x11, 0x0001);
alc_write_coef_idx(codec, 0xb7, 0x802b); alc_write_coef_idx(codec, 0xb7, 0x802b);
@ -3894,6 +3974,39 @@ static void alc_fixup_no_shutup(struct hda_codec *codec,
} }
} }
static void alc_fixup_disable_aamix(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
if (action == HDA_FIXUP_ACT_PRE_PROBE) {
struct alc_spec *spec = codec->spec;
/* Disable AA-loopback as it causes white noise */
spec->gen.mixer_nid = 0;
}
}
static unsigned int alc_power_filter_xps13(struct hda_codec *codec,
hda_nid_t nid,
unsigned int power_state)
{
struct alc_spec *spec = codec->spec;
/* Avoid pop noises when headphones are plugged in */
if (spec->gen.hp_jack_present)
if (nid == codec->afg || nid == 0x02)
return AC_PWRST_D0;
return power_state;
}
static void alc_fixup_dell_xps13(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
if (action == HDA_FIXUP_ACT_PROBE) {
struct alc_spec *spec = codec->spec;
spec->shutup = alc_no_shutup;
codec->power_filter = alc_power_filter_xps13;
}
}
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)
{ {
@ -4110,6 +4223,7 @@ enum {
ALC269_FIXUP_ASUS_G73JW, ALC269_FIXUP_ASUS_G73JW,
ALC269_FIXUP_LENOVO_EAPD, ALC269_FIXUP_LENOVO_EAPD,
ALC275_FIXUP_SONY_HWEQ, ALC275_FIXUP_SONY_HWEQ,
ALC275_FIXUP_SONY_DISABLE_AAMIX,
ALC271_FIXUP_DMIC, ALC271_FIXUP_DMIC,
ALC269_FIXUP_PCM_44K, ALC269_FIXUP_PCM_44K,
ALC269_FIXUP_STEREO_DMIC, ALC269_FIXUP_STEREO_DMIC,
@ -4159,6 +4273,8 @@ enum {
ALC255_FIXUP_DELL2_MIC_NO_PRESENCE, ALC255_FIXUP_DELL2_MIC_NO_PRESENCE,
ALC255_FIXUP_HEADSET_MODE, ALC255_FIXUP_HEADSET_MODE,
ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC, ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC,
ALC293_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC292_FIXUP_TPT440_DOCK,
}; };
static const struct hda_fixup alc269_fixups[] = { static const struct hda_fixup alc269_fixups[] = {
@ -4213,6 +4329,12 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true, .chained = true,
.chain_id = ALC275_FIXUP_SONY_VAIO_GPIO2 .chain_id = ALC275_FIXUP_SONY_VAIO_GPIO2
}, },
[ALC275_FIXUP_SONY_DISABLE_AAMIX] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_disable_aamix,
.chained = true,
.chain_id = ALC269_FIXUP_SONY_VAIO
},
[ALC271_FIXUP_DMIC] = { [ALC271_FIXUP_DMIC] = {
.type = HDA_FIXUP_FUNC, .type = HDA_FIXUP_FUNC,
.v.func = alc271_fixup_dmic, .v.func = alc271_fixup_dmic,
@ -4552,6 +4674,26 @@ static const struct hda_fixup alc269_fixups[] = {
.type = HDA_FIXUP_FUNC, .type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_headset_mode_alc255_no_hp_mic, .v.func = alc_fixup_headset_mode_alc255_no_hp_mic,
}, },
[ALC293_FIXUP_DELL1_MIC_NO_PRESENCE] = {
.type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) {
{ 0x18, 0x01a1913d }, /* use as headphone mic, without its own jack detect */
{ 0x1a, 0x01a1913c }, /* use as headset mic, without its own jack detect */
{ }
},
.chained = true,
.chain_id = ALC269_FIXUP_HEADSET_MODE
},
[ALC292_FIXUP_TPT440_DOCK] = {
.type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) {
{ 0x16, 0x21211010 }, /* dock headphone */
{ 0x19, 0x21a11010 }, /* dock mic */
{ }
},
.chained = true,
.chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST
},
}; };
static const struct snd_pci_quirk alc269_fixup_tbl[] = { static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@ -4595,31 +4737,16 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x0606, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0606, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0608, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0608, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
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, 0x060f, "Dell", ALC269_FIXUP_DELL3_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0610, "Dell", ALC269_FIXUP_DELL3_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, 0x0614, "Dell Inspiron 3135", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0614, "Dell Inspiron 3135", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0615, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK), SND_PCI_QUIRK(0x1028, 0x0615, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK),
SND_PCI_QUIRK(0x1028, 0x0616, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK), SND_PCI_QUIRK(0x1028, 0x0616, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK),
SND_PCI_QUIRK(0x1028, 0x061f, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0629, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x062c, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x062c, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x062e, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0632, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0638, "Dell Inspiron 5439", ALC290_FIXUP_MONO_SPEAKERS_HSJACK), SND_PCI_QUIRK(0x1028, 0x0638, "Dell Inspiron 5439", ALC290_FIXUP_MONO_SPEAKERS_HSJACK),
SND_PCI_QUIRK(0x1028, 0x063e, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x063f, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x063f, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0640, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x064a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x064b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x064d, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x064d, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0651, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0652, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0653, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0657, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0658, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x065c, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x065f, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0662, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0667, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0668, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0668, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0669, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0669, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0674, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0674, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
@ -4629,6 +4756,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x0684, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0684, "Dell", ALC269_FIXUP_DELL2_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(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_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),
@ -4702,6 +4831,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x104d, 0x9073, "Sony VAIO", ALC275_FIXUP_SONY_VAIO_GPIO2), SND_PCI_QUIRK(0x104d, 0x9073, "Sony VAIO", ALC275_FIXUP_SONY_VAIO_GPIO2),
SND_PCI_QUIRK(0x104d, 0x907b, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), SND_PCI_QUIRK(0x104d, 0x907b, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ),
SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ),
SND_PCI_QUIRK(0x104d, 0x9099, "Sony VAIO S13", ALC275_FIXUP_SONY_DISABLE_AAMIX),
SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO), SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO),
SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook", ALC269_FIXUP_LIFEBOOK), SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook", ALC269_FIXUP_LIFEBOOK),
SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE), SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE),
@ -4715,7 +4845,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x21fb, "Thinkpad T430s", ALC269_FIXUP_LENOVO_DOCK), SND_PCI_QUIRK(0x17aa, 0x21fb, "Thinkpad T430s", ALC269_FIXUP_LENOVO_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2203, "Thinkpad X230 Tablet", ALC269_FIXUP_LENOVO_DOCK), SND_PCI_QUIRK(0x17aa, 0x2203, "Thinkpad X230 Tablet", ALC269_FIXUP_LENOVO_DOCK),
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 T440s", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x220e, "Thinkpad T440p", ALC292_FIXUP_TPT440_DOCK),
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_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
@ -4793,9 +4924,215 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
{.id = ALC269_FIXUP_DELL2_MIC_NO_PRESENCE, .name = "dell-headset-dock"}, {.id = ALC269_FIXUP_DELL2_MIC_NO_PRESENCE, .name = "dell-headset-dock"},
{.id = ALC283_FIXUP_CHROME_BOOK, .name = "alc283-dac-wcaps"}, {.id = ALC283_FIXUP_CHROME_BOOK, .name = "alc283-dac-wcaps"},
{.id = ALC283_FIXUP_SENSE_COMBO_JACK, .name = "alc283-sense-combo"}, {.id = ALC283_FIXUP_SENSE_COMBO_JACK, .name = "alc283-sense-combo"},
{.id = ALC292_FIXUP_TPT440_DOCK, .name = "tpt440-dock"},
{} {}
}; };
static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
{
.codec = 0x10ec0255,
.subvendor = 0x1028,
#ifdef CONFIG_SND_DEBUG_VERBOSE
.name = "Dell",
#endif
.pins = (const struct hda_pintbl[]) {
{0x12, 0x90a60140},
{0x14, 0x90170110},
{0x17, 0x40000000},
{0x18, 0x411111f0},
{0x19, 0x411111f0},
{0x1a, 0x411111f0},
{0x1b, 0x411111f0},
{0x1d, 0x40700001},
{0x1e, 0x411111f0},
{0x21, 0x02211020},
},
.value = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
},
{
.codec = 0x10ec0255,
.subvendor = 0x1028,
#ifdef CONFIG_SND_DEBUG_VERBOSE
.name = "Dell",
#endif
.pins = (const struct hda_pintbl[]) {
{0x12, 0x90a60160},
{0x14, 0x90170120},
{0x17, 0x40000000},
{0x18, 0x411111f0},
{0x19, 0x411111f0},
{0x1a, 0x411111f0},
{0x1b, 0x411111f0},
{0x1d, 0x40700001},
{0x1e, 0x411111f0},
{0x21, 0x02211030},
},
.value = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
},
{
.codec = 0x10ec0255,
.subvendor = 0x1028,
#ifdef CONFIG_SND_DEBUG_VERBOSE
.name = "Dell",
#endif
.pins = (const struct hda_pintbl[]) {
{0x12, 0x90a60160},
{0x14, 0x90170130},
{0x17, 0x40000000},
{0x18, 0x411111f0},
{0x19, 0x411111f0},
{0x1a, 0x411111f0},
{0x1b, 0x411111f0},
{0x1d, 0x40700001},
{0x1e, 0x411111f0},
{0x21, 0x02211040},
},
.value = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
},
{
.codec = 0x10ec0255,
.subvendor = 0x1028,
#ifdef CONFIG_SND_DEBUG_VERBOSE
.name = "Dell",
#endif
.pins = (const struct hda_pintbl[]) {
{0x12, 0x90a60160},
{0x14, 0x90170140},
{0x17, 0x40000000},
{0x18, 0x411111f0},
{0x19, 0x411111f0},
{0x1a, 0x411111f0},
{0x1b, 0x411111f0},
{0x1d, 0x40700001},
{0x1e, 0x411111f0},
{0x21, 0x02211050},
},
.value = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
},
{
.codec = 0x10ec0255,
.subvendor = 0x1028,
#ifdef CONFIG_SND_DEBUG_VERBOSE
.name = "Dell",
#endif
.pins = (const struct hda_pintbl[]) {
{0x12, 0x90a60170},
{0x14, 0x90170120},
{0x17, 0x40000000},
{0x18, 0x411111f0},
{0x19, 0x411111f0},
{0x1a, 0x411111f0},
{0x1b, 0x411111f0},
{0x1d, 0x40700001},
{0x1e, 0x411111f0},
{0x21, 0x02211030},
},
.value = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
},
{
.codec = 0x10ec0255,
.subvendor = 0x1028,
#ifdef CONFIG_SND_DEBUG_VERBOSE
.name = "Dell",
#endif
.pins = (const struct hda_pintbl[]) {
{0x12, 0x90a60170},
{0x14, 0x90170130},
{0x17, 0x40000000},
{0x18, 0x411111f0},
{0x19, 0x411111f0},
{0x1a, 0x411111f0},
{0x1b, 0x411111f0},
{0x1d, 0x40700001},
{0x1e, 0x411111f0},
{0x21, 0x02211040},
},
.value = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
},
{
.codec = 0x10ec0283,
.subvendor = 0x1028,
#ifdef CONFIG_SND_DEBUG_VERBOSE
.name = "Dell",
#endif
.pins = (const struct hda_pintbl[]) {
{0x12, 0x90a60130},
{0x14, 0x90170110},
{0x17, 0x40020008},
{0x18, 0x411111f0},
{0x19, 0x411111f0},
{0x1a, 0x411111f0},
{0x1b, 0x411111f0},
{0x1d, 0x40e00001},
{0x1e, 0x411111f0},
{0x21, 0x0321101f},
},
.value = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
},
{
.codec = 0x10ec0283,
.subvendor = 0x1028,
#ifdef CONFIG_SND_DEBUG_VERBOSE
.name = "Dell",
#endif
.pins = (const struct hda_pintbl[]) {
{0x12, 0x90a60160},
{0x14, 0x90170120},
{0x17, 0x40000000},
{0x18, 0x411111f0},
{0x19, 0x411111f0},
{0x1a, 0x411111f0},
{0x1b, 0x411111f0},
{0x1d, 0x40700001},
{0x1e, 0x411111f0},
{0x21, 0x02211030},
},
.value = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
},
{
.codec = 0x10ec0292,
.subvendor = 0x1028,
#ifdef CONFIG_SND_DEBUG_VERBOSE
.name = "Dell",
#endif
.pins = (const struct hda_pintbl[]) {
{0x12, 0x90a60140},
{0x13, 0x411111f0},
{0x14, 0x90170110},
{0x15, 0x0221401f},
{0x16, 0x411111f0},
{0x18, 0x411111f0},
{0x19, 0x411111f0},
{0x1a, 0x411111f0},
{0x1b, 0x411111f0},
{0x1d, 0x40700001},
{0x1e, 0x411111f0},
},
.value = ALC269_FIXUP_DELL3_MIC_NO_PRESENCE,
},
{
.codec = 0x10ec0293,
.subvendor = 0x1028,
#ifdef CONFIG_SND_DEBUG_VERBOSE
.name = "Dell",
#endif
.pins = (const struct hda_pintbl[]) {
{0x12, 0x40000000},
{0x13, 0x90a60140},
{0x14, 0x90170110},
{0x15, 0x0221401f},
{0x16, 0x21014020},
{0x18, 0x411111f0},
{0x19, 0x21a19030},
{0x1a, 0x411111f0},
{0x1b, 0x411111f0},
{0x1d, 0x40700001},
{0x1e, 0x411111f0},
},
.value = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
},
{}
};
static void alc269_fill_coef(struct hda_codec *codec) static void alc269_fill_coef(struct hda_codec *codec)
{ {
@ -4857,6 +5194,7 @@ static int patch_alc269(struct hda_codec *codec)
snd_hda_pick_fixup(codec, alc269_fixup_models, snd_hda_pick_fixup(codec, alc269_fixup_models,
alc269_fixup_tbl, alc269_fixups); alc269_fixup_tbl, alc269_fixups);
snd_hda_pick_pin_fixup(codec, alc269_pin_fixup_tbl, alc269_fixups);
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
alc_auto_parse_customize_define(codec); alc_auto_parse_customize_define(codec);
@ -5313,6 +5651,8 @@ enum {
ALC662_FIXUP_BASS_1A, ALC662_FIXUP_BASS_1A,
ALC662_FIXUP_BASS_CHMAP, ALC662_FIXUP_BASS_CHMAP,
ALC668_FIXUP_AUTO_MUTE, ALC668_FIXUP_AUTO_MUTE,
ALC668_FIXUP_DELL_DISABLE_AAMIX,
ALC668_FIXUP_DELL_XPS13,
}; };
static const struct hda_fixup alc662_fixups[] = { static const struct hda_fixup alc662_fixups[] = {
@ -5479,6 +5819,18 @@ static const struct hda_fixup alc662_fixups[] = {
.type = HDA_FIXUP_FUNC, .type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_inv_dmic_0x12, .v.func = alc_fixup_inv_dmic_0x12,
}, },
[ALC668_FIXUP_DELL_XPS13] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_dell_xps13,
.chained = true,
.chain_id = ALC668_FIXUP_DELL_DISABLE_AAMIX
},
[ALC668_FIXUP_DELL_DISABLE_AAMIX] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_disable_aamix,
.chained = true,
.chain_id = ALC668_FIXUP_DELL_MIC_NO_PRESENCE
},
[ALC668_FIXUP_AUTO_MUTE] = { [ALC668_FIXUP_AUTO_MUTE] = {
.type = HDA_FIXUP_FUNC, .type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_auto_mute_via_amp, .v.func = alc_fixup_auto_mute_via_amp,
@ -5539,13 +5891,9 @@ 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, 0x060a, "Dell XPS 13", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x060a, "Dell XPS 13", ALC668_FIXUP_DELL_XPS13),
SND_PCI_QUIRK(0x1028, 0x0623, "Dell", ALC668_FIXUP_AUTO_MUTE),
SND_PCI_QUIRK(0x1028, 0x0624, "Dell", ALC668_FIXUP_AUTO_MUTE),
SND_PCI_QUIRK(0x1028, 0x0625, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0625, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0626, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0626, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0628, "Dell", ALC668_FIXUP_AUTO_MUTE),
SND_PCI_QUIRK(0x1028, 0x064e, "Dell", ALC668_FIXUP_AUTO_MUTE),
SND_PCI_QUIRK(0x1028, 0x0696, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0696, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0698, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0698, "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),
@ -5637,6 +5985,73 @@ static const struct hda_model_fixup alc662_fixup_models[] = {
{} {}
}; };
static const struct snd_hda_pin_quirk alc662_pin_fixup_tbl[] = {
{
.codec = 0x10ec0668,
.subvendor = 0x1028,
#ifdef CONFIG_SND_DEBUG_VERBOSE
.name = "Dell",
#endif
.pins = (const struct hda_pintbl[]) {
{0x12, 0x99a30130},
{0x14, 0x90170110},
{0x15, 0x0321101f},
{0x16, 0x03011020},
{0x18, 0x40000008},
{0x19, 0x411111f0},
{0x1a, 0x411111f0},
{0x1b, 0x411111f0},
{0x1d, 0x41000001},
{0x1e, 0x411111f0},
{0x1f, 0x411111f0},
},
.value = ALC668_FIXUP_AUTO_MUTE,
},
{
.codec = 0x10ec0668,
.subvendor = 0x1028,
#ifdef CONFIG_SND_DEBUG_VERBOSE
.name = "Dell",
#endif
.pins = (const struct hda_pintbl[]) {
{0x12, 0x99a30150},
{0x14, 0x90170110},
{0x15, 0x0321101f},
{0x16, 0x03011020},
{0x18, 0x40000008},
{0x19, 0x411111f0},
{0x1a, 0x411111f0},
{0x1b, 0x411111f0},
{0x1d, 0x41000001},
{0x1e, 0x411111f0},
{0x1f, 0x411111f0},
},
.value = ALC668_FIXUP_AUTO_MUTE,
},
{
.codec = 0x10ec0668,
.subvendor = 0x1028,
#ifdef CONFIG_SND_DEBUG_VERBOSE
.name = "Dell",
#endif
.pins = (const struct hda_pintbl[]) {
{0x12, 0x411111f0},
{0x14, 0x90170110},
{0x15, 0x0321101f},
{0x16, 0x03011020},
{0x18, 0x40000008},
{0x19, 0x411111f0},
{0x1a, 0x411111f0},
{0x1b, 0x411111f0},
{0x1d, 0x41000001},
{0x1e, 0x411111f0},
{0x1f, 0x411111f0},
},
.value = ALC668_FIXUP_AUTO_MUTE,
},
{}
};
static void alc662_fill_coef(struct hda_codec *codec) static void alc662_fill_coef(struct hda_codec *codec)
{ {
int val, coef; int val, coef;
@ -5686,6 +6101,7 @@ static int patch_alc662(struct hda_codec *codec)
snd_hda_pick_fixup(codec, alc662_fixup_models, snd_hda_pick_fixup(codec, alc662_fixup_models,
alc662_fixup_tbl, alc662_fixups); alc662_fixup_tbl, alc662_fixups);
snd_hda_pick_pin_fixup(codec, alc662_pin_fixup_tbl, alc662_fixups);
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
alc_auto_parse_customize_define(codec); alc_auto_parse_customize_define(codec);

View File

@ -795,7 +795,7 @@ static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity)
} }
while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) { while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) {
if (sscanf(dev->name, "HP_Mute_LED_%d_%x", if (sscanf(dev->name, "HP_Mute_LED_%u_%x",
&spec->gpio_led_polarity, &spec->gpio_led_polarity,
&spec->gpio_led) == 2) { &spec->gpio_led) == 2) {
unsigned int max_gpio; unsigned int max_gpio;
@ -808,7 +808,7 @@ static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity)
spec->vref_mute_led_nid = spec->gpio_led; spec->vref_mute_led_nid = spec->gpio_led;
return 1; return 1;
} }
if (sscanf(dev->name, "HP_Mute_LED_%d", if (sscanf(dev->name, "HP_Mute_LED_%u",
&spec->gpio_led_polarity) == 1) { &spec->gpio_led_polarity) == 1) {
set_hp_led_gpio(codec); set_hp_led_gpio(codec);
return 1; return 1;

View File

@ -151,7 +151,7 @@ static void lola_proc_codec_rw_write(struct snd_info_entry *entry,
char line[64]; char line[64];
unsigned int id, verb, data, extdata; unsigned int id, verb, data, extdata;
while (!snd_info_get_line(buffer, line, sizeof(line))) { while (!snd_info_get_line(buffer, line, sizeof(line))) {
if (sscanf(line, "%i %i %i %i", &id, &verb, &data, &extdata) != 4) if (sscanf(line, "%u %u %u %u", &id, &verb, &data, &extdata) != 4)
continue; continue;
lola_codec_read(chip, id, verb, data, extdata, lola_codec_read(chip, id, verb, data, extdata,
&chip->debug_res, &chip->debug_res,

View File

@ -24,6 +24,7 @@
/* #define RMH_DEBUG 1 */ /* #define RMH_DEBUG 1 */
#include <linux/bitops.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/delay.h> #include <linux/delay.h>
@ -429,11 +430,6 @@ int lx_dsp_read_async_events(struct lx6464es *chip, u32 *data)
return ret; return ret;
} }
#define CSES_TIMEOUT 100 /* microseconds */
#define CSES_CE 0x0001
#define CSES_BROADCAST 0x0002
#define CSES_UPDATE_LDSV 0x0004
#define PIPE_INFO_TO_CMD(capture, pipe) \ #define PIPE_INFO_TO_CMD(capture, pipe) \
((u32)((u32)(pipe) | ((capture) ? ID_IS_CAPTURE : 0L)) << ID_OFFSET) ((u32)((u32)(pipe) | ((capture) ? ID_IS_CAPTURE : 0L)) << ID_OFFSET)
@ -519,7 +515,6 @@ int lx_buffer_ask(struct lx6464es *chip, u32 pipe, int is_capture,
*r_needed += 1; *r_needed += 1;
} }
#if 0
dev_dbg(chip->card->dev, dev_dbg(chip->card->dev,
"CMD_08_ASK_BUFFERS: needed %d, freed %d\n", "CMD_08_ASK_BUFFERS: needed %d, freed %d\n",
*r_needed, *r_freed); *r_needed, *r_freed);
@ -530,7 +525,6 @@ int lx_buffer_ask(struct lx6464es *chip, u32 pipe, int is_capture,
chip->rmh.stat[i], chip->rmh.stat[i],
chip->rmh.stat[i] & MASK_DATA_SIZE); chip->rmh.stat[i] & MASK_DATA_SIZE);
} }
#endif
} }
spin_unlock_irqrestore(&chip->msg_lock, flags); spin_unlock_irqrestore(&chip->msg_lock, flags);
@ -971,9 +965,9 @@ int lx_level_peaks(struct lx6464es *chip, int is_capture, int channels,
/* interrupt handling */ /* interrupt handling */
#define PCX_IRQ_NONE 0 #define PCX_IRQ_NONE 0
#define IRQCS_ACTIVE_PCIDB 0x00002000L /* Bit nø 13 */ #define IRQCS_ACTIVE_PCIDB BIT(13)
#define IRQCS_ENABLE_PCIIRQ 0x00000100L /* Bit nø 08 */ #define IRQCS_ENABLE_PCIIRQ BIT(8)
#define IRQCS_ENABLE_PCIDB 0x00000200L /* Bit nø 09 */ #define IRQCS_ENABLE_PCIDB BIT(9)
static u32 lx_interrupt_test_ack(struct lx6464es *chip) static u32 lx_interrupt_test_ack(struct lx6464es *chip)
{ {
@ -1030,25 +1024,21 @@ static int lx_interrupt_handle_async_events(struct lx6464es *chip, u32 irqsrc,
int err; int err;
u32 stat[9]; /* answer from CMD_04_GET_EVENT */ u32 stat[9]; /* answer from CMD_04_GET_EVENT */
/* On peut optimiser pour ne pas lire les evenements vides /* We can optimize this to not read dumb events.
* les mots de ƒÂ©ponse sont dans l'ordre suivant : * Answer words are in the following order:
* Stat[0] mot de status ƒÂ©ƒÂ©ral * Stat[0] general status
* Stat[1] fin de buffer OUT pF * Stat[1] end of buffer OUT pF
* Stat[2] fin de buffer OUT pf * Stat[2] end of buffer OUT pf
* Stat[3] fin de buffer IN pF * Stat[3] end of buffer IN pF
* Stat[4] fin de buffer IN pf * Stat[4] end of buffer IN pf
* Stat[5] underrun poid fort * Stat[5] MSB underrun
* Stat[6] underrun poid faible * Stat[6] LSB underrun
* Stat[7] overrun poid fort * Stat[7] MSB overrun
* Stat[8] overrun poid faible * Stat[8] LSB overrun
* */ * */
u64 orun_mask; u64 orun_mask;
u64 urun_mask; u64 urun_mask;
#if 0
int has_underrun = (irqsrc & MASK_SYS_STATUS_URUN) ? 1 : 0;
int has_overrun = (irqsrc & MASK_SYS_STATUS_ORUN) ? 1 : 0;
#endif
int eb_pending_out = (irqsrc & MASK_SYS_STATUS_EOBO) ? 1 : 0; int eb_pending_out = (irqsrc & MASK_SYS_STATUS_EOBO) ? 1 : 0;
int eb_pending_in = (irqsrc & MASK_SYS_STATUS_EOBI) ? 1 : 0; int eb_pending_in = (irqsrc & MASK_SYS_STATUS_EOBI) ? 1 : 0;
@ -1199,9 +1189,8 @@ irqreturn_t lx_interrupt(int irq, void *dev_id)
if (irqsrc & MASK_SYS_STATUS_CMD_DONE) if (irqsrc & MASK_SYS_STATUS_CMD_DONE)
goto exit; goto exit;
#if 0
if (irqsrc & MASK_SYS_STATUS_EOBI) if (irqsrc & MASK_SYS_STATUS_EOBI)
dev_dgg(chip->card->dev, "interrupt: EOBI\n"); dev_dbg(chip->card->dev, "interrupt: EOBI\n");
if (irqsrc & MASK_SYS_STATUS_EOBO) if (irqsrc & MASK_SYS_STATUS_EOBO)
dev_dbg(chip->card->dev, "interrupt: EOBO\n"); dev_dbg(chip->card->dev, "interrupt: EOBO\n");
@ -1211,7 +1200,6 @@ irqreturn_t lx_interrupt(int irq, void *dev_id)
if (irqsrc & MASK_SYS_STATUS_ORUN) if (irqsrc & MASK_SYS_STATUS_ORUN)
dev_dbg(chip->card->dev, "interrupt: ORUN\n"); dev_dbg(chip->card->dev, "interrupt: ORUN\n");
#endif
if (async_pending) { if (async_pending) {
u64 notified_in_pipe_mask = 0; u64 notified_in_pipe_mask = 0;
@ -1238,7 +1226,6 @@ irqreturn_t lx_interrupt(int irq, void *dev_id)
} }
if (async_escmd) { if (async_escmd) {
#if 0
/* backdoor for ethersound commands /* backdoor for ethersound commands
* *
* for now, we do not need this * for now, we do not need this
@ -1246,7 +1233,6 @@ irqreturn_t lx_interrupt(int irq, void *dev_id)
* */ * */
dev_dbg(chip->card->dev, "interrupt requests escmd handling\n"); dev_dbg(chip->card->dev, "interrupt requests escmd handling\n");
#endif
} }
exit: exit:

View File

@ -35,7 +35,7 @@ config SND_AT91_SOC_SAM9G20_WM8731
config SND_ATMEL_SOC_WM8904 config SND_ATMEL_SOC_WM8904
tristate "Atmel ASoC driver for boards using WM8904 codec" tristate "Atmel ASoC driver for boards using WM8904 codec"
depends on ARCH_AT91 && ATMEL_SSC && SND_ATMEL_SOC depends on ARCH_AT91 && ATMEL_SSC && SND_ATMEL_SOC && I2C
select SND_ATMEL_SOC_SSC select SND_ATMEL_SOC_SSC
select SND_ATMEL_SOC_DMA select SND_ATMEL_SOC_DMA
select SND_SOC_WM8904 select SND_SOC_WM8904

View File

@ -76,12 +76,6 @@ struct atmel_runtime_data {
size_t period_size; size_t period_size;
dma_addr_t period_ptr; /* physical address of next period */ dma_addr_t period_ptr; /* physical address of next period */
/* PDC register save */
u32 pdc_xpr_save;
u32 pdc_xcr_save;
u32 pdc_xnpr_save;
u32 pdc_xncr_save;
}; };
/*--------------------------------------------------------------------------*\ /*--------------------------------------------------------------------------*\
@ -320,67 +314,10 @@ static struct snd_pcm_ops atmel_pcm_ops = {
.mmap = atmel_pcm_mmap, .mmap = atmel_pcm_mmap,
}; };
/*--------------------------------------------------------------------------*\
* ASoC platform driver
\*--------------------------------------------------------------------------*/
#ifdef CONFIG_PM
static int atmel_pcm_suspend(struct snd_soc_dai *dai)
{
struct snd_pcm_runtime *runtime = dai->runtime;
struct atmel_runtime_data *prtd;
struct atmel_pcm_dma_params *params;
if (!runtime)
return 0;
prtd = runtime->private_data;
params = prtd->params;
/* disable the PDC and save the PDC registers */
ssc_writel(params->ssc->regs, PDC_PTCR, params->mask->pdc_disable);
prtd->pdc_xpr_save = ssc_readx(params->ssc->regs, params->pdc->xpr);
prtd->pdc_xcr_save = ssc_readx(params->ssc->regs, params->pdc->xcr);
prtd->pdc_xnpr_save = ssc_readx(params->ssc->regs, params->pdc->xnpr);
prtd->pdc_xncr_save = ssc_readx(params->ssc->regs, params->pdc->xncr);
return 0;
}
static int atmel_pcm_resume(struct snd_soc_dai *dai)
{
struct snd_pcm_runtime *runtime = dai->runtime;
struct atmel_runtime_data *prtd;
struct atmel_pcm_dma_params *params;
if (!runtime)
return 0;
prtd = runtime->private_data;
params = prtd->params;
/* restore the PDC registers and enable the PDC */
ssc_writex(params->ssc->regs, params->pdc->xpr, prtd->pdc_xpr_save);
ssc_writex(params->ssc->regs, params->pdc->xcr, prtd->pdc_xcr_save);
ssc_writex(params->ssc->regs, params->pdc->xnpr, prtd->pdc_xnpr_save);
ssc_writex(params->ssc->regs, params->pdc->xncr, prtd->pdc_xncr_save);
ssc_writel(params->ssc->regs, PDC_PTCR, params->mask->pdc_enable);
return 0;
}
#else
#define atmel_pcm_suspend NULL
#define atmel_pcm_resume NULL
#endif
static struct snd_soc_platform_driver atmel_soc_platform = { static struct snd_soc_platform_driver atmel_soc_platform = {
.ops = &atmel_pcm_ops, .ops = &atmel_pcm_ops,
.pcm_new = atmel_pcm_new, .pcm_new = atmel_pcm_new,
.pcm_free = atmel_pcm_free, .pcm_free = atmel_pcm_free,
.suspend = atmel_pcm_suspend,
.resume = atmel_pcm_resume,
}; };
int atmel_pcm_pdc_platform_register(struct device *dev) int atmel_pcm_pdc_platform_register(struct device *dev)

View File

@ -80,17 +80,6 @@ static const struct snd_soc_dapm_route afeb9260_audio_map[] = {
{"MICIN", NULL, "Mic Jack"}, {"MICIN", NULL, "Mic Jack"},
}; };
static int afeb9260_tlv320aic23_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_dapm_context *dapm = &codec->dapm;
snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
snd_soc_dapm_enable_pin(dapm, "Line In");
snd_soc_dapm_enable_pin(dapm, "Mic Jack");
return 0;
}
/* Digital audio interface glue - connects codec <--> CPU */ /* Digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link afeb9260_dai = { static struct snd_soc_dai_link afeb9260_dai = {
@ -100,7 +89,6 @@ static struct snd_soc_dai_link afeb9260_dai = {
.codec_dai_name = "tlv320aic23-hifi", .codec_dai_name = "tlv320aic23-hifi",
.platform_name = "atmel_pcm-audio", .platform_name = "atmel_pcm-audio",
.codec_name = "tlv320aic23-codec.0-001a", .codec_name = "tlv320aic23-codec.0-001a",
.init = afeb9260_tlv320aic23_init,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF | .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF |
SND_SOC_DAIFMT_CBM_CFM, SND_SOC_DAIFMT_CBM_CFM,
.ops = &afeb9260_ops, .ops = &afeb9260_ops,

View File

@ -43,6 +43,32 @@ config SND_SOC_BFIN_EVAL_ADAU1373
Note: This driver assumes that first ADAU1373 DAI is connected to the Note: This driver assumes that first ADAU1373 DAI is connected to the
first SPORT port on the BF5XX board. first SPORT port on the BF5XX board.
config SND_SOC_BFIN_EVAL_ADAU1X61
tristate "Support for the EVAL-ADAU1X61 board on Blackfin eval boards"
depends on SND_BF5XX_I2S && I2C
select SND_BF5XX_SOC_I2S
select SND_SOC_ADAU1761_I2C
help
Say Y if you want to add support for the Analog Devices EVAL-ADAU1X61
board connected to one of the Blackfin evaluation boards like the
BF5XX-STAMP or BF5XX-EZKIT.
Note: This driver assumes that the ADAU1X61 is connected to the
first SPORT port on the BF5XX board.
config SND_SOC_BFIN_EVAL_ADAU1X81
tristate "Support for the EVAL-ADAU1X81 boards on Blackfin eval boards"
depends on SND_BF5XX_I2S && I2C
select SND_BF5XX_SOC_I2S
select SND_SOC_ADAU1781_I2C
help
Say Y if you want to add support for the Analog Devices EVAL-ADAU1X81
board connected to one of the Blackfin evaluation boards like the
BF5XX-STAMP or BF5XX-EZKIT.
Note: This driver assumes that the ADAU1X81 is connected to the
first SPORT port on the BF5XX board.
config SND_SOC_BFIN_EVAL_ADAV80X config SND_SOC_BFIN_EVAL_ADAV80X
tristate "Support for the EVAL-ADAV80X boards on Blackfin eval boards" tristate "Support for the EVAL-ADAV80X boards on Blackfin eval boards"
depends on SND_BF5XX_I2S && SND_SOC_I2C_AND_SPI depends on SND_BF5XX_I2S && SND_SOC_I2C_AND_SPI

View File

@ -22,6 +22,8 @@ snd-ssm2602-objs := bf5xx-ssm2602.o
snd-ad73311-objs := bf5xx-ad73311.o snd-ad73311-objs := bf5xx-ad73311.o
snd-ad193x-objs := bf5xx-ad193x.o snd-ad193x-objs := bf5xx-ad193x.o
snd-soc-bfin-eval-adau1373-objs := bfin-eval-adau1373.o snd-soc-bfin-eval-adau1373-objs := bfin-eval-adau1373.o
snd-soc-bfin-eval-adau1x61-objs := bfin-eval-adau1x61.o
snd-soc-bfin-eval-adau1x81-objs := bfin-eval-adau1x81.o
snd-soc-bfin-eval-adau1701-objs := bfin-eval-adau1701.o snd-soc-bfin-eval-adau1701-objs := bfin-eval-adau1701.o
snd-soc-bfin-eval-adav80x-objs := bfin-eval-adav80x.o snd-soc-bfin-eval-adav80x-objs := bfin-eval-adav80x.o
@ -31,5 +33,7 @@ obj-$(CONFIG_SND_BF5XX_SOC_SSM2602) += snd-ssm2602.o
obj-$(CONFIG_SND_BF5XX_SOC_AD73311) += snd-ad73311.o obj-$(CONFIG_SND_BF5XX_SOC_AD73311) += snd-ad73311.o
obj-$(CONFIG_SND_BF5XX_SOC_AD193X) += snd-ad193x.o obj-$(CONFIG_SND_BF5XX_SOC_AD193X) += snd-ad193x.o
obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1373) += snd-soc-bfin-eval-adau1373.o obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1373) += snd-soc-bfin-eval-adau1373.o
obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1X61) += snd-soc-bfin-eval-adau1x61.o
obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1X81) += snd-soc-bfin-eval-adau1x81.o
obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1701) += snd-soc-bfin-eval-adau1701.o obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1701) += snd-soc-bfin-eval-adau1701.o
obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAV80X) += snd-soc-bfin-eval-adav80x.o obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAV80X) += snd-soc-bfin-eval-adav80x.o

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