mirror of
https://github.com/raspberrypi/linux.git
synced 2025-12-07 18:40:10 +00:00
ASoC: Add support for Rpi-DAC ASoC: Add prompt for ICS43432 codec Without a prompt string, a config setting can't be included in a defconfig. Give CONFIG_SND_SOC_ICS43432 a prompt so that Pi soundcards can use the driver. Signed-off-by: Phil Elwell <phil@raspberrypi.org> Add IQaudIO Sound Card support for Raspberry Pi Set a limit of 0dB on Digital Volume Control The main volume control in the PCM512x DAC has a range up to +24dB. This is dangerously loud and can potentially cause massive clipping in the output stages. Therefore this sets a sensible limit of 0dB for this control. Allow up to 24dB digital gain to be applied when using IQAudIO DAC+ 24db_digital_gain DT param can be used to specify that PCM512x codec "Digital" volume control should not be limited to 0dB gain, and if specified will allow the full 24dB gain. Modify IQAudIO DAC+ ASoC driver to set card/dai config from dt Add the ability to set the card name, dai name and dai stream name, from dt config. Signed-off-by: DigitalDreamtime <clive.messer@digitaldreamtime.co.uk> IQaudIO: auto-mute for AMP+ and DigiAMP+ IQAudIO amplifier mute via GPIO22. Add dt params for "one-shot" unmute and auto mute. Revision 2, auto mute implementing HiassofT suggestion to mute/unmute using set_bias_level, rather than startup/shutdown.... "By default DAPM waits 5 seconds (pmdown_time) before shutting down playback streams so a close/stop immediately followed by open/start doesn't trigger an amp mute+unmute." Tested on both AMP+ (via DAC+) and DigiAMP+, with both options... dtoverlay=iqaudio-dacplus,unmute_amp "one-shot" unmute when kernel module loads. dtoverlay=iqaudio-dacplus,auto_mute_amp Unmute amp when ALSA device opened by a client. Mute, with 5 second delay when ALSA device closed. (Re-opening the device within the 5 second close window, will cancel mute.) Revision 4, using gpiod. Revision 5, clean-up formatting before adding mute code. - Convert tab plus 4 space formatting to 2x tab - Remove '// NOT USED' commented code Revision 6, don't attempt to "one-shot" unmute amp, unless card is successfully registered. Signed-off-by: DigitalDreamtime <clive.messer@digitaldreamtime.co.uk> ASoC: iqaudio-dac: fix S24_LE format Remove set_bclk_ratio call so 24-bit data is transmitted in 24 bclk cycles. Signed-off-by: Matthias Reichl <hias@horus.com> ASoC: iqaudio-dac: use modern dai_link style Signed-off-by: Matthias Reichl <hias@horus.com> Added support for HiFiBerry DAC+ The driver is based on the HiFiBerry DAC driver. However HiFiBerry DAC+ uses a different codec chip (PCM5122), therefore a new driver is necessary. Add support for the HiFiBerry DAC+ Pro. The HiFiBerry DAC+ and DAC+ Pro products both use the existing bcm sound driver with the DAC+ Pro having a special clock device driver representing the two high precision oscillators. An addition bug fix is included for the PCM512x codec where by the physical size of the sample frame is used in the calculation of the LRCK divisor as it was found to be wrong when using 24-bit depth sample contained in a little endian 4-byte sample frame. Limit PCM512x "Digital" gain to 0dB by default with HiFiBerry DAC+ 24db_digital_gain DT param can be used to specify that PCM512x codec "Digital" volume control should not be limited to 0dB gain, and if specified will allow the full 24dB gain. Add dt param to force HiFiBerry DAC+ Pro into slave mode "dtoverlay=hifiberry-dacplus,slave" Add 'slave' param to use HiFiBerry DAC+ Pro in slave mode, with Pi as master for bit and frame clock. Signed-off-by: DigitalDreamtime <clive.messer@digitaldreamtime.co.uk> Fixed a bug when using 352.8kHz sample rate Signed-off-by: Daniel Matuschek <daniel@hifiberry.com> ASoC: pcm512x: revert downstream changes This partially reverts commit185ea05465which was added by https://github.com/raspberrypi/linux/pull/1152 The downstream pcm512x changes caused a regression, it broke normal use of the 24bit format with the codec, eg when using simple-audio-card. The actual bug with 24bit playback is the incorrect usage of physical_width in various drivers in the downstream tree which causes 24bit data to be transmitted with 32 clock cycles. So it's not the pcm512x that needs fixing, it's the soundcard drivers. Signed-off-by: Matthias Reichl <hias@horus.com> ASoC: hifiberry_dacplus: fix S24_LE format Remove set_bclk_ratio call so 24-bit data is transmitted in 24 bclk cycles. Signed-off-by: Matthias Reichl <hias@horus.com> ASoC: hifiberry_dacplus: transmit S24_LE with 64 BCLK cycles Signed-off-by: Matthias Reichl <hias@horus.com> hifiberry_dacplus: switch to snd_soc_dai_set_bclk_ratio Signed-off-by: Matthias Reichl <hias@horus.com> ASoC: hifiberry_dacplus: use modern dai_link style Signed-off-by: Hui Wang <hui.wang@canonical.com> Add driver for rpi-proto Forward port of 3.10.x driver from https://github.com/koalo We are using a custom board and would like to use rpi 3.18.x kernel. Patch works fine for our embedded system. URL to the audio chip: http://www.mikroe.com/add-on-boards/audio-voice/audio-codec-proto/ Playback tested with devicetree enabled. Signed-off-by: Waldemar Brodkorb <wbrodkorb@conet.de> ASoC: rpi-proto: use modern dai_link style Signed-off-by: Hui Wang <hui.wang@canonical.com> Add Support for JustBoom Audio boards justboom-dac: Adjust for ALSA API change As of 4.4, snd_soc_limit_volume now takes a struct snd_soc_card * rather than a struct snd_soc_codec *. Signed-off-by: Phil Elwell <phil@raspberrypi.org> ASoC: justboom-dac: fix S24_LE format Remove set_bclk_ratio call so 24-bit data is transmitted in 24 bclk cycles. Also remove hw_params as it's no longer needed. Signed-off-by: Matthias Reichl <hias@horus.com> ASoC: justboom-dac: use modern dai_link style Signed-off-by: Matthias Reichl <hias@horus.com> New AudioInjector.net Pi soundcard with low jitter audio in and out. Contains the sound/soc/bcm ALSA machine driver and necessary alterations to the Kconfig and Makefile. Adds the dts overlay and updates the Makefile and README. Updates the relevant defconfig files to enable building for the Raspberry Pi. Thanks to Phil Elwell (pelwell) for the review, simple-card concepts and discussion. Thanks to Clive Messer for overlay naming suggestions. Added support for headphones, microphone and bclk_ratio settings. This patch adds headphone and microphone capability to the Audio Injector sound card. The patch also sets the bit clock ratio for use in the bcm2835-i2s driver. The bcm2835-i2s can't handle an 8 kHz sample rate when the bit clock is at 12 MHz because its register is only 10 bits wide which can't represent the ch2 offset of 1508. For that reason, the rate constraint is added. ASoC: audioinjector-pi-soundcard: use modern dai_link style Signed-off-by: Hui Wang <hui.wang@canonical.com> New driver for RRA DigiDAC1 soundcard using WM8741 + WM8804 ASoC: digidac1-soundcard: use modern dai_link style Signed-off-by: Hui Wang <hui.wang@canonical.com> Add support for Dion Audio LOCO DAC-AMP HAT Using dedicated machine driver and pcm5102a codec driver. Signed-off-by: DigitalDreamtime <clive.messer@digitaldreamtime.co.uk> ASoC: dionaudio_loco: use modern dai_link style Signed-off-by: Hui Wang <hui.wang@canonical.com> Allo Piano DAC boards: Initial 2 channel (stereo) support (#1645) Add initial 2 channel (stereo) support for Allo Piano DAC (2.0/2.1) boards, using allo-piano-dac-pcm512x-audio overlay and allo-piano-dac ALSA ASoC machine driver. NB. The initial support is 2 channel (stereo) ONLY! (The Piano DAC 2.1 will only support 2 channel (stereo) left/right output, pending an update to the upstream pcm512x codec driver, which will have to be submitted via upstream. With the initial downstream support, provided by this patch, the Piano DAC 2.1 subwoofer outputs will not function.) Signed-off-by: Baswaraj K <jaikumar@cem-solutions.net> Signed-off-by: Clive Messer <clive.messer@digitaldreamtime.co.uk> Tested-by: Clive Messer <clive.messer@digitaldreamtime.co.uk> ASoC: allo-piano-dac: fix S24_LE format Remove set_bclk_ratio call so 24-bit data is transmitted in 24 bclk cycles. Also remove hw_params and ops as they are no longer needed. Signed-off-by: Matthias Reichl <hias@horus.com> ASoC: allo-piano-dac: use modern dai_link style Signed-off-by: Hui Wang <hui.wang@canonical.com> Add support for Allo Piano DAC 2.1 plus add-on board for Raspberry Pi. The Piano DAC 2.1 has support for 4 channels with subwoofer. Signed-off-by: Baswaraj K <jaikumar@cem-solutions.net> Reviewed-by: Vijay Kumar B. <vijaykumar@zilogic.com> Reviewed-by: Raashid Muhammed <raashidmuhammed@zilogic.com> Add clock changes and mute gpios (#1938) Also improve code style and adhere to ALSA coding conventions. Signed-off-by: Baswaraj K <jaikumar@cem-solutions.net> Reviewed-by: Vijay Kumar B. <vijaykumar@zilogic.com> Reviewed-by: Raashid Muhammed <raashidmuhammed@zilogic.com> PianoPlus: Dual Mono & Dual Stereo features added (#2069) allo-piano-dac-plus: Master volume added + fixes Master volume added, which controls both DACs volumes. See: https://github.com/raspberrypi/linux/pull/2149 Also fix initial max volume, default mode value, and unmute. Signed-off-by: allocom <sparky-dev@allo.com> ASoC: allo-piano-dac-plus: fix S24_LE format Remove set_bclk_ratio call so 24-bit data is transmitted in 24 bclk cycles. Signed-off-by: Matthias Reichl <hias@horus.com> sound: bcm: Fix memset dereference warning This warning appears with GCC 6.4.0 from toolchains.bootlin.com: ../sound/soc/bcm/allo-piano-dac-plus.c: In function ‘snd_allo_piano_dac_init’: ../sound/soc/bcm/allo-piano-dac-plus.c:711:30: warning: argument to ‘sizeof’ in ‘memset’ call is the same expression as the destination; did you mean to dereference it? [-Wsizeof-pointer-memaccess] memset(glb_ptr, 0x00, sizeof(glb_ptr)); ^ Suggested-by: Phil Elwell <phil@raspberrypi.org> Signed-off-by: Nathan Chancellor <natechancellor@gmail.com> ASoC: allo-piano-dac-plus: use modern dai_link style Signed-off-by: Hui Wang <hui.wang@canonical.com> Add support for Allo Boss DAC add-on board for Raspberry Pi. (#1924) Signed-off-by: Baswaraj K <jaikumar@cem-solutions.net> Reviewed-by: Deepak <deepak@zilogic.com> Reviewed-by: BabuSubashChandar <babusubashchandar@zilogic.com> Add support for new clock rate and mute gpios. Signed-off-by: Baswaraj K <jaikumar@cem-solutions.net> Reviewed-by: Deepak <deepak@zilogic.com> Reviewed-by: BabuSubashChandar <babusubashchandar@zilogic.com> ASoC: allo-boss-dac: fix S24_LE format Remove set_bclk_ratio call so 24-bit data is transmitted in 24 bclk cycles. Signed-off-by: Matthias Reichl <hias@horus.com> ASoC: allo-boss-dac: transmit S24_LE with 64 BCLK cycles Signed-off-by: Matthias Reichl <hias@horus.com> allo-boss-dac: switch to snd_soc_dai_set_bclk_ratio Signed-off-by: Matthias Reichl <hias@horus.com> ASoC: allo-boss-dac: use modern dai_link style Signed-off-by: Hui Wang <hui.wang@canonical.com> Support for Blokas Labs pisound board Pisound dynamic overlay (#1760) Restructuring pisound-overlay.dts, so it can be loaded and unloaded dynamically using dtoverlay. Print a logline when the kernel module is removed. pisound improvements: * Added a writable sysfs object to enable scripts / user space software to blink MIDI activity LEDs for variable duration. * Improved hw_param constraints setting. * Added compatibility with S16_LE sample format. * Exposed some simple placeholder volume controls, so the card appears in volumealsa widget. Add missing SND_PISOUND selects dependency to SND_RAWMIDI Without it the Pisound module fails to compile. See https://github.com/raspberrypi/linux/issues/2366 Updates for Pisound module code: * Merged 'Fix a warning in DEBUG builds' (1c8b82b). * Updating some strings and copyright information. * Fix for handling high load of MIDI input and output. * Use dual rate oversampling ratio for 96kHz instead of single rate one. Signed-off-by: Giedrius Trainavicius <giedrius@blokas.io> Fixing memset call in pisound.c Signed-off-by: Giedrius Trainavicius <giedrius@blokas.io> Fix for Pisound's MIDI Input getting blocked for a while in rare cases. There was a possible race condition which could lead to Input's FIFO queue to be underflown, causing high amount of processing in the worker thread for some period of time. Signed-off-by: Giedrius Trainavicius <giedrius@blokas.io> Fix for Pisound kernel module in Real Time kernel configuration. When handler of data_available interrupt is fired, queue_work ends up getting called and it can block on a spin lock which is not allowed in interrupt context. The fix was to run the handler from a thread context instead. Pisound: Remove spinlock usage around spi_sync ASoC: pisound: use modern dai_link style Signed-off-by: Hui Wang <hui.wang@canonical.com> ASoC: pisound: fix the parameter for spi_device_match Signed-off-by: Hui Wang <hui.wang@canonical.com> ASoC: Add driver for Cirrus Logic Audio Card Note: due to problems with deferred probing of regulators the following softdep should be added to a modprobe.d file softdep arizona-spi pre: arizona-ldo1 Signed-off-by: Matthias Reichl <hias@horus.com> ASoC: rpi-cirrus: use modern dai_link style Signed-off-by: Matthias Reichl <hias@horus.com> sound: Support for Dion Audio LOCO-V2 DAC-AMP HAT Signed-off-by: Miquel Blauw <info@dionaudio.nl> ASoC: dionaudio_loco-v2: fix S24_LE format Remove set_bclk_ratio call so 24-bit data is transmitted in 24 bclk cycles. Also remove hw_params and ops as they are no longer needed. Signed-off-by: Matthias Reichl <hias@horus.com> ASoC: dionaudio_loco-v2: use modern dai_link style Signed-off-by: Hui Wang <hui.wang@canonical.com> Add support for Fe-Pi audio sound card. (#1867) Fe-Pi Audio Sound Card is based on NXP SGTL5000 codec. Mechanical specification of the board is the same the Raspberry Pi Zero. 3.5mm jacks for Headphone/Mic, Line In, and Line Out. Signed-off-by: Henry Kupis <fe-pi@cox.net> ASoC: fe-pi-audio: use modern dai_link style Signed-off-by: Hui Wang <hui.wang@canonical.com> Add support for the AudioInjector.net Octo sound card AudioInjector Octo: sample rates, regulators, reset This patch adds new sample rates to the Audioinjector Octo sound card. The new supported rates are (in kHz) : 96, 48, 32, 24, 16, 8, 88.2, 44.1, 29.4, 22.05, 14.7 Reference the bcm270x DT regulators in the overlay. This patch adds a reset GPIO for the AudioInjector.net octo sound card. Audioinjector octo : Make the playback and capture symmetric This patch ensures that the sample rate and channel count of the audioinjector octo sound card are symmetric. audioinjector-octo: Add continuous clock feature By user request, add a switch to prevent the clocks being stopped when the stream is paused, stopped or shutdown. Provide access to the switch by adding a 'non-stop-clocks' parameter to the audioinjector-addons overlay. See: https://github.com/raspberrypi/linux/issues/2409 Signed-off-by: Phil Elwell <phil@raspberrypi.org> sound: Fixes for audioinjector-octo under 4.19 1. Move the DT alias declaration to the I2C shim in the cases where the shim is enabled. This works around a problem caused by a 4.19 commit [1] that generates DT/OF uevents for I2C drivers. 2. Fix the diagnostics in an error path of the soundcard driver to correctly identify the reason for the failure to load. 3. Move the declaration of the clock node in the overlay outside the I2C node to avoid warnings. 4. Sort the overlay nodes so that dependencies are only to earlier fragments, in an attempt to get runtime dtoverlay application to work (it still doesn't...) See: https://github.com/Audio-Injector/Octo/issues/14 Signed-off-by: Phil Elwell <phil@raspberrypi.org> [1]af503716ac("i2c: core: report OF style module alias for devices registered via OF") ASoC: audioinjector-octo-soundcard: use modern dai_link style Signed-off-by: Hui Wang <hui.wang@canonical.com> Driver support for Google voiceHAT soundcard. ASoC: googlevoicehat-codec: Use correct device when grabbing GPIO The fixup for the VoiceHAT in 4.18 incorrectly tried to find the sdmode GPIO pin under the card device, not the codec device. This failed, and therefore caused the device probe to fail. Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org> ASoC: googlevoicehat-codec: Reformat for kernel coding standards Fix all whitespace, indentation, and bracing errors. Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org> ASoC: googlevoicehat-codec: Make driver function structure const Make voicehat_component_driver a const structure. Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org> ASoC: googlevoicehat-codec: Only convert from ms to jiffies once Minor optimisation and allows to become checkpatch clean. A msec value is read out of DT or from a define, and convert once to jiffies, rather than every time that it is used. Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org> Driver and overlay for Allo Katana DAC Allo Katana DAC: Updated default values Signed-off-by: Jaikumar <jaikumar@cem-solutions.com> Added mute stream func Signed-off-by: Jaikumar <jaikumar@cem-solutions.net> codecs: Correct Katana minimum volume Update Katana minimum volume to get the exact 0.5 dB value in each step. Signed-off-by: Sudeep Kumar <sudeepkumar@cem-solutions.net> ASoC: Add generic RPI driver for simple soundcards. The RPI simple sound card driver provides a generic ALSA SOC card driver supporting a variety of Pi HAT soundcards. The intention is to avoid the duplication of code for cards that can't be fully supported by the soc simple/graph cards but are otherwise almost identical. This initial commit adds support for the ADAU1977 ADC, Google VoiceHat, HifiBerry AMP, HifiBerry DAC and RPI DAC. Signed-off-by: Tim Gover <tim.gover@raspberrypi.org> ASoC: Use correct card name in rpi-simple driver Use the specific card name from drvdata instead of the snd_rpi_simple rpi-simple-soundcard: Use nicer driver name "RPi-simple" Rename the driver from "RPI simple soundcard" to "RPi-simple" so that the driver name won't be mangled allowing to be used unaltered as the card conf filename. ASoC: rpi-simple-soundcard: use modern dai_link style Signed-off-by: Hui Wang <hui.wang@canonical.com> ASoC: Add Kconfig and Makefile for sound/soc/bcm Signed-off-by: popcornmix <popcornmix@gmail.com> ASoC: Create a generic Pi Hat WM8804 driver Reduce the amount of duplicated code by creating a generic driver for Pi Hat digi cards using the WM8804 codec. This replaces the Allo DigiOne, Hifiberry Digi/Pro, JustBoom Digi and IQAudIO Digi dedicate soundcard drivers with a generic driver. There are no significant changes to the runtime behavior of the drivers and end users should not have to change any configuration settings after upgrading. Minor changes * Check the return value of snd_soc_component_update_bits * Added some pr_debug tracing * Various checkpatch tidyups * Updated allodigi-one to use use 128FS at > 96 Khz. This appears to be an omission in the original driver code so followed the Hifiberry DAC driver approach. ASoC: rpi-wm8804-soundcard: use modern dai_link style Signed-off-by: Matthias Reichl <hias@horus.com> rpi-wm8804-soundcard: drop PWRDN register writes Since kernel 4.0 the PWRDN register bits are under DAPM control from the wm8804 driver. Drop code that modifies that register to avoid interfering with DAPM. Signed-off-by: Matthias Reichl <hias@horus.com> rpi-wm8804-soundcard: configure wm8804 clocks only on rate change This should avoid clicks when stopping and immediately afterwards starting a stream with the same samplerate as before. Signed-off-by: Matthias Reichl <hias@horus.com> rpi-wm8804-soundcard: Fixed MCLKDIV for Allo Digione The Allo Digione board wants a fixed MCLKDIV of 256. See: https://github.com/raspberrypi/linux/issues/3296 Signed-off-by: Phil Elwell <phil@raspberrypi.org> ASoC: Add support for AudioSense-Pi add-on soundcard AudioSense-Pi is a RPi HAT based on a TI's TLV320AIC32x4 stereo codec This hardware provides multiple audio I/O capabilities to the RPi. The codec connects to the RPi's SoC through the I2S Bus. The following devices can be connected through a 3.5mm jack 1. Line-In: Plain old audio in from mobile phones, PCs, etc., 2. Mic-In: Connect a microphone 3. Line-Out: Connect the output to a speaker 4. Headphones: Connect a Headphone w or w/o microphones Multiple Inputs: It supports the following combinations 1. Two stereo Line-Inputs and a microphone 2. One stereo Line-Input and two microphones 3. Two stereo Line-Inputs, a microphone and one mono line-input (with h/w hack) 4. One stereo Line-Input, two microphones and one mono line-input (with h/w hack) Multiple Outputs: Audio output can be routed to the headphones or speakers (with additional hardware) Signed-off-by: b-ak <anur.bhargav@gmail.com> ASoC: audiosense-pi: use modern dai_link style Signed-off-by: Hui Wang <hui.wang@canonical.com> Added driver for the HiFiBerry DAC+ ADC (#2694) Signed-off-by: Daniel Matuschek <daniel@hifiberry.com> hifiberry_dacplusadc: switch to snd_soc_dai_set_bclk_ratio Signed-off-by: Matthias Reichl <hias@horus.com> ASoC: hifiberry_dacplusadc: fix DAI link setup The driver only defines a single DAI link and the code that tries to setup the second (non-existent) DAI link looks wrong - using dmic as a CPU/platform driver doesn't make any sense. The DT overlay doesn't define a dmic property, so the code was never executed (otherwise it would have resulted in a memory corruption). So drop the offending code to prevent issues if a dmic property should be added to the DT overlay. Signed-off-by: Matthias Reichl <hias@horus.com> ASoC: hifiberry_dacplusadc: use modern dai_link style Signed-off-by: Matthias Reichl <hias@horus.com> Audiophonics I-Sabre 9038Q2M DAC driver Signed-off-by: Audiophonics <contact@audiophonics.fr> ASoC: i-sabre-q2m: use modern dai_link style Signed-off-by: Hui Wang <hui.wang@canonical.com> Added IQaudIO Pi-Codec board support (#2969) Add support for the IQaudIO Pi-Codec board. Signed-off-by: Gordon <gordon@iqaudio.com> Fixed 48k timing issue ASoC: iqaudio-codec: use modern dai_link style Signed-off-by: Hui Wang <hui.wang@canonical.com> adds the Hifiberry DAC+ADC PRO version This adds the driver for the DAC+ADC PRO version of the Hifiberry soundcard with software controlled PCM1863 ADC Signed-off-by: Joerg Schambacher joerg@i2audio.com Add Hifiberry DAC+DSP soundcard driver (#3224) Adds the driver for the Hifiberry DAC+DSP. It supports capture and playback depending on the DSP firmware. Signed-off-by: Joerg Schambacher <joerg@i2audio.com> Allow simultaneous use of JustBoom DAC and Digi Signed-off-by: Johannes Krude <johannes@krude.de> Pisound: MIDI communication fixes for scaled down CPU. * Increased maximum SPI communication speed to avoid running too slow when the CPU is scaled down and losing MIDI data. * Keep track of buffer usage in millibytes for higher precision. Signed-off-by: Giedrius Trainavičius <giedrius@blokas.io> sound: Add the HiFiBerry DAC+HD version This adds the driver for the DAC+HD version supporting HiFiBerry's PCM179x based DACs. It also adds PLL control for clock generation. Signed-off-by: Joerg Schambacher <joerg@i2audio.com> Fix master mode settings of HiFiBerry DAC+ADC PRO card (#3424) This patch fixes the board DAI setting when in master-mode. Wrong setting could have caused random pop noise. Signed-off-by: Joerg Schambacher <joerg@i2audio.com> adds LED OFF feature to HiFiBerry DAC+ADC PRO sound card This adds a DT overlay parameter 'leds_off' which allows to switch off the onboard activity LEDs at all times which has been requested by some users. Signed-off-by: Joerg Schambacher <joerg@i2audio.com> adds LED OFF feature to HiFiBerry DAC+ADC sound card This adds a DT overlay parameter 'leds_off' which allows to switch off the onboard activity LEDs at all times which has been requested by some users. Signed-off-by: Joerg Schambacher <joerg@i2audio.com> adds LED OFF feature to HiFiBerry DAC+/DAC+PRO sound cards This adds a DT overlay parameter 'leds_off' which allows to switch off the onboard activity LEDs at all times which has been requested by some users. Signed-off-by: Joerg Schambacher <joerg@i2audio.com> pisound: Added reading Pisound board hardware revision and exposing it (#3425) pisound: Added reading Pisound board hardware revision and exposing it in kernel log and sysfs file: /sys/kernel/pisound/hw_version Signed-off-by: Giedrius <giedrius@blokas.io> Added driver for HiFiBerry Amp amplifier add-on board The driver contains a low-level hardware driver for the TAS5713 and the drivers for the Raspberry Pi I2S subsystem. TAS5713: return error if initialisation fails Existing TAS5713 driver logs errors during initialisation, but does not return an error code. Therefore even if initialisation fails, the driver will still be loaded, but won't work. This patch fixes this. I2C communication error will now reported correctly by a non-zero return code. HiFiBerry Amp: fix device-tree problems Some code to load the driver based on device-tree-overlays was missing. This is added by this patch. According to 5713 pdf doc CLOCK_CTRL is a readonly status register, and it behaves so. Remove useless setting sound: pcm512x-codec: Adding 352.8kHz samplerate support sound/soc: only first codec is master in multicodec setup When using multiple codecs, at most one codec should generate the master clock. All codecs except the first are therefore configured for slave mode. Signed-off-by: Johannes Krude <johannes@krude.de> ASoC: Fix snd_soc_get_pcm_runtime usage Commit [1] changed the snd_soc_get_pcm_runtime to take a dai_link pointer instead of a string. Patch up the downstream drivers to use the modified API. Signed-off-by: Phil Elwell <phil@raspberrypi.com> [1]4468189ff3("ASoC: soc-core: find rtd via dai_link pointer at snd_soc_get_pcm_runtime()") Add support for the AudioInjector.net Isolated sound card This patch adds support for the Audio Injector Isolated sound card. Signed-off-by: Matt Flax <flatmax@flatmax.org> Add support for merus-amp soundcard and ma120x0p codec Add 96KHz rate support to MA120X0P codec and make enable and mute gpio pins optional. Signed-off-by: AMuszkat <ariel.muszkat@gmail.com> Fixes a problem with clock settings of HiFiBerry DAC+ADC PRO (#3545) This patch fixes a problem of the re-calculation of i2s-clock and -parameter settings when only the ADC is activated. Signed-off-by: Joerg Schambacher <joerg@i2audio.com> configs: Enable the AD193x codecs See: https://github.com/raspberrypi/linux/issues/2850 Signed-off-by: Phil Elwell <phil@raspberrypi.org> Switch to snd_soc_dai_set_bclk_ratio Replaces obsolete function snd_soc_dai_set_tdm_slot Signed-off-by: Joerg Schambacher <joerg@i2audio.com> Enhances the DAC+ driver to control the optional headphone amplifier Probes on the I2C bus for TPA6130A2, if successful, it sets DT-parameter 'status' from 'disabled' to 'okay' using change_sets to enable the headphone control. Signed-off-by: Joerg Schambacher joerg@i2audio.com Update Allo Piano Dac Driver Add unique names to the individual dac coded drivers Remove some of the codec controls that are not used. Signed-off-by: Paul Hermann <paul@picoreplayer.org> Fixes an onboard clock detection problem of the PRO versions Increasing the sleep time after clock selection to 3-4ms allows the correct detection of all combinations of DAC+ Pro and DAC+ADC Pro sound cards and the various PI revisions. Signed-off-by: Joerg Schambacher <joerg@hifiberry.com> ASoC:ma120x0p: Increase maximum sample rate to 192KHz Change the maximum sample rate for the amplifier to 192KHz as given in the Infineon specification. Signed-off-by: Joerg Schambacher <joerg@hifiberry.com> ASoC: ma120x0p: Remove unnecessary const specifier Clang warns: sound/soc/codecs/ma120x0p.c:891:14: warning: duplicate 'const' declaration specifier [-Wduplicate-decl-specifier] static const SOC_VALUE_ENUM_SINGLE_DECL(pwr_mode_ctrl, ^ ./include/sound/soc.h:362:2: note: expanded from macro 'SOC_VALUE_ENUM_SINGLE_DECL' SOC_VALUE_ENUM_DOUBLE_DECL(name, xreg, xshift, xshift, xmask, xtexts, xvalues) ^ ./include/sound/soc.h:359:2: note: expanded from macro 'SOC_VALUE_ENUM_DOUBLE_DECL' const struct soc_enum name = SOC_VALUE_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, \ ^ 1 warning generated. SOC_VALUE_ENUM_DOUBLE_DECL already has a const specifier. Remove the duplicate const to clean up the warning. Fixes:42444979e7("Add support for all the downstream rpi sound card drivers") Signed-off-by: Nathan Chancellor <nathan@kernel.org> ASoC: bcm: allo-piano-dac-plus: Remove unnecessary const specifiers Clang warns: sound/soc/bcm/allo-piano-dac-plus.c:66:14: warning: duplicate 'const' declaration specifier [-Wduplicate-decl-specifier] static const SOC_ENUM_SINGLE_DECL(allo_piano_mode_enum, ^ ./include/sound/soc.h:355:2: note: expanded from macro 'SOC_ENUM_SINGLE_DECL' SOC_ENUM_DOUBLE_DECL(name, xreg, xshift, xshift, xtexts) ^ ./include/sound/soc.h:352:2: note: expanded from macro 'SOC_ENUM_DOUBLE_DECL' const struct soc_enum name = SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, \ ^ sound/soc/bcm/allo-piano-dac-plus.c:75:14: warning: duplicate 'const' declaration specifier [-Wduplicate-decl-specifier] static const SOC_ENUM_SINGLE_DECL(allo_piano_dual_mode_enum, ^ ./include/sound/soc.h:355:2: note: expanded from macro 'SOC_ENUM_SINGLE_DECL' SOC_ENUM_DOUBLE_DECL(name, xreg, xshift, xshift, xtexts) ^ ./include/sound/soc.h:352:2: note: expanded from macro 'SOC_ENUM_DOUBLE_DECL' const struct soc_enum name = SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, \ ^ sound/soc/bcm/allo-piano-dac-plus.c:96:14: warning: duplicate 'const' declaration specifier [-Wduplicate-decl-specifier] static const SOC_ENUM_SINGLE_DECL(allo_piano_enum, ^ ./include/sound/soc.h:355:2: note: expanded from macro 'SOC_ENUM_SINGLE_DECL' SOC_ENUM_DOUBLE_DECL(name, xreg, xshift, xshift, xtexts) ^ ./include/sound/soc.h:352:2: note: expanded from macro 'SOC_ENUM_DOUBLE_DECL' const struct soc_enum name = SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, \ ^ 3 warnings generated. SOC_VALUE_ENUM_DOUBLE_DECL already has a const specifier. Remove the duplicate const specifiers to clean up the warnings. Fixes:42444979e7("Add support for all the downstream rpi sound card drivers") Signed-off-by: Nathan Chancellor <nathan@kernel.org> rpi-simple-soundcard: Add Dion Audio KIWI streamer Signed-off-by: Miquel Blauw <miquelblauw@hotmail.com> rpi-simple-soundcard: adds definitions for the HiFiBerry AMP3 card Uses Infineon MA120x0 amplifier and supports full sample rate of 192ksps. Signed-off-by: Joerg Schambacher <joerg@hifiberry.com> sound: soc: bcm: Added Sound card driver for Dacberry400 Audio card for Raspberry Pi 400 Added Sound card driver for DACberry400 Audio card. Signed-off-by: Ashish Vara <ashishhvara@gmail.com> ASoC:ma120x0p: Corrects the volume level display Fixes the wrongly changed 'limiter volume' display back to -50dB minimum and sets the correct minimum volume level to -144dB to be aligned with the controls and display in alsamixer etc. Signed-off-by: Joerg Schambacher <joerg@hifiberry.com> ASoC: bcm: Fix Rpi-PROTO and audioinjector.net Pi As of kernel 5.19 the WM8731 driver has separate I2C and SPI support modules. Change the Kconfig definitions for the audioinjector.net Pi and Rpi-PROTO soundcards to select SND_SOC_WM8731_I2C. See: https://github.com/raspberrypi/linux/issues/5364 Signed-off-by: Phil Elwell <phil@raspberrypi.com> ASoC: adau1977: Add correct compatible strings Signed-off-by: Phil Elwell <phil@raspberrypi.com> ASoC: bcm2835-i2s: Use phys addresses for DAI DMA Contrary to what struct snd_dmaengine_dai_dma_data suggests, the configuration of addresses of DMA slave interfaces should be done in CPU physical addresses. Signed-off-by: Phil Elwell <phil@raspberrypi.com> rpi sound cards: Fix Codec Zero rate switching The Raspberry Pi Codec Zero (and IQaudIO Codec) don't notify the DA7213 codec when it needs to change PLL frequencies. As a result, audio can be played at the wrong rate - play a 48kHz sound immediately after a 44.1kHz sound to see the effect, but in some configurations the codec can lock into the wrong state and always get some rates wrong. Add the necessary notification to fix the issue. Signed-off-by: Phil Elwell <phil@raspberrypi.com>
2266 lines
71 KiB
C
2266 lines
71 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
*/
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/usb.h>
|
|
#include <linux/usb/audio.h>
|
|
#include <linux/usb/midi.h>
|
|
#include <linux/bits.h>
|
|
|
|
#include <sound/control.h>
|
|
#include <sound/core.h>
|
|
#include <sound/info.h>
|
|
#include <sound/pcm.h>
|
|
|
|
#include "usbaudio.h"
|
|
#include "card.h"
|
|
#include "mixer.h"
|
|
#include "mixer_quirks.h"
|
|
#include "midi.h"
|
|
#include "midi2.h"
|
|
#include "quirks.h"
|
|
#include "helper.h"
|
|
#include "endpoint.h"
|
|
#include "pcm.h"
|
|
#include "clock.h"
|
|
#include "stream.h"
|
|
|
|
/*
|
|
* handle the quirks for the contained interfaces
|
|
*/
|
|
static int create_composite_quirk(struct snd_usb_audio *chip,
|
|
struct usb_interface *iface,
|
|
struct usb_driver *driver,
|
|
const struct snd_usb_audio_quirk *quirk_comp)
|
|
{
|
|
int probed_ifnum = get_iface_desc(iface->altsetting)->bInterfaceNumber;
|
|
const struct snd_usb_audio_quirk *quirk;
|
|
int err;
|
|
|
|
for (quirk = quirk_comp->data; quirk->ifnum >= 0; ++quirk) {
|
|
iface = usb_ifnum_to_if(chip->dev, quirk->ifnum);
|
|
if (!iface)
|
|
continue;
|
|
if (quirk->ifnum != probed_ifnum &&
|
|
usb_interface_claimed(iface))
|
|
continue;
|
|
err = snd_usb_create_quirk(chip, iface, driver, quirk);
|
|
if (err < 0)
|
|
return err;
|
|
}
|
|
|
|
for (quirk = quirk_comp->data; quirk->ifnum >= 0; ++quirk) {
|
|
iface = usb_ifnum_to_if(chip->dev, quirk->ifnum);
|
|
if (!iface)
|
|
continue;
|
|
if (quirk->ifnum != probed_ifnum &&
|
|
!usb_interface_claimed(iface)) {
|
|
err = usb_driver_claim_interface(driver, iface,
|
|
USB_AUDIO_IFACE_UNUSED);
|
|
if (err < 0)
|
|
return err;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ignore_interface_quirk(struct snd_usb_audio *chip,
|
|
struct usb_interface *iface,
|
|
struct usb_driver *driver,
|
|
const struct snd_usb_audio_quirk *quirk)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int create_any_midi_quirk(struct snd_usb_audio *chip,
|
|
struct usb_interface *intf,
|
|
struct usb_driver *driver,
|
|
const struct snd_usb_audio_quirk *quirk)
|
|
{
|
|
return snd_usb_midi_v2_create(chip, intf, quirk, 0);
|
|
}
|
|
|
|
/*
|
|
* create a stream for an interface with proper descriptors
|
|
*/
|
|
static int create_standard_audio_quirk(struct snd_usb_audio *chip,
|
|
struct usb_interface *iface,
|
|
struct usb_driver *driver,
|
|
const struct snd_usb_audio_quirk *quirk)
|
|
{
|
|
struct usb_host_interface *alts;
|
|
struct usb_interface_descriptor *altsd;
|
|
int err;
|
|
|
|
alts = &iface->altsetting[0];
|
|
altsd = get_iface_desc(alts);
|
|
err = snd_usb_parse_audio_interface(chip, altsd->bInterfaceNumber);
|
|
if (err < 0) {
|
|
usb_audio_err(chip, "cannot setup if %d: error %d\n",
|
|
altsd->bInterfaceNumber, err);
|
|
return err;
|
|
}
|
|
/* reset the current interface */
|
|
usb_set_interface(chip->dev, altsd->bInterfaceNumber, 0);
|
|
return 0;
|
|
}
|
|
|
|
/* create the audio stream and the corresponding endpoints from the fixed
|
|
* audioformat object; this is used for quirks with the fixed EPs
|
|
*/
|
|
static int add_audio_stream_from_fixed_fmt(struct snd_usb_audio *chip,
|
|
struct audioformat *fp)
|
|
{
|
|
int stream, err;
|
|
|
|
stream = (fp->endpoint & USB_DIR_IN) ?
|
|
SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
|
|
|
|
snd_usb_audioformat_set_sync_ep(chip, fp);
|
|
|
|
err = snd_usb_add_audio_stream(chip, stream, fp);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
err = snd_usb_add_endpoint(chip, fp->endpoint,
|
|
SND_USB_ENDPOINT_TYPE_DATA);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
if (fp->sync_ep) {
|
|
err = snd_usb_add_endpoint(chip, fp->sync_ep,
|
|
fp->implicit_fb ?
|
|
SND_USB_ENDPOINT_TYPE_DATA :
|
|
SND_USB_ENDPOINT_TYPE_SYNC);
|
|
if (err < 0)
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* create a stream for an endpoint/altsetting without proper descriptors
|
|
*/
|
|
static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
|
|
struct usb_interface *iface,
|
|
struct usb_driver *driver,
|
|
const struct snd_usb_audio_quirk *quirk)
|
|
{
|
|
struct audioformat *fp;
|
|
struct usb_host_interface *alts;
|
|
struct usb_interface_descriptor *altsd;
|
|
unsigned *rate_table = NULL;
|
|
int err;
|
|
|
|
fp = kmemdup(quirk->data, sizeof(*fp), GFP_KERNEL);
|
|
if (!fp)
|
|
return -ENOMEM;
|
|
|
|
INIT_LIST_HEAD(&fp->list);
|
|
if (fp->nr_rates > MAX_NR_RATES) {
|
|
kfree(fp);
|
|
return -EINVAL;
|
|
}
|
|
if (fp->nr_rates > 0) {
|
|
rate_table = kmemdup(fp->rate_table,
|
|
sizeof(int) * fp->nr_rates, GFP_KERNEL);
|
|
if (!rate_table) {
|
|
kfree(fp);
|
|
return -ENOMEM;
|
|
}
|
|
fp->rate_table = rate_table;
|
|
}
|
|
|
|
if (fp->iface != get_iface_desc(&iface->altsetting[0])->bInterfaceNumber ||
|
|
fp->altset_idx >= iface->num_altsetting) {
|
|
err = -EINVAL;
|
|
goto error;
|
|
}
|
|
alts = &iface->altsetting[fp->altset_idx];
|
|
altsd = get_iface_desc(alts);
|
|
if (altsd->bNumEndpoints <= fp->ep_idx) {
|
|
err = -EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
fp->protocol = altsd->bInterfaceProtocol;
|
|
|
|
if (fp->datainterval == 0)
|
|
fp->datainterval = snd_usb_parse_datainterval(chip, alts);
|
|
if (fp->maxpacksize == 0)
|
|
fp->maxpacksize = le16_to_cpu(get_endpoint(alts, fp->ep_idx)->wMaxPacketSize);
|
|
if (!fp->fmt_type)
|
|
fp->fmt_type = UAC_FORMAT_TYPE_I;
|
|
|
|
err = add_audio_stream_from_fixed_fmt(chip, fp);
|
|
if (err < 0)
|
|
goto error;
|
|
|
|
usb_set_interface(chip->dev, fp->iface, 0);
|
|
snd_usb_init_pitch(chip, fp);
|
|
snd_usb_init_sample_rate(chip, fp, fp->rate_max);
|
|
return 0;
|
|
|
|
error:
|
|
list_del(&fp->list); /* unlink for avoiding double-free */
|
|
kfree(fp);
|
|
kfree(rate_table);
|
|
return err;
|
|
}
|
|
|
|
static int create_auto_pcm_quirk(struct snd_usb_audio *chip,
|
|
struct usb_interface *iface,
|
|
struct usb_driver *driver)
|
|
{
|
|
struct usb_host_interface *alts;
|
|
struct usb_interface_descriptor *altsd;
|
|
struct usb_endpoint_descriptor *epd;
|
|
struct uac1_as_header_descriptor *ashd;
|
|
struct uac_format_type_i_discrete_descriptor *fmtd;
|
|
|
|
/*
|
|
* Most Roland/Yamaha audio streaming interfaces have more or less
|
|
* standard descriptors, but older devices might lack descriptors, and
|
|
* future ones might change, so ensure that we fail silently if the
|
|
* interface doesn't look exactly right.
|
|
*/
|
|
|
|
/* must have a non-zero altsetting for streaming */
|
|
if (iface->num_altsetting < 2)
|
|
return -ENODEV;
|
|
alts = &iface->altsetting[1];
|
|
altsd = get_iface_desc(alts);
|
|
|
|
/* must have an isochronous endpoint for streaming */
|
|
if (altsd->bNumEndpoints < 1)
|
|
return -ENODEV;
|
|
epd = get_endpoint(alts, 0);
|
|
if (!usb_endpoint_xfer_isoc(epd))
|
|
return -ENODEV;
|
|
|
|
/* must have format descriptors */
|
|
ashd = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL,
|
|
UAC_AS_GENERAL);
|
|
fmtd = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL,
|
|
UAC_FORMAT_TYPE);
|
|
if (!ashd || ashd->bLength < 7 ||
|
|
!fmtd || fmtd->bLength < 8)
|
|
return -ENODEV;
|
|
|
|
return create_standard_audio_quirk(chip, iface, driver, NULL);
|
|
}
|
|
|
|
static int create_yamaha_midi_quirk(struct snd_usb_audio *chip,
|
|
struct usb_interface *iface,
|
|
struct usb_driver *driver,
|
|
struct usb_host_interface *alts)
|
|
{
|
|
static const struct snd_usb_audio_quirk yamaha_midi_quirk = {
|
|
.type = QUIRK_MIDI_YAMAHA
|
|
};
|
|
struct usb_midi_in_jack_descriptor *injd;
|
|
struct usb_midi_out_jack_descriptor *outjd;
|
|
|
|
/* must have some valid jack descriptors */
|
|
injd = snd_usb_find_csint_desc(alts->extra, alts->extralen,
|
|
NULL, USB_MS_MIDI_IN_JACK);
|
|
outjd = snd_usb_find_csint_desc(alts->extra, alts->extralen,
|
|
NULL, USB_MS_MIDI_OUT_JACK);
|
|
if (!injd && !outjd)
|
|
return -ENODEV;
|
|
if ((injd && !snd_usb_validate_midi_desc(injd)) ||
|
|
(outjd && !snd_usb_validate_midi_desc(outjd)))
|
|
return -ENODEV;
|
|
if (injd && (injd->bLength < 5 ||
|
|
(injd->bJackType != USB_MS_EMBEDDED &&
|
|
injd->bJackType != USB_MS_EXTERNAL)))
|
|
return -ENODEV;
|
|
if (outjd && (outjd->bLength < 6 ||
|
|
(outjd->bJackType != USB_MS_EMBEDDED &&
|
|
outjd->bJackType != USB_MS_EXTERNAL)))
|
|
return -ENODEV;
|
|
return create_any_midi_quirk(chip, iface, driver, &yamaha_midi_quirk);
|
|
}
|
|
|
|
static int create_roland_midi_quirk(struct snd_usb_audio *chip,
|
|
struct usb_interface *iface,
|
|
struct usb_driver *driver,
|
|
struct usb_host_interface *alts)
|
|
{
|
|
static const struct snd_usb_audio_quirk roland_midi_quirk = {
|
|
.type = QUIRK_MIDI_ROLAND
|
|
};
|
|
u8 *roland_desc = NULL;
|
|
|
|
/* might have a vendor-specific descriptor <06 24 F1 02 ...> */
|
|
for (;;) {
|
|
roland_desc = snd_usb_find_csint_desc(alts->extra,
|
|
alts->extralen,
|
|
roland_desc, 0xf1);
|
|
if (!roland_desc)
|
|
return -ENODEV;
|
|
if (roland_desc[0] < 6 || roland_desc[3] != 2)
|
|
continue;
|
|
return create_any_midi_quirk(chip, iface, driver,
|
|
&roland_midi_quirk);
|
|
}
|
|
}
|
|
|
|
static int create_std_midi_quirk(struct snd_usb_audio *chip,
|
|
struct usb_interface *iface,
|
|
struct usb_driver *driver,
|
|
struct usb_host_interface *alts)
|
|
{
|
|
struct usb_ms_header_descriptor *mshd;
|
|
struct usb_ms_endpoint_descriptor *msepd;
|
|
|
|
/* must have the MIDIStreaming interface header descriptor*/
|
|
mshd = (struct usb_ms_header_descriptor *)alts->extra;
|
|
if (alts->extralen < 7 ||
|
|
mshd->bLength < 7 ||
|
|
mshd->bDescriptorType != USB_DT_CS_INTERFACE ||
|
|
mshd->bDescriptorSubtype != USB_MS_HEADER)
|
|
return -ENODEV;
|
|
/* must have the MIDIStreaming endpoint descriptor*/
|
|
msepd = (struct usb_ms_endpoint_descriptor *)alts->endpoint[0].extra;
|
|
if (alts->endpoint[0].extralen < 4 ||
|
|
msepd->bLength < 4 ||
|
|
msepd->bDescriptorType != USB_DT_CS_ENDPOINT ||
|
|
msepd->bDescriptorSubtype != UAC_MS_GENERAL ||
|
|
msepd->bNumEmbMIDIJack < 1 ||
|
|
msepd->bNumEmbMIDIJack > 16)
|
|
return -ENODEV;
|
|
|
|
return create_any_midi_quirk(chip, iface, driver, NULL);
|
|
}
|
|
|
|
static int create_auto_midi_quirk(struct snd_usb_audio *chip,
|
|
struct usb_interface *iface,
|
|
struct usb_driver *driver)
|
|
{
|
|
struct usb_host_interface *alts;
|
|
struct usb_interface_descriptor *altsd;
|
|
struct usb_endpoint_descriptor *epd;
|
|
int err;
|
|
|
|
alts = &iface->altsetting[0];
|
|
altsd = get_iface_desc(alts);
|
|
|
|
/* must have at least one bulk/interrupt endpoint for streaming */
|
|
if (altsd->bNumEndpoints < 1)
|
|
return -ENODEV;
|
|
epd = get_endpoint(alts, 0);
|
|
if (!usb_endpoint_xfer_bulk(epd) &&
|
|
!usb_endpoint_xfer_int(epd))
|
|
return -ENODEV;
|
|
|
|
switch (USB_ID_VENDOR(chip->usb_id)) {
|
|
case 0x0499: /* Yamaha */
|
|
err = create_yamaha_midi_quirk(chip, iface, driver, alts);
|
|
if (err != -ENODEV)
|
|
return err;
|
|
break;
|
|
case 0x0582: /* Roland */
|
|
err = create_roland_midi_quirk(chip, iface, driver, alts);
|
|
if (err != -ENODEV)
|
|
return err;
|
|
break;
|
|
}
|
|
|
|
return create_std_midi_quirk(chip, iface, driver, alts);
|
|
}
|
|
|
|
static int create_autodetect_quirk(struct snd_usb_audio *chip,
|
|
struct usb_interface *iface,
|
|
struct usb_driver *driver,
|
|
const struct snd_usb_audio_quirk *quirk)
|
|
{
|
|
int err;
|
|
|
|
err = create_auto_pcm_quirk(chip, iface, driver);
|
|
if (err == -ENODEV)
|
|
err = create_auto_midi_quirk(chip, iface, driver);
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* Create a stream for an Edirol UA-700/UA-25/UA-4FX interface.
|
|
* The only way to detect the sample rate is by looking at wMaxPacketSize.
|
|
*/
|
|
static int create_uaxx_quirk(struct snd_usb_audio *chip,
|
|
struct usb_interface *iface,
|
|
struct usb_driver *driver,
|
|
const struct snd_usb_audio_quirk *quirk)
|
|
{
|
|
static const struct audioformat ua_format = {
|
|
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
|
|
.channels = 2,
|
|
.fmt_type = UAC_FORMAT_TYPE_I,
|
|
.altsetting = 1,
|
|
.altset_idx = 1,
|
|
.rates = SNDRV_PCM_RATE_CONTINUOUS,
|
|
};
|
|
struct usb_host_interface *alts;
|
|
struct usb_interface_descriptor *altsd;
|
|
struct audioformat *fp;
|
|
int err;
|
|
|
|
/* both PCM and MIDI interfaces have 2 or more altsettings */
|
|
if (iface->num_altsetting < 2)
|
|
return -ENXIO;
|
|
alts = &iface->altsetting[1];
|
|
altsd = get_iface_desc(alts);
|
|
|
|
if (altsd->bNumEndpoints == 2) {
|
|
static const struct snd_usb_midi_endpoint_info ua700_ep = {
|
|
.out_cables = 0x0003,
|
|
.in_cables = 0x0003
|
|
};
|
|
static const struct snd_usb_audio_quirk ua700_quirk = {
|
|
.type = QUIRK_MIDI_FIXED_ENDPOINT,
|
|
.data = &ua700_ep
|
|
};
|
|
static const struct snd_usb_midi_endpoint_info uaxx_ep = {
|
|
.out_cables = 0x0001,
|
|
.in_cables = 0x0001
|
|
};
|
|
static const struct snd_usb_audio_quirk uaxx_quirk = {
|
|
.type = QUIRK_MIDI_FIXED_ENDPOINT,
|
|
.data = &uaxx_ep
|
|
};
|
|
const struct snd_usb_audio_quirk *quirk =
|
|
chip->usb_id == USB_ID(0x0582, 0x002b)
|
|
? &ua700_quirk : &uaxx_quirk;
|
|
return __snd_usbmidi_create(chip->card, iface,
|
|
&chip->midi_list, quirk,
|
|
chip->usb_id,
|
|
&chip->num_rawmidis);
|
|
}
|
|
|
|
if (altsd->bNumEndpoints != 1)
|
|
return -ENXIO;
|
|
|
|
fp = kmemdup(&ua_format, sizeof(*fp), GFP_KERNEL);
|
|
if (!fp)
|
|
return -ENOMEM;
|
|
|
|
fp->iface = altsd->bInterfaceNumber;
|
|
fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress;
|
|
fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
|
|
fp->datainterval = 0;
|
|
fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
|
|
INIT_LIST_HEAD(&fp->list);
|
|
|
|
switch (fp->maxpacksize) {
|
|
case 0x120:
|
|
fp->rate_max = fp->rate_min = 44100;
|
|
break;
|
|
case 0x138:
|
|
case 0x140:
|
|
fp->rate_max = fp->rate_min = 48000;
|
|
break;
|
|
case 0x258:
|
|
case 0x260:
|
|
fp->rate_max = fp->rate_min = 96000;
|
|
break;
|
|
default:
|
|
usb_audio_err(chip, "unknown sample rate\n");
|
|
kfree(fp);
|
|
return -ENXIO;
|
|
}
|
|
|
|
err = add_audio_stream_from_fixed_fmt(chip, fp);
|
|
if (err < 0) {
|
|
list_del(&fp->list); /* unlink for avoiding double-free */
|
|
kfree(fp);
|
|
return err;
|
|
}
|
|
usb_set_interface(chip->dev, fp->iface, 0);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Create a standard mixer for the specified interface.
|
|
*/
|
|
static int create_standard_mixer_quirk(struct snd_usb_audio *chip,
|
|
struct usb_interface *iface,
|
|
struct usb_driver *driver,
|
|
const struct snd_usb_audio_quirk *quirk)
|
|
{
|
|
if (quirk->ifnum < 0)
|
|
return 0;
|
|
|
|
return snd_usb_create_mixer(chip, quirk->ifnum);
|
|
}
|
|
|
|
/*
|
|
* audio-interface quirks
|
|
*
|
|
* returns zero if no standard audio/MIDI parsing is needed.
|
|
* returns a positive value if standard audio/midi interfaces are parsed
|
|
* after this.
|
|
* returns a negative value at error.
|
|
*/
|
|
int snd_usb_create_quirk(struct snd_usb_audio *chip,
|
|
struct usb_interface *iface,
|
|
struct usb_driver *driver,
|
|
const struct snd_usb_audio_quirk *quirk)
|
|
{
|
|
typedef int (*quirk_func_t)(struct snd_usb_audio *,
|
|
struct usb_interface *,
|
|
struct usb_driver *,
|
|
const struct snd_usb_audio_quirk *);
|
|
static const quirk_func_t quirk_funcs[] = {
|
|
[QUIRK_IGNORE_INTERFACE] = ignore_interface_quirk,
|
|
[QUIRK_COMPOSITE] = create_composite_quirk,
|
|
[QUIRK_AUTODETECT] = create_autodetect_quirk,
|
|
[QUIRK_MIDI_STANDARD_INTERFACE] = create_any_midi_quirk,
|
|
[QUIRK_MIDI_FIXED_ENDPOINT] = create_any_midi_quirk,
|
|
[QUIRK_MIDI_YAMAHA] = create_any_midi_quirk,
|
|
[QUIRK_MIDI_ROLAND] = create_any_midi_quirk,
|
|
[QUIRK_MIDI_MIDIMAN] = create_any_midi_quirk,
|
|
[QUIRK_MIDI_NOVATION] = create_any_midi_quirk,
|
|
[QUIRK_MIDI_RAW_BYTES] = create_any_midi_quirk,
|
|
[QUIRK_MIDI_EMAGIC] = create_any_midi_quirk,
|
|
[QUIRK_MIDI_CME] = create_any_midi_quirk,
|
|
[QUIRK_MIDI_AKAI] = create_any_midi_quirk,
|
|
[QUIRK_MIDI_FTDI] = create_any_midi_quirk,
|
|
[QUIRK_MIDI_CH345] = create_any_midi_quirk,
|
|
[QUIRK_AUDIO_STANDARD_INTERFACE] = create_standard_audio_quirk,
|
|
[QUIRK_AUDIO_FIXED_ENDPOINT] = create_fixed_stream_quirk,
|
|
[QUIRK_AUDIO_EDIROL_UAXX] = create_uaxx_quirk,
|
|
[QUIRK_AUDIO_STANDARD_MIXER] = create_standard_mixer_quirk,
|
|
};
|
|
|
|
if (quirk->type < QUIRK_TYPE_COUNT) {
|
|
return quirk_funcs[quirk->type](chip, iface, driver, quirk);
|
|
} else {
|
|
usb_audio_err(chip, "invalid quirk type %d\n", quirk->type);
|
|
return -ENXIO;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* boot quirks
|
|
*/
|
|
|
|
#define EXTIGY_FIRMWARE_SIZE_OLD 794
|
|
#define EXTIGY_FIRMWARE_SIZE_NEW 483
|
|
|
|
static int snd_usb_extigy_boot_quirk(struct usb_device *dev, struct usb_interface *intf)
|
|
{
|
|
struct usb_host_config *config = dev->actconfig;
|
|
int err;
|
|
|
|
if (le16_to_cpu(get_cfg_desc(config)->wTotalLength) == EXTIGY_FIRMWARE_SIZE_OLD ||
|
|
le16_to_cpu(get_cfg_desc(config)->wTotalLength) == EXTIGY_FIRMWARE_SIZE_NEW) {
|
|
dev_dbg(&dev->dev, "sending Extigy boot sequence...\n");
|
|
/* Send message to force it to reconnect with full interface. */
|
|
err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev,0),
|
|
0x10, 0x43, 0x0001, 0x000a, NULL, 0);
|
|
if (err < 0)
|
|
dev_dbg(&dev->dev, "error sending boot message: %d\n", err);
|
|
err = usb_get_descriptor(dev, USB_DT_DEVICE, 0,
|
|
&dev->descriptor, sizeof(dev->descriptor));
|
|
config = dev->actconfig;
|
|
if (err < 0)
|
|
dev_dbg(&dev->dev, "error usb_get_descriptor: %d\n", err);
|
|
err = usb_reset_configuration(dev);
|
|
if (err < 0)
|
|
dev_dbg(&dev->dev, "error usb_reset_configuration: %d\n", err);
|
|
dev_dbg(&dev->dev, "extigy_boot: new boot length = %d\n",
|
|
le16_to_cpu(get_cfg_desc(config)->wTotalLength));
|
|
return -ENODEV; /* quit this anyway */
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int snd_usb_audigy2nx_boot_quirk(struct usb_device *dev)
|
|
{
|
|
u8 buf = 1;
|
|
|
|
snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), 0x2a,
|
|
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER,
|
|
0, 0, &buf, 1);
|
|
if (buf == 0) {
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), 0x29,
|
|
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
|
|
1, 2000, NULL, 0);
|
|
return -ENODEV;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int snd_usb_fasttrackpro_boot_quirk(struct usb_device *dev)
|
|
{
|
|
int err;
|
|
|
|
if (dev->actconfig->desc.bConfigurationValue == 1) {
|
|
dev_info(&dev->dev,
|
|
"Fast Track Pro switching to config #2\n");
|
|
/* This function has to be available by the usb core module.
|
|
* if it is not avialable the boot quirk has to be left out
|
|
* and the configuration has to be set by udev or hotplug
|
|
* rules
|
|
*/
|
|
err = usb_driver_set_configuration(dev, 2);
|
|
if (err < 0)
|
|
dev_dbg(&dev->dev,
|
|
"error usb_driver_set_configuration: %d\n",
|
|
err);
|
|
/* Always return an error, so that we stop creating a device
|
|
that will just be destroyed and recreated with a new
|
|
configuration */
|
|
return -ENODEV;
|
|
} else
|
|
dev_info(&dev->dev, "Fast Track Pro config OK\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* C-Media CM106/CM106+ have four 16-bit internal registers that are nicely
|
|
* documented in the device's data sheet.
|
|
*/
|
|
static int snd_usb_cm106_write_int_reg(struct usb_device *dev, int reg, u16 value)
|
|
{
|
|
u8 buf[4];
|
|
buf[0] = 0x20;
|
|
buf[1] = value & 0xff;
|
|
buf[2] = (value >> 8) & 0xff;
|
|
buf[3] = reg;
|
|
return snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_CONFIGURATION,
|
|
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT,
|
|
0, 0, &buf, 4);
|
|
}
|
|
|
|
static int snd_usb_cm106_boot_quirk(struct usb_device *dev)
|
|
{
|
|
/*
|
|
* Enable line-out driver mode, set headphone source to front
|
|
* channels, enable stereo mic.
|
|
*/
|
|
return snd_usb_cm106_write_int_reg(dev, 2, 0x8004);
|
|
}
|
|
|
|
/*
|
|
* CM6206 registers from the CM6206 datasheet rev 2.1
|
|
*/
|
|
#define CM6206_REG0_DMA_MASTER BIT(15)
|
|
#define CM6206_REG0_SPDIFO_RATE_48K (2 << 12)
|
|
#define CM6206_REG0_SPDIFO_RATE_96K (7 << 12)
|
|
/* Bit 4 thru 11 is the S/PDIF category code */
|
|
#define CM6206_REG0_SPDIFO_CAT_CODE_GENERAL (0 << 4)
|
|
#define CM6206_REG0_SPDIFO_EMPHASIS_CD BIT(3)
|
|
#define CM6206_REG0_SPDIFO_COPYRIGHT_NA BIT(2)
|
|
#define CM6206_REG0_SPDIFO_NON_AUDIO BIT(1)
|
|
#define CM6206_REG0_SPDIFO_PRO_FORMAT BIT(0)
|
|
|
|
#define CM6206_REG1_TEST_SEL_CLK BIT(14)
|
|
#define CM6206_REG1_PLLBIN_EN BIT(13)
|
|
#define CM6206_REG1_SOFT_MUTE_EN BIT(12)
|
|
#define CM6206_REG1_GPIO4_OUT BIT(11)
|
|
#define CM6206_REG1_GPIO4_OE BIT(10)
|
|
#define CM6206_REG1_GPIO3_OUT BIT(9)
|
|
#define CM6206_REG1_GPIO3_OE BIT(8)
|
|
#define CM6206_REG1_GPIO2_OUT BIT(7)
|
|
#define CM6206_REG1_GPIO2_OE BIT(6)
|
|
#define CM6206_REG1_GPIO1_OUT BIT(5)
|
|
#define CM6206_REG1_GPIO1_OE BIT(4)
|
|
#define CM6206_REG1_SPDIFO_INVALID BIT(3)
|
|
#define CM6206_REG1_SPDIF_LOOP_EN BIT(2)
|
|
#define CM6206_REG1_SPDIFO_DIS BIT(1)
|
|
#define CM6206_REG1_SPDIFI_MIX BIT(0)
|
|
|
|
#define CM6206_REG2_DRIVER_ON BIT(15)
|
|
#define CM6206_REG2_HEADP_SEL_SIDE_CHANNELS (0 << 13)
|
|
#define CM6206_REG2_HEADP_SEL_SURROUND_CHANNELS (1 << 13)
|
|
#define CM6206_REG2_HEADP_SEL_CENTER_SUBW (2 << 13)
|
|
#define CM6206_REG2_HEADP_SEL_FRONT_CHANNELS (3 << 13)
|
|
#define CM6206_REG2_MUTE_HEADPHONE_RIGHT BIT(12)
|
|
#define CM6206_REG2_MUTE_HEADPHONE_LEFT BIT(11)
|
|
#define CM6206_REG2_MUTE_REAR_SURROUND_RIGHT BIT(10)
|
|
#define CM6206_REG2_MUTE_REAR_SURROUND_LEFT BIT(9)
|
|
#define CM6206_REG2_MUTE_SIDE_SURROUND_RIGHT BIT(8)
|
|
#define CM6206_REG2_MUTE_SIDE_SURROUND_LEFT BIT(7)
|
|
#define CM6206_REG2_MUTE_SUBWOOFER BIT(6)
|
|
#define CM6206_REG2_MUTE_CENTER BIT(5)
|
|
#define CM6206_REG2_MUTE_RIGHT_FRONT BIT(3)
|
|
#define CM6206_REG2_MUTE_LEFT_FRONT BIT(3)
|
|
#define CM6206_REG2_EN_BTL BIT(2)
|
|
#define CM6206_REG2_MCUCLKSEL_1_5_MHZ (0)
|
|
#define CM6206_REG2_MCUCLKSEL_3_MHZ (1)
|
|
#define CM6206_REG2_MCUCLKSEL_6_MHZ (2)
|
|
#define CM6206_REG2_MCUCLKSEL_12_MHZ (3)
|
|
|
|
/* Bit 11..13 sets the sensitivity to FLY tuner volume control VP/VD signal */
|
|
#define CM6206_REG3_FLYSPEED_DEFAULT (2 << 11)
|
|
#define CM6206_REG3_VRAP25EN BIT(10)
|
|
#define CM6206_REG3_MSEL1 BIT(9)
|
|
#define CM6206_REG3_SPDIFI_RATE_44_1K BIT(0 << 7)
|
|
#define CM6206_REG3_SPDIFI_RATE_48K BIT(2 << 7)
|
|
#define CM6206_REG3_SPDIFI_RATE_32K BIT(3 << 7)
|
|
#define CM6206_REG3_PINSEL BIT(6)
|
|
#define CM6206_REG3_FOE BIT(5)
|
|
#define CM6206_REG3_ROE BIT(4)
|
|
#define CM6206_REG3_CBOE BIT(3)
|
|
#define CM6206_REG3_LOSE BIT(2)
|
|
#define CM6206_REG3_HPOE BIT(1)
|
|
#define CM6206_REG3_SPDIFI_CANREC BIT(0)
|
|
|
|
#define CM6206_REG5_DA_RSTN BIT(13)
|
|
#define CM6206_REG5_AD_RSTN BIT(12)
|
|
#define CM6206_REG5_SPDIFO_AD2SPDO BIT(12)
|
|
#define CM6206_REG5_SPDIFO_SEL_FRONT (0 << 9)
|
|
#define CM6206_REG5_SPDIFO_SEL_SIDE_SUR (1 << 9)
|
|
#define CM6206_REG5_SPDIFO_SEL_CEN_LFE (2 << 9)
|
|
#define CM6206_REG5_SPDIFO_SEL_REAR_SUR (3 << 9)
|
|
#define CM6206_REG5_CODECM BIT(8)
|
|
#define CM6206_REG5_EN_HPF BIT(7)
|
|
#define CM6206_REG5_T_SEL_DSDA4 BIT(6)
|
|
#define CM6206_REG5_T_SEL_DSDA3 BIT(5)
|
|
#define CM6206_REG5_T_SEL_DSDA2 BIT(4)
|
|
#define CM6206_REG5_T_SEL_DSDA1 BIT(3)
|
|
#define CM6206_REG5_T_SEL_DSDAD_NORMAL 0
|
|
#define CM6206_REG5_T_SEL_DSDAD_FRONT 4
|
|
#define CM6206_REG5_T_SEL_DSDAD_S_SURROUND 5
|
|
#define CM6206_REG5_T_SEL_DSDAD_CEN_LFE 6
|
|
#define CM6206_REG5_T_SEL_DSDAD_R_SURROUND 7
|
|
|
|
static int snd_usb_cm6206_boot_quirk(struct usb_device *dev)
|
|
{
|
|
int err = 0, reg;
|
|
int val[] = {
|
|
/*
|
|
* Values here are chosen based on sniffing USB traffic
|
|
* under Windows.
|
|
*
|
|
* REG0: DAC is master, sample rate 48kHz, no copyright
|
|
*/
|
|
CM6206_REG0_SPDIFO_RATE_48K |
|
|
CM6206_REG0_SPDIFO_COPYRIGHT_NA,
|
|
/*
|
|
* REG1: PLL binary search enable, soft mute enable.
|
|
*/
|
|
CM6206_REG1_PLLBIN_EN |
|
|
CM6206_REG1_SOFT_MUTE_EN,
|
|
/*
|
|
* REG2: enable output drivers,
|
|
* select front channels to the headphone output,
|
|
* then mute the headphone channels, run the MCU
|
|
* at 1.5 MHz.
|
|
*/
|
|
CM6206_REG2_DRIVER_ON |
|
|
CM6206_REG2_HEADP_SEL_FRONT_CHANNELS |
|
|
CM6206_REG2_MUTE_HEADPHONE_RIGHT |
|
|
CM6206_REG2_MUTE_HEADPHONE_LEFT,
|
|
/*
|
|
* REG3: default flyspeed, set 2.5V mic bias
|
|
* enable all line out ports and enable SPDIF
|
|
*/
|
|
CM6206_REG3_FLYSPEED_DEFAULT |
|
|
CM6206_REG3_VRAP25EN |
|
|
CM6206_REG3_FOE |
|
|
CM6206_REG3_ROE |
|
|
CM6206_REG3_CBOE |
|
|
CM6206_REG3_LOSE |
|
|
CM6206_REG3_HPOE |
|
|
CM6206_REG3_SPDIFI_CANREC,
|
|
/* REG4 is just a bunch of GPIO lines */
|
|
0x0000,
|
|
/* REG5: de-assert AD/DA reset signals */
|
|
CM6206_REG5_DA_RSTN |
|
|
CM6206_REG5_AD_RSTN };
|
|
|
|
for (reg = 0; reg < ARRAY_SIZE(val); reg++) {
|
|
err = snd_usb_cm106_write_int_reg(dev, reg, val[reg]);
|
|
if (err < 0)
|
|
return err;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
/* quirk for Plantronics GameCom 780 with CM6302 chip */
|
|
static int snd_usb_gamecon780_boot_quirk(struct usb_device *dev)
|
|
{
|
|
/* set the initial volume and don't change; other values are either
|
|
* too loud or silent due to firmware bug (bko#65251)
|
|
*/
|
|
u8 buf[2] = { 0x74, 0xe3 };
|
|
return snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
|
|
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
|
|
UAC_FU_VOLUME << 8, 9 << 8, buf, 2);
|
|
}
|
|
|
|
/*
|
|
* Novation Twitch DJ controller
|
|
* Focusrite Novation Saffire 6 USB audio card
|
|
*/
|
|
static int snd_usb_novation_boot_quirk(struct usb_device *dev)
|
|
{
|
|
/* preemptively set up the device because otherwise the
|
|
* raw MIDI endpoints are not active */
|
|
usb_set_interface(dev, 0, 1);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* This call will put the synth in "USB send" mode, i.e it will send MIDI
|
|
* messages through USB (this is disabled at startup). The synth will
|
|
* acknowledge by sending a sysex on endpoint 0x85 and by displaying a USB
|
|
* sign on its LCD. Values here are chosen based on sniffing USB traffic
|
|
* under Windows.
|
|
*/
|
|
static int snd_usb_accessmusic_boot_quirk(struct usb_device *dev)
|
|
{
|
|
int err, actual_length;
|
|
/* "midi send" enable */
|
|
static const u8 seq[] = { 0x4e, 0x73, 0x52, 0x01 };
|
|
void *buf;
|
|
|
|
if (usb_pipe_type_check(dev, usb_sndintpipe(dev, 0x05)))
|
|
return -EINVAL;
|
|
buf = kmemdup(seq, ARRAY_SIZE(seq), GFP_KERNEL);
|
|
if (!buf)
|
|
return -ENOMEM;
|
|
err = usb_interrupt_msg(dev, usb_sndintpipe(dev, 0x05), buf,
|
|
ARRAY_SIZE(seq), &actual_length, 1000);
|
|
kfree(buf);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Some sound cards from Native Instruments are in fact compliant to the USB
|
|
* audio standard of version 2 and other approved USB standards, even though
|
|
* they come up as vendor-specific device when first connected.
|
|
*
|
|
* However, they can be told to come up with a new set of descriptors
|
|
* upon their next enumeration, and the interfaces announced by the new
|
|
* descriptors will then be handled by the kernel's class drivers. As the
|
|
* product ID will also change, no further checks are required.
|
|
*/
|
|
|
|
static int snd_usb_nativeinstruments_boot_quirk(struct usb_device *dev)
|
|
{
|
|
int ret;
|
|
|
|
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
0xaf, USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
|
1, 0, NULL, 0, 1000);
|
|
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
usb_reset_device(dev);
|
|
|
|
/* return -EAGAIN, so the creation of an audio interface for this
|
|
* temporary device is aborted. The device will reconnect with a
|
|
* new product ID */
|
|
return -EAGAIN;
|
|
}
|
|
|
|
static void mbox2_setup_48_24_magic(struct usb_device *dev)
|
|
{
|
|
u8 srate[3];
|
|
u8 temp[12];
|
|
|
|
/* Choose 48000Hz permanently */
|
|
srate[0] = 0x80;
|
|
srate[1] = 0xbb;
|
|
srate[2] = 0x00;
|
|
|
|
/* Send the magic! */
|
|
snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
|
|
0x01, 0x22, 0x0100, 0x0085, &temp, 0x0003);
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
0x81, 0xa2, 0x0100, 0x0085, &srate, 0x0003);
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
0x81, 0xa2, 0x0100, 0x0086, &srate, 0x0003);
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
0x81, 0xa2, 0x0100, 0x0003, &srate, 0x0003);
|
|
return;
|
|
}
|
|
|
|
/* Digidesign Mbox 2 needs to load firmware onboard
|
|
* and driver must wait a few seconds for initialisation.
|
|
*/
|
|
|
|
#define MBOX2_FIRMWARE_SIZE 646
|
|
#define MBOX2_BOOT_LOADING 0x01 /* Hard coded into the device */
|
|
#define MBOX2_BOOT_READY 0x02 /* Hard coded into the device */
|
|
|
|
static int snd_usb_mbox2_boot_quirk(struct usb_device *dev)
|
|
{
|
|
struct usb_host_config *config = dev->actconfig;
|
|
int err;
|
|
u8 bootresponse[0x12];
|
|
int fwsize;
|
|
int count;
|
|
|
|
fwsize = le16_to_cpu(get_cfg_desc(config)->wTotalLength);
|
|
|
|
if (fwsize != MBOX2_FIRMWARE_SIZE) {
|
|
dev_err(&dev->dev, "Invalid firmware size=%d.\n", fwsize);
|
|
return -ENODEV;
|
|
}
|
|
|
|
dev_dbg(&dev->dev, "Sending Digidesign Mbox 2 boot sequence...\n");
|
|
|
|
count = 0;
|
|
bootresponse[0] = MBOX2_BOOT_LOADING;
|
|
while ((bootresponse[0] == MBOX2_BOOT_LOADING) && (count < 10)) {
|
|
msleep(500); /* 0.5 second delay */
|
|
snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
|
|
/* Control magic - load onboard firmware */
|
|
0x85, 0xc0, 0x0001, 0x0000, &bootresponse, 0x0012);
|
|
if (bootresponse[0] == MBOX2_BOOT_READY)
|
|
break;
|
|
dev_dbg(&dev->dev, "device not ready, resending boot sequence...\n");
|
|
count++;
|
|
}
|
|
|
|
if (bootresponse[0] != MBOX2_BOOT_READY) {
|
|
dev_err(&dev->dev, "Unknown bootresponse=%d, or timed out, ignoring device.\n", bootresponse[0]);
|
|
return -ENODEV;
|
|
}
|
|
|
|
dev_dbg(&dev->dev, "device initialised!\n");
|
|
|
|
err = usb_get_descriptor(dev, USB_DT_DEVICE, 0,
|
|
&dev->descriptor, sizeof(dev->descriptor));
|
|
config = dev->actconfig;
|
|
if (err < 0)
|
|
dev_dbg(&dev->dev, "error usb_get_descriptor: %d\n", err);
|
|
|
|
err = usb_reset_configuration(dev);
|
|
if (err < 0)
|
|
dev_dbg(&dev->dev, "error usb_reset_configuration: %d\n", err);
|
|
dev_dbg(&dev->dev, "mbox2_boot: new boot length = %d\n",
|
|
le16_to_cpu(get_cfg_desc(config)->wTotalLength));
|
|
|
|
mbox2_setup_48_24_magic(dev);
|
|
|
|
dev_info(&dev->dev, "Digidesign Mbox 2: 24bit 48kHz");
|
|
|
|
return 0; /* Successful boot */
|
|
}
|
|
|
|
static int snd_usb_axefx3_boot_quirk(struct usb_device *dev)
|
|
{
|
|
int err;
|
|
|
|
dev_dbg(&dev->dev, "Waiting for Axe-Fx III to boot up...\n");
|
|
|
|
/* If the Axe-Fx III has not fully booted, it will timeout when trying
|
|
* to enable the audio streaming interface. A more generous timeout is
|
|
* used here to detect when the Axe-Fx III has finished booting as the
|
|
* set interface message will be acked once it has
|
|
*/
|
|
err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
USB_REQ_SET_INTERFACE, USB_RECIP_INTERFACE,
|
|
1, 1, NULL, 0, 120000);
|
|
if (err < 0) {
|
|
dev_err(&dev->dev,
|
|
"failed waiting for Axe-Fx III to boot: %d\n", err);
|
|
return err;
|
|
}
|
|
|
|
dev_dbg(&dev->dev, "Axe-Fx III is now ready\n");
|
|
|
|
err = usb_set_interface(dev, 1, 0);
|
|
if (err < 0)
|
|
dev_dbg(&dev->dev,
|
|
"error stopping Axe-Fx III interface: %d\n", err);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void mbox3_setup_48_24_magic(struct usb_device *dev)
|
|
{
|
|
/* The Mbox 3 is "little endian" */
|
|
/* max volume is: 0x0000. */
|
|
/* min volume is: 0x0080 (shown in little endian form) */
|
|
|
|
|
|
/* Load 48000Hz rate into buffer */
|
|
u8 com_buff[4] = {0x80, 0xbb, 0x00, 0x00};
|
|
|
|
/* Set 48000Hz sample rate */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
0x01, 0x21, 0x0100, 0x0001, &com_buff, 4); //Is this really needed?
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
0x01, 0x21, 0x0100, 0x8101, &com_buff, 4);
|
|
|
|
/* Deactivate Tuner */
|
|
/* on = 0x01*/
|
|
/* off = 0x00*/
|
|
com_buff[0] = 0x00;
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
0x01, 0x21, 0x0003, 0x2001, &com_buff, 1);
|
|
|
|
/* Set clock source to Internal (as opposed to S/PDIF) */
|
|
com_buff[0] = 0x01;
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0100, 0x8001, &com_buff, 1);
|
|
|
|
/* Mute the hardware loopbacks to start the device in a known state. */
|
|
com_buff[0] = 0x00;
|
|
com_buff[1] = 0x80;
|
|
/* Analogue input 1 left channel: */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0110, 0x4001, &com_buff, 2);
|
|
/* Analogue input 1 right channel: */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0111, 0x4001, &com_buff, 2);
|
|
/* Analogue input 2 left channel: */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0114, 0x4001, &com_buff, 2);
|
|
/* Analogue input 2 right channel: */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0115, 0x4001, &com_buff, 2);
|
|
/* Analogue input 3 left channel: */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0118, 0x4001, &com_buff, 2);
|
|
/* Analogue input 3 right channel: */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0119, 0x4001, &com_buff, 2);
|
|
/* Analogue input 4 left channel: */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x011c, 0x4001, &com_buff, 2);
|
|
/* Analogue input 4 right channel: */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x011d, 0x4001, &com_buff, 2);
|
|
|
|
/* Set software sends to output */
|
|
com_buff[0] = 0x00;
|
|
com_buff[1] = 0x00;
|
|
/* Analogue software return 1 left channel: */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0100, 0x4001, &com_buff, 2);
|
|
com_buff[0] = 0x00;
|
|
com_buff[1] = 0x80;
|
|
/* Analogue software return 1 right channel: */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0101, 0x4001, &com_buff, 2);
|
|
com_buff[0] = 0x00;
|
|
com_buff[1] = 0x80;
|
|
/* Analogue software return 2 left channel: */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0104, 0x4001, &com_buff, 2);
|
|
com_buff[0] = 0x00;
|
|
com_buff[1] = 0x00;
|
|
/* Analogue software return 2 right channel: */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0105, 0x4001, &com_buff, 2);
|
|
|
|
com_buff[0] = 0x00;
|
|
com_buff[1] = 0x80;
|
|
/* Analogue software return 3 left channel: */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0108, 0x4001, &com_buff, 2);
|
|
/* Analogue software return 3 right channel: */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0109, 0x4001, &com_buff, 2);
|
|
/* Analogue software return 4 left channel: */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x010c, 0x4001, &com_buff, 2);
|
|
/* Analogue software return 4 right channel: */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x010d, 0x4001, &com_buff, 2);
|
|
|
|
/* Return to muting sends */
|
|
com_buff[0] = 0x00;
|
|
com_buff[1] = 0x80;
|
|
/* Analogue fx return left channel: */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0120, 0x4001, &com_buff, 2);
|
|
/* Analogue fx return right channel: */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0121, 0x4001, &com_buff, 2);
|
|
|
|
/* Analogue software input 1 fx send: */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0100, 0x4201, &com_buff, 2);
|
|
/* Analogue software input 2 fx send: */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0101, 0x4201, &com_buff, 2);
|
|
/* Analogue software input 3 fx send: */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0102, 0x4201, &com_buff, 2);
|
|
/* Analogue software input 4 fx send: */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0103, 0x4201, &com_buff, 2);
|
|
/* Analogue input 1 fx send: */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0104, 0x4201, &com_buff, 2);
|
|
/* Analogue input 2 fx send: */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0105, 0x4201, &com_buff, 2);
|
|
/* Analogue input 3 fx send: */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0106, 0x4201, &com_buff, 2);
|
|
/* Analogue input 4 fx send: */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0107, 0x4201, &com_buff, 2);
|
|
|
|
/* Toggle allowing host control */
|
|
com_buff[0] = 0x02;
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
3, 0x21, 0x0000, 0x2001, &com_buff, 1);
|
|
|
|
/* Do not dim fx returns */
|
|
com_buff[0] = 0x00;
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
3, 0x21, 0x0002, 0x2001, &com_buff, 1);
|
|
|
|
/* Do not set fx returns to mono */
|
|
com_buff[0] = 0x00;
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
3, 0x21, 0x0001, 0x2001, &com_buff, 1);
|
|
|
|
/* Mute the S/PDIF hardware loopback
|
|
* same odd volume logic here as above
|
|
*/
|
|
com_buff[0] = 0x00;
|
|
com_buff[1] = 0x80;
|
|
/* S/PDIF hardware input 1 left channel */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0112, 0x4001, &com_buff, 2);
|
|
/* S/PDIF hardware input 1 right channel */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0113, 0x4001, &com_buff, 2);
|
|
/* S/PDIF hardware input 2 left channel */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0116, 0x4001, &com_buff, 2);
|
|
/* S/PDIF hardware input 2 right channel */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0117, 0x4001, &com_buff, 2);
|
|
/* S/PDIF hardware input 3 left channel */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x011a, 0x4001, &com_buff, 2);
|
|
/* S/PDIF hardware input 3 right channel */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x011b, 0x4001, &com_buff, 2);
|
|
/* S/PDIF hardware input 4 left channel */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x011e, 0x4001, &com_buff, 2);
|
|
/* S/PDIF hardware input 4 right channel */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x011f, 0x4001, &com_buff, 2);
|
|
/* S/PDIF software return 1 left channel */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0102, 0x4001, &com_buff, 2);
|
|
/* S/PDIF software return 1 right channel */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0103, 0x4001, &com_buff, 2);
|
|
/* S/PDIF software return 2 left channel */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0106, 0x4001, &com_buff, 2);
|
|
/* S/PDIF software return 2 right channel */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0107, 0x4001, &com_buff, 2);
|
|
|
|
com_buff[0] = 0x00;
|
|
com_buff[1] = 0x00;
|
|
/* S/PDIF software return 3 left channel */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x010a, 0x4001, &com_buff, 2);
|
|
|
|
com_buff[0] = 0x00;
|
|
com_buff[1] = 0x80;
|
|
/* S/PDIF software return 3 right channel */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x010b, 0x4001, &com_buff, 2);
|
|
/* S/PDIF software return 4 left channel */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x010e, 0x4001, &com_buff, 2);
|
|
|
|
com_buff[0] = 0x00;
|
|
com_buff[1] = 0x00;
|
|
/* S/PDIF software return 4 right channel */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x010f, 0x4001, &com_buff, 2);
|
|
|
|
com_buff[0] = 0x00;
|
|
com_buff[1] = 0x80;
|
|
/* S/PDIF fx returns left channel */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0122, 0x4001, &com_buff, 2);
|
|
/* S/PDIF fx returns right channel */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0123, 0x4001, &com_buff, 2);
|
|
|
|
/* Set the dropdown "Effect" to the first option */
|
|
/* Room1 = 0x00 */
|
|
/* Room2 = 0x01 */
|
|
/* Room3 = 0x02 */
|
|
/* Hall 1 = 0x03 */
|
|
/* Hall 2 = 0x04 */
|
|
/* Plate = 0x05 */
|
|
/* Delay = 0x06 */
|
|
/* Echo = 0x07 */
|
|
com_buff[0] = 0x00;
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0200, 0x4301, &com_buff, 1); /* max is 0xff */
|
|
/* min is 0x00 */
|
|
|
|
|
|
/* Set the effect duration to 0 */
|
|
/* max is 0xffff */
|
|
/* min is 0x0000 */
|
|
com_buff[0] = 0x00;
|
|
com_buff[1] = 0x00;
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0400, 0x4301, &com_buff, 2);
|
|
|
|
/* Set the effect volume and feedback to 0 */
|
|
/* max is 0xff */
|
|
/* min is 0x00 */
|
|
com_buff[0] = 0x00;
|
|
/* feedback: */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0500, 0x4301, &com_buff, 1);
|
|
/* volume: */
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
1, 0x21, 0x0300, 0x4301, &com_buff, 1);
|
|
|
|
/* Set soft button hold duration */
|
|
/* 0x03 = 250ms */
|
|
/* 0x05 = 500ms DEFAULT */
|
|
/* 0x08 = 750ms */
|
|
/* 0x0a = 1sec */
|
|
com_buff[0] = 0x05;
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
3, 0x21, 0x0005, 0x2001, &com_buff, 1);
|
|
|
|
/* Use dim LEDs for button of state */
|
|
com_buff[0] = 0x00;
|
|
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
3, 0x21, 0x0004, 0x2001, &com_buff, 1);
|
|
}
|
|
|
|
#define MBOX3_DESCRIPTOR_SIZE 464
|
|
|
|
static int snd_usb_mbox3_boot_quirk(struct usb_device *dev)
|
|
{
|
|
struct usb_host_config *config = dev->actconfig;
|
|
int err;
|
|
int descriptor_size;
|
|
|
|
descriptor_size = le16_to_cpu(get_cfg_desc(config)->wTotalLength);
|
|
|
|
if (descriptor_size != MBOX3_DESCRIPTOR_SIZE) {
|
|
dev_err(&dev->dev, "Invalid descriptor size=%d.\n", descriptor_size);
|
|
return -ENODEV;
|
|
}
|
|
|
|
dev_dbg(&dev->dev, "device initialised!\n");
|
|
|
|
err = usb_get_descriptor(dev, USB_DT_DEVICE, 0,
|
|
&dev->descriptor, sizeof(dev->descriptor));
|
|
config = dev->actconfig;
|
|
if (err < 0)
|
|
dev_dbg(&dev->dev, "error usb_get_descriptor: %d\n", err);
|
|
|
|
err = usb_reset_configuration(dev);
|
|
if (err < 0)
|
|
dev_dbg(&dev->dev, "error usb_reset_configuration: %d\n", err);
|
|
dev_dbg(&dev->dev, "mbox3_boot: new boot length = %d\n",
|
|
le16_to_cpu(get_cfg_desc(config)->wTotalLength));
|
|
|
|
mbox3_setup_48_24_magic(dev);
|
|
dev_info(&dev->dev, "Digidesign Mbox 3: 24bit 48kHz");
|
|
|
|
return 0; /* Successful boot */
|
|
}
|
|
|
|
#define MICROBOOK_BUF_SIZE 128
|
|
|
|
static int snd_usb_motu_microbookii_communicate(struct usb_device *dev, u8 *buf,
|
|
int buf_size, int *length)
|
|
{
|
|
int err, actual_length;
|
|
|
|
if (usb_pipe_type_check(dev, usb_sndintpipe(dev, 0x01)))
|
|
return -EINVAL;
|
|
err = usb_interrupt_msg(dev, usb_sndintpipe(dev, 0x01), buf, *length,
|
|
&actual_length, 1000);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
print_hex_dump(KERN_DEBUG, "MicroBookII snd: ", DUMP_PREFIX_NONE, 16, 1,
|
|
buf, actual_length, false);
|
|
|
|
memset(buf, 0, buf_size);
|
|
|
|
if (usb_pipe_type_check(dev, usb_rcvintpipe(dev, 0x82)))
|
|
return -EINVAL;
|
|
err = usb_interrupt_msg(dev, usb_rcvintpipe(dev, 0x82), buf, buf_size,
|
|
&actual_length, 1000);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
print_hex_dump(KERN_DEBUG, "MicroBookII rcv: ", DUMP_PREFIX_NONE, 16, 1,
|
|
buf, actual_length, false);
|
|
|
|
*length = actual_length;
|
|
return 0;
|
|
}
|
|
|
|
static int snd_usb_motu_microbookii_boot_quirk(struct usb_device *dev)
|
|
{
|
|
int err, actual_length, poll_attempts = 0;
|
|
static const u8 set_samplerate_seq[] = { 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x0b, 0x14,
|
|
0x00, 0x00, 0x00, 0x01 };
|
|
static const u8 poll_ready_seq[] = { 0x00, 0x04, 0x00, 0x00,
|
|
0x00, 0x00, 0x0b, 0x18 };
|
|
u8 *buf = kzalloc(MICROBOOK_BUF_SIZE, GFP_KERNEL);
|
|
|
|
if (!buf)
|
|
return -ENOMEM;
|
|
|
|
dev_info(&dev->dev, "Waiting for MOTU Microbook II to boot up...\n");
|
|
|
|
/* First we tell the device which sample rate to use. */
|
|
memcpy(buf, set_samplerate_seq, sizeof(set_samplerate_seq));
|
|
actual_length = sizeof(set_samplerate_seq);
|
|
err = snd_usb_motu_microbookii_communicate(dev, buf, MICROBOOK_BUF_SIZE,
|
|
&actual_length);
|
|
|
|
if (err < 0) {
|
|
dev_err(&dev->dev,
|
|
"failed setting the sample rate for Motu MicroBook II: %d\n",
|
|
err);
|
|
goto free_buf;
|
|
}
|
|
|
|
/* Then we poll every 100 ms until the device informs of its readiness. */
|
|
while (true) {
|
|
if (++poll_attempts > 100) {
|
|
dev_err(&dev->dev,
|
|
"failed booting Motu MicroBook II: timeout\n");
|
|
err = -ENODEV;
|
|
goto free_buf;
|
|
}
|
|
|
|
memset(buf, 0, MICROBOOK_BUF_SIZE);
|
|
memcpy(buf, poll_ready_seq, sizeof(poll_ready_seq));
|
|
|
|
actual_length = sizeof(poll_ready_seq);
|
|
err = snd_usb_motu_microbookii_communicate(
|
|
dev, buf, MICROBOOK_BUF_SIZE, &actual_length);
|
|
if (err < 0) {
|
|
dev_err(&dev->dev,
|
|
"failed booting Motu MicroBook II: communication error %d\n",
|
|
err);
|
|
goto free_buf;
|
|
}
|
|
|
|
/* the device signals its readiness through a message of the
|
|
* form
|
|
* XX 06 00 00 00 00 0b 18 00 00 00 01
|
|
* If the device is not yet ready to accept audio data, the
|
|
* last byte of that sequence is 00.
|
|
*/
|
|
if (actual_length == 12 && buf[actual_length - 1] == 1)
|
|
break;
|
|
|
|
msleep(100);
|
|
}
|
|
|
|
dev_info(&dev->dev, "MOTU MicroBook II ready\n");
|
|
|
|
free_buf:
|
|
kfree(buf);
|
|
return err;
|
|
}
|
|
|
|
static int snd_usb_motu_m_series_boot_quirk(struct usb_device *dev)
|
|
{
|
|
msleep(4000);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Setup quirks
|
|
*/
|
|
#define MAUDIO_SET 0x01 /* parse device_setup */
|
|
#define MAUDIO_SET_COMPATIBLE 0x80 /* use only "win-compatible" interfaces */
|
|
#define MAUDIO_SET_DTS 0x02 /* enable DTS Digital Output */
|
|
#define MAUDIO_SET_96K 0x04 /* 48-96kHz rate if set, 8-48kHz otherwise */
|
|
#define MAUDIO_SET_24B 0x08 /* 24bits sample if set, 16bits otherwise */
|
|
#define MAUDIO_SET_DI 0x10 /* enable Digital Input */
|
|
#define MAUDIO_SET_MASK 0x1f /* bit mask for setup value */
|
|
#define MAUDIO_SET_24B_48K_DI 0x19 /* 24bits+48kHz+Digital Input */
|
|
#define MAUDIO_SET_24B_48K_NOTDI 0x09 /* 24bits+48kHz+No Digital Input */
|
|
#define MAUDIO_SET_16B_48K_DI 0x11 /* 16bits+48kHz+Digital Input */
|
|
#define MAUDIO_SET_16B_48K_NOTDI 0x01 /* 16bits+48kHz+No Digital Input */
|
|
|
|
static int quattro_skip_setting_quirk(struct snd_usb_audio *chip,
|
|
int iface, int altno)
|
|
{
|
|
/* Reset ALL ifaces to 0 altsetting.
|
|
* Call it for every possible altsetting of every interface.
|
|
*/
|
|
usb_set_interface(chip->dev, iface, 0);
|
|
if (chip->setup & MAUDIO_SET) {
|
|
if (chip->setup & MAUDIO_SET_COMPATIBLE) {
|
|
if (iface != 1 && iface != 2)
|
|
return 1; /* skip all interfaces but 1 and 2 */
|
|
} else {
|
|
unsigned int mask;
|
|
if (iface == 1 || iface == 2)
|
|
return 1; /* skip interfaces 1 and 2 */
|
|
if ((chip->setup & MAUDIO_SET_96K) && altno != 1)
|
|
return 1; /* skip this altsetting */
|
|
mask = chip->setup & MAUDIO_SET_MASK;
|
|
if (mask == MAUDIO_SET_24B_48K_DI && altno != 2)
|
|
return 1; /* skip this altsetting */
|
|
if (mask == MAUDIO_SET_24B_48K_NOTDI && altno != 3)
|
|
return 1; /* skip this altsetting */
|
|
if (mask == MAUDIO_SET_16B_48K_NOTDI && altno != 4)
|
|
return 1; /* skip this altsetting */
|
|
}
|
|
}
|
|
usb_audio_dbg(chip,
|
|
"using altsetting %d for interface %d config %d\n",
|
|
altno, iface, chip->setup);
|
|
return 0; /* keep this altsetting */
|
|
}
|
|
|
|
static int audiophile_skip_setting_quirk(struct snd_usb_audio *chip,
|
|
int iface,
|
|
int altno)
|
|
{
|
|
/* Reset ALL ifaces to 0 altsetting.
|
|
* Call it for every possible altsetting of every interface.
|
|
*/
|
|
usb_set_interface(chip->dev, iface, 0);
|
|
|
|
if (chip->setup & MAUDIO_SET) {
|
|
unsigned int mask;
|
|
if ((chip->setup & MAUDIO_SET_DTS) && altno != 6)
|
|
return 1; /* skip this altsetting */
|
|
if ((chip->setup & MAUDIO_SET_96K) && altno != 1)
|
|
return 1; /* skip this altsetting */
|
|
mask = chip->setup & MAUDIO_SET_MASK;
|
|
if (mask == MAUDIO_SET_24B_48K_DI && altno != 2)
|
|
return 1; /* skip this altsetting */
|
|
if (mask == MAUDIO_SET_24B_48K_NOTDI && altno != 3)
|
|
return 1; /* skip this altsetting */
|
|
if (mask == MAUDIO_SET_16B_48K_DI && altno != 4)
|
|
return 1; /* skip this altsetting */
|
|
if (mask == MAUDIO_SET_16B_48K_NOTDI && altno != 5)
|
|
return 1; /* skip this altsetting */
|
|
}
|
|
|
|
return 0; /* keep this altsetting */
|
|
}
|
|
|
|
static int fasttrackpro_skip_setting_quirk(struct snd_usb_audio *chip,
|
|
int iface, int altno)
|
|
{
|
|
/* Reset ALL ifaces to 0 altsetting.
|
|
* Call it for every possible altsetting of every interface.
|
|
*/
|
|
usb_set_interface(chip->dev, iface, 0);
|
|
|
|
/* possible configuration where both inputs and only one output is
|
|
*used is not supported by the current setup
|
|
*/
|
|
if (chip->setup & (MAUDIO_SET | MAUDIO_SET_24B)) {
|
|
if (chip->setup & MAUDIO_SET_96K) {
|
|
if (altno != 3 && altno != 6)
|
|
return 1;
|
|
} else if (chip->setup & MAUDIO_SET_DI) {
|
|
if (iface == 4)
|
|
return 1; /* no analog input */
|
|
if (altno != 2 && altno != 5)
|
|
return 1; /* enable only altsets 2 and 5 */
|
|
} else {
|
|
if (iface == 5)
|
|
return 1; /* disable digialt input */
|
|
if (altno != 2 && altno != 5)
|
|
return 1; /* enalbe only altsets 2 and 5 */
|
|
}
|
|
} else {
|
|
/* keep only 16-Bit mode */
|
|
if (altno != 1)
|
|
return 1;
|
|
}
|
|
|
|
usb_audio_dbg(chip,
|
|
"using altsetting %d for interface %d config %d\n",
|
|
altno, iface, chip->setup);
|
|
return 0; /* keep this altsetting */
|
|
}
|
|
|
|
static int s1810c_skip_setting_quirk(struct snd_usb_audio *chip,
|
|
int iface, int altno)
|
|
{
|
|
/*
|
|
* Altno settings:
|
|
*
|
|
* Playback (Interface 1):
|
|
* 1: 6 Analog + 2 S/PDIF
|
|
* 2: 6 Analog + 2 S/PDIF
|
|
* 3: 6 Analog
|
|
*
|
|
* Capture (Interface 2):
|
|
* 1: 8 Analog + 2 S/PDIF + 8 ADAT
|
|
* 2: 8 Analog + 2 S/PDIF + 4 ADAT
|
|
* 3: 8 Analog
|
|
*/
|
|
|
|
/*
|
|
* I'll leave 2 as the default one and
|
|
* use device_setup to switch to the
|
|
* other two.
|
|
*/
|
|
if ((chip->setup == 0 || chip->setup > 2) && altno != 2)
|
|
return 1;
|
|
else if (chip->setup == 1 && altno != 1)
|
|
return 1;
|
|
else if (chip->setup == 2 && altno != 3)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip,
|
|
int iface,
|
|
int altno)
|
|
{
|
|
/* audiophile usb: skip altsets incompatible with device_setup */
|
|
if (chip->usb_id == USB_ID(0x0763, 0x2003))
|
|
return audiophile_skip_setting_quirk(chip, iface, altno);
|
|
/* quattro usb: skip altsets incompatible with device_setup */
|
|
if (chip->usb_id == USB_ID(0x0763, 0x2001))
|
|
return quattro_skip_setting_quirk(chip, iface, altno);
|
|
/* fasttrackpro usb: skip altsets incompatible with device_setup */
|
|
if (chip->usb_id == USB_ID(0x0763, 0x2012))
|
|
return fasttrackpro_skip_setting_quirk(chip, iface, altno);
|
|
/* presonus studio 1810c: skip altsets incompatible with device_setup */
|
|
if (chip->usb_id == USB_ID(0x194f, 0x010c))
|
|
return s1810c_skip_setting_quirk(chip, iface, altno);
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
int snd_usb_apply_boot_quirk(struct usb_device *dev,
|
|
struct usb_interface *intf,
|
|
const struct snd_usb_audio_quirk *quirk,
|
|
unsigned int id)
|
|
{
|
|
switch (id) {
|
|
case USB_ID(0x041e, 0x3000):
|
|
/* SB Extigy needs special boot-up sequence */
|
|
/* if more models come, this will go to the quirk list. */
|
|
return snd_usb_extigy_boot_quirk(dev, intf);
|
|
|
|
case USB_ID(0x041e, 0x3020):
|
|
/* SB Audigy 2 NX needs its own boot-up magic, too */
|
|
return snd_usb_audigy2nx_boot_quirk(dev);
|
|
|
|
case USB_ID(0x10f5, 0x0200):
|
|
/* C-Media CM106 / Turtle Beach Audio Advantage Roadie */
|
|
return snd_usb_cm106_boot_quirk(dev);
|
|
|
|
case USB_ID(0x0d8c, 0x0102):
|
|
/* C-Media CM6206 / CM106-Like Sound Device */
|
|
case USB_ID(0x0ccd, 0x00b1): /* Terratec Aureon 7.1 USB */
|
|
return snd_usb_cm6206_boot_quirk(dev);
|
|
|
|
case USB_ID(0x0dba, 0x3000):
|
|
/* Digidesign Mbox 2 */
|
|
return snd_usb_mbox2_boot_quirk(dev);
|
|
case USB_ID(0x0dba, 0x5000):
|
|
/* Digidesign Mbox 3 */
|
|
return snd_usb_mbox3_boot_quirk(dev);
|
|
|
|
|
|
case USB_ID(0x1235, 0x0010): /* Focusrite Novation Saffire 6 USB */
|
|
case USB_ID(0x1235, 0x0018): /* Focusrite Novation Twitch */
|
|
return snd_usb_novation_boot_quirk(dev);
|
|
|
|
case USB_ID(0x133e, 0x0815):
|
|
/* Access Music VirusTI Desktop */
|
|
return snd_usb_accessmusic_boot_quirk(dev);
|
|
|
|
case USB_ID(0x17cc, 0x1000): /* Komplete Audio 6 */
|
|
case USB_ID(0x17cc, 0x1010): /* Traktor Audio 6 */
|
|
case USB_ID(0x17cc, 0x1020): /* Traktor Audio 10 */
|
|
return snd_usb_nativeinstruments_boot_quirk(dev);
|
|
case USB_ID(0x0763, 0x2012): /* M-Audio Fast Track Pro USB */
|
|
return snd_usb_fasttrackpro_boot_quirk(dev);
|
|
case USB_ID(0x047f, 0xc010): /* Plantronics Gamecom 780 */
|
|
return snd_usb_gamecon780_boot_quirk(dev);
|
|
case USB_ID(0x2466, 0x8010): /* Fractal Audio Axe-Fx 3 */
|
|
return snd_usb_axefx3_boot_quirk(dev);
|
|
case USB_ID(0x07fd, 0x0004): /* MOTU MicroBook II */
|
|
/*
|
|
* For some reason interface 3 with vendor-spec class is
|
|
* detected on MicroBook IIc.
|
|
*/
|
|
if (get_iface_desc(intf->altsetting)->bInterfaceClass ==
|
|
USB_CLASS_VENDOR_SPEC &&
|
|
get_iface_desc(intf->altsetting)->bInterfaceNumber < 3)
|
|
return snd_usb_motu_microbookii_boot_quirk(dev);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int snd_usb_apply_boot_quirk_once(struct usb_device *dev,
|
|
struct usb_interface *intf,
|
|
const struct snd_usb_audio_quirk *quirk,
|
|
unsigned int id)
|
|
{
|
|
switch (id) {
|
|
case USB_ID(0x07fd, 0x0008): /* MOTU M Series, 1st hardware version */
|
|
return snd_usb_motu_m_series_boot_quirk(dev);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* check if the device uses big-endian samples
|
|
*/
|
|
int snd_usb_is_big_endian_format(struct snd_usb_audio *chip,
|
|
const struct audioformat *fp)
|
|
{
|
|
/* it depends on altsetting whether the device is big-endian or not */
|
|
switch (chip->usb_id) {
|
|
case USB_ID(0x0763, 0x2001): /* M-Audio Quattro: captured data only */
|
|
if (fp->altsetting == 2 || fp->altsetting == 3 ||
|
|
fp->altsetting == 5 || fp->altsetting == 6)
|
|
return 1;
|
|
break;
|
|
case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
|
|
if (chip->setup == 0x00 ||
|
|
fp->altsetting == 1 || fp->altsetting == 2 ||
|
|
fp->altsetting == 3)
|
|
return 1;
|
|
break;
|
|
case USB_ID(0x0763, 0x2012): /* M-Audio Fast Track Pro */
|
|
if (fp->altsetting == 2 || fp->altsetting == 3 ||
|
|
fp->altsetting == 5 || fp->altsetting == 6)
|
|
return 1;
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* For E-Mu 0404USB/0202USB/TrackerPre/0204 sample rate should be set for device,
|
|
* not for interface.
|
|
*/
|
|
|
|
enum {
|
|
EMU_QUIRK_SR_44100HZ = 0,
|
|
EMU_QUIRK_SR_48000HZ,
|
|
EMU_QUIRK_SR_88200HZ,
|
|
EMU_QUIRK_SR_96000HZ,
|
|
EMU_QUIRK_SR_176400HZ,
|
|
EMU_QUIRK_SR_192000HZ
|
|
};
|
|
|
|
static void set_format_emu_quirk(struct snd_usb_substream *subs,
|
|
const struct audioformat *fmt)
|
|
{
|
|
unsigned char emu_samplerate_id = 0;
|
|
|
|
/* When capture is active
|
|
* sample rate shouldn't be changed
|
|
* by playback substream
|
|
*/
|
|
if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) {
|
|
if (subs->stream->substream[SNDRV_PCM_STREAM_CAPTURE].cur_audiofmt)
|
|
return;
|
|
}
|
|
|
|
switch (fmt->rate_min) {
|
|
case 48000:
|
|
emu_samplerate_id = EMU_QUIRK_SR_48000HZ;
|
|
break;
|
|
case 88200:
|
|
emu_samplerate_id = EMU_QUIRK_SR_88200HZ;
|
|
break;
|
|
case 96000:
|
|
emu_samplerate_id = EMU_QUIRK_SR_96000HZ;
|
|
break;
|
|
case 176400:
|
|
emu_samplerate_id = EMU_QUIRK_SR_176400HZ;
|
|
break;
|
|
case 192000:
|
|
emu_samplerate_id = EMU_QUIRK_SR_192000HZ;
|
|
break;
|
|
default:
|
|
emu_samplerate_id = EMU_QUIRK_SR_44100HZ;
|
|
break;
|
|
}
|
|
snd_emuusb_set_samplerate(subs->stream->chip, emu_samplerate_id);
|
|
subs->pkt_offset_adj = (emu_samplerate_id >= EMU_QUIRK_SR_176400HZ) ? 4 : 0;
|
|
}
|
|
|
|
static int pioneer_djm_set_format_quirk(struct snd_usb_substream *subs,
|
|
u16 windex)
|
|
{
|
|
unsigned int cur_rate = subs->data_endpoint->cur_rate;
|
|
u8 sr[3];
|
|
// Convert to little endian
|
|
sr[0] = cur_rate & 0xff;
|
|
sr[1] = (cur_rate >> 8) & 0xff;
|
|
sr[2] = (cur_rate >> 16) & 0xff;
|
|
usb_set_interface(subs->dev, 0, 1);
|
|
// we should derive windex from fmt-sync_ep but it's not set
|
|
snd_usb_ctl_msg(subs->stream->chip->dev,
|
|
usb_sndctrlpipe(subs->stream->chip->dev, 0),
|
|
0x01, 0x22, 0x0100, windex, &sr, 0x0003);
|
|
return 0;
|
|
}
|
|
|
|
void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
|
|
const struct audioformat *fmt)
|
|
{
|
|
switch (subs->stream->chip->usb_id) {
|
|
case USB_ID(0x041e, 0x3f02): /* E-Mu 0202 USB */
|
|
case USB_ID(0x041e, 0x3f04): /* E-Mu 0404 USB */
|
|
case USB_ID(0x041e, 0x3f0a): /* E-Mu Tracker Pre */
|
|
case USB_ID(0x041e, 0x3f19): /* E-Mu 0204 USB */
|
|
set_format_emu_quirk(subs, fmt);
|
|
break;
|
|
case USB_ID(0x534d, 0x0021): /* MacroSilicon MS2100/MS2106 */
|
|
case USB_ID(0x534d, 0x2109): /* MacroSilicon MS2109 */
|
|
subs->stream_offset_adj = 2;
|
|
break;
|
|
case USB_ID(0x2b73, 0x0013): /* Pioneer DJM-450 */
|
|
pioneer_djm_set_format_quirk(subs, 0x0082);
|
|
break;
|
|
case USB_ID(0x08e4, 0x017f): /* Pioneer DJM-750 */
|
|
case USB_ID(0x08e4, 0x0163): /* Pioneer DJM-850 */
|
|
pioneer_djm_set_format_quirk(subs, 0x0086);
|
|
break;
|
|
}
|
|
}
|
|
|
|
int snd_usb_select_mode_quirk(struct snd_usb_audio *chip,
|
|
const struct audioformat *fmt)
|
|
{
|
|
struct usb_device *dev = chip->dev;
|
|
int err;
|
|
|
|
if (chip->quirk_flags & QUIRK_FLAG_ITF_USB_DSD_DAC) {
|
|
/* First switch to alt set 0, otherwise the mode switch cmd
|
|
* will not be accepted by the DAC
|
|
*/
|
|
err = usb_set_interface(dev, fmt->iface, 0);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
msleep(20); /* Delay needed after setting the interface */
|
|
|
|
/* Vendor mode switch cmd is required. */
|
|
if (fmt->formats & SNDRV_PCM_FMTBIT_DSD_U32_BE) {
|
|
/* DSD mode (DSD_U32) requested */
|
|
err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), 0,
|
|
USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
|
|
1, 1, NULL, 0);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
} else {
|
|
/* PCM or DOP mode (S32) requested */
|
|
/* PCM mode (S16) requested */
|
|
err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), 0,
|
|
USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
|
|
0, 1, NULL, 0);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
}
|
|
msleep(20);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep)
|
|
{
|
|
/*
|
|
* "Playback Design" products send bogus feedback data at the start
|
|
* of the stream. Ignore them.
|
|
*/
|
|
if (USB_ID_VENDOR(ep->chip->usb_id) == 0x23ba &&
|
|
ep->type == SND_USB_ENDPOINT_TYPE_SYNC)
|
|
ep->skip_packets = 4;
|
|
|
|
/*
|
|
* M-Audio Fast Track C400/C600 - when packets are not skipped, real
|
|
* world latency varies by approx. +/- 50 frames (at 96kHz) each time
|
|
* the stream is (re)started. When skipping packets 16 at endpoint
|
|
* start up, the real world latency is stable within +/- 1 frame (also
|
|
* across power cycles).
|
|
*/
|
|
if ((ep->chip->usb_id == USB_ID(0x0763, 0x2030) ||
|
|
ep->chip->usb_id == USB_ID(0x0763, 0x2031)) &&
|
|
ep->type == SND_USB_ENDPOINT_TYPE_DATA)
|
|
ep->skip_packets = 16;
|
|
|
|
/* Work around devices that report unreasonable feedback data */
|
|
if ((ep->chip->usb_id == USB_ID(0x0644, 0x8038) || /* TEAC UD-H01 */
|
|
ep->chip->usb_id == USB_ID(0x1852, 0x5034)) && /* T+A Dac8 */
|
|
ep->syncmaxsize == 4)
|
|
ep->tenor_fb_quirk = 1;
|
|
}
|
|
|
|
/* quirk applied after snd_usb_ctl_msg(); not applied during boot quirks */
|
|
void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe,
|
|
__u8 request, __u8 requesttype, __u16 value,
|
|
__u16 index, void *data, __u16 size)
|
|
{
|
|
struct snd_usb_audio *chip = dev_get_drvdata(&dev->dev);
|
|
|
|
if (!chip || (requesttype & USB_TYPE_MASK) != USB_TYPE_CLASS)
|
|
return;
|
|
|
|
if (chip->quirk_flags & QUIRK_FLAG_CTL_MSG_DELAY)
|
|
msleep(20);
|
|
else if (chip->quirk_flags & QUIRK_FLAG_CTL_MSG_DELAY_1M)
|
|
usleep_range(1000, 2000);
|
|
else if (chip->quirk_flags & QUIRK_FLAG_CTL_MSG_DELAY_5M)
|
|
usleep_range(5000, 6000);
|
|
}
|
|
|
|
/*
|
|
* snd_usb_interface_dsd_format_quirks() is called from format.c to
|
|
* augment the PCM format bit-field for DSD types. The UAC standards
|
|
* don't have a designated bit field to denote DSD-capable interfaces,
|
|
* hence all hardware that is known to support this format has to be
|
|
* listed here.
|
|
*/
|
|
u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
|
|
struct audioformat *fp,
|
|
unsigned int sample_bytes)
|
|
{
|
|
struct usb_interface *iface;
|
|
|
|
/* Playback Designs */
|
|
if (USB_ID_VENDOR(chip->usb_id) == 0x23ba &&
|
|
USB_ID_PRODUCT(chip->usb_id) < 0x0110) {
|
|
switch (fp->altsetting) {
|
|
case 1:
|
|
fp->dsd_dop = true;
|
|
return SNDRV_PCM_FMTBIT_DSD_U16_LE;
|
|
case 2:
|
|
fp->dsd_bitrev = true;
|
|
return SNDRV_PCM_FMTBIT_DSD_U8;
|
|
case 3:
|
|
fp->dsd_bitrev = true;
|
|
return SNDRV_PCM_FMTBIT_DSD_U16_LE;
|
|
}
|
|
}
|
|
|
|
/* XMOS based USB DACs */
|
|
switch (chip->usb_id) {
|
|
case USB_ID(0x139f, 0x5504): /* Nagra DAC */
|
|
case USB_ID(0x20b1, 0x3089): /* Mola-Mola DAC */
|
|
case USB_ID(0x2522, 0x0007): /* LH Labs Geek Out 1V5 */
|
|
case USB_ID(0x2522, 0x0009): /* LH Labs Geek Pulse X Inifinity 2V0 */
|
|
case USB_ID(0x2522, 0x0012): /* LH Labs VI DAC Infinity */
|
|
case USB_ID(0x2772, 0x0230): /* Pro-Ject Pre Box S2 Digital */
|
|
if (fp->altsetting == 2)
|
|
return SNDRV_PCM_FMTBIT_DSD_U32_BE;
|
|
break;
|
|
|
|
case USB_ID(0x0d8c, 0x0316): /* Hegel HD12 DSD */
|
|
case USB_ID(0x10cb, 0x0103): /* The Bit Opus #3; with fp->dsd_raw */
|
|
case USB_ID(0x16d0, 0x06b2): /* NuPrime DAC-10 */
|
|
case USB_ID(0x16d0, 0x06b4): /* NuPrime Audio HD-AVP/AVA */
|
|
case USB_ID(0x16d0, 0x0733): /* Furutech ADL Stratos */
|
|
case USB_ID(0x16d0, 0x09d8): /* NuPrime IDA-8 */
|
|
case USB_ID(0x16d0, 0x09db): /* NuPrime Audio DAC-9 */
|
|
case USB_ID(0x16d0, 0x09dd): /* Encore mDSD */
|
|
case USB_ID(0x1db5, 0x0003): /* Bryston BDA3 */
|
|
case USB_ID(0x20a0, 0x4143): /* WaveIO USB Audio 2.0 */
|
|
case USB_ID(0x22e1, 0xca01): /* HDTA Serenade DSD */
|
|
case USB_ID(0x249c, 0x9326): /* M2Tech Young MkIII */
|
|
case USB_ID(0x2616, 0x0106): /* PS Audio NuWave DAC */
|
|
case USB_ID(0x2622, 0x0041): /* Audiolab M-DAC+ */
|
|
case USB_ID(0x278b, 0x5100): /* Rotel RC-1590 */
|
|
case USB_ID(0x27f7, 0x3002): /* W4S DAC-2v2SE */
|
|
case USB_ID(0x29a2, 0x0086): /* Mutec MC3+ USB */
|
|
case USB_ID(0x6b42, 0x0042): /* MSB Technology */
|
|
if (fp->altsetting == 3)
|
|
return SNDRV_PCM_FMTBIT_DSD_U32_BE;
|
|
break;
|
|
|
|
/* Amanero Combo384 USB based DACs with native DSD support */
|
|
case USB_ID(0x16d0, 0x071a): /* Amanero - Combo384 */
|
|
if (fp->altsetting == 2) {
|
|
switch (le16_to_cpu(chip->dev->descriptor.bcdDevice)) {
|
|
case 0x199:
|
|
return SNDRV_PCM_FMTBIT_DSD_U32_LE;
|
|
case 0x19b:
|
|
case 0x203:
|
|
return SNDRV_PCM_FMTBIT_DSD_U32_BE;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case USB_ID(0x16d0, 0x0a23):
|
|
if (fp->altsetting == 2)
|
|
return SNDRV_PCM_FMTBIT_DSD_U32_BE;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* ITF-USB DSD based DACs */
|
|
if (chip->quirk_flags & QUIRK_FLAG_ITF_USB_DSD_DAC) {
|
|
iface = usb_ifnum_to_if(chip->dev, fp->iface);
|
|
|
|
/* Altsetting 2 support native DSD if the num of altsets is
|
|
* three (0-2),
|
|
* Altsetting 3 support native DSD if the num of altsets is
|
|
* four (0-3).
|
|
*/
|
|
if (fp->altsetting == iface->num_altsetting - 1)
|
|
return SNDRV_PCM_FMTBIT_DSD_U32_BE;
|
|
}
|
|
|
|
/* Mostly generic method to detect many DSD-capable implementations */
|
|
if ((chip->quirk_flags & QUIRK_FLAG_DSD_RAW) && fp->dsd_raw)
|
|
return SNDRV_PCM_FMTBIT_DSD_U32_BE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void snd_usb_audioformat_attributes_quirk(struct snd_usb_audio *chip,
|
|
struct audioformat *fp,
|
|
int stream)
|
|
{
|
|
switch (chip->usb_id) {
|
|
case USB_ID(0x0a92, 0x0053): /* AudioTrak Optoplay */
|
|
/* Optoplay sets the sample rate attribute although
|
|
* it seems not supporting it in fact.
|
|
*/
|
|
fp->attributes &= ~UAC_EP_CS_ATTR_SAMPLE_RATE;
|
|
break;
|
|
case USB_ID(0x041e, 0x3020): /* Creative SB Audigy 2 NX */
|
|
case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
|
|
/* doesn't set the sample rate attribute, but supports it */
|
|
fp->attributes |= UAC_EP_CS_ATTR_SAMPLE_RATE;
|
|
break;
|
|
case USB_ID(0x0763, 0x2001): /* M-Audio Quattro USB */
|
|
case USB_ID(0x0763, 0x2012): /* M-Audio Fast Track Pro USB */
|
|
case USB_ID(0x047f, 0x0ca1): /* plantronics headset */
|
|
case USB_ID(0x077d, 0x07af): /* Griffin iMic (note that there is
|
|
an older model 77d:223) */
|
|
/*
|
|
* plantronics headset and Griffin iMic have set adaptive-in
|
|
* although it's really not...
|
|
*/
|
|
fp->ep_attr &= ~USB_ENDPOINT_SYNCTYPE;
|
|
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
|
|
fp->ep_attr |= USB_ENDPOINT_SYNC_ADAPTIVE;
|
|
else
|
|
fp->ep_attr |= USB_ENDPOINT_SYNC_SYNC;
|
|
break;
|
|
case USB_ID(0x07fd, 0x0004): /* MOTU MicroBook IIc */
|
|
/*
|
|
* MaxPacketsOnly attribute is erroneously set in endpoint
|
|
* descriptors. As a result this card produces noise with
|
|
* all sample rates other than 96 kHz.
|
|
*/
|
|
fp->attributes &= ~UAC_EP_CS_ATTR_FILL_MAX;
|
|
break;
|
|
case USB_ID(0x1224, 0x2a25): /* Jieli Technology USB PHY 2.0 */
|
|
/* mic works only when ep packet size is set to wMaxPacketSize */
|
|
fp->attributes |= UAC_EP_CS_ATTR_FILL_MAX;
|
|
break;
|
|
case USB_ID(0x3511, 0x2b1e): /* Opencomm2 UC USB Bluetooth dongle */
|
|
/* mic works only when ep pitch control is not set */
|
|
if (stream == SNDRV_PCM_STREAM_CAPTURE)
|
|
fp->attributes &= ~UAC_EP_CS_ATTR_PITCH_CONTROL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* driver behavior quirk flags
|
|
*/
|
|
struct usb_audio_quirk_flags_table {
|
|
u32 id;
|
|
u32 flags;
|
|
};
|
|
|
|
#define DEVICE_FLG(vid, pid, _flags) \
|
|
{ .id = USB_ID(vid, pid), .flags = (_flags) }
|
|
#define VENDOR_FLG(vid, _flags) DEVICE_FLG(vid, 0, _flags)
|
|
|
|
static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
|
|
/* Device matches */
|
|
DEVICE_FLG(0x041e, 0x3000, /* Creative SB Extigy */
|
|
QUIRK_FLAG_IGNORE_CTL_ERROR),
|
|
DEVICE_FLG(0x041e, 0x4080, /* Creative Live Cam VF0610 */
|
|
QUIRK_FLAG_GET_SAMPLE_RATE),
|
|
DEVICE_FLG(0x045e, 0x083c, /* MS USB Link headset */
|
|
QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_CTL_MSG_DELAY |
|
|
QUIRK_FLAG_DISABLE_AUTOSUSPEND),
|
|
DEVICE_FLG(0x046d, 0x084c, /* Logitech ConferenceCam Connect */
|
|
QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_CTL_MSG_DELAY_1M),
|
|
DEVICE_FLG(0x046d, 0x0991, /* Logitech QuickCam Pro */
|
|
QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_IGNORE_CTL_ERROR),
|
|
DEVICE_FLG(0x046d, 0x09a4, /* Logitech QuickCam E 3500 */
|
|
QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_IGNORE_CTL_ERROR),
|
|
DEVICE_FLG(0x0499, 0x1509, /* Steinberg UR22 */
|
|
QUIRK_FLAG_GENERIC_IMPLICIT_FB),
|
|
DEVICE_FLG(0x0499, 0x3108, /* Yamaha YIT-W12TX */
|
|
QUIRK_FLAG_GET_SAMPLE_RATE),
|
|
DEVICE_FLG(0x04d8, 0xfeea, /* Benchmark DAC1 Pre */
|
|
QUIRK_FLAG_GET_SAMPLE_RATE),
|
|
DEVICE_FLG(0x04e8, 0xa051, /* Samsung USBC Headset (AKG) */
|
|
QUIRK_FLAG_SKIP_CLOCK_SELECTOR | QUIRK_FLAG_CTL_MSG_DELAY_5M),
|
|
DEVICE_FLG(0x0525, 0xa4ad, /* Hamedal C20 usb camero */
|
|
QUIRK_FLAG_IFACE_SKIP_CLOSE),
|
|
DEVICE_FLG(0x054c, 0x0b8c, /* Sony WALKMAN NW-A45 DAC */
|
|
QUIRK_FLAG_SET_IFACE_FIRST),
|
|
DEVICE_FLG(0x0556, 0x0014, /* Phoenix Audio TMX320VC */
|
|
QUIRK_FLAG_GET_SAMPLE_RATE),
|
|
DEVICE_FLG(0x05a3, 0x9420, /* ELP HD USB Camera */
|
|
QUIRK_FLAG_GET_SAMPLE_RATE),
|
|
DEVICE_FLG(0x05a7, 0x1020, /* Bose Companion 5 */
|
|
QUIRK_FLAG_GET_SAMPLE_RATE),
|
|
DEVICE_FLG(0x05e1, 0x0408, /* Syntek STK1160 */
|
|
QUIRK_FLAG_ALIGN_TRANSFER),
|
|
DEVICE_FLG(0x05e1, 0x0480, /* Hauppauge Woodbury */
|
|
QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER),
|
|
DEVICE_FLG(0x0644, 0x8043, /* TEAC UD-501/UD-501V2/UD-503/NT-503 */
|
|
QUIRK_FLAG_ITF_USB_DSD_DAC | QUIRK_FLAG_CTL_MSG_DELAY |
|
|
QUIRK_FLAG_IFACE_DELAY),
|
|
DEVICE_FLG(0x0644, 0x8044, /* Esoteric D-05X */
|
|
QUIRK_FLAG_ITF_USB_DSD_DAC | QUIRK_FLAG_CTL_MSG_DELAY |
|
|
QUIRK_FLAG_IFACE_DELAY),
|
|
DEVICE_FLG(0x0644, 0x804a, /* TEAC UD-301 */
|
|
QUIRK_FLAG_ITF_USB_DSD_DAC | QUIRK_FLAG_CTL_MSG_DELAY |
|
|
QUIRK_FLAG_IFACE_DELAY),
|
|
DEVICE_FLG(0x0644, 0x805f, /* TEAC Model 12 */
|
|
QUIRK_FLAG_FORCE_IFACE_RESET),
|
|
DEVICE_FLG(0x0644, 0x806b, /* TEAC UD-701 */
|
|
QUIRK_FLAG_ITF_USB_DSD_DAC | QUIRK_FLAG_CTL_MSG_DELAY |
|
|
QUIRK_FLAG_IFACE_DELAY),
|
|
DEVICE_FLG(0x06f8, 0xb000, /* Hercules DJ Console (Windows Edition) */
|
|
QUIRK_FLAG_IGNORE_CTL_ERROR),
|
|
DEVICE_FLG(0x06f8, 0xd002, /* Hercules DJ Console (Macintosh Edition) */
|
|
QUIRK_FLAG_IGNORE_CTL_ERROR),
|
|
DEVICE_FLG(0x0711, 0x5800, /* MCT Trigger 5 USB-to-HDMI */
|
|
QUIRK_FLAG_GET_SAMPLE_RATE),
|
|
DEVICE_FLG(0x074d, 0x3553, /* Outlaw RR2150 (Micronas UAC3553B) */
|
|
QUIRK_FLAG_GET_SAMPLE_RATE),
|
|
DEVICE_FLG(0x0763, 0x2030, /* M-Audio Fast Track C400 */
|
|
QUIRK_FLAG_GENERIC_IMPLICIT_FB),
|
|
DEVICE_FLG(0x0763, 0x2031, /* M-Audio Fast Track C600 */
|
|
QUIRK_FLAG_GENERIC_IMPLICIT_FB),
|
|
DEVICE_FLG(0x07fd, 0x000b, /* MOTU M Series 2nd hardware revision */
|
|
QUIRK_FLAG_CTL_MSG_DELAY_1M),
|
|
DEVICE_FLG(0x08bb, 0x2702, /* LineX FM Transmitter */
|
|
QUIRK_FLAG_IGNORE_CTL_ERROR),
|
|
DEVICE_FLG(0x0951, 0x16ad, /* Kingston HyperX */
|
|
QUIRK_FLAG_CTL_MSG_DELAY_1M),
|
|
DEVICE_FLG(0x0b0e, 0x0349, /* Jabra 550a */
|
|
QUIRK_FLAG_CTL_MSG_DELAY_1M),
|
|
DEVICE_FLG(0x0ecb, 0x205c, /* JBL Quantum610 Wireless */
|
|
QUIRK_FLAG_FIXED_RATE),
|
|
DEVICE_FLG(0x0ecb, 0x2069, /* JBL Quantum810 Wireless */
|
|
QUIRK_FLAG_FIXED_RATE),
|
|
DEVICE_FLG(0x0fd9, 0x0008, /* Hauppauge HVR-950Q */
|
|
QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER),
|
|
DEVICE_FLG(0x1224, 0x2a25, /* Jieli Technology USB PHY 2.0 */
|
|
QUIRK_FLAG_GET_SAMPLE_RATE),
|
|
DEVICE_FLG(0x1395, 0x740a, /* Sennheiser DECT */
|
|
QUIRK_FLAG_GET_SAMPLE_RATE),
|
|
DEVICE_FLG(0x1397, 0x0507, /* Behringer UMC202HD */
|
|
QUIRK_FLAG_PLAYBACK_FIRST | QUIRK_FLAG_GENERIC_IMPLICIT_FB),
|
|
DEVICE_FLG(0x1397, 0x0508, /* Behringer UMC204HD */
|
|
QUIRK_FLAG_PLAYBACK_FIRST | QUIRK_FLAG_GENERIC_IMPLICIT_FB),
|
|
DEVICE_FLG(0x1397, 0x0509, /* Behringer UMC404HD */
|
|
QUIRK_FLAG_PLAYBACK_FIRST | QUIRK_FLAG_GENERIC_IMPLICIT_FB),
|
|
DEVICE_FLG(0x13e5, 0x0001, /* Serato Phono */
|
|
QUIRK_FLAG_IGNORE_CTL_ERROR),
|
|
DEVICE_FLG(0x154e, 0x1002, /* Denon DCD-1500RE */
|
|
QUIRK_FLAG_ITF_USB_DSD_DAC | QUIRK_FLAG_CTL_MSG_DELAY),
|
|
DEVICE_FLG(0x154e, 0x1003, /* Denon DA-300USB */
|
|
QUIRK_FLAG_ITF_USB_DSD_DAC | QUIRK_FLAG_CTL_MSG_DELAY),
|
|
DEVICE_FLG(0x154e, 0x3005, /* Marantz HD-DAC1 */
|
|
QUIRK_FLAG_ITF_USB_DSD_DAC | QUIRK_FLAG_CTL_MSG_DELAY),
|
|
DEVICE_FLG(0x154e, 0x3006, /* Marantz SA-14S1 */
|
|
QUIRK_FLAG_ITF_USB_DSD_DAC | QUIRK_FLAG_CTL_MSG_DELAY),
|
|
DEVICE_FLG(0x154e, 0x300b, /* Marantz SA-KI RUBY / SA-12 */
|
|
QUIRK_FLAG_DSD_RAW),
|
|
DEVICE_FLG(0x154e, 0x500e, /* Denon DN-X1600 */
|
|
QUIRK_FLAG_IGNORE_CLOCK_SOURCE),
|
|
DEVICE_FLG(0x1686, 0x00dd, /* Zoom R16/24 */
|
|
QUIRK_FLAG_TX_LENGTH | QUIRK_FLAG_CTL_MSG_DELAY_1M),
|
|
DEVICE_FLG(0x17aa, 0x1046, /* Lenovo ThinkStation P620 Rear Line-in, Line-out and Microphone */
|
|
QUIRK_FLAG_DISABLE_AUTOSUSPEND),
|
|
DEVICE_FLG(0x17aa, 0x104d, /* Lenovo ThinkStation P620 Internal Speaker + Front Headset */
|
|
QUIRK_FLAG_DISABLE_AUTOSUSPEND),
|
|
DEVICE_FLG(0x1852, 0x5065, /* Luxman DA-06 */
|
|
QUIRK_FLAG_ITF_USB_DSD_DAC | QUIRK_FLAG_CTL_MSG_DELAY),
|
|
DEVICE_FLG(0x1901, 0x0191, /* GE B850V3 CP2114 audio interface */
|
|
QUIRK_FLAG_GET_SAMPLE_RATE),
|
|
DEVICE_FLG(0x19f7, 0x0035, /* RODE NT-USB+ */
|
|
QUIRK_FLAG_GET_SAMPLE_RATE),
|
|
DEVICE_FLG(0x1bcf, 0x2283, /* NexiGo N930AF FHD Webcam */
|
|
QUIRK_FLAG_GET_SAMPLE_RATE),
|
|
DEVICE_FLG(0x2040, 0x7200, /* Hauppauge HVR-950Q */
|
|
QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER),
|
|
DEVICE_FLG(0x2040, 0x7201, /* Hauppauge HVR-950Q-MXL */
|
|
QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER),
|
|
DEVICE_FLG(0x2040, 0x7210, /* Hauppauge HVR-950Q */
|
|
QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER),
|
|
DEVICE_FLG(0x2040, 0x7211, /* Hauppauge HVR-950Q-MXL */
|
|
QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER),
|
|
DEVICE_FLG(0x2040, 0x7213, /* Hauppauge HVR-950Q */
|
|
QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER),
|
|
DEVICE_FLG(0x2040, 0x7217, /* Hauppauge HVR-950Q */
|
|
QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER),
|
|
DEVICE_FLG(0x2040, 0x721b, /* Hauppauge HVR-950Q */
|
|
QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER),
|
|
DEVICE_FLG(0x2040, 0x721e, /* Hauppauge HVR-950Q */
|
|
QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER),
|
|
DEVICE_FLG(0x2040, 0x721f, /* Hauppauge HVR-950Q */
|
|
QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER),
|
|
DEVICE_FLG(0x2040, 0x7240, /* Hauppauge HVR-850 */
|
|
QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER),
|
|
DEVICE_FLG(0x2040, 0x7260, /* Hauppauge HVR-950Q */
|
|
QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER),
|
|
DEVICE_FLG(0x2040, 0x7270, /* Hauppauge HVR-950Q */
|
|
QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER),
|
|
DEVICE_FLG(0x2040, 0x7280, /* Hauppauge HVR-950Q */
|
|
QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER),
|
|
DEVICE_FLG(0x2040, 0x7281, /* Hauppauge HVR-950Q-MXL */
|
|
QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER),
|
|
DEVICE_FLG(0x2040, 0x8200, /* Hauppauge Woodbury */
|
|
QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER),
|
|
DEVICE_FLG(0x21b4, 0x0081, /* AudioQuest DragonFly */
|
|
QUIRK_FLAG_GET_SAMPLE_RATE),
|
|
DEVICE_FLG(0x21b4, 0x0230, /* Ayre QB-9 Twenty */
|
|
QUIRK_FLAG_DSD_RAW),
|
|
DEVICE_FLG(0x21b4, 0x0232, /* Ayre QX-5 Twenty */
|
|
QUIRK_FLAG_DSD_RAW),
|
|
DEVICE_FLG(0x2522, 0x0007, /* LH Labs Geek Out HD Audio 1V5 */
|
|
QUIRK_FLAG_SET_IFACE_FIRST),
|
|
DEVICE_FLG(0x2708, 0x0002, /* Audient iD14 */
|
|
QUIRK_FLAG_IGNORE_CTL_ERROR),
|
|
DEVICE_FLG(0x2912, 0x30c8, /* Audioengine D1 */
|
|
QUIRK_FLAG_GET_SAMPLE_RATE),
|
|
DEVICE_FLG(0x2b53, 0x0023, /* Fiero SC-01 (firmware v1.0.0 @ 48 kHz) */
|
|
QUIRK_FLAG_GENERIC_IMPLICIT_FB),
|
|
DEVICE_FLG(0x2b53, 0x0024, /* Fiero SC-01 (firmware v1.0.0 @ 96 kHz) */
|
|
QUIRK_FLAG_GENERIC_IMPLICIT_FB),
|
|
DEVICE_FLG(0x2b53, 0x0031, /* Fiero SC-01 (firmware v1.1.0) */
|
|
QUIRK_FLAG_GENERIC_IMPLICIT_FB),
|
|
DEVICE_FLG(0x30be, 0x0101, /* Schiit Hel */
|
|
QUIRK_FLAG_IGNORE_CTL_ERROR),
|
|
DEVICE_FLG(0x413c, 0xa506, /* Dell AE515 sound bar */
|
|
QUIRK_FLAG_GET_SAMPLE_RATE),
|
|
DEVICE_FLG(0x534d, 0x0021, /* MacroSilicon MS2100/MS2106 */
|
|
QUIRK_FLAG_ALIGN_TRANSFER),
|
|
DEVICE_FLG(0x534d, 0x2109, /* MacroSilicon MS2109 */
|
|
QUIRK_FLAG_ALIGN_TRANSFER),
|
|
DEVICE_FLG(0x09da, 0x2695, /* A4Tech FHD 1080p webcam */
|
|
QUIRK_FLAG_DISABLE_AUTOSUSPEND | QUIRK_FLAG_GET_SAMPLE_RATE),
|
|
|
|
/* Vendor matches */
|
|
VENDOR_FLG(0x045e, /* MS Lifecam */
|
|
QUIRK_FLAG_GET_SAMPLE_RATE),
|
|
VENDOR_FLG(0x046d, /* Logitech */
|
|
QUIRK_FLAG_CTL_MSG_DELAY_1M),
|
|
VENDOR_FLG(0x047f, /* Plantronics */
|
|
QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_CTL_MSG_DELAY),
|
|
VENDOR_FLG(0x0644, /* TEAC Corp. */
|
|
QUIRK_FLAG_CTL_MSG_DELAY | QUIRK_FLAG_IFACE_DELAY),
|
|
VENDOR_FLG(0x07fd, /* MOTU */
|
|
QUIRK_FLAG_VALIDATE_RATES),
|
|
VENDOR_FLG(0x1235, /* Focusrite Novation */
|
|
QUIRK_FLAG_VALIDATE_RATES),
|
|
VENDOR_FLG(0x1511, /* AURALiC */
|
|
QUIRK_FLAG_DSD_RAW),
|
|
VENDOR_FLG(0x152a, /* Thesycon devices */
|
|
QUIRK_FLAG_DSD_RAW),
|
|
VENDOR_FLG(0x18d1, /* iBasso devices */
|
|
QUIRK_FLAG_DSD_RAW),
|
|
VENDOR_FLG(0x1de7, /* Phoenix Audio */
|
|
QUIRK_FLAG_GET_SAMPLE_RATE),
|
|
VENDOR_FLG(0x20b1, /* XMOS based devices */
|
|
QUIRK_FLAG_DSD_RAW),
|
|
VENDOR_FLG(0x21ed, /* Accuphase Laboratory */
|
|
QUIRK_FLAG_DSD_RAW),
|
|
VENDOR_FLG(0x22d9, /* Oppo */
|
|
QUIRK_FLAG_DSD_RAW),
|
|
VENDOR_FLG(0x23ba, /* Playback Design */
|
|
QUIRK_FLAG_CTL_MSG_DELAY | QUIRK_FLAG_IFACE_DELAY |
|
|
QUIRK_FLAG_DSD_RAW),
|
|
VENDOR_FLG(0x25ce, /* Mytek devices */
|
|
QUIRK_FLAG_DSD_RAW),
|
|
VENDOR_FLG(0x278b, /* Rotel? */
|
|
QUIRK_FLAG_DSD_RAW),
|
|
VENDOR_FLG(0x292b, /* Gustard/Ess based devices */
|
|
QUIRK_FLAG_DSD_RAW),
|
|
VENDOR_FLG(0x2972, /* FiiO devices */
|
|
QUIRK_FLAG_DSD_RAW),
|
|
VENDOR_FLG(0x2ab6, /* T+A devices */
|
|
QUIRK_FLAG_DSD_RAW),
|
|
VENDOR_FLG(0x2afd, /* McIntosh Laboratory, Inc. */
|
|
QUIRK_FLAG_DSD_RAW),
|
|
VENDOR_FLG(0x2d87, /* Cayin device */
|
|
QUIRK_FLAG_DSD_RAW),
|
|
VENDOR_FLG(0x3336, /* HEM devices */
|
|
QUIRK_FLAG_DSD_RAW),
|
|
VENDOR_FLG(0x3353, /* Khadas devices */
|
|
QUIRK_FLAG_DSD_RAW),
|
|
VENDOR_FLG(0x35f4, /* MSB Technology */
|
|
QUIRK_FLAG_DSD_RAW),
|
|
VENDOR_FLG(0x3842, /* EVGA */
|
|
QUIRK_FLAG_DSD_RAW),
|
|
VENDOR_FLG(0xc502, /* HiBy devices */
|
|
QUIRK_FLAG_DSD_RAW),
|
|
|
|
{} /* terminator */
|
|
};
|
|
|
|
void snd_usb_init_quirk_flags(struct snd_usb_audio *chip)
|
|
{
|
|
const struct usb_audio_quirk_flags_table *p;
|
|
|
|
for (p = quirk_flags_table; p->id; p++) {
|
|
if (chip->usb_id == p->id ||
|
|
(!USB_ID_PRODUCT(p->id) &&
|
|
USB_ID_VENDOR(chip->usb_id) == USB_ID_VENDOR(p->id))) {
|
|
usb_audio_dbg(chip,
|
|
"Set quirk_flags 0x%x for device %04x:%04x\n",
|
|
p->flags, USB_ID_VENDOR(chip->usb_id),
|
|
USB_ID_PRODUCT(chip->usb_id));
|
|
chip->quirk_flags |= p->flags;
|
|
return;
|
|
}
|
|
}
|
|
}
|