aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/sound/cards/audigy-mixer.rst197
-rw-r--r--Documentation/sound/cards/emu-mixer.rst226
-rw-r--r--Documentation/sound/cards/index.rst1
-rw-r--r--Documentation/sound/cards/sb-live-mixer.rst24
-rw-r--r--Documentation/sound/designs/midi-2.0.rst188
-rw-r--r--Documentation/sound/kernel-api/writing-an-alsa-driver.rst58
-rw-r--r--drivers/media/pci/solo6x10/solo6x10-g723.c39
-rw-r--r--drivers/staging/greybus/audio_codec.c18
-rw-r--r--drivers/staging/greybus/audio_codec.h1
-rw-r--r--drivers/staging/greybus/audio_helper.c20
-rw-r--r--include/sound/compress_driver.h2
-rw-r--r--include/sound/control.h31
-rw-r--r--include/sound/core.h4
-rw-r--r--include/sound/cs35l41.h5
-rw-r--r--include/sound/dmaengine_pcm.h2
-rw-r--r--include/sound/emu10k1.h20
-rw-r--r--include/sound/hwdep.h2
-rw-r--r--include/sound/info.h2
-rw-r--r--include/sound/pcm.h15
-rw-r--r--include/sound/rawmidi.h2
-rw-r--r--include/sound/soc-component.h14
-rw-r--r--include/sound/ump.h1
-rw-r--r--lib/iov_iter.c1
-rw-r--r--sound/ac97/bus.c2
-rw-r--r--sound/aoa/soundbus/i2sbus/pcm.c4
-rw-r--r--sound/atmel/ac97c.c3
-rw-r--r--sound/core/compress_offload.c16
-rw-r--r--sound/core/control.c140
-rw-r--r--sound/core/control_compat.c2
-rw-r--r--sound/core/control_led.c6
-rw-r--r--sound/core/hwdep.c38
-rw-r--r--sound/core/init.c28
-rw-r--r--sound/core/jack.c2
-rw-r--r--sound/core/memory.c56
-rw-r--r--sound/core/oss/mixer_oss.c10
-rw-r--r--sound/core/pcm.c24
-rw-r--r--sound/core/pcm_lib.c95
-rw-r--r--sound/core/pcm_native.c2
-rw-r--r--sound/core/rawmidi.c29
-rw-r--r--sound/core/seq/seq_clientmgr.c16
-rw-r--r--sound/core/timer.c16
-rw-r--r--sound/core/ump.c66
-rw-r--r--sound/core/vmaster.c28
-rw-r--r--sound/drivers/Kconfig1
-rw-r--r--sound/drivers/dummy.c12
-rw-r--r--sound/drivers/pcmtest.c102
-rw-r--r--sound/drivers/serial-generic.c2
-rw-r--r--sound/firewire/dice/Makefile2
-rw-r--r--sound/firewire/dice/dice-weiss.c104
-rw-r--r--sound/firewire/dice/dice.c63
-rw-r--r--sound/firewire/dice/dice.h1
-rw-r--r--sound/hda/intel-dsp-config.c60
-rw-r--r--sound/isa/gus/gus_pcm.c23
-rw-r--r--sound/isa/sb/emu8000.c2
-rw-r--r--sound/isa/sb/emu8000_pcm.c74
-rw-r--r--sound/isa/sb/sb16_csp.c2
-rw-r--r--sound/pci/ac97/ac97_codec.c5
-rw-r--r--sound/pci/ac97/ac97_patch.c22
-rw-r--r--sound/pci/asihpi/hpi.h16
-rw-r--r--sound/pci/asihpi/hpi_internal.h5
-rw-r--r--sound/pci/ca0106/ca0106_mixer.c30
-rw-r--r--sound/pci/cmipci.c6
-rw-r--r--sound/pci/cs46xx/cs46xx_lib.c7
-rw-r--r--sound/pci/emu10k1/emu10k1.c13
-rw-r--r--sound/pci/emu10k1/emu10k1_main.c98
-rw-r--r--sound/pci/emu10k1/emu10k1_synth.c10
-rw-r--r--sound/pci/emu10k1/emufx.c17
-rw-r--r--sound/pci/emu10k1/emumixer.c89
-rw-r--r--sound/pci/emu10k1/emumpu401.c40
-rw-r--r--sound/pci/emu10k1/emupcm.c17
-rw-r--r--sound/pci/emu10k1/emuproc.c23
-rw-r--r--sound/pci/emu10k1/io.c12
-rw-r--r--sound/pci/emu10k1/irq.c13
-rw-r--r--sound/pci/emu10k1/p16v.h56
-rw-r--r--sound/pci/emu10k1/p17v.h1
-rw-r--r--sound/pci/emu10k1/timer.c8
-rw-r--r--sound/pci/emu10k1/tina2.h1
-rw-r--r--sound/pci/emu10k1/voice.c12
-rw-r--r--sound/pci/es1938.c30
-rw-r--r--sound/pci/es1968.c15
-rw-r--r--sound/pci/hda/Kconfig18
-rw-r--r--sound/pci/hda/Makefile4
-rw-r--r--sound/pci/hda/cs35l41_hda.c360
-rw-r--r--sound/pci/hda/cs35l41_hda.h1
-rw-r--r--sound/pci/hda/cs35l41_hda_property.c105
-rw-r--r--sound/pci/hda/cs35l41_hda_property.h18
-rw-r--r--sound/pci/hda/cs35l56_hda.c93
-rw-r--r--sound/pci/hda/hda_codec.c2
-rw-r--r--sound/pci/hda/hda_component.h2
-rw-r--r--sound/pci/hda/hda_hwdep.c4
-rw-r--r--sound/pci/hda/hda_tegra.c7
-rw-r--r--sound/pci/hda/patch_cs8409-tables.c4
-rw-r--r--sound/pci/hda/patch_hdmi.c5
-rw-r--r--sound/pci/hda/patch_realtek.c152
-rw-r--r--sound/pci/hda/tas2781_hda_i2c.c856
-rw-r--r--sound/pci/ice1712/juli.c28
-rw-r--r--sound/pci/ice1712/psc724.c19
-rw-r--r--sound/pci/ice1712/quartet.c24
-rw-r--r--sound/pci/ice1712/wm8776.c6
-rw-r--r--sound/pci/korg1212/korg1212.c50
-rw-r--r--sound/pci/maestro3.c15
-rw-r--r--sound/pci/nm256/nm256.c42
-rw-r--r--sound/pci/rme32.c50
-rw-r--r--sound/pci/rme96.c42
-rw-r--r--sound/pci/rme9652/hdsp.c42
-rw-r--r--sound/pci/rme9652/rme9652.c46
-rw-r--r--sound/pci/via82xx.c6
-rw-r--r--sound/pci/ymfpci/ymfpci.c10
-rw-r--r--sound/ppc/awacs.c1
-rw-r--r--sound/ppc/burgundy.c1
-rw-r--r--sound/sh/sh_dac_audio.c25
-rw-r--r--sound/soc/amd/yc/acp6x-mach.c9
-rw-r--r--sound/soc/atmel/mchp-pdmc.c14
-rw-r--r--sound/soc/codecs/cs35l41-lib.c118
-rw-r--r--sound/soc/codecs/cs35l41.c20
-rw-r--r--sound/soc/codecs/sigmadsp.c25
-rw-r--r--sound/soc/mediatek/common/mtk-btcvsd.c27
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-mt6359.c12
-rw-r--r--sound/soc/qcom/lpass-platform.c13
-rw-r--r--sound/soc/soc-component.c16
-rw-r--r--sound/soc/soc-generic-dmaengine-pcm.c18
-rw-r--r--sound/soc/soc-pcm.c4
-rw-r--r--sound/soc/soc-topology.c3
-rw-r--r--sound/soc/stm/stm32_sai_sub.c2
-rw-r--r--sound/sparc/amd7930.c3
-rw-r--r--sound/sparc/cs4231.c2
-rw-r--r--sound/sparc/dbri.c2
-rw-r--r--sound/usb/bcd2000/bcd2000.c4
-rw-r--r--sound/usb/endpoint.c40
-rw-r--r--sound/usb/endpoint.h1
-rw-r--r--sound/usb/media.c4
-rw-r--r--sound/usb/midi2.c15
-rw-r--r--sound/usb/quirks-table.h29
-rw-r--r--sound/usb/stream.c11
-rw-r--r--sound/xen/xen_snd_front_alsa.c64
-rw-r--r--tools/testing/selftests/alsa/pcm-test.c8
136 files changed, 3305 insertions, 1647 deletions
diff --git a/Documentation/sound/cards/audigy-mixer.rst b/Documentation/sound/cards/audigy-mixer.rst
index ea66b50a2b03..7ebaacb6df3d 100644
--- a/Documentation/sound/cards/audigy-mixer.rst
+++ b/Documentation/sound/cards/audigy-mixer.rst
@@ -46,157 +46,158 @@ FX-bus
name='PCM Front Playback Volume',index=0
----------------------------------------
-This control is used to attenuate samples for left and right front PCM FX-bus
+This control is used to attenuate samples from left and right front PCM FX-bus
accumulators. ALSA uses accumulators 8 and 9 for left and right front PCM
-samples for 5.1 playback. The result samples are forwarded to the front DAC PCM
-slots of the Philips DAC.
+samples for 5.1 playback. The result samples are forwarded to the front speakers.
name='PCM Surround Playback Volume',index=0
-------------------------------------------
-This control is used to attenuate samples for left and right surround PCM FX-bus
+This control is used to attenuate samples from left and right surround PCM FX-bus
accumulators. ALSA uses accumulators 2 and 3 for left and right surround PCM
-samples for 5.1 playback. The result samples are forwarded to the surround DAC PCM
-slots of the Philips DAC.
+samples for 5.1 playback. The result samples are forwarded to the surround (rear)
+speakers.
+
+name='PCM Side Playback Volume',index=0
+---------------------------------------
+This control is used to attenuate samples from left and right side PCM FX-bus
+accumulators. ALSA uses accumulators 14 and 15 for left and right side PCM
+samples for 7.1 playback. The result samples are forwarded to the side speakers.
name='PCM Center Playback Volume',index=0
-----------------------------------------
-This control is used to attenuate samples for center PCM FX-bus accumulator.
-ALSA uses accumulator 6 for center PCM sample for 5.1 playback. The result sample
-is forwarded to the center DAC PCM slot of the Philips DAC.
+This control is used to attenuate samples from center PCM FX-bus accumulator.
+ALSA uses accumulator 6 for center PCM samples for 5.1 playback. The result
+samples are forwarded to the center speaker.
name='PCM LFE Playback Volume',index=0
--------------------------------------
This control is used to attenuate sample for LFE PCM FX-bus accumulator.
-ALSA uses accumulator 7 for LFE PCM sample for 5.1 playback. The result sample
-is forwarded to the LFE DAC PCM slot of the Philips DAC.
+ALSA uses accumulator 7 for LFE PCM samples for 5.1 playback. The result
+samples are forwarded to the subwoofer.
name='PCM Playback Volume',index=0
----------------------------------
-This control is used to attenuate samples for left and right PCM FX-bus
+This control is used to attenuate samples from left and right PCM FX-bus
accumulators. ALSA uses accumulators 0 and 1 for left and right PCM samples for
-stereo playback. The result samples are forwarded to the front DAC PCM slots
-of the Philips DAC.
+stereo playback. The result samples are forwarded to the front speakers.
name='PCM Capture Volume',index=0
---------------------------------
-This control is used to attenuate samples for left and right PCM FX-bus
-accumulator. ALSA uses accumulators 0 and 1 for left and right PCM.
-The result is forwarded to the ADC capture FIFO (thus to the standard capture
-PCM device).
+This control is used to attenuate samples from left and right PCM FX-bus
+accumulators. ALSA uses accumulators 0 and 1 for left and right PCM samples for
+stereo playback. The result is forwarded to the standard capture PCM device.
name='Music Playback Volume',index=0
------------------------------------
-This control is used to attenuate samples for left and right MIDI FX-bus
+This control is used to attenuate samples from left and right MIDI FX-bus
accumulators. ALSA uses accumulators 4 and 5 for left and right MIDI samples.
-The result samples are forwarded to the front DAC PCM slots of the AC97 codec.
+The result samples are forwarded to the virtual stereo mixer.
name='Music Capture Volume',index=0
-----------------------------------
-These controls are used to attenuate samples for left and right MIDI FX-bus
-accumulator. ALSA uses accumulators 4 and 5 for left and right PCM.
-The result is forwarded to the ADC capture FIFO (thus to the standard capture
-PCM device).
+These controls are used to attenuate samples from left and right MIDI FX-bus
+accumulator. ALSA uses accumulators 4 and 5 for left and right MIDI samples.
+The result is forwarded to the standard capture PCM device.
name='Mic Playback Volume',index=0
----------------------------------
-This control is used to attenuate samples for left and right Mic input.
-For Mic input is used AC97 codec. The result samples are forwarded to
-the front DAC PCM slots of the Philips DAC. Samples are forwarded to Mic
-capture FIFO (device 1 - 16bit/8KHz mono) too without volume control.
+This control is used to attenuate samples from left and right Mic input of
+the AC97 codec. The result samples are forwarded to the virtual stereo mixer.
name='Mic Capture Volume',index=0
---------------------------------
-This control is used to attenuate samples for left and right Mic input.
-The result is forwarded to the ADC capture FIFO (thus to the standard capture
-PCM device).
+This control is used to attenuate samples from left and right Mic input of
+the AC97 codec. The result is forwarded to the standard capture PCM device.
+
+The original samples are also forwarded to the Mic capture PCM device (device 1;
+16bit/8KHz mono) without volume control.
name='Audigy CD Playback Volume',index=0
----------------------------------------
This control is used to attenuate samples from left and right IEC958 TTL
digital inputs (usually used by a CDROM drive). The result samples are
-forwarded to the front DAC PCM slots of the Philips DAC.
+forwarded to the virtual stereo mixer.
name='Audigy CD Capture Volume',index=0
---------------------------------------
This control is used to attenuate samples from left and right IEC958 TTL
-digital inputs (usually used by a CDROM drive). The result samples are
-forwarded to the ADC capture FIFO (thus to the standard capture PCM device).
+digital inputs (usually used by a CDROM drive). The result is forwarded
+to the standard capture PCM device.
name='IEC958 Optical Playback Volume',index=0
---------------------------------------------
This control is used to attenuate samples from left and right IEC958 optical
-digital input. The result samples are forwarded to the front DAC PCM slots
-of the Philips DAC.
+digital input. The result samples are forwarded to the virtual stereo mixer.
name='IEC958 Optical Capture Volume',index=0
--------------------------------------------
This control is used to attenuate samples from left and right IEC958 optical
-digital inputs. The result samples are forwarded to the ADC capture FIFO
-(thus to the standard capture PCM device).
+digital inputs. The result is forwarded to the standard capture PCM device.
name='Line2 Playback Volume',index=0
------------------------------------
This control is used to attenuate samples from left and right I2S ADC
-inputs (on the AudigyDrive). The result samples are forwarded to the front
-DAC PCM slots of the Philips DAC.
+inputs (on the AudigyDrive). The result samples are forwarded to the virtual
+stereo mixer.
name='Line2 Capture Volume',index=1
-----------------------------------
This control is used to attenuate samples from left and right I2S ADC
-inputs (on the AudigyDrive). The result samples are forwarded to the ADC
-capture FIFO (thus to the standard capture PCM device).
+inputs (on the AudigyDrive). The result is forwarded to the standard capture
+PCM device.
name='Analog Mix Playback Volume',index=0
-----------------------------------------
This control is used to attenuate samples from left and right I2S ADC
-inputs from Philips ADC. The result samples are forwarded to the front
-DAC PCM slots of the Philips DAC. This contains mix from analog sources
-like CD, Line In, Aux, ....
+inputs from Philips ADC. The result samples are forwarded to the virtual
+stereo mixer. This contains mix from analog sources like CD, Line In, Aux, ....
name='Analog Mix Capture Volume',index=1
----------------------------------------
This control is used to attenuate samples from left and right I2S ADC
-inputs Philips ADC. The result samples are forwarded to the ADC
-capture FIFO (thus to the standard capture PCM device).
+inputs Philips ADC. The result is forwarded to the standard capture PCM device.
name='Aux2 Playback Volume',index=0
-----------------------------------
This control is used to attenuate samples from left and right I2S ADC
-inputs (on the AudigyDrive). The result samples are forwarded to the front
-DAC PCM slots of the Philips DAC.
+inputs (on the AudigyDrive). The result samples are forwarded to the virtual
+stereo mixer.
name='Aux2 Capture Volume',index=1
----------------------------------
This control is used to attenuate samples from left and right I2S ADC
-inputs (on the AudigyDrive). The result samples are forwarded to the ADC
-capture FIFO (thus to the standard capture PCM device).
+inputs (on the AudigyDrive). The result is forwarded to the standard capture
+PCM device.
name='Front Playback Volume',index=0
------------------------------------
-All stereo signals are mixed together and mirrored to surround, center and LFE.
-This control is used to attenuate samples for left and right front speakers of
-this mix.
+This control is used to attenuate samples from the virtual stereo mixer.
+The result samples are forwarded to the front speakers.
name='Surround Playback Volume',index=0
---------------------------------------
-All stereo signals are mixed together and mirrored to surround, center and LFE.
-This control is used to attenuate samples for left and right surround speakers of
-this mix.
+This control is used to attenuate samples from the virtual stereo mixer.
+The result samples are forwarded to the surround (rear) speakers.
+
+name='Side Playback Volume',index=0
+-----------------------------------
+This control is used to attenuate samples from the virtual stereo mixer.
+The result samples are forwarded to the side speakers.
name='Center Playback Volume',index=0
-------------------------------------
-All stereo signals are mixed together and mirrored to surround, center and LFE.
-This control is used to attenuate sample for center speaker of this mix.
+This control is used to attenuate samples from the virtual stereo mixer.
+The result samples are forwarded to the center speaker.
name='LFE Playback Volume',index=0
----------------------------------
-All stereo signals are mixed together and mirrored to surround, center and LFE.
-This control is used to attenuate sample for LFE speaker of this mix.
+This control is used to attenuate samples from the virtual stereo mixer.
+The result samples are forwarded to the subwoofer.
name='Tone Control - Switch',index=0
------------------------------------
-This control turns the tone control on or off. The samples for front, rear
-and center / LFE outputs are affected.
+This control turns the tone control on or off. The samples forwarded to
+the speaker outputs are affected.
name='Tone Control - Bass',index=0
----------------------------------
@@ -212,8 +213,7 @@ The closest value to pure signal is 20.
name='Master Playback Volume',index=0
-------------------------------------
-This control is used to attenuate samples for front, surround, center and
-LFE outputs.
+This control is used to attenuate samples forwarded to the speaker outputs.
name='IEC958 Optical Raw Playback Switch',index=0
-------------------------------------------------
@@ -303,69 +303,4 @@ The channel mapping is following:
MANUALS/PATENTS
===============
-ftp://opensource.creative.com/pub/doc
--------------------------------------
-
-Note that the site is defunct, but the documents are available
-from various other locations.
-
-LM4545.pdf
- AC97 Codec
-
-m2049.pdf
- The EMU10K1 Digital Audio Processor
-
-hog63.ps
- FX8010 - A DSP Chip Architecture for Audio Effects
-
-
-WIPO Patents
-------------
-
-WO 9901813 (A1)
- Audio Effects Processor with multiple asynchronous streams
- (Jan. 14, 1999)
-
-WO 9901814 (A1)
- Processor with Instruction Set for Audio Effects (Jan. 14, 1999)
-
-WO 9901953 (A1)
- Audio Effects Processor having Decoupled Instruction
- Execution and Audio Data Sequencing (Jan. 14, 1999)
-
-
-US Patents (https://www.uspto.gov/)
------------------------------------
-
-US 5925841
- Digital Sampling Instrument employing cache memory (Jul. 20, 1999)
-
-US 5928342
- Audio Effects Processor integrated on a single chip
- with a multiport memory onto which multiple asynchronous
- digital sound samples can be concurrently loaded
- (Jul. 27, 1999)
-
-US 5930158
- Processor with Instruction Set for Audio Effects (Jul. 27, 1999)
-
-US 6032235
- Memory initialization circuit (Tram) (Feb. 29, 2000)
-
-US 6138207
- Interpolation looping of audio samples in cache connected to
- system bus with prioritization and modification of bus transfers
- in accordance with loop ends and minimum block sizes
- (Oct. 24, 2000)
-
-US 6151670
- Method for conserving memory storage using a
- pool of short term memory registers
- (Nov. 21, 2000)
-
-US 6195715
- Interrupt control for multiple programs communicating with
- a common interrupt by associating programs to GP registers,
- defining interrupt register, polling GP registers, and invoking
- callback routine associated with defined interrupt register
- (Feb. 27, 2001)
+See sb-live-mixer.rst.
diff --git a/Documentation/sound/cards/emu-mixer.rst b/Documentation/sound/cards/emu-mixer.rst
new file mode 100644
index 000000000000..d87a6338d3d8
--- /dev/null
+++ b/Documentation/sound/cards/emu-mixer.rst
@@ -0,0 +1,226 @@
+==================================================
+E-MU Digital Audio System mixer / default DSP code
+==================================================
+
+This document covers the E-MU 0404/1010/1212/1616/1820 PCI/PCI-e/CardBus
+cards.
+
+These cards use regular EMU10K2 (SoundBlaster Audigy) chips, but with an
+alternative front-end geared towards semi-professional studio recording.
+
+This document is based on audigy-mixer.rst.
+
+
+Hardware compatibility
+======================
+
+The EMU10K2 chips have a very short capture FIFO, which makes recording
+unreliable if the card's PCI bus requests are not handled with the
+appropriate priority.
+This is the case on more modern motherboards, where the PCI bus is only a
+secondary peripheral, rather than the actual arbiter of device access.
+In particular, I got recording glitches during simultaneous playback on an
+Intel DP55 board (memory controller in the CPU), but had success with an
+Intel DP45 board (memory controller in the north bridge).
+
+The PCI Express variants of these cards (which have a PCI bridge on board,
+but are otherwise identical) may be less problematic.
+
+
+Driver capabilities
+===================
+
+This driver supports only 16-bit 44.1/48 kHz operation. The multi-channel
+device (see emu10k1-jack.rst) additionally supports 24-bit capture.
+
+A patchset to enhance the driver is available from `a GitHub repository
+<https://github.com/ossilator/linux/tree/ossis-emu10k1>`_.
+Its multi-channel device supports 24-bit for both playback and capture,
+and also supports full 88.2/96/176.4/192 kHz operation.
+It is not going to be upstreamed due to a fundamental disagreement about
+what constitutes a good user experience.
+
+
+Digital mixer controls
+======================
+
+Note that the controls work as attenuators: the maximum value is the neutral
+position leaving the signal unchanged. Note that if the same destination is
+mentioned in multiple controls, the signal is accumulated and can be clipped
+(set to maximal or minimal value without checking for overflow).
+
+Explanation of used abbreviations:
+
+DAC
+ digital to analog converter
+ADC
+ analog to digital converter
+LFE
+ low frequency effects (used as subwoofer signal)
+IEC958
+ S/PDIF
+FX-bus
+ the EMU10K2 chip has an effect bus containing 64 accumulators.
+ Each of the synthesizer voices can feed its output to these accumulators
+ and the DSP microcontroller can operate with the resulting sum.
+
+name='Clock Source',index=0
+---------------------------
+This control allows switching the word clock between interally generated
+44.1 or 48 kHz, or a number of external sources.
+
+Note: the sources for the 1616 CardBus card are unclear. Please report your
+findings.
+
+name='Clock Fallback',index=0
+-----------------------------
+This control determines the internal clock which the card switches to when
+the selected external clock source is/becomes invalid.
+
+name='DAC1 0202 14dB PAD',index=0, etc.
+---------------------------------------
+Output attenuation controls. Not available on 0404 cards.
+
+name='ADC1 14dB PAD 0202',index=0, etc.
+---------------------------------------
+Input attenuation controls. Not available on 0404 cards.
+
+name='Optical Output Mode',index=0
+----------------------------------
+Switches the TOSLINK output port between S/PDIF and ADAT.
+Not available on 0404 cards (fixed to S/PDIF).
+
+name='Optical Input Mode',index=0
+---------------------------------
+Switches the TOSLINK input port between S/PDIF and ADAT.
+Not available on 0404 cards (fixed to S/PDIF).
+
+name='PCM Front Playback Volume',index=0
+----------------------------------------
+This control is used to attenuate samples from left and right front PCM FX-bus
+accumulators. ALSA uses accumulators 8 and 9 for left and right front PCM
+samples for 5.1 playback. The result samples are forwarded to the DSP 0 & 1
+playback channels.
+
+name='PCM Surround Playback Volume',index=0
+-------------------------------------------
+This control is used to attenuate samples from left and right surround PCM FX-bus
+accumulators. ALSA uses accumulators 2 and 3 for left and right surround PCM
+samples for 5.1 playback. The result samples are forwarded to the DSP 2 & 3
+playback channels.
+
+name='PCM Side Playback Volume',index=0
+---------------------------------------
+This control is used to attenuate samples from left and right side PCM FX-bus
+accumulators. ALSA uses accumulators 14 and 15 for left and right side PCM
+samples for 7.1 playback. The result samples are forwarded to the DSP 6 & 7
+playback channels.
+
+name='PCM Center Playback Volume',index=0
+-----------------------------------------
+This control is used to attenuate samples from the center PCM FX-bus accumulator.
+ALSA uses accumulator 6 for center PCM samples for 5.1 playback. The result samples
+are forwarded to the DSP 4 playback channel.
+
+name='PCM LFE Playback Volume',index=0
+--------------------------------------
+This control is used to attenuate samples from the LFE PCM FX-bus accumulator.
+ALSA uses accumulator 7 for LFE PCM samples for 5.1 playback. The result samples
+are forwarded to the DSP 5 playback channel.
+
+name='PCM Playback Volume',index=0
+----------------------------------
+This control is used to attenuate samples from left and right PCM FX-bus
+accumulators. ALSA uses accumulators 0 and 1 for left and right PCM samples for
+stereo playback. The result samples are forwarded to the virtual stereo mixer.
+
+name='PCM Capture Volume',index=0
+---------------------------------
+This control is used to attenuate samples from left and right PCM FX-bus
+accumulators. ALSA uses accumulators 0 and 1 for left and right PCM.
+The result is forwarded to the standard capture PCM device.
+
+name='Music Playback Volume',index=0
+------------------------------------
+This control is used to attenuate samples from left and right MIDI FX-bus
+accumulators. ALSA uses accumulators 4 and 5 for left and right MIDI samples.
+The result samples are forwarded to the virtual stereo mixer.
+
+name='Music Capture Volume',index=0
+-----------------------------------
+These controls are used to attenuate samples from left and right MIDI FX-bus
+accumulator. ALSA uses accumulators 4 and 5 for left and right MIDI samples.
+The result is forwarded to the standard capture PCM device.
+
+name='Front Playback Volume',index=0
+------------------------------------
+This control is used to attenuate samples from the virtual stereo mixer.
+The result samples are forwarded to the DSP 0 & 1 playback channels.
+
+name='Surround Playback Volume',index=0
+---------------------------------------
+This control is used to attenuate samples from the virtual stereo mixer.
+The result samples are forwarded to the DSP 2 & 3 playback channels.
+
+name='Side Playback Volume',index=0
+-----------------------------------
+This control is used to attenuate samples from the virtual stereo mixer.
+The result samples are forwarded to the DSP 6 & 7 playback channels.
+
+name='Center Playback Volume',index=0
+-------------------------------------
+This control is used to attenuate samples from the virtual stereo mixer.
+The result samples are forwarded to the DSP 4 playback channel.
+
+name='LFE Playback Volume',index=0
+----------------------------------
+This control is used to attenuate samples from the virtual stereo mixer.
+The result samples are forwarded to the DSP 5 playback channel.
+
+name='Tone Control - Switch',index=0
+------------------------------------
+This control turns the tone control on or off. The samples forwarded to
+the DSP playback channels are affected.
+
+name='Tone Control - Bass',index=0
+----------------------------------
+This control sets the bass intensity. There is no neutral value!!
+When the tone control code is activated, the samples are always modified.
+The closest value to pure signal is 20.
+
+name='Tone Control - Treble',index=0
+------------------------------------
+This control sets the treble intensity. There is no neutral value!!
+When the tone control code is activated, the samples are always modified.
+The closest value to pure signal is 20.
+
+name='Master Playback Volume',index=0
+-------------------------------------
+This control is used to attenuate samples for all DSP playback channels.
+
+name='EMU Capture Volume',index=0
+----------------------------------
+This control is used to attenuate samples from the DSP 0 & 1 capture channels.
+The result is forwarded to the standard capture PCM device.
+
+name='DAC Left',index=0, etc.
+-----------------------------
+Select the source for the given physical audio output. These may be physical
+inputs, playback channels (DSP xx, specified as a decimal number), or silence.
+
+name='DSP x',index=0
+--------------------
+Select the source for the given capture channel (specified as a hexadecimal
+digit). Same options as for the physical audio outputs.
+
+
+PCM stream related controls
+===========================
+
+These controls are described in audigy-mixer.rst.
+
+
+MANUALS/PATENTS
+===============
+
+See sb-live-mixer.rst.
diff --git a/Documentation/sound/cards/index.rst b/Documentation/sound/cards/index.rst
index 49c1f2f688f8..e68bbb13c384 100644
--- a/Documentation/sound/cards/index.rst
+++ b/Documentation/sound/cards/index.rst
@@ -8,6 +8,7 @@ Card-Specific Information
cmipci
sb-live-mixer
audigy-mixer
+ emu-mixer
emu10k1-jack
via82xx-mixer
audiophile-usb
diff --git a/Documentation/sound/cards/sb-live-mixer.rst b/Documentation/sound/cards/sb-live-mixer.rst
index 4dd9bfe01bd8..27667f58aae1 100644
--- a/Documentation/sound/cards/sb-live-mixer.rst
+++ b/Documentation/sound/cards/sb-live-mixer.rst
@@ -61,61 +61,61 @@ FX-bus
``name='Wave Playback Volume',index=0``
---------------------------------------
-This control is used to attenuate samples for left and right PCM FX-bus
+This control is used to attenuate samples from left and right PCM FX-bus
accumulators. ALSA uses accumulators 0 and 1 for left and right PCM samples.
The result samples are forwarded to the front DAC PCM slots of the AC97 codec.
``name='Wave Surround Playback Volume',index=0``
------------------------------------------------
-This control is used to attenuate samples for left and right PCM FX-bus
+This control is used to attenuate samples from left and right PCM FX-bus
accumulators. ALSA uses accumulators 0 and 1 for left and right PCM samples.
The result samples are forwarded to the rear I2S DACs. These DACs operates
separately (they are not inside the AC97 codec).
``name='Wave Center Playback Volume',index=0``
----------------------------------------------
-This control is used to attenuate samples for left and right PCM FX-bus
+This control is used to attenuate samples from left and right PCM FX-bus
accumulators. ALSA uses accumulators 0 and 1 for left and right PCM samples.
The result is mixed to mono signal (single channel) and forwarded to
the ??rear?? right DAC PCM slot of the AC97 codec.
``name='Wave LFE Playback Volume',index=0``
-------------------------------------------
-This control is used to attenuate samples for left and right PCM FX-bus
+This control is used to attenuate samples from left and right PCM FX-bus
accumulators. ALSA uses accumulators 0 and 1 for left and right PCM.
The result is mixed to mono signal (single channel) and forwarded to
the ??rear?? left DAC PCM slot of the AC97 codec.
``name='Wave Capture Volume',index=0``, ``name='Wave Capture Switch',index=0``
------------------------------------------------------------------------------
-These controls are used to attenuate samples for left and right PCM FX-bus
+These controls are used to attenuate samples from left and right PCM FX-bus
accumulator. ALSA uses accumulators 0 and 1 for left and right PCM.
The result is forwarded to the ADC capture FIFO (thus to the standard capture
PCM device).
``name='Synth Playback Volume',index=0``
----------------------------------------
-This control is used to attenuate samples for left and right MIDI FX-bus
+This control is used to attenuate samples from left and right MIDI FX-bus
accumulators. ALSA uses accumulators 4 and 5 for left and right MIDI samples.
The result samples are forwarded to the front DAC PCM slots of the AC97 codec.
``name='Synth Capture Volume',index=0``, ``name='Synth Capture Switch',index=0``
--------------------------------------------------------------------------------
-These controls are used to attenuate samples for left and right MIDI FX-bus
-accumulator. ALSA uses accumulators 4 and 5 for left and right PCM.
+These controls are used to attenuate samples from left and right MIDI FX-bus
+accumulator. ALSA uses accumulators 4 and 5 for left and right MIDI samples.
The result is forwarded to the ADC capture FIFO (thus to the standard capture
PCM device).
``name='Surround Playback Volume',index=0``
-------------------------------------------
-This control is used to attenuate samples for left and right rear PCM FX-bus
+This control is used to attenuate samples from left and right rear PCM FX-bus
accumulators. ALSA uses accumulators 2 and 3 for left and right rear PCM samples.
The result samples are forwarded to the rear I2S DACs. These DACs operate
separately (they are not inside the AC97 codec).
``name='Surround Capture Volume',index=0``, ``name='Surround Capture Switch',index=0``
--------------------------------------------------------------------------------------
-These controls are used to attenuate samples for left and right rear PCM FX-bus
+These controls are used to attenuate samples from left and right rear PCM FX-bus
accumulators. ALSA uses accumulators 2 and 3 for left and right rear PCM samples.
The result is forwarded to the ADC capture FIFO (thus to the standard capture
PCM device).
@@ -134,7 +134,7 @@ to the ??rear?? left DAC PCM slot of the AC97 codec.
``name='AC97 Playback Volume',index=0``
---------------------------------------
-This control is used to attenuate samples for left and right front ADC PCM slots
+This control is used to attenuate samples from left and right front ADC PCM slots
of the AC97 codec. The result samples are forwarded to the front DAC PCM
slots of the AC97 codec.
@@ -145,7 +145,7 @@ slots of the AC97 codec.
``name='AC97 Capture Volume',index=0``
--------------------------------------
-This control is used to attenuate samples for left and right front ADC PCM slots
+This control is used to attenuate samples from left and right front ADC PCM slots
of the AC97 codec. The result is forwarded to the ADC capture FIFO (thus to
the standard capture PCM device).
diff --git a/Documentation/sound/designs/midi-2.0.rst b/Documentation/sound/designs/midi-2.0.rst
index 27d0d3dea1b0..45987f256b97 100644
--- a/Documentation/sound/designs/midi-2.0.rst
+++ b/Documentation/sound/designs/midi-2.0.rst
@@ -376,3 +376,191 @@ Sequencer API Extensions
name and attributes accordingly, and notifies the changes via the
announcement to the ALSA sequencer system port, similarly like the
normal port change notification.
+
+
+MIDI2 USB Gadget Function Driver
+================================
+
+The latest kernel contains the support for USB MIDI 2.0 gadget
+function driver, which can be used for prototyping and debugging MIDI
+2.0 features.
+
+`CONFIG_USB_GADGET`, `CONFIG_USB_CONFIGFS` and
+`CONFIG_USB_CONFIGFS_F_MIDI2` need to be enabled for the MIDI2 gadget
+driver.
+
+In addition, for using a gadget driver, you need a working UDC driver.
+In the example below, we use `dummy_hcd` driver (enabled via
+`CONFIG_USB_DUMMY_HCD`) that is available on PC and VM for debugging
+purpose. There are other UDC drivers depending on the platform, and
+those can be used for a real device, instead, too.
+
+At first, on a system to run the gadget, load `libcomposite` module::
+
+ % modprobe libcomposite
+
+and you'll have `usb_gadget` subdirectory under configfs space
+(typically `/sys/kernel/config` on modern OS). Then create a gadget
+instance and add configurations there, for example::
+
+ % cd /sys/kernel/config
+ % mkdir usb_gadget/g1
+
+ % cd usb_gadget/g1
+ % mkdir configs/c.1
+ % mkdir functions/midi2.usb0
+
+ % echo 0x0004 > idProduct
+ % echo 0x17b3 > idVendor
+ % mkdir strings/0x409
+ % echo "ACME Enterprises" > strings/0x409/manufacturer
+ % echo "ACMESynth" > strings/0x409/product
+ % echo "ABCD12345" > strings/0x409/serialnumber
+
+ % mkdir configs/c.1/strings/0x409
+ % echo "Monosynth" > configs/c.1/strings/0x409/configuration
+ % echo 120 > configs/c.1/MaxPower
+
+At this point, there must be a subdirectory `ep.0`, and that is the
+configuration for a UMP Endpoint. You can fill the Endpoint
+information like::
+
+ % echo "ACMESynth" > functions/midi2.usb0/iface_name
+ % echo "ACMESynth" > functions/midi2.usb0/ep.0/ep_name
+ % echo "ABCD12345" > functions/midi2.usb0/ep.0/product_id
+ % echo 0x0123 > functions/midi2.usb0/ep.0/family
+ % echo 0x4567 > functions/midi2.usb0/ep.0/model
+ % echo 0x123456 > functions/midi2.usb0/ep.0/manufacturer
+ % echo 0x12345678 > functions/midi2.usb0/ep.0/sw_revision
+
+The default MIDI protocol can be set either 1 or 2::
+
+ % echo 2 > functions/midi2.usb0/ep.0/protocol
+
+And, you can find a subdirectory `block.0` under this Endpoint
+subdirectory. This defines the Function Block information::
+
+ % echo "Monosynth" > functions/midi2.usb0/ep.0/block.0/name
+ % echo 0 > functions/midi2.usb0/ep.0/block.0/first_group
+ % echo 1 > functions/midi2.usb0/ep.0/block.0/num_groups
+
+Finally, link the configuration and enable it::
+
+ % ln -s functions/midi2.usb0 configs/c.1
+ % echo dummy_udc.0 > UDC
+
+where `dummy_udc.0` is an example case and it differs depending on the
+system. You can find the UDC instances in `/sys/class/udc` and pass
+the found name instead::
+
+ % ls /sys/class/udc
+ dummy_udc.0
+
+Now, the MIDI 2.0 gadget device is enabled, and the gadget host
+creates a new sound card instance containing a UMP rawmidi device by
+`f_midi2` driver::
+
+ % cat /proc/asound/cards
+ ....
+ 1 [Gadget ]: f_midi2 - MIDI 2.0 Gadget
+ MIDI 2.0 Gadget
+
+And on the connected host, a similar card should appear, too, but with
+the card and device names given in the configfs above::
+
+ % cat /proc/asound/cards
+ ....
+ 2 [ACMESynth ]: USB-Audio - ACMESynth
+ ACME Enterprises ACMESynth at usb-dummy_hcd.0-1, high speed
+
+You can play a MIDI file on the gadget side::
+
+ % aplaymidi -p 20:1 to_host.mid
+
+and this will appear as an input from a MIDI device on the connected
+host::
+
+ % aseqdump -p 20:0 -u 2
+
+Vice versa, a playback on the connected host will work as an input on
+the gadget, too.
+
+Each Function Block may have different direction and UI-hint,
+specified via `direction` and `ui_hint` attributes.
+Passing `1` is for input-only, `2` for out-only and `3` for
+bidirectional (the default value). For example::
+
+ % echo 2 > functions/midi2.usb0/ep.0/block.0/direction
+ % echo 2 > functions/midi2.usb0/ep.0/block.0/ui_hint
+
+When you need more than one Function Blocks, you can create
+subdirectories `block.1`, `block.2`, etc dynamically, and configure
+them in the configuration procedure above before linking.
+For example, to create a second Function Block for a keyboard::
+
+ % mkdir functions/midi2.usb0/ep.0/block.1
+ % echo "Keyboard" > functions/midi2.usb0/ep.0/block.1/name
+ % echo 1 > functions/midi2.usb0/ep.0/block.1/first_group
+ % echo 1 > functions/midi2.usb0/ep.0/block.1/num_groups
+ % echo 1 > functions/midi2.usb0/ep.0/block.1/direction
+ % echo 1 > functions/midi2.usb0/ep.0/block.1/ui_hint
+
+The `block.*` subdirectories can be removed dynamically, too (except
+for `block.0` which is persistent).
+
+For assigning a Function Block for MIDI 1.0 I/O, set up in `is_midi1`
+attribute. 1 is for MIDI 1.0, and 2 is for MIDI 1.0 with low speed
+connection::
+
+ % echo 2 > functions/midi2.usb0/ep.0/block.1/is_midi1
+
+For disabling the processing of UMP Stream messages in the gadget
+driver, pass `0` to `process_ump` attribute in the top-level config::
+
+ % echo 0 > functions/midi2.usb0/process_ump
+
+The MIDI 1.0 interface at altset 0 is supported by the gadget driver,
+too. When MIDI 1.0 interface is selected by the connected host, the
+UMP I/O on the gadget is translated from/to USB MIDI 1.0 packets
+accordingly while the gadget driver keeps communicating with the
+user-space over UMP rawmidi.
+
+MIDI 1.0 ports are set up from the config in each Function Block.
+For example::
+
+ % echo 0 > functions/midi2.usb0/ep.0/block.0/midi1_first_group
+ % echo 1 > functions/midi2.usb0/ep.0/block.0/midi1_num_groups
+
+The configuration above will enable the Group 1 (the index 0) for MIDI
+1.0 interface. Note that those groups must be in the groups defined
+for the Function Block itself.
+
+The gadget driver supports more than one UMP Endpoints, too.
+Similarly like the Function Blocks, you can create a new subdirectory
+`ep.1` (but under the card top-level config) to enable a new Endpoint::
+
+ % mkdir functions/midi2.usb0/ep.1
+
+and create a new Function Block there. For example, to create 4
+Groups for the Function Block of this new Endpoint::
+
+ % mkdir functions/midi2.usb0/ep.1/block.0
+ % echo 4 > functions/midi2.usb0/ep.1/block.0/num_groups
+
+Now, you'll have 4 rawmidi devices in total: the first two are UMP
+rawmidi devices for Endpoint 0 and Endpoint 1, and other two for the
+legacy MIDI 1.0 rawmidi devices corresponding to both EP 0 and EP 1.
+
+The current altsetting on the gadget can be informed via a control
+element "Operation Mode" with `RAWMIDI` iface. e.g. you can read it
+via `amixer` program running on the gadget host like::
+
+ % amixer -c1 cget iface=RAWMIDI,name='Operation Mode'
+ ; type=INTEGER,access=r--v----,values=1,min=0,max=2,step=0
+ : values=2
+
+The value (shown in the second returned line with `: values=`)
+indicates 1 for MIDI 1.0 (altset 0), 2 for MIDI 2.0 (altset 1) and 0
+for unset.
+
+As of now, the configurations can't be changed after binding.
diff --git a/Documentation/sound/kernel-api/writing-an-alsa-driver.rst b/Documentation/sound/kernel-api/writing-an-alsa-driver.rst
index 4335c98b3d82..cd421856409e 100644
--- a/Documentation/sound/kernel-api/writing-an-alsa-driver.rst
+++ b/Documentation/sound/kernel-api/writing-an-alsa-driver.rst
@@ -2018,8 +2018,8 @@ sleeping poll threads, etc.
This callback is also atomic by default.
-copy_user, copy_kernel and fill_silence ops
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+copy and fill_silence ops
+~~~~~~~~~~~~~~~~~~~~~~~~~
These callbacks are not mandatory, and can be omitted in most cases.
These callbacks are used when the hardware buffer cannot be in the
@@ -3444,8 +3444,8 @@ external hardware buffer in interrupts (or in tasklets, preferably).
The first case works fine if the external hardware buffer is large
enough. This method doesn't need any extra buffers and thus is more
-efficient. You need to define the ``copy_user`` and ``copy_kernel``
-callbacks for the data transfer, in addition to the ``fill_silence``
+efficient. You need to define the ``copy`` callback
+for the data transfer, in addition to the ``fill_silence``
callback for playback. However, there is a drawback: it cannot be
mmapped. The examples are GUS's GF1 PCM or emu8000's wavetable PCM.
@@ -3458,22 +3458,22 @@ Another case is when the chip uses a PCI memory-map region for the
buffer instead of the host memory. In this case, mmap is available only
on certain architectures like the Intel one. In non-mmap mode, the data
cannot be transferred as in the normal way. Thus you need to define the
-``copy_user``, ``copy_kernel`` and ``fill_silence`` callbacks as well,
+``copy`` and ``fill_silence`` callbacks as well,
as in the cases above. Examples are found in ``rme32.c`` and
``rme96.c``.
-The implementation of the ``copy_user``, ``copy_kernel`` and
+The implementation of the ``copy`` and
``silence`` callbacks depends upon whether the hardware supports
-interleaved or non-interleaved samples. The ``copy_user`` callback is
+interleaved or non-interleaved samples. The ``copy`` callback is
defined like below, a bit differently depending on whether the direction
is playback or capture::
- static int playback_copy_user(struct snd_pcm_substream *substream,
+ static int playback_copy(struct snd_pcm_substream *substream,
int channel, unsigned long pos,
- void __user *src, unsigned long count);
- static int capture_copy_user(struct snd_pcm_substream *substream,
+ struct iov_iter *src, unsigned long count);
+ static int capture_copy(struct snd_pcm_substream *substream,
int channel, unsigned long pos,
- void __user *dst, unsigned long count);
+ struct iov_iter *dst, unsigned long count);
In the case of interleaved samples, the second argument (``channel``) is
not used. The third argument (``pos``) specifies the position in bytes.
@@ -3490,18 +3490,17 @@ of data (``count``) at the specified pointer (``src``) to the specified
offset (``pos``) in the hardware buffer. When coded like memcpy-like
way, the copy would look like::
- my_memcpy_from_user(my_buffer + pos, src, count);
+ my_memcpy_from_iter(my_buffer + pos, src, count);
For the capture direction, you copy the given amount of data (``count``)
at the specified offset (``pos``) in the hardware buffer to the
specified pointer (``dst``)::
- my_memcpy_to_user(dst, my_buffer + pos, count);
+ my_memcpy_to_iter(dst, my_buffer + pos, count);
-Here the functions are named ``from_user`` and ``to_user`` because
-it's the user-space buffer that is passed to these callbacks. That
-is, the callback is supposed to copy data from/to the user-space
-directly to/from the hardware buffer.
+The given ``src`` or ``dst`` a struct iov_iter pointer containing the
+pointer and the size. Use the existing helpers to copy or access the
+data as defined in ``linux/uio.h``.
Careful readers might notice that these callbacks receive the
arguments in bytes, not in frames like other callbacks. It's because
@@ -3519,25 +3518,6 @@ the given user-space buffer, but only for the given channel. For
details, please check ``isa/gus/gus_pcm.c`` or ``pci/rme9652/rme9652.c``
as examples.
-The above callbacks are the copies from/to the user-space buffer. There
-are some cases where we want to copy from/to the kernel-space buffer
-instead. In such a case, the ``copy_kernel`` callback is called. It'd
-look like::
-
- static int playback_copy_kernel(struct snd_pcm_substream *substream,
- int channel, unsigned long pos,
- void *src, unsigned long count);
- static int capture_copy_kernel(struct snd_pcm_substream *substream,
- int channel, unsigned long pos,
- void *dst, unsigned long count);
-
-As found easily, the only difference is that the buffer pointer is
-without a ``__user`` prefix; that is, a kernel-buffer pointer is passed
-in the fourth argument. Correspondingly, the implementation would be
-a version without the user-copy, such as::
-
- my_memcpy(my_buffer + pos, src, count);
-
Usually for the playback, another callback ``fill_silence`` is
defined. It's implemented in a similar way as the copy callbacks
above::
@@ -3545,10 +3525,10 @@ above::
static int silence(struct snd_pcm_substream *substream, int channel,
unsigned long pos, unsigned long count);
-The meanings of arguments are the same as in the ``copy_user`` and
-``copy_kernel`` callbacks, although there is no buffer pointer
+The meanings of arguments are the same as in the ``copy`` callback,
+although there is no buffer pointer
argument. In the case of interleaved samples, the channel argument has
-no meaning, as for the ``copy_*`` callbacks.
+no meaning, as for the ``copy`` callback.
The role of the ``fill_silence`` callback is to set the given amount
(``count``) of silence data at the specified offset (``pos``) in the
diff --git a/drivers/media/pci/solo6x10/solo6x10-g723.c b/drivers/media/pci/solo6x10/solo6x10-g723.c
index 6cebad665565..1db9f40ee0c0 100644
--- a/drivers/media/pci/solo6x10/solo6x10-g723.c
+++ b/drivers/media/pci/solo6x10/solo6x10-g723.c
@@ -204,9 +204,9 @@ static snd_pcm_uframes_t snd_solo_pcm_pointer(struct snd_pcm_substream *ss)
return idx * G723_FRAMES_PER_PAGE;
}
-static int snd_solo_pcm_copy_user(struct snd_pcm_substream *ss, int channel,
- unsigned long pos, void __user *dst,
- unsigned long count)
+static int snd_solo_pcm_copy(struct snd_pcm_substream *ss, int channel,
+ unsigned long pos, struct iov_iter *dst,
+ unsigned long count)
{
struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss);
struct solo_dev *solo_dev = solo_pcm->solo_dev;
@@ -223,35 +223,9 @@ static int snd_solo_pcm_copy_user(struct snd_pcm_substream *ss, int channel,
if (err)
return err;
- if (copy_to_user(dst, solo_pcm->g723_buf, G723_PERIOD_BYTES))
+ if (copy_to_iter(solo_pcm->g723_buf, G723_PERIOD_BYTES, dst) !=
+ G723_PERIOD_BYTES)
return -EFAULT;
- dst += G723_PERIOD_BYTES;
- }
-
- return 0;
-}
-
-static int snd_solo_pcm_copy_kernel(struct snd_pcm_substream *ss, int channel,
- unsigned long pos, void *dst,
- unsigned long count)
-{
- struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss);
- struct solo_dev *solo_dev = solo_pcm->solo_dev;
- int err, i;
-
- for (i = 0; i < (count / G723_FRAMES_PER_PAGE); i++) {
- int page = (pos / G723_FRAMES_PER_PAGE) + i;
-
- err = solo_p2m_dma_t(solo_dev, 0, solo_pcm->g723_dma,
- SOLO_G723_EXT_ADDR(solo_dev) +
- (page * G723_PERIOD_BLOCK) +
- (ss->number * G723_PERIOD_BYTES),
- G723_PERIOD_BYTES, 0, 0);
- if (err)
- return err;
-
- memcpy(dst, solo_pcm->g723_buf, G723_PERIOD_BYTES);
- dst += G723_PERIOD_BYTES;
}
return 0;
@@ -263,8 +237,7 @@ static const struct snd_pcm_ops snd_solo_pcm_ops = {
.prepare = snd_solo_pcm_prepare,
.trigger = snd_solo_pcm_trigger,
.pointer = snd_solo_pcm_pointer,
- .copy_user = snd_solo_pcm_copy_user,
- .copy_kernel = snd_solo_pcm_copy_kernel,
+ .copy = snd_solo_pcm_copy,
};
static int snd_solo_capture_volume_info(struct snd_kcontrol *kcontrol,
diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c
index 72ace74ea605..2f05e761fb9a 100644
--- a/drivers/staging/greybus/audio_codec.c
+++ b/drivers/staging/greybus/audio_codec.c
@@ -807,7 +807,6 @@ int gbaudio_register_module(struct gbaudio_module_info *module)
{
int ret;
struct snd_soc_component *comp;
- struct snd_card *card;
struct gbaudio_jack *jack = NULL;
if (!gbcodec) {
@@ -816,21 +815,20 @@ int gbaudio_register_module(struct gbaudio_module_info *module)
}
comp = gbcodec->component;
- card = comp->card->snd_card;
- down_write(&card->controls_rwsem);
+ mutex_lock(&gbcodec->register_mutex);
if (module->num_dais) {
dev_err(gbcodec->dev,
"%d:DAIs not supported via gbcodec driver\n",
module->num_dais);
- up_write(&card->controls_rwsem);
+ mutex_unlock(&gbcodec->register_mutex);
return -EINVAL;
}
ret = gbaudio_init_jack(module, comp->card);
if (ret) {
- up_write(&card->controls_rwsem);
+ mutex_unlock(&gbcodec->register_mutex);
return ret;
}
@@ -867,7 +865,7 @@ int gbaudio_register_module(struct gbaudio_module_info *module)
ret = snd_soc_dapm_new_widgets(comp->card);
dev_dbg(comp->dev, "Registered %s module\n", module->name);
- up_write(&card->controls_rwsem);
+ mutex_unlock(&gbcodec->register_mutex);
return ret;
}
EXPORT_SYMBOL(gbaudio_register_module);
@@ -935,13 +933,12 @@ static void gbaudio_codec_cleanup(struct gbaudio_module_info *module)
void gbaudio_unregister_module(struct gbaudio_module_info *module)
{
struct snd_soc_component *comp = gbcodec->component;
- struct snd_card *card = comp->card->snd_card;
struct gbaudio_jack *jack, *n;
int mask;
dev_dbg(comp->dev, "Unregister %s module\n", module->name);
- down_write(&card->controls_rwsem);
+ mutex_lock(&gbcodec->register_mutex);
mutex_lock(&gbcodec->lock);
gbaudio_codec_cleanup(module);
list_del(&module->list);
@@ -978,10 +975,8 @@ void gbaudio_unregister_module(struct gbaudio_module_info *module)
dev_dbg(comp->dev, "Removing %d controls\n",
module->num_controls);
/* release control semaphore */
- up_write(&card->controls_rwsem);
gbaudio_remove_component_controls(comp, module->controls,
module->num_controls);
- down_write(&card->controls_rwsem);
}
if (module->dapm_widgets) {
dev_dbg(comp->dev, "Removing %d widgets\n",
@@ -992,7 +987,7 @@ void gbaudio_unregister_module(struct gbaudio_module_info *module)
dev_dbg(comp->dev, "Unregistered %s module\n", module->name);
- up_write(&card->controls_rwsem);
+ mutex_unlock(&gbcodec->register_mutex);
}
EXPORT_SYMBOL(gbaudio_unregister_module);
@@ -1012,6 +1007,7 @@ static int gbcodec_probe(struct snd_soc_component *comp)
info->dev = comp->dev;
INIT_LIST_HEAD(&info->module_list);
mutex_init(&info->lock);
+ mutex_init(&info->register_mutex);
INIT_LIST_HEAD(&info->dai_list);
/* init dai_list used to maintain runtime stream info */
diff --git a/drivers/staging/greybus/audio_codec.h b/drivers/staging/greybus/audio_codec.h
index ce15e800e607..f3f7a7ec6be4 100644
--- a/drivers/staging/greybus/audio_codec.h
+++ b/drivers/staging/greybus/audio_codec.h
@@ -71,6 +71,7 @@ struct gbaudio_codec_info {
/* to maintain runtime stream params for each DAI */
struct list_head dai_list;
struct mutex lock;
+ struct mutex register_mutex;
};
struct gbaudio_widget {
diff --git a/drivers/staging/greybus/audio_helper.c b/drivers/staging/greybus/audio_helper.c
index 223987616e07..97ce5b9ad7fd 100644
--- a/drivers/staging/greybus/audio_helper.c
+++ b/drivers/staging/greybus/audio_helper.c
@@ -149,7 +149,6 @@ static int gbaudio_remove_controls(struct snd_card *card, struct device *dev,
for (i = 0; i < num_controls; i++) {
const struct snd_kcontrol_new *control = &controls[i];
struct snd_ctl_elem_id id;
- struct snd_kcontrol *kctl;
if (prefix)
snprintf(id.name, sizeof(id.name), "%s %s", prefix,
@@ -161,17 +160,10 @@ static int gbaudio_remove_controls(struct snd_card *card, struct device *dev,
id.device = control->device;
id.subdevice = control->subdevice;
id.index = control->index;
- kctl = snd_ctl_find_id(card, &id);
- if (!kctl) {
- dev_err(dev, "Failed to find %s\n", control->name);
- continue;
- }
- err = snd_ctl_remove(card, kctl);
- if (err < 0) {
+ err = snd_ctl_remove_id(card, &id);
+ if (err < 0)
dev_err(dev, "%d: Failed to remove %s\n", err,
control->name);
- continue;
- }
}
return 0;
}
@@ -181,11 +173,7 @@ int gbaudio_remove_component_controls(struct snd_soc_component *component,
unsigned int num_controls)
{
struct snd_card *card = component->card->snd_card;
- int err;
- down_write(&card->controls_rwsem);
- err = gbaudio_remove_controls(card, component->dev, controls,
- num_controls, component->name_prefix);
- up_write(&card->controls_rwsem);
- return err;
+ return gbaudio_remove_controls(card, component->dev, controls,
+ num_controls, component->name_prefix);
}
diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h
index d91289c6f00e..bcf872c17dd3 100644
--- a/include/sound/compress_driver.h
+++ b/include/sound/compress_driver.h
@@ -148,7 +148,7 @@ struct snd_compr_ops {
*/
struct snd_compr {
const char *name;
- struct device dev;
+ struct device *dev;
struct snd_compr_ops *ops;
void *private_data;
struct snd_card *card;
diff --git a/include/sound/control.h b/include/sound/control.h
index cc3dcc6cfb0f..9a4f4f7138da 100644
--- a/include/sound/control.h
+++ b/include/sound/control.h
@@ -140,8 +140,32 @@ int snd_ctl_remove_id(struct snd_card * card, struct snd_ctl_elem_id *id);
int snd_ctl_rename_id(struct snd_card * card, struct snd_ctl_elem_id *src_id, struct snd_ctl_elem_id *dst_id);
void snd_ctl_rename(struct snd_card *card, struct snd_kcontrol *kctl, const char *name);
int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id, int active);
-struct snd_kcontrol *snd_ctl_find_numid(struct snd_card * card, unsigned int numid);
-struct snd_kcontrol *snd_ctl_find_id(struct snd_card * card, struct snd_ctl_elem_id *id);
+struct snd_kcontrol *snd_ctl_find_numid_locked(struct snd_card *card, unsigned int numid);
+struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card, unsigned int numid);
+struct snd_kcontrol *snd_ctl_find_id_locked(struct snd_card *card, const struct snd_ctl_elem_id *id);
+struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card, const struct snd_ctl_elem_id *id);
+
+/**
+ * snd_ctl_find_id_mixer - find the control instance with the given name string
+ * @card: the card instance
+ * @name: the name string
+ *
+ * Finds the control instance with the given name and
+ * @SNDRV_CTL_ELEM_IFACE_MIXER. Other fields are set to zero.
+ *
+ * This is merely a wrapper to snd_ctl_find_id().
+ *
+ * Return: The pointer of the instance if found, or %NULL if not.
+ */
+static inline struct snd_kcontrol *
+snd_ctl_find_id_mixer(struct snd_card *card, const char *name)
+{
+ struct snd_ctl_elem_id id = {};
+
+ id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ strscpy(id.name, name, sizeof(id.name));
+ return snd_ctl_find_id(card, &id);
+}
int snd_ctl_create(struct snd_card *card);
@@ -238,6 +262,9 @@ snd_ctl_add_follower(struct snd_kcontrol *master, struct snd_kcontrol *follower)
return _snd_ctl_add_follower(master, follower, 0);
}
+int snd_ctl_add_followers(struct snd_card *card, struct snd_kcontrol *master,
+ const char * const *list);
+
/**
* snd_ctl_add_follower_uncached - Add a virtual follower control
* @master: vmaster element
diff --git a/include/sound/core.h b/include/sound/core.h
index f6e0dd648b80..dfef0c9d4b9f 100644
--- a/include/sound/core.h
+++ b/include/sound/core.h
@@ -96,7 +96,7 @@ struct snd_card {
private data */
struct list_head devices; /* devices */
- struct device ctl_dev; /* control device */
+ struct device *ctl_dev; /* control device */
unsigned int last_numid; /* last used numeric ID */
struct rw_semaphore controls_rwsem; /* controls lock (list and values) */
rwlock_t ctl_files_rwlock; /* ctl_files list lock */
@@ -239,7 +239,7 @@ extern struct dentry *sound_debugfs_root;
void snd_request_card(int card);
-void snd_device_initialize(struct device *dev, struct snd_card *card);
+int snd_device_alloc(struct device **dev_p, struct snd_card *card);
int snd_register_device(int type, struct snd_card *card, int dev,
const struct file_operations *f_ops,
diff --git a/include/sound/cs35l41.h b/include/sound/cs35l41.h
index 7239d943942c..1bf757901d02 100644
--- a/include/sound/cs35l41.h
+++ b/include/sound/cs35l41.h
@@ -829,6 +829,7 @@ enum cs35l41_cspl_mbox_cmd {
CSPL_MBOX_CMD_STOP_PRE_REINIT = 4,
CSPL_MBOX_CMD_HIBERNATE = 5,
CSPL_MBOX_CMD_OUT_OF_HIBERNATE = 6,
+ CSPL_MBOX_CMD_SPK_OUT_ENABLE = 7,
CSPL_MBOX_CMD_UNKNOWN_CMD = -1,
CSPL_MBOX_CMD_INVALID_SEQUENCE = -2,
};
@@ -901,7 +902,7 @@ int cs35l41_exit_hibernate(struct device *dev, struct regmap *regmap);
int cs35l41_init_boost(struct device *dev, struct regmap *regmap,
struct cs35l41_hw_cfg *hw_cfg);
bool cs35l41_safe_reset(struct regmap *regmap, enum cs35l41_boost_type b_type);
-int cs35l41_global_enable(struct regmap *regmap, enum cs35l41_boost_type b_type, int enable,
- struct completion *pll_lock);
+int cs35l41_global_enable(struct device *dev, struct regmap *regmap, enum cs35l41_boost_type b_type,
+ int enable, struct completion *pll_lock, bool firmware_running);
#endif /* __CS35L41_H */
diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h
index 2df54cf02cb3..c9a8bce9a785 100644
--- a/include/sound/dmaengine_pcm.h
+++ b/include/sound/dmaengine_pcm.h
@@ -142,7 +142,7 @@ struct snd_dmaengine_pcm_config {
struct snd_pcm_substream *substream);
int (*process)(struct snd_pcm_substream *substream,
int channel, unsigned long hwoff,
- void *buf, unsigned long bytes);
+ struct iov_iter *buf, unsigned long bytes);
dma_filter_fn compat_filter_fn;
struct device *dma_dev;
const char *chan_names[SNDRV_PCM_STREAM_LAST + 1];
diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h
index 386a5f3be3e0..1af9e6819392 100644
--- a/include/sound/emu10k1.h
+++ b/include/sound/emu10k1.h
@@ -902,6 +902,11 @@ SUB_REG_NC(A_EHC, A_I2S_CAPTURE_RATE, 0x00000e00) /* This sets the capture PCM
#define A_TTDA 0x7a /* Tank Table DMA Address */
#define A_TTDD 0x7b /* Tank Table DMA Data */
+// In A_FXRT1 & A_FXRT2, the 0x80 bit of each byte completely disables the
+// filter (CVCF_CURRENTFILTER) for the corresponding channel. There is no
+// effect on the volume (CVCF_CURRENTVOLUME) or the interpolator's filter
+// (CCCA_INTERPROM_MASK).
+
#define A_FXRT2 0x7c
#define A_FXRT_CHANNELE 0x0000003f /* Effects send bus number for channel's effects send E */
#define A_FXRT_CHANNELF 0x00003f00 /* Effects send bus number for channel's effects send F */
@@ -914,8 +919,6 @@ SUB_REG_NC(A_EHC, A_I2S_CAPTURE_RATE, 0x00000e00) /* This sets the capture PCM
#define A_FXSENDAMOUNT_G_MASK 0x0000FF00
#define A_FXSENDAMOUNT_H_MASK 0x000000FF
-/* 0x7c, 0x7e "high bit is used for filtering" */
-
/* The send amounts for this one are the same as used with the emu10k1 */
#define A_FXRT1 0x7e
#define A_FXRT_CHANNELA 0x0000003f
@@ -992,6 +995,9 @@ SUB_REG_NC(A_EHC, A_I2S_CAPTURE_RATE, 0x00000e00) /* This sets the capture PCM
#define EMU_HANA_WCLOCK_4X 0x10
#define EMU_HANA_WCLOCK_MULT_RESERVED 0x18
+// If the selected external clock source is/becomes invalid or incompatible
+// with the clock multiplier, the clock source is reset to this value, and
+// a WCLK_CHANGED interrupt is raised.
#define EMU_HANA_DEFCLOCK 0x06 /* 000000x 1 bits Default Word Clock */
#define EMU_HANA_DEFCLOCK_48K 0x00
#define EMU_HANA_DEFCLOCK_44_1K 0x01
@@ -1523,10 +1529,10 @@ struct snd_emu10k1_pcm_mixer {
((route[0] | (route[1] << 4) | (route[2] << 8) | (route[3] << 12)) << 16)
#define snd_emu10k1_compose_audigy_fxrt1(route) \
-((unsigned int)route[0] | ((unsigned int)route[1] << 8) | ((unsigned int)route[2] << 16) | ((unsigned int)route[3] << 24))
+((unsigned int)route[0] | ((unsigned int)route[1] << 8) | ((unsigned int)route[2] << 16) | ((unsigned int)route[3] << 24) | 0x80808080)
#define snd_emu10k1_compose_audigy_fxrt2(route) \
-((unsigned int)route[4] | ((unsigned int)route[5] << 8) | ((unsigned int)route[6] << 16) | ((unsigned int)route[7] << 24))
+((unsigned int)route[4] | ((unsigned int)route[5] << 8) | ((unsigned int)route[6] << 16) | ((unsigned int)route[7] << 24) | 0x80808080)
#define snd_emu10k1_compose_audigy_sendamounts(vol) \
(((unsigned int)vol[4] << 24) | ((unsigned int)vol[5] << 16) | ((unsigned int)vol[6] << 8) | (unsigned int)vol[7])
@@ -1678,8 +1684,8 @@ struct snd_emu1010 {
unsigned int clock_fallback;
unsigned int optical_in; /* 0:SPDIF, 1:ADAT */
unsigned int optical_out; /* 0:SPDIF, 1:ADAT */
- struct delayed_work firmware_work;
- u32 last_reg;
+ struct work_struct firmware_work;
+ struct work_struct clock_work;
};
struct snd_emu10k1 {
@@ -1754,6 +1760,7 @@ struct snd_emu10k1 {
struct snd_kcontrol *ctl_efx_send_routing;
struct snd_kcontrol *ctl_efx_send_volume;
struct snd_kcontrol *ctl_efx_attn;
+ struct snd_kcontrol *ctl_clock_source;
void (*hwvol_interrupt)(struct snd_emu10k1 *emu, unsigned int status);
void (*capture_interrupt)(struct snd_emu10k1 *emu, unsigned int status);
@@ -1761,6 +1768,7 @@ struct snd_emu10k1 {
void (*capture_efx_interrupt)(struct snd_emu10k1 *emu, unsigned int status);
void (*spdif_interrupt)(struct snd_emu10k1 *emu, unsigned int status);
void (*dsp_interrupt)(struct snd_emu10k1 *emu);
+ void (*gpio_interrupt)(struct snd_emu10k1 *emu);
void (*p16v_interrupt)(struct snd_emu10k1 *emu);
struct snd_pcm_substream *pcm_capture_substream;
diff --git a/include/sound/hwdep.h b/include/sound/hwdep.h
index 8d6cdb254039..b0da633184cd 100644
--- a/include/sound/hwdep.h
+++ b/include/sound/hwdep.h
@@ -53,7 +53,7 @@ struct snd_hwdep {
wait_queue_head_t open_wait;
void *private_data;
void (*private_free) (struct snd_hwdep *hwdep);
- struct device dev;
+ struct device *dev;
struct mutex open_mutex;
int used; /* reference counter */
diff --git a/include/sound/info.h b/include/sound/info.h
index 7c13bf52cc81..adbc506860d6 100644
--- a/include/sound/info.h
+++ b/include/sound/info.h
@@ -118,8 +118,6 @@ struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card,
const char *name,
struct snd_info_entry *parent);
void snd_info_free_entry(struct snd_info_entry *entry);
-int snd_info_store_text(struct snd_info_entry *entry);
-int snd_info_restore_text(struct snd_info_entry *entry);
int snd_info_card_create(struct snd_card *card);
int snd_info_card_register(struct snd_card *card);
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 19f564606ac4..2a815373dac1 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -16,6 +16,7 @@
#include <linux/bitops.h>
#include <linux/pm_qos.h>
#include <linux/refcount.h>
+#include <linux/uio.h>
#define snd_pcm_substream_chip(substream) ((substream)->private_data)
#define snd_pcm_chip(pcm) ((pcm)->private_data)
@@ -68,11 +69,8 @@ struct snd_pcm_ops {
struct snd_pcm_audio_tstamp_report *audio_tstamp_report);
int (*fill_silence)(struct snd_pcm_substream *substream, int channel,
unsigned long pos, unsigned long bytes);
- int (*copy_user)(struct snd_pcm_substream *substream, int channel,
- unsigned long pos, void __user *buf,
- unsigned long bytes);
- int (*copy_kernel)(struct snd_pcm_substream *substream, int channel,
- unsigned long pos, void *buf, unsigned long bytes);
+ int (*copy)(struct snd_pcm_substream *substream, int channel,
+ unsigned long pos, struct iov_iter *iter, unsigned long bytes);
struct page *(*page)(struct snd_pcm_substream *substream,
unsigned long offset);
int (*mmap)(struct snd_pcm_substream *substream, struct vm_area_struct *vma);
@@ -510,7 +508,7 @@ struct snd_pcm_str {
#endif
#endif
struct snd_kcontrol *chmap_kctl; /* channel-mapping controls */
- struct device dev;
+ struct device *dev;
};
struct snd_pcm {
@@ -1556,6 +1554,11 @@ static inline u64 pcm_format_to_bits(snd_pcm_format_t pcm_format)
#define pcm_dbg(pcm, fmt, args...) \
dev_dbg((pcm)->card->dev, fmt, ##args)
+/* helpers for copying between iov_iter and iomem */
+int copy_to_iter_fromio(struct iov_iter *itert, const void __iomem *src,
+ size_t count);
+int copy_from_iter_toio(void __iomem *dst, struct iov_iter *iter, size_t count);
+
struct snd_pcm_status64 {
snd_pcm_state_t state; /* stream state */
u8 rsvd[4];
diff --git a/include/sound/rawmidi.h b/include/sound/rawmidi.h
index b0197b1d1fe4..f31cabf0158c 100644
--- a/include/sound/rawmidi.h
+++ b/include/sound/rawmidi.h
@@ -135,7 +135,7 @@ struct snd_rawmidi {
struct mutex open_mutex;
wait_queue_head_t open_wait;
- struct device dev;
+ struct device *dev;
struct snd_info_entry *proc_entry;
diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h
index 87f248a06271..17bea3144551 100644
--- a/include/sound/soc-component.h
+++ b/include/sound/soc-component.h
@@ -137,10 +137,10 @@ struct snd_soc_component_driver {
struct timespec64 *audio_ts,
struct snd_pcm_audio_tstamp_config *audio_tstamp_config,
struct snd_pcm_audio_tstamp_report *audio_tstamp_report);
- int (*copy_user)(struct snd_soc_component *component,
- struct snd_pcm_substream *substream, int channel,
- unsigned long pos, void __user *buf,
- unsigned long bytes);
+ int (*copy)(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int channel,
+ unsigned long pos, struct iov_iter *buf,
+ unsigned long bytes);
struct page *(*page)(struct snd_soc_component *component,
struct snd_pcm_substream *substream,
unsigned long offset);
@@ -509,9 +509,9 @@ int snd_soc_pcm_component_pointer(struct snd_pcm_substream *substream);
int snd_soc_pcm_component_ioctl(struct snd_pcm_substream *substream,
unsigned int cmd, void *arg);
int snd_soc_pcm_component_sync_stop(struct snd_pcm_substream *substream);
-int snd_soc_pcm_component_copy_user(struct snd_pcm_substream *substream,
- int channel, unsigned long pos,
- void __user *buf, unsigned long bytes);
+int snd_soc_pcm_component_copy(struct snd_pcm_substream *substream,
+ int channel, unsigned long pos,
+ struct iov_iter *buf, unsigned long bytes);
struct page *snd_soc_pcm_component_page(struct snd_pcm_substream *substream,
unsigned long offset);
int snd_soc_pcm_component_mmap(struct snd_pcm_substream *substream,
diff --git a/include/sound/ump.h b/include/sound/ump.h
index 44d2c2fd021d..91238dabe307 100644
--- a/include/sound/ump.h
+++ b/include/sound/ump.h
@@ -45,6 +45,7 @@ struct snd_ump_endpoint {
spinlock_t legacy_locks[2];
struct snd_rawmidi *legacy_rmidi;
struct snd_rawmidi_substream *legacy_substreams[2][SNDRV_UMP_MAX_GROUPS];
+ unsigned char legacy_mapping[SNDRV_UMP_MAX_GROUPS];
/* for legacy output; need to open the actual substream unlike input */
int legacy_out_opens;
diff --git a/lib/iov_iter.c b/lib/iov_iter.c
index e4dc809d1075..3743bbcbbb89 100644
--- a/lib/iov_iter.c
+++ b/lib/iov_iter.c
@@ -1544,6 +1544,7 @@ int import_ubuf(int rw, void __user *buf, size_t len, struct iov_iter *i)
iov_iter_ubuf(i, rw, buf, len);
return 0;
}
+EXPORT_SYMBOL_GPL(import_ubuf);
/**
* iov_iter_restore() - Restore a &struct iov_iter to the same state as when
diff --git a/sound/ac97/bus.c b/sound/ac97/bus.c
index 6067c04ce4c0..3173e9d98927 100644
--- a/sound/ac97/bus.c
+++ b/sound/ac97/bus.c
@@ -462,7 +462,7 @@ static ssize_t vendor_id_show(struct device *dev,
return sysfs_emit(buf, "%08x", codec->vendor_id);
}
-DEVICE_ATTR_RO(vendor_id);
+static DEVICE_ATTR_RO(vendor_id);
static struct attribute *ac97_dev_attrs[] = {
&dev_attr_vendor_id.attr,
diff --git a/sound/aoa/soundbus/i2sbus/pcm.c b/sound/aoa/soundbus/i2sbus/pcm.c
index a9e502a6cdeb..07df5cc0f2d7 100644
--- a/sound/aoa/soundbus/i2sbus/pcm.c
+++ b/sound/aoa/soundbus/i2sbus/pcm.c
@@ -972,7 +972,7 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card,
goto out_put_ci_module;
snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_PLAYBACK,
&i2sbus_playback_ops);
- dev->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].dev.parent =
+ dev->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].dev->parent =
&dev->ofdev.dev;
i2sdev->out.created = 1;
}
@@ -989,7 +989,7 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card,
goto out_put_ci_module;
snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_CAPTURE,
&i2sbus_record_ops);
- dev->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].dev.parent =
+ dev->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].dev->parent =
&dev->ofdev.dev;
i2sdev->in.created = 1;
}
diff --git a/sound/atmel/ac97c.c b/sound/atmel/ac97c.c
index c8912b8a1dc5..402b5f66dcc3 100644
--- a/sound/atmel/ac97c.c
+++ b/sound/atmel/ac97c.c
@@ -12,13 +12,12 @@
#include <linux/gpio/consumer.h>
#include <linux/init.h>
#include <linux/interrupt.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/types.h>
#include <linux/io.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
#include <sound/core.h>
#include <sound/initval.h>
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
index 30f73097447b..619371aa9964 100644
--- a/sound/core/compress_offload.c
+++ b/sound/core/compress_offload.c
@@ -546,7 +546,7 @@ static int snd_compr_allocate_buffer(struct snd_compr_stream *stream,
if (stream->runtime->dma_buffer_p) {
if (buffer_size > stream->runtime->dma_buffer_p->bytes)
- dev_err(&stream->device->dev,
+ dev_err(stream->device->dev,
"Not enough DMA buffer");
else
buffer = stream->runtime->dma_buffer_p->area;
@@ -1070,7 +1070,7 @@ static int snd_compress_dev_register(struct snd_device *device)
/* register compressed device */
ret = snd_register_device(SNDRV_DEVICE_TYPE_COMPRESS,
compr->card, compr->device,
- &snd_compr_file_ops, compr, &compr->dev);
+ &snd_compr_file_ops, compr, compr->dev);
if (ret < 0) {
pr_err("snd_register_device failed %d\n", ret);
return ret;
@@ -1084,7 +1084,7 @@ static int snd_compress_dev_disconnect(struct snd_device *device)
struct snd_compr *compr;
compr = device->device_data;
- snd_unregister_device(&compr->dev);
+ snd_unregister_device(compr->dev);
return 0;
}
@@ -1158,7 +1158,7 @@ static int snd_compress_dev_free(struct snd_device *device)
compr = device->device_data;
snd_compress_proc_done(compr);
- put_device(&compr->dev);
+ put_device(compr->dev);
return 0;
}
@@ -1189,12 +1189,16 @@ int snd_compress_new(struct snd_card *card, int device,
snd_compress_set_id(compr, id);
- snd_device_initialize(&compr->dev, card);
- dev_set_name(&compr->dev, "comprC%iD%i", card->number, device);
+ ret = snd_device_alloc(&compr->dev, card);
+ if (ret)
+ return ret;
+ dev_set_name(compr->dev, "comprC%iD%i", card->number, device);
ret = snd_device_new(card, SNDRV_DEV_COMPRESS, compr, &ops);
if (ret == 0)
snd_compress_proc_init(compr);
+ else
+ put_device(compr->dev);
return ret;
}
diff --git a/sound/core/control.c b/sound/core/control.c
index 8386b53acdcd..59c8658966d4 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -39,6 +39,9 @@ static LIST_HEAD(snd_control_compat_ioctls);
#endif
static struct snd_ctl_layer_ops *snd_ctl_layer;
+static int snd_ctl_remove_locked(struct snd_card *card,
+ struct snd_kcontrol *kcontrol);
+
static int snd_ctl_open(struct inode *inode, struct file *file)
{
unsigned long flags;
@@ -466,11 +469,13 @@ static int __snd_ctl_add_replace(struct snd_card *card,
struct snd_kcontrol *old;
int err;
+ lockdep_assert_held_write(&card->controls_rwsem);
+
id = kcontrol->id;
if (id.index > UINT_MAX - kcontrol->count)
return -EINVAL;
- old = snd_ctl_find_id(card, &id);
+ old = snd_ctl_find_id_locked(card, &id);
if (!old) {
if (mode == CTL_REPLACE)
return -EINVAL;
@@ -483,7 +488,7 @@ static int __snd_ctl_add_replace(struct snd_card *card,
return -EBUSY;
}
- err = snd_ctl_remove(card, old);
+ err = snd_ctl_remove_locked(card, old);
if (err < 0)
return err;
}
@@ -575,6 +580,8 @@ static int __snd_ctl_remove(struct snd_card *card,
{
unsigned int idx;
+ lockdep_assert_held_write(&card->controls_rwsem);
+
if (snd_BUG_ON(!card || !kcontrol))
return -EINVAL;
list_del(&kcontrol->list);
@@ -589,20 +596,32 @@ static int __snd_ctl_remove(struct snd_card *card,
return 0;
}
+static inline int snd_ctl_remove_locked(struct snd_card *card,
+ struct snd_kcontrol *kcontrol)
+{
+ return __snd_ctl_remove(card, kcontrol, true);
+}
+
/**
* snd_ctl_remove - remove the control from the card and release it
* @card: the card instance
* @kcontrol: the control instance to remove
*
* Removes the control from the card and then releases the instance.
- * You don't need to call snd_ctl_free_one(). You must be in
- * the write lock - down_write(&card->controls_rwsem).
+ * You don't need to call snd_ctl_free_one().
*
* Return: 0 if successful, or a negative error code on failure.
+ *
+ * Note that this function takes card->controls_rwsem lock internally.
*/
int snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol)
{
- return __snd_ctl_remove(card, kcontrol, true);
+ int ret;
+
+ down_write(&card->controls_rwsem);
+ ret = snd_ctl_remove_locked(card, kcontrol);
+ up_write(&card->controls_rwsem);
+ return ret;
}
EXPORT_SYMBOL(snd_ctl_remove);
@@ -622,12 +641,12 @@ int snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id)
int ret;
down_write(&card->controls_rwsem);
- kctl = snd_ctl_find_id(card, id);
+ kctl = snd_ctl_find_id_locked(card, id);
if (kctl == NULL) {
up_write(&card->controls_rwsem);
return -ENOENT;
}
- ret = snd_ctl_remove(card, kctl);
+ ret = snd_ctl_remove_locked(card, kctl);
up_write(&card->controls_rwsem);
return ret;
}
@@ -651,7 +670,7 @@ static int snd_ctl_remove_user_ctl(struct snd_ctl_file * file,
int idx, ret;
down_write(&card->controls_rwsem);
- kctl = snd_ctl_find_id(card, id);
+ kctl = snd_ctl_find_id_locked(card, id);
if (kctl == NULL) {
ret = -ENOENT;
goto error;
@@ -665,7 +684,7 @@ static int snd_ctl_remove_user_ctl(struct snd_ctl_file * file,
ret = -EBUSY;
goto error;
}
- ret = snd_ctl_remove(card, kctl);
+ ret = snd_ctl_remove_locked(card, kctl);
error:
up_write(&card->controls_rwsem);
return ret;
@@ -692,7 +711,7 @@ int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id,
int ret;
down_write(&card->controls_rwsem);
- kctl = snd_ctl_find_id(card, id);
+ kctl = snd_ctl_find_id_locked(card, id);
if (kctl == NULL) {
ret = -ENOENT;
goto unlock;
@@ -746,7 +765,7 @@ int snd_ctl_rename_id(struct snd_card *card, struct snd_ctl_elem_id *src_id,
int saved_numid;
down_write(&card->controls_rwsem);
- kctl = snd_ctl_find_id(card, src_id);
+ kctl = snd_ctl_find_id_locked(card, src_id);
if (kctl == NULL) {
up_write(&card->controls_rwsem);
return -ENOENT;
@@ -769,11 +788,12 @@ EXPORT_SYMBOL(snd_ctl_rename_id);
*
* Renames the specified control on the card to the new name.
*
- * Make sure to take the control write lock - down_write(&card->controls_rwsem).
+ * Note that this function takes card->controls_rwsem lock internally.
*/
void snd_ctl_rename(struct snd_card *card, struct snd_kcontrol *kctl,
const char *name)
{
+ down_write(&card->controls_rwsem);
remove_hash_entries(card, kctl);
if (strscpy(kctl->id.name, name, sizeof(kctl->id.name)) < 0)
@@ -781,6 +801,7 @@ void snd_ctl_rename(struct snd_card *card, struct snd_kcontrol *kctl,
name, kctl->id.name);
add_hash_entries(card, kctl);
+ up_write(&card->controls_rwsem);
}
EXPORT_SYMBOL(snd_ctl_rename);
@@ -799,7 +820,7 @@ snd_ctl_find_numid_slow(struct snd_card *card, unsigned int numid)
#endif /* !CONFIG_SND_CTL_FAST_LOOKUP */
/**
- * snd_ctl_find_numid - find the control instance with the given number-id
+ * snd_ctl_find_numid_locked - find the control instance with the given number-id
* @card: the card instance
* @numid: the number-id to search
*
@@ -809,22 +830,46 @@ snd_ctl_find_numid_slow(struct snd_card *card, unsigned int numid)
* (if the race condition can happen).
*
* Return: The pointer of the instance if found, or %NULL if not.
- *
*/
-struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card, unsigned int numid)
+struct snd_kcontrol *
+snd_ctl_find_numid_locked(struct snd_card *card, unsigned int numid)
{
if (snd_BUG_ON(!card || !numid))
return NULL;
+ lockdep_assert_held(&card->controls_rwsem);
#ifdef CONFIG_SND_CTL_FAST_LOOKUP
return xa_load(&card->ctl_numids, numid);
#else
return snd_ctl_find_numid_slow(card, numid);
#endif
}
+EXPORT_SYMBOL(snd_ctl_find_numid_locked);
+
+/**
+ * snd_ctl_find_numid - find the control instance with the given number-id
+ * @card: the card instance
+ * @numid: the number-id to search
+ *
+ * Finds the control instance with the given number-id from the card.
+ *
+ * Return: The pointer of the instance if found, or %NULL if not.
+ *
+ * Note that this function takes card->controls_rwsem lock internally.
+ */
+struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card,
+ unsigned int numid)
+{
+ struct snd_kcontrol *kctl;
+
+ down_read(&card->controls_rwsem);
+ kctl = snd_ctl_find_numid_locked(card, numid);
+ up_read(&card->controls_rwsem);
+ return kctl;
+}
EXPORT_SYMBOL(snd_ctl_find_numid);
/**
- * snd_ctl_find_id - find the control instance with the given id
+ * snd_ctl_find_id_locked - find the control instance with the given id
* @card: the card instance
* @id: the id to search
*
@@ -834,17 +879,17 @@ EXPORT_SYMBOL(snd_ctl_find_numid);
* (if the race condition can happen).
*
* Return: The pointer of the instance if found, or %NULL if not.
- *
*/
-struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card,
- struct snd_ctl_elem_id *id)
+struct snd_kcontrol *snd_ctl_find_id_locked(struct snd_card *card,
+ const struct snd_ctl_elem_id *id)
{
struct snd_kcontrol *kctl;
if (snd_BUG_ON(!card || !id))
return NULL;
+ lockdep_assert_held(&card->controls_rwsem);
if (id->numid != 0)
- return snd_ctl_find_numid(card, id->numid);
+ return snd_ctl_find_numid_locked(card, id->numid);
#ifdef CONFIG_SND_CTL_FAST_LOOKUP
kctl = xa_load(&card->ctl_hash, get_ctl_id_hash(id));
if (kctl && elem_id_matches(kctl, id))
@@ -859,6 +904,29 @@ struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card,
return NULL;
}
+EXPORT_SYMBOL(snd_ctl_find_id_locked);
+
+/**
+ * snd_ctl_find_id - find the control instance with the given id
+ * @card: the card instance
+ * @id: the id to search
+ *
+ * Finds the control instance with the given id from the card.
+ *
+ * Return: The pointer of the instance if found, or %NULL if not.
+ *
+ * Note that this function takes card->controls_rwsem lock internally.
+ */
+struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card,
+ const struct snd_ctl_elem_id *id)
+{
+ struct snd_kcontrol *kctl;
+
+ down_read(&card->controls_rwsem);
+ kctl = snd_ctl_find_id_locked(card, id);
+ up_read(&card->controls_rwsem);
+ return kctl;
+}
EXPORT_SYMBOL(snd_ctl_find_id);
static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl,
@@ -1173,7 +1241,7 @@ static int snd_ctl_elem_info(struct snd_ctl_file *ctl,
int result;
down_read(&card->controls_rwsem);
- kctl = snd_ctl_find_id(card, &info->id);
+ kctl = snd_ctl_find_id_locked(card, &info->id);
if (kctl == NULL)
result = -ENOENT;
else
@@ -1212,7 +1280,7 @@ static int snd_ctl_elem_read(struct snd_card *card,
int ret;
down_read(&card->controls_rwsem);
- kctl = snd_ctl_find_id(card, &control->id);
+ kctl = snd_ctl_find_id_locked(card, &control->id);
if (kctl == NULL) {
ret = -ENOENT;
goto unlock;
@@ -1289,7 +1357,7 @@ static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
int result;
down_write(&card->controls_rwsem);
- kctl = snd_ctl_find_id(card, &control->id);
+ kctl = snd_ctl_find_id_locked(card, &control->id);
if (kctl == NULL) {
up_write(&card->controls_rwsem);
return -ENOENT;
@@ -1370,7 +1438,7 @@ static int snd_ctl_elem_lock(struct snd_ctl_file *file,
if (copy_from_user(&id, _id, sizeof(id)))
return -EFAULT;
down_write(&card->controls_rwsem);
- kctl = snd_ctl_find_id(card, &id);
+ kctl = snd_ctl_find_id_locked(card, &id);
if (kctl == NULL) {
result = -ENOENT;
} else {
@@ -1398,7 +1466,7 @@ static int snd_ctl_elem_unlock(struct snd_ctl_file *file,
if (copy_from_user(&id, _id, sizeof(id)))
return -EFAULT;
down_write(&card->controls_rwsem);
- kctl = snd_ctl_find_id(card, &id);
+ kctl = snd_ctl_find_id_locked(card, &id);
if (kctl == NULL) {
result = -ENOENT;
} else {
@@ -1507,6 +1575,8 @@ static int replace_user_tlv(struct snd_kcontrol *kctl, unsigned int __user *buf,
int i;
int change;
+ lockdep_assert_held_write(&ue->card->controls_rwsem);
+
if (size > 1024 * 128) /* sane value */
return -EINVAL;
@@ -1583,6 +1653,8 @@ static int snd_ctl_elem_init_enum_names(struct user_element *ue)
unsigned int i;
const uintptr_t user_ptrval = ue->info.value.enumerated.names_ptr;
+ lockdep_assert_held_write(&ue->card->controls_rwsem);
+
buf_len = ue->info.value.enumerated.names_length;
if (buf_len > 64 * 1024)
return -EINVAL;
@@ -1887,6 +1959,8 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file,
struct snd_ctl_elem_id id;
struct snd_kcontrol_volatile *vd;
+ lockdep_assert_held(&file->card->controls_rwsem);
+
if (copy_from_user(&header, buf, sizeof(header)))
return -EFAULT;
@@ -1900,7 +1974,7 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file,
container_size = header.length;
container = buf->tlv;
- kctl = snd_ctl_find_numid(file->card, header.numid);
+ kctl = snd_ctl_find_numid_locked(file->card, header.numid);
if (kctl == NULL)
return -ENOENT;
@@ -2315,7 +2389,7 @@ static int snd_ctl_dev_register(struct snd_device *device)
int err;
err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,
- &snd_ctl_f_ops, card, &card->ctl_dev);
+ &snd_ctl_f_ops, card, card->ctl_dev);
if (err < 0)
return err;
down_read(&card->controls_rwsem);
@@ -2351,7 +2425,7 @@ static int snd_ctl_dev_disconnect(struct snd_device *device)
up_read(&snd_ctl_layer_rwsem);
up_read(&card->controls_rwsem);
- return snd_unregister_device(&card->ctl_dev);
+ return snd_unregister_device(card->ctl_dev);
}
/*
@@ -2373,7 +2447,7 @@ static int snd_ctl_dev_free(struct snd_device *device)
xa_destroy(&card->ctl_hash);
#endif
up_write(&card->controls_rwsem);
- put_device(&card->ctl_dev);
+ put_device(card->ctl_dev);
return 0;
}
@@ -2395,12 +2469,14 @@ int snd_ctl_create(struct snd_card *card)
if (snd_BUG_ON(card->number < 0 || card->number >= SNDRV_CARDS))
return -ENXIO;
- snd_device_initialize(&card->ctl_dev, card);
- dev_set_name(&card->ctl_dev, "controlC%d", card->number);
+ err = snd_device_alloc(&card->ctl_dev, card);
+ if (err < 0)
+ return err;
+ dev_set_name(card->ctl_dev, "controlC%d", card->number);
err = snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);
if (err < 0)
- put_device(&card->ctl_dev);
+ put_device(card->ctl_dev);
return err;
}
diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c
index 9cae5d74335c..0e8b1bfb040e 100644
--- a/sound/core/control_compat.c
+++ b/sound/core/control_compat.c
@@ -173,7 +173,7 @@ static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id,
int err;
down_read(&card->controls_rwsem);
- kctl = snd_ctl_find_id(card, id);
+ kctl = snd_ctl_find_id_locked(card, id);
if (! kctl) {
up_read(&card->controls_rwsem);
return -ENOENT;
diff --git a/sound/core/control_led.c b/sound/core/control_led.c
index ee77547bf8dc..a78eb48927c7 100644
--- a/sound/core/control_led.c
+++ b/sound/core/control_led.c
@@ -251,7 +251,7 @@ static int snd_ctl_led_set_id(int card_number, struct snd_ctl_elem_id *id,
card = snd_card_ref(card_number);
if (card) {
down_write(&card->controls_rwsem);
- kctl = snd_ctl_find_id(card, id);
+ kctl = snd_ctl_find_id_locked(card, id);
if (kctl) {
ioff = snd_ctl_get_ioff(kctl, id);
vd = &kctl->vd[ioff];
@@ -688,7 +688,7 @@ static void snd_ctl_led_sysfs_add(struct snd_card *card)
goto cerr;
led->cards[card->number] = led_card;
snprintf(link_name, sizeof(link_name), "led-%s", led->name);
- WARN(sysfs_create_link(&card->ctl_dev.kobj, &led_card->dev.kobj, link_name),
+ WARN(sysfs_create_link(&card->ctl_dev->kobj, &led_card->dev.kobj, link_name),
"can't create symlink to controlC%i device\n", card->number);
WARN(sysfs_create_link(&led_card->dev.kobj, &card->card_dev.kobj, "card"),
"can't create symlink to card%i\n", card->number);
@@ -714,7 +714,7 @@ static void snd_ctl_led_sysfs_remove(struct snd_card *card)
if (!led_card)
continue;
snprintf(link_name, sizeof(link_name), "led-%s", led->name);
- sysfs_remove_link(&card->ctl_dev.kobj, link_name);
+ sysfs_remove_link(&card->ctl_dev->kobj, link_name);
sysfs_remove_link(&led_card->dev.kobj, "card");
device_unregister(&led_card->dev);
led->cards[card->number] = NULL;
diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c
index e95fa275c289..de7476034f2c 100644
--- a/sound/core/hwdep.c
+++ b/sound/core/hwdep.c
@@ -338,9 +338,14 @@ static const struct file_operations snd_hwdep_f_ops =
.mmap = snd_hwdep_mmap,
};
-static void release_hwdep_device(struct device *dev)
+static void snd_hwdep_free(struct snd_hwdep *hwdep)
{
- kfree(container_of(dev, struct snd_hwdep, dev));
+ if (!hwdep)
+ return;
+ if (hwdep->private_free)
+ hwdep->private_free(hwdep);
+ put_device(hwdep->dev);
+ kfree(hwdep);
}
/**
@@ -382,16 +387,20 @@ int snd_hwdep_new(struct snd_card *card, char *id, int device,
if (id)
strscpy(hwdep->id, id, sizeof(hwdep->id));
- snd_device_initialize(&hwdep->dev, card);
- hwdep->dev.release = release_hwdep_device;
- dev_set_name(&hwdep->dev, "hwC%iD%i", card->number, device);
+ err = snd_device_alloc(&hwdep->dev, card);
+ if (err < 0) {
+ snd_hwdep_free(hwdep);
+ return err;
+ }
+
+ dev_set_name(hwdep->dev, "hwC%iD%i", card->number, device);
#ifdef CONFIG_SND_OSSEMUL
hwdep->oss_type = -1;
#endif
err = snd_device_new(card, SNDRV_DEV_HWDEP, hwdep, &ops);
if (err < 0) {
- put_device(&hwdep->dev);
+ snd_hwdep_free(hwdep);
return err;
}
@@ -403,12 +412,7 @@ EXPORT_SYMBOL(snd_hwdep_new);
static int snd_hwdep_dev_free(struct snd_device *device)
{
- struct snd_hwdep *hwdep = device->device_data;
- if (!hwdep)
- return 0;
- if (hwdep->private_free)
- hwdep->private_free(hwdep);
- put_device(&hwdep->dev);
+ snd_hwdep_free(device->device_data);
return 0;
}
@@ -426,9 +430,9 @@ static int snd_hwdep_dev_register(struct snd_device *device)
list_add_tail(&hwdep->list, &snd_hwdep_devices);
err = snd_register_device(SNDRV_DEVICE_TYPE_HWDEP,
hwdep->card, hwdep->device,
- &snd_hwdep_f_ops, hwdep, &hwdep->dev);
+ &snd_hwdep_f_ops, hwdep, hwdep->dev);
if (err < 0) {
- dev_err(&hwdep->dev, "unable to register\n");
+ dev_err(hwdep->dev, "unable to register\n");
list_del(&hwdep->list);
mutex_unlock(&register_mutex);
return err;
@@ -439,12 +443,12 @@ static int snd_hwdep_dev_register(struct snd_device *device)
if (hwdep->oss_type >= 0) {
if (hwdep->oss_type == SNDRV_OSS_DEVICE_TYPE_DMFM &&
hwdep->device)
- dev_warn(&hwdep->dev,
+ dev_warn(hwdep->dev,
"only hwdep device 0 can be registered as OSS direct FM device!\n");
else if (snd_register_oss_device(hwdep->oss_type,
card, hwdep->device,
&snd_hwdep_f_ops, hwdep) < 0)
- dev_warn(&hwdep->dev,
+ dev_warn(hwdep->dev,
"unable to register OSS compatibility device\n");
else
hwdep->ossreg = 1;
@@ -471,7 +475,7 @@ static int snd_hwdep_dev_disconnect(struct snd_device *device)
if (hwdep->ossreg)
snd_unregister_oss_device(hwdep->oss_type, hwdep->card, hwdep->device);
#endif
- snd_unregister_device(&hwdep->dev);
+ snd_unregister_device(hwdep->dev);
list_del_init(&hwdep->list);
mutex_unlock(&hwdep->open_mutex);
mutex_unlock(&register_mutex);
diff --git a/sound/core/init.c b/sound/core/init.c
index baef2688d0cf..d61bde1225f2 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -111,28 +111,36 @@ static int get_slot_from_bitmask(int mask, int (*check)(struct module *, int),
return mask; /* unchanged */
}
-/* the default release callback set in snd_device_initialize() below;
- * this is just NOP for now, as almost all jobs are already done in
- * dev_free callback of snd_device chain instead.
- */
-static void default_release(struct device *dev)
+/* the default release callback set in snd_device_alloc() */
+static void default_release_alloc(struct device *dev)
{
+ kfree(dev);
}
/**
- * snd_device_initialize - Initialize struct device for sound devices
- * @dev: device to initialize
+ * snd_device_alloc - Allocate and initialize struct device for sound devices
+ * @dev_p: pointer to store the allocated device
* @card: card to assign, optional
+ *
+ * For releasing the allocated device, call put_device().
*/
-void snd_device_initialize(struct device *dev, struct snd_card *card)
+int snd_device_alloc(struct device **dev_p, struct snd_card *card)
{
+ struct device *dev;
+
+ *dev_p = NULL;
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
device_initialize(dev);
if (card)
dev->parent = &card->card_dev;
dev->class = &sound_class;
- dev->release = default_release;
+ dev->release = default_release_alloc;
+ *dev_p = dev;
+ return 0;
}
-EXPORT_SYMBOL_GPL(snd_device_initialize);
+EXPORT_SYMBOL_GPL(snd_device_alloc);
static int snd_card_init(struct snd_card *card, struct device *parent,
int idx, const char *xid, struct module *module,
diff --git a/sound/core/jack.c b/sound/core/jack.c
index 03d155ed362b..e0f034e7275c 100644
--- a/sound/core/jack.c
+++ b/sound/core/jack.c
@@ -66,12 +66,10 @@ static int snd_jack_dev_free(struct snd_device *device)
struct snd_card *card = device->card;
struct snd_jack_kctl *jack_kctl, *tmp_jack_kctl;
- down_write(&card->controls_rwsem);
list_for_each_entry_safe(jack_kctl, tmp_jack_kctl, &jack->kctl_list, list) {
list_del_init(&jack_kctl->list);
snd_ctl_remove(card, jack_kctl->kctl);
}
- up_write(&card->controls_rwsem);
if (jack->private_free)
jack->private_free(jack);
diff --git a/sound/core/memory.c b/sound/core/memory.c
index 5d894dc32f7d..2d2d0094c897 100644
--- a/sound/core/memory.c
+++ b/sound/core/memory.c
@@ -9,6 +9,7 @@
#include <linux/io.h>
#include <linux/uaccess.h>
#include <sound/core.h>
+#include <sound/pcm.h>
/**
* copy_to_user_fromio - copy data from mmio-space to user-space
@@ -22,8 +23,29 @@
*/
int copy_to_user_fromio(void __user *dst, const volatile void __iomem *src, size_t count)
{
+ struct iov_iter iter;
+
+ if (import_ubuf(ITER_DEST, dst, count, &iter))
+ return -EFAULT;
+ return copy_to_iter_fromio(&iter, (const void __iomem *)src, count);
+}
+EXPORT_SYMBOL(copy_to_user_fromio);
+
+/**
+ * copy_to_iter_fromio - copy data from mmio-space to iov_iter
+ * @dst: the destination iov_iter
+ * @src: the source pointer on mmio
+ * @count: the data size to copy in bytes
+ *
+ * Copies the data from mmio-space to iov_iter.
+ *
+ * Return: Zero if successful, or non-zero on failure.
+ */
+int copy_to_iter_fromio(struct iov_iter *dst, const void __iomem *src,
+ size_t count)
+{
#if defined(__i386__) || defined(CONFIG_SPARC32)
- return copy_to_user(dst, (const void __force*)src, count) ? -EFAULT : 0;
+ return copy_to_iter((const void __force *)src, count, dst) == count ? 0 : -EFAULT;
#else
char buf[256];
while (count) {
@@ -31,16 +53,15 @@ int copy_to_user_fromio(void __user *dst, const volatile void __iomem *src, size
if (c > sizeof(buf))
c = sizeof(buf);
memcpy_fromio(buf, (void __iomem *)src, c);
- if (copy_to_user(dst, buf, c))
+ if (copy_to_iter(buf, c, dst) != c)
return -EFAULT;
count -= c;
- dst += c;
src += c;
}
return 0;
#endif
}
-EXPORT_SYMBOL(copy_to_user_fromio);
+EXPORT_SYMBOL(copy_to_iter_fromio);
/**
* copy_from_user_toio - copy data from user-space to mmio-space
@@ -54,22 +75,41 @@ EXPORT_SYMBOL(copy_to_user_fromio);
*/
int copy_from_user_toio(volatile void __iomem *dst, const void __user *src, size_t count)
{
+ struct iov_iter iter;
+
+ if (import_ubuf(ITER_SOURCE, (void __user *)src, count, &iter))
+ return -EFAULT;
+ return copy_from_iter_toio((void __iomem *)dst, &iter, count);
+}
+EXPORT_SYMBOL(copy_from_user_toio);
+
+/**
+ * copy_from_iter_toio - copy data from iov_iter to mmio-space
+ * @dst: the destination pointer on mmio-space
+ * @src: the source iov_iter
+ * @count: the data size to copy in bytes
+ *
+ * Copies the data from iov_iter to mmio-space.
+ *
+ * Return: Zero if successful, or non-zero on failure.
+ */
+int copy_from_iter_toio(void __iomem *dst, struct iov_iter *src, size_t count)
+{
#if defined(__i386__) || defined(CONFIG_SPARC32)
- return copy_from_user((void __force *)dst, src, count) ? -EFAULT : 0;
+ return copy_from_iter((void __force *)dst, count, src) == count ? 0 : -EFAULT;
#else
char buf[256];
while (count) {
size_t c = count;
if (c > sizeof(buf))
c = sizeof(buf);
- if (copy_from_user(buf, src, c))
+ if (copy_from_iter(buf, c, src) != c)
return -EFAULT;
memcpy_toio(dst, buf, c);
count -= c;
dst += c;
- src += c;
}
return 0;
#endif
}
-EXPORT_SYMBOL(copy_from_user_toio);
+EXPORT_SYMBOL(copy_from_iter_toio);
diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c
index 9620115cfdc0..dae2da380835 100644
--- a/sound/core/oss/mixer_oss.c
+++ b/sound/core/oss/mixer_oss.c
@@ -524,7 +524,7 @@ static struct snd_kcontrol *snd_mixer_oss_test_id(struct snd_mixer_oss *mixer, c
id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
strscpy(id.name, name, sizeof(id.name));
id.index = index;
- return snd_ctl_find_id(card, &id);
+ return snd_ctl_find_id_locked(card, &id);
}
static void snd_mixer_oss_get_volume1_vol(struct snd_mixer_oss_file *fmixer,
@@ -540,7 +540,7 @@ static void snd_mixer_oss_get_volume1_vol(struct snd_mixer_oss_file *fmixer,
if (numid == ID_UNKNOWN)
return;
down_read(&card->controls_rwsem);
- kctl = snd_ctl_find_numid(card, numid);
+ kctl = snd_ctl_find_numid_locked(card, numid);
if (!kctl) {
up_read(&card->controls_rwsem);
return;
@@ -579,7 +579,7 @@ static void snd_mixer_oss_get_volume1_sw(struct snd_mixer_oss_file *fmixer,
if (numid == ID_UNKNOWN)
return;
down_read(&card->controls_rwsem);
- kctl = snd_ctl_find_numid(card, numid);
+ kctl = snd_ctl_find_numid_locked(card, numid);
if (!kctl) {
up_read(&card->controls_rwsem);
return;
@@ -645,7 +645,7 @@ static void snd_mixer_oss_put_volume1_vol(struct snd_mixer_oss_file *fmixer,
if (numid == ID_UNKNOWN)
return;
down_read(&card->controls_rwsem);
- kctl = snd_ctl_find_numid(card, numid);
+ kctl = snd_ctl_find_numid_locked(card, numid);
if (!kctl) {
up_read(&card->controls_rwsem);
return;
@@ -688,7 +688,7 @@ static void snd_mixer_oss_put_volume1_sw(struct snd_mixer_oss_file *fmixer,
if (numid == ID_UNKNOWN)
return;
down_read(&card->controls_rwsem);
- kctl = snd_ctl_find_numid(card, numid);
+ kctl = snd_ctl_find_numid_locked(card, numid);
if (!kctl) {
up_read(&card->controls_rwsem);
return;
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index 9d95e3731123..20bb2d7c8d4b 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -604,7 +604,7 @@ static const struct attribute_group *pcm_dev_attr_groups[];
#ifdef CONFIG_PM_SLEEP
static int do_pcm_suspend(struct device *dev)
{
- struct snd_pcm_str *pstr = container_of(dev, struct snd_pcm_str, dev);
+ struct snd_pcm_str *pstr = dev_get_drvdata(dev);
if (!pstr->pcm->no_device_suspend)
snd_pcm_suspend_all(pstr->pcm);
@@ -650,11 +650,14 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
if (!substream_count)
return 0;
- snd_device_initialize(&pstr->dev, pcm->card);
- pstr->dev.groups = pcm_dev_attr_groups;
- pstr->dev.type = &pcm_dev_type;
- dev_set_name(&pstr->dev, "pcmC%iD%i%c", pcm->card->number, pcm->device,
+ err = snd_device_alloc(&pstr->dev, pcm->card);
+ if (err < 0)
+ return err;
+ dev_set_name(pstr->dev, "pcmC%iD%i%c", pcm->card->number, pcm->device,
stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c');
+ pstr->dev->groups = pcm_dev_attr_groups;
+ pstr->dev->type = &pcm_dev_type;
+ dev_set_drvdata(pstr->dev, pstr);
if (!pcm->internal) {
err = snd_pcm_stream_proc_init(pstr);
@@ -814,9 +817,7 @@ static void free_chmap(struct snd_pcm_str *pstr)
if (pstr->chmap_kctl) {
struct snd_card *card = pstr->pcm->card;
- down_write(&card->controls_rwsem);
snd_ctl_remove(card, pstr->chmap_kctl);
- up_write(&card->controls_rwsem);
pstr->chmap_kctl = NULL;
}
}
@@ -847,7 +848,7 @@ static void snd_pcm_free_stream(struct snd_pcm_str * pstr)
#endif
free_chmap(pstr);
if (pstr->substream_count)
- put_device(&pstr->dev);
+ put_device(pstr->dev);
}
#if IS_ENABLED(CONFIG_SND_PCM_OSS)
@@ -1017,7 +1018,7 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
static ssize_t pcm_class_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct snd_pcm_str *pstr = container_of(dev, struct snd_pcm_str, dev);
+ struct snd_pcm_str *pstr = dev_get_drvdata(dev);
struct snd_pcm *pcm = pstr->pcm;
const char *str;
static const char *strs[SNDRV_PCM_CLASS_LAST + 1] = {
@@ -1078,7 +1079,7 @@ static int snd_pcm_dev_register(struct snd_device *device)
/* register pcm */
err = snd_register_device(devtype, pcm->card, pcm->device,
&snd_pcm_f_ops[cidx], pcm,
- &pcm->streams[cidx].dev);
+ pcm->streams[cidx].dev);
if (err < 0) {
list_del_init(&pcm->list);
goto unlock;
@@ -1125,7 +1126,8 @@ static int snd_pcm_dev_disconnect(struct snd_device *device)
pcm_call_notify(pcm, n_disconnect);
for (cidx = 0; cidx < 2; cidx++) {
- snd_unregister_device(&pcm->streams[cidx].dev);
+ if (pcm->streams[cidx].dev)
+ snd_unregister_device(pcm->streams[cidx].dev);
free_chmap(&pcm->streams[cidx]);
}
mutex_unlock(&pcm->open_mutex);
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 9c121a921b04..4859fb1caec9 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -1973,10 +1973,11 @@ static int wait_for_avail(struct snd_pcm_substream *substream,
typedef int (*pcm_transfer_f)(struct snd_pcm_substream *substream,
int channel, unsigned long hwoff,
- void *buf, unsigned long bytes);
+ struct iov_iter *iter, unsigned long bytes);
typedef int (*pcm_copy_f)(struct snd_pcm_substream *, snd_pcm_uframes_t, void *,
- snd_pcm_uframes_t, snd_pcm_uframes_t, pcm_transfer_f);
+ snd_pcm_uframes_t, snd_pcm_uframes_t, pcm_transfer_f,
+ bool);
/* calculate the target DMA-buffer position to be written/read */
static void *get_dma_ptr(struct snd_pcm_runtime *runtime,
@@ -1986,32 +1987,24 @@ static void *get_dma_ptr(struct snd_pcm_runtime *runtime,
channel * (runtime->dma_bytes / runtime->channels);
}
-/* default copy_user ops for write; used for both interleaved and non- modes */
+/* default copy ops for write; used for both interleaved and non- modes */
static int default_write_copy(struct snd_pcm_substream *substream,
int channel, unsigned long hwoff,
- void *buf, unsigned long bytes)
+ struct iov_iter *iter, unsigned long bytes)
{
- if (copy_from_user(get_dma_ptr(substream->runtime, channel, hwoff),
- (void __user *)buf, bytes))
+ if (!copy_from_iter(get_dma_ptr(substream->runtime, channel, hwoff),
+ bytes, iter))
return -EFAULT;
return 0;
}
-/* default copy_kernel ops for write */
-static int default_write_copy_kernel(struct snd_pcm_substream *substream,
- int channel, unsigned long hwoff,
- void *buf, unsigned long bytes)
-{
- memcpy(get_dma_ptr(substream->runtime, channel, hwoff), buf, bytes);
- return 0;
-}
-
/* fill silence instead of copy data; called as a transfer helper
* from __snd_pcm_lib_write() or directly from noninterleaved_copy() when
* a NULL buffer is passed
*/
static int fill_silence(struct snd_pcm_substream *substream, int channel,
- unsigned long hwoff, void *buf, unsigned long bytes)
+ unsigned long hwoff, struct iov_iter *iter,
+ unsigned long bytes)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -2027,25 +2020,41 @@ static int fill_silence(struct snd_pcm_substream *substream, int channel,
return 0;
}
-/* default copy_user ops for read; used for both interleaved and non- modes */
+/* default copy ops for read; used for both interleaved and non- modes */
static int default_read_copy(struct snd_pcm_substream *substream,
int channel, unsigned long hwoff,
- void *buf, unsigned long bytes)
+ struct iov_iter *iter, unsigned long bytes)
{
- if (copy_to_user((void __user *)buf,
- get_dma_ptr(substream->runtime, channel, hwoff),
- bytes))
+ if (!copy_to_iter(get_dma_ptr(substream->runtime, channel, hwoff),
+ bytes, iter))
return -EFAULT;
return 0;
}
-/* default copy_kernel ops for read */
-static int default_read_copy_kernel(struct snd_pcm_substream *substream,
- int channel, unsigned long hwoff,
- void *buf, unsigned long bytes)
+/* call transfer with the filled iov_iter */
+static int do_transfer(struct snd_pcm_substream *substream, int c,
+ unsigned long hwoff, void *data, unsigned long bytes,
+ pcm_transfer_f transfer, bool in_kernel)
{
- memcpy(buf, get_dma_ptr(substream->runtime, channel, hwoff), bytes);
- return 0;
+ struct iov_iter iter;
+ int err, type;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ type = ITER_SOURCE;
+ else
+ type = ITER_DEST;
+
+ if (in_kernel) {
+ struct kvec kvec = { data, bytes };
+
+ iov_iter_kvec(&iter, type, &kvec, 1, bytes);
+ return transfer(substream, c, hwoff, &iter, bytes);
+ }
+
+ err = import_ubuf(type, (__force void __user *)data, bytes, &iter);
+ if (err)
+ return err;
+ return transfer(substream, c, hwoff, &iter, bytes);
}
/* call transfer function with the converted pointers and sizes;
@@ -2055,7 +2064,8 @@ static int interleaved_copy(struct snd_pcm_substream *substream,
snd_pcm_uframes_t hwoff, void *data,
snd_pcm_uframes_t off,
snd_pcm_uframes_t frames,
- pcm_transfer_f transfer)
+ pcm_transfer_f transfer,
+ bool in_kernel)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -2063,7 +2073,9 @@ static int interleaved_copy(struct snd_pcm_substream *substream,
hwoff = frames_to_bytes(runtime, hwoff);
off = frames_to_bytes(runtime, off);
frames = frames_to_bytes(runtime, frames);
- return transfer(substream, 0, hwoff, data + off, frames);
+
+ return do_transfer(substream, 0, hwoff, data + off, frames, transfer,
+ in_kernel);
}
/* call transfer function with the converted pointers and sizes for each
@@ -2073,7 +2085,8 @@ static int noninterleaved_copy(struct snd_pcm_substream *substream,
snd_pcm_uframes_t hwoff, void *data,
snd_pcm_uframes_t off,
snd_pcm_uframes_t frames,
- pcm_transfer_f transfer)
+ pcm_transfer_f transfer,
+ bool in_kernel)
{
struct snd_pcm_runtime *runtime = substream->runtime;
int channels = runtime->channels;
@@ -2091,8 +2104,8 @@ static int noninterleaved_copy(struct snd_pcm_substream *substream,
if (!data || !*bufs)
err = fill_silence(substream, c, hwoff, NULL, frames);
else
- err = transfer(substream, c, hwoff, *bufs + off,
- frames);
+ err = do_transfer(substream, c, hwoff, *bufs + off,
+ frames, transfer, in_kernel);
if (err < 0)
return err;
}
@@ -2108,10 +2121,10 @@ static int fill_silence_frames(struct snd_pcm_substream *substream,
if (substream->runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED ||
substream->runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED)
return interleaved_copy(substream, off, NULL, 0, frames,
- fill_silence);
+ fill_silence, true);
else
return noninterleaved_copy(substream, off, NULL, 0, frames,
- fill_silence);
+ fill_silence, true);
}
/* sanity-check for read/write methods */
@@ -2121,7 +2134,7 @@ static int pcm_sanity_check(struct snd_pcm_substream *substream)
if (PCM_RUNTIME_CHECK(substream))
return -ENXIO;
runtime = substream->runtime;
- if (snd_BUG_ON(!substream->ops->copy_user && !runtime->dma_area))
+ if (snd_BUG_ON(!substream->ops->copy && !runtime->dma_area))
return -EINVAL;
if (runtime->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
@@ -2226,15 +2239,9 @@ snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream,
transfer = fill_silence;
else
return -EINVAL;
- } else if (in_kernel) {
- if (substream->ops->copy_kernel)
- transfer = substream->ops->copy_kernel;
- else
- transfer = is_playback ?
- default_write_copy_kernel : default_read_copy_kernel;
} else {
- if (substream->ops->copy_user)
- transfer = (pcm_transfer_f)substream->ops->copy_user;
+ if (substream->ops->copy)
+ transfer = substream->ops->copy;
else
transfer = is_playback ?
default_write_copy : default_read_copy;
@@ -2307,7 +2314,7 @@ snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream,
if (!is_playback)
snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_CPU);
err = writer(substream, appl_ofs, data, offset, frames,
- transfer);
+ transfer, in_kernel);
if (is_playback)
snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
snd_pcm_stream_lock_irq(substream);
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 95fc56e403b1..bd9ddf412b46 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -809,7 +809,7 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
runtime->boundary *= 2;
/* clear the buffer for avoiding possible kernel info leaks */
- if (runtime->dma_area && !substream->ops->copy_user) {
+ if (runtime->dma_area && !substream->ops->copy) {
size_t size = runtime->dma_bytes;
if (runtime->info & SNDRV_PCM_INFO_MMAP)
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c
index 2d3cec908154..ba06484ac4aa 100644
--- a/sound/core/rawmidi.c
+++ b/sound/core/rawmidi.c
@@ -44,11 +44,11 @@ static LIST_HEAD(snd_rawmidi_devices);
static DEFINE_MUTEX(register_mutex);
#define rmidi_err(rmidi, fmt, args...) \
- dev_err(&(rmidi)->dev, fmt, ##args)
+ dev_err((rmidi)->dev, fmt, ##args)
#define rmidi_warn(rmidi, fmt, args...) \
- dev_warn(&(rmidi)->dev, fmt, ##args)
+ dev_warn((rmidi)->dev, fmt, ##args)
#define rmidi_dbg(rmidi, fmt, args...) \
- dev_dbg(&(rmidi)->dev, fmt, ##args)
+ dev_dbg((rmidi)->dev, fmt, ##args)
struct snd_rawmidi_status32 {
s32 stream;
@@ -1877,11 +1877,6 @@ static int snd_rawmidi_alloc_substreams(struct snd_rawmidi *rmidi,
return 0;
}
-static void release_rawmidi_device(struct device *dev)
-{
- kfree(container_of(dev, struct snd_rawmidi, dev));
-}
-
/* used for both rawmidi and ump */
int snd_rawmidi_init(struct snd_rawmidi *rmidi,
struct snd_card *card, char *id, int device,
@@ -1906,12 +1901,13 @@ int snd_rawmidi_init(struct snd_rawmidi *rmidi,
if (id != NULL)
strscpy(rmidi->id, id, sizeof(rmidi->id));
- snd_device_initialize(&rmidi->dev, card);
- rmidi->dev.release = release_rawmidi_device;
+ err = snd_device_alloc(&rmidi->dev, card);
+ if (err < 0)
+ return err;
if (rawmidi_is_ump(rmidi))
- dev_set_name(&rmidi->dev, "umpC%iD%i", card->number, device);
+ dev_set_name(rmidi->dev, "umpC%iD%i", card->number, device);
else
- dev_set_name(&rmidi->dev, "midiC%iD%i", card->number, device);
+ dev_set_name(rmidi->dev, "midiC%iD%i", card->number, device);
err = snd_rawmidi_alloc_substreams(rmidi,
&rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT],
@@ -1996,7 +1992,8 @@ int snd_rawmidi_free(struct snd_rawmidi *rmidi)
snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]);
if (rmidi->private_free)
rmidi->private_free(rmidi);
- put_device(&rmidi->dev);
+ put_device(rmidi->dev);
+ kfree(rmidi);
return 0;
}
EXPORT_SYMBOL_GPL(snd_rawmidi_free);
@@ -2038,7 +2035,7 @@ static int snd_rawmidi_dev_register(struct snd_device *device)
err = snd_register_device(SNDRV_DEVICE_TYPE_RAWMIDI,
rmidi->card, rmidi->device,
- &snd_rawmidi_f_ops, rmidi, &rmidi->dev);
+ &snd_rawmidi_f_ops, rmidi, rmidi->dev);
if (err < 0) {
rmidi_err(rmidi, "unable to register\n");
goto error;
@@ -2103,7 +2100,7 @@ static int snd_rawmidi_dev_register(struct snd_device *device)
return 0;
error_unregister:
- snd_unregister_device(&rmidi->dev);
+ snd_unregister_device(rmidi->dev);
error:
mutex_lock(&register_mutex);
list_del(&rmidi->list);
@@ -2142,7 +2139,7 @@ static int snd_rawmidi_dev_disconnect(struct snd_device *device)
rmidi->ossreg = 0;
}
#endif /* CONFIG_SND_OSSEMUL */
- snd_unregister_device(&rmidi->dev);
+ snd_unregister_device(rmidi->dev);
mutex_unlock(&rmidi->open_mutex);
mutex_unlock(&register_mutex);
return 0;
diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
index e3f9ea67d019..42a705141050 100644
--- a/sound/core/seq/seq_clientmgr.c
+++ b/sound/core/seq/seq_clientmgr.c
@@ -2721,7 +2721,7 @@ static const struct file_operations snd_seq_f_ops =
.compat_ioctl = snd_seq_ioctl_compat,
};
-static struct device seq_dev;
+static struct device *seq_dev;
/*
* register sequencer device
@@ -2730,15 +2730,17 @@ int __init snd_sequencer_device_init(void)
{
int err;
- snd_device_initialize(&seq_dev, NULL);
- dev_set_name(&seq_dev, "seq");
+ err = snd_device_alloc(&seq_dev, NULL);
+ if (err < 0)
+ return err;
+ dev_set_name(seq_dev, "seq");
mutex_lock(&register_mutex);
err = snd_register_device(SNDRV_DEVICE_TYPE_SEQUENCER, NULL, 0,
- &snd_seq_f_ops, NULL, &seq_dev);
+ &snd_seq_f_ops, NULL, seq_dev);
mutex_unlock(&register_mutex);
if (err < 0) {
- put_device(&seq_dev);
+ put_device(seq_dev);
return err;
}
@@ -2752,6 +2754,6 @@ int __init snd_sequencer_device_init(void)
*/
void snd_sequencer_device_done(void)
{
- snd_unregister_device(&seq_dev);
- put_device(&seq_dev);
+ snd_unregister_device(seq_dev);
+ put_device(seq_dev);
}
diff --git a/sound/core/timer.c b/sound/core/timer.c
index 9d0d2a5c2e15..e6e551d4a29e 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -2301,7 +2301,7 @@ static void snd_timer_free_all(void)
snd_timer_free(timer);
}
-static struct device timer_dev;
+static struct device *timer_dev;
/*
* ENTRY functions
@@ -2311,8 +2311,10 @@ static int __init alsa_timer_init(void)
{
int err;
- snd_device_initialize(&timer_dev, NULL);
- dev_set_name(&timer_dev, "timer");
+ err = snd_device_alloc(&timer_dev, NULL);
+ if (err < 0)
+ return err;
+ dev_set_name(timer_dev, "timer");
#ifdef SNDRV_OSS_INFO_DEV_TIMERS
snd_oss_info_register(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1,
@@ -2326,7 +2328,7 @@ static int __init alsa_timer_init(void)
}
err = snd_register_device(SNDRV_DEVICE_TYPE_TIMER, NULL, 0,
- &snd_timer_f_ops, NULL, &timer_dev);
+ &snd_timer_f_ops, NULL, timer_dev);
if (err < 0) {
pr_err("ALSA: unable to register timer device (%i)\n", err);
snd_timer_free_all();
@@ -2337,15 +2339,15 @@ static int __init alsa_timer_init(void)
return 0;
put_timer:
- put_device(&timer_dev);
+ put_device(timer_dev);
return err;
}
static void __exit alsa_timer_exit(void)
{
- snd_unregister_device(&timer_dev);
+ snd_unregister_device(timer_dev);
snd_timer_free_all();
- put_device(&timer_dev);
+ put_device(timer_dev);
snd_timer_proc_done();
#ifdef SNDRV_OSS_INFO_DEV_TIMERS
snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1);
diff --git a/sound/core/ump.c b/sound/core/ump.c
index 246348766ec1..3bef1944e955 100644
--- a/sound/core/ump.c
+++ b/sound/core/ump.c
@@ -13,10 +13,10 @@
#include <sound/ump.h>
#include <sound/ump_convert.h>
-#define ump_err(ump, fmt, args...) dev_err(&(ump)->core.dev, fmt, ##args)
-#define ump_warn(ump, fmt, args...) dev_warn(&(ump)->core.dev, fmt, ##args)
-#define ump_info(ump, fmt, args...) dev_info(&(ump)->core.dev, fmt, ##args)
-#define ump_dbg(ump, fmt, args...) dev_dbg(&(ump)->core.dev, fmt, ##args)
+#define ump_err(ump, fmt, args...) dev_err((ump)->core.dev, fmt, ##args)
+#define ump_warn(ump, fmt, args...) dev_warn((ump)->core.dev, fmt, ##args)
+#define ump_info(ump, fmt, args...) dev_info((ump)->core.dev, fmt, ##args)
+#define ump_dbg(ump, fmt, args...) dev_dbg((ump)->core.dev, fmt, ##args)
static int snd_ump_dev_register(struct snd_rawmidi *rmidi);
static int snd_ump_dev_unregister(struct snd_rawmidi *rmidi);
@@ -984,7 +984,7 @@ static int snd_ump_legacy_open(struct snd_rawmidi_substream *substream)
{
struct snd_ump_endpoint *ump = substream->rmidi->private_data;
int dir = substream->stream;
- int group = substream->number;
+ int group = ump->legacy_mapping[substream->number];
int err;
mutex_lock(&ump->open_mutex);
@@ -1016,7 +1016,7 @@ static int snd_ump_legacy_close(struct snd_rawmidi_substream *substream)
{
struct snd_ump_endpoint *ump = substream->rmidi->private_data;
int dir = substream->stream;
- int group = substream->number;
+ int group = ump->legacy_mapping[substream->number];
mutex_lock(&ump->open_mutex);
spin_lock_irq(&ump->legacy_locks[dir]);
@@ -1123,21 +1123,62 @@ static void process_legacy_input(struct snd_ump_endpoint *ump, const u32 *src,
spin_unlock_irqrestore(&ump->legacy_locks[dir], flags);
}
+/* Fill ump->legacy_mapping[] for groups to be used for legacy rawmidi */
+static int fill_legacy_mapping(struct snd_ump_endpoint *ump)
+{
+ struct snd_ump_block *fb;
+ unsigned int group_maps = 0;
+ int i, num;
+
+ if (ump->info.flags & SNDRV_UMP_EP_INFO_STATIC_BLOCKS) {
+ list_for_each_entry(fb, &ump->block_list, list) {
+ for (i = 0; i < fb->info.num_groups; i++)
+ group_maps |= 1U << (fb->info.first_group + i);
+ }
+ if (!group_maps)
+ ump_info(ump, "No UMP Group is found in FB\n");
+ }
+
+ /* use all groups for non-static case */
+ if (!group_maps)
+ group_maps = (1U << SNDRV_UMP_MAX_GROUPS) - 1;
+
+ num = 0;
+ for (i = 0; i < SNDRV_UMP_MAX_GROUPS; i++)
+ if (group_maps & (1U << i))
+ ump->legacy_mapping[num++] = i;
+
+ return num;
+}
+
+static void fill_substream_names(struct snd_ump_endpoint *ump,
+ struct snd_rawmidi *rmidi, int dir)
+{
+ struct snd_rawmidi_substream *s;
+
+ list_for_each_entry(s, &rmidi->streams[dir].substreams, list)
+ snprintf(s->name, sizeof(s->name), "Group %d (%.16s)",
+ ump->legacy_mapping[s->number] + 1, ump->info.name);
+}
+
int snd_ump_attach_legacy_rawmidi(struct snd_ump_endpoint *ump,
char *id, int device)
{
struct snd_rawmidi *rmidi;
bool input, output;
- int err;
+ int err, num;
- ump->out_cvts = kcalloc(16, sizeof(*ump->out_cvts), GFP_KERNEL);
+ ump->out_cvts = kcalloc(SNDRV_UMP_MAX_GROUPS,
+ sizeof(*ump->out_cvts), GFP_KERNEL);
if (!ump->out_cvts)
return -ENOMEM;
+ num = fill_legacy_mapping(ump);
+
input = ump->core.info_flags & SNDRV_RAWMIDI_INFO_INPUT;
output = ump->core.info_flags & SNDRV_RAWMIDI_INFO_OUTPUT;
err = snd_rawmidi_new(ump->core.card, id, device,
- output ? 16 : 0, input ? 16 : 0,
+ output ? num : 0, input ? num : 0,
&rmidi);
if (err < 0) {
kfree(ump->out_cvts);
@@ -1150,10 +1191,17 @@ int snd_ump_attach_legacy_rawmidi(struct snd_ump_endpoint *ump,
if (output)
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
&snd_ump_legacy_output_ops);
+ snprintf(rmidi->name, sizeof(rmidi->name), "%.68s (MIDI 1.0)",
+ ump->info.name);
rmidi->info_flags = ump->core.info_flags & ~SNDRV_RAWMIDI_INFO_UMP;
rmidi->ops = &snd_ump_legacy_ops;
rmidi->private_data = ump;
ump->legacy_rmidi = rmidi;
+ if (input)
+ fill_substream_names(ump, rmidi, SNDRV_RAWMIDI_STREAM_INPUT);
+ if (output)
+ fill_substream_names(ump, rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT);
+
ump_dbg(ump, "Created a legacy rawmidi #%d (%s)\n", device, id);
return 0;
}
diff --git a/sound/core/vmaster.c b/sound/core/vmaster.c
index d0f11f37889b..378d2c7c3d4a 100644
--- a/sound/core/vmaster.c
+++ b/sound/core/vmaster.c
@@ -280,6 +280,34 @@ int _snd_ctl_add_follower(struct snd_kcontrol *master,
}
EXPORT_SYMBOL(_snd_ctl_add_follower);
+/**
+ * snd_ctl_add_followers - add multiple followers to vmaster
+ * @card: card instance
+ * @master: the target vmaster kcontrol object
+ * @list: NULL-terminated list of name strings of followers to be added
+ *
+ * Adds the multiple follower kcontrols with the given names.
+ * Returns 0 for success or a negative error code.
+ */
+int snd_ctl_add_followers(struct snd_card *card, struct snd_kcontrol *master,
+ const char * const *list)
+{
+ struct snd_kcontrol *follower;
+ int err;
+
+ for (; *list; list++) {
+ follower = snd_ctl_find_id_mixer(card, *list);
+ if (follower) {
+ err = snd_ctl_add_follower(master, follower);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_ctl_add_followers);
+
/*
* ctl callbacks for master controls
*/
diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig
index 41c171468c1e..6debd8e95cb7 100644
--- a/sound/drivers/Kconfig
+++ b/sound/drivers/Kconfig
@@ -111,6 +111,7 @@ config SND_ALOOP
config SND_PCMTEST
tristate "Virtual PCM test driver"
+ depends on DEBUG_FS
select SND_PCM
help
Say 'Y' or 'M' to include support for the Virtual PCM test driver.
diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c
index 9c17b49a2ae1..4317677ba24a 100644
--- a/sound/drivers/dummy.c
+++ b/sound/drivers/dummy.c
@@ -626,14 +626,7 @@ static int alloc_fake_buffer(void)
static int dummy_pcm_copy(struct snd_pcm_substream *substream,
int channel, unsigned long pos,
- void __user *dst, unsigned long bytes)
-{
- return 0; /* do nothing */
-}
-
-static int dummy_pcm_copy_kernel(struct snd_pcm_substream *substream,
- int channel, unsigned long pos,
- void *dst, unsigned long bytes)
+ struct iov_iter *iter, unsigned long bytes)
{
return 0; /* do nothing */
}
@@ -667,8 +660,7 @@ static const struct snd_pcm_ops dummy_pcm_ops_no_buf = {
.prepare = dummy_pcm_prepare,
.trigger = dummy_pcm_trigger,
.pointer = dummy_pcm_pointer,
- .copy_user = dummy_pcm_copy,
- .copy_kernel = dummy_pcm_copy_kernel,
+ .copy = dummy_pcm_copy,
.fill_silence = dummy_pcm_silence,
.page = dummy_pcm_page,
};
diff --git a/sound/drivers/pcmtest.c b/sound/drivers/pcmtest.c
index 291e7fe47893..b59b78a09224 100644
--- a/sound/drivers/pcmtest.c
+++ b/sound/drivers/pcmtest.c
@@ -41,8 +41,6 @@
#include <linux/debugfs.h>
#include <linux/delay.h>
-#define DEVNAME "pcmtestd"
-#define CARD_NAME "pcm-test-card"
#define TIMER_PER_SEC 5
#define TIMER_INTERVAL (HZ / TIMER_PER_SEC)
#define DELAY_JIFFIES HZ
@@ -65,6 +63,7 @@ static int inject_delay;
static bool inject_hwpars_err;
static bool inject_prepare_err;
static bool inject_trigger_err;
+static bool inject_open_err;
static short fill_mode = FILL_MODE_PAT;
@@ -73,11 +72,11 @@ static u8 ioctl_reset_test;
static struct dentry *driver_debug_dir;
module_param(index, int, 0444);
-MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard");
+MODULE_PARM_DESC(index, "Index value for pcmtest soundcard");
module_param(id, charp, 0444);
-MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard");
+MODULE_PARM_DESC(id, "ID string for pcmtest soundcard");
module_param(enable, bool, 0444);
-MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
+MODULE_PARM_DESC(enable, "Enable pcmtest soundcard.");
module_param(fill_mode, short, 0600);
MODULE_PARM_DESC(fill_mode, "Buffer fill mode: rand(0) or pattern(1)");
module_param(inject_delay, int, 0600);
@@ -88,6 +87,8 @@ module_param(inject_prepare_err, bool, 0600);
MODULE_PARM_DESC(inject_prepare_err, "Inject EINVAL error in the 'prepare' callback");
module_param(inject_trigger_err, bool, 0600);
MODULE_PARM_DESC(inject_trigger_err, "Inject EINVAL error in the 'trigger' callback");
+module_param(inject_open_err, bool, 0600);
+MODULE_PARM_DESC(inject_open_err, "Inject EBUSY error in the 'open' callback");
struct pcmtst {
struct snd_pcm *pcm;
@@ -107,6 +108,7 @@ struct pcmtst_buf_iter {
size_t total_bytes; // Total bytes read/written
size_t chan_block; // Bytes in one channel buffer when non-interleaved
struct snd_pcm_substream *substream;
+ bool suspend; // We need to pause timer without shutting it down
struct timer_list timer_instance;
};
@@ -114,7 +116,8 @@ static struct snd_pcm_hardware snd_pcmtst_hw = {
.info = (SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_NONINTERLEAVED |
- SNDRV_PCM_INFO_MMAP_VALID),
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE),
.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_8000_48000,
.rate_min = 8000,
@@ -140,7 +143,8 @@ static inline void inc_buf_pos(struct pcmtst_buf_iter *v_iter, size_t by, size_t
{
v_iter->total_bytes += by;
v_iter->buf_pos += by;
- v_iter->buf_pos %= bytes;
+ if (v_iter->buf_pos >= bytes)
+ v_iter->buf_pos %= bytes;
}
/*
@@ -196,10 +200,10 @@ static void check_buf_block_ni(struct pcmtst_buf_iter *v_iter, struct snd_pcm_ru
u8 current_byte;
for (i = 0; i < v_iter->b_rw; i++) {
- current_byte = runtime->dma_area[buf_pos_n(v_iter, channels, i % channels)];
+ ch_num = i % channels;
+ current_byte = runtime->dma_area[buf_pos_n(v_iter, channels, ch_num)];
if (!current_byte)
break;
- ch_num = i % channels;
if (current_byte != patt_bufs[ch_num].buf[(v_iter->total_bytes / channels)
% patt_bufs[ch_num].len]) {
v_iter->is_buf_corrupted = true;
@@ -225,7 +229,7 @@ static void check_buf_block(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runti
/*
* Fill buffer in the non-interleaved mode. The order of samples is C0, ..., C0, C1, ..., C1, C2...
- * The channel buffers lay in the DMA buffer continuously (see default copy_user and copy_kernel
+ * The channel buffers lay in the DMA buffer continuously (see default copy
* handlers in the pcm_lib.c file).
*
* Here we increment the DMA buffer position every time we write a byte to any channel 'buffer'.
@@ -239,7 +243,7 @@ static void fill_block_pattern_n(struct pcmtst_buf_iter *v_iter, struct snd_pcm_
for (i = 0; i < v_iter->b_rw; i++) {
ch_num = i % channels;
- runtime->dma_area[buf_pos_n(v_iter, channels, i % channels)] =
+ runtime->dma_area[buf_pos_n(v_iter, channels, ch_num)] =
patt_bufs[ch_num].buf[(v_iter->total_bytes / channels)
% patt_bufs[ch_num].len];
inc_buf_pos(v_iter, 1, runtime->dma_bytes);
@@ -344,6 +348,9 @@ static void timer_timeout(struct timer_list *data)
v_iter = from_timer(v_iter, data, timer_instance);
substream = v_iter->substream;
+ if (v_iter->suspend)
+ return;
+
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && !v_iter->is_buf_corrupted)
check_buf_block(v_iter, substream->runtime);
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
@@ -356,7 +363,9 @@ static void timer_timeout(struct timer_list *data)
v_iter->period_pos %= v_iter->period_bytes;
snd_pcm_period_elapsed(substream);
}
- mod_timer(&v_iter->timer_instance, jiffies + TIMER_INTERVAL + inject_delay);
+
+ if (!v_iter->suspend)
+ mod_timer(&v_iter->timer_instance, jiffies + TIMER_INTERVAL + inject_delay);
}
static int snd_pcmtst_pcm_open(struct snd_pcm_substream *substream)
@@ -364,23 +373,22 @@ static int snd_pcmtst_pcm_open(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct pcmtst_buf_iter *v_iter;
+ if (inject_open_err)
+ return -EBUSY;
+
v_iter = kzalloc(sizeof(*v_iter), GFP_KERNEL);
if (!v_iter)
return -ENOMEM;
+ v_iter->substream = substream;
runtime->hw = snd_pcmtst_hw;
runtime->private_data = v_iter;
- v_iter->substream = substream;
- v_iter->buf_pos = 0;
- v_iter->is_buf_corrupted = false;
- v_iter->period_pos = 0;
- v_iter->total_bytes = 0;
playback_capture_test = 0;
ioctl_reset_test = 0;
timer_setup(&v_iter->timer_instance, timer_timeout, 0);
- mod_timer(&v_iter->timer_instance, jiffies + TIMER_INTERVAL);
+
return 0;
}
@@ -395,26 +403,40 @@ static int snd_pcmtst_pcm_close(struct snd_pcm_substream *substream)
return 0;
}
+static inline void reset_buf_iterator(struct pcmtst_buf_iter *v_iter)
+{
+ v_iter->buf_pos = 0;
+ v_iter->is_buf_corrupted = false;
+ v_iter->period_pos = 0;
+ v_iter->total_bytes = 0;
+}
+
+static inline void start_pcmtest_timer(struct pcmtst_buf_iter *v_iter)
+{
+ v_iter->suspend = false;
+ mod_timer(&v_iter->timer_instance, jiffies + TIMER_INTERVAL);
+}
+
static int snd_pcmtst_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct pcmtst_buf_iter *v_iter = runtime->private_data;
+ struct pcmtst_buf_iter *v_iter = substream->runtime->private_data;
if (inject_trigger_err)
return -EINVAL;
-
- v_iter->sample_bytes = runtime->sample_bits / 8;
- v_iter->period_bytes = frames_to_bytes(runtime, runtime->period_size);
- if (runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED ||
- runtime->access == SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED) {
- v_iter->chan_block = runtime->dma_bytes / runtime->channels;
- v_iter->interleaved = false;
- } else {
- v_iter->interleaved = true;
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ reset_buf_iterator(v_iter);
+ start_pcmtest_timer(v_iter);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ start_pcmtest_timer(v_iter);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ // We can't call timer_shutdown_sync here, as it is forbidden to sleep here
+ v_iter->suspend = true;
+ break;
}
- // We want to record RATE * ch_cnt samples per sec, it is rate * sample_bytes * ch_cnt bytes
- v_iter->s_rw_ch = runtime->rate / TIMER_PER_SEC;
- v_iter->b_rw = v_iter->s_rw_ch * v_iter->sample_bytes * runtime->channels;
return 0;
}
@@ -446,8 +468,24 @@ static void pcmtst_pdev_release(struct device *dev)
static int snd_pcmtst_pcm_prepare(struct snd_pcm_substream *substream)
{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcmtst_buf_iter *v_iter = runtime->private_data;
+
if (inject_prepare_err)
return -EINVAL;
+
+ v_iter->sample_bytes = samples_to_bytes(runtime, 1);
+ v_iter->period_bytes = snd_pcm_lib_period_bytes(substream);
+ v_iter->interleaved = true;
+ if (runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED ||
+ runtime->access == SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED) {
+ v_iter->chan_block = snd_pcm_lib_buffer_bytes(substream) / runtime->channels;
+ v_iter->interleaved = false;
+ }
+ // We want to record RATE * ch_cnt samples per sec, it is rate * sample_bytes * ch_cnt bytes
+ v_iter->s_rw_ch = runtime->rate / TIMER_PER_SEC;
+ v_iter->b_rw = v_iter->s_rw_ch * v_iter->sample_bytes * runtime->channels;
+
return 0;
}
diff --git a/sound/drivers/serial-generic.c b/sound/drivers/serial-generic.c
index ea8072bf3aa4..c8db6c75d133 100644
--- a/sound/drivers/serial-generic.c
+++ b/sound/drivers/serial-generic.c
@@ -16,7 +16,7 @@
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/serdev.h>
#include <linux/serial_reg.h>
#include <linux/slab.h>
diff --git a/sound/firewire/dice/Makefile b/sound/firewire/dice/Makefile
index a5f3fbf28b8c..bac8712f9014 100644
--- a/sound/firewire/dice/Makefile
+++ b/sound/firewire/dice/Makefile
@@ -2,5 +2,5 @@
snd-dice-objs := dice-transaction.o dice-stream.o dice-proc.o dice-midi.o \
dice-pcm.o dice-hwdep.o dice.o dice-tcelectronic.o \
dice-alesis.o dice-extension.o dice-mytek.o dice-presonus.o \
- dice-harman.o dice-focusrite.o
+ dice-harman.o dice-focusrite.o dice-weiss.o
obj-$(CONFIG_SND_DICE) += snd-dice.o
diff --git a/sound/firewire/dice/dice-weiss.c b/sound/firewire/dice/dice-weiss.c
new file mode 100644
index 000000000000..129d43408956
--- /dev/null
+++ b/sound/firewire/dice/dice-weiss.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0
+// dice-weiss.c - a part of driver for DICE based devices
+//
+// Copyright (c) 2023 Rolf Anderegg and Michele Perrone
+
+#include "dice.h"
+
+struct dice_weiss_spec {
+ unsigned int tx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT];
+ unsigned int rx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT];
+};
+
+// Weiss DAC202: 192kHz 2-channel DAC
+static const struct dice_weiss_spec dac202 = {
+ .tx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+ .rx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+};
+
+// Weiss MAN301: 192kHz 2-channel music archive network player
+static const struct dice_weiss_spec man301 = {
+ .tx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+ .rx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+};
+
+// Weiss INT202: 192kHz unidirectional 2-channel digital Firewire nterface
+static const struct dice_weiss_spec int202 = {
+ .tx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+ .rx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+};
+
+// Weiss INT203: 192kHz bidirectional 2-channel digital Firewire nterface
+static const struct dice_weiss_spec int203 = {
+ .tx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+ .rx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+};
+
+// Weiss ADC2: 192kHz A/D converter with microphone preamps and line nputs
+static const struct dice_weiss_spec adc2 = {
+ .tx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+ .rx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+};
+
+// Weiss DAC2/Minerva: 192kHz 2-channel DAC
+static const struct dice_weiss_spec dac2_minerva = {
+ .tx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+ .rx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+};
+
+// Weiss Vesta: 192kHz 2-channel Firewire to AES/EBU interface
+static const struct dice_weiss_spec vesta = {
+ .tx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+ .rx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+};
+
+// Weiss AFI1: 192kHz 24-channel Firewire to ADAT or AES/EBU interface
+static const struct dice_weiss_spec afi1 = {
+ .tx_pcm_chs = {{24, 16, 8}, {0, 0, 0} },
+ .rx_pcm_chs = {{24, 16, 8}, {0, 0, 0} },
+};
+
+int snd_dice_detect_weiss_formats(struct snd_dice *dice)
+{
+ static const struct {
+ u32 model_id;
+ const struct dice_weiss_spec *spec;
+ } *entry, entries[] = {
+ {0x000007, &dac202},
+ {0x000008, &dac202}, // Maya edition: same audio I/O as DAC202.
+ {0x000006, &int202},
+ {0x00000a, &int203},
+ {0x00000b, &man301},
+ {0x000001, &adc2},
+ {0x000003, &dac2_minerva},
+ {0x000002, &vesta},
+ {0x000004, &afi1},
+ };
+ struct fw_csr_iterator it;
+ int key, val, model_id;
+ int i;
+
+ model_id = 0;
+ fw_csr_iterator_init(&it, dice->unit->directory);
+ while (fw_csr_iterator_next(&it, &key, &val)) {
+ if (key == CSR_MODEL) {
+ model_id = val;
+ break;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(entries); ++i) {
+ entry = entries + i;
+ if (entry->model_id == model_id)
+ break;
+ }
+ if (i == ARRAY_SIZE(entries))
+ return -ENODEV;
+
+ memcpy(dice->tx_pcm_chs, entry->spec->tx_pcm_chs,
+ MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * sizeof(unsigned int));
+ memcpy(dice->rx_pcm_chs, entry->spec->rx_pcm_chs,
+ MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * sizeof(unsigned int));
+
+ return 0;
+}
diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c
index 6c93e6e4982c..d362e4251c68 100644
--- a/sound/firewire/dice/dice.c
+++ b/sound/firewire/dice/dice.c
@@ -392,6 +392,69 @@ static const struct ieee1394_device_id dice_id_table[] = {
.model_id = 0x0000de,
.driver_data = (kernel_ulong_t)snd_dice_detect_focusrite_pro40_tcd3070_formats,
},
+ // Weiss DAC202: 192kHz 2-channel DAC
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_WEISS,
+ .model_id = 0x000007,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_weiss_formats,
+ },
+ // Weiss DAC202: 192kHz 2-channel DAC (Maya edition)
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_WEISS,
+ .model_id = 0x000008,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_weiss_formats,
+ },
+ // Weiss MAN301: 192kHz 2-channel music archive network player
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_WEISS,
+ .model_id = 0x00000b,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_weiss_formats,
+ },
+ // Weiss INT202: 192kHz unidirectional 2-channel digital Firewire face
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_WEISS,
+ .model_id = 0x000006,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_weiss_formats,
+ },
+ // Weiss INT203: 192kHz bidirectional 2-channel digital Firewire face
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_WEISS,
+ .model_id = 0x00000a,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_weiss_formats,
+ },
+ // Weiss ADC2: 192kHz A/D converter with microphone preamps and inputs
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_WEISS,
+ .model_id = 0x000001,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_weiss_formats,
+ },
+ // Weiss DAC2/Minerva: 192kHz 2-channel DAC
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_WEISS,
+ .model_id = 0x000003,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_weiss_formats,
+ },
+ // Weiss Vesta: 192kHz 2-channel Firewire to AES/EBU interface
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_WEISS,
+ .model_id = 0x000002,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_weiss_formats,
+ },
+ // Weiss AFI1: 192kHz 24-channel Firewire to ADAT or AES/EBU face
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_WEISS,
+ .model_id = 0x000004,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_weiss_formats,
+ },
{
.match_flags = IEEE1394_MATCH_VERSION,
.version = DICE_INTERFACE,
diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h
index 674f7d552c2e..4c0ad7335998 100644
--- a/sound/firewire/dice/dice.h
+++ b/sound/firewire/dice/dice.h
@@ -232,5 +232,6 @@ int snd_dice_detect_mytek_formats(struct snd_dice *dice);
int snd_dice_detect_presonus_formats(struct snd_dice *dice);
int snd_dice_detect_harman_formats(struct snd_dice *dice);
int snd_dice_detect_focusrite_pro40_tcd3070_formats(struct snd_dice *dice);
+int snd_dice_detect_weiss_formats(struct snd_dice *dice);
#endif
diff --git a/sound/hda/intel-dsp-config.c b/sound/hda/intel-dsp-config.c
index dcf2453138a5..24a948baf1bc 100644
--- a/sound/hda/intel-dsp-config.c
+++ b/sound/hda/intel-dsp-config.c
@@ -167,10 +167,10 @@ static const struct config_entry config_table[] = {
#endif
/*
- * CoffeeLake, CannonLake, CometLake, IceLake, TigerLake use legacy
- * HDAudio driver except for Google Chromebooks and when DMICs are
- * present. Two cases are required since Coreboot does not expose NHLT
- * tables.
+ * CoffeeLake, CannonLake, CometLake, IceLake, TigerLake, AlderLake,
+ * RaptorLake use legacy HDAudio driver except for Google Chromebooks
+ * and when DMICs are present. Two cases are required since Coreboot
+ * does not expose NHLT tables.
*
* When the Chromebook quirk is not present, it's based on information
* that no such device exists. When the quirk is present, it could be
@@ -411,6 +411,19 @@ static const struct config_entry config_table[] = {
{
.flags = FLAG_SOF,
.device = PCI_DEVICE_ID_INTEL_HDA_ADL_P,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {}
+ }
+ },
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_ADL_P,
.codec_hid = &essx_83x6,
},
{
@@ -435,14 +448,53 @@ static const struct config_entry config_table[] = {
.device = PCI_DEVICE_ID_INTEL_HDA_ADL_M,
},
{
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_ADL_N,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {}
+ }
+ },
+ {
.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
.device = PCI_DEVICE_ID_INTEL_HDA_ADL_N,
},
{
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_RPL_P_0,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {}
+ }
+ },
+ {
.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
.device = PCI_DEVICE_ID_INTEL_HDA_RPL_P_0,
},
{
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_RPL_P_1,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {}
+ }
+ },
+ {
.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
.device = PCI_DEVICE_ID_INTEL_HDA_RPL_P_1,
},
diff --git a/sound/isa/gus/gus_pcm.c b/sound/isa/gus/gus_pcm.c
index 388db5fb65bd..850544725da7 100644
--- a/sound/isa/gus/gus_pcm.c
+++ b/sound/isa/gus/gus_pcm.c
@@ -369,7 +369,7 @@ static int playback_copy_ack(struct snd_pcm_substream *substream,
static int snd_gf1_pcm_playback_copy(struct snd_pcm_substream *substream,
int voice, unsigned long pos,
- void __user *src, unsigned long count)
+ struct iov_iter *src, unsigned long count)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct gus_pcm_private *pcmp = runtime->private_data;
@@ -379,27 +379,11 @@ static int snd_gf1_pcm_playback_copy(struct snd_pcm_substream *substream,
bpos = get_bpos(pcmp, voice, pos, len);
if (bpos < 0)
return pos;
- if (copy_from_user(runtime->dma_area + bpos, src, len))
+ if (copy_from_iter(runtime->dma_area + bpos, len, src) != len)
return -EFAULT;
return playback_copy_ack(substream, bpos, len);
}
-static int snd_gf1_pcm_playback_copy_kernel(struct snd_pcm_substream *substream,
- int voice, unsigned long pos,
- void *src, unsigned long count)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct gus_pcm_private *pcmp = runtime->private_data;
- unsigned int len = count;
- int bpos;
-
- bpos = get_bpos(pcmp, voice, pos, len);
- if (bpos < 0)
- return pos;
- memcpy(runtime->dma_area + bpos, src, len);
- return playback_copy_ack(substream, bpos, len);
-}
-
static int snd_gf1_pcm_playback_silence(struct snd_pcm_substream *substream,
int voice, unsigned long pos,
unsigned long count)
@@ -830,8 +814,7 @@ static const struct snd_pcm_ops snd_gf1_pcm_playback_ops = {
.prepare = snd_gf1_pcm_playback_prepare,
.trigger = snd_gf1_pcm_playback_trigger,
.pointer = snd_gf1_pcm_playback_pointer,
- .copy_user = snd_gf1_pcm_playback_copy,
- .copy_kernel = snd_gf1_pcm_playback_copy_kernel,
+ .copy = snd_gf1_pcm_playback_copy,
.fill_silence = snd_gf1_pcm_playback_silence,
};
diff --git a/sound/isa/sb/emu8000.c b/sound/isa/sb/emu8000.c
index e02029677743..a6405772d537 100644
--- a/sound/isa/sb/emu8000.c
+++ b/sound/isa/sb/emu8000.c
@@ -1040,10 +1040,8 @@ snd_emu8000_create_mixer(struct snd_card *card, struct snd_emu8000 *emu)
__error:
for (i = 0; i < EMU8000_NUM_CONTROLS; i++) {
- down_write(&card->controls_rwsem);
if (emu->controls[i])
snd_ctl_remove(card, emu->controls[i]);
- up_write(&card->controls_rwsem);
}
return err;
}
diff --git a/sound/isa/sb/emu8000_pcm.c b/sound/isa/sb/emu8000_pcm.c
index c8afc4347c54..c05935c2edc4 100644
--- a/sound/isa/sb/emu8000_pcm.c
+++ b/sound/isa/sb/emu8000_pcm.c
@@ -409,39 +409,25 @@ do { \
return -EAGAIN;\
} while (0)
-enum {
- COPY_USER, COPY_KERNEL, FILL_SILENCE,
-};
-
-#define GET_VAL(sval, buf, mode) \
+#define GET_VAL(sval, iter) \
do { \
- switch (mode) { \
- case FILL_SILENCE: \
+ if (!iter) \
sval = 0; \
- break; \
- case COPY_KERNEL: \
- sval = *buf++; \
- break; \
- default: \
- if (get_user(sval, (unsigned short __user *)buf)) \
- return -EFAULT; \
- buf++; \
- break; \
- } \
+ else if (copy_from_iter(&sval, 2, iter) != 2) \
+ return -EFAULT; \
} while (0)
#ifdef USE_NONINTERLEAVE
-#define LOOP_WRITE(rec, offset, _buf, count, mode) \
+#define LOOP_WRITE(rec, offset, iter, count) \
do { \
struct snd_emu8000 *emu = (rec)->emu; \
- unsigned short *buf = (__force unsigned short *)(_buf); \
snd_emu8000_write_wait(emu, 1); \
EMU8000_SMALW_WRITE(emu, offset); \
while (count > 0) { \
unsigned short sval; \
CHECK_SCHEDULER(); \
- GET_VAL(sval, buf, mode); \
+ GET_VAL(sval, iter); \
EMU8000_SMLD_WRITE(emu, sval); \
count--; \
} \
@@ -450,27 +436,14 @@ enum {
/* copy one channel block */
static int emu8k_pcm_copy(struct snd_pcm_substream *subs,
int voice, unsigned long pos,
- void __user *src, unsigned long count)
+ struct iov_iter *src, unsigned long count)
{
struct snd_emu8k_pcm *rec = subs->runtime->private_data;
/* convert to word unit */
pos = (pos << 1) + rec->loop_start[voice];
count <<= 1;
- LOOP_WRITE(rec, pos, src, count, COPY_USER);
- return 0;
-}
-
-static int emu8k_pcm_copy_kernel(struct snd_pcm_substream *subs,
- int voice, unsigned long pos,
- void *src, unsigned long count)
-{
- struct snd_emu8k_pcm *rec = subs->runtime->private_data;
-
- /* convert to word unit */
- pos = (pos << 1) + rec->loop_start[voice];
- count <<= 1;
- LOOP_WRITE(rec, pos, src, count, COPY_KERNEL);
+ LOOP_WRITE(rec, pos, src, count);
return 0;
}
@@ -483,16 +456,15 @@ static int emu8k_pcm_silence(struct snd_pcm_substream *subs,
/* convert to word unit */
pos = (pos << 1) + rec->loop_start[voice];
count <<= 1;
- LOOP_WRITE(rec, pos, NULL, count, FILL_SILENCE);
+ LOOP_WRITE(rec, pos, USER_SOCKPTR(NULL), count);
return 0;
}
#else /* interleave */
-#define LOOP_WRITE(rec, pos, _buf, count, mode) \
+#define LOOP_WRITE(rec, pos, iter, count) \
do { \
struct snd_emu8000 *emu = rec->emu; \
- unsigned short *buf = (__force unsigned short *)(_buf); \
snd_emu8000_write_wait(emu, 1); \
EMU8000_SMALW_WRITE(emu, pos + rec->loop_start[0]); \
if (rec->voices > 1) \
@@ -500,11 +472,11 @@ static int emu8k_pcm_silence(struct snd_pcm_substream *subs,
while (count > 0) { \
unsigned short sval; \
CHECK_SCHEDULER(); \
- GET_VAL(sval, buf, mode); \
+ GET_VAL(sval, iter); \
EMU8000_SMLD_WRITE(emu, sval); \
if (rec->voices > 1) { \
CHECK_SCHEDULER(); \
- GET_VAL(sval, buf, mode); \
+ GET_VAL(sval, iter); \
EMU8000_SMRD_WRITE(emu, sval); \
} \
count--; \
@@ -518,27 +490,14 @@ static int emu8k_pcm_silence(struct snd_pcm_substream *subs,
*/
static int emu8k_pcm_copy(struct snd_pcm_substream *subs,
int voice, unsigned long pos,
- void __user *src, unsigned long count)
-{
- struct snd_emu8k_pcm *rec = subs->runtime->private_data;
-
- /* convert to frames */
- pos = bytes_to_frames(subs->runtime, pos);
- count = bytes_to_frames(subs->runtime, count);
- LOOP_WRITE(rec, pos, src, count, COPY_USER);
- return 0;
-}
-
-static int emu8k_pcm_copy_kernel(struct snd_pcm_substream *subs,
- int voice, unsigned long pos,
- void *src, unsigned long count)
+ struct iov_iter *src, unsigned long count)
{
struct snd_emu8k_pcm *rec = subs->runtime->private_data;
/* convert to frames */
pos = bytes_to_frames(subs->runtime, pos);
count = bytes_to_frames(subs->runtime, count);
- LOOP_WRITE(rec, pos, src, count, COPY_KERNEL);
+ LOOP_WRITE(rec, pos, src, count);
return 0;
}
@@ -550,7 +509,7 @@ static int emu8k_pcm_silence(struct snd_pcm_substream *subs,
/* convert to frames */
pos = bytes_to_frames(subs->runtime, pos);
count = bytes_to_frames(subs->runtime, count);
- LOOP_WRITE(rec, pos, NULL, count, FILL_SILENCE);
+ LOOP_WRITE(rec, pos, NULL, count);
return 0;
}
#endif
@@ -666,8 +625,7 @@ static const struct snd_pcm_ops emu8k_pcm_ops = {
.prepare = emu8k_pcm_prepare,
.trigger = emu8k_pcm_trigger,
.pointer = emu8k_pcm_pointer,
- .copy_user = emu8k_pcm_copy,
- .copy_kernel = emu8k_pcm_copy_kernel,
+ .copy = emu8k_pcm_copy,
.fill_silence = emu8k_pcm_silence,
};
diff --git a/sound/isa/sb/sb16_csp.c b/sound/isa/sb/sb16_csp.c
index 7ad8c5f7b664..8d8357019719 100644
--- a/sound/isa/sb/sb16_csp.c
+++ b/sound/isa/sb/sb16_csp.c
@@ -1080,7 +1080,6 @@ static void snd_sb_qsound_destroy(struct snd_sb_csp * p)
card = p->chip->card;
- down_write(&card->controls_rwsem);
if (p->qsound_switch) {
snd_ctl_remove(card, p->qsound_switch);
p->qsound_switch = NULL;
@@ -1089,7 +1088,6 @@ static void snd_sb_qsound_destroy(struct snd_sb_csp * p)
snd_ctl_remove(card, p->qsound_space);
p->qsound_space = NULL;
}
- up_write(&card->controls_rwsem);
/* cancel pending transfer of QSound parameters */
spin_lock_irqsave (&p->q_lock, flags);
diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c
index 80a65b8ad7b9..25f93e56cfc7 100644
--- a/sound/pci/ac97/ac97_codec.c
+++ b/sound/pci/ac97/ac97_codec.c
@@ -2069,10 +2069,9 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
.dev_disconnect = snd_ac97_dev_disconnect,
};
- if (!rac97)
- return -EINVAL;
- if (snd_BUG_ON(!bus || !template))
+ if (snd_BUG_ON(!bus || !template || !rac97))
return -EINVAL;
+ *rac97 = NULL;
if (snd_BUG_ON(template->num >= 4))
return -EINVAL;
if (bus->codec[template->num])
diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c
index 4b5f33de70d5..1d786bd5ce3e 100644
--- a/sound/pci/ac97/ac97_patch.c
+++ b/sound/pci/ac97/ac97_patch.c
@@ -3431,11 +3431,7 @@ static const char * const follower_sws_vt1616[] = {
static struct snd_kcontrol *snd_ac97_find_mixer_ctl(struct snd_ac97 *ac97,
const char *name)
{
- struct snd_ctl_elem_id id;
- memset(&id, 0, sizeof(id));
- id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(id.name, name);
- return snd_ctl_find_id(ac97->bus->card, &id);
+ return snd_ctl_find_id_mixer(ac97->bus->card, name);
}
/* create a virtual master control and add followers */
@@ -3444,7 +3440,6 @@ static int snd_ac97_add_vmaster(struct snd_ac97 *ac97, char *name,
const char * const *followers)
{
struct snd_kcontrol *kctl;
- const char * const *s;
int err;
kctl = snd_ctl_make_virtual_master(name, tlv);
@@ -3454,20 +3449,7 @@ static int snd_ac97_add_vmaster(struct snd_ac97 *ac97, char *name,
if (err < 0)
return err;
- for (s = followers; *s; s++) {
- struct snd_kcontrol *sctl;
-
- sctl = snd_ac97_find_mixer_ctl(ac97, *s);
- if (!sctl) {
- dev_dbg(ac97->bus->card->dev,
- "Cannot find follower %s, skipped\n", *s);
- continue;
- }
- err = snd_ctl_add_follower(kctl, sctl);
- if (err < 0)
- return err;
- }
- return 0;
+ return snd_ctl_add_followers(ac97->bus->card, kctl, followers);
}
static int patch_vt1616_specific(struct snd_ac97 * ac97)
diff --git a/sound/pci/asihpi/hpi.h b/sound/pci/asihpi/hpi.h
index 3aebec763fb8..04a5cf6572cd 100644
--- a/sound/pci/asihpi/hpi.h
+++ b/sound/pci/asihpi/hpi.h
@@ -1191,19 +1191,6 @@ u16 hpi_adapter_set_mode_ex(u16 adapter_index, u32 adapter_mode,
u16 hpi_adapter_get_mode(u16 adapter_index, u32 *padapter_mode);
-u16 hpi_adapter_get_assert2(u16 adapter_index, u16 *p_assert_count,
- char *psz_assert, u32 *p_param1, u32 *p_param2,
- u32 *p_dsp_string_addr, u16 *p_processor_id);
-
-u16 hpi_adapter_test_assert(u16 adapter_index, u16 assert_id);
-
-u16 hpi_adapter_enable_capability(u16 adapter_index, u16 capability, u32 key);
-
-u16 hpi_adapter_self_test(u16 adapter_index);
-
-u16 hpi_adapter_debug_read(u16 adapter_index, u32 dsp_address, char *p_bytes,
- int *count_bytes);
-
u16 hpi_adapter_set_property(u16 adapter_index, u16 property, u16 paramter1,
u16 paramter2);
@@ -1488,9 +1475,6 @@ u16 hpi_pad_get_program_type(u32 h_control, u32 *ppTY);
u16 hpi_pad_get_rdsPI(u32 h_control, u32 *ppI);
-u16 hpi_pad_get_program_type_string(u32 h_control, const u32 data_type,
- const u32 pTY, char *psz_string, const u32 string_length);
-
/****************************/
/* AES/EBU Receiver control */
/****************************/
diff --git a/sound/pci/asihpi/hpi_internal.h b/sound/pci/asihpi/hpi_internal.h
index 6859d51389f5..e569e3b33b8e 100644
--- a/sound/pci/asihpi/hpi_internal.h
+++ b/sound/pci/asihpi/hpi_internal.h
@@ -1394,17 +1394,12 @@ u32 hpi_indexes_to_handle(const char c_object, const u16 adapter_index,
void hpi_send_recv(struct hpi_message *phm, struct hpi_response *phr);
/* used in PnP OS/driver */
-u16 hpi_subsys_create_adapter(const struct hpi_resource *p_resource,
- u16 *pw_adapter_index);
-
u16 hpi_outstream_host_buffer_get_info(u32 h_outstream, u8 **pp_buffer,
struct hpi_hostbuffer_status **pp_status);
u16 hpi_instream_host_buffer_get_info(u32 h_instream, u8 **pp_buffer,
struct hpi_hostbuffer_status **pp_status);
-u16 hpi_adapter_restart(u16 adapter_index);
-
/*
The following 3 functions were last declared in header files for
driver 3.10. HPI_ControlQuery() used to be the recommended way
diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c
index f6381c098d4f..1d5a899b2c24 100644
--- a/sound/pci/ca0106/ca0106_mixer.c
+++ b/sound/pci/ca0106/ca0106_mixer.c
@@ -706,19 +706,9 @@ static int remove_ctl(struct snd_card *card, const char *name)
return snd_ctl_remove_id(card, &id);
}
-static struct snd_kcontrol *ctl_find(struct snd_card *card, const char *name)
-{
- struct snd_ctl_elem_id sid;
- memset(&sid, 0, sizeof(sid));
- /* FIXME: strcpy is bad. */
- strcpy(sid.name, name);
- sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- return snd_ctl_find_id(card, &sid);
-}
-
static int rename_ctl(struct snd_card *card, const char *src, const char *dst)
{
- struct snd_kcontrol *kctl = ctl_find(card, src);
+ struct snd_kcontrol *kctl = snd_ctl_find_id_mixer(card, src);
if (kctl) {
snd_ctl_rename(card, kctl, dst);
return 0;
@@ -761,16 +751,6 @@ static const char * const follower_sws[] = {
NULL
};
-static void add_followers(struct snd_card *card,
- struct snd_kcontrol *master, const char * const *list)
-{
- for (; *list; list++) {
- struct snd_kcontrol *follower = ctl_find(card, *list);
- if (follower)
- snd_ctl_add_follower(master, follower);
- }
-}
-
int snd_ca0106_mixer(struct snd_ca0106 *emu)
{
int err;
@@ -852,7 +832,9 @@ int snd_ca0106_mixer(struct snd_ca0106 *emu)
err = snd_ctl_add(card, vmaster);
if (err < 0)
return err;
- add_followers(card, vmaster, follower_vols);
+ err = snd_ctl_add_followers(card, vmaster, follower_vols);
+ if (err < 0)
+ return err;
if (emu->details->spi_dac) {
vmaster = snd_ctl_make_virtual_master("Master Playback Switch",
@@ -862,7 +844,9 @@ int snd_ca0106_mixer(struct snd_ca0106 *emu)
err = snd_ctl_add(card, vmaster);
if (err < 0)
return err;
- add_followers(card, vmaster, follower_sws);
+ err = snd_ctl_add_followers(card, vmaster, follower_sws);
+ if (err < 0)
+ return err;
}
strcpy(card->mixername, "CA0106");
diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c
index 6d25c12d9ef0..1415baac9c36 100644
--- a/sound/pci/cmipci.c
+++ b/sound/pci/cmipci.c
@@ -2734,12 +2734,8 @@ static int snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_device)
}
for (idx = 0; idx < CM_SAVED_MIXERS; idx++) {
- struct snd_ctl_elem_id elem_id;
struct snd_kcontrol *ctl;
- memset(&elem_id, 0, sizeof(elem_id));
- elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(elem_id.name, cm_saved_mixer[idx].name);
- ctl = snd_ctl_find_id(cm->card, &elem_id);
+ ctl = snd_ctl_find_id_mixer(cm->card, cm_saved_mixer[idx].name);
if (ctl)
cm->mixer_res_ctl[idx] = ctl;
}
diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c
index 7d882b33d45e..f3a94bb537bd 100644
--- a/sound/pci/cs46xx/cs46xx_lib.c
+++ b/sound/pci/cs46xx/cs46xx_lib.c
@@ -2449,7 +2449,6 @@ static int cs46xx_detect_codec(struct snd_cs46xx *chip, int codec)
int snd_cs46xx_mixer(struct snd_cs46xx *chip, int spdif_device)
{
struct snd_card *card = chip->card;
- struct snd_ctl_elem_id id;
int err;
unsigned int idx;
static const struct snd_ac97_bus_ops ops = {
@@ -2490,10 +2489,8 @@ int snd_cs46xx_mixer(struct snd_cs46xx *chip, int spdif_device)
}
/* get EAPD mixer switch (for voyetra hack) */
- memset(&id, 0, sizeof(id));
- id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(id.name, "External Amplifier");
- chip->eapd_switch = snd_ctl_find_id(chip->card, &id);
+ chip->eapd_switch = snd_ctl_find_id_mixer(chip->card,
+ "External Amplifier");
#ifdef CONFIG_SND_CS46XX_NEW_DSP
if (chip->nr_ac97_codecs == 1) {
diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c
index 23adace1b969..fe72e7d77241 100644
--- a/sound/pci/emu10k1/emu10k1.c
+++ b/sound/pci/emu10k1/emu10k1.c
@@ -2,9 +2,7 @@
/*
* The driver for the EMU10K1 (SB Live!) based soundcards
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- * Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
- * Added support for Audigy 2 Value.
+ * James Courtier-Dutton <James@superbug.co.uk>
*/
#include <linux/init.h>
@@ -176,9 +174,6 @@ static int snd_card_emu10k1_probe(struct pci_dev *pci,
if (err < 0)
return err;
- if (emu->card_capabilities->emu_model)
- schedule_delayed_work(&emu->emu1010.firmware_work, 0);
-
pci_set_drvdata(pci, card);
dev++;
return 0;
@@ -194,7 +189,8 @@ static int snd_emu10k1_suspend(struct device *dev)
emu->suspend = 1;
- cancel_delayed_work_sync(&emu->emu1010.firmware_work);
+ cancel_work_sync(&emu->emu1010.firmware_work);
+ cancel_work_sync(&emu->emu1010.clock_work);
snd_ac97_suspend(emu->ac97);
@@ -224,9 +220,6 @@ static int snd_emu10k1_resume(struct device *dev)
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
- if (emu->card_capabilities->emu_model)
- schedule_delayed_work(&emu->emu1010.firmware_work, 0);
-
return 0;
}
diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c
index 58ed72de6403..de5c41e578e1 100644
--- a/sound/pci/emu10k1/emu10k1_main.c
+++ b/sound/pci/emu10k1/emu10k1_main.c
@@ -1,19 +1,11 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ * James Courtier-Dutton <James@superbug.co.uk>
+ * Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
* Creative Labs, Inc.
- * Routines for control of EMU10K1 chips
- *
- * Copyright (c) by James Courtier-Dutton <James@superbug.co.uk>
- * Added support for Audigy 2 Value.
- * Added EMU 1010 support.
- * General bug fixes and enhancements.
*
- * BUGS:
- * --
- *
- * TODO:
- * --
+ * Routines for control of EMU10K1 chips
*/
#include <linux/sched.h>
@@ -391,7 +383,10 @@ static void snd_emu10k1_audio_enable(struct snd_emu10k1 *emu)
}
#endif
- snd_emu10k1_intr_enable(emu, INTE_PCIERRORENABLE);
+ if (emu->card_capabilities->emu_model)
+ snd_emu10k1_intr_enable(emu, INTE_PCIERRORENABLE | INTE_A_GPIOENABLE);
+ else
+ snd_emu10k1_intr_enable(emu, INTE_PCIERRORENABLE);
}
int snd_emu10k1_done(struct snd_emu10k1 *emu)
@@ -664,7 +659,6 @@ static int snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu,
u16 reg;
u8 value;
__always_unused u16 write_post;
- unsigned long flags;
if (!fw_entry)
return -EIO;
@@ -676,7 +670,7 @@ static int snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu,
* GPIO5 -> FPGA DIN
* FPGA CONFIG OFF -> FPGA PGMN
*/
- spin_lock_irqsave(&emu->emu_lock, flags);
+ spin_lock_irq(&emu->emu_lock);
outw(0x00, emu->port + A_GPIO); /* Set PGMN low for 100uS. */
write_post = inw(emu->port + A_GPIO);
udelay(100);
@@ -699,7 +693,7 @@ static int snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu,
/* After programming, set GPIO bit 4 high again. */
outw(0x10, emu->port + A_GPIO);
write_post = inw(emu->port + A_GPIO);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
+ spin_unlock_irq(&emu->emu_lock);
return 0;
}
@@ -745,14 +739,13 @@ static void emu1010_firmware_work(struct work_struct *work)
int err;
emu = container_of(work, struct snd_emu10k1,
- emu1010.firmware_work.work);
+ emu1010.firmware_work);
if (emu->card->shutdown)
return;
#ifdef CONFIG_PM_SLEEP
if (emu->suspend)
return;
#endif
- snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp); /* IRQ Status */
snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg); /* OPTIONS: Which cards are attached to the EMU */
if (reg & EMU_HANA_OPTION_DOCK_OFFLINE) {
/* Audio Dock attached */
@@ -763,13 +756,8 @@ static void emu1010_firmware_work(struct work_struct *work)
EMU_HANA_FPGA_CONFIG_AUDIODOCK);
err = snd_emu1010_load_firmware(emu, 1, &emu->dock_fw);
if (err < 0)
- goto next;
-
+ return;
snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0);
- snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp);
- dev_info(emu->card->dev,
- "emu1010: EMU_HANA+DOCK_IRQ_STATUS = 0x%x\n", tmp);
- /* ID, should read & 0x7f = 0x55 when FPGA programmed. */
snd_emu1010_fpga_read(emu, EMU_HANA_ID, &tmp);
dev_info(emu->card->dev,
"emu1010: EMU_HANA+DOCK_ID = 0x%x\n", tmp);
@@ -778,7 +766,7 @@ static void emu1010_firmware_work(struct work_struct *work)
dev_info(emu->card->dev,
"emu1010: Loading Audio Dock Firmware file failed, reg = 0x%x\n",
tmp);
- goto next;
+ return;
}
dev_info(emu->card->dev,
"emu1010: Audio Dock Firmware loaded\n");
@@ -790,18 +778,48 @@ static void emu1010_firmware_work(struct work_struct *work)
msleep(10);
/* Unmute all. Default is muted after a firmware load */
snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
- } else if (!reg && emu->emu1010.last_reg) {
+ }
+}
+
+static void emu1010_clock_work(struct work_struct *work)
+{
+ struct snd_emu10k1 *emu;
+ struct snd_ctl_elem_id id;
+
+ emu = container_of(work, struct snd_emu10k1,
+ emu1010.clock_work);
+ if (emu->card->shutdown)
+ return;
+#ifdef CONFIG_PM_SLEEP
+ if (emu->suspend)
+ return;
+#endif
+
+ spin_lock_irq(&emu->reg_lock);
+ // This is the only thing that can actually happen.
+ emu->emu1010.clock_source = emu->emu1010.clock_fallback;
+ emu->emu1010.wclock = 1 - emu->emu1010.clock_source;
+ snd_emu1010_update_clock(emu);
+ spin_unlock_irq(&emu->reg_lock);
+ snd_ctl_build_ioff(&id, emu->ctl_clock_source, 0);
+ snd_ctl_notify(emu->card, SNDRV_CTL_EVENT_MASK_VALUE, &id);
+}
+
+static void emu1010_interrupt(struct snd_emu10k1 *emu)
+{
+ u32 sts;
+
+ snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &sts);
+ if (sts & EMU_HANA_IRQ_DOCK_LOST) {
/* Audio Dock removed */
dev_info(emu->card->dev, "emu1010: Audio Dock detached\n");
/* The hardware auto-mutes all, so we unmute again */
snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
+ } else if (sts & EMU_HANA_IRQ_DOCK) {
+ schedule_work(&emu->emu1010.firmware_work);
}
-
- next:
- emu->emu1010.last_reg = reg;
- if (!emu->card->shutdown)
- schedule_delayed_work(&emu->emu1010.firmware_work,
- msecs_to_jiffies(1000));
+ if (sts & EMU_HANA_IRQ_WCLK_CHANGED)
+ schedule_work(&emu->emu1010.clock_work);
}
/*
@@ -870,6 +888,8 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg);
dev_info(emu->card->dev, "emu1010: Card options = 0x%x\n", reg);
+ if (reg & EMU_HANA_OPTION_DOCK_OFFLINE)
+ schedule_work(&emu->emu1010.firmware_work);
if (emu->card_capabilities->no_adat) {
emu->emu1010.optical_in = 0; /* IN_SPDIF */
emu->emu1010.optical_out = 0; /* OUT_SPDIF */
@@ -895,10 +915,12 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
/* MIDI routing */
snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, EMU_HANA_MIDI_INA_FROM_HAMOA | EMU_HANA_MIDI_INB_FROM_DOCK2);
snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, EMU_HANA_MIDI_OUT_DOCK2 | EMU_HANA_MIDI_OUT_SYNC2);
- /* IRQ Enable: All on */
- /* snd_emu1010_fpga_write(emu, EMU_HANA_IRQ_ENABLE, 0x0f); */
- /* IRQ Enable: All off */
- snd_emu1010_fpga_write(emu, EMU_HANA_IRQ_ENABLE, 0x00);
+
+ emu->gpio_interrupt = emu1010_interrupt;
+ // Note: The Audigy INTE is set later
+ snd_emu1010_fpga_write(emu, EMU_HANA_IRQ_ENABLE,
+ EMU_HANA_IRQ_DOCK | EMU_HANA_IRQ_DOCK_LOST | EMU_HANA_IRQ_WCLK_CHANGED);
+ snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &reg); // Clear pending IRQs
emu->emu1010.clock_source = 1; /* 48000 */
emu->emu1010.clock_fallback = 1; /* 48000 */
@@ -938,7 +960,8 @@ static void snd_emu10k1_free(struct snd_card *card)
/* Disable 48Volt power to Audio Dock */
snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0);
}
- cancel_delayed_work_sync(&emu->emu1010.firmware_work);
+ cancel_work_sync(&emu->emu1010.firmware_work);
+ cancel_work_sync(&emu->emu1010.clock_work);
release_firmware(emu->firmware);
release_firmware(emu->dock_fw);
snd_util_memhdr_free(emu->memhdr);
@@ -1517,7 +1540,8 @@ int snd_emu10k1_create(struct snd_card *card,
emu->irq = -1;
emu->synth = NULL;
emu->get_synth_voice = NULL;
- INIT_DELAYED_WORK(&emu->emu1010.firmware_work, emu1010_firmware_work);
+ INIT_WORK(&emu->emu1010.firmware_work, emu1010_firmware_work);
+ INIT_WORK(&emu->emu1010.clock_work, emu1010_clock_work);
/* read revision & serial */
emu->revision = pci->revision;
pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &emu->serial);
diff --git a/sound/pci/emu10k1/emu10k1_synth.c b/sound/pci/emu10k1/emu10k1_synth.c
index 759e66e1105a..68dfcb24b889 100644
--- a/sound/pci/emu10k1/emu10k1_synth.c
+++ b/sound/pci/emu10k1/emu10k1_synth.c
@@ -22,7 +22,6 @@ static int snd_emu10k1_synth_probe(struct device *_dev)
struct snd_emux *emux;
struct snd_emu10k1 *hw;
struct snd_emu10k1_synth_arg *arg;
- unsigned long flags;
arg = SNDRV_SEQ_DEVICE_ARGPTR(dev);
if (arg == NULL)
@@ -56,10 +55,10 @@ static int snd_emu10k1_synth_probe(struct device *_dev)
return -ENOMEM;
}
- spin_lock_irqsave(&hw->voice_lock, flags);
+ spin_lock_irq(&hw->voice_lock);
hw->synth = emux;
hw->get_synth_voice = snd_emu10k1_synth_get_voice;
- spin_unlock_irqrestore(&hw->voice_lock, flags);
+ spin_unlock_irq(&hw->voice_lock);
dev->driver_data = emux;
@@ -71,7 +70,6 @@ static int snd_emu10k1_synth_remove(struct device *_dev)
struct snd_seq_device *dev = to_seq_dev(_dev);
struct snd_emux *emux;
struct snd_emu10k1 *hw;
- unsigned long flags;
if (dev->driver_data == NULL)
return 0; /* not registered actually */
@@ -79,10 +77,10 @@ static int snd_emu10k1_synth_remove(struct device *_dev)
emux = dev->driver_data;
hw = emux->hw;
- spin_lock_irqsave(&hw->voice_lock, flags);
+ spin_lock_irq(&hw->voice_lock);
hw->synth = NULL;
hw->get_synth_voice = NULL;
- spin_unlock_irqrestore(&hw->voice_lock, flags);
+ spin_unlock_irq(&hw->voice_lock);
snd_emux_free(emux);
return 0;
diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c
index 9904bcfee106..03efc317e05f 100644
--- a/sound/pci/emu10k1/emufx.c
+++ b/sound/pci/emu10k1/emufx.c
@@ -1,17 +1,11 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ * James Courtier-Dutton <James@superbug.co.uk>
+ * Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
* Creative Labs, Inc.
- * Routines for effect processor FX8010
- *
- * Copyright (c) by James Courtier-Dutton <James@superbug.co.uk>
- * Added EMU 1010 support.
*
- * BUGS:
- * --
- *
- * TODO:
- * --
+ * Routines for effect processor FX8010
*/
#include <linux/pci.h>
@@ -799,13 +793,10 @@ static int snd_emu10k1_verify_controls(struct snd_emu10k1 *emu,
if (snd_emu10k1_look_for_ctl(emu, &gctl->id))
continue;
gctl_id = (struct snd_ctl_elem_id *)&gctl->id;
- down_read(&emu->card->controls_rwsem);
if (snd_ctl_find_id(emu->card, gctl_id)) {
- up_read(&emu->card->controls_rwsem);
err = -EEXIST;
goto __error;
}
- up_read(&emu->card->controls_rwsem);
if (gctl_id->iface != SNDRV_CTL_ELEM_IFACE_MIXER &&
gctl_id->iface != SNDRV_CTL_ELEM_IFACE_PCM) {
err = -EINVAL;
@@ -977,11 +968,9 @@ static int snd_emu10k1_del_controls(struct snd_emu10k1 *emu,
in_kernel);
if (err < 0)
return err;
- down_write(&card->controls_rwsem);
ctl = snd_emu10k1_look_for_ctl(emu, &id);
if (ctl)
snd_ctl_remove(card, ctl->kcontrol);
- up_write(&card->controls_rwsem);
}
return 0;
}
diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c
index f9500cd50a4b..0a32ea53d8c6 100644
--- a/sound/pci/emu10k1/emumixer.c
+++ b/sound/pci/emu10k1/emumixer.c
@@ -2,18 +2,12 @@
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>,
* Takashi Iwai <tiwai@suse.de>
+ * Lee Revell <rlrevell@joe-job.com>
+ * James Courtier-Dutton <James@superbug.co.uk>
+ * Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
* Creative Labs, Inc.
- * Routines for control of EMU10K1 chips / mixer routines
- * Multichannel PCM support Copyright (c) Lee Revell <rlrevell@joe-job.com>
- *
- * Copyright (c) by James Courtier-Dutton <James@superbug.co.uk>
- * Added EMU 1010 support.
- *
- * BUGS:
- * --
*
- * TODO:
- * --
+ * Routines for control of EMU10K1 chips / mixer routines
*/
#include <linux/time.h>
@@ -770,18 +764,21 @@ static int snd_emu1010_adc_pads_put(struct snd_kcontrol *kcontrol, struct snd_ct
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
unsigned int mask = snd_emu1010_adc_pad_regs[kcontrol->private_value];
unsigned int val, cache;
+ int change;
+
val = ucontrol->value.integer.value[0];
cache = emu->emu1010.adc_pads;
if (val == 1)
cache = cache | mask;
else
cache = cache & ~mask;
- if (cache != emu->emu1010.adc_pads) {
+ change = (cache != emu->emu1010.adc_pads);
+ if (change) {
snd_emu1010_fpga_write(emu, EMU_HANA_ADC_PADS, cache );
emu->emu1010.adc_pads = cache;
}
- return 0;
+ return change;
}
static const struct snd_kcontrol_new emu1010_adc_pads_ctl = {
@@ -983,17 +980,21 @@ static int snd_emu1010_clock_source_put(struct snd_kcontrol *kcontrol,
val = ucontrol->value.enumerated.item[0] ;
if (val >= emu_ci->num)
return -EINVAL;
+ spin_lock_irq(&emu->reg_lock);
change = (emu->emu1010.clock_source != val);
if (change) {
emu->emu1010.clock_source = val;
emu->emu1010.wclock = emu_ci->vals[val];
+ snd_emu1010_update_clock(emu);
snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE);
snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, emu->emu1010.wclock);
+ spin_unlock_irq(&emu->reg_lock);
+
msleep(10); // Allow DLL to settle
snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
-
- snd_emu1010_update_clock(emu);
+ } else {
+ spin_unlock_irq(&emu->reg_lock);
}
return change;
}
@@ -1190,7 +1191,6 @@ static int snd_audigy_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
unsigned int ngain, ogain;
u16 gpio;
int change = 0;
- unsigned long flags;
u32 source;
/* If the capture source has changed,
* update the capture volume from the cached value
@@ -1204,13 +1204,13 @@ static int snd_audigy_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
change = (emu->i2c_capture_source != source_id);
if (change) {
snd_emu10k1_i2c_write(emu, ADC_MUX, 0); /* Mute input */
- spin_lock_irqsave(&emu->emu_lock, flags);
+ spin_lock_irq(&emu->emu_lock);
gpio = inw(emu->port + A_IOCFG);
if (source_id==0)
outw(gpio | 0x4, emu->port + A_IOCFG);
else
outw(gpio & ~0x4, emu->port + A_IOCFG);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
+ spin_unlock_irq(&emu->emu_lock);
ngain = emu->i2c_capture_volume[source_id][0]; /* Left */
ogain = emu->i2c_capture_volume[emu->i2c_capture_source][0]; /* Left */
@@ -1354,7 +1354,6 @@ static int snd_audigy_spdif_output_rate_put(struct snd_kcontrol *kcontrol,
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
int change;
unsigned int reg, val, tmp;
- unsigned long flags;
switch(ucontrol->value.enumerated.item[0]) {
case 0:
@@ -1372,14 +1371,14 @@ static int snd_audigy_spdif_output_rate_put(struct snd_kcontrol *kcontrol,
}
- spin_lock_irqsave(&emu->reg_lock, flags);
+ spin_lock_irq(&emu->reg_lock);
reg = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0);
tmp = reg & ~A_SPDIF_RATE_MASK;
tmp |= val;
change = (tmp != reg);
if (change)
snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, 0, tmp);
- spin_unlock_irqrestore(&emu->reg_lock, flags);
+ spin_unlock_irq(&emu->reg_lock);
return change;
}
@@ -1496,7 +1495,6 @@ static int snd_emu10k1_send_routing_get(struct snd_kcontrol *kcontrol,
static int snd_emu10k1_send_routing_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- unsigned long flags;
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
struct snd_emu10k1_pcm_mixer *mix =
&emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
@@ -1504,7 +1502,7 @@ static int snd_emu10k1_send_routing_put(struct snd_kcontrol *kcontrol,
int num_efx = emu->audigy ? 8 : 4;
int mask = emu->audigy ? 0x3f : 0x0f;
- spin_lock_irqsave(&emu->reg_lock, flags);
+ spin_lock_irq(&emu->reg_lock);
for (voice = 0; voice < 3; voice++)
for (idx = 0; idx < num_efx; idx++) {
val = ucontrol->value.integer.value[(voice * num_efx) + idx] & mask;
@@ -1524,7 +1522,7 @@ static int snd_emu10k1_send_routing_put(struct snd_kcontrol *kcontrol,
&mix->send_routing[0][0]);
}
}
- spin_unlock_irqrestore(&emu->reg_lock, flags);
+ spin_unlock_irq(&emu->reg_lock);
return change;
}
@@ -1566,14 +1564,13 @@ static int snd_emu10k1_send_volume_get(struct snd_kcontrol *kcontrol,
static int snd_emu10k1_send_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- unsigned long flags;
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
struct snd_emu10k1_pcm_mixer *mix =
&emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
int change = 0, idx, val;
int num_efx = emu->audigy ? 8 : 4;
- spin_lock_irqsave(&emu->reg_lock, flags);
+ spin_lock_irq(&emu->reg_lock);
for (idx = 0; idx < 3*num_efx; idx++) {
val = ucontrol->value.integer.value[idx] & 255;
if (mix->send_volume[idx/num_efx][idx%num_efx] != val) {
@@ -1592,7 +1589,7 @@ static int snd_emu10k1_send_volume_put(struct snd_kcontrol *kcontrol,
&mix->send_volume[0][0]);
}
}
- spin_unlock_irqrestore(&emu->reg_lock, flags);
+ spin_unlock_irq(&emu->reg_lock);
return change;
}
@@ -1632,13 +1629,12 @@ static int snd_emu10k1_attn_get(struct snd_kcontrol *kcontrol,
static int snd_emu10k1_attn_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- unsigned long flags;
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
struct snd_emu10k1_pcm_mixer *mix =
&emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
int change = 0, idx, val;
- spin_lock_irqsave(&emu->reg_lock, flags);
+ spin_lock_irq(&emu->reg_lock);
for (idx = 0; idx < 3; idx++) {
unsigned uval = ucontrol->value.integer.value[idx] & 0x1ffff;
val = uval * 0x8000U / 0xffffU;
@@ -1655,7 +1651,7 @@ static int snd_emu10k1_attn_put(struct snd_kcontrol *kcontrol,
snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[0]->number, mix->attn[0]);
}
}
- spin_unlock_irqrestore(&emu->reg_lock, flags);
+ spin_unlock_irq(&emu->reg_lock);
return change;
}
@@ -1701,7 +1697,6 @@ static int snd_emu10k1_efx_send_routing_get(struct snd_kcontrol *kcontrol,
static int snd_emu10k1_efx_send_routing_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- unsigned long flags;
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
int ch = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
struct snd_emu10k1_pcm_mixer *mix = &emu->efx_pcm_mixer[ch];
@@ -1709,7 +1704,7 @@ static int snd_emu10k1_efx_send_routing_put(struct snd_kcontrol *kcontrol,
int num_efx = emu->audigy ? 8 : 4;
int mask = emu->audigy ? 0x3f : 0x0f;
- spin_lock_irqsave(&emu->reg_lock, flags);
+ spin_lock_irq(&emu->reg_lock);
for (idx = 0; idx < num_efx; idx++) {
val = ucontrol->value.integer.value[idx] & mask;
if (mix->send_routing[0][idx] != val) {
@@ -1724,7 +1719,7 @@ static int snd_emu10k1_efx_send_routing_put(struct snd_kcontrol *kcontrol,
&mix->send_routing[0][0]);
}
}
- spin_unlock_irqrestore(&emu->reg_lock, flags);
+ spin_unlock_irq(&emu->reg_lock);
return change;
}
@@ -1766,14 +1761,13 @@ static int snd_emu10k1_efx_send_volume_get(struct snd_kcontrol *kcontrol,
static int snd_emu10k1_efx_send_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- unsigned long flags;
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
int ch = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
struct snd_emu10k1_pcm_mixer *mix = &emu->efx_pcm_mixer[ch];
int change = 0, idx, val;
int num_efx = emu->audigy ? 8 : 4;
- spin_lock_irqsave(&emu->reg_lock, flags);
+ spin_lock_irq(&emu->reg_lock);
for (idx = 0; idx < num_efx; idx++) {
val = ucontrol->value.integer.value[idx] & 255;
if (mix->send_volume[0][idx] != val) {
@@ -1787,7 +1781,7 @@ static int snd_emu10k1_efx_send_volume_put(struct snd_kcontrol *kcontrol,
&mix->send_volume[0][0]);
}
}
- spin_unlock_irqrestore(&emu->reg_lock, flags);
+ spin_unlock_irq(&emu->reg_lock);
return change;
}
@@ -1826,14 +1820,13 @@ static int snd_emu10k1_efx_attn_get(struct snd_kcontrol *kcontrol,
static int snd_emu10k1_efx_attn_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- unsigned long flags;
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
int ch = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
struct snd_emu10k1_pcm_mixer *mix = &emu->efx_pcm_mixer[ch];
int change = 0, val;
unsigned uval;
- spin_lock_irqsave(&emu->reg_lock, flags);
+ spin_lock_irq(&emu->reg_lock);
uval = ucontrol->value.integer.value[0] & 0x1ffff;
val = uval * 0x8000U / 0xffffU;
if (mix->attn[0] != val) {
@@ -1845,7 +1838,7 @@ static int snd_emu10k1_efx_attn_put(struct snd_kcontrol *kcontrol,
snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[ch]->number, mix->attn[0]);
}
}
- spin_unlock_irqrestore(&emu->reg_lock, flags);
+ spin_unlock_irq(&emu->reg_lock);
return change;
}
@@ -1881,7 +1874,6 @@ static int snd_emu10k1_shared_spdif_get(struct snd_kcontrol *kcontrol,
static int snd_emu10k1_shared_spdif_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- unsigned long flags;
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
unsigned int reg, val, sw;
int change = 0;
@@ -1889,7 +1881,7 @@ static int snd_emu10k1_shared_spdif_put(struct snd_kcontrol *kcontrol,
sw = ucontrol->value.integer.value[0];
if (emu->card_capabilities->invert_shared_spdif)
sw = !sw;
- spin_lock_irqsave(&emu->emu_lock, flags);
+ spin_lock_irq(&emu->emu_lock);
if ( emu->card_capabilities->i2c_adc) {
/* Do nothing for Audigy 2 ZS Notebook */
} else if (emu->audigy) {
@@ -1910,7 +1902,7 @@ static int snd_emu10k1_shared_spdif_put(struct snd_kcontrol *kcontrol,
reg |= val;
outl(reg | val, emu->port + HCFG);
}
- spin_unlock_irqrestore(&emu->emu_lock, flags);
+ spin_unlock_irq(&emu->emu_lock);
return change;
}
@@ -1990,18 +1982,9 @@ static int remove_ctl(struct snd_card *card, const char *name)
return snd_ctl_remove_id(card, &id);
}
-static struct snd_kcontrol *ctl_find(struct snd_card *card, const char *name)
-{
- struct snd_ctl_elem_id sid;
- memset(&sid, 0, sizeof(sid));
- strcpy(sid.name, name);
- sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- return snd_ctl_find_id(card, &sid);
-}
-
static int rename_ctl(struct snd_card *card, const char *src, const char *dst)
{
- struct snd_kcontrol *kctl = ctl_find(card, src);
+ struct snd_kcontrol *kctl = snd_ctl_find_id_mixer(card, src);
if (kctl) {
snd_ctl_rename(card, kctl, dst);
return 0;
@@ -2342,8 +2325,8 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu,
emu1010_map_source(emu_ri, emu_ri->out_dflts[i]);
snd_emu1010_apply_sources(emu);
- err = snd_ctl_add(card,
- snd_ctl_new1(&snd_emu1010_clock_source, emu));
+ kctl = emu->ctl_clock_source = snd_ctl_new1(&snd_emu1010_clock_source, emu);
+ err = snd_ctl_add(card, kctl);
if (err < 0)
return err;
err = snd_ctl_add(card,
diff --git a/sound/pci/emu10k1/emumpu401.c b/sound/pci/emu10k1/emumpu401.c
index 3ce9b2129ce6..747c34b3d566 100644
--- a/sound/pci/emu10k1/emumpu401.c
+++ b/sound/pci/emu10k1/emumpu401.c
@@ -104,10 +104,9 @@ static void snd_emu10k1_midi_interrupt2(struct snd_emu10k1 *emu, unsigned int st
static int snd_emu10k1_midi_cmd(struct snd_emu10k1 * emu, struct snd_emu10k1_midi *midi, unsigned char cmd, int ack)
{
- unsigned long flags;
int timeout, ok;
- spin_lock_irqsave(&midi->input_lock, flags);
+ spin_lock_irq(&midi->input_lock);
mpu401_write_data(emu, midi, 0x00);
/* mpu401_clear_rx(emu, midi); */
@@ -126,7 +125,7 @@ static int snd_emu10k1_midi_cmd(struct snd_emu10k1 * emu, struct snd_emu10k1_mid
} else {
ok = 1;
}
- spin_unlock_irqrestore(&midi->input_lock, flags);
+ spin_unlock_irq(&midi->input_lock);
if (!ok) {
dev_err(emu->card->dev,
"midi_cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)!!!\n",
@@ -142,22 +141,21 @@ static int snd_emu10k1_midi_input_open(struct snd_rawmidi_substream *substream)
{
struct snd_emu10k1 *emu;
struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
- unsigned long flags;
emu = midi->emu;
if (snd_BUG_ON(!emu))
return -ENXIO;
- spin_lock_irqsave(&midi->open_lock, flags);
+ spin_lock_irq(&midi->open_lock);
midi->midi_mode |= EMU10K1_MIDI_MODE_INPUT;
midi->substream_input = substream;
if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) {
- spin_unlock_irqrestore(&midi->open_lock, flags);
+ spin_unlock_irq(&midi->open_lock);
if (snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1))
goto error_out;
if (snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
goto error_out;
} else {
- spin_unlock_irqrestore(&midi->open_lock, flags);
+ spin_unlock_irq(&midi->open_lock);
}
return 0;
@@ -169,22 +167,21 @@ static int snd_emu10k1_midi_output_open(struct snd_rawmidi_substream *substream)
{
struct snd_emu10k1 *emu;
struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
- unsigned long flags;
emu = midi->emu;
if (snd_BUG_ON(!emu))
return -ENXIO;
- spin_lock_irqsave(&midi->open_lock, flags);
+ spin_lock_irq(&midi->open_lock);
midi->midi_mode |= EMU10K1_MIDI_MODE_OUTPUT;
midi->substream_output = substream;
if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
- spin_unlock_irqrestore(&midi->open_lock, flags);
+ spin_unlock_irq(&midi->open_lock);
if (snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1))
goto error_out;
if (snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
goto error_out;
} else {
- spin_unlock_irqrestore(&midi->open_lock, flags);
+ spin_unlock_irq(&midi->open_lock);
}
return 0;
@@ -196,21 +193,20 @@ static int snd_emu10k1_midi_input_close(struct snd_rawmidi_substream *substream)
{
struct snd_emu10k1 *emu;
struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
- unsigned long flags;
int err = 0;
emu = midi->emu;
if (snd_BUG_ON(!emu))
return -ENXIO;
- spin_lock_irqsave(&midi->open_lock, flags);
+ spin_lock_irq(&midi->open_lock);
snd_emu10k1_intr_disable(emu, midi->rx_enable);
midi->midi_mode &= ~EMU10K1_MIDI_MODE_INPUT;
midi->substream_input = NULL;
if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) {
- spin_unlock_irqrestore(&midi->open_lock, flags);
+ spin_unlock_irq(&midi->open_lock);
err = snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0);
} else {
- spin_unlock_irqrestore(&midi->open_lock, flags);
+ spin_unlock_irq(&midi->open_lock);
}
return err;
}
@@ -219,21 +215,20 @@ static int snd_emu10k1_midi_output_close(struct snd_rawmidi_substream *substream
{
struct snd_emu10k1 *emu;
struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
- unsigned long flags;
int err = 0;
emu = midi->emu;
if (snd_BUG_ON(!emu))
return -ENXIO;
- spin_lock_irqsave(&midi->open_lock, flags);
+ spin_lock_irq(&midi->open_lock);
snd_emu10k1_intr_disable(emu, midi->tx_enable);
midi->midi_mode &= ~EMU10K1_MIDI_MODE_OUTPUT;
midi->substream_output = NULL;
if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
- spin_unlock_irqrestore(&midi->open_lock, flags);
+ spin_unlock_irq(&midi->open_lock);
err = snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0);
} else {
- spin_unlock_irqrestore(&midi->open_lock, flags);
+ spin_unlock_irq(&midi->open_lock);
}
return err;
}
@@ -256,7 +251,6 @@ static void snd_emu10k1_midi_output_trigger(struct snd_rawmidi_substream *substr
{
struct snd_emu10k1 *emu;
struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
- unsigned long flags;
emu = midi->emu;
if (snd_BUG_ON(!emu))
@@ -267,13 +261,13 @@ static void snd_emu10k1_midi_output_trigger(struct snd_rawmidi_substream *substr
unsigned char byte;
/* try to send some amount of bytes here before interrupts */
- spin_lock_irqsave(&midi->output_lock, flags);
+ spin_lock_irq(&midi->output_lock);
while (max > 0) {
if (mpu401_output_ready(emu, midi)) {
if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT) ||
snd_rawmidi_transmit(substream, &byte, 1) != 1) {
/* no more data */
- spin_unlock_irqrestore(&midi->output_lock, flags);
+ spin_unlock_irq(&midi->output_lock);
return;
}
mpu401_write_data(emu, midi, byte);
@@ -282,7 +276,7 @@ static void snd_emu10k1_midi_output_trigger(struct snd_rawmidi_substream *substr
break;
}
}
- spin_unlock_irqrestore(&midi->output_lock, flags);
+ spin_unlock_irq(&midi->output_lock);
snd_emu10k1_intr_enable(emu, midi->tx_enable);
} else {
snd_emu10k1_intr_disable(emu, midi->tx_enable);
diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c
index 387288d623d7..7f4c1b38d6ec 100644
--- a/sound/pci/emu10k1/emupcm.c
+++ b/sound/pci/emu10k1/emupcm.c
@@ -1,15 +1,12 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ * Lee Revell <rlrevell@joe-job.com>
+ * James Courtier-Dutton <James@superbug.co.uk>
+ * Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
* Creative Labs, Inc.
- * Routines for control of EMU10K1 chips / PCM routines
- * Multichannel PCM support Copyright (c) Lee Revell <rlrevell@joe-job.com>
- *
- * BUGS:
- * --
*
- * TODO:
- * --
+ * Routines for control of EMU10K1 chips / PCM routines
*/
#include <linux/pci.h>
@@ -343,9 +340,7 @@ static void snd_emu10k1_pcm_init_voices(struct snd_emu10k1 *emu,
unsigned int end_addr,
struct snd_emu10k1_pcm_mixer *mix)
{
- unsigned long flags;
-
- spin_lock_irqsave(&emu->reg_lock, flags);
+ spin_lock_irq(&emu->reg_lock);
snd_emu10k1_pcm_init_voice(emu, evoice, w_16, stereo,
start_addr, end_addr,
&mix->send_routing[stereo][0],
@@ -355,7 +350,7 @@ static void snd_emu10k1_pcm_init_voices(struct snd_emu10k1 *emu,
start_addr, end_addr,
&mix->send_routing[2][0],
&mix->send_volume[2][0]);
- spin_unlock_irqrestore(&emu->reg_lock, flags);
+ spin_unlock_irq(&emu->reg_lock);
}
static void snd_emu10k1_pcm_init_extra_voice(struct snd_emu10k1 *emu,
diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c
index 7e2cc532471f..2f80fd91017c 100644
--- a/sound/pci/emu10k1/emuproc.c
+++ b/sound/pci/emu10k1/emuproc.c
@@ -1,17 +1,12 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ * Lee Revell <rlrevell@joe-job.com>
+ * James Courtier-Dutton <James@superbug.co.uk>
+ * Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
* Creative Labs, Inc.
- * Routines for control of EMU10K1 chips / proc interface routines
- *
- * Copyright (c) by James Courtier-Dutton <James@superbug.co.uk>
- * Added EMU 1010 support.
*
- * BUGS:
- * --
- *
- * TODO:
- * --
+ * Routines for control of EMU10K1 chips / proc interface routines
*/
#include <linux/slab.h>
@@ -536,15 +531,14 @@ static unsigned int snd_ptr_read(struct snd_emu10k1 * emu,
unsigned int reg,
unsigned int chn)
{
- unsigned long flags;
unsigned int regptr, val;
regptr = (reg << 16) | chn;
- spin_lock_irqsave(&emu->emu_lock, flags);
+ spin_lock_irq(&emu->emu_lock);
outl(regptr, emu->port + iobase + PTR);
val = inl(emu->port + iobase + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
+ spin_unlock_irq(&emu->emu_lock);
return val;
}
@@ -555,14 +549,13 @@ static void snd_ptr_write(struct snd_emu10k1 *emu,
unsigned int data)
{
unsigned int regptr;
- unsigned long flags;
regptr = (reg << 16) | chn;
- spin_lock_irqsave(&emu->emu_lock, flags);
+ spin_lock_irq(&emu->emu_lock);
outl(regptr, emu->port + iobase + PTR);
outl(data, emu->port + iobase + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
+ spin_unlock_irq(&emu->emu_lock);
}
diff --git a/sound/pci/emu10k1/io.c b/sound/pci/emu10k1/io.c
index a0d66ce3ee83..74df2330015f 100644
--- a/sound/pci/emu10k1/io.c
+++ b/sound/pci/emu10k1/io.c
@@ -1,14 +1,12 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ * Lee Revell <rlrevell@joe-job.com>
+ * James Courtier-Dutton <James@superbug.co.uk>
+ * Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
* Creative Labs, Inc.
- * Routines for control of EMU10K1 chips
- *
- * BUGS:
- * --
*
- * TODO:
- * --
+ * Routines for control of EMU10K1 chips
*/
#include <linux/time.h>
@@ -302,6 +300,8 @@ static void snd_emu1010_fpga_read_locked(struct snd_emu10k1 *emu, u32 reg, u32 *
{
// The higest input pin is used as the designated interrupt trigger,
// so it needs to be masked out.
+ // But note that any other input pin change will also cause an IRQ,
+ // so using this function often causes an IRQ as a side effect.
u32 mask = emu->card_capabilities->ca0108_chip ? 0x1f : 0x7f;
if (snd_BUG_ON(reg > 0x3f))
return;
diff --git a/sound/pci/emu10k1/irq.c b/sound/pci/emu10k1/irq.c
index a813ef8c2f8d..71aa90b9cc88 100644
--- a/sound/pci/emu10k1/irq.c
+++ b/sound/pci/emu10k1/irq.c
@@ -3,12 +3,6 @@
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Creative Labs, Inc.
* Routines for IRQ control of EMU10K1 chips
- *
- * BUGS:
- * --
- *
- * TODO:
- * --
*/
#include <linux/time.h>
@@ -149,6 +143,13 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id)
outl(0, emu->port + INTE2);
status &= ~IPR_P16V;
}
+ if (status & IPR_A_GPIO) {
+ if (emu->gpio_interrupt)
+ emu->gpio_interrupt(emu);
+ else
+ snd_emu10k1_intr_disable(emu, INTE_A_GPIOENABLE);
+ status &= ~IPR_A_GPIO;
+ }
if (status) {
dev_err(emu->card->dev,
diff --git a/sound/pci/emu10k1/p16v.h b/sound/pci/emu10k1/p16v.h
index 9d429ad1feff..95ab8071751b 100644
--- a/sound/pci/emu10k1/p16v.h
+++ b/sound/pci/emu10k1/p16v.h
@@ -2,62 +2,6 @@
/*
* Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
* Driver p16v chips
- * Version: 0.21
- *
- * FEATURES currently supported:
- * Output fixed at S32_LE, 2 channel to hw:0,0
- * Rates: 44.1, 48, 96, 192.
- *
- * Changelog:
- * 0.8
- * Use separate card based buffer for periods table.
- * 0.9
- * Use 2 channel output streams instead of 8 channel.
- * (8 channel output streams might be good for ASIO type output)
- * Corrected speaker output, so Front -> Front etc.
- * 0.10
- * Fixed missed interrupts.
- * 0.11
- * Add Sound card model number and names.
- * Add Analog volume controls.
- * 0.12
- * Corrected playback interrupts. Now interrupt per period, instead of half period.
- * 0.13
- * Use single trigger for multichannel.
- * 0.14
- * Mic capture now works at fixed: S32_LE, 96000Hz, Stereo.
- * 0.15
- * Force buffer_size / period_size == INTEGER.
- * 0.16
- * Update p16v.c to work with changed alsa api.
- * 0.17
- * Update p16v.c to work with changed alsa api. Removed boot_devs.
- * 0.18
- * Merging with snd-emu10k1 driver.
- * 0.19
- * One stereo channel at 24bit now works.
- * 0.20
- * Added better register defines.
- * 0.21
- * Split from p16v.c
- *
- * BUGS:
- * Some stability problems when unloading the snd-p16v kernel module.
- * --
- *
- * TODO:
- * SPDIF out.
- * Find out how to change capture sample rates. E.g. To record SPDIF at 48000Hz.
- * Currently capture fixed at 48000Hz.
- *
- * --
- * GENERAL INFO:
- * Model: SB0240
- * P16V Chip: CA0151-DBS
- * Audigy 2 Chip: CA0102-IAT
- * AC97 Codec: STAC 9721
- * ADC: Philips 1361T (Stereo 24bit)
- * DAC: CS4382-K (8-channel, 24bit, 192Khz)
*
* This code was initially based on code from ALSA's emu10k1x.c which is:
* Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
diff --git a/sound/pci/emu10k1/p17v.h b/sound/pci/emu10k1/p17v.h
index d4ada1c430c8..ee4f4ab4b79c 100644
--- a/sound/pci/emu10k1/p17v.h
+++ b/sound/pci/emu10k1/p17v.h
@@ -2,7 +2,6 @@
/*
* Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
* Driver p17v chips
- * Version: 0.01
*/
/******************************************************************************/
diff --git a/sound/pci/emu10k1/timer.c b/sound/pci/emu10k1/timer.c
index f3c78adf3248..bb2478319361 100644
--- a/sound/pci/emu10k1/timer.c
+++ b/sound/pci/emu10k1/timer.c
@@ -2,13 +2,9 @@
/*
* Copyright (c) by Lee Revell <rlrevell@joe-job.com>
* Clemens Ladisch <clemens@ladisch.de>
- * Routines for control of EMU10K1 chips
- *
- * BUGS:
- * --
+ * Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
*
- * TODO:
- * --
+ * Routines for control of EMU10K1 chips
*/
#include <linux/time.h>
diff --git a/sound/pci/emu10k1/tina2.h b/sound/pci/emu10k1/tina2.h
index 7fd235345292..e3fcb290271c 100644
--- a/sound/pci/emu10k1/tina2.h
+++ b/sound/pci/emu10k1/tina2.h
@@ -2,7 +2,6 @@
/*
* Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
* Driver tina2 chips
- * Version: 0.1
*/
/********************************************************************************************************/
diff --git a/sound/pci/emu10k1/voice.c b/sound/pci/emu10k1/voice.c
index 6939498e26f0..77fb5427aaed 100644
--- a/sound/pci/emu10k1/voice.c
+++ b/sound/pci/emu10k1/voice.c
@@ -1,17 +1,11 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- * Creative Labs, Inc.
* Lee Revell <rlrevell@joe-job.com>
- * Routines for control of EMU10K1 chips - voice manager
- *
- * Rewrote voice allocator for multichannel support - rlrevell 12/2004
- *
- * BUGS:
- * --
+ * Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
+ * Creative Labs, Inc.
*
- * TODO:
- * --
+ * Routines for control of EMU10K1 chips - voice manager
*/
#include <linux/time.h>
diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c
index e34ec6f89e7e..ec598ba1a883 100644
--- a/sound/pci/es1938.c
+++ b/sound/pci/es1938.c
@@ -824,7 +824,7 @@ static snd_pcm_uframes_t snd_es1938_playback_pointer(struct snd_pcm_substream *s
static int snd_es1938_capture_copy(struct snd_pcm_substream *substream,
int channel, unsigned long pos,
- void __user *dst, unsigned long count)
+ struct iov_iter *dst, unsigned long count)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct es1938 *chip = snd_pcm_substream_chip(substream);
@@ -832,36 +832,17 @@ static int snd_es1938_capture_copy(struct snd_pcm_substream *substream,
if (snd_BUG_ON(pos + count > chip->dma1_size))
return -EINVAL;
if (pos + count < chip->dma1_size) {
- if (copy_to_user(dst, runtime->dma_area + pos + 1, count))
+ if (copy_to_iter(runtime->dma_area + pos + 1, count, dst) != count)
return -EFAULT;
} else {
- if (copy_to_user(dst, runtime->dma_area + pos + 1, count - 1))
+ if (copy_to_iter(runtime->dma_area + pos + 1, count - 1, dst) != count - 1)
return -EFAULT;
- if (put_user(runtime->dma_area[0],
- ((unsigned char __user *)dst) + count - 1))
+ if (copy_to_iter(runtime->dma_area, 1, dst) != 1)
return -EFAULT;
}
return 0;
}
-static int snd_es1938_capture_copy_kernel(struct snd_pcm_substream *substream,
- int channel, unsigned long pos,
- void *dst, unsigned long count)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct es1938 *chip = snd_pcm_substream_chip(substream);
-
- if (snd_BUG_ON(pos + count > chip->dma1_size))
- return -EINVAL;
- if (pos + count < chip->dma1_size) {
- memcpy(dst, runtime->dma_area + pos + 1, count);
- } else {
- memcpy(dst, runtime->dma_area + pos + 1, count - 1);
- runtime->dma_area[0] = *((unsigned char *)dst + count - 1);
- }
- return 0;
-}
-
/* ----------------------------------------------------------------------
* Audio1 Capture (ADC)
* ----------------------------------------------------------------------*/
@@ -987,8 +968,7 @@ static const struct snd_pcm_ops snd_es1938_capture_ops = {
.prepare = snd_es1938_capture_prepare,
.trigger = snd_es1938_capture_trigger,
.pointer = snd_es1938_capture_pointer,
- .copy_user = snd_es1938_capture_copy,
- .copy_kernel = snd_es1938_capture_copy_kernel,
+ .copy = snd_es1938_capture_copy,
};
static int snd_es1938_new_pcm(struct es1938 *chip, int device)
diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c
index 4a7e20bb11bc..4bc0f53c223b 100644
--- a/sound/pci/es1968.c
+++ b/sound/pci/es1968.c
@@ -2005,9 +2005,6 @@ snd_es1968_mixer(struct es1968 *chip)
{
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
-#ifndef CONFIG_SND_ES1968_INPUT
- struct snd_ctl_elem_id elem_id;
-#endif
int err;
static const struct snd_ac97_bus_ops ops = {
.write = snd_es1968_ac97_write,
@@ -2027,14 +2024,10 @@ snd_es1968_mixer(struct es1968 *chip)
#ifndef CONFIG_SND_ES1968_INPUT
/* attach master switch / volumes for h/w volume control */
- memset(&elem_id, 0, sizeof(elem_id));
- elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(elem_id.name, "Master Playback Switch");
- chip->master_switch = snd_ctl_find_id(chip->card, &elem_id);
- memset(&elem_id, 0, sizeof(elem_id));
- elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(elem_id.name, "Master Playback Volume");
- chip->master_volume = snd_ctl_find_id(chip->card, &elem_id);
+ chip->master_switch = snd_ctl_find_id_mixer(chip->card,
+ "Master Playback Switch");
+ chip->master_volume = snd_ctl_find_id_mixer(chip->card,
+ "Master Playback Volume");
#endif
return 0;
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index dd6922267a4b..0d7502d6e060 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -104,6 +104,7 @@ config SND_HDA_SCODEC_CS35L41_I2C
tristate "Build CS35L41 HD-audio side codec support for I2C Bus"
depends on I2C
depends on ACPI
+ depends on EFI
depends on SND_SOC
select SND_SOC_CS35L41_LIB
select SND_HDA_SCODEC_CS35L41
@@ -119,6 +120,7 @@ config SND_HDA_SCODEC_CS35L41_SPI
tristate "Build CS35L41 HD-audio codec support for SPI Bus"
depends on SPI_MASTER
depends on ACPI
+ depends on EFI
depends on SND_SOC
select SND_SOC_CS35L41_LIB
select SND_HDA_SCODEC_CS35L41
@@ -161,6 +163,22 @@ config SND_HDA_SCODEC_CS35L56_SPI
Say Y or M here to include CS35L56 amplifier support with
SPI control.
+config SND_HDA_SCODEC_TAS2781_I2C
+ tristate "Build TAS2781 HD-audio side codec support for I2C Bus"
+ depends on I2C
+ depends on ACPI
+ depends on EFI
+ depends on SND_SOC
+ select SND_SOC_TAS2781_COMLIB
+ select SND_SOC_TAS2781_FMWLIB
+ select CRC32_SARWATE
+ help
+ Say Y or M here to include TAS2781 I2C HD-audio side codec support
+ in snd-hda-intel driver, such as ALC287.
+
+comment "Set to Y if you want auto-loading the side codec driver"
+ depends on SND_HDA=y && SND_HDA_SCODEC_TAS2781_I2C=m
+
config SND_HDA_CODEC_REALTEK
tristate "Build Realtek HD-audio codec support"
select SND_HDA_GENERIC
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index c6e6509e7b8e..f00fc9ed6096 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -28,13 +28,14 @@ snd-hda-codec-via-objs := patch_via.o
snd-hda-codec-hdmi-objs := patch_hdmi.o hda_eld.o
# side codecs
-snd-hda-scodec-cs35l41-objs := cs35l41_hda.o
+snd-hda-scodec-cs35l41-objs := cs35l41_hda.o cs35l41_hda_property.o
snd-hda-scodec-cs35l41-i2c-objs := cs35l41_hda_i2c.o
snd-hda-scodec-cs35l41-spi-objs := cs35l41_hda_spi.o
snd-hda-scodec-cs35l56-objs := cs35l56_hda.o
snd-hda-scodec-cs35l56-i2c-objs := cs35l56_hda_i2c.o
snd-hda-scodec-cs35l56-spi-objs := cs35l56_hda_spi.o
snd-hda-cs-dsp-ctls-objs := hda_cs_dsp_ctl.o
+snd-hda-scodec-tas2781-i2c-objs := tas2781_hda_i2c.o
# common driver
obj-$(CONFIG_SND_HDA) := snd-hda-codec.o
@@ -62,6 +63,7 @@ obj-$(CONFIG_SND_HDA_SCODEC_CS35L56) += snd-hda-scodec-cs35l56.o
obj-$(CONFIG_SND_HDA_SCODEC_CS35L56_I2C) += snd-hda-scodec-cs35l56-i2c.o
obj-$(CONFIG_SND_HDA_SCODEC_CS35L56_SPI) += snd-hda-scodec-cs35l56-spi.o
obj-$(CONFIG_SND_HDA_CS_DSP_CONTROLS) += snd-hda-cs-dsp-ctls.o
+obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_I2C) += snd-hda-scodec-tas2781-i2c.o
# this must be the last entry after codec drivers;
# otherwise the codec patches won't be hooked before the PCI probe
diff --git a/sound/pci/hda/cs35l41_hda.c b/sound/pci/hda/cs35l41_hda.c
index ce5faa620517..f9b77353c266 100644
--- a/sound/pci/hda/cs35l41_hda.c
+++ b/sound/pci/hda/cs35l41_hda.c
@@ -19,6 +19,7 @@
#include "hda_component.h"
#include "cs35l41_hda.h"
#include "hda_cs_dsp_ctl.h"
+#include "cs35l41_hda_property.h"
#define CS35L41_FIRMWARE_ROOT "cirrus/"
#define CS35L41_PART "cs35l41"
@@ -58,8 +59,6 @@ static const struct reg_sequence cs35l41_hda_config[] = {
{ CS35L41_DSP1_RX3_SRC, 0x00000018 }, // DSP1RX3 SRC = VMON
{ CS35L41_DSP1_RX4_SRC, 0x00000019 }, // DSP1RX4 SRC = IMON
{ CS35L41_DSP1_RX5_SRC, 0x00000020 }, // DSP1RX5 SRC = ERRVOL
- { CS35L41_AMP_DIG_VOL_CTRL, 0x00008000 }, // AMP_HPF_PCM_EN = 1, AMP_VOL_PCM 0.0 dB
- { CS35L41_AMP_GAIN_CTRL, 0x00000084 }, // AMP_GAIN_PCM 4.5 dB
};
static const struct reg_sequence cs35l41_hda_config_dsp[] = {
@@ -82,6 +81,14 @@ static const struct reg_sequence cs35l41_hda_config_dsp[] = {
{ CS35L41_DSP1_RX3_SRC, 0x00000018 }, // DSP1RX3 SRC = VMON
{ CS35L41_DSP1_RX4_SRC, 0x00000019 }, // DSP1RX4 SRC = IMON
{ CS35L41_DSP1_RX5_SRC, 0x00000029 }, // DSP1RX5 SRC = VBSTMON
+};
+
+static const struct reg_sequence cs35l41_hda_unmute[] = {
+ { CS35L41_AMP_DIG_VOL_CTRL, 0x00008000 }, // AMP_HPF_PCM_EN = 1, AMP_VOL_PCM 0.0 dB
+ { CS35L41_AMP_GAIN_CTRL, 0x00000084 }, // AMP_GAIN_PCM 4.5 dB
+};
+
+static const struct reg_sequence cs35l41_hda_unmute_dsp[] = {
{ CS35L41_AMP_DIG_VOL_CTRL, 0x00008000 }, // AMP_HPF_PCM_EN = 1, AMP_VOL_PCM 0.0 dB
{ CS35L41_AMP_GAIN_CTRL, 0x00000233 }, // AMP_GAIN_PCM = 17.5dB AMP_GAIN_PDM = 19.5dB
};
@@ -483,73 +490,159 @@ static void cs35l41_irq_release(struct cs35l41_hda *cs35l41)
cs35l41->irq_errors = 0;
}
-static void cs35l41_hda_playback_hook(struct device *dev, int action)
+static void cs35l41_hda_play_start(struct device *dev)
{
struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
struct regmap *reg = cs35l41->regmap;
- int ret = 0;
+
+ dev_dbg(dev, "Play (Start)\n");
+
+ if (cs35l41->playback_started) {
+ dev_dbg(dev, "Playback already started.");
+ return;
+ }
+
+ cs35l41->playback_started = true;
+
+ if (cs35l41->firmware_running) {
+ regmap_multi_reg_write(reg, cs35l41_hda_config_dsp,
+ ARRAY_SIZE(cs35l41_hda_config_dsp));
+ regmap_update_bits(reg, CS35L41_PWR_CTRL2,
+ CS35L41_VMON_EN_MASK | CS35L41_IMON_EN_MASK,
+ 1 << CS35L41_VMON_EN_SHIFT | 1 << CS35L41_IMON_EN_SHIFT);
+ cs35l41_set_cspl_mbox_cmd(cs35l41->dev, reg, CSPL_MBOX_CMD_RESUME);
+ } else {
+ regmap_multi_reg_write(reg, cs35l41_hda_config, ARRAY_SIZE(cs35l41_hda_config));
+ }
+ regmap_update_bits(reg, CS35L41_PWR_CTRL2, CS35L41_AMP_EN_MASK, 1 << CS35L41_AMP_EN_SHIFT);
+ if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST)
+ regmap_write(reg, CS35L41_GPIO1_CTRL1, 0x00008001);
+
+}
+
+static void cs35l41_hda_play_done(struct device *dev)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ struct regmap *reg = cs35l41->regmap;
+
+ dev_dbg(dev, "Play (Complete)\n");
+
+ cs35l41_global_enable(dev, reg, cs35l41->hw_cfg.bst_type, 1, NULL,
+ cs35l41->firmware_running);
+ if (cs35l41->firmware_running) {
+ regmap_multi_reg_write(reg, cs35l41_hda_unmute_dsp,
+ ARRAY_SIZE(cs35l41_hda_unmute_dsp));
+ } else {
+ regmap_multi_reg_write(reg, cs35l41_hda_unmute,
+ ARRAY_SIZE(cs35l41_hda_unmute));
+ }
+}
+
+static void cs35l41_hda_pause_start(struct device *dev)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ struct regmap *reg = cs35l41->regmap;
+
+ dev_dbg(dev, "Pause (Start)\n");
+
+ regmap_multi_reg_write(reg, cs35l41_hda_mute, ARRAY_SIZE(cs35l41_hda_mute));
+ cs35l41_global_enable(dev, reg, cs35l41->hw_cfg.bst_type, 0, NULL,
+ cs35l41->firmware_running);
+}
+
+static void cs35l41_hda_pause_done(struct device *dev)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ struct regmap *reg = cs35l41->regmap;
+
+ dev_dbg(dev, "Pause (Complete)\n");
+
+ regmap_update_bits(reg, CS35L41_PWR_CTRL2, CS35L41_AMP_EN_MASK, 0 << CS35L41_AMP_EN_SHIFT);
+ if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST)
+ regmap_write(reg, CS35L41_GPIO1_CTRL1, 0x00000001);
+ if (cs35l41->firmware_running) {
+ cs35l41_set_cspl_mbox_cmd(dev, reg, CSPL_MBOX_CMD_PAUSE);
+ regmap_update_bits(reg, CS35L41_PWR_CTRL2,
+ CS35L41_VMON_EN_MASK | CS35L41_IMON_EN_MASK,
+ 0 << CS35L41_VMON_EN_SHIFT | 0 << CS35L41_IMON_EN_SHIFT);
+ }
+ cs35l41_irq_release(cs35l41);
+ cs35l41->playback_started = false;
+}
+
+static void cs35l41_hda_pre_playback_hook(struct device *dev, int action)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
switch (action) {
- case HDA_GEN_PCM_ACT_OPEN:
- pm_runtime_get_sync(dev);
+ case HDA_GEN_PCM_ACT_CLEANUP:
mutex_lock(&cs35l41->fw_mutex);
- cs35l41->playback_started = true;
- if (cs35l41->firmware_running) {
- regmap_multi_reg_write(reg, cs35l41_hda_config_dsp,
- ARRAY_SIZE(cs35l41_hda_config_dsp));
- regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2,
- CS35L41_VMON_EN_MASK | CS35L41_IMON_EN_MASK,
- 1 << CS35L41_VMON_EN_SHIFT | 1 << CS35L41_IMON_EN_SHIFT);
- cs35l41_set_cspl_mbox_cmd(cs35l41->dev, cs35l41->regmap,
- CSPL_MBOX_CMD_RESUME);
- } else {
- regmap_multi_reg_write(reg, cs35l41_hda_config,
- ARRAY_SIZE(cs35l41_hda_config));
- }
- ret = regmap_update_bits(reg, CS35L41_PWR_CTRL2,
- CS35L41_AMP_EN_MASK, 1 << CS35L41_AMP_EN_SHIFT);
- if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST)
- regmap_write(reg, CS35L41_GPIO1_CTRL1, 0x00008001);
+ cs35l41_hda_pause_start(dev);
mutex_unlock(&cs35l41->fw_mutex);
break;
+ default:
+ break;
+ }
+}
+static void cs35l41_hda_playback_hook(struct device *dev, int action)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+
+ switch (action) {
+ case HDA_GEN_PCM_ACT_OPEN:
+ /*
+ * All amps must be resumed before we can start playing back.
+ * This ensures, for external boost, that all amps are in AMP_SAFE mode.
+ * Do this in HDA_GEN_PCM_ACT_OPEN, since this is run prior to any of the
+ * other actions.
+ */
+ pm_runtime_get_sync(dev);
+ break;
case HDA_GEN_PCM_ACT_PREPARE:
mutex_lock(&cs35l41->fw_mutex);
- ret = cs35l41_global_enable(reg, cs35l41->hw_cfg.bst_type, 1, NULL);
+ cs35l41_hda_play_start(dev);
mutex_unlock(&cs35l41->fw_mutex);
break;
case HDA_GEN_PCM_ACT_CLEANUP:
mutex_lock(&cs35l41->fw_mutex);
- regmap_multi_reg_write(reg, cs35l41_hda_mute, ARRAY_SIZE(cs35l41_hda_mute));
- ret = cs35l41_global_enable(reg, cs35l41->hw_cfg.bst_type, 0, NULL);
+ cs35l41_hda_pause_done(dev);
mutex_unlock(&cs35l41->fw_mutex);
break;
case HDA_GEN_PCM_ACT_CLOSE:
mutex_lock(&cs35l41->fw_mutex);
- ret = regmap_update_bits(reg, CS35L41_PWR_CTRL2,
- CS35L41_AMP_EN_MASK, 0 << CS35L41_AMP_EN_SHIFT);
- if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST)
- regmap_write(reg, CS35L41_GPIO1_CTRL1, 0x00000001);
- if (cs35l41->firmware_running) {
- cs35l41_set_cspl_mbox_cmd(cs35l41->dev, cs35l41->regmap,
- CSPL_MBOX_CMD_PAUSE);
- regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2,
- CS35L41_VMON_EN_MASK | CS35L41_IMON_EN_MASK,
- 0 << CS35L41_VMON_EN_SHIFT | 0 << CS35L41_IMON_EN_SHIFT);
+ if (!cs35l41->firmware_running && cs35l41->request_fw_load &&
+ !cs35l41->fw_request_ongoing) {
+ dev_info(dev, "Requesting Firmware Load after HDA_GEN_PCM_ACT_CLOSE\n");
+ cs35l41->fw_request_ongoing = true;
+ schedule_work(&cs35l41->fw_load_work);
}
- cs35l41_irq_release(cs35l41);
- cs35l41->playback_started = false;
mutex_unlock(&cs35l41->fw_mutex);
+ /*
+ * Playback must be finished for all amps before we start runtime suspend.
+ * This ensures no amps are playing back when we start putting them to sleep.
+ */
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
break;
default:
- dev_warn(cs35l41->dev, "Playback action not supported: %d\n", action);
break;
}
+}
- if (ret)
- dev_err(cs35l41->dev, "Regmap access fail: %d\n", ret);
+static void cs35l41_hda_post_playback_hook(struct device *dev, int action)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+
+ switch (action) {
+ case HDA_GEN_PCM_ACT_PREPARE:
+ mutex_lock(&cs35l41->fw_mutex);
+ cs35l41_hda_play_done(dev);
+ mutex_unlock(&cs35l41->fw_mutex);
+ break;
+ default:
+ break;
+ }
}
static int cs35l41_hda_channel_map(struct device *dev, unsigned int tx_num, unsigned int *tx_slot,
@@ -572,21 +665,62 @@ static int cs35l41_hda_channel_map(struct device *dev, unsigned int tx_num, unsi
rx_slot);
}
-static void cs35l41_ready_for_reset(struct cs35l41_hda *cs35l41)
+static int cs35l41_ready_for_reset(struct cs35l41_hda *cs35l41)
{
+ int ret = 0;
+
mutex_lock(&cs35l41->fw_mutex);
if (cs35l41->firmware_running) {
regcache_cache_only(cs35l41->regmap, false);
- cs35l41_exit_hibernate(cs35l41->dev, cs35l41->regmap);
+ ret = cs35l41_exit_hibernate(cs35l41->dev, cs35l41->regmap);
+ if (ret) {
+ dev_warn(cs35l41->dev, "Unable to exit Hibernate.");
+ goto err;
+ }
+
+ /* Test key needs to be unlocked to allow the OTP settings to re-apply */
+ cs35l41_test_key_unlock(cs35l41->dev, cs35l41->regmap);
+ ret = regcache_sync(cs35l41->regmap);
+ cs35l41_test_key_lock(cs35l41->dev, cs35l41->regmap);
+ if (ret) {
+ dev_err(cs35l41->dev, "Failed to restore register cache: %d\n", ret);
+ goto err;
+ }
+
+ if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST)
+ cs35l41_init_boost(cs35l41->dev, cs35l41->regmap, &cs35l41->hw_cfg);
+
cs35l41_shutdown_dsp(cs35l41);
cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type);
+ }
+err:
+ regcache_cache_only(cs35l41->regmap, true);
+ regcache_mark_dirty(cs35l41->regmap);
+
+ mutex_unlock(&cs35l41->fw_mutex);
- regcache_cache_only(cs35l41->regmap, true);
- regcache_mark_dirty(cs35l41->regmap);
+ return ret;
+}
+
+static int cs35l41_system_suspend_prep(struct device *dev)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+
+ dev_dbg(cs35l41->dev, "System Suspend Prepare\n");
+
+ if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH) {
+ dev_err_once(cs35l41->dev, "System Suspend not supported\n");
+ return 0; /* don't block the whole system suspend */
}
+
+ mutex_lock(&cs35l41->fw_mutex);
+ if (cs35l41->playback_started)
+ cs35l41_hda_pause_start(dev);
mutex_unlock(&cs35l41->fw_mutex);
+
+ return 0;
}
static int cs35l41_system_suspend(struct device *dev)
@@ -601,18 +735,28 @@ static int cs35l41_system_suspend(struct device *dev)
return 0; /* don't block the whole system suspend */
}
+ mutex_lock(&cs35l41->fw_mutex);
+ if (cs35l41->playback_started)
+ cs35l41_hda_pause_done(dev);
+ mutex_unlock(&cs35l41->fw_mutex);
+
ret = pm_runtime_force_suspend(dev);
- if (ret)
+ if (ret) {
+ dev_err(dev, "System Suspend Failed, unable to runtime suspend: %d\n", ret);
return ret;
+ }
/* Shutdown DSP before system suspend */
- cs35l41_ready_for_reset(cs35l41);
+ ret = cs35l41_ready_for_reset(cs35l41);
+
+ if (ret)
+ dev_err(dev, "System Suspend Failed, not ready for Reset: %d\n", ret);
/*
* Reset GPIO may be shared, so cannot reset here.
* However beyond this point, amps may be powered down.
*/
- return 0;
+ return ret;
}
static int cs35l41_system_resume(struct device *dev)
@@ -635,9 +779,14 @@ static int cs35l41_system_resume(struct device *dev)
usleep_range(2000, 2100);
ret = pm_runtime_force_resume(dev);
+ if (ret) {
+ dev_err(dev, "System Resume Failed: Unable to runtime resume: %d\n", ret);
+ return ret;
+ }
mutex_lock(&cs35l41->fw_mutex);
- if (!ret && cs35l41->request_fw_load && !cs35l41->fw_request_ongoing) {
+
+ if (cs35l41->request_fw_load && !cs35l41->fw_request_ongoing) {
cs35l41->fw_request_ongoing = true;
schedule_work(&cs35l41->fw_load_work);
}
@@ -669,20 +818,6 @@ static int cs35l41_runtime_suspend(struct device *dev)
mutex_lock(&cs35l41->fw_mutex);
- if (cs35l41->playback_started) {
- regmap_multi_reg_write(cs35l41->regmap, cs35l41_hda_mute,
- ARRAY_SIZE(cs35l41_hda_mute));
- cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 0, NULL);
- regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2,
- CS35L41_AMP_EN_MASK, 0 << CS35L41_AMP_EN_SHIFT);
- if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST)
- regmap_write(cs35l41->regmap, CS35L41_GPIO1_CTRL1, 0x00000001);
- regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2,
- CS35L41_VMON_EN_MASK | CS35L41_IMON_EN_MASK,
- 0 << CS35L41_VMON_EN_SHIFT | 0 << CS35L41_IMON_EN_SHIFT);
- cs35l41->playback_started = false;
- }
-
if (cs35l41->firmware_running) {
ret = cs35l41_enter_hibernate(cs35l41->dev, cs35l41->regmap,
cs35l41->hw_cfg.bst_type);
@@ -778,7 +913,12 @@ static int cs35l41_smart_amp(struct cs35l41_hda *cs35l41)
goto clean_dsp;
}
- cs35l41_set_cspl_mbox_cmd(cs35l41->dev, cs35l41->regmap, CSPL_MBOX_CMD_PAUSE);
+ ret = cs35l41_set_cspl_mbox_cmd(cs35l41->dev, cs35l41->regmap, CSPL_MBOX_CMD_PAUSE);
+ if (ret) {
+ dev_err(cs35l41->dev, "Error waiting for DSP to pause: %u\n", ret);
+ goto clean_dsp;
+ }
+
cs35l41->firmware_running = true;
return 0;
@@ -937,6 +1077,7 @@ static int cs35l41_hda_bind(struct device *dev, struct device *master, void *mas
{
struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
struct hda_component *comps = master_data;
+ unsigned int sleep_flags;
int ret = 0;
if (!comps || cs35l41->index < 0 || cs35l41->index >= HDA_MAX_COMPONENTS)
@@ -971,12 +1112,26 @@ static int cs35l41_hda_bind(struct device *dev, struct device *master, void *mas
ret = cs35l41_create_controls(cs35l41);
comps->playback_hook = cs35l41_hda_playback_hook;
+ comps->pre_playback_hook = cs35l41_hda_pre_playback_hook;
+ comps->post_playback_hook = cs35l41_hda_post_playback_hook;
mutex_unlock(&cs35l41->fw_mutex);
+ sleep_flags = lock_system_sleep();
+ if (!device_link_add(&comps->codec->core.dev, cs35l41->dev, DL_FLAG_STATELESS))
+ dev_warn(dev, "Unable to create device link\n");
+ unlock_system_sleep(sleep_flags);
+
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
+ dev_info(cs35l41->dev,
+ "CS35L41 Bound - SSID: %s, BST: %d, VSPK: %d, CH: %c, FW EN: %d, SPKID: %d\n",
+ cs35l41->acpi_subsystem_id, cs35l41->hw_cfg.bst_type,
+ cs35l41->hw_cfg.gpio1.func == CS35l41_VSPK_SWITCH,
+ cs35l41->hw_cfg.spk_pos ? 'R' : 'L',
+ cs35l41->firmware_running, cs35l41->speaker_id);
+
return ret;
}
@@ -984,9 +1139,14 @@ static void cs35l41_hda_unbind(struct device *dev, struct device *master, void *
{
struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
struct hda_component *comps = master_data;
+ unsigned int sleep_flags;
- if (comps[cs35l41->index].dev == dev)
+ if (comps[cs35l41->index].dev == dev) {
memset(&comps[cs35l41->index], 0, sizeof(*comps));
+ sleep_flags = lock_system_sleep();
+ device_link_remove(&comps->codec->core.dev, cs35l41->dev);
+ unlock_system_sleep(sleep_flags);
+ }
}
static const struct component_ops cs35l41_hda_comp_ops = {
@@ -1156,8 +1316,7 @@ static int cs35l41_hda_apply_properties(struct cs35l41_hda *cs35l41)
return cs35l41_hda_channel_map(cs35l41->dev, 0, NULL, 1, &hw_cfg->spk_pos);
}
-static int cs35l41_get_speaker_id(struct device *dev, int amp_index,
- int num_amps, int fixed_gpio_id)
+int cs35l41_get_speaker_id(struct device *dev, int amp_index, int num_amps, int fixed_gpio_id)
{
struct gpio_desc *speaker_id_desc;
int speaker_id = -ENODEV;
@@ -1211,49 +1370,6 @@ static int cs35l41_get_speaker_id(struct device *dev, int amp_index,
return speaker_id;
}
-/*
- * Device CLSA010(0/1) doesn't have _DSD so a gpiod_get by the label reset won't work.
- * And devices created by serial-multi-instantiate don't have their device struct
- * pointing to the correct fwnode, so acpi_dev must be used here.
- * And devm functions expect that the device requesting the resource has the correct
- * fwnode.
- */
-static int cs35l41_no_acpi_dsd(struct cs35l41_hda *cs35l41, struct device *physdev, int id,
- const char *hid)
-{
- struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg;
-
- /* check I2C address to assign the index */
- cs35l41->index = id == 0x40 ? 0 : 1;
- cs35l41->channel_index = 0;
- cs35l41->reset_gpio = gpiod_get_index(physdev, NULL, 0, GPIOD_OUT_HIGH);
- cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, 0, 0, 2);
- hw_cfg->spk_pos = cs35l41->index;
- hw_cfg->gpio2.func = CS35L41_INTERRUPT;
- hw_cfg->gpio2.valid = true;
- hw_cfg->valid = true;
-
- if (strncmp(hid, "CLSA0100", 8) == 0) {
- hw_cfg->bst_type = CS35L41_EXT_BOOST_NO_VSPK_SWITCH;
- } else if (strncmp(hid, "CLSA0101", 8) == 0) {
- hw_cfg->bst_type = CS35L41_EXT_BOOST;
- hw_cfg->gpio1.func = CS35l41_VSPK_SWITCH;
- hw_cfg->gpio1.valid = true;
- } else {
- /*
- * Note: CLSA010(0/1) are special cases which use a slightly different design.
- * All other HIDs e.g. CSC3551 require valid ACPI _DSD properties to be supported.
- */
- dev_err(cs35l41->dev, "Error: ACPI _DSD Properties are missing for HID %s.\n", hid);
- hw_cfg->valid = false;
- hw_cfg->gpio1.valid = false;
- hw_cfg->gpio2.valid = false;
- return -EINVAL;
- }
-
- return 0;
-}
-
static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, int id)
{
struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg;
@@ -1279,12 +1395,17 @@ static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, i
sub = NULL;
cs35l41->acpi_subsystem_id = sub;
+ ret = cs35l41_add_dsd_properties(cs35l41, physdev, id, hid);
+ if (!ret) {
+ dev_info(cs35l41->dev, "Using extra _DSD properties, bypassing _DSD in ACPI\n");
+ goto put_physdev;
+ }
+
property = "cirrus,dev-index";
ret = device_property_count_u32(physdev, property);
- if (ret <= 0) {
- ret = cs35l41_no_acpi_dsd(cs35l41, physdev, id, hid);
- goto err_put_physdev;
- }
+ if (ret <= 0)
+ goto err;
+
if (ret > ARRAY_SIZE(values)) {
ret = -EINVAL;
goto err;
@@ -1374,7 +1495,10 @@ static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, i
err:
dev_err(cs35l41->dev, "Failed property %s: %d\n", property, ret);
-err_put_physdev:
+ hw_cfg->valid = false;
+ hw_cfg->gpio1.valid = false;
+ hw_cfg->gpio2.valid = false;
+put_physdev:
put_device(physdev);
return ret;
@@ -1477,6 +1601,11 @@ int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int i
if (ret)
goto err;
+ ret = regmap_multi_reg_write(cs35l41->regmap, cs35l41_hda_mute,
+ ARRAY_SIZE(cs35l41_hda_mute));
+ if (ret)
+ goto err;
+
INIT_WORK(&cs35l41->fw_load_work, cs35l41_fw_load_work);
mutex_init(&cs35l41->fw_mutex);
@@ -1542,6 +1671,7 @@ EXPORT_SYMBOL_NS_GPL(cs35l41_hda_remove, SND_HDA_SCODEC_CS35L41);
const struct dev_pm_ops cs35l41_hda_pm_ops = {
RUNTIME_PM_OPS(cs35l41_runtime_suspend, cs35l41_runtime_resume,
cs35l41_runtime_idle)
+ .prepare = cs35l41_system_suspend_prep,
SYSTEM_SLEEP_PM_OPS(cs35l41_system_suspend, cs35l41_system_resume)
};
EXPORT_SYMBOL_NS_GPL(cs35l41_hda_pm_ops, SND_HDA_SCODEC_CS35L41);
diff --git a/sound/pci/hda/cs35l41_hda.h b/sound/pci/hda/cs35l41_hda.h
index bdb35f3be68a..b93bf762976e 100644
--- a/sound/pci/hda/cs35l41_hda.h
+++ b/sound/pci/hda/cs35l41_hda.h
@@ -83,5 +83,6 @@ extern const struct dev_pm_ops cs35l41_hda_pm_ops;
int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int irq,
struct regmap *regmap);
void cs35l41_hda_remove(struct device *dev);
+int cs35l41_get_speaker_id(struct device *dev, int amp_index, int num_amps, int fixed_gpio_id);
#endif /*__CS35L41_HDA_H__*/
diff --git a/sound/pci/hda/cs35l41_hda_property.c b/sound/pci/hda/cs35l41_hda_property.c
new file mode 100644
index 000000000000..b62a4e6968e2
--- /dev/null
+++ b/sound/pci/hda/cs35l41_hda_property.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// CS35L41 ALSA HDA Property driver
+//
+// Copyright 2023 Cirrus Logic, Inc.
+//
+// Author: Stefan Binding <sbinding@opensource.cirrus.com>
+
+#include <linux/gpio/consumer.h>
+#include <linux/string.h>
+#include "cs35l41_hda_property.h"
+
+/*
+ * Device CLSA010(0/1) doesn't have _DSD so a gpiod_get by the label reset won't work.
+ * And devices created by serial-multi-instantiate don't have their device struct
+ * pointing to the correct fwnode, so acpi_dev must be used here.
+ * And devm functions expect that the device requesting the resource has the correct
+ * fwnode.
+ */
+static int lenovo_legion_no_acpi(struct cs35l41_hda *cs35l41, struct device *physdev, int id,
+ const char *hid)
+{
+ struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg;
+
+ /* check I2C address to assign the index */
+ cs35l41->index = id == 0x40 ? 0 : 1;
+ cs35l41->channel_index = 0;
+ cs35l41->reset_gpio = gpiod_get_index(physdev, NULL, 0, GPIOD_OUT_HIGH);
+ cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, 0, 0, 2);
+ hw_cfg->spk_pos = cs35l41->index;
+ hw_cfg->gpio2.func = CS35L41_INTERRUPT;
+ hw_cfg->gpio2.valid = true;
+ hw_cfg->valid = true;
+
+ if (strcmp(hid, "CLSA0100") == 0) {
+ hw_cfg->bst_type = CS35L41_EXT_BOOST_NO_VSPK_SWITCH;
+ } else if (strcmp(hid, "CLSA0101") == 0) {
+ hw_cfg->bst_type = CS35L41_EXT_BOOST;
+ hw_cfg->gpio1.func = CS35l41_VSPK_SWITCH;
+ hw_cfg->gpio1.valid = true;
+ }
+
+ return 0;
+}
+
+/*
+ * Device 103C89C6 does have _DSD, however it is setup to use the wrong boost type.
+ * We can override the _DSD to correct the boost type here.
+ * Since this laptop has valid ACPI, we do not need to handle cs-gpios, since that already exists
+ * in the ACPI.
+ */
+static int hp_vision_acpi_fix(struct cs35l41_hda *cs35l41, struct device *physdev, int id,
+ const char *hid)
+{
+ struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg;
+
+ dev_info(cs35l41->dev, "Adding DSD properties for %s\n", cs35l41->acpi_subsystem_id);
+
+ cs35l41->index = id;
+ cs35l41->channel_index = 0;
+ cs35l41->reset_gpio = gpiod_get_index(physdev, NULL, 1, GPIOD_OUT_HIGH);
+ cs35l41->speaker_id = -ENOENT;
+ hw_cfg->spk_pos = cs35l41->index ? 1 : 0; // right:left
+ hw_cfg->gpio1.func = CS35L41_NOT_USED;
+ hw_cfg->gpio1.valid = true;
+ hw_cfg->gpio2.func = CS35L41_INTERRUPT;
+ hw_cfg->gpio2.valid = true;
+ hw_cfg->bst_type = CS35L41_INT_BOOST;
+ hw_cfg->bst_ind = 1000;
+ hw_cfg->bst_ipk = 4500;
+ hw_cfg->bst_cap = 24;
+ hw_cfg->valid = true;
+
+ return 0;
+}
+
+struct cs35l41_prop_model {
+ const char *hid;
+ const char *ssid;
+ int (*add_prop)(struct cs35l41_hda *cs35l41, struct device *physdev, int id,
+ const char *hid);
+};
+
+static const struct cs35l41_prop_model cs35l41_prop_model_table[] = {
+ { "CLSA0100", NULL, lenovo_legion_no_acpi },
+ { "CLSA0101", NULL, lenovo_legion_no_acpi },
+ { "CSC3551", "103C89C6", hp_vision_acpi_fix },
+ {}
+};
+
+int cs35l41_add_dsd_properties(struct cs35l41_hda *cs35l41, struct device *physdev, int id,
+ const char *hid)
+{
+ const struct cs35l41_prop_model *model;
+
+ for (model = cs35l41_prop_model_table; model->hid; model++) {
+ if (!strcmp(model->hid, hid) &&
+ (!model->ssid ||
+ (cs35l41->acpi_subsystem_id &&
+ !strcmp(model->ssid, cs35l41->acpi_subsystem_id))))
+ return model->add_prop(cs35l41, physdev, id, hid);
+ }
+
+ return -ENOENT;
+}
diff --git a/sound/pci/hda/cs35l41_hda_property.h b/sound/pci/hda/cs35l41_hda_property.h
new file mode 100644
index 000000000000..fd834042e2fd
--- /dev/null
+++ b/sound/pci/hda/cs35l41_hda_property.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * CS35L41 ALSA HDA Property driver
+ *
+ * Copyright 2023 Cirrus Logic, Inc.
+ *
+ * Author: Stefan Binding <sbinding@opensource.cirrus.com>
+ */
+
+#ifndef CS35L41_HDA_PROP_H
+#define CS35L41_HDA_PROP_H
+
+#include <linux/device.h>
+#include "cs35l41_hda.h"
+
+int cs35l41_add_dsd_properties(struct cs35l41_hda *cs35l41, struct device *physdev, int id,
+ const char *hid);
+#endif /* CS35L41_HDA_PROP_H */
diff --git a/sound/pci/hda/cs35l56_hda.c b/sound/pci/hda/cs35l56_hda.c
index 71e95e64f8a4..76b9c685560b 100644
--- a/sound/pci/hda/cs35l56_hda.c
+++ b/sound/pci/hda/cs35l56_hda.c
@@ -525,25 +525,55 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
const struct firmware *wmfw_firmware = NULL;
char *coeff_filename = NULL;
char *wmfw_filename = NULL;
+ unsigned int firmware_missing;
int ret = 0;
- cs35l56_hda_request_firmware_files(cs35l56, &wmfw_firmware, &wmfw_filename,
- &coeff_firmware, &coeff_filename);
+ /* Prepare for a new DSP power-up */
+ if (cs35l56->base.fw_patched)
+ cs_dsp_power_down(&cs35l56->cs_dsp);
- /* Nothing to do - no firmware files were found to download */
- if (!wmfw_filename && !coeff_filename)
- return 0;
+ cs35l56->base.fw_patched = false;
- mutex_lock(&cs35l56->base.irq_lock);
pm_runtime_get_sync(cs35l56->base.dev);
+ ret = regmap_read(cs35l56->base.regmap, CS35L56_PROTECTION_STATUS, &firmware_missing);
+ if (ret) {
+ dev_err(cs35l56->base.dev, "Failed to read PROTECTION_STATUS: %d\n", ret);
+ goto err_pm_put;
+ }
+
+ firmware_missing &= CS35L56_FIRMWARE_MISSING;
+
+ /*
+ * Firmware can only be downloaded if the CS35L56 is secured or is
+ * running from the built-in ROM. If it is secured the BIOS will have
+ * downloaded firmware, and the wmfw/bin files will only contain
+ * tunings that are safe to download with the firmware running.
+ */
+ if (cs35l56->base.secured || firmware_missing) {
+ cs35l56_hda_request_firmware_files(cs35l56, &wmfw_firmware, &wmfw_filename,
+ &coeff_firmware, &coeff_filename);
+ }
+
+ /*
+ * If the BIOS didn't patch the firmware a bin file is mandatory to
+ * enable the ASP·
+ */
+ if (!coeff_firmware && firmware_missing) {
+ dev_err(cs35l56->base.dev, ".bin file required but not found\n");
+ ret = -ENOENT;
+ goto err_fw_release;
+ }
+
+ mutex_lock(&cs35l56->base.irq_lock);
+
/*
* When the device is running in secure mode the firmware files can
* only contain insecure tunings and therefore we do not need to
* shutdown the firmware to apply them and can use the lower cost
* reinit sequence instead.
*/
- if (!cs35l56->base.secured) {
+ if (!cs35l56->base.secured && (wmfw_firmware || coeff_firmware)) {
ret = cs35l56_firmware_shutdown(&cs35l56->base);
if (ret)
goto err;
@@ -562,41 +592,44 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
if (coeff_filename)
dev_dbg(cs35l56->base.dev, "Loaded Coefficients: %s\n", coeff_filename);
- ret = cs_dsp_run(&cs35l56->cs_dsp);
- if (ret) {
- dev_dbg(cs35l56->base.dev, "%s: cs_dsp_run ret %d\n", __func__, ret);
- goto err;
- }
-
if (cs35l56->base.secured) {
ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT);
if (ret)
- goto err;
- } else {
- /* Reset the device and wait for it to boot */
+ goto err_powered_up;
+ } else if (wmfw_firmware || coeff_firmware) {
+ /* If we downloaded firmware, reset the device and wait for it to boot */
cs35l56_system_reset(&cs35l56->base, false);
+ regcache_mark_dirty(cs35l56->base.regmap);
ret = cs35l56_wait_for_firmware_boot(&cs35l56->base);
if (ret)
- goto err;
+ goto err_powered_up;
}
/* Disable auto-hibernate so that runtime_pm has control */
ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE);
if (ret)
- goto err;
+ goto err_powered_up;
- regcache_mark_dirty(cs35l56->base.regmap);
regcache_sync(cs35l56->base.regmap);
regmap_clear_bits(cs35l56->base.regmap, CS35L56_PROTECTION_STATUS,
CS35L56_FIRMWARE_MISSING);
cs35l56->base.fw_patched = true;
+
+ ret = cs_dsp_run(&cs35l56->cs_dsp);
+ if (ret)
+ dev_dbg(cs35l56->base.dev, "%s: cs_dsp_run ret %d\n", __func__, ret);
+
+err_powered_up:
+ if (!cs35l56->base.fw_patched)
+ cs_dsp_power_down(&cs35l56->cs_dsp);
err:
- pm_runtime_put(cs35l56->base.dev);
mutex_unlock(&cs35l56->base.irq_lock);
-
+err_fw_release:
cs35l56_hda_release_firmware_files(wmfw_firmware, wmfw_filename,
coeff_firmware, coeff_filename);
+err_pm_put:
+ pm_runtime_put(cs35l56->base.dev);
return ret;
}
@@ -648,6 +681,9 @@ static void cs35l56_hda_unbind(struct device *dev, struct device *master, void *
debugfs_remove_recursive(cs35l56->debugfs_root);
#endif
+ if (cs35l56->base.fw_patched)
+ cs_dsp_power_down(&cs35l56->cs_dsp);
+
cs_dsp_remove(&cs35l56->cs_dsp);
if (comps[cs35l56->index].dev == dev)
@@ -816,8 +852,12 @@ static int cs35l56_hda_read_acpi(struct cs35l56_hda *cs35l56, int id)
break;
}
}
+ /*
+ * It's not an error for the ID to be missing: for I2C there can be
+ * an alias address that is not a real device. So reject silently.
+ */
if (cs35l56->index == -1) {
- dev_err(cs35l56->base.dev, "No index found in %s\n", property);
+ dev_dbg(cs35l56->base.dev, "No index found in %s\n", property);
ret = -ENODEV;
goto err;
}
@@ -855,7 +895,8 @@ static int cs35l56_hda_read_acpi(struct cs35l56_hda *cs35l56, int id)
return 0;
err:
- dev_err(cs35l56->base.dev, "Failed property %s: %d\n", property, ret);
+ if (ret != -ENODEV)
+ dev_err(cs35l56->base.dev, "Failed property %s: %d\n", property, ret);
return ret;
}
@@ -868,10 +909,8 @@ int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int id)
dev_set_drvdata(cs35l56->base.dev, cs35l56);
ret = cs35l56_hda_read_acpi(cs35l56, id);
- if (ret) {
- dev_err_probe(cs35l56->base.dev, ret, "Platform not supported\n");
+ if (ret)
goto err;
- }
cs35l56->amp_name = devm_kasprintf(cs35l56->base.dev, GFP_KERNEL, "AMP%d",
cs35l56->index + 1);
@@ -907,7 +946,7 @@ int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int id)
ret = cs35l56_set_patch(&cs35l56->base);
if (ret)
- return ret;
+ goto err;
regcache_mark_dirty(cs35l56->base.regmap);
regcache_sync(cs35l56->base.regmap);
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index bd19f92aeeec..33af707a65ab 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -1769,10 +1769,8 @@ void snd_hda_ctls_clear(struct hda_codec *codec)
int i;
struct hda_nid_item *items = codec->mixers.list;
- down_write(&codec->card->controls_rwsem);
for (i = 0; i < codec->mixers.used; i++)
snd_ctl_remove(codec->card, items[i].kctl);
- up_write(&codec->card->controls_rwsem);
snd_array_free(&codec->mixers);
snd_array_free(&codec->nids);
}
diff --git a/sound/pci/hda/hda_component.h b/sound/pci/hda/hda_component.h
index 534e845b9cd1..f170aec967c1 100644
--- a/sound/pci/hda/hda_component.h
+++ b/sound/pci/hda/hda_component.h
@@ -15,5 +15,7 @@ struct hda_component {
struct device *dev;
char name[HDA_MAX_NAME_SIZE];
struct hda_codec *codec;
+ void (*pre_playback_hook)(struct device *dev, int action);
void (*playback_hook)(struct device *dev, int action);
+ void (*post_playback_hook)(struct device *dev, int action);
};
diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c
index 125e97fe0b1c..727f39acedfc 100644
--- a/sound/pci/hda/hda_hwdep.c
+++ b/sound/pci/hda/hda_hwdep.c
@@ -114,8 +114,8 @@ int snd_hda_create_hwdep(struct hda_codec *codec)
#endif
/* for sysfs */
- hwdep->dev.groups = snd_hda_dev_attr_groups;
- dev_set_drvdata(&hwdep->dev, codec);
+ hwdep->dev->groups = snd_hda_dev_attr_groups;
+ dev_set_drvdata(hwdep->dev, codec);
return 0;
}
diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c
index 9d0ab043880b..d967e70a7058 100644
--- a/sound/pci/hda/hda_tegra.c
+++ b/sound/pci/hda/hda_tegra.c
@@ -16,7 +16,8 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/mutex.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
#include <linux/reset.h>
#include <linux/slab.h>
#include <linux/time.h>
@@ -378,14 +379,14 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev)
}
/* driver name */
- strncpy(card->driver, drv_name, sizeof(card->driver));
+ strscpy(card->driver, drv_name, sizeof(card->driver));
/* shortname for card */
sname = of_get_property(np, "nvidia,model", NULL);
if (!sname)
sname = drv_name;
if (strlen(sname) > sizeof(card->shortname))
dev_info(card->dev, "truncating shortname for card\n");
- strncpy(card->shortname, sname, sizeof(card->shortname));
+ strscpy(card->shortname, sname, sizeof(card->shortname));
/* longname for card */
snprintf(card->longname, sizeof(card->longname),
diff --git a/sound/pci/hda/patch_cs8409-tables.c b/sound/pci/hda/patch_cs8409-tables.c
index b288874e401e..36b411d1a960 100644
--- a/sound/pci/hda/patch_cs8409-tables.c
+++ b/sound/pci/hda/patch_cs8409-tables.c
@@ -550,6 +550,10 @@ const struct snd_pci_quirk cs8409_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x0C50, "Dolphin", CS8409_DOLPHIN),
SND_PCI_QUIRK(0x1028, 0x0C51, "Dolphin", CS8409_DOLPHIN),
SND_PCI_QUIRK(0x1028, 0x0C52, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0C73, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0C75, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0C7D, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0C7F, "Dolphin", CS8409_DOLPHIN),
{} /* terminator */
};
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 260d3e64f658..1cde2a69bdb4 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -4632,8 +4632,9 @@ HDA_CODEC_ENTRY(0x80862819, "DG2 HDMI", patch_i915_tgl_hdmi),
HDA_CODEC_ENTRY(0x8086281a, "Jasperlake HDMI", patch_i915_icl_hdmi),
HDA_CODEC_ENTRY(0x8086281b, "Elkhartlake HDMI", patch_i915_icl_hdmi),
HDA_CODEC_ENTRY(0x8086281c, "Alderlake-P HDMI", patch_i915_adlp_hdmi),
-HDA_CODEC_ENTRY(0x8086281f, "Raptorlake-P HDMI", patch_i915_adlp_hdmi),
-HDA_CODEC_ENTRY(0x8086281d, "Meteorlake HDMI", patch_i915_adlp_hdmi),
+HDA_CODEC_ENTRY(0x8086281d, "Meteor Lake HDMI", patch_i915_adlp_hdmi),
+HDA_CODEC_ENTRY(0x8086281f, "Raptor Lake P HDMI", patch_i915_adlp_hdmi),
+HDA_CODEC_ENTRY(0x80862820, "Lunar Lake HDMI", patch_i915_adlp_hdmi),
HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi),
HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI", patch_i915_byt_hdmi),
HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI", patch_i915_byt_hdmi),
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 44fccfb93cff..a07df6f92960 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -4639,6 +4639,22 @@ static void alc236_fixup_hp_mute_led_coefbit2(struct hda_codec *codec,
}
}
+static void alc245_fixup_hp_mute_led_coefbit(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->mute_led_polarity = 0;
+ spec->mute_led_coef.idx = 0x0b;
+ spec->mute_led_coef.mask = 3 << 2;
+ spec->mute_led_coef.on = 2 << 2;
+ spec->mute_led_coef.off = 1 << 2;
+ snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set);
+ }
+}
+
/* turn on/off mic-mute LED per capture hook by coef bit */
static int coef_micmute_led_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
@@ -6716,12 +6732,20 @@ static void comp_generic_playback_hook(struct hda_pcm_stream *hinfo, struct hda_
int i;
for (i = 0; i < HDA_MAX_COMPONENTS; i++) {
- if (spec->comps[i].dev)
+ if (spec->comps[i].dev && spec->comps[i].pre_playback_hook)
+ spec->comps[i].pre_playback_hook(spec->comps[i].dev, action);
+ }
+ for (i = 0; i < HDA_MAX_COMPONENTS; i++) {
+ if (spec->comps[i].dev && spec->comps[i].playback_hook)
spec->comps[i].playback_hook(spec->comps[i].dev, action);
}
+ for (i = 0; i < HDA_MAX_COMPONENTS; i++) {
+ if (spec->comps[i].dev && spec->comps[i].post_playback_hook)
+ spec->comps[i].post_playback_hook(spec->comps[i].dev, action);
+ }
}
-struct cs35l41_dev_name {
+struct scodec_dev_name {
const char *bus;
const char *hid;
int index;
@@ -6730,7 +6754,7 @@ struct cs35l41_dev_name {
/* match the device name in a slightly relaxed manner */
static int comp_match_cs35l41_dev_name(struct device *dev, void *data)
{
- struct cs35l41_dev_name *p = data;
+ struct scodec_dev_name *p = data;
const char *d = dev_name(dev);
int n = strlen(p->bus);
char tmp[32];
@@ -6746,12 +6770,32 @@ static int comp_match_cs35l41_dev_name(struct device *dev, void *data)
return !strcmp(d + n, tmp);
}
+static int comp_match_tas2781_dev_name(struct device *dev,
+ void *data)
+{
+ struct scodec_dev_name *p = data;
+ const char *d = dev_name(dev);
+ int n = strlen(p->bus);
+ char tmp[32];
+
+ /* check the bus name */
+ if (strncmp(d, p->bus, n))
+ return 0;
+ /* skip the bus number */
+ if (isdigit(d[n]))
+ n++;
+ /* the rest must be exact matching */
+ snprintf(tmp, sizeof(tmp), "-%s:00", p->hid);
+
+ return !strcmp(d + n, tmp);
+}
+
static void cs35l41_generic_fixup(struct hda_codec *cdc, int action, const char *bus,
const char *hid, int count)
{
struct device *dev = hda_codec_dev(cdc);
struct alc_spec *spec = cdc->spec;
- struct cs35l41_dev_name *rec;
+ struct scodec_dev_name *rec;
int ret, i;
switch (action) {
@@ -6779,6 +6823,41 @@ static void cs35l41_generic_fixup(struct hda_codec *cdc, int action, const char
}
}
+static void tas2781_generic_fixup(struct hda_codec *cdc, int action,
+ const char *bus, const char *hid)
+{
+ struct device *dev = hda_codec_dev(cdc);
+ struct alc_spec *spec = cdc->spec;
+ struct scodec_dev_name *rec;
+ int ret;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ rec = devm_kmalloc(dev, sizeof(*rec), GFP_KERNEL);
+ if (!rec)
+ return;
+ rec->bus = bus;
+ rec->hid = hid;
+ rec->index = 0;
+ spec->comps[0].codec = cdc;
+ component_match_add(dev, &spec->match,
+ comp_match_tas2781_dev_name, rec);
+ ret = component_master_add_with_match(dev, &comp_master_ops,
+ spec->match);
+ if (ret)
+ codec_err(cdc,
+ "Fail to register component aggregator %d\n",
+ ret);
+ else
+ spec->gen.pcm_playback_hook =
+ comp_generic_playback_hook;
+ break;
+ case HDA_FIXUP_ACT_FREE:
+ component_master_del(dev, &comp_master_ops);
+ break;
+ }
+}
+
static void cs35l41_fixup_i2c_two(struct hda_codec *cdc, const struct hda_fixup *fix, int action)
{
cs35l41_generic_fixup(cdc, action, "i2c", "CSC3551", 2);
@@ -6806,6 +6885,12 @@ static void alc287_fixup_legion_16ithg6_speakers(struct hda_codec *cdc, const st
cs35l41_generic_fixup(cdc, action, "i2c", "CLSA0101", 2);
}
+static void tas2781_fixup_i2c(struct hda_codec *cdc,
+ const struct hda_fixup *fix, int action)
+{
+ tas2781_generic_fixup(cdc, action, "i2c", "TIAS2781");
+}
+
/* for alc295_fixup_hp_top_speakers */
#include "hp_x360_helper.c"
@@ -7231,6 +7316,9 @@ enum {
ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS,
ALC236_FIXUP_DELL_DUAL_CODECS,
ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI,
+ ALC287_FIXUP_TAS2781_I2C,
+ ALC245_FIXUP_HP_MUTE_LED_COEFBIT,
+ ALC245_FIXUP_HP_X360_MUTE_LEDS,
};
/* A special fixup for Lenovo C940 and Yoga Duet 7;
@@ -9309,6 +9397,22 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC269_FIXUP_THINKPAD_ACPI,
},
+ [ALC287_FIXUP_TAS2781_I2C] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = tas2781_fixup_i2c,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI,
+ },
+ [ALC245_FIXUP_HP_MUTE_LED_COEFBIT] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc245_fixup_hp_mute_led_coefbit,
+ },
+ [ALC245_FIXUP_HP_X360_MUTE_LEDS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc245_fixup_hp_mute_led_coefbit,
+ .chained = true,
+ .chain_id = ALC245_FIXUP_HP_GPIO_LED
+ },
};
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -9422,11 +9526,10 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x0cbd, "Dell Oasis 13 CS MTL-U", ALC245_FIXUP_CS35L41_SPI_2),
SND_PCI_QUIRK(0x1028, 0x0cbe, "Dell Oasis 13 2-IN-1 MTL-U", ALC245_FIXUP_CS35L41_SPI_2),
SND_PCI_QUIRK(0x1028, 0x0cbf, "Dell Oasis 13 Low Weight MTU-L", ALC245_FIXUP_CS35L41_SPI_2),
- SND_PCI_QUIRK(0x1028, 0x0cc1, "Dell Oasis 14 MTL-H/U", ALC287_FIXUP_CS35L41_I2C_2),
- SND_PCI_QUIRK(0x1028, 0x0cc2, "Dell Oasis 14 2-in-1 MTL-H/U", ALC287_FIXUP_CS35L41_I2C_2),
- SND_PCI_QUIRK(0x1028, 0x0cc3, "Dell Oasis 14 Low Weight MTL-U", ALC287_FIXUP_CS35L41_I2C_2),
- SND_PCI_QUIRK(0x1028, 0x0cc4, "Dell Oasis 16 MTL-H/U", ALC287_FIXUP_CS35L41_I2C_2),
- SND_PCI_QUIRK(0x1028, 0x0cc5, "Dell Oasis MLK 14 RPL-P", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1028, 0x0cc1, "Dell Oasis 14 MTL-H/U", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1028, 0x0cc2, "Dell Oasis 14 2-in-1 MTL-H/U", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1028, 0x0cc3, "Dell Oasis 14 Low Weight MTL-U", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1028, 0x0cc4, "Dell Oasis 16 MTL-H/U", ALC245_FIXUP_CS35L41_SPI_2),
SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2),
@@ -9552,6 +9655,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8870, "HP ZBook Fury 15.6 Inch G8 Mobile Workstation PC", ALC285_FIXUP_HP_GPIO_AMP_INIT),
SND_PCI_QUIRK(0x103c, 0x8873, "HP ZBook Studio 15.6 Inch G8 Mobile Workstation PC", ALC285_FIXUP_HP_GPIO_AMP_INIT),
SND_PCI_QUIRK(0x103c, 0x887a, "HP Laptop 15s-eq2xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
+ SND_PCI_QUIRK(0x103c, 0x888a, "HP ENVY x360 Convertible 15-eu0xxx", ALC245_FIXUP_HP_X360_MUTE_LEDS),
SND_PCI_QUIRK(0x103c, 0x888d, "HP ZBook Power 15.6 inch G8 Mobile Workstation PC", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8895, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED),
SND_PCI_QUIRK(0x103c, 0x8896, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_MUTE_LED),
@@ -9583,6 +9687,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x89c6, "Zbook Fury 17 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x89ca, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
SND_PCI_QUIRK(0x103c, 0x89d3, "HP EliteBook 645 G9 (MB 89D2)", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8a25, "HP Victus 16-d1xxx (MB 8A25)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
SND_PCI_QUIRK(0x103c, 0x8a78, "HP Dev One", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x103c, 0x8aa0, "HP ProBook 440 G9 (MB 8A9E)", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8aa3, "HP ProBook 450 G9 (MB 8AA1)", ALC236_FIXUP_HP_GPIO_LED),
@@ -9617,7 +9722,13 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8b96, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
SND_PCI_QUIRK(0x103c, 0x8b97, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
SND_PCI_QUIRK(0x103c, 0x8bf0, "HP", ALC236_FIXUP_HP_GPIO_LED),
- SND_PCI_QUIRK(0x103c, 0x8c26, "HP HP EliteBook 800G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c46, "HP EliteBook 830 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c47, "HP EliteBook 840 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c48, "HP EliteBook 860 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c49, "HP Elite x360 830 2-in-1 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c70, "HP EliteBook 835 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c71, "HP EliteBook 845 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c72, "HP EliteBook 865 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC),
SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300),
SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
@@ -9884,6 +9995,20 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x3853, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
SND_PCI_QUIRK(0x17aa, 0x3855, "Legion 7 16ITHG6", ALC287_FIXUP_LEGION_16ITHG6),
SND_PCI_QUIRK(0x17aa, 0x3869, "Lenovo Yoga7 14IAL7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
+ SND_PCI_QUIRK(0x17aa, 0x387d, "Yoga S780-16 pro Quad AAC", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x387e, "Yoga S780-16 pro Quad YC", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x3881, "YB9 dual power mode2 YC", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x3884, "Y780 YG DUAL", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x3886, "Y780 VECO DUAL", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38a7, "Y780P AMD YG dual", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38a8, "Y780P AMD VECO dual", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38ba, "Yoga S780-14.5 Air AMD quad YC", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38bb, "Yoga S780-14.5 Air AMD quad AAC", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38be, "Yoga S980-14.5 proX YC Dual", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38bf, "Yoga S980-14.5 proX LX Dual", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38c3, "Y980 DUAL", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38cb, "Y790 YG DUAL", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38cd, "Y790 VECO DUAL", ALC287_FIXUP_TAS2781_I2C),
SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI),
SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC),
SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo B50-70", ALC269_FIXUP_DMIC_THINKPAD_ACPI),
@@ -10638,6 +10763,7 @@ static int patch_alc269(struct hda_codec *codec)
spec = codec->spec;
spec->gen.shared_mic_vref_pin = 0x18;
codec->power_save_node = 0;
+ spec->en_3kpull_low = true;
#ifdef CONFIG_PM
codec->patch_ops.suspend = alc269_suspend;
@@ -10720,14 +10846,16 @@ static int patch_alc269(struct hda_codec *codec)
spec->shutup = alc256_shutup;
spec->init_hook = alc256_init;
spec->gen.mixer_nid = 0; /* ALC256 does not have any loopback mixer path */
- if (codec->bus->pci->vendor == PCI_VENDOR_ID_AMD)
- spec->en_3kpull_low = true;
+ if (codec->core.vendor_id == 0x10ec0236 &&
+ codec->bus->pci->vendor != PCI_VENDOR_ID_AMD)
+ spec->en_3kpull_low = false;
break;
case 0x10ec0257:
spec->codec_variant = ALC269_TYPE_ALC257;
spec->shutup = alc256_shutup;
spec->init_hook = alc256_init;
spec->gen.mixer_nid = 0;
+ spec->en_3kpull_low = false;
break;
case 0x10ec0215:
case 0x10ec0245:
diff --git a/sound/pci/hda/tas2781_hda_i2c.c b/sound/pci/hda/tas2781_hda_i2c.c
new file mode 100644
index 000000000000..37114fd61a38
--- /dev/null
+++ b/sound/pci/hda/tas2781_hda_i2c.c
@@ -0,0 +1,856 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// TAS2781 HDA I2C driver
+//
+// Copyright 2023 Texas Instruments, Inc.
+//
+// Author: Shenghao Ding <shenghao-ding@ti.com>
+
+#include <linux/acpi.h>
+#include <linux/crc8.h>
+#include <linux/crc32.h>
+#include <linux/efi.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/hda_codec.h>
+#include <sound/soc.h>
+#include <sound/tas2781.h>
+#include <sound/tlv.h>
+#include <sound/tas2781-tlv.h>
+
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_component.h"
+#include "hda_jack.h"
+#include "hda_generic.h"
+
+#define TASDEVICE_SPEAKER_CALIBRATION_SIZE 20
+
+/* No standard control callbacks for SNDRV_CTL_ELEM_IFACE_CARD
+ * Define two controls, one is Volume control callbacks, the other is
+ * flag setting control callbacks.
+ */
+
+/* Volume control callbacks for tas2781 */
+#define ACARD_SINGLE_RANGE_EXT_TLV(xname, xreg, xshift, xmin, xmax, xinvert, \
+ xhandler_get, xhandler_put, tlv_array) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_CARD, .name = (xname),\
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
+ SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+ .tlv.p = (tlv_array), \
+ .info = snd_soc_info_volsw_range, \
+ .get = xhandler_get, .put = xhandler_put, \
+ .private_value = (unsigned long)&(struct soc_mixer_control) \
+ {.reg = xreg, .rreg = xreg, .shift = xshift, \
+ .rshift = xshift, .min = xmin, .max = xmax, \
+ .invert = xinvert} }
+
+/* Flag control callbacks for tas2781 */
+#define ACARD_SINGLE_BOOL_EXT(xname, xdata, xhandler_get, xhandler_put) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_CARD, .name = xname, \
+ .info = snd_ctl_boolean_mono_info, \
+ .get = xhandler_get, .put = xhandler_put, \
+ .private_value = xdata }
+
+enum calib_data {
+ R0_VAL = 0,
+ INV_R0,
+ R0LOW,
+ POWER,
+ TLIM,
+ CALIB_MAX
+};
+
+static int tas2781_get_i2c_res(struct acpi_resource *ares, void *data)
+{
+ struct tasdevice_priv *tas_priv = data;
+ struct acpi_resource_i2c_serialbus *sb;
+
+ if (i2c_acpi_get_i2c_resource(ares, &sb)) {
+ if (tas_priv->ndev < TASDEVICE_MAX_CHANNELS &&
+ sb->slave_address != TAS2781_GLOBAL_ADDR) {
+ tas_priv->tasdevice[tas_priv->ndev].dev_addr =
+ (unsigned int)sb->slave_address;
+ tas_priv->ndev++;
+ }
+ }
+ return 1;
+}
+
+static int tas2781_read_acpi(struct tasdevice_priv *p, const char *hid)
+{
+ struct acpi_device *adev;
+ struct device *physdev;
+ LIST_HEAD(resources);
+ const char *sub;
+ int ret;
+
+ adev = acpi_dev_get_first_match_dev(hid, NULL, -1);
+ if (!adev) {
+ dev_err(p->dev,
+ "Failed to find an ACPI device for %s\n", hid);
+ return -ENODEV;
+ }
+
+ ret = acpi_dev_get_resources(adev, &resources, tas2781_get_i2c_res, p);
+ if (ret < 0)
+ goto err;
+
+ acpi_dev_free_resource_list(&resources);
+ strscpy(p->dev_name, hid, sizeof(p->dev_name));
+ physdev = get_device(acpi_get_first_physical_node(adev));
+ acpi_dev_put(adev);
+
+ /* No side-effect to the playback even if subsystem_id is NULL*/
+ sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev));
+ if (IS_ERR(sub))
+ sub = NULL;
+
+ p->acpi_subsystem_id = sub;
+
+ put_device(physdev);
+
+ return 0;
+
+err:
+ dev_err(p->dev, "read acpi error, ret: %d\n", ret);
+ acpi_dev_put(adev);
+
+ return ret;
+}
+
+static void tas2781_hda_playback_hook(struct device *dev, int action)
+{
+ struct tasdevice_priv *tas_priv = dev_get_drvdata(dev);
+
+ dev_dbg(tas_priv->dev, "%s: action = %d\n", __func__, action);
+ switch (action) {
+ case HDA_GEN_PCM_ACT_OPEN:
+ pm_runtime_get_sync(dev);
+ mutex_lock(&tas_priv->codec_lock);
+ tasdevice_tuning_switch(tas_priv, 0);
+ mutex_unlock(&tas_priv->codec_lock);
+ break;
+ case HDA_GEN_PCM_ACT_CLOSE:
+ mutex_lock(&tas_priv->codec_lock);
+ tasdevice_tuning_switch(tas_priv, 1);
+ mutex_unlock(&tas_priv->codec_lock);
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+ break;
+ default:
+ dev_dbg(tas_priv->dev, "Playback action not supported: %d\n",
+ action);
+ break;
+ }
+}
+
+static int tasdevice_info_profile(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = tas_priv->rcabin.ncfgs - 1;
+
+ return 0;
+}
+
+static int tasdevice_get_profile_id(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = tas_priv->rcabin.profile_cfg_id;
+
+ return 0;
+}
+
+static int tasdevice_hda_clamp(int val, int max)
+{
+ if (val > max)
+ val = max;
+
+ if (val < 0)
+ val = 0;
+ return val;
+}
+
+static int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ int nr_profile = ucontrol->value.integer.value[0];
+ int max = tas_priv->rcabin.ncfgs - 1;
+ int val, ret = 0;
+
+ val = tasdevice_hda_clamp(nr_profile, max);
+
+ if (tas_priv->rcabin.profile_cfg_id != val) {
+ tas_priv->rcabin.profile_cfg_id = val;
+ ret = 1;
+ }
+
+ return ret;
+}
+
+static int tasdevice_info_programs(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_fw *tas_fw = tas_priv->fmw;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = tas_fw->nr_programs - 1;
+
+ return 0;
+}
+
+static int tasdevice_info_config(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_fw *tas_fw = tas_priv->fmw;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = tas_fw->nr_configurations - 1;
+
+ return 0;
+}
+
+static int tasdevice_program_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = tas_priv->cur_prog;
+
+ return 0;
+}
+
+static int tasdevice_program_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_fw *tas_fw = tas_priv->fmw;
+ int nr_program = ucontrol->value.integer.value[0];
+ int max = tas_fw->nr_programs - 1;
+ int val, ret = 0;
+
+ val = tasdevice_hda_clamp(nr_program, max);
+
+ if (tas_priv->cur_prog != val) {
+ tas_priv->cur_prog = val;
+ ret = 1;
+ }
+
+ return ret;
+}
+
+static int tasdevice_config_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = tas_priv->cur_conf;
+
+ return 0;
+}
+
+static int tasdevice_config_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_fw *tas_fw = tas_priv->fmw;
+ int nr_config = ucontrol->value.integer.value[0];
+ int max = tas_fw->nr_configurations - 1;
+ int val, ret = 0;
+
+ val = tasdevice_hda_clamp(nr_config, max);
+
+ if (tas_priv->cur_conf != val) {
+ tas_priv->cur_conf = val;
+ ret = 1;
+ }
+
+ return ret;
+}
+
+/*
+ * tas2781_digital_getvol - get the volum control
+ * @kcontrol: control pointer
+ * @ucontrol: User data
+ * Customer Kcontrol for tas2781 is primarily for regmap booking, paging
+ * depends on internal regmap mechanism.
+ * tas2781 contains book and page two-level register map, especially
+ * book switching will set the register BXXP00R7F, after switching to the
+ * correct book, then leverage the mechanism for paging to access the
+ * register.
+ */
+static int tas2781_digital_getvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ return tasdevice_digital_getvol(tas_priv, ucontrol, mc);
+}
+
+static int tas2781_amp_getvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ return tasdevice_amp_getvol(tas_priv, ucontrol, mc);
+}
+
+static int tas2781_digital_putvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ /* The check of the given value is in tasdevice_digital_putvol. */
+ return tasdevice_digital_putvol(tas_priv, ucontrol, mc);
+}
+
+static int tas2781_amp_putvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ /* The check of the given value is in tasdevice_amp_putvol. */
+ return tasdevice_amp_putvol(tas_priv, ucontrol, mc);
+}
+
+static int tas2781_force_fwload_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = (int)tas_priv->force_fwload_status;
+ dev_dbg(tas_priv->dev, "%s : Force FWload %s\n", __func__,
+ tas_priv->force_fwload_status ? "ON" : "OFF");
+
+ return 0;
+}
+
+static int tas2781_force_fwload_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ bool change, val = (bool)ucontrol->value.integer.value[0];
+
+ if (tas_priv->force_fwload_status == val)
+ change = false;
+ else {
+ change = true;
+ tas_priv->force_fwload_status = val;
+ }
+ dev_dbg(tas_priv->dev, "%s : Force FWload %s\n", __func__,
+ tas_priv->force_fwload_status ? "ON" : "OFF");
+
+ return change;
+}
+
+static const struct snd_kcontrol_new tas2781_snd_controls[] = {
+ ACARD_SINGLE_RANGE_EXT_TLV("Speaker Analog Gain", TAS2781_AMP_LEVEL,
+ 1, 0, 20, 0, tas2781_amp_getvol,
+ tas2781_amp_putvol, amp_vol_tlv),
+ ACARD_SINGLE_RANGE_EXT_TLV("Speaker Digital Gain", TAS2781_DVC_LVL,
+ 0, 0, 200, 1, tas2781_digital_getvol,
+ tas2781_digital_putvol, dvc_tlv),
+ ACARD_SINGLE_BOOL_EXT("Speaker Force Firmware Load", 0,
+ tas2781_force_fwload_get, tas2781_force_fwload_put),
+};
+
+static const struct snd_kcontrol_new tas2781_prof_ctrl = {
+ .name = "Speaker Profile Id",
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = tasdevice_info_profile,
+ .get = tasdevice_get_profile_id,
+ .put = tasdevice_set_profile_id,
+};
+
+static const struct snd_kcontrol_new tas2781_dsp_prog_ctrl = {
+ .name = "Speaker Program Id",
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = tasdevice_info_programs,
+ .get = tasdevice_program_get,
+ .put = tasdevice_program_put,
+};
+
+static const struct snd_kcontrol_new tas2781_dsp_conf_ctrl = {
+ .name = "Speaker Config Id",
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = tasdevice_info_config,
+ .get = tasdevice_config_get,
+ .put = tasdevice_config_put,
+};
+
+static void tas2781_apply_calib(struct tasdevice_priv *tas_priv)
+{
+ static const unsigned char page_array[CALIB_MAX] = {
+ 0x17, 0x18, 0x18, 0x0d, 0x18
+ };
+ static const unsigned char rgno_array[CALIB_MAX] = {
+ 0x74, 0x0c, 0x14, 0x3c, 0x7c
+ };
+ unsigned char *data;
+ int i, j, rc;
+
+ for (i = 0; i < tas_priv->ndev; i++) {
+ data = tas_priv->cali_data.data +
+ i * TASDEVICE_SPEAKER_CALIBRATION_SIZE;
+ for (j = 0; j < CALIB_MAX; j++) {
+ rc = tasdevice_dev_bulk_write(tas_priv, i,
+ TASDEVICE_REG(0, page_array[j], rgno_array[j]),
+ &(data[4 * j]), 4);
+ if (rc < 0)
+ dev_err(tas_priv->dev,
+ "chn %d calib %d bulk_wr err = %d\n",
+ i, j, rc);
+ }
+ }
+}
+
+/* Update the calibrate data, including speaker impedance, f0, etc, into algo.
+ * Calibrate data is done by manufacturer in the factory. These data are used
+ * by Algo for calucating the speaker temperature, speaker membrance excursion
+ * and f0 in real time during playback.
+ */
+static int tas2781_save_calibration(struct tasdevice_priv *tas_priv)
+{
+ efi_guid_t efi_guid = EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d,
+ 0x93, 0xfe, 0x5a, 0xa3, 0x5d, 0xb3);
+ static efi_char16_t efi_name[] = L"CALI_DATA";
+ struct tm *tm = &tas_priv->tm;
+ unsigned int attr, crc;
+ unsigned int *tmp_val;
+ efi_status_t status;
+
+ /* Lenovo devices */
+ if (tas_priv->catlog_id == LENOVO)
+ efi_guid = EFI_GUID(0x1f52d2a1, 0xbb3a, 0x457d, 0xbc, 0x09,
+ 0x43, 0xa3, 0xf4, 0x31, 0x0a, 0x92);
+
+ tas_priv->cali_data.total_sz = 0;
+ /* Get real size of UEFI variable */
+ status = efi.get_variable(efi_name, &efi_guid, &attr,
+ &tas_priv->cali_data.total_sz, tas_priv->cali_data.data);
+ if (status == EFI_BUFFER_TOO_SMALL) {
+ /* Allocate data buffer of data_size bytes */
+ tas_priv->cali_data.data = devm_kzalloc(tas_priv->dev,
+ tas_priv->cali_data.total_sz, GFP_KERNEL);
+ if (!tas_priv->cali_data.data)
+ return -ENOMEM;
+ /* Get variable contents into buffer */
+ status = efi.get_variable(efi_name, &efi_guid, &attr,
+ &tas_priv->cali_data.total_sz,
+ tas_priv->cali_data.data);
+ if (status != EFI_SUCCESS)
+ return -EINVAL;
+ }
+
+ tmp_val = (unsigned int *)tas_priv->cali_data.data;
+
+ crc = crc32(~0, tas_priv->cali_data.data, 84) ^ ~0;
+ dev_dbg(tas_priv->dev, "cali crc 0x%08x PK tmp_val 0x%08x\n",
+ crc, tmp_val[21]);
+
+ if (crc == tmp_val[21]) {
+ time64_to_tm(tmp_val[20], 0, tm);
+ dev_dbg(tas_priv->dev, "%4ld-%2d-%2d, %2d:%2d:%2d\n",
+ tm->tm_year, tm->tm_mon, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+ tas2781_apply_calib(tas_priv);
+ } else
+ tas_priv->cali_data.total_sz = 0;
+
+ return 0;
+}
+
+static void tasdev_fw_ready(const struct firmware *fmw, void *context)
+{
+ struct tasdevice_priv *tas_priv = context;
+ struct hda_codec *codec = tas_priv->codec;
+ int i, ret;
+
+ pm_runtime_get_sync(tas_priv->dev);
+ mutex_lock(&tas_priv->codec_lock);
+
+ ret = tasdevice_rca_parser(tas_priv, fmw);
+ if (ret)
+ goto out;
+
+ ret = snd_ctl_add(codec->card,
+ snd_ctl_new1(&tas2781_prof_ctrl, tas_priv));
+ if (ret) {
+ dev_err(tas_priv->dev,
+ "Failed to add KControl %s = %d\n",
+ tas2781_prof_ctrl.name, ret);
+ goto out;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(tas2781_snd_controls); i++) {
+ ret = snd_ctl_add(codec->card,
+ snd_ctl_new1(&tas2781_snd_controls[i], tas_priv));
+ if (ret) {
+ dev_err(tas_priv->dev,
+ "Failed to add KControl %s = %d\n",
+ tas2781_snd_controls[i].name, ret);
+ goto out;
+ }
+ }
+
+ tasdevice_dsp_remove(tas_priv);
+
+ tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING;
+ scnprintf(tas_priv->coef_binaryname, 64, "TAS2XXX%04X.bin",
+ codec->core.subsystem_id & 0xffff);
+ ret = tasdevice_dsp_parser(tas_priv);
+ if (ret) {
+ dev_err(tas_priv->dev, "dspfw load %s error\n",
+ tas_priv->coef_binaryname);
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ goto out;
+ }
+
+ ret = snd_ctl_add(codec->card,
+ snd_ctl_new1(&tas2781_dsp_prog_ctrl, tas_priv));
+ if (ret) {
+ dev_err(tas_priv->dev,
+ "Failed to add KControl %s = %d\n",
+ tas2781_dsp_prog_ctrl.name, ret);
+ goto out;
+ }
+
+ ret = snd_ctl_add(codec->card,
+ snd_ctl_new1(&tas2781_dsp_conf_ctrl, tas_priv));
+ if (ret) {
+ dev_err(tas_priv->dev,
+ "Failed to add KControl %s = %d\n",
+ tas2781_dsp_conf_ctrl.name, ret);
+ goto out;
+ }
+
+ tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK;
+ tasdevice_prmg_load(tas_priv, 0);
+
+ /* If calibrated data occurs error, dsp will still works with default
+ * calibrated data inside algo.
+ */
+ tas2781_save_calibration(tas_priv);
+
+out:
+ if (tas_priv->fw_state == TASDEVICE_DSP_FW_FAIL) {
+ /*If DSP FW fail, kcontrol won't be created */
+ tasdevice_config_info_remove(tas_priv);
+ tasdevice_dsp_remove(tas_priv);
+ }
+ mutex_unlock(&tas_priv->codec_lock);
+ if (fmw)
+ release_firmware(fmw);
+ pm_runtime_mark_last_busy(tas_priv->dev);
+ pm_runtime_put_autosuspend(tas_priv->dev);
+}
+
+static int tas2781_hda_bind(struct device *dev, struct device *master,
+ void *master_data)
+{
+ struct tasdevice_priv *tas_priv = dev_get_drvdata(dev);
+ struct hda_component *comps = master_data;
+ struct hda_codec *codec;
+ unsigned int subid;
+ int ret;
+
+ if (!comps || tas_priv->index < 0 ||
+ tas_priv->index >= HDA_MAX_COMPONENTS)
+ return -EINVAL;
+
+ comps = &comps[tas_priv->index];
+ if (comps->dev)
+ return -EBUSY;
+
+ codec = comps->codec;
+ subid = codec->core.subsystem_id >> 16;
+
+ switch (subid) {
+ case 0x17aa:
+ tas_priv->catlog_id = LENOVO;
+ break;
+ default:
+ tas_priv->catlog_id = OTHERS;
+ break;
+ }
+
+ pm_runtime_get_sync(dev);
+
+ comps->dev = dev;
+
+ strscpy(comps->name, dev_name(dev), sizeof(comps->name));
+
+ ret = tascodec_init(tas_priv, codec, tasdev_fw_ready);
+ if (!ret)
+ comps->playback_hook = tas2781_hda_playback_hook;
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
+static void tas2781_hda_unbind(struct device *dev,
+ struct device *master, void *master_data)
+{
+ struct tasdevice_priv *tas_priv = dev_get_drvdata(dev);
+ struct hda_component *comps = master_data;
+
+ if (comps[tas_priv->index].dev == dev)
+ memset(&comps[tas_priv->index], 0, sizeof(*comps));
+
+ tasdevice_config_info_remove(tas_priv);
+ tasdevice_dsp_remove(tas_priv);
+
+ tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING;
+}
+
+static const struct component_ops tas2781_hda_comp_ops = {
+ .bind = tas2781_hda_bind,
+ .unbind = tas2781_hda_unbind,
+};
+
+static void tas2781_hda_remove(struct device *dev)
+{
+ struct tasdevice_priv *tas_priv = dev_get_drvdata(dev);
+
+ pm_runtime_get_sync(tas_priv->dev);
+ pm_runtime_disable(tas_priv->dev);
+
+ component_del(tas_priv->dev, &tas2781_hda_comp_ops);
+
+ pm_runtime_put_noidle(tas_priv->dev);
+
+ tasdevice_remove(tas_priv);
+}
+
+static int tas2781_hda_i2c_probe(struct i2c_client *clt)
+{
+ struct tasdevice_priv *tas_priv;
+ const char *device_name;
+ int ret;
+
+ if (strstr(dev_name(&clt->dev), "TIAS2781"))
+ device_name = "TIAS2781";
+ else
+ return -ENODEV;
+
+ tas_priv = tasdevice_kzalloc(clt);
+ if (!tas_priv)
+ return -ENOMEM;
+
+ tas_priv->irq_info.irq = clt->irq;
+ ret = tas2781_read_acpi(tas_priv, device_name);
+ if (ret)
+ return dev_err_probe(tas_priv->dev, ret,
+ "Platform not supported\n");
+
+ ret = tasdevice_init(tas_priv);
+ if (ret)
+ goto err;
+
+ pm_runtime_set_autosuspend_delay(tas_priv->dev, 3000);
+ pm_runtime_use_autosuspend(tas_priv->dev);
+ pm_runtime_mark_last_busy(tas_priv->dev);
+ pm_runtime_set_active(tas_priv->dev);
+ pm_runtime_get_noresume(tas_priv->dev);
+ pm_runtime_enable(tas_priv->dev);
+
+ pm_runtime_put_autosuspend(tas_priv->dev);
+
+ ret = component_add(tas_priv->dev, &tas2781_hda_comp_ops);
+ if (ret) {
+ dev_err(tas_priv->dev, "Register component failed: %d\n", ret);
+ pm_runtime_disable(tas_priv->dev);
+ goto err;
+ }
+
+ tas2781_reset(tas_priv);
+err:
+ if (ret)
+ tas2781_hda_remove(&clt->dev);
+ return ret;
+}
+
+static void tas2781_hda_i2c_remove(struct i2c_client *clt)
+{
+ tas2781_hda_remove(&clt->dev);
+}
+
+static int tas2781_runtime_suspend(struct device *dev)
+{
+ struct tasdevice_priv *tas_priv = dev_get_drvdata(dev);
+ int i;
+
+ dev_dbg(tas_priv->dev, "Runtime Suspend\n");
+
+ mutex_lock(&tas_priv->codec_lock);
+
+ if (tas_priv->playback_started) {
+ tasdevice_tuning_switch(tas_priv, 1);
+ tas_priv->playback_started = false;
+ }
+
+ for (i = 0; i < tas_priv->ndev; i++) {
+ tas_priv->tasdevice[i].cur_book = -1;
+ tas_priv->tasdevice[i].cur_prog = -1;
+ tas_priv->tasdevice[i].cur_conf = -1;
+ }
+
+ regcache_cache_only(tas_priv->regmap, true);
+ regcache_mark_dirty(tas_priv->regmap);
+
+ mutex_unlock(&tas_priv->codec_lock);
+
+ return 0;
+}
+
+static int tas2781_runtime_resume(struct device *dev)
+{
+ struct tasdevice_priv *tas_priv = dev_get_drvdata(dev);
+ unsigned long calib_data_sz =
+ tas_priv->ndev * TASDEVICE_SPEAKER_CALIBRATION_SIZE;
+ int ret;
+
+ dev_dbg(tas_priv->dev, "Runtime Resume\n");
+
+ mutex_lock(&tas_priv->codec_lock);
+
+ regcache_cache_only(tas_priv->regmap, false);
+ ret = regcache_sync(tas_priv->regmap);
+ if (ret) {
+ dev_err(tas_priv->dev,
+ "Failed to restore register cache: %d\n", ret);
+ goto out;
+ }
+
+ tasdevice_prmg_load(tas_priv, tas_priv->cur_prog);
+
+ /* If calibrated data occurs error, dsp will still works with default
+ * calibrated data inside algo.
+ */
+ if (tas_priv->cali_data.total_sz > calib_data_sz)
+ tas2781_apply_calib(tas_priv);
+
+out:
+ mutex_unlock(&tas_priv->codec_lock);
+
+ return ret;
+}
+
+static int tas2781_system_suspend(struct device *dev)
+{
+ struct tasdevice_priv *tas_priv = dev_get_drvdata(dev);
+ int ret;
+
+ dev_dbg(tas_priv->dev, "System Suspend\n");
+
+ ret = pm_runtime_force_suspend(dev);
+ if (ret)
+ return ret;
+
+ /* Shutdown chip before system suspend */
+ regcache_cache_only(tas_priv->regmap, false);
+ tasdevice_tuning_switch(tas_priv, 1);
+ regcache_cache_only(tas_priv->regmap, true);
+ regcache_mark_dirty(tas_priv->regmap);
+
+ /*
+ * Reset GPIO may be shared, so cannot reset here.
+ * However beyond this point, amps may be powered down.
+ */
+ return 0;
+}
+
+static int tas2781_system_resume(struct device *dev)
+{
+ struct tasdevice_priv *tas_priv = dev_get_drvdata(dev);
+ unsigned long calib_data_sz =
+ tas_priv->ndev * TASDEVICE_SPEAKER_CALIBRATION_SIZE;
+ int i, ret;
+
+ dev_dbg(tas_priv->dev, "System Resume\n");
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret)
+ return ret;
+
+ mutex_lock(&tas_priv->codec_lock);
+
+ for (i = 0; i < tas_priv->ndev; i++) {
+ tas_priv->tasdevice[i].cur_book = -1;
+ tas_priv->tasdevice[i].cur_prog = -1;
+ tas_priv->tasdevice[i].cur_conf = -1;
+ }
+ tas2781_reset(tas_priv);
+ tasdevice_prmg_load(tas_priv, tas_priv->cur_prog);
+
+ /* If calibrated data occurs error, dsp will still work with default
+ * calibrated data inside algo.
+ */
+ if (tas_priv->cali_data.total_sz > calib_data_sz)
+ tas2781_apply_calib(tas_priv);
+ mutex_unlock(&tas_priv->codec_lock);
+
+ return 0;
+}
+
+static const struct dev_pm_ops tas2781_hda_pm_ops = {
+ RUNTIME_PM_OPS(tas2781_runtime_suspend, tas2781_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(tas2781_system_suspend, tas2781_system_resume)
+};
+
+static const struct i2c_device_id tas2781_hda_i2c_id[] = {
+ { "tas2781-hda", 0 },
+ {}
+};
+
+static const struct acpi_device_id tas2781_acpi_hda_match[] = {
+ {"TIAS2781", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, tas2781_acpi_hda_match);
+
+static struct i2c_driver tas2781_hda_i2c_driver = {
+ .driver = {
+ .name = "tas2781-hda",
+ .acpi_match_table = tas2781_acpi_hda_match,
+ .pm = &tas2781_hda_pm_ops,
+ },
+ .id_table = tas2781_hda_i2c_id,
+ .probe = tas2781_hda_i2c_probe,
+ .remove = tas2781_hda_i2c_remove,
+};
+module_i2c_driver(tas2781_hda_i2c_driver);
+
+MODULE_DESCRIPTION("TAS2781 HDA Driver");
+MODULE_AUTHOR("Shenghao Ding, TI, <shenghao-ding@ti.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(SND_SOC_TAS2781_FMWLIB);
diff --git a/sound/pci/ice1712/juli.c b/sound/pci/ice1712/juli.c
index f0f8324b08b6..d679842ae1bd 100644
--- a/sound/pci/ice1712/juli.c
+++ b/sound/pci/ice1712/juli.c
@@ -408,30 +408,6 @@ static const char * const follower_vols[] = {
static
DECLARE_TLV_DB_SCALE(juli_master_db_scale, -6350, 50, 1);
-static struct snd_kcontrol *ctl_find(struct snd_card *card,
- const char *name)
-{
- struct snd_ctl_elem_id sid = {0};
-
- strscpy(sid.name, name, sizeof(sid.name));
- sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- return snd_ctl_find_id(card, &sid);
-}
-
-static void add_followers(struct snd_card *card,
- struct snd_kcontrol *master,
- const char * const *list)
-{
- for (; *list; list++) {
- struct snd_kcontrol *follower = ctl_find(card, *list);
- /* dev_dbg(card->dev, "add_followers - %s\n", *list); */
- if (follower) {
- /* dev_dbg(card->dev, "follower %s found\n", *list); */
- snd_ctl_add_follower(master, follower);
- }
- }
-}
-
static int juli_add_controls(struct snd_ice1712 *ice)
{
struct juli_spec *spec = ice->spec;
@@ -454,10 +430,12 @@ static int juli_add_controls(struct snd_ice1712 *ice)
juli_master_db_scale);
if (!vmaster)
return -ENOMEM;
- add_followers(ice->card, vmaster, follower_vols);
err = snd_ctl_add(ice->card, vmaster);
if (err < 0)
return err;
+ err = snd_ctl_add_followers(ice->card, vmaster, follower_vols);
+ if (err < 0)
+ return err;
/* only capture SPDIF over AK4114 */
return snd_ak4114_build(spec->ak4114, NULL,
diff --git a/sound/pci/ice1712/psc724.c b/sound/pci/ice1712/psc724.c
index 82cf365cda10..0818e42c94ca 100644
--- a/sound/pci/ice1712/psc724.c
+++ b/sound/pci/ice1712/psc724.c
@@ -177,7 +177,6 @@ static bool psc724_get_master_switch(struct snd_ice1712 *ice)
static void psc724_set_jack_state(struct snd_ice1712 *ice, bool hp_connected)
{
struct psc724_spec *spec = ice->spec;
- struct snd_ctl_elem_id elem_id;
struct snd_kcontrol *kctl;
u16 power = spec->wm8776.regs[WM8776_REG_PWRDOWN] & ~WM8776_PWR_HPPD;
@@ -187,17 +186,15 @@ static void psc724_set_jack_state(struct snd_ice1712 *ice, bool hp_connected)
snd_wm8776_set_power(&spec->wm8776, power);
spec->hp_connected = hp_connected;
/* notify about master speaker mute change */
- memset(&elem_id, 0, sizeof(elem_id));
- elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strscpy(elem_id.name, "Master Speakers Playback Switch",
- sizeof(elem_id.name));
- kctl = snd_ctl_find_id(ice->card, &elem_id);
- snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
+ kctl = snd_ctl_find_id_mixer(ice->card,
+ "Master Speakers Playback Switch");
+ if (kctl)
+ snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
/* and headphone mute change */
- strscpy(elem_id.name, spec->wm8776.ctl[WM8776_CTL_HP_SW].name,
- sizeof(elem_id.name));
- kctl = snd_ctl_find_id(ice->card, &elem_id);
- snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
+ kctl = snd_ctl_find_id_mixer(ice->card,
+ spec->wm8776.ctl[WM8776_CTL_HP_SW].name);
+ if (kctl)
+ snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
}
static void psc724_update_hp_jack_state(struct work_struct *work)
diff --git a/sound/pci/ice1712/quartet.c b/sound/pci/ice1712/quartet.c
index 20b3e8f94719..f61ee9f5c754 100644
--- a/sound/pci/ice1712/quartet.c
+++ b/sound/pci/ice1712/quartet.c
@@ -766,26 +766,6 @@ static const char * const follower_vols[] = {
static
DECLARE_TLV_DB_SCALE(qtet_master_db_scale, -6350, 50, 1);
-static struct snd_kcontrol *ctl_find(struct snd_card *card,
- const char *name)
-{
- struct snd_ctl_elem_id sid = {0};
-
- strscpy(sid.name, name, sizeof(sid.name));
- sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- return snd_ctl_find_id(card, &sid);
-}
-
-static void add_followers(struct snd_card *card,
- struct snd_kcontrol *master, const char * const *list)
-{
- for (; *list; list++) {
- struct snd_kcontrol *follower = ctl_find(card, *list);
- if (follower)
- snd_ctl_add_follower(master, follower);
- }
-}
-
static int qtet_add_controls(struct snd_ice1712 *ice)
{
struct qtet_spec *spec = ice->spec;
@@ -806,10 +786,12 @@ static int qtet_add_controls(struct snd_ice1712 *ice)
qtet_master_db_scale);
if (!vmaster)
return -ENOMEM;
- add_followers(ice->card, vmaster, follower_vols);
err = snd_ctl_add(ice->card, vmaster);
if (err < 0)
return err;
+ err = snd_ctl_add_followers(ice->card, vmaster, follower_vols);
+ if (err < 0)
+ return err;
/* only capture SPDIF over AK4113 */
return snd_ak4113_build(spec->ak4113,
ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
diff --git a/sound/pci/ice1712/wm8776.c b/sound/pci/ice1712/wm8776.c
index 6eda86119dff..493425697bb4 100644
--- a/sound/pci/ice1712/wm8776.c
+++ b/sound/pci/ice1712/wm8776.c
@@ -34,13 +34,9 @@ static void snd_wm8776_activate_ctl(struct snd_wm8776 *wm,
struct snd_card *card = wm->card;
struct snd_kcontrol *kctl;
struct snd_kcontrol_volatile *vd;
- struct snd_ctl_elem_id elem_id;
unsigned int index_offset;
- memset(&elem_id, 0, sizeof(elem_id));
- strscpy(elem_id.name, ctl_name, sizeof(elem_id.name));
- elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- kctl = snd_ctl_find_id(card, &elem_id);
+ kctl = snd_ctl_find_id_mixer(card, ctl_name);
if (!kctl)
return;
index_offset = snd_ctl_get_ioff(kctl, &kctl->id);
diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c
index 33b4f95d65b3..5c2cac201a28 100644
--- a/sound/pci/korg1212/korg1212.c
+++ b/sound/pci/korg1212/korg1212.c
@@ -1285,8 +1285,7 @@ static int snd_korg1212_silence(struct snd_korg1212 *korg1212, int pos, int coun
}
static int snd_korg1212_copy_to(struct snd_pcm_substream *substream,
- void __user *dst, int pos, int count,
- bool in_kernel)
+ struct iov_iter *dst, int pos, int count)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream);
@@ -1306,24 +1305,20 @@ static int snd_korg1212_copy_to(struct snd_pcm_substream *substream,
#if K1212_DEBUG_LEVEL > 0
if ( (void *) src < (void *) korg1212->recordDataBufsPtr ||
(void *) src > (void *) korg1212->recordDataBufsPtr[8].bufferData ) {
- printk(KERN_DEBUG "K1212_DEBUG: snd_korg1212_copy_to KERNEL EFAULT, src=%p dst=%p iter=%d\n", src, dst, i);
+ printk(KERN_DEBUG "K1212_DEBUG: snd_korg1212_copy_to KERNEL EFAULT, src=%p dst=%p iter=%d\n", src, dst->kvec.iov_base, i);
return -EFAULT;
}
#endif
- if (in_kernel)
- memcpy((__force void *)dst, src, size);
- else if (copy_to_user(dst, src, size))
+ if (copy_to_iter(src, size, dst) != size)
return -EFAULT;
src++;
- dst += size;
}
return 0;
}
static int snd_korg1212_copy_from(struct snd_pcm_substream *substream,
- void __user *src, int pos, int count,
- bool in_kernel)
+ struct iov_iter *src, int pos, int count)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream);
@@ -1345,16 +1340,13 @@ static int snd_korg1212_copy_from(struct snd_pcm_substream *substream,
#if K1212_DEBUG_LEVEL > 0
if ( (void *) dst < (void *) korg1212->playDataBufsPtr ||
(void *) dst > (void *) korg1212->playDataBufsPtr[8].bufferData ) {
- printk(KERN_DEBUG "K1212_DEBUG: snd_korg1212_copy_from KERNEL EFAULT, src=%p dst=%p iter=%d\n", src, dst, i);
+ printk(KERN_DEBUG "K1212_DEBUG: snd_korg1212_copy_from KERNEL EFAULT, src=%p dst=%p iter=%d\n", src->kvec.iov_base, dst, i);
return -EFAULT;
}
#endif
- if (in_kernel)
- memcpy(dst, (__force void *)src, size);
- else if (copy_from_user(dst, src, size))
+ if (copy_from_iter(dst, size, src) != size)
return -EFAULT;
dst++;
- src += size;
}
return 0;
@@ -1642,17 +1634,9 @@ static snd_pcm_uframes_t snd_korg1212_capture_pointer(struct snd_pcm_substream *
static int snd_korg1212_playback_copy(struct snd_pcm_substream *substream,
int channel, unsigned long pos,
- void __user *src, unsigned long count)
+ struct iov_iter *src, unsigned long count)
{
- return snd_korg1212_copy_from(substream, src, pos, count, false);
-}
-
-static int snd_korg1212_playback_copy_kernel(struct snd_pcm_substream *substream,
- int channel, unsigned long pos,
- void *src, unsigned long count)
-{
- return snd_korg1212_copy_from(substream, (void __user *)src,
- pos, count, true);
+ return snd_korg1212_copy_from(substream, src, pos, count);
}
static int snd_korg1212_playback_silence(struct snd_pcm_substream *substream,
@@ -1670,17 +1654,9 @@ static int snd_korg1212_playback_silence(struct snd_pcm_substream *substream,
static int snd_korg1212_capture_copy(struct snd_pcm_substream *substream,
int channel, unsigned long pos,
- void __user *dst, unsigned long count)
-{
- return snd_korg1212_copy_to(substream, dst, pos, count, false);
-}
-
-static int snd_korg1212_capture_copy_kernel(struct snd_pcm_substream *substream,
- int channel, unsigned long pos,
- void *dst, unsigned long count)
+ struct iov_iter *dst, unsigned long count)
{
- return snd_korg1212_copy_to(substream, (void __user *)dst,
- pos, count, true);
+ return snd_korg1212_copy_to(substream, dst, pos, count);
}
static const struct snd_pcm_ops snd_korg1212_playback_ops = {
@@ -1691,8 +1667,7 @@ static const struct snd_pcm_ops snd_korg1212_playback_ops = {
.prepare = snd_korg1212_prepare,
.trigger = snd_korg1212_trigger,
.pointer = snd_korg1212_playback_pointer,
- .copy_user = snd_korg1212_playback_copy,
- .copy_kernel = snd_korg1212_playback_copy_kernel,
+ .copy = snd_korg1212_playback_copy,
.fill_silence = snd_korg1212_playback_silence,
};
@@ -1704,8 +1679,7 @@ static const struct snd_pcm_ops snd_korg1212_capture_ops = {
.prepare = snd_korg1212_prepare,
.trigger = snd_korg1212_trigger,
.pointer = snd_korg1212_capture_pointer,
- .copy_user = snd_korg1212_capture_copy,
- .copy_kernel = snd_korg1212_capture_copy_kernel,
+ .copy = snd_korg1212_capture_copy,
};
/*
diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c
index 261850775c80..305cbd24a391 100644
--- a/sound/pci/maestro3.c
+++ b/sound/pci/maestro3.c
@@ -2029,9 +2029,6 @@ static int snd_m3_mixer(struct snd_m3 *chip)
{
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
-#ifndef CONFIG_SND_MAESTRO3_INPUT
- struct snd_ctl_elem_id elem_id;
-#endif
int err;
static const struct snd_ac97_bus_ops ops = {
.write = snd_m3_ac97_write,
@@ -2054,14 +2051,10 @@ static int snd_m3_mixer(struct snd_m3 *chip)
snd_ac97_write(chip->ac97, AC97_PCM, 0);
#ifndef CONFIG_SND_MAESTRO3_INPUT
- memset(&elem_id, 0, sizeof(elem_id));
- elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(elem_id.name, "Master Playback Switch");
- chip->master_switch = snd_ctl_find_id(chip->card, &elem_id);
- memset(&elem_id, 0, sizeof(elem_id));
- elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(elem_id.name, "Master Playback Volume");
- chip->master_volume = snd_ctl_find_id(chip->card, &elem_id);
+ chip->master_switch = snd_ctl_find_id_mixer(chip->card,
+ "Master Playback Switch");
+ chip->master_volume = snd_ctl_find_id_mixer(chip->card,
+ "Master Playback Volume");
#endif
return 0;
diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c
index f99a1e96e923..34f90829e656 100644
--- a/sound/pci/nm256/nm256.c
+++ b/sound/pci/nm256/nm256.c
@@ -691,26 +691,12 @@ snd_nm256_playback_silence(struct snd_pcm_substream *substream,
static int
snd_nm256_playback_copy(struct snd_pcm_substream *substream,
int channel, unsigned long pos,
- void __user *src, unsigned long count)
+ struct iov_iter *src, unsigned long count)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct nm256_stream *s = runtime->private_data;
- if (copy_from_user_toio(s->bufptr + pos, src, count))
- return -EFAULT;
- return 0;
-}
-
-static int
-snd_nm256_playback_copy_kernel(struct snd_pcm_substream *substream,
- int channel, unsigned long pos,
- void *src, unsigned long count)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct nm256_stream *s = runtime->private_data;
-
- memcpy_toio(s->bufptr + pos, src, count);
- return 0;
+ return copy_from_iter_toio(s->bufptr + pos, src, count);
}
/*
@@ -719,26 +705,12 @@ snd_nm256_playback_copy_kernel(struct snd_pcm_substream *substream,
static int
snd_nm256_capture_copy(struct snd_pcm_substream *substream,
int channel, unsigned long pos,
- void __user *dst, unsigned long count)
+ struct iov_iter *dst, unsigned long count)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct nm256_stream *s = runtime->private_data;
- if (copy_to_user_fromio(dst, s->bufptr + pos, count))
- return -EFAULT;
- return 0;
-}
-
-static int
-snd_nm256_capture_copy_kernel(struct snd_pcm_substream *substream,
- int channel, unsigned long pos,
- void *dst, unsigned long count)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct nm256_stream *s = runtime->private_data;
-
- memcpy_fromio(dst, s->bufptr + pos, count);
- return 0;
+ return copy_to_iter_fromio(dst, s->bufptr + pos, count);
}
#endif /* !__i386__ */
@@ -909,8 +881,7 @@ static const struct snd_pcm_ops snd_nm256_playback_ops = {
.trigger = snd_nm256_playback_trigger,
.pointer = snd_nm256_playback_pointer,
#ifndef __i386__
- .copy_user = snd_nm256_playback_copy,
- .copy_kernel = snd_nm256_playback_copy_kernel,
+ .copy = snd_nm256_playback_copy,
.fill_silence = snd_nm256_playback_silence,
#endif
.mmap = snd_pcm_lib_mmap_iomem,
@@ -924,8 +895,7 @@ static const struct snd_pcm_ops snd_nm256_capture_ops = {
.trigger = snd_nm256_capture_trigger,
.pointer = snd_nm256_capture_pointer,
#ifndef __i386__
- .copy_user = snd_nm256_capture_copy,
- .copy_kernel = snd_nm256_capture_copy_kernel,
+ .copy = snd_nm256_capture_copy,
#endif
.mmap = snd_pcm_lib_mmap_iomem,
};
diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c
index 9c0ac025e143..02144bbee6d5 100644
--- a/sound/pci/rme32.c
+++ b/sound/pci/rme32.c
@@ -252,48 +252,24 @@ static int snd_rme32_playback_silence(struct snd_pcm_substream *substream,
/* copy callback for halfduplex mode */
static int snd_rme32_playback_copy(struct snd_pcm_substream *substream,
int channel, unsigned long pos,
- void __user *src, unsigned long count)
+ struct iov_iter *src, unsigned long count)
{
struct rme32 *rme32 = snd_pcm_substream_chip(substream);
- if (copy_from_user_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos,
- src, count))
- return -EFAULT;
- return 0;
-}
-
-static int snd_rme32_playback_copy_kernel(struct snd_pcm_substream *substream,
- int channel, unsigned long pos,
- void *src, unsigned long count)
-{
- struct rme32 *rme32 = snd_pcm_substream_chip(substream);
-
- memcpy_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos, src, count);
- return 0;
+ return copy_from_iter_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos,
+ src, count);
}
/* copy callback for halfduplex mode */
static int snd_rme32_capture_copy(struct snd_pcm_substream *substream,
int channel, unsigned long pos,
- void __user *dst, unsigned long count)
+ struct iov_iter *dst, unsigned long count)
{
struct rme32 *rme32 = snd_pcm_substream_chip(substream);
- if (copy_to_user_fromio(dst,
- rme32->iobase + RME32_IO_DATA_BUFFER + pos,
- count))
- return -EFAULT;
- return 0;
-}
-
-static int snd_rme32_capture_copy_kernel(struct snd_pcm_substream *substream,
- int channel, unsigned long pos,
- void *dst, unsigned long count)
-{
- struct rme32 *rme32 = snd_pcm_substream_chip(substream);
-
- memcpy_fromio(dst, rme32->iobase + RME32_IO_DATA_BUFFER + pos, count);
- return 0;
+ return copy_to_iter_fromio(dst,
+ rme32->iobase + RME32_IO_DATA_BUFFER + pos,
+ count);
}
/*
@@ -1194,8 +1170,7 @@ static const struct snd_pcm_ops snd_rme32_playback_spdif_ops = {
.prepare = snd_rme32_playback_prepare,
.trigger = snd_rme32_pcm_trigger,
.pointer = snd_rme32_playback_pointer,
- .copy_user = snd_rme32_playback_copy,
- .copy_kernel = snd_rme32_playback_copy_kernel,
+ .copy = snd_rme32_playback_copy,
.fill_silence = snd_rme32_playback_silence,
.mmap = snd_pcm_lib_mmap_iomem,
};
@@ -1207,8 +1182,7 @@ static const struct snd_pcm_ops snd_rme32_capture_spdif_ops = {
.prepare = snd_rme32_capture_prepare,
.trigger = snd_rme32_pcm_trigger,
.pointer = snd_rme32_capture_pointer,
- .copy_user = snd_rme32_capture_copy,
- .copy_kernel = snd_rme32_capture_copy_kernel,
+ .copy = snd_rme32_capture_copy,
.mmap = snd_pcm_lib_mmap_iomem,
};
@@ -1219,8 +1193,7 @@ static const struct snd_pcm_ops snd_rme32_playback_adat_ops = {
.prepare = snd_rme32_playback_prepare,
.trigger = snd_rme32_pcm_trigger,
.pointer = snd_rme32_playback_pointer,
- .copy_user = snd_rme32_playback_copy,
- .copy_kernel = snd_rme32_playback_copy_kernel,
+ .copy = snd_rme32_playback_copy,
.fill_silence = snd_rme32_playback_silence,
.mmap = snd_pcm_lib_mmap_iomem,
};
@@ -1232,8 +1205,7 @@ static const struct snd_pcm_ops snd_rme32_capture_adat_ops = {
.prepare = snd_rme32_capture_prepare,
.trigger = snd_rme32_pcm_trigger,
.pointer = snd_rme32_capture_pointer,
- .copy_user = snd_rme32_capture_copy,
- .copy_kernel = snd_rme32_capture_copy_kernel,
+ .copy = snd_rme32_capture_copy,
.mmap = snd_pcm_lib_mmap_iomem,
};
diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c
index bccb7e0d3d11..6b5ffb18197b 100644
--- a/sound/pci/rme96.c
+++ b/sound/pci/rme96.c
@@ -320,48 +320,26 @@ snd_rme96_playback_silence(struct snd_pcm_substream *substream,
static int
snd_rme96_playback_copy(struct snd_pcm_substream *substream,
int channel, unsigned long pos,
- void __user *src, unsigned long count)
+ struct iov_iter *src, unsigned long count)
{
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
- return copy_from_user_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + pos,
+ return copy_from_iter_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + pos,
src, count);
}
static int
-snd_rme96_playback_copy_kernel(struct snd_pcm_substream *substream,
- int channel, unsigned long pos,
- void *src, unsigned long count)
-{
- struct rme96 *rme96 = snd_pcm_substream_chip(substream);
-
- memcpy_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, src, count);
- return 0;
-}
-
-static int
snd_rme96_capture_copy(struct snd_pcm_substream *substream,
int channel, unsigned long pos,
- void __user *dst, unsigned long count)
+ struct iov_iter *dst, unsigned long count)
{
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
- return copy_to_user_fromio(dst,
+ return copy_to_iter_fromio(dst,
rme96->iobase + RME96_IO_REC_BUFFER + pos,
count);
}
-static int
-snd_rme96_capture_copy_kernel(struct snd_pcm_substream *substream,
- int channel, unsigned long pos,
- void *dst, unsigned long count)
-{
- struct rme96 *rme96 = snd_pcm_substream_chip(substream);
-
- memcpy_fromio(dst, rme96->iobase + RME96_IO_REC_BUFFER + pos, count);
- return 0;
-}
-
/*
* Digital output capabilities (S/PDIF)
*/
@@ -1518,8 +1496,7 @@ static const struct snd_pcm_ops snd_rme96_playback_spdif_ops = {
.prepare = snd_rme96_playback_prepare,
.trigger = snd_rme96_playback_trigger,
.pointer = snd_rme96_playback_pointer,
- .copy_user = snd_rme96_playback_copy,
- .copy_kernel = snd_rme96_playback_copy_kernel,
+ .copy = snd_rme96_playback_copy,
.fill_silence = snd_rme96_playback_silence,
.mmap = snd_pcm_lib_mmap_iomem,
};
@@ -1531,8 +1508,7 @@ static const struct snd_pcm_ops snd_rme96_capture_spdif_ops = {
.prepare = snd_rme96_capture_prepare,
.trigger = snd_rme96_capture_trigger,
.pointer = snd_rme96_capture_pointer,
- .copy_user = snd_rme96_capture_copy,
- .copy_kernel = snd_rme96_capture_copy_kernel,
+ .copy = snd_rme96_capture_copy,
.mmap = snd_pcm_lib_mmap_iomem,
};
@@ -1543,8 +1519,7 @@ static const struct snd_pcm_ops snd_rme96_playback_adat_ops = {
.prepare = snd_rme96_playback_prepare,
.trigger = snd_rme96_playback_trigger,
.pointer = snd_rme96_playback_pointer,
- .copy_user = snd_rme96_playback_copy,
- .copy_kernel = snd_rme96_playback_copy_kernel,
+ .copy = snd_rme96_playback_copy,
.fill_silence = snd_rme96_playback_silence,
.mmap = snd_pcm_lib_mmap_iomem,
};
@@ -1556,8 +1531,7 @@ static const struct snd_pcm_ops snd_rme96_capture_adat_ops = {
.prepare = snd_rme96_capture_prepare,
.trigger = snd_rme96_capture_trigger,
.pointer = snd_rme96_capture_pointer,
- .copy_user = snd_rme96_capture_copy,
- .copy_kernel = snd_rme96_capture_copy_kernel,
+ .copy = snd_rme96_capture_copy,
.mmap = snd_pcm_lib_mmap_iomem,
};
diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c
index 65add92c88aa..e7d1b43471a2 100644
--- a/sound/pci/rme9652/hdsp.c
+++ b/sound/pci/rme9652/hdsp.c
@@ -3961,7 +3961,7 @@ static signed char *hdsp_channel_buffer_location(struct hdsp *hdsp,
static int snd_hdsp_playback_copy(struct snd_pcm_substream *substream,
int channel, unsigned long pos,
- void __user *src, unsigned long count)
+ struct iov_iter *src, unsigned long count)
{
struct hdsp *hdsp = snd_pcm_substream_chip(substream);
signed char *channel_buf;
@@ -3972,28 +3972,14 @@ static int snd_hdsp_playback_copy(struct snd_pcm_substream *substream,
channel_buf = hdsp_channel_buffer_location (hdsp, substream->pstr->stream, channel);
if (snd_BUG_ON(!channel_buf))
return -EIO;
- if (copy_from_user(channel_buf + pos, src, count))
+ if (copy_from_iter(channel_buf + pos, count, src) != count)
return -EFAULT;
return 0;
}
-static int snd_hdsp_playback_copy_kernel(struct snd_pcm_substream *substream,
- int channel, unsigned long pos,
- void *src, unsigned long count)
-{
- struct hdsp *hdsp = snd_pcm_substream_chip(substream);
- signed char *channel_buf;
-
- channel_buf = hdsp_channel_buffer_location(hdsp, substream->pstr->stream, channel);
- if (snd_BUG_ON(!channel_buf))
- return -EIO;
- memcpy(channel_buf + pos, src, count);
- return 0;
-}
-
static int snd_hdsp_capture_copy(struct snd_pcm_substream *substream,
int channel, unsigned long pos,
- void __user *dst, unsigned long count)
+ struct iov_iter *dst, unsigned long count)
{
struct hdsp *hdsp = snd_pcm_substream_chip(substream);
signed char *channel_buf;
@@ -4004,25 +3990,11 @@ static int snd_hdsp_capture_copy(struct snd_pcm_substream *substream,
channel_buf = hdsp_channel_buffer_location (hdsp, substream->pstr->stream, channel);
if (snd_BUG_ON(!channel_buf))
return -EIO;
- if (copy_to_user(dst, channel_buf + pos, count))
+ if (copy_to_iter(channel_buf + pos, count, dst) != count)
return -EFAULT;
return 0;
}
-static int snd_hdsp_capture_copy_kernel(struct snd_pcm_substream *substream,
- int channel, unsigned long pos,
- void *dst, unsigned long count)
-{
- struct hdsp *hdsp = snd_pcm_substream_chip(substream);
- signed char *channel_buf;
-
- channel_buf = hdsp_channel_buffer_location(hdsp, substream->pstr->stream, channel);
- if (snd_BUG_ON(!channel_buf))
- return -EIO;
- memcpy(dst, channel_buf + pos, count);
- return 0;
-}
-
static int snd_hdsp_hw_silence(struct snd_pcm_substream *substream,
int channel, unsigned long pos,
unsigned long count)
@@ -4950,8 +4922,7 @@ static const struct snd_pcm_ops snd_hdsp_playback_ops = {
.prepare = snd_hdsp_prepare,
.trigger = snd_hdsp_trigger,
.pointer = snd_hdsp_hw_pointer,
- .copy_user = snd_hdsp_playback_copy,
- .copy_kernel = snd_hdsp_playback_copy_kernel,
+ .copy = snd_hdsp_playback_copy,
.fill_silence = snd_hdsp_hw_silence,
};
@@ -4963,8 +4934,7 @@ static const struct snd_pcm_ops snd_hdsp_capture_ops = {
.prepare = snd_hdsp_prepare,
.trigger = snd_hdsp_trigger,
.pointer = snd_hdsp_hw_pointer,
- .copy_user = snd_hdsp_capture_copy,
- .copy_kernel = snd_hdsp_capture_copy_kernel,
+ .copy = snd_hdsp_capture_copy,
};
static int snd_hdsp_create_hwdep(struct snd_card *card, struct hdsp *hdsp)
diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c
index e7c320afefe8..d066c70ae160 100644
--- a/sound/pci/rme9652/rme9652.c
+++ b/sound/pci/rme9652/rme9652.c
@@ -1844,7 +1844,7 @@ static signed char *rme9652_channel_buffer_location(struct snd_rme9652 *rme9652,
static int snd_rme9652_playback_copy(struct snd_pcm_substream *substream,
int channel, unsigned long pos,
- void __user *src, unsigned long count)
+ struct iov_iter *src, unsigned long count)
{
struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream);
signed char *channel_buf;
@@ -1857,30 +1857,14 @@ static int snd_rme9652_playback_copy(struct snd_pcm_substream *substream,
channel);
if (snd_BUG_ON(!channel_buf))
return -EIO;
- if (copy_from_user(channel_buf + pos, src, count))
+ if (copy_from_iter(channel_buf + pos, count, src) != count)
return -EFAULT;
return 0;
}
-static int snd_rme9652_playback_copy_kernel(struct snd_pcm_substream *substream,
- int channel, unsigned long pos,
- void *src, unsigned long count)
-{
- struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream);
- signed char *channel_buf;
-
- channel_buf = rme9652_channel_buffer_location(rme9652,
- substream->pstr->stream,
- channel);
- if (snd_BUG_ON(!channel_buf))
- return -EIO;
- memcpy(channel_buf + pos, src, count);
- return 0;
-}
-
static int snd_rme9652_capture_copy(struct snd_pcm_substream *substream,
int channel, unsigned long pos,
- void __user *dst, unsigned long count)
+ struct iov_iter *dst, unsigned long count)
{
struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream);
signed char *channel_buf;
@@ -1893,27 +1877,11 @@ static int snd_rme9652_capture_copy(struct snd_pcm_substream *substream,
channel);
if (snd_BUG_ON(!channel_buf))
return -EIO;
- if (copy_to_user(dst, channel_buf + pos, count))
+ if (copy_to_iter(channel_buf + pos, count, dst) != count)
return -EFAULT;
return 0;
}
-static int snd_rme9652_capture_copy_kernel(struct snd_pcm_substream *substream,
- int channel, unsigned long pos,
- void *dst, unsigned long count)
-{
- struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream);
- signed char *channel_buf;
-
- channel_buf = rme9652_channel_buffer_location(rme9652,
- substream->pstr->stream,
- channel);
- if (snd_BUG_ON(!channel_buf))
- return -EIO;
- memcpy(dst, channel_buf + pos, count);
- return 0;
-}
-
static int snd_rme9652_hw_silence(struct snd_pcm_substream *substream,
int channel, unsigned long pos,
unsigned long count)
@@ -2370,8 +2338,7 @@ static const struct snd_pcm_ops snd_rme9652_playback_ops = {
.prepare = snd_rme9652_prepare,
.trigger = snd_rme9652_trigger,
.pointer = snd_rme9652_hw_pointer,
- .copy_user = snd_rme9652_playback_copy,
- .copy_kernel = snd_rme9652_playback_copy_kernel,
+ .copy = snd_rme9652_playback_copy,
.fill_silence = snd_rme9652_hw_silence,
};
@@ -2383,8 +2350,7 @@ static const struct snd_pcm_ops snd_rme9652_capture_ops = {
.prepare = snd_rme9652_prepare,
.trigger = snd_rme9652_trigger,
.pointer = snd_rme9652_hw_pointer,
- .copy_user = snd_rme9652_capture_copy,
- .copy_kernel = snd_rme9652_capture_copy_kernel,
+ .copy = snd_rme9652_capture_copy,
};
static int snd_rme9652_create_pcm(struct snd_card *card,
diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c
index 361b83fd721e..d8666ff7bdfa 100644
--- a/sound/pci/via82xx.c
+++ b/sound/pci/via82xx.c
@@ -1984,11 +1984,7 @@ static int snd_via8233_init_misc(struct via82xx *chip)
/* when no h/w PCM volume control is found, use DXS volume control
* as the PCM vol control
*/
- struct snd_ctl_elem_id sid;
- memset(&sid, 0, sizeof(sid));
- strcpy(sid.name, "PCM Playback Volume");
- sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- if (! snd_ctl_find_id(chip->card, &sid)) {
+ if (!snd_ctl_find_id_mixer(chip->card, "PCM Playback Volume")) {
dev_info(chip->card->dev,
"Using DXS as PCM Playback\n");
err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_via8233_pcmdxs_volume_control, chip));
diff --git a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c
index b033bd290940..48444dda44de 100644
--- a/sound/pci/ymfpci/ymfpci.c
+++ b/sound/pci/ymfpci/ymfpci.c
@@ -152,8 +152,8 @@ static inline int snd_ymfpci_create_gameport(struct snd_ymfpci *chip, int dev, i
void snd_ymfpci_free_gameport(struct snd_ymfpci *chip) { }
#endif /* SUPPORT_JOYSTICK */
-static int snd_card_ymfpci_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_card_ymfpci_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -348,6 +348,12 @@ static int snd_card_ymfpci_probe(struct pci_dev *pci,
return 0;
}
+static int snd_card_ymfpci_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ return snd_card_free_on_error(&pci->dev, __snd_card_ymfpci_probe(pci, pci_id));
+}
+
static struct pci_driver ymfpci_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_ymfpci_ids,
diff --git a/sound/ppc/awacs.c b/sound/ppc/awacs.c
index 53d558b2806c..659866cfe3b4 100644
--- a/sound/ppc/awacs.c
+++ b/sound/ppc/awacs.c
@@ -11,6 +11,7 @@
#include <asm/nvram.h>
#include <linux/init.h>
#include <linux/delay.h>
+#include <linux/of.h>
#include <linux/slab.h>
#include <sound/core.h>
#include "pmac.h"
diff --git a/sound/ppc/burgundy.c b/sound/ppc/burgundy.c
index 4fb990ab2ceb..400a886562b1 100644
--- a/sound/ppc/burgundy.c
+++ b/sound/ppc/burgundy.c
@@ -9,6 +9,7 @@
#include <linux/io.h>
#include <linux/init.h>
#include <linux/delay.h>
+#include <linux/of.h>
#include <sound/core.h>
#include "pmac.h"
#include "burgundy.h"
diff --git a/sound/sh/sh_dac_audio.c b/sound/sh/sh_dac_audio.c
index 8cf571955c9d..95ba3abd4e47 100644
--- a/sound/sh/sh_dac_audio.c
+++ b/sound/sh/sh_dac_audio.c
@@ -158,12 +158,12 @@ static int snd_sh_dac_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
static int snd_sh_dac_pcm_copy(struct snd_pcm_substream *substream,
int channel, unsigned long pos,
- void __user *src, unsigned long count)
+ struct iov_iter *src, unsigned long count)
{
/* channel is not used (interleaved data) */
struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
- if (copy_from_user_toio(chip->data_buffer + pos, src, count))
+ if (copy_from_iter_toio(chip->data_buffer + pos, src, count))
return -EFAULT;
chip->buffer_end = chip->data_buffer + pos + count;
@@ -175,24 +175,6 @@ static int snd_sh_dac_pcm_copy(struct snd_pcm_substream *substream,
return 0;
}
-static int snd_sh_dac_pcm_copy_kernel(struct snd_pcm_substream *substream,
- int channel, unsigned long pos,
- void *src, unsigned long count)
-{
- /* channel is not used (interleaved data) */
- struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
-
- memcpy_toio(chip->data_buffer + pos, src, count);
- chip->buffer_end = chip->data_buffer + pos + count;
-
- if (chip->empty) {
- chip->empty = 0;
- dac_audio_start_timer(chip);
- }
-
- return 0;
-}
-
static int snd_sh_dac_pcm_silence(struct snd_pcm_substream *substream,
int channel, unsigned long pos,
unsigned long count)
@@ -227,8 +209,7 @@ static const struct snd_pcm_ops snd_sh_dac_pcm_ops = {
.prepare = snd_sh_dac_pcm_prepare,
.trigger = snd_sh_dac_pcm_trigger,
.pointer = snd_sh_dac_pcm_pointer,
- .copy_user = snd_sh_dac_pcm_copy,
- .copy_kernel = snd_sh_dac_pcm_copy_kernel,
+ .copy = snd_sh_dac_pcm_copy,
.fill_silence = snd_sh_dac_pcm_silence,
.mmap = snd_pcm_lib_mmap_iomem,
};
diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c
index a2fe3bd4f9a1..b304b3562c82 100644
--- a/sound/soc/amd/yc/acp6x-mach.c
+++ b/sound/soc/amd/yc/acp6x-mach.c
@@ -217,7 +217,7 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
.driver_data = &acp6x_card,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_NAME, "82"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "82V2"),
}
},
{
@@ -251,6 +251,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
{
.driver_data = &acp6x_card,
.matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "M6500RC"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Alienware"),
DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m17 R5 AMD"),
}
diff --git a/sound/soc/atmel/mchp-pdmc.c b/sound/soc/atmel/mchp-pdmc.c
index 944d78ef2f36..afe213a71212 100644
--- a/sound/soc/atmel/mchp-pdmc.c
+++ b/sound/soc/atmel/mchp-pdmc.c
@@ -386,7 +386,6 @@ static int mchp_pdmc_open(struct snd_soc_component *component,
for (i = 0; i < ARRAY_SIZE(mchp_pdmc_snd_controls); i++) {
const struct snd_kcontrol_new *control = &mchp_pdmc_snd_controls[i];
struct snd_ctl_elem_id id;
- struct snd_kcontrol *kctl;
int err;
if (component->name_prefix)
@@ -400,17 +399,10 @@ static int mchp_pdmc_open(struct snd_soc_component *component,
id.device = control->device;
id.subdevice = control->subdevice;
id.index = control->index;
- kctl = snd_ctl_find_id(component->card->snd_card, &id);
- if (!kctl) {
- dev_err(component->dev, "Failed to find %s\n", control->name);
- continue;
- }
- err = snd_ctl_remove(component->card->snd_card, kctl);
- if (err < 0) {
+ err = snd_ctl_remove_id(component->card->snd_card, &id);
+ if (err < 0)
dev_err(component->dev, "%d: Failed to remove %s\n", err,
control->name);
- continue;
- }
}
return 0;
@@ -962,7 +954,7 @@ static int mchp_pdmc_dt_init(struct mchp_pdmc *dd)
/* used to clean the channel index found on RHR's MSB */
static int mchp_pdmc_process(struct snd_pcm_substream *substream,
int channel, unsigned long hwoff,
- void *buf, unsigned long bytes)
+ struct iov_iter *buf, unsigned long bytes)
{
struct snd_pcm_runtime *runtime = substream->runtime;
u8 *dma_ptr = runtime->dma_area + hwoff +
diff --git a/sound/soc/codecs/cs35l41-lib.c b/sound/soc/codecs/cs35l41-lib.c
index ac7cc492bcb0..4ec306cd2f47 100644
--- a/sound/soc/codecs/cs35l41-lib.c
+++ b/sound/soc/codecs/cs35l41-lib.c
@@ -1080,28 +1080,32 @@ static const struct reg_sequence cs35l41_safe_to_reset[] = {
{ 0x00000040, 0x00000033 },
};
-static const struct reg_sequence cs35l41_active_to_safe[] = {
+static const struct reg_sequence cs35l41_active_to_safe_start[] = {
{ 0x00000040, 0x00000055 },
{ 0x00000040, 0x000000AA },
{ 0x00007438, 0x00585941 },
{ CS35L41_PWR_CTRL1, 0x00000000 },
- { 0x0000742C, 0x00000009, 3000 },
+ { 0x0000742C, 0x00000009 },
+};
+
+static const struct reg_sequence cs35l41_active_to_safe_end[] = {
{ 0x00007438, 0x00580941 },
{ 0x00000040, 0x000000CC },
{ 0x00000040, 0x00000033 },
};
-static const struct reg_sequence cs35l41_safe_to_active[] = {
+static const struct reg_sequence cs35l41_safe_to_active_start[] = {
{ 0x00000040, 0x00000055 },
{ 0x00000040, 0x000000AA },
{ 0x0000742C, 0x0000000F },
{ 0x0000742C, 0x00000079 },
{ 0x00007438, 0x00585941 },
- { CS35L41_PWR_CTRL1, 0x00000001, 3000 }, // GLOBAL_EN = 1
+ { CS35L41_PWR_CTRL1, 0x00000001 }, // GLOBAL_EN = 1
+};
+
+static const struct reg_sequence cs35l41_safe_to_active_en_spk[] = {
{ 0x0000742C, 0x000000F9 },
{ 0x00007438, 0x00580941 },
- { 0x00000040, 0x000000CC },
- { 0x00000040, 0x00000033 },
};
static const struct reg_sequence cs35l41_reset_to_safe[] = {
@@ -1188,11 +1192,12 @@ bool cs35l41_safe_reset(struct regmap *regmap, enum cs35l41_boost_type b_type)
}
EXPORT_SYMBOL_GPL(cs35l41_safe_reset);
-int cs35l41_global_enable(struct regmap *regmap, enum cs35l41_boost_type b_type, int enable,
- struct completion *pll_lock)
+int cs35l41_global_enable(struct device *dev, struct regmap *regmap, enum cs35l41_boost_type b_type,
+ int enable, struct completion *pll_lock, bool firmware_running)
{
int ret;
- unsigned int gpio1_func, pad_control, pwr_ctrl1, pwr_ctrl3;
+ unsigned int gpio1_func, pad_control, pwr_ctrl1, pwr_ctrl3, int_status, pup_pdn_mask;
+ unsigned int pwr_ctl1_val;
struct reg_sequence cs35l41_mdsync_down_seq[] = {
{CS35L41_PWR_CTRL3, 0},
{CS35L41_GPIO_PAD_CONTROL, 0},
@@ -1204,6 +1209,20 @@ int cs35l41_global_enable(struct regmap *regmap, enum cs35l41_boost_type b_type,
{CS35L41_PWR_CTRL1, 0x00000001, 3000},
};
+ pup_pdn_mask = enable ? CS35L41_PUP_DONE_MASK : CS35L41_PDN_DONE_MASK;
+
+ ret = regmap_read(regmap, CS35L41_PWR_CTRL1, &pwr_ctl1_val);
+ if (ret)
+ return ret;
+
+ if ((pwr_ctl1_val & CS35L41_GLOBAL_EN_MASK) && enable) {
+ dev_dbg(dev, "Cannot set Global Enable - already set.\n");
+ return 0;
+ } else if (!(pwr_ctl1_val & CS35L41_GLOBAL_EN_MASK) && !enable) {
+ dev_dbg(dev, "Cannot unset Global Enable - not set.\n");
+ return 0;
+ }
+
switch (b_type) {
case CS35L41_SHD_BOOST_ACTV:
case CS35L41_SHD_BOOST_PASS:
@@ -1240,20 +1259,85 @@ int cs35l41_global_enable(struct regmap *regmap, enum cs35l41_boost_type b_type,
ret = regmap_multi_reg_write(regmap, cs35l41_mdsync_up_seq,
ARRAY_SIZE(cs35l41_mdsync_up_seq));
}
+
+ ret = regmap_read_poll_timeout(regmap, CS35L41_IRQ1_STATUS1,
+ int_status, int_status & pup_pdn_mask,
+ 1000, 100000);
+ if (ret)
+ dev_err(dev, "Enable(%d) failed: %d\n", enable, ret);
+
+ // Clear PUP/PDN status
+ regmap_write(regmap, CS35L41_IRQ1_STATUS1, pup_pdn_mask);
break;
case CS35L41_INT_BOOST:
ret = regmap_update_bits(regmap, CS35L41_PWR_CTRL1, CS35L41_GLOBAL_EN_MASK,
enable << CS35L41_GLOBAL_EN_SHIFT);
- usleep_range(3000, 3100);
+ if (ret) {
+ dev_err(dev, "CS35L41_PWR_CTRL1 set failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_read_poll_timeout(regmap, CS35L41_IRQ1_STATUS1,
+ int_status, int_status & pup_pdn_mask,
+ 1000, 100000);
+ if (ret)
+ dev_err(dev, "Enable(%d) failed: %d\n", enable, ret);
+
+ /* Clear PUP/PDN status */
+ regmap_write(regmap, CS35L41_IRQ1_STATUS1, pup_pdn_mask);
break;
case CS35L41_EXT_BOOST:
case CS35L41_EXT_BOOST_NO_VSPK_SWITCH:
- if (enable)
- ret = regmap_multi_reg_write(regmap, cs35l41_safe_to_active,
- ARRAY_SIZE(cs35l41_safe_to_active));
- else
- ret = regmap_multi_reg_write(regmap, cs35l41_active_to_safe,
- ARRAY_SIZE(cs35l41_active_to_safe));
+ if (enable) {
+ /* Test Key is unlocked here */
+ ret = regmap_multi_reg_write(regmap, cs35l41_safe_to_active_start,
+ ARRAY_SIZE(cs35l41_safe_to_active_start));
+ if (ret)
+ return ret;
+
+ ret = regmap_read_poll_timeout(regmap, CS35L41_IRQ1_STATUS1, int_status,
+ int_status & CS35L41_PUP_DONE_MASK, 1000, 100000);
+ if (ret) {
+ dev_err(dev, "Failed waiting for CS35L41_PUP_DONE_MASK: %d\n", ret);
+ /* Lock the test key, it was unlocked during the multi_reg_write */
+ cs35l41_test_key_lock(dev, regmap);
+ return ret;
+ }
+ regmap_write(regmap, CS35L41_IRQ1_STATUS1, CS35L41_PUP_DONE_MASK);
+
+ if (firmware_running)
+ ret = cs35l41_set_cspl_mbox_cmd(dev, regmap,
+ CSPL_MBOX_CMD_SPK_OUT_ENABLE);
+ else
+ ret = regmap_multi_reg_write(regmap, cs35l41_safe_to_active_en_spk,
+ ARRAY_SIZE(cs35l41_safe_to_active_en_spk));
+
+ /* Lock the test key, it was unlocked during the multi_reg_write */
+ cs35l41_test_key_lock(dev, regmap);
+ } else {
+ /* Test Key is unlocked here */
+ ret = regmap_multi_reg_write(regmap, cs35l41_active_to_safe_start,
+ ARRAY_SIZE(cs35l41_active_to_safe_start));
+ if (ret) {
+ /* Lock the test key, it was unlocked during the multi_reg_write */
+ cs35l41_test_key_lock(dev, regmap);
+ return ret;
+ }
+
+ ret = regmap_read_poll_timeout(regmap, CS35L41_IRQ1_STATUS1, int_status,
+ int_status & CS35L41_PDN_DONE_MASK, 1000, 100000);
+ if (ret) {
+ dev_err(dev, "Failed waiting for CS35L41_PDN_DONE_MASK: %d\n", ret);
+ /* Lock the test key, it was unlocked during the multi_reg_write */
+ cs35l41_test_key_lock(dev, regmap);
+ return ret;
+ }
+ regmap_write(regmap, CS35L41_IRQ1_STATUS1, CS35L41_PDN_DONE_MASK);
+
+ /* Test Key is locked here */
+ ret = regmap_multi_reg_write(regmap, cs35l41_active_to_safe_end,
+ ARRAY_SIZE(cs35l41_active_to_safe_end));
+ }
break;
default:
ret = -EINVAL;
@@ -1344,6 +1428,8 @@ static bool cs35l41_check_cspl_mbox_sts(enum cs35l41_cspl_mbox_cmd cmd,
return (sts == CSPL_MBOX_STS_RUNNING);
case CSPL_MBOX_CMD_STOP_PRE_REINIT:
return (sts == CSPL_MBOX_STS_RDY_FOR_REINIT);
+ case CSPL_MBOX_CMD_SPK_OUT_ENABLE:
+ return (sts == CSPL_MBOX_STS_RUNNING);
default:
return false;
}
diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c
index 6ac501f008ec..722b69a6de26 100644
--- a/sound/soc/codecs/cs35l41.c
+++ b/sound/soc/codecs/cs35l41.c
@@ -168,7 +168,7 @@ static int cs35l41_get_fs_mon_config_index(int freq)
static const DECLARE_TLV_DB_RANGE(dig_vol_tlv,
0, 0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
1, 913, TLV_DB_MINMAX_ITEM(-10200, 1200));
-static DECLARE_TLV_DB_SCALE(amp_gain_tlv, 0, 1, 1);
+static DECLARE_TLV_DB_SCALE(amp_gain_tlv, 50, 100, 0);
static const struct snd_kcontrol_new dre_ctrl =
SOC_DAPM_SINGLE("Switch", CS35L41_PWR_CTRL3, 20, 1, 0);
@@ -491,7 +491,6 @@ static int cs35l41_main_amp_event(struct snd_soc_dapm_widget *w,
{
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component);
- unsigned int val;
int ret = 0;
switch (event) {
@@ -500,21 +499,12 @@ static int cs35l41_main_amp_event(struct snd_soc_dapm_widget *w,
cs35l41_pup_patch,
ARRAY_SIZE(cs35l41_pup_patch));
- cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 1,
- &cs35l41->pll_lock);
+ ret = cs35l41_global_enable(cs35l41->dev, cs35l41->regmap, cs35l41->hw_cfg.bst_type,
+ 1, &cs35l41->pll_lock, cs35l41->dsp.cs_dsp.running);
break;
case SND_SOC_DAPM_POST_PMD:
- cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 0,
- &cs35l41->pll_lock);
-
- ret = regmap_read_poll_timeout(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
- val, val & CS35L41_PDN_DONE_MASK,
- 1000, 100000);
- if (ret)
- dev_warn(cs35l41->dev, "PDN failed: %d\n", ret);
-
- regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
- CS35L41_PDN_DONE_MASK);
+ ret = cs35l41_global_enable(cs35l41->dev, cs35l41->regmap, cs35l41->hw_cfg.bst_type,
+ 0, &cs35l41->pll_lock, cs35l41->dsp.cs_dsp.running);
regmap_multi_reg_write_bypassed(cs35l41->regmap,
cs35l41_pdn_patch,
diff --git a/sound/soc/codecs/sigmadsp.c b/sound/soc/codecs/sigmadsp.c
index 3047a6fbb380..b93c078a8040 100644
--- a/sound/soc/codecs/sigmadsp.c
+++ b/sound/soc/codecs/sigmadsp.c
@@ -669,36 +669,19 @@ static void sigmadsp_activate_ctrl(struct sigmadsp *sigmadsp,
struct sigmadsp_control *ctrl, unsigned int samplerate_mask)
{
struct snd_card *card = sigmadsp->component->card->snd_card;
- struct snd_kcontrol_volatile *vd;
- struct snd_ctl_elem_id id;
bool active;
- bool changed = false;
+ int changed;
active = sigmadsp_samplerate_valid(ctrl->samplerates, samplerate_mask);
-
- down_write(&card->controls_rwsem);
- if (!ctrl->kcontrol) {
- up_write(&card->controls_rwsem);
+ if (!ctrl->kcontrol)
return;
- }
-
- id = ctrl->kcontrol->id;
- vd = &ctrl->kcontrol->vd[0];
- if (active == (bool)(vd->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE)) {
- vd->access ^= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
- changed = true;
- }
- up_write(&card->controls_rwsem);
-
- if (active && changed) {
+ changed = snd_ctl_activate_id(card, &ctrl->kcontrol->id, active);
+ if (active && changed > 0) {
mutex_lock(&sigmadsp->lock);
if (ctrl->cached)
sigmadsp_ctrl_write(sigmadsp, ctrl, ctrl->cache);
mutex_unlock(&sigmadsp->lock);
}
-
- if (changed)
- snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, &id);
}
/**
diff --git a/sound/soc/mediatek/common/mtk-btcvsd.c b/sound/soc/mediatek/common/mtk-btcvsd.c
index 1ba0633e542f..c12d170fa1de 100644
--- a/sound/soc/mediatek/common/mtk-btcvsd.c
+++ b/sound/soc/mediatek/common/mtk-btcvsd.c
@@ -696,11 +696,10 @@ static int wait_for_bt_irq(struct mtk_btcvsd_snd *bt,
}
static ssize_t mtk_btcvsd_snd_read(struct mtk_btcvsd_snd *bt,
- char __user *buf,
+ struct iov_iter *buf,
size_t count)
{
ssize_t read_size = 0, read_count = 0, cur_read_idx, cont;
- unsigned int cur_buf_ofs = 0;
unsigned long avail;
unsigned long flags;
unsigned int packet_size = bt->rx->packet_size;
@@ -743,10 +742,9 @@ static ssize_t mtk_btcvsd_snd_read(struct mtk_btcvsd_snd *bt,
if (read_size > cont)
read_size = cont;
- if (copy_to_user(buf + cur_buf_ofs,
- bt->rx_packet_buf + cur_read_idx,
- read_size)) {
- dev_warn(bt->dev, "%s(), copy_to_user fail\n",
+ if (copy_to_iter(bt->rx_packet_buf + cur_read_idx,
+ read_size, buf) != read_size) {
+ dev_warn(bt->dev, "%s(), copy_to_iter fail\n",
__func__);
return -EFAULT;
}
@@ -756,7 +754,6 @@ static ssize_t mtk_btcvsd_snd_read(struct mtk_btcvsd_snd *bt,
spin_unlock_irqrestore(&bt->rx_lock, flags);
read_count += read_size;
- cur_buf_ofs += read_size;
count -= read_size;
}
@@ -777,11 +774,10 @@ static ssize_t mtk_btcvsd_snd_read(struct mtk_btcvsd_snd *bt,
}
static ssize_t mtk_btcvsd_snd_write(struct mtk_btcvsd_snd *bt,
- char __user *buf,
+ struct iov_iter *buf,
size_t count)
{
int written_size = count, avail, cur_write_idx, write_size, cont;
- unsigned int cur_buf_ofs = 0;
unsigned long flags;
unsigned int packet_size = bt->tx->packet_size;
@@ -835,11 +831,9 @@ static ssize_t mtk_btcvsd_snd_write(struct mtk_btcvsd_snd *bt,
if (write_size > cont)
write_size = cont;
- if (copy_from_user(bt->tx_packet_buf +
- cur_write_idx,
- buf + cur_buf_ofs,
- write_size)) {
- dev_warn(bt->dev, "%s(), copy_from_user fail\n",
+ if (copy_from_iter(bt->tx_packet_buf + cur_write_idx,
+ write_size, buf) != write_size) {
+ dev_warn(bt->dev, "%s(), copy_from_iter fail\n",
__func__);
return -EFAULT;
}
@@ -847,7 +841,6 @@ static ssize_t mtk_btcvsd_snd_write(struct mtk_btcvsd_snd *bt,
spin_lock_irqsave(&bt->tx_lock, flags);
bt->tx->packet_w += write_size / packet_size;
spin_unlock_irqrestore(&bt->tx_lock, flags);
- cur_buf_ofs += write_size;
count -= write_size;
}
@@ -1033,7 +1026,7 @@ static snd_pcm_uframes_t mtk_pcm_btcvsd_pointer(
static int mtk_pcm_btcvsd_copy(struct snd_soc_component *component,
struct snd_pcm_substream *substream,
int channel, unsigned long pos,
- void __user *buf, unsigned long count)
+ struct iov_iter *buf, unsigned long count)
{
struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component);
@@ -1274,7 +1267,7 @@ static const struct snd_soc_component_driver mtk_btcvsd_snd_platform = {
.prepare = mtk_pcm_btcvsd_prepare,
.trigger = mtk_pcm_btcvsd_trigger,
.pointer = mtk_pcm_btcvsd_pointer,
- .copy_user = mtk_pcm_btcvsd_copy,
+ .copy = mtk_pcm_btcvsd_copy,
};
static int mtk_btcvsd_snd_probe(struct platform_device *pdev)
diff --git a/sound/soc/mediatek/mt8188/mt8188-mt6359.c b/sound/soc/mediatek/mt8188/mt8188-mt6359.c
index 7c9e08e6a4f5..9017f48b6272 100644
--- a/sound/soc/mediatek/mt8188/mt8188-mt6359.c
+++ b/sound/soc/mediatek/mt8188/mt8188-mt6359.c
@@ -984,16 +984,6 @@ static struct snd_soc_dai_link mt8188_mt6359_dai_links[] = {
},
};
-static struct snd_kcontrol *ctl_find(struct snd_card *card, const char *name)
-{
- struct snd_ctl_elem_id sid;
-
- memset(&sid, 0, sizeof(sid));
- strcpy(sid.name, name);
- sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- return snd_ctl_find_id(card, &sid);
-}
-
static void mt8188_fixup_controls(struct snd_soc_card *card)
{
struct mt8188_mt6359_priv *priv = snd_soc_card_get_drvdata(card);
@@ -1010,7 +1000,7 @@ static void mt8188_fixup_controls(struct snd_soc_card *card)
snd_soc_dapm_free_widget(w);
}
- kctl = ctl_find(card->snd_card, "Headphone Switch");
+ kctl = snd_ctl_find_id_mixer(card->snd_card, "Headphone Switch");
if (kctl)
snd_ctl_remove(card->snd_card, kctl);
else
diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c
index ef5cb40b2d9b..990d7c33f90f 100644
--- a/sound/soc/qcom/lpass-platform.c
+++ b/sound/soc/qcom/lpass-platform.c
@@ -1219,7 +1219,8 @@ static int lpass_platform_pcmops_resume(struct snd_soc_component *component)
static int lpass_platform_copy(struct snd_soc_component *component,
struct snd_pcm_substream *substream, int channel,
- unsigned long pos, void __user *buf, unsigned long bytes)
+ unsigned long pos, struct iov_iter *buf,
+ unsigned long bytes)
{
struct snd_pcm_runtime *rt = substream->runtime;
unsigned int dai_id = component->id;
@@ -1230,16 +1231,16 @@ static int lpass_platform_copy(struct snd_soc_component *component,
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
if (is_cdc_dma_port(dai_id)) {
- ret = copy_from_user_toio(dma_buf, buf, bytes);
+ ret = copy_from_iter_toio(dma_buf, buf, bytes);
} else {
- if (copy_from_user((void __force *)dma_buf, buf, bytes))
+ if (copy_from_iter((void __force *)dma_buf, bytes, buf) != bytes)
ret = -EFAULT;
}
} else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
if (is_cdc_dma_port(dai_id)) {
- ret = copy_to_user_fromio(buf, dma_buf, bytes);
+ ret = copy_to_iter_fromio(buf, dma_buf, bytes);
} else {
- if (copy_to_user(buf, (void __force *)dma_buf, bytes))
+ if (copy_to_iter((void __force *)dma_buf, bytes, buf) != bytes)
ret = -EFAULT;
}
}
@@ -1260,7 +1261,7 @@ static const struct snd_soc_component_driver lpass_component_driver = {
.pcm_construct = lpass_platform_pcm_new,
.suspend = lpass_platform_pcmops_suspend,
.resume = lpass_platform_pcmops_resume,
- .copy_user = lpass_platform_copy,
+ .copy = lpass_platform_copy,
};
diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c
index 4356cc320fea..f18406dfa1e4 100644
--- a/sound/soc/soc-component.c
+++ b/sound/soc/soc-component.c
@@ -1052,9 +1052,9 @@ int snd_soc_pcm_component_sync_stop(struct snd_pcm_substream *substream)
return 0;
}
-int snd_soc_pcm_component_copy_user(struct snd_pcm_substream *substream,
- int channel, unsigned long pos,
- void __user *buf, unsigned long bytes)
+int snd_soc_pcm_component_copy(struct snd_pcm_substream *substream,
+ int channel, unsigned long pos,
+ struct iov_iter *buf, unsigned long bytes)
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_soc_component *component;
@@ -1062,12 +1062,10 @@ int snd_soc_pcm_component_copy_user(struct snd_pcm_substream *substream,
/* FIXME. it returns 1st copy now */
for_each_rtd_components(rtd, i, component)
- if (component->driver->copy_user)
- return soc_component_ret(
- component,
- component->driver->copy_user(
- component, substream, channel,
- pos, buf, bytes));
+ if (component->driver->copy)
+ return soc_component_ret(component,
+ component->driver->copy(component, substream,
+ channel, pos, buf, bytes));
return -EINVAL;
}
diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c
index 3b99f619e37e..ff2166525dbc 100644
--- a/sound/soc/soc-generic-dmaengine-pcm.c
+++ b/sound/soc/soc-generic-dmaengine-pcm.c
@@ -287,32 +287,32 @@ static snd_pcm_uframes_t dmaengine_pcm_pointer(
return snd_dmaengine_pcm_pointer(substream);
}
-static int dmaengine_copy_user(struct snd_soc_component *component,
- struct snd_pcm_substream *substream,
- int channel, unsigned long hwoff,
- void __user *buf, unsigned long bytes)
+static int dmaengine_copy(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ int channel, unsigned long hwoff,
+ struct iov_iter *buf, unsigned long bytes)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct dmaengine_pcm *pcm = soc_component_to_pcm(component);
int (*process)(struct snd_pcm_substream *substream,
int channel, unsigned long hwoff,
- void *buf, unsigned long bytes) = pcm->config->process;
+ struct iov_iter *buf, unsigned long bytes) = pcm->config->process;
bool is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
void *dma_ptr = runtime->dma_area + hwoff +
channel * (runtime->dma_bytes / runtime->channels);
if (is_playback)
- if (copy_from_user(dma_ptr, buf, bytes))
+ if (copy_from_iter(dma_ptr, bytes, buf) != bytes)
return -EFAULT;
if (process) {
- int ret = process(substream, channel, hwoff, (__force void *)buf, bytes);
+ int ret = process(substream, channel, hwoff, buf, bytes);
if (ret < 0)
return ret;
}
if (!is_playback)
- if (copy_to_user(buf, dma_ptr, bytes))
+ if (copy_to_iter(dma_ptr, bytes, buf) != bytes)
return -EFAULT;
return 0;
@@ -337,7 +337,7 @@ static const struct snd_soc_component_driver dmaengine_pcm_component_process = {
.hw_params = dmaengine_pcm_hw_params,
.trigger = dmaengine_pcm_trigger,
.pointer = dmaengine_pcm_pointer,
- .copy_user = dmaengine_copy_user,
+ .copy = dmaengine_copy,
.pcm_construct = dmaengine_pcm_new,
};
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 3aa6b988cb4b..eb0723876851 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -2977,8 +2977,8 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
rtd->ops.ioctl = snd_soc_pcm_component_ioctl;
if (drv->sync_stop)
rtd->ops.sync_stop = snd_soc_pcm_component_sync_stop;
- if (drv->copy_user)
- rtd->ops.copy_user = snd_soc_pcm_component_copy_user;
+ if (drv->copy)
+ rtd->ops.copy = snd_soc_pcm_component_copy;
if (drv->page)
rtd->ops.page = snd_soc_pcm_component_page;
if (drv->mmap)
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
index e783055b6c3a..2362c282ec8b 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -2572,7 +2572,6 @@ EXPORT_SYMBOL_GPL(snd_soc_tplg_component_load);
/* remove dynamic controls from the component driver */
int snd_soc_tplg_component_remove(struct snd_soc_component *comp)
{
- struct snd_card *card = comp->card->snd_card;
struct snd_soc_dobj *dobj, *next_dobj;
int pass;
@@ -2580,7 +2579,6 @@ int snd_soc_tplg_component_remove(struct snd_soc_component *comp)
for (pass = SOC_TPLG_PASS_END; pass >= SOC_TPLG_PASS_START; pass--) {
/* remove mixer controls */
- down_write(&card->controls_rwsem);
list_for_each_entry_safe(dobj, next_dobj, &comp->dobj_list,
list) {
@@ -2615,7 +2613,6 @@ int snd_soc_tplg_component_remove(struct snd_soc_component *comp)
break;
}
}
- up_write(&card->controls_rwsem);
}
/* let caller know if FW can be freed when no objects are left */
diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c
index 42d67b7a68e8..f9b5d5969155 100644
--- a/sound/soc/stm/stm32_sai_sub.c
+++ b/sound/soc/stm/stm32_sai_sub.c
@@ -1246,7 +1246,7 @@ static const struct snd_soc_dai_ops stm32_sai_pcm_dai_ops2 = {
static int stm32_sai_pcm_process_spdif(struct snd_pcm_substream *substream,
int channel, unsigned long hwoff,
- void *buf, unsigned long bytes)
+ struct iov_iter *buf, unsigned long bytes)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
diff --git a/sound/sparc/amd7930.c b/sound/sparc/amd7930.c
index c434b69a83f1..0fea04acc3ea 100644
--- a/sound/sparc/amd7930.c
+++ b/sound/sparc/amd7930.c
@@ -37,7 +37,7 @@
#include <linux/interrupt.h>
#include <linux/moduleparam.h>
#include <linux/of.h>
-#include <linux/of_device.h>
+#include <linux/platform_device.h>
#include <linux/io.h>
#include <sound/core.h>
@@ -47,7 +47,6 @@
#include <sound/initval.h>
#include <asm/irq.h>
-#include <asm/prom.h>
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
diff --git a/sound/sparc/cs4231.c b/sound/sparc/cs4231.c
index 31bac355ec4d..c2ad3fa2f25a 100644
--- a/sound/sparc/cs4231.c
+++ b/sound/sparc/cs4231.c
@@ -18,7 +18,7 @@
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/of.h>
-#include <linux/of_device.h>
+#include <linux/platform_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c
index 376aed136a45..050e98f32d36 100644
--- a/sound/sparc/dbri.c
+++ b/sound/sparc/dbri.c
@@ -69,7 +69,7 @@
#include <sound/initval.h>
#include <linux/of.h>
-#include <linux/of_device.h>
+#include <linux/platform_device.h>
#include <linux/atomic.h>
#include <linux/module.h>
diff --git a/sound/usb/bcd2000/bcd2000.c b/sound/usb/bcd2000/bcd2000.c
index 7aec0a95c609..392b4d8e9e76 100644
--- a/sound/usb/bcd2000/bcd2000.c
+++ b/sound/usb/bcd2000/bcd2000.c
@@ -395,8 +395,8 @@ static int bcd2000_probe(struct usb_interface *interface,
snd_card_set_dev(card, &interface->dev);
- strncpy(card->driver, "snd-bcd2000", sizeof(card->driver));
- strncpy(card->shortname, "BCD2000", sizeof(card->shortname));
+ strscpy(card->driver, "snd-bcd2000", sizeof(card->driver));
+ strscpy(card->shortname, "BCD2000", sizeof(card->shortname));
usb_make_path(bcd2k->dev, usb_path, sizeof(usb_path));
snprintf(bcd2k->card->longname, sizeof(bcd2k->card->longname),
"Behringer BCD2000 at %s",
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index a385e85c4650..8f65349a06d3 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -505,13 +505,18 @@ int snd_usb_queue_pending_output_urbs(struct snd_usb_endpoint *ep,
return -EPIPE;
}
- err = usb_submit_urb(ctx->urb, GFP_ATOMIC);
+ if (!atomic_read(&ep->chip->shutdown))
+ err = usb_submit_urb(ctx->urb, GFP_ATOMIC);
+ else
+ err = -ENODEV;
if (err < 0) {
- usb_audio_err(ep->chip,
- "Unable to submit urb #%d: %d at %s\n",
- ctx->index, err, __func__);
- if (!in_stream_lock)
- notify_xrun(ep);
+ if (!atomic_read(&ep->chip->shutdown)) {
+ usb_audio_err(ep->chip,
+ "Unable to submit urb #%d: %d at %s\n",
+ ctx->index, err, __func__);
+ if (!in_stream_lock)
+ notify_xrun(ep);
+ }
return -EPIPE;
}
@@ -575,12 +580,17 @@ static void snd_complete_urb(struct urb *urb)
prepare_inbound_urb(ep, ctx);
}
- err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (!atomic_read(&ep->chip->shutdown))
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ else
+ err = -ENODEV;
if (err == 0)
return;
- usb_audio_err(ep->chip, "cannot submit urb (err = %d)\n", err);
- notify_xrun(ep);
+ if (!atomic_read(&ep->chip->shutdown)) {
+ usb_audio_err(ep->chip, "cannot submit urb (err = %d)\n", err);
+ notify_xrun(ep);
+ }
exit_clear:
clear_bit(ctx->index, &ep->active_mask);
@@ -1603,11 +1613,15 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep)
goto __error;
}
- err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (!atomic_read(&ep->chip->shutdown))
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ else
+ err = -ENODEV;
if (err < 0) {
- usb_audio_err(ep->chip,
- "cannot submit urb %d, error %d: %s\n",
- i, err, usb_error_string(err));
+ if (!atomic_read(&ep->chip->shutdown))
+ usb_audio_err(ep->chip,
+ "cannot submit urb %d, error %d: %s\n",
+ i, err, usb_error_string(err));
goto __error;
}
set_bit(i, &ep->active_mask);
diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h
index c09f68ce08b1..ba70f52f6860 100644
--- a/sound/usb/endpoint.h
+++ b/sound/usb/endpoint.h
@@ -44,7 +44,6 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep);
void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep, bool keep_pending);
void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep);
void snd_usb_endpoint_suspend(struct snd_usb_endpoint *ep);
-int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep);
void snd_usb_endpoint_release(struct snd_usb_endpoint *ep);
void snd_usb_endpoint_free_all(struct snd_usb_audio *chip);
diff --git a/sound/usb/media.c b/sound/usb/media.c
index 840f42cb9272..d48db6f3ae65 100644
--- a/sound/usb/media.c
+++ b/sound/usb/media.c
@@ -35,7 +35,7 @@ int snd_media_stream_init(struct snd_usb_substream *subs, struct snd_pcm *pcm,
{
struct media_device *mdev;
struct media_ctl *mctl;
- struct device *pcm_dev = &pcm->streams[stream].dev;
+ struct device *pcm_dev = pcm->streams[stream].dev;
u32 intf_type;
int ret = 0;
u16 mixer_pad;
@@ -163,7 +163,7 @@ void snd_media_stop_pipeline(struct snd_usb_substream *subs)
static int snd_media_mixer_init(struct snd_usb_audio *chip)
{
- struct device *ctl_dev = &chip->card->ctl_dev;
+ struct device *ctl_dev = chip->card->ctl_dev;
struct media_intf_devnode *ctl_intf;
struct usb_mixer_interface *mixer;
struct media_device *mdev = chip->media_dev;
diff --git a/sound/usb/midi2.c b/sound/usb/midi2.c
index ee2835741479..a27e244650c8 100644
--- a/sound/usb/midi2.c
+++ b/sound/usb/midi2.c
@@ -990,7 +990,7 @@ static int parse_midi_2_0(struct snd_usb_midi2_interface *umidi)
}
}
- return attach_legacy_rawmidi(umidi);
+ return 0;
}
/* is the given interface for MIDI 2.0? */
@@ -1059,12 +1059,6 @@ static void set_fallback_rawmidi_names(struct snd_usb_midi2_interface *umidi)
usb_string(dev, dev->descriptor.iSerialNumber,
ump->info.product_id,
sizeof(ump->info.product_id));
-#if IS_ENABLED(CONFIG_SND_UMP_LEGACY_RAWMIDI)
- if (ump->legacy_rmidi && !*ump->legacy_rmidi->name)
- snprintf(ump->legacy_rmidi->name,
- sizeof(ump->legacy_rmidi->name),
- "%s (MIDI 1.0)", ump->info.name);
-#endif
}
}
@@ -1157,6 +1151,13 @@ int snd_usb_midi_v2_create(struct snd_usb_audio *chip,
}
set_fallback_rawmidi_names(umidi);
+
+ err = attach_legacy_rawmidi(umidi);
+ if (err < 0) {
+ usb_audio_err(chip, "Failed to create legacy rawmidi\n");
+ goto error;
+ }
+
return 0;
error:
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index efb4a3311cc5..5d72dc8441cb 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -4507,6 +4507,35 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
+{
+ /* Advanced modes of the Mythware XA001AU.
+ * For the standard mode, Mythware XA001AU has ID ffad:a001
+ */
+ USB_DEVICE_VENDOR_SPEC(0xffad, 0xa001),
+ .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ .vendor_name = "Mythware",
+ .product_name = "XA001AU",
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = (const struct snd_usb_audio_quirk[]) {
+ {
+ .ifnum = 0,
+ .type = QUIRK_IGNORE_INTERFACE,
+ },
+ {
+ .ifnum = 1,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE,
+ },
+ {
+ .ifnum = 2,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE,
+ },
+ {
+ .ifnum = -1
+ }
+ }
+ }
+},
#undef USB_DEVICE_VENDOR_SPEC
#undef USB_AUDIO_DEVICE
diff --git a/sound/usb/stream.c b/sound/usb/stream.c
index f10f4e6d3fb8..3d4add94e367 100644
--- a/sound/usb/stream.c
+++ b/sound/usb/stream.c
@@ -1093,6 +1093,7 @@ static int __snd_usb_parse_audio_interface(struct snd_usb_audio *chip,
int i, altno, err, stream;
struct audioformat *fp = NULL;
struct snd_usb_power_domain *pd = NULL;
+ bool set_iface_first;
int num, protocol;
dev = chip->dev;
@@ -1223,11 +1224,19 @@ static int __snd_usb_parse_audio_interface(struct snd_usb_audio *chip,
return err;
}
+ set_iface_first = false;
+ if (protocol == UAC_VERSION_1 ||
+ (chip->quirk_flags & QUIRK_FLAG_SET_IFACE_FIRST))
+ set_iface_first = true;
+
/* try to set the interface... */
usb_set_interface(chip->dev, iface_no, 0);
+ if (set_iface_first)
+ usb_set_interface(chip->dev, iface_no, altno);
snd_usb_init_pitch(chip, fp);
snd_usb_init_sample_rate(chip, fp, fp->rate_max);
- usb_set_interface(chip->dev, iface_no, altno);
+ if (!set_iface_first)
+ usb_set_interface(chip->dev, iface_no, altno);
}
return 0;
}
diff --git a/sound/xen/xen_snd_front_alsa.c b/sound/xen/xen_snd_front_alsa.c
index db917453a473..31b5dc0f34d2 100644
--- a/sound/xen/xen_snd_front_alsa.c
+++ b/sound/xen/xen_snd_front_alsa.c
@@ -602,56 +602,24 @@ static snd_pcm_uframes_t alsa_pointer(struct snd_pcm_substream *substream)
return (snd_pcm_uframes_t)atomic_read(&stream->hw_ptr);
}
-static int alsa_pb_copy_user(struct snd_pcm_substream *substream,
- int channel, unsigned long pos, void __user *src,
- unsigned long count)
+static int alsa_pb_copy(struct snd_pcm_substream *substream,
+ int channel, unsigned long pos, struct iov_iter *src,
+ unsigned long count)
{
struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
if (unlikely(pos + count > stream->buffer_sz))
return -EINVAL;
- if (copy_from_user(stream->buffer + pos, src, count))
+ if (copy_from_iter(stream->buffer + pos, count, src) != count)
return -EFAULT;
return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count);
}
-static int alsa_pb_copy_kernel(struct snd_pcm_substream *substream,
- int channel, unsigned long pos, void *src,
- unsigned long count)
-{
- struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
-
- if (unlikely(pos + count > stream->buffer_sz))
- return -EINVAL;
-
- memcpy(stream->buffer + pos, src, count);
-
- return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count);
-}
-
-static int alsa_cap_copy_user(struct snd_pcm_substream *substream,
- int channel, unsigned long pos, void __user *dst,
- unsigned long count)
-{
- struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
- int ret;
-
- if (unlikely(pos + count > stream->buffer_sz))
- return -EINVAL;
-
- ret = xen_snd_front_stream_read(&stream->evt_pair->req, pos, count);
- if (ret < 0)
- return ret;
-
- return copy_to_user(dst, stream->buffer + pos, count) ?
- -EFAULT : 0;
-}
-
-static int alsa_cap_copy_kernel(struct snd_pcm_substream *substream,
- int channel, unsigned long pos, void *dst,
- unsigned long count)
+static int alsa_cap_copy(struct snd_pcm_substream *substream,
+ int channel, unsigned long pos, struct iov_iter *dst,
+ unsigned long count)
{
struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
int ret;
@@ -663,8 +631,8 @@ static int alsa_cap_copy_kernel(struct snd_pcm_substream *substream,
if (ret < 0)
return ret;
- memcpy(dst, stream->buffer + pos, count);
-
+ if (copy_to_iter(stream->buffer + pos, count, dst) != count)
+ return -EFAULT;
return 0;
}
@@ -697,8 +665,7 @@ static const struct snd_pcm_ops snd_drv_alsa_playback_ops = {
.prepare = alsa_prepare,
.trigger = alsa_trigger,
.pointer = alsa_pointer,
- .copy_user = alsa_pb_copy_user,
- .copy_kernel = alsa_pb_copy_kernel,
+ .copy = alsa_pb_copy,
.fill_silence = alsa_pb_fill_silence,
};
@@ -710,8 +677,7 @@ static const struct snd_pcm_ops snd_drv_alsa_capture_ops = {
.prepare = alsa_prepare,
.trigger = alsa_trigger,
.pointer = alsa_pointer,
- .copy_user = alsa_cap_copy_user,
- .copy_kernel = alsa_cap_copy_kernel,
+ .copy = alsa_cap_copy,
};
static int new_pcm_instance(struct xen_snd_front_card_info *card_info,
@@ -783,7 +749,7 @@ static int new_pcm_instance(struct xen_snd_front_card_info *card_info,
pcm->info_flags = 0;
/* we want to handle all PCM operations in non-atomic context */
pcm->nonatomic = true;
- strncpy(pcm->name, "Virtual card PCM", sizeof(pcm->name));
+ strscpy(pcm->name, "Virtual card PCM", sizeof(pcm->name));
if (instance_cfg->num_streams_pb)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
@@ -835,9 +801,9 @@ int xen_snd_front_alsa_init(struct xen_snd_front_info *front_info)
goto fail;
}
- strncpy(card->driver, XENSND_DRIVER_NAME, sizeof(card->driver));
- strncpy(card->shortname, cfg->name_short, sizeof(card->shortname));
- strncpy(card->longname, cfg->name_long, sizeof(card->longname));
+ strscpy(card->driver, XENSND_DRIVER_NAME, sizeof(card->driver));
+ strscpy(card->shortname, cfg->name_short, sizeof(card->shortname));
+ strscpy(card->longname, cfg->name_long, sizeof(card->longname));
ret = snd_card_register(card);
if (ret < 0)
diff --git a/tools/testing/selftests/alsa/pcm-test.c b/tools/testing/selftests/alsa/pcm-test.c
index b7eef32addb4..2f5e3c462194 100644
--- a/tools/testing/selftests/alsa/pcm-test.c
+++ b/tools/testing/selftests/alsa/pcm-test.c
@@ -258,6 +258,8 @@ static void test_pcm_time(struct pcm_data *data, enum test_class class,
const char *test_name, snd_config_t *pcm_cfg)
{
char name[64], key[128], msg[256];
+ const int duration_s = 2, margin_ms = 100;
+ const int duration_ms = duration_s * 1000;
const char *cs;
int i, err;
snd_pcm_t *handle = NULL;
@@ -442,7 +444,7 @@ __format:
skip = false;
timestamp_now(&tstamp);
- for (i = 0; i < 4; i++) {
+ for (i = 0; i < duration_s; i++) {
if (data->stream == SND_PCM_STREAM_PLAYBACK) {
frames = snd_pcm_writei(handle, samples, rate);
if (frames < 0) {
@@ -472,8 +474,8 @@ __format:
snd_pcm_drain(handle);
ms = timestamp_diff_ms(&tstamp);
- if (ms < 3900 || ms > 4100) {
- snprintf(msg, sizeof(msg), "time mismatch: expected 4000ms got %lld", ms);
+ if (ms < duration_ms - margin_ms || ms > duration_ms + margin_ms) {
+ snprintf(msg, sizeof(msg), "time mismatch: expected %dms got %lld", duration_ms, ms);
goto __close;
}