aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-12-11 13:20:50 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2014-12-11 13:20:50 -0800
commitbae41e45b7400496b9bf0c70c6004419d9987819 (patch)
treecf22a65d119da1c414dbc79518857800fbe7a24b
parent7ef58b32f571bffb7763c6252ad7527562081f34 (diff)
parent6e1d7a51392f06899bd7b693f28ac60fa1e00032 (diff)
Merge tag 'sound-3.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai: "This became a fairly large pull request. In addition to the usual driver updates / fixes, there have been a high amount of cleanups in ASoC area, as well as control API helpers and kernel documentations fixes touching through the whole tree. In the driver side, the biggest changes are the support for new Intel SoC found on new x86 machines, and the updates of FireWire dice and oxfw drivers. Some remarkable items are below: ALSA core: - PCM mmap code cleanup, removal of arch-dependent codes - PCM xrun injection support - PCM hwptr tracepoint support - Refactoring of snd_pcm_action(), simplification of PCM locking - Robustified sequecner auto-load functionality - New control API helpers and lots of cleanups along with them - Lots of kerneldoc fixes and cleanups USB-audio: - The mixer resume code was largely rewritten, and the devices with quirks are resumed properly. - New hardware support: Focusrite Scarlett, Digidesign Mbox1, Denon/Marantz DACs, Zoom R16/24 FireWire: - DICE driver updates with better duplex and sync support, including MIDI support - New OXFW driver for Oxford Semiconductor FW970/971 chipset, including the previous LaCie Speakers device. Fullduplex and MIDI support included as well as DICE driver. HD-audio: - Refactoring the driver-caps quirk handling in snd-hda-intel - More consistent control names representing the topology better - Fixups: HP mute LED with ALC268 codec, Ideapad S210 built-in mic fix, ASUS Z99He laptop EAPD ASoC: - Conversion of AC'97 drivers to use regmap, bringing us closer to the removal of the ASoC level I/O code - Clean up a lot of old drivers that were open coding things that have subsequently been implemented in the core - Some DAPM performance improvements - Removal of the now seldom used CODEC mutex - Lots of updates for the newer Intel SoC support, including support for the DSP and some Cherrytrail and Braswell machine drivers - Support for Samsung boards using rt5631 as the CODEC - Removal of the obsolete AFEB9260 machine driver - Driver support for the TI TS3A227E headset driver used in some Chrombeooks Others: - ASIHPI driver update and cleanups - Lots of dev_*() printk conversions - Lots of trivial cleanups for the codes spotted by Coccinelle" * tag 'sound-3.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (594 commits) ALSA: pcxhr: NULL dereference on probe failure ALSA: lola: NULL dereference on probe failure ALSA: hda - Add "eapd" model string for AD1986A codec ALSA: hda - Add EAPD fixup for ASUS Z99He laptop ALSA: oxfw: Add hwdep interface ALSA: oxfw: Add support for capture/playback MIDI messages ALSA: oxfw: add support for capturing PCM samples ALSA: oxfw: Add support AMDTP in-stream ALSA: oxfw: Add support for Behringer/Mackie devices ALSA: oxfw: Change the way to start stream ALSA: oxfw: Add proc interface for debugging purpose ALSA: oxfw: Change the way to make PCM rules/constraints ALSA: oxfw: Add support for AV/C stream format command to get/set supported stream formation ALSA: oxfw: Change the way to name card ALSA: dice: Add support for MIDI capture/playback ALSA: dice: Add support for capturing PCM samples ALSA: dice: Support for non SYT-Match sampling clock source mode ALSA: dice: Add support for duplex streams with synchronization ALSA: dice: Change the way to start stream ALSA: jack: Add dummy snd_jack_set_key() definition ...
-rw-r--r--Documentation/DocBook/alsa-driver-api.tmpl31
-rw-r--r--Documentation/DocBook/writing-an-alsa-driver.tmpl23
-rw-r--r--Documentation/devicetree/bindings/sound/arndale.txt24
-rw-r--r--Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt2
-rw-r--r--Documentation/devicetree/bindings/sound/eukrea-tlv320.txt15
-rw-r--r--Documentation/devicetree/bindings/sound/fsl,esai.txt44
-rw-r--r--Documentation/devicetree/bindings/sound/fsl,spdif.txt37
-rw-r--r--Documentation/devicetree/bindings/sound/fsl-sai.txt66
-rw-r--r--Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt61
-rw-r--r--Documentation/devicetree/bindings/sound/imx-audio-spdif.txt22
-rw-r--r--Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt45
-rw-r--r--Documentation/devicetree/bindings/sound/imx-audmux.txt22
-rw-r--r--Documentation/devicetree/bindings/sound/max98090.txt2
-rw-r--r--Documentation/devicetree/bindings/sound/renesas,fsi.txt9
-rw-r--r--Documentation/devicetree/bindings/sound/renesas,rsnd.txt10
-rw-r--r--Documentation/devicetree/bindings/sound/rt5631.txt48
-rw-r--r--Documentation/devicetree/bindings/sound/rt5677.txt17
-rw-r--r--Documentation/devicetree/bindings/sound/samsung-i2s.txt15
-rw-r--r--Documentation/devicetree/bindings/sound/sgtl5000.txt13
-rw-r--r--Documentation/devicetree/bindings/sound/ts3a227e.txt26
-rw-r--r--Documentation/devicetree/bindings/sound/wm8960.txt31
-rw-r--r--Documentation/sound/alsa/ControlNames.txt32
-rw-r--r--Documentation/sound/alsa/HD-Audio-Models.txt8
-rw-r--r--Documentation/sound/alsa/Procfile.txt17
-rw-r--r--MAINTAINERS6
-rw-r--r--arch/arm/mach-pxa/spitz.c9
-rw-r--r--arch/x86/include/asm/platform_sst_audio.h62
-rw-r--r--drivers/media/pci/saa7134/saa7134-alsa.c4
-rw-r--r--include/linux/mfd/arizona/core.h1
-rw-r--r--include/linux/mfd/davinci_voicecodec.h7
-rw-r--r--include/linux/platform_data/asoc-s3c.h1
-rw-r--r--include/sound/compress_driver.h5
-rw-r--r--include/sound/jack.h26
-rw-r--r--include/sound/pcm.h254
-rw-r--r--include/sound/rcar_snd.h8
-rw-r--r--include/sound/rt5645.h4
-rw-r--r--include/sound/rt5677.h10
-rw-r--r--include/sound/seq_kernel.h4
-rw-r--r--include/sound/soc-dai.h7
-rw-r--r--include/sound/soc-dapm.h9
-rw-r--r--include/sound/soc.h116
-rw-r--r--include/sound/uda134x.h12
-rw-r--r--include/trace/events/asoc.h25
-rw-r--r--include/uapi/sound/asound.h3
-rw-r--r--include/uapi/sound/compress_offload.h17
-rw-r--r--include/uapi/sound/firewire.h3
-rw-r--r--include/uapi/sound/hdspm.h12
-rw-r--r--sound/aoa/codecs/onyx.c8
-rw-r--r--sound/aoa/codecs/tas.c10
-rw-r--r--sound/aoa/soundbus/i2sbus/core.c6
-rw-r--r--sound/arm/pxa2xx-pcm-lib.c4
-rw-r--r--sound/atmel/abdac.c18
-rw-r--r--sound/atmel/ac97c.c12
-rw-r--r--sound/core/Makefile3
-rw-r--r--sound/core/control.c71
-rw-r--r--sound/core/init.c33
-rw-r--r--sound/core/pcm.c48
-rw-r--r--sound/core/pcm_lib.c148
-rw-r--r--sound/core/pcm_native.c214
-rw-r--r--sound/core/pcm_trace.h110
-rw-r--r--sound/core/seq/oss/seq_oss_init.c9
-rw-r--r--sound/core/seq/seq.c5
-rw-r--r--sound/core/seq/seq_device.c103
-rw-r--r--sound/core/sgbuf.c3
-rw-r--r--sound/core/sound.c9
-rw-r--r--sound/drivers/mts64.c18
-rw-r--r--sound/drivers/virmidi.c21
-rw-r--r--sound/drivers/vx/vx_core.c10
-rw-r--r--sound/drivers/vx/vx_mixer.c35
-rw-r--r--sound/firewire/Kconfig26
-rw-r--r--sound/firewire/Makefile7
-rw-r--r--sound/firewire/amdtp.c8
-rw-r--r--sound/firewire/amdtp.h24
-rw-r--r--sound/firewire/bebob/bebob.h4
-rw-r--r--sound/firewire/bebob/bebob_focusrite.c10
-rw-r--r--sound/firewire/bebob/bebob_maudio.c59
-rw-r--r--sound/firewire/bebob/bebob_terratec.c4
-rw-r--r--sound/firewire/bebob/bebob_yamaha.c2
-rw-r--r--sound/firewire/cmp.c2
-rw-r--r--sound/firewire/dice.c1511
-rw-r--r--sound/firewire/dice/Makefile3
-rw-r--r--sound/firewire/dice/dice-hwdep.c190
-rw-r--r--sound/firewire/dice/dice-interface.h (renamed from sound/firewire/dice-interface.h)0
-rw-r--r--sound/firewire/dice/dice-midi.c157
-rw-r--r--sound/firewire/dice/dice-pcm.c422
-rw-r--r--sound/firewire/dice/dice-proc.c252
-rw-r--r--sound/firewire/dice/dice-stream.c407
-rw-r--r--sound/firewire/dice/dice-transaction.c382
-rw-r--r--sound/firewire/dice/dice.c361
-rw-r--r--sound/firewire/dice/dice.h189
-rw-r--r--sound/firewire/isight.c10
-rw-r--r--sound/firewire/oxfw/Makefile3
-rw-r--r--sound/firewire/oxfw/oxfw-command.c153
-rw-r--r--sound/firewire/oxfw/oxfw-control.c283
-rw-r--r--sound/firewire/oxfw/oxfw-hwdep.c190
-rw-r--r--sound/firewire/oxfw/oxfw-midi.c207
-rw-r--r--sound/firewire/oxfw/oxfw-pcm.c424
-rw-r--r--sound/firewire/oxfw/oxfw-proc.c113
-rw-r--r--sound/firewire/oxfw/oxfw-stream.c685
-rw-r--r--sound/firewire/oxfw/oxfw.c317
-rw-r--r--sound/firewire/oxfw/oxfw.h146
-rw-r--r--sound/firewire/speakers.c792
-rw-r--r--sound/i2c/other/ak4xxx-adda.c26
-rw-r--r--sound/isa/ad1816a/ad1816a_lib.c10
-rw-r--r--sound/isa/es1688/es1688_lib.c13
-rw-r--r--sound/isa/es18xx.c78
-rw-r--r--sound/isa/msnd/msnd_pinnacle_mixer.c11
-rw-r--r--sound/isa/sb/emu8000_synth.c3
-rw-r--r--sound/isa/sb/sb16_main.c10
-rw-r--r--sound/isa/sb/sb_common.c3
-rw-r--r--sound/isa/sb/sb_mixer.c31
-rw-r--r--sound/isa/wss/wss_lib.c16
-rw-r--r--sound/mips/sgio2audio.c11
-rw-r--r--sound/oss/uart401.c11
-rw-r--r--sound/parisc/harmony.c12
-rw-r--r--sound/pci/ac97/ac97_codec.c10
-rw-r--r--sound/pci/ac97/ac97_patch.c198
-rw-r--r--sound/pci/ac97/ac97_patch.h2
-rw-r--r--sound/pci/asihpi/asihpi.c377
-rw-r--r--sound/pci/asihpi/hpi.h16
-rw-r--r--sound/pci/asihpi/hpi6205.c43
-rw-r--r--sound/pci/asihpi/hpi_internal.h20
-rw-r--r--sound/pci/asihpi/hpicmn.c107
-rw-r--r--sound/pci/asihpi/hpicmn.h19
-rw-r--r--sound/pci/asihpi/hpimsginit.c30
-rw-r--r--sound/pci/asihpi/hpimsgx.c15
-rw-r--r--sound/pci/asihpi/hpioctl.c126
-rw-r--r--sound/pci/asihpi/hpios.h8
-rw-r--r--sound/pci/atiixp.c4
-rw-r--r--sound/pci/atiixp_modem.c4
-rw-r--r--sound/pci/au88x0/au88x0.c35
-rw-r--r--sound/pci/au88x0/au88x0.h4
-rw-r--r--sound/pci/au88x0/au88x0_a3d.c21
-rw-r--r--sound/pci/au88x0/au88x0_core.c102
-rw-r--r--sound/pci/au88x0/au88x0_eq.c3
-rw-r--r--sound/pci/au88x0/au88x0_game.c3
-rw-r--r--sound/pci/au88x0/au88x0_mpu401.c2
-rw-r--r--sound/pci/au88x0/au88x0_pcm.c8
-rw-r--r--sound/pci/au88x0/au88x0_synth.c17
-rw-r--r--sound/pci/aw2/aw2-alsa.c13
-rw-r--r--sound/pci/azt3328.c13
-rw-r--r--sound/pci/ca0106/ca0106_mixer.c40
-rw-r--r--sound/pci/ctxfi/ctatc.c10
-rw-r--r--sound/pci/ctxfi/ctdaio.c30
-rw-r--r--sound/pci/ctxfi/cttimer.c4
-rw-r--r--sound/pci/echoaudio/darla20_dsp.c5
-rw-r--r--sound/pci/echoaudio/darla24_dsp.c13
-rw-r--r--sound/pci/echoaudio/echo3g_dsp.c5
-rw-r--r--sound/pci/echoaudio/echoaudio.c145
-rw-r--r--sound/pci/echoaudio/echoaudio.h31
-rw-r--r--sound/pci/echoaudio/echoaudio_3g.c27
-rw-r--r--sound/pci/echoaudio/echoaudio_dsp.c109
-rw-r--r--sound/pci/echoaudio/echoaudio_gml.c11
-rw-r--r--sound/pci/echoaudio/gina20_dsp.c9
-rw-r--r--sound/pci/echoaudio/gina24_dsp.c30
-rw-r--r--sound/pci/echoaudio/indigo_dsp.c11
-rw-r--r--sound/pci/echoaudio/indigo_express_dsp.c6
-rw-r--r--sound/pci/echoaudio/indigodj_dsp.c11
-rw-r--r--sound/pci/echoaudio/indigodjx_dsp.c5
-rw-r--r--sound/pci/echoaudio/indigoio_dsp.c8
-rw-r--r--sound/pci/echoaudio/indigoiox_dsp.c5
-rw-r--r--sound/pci/echoaudio/layla20_dsp.c28
-rw-r--r--sound/pci/echoaudio/layla24_dsp.c26
-rw-r--r--sound/pci/echoaudio/mia_dsp.c15
-rw-r--r--sound/pci/echoaudio/midi.c23
-rw-r--r--sound/pci/echoaudio/mona_dsp.c29
-rw-r--r--sound/pci/emu10k1/emu10k1_main.c9
-rw-r--r--sound/pci/emu10k1/emu10k1x.c2
-rw-r--r--sound/pci/emu10k1/emufx.c3
-rw-r--r--sound/pci/emu10k1/emumixer.c58
-rw-r--r--sound/pci/emu10k1/p16v.c20
-rw-r--r--sound/pci/es1938.c10
-rw-r--r--sound/pci/es1968.c15
-rw-r--r--sound/pci/fm801.c10
-rw-r--r--sound/pci/hda/hda_auto_parser.c66
-rw-r--r--sound/pci/hda/hda_beep.c38
-rw-r--r--sound/pci/hda/hda_codec.c298
-rw-r--r--sound/pci/hda/hda_eld.c2
-rw-r--r--sound/pci/hda/hda_generic.c181
-rw-r--r--sound/pci/hda/hda_intel.c150
-rw-r--r--sound/pci/hda/hda_jack.c60
-rw-r--r--sound/pci/hda/hda_jack.h5
-rw-r--r--sound/pci/hda/hda_priv.h16
-rw-r--r--sound/pci/hda/hda_sysfs.c35
-rw-r--r--sound/pci/hda/patch_analog.c2
-rw-r--r--sound/pci/hda/patch_ca0132.c11
-rw-r--r--sound/pci/hda/patch_realtek.c109
-rw-r--r--sound/pci/ice1712/aureon.c46
-rw-r--r--sound/pci/ice1712/ews.c16
-rw-r--r--sound/pci/ice1712/hoontech.c6
-rw-r--r--sound/pci/ice1712/ice1712.c52
-rw-r--r--sound/pci/ice1712/ice1724.c18
-rw-r--r--sound/pci/ice1712/juli.c5
-rw-r--r--sound/pci/ice1712/maya44.c20
-rw-r--r--sound/pci/ice1712/phase.c12
-rw-r--r--sound/pci/ice1712/pontis.c8
-rw-r--r--sound/pci/ice1712/prodigy192.c22
-rw-r--r--sound/pci/ice1712/prodigy_hifi.c11
-rw-r--r--sound/pci/ice1712/quartet.c32
-rw-r--r--sound/pci/ice1712/revo.c4
-rw-r--r--sound/pci/ice1712/se.c9
-rw-r--r--sound/pci/korg1212/korg1212.c26
-rw-r--r--sound/pci/lola/lola.c2
-rw-r--r--sound/pci/lola/lola_mixer.c3
-rw-r--r--sound/pci/lx6464es/lx_defs.h2
-rw-r--r--sound/pci/mixart/mixart_hwdep.c9
-rw-r--r--sound/pci/pcxhr/pcxhr.c54
-rw-r--r--sound/pci/pcxhr/pcxhr_core.c10
-rw-r--r--sound/pci/pcxhr/pcxhr_mixer.c18
-rw-r--r--sound/pci/rme32.c34
-rw-r--r--sound/pci/rme96.c62
-rw-r--r--sound/pci/rme9652/hdsp.c178
-rw-r--r--sound/pci/rme9652/hdspm.c84
-rw-r--r--sound/pci/rme9652/rme9652.c58
-rw-r--r--sound/pci/sonicvibes.c10
-rw-r--r--sound/pci/trident/trident_main.c3
-rw-r--r--sound/pci/via82xx.c10
-rw-r--r--sound/pci/vx222/vx222_ops.c5
-rw-r--r--sound/pcmcia/vx/vxpocket.c1
-rw-r--r--sound/ppc/pmac.c3
-rw-r--r--sound/ppc/tumbler.c11
-rw-r--r--sound/soc/Makefile6
-rw-r--r--sound/soc/atmel/Kconfig9
-rw-r--r--sound/soc/atmel/Makefile1
-rw-r--r--sound/soc/atmel/atmel-pcm-dma.c4
-rw-r--r--sound/soc/atmel/atmel_ssc_dai.c5
-rw-r--r--sound/soc/atmel/snd-soc-afeb9260.c151
-rw-r--r--sound/soc/au1x/ac97c.c2
-rw-r--r--sound/soc/au1x/psc-ac97.c2
-rw-r--r--sound/soc/blackfin/bf5xx-ac97.c2
-rw-r--r--sound/soc/blackfin/bf5xx-ad1980.c2
-rw-r--r--sound/soc/cirrus/Kconfig3
-rw-r--r--sound/soc/cirrus/ep93xx-ac97.c2
-rw-r--r--sound/soc/codecs/Kconfig47
-rw-r--r--sound/soc/codecs/Makefile10
-rw-r--r--sound/soc/codecs/ab8500-codec.c32
-rw-r--r--sound/soc/codecs/ac97.c18
-rw-r--r--sound/soc/codecs/ad193x.c14
-rw-r--r--sound/soc/codecs/ad1980.c212
-rw-r--r--sound/soc/codecs/ad1980.h26
-rw-r--r--sound/soc/codecs/adau1373.c6
-rw-r--r--sound/soc/codecs/adau1701.c86
-rw-r--r--sound/soc/codecs/adau1761.c25
-rw-r--r--sound/soc/codecs/adau1781.c33
-rw-r--r--sound/soc/codecs/adau17x1.c71
-rw-r--r--sound/soc/codecs/adau17x1.h10
-rw-r--r--sound/soc/codecs/adav80x.c4
-rw-r--r--sound/soc/codecs/ak4535.c31
-rw-r--r--sound/soc/codecs/ak4641.c33
-rw-r--r--sound/soc/codecs/ak4642.c16
-rw-r--r--sound/soc/codecs/ak4671.c13
-rw-r--r--sound/soc/codecs/alc5623.c22
-rw-r--r--sound/soc/codecs/alc5632.c22
-rw-r--r--sound/soc/codecs/arizona.c34
-rw-r--r--sound/soc/codecs/cq93vc.c33
-rw-r--r--sound/soc/codecs/cs4265.c2
-rw-r--r--sound/soc/codecs/cs4271-i2c.c62
-rw-r--r--sound/soc/codecs/cs4271-spi.c55
-rw-r--r--sound/soc/codecs/cs4271.c155
-rw-r--r--sound/soc/codecs/cs4271.h11
-rw-r--r--sound/soc/codecs/cs42l51.c6
-rw-r--r--sound/soc/codecs/cs42l73.c6
-rw-r--r--sound/soc/codecs/hdmi.c2
-rw-r--r--sound/soc/codecs/lm49453.c8
-rw-r--r--sound/soc/codecs/max98088.c31
-rw-r--r--sound/soc/codecs/max98090.c201
-rw-r--r--sound/soc/codecs/max98090.h8
-rw-r--r--sound/soc/codecs/max98095.c23
-rw-r--r--sound/soc/codecs/max9850.c22
-rw-r--r--sound/soc/codecs/rt286.c231
-rw-r--r--sound/soc/codecs/rt5631.c38
-rw-r--r--sound/soc/codecs/rt5645.c200
-rw-r--r--sound/soc/codecs/rt5645.h19
-rw-r--r--sound/soc/codecs/rt5670.c136
-rw-r--r--sound/soc/codecs/rt5670.h6
-rw-r--r--sound/soc/codecs/rt5677-spi.c130
-rw-r--r--sound/soc/codecs/rt5677-spi.h21
-rw-r--r--sound/soc/codecs/rt5677.c1198
-rw-r--r--sound/soc/codecs/rt5677.h162
-rw-r--r--sound/soc/codecs/sgtl5000.c111
-rw-r--r--sound/soc/codecs/sigmadsp-i2c.c81
-rw-r--r--sound/soc/codecs/sigmadsp-regmap.c46
-rw-r--r--sound/soc/codecs/sigmadsp.c711
-rw-r--r--sound/soc/codecs/sigmadsp.h59
-rw-r--r--sound/soc/codecs/sirf-audio-codec.c6
-rw-r--r--sound/soc/codecs/sn95031.c15
-rw-r--r--sound/soc/codecs/ssm4567.c128
-rw-r--r--sound/soc/codecs/sta32x.c21
-rw-r--r--sound/soc/codecs/sta350.c21
-rw-r--r--sound/soc/codecs/sta529.c35
-rw-r--r--sound/soc/codecs/stac9766.c60
-rw-r--r--sound/soc/codecs/tas2552.c10
-rw-r--r--sound/soc/codecs/tfa9879.c328
-rw-r--r--sound/soc/codecs/tfa9879.h202
-rw-r--r--sound/soc/codecs/tlv320aic23.c21
-rw-r--r--sound/soc/codecs/tlv320aic31xx.c31
-rw-r--r--sound/soc/codecs/tlv320aic32x4.c24
-rw-r--r--sound/soc/codecs/tlv320aic3x.c228
-rw-r--r--sound/soc/codecs/tlv320aic3x.h1
-rw-r--r--sound/soc/codecs/tlv320dac33.c2
-rw-r--r--sound/soc/codecs/ts3a227e.c314
-rw-r--r--sound/soc/codecs/ts3a227e.h17
-rw-r--r--sound/soc/codecs/twl4030.c2
-rw-r--r--sound/soc/codecs/twl6040.c23
-rw-r--r--sound/soc/codecs/uda134x.c32
-rw-r--r--sound/soc/codecs/uda1380.c20
-rw-r--r--sound/soc/codecs/wl1273.c10
-rw-r--r--sound/soc/codecs/wm5102.c18
-rw-r--r--sound/soc/codecs/wm8350.c21
-rw-r--r--sound/soc/codecs/wm8400.c34
-rw-r--r--sound/soc/codecs/wm8510.c26
-rw-r--r--sound/soc/codecs/wm8523.c29
-rw-r--r--sound/soc/codecs/wm8580.c4
-rw-r--r--sound/soc/codecs/wm8711.c27
-rw-r--r--sound/soc/codecs/wm8728.c34
-rw-r--r--sound/soc/codecs/wm8731.c37
-rw-r--r--sound/soc/codecs/wm8737.c49
-rw-r--r--sound/soc/codecs/wm8750.c25
-rw-r--r--sound/soc/codecs/wm8776.c31
-rw-r--r--sound/soc/codecs/wm8804.c3
-rw-r--r--sound/soc/codecs/wm8900.c8
-rw-r--r--sound/soc/codecs/wm8903.c43
-rw-r--r--sound/soc/codecs/wm8940.c22
-rw-r--r--sound/soc/codecs/wm8955.c33
-rw-r--r--sound/soc/codecs/wm8958-dsp2.c12
-rw-r--r--sound/soc/codecs/wm8960.c113
-rw-r--r--sound/soc/codecs/wm8961.c34
-rw-r--r--sound/soc/codecs/wm8962.c11
-rw-r--r--sound/soc/codecs/wm8974.c25
-rw-r--r--sound/soc/codecs/wm8978.c10
-rw-r--r--sound/soc/codecs/wm8983.c27
-rw-r--r--sound/soc/codecs/wm8985.c28
-rw-r--r--sound/soc/codecs/wm8988.c27
-rw-r--r--sound/soc/codecs/wm8990.c24
-rw-r--r--sound/soc/codecs/wm8991.c32
-rw-r--r--sound/soc/codecs/wm8993.c12
-rw-r--r--sound/soc/codecs/wm8994.c4
-rw-r--r--sound/soc/codecs/wm8994.h2
-rw-r--r--sound/soc/codecs/wm8995.c17
-rw-r--r--sound/soc/codecs/wm9081.c7
-rw-r--r--sound/soc/codecs/wm9090.c32
-rw-r--r--sound/soc/codecs/wm9705.c46
-rw-r--r--sound/soc/codecs/wm9712.c219
-rw-r--r--sound/soc/codecs/wm9713.c230
-rw-r--r--sound/soc/codecs/wm_adsp.c97
-rw-r--r--sound/soc/davinci/davinci-mcasp.c339
-rw-r--r--sound/soc/davinci/davinci-mcasp.h17
-rw-r--r--sound/soc/dwc/designware_i2s.c46
-rw-r--r--sound/soc/fsl/eukrea-tlv320.c5
-rw-r--r--sound/soc/fsl/fsl-asoc-card.c19
-rw-r--r--sound/soc/fsl/fsl_dma.c9
-rw-r--r--sound/soc/fsl/fsl_esai.c12
-rw-r--r--sound/soc/fsl/fsl_ssi.c17
-rw-r--r--sound/soc/fsl/imx-sgtl5000.c6
-rw-r--r--sound/soc/fsl/imx-spdif.c3
-rw-r--r--sound/soc/fsl/imx-ssi.c2
-rw-r--r--sound/soc/fsl/imx-wm8962.c6
-rw-r--r--sound/soc/fsl/mpc5200_dma.c3
-rw-r--r--sound/soc/fsl/mpc5200_psc_ac97.c6
-rw-r--r--sound/soc/generic/simple-card.c160
-rw-r--r--sound/soc/intel/Kconfig44
-rw-r--r--sound/soc/intel/Makefile7
-rw-r--r--sound/soc/intel/broadwell.c64
-rw-r--r--sound/soc/intel/bytcr_dpcm_rt5640.c230
-rw-r--r--sound/soc/intel/cht_bsw_rt5672.c285
-rw-r--r--sound/soc/intel/haswell.c14
-rw-r--r--sound/soc/intel/sst-atom-controls.c1208
-rw-r--r--sound/soc/intel/sst-atom-controls.h428
-rw-r--r--sound/soc/intel/sst-baytrail-dsp.c24
-rw-r--r--sound/soc/intel/sst-dsp-priv.h136
-rw-r--r--sound/soc/intel/sst-dsp.c31
-rw-r--r--sound/soc/intel/sst-dsp.h28
-rw-r--r--sound/soc/intel/sst-firmware.c937
-rw-r--r--sound/soc/intel/sst-haswell-dsp.c295
-rw-r--r--sound/soc/intel/sst-haswell-ipc.c413
-rw-r--r--sound/soc/intel/sst-haswell-ipc.h25
-rw-r--r--sound/soc/intel/sst-haswell-pcm.c419
-rw-r--r--sound/soc/intel/sst-mfld-platform-compress.c8
-rw-r--r--sound/soc/intel/sst-mfld-platform-pcm.c154
-rw-r--r--sound/soc/intel/sst-mfld-platform.h5
-rw-r--r--sound/soc/intel/sst/Makefile7
-rw-r--r--sound/soc/intel/sst/sst.c437
-rw-r--r--sound/soc/intel/sst/sst.h546
-rw-r--r--sound/soc/intel/sst/sst_acpi.c383
-rw-r--r--sound/soc/intel/sst/sst_drv_interface.c686
-rw-r--r--sound/soc/intel/sst/sst_ipc.c373
-rw-r--r--sound/soc/intel/sst/sst_loader.c456
-rw-r--r--sound/soc/intel/sst/sst_pci.c209
-rw-r--r--sound/soc/intel/sst/sst_pvt.c449
-rw-r--r--sound/soc/intel/sst/sst_stream.c437
-rw-r--r--sound/soc/jz4740/qi_lb60.c11
-rw-r--r--sound/soc/mxs/mxs-saif.c2
-rw-r--r--sound/soc/mxs/mxs-sgtl5000.c7
-rw-r--r--sound/soc/nuc900/nuc900-ac97.c2
-rw-r--r--sound/soc/omap/Kconfig8
-rw-r--r--sound/soc/omap/mcbsp.c3
-rw-r--r--sound/soc/pxa/mioa701_wm9713.c7
-rw-r--r--sound/soc/pxa/pxa-ssp.c16
-rw-r--r--sound/soc/pxa/pxa2xx-ac97.c6
-rw-r--r--sound/soc/pxa/spitz.c52
-rw-r--r--sound/soc/rockchip/Kconfig9
-rw-r--r--sound/soc/samsung/Kconfig8
-rw-r--r--sound/soc/samsung/Makefile2
-rw-r--r--sound/soc/samsung/ac97.c4
-rw-r--r--sound/soc/samsung/arndale_rt5631.c150
-rw-r--r--sound/soc/samsung/i2s-regs.h10
-rw-r--r--sound/soc/samsung/i2s.c244
-rw-r--r--sound/soc/samsung/odroidx2_max98090.c4
-rw-r--r--sound/soc/sh/fsi.c9
-rw-r--r--sound/soc/sh/hac.c2
-rw-r--r--sound/soc/sh/rcar/adg.c2
-rw-r--r--sound/soc/sh/rcar/core.c236
-rw-r--r--sound/soc/sh/rcar/dvc.c215
-rw-r--r--sound/soc/sh/rcar/gen.c30
-rw-r--r--sound/soc/sh/rcar/rsnd.h92
-rw-r--r--sound/soc/sh/rcar/src.c101
-rw-r--r--sound/soc/sh/rcar/ssi.c233
-rw-r--r--sound/soc/soc-ac97.c256
-rw-r--r--sound/soc/soc-cache.c149
-rw-r--r--sound/soc/soc-compress.c11
-rw-r--r--sound/soc/soc-core.c1642
-rw-r--r--sound/soc/soc-dapm.c755
-rw-r--r--sound/soc/soc-jack.c11
-rw-r--r--sound/soc/soc-ops.c952
-rw-r--r--sound/soc/soc-pcm.c23
-rw-r--r--sound/soc/tegra/tegra20_ac97.c2
-rw-r--r--sound/soc/tegra/tegra_rt5640.c6
-rw-r--r--sound/soc/txx9/txx9aclc-ac97.c2
-rw-r--r--sound/soc/txx9/txx9aclc.c2
-rw-r--r--sound/soc/ux500/mop500.c8
-rw-r--r--sound/sparc/cs4231.c12
-rw-r--r--sound/usb/6fire/control.c22
-rw-r--r--sound/usb/6fire/firmware.c2
-rw-r--r--sound/usb/6fire/pcm.c17
-rw-r--r--sound/usb/Makefile1
-rw-r--r--sound/usb/card.c109
-rw-r--r--sound/usb/endpoint.c16
-rw-r--r--sound/usb/endpoint.h2
-rw-r--r--sound/usb/midi.c2
-rw-r--r--sound/usb/misc/ua101.c18
-rw-r--r--sound/usb/mixer.c241
-rw-r--r--sound/usb/mixer.h40
-rw-r--r--sound/usb/mixer_maps.c9
-rw-r--r--sound/usb/mixer_quirks.c920
-rw-r--r--sound/usb/mixer_scarlett.c1004
-rw-r--r--sound/usb/mixer_scarlett.h6
-rw-r--r--sound/usb/pcm.c5
-rw-r--r--sound/usb/quirks-table.h132
-rw-r--r--sound/usb/quirks.c72
-rw-r--r--sound/usb/quirks.h3
-rw-r--r--sound/usb/usx2y/usbusx2yaudio.c9
451 files changed, 27224 insertions, 11303 deletions
diff --git a/Documentation/DocBook/alsa-driver-api.tmpl b/Documentation/DocBook/alsa-driver-api.tmpl
index 0230a96f0564..71f9246127ec 100644
--- a/Documentation/DocBook/alsa-driver-api.tmpl
+++ b/Documentation/DocBook/alsa-driver-api.tmpl
@@ -57,6 +57,7 @@
!Esound/core/pcm.c
!Esound/core/pcm_lib.c
!Esound/core/pcm_native.c
+!Iinclude/sound/pcm.h
</sect1>
<sect1><title>PCM Format Helpers</title>
!Esound/core/pcm_misc.c
@@ -64,6 +65,10 @@
<sect1><title>PCM Memory Management</title>
!Esound/core/pcm_memory.c
</sect1>
+ <sect1><title>PCM DMA Engine API</title>
+!Esound/core/pcm_dmaengine.c
+!Iinclude/sound/dmaengine_pcm.h
+ </sect1>
</chapter>
<chapter><title>Control/Mixer API</title>
<sect1><title>General Control Interface</title>
@@ -91,12 +96,38 @@
!Esound/core/info.c
</sect1>
</chapter>
+ <chapter><title>Compress Offload</title>
+ <sect1><title>Compress Offload API</title>
+!Esound/core/compress_offload.c
+!Iinclude/uapi/sound/compress_offload.h
+!Iinclude/uapi/sound/compress_params.h
+!Iinclude/sound/compress_driver.h
+ </sect1>
+ </chapter>
+ <chapter><title>ASoC</title>
+ <sect1><title>ASoC Core API</title>
+!Iinclude/sound/soc.h
+!Esound/soc/soc-core.c
+!Esound/soc/soc-cache.c
+!Esound/soc/soc-devres.c
+!Esound/soc/soc-io.c
+!Esound/soc/soc-pcm.c
+ </sect1>
+ <sect1><title>ASoC DAPM API</title>
+!Esound/soc/soc-dapm.c
+ </sect1>
+ <sect1><title>ASoC DMA Engine API</title>
+!Esound/soc/soc-generic-dmaengine-pcm.c
+ </sect1>
+ </chapter>
<chapter><title>Miscellaneous Functions</title>
<sect1><title>Hardware-Dependent Devices API</title>
!Esound/core/hwdep.c
</sect1>
<sect1><title>Jack Abstraction Layer API</title>
+!Iinclude/sound/jack.h
!Esound/core/jack.c
+!Esound/soc/soc-jack.c
</sect1>
<sect1><title>ISA DMA Helpers</title>
!Esound/core/isadma.c
diff --git a/Documentation/DocBook/writing-an-alsa-driver.tmpl b/Documentation/DocBook/writing-an-alsa-driver.tmpl
index 784793df81ed..84ef6a90131c 100644
--- a/Documentation/DocBook/writing-an-alsa-driver.tmpl
+++ b/Documentation/DocBook/writing-an-alsa-driver.tmpl
@@ -3658,6 +3658,29 @@ struct _snd_pcm_runtime {
</para>
<para>
+ The above callback can be simplified with a helper function,
+ <function>snd_ctl_enum_info</function>. The final code
+ looks like below.
+ (You can pass ARRAY_SIZE(texts) instead of 4 in the third
+ argument; it's a matter of taste.)
+
+ <informalexample>
+ <programlisting>
+<![CDATA[
+ static int snd_myctl_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+ {
+ static char *texts[4] = {
+ "First", "Second", "Third", "Fourth"
+ };
+ return snd_ctl_enum_info(uinfo, 1, 4, texts);
+ }
+]]>
+ </programlisting>
+ </informalexample>
+ </para>
+
+ <para>
Some common info callbacks are available for your convenience:
<function>snd_ctl_boolean_mono_info()</function> and
<function>snd_ctl_boolean_stereo_info()</function>.
diff --git a/Documentation/devicetree/bindings/sound/arndale.txt b/Documentation/devicetree/bindings/sound/arndale.txt
new file mode 100644
index 000000000000..0e76946385ae
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/arndale.txt
@@ -0,0 +1,24 @@
+Audio Binding for Arndale boards
+
+Required properties:
+- compatible : Can be the following,
+ "samsung,arndale-rt5631"
+
+- samsung,audio-cpu: The phandle of the Samsung I2S controller
+- samsung,audio-codec: The phandle of the audio codec
+
+Optional:
+- samsung,model: The name of the sound-card
+
+Arndale Boards has many audio daughter cards, one of them is
+rt5631/alc5631. Below example shows audio bindings for rt5631/
+alc5631 based codec.
+
+Example:
+
+sound {
+ compatible = "samsung,arndale-rt5631";
+
+ samsung,audio-cpu = <&i2s0>
+ samsung,audio-codec = <&rt5631>;
+};
diff --git a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt
index 60ca07996458..46bc9829c71a 100644
--- a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt
+++ b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt
@@ -32,7 +32,7 @@ Optional properties:
- rx-num-evt : FIFO levels.
- sram-size-playback : size of sram to be allocated during playback
- sram-size-capture : size of sram to be allocated during capture
-- interrupts : Interrupt numbers for McASP, currently not used by the driver
+- interrupts : Interrupt numbers for McASP
- interrupt-names : Known interrupt names are "tx" and "rx"
- pinctrl-0: Should specify pin control group used for this controller.
- pinctrl-names: Should contain only one value - "default", for more details
diff --git a/Documentation/devicetree/bindings/sound/eukrea-tlv320.txt b/Documentation/devicetree/bindings/sound/eukrea-tlv320.txt
index 0d7985c864af..6dfa88c4dc1e 100644
--- a/Documentation/devicetree/bindings/sound/eukrea-tlv320.txt
+++ b/Documentation/devicetree/bindings/sound/eukrea-tlv320.txt
@@ -1,11 +1,16 @@
Audio complex for Eukrea boards with tlv320aic23 codec.
Required properties:
-- compatible : "eukrea,asoc-tlv320"
-- eukrea,model : The user-visible name of this sound complex.
-- ssi-controller : The phandle of the SSI controller.
-- fsl,mux-int-port : The internal port of the i.MX audio muxer (AUDMUX).
-- fsl,mux-ext-port : The external port of the i.MX audio muxer.
+
+ - compatible : "eukrea,asoc-tlv320"
+
+ - eukrea,model : The user-visible name of this sound complex.
+
+ - ssi-controller : The phandle of the SSI controller.
+
+ - fsl,mux-int-port : The internal port of the i.MX audio muxer (AUDMUX).
+
+ - fsl,mux-ext-port : The external port of the i.MX audio muxer.
Note: The AUDMUX port numbering should start at 1, which is consistent with
hardware manual.
diff --git a/Documentation/devicetree/bindings/sound/fsl,esai.txt b/Documentation/devicetree/bindings/sound/fsl,esai.txt
index 52f5b6bf3e8e..d3b6b5f48010 100644
--- a/Documentation/devicetree/bindings/sound/fsl,esai.txt
+++ b/Documentation/devicetree/bindings/sound/fsl,esai.txt
@@ -7,37 +7,39 @@ other DSPs. It has up to six transmitters and four receivers.
Required properties:
- - compatible : Compatible list, must contain "fsl,imx35-esai" or
- "fsl,vf610-esai"
+ - compatible : Compatible list, must contain "fsl,imx35-esai" or
+ "fsl,vf610-esai"
- - reg : Offset and length of the register set for the device.
+ - reg : Offset and length of the register set for the device.
- - interrupts : Contains the spdif interrupt.
+ - interrupts : Contains the spdif interrupt.
- - dmas : Generic dma devicetree binding as described in
- Documentation/devicetree/bindings/dma/dma.txt.
+ - dmas : Generic dma devicetree binding as described in
+ Documentation/devicetree/bindings/dma/dma.txt.
- - dma-names : Two dmas have to be defined, "tx" and "rx".
+ - dma-names : Two dmas have to be defined, "tx" and "rx".
- - clocks: Contains an entry for each entry in clock-names.
+ - clocks : Contains an entry for each entry in clock-names.
- - clock-names : Includes the following entries:
- "core" The core clock used to access registers
- "extal" The esai baud clock for esai controller used to derive
- HCK, SCK and FS.
- "fsys" The system clock derived from ahb clock used to derive
- HCK, SCK and FS.
+ - clock-names : Includes the following entries:
+ "core" The core clock used to access registers
+ "extal" The esai baud clock for esai controller used to
+ derive HCK, SCK and FS.
+ "fsys" The system clock derived from ahb clock used to
+ derive HCK, SCK and FS.
- - fsl,fifo-depth: The number of elements in the transmit and receive FIFOs.
- This number is the maximum allowed value for TFCR[TFWM] or RFCR[RFWM].
+ - fsl,fifo-depth : The number of elements in the transmit and receive
+ FIFOs. This number is the maximum allowed value for
+ TFCR[TFWM] or RFCR[RFWM].
- fsl,esai-synchronous: This is a boolean property. If present, indicating
- that ESAI would work in the synchronous mode, which means all the settings
- for Receiving would be duplicated from Transmition related registers.
+ that ESAI would work in the synchronous mode, which
+ means all the settings for Receiving would be
+ duplicated from Transmition related registers.
- - big-endian : If this property is absent, the native endian mode will
- be in use as default, or the big endian mode will be in use for all the
- device registers.
+ - big-endian : If this property is absent, the native endian mode
+ will be in use as default, or the big endian mode
+ will be in use for all the device registers.
Example:
diff --git a/Documentation/devicetree/bindings/sound/fsl,spdif.txt b/Documentation/devicetree/bindings/sound/fsl,spdif.txt
index 3e9e82c8eab3..b5ee32ee3706 100644
--- a/Documentation/devicetree/bindings/sound/fsl,spdif.txt
+++ b/Documentation/devicetree/bindings/sound/fsl,spdif.txt
@@ -6,32 +6,31 @@ a fibre cable.
Required properties:
- - compatible : Compatible list, must contain "fsl,imx35-spdif".
+ - compatible : Compatible list, must contain "fsl,imx35-spdif".
- - reg : Offset and length of the register set for the device.
+ - reg : Offset and length of the register set for the device.
- - interrupts : Contains the spdif interrupt.
+ - interrupts : Contains the spdif interrupt.
- - dmas : Generic dma devicetree binding as described in
- Documentation/devicetree/bindings/dma/dma.txt.
+ - dmas : Generic dma devicetree binding as described in
+ Documentation/devicetree/bindings/dma/dma.txt.
- - dma-names : Two dmas have to be defined, "tx" and "rx".
+ - dma-names : Two dmas have to be defined, "tx" and "rx".
- - clocks : Contains an entry for each entry in clock-names.
+ - clocks : Contains an entry for each entry in clock-names.
- - clock-names : Includes the following entries:
- "core" The core clock of spdif controller
- "rxtx<0-7>" Clock source list for tx and rx clock.
- This clock list should be identical to
- the source list connecting to the spdif
- clock mux in "SPDIF Transceiver Clock
- Diagram" of SoC reference manual. It
- can also be referred to TxClk_Source
- bit of register SPDIF_STC.
+ - clock-names : Includes the following entries:
+ "core" The core clock of spdif controller.
+ "rxtx<0-7>" Clock source list for tx and rx clock.
+ This clock list should be identical to the source
+ list connecting to the spdif clock mux in "SPDIF
+ Transceiver Clock Diagram" of SoC reference manual.
+ It can also be referred to TxClk_Source bit of
+ register SPDIF_STC.
- - big-endian : If this property is absent, the native endian mode will
- be in use as default, or the big endian mode will be in use for all the
- device registers.
+ - big-endian : If this property is absent, the native endian mode
+ will be in use as default, or the big endian mode
+ will be in use for all the device registers.
Example:
diff --git a/Documentation/devicetree/bindings/sound/fsl-sai.txt b/Documentation/devicetree/bindings/sound/fsl-sai.txt
index 4956b14d4b06..044e5d76e2dd 100644
--- a/Documentation/devicetree/bindings/sound/fsl-sai.txt
+++ b/Documentation/devicetree/bindings/sound/fsl-sai.txt
@@ -5,32 +5,48 @@ which provides a synchronous audio interface that supports fullduplex
serial interfaces with frame synchronization such as I2S, AC97, TDM, and
codec/DSP interfaces.
-
Required properties:
-- compatible: Compatible list, contains "fsl,vf610-sai" or "fsl,imx6sx-sai".
-- reg: Offset and length of the register set for the device.
-- clocks: Must contain an entry for each entry in clock-names.
-- clock-names : Must include the "bus" for register access and "mclk1" "mclk2"
- "mclk3" for bit clock and frame clock providing.
-- dmas : Generic dma devicetree binding as described in
- Documentation/devicetree/bindings/dma/dma.txt.
-- dma-names : Two dmas have to be defined, "tx" and "rx".
-- pinctrl-names: Must contain a "default" entry.
-- pinctrl-NNN: One property must exist for each entry in pinctrl-names.
- See ../pinctrl/pinctrl-bindings.txt for details of the property values.
-- big-endian: Boolean property, required if all the FTM_PWM registers
- are big-endian rather than little-endian.
-- lsb-first: Configures whether the LSB or the MSB is transmitted first for
- the fifo data. If this property is absent, the MSB is transmitted first as
- default, or the LSB is transmitted first.
-- fsl,sai-synchronous-rx: This is a boolean property. If present, indicating
- that SAI will work in the synchronous mode (sync Tx with Rx) which means
- both the transimitter and receiver will send and receive data by following
- receiver's bit clocks and frame sync clocks.
-- fsl,sai-asynchronous: This is a boolean property. If present, indicating
- that SAI will work in the asynchronous mode, which means both transimitter
- and receiver will send and receive data by following their own bit clocks
- and frame sync clocks separately.
+
+ - compatible : Compatible list, contains "fsl,vf610-sai" or
+ "fsl,imx6sx-sai".
+
+ - reg : Offset and length of the register set for the device.
+
+ - clocks : Must contain an entry for each entry in clock-names.
+
+ - clock-names : Must include the "bus" for register access and
+ "mclk1", "mclk2", "mclk3" for bit clock and frame
+ clock providing.
+ - dmas : Generic dma devicetree binding as described in
+ Documentation/devicetree/bindings/dma/dma.txt.
+
+ - dma-names : Two dmas have to be defined, "tx" and "rx".
+
+ - pinctrl-names : Must contain a "default" entry.
+
+ - pinctrl-NNN : One property must exist for each entry in
+ pinctrl-names. See ../pinctrl/pinctrl-bindings.txt
+ for details of the property values.
+
+ - big-endian : Boolean property, required if all the FTM_PWM
+ registers are big-endian rather than little-endian.
+
+ - lsb-first : Configures whether the LSB or the MSB is transmitted
+ first for the fifo data. If this property is absent,
+ the MSB is transmitted first as default, or the LSB
+ is transmitted first.
+
+ - fsl,sai-synchronous-rx: This is a boolean property. If present, indicating
+ that SAI will work in the synchronous mode (sync Tx
+ with Rx) which means both the transimitter and the
+ receiver will send and receive data by following
+ receiver's bit clocks and frame sync clocks.
+
+ - fsl,sai-asynchronous: This is a boolean property. If present, indicating
+ that SAI will work in the asynchronous mode, which
+ means both transimitter and receiver will send and
+ receive data by following their own bit clocks and
+ frame sync clocks separately.
Note:
- If both fsl,sai-asynchronous and fsl,sai-synchronous-rx are absent, the
diff --git a/Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt b/Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt
index e4acdd891e49..2f89db88fd57 100644
--- a/Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt
+++ b/Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt
@@ -1,33 +1,40 @@
Freescale i.MX audio complex with SGTL5000 codec
Required properties:
-- compatible : "fsl,imx-audio-sgtl5000"
-- model : The user-visible name of this sound complex
-- ssi-controller : The phandle of the i.MX SSI controller
-- audio-codec : The phandle of the SGTL5000 audio codec
-- audio-routing : A list of the connections between audio components.
- Each entry is a pair of strings, the first being the connection's sink,
- the second being the connection's source. Valid names could be power
- supplies, SGTL5000 pins, and the jacks on the board:
-
- Power supplies:
- * Mic Bias
-
- SGTL5000 pins:
- * MIC_IN
- * LINE_IN
- * HP_OUT
- * LINE_OUT
-
- Board connectors:
- * Mic Jack
- * Line In Jack
- * Headphone Jack
- * Line Out Jack
- * Ext Spk
-
-- mux-int-port : The internal port of the i.MX audio muxer (AUDMUX)
-- mux-ext-port : The external port of the i.MX audio muxer
+
+ - compatible : "fsl,imx-audio-sgtl5000"
+
+ - model : The user-visible name of this sound complex
+
+ - ssi-controller : The phandle of the i.MX SSI controller
+
+ - audio-codec : The phandle of the SGTL5000 audio codec
+
+ - audio-routing : A list of the connections between audio components.
+ Each entry is a pair of strings, the first being the
+ connection's sink, the second being the connection's
+ source. Valid names could be power supplies, SGTL5000
+ pins, and the jacks on the board:
+
+ Power supplies:
+ * Mic Bias
+
+ SGTL5000 pins:
+ * MIC_IN
+ * LINE_IN
+ * HP_OUT
+ * LINE_OUT
+
+ Board connectors:
+ * Mic Jack
+ * Line In Jack
+ * Headphone Jack
+ * Line Out Jack
+ * Ext Spk
+
+ - mux-int-port : The internal port of the i.MX audio muxer (AUDMUX)
+
+ - mux-ext-port : The external port of the i.MX audio muxer
Note: The AUDMUX port numbering should start at 1, which is consistent with
hardware manual.
diff --git a/Documentation/devicetree/bindings/sound/imx-audio-spdif.txt b/Documentation/devicetree/bindings/sound/imx-audio-spdif.txt
index 7d13479f9c3c..da84a442ccea 100644
--- a/Documentation/devicetree/bindings/sound/imx-audio-spdif.txt
+++ b/Documentation/devicetree/bindings/sound/imx-audio-spdif.txt
@@ -2,23 +2,25 @@ Freescale i.MX audio complex with S/PDIF transceiver
Required properties:
- - compatible : "fsl,imx-audio-spdif"
+ - compatible : "fsl,imx-audio-spdif"
- - model : The user-visible name of this sound complex
+ - model : The user-visible name of this sound complex
- - spdif-controller : The phandle of the i.MX S/PDIF controller
+ - spdif-controller : The phandle of the i.MX S/PDIF controller
Optional properties:
- - spdif-out : This is a boolean property. If present, the transmitting
- function of S/PDIF will be enabled, indicating there's a physical
- S/PDIF out connector/jack on the board or it's connecting to some
- other IP block, such as an HDMI encoder/display-controller.
+ - spdif-out : This is a boolean property. If present, the
+ transmitting function of S/PDIF will be enabled,
+ indicating there's a physical S/PDIF out connector
+ or jack on the board or it's connecting to some
+ other IP block, such as an HDMI encoder or
+ display-controller.
- - spdif-in : This is a boolean property. If present, the receiving
- function of S/PDIF will be enabled, indicating there's a physical
- S/PDIF in connector/jack on the board.
+ - spdif-in : This is a boolean property. If present, the receiving
+ function of S/PDIF will be enabled, indicating there
+ is a physical S/PDIF in connector/jack on the board.
* Note: At least one of these two properties should be set in the DT binding.
diff --git a/Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt b/Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt
index f49450a87890..acea71bee34f 100644
--- a/Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt
+++ b/Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt
@@ -1,25 +1,32 @@
Freescale i.MX audio complex with WM8962 codec
Required properties:
-- compatible : "fsl,imx-audio-wm8962"
-- model : The user-visible name of this sound complex
-- ssi-controller : The phandle of the i.MX SSI controller
-- audio-codec : The phandle of the WM8962 audio codec
-- audio-routing : A list of the connections between audio components.
- Each entry is a pair of strings, the first being the connection's sink,
- the second being the connection's source. Valid names could be power
- supplies, WM8962 pins, and the jacks on the board:
-
- Power supplies:
- * Mic Bias
-
- Board connectors:
- * Mic Jack
- * Headphone Jack
- * Ext Spk
-
-- mux-int-port : The internal port of the i.MX audio muxer (AUDMUX)
-- mux-ext-port : The external port of the i.MX audio muxer
+
+ - compatible : "fsl,imx-audio-wm8962"
+
+ - model : The user-visible name of this sound complex
+
+ - ssi-controller : The phandle of the i.MX SSI controller
+
+ - audio-codec : The phandle of the WM8962 audio codec
+
+ - audio-routing : A list of the connections between audio components.
+ Each entry is a pair of strings, the first being the
+ connection's sink, the second being the connection's
+ source. Valid names could be power supplies, WM8962
+ pins, and the jacks on the board:
+
+ Power supplies:
+ * Mic Bias
+
+ Board connectors:
+ * Mic Jack
+ * Headphone Jack
+ * Ext Spk
+
+ - mux-int-port : The internal port of the i.MX audio muxer (AUDMUX)
+
+ - mux-ext-port : The external port of the i.MX audio muxer
Note: The AUDMUX port numbering should start at 1, which is consistent with
hardware manual.
diff --git a/Documentation/devicetree/bindings/sound/imx-audmux.txt b/Documentation/devicetree/bindings/sound/imx-audmux.txt
index f88a00e54c63..b30a737e209e 100644
--- a/Documentation/devicetree/bindings/sound/imx-audmux.txt
+++ b/Documentation/devicetree/bindings/sound/imx-audmux.txt
@@ -1,18 +1,24 @@
Freescale Digital Audio Mux (AUDMUX) device
Required properties:
-- compatible : "fsl,imx21-audmux" for AUDMUX version firstly used on i.MX21,
- or "fsl,imx31-audmux" for the version firstly used on i.MX31.
-- reg : Should contain AUDMUX registers location and length
+
+ - compatible : "fsl,imx21-audmux" for AUDMUX version firstly used
+ on i.MX21, or "fsl,imx31-audmux" for the version
+ firstly used on i.MX31.
+
+ - reg : Should contain AUDMUX registers location and length.
An initial configuration can be setup using child nodes.
Required properties of optional child nodes:
-- fsl,audmux-port : Integer of the audmux port that is configured by this
- child node.
-- fsl,port-config : List of configuration options for the specific port. For
- imx31-audmux and above, it is a list of tuples <ptcr pdcr>. For
- imx21-audmux it is a list of pcr values.
+
+ - fsl,audmux-port : Integer of the audmux port that is configured by this
+ child node.
+
+ - fsl,port-config : List of configuration options for the specific port.
+ For imx31-audmux and above, it is a list of tuples
+ <ptcr pdcr>. For imx21-audmux it is a list of pcr
+ values.
Example:
diff --git a/Documentation/devicetree/bindings/sound/max98090.txt b/Documentation/devicetree/bindings/sound/max98090.txt
index c454e67f54bb..aa802a274520 100644
--- a/Documentation/devicetree/bindings/sound/max98090.txt
+++ b/Documentation/devicetree/bindings/sound/max98090.txt
@@ -16,6 +16,8 @@ Optional properties:
- clock-names: Should be "mclk"
+- maxim,dmic-freq: Frequency at which to clock DMIC
+
Pins on the device (for linking into audio routes):
* MIC1
diff --git a/Documentation/devicetree/bindings/sound/renesas,fsi.txt b/Documentation/devicetree/bindings/sound/renesas,fsi.txt
index c5be003f413e..0d0ab51105b0 100644
--- a/Documentation/devicetree/bindings/sound/renesas,fsi.txt
+++ b/Documentation/devicetree/bindings/sound/renesas,fsi.txt
@@ -1,11 +1,16 @@
Renesas FSI
Required properties:
-- compatible : "renesas,sh_fsi2" or "renesas,sh_fsi"
+- compatible : "renesas,fsi2-<soctype>",
+ "renesas,sh_fsi2" or "renesas,sh_fsi" as
+ fallback.
+ Examples with soctypes are:
+ - "renesas,fsi2-r8a7740" (R-Mobile A1)
+ - "renesas,fsi2-sh73a0" (SH-Mobile AG5)
- reg : Should contain the register physical address and length
- interrupts : Should contain FSI interrupt
-- fsia,spdif-connection : FSI is connected by S/PDFI
+- fsia,spdif-connection : FSI is connected by S/PDIF
- fsia,stream-mode-support : FSI supports 16bit stream mode.
- fsia,use-internal-clock : FSI uses internal clock when master mode.
diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt
index aa697abf337e..2dd690bc19cc 100644
--- a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt
+++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt
@@ -1,8 +1,12 @@
Renesas R-Car sound
Required properties:
-- compatible : "renesas,rcar_sound-gen1" if generation1
+- compatible : "renesas,rcar_sound-<soctype>", fallbacks
+ "renesas,rcar_sound-gen1" if generation1, and
"renesas,rcar_sound-gen2" if generation2
+ Examples with soctypes are:
+ - "renesas,rcar_sound-r8a7790" (R-Car H2)
+ - "renesas,rcar_sound-r8a7791" (R-Car M2-W)
- reg : Should contain the register physical address.
required register is
SRU/ADG/SSI if generation1
@@ -35,9 +39,9 @@ DAI subnode properties:
Example:
-rcar_sound: rcar_sound@0xffd90000 {
+rcar_sound: rcar_sound@ec500000 {
#sound-dai-cells = <1>;
- compatible = "renesas,rcar_sound-gen2";
+ compatible = "renesas,rcar_sound-r8a7791", "renesas,rcar_sound-gen2";
reg = <0 0xec500000 0 0x1000>, /* SCU */
<0 0xec5a0000 0 0x100>, /* ADG */
<0 0xec540000 0 0x1000>, /* SSIU */
diff --git a/Documentation/devicetree/bindings/sound/rt5631.txt b/Documentation/devicetree/bindings/sound/rt5631.txt
new file mode 100644
index 000000000000..92b986ca337b
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/rt5631.txt
@@ -0,0 +1,48 @@
+ALC5631/RT5631 audio CODEC
+
+This device supports I2C only.
+
+Required properties:
+
+ - compatible : "realtek,alc5631" or "realtek,rt5631"
+
+ - reg : the I2C address of the device.
+
+Pins on the device (for linking into audio routes):
+
+ * SPK_OUT_R_P
+ * SPK_OUT_R_N
+ * SPK_OUT_L_P
+ * SPK_OUT_L_N
+ * HP_OUT_L
+ * HP_OUT_R
+ * AUX_OUT2_LP
+ * AUX_OUT2_RN
+ * AUX_OUT1_LP
+ * AUX_OUT1_RN
+ * AUX_IN_L_JD
+ * AUX_IN_R_JD
+ * MONO_IN_P
+ * MONO_IN_N
+ * MIC1_P
+ * MIC1_N
+ * MIC2_P
+ * MIC2_N
+ * MONO_OUT_P
+ * MONO_OUT_N
+ * MICBIAS1
+ * MICBIAS2
+
+Example:
+
+alc5631: alc5631@1a {
+ compatible = "realtek,alc5631";
+ reg = <0x1a>;
+};
+
+or
+
+rt5631: rt5631@1a {
+ compatible = "realtek,rt5631";
+ reg = <0x1a>;
+};
diff --git a/Documentation/devicetree/bindings/sound/rt5677.txt b/Documentation/devicetree/bindings/sound/rt5677.txt
index 0701b834fc73..740ff771aa8b 100644
--- a/Documentation/devicetree/bindings/sound/rt5677.txt
+++ b/Documentation/devicetree/bindings/sound/rt5677.txt
@@ -27,6 +27,21 @@ Optional properties:
Boolean. Indicate MIC1/2 input and LOUT1/2/3 outputs are differential,
rather than single-ended.
+- realtek,gpio-config
+ Array of six 8bit elements that configures GPIO.
+ 0 - floating (reset value)
+ 1 - pull down
+ 2 - pull up
+
+- realtek,jd1-gpio
+ Configures GPIO Mic Jack detection 1.
+ Select 0 ~ 3 as OFF, GPIO1, GPIO2 and GPIO3 respectively.
+
+- realtek,jd2-gpio
+- realtek,jd3-gpio
+ Configures GPIO Mic Jack detection 2 and 3.
+ Select 0 ~ 3 as OFF, GPIO4, GPIO5 and GPIO6 respectively.
+
Pins on the device (for linking into audio routes):
* IN1P
@@ -56,4 +71,6 @@ rt5677 {
realtek,pow-ldo2-gpio =
<&gpio TEGRA_GPIO(V, 3) GPIO_ACTIVE_HIGH>;
realtek,in1-differential = "true";
+ realtek,gpio-config = /bits/ 8 <0 0 0 0 0 2>; /* pull up GPIO6 */
+ realtek,jd2-gpio = <3>; /* Enables Jack detection for GPIO6 */
};
diff --git a/Documentation/devicetree/bindings/sound/samsung-i2s.txt b/Documentation/devicetree/bindings/sound/samsung-i2s.txt
index 7386d444ada1..d188296bb6ec 100644
--- a/Documentation/devicetree/bindings/sound/samsung-i2s.txt
+++ b/Documentation/devicetree/bindings/sound/samsung-i2s.txt
@@ -6,10 +6,17 @@ Required SoC Specific Properties:
- samsung,s3c6410-i2s: for 8/16/24bit stereo I2S.
- samsung,s5pv210-i2s: for 8/16/24bit multichannel(5.1) I2S with
secondary fifo, s/w reset control and internal mux for root clk src.
- - samsung,exynos5420-i2s: for 8/16/24bit multichannel(7.1) I2S with
- secondary fifo, s/w reset control, internal mux for root clk src and
- TDM support. TDM (Time division multiplexing) is to allow transfer of
- multiple channel audio data on single data line.
+ - samsung,exynos5420-i2s: for 8/16/24bit multichannel(5.1) I2S for
+ playback, sterio channel capture, secondary fifo using internal
+ or external dma, s/w reset control, internal mux for root clk src
+ and 7.1 channel TDM support for playback. TDM (Time division multiplexing)
+ is to allow transfer of multiple channel audio data on single data line.
+ - samsung,exynos7-i2s: with all the available features of exynos5 i2s,
+ exynos7 I2S has 7.1 channel TDM support for capture, secondary fifo
+ with only external dma and more no.of root clk sampling frequencies.
+ - samsung,exynos7-i2s1: I2S1 on previous samsung platforms supports
+ stereo channels. exynos7 i2s1 upgraded to 5.1 multichannel with
+ slightly modified bit offsets.
- reg: physical base address of the controller and length of memory mapped
region.
diff --git a/Documentation/devicetree/bindings/sound/sgtl5000.txt b/Documentation/devicetree/bindings/sound/sgtl5000.txt
index d556dcb8816b..0e5e4eb3ef1b 100644
--- a/Documentation/devicetree/bindings/sound/sgtl5000.txt
+++ b/Documentation/devicetree/bindings/sound/sgtl5000.txt
@@ -7,6 +7,17 @@ Required properties:
- clocks : the clock provider of SYS_MCLK
+- micbias-resistor-k-ohms : the bias resistor to be used in kOmhs
+ The resistor can take values of 2k, 4k or 8k.
+ If set to 0 it will be off.
+ If this node is not mentioned or if the value is unknown, then
+ micbias resistor is set to 4K.
+
+- micbias-voltage-m-volts : the bias voltage to be used in mVolts
+ The voltage can take values from 1.25V to 3V by 250mV steps
+ If this node is not mentionned or the value is unknown, then
+ the value is set to 1.25V.
+
- VDDA-supply : the regulator provider of VDDA
- VDDIO-supply: the regulator provider of VDDIO
@@ -21,6 +32,8 @@ codec: sgtl5000@0a {
compatible = "fsl,sgtl5000";
reg = <0x0a>;
clocks = <&clks 150>;
+ micbias-resistor-k-ohms = <2>;
+ micbias-voltage-m-volts = <2250>;
VDDA-supply = <&reg_3p3v>;
VDDIO-supply = <&reg_3p3v>;
};
diff --git a/Documentation/devicetree/bindings/sound/ts3a227e.txt b/Documentation/devicetree/bindings/sound/ts3a227e.txt
new file mode 100644
index 000000000000..e8bf23eb1803
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/ts3a227e.txt
@@ -0,0 +1,26 @@
+Texas Instruments TS3A227E
+Autonomous Audio Accessory Detection and Configuration Switch
+
+The TS3A227E detect headsets of 3-ring and 4-ring standards and
+switches automatically to route the microphone correctly. It also
+handles key press detection in accordance with the Android audio
+headset specification v1.0.
+
+Required properties:
+
+ - compatible: Should contain "ti,ts3a227e".
+ - reg: The i2c address. Should contain <0x3b>.
+ - interrupt-parent: The parent interrupt controller
+ - interrupts: Interrupt number for /INT pin from the 227e
+
+
+Examples:
+
+ i2c {
+ ts3a227e@3b {
+ compatible = "ti,ts3a227e";
+ reg = <0x3b>;
+ interrupt-parent = <&gpio>;
+ interrupts = <3 IRQ_TYPE_LEVEL_LOW>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/wm8960.txt b/Documentation/devicetree/bindings/sound/wm8960.txt
new file mode 100644
index 000000000000..2deb8a3da9c5
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/wm8960.txt
@@ -0,0 +1,31 @@
+WM8960 audio CODEC
+
+This device supports I2C only.
+
+Required properties:
+
+ - compatible : "wlf,wm8960"
+
+ - reg : the I2C address of the device.
+
+Optional properties:
+ - wlf,shared-lrclk: This is a boolean property. If present, the LRCM bit of
+ R24 (Additional control 2) gets set, indicating that ADCLRC and DACLRC pins
+ will be disabled only when ADC (Left and Right) and DAC (Left and Right)
+ are disabled.
+ When wm8960 works on synchronize mode and DACLRC pin is used to supply
+ frame clock, it will no frame clock for captrue unless enable DAC to enable
+ DACLRC pin. If shared-lrclk is present, no need to enable DAC for captrue.
+
+ - wlf,capless: This is a boolean property. If present, OUT3 pin will be
+ enabled and disabled together with HP_L and HP_R pins in response to jack
+ detect events.
+
+Example:
+
+codec: wm8960@1a {
+ compatible = "wlf,wm8960";
+ reg = <0x1a>;
+
+ wlf,shared-lrclk;
+};
diff --git a/Documentation/sound/alsa/ControlNames.txt b/Documentation/sound/alsa/ControlNames.txt
index fea65bb6269e..79a6127863ca 100644
--- a/Documentation/sound/alsa/ControlNames.txt
+++ b/Documentation/sound/alsa/ControlNames.txt
@@ -1,6 +1,6 @@
This document describes standard names of mixer controls.
-Syntax: SOURCE [DIRECTION] FUNCTION
+Syntax: [LOCATION] SOURCE [CHANNEL] [DIRECTION] FUNCTION
DIRECTION:
<nothing> (both directions)
@@ -14,12 +14,29 @@ FUNCTION:
Volume
Route (route control, hardware specific)
+CHANNEL:
+ <nothing> (channel independent, or applies to all channels)
+ Front
+ Surround (rear left/right in 4.0/5.1 surround)
+ CLFE
+ Center
+ LFE
+ Side (side left/right for 7.1 surround)
+
+LOCATION: (physical location of source)
+ Front
+ Rear
+ Dock (docking station)
+ Internal
+
SOURCE:
Master
Master Mono
Hardware Master
Speaker (internal speaker)
+ Bass Speaker (internal LFE speaker)
Headphone
+ Line Out
Beep (beep generator)
Phone
Phone Input
@@ -27,14 +44,14 @@ SOURCE:
Synth
FM
Mic
- Line
+ Headset Mic (mic part of combined headset jack - 4-pin headphone + mic)
+ Headphone Mic (mic part of either/or - 3-pin headphone or mic)
+ Line (input only, use "Line Out" for output)
CD
Video
Zoom Video
Aux
PCM
- PCM Front
- PCM Rear
PCM Pan
Loopback
Analog Loopback (D/A -> A/D loopback)
@@ -47,8 +64,13 @@ SOURCE:
Music
I2S
IEC958
+ HDMI
+ SPDIF (output only)
+ SPDIF In
+ Digital In
+ HDMI/DP (either HDMI or DisplayPort)
-Exceptions:
+Exceptions (deprecated):
[Digital] Capture Source
[Digital] Capture Switch (aka input gain switch)
[Digital] Capture Volume (aka input gain volume)
diff --git a/Documentation/sound/alsa/HD-Audio-Models.txt b/Documentation/sound/alsa/HD-Audio-Models.txt
index a5e754714344..5a3163cac6c3 100644
--- a/Documentation/sound/alsa/HD-Audio-Models.txt
+++ b/Documentation/sound/alsa/HD-Audio-Models.txt
@@ -113,14 +113,10 @@ AD1984
AD1986A
=======
- 6stack 6-jack, separate surrounds (default)
3stack 3-stack, shared surrounds
laptop 2-channel only (FSC V2060, Samsung M50)
- laptop-eapd 2-channel with EAPD (ASUS A6J)
- laptop-automute 2-channel with EAPD and HP-automute (Lenovo N100)
- ultra 2-channel with EAPD (Samsung Ultra tablet PC)
- samsung 2-channel with EAPD (Samsung R65)
- samsung-p50 2-channel with HP-automute (Samsung P50)
+ laptop-imic 2-channel with built-in mic
+ eapd Turn on EAPD constantly
AD1988/AD1988B/AD1989A/AD1989B
==============================
diff --git a/Documentation/sound/alsa/Procfile.txt b/Documentation/sound/alsa/Procfile.txt
index 7fcd1ad96fcc..7f8a0d325905 100644
--- a/Documentation/sound/alsa/Procfile.txt
+++ b/Documentation/sound/alsa/Procfile.txt
@@ -101,10 +101,6 @@ card*/pcm*/xrun_debug
bit 0 = Enable XRUN/jiffies debug messages
bit 1 = Show stack trace at XRUN / jiffies check
bit 2 = Enable additional jiffies check
- bit 3 = Log hwptr update at each period interrupt
- bit 4 = Log hwptr update at each snd_pcm_update_hw_ptr()
- bit 5 = Show last 10 positions on error
- bit 6 = Do above only once
When the bit 0 is set, the driver will show the messages to
kernel log when an xrun is detected. The debug message is
@@ -121,15 +117,6 @@ card*/pcm*/xrun_debug
buggy) hardware that doesn't give smooth pointer updates.
This feature is enabled via the bit 2.
- Bits 3 and 4 are for logging the hwptr records. Note that
- these will give flood of kernel messages.
-
- When bit 5 is set, the driver logs the last 10 xrun errors and
- the proc file shows each jiffies, position, period_size,
- buffer_size, old_hw_ptr, and hw_ptr_base values.
-
- When bit 6 is set, the full xrun log is shown only once.
-
card*/pcm*/sub*/info
The general information of this PCM sub-stream.
@@ -146,6 +133,10 @@ card*/pcm*/sub*/sw_params
card*/pcm*/sub*/prealloc
The buffer pre-allocation information.
+card*/pcm*/sub*/xrun_injection
+ Triggers an XRUN to the running stream when any value is
+ written to this proc file. Used for fault injection.
+ This entry is write-only.
AC97 Codec Information
----------------------
diff --git a/MAINTAINERS b/MAINTAINERS
index 7bff8087fce7..132958a4e27c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6682,6 +6682,12 @@ S: Supported
F: drivers/gpu/drm/i2c/tda998x_drv.c
F: include/drm/i2c/tda998x.h
+NXP TFA9879 DRIVER
+M: Peter Rosin <peda@axentia.se>
+L: alsa-devel@alsa-project.org (moderated for non-subscribers)
+S: Maintained
+F: sound/soc/codecs/tfa9879*
+
OMAP SUPPORT
M: Tony Lindgren <tony@atomide.com>
L: linux-omap@vger.kernel.org
diff --git a/arch/arm/mach-pxa/spitz.c b/arch/arm/mach-pxa/spitz.c
index 840c3a48e720..962a7f31f596 100644
--- a/arch/arm/mach-pxa/spitz.c
+++ b/arch/arm/mach-pxa/spitz.c
@@ -924,6 +924,14 @@ static inline void spitz_i2c_init(void) {}
#endif
/******************************************************************************
+ * Audio devices
+ ******************************************************************************/
+static inline void spitz_audio_init(void)
+{
+ platform_device_register_simple("spitz-audio", -1, NULL, 0);
+}
+
+/******************************************************************************
* Machine init
******************************************************************************/
static void spitz_poweroff(void)
@@ -970,6 +978,7 @@ static void __init spitz_init(void)
spitz_nor_init();
spitz_nand_init();
spitz_i2c_init();
+ spitz_audio_init();
}
static void __init spitz_fixup(struct tag *tags, char **cmdline)
diff --git a/arch/x86/include/asm/platform_sst_audio.h b/arch/x86/include/asm/platform_sst_audio.h
index 0a4e140315b6..7249e6d0902d 100644
--- a/arch/x86/include/asm/platform_sst_audio.h
+++ b/arch/x86/include/asm/platform_sst_audio.h
@@ -16,6 +16,9 @@
#include <linux/sfi.h>
+#define MAX_NUM_STREAMS_MRFLD 25
+#define MAX_NUM_STREAMS MAX_NUM_STREAMS_MRFLD
+
enum sst_audio_task_id_mrfld {
SST_TASK_ID_NONE = 0,
SST_TASK_ID_SBA = 1,
@@ -73,6 +76,65 @@ struct sst_platform_data {
unsigned int strm_map_size;
};
+struct sst_info {
+ u32 iram_start;
+ u32 iram_end;
+ bool iram_use;
+ u32 dram_start;
+ u32 dram_end;
+ bool dram_use;
+ u32 imr_start;
+ u32 imr_end;
+ bool imr_use;
+ u32 mailbox_start;
+ bool use_elf;
+ bool lpe_viewpt_rqd;
+ unsigned int max_streams;
+ u32 dma_max_len;
+ u8 num_probes;
+};
+
+struct sst_lib_dnld_info {
+ unsigned int mod_base;
+ unsigned int mod_end;
+ unsigned int mod_table_offset;
+ unsigned int mod_table_size;
+ bool mod_ddr_dnld;
+};
+
+struct sst_res_info {
+ unsigned int shim_offset;
+ unsigned int shim_size;
+ unsigned int shim_phy_addr;
+ unsigned int ssp0_offset;
+ unsigned int ssp0_size;
+ unsigned int dma0_offset;
+ unsigned int dma0_size;
+ unsigned int dma1_offset;
+ unsigned int dma1_size;
+ unsigned int iram_offset;
+ unsigned int iram_size;
+ unsigned int dram_offset;
+ unsigned int dram_size;
+ unsigned int mbox_offset;
+ unsigned int mbox_size;
+ unsigned int acpi_lpe_res_index;
+ unsigned int acpi_ddr_index;
+ unsigned int acpi_ipc_irq_index;
+};
+
+struct sst_ipc_info {
+ int ipc_offset;
+ unsigned int mbox_recv_off;
+};
+
+struct sst_platform_info {
+ const struct sst_info *probe_data;
+ const struct sst_ipc_info *ipc_info;
+ const struct sst_res_info *res_info;
+ const struct sst_lib_dnld_info *lib_info;
+ const char *platform;
+};
int add_sst_platform_device(void);
#endif
diff --git a/drivers/media/pci/saa7134/saa7134-alsa.c b/drivers/media/pci/saa7134/saa7134-alsa.c
index 40569894c1c9..ac3cd74e824e 100644
--- a/drivers/media/pci/saa7134/saa7134-alsa.c
+++ b/drivers/media/pci/saa7134/saa7134-alsa.c
@@ -173,9 +173,7 @@ static void saa7134_irq_alsa_done(struct saa7134_dev *dev,
dprintk("irq: overrun [full=%d/%d] - Blocks in %d\n",dev->dmasound.read_count,
dev->dmasound.bufsize, dev->dmasound.blocks);
spin_unlock(&dev->slock);
- snd_pcm_stream_lock(dev->dmasound.substream);
- snd_pcm_stop(dev->dmasound.substream,SNDRV_PCM_STATE_XRUN);
- snd_pcm_stream_unlock(dev->dmasound.substream);
+ snd_pcm_stop_xrun(dev->dmasound.substream);
return;
}
diff --git a/include/linux/mfd/arizona/core.h b/include/linux/mfd/arizona/core.h
index f34723f7663c..910e3aa1e965 100644
--- a/include/linux/mfd/arizona/core.h
+++ b/include/linux/mfd/arizona/core.h
@@ -141,6 +141,7 @@ struct arizona {
uint16_t dac_comp_coeff;
uint8_t dac_comp_enabled;
+ struct mutex dac_comp_lock;
};
int arizona_clk32k_enable(struct arizona *arizona);
diff --git a/include/linux/mfd/davinci_voicecodec.h b/include/linux/mfd/davinci_voicecodec.h
index cb01496bfa49..8e1cdbef3dad 100644
--- a/include/linux/mfd/davinci_voicecodec.h
+++ b/include/linux/mfd/davinci_voicecodec.h
@@ -99,12 +99,6 @@ struct davinci_vcif {
dma_addr_t dma_rx_addr;
};
-struct cq93vc {
- struct platform_device *pdev;
- struct snd_soc_codec *codec;
- u32 sysclk;
-};
-
struct davinci_vc;
struct davinci_vc {
@@ -122,7 +116,6 @@ struct davinci_vc {
/* Client devices */
struct davinci_vcif davinci_vcif;
- struct cq93vc cq93vc;
};
#endif
diff --git a/include/linux/platform_data/asoc-s3c.h b/include/linux/platform_data/asoc-s3c.h
index a6591c693ebb..5e0bc779e6c5 100644
--- a/include/linux/platform_data/asoc-s3c.h
+++ b/include/linux/platform_data/asoc-s3c.h
@@ -27,6 +27,7 @@ struct samsung_i2s {
#define QUIRK_NO_MUXPSR (1 << 2)
#define QUIRK_NEED_RSTCLR (1 << 3)
#define QUIRK_SUPPORTS_TDM (1 << 4)
+#define QUIRK_SUPPORTS_IDMA (1 << 5)
/* Quirks of the I2S controller */
u32 quirks;
dma_addr_t idma_addr;
diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h
index ae6c3b8ed2f5..396e8f73670a 100644
--- a/include/sound/compress_driver.h
+++ b/include/sound/compress_driver.h
@@ -42,12 +42,11 @@ struct snd_compr_ops;
* @buffer_size: size of the above buffer
* @fragment_size: size of buffer fragment in bytes
* @fragments: number of such fragments
- * @hw_pointer: offset of last location in buffer where DSP copied data
- * @app_pointer: offset of last location in buffer where app wrote data
* @total_bytes_available: cumulative number of bytes made available in
* the ring buffer
* @total_bytes_transferred: cumulative bytes transferred by offload DSP
* @sleep: poll sleep
+ * @private_data: driver private data pointer
*/
struct snd_compr_runtime {
snd_pcm_state_t state;
@@ -94,6 +93,8 @@ struct snd_compr_stream {
* This can be called in during stream creation only to set codec params
* and the stream properties
* @get_params: retrieve the codec parameters, mandatory
+ * @set_metadata: Set the metadata values for a stream
+ * @get_metadata: retreives the requested metadata values from stream
* @trigger: Trigger operations like start, pause, resume, drain, stop.
* This callback is mandatory
* @pointer: Retrieve current h/w pointer information. Mandatory
diff --git a/include/sound/jack.h b/include/sound/jack.h
index 58916573db58..218235030ebc 100644
--- a/include/sound/jack.h
+++ b/include/sound/jack.h
@@ -28,8 +28,23 @@
struct input_dev;
/**
- * Jack types which can be reported. These values are used as a
- * bitmask.
+ * enum snd_jack_types - Jack types which can be reported
+ * @SND_JACK_HEADPHONE: Headphone
+ * @SND_JACK_MICROPHONE: Microphone
+ * @SND_JACK_HEADSET: Headset
+ * @SND_JACK_LINEOUT: Line out
+ * @SND_JACK_MECHANICAL: Mechanical switch
+ * @SND_JACK_VIDEOOUT: Video out
+ * @SND_JACK_AVOUT: AV (Audio Video) out
+ * @SND_JACK_LINEIN: Line in
+ * @SND_JACK_BTN_0: Button 0
+ * @SND_JACK_BTN_1: Button 1
+ * @SND_JACK_BTN_2: Button 2
+ * @SND_JACK_BTN_3: Button 3
+ * @SND_JACK_BTN_4: Button 4
+ * @SND_JACK_BTN_5: Button 5
+ *
+ * These values are used as a bitmask.
*
* Note that this must be kept in sync with the lookup table in
* sound/core/jack.c.
@@ -90,6 +105,13 @@ static inline void snd_jack_set_parent(struct snd_jack *jack,
{
}
+static inline int snd_jack_set_key(struct snd_jack *jack,
+ enum snd_jack_types type,
+ int keytype)
+{
+ return 0;
+}
+
static inline void snd_jack_report(struct snd_jack *jack, int status)
{
}
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 8bb00a27e219..1e7f74acc2ec 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -418,7 +418,10 @@ struct snd_pcm_substream {
struct snd_info_entry *proc_status_entry;
struct snd_info_entry *proc_prealloc_entry;
struct snd_info_entry *proc_prealloc_max_entry;
+#ifdef CONFIG_SND_PCM_XRUN_DEBUG
+ struct snd_info_entry *proc_xrun_injection_entry;
#endif
+#endif /* CONFIG_SND_VERBOSE_PROCFS */
/* misc flags */
unsigned int hw_opened: 1;
};
@@ -505,6 +508,7 @@ int snd_pcm_status(struct snd_pcm_substream *substream,
int snd_pcm_start(struct snd_pcm_substream *substream);
int snd_pcm_stop(struct snd_pcm_substream *substream, snd_pcm_state_t status);
int snd_pcm_drain_done(struct snd_pcm_substream *substream);
+int snd_pcm_stop_xrun(struct snd_pcm_substream *substream);
#ifdef CONFIG_PM
int snd_pcm_suspend(struct snd_pcm_substream *substream);
int snd_pcm_suspend_all(struct snd_pcm *pcm);
@@ -535,6 +539,12 @@ snd_pcm_debug_name(struct snd_pcm_substream *substream, char *buf, size_t size)
* PCM library
*/
+/**
+ * snd_pcm_stream_linked - Check whether the substream is linked with others
+ * @substream: substream to check
+ *
+ * Returns true if the given substream is being linked with others.
+ */
static inline int snd_pcm_stream_linked(struct snd_pcm_substream *substream)
{
return substream->group != &substream->self_group;
@@ -545,6 +555,16 @@ void snd_pcm_stream_unlock(struct snd_pcm_substream *substream);
void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream);
void snd_pcm_stream_unlock_irq(struct snd_pcm_substream *substream);
unsigned long _snd_pcm_stream_lock_irqsave(struct snd_pcm_substream *substream);
+
+/**
+ * snd_pcm_stream_lock_irqsave - Lock the PCM stream
+ * @substream: PCM substream
+ * @flags: irq flags
+ *
+ * This locks the PCM stream like snd_pcm_stream_lock() but with the local
+ * IRQ (only when nonatomic is false). In nonatomic case, this is identical
+ * as snd_pcm_stream_lock().
+ */
#define snd_pcm_stream_lock_irqsave(substream, flags) \
do { \
typecheck(unsigned long, flags); \
@@ -553,9 +573,25 @@ unsigned long _snd_pcm_stream_lock_irqsave(struct snd_pcm_substream *substream);
void snd_pcm_stream_unlock_irqrestore(struct snd_pcm_substream *substream,
unsigned long flags);
+/**
+ * snd_pcm_group_for_each_entry - iterate over the linked substreams
+ * @s: the iterator
+ * @substream: the substream
+ *
+ * Iterate over the all linked substreams to the given @substream.
+ * When @substream isn't linked with any others, this gives returns @substream
+ * itself once.
+ */
#define snd_pcm_group_for_each_entry(s, substream) \
list_for_each_entry(s, &substream->group->substreams, link_list)
+/**
+ * snd_pcm_running - Check whether the substream is in a running state
+ * @substream: substream to check
+ *
+ * Returns true if the given substream is in the state RUNNING, or in the
+ * state DRAINING for playback.
+ */
static inline int snd_pcm_running(struct snd_pcm_substream *substream)
{
return (substream->runtime->status->state == SNDRV_PCM_STATE_RUNNING ||
@@ -563,45 +599,81 @@ static inline int snd_pcm_running(struct snd_pcm_substream *substream)
substream->stream == SNDRV_PCM_STREAM_PLAYBACK));
}
+/**
+ * bytes_to_samples - Unit conversion of the size from bytes to samples
+ * @runtime: PCM runtime instance
+ * @size: size in bytes
+ */
static inline ssize_t bytes_to_samples(struct snd_pcm_runtime *runtime, ssize_t size)
{
return size * 8 / runtime->sample_bits;
}
+/**
+ * bytes_to_frames - Unit conversion of the size from bytes to frames
+ * @runtime: PCM runtime instance
+ * @size: size in bytes
+ */
static inline snd_pcm_sframes_t bytes_to_frames(struct snd_pcm_runtime *runtime, ssize_t size)
{
return size * 8 / runtime->frame_bits;
}
+/**
+ * samples_to_bytes - Unit conversion of the size from samples to bytes
+ * @runtime: PCM runtime instance
+ * @size: size in samples
+ */
static inline ssize_t samples_to_bytes(struct snd_pcm_runtime *runtime, ssize_t size)
{
return size * runtime->sample_bits / 8;
}
+/**
+ * frames_to_bytes - Unit conversion of the size from frames to bytes
+ * @runtime: PCM runtime instance
+ * @size: size in frames
+ */
static inline ssize_t frames_to_bytes(struct snd_pcm_runtime *runtime, snd_pcm_sframes_t size)
{
return size * runtime->frame_bits / 8;
}
+/**
+ * frame_aligned - Check whether the byte size is aligned to frames
+ * @runtime: PCM runtime instance
+ * @bytes: size in bytes
+ */
static inline int frame_aligned(struct snd_pcm_runtime *runtime, ssize_t bytes)
{
return bytes % runtime->byte_align == 0;
}
+/**
+ * snd_pcm_lib_buffer_bytes - Get the buffer size of the current PCM in bytes
+ * @substream: PCM substream
+ */
static inline size_t snd_pcm_lib_buffer_bytes(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
return frames_to_bytes(runtime, runtime->buffer_size);
}
+/**
+ * snd_pcm_lib_period_bytes - Get the period size of the current PCM in bytes
+ * @substream: PCM substream
+ */
static inline size_t snd_pcm_lib_period_bytes(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
return frames_to_bytes(runtime, runtime->period_size);
}
-/*
- * result is: 0 ... (boundary - 1)
+/**
+ * snd_pcm_playback_avail - Get the available (writable) space for playback
+ * @runtime: PCM runtime instance
+ *
+ * Result is between 0 ... (boundary - 1)
*/
static inline snd_pcm_uframes_t snd_pcm_playback_avail(struct snd_pcm_runtime *runtime)
{
@@ -613,8 +685,11 @@ static inline snd_pcm_uframes_t snd_pcm_playback_avail(struct snd_pcm_runtime *r
return avail;
}
-/*
- * result is: 0 ... (boundary - 1)
+/**
+ * snd_pcm_playback_avail - Get the available (readable) space for capture
+ * @runtime: PCM runtime instance
+ *
+ * Result is between 0 ... (boundary - 1)
*/
static inline snd_pcm_uframes_t snd_pcm_capture_avail(struct snd_pcm_runtime *runtime)
{
@@ -624,11 +699,19 @@ static inline snd_pcm_uframes_t snd_pcm_capture_avail(struct snd_pcm_runtime *ru
return avail;
}
+/**
+ * snd_pcm_playback_hw_avail - Get the queued space for playback
+ * @runtime: PCM runtime instance
+ */
static inline snd_pcm_sframes_t snd_pcm_playback_hw_avail(struct snd_pcm_runtime *runtime)
{
return runtime->buffer_size - snd_pcm_playback_avail(runtime);
}
+/**
+ * snd_pcm_capture_hw_avail - Get the free space for capture
+ * @runtime: PCM runtime instance
+ */
static inline snd_pcm_sframes_t snd_pcm_capture_hw_avail(struct snd_pcm_runtime *runtime)
{
return runtime->buffer_size - snd_pcm_capture_avail(runtime);
@@ -708,6 +791,20 @@ static inline int snd_pcm_capture_empty(struct snd_pcm_substream *substream)
return snd_pcm_capture_avail(runtime) == 0;
}
+/**
+ * snd_pcm_trigger_done - Mark the master substream
+ * @substream: the pcm substream instance
+ * @master: the linked master substream
+ *
+ * When multiple substreams of the same card are linked and the hardware
+ * supports the single-shot operation, the driver calls this in the loop
+ * in snd_pcm_group_for_each_entry() for marking the substream as "done".
+ * Then most of trigger operations are performed only to the given master
+ * substream.
+ *
+ * The trigger_master mark is cleared at timestamp updates at the end
+ * of trigger operations.
+ */
static inline void snd_pcm_trigger_done(struct snd_pcm_substream *substream,
struct snd_pcm_substream *master)
{
@@ -750,18 +847,59 @@ static inline const struct snd_interval *hw_param_interval_c(const struct snd_pc
return &params->intervals[var - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL];
}
-#define params_channels(p) \
- (hw_param_interval_c((p), SNDRV_PCM_HW_PARAM_CHANNELS)->min)
-#define params_rate(p) \
- (hw_param_interval_c((p), SNDRV_PCM_HW_PARAM_RATE)->min)
-#define params_period_size(p) \
- (hw_param_interval_c((p), SNDRV_PCM_HW_PARAM_PERIOD_SIZE)->min)
-#define params_periods(p) \
- (hw_param_interval_c((p), SNDRV_PCM_HW_PARAM_PERIODS)->min)
-#define params_buffer_size(p) \
- (hw_param_interval_c((p), SNDRV_PCM_HW_PARAM_BUFFER_SIZE)->min)
-#define params_buffer_bytes(p) \
- (hw_param_interval_c((p), SNDRV_PCM_HW_PARAM_BUFFER_BYTES)->min)
+/**
+ * params_channels - Get the number of channels from the hw params
+ * @p: hw params
+ */
+static inline unsigned int params_channels(const struct snd_pcm_hw_params *p)
+{
+ return hw_param_interval_c(p, SNDRV_PCM_HW_PARAM_CHANNELS)->min;
+}
+
+/**
+ * params_channels - Get the sample rate from the hw params
+ * @p: hw params
+ */
+static inline unsigned int params_rate(const struct snd_pcm_hw_params *p)
+{
+ return hw_param_interval_c(p, SNDRV_PCM_HW_PARAM_RATE)->min;
+}
+
+/**
+ * params_channels - Get the period size (in frames) from the hw params
+ * @p: hw params
+ */
+static inline unsigned int params_period_size(const struct snd_pcm_hw_params *p)
+{
+ return hw_param_interval_c(p, SNDRV_PCM_HW_PARAM_PERIOD_SIZE)->min;
+}
+
+/**
+ * params_channels - Get the number of periods from the hw params
+ * @p: hw params
+ */
+static inline unsigned int params_periods(const struct snd_pcm_hw_params *p)
+{
+ return hw_param_interval_c(p, SNDRV_PCM_HW_PARAM_PERIODS)->min;
+}
+
+/**
+ * params_channels - Get the buffer size (in frames) from the hw params
+ * @p: hw params
+ */
+static inline unsigned int params_buffer_size(const struct snd_pcm_hw_params *p)
+{
+ return hw_param_interval_c(p, SNDRV_PCM_HW_PARAM_BUFFER_SIZE)->min;
+}
+
+/**
+ * params_channels - Get the buffer size (in bytes) from the hw params
+ * @p: hw params
+ */
+static inline unsigned int params_buffer_bytes(const struct snd_pcm_hw_params *p)
+{
+ return hw_param_interval_c(p, SNDRV_PCM_HW_PARAM_BUFFER_BYTES)->min;
+}
int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v);
void snd_interval_mul(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c);
@@ -883,6 +1021,14 @@ unsigned int snd_pcm_rate_bit_to_rate(unsigned int rate_bit);
unsigned int snd_pcm_rate_mask_intersect(unsigned int rates_a,
unsigned int rates_b);
+/**
+ * snd_pcm_set_runtime_buffer - Set the PCM runtime buffer
+ * @substream: PCM substream to set
+ * @bufp: the buffer information, NULL to clear
+ *
+ * Copy the buffer information to runtime->dma_buffer when @bufp is non-NULL.
+ * Otherwise it clears the current buffer information.
+ */
static inline void snd_pcm_set_runtime_buffer(struct snd_pcm_substream *substream,
struct snd_dma_buffer *bufp)
{
@@ -908,6 +1054,11 @@ void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream);
void snd_pcm_timer_init(struct snd_pcm_substream *substream);
void snd_pcm_timer_done(struct snd_pcm_substream *substream);
+/**
+ * snd_pcm_gettime - Fill the timespec depending on the timestamp mode
+ * @runtime: PCM runtime instance
+ * @tv: timespec to fill
+ */
static inline void snd_pcm_gettime(struct snd_pcm_runtime *runtime,
struct timespec *tv)
{
@@ -944,7 +1095,6 @@ int _snd_pcm_lib_alloc_vmalloc_buffer(struct snd_pcm_substream *substream,
int snd_pcm_lib_free_vmalloc_buffer(struct snd_pcm_substream *substream);
struct page *snd_pcm_lib_get_vmalloc_page(struct snd_pcm_substream *substream,
unsigned long offset);
-#if 0 /* for kernel-doc */
/**
* snd_pcm_lib_alloc_vmalloc_buffer - allocate virtual DMA buffer
* @substream: the substream to allocate the buffer to
@@ -957,8 +1107,13 @@ struct page *snd_pcm_lib_get_vmalloc_page(struct snd_pcm_substream *substream,
* Return: 1 if the buffer was changed, 0 if not changed, or a negative error
* code.
*/
-static int snd_pcm_lib_alloc_vmalloc_buffer
- (struct snd_pcm_substream *substream, size_t size);
+static inline int snd_pcm_lib_alloc_vmalloc_buffer
+ (struct snd_pcm_substream *substream, size_t size)
+{
+ return _snd_pcm_lib_alloc_vmalloc_buffer(substream, size,
+ GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);
+}
+
/**
* snd_pcm_lib_alloc_vmalloc_32_buffer - allocate 32-bit-addressable buffer
* @substream: the substream to allocate the buffer to
@@ -970,15 +1125,12 @@ static int snd_pcm_lib_alloc_vmalloc_buffer
* Return: 1 if the buffer was changed, 0 if not changed, or a negative error
* code.
*/
-static int snd_pcm_lib_alloc_vmalloc_32_buffer
- (struct snd_pcm_substream *substream, size_t size);
-#endif
-#define snd_pcm_lib_alloc_vmalloc_buffer(subs, size) \
- _snd_pcm_lib_alloc_vmalloc_buffer \
- (subs, size, GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO)
-#define snd_pcm_lib_alloc_vmalloc_32_buffer(subs, size) \
- _snd_pcm_lib_alloc_vmalloc_buffer \
- (subs, size, GFP_KERNEL | GFP_DMA32 | __GFP_ZERO)
+static inline int snd_pcm_lib_alloc_vmalloc_32_buffer
+ (struct snd_pcm_substream *substream, size_t size)
+{
+ return _snd_pcm_lib_alloc_vmalloc_buffer(substream, size,
+ GFP_KERNEL | GFP_DMA32 | __GFP_ZERO);
+}
#define snd_pcm_get_dma_buf(substream) ((substream)->runtime->dma_buffer_p)
@@ -998,18 +1150,35 @@ struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream,
#define snd_pcm_sgbuf_ops_page NULL
#endif /* SND_DMA_SGBUF */
+/**
+ * snd_pcm_sgbuf_get_addr - Get the DMA address at the corresponding offset
+ * @substream: PCM substream
+ * @ofs: byte offset
+ */
static inline dma_addr_t
snd_pcm_sgbuf_get_addr(struct snd_pcm_substream *substream, unsigned int ofs)
{
return snd_sgbuf_get_addr(snd_pcm_get_dma_buf(substream), ofs);
}
+/**
+ * snd_pcm_sgbuf_get_ptr - Get the virtual address at the corresponding offset
+ * @substream: PCM substream
+ * @ofs: byte offset
+ */
static inline void *
snd_pcm_sgbuf_get_ptr(struct snd_pcm_substream *substream, unsigned int ofs)
{
return snd_sgbuf_get_ptr(snd_pcm_get_dma_buf(substream), ofs);
}
+/**
+ * snd_pcm_sgbuf_chunk_size - Compute the max size that fits within the contig.
+ * page from the given size
+ * @substream: PCM substream
+ * @ofs: byte offset
+ * @size: byte size to examine
+ */
static inline unsigned int
snd_pcm_sgbuf_get_chunk_size(struct snd_pcm_substream *substream,
unsigned int ofs, unsigned int size)
@@ -1017,13 +1186,24 @@ snd_pcm_sgbuf_get_chunk_size(struct snd_pcm_substream *substream,
return snd_sgbuf_get_chunk_size(snd_pcm_get_dma_buf(substream), ofs, size);
}
-/* handle mmap counter - PCM mmap callback should handle this counter properly */
+/**
+ * snd_pcm_mmap_data_open - increase the mmap counter
+ * @area: VMA
+ *
+ * PCM mmap callback should handle this counter properly
+ */
static inline void snd_pcm_mmap_data_open(struct vm_area_struct *area)
{
struct snd_pcm_substream *substream = (struct snd_pcm_substream *)area->vm_private_data;
atomic_inc(&substream->mmap_count);
}
+/**
+ * snd_pcm_mmap_data_close - decrease the mmap counter
+ * @area: VMA
+ *
+ * PCM mmap callback should handle this counter properly
+ */
static inline void snd_pcm_mmap_data_close(struct vm_area_struct *area)
{
struct snd_pcm_substream *substream = (struct snd_pcm_substream *)area->vm_private_data;
@@ -1043,6 +1223,11 @@ int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream, struct vm_area_s
#define snd_pcm_lib_mmap_vmalloc NULL
+/**
+ * snd_pcm_limit_isa_dma_size - Get the max size fitting with ISA DMA transfer
+ * @dma: DMA number
+ * @max: pointer to store the max size
+ */
static inline void snd_pcm_limit_isa_dma_size(int dma, size_t *max)
{
*max = dma < 4 ? 64 * 1024 : 128 * 1024;
@@ -1095,7 +1280,11 @@ struct snd_pcm_chmap {
void *private_data; /* optional: private data pointer */
};
-/* get the PCM substream assigned to the given chmap info */
+/**
+ * snd_pcm_chmap_substream - get the PCM substream assigned to the given chmap info
+ * @info: chmap information
+ * @idx: the substream number index
+ */
static inline struct snd_pcm_substream *
snd_pcm_chmap_substream(struct snd_pcm_chmap *info, unsigned int idx)
{
@@ -1122,7 +1311,10 @@ int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream,
unsigned long private_value,
struct snd_pcm_chmap **info_ret);
-/* Strong-typed conversion of pcm_format to bitwise */
+/**
+ * pcm_format_to_bits - Strong-typed conversion of pcm_format to bitwise
+ * @pcm_format: PCM format
+ */
static inline u64 pcm_format_to_bits(snd_pcm_format_t pcm_format)
{
return 1ULL << (__force int) pcm_format;
diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h
index d76412b84b48..83284cae464c 100644
--- a/include/sound/rcar_snd.h
+++ b/include/sound/rcar_snd.h
@@ -36,14 +36,14 @@
#define RSND_SSI_CLK_PIN_SHARE (1 << 31)
#define RSND_SSI_NO_BUSIF (1 << 30) /* SSI+DMA without BUSIF */
-#define RSND_SSI(_dma_id, _pio_irq, _flags) \
-{ .dma_id = _dma_id, .pio_irq = _pio_irq, .flags = _flags }
+#define RSND_SSI(_dma_id, _irq, _flags) \
+{ .dma_id = _dma_id, .irq = _irq, .flags = _flags }
#define RSND_SSI_UNUSED \
-{ .dma_id = -1, .pio_irq = -1, .flags = 0 }
+{ .dma_id = -1, .irq = -1, .flags = 0 }
struct rsnd_ssi_platform_info {
int dma_id;
- int pio_irq;
+ int irq;
u32 flags;
};
diff --git a/include/sound/rt5645.h b/include/sound/rt5645.h
index a5352712194b..120d9610054e 100644
--- a/include/sound/rt5645.h
+++ b/include/sound/rt5645.h
@@ -23,6 +23,10 @@ struct rt5645_platform_data {
unsigned int hp_det_gpio;
bool gpio_hp_det_active_high;
+
+ /* true if codec's jd function is used */
+ bool en_jd_func;
+ unsigned int jd_mode;
};
#endif
diff --git a/include/sound/rt5677.h b/include/sound/rt5677.h
index 082670e3a353..d9eb7d861cd0 100644
--- a/include/sound/rt5677.h
+++ b/include/sound/rt5677.h
@@ -27,6 +27,16 @@ struct rt5677_platform_data {
bool lout3_diff;
/* DMIC2 clock source selection */
enum rt5677_dmic2_clk dmic2_clk_pin;
+
+ /* configures GPIO, 0 - floating, 1 - pulldown, 2 - pullup */
+ u8 gpio_config[6];
+
+ /* jd1 can select 0 ~ 3 as OFF, GPIO1, GPIO2 and GPIO3 respectively */
+ unsigned int jd1_gpio;
+ /* jd2 and jd3 can select 0 ~ 3 as
+ OFF, GPIO4, GPIO5 and GPIO6 respectively */
+ unsigned int jd2_gpio;
+ unsigned int jd3_gpio;
};
#endif
diff --git a/include/sound/seq_kernel.h b/include/sound/seq_kernel.h
index 2398521f0998..eea5400fe373 100644
--- a/include/sound/seq_kernel.h
+++ b/include/sound/seq_kernel.h
@@ -108,9 +108,13 @@ int snd_seq_event_port_detach(int client, int port);
#ifdef CONFIG_MODULES
void snd_seq_autoload_lock(void);
void snd_seq_autoload_unlock(void);
+void snd_seq_autoload_init(void);
+#define snd_seq_autoload_exit() snd_seq_autoload_lock()
#else
#define snd_seq_autoload_lock()
#define snd_seq_autoload_unlock()
+#define snd_seq_autoload_init()
+#define snd_seq_autoload_exit()
#endif
#endif /* __SOUND_SEQ_KERNEL_H */
diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h
index e8b3080d196a..2df96b1384c7 100644
--- a/include/sound/soc-dai.h
+++ b/include/sound/soc-dai.h
@@ -206,7 +206,6 @@ struct snd_soc_dai_driver {
/* DAI description */
const char *name;
unsigned int id;
- int ac97_control;
unsigned int base;
/* DAI driver callbacks */
@@ -216,6 +215,8 @@ struct snd_soc_dai_driver {
int (*resume)(struct snd_soc_dai *dai);
/* compress dai */
bool compress_dai;
+ /* DAI is also used for the control bus */
+ bool bus_control;
/* ops */
const struct snd_soc_dai_ops *ops;
@@ -241,7 +242,6 @@ struct snd_soc_dai {
const char *name;
int id;
struct device *dev;
- void *ac97_pdata; /* platform_data for the ac97 codec */
/* driver ops */
struct snd_soc_dai_driver *driver;
@@ -268,7 +268,6 @@ struct snd_soc_dai {
unsigned int sample_bits;
/* parent platform/codec */
- struct snd_soc_platform *platform;
struct snd_soc_codec *codec;
struct snd_soc_component *component;
@@ -276,8 +275,6 @@ struct snd_soc_dai {
unsigned int tx_mask;
unsigned int rx_mask;
- struct snd_soc_card *card;
-
struct list_head list;
};
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index 3a4d7da67b8d..89823cfe6f04 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -435,7 +435,7 @@ void snd_soc_dapm_auto_nc_pins(struct snd_soc_card *card);
unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol);
/* Mostly internal - should not normally be used */
-void dapm_mark_io_dirty(struct snd_soc_dapm_context *dapm);
+void dapm_mark_endpoints_dirty(struct snd_soc_card *card);
/* dapm path query */
int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
@@ -508,9 +508,9 @@ struct snd_soc_dapm_path {
/* status */
u32 connect:1; /* source and sink widgets are connected */
- u32 walked:1; /* path has been walked */
u32 walking:1; /* path is in the process of being walked */
u32 weak:1; /* path ignored for power management */
+ u32 is_supply:1; /* At least one of the connected widgets is a supply */
int (*connected)(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink);
@@ -544,11 +544,13 @@ struct snd_soc_dapm_widget {
unsigned char active:1; /* active stream on DAC, ADC's */
unsigned char connected:1; /* connected codec pin */
unsigned char new:1; /* cnew complete */
- unsigned char ext:1; /* has external widgets */
unsigned char force:1; /* force state */
unsigned char ignore_suspend:1; /* kept enabled over suspend */
unsigned char new_power:1; /* power from this run */
unsigned char power_checked:1; /* power checked this run */
+ unsigned char is_supply:1; /* Widget is a supply type widget */
+ unsigned char is_sink:1; /* Widget is a sink type widget */
+ unsigned char is_source:1; /* Widget is a source type widget */
int subseq; /* sort within widget type */
int (*power_check)(struct snd_soc_dapm_widget *w);
@@ -567,6 +569,7 @@ struct snd_soc_dapm_widget {
struct list_head sinks;
/* used during DAPM updates */
+ struct list_head work_list;
struct list_head power_list;
struct list_head dirty;
int inputs;
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 7ba7130037a0..b4fca9aed2a2 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -36,6 +36,11 @@
{.reg = xreg, .rreg = xreg, .shift = shift_left, \
.rshift = shift_right, .max = xmax, .platform_max = xmax, \
.invert = xinvert, .autodisable = xautodisable})
+#define SOC_DOUBLE_S_VALUE(xreg, shift_left, shift_right, xmin, xmax, xsign_bit, xinvert, xautodisable) \
+ ((unsigned long)&(struct soc_mixer_control) \
+ {.reg = xreg, .rreg = xreg, .shift = shift_left, \
+ .rshift = shift_right, .min = xmin, .max = xmax, .platform_max = xmax, \
+ .sign_bit = xsign_bit, .invert = xinvert, .autodisable = xautodisable})
#define SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert, xautodisable) \
SOC_DOUBLE_VALUE(xreg, xshift, xshift, xmax, xinvert, xautodisable)
#define SOC_SINGLE_VALUE_EXT(xreg, xmax, xinvert) \
@@ -171,11 +176,9 @@
.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
SNDRV_CTL_ELEM_ACCESS_READWRITE, \
.tlv.p = (tlv_array), \
- .info = snd_soc_info_volsw_s8, .get = snd_soc_get_volsw_s8, \
- .put = snd_soc_put_volsw_s8, \
- .private_value = (unsigned long)&(struct soc_mixer_control) \
- {.reg = xreg, .min = xmin, .max = xmax, \
- .platform_max = xmax} }
+ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\
+ .put = snd_soc_put_volsw, \
+ .private_value = SOC_DOUBLE_S_VALUE(xreg, 0, 8, xmin, xmax, 7, 0, 0) }
#define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xitems, xtexts) \
{ .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \
.items = xitems, .texts = xtexts, \
@@ -366,8 +369,6 @@ struct snd_soc_jack_gpio;
typedef int (*hw_write_t)(void *,const char* ,int);
-extern struct snd_ac97_bus_ops *soc_ac97_ops;
-
enum snd_soc_pcm_subclass {
SND_SOC_PCM_CLASS_PCM = 0,
SND_SOC_PCM_CLASS_BE = 1,
@@ -409,13 +410,9 @@ int devm_snd_soc_register_component(struct device *dev,
const struct snd_soc_component_driver *cmpnt_drv,
struct snd_soc_dai_driver *dai_drv, int num_dai);
void snd_soc_unregister_component(struct device *dev);
-int snd_soc_cache_sync(struct snd_soc_codec *codec);
int snd_soc_cache_init(struct snd_soc_codec *codec);
int snd_soc_cache_exit(struct snd_soc_codec *codec);
-int snd_soc_cache_write(struct snd_soc_codec *codec,
- unsigned int reg, unsigned int value);
-int snd_soc_cache_read(struct snd_soc_codec *codec,
- unsigned int reg, unsigned int *value);
+
int snd_soc_platform_read(struct snd_soc_platform *platform,
unsigned int reg);
int snd_soc_platform_write(struct snd_soc_platform *platform,
@@ -500,14 +497,28 @@ int snd_soc_update_bits_locked(struct snd_soc_codec *codec,
int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned int reg,
unsigned int mask, unsigned int value);
-int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
- struct snd_ac97_bus_ops *ops, int num);
-void snd_soc_free_ac97_codec(struct snd_soc_codec *codec);
+#ifdef CONFIG_SND_SOC_AC97_BUS
+struct snd_ac97 *snd_soc_new_ac97_codec(struct snd_soc_codec *codec);
+void snd_soc_free_ac97_codec(struct snd_ac97 *ac97);
int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops);
int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops,
struct platform_device *pdev);
+extern struct snd_ac97_bus_ops *soc_ac97_ops;
+#else
+static inline int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops,
+ struct platform_device *pdev)
+{
+ return 0;
+}
+
+static inline int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops)
+{
+ return 0;
+}
+#endif
+
/*
*Controls
*/
@@ -545,12 +556,6 @@ int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
-int snd_soc_info_volsw_s8(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo);
-int snd_soc_get_volsw_s8(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol);
-int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol);
int snd_soc_info_volsw_range(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo);
int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol,
@@ -780,24 +785,18 @@ struct snd_soc_codec {
struct device *dev;
const struct snd_soc_codec_driver *driver;
- struct mutex mutex;
struct list_head list;
struct list_head card_list;
/* runtime */
- struct snd_ac97 *ac97; /* for ad-hoc ac97 devices */
unsigned int cache_bypass:1; /* Suppress access to the cache */
unsigned int suspended:1; /* Codec is in suspend PM state */
- unsigned int ac97_registered:1; /* Codec has been AC97 registered */
- unsigned int ac97_created:1; /* Codec has been created by SoC */
unsigned int cache_init:1; /* codec cache has been initialized */
- u32 cache_sync; /* Cache needs to be synced to hardware */
/* codec IO */
void *control_data; /* codec control (i2c/3wire) data */
hw_write_t hw_write;
void *reg_cache;
- struct mutex cache_rw_mutex;
/* component */
struct snd_soc_component component;
@@ -860,8 +859,6 @@ struct snd_soc_platform_driver {
int (*probe)(struct snd_soc_platform *);
int (*remove)(struct snd_soc_platform *);
- int (*suspend)(struct snd_soc_dai *dai);
- int (*resume)(struct snd_soc_dai *dai);
struct snd_soc_component_driver component_driver;
/* pcm creation and destruction */
@@ -886,7 +883,7 @@ struct snd_soc_platform_driver {
struct snd_soc_dai_link_component {
const char *name;
- const struct device_node *of_node;
+ struct device_node *of_node;
const char *dai_name;
};
@@ -894,8 +891,6 @@ struct snd_soc_platform {
struct device *dev;
const struct snd_soc_platform_driver *driver;
- unsigned int suspended:1; /* platform is suspended */
-
struct list_head list;
struct snd_soc_component component;
@@ -990,7 +985,7 @@ struct snd_soc_codec_conf {
* DT/OF node, but not both.
*/
const char *dev_name;
- const struct device_node *of_node;
+ struct device_node *of_node;
/*
* optional map of kcontrol, widget and path name prefixes that are
@@ -1007,7 +1002,7 @@ struct snd_soc_aux_dev {
* DT/OF node, but not both.
*/
const char *codec_name;
- const struct device_node *codec_of_node;
+ struct device_node *codec_of_node;
/* codec/machine specific init - e.g. add machine controls */
int (*init)(struct snd_soc_component *component);
@@ -1264,6 +1259,17 @@ unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg);
int snd_soc_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int val);
+/**
+ * snd_soc_cache_sync() - Sync the register cache with the hardware
+ * @codec: CODEC to sync
+ *
+ * Note: This function will call regcache_sync()
+ */
+static inline int snd_soc_cache_sync(struct snd_soc_codec *codec)
+{
+ return regcache_sync(codec->component.regmap);
+}
+
/* component IO */
int snd_soc_component_read(struct snd_soc_component *component,
unsigned int reg, unsigned int *val);
@@ -1277,6 +1283,45 @@ void snd_soc_component_async_complete(struct snd_soc_component *component);
int snd_soc_component_test_bits(struct snd_soc_component *component,
unsigned int reg, unsigned int mask, unsigned int value);
+#ifdef CONFIG_REGMAP
+
+void snd_soc_component_init_regmap(struct snd_soc_component *component,
+ struct regmap *regmap);
+void snd_soc_component_exit_regmap(struct snd_soc_component *component);
+
+/**
+ * snd_soc_codec_init_regmap() - Initialize regmap instance for the CODEC
+ * @codec: The CODEC for which to initialize the regmap instance
+ * @regmap: The regmap instance that should be used by the CODEC
+ *
+ * This function allows deferred assignment of the regmap instance that is
+ * associated with the CODEC. Only use this if the regmap instance is not yet
+ * ready when the CODEC is registered. The function must also be called before
+ * the first IO attempt of the CODEC.
+ */
+static inline void snd_soc_codec_init_regmap(struct snd_soc_codec *codec,
+ struct regmap *regmap)
+{
+ snd_soc_component_init_regmap(&codec->component, regmap);
+}
+
+/**
+ * snd_soc_codec_exit_regmap() - De-initialize regmap instance for the CODEC
+ * @codec: The CODEC for which to de-initialize the regmap instance
+ *
+ * Calls regmap_exit() on the regmap instance associated to the CODEC and
+ * removes the regmap instance from the CODEC.
+ *
+ * This function should only be used if snd_soc_codec_init_regmap() was used to
+ * initialize the regmap instance.
+ */
+static inline void snd_soc_codec_exit_regmap(struct snd_soc_codec *codec)
+{
+ snd_soc_component_exit_regmap(&codec->component);
+}
+
+#endif
+
/* device driver data */
static inline void snd_soc_card_set_drvdata(struct snd_soc_card *card,
@@ -1451,6 +1496,9 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np,
struct device_node **framemaster);
int snd_soc_of_get_dai_name(struct device_node *of_node,
const char **dai_name);
+int snd_soc_of_get_dai_link_codecs(struct device *dev,
+ struct device_node *of_node,
+ struct snd_soc_dai_link *dai_link);
#include <sound/soc-dai.h>
diff --git a/include/sound/uda134x.h b/include/sound/uda134x.h
index e475659bd3be..509efb050176 100644
--- a/include/sound/uda134x.h
+++ b/include/sound/uda134x.h
@@ -18,18 +18,6 @@ struct uda134x_platform_data {
struct l3_pins l3;
void (*power) (int);
int model;
- /*
- ALSA SOC usually puts the device in standby mode when it's not used
- for sometime. If you unset is_powered_on_standby the driver will
- turn off the ADC/DAC when this callback is invoked and turn it back
- on when needed. Unfortunately this will result in a very light bump
- (it can be audible only with good earphones). If this bothers you
- set is_powered_on_standby, you will have slightly higher power
- consumption. Please note that sending the L3 command for ADC is
- enough to make the bump, so it doesn't make difference if you
- completely take off power from the codec.
- */
- int is_powered_on_standby;
#define UDA134X_UDA1340 1
#define UDA134X_UDA1341 2
#define UDA134X_UDA1344 3
diff --git a/include/trace/events/asoc.h b/include/trace/events/asoc.h
index b04ee7e5a466..88cf39d96d0f 100644
--- a/include/trace/events/asoc.h
+++ b/include/trace/events/asoc.h
@@ -288,31 +288,6 @@ TRACE_EVENT(snd_soc_jack_notify,
TP_printk("jack=%s %x", __get_str(name), (int)__entry->val)
);
-TRACE_EVENT(snd_soc_cache_sync,
-
- TP_PROTO(struct snd_soc_codec *codec, const char *type,
- const char *status),
-
- TP_ARGS(codec, type, status),
-
- TP_STRUCT__entry(
- __string( name, codec->component.name)
- __string( status, status )
- __string( type, type )
- __field( int, id )
- ),
-
- TP_fast_assign(
- __assign_str(name, codec->component.name);
- __assign_str(status, status);
- __assign_str(type, type);
- __entry->id = codec->component.id;
- ),
-
- TP_printk("codec=%s.%d type=%s status=%s", __get_str(name),
- (int)__entry->id, __get_str(type), __get_str(status))
-);
-
#endif /* _TRACE_ASOC_H */
/* This part must be outside protection */
diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
index 941d32f007dc..1f23cd635957 100644
--- a/include/uapi/sound/asound.h
+++ b/include/uapi/sound/asound.h
@@ -96,9 +96,10 @@ enum {
SNDRV_HWDEP_IFACE_FW_DICE, /* TC DICE FireWire device */
SNDRV_HWDEP_IFACE_FW_FIREWORKS, /* Echo Audio Fireworks based device */
SNDRV_HWDEP_IFACE_FW_BEBOB, /* BridgeCo BeBoB based device */
+ SNDRV_HWDEP_IFACE_FW_OXFW, /* Oxford OXFW970/971 based device */
/* Don't forget to change the following: */
- SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_BEBOB
+ SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_OXFW
};
struct snd_hwdep_info {
diff --git a/include/uapi/sound/compress_offload.h b/include/uapi/sound/compress_offload.h
index 1964026b5e09..22ed8cb7800b 100644
--- a/include/uapi/sound/compress_offload.h
+++ b/include/uapi/sound/compress_offload.h
@@ -32,7 +32,7 @@
#define SNDRV_COMPRESS_VERSION SNDRV_PROTOCOL_VERSION(0, 1, 2)
/**
- * struct snd_compressed_buffer: compressed buffer
+ * struct snd_compressed_buffer - compressed buffer
* @fragment_size: size of buffer fragment in bytes
* @fragments: number of such fragments
*/
@@ -42,7 +42,7 @@ struct snd_compressed_buffer {
} __attribute__((packed, aligned(4)));
/**
- * struct snd_compr_params: compressed stream params
+ * struct snd_compr_params - compressed stream params
* @buffer: buffer description
* @codec: codec parameters
* @no_wake_mode: dont wake on fragment elapsed
@@ -54,7 +54,7 @@ struct snd_compr_params {
} __attribute__((packed, aligned(4)));
/**
- * struct snd_compr_tstamp: timestamp descriptor
+ * struct snd_compr_tstamp - timestamp descriptor
* @byte_offset: Byte offset in ring buffer to DSP
* @copied_total: Total number of bytes copied from/to ring buffer to/by DSP
* @pcm_frames: Frames decoded or encoded by DSP. This field will evolve by
@@ -73,7 +73,7 @@ struct snd_compr_tstamp {
} __attribute__((packed, aligned(4)));
/**
- * struct snd_compr_avail: avail descriptor
+ * struct snd_compr_avail - avail descriptor
* @avail: Number of bytes available in ring buffer for writing/reading
* @tstamp: timestamp infomation
*/
@@ -88,7 +88,7 @@ enum snd_compr_direction {
};
/**
- * struct snd_compr_caps: caps descriptor
+ * struct snd_compr_caps - caps descriptor
* @codecs: pointer to array of codecs
* @direction: direction supported. Of type snd_compr_direction
* @min_fragment_size: minimum fragment supported by DSP
@@ -110,7 +110,7 @@ struct snd_compr_caps {
} __attribute__((packed, aligned(4)));
/**
- * struct snd_compr_codec_caps: query capability of codec
+ * struct snd_compr_codec_caps - query capability of codec
* @codec: codec for which capability is queried
* @num_descriptors: number of codec descriptors
* @descriptor: array of codec capability descriptor
@@ -122,18 +122,19 @@ struct snd_compr_codec_caps {
} __attribute__((packed, aligned(4)));
/**
+ * enum sndrv_compress_encoder
* @SNDRV_COMPRESS_ENCODER_PADDING: no of samples appended by the encoder at the
* end of the track
* @SNDRV_COMPRESS_ENCODER_DELAY: no of samples inserted by the encoder at the
* beginning of the track
*/
-enum {
+enum sndrv_compress_encoder {
SNDRV_COMPRESS_ENCODER_PADDING = 1,
SNDRV_COMPRESS_ENCODER_DELAY = 2,
};
/**
- * struct snd_compr_metadata: compressed stream metadata
+ * struct snd_compr_metadata - compressed stream metadata
* @key: key id
* @value: key value
*/
diff --git a/include/uapi/sound/firewire.h b/include/uapi/sound/firewire.h
index af4bd136c75d..49122df3b56b 100644
--- a/include/uapi/sound/firewire.h
+++ b/include/uapi/sound/firewire.h
@@ -55,7 +55,8 @@ union snd_firewire_event {
#define SNDRV_FIREWIRE_TYPE_DICE 1
#define SNDRV_FIREWIRE_TYPE_FIREWORKS 2
#define SNDRV_FIREWIRE_TYPE_BEBOB 3
-/* AV/C, RME, MOTU, ... */
+#define SNDRV_FIREWIRE_TYPE_OXFW 4
+/* RME, MOTU, ... */
struct snd_firewire_get_info {
unsigned int type; /* SNDRV_FIREWIRE_TYPE_xxx */
diff --git a/include/uapi/sound/hdspm.h b/include/uapi/sound/hdspm.h
index d956c3593f65..b357f1a5e29c 100644
--- a/include/uapi/sound/hdspm.h
+++ b/include/uapi/sound/hdspm.h
@@ -74,14 +74,14 @@ struct hdspm_config {
#define SNDRV_HDSPM_IOCTL_GET_CONFIG \
_IOR('H', 0x41, struct hdspm_config)
-/**
+/*
* If there's a TCO (TimeCode Option) board installed,
* there are further options and status data available.
* The hdspm_ltc structure contains the current SMPTE
* timecode and some status information and can be
* obtained via SNDRV_HDSPM_IOCTL_GET_LTC or in the
* hdspm_status struct.
- **/
+ */
enum hdspm_ltc_format {
format_invalid,
@@ -113,11 +113,11 @@ struct hdspm_ltc {
#define SNDRV_HDSPM_IOCTL_GET_LTC _IOR('H', 0x46, struct hdspm_ltc)
-/**
+/*
* The status data reflects the device's current state
* as determined by the card's configuration and
* connection status.
- **/
+ */
enum hdspm_sync {
hdspm_sync_no_lock = 0,
@@ -171,9 +171,9 @@ struct hdspm_status {
#define SNDRV_HDSPM_IOCTL_GET_STATUS \
_IOR('H', 0x47, struct hdspm_status)
-/**
+/*
* Get information about the card and its add-ons.
- **/
+ */
#define HDSPM_ADDON_TCO 1
diff --git a/sound/aoa/codecs/onyx.c b/sound/aoa/codecs/onyx.c
index 401107b85d30..23c371ecfb6b 100644
--- a/sound/aoa/codecs/onyx.c
+++ b/sound/aoa/codecs/onyx.c
@@ -243,13 +243,7 @@ static int onyx_snd_capture_source_info(struct snd_kcontrol *kcontrol,
{
static const char * const texts[] = { "Line-In", "Microphone" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int onyx_snd_capture_source_get(struct snd_kcontrol *kcontrol,
diff --git a/sound/aoa/codecs/tas.c b/sound/aoa/codecs/tas.c
index cf3c6303b7e3..364c7c4416e8 100644
--- a/sound/aoa/codecs/tas.c
+++ b/sound/aoa/codecs/tas.c
@@ -478,15 +478,9 @@ static struct snd_kcontrol_new drc_switch_control = {
static int tas_snd_capture_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = { "Line-In", "Microphone" };
+ static const char * const texts[] = { "Line-In", "Microphone" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int tas_snd_capture_source_get(struct snd_kcontrol *kcontrol,
diff --git a/sound/aoa/soundbus/i2sbus/core.c b/sound/aoa/soundbus/i2sbus/core.c
index a80d5ea87ccd..4e2b4fbf2496 100644
--- a/sound/aoa/soundbus/i2sbus/core.c
+++ b/sound/aoa/soundbus/i2sbus/core.c
@@ -79,8 +79,7 @@ static void i2sbus_release_dev(struct device *dev)
if (i2sdev->out.dbdma) iounmap(i2sdev->out.dbdma);
if (i2sdev->in.dbdma) iounmap(i2sdev->in.dbdma);
for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++)
- if (i2sdev->allocated_resource[i])
- release_and_free_resource(i2sdev->allocated_resource[i]);
+ release_and_free_resource(i2sdev->allocated_resource[i]);
free_dbdma_descriptor_ring(i2sdev, &i2sdev->out.dbdma_ring);
free_dbdma_descriptor_ring(i2sdev, &i2sdev->in.dbdma_ring);
for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++)
@@ -323,8 +322,7 @@ static int i2sbus_add_dev(struct macio_dev *macio,
if (dev->out.dbdma) iounmap(dev->out.dbdma);
if (dev->in.dbdma) iounmap(dev->in.dbdma);
for (i=0;i<3;i++)
- if (dev->allocated_resource[i])
- release_and_free_resource(dev->allocated_resource[i]);
+ release_and_free_resource(dev->allocated_resource[i]);
mutex_destroy(&dev->lock);
kfree(dev);
return 0;
diff --git a/sound/arm/pxa2xx-pcm-lib.c b/sound/arm/pxa2xx-pcm-lib.c
index a61d7a9a995e..01f8fdc42b1b 100644
--- a/sound/arm/pxa2xx-pcm-lib.c
+++ b/sound/arm/pxa2xx-pcm-lib.c
@@ -200,9 +200,7 @@ void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id)
} else {
printk(KERN_ERR "DMA error on channel %d (DCSR=%#x)\n",
dma_ch, dcsr);
- snd_pcm_stream_lock(substream);
- snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
- snd_pcm_stream_unlock(substream);
+ snd_pcm_stop_xrun(substream);
}
}
EXPORT_SYMBOL(pxa2xx_pcm_dma_irq);
diff --git a/sound/atmel/abdac.c b/sound/atmel/abdac.c
index 31061e3521d4..023140504104 100644
--- a/sound/atmel/abdac.c
+++ b/sound/atmel/abdac.c
@@ -242,7 +242,7 @@ static int atmel_abdac_trigger(struct snd_pcm_substream *substream, int cmd)
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */
case SNDRV_PCM_TRIGGER_RESUME: /* fall through */
case SNDRV_PCM_TRIGGER_START:
- clk_enable(dac->sample_clk);
+ clk_prepare_enable(dac->sample_clk);
retval = dw_dma_cyclic_start(dac->dma.chan);
if (retval)
goto out;
@@ -254,7 +254,7 @@ static int atmel_abdac_trigger(struct snd_pcm_substream *substream, int cmd)
dw_dma_cyclic_stop(dac->dma.chan);
dac_writel(dac, DATA, 0);
dac_writel(dac, CTRL, 0);
- clk_disable(dac->sample_clk);
+ clk_disable_unprepare(dac->sample_clk);
break;
default:
retval = -EINVAL;
@@ -429,7 +429,7 @@ static int atmel_abdac_probe(struct platform_device *pdev)
retval = PTR_ERR(sample_clk);
goto out_put_pclk;
}
- clk_enable(pclk);
+ clk_prepare_enable(pclk);
retval = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1,
SNDRV_DEFAULT_STR1, THIS_MODULE,
@@ -528,7 +528,7 @@ out_free_card:
snd_card_free(card);
out_put_sample_clk:
clk_put(sample_clk);
- clk_disable(pclk);
+ clk_disable_unprepare(pclk);
out_put_pclk:
clk_put(pclk);
return retval;
@@ -541,8 +541,8 @@ static int atmel_abdac_suspend(struct device *pdev)
struct atmel_abdac *dac = card->private_data;
dw_dma_cyclic_stop(dac->dma.chan);
- clk_disable(dac->sample_clk);
- clk_disable(dac->pclk);
+ clk_disable_unprepare(dac->sample_clk);
+ clk_disable_unprepare(dac->pclk);
return 0;
}
@@ -552,8 +552,8 @@ static int atmel_abdac_resume(struct device *pdev)
struct snd_card *card = dev_get_drvdata(pdev);
struct atmel_abdac *dac = card->private_data;
- clk_enable(dac->pclk);
- clk_enable(dac->sample_clk);
+ clk_prepare_enable(dac->pclk);
+ clk_prepare_enable(dac->sample_clk);
if (test_bit(DMA_READY, &dac->flags))
dw_dma_cyclic_start(dac->dma.chan);
@@ -572,7 +572,7 @@ static int atmel_abdac_remove(struct platform_device *pdev)
struct atmel_abdac *dac = get_dac(card);
clk_put(dac->sample_clk);
- clk_disable(dac->pclk);
+ clk_disable_unprepare(dac->pclk);
clk_put(dac->pclk);
dma_release_channel(dac->dma.chan);
diff --git a/sound/atmel/ac97c.c b/sound/atmel/ac97c.c
index b59427d5a697..cb44c74c9702 100644
--- a/sound/atmel/ac97c.c
+++ b/sound/atmel/ac97c.c
@@ -773,7 +773,7 @@ static int atmel_ac97c_pcm_new(struct atmel_ac97c *chip)
return err;
}
retval = snd_pcm_new(chip->card, chip->card->shortname,
- chip->pdev->id, playback, capture, &pcm);
+ 0, playback, capture, &pcm);
if (retval)
return retval;
@@ -944,7 +944,7 @@ static int atmel_ac97c_probe(struct platform_device *pdev)
dev_dbg(&pdev->dev, "no peripheral clock\n");
return PTR_ERR(pclk);
}
- clk_enable(pclk);
+ clk_prepare_enable(pclk);
retval = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1,
SNDRV_DEFAULT_STR1, THIS_MODULE,
@@ -1122,7 +1122,7 @@ err_ioremap:
err_request_irq:
snd_card_free(card);
err_snd_card_new:
- clk_disable(pclk);
+ clk_disable_unprepare(pclk);
clk_put(pclk);
return retval;
}
@@ -1139,7 +1139,7 @@ static int atmel_ac97c_suspend(struct device *pdev)
if (test_bit(DMA_TX_READY, &chip->flags))
dw_dma_cyclic_stop(chip->dma.tx_chan);
}
- clk_disable(chip->pclk);
+ clk_disable_unprepare(chip->pclk);
return 0;
}
@@ -1149,7 +1149,7 @@ static int atmel_ac97c_resume(struct device *pdev)
struct snd_card *card = dev_get_drvdata(pdev);
struct atmel_ac97c *chip = card->private_data;
- clk_enable(chip->pclk);
+ clk_prepare_enable(chip->pclk);
if (cpu_is_at32ap7000()) {
if (test_bit(DMA_RX_READY, &chip->flags))
dw_dma_cyclic_start(chip->dma.rx_chan);
@@ -1177,7 +1177,7 @@ static int atmel_ac97c_remove(struct platform_device *pdev)
ac97c_writel(chip, COMR, 0);
ac97c_writel(chip, MR, 0);
- clk_disable(chip->pclk);
+ clk_disable_unprepare(chip->pclk);
clk_put(chip->pclk);
iounmap(chip->regs);
free_irq(chip->irq, chip);
diff --git a/sound/core/Makefile b/sound/core/Makefile
index 394a38909f6b..4daf2f58261c 100644
--- a/sound/core/Makefile
+++ b/sound/core/Makefile
@@ -14,6 +14,9 @@ snd-pcm-y := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \
pcm_memory.o memalloc.o
snd-pcm-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o
+# for trace-points
+CFLAGS_pcm_lib.o := -I$(src)
+
snd-pcm-dmaengine-objs := pcm_dmaengine.o
snd-rawmidi-objs := rawmidi.o
diff --git a/sound/core/control.c b/sound/core/control.c
index b9611344ff9e..bb96a467e88d 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -141,6 +141,16 @@ static int snd_ctl_release(struct inode *inode, struct file *file)
return 0;
}
+/**
+ * snd_ctl_notify - Send notification to user-space for a control change
+ * @card: the card to send notification
+ * @mask: the event mask, SNDRV_CTL_EVENT_*
+ * @id: the ctl element id to send notification
+ *
+ * This function adds an event record with the given id and mask, appends
+ * to the list and wakes up the user-space for notification. This can be
+ * called in the atomic context.
+ */
void snd_ctl_notify(struct snd_card *card, unsigned int mask,
struct snd_ctl_elem_id *id)
{
@@ -179,7 +189,6 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask,
}
read_unlock(&card->ctl_files_rwlock);
}
-
EXPORT_SYMBOL(snd_ctl_notify);
/**
@@ -261,7 +270,6 @@ struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol,
kctl.private_data = private_data;
return snd_ctl_new(&kctl, access);
}
-
EXPORT_SYMBOL(snd_ctl_new1);
/**
@@ -280,7 +288,6 @@ void snd_ctl_free_one(struct snd_kcontrol *kcontrol)
kfree(kcontrol);
}
}
-
EXPORT_SYMBOL(snd_ctl_free_one);
static bool snd_ctl_remove_numid_conflict(struct snd_card *card,
@@ -376,7 +383,6 @@ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol)
snd_ctl_free_one(kcontrol);
return err;
}
-
EXPORT_SYMBOL(snd_ctl_add);
/**
@@ -471,7 +477,6 @@ int snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol)
snd_ctl_free_one(kcontrol);
return 0;
}
-
EXPORT_SYMBOL(snd_ctl_remove);
/**
@@ -499,7 +504,6 @@ int snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id)
up_write(&card->controls_rwsem);
return ret;
}
-
EXPORT_SYMBOL(snd_ctl_remove_id);
/**
@@ -568,7 +572,7 @@ int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id,
ret = -ENOENT;
goto unlock;
}
- index_offset = snd_ctl_get_ioff(kctl, &kctl->id);
+ index_offset = snd_ctl_get_ioff(kctl, id);
vd = &kctl->vd[index_offset];
ret = 0;
if (active) {
@@ -617,7 +621,6 @@ int snd_ctl_rename_id(struct snd_card *card, struct snd_ctl_elem_id *src_id,
up_write(&card->controls_rwsem);
return 0;
}
-
EXPORT_SYMBOL(snd_ctl_rename_id);
/**
@@ -645,7 +648,6 @@ struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card, unsigned int numi
}
return NULL;
}
-
EXPORT_SYMBOL(snd_ctl_find_numid);
/**
@@ -687,7 +689,6 @@ struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card,
}
return NULL;
}
-
EXPORT_SYMBOL(snd_ctl_find_id);
static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl,
@@ -1526,19 +1527,28 @@ static int _snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn, struct list_head *
return 0;
}
+/**
+ * snd_ctl_register_ioctl - register the device-specific control-ioctls
+ * @fcn: ioctl callback function
+ *
+ * called from each device manager like pcm.c, hwdep.c, etc.
+ */
int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn)
{
return _snd_ctl_register_ioctl(fcn, &snd_control_ioctls);
}
-
EXPORT_SYMBOL(snd_ctl_register_ioctl);
#ifdef CONFIG_COMPAT
+/**
+ * snd_ctl_register_ioctl_compat - register the device-specific 32bit compat
+ * control-ioctls
+ * @fcn: ioctl callback function
+ */
int snd_ctl_register_ioctl_compat(snd_kctl_ioctl_func_t fcn)
{
return _snd_ctl_register_ioctl(fcn, &snd_control_compat_ioctls);
}
-
EXPORT_SYMBOL(snd_ctl_register_ioctl_compat);
#endif
@@ -1566,19 +1576,26 @@ static int _snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn,
return -EINVAL;
}
+/**
+ * snd_ctl_unregister_ioctl - de-register the device-specific control-ioctls
+ * @fcn: ioctl callback function to unregister
+ */
int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn)
{
return _snd_ctl_unregister_ioctl(fcn, &snd_control_ioctls);
}
-
EXPORT_SYMBOL(snd_ctl_unregister_ioctl);
#ifdef CONFIG_COMPAT
+/**
+ * snd_ctl_unregister_ioctl - de-register the device-specific compat 32bit
+ * control-ioctls
+ * @fcn: ioctl callback function to unregister
+ */
int snd_ctl_unregister_ioctl_compat(snd_kctl_ioctl_func_t fcn)
{
return _snd_ctl_unregister_ioctl(fcn, &snd_control_compat_ioctls);
}
-
EXPORT_SYMBOL(snd_ctl_unregister_ioctl_compat);
#endif
@@ -1702,6 +1719,16 @@ int snd_ctl_create(struct snd_card *card)
/*
* Frequently used control callbacks/helpers
*/
+
+/**
+ * snd_ctl_boolean_mono_info - Helper function for a standard boolean info
+ * callback with a mono channel
+ * @kcontrol: the kcontrol instance
+ * @uinfo: info to store
+ *
+ * This is a function that can be used as info callback for a standard
+ * boolean control with a single mono channel.
+ */
int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -1711,9 +1738,17 @@ int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol,
uinfo->value.integer.max = 1;
return 0;
}
-
EXPORT_SYMBOL(snd_ctl_boolean_mono_info);
+/**
+ * snd_ctl_boolean_stereo_info - Helper function for a standard boolean info
+ * callback with stereo two channels
+ * @kcontrol: the kcontrol instance
+ * @uinfo: info to store
+ *
+ * This is a function that can be used as info callback for a standard
+ * boolean control with stereo two channels.
+ */
int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -1723,7 +1758,6 @@ int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol,
uinfo->value.integer.max = 1;
return 0;
}
-
EXPORT_SYMBOL(snd_ctl_boolean_stereo_info);
/**
@@ -1745,8 +1779,13 @@ int snd_ctl_enum_info(struct snd_ctl_elem_info *info, unsigned int channels,
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
info->count = channels;
info->value.enumerated.items = items;
+ if (!items)
+ return 0;
if (info->value.enumerated.item >= items)
info->value.enumerated.item = items - 1;
+ WARN(strlen(names[info->value.enumerated.item]) >= sizeof(info->value.enumerated.name),
+ "ALSA: too long item name '%s'\n",
+ names[info->value.enumerated.item]);
strlcpy(info->value.enumerated.name,
names[info->value.enumerated.item],
sizeof(info->value.enumerated.name));
diff --git a/sound/core/init.c b/sound/core/init.c
index 7bdfd19e24a8..074875d68c15 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -438,17 +438,6 @@ int snd_card_disconnect(struct snd_card *card)
EXPORT_SYMBOL(snd_card_disconnect);
-/**
- * snd_card_free - frees given soundcard structure
- * @card: soundcard structure
- *
- * This function releases the soundcard structure and the all assigned
- * devices automatically. That is, you don't have to release the devices
- * by yourself.
- *
- * Return: Zero. Frees all associated devices and frees the control
- * interface associated to given soundcard.
- */
static int snd_card_do_free(struct snd_card *card)
{
#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
@@ -469,6 +458,15 @@ static int snd_card_do_free(struct snd_card *card)
return 0;
}
+/**
+ * snd_card_free_when_closed - Disconnect the card, free it later eventually
+ * @card: soundcard structure
+ *
+ * Unlike snd_card_free(), this function doesn't try to release the card
+ * resource immediately, but tries to disconnect at first. When the card
+ * is still in use, the function returns before freeing the resources.
+ * The card resources will be freed when the refcount gets to zero.
+ */
int snd_card_free_when_closed(struct snd_card *card)
{
int ret = snd_card_disconnect(card);
@@ -479,6 +477,19 @@ int snd_card_free_when_closed(struct snd_card *card)
}
EXPORT_SYMBOL(snd_card_free_when_closed);
+/**
+ * snd_card_free - frees given soundcard structure
+ * @card: soundcard structure
+ *
+ * This function releases the soundcard structure and the all assigned
+ * devices automatically. That is, you don't have to release the devices
+ * by yourself.
+ *
+ * This function waits until the all resources are properly released.
+ *
+ * Return: Zero. Frees all associated devices and frees the control
+ * interface associated to given soundcard.
+ */
int snd_card_free(struct snd_card *card)
{
struct completion released;
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index c6ff94ab1ad6..cfc56c806964 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -220,6 +220,10 @@ static char *snd_pcm_format_names[] = {
FORMAT(DSD_U32_BE),
};
+/**
+ * snd_pcm_format_name - Return a name string for the given PCM format
+ * @format: PCM format
+ */
const char *snd_pcm_format_name(snd_pcm_format_t format)
{
if ((__force unsigned int)format >= ARRAY_SIZE(snd_pcm_format_names))
@@ -481,6 +485,19 @@ static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry,
}
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
+static void snd_pcm_xrun_injection_write(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_pcm_substream *substream = entry->private_data;
+ struct snd_pcm_runtime *runtime;
+
+ snd_pcm_stream_lock_irq(substream);
+ runtime = substream->runtime;
+ if (runtime && runtime->status->state == SNDRV_PCM_STATE_RUNNING)
+ snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+ snd_pcm_stream_unlock_irq(substream);
+}
+
static void snd_pcm_xrun_debug_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
@@ -612,6 +629,22 @@ static int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream)
}
substream->proc_status_entry = entry;
+#ifdef CONFIG_SND_PCM_XRUN_DEBUG
+ entry = snd_info_create_card_entry(card, "xrun_injection",
+ substream->proc_root);
+ if (entry) {
+ entry->private_data = substream;
+ entry->c.text.read = NULL;
+ entry->c.text.write = snd_pcm_xrun_injection_write;
+ entry->mode = S_IFREG | S_IWUSR;
+ if (snd_info_register(entry) < 0) {
+ snd_info_free_entry(entry);
+ entry = NULL;
+ }
+ }
+ substream->proc_xrun_injection_entry = entry;
+#endif /* CONFIG_SND_PCM_XRUN_DEBUG */
+
return 0;
}
@@ -625,6 +658,10 @@ static int snd_pcm_substream_proc_done(struct snd_pcm_substream *substream)
substream->proc_sw_params_entry = NULL;
snd_info_free_entry(substream->proc_status_entry);
substream->proc_status_entry = NULL;
+#ifdef CONFIG_SND_PCM_XRUN_DEBUG
+ snd_info_free_entry(substream->proc_xrun_injection_entry);
+ substream->proc_xrun_injection_entry = NULL;
+#endif
snd_info_free_entry(substream->proc_root);
substream->proc_root = NULL;
return 0;
@@ -709,7 +746,6 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
}
return 0;
}
-
EXPORT_SYMBOL(snd_pcm_new_stream);
static int _snd_pcm_new(struct snd_card *card, const char *id, int device,
@@ -1157,6 +1193,15 @@ static int snd_pcm_dev_disconnect(struct snd_device *device)
return 0;
}
+/**
+ * snd_pcm_notify - Add/remove the notify list
+ * @notify: PCM notify list
+ * @nfree: 0 = register, 1 = unregister
+ *
+ * This adds the given notifier to the global list so that the callback is
+ * called for each registered PCM devices. This exists only for PCM OSS
+ * emulation, so far.
+ */
int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree)
{
struct snd_pcm *pcm;
@@ -1179,7 +1224,6 @@ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree)
mutex_unlock(&register_mutex);
return 0;
}
-
EXPORT_SYMBOL(snd_pcm_notify);
#ifdef CONFIG_PROC_FS
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index dfc28542a007..ec9e7866177f 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -32,6 +32,15 @@
#include <sound/pcm_params.h>
#include <sound/timer.h>
+#ifdef CONFIG_SND_PCM_XRUN_DEBUG
+#define CREATE_TRACE_POINTS
+#include "pcm_trace.h"
+#else
+#define trace_hwptr(substream, pos, in_interrupt)
+#define trace_xrun(substream)
+#define trace_hw_ptr_error(substream, reason)
+#endif
+
/*
* fill ring buffer with silence
* runtime->silence_start: starting pointer to silence area
@@ -146,10 +155,6 @@ EXPORT_SYMBOL(snd_pcm_debug_name);
#define XRUN_DEBUG_BASIC (1<<0)
#define XRUN_DEBUG_STACK (1<<1) /* dump also stack */
#define XRUN_DEBUG_JIFFIESCHECK (1<<2) /* do jiffies check */
-#define XRUN_DEBUG_PERIODUPDATE (1<<3) /* full period update info */
-#define XRUN_DEBUG_HWPTRUPDATE (1<<4) /* full hwptr update info */
-#define XRUN_DEBUG_LOG (1<<5) /* show last 10 positions on err */
-#define XRUN_DEBUG_LOGONCE (1<<6) /* do above only once */
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
@@ -168,6 +173,7 @@ static void xrun(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
+ trace_xrun(substream);
if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
@@ -180,97 +186,19 @@ static void xrun(struct snd_pcm_substream *substream)
}
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
-#define hw_ptr_error(substream, fmt, args...) \
+#define hw_ptr_error(substream, in_interrupt, reason, fmt, args...) \
do { \
+ trace_hw_ptr_error(substream, reason); \
if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { \
- xrun_log_show(substream); \
- pr_err_ratelimited("ALSA: PCM: " fmt, ##args); \
+ pr_err_ratelimited("ALSA: PCM: [%c] " reason ": " fmt, \
+ (in_interrupt) ? 'Q' : 'P', ##args); \
dump_stack_on_xrun(substream); \
} \
} while (0)
-#define XRUN_LOG_CNT 10
-
-struct hwptr_log_entry {
- unsigned int in_interrupt;
- unsigned long jiffies;
- snd_pcm_uframes_t pos;
- snd_pcm_uframes_t period_size;
- snd_pcm_uframes_t buffer_size;
- snd_pcm_uframes_t old_hw_ptr;
- snd_pcm_uframes_t hw_ptr_base;
-};
-
-struct snd_pcm_hwptr_log {
- unsigned int idx;
- unsigned int hit: 1;
- struct hwptr_log_entry entries[XRUN_LOG_CNT];
-};
-
-static void xrun_log(struct snd_pcm_substream *substream,
- snd_pcm_uframes_t pos, int in_interrupt)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_pcm_hwptr_log *log = runtime->hwptr_log;
- struct hwptr_log_entry *entry;
-
- if (log == NULL) {
- log = kzalloc(sizeof(*log), GFP_ATOMIC);
- if (log == NULL)
- return;
- runtime->hwptr_log = log;
- } else {
- if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit)
- return;
- }
- entry = &log->entries[log->idx];
- entry->in_interrupt = in_interrupt;
- entry->jiffies = jiffies;
- entry->pos = pos;
- entry->period_size = runtime->period_size;
- entry->buffer_size = runtime->buffer_size;
- entry->old_hw_ptr = runtime->status->hw_ptr;
- entry->hw_ptr_base = runtime->hw_ptr_base;
- log->idx = (log->idx + 1) % XRUN_LOG_CNT;
-}
-
-static void xrun_log_show(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_hwptr_log *log = substream->runtime->hwptr_log;
- struct hwptr_log_entry *entry;
- char name[16];
- unsigned int idx;
- int cnt;
-
- if (log == NULL)
- return;
- if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit)
- return;
- snd_pcm_debug_name(substream, name, sizeof(name));
- for (cnt = 0, idx = log->idx; cnt < XRUN_LOG_CNT; cnt++) {
- entry = &log->entries[idx];
- if (entry->period_size == 0)
- break;
- pr_info("hwptr log: %s: %sj=%lu, pos=%ld/%ld/%ld, "
- "hwptr=%ld/%ld\n",
- name, entry->in_interrupt ? "[Q] " : "",
- entry->jiffies,
- (unsigned long)entry->pos,
- (unsigned long)entry->period_size,
- (unsigned long)entry->buffer_size,
- (unsigned long)entry->old_hw_ptr,
- (unsigned long)entry->hw_ptr_base);
- idx++;
- idx %= XRUN_LOG_CNT;
- }
- log->hit = 1;
-}
-
#else /* ! CONFIG_SND_PCM_XRUN_DEBUG */
#define hw_ptr_error(substream, fmt, args...) do { } while (0)
-#define xrun_log(substream, pos, in_interrupt) do { } while (0)
-#define xrun_log_show(substream) do { } while (0)
#endif
@@ -343,17 +271,15 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
if (printk_ratelimit()) {
char name[16];
snd_pcm_debug_name(substream, name, sizeof(name));
- xrun_log_show(substream);
pcm_err(substream->pcm,
- "XRUN: %s, pos = %ld, buffer size = %ld, period size = %ld\n",
+ "BUG: %s, pos = %ld, buffer size = %ld, period size = %ld\n",
name, pos, runtime->buffer_size,
runtime->period_size);
}
pos = 0;
}
pos -= pos % runtime->min_align;
- if (xrun_debug(substream, XRUN_DEBUG_LOG))
- xrun_log(substream, pos, in_interrupt);
+ trace_hwptr(substream, pos, in_interrupt);
hw_base = runtime->hw_ptr_base;
new_hw_ptr = hw_base + pos;
if (in_interrupt) {
@@ -388,22 +314,6 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
delta = new_hw_ptr - old_hw_ptr;
if (delta < 0)
delta += runtime->boundary;
- if (xrun_debug(substream, in_interrupt ?
- XRUN_DEBUG_PERIODUPDATE : XRUN_DEBUG_HWPTRUPDATE)) {
- char name[16];
- snd_pcm_debug_name(substream, name, sizeof(name));
- pcm_dbg(substream->pcm,
- "%s_update: %s: pos=%u/%u/%u, hwptr=%ld/%ld/%ld/%ld\n",
- in_interrupt ? "period" : "hwptr",
- name,
- (unsigned int)pos,
- (unsigned int)runtime->period_size,
- (unsigned int)runtime->buffer_size,
- (unsigned long)delta,
- (unsigned long)old_hw_ptr,
- (unsigned long)new_hw_ptr,
- (unsigned long)runtime->hw_ptr_base);
- }
if (runtime->no_period_wakeup) {
snd_pcm_sframes_t xrun_threshold;
@@ -431,13 +341,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
/* something must be really wrong */
if (delta >= runtime->buffer_size + runtime->period_size) {
- hw_ptr_error(substream,
- "Unexpected hw_pointer value %s"
- "(stream=%i, pos=%ld, new_hw_ptr=%ld, "
- "old_hw_ptr=%ld)\n",
- in_interrupt ? "[Q] " : "[P]",
- substream->stream, (long)pos,
- (long)new_hw_ptr, (long)old_hw_ptr);
+ hw_ptr_error(substream, in_interrupt, "Unexpected hw_ptr",
+ "(stream=%i, pos=%ld, new_hw_ptr=%ld, old_hw_ptr=%ld)\n",
+ substream->stream, (long)pos,
+ (long)new_hw_ptr, (long)old_hw_ptr);
return 0;
}
@@ -474,11 +381,8 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
delta--;
}
/* align hw_base to buffer_size */
- hw_ptr_error(substream,
- "hw_ptr skipping! %s"
- "(pos=%ld, delta=%ld, period=%ld, "
- "jdelta=%lu/%lu/%lu, hw_ptr=%ld/%ld)\n",
- in_interrupt ? "[Q] " : "",
+ hw_ptr_error(substream, in_interrupt, "hw_ptr skipping",
+ "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu/%lu, hw_ptr=%ld/%ld)\n",
(long)pos, (long)hdelta,
(long)runtime->period_size, jdelta,
((hdelta * HZ) / runtime->rate), hw_base,
@@ -490,11 +394,9 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
}
no_jiffies_check:
if (delta > runtime->period_size + runtime->period_size / 2) {
- hw_ptr_error(substream,
- "Lost interrupts? %s"
- "(stream=%i, delta=%ld, new_hw_ptr=%ld, "
- "old_hw_ptr=%ld)\n",
- in_interrupt ? "[Q] " : "",
+ hw_ptr_error(substream, in_interrupt,
+ "Lost interrupts?",
+ "(stream=%i, delta=%ld, new_hw_ptr=%ld, old_hw_ptr=%ld)\n",
substream->stream, (long)delta,
(long)new_hw_ptr,
(long)old_hw_ptr);
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 166d59cdc86b..095d9572ad2b 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -35,9 +35,6 @@
#include <sound/timer.h>
#include <sound/minors.h>
#include <asm/io.h>
-#if defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT)
-#include <dma-coherence.h>
-#endif
/*
* Compatibility
@@ -77,6 +74,14 @@ static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream);
static DEFINE_RWLOCK(snd_pcm_link_rwlock);
static DECLARE_RWSEM(snd_pcm_link_rwsem);
+/**
+ * snd_pcm_stream_lock - Lock the PCM stream
+ * @substream: PCM substream
+ *
+ * This locks the PCM stream's spinlock or mutex depending on the nonatomic
+ * flag of the given substream. This also takes the global link rw lock
+ * (or rw sem), too, for avoiding the race with linked streams.
+ */
void snd_pcm_stream_lock(struct snd_pcm_substream *substream)
{
if (substream->pcm->nonatomic) {
@@ -89,6 +94,12 @@ void snd_pcm_stream_lock(struct snd_pcm_substream *substream)
}
EXPORT_SYMBOL_GPL(snd_pcm_stream_lock);
+/**
+ * snd_pcm_stream_lock - Unlock the PCM stream
+ * @substream: PCM substream
+ *
+ * This unlocks the PCM stream that has been locked via snd_pcm_stream_lock().
+ */
void snd_pcm_stream_unlock(struct snd_pcm_substream *substream)
{
if (substream->pcm->nonatomic) {
@@ -101,6 +112,14 @@ void snd_pcm_stream_unlock(struct snd_pcm_substream *substream)
}
EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock);
+/**
+ * snd_pcm_stream_lock_irq - Lock the PCM stream
+ * @substream: PCM substream
+ *
+ * This locks the PCM stream like snd_pcm_stream_lock() and disables the local
+ * IRQ (only when nonatomic is false). In nonatomic case, this is identical
+ * as snd_pcm_stream_lock().
+ */
void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream)
{
if (!substream->pcm->nonatomic)
@@ -109,6 +128,12 @@ void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream)
}
EXPORT_SYMBOL_GPL(snd_pcm_stream_lock_irq);
+/**
+ * snd_pcm_stream_unlock_irq - Unlock the PCM stream
+ * @substream: PCM substream
+ *
+ * This is a counter-part of snd_pcm_stream_lock_irq().
+ */
void snd_pcm_stream_unlock_irq(struct snd_pcm_substream *substream)
{
snd_pcm_stream_unlock(substream);
@@ -127,6 +152,13 @@ unsigned long _snd_pcm_stream_lock_irqsave(struct snd_pcm_substream *substream)
}
EXPORT_SYMBOL_GPL(_snd_pcm_stream_lock_irqsave);
+/**
+ * snd_pcm_stream_unlock_irqrestore - Unlock the PCM stream
+ * @substream: PCM substream
+ * @flags: irq flags
+ *
+ * This is a counter-part of snd_pcm_stream_lock_irqsave().
+ */
void snd_pcm_stream_unlock_irqrestore(struct snd_pcm_substream *substream,
unsigned long flags)
{
@@ -195,6 +227,21 @@ int snd_pcm_info_user(struct snd_pcm_substream *substream,
return err;
}
+static bool hw_support_mmap(struct snd_pcm_substream *substream)
+{
+ if (!(substream->runtime->hw.info & SNDRV_PCM_INFO_MMAP))
+ return false;
+ /* check architectures that return -EINVAL from dma_mmap_coherent() */
+ /* FIXME: this should be some global flag */
+#if defined(CONFIG_C6X) || defined(CONFIG_FRV) || defined(CONFIG_MN10300) ||\
+ defined(CONFIG_PARISC) || defined(CONFIG_XTENSA)
+ if (!substream->ops->mmap &&
+ substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV)
+ return false;
+#endif
+ return true;
+}
+
#undef RULES_DEBUG
#ifdef RULES_DEBUG
@@ -372,8 +419,12 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream,
}
hw = &substream->runtime->hw;
- if (!params->info)
+ if (!params->info) {
params->info = hw->info & ~SNDRV_PCM_INFO_FIFO_IN_FRAMES;
+ if (!hw_support_mmap(substream))
+ params->info &= ~(SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID);
+ }
if (!params->fifo_size) {
m = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
@@ -849,14 +900,19 @@ static int snd_pcm_action_single(struct action_ops *ops,
return res;
}
-/* call in mutex-protected context */
-static int snd_pcm_action_mutex(struct action_ops *ops,
- struct snd_pcm_substream *substream,
- int state)
+/*
+ * Note: call with stream lock
+ */
+static int snd_pcm_action(struct action_ops *ops,
+ struct snd_pcm_substream *substream,
+ int state)
{
int res;
- if (snd_pcm_stream_linked(substream)) {
+ if (!snd_pcm_stream_linked(substream))
+ return snd_pcm_action_single(ops, substream, state);
+
+ if (substream->pcm->nonatomic) {
if (!mutex_trylock(&substream->group->mutex)) {
mutex_unlock(&substream->self_group.mutex);
mutex_lock(&substream->group->mutex);
@@ -865,24 +921,6 @@ static int snd_pcm_action_mutex(struct action_ops *ops,
res = snd_pcm_action_group(ops, substream, state, 1);
mutex_unlock(&substream->group->mutex);
} else {
- res = snd_pcm_action_single(ops, substream, state);
- }
- return res;
-}
-
-/*
- * Note: call with stream lock
- */
-static int snd_pcm_action(struct action_ops *ops,
- struct snd_pcm_substream *substream,
- int state)
-{
- int res;
-
- if (substream->pcm->nonatomic)
- return snd_pcm_action_mutex(ops, substream, state);
-
- if (snd_pcm_stream_linked(substream)) {
if (!spin_trylock(&substream->group->lock)) {
spin_unlock(&substream->self_group.lock);
spin_lock(&substream->group->lock);
@@ -890,34 +928,10 @@ static int snd_pcm_action(struct action_ops *ops,
}
res = snd_pcm_action_group(ops, substream, state, 1);
spin_unlock(&substream->group->lock);
- } else {
- res = snd_pcm_action_single(ops, substream, state);
}
return res;
}
-static int snd_pcm_action_lock_mutex(struct action_ops *ops,
- struct snd_pcm_substream *substream,
- int state)
-{
- int res;
-
- down_read(&snd_pcm_link_rwsem);
- if (snd_pcm_stream_linked(substream)) {
- mutex_lock(&substream->group->mutex);
- mutex_lock(&substream->self_group.mutex);
- res = snd_pcm_action_group(ops, substream, state, 1);
- mutex_unlock(&substream->self_group.mutex);
- mutex_unlock(&substream->group->mutex);
- } else {
- mutex_lock(&substream->self_group.mutex);
- res = snd_pcm_action_single(ops, substream, state);
- mutex_unlock(&substream->self_group.mutex);
- }
- up_read(&snd_pcm_link_rwsem);
- return res;
-}
-
/*
* Note: don't use any locks before
*/
@@ -927,22 +941,9 @@ static int snd_pcm_action_lock_irq(struct action_ops *ops,
{
int res;
- if (substream->pcm->nonatomic)
- return snd_pcm_action_lock_mutex(ops, substream, state);
-
- read_lock_irq(&snd_pcm_link_rwlock);
- if (snd_pcm_stream_linked(substream)) {
- spin_lock(&substream->group->lock);
- spin_lock(&substream->self_group.lock);
- res = snd_pcm_action_group(ops, substream, state, 1);
- spin_unlock(&substream->self_group.lock);
- spin_unlock(&substream->group->lock);
- } else {
- spin_lock(&substream->self_group.lock);
- res = snd_pcm_action_single(ops, substream, state);
- spin_unlock(&substream->self_group.lock);
- }
- read_unlock_irq(&snd_pcm_link_rwlock);
+ snd_pcm_stream_lock_irq(substream);
+ res = snd_pcm_action(ops, substream, state);
+ snd_pcm_stream_unlock_irq(substream);
return res;
}
@@ -1051,10 +1052,10 @@ static void snd_pcm_post_stop(struct snd_pcm_substream *substream, int state)
struct snd_pcm_runtime *runtime = substream->runtime;
if (runtime->status->state != state) {
snd_pcm_trigger_tstamp(substream);
+ runtime->status->state = state;
if (substream->timer)
snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTOP,
&runtime->trigger_tstamp);
- runtime->status->state = state;
}
wake_up(&runtime->sleep);
wake_up(&runtime->tsleep);
@@ -1097,6 +1098,28 @@ int snd_pcm_drain_done(struct snd_pcm_substream *substream)
SNDRV_PCM_STATE_SETUP);
}
+/**
+ * snd_pcm_stop_xrun - stop the running streams as XRUN
+ * @substream: the PCM substream instance
+ *
+ * This stops the given running substream (and all linked substreams) as XRUN.
+ * Unlike snd_pcm_stop(), this function takes the substream lock by itself.
+ *
+ * Return: Zero if successful, or a negative error code.
+ */
+int snd_pcm_stop_xrun(struct snd_pcm_substream *substream)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ snd_pcm_stream_lock_irqsave(substream, flags);
+ if (snd_pcm_running(substream))
+ ret = snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+ snd_pcm_stream_unlock_irqrestore(substream, flags);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_pcm_stop_xrun);
+
/*
* pause callbacks
*/
@@ -1203,11 +1226,11 @@ static void snd_pcm_post_suspend(struct snd_pcm_substream *substream, int state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_trigger_tstamp(substream);
+ runtime->status->suspended_state = runtime->status->state;
+ runtime->status->state = SNDRV_PCM_STATE_SUSPENDED;
if (substream->timer)
snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSUSPEND,
&runtime->trigger_tstamp);
- runtime->status->suspended_state = runtime->status->state;
- runtime->status->state = SNDRV_PCM_STATE_SUSPENDED;
wake_up(&runtime->sleep);
wake_up(&runtime->tsleep);
}
@@ -1310,10 +1333,10 @@ static void snd_pcm_post_resume(struct snd_pcm_substream *substream, int state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_trigger_tstamp(substream);
+ runtime->status->state = runtime->status->suspended_state;
if (substream->timer)
snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MRESUME,
&runtime->trigger_tstamp);
- runtime->status->state = runtime->status->suspended_state;
}
static struct action_ops snd_pcm_action_resume = {
@@ -2070,7 +2093,7 @@ int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
mask |= 1 << SNDRV_PCM_ACCESS_RW_INTERLEAVED;
if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED)
mask |= 1 << SNDRV_PCM_ACCESS_RW_NONINTERLEAVED;
- if (hw->info & SNDRV_PCM_INFO_MMAP) {
+ if (hw_support_mmap(substream)) {
if (hw->info & SNDRV_PCM_INFO_INTERLEAVED)
mask |= 1 << SNDRV_PCM_ACCESS_MMAP_INTERLEAVED;
if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED)
@@ -3249,20 +3272,6 @@ static inline struct page *
snd_pcm_default_page_ops(struct snd_pcm_substream *substream, unsigned long ofs)
{
void *vaddr = substream->runtime->dma_area + ofs;
-#if defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT)
- if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV)
- return virt_to_page(CAC_ADDR(vaddr));
-#endif
-#if defined(CONFIG_PPC32) && defined(CONFIG_NOT_COHERENT_CACHE)
- if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) {
- dma_addr_t addr = substream->runtime->dma_addr + ofs;
- addr -= get_dma_offset(substream->dma_buffer.dev.dev);
- /* assume dma_handle set via pfn_to_phys() in
- * mm/dma-noncoherent.c
- */
- return pfn_to_page(addr >> PAGE_SHIFT);
- }
-#endif
return virt_to_page(vaddr);
}
@@ -3307,16 +3316,18 @@ static const struct vm_operations_struct snd_pcm_vm_ops_data_fault = {
.fault = snd_pcm_mmap_data_fault,
};
-#ifndef ARCH_HAS_DMA_MMAP_COHERENT
-/* This should be defined / handled globally! */
-#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
-#define ARCH_HAS_DMA_MMAP_COHERENT
-#endif
-#endif
-
/*
* mmap the DMA buffer on RAM
*/
+
+/**
+ * snd_pcm_lib_default_mmap - Default PCM data mmap function
+ * @substream: PCM substream
+ * @area: VMA
+ *
+ * This is the default mmap handler for PCM data. When mmap pcm_ops is NULL,
+ * this function is invoked implicitly.
+ */
int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *area)
{
@@ -3329,7 +3340,7 @@ int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream,
area->vm_end - area->vm_start, area->vm_page_prot);
}
#endif /* CONFIG_GENERIC_ALLOCATOR */
-#ifdef ARCH_HAS_DMA_MMAP_COHERENT
+#ifndef CONFIG_X86 /* for avoiding warnings arch/x86/mm/pat.c */
if (!substream->ops->page &&
substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV)
return dma_mmap_coherent(substream->dma_buffer.dev.dev,
@@ -3337,11 +3348,7 @@ int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream,
substream->runtime->dma_area,
substream->runtime->dma_addr,
area->vm_end - area->vm_start);
-#elif defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT)
- if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV &&
- !plat_device_is_coherent(substream->dma_buffer.dev.dev))
- area->vm_page_prot = pgprot_noncached(area->vm_page_prot);
-#endif /* ARCH_HAS_DMA_MMAP_COHERENT */
+#endif /* CONFIG_X86 */
/* mmap with fault handler */
area->vm_ops = &snd_pcm_vm_ops_data_fault;
return 0;
@@ -3352,6 +3359,15 @@ EXPORT_SYMBOL_GPL(snd_pcm_lib_default_mmap);
* mmap the DMA buffer on I/O memory area
*/
#if SNDRV_PCM_INFO_MMAP_IOMEM
+/**
+ * snd_pcm_lib_mmap_iomem - Default PCM data mmap function for I/O mem
+ * @substream: PCM substream
+ * @area: VMA
+ *
+ * When your hardware uses the iomapped pages as the hardware buffer and
+ * wants to mmap it, pass this function as mmap pcm_ops. Note that this
+ * is supposed to work only on limited architectures.
+ */
int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream,
struct vm_area_struct *area)
{
diff --git a/sound/core/pcm_trace.h b/sound/core/pcm_trace.h
new file mode 100644
index 000000000000..b63b654da5ff
--- /dev/null
+++ b/sound/core/pcm_trace.h
@@ -0,0 +1,110 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM snd_pcm
+#define TRACE_INCLUDE_FILE pcm_trace
+
+#if !defined(_PCM_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _PCM_TRACE_H
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(hwptr,
+ TP_PROTO(struct snd_pcm_substream *substream, snd_pcm_uframes_t pos, bool irq),
+ TP_ARGS(substream, pos, irq),
+ TP_STRUCT__entry(
+ __field( bool, in_interrupt )
+ __field( unsigned int, card )
+ __field( unsigned int, device )
+ __field( unsigned int, number )
+ __field( unsigned int, stream )
+ __field( snd_pcm_uframes_t, pos )
+ __field( snd_pcm_uframes_t, period_size )
+ __field( snd_pcm_uframes_t, buffer_size )
+ __field( snd_pcm_uframes_t, old_hw_ptr )
+ __field( snd_pcm_uframes_t, hw_ptr_base )
+ ),
+ TP_fast_assign(
+ __entry->in_interrupt = (irq);
+ __entry->card = (substream)->pcm->card->number;
+ __entry->device = (substream)->pcm->device;
+ __entry->number = (substream)->number;
+ __entry->stream = (substream)->stream;
+ __entry->pos = (pos);
+ __entry->period_size = (substream)->runtime->period_size;
+ __entry->buffer_size = (substream)->runtime->buffer_size;
+ __entry->old_hw_ptr = (substream)->runtime->status->hw_ptr;
+ __entry->hw_ptr_base = (substream)->runtime->hw_ptr_base;
+ ),
+ TP_printk("pcmC%dD%d%c/sub%d: %s: pos=%lu, old=%lu, base=%lu, period=%lu, buf=%lu",
+ __entry->card, __entry->device,
+ __entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c',
+ __entry->number,
+ __entry->in_interrupt ? "IRQ" : "POS",
+ (unsigned long)__entry->pos,
+ (unsigned long)__entry->old_hw_ptr,
+ (unsigned long)__entry->hw_ptr_base,
+ (unsigned long)__entry->period_size,
+ (unsigned long)__entry->buffer_size)
+);
+
+TRACE_EVENT(xrun,
+ TP_PROTO(struct snd_pcm_substream *substream),
+ TP_ARGS(substream),
+ TP_STRUCT__entry(
+ __field( unsigned int, card )
+ __field( unsigned int, device )
+ __field( unsigned int, number )
+ __field( unsigned int, stream )
+ __field( snd_pcm_uframes_t, period_size )
+ __field( snd_pcm_uframes_t, buffer_size )
+ __field( snd_pcm_uframes_t, old_hw_ptr )
+ __field( snd_pcm_uframes_t, hw_ptr_base )
+ ),
+ TP_fast_assign(
+ __entry->card = (substream)->pcm->card->number;
+ __entry->device = (substream)->pcm->device;
+ __entry->number = (substream)->number;
+ __entry->stream = (substream)->stream;
+ __entry->period_size = (substream)->runtime->period_size;
+ __entry->buffer_size = (substream)->runtime->buffer_size;
+ __entry->old_hw_ptr = (substream)->runtime->status->hw_ptr;
+ __entry->hw_ptr_base = (substream)->runtime->hw_ptr_base;
+ ),
+ TP_printk("pcmC%dD%d%c/sub%d: XRUN: old=%lu, base=%lu, period=%lu, buf=%lu",
+ __entry->card, __entry->device,
+ __entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c',
+ __entry->number,
+ (unsigned long)__entry->old_hw_ptr,
+ (unsigned long)__entry->hw_ptr_base,
+ (unsigned long)__entry->period_size,
+ (unsigned long)__entry->buffer_size)
+);
+
+TRACE_EVENT(hw_ptr_error,
+ TP_PROTO(struct snd_pcm_substream *substream, const char *why),
+ TP_ARGS(substream, why),
+ TP_STRUCT__entry(
+ __field( unsigned int, card )
+ __field( unsigned int, device )
+ __field( unsigned int, number )
+ __field( unsigned int, stream )
+ __field( const char *, reason )
+ ),
+ TP_fast_assign(
+ __entry->card = (substream)->pcm->card->number;
+ __entry->device = (substream)->pcm->device;
+ __entry->number = (substream)->number;
+ __entry->stream = (substream)->stream;
+ __entry->reason = (why);
+ ),
+ TP_printk("pcmC%dD%d%c/sub%d: ERROR: %s",
+ __entry->card, __entry->device,
+ __entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c',
+ __entry->number, __entry->reason)
+);
+
+#endif /* _PCM_TRACE_H */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#include <trace/define_trace.h>
diff --git a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c
index b9184d20c39f..b0e32e161dd1 100644
--- a/sound/core/seq/oss/seq_oss_init.c
+++ b/sound/core/seq/oss/seq_oss_init.c
@@ -403,14 +403,11 @@ free_devinfo(void *private)
{
struct seq_oss_devinfo *dp = (struct seq_oss_devinfo *)private;
- if (dp->timer)
- snd_seq_oss_timer_delete(dp->timer);
+ snd_seq_oss_timer_delete(dp->timer);
- if (dp->writeq)
- snd_seq_oss_writeq_delete(dp->writeq);
+ snd_seq_oss_writeq_delete(dp->writeq);
- if (dp->readq)
- snd_seq_oss_readq_delete(dp->readq);
+ snd_seq_oss_readq_delete(dp->readq);
kfree(dp);
}
diff --git a/sound/core/seq/seq.c b/sound/core/seq/seq.c
index 712110561082..7e0aabb808a6 100644
--- a/sound/core/seq/seq.c
+++ b/sound/core/seq/seq.c
@@ -86,7 +86,6 @@ static int __init alsa_seq_init(void)
{
int err;
- snd_seq_autoload_lock();
if ((err = client_init_data()) < 0)
goto error;
@@ -110,8 +109,8 @@ static int __init alsa_seq_init(void)
if ((err = snd_seq_system_client_init()) < 0)
goto error;
+ snd_seq_autoload_init();
error:
- snd_seq_autoload_unlock();
return err;
}
@@ -131,6 +130,8 @@ static void __exit alsa_seq_exit(void)
/* release event memory */
snd_sequencer_memory_done();
+
+ snd_seq_autoload_exit();
}
module_init(alsa_seq_init)
diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c
index 91a786a783e1..0631bdadd12b 100644
--- a/sound/core/seq/seq_device.c
+++ b/sound/core/seq/seq_device.c
@@ -56,6 +56,7 @@ MODULE_LICENSE("GPL");
#define DRIVER_LOADED (1<<0)
#define DRIVER_REQUESTED (1<<1)
#define DRIVER_LOCKED (1<<2)
+#define DRIVER_REQUESTING (1<<3)
struct ops_list {
char id[ID_LEN]; /* driver id */
@@ -127,42 +128,82 @@ static void snd_seq_device_info(struct snd_info_entry *entry,
#ifdef CONFIG_MODULES
/* avoid auto-loading during module_init() */
-static int snd_seq_in_init;
+static atomic_t snd_seq_in_init = ATOMIC_INIT(1); /* blocked as default */
void snd_seq_autoload_lock(void)
{
- snd_seq_in_init++;
+ atomic_inc(&snd_seq_in_init);
}
void snd_seq_autoload_unlock(void)
{
- snd_seq_in_init--;
+ atomic_dec(&snd_seq_in_init);
}
-#endif
-void snd_seq_device_load_drivers(void)
+static void autoload_drivers(void)
{
-#ifdef CONFIG_MODULES
- struct ops_list *ops;
+ /* avoid reentrance */
+ if (atomic_inc_return(&snd_seq_in_init) == 1) {
+ struct ops_list *ops;
+
+ mutex_lock(&ops_mutex);
+ list_for_each_entry(ops, &opslist, list) {
+ if ((ops->driver & DRIVER_REQUESTING) &&
+ !(ops->driver & DRIVER_REQUESTED)) {
+ ops->used++;
+ mutex_unlock(&ops_mutex);
+ ops->driver |= DRIVER_REQUESTED;
+ request_module("snd-%s", ops->id);
+ mutex_lock(&ops_mutex);
+ ops->used--;
+ }
+ }
+ mutex_unlock(&ops_mutex);
+ }
+ atomic_dec(&snd_seq_in_init);
+}
- /* Calling request_module during module_init()
- * may cause blocking.
- */
- if (snd_seq_in_init)
- return;
+static void call_autoload(struct work_struct *work)
+{
+ autoload_drivers();
+}
- mutex_lock(&ops_mutex);
- list_for_each_entry(ops, &opslist, list) {
- if (! (ops->driver & DRIVER_LOADED) &&
- ! (ops->driver & DRIVER_REQUESTED)) {
- ops->used++;
- mutex_unlock(&ops_mutex);
- ops->driver |= DRIVER_REQUESTED;
- request_module("snd-%s", ops->id);
- mutex_lock(&ops_mutex);
- ops->used--;
- }
+static DECLARE_WORK(autoload_work, call_autoload);
+
+static void try_autoload(struct ops_list *ops)
+{
+ if (!ops->driver) {
+ ops->driver |= DRIVER_REQUESTING;
+ schedule_work(&autoload_work);
}
+}
+
+static void queue_autoload_drivers(void)
+{
+ struct ops_list *ops;
+
+ mutex_lock(&ops_mutex);
+ list_for_each_entry(ops, &opslist, list)
+ try_autoload(ops);
mutex_unlock(&ops_mutex);
+}
+
+void snd_seq_autoload_init(void)
+{
+ atomic_dec(&snd_seq_in_init);
+#ifdef CONFIG_SND_SEQUENCER_MODULE
+ /* initial autoload only when snd-seq is a module */
+ queue_autoload_drivers();
+#endif
+}
+#else
+#define try_autoload(ops) /* NOP */
+#endif
+
+void snd_seq_device_load_drivers(void)
+{
+#ifdef CONFIG_MODULES
+ queue_autoload_drivers();
+ flush_work(&autoload_work);
#endif
}
@@ -214,13 +255,14 @@ int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize,
ops->num_devices++;
mutex_unlock(&ops->reg_mutex);
- unlock_driver(ops);
-
if ((err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops)) < 0) {
snd_seq_device_free(dev);
return err;
}
+ try_autoload(ops);
+ unlock_driver(ops);
+
if (result)
*result = dev;
@@ -318,16 +360,12 @@ int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry,
entry->init_device == NULL || entry->free_device == NULL)
return -EINVAL;
- snd_seq_autoload_lock();
ops = find_driver(id, 1);
- if (ops == NULL) {
- snd_seq_autoload_unlock();
+ if (ops == NULL)
return -ENOMEM;
- }
if (ops->driver & DRIVER_LOADED) {
pr_warn("ALSA: seq: driver_register: driver '%s' already exists\n", id);
unlock_driver(ops);
- snd_seq_autoload_unlock();
return -EBUSY;
}
@@ -344,7 +382,6 @@ int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry,
mutex_unlock(&ops->reg_mutex);
unlock_driver(ops);
- snd_seq_autoload_unlock();
return 0;
}
@@ -554,6 +591,9 @@ static int __init alsa_seq_device_init(void)
static void __exit alsa_seq_device_exit(void)
{
+#ifdef CONFIG_MODULES
+ cancel_work_sync(&autoload_work);
+#endif
remove_drivers();
#ifdef CONFIG_PROC_FS
snd_info_free_entry(info_entry);
@@ -570,6 +610,7 @@ EXPORT_SYMBOL(snd_seq_device_new);
EXPORT_SYMBOL(snd_seq_device_register_driver);
EXPORT_SYMBOL(snd_seq_device_unregister_driver);
#ifdef CONFIG_MODULES
+EXPORT_SYMBOL(snd_seq_autoload_init);
EXPORT_SYMBOL(snd_seq_autoload_lock);
EXPORT_SYMBOL(snd_seq_autoload_unlock);
#endif
diff --git a/sound/core/sgbuf.c b/sound/core/sgbuf.c
index 0a418503ec41..84fffabdd129 100644
--- a/sound/core/sgbuf.c
+++ b/sound/core/sgbuf.c
@@ -39,8 +39,7 @@ int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab)
if (! sgbuf)
return -EINVAL;
- if (dmab->area)
- vunmap(dmab->area);
+ vunmap(dmab->area);
dmab->area = NULL;
tmpb.dev.type = SNDRV_DMA_TYPE_DEV;
diff --git a/sound/core/sound.c b/sound/core/sound.c
index 38ad1a0dd3f7..f1333060bf1c 100644
--- a/sound/core/sound.c
+++ b/sound/core/sound.c
@@ -355,8 +355,13 @@ int snd_unregister_device(int type, struct snd_card *card, int dev)
EXPORT_SYMBOL(snd_unregister_device);
-/* get the assigned device to the given type and device number;
- * the caller needs to release it via put_device() after using it
+/**
+ * snd_get_device - get the assigned device to the given type and device number
+ * @type: the device type, SNDRV_DEVICE_TYPE_XXX
+ * @card:the card instance
+ * @dev: the device index
+ *
+ * The caller needs to release it via put_device() after using it.
*/
struct device *snd_get_device(int type, struct snd_card *card, int dev)
{
diff --git a/sound/drivers/mts64.c b/sound/drivers/mts64.c
index f5fd448dbc57..0388fbbd2c06 100644
--- a/sound/drivers/mts64.c
+++ b/sound/drivers/mts64.c
@@ -604,21 +604,11 @@ static struct snd_kcontrol_new mts64_ctl_smpte_time_frames = {
static int snd_mts64_ctl_smpte_fps_info(struct snd_kcontrol *kctl,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[5] = { "24",
- "25",
- "29.97",
- "30D",
- "30" };
+ static const char * const texts[5] = {
+ "24", "25", "29.97", "30D", "30"
+ };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 5;
- if (uinfo->value.enumerated.item > 4)
- uinfo->value.enumerated.item = 4;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
-
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 5, texts);
}
static int snd_mts64_ctl_smpte_fps_get(struct snd_kcontrol *kctl,
diff --git a/sound/drivers/virmidi.c b/sound/drivers/virmidi.c
index b178724295f3..d28d8706443c 100644
--- a/sound/drivers/virmidi.c
+++ b/sound/drivers/virmidi.c
@@ -99,30 +99,33 @@ static int snd_virmidi_probe(struct platform_device *devptr)
if (midi_devs[dev] > MAX_MIDI_DEVICES) {
snd_printk(KERN_WARNING
- "too much midi devices for virmidi %d: "
- "force to use %d\n", dev, MAX_MIDI_DEVICES);
+ "too much midi devices for virmidi %d: force to use %d\n",
+ dev, MAX_MIDI_DEVICES);
midi_devs[dev] = MAX_MIDI_DEVICES;
}
for (idx = 0; idx < midi_devs[dev]; idx++) {
struct snd_rawmidi *rmidi;
struct snd_virmidi_dev *rdev;
- if ((err = snd_virmidi_new(card, idx, &rmidi)) < 0)
+
+ err = snd_virmidi_new(card, idx, &rmidi);
+ if (err < 0)
goto __nodev;
rdev = rmidi->private_data;
vmidi->midi[idx] = rmidi;
strcpy(rmidi->name, "Virtual Raw MIDI");
rdev->seq_mode = SNDRV_VIRMIDI_SEQ_DISPATCH;
}
-
+
strcpy(card->driver, "VirMIDI");
strcpy(card->shortname, "VirMIDI");
sprintf(card->longname, "Virtual MIDI Card %i", dev + 1);
- if ((err = snd_card_register(card)) == 0) {
+ err = snd_card_register(card);
+ if (!err) {
platform_set_drvdata(devptr, card);
return 0;
}
- __nodev:
+__nodev:
snd_card_free(card);
return err;
}
@@ -157,13 +160,15 @@ static int __init alsa_card_virmidi_init(void)
{
int i, cards, err;
- if ((err = platform_driver_register(&snd_virmidi_driver)) < 0)
+ err = platform_driver_register(&snd_virmidi_driver);
+ if (err < 0)
return err;
cards = 0;
for (i = 0; i < SNDRV_CARDS; i++) {
struct platform_device *device;
- if (! enable[i])
+
+ if (!enable[i])
continue;
device = platform_device_register_simple(SND_VIRMIDI_DRIVER,
i, NULL, 0);
diff --git a/sound/drivers/vx/vx_core.c b/sound/drivers/vx/vx_core.c
index e8cc16993903..fc05a37fd017 100644
--- a/sound/drivers/vx/vx_core.c
+++ b/sound/drivers/vx/vx_core.c
@@ -416,6 +416,7 @@ int vx_send_rih(struct vx_core *chip, int cmd)
/**
* snd_vx_boot_xilinx - boot up the xilinx interface
+ * @chip: VX core instance
* @boot: the boot record to load
*/
int snd_vx_load_boot_image(struct vx_core *chip, const struct firmware *boot)
@@ -538,6 +539,8 @@ EXPORT_SYMBOL(snd_vx_threaded_irq_handler);
/**
* snd_vx_irq_handler - interrupt handler
+ * @irq: irq number
+ * @dev: VX core instance
*/
irqreturn_t snd_vx_irq_handler(int irq, void *dev)
{
@@ -649,6 +652,8 @@ static void vx_proc_init(struct vx_core *chip)
/**
* snd_vx_dsp_boot - load the DSP boot
+ * @chip: VX core instance
+ * @boot: firmware data
*/
int snd_vx_dsp_boot(struct vx_core *chip, const struct firmware *boot)
{
@@ -669,6 +674,8 @@ EXPORT_SYMBOL(snd_vx_dsp_boot);
/**
* snd_vx_dsp_load - load the DSP image
+ * @chip: VX core instance
+ * @dsp: firmware data
*/
int snd_vx_dsp_load(struct vx_core *chip, const struct firmware *dsp)
{
@@ -768,7 +775,10 @@ EXPORT_SYMBOL(snd_vx_resume);
/**
* snd_vx_create - constructor for struct vx_core
+ * @card: card instance
* @hw: hardware specific record
+ * @ops: VX ops pointer
+ * @extra_size: extra byte size to allocate appending to chip
*
* this function allocates the instance and prepare for the hardware
* initialization.
diff --git a/sound/drivers/vx/vx_mixer.c b/sound/drivers/vx/vx_mixer.c
index 3b6823fc0606..be9477e30739 100644
--- a/sound/drivers/vx/vx_mixer.c
+++ b/sound/drivers/vx/vx_mixer.c
@@ -471,30 +471,18 @@ static struct snd_kcontrol_new vx_control_output_level = {
*/
static int vx_audio_src_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts_mic[3] = {
+ static const char * const texts_mic[3] = {
"Digital", "Line", "Mic"
};
- static char *texts_vx2[2] = {
+ static const char * const texts_vx2[2] = {
"Digital", "Analog"
};
struct vx_core *chip = snd_kcontrol_chip(kcontrol);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- if (chip->type >= VX_TYPE_VXPOCKET) {
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item > 2)
- uinfo->value.enumerated.item = 2;
- strcpy(uinfo->value.enumerated.name,
- texts_mic[uinfo->value.enumerated.item]);
- } else {
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name,
- texts_vx2[uinfo->value.enumerated.item]);
- }
- return 0;
+ if (chip->type >= VX_TYPE_VXPOCKET)
+ return snd_ctl_enum_info(uinfo, 1, 3, texts_mic);
+ else
+ return snd_ctl_enum_info(uinfo, 1, 2, texts_vx2);
}
static int vx_audio_src_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -539,18 +527,11 @@ static struct snd_kcontrol_new vx_control_audio_src = {
*/
static int vx_clock_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[3] = {
+ static const char * const texts[3] = {
"Auto", "Internal", "External"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item > 2)
- uinfo->value.enumerated.item = 2;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int vx_clock_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index 46dff64908c8..ecec547782b2 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -13,28 +13,34 @@ config SND_FIREWIRE_LIB
select SND_RAWMIDI
config SND_DICE
- tristate "DICE-based DACs (EXPERIMENTAL)"
+ tristate "DICE-based DACs support"
select SND_HWDEP
select SND_FIREWIRE_LIB
help
Say Y here to include support for many DACs based on the DICE
- chip family (DICE-II/Jr/Mini) from TC Applied Technologies.
-
- At the moment, this driver supports playback only. If you
- want to use devices that support capturing, use FFADO instead.
+ chip family (DICE-II/Jr/Mini) which TC Applied Technologies produces.
To compile this driver as a module, choose M here: the module
will be called snd-dice.
-config SND_FIREWIRE_SPEAKERS
- tristate "FireWire speakers"
+config SND_OXFW
+ tristate "Oxford Semiconductor FW970/971 chipset support"
select SND_FIREWIRE_LIB
+ select SND_HWDEP
help
- Say Y here to include support for the Griffin FireWave Surround
- and the LaCie FireWire Speakers.
+ Say Y here to include support for FireWire devices based on
+ Oxford Semiconductor FW970/971 chipset.
+ * Griffin Firewave
+ * LaCie Firewire Speakers
+ * Behringer F-Control Audio 202
+ * Mackie(Loud) Onyx-i series (former models)
+ * Mackie(Loud) Onyx Satellite
+ * Mackie(Loud) Tapco Link.Firewire
+ * Mackie(Loud) d.2 pro/d.4 pro
+ * Mackie(Loud) U.420/U.420d
To compile this driver as a module, choose M here: the module
- will be called snd-firewire-speakers.
+ will be called snd-oxfw.
config SND_ISIGHT
tristate "Apple iSight microphone"
diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile
index fad8d49306ab..8b37f084b2ab 100644
--- a/sound/firewire/Makefile
+++ b/sound/firewire/Makefile
@@ -1,13 +1,12 @@
snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \
fcp.o cmp.o amdtp.o
-snd-dice-objs := dice.o
-snd-firewire-speakers-objs := speakers.o
+snd-oxfw-objs := oxfw.o
snd-isight-objs := isight.o
snd-scs1x-objs := scs1x.o
obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o
-obj-$(CONFIG_SND_DICE) += snd-dice.o
-obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o
+obj-$(CONFIG_SND_DICE) += dice/
+obj-$(CONFIG_SND_OXFW) += oxfw/
obj-$(CONFIG_SND_ISIGHT) += snd-isight.o
obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o
obj-$(CONFIG_SND_FIREWORKS) += fireworks/
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c
index 95fc2eaf11dc..3badc70124ab 100644
--- a/sound/firewire/amdtp.c
+++ b/sound/firewire/amdtp.c
@@ -1006,11 +1006,7 @@ void amdtp_stream_pcm_abort(struct amdtp_stream *s)
struct snd_pcm_substream *pcm;
pcm = ACCESS_ONCE(s->pcm);
- if (pcm) {
- snd_pcm_stream_lock_irq(pcm);
- if (snd_pcm_running(pcm))
- snd_pcm_stop(pcm, SNDRV_PCM_STATE_XRUN);
- snd_pcm_stream_unlock_irq(pcm);
- }
+ if (pcm)
+ snd_pcm_stop_xrun(pcm);
}
EXPORT_SYMBOL(amdtp_stream_pcm_abort);
diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h
index 4823c08196ac..e6e8926275b0 100644
--- a/sound/firewire/amdtp.h
+++ b/sound/firewire/amdtp.h
@@ -23,7 +23,7 @@
* corresponds to the end of event in the packet. Out of IEC 61883.
* @CIP_WRONG_DBS: Only for in-stream. The value of dbs is wrong in in-packets.
* The value of data_block_quadlets is used instead of reported value.
- * @SKIP_DBC_ZERO_CHECK: Only for in-stream. Packets with zero in dbc is
+ * @CIP_SKIP_DBC_ZERO_CHECK: Only for in-stream. Packets with zero in dbc is
* skipped for detecting discontinuity.
* @CIP_SKIP_INIT_DBC_CHECK: Only for in-stream. The value of dbc in first
* packet is not continuous from an initial value.
@@ -43,7 +43,27 @@ enum cip_flags {
};
/**
- * enum cip_sfc - a stream's sample rate
+ * enum cip_sfc - supported Sampling Frequency Codes (SFCs)
+ * @CIP_SFC_32000: 32,000 data blocks
+ * @CIP_SFC_44100: 44,100 data blocks
+ * @CIP_SFC_48000: 48,000 data blocks
+ * @CIP_SFC_88200: 88,200 data blocks
+ * @CIP_SFC_96000: 96,000 data blocks
+ * @CIP_SFC_176400: 176,400 data blocks
+ * @CIP_SFC_192000: 192,000 data blocks
+ * @CIP_SFC_COUNT: the number of supported SFCs
+ *
+ * These values are used to show nominal Sampling Frequency Code in
+ * Format Dependent Field (FDF) of AMDTP packet header. In IEC 61883-6:2002,
+ * this code means the number of events per second. Actually the code
+ * represents the number of data blocks transferred per second in an AMDTP
+ * stream.
+ *
+ * In IEC 61883-6:2005, some extensions were added to support more types of
+ * data such as 'One Bit LInear Audio', therefore the meaning of SFC became
+ * different depending on the types.
+ *
+ * Currently our implementation is compatible with IEC 61883-6:2002.
*/
enum cip_sfc {
CIP_SFC_32000 = 0,
diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h
index e13eef99c27a..dfbcd233178c 100644
--- a/sound/firewire/bebob/bebob.h
+++ b/sound/firewire/bebob/bebob.h
@@ -52,7 +52,7 @@ extern const unsigned int snd_bebob_rate_table[SND_BEBOB_STRM_FMT_ENTRIES];
#define SND_BEBOB_CLOCK_INTERNAL "Internal"
struct snd_bebob_clock_spec {
unsigned int num;
- char *const *labels;
+ const char *const *labels;
int (*get)(struct snd_bebob *bebob, unsigned int *id);
};
struct snd_bebob_rate_spec {
@@ -61,7 +61,7 @@ struct snd_bebob_rate_spec {
};
struct snd_bebob_meter_spec {
unsigned int num;
- char *const *labels;
+ const char *const *labels;
int (*get)(struct snd_bebob *bebob, u32 *target, unsigned int size);
};
struct snd_bebob_spec {
diff --git a/sound/firewire/bebob/bebob_focusrite.c b/sound/firewire/bebob/bebob_focusrite.c
index 3b052ed0fbf5..fc67c1b7cb5b 100644
--- a/sound/firewire/bebob/bebob_focusrite.c
+++ b/sound/firewire/bebob/bebob_focusrite.c
@@ -103,10 +103,10 @@ saffire_write_quad(struct snd_bebob *bebob, u64 offset, u32 value)
&data, sizeof(__be32), 0);
}
-static char *const saffirepro_10_clk_src_labels[] = {
+static const char *const saffirepro_10_clk_src_labels[] = {
SND_BEBOB_CLOCK_INTERNAL, "S/PDIF", "Word Clock"
};
-static char *const saffirepro_26_clk_src_labels[] = {
+static const char *const saffirepro_26_clk_src_labels[] = {
SND_BEBOB_CLOCK_INTERNAL, "S/PDIF", "ADAT1", "ADAT2", "Word Clock"
};
/* Value maps between registers and labels for SaffirePro 10/26. */
@@ -195,7 +195,7 @@ end:
}
struct snd_bebob_spec saffire_le_spec;
-static char *const saffire_both_clk_src_labels[] = {
+static const char *const saffire_both_clk_src_labels[] = {
SND_BEBOB_CLOCK_INTERNAL, "S/PDIF"
};
static int
@@ -210,12 +210,12 @@ saffire_both_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
return err;
};
-static char *const saffire_le_meter_labels[] = {
+static const char *const saffire_le_meter_labels[] = {
ANA_IN, ANA_IN, DIG_IN,
ANA_OUT, ANA_OUT, ANA_OUT, ANA_OUT,
STM_IN, STM_IN
};
-static char *const saffire_meter_labels[] = {
+static const char *const saffire_meter_labels[] = {
ANA_IN, ANA_IN,
STM_IN, STM_IN, STM_IN, STM_IN, STM_IN,
};
diff --git a/sound/firewire/bebob/bebob_maudio.c b/sound/firewire/bebob/bebob_maudio.c
index 70faa3a32526..a422aaa3bb0c 100644
--- a/sound/firewire/bebob/bebob_maudio.c
+++ b/sound/firewire/bebob/bebob_maudio.c
@@ -340,7 +340,7 @@ end:
}
/* Clock source control for special firmware */
-static char *const special_clk_labels[] = {
+static const char *const special_clk_labels[] = {
SND_BEBOB_CLOCK_INTERNAL " with Digital Mute", "Digital",
"Word Clock", SND_BEBOB_CLOCK_INTERNAL};
static int special_clk_get(struct snd_bebob *bebob, unsigned int *id)
@@ -352,17 +352,8 @@ static int special_clk_get(struct snd_bebob *bebob, unsigned int *id)
static int special_clk_ctl_info(struct snd_kcontrol *kctl,
struct snd_ctl_elem_info *einf)
{
- einf->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- einf->count = 1;
- einf->value.enumerated.items = ARRAY_SIZE(special_clk_labels);
-
- if (einf->value.enumerated.item >= einf->value.enumerated.items)
- einf->value.enumerated.item = einf->value.enumerated.items - 1;
-
- strcpy(einf->value.enumerated.name,
- special_clk_labels[einf->value.enumerated.item]);
-
- return 0;
+ return snd_ctl_enum_info(einf, 1, ARRAY_SIZE(special_clk_labels),
+ special_clk_labels);
}
static int special_clk_ctl_get(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *uval)
@@ -438,23 +429,15 @@ static struct snd_kcontrol_new special_sync_ctl = {
};
/* Digital input interface control for special firmware */
-static char *const special_dig_in_iface_labels[] = {
+static const char *const special_dig_in_iface_labels[] = {
"S/PDIF Optical", "S/PDIF Coaxial", "ADAT Optical"
};
static int special_dig_in_iface_ctl_info(struct snd_kcontrol *kctl,
struct snd_ctl_elem_info *einf)
{
- einf->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- einf->count = 1;
- einf->value.enumerated.items = ARRAY_SIZE(special_dig_in_iface_labels);
-
- if (einf->value.enumerated.item >= einf->value.enumerated.items)
- einf->value.enumerated.item = einf->value.enumerated.items - 1;
-
- strcpy(einf->value.enumerated.name,
- special_dig_in_iface_labels[einf->value.enumerated.item]);
-
- return 0;
+ return snd_ctl_enum_info(einf, 1,
+ ARRAY_SIZE(special_dig_in_iface_labels),
+ special_dig_in_iface_labels);
}
static int special_dig_in_iface_ctl_get(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *uval)
@@ -539,23 +522,15 @@ static struct snd_kcontrol_new special_dig_in_iface_ctl = {
};
/* Digital output interface control for special firmware */
-static char *const special_dig_out_iface_labels[] = {
+static const char *const special_dig_out_iface_labels[] = {
"S/PDIF Optical and Coaxial", "ADAT Optical"
};
static int special_dig_out_iface_ctl_info(struct snd_kcontrol *kctl,
struct snd_ctl_elem_info *einf)
{
- einf->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- einf->count = 1;
- einf->value.enumerated.items = ARRAY_SIZE(special_dig_out_iface_labels);
-
- if (einf->value.enumerated.item >= einf->value.enumerated.items)
- einf->value.enumerated.item = einf->value.enumerated.items - 1;
-
- strcpy(einf->value.enumerated.name,
- special_dig_out_iface_labels[einf->value.enumerated.item]);
-
- return 0;
+ return snd_ctl_enum_info(einf, 1,
+ ARRAY_SIZE(special_dig_out_iface_labels),
+ special_dig_out_iface_labels);
}
static int special_dig_out_iface_ctl_get(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *uval)
@@ -631,7 +606,7 @@ end:
}
/* Hardware metering for special firmware */
-static char *const special_meter_labels[] = {
+static const char *const special_meter_labels[] = {
ANA_IN, ANA_IN, ANA_IN, ANA_IN,
SPDIF_IN,
ADAT_IN, ADAT_IN, ADAT_IN, ADAT_IN,
@@ -671,30 +646,30 @@ end:
}
/* last 4 bytes are omitted because it's clock info. */
-static char *const fw410_meter_labels[] = {
+static const char *const fw410_meter_labels[] = {
ANA_IN, DIG_IN,
ANA_OUT, ANA_OUT, ANA_OUT, ANA_OUT, DIG_OUT,
HP_OUT
};
-static char *const audiophile_meter_labels[] = {
+static const char *const audiophile_meter_labels[] = {
ANA_IN, DIG_IN,
ANA_OUT, ANA_OUT, DIG_OUT,
HP_OUT, AUX_OUT,
};
-static char *const solo_meter_labels[] = {
+static const char *const solo_meter_labels[] = {
ANA_IN, DIG_IN,
STRM_IN, STRM_IN,
ANA_OUT, DIG_OUT
};
/* no clock info */
-static char *const ozonic_meter_labels[] = {
+static const char *const ozonic_meter_labels[] = {
ANA_IN, ANA_IN,
STRM_IN, STRM_IN,
ANA_OUT, ANA_OUT
};
/* TODO: need testers. these positions are based on authour's assumption */
-static char *const nrv10_meter_labels[] = {
+static const char *const nrv10_meter_labels[] = {
ANA_IN, ANA_IN, ANA_IN, ANA_IN,
DIG_IN,
ANA_OUT, ANA_OUT, ANA_OUT, ANA_OUT,
diff --git a/sound/firewire/bebob/bebob_terratec.c b/sound/firewire/bebob/bebob_terratec.c
index 9940611f2e1b..ad635004d699 100644
--- a/sound/firewire/bebob/bebob_terratec.c
+++ b/sound/firewire/bebob/bebob_terratec.c
@@ -8,7 +8,7 @@
#include "./bebob.h"
-static char *const phase88_rack_clk_src_labels[] = {
+static const char *const phase88_rack_clk_src_labels[] = {
SND_BEBOB_CLOCK_INTERNAL, "Digital In", "Word Clock"
};
static int
@@ -34,7 +34,7 @@ end:
return err;
}
-static char *const phase24_series_clk_src_labels[] = {
+static const char *const phase24_series_clk_src_labels[] = {
SND_BEBOB_CLOCK_INTERNAL, "Digital In"
};
static int
diff --git a/sound/firewire/bebob/bebob_yamaha.c b/sound/firewire/bebob/bebob_yamaha.c
index 9b7e798180ff..ef1fe3823a9c 100644
--- a/sound/firewire/bebob/bebob_yamaha.c
+++ b/sound/firewire/bebob/bebob_yamaha.c
@@ -28,7 +28,7 @@
* reccomend users to close ffado-mixer at 192.0kHz if mixer is needless.
*/
-static char *const clk_src_labels[] = {SND_BEBOB_CLOCK_INTERNAL, "SPDIF"};
+static const char *const clk_src_labels[] = {SND_BEBOB_CLOCK_INTERNAL, "SPDIF"};
static int
clk_src_get(struct snd_bebob *bebob, unsigned int *id)
{
diff --git a/sound/firewire/cmp.c b/sound/firewire/cmp.c
index ba8df5a1be39..ae3bc1940efa 100644
--- a/sound/firewire/cmp.c
+++ b/sound/firewire/cmp.c
@@ -114,6 +114,7 @@ static int pcr_modify(struct cmp_connection *c,
* cmp_connection_init - initializes a connection manager
* @c: the connection manager to initialize
* @unit: a unit of the target device
+ * @direction: input or output
* @pcr_index: the index of the iPCR/oPCR on the target device
*/
int cmp_connection_init(struct cmp_connection *c,
@@ -154,6 +155,7 @@ EXPORT_SYMBOL(cmp_connection_init);
/**
* cmp_connection_check_used - check connection is already esablished or not
* @c: the connection manager to be checked
+ * @used: the pointer to store the result of checking the connection
*/
int cmp_connection_check_used(struct cmp_connection *c, bool *used)
{
diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c
deleted file mode 100644
index e3a04d69c853..000000000000
--- a/sound/firewire/dice.c
+++ /dev/null
@@ -1,1511 +0,0 @@
-/*
- * TC Applied Technologies Digital Interface Communications Engine driver
- *
- * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
- * Licensed under the terms of the GNU General Public License, version 2.
- */
-
-#include <linux/compat.h>
-#include <linux/completion.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/firewire.h>
-#include <linux/firewire-constants.h>
-#include <linux/jiffies.h>
-#include <linux/module.h>
-#include <linux/mod_devicetable.h>
-#include <linux/mutex.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include <linux/wait.h>
-#include <sound/control.h>
-#include <sound/core.h>
-#include <sound/firewire.h>
-#include <sound/hwdep.h>
-#include <sound/info.h>
-#include <sound/initval.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include "amdtp.h"
-#include "iso-resources.h"
-#include "lib.h"
-#include "dice-interface.h"
-
-
-struct dice {
- struct snd_card *card;
- struct fw_unit *unit;
- spinlock_t lock;
- struct mutex mutex;
- unsigned int global_offset;
- unsigned int rx_offset;
- unsigned int clock_caps;
- unsigned int rx_channels[3];
- unsigned int rx_midi_ports[3];
- struct fw_address_handler notification_handler;
- int owner_generation;
- int dev_lock_count; /* > 0 driver, < 0 userspace */
- bool dev_lock_changed;
- bool global_enabled;
- struct completion clock_accepted;
- wait_queue_head_t hwdep_wait;
- u32 notification_bits;
- struct fw_iso_resources resources;
- struct amdtp_stream stream;
-};
-
-MODULE_DESCRIPTION("DICE driver");
-MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
-MODULE_LICENSE("GPL v2");
-
-static const unsigned int dice_rates[] = {
- /* mode 0 */
- [0] = 32000,
- [1] = 44100,
- [2] = 48000,
- /* mode 1 */
- [3] = 88200,
- [4] = 96000,
- /* mode 2 */
- [5] = 176400,
- [6] = 192000,
-};
-
-static unsigned int rate_to_index(unsigned int rate)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(dice_rates); ++i)
- if (dice_rates[i] == rate)
- return i;
-
- return 0;
-}
-
-static unsigned int rate_index_to_mode(unsigned int rate_index)
-{
- return ((int)rate_index - 1) / 2;
-}
-
-static void dice_lock_changed(struct dice *dice)
-{
- dice->dev_lock_changed = true;
- wake_up(&dice->hwdep_wait);
-}
-
-static int dice_try_lock(struct dice *dice)
-{
- int err;
-
- spin_lock_irq(&dice->lock);
-
- if (dice->dev_lock_count < 0) {
- err = -EBUSY;
- goto out;
- }
-
- if (dice->dev_lock_count++ == 0)
- dice_lock_changed(dice);
- err = 0;
-
-out:
- spin_unlock_irq(&dice->lock);
-
- return err;
-}
-
-static void dice_unlock(struct dice *dice)
-{
- spin_lock_irq(&dice->lock);
-
- if (WARN_ON(dice->dev_lock_count <= 0))
- goto out;
-
- if (--dice->dev_lock_count == 0)
- dice_lock_changed(dice);
-
-out:
- spin_unlock_irq(&dice->lock);
-}
-
-static inline u64 global_address(struct dice *dice, unsigned int offset)
-{
- return DICE_PRIVATE_SPACE + dice->global_offset + offset;
-}
-
-// TODO: rx index
-static inline u64 rx_address(struct dice *dice, unsigned int offset)
-{
- return DICE_PRIVATE_SPACE + dice->rx_offset + offset;
-}
-
-static int dice_owner_set(struct dice *dice)
-{
- struct fw_device *device = fw_parent_device(dice->unit);
- __be64 *buffer;
- int err, errors = 0;
-
- buffer = kmalloc(2 * 8, GFP_KERNEL);
- if (!buffer)
- return -ENOMEM;
-
- for (;;) {
- buffer[0] = cpu_to_be64(OWNER_NO_OWNER);
- buffer[1] = cpu_to_be64(
- ((u64)device->card->node_id << OWNER_NODE_SHIFT) |
- dice->notification_handler.offset);
-
- dice->owner_generation = device->generation;
- smp_rmb(); /* node_id vs. generation */
- err = snd_fw_transaction(dice->unit,
- TCODE_LOCK_COMPARE_SWAP,
- global_address(dice, GLOBAL_OWNER),
- buffer, 2 * 8,
- FW_FIXED_GENERATION |
- dice->owner_generation);
-
- if (err == 0) {
- if (buffer[0] != cpu_to_be64(OWNER_NO_OWNER)) {
- dev_err(&dice->unit->device,
- "device is already in use\n");
- err = -EBUSY;
- }
- break;
- }
- if (err != -EAGAIN || ++errors >= 3)
- break;
-
- msleep(20);
- }
-
- kfree(buffer);
-
- return err;
-}
-
-static int dice_owner_update(struct dice *dice)
-{
- struct fw_device *device = fw_parent_device(dice->unit);
- __be64 *buffer;
- int err;
-
- if (dice->owner_generation == -1)
- return 0;
-
- buffer = kmalloc(2 * 8, GFP_KERNEL);
- if (!buffer)
- return -ENOMEM;
-
- buffer[0] = cpu_to_be64(OWNER_NO_OWNER);
- buffer[1] = cpu_to_be64(
- ((u64)device->card->node_id << OWNER_NODE_SHIFT) |
- dice->notification_handler.offset);
-
- dice->owner_generation = device->generation;
- smp_rmb(); /* node_id vs. generation */
- err = snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP,
- global_address(dice, GLOBAL_OWNER),
- buffer, 2 * 8,
- FW_FIXED_GENERATION | dice->owner_generation);
-
- if (err == 0) {
- if (buffer[0] != cpu_to_be64(OWNER_NO_OWNER)) {
- dev_err(&dice->unit->device,
- "device is already in use\n");
- err = -EBUSY;
- }
- } else if (err == -EAGAIN) {
- err = 0; /* try again later */
- }
-
- kfree(buffer);
-
- if (err < 0)
- dice->owner_generation = -1;
-
- return err;
-}
-
-static void dice_owner_clear(struct dice *dice)
-{
- struct fw_device *device = fw_parent_device(dice->unit);
- __be64 *buffer;
-
- buffer = kmalloc(2 * 8, GFP_KERNEL);
- if (!buffer)
- return;
-
- buffer[0] = cpu_to_be64(
- ((u64)device->card->node_id << OWNER_NODE_SHIFT) |
- dice->notification_handler.offset);
- buffer[1] = cpu_to_be64(OWNER_NO_OWNER);
- snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP,
- global_address(dice, GLOBAL_OWNER),
- buffer, 2 * 8, FW_QUIET |
- FW_FIXED_GENERATION | dice->owner_generation);
-
- kfree(buffer);
-
- dice->owner_generation = -1;
-}
-
-static int dice_enable_set(struct dice *dice)
-{
- __be32 value;
- int err;
-
- value = cpu_to_be32(1);
- err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
- global_address(dice, GLOBAL_ENABLE),
- &value, 4,
- FW_FIXED_GENERATION | dice->owner_generation);
- if (err < 0)
- return err;
-
- dice->global_enabled = true;
-
- return 0;
-}
-
-static void dice_enable_clear(struct dice *dice)
-{
- __be32 value;
-
- if (!dice->global_enabled)
- return;
-
- value = 0;
- snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
- global_address(dice, GLOBAL_ENABLE),
- &value, 4, FW_QUIET |
- FW_FIXED_GENERATION | dice->owner_generation);
-
- dice->global_enabled = false;
-}
-
-static void dice_notification(struct fw_card *card, struct fw_request *request,
- int tcode, int destination, int source,
- int generation, unsigned long long offset,
- void *data, size_t length, void *callback_data)
-{
- struct dice *dice = callback_data;
- u32 bits;
- unsigned long flags;
-
- if (tcode != TCODE_WRITE_QUADLET_REQUEST) {
- fw_send_response(card, request, RCODE_TYPE_ERROR);
- return;
- }
- if ((offset & 3) != 0) {
- fw_send_response(card, request, RCODE_ADDRESS_ERROR);
- return;
- }
-
- bits = be32_to_cpup(data);
-
- spin_lock_irqsave(&dice->lock, flags);
- dice->notification_bits |= bits;
- spin_unlock_irqrestore(&dice->lock, flags);
-
- fw_send_response(card, request, RCODE_COMPLETE);
-
- if (bits & NOTIFY_CLOCK_ACCEPTED)
- complete(&dice->clock_accepted);
- wake_up(&dice->hwdep_wait);
-}
-
-static int dice_rate_constraint(struct snd_pcm_hw_params *params,
- struct snd_pcm_hw_rule *rule)
-{
- struct dice *dice = rule->private;
- const struct snd_interval *channels =
- hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
- struct snd_interval *rate =
- hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
- struct snd_interval allowed_rates = {
- .min = UINT_MAX, .max = 0, .integer = 1
- };
- unsigned int i, mode;
-
- for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) {
- mode = rate_index_to_mode(i);
- if ((dice->clock_caps & (1 << i)) &&
- snd_interval_test(channels, dice->rx_channels[mode])) {
- allowed_rates.min = min(allowed_rates.min,
- dice_rates[i]);
- allowed_rates.max = max(allowed_rates.max,
- dice_rates[i]);
- }
- }
-
- return snd_interval_refine(rate, &allowed_rates);
-}
-
-static int dice_channels_constraint(struct snd_pcm_hw_params *params,
- struct snd_pcm_hw_rule *rule)
-{
- struct dice *dice = rule->private;
- const struct snd_interval *rate =
- hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
- struct snd_interval *channels =
- hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
- struct snd_interval allowed_channels = {
- .min = UINT_MAX, .max = 0, .integer = 1
- };
- unsigned int i, mode;
-
- for (i = 0; i < ARRAY_SIZE(dice_rates); ++i)
- if ((dice->clock_caps & (1 << i)) &&
- snd_interval_test(rate, dice_rates[i])) {
- mode = rate_index_to_mode(i);
- allowed_channels.min = min(allowed_channels.min,
- dice->rx_channels[mode]);
- allowed_channels.max = max(allowed_channels.max,
- dice->rx_channels[mode]);
- }
-
- return snd_interval_refine(channels, &allowed_channels);
-}
-
-static int dice_open(struct snd_pcm_substream *substream)
-{
- static const struct snd_pcm_hardware hardware = {
- .info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_BATCH |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_BLOCK_TRANSFER,
- .formats = AMDTP_OUT_PCM_FORMAT_BITS,
- .channels_min = UINT_MAX,
- .channels_max = 0,
- .buffer_bytes_max = 16 * 1024 * 1024,
- .period_bytes_min = 1,
- .period_bytes_max = UINT_MAX,
- .periods_min = 1,
- .periods_max = UINT_MAX,
- };
- struct dice *dice = substream->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned int i;
- int err;
-
- err = dice_try_lock(dice);
- if (err < 0)
- goto error;
-
- runtime->hw = hardware;
-
- for (i = 0; i < ARRAY_SIZE(dice_rates); ++i)
- if (dice->clock_caps & (1 << i))
- runtime->hw.rates |=
- snd_pcm_rate_to_rate_bit(dice_rates[i]);
- snd_pcm_limit_hw_rates(runtime);
-
- for (i = 0; i < 3; ++i)
- if (dice->rx_channels[i]) {
- runtime->hw.channels_min = min(runtime->hw.channels_min,
- dice->rx_channels[i]);
- runtime->hw.channels_max = max(runtime->hw.channels_max,
- dice->rx_channels[i]);
- }
-
- err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
- dice_rate_constraint, dice,
- SNDRV_PCM_HW_PARAM_CHANNELS, -1);
- if (err < 0)
- goto err_lock;
- err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
- dice_channels_constraint, dice,
- SNDRV_PCM_HW_PARAM_RATE, -1);
- if (err < 0)
- goto err_lock;
-
- err = amdtp_stream_add_pcm_hw_constraints(&dice->stream, runtime);
- if (err < 0)
- goto err_lock;
-
- return 0;
-
-err_lock:
- dice_unlock(dice);
-error:
- return err;
-}
-
-static int dice_close(struct snd_pcm_substream *substream)
-{
- struct dice *dice = substream->private_data;
-
- dice_unlock(dice);
-
- return 0;
-}
-
-static int dice_stream_start_packets(struct dice *dice)
-{
- int err;
-
- if (amdtp_stream_running(&dice->stream))
- return 0;
-
- err = amdtp_stream_start(&dice->stream, dice->resources.channel,
- fw_parent_device(dice->unit)->max_speed);
- if (err < 0)
- return err;
-
- err = dice_enable_set(dice);
- if (err < 0) {
- amdtp_stream_stop(&dice->stream);
- return err;
- }
-
- return 0;
-}
-
-static int dice_stream_start(struct dice *dice)
-{
- __be32 channel;
- int err;
-
- if (!dice->resources.allocated) {
- err = fw_iso_resources_allocate(&dice->resources,
- amdtp_stream_get_max_payload(&dice->stream),
- fw_parent_device(dice->unit)->max_speed);
- if (err < 0)
- goto error;
-
- channel = cpu_to_be32(dice->resources.channel);
- err = snd_fw_transaction(dice->unit,
- TCODE_WRITE_QUADLET_REQUEST,
- rx_address(dice, RX_ISOCHRONOUS),
- &channel, 4, 0);
- if (err < 0)
- goto err_resources;
- }
-
- err = dice_stream_start_packets(dice);
- if (err < 0)
- goto err_rx_channel;
-
- return 0;
-
-err_rx_channel:
- channel = cpu_to_be32((u32)-1);
- snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
- rx_address(dice, RX_ISOCHRONOUS), &channel, 4, 0);
-err_resources:
- fw_iso_resources_free(&dice->resources);
-error:
- return err;
-}
-
-static void dice_stream_stop_packets(struct dice *dice)
-{
- if (amdtp_stream_running(&dice->stream)) {
- dice_enable_clear(dice);
- amdtp_stream_stop(&dice->stream);
- }
-}
-
-static void dice_stream_stop(struct dice *dice)
-{
- __be32 channel;
-
- dice_stream_stop_packets(dice);
-
- if (!dice->resources.allocated)
- return;
-
- channel = cpu_to_be32((u32)-1);
- snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
- rx_address(dice, RX_ISOCHRONOUS), &channel, 4, 0);
-
- fw_iso_resources_free(&dice->resources);
-}
-
-static int dice_change_rate(struct dice *dice, unsigned int clock_rate)
-{
- __be32 value;
- int err;
-
- reinit_completion(&dice->clock_accepted);
-
- value = cpu_to_be32(clock_rate | CLOCK_SOURCE_ARX1);
- err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
- global_address(dice, GLOBAL_CLOCK_SELECT),
- &value, 4, 0);
- if (err < 0)
- return err;
-
- if (!wait_for_completion_timeout(&dice->clock_accepted,
- msecs_to_jiffies(100)))
- dev_warn(&dice->unit->device, "clock change timed out\n");
-
- return 0;
-}
-
-static int dice_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- struct dice *dice = substream->private_data;
- unsigned int rate_index, mode, rate, channels, i;
- int err;
-
- mutex_lock(&dice->mutex);
- dice_stream_stop(dice);
- mutex_unlock(&dice->mutex);
-
- err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
-
- rate = params_rate(hw_params);
- rate_index = rate_to_index(rate);
- err = dice_change_rate(dice, rate_index << CLOCK_RATE_SHIFT);
- if (err < 0)
- return err;
-
- /*
- * At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in
- * one data block of AMDTP packet. Thus sampling transfer frequency is
- * a half of PCM sampling frequency, i.e. PCM frames at 192.0 kHz are
- * transferred on AMDTP packets at 96 kHz. Two successive samples of a
- * channel are stored consecutively in the packet. This quirk is called
- * as 'Dual Wire'.
- * For this quirk, blocking mode is required and PCM buffer size should
- * be aligned to SYT_INTERVAL.
- */
- channels = params_channels(hw_params);
- if (rate_index > 4) {
- if (channels > AMDTP_MAX_CHANNELS_FOR_PCM / 2) {
- err = -ENOSYS;
- return err;
- }
-
- rate /= 2;
- channels *= 2;
- dice->stream.double_pcm_frames = true;
- } else {
- dice->stream.double_pcm_frames = false;
- }
-
- mode = rate_index_to_mode(rate_index);
- amdtp_stream_set_parameters(&dice->stream, rate, channels,
- dice->rx_midi_ports[mode]);
- if (rate_index > 4) {
- channels /= 2;
-
- for (i = 0; i < channels; i++) {
- dice->stream.pcm_positions[i] = i * 2;
- dice->stream.pcm_positions[i + channels] = i * 2 + 1;
- }
- }
-
- amdtp_stream_set_pcm_format(&dice->stream,
- params_format(hw_params));
-
- return 0;
-}
-
-static int dice_hw_free(struct snd_pcm_substream *substream)
-{
- struct dice *dice = substream->private_data;
-
- mutex_lock(&dice->mutex);
- dice_stream_stop(dice);
- mutex_unlock(&dice->mutex);
-
- return snd_pcm_lib_free_vmalloc_buffer(substream);
-}
-
-static int dice_prepare(struct snd_pcm_substream *substream)
-{
- struct dice *dice = substream->private_data;
- int err;
-
- mutex_lock(&dice->mutex);
-
- if (amdtp_streaming_error(&dice->stream))
- dice_stream_stop_packets(dice);
-
- err = dice_stream_start(dice);
- if (err < 0) {
- mutex_unlock(&dice->mutex);
- return err;
- }
-
- mutex_unlock(&dice->mutex);
-
- amdtp_stream_pcm_prepare(&dice->stream);
-
- return 0;
-}
-
-static int dice_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct dice *dice = substream->private_data;
- struct snd_pcm_substream *pcm;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- pcm = substream;
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- pcm = NULL;
- break;
- default:
- return -EINVAL;
- }
- amdtp_stream_pcm_trigger(&dice->stream, pcm);
-
- return 0;
-}
-
-static snd_pcm_uframes_t dice_pointer(struct snd_pcm_substream *substream)
-{
- struct dice *dice = substream->private_data;
-
- return amdtp_stream_pcm_pointer(&dice->stream);
-}
-
-static int dice_create_pcm(struct dice *dice)
-{
- static struct snd_pcm_ops ops = {
- .open = dice_open,
- .close = dice_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = dice_hw_params,
- .hw_free = dice_hw_free,
- .prepare = dice_prepare,
- .trigger = dice_trigger,
- .pointer = dice_pointer,
- .page = snd_pcm_lib_get_vmalloc_page,
- .mmap = snd_pcm_lib_mmap_vmalloc,
- };
- struct snd_pcm *pcm;
- int err;
-
- err = snd_pcm_new(dice->card, "DICE", 0, 1, 0, &pcm);
- if (err < 0)
- return err;
- pcm->private_data = dice;
- strcpy(pcm->name, dice->card->shortname);
- pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->ops = &ops;
-
- return 0;
-}
-
-static long dice_hwdep_read(struct snd_hwdep *hwdep, char __user *buf,
- long count, loff_t *offset)
-{
- struct dice *dice = hwdep->private_data;
- DEFINE_WAIT(wait);
- union snd_firewire_event event;
-
- spin_lock_irq(&dice->lock);
-
- while (!dice->dev_lock_changed && dice->notification_bits == 0) {
- prepare_to_wait(&dice->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
- spin_unlock_irq(&dice->lock);
- schedule();
- finish_wait(&dice->hwdep_wait, &wait);
- if (signal_pending(current))
- return -ERESTARTSYS;
- spin_lock_irq(&dice->lock);
- }
-
- memset(&event, 0, sizeof(event));
- if (dice->dev_lock_changed) {
- event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
- event.lock_status.status = dice->dev_lock_count > 0;
- dice->dev_lock_changed = false;
-
- count = min(count, (long)sizeof(event.lock_status));
- } else {
- event.dice_notification.type = SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION;
- event.dice_notification.notification = dice->notification_bits;
- dice->notification_bits = 0;
-
- count = min(count, (long)sizeof(event.dice_notification));
- }
-
- spin_unlock_irq(&dice->lock);
-
- if (copy_to_user(buf, &event, count))
- return -EFAULT;
-
- return count;
-}
-
-static unsigned int dice_hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
- poll_table *wait)
-{
- struct dice *dice = hwdep->private_data;
- unsigned int events;
-
- poll_wait(file, &dice->hwdep_wait, wait);
-
- spin_lock_irq(&dice->lock);
- if (dice->dev_lock_changed || dice->notification_bits != 0)
- events = POLLIN | POLLRDNORM;
- else
- events = 0;
- spin_unlock_irq(&dice->lock);
-
- return events;
-}
-
-static int dice_hwdep_get_info(struct dice *dice, void __user *arg)
-{
- struct fw_device *dev = fw_parent_device(dice->unit);
- struct snd_firewire_get_info info;
-
- memset(&info, 0, sizeof(info));
- info.type = SNDRV_FIREWIRE_TYPE_DICE;
- info.card = dev->card->index;
- *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
- *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
- strlcpy(info.device_name, dev_name(&dev->device),
- sizeof(info.device_name));
-
- if (copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
-
- return 0;
-}
-
-static int dice_hwdep_lock(struct dice *dice)
-{
- int err;
-
- spin_lock_irq(&dice->lock);
-
- if (dice->dev_lock_count == 0) {
- dice->dev_lock_count = -1;
- err = 0;
- } else {
- err = -EBUSY;
- }
-
- spin_unlock_irq(&dice->lock);
-
- return err;
-}
-
-static int dice_hwdep_unlock(struct dice *dice)
-{
- int err;
-
- spin_lock_irq(&dice->lock);
-
- if (dice->dev_lock_count == -1) {
- dice->dev_lock_count = 0;
- err = 0;
- } else {
- err = -EBADFD;
- }
-
- spin_unlock_irq(&dice->lock);
-
- return err;
-}
-
-static int dice_hwdep_release(struct snd_hwdep *hwdep, struct file *file)
-{
- struct dice *dice = hwdep->private_data;
-
- spin_lock_irq(&dice->lock);
- if (dice->dev_lock_count == -1)
- dice->dev_lock_count = 0;
- spin_unlock_irq(&dice->lock);
-
- return 0;
-}
-
-static int dice_hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- struct dice *dice = hwdep->private_data;
-
- switch (cmd) {
- case SNDRV_FIREWIRE_IOCTL_GET_INFO:
- return dice_hwdep_get_info(dice, (void __user *)arg);
- case SNDRV_FIREWIRE_IOCTL_LOCK:
- return dice_hwdep_lock(dice);
- case SNDRV_FIREWIRE_IOCTL_UNLOCK:
- return dice_hwdep_unlock(dice);
- default:
- return -ENOIOCTLCMD;
- }
-}
-
-#ifdef CONFIG_COMPAT
-static int dice_hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- return dice_hwdep_ioctl(hwdep, file, cmd,
- (unsigned long)compat_ptr(arg));
-}
-#else
-#define dice_hwdep_compat_ioctl NULL
-#endif
-
-static int dice_create_hwdep(struct dice *dice)
-{
- static const struct snd_hwdep_ops ops = {
- .read = dice_hwdep_read,
- .release = dice_hwdep_release,
- .poll = dice_hwdep_poll,
- .ioctl = dice_hwdep_ioctl,
- .ioctl_compat = dice_hwdep_compat_ioctl,
- };
- struct snd_hwdep *hwdep;
- int err;
-
- err = snd_hwdep_new(dice->card, "DICE", 0, &hwdep);
- if (err < 0)
- return err;
- strcpy(hwdep->name, "DICE");
- hwdep->iface = SNDRV_HWDEP_IFACE_FW_DICE;
- hwdep->ops = ops;
- hwdep->private_data = dice;
- hwdep->exclusive = true;
-
- return 0;
-}
-
-static int dice_proc_read_mem(struct dice *dice, void *buffer,
- unsigned int offset_q, unsigned int quadlets)
-{
- unsigned int i;
- int err;
-
- err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
- DICE_PRIVATE_SPACE + 4 * offset_q,
- buffer, 4 * quadlets, 0);
- if (err < 0)
- return err;
-
- for (i = 0; i < quadlets; ++i)
- be32_to_cpus(&((u32 *)buffer)[i]);
-
- return 0;
-}
-
-static const char *str_from_array(const char *const strs[], unsigned int count,
- unsigned int i)
-{
- if (i < count)
- return strs[i];
- else
- return "(unknown)";
-}
-
-static void dice_proc_fixup_string(char *s, unsigned int size)
-{
- unsigned int i;
-
- for (i = 0; i < size; i += 4)
- cpu_to_le32s((u32 *)(s + i));
-
- for (i = 0; i < size - 2; ++i) {
- if (s[i] == '\0')
- return;
- if (s[i] == '\\' && s[i + 1] == '\\') {
- s[i + 2] = '\0';
- return;
- }
- }
- s[size - 1] = '\0';
-}
-
-static void dice_proc_read(struct snd_info_entry *entry,
- struct snd_info_buffer *buffer)
-{
- static const char *const section_names[5] = {
- "global", "tx", "rx", "ext_sync", "unused2"
- };
- static const char *const clock_sources[] = {
- "aes1", "aes2", "aes3", "aes4", "aes", "adat", "tdif",
- "wc", "arx1", "arx2", "arx3", "arx4", "internal"
- };
- static const char *const rates[] = {
- "32000", "44100", "48000", "88200", "96000", "176400", "192000",
- "any low", "any mid", "any high", "none"
- };
- struct dice *dice = entry->private_data;
- u32 sections[ARRAY_SIZE(section_names) * 2];
- struct {
- u32 number;
- u32 size;
- } tx_rx_header;
- union {
- struct {
- u32 owner_hi, owner_lo;
- u32 notification;
- char nick_name[NICK_NAME_SIZE];
- u32 clock_select;
- u32 enable;
- u32 status;
- u32 extended_status;
- u32 sample_rate;
- u32 version;
- u32 clock_caps;
- char clock_source_names[CLOCK_SOURCE_NAMES_SIZE];
- } global;
- struct {
- u32 iso;
- u32 number_audio;
- u32 number_midi;
- u32 speed;
- char names[TX_NAMES_SIZE];
- u32 ac3_caps;
- u32 ac3_enable;
- } tx;
- struct {
- u32 iso;
- u32 seq_start;
- u32 number_audio;
- u32 number_midi;
- char names[RX_NAMES_SIZE];
- u32 ac3_caps;
- u32 ac3_enable;
- } rx;
- struct {
- u32 clock_source;
- u32 locked;
- u32 rate;
- u32 adat_user_data;
- } ext_sync;
- } buf;
- unsigned int quadlets, stream, i;
-
- if (dice_proc_read_mem(dice, sections, 0, ARRAY_SIZE(sections)) < 0)
- return;
- snd_iprintf(buffer, "sections:\n");
- for (i = 0; i < ARRAY_SIZE(section_names); ++i)
- snd_iprintf(buffer, " %s: offset %u, size %u\n",
- section_names[i],
- sections[i * 2], sections[i * 2 + 1]);
-
- quadlets = min_t(u32, sections[1], sizeof(buf.global) / 4);
- if (dice_proc_read_mem(dice, &buf.global, sections[0], quadlets) < 0)
- return;
- snd_iprintf(buffer, "global:\n");
- snd_iprintf(buffer, " owner: %04x:%04x%08x\n",
- buf.global.owner_hi >> 16,
- buf.global.owner_hi & 0xffff, buf.global.owner_lo);
- snd_iprintf(buffer, " notification: %08x\n", buf.global.notification);
- dice_proc_fixup_string(buf.global.nick_name, NICK_NAME_SIZE);
- snd_iprintf(buffer, " nick name: %s\n", buf.global.nick_name);
- snd_iprintf(buffer, " clock select: %s %s\n",
- str_from_array(clock_sources, ARRAY_SIZE(clock_sources),
- buf.global.clock_select & CLOCK_SOURCE_MASK),
- str_from_array(rates, ARRAY_SIZE(rates),
- (buf.global.clock_select & CLOCK_RATE_MASK)
- >> CLOCK_RATE_SHIFT));
- snd_iprintf(buffer, " enable: %u\n", buf.global.enable);
- snd_iprintf(buffer, " status: %slocked %s\n",
- buf.global.status & STATUS_SOURCE_LOCKED ? "" : "un",
- str_from_array(rates, ARRAY_SIZE(rates),
- (buf.global.status &
- STATUS_NOMINAL_RATE_MASK)
- >> CLOCK_RATE_SHIFT));
- snd_iprintf(buffer, " ext status: %08x\n", buf.global.extended_status);
- snd_iprintf(buffer, " sample rate: %u\n", buf.global.sample_rate);
- snd_iprintf(buffer, " version: %u.%u.%u.%u\n",
- (buf.global.version >> 24) & 0xff,
- (buf.global.version >> 16) & 0xff,
- (buf.global.version >> 8) & 0xff,
- (buf.global.version >> 0) & 0xff);
- if (quadlets >= 90) {
- snd_iprintf(buffer, " clock caps:");
- for (i = 0; i <= 6; ++i)
- if (buf.global.clock_caps & (1 << i))
- snd_iprintf(buffer, " %s", rates[i]);
- for (i = 0; i <= 12; ++i)
- if (buf.global.clock_caps & (1 << (16 + i)))
- snd_iprintf(buffer, " %s", clock_sources[i]);
- snd_iprintf(buffer, "\n");
- dice_proc_fixup_string(buf.global.clock_source_names,
- CLOCK_SOURCE_NAMES_SIZE);
- snd_iprintf(buffer, " clock source names: %s\n",
- buf.global.clock_source_names);
- }
-
- if (dice_proc_read_mem(dice, &tx_rx_header, sections[2], 2) < 0)
- return;
- quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.tx) / 4);
- for (stream = 0; stream < tx_rx_header.number; ++stream) {
- if (dice_proc_read_mem(dice, &buf.tx, sections[2] + 2 +
- stream * tx_rx_header.size,
- quadlets) < 0)
- break;
- snd_iprintf(buffer, "tx %u:\n", stream);
- snd_iprintf(buffer, " iso channel: %d\n", (int)buf.tx.iso);
- snd_iprintf(buffer, " audio channels: %u\n",
- buf.tx.number_audio);
- snd_iprintf(buffer, " midi ports: %u\n", buf.tx.number_midi);
- snd_iprintf(buffer, " speed: S%u\n", 100u << buf.tx.speed);
- if (quadlets >= 68) {
- dice_proc_fixup_string(buf.tx.names, TX_NAMES_SIZE);
- snd_iprintf(buffer, " names: %s\n", buf.tx.names);
- }
- if (quadlets >= 70) {
- snd_iprintf(buffer, " ac3 caps: %08x\n",
- buf.tx.ac3_caps);
- snd_iprintf(buffer, " ac3 enable: %08x\n",
- buf.tx.ac3_enable);
- }
- }
-
- if (dice_proc_read_mem(dice, &tx_rx_header, sections[4], 2) < 0)
- return;
- quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.rx) / 4);
- for (stream = 0; stream < tx_rx_header.number; ++stream) {
- if (dice_proc_read_mem(dice, &buf.rx, sections[4] + 2 +
- stream * tx_rx_header.size,
- quadlets) < 0)
- break;
- snd_iprintf(buffer, "rx %u:\n", stream);
- snd_iprintf(buffer, " iso channel: %d\n", (int)buf.rx.iso);
- snd_iprintf(buffer, " sequence start: %u\n", buf.rx.seq_start);
- snd_iprintf(buffer, " audio channels: %u\n",
- buf.rx.number_audio);
- snd_iprintf(buffer, " midi ports: %u\n", buf.rx.number_midi);
- if (quadlets >= 68) {
- dice_proc_fixup_string(buf.rx.names, RX_NAMES_SIZE);
- snd_iprintf(buffer, " names: %s\n", buf.rx.names);
- }
- if (quadlets >= 70) {
- snd_iprintf(buffer, " ac3 caps: %08x\n",
- buf.rx.ac3_caps);
- snd_iprintf(buffer, " ac3 enable: %08x\n",
- buf.rx.ac3_enable);
- }
- }
-
- quadlets = min_t(u32, sections[7], sizeof(buf.ext_sync) / 4);
- if (quadlets >= 4) {
- if (dice_proc_read_mem(dice, &buf.ext_sync,
- sections[6], 4) < 0)
- return;
- snd_iprintf(buffer, "ext status:\n");
- snd_iprintf(buffer, " clock source: %s\n",
- str_from_array(clock_sources,
- ARRAY_SIZE(clock_sources),
- buf.ext_sync.clock_source));
- snd_iprintf(buffer, " locked: %u\n", buf.ext_sync.locked);
- snd_iprintf(buffer, " rate: %s\n",
- str_from_array(rates, ARRAY_SIZE(rates),
- buf.ext_sync.rate));
- snd_iprintf(buffer, " adat user data: ");
- if (buf.ext_sync.adat_user_data & ADAT_USER_DATA_NO_DATA)
- snd_iprintf(buffer, "-\n");
- else
- snd_iprintf(buffer, "%x\n",
- buf.ext_sync.adat_user_data);
- }
-}
-
-static void dice_create_proc(struct dice *dice)
-{
- struct snd_info_entry *entry;
-
- if (!snd_card_proc_new(dice->card, "dice", &entry))
- snd_info_set_text_ops(entry, dice, dice_proc_read);
-}
-
-static void dice_card_free(struct snd_card *card)
-{
- struct dice *dice = card->private_data;
-
- amdtp_stream_destroy(&dice->stream);
- fw_core_remove_address_handler(&dice->notification_handler);
- mutex_destroy(&dice->mutex);
-}
-
-#define OUI_WEISS 0x001c6a
-
-#define DICE_CATEGORY_ID 0x04
-#define WEISS_CATEGORY_ID 0x00
-
-static int dice_interface_check(struct fw_unit *unit)
-{
- static const int min_values[10] = {
- 10, 0x64 / 4,
- 10, 0x18 / 4,
- 10, 0x18 / 4,
- 0, 0,
- 0, 0,
- };
- struct fw_device *device = fw_parent_device(unit);
- struct fw_csr_iterator it;
- int key, value, vendor = -1, model = -1, err;
- unsigned int category, i;
- __be32 pointers[ARRAY_SIZE(min_values)];
- __be32 tx_data[4];
- __be32 version;
-
- /*
- * Check that GUID and unit directory are constructed according to DICE
- * rules, i.e., that the specifier ID is the GUID's OUI, and that the
- * GUID chip ID consists of the 8-bit category ID, the 10-bit product
- * ID, and a 22-bit serial number.
- */
- fw_csr_iterator_init(&it, unit->directory);
- while (fw_csr_iterator_next(&it, &key, &value)) {
- switch (key) {
- case CSR_SPECIFIER_ID:
- vendor = value;
- break;
- case CSR_MODEL:
- model = value;
- break;
- }
- }
- if (vendor == OUI_WEISS)
- category = WEISS_CATEGORY_ID;
- else
- category = DICE_CATEGORY_ID;
- if (device->config_rom[3] != ((vendor << 8) | category) ||
- device->config_rom[4] >> 22 != model)
- return -ENODEV;
-
- /*
- * Check that the sub address spaces exist and are located inside the
- * private address space. The minimum values are chosen so that all
- * minimally required registers are included.
- */
- err = snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST,
- DICE_PRIVATE_SPACE,
- pointers, sizeof(pointers), 0);
- if (err < 0)
- return -ENODEV;
- for (i = 0; i < ARRAY_SIZE(pointers); ++i) {
- value = be32_to_cpu(pointers[i]);
- if (value < min_values[i] || value >= 0x40000)
- return -ENODEV;
- }
-
- /* We support playback only. Let capture devices be handled by FFADO. */
- err = snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST,
- DICE_PRIVATE_SPACE +
- be32_to_cpu(pointers[2]) * 4,
- tx_data, sizeof(tx_data), 0);
- if (err < 0 || (tx_data[0] && tx_data[3]))
- return -ENODEV;
-
- /*
- * Check that the implemented DICE driver specification major version
- * number matches.
- */
- err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
- DICE_PRIVATE_SPACE +
- be32_to_cpu(pointers[0]) * 4 + GLOBAL_VERSION,
- &version, 4, 0);
- if (err < 0)
- return -ENODEV;
- if ((version & cpu_to_be32(0xff000000)) != cpu_to_be32(0x01000000)) {
- dev_err(&unit->device,
- "unknown DICE version: 0x%08x\n", be32_to_cpu(version));
- return -ENODEV;
- }
-
- return 0;
-}
-
-static int highest_supported_mode_rate(struct dice *dice, unsigned int mode)
-{
- int i;
-
- for (i = ARRAY_SIZE(dice_rates) - 1; i >= 0; --i)
- if ((dice->clock_caps & (1 << i)) &&
- rate_index_to_mode(i) == mode)
- return i;
-
- return -1;
-}
-
-static int dice_read_mode_params(struct dice *dice, unsigned int mode)
-{
- __be32 values[2];
- int rate_index, err;
-
- rate_index = highest_supported_mode_rate(dice, mode);
- if (rate_index < 0) {
- dice->rx_channels[mode] = 0;
- dice->rx_midi_ports[mode] = 0;
- return 0;
- }
-
- err = dice_change_rate(dice, rate_index << CLOCK_RATE_SHIFT);
- if (err < 0)
- return err;
-
- err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
- rx_address(dice, RX_NUMBER_AUDIO),
- values, 2 * 4, 0);
- if (err < 0)
- return err;
-
- dice->rx_channels[mode] = be32_to_cpu(values[0]);
- dice->rx_midi_ports[mode] = be32_to_cpu(values[1]);
-
- return 0;
-}
-
-static int dice_read_params(struct dice *dice)
-{
- __be32 pointers[6];
- __be32 value;
- int mode, err;
-
- err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
- DICE_PRIVATE_SPACE,
- pointers, sizeof(pointers), 0);
- if (err < 0)
- return err;
-
- dice->global_offset = be32_to_cpu(pointers[0]) * 4;
- dice->rx_offset = be32_to_cpu(pointers[4]) * 4;
-
- /* some very old firmwares don't tell about their clock support */
- if (be32_to_cpu(pointers[1]) * 4 >= GLOBAL_CLOCK_CAPABILITIES + 4) {
- err = snd_fw_transaction(
- dice->unit, TCODE_READ_QUADLET_REQUEST,
- global_address(dice, GLOBAL_CLOCK_CAPABILITIES),
- &value, 4, 0);
- if (err < 0)
- return err;
- dice->clock_caps = be32_to_cpu(value);
- } else {
- /* this should be supported by any device */
- dice->clock_caps = CLOCK_CAP_RATE_44100 |
- CLOCK_CAP_RATE_48000 |
- CLOCK_CAP_SOURCE_ARX1 |
- CLOCK_CAP_SOURCE_INTERNAL;
- }
-
- for (mode = 2; mode >= 0; --mode) {
- err = dice_read_mode_params(dice, mode);
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
-static void dice_card_strings(struct dice *dice)
-{
- struct snd_card *card = dice->card;
- struct fw_device *dev = fw_parent_device(dice->unit);
- char vendor[32], model[32];
- unsigned int i;
- int err;
-
- strcpy(card->driver, "DICE");
-
- strcpy(card->shortname, "DICE");
- BUILD_BUG_ON(NICK_NAME_SIZE < sizeof(card->shortname));
- err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
- global_address(dice, GLOBAL_NICK_NAME),
- card->shortname, sizeof(card->shortname), 0);
- if (err >= 0) {
- /* DICE strings are returned in "always-wrong" endianness */
- BUILD_BUG_ON(sizeof(card->shortname) % 4 != 0);
- for (i = 0; i < sizeof(card->shortname); i += 4)
- swab32s((u32 *)&card->shortname[i]);
- card->shortname[sizeof(card->shortname) - 1] = '\0';
- }
-
- strcpy(vendor, "?");
- fw_csr_string(dev->config_rom + 5, CSR_VENDOR, vendor, sizeof(vendor));
- strcpy(model, "?");
- fw_csr_string(dice->unit->directory, CSR_MODEL, model, sizeof(model));
- snprintf(card->longname, sizeof(card->longname),
- "%s %s (serial %u) at %s, S%d",
- vendor, model, dev->config_rom[4] & 0x3fffff,
- dev_name(&dice->unit->device), 100 << dev->max_speed);
-
- strcpy(card->mixername, "DICE");
-}
-
-static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
-{
- struct snd_card *card;
- struct dice *dice;
- __be32 clock_sel;
- int err;
-
- err = dice_interface_check(unit);
- if (err < 0)
- return err;
-
- err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
- sizeof(*dice), &card);
- if (err < 0)
- return err;
-
- dice = card->private_data;
- dice->card = card;
- spin_lock_init(&dice->lock);
- mutex_init(&dice->mutex);
- dice->unit = unit;
- init_completion(&dice->clock_accepted);
- init_waitqueue_head(&dice->hwdep_wait);
-
- dice->notification_handler.length = 4;
- dice->notification_handler.address_callback = dice_notification;
- dice->notification_handler.callback_data = dice;
- err = fw_core_add_address_handler(&dice->notification_handler,
- &fw_high_memory_region);
- if (err < 0)
- goto err_mutex;
-
- err = dice_owner_set(dice);
- if (err < 0)
- goto err_notification_handler;
-
- err = dice_read_params(dice);
- if (err < 0)
- goto err_owner;
-
- err = fw_iso_resources_init(&dice->resources, unit);
- if (err < 0)
- goto err_owner;
- dice->resources.channels_mask = 0x00000000ffffffffuLL;
-
- err = amdtp_stream_init(&dice->stream, unit, AMDTP_OUT_STREAM,
- CIP_BLOCKING);
- if (err < 0)
- goto err_resources;
-
- card->private_free = dice_card_free;
-
- dice_card_strings(dice);
-
- err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
- global_address(dice, GLOBAL_CLOCK_SELECT),
- &clock_sel, 4, 0);
- if (err < 0)
- goto error;
- clock_sel &= cpu_to_be32(~CLOCK_SOURCE_MASK);
- clock_sel |= cpu_to_be32(CLOCK_SOURCE_ARX1);
- err = snd_fw_transaction(unit, TCODE_WRITE_QUADLET_REQUEST,
- global_address(dice, GLOBAL_CLOCK_SELECT),
- &clock_sel, 4, 0);
- if (err < 0)
- goto error;
-
- err = dice_create_pcm(dice);
- if (err < 0)
- goto error;
-
- err = dice_create_hwdep(dice);
- if (err < 0)
- goto error;
-
- dice_create_proc(dice);
-
- err = snd_card_register(card);
- if (err < 0)
- goto error;
-
- dev_set_drvdata(&unit->device, dice);
-
- return 0;
-
-err_resources:
- fw_iso_resources_destroy(&dice->resources);
-err_owner:
- dice_owner_clear(dice);
-err_notification_handler:
- fw_core_remove_address_handler(&dice->notification_handler);
-err_mutex:
- mutex_destroy(&dice->mutex);
-error:
- snd_card_free(card);
- return err;
-}
-
-static void dice_remove(struct fw_unit *unit)
-{
- struct dice *dice = dev_get_drvdata(&unit->device);
-
- amdtp_stream_pcm_abort(&dice->stream);
-
- snd_card_disconnect(dice->card);
-
- mutex_lock(&dice->mutex);
-
- dice_stream_stop(dice);
- dice_owner_clear(dice);
-
- mutex_unlock(&dice->mutex);
-
- snd_card_free_when_closed(dice->card);
-}
-
-static void dice_bus_reset(struct fw_unit *unit)
-{
- struct dice *dice = dev_get_drvdata(&unit->device);
-
- /*
- * On a bus reset, the DICE firmware disables streaming and then goes
- * off contemplating its own navel for hundreds of milliseconds before
- * it can react to any of our attempts to reenable streaming. This
- * means that we lose synchronization anyway, so we force our streams
- * to stop so that the application can restart them in an orderly
- * manner.
- */
- amdtp_stream_pcm_abort(&dice->stream);
-
- mutex_lock(&dice->mutex);
-
- dice->global_enabled = false;
- dice_stream_stop_packets(dice);
-
- dice_owner_update(dice);
-
- fw_iso_resources_update(&dice->resources);
-
- mutex_unlock(&dice->mutex);
-}
-
-#define DICE_INTERFACE 0x000001
-
-static const struct ieee1394_device_id dice_id_table[] = {
- {
- .match_flags = IEEE1394_MATCH_VERSION,
- .version = DICE_INTERFACE,
- },
- { }
-};
-MODULE_DEVICE_TABLE(ieee1394, dice_id_table);
-
-static struct fw_driver dice_driver = {
- .driver = {
- .owner = THIS_MODULE,
- .name = KBUILD_MODNAME,
- .bus = &fw_bus_type,
- },
- .probe = dice_probe,
- .update = dice_bus_reset,
- .remove = dice_remove,
- .id_table = dice_id_table,
-};
-
-static int __init alsa_dice_init(void)
-{
- return driver_register(&dice_driver.driver);
-}
-
-static void __exit alsa_dice_exit(void)
-{
- driver_unregister(&dice_driver.driver);
-}
-
-module_init(alsa_dice_init);
-module_exit(alsa_dice_exit);
diff --git a/sound/firewire/dice/Makefile b/sound/firewire/dice/Makefile
new file mode 100644
index 000000000000..9ef228ef7baf
--- /dev/null
+++ b/sound/firewire/dice/Makefile
@@ -0,0 +1,3 @@
+snd-dice-objs := dice-transaction.o dice-stream.o dice-proc.o dice-midi.o \
+ dice-pcm.o dice-hwdep.o dice.o
+obj-m += snd-dice.o
diff --git a/sound/firewire/dice/dice-hwdep.c b/sound/firewire/dice/dice-hwdep.c
new file mode 100644
index 000000000000..a4dc02a86f12
--- /dev/null
+++ b/sound/firewire/dice/dice-hwdep.c
@@ -0,0 +1,190 @@
+/*
+ * dice_hwdep.c - a part of driver for DICE based devices
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Copyright (c) 2014 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "dice.h"
+
+static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf,
+ long count, loff_t *offset)
+{
+ struct snd_dice *dice = hwdep->private_data;
+ DEFINE_WAIT(wait);
+ union snd_firewire_event event;
+
+ spin_lock_irq(&dice->lock);
+
+ while (!dice->dev_lock_changed && dice->notification_bits == 0) {
+ prepare_to_wait(&dice->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+ spin_unlock_irq(&dice->lock);
+ schedule();
+ finish_wait(&dice->hwdep_wait, &wait);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ spin_lock_irq(&dice->lock);
+ }
+
+ memset(&event, 0, sizeof(event));
+ if (dice->dev_lock_changed) {
+ event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
+ event.lock_status.status = dice->dev_lock_count > 0;
+ dice->dev_lock_changed = false;
+
+ count = min_t(long, count, sizeof(event.lock_status));
+ } else {
+ event.dice_notification.type =
+ SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION;
+ event.dice_notification.notification = dice->notification_bits;
+ dice->notification_bits = 0;
+
+ count = min_t(long, count, sizeof(event.dice_notification));
+ }
+
+ spin_unlock_irq(&dice->lock);
+
+ if (copy_to_user(buf, &event, count))
+ return -EFAULT;
+
+ return count;
+}
+
+static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
+ poll_table *wait)
+{
+ struct snd_dice *dice = hwdep->private_data;
+ unsigned int events;
+
+ poll_wait(file, &dice->hwdep_wait, wait);
+
+ spin_lock_irq(&dice->lock);
+ if (dice->dev_lock_changed || dice->notification_bits != 0)
+ events = POLLIN | POLLRDNORM;
+ else
+ events = 0;
+ spin_unlock_irq(&dice->lock);
+
+ return events;
+}
+
+static int hwdep_get_info(struct snd_dice *dice, void __user *arg)
+{
+ struct fw_device *dev = fw_parent_device(dice->unit);
+ struct snd_firewire_get_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.type = SNDRV_FIREWIRE_TYPE_DICE;
+ info.card = dev->card->index;
+ *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+ *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+ strlcpy(info.device_name, dev_name(&dev->device),
+ sizeof(info.device_name));
+
+ if (copy_to_user(arg, &info, sizeof(info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int hwdep_lock(struct snd_dice *dice)
+{
+ int err;
+
+ spin_lock_irq(&dice->lock);
+
+ if (dice->dev_lock_count == 0) {
+ dice->dev_lock_count = -1;
+ err = 0;
+ } else {
+ err = -EBUSY;
+ }
+
+ spin_unlock_irq(&dice->lock);
+
+ return err;
+}
+
+static int hwdep_unlock(struct snd_dice *dice)
+{
+ int err;
+
+ spin_lock_irq(&dice->lock);
+
+ if (dice->dev_lock_count == -1) {
+ dice->dev_lock_count = 0;
+ err = 0;
+ } else {
+ err = -EBADFD;
+ }
+
+ spin_unlock_irq(&dice->lock);
+
+ return err;
+}
+
+static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+ struct snd_dice *dice = hwdep->private_data;
+
+ spin_lock_irq(&dice->lock);
+ if (dice->dev_lock_count == -1)
+ dice->dev_lock_count = 0;
+ spin_unlock_irq(&dice->lock);
+
+ return 0;
+}
+
+static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct snd_dice *dice = hwdep->private_data;
+
+ switch (cmd) {
+ case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+ return hwdep_get_info(dice, (void __user *)arg);
+ case SNDRV_FIREWIRE_IOCTL_LOCK:
+ return hwdep_lock(dice);
+ case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+ return hwdep_unlock(dice);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return hwdep_ioctl(hwdep, file, cmd,
+ (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+int snd_dice_create_hwdep(struct snd_dice *dice)
+{
+ static const struct snd_hwdep_ops ops = {
+ .read = hwdep_read,
+ .release = hwdep_release,
+ .poll = hwdep_poll,
+ .ioctl = hwdep_ioctl,
+ .ioctl_compat = hwdep_compat_ioctl,
+ };
+ struct snd_hwdep *hwdep;
+ int err;
+
+ err = snd_hwdep_new(dice->card, "DICE", 0, &hwdep);
+ if (err < 0)
+ return err;
+ strcpy(hwdep->name, "DICE");
+ hwdep->iface = SNDRV_HWDEP_IFACE_FW_DICE;
+ hwdep->ops = ops;
+ hwdep->private_data = dice;
+ hwdep->exclusive = true;
+
+ return 0;
+}
diff --git a/sound/firewire/dice-interface.h b/sound/firewire/dice/dice-interface.h
index 27b044f84c81..27b044f84c81 100644
--- a/sound/firewire/dice-interface.h
+++ b/sound/firewire/dice/dice-interface.h
diff --git a/sound/firewire/dice/dice-midi.c b/sound/firewire/dice/dice-midi.c
new file mode 100644
index 000000000000..fe43ce791f84
--- /dev/null
+++ b/sound/firewire/dice/dice-midi.c
@@ -0,0 +1,157 @@
+/*
+ * dice_midi.c - a part of driver for Dice based devices
+ *
+ * Copyright (c) 2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+#include "dice.h"
+
+static int midi_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_dice *dice = substream->rmidi->private_data;
+ int err;
+
+ err = snd_dice_stream_lock_try(dice);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&dice->mutex);
+
+ dice->substreams_counter++;
+ err = snd_dice_stream_start_duplex(dice, 0);
+
+ mutex_unlock(&dice->mutex);
+
+ if (err < 0)
+ snd_dice_stream_lock_release(dice);
+
+ return err;
+}
+
+static int midi_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_dice *dice = substream->rmidi->private_data;
+
+ mutex_lock(&dice->mutex);
+
+ dice->substreams_counter--;
+ snd_dice_stream_stop_duplex(dice);
+
+ mutex_unlock(&dice->mutex);
+
+ snd_dice_stream_lock_release(dice);
+ return 0;
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_dice *dice = substrm->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dice->lock, flags);
+
+ if (up)
+ amdtp_stream_midi_trigger(&dice->tx_stream,
+ substrm->number, substrm);
+ else
+ amdtp_stream_midi_trigger(&dice->tx_stream,
+ substrm->number, NULL);
+
+ spin_unlock_irqrestore(&dice->lock, flags);
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_dice *dice = substrm->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dice->lock, flags);
+
+ if (up)
+ amdtp_stream_midi_trigger(&dice->rx_stream,
+ substrm->number, substrm);
+ else
+ amdtp_stream_midi_trigger(&dice->rx_stream,
+ substrm->number, NULL);
+
+ spin_unlock_irqrestore(&dice->lock, flags);
+}
+
+static struct snd_rawmidi_ops capture_ops = {
+ .open = midi_open,
+ .close = midi_close,
+ .trigger = midi_capture_trigger,
+};
+
+static struct snd_rawmidi_ops playback_ops = {
+ .open = midi_open,
+ .close = midi_close,
+ .trigger = midi_playback_trigger,
+};
+
+static void set_midi_substream_names(struct snd_dice *dice,
+ struct snd_rawmidi_str *str)
+{
+ struct snd_rawmidi_substream *subs;
+
+ list_for_each_entry(subs, &str->substreams, list) {
+ snprintf(subs->name, sizeof(subs->name),
+ "%s MIDI %d", dice->card->shortname, subs->number + 1);
+ }
+}
+
+int snd_dice_create_midi(struct snd_dice *dice)
+{
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_str *str;
+ unsigned int i, midi_in_ports, midi_out_ports;
+ int err;
+
+ midi_in_ports = midi_out_ports = 0;
+ for (i = 0; i < 3; i++) {
+ midi_in_ports = max(dice->tx_midi_ports[i], midi_in_ports);
+ midi_out_ports = max(dice->rx_midi_ports[i], midi_out_ports);
+ }
+
+ if (midi_in_ports + midi_out_ports == 0)
+ return 0;
+
+ /* create midi ports */
+ err = snd_rawmidi_new(dice->card, dice->card->driver, 0,
+ midi_out_ports, midi_in_ports,
+ &rmidi);
+ if (err < 0)
+ return err;
+
+ snprintf(rmidi->name, sizeof(rmidi->name),
+ "%s MIDI", dice->card->shortname);
+ rmidi->private_data = dice;
+
+ if (midi_in_ports > 0) {
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &capture_ops);
+
+ str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+
+ set_midi_substream_names(dice, str);
+ }
+
+ if (midi_out_ports > 0) {
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &playback_ops);
+
+ str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+
+ set_midi_substream_names(dice, str);
+ }
+
+ if ((midi_out_ports > 0) && (midi_in_ports > 0))
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ return 0;
+}
diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c
new file mode 100644
index 000000000000..f77714511f8b
--- /dev/null
+++ b/sound/firewire/dice/dice-pcm.c
@@ -0,0 +1,422 @@
+/*
+ * dice_pcm.c - a part of driver for DICE based devices
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Copyright (c) 2014 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "dice.h"
+
+static int dice_rate_constraint(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_pcm_substream *substream = rule->private;
+ struct snd_dice *dice = substream->private_data;
+
+ const struct snd_interval *c =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval *r =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval rates = {
+ .min = UINT_MAX, .max = 0, .integer = 1
+ };
+ unsigned int i, rate, mode, *pcm_channels;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ pcm_channels = dice->tx_channels;
+ else
+ pcm_channels = dice->rx_channels;
+
+ for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
+ rate = snd_dice_rates[i];
+ if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
+ continue;
+
+ if (!snd_interval_test(c, pcm_channels[mode]))
+ continue;
+
+ rates.min = min(rates.min, rate);
+ rates.max = max(rates.max, rate);
+ }
+
+ return snd_interval_refine(r, &rates);
+}
+
+static int dice_channels_constraint(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_pcm_substream *substream = rule->private;
+ struct snd_dice *dice = substream->private_data;
+
+ const struct snd_interval *r =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *c =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval channels = {
+ .min = UINT_MAX, .max = 0, .integer = 1
+ };
+ unsigned int i, rate, mode, *pcm_channels;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ pcm_channels = dice->tx_channels;
+ else
+ pcm_channels = dice->rx_channels;
+
+ for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
+ rate = snd_dice_rates[i];
+ if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
+ continue;
+
+ if (!snd_interval_test(r, rate))
+ continue;
+
+ channels.min = min(channels.min, pcm_channels[mode]);
+ channels.max = max(channels.max, pcm_channels[mode]);
+ }
+
+ return snd_interval_refine(c, &channels);
+}
+
+static void limit_channels_and_rates(struct snd_dice *dice,
+ struct snd_pcm_runtime *runtime,
+ unsigned int *pcm_channels)
+{
+ struct snd_pcm_hardware *hw = &runtime->hw;
+ unsigned int i, rate, mode;
+
+ hw->channels_min = UINT_MAX;
+ hw->channels_max = 0;
+
+ for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
+ rate = snd_dice_rates[i];
+ if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
+ continue;
+ hw->rates |= snd_pcm_rate_to_rate_bit(rate);
+
+ if (pcm_channels[mode] == 0)
+ continue;
+ hw->channels_min = min(hw->channels_min, pcm_channels[mode]);
+ hw->channels_max = max(hw->channels_max, pcm_channels[mode]);
+ }
+
+ snd_pcm_limit_hw_rates(runtime);
+}
+
+static void limit_period_and_buffer(struct snd_pcm_hardware *hw)
+{
+ hw->periods_min = 2; /* SNDRV_PCM_INFO_BATCH */
+ hw->periods_max = UINT_MAX;
+
+ hw->period_bytes_min = 4 * hw->channels_max; /* byte for a frame */
+
+ /* Just to prevent from allocating much pages. */
+ hw->period_bytes_max = hw->period_bytes_min * 2048;
+ hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min;
+}
+
+static int init_hw_info(struct snd_dice *dice,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_pcm_hardware *hw = &runtime->hw;
+ struct amdtp_stream *stream;
+ unsigned int *pcm_channels;
+ int err;
+
+ hw->info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_JOINT_DUPLEX |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ hw->formats = AMDTP_IN_PCM_FORMAT_BITS;
+ stream = &dice->tx_stream;
+ pcm_channels = dice->tx_channels;
+ } else {
+ hw->formats = AMDTP_OUT_PCM_FORMAT_BITS;
+ stream = &dice->rx_stream;
+ pcm_channels = dice->rx_channels;
+ }
+
+ limit_channels_and_rates(dice, runtime, pcm_channels);
+ limit_period_and_buffer(hw);
+
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ dice_rate_constraint, substream,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (err < 0)
+ goto end;
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ dice_channels_constraint, substream,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ if (err < 0)
+ goto end;
+
+ err = amdtp_stream_add_pcm_hw_constraints(stream, runtime);
+end:
+ return err;
+}
+
+static int pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_dice *dice = substream->private_data;
+ unsigned int source, rate;
+ bool internal;
+ int err;
+
+ err = snd_dice_stream_lock_try(dice);
+ if (err < 0)
+ goto end;
+
+ err = init_hw_info(dice, substream);
+ if (err < 0)
+ goto err_locked;
+
+ err = snd_dice_transaction_get_clock_source(dice, &source);
+ if (err < 0)
+ goto err_locked;
+ switch (source) {
+ case CLOCK_SOURCE_AES1:
+ case CLOCK_SOURCE_AES2:
+ case CLOCK_SOURCE_AES3:
+ case CLOCK_SOURCE_AES4:
+ case CLOCK_SOURCE_AES_ANY:
+ case CLOCK_SOURCE_ADAT:
+ case CLOCK_SOURCE_TDIF:
+ case CLOCK_SOURCE_WC:
+ internal = false;
+ break;
+ default:
+ internal = true;
+ break;
+ }
+
+ /*
+ * When source of clock is not internal or any PCM streams are running,
+ * available sampling rate is limited at current sampling rate.
+ */
+ if (!internal ||
+ amdtp_stream_pcm_running(&dice->tx_stream) ||
+ amdtp_stream_pcm_running(&dice->rx_stream)) {
+ err = snd_dice_transaction_get_rate(dice, &rate);
+ if (err < 0)
+ goto err_locked;
+ substream->runtime->hw.rate_min = rate;
+ substream->runtime->hw.rate_max = rate;
+ }
+
+ snd_pcm_set_sync(substream);
+end:
+ return err;
+err_locked:
+ snd_dice_stream_lock_release(dice);
+ return err;
+}
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_dice *dice = substream->private_data;
+
+ snd_dice_stream_lock_release(dice);
+
+ return 0;
+}
+
+static int capture_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_dice *dice = substream->private_data;
+
+ if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ mutex_lock(&dice->mutex);
+ dice->substreams_counter++;
+ mutex_unlock(&dice->mutex);
+ }
+
+ amdtp_stream_set_pcm_format(&dice->tx_stream,
+ params_format(hw_params));
+
+ return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+}
+static int playback_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_dice *dice = substream->private_data;
+
+ if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ mutex_lock(&dice->mutex);
+ dice->substreams_counter++;
+ mutex_unlock(&dice->mutex);
+ }
+
+ amdtp_stream_set_pcm_format(&dice->rx_stream,
+ params_format(hw_params));
+
+ return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+}
+
+static int capture_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_dice *dice = substream->private_data;
+
+ mutex_lock(&dice->mutex);
+
+ if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+ dice->substreams_counter--;
+
+ snd_dice_stream_stop_duplex(dice);
+
+ mutex_unlock(&dice->mutex);
+
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int playback_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_dice *dice = substream->private_data;
+
+ mutex_lock(&dice->mutex);
+
+ if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+ dice->substreams_counter--;
+
+ snd_dice_stream_stop_duplex(dice);
+
+ mutex_unlock(&dice->mutex);
+
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_dice *dice = substream->private_data;
+ int err;
+
+ mutex_lock(&dice->mutex);
+ err = snd_dice_stream_start_duplex(dice, substream->runtime->rate);
+ mutex_unlock(&dice->mutex);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&dice->tx_stream);
+
+ return 0;
+}
+static int playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_dice *dice = substream->private_data;
+ int err;
+
+ mutex_lock(&dice->mutex);
+ err = snd_dice_stream_start_duplex(dice, substream->runtime->rate);
+ mutex_unlock(&dice->mutex);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&dice->rx_stream);
+
+ return err;
+}
+
+static int capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_dice *dice = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&dice->tx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&dice->tx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+static int playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_dice *dice = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&dice->rx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&dice->rx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_dice *dice = substream->private_data;
+
+ return amdtp_stream_pcm_pointer(&dice->tx_stream);
+}
+static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_dice *dice = substream->private_data;
+
+ return amdtp_stream_pcm_pointer(&dice->rx_stream);
+}
+
+int snd_dice_create_pcm(struct snd_dice *dice)
+{
+ static struct snd_pcm_ops capture_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = capture_hw_params,
+ .hw_free = capture_hw_free,
+ .prepare = capture_prepare,
+ .trigger = capture_trigger,
+ .pointer = capture_pointer,
+ .page = snd_pcm_lib_get_vmalloc_page,
+ .mmap = snd_pcm_lib_mmap_vmalloc,
+ };
+ static struct snd_pcm_ops playback_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = playback_hw_params,
+ .hw_free = playback_hw_free,
+ .prepare = playback_prepare,
+ .trigger = playback_trigger,
+ .pointer = playback_pointer,
+ .page = snd_pcm_lib_get_vmalloc_page,
+ .mmap = snd_pcm_lib_mmap_vmalloc,
+ };
+ struct snd_pcm *pcm;
+ unsigned int i, capture, playback;
+ int err;
+
+ capture = playback = 0;
+ for (i = 0; i < 3; i++) {
+ if (dice->tx_channels[i] > 0)
+ capture = 1;
+ if (dice->rx_channels[i] > 0)
+ playback = 1;
+ }
+
+ err = snd_pcm_new(dice->card, "DICE", 0, playback, capture, &pcm);
+ if (err < 0)
+ return err;
+ pcm->private_data = dice;
+ strcpy(pcm->name, dice->card->shortname);
+
+ if (capture > 0)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
+
+ if (playback > 0)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
+
+ return 0;
+}
diff --git a/sound/firewire/dice/dice-proc.c b/sound/firewire/dice/dice-proc.c
new file mode 100644
index 000000000000..f5c1d1bced59
--- /dev/null
+++ b/sound/firewire/dice/dice-proc.c
@@ -0,0 +1,252 @@
+/*
+ * dice_proc.c - a part of driver for Dice based devices
+ *
+ * Copyright (c) Clemens Ladisch
+ * Copyright (c) 2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "dice.h"
+
+static int dice_proc_read_mem(struct snd_dice *dice, void *buffer,
+ unsigned int offset_q, unsigned int quadlets)
+{
+ unsigned int i;
+ int err;
+
+ err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
+ DICE_PRIVATE_SPACE + 4 * offset_q,
+ buffer, 4 * quadlets, 0);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < quadlets; ++i)
+ be32_to_cpus(&((u32 *)buffer)[i]);
+
+ return 0;
+}
+
+static const char *str_from_array(const char *const strs[], unsigned int count,
+ unsigned int i)
+{
+ if (i < count)
+ return strs[i];
+
+ return "(unknown)";
+}
+
+static void dice_proc_fixup_string(char *s, unsigned int size)
+{
+ unsigned int i;
+
+ for (i = 0; i < size; i += 4)
+ cpu_to_le32s((u32 *)(s + i));
+
+ for (i = 0; i < size - 2; ++i) {
+ if (s[i] == '\0')
+ return;
+ if (s[i] == '\\' && s[i + 1] == '\\') {
+ s[i + 2] = '\0';
+ return;
+ }
+ }
+ s[size - 1] = '\0';
+}
+
+static void dice_proc_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ static const char *const section_names[5] = {
+ "global", "tx", "rx", "ext_sync", "unused2"
+ };
+ static const char *const clock_sources[] = {
+ "aes1", "aes2", "aes3", "aes4", "aes", "adat", "tdif",
+ "wc", "arx1", "arx2", "arx3", "arx4", "internal"
+ };
+ static const char *const rates[] = {
+ "32000", "44100", "48000", "88200", "96000", "176400", "192000",
+ "any low", "any mid", "any high", "none"
+ };
+ struct snd_dice *dice = entry->private_data;
+ u32 sections[ARRAY_SIZE(section_names) * 2];
+ struct {
+ u32 number;
+ u32 size;
+ } tx_rx_header;
+ union {
+ struct {
+ u32 owner_hi, owner_lo;
+ u32 notification;
+ char nick_name[NICK_NAME_SIZE];
+ u32 clock_select;
+ u32 enable;
+ u32 status;
+ u32 extended_status;
+ u32 sample_rate;
+ u32 version;
+ u32 clock_caps;
+ char clock_source_names[CLOCK_SOURCE_NAMES_SIZE];
+ } global;
+ struct {
+ u32 iso;
+ u32 number_audio;
+ u32 number_midi;
+ u32 speed;
+ char names[TX_NAMES_SIZE];
+ u32 ac3_caps;
+ u32 ac3_enable;
+ } tx;
+ struct {
+ u32 iso;
+ u32 seq_start;
+ u32 number_audio;
+ u32 number_midi;
+ char names[RX_NAMES_SIZE];
+ u32 ac3_caps;
+ u32 ac3_enable;
+ } rx;
+ struct {
+ u32 clock_source;
+ u32 locked;
+ u32 rate;
+ u32 adat_user_data;
+ } ext_sync;
+ } buf;
+ unsigned int quadlets, stream, i;
+
+ if (dice_proc_read_mem(dice, sections, 0, ARRAY_SIZE(sections)) < 0)
+ return;
+ snd_iprintf(buffer, "sections:\n");
+ for (i = 0; i < ARRAY_SIZE(section_names); ++i)
+ snd_iprintf(buffer, " %s: offset %u, size %u\n",
+ section_names[i],
+ sections[i * 2], sections[i * 2 + 1]);
+
+ quadlets = min_t(u32, sections[1], sizeof(buf.global) / 4);
+ if (dice_proc_read_mem(dice, &buf.global, sections[0], quadlets) < 0)
+ return;
+ snd_iprintf(buffer, "global:\n");
+ snd_iprintf(buffer, " owner: %04x:%04x%08x\n",
+ buf.global.owner_hi >> 16,
+ buf.global.owner_hi & 0xffff, buf.global.owner_lo);
+ snd_iprintf(buffer, " notification: %08x\n", buf.global.notification);
+ dice_proc_fixup_string(buf.global.nick_name, NICK_NAME_SIZE);
+ snd_iprintf(buffer, " nick name: %s\n", buf.global.nick_name);
+ snd_iprintf(buffer, " clock select: %s %s\n",
+ str_from_array(clock_sources, ARRAY_SIZE(clock_sources),
+ buf.global.clock_select & CLOCK_SOURCE_MASK),
+ str_from_array(rates, ARRAY_SIZE(rates),
+ (buf.global.clock_select & CLOCK_RATE_MASK)
+ >> CLOCK_RATE_SHIFT));
+ snd_iprintf(buffer, " enable: %u\n", buf.global.enable);
+ snd_iprintf(buffer, " status: %slocked %s\n",
+ buf.global.status & STATUS_SOURCE_LOCKED ? "" : "un",
+ str_from_array(rates, ARRAY_SIZE(rates),
+ (buf.global.status &
+ STATUS_NOMINAL_RATE_MASK)
+ >> CLOCK_RATE_SHIFT));
+ snd_iprintf(buffer, " ext status: %08x\n", buf.global.extended_status);
+ snd_iprintf(buffer, " sample rate: %u\n", buf.global.sample_rate);
+ snd_iprintf(buffer, " version: %u.%u.%u.%u\n",
+ (buf.global.version >> 24) & 0xff,
+ (buf.global.version >> 16) & 0xff,
+ (buf.global.version >> 8) & 0xff,
+ (buf.global.version >> 0) & 0xff);
+ if (quadlets >= 90) {
+ snd_iprintf(buffer, " clock caps:");
+ for (i = 0; i <= 6; ++i)
+ if (buf.global.clock_caps & (1 << i))
+ snd_iprintf(buffer, " %s", rates[i]);
+ for (i = 0; i <= 12; ++i)
+ if (buf.global.clock_caps & (1 << (16 + i)))
+ snd_iprintf(buffer, " %s", clock_sources[i]);
+ snd_iprintf(buffer, "\n");
+ dice_proc_fixup_string(buf.global.clock_source_names,
+ CLOCK_SOURCE_NAMES_SIZE);
+ snd_iprintf(buffer, " clock source names: %s\n",
+ buf.global.clock_source_names);
+ }
+
+ if (dice_proc_read_mem(dice, &tx_rx_header, sections[2], 2) < 0)
+ return;
+ quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.tx) / 4);
+ for (stream = 0; stream < tx_rx_header.number; ++stream) {
+ if (dice_proc_read_mem(dice, &buf.tx, sections[2] + 2 +
+ stream * tx_rx_header.size,
+ quadlets) < 0)
+ break;
+ snd_iprintf(buffer, "tx %u:\n", stream);
+ snd_iprintf(buffer, " iso channel: %d\n", (int)buf.tx.iso);
+ snd_iprintf(buffer, " audio channels: %u\n",
+ buf.tx.number_audio);
+ snd_iprintf(buffer, " midi ports: %u\n", buf.tx.number_midi);
+ snd_iprintf(buffer, " speed: S%u\n", 100u << buf.tx.speed);
+ if (quadlets >= 68) {
+ dice_proc_fixup_string(buf.tx.names, TX_NAMES_SIZE);
+ snd_iprintf(buffer, " names: %s\n", buf.tx.names);
+ }
+ if (quadlets >= 70) {
+ snd_iprintf(buffer, " ac3 caps: %08x\n",
+ buf.tx.ac3_caps);
+ snd_iprintf(buffer, " ac3 enable: %08x\n",
+ buf.tx.ac3_enable);
+ }
+ }
+
+ if (dice_proc_read_mem(dice, &tx_rx_header, sections[4], 2) < 0)
+ return;
+ quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.rx) / 4);
+ for (stream = 0; stream < tx_rx_header.number; ++stream) {
+ if (dice_proc_read_mem(dice, &buf.rx, sections[4] + 2 +
+ stream * tx_rx_header.size,
+ quadlets) < 0)
+ break;
+ snd_iprintf(buffer, "rx %u:\n", stream);
+ snd_iprintf(buffer, " iso channel: %d\n", (int)buf.rx.iso);
+ snd_iprintf(buffer, " sequence start: %u\n", buf.rx.seq_start);
+ snd_iprintf(buffer, " audio channels: %u\n",
+ buf.rx.number_audio);
+ snd_iprintf(buffer, " midi ports: %u\n", buf.rx.number_midi);
+ if (quadlets >= 68) {
+ dice_proc_fixup_string(buf.rx.names, RX_NAMES_SIZE);
+ snd_iprintf(buffer, " names: %s\n", buf.rx.names);
+ }
+ if (quadlets >= 70) {
+ snd_iprintf(buffer, " ac3 caps: %08x\n",
+ buf.rx.ac3_caps);
+ snd_iprintf(buffer, " ac3 enable: %08x\n",
+ buf.rx.ac3_enable);
+ }
+ }
+
+ quadlets = min_t(u32, sections[7], sizeof(buf.ext_sync) / 4);
+ if (quadlets >= 4) {
+ if (dice_proc_read_mem(dice, &buf.ext_sync,
+ sections[6], 4) < 0)
+ return;
+ snd_iprintf(buffer, "ext status:\n");
+ snd_iprintf(buffer, " clock source: %s\n",
+ str_from_array(clock_sources,
+ ARRAY_SIZE(clock_sources),
+ buf.ext_sync.clock_source));
+ snd_iprintf(buffer, " locked: %u\n", buf.ext_sync.locked);
+ snd_iprintf(buffer, " rate: %s\n",
+ str_from_array(rates, ARRAY_SIZE(rates),
+ buf.ext_sync.rate));
+ snd_iprintf(buffer, " adat user data: ");
+ if (buf.ext_sync.adat_user_data & ADAT_USER_DATA_NO_DATA)
+ snd_iprintf(buffer, "-\n");
+ else
+ snd_iprintf(buffer, "%x\n",
+ buf.ext_sync.adat_user_data);
+ }
+}
+
+void snd_dice_create_proc(struct snd_dice *dice)
+{
+ struct snd_info_entry *entry;
+
+ if (!snd_card_proc_new(dice->card, "dice", &entry))
+ snd_info_set_text_ops(entry, dice, dice_proc_read);
+}
diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c
new file mode 100644
index 000000000000..fa9cf761b610
--- /dev/null
+++ b/sound/firewire/dice/dice-stream.c
@@ -0,0 +1,407 @@
+/*
+ * dice_stream.c - a part of driver for DICE based devices
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Copyright (c) 2014 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "dice.h"
+
+#define CALLBACK_TIMEOUT 200
+
+const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = {
+ /* mode 0 */
+ [0] = 32000,
+ [1] = 44100,
+ [2] = 48000,
+ /* mode 1 */
+ [3] = 88200,
+ [4] = 96000,
+ /* mode 2 */
+ [5] = 176400,
+ [6] = 192000,
+};
+
+int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate,
+ unsigned int *mode)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) {
+ if (!(dice->clock_caps & BIT(i)))
+ continue;
+ if (snd_dice_rates[i] != rate)
+ continue;
+
+ *mode = (i - 1) / 2;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static void release_resources(struct snd_dice *dice,
+ struct fw_iso_resources *resources)
+{
+ unsigned int channel;
+
+ /* Reset channel number */
+ channel = cpu_to_be32((u32)-1);
+ if (resources == &dice->tx_resources)
+ snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS,
+ &channel, 4);
+ else
+ snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS,
+ &channel, 4);
+
+ fw_iso_resources_free(resources);
+}
+
+static int keep_resources(struct snd_dice *dice,
+ struct fw_iso_resources *resources,
+ unsigned int max_payload_bytes)
+{
+ unsigned int channel;
+ int err;
+
+ err = fw_iso_resources_allocate(resources, max_payload_bytes,
+ fw_parent_device(dice->unit)->max_speed);
+ if (err < 0)
+ goto end;
+
+ /* Set channel number */
+ channel = cpu_to_be32(resources->channel);
+ if (resources == &dice->tx_resources)
+ err = snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS,
+ &channel, 4);
+ else
+ err = snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS,
+ &channel, 4);
+ if (err < 0)
+ release_resources(dice, resources);
+end:
+ return err;
+}
+
+static void stop_stream(struct snd_dice *dice, struct amdtp_stream *stream)
+{
+ amdtp_stream_pcm_abort(stream);
+ amdtp_stream_stop(stream);
+
+ if (stream == &dice->tx_stream)
+ release_resources(dice, &dice->tx_resources);
+ else
+ release_resources(dice, &dice->rx_resources);
+}
+
+static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream,
+ unsigned int rate)
+{
+ struct fw_iso_resources *resources;
+ unsigned int i, mode, pcm_chs, midi_ports;
+ int err;
+
+ err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
+ if (err < 0)
+ goto end;
+ if (stream == &dice->tx_stream) {
+ resources = &dice->tx_resources;
+ pcm_chs = dice->tx_channels[mode];
+ midi_ports = dice->tx_midi_ports[mode];
+ } else {
+ resources = &dice->rx_resources;
+ pcm_chs = dice->rx_channels[mode];
+ midi_ports = dice->rx_midi_ports[mode];
+ }
+
+ /*
+ * At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in
+ * one data block of AMDTP packet. Thus sampling transfer frequency is
+ * a half of PCM sampling frequency, i.e. PCM frames at 192.0 kHz are
+ * transferred on AMDTP packets at 96 kHz. Two successive samples of a
+ * channel are stored consecutively in the packet. This quirk is called
+ * as 'Dual Wire'.
+ * For this quirk, blocking mode is required and PCM buffer size should
+ * be aligned to SYT_INTERVAL.
+ */
+ if (mode > 1) {
+ rate /= 2;
+ pcm_chs *= 2;
+ stream->double_pcm_frames = true;
+ } else {
+ stream->double_pcm_frames = false;
+ }
+
+ amdtp_stream_set_parameters(stream, rate, pcm_chs, midi_ports);
+ if (mode > 1) {
+ pcm_chs /= 2;
+
+ for (i = 0; i < pcm_chs; i++) {
+ stream->pcm_positions[i] = i * 2;
+ stream->pcm_positions[i + pcm_chs] = i * 2 + 1;
+ }
+ }
+
+ err = keep_resources(dice, resources,
+ amdtp_stream_get_max_payload(stream));
+ if (err < 0) {
+ dev_err(&dice->unit->device,
+ "fail to keep isochronous resources\n");
+ goto end;
+ }
+
+ err = amdtp_stream_start(stream, resources->channel,
+ fw_parent_device(dice->unit)->max_speed);
+ if (err < 0)
+ release_resources(dice, resources);
+end:
+ return err;
+}
+
+static int get_sync_mode(struct snd_dice *dice, enum cip_flags *sync_mode)
+{
+ u32 source;
+ int err;
+
+ err = snd_dice_transaction_get_clock_source(dice, &source);
+ if (err < 0)
+ goto end;
+
+ switch (source) {
+ /* So-called 'SYT Match' modes, sync_to_syt value of packets received */
+ case CLOCK_SOURCE_ARX4: /* in 4th stream */
+ case CLOCK_SOURCE_ARX3: /* in 3rd stream */
+ case CLOCK_SOURCE_ARX2: /* in 2nd stream */
+ err = -ENOSYS;
+ break;
+ case CLOCK_SOURCE_ARX1: /* in 1st stream, which this driver uses */
+ *sync_mode = 0;
+ break;
+ default:
+ *sync_mode = CIP_SYNC_TO_DEVICE;
+ break;
+ }
+end:
+ return err;
+}
+
+int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate)
+{
+ struct amdtp_stream *master, *slave;
+ unsigned int curr_rate;
+ enum cip_flags sync_mode;
+ int err = 0;
+
+ if (dice->substreams_counter == 0)
+ goto end;
+
+ err = get_sync_mode(dice, &sync_mode);
+ if (err < 0)
+ goto end;
+ if (sync_mode == CIP_SYNC_TO_DEVICE) {
+ master = &dice->tx_stream;
+ slave = &dice->rx_stream;
+ } else {
+ master = &dice->rx_stream;
+ slave = &dice->tx_stream;
+ }
+
+ /* Some packet queueing errors. */
+ if (amdtp_streaming_error(master) || amdtp_streaming_error(slave))
+ stop_stream(dice, master);
+
+ /* Stop stream if rate is different. */
+ err = snd_dice_transaction_get_rate(dice, &curr_rate);
+ if (err < 0) {
+ dev_err(&dice->unit->device,
+ "fail to get sampling rate\n");
+ goto end;
+ }
+ if (rate == 0)
+ rate = curr_rate;
+ if (rate != curr_rate)
+ stop_stream(dice, master);
+
+ if (!amdtp_stream_running(master)) {
+ stop_stream(dice, slave);
+ snd_dice_transaction_clear_enable(dice);
+
+ amdtp_stream_set_sync(sync_mode, master, slave);
+
+ err = snd_dice_transaction_set_rate(dice, rate);
+ if (err < 0) {
+ dev_err(&dice->unit->device,
+ "fail to set sampling rate\n");
+ goto end;
+ }
+
+ /* Start both streams. */
+ err = start_stream(dice, master, rate);
+ if (err < 0) {
+ dev_err(&dice->unit->device,
+ "fail to start AMDTP master stream\n");
+ goto end;
+ }
+ err = start_stream(dice, slave, rate);
+ if (err < 0) {
+ dev_err(&dice->unit->device,
+ "fail to start AMDTP slave stream\n");
+ stop_stream(dice, master);
+ goto end;
+ }
+ err = snd_dice_transaction_set_enable(dice);
+ if (err < 0) {
+ dev_err(&dice->unit->device,
+ "fail to enable interface\n");
+ stop_stream(dice, master);
+ stop_stream(dice, slave);
+ goto end;
+ }
+
+ /* Wait first callbacks */
+ if (!amdtp_stream_wait_callback(master, CALLBACK_TIMEOUT) ||
+ !amdtp_stream_wait_callback(slave, CALLBACK_TIMEOUT)) {
+ snd_dice_transaction_clear_enable(dice);
+ stop_stream(dice, master);
+ stop_stream(dice, slave);
+ err = -ETIMEDOUT;
+ }
+ }
+end:
+ return err;
+}
+
+void snd_dice_stream_stop_duplex(struct snd_dice *dice)
+{
+ if (dice->substreams_counter > 0)
+ return;
+
+ snd_dice_transaction_clear_enable(dice);
+
+ stop_stream(dice, &dice->tx_stream);
+ stop_stream(dice, &dice->rx_stream);
+}
+
+static int init_stream(struct snd_dice *dice, struct amdtp_stream *stream)
+{
+ int err;
+ struct fw_iso_resources *resources;
+ enum amdtp_stream_direction dir;
+
+ if (stream == &dice->tx_stream) {
+ resources = &dice->tx_resources;
+ dir = AMDTP_IN_STREAM;
+ } else {
+ resources = &dice->rx_resources;
+ dir = AMDTP_OUT_STREAM;
+ }
+
+ err = fw_iso_resources_init(resources, dice->unit);
+ if (err < 0)
+ goto end;
+ resources->channels_mask = 0x00000000ffffffffuLL;
+
+ err = amdtp_stream_init(stream, dice->unit, dir, CIP_BLOCKING);
+ if (err < 0) {
+ amdtp_stream_destroy(stream);
+ fw_iso_resources_destroy(resources);
+ }
+end:
+ return err;
+}
+
+static void destroy_stream(struct snd_dice *dice, struct amdtp_stream *stream)
+{
+ amdtp_stream_destroy(stream);
+
+ if (stream == &dice->tx_stream)
+ fw_iso_resources_destroy(&dice->tx_resources);
+ else
+ fw_iso_resources_destroy(&dice->rx_resources);
+}
+
+int snd_dice_stream_init_duplex(struct snd_dice *dice)
+{
+ int err;
+
+ dice->substreams_counter = 0;
+
+ err = init_stream(dice, &dice->tx_stream);
+ if (err < 0)
+ goto end;
+
+ err = init_stream(dice, &dice->rx_stream);
+end:
+ return err;
+}
+
+void snd_dice_stream_destroy_duplex(struct snd_dice *dice)
+{
+ snd_dice_transaction_clear_enable(dice);
+
+ stop_stream(dice, &dice->tx_stream);
+ destroy_stream(dice, &dice->tx_stream);
+
+ stop_stream(dice, &dice->rx_stream);
+ destroy_stream(dice, &dice->rx_stream);
+
+ dice->substreams_counter = 0;
+}
+
+void snd_dice_stream_update_duplex(struct snd_dice *dice)
+{
+ /*
+ * On a bus reset, the DICE firmware disables streaming and then goes
+ * off contemplating its own navel for hundreds of milliseconds before
+ * it can react to any of our attempts to reenable streaming. This
+ * means that we lose synchronization anyway, so we force our streams
+ * to stop so that the application can restart them in an orderly
+ * manner.
+ */
+ dice->global_enabled = false;
+
+ stop_stream(dice, &dice->rx_stream);
+ stop_stream(dice, &dice->tx_stream);
+
+ fw_iso_resources_update(&dice->rx_resources);
+ fw_iso_resources_update(&dice->tx_resources);
+}
+
+static void dice_lock_changed(struct snd_dice *dice)
+{
+ dice->dev_lock_changed = true;
+ wake_up(&dice->hwdep_wait);
+}
+
+int snd_dice_stream_lock_try(struct snd_dice *dice)
+{
+ int err;
+
+ spin_lock_irq(&dice->lock);
+
+ if (dice->dev_lock_count < 0) {
+ err = -EBUSY;
+ goto out;
+ }
+
+ if (dice->dev_lock_count++ == 0)
+ dice_lock_changed(dice);
+ err = 0;
+out:
+ spin_unlock_irq(&dice->lock);
+ return err;
+}
+
+void snd_dice_stream_lock_release(struct snd_dice *dice)
+{
+ spin_lock_irq(&dice->lock);
+
+ if (WARN_ON(dice->dev_lock_count <= 0))
+ goto out;
+
+ if (--dice->dev_lock_count == 0)
+ dice_lock_changed(dice);
+out:
+ spin_unlock_irq(&dice->lock);
+}
diff --git a/sound/firewire/dice/dice-transaction.c b/sound/firewire/dice/dice-transaction.c
new file mode 100644
index 000000000000..aee746187665
--- /dev/null
+++ b/sound/firewire/dice/dice-transaction.c
@@ -0,0 +1,382 @@
+/*
+ * dice_transaction.c - a part of driver for Dice based devices
+ *
+ * Copyright (c) Clemens Ladisch
+ * Copyright (c) 2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "dice.h"
+
+#define NOTIFICATION_TIMEOUT_MS 100
+
+static u64 get_subaddr(struct snd_dice *dice, enum snd_dice_addr_type type,
+ u64 offset)
+{
+ switch (type) {
+ case SND_DICE_ADDR_TYPE_TX:
+ offset += dice->tx_offset;
+ break;
+ case SND_DICE_ADDR_TYPE_RX:
+ offset += dice->rx_offset;
+ break;
+ case SND_DICE_ADDR_TYPE_SYNC:
+ offset += dice->sync_offset;
+ break;
+ case SND_DICE_ADDR_TYPE_RSRV:
+ offset += dice->rsrv_offset;
+ break;
+ case SND_DICE_ADDR_TYPE_GLOBAL:
+ default:
+ offset += dice->global_offset;
+ break;
+ }
+ offset += DICE_PRIVATE_SPACE;
+ return offset;
+}
+
+int snd_dice_transaction_write(struct snd_dice *dice,
+ enum snd_dice_addr_type type,
+ unsigned int offset, void *buf, unsigned int len)
+{
+ return snd_fw_transaction(dice->unit,
+ (len == 4) ? TCODE_WRITE_QUADLET_REQUEST :
+ TCODE_WRITE_BLOCK_REQUEST,
+ get_subaddr(dice, type, offset), buf, len, 0);
+}
+
+int snd_dice_transaction_read(struct snd_dice *dice,
+ enum snd_dice_addr_type type, unsigned int offset,
+ void *buf, unsigned int len)
+{
+ return snd_fw_transaction(dice->unit,
+ (len == 4) ? TCODE_READ_QUADLET_REQUEST :
+ TCODE_READ_BLOCK_REQUEST,
+ get_subaddr(dice, type, offset), buf, len, 0);
+}
+
+static unsigned int get_clock_info(struct snd_dice *dice, __be32 *info)
+{
+ return snd_dice_transaction_read_global(dice, GLOBAL_CLOCK_SELECT,
+ info, 4);
+}
+
+static int set_clock_info(struct snd_dice *dice,
+ unsigned int rate, unsigned int source)
+{
+ unsigned int retries = 3;
+ unsigned int i;
+ __be32 info;
+ u32 mask;
+ u32 clock;
+ int err;
+retry:
+ err = get_clock_info(dice, &info);
+ if (err < 0)
+ goto end;
+
+ clock = be32_to_cpu(info);
+ if (source != UINT_MAX) {
+ mask = CLOCK_SOURCE_MASK;
+ clock &= ~mask;
+ clock |= source;
+ }
+ if (rate != UINT_MAX) {
+ for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) {
+ if (snd_dice_rates[i] == rate)
+ break;
+ }
+ if (i == ARRAY_SIZE(snd_dice_rates)) {
+ err = -EINVAL;
+ goto end;
+ }
+
+ mask = CLOCK_RATE_MASK;
+ clock &= ~mask;
+ clock |= i << CLOCK_RATE_SHIFT;
+ }
+ info = cpu_to_be32(clock);
+
+ if (completion_done(&dice->clock_accepted))
+ reinit_completion(&dice->clock_accepted);
+
+ err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT,
+ &info, 4);
+ if (err < 0)
+ goto end;
+
+ /* Timeout means it's invalid request, probably bus reset occurred. */
+ if (wait_for_completion_timeout(&dice->clock_accepted,
+ msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0) {
+ if (retries-- == 0) {
+ err = -ETIMEDOUT;
+ goto end;
+ }
+
+ err = snd_dice_transaction_reinit(dice);
+ if (err < 0)
+ goto end;
+
+ msleep(500); /* arbitrary */
+ goto retry;
+ }
+end:
+ return err;
+}
+
+int snd_dice_transaction_get_clock_source(struct snd_dice *dice,
+ unsigned int *source)
+{
+ __be32 info;
+ int err;
+
+ err = get_clock_info(dice, &info);
+ if (err >= 0)
+ *source = be32_to_cpu(info) & CLOCK_SOURCE_MASK;
+
+ return err;
+}
+
+int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate)
+{
+ __be32 info;
+ unsigned int index;
+ int err;
+
+ err = get_clock_info(dice, &info);
+ if (err < 0)
+ goto end;
+
+ index = (be32_to_cpu(info) & CLOCK_RATE_MASK) >> CLOCK_RATE_SHIFT;
+ if (index >= SND_DICE_RATES_COUNT) {
+ err = -ENOSYS;
+ goto end;
+ }
+
+ *rate = snd_dice_rates[index];
+end:
+ return err;
+}
+int snd_dice_transaction_set_rate(struct snd_dice *dice, unsigned int rate)
+{
+ return set_clock_info(dice, rate, UINT_MAX);
+}
+
+int snd_dice_transaction_set_enable(struct snd_dice *dice)
+{
+ __be32 value;
+ int err = 0;
+
+ if (dice->global_enabled)
+ goto end;
+
+ value = cpu_to_be32(1);
+ err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
+ get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,
+ GLOBAL_ENABLE),
+ &value, 4,
+ FW_FIXED_GENERATION | dice->owner_generation);
+ if (err < 0)
+ goto end;
+
+ dice->global_enabled = true;
+end:
+ return err;
+}
+
+void snd_dice_transaction_clear_enable(struct snd_dice *dice)
+{
+ __be32 value;
+
+ value = 0;
+ snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
+ get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,
+ GLOBAL_ENABLE),
+ &value, 4, FW_QUIET |
+ FW_FIXED_GENERATION | dice->owner_generation);
+
+ dice->global_enabled = false;
+}
+
+static void dice_notification(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source,
+ int generation, unsigned long long offset,
+ void *data, size_t length, void *callback_data)
+{
+ struct snd_dice *dice = callback_data;
+ u32 bits;
+ unsigned long flags;
+
+ if (tcode != TCODE_WRITE_QUADLET_REQUEST) {
+ fw_send_response(card, request, RCODE_TYPE_ERROR);
+ return;
+ }
+ if ((offset & 3) != 0) {
+ fw_send_response(card, request, RCODE_ADDRESS_ERROR);
+ return;
+ }
+
+ bits = be32_to_cpup(data);
+
+ spin_lock_irqsave(&dice->lock, flags);
+ dice->notification_bits |= bits;
+ spin_unlock_irqrestore(&dice->lock, flags);
+
+ fw_send_response(card, request, RCODE_COMPLETE);
+
+ if (bits & NOTIFY_CLOCK_ACCEPTED)
+ complete(&dice->clock_accepted);
+ wake_up(&dice->hwdep_wait);
+}
+
+static int register_notification_address(struct snd_dice *dice, bool retry)
+{
+ struct fw_device *device = fw_parent_device(dice->unit);
+ __be64 *buffer;
+ unsigned int retries;
+ int err;
+
+ retries = (retry) ? 3 : 0;
+
+ buffer = kmalloc(2 * 8, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ for (;;) {
+ buffer[0] = cpu_to_be64(OWNER_NO_OWNER);
+ buffer[1] = cpu_to_be64(
+ ((u64)device->card->node_id << OWNER_NODE_SHIFT) |
+ dice->notification_handler.offset);
+
+ dice->owner_generation = device->generation;
+ smp_rmb(); /* node_id vs. generation */
+ err = snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP,
+ get_subaddr(dice,
+ SND_DICE_ADDR_TYPE_GLOBAL,
+ GLOBAL_OWNER),
+ buffer, 2 * 8,
+ FW_FIXED_GENERATION |
+ dice->owner_generation);
+ if (err == 0) {
+ /* success */
+ if (buffer[0] == cpu_to_be64(OWNER_NO_OWNER))
+ break;
+ /* The address seems to be already registered. */
+ if (buffer[0] == buffer[1])
+ break;
+
+ dev_err(&dice->unit->device,
+ "device is already in use\n");
+ err = -EBUSY;
+ }
+ if (err != -EAGAIN || retries-- > 0)
+ break;
+
+ msleep(20);
+ }
+
+ kfree(buffer);
+
+ if (err < 0)
+ dice->owner_generation = -1;
+
+ return err;
+}
+
+static void unregister_notification_address(struct snd_dice *dice)
+{
+ struct fw_device *device = fw_parent_device(dice->unit);
+ __be64 *buffer;
+
+ buffer = kmalloc(2 * 8, GFP_KERNEL);
+ if (buffer == NULL)
+ return;
+
+ buffer[0] = cpu_to_be64(
+ ((u64)device->card->node_id << OWNER_NODE_SHIFT) |
+ dice->notification_handler.offset);
+ buffer[1] = cpu_to_be64(OWNER_NO_OWNER);
+ snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP,
+ get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,
+ GLOBAL_OWNER),
+ buffer, 2 * 8, FW_QUIET |
+ FW_FIXED_GENERATION | dice->owner_generation);
+
+ kfree(buffer);
+
+ dice->owner_generation = -1;
+}
+
+void snd_dice_transaction_destroy(struct snd_dice *dice)
+{
+ struct fw_address_handler *handler = &dice->notification_handler;
+
+ if (handler->callback_data == NULL)
+ return;
+
+ unregister_notification_address(dice);
+
+ fw_core_remove_address_handler(handler);
+ handler->callback_data = NULL;
+}
+
+int snd_dice_transaction_reinit(struct snd_dice *dice)
+{
+ struct fw_address_handler *handler = &dice->notification_handler;
+
+ if (handler->callback_data == NULL)
+ return -EINVAL;
+
+ return register_notification_address(dice, false);
+}
+
+int snd_dice_transaction_init(struct snd_dice *dice)
+{
+ struct fw_address_handler *handler = &dice->notification_handler;
+ __be32 *pointers;
+ int err;
+
+ /* Use the same way which dice_interface_check() does. */
+ pointers = kmalloc(sizeof(__be32) * 10, GFP_KERNEL);
+ if (pointers == NULL)
+ return -ENOMEM;
+
+ /* Get offsets for sub-addresses */
+ err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
+ DICE_PRIVATE_SPACE,
+ pointers, sizeof(__be32) * 10, 0);
+ if (err < 0)
+ goto end;
+
+ /* Allocation callback in address space over host controller */
+ handler->length = 4;
+ handler->address_callback = dice_notification;
+ handler->callback_data = dice;
+ err = fw_core_add_address_handler(handler, &fw_high_memory_region);
+ if (err < 0) {
+ handler->callback_data = NULL;
+ goto end;
+ }
+
+ /* Register the address space */
+ err = register_notification_address(dice, true);
+ if (err < 0) {
+ fw_core_remove_address_handler(handler);
+ handler->callback_data = NULL;
+ goto end;
+ }
+
+ dice->global_offset = be32_to_cpu(pointers[0]) * 4;
+ dice->tx_offset = be32_to_cpu(pointers[2]) * 4;
+ dice->rx_offset = be32_to_cpu(pointers[4]) * 4;
+ dice->sync_offset = be32_to_cpu(pointers[6]) * 4;
+ dice->rsrv_offset = be32_to_cpu(pointers[8]) * 4;
+
+ /* Set up later. */
+ if (be32_to_cpu(pointers[1]) * 4 >= GLOBAL_CLOCK_CAPABILITIES + 4)
+ dice->clock_caps = 1;
+end:
+ kfree(pointers);
+ return err;
+}
diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c
new file mode 100644
index 000000000000..90d8f40ff727
--- /dev/null
+++ b/sound/firewire/dice/dice.c
@@ -0,0 +1,361 @@
+/*
+ * TC Applied Technologies Digital Interface Communications Engine driver
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "dice.h"
+
+MODULE_DESCRIPTION("DICE driver");
+MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
+MODULE_LICENSE("GPL v2");
+
+#define OUI_WEISS 0x001c6a
+
+#define DICE_CATEGORY_ID 0x04
+#define WEISS_CATEGORY_ID 0x00
+
+static int dice_interface_check(struct fw_unit *unit)
+{
+ static const int min_values[10] = {
+ 10, 0x64 / 4,
+ 10, 0x18 / 4,
+ 10, 0x18 / 4,
+ 0, 0,
+ 0, 0,
+ };
+ struct fw_device *device = fw_parent_device(unit);
+ struct fw_csr_iterator it;
+ int key, val, vendor = -1, model = -1, err;
+ unsigned int category, i;
+ __be32 *pointers, value;
+ __be32 version;
+
+ pointers = kmalloc_array(ARRAY_SIZE(min_values), sizeof(__be32),
+ GFP_KERNEL);
+ if (pointers == NULL)
+ return -ENOMEM;
+
+ /*
+ * Check that GUID and unit directory are constructed according to DICE
+ * rules, i.e., that the specifier ID is the GUID's OUI, and that the
+ * GUID chip ID consists of the 8-bit category ID, the 10-bit product
+ * ID, and a 22-bit serial number.
+ */
+ fw_csr_iterator_init(&it, unit->directory);
+ while (fw_csr_iterator_next(&it, &key, &val)) {
+ switch (key) {
+ case CSR_SPECIFIER_ID:
+ vendor = val;
+ break;
+ case CSR_MODEL:
+ model = val;
+ break;
+ }
+ }
+ if (vendor == OUI_WEISS)
+ category = WEISS_CATEGORY_ID;
+ else
+ category = DICE_CATEGORY_ID;
+ if (device->config_rom[3] != ((vendor << 8) | category) ||
+ device->config_rom[4] >> 22 != model) {
+ err = -ENODEV;
+ goto end;
+ }
+
+ /*
+ * Check that the sub address spaces exist and are located inside the
+ * private address space. The minimum values are chosen so that all
+ * minimally required registers are included.
+ */
+ err = snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST,
+ DICE_PRIVATE_SPACE, pointers,
+ sizeof(__be32) * ARRAY_SIZE(min_values), 0);
+ if (err < 0) {
+ err = -ENODEV;
+ goto end;
+ }
+ for (i = 0; i < ARRAY_SIZE(min_values); ++i) {
+ value = be32_to_cpu(pointers[i]);
+ if (value < min_values[i] || value >= 0x40000) {
+ err = -ENODEV;
+ goto end;
+ }
+ }
+
+ /*
+ * Check that the implemented DICE driver specification major version
+ * number matches.
+ */
+ err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
+ DICE_PRIVATE_SPACE +
+ be32_to_cpu(pointers[0]) * 4 + GLOBAL_VERSION,
+ &version, 4, 0);
+ if (err < 0) {
+ err = -ENODEV;
+ goto end;
+ }
+ if ((version & cpu_to_be32(0xff000000)) != cpu_to_be32(0x01000000)) {
+ dev_err(&unit->device,
+ "unknown DICE version: 0x%08x\n", be32_to_cpu(version));
+ err = -ENODEV;
+ goto end;
+ }
+end:
+ return err;
+}
+
+static int highest_supported_mode_rate(struct snd_dice *dice,
+ unsigned int mode, unsigned int *rate)
+{
+ unsigned int i, m;
+
+ for (i = ARRAY_SIZE(snd_dice_rates); i > 0; i--) {
+ *rate = snd_dice_rates[i - 1];
+ if (snd_dice_stream_get_rate_mode(dice, *rate, &m) < 0)
+ continue;
+ if (mode == m)
+ break;
+ }
+ if (i == 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int dice_read_mode_params(struct snd_dice *dice, unsigned int mode)
+{
+ __be32 values[2];
+ unsigned int rate;
+ int err;
+
+ if (highest_supported_mode_rate(dice, mode, &rate) < 0) {
+ dice->tx_channels[mode] = 0;
+ dice->tx_midi_ports[mode] = 0;
+ dice->rx_channels[mode] = 0;
+ dice->rx_midi_ports[mode] = 0;
+ return 0;
+ }
+
+ err = snd_dice_transaction_set_rate(dice, rate);
+ if (err < 0)
+ return err;
+
+ err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO,
+ values, sizeof(values));
+ if (err < 0)
+ return err;
+
+ dice->tx_channels[mode] = be32_to_cpu(values[0]);
+ dice->tx_midi_ports[mode] = be32_to_cpu(values[1]);
+
+ err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO,
+ values, sizeof(values));
+ if (err < 0)
+ return err;
+
+ dice->rx_channels[mode] = be32_to_cpu(values[0]);
+ dice->rx_midi_ports[mode] = be32_to_cpu(values[1]);
+
+ return 0;
+}
+
+static int dice_read_params(struct snd_dice *dice)
+{
+ __be32 value;
+ int mode, err;
+
+ /* some very old firmwares don't tell about their clock support */
+ if (dice->clock_caps > 0) {
+ err = snd_dice_transaction_read_global(dice,
+ GLOBAL_CLOCK_CAPABILITIES,
+ &value, 4);
+ if (err < 0)
+ return err;
+ dice->clock_caps = be32_to_cpu(value);
+ } else {
+ /* this should be supported by any device */
+ dice->clock_caps = CLOCK_CAP_RATE_44100 |
+ CLOCK_CAP_RATE_48000 |
+ CLOCK_CAP_SOURCE_ARX1 |
+ CLOCK_CAP_SOURCE_INTERNAL;
+ }
+
+ for (mode = 2; mode >= 0; --mode) {
+ err = dice_read_mode_params(dice, mode);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static void dice_card_strings(struct snd_dice *dice)
+{
+ struct snd_card *card = dice->card;
+ struct fw_device *dev = fw_parent_device(dice->unit);
+ char vendor[32], model[32];
+ unsigned int i;
+ int err;
+
+ strcpy(card->driver, "DICE");
+
+ strcpy(card->shortname, "DICE");
+ BUILD_BUG_ON(NICK_NAME_SIZE < sizeof(card->shortname));
+ err = snd_dice_transaction_read_global(dice, GLOBAL_NICK_NAME,
+ card->shortname,
+ sizeof(card->shortname));
+ if (err >= 0) {
+ /* DICE strings are returned in "always-wrong" endianness */
+ BUILD_BUG_ON(sizeof(card->shortname) % 4 != 0);
+ for (i = 0; i < sizeof(card->shortname); i += 4)
+ swab32s((u32 *)&card->shortname[i]);
+ card->shortname[sizeof(card->shortname) - 1] = '\0';
+ }
+
+ strcpy(vendor, "?");
+ fw_csr_string(dev->config_rom + 5, CSR_VENDOR, vendor, sizeof(vendor));
+ strcpy(model, "?");
+ fw_csr_string(dice->unit->directory, CSR_MODEL, model, sizeof(model));
+ snprintf(card->longname, sizeof(card->longname),
+ "%s %s (serial %u) at %s, S%d",
+ vendor, model, dev->config_rom[4] & 0x3fffff,
+ dev_name(&dice->unit->device), 100 << dev->max_speed);
+
+ strcpy(card->mixername, "DICE");
+}
+
+static void dice_card_free(struct snd_card *card)
+{
+ struct snd_dice *dice = card->private_data;
+
+ snd_dice_transaction_destroy(dice);
+ mutex_destroy(&dice->mutex);
+}
+
+static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
+{
+ struct snd_card *card;
+ struct snd_dice *dice;
+ int err;
+
+ err = dice_interface_check(unit);
+ if (err < 0)
+ goto end;
+
+ err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
+ sizeof(*dice), &card);
+ if (err < 0)
+ goto end;
+
+ dice = card->private_data;
+ dice->card = card;
+ dice->unit = unit;
+ card->private_free = dice_card_free;
+
+ spin_lock_init(&dice->lock);
+ mutex_init(&dice->mutex);
+ init_completion(&dice->clock_accepted);
+ init_waitqueue_head(&dice->hwdep_wait);
+
+ err = snd_dice_transaction_init(dice);
+ if (err < 0)
+ goto error;
+
+ err = dice_read_params(dice);
+ if (err < 0)
+ goto error;
+
+ dice_card_strings(dice);
+
+ err = snd_dice_create_pcm(dice);
+ if (err < 0)
+ goto error;
+
+ err = snd_dice_create_hwdep(dice);
+ if (err < 0)
+ goto error;
+
+ snd_dice_create_proc(dice);
+
+ err = snd_dice_create_midi(dice);
+ if (err < 0)
+ goto error;
+
+ err = snd_dice_stream_init_duplex(dice);
+ if (err < 0)
+ goto error;
+
+ err = snd_card_register(card);
+ if (err < 0) {
+ snd_dice_stream_destroy_duplex(dice);
+ goto error;
+ }
+
+ dev_set_drvdata(&unit->device, dice);
+end:
+ return err;
+error:
+ snd_card_free(card);
+ return err;
+}
+
+static void dice_remove(struct fw_unit *unit)
+{
+ struct snd_dice *dice = dev_get_drvdata(&unit->device);
+
+ snd_card_disconnect(dice->card);
+
+ snd_dice_stream_destroy_duplex(dice);
+
+ snd_card_free_when_closed(dice->card);
+}
+
+static void dice_bus_reset(struct fw_unit *unit)
+{
+ struct snd_dice *dice = dev_get_drvdata(&unit->device);
+
+ /* The handler address register becomes initialized. */
+ snd_dice_transaction_reinit(dice);
+
+ mutex_lock(&dice->mutex);
+ snd_dice_stream_update_duplex(dice);
+ mutex_unlock(&dice->mutex);
+}
+
+#define DICE_INTERFACE 0x000001
+
+static const struct ieee1394_device_id dice_id_table[] = {
+ {
+ .match_flags = IEEE1394_MATCH_VERSION,
+ .version = DICE_INTERFACE,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(ieee1394, dice_id_table);
+
+static struct fw_driver dice_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = KBUILD_MODNAME,
+ .bus = &fw_bus_type,
+ },
+ .probe = dice_probe,
+ .update = dice_bus_reset,
+ .remove = dice_remove,
+ .id_table = dice_id_table,
+};
+
+static int __init alsa_dice_init(void)
+{
+ return driver_register(&dice_driver.driver);
+}
+
+static void __exit alsa_dice_exit(void)
+{
+ driver_unregister(&dice_driver.driver);
+}
+
+module_init(alsa_dice_init);
+module_exit(alsa_dice_exit);
diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h
new file mode 100644
index 000000000000..ecf5dc862235
--- /dev/null
+++ b/sound/firewire/dice/dice.h
@@ -0,0 +1,189 @@
+/*
+ * dice.h - a part of driver for Dice based devices
+ *
+ * Copyright (c) Clemens Ladisch
+ * Copyright (c) 2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#ifndef SOUND_DICE_H_INCLUDED
+#define SOUND_DICE_H_INCLUDED
+
+#include <linux/compat.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/firewire.h>
+#include <sound/hwdep.h>
+#include <sound/info.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/rawmidi.h>
+
+#include "../amdtp.h"
+#include "../iso-resources.h"
+#include "../lib.h"
+#include "dice-interface.h"
+
+struct snd_dice {
+ struct snd_card *card;
+ struct fw_unit *unit;
+ spinlock_t lock;
+ struct mutex mutex;
+
+ /* Offsets for sub-addresses */
+ unsigned int global_offset;
+ unsigned int rx_offset;
+ unsigned int tx_offset;
+ unsigned int sync_offset;
+ unsigned int rsrv_offset;
+
+ unsigned int clock_caps;
+ unsigned int tx_channels[3];
+ unsigned int rx_channels[3];
+ unsigned int tx_midi_ports[3];
+ unsigned int rx_midi_ports[3];
+
+ struct fw_address_handler notification_handler;
+ int owner_generation;
+ u32 notification_bits;
+
+ /* For uapi */
+ int dev_lock_count; /* > 0 driver, < 0 userspace */
+ bool dev_lock_changed;
+ wait_queue_head_t hwdep_wait;
+
+ /* For streaming */
+ struct fw_iso_resources tx_resources;
+ struct fw_iso_resources rx_resources;
+ struct amdtp_stream tx_stream;
+ struct amdtp_stream rx_stream;
+ bool global_enabled;
+ struct completion clock_accepted;
+ unsigned int substreams_counter;
+};
+
+enum snd_dice_addr_type {
+ SND_DICE_ADDR_TYPE_PRIVATE,
+ SND_DICE_ADDR_TYPE_GLOBAL,
+ SND_DICE_ADDR_TYPE_TX,
+ SND_DICE_ADDR_TYPE_RX,
+ SND_DICE_ADDR_TYPE_SYNC,
+ SND_DICE_ADDR_TYPE_RSRV,
+};
+
+int snd_dice_transaction_write(struct snd_dice *dice,
+ enum snd_dice_addr_type type,
+ unsigned int offset,
+ void *buf, unsigned int len);
+int snd_dice_transaction_read(struct snd_dice *dice,
+ enum snd_dice_addr_type type, unsigned int offset,
+ void *buf, unsigned int len);
+
+static inline int snd_dice_transaction_write_global(struct snd_dice *dice,
+ unsigned int offset,
+ void *buf, unsigned int len)
+{
+ return snd_dice_transaction_write(dice,
+ SND_DICE_ADDR_TYPE_GLOBAL, offset,
+ buf, len);
+}
+static inline int snd_dice_transaction_read_global(struct snd_dice *dice,
+ unsigned int offset,
+ void *buf, unsigned int len)
+{
+ return snd_dice_transaction_read(dice,
+ SND_DICE_ADDR_TYPE_GLOBAL, offset,
+ buf, len);
+}
+static inline int snd_dice_transaction_write_tx(struct snd_dice *dice,
+ unsigned int offset,
+ void *buf, unsigned int len)
+{
+ return snd_dice_transaction_write(dice, SND_DICE_ADDR_TYPE_TX, offset,
+ buf, len);
+}
+static inline int snd_dice_transaction_read_tx(struct snd_dice *dice,
+ unsigned int offset,
+ void *buf, unsigned int len)
+{
+ return snd_dice_transaction_read(dice, SND_DICE_ADDR_TYPE_TX, offset,
+ buf, len);
+}
+static inline int snd_dice_transaction_write_rx(struct snd_dice *dice,
+ unsigned int offset,
+ void *buf, unsigned int len)
+{
+ return snd_dice_transaction_write(dice, SND_DICE_ADDR_TYPE_RX, offset,
+ buf, len);
+}
+static inline int snd_dice_transaction_read_rx(struct snd_dice *dice,
+ unsigned int offset,
+ void *buf, unsigned int len)
+{
+ return snd_dice_transaction_read(dice, SND_DICE_ADDR_TYPE_RX, offset,
+ buf, len);
+}
+static inline int snd_dice_transaction_write_sync(struct snd_dice *dice,
+ unsigned int offset,
+ void *buf, unsigned int len)
+{
+ return snd_dice_transaction_write(dice, SND_DICE_ADDR_TYPE_SYNC, offset,
+ buf, len);
+}
+static inline int snd_dice_transaction_read_sync(struct snd_dice *dice,
+ unsigned int offset,
+ void *buf, unsigned int len)
+{
+ return snd_dice_transaction_read(dice, SND_DICE_ADDR_TYPE_SYNC, offset,
+ buf, len);
+}
+
+int snd_dice_transaction_get_clock_source(struct snd_dice *dice,
+ unsigned int *source);
+int snd_dice_transaction_set_rate(struct snd_dice *dice, unsigned int rate);
+int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate);
+int snd_dice_transaction_set_enable(struct snd_dice *dice);
+void snd_dice_transaction_clear_enable(struct snd_dice *dice);
+int snd_dice_transaction_init(struct snd_dice *dice);
+int snd_dice_transaction_reinit(struct snd_dice *dice);
+void snd_dice_transaction_destroy(struct snd_dice *dice);
+
+#define SND_DICE_RATES_COUNT 7
+extern const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT];
+
+int snd_dice_stream_get_rate_mode(struct snd_dice *dice,
+ unsigned int rate, unsigned int *mode);
+
+int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate);
+void snd_dice_stream_stop_duplex(struct snd_dice *dice);
+int snd_dice_stream_init_duplex(struct snd_dice *dice);
+void snd_dice_stream_destroy_duplex(struct snd_dice *dice);
+void snd_dice_stream_update_duplex(struct snd_dice *dice);
+
+int snd_dice_stream_lock_try(struct snd_dice *dice);
+void snd_dice_stream_lock_release(struct snd_dice *dice);
+
+int snd_dice_create_pcm(struct snd_dice *dice);
+
+int snd_dice_create_hwdep(struct snd_dice *dice);
+
+void snd_dice_create_proc(struct snd_dice *dice);
+
+int snd_dice_create_midi(struct snd_dice *dice);
+
+#endif
diff --git a/sound/firewire/isight.c b/sound/firewire/isight.c
index 7ac94439e758..48d6dca471c6 100644
--- a/sound/firewire/isight.c
+++ b/sound/firewire/isight.c
@@ -131,14 +131,8 @@ static void isight_samples(struct isight *isight,
static void isight_pcm_abort(struct isight *isight)
{
- unsigned long flags;
-
- if (ACCESS_ONCE(isight->pcm_active)) {
- snd_pcm_stream_lock_irqsave(isight->pcm, flags);
- if (snd_pcm_running(isight->pcm))
- snd_pcm_stop(isight->pcm, SNDRV_PCM_STATE_XRUN);
- snd_pcm_stream_unlock_irqrestore(isight->pcm, flags);
- }
+ if (ACCESS_ONCE(isight->pcm_active))
+ snd_pcm_stop_xrun(isight->pcm);
}
static void isight_dropped_samples(struct isight *isight, unsigned int total)
diff --git a/sound/firewire/oxfw/Makefile b/sound/firewire/oxfw/Makefile
new file mode 100644
index 000000000000..a926850864f6
--- /dev/null
+++ b/sound/firewire/oxfw/Makefile
@@ -0,0 +1,3 @@
+snd-oxfw-objs := oxfw-command.o oxfw-stream.o oxfw-control.o oxfw-pcm.o \
+ oxfw-proc.o oxfw-midi.o oxfw-hwdep.o oxfw.o
+obj-m += snd-oxfw.o
diff --git a/sound/firewire/oxfw/oxfw-command.c b/sound/firewire/oxfw/oxfw-command.c
new file mode 100644
index 000000000000..12ef3253bc89
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw-command.c
@@ -0,0 +1,153 @@
+/*
+ * oxfw_command.c - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) 2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "oxfw.h"
+
+int avc_stream_set_format(struct fw_unit *unit, enum avc_general_plug_dir dir,
+ unsigned int pid, u8 *format, unsigned int len)
+{
+ u8 *buf;
+ int err;
+
+ buf = kmalloc(len + 10, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ buf[0] = 0x00; /* CONTROL */
+ buf[1] = 0xff; /* UNIT */
+ buf[2] = 0xbf; /* EXTENDED STREAM FORMAT INFORMATION */
+ buf[3] = 0xc0; /* SINGLE subfunction */
+ buf[4] = dir; /* Plug Direction */
+ buf[5] = 0x00; /* UNIT */
+ buf[6] = 0x00; /* PCR (Isochronous Plug) */
+ buf[7] = 0xff & pid; /* Plug ID */
+ buf[8] = 0xff; /* Padding */
+ buf[9] = 0xff; /* Support status in response */
+ memcpy(buf + 10, format, len);
+
+ /* do transaction and check buf[1-8] are the same against command */
+ err = fcp_avc_transaction(unit, buf, len + 10, buf, len + 10,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+ BIT(6) | BIT(7) | BIT(8));
+ if ((err > 0) && (err < len + 10))
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ else
+ err = 0;
+
+ kfree(buf);
+
+ return err;
+}
+
+int avc_stream_get_format(struct fw_unit *unit,
+ enum avc_general_plug_dir dir, unsigned int pid,
+ u8 *buf, unsigned int *len, unsigned int eid)
+{
+ unsigned int subfunc;
+ int err;
+
+ if (eid == 0xff)
+ subfunc = 0xc0; /* SINGLE */
+ else
+ subfunc = 0xc1; /* LIST */
+
+ buf[0] = 0x01; /* STATUS */
+ buf[1] = 0xff; /* UNIT */
+ buf[2] = 0xbf; /* EXTENDED STREAM FORMAT INFORMATION */
+ buf[3] = subfunc; /* SINGLE or LIST */
+ buf[4] = dir; /* Plug Direction */
+ buf[5] = 0x00; /* Unit */
+ buf[6] = 0x00; /* PCR (Isochronous Plug) */
+ buf[7] = 0xff & pid; /* Plug ID */
+ buf[8] = 0xff; /* Padding */
+ buf[9] = 0xff; /* support status in response */
+ buf[10] = 0xff & eid; /* entry ID for LIST subfunction */
+ buf[11] = 0xff; /* padding */
+
+ /* do transaction and check buf[1-7] are the same against command */
+ err = fcp_avc_transaction(unit, buf, 12, buf, *len,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+ BIT(6) | BIT(7));
+ if ((err > 0) && (err < 10))
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ else if (buf[0] == 0x0b) /* IN TRANSITION */
+ err = -EAGAIN;
+ /* LIST subfunction has entry ID */
+ else if ((subfunc == 0xc1) && (buf[10] != eid))
+ err = -EIO;
+ if (err < 0)
+ goto end;
+
+ /* keep just stream format information */
+ if (subfunc == 0xc0) {
+ memmove(buf, buf + 10, err - 10);
+ *len = err - 10;
+ } else {
+ memmove(buf, buf + 11, err - 11);
+ *len = err - 11;
+ }
+
+ err = 0;
+end:
+ return err;
+}
+
+int avc_general_inquiry_sig_fmt(struct fw_unit *unit, unsigned int rate,
+ enum avc_general_plug_dir dir,
+ unsigned short pid)
+{
+ unsigned int sfc;
+ u8 *buf;
+ int err;
+
+ for (sfc = 0; sfc < CIP_SFC_COUNT; sfc++) {
+ if (amdtp_rate_table[sfc] == rate)
+ break;
+ }
+ if (sfc == CIP_SFC_COUNT)
+ return -EINVAL;
+
+ buf = kzalloc(8, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ buf[0] = 0x02; /* SPECIFIC INQUIRY */
+ buf[1] = 0xff; /* UNIT */
+ if (dir == AVC_GENERAL_PLUG_DIR_IN)
+ buf[2] = 0x19; /* INPUT PLUG SIGNAL FORMAT */
+ else
+ buf[2] = 0x18; /* OUTPUT PLUG SIGNAL FORMAT */
+ buf[3] = 0xff & pid; /* plug id */
+ buf[4] = 0x90; /* EOH_1, Form_1, FMT. AM824 */
+ buf[5] = 0x07 & sfc; /* FDF-hi. AM824, frequency */
+ buf[6] = 0xff; /* FDF-mid. AM824, SYT hi (not used) */
+ buf[7] = 0xff; /* FDF-low. AM824, SYT lo (not used) */
+
+ /* do transaction and check buf[1-5] are the same against command */
+ err = fcp_avc_transaction(unit, buf, 8, buf, 8,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5));
+ if ((err > 0) && (err < 8))
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENOSYS;
+ if (err < 0)
+ goto end;
+
+ err = 0;
+end:
+ kfree(buf);
+ return err;
+}
diff --git a/sound/firewire/oxfw/oxfw-control.c b/sound/firewire/oxfw/oxfw-control.c
new file mode 100644
index 000000000000..02a1cb90f20d
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw-control.c
@@ -0,0 +1,283 @@
+/*
+ * oxfw_stream.c - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "oxfw.h"
+
+enum control_action { CTL_READ, CTL_WRITE };
+enum control_attribute {
+ CTL_MIN = 0x02,
+ CTL_MAX = 0x03,
+ CTL_CURRENT = 0x10,
+};
+
+static int oxfw_mute_command(struct snd_oxfw *oxfw, bool *value,
+ enum control_action action)
+{
+ u8 *buf;
+ u8 response_ok;
+ int err;
+
+ buf = kmalloc(11, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ if (action == CTL_READ) {
+ buf[0] = 0x01; /* AV/C, STATUS */
+ response_ok = 0x0c; /* STABLE */
+ } else {
+ buf[0] = 0x00; /* AV/C, CONTROL */
+ response_ok = 0x09; /* ACCEPTED */
+ }
+ buf[1] = 0x08; /* audio unit 0 */
+ buf[2] = 0xb8; /* FUNCTION BLOCK */
+ buf[3] = 0x81; /* function block type: feature */
+ buf[4] = oxfw->device_info->mute_fb_id; /* function block ID */
+ buf[5] = 0x10; /* control attribute: current */
+ buf[6] = 0x02; /* selector length */
+ buf[7] = 0x00; /* audio channel number */
+ buf[8] = 0x01; /* control selector: mute */
+ buf[9] = 0x01; /* control data length */
+ if (action == CTL_READ)
+ buf[10] = 0xff;
+ else
+ buf[10] = *value ? 0x70 : 0x60;
+
+ err = fcp_avc_transaction(oxfw->unit, buf, 11, buf, 11, 0x3fe);
+ if (err < 0)
+ goto error;
+ if (err < 11) {
+ dev_err(&oxfw->unit->device, "short FCP response\n");
+ err = -EIO;
+ goto error;
+ }
+ if (buf[0] != response_ok) {
+ dev_err(&oxfw->unit->device, "mute command failed\n");
+ err = -EIO;
+ goto error;
+ }
+ if (action == CTL_READ)
+ *value = buf[10] == 0x70;
+
+ err = 0;
+
+error:
+ kfree(buf);
+
+ return err;
+}
+
+static int oxfw_volume_command(struct snd_oxfw *oxfw, s16 *value,
+ unsigned int channel,
+ enum control_attribute attribute,
+ enum control_action action)
+{
+ u8 *buf;
+ u8 response_ok;
+ int err;
+
+ buf = kmalloc(12, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ if (action == CTL_READ) {
+ buf[0] = 0x01; /* AV/C, STATUS */
+ response_ok = 0x0c; /* STABLE */
+ } else {
+ buf[0] = 0x00; /* AV/C, CONTROL */
+ response_ok = 0x09; /* ACCEPTED */
+ }
+ buf[1] = 0x08; /* audio unit 0 */
+ buf[2] = 0xb8; /* FUNCTION BLOCK */
+ buf[3] = 0x81; /* function block type: feature */
+ buf[4] = oxfw->device_info->volume_fb_id; /* function block ID */
+ buf[5] = attribute; /* control attribute */
+ buf[6] = 0x02; /* selector length */
+ buf[7] = channel; /* audio channel number */
+ buf[8] = 0x02; /* control selector: volume */
+ buf[9] = 0x02; /* control data length */
+ if (action == CTL_READ) {
+ buf[10] = 0xff;
+ buf[11] = 0xff;
+ } else {
+ buf[10] = *value >> 8;
+ buf[11] = *value;
+ }
+
+ err = fcp_avc_transaction(oxfw->unit, buf, 12, buf, 12, 0x3fe);
+ if (err < 0)
+ goto error;
+ if (err < 12) {
+ dev_err(&oxfw->unit->device, "short FCP response\n");
+ err = -EIO;
+ goto error;
+ }
+ if (buf[0] != response_ok) {
+ dev_err(&oxfw->unit->device, "volume command failed\n");
+ err = -EIO;
+ goto error;
+ }
+ if (action == CTL_READ)
+ *value = (buf[10] << 8) | buf[11];
+
+ err = 0;
+
+error:
+ kfree(buf);
+
+ return err;
+}
+
+static int oxfw_mute_get(struct snd_kcontrol *control,
+ struct snd_ctl_elem_value *value)
+{
+ struct snd_oxfw *oxfw = control->private_data;
+
+ value->value.integer.value[0] = !oxfw->mute;
+
+ return 0;
+}
+
+static int oxfw_mute_put(struct snd_kcontrol *control,
+ struct snd_ctl_elem_value *value)
+{
+ struct snd_oxfw *oxfw = control->private_data;
+ bool mute;
+ int err;
+
+ mute = !value->value.integer.value[0];
+
+ if (mute == oxfw->mute)
+ return 0;
+
+ err = oxfw_mute_command(oxfw, &mute, CTL_WRITE);
+ if (err < 0)
+ return err;
+ oxfw->mute = mute;
+
+ return 1;
+}
+
+static int oxfw_volume_info(struct snd_kcontrol *control,
+ struct snd_ctl_elem_info *info)
+{
+ struct snd_oxfw *oxfw = control->private_data;
+
+ info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ info->count = oxfw->device_info->mixer_channels;
+ info->value.integer.min = oxfw->volume_min;
+ info->value.integer.max = oxfw->volume_max;
+
+ return 0;
+}
+
+static const u8 channel_map[6] = { 0, 1, 4, 5, 2, 3 };
+
+static int oxfw_volume_get(struct snd_kcontrol *control,
+ struct snd_ctl_elem_value *value)
+{
+ struct snd_oxfw *oxfw = control->private_data;
+ unsigned int i;
+
+ for (i = 0; i < oxfw->device_info->mixer_channels; ++i)
+ value->value.integer.value[channel_map[i]] = oxfw->volume[i];
+
+ return 0;
+}
+
+static int oxfw_volume_put(struct snd_kcontrol *control,
+ struct snd_ctl_elem_value *value)
+{
+ struct snd_oxfw *oxfw = control->private_data;
+ unsigned int i, changed_channels;
+ bool equal_values = true;
+ s16 volume;
+ int err;
+
+ for (i = 0; i < oxfw->device_info->mixer_channels; ++i) {
+ if (value->value.integer.value[i] < oxfw->volume_min ||
+ value->value.integer.value[i] > oxfw->volume_max)
+ return -EINVAL;
+ if (value->value.integer.value[i] !=
+ value->value.integer.value[0])
+ equal_values = false;
+ }
+
+ changed_channels = 0;
+ for (i = 0; i < oxfw->device_info->mixer_channels; ++i)
+ if (value->value.integer.value[channel_map[i]] !=
+ oxfw->volume[i])
+ changed_channels |= 1 << (i + 1);
+
+ if (equal_values && changed_channels != 0)
+ changed_channels = 1 << 0;
+
+ for (i = 0; i <= oxfw->device_info->mixer_channels; ++i) {
+ volume = value->value.integer.value[channel_map[i ? i - 1 : 0]];
+ if (changed_channels & (1 << i)) {
+ err = oxfw_volume_command(oxfw, &volume, i,
+ CTL_CURRENT, CTL_WRITE);
+ if (err < 0)
+ return err;
+ }
+ if (i > 0)
+ oxfw->volume[i - 1] = volume;
+ }
+
+ return changed_channels != 0;
+}
+
+int snd_oxfw_create_mixer(struct snd_oxfw *oxfw)
+{
+ static const struct snd_kcontrol_new controls[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "PCM Playback Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = oxfw_mute_get,
+ .put = oxfw_mute_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "PCM Playback Volume",
+ .info = oxfw_volume_info,
+ .get = oxfw_volume_get,
+ .put = oxfw_volume_put,
+ },
+ };
+ unsigned int i, first_ch;
+ int err;
+
+ err = oxfw_volume_command(oxfw, &oxfw->volume_min,
+ 0, CTL_MIN, CTL_READ);
+ if (err < 0)
+ return err;
+ err = oxfw_volume_command(oxfw, &oxfw->volume_max,
+ 0, CTL_MAX, CTL_READ);
+ if (err < 0)
+ return err;
+
+ err = oxfw_mute_command(oxfw, &oxfw->mute, CTL_READ);
+ if (err < 0)
+ return err;
+
+ first_ch = oxfw->device_info->mixer_channels == 1 ? 0 : 1;
+ for (i = 0; i < oxfw->device_info->mixer_channels; ++i) {
+ err = oxfw_volume_command(oxfw, &oxfw->volume[i],
+ first_ch + i, CTL_CURRENT, CTL_READ);
+ if (err < 0)
+ return err;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(controls); ++i) {
+ err = snd_ctl_add(oxfw->card,
+ snd_ctl_new1(&controls[i], oxfw));
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
diff --git a/sound/firewire/oxfw/oxfw-hwdep.c b/sound/firewire/oxfw/oxfw-hwdep.c
new file mode 100644
index 000000000000..ff2687ad0460
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw-hwdep.c
@@ -0,0 +1,190 @@
+/*
+ * oxfw_hwdep.c - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) 2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+/*
+ * This codes give three functionality.
+ *
+ * 1.get firewire node information
+ * 2.get notification about starting/stopping stream
+ * 3.lock/unlock stream
+ */
+
+#include "oxfw.h"
+
+static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
+ loff_t *offset)
+{
+ struct snd_oxfw *oxfw = hwdep->private_data;
+ DEFINE_WAIT(wait);
+ union snd_firewire_event event;
+
+ spin_lock_irq(&oxfw->lock);
+
+ while (!oxfw->dev_lock_changed) {
+ prepare_to_wait(&oxfw->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+ spin_unlock_irq(&oxfw->lock);
+ schedule();
+ finish_wait(&oxfw->hwdep_wait, &wait);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ spin_lock_irq(&oxfw->lock);
+ }
+
+ memset(&event, 0, sizeof(event));
+ if (oxfw->dev_lock_changed) {
+ event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
+ event.lock_status.status = (oxfw->dev_lock_count > 0);
+ oxfw->dev_lock_changed = false;
+
+ count = min_t(long, count, sizeof(event.lock_status));
+ }
+
+ spin_unlock_irq(&oxfw->lock);
+
+ if (copy_to_user(buf, &event, count))
+ return -EFAULT;
+
+ return count;
+}
+
+static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
+ poll_table *wait)
+{
+ struct snd_oxfw *oxfw = hwdep->private_data;
+ unsigned int events;
+
+ poll_wait(file, &oxfw->hwdep_wait, wait);
+
+ spin_lock_irq(&oxfw->lock);
+ if (oxfw->dev_lock_changed)
+ events = POLLIN | POLLRDNORM;
+ else
+ events = 0;
+ spin_unlock_irq(&oxfw->lock);
+
+ return events;
+}
+
+static int hwdep_get_info(struct snd_oxfw *oxfw, void __user *arg)
+{
+ struct fw_device *dev = fw_parent_device(oxfw->unit);
+ struct snd_firewire_get_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.type = SNDRV_FIREWIRE_TYPE_OXFW;
+ info.card = dev->card->index;
+ *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+ *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+ strlcpy(info.device_name, dev_name(&dev->device),
+ sizeof(info.device_name));
+
+ if (copy_to_user(arg, &info, sizeof(info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int hwdep_lock(struct snd_oxfw *oxfw)
+{
+ int err;
+
+ spin_lock_irq(&oxfw->lock);
+
+ if (oxfw->dev_lock_count == 0) {
+ oxfw->dev_lock_count = -1;
+ err = 0;
+ } else {
+ err = -EBUSY;
+ }
+
+ spin_unlock_irq(&oxfw->lock);
+
+ return err;
+}
+
+static int hwdep_unlock(struct snd_oxfw *oxfw)
+{
+ int err;
+
+ spin_lock_irq(&oxfw->lock);
+
+ if (oxfw->dev_lock_count == -1) {
+ oxfw->dev_lock_count = 0;
+ err = 0;
+ } else {
+ err = -EBADFD;
+ }
+
+ spin_unlock_irq(&oxfw->lock);
+
+ return err;
+}
+
+static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+ struct snd_oxfw *oxfw = hwdep->private_data;
+
+ spin_lock_irq(&oxfw->lock);
+ if (oxfw->dev_lock_count == -1)
+ oxfw->dev_lock_count = 0;
+ spin_unlock_irq(&oxfw->lock);
+
+ return 0;
+}
+
+static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct snd_oxfw *oxfw = hwdep->private_data;
+
+ switch (cmd) {
+ case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+ return hwdep_get_info(oxfw, (void __user *)arg);
+ case SNDRV_FIREWIRE_IOCTL_LOCK:
+ return hwdep_lock(oxfw);
+ case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+ return hwdep_unlock(oxfw);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return hwdep_ioctl(hwdep, file, cmd,
+ (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+int snd_oxfw_create_hwdep(struct snd_oxfw *oxfw)
+{
+ static const struct snd_hwdep_ops hwdep_ops = {
+ .read = hwdep_read,
+ .release = hwdep_release,
+ .poll = hwdep_poll,
+ .ioctl = hwdep_ioctl,
+ .ioctl_compat = hwdep_compat_ioctl,
+ };
+ struct snd_hwdep *hwdep;
+ int err;
+
+ err = snd_hwdep_new(oxfw->card, oxfw->card->driver, 0, &hwdep);
+ if (err < 0)
+ goto end;
+ strcpy(hwdep->name, oxfw->card->driver);
+ hwdep->iface = SNDRV_HWDEP_IFACE_FW_OXFW;
+ hwdep->ops = hwdep_ops;
+ hwdep->private_data = oxfw;
+ hwdep->exclusive = true;
+end:
+ return err;
+}
diff --git a/sound/firewire/oxfw/oxfw-midi.c b/sound/firewire/oxfw/oxfw-midi.c
new file mode 100644
index 000000000000..540a30338516
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw-midi.c
@@ -0,0 +1,207 @@
+/*
+ * oxfw_midi.c - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) 2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "oxfw.h"
+
+static int midi_capture_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->rmidi->private_data;
+ int err;
+
+ err = snd_oxfw_stream_lock_try(oxfw);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&oxfw->mutex);
+
+ oxfw->capture_substreams++;
+ err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->tx_stream, 0, 0);
+
+ mutex_unlock(&oxfw->mutex);
+
+ if (err < 0)
+ snd_oxfw_stream_lock_release(oxfw);
+
+ return err;
+}
+
+static int midi_playback_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->rmidi->private_data;
+ int err;
+
+ err = snd_oxfw_stream_lock_try(oxfw);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&oxfw->mutex);
+
+ oxfw->playback_substreams++;
+ err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->rx_stream, 0, 0);
+
+ mutex_unlock(&oxfw->mutex);
+
+ if (err < 0)
+ snd_oxfw_stream_lock_release(oxfw);
+
+ return err;
+}
+
+static int midi_capture_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->rmidi->private_data;
+
+ mutex_lock(&oxfw->mutex);
+
+ oxfw->capture_substreams--;
+ snd_oxfw_stream_stop_simplex(oxfw, &oxfw->tx_stream);
+
+ mutex_unlock(&oxfw->mutex);
+
+ snd_oxfw_stream_lock_release(oxfw);
+ return 0;
+}
+
+static int midi_playback_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->rmidi->private_data;
+
+ mutex_lock(&oxfw->mutex);
+
+ oxfw->playback_substreams--;
+ snd_oxfw_stream_stop_simplex(oxfw, &oxfw->rx_stream);
+
+ mutex_unlock(&oxfw->mutex);
+
+ snd_oxfw_stream_lock_release(oxfw);
+ return 0;
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_oxfw *oxfw = substrm->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&oxfw->lock, flags);
+
+ if (up)
+ amdtp_stream_midi_trigger(&oxfw->tx_stream,
+ substrm->number, substrm);
+ else
+ amdtp_stream_midi_trigger(&oxfw->tx_stream,
+ substrm->number, NULL);
+
+ spin_unlock_irqrestore(&oxfw->lock, flags);
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_oxfw *oxfw = substrm->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&oxfw->lock, flags);
+
+ if (up)
+ amdtp_stream_midi_trigger(&oxfw->rx_stream,
+ substrm->number, substrm);
+ else
+ amdtp_stream_midi_trigger(&oxfw->rx_stream,
+ substrm->number, NULL);
+
+ spin_unlock_irqrestore(&oxfw->lock, flags);
+}
+
+static struct snd_rawmidi_ops midi_capture_ops = {
+ .open = midi_capture_open,
+ .close = midi_capture_close,
+ .trigger = midi_capture_trigger,
+};
+
+static struct snd_rawmidi_ops midi_playback_ops = {
+ .open = midi_playback_open,
+ .close = midi_playback_close,
+ .trigger = midi_playback_trigger,
+};
+
+static void set_midi_substream_names(struct snd_oxfw *oxfw,
+ struct snd_rawmidi_str *str)
+{
+ struct snd_rawmidi_substream *subs;
+
+ list_for_each_entry(subs, &str->substreams, list) {
+ snprintf(subs->name, sizeof(subs->name),
+ "%s MIDI %d",
+ oxfw->card->shortname, subs->number + 1);
+ }
+}
+
+int snd_oxfw_create_midi(struct snd_oxfw *oxfw)
+{
+ struct snd_oxfw_stream_formation formation;
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_str *str;
+ u8 *format;
+ int i, err;
+
+ /* If its stream has MIDI conformant data channel, add one MIDI port */
+ for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+ format = oxfw->tx_stream_formats[i];
+ if (format != NULL) {
+ err = snd_oxfw_stream_parse_format(format, &formation);
+ if (err >= 0 && formation.midi > 0)
+ oxfw->midi_input_ports = 1;
+ }
+
+ format = oxfw->rx_stream_formats[i];
+ if (format != NULL) {
+ err = snd_oxfw_stream_parse_format(format, &formation);
+ if (err >= 0 && formation.midi > 0)
+ oxfw->midi_output_ports = 1;
+ }
+ }
+ if ((oxfw->midi_input_ports == 0) && (oxfw->midi_output_ports == 0))
+ return 0;
+
+ /* create midi ports */
+ err = snd_rawmidi_new(oxfw->card, oxfw->card->driver, 0,
+ oxfw->midi_output_ports, oxfw->midi_input_ports,
+ &rmidi);
+ if (err < 0)
+ return err;
+
+ snprintf(rmidi->name, sizeof(rmidi->name),
+ "%s MIDI", oxfw->card->shortname);
+ rmidi->private_data = oxfw;
+
+ if (oxfw->midi_input_ports > 0) {
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &midi_capture_ops);
+
+ str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+
+ set_midi_substream_names(oxfw, str);
+ }
+
+ if (oxfw->midi_output_ports > 0) {
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &midi_playback_ops);
+
+ str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+
+ set_midi_substream_names(oxfw, str);
+ }
+
+ if ((oxfw->midi_output_ports > 0) && (oxfw->midi_input_ports > 0))
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ return 0;
+}
diff --git a/sound/firewire/oxfw/oxfw-pcm.c b/sound/firewire/oxfw/oxfw-pcm.c
new file mode 100644
index 000000000000..9bc556b15a92
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw-pcm.c
@@ -0,0 +1,424 @@
+/*
+ * oxfw_pcm.c - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "oxfw.h"
+
+static int hw_rule_rate(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ u8 **formats = rule->private;
+ struct snd_interval *r =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ const struct snd_interval *c =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval t = {
+ .min = UINT_MAX, .max = 0, .integer = 1
+ };
+ struct snd_oxfw_stream_formation formation;
+ unsigned int i, err;
+
+ for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+ if (formats[i] == NULL)
+ continue;
+
+ err = snd_oxfw_stream_parse_format(formats[i], &formation);
+ if (err < 0)
+ continue;
+ if (!snd_interval_test(c, formation.pcm))
+ continue;
+
+ t.min = min(t.min, formation.rate);
+ t.max = max(t.max, formation.rate);
+
+ }
+ return snd_interval_refine(r, &t);
+}
+
+static int hw_rule_channels(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ u8 **formats = rule->private;
+ struct snd_interval *c =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ const struct snd_interval *r =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_oxfw_stream_formation formation;
+ unsigned int i, j, err;
+ unsigned int count, list[SND_OXFW_STREAM_FORMAT_ENTRIES] = {0};
+
+ count = 0;
+ for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+ if (formats[i] == NULL)
+ break;
+
+ err = snd_oxfw_stream_parse_format(formats[i], &formation);
+ if (err < 0)
+ continue;
+ if (!snd_interval_test(r, formation.rate))
+ continue;
+ if (list[count] == formation.pcm)
+ continue;
+
+ for (j = 0; j < ARRAY_SIZE(list); j++) {
+ if (list[j] == formation.pcm)
+ break;
+ }
+ if (j == ARRAY_SIZE(list)) {
+ list[count] = formation.pcm;
+ if (++count == ARRAY_SIZE(list))
+ break;
+ }
+ }
+
+ return snd_interval_list(c, count, list, 0);
+}
+
+static void limit_channels_and_rates(struct snd_pcm_hardware *hw, u8 **formats)
+{
+ struct snd_oxfw_stream_formation formation;
+ unsigned int i, err;
+
+ hw->channels_min = UINT_MAX;
+ hw->channels_max = 0;
+
+ hw->rate_min = UINT_MAX;
+ hw->rate_max = 0;
+ hw->rates = 0;
+
+ for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+ if (formats[i] == NULL)
+ break;
+
+ err = snd_oxfw_stream_parse_format(formats[i], &formation);
+ if (err < 0)
+ continue;
+
+ hw->channels_min = min(hw->channels_min, formation.pcm);
+ hw->channels_max = max(hw->channels_max, formation.pcm);
+
+ hw->rate_min = min(hw->rate_min, formation.rate);
+ hw->rate_max = max(hw->rate_max, formation.rate);
+ hw->rates |= snd_pcm_rate_to_rate_bit(formation.rate);
+ }
+}
+
+static void limit_period_and_buffer(struct snd_pcm_hardware *hw)
+{
+ hw->periods_min = 2; /* SNDRV_PCM_INFO_BATCH */
+ hw->periods_max = UINT_MAX;
+
+ hw->period_bytes_min = 4 * hw->channels_max; /* bytes for a frame */
+
+ /* Just to prevent from allocating much pages. */
+ hw->period_bytes_max = hw->period_bytes_min * 2048;
+ hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min;
+}
+
+static int init_hw_params(struct snd_oxfw *oxfw,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ u8 **formats;
+ struct amdtp_stream *stream;
+ int err;
+
+ runtime->hw.info = SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_JOINT_DUPLEX |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ runtime->hw.formats = AMDTP_IN_PCM_FORMAT_BITS;
+ stream = &oxfw->tx_stream;
+ formats = oxfw->tx_stream_formats;
+ } else {
+ runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS;
+ stream = &oxfw->rx_stream;
+ formats = oxfw->rx_stream_formats;
+ }
+
+ limit_channels_and_rates(&runtime->hw, formats);
+ limit_period_and_buffer(&runtime->hw);
+
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ hw_rule_channels, formats,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ if (err < 0)
+ goto end;
+
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ hw_rule_rate, formats,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (err < 0)
+ goto end;
+
+ err = amdtp_stream_add_pcm_hw_constraints(stream, runtime);
+end:
+ return err;
+}
+
+static int limit_to_current_params(struct snd_pcm_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+ struct snd_oxfw_stream_formation formation;
+ enum avc_general_plug_dir dir;
+ int err;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ dir = AVC_GENERAL_PLUG_DIR_OUT;
+ else
+ dir = AVC_GENERAL_PLUG_DIR_IN;
+
+ err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation);
+ if (err < 0)
+ goto end;
+
+ substream->runtime->hw.channels_min = formation.pcm;
+ substream->runtime->hw.channels_max = formation.pcm;
+ substream->runtime->hw.rate_min = formation.rate;
+ substream->runtime->hw.rate_max = formation.rate;
+end:
+ return err;
+}
+
+static int pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+ int err;
+
+ err = snd_oxfw_stream_lock_try(oxfw);
+ if (err < 0)
+ goto end;
+
+ err = init_hw_params(oxfw, substream);
+ if (err < 0)
+ goto err_locked;
+
+ /*
+ * When any PCM streams are already running, the available sampling
+ * rate is limited at current value.
+ */
+ if (amdtp_stream_pcm_running(&oxfw->tx_stream) ||
+ amdtp_stream_pcm_running(&oxfw->rx_stream)) {
+ err = limit_to_current_params(substream);
+ if (err < 0)
+ goto end;
+ }
+
+ snd_pcm_set_sync(substream);
+end:
+ return err;
+err_locked:
+ snd_oxfw_stream_lock_release(oxfw);
+ return err;
+}
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+
+ snd_oxfw_stream_lock_release(oxfw);
+ return 0;
+}
+
+static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+
+
+ if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ mutex_lock(&oxfw->mutex);
+ oxfw->capture_substreams++;
+ mutex_unlock(&oxfw->mutex);
+ }
+
+ amdtp_stream_set_pcm_format(&oxfw->tx_stream, params_format(hw_params));
+
+ return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+}
+static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+
+ if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ mutex_lock(&oxfw->mutex);
+ oxfw->playback_substreams++;
+ mutex_unlock(&oxfw->mutex);
+ }
+
+ amdtp_stream_set_pcm_format(&oxfw->rx_stream, params_format(hw_params));
+
+ return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+}
+
+static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+
+ mutex_lock(&oxfw->mutex);
+
+ if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+ oxfw->capture_substreams--;
+
+ snd_oxfw_stream_stop_simplex(oxfw, &oxfw->tx_stream);
+
+ mutex_unlock(&oxfw->mutex);
+
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+
+ mutex_lock(&oxfw->mutex);
+
+ if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+ oxfw->playback_substreams--;
+
+ snd_oxfw_stream_stop_simplex(oxfw, &oxfw->rx_stream);
+
+ mutex_unlock(&oxfw->mutex);
+
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ mutex_lock(&oxfw->mutex);
+ err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->tx_stream,
+ runtime->rate, runtime->channels);
+ mutex_unlock(&oxfw->mutex);
+ if (err < 0)
+ goto end;
+
+ amdtp_stream_pcm_prepare(&oxfw->tx_stream);
+end:
+ return err;
+}
+static int pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ mutex_lock(&oxfw->mutex);
+ err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->rx_stream,
+ runtime->rate, runtime->channels);
+ mutex_unlock(&oxfw->mutex);
+ if (err < 0)
+ goto end;
+
+ amdtp_stream_pcm_prepare(&oxfw->rx_stream);
+end:
+ return err;
+}
+
+static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+ struct snd_pcm_substream *pcm;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ pcm = substream;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ pcm = NULL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ amdtp_stream_pcm_trigger(&oxfw->tx_stream, pcm);
+ return 0;
+}
+static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+ struct snd_pcm_substream *pcm;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ pcm = substream;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ pcm = NULL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ amdtp_stream_pcm_trigger(&oxfw->rx_stream, pcm);
+ return 0;
+}
+
+static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstm)
+{
+ struct snd_oxfw *oxfw = sbstm->private_data;
+
+ return amdtp_stream_pcm_pointer(&oxfw->tx_stream);
+}
+static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstm)
+{
+ struct snd_oxfw *oxfw = sbstm->private_data;
+
+ return amdtp_stream_pcm_pointer(&oxfw->rx_stream);
+}
+
+int snd_oxfw_create_pcm(struct snd_oxfw *oxfw)
+{
+ static struct snd_pcm_ops capture_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = pcm_capture_hw_params,
+ .hw_free = pcm_capture_hw_free,
+ .prepare = pcm_capture_prepare,
+ .trigger = pcm_capture_trigger,
+ .pointer = pcm_capture_pointer,
+ .page = snd_pcm_lib_get_vmalloc_page,
+ .mmap = snd_pcm_lib_mmap_vmalloc,
+ };
+ static struct snd_pcm_ops playback_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = pcm_playback_hw_params,
+ .hw_free = pcm_playback_hw_free,
+ .prepare = pcm_playback_prepare,
+ .trigger = pcm_playback_trigger,
+ .pointer = pcm_playback_pointer,
+ .page = snd_pcm_lib_get_vmalloc_page,
+ .mmap = snd_pcm_lib_mmap_vmalloc,
+ };
+ struct snd_pcm *pcm;
+ unsigned int cap = 0;
+ int err;
+
+ if (oxfw->has_output)
+ cap = 1;
+
+ err = snd_pcm_new(oxfw->card, oxfw->card->driver, 0, 1, cap, &pcm);
+ if (err < 0)
+ return err;
+
+ pcm->private_data = oxfw;
+ strcpy(pcm->name, oxfw->card->shortname);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
+ if (cap > 0)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
+
+ return 0;
+}
diff --git a/sound/firewire/oxfw/oxfw-proc.c b/sound/firewire/oxfw/oxfw-proc.c
new file mode 100644
index 000000000000..604808e5526d
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw-proc.c
@@ -0,0 +1,113 @@
+/*
+ * oxfw_proc.c - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) 2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./oxfw.h"
+
+static void proc_read_formation(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_oxfw *oxfw = entry->private_data;
+ struct snd_oxfw_stream_formation formation, curr;
+ u8 *format;
+ char flag;
+ unsigned int i, err;
+
+ /* Show input. */
+ err = snd_oxfw_stream_get_current_formation(oxfw,
+ AVC_GENERAL_PLUG_DIR_IN,
+ &curr);
+ if (err < 0)
+ return;
+
+ snd_iprintf(buffer, "Input Stream to device:\n");
+ snd_iprintf(buffer, "\tRate\tPCM\tMIDI\n");
+ for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+ format = oxfw->rx_stream_formats[i];
+ if (format == NULL)
+ continue;
+
+ err = snd_oxfw_stream_parse_format(format, &formation);
+ if (err < 0)
+ continue;
+
+ if (memcmp(&formation, &curr, sizeof(curr)) == 0)
+ flag = '*';
+ else
+ flag = ' ';
+
+ snd_iprintf(buffer, "%c\t%d\t%d\t%d\n", flag,
+ formation.rate, formation.pcm, formation.midi);
+ }
+
+ if (!oxfw->has_output)
+ return;
+
+ /* Show output. */
+ err = snd_oxfw_stream_get_current_formation(oxfw,
+ AVC_GENERAL_PLUG_DIR_OUT,
+ &curr);
+ if (err < 0)
+ return;
+
+ snd_iprintf(buffer, "Output Stream from device:\n");
+ snd_iprintf(buffer, "\tRate\tPCM\tMIDI\n");
+ for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+ format = oxfw->tx_stream_formats[i];
+ if (format == NULL)
+ continue;
+
+ err = snd_oxfw_stream_parse_format(format, &formation);
+ if (err < 0)
+ continue;
+
+ if (memcmp(&formation, &curr, sizeof(curr)) == 0)
+ flag = '*';
+ else
+ flag = ' ';
+
+ snd_iprintf(buffer, "%c\t%d\t%d\t%d\n", flag,
+ formation.rate, formation.pcm, formation.midi);
+ }
+}
+
+static void add_node(struct snd_oxfw *oxfw, struct snd_info_entry *root,
+ const char *name,
+ void (*op)(struct snd_info_entry *e,
+ struct snd_info_buffer *b))
+{
+ struct snd_info_entry *entry;
+
+ entry = snd_info_create_card_entry(oxfw->card, name, root);
+ if (entry == NULL)
+ return;
+
+ snd_info_set_text_ops(entry, oxfw, op);
+ if (snd_info_register(entry) < 0)
+ snd_info_free_entry(entry);
+}
+
+void snd_oxfw_proc_init(struct snd_oxfw *oxfw)
+{
+ struct snd_info_entry *root;
+
+ /*
+ * All nodes are automatically removed at snd_card_disconnect(),
+ * by following to link list.
+ */
+ root = snd_info_create_card_entry(oxfw->card, "firewire",
+ oxfw->card->proc_root);
+ if (root == NULL)
+ return;
+ root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+ if (snd_info_register(root) < 0) {
+ snd_info_free_entry(root);
+ return;
+ }
+
+ add_node(oxfw, root, "formation", proc_read_formation);
+}
diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c
new file mode 100644
index 000000000000..b77cf80f1678
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw-stream.c
@@ -0,0 +1,685 @@
+/*
+ * oxfw_stream.c - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) 2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "oxfw.h"
+#include <linux/delay.h>
+
+#define AVC_GENERIC_FRAME_MAXIMUM_BYTES 512
+#define CALLBACK_TIMEOUT 200
+
+/*
+ * According to datasheet of Oxford Semiconductor:
+ * OXFW970: 32.0/44.1/48.0/96.0 Khz, 8 audio channels I/O
+ * OXFW971: 32.0/44.1/48.0/88.2/96.0/192.0 kHz, 16 audio channels I/O, MIDI I/O
+ */
+static const unsigned int oxfw_rate_table[] = {
+ [0] = 32000,
+ [1] = 44100,
+ [2] = 48000,
+ [3] = 88200,
+ [4] = 96000,
+ [5] = 192000,
+};
+
+/*
+ * See Table 5.7 – Sampling frequency for Multi-bit Audio
+ * in AV/C Stream Format Information Specification 1.1 (Apr 2005, 1394TA)
+ */
+static const unsigned int avc_stream_rate_table[] = {
+ [0] = 0x02,
+ [1] = 0x03,
+ [2] = 0x04,
+ [3] = 0x0a,
+ [4] = 0x05,
+ [5] = 0x07,
+};
+
+static int set_rate(struct snd_oxfw *oxfw, unsigned int rate)
+{
+ int err;
+
+ err = avc_general_set_sig_fmt(oxfw->unit, rate,
+ AVC_GENERAL_PLUG_DIR_IN, 0);
+ if (err < 0)
+ goto end;
+
+ if (oxfw->has_output)
+ err = avc_general_set_sig_fmt(oxfw->unit, rate,
+ AVC_GENERAL_PLUG_DIR_OUT, 0);
+end:
+ return err;
+}
+
+static int set_stream_format(struct snd_oxfw *oxfw, struct amdtp_stream *s,
+ unsigned int rate, unsigned int pcm_channels)
+{
+ u8 **formats;
+ struct snd_oxfw_stream_formation formation;
+ enum avc_general_plug_dir dir;
+ unsigned int i, err, len;
+
+ if (s == &oxfw->tx_stream) {
+ formats = oxfw->tx_stream_formats;
+ dir = AVC_GENERAL_PLUG_DIR_OUT;
+ } else {
+ formats = oxfw->rx_stream_formats;
+ dir = AVC_GENERAL_PLUG_DIR_IN;
+ }
+
+ /* Seek stream format for requirements. */
+ for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+ err = snd_oxfw_stream_parse_format(formats[i], &formation);
+ if (err < 0)
+ return err;
+
+ if ((formation.rate == rate) && (formation.pcm == pcm_channels))
+ break;
+ }
+ if (i == SND_OXFW_STREAM_FORMAT_ENTRIES)
+ return -EINVAL;
+
+ /* If assumed, just change rate. */
+ if (oxfw->assumed)
+ return set_rate(oxfw, rate);
+
+ /* Calculate format length. */
+ len = 5 + formats[i][4] * 2;
+
+ err = avc_stream_set_format(oxfw->unit, dir, 0, formats[i], len);
+ if (err < 0)
+ return err;
+
+ /* Some requests just after changing format causes freezing. */
+ msleep(100);
+
+ return 0;
+}
+
+static void stop_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
+{
+ amdtp_stream_pcm_abort(stream);
+ amdtp_stream_stop(stream);
+
+ if (stream == &oxfw->tx_stream)
+ cmp_connection_break(&oxfw->out_conn);
+ else
+ cmp_connection_break(&oxfw->in_conn);
+}
+
+static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream,
+ unsigned int rate, unsigned int pcm_channels)
+{
+ u8 **formats;
+ struct cmp_connection *conn;
+ struct snd_oxfw_stream_formation formation;
+ unsigned int i, midi_ports;
+ int err;
+
+ if (stream == &oxfw->rx_stream) {
+ formats = oxfw->rx_stream_formats;
+ conn = &oxfw->in_conn;
+ } else {
+ formats = oxfw->tx_stream_formats;
+ conn = &oxfw->out_conn;
+ }
+
+ /* Get stream format */
+ for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+ if (formats[i] == NULL)
+ break;
+
+ err = snd_oxfw_stream_parse_format(formats[i], &formation);
+ if (err < 0)
+ goto end;
+ if (rate != formation.rate)
+ continue;
+ if (pcm_channels == 0 || pcm_channels == formation.pcm)
+ break;
+ }
+ if (i == SND_OXFW_STREAM_FORMAT_ENTRIES) {
+ err = -EINVAL;
+ goto end;
+ }
+
+ pcm_channels = formation.pcm;
+ midi_ports = DIV_ROUND_UP(formation.midi, 8);
+
+ /* The stream should have one pcm channels at least */
+ if (pcm_channels == 0) {
+ err = -EINVAL;
+ goto end;
+ }
+ amdtp_stream_set_parameters(stream, rate, pcm_channels, midi_ports);
+
+ err = cmp_connection_establish(conn,
+ amdtp_stream_get_max_payload(stream));
+ if (err < 0)
+ goto end;
+
+ err = amdtp_stream_start(stream,
+ conn->resources.channel,
+ conn->speed);
+ if (err < 0) {
+ cmp_connection_break(conn);
+ goto end;
+ }
+
+ /* Wait first packet */
+ err = amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT);
+ if (err < 0)
+ stop_stream(oxfw, stream);
+end:
+ return err;
+}
+
+static int check_connection_used_by_others(struct snd_oxfw *oxfw,
+ struct amdtp_stream *stream)
+{
+ struct cmp_connection *conn;
+ bool used;
+ int err;
+
+ if (stream == &oxfw->tx_stream)
+ conn = &oxfw->out_conn;
+ else
+ conn = &oxfw->in_conn;
+
+ err = cmp_connection_check_used(conn, &used);
+ if ((err >= 0) && used && !amdtp_stream_running(stream)) {
+ dev_err(&oxfw->unit->device,
+ "Connection established by others: %cPCR[%d]\n",
+ (conn->direction == CMP_OUTPUT) ? 'o' : 'i',
+ conn->pcr_index);
+ err = -EBUSY;
+ }
+
+ return err;
+}
+
+int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
+ struct amdtp_stream *stream)
+{
+ struct cmp_connection *conn;
+ enum cmp_direction c_dir;
+ enum amdtp_stream_direction s_dir;
+ int err;
+
+ if (stream == &oxfw->tx_stream) {
+ conn = &oxfw->out_conn;
+ c_dir = CMP_OUTPUT;
+ s_dir = AMDTP_IN_STREAM;
+ } else {
+ conn = &oxfw->in_conn;
+ c_dir = CMP_INPUT;
+ s_dir = AMDTP_OUT_STREAM;
+ }
+
+ err = cmp_connection_init(conn, oxfw->unit, c_dir, 0);
+ if (err < 0)
+ goto end;
+
+ err = amdtp_stream_init(stream, oxfw->unit, s_dir, CIP_NONBLOCKING);
+ if (err < 0) {
+ amdtp_stream_destroy(stream);
+ cmp_connection_destroy(conn);
+ goto end;
+ }
+
+ /* OXFW starts to transmit packets with non-zero dbc. */
+ if (stream == &oxfw->tx_stream)
+ oxfw->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK;
+end:
+ return err;
+}
+
+int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw,
+ struct amdtp_stream *stream,
+ unsigned int rate, unsigned int pcm_channels)
+{
+ struct amdtp_stream *opposite;
+ struct snd_oxfw_stream_formation formation;
+ enum avc_general_plug_dir dir;
+ unsigned int substreams, opposite_substreams;
+ int err = 0;
+
+ if (stream == &oxfw->tx_stream) {
+ substreams = oxfw->capture_substreams;
+ opposite = &oxfw->rx_stream;
+ opposite_substreams = oxfw->playback_substreams;
+ dir = AVC_GENERAL_PLUG_DIR_OUT;
+ } else {
+ substreams = oxfw->playback_substreams;
+ opposite_substreams = oxfw->capture_substreams;
+
+ if (oxfw->has_output)
+ opposite = &oxfw->rx_stream;
+ else
+ opposite = NULL;
+
+ dir = AVC_GENERAL_PLUG_DIR_IN;
+ }
+
+ if (substreams == 0)
+ goto end;
+
+ /*
+ * Considering JACK/FFADO streaming:
+ * TODO: This can be removed hwdep functionality becomes popular.
+ */
+ err = check_connection_used_by_others(oxfw, stream);
+ if (err < 0)
+ goto end;
+
+ /* packet queueing error */
+ if (amdtp_streaming_error(stream))
+ stop_stream(oxfw, stream);
+
+ err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation);
+ if (err < 0)
+ goto end;
+ if (rate == 0)
+ rate = formation.rate;
+ if (pcm_channels == 0)
+ pcm_channels = formation.pcm;
+
+ if ((formation.rate != rate) || (formation.pcm != pcm_channels)) {
+ if (opposite != NULL) {
+ err = check_connection_used_by_others(oxfw, opposite);
+ if (err < 0)
+ goto end;
+ stop_stream(oxfw, opposite);
+ }
+ stop_stream(oxfw, stream);
+
+ err = set_stream_format(oxfw, stream, rate, pcm_channels);
+ if (err < 0) {
+ dev_err(&oxfw->unit->device,
+ "fail to set stream format: %d\n", err);
+ goto end;
+ }
+
+ /* Start opposite stream if needed. */
+ if (opposite && !amdtp_stream_running(opposite) &&
+ (opposite_substreams > 0)) {
+ err = start_stream(oxfw, opposite, rate, 0);
+ if (err < 0) {
+ dev_err(&oxfw->unit->device,
+ "fail to restart stream: %d\n", err);
+ goto end;
+ }
+ }
+ }
+
+ /* Start requested stream. */
+ if (!amdtp_stream_running(stream)) {
+ err = start_stream(oxfw, stream, rate, pcm_channels);
+ if (err < 0)
+ dev_err(&oxfw->unit->device,
+ "fail to start stream: %d\n", err);
+ }
+end:
+ return err;
+}
+
+void snd_oxfw_stream_stop_simplex(struct snd_oxfw *oxfw,
+ struct amdtp_stream *stream)
+{
+ if (((stream == &oxfw->tx_stream) && (oxfw->capture_substreams > 0)) ||
+ ((stream == &oxfw->rx_stream) && (oxfw->playback_substreams > 0)))
+ return;
+
+ stop_stream(oxfw, stream);
+}
+
+void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw,
+ struct amdtp_stream *stream)
+{
+ struct cmp_connection *conn;
+
+ if (stream == &oxfw->tx_stream)
+ conn = &oxfw->out_conn;
+ else
+ conn = &oxfw->in_conn;
+
+ stop_stream(oxfw, stream);
+
+ amdtp_stream_destroy(stream);
+ cmp_connection_destroy(conn);
+}
+
+void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw,
+ struct amdtp_stream *stream)
+{
+ struct cmp_connection *conn;
+
+ if (stream == &oxfw->tx_stream)
+ conn = &oxfw->out_conn;
+ else
+ conn = &oxfw->in_conn;
+
+ if (cmp_connection_update(conn) < 0)
+ stop_stream(oxfw, stream);
+ else
+ amdtp_stream_update(stream);
+}
+
+int snd_oxfw_stream_get_current_formation(struct snd_oxfw *oxfw,
+ enum avc_general_plug_dir dir,
+ struct snd_oxfw_stream_formation *formation)
+{
+ u8 *format;
+ unsigned int len;
+ int err;
+
+ len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
+ format = kmalloc(len, GFP_KERNEL);
+ if (format == NULL)
+ return -ENOMEM;
+
+ err = avc_stream_get_format_single(oxfw->unit, dir, 0, format, &len);
+ if (err < 0)
+ goto end;
+ if (len < 3) {
+ err = -EIO;
+ goto end;
+ }
+
+ err = snd_oxfw_stream_parse_format(format, formation);
+end:
+ kfree(format);
+ return err;
+}
+
+/*
+ * See Table 6.16 - AM824 Stream Format
+ * Figure 6.19 - format_information field for AM824 Compound
+ * in AV/C Stream Format Information Specification 1.1 (Apr 2005, 1394TA)
+ * Also 'Clause 12 AM824 sequence adaption layers' in IEC 61883-6:2005
+ */
+int snd_oxfw_stream_parse_format(u8 *format,
+ struct snd_oxfw_stream_formation *formation)
+{
+ unsigned int i, e, channels, type;
+
+ memset(formation, 0, sizeof(struct snd_oxfw_stream_formation));
+
+ /*
+ * this module can support a hierarchy combination that:
+ * Root: Audio and Music (0x90)
+ * Level 1: AM824 Compound (0x40)
+ */
+ if ((format[0] != 0x90) || (format[1] != 0x40))
+ return -ENOSYS;
+
+ /* check the sampling rate */
+ for (i = 0; i < ARRAY_SIZE(avc_stream_rate_table); i++) {
+ if (format[2] == avc_stream_rate_table[i])
+ break;
+ }
+ if (i == ARRAY_SIZE(avc_stream_rate_table))
+ return -ENOSYS;
+
+ formation->rate = oxfw_rate_table[i];
+
+ for (e = 0; e < format[4]; e++) {
+ channels = format[5 + e * 2];
+ type = format[6 + e * 2];
+
+ switch (type) {
+ /* IEC 60958 Conformant, currently handled as MBLA */
+ case 0x00:
+ /* Multi Bit Linear Audio (Raw) */
+ case 0x06:
+ formation->pcm += channels;
+ break;
+ /* MIDI Conformant */
+ case 0x0d:
+ formation->midi = channels;
+ break;
+ /* IEC 61937-3 to 7 */
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ /* Multi Bit Linear Audio */
+ case 0x07: /* DVD-Audio */
+ case 0x0c: /* High Precision */
+ /* One Bit Audio */
+ case 0x08: /* (Plain) Raw */
+ case 0x09: /* (Plain) SACD */
+ case 0x0a: /* (Encoded) Raw */
+ case 0x0b: /* (Encoded) SACD */
+ /* SMPTE Time-Code conformant */
+ case 0x0e:
+ /* Sample Count */
+ case 0x0f:
+ /* Anciliary Data */
+ case 0x10:
+ /* Synchronization Stream (Stereo Raw audio) */
+ case 0x40:
+ /* Don't care */
+ case 0xff:
+ default:
+ return -ENOSYS; /* not supported */
+ }
+ }
+
+ if (formation->pcm > AMDTP_MAX_CHANNELS_FOR_PCM ||
+ formation->midi > AMDTP_MAX_CHANNELS_FOR_MIDI)
+ return -ENOSYS;
+
+ return 0;
+}
+
+static int
+assume_stream_formats(struct snd_oxfw *oxfw, enum avc_general_plug_dir dir,
+ unsigned int pid, u8 *buf, unsigned int *len,
+ u8 **formats)
+{
+ struct snd_oxfw_stream_formation formation;
+ unsigned int i, eid;
+ int err;
+
+ /* get format at current sampling rate */
+ err = avc_stream_get_format_single(oxfw->unit, dir, pid, buf, len);
+ if (err < 0) {
+ dev_err(&oxfw->unit->device,
+ "fail to get current stream format for isoc %s plug %d:%d\n",
+ (dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" : "out",
+ pid, err);
+ goto end;
+ }
+
+ /* parse and set stream format */
+ eid = 0;
+ err = snd_oxfw_stream_parse_format(buf, &formation);
+ if (err < 0)
+ goto end;
+
+ formats[eid] = kmalloc(*len, GFP_KERNEL);
+ if (formats[eid] == NULL) {
+ err = -ENOMEM;
+ goto end;
+ }
+ memcpy(formats[eid], buf, *len);
+
+ /* apply the format for each available sampling rate */
+ for (i = 0; i < ARRAY_SIZE(oxfw_rate_table); i++) {
+ if (formation.rate == oxfw_rate_table[i])
+ continue;
+
+ err = avc_general_inquiry_sig_fmt(oxfw->unit,
+ oxfw_rate_table[i],
+ dir, pid);
+ if (err < 0)
+ continue;
+
+ eid++;
+ formats[eid] = kmalloc(*len, GFP_KERNEL);
+ if (formats[eid] == NULL) {
+ err = -ENOMEM;
+ goto end;
+ }
+ memcpy(formats[eid], buf, *len);
+ formats[eid][2] = avc_stream_rate_table[i];
+ }
+
+ err = 0;
+ oxfw->assumed = true;
+end:
+ return err;
+}
+
+static int fill_stream_formats(struct snd_oxfw *oxfw,
+ enum avc_general_plug_dir dir,
+ unsigned short pid)
+{
+ u8 *buf, **formats;
+ unsigned int len, eid = 0;
+ struct snd_oxfw_stream_formation dummy;
+ int err;
+
+ buf = kmalloc(AVC_GENERIC_FRAME_MAXIMUM_BYTES, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ if (dir == AVC_GENERAL_PLUG_DIR_OUT)
+ formats = oxfw->tx_stream_formats;
+ else
+ formats = oxfw->rx_stream_formats;
+
+ /* get first entry */
+ len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
+ err = avc_stream_get_format_list(oxfw->unit, dir, 0, buf, &len, 0);
+ if (err == -ENOSYS) {
+ /* LIST subfunction is not implemented */
+ len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
+ err = assume_stream_formats(oxfw, dir, pid, buf, &len,
+ formats);
+ goto end;
+ } else if (err < 0) {
+ dev_err(&oxfw->unit->device,
+ "fail to get stream format %d for isoc %s plug %d:%d\n",
+ eid, (dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" : "out",
+ pid, err);
+ goto end;
+ }
+
+ /* LIST subfunction is implemented */
+ while (eid < SND_OXFW_STREAM_FORMAT_ENTRIES) {
+ /* The format is too short. */
+ if (len < 3) {
+ err = -EIO;
+ break;
+ }
+
+ /* parse and set stream format */
+ err = snd_oxfw_stream_parse_format(buf, &dummy);
+ if (err < 0)
+ break;
+
+ formats[eid] = kmalloc(len, GFP_KERNEL);
+ if (formats[eid] == NULL) {
+ err = -ENOMEM;
+ break;
+ }
+ memcpy(formats[eid], buf, len);
+
+ /* get next entry */
+ len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
+ err = avc_stream_get_format_list(oxfw->unit, dir, 0,
+ buf, &len, ++eid);
+ /* No entries remained. */
+ if (err == -EINVAL) {
+ err = 0;
+ break;
+ } else if (err < 0) {
+ dev_err(&oxfw->unit->device,
+ "fail to get stream format %d for isoc %s plug %d:%d\n",
+ eid, (dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" :
+ "out",
+ pid, err);
+ break;
+ }
+ }
+end:
+ kfree(buf);
+ return err;
+}
+
+int snd_oxfw_stream_discover(struct snd_oxfw *oxfw)
+{
+ u8 plugs[AVC_PLUG_INFO_BUF_BYTES];
+ int err;
+
+ /* the number of plugs for isoc in/out, ext in/out */
+ err = avc_general_get_plug_info(oxfw->unit, 0x1f, 0x07, 0x00, plugs);
+ if (err < 0) {
+ dev_err(&oxfw->unit->device,
+ "fail to get info for isoc/external in/out plugs: %d\n",
+ err);
+ goto end;
+ } else if ((plugs[0] == 0) && (plugs[1] == 0)) {
+ err = -ENOSYS;
+ goto end;
+ }
+
+ /* use oPCR[0] if exists */
+ if (plugs[1] > 0) {
+ err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_OUT, 0);
+ if (err < 0)
+ goto end;
+ oxfw->has_output = true;
+ }
+
+ /* use iPCR[0] if exists */
+ if (plugs[0] > 0)
+ err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_IN, 0);
+end:
+ return err;
+}
+
+void snd_oxfw_stream_lock_changed(struct snd_oxfw *oxfw)
+{
+ oxfw->dev_lock_changed = true;
+ wake_up(&oxfw->hwdep_wait);
+}
+
+int snd_oxfw_stream_lock_try(struct snd_oxfw *oxfw)
+{
+ int err;
+
+ spin_lock_irq(&oxfw->lock);
+
+ /* user land lock this */
+ if (oxfw->dev_lock_count < 0) {
+ err = -EBUSY;
+ goto end;
+ }
+
+ /* this is the first time */
+ if (oxfw->dev_lock_count++ == 0)
+ snd_oxfw_stream_lock_changed(oxfw);
+ err = 0;
+end:
+ spin_unlock_irq(&oxfw->lock);
+ return err;
+}
+
+void snd_oxfw_stream_lock_release(struct snd_oxfw *oxfw)
+{
+ spin_lock_irq(&oxfw->lock);
+
+ if (WARN_ON(oxfw->dev_lock_count <= 0))
+ goto end;
+ if (--oxfw->dev_lock_count == 0)
+ snd_oxfw_stream_lock_changed(oxfw);
+end:
+ spin_unlock_irq(&oxfw->lock);
+}
diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c
new file mode 100644
index 000000000000..cf1d0b55e827
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw.c
@@ -0,0 +1,317 @@
+/*
+ * oxfw.c - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "oxfw.h"
+
+#define OXFORD_FIRMWARE_ID_ADDRESS (CSR_REGISTER_BASE + 0x50000)
+/* 0x970?vvvv or 0x971?vvvv, where vvvv = firmware version */
+
+#define OXFORD_HARDWARE_ID_ADDRESS (CSR_REGISTER_BASE + 0x90020)
+#define OXFORD_HARDWARE_ID_OXFW970 0x39443841
+#define OXFORD_HARDWARE_ID_OXFW971 0x39373100
+
+#define VENDOR_LOUD 0x000ff2
+#define VENDOR_GRIFFIN 0x001292
+#define VENDOR_BEHRINGER 0x001564
+#define VENDOR_LACIE 0x00d04b
+
+#define SPECIFIER_1394TA 0x00a02d
+#define VERSION_AVC 0x010001
+
+MODULE_DESCRIPTION("Oxford Semiconductor FW970/971 driver");
+MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("snd-firewire-speakers");
+
+static bool detect_loud_models(struct fw_unit *unit)
+{
+ const char *const models[] = {
+ "Onyxi",
+ "Onyx-i",
+ "d.Pro",
+ "Mackie Onyx Satellite",
+ "Tapco LINK.firewire 4x6",
+ "U.420"};
+ char model[32];
+ unsigned int i;
+ int err;
+
+ err = fw_csr_string(unit->directory, CSR_MODEL,
+ model, sizeof(model));
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < ARRAY_SIZE(models); i++) {
+ if (strcmp(models[i], model) == 0)
+ break;
+ }
+
+ return (i < ARRAY_SIZE(models));
+}
+
+static int name_card(struct snd_oxfw *oxfw)
+{
+ struct fw_device *fw_dev = fw_parent_device(oxfw->unit);
+ char vendor[24];
+ char model[32];
+ const char *d, *v, *m;
+ u32 firmware;
+ int err;
+
+ /* get vendor name from root directory */
+ err = fw_csr_string(fw_dev->config_rom + 5, CSR_VENDOR,
+ vendor, sizeof(vendor));
+ if (err < 0)
+ goto end;
+
+ /* get model name from unit directory */
+ err = fw_csr_string(oxfw->unit->directory, CSR_MODEL,
+ model, sizeof(model));
+ if (err < 0)
+ goto end;
+
+ err = snd_fw_transaction(oxfw->unit, TCODE_READ_QUADLET_REQUEST,
+ OXFORD_FIRMWARE_ID_ADDRESS, &firmware, 4, 0);
+ if (err < 0)
+ goto end;
+ be32_to_cpus(&firmware);
+
+ /* to apply card definitions */
+ if (oxfw->device_info) {
+ d = oxfw->device_info->driver_name;
+ v = oxfw->device_info->vendor_name;
+ m = oxfw->device_info->model_name;
+ } else {
+ d = "OXFW";
+ v = vendor;
+ m = model;
+ }
+
+ strcpy(oxfw->card->driver, d);
+ strcpy(oxfw->card->mixername, m);
+ strcpy(oxfw->card->shortname, m);
+
+ snprintf(oxfw->card->longname, sizeof(oxfw->card->longname),
+ "%s %s (OXFW%x %04x), GUID %08x%08x at %s, S%d",
+ v, m, firmware >> 20, firmware & 0xffff,
+ fw_dev->config_rom[3], fw_dev->config_rom[4],
+ dev_name(&oxfw->unit->device), 100 << fw_dev->max_speed);
+end:
+ return err;
+}
+
+static void oxfw_card_free(struct snd_card *card)
+{
+ struct snd_oxfw *oxfw = card->private_data;
+ unsigned int i;
+
+ for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+ kfree(oxfw->tx_stream_formats[i]);
+ kfree(oxfw->rx_stream_formats[i]);
+ }
+
+ mutex_destroy(&oxfw->mutex);
+}
+
+static int oxfw_probe(struct fw_unit *unit,
+ const struct ieee1394_device_id *id)
+{
+ struct snd_card *card;
+ struct snd_oxfw *oxfw;
+ int err;
+
+ if ((id->vendor_id == VENDOR_LOUD) && !detect_loud_models(unit))
+ return -ENODEV;
+
+ err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
+ sizeof(*oxfw), &card);
+ if (err < 0)
+ return err;
+
+ card->private_free = oxfw_card_free;
+ oxfw = card->private_data;
+ oxfw->card = card;
+ mutex_init(&oxfw->mutex);
+ oxfw->unit = unit;
+ oxfw->device_info = (const struct device_info *)id->driver_data;
+ spin_lock_init(&oxfw->lock);
+ init_waitqueue_head(&oxfw->hwdep_wait);
+
+ err = snd_oxfw_stream_discover(oxfw);
+ if (err < 0)
+ goto error;
+
+ err = name_card(oxfw);
+ if (err < 0)
+ goto error;
+
+ err = snd_oxfw_create_pcm(oxfw);
+ if (err < 0)
+ goto error;
+
+ if (oxfw->device_info) {
+ err = snd_oxfw_create_mixer(oxfw);
+ if (err < 0)
+ goto error;
+ }
+
+ snd_oxfw_proc_init(oxfw);
+
+ err = snd_oxfw_create_midi(oxfw);
+ if (err < 0)
+ goto error;
+
+ err = snd_oxfw_create_hwdep(oxfw);
+ if (err < 0)
+ goto error;
+
+ err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->rx_stream);
+ if (err < 0)
+ goto error;
+ if (oxfw->has_output) {
+ err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->tx_stream);
+ if (err < 0)
+ goto error;
+ }
+
+ err = snd_card_register(card);
+ if (err < 0) {
+ snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream);
+ if (oxfw->has_output)
+ snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream);
+ goto error;
+ }
+ dev_set_drvdata(&unit->device, oxfw);
+
+ return 0;
+error:
+ snd_card_free(card);
+ return err;
+}
+
+static void oxfw_bus_reset(struct fw_unit *unit)
+{
+ struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device);
+
+ fcp_bus_reset(oxfw->unit);
+
+ mutex_lock(&oxfw->mutex);
+
+ snd_oxfw_stream_update_simplex(oxfw, &oxfw->rx_stream);
+ if (oxfw->has_output)
+ snd_oxfw_stream_update_simplex(oxfw, &oxfw->tx_stream);
+
+ mutex_unlock(&oxfw->mutex);
+}
+
+static void oxfw_remove(struct fw_unit *unit)
+{
+ struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device);
+
+ snd_card_disconnect(oxfw->card);
+
+ snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream);
+ if (oxfw->has_output)
+ snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream);
+
+ snd_card_free_when_closed(oxfw->card);
+}
+
+static const struct device_info griffin_firewave = {
+ .driver_name = "FireWave",
+ .vendor_name = "Griffin",
+ .model_name = "FireWave",
+ .mixer_channels = 6,
+ .mute_fb_id = 0x01,
+ .volume_fb_id = 0x02,
+};
+
+static const struct device_info lacie_speakers = {
+ .driver_name = "FWSpeakers",
+ .vendor_name = "LaCie",
+ .model_name = "FireWire Speakers",
+ .mixer_channels = 1,
+ .mute_fb_id = 0x01,
+ .volume_fb_id = 0x01,
+};
+
+static const struct ieee1394_device_id oxfw_id_table[] = {
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID |
+ IEEE1394_MATCH_SPECIFIER_ID |
+ IEEE1394_MATCH_VERSION,
+ .vendor_id = VENDOR_GRIFFIN,
+ .model_id = 0x00f970,
+ .specifier_id = SPECIFIER_1394TA,
+ .version = VERSION_AVC,
+ .driver_data = (kernel_ulong_t)&griffin_firewave,
+ },
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID |
+ IEEE1394_MATCH_SPECIFIER_ID |
+ IEEE1394_MATCH_VERSION,
+ .vendor_id = VENDOR_LACIE,
+ .model_id = 0x00f970,
+ .specifier_id = SPECIFIER_1394TA,
+ .version = VERSION_AVC,
+ .driver_data = (kernel_ulong_t)&lacie_speakers,
+ },
+ /* Behringer,F-Control Audio 202 */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = VENDOR_BEHRINGER,
+ .model_id = 0x00fc22,
+ },
+ /*
+ * Any Mackie(Loud) models (name string/model id):
+ * Onyx-i series (former models): 0x081216
+ * Mackie Onyx Satellite: 0x00200f
+ * Tapco LINK.firewire 4x6: 0x000460
+ * d.2 pro: Unknown
+ * d.4 pro: Unknown
+ * U.420: Unknown
+ * U.420d: Unknown
+ */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_SPECIFIER_ID |
+ IEEE1394_MATCH_VERSION,
+ .vendor_id = VENDOR_LOUD,
+ .specifier_id = SPECIFIER_1394TA,
+ .version = VERSION_AVC,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(ieee1394, oxfw_id_table);
+
+static struct fw_driver oxfw_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = KBUILD_MODNAME,
+ .bus = &fw_bus_type,
+ },
+ .probe = oxfw_probe,
+ .update = oxfw_bus_reset,
+ .remove = oxfw_remove,
+ .id_table = oxfw_id_table,
+};
+
+static int __init snd_oxfw_init(void)
+{
+ return driver_register(&oxfw_driver.driver);
+}
+
+static void __exit snd_oxfw_exit(void)
+{
+ driver_unregister(&oxfw_driver.driver);
+}
+
+module_init(snd_oxfw_init);
+module_exit(snd_oxfw_exit);
diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h
new file mode 100644
index 000000000000..cace5ad4fe76
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw.h
@@ -0,0 +1,146 @@
+/*
+ * oxfw.h - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/compat.h>
+
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <sound/rawmidi.h>
+#include <sound/firewire.h>
+#include <sound/hwdep.h>
+
+#include "../lib.h"
+#include "../fcp.h"
+#include "../packets-buffer.h"
+#include "../iso-resources.h"
+#include "../amdtp.h"
+#include "../cmp.h"
+
+struct device_info {
+ const char *driver_name;
+ const char *vendor_name;
+ const char *model_name;
+ unsigned int mixer_channels;
+ u8 mute_fb_id;
+ u8 volume_fb_id;
+};
+
+/* This is an arbitrary number for convinience. */
+#define SND_OXFW_STREAM_FORMAT_ENTRIES 10
+struct snd_oxfw {
+ struct snd_card *card;
+ struct fw_unit *unit;
+ const struct device_info *device_info;
+ struct mutex mutex;
+ spinlock_t lock;
+
+ bool has_output;
+ u8 *tx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES];
+ u8 *rx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES];
+ bool assumed;
+ struct cmp_connection out_conn;
+ struct cmp_connection in_conn;
+ struct amdtp_stream tx_stream;
+ struct amdtp_stream rx_stream;
+ unsigned int capture_substreams;
+ unsigned int playback_substreams;
+
+ unsigned int midi_input_ports;
+ unsigned int midi_output_ports;
+
+ bool mute;
+ s16 volume[6];
+ s16 volume_min;
+ s16 volume_max;
+
+ int dev_lock_count;
+ bool dev_lock_changed;
+ wait_queue_head_t hwdep_wait;
+};
+
+/*
+ * AV/C Stream Format Information Specification 1.1 Working Draft
+ * (Apr 2005, 1394TA)
+ */
+int avc_stream_set_format(struct fw_unit *unit, enum avc_general_plug_dir dir,
+ unsigned int pid, u8 *format, unsigned int len);
+int avc_stream_get_format(struct fw_unit *unit,
+ enum avc_general_plug_dir dir, unsigned int pid,
+ u8 *buf, unsigned int *len, unsigned int eid);
+static inline int
+avc_stream_get_format_single(struct fw_unit *unit,
+ enum avc_general_plug_dir dir, unsigned int pid,
+ u8 *buf, unsigned int *len)
+{
+ return avc_stream_get_format(unit, dir, pid, buf, len, 0xff);
+}
+static inline int
+avc_stream_get_format_list(struct fw_unit *unit,
+ enum avc_general_plug_dir dir, unsigned int pid,
+ u8 *buf, unsigned int *len,
+ unsigned int eid)
+{
+ return avc_stream_get_format(unit, dir, pid, buf, len, eid);
+}
+
+/*
+ * AV/C Digital Interface Command Set General Specification 4.2
+ * (Sep 2004, 1394TA)
+ */
+int avc_general_inquiry_sig_fmt(struct fw_unit *unit, unsigned int rate,
+ enum avc_general_plug_dir dir,
+ unsigned short pid);
+
+int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
+ struct amdtp_stream *stream);
+int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw,
+ struct amdtp_stream *stream,
+ unsigned int rate, unsigned int pcm_channels);
+void snd_oxfw_stream_stop_simplex(struct snd_oxfw *oxfw,
+ struct amdtp_stream *stream);
+void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw,
+ struct amdtp_stream *stream);
+void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw,
+ struct amdtp_stream *stream);
+
+struct snd_oxfw_stream_formation {
+ unsigned int rate;
+ unsigned int pcm;
+ unsigned int midi;
+};
+int snd_oxfw_stream_parse_format(u8 *format,
+ struct snd_oxfw_stream_formation *formation);
+int snd_oxfw_stream_get_current_formation(struct snd_oxfw *oxfw,
+ enum avc_general_plug_dir dir,
+ struct snd_oxfw_stream_formation *formation);
+
+int snd_oxfw_stream_discover(struct snd_oxfw *oxfw);
+
+void snd_oxfw_stream_lock_changed(struct snd_oxfw *oxfw);
+int snd_oxfw_stream_lock_try(struct snd_oxfw *oxfw);
+void snd_oxfw_stream_lock_release(struct snd_oxfw *oxfw);
+
+int snd_oxfw_create_pcm(struct snd_oxfw *oxfw);
+
+int snd_oxfw_create_mixer(struct snd_oxfw *oxfw);
+
+void snd_oxfw_proc_init(struct snd_oxfw *oxfw);
+
+int snd_oxfw_create_midi(struct snd_oxfw *oxfw);
+
+int snd_oxfw_create_hwdep(struct snd_oxfw *oxfw);
diff --git a/sound/firewire/speakers.c b/sound/firewire/speakers.c
deleted file mode 100644
index 768d40ddfebb..000000000000
--- a/sound/firewire/speakers.c
+++ /dev/null
@@ -1,792 +0,0 @@
-/*
- * OXFW970-based speakers driver
- *
- * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
- * Licensed under the terms of the GNU General Public License, version 2.
- */
-
-#include <linux/device.h>
-#include <linux/firewire.h>
-#include <linux/firewire-constants.h>
-#include <linux/module.h>
-#include <linux/mod_devicetable.h>
-#include <linux/mutex.h>
-#include <linux/slab.h>
-#include <sound/control.h>
-#include <sound/core.h>
-#include <sound/initval.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include "cmp.h"
-#include "fcp.h"
-#include "amdtp.h"
-#include "lib.h"
-
-#define OXFORD_FIRMWARE_ID_ADDRESS (CSR_REGISTER_BASE + 0x50000)
-/* 0x970?vvvv or 0x971?vvvv, where vvvv = firmware version */
-
-#define OXFORD_HARDWARE_ID_ADDRESS (CSR_REGISTER_BASE + 0x90020)
-#define OXFORD_HARDWARE_ID_OXFW970 0x39443841
-#define OXFORD_HARDWARE_ID_OXFW971 0x39373100
-
-#define VENDOR_GRIFFIN 0x001292
-#define VENDOR_LACIE 0x00d04b
-
-#define SPECIFIER_1394TA 0x00a02d
-#define VERSION_AVC 0x010001
-
-struct device_info {
- const char *driver_name;
- const char *short_name;
- const char *long_name;
- int (*pcm_constraints)(struct snd_pcm_runtime *runtime);
- unsigned int mixer_channels;
- u8 mute_fb_id;
- u8 volume_fb_id;
-};
-
-struct fwspk {
- struct snd_card *card;
- struct fw_unit *unit;
- const struct device_info *device_info;
- struct mutex mutex;
- struct cmp_connection connection;
- struct amdtp_stream stream;
- bool mute;
- s16 volume[6];
- s16 volume_min;
- s16 volume_max;
-};
-
-MODULE_DESCRIPTION("FireWire speakers driver");
-MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
-MODULE_LICENSE("GPL v2");
-
-static int firewave_rate_constraint(struct snd_pcm_hw_params *params,
- struct snd_pcm_hw_rule *rule)
-{
- static unsigned int stereo_rates[] = { 48000, 96000 };
- struct snd_interval *channels =
- hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
- struct snd_interval *rate =
- hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
-
- /* two channels work only at 48/96 kHz */
- if (snd_interval_max(channels) < 6)
- return snd_interval_list(rate, 2, stereo_rates, 0);
- return 0;
-}
-
-static int firewave_channels_constraint(struct snd_pcm_hw_params *params,
- struct snd_pcm_hw_rule *rule)
-{
- static const struct snd_interval all_channels = { .min = 6, .max = 6 };
- struct snd_interval *rate =
- hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
- struct snd_interval *channels =
- hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
-
- /* 32/44.1 kHz work only with all six channels */
- if (snd_interval_max(rate) < 48000)
- return snd_interval_refine(channels, &all_channels);
- return 0;
-}
-
-static int firewave_constraints(struct snd_pcm_runtime *runtime)
-{
- static unsigned int channels_list[] = { 2, 6 };
- static struct snd_pcm_hw_constraint_list channels_list_constraint = {
- .count = 2,
- .list = channels_list,
- };
- int err;
-
- runtime->hw.rates = SNDRV_PCM_RATE_32000 |
- SNDRV_PCM_RATE_44100 |
- SNDRV_PCM_RATE_48000 |
- SNDRV_PCM_RATE_96000;
- runtime->hw.channels_max = 6;
-
- err = snd_pcm_hw_constraint_list(runtime, 0,
- SNDRV_PCM_HW_PARAM_CHANNELS,
- &channels_list_constraint);
- if (err < 0)
- return err;
- err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
- firewave_rate_constraint, NULL,
- SNDRV_PCM_HW_PARAM_CHANNELS, -1);
- if (err < 0)
- return err;
- err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
- firewave_channels_constraint, NULL,
- SNDRV_PCM_HW_PARAM_RATE, -1);
- if (err < 0)
- return err;
-
- return 0;
-}
-
-static int lacie_speakers_constraints(struct snd_pcm_runtime *runtime)
-{
- runtime->hw.rates = SNDRV_PCM_RATE_32000 |
- SNDRV_PCM_RATE_44100 |
- SNDRV_PCM_RATE_48000 |
- SNDRV_PCM_RATE_88200 |
- SNDRV_PCM_RATE_96000;
-
- return 0;
-}
-
-static int fwspk_open(struct snd_pcm_substream *substream)
-{
- static const struct snd_pcm_hardware hardware = {
- .info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_BATCH |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_BLOCK_TRANSFER,
- .formats = AMDTP_OUT_PCM_FORMAT_BITS,
- .channels_min = 2,
- .channels_max = 2,
- .buffer_bytes_max = 4 * 1024 * 1024,
- .period_bytes_min = 1,
- .period_bytes_max = UINT_MAX,
- .periods_min = 1,
- .periods_max = UINT_MAX,
- };
- struct fwspk *fwspk = substream->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
- int err;
-
- runtime->hw = hardware;
-
- err = fwspk->device_info->pcm_constraints(runtime);
- if (err < 0)
- return err;
- err = snd_pcm_limit_hw_rates(runtime);
- if (err < 0)
- return err;
-
- err = amdtp_stream_add_pcm_hw_constraints(&fwspk->stream, runtime);
- if (err < 0)
- return err;
-
- return 0;
-}
-
-static int fwspk_close(struct snd_pcm_substream *substream)
-{
- return 0;
-}
-
-static void fwspk_stop_stream(struct fwspk *fwspk)
-{
- if (amdtp_stream_running(&fwspk->stream)) {
- amdtp_stream_stop(&fwspk->stream);
- cmp_connection_break(&fwspk->connection);
- }
-}
-
-static int fwspk_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- struct fwspk *fwspk = substream->private_data;
- int err;
-
- mutex_lock(&fwspk->mutex);
- fwspk_stop_stream(fwspk);
- mutex_unlock(&fwspk->mutex);
-
- err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- goto error;
-
- amdtp_stream_set_parameters(&fwspk->stream,
- params_rate(hw_params),
- params_channels(hw_params),
- 0);
-
- amdtp_stream_set_pcm_format(&fwspk->stream,
- params_format(hw_params));
-
- err = avc_general_set_sig_fmt(fwspk->unit, params_rate(hw_params),
- AVC_GENERAL_PLUG_DIR_IN, 0);
- if (err < 0) {
- dev_err(&fwspk->unit->device, "failed to set sample rate\n");
- goto err_buffer;
- }
-
- return 0;
-
-err_buffer:
- snd_pcm_lib_free_vmalloc_buffer(substream);
-error:
- return err;
-}
-
-static int fwspk_hw_free(struct snd_pcm_substream *substream)
-{
- struct fwspk *fwspk = substream->private_data;
-
- mutex_lock(&fwspk->mutex);
- fwspk_stop_stream(fwspk);
- mutex_unlock(&fwspk->mutex);
-
- return snd_pcm_lib_free_vmalloc_buffer(substream);
-}
-
-static int fwspk_prepare(struct snd_pcm_substream *substream)
-{
- struct fwspk *fwspk = substream->private_data;
- int err;
-
- mutex_lock(&fwspk->mutex);
-
- if (amdtp_streaming_error(&fwspk->stream))
- fwspk_stop_stream(fwspk);
-
- if (!amdtp_stream_running(&fwspk->stream)) {
- err = cmp_connection_establish(&fwspk->connection,
- amdtp_stream_get_max_payload(&fwspk->stream));
- if (err < 0)
- goto err_mutex;
-
- err = amdtp_stream_start(&fwspk->stream,
- fwspk->connection.resources.channel,
- fwspk->connection.speed);
- if (err < 0)
- goto err_connection;
- }
-
- mutex_unlock(&fwspk->mutex);
-
- amdtp_stream_pcm_prepare(&fwspk->stream);
-
- return 0;
-
-err_connection:
- cmp_connection_break(&fwspk->connection);
-err_mutex:
- mutex_unlock(&fwspk->mutex);
-
- return err;
-}
-
-static int fwspk_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct fwspk *fwspk = substream->private_data;
- struct snd_pcm_substream *pcm;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- pcm = substream;
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- pcm = NULL;
- break;
- default:
- return -EINVAL;
- }
- amdtp_stream_pcm_trigger(&fwspk->stream, pcm);
- return 0;
-}
-
-static snd_pcm_uframes_t fwspk_pointer(struct snd_pcm_substream *substream)
-{
- struct fwspk *fwspk = substream->private_data;
-
- return amdtp_stream_pcm_pointer(&fwspk->stream);
-}
-
-static int fwspk_create_pcm(struct fwspk *fwspk)
-{
- static struct snd_pcm_ops ops = {
- .open = fwspk_open,
- .close = fwspk_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = fwspk_hw_params,
- .hw_free = fwspk_hw_free,
- .prepare = fwspk_prepare,
- .trigger = fwspk_trigger,
- .pointer = fwspk_pointer,
- .page = snd_pcm_lib_get_vmalloc_page,
- .mmap = snd_pcm_lib_mmap_vmalloc,
- };
- struct snd_pcm *pcm;
- int err;
-
- err = snd_pcm_new(fwspk->card, "OXFW970", 0, 1, 0, &pcm);
- if (err < 0)
- return err;
- pcm->private_data = fwspk;
- strcpy(pcm->name, fwspk->device_info->short_name);
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ops);
- return 0;
-}
-
-enum control_action { CTL_READ, CTL_WRITE };
-enum control_attribute {
- CTL_MIN = 0x02,
- CTL_MAX = 0x03,
- CTL_CURRENT = 0x10,
-};
-
-static int fwspk_mute_command(struct fwspk *fwspk, bool *value,
- enum control_action action)
-{
- u8 *buf;
- u8 response_ok;
- int err;
-
- buf = kmalloc(11, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- if (action == CTL_READ) {
- buf[0] = 0x01; /* AV/C, STATUS */
- response_ok = 0x0c; /* STABLE */
- } else {
- buf[0] = 0x00; /* AV/C, CONTROL */
- response_ok = 0x09; /* ACCEPTED */
- }
- buf[1] = 0x08; /* audio unit 0 */
- buf[2] = 0xb8; /* FUNCTION BLOCK */
- buf[3] = 0x81; /* function block type: feature */
- buf[4] = fwspk->device_info->mute_fb_id; /* function block ID */
- buf[5] = 0x10; /* control attribute: current */
- buf[6] = 0x02; /* selector length */
- buf[7] = 0x00; /* audio channel number */
- buf[8] = 0x01; /* control selector: mute */
- buf[9] = 0x01; /* control data length */
- if (action == CTL_READ)
- buf[10] = 0xff;
- else
- buf[10] = *value ? 0x70 : 0x60;
-
- err = fcp_avc_transaction(fwspk->unit, buf, 11, buf, 11, 0x3fe);
- if (err < 0)
- goto error;
- if (err < 11) {
- dev_err(&fwspk->unit->device, "short FCP response\n");
- err = -EIO;
- goto error;
- }
- if (buf[0] != response_ok) {
- dev_err(&fwspk->unit->device, "mute command failed\n");
- err = -EIO;
- goto error;
- }
- if (action == CTL_READ)
- *value = buf[10] == 0x70;
-
- err = 0;
-
-error:
- kfree(buf);
-
- return err;
-}
-
-static int fwspk_volume_command(struct fwspk *fwspk, s16 *value,
- unsigned int channel,
- enum control_attribute attribute,
- enum control_action action)
-{
- u8 *buf;
- u8 response_ok;
- int err;
-
- buf = kmalloc(12, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- if (action == CTL_READ) {
- buf[0] = 0x01; /* AV/C, STATUS */
- response_ok = 0x0c; /* STABLE */
- } else {
- buf[0] = 0x00; /* AV/C, CONTROL */
- response_ok = 0x09; /* ACCEPTED */
- }
- buf[1] = 0x08; /* audio unit 0 */
- buf[2] = 0xb8; /* FUNCTION BLOCK */
- buf[3] = 0x81; /* function block type: feature */
- buf[4] = fwspk->device_info->volume_fb_id; /* function block ID */
- buf[5] = attribute; /* control attribute */
- buf[6] = 0x02; /* selector length */
- buf[7] = channel; /* audio channel number */
- buf[8] = 0x02; /* control selector: volume */
- buf[9] = 0x02; /* control data length */
- if (action == CTL_READ) {
- buf[10] = 0xff;
- buf[11] = 0xff;
- } else {
- buf[10] = *value >> 8;
- buf[11] = *value;
- }
-
- err = fcp_avc_transaction(fwspk->unit, buf, 12, buf, 12, 0x3fe);
- if (err < 0)
- goto error;
- if (err < 12) {
- dev_err(&fwspk->unit->device, "short FCP response\n");
- err = -EIO;
- goto error;
- }
- if (buf[0] != response_ok) {
- dev_err(&fwspk->unit->device, "volume command failed\n");
- err = -EIO;
- goto error;
- }
- if (action == CTL_READ)
- *value = (buf[10] << 8) | buf[11];
-
- err = 0;
-
-error:
- kfree(buf);
-
- return err;
-}
-
-static int fwspk_mute_get(struct snd_kcontrol *control,
- struct snd_ctl_elem_value *value)
-{
- struct fwspk *fwspk = control->private_data;
-
- value->value.integer.value[0] = !fwspk->mute;
-
- return 0;
-}
-
-static int fwspk_mute_put(struct snd_kcontrol *control,
- struct snd_ctl_elem_value *value)
-{
- struct fwspk *fwspk = control->private_data;
- bool mute;
- int err;
-
- mute = !value->value.integer.value[0];
-
- if (mute == fwspk->mute)
- return 0;
-
- err = fwspk_mute_command(fwspk, &mute, CTL_WRITE);
- if (err < 0)
- return err;
- fwspk->mute = mute;
-
- return 1;
-}
-
-static int fwspk_volume_info(struct snd_kcontrol *control,
- struct snd_ctl_elem_info *info)
-{
- struct fwspk *fwspk = control->private_data;
-
- info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- info->count = fwspk->device_info->mixer_channels;
- info->value.integer.min = fwspk->volume_min;
- info->value.integer.max = fwspk->volume_max;
-
- return 0;
-}
-
-static const u8 channel_map[6] = { 0, 1, 4, 5, 2, 3 };
-
-static int fwspk_volume_get(struct snd_kcontrol *control,
- struct snd_ctl_elem_value *value)
-{
- struct fwspk *fwspk = control->private_data;
- unsigned int i;
-
- for (i = 0; i < fwspk->device_info->mixer_channels; ++i)
- value->value.integer.value[channel_map[i]] = fwspk->volume[i];
-
- return 0;
-}
-
-static int fwspk_volume_put(struct snd_kcontrol *control,
- struct snd_ctl_elem_value *value)
-{
- struct fwspk *fwspk = control->private_data;
- unsigned int i, changed_channels;
- bool equal_values = true;
- s16 volume;
- int err;
-
- for (i = 0; i < fwspk->device_info->mixer_channels; ++i) {
- if (value->value.integer.value[i] < fwspk->volume_min ||
- value->value.integer.value[i] > fwspk->volume_max)
- return -EINVAL;
- if (value->value.integer.value[i] !=
- value->value.integer.value[0])
- equal_values = false;
- }
-
- changed_channels = 0;
- for (i = 0; i < fwspk->device_info->mixer_channels; ++i)
- if (value->value.integer.value[channel_map[i]] !=
- fwspk->volume[i])
- changed_channels |= 1 << (i + 1);
-
- if (equal_values && changed_channels != 0)
- changed_channels = 1 << 0;
-
- for (i = 0; i <= fwspk->device_info->mixer_channels; ++i) {
- volume = value->value.integer.value[channel_map[i ? i - 1 : 0]];
- if (changed_channels & (1 << i)) {
- err = fwspk_volume_command(fwspk, &volume, i,
- CTL_CURRENT, CTL_WRITE);
- if (err < 0)
- return err;
- }
- if (i > 0)
- fwspk->volume[i - 1] = volume;
- }
-
- return changed_channels != 0;
-}
-
-static int fwspk_create_mixer(struct fwspk *fwspk)
-{
- static const struct snd_kcontrol_new controls[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "PCM Playback Switch",
- .info = snd_ctl_boolean_mono_info,
- .get = fwspk_mute_get,
- .put = fwspk_mute_put,
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "PCM Playback Volume",
- .info = fwspk_volume_info,
- .get = fwspk_volume_get,
- .put = fwspk_volume_put,
- },
- };
- unsigned int i, first_ch;
- int err;
-
- err = fwspk_volume_command(fwspk, &fwspk->volume_min,
- 0, CTL_MIN, CTL_READ);
- if (err < 0)
- return err;
- err = fwspk_volume_command(fwspk, &fwspk->volume_max,
- 0, CTL_MAX, CTL_READ);
- if (err < 0)
- return err;
-
- err = fwspk_mute_command(fwspk, &fwspk->mute, CTL_READ);
- if (err < 0)
- return err;
-
- first_ch = fwspk->device_info->mixer_channels == 1 ? 0 : 1;
- for (i = 0; i < fwspk->device_info->mixer_channels; ++i) {
- err = fwspk_volume_command(fwspk, &fwspk->volume[i],
- first_ch + i, CTL_CURRENT, CTL_READ);
- if (err < 0)
- return err;
- }
-
- for (i = 0; i < ARRAY_SIZE(controls); ++i) {
- err = snd_ctl_add(fwspk->card,
- snd_ctl_new1(&controls[i], fwspk));
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
-static u32 fwspk_read_firmware_version(struct fw_unit *unit)
-{
- __be32 data;
- int err;
-
- err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
- OXFORD_FIRMWARE_ID_ADDRESS, &data, 4, 0);
- return err >= 0 ? be32_to_cpu(data) : 0;
-}
-
-static void fwspk_card_free(struct snd_card *card)
-{
- struct fwspk *fwspk = card->private_data;
-
- amdtp_stream_destroy(&fwspk->stream);
- cmp_connection_destroy(&fwspk->connection);
- fw_unit_put(fwspk->unit);
- mutex_destroy(&fwspk->mutex);
-}
-
-static int fwspk_probe(struct fw_unit *unit,
- const struct ieee1394_device_id *id)
-{
- struct fw_device *fw_dev = fw_parent_device(unit);
- struct snd_card *card;
- struct fwspk *fwspk;
- u32 firmware;
- int err;
-
- err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
- sizeof(*fwspk), &card);
- if (err < 0)
- return err;
-
- fwspk = card->private_data;
- fwspk->card = card;
- mutex_init(&fwspk->mutex);
- fwspk->unit = fw_unit_get(unit);
- fwspk->device_info = (const struct device_info *)id->driver_data;
-
- err = cmp_connection_init(&fwspk->connection, unit, CMP_INPUT, 0);
- if (err < 0)
- goto err_unit;
-
- err = amdtp_stream_init(&fwspk->stream, unit, AMDTP_OUT_STREAM,
- CIP_NONBLOCKING);
- if (err < 0)
- goto err_connection;
-
- card->private_free = fwspk_card_free;
-
- strcpy(card->driver, fwspk->device_info->driver_name);
- strcpy(card->shortname, fwspk->device_info->short_name);
- firmware = fwspk_read_firmware_version(unit);
- snprintf(card->longname, sizeof(card->longname),
- "%s (OXFW%x %04x), GUID %08x%08x at %s, S%d",
- fwspk->device_info->long_name,
- firmware >> 20, firmware & 0xffff,
- fw_dev->config_rom[3], fw_dev->config_rom[4],
- dev_name(&unit->device), 100 << fw_dev->max_speed);
- strcpy(card->mixername, "OXFW970");
-
- err = fwspk_create_pcm(fwspk);
- if (err < 0)
- goto error;
-
- err = fwspk_create_mixer(fwspk);
- if (err < 0)
- goto error;
-
- err = snd_card_register(card);
- if (err < 0)
- goto error;
-
- dev_set_drvdata(&unit->device, fwspk);
-
- return 0;
-
-err_connection:
- cmp_connection_destroy(&fwspk->connection);
-err_unit:
- fw_unit_put(fwspk->unit);
- mutex_destroy(&fwspk->mutex);
-error:
- snd_card_free(card);
- return err;
-}
-
-static void fwspk_bus_reset(struct fw_unit *unit)
-{
- struct fwspk *fwspk = dev_get_drvdata(&unit->device);
-
- fcp_bus_reset(fwspk->unit);
-
- if (cmp_connection_update(&fwspk->connection) < 0) {
- amdtp_stream_pcm_abort(&fwspk->stream);
- mutex_lock(&fwspk->mutex);
- fwspk_stop_stream(fwspk);
- mutex_unlock(&fwspk->mutex);
- return;
- }
-
- amdtp_stream_update(&fwspk->stream);
-}
-
-static void fwspk_remove(struct fw_unit *unit)
-{
- struct fwspk *fwspk = dev_get_drvdata(&unit->device);
-
- amdtp_stream_pcm_abort(&fwspk->stream);
- snd_card_disconnect(fwspk->card);
-
- mutex_lock(&fwspk->mutex);
- fwspk_stop_stream(fwspk);
- mutex_unlock(&fwspk->mutex);
-
- snd_card_free_when_closed(fwspk->card);
-}
-
-static const struct device_info griffin_firewave = {
- .driver_name = "FireWave",
- .short_name = "FireWave",
- .long_name = "Griffin FireWave Surround",
- .pcm_constraints = firewave_constraints,
- .mixer_channels = 6,
- .mute_fb_id = 0x01,
- .volume_fb_id = 0x02,
-};
-
-static const struct device_info lacie_speakers = {
- .driver_name = "FWSpeakers",
- .short_name = "FireWire Speakers",
- .long_name = "LaCie FireWire Speakers",
- .pcm_constraints = lacie_speakers_constraints,
- .mixer_channels = 1,
- .mute_fb_id = 0x01,
- .volume_fb_id = 0x01,
-};
-
-static const struct ieee1394_device_id fwspk_id_table[] = {
- {
- .match_flags = IEEE1394_MATCH_VENDOR_ID |
- IEEE1394_MATCH_MODEL_ID |
- IEEE1394_MATCH_SPECIFIER_ID |
- IEEE1394_MATCH_VERSION,
- .vendor_id = VENDOR_GRIFFIN,
- .model_id = 0x00f970,
- .specifier_id = SPECIFIER_1394TA,
- .version = VERSION_AVC,
- .driver_data = (kernel_ulong_t)&griffin_firewave,
- },
- {
- .match_flags = IEEE1394_MATCH_VENDOR_ID |
- IEEE1394_MATCH_MODEL_ID |
- IEEE1394_MATCH_SPECIFIER_ID |
- IEEE1394_MATCH_VERSION,
- .vendor_id = VENDOR_LACIE,
- .model_id = 0x00f970,
- .specifier_id = SPECIFIER_1394TA,
- .version = VERSION_AVC,
- .driver_data = (kernel_ulong_t)&lacie_speakers,
- },
- { }
-};
-MODULE_DEVICE_TABLE(ieee1394, fwspk_id_table);
-
-static struct fw_driver fwspk_driver = {
- .driver = {
- .owner = THIS_MODULE,
- .name = KBUILD_MODNAME,
- .bus = &fw_bus_type,
- },
- .probe = fwspk_probe,
- .update = fwspk_bus_reset,
- .remove = fwspk_remove,
- .id_table = fwspk_id_table,
-};
-
-static int __init alsa_fwspk_init(void)
-{
- return driver_register(&fwspk_driver.driver);
-}
-
-static void __exit alsa_fwspk_exit(void)
-{
- driver_unregister(&fwspk_driver.driver);
-}
-
-module_init(alsa_fwspk_init);
-module_exit(alsa_fwspk_exit);
diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c
index f3735e64791c..67dbfde837ab 100644
--- a/sound/i2c/other/ak4xxx-adda.c
+++ b/sound/i2c/other/ak4xxx-adda.c
@@ -465,17 +465,10 @@ static int snd_akm4xxx_stereo_volume_put(struct snd_kcontrol *kcontrol,
static int snd_akm4xxx_deemphasis_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[4] = {
+ static const char * const texts[4] = {
"44.1kHz", "Off", "48kHz", "32kHz",
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 4;
- if (uinfo->value.enumerated.item >= 4)
- uinfo->value.enumerated.item = 3;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 4, texts);
}
static int snd_akm4xxx_deemphasis_get(struct snd_kcontrol *kcontrol,
@@ -570,22 +563,13 @@ static int ak4xxx_capture_source_info(struct snd_kcontrol *kcontrol,
{
struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
int mixer_ch = AK_GET_SHIFT(kcontrol->private_value);
- const char **input_names;
- unsigned int num_names, idx;
+ unsigned int num_names;
num_names = ak4xxx_capture_num_inputs(ak, mixer_ch);
if (!num_names)
return -EINVAL;
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = num_names;
- idx = uinfo->value.enumerated.item;
- if (idx >= num_names)
- return -EINVAL;
- input_names = ak->adc_info[mixer_ch].input_names;
- strlcpy(uinfo->value.enumerated.name, input_names[idx],
- sizeof(uinfo->value.enumerated.name));
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, num_names,
+ ak->adc_info[mixer_ch].input_names);
}
static int ak4xxx_capture_source_get(struct snd_kcontrol *kcontrol,
diff --git a/sound/isa/ad1816a/ad1816a_lib.c b/sound/isa/ad1816a/ad1816a_lib.c
index f0fd98e695e3..01a07986f4a3 100644
--- a/sound/isa/ad1816a/ad1816a_lib.c
+++ b/sound/isa/ad1816a/ad1816a_lib.c
@@ -731,18 +731,12 @@ int snd_ad1816a_timer(struct snd_ad1816a *chip, int device,
static int snd_ad1816a_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[8] = {
+ static const char * const texts[8] = {
"Line", "Mix", "CD", "Synth", "Video",
"Mic", "Phone",
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 2;
- uinfo->value.enumerated.items = 7;
- if (uinfo->value.enumerated.item > 6)
- uinfo->value.enumerated.item = 6;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 2, 7, texts);
}
static int snd_ad1816a_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
diff --git a/sound/isa/es1688/es1688_lib.c b/sound/isa/es1688/es1688_lib.c
index b3b4f15e45ba..b5450143407b 100644
--- a/sound/isa/es1688/es1688_lib.c
+++ b/sound/isa/es1688/es1688_lib.c
@@ -614,8 +614,7 @@ static int snd_es1688_free(struct snd_es1688 *chip)
{
if (chip->hardware != ES1688_HW_UNDEF)
snd_es1688_init(chip, 0);
- if (chip->res_port)
- release_and_free_resource(chip->res_port);
+ release_and_free_resource(chip->res_port);
if (chip->irq >= 0)
free_irq(chip->irq, (void *) chip);
if (chip->dma8 >= 0) {
@@ -762,18 +761,12 @@ int snd_es1688_pcm(struct snd_card *card, struct snd_es1688 *chip,
static int snd_es1688_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[9] = {
+ static const char * const texts[8] = {
"Mic", "Mic Master", "CD", "AOUT",
"Mic1", "Mix", "Line", "Master"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 8;
- if (uinfo->value.enumerated.item > 7)
- uinfo->value.enumerated.item = 7;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 8, texts);
}
static int snd_es1688_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
diff --git a/sound/isa/es18xx.c b/sound/isa/es18xx.c
index 6faaac60161a..b481bb8c31bc 100644
--- a/sound/isa/es18xx.c
+++ b/sound/isa/es18xx.c
@@ -156,6 +156,7 @@ struct snd_es18xx {
#define ES18XX_I2S 0x0200 /* I2S mixer control */
#define ES18XX_MUTEREC 0x0400 /* Record source can be muted */
#define ES18XX_CONTROL 0x0800 /* Has control ports */
+#define ES18XX_GPO_2BIT 0x1000 /* GPO0,1 controlled by PM port */
/* Power Management */
#define ES18XX_PM 0x07
@@ -964,44 +965,28 @@ static int snd_es18xx_capture_close(struct snd_pcm_substream *substream)
static int snd_es18xx_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts5Source[5] = {
+ static const char * const texts5Source[5] = {
"Mic", "CD", "Line", "Master", "Mix"
};
- static char *texts8Source[8] = {
+ static const char * const texts8Source[8] = {
"Mic", "Mic Master", "CD", "AOUT",
"Mic1", "Mix", "Line", "Master"
};
struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
switch (chip->version) {
case 0x1868:
case 0x1878:
- uinfo->value.enumerated.items = 4;
- if (uinfo->value.enumerated.item > 3)
- uinfo->value.enumerated.item = 3;
- strcpy(uinfo->value.enumerated.name,
- texts5Source[uinfo->value.enumerated.item]);
- break;
+ return snd_ctl_enum_info(uinfo, 1, 4, texts5Source);
case 0x1887:
case 0x1888:
- uinfo->value.enumerated.items = 5;
- if (uinfo->value.enumerated.item > 4)
- uinfo->value.enumerated.item = 4;
- strcpy(uinfo->value.enumerated.name, texts5Source[uinfo->value.enumerated.item]);
- break;
+ return snd_ctl_enum_info(uinfo, 1, 5, texts5Source);
case 0x1869: /* DS somewhat contradictory for 1869: could be be 5 or 8 */
case 0x1879:
- uinfo->value.enumerated.items = 8;
- if (uinfo->value.enumerated.item > 7)
- uinfo->value.enumerated.item = 7;
- strcpy(uinfo->value.enumerated.name, texts8Source[uinfo->value.enumerated.item]);
- break;
+ return snd_ctl_enum_info(uinfo, 1, 8, texts8Source);
default:
return -EINVAL;
}
- return 0;
}
static int snd_es18xx_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -1136,11 +1121,14 @@ static int snd_es18xx_reg_read(struct snd_es18xx *chip, unsigned char reg)
return snd_es18xx_read(chip, reg);
}
-#define ES18XX_SINGLE(xname, xindex, reg, shift, mask, invert) \
+#define ES18XX_SINGLE(xname, xindex, reg, shift, mask, flags) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
.info = snd_es18xx_info_single, \
.get = snd_es18xx_get_single, .put = snd_es18xx_put_single, \
- .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
+ .private_value = reg | (shift << 8) | (mask << 16) | (flags << 24) }
+
+#define ES18XX_FL_INVERT (1 << 0)
+#define ES18XX_FL_PMPORT (1 << 1)
static int snd_es18xx_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
@@ -1159,10 +1147,14 @@ static int snd_es18xx_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_e
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
int mask = (kcontrol->private_value >> 16) & 0xff;
- int invert = (kcontrol->private_value >> 24) & 0xff;
+ int invert = (kcontrol->private_value >> 24) & ES18XX_FL_INVERT;
+ int pm_port = (kcontrol->private_value >> 24) & ES18XX_FL_PMPORT;
int val;
-
- val = snd_es18xx_reg_read(chip, reg);
+
+ if (pm_port)
+ val = inb(chip->port + ES18XX_PM);
+ else
+ val = snd_es18xx_reg_read(chip, reg);
ucontrol->value.integer.value[0] = (val >> shift) & mask;
if (invert)
ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
@@ -1175,7 +1167,8 @@ static int snd_es18xx_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_e
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
int mask = (kcontrol->private_value >> 16) & 0xff;
- int invert = (kcontrol->private_value >> 24) & 0xff;
+ int invert = (kcontrol->private_value >> 24) & ES18XX_FL_INVERT;
+ int pm_port = (kcontrol->private_value >> 24) & ES18XX_FL_PMPORT;
unsigned char val;
val = (ucontrol->value.integer.value[0] & mask);
@@ -1183,6 +1176,15 @@ static int snd_es18xx_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_e
val = mask - val;
mask <<= shift;
val <<= shift;
+ if (pm_port) {
+ unsigned char cur = inb(chip->port + ES18XX_PM);
+
+ if ((cur & mask) == val)
+ return 0;
+ outb((cur & ~mask) | val, chip->port + ES18XX_PM);
+ return 1;
+ }
+
return snd_es18xx_reg_bits(chip, reg, mask, val) != val;
}
@@ -1304,7 +1306,7 @@ static struct snd_kcontrol_new snd_es18xx_opt_speaker =
ES18XX_SINGLE("Beep Playback Volume", 0, 0x3c, 0, 7, 0);
static struct snd_kcontrol_new snd_es18xx_opt_1869[] = {
-ES18XX_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1),
+ES18XX_SINGLE("Capture Switch", 0, 0x1c, 4, 1, ES18XX_FL_INVERT),
ES18XX_SINGLE("Video Playback Switch", 0, 0x7f, 0, 1, 0),
ES18XX_DOUBLE("Mono Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0),
ES18XX_DOUBLE("Mono Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0)
@@ -1363,6 +1365,11 @@ static struct snd_kcontrol_new snd_es18xx_hw_volume_controls[] = {
ES18XX_SINGLE("Hardware Master Volume Split", 0, 0x64, 7, 1, 0),
};
+static struct snd_kcontrol_new snd_es18xx_opt_gpo_2bit[] = {
+ES18XX_SINGLE("GPO0 Switch", 0, ES18XX_PM, 0, 1, ES18XX_FL_PMPORT),
+ES18XX_SINGLE("GPO1 Switch", 0, ES18XX_PM, 1, 1, ES18XX_FL_PMPORT),
+};
+
static int snd_es18xx_config_read(struct snd_es18xx *chip, unsigned char reg)
{
int data;
@@ -1629,10 +1636,10 @@ static int snd_es18xx_probe(struct snd_es18xx *chip,
switch (chip->version) {
case 0x1868:
- chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_CONTROL;
+ chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_CONTROL | ES18XX_GPO_2BIT;
break;
case 0x1869:
- chip->caps = ES18XX_PCM2 | ES18XX_SPATIALIZER | ES18XX_RECMIX | ES18XX_NEW_RATE | ES18XX_AUXB | ES18XX_MONO | ES18XX_MUTEREC | ES18XX_CONTROL | ES18XX_HWV;
+ chip->caps = ES18XX_PCM2 | ES18XX_SPATIALIZER | ES18XX_RECMIX | ES18XX_NEW_RATE | ES18XX_AUXB | ES18XX_MONO | ES18XX_MUTEREC | ES18XX_CONTROL | ES18XX_HWV | ES18XX_GPO_2BIT;
break;
case 0x1878:
chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_I2S | ES18XX_CONTROL;
@@ -1642,7 +1649,7 @@ static int snd_es18xx_probe(struct snd_es18xx *chip,
break;
case 0x1887:
case 0x1888:
- chip->caps = ES18XX_PCM2 | ES18XX_RECMIX | ES18XX_AUXB | ES18XX_DUPLEX_SAME;
+ chip->caps = ES18XX_PCM2 | ES18XX_RECMIX | ES18XX_AUXB | ES18XX_DUPLEX_SAME | ES18XX_GPO_2BIT;
break;
default:
snd_printk(KERN_ERR "[0x%lx] unsupported chip ES%x\n",
@@ -1944,6 +1951,15 @@ static int snd_es18xx_mixer(struct snd_card *card)
return err;
}
}
+ if (chip->caps & ES18XX_GPO_2BIT) {
+ for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_opt_gpo_2bit); idx++) {
+ err = snd_ctl_add(card,
+ snd_ctl_new1(&snd_es18xx_opt_gpo_2bit[idx],
+ chip));
+ if (err < 0)
+ return err;
+ }
+ }
return 0;
}
diff --git a/sound/isa/msnd/msnd_pinnacle_mixer.c b/sound/isa/msnd/msnd_pinnacle_mixer.c
index 031dc69b7470..17e49a071af4 100644
--- a/sound/isa/msnd/msnd_pinnacle_mixer.c
+++ b/sound/isa/msnd/msnd_pinnacle_mixer.c
@@ -55,20 +55,13 @@
static int snd_msndmix_info_mux(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[3] = {
+ static const char * const texts[3] = {
"Analog", "MASS", "SPDIF",
};
struct snd_msnd *chip = snd_kcontrol_chip(kcontrol);
unsigned items = test_bit(F_HAVEDIGITAL, &chip->flags) ? 3 : 2;
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = items;
- if (uinfo->value.enumerated.item >= items)
- uinfo->value.enumerated.item = items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, items, texts);
}
static int snd_msndmix_get_mux(struct snd_kcontrol *kcontrol,
diff --git a/sound/isa/sb/emu8000_synth.c b/sound/isa/sb/emu8000_synth.c
index 4e3fcfb15ad4..95b39beb61c1 100644
--- a/sound/isa/sb/emu8000_synth.c
+++ b/sound/isa/sb/emu8000_synth.c
@@ -105,8 +105,7 @@ static int snd_emu8000_delete_device(struct snd_seq_device *dev)
snd_device_free(dev->card, hw->pcm);
if (hw->emu)
snd_emux_free(hw->emu);
- if (hw->memhdr)
- snd_util_memhdr_free(hw->memhdr);
+ snd_util_memhdr_free(hw->memhdr);
hw->emu = NULL;
hw->memhdr = NULL;
return 0;
diff --git a/sound/isa/sb/sb16_main.c b/sound/isa/sb/sb16_main.c
index 0bbcd4714d28..72b10f4f3e70 100644
--- a/sound/isa/sb/sb16_main.c
+++ b/sound/isa/sb/sb16_main.c
@@ -702,17 +702,11 @@ static int snd_sb16_get_dma_mode(struct snd_sb *chip)
static int snd_sb16_dma_control_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[3] = {
+ static const char * const texts[3] = {
"Auto", "Playback", "Capture"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item > 2)
- uinfo->value.enumerated.item = 2;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int snd_sb16_dma_control_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
diff --git a/sound/isa/sb/sb_common.c b/sound/isa/sb/sb_common.c
index 3ef990602cdd..f22b4480828e 100644
--- a/sound/isa/sb/sb_common.c
+++ b/sound/isa/sb/sb_common.c
@@ -184,8 +184,7 @@ static int snd_sbdsp_probe(struct snd_sb * chip)
static int snd_sbdsp_free(struct snd_sb *chip)
{
- if (chip->res_port)
- release_and_free_resource(chip->res_port);
+ release_and_free_resource(chip->res_port);
if (chip->irq >= 0)
free_irq(chip->irq, (void *) chip);
#ifdef CONFIG_ISA
diff --git a/sound/isa/sb/sb_mixer.c b/sound/isa/sb/sb_mixer.c
index 1ff78ec9f0ac..e403334a19ad 100644
--- a/sound/isa/sb/sb_mixer.c
+++ b/sound/isa/sb/sb_mixer.c
@@ -182,17 +182,11 @@ static int snd_sbmixer_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
static int snd_dt019x_input_sw_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static const char *texts[5] = {
+ static const char * const texts[5] = {
"CD", "Mic", "Line", "Synth", "Master"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 5;
- if (uinfo->value.enumerated.item > 4)
- uinfo->value.enumerated.item = 4;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 5, texts);
}
static int snd_dt019x_input_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -275,18 +269,11 @@ static int snd_dt019x_input_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl
static int snd_als4k_mono_capture_route_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static const char *texts[3] = {
+ static const char * const texts[3] = {
"L chan only", "R chan only", "L ch/2 + R ch/2"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item > 2)
- uinfo->value.enumerated.item = 2;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int snd_als4k_mono_capture_route_get(struct snd_kcontrol *kcontrol,
@@ -335,17 +322,11 @@ static int snd_als4k_mono_capture_route_put(struct snd_kcontrol *kcontrol,
static int snd_sb8mixer_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static const char *texts[3] = {
+ static const char * const texts[3] = {
"Mic", "CD", "Line"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item > 2)
- uinfo->value.enumerated.item = 2;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
diff --git a/sound/isa/wss/wss_lib.c b/sound/isa/wss/wss_lib.c
index 360b08b03e1d..347bb1bda110 100644
--- a/sound/isa/wss/wss_lib.c
+++ b/sound/isa/wss/wss_lib.c
@@ -1993,25 +1993,20 @@ EXPORT_SYMBOL(snd_wss_timer);
static int snd_wss_info_mux(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[4] = {
+ static const char * const texts[4] = {
"Line", "Aux", "Mic", "Mix"
};
- static char *opl3sa_texts[4] = {
+ static const char * const opl3sa_texts[4] = {
"Line", "CD", "Mic", "Mix"
};
- static char *gusmax_texts[4] = {
+ static const char * const gusmax_texts[4] = {
"Line", "Synth", "Mic", "Mix"
};
- char **ptexts = texts;
+ const char * const *ptexts = texts;
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
if (snd_BUG_ON(!chip->card))
return -EINVAL;
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 2;
- uinfo->value.enumerated.items = 4;
- if (uinfo->value.enumerated.item > 3)
- uinfo->value.enumerated.item = 3;
if (!strcmp(chip->card->driver, "GUS MAX"))
ptexts = gusmax_texts;
switch (chip->hardware) {
@@ -2023,8 +2018,7 @@ static int snd_wss_info_mux(struct snd_kcontrol *kcontrol,
ptexts = opl3sa_texts;
break;
}
- strcpy(uinfo->value.enumerated.name, ptexts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 2, 4, ptexts);
}
static int snd_wss_get_mux(struct snd_kcontrol *kcontrol,
diff --git a/sound/mips/sgio2audio.c b/sound/mips/sgio2audio.c
index 04bb06c03ec8..33b08fcc27a9 100644
--- a/sound/mips/sgio2audio.c
+++ b/sound/mips/sgio2audio.c
@@ -201,17 +201,10 @@ static int sgio2audio_gain_put(struct snd_kcontrol *kcontrol,
static int sgio2audio_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static const char *texts[3] = {
+ static const char * const texts[3] = {
"Cam Mic", "Mic", "Line"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item >= 3)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int sgio2audio_source_get(struct snd_kcontrol *kcontrol,
diff --git a/sound/oss/uart401.c b/sound/oss/uart401.c
index 279bc565ac7e..dae4d4344407 100644
--- a/sound/oss/uart401.c
+++ b/sound/oss/uart401.c
@@ -412,13 +412,10 @@ void unload_uart401(struct address_info *hw_config)
if (!devc->share_irq)
free_irq(devc->irq, devc);
- if (devc)
- {
- kfree(midi_devs[devc->my_dev]->converter);
- kfree(midi_devs[devc->my_dev]);
- kfree(devc);
- devc = NULL;
- }
+ kfree(midi_devs[devc->my_dev]->converter);
+ kfree(midi_devs[devc->my_dev]);
+ kfree(devc);
+
/* This kills midi_devs[x] */
sound_unload_mididev(hw_config->slots[4]);
}
diff --git a/sound/parisc/harmony.c b/sound/parisc/harmony.c
index 4b20be79c1dd..29604a239c44 100644
--- a/sound/parisc/harmony.c
+++ b/sound/parisc/harmony.c
@@ -776,15 +776,9 @@ static int
snd_harmony_captureroute_info(struct snd_kcontrol *kc,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[2] = { "Line", "Mic" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
- return 0;
+ static const char * const texts[2] = { "Line", "Mic" };
+
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int
diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c
index 14ad54b7928c..5ee2f17c287c 100644
--- a/sound/pci/ac97/ac97_codec.c
+++ b/sound/pci/ac97/ac97_codec.c
@@ -463,14 +463,8 @@ static int snd_ac97_info_enum_double(struct snd_kcontrol *kcontrol,
{
struct ac97_enum *e = (struct ac97_enum *)kcontrol->private_value;
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = e->shift_l == e->shift_r ? 1 : 2;
- uinfo->value.enumerated.items = e->mask;
-
- if (uinfo->value.enumerated.item > e->mask - 1)
- uinfo->value.enumerated.item = e->mask - 1;
- strcpy(uinfo->value.enumerated.name, e->texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, e->shift_l == e->shift_r ? 1 : 2,
+ e->mask, e->texts);
}
static int snd_ac97_get_enum_double(struct snd_kcontrol *kcontrol,
diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c
index 991762215417..ceaac1c41906 100644
--- a/sound/pci/ac97/ac97_patch.c
+++ b/sound/pci/ac97/ac97_patch.c
@@ -33,7 +33,8 @@
static struct snd_kcontrol *snd_ac97_find_mixer_ctl(struct snd_ac97 *ac97,
const char *name);
static int snd_ac97_add_vmaster(struct snd_ac97 *ac97, char *name,
- const unsigned int *tlv, const char **slaves);
+ const unsigned int *tlv,
+ const char * const *slaves);
/*
* Chip specific initialization
@@ -81,22 +82,11 @@ static int ac97_update_bits_page(struct snd_ac97 *ac97, unsigned short reg, unsi
/*
* shared line-in/mic controls
*/
-static int ac97_enum_text_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo,
- const char **texts, unsigned int nums)
-{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = nums;
- if (uinfo->value.enumerated.item > nums - 1)
- uinfo->value.enumerated.item = nums - 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
-}
-
static int ac97_surround_jack_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static const char *texts[] = { "Shared", "Independent" };
- return ac97_enum_text_info(kcontrol, uinfo, texts, 2);
+ static const char * const texts[] = { "Shared", "Independent" };
+
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int ac97_surround_jack_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -123,9 +113,9 @@ static int ac97_surround_jack_mode_put(struct snd_kcontrol *kcontrol, struct snd
static int ac97_channel_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static const char *texts[] = { "2ch", "4ch", "6ch", "8ch" };
- return ac97_enum_text_info(kcontrol, uinfo, texts,
- kcontrol->private_value);
+ static const char * const texts[] = { "2ch", "4ch", "6ch", "8ch" };
+
+ return snd_ctl_enum_info(uinfo, 1, kcontrol->private_value, texts);
}
static int ac97_channel_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -240,17 +230,11 @@ static inline int alc850_is_aux_back_surround(struct snd_ac97 *ac97)
static int snd_ac97_ymf7x3_info_speaker(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[3] = {
+ static const char * const texts[3] = {
"Standard", "Small", "Smaller"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item > 2)
- uinfo->value.enumerated.item = 2;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int snd_ac97_ymf7x3_get_speaker(struct snd_kcontrol *kcontrol,
@@ -293,15 +277,9 @@ static const struct snd_kcontrol_new snd_ac97_ymf7x3_controls_speaker =
static int snd_ac97_ymf7x3_spdif_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[2] = { "AC-Link", "A/D Converter" };
+ static const char * const texts[2] = { "AC-Link", "A/D Converter" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int snd_ac97_ymf7x3_spdif_source_get(struct snd_kcontrol *kcontrol,
@@ -401,15 +379,9 @@ static int patch_yamaha_ymf743(struct snd_ac97 *ac97)
There is also a bit to mute S/PDIF output in a vendor-specific register. */
static int snd_ac97_ymf753_spdif_output_pin_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[3] = { "Disabled", "Pin 43", "Pin 48" };
+ static const char * const texts[3] = { "Disabled", "Pin 43", "Pin 48" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item > 2)
- uinfo->value.enumerated.item = 2;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int snd_ac97_ymf753_spdif_output_pin_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -1103,16 +1075,11 @@ static int patch_sigmatel_stac9756(struct snd_ac97 * ac97)
static int snd_ac97_stac9758_output_jack_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[5] = { "Input/Disabled", "Front Output",
+ static const char * const texts[5] = {
+ "Input/Disabled", "Front Output",
"Rear Output", "Center/LFE Output", "Mixer Output" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 5;
- if (uinfo->value.enumerated.item > 4)
- uinfo->value.enumerated.item = 4;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 5, texts);
}
static int snd_ac97_stac9758_output_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -1147,16 +1114,11 @@ static int snd_ac97_stac9758_output_jack_put(struct snd_kcontrol *kcontrol, stru
static int snd_ac97_stac9758_input_jack_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[7] = { "Mic2 Jack", "Mic1 Jack", "Line In Jack",
+ static const char * const texts[7] = {
+ "Mic2 Jack", "Mic1 Jack", "Line In Jack",
"Front Jack", "Rear Jack", "Center/LFE Jack", "Mute" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 7;
- if (uinfo->value.enumerated.item > 6)
- uinfo->value.enumerated.item = 6;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 7, texts);
}
static int snd_ac97_stac9758_input_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -1181,15 +1143,11 @@ static int snd_ac97_stac9758_input_jack_put(struct snd_kcontrol *kcontrol, struc
static int snd_ac97_stac9758_phonesel_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[3] = { "None", "Front Jack", "Rear Jack" };
+ static const char * const texts[3] = {
+ "None", "Front Jack", "Rear Jack"
+ };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item > 2)
- uinfo->value.enumerated.item = 2;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int snd_ac97_stac9758_phonesel_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -1804,15 +1762,9 @@ static int patch_ad1886(struct snd_ac97 * ac97)
static int snd_ac97_ad198x_spdif_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[2] = { "AC-Link", "A/D Converter" };
+ static const char * const texts[2] = { "AC-Link", "A/D Converter" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int snd_ac97_ad198x_spdif_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -1994,15 +1946,9 @@ static int snd_ac97_ad1888_lohpsel_put(struct snd_kcontrol *kcontrol, struct snd
static int snd_ac97_ad1888_downmix_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[3] = {"Off", "6 -> 4", "6 -> 2"};
+ static const char * const texts[3] = {"Off", "6 -> 4", "6 -> 2"};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item > 2)
- uinfo->value.enumerated.item = 2;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int snd_ac97_ad1888_downmix_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -2153,16 +2099,11 @@ static int patch_ad1980(struct snd_ac97 * ac97)
static int snd_ac97_ad1985_vrefout_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[4] = {"High-Z", "3.7 V", "2.25 V", "0 V"};
+ static const char * const texts[4] = {
+ "High-Z", "3.7 V", "2.25 V", "0 V"
+ };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 4;
- if (uinfo->value.enumerated.item > 3)
- uinfo->value.enumerated.item = 3;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 4, texts);
}
static int snd_ac97_ad1985_vrefout_get(struct snd_kcontrol *kcontrol,
@@ -2756,20 +2697,18 @@ static const struct snd_kcontrol_new snd_ac97_controls_alc655[] = {
static int alc655_iec958_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts_655[3] = { "PCM", "Analog In", "IEC958 In" };
- static char *texts_658[4] = { "PCM", "Analog1 In", "Analog2 In", "IEC958 In" };
+ static const char * const texts_655[3] = {
+ "PCM", "Analog In", "IEC958 In"
+ };
+ static const char * const texts_658[4] = {
+ "PCM", "Analog1 In", "Analog2 In", "IEC958 In"
+ };
struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = ac97->spec.dev_flags ? 4 : 3;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- ac97->spec.dev_flags ?
- texts_658[uinfo->value.enumerated.item] :
- texts_655[uinfo->value.enumerated.item]);
- return 0;
+ if (ac97->spec.dev_flags)
+ return snd_ctl_enum_info(uinfo, 1, 4, texts_658);
+ else
+ return snd_ctl_enum_info(uinfo, 1, 3, texts_655);
}
static int alc655_iec958_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -3055,15 +2994,9 @@ static int patch_cm9738(struct snd_ac97 * ac97)
static int snd_ac97_cmedia_spdif_playback_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = { "Analog", "Digital" };
+ static const char * const texts[] = { "Analog", "Digital" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int snd_ac97_cmedia_spdif_playback_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -3235,15 +3168,9 @@ static const struct snd_kcontrol_new snd_ac97_cm9761_controls[] = {
static int cm9761_spdif_out_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = { "AC-Link", "ADC", "SPDIF-In" };
+ static const char * const texts[] = { "AC-Link", "ADC", "SPDIF-In" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item > 2)
- uinfo->value.enumerated.item = 2;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int cm9761_spdif_out_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -3270,7 +3197,9 @@ static int cm9761_spdif_out_source_put(struct snd_kcontrol *kcontrol, struct snd
ucontrol->value.enumerated.item[0] == 1 ? 0x2 : 0);
}
-static const char *cm9761_dac_clock[] = { "AC-Link", "SPDIF-In", "Both" };
+static const char * const cm9761_dac_clock[] = {
+ "AC-Link", "SPDIF-In", "Both"
+};
static const struct ac97_enum cm9761_dac_clock_enum =
AC97_ENUM_SINGLE(AC97_CM9761_SPDIF_CTRL, 9, 3, cm9761_dac_clock);
@@ -3384,7 +3313,9 @@ static int patch_cm9761(struct snd_ac97 *ac97)
#define AC97_CM9780_MULTI_CHAN 0x66
#define AC97_CM9780_SPDIF 0x6c
-static const char *cm9780_ch_select[] = { "Front", "Side", "Center/LFE", "Rear" };
+static const char * const cm9780_ch_select[] = {
+ "Front", "Side", "Center/LFE", "Rear"
+};
static const struct ac97_enum cm9780_ch_select_enum =
AC97_ENUM_SINGLE(AC97_CM9780_MULTI_CHAN, 6, 4, cm9780_ch_select);
static const struct snd_kcontrol_new cm9780_controls[] = {
@@ -3430,7 +3361,7 @@ AC97_SINGLE("Downmix LFE and Center to Front", 0x5a, 12, 1, 0),
AC97_SINGLE("Downmix Surround to Front", 0x5a, 11, 1, 0),
};
-static const char *slave_vols_vt1616[] = {
+static const char * const slave_vols_vt1616[] = {
"Front Playback Volume",
"Surround Playback Volume",
"Center Playback Volume",
@@ -3438,7 +3369,7 @@ static const char *slave_vols_vt1616[] = {
NULL
};
-static const char *slave_sws_vt1616[] = {
+static const char * const slave_sws_vt1616[] = {
"Front Playback Switch",
"Surround Playback Switch",
"Center Playback Switch",
@@ -3459,10 +3390,11 @@ static struct snd_kcontrol *snd_ac97_find_mixer_ctl(struct snd_ac97 *ac97,
/* create a virtual master control and add slaves */
static int snd_ac97_add_vmaster(struct snd_ac97 *ac97, char *name,
- const unsigned int *tlv, const char **slaves)
+ const unsigned int *tlv,
+ const char * const *slaves)
{
struct snd_kcontrol *kctl;
- const char **s;
+ const char * const *s;
int err;
kctl = snd_ctl_make_virtual_master(name, tlv);
@@ -3552,11 +3484,12 @@ static int snd_ac97_vt1617a_smart51_info(struct snd_kcontrol *kcontrol,
* is SM51EN *AND* it's Bit14, not Bit15 so the table is very
* counter-intuitive */
- static const char* texts[] = { "LineIn Mic1", "LineIn Mic1 Mic3",
+ static const char * const texts[] = {"LineIn Mic1", "LineIn Mic1 Mic3",
"Surr LFE/C Mic3", "LineIn LFE/C Mic3",
"LineIn Mic2", "LineIn Mic2 Mic1",
"Surr LFE Mic1", "Surr LFE Mic1 Mic2"};
- return ac97_enum_text_info(kcontrol, uinfo, texts, 8);
+
+ return snd_ctl_enum_info(uinfo, 1, 8, texts);
}
static int snd_ac97_vt1617a_smart51_get(struct snd_kcontrol *kcontrol,
@@ -3685,7 +3618,7 @@ static int patch_vt1617a(struct snd_ac97 * ac97)
struct vt1618_uaj_item {
unsigned short mask;
unsigned short shift;
- const char *items[4];
+ const char * const items[4];
};
/* This list reflects the vt1618 docs for Vendor Defined Register 0x60. */
@@ -3720,9 +3653,8 @@ static struct vt1618_uaj_item vt1618_uaj[3] = {
static int snd_ac97_vt1618_UAJ_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- return ac97_enum_text_info(kcontrol, uinfo,
- vt1618_uaj[kcontrol->private_value].items,
- 4);
+ return snd_ctl_enum_info(uinfo, 1, 4,
+ vt1618_uaj[kcontrol->private_value].items);
}
/* All of the vt1618 Universal Audio Jack twiddlers are on
@@ -3767,9 +3699,9 @@ static int snd_ac97_vt1618_UAJ_put(struct snd_kcontrol *kcontrol,
static int snd_ac97_vt1618_aux_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static const char *txt_aux[] = {"Aux In", "Back Surr Out"};
+ static const char * const txt_aux[] = {"Aux In", "Back Surr Out"};
- return ac97_enum_text_info(kcontrol, uinfo, txt_aux, 2);
+ return snd_ctl_enum_info(uinfo, 1, 2, txt_aux);
}
static int snd_ac97_vt1618_aux_get(struct snd_kcontrol *kcontrol,
diff --git a/sound/pci/ac97/ac97_patch.h b/sound/pci/ac97/ac97_patch.h
index 47bf8dfe8276..d1ce151fe722 100644
--- a/sound/pci/ac97/ac97_patch.h
+++ b/sound/pci/ac97/ac97_patch.h
@@ -49,7 +49,7 @@ struct ac97_enum {
unsigned char shift_l;
unsigned char shift_r;
unsigned short mask;
- const char **texts;
+ const char * const *texts;
};
#define AC97_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xtexts) \
diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c
index 5017176bfaa1..e9273fb2a505 100644
--- a/sound/pci/asihpi/asihpi.c
+++ b/sound/pci/asihpi/asihpi.c
@@ -1,6 +1,6 @@
/*
* Asihpi soundcard
- * Copyright (c) by AudioScience Inc <alsa@audioscience.com>
+ * Copyright (c) by AudioScience Inc <support@audioscience.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -28,7 +28,6 @@
#include "hpioctl.h"
#include "hpicmn.h"
-
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/jiffies.h>
@@ -47,7 +46,7 @@
MODULE_LICENSE("GPL");
MODULE_AUTHOR("AudioScience inc. <support@audioscience.com>");
-MODULE_DESCRIPTION("AudioScience ALSA ASI5000 ASI6000 ASI87xx ASI89xx "
+MODULE_DESCRIPTION("AudioScience ALSA ASI5xxx ASI6xxx ASI87xx ASI89xx "
HPI_VER_STRING);
#if defined CONFIG_SND_DEBUG_VERBOSE
@@ -87,11 +86,11 @@ MODULE_PARM_DESC(enable_hpi_hwdep,
#ifdef KERNEL_ALSA_BUILD
static char *build_info = "Built using headers from kernel source";
module_param(build_info, charp, S_IRUGO);
-MODULE_PARM_DESC(build_info, "built using headers from kernel source");
+MODULE_PARM_DESC(build_info, "Built using headers from kernel source");
#else
static char *build_info = "Built within ALSA source";
module_param(build_info, charp, S_IRUGO);
-MODULE_PARM_DESC(build_info, "built within ALSA source");
+MODULE_PARM_DESC(build_info, "Built within ALSA source");
#endif
/* set to 1 to dump every control from adapter to log */
@@ -110,7 +109,7 @@ static int adapter_fs = DEFAULT_SAMPLERATE;
struct clk_source {
int source;
int index;
- char *name;
+ const char *name;
};
struct clk_cache {
@@ -125,6 +124,16 @@ struct snd_card_asihpi {
struct pci_dev *pci;
struct hpi_adapter *hpi;
+ /* In low latency mode there is only one stream, a pointer to its
+ * private data is stored here on trigger and cleared on stop.
+ * The interrupt handler uses it as a parameter when calling
+ * snd_card_asihpi_timer_function().
+ */
+ struct snd_card_asihpi_pcm *llmode_streampriv;
+ struct tasklet_struct t;
+ void (*pcm_start)(struct snd_pcm_substream *substream);
+ void (*pcm_stop)(struct snd_pcm_substream *substream);
+
u32 h_mixer;
struct clk_cache cc;
@@ -289,21 +298,17 @@ static void print_hwparams(struct snd_pcm_substream *substream,
{
char name[16];
snd_pcm_debug_name(substream, name, sizeof(name));
- snd_printd("%s HWPARAMS\n", name);
- snd_printd(" samplerate %d Hz\n", params_rate(p));
- snd_printd(" channels %d\n", params_channels(p));
- snd_printd(" format %d\n", params_format(p));
- snd_printd(" subformat %d\n", params_subformat(p));
- snd_printd(" buffer %d B\n", params_buffer_bytes(p));
- snd_printd(" period %d B\n", params_period_bytes(p));
- snd_printd(" access %d\n", params_access(p));
- snd_printd(" period_size %d\n", params_period_size(p));
- snd_printd(" periods %d\n", params_periods(p));
- snd_printd(" buffer_size %d\n", params_buffer_size(p));
- snd_printd(" %d B/s\n", params_rate(p) *
- params_channels(p) *
+ snd_printdd("%s HWPARAMS\n", name);
+ snd_printdd(" samplerate=%dHz channels=%d format=%d subformat=%d\n",
+ params_rate(p), params_channels(p),
+ params_format(p), params_subformat(p));
+ snd_printdd(" buffer=%dB period=%dB period_size=%dB periods=%d\n",
+ params_buffer_bytes(p), params_period_bytes(p),
+ params_period_size(p), params_periods(p));
+ snd_printdd(" buffer_size=%d access=%d data_rate=%dB/s\n",
+ params_buffer_size(p), params_access(p),
+ params_rate(p) * params_channels(p) *
snd_pcm_format_width(params_format(p)) / 8);
-
}
static snd_pcm_format_t hpi_to_alsa_formats[] = {
@@ -375,7 +380,7 @@ static void snd_card_asihpi_pcm_samplerates(struct snd_card_asihpi *asihpi,
HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0,
HPI_CONTROL_SAMPLECLOCK, &h_control);
if (err) {
- snd_printk(KERN_ERR
+ dev_err(&asihpi->pci->dev,
"No local sampleclock, err %d\n", err);
}
@@ -481,7 +486,7 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream,
params_buffer_bytes(params), runtime->dma_addr);
if (err == 0) {
snd_printdd(
- "stream_host_buffer_attach succeeded %u %lu\n",
+ "stream_host_buffer_attach success %u %lu\n",
params_buffer_bytes(params),
(unsigned long)runtime->dma_addr);
} else {
@@ -491,12 +496,7 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream,
}
err = hpi_stream_get_info_ex(dpcm->h_stream, NULL,
- &dpcm->hpi_buffer_attached,
- NULL, NULL, NULL);
-
- snd_printdd("stream_host_buffer_attach status 0x%x\n",
- dpcm->hpi_buffer_attached);
-
+ &dpcm->hpi_buffer_attached, NULL, NULL, NULL);
}
bytes_per_sec = params_rate(params) * params_channels(params);
width = snd_pcm_format_width(params_format(params));
@@ -538,7 +538,7 @@ static void snd_card_asihpi_pcm_timer_start(struct snd_pcm_substream *
int expiry;
expiry = HZ / 200;
- /*? (dpcm->period_bytes * HZ / dpcm->bytes_per_sec); */
+
expiry = max(expiry, 1); /* don't let it be zero! */
dpcm->timer.expires = jiffies + expiry;
dpcm->respawn_timer = 1;
@@ -554,6 +554,48 @@ static void snd_card_asihpi_pcm_timer_stop(struct snd_pcm_substream *substream)
del_timer(&dpcm->timer);
}
+static void snd_card_asihpi_pcm_int_start(struct snd_pcm_substream *substream)
+{
+ struct snd_card_asihpi_pcm *dpcm;
+ struct snd_card_asihpi *card;
+
+ BUG_ON(!substream);
+
+ dpcm = (struct snd_card_asihpi_pcm *)substream->runtime->private_data;
+ card = snd_pcm_substream_chip(substream);
+
+ BUG_ON(in_interrupt());
+ tasklet_disable(&card->t);
+ card->llmode_streampriv = dpcm;
+ tasklet_enable(&card->t);
+
+ hpi_handle_error(hpi_adapter_set_property(card->hpi->adapter->index,
+ HPI_ADAPTER_PROPERTY_IRQ_RATE,
+ card->update_interval_frames, 0));
+}
+
+static void snd_card_asihpi_pcm_int_stop(struct snd_pcm_substream *substream)
+{
+ struct snd_card_asihpi_pcm *dpcm;
+ struct snd_card_asihpi *card;
+
+ BUG_ON(!substream);
+
+ dpcm = (struct snd_card_asihpi_pcm *)substream->runtime->private_data;
+ card = snd_pcm_substream_chip(substream);
+
+ hpi_handle_error(hpi_adapter_set_property(card->hpi->adapter->index,
+ HPI_ADAPTER_PROPERTY_IRQ_RATE, 0, 0));
+
+ if (in_interrupt())
+ card->llmode_streampriv = NULL;
+ else {
+ tasklet_disable(&card->t);
+ card->llmode_streampriv = NULL;
+ tasklet_enable(&card->t);
+ }
+}
+
static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
int cmd)
{
@@ -564,10 +606,10 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
char name[16];
snd_pcm_debug_name(substream, name, sizeof(name));
- snd_printdd("%s trigger\n", name);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
+ snd_printdd("%s trigger start\n", name);
snd_pcm_group_for_each_entry(s, substream) {
struct snd_pcm_runtime *runtime = s->runtime;
struct snd_card_asihpi_pcm *ds = runtime->private_data;
@@ -588,7 +630,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
* data??
*/
unsigned int preload = ds->period_bytes * 1;
- snd_printddd("%d preload x%x\n", s->number, preload);
+ snd_printddd("%d preload %d\n", s->number, preload);
hpi_handle_error(hpi_outstream_write_buf(
ds->h_stream,
&runtime->dma_area[0],
@@ -611,16 +653,16 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
} else
break;
}
- snd_printdd("start\n");
/* start the master stream */
- snd_card_asihpi_pcm_timer_start(substream);
+ card->pcm_start(substream);
if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE) ||
!card->can_dma)
hpi_handle_error(hpi_stream_start(dpcm->h_stream));
break;
case SNDRV_PCM_TRIGGER_STOP:
- snd_card_asihpi_pcm_timer_stop(substream);
+ snd_printdd("%s trigger stop\n", name);
+ card->pcm_stop(substream);
snd_pcm_group_for_each_entry(s, substream) {
if (snd_pcm_substream_chip(s) != card)
continue;
@@ -638,7 +680,6 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
} else
break;
}
- snd_printdd("stop\n");
/* _prepare and _hwparams reset the stream */
hpi_handle_error(hpi_stream_stop(dpcm->h_stream));
@@ -651,13 +692,13 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- snd_printdd("pause release\n");
+ snd_printdd("%s trigger pause release\n", name);
+ card->pcm_start(substream);
hpi_handle_error(hpi_stream_start(dpcm->h_stream));
- snd_card_asihpi_pcm_timer_start(substream);
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- snd_printdd("pause\n");
- snd_card_asihpi_pcm_timer_stop(substream);
+ snd_printdd("%s trigger pause push\n", name);
+ card->pcm_stop(substream);
hpi_handle_error(hpi_stream_stop(dpcm->h_stream));
break;
default:
@@ -729,9 +770,8 @@ static void snd_card_asihpi_timer_function(unsigned long data)
u32 buffer_size, bytes_avail, samples_played, on_card_bytes;
char name[16];
- snd_pcm_debug_name(substream, name, sizeof(name));
- snd_printdd("%s snd_card_asihpi_timer_function\n", name);
+ snd_pcm_debug_name(substream, name, sizeof(name));
/* find minimum newdata and buffer pos in group */
snd_pcm_group_for_each_entry(s, substream) {
@@ -769,10 +809,7 @@ static void snd_card_asihpi_timer_function(unsigned long data)
s->number);
ds->drained_count++;
if (ds->drained_count > 20) {
- unsigned long flags;
- snd_pcm_stream_lock_irqsave(s, flags);
- snd_pcm_stop(s, SNDRV_PCM_STATE_XRUN);
- snd_pcm_stream_unlock_irqrestore(s, flags);
+ snd_pcm_stop_xrun(s);
continue;
}
} else {
@@ -794,19 +831,21 @@ static void snd_card_asihpi_timer_function(unsigned long data)
newdata);
}
- snd_printdd("hw_ptr 0x%04lX, appl_ptr 0x%04lX\n",
+ snd_printddd(
+ "timer1, %s, %d, S=%d, elap=%d, rw=%d, dsp=%d, left=%d, aux=%d, space=%d, hw_ptr=%ld, appl_ptr=%ld\n",
+ name, s->number, state,
+ ds->pcm_buf_elapsed_dma_ofs,
+ ds->pcm_buf_host_rw_ofs,
+ pcm_buf_dma_ofs,
+ (int)bytes_avail,
+
+ (int)on_card_bytes,
+ buffer_size-bytes_avail,
(unsigned long)frames_to_bytes(runtime,
runtime->status->hw_ptr),
(unsigned long)frames_to_bytes(runtime,
- runtime->control->appl_ptr));
-
- snd_printdd("%d S=%d, "
- "rw=0x%04X, dma=0x%04X, left=0x%04X, "
- "aux=0x%04X space=0x%04X\n",
- s->number, state,
- ds->pcm_buf_host_rw_ofs, pcm_buf_dma_ofs,
- (int)bytes_avail,
- (int)on_card_bytes, buffer_size-bytes_avail);
+ runtime->control->appl_ptr)
+ );
loops++;
}
pcm_buf_dma_ofs = min_buf_pos;
@@ -824,16 +863,18 @@ static void snd_card_asihpi_timer_function(unsigned long data)
next_jiffies = max(next_jiffies, 1U);
dpcm->timer.expires = jiffies + next_jiffies;
- snd_printdd("jif %d buf pos 0x%04X newdata 0x%04X xfer 0x%04X\n",
+ snd_printddd("timer2, jif=%d, buf_pos=%d, newdata=%d, xfer=%d\n",
next_jiffies, pcm_buf_dma_ofs, newdata, xfercount);
snd_pcm_group_for_each_entry(s, substream) {
struct snd_card_asihpi_pcm *ds = s->runtime->private_data;
+ runtime = s->runtime;
/* don't link Cap and Play */
if (substream->stream != s->stream)
continue;
+ /* Store dma offset for use by pointer callback */
ds->pcm_buf_dma_ofs = pcm_buf_dma_ofs;
if (xfercount &&
@@ -856,7 +897,7 @@ static void snd_card_asihpi_timer_function(unsigned long data)
}
if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- snd_printddd("P%d write1 0x%04X 0x%04X\n",
+ snd_printddd("write1, P=%d, xfer=%d, buf_ofs=%d\n",
s->number, xfer1, buf_ofs);
hpi_handle_error(
hpi_outstream_write_buf(
@@ -866,7 +907,7 @@ static void snd_card_asihpi_timer_function(unsigned long data)
if (xfer2) {
pd = s->runtime->dma_area;
- snd_printddd("P%d write2 0x%04X 0x%04X\n",
+ snd_printddd("write2, P=%d, xfer=%d, buf_ofs=%d\n",
s->number,
xfercount - xfer1, buf_ofs);
hpi_handle_error(
@@ -876,7 +917,7 @@ static void snd_card_asihpi_timer_function(unsigned long data)
&ds->format));
}
} else {
- snd_printddd("C%d read1 0x%04x\n",
+ snd_printddd("read1, C=%d, xfer=%d\n",
s->number, xfer1);
hpi_handle_error(
hpi_instream_read_buf(
@@ -884,7 +925,7 @@ static void snd_card_asihpi_timer_function(unsigned long data)
pd, xfer1));
if (xfer2) {
pd = s->runtime->dma_area;
- snd_printddd("C%d read2 0x%04x\n",
+ snd_printddd("read2, C=%d, xfer=%d\n",
s->number, xfer2);
hpi_handle_error(
hpi_instream_read_buf(
@@ -892,16 +933,38 @@ static void snd_card_asihpi_timer_function(unsigned long data)
pd, xfer2));
}
}
+ /* ? host_rw_ofs always ahead of elapsed_dma_ofs by preload size? */
ds->pcm_buf_host_rw_ofs += xfercount;
ds->pcm_buf_elapsed_dma_ofs += xfercount;
snd_pcm_period_elapsed(s);
}
}
- if (dpcm->respawn_timer)
+ if (!card->hpi->interrupt_mode && dpcm->respawn_timer)
add_timer(&dpcm->timer);
}
+static void snd_card_asihpi_int_task(unsigned long data)
+{
+ struct hpi_adapter *a = (struct hpi_adapter *)data;
+ struct snd_card_asihpi *asihpi;
+
+ WARN_ON(!a || !a->snd_card || !a->snd_card->private_data);
+ asihpi = (struct snd_card_asihpi *)a->snd_card->private_data;
+ if (asihpi->llmode_streampriv)
+ snd_card_asihpi_timer_function(
+ (unsigned long)asihpi->llmode_streampriv);
+}
+
+static void snd_card_asihpi_isr(struct hpi_adapter *a)
+{
+ struct snd_card_asihpi *asihpi;
+
+ WARN_ON(!a || !a->snd_card || !a->snd_card->private_data);
+ asihpi = (struct snd_card_asihpi *)a->snd_card->private_data;
+ tasklet_schedule(&asihpi->t);
+}
+
/***************************** PLAYBACK OPS ****************/
static int snd_card_asihpi_playback_ioctl(struct snd_pcm_substream *substream,
unsigned int cmd, void *arg)
@@ -937,7 +1000,7 @@ snd_card_asihpi_playback_pointer(struct snd_pcm_substream *substream)
snd_pcm_debug_name(substream, name, sizeof(name));
ptr = bytes_to_frames(runtime, dpcm->pcm_buf_dma_ofs % dpcm->buffer_bytes);
- snd_printddd("%s pointer = 0x%04lx\n", name, (unsigned long)ptr);
+ snd_printddd("%s, pointer=%ld\n", name, (unsigned long)ptr);
return ptr;
}
@@ -1009,13 +1072,22 @@ static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream)
runtime->private_free = snd_card_asihpi_runtime_free;
memset(&snd_card_asihpi_playback, 0, sizeof(snd_card_asihpi_playback));
- snd_card_asihpi_playback.buffer_bytes_max = BUFFER_BYTES_MAX;
- snd_card_asihpi_playback.period_bytes_min = PERIOD_BYTES_MIN;
- /*?snd_card_asihpi_playback.period_bytes_min =
- card->out_max_chans * 4096; */
- snd_card_asihpi_playback.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN;
- snd_card_asihpi_playback.periods_min = PERIODS_MIN;
- snd_card_asihpi_playback.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN;
+ if (!card->hpi->interrupt_mode) {
+ snd_card_asihpi_playback.buffer_bytes_max = BUFFER_BYTES_MAX;
+ snd_card_asihpi_playback.period_bytes_min = PERIOD_BYTES_MIN;
+ snd_card_asihpi_playback.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN;
+ snd_card_asihpi_playback.periods_min = PERIODS_MIN;
+ snd_card_asihpi_playback.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN;
+ } else {
+ size_t pbmin = card->update_interval_frames *
+ card->out_max_chans;
+ snd_card_asihpi_playback.buffer_bytes_max = BUFFER_BYTES_MAX;
+ snd_card_asihpi_playback.period_bytes_min = pbmin;
+ snd_card_asihpi_playback.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN;
+ snd_card_asihpi_playback.periods_min = PERIODS_MIN;
+ snd_card_asihpi_playback.periods_max = BUFFER_BYTES_MAX / pbmin;
+ }
+
/* snd_card_asihpi_playback.fifo_size = 0; */
snd_card_asihpi_playback.channels_max = card->out_max_chans;
snd_card_asihpi_playback.channels_min = card->out_min_chans;
@@ -1050,7 +1122,7 @@ static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream)
card->update_interval_frames);
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
- card->update_interval_frames * 2, UINT_MAX);
+ card->update_interval_frames, UINT_MAX);
snd_printdd("playback open\n");
@@ -1085,9 +1157,10 @@ snd_card_asihpi_capture_pointer(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
+ char name[16];
+ snd_pcm_debug_name(substream, name, sizeof(name));
- snd_printddd("capture pointer %d=%d\n",
- substream->number, dpcm->pcm_buf_dma_ofs);
+ snd_printddd("%s, pointer=%d\n", name, dpcm->pcm_buf_dma_ofs);
/* NOTE Unlike playback can't use actual samples_played
for the capture position, because those samples aren't yet in
the local buffer available for reading.
@@ -1115,8 +1188,6 @@ static int snd_card_asihpi_capture_prepare(struct snd_pcm_substream *substream)
return 0;
}
-
-
static u64 snd_card_asihpi_capture_formats(struct snd_card_asihpi *asihpi,
u32 h_stream)
{
@@ -1183,11 +1254,21 @@ static int snd_card_asihpi_capture_open(struct snd_pcm_substream *substream)
runtime->private_free = snd_card_asihpi_runtime_free;
memset(&snd_card_asihpi_capture, 0, sizeof(snd_card_asihpi_capture));
- snd_card_asihpi_capture.buffer_bytes_max = BUFFER_BYTES_MAX;
- snd_card_asihpi_capture.period_bytes_min = PERIOD_BYTES_MIN;
- snd_card_asihpi_capture.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN;
- snd_card_asihpi_capture.periods_min = PERIODS_MIN;
- snd_card_asihpi_capture.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN;
+ if (!card->hpi->interrupt_mode) {
+ snd_card_asihpi_capture.buffer_bytes_max = BUFFER_BYTES_MAX;
+ snd_card_asihpi_capture.period_bytes_min = PERIOD_BYTES_MIN;
+ snd_card_asihpi_capture.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN;
+ snd_card_asihpi_capture.periods_min = PERIODS_MIN;
+ snd_card_asihpi_capture.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN;
+ } else {
+ size_t pbmin = card->update_interval_frames *
+ card->out_max_chans;
+ snd_card_asihpi_capture.buffer_bytes_max = BUFFER_BYTES_MAX;
+ snd_card_asihpi_capture.period_bytes_min = pbmin;
+ snd_card_asihpi_capture.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN;
+ snd_card_asihpi_capture.periods_min = PERIODS_MIN;
+ snd_card_asihpi_capture.periods_max = BUFFER_BYTES_MAX / pbmin;
+ }
/* snd_card_asihpi_capture.fifo_size = 0; */
snd_card_asihpi_capture.channels_max = card->in_max_chans;
snd_card_asihpi_capture.channels_min = card->in_min_chans;
@@ -1212,7 +1293,7 @@ static int snd_card_asihpi_capture_open(struct snd_pcm_substream *substream)
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
card->update_interval_frames);
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
- card->update_interval_frames * 2, UINT_MAX);
+ card->update_interval_frames, UINT_MAX);
snd_pcm_set_sync(substream);
@@ -1296,8 +1377,9 @@ static const char * const asihpi_tuner_band_names[] = {
"TV PAL I",
"TV PAL DK",
"TV SECAM",
+ "TV DAB",
};
-
+/* Number of strings must match the enumerations for HPI_TUNER_BAND in hpi.h */
compile_time_assert(
(ARRAY_SIZE(asihpi_tuner_band_names) ==
(HPI_TUNER_BAND_LAST+1)),
@@ -1317,9 +1399,11 @@ static const char * const asihpi_src_names[] = {
"Analog",
"Adapter",
"RTP",
- "Internal"
+ "Internal",
+ "AVB",
+ "BLU-Link"
};
-
+/* Number of strings must match the enumerations for HPI_SOURCENODES in hpi.h */
compile_time_assert(
(ARRAY_SIZE(asihpi_src_names) ==
(HPI_SOURCENODE_LAST_INDEX-HPI_SOURCENODE_NONE+1)),
@@ -1335,8 +1419,11 @@ static const char * const asihpi_dst_names[] = {
"Net",
"Analog",
"RTP",
+ "AVB",
+ "Internal",
+ "BLU-Link"
};
-
+/* Number of strings must match the enumerations for HPI_DESTNODES in hpi.h */
compile_time_assert(
(ARRAY_SIZE(asihpi_dst_names) ==
(HPI_DESTNODE_LAST_INDEX-HPI_DESTNODE_NONE+1)),
@@ -1351,7 +1438,7 @@ static inline int ctl_add(struct snd_card *card, struct snd_kcontrol_new *ctl,
if (err < 0)
return err;
else if (mixer_dump)
- snd_printk(KERN_INFO "added %s(%d)\n", ctl->name, ctl->index);
+ dev_info(&asihpi->pci->dev, "added %s(%d)\n", ctl->name, ctl->index);
return 0;
}
@@ -1625,18 +1712,7 @@ static const char * const asihpi_aesebu_format_names[] = {
static int snd_asihpi_aesebu_format_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
-
- strcpy(uinfo->value.enumerated.name,
- asihpi_aesebu_format_names[uinfo->value.enumerated.item]);
-
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, asihpi_aesebu_format_names);
}
static int snd_asihpi_aesebu_format_get(struct snd_kcontrol *kcontrol,
@@ -1863,22 +1939,7 @@ static int snd_asihpi_tuner_band_info(struct snd_kcontrol *kcontrol,
if (num_bands < 0)
return num_bands;
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = num_bands;
-
- if (num_bands > 0) {
- if (uinfo->value.enumerated.item >=
- uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
-
- strcpy(uinfo->value.enumerated.name,
- asihpi_tuner_band_names[
- tuner_bands[uinfo->value.enumerated.item]]);
-
- }
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, num_bands, asihpi_tuner_band_names);
}
static int snd_asihpi_tuner_band_get(struct snd_kcontrol *kcontrol,
@@ -2253,7 +2314,7 @@ static int snd_asihpi_cmode_info(struct snd_kcontrol *kcontrol,
u32 h_control = kcontrol->private_value;
u16 mode;
int i;
- u16 mode_map[6];
+ const char *mapped_names[6];
int valid_modes = 0;
/* HPI channel mode values can be from 1 to 6
@@ -2262,24 +2323,14 @@ static int snd_asihpi_cmode_info(struct snd_kcontrol *kcontrol,
for (i = 0; i < HPI_CHANNEL_MODE_LAST; i++)
if (!hpi_channel_mode_query_mode(
h_control, i, &mode)) {
- mode_map[valid_modes] = mode;
+ mapped_names[valid_modes] = mode_names[mode];
valid_modes++;
}
if (!valid_modes)
return -EINVAL;
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = valid_modes;
-
- if (uinfo->value.enumerated.item >= valid_modes)
- uinfo->value.enumerated.item = valid_modes - 1;
-
- strcpy(uinfo->value.enumerated.name,
- mode_names[mode_map[uinfo->value.enumerated.item]]);
-
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, valid_modes, mapped_names);
}
static int snd_asihpi_cmode_get(struct snd_kcontrol *kcontrol,
@@ -2328,13 +2379,18 @@ static int snd_asihpi_cmode_add(struct snd_card_asihpi *asihpi,
/*------------------------------------------------------------
Sampleclock source controls
------------------------------------------------------------*/
-static char *sampleclock_sources[MAX_CLOCKSOURCES] = {
+static const char const *sampleclock_sources[] = {
"N/A", "Local PLL", "Digital Sync", "Word External", "Word Header",
"SMPTE", "Digital1", "Auto", "Network", "Invalid",
- "Prev Module",
+ "Prev Module", "BLU-Link",
"Digital2", "Digital3", "Digital4", "Digital5",
"Digital6", "Digital7", "Digital8"};
+ /* Number of strings must match expected enumerated values */
+ compile_time_assert(
+ (ARRAY_SIZE(sampleclock_sources) == MAX_CLOCKSOURCES),
+ assert_sampleclock_sources_size);
+
static int snd_asihpi_clksrc_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -2482,15 +2538,19 @@ static int snd_asihpi_clkrate_get(struct snd_kcontrol *kcontrol,
static int snd_asihpi_sampleclock_add(struct snd_card_asihpi *asihpi,
struct hpi_control *hpi_ctl)
{
- struct snd_card *card = asihpi->card;
+ struct snd_card *card;
struct snd_kcontrol_new snd_control;
- struct clk_cache *clkcache = &asihpi->cc;
+ struct clk_cache *clkcache;
u32 hSC = hpi_ctl->h_control;
int has_aes_in = 0;
int i, j;
u16 source;
+ if (snd_BUG_ON(!asihpi))
+ return -EINVAL;
+ card = asihpi->card;
+ clkcache = &asihpi->cc;
snd_control.private_value = hpi_ctl->h_control;
clkcache->has_local = 0;
@@ -2592,7 +2652,7 @@ static int snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi)
if (err) {
if (err == HPI_ERROR_CONTROL_DISABLED) {
if (mixer_dump)
- snd_printk(KERN_INFO
+ dev_info(&asihpi->pci->dev,
"Disabled HPI Control(%d)\n",
idx);
continue;
@@ -2657,9 +2717,8 @@ static int snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi)
case HPI_CONTROL_COMPANDER:
default:
if (mixer_dump)
- snd_printk(KERN_INFO
- "Untranslated HPI Control"
- "(%d) %d %d %d %d %d\n",
+ dev_info(&asihpi->pci->dev,
+ "Untranslated HPI Control (%d) %d %d %d %d %d\n",
idx,
hpi_ctl.control_type,
hpi_ctl.src_node_type,
@@ -2674,7 +2733,7 @@ static int snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi)
if (HPI_ERROR_INVALID_OBJ_INDEX != err)
hpi_handle_error(err);
- snd_printk(KERN_INFO "%d mixer controls found\n", idx);
+ dev_info(&asihpi->pci->dev, "%d mixer controls found\n", idx);
return 0;
}
@@ -2837,8 +2896,7 @@ static int snd_asihpi_probe(struct pci_dev *pci_dev,
&card);
if (err < 0)
return err;
- snd_printk(KERN_WARNING
- "**** WARNING **** Adapter index %d->ALSA index %d\n",
+ dev_warn(&pci_dev->dev, "Adapter index %d->ALSA index %d\n",
adapter_index, card->number);
}
@@ -2846,9 +2904,7 @@ static int snd_asihpi_probe(struct pci_dev *pci_dev,
asihpi->card = card;
asihpi->pci = pci_dev;
asihpi->hpi = hpi;
-
- snd_printk(KERN_INFO "adapter ID=%4X index=%d\n",
- asihpi->hpi->adapter->type, adapter_index);
+ hpi->snd_card = card;
err = hpi_adapter_get_property(adapter_index,
HPI_ADAPTER_PROPERTY_CAPS1,
@@ -2868,8 +2924,16 @@ static int snd_asihpi_probe(struct pci_dev *pci_dev,
if (err)
asihpi->update_interval_frames = 512;
- if (!asihpi->can_dma)
- asihpi->update_interval_frames *= 2;
+ if (hpi->interrupt_mode) {
+ asihpi->pcm_start = snd_card_asihpi_pcm_int_start;
+ asihpi->pcm_stop = snd_card_asihpi_pcm_int_stop;
+ tasklet_init(&asihpi->t, snd_card_asihpi_int_task,
+ (unsigned long)hpi);
+ hpi->interrupt_callback = snd_card_asihpi_isr;
+ } else {
+ asihpi->pcm_start = snd_card_asihpi_pcm_timer_start;
+ asihpi->pcm_stop = snd_card_asihpi_pcm_timer_stop;
+ }
hpi_handle_error(hpi_instream_open(adapter_index,
0, &h_stream));
@@ -2879,6 +2943,9 @@ static int snd_asihpi_probe(struct pci_dev *pci_dev,
hpi_handle_error(hpi_instream_close(h_stream));
+ if (!asihpi->can_dma)
+ asihpi->update_interval_frames *= 2;
+
err = hpi_adapter_get_property(adapter_index,
HPI_ADAPTER_PROPERTY_CURCHANNELS,
&asihpi->in_max_chans, &asihpi->out_max_chans);
@@ -2896,20 +2963,21 @@ static int snd_asihpi_probe(struct pci_dev *pci_dev,
asihpi->in_min_chans = 1;
}
- snd_printk(KERN_INFO "Has dma:%d, grouping:%d, mrx:%d\n",
+ dev_info(&pci_dev->dev, "Has dma:%d, grouping:%d, mrx:%d, uif:%d\n",
asihpi->can_dma,
asihpi->support_grouping,
- asihpi->support_mrx
+ asihpi->support_mrx,
+ asihpi->update_interval_frames
);
err = snd_card_asihpi_pcm_new(asihpi, 0);
if (err < 0) {
- snd_printk(KERN_ERR "pcm_new failed\n");
+ dev_err(&pci_dev->dev, "pcm_new failed\n");
goto __nodev;
}
err = snd_card_asihpi_mixer_new(asihpi);
if (err < 0) {
- snd_printk(KERN_ERR "mixer_new failed\n");
+ dev_err(&pci_dev->dev, "mixer_new failed\n");
goto __nodev;
}
@@ -2936,13 +3004,12 @@ static int snd_asihpi_probe(struct pci_dev *pci_dev,
err = snd_card_register(card);
if (!err) {
- hpi->snd_card = card;
dev++;
return 0;
}
__nodev:
snd_card_free(card);
- snd_printk(KERN_ERR "snd_asihpi_probe error %d\n", err);
+ dev_err(&pci_dev->dev, "snd_asihpi_probe error %d\n", err);
return err;
}
@@ -2950,6 +3017,16 @@ __nodev:
static void snd_asihpi_remove(struct pci_dev *pci_dev)
{
struct hpi_adapter *hpi = pci_get_drvdata(pci_dev);
+ struct snd_card_asihpi *asihpi = hpi->snd_card->private_data;
+
+ /* Stop interrupts */
+ if (hpi->interrupt_mode) {
+ hpi->interrupt_callback = NULL;
+ hpi_handle_error(hpi_adapter_set_property(hpi->adapter->index,
+ HPI_ADAPTER_PROPERTY_IRQ_RATE, 0, 0));
+ tasklet_kill(&asihpi->t);
+ }
+
snd_card_free(hpi->snd_card);
hpi->snd_card = NULL;
asihpi_adapter_remove(pci_dev);
@@ -2971,10 +3048,6 @@ static struct pci_driver driver = {
.id_table = asihpi_pci_tbl,
.probe = snd_asihpi_probe,
.remove = snd_asihpi_remove,
-#ifdef CONFIG_PM_SLEEP
-/* .suspend = snd_asihpi_suspend,
- .resume = snd_asihpi_resume, */
-#endif
};
static int __init snd_asihpi_init(void)
diff --git a/sound/pci/asihpi/hpi.h b/sound/pci/asihpi/hpi.h
index 20887241a3ae..4466bd2c5272 100644
--- a/sound/pci/asihpi/hpi.h
+++ b/sound/pci/asihpi/hpi.h
@@ -196,8 +196,10 @@ enum HPI_SOURCENODES {
packets of RTP audio samples from other devices. */
HPI_SOURCENODE_RTP_DESTINATION = 112,
HPI_SOURCENODE_INTERNAL = 113, /**< node internal to the device. */
+ HPI_SOURCENODE_AVB = 114, /**< AVB input stream */
+ HPI_SOURCENODE_BLULINK = 115, /**< BLU-link input channel */
/* !!!Update this AND hpidebug.h if you add a new sourcenode type!!! */
- HPI_SOURCENODE_LAST_INDEX = 113 /**< largest ID */
+ HPI_SOURCENODE_LAST_INDEX = 115 /**< largest ID */
/* AX6 max sourcenode types = 15 */
};
@@ -224,8 +226,11 @@ enum HPI_DESTNODES {
/** RTP stream output node - This node is a source for
packets of RTP audio samples that are sent to other devices. */
HPI_DESTNODE_RTP_SOURCE = 208,
+ HPI_DESTNODE_AVB = 209, /**< AVB output stream */
+ HPI_DESTNODE_INTERNAL = 210, /**< node internal to the device. */
+ HPI_DESTNODE_BLULINK = 211, /**< BLU-link output channel. */
/* !!!Update this AND hpidebug.h if you add a new destnode type!!! */
- HPI_DESTNODE_LAST_INDEX = 208 /**< largest ID */
+ HPI_DESTNODE_LAST_INDEX = 211 /**< largest ID */
/* AX6 max destnode types = 15 */
};
@@ -752,7 +757,8 @@ enum HPI_TUNER_BAND {
HPI_TUNER_BAND_TV_PAL_I = 7, /**< PAL-I TV band*/
HPI_TUNER_BAND_TV_PAL_DK = 8, /**< PAL-D/K TV band*/
HPI_TUNER_BAND_TV_SECAM_L = 9, /**< SECAM-L TV band*/
- HPI_TUNER_BAND_LAST = 9 /**< the index of the last tuner band. */
+ HPI_TUNER_BAND_DAB = 10,
+ HPI_TUNER_BAND_LAST = 10 /**< the index of the last tuner band. */
};
/** Tuner mode attributes
@@ -842,8 +848,10 @@ enum HPI_SAMPLECLOCK_SOURCES {
HPI_SAMPLECLOCK_SOURCE_NETWORK = 8,
/** From previous adjacent module (ASI2416 only)*/
HPI_SAMPLECLOCK_SOURCE_PREV_MODULE = 10,
+/** Blu link sample clock*/
+ HPI_SAMPLECLOCK_SOURCE_BLULINK = 11,
/*! Update this if you add a new clock source.*/
- HPI_SAMPLECLOCK_SOURCE_LAST = 10
+ HPI_SAMPLECLOCK_SOURCE_LAST = 11
};
/** Equalizer filter types. Used by HPI_ParametricEq_SetBand()
diff --git a/sound/pci/asihpi/hpi6205.c b/sound/pci/asihpi/hpi6205.c
index 4f2873880b16..8d5abfa4e24b 100644
--- a/sound/pci/asihpi/hpi6205.c
+++ b/sound/pci/asihpi/hpi6205.c
@@ -1,7 +1,7 @@
/******************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2014 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
@@ -163,6 +163,9 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
static void delete_adapter_obj(struct hpi_adapter_obj *pao);
+static int adapter_irq_query_and_clear(struct hpi_adapter_obj *pao,
+ u32 message);
+
static void outstream_host_buffer_allocate(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr);
@@ -283,7 +286,6 @@ static void adapter_message(struct hpi_adapter_obj *pao,
case HPI_ADAPTER_DELETE:
adapter_delete(pao, phm, phr);
break;
-
default:
hw_message(pao, phm, phr);
break;
@@ -673,6 +675,12 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
HPI_DEBUG_LOG(INFO, "bootload DSP OK\n");
+ pao->irq_query_and_clear = adapter_irq_query_and_clear;
+ pao->instream_host_buffer_status =
+ phw->p_interface_buffer->instream_host_buffer_status;
+ pao->outstream_host_buffer_status =
+ phw->p_interface_buffer->outstream_host_buffer_status;
+
return hpi_add_adapter(pao);
}
@@ -713,6 +721,21 @@ static void delete_adapter_obj(struct hpi_adapter_obj *pao)
/*****************************************************************************/
/* Adapter functions */
+static int adapter_irq_query_and_clear(struct hpi_adapter_obj *pao,
+ u32 message)
+{
+ struct hpi_hw_obj *phw = pao->priv;
+ u32 hsr = 0;
+
+ hsr = ioread32(phw->prHSR);
+ if (hsr & C6205_HSR_INTSRC) {
+ /* reset the interrupt from the DSP */
+ iowrite32(C6205_HSR_INTSRC, phw->prHSR);
+ return HPI_IRQ_MIXER;
+ }
+
+ return HPI_IRQ_NONE;
+}
/*****************************************************************************/
/* OutStream Host buffer functions */
@@ -1331,17 +1354,21 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
if (boot_code_id[1] != 0) {
/* DSP 1 is a C6713 */
/* CLKX0 <- '1' release the C6205 bootmode pulldowns */
- boot_loader_write_mem32(pao, 0, (0x018C0024L), 0x00002202);
+ boot_loader_write_mem32(pao, 0, 0x018C0024, 0x00002202);
hpios_delay_micro_seconds(100);
/* Reset the 6713 #1 - revB */
boot_loader_write_mem32(pao, 0, C6205_BAR0_TIMER1_CTL, 0);
-
- /* dummy read every 4 words for 6205 advisory 1.4.4 */
- boot_loader_read_mem32(pao, 0, 0);
-
+ /* value of bit 3 is unknown after DSP reset, other bits shoudl be 0 */
+ if (0 != (boot_loader_read_mem32(pao, 0,
+ (C6205_BAR0_TIMER1_CTL)) & ~8))
+ return HPI6205_ERROR_6205_REG;
hpios_delay_micro_seconds(100);
+
/* Release C6713 from reset - revB */
boot_loader_write_mem32(pao, 0, C6205_BAR0_TIMER1_CTL, 4);
+ if (4 != (boot_loader_read_mem32(pao, 0,
+ (C6205_BAR0_TIMER1_CTL)) & ~8))
+ return HPI6205_ERROR_6205_REG;
hpios_delay_micro_seconds(100);
}
@@ -2089,7 +2116,7 @@ static u16 message_response_sequence(struct hpi_adapter_obj *pao,
return 0;
}
- /* Assume buffer of type struct bus_master_interface
+ /* Assume buffer of type struct bus_master_interface_62
is allocated "noncacheable" */
if (!wait_dsp_ack(phw, H620_HIF_IDLE, HPI6205_TIMEOUT)) {
diff --git a/sound/pci/asihpi/hpi_internal.h b/sound/pci/asihpi/hpi_internal.h
index bc86cb726d79..48380ce2c81b 100644
--- a/sound/pci/asihpi/hpi_internal.h
+++ b/sound/pci/asihpi/hpi_internal.h
@@ -554,17 +554,21 @@ struct hpi_pci {
struct pci_dev *pci_dev;
};
+/** Adapter specification resource */
+struct hpi_adapter_specification {
+ u32 type;
+ u8 modules[4];
+};
+
struct hpi_resource {
union {
const struct hpi_pci *pci;
const char *net_if;
+ struct hpi_adapter_specification adapter_spec;
+ const void *sw_if;
} r;
-#ifndef HPI64BIT /* keep structure size constant */
- u32 pad_to64;
-#endif
u16 bus_type; /* HPI_BUS_PNPISA, _PCI, _USB etc */
u16 padding;
-
};
/** Format info used inside struct hpi_message
@@ -582,7 +586,7 @@ struct hpi_msg_format {
struct hpi_msg_data {
struct hpi_msg_format format;
u8 *pb_data;
-#ifndef HPI64BIT
+#ifndef CONFIG_64BIT
u32 padding;
#endif
u32 data_size;
@@ -595,7 +599,7 @@ struct hpi_data_legacy32 {
u32 data_size;
};
-#ifdef HPI64BIT
+#ifdef CONFIG_64BIT
/* Compatibility version of struct hpi_data*/
struct hpi_data_compat32 {
struct hpi_msg_format format;
@@ -682,8 +686,8 @@ union hpi_adapterx_msg {
u16 value;
} test_assert;
struct {
- u32 yes;
- } irq_query;
+ u32 message;
+ } irq;
u32 pad[3];
};
diff --git a/sound/pci/asihpi/hpicmn.c b/sound/pci/asihpi/hpicmn.c
index 7ed5c26c3737..c7751243dc42 100644
--- a/sound/pci/asihpi/hpicmn.c
+++ b/sound/pci/asihpi/hpicmn.c
@@ -1,7 +1,7 @@
/******************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2014 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
@@ -206,6 +206,14 @@ static unsigned int control_cache_alloc_check(struct hpi_control_cache *pC)
struct hpi_control_cache_info *info =
(struct hpi_control_cache_info *)
&p_master_cache[byte_count];
+ u16 control_index = info->control_index;
+
+ if (control_index >= pC->control_count) {
+ HPI_DEBUG_LOG(INFO,
+ "adap %d control index %d out of range, cache not ready?\n",
+ pC->adap_idx, control_index);
+ return 0;
+ }
if (!info->size_in32bit_words) {
if (!i) {
@@ -225,10 +233,10 @@ static unsigned int control_cache_alloc_check(struct hpi_control_cache *pC)
}
if (info->control_type) {
- pC->p_info[info->control_index] = info;
+ pC->p_info[control_index] = info;
cached++;
} else { /* dummy cache entry */
- pC->p_info[info->control_index] = NULL;
+ pC->p_info[control_index] = NULL;
}
byte_count += info->size_in32bit_words * 4;
@@ -309,35 +317,18 @@ static const struct pad_ofs_size pad_desc[] = {
/** CheckControlCache checks the cache and fills the struct hpi_response
* accordingly. It returns one if a cache hit occurred, zero otherwise.
*/
-short hpi_check_control_cache(struct hpi_control_cache *p_cache,
+short hpi_check_control_cache_single(struct hpi_control_cache_single *pC,
struct hpi_message *phm, struct hpi_response *phr)
{
- short found = 1;
- struct hpi_control_cache_info *pI;
- struct hpi_control_cache_single *pC;
size_t response_size;
- if (!find_control(phm->obj_index, p_cache, &pI)) {
- HPI_DEBUG_LOG(VERBOSE,
- "HPICMN find_control() failed for adap %d\n",
- phm->adapter_index);
- return 0;
- }
-
- phr->error = 0;
- phr->specific_error = 0;
- phr->version = 0;
+ short found = 1;
/* set the default response size */
response_size =
sizeof(struct hpi_response_header) +
sizeof(struct hpi_control_res);
- /* pC is the default cached control strucure. May be cast to
- something else in the following switch statement.
- */
- pC = (struct hpi_control_cache_single *)pI;
-
- switch (pI->control_type) {
+ switch (pC->u.i.control_type) {
case HPI_CONTROL_METER:
if (phm->u.c.attribute == HPI_METER_PEAK) {
@@ -467,7 +458,7 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache,
break;
case HPI_CONTROL_PAD:{
struct hpi_control_cache_pad *p_pad;
- p_pad = (struct hpi_control_cache_pad *)pI;
+ p_pad = (struct hpi_control_cache_pad *)pC;
if (!(p_pad->field_valid_flags & (1 <<
HPI_CTL_ATTR_INDEX(phm->u.c.
@@ -531,7 +522,8 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache,
HPI_DEBUG_LOG(VERBOSE, "%s Adap %d, Ctl %d, Type %d, Attr %d\n",
found ? "Cached" : "Uncached", phm->adapter_index,
- pI->control_index, pI->control_type, phm->u.c.attribute);
+ pC->u.i.control_index, pC->u.i.control_type,
+ phm->u.c.attribute);
if (found) {
phr->size = (u16)response_size;
@@ -543,34 +535,36 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache,
return found;
}
-/** Updates the cache with Set values.
-
-Only update if no error.
-Volume and Level return the limited values in the response, so use these
-Multiplexer does so use sent values
-*/
-void hpi_cmn_control_cache_sync_to_msg(struct hpi_control_cache *p_cache,
+short hpi_check_control_cache(struct hpi_control_cache *p_cache,
struct hpi_message *phm, struct hpi_response *phr)
{
- struct hpi_control_cache_single *pC;
struct hpi_control_cache_info *pI;
- if (phr->error)
- return;
-
if (!find_control(phm->obj_index, p_cache, &pI)) {
HPI_DEBUG_LOG(VERBOSE,
"HPICMN find_control() failed for adap %d\n",
phm->adapter_index);
- return;
+ return 0;
}
- /* pC is the default cached control strucure.
- May be cast to something else in the following switch statement.
- */
- pC = (struct hpi_control_cache_single *)pI;
+ phr->error = 0;
+ phr->specific_error = 0;
+ phr->version = 0;
+
+ return hpi_check_control_cache_single((struct hpi_control_cache_single
+ *)pI, phm, phr);
+}
+
+/** Updates the cache with Set values.
- switch (pI->control_type) {
+Only update if no error.
+Volume and Level return the limited values in the response, so use these
+Multiplexer does so use sent values
+*/
+void hpi_cmn_control_cache_sync_to_msg_single(struct hpi_control_cache_single
+ *pC, struct hpi_message *phm, struct hpi_response *phr)
+{
+ switch (pC->u.i.control_type) {
case HPI_CONTROL_VOLUME:
if (phm->u.c.attribute == HPI_VOLUME_GAIN) {
pC->u.vol.an_log[0] = phr->u.c.an_log_value[0];
@@ -625,6 +619,30 @@ void hpi_cmn_control_cache_sync_to_msg(struct hpi_control_cache *p_cache,
}
}
+void hpi_cmn_control_cache_sync_to_msg(struct hpi_control_cache *p_cache,
+ struct hpi_message *phm, struct hpi_response *phr)
+{
+ struct hpi_control_cache_single *pC;
+ struct hpi_control_cache_info *pI;
+
+ if (phr->error)
+ return;
+
+ if (!find_control(phm->obj_index, p_cache, &pI)) {
+ HPI_DEBUG_LOG(VERBOSE,
+ "HPICMN find_control() failed for adap %d\n",
+ phm->adapter_index);
+ return;
+ }
+
+ /* pC is the default cached control strucure.
+ May be cast to something else in the following switch statement.
+ */
+ pC = (struct hpi_control_cache_single *)pI;
+
+ hpi_cmn_control_cache_sync_to_msg_single(pC, phm, phr);
+}
+
/** Allocate control cache.
\return Cache pointer, or NULL if allocation fails.
@@ -637,12 +655,13 @@ struct hpi_control_cache *hpi_alloc_control_cache(const u32 control_count,
if (!p_cache)
return NULL;
- p_cache->p_info = kcalloc(control_count, sizeof(*p_cache->p_info),
- GFP_KERNEL);
+ p_cache->p_info =
+ kcalloc(control_count, sizeof(*p_cache->p_info), GFP_KERNEL);
if (!p_cache->p_info) {
kfree(p_cache);
return NULL;
}
+
p_cache->cache_size_in_bytes = size_in_bytes;
p_cache->control_count = control_count;
p_cache->p_cache = p_dsp_control_buffer;
diff --git a/sound/pci/asihpi/hpicmn.h b/sound/pci/asihpi/hpicmn.h
index e44121283047..46629c2d101b 100644
--- a/sound/pci/asihpi/hpicmn.h
+++ b/sound/pci/asihpi/hpicmn.h
@@ -1,7 +1,7 @@
/**
AudioScience HPI driver
- Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2014 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
@@ -21,7 +21,11 @@
struct hpi_adapter_obj;
/* a function that takes an adapter obj and returns an int */
-typedef int adapter_int_func(struct hpi_adapter_obj *pao);
+typedef int adapter_int_func(struct hpi_adapter_obj *pao, u32 message);
+
+#define HPI_IRQ_NONE (0)
+#define HPI_IRQ_MESSAGE (1)
+#define HPI_IRQ_MIXER (2)
struct hpi_adapter_obj {
struct hpi_pci pci; /* PCI info - bus#,dev#,address etc */
@@ -33,6 +37,9 @@ struct hpi_adapter_obj {
u16 dsp_crashed;
u16 has_control_cache;
void *priv;
+ adapter_int_func *irq_query_and_clear;
+ struct hpi_hostbuffer_status *instream_host_buffer_status;
+ struct hpi_hostbuffer_status *outstream_host_buffer_status;
};
struct hpi_control_cache {
@@ -55,13 +62,21 @@ void hpi_delete_adapter(struct hpi_adapter_obj *pao);
short hpi_check_control_cache(struct hpi_control_cache *pC,
struct hpi_message *phm, struct hpi_response *phr);
+
+short hpi_check_control_cache_single(struct hpi_control_cache_single *pC,
+ struct hpi_message *phm, struct hpi_response *phr);
+
struct hpi_control_cache *hpi_alloc_control_cache(const u32
number_of_controls, const u32 size_in_bytes, u8 *pDSP_control_buffer);
+
void hpi_free_control_cache(struct hpi_control_cache *p_cache);
void hpi_cmn_control_cache_sync_to_msg(struct hpi_control_cache *pC,
struct hpi_message *phm, struct hpi_response *phr);
+void hpi_cmn_control_cache_sync_to_msg_single(struct hpi_control_cache_single
+ *pC, struct hpi_message *phm, struct hpi_response *phr);
+
u16 hpi_validate_response(struct hpi_message *phm, struct hpi_response *phr);
hpi_handler_func HPI_COMMON;
diff --git a/sound/pci/asihpi/hpimsginit.c b/sound/pci/asihpi/hpimsginit.c
index 032d563e3708..7eb617175fde 100644
--- a/sound/pci/asihpi/hpimsginit.c
+++ b/sound/pci/asihpi/hpimsginit.c
@@ -1,7 +1,7 @@
/******************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2014 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
@@ -37,11 +37,15 @@ static u16 gwSSX2_bypass;
static void hpi_init_message(struct hpi_message *phm, u16 object,
u16 function)
{
- memset(phm, 0, sizeof(*phm));
+ u16 size;
+
if ((object > 0) && (object <= HPI_OBJ_MAXINDEX))
- phm->size = msg_size[object];
+ size = msg_size[object];
else
- phm->size = sizeof(*phm);
+ size = sizeof(*phm);
+
+ memset(phm, 0, size);
+ phm->size = size;
if (gwSSX2_bypass)
phm->type = HPI_TYPE_SSX2BYPASS_MESSAGE;
@@ -60,12 +64,16 @@ static void hpi_init_message(struct hpi_message *phm, u16 object,
void hpi_init_response(struct hpi_response *phr, u16 object, u16 function,
u16 error)
{
- memset(phr, 0, sizeof(*phr));
- phr->type = HPI_TYPE_RESPONSE;
+ u16 size;
+
if ((object > 0) && (object <= HPI_OBJ_MAXINDEX))
- phr->size = res_size[object];
+ size = res_size[object];
else
- phr->size = sizeof(*phr);
+ size = sizeof(*phr);
+
+ memset(phr, 0, sizeof(*phr));
+ phr->size = size;
+ phr->type = HPI_TYPE_RESPONSE;
phr->object = object;
phr->function = function;
phr->error = error;
@@ -86,7 +94,7 @@ void hpi_init_message_response(struct hpi_message *phm,
static void hpi_init_messageV1(struct hpi_message_header *phm, u16 size,
u16 object, u16 function)
{
- memset(phm, 0, sizeof(*phm));
+ memset(phm, 0, size);
if ((object > 0) && (object <= HPI_OBJ_MAXINDEX)) {
phm->size = size;
phm->type = HPI_TYPE_REQUEST;
@@ -100,7 +108,9 @@ static void hpi_init_messageV1(struct hpi_message_header *phm, u16 size,
void hpi_init_responseV1(struct hpi_response_header *phr, u16 size,
u16 object, u16 function)
{
- memset(phr, 0, sizeof(*phr));
+ (void)object;
+ (void)function;
+ memset(phr, 0, size);
phr->size = size;
phr->version = 1;
phr->type = HPI_TYPE_RESPONSE;
diff --git a/sound/pci/asihpi/hpimsgx.c b/sound/pci/asihpi/hpimsgx.c
index d4790ddc225c..736f45337fc7 100644
--- a/sound/pci/asihpi/hpimsgx.c
+++ b/sound/pci/asihpi/hpimsgx.c
@@ -1,7 +1,7 @@
/******************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2014 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
@@ -35,6 +35,7 @@ static struct pci_device_id asihpi_pci_tbl[] = {
static struct hpios_spinlock msgx_lock;
static hpi_handler_func *hpi_entry_points[HPI_MAX_ADAPTERS];
+static int logging_enabled = 1;
static hpi_handler_func *hpi_lookup_entry_point_function(const struct hpi_pci
*pci_info)
@@ -312,7 +313,9 @@ static void instream_message(struct hpi_message *phm,
void hpi_send_recv_ex(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner)
{
- HPI_DEBUG_MESSAGE(DEBUG, phm);
+
+ if (logging_enabled)
+ HPI_DEBUG_MESSAGE(DEBUG, phm);
if (phm->type != HPI_TYPE_REQUEST) {
hpi_init_response(phr, phm->object, phm->function,
@@ -352,8 +355,14 @@ void hpi_send_recv_ex(struct hpi_message *phm, struct hpi_response *phr,
hw_entry_point(phm, phr);
break;
}
- HPI_DEBUG_RESPONSE(phr);
+ if (logging_enabled)
+ HPI_DEBUG_RESPONSE(phr);
+
+ if (phr->error >= HPI_ERROR_DSP_COMMUNICATION) {
+ hpi_debug_level_set(HPI_DEBUG_LEVEL_ERROR);
+ logging_enabled = 0;
+ }
}
static void adapter_open(struct hpi_message *phm, struct hpi_response *phr)
diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c
index 7f0272032fbb..6aa677e60555 100644
--- a/sound/pci/asihpi/hpioctl.c
+++ b/sound/pci/asihpi/hpioctl.c
@@ -1,7 +1,8 @@
/*******************************************************************************
-
AudioScience HPI driver
- Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
+ Common Linux HPI ioctl and module probe/remove functions
+
+ Copyright (C) 1997-2014 AudioScience Inc. <support@audioscience.com>
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
@@ -12,11 +13,6 @@
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-Common Linux HPI ioctl and module probe/remove functions
*******************************************************************************/
#define SOURCEFILE_NAME "hpioctl.c"
@@ -29,6 +25,7 @@ Common Linux HPI ioctl and module probe/remove functions
#include "hpicmn.h"
#include <linux/fs.h>
+#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/moduleparam.h>
#include <asm/uaccess.h>
@@ -307,10 +304,38 @@ out:
return err;
}
+static int asihpi_irq_count;
+
+static irqreturn_t asihpi_isr(int irq, void *dev_id)
+{
+ struct hpi_adapter *a = dev_id;
+ int handled;
+
+ if (!a->adapter->irq_query_and_clear) {
+ pr_err("asihpi_isr ASI%04X:%d no handler\n", a->adapter->type,
+ a->adapter->index);
+ return IRQ_NONE;
+ }
+
+ handled = a->adapter->irq_query_and_clear(a->adapter, 0);
+
+ if (!handled)
+ return IRQ_NONE;
+
+ asihpi_irq_count++;
+ /* printk(KERN_INFO "asihpi_isr %d ASI%04X:%d irq handled\n",
+ asihpi_irq_count, a->adapter->type, a->adapter->index); */
+
+ if (a->interrupt_callback)
+ a->interrupt_callback(a);
+
+ return IRQ_HANDLED;
+}
+
int asihpi_adapter_probe(struct pci_dev *pci_dev,
const struct pci_device_id *pci_id)
{
- int idx, nm;
+ int idx, nm, low_latency_mode = 0, irq_supported = 0;
int adapter_index;
unsigned int memlen;
struct hpi_message hm;
@@ -388,8 +413,38 @@ int asihpi_adapter_probe(struct pci_dev *pci_dev,
hm.adapter_index = adapter.adapter->index;
hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
- if (hr.error)
+ if (hr.error) {
+ HPI_DEBUG_LOG(ERROR, "HPI_ADAPTER_OPEN failed, aborting\n");
goto err;
+ }
+
+ /* Check if current mode == Low Latency mode */
+ hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
+ HPI_ADAPTER_GET_MODE);
+ hm.adapter_index = adapter.adapter->index;
+ hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
+
+ if (!hr.error
+ && hr.u.ax.mode.adapter_mode == HPI_ADAPTER_MODE_LOW_LATENCY)
+ low_latency_mode = 1;
+ else
+ dev_info(&pci_dev->dev,
+ "Adapter at index %d is not in low latency mode\n",
+ adapter.adapter->index);
+
+ /* Check if IRQs are supported */
+ hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
+ HPI_ADAPTER_GET_PROPERTY);
+ hm.adapter_index = adapter.adapter->index;
+ hm.u.ax.property_set.property = HPI_ADAPTER_PROPERTY_SUPPORTS_IRQ;
+ hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
+ if (hr.error || !hr.u.ax.property_get.parameter1) {
+ dev_info(&pci_dev->dev,
+ "IRQs not supported by adapter at index %d\n",
+ adapter.adapter->index);
+ } else {
+ irq_supported = 1;
+ }
/* WARNING can't init mutex in 'adapter'
* and then copy it to adapters[] ?!?!
@@ -398,6 +453,44 @@ int asihpi_adapter_probe(struct pci_dev *pci_dev,
mutex_init(&adapters[adapter_index].mutex);
pci_set_drvdata(pci_dev, &adapters[adapter_index]);
+ if (low_latency_mode && irq_supported) {
+ if (!adapter.adapter->irq_query_and_clear) {
+ dev_err(&pci_dev->dev,
+ "no IRQ handler for adapter %d, aborting\n",
+ adapter.adapter->index);
+ goto err;
+ }
+
+ /* Disable IRQ generation on DSP side by setting the rate to 0 */
+ hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
+ HPI_ADAPTER_SET_PROPERTY);
+ hm.adapter_index = adapter.adapter->index;
+ hm.u.ax.property_set.property = HPI_ADAPTER_PROPERTY_IRQ_RATE;
+ hm.u.ax.property_set.parameter1 = 0;
+ hm.u.ax.property_set.parameter2 = 0;
+ hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
+ if (hr.error) {
+ HPI_DEBUG_LOG(ERROR,
+ "HPI_ADAPTER_GET_MODE failed, aborting\n");
+ goto err;
+ }
+
+ /* Note: request_irq calls asihpi_isr here */
+ if (request_irq(pci_dev->irq, asihpi_isr, IRQF_SHARED,
+ "asihpi", &adapters[adapter_index])) {
+ dev_err(&pci_dev->dev, "request_irq(%d) failed\n",
+ pci_dev->irq);
+ goto err;
+ }
+
+ adapters[adapter_index].interrupt_mode = 1;
+
+ dev_info(&pci_dev->dev, "using irq %d\n", pci_dev->irq);
+ adapters[adapter_index].irq = pci_dev->irq;
+ } else {
+ dev_info(&pci_dev->dev, "using polled mode\n");
+ }
+
dev_info(&pci_dev->dev, "probe succeeded for ASI%04X HPI index %d\n",
adapter.adapter->type, adapter_index);
@@ -431,6 +524,15 @@ void asihpi_adapter_remove(struct pci_dev *pci_dev)
pa = pci_get_drvdata(pci_dev);
pci = pa->adapter->pci;
+ /* Disable IRQ generation on DSP side */
+ hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
+ HPI_ADAPTER_SET_PROPERTY);
+ hm.adapter_index = pa->adapter->index;
+ hm.u.ax.property_set.property = HPI_ADAPTER_PROPERTY_IRQ_RATE;
+ hm.u.ax.property_set.parameter1 = 0;
+ hm.u.ax.property_set.parameter2 = 0;
+ hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
+
hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
HPI_ADAPTER_DELETE);
hm.adapter_index = pa->adapter->index;
@@ -442,8 +544,10 @@ void asihpi_adapter_remove(struct pci_dev *pci_dev)
iounmap(pci.ap_mem_base[idx]);
}
- if (pa->p_buffer)
- vfree(pa->p_buffer);
+ if (pa->irq)
+ free_irq(pa->irq, pa);
+
+ vfree(pa->p_buffer);
if (1)
dev_info(&pci_dev->dev,
diff --git a/sound/pci/asihpi/hpios.h b/sound/pci/asihpi/hpios.h
index d3fbd0d76c37..4e383601b9cf 100644
--- a/sound/pci/asihpi/hpios.h
+++ b/sound/pci/asihpi/hpios.h
@@ -41,10 +41,6 @@ HPI Operating System Specific macros for Linux Kernel driver
#define HPI_NO_OS_FILE_OPS
-#ifdef CONFIG_64BIT
-#define HPI64BIT
-#endif
-
/** Details of a memory area allocated with pci_alloc_consistent
Need all info for parameters to pci_free_consistent
*/
@@ -155,6 +151,10 @@ struct hpi_adapter {
struct hpi_adapter_obj *adapter;
struct snd_card *snd_card;
+ int irq;
+ int interrupt_mode;
+ void (*interrupt_callback) (struct hpi_adapter *);
+
/* mutex prevents contention for one card
between multiple user programs (via ioctl) */
struct mutex mutex;
diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c
index 7895c5d300c7..9c1c4452a8ee 100644
--- a/sound/pci/atiixp.c
+++ b/sound/pci/atiixp.c
@@ -688,9 +688,7 @@ static void snd_atiixp_xrun_dma(struct atiixp *chip, struct atiixp_dma *dma)
if (! dma->substream || ! dma->running)
return;
dev_dbg(chip->card->dev, "XRUN detected (DMA %d)\n", dma->ops->type);
- snd_pcm_stream_lock(dma->substream);
- snd_pcm_stop(dma->substream, SNDRV_PCM_STATE_XRUN);
- snd_pcm_stream_unlock(dma->substream);
+ snd_pcm_stop_xrun(dma->substream);
}
/*
diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c
index 3c3241309a30..b2f63e0727de 100644
--- a/sound/pci/atiixp_modem.c
+++ b/sound/pci/atiixp_modem.c
@@ -638,9 +638,7 @@ static void snd_atiixp_xrun_dma(struct atiixp_modem *chip,
if (! dma->substream || ! dma->running)
return;
dev_dbg(chip->card->dev, "XRUN detected (DMA %d)\n", dma->ops->type);
- snd_pcm_stream_lock(dma->substream);
- snd_pcm_stop(dma->substream, SNDRV_PCM_STATE_XRUN);
- snd_pcm_stream_unlock(dma->substream);
+ snd_pcm_stop_xrun(dma->substream);
}
/*
diff --git a/sound/pci/au88x0/au88x0.c b/sound/pci/au88x0/au88x0.c
index 21ce31f636bc..996369134ea8 100644
--- a/sound/pci/au88x0/au88x0.c
+++ b/sound/pci/au88x0/au88x0.c
@@ -48,11 +48,10 @@ static void vortex_fix_latency(struct pci_dev *vortex)
{
int rc;
if (!(rc = pci_write_config_byte(vortex, 0x40, 0xff))) {
- pr_info( CARD_NAME
- ": vortex latency is 0xff\n");
+ dev_info(&vortex->dev, "vortex latency is 0xff\n");
} else {
- pr_warn( CARD_NAME
- ": could not set vortex latency: pci error 0x%x\n", rc);
+ dev_warn(&vortex->dev,
+ "could not set vortex latency: pci error 0x%x\n", rc);
}
}
@@ -70,11 +69,10 @@ static void vortex_fix_agp_bridge(struct pci_dev *via)
if (!(rc = pci_read_config_byte(via, 0x42, &value))
&& ((value & 0x10)
|| !(rc = pci_write_config_byte(via, 0x42, value | 0x10)))) {
- pr_info( CARD_NAME
- ": bridge config is 0x%x\n", value | 0x10);
+ dev_info(&via->dev, "bridge config is 0x%x\n", value | 0x10);
} else {
- pr_warn( CARD_NAME
- ": could not set vortex latency: pci error 0x%x\n", rc);
+ dev_warn(&via->dev,
+ "could not set vortex latency: pci error 0x%x\n", rc);
}
}
@@ -97,7 +95,8 @@ static void snd_vortex_workaround(struct pci_dev *vortex, int fix)
PCI_DEVICE_ID_AMD_FE_GATE_7007, NULL);
}
if (via) {
- pr_info( CARD_NAME ": Activating latency workaround...\n");
+ dev_info(&vortex->dev,
+ "Activating latency workaround...\n");
vortex_fix_latency(vortex);
vortex_fix_agp_bridge(via);
}
@@ -153,7 +152,7 @@ snd_vortex_create(struct snd_card *card, struct pci_dev *pci, vortex_t ** rchip)
return err;
if (pci_set_dma_mask(pci, DMA_BIT_MASK(32)) < 0 ||
pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32)) < 0) {
- pr_err( "error to set DMA mask\n");
+ dev_err(card->dev, "error to set DMA mask\n");
pci_disable_device(pci);
return -ENXIO;
}
@@ -182,7 +181,7 @@ snd_vortex_create(struct snd_card *card, struct pci_dev *pci, vortex_t ** rchip)
chip->mmio = pci_ioremap_bar(pci, 0);
if (!chip->mmio) {
- pr_err( "MMIO area remap failed.\n");
+ dev_err(card->dev, "MMIO area remap failed.\n");
err = -ENOMEM;
goto ioremap_out;
}
@@ -191,14 +190,14 @@ snd_vortex_create(struct snd_card *card, struct pci_dev *pci, vortex_t ** rchip)
* This must be done before we do request_irq otherwise we can get spurious
* interrupts that we do not handle properly and make a mess of things */
if ((err = vortex_core_init(chip)) != 0) {
- pr_err( "hw core init failed\n");
+ dev_err(card->dev, "hw core init failed\n");
goto core_out;
}
if ((err = request_irq(pci->irq, vortex_interrupt,
IRQF_SHARED, KBUILD_MODNAME,
chip)) != 0) {
- pr_err( "cannot grab irq\n");
+ dev_err(card->dev, "cannot grab irq\n");
goto irq_out;
}
chip->irq = pci->irq;
@@ -315,7 +314,7 @@ snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
if (snd_seq_device_new(card, 1, SNDRV_SEQ_DEV_ID_VORTEX_SYNTH,
sizeof(snd_vortex_synth_arg_t), &wave) < 0
|| wave == NULL) {
- snd_printk(KERN_ERR "Can't initialize Aureal wavetable synth\n");
+ dev_err(card->dev, "Can't initialize Aureal wavetable synth\n");
} else {
snd_vortex_synth_arg_t *arg;
@@ -342,11 +341,11 @@ snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
chip->rev = pci->revision;
#ifdef CHIP_AU8830
if ((chip->rev) != 0xfe && (chip->rev) != 0xfa) {
- pr_alert(
- "vortex: The revision (%x) of your card has not been seen before.\n",
+ dev_alert(card->dev,
+ "The revision (%x) of your card has not been seen before.\n",
chip->rev);
- pr_alert(
- "vortex: Please email the results of 'lspci -vv' to openvortex-dev@nongnu.org.\n");
+ dev_alert(card->dev,
+ "Please email the results of 'lspci -vv' to openvortex-dev@nongnu.org.\n");
snd_card_free(card);
err = -ENODEV;
return err;
diff --git a/sound/pci/au88x0/au88x0.h b/sound/pci/au88x0/au88x0.h
index 466a5c8e8354..3a8fefefea77 100644
--- a/sound/pci/au88x0/au88x0.h
+++ b/sound/pci/au88x0/au88x0.h
@@ -243,7 +243,7 @@ static int vortex_core_init(vortex_t * card);
static int vortex_core_shutdown(vortex_t * card);
static void vortex_enable_int(vortex_t * card);
static irqreturn_t vortex_interrupt(int irq, void *dev_id);
-static int vortex_alsafmt_aspfmt(int alsafmt);
+static int vortex_alsafmt_aspfmt(int alsafmt, vortex_t *v);
/* Connection stuff. */
static void vortex_connect_default(vortex_t * vortex, int en);
@@ -278,7 +278,7 @@ static void vortex_mix_setvolumebyte(vortex_t * vortex, unsigned char mix,
static void vortex_Vort3D_enable(vortex_t * v);
static void vortex_Vort3D_disable(vortex_t * v);
static void vortex_Vort3D_connect(vortex_t * vortex, int en);
-static void vortex_Vort3D_InitializeSource(a3dsrc_t * a, int en);
+static void vortex_Vort3D_InitializeSource(a3dsrc_t *a, int en, vortex_t *v);
#endif
/* Driver stuff. */
diff --git a/sound/pci/au88x0/au88x0_a3d.c b/sound/pci/au88x0/au88x0_a3d.c
index 30f760e3d2c0..ab0f87312911 100644
--- a/sound/pci/au88x0/au88x0_a3d.c
+++ b/sound/pci/au88x0/au88x0_a3d.c
@@ -484,12 +484,13 @@ static void a3dsrc_ZeroState(a3dsrc_t * a)
}
/* Reset entire A3D engine */
-static void a3dsrc_ZeroStateA3D(a3dsrc_t * a)
+static void a3dsrc_ZeroStateA3D(a3dsrc_t *a, vortex_t *v)
{
int i, var, var2;
if ((a->vortex) == NULL) {
- pr_err( "vortex: ZeroStateA3D: ERROR: a->vortex is NULL\n");
+ dev_err(v->card->dev,
+ "ZeroStateA3D: ERROR: a->vortex is NULL\n");
return;
}
@@ -601,7 +602,7 @@ static void vortex_Vort3D_enable(vortex_t *v)
Vort3DRend_Initialize(v, XT_HEADPHONE);
for (i = 0; i < NR_A3D; i++) {
vortex_A3dSourceHw_Initialize(v, i % 4, i >> 2);
- a3dsrc_ZeroStateA3D(&(v->a3d[0]));
+ a3dsrc_ZeroStateA3D(&v->a3d[0], v);
}
/* Register ALSA controls */
vortex_a3d_register_controls(v);
@@ -628,15 +629,15 @@ static void vortex_Vort3D_connect(vortex_t * v, int en)
v->mixxtlk[0] =
vortex_adb_checkinout(v, v->fixed_res, en, VORTEX_RESOURCE_MIXIN);
if (v->mixxtlk[0] < 0) {
- pr_warn
- ("vortex: vortex_Vort3D: ERROR: not enough free mixer resources.\n");
+ dev_warn(v->card->dev,
+ "vortex_Vort3D: ERROR: not enough free mixer resources.\n");
return;
}
v->mixxtlk[1] =
vortex_adb_checkinout(v, v->fixed_res, en, VORTEX_RESOURCE_MIXIN);
if (v->mixxtlk[1] < 0) {
- pr_warn
- ("vortex: vortex_Vort3D: ERROR: not enough free mixer resources.\n");
+ dev_warn(v->card->dev,
+ "vortex_Vort3D: ERROR: not enough free mixer resources.\n");
return;
}
#endif
@@ -676,11 +677,11 @@ static void vortex_Vort3D_connect(vortex_t * v, int en)
}
/* Initialize one single A3D source. */
-static void vortex_Vort3D_InitializeSource(a3dsrc_t * a, int en)
+static void vortex_Vort3D_InitializeSource(a3dsrc_t *a, int en, vortex_t *v)
{
if (a->vortex == NULL) {
- pr_warn
- ("vortex: Vort3D_InitializeSource: A3D source not initialized\n");
+ dev_warn(v->card->dev,
+ "Vort3D_InitializeSource: A3D source not initialized\n");
return;
}
if (en) {
diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c
index 72e81286b70e..4667c3232b7f 100644
--- a/sound/pci/au88x0/au88x0_core.c
+++ b/sound/pci/au88x0/au88x0_core.c
@@ -285,8 +285,8 @@ vortex_mixer_addWTD(vortex_t * vortex, unsigned char mix, unsigned char ch)
temp = hwread(vortex->mmio, prev);
//printk(KERN_INFO "vortex: mixAddWTD: while addr=%x, val=%x\n", prev, temp);
if ((++lifeboat) > 0xf) {
- pr_err(
- "vortex_mixer_addWTD: lifeboat overflow\n");
+ dev_err(vortex->card->dev,
+ "vortex_mixer_addWTD: lifeboat overflow\n");
return 0;
}
}
@@ -303,7 +303,7 @@ vortex_mixer_delWTD(vortex_t * vortex, unsigned char mix, unsigned char ch)
eax = hwread(vortex->mmio, VORTEX_MIXER_SR);
if (((1 << ch) & eax) == 0) {
- pr_err( "mix ALARM %x\n", eax);
+ dev_err(vortex->card->dev, "mix ALARM %x\n", eax);
return 0;
}
ebp = VORTEX_MIXER_CHNBASE + (ch << 2);
@@ -324,8 +324,8 @@ vortex_mixer_delWTD(vortex_t * vortex, unsigned char mix, unsigned char ch)
//printk(KERN_INFO "vortex: mixdelWTD: 1 addr=%x, val=%x, src=%x\n", ebx, edx, src);
while ((edx & 0xf) != mix) {
if ((esi) > 0xf) {
- pr_err(
- "vortex: mixdelWTD: error lifeboat overflow\n");
+ dev_err(vortex->card->dev,
+ "mixdelWTD: error lifeboat overflow\n");
return 0;
}
esp14 = ebx;
@@ -492,7 +492,7 @@ vortex_src_persist_convratio(vortex_t * vortex, unsigned char src, int ratio)
hwwrite(vortex->mmio, VORTEX_SRC_CONVRATIO + (src << 2), ratio);
temp = hwread(vortex->mmio, VORTEX_SRC_CONVRATIO + (src << 2));
if ((++lifeboat) > 0x9) {
- pr_err( "Vortex: Src cvr fail\n");
+ dev_err(vortex->card->dev, "Src cvr fail\n");
break;
}
}
@@ -684,8 +684,8 @@ vortex_src_addWTD(vortex_t * vortex, unsigned char src, unsigned char ch)
temp = hwread(vortex->mmio, prev);
//printk(KERN_INFO "vortex: srcAddWTD: while addr=%x, val=%x\n", prev, temp);
if ((++lifeboat) > 0xf) {
- pr_err(
- "vortex_src_addWTD: lifeboat overflow\n");
+ dev_err(vortex->card->dev,
+ "vortex_src_addWTD: lifeboat overflow\n");
return 0;
}
}
@@ -703,7 +703,7 @@ vortex_src_delWTD(vortex_t * vortex, unsigned char src, unsigned char ch)
eax = hwread(vortex->mmio, VORTEX_SRCBLOCK_SR);
if (((1 << ch) & eax) == 0) {
- pr_err( "src alarm\n");
+ dev_err(vortex->card->dev, "src alarm\n");
return 0;
}
ebp = VORTEX_SRC_CHNBASE + (ch << 2);
@@ -724,8 +724,8 @@ vortex_src_delWTD(vortex_t * vortex, unsigned char src, unsigned char ch)
//printk(KERN_INFO "vortex: srcdelWTD: 1 addr=%x, val=%x, src=%x\n", ebx, edx, src);
while ((edx & 0xf) != src) {
if ((esi) > 0xf) {
- pr_warn
- ("vortex: srcdelWTD: error, lifeboat overflow\n");
+ dev_warn(vortex->card->dev,
+ "srcdelWTD: error, lifeboat overflow\n");
return 0;
}
esp14 = ebx;
@@ -819,8 +819,8 @@ vortex_fifo_setadbctrl(vortex_t * vortex, int fifo, int stereo, int priority,
do {
temp = hwread(vortex->mmio, VORTEX_FIFO_ADBCTRL + (fifo << 2));
if (lifeboat++ > 0xbb8) {
- pr_err(
- "Vortex: vortex_fifo_setadbctrl fail\n");
+ dev_err(vortex->card->dev,
+ "vortex_fifo_setadbctrl fail\n");
break;
}
}
@@ -915,7 +915,8 @@ vortex_fifo_setwtctrl(vortex_t * vortex, int fifo, int ctrl, int priority,
do {
temp = hwread(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2));
if (lifeboat++ > 0xbb8) {
- pr_err( "Vortex: vortex_fifo_setwtctrl fail\n");
+ dev_err(vortex->card->dev,
+ "vortex_fifo_setwtctrl fail\n");
break;
}
}
@@ -1042,7 +1043,7 @@ static void vortex_fifo_init(vortex_t * vortex)
for (x = NR_ADB - 1; x >= 0; x--) {
hwwrite(vortex->mmio, addr, (FIFO_U0 | FIFO_U1));
if (hwread(vortex->mmio, addr) != (FIFO_U0 | FIFO_U1))
- pr_err( "bad adb fifo reset!");
+ dev_err(vortex->card->dev, "bad adb fifo reset!");
vortex_fifo_clearadbdata(vortex, x, FIFO_SIZE);
addr -= 4;
}
@@ -1053,9 +1054,9 @@ static void vortex_fifo_init(vortex_t * vortex)
for (x = NR_WT - 1; x >= 0; x--) {
hwwrite(vortex->mmio, addr, FIFO_U0);
if (hwread(vortex->mmio, addr) != FIFO_U0)
- pr_err(
- "bad wt fifo reset (0x%08x, 0x%08x)!\n",
- addr, hwread(vortex->mmio, addr));
+ dev_err(vortex->card->dev,
+ "bad wt fifo reset (0x%08x, 0x%08x)!\n",
+ addr, hwread(vortex->mmio, addr));
vortex_fifo_clearwtdata(vortex, x, FIFO_SIZE);
addr -= 4;
}
@@ -1213,8 +1214,9 @@ static int vortex_adbdma_bufshift(vortex_t * vortex, int adbdma)
if (dma->period_virt >= dma->nr_periods)
dma->period_virt -= dma->nr_periods;
if (delta != 1)
- pr_info( "vortex: %d virt=%d, real=%d, delta=%d\n",
- adbdma, dma->period_virt, dma->period_real, delta);
+ dev_info(vortex->card->dev,
+ "%d virt=%d, real=%d, delta=%d\n",
+ adbdma, dma->period_virt, dma->period_real, delta);
return delta;
}
@@ -1482,8 +1484,8 @@ static int vortex_wtdma_bufshift(vortex_t * vortex, int wtdma)
dma->period_real = page;
if (delta != 1)
- pr_warn( "vortex: wt virt = %d, delta = %d\n",
- dma->period_virt, delta);
+ dev_warn(vortex->card->dev, "wt virt = %d, delta = %d\n",
+ dma->period_virt, delta);
return delta;
}
@@ -1667,9 +1669,9 @@ vortex_adb_addroutes(vortex_t * vortex, unsigned char channel,
hwread(vortex->mmio,
VORTEX_ADB_RTBASE + (temp << 2)) & ADB_MASK;
if ((lifeboat++) > ADB_MASK) {
- pr_err(
- "vortex_adb_addroutes: unending route! 0x%x\n",
- *route);
+ dev_err(vortex->card->dev,
+ "vortex_adb_addroutes: unending route! 0x%x\n",
+ *route);
return;
}
}
@@ -1703,9 +1705,9 @@ vortex_adb_delroutes(vortex_t * vortex, unsigned char channel,
hwread(vortex->mmio,
VORTEX_ADB_RTBASE + (prev << 2)) & ADB_MASK;
if (((lifeboat++) > ADB_MASK) || (temp == ADB_MASK)) {
- pr_err(
- "vortex_adb_delroutes: route not found! 0x%x\n",
- route0);
+ dev_err(vortex->card->dev,
+ "vortex_adb_delroutes: route not found! 0x%x\n",
+ route0);
return;
}
}
@@ -2045,7 +2047,9 @@ vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out, int restype)
}
}
}
- pr_err( "vortex: FATAL: ResManager: resource type %d exhausted.\n", restype);
+ dev_err(vortex->card->dev,
+ "FATAL: ResManager: resource type %d exhausted.\n",
+ restype);
return -ENOMEM;
}
@@ -2173,11 +2177,13 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir,
memset(stream->resources, 0,
sizeof(unsigned char) *
VORTEX_RESOURCE_LAST);
- pr_err( "vortex: out of A3D sources. Sorry\n");
+ dev_err(vortex->card->dev,
+ "out of A3D sources. Sorry\n");
return -EBUSY;
}
/* (De)Initialize A3D hardware source. */
- vortex_Vort3D_InitializeSource(&(vortex->a3d[a3d]), en);
+ vortex_Vort3D_InitializeSource(&vortex->a3d[a3d], en,
+ vortex);
}
/* Make SPDIF out exclusive to "spdif" device when in use. */
if ((stream->type == VORTEX_PCM_SPDIF) && (en)) {
@@ -2421,7 +2427,7 @@ static irqreturn_t vortex_interrupt(int irq, void *dev_id)
hwread(vortex->mmio, VORTEX_IRQ_SOURCE);
// Is at least one IRQ flag set?
if (source == 0) {
- pr_err( "vortex: missing irq source\n");
+ dev_err(vortex->card->dev, "missing irq source\n");
return IRQ_NONE;
}
@@ -2429,19 +2435,19 @@ static irqreturn_t vortex_interrupt(int irq, void *dev_id)
// Attend every interrupt source.
if (unlikely(source & IRQ_ERR_MASK)) {
if (source & IRQ_FATAL) {
- pr_err( "vortex: IRQ fatal error\n");
+ dev_err(vortex->card->dev, "IRQ fatal error\n");
}
if (source & IRQ_PARITY) {
- pr_err( "vortex: IRQ parity error\n");
+ dev_err(vortex->card->dev, "IRQ parity error\n");
}
if (source & IRQ_REG) {
- pr_err( "vortex: IRQ reg error\n");
+ dev_err(vortex->card->dev, "IRQ reg error\n");
}
if (source & IRQ_FIFO) {
- pr_err( "vortex: IRQ fifo error\n");
+ dev_err(vortex->card->dev, "IRQ fifo error\n");
}
if (source & IRQ_DMA) {
- pr_err( "vortex: IRQ dma error\n");
+ dev_err(vortex->card->dev, "IRQ dma error\n");
}
handled = 1;
}
@@ -2489,7 +2495,7 @@ static irqreturn_t vortex_interrupt(int irq, void *dev_id)
}
if (!handled) {
- pr_err( "vortex: unknown irq source %x\n", source);
+ dev_err(vortex->card->dev, "unknown irq source %x\n", source);
}
return IRQ_RETVAL(handled);
}
@@ -2546,7 +2552,7 @@ vortex_codec_write(struct snd_ac97 * codec, unsigned short addr, unsigned short
while (!(hwread(card->mmio, VORTEX_CODEC_CTRL) & 0x100)) {
udelay(100);
if (lifeboat++ > POLL_COUNT) {
- pr_err( "vortex: ac97 codec stuck busy\n");
+ dev_err(card->card->dev, "ac97 codec stuck busy\n");
return;
}
}
@@ -2572,7 +2578,7 @@ static unsigned short vortex_codec_read(struct snd_ac97 * codec, unsigned short
while (!(hwread(card->mmio, VORTEX_CODEC_CTRL) & 0x100)) {
udelay(100);
if (lifeboat++ > POLL_COUNT) {
- pr_err( "vortex: ac97 codec stuck busy\n");
+ dev_err(card->card->dev, "ac97 codec stuck busy\n");
return 0xffff;
}
}
@@ -2586,7 +2592,8 @@ static unsigned short vortex_codec_read(struct snd_ac97 * codec, unsigned short
udelay(100);
data = hwread(card->mmio, VORTEX_CODEC_IO);
if (lifeboat++ > POLL_COUNT) {
- pr_err( "vortex: ac97 address never arrived\n");
+ dev_err(card->card->dev,
+ "ac97 address never arrived\n");
return 0xffff;
}
} while ((data & VORTEX_CODEC_ADDMASK) !=
@@ -2683,7 +2690,7 @@ static void vortex_spdif_init(vortex_t * vortex, int spdif_sr, int spdif_mode)
static int vortex_core_init(vortex_t *vortex)
{
- pr_info( "Vortex: init.... ");
+ dev_info(vortex->card->dev, "init started\n");
/* Hardware Init. */
hwwrite(vortex->mmio, VORTEX_CTRL, 0xffffffff);
msleep(5);
@@ -2728,7 +2735,7 @@ static int vortex_core_init(vortex_t *vortex)
//vortex_enable_timer_int(vortex);
//vortex_disable_timer_int(vortex);
- pr_info( "done.\n");
+ dev_info(vortex->card->dev, "init.... done.\n");
spin_lock_init(&vortex->lock);
return 0;
@@ -2737,7 +2744,7 @@ static int vortex_core_init(vortex_t *vortex)
static int vortex_core_shutdown(vortex_t * vortex)
{
- pr_info( "Vortex: shutdown...");
+ dev_info(vortex->card->dev, "shutdown started\n");
#ifndef CHIP_AU8820
vortex_eq_free(vortex);
vortex_Vort3D_disable(vortex);
@@ -2759,13 +2766,13 @@ static int vortex_core_shutdown(vortex_t * vortex)
msleep(5);
hwwrite(vortex->mmio, VORTEX_IRQ_SOURCE, 0xffff);
- pr_info( "done.\n");
+ dev_info(vortex->card->dev, "shutdown.... done.\n");
return 0;
}
/* Alsa support. */
-static int vortex_alsafmt_aspfmt(int alsafmt)
+static int vortex_alsafmt_aspfmt(int alsafmt, vortex_t *v)
{
int fmt;
@@ -2793,7 +2800,8 @@ static int vortex_alsafmt_aspfmt(int alsafmt)
break;
default:
fmt = 0x8;
- pr_err( "vortex: format unsupported %d\n", alsafmt);
+ dev_err(v->card->dev,
+ "format unsupported %d\n", alsafmt);
break;
}
return fmt;
diff --git a/sound/pci/au88x0/au88x0_eq.c b/sound/pci/au88x0/au88x0_eq.c
index 9404ba73eaf6..9585c5c63b96 100644
--- a/sound/pci/au88x0/au88x0_eq.c
+++ b/sound/pci/au88x0/au88x0_eq.c
@@ -845,7 +845,8 @@ snd_vortex_peaks_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u
vortex_Eqlzr_GetAllPeaks(vortex, peaks, &count);
if (count != 20) {
- pr_err( "vortex: peak count error 20 != %d \n", count);
+ dev_err(vortex->card->dev,
+ "peak count error 20 != %d\n", count);
return -1;
}
for (i = 0; i < 20; i++)
diff --git a/sound/pci/au88x0/au88x0_game.c b/sound/pci/au88x0/au88x0_game.c
index 72daf6cf8169..151815b857a0 100644
--- a/sound/pci/au88x0/au88x0_game.c
+++ b/sound/pci/au88x0/au88x0_game.c
@@ -98,7 +98,8 @@ static int vortex_gameport_register(vortex_t *vortex)
vortex->gameport = gp = gameport_allocate_port();
if (!gp) {
- pr_err( "vortex: cannot allocate memory for gameport\n");
+ dev_err(vortex->card->dev,
+ "cannot allocate memory for gameport\n");
return -ENOMEM;
}
diff --git a/sound/pci/au88x0/au88x0_mpu401.c b/sound/pci/au88x0/au88x0_mpu401.c
index 328c1943c0c3..1025e55ca854 100644
--- a/sound/pci/au88x0/au88x0_mpu401.c
+++ b/sound/pci/au88x0/au88x0_mpu401.c
@@ -73,7 +73,7 @@ static int snd_vortex_midi(vortex_t *vortex)
/* Check if anything is OK. */
temp = hwread(vortex->mmio, VORTEX_MIDI_DATA);
if (temp != MPU401_ACK /*0xfe */ ) {
- pr_err( "midi port doesn't acknowledge!\n");
+ dev_err(vortex->card->dev, "midi port doesn't acknowledge!\n");
return -ENODEV;
}
/* Enable MPU401 interrupts. */
diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c
index 5adc6b92ffab..a6d6d8d0867a 100644
--- a/sound/pci/au88x0/au88x0_pcm.c
+++ b/sound/pci/au88x0/au88x0_pcm.c
@@ -227,7 +227,7 @@ snd_vortex_pcm_hw_params(struct snd_pcm_substream *substream,
err =
snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
if (err < 0) {
- pr_err( "Vortex: pcm page alloc failed!\n");
+ dev_err(chip->card->dev, "Vortex: pcm page alloc failed!\n");
return err;
}
/*
@@ -332,7 +332,7 @@ static int snd_vortex_pcm_prepare(struct snd_pcm_substream *substream)
dir = 1;
else
dir = 0;
- fmt = vortex_alsafmt_aspfmt(runtime->format);
+ fmt = vortex_alsafmt_aspfmt(runtime->format, chip);
spin_lock_irq(&chip->lock);
if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
vortex_adbdma_setmode(chip, dma, 1, dir, fmt,
@@ -371,7 +371,7 @@ static int snd_vortex_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
}
#ifndef CHIP_AU8810
else {
- pr_info( "vortex: wt start %d\n", dma);
+ dev_info(chip->card->dev, "wt start %d\n", dma);
vortex_wtdma_startfifo(chip, dma);
}
#endif
@@ -384,7 +384,7 @@ static int snd_vortex_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
vortex_adbdma_stopfifo(chip, dma);
#ifndef CHIP_AU8810
else {
- pr_info( "vortex: wt stop %d\n", dma);
+ dev_info(chip->card->dev, "wt stop %d\n", dma);
vortex_wtdma_stopfifo(chip, dma);
}
#endif
diff --git a/sound/pci/au88x0/au88x0_synth.c b/sound/pci/au88x0/au88x0_synth.c
index f094bac24291..78e12f4796f3 100644
--- a/sound/pci/au88x0/au88x0_synth.c
+++ b/sound/pci/au88x0/au88x0_synth.c
@@ -90,7 +90,7 @@ static int vortex_wt_allocroute(vortex_t * vortex, int wt, int nr_ch)
hwwrite(vortex->mmio, WT_PARM(wt, 2), 0);
temp = hwread(vortex->mmio, WT_PARM(wt, 3));
- pr_debug( "vortex: WT PARM3: %x\n", temp);
+ dev_dbg(vortex->card->dev, "WT PARM3: %x\n", temp);
//hwwrite(vortex->mmio, WT_PARM(wt, 3), temp);
hwwrite(vortex->mmio, WT_DELAY(wt, 0), 0);
@@ -98,7 +98,8 @@ static int vortex_wt_allocroute(vortex_t * vortex, int wt, int nr_ch)
hwwrite(vortex->mmio, WT_DELAY(wt, 2), 0);
hwwrite(vortex->mmio, WT_DELAY(wt, 3), 0);
- pr_debug( "vortex: WT GMODE: %x\n", hwread(vortex->mmio, WT_GMODE(wt)));
+ dev_dbg(vortex->card->dev, "WT GMODE: %x\n",
+ hwread(vortex->mmio, WT_GMODE(wt)));
hwwrite(vortex->mmio, WT_PARM(wt, 2), 0xffffffff);
hwwrite(vortex->mmio, WT_PARM(wt, 3), 0xcff1c810);
@@ -106,7 +107,8 @@ static int vortex_wt_allocroute(vortex_t * vortex, int wt, int nr_ch)
voice->parm0 = voice->parm1 = 0xcfb23e2f;
hwwrite(vortex->mmio, WT_PARM(wt, 0), voice->parm0);
hwwrite(vortex->mmio, WT_PARM(wt, 1), voice->parm1);
- pr_debug( "vortex: WT GMODE 2 : %x\n", hwread(vortex->mmio, WT_GMODE(wt)));
+ dev_dbg(vortex->card->dev, "WT GMODE 2 : %x\n",
+ hwread(vortex->mmio, WT_GMODE(wt)));
return 0;
}
@@ -196,14 +198,15 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt,
if ((reg == 5) || ((reg >= 7) && (reg <= 10)) || (reg == 0xc)) {
if (wt >= (NR_WT / NR_WT_PB)) {
- pr_warn
- ("vortex: WT SetReg: bank out of range. reg=0x%x, wt=%d\n",
- reg, wt);
+ dev_warn(vortex->card->dev,
+ "WT SetReg: bank out of range. reg=0x%x, wt=%d\n",
+ reg, wt);
return 0;
}
} else {
if (wt >= NR_WT) {
- pr_err( "vortex: WT SetReg: voice out of range\n");
+ dev_err(vortex->card->dev,
+ "WT SetReg: voice out of range\n");
return 0;
}
}
diff --git a/sound/pci/aw2/aw2-alsa.c b/sound/pci/aw2/aw2-alsa.c
index 3878cf5de9a4..e1cf01949fda 100644
--- a/sound/pci/aw2/aw2-alsa.c
+++ b/sound/pci/aw2/aw2-alsa.c
@@ -725,19 +725,10 @@ static int snd_aw2_new_pcm(struct aw2 *chip)
static int snd_aw2_control_switch_capture_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[2] = {
+ static const char * const texts[2] = {
"Analog", "Digital"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) {
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- }
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int snd_aw2_control_switch_capture_get(struct snd_kcontrol *kcontrol,
diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c
index 5a69e26cb2fb..fdbb9c05c77b 100644
--- a/sound/pci/azt3328.c
+++ b/sound/pci/azt3328.c
@@ -1034,11 +1034,6 @@ snd_azf3328_info_mixer_enum(struct snd_kcontrol *kcontrol,
const char * const *p = NULL;
snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = (reg.reg == IDX_MIXER_REC_SELECT) ? 2 : 1;
- uinfo->value.enumerated.items = reg.enum_c;
- if (uinfo->value.enumerated.item > reg.enum_c - 1U)
- uinfo->value.enumerated.item = reg.enum_c - 1U;
if (reg.reg == IDX_MIXER_ADVCTL2) {
switch(reg.lchan_shift) {
case 8: /* modem out sel */
@@ -1051,12 +1046,12 @@ snd_azf3328_info_mixer_enum(struct snd_kcontrol *kcontrol,
p = texts4;
break;
}
- } else
- if (reg.reg == IDX_MIXER_REC_SELECT)
+ } else if (reg.reg == IDX_MIXER_REC_SELECT)
p = texts3;
- strcpy(uinfo->value.enumerated.name, p[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo,
+ (reg.reg == IDX_MIXER_REC_SELECT) ? 2 : 1,
+ reg.enum_c, p);
}
static int
diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c
index 27de0de90018..68c0eb0a2807 100644
--- a/sound/pci/ca0106/ca0106_mixer.c
+++ b/sound/pci/ca0106/ca0106_mixer.c
@@ -185,17 +185,11 @@ static int snd_ca0106_shared_spdif_put(struct snd_kcontrol *kcontrol,
static int snd_ca0106_capture_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[6] = {
+ static const char * const texts[6] = {
"IEC958 out", "i2s mixer out", "IEC958 in", "i2s in", "AC97 in", "SRC out"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 6;
- if (uinfo->value.enumerated.item > 5)
- uinfo->value.enumerated.item = 5;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 6, texts);
}
static int snd_ca0106_capture_source_get(struct snd_kcontrol *kcontrol,
@@ -228,17 +222,11 @@ static int snd_ca0106_capture_source_put(struct snd_kcontrol *kcontrol,
static int snd_ca0106_i2c_capture_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[6] = {
+ static const char * const texts[4] = {
"Phone", "Mic", "Line in", "Aux"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 4;
- if (uinfo->value.enumerated.item > 3)
- uinfo->value.enumerated.item = 3;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 4, texts);
}
static int snd_ca0106_i2c_capture_source_get(struct snd_kcontrol *kcontrol,
@@ -273,29 +261,17 @@ static int snd_ca0106_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
static int snd_ca0106_capture_line_in_side_out_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[2] = { "Side out", "Line in" };
+ static const char * const texts[2] = { "Side out", "Line in" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int snd_ca0106_capture_mic_line_in_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[2] = { "Line in", "Mic in" };
+ static const char * const texts[2] = { "Line in", "Mic in" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int snd_ca0106_capture_mic_line_in_get(struct snd_kcontrol *kcontrol,
diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c
index 454659074390..977a59855fa6 100644
--- a/sound/pci/ctxfi/ctatc.c
+++ b/sound/pci/ctxfi/ctatc.c
@@ -438,7 +438,9 @@ atc_pcm_playback_position(struct ct_atc *atc, struct ct_atc_pcm *apcm)
position = src->ops->get_ca(src);
if (position < apcm->vm_block->addr) {
- snd_printdd("ctxfi: bad ca - ca=0x%08x, vba=0x%08x, vbs=0x%08x\n", position, apcm->vm_block->addr, apcm->vm_block->size);
+ dev_dbg(atc->card->dev,
+ "bad ca - ca=0x%08x, vba=0x%08x, vbs=0x%08x\n",
+ position, apcm->vm_block->addr, apcm->vm_block->size);
position = apcm->vm_block->addr;
}
@@ -1145,7 +1147,6 @@ static int atc_release_resources(struct ct_atc *atc)
int i;
struct daio_mgr *daio_mgr = NULL;
struct dao *dao = NULL;
- struct dai *dai = NULL;
struct daio *daio = NULL;
struct sum_mgr *sum_mgr = NULL;
struct src_mgr *src_mgr = NULL;
@@ -1172,9 +1173,6 @@ static int atc_release_resources(struct ct_atc *atc)
dao = container_of(daio, struct dao, daio);
dao->ops->clear_left_input(dao);
dao->ops->clear_right_input(dao);
- } else {
- dai = container_of(daio, struct dai, daio);
- /* some thing to do for dai ... */
}
daio_mgr->put_daio(daio_mgr, daio);
}
@@ -1299,7 +1297,7 @@ static int atc_identify_card(struct ct_atc *atc, unsigned int ssid)
atc->model = CT20K2_UNKNOWN;
}
atc->model_name = ct_subsys_name[atc->model];
- snd_printd("ctxfi: chip %s model %s (%04x:%04x) is found\n",
+ dev_info(atc->card->dev, "chip %s model %s (%04x:%04x) is found\n",
atc->chip_name, atc->model_name,
vendor_id, device_id);
return 0;
diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c
index c1c3f8816fff..9b87dd28de83 100644
--- a/sound/pci/ctxfi/ctdaio.c
+++ b/sound/pci/ctxfi/ctdaio.c
@@ -528,8 +528,6 @@ static int get_daio_rsc(struct daio_mgr *mgr,
struct daio **rdaio)
{
int err;
- struct dai *dai = NULL;
- struct dao *dao = NULL;
unsigned long flags;
*rdaio = NULL;
@@ -544,27 +542,30 @@ static int get_daio_rsc(struct daio_mgr *mgr,
return err;
}
+ err = -ENOMEM;
/* Allocate mem for daio resource */
if (desc->type <= DAIO_OUT_MAX) {
- dao = kzalloc(sizeof(*dao), GFP_KERNEL);
- if (!dao) {
- err = -ENOMEM;
+ struct dao *dao = kzalloc(sizeof(*dao), GFP_KERNEL);
+ if (!dao)
goto error;
- }
+
err = dao_rsc_init(dao, desc, mgr);
- if (err)
+ if (err) {
+ kfree(dao);
goto error;
+ }
*rdaio = &dao->daio;
} else {
- dai = kzalloc(sizeof(*dai), GFP_KERNEL);
- if (!dai) {
- err = -ENOMEM;
+ struct dai *dai = kzalloc(sizeof(*dai), GFP_KERNEL);
+ if (!dai)
goto error;
- }
+
err = dai_rsc_init(dai, desc, mgr);
- if (err)
+ if (err) {
+ kfree(dai);
goto error;
+ }
*rdaio = &dai->daio;
}
@@ -575,11 +576,6 @@ static int get_daio_rsc(struct daio_mgr *mgr,
return 0;
error:
- if (dao)
- kfree(dao);
- else if (dai)
- kfree(dai);
-
spin_lock_irqsave(&mgr->mgr_lock, flags);
daio_mgr_put_rsc(&mgr->mgr, desc->type);
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
diff --git a/sound/pci/ctxfi/cttimer.c b/sound/pci/ctxfi/cttimer.c
index 03fb909085af..a5d460453d7b 100644
--- a/sound/pci/ctxfi/cttimer.c
+++ b/sound/pci/ctxfi/cttimer.c
@@ -421,12 +421,12 @@ struct ct_timer *ct_timer_new(struct ct_atc *atc)
atimer->atc = atc;
hw = atc->hw;
if (!use_system_timer && hw->set_timer_irq) {
- snd_printd(KERN_INFO "ctxfi: Use xfi-native timer\n");
+ dev_info(atc->card->dev, "Use xfi-native timer\n");
atimer->ops = &ct_xfitimer_ops;
hw->irq_callback_data = atimer;
hw->irq_callback = ct_timer_interrupt;
} else {
- snd_printd(KERN_INFO "ctxfi: Use system timer\n");
+ dev_info(atc->card->dev, "Use system timer\n");
atimer->ops = &ct_systimer_ops;
}
return atimer;
diff --git a/sound/pc