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:
Коммит
b77279bc2e
|
@ -10,6 +10,9 @@ Optional properties:
|
|||
- fsl,mc13xxx-uses-touch : Indicate the touchscreen controller is being used
|
||||
|
||||
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
|
||||
"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
|
||||
|
|
|
@ -8,6 +8,8 @@ Required properties:
|
|||
|
||||
- reg : The chip select number on the SPI bus
|
||||
|
||||
- vdd-supply : A regulator node, providing 2.7V - 3.6V
|
||||
|
||||
Optional properties:
|
||||
|
||||
- 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";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <5000000>;
|
||||
vdd-supply = <&vdd_3v3_reg>;
|
||||
};
|
||||
|
|
|
@ -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>;
|
||||
};
|
|
@ -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 = <®_audio>;
|
||||
};
|
|
@ -7,10 +7,11 @@ codec/DSP interfaces.
|
|||
|
||||
|
||||
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.
|
||||
- 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
|
||||
Documentation/devicetree/bindings/dma/dma.txt.
|
||||
- dma-names : Two dmas have to be defined, "tx" and "rx".
|
||||
|
@ -30,8 +31,10 @@ sai2: sai@40031000 {
|
|||
reg = <0x40031000 0x1000>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_sai2_1>;
|
||||
clocks = <&clks VF610_CLK_SAI2>;
|
||||
clock-names = "sai";
|
||||
clocks = <&clks VF610_CLK_PLATFORM_BUS>,
|
||||
<&clks VF610_CLK_SAI2>,
|
||||
<&clks 0>, <&clks 0>;
|
||||
clock-names = "bus", "mclk1", "mclk2", "mclk3";
|
||||
dma-names = "tx", "rx";
|
||||
dmas = <&edma0 0 VF610_EDMA_MUXID0_SAI2_TX>,
|
||||
<&edma0 0 VF610_EDMA_MUXID0_SAI2_RX>;
|
||||
|
|
|
@ -10,6 +10,12 @@ Required properties:
|
|||
|
||||
- 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):
|
||||
|
||||
* MIC1
|
||||
|
|
|
@ -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>;
|
||||
};
|
|
@ -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>;
|
||||
};
|
|
@ -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";
|
||||
};
|
|
@ -20,6 +20,7 @@ Required properties:
|
|||
SSI subnode properties:
|
||||
- interrupts : Should contain SSI interrupt for PIO transfer
|
||||
- shared-pin : if shared clock pin
|
||||
- pio-transfer : use PIO transfer mode
|
||||
|
||||
SRC subnode properties:
|
||||
no properties at this point
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
RT5640 audio CODEC
|
||||
RT5640/RT5639 audio CODEC
|
||||
|
||||
This device supports I2C only.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "realtek,rt5640".
|
||||
- compatible : One of "realtek,rt5640" or "realtek,rt5639".
|
||||
|
||||
- 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.
|
||||
|
||||
Pins on the device (for linking into audio routes):
|
||||
Pins on the device (for linking into audio routes) for RT5639/RT5640:
|
||||
|
||||
* DMIC1
|
||||
* DMIC2
|
||||
|
@ -31,13 +31,16 @@ Pins on the device (for linking into audio routes):
|
|||
* HPOR
|
||||
* LOUTL
|
||||
* LOUTR
|
||||
* MONOP
|
||||
* MONON
|
||||
* SPOLP
|
||||
* SPOLN
|
||||
* SPORP
|
||||
* SPORN
|
||||
|
||||
Additional pins on the device for RT5640:
|
||||
|
||||
* MONOP
|
||||
* MONON
|
||||
|
||||
Example:
|
||||
|
||||
rt5640 {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
Simple-Card:
|
||||
|
||||
Simple-Card specifies audio DAI connection of SoC <-> codec.
|
||||
Simple-Card specifies audio DAI connections of SoC <-> codec.
|
||||
|
||||
Required properties:
|
||||
|
||||
|
@ -10,26 +10,54 @@ Optional properties:
|
|||
|
||||
- simple-audio-card,name : User specified audio sound card name, one string
|
||||
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,routing : A list of the connections between audio components.
|
||||
Each entry is a pair of strings, the first being the
|
||||
connection's sink, the second being the connection's
|
||||
source.
|
||||
- dai-tdm-slot-num : Please refer to tdm-slot.txt.
|
||||
- dai-tdm-slot-width : Please refer to tdm-slot.txt.
|
||||
- simple-audio-card,mclk-fs : Multiplication factor between stream rate and codec
|
||||
mclk.
|
||||
|
||||
Required subnodes:
|
||||
Optional subnodes:
|
||||
|
||||
- simple-audio-card,dai-link : container for the CPU and CODEC sub-nodes
|
||||
This container may be omitted when the
|
||||
card has only one DAI link.
|
||||
See the examples.
|
||||
- simple-audio-card,dai-link : Container for dai-link level
|
||||
properties and the CPU and CODEC
|
||||
sub-nodes. This container may be
|
||||
omitted when the card has only one
|
||||
DAI link. See the examples and the
|
||||
section bellow.
|
||||
|
||||
- simple-audio-card,cpu : CPU sub-node
|
||||
- simple-audio-card,codec : CODEC sub-node
|
||||
Dai-link subnode properties and subnodes:
|
||||
|
||||
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:
|
||||
|
||||
|
@ -37,29 +65,21 @@ Required CPU/CODEC subnodes properties:
|
|||
|
||||
Optional CPU/CODEC subnodes properties:
|
||||
|
||||
- format : CPU/CODEC specific audio format if needed.
|
||||
see simple-audio-card,format
|
||||
- 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
|
||||
- dai-tdm-slot-num : Please refer to tdm-slot.txt.
|
||||
- dai-tdm-slot-width : Please refer to tdm-slot.txt.
|
||||
- clocks / system-clock-frequency : specify subnode's clock if needed.
|
||||
it can be specified via "clocks" if system has
|
||||
clock node (= common clock), or "system-clock-frequency"
|
||||
(if system doens't support common clock)
|
||||
|
||||
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:
|
||||
|
||||
sound {
|
||||
compatible = "simple-audio-card";
|
||||
simple-audio-card,name = "VF610-Tower-Sound-Card";
|
||||
simple-audio-card,format = "left_j";
|
||||
simple-audio-card,bitclock-master = <&dailink0_master>;
|
||||
simple-audio-card,frame-master = <&dailink0_master>;
|
||||
simple-audio-card,widgets =
|
||||
"Microphone", "Microphone Jack",
|
||||
"Headphone", "Headphone Jack",
|
||||
|
@ -69,17 +89,12 @@ sound {
|
|||
"Headphone Jack", "HP_OUT",
|
||||
"External Speaker", "LINE_OUT";
|
||||
|
||||
dai-tdm-slot-num = <2>;
|
||||
dai-tdm-slot-width = <8>;
|
||||
|
||||
simple-audio-card,cpu {
|
||||
sound-dai = <&sh_fsi2 0>;
|
||||
};
|
||||
|
||||
simple-audio-card,codec {
|
||||
dailink0_master: simple-audio-card,codec {
|
||||
sound-dai = <&ak4648>;
|
||||
bitclock-master;
|
||||
frame-master;
|
||||
clocks = <&osc>;
|
||||
};
|
||||
};
|
||||
|
@ -105,31 +120,31 @@ Example 2 - many DAI links:
|
|||
sound {
|
||||
compatible = "simple-audio-card";
|
||||
simple-audio-card,name = "Cubox Audio";
|
||||
simple-audio-card,format = "i2s";
|
||||
|
||||
simple-audio-card,dai-link@0 { /* I2S - HDMI */
|
||||
simple-audio-card,cpu {
|
||||
format = "i2s";
|
||||
cpu {
|
||||
sound-dai = <&audio1 0>;
|
||||
};
|
||||
simple-audio-card,codec {
|
||||
codec {
|
||||
sound-dai = <&tda998x 0>;
|
||||
};
|
||||
};
|
||||
|
||||
simple-audio-card,dai-link@1 { /* S/PDIF - HDMI */
|
||||
simple-audio-card,cpu {
|
||||
cpu {
|
||||
sound-dai = <&audio1 1>;
|
||||
};
|
||||
simple-audio-card,codec {
|
||||
codec {
|
||||
sound-dai = <&tda998x 1>;
|
||||
};
|
||||
};
|
||||
|
||||
simple-audio-card,dai-link@2 { /* S/PDIF - S/PDIF */
|
||||
simple-audio-card,cpu {
|
||||
cpu {
|
||||
sound-dai = <&audio1 1>;
|
||||
};
|
||||
simple-audio-card,codec {
|
||||
codec {
|
||||
sound-dai = <&spdif_codec>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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>;
|
||||
};
|
|
@ -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.
|
||||
};
|
|
@ -8270,6 +8270,7 @@ L: alsa-devel@alsa-project.org (moderated for non-subscribers)
|
|||
W: http://www.alsa-project.org/
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
|
||||
T: git git://git.alsa-project.org/alsa-kernel.git
|
||||
Q: http://patchwork.kernel.org/project/alsa-devel/list/
|
||||
S: Maintained
|
||||
F: Documentation/sound/
|
||||
F: include/sound/
|
||||
|
|
|
@ -22,8 +22,6 @@ enum jz4740_dma_request_type {
|
|||
JZ4740_DMA_TYPE_UART_RECEIVE = 21,
|
||||
JZ4740_DMA_TYPE_SPI_TRANSMIT = 22,
|
||||
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_RECEIVE = 27,
|
||||
JZ4740_DMA_TYPE_TCU = 28,
|
||||
|
|
|
@ -425,6 +425,15 @@ static struct platform_device qi_lb60_audio_device = {
|
|||
.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 = {
|
||||
&jz4740_udc_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_mmc_device.dev.platform_data = &qi_lb60_mmc_pdata;
|
||||
|
||||
gpiod_add_lookup_table(&qi_lb60_audio_gpio_table);
|
||||
|
||||
jz4740_serial_device_register();
|
||||
|
||||
spi_register_board_info(qi_lb60_spi_board_info,
|
||||
|
|
|
@ -118,7 +118,6 @@ int fw_card_add(struct fw_card *card,
|
|||
u32 max_receive, u32 link_speed, u64 guid);
|
||||
void fw_core_remove_card(struct fw_card *card);
|
||||
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);
|
||||
|
||||
/* -cdev */
|
||||
|
|
|
@ -673,9 +673,13 @@ int mc13xxx_common_init(struct device *dev)
|
|||
if (mc13xxx->flags & MC13XXX_USE_ADC)
|
||||
mc13xxx_add_subdevice(mc13xxx, "%s-adc");
|
||||
|
||||
if (mc13xxx->flags & MC13XXX_USE_CODEC)
|
||||
mc13xxx_add_subdevice_pdata(mc13xxx, "%s-codec",
|
||||
pdata->codec, sizeof(*pdata->codec));
|
||||
if (mc13xxx->flags & MC13XXX_USE_CODEC) {
|
||||
if (pdata)
|
||||
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)
|
||||
mc13xxx_add_subdevice(mc13xxx, "%s-rtc");
|
||||
|
|
|
@ -367,6 +367,9 @@ static inline int fw_stream_packet_destination_id(int tag, int channel, int 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 list_head link;
|
||||
size_t length;
|
||||
|
|
|
@ -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
|
|
@ -23,7 +23,6 @@
|
|||
* @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
|
||||
* 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.
|
||||
* only one feature is required on the board. The slave for playback or capture
|
||||
|
@ -33,7 +32,6 @@
|
|||
struct ac97c_platform_data {
|
||||
struct dw_dma_slave rx_dws;
|
||||
struct dw_dma_slave tx_dws;
|
||||
unsigned int flags;
|
||||
int reset_pin;
|
||||
};
|
||||
|
||||
|
|
|
@ -282,13 +282,6 @@ int snd_card_new(struct device *parent, int idx, const char *xid,
|
|||
struct module *module, int extra_size,
|
||||
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_free(struct snd_card *card);
|
||||
int snd_card_free_when_closed(struct snd_card *card);
|
||||
|
|
|
@ -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 */
|
|
@ -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__ */
|
|
@ -34,47 +34,39 @@
|
|||
* B : SSI direction
|
||||
*/
|
||||
#define RSND_SSI_CLK_PIN_SHARE (1 << 31)
|
||||
#define RSND_SSI_PLAY (1 << 24)
|
||||
|
||||
#define RSND_SSI(_dma_id, _pio_irq, _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 \
|
||||
{ .dai_id = -1, .dma_id = -1, .pio_irq = -1, .flags = 0 }
|
||||
{ .dma_id = -1, .pio_irq = -1, .flags = 0 }
|
||||
|
||||
struct rsnd_ssi_platform_info {
|
||||
int dai_id; /* will be removed */
|
||||
int dma_id;
|
||||
int pio_irq;
|
||||
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
|
||||
*/
|
||||
#define RSND_SCU_USE_HPBIF (1 << 31) /* it needs RSND_SSI_DEPENDENT */
|
||||
|
||||
#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 {
|
||||
struct rsnd_dvc_platform_info {
|
||||
u32 flags;
|
||||
u32 convert_rate; /* sampling rate convert */
|
||||
int dma_id; /* for Gen2 SCU */
|
||||
};
|
||||
|
||||
struct rsnd_dai_path_info {
|
||||
struct rsnd_ssi_platform_info *ssi;
|
||||
struct rsnd_src_platform_info *src;
|
||||
struct rsnd_dvc_platform_info *dvc;
|
||||
};
|
||||
|
||||
struct rsnd_dai_platform_info {
|
||||
|
@ -99,6 +91,8 @@ struct rcar_snd_info {
|
|||
int ssi_info_nr;
|
||||
struct rsnd_src_platform_info *src_info;
|
||||
int src_info_nr;
|
||||
struct rsnd_dvc_platform_info *dvc_info;
|
||||
int dvc_info_nr;
|
||||
struct rsnd_dai_platform_info *dai_info;
|
||||
int dai_info_nr;
|
||||
int (*start)(int id);
|
||||
|
|
|
@ -16,6 +16,10 @@ struct rt5640_platform_data {
|
|||
bool in1_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 */
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -252,7 +252,6 @@ struct snd_soc_dai {
|
|||
unsigned int symmetric_rates:1;
|
||||
unsigned int symmetric_channels:1;
|
||||
unsigned int symmetric_samplebits:1;
|
||||
struct snd_pcm_runtime *runtime;
|
||||
unsigned int active;
|
||||
unsigned char probed:1;
|
||||
|
||||
|
@ -277,7 +276,6 @@ struct snd_soc_dai {
|
|||
struct snd_soc_card *card;
|
||||
|
||||
struct list_head list;
|
||||
struct list_head card_list;
|
||||
};
|
||||
|
||||
static inline void *snd_soc_dai_get_dma_data(const struct snd_soc_dai *dai,
|
||||
|
|
|
@ -107,10 +107,6 @@ struct device;
|
|||
{ .id = snd_soc_dapm_mux, .name = wname, \
|
||||
SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
|
||||
.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) */
|
||||
#define SOC_PGA_ARRAY(wname, wreg, wshift, winvert,\
|
||||
|
@ -166,10 +162,6 @@ struct device;
|
|||
SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
|
||||
.kcontrol_news = wcontrols, .num_kcontrols = 1, \
|
||||
.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 */
|
||||
#define SND_SOC_DAPM_PGA_S(wname, wsubseq, wreg, wshift, winvert, \
|
||||
|
@ -256,9 +248,8 @@ struct device;
|
|||
/* generic widgets */
|
||||
#define SND_SOC_DAPM_REG(wid, wname, wreg, wshift, wmask, won_val, woff_val) \
|
||||
{ .id = wid, .name = wname, .kcontrol_news = NULL, .num_kcontrols = 0, \
|
||||
.reg = -((wreg) + 1), .shift = wshift, .mask = wmask, \
|
||||
.on_val = won_val, .off_val = woff_val, .event = dapm_reg_event, \
|
||||
.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD}
|
||||
.reg = wreg, .shift = wshift, .mask = wmask, \
|
||||
.on_val = won_val, .off_val = woff_val, }
|
||||
#define SND_SOC_DAPM_SUPPLY(wname, wreg, wshift, winvert, wevent, wflags) \
|
||||
{ .id = snd_soc_dapm_supply, .name = wname, \
|
||||
SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
|
||||
|
@ -305,16 +296,12 @@ struct device;
|
|||
.get = snd_soc_dapm_get_enum_double, \
|
||||
.put = snd_soc_dapm_put_enum_double, \
|
||||
.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) \
|
||||
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
|
||||
.info = snd_soc_info_enum_double, \
|
||||
.get = xget, \
|
||||
.put = xput, \
|
||||
.private_value = (unsigned long)&xenum }
|
||||
#define SOC_DAPM_VALUE_ENUM(xname, xenum) \
|
||||
SOC_DAPM_ENUM(xname, xenum)
|
||||
#define SOC_DAPM_PIN_SWITCH(xname) \
|
||||
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname " Switch", \
|
||||
.info = snd_soc_dapm_info_pin_switch, \
|
||||
|
@ -362,8 +349,6 @@ struct regulator;
|
|||
struct snd_soc_dapm_widget_list;
|
||||
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,
|
||||
struct snd_kcontrol *kcontrol, int event);
|
||||
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);
|
||||
|
||||
struct device *dev; /* from parent - for debug */
|
||||
struct snd_soc_component *component; /* parent component */
|
||||
struct snd_soc_codec *codec; /* parent codec */
|
||||
struct snd_soc_platform *platform; /* parent platform */
|
||||
struct snd_soc_card *card; /* parent card */
|
||||
|
|
|
@ -196,8 +196,6 @@
|
|||
.info = snd_soc_info_enum_double, \
|
||||
.get = snd_soc_get_enum_double, .put = snd_soc_put_enum_double, \
|
||||
.private_value = (unsigned long)&xenum }
|
||||
#define SOC_VALUE_ENUM(xname, xenum) \
|
||||
SOC_ENUM(xname, xenum)
|
||||
#define SOC_SINGLE_EXT(xname, xreg, xshift, xmax, xinvert,\
|
||||
xhandler_get, xhandler_put) \
|
||||
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
|
||||
|
@ -266,6 +264,13 @@
|
|||
{.base = xbase, .num_regs = xregs, \
|
||||
.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, \
|
||||
xmin, xmax, xinvert) \
|
||||
{ .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_register_platform(struct device *dev,
|
||||
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);
|
||||
int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform,
|
||||
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,
|
||||
struct snd_soc_dai_driver *dai_drv, int num_dai);
|
||||
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_init(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
|
||||
int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
|
||||
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,
|
||||
struct snd_soc_jack_gpio *gpios);
|
||||
#else
|
||||
|
@ -462,6 +464,14 @@ static inline int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
|
|||
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,
|
||||
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
|
||||
|
||||
/* 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);
|
||||
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);
|
||||
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);
|
||||
|
||||
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);
|
||||
int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
|
||||
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,
|
||||
struct snd_ctl_elem_info *uinfo);
|
||||
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
|
||||
*
|
||||
* @gpio: gpio number
|
||||
* @name: gpio name
|
||||
* @gpio: legacy gpio number
|
||||
* @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
|
||||
* @invert: report presence in low state
|
||||
* @debouce_time: debouce time in ms
|
||||
|
@ -598,6 +614,8 @@ struct snd_soc_jack_zone {
|
|||
*/
|
||||
struct snd_soc_jack_gpio {
|
||||
unsigned int gpio;
|
||||
unsigned int idx;
|
||||
struct device *gpiod_dev;
|
||||
const char *name;
|
||||
int report;
|
||||
int invert;
|
||||
|
@ -606,6 +624,7 @@ struct snd_soc_jack_gpio {
|
|||
|
||||
struct snd_soc_jack *jack;
|
||||
struct delayed_work work;
|
||||
struct gpio_desc *desc;
|
||||
|
||||
void *data;
|
||||
int (*jack_status_check)(void *data);
|
||||
|
@ -668,6 +687,7 @@ struct snd_soc_component {
|
|||
unsigned int active;
|
||||
|
||||
unsigned int ignore_pmdown_time:1; /* pmdown_time is ignored at stop */
|
||||
unsigned int registered_as_component:1;
|
||||
|
||||
struct list_head list;
|
||||
|
||||
|
@ -677,6 +697,14 @@ struct snd_soc_component {
|
|||
const struct snd_soc_component_driver *driver;
|
||||
|
||||
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 */
|
||||
|
@ -691,10 +719,6 @@ struct snd_soc_codec {
|
|||
struct snd_soc_card *card;
|
||||
struct list_head 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 */
|
||||
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_created:1; /* Codec has been created by SoC */
|
||||
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_sync; /* Cache needs to be synced to hardware */
|
||||
|
||||
/* codec IO */
|
||||
void *control_data; /* codec control (i2c/3wire) data */
|
||||
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;
|
||||
struct mutex cache_rw_mutex;
|
||||
int val_bytes;
|
||||
|
||||
/* component */
|
||||
struct snd_soc_component component;
|
||||
|
@ -754,13 +774,9 @@ struct snd_soc_codec_driver {
|
|||
unsigned int freq_in, unsigned int freq_out);
|
||||
|
||||
/* codec IO */
|
||||
struct regmap *(*get_regmap)(struct device *);
|
||||
unsigned int (*read)(struct snd_soc_codec *, 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;
|
||||
short reg_cache_step;
|
||||
short reg_word_size;
|
||||
|
@ -791,6 +807,7 @@ struct snd_soc_platform_driver {
|
|||
int (*remove)(struct snd_soc_platform *);
|
||||
int (*suspend)(struct snd_soc_dai *dai);
|
||||
int (*resume)(struct snd_soc_dai *dai);
|
||||
struct snd_soc_component_driver component_driver;
|
||||
|
||||
/* pcm creation and destruction */
|
||||
int (*pcm_new)(struct snd_soc_pcm_runtime *);
|
||||
|
@ -835,7 +852,6 @@ struct snd_soc_platform {
|
|||
int id;
|
||||
struct device *dev;
|
||||
const struct snd_soc_platform_driver *driver;
|
||||
struct mutex mutex;
|
||||
|
||||
unsigned int suspended:1; /* platform is suspended */
|
||||
unsigned int probed:1;
|
||||
|
@ -844,6 +860,8 @@ struct snd_soc_platform {
|
|||
struct list_head list;
|
||||
struct list_head card_list;
|
||||
|
||||
struct snd_soc_component component;
|
||||
|
||||
struct snd_soc_dapm_context dapm;
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
@ -931,7 +949,12 @@ struct snd_soc_dai_link {
|
|||
};
|
||||
|
||||
struct snd_soc_codec_conf {
|
||||
/*
|
||||
* specify device either by device name, or by
|
||||
* DT/OF node, but not both.
|
||||
*/
|
||||
const char *dev_name;
|
||||
const struct device_node *of_node;
|
||||
|
||||
/*
|
||||
* 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 {
|
||||
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 */
|
||||
int (*init)(struct snd_soc_dapm_context *dapm);
|
||||
|
@ -957,7 +986,6 @@ struct snd_soc_card {
|
|||
struct snd_card *snd_card;
|
||||
struct module *owner;
|
||||
|
||||
struct list_head list;
|
||||
struct mutex mutex;
|
||||
struct mutex dapm_mutex;
|
||||
|
||||
|
@ -1020,7 +1048,6 @@ struct snd_soc_card {
|
|||
/* lists of probed devices belonging to this card */
|
||||
struct list_head codec_dev_list;
|
||||
struct list_head platform_dev_list;
|
||||
struct list_head dai_dev_list;
|
||||
|
||||
struct list_head widgets;
|
||||
struct list_head paths;
|
||||
|
@ -1090,6 +1117,10 @@ struct soc_bytes {
|
|||
u32 mask;
|
||||
};
|
||||
|
||||
struct soc_bytes_ext {
|
||||
int max;
|
||||
};
|
||||
|
||||
/* multi register control */
|
||||
struct soc_mreg_control {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 */
|
||||
unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg);
|
||||
unsigned int snd_soc_write(struct snd_soc_codec *codec,
|
||||
unsigned int reg, unsigned int val);
|
||||
int snd_soc_write(struct snd_soc_codec *codec, unsigned int reg,
|
||||
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 */
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
INIT_LIST_HEAD(&card->dai_dev_list);
|
||||
INIT_LIST_HEAD(&card->codec_dev_list);
|
||||
INIT_LIST_HEAD(&card->platform_dev_list);
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
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,
|
||||
const char *propname);
|
||||
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,
|
||||
const char **dai_name);
|
||||
|
||||
|
|
|
@ -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 */
|
|
@ -11,102 +11,10 @@
|
|||
|
||||
struct snd_soc_jack;
|
||||
struct snd_soc_codec;
|
||||
struct snd_soc_platform;
|
||||
struct snd_soc_card;
|
||||
struct snd_soc_dapm_widget;
|
||||
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,
|
||||
|
||||
TP_PROTO(struct snd_soc_card *card, int val),
|
||||
|
|
|
@ -94,9 +94,11 @@ enum {
|
|||
SNDRV_HWDEP_IFACE_HDA, /* HD-audio */
|
||||
SNDRV_HWDEP_IFACE_USB_STREAM, /* direct access to usb stream */
|
||||
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: */
|
||||
SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_DICE
|
||||
SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_BEBOB
|
||||
};
|
||||
|
||||
struct snd_hwdep_info {
|
||||
|
|
|
@ -2,11 +2,13 @@
|
|||
#define _UAPI_SOUND_FIREWIRE_H_INCLUDED
|
||||
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/* events can be read() from the hwdep device */
|
||||
|
||||
#define SNDRV_FIREWIRE_EVENT_LOCK_STATUS 0x000010cc
|
||||
#define SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION 0xd1ce004e
|
||||
#define SNDRV_FIREWIRE_EVENT_EFW_RESPONSE 0x4e617475
|
||||
|
||||
struct snd_firewire_event_common {
|
||||
unsigned int type; /* SNDRV_FIREWIRE_EVENT_xxx */
|
||||
|
@ -22,10 +24,27 @@ struct snd_firewire_event_dice_notification {
|
|||
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 {
|
||||
struct snd_firewire_event_common common;
|
||||
struct snd_firewire_event_lock_status lock_status;
|
||||
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_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 {
|
||||
unsigned int type; /* SNDRV_FIREWIRE_TYPE_xxx */
|
||||
|
|
|
@ -241,7 +241,7 @@ static struct snd_kcontrol_new inputgain_control = {
|
|||
static int onyx_snd_capture_source_info(struct snd_kcontrol *kcontrol,
|
||||
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->count = 1;
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmaengine.h>
|
||||
|
||||
#include <mach/dma.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/pxa2xx-lib.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
|
|
|
@ -9,12 +9,11 @@
|
|||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <mach/dma.h>
|
||||
|
||||
struct pxa2xx_runtime_data {
|
||||
int dma_ch;
|
||||
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;
|
||||
};
|
||||
|
||||
|
|
|
@ -1198,6 +1198,7 @@ static int atmel_ac97c_remove(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
static struct platform_driver atmel_ac97c_driver = {
|
||||
.probe = atmel_ac97c_probe,
|
||||
.remove = atmel_ac97c_remove,
|
||||
.driver = {
|
||||
.name = "atmel_ac97c",
|
||||
|
@ -1205,19 +1206,7 @@ static struct platform_driver atmel_ac97c_driver = {
|
|||
.pm = ATMEL_AC97C_PM_OPS,
|
||||
},
|
||||
};
|
||||
|
||||
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_platform_driver(atmel_ac97c_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Driver for Atmel AC97 controller");
|
||||
|
|
|
@ -345,7 +345,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
|
|||
snd_pcm_debug_name(substream, name, sizeof(name));
|
||||
xrun_log_show(substream);
|
||||
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,
|
||||
runtime->period_size);
|
||||
}
|
||||
|
|
|
@ -362,13 +362,13 @@ snd_seq_midisynth_register_port(struct snd_seq_device *dev)
|
|||
if (! port->name[0]) {
|
||||
if (info->name[0]) {
|
||||
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
|
||||
snprintf(port->name, sizeof(port->name), "%s", info->name);
|
||||
} else {
|
||||
/* last resort */
|
||||
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
|
||||
sprintf(port->name, "MIDI %d-%d", card->number, device);
|
||||
}
|
||||
|
|
|
@ -9,12 +9,12 @@ if SND_FIREWIRE && FIREWIRE
|
|||
|
||||
config SND_FIREWIRE_LIB
|
||||
tristate
|
||||
depends on SND_PCM
|
||||
select SND_PCM
|
||||
select SND_RAWMIDI
|
||||
|
||||
config SND_DICE
|
||||
tristate "DICE-based DACs (EXPERIMENTAL)"
|
||||
select SND_HWDEP
|
||||
select SND_PCM
|
||||
select SND_FIREWIRE_LIB
|
||||
help
|
||||
Say Y here to include support for many DACs based on the DICE
|
||||
|
@ -28,7 +28,6 @@ config SND_DICE
|
|||
|
||||
config SND_FIREWIRE_SPEAKERS
|
||||
tristate "FireWire speakers"
|
||||
select SND_PCM
|
||||
select SND_FIREWIRE_LIB
|
||||
help
|
||||
Say Y here to include support for the Griffin FireWave Surround
|
||||
|
@ -39,7 +38,6 @@ config SND_FIREWIRE_SPEAKERS
|
|||
|
||||
config SND_ISIGHT
|
||||
tristate "Apple iSight microphone"
|
||||
select SND_PCM
|
||||
select SND_FIREWIRE_LIB
|
||||
help
|
||||
Say Y here to include support for the front and rear microphones
|
||||
|
@ -50,8 +48,6 @@ config SND_ISIGHT
|
|||
|
||||
config SND_SCS1X
|
||||
tristate "Stanton Control System 1 MIDI"
|
||||
select SND_PCM
|
||||
select SND_RAWMIDI
|
||||
select SND_FIREWIRE_LIB
|
||||
help
|
||||
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
|
||||
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
|
||||
|
|
|
@ -10,3 +10,5 @@ obj-$(CONFIG_SND_DICE) += snd-dice.o
|
|||
obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o
|
||||
obj-$(CONFIG_SND_ISIGHT) += snd-isight.o
|
||||
obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o
|
||||
obj-$(CONFIG_SND_FIREWORKS) += fireworks/
|
||||
obj-$(CONFIG_SND_BEBOB) += bebob/
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -8,7 +8,7 @@
|
|||
#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
|
||||
* sample_rate/8000 samples, with rounding up or down to adjust
|
||||
* 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
|
||||
* SYT_INTERVAL samples, with these two types alternating so that
|
||||
* the overall sample rate comes out right.
|
||||
* @CIP_HI_DUALWIRE: At rates above 96 kHz, pretend that the stream runs
|
||||
* at half the actual sample rate with twice the number of channels;
|
||||
* two samples of a channel are stored consecutively in the packet.
|
||||
* Requires blocking mode and SYT_INTERVAL-aligned PCM buffer size.
|
||||
* @CIP_SYNC_TO_DEVICE: In sync to device mode, time stamp in out packets is
|
||||
* generated by in packets. Defaultly this driver generates timestamp.
|
||||
* @CIP_EMPTY_WITH_TAG0: Only for in-stream. Empty in-packets have TAG0.
|
||||
* @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 {
|
||||
CIP_NONBLOCKING = 0x00,
|
||||
CIP_BLOCKING = 0x01,
|
||||
CIP_HI_DUALWIRE = 0x02,
|
||||
enum cip_flags {
|
||||
CIP_NONBLOCKING = 0x00,
|
||||
CIP_BLOCKING = 0x01,
|
||||
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
|
||||
};
|
||||
|
||||
#define AMDTP_IN_PCM_FORMAT_BITS SNDRV_PCM_FMTBIT_S32
|
||||
|
||||
#define AMDTP_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \
|
||||
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_iso_context;
|
||||
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;
|
||||
enum cip_out_flags flags;
|
||||
enum cip_flags flags;
|
||||
enum amdtp_stream_direction direction;
|
||||
struct fw_iso_context *context;
|
||||
struct mutex mutex;
|
||||
|
||||
enum cip_sfc sfc;
|
||||
bool dual_wire;
|
||||
unsigned int data_block_quadlets;
|
||||
unsigned int pcm_channels;
|
||||
unsigned int midi_ports;
|
||||
void (*transfer_samples)(struct amdtp_out_stream *s,
|
||||
void (*transfer_samples)(struct amdtp_stream *s,
|
||||
struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int frames);
|
||||
u8 pcm_positions[AMDTP_MAX_CHANNELS_FOR_PCM];
|
||||
u8 midi_position;
|
||||
|
||||
unsigned int syt_interval;
|
||||
unsigned int transfer_delay;
|
||||
|
@ -82,65 +125,148 @@ struct amdtp_out_stream {
|
|||
unsigned int pcm_buffer_pointer;
|
||||
unsigned int pcm_period_pointer;
|
||||
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,
|
||||
enum cip_out_flags flags);
|
||||
void amdtp_out_stream_destroy(struct amdtp_out_stream *s);
|
||||
int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
enum amdtp_stream_direction dir,
|
||||
enum cip_flags flags);
|
||||
void amdtp_stream_destroy(struct amdtp_stream *s);
|
||||
|
||||
void amdtp_out_stream_set_parameters(struct amdtp_out_stream *s,
|
||||
unsigned int rate,
|
||||
unsigned int pcm_channels,
|
||||
unsigned int midi_ports);
|
||||
unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s);
|
||||
void amdtp_stream_set_parameters(struct amdtp_stream *s,
|
||||
unsigned int rate,
|
||||
unsigned int pcm_channels,
|
||||
unsigned int midi_ports);
|
||||
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);
|
||||
void amdtp_out_stream_update(struct amdtp_out_stream *s);
|
||||
void amdtp_out_stream_stop(struct amdtp_out_stream *s);
|
||||
int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed);
|
||||
void amdtp_stream_update(struct amdtp_stream *s);
|
||||
void amdtp_stream_stop(struct amdtp_stream *s);
|
||||
|
||||
void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s,
|
||||
snd_pcm_format_t format);
|
||||
void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s);
|
||||
unsigned long amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s);
|
||||
void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s);
|
||||
int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
|
||||
struct snd_pcm_runtime *runtime);
|
||||
void amdtp_stream_set_pcm_format(struct amdtp_stream *s,
|
||||
snd_pcm_format_t format);
|
||||
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_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);
|
||||
}
|
||||
|
||||
/**
|
||||
* amdtp_out_streaming_error - check for streaming error
|
||||
* @s: the AMDTP output stream
|
||||
* amdtp_streaming_error - check for streaming error
|
||||
* @s: the AMDTP stream
|
||||
*
|
||||
* If this function returns true, the stream's packet queue has stopped due to
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* amdtp_out_stream_pcm_trigger - start/stop playback from a PCM device
|
||||
* @s: the AMDTP output stream
|
||||
* amdtp_stream_pcm_running - check PCM substream is running or not
|
||||
* @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
|
||||
*
|
||||
* Call this function on a running isochronous stream to enable the actual
|
||||
* transmission of PCM data. This function should be called from the PCM
|
||||
* device's .trigger callback.
|
||||
*/
|
||||
static inline void amdtp_out_stream_pcm_trigger(struct amdtp_out_stream *s,
|
||||
struct snd_pcm_substream *pcm)
|
||||
static inline void amdtp_stream_pcm_trigger(struct amdtp_stream *s,
|
||||
struct snd_pcm_substream *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)
|
||||
{
|
||||
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
|
||||
|
|
|
@ -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
|
|
@ -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);
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
};
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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
|
||||
};
|
|
@ -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
|
||||
};
|
|
@ -14,18 +14,28 @@
|
|||
#include "iso-resources.h"
|
||||
#include "cmp.h"
|
||||
|
||||
#define IMPR_SPEED_MASK 0xc0000000
|
||||
#define IMPR_SPEED_SHIFT 30
|
||||
#define IMPR_XSPEED_MASK 0x00000060
|
||||
#define IMPR_XSPEED_SHIFT 5
|
||||
#define IMPR_PLUGS_MASK 0x0000001f
|
||||
/* MPR common fields */
|
||||
#define MPR_SPEED_MASK 0xc0000000
|
||||
#define MPR_SPEED_SHIFT 30
|
||||
#define MPR_XSPEED_MASK 0x00000060
|
||||
#define MPR_XSPEED_SHIFT 5
|
||||
#define MPR_PLUGS_MASK 0x0000001f
|
||||
|
||||
#define IPCR_ONLINE 0x80000000
|
||||
#define IPCR_BCAST_CONN 0x40000000
|
||||
#define IPCR_P2P_CONN_MASK 0x3f000000
|
||||
#define IPCR_P2P_CONN_SHIFT 24
|
||||
#define IPCR_CHANNEL_MASK 0x003f0000
|
||||
#define IPCR_CHANNEL_SHIFT 16
|
||||
/* PCR common fields */
|
||||
#define PCR_ONLINE 0x80000000
|
||||
#define PCR_BCAST_CONN 0x40000000
|
||||
#define PCR_P2P_CONN_MASK 0x3f000000
|
||||
#define PCR_P2P_CONN_SHIFT 24
|
||||
#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 {
|
||||
ABORT_ON_BUS_RESET,
|
||||
|
@ -39,10 +49,27 @@ void cmp_error(struct cmp_connection *c, const char *fmt, ...)
|
|||
|
||||
va_start(va, fmt);
|
||||
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);
|
||||
}
|
||||
|
||||
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,
|
||||
__be32 (*modify)(struct cmp_connection *c, __be32 old),
|
||||
int (*check)(struct cmp_connection *c, __be32 pcr),
|
||||
|
@ -58,8 +85,7 @@ static int pcr_modify(struct cmp_connection *c,
|
|||
|
||||
err = snd_fw_transaction(
|
||||
c->resources.unit, TCODE_LOCK_COMPARE_SWAP,
|
||||
CSR_REGISTER_BASE + CSR_IPCR(c->pcr_index),
|
||||
buffer, 8,
|
||||
pcr_address(c), buffer, 8,
|
||||
FW_FIXED_GENERATION | c->resources.generation);
|
||||
|
||||
if (err < 0) {
|
||||
|
@ -88,24 +114,25 @@ static int pcr_modify(struct cmp_connection *c,
|
|||
* cmp_connection_init - initializes a connection manager
|
||||
* @c: the connection manager to initialize
|
||||
* @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,
|
||||
struct fw_unit *unit,
|
||||
unsigned int ipcr_index)
|
||||
enum cmp_direction direction,
|
||||
unsigned int pcr_index)
|
||||
{
|
||||
__be32 impr_be;
|
||||
u32 impr;
|
||||
__be32 mpr_be;
|
||||
u32 mpr;
|
||||
int err;
|
||||
|
||||
c->direction = direction;
|
||||
err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
|
||||
CSR_REGISTER_BASE + CSR_IMPR,
|
||||
&impr_be, 4, 0);
|
||||
mpr_address(c), &mpr_be, 4, 0);
|
||||
if (err < 0)
|
||||
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;
|
||||
|
||||
err = fw_iso_resources_init(&c->resources, unit);
|
||||
|
@ -115,15 +142,35 @@ int cmp_connection_init(struct cmp_connection *c,
|
|||
c->connected = false;
|
||||
mutex_init(&c->mutex);
|
||||
c->last_pcr_value = cpu_to_be32(0x80000000);
|
||||
c->pcr_index = ipcr_index;
|
||||
c->max_speed = (impr & IMPR_SPEED_MASK) >> IMPR_SPEED_SHIFT;
|
||||
c->pcr_index = pcr_index;
|
||||
c->max_speed = (mpr & MPR_SPEED_MASK) >> MPR_SPEED_SHIFT;
|
||||
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;
|
||||
}
|
||||
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
|
||||
* @c: the connection manager
|
||||
|
@ -139,23 +186,70 @@ EXPORT_SYMBOL(cmp_connection_destroy);
|
|||
|
||||
static __be32 ipcr_set_modify(struct cmp_connection *c, __be32 ipcr)
|
||||
{
|
||||
ipcr &= ~cpu_to_be32(IPCR_BCAST_CONN |
|
||||
IPCR_P2P_CONN_MASK |
|
||||
IPCR_CHANNEL_MASK);
|
||||
ipcr |= cpu_to_be32(1 << IPCR_P2P_CONN_SHIFT);
|
||||
ipcr |= cpu_to_be32(c->resources.channel << IPCR_CHANNEL_SHIFT);
|
||||
ipcr &= ~cpu_to_be32(PCR_BCAST_CONN |
|
||||
PCR_P2P_CONN_MASK |
|
||||
PCR_CHANNEL_MASK);
|
||||
ipcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT);
|
||||
ipcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT);
|
||||
|
||||
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 |
|
||||
IPCR_P2P_CONN_MASK)) {
|
||||
int id;
|
||||
|
||||
/*
|
||||
* 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");
|
||||
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");
|
||||
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
|
||||
* computer to the target by allocating isochronous resources (channel and
|
||||
* bandwidth) and setting the target's input plug control register. When this
|
||||
* function succeeds, the caller is responsible for starting transmitting
|
||||
* packets.
|
||||
* bandwidth) and setting the target's input/output plug control register.
|
||||
* When this function succeeds, the caller is responsible for starting
|
||||
* transmitting packets.
|
||||
*/
|
||||
int cmp_connection_establish(struct cmp_connection *c,
|
||||
unsigned int max_payload_bytes)
|
||||
|
@ -193,8 +287,13 @@ retry_after_bus_reset:
|
|||
if (err < 0)
|
||||
goto err_mutex;
|
||||
|
||||
err = pcr_modify(c, ipcr_set_modify, ipcr_set_check,
|
||||
ABORT_ON_BUS_RESET);
|
||||
if (c->direction == CMP_OUTPUT)
|
||||
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) {
|
||||
fw_iso_resources_free(&c->resources);
|
||||
goto retry_after_bus_reset;
|
||||
|
@ -221,8 +320,8 @@ EXPORT_SYMBOL(cmp_connection_establish);
|
|||
* cmp_connection_update - update the connection after a bus reset
|
||||
* @c: the connection manager
|
||||
*
|
||||
* This function must be called from the driver's .update handler to reestablish
|
||||
* any connection that might have been active.
|
||||
* This function must be called from the driver's .update handler to
|
||||
* reestablish any connection that might have been active.
|
||||
*
|
||||
* Returns zero on success, or a negative error code. On an error, the
|
||||
* 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)
|
||||
goto err_unconnect;
|
||||
|
||||
err = pcr_modify(c, ipcr_set_modify, ipcr_set_check,
|
||||
SUCCEED_ON_BUS_RESET);
|
||||
if (c->direction == CMP_OUTPUT)
|
||||
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)
|
||||
goto err_resources;
|
||||
|
||||
|
@ -261,19 +365,18 @@ err_unconnect:
|
|||
}
|
||||
EXPORT_SYMBOL(cmp_connection_update);
|
||||
|
||||
|
||||
static __be32 ipcr_break_modify(struct cmp_connection *c, __be32 ipcr)
|
||||
static __be32 pcr_break_modify(struct cmp_connection *c, __be32 pcr)
|
||||
{
|
||||
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
|
||||
* @c: the connection manager
|
||||
*
|
||||
* This function deactives the connection in the target's input plug control
|
||||
* register, and frees the isochronous resources of the connection. Before
|
||||
* calling this function, the caller should cease transmitting packets.
|
||||
* This function deactives the connection in the target's input/output plug
|
||||
* control register, and frees the isochronous resources of the connection.
|
||||
* Before calling this function, the caller should cease transmitting packets.
|
||||
*/
|
||||
void cmp_connection_break(struct cmp_connection *c)
|
||||
{
|
||||
|
@ -286,7 +389,7 @@ void cmp_connection_break(struct cmp_connection *c)
|
|||
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)
|
||||
cmp_error(c, "plug is still connected\n");
|
||||
|
||||
|
|
|
@ -7,12 +7,17 @@
|
|||
|
||||
struct fw_unit;
|
||||
|
||||
enum cmp_direction {
|
||||
CMP_INPUT = 0,
|
||||
CMP_OUTPUT,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cmp_connection - manages an isochronous connection to a device
|
||||
* @speed: the connection's actual speed
|
||||
*
|
||||
* This structure manages (using CMP) an isochronous stream from the local
|
||||
* computer to a device's input plug (iPCR).
|
||||
* This structure manages (using CMP) an isochronous stream between the local
|
||||
* 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
|
||||
* possible to overlay connections on top of this one.
|
||||
|
@ -26,11 +31,14 @@ struct cmp_connection {
|
|||
__be32 last_pcr_value;
|
||||
unsigned int pcr_index;
|
||||
unsigned int max_speed;
|
||||
enum cmp_direction direction;
|
||||
};
|
||||
|
||||
int cmp_connection_init(struct cmp_connection *connection,
|
||||
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);
|
||||
|
||||
int cmp_connection_establish(struct cmp_connection *connection,
|
||||
|
|
|
@ -51,7 +51,7 @@ struct dice {
|
|||
wait_queue_head_t hwdep_wait;
|
||||
u32 notification_bits;
|
||||
struct fw_iso_resources resources;
|
||||
struct amdtp_out_stream stream;
|
||||
struct amdtp_stream stream;
|
||||
};
|
||||
|
||||
MODULE_DESCRIPTION("DICE driver");
|
||||
|
@ -420,22 +420,7 @@ static int dice_open(struct snd_pcm_substream *substream)
|
|||
if (err < 0)
|
||||
goto err_lock;
|
||||
|
||||
err = snd_pcm_hw_constraint_step(runtime, 0,
|
||||
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);
|
||||
err = amdtp_stream_add_pcm_hw_constraints(&dice->stream, runtime);
|
||||
if (err < 0)
|
||||
goto err_lock;
|
||||
|
||||
|
@ -460,17 +445,17 @@ static int dice_stream_start_packets(struct dice *dice)
|
|||
{
|
||||
int err;
|
||||
|
||||
if (amdtp_out_stream_running(&dice->stream))
|
||||
if (amdtp_stream_running(&dice->stream))
|
||||
return 0;
|
||||
|
||||
err = amdtp_out_stream_start(&dice->stream, dice->resources.channel,
|
||||
fw_parent_device(dice->unit)->max_speed);
|
||||
err = amdtp_stream_start(&dice->stream, dice->resources.channel,
|
||||
fw_parent_device(dice->unit)->max_speed);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = dice_enable_set(dice);
|
||||
if (err < 0) {
|
||||
amdtp_out_stream_stop(&dice->stream);
|
||||
amdtp_stream_stop(&dice->stream);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -484,7 +469,7 @@ static int dice_stream_start(struct dice *dice)
|
|||
|
||||
if (!dice->resources.allocated) {
|
||||
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);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
@ -516,9 +501,9 @@ error:
|
|||
|
||||
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);
|
||||
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 dice *dice = substream->private_data;
|
||||
unsigned int rate_index, mode;
|
||||
unsigned int rate_index, mode, rate, channels, i;
|
||||
int err;
|
||||
|
||||
mutex_lock(&dice->mutex);
|
||||
|
@ -575,18 +560,39 @@ static int dice_hw_params(struct snd_pcm_substream *substream,
|
|||
if (err < 0)
|
||||
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);
|
||||
if (err < 0)
|
||||
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);
|
||||
amdtp_out_stream_set_parameters(&dice->stream,
|
||||
params_rate(hw_params),
|
||||
params_channels(hw_params),
|
||||
dice->rx_midi_ports[mode]);
|
||||
amdtp_out_stream_set_pcm_format(&dice->stream,
|
||||
params_format(hw_params));
|
||||
amdtp_stream_set_parameters(&dice->stream, rate, channels,
|
||||
dice->rx_midi_ports[mode]);
|
||||
amdtp_stream_set_pcm_format(&dice->stream,
|
||||
params_format(hw_params));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -609,7 +615,7 @@ static int dice_prepare(struct snd_pcm_substream *substream)
|
|||
|
||||
mutex_lock(&dice->mutex);
|
||||
|
||||
if (amdtp_out_streaming_error(&dice->stream))
|
||||
if (amdtp_streaming_error(&dice->stream))
|
||||
dice_stream_stop_packets(dice);
|
||||
|
||||
err = dice_stream_start(dice);
|
||||
|
@ -620,7 +626,7 @@ static int dice_prepare(struct snd_pcm_substream *substream)
|
|||
|
||||
mutex_unlock(&dice->mutex);
|
||||
|
||||
amdtp_out_stream_pcm_prepare(&dice->stream);
|
||||
amdtp_stream_pcm_prepare(&dice->stream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -640,7 +646,7 @@ static int dice_trigger(struct snd_pcm_substream *substream, int cmd)
|
|||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
amdtp_out_stream_pcm_trigger(&dice->stream, pcm);
|
||||
amdtp_stream_pcm_trigger(&dice->stream, pcm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -649,7 +655,7 @@ static snd_pcm_uframes_t dice_pointer(struct snd_pcm_substream *substream)
|
|||
{
|
||||
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)
|
||||
|
@ -1104,7 +1110,7 @@ static void dice_card_free(struct snd_card *card)
|
|||
{
|
||||
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);
|
||||
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;
|
||||
dice->resources.channels_mask = 0x00000000ffffffffuLL;
|
||||
|
||||
err = amdtp_out_stream_init(&dice->stream, unit,
|
||||
CIP_BLOCKING | CIP_HI_DUALWIRE);
|
||||
err = amdtp_stream_init(&dice->stream, unit, AMDTP_OUT_STREAM,
|
||||
CIP_BLOCKING);
|
||||
if (err < 0)
|
||||
goto err_resources;
|
||||
|
||||
|
@ -1417,7 +1423,7 @@ static void dice_remove(struct fw_unit *unit)
|
|||
{
|
||||
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);
|
||||
|
||||
|
@ -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
|
||||
* manner.
|
||||
*/
|
||||
amdtp_out_stream_pcm_abort(&dice->stream);
|
||||
amdtp_stream_pcm_abort(&dice->stream);
|
||||
|
||||
mutex_lock(&dice->mutex);
|
||||
|
||||
|
|
|
@ -10,12 +10,14 @@
|
|||
#include <linux/firewire-constants.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/delay.h>
|
||||
#include "fcp.h"
|
||||
#include "lib.h"
|
||||
#include "amdtp.h"
|
||||
|
||||
#define CTS_AVC 0x00
|
||||
|
||||
|
@ -23,6 +25,158 @@
|
|||
#define ERROR_DELAY_MS 5
|
||||
#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 LIST_HEAD(transactions);
|
||||
|
||||
|
@ -30,6 +184,7 @@ enum fcp_state {
|
|||
STATE_PENDING,
|
||||
STATE_BUS_RESET,
|
||||
STATE_COMPLETE,
|
||||
STATE_DEFERRED,
|
||||
};
|
||||
|
||||
struct fcp_transaction {
|
||||
|
@ -40,6 +195,7 @@ struct fcp_transaction {
|
|||
unsigned int response_match_bytes;
|
||||
enum fcp_state state;
|
||||
wait_queue_head_t wait;
|
||||
bool deferrable;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -62,8 +218,6 @@ struct fcp_transaction {
|
|||
*
|
||||
* @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.
|
||||
*/
|
||||
int fcp_avc_transaction(struct fw_unit *unit,
|
||||
|
@ -81,6 +235,9 @@ int fcp_avc_transaction(struct fw_unit *unit,
|
|||
t.state = STATE_PENDING;
|
||||
init_waitqueue_head(&t.wait);
|
||||
|
||||
if (*(const u8 *)command == 0x00 || *(const u8 *)command == 0x03)
|
||||
t.deferrable = true;
|
||||
|
||||
spin_lock_irq(&transactions_lock);
|
||||
list_add_tail(&t.list, &transactions);
|
||||
spin_unlock_irq(&transactions_lock);
|
||||
|
@ -93,11 +250,21 @@ int fcp_avc_transaction(struct fw_unit *unit,
|
|||
(void *)command, command_size, 0);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
deferred:
|
||||
wait_event_timeout(t.wait, t.state != STATE_PENDING,
|
||||
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;
|
||||
break;
|
||||
} else if (t.state == STATE_BUS_RESET) {
|
||||
|
@ -132,7 +299,8 @@ void fcp_bus_reset(struct fw_unit *unit)
|
|||
spin_lock_irq(&transactions_lock);
|
||||
list_for_each_entry(t, &transactions, list) {
|
||||
if (t->unit == unit &&
|
||||
t->state == STATE_PENDING) {
|
||||
(t->state == STATE_PENDING ||
|
||||
t->state == STATE_DEFERRED)) {
|
||||
t->state = STATE_BUS_RESET;
|
||||
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 &&
|
||||
is_matching_response(t, data, length)) {
|
||||
t->state = STATE_COMPLETE;
|
||||
t->response_size = min((unsigned int)length,
|
||||
t->response_size);
|
||||
memcpy(t->response_buffer, data, t->response_size);
|
||||
if (t->deferrable && *(const u8 *)data == 0x0f) {
|
||||
t->state = STATE_DEFERRED;
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,29 @@
|
|||
#ifndef SOUND_FIREWIRE_FCP_H_INCLUDED
|
||||
#define SOUND_FIREWIRE_FCP_H_INCLUDED
|
||||
|
||||
#define AVC_PLUG_INFO_BUF_BYTES 4
|
||||
|
||||
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,
|
||||
const void *command, unsigned int command_size,
|
||||
void *response, unsigned int response_size,
|
||||
|
|
|
@ -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
|
|
@ -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);
|
|
@ -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
|
|
@ -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,
|
||||
¶m, 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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -51,7 +51,7 @@ struct fwspk {
|
|||
const struct device_info *device_info;
|
||||
struct mutex mutex;
|
||||
struct cmp_connection connection;
|
||||
struct amdtp_out_stream stream;
|
||||
struct amdtp_stream stream;
|
||||
bool mute;
|
||||
s16 volume[6];
|
||||
s16 volume_min;
|
||||
|
@ -167,13 +167,7 @@ static int fwspk_open(struct snd_pcm_substream *substream)
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = snd_pcm_hw_constraint_minmax(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);
|
||||
err = amdtp_stream_add_pcm_hw_constraints(&fwspk->stream, runtime);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@ -187,48 +181,12 @@ static int fwspk_close(struct snd_pcm_substream *substream)
|
|||
|
||||
static void fwspk_stop_stream(struct fwspk *fwspk)
|
||||
{
|
||||
if (amdtp_out_stream_running(&fwspk->stream)) {
|
||||
amdtp_out_stream_stop(&fwspk->stream);
|
||||
if (amdtp_stream_running(&fwspk->stream)) {
|
||||
amdtp_stream_stop(&fwspk->stream);
|
||||
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,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
|
@ -244,17 +202,20 @@ static int fwspk_hw_params(struct snd_pcm_substream *substream,
|
|||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
amdtp_out_stream_set_parameters(&fwspk->stream,
|
||||
params_rate(hw_params),
|
||||
params_channels(hw_params),
|
||||
0);
|
||||
amdtp_stream_set_parameters(&fwspk->stream,
|
||||
params_rate(hw_params),
|
||||
params_channels(hw_params),
|
||||
0);
|
||||
|
||||
amdtp_out_stream_set_pcm_format(&fwspk->stream,
|
||||
params_format(hw_params));
|
||||
amdtp_stream_set_pcm_format(&fwspk->stream,
|
||||
params_format(hw_params));
|
||||
|
||||
err = fwspk_set_rate(fwspk, fwspk->stream.sfc);
|
||||
if (err < 0)
|
||||
err = avc_general_set_sig_fmt(fwspk->unit, params_rate(hw_params),
|
||||
AVC_GENERAL_PLUG_DIR_IN, 0);
|
||||
if (err < 0) {
|
||||
dev_err(&fwspk->unit->device, "failed to set sample rate\n");
|
||||
goto err_buffer;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -282,25 +243,25 @@ static int fwspk_prepare(struct snd_pcm_substream *substream)
|
|||
|
||||
mutex_lock(&fwspk->mutex);
|
||||
|
||||
if (amdtp_out_streaming_error(&fwspk->stream))
|
||||
if (amdtp_streaming_error(&fwspk->stream))
|
||||
fwspk_stop_stream(fwspk);
|
||||
|
||||
if (!amdtp_out_stream_running(&fwspk->stream)) {
|
||||
if (!amdtp_stream_running(&fwspk->stream)) {
|
||||
err = cmp_connection_establish(&fwspk->connection,
|
||||
amdtp_out_stream_get_max_payload(&fwspk->stream));
|
||||
amdtp_stream_get_max_payload(&fwspk->stream));
|
||||
if (err < 0)
|
||||
goto err_mutex;
|
||||
|
||||
err = amdtp_out_stream_start(&fwspk->stream,
|
||||
fwspk->connection.resources.channel,
|
||||
fwspk->connection.speed);
|
||||
err = amdtp_stream_start(&fwspk->stream,
|
||||
fwspk->connection.resources.channel,
|
||||
fwspk->connection.speed);
|
||||
if (err < 0)
|
||||
goto err_connection;
|
||||
}
|
||||
|
||||
mutex_unlock(&fwspk->mutex);
|
||||
|
||||
amdtp_out_stream_pcm_prepare(&fwspk->stream);
|
||||
amdtp_stream_pcm_prepare(&fwspk->stream);
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -327,7 +288,7 @@ static int fwspk_trigger(struct snd_pcm_substream *substream, int cmd)
|
|||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
amdtp_out_stream_pcm_trigger(&fwspk->stream, pcm);
|
||||
amdtp_stream_pcm_trigger(&fwspk->stream, pcm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -335,7 +296,7 @@ static snd_pcm_uframes_t fwspk_pointer(struct snd_pcm_substream *substream)
|
|||
{
|
||||
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)
|
||||
|
@ -653,7 +614,7 @@ static void fwspk_card_free(struct snd_card *card)
|
|||
{
|
||||
struct fwspk *fwspk = card->private_data;
|
||||
|
||||
amdtp_out_stream_destroy(&fwspk->stream);
|
||||
amdtp_stream_destroy(&fwspk->stream);
|
||||
cmp_connection_destroy(&fwspk->connection);
|
||||
fw_unit_put(fwspk->unit);
|
||||
mutex_destroy(&fwspk->mutex);
|
||||
|
@ -679,11 +640,12 @@ static int fwspk_probe(struct fw_unit *unit,
|
|||
fwspk->unit = fw_unit_get(unit);
|
||||
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)
|
||||
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)
|
||||
goto err_connection;
|
||||
|
||||
|
@ -733,21 +695,21 @@ static void fwspk_bus_reset(struct fw_unit *unit)
|
|||
fcp_bus_reset(fwspk->unit);
|
||||
|
||||
if (cmp_connection_update(&fwspk->connection) < 0) {
|
||||
amdtp_out_stream_pcm_abort(&fwspk->stream);
|
||||
amdtp_stream_pcm_abort(&fwspk->stream);
|
||||
mutex_lock(&fwspk->mutex);
|
||||
fwspk_stop_stream(fwspk);
|
||||
mutex_unlock(&fwspk->mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
amdtp_out_stream_update(&fwspk->stream);
|
||||
amdtp_stream_update(&fwspk->stream);
|
||||
}
|
||||
|
||||
static void fwspk_remove(struct fw_unit *unit)
|
||||
{
|
||||
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);
|
||||
|
||||
mutex_lock(&fwspk->mutex);
|
||||
|
|
|
@ -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 (i = 0; i < 8; ++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))
|
||||
continue; /* first check */
|
||||
csum = 0;
|
||||
for (i = 0; i < sizeof(struct rom_hdr); 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)
|
||||
continue; /* not valid rom */
|
||||
gus->gf1.rom_banks++;
|
||||
|
|
|
@ -648,14 +648,14 @@ static int au1000_ac97_probe(struct platform_device *pdev)
|
|||
goto out;
|
||||
|
||||
err = -EBUSY;
|
||||
au1000->ac97_res_port = request_mem_region(r->start,
|
||||
r->end - r->start + 1, pdev->name);
|
||||
au1000->ac97_res_port = request_mem_region(r->start, resource_size(r),
|
||||
pdev->name);
|
||||
if (!au1000->ac97_res_port) {
|
||||
snd_printk(KERN_ERR "ALSA AC97: can't grab AC97 port\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
io = ioremap(r->start, r->end - r->start + 1);
|
||||
io = ioremap(r->start, resource_size(r));
|
||||
if (!io)
|
||||
goto out;
|
||||
|
||||
|
|
|
@ -567,7 +567,6 @@ static int mpu401_out(int dev, unsigned char midi_byte)
|
|||
static int mpu401_command(int dev, mpu_command_rec * cmd)
|
||||
{
|
||||
int i, timeout, ok;
|
||||
int ret = 0;
|
||||
unsigned long flags;
|
||||
struct mpu_config *devc;
|
||||
|
||||
|
@ -644,7 +643,6 @@ retry:
|
|||
}
|
||||
}
|
||||
}
|
||||
ret = 0;
|
||||
cmd->data[0] = 0;
|
||||
|
||||
if (cmd->nr_returns)
|
||||
|
@ -666,7 +664,7 @@ retry:
|
|||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&devc->lock,flags);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpu_cmd(int dev, int cmd, int data)
|
||||
|
|
|
@ -2625,15 +2625,12 @@ static int __init cs4297a_init(void)
|
|||
u32 pwr, id;
|
||||
mm_segment_t fs;
|
||||
int rval;
|
||||
#ifndef CONFIG_BCM_CS4297A_CSWARM
|
||||
u64 cfg;
|
||||
int mdio_val;
|
||||
#endif
|
||||
|
||||
CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO
|
||||
"cs4297a: cs4297a_init_module()+ \n"));
|
||||
|
||||
#ifndef CONFIG_BCM_CS4297A_CSWARM
|
||||
mdio_val = __raw_readq(KSEG1 + A_MAC_REGISTER(2, R_MAC_MDIO)) &
|
||||
(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));
|
||||
/* Give the codec some time to finish resetting (start the bit clock) */
|
||||
udelay(100);
|
||||
#endif
|
||||
|
||||
if (!(s = kzalloc(sizeof(struct cs4297a_state), GFP_KERNEL))) {
|
||||
CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -34,8 +35,6 @@
|
|||
#include <sound/opl3.h>
|
||||
#include <sound/initval.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#ifdef CONFIG_SND_FM801_TEA575X_BOOL
|
||||
#include <media/tea575x.h>
|
||||
#endif
|
||||
|
@ -80,7 +79,10 @@ MODULE_PARM_DESC(radio_nr, "Radio device numbers");
|
|||
* 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_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_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 {
|
||||
int irq;
|
||||
|
||||
unsigned long port; /* I/O port number */
|
||||
unsigned int multichannel: 1, /* multichannel support */
|
||||
secondary: 1; /* secondary codec */
|
||||
unsigned char secondary_addr; /* address of the secondary codec */
|
||||
unsigned int tea575x_tuner; /* tuner access method & flags */
|
||||
unsigned long port;
|
||||
unsigned int multichannel: 1,
|
||||
secondary: 1;
|
||||
unsigned char secondary_addr;
|
||||
unsigned int tea575x_tuner;
|
||||
|
||||
unsigned short ply_ctrl; /* playback control */
|
||||
unsigned short cap_ctrl; /* capture control */
|
||||
unsigned short ply_ctrl;
|
||||
unsigned short cap_ctrl;
|
||||
|
||||
unsigned long ply_buffer;
|
||||
unsigned int ply_buf;
|
||||
|
@ -222,6 +230,30 @@ MODULE_DEVICE_TABLE(pci, snd_fm801_ids);
|
|||
* 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,
|
||||
unsigned short mask, unsigned short value)
|
||||
{
|
||||
|
@ -244,73 +276,54 @@ static void snd_fm801_codec_write(struct snd_ac97 *ac97,
|
|||
unsigned short val)
|
||||
{
|
||||
struct fm801 *chip = ac97->private_data;
|
||||
int idx;
|
||||
|
||||
/*
|
||||
* Wait until the codec interface is not ready..
|
||||
*/
|
||||
for (idx = 0; idx < 100; idx++) {
|
||||
if (!(inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_BUSY))
|
||||
goto ok1;
|
||||
udelay(10);
|
||||
if (!fm801_ac97_is_ready(chip, 100)) {
|
||||
dev_err(chip->card->dev, "AC'97 interface is busy (1)\n");
|
||||
return;
|
||||
}
|
||||
dev_err(chip->card->dev, "AC'97 interface is busy (1)\n");
|
||||
return;
|
||||
|
||||
ok1:
|
||||
/* write data and address */
|
||||
outw(val, FM801_REG(chip, AC97_DATA));
|
||||
outw(reg | (ac97->addr << FM801_AC97_ADDR_SHIFT), FM801_REG(chip, AC97_CMD));
|
||||
fm801_writew(chip, AC97_DATA, val);
|
||||
fm801_writew(chip, AC97_CMD, reg | (ac97->addr << FM801_AC97_ADDR_SHIFT));
|
||||
/*
|
||||
* Wait until the write command is not completed..
|
||||
*/
|
||||
for (idx = 0; idx < 1000; idx++) {
|
||||
if (!(inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_BUSY))
|
||||
return;
|
||||
udelay(10);
|
||||
}
|
||||
dev_err(chip->card->dev, "AC'97 interface #%d is busy (2)\n", ac97->num);
|
||||
*/
|
||||
if (!fm801_ac97_is_ready(chip, 1000))
|
||||
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)
|
||||
{
|
||||
struct fm801 *chip = ac97->private_data;
|
||||
int idx;
|
||||
|
||||
/*
|
||||
* Wait until the codec interface is not ready..
|
||||
*/
|
||||
for (idx = 0; idx < 100; idx++) {
|
||||
if (!(inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_BUSY))
|
||||
goto ok1;
|
||||
udelay(10);
|
||||
if (!fm801_ac97_is_ready(chip, 100)) {
|
||||
dev_err(chip->card->dev, "AC'97 interface is busy (1)\n");
|
||||
return 0;
|
||||
}
|
||||
dev_err(chip->card->dev, "AC'97 interface is busy (1)\n");
|
||||
return 0;
|
||||
|
||||
ok1:
|
||||
/* read command */
|
||||
outw(reg | (ac97->addr << FM801_AC97_ADDR_SHIFT) | FM801_AC97_READ,
|
||||
FM801_REG(chip, AC97_CMD));
|
||||
for (idx = 0; idx < 100; idx++) {
|
||||
if (!(inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_BUSY))
|
||||
goto ok2;
|
||||
udelay(10);
|
||||
fm801_writew(chip, AC97_CMD,
|
||||
reg | (ac97->addr << FM801_AC97_ADDR_SHIFT) | FM801_AC97_READ);
|
||||
if (!fm801_ac97_is_ready(chip, 100)) {
|
||||
dev_err(chip->card->dev, "AC'97 interface #%d is busy (2)\n",
|
||||
ac97->num);
|
||||
return 0;
|
||||
}
|
||||
dev_err(chip->card->dev, "AC'97 interface #%d is busy (2)\n", ac97->num);
|
||||
return 0;
|
||||
|
||||
ok2:
|
||||
for (idx = 0; idx < 1000; idx++) {
|
||||
if (inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_VALID)
|
||||
goto ok3;
|
||||
udelay(10);
|
||||
if (!fm801_ac97_is_valid(chip, 1000)) {
|
||||
dev_err(chip->card->dev,
|
||||
"AC'97 interface #%d is not valid (2)\n", ac97->num);
|
||||
return 0;
|
||||
}
|
||||
dev_err(chip->card->dev, "AC'97 interface #%d is not valid (2)\n", ac97->num);
|
||||
return 0;
|
||||
|
||||
ok3:
|
||||
return inw(FM801_REG(chip, AC97_DATA));
|
||||
return fm801_readw(chip, AC97_DATA);
|
||||
}
|
||||
|
||||
static unsigned int rates[] = {
|
||||
|
@ -384,7 +397,7 @@ static int snd_fm801_playback_trigger(struct snd_pcm_substream *substream,
|
|||
snd_BUG();
|
||||
return -EINVAL;
|
||||
}
|
||||
outw(chip->ply_ctrl, FM801_REG(chip, PLY_CTRL));
|
||||
fm801_writew(chip, PLY_CTRL, chip->ply_ctrl);
|
||||
spin_unlock(&chip->reg_lock);
|
||||
return 0;
|
||||
}
|
||||
|
@ -419,7 +432,7 @@ static int snd_fm801_capture_trigger(struct snd_pcm_substream *substream,
|
|||
snd_BUG();
|
||||
return -EINVAL;
|
||||
}
|
||||
outw(chip->cap_ctrl, FM801_REG(chip, CAP_CTRL));
|
||||
fm801_writew(chip, CAP_CTRL, chip->cap_ctrl);
|
||||
spin_unlock(&chip->reg_lock);
|
||||
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_buf = 0;
|
||||
outw(chip->ply_ctrl, FM801_REG(chip, PLY_CTRL));
|
||||
outw(chip->ply_count - 1, FM801_REG(chip, PLY_COUNT));
|
||||
fm801_writew(chip, PLY_CTRL, chip->ply_ctrl);
|
||||
fm801_writew(chip, PLY_COUNT, chip->ply_count - 1);
|
||||
chip->ply_buffer = runtime->dma_addr;
|
||||
chip->ply_pos = 0;
|
||||
outl(chip->ply_buffer, FM801_REG(chip, PLY_BUF1));
|
||||
outl(chip->ply_buffer + (chip->ply_count % chip->ply_size), FM801_REG(chip, PLY_BUF2));
|
||||
fm801_writel(chip, PLY_BUF1, chip->ply_buffer);
|
||||
fm801_writel(chip, PLY_BUF2,
|
||||
chip->ply_buffer + (chip->ply_count % chip->ply_size));
|
||||
spin_unlock_irq(&chip->reg_lock);
|
||||
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 |= snd_fm801_rate_bits(runtime->rate) << FM801_RATE_SHIFT;
|
||||
chip->cap_buf = 0;
|
||||
outw(chip->cap_ctrl, FM801_REG(chip, CAP_CTRL));
|
||||
outw(chip->cap_count - 1, FM801_REG(chip, CAP_COUNT));
|
||||
fm801_writew(chip, CAP_CTRL, chip->cap_ctrl);
|
||||
fm801_writew(chip, CAP_COUNT, chip->cap_count - 1);
|
||||
chip->cap_buffer = runtime->dma_addr;
|
||||
chip->cap_pos = 0;
|
||||
outl(chip->cap_buffer, FM801_REG(chip, CAP_BUF1));
|
||||
outl(chip->cap_buffer + (chip->cap_count % chip->cap_size), FM801_REG(chip, CAP_BUF2));
|
||||
fm801_writel(chip, CAP_BUF1, chip->cap_buffer);
|
||||
fm801_writel(chip, CAP_BUF2,
|
||||
chip->cap_buffer + (chip->cap_count % chip->cap_size));
|
||||
spin_unlock_irq(&chip->reg_lock);
|
||||
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))
|
||||
return 0;
|
||||
spin_lock(&chip->reg_lock);
|
||||
ptr = chip->ply_pos + (chip->ply_count - 1) - inw(FM801_REG(chip, PLY_COUNT));
|
||||
if (inw(FM801_REG(chip, IRQ_STATUS)) & FM801_IRQ_PLAYBACK) {
|
||||
ptr = chip->ply_pos + (chip->ply_count - 1) - fm801_readw(chip, PLY_COUNT);
|
||||
if (fm801_readw(chip, IRQ_STATUS) & FM801_IRQ_PLAYBACK) {
|
||||
ptr += chip->ply_count;
|
||||
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))
|
||||
return 0;
|
||||
spin_lock(&chip->reg_lock);
|
||||
ptr = chip->cap_pos + (chip->cap_count - 1) - inw(FM801_REG(chip, CAP_COUNT));
|
||||
if (inw(FM801_REG(chip, IRQ_STATUS)) & FM801_IRQ_CAPTURE) {
|
||||
ptr = chip->cap_pos + (chip->cap_count - 1) - fm801_readw(chip, CAP_COUNT);
|
||||
if (fm801_readw(chip, IRQ_STATUS) & FM801_IRQ_CAPTURE) {
|
||||
ptr += chip->cap_count;
|
||||
ptr %= chip->cap_size;
|
||||
}
|
||||
|
@ -533,12 +548,12 @@ static irqreturn_t snd_fm801_interrupt(int irq, void *dev_id)
|
|||
unsigned short status;
|
||||
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;
|
||||
if (! status)
|
||||
return IRQ_NONE;
|
||||
/* 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) {
|
||||
spin_lock(&chip->reg_lock);
|
||||
chip->ply_buf++;
|
||||
|
@ -546,10 +561,10 @@ static irqreturn_t snd_fm801_interrupt(int irq, void *dev_id)
|
|||
chip->ply_pos %= chip->ply_size;
|
||||
tmp = chip->ply_pos + chip->ply_count;
|
||||
tmp %= chip->ply_size;
|
||||
outl(chip->ply_buffer + tmp,
|
||||
(chip->ply_buf & 1) ?
|
||||
FM801_REG(chip, PLY_BUF1) :
|
||||
FM801_REG(chip, PLY_BUF2));
|
||||
if (chip->ply_buf & 1)
|
||||
fm801_writel(chip, PLY_BUF1, chip->ply_buffer + tmp);
|
||||
else
|
||||
fm801_writel(chip, PLY_BUF2, chip->ply_buffer + tmp);
|
||||
spin_unlock(&chip->reg_lock);
|
||||
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;
|
||||
tmp = chip->cap_pos + chip->cap_count;
|
||||
tmp %= chip->cap_size;
|
||||
outl(chip->cap_buffer + tmp,
|
||||
(chip->cap_buf & 1) ?
|
||||
FM801_REG(chip, CAP_BUF1) :
|
||||
FM801_REG(chip, CAP_BUF2));
|
||||
if (chip->cap_buf & 1)
|
||||
fm801_writel(chip, CAP_BUF1, chip->cap_buffer + tmp);
|
||||
else
|
||||
fm801_writel(chip, CAP_BUF2, chip->cap_buffer + tmp);
|
||||
spin_unlock(&chip->reg_lock);
|
||||
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)
|
||||
{
|
||||
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);
|
||||
|
||||
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 */
|
||||
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)
|
||||
{
|
||||
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);
|
||||
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)
|
||||
{
|
||||
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);
|
||||
|
||||
/* 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));
|
||||
}
|
||||
|
||||
outw(reg, FM801_REG(chip, GPIO_CTRL));
|
||||
fm801_writew(chip, GPIO_CTRL, reg);
|
||||
}
|
||||
|
||||
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);
|
||||
unsigned short val;
|
||||
|
||||
val = inw(FM801_REG(chip, REC_SRC)) & 7;
|
||||
val = fm801_readw(chip, REC_SRC) & 7;
|
||||
if (val > 4)
|
||||
val = 4;
|
||||
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;
|
||||
|
||||
outw(FM801_AC97_READ | (codec_id << FM801_AC97_ADDR_SHIFT) | reg,
|
||||
FM801_REG(chip, AC97_CMD));
|
||||
fm801_writew(chip, AC97_CMD,
|
||||
reg | (codec_id << FM801_AC97_ADDR_SHIFT) | FM801_AC97_READ);
|
||||
udelay(5);
|
||||
do {
|
||||
if ((inw(FM801_REG(chip, AC97_CMD)) & (FM801_AC97_VALID|FM801_AC97_BUSY))
|
||||
== FM801_AC97_VALID)
|
||||
if ((fm801_readw(chip, AC97_CMD) &
|
||||
(FM801_AC97_VALID | FM801_AC97_BUSY)) == FM801_AC97_VALID)
|
||||
return 0;
|
||||
schedule_timeout_uninterruptible(1);
|
||||
} while (time_after(timeout, jiffies));
|
||||
|
@ -1093,10 +1108,10 @@ static int snd_fm801_chip_init(struct fm801 *chip, int resume)
|
|||
goto __ac97_ok;
|
||||
|
||||
/* codec cold reset + AC'97 warm reset */
|
||||
outw((1<<5) | (1<<6), FM801_REG(chip, CODEC_CTRL));
|
||||
inw(FM801_REG(chip, CODEC_CTRL)); /* flush posting data */
|
||||
fm801_writew(chip, CODEC_CTRL, (1 << 5) | (1 << 6));
|
||||
fm801_readw(chip, CODEC_CTRL); /* flush posting data */
|
||||
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 (!resume) {
|
||||
|
@ -1117,7 +1132,7 @@ static int snd_fm801_chip_init(struct fm801 *chip, int resume)
|
|||
for (i = 3; i > 0; i--) {
|
||||
if (!wait_for_codec(chip, i, AC97_VENDOR_ID1,
|
||||
msecs_to_jiffies(50))) {
|
||||
cmdw = inw(FM801_REG(chip, AC97_DATA));
|
||||
cmdw = fm801_readw(chip, AC97_DATA);
|
||||
if (cmdw != 0xffff && cmdw != 0) {
|
||||
chip->secondary = 1;
|
||||
chip->secondary_addr = i;
|
||||
|
@ -1135,23 +1150,24 @@ static int snd_fm801_chip_init(struct fm801 *chip, int resume)
|
|||
__ac97_ok:
|
||||
|
||||
/* init volume */
|
||||
outw(0x0808, FM801_REG(chip, PCM_VOL));
|
||||
outw(0x9f1f, FM801_REG(chip, FM_VOL));
|
||||
outw(0x8808, FM801_REG(chip, I2S_VOL));
|
||||
fm801_writew(chip, PCM_VOL, 0x0808);
|
||||
fm801_writew(chip, FM_VOL, 0x9f1f);
|
||||
fm801_writew(chip, I2S_VOL, 0x8808);
|
||||
|
||||
/* I2S control - I2S mode */
|
||||
outw(0x0003, FM801_REG(chip, I2S_MODE));
|
||||
fm801_writew(chip, I2S_MODE, 0x0003);
|
||||
|
||||
/* interrupt setup */
|
||||
cmdw = inw(FM801_REG(chip, IRQ_MASK));
|
||||
cmdw = fm801_readw(chip, IRQ_MASK);
|
||||
if (chip->irq < 0)
|
||||
cmdw |= 0x00c3; /* mask everything, no PCM nor MPU */
|
||||
else
|
||||
cmdw &= ~0x0083; /* unmask MPU, PLAYBACK & CAPTURE */
|
||||
outw(cmdw, FM801_REG(chip, IRQ_MASK));
|
||||
fm801_writew(chip, IRQ_MASK, cmdw);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
@ -1165,9 +1181,9 @@ static int snd_fm801_free(struct fm801 *chip)
|
|||
goto __end_hw;
|
||||
|
||||
/* interrupt setup - mask everything */
|
||||
cmdw = inw(FM801_REG(chip, IRQ_MASK));
|
||||
cmdw = fm801_readw(chip, IRQ_MASK);
|
||||
cmdw |= 0x00c3;
|
||||
outw(cmdw, FM801_REG(chip, IRQ_MASK));
|
||||
fm801_writew(chip, IRQ_MASK, cmdw);
|
||||
|
||||
__end_hw:
|
||||
#ifdef CONFIG_SND_FM801_TEA575X_BOOL
|
||||
|
@ -1339,15 +1355,15 @@ static int snd_card_fm801_probe(struct pci_dev *pci,
|
|||
return err;
|
||||
}
|
||||
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_IRQ_HOOK,
|
||||
-1, &chip->rmidi)) < 0) {
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
if ((err = snd_opl3_create(card, FM801_REG(chip, OPL3_BANK0),
|
||||
FM801_REG(chip, OPL3_BANK1),
|
||||
if ((err = snd_opl3_create(card, chip->port + FM801_OPL3_BANK0,
|
||||
chip->port + FM801_OPL3_BANK1,
|
||||
OPL3_HW_OPL3_FM801, 1, &opl3)) < 0) {
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
|
|
|
@ -20,6 +20,21 @@ config SND_HDA_INTEL
|
|||
To compile this driver as a module, choose M here: the module
|
||||
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
|
||||
|
||||
config SND_HDA_DSP_LOADER
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
snd-hda-intel-objs := hda_intel.o
|
||||
snd-hda-controller-objs := hda_controller.o
|
||||
snd-hda-tegra-objs := hda_tegra.o
|
||||
# for haswell power well
|
||||
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
|
||||
# when built in kernel
|
||||
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o
|
||||
obj-$(CONFIG_SND_HDA_TEGRA) += snd-hda-tegra.o
|
||||
|
|
|
@ -839,6 +839,43 @@ void snd_hda_apply_fixup(struct hda_codec *codec, int action)
|
|||
}
|
||||
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,
|
||||
const struct hda_model_fixup *models,
|
||||
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")) {
|
||||
codec->fixup_list = NULL;
|
||||
codec->fixup_id = -1;
|
||||
codec->fixup_forced = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (codec->modelname && models) {
|
||||
while (models->name) {
|
||||
if (!strcmp(codec->modelname, models->name)) {
|
||||
id = models->id;
|
||||
name = models->name;
|
||||
break;
|
||||
codec->fixup_id = models->id;
|
||||
codec->fixup_name = models->name;
|
||||
codec->fixup_forced = 1;
|
||||
return;
|
||||
}
|
||||
models++;
|
||||
}
|
||||
|
@ -889,6 +928,7 @@ void snd_hda_pick_fixup(struct hda_codec *codec,
|
|||
}
|
||||
}
|
||||
|
||||
codec->fixup_forced = 0;
|
||||
codec->fixup_id = id;
|
||||
if (id >= 0) {
|
||||
codec->fixup_list = fixlist;
|
||||
|
|
|
@ -402,6 +402,7 @@ struct hda_codec {
|
|||
|
||||
/* fix-up list */
|
||||
int fixup_id;
|
||||
unsigned int fixup_forced:1; /* fixup explicitly set by user */
|
||||
const struct hda_fixup *fixup_list;
|
||||
const char *fixup_name;
|
||||
|
||||
|
|
|
@ -3722,7 +3722,7 @@ static void parse_digital(struct hda_codec *codec)
|
|||
} else {
|
||||
spec->multiout.slave_dig_outs = spec->slave_dig_outs;
|
||||
if (nums >= ARRAY_SIZE(spec->slave_dig_outs) - 1)
|
||||
break;
|
||||
break;
|
||||
spec->slave_dig_outs[nums - 1] = dig_nid;
|
||||
}
|
||||
nums++;
|
||||
|
|
|
@ -1730,7 +1730,7 @@ static void azx_remove(struct pci_dev *pci)
|
|||
}
|
||||
|
||||
/* PCI IDs */
|
||||
static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
|
||||
static const struct pci_device_id azx_ids[] = {
|
||||
/* CPT */
|
||||
{ PCI_DEVICE(0x8086, 0x1c20),
|
||||
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM },
|
||||
|
|
|
@ -407,6 +407,16 @@ struct hda_fixup {
|
|||
} 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 */
|
||||
enum {
|
||||
HDA_FIXUP_INVALID,
|
||||
|
@ -434,6 +444,10 @@ void snd_hda_pick_fixup(struct hda_codec *codec,
|
|||
const struct hda_model_fixup *models,
|
||||
const struct snd_pci_quirk *quirk,
|
||||
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
|
||||
|
|
|
@ -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");
|
|
@ -332,6 +332,7 @@ static const struct hda_fixup ad1986a_fixups[] = {
|
|||
|
||||
static const struct snd_pci_quirk ad1986a_fixup_tbl[] = {
|
||||
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, 0x8200, "ASUS M2", AD1986A_FIXUP_3STACK),
|
||||
SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_FIXUP_3STACK),
|
||||
|
|
|
@ -1127,10 +1127,6 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
|
|||
AMP_OUT_UNMUTE);
|
||||
|
||||
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)
|
||||
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 = 0x10de001b, .name = "GPU 1b 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 = 0x10de0041, .name = "GPU 41 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:10de001b");
|
||||
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:10de0041");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0042");
|
||||
|
|
|
@ -951,7 +951,9 @@ static struct alc_codec_rename_pci_table rename_pci_tbl[] = {
|
|||
{ 0x10ec0280, 0x1028, 0, "ALC3220" },
|
||||
{ 0x10ec0282, 0x1028, 0, "ALC3221" },
|
||||
{ 0x10ec0283, 0x1028, 0, "ALC3223" },
|
||||
{ 0x10ec0288, 0x1028, 0, "ALC3263" },
|
||||
{ 0x10ec0292, 0x1028, 0, "ALC3226" },
|
||||
{ 0x10ec0293, 0x1028, 0, "ALC3235" },
|
||||
{ 0x10ec0255, 0x1028, 0, "ALC3234" },
|
||||
{ 0x10ec0668, 0x1028, 0, "ALC3661" },
|
||||
{ } /* terminator */
|
||||
|
@ -1647,12 +1649,10 @@ static const struct hda_fixup alc260_fixups[] = {
|
|||
[ALC260_FIXUP_COEF] = {
|
||||
.type = HDA_FIXUP_VERBS,
|
||||
.v.verbs = (const struct hda_verb[]) {
|
||||
{ 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
|
||||
{ 0x20, AC_VERB_SET_PROC_COEF, 0x3040 },
|
||||
{ 0x1a, AC_VERB_SET_COEF_INDEX, 0x07 },
|
||||
{ 0x1a, AC_VERB_SET_PROC_COEF, 0x3040 },
|
||||
{ }
|
||||
},
|
||||
.chained = true,
|
||||
.chain_id = ALC260_FIXUP_HP_PIN_0F,
|
||||
},
|
||||
[ALC260_FIXUP_GPIO1] = {
|
||||
.type = HDA_FIXUP_VERBS,
|
||||
|
@ -1667,8 +1667,8 @@ static const struct hda_fixup alc260_fixups[] = {
|
|||
[ALC260_FIXUP_REPLACER] = {
|
||||
.type = HDA_FIXUP_VERBS,
|
||||
.v.verbs = (const struct hda_verb[]) {
|
||||
{ 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
|
||||
{ 0x20, AC_VERB_SET_PROC_COEF, 0x3050 },
|
||||
{ 0x1a, AC_VERB_SET_COEF_INDEX, 0x07 },
|
||||
{ 0x1a, AC_VERB_SET_PROC_COEF, 0x3050 },
|
||||
{ }
|
||||
},
|
||||
.chained = true,
|
||||
|
@ -3522,6 +3522,7 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec)
|
|||
/* Direct Drive HP Amp control */
|
||||
alc_write_coefex_idx(codec, 0x57, 0x03, 0x8aa6);
|
||||
break;
|
||||
case 0x10ec0233:
|
||||
case 0x10ec0283:
|
||||
alc_write_coef_idx(codec, 0x1b, 0x0c0b);
|
||||
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, 0x6b, 0xc429);
|
||||
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:
|
||||
alc_write_coef_idx(codec, 0x15, 0x0d40);
|
||||
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);
|
||||
snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
|
||||
break;
|
||||
case 0x10ec0233:
|
||||
case 0x10ec0283:
|
||||
alc_write_coef_idx(codec, 0x45, 0xc429);
|
||||
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, 0x2e, 0xacf0);
|
||||
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:
|
||||
alc_write_coef_idx(codec, 0x11, 0x0001);
|
||||
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)
|
||||
{
|
||||
int val;
|
||||
|
||||
switch (codec->vendor_id) {
|
||||
case 0x10ec0255:
|
||||
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_coef_idx(codec, 0x49, 0x0049);
|
||||
break;
|
||||
case 0x10ec0233:
|
||||
case 0x10ec0283:
|
||||
alc_write_coef_idx(codec, 0x06, 0x2100);
|
||||
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, 0x18, 0x7308);
|
||||
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:
|
||||
alc_write_coef_idx(codec, 0x11, 0x0041);
|
||||
alc_write_coef_idx(codec, 0x15, 0x0d40);
|
||||
|
@ -3620,6 +3669,8 @@ static void alc_headset_mode_default(struct hda_codec *codec)
|
|||
/* Iphone type */
|
||||
static void alc_headset_mode_ctia(struct hda_codec *codec)
|
||||
{
|
||||
int val;
|
||||
|
||||
switch (codec->vendor_id) {
|
||||
case 0x10ec0255:
|
||||
/* 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_coefex_idx(codec, 0x57, 0x03, 0x8ea6);
|
||||
break;
|
||||
case 0x10ec0233:
|
||||
case 0x10ec0283:
|
||||
alc_write_coef_idx(codec, 0x45, 0xd429);
|
||||
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, 0x18, 0x7388);
|
||||
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:
|
||||
alc_write_coef_idx(codec, 0x11, 0x0001);
|
||||
alc_write_coef_idx(codec, 0x15, 0x0d60);
|
||||
|
@ -3649,6 +3708,8 @@ static void alc_headset_mode_ctia(struct hda_codec *codec)
|
|||
/* Nokia type */
|
||||
static void alc_headset_mode_omtp(struct hda_codec *codec)
|
||||
{
|
||||
int val;
|
||||
|
||||
switch (codec->vendor_id) {
|
||||
case 0x10ec0255:
|
||||
/* 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_coefex_idx(codec, 0x57, 0x03, 0x8ea6);
|
||||
break;
|
||||
case 0x10ec0233:
|
||||
case 0x10ec0283:
|
||||
alc_write_coef_idx(codec, 0x45, 0xe429);
|
||||
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, 0x18, 0x7388);
|
||||
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:
|
||||
alc_write_coef_idx(codec, 0x11, 0x0001);
|
||||
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);
|
||||
is_ctia = (val & 0x0070) == 0x0070;
|
||||
break;
|
||||
case 0x10ec0233:
|
||||
case 0x10ec0283:
|
||||
alc_write_coef_idx(codec, 0x45, 0xd029);
|
||||
msleep(300);
|
||||
|
@ -3703,6 +3773,16 @@ static void alc_determine_headset_type(struct hda_codec *codec)
|
|||
val = alc_read_coef_idx(codec, 0x6c);
|
||||
is_ctia = (val & 0x001c) == 0x001c;
|
||||
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:
|
||||
alc_write_coef_idx(codec, 0x11, 0x0001);
|
||||
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,
|
||||
const struct hda_fixup *fix, int action)
|
||||
{
|
||||
|
@ -4110,6 +4223,7 @@ enum {
|
|||
ALC269_FIXUP_ASUS_G73JW,
|
||||
ALC269_FIXUP_LENOVO_EAPD,
|
||||
ALC275_FIXUP_SONY_HWEQ,
|
||||
ALC275_FIXUP_SONY_DISABLE_AAMIX,
|
||||
ALC271_FIXUP_DMIC,
|
||||
ALC269_FIXUP_PCM_44K,
|
||||
ALC269_FIXUP_STEREO_DMIC,
|
||||
|
@ -4159,6 +4273,8 @@ enum {
|
|||
ALC255_FIXUP_DELL2_MIC_NO_PRESENCE,
|
||||
ALC255_FIXUP_HEADSET_MODE,
|
||||
ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC,
|
||||
ALC293_FIXUP_DELL1_MIC_NO_PRESENCE,
|
||||
ALC292_FIXUP_TPT440_DOCK,
|
||||
};
|
||||
|
||||
static const struct hda_fixup alc269_fixups[] = {
|
||||
|
@ -4213,6 +4329,12 @@ static const struct hda_fixup alc269_fixups[] = {
|
|||
.chained = true,
|
||||
.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] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = alc271_fixup_dmic,
|
||||
|
@ -4552,6 +4674,26 @@ static const struct hda_fixup alc269_fixups[] = {
|
|||
.type = HDA_FIXUP_FUNC,
|
||||
.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[] = {
|
||||
|
@ -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, 0x0608, "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, 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, 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, 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, 0x063e, "Dell", ALC269_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, 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, 0x0669, "Dell", ALC255_FIXUP_DELL2_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, 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, 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, 0x18e6, "HP", ALC269_FIXUP_HP_GPIO_LED),
|
||||
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, 0x907b, "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(0x10cf, 0x1475, "Lifebook", ALC269_FIXUP_LIFEBOOK),
|
||||
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, 0x2203, "Thinkpad X230 Tablet", 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, 0x2214, "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 = ALC283_FIXUP_CHROME_BOOK, .name = "alc283-dac-wcaps"},
|
||||
{.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)
|
||||
{
|
||||
|
@ -4857,6 +5194,7 @@ static int patch_alc269(struct hda_codec *codec)
|
|||
|
||||
snd_hda_pick_fixup(codec, alc269_fixup_models,
|
||||
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);
|
||||
|
||||
alc_auto_parse_customize_define(codec);
|
||||
|
@ -5313,6 +5651,8 @@ enum {
|
|||
ALC662_FIXUP_BASS_1A,
|
||||
ALC662_FIXUP_BASS_CHMAP,
|
||||
ALC668_FIXUP_AUTO_MUTE,
|
||||
ALC668_FIXUP_DELL_DISABLE_AAMIX,
|
||||
ALC668_FIXUP_DELL_XPS13,
|
||||
};
|
||||
|
||||
static const struct hda_fixup alc662_fixups[] = {
|
||||
|
@ -5479,6 +5819,18 @@ static const struct hda_fixup alc662_fixups[] = {
|
|||
.type = HDA_FIXUP_FUNC,
|
||||
.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] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.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(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, 0x060a, "Dell XPS 13", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
|
||||
SND_PCI_QUIRK(0x1028, 0x0623, "Dell", ALC668_FIXUP_AUTO_MUTE),
|
||||
SND_PCI_QUIRK(0x1028, 0x0624, "Dell", ALC668_FIXUP_AUTO_MUTE),
|
||||
SND_PCI_QUIRK(0x1028, 0x060a, "Dell XPS 13", ALC668_FIXUP_DELL_XPS13),
|
||||
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, 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, 0x0698, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
|
||||
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)
|
||||
{
|
||||
int val, coef;
|
||||
|
@ -5686,6 +6101,7 @@ static int patch_alc662(struct hda_codec *codec)
|
|||
|
||||
snd_hda_pick_fixup(codec, alc662_fixup_models,
|
||||
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);
|
||||
|
||||
alc_auto_parse_customize_define(codec);
|
||||
|
|
|
@ -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))) {
|
||||
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) == 2) {
|
||||
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;
|
||||
return 1;
|
||||
}
|
||||
if (sscanf(dev->name, "HP_Mute_LED_%d",
|
||||
if (sscanf(dev->name, "HP_Mute_LED_%u",
|
||||
&spec->gpio_led_polarity) == 1) {
|
||||
set_hp_led_gpio(codec);
|
||||
return 1;
|
||||
|
|
|
@ -151,7 +151,7 @@ static void lola_proc_codec_rw_write(struct snd_info_entry *entry,
|
|||
char line[64];
|
||||
unsigned int id, verb, data, extdata;
|
||||
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;
|
||||
lola_codec_read(chip, id, verb, data, extdata,
|
||||
&chip->debug_res,
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
/* #define RMH_DEBUG 1 */
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
|
@ -429,11 +430,6 @@ int lx_dsp_read_async_events(struct lx6464es *chip, u32 *data)
|
|||
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) \
|
||||
((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;
|
||||
}
|
||||
|
||||
#if 0
|
||||
dev_dbg(chip->card->dev,
|
||||
"CMD_08_ASK_BUFFERS: needed %d, freed %d\n",
|
||||
*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] & MASK_DATA_SIZE);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
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 */
|
||||
#define PCX_IRQ_NONE 0
|
||||
#define IRQCS_ACTIVE_PCIDB 0x00002000L /* Bit nø 13 */
|
||||
#define IRQCS_ENABLE_PCIIRQ 0x00000100L /* Bit nø 08 */
|
||||
#define IRQCS_ENABLE_PCIDB 0x00000200L /* Bit nø 09 */
|
||||
#define IRQCS_ACTIVE_PCIDB BIT(13)
|
||||
#define IRQCS_ENABLE_PCIIRQ BIT(8)
|
||||
#define IRQCS_ENABLE_PCIDB BIT(9)
|
||||
|
||||
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;
|
||||
u32 stat[9]; /* answer from CMD_04_GET_EVENT */
|
||||
|
||||
/* On peut optimiser pour ne pas lire les evenements vides
|
||||
* les mots de réponse sont dans l'ordre suivant :
|
||||
* Stat[0] mot de status général
|
||||
* Stat[1] fin de buffer OUT pF
|
||||
* Stat[2] fin de buffer OUT pf
|
||||
* Stat[3] fin de buffer IN pF
|
||||
* Stat[4] fin de buffer IN pf
|
||||
* Stat[5] underrun poid fort
|
||||
* Stat[6] underrun poid faible
|
||||
* Stat[7] overrun poid fort
|
||||
* Stat[8] overrun poid faible
|
||||
/* We can optimize this to not read dumb events.
|
||||
* Answer words are in the following order:
|
||||
* Stat[0] general status
|
||||
* Stat[1] end of buffer OUT pF
|
||||
* Stat[2] end of buffer OUT pf
|
||||
* Stat[3] end of buffer IN pF
|
||||
* Stat[4] end of buffer IN pf
|
||||
* Stat[5] MSB underrun
|
||||
* Stat[6] LSB underrun
|
||||
* Stat[7] MSB overrun
|
||||
* Stat[8] LSB overrun
|
||||
* */
|
||||
|
||||
u64 orun_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_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)
|
||||
goto exit;
|
||||
|
||||
#if 0
|
||||
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)
|
||||
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)
|
||||
dev_dbg(chip->card->dev, "interrupt: ORUN\n");
|
||||
#endif
|
||||
|
||||
if (async_pending) {
|
||||
u64 notified_in_pipe_mask = 0;
|
||||
|
@ -1238,7 +1226,6 @@ irqreturn_t lx_interrupt(int irq, void *dev_id)
|
|||
}
|
||||
|
||||
if (async_escmd) {
|
||||
#if 0
|
||||
/* backdoor for ethersound commands
|
||||
*
|
||||
* 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");
|
||||
#endif
|
||||
}
|
||||
|
||||
exit:
|
||||
|
|
|
@ -35,7 +35,7 @@ config SND_AT91_SOC_SAM9G20_WM8731
|
|||
|
||||
config SND_ATMEL_SOC_WM8904
|
||||
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_DMA
|
||||
select SND_SOC_WM8904
|
||||
|
|
|
@ -76,12 +76,6 @@ struct atmel_runtime_data {
|
|||
size_t period_size;
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------------*\
|
||||
* 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 = {
|
||||
.ops = &atmel_pcm_ops,
|
||||
.pcm_new = atmel_pcm_new,
|
||||
.pcm_free = atmel_pcm_free,
|
||||
.suspend = atmel_pcm_suspend,
|
||||
.resume = atmel_pcm_resume,
|
||||
};
|
||||
|
||||
int atmel_pcm_pdc_platform_register(struct device *dev)
|
||||
|
|
|
@ -80,17 +80,6 @@ static const struct snd_soc_dapm_route afeb9260_audio_map[] = {
|
|||
{"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 */
|
||||
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",
|
||||
.platform_name = "atmel_pcm-audio",
|
||||
.codec_name = "tlv320aic23-codec.0-001a",
|
||||
.init = afeb9260_tlv320aic23_init,
|
||||
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF |
|
||||
SND_SOC_DAIFMT_CBM_CFM,
|
||||
.ops = &afeb9260_ops,
|
||||
|
|
|
@ -43,6 +43,32 @@ config SND_SOC_BFIN_EVAL_ADAU1373
|
|||
Note: This driver assumes that first ADAU1373 DAI is connected to the
|
||||
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
|
||||
tristate "Support for the EVAL-ADAV80X boards on Blackfin eval boards"
|
||||
depends on SND_BF5XX_I2S && SND_SOC_I2C_AND_SPI
|
||||
|
|
|
@ -22,6 +22,8 @@ snd-ssm2602-objs := bf5xx-ssm2602.o
|
|||
snd-ad73311-objs := bf5xx-ad73311.o
|
||||
snd-ad193x-objs := bf5xx-ad193x.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-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_AD193X) += snd-ad193x.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_ADAV80X) += snd-soc-bfin-eval-adav80x.o
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче