summaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/Makefile6
-rw-r--r--sound/aoa/Kconfig5
-rw-r--r--sound/aoa/Makefile4
-rw-r--r--sound/aoa/codecs/Kconfig4
-rw-r--r--sound/aoa/codecs/snd-aoa-codec-tas-basstreble.h134
-rw-r--r--sound/aoa/codecs/snd-aoa-codec-tas.c413
-rw-r--r--sound/aoa/codecs/snd-aoa-codec-tas.h8
-rw-r--r--sound/aoa/codecs/snd-aoa-codec-toonie.c17
-rw-r--r--sound/aoa/core/snd-aoa-gpio-feature.c29
-rw-r--r--sound/aoa/core/snd-aoa-gpio-pmf.c14
-rw-r--r--sound/aoa/fabrics/snd-aoa-fabric-layout.c40
-rw-r--r--sound/aoa/soundbus/Kconfig3
-rw-r--r--sound/aoa/soundbus/core.c22
-rw-r--r--sound/aoa/soundbus/i2sbus/i2sbus-control.c79
-rw-r--r--sound/aoa/soundbus/i2sbus/i2sbus-control.h37
-rw-r--r--sound/aoa/soundbus/i2sbus/i2sbus-core.c128
-rw-r--r--sound/aoa/soundbus/i2sbus/i2sbus.h18
-rw-r--r--sound/arm/aaci.c7
-rw-r--r--sound/arm/sa11xx-uda1341.c1
-rw-r--r--sound/core/Kconfig4
-rw-r--r--sound/core/control.c160
-rw-r--r--sound/core/control_compat.c4
-rw-r--r--sound/core/device.c20
-rw-r--r--sound/core/hwdep.c12
-rw-r--r--sound/core/info.c109
-rw-r--r--sound/core/info_oss.c6
-rw-r--r--sound/core/init.c116
-rw-r--r--sound/core/memalloc.c1
-rw-r--r--sound/core/oss/mixer_oss.c25
-rw-r--r--sound/core/oss/pcm_oss.c26
-rw-r--r--sound/core/pcm.c128
-rw-r--r--sound/core/pcm_compat.c2
-rw-r--r--sound/core/pcm_memory.c2
-rw-r--r--sound/core/pcm_native.c49
-rw-r--r--sound/core/rawmidi.c38
-rw-r--r--sound/core/rtctimer.c2
-rw-r--r--sound/core/seq/oss/seq_oss.c3
-rw-r--r--sound/core/seq/seq_device.c22
-rw-r--r--sound/core/seq/seq_info.c6
-rw-r--r--sound/core/seq/seq_memory.h2
-rw-r--r--sound/core/seq/seq_ports.c10
-rw-r--r--sound/core/sgbuf.c10
-rw-r--r--sound/core/sound.c86
-rw-r--r--sound/core/sound_oss.c3
-rw-r--r--sound/core/timer.c67
-rw-r--r--sound/drivers/Kconfig13
-rw-r--r--sound/drivers/Makefile2
-rw-r--r--sound/drivers/dummy.c20
-rw-r--r--sound/drivers/mpu401/mpu401.c9
-rw-r--r--sound/drivers/mtpav.c2
-rw-r--r--sound/drivers/mts64.c1091
-rw-r--r--sound/drivers/opl4/opl4_proc.c9
-rw-r--r--sound/drivers/serial-u16550.c2
-rw-r--r--sound/drivers/vx/vx_mixer.c17
-rw-r--r--sound/drivers/vx/vx_pcm.c7
-rw-r--r--sound/i2c/cs8427.c116
-rw-r--r--sound/i2c/other/ak4xxx-adda.c585
-rw-r--r--sound/isa/ad1816a/ad1816a.c2
-rw-r--r--sound/isa/ad1816a/ad1816a_lib.c57
-rw-r--r--sound/isa/ad1848/ad1848_lib.c51
-rw-r--r--sound/isa/als100.c2
-rw-r--r--sound/isa/azt2320.c2
-rw-r--r--sound/isa/cs423x/Makefile1
-rw-r--r--sound/isa/cs423x/cs4231.c2
-rw-r--r--sound/isa/cs423x/cs4231_lib.c2
-rw-r--r--sound/isa/cs423x/cs4236.c2
-rw-r--r--sound/isa/dt019x.c2
-rw-r--r--sound/isa/es1688/es1688.c2
-rw-r--r--sound/isa/es1688/es1688_lib.c2
-rw-r--r--sound/isa/es18xx.c224
-rw-r--r--sound/isa/gus/gus_main.c2
-rw-r--r--sound/isa/gus/gus_mem_proc.c6
-rw-r--r--sound/isa/gus/gusextreme.c4
-rw-r--r--sound/isa/gus/gusmax.c2
-rw-r--r--sound/isa/gus/interwave.c10
-rw-r--r--sound/isa/opl3sa2.c28
-rw-r--r--sound/isa/opti9xx/miro.c2
-rw-r--r--sound/isa/opti9xx/opti92x-ad1848.c4
-rw-r--r--sound/isa/sb/sb16.c3
-rw-r--r--sound/isa/sb/sb_common.c2
-rw-r--r--sound/isa/sgalaxy.c2
-rw-r--r--sound/isa/sscape.c2
-rw-r--r--sound/isa/wavefront/wavefront.c4
-rw-r--r--sound/isa/wavefront/wavefront_fx.c36
-rw-r--r--sound/isa/wavefront/wavefront_midi.c2
-rw-r--r--sound/isa/wavefront/wavefront_synth.c14
-rw-r--r--sound/mips/au1x00.c6
-rw-r--r--sound/oss/COPYING339
-rw-r--r--sound/oss/Kconfig56
-rw-r--r--sound/oss/ad1816.c1
-rw-r--r--sound/oss/ad1848.c1
-rw-r--r--sound/oss/ad1889.c3
-rw-r--r--sound/oss/aedsp16.c1
-rw-r--r--sound/oss/ali5455.c2
-rw-r--r--sound/oss/au1000.c4
-rw-r--r--sound/oss/au1550_ac97.c15
-rw-r--r--sound/oss/btaudio.c2
-rw-r--r--sound/oss/cmpci.c2
-rw-r--r--sound/oss/cs4232.c3
-rw-r--r--sound/oss/cs4281/cs4281m.c2
-rw-r--r--sound/oss/cs46xx.c14
-rw-r--r--sound/oss/cs46xxpm-24.h48
-rw-r--r--sound/oss/dmabuf.c6
-rw-r--r--sound/oss/dmasound/dmasound.h1
-rw-r--r--sound/oss/dmasound/dmasound_awacs.c28
-rw-r--r--sound/oss/dmasound/dmasound_paula.c1
-rw-r--r--sound/oss/emu10k1/main.c2
-rw-r--r--sound/oss/emu10k1/midi.c2
-rw-r--r--sound/oss/es1370.c2
-rw-r--r--sound/oss/es1371.c2
-rw-r--r--sound/oss/esssolo1.c2
-rw-r--r--sound/oss/forte.c7
-rw-r--r--sound/oss/hal2.c2
-rw-r--r--sound/oss/i810_audio.c2
-rw-r--r--sound/oss/ite8172.c2
-rw-r--r--sound/oss/kahlua.c1
-rw-r--r--sound/oss/maestro.c2
-rw-r--r--sound/oss/maestro3.c2
-rw-r--r--sound/oss/msnd.c2
-rw-r--r--sound/oss/msnd_classic.h1
-rw-r--r--sound/oss/msnd_pinnacle.c1
-rw-r--r--sound/oss/msnd_pinnacle.h1
-rw-r--r--sound/oss/nec_vrc5477.c2
-rw-r--r--sound/oss/nm256_audio.c2
-rw-r--r--sound/oss/opl3sa2.c1
-rw-r--r--sound/oss/pas2_card.c1
-rw-r--r--sound/oss/pss.c1
-rw-r--r--sound/oss/rme96xx.c2
-rw-r--r--sound/oss/sb_card.c1
-rw-r--r--sound/oss/sb_common.c3
-rw-r--r--sound/oss/sb_ess.c28
-rw-r--r--sound/oss/sh_dac_audio.c63
-rw-r--r--sound/oss/sonicvibes.c2
-rw-r--r--sound/oss/sound_config.h1
-rw-r--r--sound/oss/soundcard.c17
-rw-r--r--sound/oss/trident.c25
-rw-r--r--sound/oss/via82cxxx_audio.c12
-rw-r--r--sound/oss/vidc.c1
-rw-r--r--sound/oss/waveartist.c1
-rw-r--r--sound/oss/wavfront.c2
-rw-r--r--sound/oss/wf_midi.c2
-rw-r--r--sound/oss/ymfpci.c2
-rw-r--r--sound/pci/Kconfig237
-rw-r--r--sound/pci/Makefile1
-rw-r--r--sound/pci/ac97/ac97_codec.c338
-rw-r--r--sound/pci/ac97/ac97_patch.c100
-rw-r--r--sound/pci/ac97/ac97_patch.h1
-rw-r--r--sound/pci/ac97/ac97_pcm.c18
-rw-r--r--sound/pci/ac97/ac97_proc.c18
-rw-r--r--sound/pci/ac97/ak4531_codec.c49
-rw-r--r--sound/pci/ad1889.c10
-rw-r--r--sound/pci/ali5451/ali5451.c4
-rw-r--r--sound/pci/als300.c4
-rw-r--r--sound/pci/als4000.c2
-rw-r--r--sound/pci/atiixp.c4
-rw-r--r--sound/pci/atiixp_modem.c4
-rw-r--r--sound/pci/au88x0/au8810.c2
-rw-r--r--sound/pci/au88x0/au8820.c2
-rw-r--r--sound/pci/au88x0/au8830.c2
-rw-r--r--sound/pci/au88x0/au88x0.c2
-rw-r--r--sound/pci/au88x0/au88x0.h3
-rw-r--r--sound/pci/au88x0/au88x0_a3d.c29
-rw-r--r--sound/pci/au88x0/au88x0_core.c4
-rw-r--r--sound/pci/azt3328.c4
-rw-r--r--sound/pci/bt87x.c9
-rw-r--r--sound/pci/ca0106/ca0106_main.c4
-rw-r--r--sound/pci/ca0106/ca0106_mixer.c10
-rw-r--r--sound/pci/cmipci.c4
-rw-r--r--sound/pci/cs4281.c9
-rw-r--r--sound/pci/cs46xx/cs46xx.c2
-rw-r--r--sound/pci/cs46xx/cs46xx_lib.c9
-rw-r--r--sound/pci/cs46xx/dsp_spos.c52
-rw-r--r--sound/pci/cs46xx/dsp_spos_scb_lib.c7
-rw-r--r--sound/pci/cs5535audio/Makefile2
-rw-r--r--sound/pci/cs5535audio/cs5535audio.c4
-rw-r--r--sound/pci/cs5535audio/cs5535audio_pcm.c2
-rw-r--r--sound/pci/echoaudio/Makefile30
-rw-r--r--sound/pci/echoaudio/darla20.c99
-rw-r--r--sound/pci/echoaudio/darla20_dsp.c125
-rw-r--r--sound/pci/echoaudio/darla24.c106
-rw-r--r--sound/pci/echoaudio/darla24_dsp.c156
-rw-r--r--sound/pci/echoaudio/echo3g.c118
-rw-r--r--sound/pci/echoaudio/echo3g_dsp.c131
-rw-r--r--sound/pci/echoaudio/echoaudio.c2196
-rw-r--r--sound/pci/echoaudio/echoaudio.h590
-rw-r--r--sound/pci/echoaudio/echoaudio_3g.c431
-rw-r--r--sound/pci/echoaudio/echoaudio_dsp.c1125
-rw-r--r--sound/pci/echoaudio/echoaudio_dsp.h694
-rw-r--r--sound/pci/echoaudio/echoaudio_gml.c198
-rw-r--r--sound/pci/echoaudio/gina20.c103
-rw-r--r--sound/pci/echoaudio/gina20_dsp.c215
-rw-r--r--sound/pci/echoaudio/gina24.c123
-rw-r--r--sound/pci/echoaudio/gina24_dsp.c346
-rw-r--r--sound/pci/echoaudio/indigo.c104
-rw-r--r--sound/pci/echoaudio/indigo_dsp.c170
-rw-r--r--sound/pci/echoaudio/indigodj.c104
-rw-r--r--sound/pci/echoaudio/indigodj_dsp.c170
-rw-r--r--sound/pci/echoaudio/indigoio.c105
-rw-r--r--sound/pci/echoaudio/indigoio_dsp.c141
-rw-r--r--sound/pci/echoaudio/layla20.c112
-rw-r--r--sound/pci/echoaudio/layla20_dsp.c290
-rw-r--r--sound/pci/echoaudio/layla24.c121
-rw-r--r--sound/pci/echoaudio/layla24_dsp.c394
-rw-r--r--sound/pci/echoaudio/mia.c117
-rw-r--r--sound/pci/echoaudio/mia_dsp.c229
-rw-r--r--sound/pci/echoaudio/midi.c327
-rw-r--r--sound/pci/echoaudio/mona.c129
-rw-r--r--sound/pci/echoaudio/mona_dsp.c428
-rw-r--r--sound/pci/emu10k1/emu10k1.c4
-rw-r--r--sound/pci/emu10k1/emu10k1_main.c24
-rw-r--r--sound/pci/emu10k1/emu10k1x.c46
-rw-r--r--sound/pci/emu10k1/emufx.c12
-rw-r--r--sound/pci/emu10k1/emumpu401.c35
-rw-r--r--sound/pci/emu10k1/irq.c6
-rw-r--r--sound/pci/emu10k1/p16v.c5
-rw-r--r--sound/pci/ens1370.c4
-rw-r--r--sound/pci/es1938.c110
-rw-r--r--sound/pci/es1968.c44
-rw-r--r--sound/pci/fm801.c67
-rw-r--r--sound/pci/hda/hda_codec.c80
-rw-r--r--sound/pci/hda/hda_codec.h2
-rw-r--r--sound/pci/hda/hda_generic.c199
-rw-r--r--sound/pci/hda/hda_intel.c136
-rw-r--r--sound/pci/hda/hda_local.h8
-rw-r--r--sound/pci/hda/hda_proc.c12
-rw-r--r--sound/pci/hda/patch_analog.c42
-rw-r--r--sound/pci/hda/patch_realtek.c1394
-rw-r--r--sound/pci/hda/patch_si3054.c1
-rw-r--r--sound/pci/hda/patch_sigmatel.c899
-rw-r--r--sound/pci/ice1712/aureon.c106
-rw-r--r--sound/pci/ice1712/ice1712.c18
-rw-r--r--sound/pci/ice1712/ice1724.c4
-rw-r--r--sound/pci/ice1712/phase.c39
-rw-r--r--sound/pci/ice1712/pontis.c9
-rw-r--r--sound/pci/ice1712/prodigy192.c14
-rw-r--r--sound/pci/ice1712/revo.c63
-rw-r--r--sound/pci/ice1712/revo.h2
-rw-r--r--sound/pci/intel8x0.c26
-rw-r--r--sound/pci/intel8x0m.c9
-rw-r--r--sound/pci/korg1212/korg1212.c4
-rw-r--r--sound/pci/maestro3.c12
-rw-r--r--sound/pci/mixart/mixart.c16
-rw-r--r--sound/pci/mixart/mixart_mixer.c14
-rw-r--r--sound/pci/nm256/nm256.c4
-rw-r--r--sound/pci/pcxhr/pcxhr.c4
-rw-r--r--sound/pci/pcxhr/pcxhr_mixer.c16
-rw-r--r--sound/pci/riptide/riptide.c14
-rw-r--r--sound/pci/rme32.c4
-rw-r--r--sound/pci/rme96.c4
-rw-r--r--sound/pci/rme9652/hdsp.c54
-rw-r--r--sound/pci/rme9652/hdspm.c2
-rw-r--r--sound/pci/rme9652/rme9652.c4
-rw-r--r--sound/pci/sonicvibes.c8
-rw-r--r--sound/pci/trident/trident.c2
-rw-r--r--sound/pci/trident/trident_main.c12
-rw-r--r--sound/pci/via82xx.c27
-rw-r--r--sound/pci/via82xx_modem.c4
-rw-r--r--sound/pci/vx222/vx222.c11
-rw-r--r--sound/pci/vx222/vx222_ops.c9
-rw-r--r--sound/pci/ymfpci/ymfpci.c2
-rw-r--r--sound/pci/ymfpci/ymfpci_main.c9
-rw-r--r--sound/pcmcia/pdaudiocf/pdaudiocf.c6
-rw-r--r--sound/pcmcia/vx/vxp_mixer.c6
-rw-r--r--sound/pcmcia/vx/vxpocket.c9
-rw-r--r--sound/ppc/awacs.c3
-rw-r--r--sound/ppc/beep.c32
-rw-r--r--sound/ppc/daca.c3
-rw-r--r--sound/ppc/keywest.c6
-rw-r--r--sound/ppc/pmac.c47
-rw-r--r--sound/ppc/powermac.c13
-rw-r--r--sound/ppc/toonie.c0
-rw-r--r--sound/ppc/tumbler.c31
-rw-r--r--sound/sound_core.c11
-rw-r--r--sound/sound_firmware.c6
-rw-r--r--sound/sparc/amd7930.c158
-rw-r--r--sound/sparc/cs4231.c20
-rw-r--r--sound/sparc/dbri.c829
-rw-r--r--sound/synth/emux/emux_proc.c6
-rw-r--r--sound/usb/usbaudio.c92
-rw-r--r--sound/usb/usbmidi.c6
-rw-r--r--sound/usb/usbmixer.c27
-rw-r--r--sound/usb/usbmixer_maps.c24
-rw-r--r--sound/usb/usbquirks.h5
283 files changed, 17617 insertions, 3024 deletions
diff --git a/sound/Makefile b/sound/Makefile
index a682ea30f0c9..5f6bef57e825 100644
--- a/sound/Makefile
+++ b/sound/Makefile
@@ -2,12 +2,14 @@
#
obj-$(CONFIG_SOUND) += soundcore.o
+obj-$(CONFIG_SOUND_PRIME) += sound_firmware.o
obj-$(CONFIG_SOUND_PRIME) += oss/
obj-$(CONFIG_DMASOUND) += oss/
-obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ synth/ usb/ sparc/ parisc/ pcmcia/ mips/ aoa/
+obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ synth/ usb/ sparc/ parisc/ pcmcia/ mips/
+obj-$(CONFIG_SND_AOA) += aoa/
ifeq ($(CONFIG_SND),y)
obj-y += last.o
endif
-soundcore-objs := sound_core.o sound_firmware.o
+soundcore-objs := sound_core.o
diff --git a/sound/aoa/Kconfig b/sound/aoa/Kconfig
index a85194fe0b06..5d5813cec4c8 100644
--- a/sound/aoa/Kconfig
+++ b/sound/aoa/Kconfig
@@ -1,9 +1,10 @@
menu "Apple Onboard Audio driver"
- depends on SND!=n && PPC
+ depends on SND!=n && PPC_PMAC
config SND_AOA
tristate "Apple Onboard Audio driver"
- depends on SOUND && SND_PCM
+ depends on SND
+ select SND_PCM
---help---
This option enables the new driver for the various
Apple Onboard Audio components.
diff --git a/sound/aoa/Makefile b/sound/aoa/Makefile
index d8de3e7df48d..a8c037f908f8 100644
--- a/sound/aoa/Makefile
+++ b/sound/aoa/Makefile
@@ -1,4 +1,4 @@
obj-$(CONFIG_SND_AOA) += core/
-obj-$(CONFIG_SND_AOA) += codecs/
-obj-$(CONFIG_SND_AOA) += fabrics/
obj-$(CONFIG_SND_AOA_SOUNDBUS) += soundbus/
+obj-$(CONFIG_SND_AOA) += fabrics/
+obj-$(CONFIG_SND_AOA) += codecs/
diff --git a/sound/aoa/codecs/Kconfig b/sound/aoa/codecs/Kconfig
index 90cf58f68630..d5fbd6016e93 100644
--- a/sound/aoa/codecs/Kconfig
+++ b/sound/aoa/codecs/Kconfig
@@ -1,6 +1,8 @@
config SND_AOA_ONYX
tristate "support Onyx chip"
depends on SND_AOA
+ select I2C
+ select I2C_POWERMAC
---help---
This option enables support for the Onyx (pcm3052)
codec chip found in the latest Apple machines
@@ -18,6 +20,8 @@ config SND_AOA_ONYX
config SND_AOA_TAS
tristate "support TAS chips"
depends on SND_AOA
+ select I2C
+ select I2C_POWERMAC
---help---
This option enables support for the tas chips
found in a lot of Apple Machines, especially
diff --git a/sound/aoa/codecs/snd-aoa-codec-tas-basstreble.h b/sound/aoa/codecs/snd-aoa-codec-tas-basstreble.h
new file mode 100644
index 000000000000..69b61136fd54
--- /dev/null
+++ b/sound/aoa/codecs/snd-aoa-codec-tas-basstreble.h
@@ -0,0 +1,134 @@
+/*
+ * This file is only included exactly once!
+ *
+ * The tables here are derived from the tas3004 datasheet,
+ * modulo typo corrections and some smoothing...
+ */
+
+#define TAS3004_TREBLE_MIN 0
+#define TAS3004_TREBLE_MAX 72
+#define TAS3004_BASS_MIN 0
+#define TAS3004_BASS_MAX 72
+#define TAS3004_TREBLE_ZERO 36
+#define TAS3004_BASS_ZERO 36
+
+static u8 tas3004_treble_table[] = {
+ 150, /* -18 dB */
+ 149,
+ 148,
+ 147,
+ 146,
+ 145,
+ 144,
+ 143,
+ 142,
+ 141,
+ 140,
+ 139,
+ 138,
+ 137,
+ 136,
+ 135,
+ 134,
+ 133,
+ 132,
+ 131,
+ 130,
+ 129,
+ 128,
+ 127,
+ 126,
+ 125,
+ 124,
+ 123,
+ 122,
+ 121,
+ 120,
+ 119,
+ 118,
+ 117,
+ 116,
+ 115,
+ 114, /* 0 dB */
+ 113,
+ 112,
+ 111,
+ 109,
+ 108,
+ 107,
+ 105,
+ 104,
+ 103,
+ 101,
+ 99,
+ 98,
+ 96,
+ 93,
+ 91,
+ 89,
+ 86,
+ 83,
+ 81,
+ 77,
+ 74,
+ 71,
+ 67,
+ 63,
+ 59,
+ 54,
+ 49,
+ 44,
+ 38,
+ 32,
+ 26,
+ 19,
+ 10,
+ 4,
+ 2,
+ 1, /* +18 dB */
+};
+
+static inline u8 tas3004_treble(int idx)
+{
+ return tas3004_treble_table[idx];
+}
+
+/* I only save the difference here to the treble table
+ * so that the binary is smaller...
+ * I have also ignored completely differences of
+ * +/- 1
+ */
+static s8 tas3004_bass_diff_to_treble[] = {
+ 2, /* 7 dB, offset 50 */
+ 2,
+ 2,
+ 2,
+ 2,
+ 1,
+ 2,
+ 2,
+ 2,
+ 3,
+ 4,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 14,
+ 13,
+ 8,
+ 1, /* 18 dB */
+};
+
+static inline u8 tas3004_bass(int idx)
+{
+ u8 result = tas3004_treble_table[idx];
+
+ if (idx >= 50)
+ result += tas3004_bass_diff_to_treble[idx-50];
+ return result;
+}
diff --git a/sound/aoa/codecs/snd-aoa-codec-tas.c b/sound/aoa/codecs/snd-aoa-codec-tas.c
index 2e39ff6ee349..2ef55a17917c 100644
--- a/sound/aoa/codecs/snd-aoa-codec-tas.c
+++ b/sound/aoa/codecs/snd-aoa-codec-tas.c
@@ -66,28 +66,41 @@
#include <asm/prom.h>
#include <linux/delay.h>
#include <linux/module.h>
+#include <linux/mutex.h>
+
MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("tas codec driver for snd-aoa");
#include "snd-aoa-codec-tas.h"
#include "snd-aoa-codec-tas-gain-table.h"
+#include "snd-aoa-codec-tas-basstreble.h"
#include "../aoa.h"
#include "../soundbus/soundbus.h"
-
#define PFX "snd-aoa-codec-tas: "
+
struct tas {
struct aoa_codec codec;
struct i2c_client i2c;
- u32 muted_l:1, muted_r:1,
- controls_created:1;
+ u32 mute_l:1, mute_r:1 ,
+ controls_created:1 ,
+ drc_enabled:1,
+ hw_enabled:1;
u8 cached_volume_l, cached_volume_r;
u8 mixer_l[3], mixer_r[3];
+ u8 bass, treble;
u8 acr;
+ int drc_range;
+ /* protects hardware access against concurrency from
+ * userspace when hitting controls and during
+ * codec init/suspend/resume */
+ struct mutex mtx;
};
+static int tas_reset_init(struct tas *tas);
+
static struct tas *codec_to_tas(struct aoa_codec *codec)
{
return container_of(codec, struct tas, codec);
@@ -101,6 +114,44 @@ static inline int tas_write_reg(struct tas *tas, u8 reg, u8 len, u8 *data)
return i2c_smbus_write_i2c_block_data(&tas->i2c, reg, len, data);
}
+static void tas3004_set_drc(struct tas *tas)
+{
+ unsigned char val[6];
+
+ if (tas->drc_enabled)
+ val[0] = 0x50; /* 3:1 above threshold */
+ else
+ val[0] = 0x51; /* disabled */
+ val[1] = 0x02; /* 1:1 below threshold */
+ if (tas->drc_range > 0xef)
+ val[2] = 0xef;
+ else if (tas->drc_range < 0)
+ val[2] = 0x00;
+ else
+ val[2] = tas->drc_range;
+ val[3] = 0xb0;
+ val[4] = 0x60;
+ val[5] = 0xa0;
+
+ tas_write_reg(tas, TAS_REG_DRC, 6, val);
+}
+
+static void tas_set_treble(struct tas *tas)
+{
+ u8 tmp;
+
+ tmp = tas3004_treble(tas->treble);
+ tas_write_reg(tas, TAS_REG_TREBLE, 1, &tmp);
+}
+
+static void tas_set_bass(struct tas *tas)
+{
+ u8 tmp;
+
+ tmp = tas3004_bass(tas->bass);
+ tas_write_reg(tas, TAS_REG_BASS, 1, &tmp);
+}
+
static void tas_set_volume(struct tas *tas)
{
u8 block[6];
@@ -113,8 +164,8 @@ static void tas_set_volume(struct tas *tas)
if (left > 177) left = 177;
if (right > 177) right = 177;
- if (tas->muted_l) left = 0;
- if (tas->muted_r) right = 0;
+ if (tas->mute_l) left = 0;
+ if (tas->mute_r) right = 0;
/* analysing the volume and mixer tables shows
* that they are similar enough when we shift
@@ -186,8 +237,10 @@ static int tas_snd_vol_get(struct snd_kcontrol *kcontrol,
{
struct tas *tas = snd_kcontrol_chip(kcontrol);
+ mutex_lock(&tas->mtx);
ucontrol->value.integer.value[0] = tas->cached_volume_l;
ucontrol->value.integer.value[1] = tas->cached_volume_r;
+ mutex_unlock(&tas->mtx);
return 0;
}
@@ -196,13 +249,18 @@ static int tas_snd_vol_put(struct snd_kcontrol *kcontrol,
{
struct tas *tas = snd_kcontrol_chip(kcontrol);
+ mutex_lock(&tas->mtx);
if (tas->cached_volume_l == ucontrol->value.integer.value[0]
- && tas->cached_volume_r == ucontrol->value.integer.value[1])
+ && tas->cached_volume_r == ucontrol->value.integer.value[1]) {
+ mutex_unlock(&tas->mtx);
return 0;
+ }
tas->cached_volume_l = ucontrol->value.integer.value[0];
tas->cached_volume_r = ucontrol->value.integer.value[1];
- tas_set_volume(tas);
+ if (tas->hw_enabled)
+ tas_set_volume(tas);
+ mutex_unlock(&tas->mtx);
return 1;
}
@@ -230,8 +288,10 @@ static int tas_snd_mute_get(struct snd_kcontrol *kcontrol,
{
struct tas *tas = snd_kcontrol_chip(kcontrol);
- ucontrol->value.integer.value[0] = !tas->muted_l;
- ucontrol->value.integer.value[1] = !tas->muted_r;
+ mutex_lock(&tas->mtx);
+ ucontrol->value.integer.value[0] = !tas->mute_l;
+ ucontrol->value.integer.value[1] = !tas->mute_r;
+ mutex_unlock(&tas->mtx);
return 0;
}
@@ -240,13 +300,18 @@ static int tas_snd_mute_put(struct snd_kcontrol *kcontrol,
{
struct tas *tas = snd_kcontrol_chip(kcontrol);
- if (tas->muted_l == !ucontrol->value.integer.value[0]
- && tas->muted_r == !ucontrol->value.integer.value[1])
+ mutex_lock(&tas->mtx);
+ if (tas->mute_l == !ucontrol->value.integer.value[0]
+ && tas->mute_r == !ucontrol->value.integer.value[1]) {
+ mutex_unlock(&tas->mtx);
return 0;
+ }
- tas->muted_l = !ucontrol->value.integer.value[0];
- tas->muted_r = !ucontrol->value.integer.value[1];
- tas_set_volume(tas);
+ tas->mute_l = !ucontrol->value.integer.value[0];
+ tas->mute_r = !ucontrol->value.integer.value[1];
+ if (tas->hw_enabled)
+ tas_set_volume(tas);
+ mutex_unlock(&tas->mtx);
return 1;
}
@@ -275,8 +340,10 @@ static int tas_snd_mixer_get(struct snd_kcontrol *kcontrol,
struct tas *tas = snd_kcontrol_chip(kcontrol);
int idx = kcontrol->private_value;
+ mutex_lock(&tas->mtx);
ucontrol->value.integer.value[0] = tas->mixer_l[idx];
ucontrol->value.integer.value[1] = tas->mixer_r[idx];
+ mutex_unlock(&tas->mtx);
return 0;
}
@@ -287,14 +354,19 @@ static int tas_snd_mixer_put(struct snd_kcontrol *kcontrol,
struct tas *tas = snd_kcontrol_chip(kcontrol);
int idx = kcontrol->private_value;
+ mutex_lock(&tas->mtx);
if (tas->mixer_l[idx] == ucontrol->value.integer.value[0]
- && tas->mixer_r[idx] == ucontrol->value.integer.value[1])
+ && tas->mixer_r[idx] == ucontrol->value.integer.value[1]) {
+ mutex_unlock(&tas->mtx);
return 0;
+ }
tas->mixer_l[idx] = ucontrol->value.integer.value[0];
tas->mixer_r[idx] = ucontrol->value.integer.value[1];
- tas_set_mixer(tas);
+ if (tas->hw_enabled)
+ tas_set_mixer(tas);
+ mutex_unlock(&tas->mtx);
return 1;
}
@@ -309,9 +381,105 @@ static struct snd_kcontrol_new n##_control = { \
.private_value = idx, \
}
-MIXER_CONTROL(pcm1, "PCM1", 0);
+MIXER_CONTROL(pcm1, "PCM", 0);
MIXER_CONTROL(monitor, "Monitor", 2);
+static int tas_snd_drc_range_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = TAS3004_DRC_MAX;
+ return 0;
+}
+
+static int tas_snd_drc_range_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tas *tas = snd_kcontrol_chip(kcontrol);
+
+ mutex_lock(&tas->mtx);
+ ucontrol->value.integer.value[0] = tas->drc_range;
+ mutex_unlock(&tas->mtx);
+ return 0;
+}
+
+static int tas_snd_drc_range_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tas *tas = snd_kcontrol_chip(kcontrol);
+
+ mutex_lock(&tas->mtx);
+ if (tas->drc_range == ucontrol->value.integer.value[0]) {
+ mutex_unlock(&tas->mtx);
+ return 0;
+ }
+
+ tas->drc_range = ucontrol->value.integer.value[0];
+ if (tas->hw_enabled)
+ tas3004_set_drc(tas);
+ mutex_unlock(&tas->mtx);
+ return 1;
+}
+
+static struct snd_kcontrol_new drc_range_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "DRC Range",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = tas_snd_drc_range_info,
+ .get = tas_snd_drc_range_get,
+ .put = tas_snd_drc_range_put,
+};
+
+static int tas_snd_drc_switch_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int tas_snd_drc_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tas *tas = snd_kcontrol_chip(kcontrol);
+
+ mutex_lock(&tas->mtx);
+ ucontrol->value.integer.value[0] = tas->drc_enabled;
+ mutex_unlock(&tas->mtx);
+ return 0;
+}
+
+static int tas_snd_drc_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tas *tas = snd_kcontrol_chip(kcontrol);
+
+ mutex_lock(&tas->mtx);
+ if (tas->drc_enabled == ucontrol->value.integer.value[0]) {
+ mutex_unlock(&tas->mtx);
+ return 0;
+ }
+
+ tas->drc_enabled = ucontrol->value.integer.value[0];
+ if (tas->hw_enabled)
+ tas3004_set_drc(tas);
+ mutex_unlock(&tas->mtx);
+ return 1;
+}
+
+static struct snd_kcontrol_new drc_switch_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "DRC Range Switch",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = tas_snd_drc_switch_info,
+ .get = tas_snd_drc_switch_get,
+ .put = tas_snd_drc_switch_put,
+};
+
static int tas_snd_capture_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -331,7 +499,9 @@ static int tas_snd_capture_source_get(struct snd_kcontrol *kcontrol,
{
struct tas *tas = snd_kcontrol_chip(kcontrol);
+ mutex_lock(&tas->mtx);
ucontrol->value.enumerated.item[0] = !!(tas->acr & TAS_ACR_INPUT_B);
+ mutex_unlock(&tas->mtx);
return 0;
}
@@ -339,14 +509,21 @@ static int tas_snd_capture_source_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct tas *tas = snd_kcontrol_chip(kcontrol);
- int oldacr = tas->acr;
+ int oldacr;
+
+ mutex_lock(&tas->mtx);
+ oldacr = tas->acr;
tas->acr &= ~TAS_ACR_INPUT_B;
if (ucontrol->value.enumerated.item[0])
tas->acr |= TAS_ACR_INPUT_B;
- if (oldacr == tas->acr)
+ if (oldacr == tas->acr) {
+ mutex_unlock(&tas->mtx);
return 0;
- tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr);
+ }
+ if (tas->hw_enabled)
+ tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr);
+ mutex_unlock(&tas->mtx);
return 1;
}
@@ -370,6 +547,101 @@ static struct snd_kcontrol_new capture_source_control = {
.put = tas_snd_capture_source_put,
};
+static int tas_snd_treble_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = TAS3004_TREBLE_MIN;
+ uinfo->value.integer.max = TAS3004_TREBLE_MAX;
+ return 0;
+}
+
+static int tas_snd_treble_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tas *tas = snd_kcontrol_chip(kcontrol);
+
+ mutex_lock(&tas->mtx);
+ ucontrol->value.integer.value[0] = tas->treble;
+ mutex_unlock(&tas->mtx);
+ return 0;
+}
+
+static int tas_snd_treble_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tas *tas = snd_kcontrol_chip(kcontrol);
+
+ mutex_lock(&tas->mtx);
+ if (tas->treble == ucontrol->value.integer.value[0]) {
+ mutex_unlock(&tas->mtx);
+ return 0;
+ }
+
+ tas->treble = ucontrol->value.integer.value[0];
+ if (tas->hw_enabled)
+ tas_set_treble(tas);
+ mutex_unlock(&tas->mtx);
+ return 1;
+}
+
+static struct snd_kcontrol_new treble_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Treble",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = tas_snd_treble_info,
+ .get = tas_snd_treble_get,
+ .put = tas_snd_treble_put,
+};
+
+static int tas_snd_bass_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = TAS3004_BASS_MIN;
+ uinfo->value.integer.max = TAS3004_BASS_MAX;
+ return 0;
+}
+
+static int tas_snd_bass_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tas *tas = snd_kcontrol_chip(kcontrol);
+
+ mutex_lock(&tas->mtx);
+ ucontrol->value.integer.value[0] = tas->bass;
+ mutex_unlock(&tas->mtx);
+ return 0;
+}
+
+static int tas_snd_bass_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tas *tas = snd_kcontrol_chip(kcontrol);
+
+ mutex_lock(&tas->mtx);
+ if (tas->bass == ucontrol->value.integer.value[0]) {
+ mutex_unlock(&tas->mtx);
+ return 0;
+ }
+
+ tas->bass = ucontrol->value.integer.value[0];
+ if (tas->hw_enabled)
+ tas_set_bass(tas);
+ mutex_unlock(&tas->mtx);
+ return 1;
+}
+
+static struct snd_kcontrol_new bass_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Bass",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = tas_snd_bass_info,
+ .get = tas_snd_bass_get,
+ .put = tas_snd_bass_put,
+};
static struct transfer_info tas_transfers[] = {
{
@@ -399,27 +671,72 @@ static int tas_usable(struct codec_info_item *cii,
static int tas_reset_init(struct tas *tas)
{
u8 tmp;
+
+ tas->codec.gpio->methods->all_amps_off(tas->codec.gpio);
+ msleep(5);
tas->codec.gpio->methods->set_hw_reset(tas->codec.gpio, 0);
- msleep(1);
+ msleep(5);
tas->codec.gpio->methods->set_hw_reset(tas->codec.gpio, 1);
- msleep(1);
+ msleep(20);
tas->codec.gpio->methods->set_hw_reset(tas->codec.gpio, 0);
- msleep(1);
-
- tas->acr &= ~TAS_ACR_ANALOG_PDOWN;
- tas->acr |= TAS_ACR_B_MONAUREAL | TAS_ACR_B_MON_SEL_RIGHT;
- if (tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr))
- return -ENODEV;
+ msleep(10);
+ tas->codec.gpio->methods->all_amps_restore(tas->codec.gpio);
tmp = TAS_MCS_SCLK64 | TAS_MCS_SPORT_MODE_I2S | TAS_MCS_SPORT_WL_24BIT;
if (tas_write_reg(tas, TAS_REG_MCS, 1, &tmp))
- return -ENODEV;
+ goto outerr;
+
+ tas->acr |= TAS_ACR_ANALOG_PDOWN | TAS_ACR_B_MONAUREAL |
+ TAS_ACR_B_MON_SEL_RIGHT;
+ if (tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr))
+ goto outerr;
tmp = 0;
if (tas_write_reg(tas, TAS_REG_MCS2, 1, &tmp))
- return -ENODEV;
+ goto outerr;
+
+ tas3004_set_drc(tas);
+
+ /* Set treble & bass to 0dB */
+ tas->treble = TAS3004_TREBLE_ZERO;
+ tas->bass = TAS3004_BASS_ZERO;
+ tas_set_treble(tas);
+ tas_set_bass(tas);
+
+ tas->acr &= ~TAS_ACR_ANALOG_PDOWN;
+ if (tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr))
+ goto outerr;
return 0;
+ outerr:
+ return -ENODEV;
+}
+
+static int tas_switch_clock(struct codec_info_item *cii, enum clock_switch clock)
+{
+ struct tas *tas = cii->codec_data;
+
+ switch(clock) {
+ case CLOCK_SWITCH_PREPARE_SLAVE:
+ /* Clocks are going away, mute mute mute */
+ tas->codec.gpio->methods->all_amps_off(tas->codec.gpio);
+ tas->hw_enabled = 0;
+ break;
+ case CLOCK_SWITCH_SLAVE:
+ /* Clocks are back, re-init the codec */
+ mutex_lock(&tas->mtx);
+ tas_reset_init(tas);
+ tas_set_volume(tas);
+ tas_set_mixer(tas);
+ tas->hw_enabled = 1;
+ tas->codec.gpio->methods->all_amps_restore(tas->codec.gpio);
+ mutex_unlock(&tas->mtx);
+ break;
+ default:
+ /* doesn't happen as of now */
+ return -EINVAL;
+ }
+ return 0;
}
/* we are controlled via i2c and assume that is always up
@@ -427,17 +744,23 @@ static int tas_reset_init(struct tas *tas)
* our i2c device is suspended, and then take note of that! */
static int tas_suspend(struct tas *tas)
{
+ mutex_lock(&tas->mtx);
+ tas->hw_enabled = 0;
tas->acr |= TAS_ACR_ANALOG_PDOWN;
tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr);
+ mutex_unlock(&tas->mtx);
return 0;
}
static int tas_resume(struct tas *tas)
{
/* reset codec */
+ mutex_lock(&tas->mtx);
tas_reset_init(tas);
tas_set_volume(tas);
tas_set_mixer(tas);
+ tas->hw_enabled = 1;
+ mutex_unlock(&tas->mtx);
return 0;
}
@@ -463,6 +786,7 @@ static struct codec_info tas_codec_info = {
.bus_factor = 64,
.owner = THIS_MODULE,
.usable = tas_usable,
+ .switch_clock = tas_switch_clock,
#ifdef CONFIG_PM
.suspend = _tas_suspend,
.resume = _tas_resume,
@@ -479,10 +803,14 @@ static int tas_init_codec(struct aoa_codec *codec)
return -EINVAL;
}
+ mutex_lock(&tas->mtx);
if (tas_reset_init(tas)) {
printk(KERN_ERR PFX "tas failed to initialise\n");
+ mutex_unlock(&tas->mtx);
return -ENXIO;
}
+ tas->hw_enabled = 1;
+ mutex_unlock(&tas->mtx);
if (tas->codec.soundbus_dev->attach_codec(tas->codec.soundbus_dev,
aoa_get_card(),
@@ -515,6 +843,22 @@ static int tas_init_codec(struct aoa_codec *codec)
if (err)
goto error;
+ err = aoa_snd_ctl_add(snd_ctl_new1(&drc_range_control, tas));
+ if (err)
+ goto error;
+
+ err = aoa_snd_ctl_add(snd_ctl_new1(&drc_switch_control, tas));
+ if (err)
+ goto error;
+
+ err = aoa_snd_ctl_add(snd_ctl_new1(&treble_control, tas));
+ if (err)
+ goto error;
+
+ err = aoa_snd_ctl_add(snd_ctl_new1(&bass_control, tas));
+ if (err)
+ goto error;
+
return 0;
error:
tas->codec.soundbus_dev->detach_codec(tas->codec.soundbus_dev, tas);
@@ -545,9 +889,12 @@ static int tas_create(struct i2c_adapter *adapter,
if (!tas)
return -ENOMEM;
+ mutex_init(&tas->mtx);
tas->i2c.driver = &tas_driver;
tas->i2c.adapter = adapter;
tas->i2c.addr = addr;
+ /* seems that half is a saner default */
+ tas->drc_range = TAS3004_DRC_MAX / 2;
strlcpy(tas->i2c.name, "tas audio codec", I2C_NAME_SIZE-1);
if (i2c_attach_client(&tas->i2c)) {
@@ -564,11 +911,14 @@ static int tas_create(struct i2c_adapter *adapter,
if (aoa_codec_register(&tas->codec)) {
goto detach;
}
- printk(KERN_DEBUG "snd-aoa-codec-tas: created and attached tas instance\n");
+ printk(KERN_DEBUG
+ "snd-aoa-codec-tas: tas found, addr 0x%02x on %s\n",
+ addr, node->full_name);
return 0;
detach:
i2c_detach_client(&tas->i2c);
fail:
+ mutex_destroy(&tas->mtx);
kfree(tas);
return -EINVAL;
}
@@ -627,6 +977,7 @@ static int tas_i2c_detach(struct i2c_client *client)
/* power down codec chip */
tas_write_reg(tas, TAS_REG_ACR, 1, &tmp);
+ mutex_destroy(&tas->mtx);
kfree(tas);
return 0;
}
diff --git a/sound/aoa/codecs/snd-aoa-codec-tas.h b/sound/aoa/codecs/snd-aoa-codec-tas.h
index daf81f45d83a..ae177e3466e6 100644
--- a/sound/aoa/codecs/snd-aoa-codec-tas.h
+++ b/sound/aoa/codecs/snd-aoa-codec-tas.h
@@ -44,4 +44,12 @@
#define TAS_REG_LEFT_BIQUAD6 0x10
#define TAS_REG_RIGHT_BIQUAD6 0x19
+#define TAS_REG_LEFT_LOUDNESS 0x21
+#define TAS_REG_RIGHT_LOUDNESS 0x22
+#define TAS_REG_LEFT_LOUDNESS_GAIN 0x23
+#define TAS_REG_RIGHT_LOUDNESS_GAIN 0x24
+
+#define TAS3001_DRC_MAX 0x5f
+#define TAS3004_DRC_MAX 0xef
+
#endif /* __SND_AOA_CODECTASH */
diff --git a/sound/aoa/codecs/snd-aoa-codec-toonie.c b/sound/aoa/codecs/snd-aoa-codec-toonie.c
index bcc555647e79..3c7d1d8a9a6f 100644
--- a/sound/aoa/codecs/snd-aoa-codec-toonie.c
+++ b/sound/aoa/codecs/snd-aoa-codec-toonie.c
@@ -51,6 +51,13 @@ static struct transfer_info toonie_transfers[] = {
{}
};
+static int toonie_usable(struct codec_info_item *cii,
+ struct transfer_info *ti,
+ struct transfer_info *out)
+{
+ return 1;
+}
+
#ifdef CONFIG_PM
static int toonie_suspend(struct codec_info_item *cii, pm_message_t state)
{
@@ -69,6 +76,7 @@ static struct codec_info toonie_codec_info = {
.sysclock_factor = 256,
.bus_factor = 64,
.owner = THIS_MODULE,
+ .usable = toonie_usable,
#ifdef CONFIG_PM
.suspend = toonie_suspend,
.resume = toonie_resume,
@@ -79,19 +87,20 @@ static int toonie_init_codec(struct aoa_codec *codec)
{
struct toonie *toonie = codec_to_toonie(codec);
+ /* nothing connected? what a joke! */
+ if (toonie->codec.connected != 1)
+ return -ENOTCONN;
+
if (aoa_snd_device_new(SNDRV_DEV_LOWLEVEL, toonie, &ops)) {
printk(KERN_ERR PFX "failed to create toonie snd device!\n");
return -ENODEV;
}
- /* nothing connected? what a joke! */
- if (toonie->codec.connected != 1)
- return -ENOTCONN;
-
if (toonie->codec.soundbus_dev->attach_codec(toonie->codec.soundbus_dev,
aoa_get_card(),
&toonie_codec_info, toonie)) {
printk(KERN_ERR PFX "error creating toonie pcm\n");
+ snd_device_free(aoa_get_card(), toonie);
return -ENODEV;
}
diff --git a/sound/aoa/core/snd-aoa-gpio-feature.c b/sound/aoa/core/snd-aoa-gpio-feature.c
index 2c6eb7784cc9..7c26089527f6 100644
--- a/sound/aoa/core/snd-aoa-gpio-feature.c
+++ b/sound/aoa/core/snd-aoa-gpio-feature.c
@@ -56,7 +56,7 @@ static struct device_node *get_gpio(char *name,
{
struct device_node *np, *gpio;
u32 *reg;
- char *audio_gpio;
+ const char *audio_gpio;
*gpioptr = -1;
@@ -112,12 +112,10 @@ static struct device_node *get_gpio(char *name,
static void get_irq(struct device_node * np, int *irqptr)
{
- *irqptr = -1;
- if (!np)
- return;
- if (np->n_intrs != 1)
- return;
- *irqptr = np->intrs[0].line;
+ if (np)
+ *irqptr = irq_of_parse_and_map(np, 0);
+ else
+ *irqptr = NO_IRQ;
}
/* 0x4 is outenable, 0x1 is out, thus 4 or 5 */
@@ -207,6 +205,17 @@ static void ftr_handle_notify(void *data)
mutex_unlock(&notif->mutex);
}
+static void gpio_enable_dual_edge(int gpio)
+{
+ int v;
+
+ if (gpio == -1)
+ return;
+ v = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, gpio, 0);
+ v |= 0x80; /* enable dual edge */
+ pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, gpio, v);
+}
+
static void ftr_gpio_init(struct gpio_runtime *rt)
{
get_gpio("headphone-mute", NULL,
@@ -234,6 +243,10 @@ static void ftr_gpio_init(struct gpio_runtime *rt)
&linein_detect_gpio,
&linein_detect_gpio_activestate);
+ gpio_enable_dual_edge(headphone_detect_gpio);
+ gpio_enable_dual_edge(lineout_detect_gpio);
+ gpio_enable_dual_edge(linein_detect_gpio);
+
get_irq(headphone_detect_node, &headphone_detect_irq);
get_irq(lineout_detect_node, &lineout_detect_irq);
get_irq(linein_detect_node, &linein_detect_irq);
@@ -312,7 +325,7 @@ static int ftr_set_notify(struct gpio_runtime *rt,
return -EINVAL;
}
- if (irq == -1)
+ if (irq == NO_IRQ)
return -ENODEV;
mutex_lock(&notif->mutex);
diff --git a/sound/aoa/core/snd-aoa-gpio-pmf.c b/sound/aoa/core/snd-aoa-gpio-pmf.c
index 0e9b9bb2a6de..2836c3218391 100644
--- a/sound/aoa/core/snd-aoa-gpio-pmf.c
+++ b/sound/aoa/core/snd-aoa-gpio-pmf.c
@@ -14,9 +14,13 @@
static void pmf_gpio_set_##name(struct gpio_runtime *rt, int on)\
{ \
struct pmf_args args = { .count = 1, .u[0].v = !on }; \
- \
+ int rc; \
+ \
if (unlikely(!rt)) return; \
- pmf_call_function(rt->node, #name "-mute", &args); \
+ rc = pmf_call_function(rt->node, #name "-mute", &args); \
+ if (rc && rc != -ENODEV) \
+ printk(KERN_WARNING "pmf_gpio_set_" #name \
+ " failed, rc: %d\n", rc); \
rt->implementation_private &= ~(1<<bit); \
rt->implementation_private |= (!!on << bit); \
} \
@@ -33,9 +37,13 @@ PMF_GPIO(lineout, 2);
static void pmf_gpio_set_hw_reset(struct gpio_runtime *rt, int on)
{
struct pmf_args args = { .count = 1, .u[0].v = !!on };
+ int rc;
if (unlikely(!rt)) return;
- pmf_call_function(rt->node, "hw-reset", &args);
+ rc = pmf_call_function(rt->node, "hw-reset", &args);
+ if (rc)
+ printk(KERN_WARNING "pmf_gpio_set_hw_reset"
+ " failed, rc: %d\n", rc);
}
static void pmf_gpio_all_amps_off(struct gpio_runtime *rt)
diff --git a/sound/aoa/fabrics/snd-aoa-fabric-layout.c b/sound/aoa/fabrics/snd-aoa-fabric-layout.c
index 04a7238e9494..172eb95476c0 100644
--- a/sound/aoa/fabrics/snd-aoa-fabric-layout.c
+++ b/sound/aoa/fabrics/snd-aoa-fabric-layout.c
@@ -77,23 +77,39 @@ struct layout {
int pcmid;
};
+MODULE_ALIAS("sound-layout-36");
MODULE_ALIAS("sound-layout-41");
MODULE_ALIAS("sound-layout-45");
+MODULE_ALIAS("sound-layout-47");
+MODULE_ALIAS("sound-layout-48");
+MODULE_ALIAS("sound-layout-49");
+MODULE_ALIAS("sound-layout-50");
MODULE_ALIAS("sound-layout-51");
+MODULE_ALIAS("sound-layout-56");
+MODULE_ALIAS("sound-layout-57");
MODULE_ALIAS("sound-layout-58");
MODULE_ALIAS("sound-layout-60");
MODULE_ALIAS("sound-layout-61");
+MODULE_ALIAS("sound-layout-62");
MODULE_ALIAS("sound-layout-64");
MODULE_ALIAS("sound-layout-65");
+MODULE_ALIAS("sound-layout-66");
+MODULE_ALIAS("sound-layout-67");
MODULE_ALIAS("sound-layout-68");
MODULE_ALIAS("sound-layout-69");
MODULE_ALIAS("sound-layout-70");
MODULE_ALIAS("sound-layout-72");
+MODULE_ALIAS("sound-layout-76");
MODULE_ALIAS("sound-layout-80");
MODULE_ALIAS("sound-layout-82");
MODULE_ALIAS("sound-layout-84");
MODULE_ALIAS("sound-layout-86");
+MODULE_ALIAS("sound-layout-90");
MODULE_ALIAS("sound-layout-92");
+MODULE_ALIAS("sound-layout-94");
+MODULE_ALIAS("sound-layout-96");
+MODULE_ALIAS("sound-layout-98");
+MODULE_ALIAS("sound-layout-100");
/* onyx with all but microphone connected */
static struct codec_connection onyx_connections_nomic[] = {
@@ -381,6 +397,13 @@ static struct layout layouts[] = {
.connections = toonie_connections,
},
},
+ {
+ .layout_id = 96,
+ .codecs[0] = {
+ .name = "onyx",
+ .connections = onyx_connections_noheadphones,
+ },
+ },
/* unknown, untested, but this comes from Apple */
{ .layout_id = 41,
.codecs[0] = {
@@ -479,12 +502,6 @@ static struct layout layouts[] = {
.connections = onyx_connections_noheadphones,
},
},
- { .layout_id = 96,
- .codecs[0] = {
- .name = "onyx",
- .connections = onyx_connections_noheadphones,
- },
- },
{ .layout_id = 98,
.codecs[0] = {
.name = "toonie",
@@ -948,11 +965,12 @@ static int aoa_fabric_layout_probe(struct soundbus_dev *sdev)
layout_id = (unsigned int *) get_property(sound, "layout-id", NULL);
if (!layout_id)
goto outnodev;
- printk(KERN_INFO "snd-aoa-fabric-layout: found bus with layout %d ", *layout_id);
+ printk(KERN_INFO "snd-aoa-fabric-layout: found bus with layout %d\n",
+ *layout_id);
layout = find_layout_by_id(*layout_id);
if (!layout) {
- printk("(no idea how to handle)\n");
+ printk(KERN_ERR "snd-aoa-fabric-layout: unknown layout\n");
goto outnodev;
}
@@ -970,15 +988,17 @@ static int aoa_fabric_layout_probe(struct soundbus_dev *sdev)
case 51: /* PowerBook5,4 */
case 58: /* Mac Mini */
ldev->gpio.methods = ftr_gpio_methods;
+ printk(KERN_DEBUG
+ "snd-aoa-fabric-layout: Using direct GPIOs\n");
break;
default:
ldev->gpio.methods = pmf_gpio_methods;
+ printk(KERN_DEBUG
+ "snd-aoa-fabric-layout: Using PMF GPIOs\n");
}
ldev->selfptr_headphone.ptr = ldev;
ldev->selfptr_lineout.ptr = ldev;
sdev->ofdev.dev.driver_data = ldev;
-
- printk("(using)\n");
list_add(&ldev->list, &layouts_list);
layouts_list_items++;
diff --git a/sound/aoa/soundbus/Kconfig b/sound/aoa/soundbus/Kconfig
index d532d27a9f54..7368b7ddfe0d 100644
--- a/sound/aoa/soundbus/Kconfig
+++ b/sound/aoa/soundbus/Kconfig
@@ -1,6 +1,7 @@
config SND_AOA_SOUNDBUS
tristate "Apple Soundbus support"
- depends on SOUND && SND_PCM && EXPERIMENTAL
+ depends on SOUND
+ select SND_PCM
---help---
This option enables the generic driver for the soundbus
support on Apple machines.
diff --git a/sound/aoa/soundbus/core.c b/sound/aoa/soundbus/core.c
index abe84a76c835..47b3e3768df0 100644
--- a/sound/aoa/soundbus/core.c
+++ b/sound/aoa/soundbus/core.c
@@ -194,16 +194,6 @@ static struct bus_type soundbus_bus_type = {
.dev_attrs = soundbus_dev_attrs,
};
-static int __init soundbus_init(void)
-{
- return bus_register(&soundbus_bus_type);
-}
-
-static void __exit soundbus_exit(void)
-{
- bus_unregister(&soundbus_bus_type);
-}
-
int soundbus_add_one(struct soundbus_dev *dev)
{
static int devcount;
@@ -246,5 +236,15 @@ void soundbus_unregister_driver(struct soundbus_driver *drv)
}
EXPORT_SYMBOL_GPL(soundbus_unregister_driver);
-module_init(soundbus_init);
+static int __init soundbus_init(void)
+{
+ return bus_register(&soundbus_bus_type);
+}
+
+static void __exit soundbus_exit(void)
+{
+ bus_unregister(&soundbus_bus_type);
+}
+
+subsys_initcall(soundbus_init);
module_exit(soundbus_exit);
diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-control.c b/sound/aoa/soundbus/i2sbus/i2sbus-control.c
index f50407952d3c..87beb4ad4d63 100644
--- a/sound/aoa/soundbus/i2sbus/i2sbus-control.c
+++ b/sound/aoa/soundbus/i2sbus/i2sbus-control.c
@@ -6,12 +6,16 @@
* GPL v2, can be found in COPYING.
*/
-#include <asm/io.h>
+#include <linux/kernel.h>
#include <linux/delay.h>
+
+#include <asm/io.h>
#include <asm/prom.h>
#include <asm/macio.h>
#include <asm/pmac_feature.h>
#include <asm/pmac_pfunc.h>
+#include <asm/keylargo.h>
+
#include "i2sbus.h"
int i2sbus_control_init(struct macio_dev* dev, struct i2sbus_control **c)
@@ -22,26 +26,12 @@ int i2sbus_control_init(struct macio_dev* dev, struct i2sbus_control **c)
INIT_LIST_HEAD(&(*c)->list);
- if (of_address_to_resource(dev->ofdev.node, 0, &(*c)->rsrc))
- goto err;
- /* we really should be using feature calls instead of mapping
- * these registers. It's safe for now since no one else is
- * touching them... */
- (*c)->controlregs = ioremap((*c)->rsrc.start,
- sizeof(struct i2s_control_regs));
- if (!(*c)->controlregs)
- goto err;
-
+ (*c)->macio = dev->bus->chip;
return 0;
- err:
- kfree(*c);
- *c = NULL;
- return -ENODEV;
}
void i2sbus_control_destroy(struct i2sbus_control *c)
{
- iounmap(c->controlregs);
kfree(c);
}
@@ -93,19 +83,22 @@ int i2sbus_control_enable(struct i2sbus_control *c,
struct i2sbus_dev *i2sdev)
{
struct pmf_args args = { .count = 0 };
- int cc;
+ struct macio_chip *macio = c->macio;
if (i2sdev->enable)
return pmf_call_one(i2sdev->enable, &args);
+ if (macio == NULL || macio->base == NULL)
+ return -ENODEV;
+
switch (i2sdev->bus_number) {
case 0:
- cc = in_le32(&c->controlregs->cell_control);
- out_le32(&c->controlregs->cell_control, cc | CTRL_CLOCK_INTF_0_ENABLE);
+ /* these need to be locked or done through
+ * newly created feature calls! */
+ MACIO_BIS(KEYLARGO_FCR1, KL1_I2S0_ENABLE);
break;
case 1:
- cc = in_le32(&c->controlregs->cell_control);
- out_le32(&c->controlregs->cell_control, cc | CTRL_CLOCK_INTF_1_ENABLE);
+ MACIO_BIS(KEYLARGO_FCR1, KL1_I2S1_ENABLE);
break;
default:
return -ENODEV;
@@ -118,7 +111,7 @@ int i2sbus_control_cell(struct i2sbus_control *c,
int enable)
{
struct pmf_args args = { .count = 0 };
- int cc;
+ struct macio_chip *macio = c->macio;
switch (enable) {
case 0:
@@ -133,18 +126,22 @@ int i2sbus_control_cell(struct i2sbus_control *c,
printk(KERN_ERR "i2sbus: INVALID CELL ENABLE VALUE\n");
return -ENODEV;
}
+
+ if (macio == NULL || macio->base == NULL)
+ return -ENODEV;
+
switch (i2sdev->bus_number) {
case 0:
- cc = in_le32(&c->controlregs->cell_control);
- cc &= ~CTRL_CLOCK_CELL_0_ENABLE;
- cc |= enable * CTRL_CLOCK_CELL_0_ENABLE;
- out_le32(&c->controlregs->cell_control, cc);
+ if (enable)
+ MACIO_BIS(KEYLARGO_FCR1, KL1_I2S0_CELL_ENABLE);
+ else
+ MACIO_BIC(KEYLARGO_FCR1, KL1_I2S0_CELL_ENABLE);
break;
case 1:
- cc = in_le32(&c->controlregs->cell_control);
- cc &= ~CTRL_CLOCK_CELL_1_ENABLE;
- cc |= enable * CTRL_CLOCK_CELL_1_ENABLE;
- out_le32(&c->controlregs->cell_control, cc);
+ if (enable)
+ MACIO_BIS(KEYLARGO_FCR1, KL1_I2S1_CELL_ENABLE);
+ else
+ MACIO_BIC(KEYLARGO_FCR1, KL1_I2S1_CELL_ENABLE);
break;
default:
return -ENODEV;
@@ -157,7 +154,7 @@ int i2sbus_control_clock(struct i2sbus_control *c,
int enable)
{
struct pmf_args args = { .count = 0 };
- int cc;
+ struct macio_chip *macio = c->macio;
switch (enable) {
case 0:
@@ -172,18 +169,22 @@ int i2sbus_control_clock(struct i2sbus_control *c,
printk(KERN_ERR "i2sbus: INVALID CLOCK ENABLE VALUE\n");
return -ENODEV;
}
+
+ if (macio == NULL || macio->base == NULL)
+ return -ENODEV;
+
switch (i2sdev->bus_number) {
case 0:
- cc = in_le32(&c->controlregs->cell_control);
- cc &= ~CTRL_CLOCK_CLOCK_0_ENABLE;
- cc |= enable * CTRL_CLOCK_CLOCK_0_ENABLE;
- out_le32(&c->controlregs->cell_control, cc);
+ if (enable)
+ MACIO_BIS(KEYLARGO_FCR1, KL1_I2S0_CLK_ENABLE_BIT);
+ else
+ MACIO_BIC(KEYLARGO_FCR1, KL1_I2S0_CLK_ENABLE_BIT);
break;
case 1:
- cc = in_le32(&c->controlregs->cell_control);
- cc &= ~CTRL_CLOCK_CLOCK_1_ENABLE;
- cc |= enable * CTRL_CLOCK_CLOCK_1_ENABLE;
- out_le32(&c->controlregs->cell_control, cc);
+ if (enable)
+ MACIO_BIS(KEYLARGO_FCR1, KL1_I2S1_CLK_ENABLE_BIT);
+ else
+ MACIO_BIC(KEYLARGO_FCR1, KL1_I2S1_CLK_ENABLE_BIT);
break;
default:
return -ENODEV;
diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-control.h b/sound/aoa/soundbus/i2sbus/i2sbus-control.h
deleted file mode 100644
index bb05550f730b..000000000000
--- a/sound/aoa/soundbus/i2sbus/i2sbus-control.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * i2sbus driver -- bus register definitions
- *
- * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
- */
-#ifndef __I2SBUS_CONTROLREGS_H
-#define __I2SBUS_CONTROLREGS_H
-
-/* i2s control registers, at least what we know about them */
-
-#define __PAD(m,n) u8 __pad##m[n]
-#define _PAD(line, n) __PAD(line, n)
-#define PAD(n) _PAD(__LINE__, (n))
-struct i2s_control_regs {
- PAD(0x38);
- __le32 fcr0; /* 0x38 (unknown) */
- __le32 cell_control; /* 0x3c (fcr1) */
- __le32 fcr2; /* 0x40 (unknown) */
- __le32 fcr3; /* 0x44 (fcr3) */
- __le32 clock_control; /* 0x48 (unknown) */
- PAD(4);
- /* total size: 0x50 bytes */
-} __attribute__((__packed__));
-
-#define CTRL_CLOCK_CELL_0_ENABLE (1<<10)
-#define CTRL_CLOCK_CLOCK_0_ENABLE (1<<12)
-#define CTRL_CLOCK_SWRESET_0 (1<<11)
-#define CTRL_CLOCK_INTF_0_ENABLE (1<<13)
-
-#define CTRL_CLOCK_CELL_1_ENABLE (1<<17)
-#define CTRL_CLOCK_CLOCK_1_ENABLE (1<<18)
-#define CTRL_CLOCK_SWRESET_1 (1<<19)
-#define CTRL_CLOCK_INTF_1_ENABLE (1<<20)
-
-#endif /* __I2SBUS_CONTROLREGS_H */
diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-core.c b/sound/aoa/soundbus/i2sbus/i2sbus-core.c
index f268dacdaa00..23190aa6bc7b 100644
--- a/sound/aoa/soundbus/i2sbus/i2sbus-core.c
+++ b/sound/aoa/soundbus/i2sbus/i2sbus-core.c
@@ -7,13 +7,16 @@
*/
#include <linux/module.h>
-#include <asm/macio.h>
-#include <asm/dbdma.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+
#include <sound/driver.h>
#include <sound/core.h>
-#include <linux/dma-mapping.h>
+
+#include <asm/macio.h>
+#include <asm/dbdma.h>
+
#include "../soundbus.h"
#include "i2sbus.h"
@@ -24,6 +27,11 @@ MODULE_DESCRIPTION("Apple Soundbus: I2S support");
* string that macio puts into the relevant device */
MODULE_ALIAS("of:Ni2sTi2sC");
+static int force;
+module_param(force, int, 0444);
+MODULE_PARM_DESC(force, "Force loading i2sbus even when"
+ " no layout-id property is present");
+
static struct of_device_id i2sbus_match[] = {
{ .name = "i2s" },
{ }
@@ -73,12 +81,12 @@ static void i2sbus_release_dev(struct device *dev)
if (i2sdev->intfregs) iounmap(i2sdev->intfregs);
if (i2sdev->out.dbdma) iounmap(i2sdev->out.dbdma);
if (i2sdev->in.dbdma) iounmap(i2sdev->in.dbdma);
- for (i=0;i<3;i++)
+ for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++)
if (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=0;i<3;i++)
+ for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++)
free_irq(i2sdev->interrupts[i], i2sdev);
i2sbus_control_remove_dev(i2sdev->control, i2sdev);
mutex_destroy(&i2sdev->lock);
@@ -101,10 +109,49 @@ static irqreturn_t i2sbus_bus_intr(int irq, void *devid, struct pt_regs *regs)
return IRQ_HANDLED;
}
-static int force;
-module_param(force, int, 0444);
-MODULE_PARM_DESC(force, "Force loading i2sbus even when"
- " no layout-id property is present");
+
+/*
+ * XXX FIXME: We test the layout_id's here to get the proper way of
+ * mapping in various registers, thanks to bugs in Apple device-trees.
+ * We could instead key off the machine model and the name of the i2s
+ * node (i2s-a). This we'll do when we move it all to macio_asic.c
+ * and have that export items for each sub-node too.
+ */
+static int i2sbus_get_and_fixup_rsrc(struct device_node *np, int index,
+ int layout, struct resource *res)
+{
+ struct device_node *parent;
+ int pindex, rc = -ENXIO;
+ u32 *reg;
+
+ /* Machines with layout 76 and 36 (K2 based) have a weird device
+ * tree what we need to special case.
+ * Normal machines just fetch the resource from the i2s-X node.
+ * Darwin further divides normal machines into old and new layouts
+ * with a subtely different code path but that doesn't seem necessary
+ * in practice, they just bloated it. In addition, even on our K2
+ * case the i2s-modem node, if we ever want to handle it, uses the
+ * normal layout
+ */
+ if (layout != 76 && layout != 36)
+ return of_address_to_resource(np, index, res);
+
+ parent = of_get_parent(np);
+ pindex = (index == aoa_resource_i2smmio) ? 0 : 1;
+ rc = of_address_to_resource(parent, pindex, res);
+ if (rc)
+ goto bail;
+ reg = (u32 *)get_property(np, "reg", NULL);
+ if (reg == NULL) {
+ rc = -ENXIO;
+ goto bail;
+ }
+ res->start += reg[index * 2];
+ res->end = res->start + reg[index * 2 + 1] - 1;
+ bail:
+ of_node_put(parent);
+ return rc;
+}
/* FIXME: look at device node refcounting */
static int i2sbus_add_dev(struct macio_dev *macio,
@@ -113,7 +160,8 @@ static int i2sbus_add_dev(struct macio_dev *macio,
{
struct i2sbus_dev *dev;
struct device_node *child = NULL, *sound = NULL;
- int i;
+ struct resource *r;
+ int i, layout = 0, rlen;
static const char *rnames[] = { "i2sbus: %s (control)",
"i2sbus: %s (tx)",
"i2sbus: %s (rx)" };
@@ -129,9 +177,6 @@ static int i2sbus_add_dev(struct macio_dev *macio,
if (strncmp(np->name, "i2s-", 4))
return 0;
- if (np->n_intrs != 3)
- return 0;
-
dev = kzalloc(sizeof(struct i2sbus_dev), GFP_KERNEL);
if (!dev)
return 0;
@@ -147,8 +192,9 @@ static int i2sbus_add_dev(struct macio_dev *macio,
u32 *layout_id;
layout_id = (u32*) get_property(sound, "layout-id", NULL);
if (layout_id) {
+ layout = *layout_id;
snprintf(dev->sound.modalias, 32,
- "sound-layout-%d", *layout_id);
+ "sound-layout-%d", layout);
force = 1;
}
}
@@ -178,22 +224,32 @@ static int i2sbus_add_dev(struct macio_dev *macio,
dev->bus_number = np->name[4] - 'a';
INIT_LIST_HEAD(&dev->sound.codec_list);
- for (i=0;i<3;i++) {
+ for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) {
dev->interrupts[i] = -1;
- snprintf(dev->rnames[i], sizeof(dev->rnames[i]), rnames[i], np->name);
+ snprintf(dev->rnames[i], sizeof(dev->rnames[i]),
+ rnames[i], np->name);
}
- for (i=0;i<3;i++) {
- if (request_irq(np->intrs[i].line, ints[i], 0, dev->rnames[i], dev))
+ for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) {
+ int irq = irq_of_parse_and_map(np, i);
+ if (request_irq(irq, ints[i], 0, dev->rnames[i], dev))
goto err;
- dev->interrupts[i] = np->intrs[i].line;
+ dev->interrupts[i] = irq;
}
- for (i=0;i<3;i++) {
- if (of_address_to_resource(np, i, &dev->resources[i]))
+
+ /* Resource handling is problematic as some device-trees contain
+ * useless crap (ugh ugh ugh). We work around that here by calling
+ * specific functions for calculating the appropriate resources.
+ *
+ * This will all be moved to macio_asic.c at one point
+ */
+ for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) {
+ if (i2sbus_get_and_fixup_rsrc(np,i,layout,&dev->resources[i]))
goto err;
- /* if only we could use our resource dev->resources[i]...
+ /* If only we could use our resource dev->resources[i]...
* but request_resource doesn't know about parents and
- * contained resources... */
+ * contained resources...
+ */
dev->allocated_resource[i] =
request_mem_region(dev->resources[i].start,
dev->resources[i].end -
@@ -204,13 +260,25 @@ static int i2sbus_add_dev(struct macio_dev *macio,
goto err;
}
}
- /* should do sanity checking here about length of them */
- dev->intfregs = ioremap(dev->resources[0].start,
- dev->resources[0].end-dev->resources[0].start+1);
- dev->out.dbdma = ioremap(dev->resources[1].start,
- dev->resources[1].end-dev->resources[1].start+1);
- dev->in.dbdma = ioremap(dev->resources[2].start,
- dev->resources[2].end-dev->resources[2].start+1);
+
+ r = &dev->resources[aoa_resource_i2smmio];
+ rlen = r->end - r->start + 1;
+ if (rlen < sizeof(struct i2s_interface_regs))
+ goto err;
+ dev->intfregs = ioremap(r->start, rlen);
+
+ r = &dev->resources[aoa_resource_txdbdma];
+ rlen = r->end - r->start + 1;
+ if (rlen < sizeof(struct dbdma_regs))
+ goto err;
+ dev->out.dbdma = ioremap(r->start, rlen);
+
+ r = &dev->resources[aoa_resource_rxdbdma];
+ rlen = r->end - r->start + 1;
+ if (rlen < sizeof(struct dbdma_regs))
+ goto err;
+ dev->in.dbdma = ioremap(r->start, rlen);
+
if (!dev->intfregs || !dev->out.dbdma || !dev->in.dbdma)
goto err;
diff --git a/sound/aoa/soundbus/i2sbus/i2sbus.h b/sound/aoa/soundbus/i2sbus/i2sbus.h
index cfa5162e3b0f..0c69d209be50 100644
--- a/sound/aoa/soundbus/i2sbus/i2sbus.h
+++ b/sound/aoa/soundbus/i2sbus/i2sbus.h
@@ -7,20 +7,22 @@
*/
#ifndef __I2SBUS_H
#define __I2SBUS_H
-#include <asm/dbdma.h>
#include <linux/interrupt.h>
-#include <sound/pcm.h>
#include <linux/spinlock.h>
#include <linux/mutex.h>
+
+#include <sound/pcm.h>
+
#include <asm/prom.h>
+#include <asm/pmac_feature.h>
+#include <asm/dbdma.h>
+
#include "i2sbus-interface.h"
-#include "i2sbus-control.h"
#include "../soundbus.h"
struct i2sbus_control {
- volatile struct i2s_control_regs __iomem *controlregs;
- struct resource rsrc;
struct list_head list;
+ struct macio_chip *macio;
};
#define MAX_DBDMA_COMMANDS 32
@@ -45,6 +47,12 @@ struct pcm_info {
volatile struct dbdma_regs __iomem *dbdma;
};
+enum {
+ aoa_resource_i2smmio = 0,
+ aoa_resource_txdbdma,
+ aoa_resource_rxdbdma,
+};
+
struct i2sbus_dev {
struct soundbus_dev sound;
struct macio_dev *macio;
diff --git a/sound/arm/aaci.c b/sound/arm/aaci.c
index 5f22d70fefc0..8435fdd1c87c 100644
--- a/sound/arm/aaci.c
+++ b/sound/arm/aaci.c
@@ -360,7 +360,7 @@ static int aaci_pcm_open(struct aaci *aaci, struct snd_pcm_substream *substream,
if (ret)
goto out;
- ret = request_irq(aaci->dev->irq[0], aaci_irq, SA_SHIRQ|SA_INTERRUPT,
+ ret = request_irq(aaci->dev->irq[0], aaci_irq, IRQF_SHARED|IRQF_DISABLED,
DRIVER_NAME, aaci);
if (ret)
goto out;
@@ -779,8 +779,9 @@ static struct aaci * __devinit aaci_init_card(struct amba_device *dev)
strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver));
strlcpy(card->shortname, "ARM AC'97 Interface", sizeof(card->shortname));
snprintf(card->longname, sizeof(card->longname),
- "%s at 0x%08lx, irq %d",
- card->shortname, dev->res.start, dev->irq[0]);
+ "%s at 0x%016llx, irq %d",
+ card->shortname, (unsigned long long)dev->res.start,
+ dev->irq[0]);
aaci = card->private_data;
mutex_init(&aaci->ac97_sem);
diff --git a/sound/arm/sa11xx-uda1341.c b/sound/arm/sa11xx-uda1341.c
index b88fb0c5a68a..c79a9afd0955 100644
--- a/sound/arm/sa11xx-uda1341.c
+++ b/sound/arm/sa11xx-uda1341.c
@@ -59,7 +59,6 @@
*
***************************************************************************************************/
-#include <linux/config.h>
#include <sound/driver.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
diff --git a/sound/core/Kconfig b/sound/core/Kconfig
index 4262a1c87731..b2927523d79d 100644
--- a/sound/core/Kconfig
+++ b/sound/core/Kconfig
@@ -122,8 +122,8 @@ config SND_SEQ_RTCTIMER_DEFAULT
If in doubt, say Y.
config SND_DYNAMIC_MINORS
- bool "Dynamic device file minor numbers (EXPERIMENTAL)"
- depends on SND && EXPERIMENTAL
+ bool "Dynamic device file minor numbers"
+ depends on SND
help
If you say Y here, the minor numbers of ALSA device files in
/dev/snd/ are allocated dynamically. This allows you to have
diff --git a/sound/core/control.c b/sound/core/control.c
index bb397eaa7187..6973a9686b67 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -75,6 +75,8 @@ static int snd_ctl_open(struct inode *inode, struct file *file)
init_waitqueue_head(&ctl->change_sleep);
spin_lock_init(&ctl->read_lock);
ctl->card = card;
+ ctl->prefer_pcm_subdevice = -1;
+ ctl->prefer_rawmidi_subdevice = -1;
ctl->pid = current->pid;
file->private_data = ctl;
write_lock_irqsave(&card->ctl_files_rwlock, flags);
@@ -236,11 +238,16 @@ struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol,
kctl.id.index = ncontrol->index;
kctl.count = ncontrol->count ? ncontrol->count : 1;
access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
- (ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|SNDRV_CTL_ELEM_ACCESS_INACTIVE|
- SNDRV_CTL_ELEM_ACCESS_DINDIRECT|SNDRV_CTL_ELEM_ACCESS_INDIRECT));
+ (ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
+ SNDRV_CTL_ELEM_ACCESS_INACTIVE|
+ SNDRV_CTL_ELEM_ACCESS_DINDIRECT|
+ SNDRV_CTL_ELEM_ACCESS_INDIRECT|
+ SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE|
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK));
kctl.info = ncontrol->info;
kctl.get = ncontrol->get;
kctl.put = ncontrol->put;
+ kctl.tlv.p = ncontrol->tlv.p;
kctl.private_value = ncontrol->private_value;
kctl.private_data = private_data;
return snd_ctl_new(&kctl, access);
@@ -882,6 +889,8 @@ struct user_element {
struct snd_ctl_elem_info info;
void *elem_data; /* element data */
unsigned long elem_data_size; /* size of element data in bytes */
+ void *tlv_data; /* TLV data */
+ unsigned long tlv_data_size; /* TLV data size */
void *priv_data; /* private data (like strings for enumerated type) */
unsigned long priv_data_size; /* size of private data in bytes */
};
@@ -916,9 +925,48 @@ static int snd_ctl_elem_user_put(struct snd_kcontrol *kcontrol,
return change;
}
+static int snd_ctl_elem_user_tlv(struct snd_kcontrol *kcontrol,
+ int op_flag,
+ unsigned int size,
+ unsigned int __user *tlv)
+{
+ struct user_element *ue = kcontrol->private_data;
+ int change = 0;
+ void *new_data;
+
+ if (op_flag > 0) {
+ if (size > 1024 * 128) /* sane value */
+ return -EINVAL;
+ new_data = kmalloc(size, GFP_KERNEL);
+ if (new_data == NULL)
+ return -ENOMEM;
+ if (copy_from_user(new_data, tlv, size)) {
+ kfree(new_data);
+ return -EFAULT;
+ }
+ change = ue->tlv_data_size != size;
+ if (!change)
+ change = memcmp(ue->tlv_data, new_data, size);
+ kfree(ue->tlv_data);
+ ue->tlv_data = new_data;
+ ue->tlv_data_size = size;
+ } else {
+ if (! ue->tlv_data_size || ! ue->tlv_data)
+ return -ENXIO;
+ if (size < ue->tlv_data_size)
+ return -ENOSPC;
+ if (copy_to_user(tlv, ue->tlv_data, ue->tlv_data_size))
+ return -EFAULT;
+ }
+ return change;
+}
+
static void snd_ctl_elem_user_free(struct snd_kcontrol *kcontrol)
{
- kfree(kcontrol->private_data);
+ struct user_element *ue = kcontrol->private_data;
+ if (ue->tlv_data)
+ kfree(ue->tlv_data);
+ kfree(ue);
}
static int snd_ctl_elem_add(struct snd_ctl_file *file,
@@ -937,7 +985,8 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
return -EINVAL;
access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
(info->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
- SNDRV_CTL_ELEM_ACCESS_INACTIVE));
+ SNDRV_CTL_ELEM_ACCESS_INACTIVE|
+ SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE));
info->id.numid = 0;
memset(&kctl, 0, sizeof(kctl));
down_write(&card->controls_rwsem);
@@ -963,6 +1012,10 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
kctl.get = snd_ctl_elem_user_get;
if (access & SNDRV_CTL_ELEM_ACCESS_WRITE)
kctl.put = snd_ctl_elem_user_put;
+ if (access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) {
+ kctl.tlv.c = snd_ctl_elem_user_tlv;
+ access |= SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+ }
switch (info->type) {
case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
private_size = sizeof(char);
@@ -997,6 +1050,7 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
if (ue == NULL)
return -ENOMEM;
ue->info = *info;
+ ue->info.access = 0;
ue->elem_data = (char *)ue + sizeof(*ue);
ue->elem_data_size = private_size;
kctl.private_free = snd_ctl_elem_user_free;
@@ -1067,6 +1121,67 @@ static int snd_ctl_subscribe_events(struct snd_ctl_file *file, int __user *ptr)
return 0;
}
+static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file,
+ struct snd_ctl_tlv __user *_tlv,
+ int op_flag)
+{
+ struct snd_card *card = file->card;
+ struct snd_ctl_tlv tlv;
+ struct snd_kcontrol *kctl;
+ struct snd_kcontrol_volatile *vd;
+ unsigned int len;
+ int err = 0;
+
+ if (copy_from_user(&tlv, _tlv, sizeof(tlv)))
+ return -EFAULT;
+ if (tlv.length < sizeof(unsigned int) * 3)
+ return -EINVAL;
+ down_read(&card->controls_rwsem);
+ kctl = snd_ctl_find_numid(card, tlv.numid);
+ if (kctl == NULL) {
+ err = -ENOENT;
+ goto __kctl_end;
+ }
+ if (kctl->tlv.p == NULL) {
+ err = -ENXIO;
+ goto __kctl_end;
+ }
+ vd = &kctl->vd[tlv.numid - kctl->id.numid];
+ if ((op_flag == 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) == 0) ||
+ (op_flag > 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) == 0) ||
+ (op_flag < 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND) == 0)) {
+ err = -ENXIO;
+ goto __kctl_end;
+ }
+ if (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
+ if (file && vd->owner != NULL && vd->owner != file) {
+ err = -EPERM;
+ goto __kctl_end;
+ }
+ err = kctl->tlv.c(kctl, op_flag, tlv.length, _tlv->tlv);
+ if (err > 0) {
+ up_read(&card->controls_rwsem);
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_TLV, &kctl->id);
+ return 0;
+ }
+ } else {
+ if (op_flag) {
+ err = -ENXIO;
+ goto __kctl_end;
+ }
+ len = kctl->tlv.p[1] + 2 * sizeof(unsigned int);
+ if (tlv.length < len) {
+ err = -ENOMEM;
+ goto __kctl_end;
+ }
+ if (copy_to_user(_tlv->tlv, kctl->tlv.p, len))
+ err = -EFAULT;
+ }
+ __kctl_end:
+ up_read(&card->controls_rwsem);
+ return err;
+}
+
static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct snd_ctl_file *ctl;
@@ -1086,11 +1201,11 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg
case SNDRV_CTL_IOCTL_CARD_INFO:
return snd_ctl_card_info(card, ctl, cmd, argp);
case SNDRV_CTL_IOCTL_ELEM_LIST:
- return snd_ctl_elem_list(ctl->card, argp);
+ return snd_ctl_elem_list(card, argp);
case SNDRV_CTL_IOCTL_ELEM_INFO:
return snd_ctl_elem_info_user(ctl, argp);
case SNDRV_CTL_IOCTL_ELEM_READ:
- return snd_ctl_elem_read_user(ctl->card, argp);
+ return snd_ctl_elem_read_user(card, argp);
case SNDRV_CTL_IOCTL_ELEM_WRITE:
return snd_ctl_elem_write_user(ctl, argp);
case SNDRV_CTL_IOCTL_ELEM_LOCK:
@@ -1105,6 +1220,12 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg
return snd_ctl_elem_remove(ctl, argp);
case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
return snd_ctl_subscribe_events(ctl, ip);
+ case SNDRV_CTL_IOCTL_TLV_READ:
+ return snd_ctl_tlv_ioctl(ctl, argp, 0);
+ case SNDRV_CTL_IOCTL_TLV_WRITE:
+ return snd_ctl_tlv_ioctl(ctl, argp, 1);
+ case SNDRV_CTL_IOCTL_TLV_COMMAND:
+ return snd_ctl_tlv_ioctl(ctl, argp, -1);
case SNDRV_CTL_IOCTL_POWER:
return -ENOPROTOOPT;
case SNDRV_CTL_IOCTL_POWER_STATE:
@@ -1338,6 +1459,11 @@ static int snd_ctl_dev_disconnect(struct snd_device *device)
struct snd_card *card = device->device_data;
struct list_head *flist;
struct snd_ctl_file *ctl;
+ int err, cardnum;
+
+ snd_assert(card != NULL, return -ENXIO);
+ cardnum = card->number;
+ snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO);
down_read(&card->controls_rwsem);
list_for_each(flist, &card->ctl_files) {
@@ -1346,6 +1472,10 @@ static int snd_ctl_dev_disconnect(struct snd_device *device)
kill_fasync(&ctl->fasync, SIGIO, POLL_ERR);
}
up_read(&card->controls_rwsem);
+
+ if ((err = snd_unregister_device(SNDRV_DEVICE_TYPE_CONTROL,
+ card, -1)) < 0)
+ return err;
return 0;
}
@@ -1367,23 +1497,6 @@ static int snd_ctl_dev_free(struct snd_device *device)
}
/*
- * de-registration of the control device
- */
-static int snd_ctl_dev_unregister(struct snd_device *device)
-{
- struct snd_card *card = device->device_data;
- int err, cardnum;
-
- snd_assert(card != NULL, return -ENXIO);
- cardnum = card->number;
- snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO);
- if ((err = snd_unregister_device(SNDRV_DEVICE_TYPE_CONTROL,
- card, -1)) < 0)
- return err;
- return snd_ctl_dev_free(device);
-}
-
-/*
* create control core:
* called from init.c
*/
@@ -1393,7 +1506,6 @@ int snd_ctl_create(struct snd_card *card)
.dev_free = snd_ctl_dev_free,
.dev_register = snd_ctl_dev_register,
.dev_disconnect = snd_ctl_dev_disconnect,
- .dev_unregister = snd_ctl_dev_unregister
};
snd_assert(card != NULL, return -ENXIO);
diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c
index 3c0161bb5ba4..ab48962c48ce 100644
--- a/sound/core/control_compat.c
+++ b/sound/core/control_compat.c
@@ -407,6 +407,10 @@ static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, uns
case SNDRV_CTL_IOCTL_POWER_STATE:
case SNDRV_CTL_IOCTL_ELEM_LOCK:
case SNDRV_CTL_IOCTL_ELEM_UNLOCK:
+ case SNDRV_CTL_IOCTL_ELEM_REMOVE:
+ case SNDRV_CTL_IOCTL_TLV_READ:
+ case SNDRV_CTL_IOCTL_TLV_WRITE:
+ case SNDRV_CTL_IOCTL_TLV_COMMAND:
return snd_ctl_ioctl(file, cmd, (unsigned long)argp);
case SNDRV_CTL_IOCTL_ELEM_LIST32:
return snd_ctl_elem_list_compat(ctl->card, argp);
diff --git a/sound/core/device.c b/sound/core/device.c
index 6ce4da4a1081..ccb25816ac9e 100644
--- a/sound/core/device.c
+++ b/sound/core/device.c
@@ -71,7 +71,7 @@ EXPORT_SYMBOL(snd_device_new);
* @device_data: the data pointer to release
*
* Removes the device from the list on the card and invokes the
- * callback, dev_unregister or dev_free, corresponding to the state.
+ * callbacks, dev_disconnect and dev_free, corresponding to the state.
* Then release the device.
*
* Returns zero if successful, or a negative error code on failure or if the
@@ -90,16 +90,14 @@ int snd_device_free(struct snd_card *card, void *device_data)
continue;
/* unlink */
list_del(&dev->list);
- if ((dev->state == SNDRV_DEV_REGISTERED ||
- dev->state == SNDRV_DEV_DISCONNECTED) &&
- dev->ops->dev_unregister) {
- if (dev->ops->dev_unregister(dev))
- snd_printk(KERN_ERR "device unregister failure\n");
- } else {
- if (dev->ops->dev_free) {
- if (dev->ops->dev_free(dev))
- snd_printk(KERN_ERR "device free failure\n");
- }
+ if (dev->state == SNDRV_DEV_REGISTERED &&
+ dev->ops->dev_disconnect)
+ if (dev->ops->dev_disconnect(dev))
+ snd_printk(KERN_ERR
+ "device disconnect failure\n");
+ if (dev->ops->dev_free) {
+ if (dev->ops->dev_free(dev))
+ snd_printk(KERN_ERR "device free failure\n");
}
kfree(dev);
return 0;
diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c
index 8bd0dcc93eba..9aa9d94891f0 100644
--- a/sound/core/hwdep.c
+++ b/sound/core/hwdep.c
@@ -42,7 +42,7 @@ static DEFINE_MUTEX(register_mutex);
static int snd_hwdep_free(struct snd_hwdep *hwdep);
static int snd_hwdep_dev_free(struct snd_device *device);
static int snd_hwdep_dev_register(struct snd_device *device);
-static int snd_hwdep_dev_unregister(struct snd_device *device);
+static int snd_hwdep_dev_disconnect(struct snd_device *device);
static struct snd_hwdep *snd_hwdep_search(struct snd_card *card, int device)
@@ -353,7 +353,7 @@ int snd_hwdep_new(struct snd_card *card, char *id, int device,
static struct snd_device_ops ops = {
.dev_free = snd_hwdep_dev_free,
.dev_register = snd_hwdep_dev_register,
- .dev_unregister = snd_hwdep_dev_unregister
+ .dev_disconnect = snd_hwdep_dev_disconnect,
};
snd_assert(rhwdep != NULL, return -EINVAL);
@@ -439,7 +439,7 @@ static int snd_hwdep_dev_register(struct snd_device *device)
return 0;
}
-static int snd_hwdep_dev_unregister(struct snd_device *device)
+static int snd_hwdep_dev_disconnect(struct snd_device *device)
{
struct snd_hwdep *hwdep = device->device_data;
@@ -454,9 +454,9 @@ static int snd_hwdep_dev_unregister(struct snd_device *device)
snd_unregister_oss_device(hwdep->oss_type, hwdep->card, hwdep->device);
#endif
snd_unregister_device(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card, hwdep->device);
- list_del(&hwdep->list);
+ list_del_init(&hwdep->list);
mutex_unlock(&register_mutex);
- return snd_hwdep_free(hwdep);
+ return 0;
}
#ifdef CONFIG_PROC_FS
@@ -497,7 +497,7 @@ static void __init snd_hwdep_proc_init(void)
static void __exit snd_hwdep_proc_done(void)
{
- snd_info_unregister(snd_hwdep_proc_entry);
+ snd_info_free_entry(snd_hwdep_proc_entry);
}
#else /* !CONFIG_PROC_FS */
#define snd_hwdep_proc_init()
diff --git a/sound/core/info.c b/sound/core/info.c
index 10c1772bf3ea..e43662b33f16 100644
--- a/sound/core/info.c
+++ b/sound/core/info.c
@@ -29,7 +29,6 @@
#include <sound/info.h>
#include <sound/version.h>
#include <linux/proc_fs.h>
-#include <linux/devfs_fs_kernel.h>
#include <linux/mutex.h>
#include <stdarg.h>
@@ -79,6 +78,7 @@ struct snd_info_private_data {
static int snd_info_version_init(void);
static int snd_info_version_done(void);
+static void snd_info_disconnect(struct snd_info_entry *entry);
/* resize the proc r/w buffer */
@@ -175,15 +175,15 @@ static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
switch (entry->content) {
case SNDRV_INFO_CONTENT_TEXT:
switch (orig) {
- case 0: /* SEEK_SET */
+ case SEEK_SET:
file->f_pos = offset;
ret = file->f_pos;
goto out;
- case 1: /* SEEK_CUR */
+ case SEEK_CUR:
file->f_pos += offset;
ret = file->f_pos;
goto out;
- case 2: /* SEEK_END */
+ case SEEK_END:
default:
ret = -EINVAL;
goto out;
@@ -305,7 +305,7 @@ static int snd_info_entry_open(struct inode *inode, struct file *file)
mutex_lock(&info_mutex);
p = PDE(inode);
entry = p == NULL ? NULL : (struct snd_info_entry *)p->data;
- if (entry == NULL || entry->disconnected) {
+ if (entry == NULL || ! entry->p) {
mutex_unlock(&info_mutex);
return -ENODEV;
}
@@ -587,10 +587,10 @@ int __exit snd_info_done(void)
snd_info_version_done();
if (snd_proc_root) {
#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
- snd_info_unregister(snd_seq_root);
+ snd_info_free_entry(snd_seq_root);
#endif
#ifdef CONFIG_SND_OSSEMUL
- snd_info_unregister(snd_oss_root);
+ snd_info_free_entry(snd_oss_root);
#endif
snd_remove_proc_entry(&proc_root, snd_proc_root);
}
@@ -649,17 +649,28 @@ int snd_info_card_register(struct snd_card *card)
* de-register the card proc file
* called from init.c
*/
-int snd_info_card_free(struct snd_card *card)
+void snd_info_card_disconnect(struct snd_card *card)
{
- snd_assert(card != NULL, return -ENXIO);
+ snd_assert(card != NULL, return);
+ mutex_lock(&info_mutex);
if (card->proc_root_link) {
snd_remove_proc_entry(snd_proc_root, card->proc_root_link);
card->proc_root_link = NULL;
}
- if (card->proc_root) {
- snd_info_unregister(card->proc_root);
- card->proc_root = NULL;
- }
+ if (card->proc_root)
+ snd_info_disconnect(card->proc_root);
+ mutex_unlock(&info_mutex);
+}
+
+/*
+ * release the card proc file resources
+ * called from init.c
+ */
+int snd_info_card_free(struct snd_card *card)
+{
+ snd_assert(card != NULL, return -ENXIO);
+ snd_info_free_entry(card->proc_root);
+ card->proc_root = NULL;
return 0;
}
@@ -768,6 +779,8 @@ static struct snd_info_entry *snd_info_create_entry(const char *name)
entry->mode = S_IFREG | S_IRUGO;
entry->content = SNDRV_INFO_CONTENT_TEXT;
mutex_init(&entry->access);
+ INIT_LIST_HEAD(&entry->children);
+ INIT_LIST_HEAD(&entry->list);
return entry;
}
@@ -820,30 +833,35 @@ struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card,
EXPORT_SYMBOL(snd_info_create_card_entry);
-static int snd_info_dev_free_entry(struct snd_device *device)
+static void snd_info_disconnect(struct snd_info_entry *entry)
{
- struct snd_info_entry *entry = device->device_data;
- snd_info_free_entry(entry);
- return 0;
-}
+ struct list_head *p, *n;
+ struct proc_dir_entry *root;
-static int snd_info_dev_register_entry(struct snd_device *device)
-{
- struct snd_info_entry *entry = device->device_data;
- return snd_info_register(entry);
+ list_for_each_safe(p, n, &entry->children) {
+ snd_info_disconnect(list_entry(p, struct snd_info_entry, list));
+ }
+
+ if (! entry->p)
+ return;
+ list_del_init(&entry->list);
+ root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
+ snd_assert(root, return);
+ snd_remove_proc_entry(root, entry->p);
+ entry->p = NULL;
}
-static int snd_info_dev_disconnect_entry(struct snd_device *device)
+static int snd_info_dev_free_entry(struct snd_device *device)
{
struct snd_info_entry *entry = device->device_data;
- entry->disconnected = 1;
+ snd_info_free_entry(entry);
return 0;
}
-static int snd_info_dev_unregister_entry(struct snd_device *device)
+static int snd_info_dev_register_entry(struct snd_device *device)
{
struct snd_info_entry *entry = device->device_data;
- return snd_info_unregister(entry);
+ return snd_info_register(entry);
}
/**
@@ -872,8 +890,7 @@ int snd_card_proc_new(struct snd_card *card, const char *name,
static struct snd_device_ops ops = {
.dev_free = snd_info_dev_free_entry,
.dev_register = snd_info_dev_register_entry,
- .dev_disconnect = snd_info_dev_disconnect_entry,
- .dev_unregister = snd_info_dev_unregister_entry
+ /* disconnect is done via snd_info_card_disconnect() */
};
struct snd_info_entry *entry;
int err;
@@ -902,6 +919,11 @@ void snd_info_free_entry(struct snd_info_entry * entry)
{
if (entry == NULL)
return;
+ if (entry->p) {
+ mutex_lock(&info_mutex);
+ snd_info_disconnect(entry);
+ mutex_unlock(&info_mutex);
+ }
kfree(entry->name);
if (entry->private_free)
entry->private_free(entry);
@@ -936,38 +958,14 @@ int snd_info_register(struct snd_info_entry * entry)
p->size = entry->size;
p->data = entry;
entry->p = p;
+ if (entry->parent)
+ list_add_tail(&entry->list, &entry->parent->children);
mutex_unlock(&info_mutex);
return 0;
}
EXPORT_SYMBOL(snd_info_register);
-/**
- * snd_info_unregister - de-register the info entry
- * @entry: the info entry
- *
- * De-registers the info entry and releases the instance.
- *
- * Returns zero if successful, or a negative error code on failure.
- */
-int snd_info_unregister(struct snd_info_entry * entry)
-{
- struct proc_dir_entry *root;
-
- if (! entry)
- return 0;
- snd_assert(entry->p != NULL, return -ENXIO);
- root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
- snd_assert(root, return -ENXIO);
- mutex_lock(&info_mutex);
- snd_remove_proc_entry(root, entry->p);
- mutex_unlock(&info_mutex);
- snd_info_free_entry(entry);
- return 0;
-}
-
-EXPORT_SYMBOL(snd_info_unregister);
-
/*
*/
@@ -1000,8 +998,7 @@ static int __init snd_info_version_init(void)
static int __exit snd_info_version_done(void)
{
- if (snd_info_version_entry)
- snd_info_unregister(snd_info_version_entry);
+ snd_info_free_entry(snd_info_version_entry);
return 0;
}
diff --git a/sound/core/info_oss.c b/sound/core/info_oss.c
index bb2c40d0ab66..3ebc34919c76 100644
--- a/sound/core/info_oss.c
+++ b/sound/core/info_oss.c
@@ -131,10 +131,8 @@ int snd_info_minor_register(void)
int snd_info_minor_unregister(void)
{
- if (snd_sndstat_proc_entry) {
- snd_info_unregister(snd_sndstat_proc_entry);
- snd_sndstat_proc_entry = NULL;
- }
+ snd_info_free_entry(snd_sndstat_proc_entry);
+ snd_sndstat_proc_entry = NULL;
return 0;
}
diff --git a/sound/core/init.c b/sound/core/init.c
index 4d9258884e44..d7607a25acdf 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -81,8 +81,6 @@ static inline int init_info_for_card(struct snd_card *card)
#define init_info_for_card(card)
#endif
-static void snd_card_free_thread(void * __card);
-
/**
* snd_card_new - create and initialize a soundcard structure
* @idx: card index (address) [0 ... (SNDRV_CARDS-1)]
@@ -145,7 +143,6 @@ struct snd_card *snd_card_new(int idx, const char *xid,
INIT_LIST_HEAD(&card->ctl_files);
spin_lock_init(&card->files_lock);
init_waitqueue_head(&card->shutdown_sleep);
- INIT_WORK(&card->free_workq, snd_card_free_thread, card);
#ifdef CONFIG_PM
mutex_init(&card->power_lock);
init_waitqueue_head(&card->power_sleep);
@@ -310,6 +307,7 @@ int snd_card_disconnect(struct snd_card *card)
if (err < 0)
snd_printk(KERN_ERR "not all devices for card %i can be disconnected\n", card->number);
+ snd_info_card_disconnect(card);
return 0;
}
@@ -326,22 +324,10 @@ EXPORT_SYMBOL(snd_card_disconnect);
* Returns zero. Frees all associated devices and frees the control
* interface associated to given soundcard.
*/
-int snd_card_free(struct snd_card *card)
+static int snd_card_do_free(struct snd_card *card)
{
struct snd_shutdown_f_ops *s_f_ops;
- if (card == NULL)
- return -EINVAL;
- mutex_lock(&snd_card_mutex);
- snd_cards[card->number] = NULL;
- mutex_unlock(&snd_card_mutex);
-
-#ifdef CONFIG_PM
- wake_up(&card->power_sleep);
-#endif
- /* wait, until all devices are ready for the free operation */
- wait_event(card->shutdown_sleep, card->files == NULL);
-
#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
if (snd_mixer_oss_notify_callback)
snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE);
@@ -360,7 +346,7 @@ int snd_card_free(struct snd_card *card)
}
if (card->private_free)
card->private_free(card);
- snd_info_unregister(card->proc_id);
+ snd_info_free_entry(card->proc_id);
if (snd_info_card_free(card) < 0) {
snd_printk(KERN_WARNING "unable to free card info\n");
/* Not fatal error */
@@ -370,61 +356,59 @@ int snd_card_free(struct snd_card *card)
card->s_f_ops = s_f_ops->next;
kfree(s_f_ops);
}
+ kfree(card);
+ return 0;
+}
+
+static int snd_card_free_prepare(struct snd_card *card)
+{
+ if (card == NULL)
+ return -EINVAL;
+ (void) snd_card_disconnect(card);
mutex_lock(&snd_card_mutex);
+ snd_cards[card->number] = NULL;
snd_cards_lock &= ~(1 << card->number);
mutex_unlock(&snd_card_mutex);
- kfree(card);
+#ifdef CONFIG_PM
+ wake_up(&card->power_sleep);
+#endif
return 0;
}
-EXPORT_SYMBOL(snd_card_free);
-
-static void snd_card_free_thread(void * __card)
+int snd_card_free_when_closed(struct snd_card *card)
{
- struct snd_card *card = __card;
- struct module * module = card->module;
-
- if (!try_module_get(module)) {
- snd_printk(KERN_ERR "unable to lock toplevel module for card %i in free thread\n", card->number);
- module = NULL;
- }
+ int free_now = 0;
+ int ret = snd_card_free_prepare(card);
+ if (ret)
+ return ret;
- snd_card_free(card);
+ spin_lock(&card->files_lock);
+ if (card->files == NULL)
+ free_now = 1;
+ else
+ card->free_on_last_close = 1;
+ spin_unlock(&card->files_lock);
- module_put(module);
+ if (free_now)
+ snd_card_do_free(card);
+ return 0;
}
-/**
- * snd_card_free_in_thread - call snd_card_free() in thread
- * @card: soundcard structure
- *
- * This function schedules the call of snd_card_free() function in a
- * work queue. When all devices are released (non-busy), the work
- * is woken up and calls snd_card_free().
- *
- * When a card can be disconnected at any time by hotplug service,
- * this function should be used in disconnect (or detach) callback
- * instead of calling snd_card_free() directly.
- *
- * Returns - zero otherwise a negative error code if the start of thread failed.
- */
-int snd_card_free_in_thread(struct snd_card *card)
-{
- if (card->files == NULL) {
- snd_card_free(card);
- return 0;
- }
+EXPORT_SYMBOL(snd_card_free_when_closed);
- if (schedule_work(&card->free_workq))
- return 0;
+int snd_card_free(struct snd_card *card)
+{
+ int ret = snd_card_free_prepare(card);
+ if (ret)
+ return ret;
- snd_printk(KERN_ERR "schedule_work() failed in snd_card_free_in_thread for card %i\n", card->number);
- /* try to free the structure immediately */
- snd_card_free(card);
- return -EFAULT;
+ /* wait, until all devices are ready for the free operation */
+ wait_event(card->shutdown_sleep, card->files == NULL);
+ snd_card_do_free(card);
+ return 0;
}
-EXPORT_SYMBOL(snd_card_free_in_thread);
+EXPORT_SYMBOL(snd_card_free);
static void choose_default_id(struct snd_card *card)
{
@@ -625,9 +609,9 @@ int __init snd_card_info_init(void)
int __exit snd_card_info_done(void)
{
- snd_info_unregister(snd_card_info_entry);
+ snd_info_free_entry(snd_card_info_entry);
#ifdef MODULE
- snd_info_unregister(snd_card_module_info_entry);
+ snd_info_free_entry(snd_card_module_info_entry);
#endif
return 0;
}
@@ -708,15 +692,16 @@ EXPORT_SYMBOL(snd_card_file_add);
*
* This function removes the file formerly added to the card via
* snd_card_file_add() function.
- * If all files are removed and the release of the card is
- * scheduled, it will wake up the the thread to call snd_card_free()
- * (see snd_card_free_in_thread() function).
+ * If all files are removed and snd_card_free_when_closed() was
+ * called beforehand, it processes the pending release of
+ * resources.
*
* Returns zero or a negative error code.
*/
int snd_card_file_remove(struct snd_card *card, struct file *file)
{
struct snd_monitor_file *mfile, *pfile = NULL;
+ int last_close = 0;
spin_lock(&card->files_lock);
mfile = card->files;
@@ -731,9 +716,14 @@ int snd_card_file_remove(struct snd_card *card, struct file *file)
pfile = mfile;
mfile = mfile->next;
}
- spin_unlock(&card->files_lock);
if (card->files == NULL)
+ last_close = 1;
+ spin_unlock(&card->files_lock);
+ if (last_close) {
wake_up(&card->shutdown_sleep);
+ if (card->free_on_last_close)
+ snd_card_do_free(card);
+ }
if (!mfile) {
snd_printk(KERN_ERR "ALSA card file remove problem (%p)\n", file);
return -ENOENT;
diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c
index 3fc6f97075ed..bc0bd0910a62 100644
--- a/sound/core/memalloc.c
+++ b/sound/core/memalloc.c
@@ -21,7 +21,6 @@
*
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c
index 71b5080fa66d..f4c67042e3ac 100644
--- a/sound/core/oss/mixer_oss.c
+++ b/sound/core/oss/mixer_oss.c
@@ -988,13 +988,12 @@ static int snd_mixer_oss_build_input(struct snd_mixer_oss *mixer, struct snd_mix
if (ptr->index == 0 && (kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0)) != NULL) {
struct snd_ctl_elem_info *uinfo;
- uinfo = kmalloc(sizeof(*uinfo), GFP_KERNEL);
+ uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
if (! uinfo) {
up_read(&mixer->card->controls_rwsem);
return -ENOMEM;
}
- memset(uinfo, 0, sizeof(*uinfo));
if (kctl->info(kctl, uinfo)) {
up_read(&mixer->card->controls_rwsem);
return 0;
@@ -1194,10 +1193,8 @@ static void snd_mixer_oss_proc_init(struct snd_mixer_oss *mixer)
static void snd_mixer_oss_proc_done(struct snd_mixer_oss *mixer)
{
- if (mixer->proc_entry) {
- snd_info_unregister(mixer->proc_entry);
- mixer->proc_entry = NULL;
- }
+ snd_info_free_entry(mixer->proc_entry);
+ mixer->proc_entry = NULL;
}
#else /* !CONFIG_PROC_FS */
#define snd_mixer_oss_proc_init(mix)
@@ -1313,21 +1310,19 @@ static int snd_mixer_oss_notify_handler(struct snd_card *card, int cmd)
card->mixer_oss = mixer;
snd_mixer_oss_build(mixer);
snd_mixer_oss_proc_init(mixer);
- } else if (cmd == SND_MIXER_OSS_NOTIFY_DISCONNECT) {
- mixer = card->mixer_oss;
- if (mixer == NULL || !mixer->oss_dev_alloc)
- return 0;
- snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, mixer->card, 0);
- mixer->oss_dev_alloc = 0;
- } else { /* free */
+ } else {
mixer = card->mixer_oss;
if (mixer == NULL)
return 0;
+ if (mixer->oss_dev_alloc) {
#ifdef SNDRV_OSS_INFO_DEV_MIXERS
- snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_MIXERS, mixer->card->number);
+ snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_MIXERS, mixer->card->number);
#endif
- if (mixer->oss_dev_alloc)
snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, mixer->card, 0);
+ mixer->oss_dev_alloc = 0;
+ }
+ if (cmd == SND_MIXER_OSS_NOTIFY_DISCONNECT)
+ return 0;
snd_mixer_oss_proc_done(mixer);
return snd_mixer_oss_free1(mixer);
}
diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c
index f5ff4f4a16ee..505b23ec4058 100644
--- a/sound/core/oss/pcm_oss.c
+++ b/sound/core/oss/pcm_oss.c
@@ -2228,6 +2228,8 @@ static int snd_pcm_oss_open_file(struct file *file,
for (idx = 0; idx < 2; idx++) {
if (setup[idx].disable)
continue;
+ if (! pcm->streams[idx].substream_count)
+ continue; /* no matching substream */
if (idx == SNDRV_PCM_STREAM_PLAYBACK) {
if (! (f_mode & FMODE_WRITE))
continue;
@@ -2844,11 +2846,9 @@ static void snd_pcm_oss_proc_done(struct snd_pcm *pcm)
int stream;
for (stream = 0; stream < 2; ++stream) {
struct snd_pcm_str *pstr = &pcm->streams[stream];
- if (pstr->oss.proc_entry) {
- snd_info_unregister(pstr->oss.proc_entry);
- pstr->oss.proc_entry = NULL;
- snd_pcm_oss_proc_free_setup_list(pstr);
- }
+ snd_info_free_entry(pstr->oss.proc_entry);
+ pstr->oss.proc_entry = NULL;
+ snd_pcm_oss_proc_free_setup_list(pstr);
}
}
#else /* !CONFIG_SND_VERBOSE_PROCFS */
@@ -2929,25 +2929,23 @@ static int snd_pcm_oss_disconnect_minor(struct snd_pcm *pcm)
snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM,
pcm->card, 1);
}
- }
- return 0;
-}
-
-static int snd_pcm_oss_unregister_minor(struct snd_pcm *pcm)
-{
- snd_pcm_oss_disconnect_minor(pcm);
- if (pcm->oss.reg) {
if (dsp_map[pcm->card->number] == (int)pcm->device) {
#ifdef SNDRV_OSS_INFO_DEV_AUDIO
snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_AUDIO, pcm->card->number);
#endif
}
pcm->oss.reg = 0;
- snd_pcm_oss_proc_done(pcm);
}
return 0;
}
+static int snd_pcm_oss_unregister_minor(struct snd_pcm *pcm)
+{
+ snd_pcm_oss_disconnect_minor(pcm);
+ snd_pcm_oss_proc_done(pcm);
+ return 0;
+}
+
static struct snd_pcm_notify snd_pcm_oss_notify =
{
.n_register = snd_pcm_oss_register_minor,
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index 7581edd7b9ff..bf8f412988b8 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -42,7 +42,6 @@ static int snd_pcm_free(struct snd_pcm *pcm);
static int snd_pcm_dev_free(struct snd_device *device);
static int snd_pcm_dev_register(struct snd_device *device);
static int snd_pcm_dev_disconnect(struct snd_device *device);
-static int snd_pcm_dev_unregister(struct snd_device *device);
static struct snd_pcm *snd_pcm_search(struct snd_card *card, int device)
{
@@ -494,19 +493,13 @@ static int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr)
static int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr)
{
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
- if (pstr->proc_xrun_debug_entry) {
- snd_info_unregister(pstr->proc_xrun_debug_entry);
- pstr->proc_xrun_debug_entry = NULL;
- }
+ snd_info_free_entry(pstr->proc_xrun_debug_entry);
+ pstr->proc_xrun_debug_entry = NULL;
#endif
- if (pstr->proc_info_entry) {
- snd_info_unregister(pstr->proc_info_entry);
- pstr->proc_info_entry = NULL;
- }
- if (pstr->proc_root) {
- snd_info_unregister(pstr->proc_root);
- pstr->proc_root = NULL;
- }
+ snd_info_free_entry(pstr->proc_info_entry);
+ pstr->proc_info_entry = NULL;
+ snd_info_free_entry(pstr->proc_root);
+ pstr->proc_root = NULL;
return 0;
}
@@ -570,29 +563,19 @@ static int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream)
return 0;
}
-
+
static int snd_pcm_substream_proc_done(struct snd_pcm_substream *substream)
{
- if (substream->proc_info_entry) {
- snd_info_unregister(substream->proc_info_entry);
- substream->proc_info_entry = NULL;
- }
- if (substream->proc_hw_params_entry) {
- snd_info_unregister(substream->proc_hw_params_entry);
- substream->proc_hw_params_entry = NULL;
- }
- if (substream->proc_sw_params_entry) {
- snd_info_unregister(substream->proc_sw_params_entry);
- substream->proc_sw_params_entry = NULL;
- }
- if (substream->proc_status_entry) {
- snd_info_unregister(substream->proc_status_entry);
- substream->proc_status_entry = NULL;
- }
- if (substream->proc_root) {
- snd_info_unregister(substream->proc_root);
- substream->proc_root = NULL;
- }
+ snd_info_free_entry(substream->proc_info_entry);
+ substream->proc_info_entry = NULL;
+ snd_info_free_entry(substream->proc_hw_params_entry);
+ substream->proc_hw_params_entry = NULL;
+ snd_info_free_entry(substream->proc_sw_params_entry);
+ substream->proc_sw_params_entry = NULL;
+ snd_info_free_entry(substream->proc_status_entry);
+ substream->proc_status_entry = NULL;
+ snd_info_free_entry(substream->proc_root);
+ substream->proc_root = NULL;
return 0;
}
#else /* !CONFIG_SND_VERBOSE_PROCFS */
@@ -696,7 +679,6 @@ int snd_pcm_new(struct snd_card *card, char *id, int device,
.dev_free = snd_pcm_dev_free,
.dev_register = snd_pcm_dev_register,
.dev_disconnect = snd_pcm_dev_disconnect,
- .dev_unregister = snd_pcm_dev_unregister
};
snd_assert(rpcm != NULL, return -EINVAL);
@@ -740,6 +722,7 @@ static void snd_pcm_free_stream(struct snd_pcm_str * pstr)
substream = pstr->substream;
while (substream) {
substream_next = substream->next;
+ snd_pcm_timer_done(substream);
snd_pcm_substream_proc_done(substream);
kfree(substream);
substream = substream_next;
@@ -756,7 +739,12 @@ static void snd_pcm_free_stream(struct snd_pcm_str * pstr)
static int snd_pcm_free(struct snd_pcm *pcm)
{
+ struct snd_pcm_notify *notify;
+
snd_assert(pcm != NULL, return -ENXIO);
+ list_for_each_entry(notify, &snd_pcm_notify_list, list) {
+ notify->n_unregister(pcm);
+ }
if (pcm->private_free)
pcm->private_free(pcm);
snd_pcm_lib_preallocate_free_for_all(pcm);
@@ -804,7 +792,8 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
kctl = snd_ctl_file(list);
if (kctl->pid == current->pid) {
prefer_subdevice = kctl->prefer_pcm_subdevice;
- break;
+ if (prefer_subdevice != -1)
+ break;
}
}
up_read(&card->controls_rwsem);
@@ -918,6 +907,28 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
substream->pstr->substream_opened--;
}
+static ssize_t show_pcm_class(struct class_device *class_device, char *buf)
+{
+ struct snd_pcm *pcm;
+ const char *str;
+ static const char *strs[SNDRV_PCM_CLASS_LAST + 1] = {
+ [SNDRV_PCM_CLASS_GENERIC] = "generic",
+ [SNDRV_PCM_CLASS_MULTI] = "multi",
+ [SNDRV_PCM_CLASS_MODEM] = "modem",
+ [SNDRV_PCM_CLASS_DIGITIZER] = "digitizer",
+ };
+
+ if (! (pcm = class_get_devdata(class_device)) ||
+ pcm->dev_class > SNDRV_PCM_CLASS_LAST)
+ str = "none";
+ else
+ str = strs[pcm->dev_class];
+ return snprintf(buf, PAGE_SIZE, "%s\n", str);
+}
+
+static struct class_device_attribute pcm_attrs =
+ __ATTR(pcm_class, S_IRUGO, show_pcm_class, NULL);
+
static int snd_pcm_dev_register(struct snd_device *device)
{
int cidx, err;
@@ -956,6 +967,8 @@ static int snd_pcm_dev_register(struct snd_device *device)
mutex_unlock(&register_mutex);
return err;
}
+ snd_add_device_sysfs_file(devtype, pcm->card, pcm->device,
+ &pcm_attrs);
for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
snd_pcm_timer_init(substream);
}
@@ -971,35 +984,22 @@ static int snd_pcm_dev_register(struct snd_device *device)
static int snd_pcm_dev_disconnect(struct snd_device *device)
{
struct snd_pcm *pcm = device->device_data;
- struct list_head *list;
+ struct snd_pcm_notify *notify;
struct snd_pcm_substream *substream;
- int cidx;
+ int cidx, devtype;
mutex_lock(&register_mutex);
+ if (list_empty(&pcm->list))
+ goto unlock;
+
list_del_init(&pcm->list);
for (cidx = 0; cidx < 2; cidx++)
for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
if (substream->runtime)
substream->runtime->status->state = SNDRV_PCM_STATE_DISCONNECTED;
- list_for_each(list, &snd_pcm_notify_list) {
- struct snd_pcm_notify *notify;
- notify = list_entry(list, struct snd_pcm_notify, list);
+ list_for_each_entry(notify, &snd_pcm_notify_list, list) {
notify->n_disconnect(pcm);
}
- mutex_unlock(&register_mutex);
- return 0;
-}
-
-static int snd_pcm_dev_unregister(struct snd_device *device)
-{
- int cidx, devtype;
- struct snd_pcm_substream *substream;
- struct list_head *list;
- struct snd_pcm *pcm = device->device_data;
-
- snd_assert(pcm != NULL, return -ENXIO);
- mutex_lock(&register_mutex);
- list_del(&pcm->list);
for (cidx = 0; cidx < 2; cidx++) {
devtype = -1;
switch (cidx) {
@@ -1011,23 +1011,20 @@ static int snd_pcm_dev_unregister(struct snd_device *device)
break;
}
snd_unregister_device(devtype, pcm->card, pcm->device);
- for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
- snd_pcm_timer_done(substream);
- }
- list_for_each(list, &snd_pcm_notify_list) {
- struct snd_pcm_notify *notify;
- notify = list_entry(list, struct snd_pcm_notify, list);
- notify->n_unregister(pcm);
}
+ unlock:
mutex_unlock(&register_mutex);
- return snd_pcm_free(pcm);
+ return 0;
}
int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree)
{
struct list_head *p;
- snd_assert(notify != NULL && notify->n_register != NULL && notify->n_unregister != NULL, return -EINVAL);
+ snd_assert(notify != NULL &&
+ notify->n_register != NULL &&
+ notify->n_unregister != NULL &&
+ notify->n_disconnect, return -EINVAL);
mutex_lock(&register_mutex);
if (nfree) {
list_del(&notify->list);
@@ -1090,8 +1087,7 @@ static void snd_pcm_proc_init(void)
static void snd_pcm_proc_done(void)
{
- if (snd_pcm_proc_entry)
- snd_info_unregister(snd_pcm_proc_entry);
+ snd_info_free_entry(snd_pcm_proc_entry);
}
#else /* !CONFIG_PROC_FS */
diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c
index 2b8aab6fd6cd..2b539799d23b 100644
--- a/sound/core/pcm_compat.c
+++ b/sound/core/pcm_compat.c
@@ -478,7 +478,7 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
* mmap of PCM status/control records because of the size
* incompatibility.
*/
- substream->no_mmap_ctrl = 1;
+ pcm_file->no_compat_mmap = 1;
switch (cmd) {
case SNDRV_PCM_IOCTL_PVERSION:
diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c
index 067d2056db9a..be030cb4d373 100644
--- a/sound/core/pcm_memory.c
+++ b/sound/core/pcm_memory.c
@@ -101,7 +101,7 @@ int snd_pcm_lib_preallocate_free(struct snd_pcm_substream *substream)
{
snd_pcm_lib_preallocate_dma_free(substream);
#ifdef CONFIG_SND_VERBOSE_PROCFS
- snd_info_unregister(substream->proc_prealloc_entry);
+ snd_info_free_entry(substream->proc_prealloc_entry);
substream->proc_prealloc_entry = NULL;
#endif
return 0;
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 439f047929e1..0224c70414f5 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -1992,35 +1992,9 @@ int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
return 0;
}
-static void snd_pcm_add_file(struct snd_pcm_str *str,
- struct snd_pcm_file *pcm_file)
-{
- pcm_file->next = str->files;
- str->files = pcm_file;
-}
-
-static void snd_pcm_remove_file(struct snd_pcm_str *str,
- struct snd_pcm_file *pcm_file)
-{
- struct snd_pcm_file * pcm_file1;
- if (str->files == pcm_file) {
- str->files = pcm_file->next;
- } else {
- pcm_file1 = str->files;
- while (pcm_file1 && pcm_file1->next != pcm_file)
- pcm_file1 = pcm_file1->next;
- if (pcm_file1 != NULL)
- pcm_file1->next = pcm_file->next;
- }
-}
-
static void pcm_release_private(struct snd_pcm_substream *substream)
{
- struct snd_pcm_file *pcm_file = substream->file;
-
snd_pcm_unlink(substream);
- snd_pcm_remove_file(substream->pstr, pcm_file);
- kfree(pcm_file);
}
void snd_pcm_release_substream(struct snd_pcm_substream *substream)
@@ -2060,7 +2034,6 @@ int snd_pcm_open_substream(struct snd_pcm *pcm, int stream,
return 0;
}
- substream->no_mmap_ctrl = 0;
err = snd_pcm_hw_constraints_init(substream);
if (err < 0) {
snd_printd("snd_pcm_hw_constraints_init failed\n");
@@ -2105,19 +2078,16 @@ static int snd_pcm_open_file(struct file *file,
if (err < 0)
return err;
- if (substream->ref_count > 1)
- pcm_file = substream->file;
- else {
- pcm_file = kzalloc(sizeof(*pcm_file), GFP_KERNEL);
- if (pcm_file == NULL) {
- snd_pcm_release_substream(substream);
- return -ENOMEM;
- }
+ pcm_file = kzalloc(sizeof(*pcm_file), GFP_KERNEL);
+ if (pcm_file == NULL) {
+ snd_pcm_release_substream(substream);
+ return -ENOMEM;
+ }
+ pcm_file->substream = substream;
+ if (substream->ref_count == 1) {
str = substream->pstr;
substream->file = pcm_file;
substream->pcm_release = pcm_release_private;
- pcm_file->substream = substream;
- snd_pcm_add_file(str, pcm_file);
}
file->private_data = pcm_file;
*rpcm_file = pcm_file;
@@ -2209,6 +2179,7 @@ static int snd_pcm_release(struct inode *inode, struct file *file)
fasync_helper(-1, file, 0, &substream->runtime->fasync);
mutex_lock(&pcm->open_mutex);
snd_pcm_release_substream(substream);
+ kfree(pcm_file);
mutex_unlock(&pcm->open_mutex);
wake_up(&pcm->open_wait);
module_put(pcm->card->module);
@@ -3270,11 +3241,11 @@ static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area)
offset = area->vm_pgoff << PAGE_SHIFT;
switch (offset) {
case SNDRV_PCM_MMAP_OFFSET_STATUS:
- if (substream->no_mmap_ctrl)
+ if (pcm_file->no_compat_mmap)
return -ENXIO;
return snd_pcm_mmap_status(substream, file, area);
case SNDRV_PCM_MMAP_OFFSET_CONTROL:
- if (substream->no_mmap_ctrl)
+ if (pcm_file->no_compat_mmap)
return -ENXIO;
return snd_pcm_mmap_control(substream, file, area);
default:
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c
index 8c15c66eb4aa..269c467ca9bb 100644
--- a/sound/core/rawmidi.c
+++ b/sound/core/rawmidi.c
@@ -55,7 +55,6 @@ static int snd_rawmidi_free(struct snd_rawmidi *rawmidi);
static int snd_rawmidi_dev_free(struct snd_device *device);
static int snd_rawmidi_dev_register(struct snd_device *device);
static int snd_rawmidi_dev_disconnect(struct snd_device *device);
-static int snd_rawmidi_dev_unregister(struct snd_device *device);
static LIST_HEAD(snd_rawmidi_devices);
static DEFINE_MUTEX(register_mutex);
@@ -431,7 +430,8 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
kctl = snd_ctl_file(list);
if (kctl->pid == current->pid) {
subdevice = kctl->prefer_rawmidi_subdevice;
- break;
+ if (subdevice != -1)
+ break;
}
}
up_read(&card->controls_rwsem);
@@ -1426,7 +1426,6 @@ int snd_rawmidi_new(struct snd_card *card, char *id, int device,
.dev_free = snd_rawmidi_dev_free,
.dev_register = snd_rawmidi_dev_register,
.dev_disconnect = snd_rawmidi_dev_disconnect,
- .dev_unregister = snd_rawmidi_dev_unregister
};
snd_assert(rrawmidi != NULL, return -EINVAL);
@@ -1479,6 +1478,14 @@ static void snd_rawmidi_free_substreams(struct snd_rawmidi_str *stream)
static int snd_rawmidi_free(struct snd_rawmidi *rmidi)
{
snd_assert(rmidi != NULL, return -ENXIO);
+
+ snd_info_free_entry(rmidi->proc_entry);
+ rmidi->proc_entry = NULL;
+ mutex_lock(&register_mutex);
+ if (rmidi->ops && rmidi->ops->dev_unregister)
+ rmidi->ops->dev_unregister(rmidi);
+ mutex_unlock(&register_mutex);
+
snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]);
snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]);
if (rmidi->private_free)
@@ -1587,21 +1594,6 @@ static int snd_rawmidi_dev_disconnect(struct snd_device *device)
mutex_lock(&register_mutex);
list_del_init(&rmidi->list);
- mutex_unlock(&register_mutex);
- return 0;
-}
-
-static int snd_rawmidi_dev_unregister(struct snd_device *device)
-{
- struct snd_rawmidi *rmidi = device->device_data;
-
- snd_assert(rmidi != NULL, return -ENXIO);
- mutex_lock(&register_mutex);
- list_del(&rmidi->list);
- if (rmidi->proc_entry) {
- snd_info_unregister(rmidi->proc_entry);
- rmidi->proc_entry = NULL;
- }
#ifdef CONFIG_SND_OSSEMUL
if (rmidi->ossreg) {
if ((int)rmidi->device == midi_map[rmidi->card->number]) {
@@ -1615,17 +1607,9 @@ static int snd_rawmidi_dev_unregister(struct snd_device *device)
rmidi->ossreg = 0;
}
#endif /* CONFIG_SND_OSSEMUL */
- if (rmidi->ops && rmidi->ops->dev_unregister)
- rmidi->ops->dev_unregister(rmidi);
snd_unregister_device(SNDRV_DEVICE_TYPE_RAWMIDI, rmidi->card, rmidi->device);
mutex_unlock(&register_mutex);
-#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
- if (rmidi->seq_dev) {
- snd_device_free(rmidi->card, rmidi->seq_dev);
- rmidi->seq_dev = NULL;
- }
-#endif
- return snd_rawmidi_free(rmidi);
+ return 0;
}
/**
diff --git a/sound/core/rtctimer.c b/sound/core/rtctimer.c
index 84704ccb1829..412dd62b654e 100644
--- a/sound/core/rtctimer.c
+++ b/sound/core/rtctimer.c
@@ -156,7 +156,7 @@ static int __init rtctimer_init(void)
static void __exit rtctimer_exit(void)
{
if (rtctimer) {
- snd_timer_global_unregister(rtctimer);
+ snd_timer_global_free(rtctimer);
rtctimer = NULL;
}
}
diff --git a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c
index e7234135641c..92858cf8b6eb 100644
--- a/sound/core/seq/oss/seq_oss.c
+++ b/sound/core/seq/oss/seq_oss.c
@@ -303,8 +303,7 @@ register_proc(void)
static void
unregister_proc(void)
{
- if (info_entry)
- snd_info_unregister(info_entry);
+ snd_info_free_entry(info_entry);
info_entry = NULL;
}
#endif /* CONFIG_PROC_FS */
diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c
index d812dc886360..b79d011813c0 100644
--- a/sound/core/seq/seq_device.c
+++ b/sound/core/seq/seq_device.c
@@ -90,7 +90,6 @@ static int snd_seq_device_free(struct snd_seq_device *dev);
static int snd_seq_device_dev_free(struct snd_device *device);
static int snd_seq_device_dev_register(struct snd_device *device);
static int snd_seq_device_dev_disconnect(struct snd_device *device);
-static int snd_seq_device_dev_unregister(struct snd_device *device);
static int init_device(struct snd_seq_device *dev, struct ops_list *ops);
static int free_device(struct snd_seq_device *dev, struct ops_list *ops);
@@ -189,7 +188,6 @@ int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize,
.dev_free = snd_seq_device_dev_free,
.dev_register = snd_seq_device_dev_register,
.dev_disconnect = snd_seq_device_dev_disconnect,
- .dev_unregister = snd_seq_device_dev_unregister
};
if (result)
@@ -309,15 +307,6 @@ static int snd_seq_device_dev_disconnect(struct snd_device *device)
}
/*
- * unregister the existing device
- */
-static int snd_seq_device_dev_unregister(struct snd_device *device)
-{
- struct snd_seq_device *dev = device->device_data;
- return snd_seq_device_free(dev);
-}
-
-/*
* register device driver
* id = driver id
* entry = driver operators - duplicated to each instance
@@ -372,14 +361,19 @@ static struct ops_list * create_driver(char *id)
{
struct ops_list *ops;
- ops = kmalloc(sizeof(*ops), GFP_KERNEL);
+ ops = kzalloc(sizeof(*ops), GFP_KERNEL);
if (ops == NULL)
return ops;
- memset(ops, 0, sizeof(*ops));
/* set up driver entry */
strlcpy(ops->id, id, sizeof(ops->id));
mutex_init(&ops->reg_mutex);
+ /*
+ * The ->reg_mutex locking rules are per-driver, so we create
+ * separate per-driver lock classes:
+ */
+ lockdep_set_class(&ops->reg_mutex, (struct lock_class_key *)id);
+
ops->driver = DRIVER_EMPTY;
INIT_LIST_HEAD(&ops->dev_list);
/* lock this instance */
@@ -568,7 +562,7 @@ static void __exit alsa_seq_device_exit(void)
{
remove_drivers();
#ifdef CONFIG_PROC_FS
- snd_info_unregister(info_entry);
+ snd_info_free_entry(info_entry);
#endif
if (num_ops)
snd_printk(KERN_ERR "drivers not released (%d)\n", num_ops);
diff --git a/sound/core/seq/seq_info.c b/sound/core/seq/seq_info.c
index 142e9e6882c9..8a7fe5cca1c9 100644
--- a/sound/core/seq/seq_info.c
+++ b/sound/core/seq/seq_info.c
@@ -64,9 +64,9 @@ int __init snd_seq_info_init(void)
int __exit snd_seq_info_done(void)
{
- snd_info_unregister(queues_entry);
- snd_info_unregister(clients_entry);
- snd_info_unregister(timer_entry);
+ snd_info_free_entry(queues_entry);
+ snd_info_free_entry(clients_entry);
+ snd_info_free_entry(timer_entry);
return 0;
}
#endif
diff --git a/sound/core/seq/seq_memory.h b/sound/core/seq/seq_memory.h
index 39c60d9e1efc..63e91431a29f 100644
--- a/sound/core/seq/seq_memory.h
+++ b/sound/core/seq/seq_memory.h
@@ -31,7 +31,7 @@ struct snd_seq_event_cell {
struct snd_seq_event_cell *next; /* next cell */
};
-/* design note: the pool is a contigious block of memory, if we dynamicly
+/* design note: the pool is a contiguous block of memory, if we dynamicly
want to add additional cells to the pool be better store this in another
pool as we need to know the base address of the pool when releasing
memory. */
diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c
index 334579a9f268..8c64b58ff77b 100644
--- a/sound/core/seq/seq_ports.c
+++ b/sound/core/seq/seq_ports.c
@@ -322,10 +322,8 @@ int snd_seq_delete_all_ports(struct snd_seq_client *client)
mutex_lock(&client->ports_mutex);
write_lock_irqsave(&client->ports_lock, flags);
if (! list_empty(&client->ports_list_head)) {
- __list_add(&deleted_list,
- client->ports_list_head.prev,
- client->ports_list_head.next);
- INIT_LIST_HEAD(&client->ports_list_head);
+ list_add(&deleted_list, &client->ports_list_head);
+ list_del_init(&client->ports_list_head);
} else {
INIT_LIST_HEAD(&deleted_list);
}
@@ -516,7 +514,7 @@ int snd_seq_port_connect(struct snd_seq_client *connector,
atomic_set(&subs->ref_count, 2);
down_write(&src->list_mutex);
- down_write(&dest->list_mutex);
+ down_write_nested(&dest->list_mutex, SINGLE_DEPTH_NESTING);
exclusive = info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE ? 1 : 0;
err = -EBUSY;
@@ -589,7 +587,7 @@ int snd_seq_port_disconnect(struct snd_seq_client *connector,
unsigned long flags;
down_write(&src->list_mutex);
- down_write(&dest->list_mutex);
+ down_write_nested(&dest->list_mutex, SINGLE_DEPTH_NESTING);
/* look for the connection */
list_for_each(p, &src->list_head) {
diff --git a/sound/core/sgbuf.c b/sound/core/sgbuf.c
index 74745da9deb6..c30669f14ac0 100644
--- a/sound/core/sgbuf.c
+++ b/sound/core/sgbuf.c
@@ -19,7 +19,6 @@
*
*/
-#include <linux/config.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>
@@ -69,21 +68,18 @@ void *snd_malloc_sgbuf_pages(struct device *device,
dmab->area = NULL;
dmab->addr = 0;
- dmab->private_data = sgbuf = kmalloc(sizeof(*sgbuf), GFP_KERNEL);
+ dmab->private_data = sgbuf = kzalloc(sizeof(*sgbuf), GFP_KERNEL);
if (! sgbuf)
return NULL;
- memset(sgbuf, 0, sizeof(*sgbuf));
sgbuf->dev = device;
pages = snd_sgbuf_aligned_pages(size);
sgbuf->tblsize = sgbuf_align_table(pages);
- sgbuf->table = kmalloc(sizeof(*sgbuf->table) * sgbuf->tblsize, GFP_KERNEL);
+ sgbuf->table = kcalloc(sgbuf->tblsize, sizeof(*sgbuf->table), GFP_KERNEL);
if (! sgbuf->table)
goto _failed;
- memset(sgbuf->table, 0, sizeof(*sgbuf->table) * sgbuf->tblsize);
- sgbuf->page_table = kmalloc(sizeof(*sgbuf->page_table) * sgbuf->tblsize, GFP_KERNEL);
+ sgbuf->page_table = kcalloc(sgbuf->tblsize, sizeof(*sgbuf->page_table), GFP_KERNEL);
if (! sgbuf->page_table)
goto _failed;
- memset(sgbuf->page_table, 0, sizeof(*sgbuf->page_table) * sgbuf->tblsize);
/* allocate each page */
for (i = 0; i < pages; i++) {
diff --git a/sound/core/sound.c b/sound/core/sound.c
index cd862728346c..efa476c5210a 100644
--- a/sound/core/sound.c
+++ b/sound/core/sound.c
@@ -32,7 +32,6 @@
#include <sound/control.h>
#include <sound/initval.h>
#include <linux/kmod.h>
-#include <linux/devfs_fs_kernel.h>
#include <linux/mutex.h>
#define SNDRV_OS_MINORS 256
@@ -42,7 +41,6 @@ int snd_major;
EXPORT_SYMBOL(snd_major);
static int cards_limit = 1;
-static int device_mode = S_IFCHR | S_IRUGO | S_IWUGO;
MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
MODULE_DESCRIPTION("Advanced Linux Sound Architecture driver for soundcards.");
@@ -51,10 +49,6 @@ module_param(major, int, 0444);
MODULE_PARM_DESC(major, "Major # for sound driver.");
module_param(cards_limit, int, 0444);
MODULE_PARM_DESC(cards_limit, "Count of auto-loadable soundcards.");
-#ifdef CONFIG_DEVFS_FS
-module_param(device_mode, int, 0444);
-MODULE_PARM_DESC(device_mode, "Device file permission mask for devfs.");
-#endif
MODULE_ALIAS_CHARDEV_MAJOR(CONFIG_SND_MAJOR);
/* this one holds the actual max. card number currently available.
@@ -250,7 +244,7 @@ int snd_register_device(int type, struct snd_card *card, int dev,
struct device *device = NULL;
snd_assert(name, return -EINVAL);
- preg = kmalloc(sizeof(struct snd_minor) + strlen(name) + 1, GFP_KERNEL);
+ preg = kmalloc(sizeof *preg, GFP_KERNEL);
if (preg == NULL)
return -ENOMEM;
preg->type = type;
@@ -258,7 +252,6 @@ int snd_register_device(int type, struct snd_card *card, int dev,
preg->device = dev;
preg->f_ops = f_ops;
preg->private_data = private_data;
- strcpy(preg->name, name);
mutex_lock(&sound_mutex);
#ifdef CONFIG_SND_DYNAMIC_MINORS
minor = snd_find_free_minor();
@@ -273,11 +266,13 @@ int snd_register_device(int type, struct snd_card *card, int dev,
return minor;
}
snd_minors[minor] = preg;
- if (type != SNDRV_DEVICE_TYPE_CONTROL || preg->card >= cards_limit)
- devfs_mk_cdev(MKDEV(major, minor), S_IFCHR | device_mode, "snd/%s", name);
if (card)
device = card->dev;
- class_device_create(sound_class, NULL, MKDEV(major, minor), device, "%s", name);
+ preg->class_dev = class_device_create(sound_class, NULL,
+ MKDEV(major, minor),
+ device, "%s", name);
+ if (preg->class_dev)
+ class_set_devdata(preg->class_dev, private_data);
mutex_unlock(&sound_mutex);
return 0;
@@ -285,6 +280,24 @@ int snd_register_device(int type, struct snd_card *card, int dev,
EXPORT_SYMBOL(snd_register_device);
+/* find the matching minor record
+ * return the index of snd_minor, or -1 if not found
+ */
+static int find_snd_minor(int type, struct snd_card *card, int dev)
+{
+ int cardnum, minor;
+ struct snd_minor *mptr;
+
+ cardnum = card ? card->number : -1;
+ for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor)
+ if ((mptr = snd_minors[minor]) != NULL &&
+ mptr->type == type &&
+ mptr->card == cardnum &&
+ mptr->device == dev)
+ return minor;
+ return -1;
+}
+
/**
* snd_unregister_device - unregister the device on the given card
* @type: the device type, SNDRV_DEVICE_TYPE_XXX
@@ -298,35 +311,42 @@ EXPORT_SYMBOL(snd_register_device);
*/
int snd_unregister_device(int type, struct snd_card *card, int dev)
{
- int cardnum, minor;
- struct snd_minor *mptr;
+ int minor;
- cardnum = card ? card->number : -1;
mutex_lock(&sound_mutex);
- for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor)
- if ((mptr = snd_minors[minor]) != NULL &&
- mptr->type == type &&
- mptr->card == cardnum &&
- mptr->device == dev)
- break;
- if (minor == ARRAY_SIZE(snd_minors)) {
+ minor = find_snd_minor(type, card, dev);
+ if (minor < 0) {
mutex_unlock(&sound_mutex);
return -EINVAL;
}
- if (mptr->type != SNDRV_DEVICE_TYPE_CONTROL ||
- mptr->card >= cards_limit) /* created in sound.c */
- devfs_remove("snd/%s", mptr->name);
class_device_destroy(sound_class, MKDEV(major, minor));
+ kfree(snd_minors[minor]);
snd_minors[minor] = NULL;
mutex_unlock(&sound_mutex);
- kfree(mptr);
return 0;
}
EXPORT_SYMBOL(snd_unregister_device);
+int snd_add_device_sysfs_file(int type, struct snd_card *card, int dev,
+ const struct class_device_attribute *attr)
+{
+ int minor, ret = -EINVAL;
+ struct class_device *cdev;
+
+ mutex_lock(&sound_mutex);
+ minor = find_snd_minor(type, card, dev);
+ if (minor >= 0 && (cdev = snd_minors[minor]->class_dev) != NULL)
+ ret = class_device_create_file(cdev, attr);
+ mutex_unlock(&sound_mutex);
+ return ret;
+
+}
+
+EXPORT_SYMBOL(snd_add_device_sysfs_file);
+
#ifdef CONFIG_PROC_FS
/*
* INFO PART
@@ -399,8 +419,7 @@ int __init snd_minor_info_init(void)
int __exit snd_minor_info_done(void)
{
- if (snd_minor_info_entry)
- snd_info_unregister(snd_minor_info_entry);
+ snd_info_free_entry(snd_minor_info_entry);
return 0;
}
#endif /* CONFIG_PROC_FS */
@@ -411,24 +430,17 @@ int __exit snd_minor_info_done(void)
static int __init alsa_sound_init(void)
{
- short controlnum;
-
snd_major = major;
snd_ecards_limit = cards_limit;
- devfs_mk_dir("snd");
if (register_chrdev(major, "alsa", &snd_fops)) {
snd_printk(KERN_ERR "unable to register native major device number %d\n", major);
- devfs_remove("snd");
return -EIO;
}
if (snd_info_init() < 0) {
unregister_chrdev(major, "alsa");
- devfs_remove("snd");
return -ENOMEM;
}
snd_info_minor_register();
- for (controlnum = 0; controlnum < cards_limit; controlnum++)
- devfs_mk_cdev(MKDEV(major, controlnum<<5), S_IFCHR | device_mode, "snd/controlC%d", controlnum);
#ifndef MODULE
printk(KERN_INFO "Advanced Linux Sound Architecture Driver Version " CONFIG_SND_VERSION CONFIG_SND_DATE ".\n");
#endif
@@ -437,16 +449,10 @@ static int __init alsa_sound_init(void)
static void __exit alsa_sound_exit(void)
{
- short controlnum;
-
- for (controlnum = 0; controlnum < cards_limit; controlnum++)
- devfs_remove("snd/controlC%d", controlnum);
-
snd_info_minor_unregister();
snd_info_done();
if (unregister_chrdev(major, "alsa") != 0)
snd_printk(KERN_ERR "unable to unregister major device number %d\n", major);
- devfs_remove("snd");
}
module_init(alsa_sound_init)
diff --git a/sound/core/sound_oss.c b/sound/core/sound_oss.c
index 74f0fe5a1ba0..b2fc40aa520b 100644
--- a/sound/core/sound_oss.c
+++ b/sound/core/sound_oss.c
@@ -270,8 +270,7 @@ int __init snd_minor_info_oss_init(void)
int __exit snd_minor_info_oss_done(void)
{
- if (snd_minor_info_oss_entry)
- snd_info_unregister(snd_minor_info_oss_entry);
+ snd_info_free_entry(snd_minor_info_oss_entry);
return 0;
}
#endif /* CONFIG_PROC_FS */
diff --git a/sound/core/timer.c b/sound/core/timer.c
index 78199f58b93a..10a79aed33f8 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -88,7 +88,7 @@ static DEFINE_MUTEX(register_mutex);
static int snd_timer_free(struct snd_timer *timer);
static int snd_timer_dev_free(struct snd_device *device);
static int snd_timer_dev_register(struct snd_device *device);
-static int snd_timer_dev_unregister(struct snd_device *device);
+static int snd_timer_dev_disconnect(struct snd_device *device);
static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_left);
@@ -628,8 +628,9 @@ static void snd_timer_tasklet(unsigned long arg)
struct snd_timer_instance *ti;
struct list_head *p;
unsigned long resolution, ticks;
+ unsigned long flags;
- spin_lock(&timer->lock);
+ spin_lock_irqsave(&timer->lock, flags);
/* now process all callbacks */
while (!list_empty(&timer->sack_list_head)) {
p = timer->sack_list_head.next; /* get first item */
@@ -649,7 +650,7 @@ static void snd_timer_tasklet(unsigned long arg)
spin_lock(&timer->lock);
ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK;
}
- spin_unlock(&timer->lock);
+ spin_unlock_irqrestore(&timer->lock, flags);
}
/*
@@ -717,7 +718,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
}
}
if (timer->flags & SNDRV_TIMER_FLG_RESCHED)
- snd_timer_reschedule(timer, ticks_left);
+ snd_timer_reschedule(timer, timer->sticks);
if (timer->running) {
if (timer->hw.flags & SNDRV_TIMER_HW_STOP) {
timer->hw.stop(timer);
@@ -772,7 +773,7 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid,
static struct snd_device_ops ops = {
.dev_free = snd_timer_dev_free,
.dev_register = snd_timer_dev_register,
- .dev_unregister = snd_timer_dev_unregister
+ .dev_disconnect = snd_timer_dev_disconnect,
};
snd_assert(tid != NULL, return -EINVAL);
@@ -812,6 +813,21 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid,
static int snd_timer_free(struct snd_timer *timer)
{
snd_assert(timer != NULL, return -ENXIO);
+
+ mutex_lock(&register_mutex);
+ if (! list_empty(&timer->open_list_head)) {
+ struct list_head *p, *n;
+ struct snd_timer_instance *ti;
+ snd_printk(KERN_WARNING "timer %p is busy?\n", timer);
+ list_for_each_safe(p, n, &timer->open_list_head) {
+ list_del_init(p);
+ ti = list_entry(p, struct snd_timer_instance, open_list);
+ ti->timer = NULL;
+ }
+ }
+ list_del(&timer->device_list);
+ mutex_unlock(&register_mutex);
+
if (timer->private_free)
timer->private_free(timer);
kfree(timer);
@@ -866,30 +882,13 @@ static int snd_timer_dev_register(struct snd_device *dev)
return 0;
}
-static int snd_timer_unregister(struct snd_timer *timer)
+static int snd_timer_dev_disconnect(struct snd_device *device)
{
- struct list_head *p, *n;
- struct snd_timer_instance *ti;
-
- snd_assert(timer != NULL, return -ENXIO);
+ struct snd_timer *timer = device->device_data;
mutex_lock(&register_mutex);
- if (! list_empty(&timer->open_list_head)) {
- snd_printk(KERN_WARNING "timer 0x%lx is busy?\n", (long)timer);
- list_for_each_safe(p, n, &timer->open_list_head) {
- list_del_init(p);
- ti = list_entry(p, struct snd_timer_instance, open_list);
- ti->timer = NULL;
- }
- }
- list_del(&timer->device_list);
+ list_del_init(&timer->device_list);
mutex_unlock(&register_mutex);
- return snd_timer_free(timer);
-}
-
-static int snd_timer_dev_unregister(struct snd_device *device)
-{
- struct snd_timer *timer = device->device_data;
- return snd_timer_unregister(timer);
+ return 0;
}
void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstamp)
@@ -954,18 +953,12 @@ int snd_timer_global_register(struct snd_timer *timer)
return snd_timer_dev_register(&dev);
}
-int snd_timer_global_unregister(struct snd_timer *timer)
-{
- return snd_timer_unregister(timer);
-}
-
/*
* System timer
*/
struct snd_timer_system_private {
struct timer_list tlist;
- struct timer * timer;
unsigned long last_expires;
unsigned long last_jiffies;
unsigned long correction;
@@ -977,7 +970,7 @@ static void snd_timer_s_function(unsigned long data)
struct snd_timer_system_private *priv = timer->private_data;
unsigned long jiff = jiffies;
if (time_after(jiff, priv->last_expires))
- priv->correction = (long)jiff - (long)priv->last_expires;
+ priv->correction += (long)jiff - (long)priv->last_expires;
snd_timer_interrupt(timer, (long)jiff - (long)priv->last_jiffies);
}
@@ -993,7 +986,7 @@ static int snd_timer_s_start(struct snd_timer * timer)
njiff++;
} else {
njiff += timer->sticks - priv->correction;
- priv->correction -= timer->sticks;
+ priv->correction = 0;
}
priv->last_expires = priv->tlist.expires = njiff;
add_timer(&priv->tlist);
@@ -1012,6 +1005,7 @@ static int snd_timer_s_stop(struct snd_timer * timer)
timer->sticks = priv->last_expires - jiff;
else
timer->sticks = 1;
+ priv->correction = 0;
return 0;
}
@@ -1125,7 +1119,7 @@ static void __init snd_timer_proc_init(void)
static void __exit snd_timer_proc_done(void)
{
- snd_info_unregister(snd_timer_proc_entry);
+ snd_info_free_entry(snd_timer_proc_entry);
}
#else /* !CONFIG_PROC_FS */
#define snd_timer_proc_init()
@@ -1981,7 +1975,7 @@ static void __exit alsa_timer_exit(void)
/* unregister the system timer */
list_for_each_safe(p, n, &snd_timer_list) {
struct snd_timer *timer = list_entry(p, struct snd_timer, device_list);
- snd_timer_unregister(timer);
+ snd_timer_free(timer);
}
snd_timer_proc_done();
#ifdef SNDRV_OSS_INFO_DEV_TIMERS
@@ -2004,5 +1998,4 @@ EXPORT_SYMBOL(snd_timer_notify);
EXPORT_SYMBOL(snd_timer_global_new);
EXPORT_SYMBOL(snd_timer_global_free);
EXPORT_SYMBOL(snd_timer_global_register);
-EXPORT_SYMBOL(snd_timer_global_unregister);
EXPORT_SYMBOL(snd_timer_interrupt);
diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig
index 395c4ef52ac9..7971285dfd5b 100644
--- a/sound/drivers/Kconfig
+++ b/sound/drivers/Kconfig
@@ -73,6 +73,19 @@ config SND_MTPAV
To compile this driver as a module, choose M here: the module
will be called snd-mtpav.
+config SND_MTS64
+ tristate "ESI Miditerminal 4140 driver"
+ depends on SND && PARPORT
+ select SND_RAWMIDI
+ help
+ The ESI Miditerminal 4140 is a 4 In 4 Out MIDI Interface with
+ additional SMPTE Timecode capabilities for the parallel port.
+
+ Say 'Y' to include support for this device.
+
+ To compile this driver as a module, chose 'M' here: the module
+ will be called snd-mts64.
+
config SND_SERIAL_U16550
tristate "UART16550 serial MIDI driver"
depends on SND
diff --git a/sound/drivers/Makefile b/sound/drivers/Makefile
index cb98c3d662be..c9bad6d67e73 100644
--- a/sound/drivers/Makefile
+++ b/sound/drivers/Makefile
@@ -5,6 +5,7 @@
snd-dummy-objs := dummy.o
snd-mtpav-objs := mtpav.o
+snd-mts64-objs := mts64.o
snd-serial-u16550-objs := serial-u16550.o
snd-virmidi-objs := virmidi.o
@@ -13,5 +14,6 @@ obj-$(CONFIG_SND_DUMMY) += snd-dummy.o
obj-$(CONFIG_SND_VIRMIDI) += snd-virmidi.o
obj-$(CONFIG_SND_SERIAL_U16550) += snd-serial-u16550.o
obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o
+obj-$(CONFIG_SND_MTS64) += snd-mts64.o
obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/
diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c
index ffeafaf2ecca..42001efa9f3e 100644
--- a/sound/drivers/dummy.c
+++ b/sound/drivers/dummy.c
@@ -29,6 +29,7 @@
#include <linux/moduleparam.h>
#include <sound/core.h>
#include <sound/control.h>
+#include <sound/tlv.h>
#include <sound/pcm.h>
#include <sound/rawmidi.h>
#include <sound/initval.h>
@@ -285,7 +286,7 @@ static struct snd_pcm_hardware snd_card_dummy_playback =
.channels_max = USE_CHANNELS_MAX,
.buffer_bytes_max = MAX_BUFFER_SIZE,
.period_bytes_min = 64,
- .period_bytes_max = MAX_BUFFER_SIZE,
+ .period_bytes_max = MAX_PERIOD_SIZE,
.periods_min = USE_PERIODS_MIN,
.periods_max = USE_PERIODS_MAX,
.fifo_size = 0,
@@ -443,10 +444,13 @@ static int __init snd_card_dummy_pcm(struct snd_dummy *dummy, int device, int su
}
#define DUMMY_VOLUME(xname, xindex, addr) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
+ .name = xname, .index = xindex, \
.info = snd_dummy_volume_info, \
.get = snd_dummy_volume_get, .put = snd_dummy_volume_put, \
- .private_value = addr }
+ .private_value = addr, \
+ .tlv = { .p = db_scale_dummy } }
static int snd_dummy_volume_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
@@ -497,6 +501,8 @@ static int snd_dummy_volume_put(struct snd_kcontrol *kcontrol,
return change;
}
+static DECLARE_TLV_DB_SCALE(db_scale_dummy, -4500, 30, 0);
+
#define DUMMY_CAPSRC(xname, xindex, addr) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
.info = snd_dummy_capsrc_info, \
@@ -547,13 +553,13 @@ static struct snd_kcontrol_new snd_dummy_controls[] = {
DUMMY_VOLUME("Master Volume", 0, MIXER_ADDR_MASTER),
DUMMY_CAPSRC("Master Capture Switch", 0, MIXER_ADDR_MASTER),
DUMMY_VOLUME("Synth Volume", 0, MIXER_ADDR_SYNTH),
-DUMMY_CAPSRC("Synth Capture Switch", 0, MIXER_ADDR_MASTER),
+DUMMY_CAPSRC("Synth Capture Switch", 0, MIXER_ADDR_SYNTH),
DUMMY_VOLUME("Line Volume", 0, MIXER_ADDR_LINE),
-DUMMY_CAPSRC("Line Capture Switch", 0, MIXER_ADDR_MASTER),
+DUMMY_CAPSRC("Line Capture Switch", 0, MIXER_ADDR_LINE),
DUMMY_VOLUME("Mic Volume", 0, MIXER_ADDR_MIC),
-DUMMY_CAPSRC("Mic Capture Switch", 0, MIXER_ADDR_MASTER),
+DUMMY_CAPSRC("Mic Capture Switch", 0, MIXER_ADDR_MIC),
DUMMY_VOLUME("CD Volume", 0, MIXER_ADDR_CD),
-DUMMY_CAPSRC("CD Capture Switch", 0, MIXER_ADDR_MASTER)
+DUMMY_CAPSRC("CD Capture Switch", 0, MIXER_ADDR_CD)
};
static int __init snd_card_dummy_new_mixer(struct snd_dummy *dummy)
diff --git a/sound/drivers/mpu401/mpu401.c b/sound/drivers/mpu401/mpu401.c
index d3cbbb047582..2de181ad0b05 100644
--- a/sound/drivers/mpu401/mpu401.c
+++ b/sound/drivers/mpu401/mpu401.c
@@ -83,7 +83,7 @@ static int snd_mpu401_create(int dev, struct snd_card **rcard)
if ((err = snd_mpu401_uart_new(card, 0,
MPU401_HW_MPU401,
port[dev], 0,
- irq[dev], irq[dev] >= 0 ? SA_INTERRUPT : 0, NULL)) < 0) {
+ irq[dev], irq[dev] >= 0 ? IRQF_DISABLED : 0, NULL)) < 0) {
printk(KERN_ERR "MPU401 not detected at 0x%lx\n", port[dev]);
goto _err;
}
@@ -160,8 +160,9 @@ static int __devinit snd_mpu401_pnp(int dev, struct pnp_dev *device,
return -ENODEV;
}
if (pnp_port_len(device, 0) < IO_EXTENT) {
- snd_printk(KERN_ERR "PnP port length is %ld, expected %d\n",
- pnp_port_len(device, 0), IO_EXTENT);
+ snd_printk(KERN_ERR "PnP port length is %llu, expected %d\n",
+ (unsigned long long)pnp_port_len(device, 0),
+ IO_EXTENT);
return -ENODEV;
}
port[dev] = pnp_port_start(device, 0);
@@ -210,7 +211,7 @@ static void __devexit snd_mpu401_pnp_remove(struct pnp_dev *dev)
struct snd_card *card = (struct snd_card *) pnp_get_drvdata(dev);
snd_card_disconnect(card);
- snd_card_free_in_thread(card);
+ snd_card_free_when_closed(card);
}
static struct pnp_driver snd_mpu401_pnp_driver = {
diff --git a/sound/drivers/mtpav.c b/sound/drivers/mtpav.c
index 474eed06e70f..e064d6c5685b 100644
--- a/sound/drivers/mtpav.c
+++ b/sound/drivers/mtpav.c
@@ -590,7 +590,7 @@ static int __init snd_mtpav_get_ISA(struct mtpav * mcard)
return -EBUSY;
}
mcard->port = port;
- if (request_irq(irq, snd_mtpav_irqh, SA_INTERRUPT, "MOTU MTPAV", mcard)) {
+ if (request_irq(irq, snd_mtpav_irqh, IRQF_DISABLED, "MOTU MTPAV", mcard)) {
snd_printk("MTVAP IRQ %d busy\n", irq);
return -EBUSY;
}
diff --git a/sound/drivers/mts64.c b/sound/drivers/mts64.c
new file mode 100644
index 000000000000..ab8d4effcf9e
--- /dev/null
+++ b/sound/drivers/mts64.c
@@ -0,0 +1,1091 @@
+/*
+ * ALSA Driver for Ego Systems Inc. (ESI) Miditerminal 4140
+ * Copyright (c) 2006 by Matthias König <mk@phasorlab.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/parport.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/rawmidi.h>
+#include <sound/control.h>
+
+#define CARD_NAME "Miditerminal 4140"
+#define DRIVER_NAME "MTS64"
+#define PLATFORM_DRIVER "snd_mts64"
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+static struct platform_device *platform_devices[SNDRV_CARDS];
+static int device_count;
+
+module_param_array(index, int, NULL, S_IRUGO);
+MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
+module_param_array(id, charp, NULL, S_IRUGO);
+MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
+module_param_array(enable, bool, NULL, S_IRUGO);
+MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
+
+MODULE_AUTHOR("Matthias Koenig <mk@phasorlab.de>");
+MODULE_DESCRIPTION("ESI Miditerminal 4140");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{ESI,Miditerminal 4140}}");
+
+/*********************************************************************
+ * Chip specific
+ *********************************************************************/
+#define MTS64_NUM_INPUT_PORTS 5
+#define MTS64_NUM_OUTPUT_PORTS 4
+#define MTS64_SMPTE_SUBSTREAM 4
+
+struct mts64 {
+ spinlock_t lock;
+ struct snd_card *card;
+ struct snd_rawmidi *rmidi;
+ struct pardevice *pardev;
+ int pardev_claimed;
+
+ int open_count;
+ int current_midi_output_port;
+ int current_midi_input_port;
+ u8 mode[MTS64_NUM_INPUT_PORTS];
+ struct snd_rawmidi_substream *midi_input_substream[MTS64_NUM_INPUT_PORTS];
+ int smpte_switch;
+ u8 time[4]; /* [0]=hh, [1]=mm, [2]=ss, [3]=ff */
+ u8 fps;
+};
+
+static int snd_mts64_free(struct mts64 *mts)
+{
+ kfree(mts);
+ return 0;
+}
+
+static int __devinit snd_mts64_create(struct snd_card *card,
+ struct pardevice *pardev,
+ struct mts64 **rchip)
+{
+ struct mts64 *mts;
+
+ *rchip = NULL;
+
+ mts = kzalloc(sizeof(struct mts64), GFP_KERNEL);
+ if (mts == NULL)
+ return -ENOMEM;
+
+ /* Init chip specific data */
+ spin_lock_init(&mts->lock);
+ mts->card = card;
+ mts->pardev = pardev;
+ mts->current_midi_output_port = -1;
+ mts->current_midi_input_port = -1;
+
+ *rchip = mts;
+
+ return 0;
+}
+
+/*********************************************************************
+ * HW register related constants
+ *********************************************************************/
+
+/* Status Bits */
+#define MTS64_STAT_BSY 0x80
+#define MTS64_STAT_BIT_SET 0x20 /* readout process, bit is set */
+#define MTS64_STAT_PORT 0x10 /* read byte is a port number */
+
+/* Control Bits */
+#define MTS64_CTL_READOUT 0x08 /* enable readout */
+#define MTS64_CTL_WRITE_CMD 0x06
+#define MTS64_CTL_WRITE_DATA 0x02
+#define MTS64_CTL_STROBE 0x01
+
+/* Command */
+#define MTS64_CMD_RESET 0xfe
+#define MTS64_CMD_PROBE 0x8f /* Used in probing procedure */
+#define MTS64_CMD_SMPTE_SET_TIME 0xe8
+#define MTS64_CMD_SMPTE_SET_FPS 0xee
+#define MTS64_CMD_SMPTE_STOP 0xef
+#define MTS64_CMD_SMPTE_FPS_24 0xe3
+#define MTS64_CMD_SMPTE_FPS_25 0xe2
+#define MTS64_CMD_SMPTE_FPS_2997 0xe4
+#define MTS64_CMD_SMPTE_FPS_30D 0xe1
+#define MTS64_CMD_SMPTE_FPS_30 0xe0
+#define MTS64_CMD_COM_OPEN 0xf8 /* setting the communication mode */
+#define MTS64_CMD_COM_CLOSE1 0xff /* clearing communication mode */
+#define MTS64_CMD_COM_CLOSE2 0xf5
+
+/*********************************************************************
+ * Hardware specific functions
+ *********************************************************************/
+static void mts64_enable_readout(struct parport *p);
+static void mts64_disable_readout(struct parport *p);
+static int mts64_device_ready(struct parport *p);
+static int mts64_device_init(struct parport *p);
+static int mts64_device_open(struct mts64 *mts);
+static int mts64_device_close(struct mts64 *mts);
+static u8 mts64_map_midi_input(u8 c);
+static int mts64_probe(struct parport *p);
+static u16 mts64_read(struct parport *p);
+static u8 mts64_read_char(struct parport *p);
+static void mts64_smpte_start(struct parport *p,
+ u8 hours, u8 minutes,
+ u8 seconds, u8 frames,
+ u8 idx);
+static void mts64_smpte_stop(struct parport *p);
+static void mts64_write_command(struct parport *p, u8 c);
+static void mts64_write_data(struct parport *p, u8 c);
+static void mts64_write_midi(struct mts64 *mts, u8 c, int midiport);
+
+
+/* Enables the readout procedure
+ *
+ * Before we can read a midi byte from the device, we have to set
+ * bit 3 of control port.
+ */
+static void mts64_enable_readout(struct parport *p)
+{
+ u8 c;
+
+ c = parport_read_control(p);
+ c |= MTS64_CTL_READOUT;
+ parport_write_control(p, c);
+}
+
+/* Disables readout
+ *
+ * Readout is disabled by clearing bit 3 of control
+ */
+static void mts64_disable_readout(struct parport *p)
+{
+ u8 c;
+
+ c = parport_read_control(p);
+ c &= ~MTS64_CTL_READOUT;
+ parport_write_control(p, c);
+}
+
+/* waits for device ready
+ *
+ * Checks if BUSY (Bit 7 of status) is clear
+ * 1 device ready
+ * 0 failure
+ */
+static int mts64_device_ready(struct parport *p)
+{
+ int i;
+ u8 c;
+
+ for (i = 0; i < 0xffff; ++i) {
+ c = parport_read_status(p);
+ c &= MTS64_STAT_BSY;
+ if (c != 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Init device (LED blinking startup magic)
+ *
+ * Returns:
+ * 0 init ok
+ * -EIO failure
+ */
+static int __devinit mts64_device_init(struct parport *p)
+{
+ int i;
+
+ mts64_write_command(p, MTS64_CMD_RESET);
+
+ for (i = 0; i < 64; ++i) {
+ msleep(100);
+
+ if (mts64_probe(p) == 0) {
+ /* success */
+ mts64_disable_readout(p);
+ return 0;
+ }
+ }
+ mts64_disable_readout(p);
+
+ return -EIO;
+}
+
+/*
+ * Opens the device (set communication mode)
+ */
+static int mts64_device_open(struct mts64 *mts)
+{
+ int i;
+ struct parport *p = mts->pardev->port;
+
+ for (i = 0; i < 5; ++i)
+ mts64_write_command(p, MTS64_CMD_COM_OPEN);
+
+ return 0;
+}
+
+/*
+ * Close device (clear communication mode)
+ */
+static int mts64_device_close(struct mts64 *mts)
+{
+ int i;
+ struct parport *p = mts->pardev->port;
+
+ for (i = 0; i < 5; ++i) {
+ mts64_write_command(p, MTS64_CMD_COM_CLOSE1);
+ mts64_write_command(p, MTS64_CMD_COM_CLOSE2);
+ }
+
+ return 0;
+}
+
+/* map hardware port to substream number
+ *
+ * When reading a byte from the device, the device tells us
+ * on what port the byte is. This HW port has to be mapped to
+ * the midiport (substream number).
+ * substream 0-3 are Midiports 1-4
+ * substream 4 is SMPTE Timecode
+ * The mapping is done by the table:
+ * HW | 0 | 1 | 2 | 3 | 4
+ * SW | 0 | 1 | 4 | 2 | 3
+ */
+static u8 mts64_map_midi_input(u8 c)
+{
+ static u8 map[] = { 0, 1, 4, 2, 3 };
+
+ return map[c];
+}
+
+
+/* Probe parport for device
+ *
+ * Do we have a Miditerminal 4140 on parport?
+ * Returns:
+ * 0 device found
+ * -ENODEV no device
+ */
+static int __devinit mts64_probe(struct parport *p)
+{
+ u8 c;
+
+ mts64_smpte_stop(p);
+ mts64_write_command(p, MTS64_CMD_PROBE);
+
+ msleep(50);
+
+ c = mts64_read(p);
+
+ c &= 0x00ff;
+ if (c != MTS64_CMD_PROBE)
+ return -ENODEV;
+ else
+ return 0;
+
+}
+
+/* Read byte incl. status from device
+ *
+ * Returns:
+ * data in lower 8 bits and status in upper 8 bits
+ */
+static u16 mts64_read(struct parport *p)
+{
+ u8 data, status;
+
+ mts64_device_ready(p);
+ mts64_enable_readout(p);
+ status = parport_read_status(p);
+ data = mts64_read_char(p);
+ mts64_disable_readout(p);
+
+ return (status << 8) | data;
+}
+
+/* Read a byte from device
+ *
+ * Note, that readout mode has to be enabled.
+ * readout procedure is as follows:
+ * - Write number of the Bit to read to DATA
+ * - Read STATUS
+ * - Bit 5 of STATUS indicates if Bit is set
+ *
+ * Returns:
+ * Byte read from device
+ */
+static u8 mts64_read_char(struct parport *p)
+{
+ u8 c = 0;
+ u8 status;
+ u8 i;
+
+ for (i = 0; i < 8; ++i) {
+ parport_write_data(p, i);
+ c >>= 1;
+ status = parport_read_status(p);
+ if (status & MTS64_STAT_BIT_SET)
+ c |= 0x80;
+ }
+
+ return c;
+}
+
+/* Starts SMPTE Timecode generation
+ *
+ * The device creates SMPTE Timecode by hardware.
+ * 0 24 fps
+ * 1 25 fps
+ * 2 29.97 fps
+ * 3 30 fps (Drop-frame)
+ * 4 30 fps
+ */
+static void mts64_smpte_start(struct parport *p,
+ u8 hours, u8 minutes,
+ u8 seconds, u8 frames,
+ u8 idx)
+{
+ static u8 fps[5] = { MTS64_CMD_SMPTE_FPS_24,
+ MTS64_CMD_SMPTE_FPS_25,
+ MTS64_CMD_SMPTE_FPS_2997,
+ MTS64_CMD_SMPTE_FPS_30D,
+ MTS64_CMD_SMPTE_FPS_30 };
+
+ mts64_write_command(p, MTS64_CMD_SMPTE_SET_TIME);
+ mts64_write_command(p, frames);
+ mts64_write_command(p, seconds);
+ mts64_write_command(p, minutes);
+ mts64_write_command(p, hours);
+
+ mts64_write_command(p, MTS64_CMD_SMPTE_SET_FPS);
+ mts64_write_command(p, fps[idx]);
+}
+
+/* Stops SMPTE Timecode generation
+ */
+static void mts64_smpte_stop(struct parport *p)
+{
+ mts64_write_command(p, MTS64_CMD_SMPTE_STOP);
+}
+
+/* Write a command byte to device
+ */
+static void mts64_write_command(struct parport *p, u8 c)
+{
+ mts64_device_ready(p);
+
+ parport_write_data(p, c);
+
+ parport_write_control(p, MTS64_CTL_WRITE_CMD);
+ parport_write_control(p, MTS64_CTL_WRITE_CMD | MTS64_CTL_STROBE);
+ parport_write_control(p, MTS64_CTL_WRITE_CMD);
+}
+
+/* Write a data byte to device
+ */
+static void mts64_write_data(struct parport *p, u8 c)
+{
+ mts64_device_ready(p);
+
+ parport_write_data(p, c);
+
+ parport_write_control(p, MTS64_CTL_WRITE_DATA);
+ parport_write_control(p, MTS64_CTL_WRITE_DATA | MTS64_CTL_STROBE);
+ parport_write_control(p, MTS64_CTL_WRITE_DATA);
+}
+
+/* Write a MIDI byte to midiport
+ *
+ * midiport ranges from 0-3 and maps to Ports 1-4
+ * assumptions: communication mode is on
+ */
+static void mts64_write_midi(struct mts64 *mts, u8 c,
+ int midiport)
+{
+ struct parport *p = mts->pardev->port;
+
+ /* check current midiport */
+ if (mts->current_midi_output_port != midiport)
+ mts64_write_command(p, midiport);
+
+ /* write midi byte */
+ mts64_write_data(p, c);
+}
+
+/*********************************************************************
+ * Control elements
+ *********************************************************************/
+
+/* SMPTE Switch */
+static int snd_mts64_ctl_smpte_switch_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_mts64_ctl_smpte_switch_get(struct snd_kcontrol* kctl,
+ struct snd_ctl_elem_value *uctl)
+{
+ struct mts64 *mts = snd_kcontrol_chip(kctl);
+
+ spin_lock_irq(&mts->lock);
+ uctl->value.integer.value[0] = mts->smpte_switch;
+ spin_unlock_irq(&mts->lock);
+
+ return 0;
+}
+
+/* smpte_switch is not accessed from IRQ handler, so we just need
+ to protect the HW access */
+static int snd_mts64_ctl_smpte_switch_put(struct snd_kcontrol* kctl,
+ struct snd_ctl_elem_value *uctl)
+{
+ struct mts64 *mts = snd_kcontrol_chip(kctl);
+ int changed = 0;
+
+ spin_lock_irq(&mts->lock);
+ if (mts->smpte_switch == uctl->value.integer.value[0])
+ goto __out;
+
+ changed = 1;
+ mts->smpte_switch = uctl->value.integer.value[0];
+ if (mts->smpte_switch) {
+ mts64_smpte_start(mts->pardev->port,
+ mts->time[0], mts->time[1],
+ mts->time[2], mts->time[3],
+ mts->fps);
+ } else {
+ mts64_smpte_stop(mts->pardev->port);
+ }
+__out:
+ spin_unlock_irq(&mts->lock);
+ return changed;
+}
+
+static struct snd_kcontrol_new mts64_ctl_smpte_switch __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI,
+ .name = "SMPTE Playback Switch",
+ .index = 0,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .private_value = 0,
+ .info = snd_mts64_ctl_smpte_switch_info,
+ .get = snd_mts64_ctl_smpte_switch_get,
+ .put = snd_mts64_ctl_smpte_switch_put
+};
+
+/* Time */
+static int snd_mts64_ctl_smpte_time_h_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 23;
+ return 0;
+}
+
+static int snd_mts64_ctl_smpte_time_f_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 99;
+ return 0;
+}
+
+static int snd_mts64_ctl_smpte_time_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 59;
+ return 0;
+}
+
+static int snd_mts64_ctl_smpte_time_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uctl)
+{
+ struct mts64 *mts = snd_kcontrol_chip(kctl);
+ int idx = kctl->private_value;
+
+ spin_lock_irq(&mts->lock);
+ uctl->value.integer.value[0] = mts->time[idx];
+ spin_unlock_irq(&mts->lock);
+
+ return 0;
+}
+
+static int snd_mts64_ctl_smpte_time_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uctl)
+{
+ struct mts64 *mts = snd_kcontrol_chip(kctl);
+ int idx = kctl->private_value;
+ int changed = 0;
+
+ spin_lock_irq(&mts->lock);
+ if (mts->time[idx] != uctl->value.integer.value[0]) {
+ changed = 1;
+ mts->time[idx] = uctl->value.integer.value[0];
+ }
+ spin_unlock_irq(&mts->lock);
+
+ return changed;
+}
+
+static struct snd_kcontrol_new mts64_ctl_smpte_time_hours __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI,
+ .name = "SMPTE Time Hours",
+ .index = 0,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .private_value = 0,
+ .info = snd_mts64_ctl_smpte_time_h_info,
+ .get = snd_mts64_ctl_smpte_time_get,
+ .put = snd_mts64_ctl_smpte_time_put
+};
+
+static struct snd_kcontrol_new mts64_ctl_smpte_time_minutes __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI,
+ .name = "SMPTE Time Minutes",
+ .index = 0,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .private_value = 1,
+ .info = snd_mts64_ctl_smpte_time_info,
+ .get = snd_mts64_ctl_smpte_time_get,
+ .put = snd_mts64_ctl_smpte_time_put
+};
+
+static struct snd_kcontrol_new mts64_ctl_smpte_time_seconds __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI,
+ .name = "SMPTE Time Seconds",
+ .index = 0,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .private_value = 2,
+ .info = snd_mts64_ctl_smpte_time_info,
+ .get = snd_mts64_ctl_smpte_time_get,
+ .put = snd_mts64_ctl_smpte_time_put
+};
+
+static struct snd_kcontrol_new mts64_ctl_smpte_time_frames __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI,
+ .name = "SMPTE Time Frames",
+ .index = 0,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .private_value = 3,
+ .info = snd_mts64_ctl_smpte_time_f_info,
+ .get = snd_mts64_ctl_smpte_time_get,
+ .put = snd_mts64_ctl_smpte_time_put
+};
+
+/* FPS */
+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" };
+
+ 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;
+}
+
+static int snd_mts64_ctl_smpte_fps_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uctl)
+{
+ struct mts64 *mts = snd_kcontrol_chip(kctl);
+
+ spin_lock_irq(&mts->lock);
+ uctl->value.enumerated.item[0] = mts->fps;
+ spin_unlock_irq(&mts->lock);
+
+ return 0;
+}
+
+static int snd_mts64_ctl_smpte_fps_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uctl)
+{
+ struct mts64 *mts = snd_kcontrol_chip(kctl);
+ int changed = 0;
+
+ spin_lock_irq(&mts->lock);
+ if (mts->fps != uctl->value.enumerated.item[0]) {
+ changed = 1;
+ mts->fps = uctl->value.enumerated.item[0];
+ }
+ spin_unlock_irq(&mts->lock);
+
+ return changed;
+}
+
+static struct snd_kcontrol_new mts64_ctl_smpte_fps __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI,
+ .name = "SMPTE Fps",
+ .index = 0,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .private_value = 0,
+ .info = snd_mts64_ctl_smpte_fps_info,
+ .get = snd_mts64_ctl_smpte_fps_get,
+ .put = snd_mts64_ctl_smpte_fps_put
+};
+
+
+static int __devinit snd_mts64_ctl_create(struct snd_card *card,
+ struct mts64 *mts)
+{
+ int err, i;
+ static struct snd_kcontrol_new *control[] = {
+ &mts64_ctl_smpte_switch,
+ &mts64_ctl_smpte_time_hours,
+ &mts64_ctl_smpte_time_minutes,
+ &mts64_ctl_smpte_time_seconds,
+ &mts64_ctl_smpte_time_frames,
+ &mts64_ctl_smpte_fps,
+ NULL };
+
+ for (i = 0; control[i]; ++i) {
+ err = snd_ctl_add(card, snd_ctl_new1(control[i], mts));
+ if (err < 0) {
+ snd_printd("Cannot create control: %s\n",
+ control[i]->name);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+/*********************************************************************
+ * Rawmidi
+ *********************************************************************/
+#define MTS64_MODE_INPUT_TRIGGERED 0x01
+
+static int snd_mts64_rawmidi_open(struct snd_rawmidi_substream *substream)
+{
+ struct mts64 *mts = substream->rmidi->private_data;
+
+ if (mts->open_count == 0) {
+ /* We don't need a spinlock here, because this is just called
+ if the device has not been opened before.
+ So there aren't any IRQs from the device */
+ mts64_device_open(mts);
+
+ msleep(50);
+ }
+ ++(mts->open_count);
+
+ return 0;
+}
+
+static int snd_mts64_rawmidi_close(struct snd_rawmidi_substream *substream)
+{
+ struct mts64 *mts = substream->rmidi->private_data;
+ unsigned long flags;
+
+ --(mts->open_count);
+ if (mts->open_count == 0) {
+ /* We need the spinlock_irqsave here because we can still
+ have IRQs at this point */
+ spin_lock_irqsave(&mts->lock, flags);
+ mts64_device_close(mts);
+ spin_unlock_irqrestore(&mts->lock, flags);
+
+ msleep(500);
+
+ } else if (mts->open_count < 0)
+ mts->open_count = 0;
+
+ return 0;
+}
+
+static void snd_mts64_rawmidi_output_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct mts64 *mts = substream->rmidi->private_data;
+ u8 data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mts->lock, flags);
+ while (snd_rawmidi_transmit_peek(substream, &data, 1) == 1) {
+ mts64_write_midi(mts, data, substream->number+1);
+ snd_rawmidi_transmit_ack(substream, 1);
+ }
+ spin_unlock_irqrestore(&mts->lock, flags);
+}
+
+static void snd_mts64_rawmidi_input_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct mts64 *mts = substream->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mts->lock, flags);
+ if (up)
+ mts->mode[substream->number] |= MTS64_MODE_INPUT_TRIGGERED;
+ else
+ mts->mode[substream->number] &= ~MTS64_MODE_INPUT_TRIGGERED;
+
+ spin_unlock_irqrestore(&mts->lock, flags);
+}
+
+static struct snd_rawmidi_ops snd_mts64_rawmidi_output_ops = {
+ .open = snd_mts64_rawmidi_open,
+ .close = snd_mts64_rawmidi_close,
+ .trigger = snd_mts64_rawmidi_output_trigger
+};
+
+static struct snd_rawmidi_ops snd_mts64_rawmidi_input_ops = {
+ .open = snd_mts64_rawmidi_open,
+ .close = snd_mts64_rawmidi_close,
+ .trigger = snd_mts64_rawmidi_input_trigger
+};
+
+/* Create and initialize the rawmidi component */
+static int __devinit snd_mts64_rawmidi_create(struct snd_card *card)
+{
+ struct mts64 *mts = card->private_data;
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_substream *substream;
+ struct list_head *list;
+ int err;
+
+ err = snd_rawmidi_new(card, CARD_NAME, 0,
+ MTS64_NUM_OUTPUT_PORTS,
+ MTS64_NUM_INPUT_PORTS,
+ &rmidi);
+ if (err < 0)
+ return err;
+
+ rmidi->private_data = mts;
+ strcpy(rmidi->name, CARD_NAME);
+ rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
+ SNDRV_RAWMIDI_INFO_INPUT |
+ SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ mts->rmidi = rmidi;
+
+ /* register rawmidi ops */
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &snd_mts64_rawmidi_output_ops);
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &snd_mts64_rawmidi_input_ops);
+
+ /* name substreams */
+ /* output */
+ list_for_each(list,
+ &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) {
+ substream = list_entry(list, struct snd_rawmidi_substream, list);
+ sprintf(substream->name,
+ "Miditerminal %d", substream->number+1);
+ }
+ /* input */
+ list_for_each(list,
+ &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams) {
+ substream = list_entry(list, struct snd_rawmidi_substream, list);
+ mts->midi_input_substream[substream->number] = substream;
+ switch(substream->number) {
+ case MTS64_SMPTE_SUBSTREAM:
+ strcpy(substream->name, "Miditerminal SMPTE");
+ break;
+ default:
+ sprintf(substream->name,
+ "Miditerminal %d", substream->number+1);
+ }
+ }
+
+ /* controls */
+ err = snd_mts64_ctl_create(card, mts);
+
+ return err;
+}
+
+/*********************************************************************
+ * parport stuff
+ *********************************************************************/
+static void snd_mts64_interrupt(int irq, void *private, struct pt_regs *r)
+{
+ struct mts64 *mts = ((struct snd_card*)private)->private_data;
+ u16 ret;
+ u8 status, data;
+ struct snd_rawmidi_substream *substream;
+
+ spin_lock(&mts->lock);
+ ret = mts64_read(mts->pardev->port);
+ data = ret & 0x00ff;
+ status = ret >> 8;
+
+ if (status & MTS64_STAT_PORT) {
+ mts->current_midi_input_port = mts64_map_midi_input(data);
+ } else {
+ if (mts->current_midi_input_port == -1)
+ goto __out;
+ substream = mts->midi_input_substream[mts->current_midi_input_port];
+ if (mts->mode[substream->number] & MTS64_MODE_INPUT_TRIGGERED)
+ snd_rawmidi_receive(substream, &data, 1);
+ }
+__out:
+ spin_unlock(&mts->lock);
+}
+
+static int __devinit snd_mts64_probe_port(struct parport *p)
+{
+ struct pardevice *pardev;
+ int res;
+
+ pardev = parport_register_device(p, DRIVER_NAME,
+ NULL, NULL, NULL,
+ 0, NULL);
+ if (!pardev)
+ return -EIO;
+
+ if (parport_claim(pardev)) {
+ parport_unregister_device(pardev);
+ return -EIO;
+ }
+
+ res = mts64_probe(p);
+
+ parport_release(pardev);
+ parport_unregister_device(pardev);
+
+ return res;
+}
+
+static void __devinit snd_mts64_attach(struct parport *p)
+{
+ struct platform_device *device;
+
+ device = platform_device_alloc(PLATFORM_DRIVER, device_count);
+ if (!device)
+ return;
+
+ /* Temporary assignment to forward the parport */
+ platform_set_drvdata(device, p);
+
+ if (platform_device_register(device) < 0) {
+ platform_device_put(device);
+ return;
+ }
+
+ /* Since we dont get the return value of probe
+ * We need to check if device probing succeeded or not */
+ if (!platform_get_drvdata(device)) {
+ platform_device_unregister(device);
+ return;
+ }
+
+ /* register device in global table */
+ platform_devices[device_count] = device;
+ device_count++;
+}
+
+static void snd_mts64_detach(struct parport *p)
+{
+ /* nothing to do here */
+}
+
+static struct parport_driver mts64_parport_driver = {
+ .name = "mts64",
+ .attach = snd_mts64_attach,
+ .detach = snd_mts64_detach
+};
+
+/*********************************************************************
+ * platform stuff
+ *********************************************************************/
+static void snd_mts64_card_private_free(struct snd_card *card)
+{
+ struct mts64 *mts = card->private_data;
+ struct pardevice *pardev = mts->pardev;
+
+ if (pardev) {
+ if (mts->pardev_claimed)
+ parport_release(pardev);
+ parport_unregister_device(pardev);
+ }
+
+ snd_mts64_free(mts);
+}
+
+static int __devinit snd_mts64_probe(struct platform_device *pdev)
+{
+ struct pardevice *pardev;
+ struct parport *p;
+ int dev = pdev->id;
+ struct snd_card *card = NULL;
+ struct mts64 *mts = NULL;
+ int err;
+
+ p = platform_get_drvdata(pdev);
+ platform_set_drvdata(pdev, NULL);
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev])
+ return -ENOENT;
+ if ((err = snd_mts64_probe_port(p)) < 0)
+ return err;
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ if (card == NULL) {
+ snd_printd("Cannot create card\n");
+ return -ENOMEM;
+ }
+ strcpy(card->driver, DRIVER_NAME);
+ strcpy(card->shortname, "ESI " CARD_NAME);
+ sprintf(card->longname, "%s at 0x%lx, irq %i",
+ card->shortname, p->base, p->irq);
+
+ pardev = parport_register_device(p, /* port */
+ DRIVER_NAME, /* name */
+ NULL, /* preempt */
+ NULL, /* wakeup */
+ snd_mts64_interrupt, /* ISR */
+ PARPORT_DEV_EXCL, /* flags */
+ (void *)card); /* private */
+ if (pardev == NULL) {
+ snd_printd("Cannot register pardevice\n");
+ err = -EIO;
+ goto __err;
+ }
+
+ if ((err = snd_mts64_create(card, pardev, &mts)) < 0) {
+ snd_printd("Cannot create main component\n");
+ parport_unregister_device(pardev);
+ goto __err;
+ }
+ card->private_data = mts;
+ card->private_free = snd_mts64_card_private_free;
+
+ if ((err = snd_mts64_rawmidi_create(card)) < 0) {
+ snd_printd("Creating Rawmidi component failed\n");
+ goto __err;
+ }
+
+ /* claim parport */
+ if (parport_claim(pardev)) {
+ snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base);
+ err = -EIO;
+ goto __err;
+ }
+ mts->pardev_claimed = 1;
+
+ /* init device */
+ if ((err = mts64_device_init(p)) < 0)
+ goto __err;
+
+ platform_set_drvdata(pdev, card);
+
+ /* At this point card will be usable */
+ if ((err = snd_card_register(card)) < 0) {
+ snd_printd("Cannot register card\n");
+ goto __err;
+ }
+
+ snd_printk("ESI Miditerminal 4140 on 0x%lx\n", p->base);
+ return 0;
+
+__err:
+ snd_card_free(card);
+ return err;
+}
+
+static int snd_mts64_remove(struct platform_device *pdev)
+{
+ struct snd_card *card = platform_get_drvdata(pdev);
+
+ if (card)
+ snd_card_free(card);
+
+ return 0;
+}
+
+
+static struct platform_driver snd_mts64_driver = {
+ .probe = snd_mts64_probe,
+ .remove = snd_mts64_remove,
+ .driver = {
+ .name = PLATFORM_DRIVER
+ }
+};
+
+/*********************************************************************
+ * module init stuff
+ *********************************************************************/
+static void snd_mts64_unregister_all(void)
+{
+ int i;
+
+ for (i = 0; i < SNDRV_CARDS; ++i) {
+ if (platform_devices[i]) {
+ platform_device_unregister(platform_devices[i]);
+ platform_devices[i] = NULL;
+ }
+ }
+ platform_driver_unregister(&snd_mts64_driver);
+ parport_unregister_driver(&mts64_parport_driver);
+}
+
+static int __init snd_mts64_module_init(void)
+{
+ int err;
+
+ if ((err = platform_driver_register(&snd_mts64_driver)) < 0)
+ return err;
+
+ if (parport_register_driver(&mts64_parport_driver) != 0) {
+ platform_driver_unregister(&snd_mts64_driver);
+ return -EIO;
+ }
+
+ if (device_count == 0) {
+ snd_mts64_unregister_all();
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void __exit snd_mts64_module_exit(void)
+{
+ snd_mts64_unregister_all();
+}
+
+module_init(snd_mts64_module_init);
+module_exit(snd_mts64_module_exit);
diff --git a/sound/drivers/opl4/opl4_proc.c b/sound/drivers/opl4/opl4_proc.c
index e552ec34166f..1679300b7583 100644
--- a/sound/drivers/opl4/opl4_proc.c
+++ b/sound/drivers/opl4/opl4_proc.c
@@ -105,13 +105,13 @@ static long long snd_opl4_mem_proc_llseek(struct snd_info_entry *entry, void *fi
struct file *file, long long offset, int orig)
{
switch (orig) {
- case 0: /* SEEK_SET */
+ case SEEK_SET:
file->f_pos = offset;
break;
- case 1: /* SEEK_CUR */
+ case SEEK_CUR:
file->f_pos += offset;
break;
- case 2: /* SEEK_END, offset is negative */
+ case SEEK_END: /* offset is negative */
file->f_pos = entry->size + offset;
break;
default:
@@ -159,8 +159,7 @@ int snd_opl4_create_proc(struct snd_opl4 *opl4)
void snd_opl4_free_proc(struct snd_opl4 *opl4)
{
- if (opl4->proc_entry)
- snd_info_unregister(opl4->proc_entry);
+ snd_info_free_entry(opl4->proc_entry);
}
#endif /* CONFIG_PROC_FS */
diff --git a/sound/drivers/serial-u16550.c b/sound/drivers/serial-u16550.c
index 2330fec505da..52afb4bd2079 100644
--- a/sound/drivers/serial-u16550.c
+++ b/sound/drivers/serial-u16550.c
@@ -795,7 +795,7 @@ static int __init snd_uart16550_create(struct snd_card *card,
if (irq >= 0 && irq != SNDRV_AUTO_IRQ) {
if (request_irq(irq, snd_uart16550_interrupt,
- SA_INTERRUPT, "Serial MIDI", (void *) uart)) {
+ IRQF_DISABLED, "Serial MIDI", (void *) uart)) {
snd_printk("irq %d busy. Using Polling.\n", irq);
} else {
uart->irq = irq;
diff --git a/sound/drivers/vx/vx_mixer.c b/sound/drivers/vx/vx_mixer.c
index c1d7fcdd1973..1613ed844ac6 100644
--- a/sound/drivers/vx/vx_mixer.c
+++ b/sound/drivers/vx/vx_mixer.c
@@ -23,6 +23,7 @@
#include <sound/driver.h>
#include <sound/core.h>
#include <sound/control.h>
+#include <sound/tlv.h>
#include <sound/vx_core.h>
#include "vx_cmd.h"
@@ -455,10 +456,13 @@ static int vx_output_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
static struct snd_kcontrol_new vx_control_output_level = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Master Playback Volume",
.info = vx_output_level_info,
.get = vx_output_level_get,
.put = vx_output_level_put,
+ /* tlv will be filled later */
};
/*
@@ -712,12 +716,17 @@ static int vx_monitor_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
return 0;
}
+static DECLARE_TLV_DB_SCALE(db_scale_audio_gain, -10975, 25, 0);
+
static struct snd_kcontrol_new vx_control_audio_gain = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
/* name will be filled later */
.info = vx_audio_gain_info,
.get = vx_audio_gain_get,
- .put = vx_audio_gain_put
+ .put = vx_audio_gain_put,
+ .tlv = { .p = db_scale_audio_gain },
};
static struct snd_kcontrol_new vx_control_output_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -729,9 +738,12 @@ static struct snd_kcontrol_new vx_control_output_switch = {
static struct snd_kcontrol_new vx_control_monitor_gain = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Monitoring Volume",
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.info = vx_audio_gain_info, /* shared */
.get = vx_audio_monitor_get,
- .put = vx_audio_monitor_put
+ .put = vx_audio_monitor_put,
+ .tlv = { .p = db_scale_audio_gain },
};
static struct snd_kcontrol_new vx_control_monitor_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -918,6 +930,7 @@ int snd_vx_mixer_new(struct vx_core *chip)
for (i = 0; i < chip->hw->num_outs; i++) {
temp = vx_control_output_level;
temp.index = i;
+ temp.tlv.p = chip->hw->output_level_db_scale;
if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
return err;
}
diff --git a/sound/drivers/vx/vx_pcm.c b/sound/drivers/vx/vx_pcm.c
index c4af84995d05..7e65a103fbb2 100644
--- a/sound/drivers/vx/vx_pcm.c
+++ b/sound/drivers/vx/vx_pcm.c
@@ -1252,18 +1252,15 @@ static int vx_init_audio_io(struct vx_core *chip)
chip->audio_info = rmh.Stat[1];
/* allocate pipes */
- chip->playback_pipes = kmalloc(sizeof(struct vx_pipe *) * chip->audio_outs, GFP_KERNEL);
+ chip->playback_pipes = kcalloc(chip->audio_outs, sizeof(struct vx_pipe *), GFP_KERNEL);
if (!chip->playback_pipes)
return -ENOMEM;
- chip->capture_pipes = kmalloc(sizeof(struct vx_pipe *) * chip->audio_ins, GFP_KERNEL);
+ chip->capture_pipes = kcalloc(chip->audio_ins, sizeof(struct vx_pipe *), GFP_KERNEL);
if (!chip->capture_pipes) {
kfree(chip->playback_pipes);
return -ENOMEM;
}
- memset(chip->playback_pipes, 0, sizeof(struct vx_pipe *) * chip->audio_outs);
- memset(chip->capture_pipes, 0, sizeof(struct vx_pipe *) * chip->audio_ins);
-
preferred = chip->ibl.size;
chip->ibl.size = 0;
vx_set_ibl(chip, &chip->ibl); /* query the info */
diff --git a/sound/i2c/cs8427.c b/sound/i2c/cs8427.c
index cb89f7eb9236..64388cb8d6e5 100644
--- a/sound/i2c/cs8427.c
+++ b/sound/i2c/cs8427.c
@@ -76,23 +76,28 @@ int snd_cs8427_reg_write(struct snd_i2c_device *device, unsigned char reg,
buf[0] = reg & 0x7f;
buf[1] = val;
if ((err = snd_i2c_sendbytes(device, buf, 2)) != 2) {
- snd_printk(KERN_ERR "unable to send bytes 0x%02x:0x%02x to CS8427 (%i)\n", buf[0], buf[1], err);
+ snd_printk(KERN_ERR "unable to send bytes 0x%02x:0x%02x "
+ "to CS8427 (%i)\n", buf[0], buf[1], err);
return err < 0 ? err : -EIO;
}
return 0;
}
+EXPORT_SYMBOL(snd_cs8427_reg_write);
+
static int snd_cs8427_reg_read(struct snd_i2c_device *device, unsigned char reg)
{
int err;
unsigned char buf;
if ((err = snd_i2c_sendbytes(device, &reg, 1)) != 1) {
- snd_printk(KERN_ERR "unable to send register 0x%x byte to CS8427\n", reg);
+ snd_printk(KERN_ERR "unable to send register 0x%x byte "
+ "to CS8427\n", reg);
return err < 0 ? err : -EIO;
}
if ((err = snd_i2c_readbytes(device, &buf, 1)) != 1) {
- snd_printk(KERN_ERR "unable to read register 0x%x byte from CS8427\n", reg);
+ snd_printk(KERN_ERR "unable to read register 0x%x byte "
+ "from CS8427\n", reg);
return err < 0 ? err : -EIO;
}
return buf;
@@ -121,7 +126,8 @@ static int snd_cs8427_send_corudata(struct snd_i2c_device *device,
int count)
{
struct cs8427 *chip = device->private_data;
- char *hw_data = udata ? chip->playback.hw_udata : chip->playback.hw_status;
+ char *hw_data = udata ?
+ chip->playback.hw_udata : chip->playback.hw_status;
char data[32];
int err, idx;
@@ -134,11 +140,11 @@ static int snd_cs8427_send_corudata(struct snd_i2c_device *device,
memset(data, 0, sizeof(data));
if (memcmp(hw_data, data, count) == 0) {
chip->regmap[CS8427_REG_UDATABUF] &= ~CS8427_UBMMASK;
- chip->regmap[CS8427_REG_UDATABUF] |= CS8427_UBMZEROS | CS8427_EFTUI;
- if ((err = snd_cs8427_reg_write(device, CS8427_REG_UDATABUF,
- chip->regmap[CS8427_REG_UDATABUF])) < 0)
- return err;
- return 0;
+ chip->regmap[CS8427_REG_UDATABUF] |= CS8427_UBMZEROS |
+ CS8427_EFTUI;
+ err = snd_cs8427_reg_write(device, CS8427_REG_UDATABUF,
+ chip->regmap[CS8427_REG_UDATABUF]);
+ return err < 0 ? err : 0;
}
}
data[0] = CS8427_REG_AUTOINC | CS8427_REG_CORU_DATABUF;
@@ -161,24 +167,32 @@ int snd_cs8427_create(struct snd_i2c_bus *bus,
{
static unsigned char initvals1[] = {
CS8427_REG_CONTROL1 | CS8427_REG_AUTOINC,
- /* CS8427_REG_CONTROL1: RMCK to OMCK, valid PCM audio, disable mutes, TCBL=output */
+ /* CS8427_REG_CONTROL1: RMCK to OMCK, valid PCM audio, disable mutes,
+ TCBL=output */
CS8427_SWCLK | CS8427_TCBLDIR,
- /* CS8427_REG_CONTROL2: hold last valid audio sample, RMCK=256*Fs, normal stereo operation */
+ /* CS8427_REG_CONTROL2: hold last valid audio sample, RMCK=256*Fs,
+ normal stereo operation */
0x00,
- /* CS8427_REG_DATAFLOW: output drivers normal operation, Tx<=serial, Rx=>serial */
+ /* CS8427_REG_DATAFLOW: output drivers normal operation, Tx<=serial,
+ Rx=>serial */
CS8427_TXDSERIAL | CS8427_SPDAES3RECEIVER,
- /* CS8427_REG_CLOCKSOURCE: Run off, CMCK=256*Fs, output time base = OMCK, input time base =
- recovered input clock, recovered input clock source is ILRCK changed to AES3INPUT (workaround, see snd_cs8427_reset) */
+ /* CS8427_REG_CLOCKSOURCE: Run off, CMCK=256*Fs,
+ output time base = OMCK, input time base = recovered input clock,
+ recovered input clock source is ILRCK changed to AES3INPUT
+ (workaround, see snd_cs8427_reset) */
CS8427_RXDILRCK,
- /* CS8427_REG_SERIALINPUT: Serial audio input port data format = I2S, 24-bit, 64*Fsi */
+ /* CS8427_REG_SERIALINPUT: Serial audio input port data format = I2S,
+ 24-bit, 64*Fsi */
CS8427_SIDEL | CS8427_SILRPOL,
- /* CS8427_REG_SERIALOUTPUT: Serial audio output port data format = I2S, 24-bit, 64*Fsi */
+ /* CS8427_REG_SERIALOUTPUT: Serial audio output port data format
+ = I2S, 24-bit, 64*Fsi */
CS8427_SODEL | CS8427_SOLRPOL,
};
static unsigned char initvals2[] = {
CS8427_REG_RECVERRMASK | CS8427_REG_AUTOINC,
- /* CS8427_REG_RECVERRMASK: unmask the input PLL clock, V, confidence, biphase, parity status bits */
- /* CS8427_UNLOCK | CS8427_V | CS8427_CONF | CS8427_BIP | CS8427_PAR, */
+ /* CS8427_REG_RECVERRMASK: unmask the input PLL clock, V, confidence,
+ biphase, parity status bits */
+ /* CS8427_UNLOCK | CS8427_V | CS8427_CONF | CS8427_BIP | CS8427_PAR,*/
0xff, /* set everything */
/* CS8427_REG_CSDATABUF:
Registers 32-55 window to CS buffer
@@ -201,7 +215,8 @@ int snd_cs8427_create(struct snd_i2c_bus *bus,
struct snd_i2c_device *device;
unsigned char buf[24];
- if ((err = snd_i2c_device_create(bus, "CS8427", CS8427_ADDR | (addr & 7),
+ if ((err = snd_i2c_device_create(bus, "CS8427",
+ CS8427_ADDR | (addr & 7),
&device)) < 0)
return err;
chip = device->private_data = kzalloc(sizeof(*chip), GFP_KERNEL);
@@ -212,8 +227,8 @@ int snd_cs8427_create(struct snd_i2c_bus *bus,
device->private_free = snd_cs8427_free;
snd_i2c_lock(bus);
- if ((err = snd_cs8427_reg_read(device, CS8427_REG_ID_AND_VER)) !=
- CS8427_VER8427A) {
+ err = snd_cs8427_reg_read(device, CS8427_REG_ID_AND_VER);
+ if (err != CS8427_VER8427A) {
snd_i2c_unlock(bus);
snd_printk(KERN_ERR "unable to find CS8427 signature "
"(expected 0x%x, read 0x%x),\n",
@@ -222,7 +237,8 @@ int snd_cs8427_create(struct snd_i2c_bus *bus,
return -EFAULT;
}
/* turn off run bit while making changes to configuration */
- if ((err = snd_cs8427_reg_write(device, CS8427_REG_CLOCKSOURCE, 0x00)) < 0)
+ err = snd_cs8427_reg_write(device, CS8427_REG_CLOCKSOURCE, 0x00);
+ if (err < 0)
goto __fail;
/* send initial values */
memcpy(chip->regmap + (initvals1[0] & 0x7f), initvals1 + 1, 6);
@@ -282,6 +298,8 @@ int snd_cs8427_create(struct snd_i2c_bus *bus,
return err < 0 ? err : -EIO;
}
+EXPORT_SYMBOL(snd_cs8427_create);
+
/*
* Reset the chip using run bit, also lock PLL using ILRCK and
* put back AES3INPUT. This workaround is described in latest
@@ -296,7 +314,8 @@ static void snd_cs8427_reset(struct snd_i2c_device *cs8427)
snd_assert(cs8427, return);
chip = cs8427->private_data;
snd_i2c_lock(cs8427->bus);
- if ((chip->regmap[CS8427_REG_CLOCKSOURCE] & CS8427_RXDAES3INPUT) == CS8427_RXDAES3INPUT) /* AES3 bit is set */
+ if ((chip->regmap[CS8427_REG_CLOCKSOURCE] & CS8427_RXDAES3INPUT) ==
+ CS8427_RXDAES3INPUT) /* AES3 bit is set */
aes3input = 1;
chip->regmap[CS8427_REG_CLOCKSOURCE] &= ~(CS8427_RUN | CS8427_RXDMASK);
snd_cs8427_reg_write(cs8427, CS8427_REG_CLOCKSOURCE,
@@ -367,12 +386,15 @@ static int snd_cs8427_qsubcode_get(struct snd_kcontrol *kcontrol,
snd_i2c_lock(device->bus);
if ((err = snd_i2c_sendbytes(device, &reg, 1)) != 1) {
- snd_printk(KERN_ERR "unable to send register 0x%x byte to CS8427\n", reg);
+ snd_printk(KERN_ERR "unable to send register 0x%x byte "
+ "to CS8427\n", reg);
snd_i2c_unlock(device->bus);
return err < 0 ? err : -EIO;
}
- if ((err = snd_i2c_readbytes(device, ucontrol->value.bytes.data, 10)) != 10) {
- snd_printk(KERN_ERR "unable to read Q-subcode bytes from CS8427\n");
+ err = snd_i2c_readbytes(device, ucontrol->value.bytes.data, 10);
+ if (err != 10) {
+ snd_printk(KERN_ERR "unable to read Q-subcode bytes "
+ "from CS8427\n");
snd_i2c_unlock(device->bus);
return err < 0 ? err : -EIO;
}
@@ -380,7 +402,8 @@ static int snd_cs8427_qsubcode_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static int snd_cs8427_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static int snd_cs8427_spdif_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
uinfo->count = 1;
@@ -413,7 +436,8 @@ static int snd_cs8427_spdif_put(struct snd_kcontrol *kcontrol,
snd_i2c_lock(device->bus);
change = memcmp(ucontrol->value.iec958.status, status, 24) != 0;
memcpy(status, ucontrol->value.iec958.status, 24);
- if (change && (kcontrol->private_value ? runtime != NULL : runtime == NULL)) {
+ if (change && (kcontrol->private_value ?
+ runtime != NULL : runtime == NULL)) {
err = snd_cs8427_send_corudata(device, 0, status, 24);
if (err < 0)
change = err;
@@ -442,7 +466,8 @@ static struct snd_kcontrol_new snd_cs8427_iec958_controls[] = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.info = snd_cs8427_in_status_info,
.name = "IEC958 CS8427 Input Status",
- .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE),
.get = snd_cs8427_in_status_get,
.private_value = 15,
},
@@ -450,7 +475,8 @@ static struct snd_kcontrol_new snd_cs8427_iec958_controls[] = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.info = snd_cs8427_in_status_info,
.name = "IEC958 CS8427 Error Status",
- .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE),
.get = snd_cs8427_in_status_get,
.private_value = 16,
},
@@ -470,7 +496,8 @@ static struct snd_kcontrol_new snd_cs8427_iec958_controls[] = {
.private_value = 0
},
{
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_INACTIVE),
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
.info = snd_cs8427_spdif_info,
@@ -482,7 +509,8 @@ static struct snd_kcontrol_new snd_cs8427_iec958_controls[] = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.info = snd_cs8427_qsubcode_info,
.name = "IEC958 Q-subcode Capture Default",
- .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE),
.get = snd_cs8427_qsubcode_get
}};
@@ -505,7 +533,8 @@ int snd_cs8427_iec958_build(struct snd_i2c_device *cs8427,
err = snd_ctl_add(cs8427->bus->card, kctl);
if (err < 0)
return err;
- if (!strcmp(kctl->id.name, SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM)))
+ if (! strcmp(kctl->id.name,
+ SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM)))
chip->playback.pcm_ctl = kctl;
}
@@ -515,6 +544,8 @@ int snd_cs8427_iec958_build(struct snd_i2c_device *cs8427,
return 0;
}
+EXPORT_SYMBOL(snd_cs8427_iec958_build);
+
int snd_cs8427_iec958_active(struct snd_i2c_device *cs8427, int active)
{
struct cs8427 *chip;
@@ -522,13 +553,17 @@ int snd_cs8427_iec958_active(struct snd_i2c_device *cs8427, int active)
snd_assert(cs8427, return -ENXIO);
chip = cs8427->private_data;
if (active)
- memcpy(chip->playback.pcm_status, chip->playback.def_status, 24);
+ memcpy(chip->playback.pcm_status,
+ chip->playback.def_status, 24);
chip->playback.pcm_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
- snd_ctl_notify(cs8427->bus->card, SNDRV_CTL_EVENT_MASK_VALUE |
- SNDRV_CTL_EVENT_MASK_INFO, &chip->playback.pcm_ctl->id);
+ snd_ctl_notify(cs8427->bus->card,
+ SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO,
+ &chip->playback.pcm_ctl->id);
return 0;
}
+EXPORT_SYMBOL(snd_cs8427_iec958_active);
+
int snd_cs8427_iec958_pcm(struct snd_i2c_device *cs8427, unsigned int rate)
{
struct cs8427 *chip;
@@ -568,6 +603,8 @@ int snd_cs8427_iec958_pcm(struct snd_i2c_device *cs8427, unsigned int rate)
return err < 0 ? err : 0;
}
+EXPORT_SYMBOL(snd_cs8427_iec958_pcm);
+
static int __init alsa_cs8427_module_init(void)
{
return 0;
@@ -579,10 +616,3 @@ static void __exit alsa_cs8427_module_exit(void)
module_init(alsa_cs8427_module_init)
module_exit(alsa_cs8427_module_exit)
-
-EXPORT_SYMBOL(snd_cs8427_create);
-EXPORT_SYMBOL(snd_cs8427_reset);
-EXPORT_SYMBOL(snd_cs8427_reg_write);
-EXPORT_SYMBOL(snd_cs8427_iec958_build);
-EXPORT_SYMBOL(snd_cs8427_iec958_active);
-EXPORT_SYMBOL(snd_cs8427_iec958_pcm);
diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c
index 045e32a311e0..5da49e2eb350 100644
--- a/sound/i2c/other/ak4xxx-adda.c
+++ b/sound/i2c/other/ak4xxx-adda.c
@@ -28,30 +28,80 @@
#include <linux/init.h>
#include <sound/core.h>
#include <sound/control.h>
+#include <sound/tlv.h>
#include <sound/ak4xxx-adda.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Takashi Iwai <tiwai@suse.de>");
MODULE_DESCRIPTION("Routines for control of AK452x / AK43xx AD/DA converters");
MODULE_LICENSE("GPL");
-void snd_akm4xxx_write(struct snd_akm4xxx *ak, int chip, unsigned char reg, unsigned char val)
+/* write the given register and save the data to the cache */
+void snd_akm4xxx_write(struct snd_akm4xxx *ak, int chip, unsigned char reg,
+ unsigned char val)
{
ak->ops.lock(ak, chip);
ak->ops.write(ak, chip, reg, val);
/* save the data */
- if (ak->type == SND_AK4524 || ak->type == SND_AK4528) {
- if ((reg != 0x04 && reg != 0x05) || (val & 0x80) == 0)
- snd_akm4xxx_set(ak, chip, reg, val);
- else
- snd_akm4xxx_set_ipga(ak, chip, reg, val);
- } else {
- /* AK4529, or else */
- snd_akm4xxx_set(ak, chip, reg, val);
- }
+ snd_akm4xxx_set(ak, chip, reg, val);
ak->ops.unlock(ak, chip);
}
+EXPORT_SYMBOL(snd_akm4xxx_write);
+
+/* reset procedure for AK4524 and AK4528 */
+static void ak4524_reset(struct snd_akm4xxx *ak, int state)
+{
+ unsigned int chip;
+ unsigned char reg, maxreg;
+
+ if (ak->type == SND_AK4528)
+ maxreg = 0x06;
+ else
+ maxreg = 0x08;
+ for (chip = 0; chip < ak->num_dacs/2; chip++) {
+ snd_akm4xxx_write(ak, chip, 0x01, state ? 0x00 : 0x03);
+ if (state)
+ continue;
+ /* DAC volumes */
+ for (reg = 0x04; reg < maxreg; reg++)
+ snd_akm4xxx_write(ak, chip, reg,
+ snd_akm4xxx_get(ak, chip, reg));
+ }
+}
+
+/* reset procedure for AK4355 and AK4358 */
+static void ak4355_reset(struct snd_akm4xxx *ak, int state)
+{
+ unsigned char reg;
+
+ if (state) {
+ snd_akm4xxx_write(ak, 0, 0x01, 0x02); /* reset and soft-mute */
+ return;
+ }
+ for (reg = 0x00; reg < 0x0b; reg++)
+ if (reg != 0x01)
+ snd_akm4xxx_write(ak, 0, reg,
+ snd_akm4xxx_get(ak, 0, reg));
+ snd_akm4xxx_write(ak, 0, 0x01, 0x01); /* un-reset, unmute */
+}
+
+/* reset procedure for AK4381 */
+static void ak4381_reset(struct snd_akm4xxx *ak, int state)
+{
+ unsigned int chip;
+ unsigned char reg;
+
+ for (chip = 0; chip < ak->num_dacs/2; chip++) {
+ snd_akm4xxx_write(ak, chip, 0x00, state ? 0x0c : 0x0f);
+ if (state)
+ continue;
+ for (reg = 0x01; reg < 0x05; reg++)
+ snd_akm4xxx_write(ak, chip, reg,
+ snd_akm4xxx_get(ak, chip, reg));
+ }
+}
+
/*
* reset the AKM codecs
* @state: 1 = reset codec, 0 = restore the registers
@@ -60,52 +110,63 @@ void snd_akm4xxx_write(struct snd_akm4xxx *ak, int chip, unsigned char reg, unsi
*/
void snd_akm4xxx_reset(struct snd_akm4xxx *ak, int state)
{
- unsigned int chip;
- unsigned char reg;
-
switch (ak->type) {
case SND_AK4524:
case SND_AK4528:
- for (chip = 0; chip < ak->num_dacs/2; chip++) {
- snd_akm4xxx_write(ak, chip, 0x01, state ? 0x00 : 0x03);
- if (state)
- continue;
- /* DAC volumes */
- for (reg = 0x04; reg < (ak->type == SND_AK4528 ? 0x06 : 0x08); reg++)
- snd_akm4xxx_write(ak, chip, reg, snd_akm4xxx_get(ak, chip, reg));
- if (ak->type == SND_AK4528)
- continue;
- /* IPGA */
- for (reg = 0x04; reg < 0x06; reg++)
- snd_akm4xxx_write(ak, chip, reg, snd_akm4xxx_get_ipga(ak, chip, reg));
- }
+ ak4524_reset(ak, state);
break;
case SND_AK4529:
/* FIXME: needed for ak4529? */
break;
case SND_AK4355:
case SND_AK4358:
- if (state) {
- snd_akm4xxx_write(ak, 0, 0x01, 0x02); /* reset and soft-mute */
- return;
- }
- for (reg = 0x00; reg < 0x0b; reg++)
- if (reg != 0x01)
- snd_akm4xxx_write(ak, 0, reg, snd_akm4xxx_get(ak, 0, reg));
- snd_akm4xxx_write(ak, 0, 0x01, 0x01); /* un-reset, unmute */
+ ak4355_reset(ak, state);
break;
case SND_AK4381:
- for (chip = 0; chip < ak->num_dacs/2; chip++) {
- snd_akm4xxx_write(ak, chip, 0x00, state ? 0x0c : 0x0f);
- if (state)
- continue;
- for (reg = 0x01; reg < 0x05; reg++)
- snd_akm4xxx_write(ak, chip, reg, snd_akm4xxx_get(ak, chip, reg));
- }
+ ak4381_reset(ak, state);
+ break;
+ default:
break;
}
}
+EXPORT_SYMBOL(snd_akm4xxx_reset);
+
+
+/*
+ * Volume conversion table for non-linear volumes
+ * from -63.5dB (mute) to 0dB step 0.5dB
+ *
+ * Used for AK4524 input/ouput attenuation, AK4528, and
+ * AK5365 input attenuation
+ */
+static unsigned char vol_cvt_datt[128] = {
+ 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,
+ 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x06,
+ 0x06, 0x07, 0x07, 0x08, 0x08, 0x08, 0x09, 0x0a,
+ 0x0a, 0x0b, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x0f,
+ 0x10, 0x10, 0x11, 0x12, 0x12, 0x13, 0x13, 0x14,
+ 0x15, 0x16, 0x17, 0x17, 0x18, 0x19, 0x1a, 0x1c,
+ 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x23,
+ 0x24, 0x25, 0x26, 0x28, 0x29, 0x2a, 0x2b, 0x2d,
+ 0x2e, 0x30, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
+ 0x37, 0x38, 0x39, 0x3b, 0x3c, 0x3e, 0x3f, 0x40,
+ 0x41, 0x42, 0x43, 0x44, 0x46, 0x47, 0x48, 0x4a,
+ 0x4b, 0x4d, 0x4e, 0x50, 0x51, 0x52, 0x53, 0x54,
+ 0x55, 0x56, 0x58, 0x59, 0x5b, 0x5c, 0x5e, 0x5f,
+ 0x60, 0x61, 0x62, 0x64, 0x65, 0x66, 0x67, 0x69,
+ 0x6a, 0x6c, 0x6d, 0x6f, 0x70, 0x71, 0x72, 0x73,
+ 0x75, 0x76, 0x77, 0x79, 0x7a, 0x7c, 0x7d, 0x7f,
+};
+
+/*
+ * dB tables
+ */
+static DECLARE_TLV_DB_SCALE(db_scale_vol_datt, -6350, 50, 1);
+static DECLARE_TLV_DB_SCALE(db_scale_8bit, -12750, 50, 1);
+static DECLARE_TLV_DB_SCALE(db_scale_7bit, -6350, 50, 1);
+static DECLARE_TLV_DB_LINEAR(db_scale_linear, TLV_DB_GAIN_MUTE, 0);
+
/*
* initialize all the ak4xxx chips
*/
@@ -119,8 +180,6 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
0x01, 0x03, /* 1: ADC/DAC enable */
0x04, 0x00, /* 4: ADC left muted */
0x05, 0x00, /* 5: ADC right muted */
- 0x04, 0x80, /* 4: ADC IPGA gain 0dB */
- 0x05, 0x80, /* 5: ADC IPGA gain 0dB */
0x06, 0x00, /* 6: DAC left muted */
0x07, 0x00, /* 7: DAC right muted */
0xff, 0xff
@@ -153,7 +212,8 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
};
static unsigned char inits_ak4355[] = {
0x01, 0x02, /* 1: reset and soft-mute */
- 0x00, 0x06, /* 0: mode3(i2s), disable auto-clock detect, disable DZF, sharp roll-off, RSTN#=0 */
+ 0x00, 0x06, /* 0: mode3(i2s), disable auto-clock detect,
+ * disable DZF, sharp roll-off, RSTN#=0 */
0x02, 0x0e, /* 2: DA's power up, normal speed, RSTN#=0 */
// 0x02, 0x2e, /* quad speed */
0x03, 0x01, /* 3: de-emphasis off */
@@ -169,7 +229,8 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
};
static unsigned char inits_ak4358[] = {
0x01, 0x02, /* 1: reset and soft-mute */
- 0x00, 0x06, /* 0: mode3(i2s), disable auto-clock detect, disable DZF, sharp roll-off, RSTN#=0 */
+ 0x00, 0x06, /* 0: mode3(i2s), disable auto-clock detect,
+ * disable DZF, sharp roll-off, RSTN#=0 */
0x02, 0x0e, /* 2: DA's power up, normal speed, RSTN#=0 */
// 0x02, 0x2e, /* quad speed */
0x03, 0x01, /* 3: de-emphasis off */
@@ -187,7 +248,8 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
};
static unsigned char inits_ak4381[] = {
0x00, 0x0c, /* 0: mode3(i2s), disable auto-clock detect */
- 0x01, 0x02, /* 1: de-emphasis off, normal speed, sharp roll-off, DZF off */
+ 0x01, 0x02, /* 1: de-emphasis off, normal speed,
+ * sharp roll-off, DZF off */
// 0x01, 0x12, /* quad speed */
0x02, 0x00, /* 2: DZF disabled */
0x03, 0x00, /* 3: LATT 0 */
@@ -199,6 +261,9 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
int chip, num_chips;
unsigned char *ptr, reg, data, *inits;
+ memset(ak->images, 0, sizeof(ak->images));
+ memset(ak->volumes, 0, sizeof(ak->volumes));
+
switch (ak->type) {
case SND_AK4524:
inits = inits_ak4524;
@@ -224,6 +289,9 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
inits = inits_ak4381;
num_chips = ak->num_dacs / 2;
break;
+ case SND_AK5365:
+ /* FIXME: any init sequence? */
+ return;
default:
snd_BUG();
return;
@@ -239,13 +307,25 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
}
}
+EXPORT_SYMBOL(snd_akm4xxx_init);
+
+/*
+ * Mixer callbacks
+ */
+#define AK_IPGA (1<<20) /* including IPGA */
+#define AK_VOL_CVT (1<<21) /* need dB conversion */
+#define AK_NEEDSMSB (1<<22) /* need MSB update bit */
+#define AK_INVERT (1<<23) /* data is inverted */
#define AK_GET_CHIP(val) (((val) >> 8) & 0xff)
#define AK_GET_ADDR(val) ((val) & 0xff)
-#define AK_GET_SHIFT(val) (((val) >> 16) & 0x7f)
+#define AK_GET_SHIFT(val) (((val) >> 16) & 0x0f)
+#define AK_GET_VOL_CVT(val) (((val) >> 21) & 1)
+#define AK_GET_IPGA(val) (((val) >> 20) & 1)
+#define AK_GET_NEEDSMSB(val) (((val) >> 22) & 1)
#define AK_GET_INVERT(val) (((val) >> 23) & 1)
#define AK_GET_MASK(val) (((val) >> 24) & 0xff)
-#define AK_COMPOSE(chip,addr,shift,mask) (((chip) << 8) | (addr) | ((shift) << 16) | ((mask) << 24))
-#define AK_INVERT (1<<23)
+#define AK_COMPOSE(chip,addr,shift,mask) \
+ (((chip) << 8) | (addr) | ((shift) << 16) | ((mask) << 24))
static int snd_akm4xxx_volume_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
@@ -265,63 +345,74 @@ static int snd_akm4xxx_volume_get(struct snd_kcontrol *kcontrol,
struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
int chip = AK_GET_CHIP(kcontrol->private_value);
int addr = AK_GET_ADDR(kcontrol->private_value);
- int invert = AK_GET_INVERT(kcontrol->private_value);
- unsigned int mask = AK_GET_MASK(kcontrol->private_value);
- unsigned char val = snd_akm4xxx_get(ak, chip, addr);
-
- ucontrol->value.integer.value[0] = invert ? mask - val : val;
+
+ ucontrol->value.integer.value[0] = snd_akm4xxx_get_vol(ak, chip, addr);
return 0;
}
-static int snd_akm4xxx_volume_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int put_ak_reg(struct snd_kcontrol *kcontrol, int addr,
+ unsigned char nval)
{
struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
- int chip = AK_GET_CHIP(kcontrol->private_value);
- int addr = AK_GET_ADDR(kcontrol->private_value);
- int invert = AK_GET_INVERT(kcontrol->private_value);
unsigned int mask = AK_GET_MASK(kcontrol->private_value);
- unsigned char nval = ucontrol->value.integer.value[0] % (mask+1);
- int change;
+ int chip = AK_GET_CHIP(kcontrol->private_value);
- if (invert)
+ if (snd_akm4xxx_get_vol(ak, chip, addr) == nval)
+ return 0;
+
+ snd_akm4xxx_set_vol(ak, chip, addr, nval);
+ if (AK_GET_VOL_CVT(kcontrol->private_value) && nval < 128)
+ nval = vol_cvt_datt[nval];
+ if (AK_GET_IPGA(kcontrol->private_value) && nval >= 128)
+ nval++; /* need to correct + 1 since both 127 and 128 are 0dB */
+ if (AK_GET_INVERT(kcontrol->private_value))
nval = mask - nval;
- change = snd_akm4xxx_get(ak, chip, addr) != nval;
- if (change)
- snd_akm4xxx_write(ak, chip, addr, nval);
- return change;
+ if (AK_GET_NEEDSMSB(kcontrol->private_value))
+ nval |= 0x80;
+ snd_akm4xxx_write(ak, chip, addr, nval);
+ return 1;
+}
+
+static int snd_akm4xxx_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return put_ak_reg(kcontrol, AK_GET_ADDR(kcontrol->private_value),
+ ucontrol->value.integer.value[0]);
}
-static int snd_akm4xxx_ipga_gain_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+static int snd_akm4xxx_stereo_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
+ unsigned int mask = AK_GET_MASK(kcontrol->private_value);
+
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 1;
+ uinfo->count = 2;
uinfo->value.integer.min = 0;
- uinfo->value.integer.max = 36;
+ uinfo->value.integer.max = mask;
return 0;
}
-static int snd_akm4xxx_ipga_gain_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int snd_akm4xxx_stereo_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
int chip = AK_GET_CHIP(kcontrol->private_value);
int addr = AK_GET_ADDR(kcontrol->private_value);
- ucontrol->value.integer.value[0] = snd_akm4xxx_get_ipga(ak, chip, addr) & 0x7f;
+
+ ucontrol->value.integer.value[0] = snd_akm4xxx_get_vol(ak, chip, addr);
+ ucontrol->value.integer.value[1] = snd_akm4xxx_get_vol(ak, chip, addr+1);
return 0;
}
-static int snd_akm4xxx_ipga_gain_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int snd_akm4xxx_stereo_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
- int chip = AK_GET_CHIP(kcontrol->private_value);
int addr = AK_GET_ADDR(kcontrol->private_value);
- unsigned char nval = (ucontrol->value.integer.value[0] % 37) | 0x80;
- int change = snd_akm4xxx_get_ipga(ak, chip, addr) != nval;
- if (change)
- snd_akm4xxx_write(ak, chip, addr, nval);
+ int change;
+
+ change = put_ak_reg(kcontrol, addr, ucontrol->value.integer.value[0]);
+ change |= put_ak_reg(kcontrol, addr + 1,
+ ucontrol->value.integer.value[1]);
return change;
}
@@ -336,7 +427,8 @@ static int snd_akm4xxx_deemphasis_info(struct snd_kcontrol *kcontrol,
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]);
+ strcpy(uinfo->value.enumerated.name,
+ texts[uinfo->value.enumerated.item]);
return 0;
}
@@ -347,7 +439,8 @@ static int snd_akm4xxx_deemphasis_get(struct snd_kcontrol *kcontrol,
int chip = AK_GET_CHIP(kcontrol->private_value);
int addr = AK_GET_ADDR(kcontrol->private_value);
int shift = AK_GET_SHIFT(kcontrol->private_value);
- ucontrol->value.enumerated.item[0] = (snd_akm4xxx_get(ak, chip, addr) >> shift) & 3;
+ ucontrol->value.enumerated.item[0] =
+ (snd_akm4xxx_get(ak, chip, addr) >> shift) & 3;
return 0;
}
@@ -361,136 +454,289 @@ static int snd_akm4xxx_deemphasis_put(struct snd_kcontrol *kcontrol,
unsigned char nval = ucontrol->value.enumerated.item[0] & 3;
int change;
- nval = (nval << shift) | (snd_akm4xxx_get(ak, chip, addr) & ~(3 << shift));
+ nval = (nval << shift) |
+ (snd_akm4xxx_get(ak, chip, addr) & ~(3 << shift));
change = snd_akm4xxx_get(ak, chip, addr) != nval;
if (change)
snd_akm4xxx_write(ak, chip, addr, nval);
return change;
}
+static int ak4xxx_switch_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int ak4xxx_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
+ int chip = AK_GET_CHIP(kcontrol->private_value);
+ int addr = AK_GET_ADDR(kcontrol->private_value);
+ int shift = AK_GET_SHIFT(kcontrol->private_value);
+ int invert = AK_GET_INVERT(kcontrol->private_value);
+ unsigned char val = snd_akm4xxx_get(ak, chip, addr);
+
+ if (invert)
+ val = ! val;
+ ucontrol->value.integer.value[0] = (val & (1<<shift)) != 0;
+ return 0;
+}
+
+static int ak4xxx_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
+ int chip = AK_GET_CHIP(kcontrol->private_value);
+ int addr = AK_GET_ADDR(kcontrol->private_value);
+ int shift = AK_GET_SHIFT(kcontrol->private_value);
+ int invert = AK_GET_INVERT(kcontrol->private_value);
+ long flag = ucontrol->value.integer.value[0];
+ unsigned char val, oval;
+ int change;
+
+ if (invert)
+ flag = ! flag;
+ oval = snd_akm4xxx_get(ak, chip, addr);
+ if (flag)
+ val = oval | (1<<shift);
+ else
+ val = oval & ~(1<<shift);
+ change = (oval != val);
+ if (change)
+ snd_akm4xxx_write(ak, chip, addr, val);
+ return change;
+}
+
/*
* build AK4xxx controls
*/
-int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak)
+static int build_dac_controls(struct snd_akm4xxx *ak)
{
- unsigned int idx, num_emphs;
- struct snd_kcontrol *ctl;
- int err;
-
- ctl = kmalloc(sizeof(*ctl), GFP_KERNEL);
- if (! ctl)
- return -ENOMEM;
-
- for (idx = 0; idx < ak->num_dacs; ++idx) {
- memset(ctl, 0, sizeof(*ctl));
- strcpy(ctl->id.name, "DAC Volume");
- ctl->id.index = idx + ak->idx_offset * 2;
- ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- ctl->count = 1;
- ctl->info = snd_akm4xxx_volume_info;
- ctl->get = snd_akm4xxx_volume_get;
- ctl->put = snd_akm4xxx_volume_put;
+ int idx, err, mixer_ch, num_stereo;
+ struct snd_kcontrol_new knew;
+
+ mixer_ch = 0;
+ for (idx = 0; idx < ak->num_dacs; ) {
+ memset(&knew, 0, sizeof(knew));
+ if (! ak->dac_info || ! ak->dac_info[mixer_ch].name) {
+ knew.name = "DAC Volume";
+ knew.index = mixer_ch + ak->idx_offset * 2;
+ num_stereo = 1;
+ } else {
+ knew.name = ak->dac_info[mixer_ch].name;
+ num_stereo = ak->dac_info[mixer_ch].num_channels;
+ }
+ knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ knew.count = 1;
+ knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+ if (num_stereo == 2) {
+ knew.info = snd_akm4xxx_stereo_volume_info;
+ knew.get = snd_akm4xxx_stereo_volume_get;
+ knew.put = snd_akm4xxx_stereo_volume_put;
+ } else {
+ knew.info = snd_akm4xxx_volume_info;
+ knew.get = snd_akm4xxx_volume_get;
+ knew.put = snd_akm4xxx_volume_put;
+ }
switch (ak->type) {
case SND_AK4524:
- ctl->private_value = AK_COMPOSE(idx/2, (idx%2) + 6, 0, 127); /* register 6 & 7 */
+ /* register 6 & 7 */
+ knew.private_value =
+ AK_COMPOSE(idx/2, (idx%2) + 6, 0, 127) |
+ AK_VOL_CVT;
+ knew.tlv.p = db_scale_vol_datt;
break;
case SND_AK4528:
- ctl->private_value = AK_COMPOSE(idx/2, (idx%2) + 4, 0, 127); /* register 4 & 5 */
+ /* register 4 & 5 */
+ knew.private_value =
+ AK_COMPOSE(idx/2, (idx%2) + 4, 0, 127) |
+ AK_VOL_CVT;
+ knew.tlv.p = db_scale_vol_datt;
break;
case SND_AK4529: {
- int val = idx < 6 ? idx + 2 : (idx - 6) + 0xb; /* registers 2-7 and b,c */
- ctl->private_value = AK_COMPOSE(0, val, 0, 255) | AK_INVERT;
+ /* registers 2-7 and b,c */
+ int val = idx < 6 ? idx + 2 : (idx - 6) + 0xb;
+ knew.private_value =
+ AK_COMPOSE(0, val, 0, 255) | AK_INVERT;
+ knew.tlv.p = db_scale_8bit;
break;
}
case SND_AK4355:
- ctl->private_value = AK_COMPOSE(0, idx + 4, 0, 255); /* register 4-9, chip #0 only */
+ /* register 4-9, chip #0 only */
+ knew.private_value = AK_COMPOSE(0, idx + 4, 0, 255);
+ knew.tlv.p = db_scale_8bit;
break;
- case SND_AK4358:
- if (idx >= 6)
- ctl->private_value = AK_COMPOSE(0, idx + 5, 0, 255); /* register 4-9, chip #0 only */
- else
- ctl->private_value = AK_COMPOSE(0, idx + 4, 0, 255); /* register 4-9, chip #0 only */
+ case SND_AK4358: {
+ /* register 4-9 and 11-12, chip #0 only */
+ int addr = idx < 6 ? idx + 4 : idx + 5;
+ knew.private_value =
+ AK_COMPOSE(0, addr, 0, 127) | AK_NEEDSMSB;
+ knew.tlv.p = db_scale_7bit;
break;
+ }
case SND_AK4381:
- ctl->private_value = AK_COMPOSE(idx/2, (idx%2) + 3, 0, 255); /* register 3 & 4 */
+ /* register 3 & 4 */
+ knew.private_value =
+ AK_COMPOSE(idx/2, (idx%2) + 3, 0, 255);
+ knew.tlv.p = db_scale_linear;
break;
default:
- err = -EINVAL;
- goto __error;
+ return -EINVAL;
}
- ctl->private_data = ak;
- if ((err = snd_ctl_add(ak->card, snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE))) < 0)
- goto __error;
+
+ err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak));
+ if (err < 0)
+ return err;
+
+ idx += num_stereo;
+ mixer_ch++;
}
- for (idx = 0; idx < ak->num_adcs && ak->type == SND_AK4524; ++idx) {
- memset(ctl, 0, sizeof(*ctl));
- strcpy(ctl->id.name, "ADC Volume");
- ctl->id.index = idx + ak->idx_offset * 2;
- ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- ctl->count = 1;
- ctl->info = snd_akm4xxx_volume_info;
- ctl->get = snd_akm4xxx_volume_get;
- ctl->put = snd_akm4xxx_volume_put;
- ctl->private_value = AK_COMPOSE(idx/2, (idx%2) + 4, 0, 127); /* register 4 & 5 */
- ctl->private_data = ak;
- if ((err = snd_ctl_add(ak->card, snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE))) < 0)
- goto __error;
-
- memset(ctl, 0, sizeof(*ctl));
- strcpy(ctl->id.name, "IPGA Analog Capture Volume");
- ctl->id.index = idx + ak->idx_offset * 2;
- ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- ctl->count = 1;
- ctl->info = snd_akm4xxx_ipga_gain_info;
- ctl->get = snd_akm4xxx_ipga_gain_get;
- ctl->put = snd_akm4xxx_ipga_gain_put;
- ctl->private_value = AK_COMPOSE(idx/2, (idx%2) + 4, 0, 0); /* register 4 & 5 */
- ctl->private_data = ak;
- if ((err = snd_ctl_add(ak->card, snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE))) < 0)
- goto __error;
+ return 0;
+}
+
+static int build_adc_controls(struct snd_akm4xxx *ak)
+{
+ int idx, err, mixer_ch, num_stereo;
+ struct snd_kcontrol_new knew;
+
+ mixer_ch = 0;
+ for (idx = 0; idx < ak->num_adcs;) {
+ memset(&knew, 0, sizeof(knew));
+ if (! ak->adc_info || ! ak->adc_info[mixer_ch].name) {
+ knew.name = "ADC Volume";
+ knew.index = mixer_ch + ak->idx_offset * 2;
+ num_stereo = 1;
+ } else {
+ knew.name = ak->adc_info[mixer_ch].name;
+ num_stereo = ak->adc_info[mixer_ch].num_channels;
+ }
+ knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ knew.count = 1;
+ knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+ if (num_stereo == 2) {
+ knew.info = snd_akm4xxx_stereo_volume_info;
+ knew.get = snd_akm4xxx_stereo_volume_get;
+ knew.put = snd_akm4xxx_stereo_volume_put;
+ } else {
+ knew.info = snd_akm4xxx_volume_info;
+ knew.get = snd_akm4xxx_volume_get;
+ knew.put = snd_akm4xxx_volume_put;
+ }
+ /* register 4 & 5 */
+ if (ak->type == SND_AK5365)
+ knew.private_value =
+ AK_COMPOSE(idx/2, (idx%2) + 4, 0, 151) |
+ AK_VOL_CVT | AK_IPGA;
+ else
+ knew.private_value =
+ AK_COMPOSE(idx/2, (idx%2) + 4, 0, 163) |
+ AK_VOL_CVT | AK_IPGA;
+ knew.tlv.p = db_scale_vol_datt;
+ err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak));
+ if (err < 0)
+ return err;
+
+ if (ak->type == SND_AK5365 && (idx % 2) == 0) {
+ if (! ak->adc_info ||
+ ! ak->adc_info[mixer_ch].switch_name)
+ knew.name = "Capture Switch";
+ else
+ knew.name = ak->adc_info[mixer_ch].switch_name;
+ knew.info = ak4xxx_switch_info;
+ knew.get = ak4xxx_switch_get;
+ knew.put = ak4xxx_switch_put;
+ knew.access = 0;
+ /* register 2, bit 0 (SMUTE): 0 = normal operation,
+ 1 = mute */
+ knew.private_value =
+ AK_COMPOSE(idx/2, 2, 0, 0) | AK_INVERT;
+ err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak));
+ if (err < 0)
+ return err;
+ }
+
+ idx += num_stereo;
+ mixer_ch++;
}
- if (ak->type == SND_AK4355 || ak->type == SND_AK4358)
- num_emphs = 1;
- else
- num_emphs = ak->num_dacs / 2;
+ return 0;
+}
+
+static int build_deemphasis(struct snd_akm4xxx *ak, int num_emphs)
+{
+ int idx, err;
+ struct snd_kcontrol_new knew;
+
for (idx = 0; idx < num_emphs; idx++) {
- memset(ctl, 0, sizeof(*ctl));
- strcpy(ctl->id.name, "Deemphasis");
- ctl->id.index = idx + ak->idx_offset;
- ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- ctl->count = 1;
- ctl->info = snd_akm4xxx_deemphasis_info;
- ctl->get = snd_akm4xxx_deemphasis_get;
- ctl->put = snd_akm4xxx_deemphasis_put;
+ memset(&knew, 0, sizeof(knew));
+ knew.name = "Deemphasis";
+ knew.index = idx + ak->idx_offset;
+ knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ knew.count = 1;
+ knew.info = snd_akm4xxx_deemphasis_info;
+ knew.get = snd_akm4xxx_deemphasis_get;
+ knew.put = snd_akm4xxx_deemphasis_put;
switch (ak->type) {
case SND_AK4524:
case SND_AK4528:
- ctl->private_value = AK_COMPOSE(idx, 3, 0, 0); /* register 3 */
+ /* register 3 */
+ knew.private_value = AK_COMPOSE(idx, 3, 0, 0);
break;
case SND_AK4529: {
int shift = idx == 3 ? 6 : (2 - idx) * 2;
- ctl->private_value = AK_COMPOSE(0, 8, shift, 0); /* register 8 with shift */
+ /* register 8 with shift */
+ knew.private_value = AK_COMPOSE(0, 8, shift, 0);
break;
}
case SND_AK4355:
case SND_AK4358:
- ctl->private_value = AK_COMPOSE(idx, 3, 0, 0);
+ knew.private_value = AK_COMPOSE(idx, 3, 0, 0);
break;
case SND_AK4381:
- ctl->private_value = AK_COMPOSE(idx, 1, 1, 0);
+ knew.private_value = AK_COMPOSE(idx, 1, 1, 0);
break;
+ default:
+ return -EINVAL;
}
- ctl->private_data = ak;
- if ((err = snd_ctl_add(ak->card, snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE))) < 0)
- goto __error;
+ err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak));
+ if (err < 0)
+ return err;
}
- err = 0;
+ return 0;
+}
+
+int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak)
+{
+ int err, num_emphs;
+
+ err = build_dac_controls(ak);
+ if (err < 0)
+ return err;
+
+ err = build_adc_controls(ak);
+ if (err < 0)
+ return err;
+
+ if (ak->type == SND_AK4355 || ak->type == SND_AK4358)
+ num_emphs = 1;
+ else
+ num_emphs = ak->num_dacs / 2;
+ err = build_deemphasis(ak, num_emphs);
+ if (err < 0)
+ return err;
- __error:
- kfree(ctl);
- return err;
+ return 0;
}
+
+EXPORT_SYMBOL(snd_akm4xxx_build_controls);
static int __init alsa_akm4xxx_module_init(void)
{
@@ -503,8 +749,3 @@ static void __exit alsa_akm4xxx_module_exit(void)
module_init(alsa_akm4xxx_module_init)
module_exit(alsa_akm4xxx_module_exit)
-
-EXPORT_SYMBOL(snd_akm4xxx_write);
-EXPORT_SYMBOL(snd_akm4xxx_reset);
-EXPORT_SYMBOL(snd_akm4xxx_init);
-EXPORT_SYMBOL(snd_akm4xxx_build_controls);
diff --git a/sound/isa/ad1816a/ad1816a.c b/sound/isa/ad1816a/ad1816a.c
index 31f299aed281..b33a5fb59ec2 100644
--- a/sound/isa/ad1816a/ad1816a.c
+++ b/sound/isa/ad1816a/ad1816a.c
@@ -232,7 +232,7 @@ static int __devinit snd_card_ad1816a_probe(int dev, struct pnp_card_link *pcard
if (mpu_port[dev] > 0) {
if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
- mpu_port[dev], 0, mpu_irq[dev], SA_INTERRUPT,
+ mpu_port[dev], 0, mpu_irq[dev], IRQF_DISABLED,
NULL) < 0)
printk(KERN_ERR PFX "no MPU-401 device at 0x%lx.\n", mpu_port[dev]);
}
diff --git a/sound/isa/ad1816a/ad1816a_lib.c b/sound/isa/ad1816a/ad1816a_lib.c
index fd8fe16c09ee..fd9b61eda0f3 100644
--- a/sound/isa/ad1816a/ad1816a_lib.c
+++ b/sound/isa/ad1816a/ad1816a_lib.c
@@ -24,6 +24,7 @@
#include <linux/slab.h>
#include <linux/ioport.h>
#include <sound/core.h>
+#include <sound/tlv.h>
#include <sound/ad1816a.h>
#include <asm/io.h>
@@ -599,7 +600,7 @@ int __devinit snd_ad1816a_create(struct snd_card *card,
snd_ad1816a_free(chip);
return -EBUSY;
}
- if (request_irq(irq, snd_ad1816a_interrupt, SA_INTERRUPT, "AD1816A", (void *) chip)) {
+ if (request_irq(irq, snd_ad1816a_interrupt, IRQF_DISABLED, "AD1816A", (void *) chip)) {
snd_printk(KERN_ERR "ad1816a: can't grab IRQ %d\n", irq);
snd_ad1816a_free(chip);
return -EBUSY;
@@ -765,6 +766,13 @@ static int snd_ad1816a_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
return change;
}
+#define AD1816A_SINGLE_TLV(xname, reg, shift, mask, invert, xtlv) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
+ .name = xname, .info = snd_ad1816a_info_single, \
+ .get = snd_ad1816a_get_single, .put = snd_ad1816a_put_single, \
+ .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24), \
+ .tlv = { .p = (xtlv) } }
#define AD1816A_SINGLE(xname, reg, shift, mask, invert) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ad1816a_info_single, \
.get = snd_ad1816a_get_single, .put = snd_ad1816a_put_single, \
@@ -822,6 +830,14 @@ static int snd_ad1816a_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_
return change;
}
+#define AD1816A_DOUBLE_TLV(xname, reg, shift_left, shift_right, mask, invert, xtlv) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
+ .name = xname, .info = snd_ad1816a_info_double, \
+ .get = snd_ad1816a_get_double, .put = snd_ad1816a_put_double, \
+ .private_value = reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24), \
+ .tlv = { .p = (xtlv) } }
+
#define AD1816A_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ad1816a_info_double, \
.get = snd_ad1816a_get_double, .put = snd_ad1816a_put_double, \
@@ -890,28 +906,44 @@ static int snd_ad1816a_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
return change;
}
+static DECLARE_TLV_DB_SCALE(db_scale_4bit, -4500, 300, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_5bit, -4650, 150, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
+
static struct snd_kcontrol_new snd_ad1816a_controls[] __devinitdata = {
AD1816A_DOUBLE("Master Playback Switch", AD1816A_MASTER_ATT, 15, 7, 1, 1),
-AD1816A_DOUBLE("Master Playback Volume", AD1816A_MASTER_ATT, 8, 0, 31, 1),
+AD1816A_DOUBLE_TLV("Master Playback Volume", AD1816A_MASTER_ATT, 8, 0, 31, 1,
+ db_scale_5bit),
AD1816A_DOUBLE("PCM Playback Switch", AD1816A_VOICE_ATT, 15, 7, 1, 1),
-AD1816A_DOUBLE("PCM Playback Volume", AD1816A_VOICE_ATT, 8, 0, 63, 1),
+AD1816A_DOUBLE_TLV("PCM Playback Volume", AD1816A_VOICE_ATT, 8, 0, 63, 1,
+ db_scale_6bit),
AD1816A_DOUBLE("Line Playback Switch", AD1816A_LINE_GAIN_ATT, 15, 7, 1, 1),
-AD1816A_DOUBLE("Line Playback Volume", AD1816A_LINE_GAIN_ATT, 8, 0, 31, 1),
+AD1816A_DOUBLE_TLV("Line Playback Volume", AD1816A_LINE_GAIN_ATT, 8, 0, 31, 1,
+ db_scale_5bit_12db_max),
AD1816A_DOUBLE("CD Playback Switch", AD1816A_CD_GAIN_ATT, 15, 7, 1, 1),
-AD1816A_DOUBLE("CD Playback Volume", AD1816A_CD_GAIN_ATT, 8, 0, 31, 1),
+AD1816A_DOUBLE_TLV("CD Playback Volume", AD1816A_CD_GAIN_ATT, 8, 0, 31, 1,
+ db_scale_5bit_12db_max),
AD1816A_DOUBLE("Synth Playback Switch", AD1816A_SYNTH_GAIN_ATT, 15, 7, 1, 1),
-AD1816A_DOUBLE("Synth Playback Volume", AD1816A_SYNTH_GAIN_ATT, 8, 0, 31, 1),
+AD1816A_DOUBLE_TLV("Synth Playback Volume", AD1816A_SYNTH_GAIN_ATT, 8, 0, 31, 1,
+ db_scale_5bit_12db_max),
AD1816A_DOUBLE("FM Playback Switch", AD1816A_FM_ATT, 15, 7, 1, 1),
-AD1816A_DOUBLE("FM Playback Volume", AD1816A_FM_ATT, 8, 0, 63, 1),
+AD1816A_DOUBLE_TLV("FM Playback Volume", AD1816A_FM_ATT, 8, 0, 63, 1,
+ db_scale_6bit),
AD1816A_SINGLE("Mic Playback Switch", AD1816A_MIC_GAIN_ATT, 15, 1, 1),
-AD1816A_SINGLE("Mic Playback Volume", AD1816A_MIC_GAIN_ATT, 8, 31, 1),
+AD1816A_SINGLE_TLV("Mic Playback Volume", AD1816A_MIC_GAIN_ATT, 8, 31, 1,
+ db_scale_5bit_12db_max),
AD1816A_SINGLE("Mic Boost", AD1816A_MIC_GAIN_ATT, 14, 1, 0),
AD1816A_DOUBLE("Video Playback Switch", AD1816A_VID_GAIN_ATT, 15, 7, 1, 1),
-AD1816A_DOUBLE("Video Playback Volume", AD1816A_VID_GAIN_ATT, 8, 0, 31, 1),
+AD1816A_DOUBLE_TLV("Video Playback Volume", AD1816A_VID_GAIN_ATT, 8, 0, 31, 1,
+ db_scale_5bit_12db_max),
AD1816A_SINGLE("Phone Capture Switch", AD1816A_PHONE_IN_GAIN_ATT, 15, 1, 1),
-AD1816A_SINGLE("Phone Capture Volume", AD1816A_PHONE_IN_GAIN_ATT, 0, 15, 1),
+AD1816A_SINGLE_TLV("Phone Capture Volume", AD1816A_PHONE_IN_GAIN_ATT, 0, 15, 1,
+ db_scale_4bit),
AD1816A_SINGLE("Phone Playback Switch", AD1816A_PHONE_OUT_ATT, 7, 1, 1),
-AD1816A_SINGLE("Phone Playback Volume", AD1816A_PHONE_OUT_ATT, 0, 31, 1),
+AD1816A_SINGLE_TLV("Phone Playback Volume", AD1816A_PHONE_OUT_ATT, 0, 31, 1,
+ db_scale_5bit),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Source",
@@ -920,7 +952,8 @@ AD1816A_SINGLE("Phone Playback Volume", AD1816A_PHONE_OUT_ATT, 0, 31, 1),
.put = snd_ad1816a_put_mux,
},
AD1816A_DOUBLE("Capture Switch", AD1816A_ADC_PGA, 15, 7, 1, 1),
-AD1816A_DOUBLE("Capture Volume", AD1816A_ADC_PGA, 8, 0, 15, 0),
+AD1816A_DOUBLE_TLV("Capture Volume", AD1816A_ADC_PGA, 8, 0, 15, 0,
+ db_scale_rec_gain),
AD1816A_SINGLE("3D Control - Switch", AD1816A_3D_PHAT_CTRL, 15, 1, 1),
AD1816A_SINGLE("3D Control - Level", AD1816A_3D_PHAT_CTRL, 0, 15, 0),
};
diff --git a/sound/isa/ad1848/ad1848_lib.c b/sound/isa/ad1848/ad1848_lib.c
index e0f8baa843b9..a6fbd5d1d62f 100644
--- a/sound/isa/ad1848/ad1848_lib.c
+++ b/sound/isa/ad1848/ad1848_lib.c
@@ -29,6 +29,7 @@
#include <sound/core.h>
#include <sound/ad1848.h>
#include <sound/control.h>
+#include <sound/tlv.h>
#include <sound/pcm_params.h>
#include <asm/io.h>
@@ -118,6 +119,8 @@ void snd_ad1848_out(struct snd_ad1848 *chip,
#endif
}
+EXPORT_SYMBOL(snd_ad1848_out);
+
static void snd_ad1848_dout(struct snd_ad1848 *chip,
unsigned char reg, unsigned char value)
{
@@ -902,7 +905,7 @@ int snd_ad1848_create(struct snd_card *card,
snd_ad1848_free(chip);
return -EBUSY;
}
- if (request_irq(irq, snd_ad1848_interrupt, SA_INTERRUPT, "AD1848", (void *) chip)) {
+ if (request_irq(irq, snd_ad1848_interrupt, IRQF_DISABLED, "AD1848", (void *) chip)) {
snd_printk(KERN_ERR "ad1848: can't grab IRQ %d\n", irq);
snd_ad1848_free(chip);
return -EBUSY;
@@ -941,6 +944,8 @@ int snd_ad1848_create(struct snd_card *card,
return 0;
}
+EXPORT_SYMBOL(snd_ad1848_create);
+
static struct snd_pcm_ops snd_ad1848_playback_ops = {
.open = snd_ad1848_playback_open,
.close = snd_ad1848_playback_close,
@@ -988,12 +993,16 @@ int snd_ad1848_pcm(struct snd_ad1848 *chip, int device, struct snd_pcm **rpcm)
return 0;
}
+EXPORT_SYMBOL(snd_ad1848_pcm);
+
const struct snd_pcm_ops *snd_ad1848_get_pcm_ops(int direction)
{
return direction == SNDRV_PCM_STREAM_PLAYBACK ?
&snd_ad1848_playback_ops : &snd_ad1848_capture_ops;
}
+EXPORT_SYMBOL(snd_ad1848_get_pcm_ops);
+
/*
* MIXER part
*/
@@ -1171,7 +1180,8 @@ static int snd_ad1848_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
/*
*/
-int snd_ad1848_add_ctl(struct snd_ad1848 *chip, const char *name, int index, int type, unsigned long value)
+int snd_ad1848_add_ctl_elem(struct snd_ad1848 *chip,
+ const struct ad1848_mix_elem *c)
{
static struct snd_kcontrol_new newctls[] = {
[AD1848_MIX_SINGLE] = {
@@ -1196,32 +1206,46 @@ int snd_ad1848_add_ctl(struct snd_ad1848 *chip, const char *name, int index, int
struct snd_kcontrol *ctl;
int err;
- ctl = snd_ctl_new1(&newctls[type], chip);
+ ctl = snd_ctl_new1(&newctls[c->type], chip);
if (! ctl)
return -ENOMEM;
- strlcpy(ctl->id.name, name, sizeof(ctl->id.name));
- ctl->id.index = index;
- ctl->private_value = value;
+ strlcpy(ctl->id.name, c->name, sizeof(ctl->id.name));
+ ctl->id.index = c->index;
+ ctl->private_value = c->private_value;
+ if (c->tlv) {
+ ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+ ctl->tlv.p = c->tlv;
+ }
if ((err = snd_ctl_add(chip->card, ctl)) < 0)
return err;
return 0;
}
+EXPORT_SYMBOL(snd_ad1848_add_ctl_elem);
+
+static DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
static struct ad1848_mix_elem snd_ad1848_controls[] = {
AD1848_DOUBLE("PCM Playback Switch", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 7, 7, 1, 1),
-AD1848_DOUBLE("PCM Playback Volume", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 0, 0, 63, 1),
+AD1848_DOUBLE_TLV("PCM Playback Volume", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 0, 0, 63, 1,
+ db_scale_6bit),
AD1848_DOUBLE("Aux Playback Switch", 0, AD1848_AUX1_LEFT_INPUT, AD1848_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
-AD1848_DOUBLE("Aux Playback Volume", 0, AD1848_AUX1_LEFT_INPUT, AD1848_AUX1_RIGHT_INPUT, 0, 0, 31, 1),
+AD1848_DOUBLE_TLV("Aux Playback Volume", 0, AD1848_AUX1_LEFT_INPUT, AD1848_AUX1_RIGHT_INPUT, 0, 0, 31, 1,
+ db_scale_5bit_12db_max),
AD1848_DOUBLE("Aux Playback Switch", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
-AD1848_DOUBLE("Aux Playback Volume", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
-AD1848_DOUBLE("Capture Volume", 0, AD1848_LEFT_INPUT, AD1848_RIGHT_INPUT, 0, 0, 15, 0),
+AD1848_DOUBLE_TLV("Aux Playback Volume", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 0, 0, 31, 1,
+ db_scale_5bit_12db_max),
+AD1848_DOUBLE_TLV("Capture Volume", 0, AD1848_LEFT_INPUT, AD1848_RIGHT_INPUT, 0, 0, 15, 0,
+ db_scale_rec_gain),
{
.name = "Capture Source",
.type = AD1848_MIX_CAPTURE,
},
AD1848_SINGLE("Loopback Capture Switch", 0, AD1848_LOOPBACK, 0, 1, 0),
-AD1848_SINGLE("Loopback Capture Volume", 0, AD1848_LOOPBACK, 1, 63, 0)
+AD1848_SINGLE_TLV("Loopback Capture Volume", 0, AD1848_LOOPBACK, 1, 63, 0,
+ db_scale_6bit),
};
int snd_ad1848_mixer(struct snd_ad1848 *chip)
@@ -1245,12 +1269,7 @@ int snd_ad1848_mixer(struct snd_ad1848 *chip)
return 0;
}
-EXPORT_SYMBOL(snd_ad1848_out);
-EXPORT_SYMBOL(snd_ad1848_create);
-EXPORT_SYMBOL(snd_ad1848_pcm);
-EXPORT_SYMBOL(snd_ad1848_get_pcm_ops);
EXPORT_SYMBOL(snd_ad1848_mixer);
-EXPORT_SYMBOL(snd_ad1848_add_ctl);
/*
* INIT part
diff --git a/sound/isa/als100.c b/sound/isa/als100.c
index a52bd8a14c9b..f2bcfb2cf5f5 100644
--- a/sound/isa/als100.c
+++ b/sound/isa/als100.c
@@ -250,7 +250,7 @@ static int __devinit snd_card_als100_probe(int dev,
if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) {
if (snd_mpu401_uart_new(card, 0, MPU401_HW_ALS100,
mpu_port[dev], 0,
- mpu_irq[dev], SA_INTERRUPT,
+ mpu_irq[dev], IRQF_DISABLED,
NULL) < 0)
snd_printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n", mpu_port[dev]);
}
diff --git a/sound/isa/azt2320.c b/sound/isa/azt2320.c
index 15e59283aac6..b615538a928d 100644
--- a/sound/isa/azt2320.c
+++ b/sound/isa/azt2320.c
@@ -279,7 +279,7 @@ static int __devinit snd_card_azt2320_probe(int dev,
if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) {
if (snd_mpu401_uart_new(card, 0, MPU401_HW_AZT2320,
mpu_port[dev], 0,
- mpu_irq[dev], SA_INTERRUPT,
+ mpu_irq[dev], IRQF_DISABLED,
NULL) < 0)
snd_printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n", mpu_port[dev]);
}
diff --git a/sound/isa/cs423x/Makefile b/sound/isa/cs423x/Makefile
index d2afaea30cbc..2fb4f7409d7c 100644
--- a/sound/isa/cs423x/Makefile
+++ b/sound/isa/cs423x/Makefile
@@ -11,6 +11,7 @@ snd-cs4236-objs := cs4236.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_AZT2320) += snd-cs4231-lib.o
+obj-$(CONFIG_SND_MIRO) += snd-cs4231-lib.o
obj-$(CONFIG_SND_OPL3SA2) += snd-cs4231-lib.o
obj-$(CONFIG_SND_CS4231) += snd-cs4231.o snd-cs4231-lib.o
obj-$(CONFIG_SND_CS4232) += snd-cs4232.o snd-cs4231-lib.o
diff --git a/sound/isa/cs423x/cs4231.c b/sound/isa/cs423x/cs4231.c
index 397310f358b7..696a5c86bcfa 100644
--- a/sound/isa/cs423x/cs4231.c
+++ b/sound/isa/cs423x/cs4231.c
@@ -122,7 +122,7 @@ static int __init snd_cs4231_probe(struct platform_device *pdev)
if (snd_mpu401_uart_new(card, 0, MPU401_HW_CS4232,
mpu_port[dev], 0,
mpu_irq[dev],
- mpu_irq[dev] >= 0 ? SA_INTERRUPT : 0,
+ mpu_irq[dev] >= 0 ? IRQF_DISABLED : 0,
NULL) < 0)
printk(KERN_WARNING "cs4231: MPU401 not detected\n");
}
diff --git a/sound/isa/cs423x/cs4231_lib.c b/sound/isa/cs423x/cs4231_lib.c
index 823db8246701..fbb20176cca4 100644
--- a/sound/isa/cs423x/cs4231_lib.c
+++ b/sound/isa/cs423x/cs4231_lib.c
@@ -1454,7 +1454,7 @@ int snd_cs4231_create(struct snd_card *card,
return -ENODEV;
}
chip->cport = cport;
- if (!(hwshare & CS4231_HWSHARE_IRQ) && request_irq(irq, snd_cs4231_interrupt, SA_INTERRUPT, "CS4231", (void *) chip)) {
+ if (!(hwshare & CS4231_HWSHARE_IRQ) && request_irq(irq, snd_cs4231_interrupt, IRQF_DISABLED, "CS4231", (void *) chip)) {
snd_printk(KERN_ERR "cs4231: can't grab IRQ %d\n", irq);
snd_cs4231_free(chip);
return -EBUSY;
diff --git a/sound/isa/cs423x/cs4236.c b/sound/isa/cs423x/cs4236.c
index f7fa779347ff..07ffd5c22e81 100644
--- a/sound/isa/cs423x/cs4236.c
+++ b/sound/isa/cs423x/cs4236.c
@@ -535,7 +535,7 @@ static int __devinit snd_cs423x_probe(struct snd_card *card, int dev)
if (snd_mpu401_uart_new(card, 0, MPU401_HW_CS4232,
mpu_port[dev], 0,
mpu_irq[dev],
- mpu_irq[dev] >= 0 ? SA_INTERRUPT : 0, NULL) < 0)
+ mpu_irq[dev] >= 0 ? IRQF_DISABLED : 0, NULL) < 0)
printk(KERN_WARNING IDENT ": MPU401 not detected\n");
}
diff --git a/sound/isa/dt019x.c b/sound/isa/dt019x.c
index 0acb4e5da47f..c20a4b1b1536 100644
--- a/sound/isa/dt019x.c
+++ b/sound/isa/dt019x.c
@@ -240,7 +240,7 @@ static int __devinit snd_card_dt019x_probe(int dev, struct pnp_card_link *pcard,
MPU401_HW_MPU401,
mpu_port[dev], 0,
mpu_irq[dev],
- mpu_irq[dev] >= 0 ? SA_INTERRUPT : 0,
+ mpu_irq[dev] >= 0 ? IRQF_DISABLED : 0,
NULL) < 0)
snd_printk(KERN_ERR PFX "no MPU-401 device at 0x%lx ?\n", mpu_port[dev]);
}
diff --git a/sound/isa/es1688/es1688.c b/sound/isa/es1688/es1688.c
index e90689ee162f..7f29f56e405b 100644
--- a/sound/isa/es1688/es1688.c
+++ b/sound/isa/es1688/es1688.c
@@ -153,7 +153,7 @@ static int __init snd_es1688_probe(struct platform_device *pdev)
if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES1688,
chip->mpu_port, 0,
xmpu_irq,
- SA_INTERRUPT,
+ IRQF_DISABLED,
NULL)) < 0)
goto _err;
}
diff --git a/sound/isa/es1688/es1688_lib.c b/sound/isa/es1688/es1688_lib.c
index 702ad51ee9d5..7e985d3bc510 100644
--- a/sound/isa/es1688/es1688_lib.c
+++ b/sound/isa/es1688/es1688_lib.c
@@ -659,7 +659,7 @@ int snd_es1688_create(struct snd_card *card,
snd_es1688_free(chip);
return -EBUSY;
}
- if (request_irq(irq, snd_es1688_interrupt, SA_INTERRUPT, "ES1688", (void *) chip)) {
+ if (request_irq(irq, snd_es1688_interrupt, IRQF_DISABLED, "ES1688", (void *) chip)) {
snd_printk(KERN_ERR "es1688: can't grab IRQ %d\n", irq);
snd_es1688_free(chip);
return -EBUSY;
diff --git a/sound/isa/es18xx.c b/sound/isa/es18xx.c
index e6945db8ed1b..85818200333f 100644
--- a/sound/isa/es18xx.c
+++ b/sound/isa/es18xx.c
@@ -1837,7 +1837,7 @@ static int __devinit snd_es18xx_new_device(struct snd_card *card,
return -EBUSY;
}
- if (request_irq(irq, snd_es18xx_interrupt, SA_INTERRUPT, "ES18xx", (void *) chip)) {
+ if (request_irq(irq, snd_es18xx_interrupt, IRQF_DISABLED, "ES18xx", (void *) chip)) {
snd_es18xx_free(chip);
snd_printk(KERN_ERR PFX "unable to grap IRQ %d\n", irq);
return -EBUSY;
@@ -2038,7 +2038,80 @@ MODULE_PARM_DESC(dma2, "DMA 2 # for ES18xx driver.");
static struct platform_device *platform_devices[SNDRV_CARDS];
#ifdef CONFIG_PNP
-static int pnp_registered;
+static int pnp_registered, pnpc_registered;
+
+static struct pnp_device_id snd_audiodrive_pnpbiosids[] = {
+ { .id = "ESS1869" },
+ { .id = "" } /* end */
+};
+
+MODULE_DEVICE_TABLE(pnp, snd_audiodrive_pnpbiosids);
+
+/* PnP main device initialization */
+static int __devinit snd_audiodrive_pnp_init_main(int dev, struct pnp_dev *pdev,
+ struct pnp_resource_table *cfg)
+{
+ int err;
+
+ pnp_init_resource_table(cfg);
+ if (port[dev] != SNDRV_AUTO_PORT)
+ pnp_resource_change(&cfg->port_resource[0], port[dev], 16);
+ if (fm_port[dev] != SNDRV_AUTO_PORT)
+ pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4);
+ if (mpu_port[dev] != SNDRV_AUTO_PORT)
+ pnp_resource_change(&cfg->port_resource[2], mpu_port[dev], 2);
+ if (dma1[dev] != SNDRV_AUTO_DMA)
+ pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1);
+ if (dma2[dev] != SNDRV_AUTO_DMA)
+ pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1);
+ if (irq[dev] != SNDRV_AUTO_IRQ)
+ pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1);
+ if (pnp_device_is_isapnp(pdev)) {
+ err = pnp_manual_config_dev(pdev, cfg, 0);
+ if (err < 0)
+ snd_printk(KERN_ERR PFX "PnP manual resources are invalid, using auto config\n");
+ }
+ err = pnp_activate_dev(pdev);
+ if (err < 0) {
+ snd_printk(KERN_ERR PFX "PnP configure failure (out of resources?)\n");
+ return -EBUSY;
+ }
+ /* ok. hack using Vendor-Defined Card-Level registers */
+ /* skip csn and logdev initialization - already done in isapnp_configure */
+ if (pnp_device_is_isapnp(pdev)) {
+ isapnp_cfg_begin(isapnp_card_number(pdev), isapnp_csn_number(pdev));
+ isapnp_write_byte(0x27, pnp_irq(pdev, 0)); /* Hardware Volume IRQ Number */
+ if (mpu_port[dev] != SNDRV_AUTO_PORT)
+ isapnp_write_byte(0x28, pnp_irq(pdev, 0)); /* MPU-401 IRQ Number */
+ isapnp_write_byte(0x72, pnp_irq(pdev, 0)); /* second IRQ */
+ isapnp_cfg_end();
+ }
+ port[dev] = pnp_port_start(pdev, 0);
+ fm_port[dev] = pnp_port_start(pdev, 1);
+ mpu_port[dev] = pnp_port_start(pdev, 2);
+ dma1[dev] = pnp_dma(pdev, 0);
+ dma2[dev] = pnp_dma(pdev, 1);
+ irq[dev] = pnp_irq(pdev, 0);
+ snd_printdd("PnP ES18xx: port=0x%lx, fm port=0x%lx, mpu port=0x%lx\n", port[dev], fm_port[dev], mpu_port[dev]);
+ snd_printdd("PnP ES18xx: dma1=%i, dma2=%i, irq=%i\n", dma1[dev], dma2[dev], irq[dev]);
+ return 0;
+}
+
+static int __devinit snd_audiodrive_pnp(int dev, struct snd_audiodrive *acard,
+ struct pnp_dev *pdev)
+{
+ struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL);
+
+ if (!cfg)
+ return -ENOMEM;
+ acard->dev = pdev;
+ if (snd_audiodrive_pnp_init_main(dev, acard->dev, cfg) < 0) {
+ kfree(cfg);
+ return -EBUSY;
+ }
+ kfree(cfg);
+ return 0;
+}
static struct pnp_card_device_id snd_audiodrive_pnpids[] = {
/* ESS 1868 (integrated on Compaq dual P-Pro motherboard and Genius 18PnP 3D) */
@@ -2061,13 +2134,11 @@ static struct pnp_card_device_id snd_audiodrive_pnpids[] = {
MODULE_DEVICE_TABLE(pnp_card, snd_audiodrive_pnpids);
-static int __devinit snd_audiodrive_pnp(int dev, struct snd_audiodrive *acard,
+static int __devinit snd_audiodrive_pnpc(int dev, struct snd_audiodrive *acard,
struct pnp_card_link *card,
const struct pnp_card_device_id *id)
{
- struct pnp_dev *pdev;
struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL);
- int err;
if (!cfg)
return -ENOMEM;
@@ -2082,57 +2153,16 @@ static int __devinit snd_audiodrive_pnp(int dev, struct snd_audiodrive *acard,
return -EBUSY;
}
/* Control port initialization */
- err = pnp_activate_dev(acard->devc);
- if (err < 0) {
+ if (pnp_activate_dev(acard->devc) < 0) {
snd_printk(KERN_ERR PFX "PnP control configure failure (out of resources?)\n");
- kfree(cfg);
return -EAGAIN;
}
- snd_printdd("pnp: port=0x%lx\n", pnp_port_start(acard->devc, 0));
- /* PnP initialization */
- pdev = acard->dev;
- pnp_init_resource_table(cfg);
- if (port[dev] != SNDRV_AUTO_PORT)
- pnp_resource_change(&cfg->port_resource[0], port[dev], 16);
- if (fm_port[dev] != SNDRV_AUTO_PORT)
- pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4);
- if (mpu_port[dev] != SNDRV_AUTO_PORT)
- pnp_resource_change(&cfg->port_resource[2], mpu_port[dev], 2);
- if (dma1[dev] != SNDRV_AUTO_DMA)
- pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1);
- if (dma2[dev] != SNDRV_AUTO_DMA)
- pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1);
- if (irq[dev] != SNDRV_AUTO_IRQ)
- pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1);
- err = pnp_manual_config_dev(pdev, cfg, 0);
- if (err < 0)
- snd_printk(KERN_ERR PFX "PnP manual resources are invalid, using auto config\n");
- err = pnp_activate_dev(pdev);
- if (err < 0) {
- snd_printk(KERN_ERR PFX "PnP configure failure (out of resources?)\n");
+ snd_printdd("pnp: port=0x%llx\n",
+ (unsigned long long)pnp_port_start(acard->devc, 0));
+ if (snd_audiodrive_pnp_init_main(dev, acard->dev, cfg) < 0) {
kfree(cfg);
return -EBUSY;
}
- /* ok. hack using Vendor-Defined Card-Level registers */
- /* skip csn and logdev initialization - already done in isapnp_configure */
- if (pnp_device_is_isapnp(pdev)) {
- isapnp_cfg_begin(isapnp_card_number(pdev), isapnp_csn_number(pdev));
- isapnp_write_byte(0x27, pnp_irq(pdev, 0)); /* Hardware Volume IRQ Number */
- if (mpu_port[dev] != SNDRV_AUTO_PORT)
- isapnp_write_byte(0x28, pnp_irq(pdev, 0)); /* MPU-401 IRQ Number */
- isapnp_write_byte(0x72, pnp_irq(pdev, 0)); /* second IRQ */
- isapnp_cfg_end();
- } else {
- snd_printk(KERN_ERR PFX "unable to install ISA PnP hack, expect malfunction\n");
- }
- port[dev] = pnp_port_start(pdev, 0);
- fm_port[dev] = pnp_port_start(pdev, 1);
- mpu_port[dev] = pnp_port_start(pdev, 2);
- dma1[dev] = pnp_dma(pdev, 0);
- dma2[dev] = pnp_dma(pdev, 1);
- irq[dev] = pnp_irq(pdev, 0);
- snd_printdd("PnP ES18xx: port=0x%lx, fm port=0x%lx, mpu port=0x%lx\n", port[dev], fm_port[dev], mpu_port[dev]);
- snd_printdd("PnP ES18xx: dma1=%i, dma2=%i, irq=%i\n", dma1[dev], dma2[dev], irq[dev]);
kfree(cfg);
return 0;
}
@@ -2301,7 +2331,69 @@ static struct platform_driver snd_es18xx_nonpnp_driver = {
#ifdef CONFIG_PNP
static unsigned int __devinitdata es18xx_pnp_devices;
-static int __devinit snd_audiodrive_pnp_detect(struct pnp_card_link *pcard,
+static int __devinit snd_audiodrive_pnp_detect(struct pnp_dev *pdev,
+ const struct pnp_device_id *id)
+{
+ static int dev;
+ int err;
+ struct snd_card *card;
+
+ if (pnp_device_is_isapnp(pdev))
+ return -ENOENT; /* we have another procedure - card */
+ for (; dev < SNDRV_CARDS; dev++) {
+ if (enable[dev] && isapnp[dev])
+ break;
+ }
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+
+ card = snd_es18xx_card_new(dev);
+ if (! card)
+ return -ENOMEM;
+ if ((err = snd_audiodrive_pnp(dev, card->private_data, pdev)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ snd_card_set_dev(card, &pdev->dev);
+ if ((err = snd_audiodrive_probe(card, dev)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ pnp_set_drvdata(pdev, card);
+ dev++;
+ es18xx_pnp_devices++;
+ return 0;
+}
+
+static void __devexit snd_audiodrive_pnp_remove(struct pnp_dev * pdev)
+{
+ snd_card_free(pnp_get_drvdata(pdev));
+ pnp_set_drvdata(pdev, NULL);
+}
+
+#ifdef CONFIG_PM
+static int snd_audiodrive_pnp_suspend(struct pnp_dev *pdev, pm_message_t state)
+{
+ return snd_es18xx_suspend(pnp_get_drvdata(pdev), state);
+}
+static int snd_audiodrive_pnp_resume(struct pnp_dev *pdev)
+{
+ return snd_es18xx_resume(pnp_get_drvdata(pdev));
+}
+#endif
+
+static struct pnp_driver es18xx_pnp_driver = {
+ .name = "es18xx-pnpbios",
+ .id_table = snd_audiodrive_pnpbiosids,
+ .probe = snd_audiodrive_pnp_detect,
+ .remove = __devexit_p(snd_audiodrive_pnp_remove),
+#ifdef CONFIG_PM
+ .suspend = snd_audiodrive_pnp_suspend,
+ .resume = snd_audiodrive_pnp_resume,
+#endif
+};
+
+static int __devinit snd_audiodrive_pnpc_detect(struct pnp_card_link *pcard,
const struct pnp_card_device_id *pid)
{
static int dev;
@@ -2319,7 +2411,7 @@ static int __devinit snd_audiodrive_pnp_detect(struct pnp_card_link *pcard,
if (! card)
return -ENOMEM;
- if ((res = snd_audiodrive_pnp(dev, card->private_data, pcard, pid)) < 0) {
+ if ((res = snd_audiodrive_pnpc(dev, card->private_data, pcard, pid)) < 0) {
snd_card_free(card);
return res;
}
@@ -2335,19 +2427,19 @@ static int __devinit snd_audiodrive_pnp_detect(struct pnp_card_link *pcard,
return 0;
}
-static void __devexit snd_audiodrive_pnp_remove(struct pnp_card_link * pcard)
+static void __devexit snd_audiodrive_pnpc_remove(struct pnp_card_link * pcard)
{
snd_card_free(pnp_get_card_drvdata(pcard));
pnp_set_card_drvdata(pcard, NULL);
}
#ifdef CONFIG_PM
-static int snd_audiodrive_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state)
+static int snd_audiodrive_pnpc_suspend(struct pnp_card_link *pcard, pm_message_t state)
{
return snd_es18xx_suspend(pnp_get_card_drvdata(pcard), state);
}
-static int snd_audiodrive_pnp_resume(struct pnp_card_link *pcard)
+static int snd_audiodrive_pnpc_resume(struct pnp_card_link *pcard)
{
return snd_es18xx_resume(pnp_get_card_drvdata(pcard));
}
@@ -2358,11 +2450,11 @@ static struct pnp_card_driver es18xx_pnpc_driver = {
.flags = PNP_DRIVER_RES_DISABLE,
.name = "es18xx",
.id_table = snd_audiodrive_pnpids,
- .probe = snd_audiodrive_pnp_detect,
- .remove = __devexit_p(snd_audiodrive_pnp_remove),
+ .probe = snd_audiodrive_pnpc_detect,
+ .remove = __devexit_p(snd_audiodrive_pnpc_remove),
#ifdef CONFIG_PM
- .suspend = snd_audiodrive_pnp_suspend,
- .resume = snd_audiodrive_pnp_resume,
+ .suspend = snd_audiodrive_pnpc_suspend,
+ .resume = snd_audiodrive_pnpc_resume,
#endif
};
#endif /* CONFIG_PNP */
@@ -2372,8 +2464,10 @@ static void __init_or_module snd_es18xx_unregister_all(void)
int i;
#ifdef CONFIG_PNP
- if (pnp_registered)
+ if (pnpc_registered)
pnp_unregister_card_driver(&es18xx_pnpc_driver);
+ if (pnp_registered)
+ pnp_unregister_driver(&es18xx_pnp_driver);
#endif
for (i = 0; i < ARRAY_SIZE(platform_devices); ++i)
platform_device_unregister(platform_devices[i]);
@@ -2404,11 +2498,13 @@ static int __init alsa_card_es18xx_init(void)
}
#ifdef CONFIG_PNP
- err = pnp_register_card_driver(&es18xx_pnpc_driver);
- if (!err) {
+ err = pnp_register_driver(&es18xx_pnp_driver);
+ if (!err)
pnp_registered = 1;
- cards += es18xx_pnp_devices;
- }
+ err = pnp_register_card_driver(&es18xx_pnpc_driver);
+ if (!err)
+ pnpc_registered = 1;
+ cards += es18xx_pnp_devices;
#endif
if(!cards) {
diff --git a/sound/isa/gus/gus_main.c b/sound/isa/gus/gus_main.c
index 53eeaf37007d..b680fddf0d74 100644
--- a/sound/isa/gus/gus_main.c
+++ b/sound/isa/gus/gus_main.c
@@ -179,7 +179,7 @@ int snd_gus_create(struct snd_card *card,
snd_gus_free(gus);
return -EBUSY;
}
- if (irq >= 0 && request_irq(irq, snd_gus_interrupt, SA_INTERRUPT, "GUS GF1", (void *) gus)) {
+ if (irq >= 0 && request_irq(irq, snd_gus_interrupt, IRQF_DISABLED, "GUS GF1", (void *) gus)) {
snd_printk(KERN_ERR "gus: can't grab irq %d\n", irq);
snd_gus_free(gus);
return -EBUSY;
diff --git a/sound/isa/gus/gus_mem_proc.c b/sound/isa/gus/gus_mem_proc.c
index 4080255007d5..80f0a83818b2 100644
--- a/sound/isa/gus/gus_mem_proc.c
+++ b/sound/isa/gus/gus_mem_proc.c
@@ -61,13 +61,13 @@ static long long snd_gf1_mem_proc_llseek(struct snd_info_entry *entry,
struct gus_proc_private *priv = entry->private_data;
switch (orig) {
- case 0: /* SEEK_SET */
+ case SEEK_SET:
file->f_pos = offset;
break;
- case 1: /* SEEK_CUR */
+ case SEEK_CUR:
file->f_pos += offset;
break;
- case 2: /* SEEK_END, offset is negative */
+ case SEEK_END: /* offset is negative */
file->f_pos = priv->size + offset;
break;
default:
diff --git a/sound/isa/gus/gusextreme.c b/sound/isa/gus/gusextreme.c
index 05852fcc613b..532c56e35ca4 100644
--- a/sound/isa/gus/gusextreme.c
+++ b/sound/isa/gus/gusextreme.c
@@ -87,7 +87,7 @@ MODULE_PARM_DESC(channels, "GF1 channels for GUS Extreme driver.");
module_param_array(pcm_channels, int, NULL, 0444);
MODULE_PARM_DESC(pcm_channels, "Reserved PCM channels for GUS Extreme driver.");
-struct platform_device *devices[SNDRV_CARDS];
+static struct platform_device *devices[SNDRV_CARDS];
#define PFX "gusextreme: "
@@ -301,7 +301,7 @@ static int __init snd_gusextreme_probe(struct platform_device *pdev)
(err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES1688,
es1688->mpu_port, 0,
xmpu_irq,
- SA_INTERRUPT,
+ IRQF_DISABLED,
NULL)) < 0)
goto out;
diff --git a/sound/isa/gus/gusmax.c b/sound/isa/gus/gusmax.c
index fcf2c8fe6f94..ac11cae8589a 100644
--- a/sound/isa/gus/gusmax.c
+++ b/sound/isa/gus/gusmax.c
@@ -292,7 +292,7 @@ static int __init snd_gusmax_probe(struct platform_device *pdev)
goto _err;
}
- if (request_irq(xirq, snd_gusmax_interrupt, SA_INTERRUPT, "GUS MAX", (void *)maxcard)) {
+ if (request_irq(xirq, snd_gusmax_interrupt, IRQF_DISABLED, "GUS MAX", (void *)maxcard)) {
snd_printk(KERN_ERR PFX "unable to grab IRQ %d\n", xirq);
err = -EBUSY;
goto _err;
diff --git a/sound/isa/gus/interwave.c b/sound/isa/gus/interwave.c
index 866300f2acbb..ea69f25506fb 100644
--- a/sound/isa/gus/interwave.c
+++ b/sound/isa/gus/interwave.c
@@ -611,10 +611,10 @@ static int __devinit snd_interwave_pnp(int dev, struct snd_interwave *iwcard,
if (dma2[dev] >= 0)
dma2[dev] = pnp_dma(pdev, 1);
irq[dev] = pnp_irq(pdev, 0);
- snd_printdd("isapnp IW: sb port=0x%lx, gf1 port=0x%lx, codec port=0x%lx\n",
- pnp_port_start(pdev, 0),
- pnp_port_start(pdev, 1),
- pnp_port_start(pdev, 2));
+ snd_printdd("isapnp IW: sb port=0x%llx, gf1 port=0x%llx, codec port=0x%llx\n",
+ (unsigned long long)pnp_port_start(pdev, 0),
+ (unsigned long long)pnp_port_start(pdev, 1),
+ (unsigned long long)pnp_port_start(pdev, 2));
snd_printdd("isapnp IW: dma1=%i, dma2=%i, irq=%i\n", dma1[dev], dma2[dev], irq[dev]);
#ifdef SNDRV_STB
/* Tone Control initialization */
@@ -706,7 +706,7 @@ static int __devinit snd_interwave_probe(struct snd_card *card, int dev)
if ((err = snd_gus_initialize(gus)) < 0)
return err;
- if (request_irq(xirq, snd_interwave_interrupt, SA_INTERRUPT,
+ if (request_irq(xirq, snd_interwave_interrupt, IRQF_DISABLED,
"InterWave", iwcard)) {
snd_printk(KERN_ERR PFX "unable to grab IRQ %d\n", xirq);
return -EBUSY;
diff --git a/sound/isa/opl3sa2.c b/sound/isa/opl3sa2.c
index 647a996791e9..da92bf6c392b 100644
--- a/sound/isa/opl3sa2.c
+++ b/sound/isa/opl3sa2.c
@@ -33,6 +33,7 @@
#include <sound/mpu401.h>
#include <sound/opl3.h>
#include <sound/initval.h>
+#include <sound/tlv.h>
#include <asm/io.h>
@@ -337,6 +338,14 @@ static irqreturn_t snd_opl3sa2_interrupt(int irq, void *dev_id, struct pt_regs *
.info = snd_opl3sa2_info_single, \
.get = snd_opl3sa2_get_single, .put = snd_opl3sa2_put_single, \
.private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
+#define OPL3SA2_SINGLE_TLV(xname, xindex, reg, shift, mask, invert, xtlv) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
+ .name = xname, .index = xindex, \
+ .info = snd_opl3sa2_info_single, \
+ .get = snd_opl3sa2_get_single, .put = snd_opl3sa2_put_single, \
+ .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24), \
+ .tlv = { .p = (xtlv) } }
static int snd_opl3sa2_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
@@ -395,6 +404,14 @@ static int snd_opl3sa2_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_
.info = snd_opl3sa2_info_double, \
.get = snd_opl3sa2_get_double, .put = snd_opl3sa2_put_double, \
.private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+#define OPL3SA2_DOUBLE_TLV(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert, xtlv) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
+ .name = xname, .index = xindex, \
+ .info = snd_opl3sa2_info_double, \
+ .get = snd_opl3sa2_get_double, .put = snd_opl3sa2_put_double, \
+ .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22), \
+ .tlv = { .p = (xtlv) } }
static int snd_opl3sa2_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
@@ -469,11 +486,16 @@ static int snd_opl3sa2_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
return change;
}
+static DECLARE_TLV_DB_SCALE(db_scale_master, -3000, 200, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
+
static struct snd_kcontrol_new snd_opl3sa2_controls[] = {
OPL3SA2_DOUBLE("Master Playback Switch", 0, 0x07, 0x08, 7, 7, 1, 1),
-OPL3SA2_DOUBLE("Master Playback Volume", 0, 0x07, 0x08, 0, 0, 15, 1),
+OPL3SA2_DOUBLE_TLV("Master Playback Volume", 0, 0x07, 0x08, 0, 0, 15, 1,
+ db_scale_master),
OPL3SA2_SINGLE("Mic Playback Switch", 0, 0x09, 7, 1, 1),
-OPL3SA2_SINGLE("Mic Playback Volume", 0, 0x09, 0, 31, 1)
+OPL3SA2_SINGLE_TLV("Mic Playback Volume", 0, 0x09, 0, 31, 1,
+ db_scale_5bit_12db_max),
};
static struct snd_kcontrol_new snd_opl3sa2_tone_controls[] = {
@@ -683,7 +705,7 @@ static int __devinit snd_opl3sa2_probe(struct snd_card *card, int dev)
chip->single_dma = 1;
if ((err = snd_opl3sa2_detect(chip)) < 0)
return err;
- if (request_irq(xirq, snd_opl3sa2_interrupt, SA_INTERRUPT, "OPL3-SA2", chip)) {
+ if (request_irq(xirq, snd_opl3sa2_interrupt, IRQF_DISABLED, "OPL3-SA2", chip)) {
snd_printk(KERN_ERR PFX "can't grab IRQ %d\n", xirq);
return -ENODEV;
}
diff --git a/sound/isa/opti9xx/miro.c b/sound/isa/opti9xx/miro.c
index 283817f2de75..1dd98375ac85 100644
--- a/sound/isa/opti9xx/miro.c
+++ b/sound/isa/opti9xx/miro.c
@@ -1382,7 +1382,7 @@ static int __init snd_miro_probe(struct platform_device *devptr)
rmidi = NULL;
else
if ((error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
- miro->mpu_port, 0, miro->mpu_irq, SA_INTERRUPT,
+ miro->mpu_port, 0, miro->mpu_irq, IRQF_DISABLED,
&rmidi)))
snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n", miro->mpu_port);
diff --git a/sound/isa/opti9xx/opti92x-ad1848.c b/sound/isa/opti9xx/opti92x-ad1848.c
index 8ee0d70536f9..9d528ae00bff 100644
--- a/sound/isa/opti9xx/opti92x-ad1848.c
+++ b/sound/isa/opti9xx/opti92x-ad1848.c
@@ -1291,7 +1291,7 @@ static int snd_opti93x_create(struct snd_card *card, struct snd_opti9xx *chip,
}
codec->dma2 = chip->dma2;
- if (request_irq(chip->irq, snd_opti93x_interrupt, SA_INTERRUPT, DRIVER_NAME" - WSS", codec)) {
+ if (request_irq(chip->irq, snd_opti93x_interrupt, IRQF_DISABLED, DRIVER_NAME" - WSS", codec)) {
snd_printk(KERN_ERR "opti9xx: can't grab IRQ %d\n", chip->irq);
snd_opti93x_free(codec);
return -EBUSY;
@@ -1863,7 +1863,7 @@ static int __init snd_opti9xx_probe(struct snd_card *card)
rmidi = NULL;
else
if ((error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
- chip->mpu_port, 0, chip->mpu_irq, SA_INTERRUPT,
+ chip->mpu_port, 0, chip->mpu_irq, IRQF_DISABLED,
&rmidi)))
snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n",
chip->mpu_port);
diff --git a/sound/isa/sb/sb16.c b/sound/isa/sb/sb16.c
index 7f7f05fa518a..d64e67f2bafa 100644
--- a/sound/isa/sb/sb16.c
+++ b/sound/isa/sb/sb16.c
@@ -327,7 +327,8 @@ static int __devinit snd_card_sb16_pnp(int dev, struct snd_card_sb16 *acard,
goto __wt_error;
}
awe_port[dev] = pnp_port_start(pdev, 0);
- snd_printdd("pnp SB16: wavetable port=0x%lx\n", pnp_port_start(pdev, 0));
+ snd_printdd("pnp SB16: wavetable port=0x%llx\n",
+ (unsigned long long)pnp_port_start(pdev, 0));
} else {
__wt_error:
if (pdev) {
diff --git a/sound/isa/sb/sb_common.c b/sound/isa/sb/sb_common.c
index f343a8211d2d..f17de2bdd9e0 100644
--- a/sound/isa/sb/sb_common.c
+++ b/sound/isa/sb/sb_common.c
@@ -232,7 +232,7 @@ int snd_sbdsp_create(struct snd_card *card,
chip->port = port;
if (request_irq(irq, irq_handler, hardware == SB_HW_ALS4000 ?
- SA_INTERRUPT | SA_SHIRQ : SA_INTERRUPT,
+ IRQF_DISABLED | IRQF_SHARED : IRQF_DISABLED,
"SoundBlaster", (void *) chip)) {
snd_printk(KERN_ERR "sb: can't grab irq %d\n", irq);
snd_sbdsp_free(chip);
diff --git a/sound/isa/sgalaxy.c b/sound/isa/sgalaxy.c
index 09c8e8c6b5e3..8742fa517491 100644
--- a/sound/isa/sgalaxy.c
+++ b/sound/isa/sgalaxy.c
@@ -147,7 +147,7 @@ static int __init snd_sgalaxy_setup_wss(unsigned long port, int irq, int dma)
if (tmp < 0)
return -EINVAL;
- if (request_irq(irq, snd_sgalaxy_dummy_interrupt, SA_INTERRUPT, "sgalaxy", NULL)) {
+ if (request_irq(irq, snd_sgalaxy_dummy_interrupt, IRQF_DISABLED, "sgalaxy", NULL)) {
snd_printk(KERN_ERR "sgalaxy: can't grab irq %d\n", irq);
return -EIO;
}
diff --git a/sound/isa/sscape.c b/sound/isa/sscape.c
index 27271c9446dc..b1f25823c652 100644
--- a/sound/isa/sscape.c
+++ b/sound/isa/sscape.c
@@ -900,7 +900,7 @@ static int __devinit create_mpu401(struct snd_card *card, int devnum, unsigned l
if ((err = snd_mpu401_uart_new(card, devnum,
MPU401_HW_MPU401,
port, MPU401_INFO_INTEGRATED,
- irq, SA_INTERRUPT,
+ irq, IRQF_DISABLED,
&rawmidi)) == 0) {
struct snd_mpu401 *mpu = (struct snd_mpu401 *) rawmidi->private_data;
mpu->open_input = mpu401_open;
diff --git a/sound/isa/wavefront/wavefront.c b/sound/isa/wavefront/wavefront.c
index 9eb27082c659..a8f8d2fa9d76 100644
--- a/sound/isa/wavefront/wavefront.c
+++ b/sound/isa/wavefront/wavefront.c
@@ -467,7 +467,7 @@ snd_wavefront_probe (struct snd_card *card, int dev)
return -EBUSY;
}
if (request_irq(ics2115_irq[dev], snd_wavefront_ics2115_interrupt,
- SA_INTERRUPT, "ICS2115", acard)) {
+ IRQF_DISABLED, "ICS2115", acard)) {
snd_printk(KERN_ERR "unable to use ICS2115 IRQ %d\n", ics2115_irq[dev]);
return -EBUSY;
}
@@ -497,7 +497,7 @@ snd_wavefront_probe (struct snd_card *card, int dev)
if ((err = snd_mpu401_uart_new(card, midi_dev, MPU401_HW_CS4232,
cs4232_mpu_port[dev], 0,
cs4232_mpu_irq[dev],
- SA_INTERRUPT,
+ IRQF_DISABLED,
NULL)) < 0) {
snd_printk (KERN_ERR "can't allocate CS4232 MPU-401 device\n");
return err;
diff --git a/sound/isa/wavefront/wavefront_fx.c b/sound/isa/wavefront/wavefront_fx.c
index 180661c5ffdc..4f0846feb73f 100644
--- a/sound/isa/wavefront/wavefront_fx.c
+++ b/sound/isa/wavefront/wavefront_fx.c
@@ -34,7 +34,7 @@
/* weird stuff, derived from port I/O tracing with dosemu */
-static unsigned char page_zero[] __initdata = {
+static unsigned char page_zero[] __devinitdata = {
0x01, 0x7c, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0x00,
0x11, 0x00, 0x20, 0x00, 0x32, 0x00, 0x40, 0x00, 0x13, 0x00, 0x00,
0x00, 0x14, 0x02, 0x76, 0x00, 0x60, 0x00, 0x80, 0x02, 0x00, 0x00,
@@ -61,7 +61,7 @@ static unsigned char page_zero[] __initdata = {
0x1d, 0x02, 0xdf
};
-static unsigned char page_one[] __initdata = {
+static unsigned char page_one[] __devinitdata = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x19, 0x00,
0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xd8, 0x00, 0x00,
0x02, 0x20, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x01,
@@ -88,7 +88,7 @@ static unsigned char page_one[] __initdata = {
0x60, 0x00, 0x1b
};
-static unsigned char page_two[] __initdata = {
+static unsigned char page_two[] __devinitdata = {
0xc4, 0x00, 0x44, 0x07, 0x44, 0x00, 0x40, 0x25, 0x01, 0x06, 0xc4,
0x07, 0x40, 0x25, 0x01, 0x00, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -103,7 +103,7 @@ static unsigned char page_two[] __initdata = {
0x46, 0x05, 0x46, 0x07, 0x46, 0x07, 0x44
};
-static unsigned char page_three[] __initdata = {
+static unsigned char page_three[] __devinitdata = {
0x07, 0x40, 0x00, 0x00, 0x00, 0x47, 0x00, 0x40, 0x00, 0x40, 0x06,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -118,7 +118,7 @@ static unsigned char page_three[] __initdata = {
0x02, 0x00, 0x42, 0x00, 0xc0, 0x00, 0x40
};
-static unsigned char page_four[] __initdata = {
+static unsigned char page_four[] __devinitdata = {
0x63, 0x03, 0x26, 0x02, 0x2c, 0x00, 0x24, 0x00, 0x2e, 0x02, 0x02,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -133,7 +133,7 @@ static unsigned char page_four[] __initdata = {
0x02, 0x62, 0x02, 0x20, 0x01, 0x21, 0x01
};
-static unsigned char page_six[] __initdata = {
+static unsigned char page_six[] __devinitdata = {
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x06, 0x00,
0x00, 0x08, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0e,
0x00, 0x00, 0x10, 0x00, 0x00, 0x12, 0x00, 0x00, 0x14, 0x00, 0x00,
@@ -154,7 +154,7 @@ static unsigned char page_six[] __initdata = {
0x80, 0x00, 0x7e, 0x80, 0x80
};
-static unsigned char page_seven[] __initdata = {
+static unsigned char page_seven[] __devinitdata = {
0x0f, 0xff, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
0x08, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x0f,
@@ -181,7 +181,7 @@ static unsigned char page_seven[] __initdata = {
0x00, 0x02, 0x00
};
-static unsigned char page_zero_v2[] __initdata = {
+static unsigned char page_zero_v2[] __devinitdata = {
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -193,7 +193,7 @@ static unsigned char page_zero_v2[] __initdata = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
-static unsigned char page_one_v2[] __initdata = {
+static unsigned char page_one_v2[] __devinitdata = {
0x01, 0xc0, 0x01, 0xfa, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -205,21 +205,21 @@ static unsigned char page_one_v2[] __initdata = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
-static unsigned char page_two_v2[] __initdata = {
+static unsigned char page_two_v2[] __devinitdata = {
0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
};
-static unsigned char page_three_v2[] __initdata = {
+static unsigned char page_three_v2[] __devinitdata = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
};
-static unsigned char page_four_v2[] __initdata = {
+static unsigned char page_four_v2[] __devinitdata = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -227,7 +227,7 @@ static unsigned char page_four_v2[] __initdata = {
0x00, 0x00, 0x00, 0x00
};
-static unsigned char page_seven_v2[] __initdata = {
+static unsigned char page_seven_v2[] __devinitdata = {
0x0f, 0xff, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -239,7 +239,7 @@ static unsigned char page_seven_v2[] __initdata = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
-static unsigned char mod_v2[] __initdata = {
+static unsigned char mod_v2[] __devinitdata = {
0x01, 0x00, 0x02, 0x00, 0x01, 0x01, 0x02, 0x00, 0x01, 0x02, 0x02,
0x00, 0x01, 0x03, 0x02, 0x00, 0x01, 0x04, 0x02, 0x00, 0x01, 0x05,
0x02, 0x00, 0x01, 0x06, 0x02, 0x00, 0x01, 0x07, 0x02, 0x00, 0xb0,
@@ -269,7 +269,7 @@ static unsigned char mod_v2[] __initdata = {
0x02, 0x01, 0x01, 0x04, 0x02, 0x01, 0x01, 0x05, 0x02, 0x01, 0x01,
0x06, 0x02, 0x01, 0x01, 0x07, 0x02, 0x01
};
-static unsigned char coefficients[] __initdata = {
+static unsigned char coefficients[] __devinitdata = {
0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x00, 0x4b, 0x03,
0x11, 0x00, 0x4d, 0x01, 0x32, 0x07, 0x46, 0x00, 0x00, 0x07, 0x49,
0x00, 0x00, 0x07, 0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x01,
@@ -305,14 +305,14 @@ static unsigned char coefficients[] __initdata = {
0x06, 0x6c, 0x4c, 0x6c, 0x06, 0x50, 0x52, 0xe2, 0x06, 0x42, 0x02,
0xba
};
-static unsigned char coefficients2[] __initdata = {
+static unsigned char coefficients2[] __devinitdata = {
0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x45, 0x0f,
0xff, 0x07, 0x48, 0x0f, 0xff, 0x07, 0x7b, 0x04, 0xcc, 0x07, 0x7d,
0x04, 0xcc, 0x07, 0x7c, 0x00, 0x00, 0x07, 0x7e, 0x00, 0x00, 0x07,
0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x47, 0x00, 0x00,
0x07, 0x4a, 0x00, 0x00, 0x07, 0x4c, 0x00, 0x00, 0x07, 0x4e, 0x00, 0x00
};
-static unsigned char coefficients3[] __initdata = {
+static unsigned char coefficients3[] __devinitdata = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x28, 0x00, 0x51, 0x00,
0x51, 0x00, 0x7a, 0x00, 0x7a, 0x00, 0xa3, 0x00, 0xa3, 0x00, 0xcc,
0x00, 0xcc, 0x00, 0xf5, 0x00, 0xf5, 0x01, 0x1e, 0x01, 0x1e, 0x01,
@@ -563,7 +563,7 @@ snd_wavefront_fx_ioctl (struct snd_hwdep *sdev, struct file *file,
*/
-int __init
+int __devinit
snd_wavefront_fx_start (snd_wavefront_t *dev)
{
diff --git a/sound/isa/wavefront/wavefront_midi.c b/sound/isa/wavefront/wavefront_midi.c
index 15888ba2169b..cb3460094324 100644
--- a/sound/isa/wavefront/wavefront_midi.c
+++ b/sound/isa/wavefront/wavefront_midi.c
@@ -474,7 +474,7 @@ snd_wavefront_midi_disable_virtual (snd_wavefront_card_t *card)
spin_unlock_irqrestore (&card->wavefront.midi.virtual, flags);
}
-int __init
+int __devinit
snd_wavefront_midi_start (snd_wavefront_card_t *card)
{
diff --git a/sound/isa/wavefront/wavefront_synth.c b/sound/isa/wavefront/wavefront_synth.c
index 68aa091e8961..bed329edbdd7 100644
--- a/sound/isa/wavefront/wavefront_synth.c
+++ b/sound/isa/wavefront/wavefront_synth.c
@@ -1738,7 +1738,7 @@ snd_wavefront_internal_interrupt (snd_wavefront_card_t *card)
7 Unused
*/
-static int __init
+static int __devinit
snd_wavefront_interrupt_bits (int irq)
{
@@ -1766,7 +1766,7 @@ snd_wavefront_interrupt_bits (int irq)
return bits;
}
-static void __init
+static void __devinit
wavefront_should_cause_interrupt (snd_wavefront_t *dev,
int val, int port, int timeout)
@@ -1787,7 +1787,7 @@ wavefront_should_cause_interrupt (snd_wavefront_t *dev,
}
}
-static int __init
+static int __devinit
wavefront_reset_to_cleanliness (snd_wavefront_t *dev)
{
@@ -1946,7 +1946,7 @@ wavefront_reset_to_cleanliness (snd_wavefront_t *dev)
#include <asm/uaccess.h>
-static int __init
+static int __devinit
wavefront_download_firmware (snd_wavefront_t *dev, char *path)
{
@@ -2047,7 +2047,7 @@ wavefront_download_firmware (snd_wavefront_t *dev, char *path)
}
-static int __init
+static int __devinit
wavefront_do_reset (snd_wavefront_t *dev)
{
@@ -2136,7 +2136,7 @@ wavefront_do_reset (snd_wavefront_t *dev)
return 1;
}
-int __init
+int __devinit
snd_wavefront_start (snd_wavefront_t *dev)
{
@@ -2178,7 +2178,7 @@ snd_wavefront_start (snd_wavefront_t *dev)
return (0);
}
-int __init
+int __devinit
snd_wavefront_detect (snd_wavefront_card_t *card)
{
diff --git a/sound/mips/au1x00.c b/sound/mips/au1x00.c
index cf476fe1ac56..ff6e6fc198a1 100644
--- a/sound/mips/au1x00.c
+++ b/sound/mips/au1x00.c
@@ -258,7 +258,7 @@ au1000_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs)
static unsigned int rates[] = {8000, 11025, 16000, 22050};
static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
- .count = sizeof(rates) / sizeof(rates[0]),
+ .count = ARRAY_SIZE(rates),
.list = rates,
.mask = 0,
};
@@ -465,13 +465,13 @@ snd_au1000_pcm_new(struct snd_au1000 *au1000)
flags = claim_dma_lock();
if ((au1000->stream[PLAYBACK]->dma = request_au1000_dma(DMA_ID_AC97C_TX,
- "AC97 TX", au1000_dma_interrupt, SA_INTERRUPT,
+ "AC97 TX", au1000_dma_interrupt, IRQF_DISABLED,
au1000->stream[PLAYBACK])) < 0) {
release_dma_lock(flags);
return -EBUSY;
}
if ((au1000->stream[CAPTURE]->dma = request_au1000_dma(DMA_ID_AC97C_RX,
- "AC97 RX", au1000_dma_interrupt, SA_INTERRUPT,
+ "AC97 RX", au1000_dma_interrupt, IRQF_DISABLED,
au1000->stream[CAPTURE])) < 0){
release_dma_lock(flags);
return -EBUSY;
diff --git a/sound/oss/COPYING b/sound/oss/COPYING
deleted file mode 100644
index 916d1f0f2842..000000000000
--- a/sound/oss/COPYING
+++ /dev/null
@@ -1,339 +0,0 @@
- GNU GENERAL PUBLIC LICENSE
- Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
- 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The licenses for most software are designed to take away your
-freedom to share and change it. By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users. This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it. (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.) You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
- To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have. You must make sure that they, too, receive or can get the
-source code. And you must show them these terms so they know their
-rights.
-
- We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
- Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software. If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
- Finally, any free program is threatened constantly by software
-patents. We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary. To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- GNU GENERAL PUBLIC LICENSE
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
- 0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License. The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language. (Hereinafter, translation is included without limitation in
-the term "modification".) Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope. The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
- 1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
- 2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
- a) You must cause the modified files to carry prominent notices
- stating that you changed the files and the date of any change.
-
- b) You must cause any work that you distribute or publish, that in
- whole or in part contains or is derived from the Program or any
- part thereof, to be licensed as a whole at no charge to all third
- parties under the terms of this License.
-
- c) If the modified program normally reads commands interactively
- when run, you must cause it, when started running for such
- interactive use in the most ordinary way, to print or display an
- announcement including an appropriate copyright notice and a
- notice that there is no warranty (or else, saying that you provide
- a warranty) and that users may redistribute the program under
- these conditions, and telling the user how to view a copy of this
- License. (Exception: if the Program itself is interactive but
- does not normally print such an announcement, your work based on
- the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole. If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works. But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
- 3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
- a) Accompany it with the complete corresponding machine-readable
- source code, which must be distributed under the terms of Sections
- 1 and 2 above on a medium customarily used for software interchange; or,
-
- b) Accompany it with a written offer, valid for at least three
- years, to give any third party, for a charge no more than your
- cost of physically performing source distribution, a complete
- machine-readable copy of the corresponding source code, to be
- distributed under the terms of Sections 1 and 2 above on a medium
- customarily used for software interchange; or,
-
- c) Accompany it with the information you received as to the offer
- to distribute corresponding source code. (This alternative is
- allowed only for noncommercial distribution and only if you
- received the program in object code or executable form with such
- an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it. For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable. However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
- 4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License. Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
- 5. You are not required to accept this License, since you have not
-signed it. However, nothing else grants you permission to modify or
-distribute the Program or its derivative works. These actions are
-prohibited by law if you do not accept this License. Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
- 6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions. You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
- 7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all. For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices. Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
- 8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded. In such case, this License incorporates
-the limitation as if written in the body of this License.
-
- 9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation. If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
- 10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission. For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this. Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
- NO WARRANTY
-
- 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
- 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
- END OF TERMS AND CONDITIONS
-
- Appendix: How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
- <one line to give the program's name and a brief idea of what it does.>
- Copyright (C) 19yy <name of author>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- 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
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
- Gnomovision version 69, Copyright (C) 19yy name of author
- Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary. Here is a sample; alter the names:
-
- Yoyodyne, Inc., hereby disclaims all copyright interest in the program
- `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
- <signature of Ty Coon>, 1 April 1989
- Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs. If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library. If this is what you want to do, use the GNU Library General
-Public License instead of this License.
diff --git a/sound/oss/Kconfig b/sound/oss/Kconfig
index 080ab036b67a..97e38b665587 100644
--- a/sound/oss/Kconfig
+++ b/sound/oss/Kconfig
@@ -5,6 +5,20 @@
#
# Prompt user for primary drivers.
+config OSS_OBSOLETE_DRIVER
+ bool "Obsolete OSS drivers"
+ depends on SOUND_PRIME
+ help
+ This option enables support for obsolete OSS drivers that
+ are scheduled for removal in the near future since there
+ are ALSA drivers for the same hardware.
+
+ Please contact Adrian Bunk <bunk@stusta.de> if you had to
+ say Y here because your soundcard is not properly supported
+ by ALSA.
+
+ If unsure, say N.
+
config SOUND_BT878
tristate "BT878 audio dma"
depends on SOUND_PRIME && PCI
@@ -23,7 +37,7 @@ config SOUND_BT878
config SOUND_EMU10K1
tristate "Creative SBLive! (EMU10K1)"
- depends on SOUND_PRIME && PCI
+ depends on SOUND_PRIME && PCI && OSS_OBSOLETE_DRIVER
---help---
Say Y or M if you have a PCI sound card using the EMU10K1 chipset,
such as the Creative SBLive!, SB PCI512 or Emu-APS.
@@ -31,7 +45,7 @@ config SOUND_EMU10K1
For more information on this driver and the degree of support for
the different card models please check:
- <http://sourceforge.net/projects/emu10k1/>
+ <http://sourceforge.net/projects/emu10k1/>
It is now possible to load dsp microcode patches into the EMU10K1
chip. These patches are used to implement real time sound
@@ -49,7 +63,7 @@ config MIDI_EMU10K1
config SOUND_FUSION
tristate "Crystal SoundFusion (CS4280/461x)"
- depends on SOUND_PRIME && PCI
+ depends on SOUND_PRIME && PCI && OSS_OBSOLETE_DRIVER
help
This module drives the Crystal SoundFusion devices (CS4280/46xx
series) when wired as native sound drivers with AC97 codecs. If
@@ -114,8 +128,9 @@ config SOUND_VRC5477
with the AC97 codec.
config SOUND_AU1550_AC97
- tristate "Au1550 AC97 Sound"
- depends on SOUND_PRIME && SOC_AU1550
+ tristate "Au1550/Au1200 AC97 Sound"
+ select SND_AC97_CODEC
+ depends on SOUND_PRIME && (SOC_AU1550 || SOC_AU1200)
config SOUND_TRIDENT
tristate "Trident 4DWave DX/NX, SiS 7018 or ALi 5451 PCI Audio Core"
@@ -139,7 +154,7 @@ config SOUND_TRIDENT
system support" and "Sysctl support", and after the /proc file
system has been mounted, executing the command
- command what is enabled
+ command what is enabled
echo 0>/proc/ALi5451 pcm out is also set to S/PDIF out. (Default).
@@ -439,7 +454,7 @@ config SOUND_DMAP
config SOUND_AD1816
tristate "AD1816(A) based cards (EXPERIMENTAL)"
- depends on EXPERIMENTAL && SOUND_OSS
+ depends on EXPERIMENTAL && SOUND_OSS && OSS_OBSOLETE_DRIVER
help
Say M here if you have a sound card based on the Analog Devices
AD1816(A) chip.
@@ -449,21 +464,21 @@ config SOUND_AD1816
config SOUND_AD1889
tristate "AD1889 based cards (AD1819 codec) (EXPERIMENTAL)"
- depends on EXPERIMENTAL && SOUND_OSS && PCI
+ depends on EXPERIMENTAL && SOUND_OSS && PCI && OSS_OBSOLETE_DRIVER
help
Say M here if you have a sound card based on the Analog Devices
AD1889 chip.
config SOUND_ADLIB
tristate "Adlib Cards"
- depends on SOUND_OSS
+ depends on SOUND_OSS && OSS_OBSOLETE_DRIVER
help
Includes ASB 64 4D. Information on programming AdLib cards is
available at <http://www.itsnet.com/home/ldragon/Specs/adlib.html>.
config SOUND_ACI_MIXER
tristate "ACI mixer (miroSOUND PCM1-pro/PCM12/PCM20)"
- depends on SOUND_OSS
+ depends on SOUND_OSS && OSS_OBSOLETE_DRIVER
---help---
ACI (Audio Command Interface) is a protocol used to communicate with
the microcontroller on some sound cards produced by miro and
@@ -492,6 +507,19 @@ config SOUND_CS4232
See <file:Documentation/sound/oss/CS4232> for more information on
configuring this card.
+config SOUND_SSCAPE
+ tristate "Ensoniq SoundScape support"
+ depends on SOUND_OSS
+ help
+ Answer Y if you have a sound card based on the Ensoniq SoundScape
+ chipset. Such cards are being manufactured at least by Ensoniq, Spea
+ and Reveal (Reveal makes also other cards).
+
+ If you compile the driver into the kernel, you have to add
+ "sscape=<io>,<irq>,<dma>,<mpuio>,<mpuirq>" to the kernel command
+ line.
+
+
config SOUND_VMIDI
tristate "Loopback MIDI device support"
depends on SOUND_OSS
@@ -572,7 +600,7 @@ config SOUND_MPU401
config SOUND_NM256
tristate "NM256AV/NM256ZX audio support"
- depends on SOUND_OSS
+ depends on SOUND_OSS && OSS_OBSOLETE_DRIVER
help
Say M here to include audio support for the NeoMagic 256AV/256ZX
chipsets. These are the audio chipsets found in the Sony
@@ -692,7 +720,7 @@ config SOUND_YM3812
config SOUND_OPL3SA2
tristate "Yamaha OPL3-SA2 and SA3 based PnP cards"
- depends on SOUND_OSS
+ depends on SOUND_OSS && OSS_OBSOLETE_DRIVER
help
Say Y or M if you have a card based on one of these Yamaha sound
chipsets or the "SAx", which is actually a SA3. Read
@@ -824,7 +852,7 @@ config SOUND_WAVEARTIST
config SOUND_TVMIXER
tristate "TV card (bt848) mixer support"
- depends on SOUND_PRIME && I2C
+ depends on SOUND_PRIME && I2C && VIDEO_V4L1
help
Support for audio mixer facilities on the BT848 TV frame-grabber
card.
@@ -838,6 +866,6 @@ config SOUND_SH_DAC_AUDIO
depends on SOUND_PRIME && CPU_SH3
config SOUND_SH_DAC_AUDIO_CHANNEL
- int " DAC channel"
+ int "DAC channel"
default "1"
depends on SOUND_SH_DAC_AUDIO
diff --git a/sound/oss/ad1816.c b/sound/oss/ad1816.c
index 95586de02028..29057836c644 100644
--- a/sound/oss/ad1816.c
+++ b/sound/oss/ad1816.c
@@ -41,7 +41,6 @@
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/sound/oss/ad1848.c b/sound/oss/ad1848.c
index e04fa49b0dc8..3b45e11e5303 100644
--- a/sound/oss/ad1848.c
+++ b/sound/oss/ad1848.c
@@ -41,7 +41,6 @@
* Tested. Believed fully functional.
*/
-#include <linux/config.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
diff --git a/sound/oss/ad1889.c b/sound/oss/ad1889.c
index a4ca7569e700..f56f870b4840 100644
--- a/sound/oss/ad1889.c
+++ b/sound/oss/ad1889.c
@@ -26,7 +26,6 @@
*
* $Id: ad1889.c,v 1.3 2002/10/19 21:31:44 grundler Exp $
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/ioport.h>
@@ -1011,7 +1010,7 @@ static int __devinit ad1889_probe(struct pci_dev *pcidev, const struct pci_devic
goto out2;
}
- if (request_irq(pcidev->irq, ad1889_interrupt, SA_SHIRQ, DEVNAME, dev) != 0) {
+ if (request_irq(pcidev->irq, ad1889_interrupt, IRQF_SHARED, DEVNAME, dev) != 0) {
printk(KERN_ERR DEVNAME ": unable to request interrupt\n");
goto out3;
}
diff --git a/sound/oss/aedsp16.c b/sound/oss/aedsp16.c
index b556263a57f5..51e1fde62e8d 100644
--- a/sound/oss/aedsp16.c
+++ b/sound/oss/aedsp16.c
@@ -23,7 +23,6 @@
* Include the main OSS Lite header file. It include all the os, OSS Lite, etc
* headers needed by this source.
*/
-#include <linux/config.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/init.h>
diff --git a/sound/oss/ali5455.c b/sound/oss/ali5455.c
index 62bb936b1f3d..70dcd703a66f 100644
--- a/sound/oss/ali5455.c
+++ b/sound/oss/ali5455.c
@@ -3460,7 +3460,7 @@ static int __devinit ali_probe(struct pci_dev *pci_dev,
card->channel[4].num = 4;
/* claim our iospace and irq */
request_region(card->iobase, 256, card_names[pci_id->driver_data]);
- if (request_irq(card->irq, &ali_interrupt, SA_SHIRQ,
+ if (request_irq(card->irq, &ali_interrupt, IRQF_SHARED,
card_names[pci_id->driver_data], card)) {
printk(KERN_ERR "ali_audio: unable to allocate irq %d\n",
card->irq);
diff --git a/sound/oss/au1000.c b/sound/oss/au1000.c
index eacb0aef21e1..e3796231452a 100644
--- a/sound/oss/au1000.c
+++ b/sound/oss/au1000.c
@@ -2015,14 +2015,14 @@ static int __devinit au1000_probe(void)
if ((s->dma_dac.dmanr = request_au1000_dma(DMA_ID_AC97C_TX,
"audio DAC",
dac_dma_interrupt,
- SA_INTERRUPT, s)) < 0) {
+ IRQF_DISABLED, s)) < 0) {
err("Can't get DAC DMA");
goto err_dma1;
}
if ((s->dma_adc.dmanr = request_au1000_dma(DMA_ID_AC97C_RX,
"audio ADC",
adc_dma_interrupt,
- SA_INTERRUPT, s)) < 0) {
+ IRQF_DISABLED, s)) < 0) {
err("Can't get ADC DMA");
goto err_dma2;
}
diff --git a/sound/oss/au1550_ac97.c b/sound/oss/au1550_ac97.c
index 9011abe241ab..219795171c71 100644
--- a/sound/oss/au1550_ac97.c
+++ b/sound/oss/au1550_ac97.c
@@ -213,7 +213,8 @@ rdcodec(struct ac97_codec *codec, u8 addr)
}
if (i == POLL_COUNT) {
err("rdcodec: read poll expired!");
- return 0;
+ data = 0;
+ goto out;
}
/* wait for command done?
@@ -226,7 +227,8 @@ rdcodec(struct ac97_codec *codec, u8 addr)
}
if (i == POLL_COUNT) {
err("rdcodec: read cmdwait expired!");
- return 0;
+ data = 0;
+ goto out;
}
data = au_readl(PSC_AC97CDC) & 0xffff;
@@ -237,6 +239,7 @@ rdcodec(struct ac97_codec *codec, u8 addr)
au_writel(PSC_AC97EVNT_CD, PSC_AC97EVNT);
au_sync();
+ out:
spin_unlock_irqrestore(&s->lock, flags);
return data;
@@ -716,8 +719,7 @@ prog_dmabuf_dac(struct au1550_state *s)
}
-static void
-dac_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static void dac_dma_interrupt(int irq, void *dev_id)
{
struct au1550_state *s = (struct au1550_state *) dev_id;
struct dmabuf *db = &s->dma_dac;
@@ -751,8 +753,7 @@ dac_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs)
}
-static void
-adc_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static void adc_dma_interrupt(int irq, void *dev_id)
{
struct au1550_state *s = (struct au1550_state *)dev_id;
struct dmabuf *dp = &s->dma_adc;
@@ -1892,6 +1893,8 @@ static /*const */ struct file_operations au1550_audio_fops = {
MODULE_AUTHOR("Advanced Micro Devices (AMD), dan@embeddededge.com");
MODULE_DESCRIPTION("Au1550 AC97 Audio Driver");
+MODULE_LICENSE("GPL");
+
static int __devinit
au1550_probe(void)
diff --git a/sound/oss/btaudio.c b/sound/oss/btaudio.c
index bfe3b534ef30..324a81fd3a3b 100644
--- a/sound/oss/btaudio.c
+++ b/sound/oss/btaudio.c
@@ -966,7 +966,7 @@ static int __devinit btaudio_probe(struct pci_dev *pci_dev,
btwrite(~0U, REG_INT_STAT);
pci_set_master(pci_dev);
- if ((rc = request_irq(bta->irq, btaudio_irq, SA_SHIRQ|SA_INTERRUPT,
+ if ((rc = request_irq(bta->irq, btaudio_irq, IRQF_SHARED|IRQF_DISABLED,
"btaudio",(void *)bta)) < 0) {
printk(KERN_WARNING
"btaudio: can't request irq (rc=%d)\n",rc);
diff --git a/sound/oss/cmpci.c b/sound/oss/cmpci.c
index de60a059ff5f..ea51aafaf401 100644
--- a/sound/oss/cmpci.c
+++ b/sound/oss/cmpci.c
@@ -3122,7 +3122,7 @@ static int __devinit cm_probe(struct pci_dev *pcidev, const struct pci_device_id
wrmixer(s, DSP_MIX_DATARESETIDX, 0);
/* request irq */
- if ((ret = request_irq(s->irq, cm_interrupt, SA_SHIRQ, "cmpci", s))) {
+ if ((ret = request_irq(s->irq, cm_interrupt, IRQF_SHARED, "cmpci", s))) {
printk(KERN_ERR "cmpci: irq %u in use\n", s->irq);
goto err_irq;
}
diff --git a/sound/oss/cs4232.c b/sound/oss/cs4232.c
index c7f86f09c28d..b6924c7f1484 100644
--- a/sound/oss/cs4232.c
+++ b/sound/oss/cs4232.c
@@ -47,7 +47,6 @@
* Marcus Meissner Added ISA PnP support.
*/
-#include <linux/config.h>
#include <linux/pnp.h>
#include <linux/module.h>
#include <linux/init.h>
@@ -405,7 +404,7 @@ static const struct pnp_device_id cs4232_pnp_table[] = {
MODULE_DEVICE_TABLE(pnp, cs4232_pnp_table);
-static int cs4232_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
+static int __init cs4232_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
{
struct address_info *isapnpcfg;
diff --git a/sound/oss/cs4281/cs4281m.c b/sound/oss/cs4281/cs4281m.c
index 0004442f9b7e..0400a416dc93 100644
--- a/sound/oss/cs4281/cs4281m.c
+++ b/sound/oss/cs4281/cs4281m.c
@@ -4346,7 +4346,7 @@ static int __devinit cs4281_probe(struct pci_dev *pcidev,
s->pcidev = pcidev;
s->irq = pcidev->irq;
if (request_irq
- (s->irq, cs4281_interrupt, SA_SHIRQ, "Crystal CS4281", s)) {
+ (s->irq, cs4281_interrupt, IRQF_SHARED, "Crystal CS4281", s)) {
CS_DBGOUT(CS_INIT | CS_ERROR, 1,
printk(KERN_ERR "cs4281: irq %u in use\n", s->irq));
goto err_irq;
diff --git a/sound/oss/cs46xx.c b/sound/oss/cs46xx.c
index 994c71e986e4..3f81b79afbaa 100644
--- a/sound/oss/cs46xx.c
+++ b/sound/oss/cs46xx.c
@@ -96,7 +96,7 @@
#include <asm/dma.h>
#include <asm/uaccess.h>
-#include "cs46xxpm-24.h"
+#include "cs46xxpm.h"
#include "cs46xx_wrapper-24.h"
#include "cs461x.h"
@@ -389,8 +389,10 @@ static int cs_hardware_init(struct cs_card *card);
static int cs46xx_powerup(struct cs_card *card, unsigned int type);
static int cs461x_powerdown(struct cs_card *card, unsigned int type, int suspendflag);
static void cs461x_clear_serial_FIFOs(struct cs_card *card, int type);
+#ifdef CONFIG_PM
static int cs46xx_suspend_tbl(struct pci_dev *pcidev, pm_message_t state);
static int cs46xx_resume_tbl(struct pci_dev *pcidev);
+#endif
#if CSDEBUG
@@ -5177,7 +5179,7 @@ static int __devinit cs46xx_probe(struct pci_dev *pci_dev,
card->ba1.name.reg == 0)
goto fail2;
- if (request_irq(card->irq, &cs_interrupt, SA_SHIRQ, "cs46xx", card)) {
+ if (request_irq(card->irq, &cs_interrupt, IRQF_SHARED, "cs46xx", card)) {
printk(KERN_ERR "cs46xx: unable to allocate irq %d\n", card->irq);
goto fail2;
}
@@ -5389,8 +5391,10 @@ static struct pci_driver cs46xx_pci_driver = {
.id_table = cs46xx_pci_tbl,
.probe = cs46xx_probe,
.remove = __devexit_p(cs46xx_remove),
- .suspend = CS46XX_SUSPEND_TBL,
- .resume = CS46XX_RESUME_TBL,
+#ifdef CONFIG_PM
+ .suspend = cs46xx_suspend_tbl,
+ .resume = cs46xx_resume_tbl,
+#endif
};
static int __init cs46xx_init_module(void)
@@ -5420,7 +5424,7 @@ static void __exit cs46xx_cleanup_module(void)
module_init(cs46xx_init_module);
module_exit(cs46xx_cleanup_module);
-#if CS46XX_ACPI_SUPPORT
+#ifdef CONFIG_PM
static int cs46xx_suspend_tbl(struct pci_dev *pcidev, pm_message_t state)
{
struct cs_card *s = PCI_GET_DRIVER_DATA(pcidev);
diff --git a/sound/oss/cs46xxpm-24.h b/sound/oss/cs46xxpm-24.h
deleted file mode 100644
index ad82db84d013..000000000000
--- a/sound/oss/cs46xxpm-24.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*******************************************************************************
-*
-* "cs46xxpm-24.h" -- Cirrus Logic-Crystal CS46XX linux audio driver.
-*
-* Copyright (C) 2000,2001 Cirrus Logic Corp.
-* -- tom woller (twoller@crystal.cirrus.com) or
-* (pcaudio@crystal.cirrus.com).
-*
-* This program is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published by
-* the Free Software Foundation; either version 2 of the License, or
-* (at your option) any later version.
-*
-* This program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU General Public License for more details.
-*
-* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
-*
-* 12/22/00 trw - new file.
-*
-*******************************************************************************/
-#ifndef __CS46XXPM24_H
-#define __CS46XXPM24_H
-
-#include <linux/pm.h>
-#include "cs46xxpm.h"
-
-
-#define CS46XX_ACPI_SUPPORT 1
-#ifdef CS46XX_ACPI_SUPPORT
-/*
-* for now (12/22/00) only enable the pm_register PM support.
-* allow these table entries to be null.
-*/
-static int cs46xx_suspend_tbl(struct pci_dev *pcidev, pm_message_t state);
-static int cs46xx_resume_tbl(struct pci_dev *pcidev);
-#define CS46XX_SUSPEND_TBL cs46xx_suspend_tbl
-#define CS46XX_RESUME_TBL cs46xx_resume_tbl
-#else
-#define CS46XX_SUSPEND_TBL cs46xx_null
-#define CS46XX_RESUME_TBL cs46xx_null
-#endif
-
-#endif
diff --git a/sound/oss/dmabuf.c b/sound/oss/dmabuf.c
index baf4244a54f2..15ce7119c5f4 100644
--- a/sound/oss/dmabuf.c
+++ b/sound/oss/dmabuf.c
@@ -547,7 +547,7 @@ int DMAbuf_activate_recording(int dev, struct dma_buffparms *dmap)
}
return 0;
}
-/* aquires lock */
+/* acquires lock */
int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock)
{
struct audio_operations *adev = audio_devs[dev];
@@ -821,7 +821,7 @@ static int find_output_space(int dev, char **buf, int *size)
*size = len & ~SAMPLE_ROUNDUP;
return (*size > 0);
}
-/* aquires lock */
+/* acquires lock */
int DMAbuf_getwrbuffer(int dev, char **buf, int *size, int dontblock)
{
struct audio_operations *adev = audio_devs[dev];
@@ -855,7 +855,7 @@ int DMAbuf_getwrbuffer(int dev, char **buf, int *size, int dontblock)
spin_unlock_irqrestore(&dmap->lock,flags);
return 0;
}
-/* has to aquire dmap->lock */
+/* has to acquire dmap->lock */
int DMAbuf_move_wrpointer(int dev, int l)
{
struct audio_operations *adev = audio_devs[dev];
diff --git a/sound/oss/dmasound/dmasound.h b/sound/oss/dmasound/dmasound.h
index a1b0b92af4b5..25dd5a318eb4 100644
--- a/sound/oss/dmasound/dmasound.h
+++ b/sound/oss/dmasound/dmasound.h
@@ -13,7 +13,6 @@
#define _dmasound_h_
#include <linux/types.h>
-#include <linux/config.h>
#define SND_NDEVS 256 /* Number of supported devices */
#define SND_DEV_CTL 0 /* Control port /dev/mixer */
diff --git a/sound/oss/dmasound/dmasound_awacs.c b/sound/oss/dmasound/dmasound_awacs.c
index c8e210326893..9ae659f82430 100644
--- a/sound/oss/dmasound/dmasound_awacs.c
+++ b/sound/oss/dmasound/dmasound_awacs.c
@@ -67,7 +67,6 @@
#include <linux/types.h>
#include <linux/module.h>
-#include <linux/config.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/delay.h>
@@ -348,8 +347,8 @@ int
setup_audio_gpio(const char *name, const char* compatible, int *gpio_addr, int* gpio_pol)
{
struct device_node *np;
- u32* pp;
-
+ const u32* pp;
+
np = find_devices("gpio");
if (!np)
return -ENODEV;
@@ -357,7 +356,8 @@ setup_audio_gpio(const char *name, const char* compatible, int *gpio_addr, int*
np = np->child;
while(np != 0) {
if (name) {
- char *property = get_property(np,"audio-gpio",NULL);
+ const char *property =
+ get_property(np,"audio-gpio",NULL);
if (property != 0 && strcmp(property,name) == 0)
break;
} else if (compatible && device_is_compatible(np, compatible))
@@ -366,19 +366,16 @@ setup_audio_gpio(const char *name, const char* compatible, int *gpio_addr, int*
}
if (!np)
return -ENODEV;
- pp = (u32 *)get_property(np, "AAPL,address", NULL);
+ pp = get_property(np, "AAPL,address", NULL);
if (!pp)
return -ENODEV;
*gpio_addr = (*pp) & 0x0000ffff;
- pp = (u32 *)get_property(np, "audio-gpio-active-state", NULL);
+ pp = get_property(np, "audio-gpio-active-state", NULL);
if (pp)
*gpio_pol = *pp;
else
*gpio_pol = 1;
- if (np->n_intrs > 0)
- return np->intrs[0].line;
-
- return 0;
+ return irq_of_parse_and_map(np, 0);
}
static inline void
@@ -2865,14 +2862,13 @@ printk("dmasound_pmac: couldn't find a Codec we can handle\n");
* other info if necessary (early AWACS we want to read chip ids)
*/
- if (of_get_address(io, 2, NULL, NULL) == NULL || io->n_intrs < 3) {
+ if (of_get_address(io, 2, NULL, NULL) == NULL) {
/* OK - maybe we need to use the 'awacs' node (on earlier
* machines).
*/
if (awacs_node) {
io = awacs_node ;
- if (of_get_address(io, 2, NULL, NULL) == NULL ||
- io->n_intrs < 3) {
+ if (of_get_address(io, 2, NULL, NULL) == NULL) {
printk("dmasound_pmac: can't use %s\n",
io->full_name);
return -ENODEV;
@@ -2941,9 +2937,9 @@ printk("dmasound_pmac: couldn't find a Codec we can handle\n");
if (awacs_revision == AWACS_SCREAMER && awacs)
awacs_recalibrate();
- awacs_irq = io->intrs[0].line;
- awacs_tx_irq = io->intrs[1].line;
- awacs_rx_irq = io->intrs[2].line;
+ awacs_irq = irq_of_parse_and_map(io, 0);
+ awacs_tx_irq = irq_of_parse_and_map(io, 1);
+ awacs_rx_irq = irq_of_parse_and_map(io, 2);
/* Hack for legacy crap that will be killed someday */
awacs_node = io;
diff --git a/sound/oss/dmasound/dmasound_paula.c b/sound/oss/dmasound/dmasound_paula.c
index 494070a3f870..68e1d8f6c359 100644
--- a/sound/oss/dmasound/dmasound_paula.c
+++ b/sound/oss/dmasound/dmasound_paula.c
@@ -16,7 +16,6 @@
#include <linux/module.h>
-#include <linux/config.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/ioport.h>
diff --git a/sound/oss/emu10k1/main.c b/sound/oss/emu10k1/main.c
index 3721c5857b90..c4ce94d6e10c 100644
--- a/sound/oss/emu10k1/main.c
+++ b/sound/oss/emu10k1/main.c
@@ -1301,7 +1301,7 @@ static int __devinit emu10k1_probe(struct pci_dev *pci_dev, const struct pci_dev
card->pci_dev = pci_dev;
/* Reserve IRQ Line */
- if (request_irq(card->irq, emu10k1_interrupt, SA_SHIRQ, card_names[pci_id->driver_data], card)) {
+ if (request_irq(card->irq, emu10k1_interrupt, IRQF_SHARED, card_names[pci_id->driver_data], card)) {
printk(KERN_ERR "emu10k1: IRQ in use\n");
ret = -EBUSY;
goto err_irq;
diff --git a/sound/oss/emu10k1/midi.c b/sound/oss/emu10k1/midi.c
index 25ae8e4a488d..8ac77df86397 100644
--- a/sound/oss/emu10k1/midi.c
+++ b/sound/oss/emu10k1/midi.c
@@ -45,7 +45,7 @@
#include "../sound_config.h"
#endif
-static DEFINE_SPINLOCK(midi_spinlock __attribute((unused)));
+static DEFINE_SPINLOCK(midi_spinlock);
static void init_midi_hdr(struct midi_hdr *midihdr)
{
diff --git a/sound/oss/es1370.c b/sound/oss/es1370.c
index 094f569cc6e0..13f483149737 100644
--- a/sound/oss/es1370.c
+++ b/sound/oss/es1370.c
@@ -2650,7 +2650,7 @@ static int __devinit es1370_probe(struct pci_dev *pcidev, const struct pci_devic
ret = -EBUSY;
goto err_region;
}
- if ((ret=request_irq(s->irq, es1370_interrupt, SA_SHIRQ, "es1370",s))) {
+ if ((ret=request_irq(s->irq, es1370_interrupt, IRQF_SHARED, "es1370",s))) {
printk(KERN_ERR "es1370: irq %u in use\n", s->irq);
goto err_irq;
}
diff --git a/sound/oss/es1371.c b/sound/oss/es1371.c
index 4400c8538686..a2ffe723dad5 100644
--- a/sound/oss/es1371.c
+++ b/sound/oss/es1371.c
@@ -2905,7 +2905,7 @@ static int __devinit es1371_probe(struct pci_dev *pcidev, const struct pci_devic
res = -EBUSY;
goto err_region;
}
- if ((res=request_irq(s->irq, es1371_interrupt, SA_SHIRQ, "es1371",s))) {
+ if ((res=request_irq(s->irq, es1371_interrupt, IRQF_SHARED, "es1371",s))) {
printk(KERN_ERR PFX "irq %u in use\n", s->irq);
goto err_irq;
}
diff --git a/sound/oss/esssolo1.c b/sound/oss/esssolo1.c
index 6861563d7525..82f40a0a5c9c 100644
--- a/sound/oss/esssolo1.c
+++ b/sound/oss/esssolo1.c
@@ -2392,7 +2392,7 @@ static int __devinit solo1_probe(struct pci_dev *pcidev, const struct pci_device
printk(KERN_ERR "solo1: io ports in use\n");
goto err_region4;
}
- if ((ret=request_irq(s->irq,solo1_interrupt,SA_SHIRQ,"ESS Solo1",s))) {
+ if ((ret=request_irq(s->irq,solo1_interrupt,IRQF_SHARED,"ESS Solo1",s))) {
printk(KERN_ERR "solo1: irq %u in use\n", s->irq);
goto err_irq;
}
diff --git a/sound/oss/forte.c b/sound/oss/forte.c
index 0294eec8ad90..ea1c0207aef2 100644
--- a/sound/oss/forte.c
+++ b/sound/oss/forte.c
@@ -2026,7 +2026,7 @@ forte_probe (struct pci_dev *pci_dev, const struct pci_device_id *pci_id)
chip->iobase = pci_resource_start (pci_dev, 0);
chip->irq = pci_dev->irq;
- if (request_irq (chip->irq, forte_interrupt, SA_SHIRQ, DRIVER_NAME,
+ if (request_irq (chip->irq, forte_interrupt, IRQF_SHARED, DRIVER_NAME,
chip)) {
printk (KERN_WARNING PFX "Unable to reserve IRQ");
ret = -EIO;
@@ -2035,8 +2035,9 @@ forte_probe (struct pci_dev *pci_dev, const struct pci_device_id *pci_id)
pci_set_drvdata (pci_dev, chip);
- printk (KERN_INFO PFX "FM801 chip found at 0x%04lX-0x%04lX IRQ %u\n",
- chip->iobase, pci_resource_end (pci_dev, 0), chip->irq);
+ printk (KERN_INFO PFX "FM801 chip found at 0x%04lX-0x%16llX IRQ %u\n",
+ chip->iobase, (unsigned long long)pci_resource_end (pci_dev, 0),
+ chip->irq);
/* Power it up */
if ((ret = forte_chip_init (chip)) == 0)
diff --git a/sound/oss/hal2.c b/sound/oss/hal2.c
index dd4f59d30a3a..80ab402dae9a 100644
--- a/sound/oss/hal2.c
+++ b/sound/oss/hal2.c
@@ -1479,7 +1479,7 @@ static int hal2_init_card(struct hal2_card **phal2, struct hpc3_regs *hpc3)
hpc3->pbus_dmacfg[hal2->dac.pbus.pbusnr][0] = 0x8208844;
hpc3->pbus_dmacfg[hal2->adc.pbus.pbusnr][0] = 0x8208844;
- if (request_irq(SGI_HPCDMA_IRQ, hal2_interrupt, SA_SHIRQ,
+ if (request_irq(SGI_HPCDMA_IRQ, hal2_interrupt, IRQF_SHARED,
hal2str, hal2)) {
printk(KERN_ERR "HAL2: Can't get irq %d\n", SGI_HPCDMA_IRQ);
ret = -EAGAIN;
diff --git a/sound/oss/i810_audio.c b/sound/oss/i810_audio.c
index dd2b871cdac5..ddcddc2347f7 100644
--- a/sound/oss/i810_audio.c
+++ b/sound/oss/i810_audio.c
@@ -3413,7 +3413,7 @@ static int __devinit i810_probe(struct pci_dev *pci_dev, const struct pci_device
goto out_iospace;
}
- if (request_irq(card->irq, &i810_interrupt, SA_SHIRQ,
+ if (request_irq(card->irq, &i810_interrupt, IRQF_SHARED,
card_names[pci_id->driver_data], card)) {
printk(KERN_ERR "i810_audio: unable to allocate irq %d\n", card->irq);
goto out_iospace;
diff --git a/sound/oss/ite8172.c b/sound/oss/ite8172.c
index 00ac1c95a429..68aab3605d74 100644
--- a/sound/oss/ite8172.c
+++ b/sound/oss/ite8172.c
@@ -2019,7 +2019,7 @@ static int __devinit it8172_probe(struct pci_dev *pcidev,
s->io, s->io + pci_resource_len(pcidev,0)-1);
goto err_region;
}
- if (request_irq(s->irq, it8172_interrupt, SA_INTERRUPT,
+ if (request_irq(s->irq, it8172_interrupt, IRQF_DISABLED,
IT8172_MODULE_NAME, s)) {
err("irq %u in use", s->irq);
goto err_irq;
diff --git a/sound/oss/kahlua.c b/sound/oss/kahlua.c
index 2835a7c038ef..12e7b3038be2 100644
--- a/sound/oss/kahlua.c
+++ b/sound/oss/kahlua.c
@@ -27,7 +27,6 @@
* same manner.
*/
-#include <linux/config.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/module.h>
diff --git a/sound/oss/maestro.c b/sound/oss/maestro.c
index e647f2f86279..1d98d100d739 100644
--- a/sound/oss/maestro.c
+++ b/sound/oss/maestro.c
@@ -3545,7 +3545,7 @@ maestro_probe(struct pci_dev *pcidev,const struct pci_device_id *pdid)
mixer_push_state(card);
}
- if((ret=request_irq(card->irq, ess_interrupt, SA_SHIRQ, card_names[card_type], card)))
+ if((ret=request_irq(card->irq, ess_interrupt, IRQF_SHARED, card_names[card_type], card)))
{
printk(KERN_ERR "maestro: unable to allocate irq %d,\n", card->irq);
unregister_sound_mixer(card->dev_mixer);
diff --git a/sound/oss/maestro3.c b/sound/oss/maestro3.c
index 4a5e4237a110..5548e3cff7ce 100644
--- a/sound/oss/maestro3.c
+++ b/sound/oss/maestro3.c
@@ -2694,7 +2694,7 @@ static int __devinit m3_probe(struct pci_dev *pci_dev, const struct pci_device_i
}
}
- if(request_irq(card->irq, m3_interrupt, SA_SHIRQ, card_names[card->card_type], card)) {
+ if(request_irq(card->irq, m3_interrupt, IRQF_SHARED, card_names[card->card_type], card)) {
printk(KERN_ERR PFX "unable to allocate irq %d,\n", card->irq);
diff --git a/sound/oss/msnd.c b/sound/oss/msnd.c
index 5dbfc0f9c3c7..ba38d6200099 100644
--- a/sound/oss/msnd.c
+++ b/sound/oss/msnd.c
@@ -47,7 +47,7 @@
static multisound_dev_t *devs[MSND_MAX_DEVS];
static int num_devs;
-int __init msnd_register(multisound_dev_t *dev)
+int msnd_register(multisound_dev_t *dev)
{
int i;
diff --git a/sound/oss/msnd_classic.h b/sound/oss/msnd_classic.h
index 83c3c46ffffe..7ffea5267f96 100644
--- a/sound/oss/msnd_classic.h
+++ b/sound/oss/msnd_classic.h
@@ -30,7 +30,6 @@
#ifndef __MSND_CLASSIC_H
#define __MSND_CLASSIC_H
-#include <linux/config.h>
#define DSP_NUMIO 0x10
diff --git a/sound/oss/msnd_pinnacle.c b/sound/oss/msnd_pinnacle.c
index 0c2db657badd..6d7763dae895 100644
--- a/sound/oss/msnd_pinnacle.c
+++ b/sound/oss/msnd_pinnacle.c
@@ -39,7 +39,6 @@
********************************************************************/
#include <linux/kernel.h>
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/types.h>
diff --git a/sound/oss/msnd_pinnacle.h b/sound/oss/msnd_pinnacle.h
index e85aef4a55e0..cce911487004 100644
--- a/sound/oss/msnd_pinnacle.h
+++ b/sound/oss/msnd_pinnacle.h
@@ -30,7 +30,6 @@
#ifndef __MSND_PINNACLE_H
#define __MSND_PINNACLE_H
-#include <linux/config.h>
#define DSP_NUMIO 0x08
diff --git a/sound/oss/nec_vrc5477.c b/sound/oss/nec_vrc5477.c
index 21c1954d9108..6f7f2f0423e4 100644
--- a/sound/oss/nec_vrc5477.c
+++ b/sound/oss/nec_vrc5477.c
@@ -1909,7 +1909,7 @@ static int __devinit vrc5477_ac97_probe(struct pci_dev *pcidev,
s->io, s->io + pci_resource_len(pcidev,0)-1);
goto err_region;
}
- if (request_irq(s->irq, vrc5477_ac97_interrupt, SA_INTERRUPT,
+ if (request_irq(s->irq, vrc5477_ac97_interrupt, IRQF_DISABLED,
VRC5477_AC97_MODULE_NAME, s)) {
printk(KERN_ERR PFX "irq %u in use\n", s->irq);
goto err_irq;
diff --git a/sound/oss/nm256_audio.c b/sound/oss/nm256_audio.c
index 6e662ac009ae..7760dddf2b32 100644
--- a/sound/oss/nm256_audio.c
+++ b/sound/oss/nm256_audio.c
@@ -733,7 +733,7 @@ static int
nm256_grabInterrupt (struct nm256_info *card)
{
if (card->has_irq++ == 0) {
- if (request_irq (card->irq, card->introutine, SA_SHIRQ,
+ if (request_irq (card->irq, card->introutine, IRQF_SHARED,
"NM256_audio", card) < 0) {
printk (KERN_ERR "NM256: can't obtain IRQ %d\n", card->irq);
return -1;
diff --git a/sound/oss/opl3sa2.c b/sound/oss/opl3sa2.c
index 0e161c6a0477..aec05a2bfc87 100644
--- a/sound/oss/opl3sa2.c
+++ b/sound/oss/opl3sa2.c
@@ -64,7 +64,6 @@
*
*/
-#include <linux/config.h>
#include <linux/pnp.h>
#include <linux/init.h>
#include <linux/module.h>
diff --git a/sound/oss/pas2_card.c b/sound/oss/pas2_card.c
index c9696dc9fdf9..97666007b274 100644
--- a/sound/oss/pas2_card.c
+++ b/sound/oss/pas2_card.c
@@ -4,7 +4,6 @@
* Detection routine for the Pro Audio Spectrum cards.
*/
-#include <linux/config.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
diff --git a/sound/oss/pss.c b/sound/oss/pss.c
index a617ccb40e00..37ee234b587c 100644
--- a/sound/oss/pss.c
+++ b/sound/oss/pss.c
@@ -57,7 +57,6 @@
*/
-#include <linux/config.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/spinlock.h>
diff --git a/sound/oss/rme96xx.c b/sound/oss/rme96xx.c
index a1ec9d131ab3..f17d25b6f836 100644
--- a/sound/oss/rme96xx.c
+++ b/sound/oss/rme96xx.c
@@ -994,7 +994,7 @@ static int __devinit rme96xx_probe(struct pci_dev *pcidev, const struct pci_devi
if (pci_enable_device(pcidev))
goto err_irq;
- if (request_irq(s->irq, rme96xx_interrupt, SA_SHIRQ, "rme96xx", s)) {
+ if (request_irq(s->irq, rme96xx_interrupt, IRQF_SHARED, "rme96xx", s)) {
printk(KERN_ERR RME_MESS" irq %u in use\n", s->irq);
goto err_irq;
}
diff --git a/sound/oss/sb_card.c b/sound/oss/sb_card.c
index 4708cbdc3149..8666291c0052 100644
--- a/sound/oss/sb_card.c
+++ b/sound/oss/sb_card.c
@@ -22,7 +22,6 @@
* 02-07-2003 Bug made it into first release. Take two.
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
diff --git a/sound/oss/sb_common.c b/sound/oss/sb_common.c
index 5f955e3d2e26..35bab6e2f998 100644
--- a/sound/oss/sb_common.c
+++ b/sound/oss/sb_common.c
@@ -26,7 +26,6 @@
* Chris Rankin <rankinc@zipworld.com.au>
*/
-#include <linux/config.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
@@ -678,7 +677,7 @@ int sb_dsp_init(struct address_info *hw_config, struct module *owner)
* will get shared PCI irq lines we must cope.
*/
- int i=(devc->caps&SB_PCI_IRQ)?SA_SHIRQ:0;
+ int i=(devc->caps&SB_PCI_IRQ)?IRQF_SHARED:0;
if (request_irq(hw_config->irq, sbintr, i, "soundblaster", devc) < 0)
{
diff --git a/sound/oss/sb_ess.c b/sound/oss/sb_ess.c
index fae05fe3de43..180e95c87e3e 100644
--- a/sound/oss/sb_ess.c
+++ b/sound/oss/sb_ess.c
@@ -97,19 +97,19 @@
*
* The documentation is an adventure: it's close but not fully accurate. I
* found out that after a reset some registers are *NOT* reset, though the
- * docs say the would be. Interresting ones are 0x7f, 0x7d and 0x7a. They are
- * related to the Audio 2 channel. I also was suprised about the consequenses
+ * docs say the would be. Interesting ones are 0x7f, 0x7d and 0x7a. They are
+ * related to the Audio 2 channel. I also was surprised about the consequences
* of writing 0x00 to 0x7f (which should be done by reset): The ES1887 moves
* into ES1888 mode. This means that it claims IRQ 11, which happens to be my
* ISDN adapter. Needless to say it no longer worked. I now understand why
* after rebooting 0x7f already was 0x05, the value of my choice: the BIOS
* did it.
*
- * Oh, and this is another trap: in ES1887 docs mixer register 0x70 is decribed
- * as if it's exactly the same as register 0xa1. This is *NOT* true. The
- * description of 0x70 in ES1869 docs is accurate however.
+ * Oh, and this is another trap: in ES1887 docs mixer register 0x70 is
+ * described as if it's exactly the same as register 0xa1. This is *NOT* true.
+ * The description of 0x70 in ES1869 docs is accurate however.
* Well, the assumption about ES1869 was wrong: register 0x70 is very much
- * like register 0xa1, except that bit 7 is allways 1, whatever you want
+ * like register 0xa1, except that bit 7 is always 1, whatever you want
* it to be.
*
* When using audio 2 mixer register 0x72 seems te be meaningless. Only 0xa2
@@ -117,10 +117,10 @@
*
* Software reset not being able to reset all registers is great! Especially
* the fact that register 0x78 isn't reset is great when you wanna change back
- * to single dma operation (simplex): audio 2 is still operation, and uses the
- * same dma as audio 1: your ess changes into a funny echo machine.
+ * to single dma operation (simplex): audio 2 is still operational, and uses
+ * the same dma as audio 1: your ess changes into a funny echo machine.
*
- * Received the new that ES1688 is detected as a ES1788. Did some thinking:
+ * Received the news that ES1688 is detected as a ES1788. Did some thinking:
* the ES1887 detection scheme suggests in step 2 to try if bit 3 of register
* 0x64 can be changed. This is inaccurate, first I inverted the * check: "If
* can be modified, it's a 1688", which lead to a correct detection
@@ -135,7 +135,7 @@
* About recognition of ESS chips
*
* The distinction of ES688, ES1688, ES1788, ES1887 and ES1888 is described in
- * a (preliminary ??) datasheet on ES1887. It's aim is to identify ES1887, but
+ * a (preliminary ??) datasheet on ES1887. Its aim is to identify ES1887, but
* during detection the text claims that "this chip may be ..." when a step
* fails. This scheme is used to distinct between the above chips.
* It appears however that some PnP chips like ES1868 are recognized as ES1788
@@ -156,9 +156,9 @@
*
* The existing ES1688 support didn't take care of the ES1688+ recording
* levels very well. Whenever a device was selected (recmask) for recording
- * it's recording level was loud, and it couldn't be changed. The fact that
+ * its recording level was loud, and it couldn't be changed. The fact that
* internal register 0xb4 could take care of RECLEV, didn't work meaning until
- * it's value was restored every time the chip was reset; this reset the
+ * its value was restored every time the chip was reset; this reset the
* value of 0xb4 too. I guess that's what 4front also had (have?) trouble with.
*
* About ES1887 support:
@@ -169,9 +169,9 @@
* the latter case the recording volumes are 0.
* Now recording levels of inputs can be controlled, by changing the playback
* levels. Futhermore several devices can be recorded together (which is not
- * possible with the ES1688.
+ * possible with the ES1688).
* Besides the separate recording level control for each input, the common
- * recordig level can also be controlled by RECLEV as described above.
+ * recording level can also be controlled by RECLEV as described above.
*
* Not only ES1887 have this recording mixer. I know the following from the
* documentation:
diff --git a/sound/oss/sh_dac_audio.c b/sound/oss/sh_dac_audio.c
index 3f7427cd195a..83ff8a71f716 100644
--- a/sound/oss/sh_dac_audio.c
+++ b/sound/oss/sh_dac_audio.c
@@ -1,4 +1,14 @@
-#include <linux/config.h>
+/*
+ * sound/oss/sh_dac_audio.c
+ *
+ * SH DAC based sound :(
+ *
+ * Copyright (C) 2004,2005 Andriy Skulysh
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
@@ -7,18 +17,17 @@
#include <linux/fs.h>
#include <linux/sound.h>
#include <linux/soundcard.h>
+#include <linux/interrupt.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/delay.h>
-#include <linux/interrupt.h>
-
+#include <asm/clock.h>
#include <asm/cpu/dac.h>
-
-#ifdef MACH_HP600
+#include <asm/cpu/timer.h>
+#include <asm/machvec.h>
#include <asm/hp6xx/hp6xx.h>
-#include <asm/hd64461/hd64461.h>
-#endif
+#include <asm/hd64461.h>
#define MODNAME "sh_dac_audio"
@@ -27,11 +36,6 @@
#define TMU1_TCR_INIT 0x0020 /* Clock/4, rising edge; interrupt on */
#define TMU1_TSTR_INIT 0x02 /* Bit to turn on TMU1 */
-#define TMU_TSTR 0xfffffe92
-#define TMU1_TCOR 0xfffffea0
-#define TMU1_TCNT 0xfffffea4
-#define TMU1_TCR 0xfffffea8
-
#define BUFFER_SIZE 48000
static int rate;
@@ -72,34 +76,37 @@ static void dac_audio_sync(void)
static void dac_audio_start(void)
{
-#ifdef MACH_HP600
- u16 v;
- v = inw(HD64461_GPADR);
- v &= ~HD64461_GPADR_SPEAKER;
- outw(v, HD64461_GPADR);
-#endif
+ if (mach_is_hp6xx()) {
+ u16 v = inw(HD64461_GPADR);
+ v &= ~HD64461_GPADR_SPEAKER;
+ outw(v, HD64461_GPADR);
+ }
+
sh_dac_enable(CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL);
ctrl_outw(TMU1_TCR_INIT, TMU1_TCR);
}
static void dac_audio_stop(void)
{
-#ifdef MACH_HP600
- u16 v;
-#endif
dac_audio_stop_timer();
-#ifdef MACH_HP600
- v = inw(HD64461_GPADR);
- v |= HD64461_GPADR_SPEAKER;
- outw(v, HD64461_GPADR);
-#endif
+
+ if (mach_is_hp6xx()) {
+ u16 v = inw(HD64461_GPADR);
+ v |= HD64461_GPADR_SPEAKER;
+ outw(v, HD64461_GPADR);
+ }
+
+ sh_dac_output(0, CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL);
sh_dac_disable(CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL);
}
static void dac_audio_set_rate(void)
{
unsigned long interval;
+ struct clk *clk;
- interval = (current_cpu_data.module_clock / 4) / rate;
+ clk = clk_get("module_clk");
+ interval = (clk_get_rate(clk) / 4) / rate;
+ clk_put(clk);
ctrl_outl(interval, TMU1_TCOR);
ctrl_outl(interval, TMU1_TCNT);
}
@@ -298,7 +305,7 @@ static int __init dac_audio_init(void)
dac_audio_set_rate();
retval =
- request_irq(TIMER1_IRQ, timer1_interrupt, SA_INTERRUPT, MODNAME, 0);
+ request_irq(TIMER1_IRQ, timer1_interrupt, IRQF_DISABLED, MODNAME, 0);
if (retval < 0) {
printk(KERN_ERR "sh_dac_audio: IRQ %d request failed\n",
TIMER1_IRQ);
diff --git a/sound/oss/sonicvibes.c b/sound/oss/sonicvibes.c
index 42bd276cfc39..8ea532d40198 100644
--- a/sound/oss/sonicvibes.c
+++ b/sound/oss/sonicvibes.c
@@ -2632,7 +2632,7 @@ static int __devinit sv_probe(struct pci_dev *pcidev, const struct pci_device_id
wrindir(s, SV_CIPCMSR1, ((8000 * 65536 / FULLRATE) >> 8) & 0xff);
wrindir(s, SV_CIADCOUTPUT, 0);
/* request irq */
- if ((ret=request_irq(s->irq,sv_interrupt,SA_SHIRQ,"S3 SonicVibes",s))) {
+ if ((ret=request_irq(s->irq,sv_interrupt,IRQF_SHARED,"S3 SonicVibes",s))) {
printk(KERN_ERR "sv: irq %u in use\n", s->irq);
goto err_irq;
}
diff --git a/sound/oss/sound_config.h b/sound/oss/sound_config.h
index 9f912b8a2969..1a00a3210616 100644
--- a/sound/oss/sound_config.h
+++ b/sound/oss/sound_config.h
@@ -14,7 +14,6 @@
#ifndef _SOUND_CONFIG_H_
#define _SOUND_CONFIG_H_
-#include <linux/config.h>
#include <linux/fs.h>
#include <linux/sound.h>
diff --git a/sound/oss/soundcard.c b/sound/oss/soundcard.c
index d33bb464f70e..0860d6789715 100644
--- a/sound/oss/soundcard.c
+++ b/sound/oss/soundcard.c
@@ -22,7 +22,6 @@
* Christoph Hellwig : Some cleanup work (2000/03/01)
*/
-#include <linux/config.h>
#include "sound_config.h"
#include <linux/init.h>
@@ -38,7 +37,6 @@
#include <linux/wait.h>
#include <linux/slab.h>
#include <linux/ioport.h>
-#include <linux/devfs_fs_kernel.h>
#include <linux/major.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
@@ -564,9 +562,6 @@ static int __init oss_init(void)
sound_dmap_flag = (dmabuf > 0 ? 1 : 0);
for (i = 0; i < sizeof (dev_list) / sizeof *dev_list; i++) {
- devfs_mk_cdev(MKDEV(SOUND_MAJOR, dev_list[i].minor),
- S_IFCHR | dev_list[i].mode,
- "sound/%s", dev_list[i].name);
class_device_create(sound_class, NULL,
MKDEV(SOUND_MAJOR, dev_list[i].minor),
NULL, "%s", dev_list[i].name);
@@ -574,15 +569,10 @@ static int __init oss_init(void)
if (!dev_list[i].num)
continue;
- for (j = 1; j < *dev_list[i].num; j++) {
- devfs_mk_cdev(MKDEV(SOUND_MAJOR,
- dev_list[i].minor + (j*0x10)),
- S_IFCHR | dev_list[i].mode,
- "sound/%s%d", dev_list[i].name, j);
+ for (j = 1; j < *dev_list[i].num; j++)
class_device_create(sound_class, NULL,
MKDEV(SOUND_MAJOR, dev_list[i].minor + (j*0x10)),
NULL, "%s%d", dev_list[i].name, j);
- }
}
if (sound_nblocks >= 1024)
@@ -596,14 +586,11 @@ static void __exit oss_cleanup(void)
int i, j;
for (i = 0; i < sizeof (dev_list) / sizeof *dev_list; i++) {
- devfs_remove("sound/%s", dev_list[i].name);
class_device_destroy(sound_class, MKDEV(SOUND_MAJOR, dev_list[i].minor));
if (!dev_list[i].num)
continue;
- for (j = 1; j < *dev_list[i].num; j++) {
- devfs_remove("sound/%s%d", dev_list[i].name, j);
+ for (j = 1; j < *dev_list[i].num; j++)
class_device_destroy(sound_class, MKDEV(SOUND_MAJOR, dev_list[i].minor + (j*0x10)));
- }
}
unregister_sound_special(1);
diff --git a/sound/oss/trident.c b/sound/oss/trident.c
index e61a454a8150..d4844de0c3b7 100644
--- a/sound/oss/trident.c
+++ b/sound/oss/trident.c
@@ -194,7 +194,6 @@
* sem - guard dmabuf, write re-entry etc
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/ctype.h>
@@ -489,10 +488,6 @@ static void ali_set_spdif_out_rate(struct trident_card *card, unsigned int rate)
static void ali_enable_special_channel(struct trident_state *stat);
static struct trident_channel *ali_alloc_rec_pcm_channel(struct trident_card *card);
static struct trident_channel *ali_alloc_pcm_channel(struct trident_card *card);
-static void ali_restore_regs(struct trident_card *card);
-static void ali_save_regs(struct trident_card *card);
-static int trident_suspend(struct pci_dev *dev, pm_message_t unused);
-static int trident_resume(struct pci_dev *dev);
static void ali_free_pcm_channel(struct trident_card *card, unsigned int channel);
static int ali_setup_multi_channels(struct trident_card *card, int chan_nums);
static unsigned int ali_get_spdif_in_rate(struct trident_card *card);
@@ -508,13 +503,6 @@ static int ali_allocate_other_states_resources(struct trident_state *state,
int chan_nums);
static void ali_free_other_states_resources(struct trident_state *state);
-/* save registers for ALi Power Management */
-static struct ali_saved_registers {
- unsigned long global_regs[ALI_GLOBAL_REGS];
- unsigned long channel_regs[ALI_CHANNELS][ALI_CHANNEL_REGS];
- unsigned mixer_regs[ALI_MIXER_REGS];
-} ali_registers;
-
#define seek_offset(dma_ptr, buffer, cnt, offset, copy_count) do { \
(dma_ptr) += (offset); \
(buffer) += (offset); \
@@ -3654,6 +3642,14 @@ ali_allocate_other_states_resources(struct trident_state *state, int chan_nums)
return 0;
}
+#ifdef CONFIG_PM
+/* save registers for ALi Power Management */
+static struct ali_saved_registers {
+ unsigned long global_regs[ALI_GLOBAL_REGS];
+ unsigned long channel_regs[ALI_CHANNELS][ALI_CHANNEL_REGS];
+ unsigned mixer_regs[ALI_MIXER_REGS];
+} ali_registers;
+
static void
ali_save_regs(struct trident_card *card)
{
@@ -3747,6 +3743,7 @@ trident_resume(struct pci_dev *dev)
}
return 0;
}
+#endif
static struct trident_channel *
ali_alloc_pcm_channel(struct trident_card *card)
@@ -4473,7 +4470,7 @@ trident_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id)
/* claim our irq */
rc = -ENODEV;
- if (request_irq(card->irq, &trident_interrupt, SA_SHIRQ,
+ if (request_irq(card->irq, &trident_interrupt, IRQF_SHARED,
card_names[pci_id->driver_data], card)) {
printk(KERN_ERR "trident: unable to allocate irq %d\n",
card->irq);
@@ -4617,8 +4614,10 @@ static struct pci_driver trident_pci_driver = {
.id_table = trident_pci_tbl,
.probe = trident_probe,
.remove = __devexit_p(trident_remove),
+#ifdef CONFIG_PM
.suspend = trident_suspend,
.resume = trident_resume
+#endif
};
static int __init
diff --git a/sound/oss/via82cxxx_audio.c b/sound/oss/via82cxxx_audio.c
index 1a921ee71aba..08d8c94d01b2 100644
--- a/sound/oss/via82cxxx_audio.c
+++ b/sound/oss/via82cxxx_audio.c
@@ -18,12 +18,12 @@
#define VIA_VERSION "1.9.1-ac4-2.5"
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/pci.h>
+#include <linux/poison.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/proc_fs.h>
@@ -308,7 +308,7 @@ struct via_info {
unsigned sixchannel: 1; /* 8233/35 with 6 channel support */
unsigned volume: 1;
- int locked_rate : 1;
+ unsigned locked_rate : 1;
int mixer_vol; /* 8233/35 volume - not yet implemented */
@@ -2013,7 +2013,7 @@ static int via_interrupt_init (struct via_info *card)
tmp8 |= VIA_CR48_FM_TRAP_TO_NMI;
pci_write_config_byte (card->pdev, VIA_FM_NMI_CTRL, tmp8);
}
- if (request_irq (card->pdev->irq, via_interrupt, SA_SHIRQ, VIA_MODULE_NAME, card)) {
+ if (request_irq (card->pdev->irq, via_interrupt, IRQF_SHARED, VIA_MODULE_NAME, card)) {
printk (KERN_ERR PFX "unable to obtain IRQ %d, aborting\n",
card->pdev->irq);
DPRINTK ("EXIT, returning -EBUSY\n");
@@ -2022,7 +2022,7 @@ static int via_interrupt_init (struct via_info *card)
}
else
{
- if (request_irq (card->pdev->irq, via_new_interrupt, SA_SHIRQ, VIA_MODULE_NAME, card)) {
+ if (request_irq (card->pdev->irq, via_new_interrupt, IRQF_SHARED, VIA_MODULE_NAME, card)) {
printk (KERN_ERR PFX "unable to obtain IRQ %d, aborting\n",
card->pdev->irq);
DPRINTK ("EXIT, returning -EBUSY\n");
@@ -3522,7 +3522,7 @@ err_out_have_mixer:
err_out_kfree:
#ifndef VIA_NDEBUG
- memset (card, 0xAB, sizeof (*card)); /* poison memory */
+ memset (card, OSS_POISON_FREE, sizeof (*card)); /* poison memory */
#endif
kfree (card);
@@ -3559,7 +3559,7 @@ static void __devexit via_remove_one (struct pci_dev *pdev)
via_ac97_cleanup (card);
#ifndef VIA_NDEBUG
- memset (card, 0xAB, sizeof (*card)); /* poison memory */
+ memset (card, OSS_POISON_FREE, sizeof (*card)); /* poison memory */
#endif
kfree (card);
diff --git a/sound/oss/vidc.c b/sound/oss/vidc.c
index 00fe5cec9dc1..8932d89408ef 100644
--- a/sound/oss/vidc.c
+++ b/sound/oss/vidc.c
@@ -17,7 +17,6 @@
* We currently support a mixer device, but it is currently non-functional.
*/
-#include <linux/config.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
diff --git a/sound/oss/waveartist.c b/sound/oss/waveartist.c
index afcb524a40eb..22d26624b34a 100644
--- a/sound/oss/waveartist.c
+++ b/sound/oss/waveartist.c
@@ -35,7 +35,6 @@
#include <linux/module.h>
#include <linux/init.h>
-#include <linux/config.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
diff --git a/sound/oss/wavfront.c b/sound/oss/wavfront.c
index b1a4eeb9dc08..1dec3958cc7b 100644
--- a/sound/oss/wavfront.c
+++ b/sound/oss/wavfront.c
@@ -2268,7 +2268,7 @@ static int __init wavefront_hw_reset (void)
}
if (request_irq (dev.irq, wavefrontintr,
- SA_INTERRUPT|SA_SHIRQ,
+ IRQF_DISABLED|IRQF_SHARED,
"wavefront synth", &dev) < 0) {
printk (KERN_WARNING LOGNAME "IRQ %d not available!\n",
dev.irq);
diff --git a/sound/oss/wf_midi.c b/sound/oss/wf_midi.c
index 7b167b74375b..3f3a390014ca 100644
--- a/sound/oss/wf_midi.c
+++ b/sound/oss/wf_midi.c
@@ -820,7 +820,7 @@ int __init install_wf_mpu (void)
/* OK, now we're configured to handle an interrupt ... */
- if (request_irq (phys_dev->irq, wf_mpuintr, SA_INTERRUPT|SA_SHIRQ,
+ if (request_irq (phys_dev->irq, wf_mpuintr, IRQF_DISABLED|IRQF_SHARED,
"wavefront midi", phys_dev) < 0) {
printk (KERN_ERR "WF-MPU: Failed to allocate IRQ%d\n",
diff --git a/sound/oss/ymfpci.c b/sound/oss/ymfpci.c
index bf90c124a7e6..6e22472df952 100644
--- a/sound/oss/ymfpci.c
+++ b/sound/oss/ymfpci.c
@@ -2573,7 +2573,7 @@ static int __devinit ymf_probe_one(struct pci_dev *pcidev, const struct pci_devi
goto out_disable_dsp;
ymf_memload(codec);
- if (request_irq(pcidev->irq, ymf_interrupt, SA_SHIRQ, "ymfpci", codec) != 0) {
+ if (request_irq(pcidev->irq, ymf_interrupt, IRQF_SHARED, "ymfpci", codec) != 0) {
printk(KERN_ERR "ymfpci: unable to request IRQ %d\n",
pcidev->irq);
goto out_memfree;
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index d37346b12dc0..8a6b1803c763 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -16,16 +16,16 @@ config SND_AD1889
will be called snd-ad1889.
config SND_ALS300
- tristate "Avance Logic ALS300/ALS300+"
- depends on SND
- select SND_PCM
- select SND_AC97_CODEC
- select SND_OPL3_LIB
- help
- Say 'Y' or 'M' to include support for Avance Logic ALS300/ALS300+
+ tristate "Avance Logic ALS300/ALS300+"
+ depends on SND
+ select SND_PCM
+ select SND_AC97_CODEC
+ select SND_OPL3_LIB
+ help
+ Say 'Y' or 'M' to include support for Avance Logic ALS300/ALS300+
- To compile this driver as a module, choose M here: the module
- will be called snd-als300
+ To compile this driver as a module, choose M here: the module
+ will be called snd-als300
config SND_ALS4000
tristate "Avance Logic ALS4000"
@@ -78,49 +78,49 @@ config SND_ATIIXP_MODEM
will be called snd-atiixp-modem.
config SND_AU8810
- tristate "Aureal Advantage"
- depends on SND
+ tristate "Aureal Advantage"
+ depends on SND
select SND_MPU401_UART
select SND_AC97_CODEC
- help
+ help
Say Y here to include support for Aureal Advantage soundcards.
Supported features: Hardware Mixer, SRC, EQ and SPDIF output.
- 3D support code is in place, but not yet useable. For more info,
- email the ALSA developer list, or <mjander@users.sourceforge.net>.
+ 3D support code is in place, but not yet useable. For more info,
+ email the ALSA developer list, or <mjander@users.sourceforge.net>.
To compile this driver as a module, choose M here: the module
will be called snd-au8810.
-
+
config SND_AU8820
- tristate "Aureal Vortex"
- depends on SND
+ tristate "Aureal Vortex"
+ depends on SND
select SND_MPU401_UART
select SND_AC97_CODEC
- help
+ help
Say Y here to include support for Aureal Vortex soundcards.
- Supported features: Hardware Mixer and SRC. For more info, email
- the ALSA developer list, or <mjander@users.sourceforge.net>.
+ Supported features: Hardware Mixer and SRC. For more info, email
+ the ALSA developer list, or <mjander@users.sourceforge.net>.
To compile this driver as a module, choose M here: the module
will be called snd-au8820.
-
+
config SND_AU8830
- tristate "Aureal Vortex 2"
- depends on SND
+ tristate "Aureal Vortex 2"
+ depends on SND
select SND_MPU401_UART
select SND_AC97_CODEC
- help
+ help
Say Y here to include support for Aureal Vortex 2 soundcards.
- Supported features: Hardware Mixer, SRC, EQ and SPDIF output.
- 3D support code is in place, but not yet useable. For more info,
- email the ALSA developer list, or <mjander@users.sourceforge.net>.
+ Supported features: Hardware Mixer, SRC, EQ and SPDIF output.
+ 3D support code is in place, but not yet useable. For more info,
+ email the ALSA developer list, or <mjander@users.sourceforge.net>.
To compile this driver as a module, choose M here: the module
will be called snd-au8830.
-
+
config SND_AZT3328
tristate "Aztech AZF3328 / PCI168 (EXPERIMENTAL)"
depends on SND && EXPERIMENTAL
@@ -135,10 +135,10 @@ config SND_AZT3328
will be called snd-azt3328.
config SND_BT87X
- tristate "Bt87x Audio Capture"
- depends on SND
+ tristate "Bt87x Audio Capture"
+ depends on SND
select SND_PCM
- help
+ help
If you want to record audio from TV cards based on
Brooktree Bt878/Bt879 chips, say Y here and read
<file:Documentation/sound/alsa/Bt87x.txt>.
@@ -209,7 +209,7 @@ config SND_CS46XX
config SND_CS46XX_NEW_DSP
bool "Cirrus Logic (Sound Fusion) New DSP support"
depends on SND_CS46XX
- default y
+ default y
help
Say Y here to use a new DSP image for SPDIF and dual codecs.
@@ -225,7 +225,7 @@ config SND_CS5535AUDIO
referred to as NS CS5535 IO or AMD CS5535 IO companion in
various literature. This driver also supports the CS5536 audio
device. However, for both chips, on certain boards, you may
- need to use ac97_quirk=hp_only if your board has physically
+ need to use ac97_quirk=hp_only if your board has physically
mapped headphone out to master output. If that works for you,
send lspci -vvv output to the mailing list so that your board
can be identified in the quirks list.
@@ -233,6 +233,143 @@ config SND_CS5535AUDIO
To compile this driver as a module, choose M here: the module
will be called snd-cs5535audio.
+config SND_DARLA20
+ tristate "(Echoaudio) Darla20"
+ depends on SND
+ depends on FW_LOADER
+ select SND_PCM
+ help
+ Say 'Y' or 'M' to include support for Echoaudio Darla.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-darla20
+
+config SND_GINA20
+ tristate "(Echoaudio) Gina20"
+ depends on SND
+ depends on FW_LOADER
+ select SND_PCM
+ help
+ Say 'Y' or 'M' to include support for Echoaudio Gina.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-gina20
+
+config SND_LAYLA20
+ tristate "(Echoaudio) Layla20"
+ depends on SND
+ depends on FW_LOADER
+ select SND_RAWMIDI
+ select SND_PCM
+ help
+ Say 'Y' or 'M' to include support for Echoaudio Layla.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-layla20
+
+config SND_DARLA24
+ tristate "(Echoaudio) Darla24"
+ depends on SND
+ depends on FW_LOADER
+ select SND_PCM
+ help
+ Say 'Y' or 'M' to include support for Echoaudio Darla24.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-darla24
+
+config SND_GINA24
+ tristate "(Echoaudio) Gina24"
+ depends on SND
+ depends on FW_LOADER
+ select SND_PCM
+ help
+ Say 'Y' or 'M' to include support for Echoaudio Gina24.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-gina24
+
+config SND_LAYLA24
+ tristate "(Echoaudio) Layla24"
+ depends on SND
+ depends on FW_LOADER
+ select SND_RAWMIDI
+ select SND_PCM
+ help
+ Say 'Y' or 'M' to include support for Echoaudio Layla24.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-layla24
+
+config SND_MONA
+ tristate "(Echoaudio) Mona"
+ depends on SND
+ depends on FW_LOADER
+ select SND_RAWMIDI
+ select SND_PCM
+ help
+ Say 'Y' or 'M' to include support for Echoaudio Mona.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-mona
+
+config SND_MIA
+ tristate "(Echoaudio) Mia"
+ depends on SND
+ depends on FW_LOADER
+ select SND_RAWMIDI
+ select SND_PCM
+ help
+ Say 'Y' or 'M' to include support for Echoaudio Mia and Mia-midi.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-mia
+
+config SND_ECHO3G
+ tristate "(Echoaudio) 3G cards"
+ depends on SND
+ depends on FW_LOADER
+ select SND_RAWMIDI
+ select SND_PCM
+ help
+ Say 'Y' or 'M' to include support for Echoaudio Gina3G and Layla3G.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-echo3g
+
+config SND_INDIGO
+ tristate "(Echoaudio) Indigo"
+ depends on SND
+ depends on FW_LOADER
+ select SND_PCM
+ help
+ Say 'Y' or 'M' to include support for Echoaudio Indigo.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-indigo
+
+config SND_INDIGOIO
+ tristate "(Echoaudio) Indigo IO"
+ depends on SND
+ depends on FW_LOADER
+ select SND_PCM
+ help
+ Say 'Y' or 'M' to include support for Echoaudio Indigo IO.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-indigoio
+
+config SND_INDIGODJ
+ tristate "(Echoaudio) Indigo DJ"
+ depends on SND
+ depends on FW_LOADER
+ select SND_PCM
+ help
+ Say 'Y' or 'M' to include support for Echoaudio Indigo DJ.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-indigodj
+
config SND_EMU10K1
tristate "Emu10k1 (SB Live!, Audigy, E-mu APS)"
depends on SND
@@ -323,17 +460,22 @@ config SND_FM801
To compile this driver as a module, choose M here: the module
will be called snd-fm801.
-config SND_FM801_TEA575X
- tristate "ForteMedia FM801 + TEA5757 tuner"
+config SND_FM801_TEA575X_BOOL
+ bool "ForteMedia FM801 + TEA5757 tuner"
depends on SND_FM801
- select VIDEO_DEV
help
Say Y here to include support for soundcards based on the ForteMedia
FM801 chip with a TEA5757 tuner connected to GPIO1-3 pins (Media
- Forte SF256-PCS-02).
+ Forte SF256-PCS-02) into the snd-fm801 driver.
- To compile this driver as a module, choose M here: the module
- will be called snd-fm801-tea575x.
+ This will enable support for the old V4L1 API.
+
+config SND_FM801_TEA575X
+ tristate
+ depends on SND_FM801_TEA575X_BOOL
+ default SND_FM801
+ select VIDEO_V4L1
+ select VIDEO_DEV
config SND_HDA_INTEL
tristate "Intel HD Audio"
@@ -420,8 +562,8 @@ config SND_INTEL8X0
will be called snd-intel8x0.
config SND_INTEL8X0M
- tristate "Intel/SiS/nVidia/AMD MC97 Modem (EXPERIMENTAL)"
- depends on SND && EXPERIMENTAL
+ tristate "Intel/SiS/nVidia/AMD MC97 Modem"
+ depends on SND
select SND_AC97_CODEC
help
Say Y here to include support for the integrated MC97 modem on
@@ -602,4 +744,17 @@ config SND_YMFPCI
To compile this driver as a module, choose M here: the module
will be called snd-ymfpci.
+config SND_AC97_POWER_SAVE
+ bool "AC97 Power-Saving Mode"
+ depends on SND_AC97_CODEC && EXPERIMENTAL
+ default n
+ help
+ Say Y here to enable the aggressive power-saving support of
+ AC97 codecs. In this mode, the power-mode is dynamically
+ controlled at each open/close.
+
+ The mode is activated by passing power_save=1 option to
+ snd-ac97-codec driver. You can toggle it dynamically over
+ sysfs, too.
+
endmenu
diff --git a/sound/pci/Makefile b/sound/pci/Makefile
index cba5105aafea..e06736da9ef1 100644
--- a/sound/pci/Makefile
+++ b/sound/pci/Makefile
@@ -57,6 +57,7 @@ obj-$(CONFIG_SND) += \
ca0106/ \
cs46xx/ \
cs5535audio/ \
+ echoaudio/ \
emu10k1/ \
hda/ \
ice1712/ \
diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c
index 0abf2808d59f..a79e91850ba3 100644
--- a/sound/pci/ac97/ac97_codec.c
+++ b/sound/pci/ac97/ac97_codec.c
@@ -31,6 +31,7 @@
#include <linux/mutex.h>
#include <sound/core.h>
#include <sound/pcm.h>
+#include <sound/tlv.h>
#include <sound/ac97_codec.h>
#include <sound/asoundef.h>
#include <sound/initval.h>
@@ -47,6 +48,11 @@ static int enable_loopback;
module_param(enable_loopback, bool, 0444);
MODULE_PARM_DESC(enable_loopback, "Enable AC97 ADC/DAC Loopback Control");
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+static int power_save;
+module_param(power_save, bool, 0644);
+MODULE_PARM_DESC(power_save, "Enable AC97 power-saving control");
+#endif
/*
*/
@@ -151,7 +157,7 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = {
{ 0x4e534300, 0xffffffff, "LM4540,43,45,46,48", NULL, NULL }, // only guess --jk
{ 0x4e534331, 0xffffffff, "LM4549", NULL, NULL },
{ 0x4e534350, 0xffffffff, "LM4550", patch_lm4550, NULL }, // volume wrap fix
-{ 0x50534304, 0xffffffff, "UCB1400", NULL, NULL },
+{ 0x50534304, 0xffffffff, "UCB1400", patch_ucb1400, NULL },
{ 0x53494c20, 0xffffffe0, "Si3036,8", mpatch_si3036, mpatch_si3036, AC97_MODEM_PATCH },
{ 0x54524102, 0xffffffff, "TR28022", NULL, NULL },
{ 0x54524106, 0xffffffff, "TR28026", NULL, NULL },
@@ -187,6 +193,8 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = {
};
+static void update_power_regs(struct snd_ac97 *ac97);
+
/*
* I/O routines
*/
@@ -554,6 +562,18 @@ int snd_ac97_put_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value
}
err = snd_ac97_update_bits(ac97, reg, val_mask, val);
snd_ac97_page_restore(ac97, page_save);
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+ /* check analog mixer power-down */
+ if ((val_mask & 0x8000) &&
+ (kcontrol->private_value & (1<<30))) {
+ if (val & 0x8000)
+ ac97->power_up &= ~(1 << (reg>>1));
+ else
+ ac97->power_up |= 1 << (reg>>1);
+ if (power_save)
+ update_power_regs(ac97);
+ }
+#endif
return err;
}
@@ -573,7 +593,7 @@ AC97_SINGLE("PC Speaker Playback Volume", AC97_PC_BEEP, 1, 15, 1)
};
static const struct snd_kcontrol_new snd_ac97_controls_mic_boost =
- AC97_SINGLE("Mic Boost (+20dB) Switch", AC97_MIC, 6, 1, 0);
+ AC97_SINGLE("Mic Boost (+20dB)", AC97_MIC, 6, 1, 0);
static const char* std_rec_sel[] = {"Mic", "CD", "Video", "Aux", "Line", "Mix", "Mix Mono", "Phone"};
@@ -615,7 +635,7 @@ AC97_SINGLE("Simulated Stereo Enhancement", AC97_GENERAL_PURPOSE, 14, 1, 0),
AC97_SINGLE("3D Control - Switch", AC97_GENERAL_PURPOSE, 13, 1, 0),
AC97_SINGLE("Loudness (bass boost)", AC97_GENERAL_PURPOSE, 12, 1, 0),
AC97_ENUM("Mono Output Select", std_enum[2]),
-AC97_ENUM("Mic Select Capture Switch", std_enum[3]),
+AC97_ENUM("Mic Select", std_enum[3]),
AC97_SINGLE("ADC/DAC Loopback", AC97_GENERAL_PURPOSE, 7, 1, 0)
};
@@ -962,6 +982,10 @@ static int snd_ac97_bus_dev_free(struct snd_device *device)
static int snd_ac97_free(struct snd_ac97 *ac97)
{
if (ac97) {
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+ if (ac97->power_workq)
+ destroy_workqueue(ac97->power_workq);
+#endif
snd_ac97_proc_done(ac97);
if (ac97->bus)
ac97->bus->codec[ac97->num] = NULL;
@@ -1117,7 +1141,9 @@ struct snd_kcontrol *snd_ac97_cnew(const struct snd_kcontrol_new *_template, str
/*
* create mute switch(es) for normal stereo controls
*/
-static int snd_ac97_cmute_new_stereo(struct snd_card *card, char *name, int reg, int check_stereo, struct snd_ac97 *ac97)
+static int snd_ac97_cmute_new_stereo(struct snd_card *card, char *name, int reg,
+ int check_stereo, int check_amix,
+ struct snd_ac97 *ac97)
{
struct snd_kcontrol *kctl;
int err;
@@ -1137,10 +1163,14 @@ static int snd_ac97_cmute_new_stereo(struct snd_card *card, char *name, int reg,
}
if (mute_mask == 0x8080) {
struct snd_kcontrol_new tmp = AC97_DOUBLE(name, reg, 15, 7, 1, 1);
+ if (check_amix)
+ tmp.private_value |= (1 << 30);
tmp.index = ac97->num;
kctl = snd_ctl_new1(&tmp, ac97);
} else {
struct snd_kcontrol_new tmp = AC97_SINGLE(name, reg, 15, 1, 1);
+ if (check_amix)
+ tmp.private_value |= (1 << 30);
tmp.index = ac97->num;
kctl = snd_ctl_new1(&tmp, ac97);
}
@@ -1153,6 +1183,32 @@ static int snd_ac97_cmute_new_stereo(struct snd_card *card, char *name, int reg,
}
/*
+ * set dB information
+ */
+static DECLARE_TLV_DB_SCALE(db_scale_4bit, -4500, 300, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_5bit, -4650, 150, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
+
+static unsigned int *find_db_scale(unsigned int maxval)
+{
+ switch (maxval) {
+ case 0x0f: return db_scale_4bit;
+ case 0x1f: return db_scale_5bit;
+ case 0x3f: return db_scale_6bit;
+ }
+ return NULL;
+}
+
+static void set_tlv_db_scale(struct snd_kcontrol *kctl, unsigned int *tlv)
+{
+ kctl->tlv.p = tlv;
+ if (tlv)
+ kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+}
+
+/*
* create a volume for normal stereo/mono controls
*/
static int snd_ac97_cvol_new(struct snd_card *card, char *name, int reg, unsigned int lo_max,
@@ -1174,6 +1230,10 @@ static int snd_ac97_cvol_new(struct snd_card *card, char *name, int reg, unsigne
tmp.index = ac97->num;
kctl = snd_ctl_new1(&tmp, ac97);
}
+ if (reg >= AC97_PHONE && reg <= AC97_PCM)
+ set_tlv_db_scale(kctl, db_scale_5bit_12db_max);
+ else
+ set_tlv_db_scale(kctl, find_db_scale(lo_max));
err = snd_ctl_add(card, kctl);
if (err < 0)
return err;
@@ -1186,7 +1246,9 @@ static int snd_ac97_cvol_new(struct snd_card *card, char *name, int reg, unsigne
/*
* create a mute-switch and a volume for normal stereo/mono controls
*/
-static int snd_ac97_cmix_new_stereo(struct snd_card *card, const char *pfx, int reg, int check_stereo, struct snd_ac97 *ac97)
+static int snd_ac97_cmix_new_stereo(struct snd_card *card, const char *pfx,
+ int reg, int check_stereo, int check_amix,
+ struct snd_ac97 *ac97)
{
int err;
char name[44];
@@ -1197,7 +1259,9 @@ static int snd_ac97_cmix_new_stereo(struct snd_card *card, const char *pfx, int
if (snd_ac97_try_bit(ac97, reg, 15)) {
sprintf(name, "%s Switch", pfx);
- if ((err = snd_ac97_cmute_new_stereo(card, name, reg, check_stereo, ac97)) < 0)
+ if ((err = snd_ac97_cmute_new_stereo(card, name, reg,
+ check_stereo, check_amix,
+ ac97)) < 0)
return err;
}
check_volume_resolution(ac97, reg, &lo_max, &hi_max);
@@ -1209,8 +1273,10 @@ static int snd_ac97_cmix_new_stereo(struct snd_card *card, const char *pfx, int
return 0;
}
-#define snd_ac97_cmix_new(card, pfx, reg, ac97) snd_ac97_cmix_new_stereo(card, pfx, reg, 0, ac97)
-#define snd_ac97_cmute_new(card, name, reg, ac97) snd_ac97_cmute_new_stereo(card, name, reg, 0, ac97)
+#define snd_ac97_cmix_new(card, pfx, reg, acheck, ac97) \
+ snd_ac97_cmix_new_stereo(card, pfx, reg, 0, acheck, ac97)
+#define snd_ac97_cmute_new(card, name, reg, acheck, ac97) \
+ snd_ac97_cmute_new_stereo(card, name, reg, 0, acheck, ac97)
static unsigned int snd_ac97_determine_spdif_rates(struct snd_ac97 *ac97);
@@ -1226,9 +1292,11 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
/* AD claims to remove this control from AD1887, although spec v2.2 does not allow this */
if (snd_ac97_try_volume_mix(ac97, AC97_MASTER)) {
if (ac97->flags & AC97_HAS_NO_MASTER_VOL)
- err = snd_ac97_cmute_new(card, "Master Playback Switch", AC97_MASTER, ac97);
+ err = snd_ac97_cmute_new(card, "Master Playback Switch",
+ AC97_MASTER, 0, ac97);
else
- err = snd_ac97_cmix_new(card, "Master Playback", AC97_MASTER, ac97);
+ err = snd_ac97_cmix_new(card, "Master Playback",
+ AC97_MASTER, 0, ac97);
if (err < 0)
return err;
}
@@ -1245,6 +1313,7 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
snd_ac97_change_volume_params2(ac97, AC97_CENTER_LFE_MASTER, 0, &max);
kctl->private_value &= ~(0xff << 16);
kctl->private_value |= (int)max << 16;
+ set_tlv_db_scale(kctl, find_db_scale(max));
snd_ac97_write_cache(ac97, AC97_CENTER_LFE_MASTER, ac97->regs[AC97_CENTER_LFE_MASTER] | max);
}
@@ -1258,6 +1327,7 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
snd_ac97_change_volume_params2(ac97, AC97_CENTER_LFE_MASTER, 8, &max);
kctl->private_value &= ~(0xff << 16);
kctl->private_value |= (int)max << 16;
+ set_tlv_db_scale(kctl, find_db_scale(max));
snd_ac97_write_cache(ac97, AC97_CENTER_LFE_MASTER, ac97->regs[AC97_CENTER_LFE_MASTER] | max << 8);
}
@@ -1265,19 +1335,23 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
if ((snd_ac97_try_volume_mix(ac97, AC97_SURROUND_MASTER))
&& !(ac97->flags & AC97_AD_MULTI)) {
/* Surround Master (0x38) is with stereo mutes */
- if ((err = snd_ac97_cmix_new_stereo(card, "Surround Playback", AC97_SURROUND_MASTER, 1, ac97)) < 0)
+ if ((err = snd_ac97_cmix_new_stereo(card, "Surround Playback",
+ AC97_SURROUND_MASTER, 1, 0,
+ ac97)) < 0)
return err;
}
/* build headphone controls */
if (snd_ac97_try_volume_mix(ac97, AC97_HEADPHONE)) {
- if ((err = snd_ac97_cmix_new(card, "Headphone Playback", AC97_HEADPHONE, ac97)) < 0)
+ if ((err = snd_ac97_cmix_new(card, "Headphone Playback",
+ AC97_HEADPHONE, 0, ac97)) < 0)
return err;
}
/* build master mono controls */
if (snd_ac97_try_volume_mix(ac97, AC97_MASTER_MONO)) {
- if ((err = snd_ac97_cmix_new(card, "Master Mono Playback", AC97_MASTER_MONO, ac97)) < 0)
+ if ((err = snd_ac97_cmix_new(card, "Master Mono Playback",
+ AC97_MASTER_MONO, 0, ac97)) < 0)
return err;
}
@@ -1301,8 +1375,9 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
((ac97->flags & AC97_HAS_PC_BEEP) ||
snd_ac97_try_volume_mix(ac97, AC97_PC_BEEP))) {
for (idx = 0; idx < 2; idx++)
- if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_pc_beep[idx], ac97))) < 0)
+ if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_pc_beep[idx], ac97))) < 0)
return err;
+ set_tlv_db_scale(kctl, db_scale_4bit);
snd_ac97_write_cache(ac97, AC97_PC_BEEP,
snd_ac97_read(ac97, AC97_PC_BEEP) | 0x801e);
}
@@ -1310,7 +1385,8 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
/* build Phone controls */
if (!(ac97->flags & AC97_HAS_NO_PHONE)) {
if (snd_ac97_try_volume_mix(ac97, AC97_PHONE)) {
- if ((err = snd_ac97_cmix_new(card, "Phone Playback", AC97_PHONE, ac97)) < 0)
+ if ((err = snd_ac97_cmix_new(card, "Phone Playback",
+ AC97_PHONE, 1, ac97)) < 0)
return err;
}
}
@@ -1318,7 +1394,8 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
/* build MIC controls */
if (!(ac97->flags & AC97_HAS_NO_MIC)) {
if (snd_ac97_try_volume_mix(ac97, AC97_MIC)) {
- if ((err = snd_ac97_cmix_new(card, "Mic Playback", AC97_MIC, ac97)) < 0)
+ if ((err = snd_ac97_cmix_new(card, "Mic Playback",
+ AC97_MIC, 1, ac97)) < 0)
return err;
if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_mic_boost, ac97))) < 0)
return err;
@@ -1327,14 +1404,16 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
/* build Line controls */
if (snd_ac97_try_volume_mix(ac97, AC97_LINE)) {
- if ((err = snd_ac97_cmix_new(card, "Line Playback", AC97_LINE, ac97)) < 0)
+ if ((err = snd_ac97_cmix_new(card, "Line Playback",
+ AC97_LINE, 1, ac97)) < 0)
return err;
}
/* build CD controls */
if (!(ac97->flags & AC97_HAS_NO_CD)) {
if (snd_ac97_try_volume_mix(ac97, AC97_CD)) {
- if ((err = snd_ac97_cmix_new(card, "CD Playback", AC97_CD, ac97)) < 0)
+ if ((err = snd_ac97_cmix_new(card, "CD Playback",
+ AC97_CD, 1, ac97)) < 0)
return err;
}
}
@@ -1342,7 +1421,8 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
/* build Video controls */
if (!(ac97->flags & AC97_HAS_NO_VIDEO)) {
if (snd_ac97_try_volume_mix(ac97, AC97_VIDEO)) {
- if ((err = snd_ac97_cmix_new(card, "Video Playback", AC97_VIDEO, ac97)) < 0)
+ if ((err = snd_ac97_cmix_new(card, "Video Playback",
+ AC97_VIDEO, 1, ac97)) < 0)
return err;
}
}
@@ -1350,7 +1430,8 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
/* build Aux controls */
if (!(ac97->flags & AC97_HAS_NO_AUX)) {
if (snd_ac97_try_volume_mix(ac97, AC97_AUX)) {
- if ((err = snd_ac97_cmix_new(card, "Aux Playback", AC97_AUX, ac97)) < 0)
+ if ((err = snd_ac97_cmix_new(card, "Aux Playback",
+ AC97_AUX, 1, ac97)) < 0)
return err;
}
}
@@ -1363,31 +1444,38 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
else
init_val = 0x9f1f;
for (idx = 0; idx < 2; idx++)
- if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_ad18xx_pcm[idx], ac97))) < 0)
+ if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_pcm[idx], ac97))) < 0)
return err;
+ set_tlv_db_scale(kctl, db_scale_5bit);
ac97->spec.ad18xx.pcmreg[0] = init_val;
if (ac97->scaps & AC97_SCAP_SURROUND_DAC) {
for (idx = 0; idx < 2; idx++)
- if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_ad18xx_surround[idx], ac97))) < 0)
+ if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_surround[idx], ac97))) < 0)
return err;
+ set_tlv_db_scale(kctl, db_scale_5bit);
ac97->spec.ad18xx.pcmreg[1] = init_val;
}
if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) {
for (idx = 0; idx < 2; idx++)
- if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_ad18xx_center[idx], ac97))) < 0)
+ if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_center[idx], ac97))) < 0)
return err;
+ set_tlv_db_scale(kctl, db_scale_5bit);
for (idx = 0; idx < 2; idx++)
- if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_ad18xx_lfe[idx], ac97))) < 0)
+ if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_lfe[idx], ac97))) < 0)
return err;
+ set_tlv_db_scale(kctl, db_scale_5bit);
ac97->spec.ad18xx.pcmreg[2] = init_val;
}
snd_ac97_write_cache(ac97, AC97_PCM, init_val);
} else {
if (!(ac97->flags & AC97_HAS_NO_STD_PCM)) {
if (ac97->flags & AC97_HAS_NO_PCM_VOL)
- err = snd_ac97_cmute_new(card, "PCM Playback Switch", AC97_PCM, ac97);
+ err = snd_ac97_cmute_new(card,
+ "PCM Playback Switch",
+ AC97_PCM, 0, ac97);
else
- err = snd_ac97_cmix_new(card, "PCM Playback", AC97_PCM, ac97);
+ err = snd_ac97_cmix_new(card, "PCM Playback",
+ AC97_PCM, 0, ac97);
if (err < 0)
return err;
}
@@ -1398,19 +1486,23 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_capture_src, ac97))) < 0)
return err;
if (snd_ac97_try_bit(ac97, AC97_REC_GAIN, 15)) {
- if ((err = snd_ac97_cmute_new(card, "Capture Switch", AC97_REC_GAIN, ac97)) < 0)
+ err = snd_ac97_cmute_new(card, "Capture Switch",
+ AC97_REC_GAIN, 0, ac97);
+ if (err < 0)
return err;
}
- if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_capture_vol, ac97))) < 0)
+ if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_control_capture_vol, ac97))) < 0)
return err;
+ set_tlv_db_scale(kctl, db_scale_rec_gain);
snd_ac97_write_cache(ac97, AC97_REC_SEL, 0x0000);
snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x0000);
}
/* build MIC Capture controls */
if (snd_ac97_try_volume_mix(ac97, AC97_REC_GAIN_MIC)) {
for (idx = 0; idx < 2; idx++)
- if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_mic_capture[idx], ac97))) < 0)
+ if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_mic_capture[idx], ac97))) < 0)
return err;
+ set_tlv_db_scale(kctl, db_scale_rec_gain);
snd_ac97_write_cache(ac97, AC97_REC_GAIN_MIC, 0x0000);
}
@@ -1481,6 +1573,12 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
}
/* build S/PDIF controls */
+
+ /* Hack for ASUS P5P800-VM, which does not indicate S/PDIF capability */
+ if (ac97->subsystem_vendor == 0x1043 &&
+ ac97->subsystem_device == 0x810f)
+ ac97->ext_id |= AC97_EI_SPDIF;
+
if ((ac97->ext_id & AC97_EI_SPDIF) && !(ac97->scaps & AC97_SCAP_NO_SPDIF)) {
if (ac97->build_ops->build_spdif) {
if ((err = ac97->build_ops->build_spdif(ac97)) < 0)
@@ -1817,18 +1915,25 @@ static int snd_ac97_dev_register(struct snd_device *device)
return 0;
}
-/* unregister ac97 codec */
-static int snd_ac97_dev_unregister(struct snd_device *device)
+/* disconnect ac97 codec */
+static int snd_ac97_dev_disconnect(struct snd_device *device)
{
struct snd_ac97 *ac97 = device->device_data;
if (ac97->dev.bus)
device_unregister(&ac97->dev);
- return snd_ac97_free(ac97);
+ return 0;
}
/* build_ops to do nothing */
static struct snd_ac97_build_ops null_build_ops;
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+static void do_update_power(void *data)
+{
+ update_power_regs(data);
+}
+#endif
+
/**
* snd_ac97_mixer - create an Codec97 component
* @bus: the AC97 bus which codec is attached to
@@ -1860,7 +1965,7 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
static struct snd_device_ops ops = {
.dev_free = snd_ac97_dev_free,
.dev_register = snd_ac97_dev_register,
- .dev_unregister = snd_ac97_dev_unregister,
+ .dev_disconnect = snd_ac97_dev_disconnect,
};
snd_assert(rac97 != NULL, return -EINVAL);
@@ -1883,6 +1988,10 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
bus->codec[ac97->num] = ac97;
mutex_init(&ac97->reg_mutex);
mutex_init(&ac97->page_mutex);
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+ ac97->power_workq = create_workqueue("ac97");
+ INIT_WORK(&ac97->power_work, do_update_power, ac97);
+#endif
#ifdef CONFIG_PCI
if (ac97->pci) {
@@ -2117,15 +2226,8 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
return -ENOMEM;
}
}
- /* make sure the proper powerdown bits are cleared */
- if (ac97->scaps && ac97_is_audio(ac97)) {
- reg = snd_ac97_read(ac97, AC97_EXTENDED_STATUS);
- if (ac97->scaps & AC97_SCAP_SURROUND_DAC)
- reg &= ~AC97_EA_PRJ;
- if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC)
- reg &= ~(AC97_EA_PRI | AC97_EA_PRK);
- snd_ac97_write_cache(ac97, AC97_EXTENDED_STATUS, reg);
- }
+ if (ac97_is_audio(ac97))
+ update_power_regs(ac97);
snd_ac97_proc_init(ac97);
if ((err = snd_device_new(card, SNDRV_DEV_CODEC, ac97, &ops)) < 0) {
snd_ac97_free(ac97);
@@ -2153,19 +2255,152 @@ static void snd_ac97_powerdown(struct snd_ac97 *ac97)
snd_ac97_write(ac97, AC97_HEADPHONE, 0x9f9f);
}
- power = ac97->regs[AC97_POWERDOWN] | 0x8000; /* EAPD */
- power |= 0x4000; /* Headphone amplifier powerdown */
- power |= 0x0300; /* ADC & DAC powerdown */
- snd_ac97_write(ac97, AC97_POWERDOWN, power);
- udelay(100);
- power |= 0x0400; /* Analog Mixer powerdown (Vref on) */
+ /* surround, CLFE, mic powerdown */
+ power = ac97->regs[AC97_EXTENDED_STATUS];
+ if (ac97->scaps & AC97_SCAP_SURROUND_DAC)
+ power |= AC97_EA_PRJ;
+ if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC)
+ power |= AC97_EA_PRI | AC97_EA_PRK;
+ power |= AC97_EA_PRL;
+ snd_ac97_write(ac97, AC97_EXTENDED_STATUS, power);
+
+ /* powerdown external amplifier */
+ if (ac97->scaps & AC97_SCAP_INV_EAPD)
+ power = ac97->regs[AC97_POWERDOWN] & ~AC97_PD_EAPD;
+ else if (! (ac97->scaps & AC97_SCAP_EAPD_LED))
+ power = ac97->regs[AC97_POWERDOWN] | AC97_PD_EAPD;
+ power |= AC97_PD_PR6; /* Headphone amplifier powerdown */
+ power |= AC97_PD_PR0 | AC97_PD_PR1; /* ADC & DAC powerdown */
snd_ac97_write(ac97, AC97_POWERDOWN, power);
udelay(100);
-#if 0
- /* FIXME: this causes click noises on some boards at resume */
- power |= 0x3800; /* AC-link powerdown, internal Clk disable */
+ power |= AC97_PD_PR2 | AC97_PD_PR3; /* Analog Mixer powerdown */
snd_ac97_write(ac97, AC97_POWERDOWN, power);
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+ if (power_save) {
+ udelay(100);
+ /* AC-link powerdown, internal Clk disable */
+ /* FIXME: this may cause click noises on some boards */
+ power |= AC97_PD_PR4 | AC97_PD_PR5;
+ snd_ac97_write(ac97, AC97_POWERDOWN, power);
+ }
+#endif
+}
+
+
+struct ac97_power_reg {
+ unsigned short reg;
+ unsigned short power_reg;
+ unsigned short mask;
+};
+
+enum { PWIDX_ADC, PWIDX_FRONT, PWIDX_CLFE, PWIDX_SURR, PWIDX_MIC, PWIDX_SIZE };
+
+static struct ac97_power_reg power_regs[PWIDX_SIZE] = {
+ [PWIDX_ADC] = { AC97_PCM_LR_ADC_RATE, AC97_POWERDOWN, AC97_PD_PR0},
+ [PWIDX_FRONT] = { AC97_PCM_FRONT_DAC_RATE, AC97_POWERDOWN, AC97_PD_PR1},
+ [PWIDX_CLFE] = { AC97_PCM_LFE_DAC_RATE, AC97_EXTENDED_STATUS,
+ AC97_EA_PRI | AC97_EA_PRK},
+ [PWIDX_SURR] = { AC97_PCM_SURR_DAC_RATE, AC97_EXTENDED_STATUS,
+ AC97_EA_PRJ},
+ [PWIDX_MIC] = { AC97_PCM_MIC_ADC_RATE, AC97_EXTENDED_STATUS,
+ AC97_EA_PRL},
+};
+
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+/**
+ * snd_ac97_update_power - update the powerdown register
+ * @ac97: the codec instance
+ * @reg: the rate register, e.g. AC97_PCM_FRONT_DAC_RATE
+ * @powerup: non-zero when power up the part
+ *
+ * Update the AC97 powerdown register bits of the given part.
+ */
+int snd_ac97_update_power(struct snd_ac97 *ac97, int reg, int powerup)
+{
+ int i;
+
+ if (! ac97)
+ return 0;
+
+ if (reg) {
+ /* SPDIF requires DAC power, too */
+ if (reg == AC97_SPDIF)
+ reg = AC97_PCM_FRONT_DAC_RATE;
+ for (i = 0; i < PWIDX_SIZE; i++) {
+ if (power_regs[i].reg == reg) {
+ if (powerup)
+ ac97->power_up |= (1 << i);
+ else
+ ac97->power_up &= ~(1 << i);
+ break;
+ }
+ }
+ }
+
+ if (! power_save)
+ return 0;
+
+ if (! powerup && ac97->power_workq)
+ /* adjust power-down bits after two seconds delay
+ * (for avoiding loud click noises for many (OSS) apps
+ * that open/close frequently)
+ */
+ queue_delayed_work(ac97->power_workq, &ac97->power_work, HZ*2);
+ else
+ update_power_regs(ac97);
+
+ return 0;
+}
+
+EXPORT_SYMBOL(snd_ac97_update_power);
+#endif /* CONFIG_SND_AC97_POWER_SAVE */
+
+static void update_power_regs(struct snd_ac97 *ac97)
+{
+ unsigned int power_up, bits;
+ int i;
+
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+ if (power_save)
+ power_up = ac97->power_up;
+ else {
#endif
+ power_up = (1 << PWIDX_FRONT) | (1 << PWIDX_ADC);
+ power_up |= (1 << PWIDX_MIC);
+ if (ac97->scaps & AC97_SCAP_SURROUND_DAC)
+ power_up |= (1 << PWIDX_SURR);
+ if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC)
+ power_up |= (1 << PWIDX_CLFE);
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+ }
+#endif
+ if (power_up) {
+ if (ac97->regs[AC97_POWERDOWN] & AC97_PD_PR2) {
+ /* needs power-up analog mix and vref */
+ snd_ac97_update_bits(ac97, AC97_POWERDOWN,
+ AC97_PD_PR3, 0);
+ msleep(1);
+ snd_ac97_update_bits(ac97, AC97_POWERDOWN,
+ AC97_PD_PR2, 0);
+ }
+ }
+ for (i = 0; i < PWIDX_SIZE; i++) {
+ if (power_up & (1 << i))
+ bits = 0;
+ else
+ bits = power_regs[i].mask;
+ snd_ac97_update_bits(ac97, power_regs[i].power_reg,
+ power_regs[i].mask, bits);
+ }
+ if (! power_up) {
+ if (! (ac97->regs[AC97_POWERDOWN] & AC97_PD_PR2)) {
+ /* power down analog mix and vref */
+ snd_ac97_update_bits(ac97, AC97_POWERDOWN,
+ AC97_PD_PR2, AC97_PD_PR2);
+ snd_ac97_update_bits(ac97, AC97_POWERDOWN,
+ AC97_PD_PR3, AC97_PD_PR3);
+ }
+ }
}
@@ -2484,6 +2719,7 @@ static int tune_mute_led(struct snd_ac97 *ac97)
msw->put = master_mute_sw_put;
snd_ac97_remove_ctl(ac97, "External Amplifier", NULL);
snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000, 0x8000); /* mute LED on */
+ ac97->scaps |= AC97_SCAP_EAPD_LED;
return 0;
}
diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c
index 7f197c780816..dc28b111a06d 100644
--- a/sound/pci/ac97/ac97_patch.c
+++ b/sound/pci/ac97/ac97_patch.c
@@ -32,6 +32,7 @@
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/control.h>
+#include <sound/tlv.h>
#include <sound/ac97_codec.h>
#include "ac97_patch.h"
#include "ac97_id.h"
@@ -51,6 +52,20 @@ static int patch_build_controls(struct snd_ac97 * ac97, const struct snd_kcontro
return 0;
}
+/* replace with a new TLV */
+static void reset_tlv(struct snd_ac97 *ac97, const char *name,
+ unsigned int *tlv)
+{
+ struct snd_ctl_elem_id sid;
+ struct snd_kcontrol *kctl;
+ memset(&sid, 0, sizeof(sid));
+ strcpy(sid.name, name);
+ sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ kctl = snd_ctl_find_id(ac97->bus->card, &sid);
+ if (kctl && kctl->tlv.p)
+ kctl->tlv.p = tlv;
+}
+
/* set to the page, update bits and restore the page */
static int ac97_update_bits_page(struct snd_ac97 *ac97, unsigned short reg, unsigned short mask, unsigned short value, unsigned short page)
{
@@ -466,7 +481,7 @@ int patch_wolfson05(struct snd_ac97 * ac97)
ac97->build_ops = &patch_wolfson_wm9705_ops;
#ifdef CONFIG_TOUCHSCREEN_WM9705
/* WM9705 touchscreen uses AUX and VIDEO for touch */
- ac97->flags |=3D AC97_HAS_NO_VIDEO | AC97_HAS_NO_AUX;
+ ac97->flags |= AC97_HAS_NO_VIDEO | AC97_HAS_NO_AUX;
#endif
return 0;
}
@@ -1380,6 +1395,17 @@ static void ad1888_resume(struct snd_ac97 *ac97)
#endif
+static const struct snd_ac97_res_table ad1819_restbl[] = {
+ { AC97_PHONE, 0x9f1f },
+ { AC97_MIC, 0x9f1f },
+ { AC97_LINE, 0x9f1f },
+ { AC97_CD, 0x9f1f },
+ { AC97_VIDEO, 0x9f1f },
+ { AC97_AUX, 0x9f1f },
+ { AC97_PCM, 0x9f1f },
+ { } /* terminator */
+};
+
int patch_ad1819(struct snd_ac97 * ac97)
{
unsigned short scfg;
@@ -1387,6 +1413,7 @@ int patch_ad1819(struct snd_ac97 * ac97)
// patch for Analog Devices
scfg = snd_ac97_read(ac97, AC97_AD_SERIAL_CFG);
snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, scfg | 0x7000); /* select all codecs */
+ ac97->res_table = ad1819_restbl;
return 0;
}
@@ -1522,12 +1549,16 @@ static const struct snd_kcontrol_new snd_ac97_controls_ad1885[] = {
AC97_SINGLE("Line Jack Sense", AC97_AD_JACK_SPDIF, 8, 1, 1), /* inverted */
};
+static DECLARE_TLV_DB_SCALE(db_scale_6bit_6db_max, -8850, 150, 0);
+
static int patch_ad1885_specific(struct snd_ac97 * ac97)
{
int err;
if ((err = patch_build_controls(ac97, snd_ac97_controls_ad1885, ARRAY_SIZE(snd_ac97_controls_ad1885))) < 0)
return err;
+ reset_tlv(ac97, "Headphone Playback Volume",
+ db_scale_6bit_6db_max);
return 0;
}
@@ -1551,12 +1582,27 @@ int patch_ad1885(struct snd_ac97 * ac97)
return 0;
}
+static int patch_ad1886_specific(struct snd_ac97 * ac97)
+{
+ reset_tlv(ac97, "Headphone Playback Volume",
+ db_scale_6bit_6db_max);
+ return 0;
+}
+
+static struct snd_ac97_build_ops patch_ad1886_build_ops = {
+ .build_specific = &patch_ad1886_specific,
+#ifdef CONFIG_PM
+ .resume = ad18xx_resume
+#endif
+};
+
int patch_ad1886(struct snd_ac97 * ac97)
{
patch_ad1881(ac97);
/* Presario700 workaround */
/* for Jack Sense/SPDIF Register misetting causing */
snd_ac97_write_cache(ac97, AC97_AD_JACK_SPDIF, 0x0010);
+ ac97->build_ops = &patch_ad1886_build_ops;
return 0;
}
@@ -1824,6 +1870,8 @@ static const struct snd_kcontrol_new snd_ac97_ad1888_controls[] = {
.get = snd_ac97_ad1888_lohpsel_get,
.put = snd_ac97_ad1888_lohpsel_put
},
+ AC97_SINGLE("V_REFOUT Enable", AC97_AD_MISC, 2, 1, 1),
+ AC97_SINGLE("High Pass Filter Enable", AC97_AD_TEST2, 12, 1, 1),
AC97_SINGLE("Spread Front to Surround and Center/LFE", AC97_AD_MISC, 7, 1, 0),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -2013,6 +2061,8 @@ static const struct snd_kcontrol_new snd_ac97_spdif_controls_alc650[] = {
/* AC97_SINGLE("IEC958 Input Monitor", AC97_ALC650_MULTICH, 13, 1, 0), */
};
+static DECLARE_TLV_DB_SCALE(db_scale_5bit_3db_max, -4350, 150, 0);
+
static int patch_alc650_specific(struct snd_ac97 * ac97)
{
int err;
@@ -2023,6 +2073,9 @@ static int patch_alc650_specific(struct snd_ac97 * ac97)
if ((err = patch_build_controls(ac97, snd_ac97_spdif_controls_alc650, ARRAY_SIZE(snd_ac97_spdif_controls_alc650))) < 0)
return err;
}
+ if (ac97->id != AC97_ID_ALC650F)
+ reset_tlv(ac97, "Master Playback Volume",
+ db_scale_5bit_3db_max);
return 0;
}
@@ -2206,7 +2259,8 @@ int patch_alc655(struct snd_ac97 * ac97)
val &= ~(1 << 1); /* Pin 47 is spdif input pin */
else { /* ALC655 */
if (ac97->subsystem_vendor == 0x1462 &&
- ac97->subsystem_device == 0x0131) /* MSI S270 laptop */
+ (ac97->subsystem_device == 0x0131 || /* MSI S270 laptop */
+ ac97->subsystem_device == 0x0161)) /* LG K1 Express */
val &= ~(1 << 1); /* Pin 47 is EAPD (for internal speaker) */
else
val |= (1 << 1); /* Pin 47 is spdif input pin */
@@ -2757,6 +2811,10 @@ int patch_vt1616(struct snd_ac97 * ac97)
*/
int patch_vt1617a(struct snd_ac97 * ac97)
{
+ /* bring analog power consumption to normal, like WinXP driver
+ * for EPIA SP
+ */
+ snd_ac97_write_cache(ac97, 0x5c, 0x20);
ac97->ext_id |= AC97_EI_SPDIF; /* force the detection of spdif */
ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000;
return 0;
@@ -2870,3 +2928,41 @@ int patch_lm4550(struct snd_ac97 *ac97)
ac97->res_table = lm4550_restbl;
return 0;
}
+
+/*
+ * UCB1400 codec (http://www.semiconductors.philips.com/acrobat_download/datasheets/UCB1400-02.pdf)
+ */
+static const struct snd_kcontrol_new snd_ac97_controls_ucb1400[] = {
+/* enable/disable headphone driver which allows direct connection to
+ stereo headphone without the use of external DC blocking
+ capacitors */
+AC97_SINGLE("Headphone Driver", 0x6a, 6, 1, 0),
+/* Filter used to compensate the DC offset is added in the ADC to remove idle
+ tones from the audio band. */
+AC97_SINGLE("DC Filter", 0x6a, 4, 1, 0),
+/* Control smart-low-power mode feature. Allows automatic power down
+ of unused blocks in the ADC analog front end and the PLL. */
+AC97_SINGLE("Smart Low Power Mode", 0x6c, 4, 3, 0),
+};
+
+static int patch_ucb1400_specific(struct snd_ac97 * ac97)
+{
+ int idx, err;
+ for (idx = 0; idx < ARRAY_SIZE(snd_ac97_controls_ucb1400); idx++)
+ if ((err = snd_ctl_add(ac97->bus->card, snd_ctl_new1(&snd_ac97_controls_ucb1400[idx], ac97))) < 0)
+ return err;
+ return 0;
+}
+
+static struct snd_ac97_build_ops patch_ucb1400_ops = {
+ .build_specific = patch_ucb1400_specific,
+};
+
+int patch_ucb1400(struct snd_ac97 * ac97)
+{
+ ac97->build_ops = &patch_ucb1400_ops;
+ /* enable headphone driver and smart low power mode by default */
+ snd_ac97_write(ac97, 0x6a, 0x0050);
+ snd_ac97_write(ac97, 0x6c, 0x0030);
+ return 0;
+}
diff --git a/sound/pci/ac97/ac97_patch.h b/sound/pci/ac97/ac97_patch.h
index adcaa04586cb..741979217207 100644
--- a/sound/pci/ac97/ac97_patch.h
+++ b/sound/pci/ac97/ac97_patch.h
@@ -58,5 +58,6 @@ int patch_cm9780(struct snd_ac97 * ac97);
int patch_vt1616(struct snd_ac97 * ac97);
int patch_vt1617a(struct snd_ac97 * ac97);
int patch_it2646(struct snd_ac97 * ac97);
+int patch_ucb1400(struct snd_ac97 * ac97);
int mpatch_si3036(struct snd_ac97 * ac97);
int patch_lm4550(struct snd_ac97 * ac97);
diff --git a/sound/pci/ac97/ac97_pcm.c b/sound/pci/ac97/ac97_pcm.c
index f684aa2c0067..3758d07182f8 100644
--- a/sound/pci/ac97/ac97_pcm.c
+++ b/sound/pci/ac97/ac97_pcm.c
@@ -269,6 +269,7 @@ int snd_ac97_set_rate(struct snd_ac97 *ac97, int reg, unsigned int rate)
return -EINVAL;
}
+ snd_ac97_update_power(ac97, reg, 1);
switch (reg) {
case AC97_PCM_MIC_ADC_RATE:
if ((ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_VRM) == 0) /* MIC VRA */
@@ -606,6 +607,7 @@ int snd_ac97_pcm_open(struct ac97_pcm *pcm, unsigned int rate,
goto error;
}
}
+ pcm->cur_dbl = r;
spin_unlock_irq(&pcm->bus->bus_lock);
for (i = 3; i < 12; i++) {
if (!(slots & (1 << i)))
@@ -651,6 +653,21 @@ int snd_ac97_pcm_close(struct ac97_pcm *pcm)
unsigned short slots = pcm->aslots;
int i, cidx;
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+ int r = pcm->cur_dbl;
+ for (i = 3; i < 12; i++) {
+ if (!(slots & (1 << i)))
+ continue;
+ for (cidx = 0; cidx < 4; cidx++) {
+ if (pcm->r[r].rslots[cidx] & (1 << i)) {
+ int reg = get_slot_reg(pcm, cidx, i, r);
+ snd_ac97_update_power(pcm->r[r].codec[cidx],
+ reg, 0);
+ }
+ }
+ }
+#endif
+
bus = pcm->bus;
spin_lock_irq(&pcm->bus->bus_lock);
for (i = 3; i < 12; i++) {
@@ -660,6 +677,7 @@ int snd_ac97_pcm_close(struct ac97_pcm *pcm)
bus->used_slots[pcm->stream][cidx] &= ~(1 << i);
}
pcm->aslots = 0;
+ pcm->cur_dbl = 0;
spin_unlock_irq(&pcm->bus->bus_lock);
return 0;
}
diff --git a/sound/pci/ac97/ac97_proc.c b/sound/pci/ac97/ac97_proc.c
index 2118df50b9d6..a3fdd7da911c 100644
--- a/sound/pci/ac97/ac97_proc.c
+++ b/sound/pci/ac97/ac97_proc.c
@@ -457,14 +457,10 @@ void snd_ac97_proc_init(struct snd_ac97 * ac97)
void snd_ac97_proc_done(struct snd_ac97 * ac97)
{
- if (ac97->proc_regs) {
- snd_info_unregister(ac97->proc_regs);
- ac97->proc_regs = NULL;
- }
- if (ac97->proc) {
- snd_info_unregister(ac97->proc);
- ac97->proc = NULL;
- }
+ snd_info_free_entry(ac97->proc_regs);
+ ac97->proc_regs = NULL;
+ snd_info_free_entry(ac97->proc);
+ ac97->proc = NULL;
}
void snd_ac97_bus_proc_init(struct snd_ac97_bus * bus)
@@ -485,8 +481,6 @@ void snd_ac97_bus_proc_init(struct snd_ac97_bus * bus)
void snd_ac97_bus_proc_done(struct snd_ac97_bus * bus)
{
- if (bus->proc) {
- snd_info_unregister(bus->proc);
- bus->proc = NULL;
- }
+ snd_info_free_entry(bus->proc);
+ bus->proc = NULL;
}
diff --git a/sound/pci/ac97/ak4531_codec.c b/sound/pci/ac97/ak4531_codec.c
index 94c26ec05882..c153cb79c518 100644
--- a/sound/pci/ac97/ak4531_codec.c
+++ b/sound/pci/ac97/ak4531_codec.c
@@ -27,6 +27,7 @@
#include <sound/core.h>
#include <sound/ak4531_codec.h>
+#include <sound/tlv.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
MODULE_DESCRIPTION("Universal routines for AK4531 codec");
@@ -63,6 +64,14 @@ static void snd_ak4531_dump(struct snd_ak4531 *ak4531)
.info = snd_ak4531_info_single, \
.get = snd_ak4531_get_single, .put = snd_ak4531_put_single, \
.private_value = reg | (shift << 16) | (mask << 24) | (invert << 22) }
+#define AK4531_SINGLE_TLV(xname, xindex, reg, shift, mask, invert, xtlv) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
+ .name = xname, .index = xindex, \
+ .info = snd_ak4531_info_single, \
+ .get = snd_ak4531_get_single, .put = snd_ak4531_put_single, \
+ .private_value = reg | (shift << 16) | (mask << 24) | (invert << 22), \
+ .tlv = { .p = (xtlv) } }
static int snd_ak4531_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
@@ -122,6 +131,14 @@ static int snd_ak4531_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_e
.info = snd_ak4531_info_double, \
.get = snd_ak4531_get_double, .put = snd_ak4531_put_double, \
.private_value = left_reg | (right_reg << 8) | (left_shift << 16) | (right_shift << 19) | (mask << 24) | (invert << 22) }
+#define AK4531_DOUBLE_TLV(xname, xindex, left_reg, right_reg, left_shift, right_shift, mask, invert, xtlv) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
+ .name = xname, .index = xindex, \
+ .info = snd_ak4531_info_double, \
+ .get = snd_ak4531_get_double, .put = snd_ak4531_put_double, \
+ .private_value = left_reg | (right_reg << 8) | (left_shift << 16) | (right_shift << 19) | (mask << 24) | (invert << 22), \
+ .tlv = { .p = (xtlv) } }
static int snd_ak4531_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
@@ -250,50 +267,62 @@ static int snd_ak4531_put_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl
return change;
}
+static DECLARE_TLV_DB_SCALE(db_scale_master, -6200, 200, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_mono, -2800, 400, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_input, -5000, 200, 0);
+
static struct snd_kcontrol_new snd_ak4531_controls[] = {
-AK4531_DOUBLE("Master Playback Switch", 0, AK4531_LMASTER, AK4531_RMASTER, 7, 7, 1, 1),
+AK4531_DOUBLE_TLV("Master Playback Switch", 0,
+ AK4531_LMASTER, AK4531_RMASTER, 7, 7, 1, 1,
+ db_scale_master),
AK4531_DOUBLE("Master Playback Volume", 0, AK4531_LMASTER, AK4531_RMASTER, 0, 0, 0x1f, 1),
-AK4531_SINGLE("Master Mono Playback Switch", 0, AK4531_MONO_OUT, 7, 1, 1),
+AK4531_SINGLE_TLV("Master Mono Playback Switch", 0, AK4531_MONO_OUT, 7, 1, 1,
+ db_scale_mono),
AK4531_SINGLE("Master Mono Playback Volume", 0, AK4531_MONO_OUT, 0, 0x07, 1),
AK4531_DOUBLE("PCM Switch", 0, AK4531_LVOICE, AK4531_RVOICE, 7, 7, 1, 1),
-AK4531_DOUBLE("PCM Volume", 0, AK4531_LVOICE, AK4531_RVOICE, 0, 0, 0x1f, 1),
+AK4531_DOUBLE_TLV("PCM Volume", 0, AK4531_LVOICE, AK4531_RVOICE, 0, 0, 0x1f, 1,
+ db_scale_input),
AK4531_DOUBLE("PCM Playback Switch", 0, AK4531_OUT_SW2, AK4531_OUT_SW2, 3, 2, 1, 0),
AK4531_DOUBLE("PCM Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 2, 2, 1, 0),
AK4531_DOUBLE("PCM Switch", 1, AK4531_LFM, AK4531_RFM, 7, 7, 1, 1),
-AK4531_DOUBLE("PCM Volume", 1, AK4531_LFM, AK4531_RFM, 0, 0, 0x1f, 1),
+AK4531_DOUBLE_TLV("PCM Volume", 1, AK4531_LFM, AK4531_RFM, 0, 0, 0x1f, 1,
+ db_scale_input),
AK4531_DOUBLE("PCM Playback Switch", 1, AK4531_OUT_SW1, AK4531_OUT_SW1, 6, 5, 1, 0),
AK4531_INPUT_SW("PCM Capture Route", 1, AK4531_LIN_SW1, AK4531_RIN_SW1, 6, 5),
AK4531_DOUBLE("CD Switch", 0, AK4531_LCD, AK4531_RCD, 7, 7, 1, 1),
-AK4531_DOUBLE("CD Volume", 0, AK4531_LCD, AK4531_RCD, 0, 0, 0x1f, 1),
+AK4531_DOUBLE_TLV("CD Volume", 0, AK4531_LCD, AK4531_RCD, 0, 0, 0x1f, 1,
+ db_scale_input),
AK4531_DOUBLE("CD Playback Switch", 0, AK4531_OUT_SW1, AK4531_OUT_SW1, 2, 1, 1, 0),
AK4531_INPUT_SW("CD Capture Route", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 2, 1),
AK4531_DOUBLE("Line Switch", 0, AK4531_LLINE, AK4531_RLINE, 7, 7, 1, 1),
-AK4531_DOUBLE("Line Volume", 0, AK4531_LLINE, AK4531_RLINE, 0, 0, 0x1f, 1),
+AK4531_DOUBLE_TLV("Line Volume", 0, AK4531_LLINE, AK4531_RLINE, 0, 0, 0x1f, 1,
+ db_scale_input),
AK4531_DOUBLE("Line Playback Switch", 0, AK4531_OUT_SW1, AK4531_OUT_SW1, 4, 3, 1, 0),
AK4531_INPUT_SW("Line Capture Route", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 4, 3),
AK4531_DOUBLE("Aux Switch", 0, AK4531_LAUXA, AK4531_RAUXA, 7, 7, 1, 1),
-AK4531_DOUBLE("Aux Volume", 0, AK4531_LAUXA, AK4531_RAUXA, 0, 0, 0x1f, 1),
+AK4531_DOUBLE_TLV("Aux Volume", 0, AK4531_LAUXA, AK4531_RAUXA, 0, 0, 0x1f, 1,
+ db_scale_input),
AK4531_DOUBLE("Aux Playback Switch", 0, AK4531_OUT_SW2, AK4531_OUT_SW2, 5, 4, 1, 0),
AK4531_INPUT_SW("Aux Capture Route", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 4, 3),
AK4531_SINGLE("Mono Switch", 0, AK4531_MONO1, 7, 1, 1),
-AK4531_SINGLE("Mono Volume", 0, AK4531_MONO1, 0, 0x1f, 1),
+AK4531_SINGLE_TLV("Mono Volume", 0, AK4531_MONO1, 0, 0x1f, 1, db_scale_input),
AK4531_SINGLE("Mono Playback Switch", 0, AK4531_OUT_SW2, 0, 1, 0),
AK4531_DOUBLE("Mono Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 0, 0, 1, 0),
AK4531_SINGLE("Mono Switch", 1, AK4531_MONO2, 7, 1, 1),
-AK4531_SINGLE("Mono Volume", 1, AK4531_MONO2, 0, 0x1f, 1),
+AK4531_SINGLE_TLV("Mono Volume", 1, AK4531_MONO2, 0, 0x1f, 1, db_scale_input),
AK4531_SINGLE("Mono Playback Switch", 1, AK4531_OUT_SW2, 1, 1, 0),
AK4531_DOUBLE("Mono Capture Switch", 1, AK4531_LIN_SW2, AK4531_RIN_SW2, 1, 1, 1, 0),
-AK4531_SINGLE("Mic Volume", 0, AK4531_MIC, 0, 0x1f, 1),
+AK4531_SINGLE_TLV("Mic Volume", 0, AK4531_MIC, 0, 0x1f, 1, db_scale_input),
AK4531_SINGLE("Mic Switch", 0, AK4531_MIC, 7, 1, 1),
AK4531_SINGLE("Mic Playback Switch", 0, AK4531_OUT_SW1, 0, 1, 0),
AK4531_DOUBLE("Mic Capture Switch", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 0, 0, 1, 0),
diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c
index d42bf4570367..0786d0edaca5 100644
--- a/sound/pci/ad1889.c
+++ b/sound/pci/ad1889.c
@@ -241,14 +241,14 @@ ad1889_channel_reset(struct snd_ad1889 *chip, unsigned int channel)
}
}
-static inline u16
+static u16
snd_ad1889_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
{
struct snd_ad1889 *chip = ac97->private_data;
return ad1889_readw(chip, AD_AC97_BASE + reg);
}
-static inline void
+static void
snd_ad1889_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)
{
struct snd_ad1889 *chip = ac97->private_data;
@@ -873,7 +873,7 @@ skip_hw:
return 0;
}
-static inline int
+static int
snd_ad1889_dev_free(struct snd_device *device)
{
struct snd_ad1889 *chip = device->device_data;
@@ -947,7 +947,7 @@ snd_ad1889_create(struct snd_card *card,
spin_lock_init(&chip->lock); /* only now can we call ad1889_free */
if (request_irq(pci->irq, snd_ad1889_interrupt,
- SA_INTERRUPT|SA_SHIRQ, card->driver, (void*)chip)) {
+ IRQF_DISABLED|IRQF_SHARED, card->driver, (void*)chip)) {
printk(KERN_ERR PFX "cannot obtain IRQ %d\n", pci->irq);
snd_ad1889_free(chip);
return -EBUSY;
@@ -1051,7 +1051,7 @@ snd_ad1889_remove(struct pci_dev *pci)
pci_set_drvdata(pci, NULL);
}
-static struct pci_device_id snd_ad1889_ids[] __devinitdata = {
+static struct pci_device_id snd_ad1889_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_ANALOG_DEVICES, PCI_DEVICE_ID_AD1889JS) },
{ 0, },
};
diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c
index 5dfdbf6657f2..74668398eac5 100644
--- a/sound/pci/ali5451/ali5451.c
+++ b/sound/pci/ali5451/ali5451.c
@@ -279,7 +279,7 @@ struct snd_ali {
#endif
};
-static struct pci_device_id snd_ali_ids[] __devinitdata = {
+static struct pci_device_id snd_ali_ids[] = {
{PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5451), 0, 0, 0},
{0, }
};
@@ -2185,7 +2185,7 @@ static int __devinit snd_ali_resources(struct snd_ali *codec)
return err;
codec->port = pci_resource_start(codec->pci, 0);
- if (request_irq(codec->pci->irq, snd_ali_card_interrupt, SA_INTERRUPT|SA_SHIRQ, "ALI 5451", (void *)codec)) {
+ if (request_irq(codec->pci->irq, snd_ali_card_interrupt, IRQF_DISABLED|IRQF_SHARED, "ALI 5451", (void *)codec)) {
snd_printk(KERN_ERR "Unable to request irq.\n");
return -EBUSY;
}
diff --git a/sound/pci/als300.c b/sound/pci/als300.c
index 901b08ae9174..96cfb8ae5055 100644
--- a/sound/pci/als300.c
+++ b/sound/pci/als300.c
@@ -146,7 +146,7 @@ struct snd_als300_substream_data {
int block_counter_register;
};
-static struct pci_device_id snd_als300_ids[] __devinitdata = {
+static struct pci_device_id snd_als300_ids[] = {
{ 0x4005, 0x0300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_ALS300 },
{ 0x4005, 0x0308, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_ALS300_PLUS },
{ 0, }
@@ -724,7 +724,7 @@ static int __devinit snd_als300_create(snd_card_t *card,
else
irq_handler = snd_als300_interrupt;
- if (request_irq(pci->irq, irq_handler, SA_INTERRUPT|SA_SHIRQ,
+ if (request_irq(pci->irq, irq_handler, IRQF_DISABLED|IRQF_SHARED,
card->shortname, (void *)chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_als300_free(chip);
diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c
index a9f08066459a..9e596f750cbd 100644
--- a/sound/pci/als4000.c
+++ b/sound/pci/als4000.c
@@ -116,7 +116,7 @@ struct snd_card_als4000 {
#endif
};
-static struct pci_device_id snd_als4000_ids[] __devinitdata = {
+static struct pci_device_id snd_als4000_ids[] = {
{ 0x4005, 0x4000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* ALS4000 */
{ 0, }
};
diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c
index f18a8c0e4688..347e25ff073d 100644
--- a/sound/pci/atiixp.c
+++ b/sound/pci/atiixp.c
@@ -284,7 +284,7 @@ struct atiixp {
/*
*/
-static struct pci_device_id snd_atiixp_ids[] __devinitdata = {
+static struct pci_device_id snd_atiixp_ids[] = {
{ 0x1002, 0x4341, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB200 */
{ 0x1002, 0x4361, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB300 */
{ 0x1002, 0x4370, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB400 */
@@ -1578,7 +1578,7 @@ static int __devinit snd_atiixp_create(struct snd_card *card,
return -EIO;
}
- if (request_irq(pci->irq, snd_atiixp_interrupt, SA_INTERRUPT|SA_SHIRQ,
+ if (request_irq(pci->irq, snd_atiixp_interrupt, IRQF_DISABLED|IRQF_SHARED,
card->shortname, chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_atiixp_free(chip);
diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c
index 40739057076b..a89d67c4598b 100644
--- a/sound/pci/atiixp_modem.c
+++ b/sound/pci/atiixp_modem.c
@@ -262,7 +262,7 @@ struct atiixp_modem {
/*
*/
-static struct pci_device_id snd_atiixp_ids[] __devinitdata = {
+static struct pci_device_id snd_atiixp_ids[] = {
{ 0x1002, 0x434d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB200 */
{ 0x1002, 0x4378, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB400 */
{ 0, }
@@ -1251,7 +1251,7 @@ static int __devinit snd_atiixp_create(struct snd_card *card,
return -EIO;
}
- if (request_irq(pci->irq, snd_atiixp_interrupt, SA_INTERRUPT|SA_SHIRQ,
+ if (request_irq(pci->irq, snd_atiixp_interrupt, IRQF_DISABLED|IRQF_SHARED,
card->shortname, chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_atiixp_free(chip);
diff --git a/sound/pci/au88x0/au8810.c b/sound/pci/au88x0/au8810.c
index bd3352998ad0..fce22c7af0ea 100644
--- a/sound/pci/au88x0/au8810.c
+++ b/sound/pci/au88x0/au8810.c
@@ -1,6 +1,6 @@
#include "au8810.h"
#include "au88x0.h"
-static struct pci_device_id snd_vortex_ids[] __devinitdata = {
+static struct pci_device_id snd_vortex_ids[] = {
{PCI_VENDOR_ID_AUREAL, PCI_DEVICE_ID_AUREAL_ADVANTAGE,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1,},
{0,}
diff --git a/sound/pci/au88x0/au8820.c b/sound/pci/au88x0/au8820.c
index 7e3fd8372d8d..d1fbcce07257 100644
--- a/sound/pci/au88x0/au8820.c
+++ b/sound/pci/au88x0/au8820.c
@@ -1,6 +1,6 @@
#include "au8820.h"
#include "au88x0.h"
-static struct pci_device_id snd_vortex_ids[] __devinitdata = {
+static struct pci_device_id snd_vortex_ids[] = {
{PCI_VENDOR_ID_AUREAL, PCI_DEVICE_ID_AUREAL_VORTEX_1,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,},
{0,}
diff --git a/sound/pci/au88x0/au8830.c b/sound/pci/au88x0/au8830.c
index b840f6608a61..d4f2717c14fb 100644
--- a/sound/pci/au88x0/au8830.c
+++ b/sound/pci/au88x0/au8830.c
@@ -1,6 +1,6 @@
#include "au8830.h"
#include "au88x0.h"
-static struct pci_device_id snd_vortex_ids[] __devinitdata = {
+static struct pci_device_id snd_vortex_ids[] = {
{PCI_VENDOR_ID_AUREAL, PCI_DEVICE_ID_AUREAL_VORTEX_2,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,},
{0,}
diff --git a/sound/pci/au88x0/au88x0.c b/sound/pci/au88x0/au88x0.c
index 8a3b118989bf..ef189d7f09d3 100644
--- a/sound/pci/au88x0/au88x0.c
+++ b/sound/pci/au88x0/au88x0.c
@@ -197,7 +197,7 @@ snd_vortex_create(struct snd_card *card, struct pci_dev *pci, vortex_t ** rchip)
}
if ((err = request_irq(pci->irq, vortex_interrupt,
- SA_INTERRUPT | SA_SHIRQ, CARD_NAME_SHORT,
+ IRQF_DISABLED | IRQF_SHARED, CARD_NAME_SHORT,
chip)) != 0) {
printk(KERN_ERR "cannot grab irq\n");
goto irq_out;
diff --git a/sound/pci/au88x0/au88x0.h b/sound/pci/au88x0/au88x0.h
index f078b716d2b0..b1cfc3c79d07 100644
--- a/sound/pci/au88x0/au88x0.h
+++ b/sound/pci/au88x0/au88x0.h
@@ -270,7 +270,8 @@ static void vortex_mix_setvolumebyte(vortex_t * vortex, unsigned char mix,
/* A3D functions. */
#ifndef CHIP_AU8820
-static void vortex_Vort3D(vortex_t * v, int en);
+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);
#endif
diff --git a/sound/pci/au88x0/au88x0_a3d.c b/sound/pci/au88x0/au88x0_a3d.c
index d215f393ea64..649849e540d3 100644
--- a/sound/pci/au88x0/au88x0_a3d.c
+++ b/sound/pci/au88x0/au88x0_a3d.c
@@ -593,24 +593,23 @@ static int Vort3DRend_Initialize(vortex_t * v, unsigned short mode)
static int vortex_a3d_register_controls(vortex_t * vortex);
static void vortex_a3d_unregister_controls(vortex_t * vortex);
/* A3D base support init/shudown */
-static void vortex_Vort3D(vortex_t * v, int en)
+static void __devinit vortex_Vort3D_enable(vortex_t * v)
{
int i;
- if (en) {
- 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]));
- }
- } else {
- vortex_XtalkHw_Disable(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]));
}
/* Register ALSA controls */
- if (en) {
- vortex_a3d_register_controls(v);
- } else {
- vortex_a3d_unregister_controls(v);
- }
+ vortex_a3d_register_controls(v);
+}
+
+static void vortex_Vort3D_disable(vortex_t * v)
+{
+ vortex_XtalkHw_Disable(v);
+ vortex_a3d_unregister_controls(v);
}
/* Make A3D subsystem connections. */
@@ -855,7 +854,7 @@ static struct snd_kcontrol_new vortex_a3d_kcontrol __devinitdata = {
};
/* Control (un)registration. */
-static int vortex_a3d_register_controls(vortex_t * vortex)
+static int __devinit vortex_a3d_register_controls(vortex_t * vortex)
{
struct snd_kcontrol *kcontrol;
int err, i;
diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c
index 4347e6abc1d5..5299cce583d3 100644
--- a/sound/pci/au88x0/au88x0_core.c
+++ b/sound/pci/au88x0/au88x0_core.c
@@ -2690,7 +2690,7 @@ static int __devinit vortex_core_init(vortex_t * vortex)
#ifndef CHIP_AU8820
vortex_eq_init(vortex);
vortex_spdif_init(vortex, 48000, 1);
- vortex_Vort3D(vortex, 1);
+ vortex_Vort3D_enable(vortex);
#endif
#ifndef CHIP_AU8810
vortex_wt_init(vortex);
@@ -2718,7 +2718,7 @@ static int vortex_core_shutdown(vortex_t * vortex)
printk(KERN_INFO "Vortex: shutdown...");
#ifndef CHIP_AU8820
vortex_eq_free(vortex);
- vortex_Vort3D(vortex, 0);
+ vortex_Vort3D_disable(vortex);
#endif
//vortex_disable_timer_int(vortex);
vortex_disable_int(vortex);
diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c
index 6e62dafb66cd..bac8e9cfd921 100644
--- a/sound/pci/azt3328.c
+++ b/sound/pci/azt3328.c
@@ -238,7 +238,7 @@ struct snd_azf3328 {
#endif
};
-static const struct pci_device_id snd_azf3328_ids[] __devinitdata = {
+static const struct pci_device_id snd_azf3328_ids[] = {
{ 0x122D, 0x50DC, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* PCI168/3328 */
{ 0x122D, 0x80DA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* 3328 */
{ 0, }
@@ -1724,7 +1724,7 @@ snd_azf3328_create(struct snd_card *card,
chip->synth_port = pci_resource_start(pci, 3);
chip->mixer_port = pci_resource_start(pci, 4);
- if (request_irq(pci->irq, snd_azf3328_interrupt, SA_INTERRUPT|SA_SHIRQ, card->shortname, (void *)chip)) {
+ if (request_irq(pci->irq, snd_azf3328_interrupt, IRQF_DISABLED|IRQF_SHARED, card->shortname, (void *)chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
err = -EBUSY;
goto out_err;
diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c
index c33642d8d9a1..97a280a246cb 100644
--- a/sound/pci/bt87x.c
+++ b/sound/pci/bt87x.c
@@ -747,7 +747,7 @@ static int __devinit snd_bt87x_create(struct snd_card *card,
snd_bt87x_writel(chip, REG_INT_MASK, 0);
snd_bt87x_writel(chip, REG_INT_STAT, MY_INTERRUPTS);
- if (request_irq(pci->irq, snd_bt87x_interrupt, SA_INTERRUPT | SA_SHIRQ,
+ if (request_irq(pci->irq, snd_bt87x_interrupt, IRQF_DISABLED | IRQF_SHARED,
"Bt87x audio", chip)) {
snd_bt87x_free(chip);
snd_printk(KERN_ERR "cannot grab irq\n");
@@ -774,7 +774,7 @@ static int __devinit snd_bt87x_create(struct snd_card *card,
.driver_data = rate }
/* driver_data is the default digital_rate value for that device */
-static struct pci_device_id snd_bt87x_ids[] __devinitdata = {
+static struct pci_device_id snd_bt87x_ids[] = {
/* Hauppauge WinTV series */
BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x0070, 0x13eb, 32000),
/* Hauppauge WinTV series */
@@ -888,8 +888,9 @@ static int __devinit snd_bt87x_probe(struct pci_dev *pci,
strcpy(card->driver, "Bt87x");
sprintf(card->shortname, "Brooktree Bt%x", pci->device);
- sprintf(card->longname, "%s at %#lx, irq %i",
- card->shortname, pci_resource_start(pci, 0), chip->irq);
+ sprintf(card->longname, "%s at %#llx, irq %i",
+ card->shortname, (unsigned long long)pci_resource_start(pci, 0),
+ chip->irq);
strcpy(card->mixername, "Bt87x");
err = snd_card_register(card);
diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c
index 59bf9bd02534..12bbbb6afd2d 100644
--- a/sound/pci/ca0106/ca0106_main.c
+++ b/sound/pci/ca0106/ca0106_main.c
@@ -1268,7 +1268,7 @@ static int __devinit snd_ca0106_create(struct snd_card *card,
}
if (request_irq(pci->irq, snd_ca0106_interrupt,
- SA_INTERRUPT|SA_SHIRQ, "snd_ca0106",
+ IRQF_DISABLED|IRQF_SHARED, "snd_ca0106",
(void *)chip)) {
snd_ca0106_free(chip);
printk(KERN_ERR "cannot grab irq\n");
@@ -1602,7 +1602,7 @@ static void __devexit snd_ca0106_remove(struct pci_dev *pci)
}
// PCI IDs
-static struct pci_device_id snd_ca0106_ids[] __devinitdata = {
+static struct pci_device_id snd_ca0106_ids[] = {
{ 0x1102, 0x0007, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* Audigy LS or Live 24bit */
{ 0, }
};
diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c
index 146eed70dce6..9855f528ea78 100644
--- a/sound/pci/ca0106/ca0106_mixer.c
+++ b/sound/pci/ca0106/ca0106_mixer.c
@@ -70,9 +70,13 @@
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
#include <sound/info.h>
+#include <sound/tlv.h>
#include "ca0106.h"
+static DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale1, -5175, 25, 1);
+static DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale2, -10350, 50, 1);
+
static int snd_ca0106_shared_spdif_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -469,18 +473,24 @@ static int snd_ca0106_i2c_volume_put(struct snd_kcontrol *kcontrol,
#define CA_VOLUME(xname,chid,reg) \
{ \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
.info = snd_ca0106_volume_info, \
.get = snd_ca0106_volume_get, \
.put = snd_ca0106_volume_put, \
+ .tlv = { .p = snd_ca0106_db_scale1 }, \
.private_value = ((chid) << 8) | (reg) \
}
#define I2C_VOLUME(xname,chid) \
{ \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
.info = snd_ca0106_i2c_volume_info, \
.get = snd_ca0106_i2c_volume_get, \
.put = snd_ca0106_i2c_volume_put, \
+ .tlv = { .p = snd_ca0106_db_scale2 }, \
.private_value = chid \
}
diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c
index 0938c158b5c9..876b64464b6f 100644
--- a/sound/pci/cmipci.c
+++ b/sound/pci/cmipci.c
@@ -2609,7 +2609,7 @@ static inline void snd_cmipci_proc_init(struct cmipci *cm) {}
#endif
-static struct pci_device_id snd_cmipci_ids[] __devinitdata = {
+static struct pci_device_id snd_cmipci_ids[] = {
{PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
@@ -2862,7 +2862,7 @@ static int __devinit snd_cmipci_create(struct snd_card *card, struct pci_dev *pc
cm->iobase = pci_resource_start(pci, 0);
if (request_irq(pci->irq, snd_cmipci_interrupt,
- SA_INTERRUPT|SA_SHIRQ, card->driver, cm)) {
+ IRQF_DISABLED|IRQF_SHARED, card->driver, cm)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_cmipci_free(cm);
return -EBUSY;
diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c
index e77a4ce314b7..1990430a21c1 100644
--- a/sound/pci/cs4281.c
+++ b/sound/pci/cs4281.c
@@ -33,6 +33,7 @@
#include <sound/pcm.h>
#include <sound/rawmidi.h>
#include <sound/ac97_codec.h>
+#include <sound/tlv.h>
#include <sound/opl3.h>
#include <sound/initval.h>
@@ -494,7 +495,7 @@ struct cs4281 {
static irqreturn_t snd_cs4281_interrupt(int irq, void *dev_id, struct pt_regs *regs);
-static struct pci_device_id snd_cs4281_ids[] __devinitdata = {
+static struct pci_device_id snd_cs4281_ids[] = {
{ 0x1013, 0x6005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* CS4281 */
{ 0, }
};
@@ -1054,6 +1055,8 @@ static int snd_cs4281_put_volume(struct snd_kcontrol *kcontrol,
return change;
}
+static DECLARE_TLV_DB_SCALE(db_scale_dsp, -4650, 150, 0);
+
static struct snd_kcontrol_new snd_cs4281_fm_vol =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1062,6 +1065,7 @@ static struct snd_kcontrol_new snd_cs4281_fm_vol =
.get = snd_cs4281_get_volume,
.put = snd_cs4281_put_volume,
.private_value = ((BA0_FMLVC << 16) | BA0_FMRVC),
+ .tlv = { .p = db_scale_dsp },
};
static struct snd_kcontrol_new snd_cs4281_pcm_vol =
@@ -1072,6 +1076,7 @@ static struct snd_kcontrol_new snd_cs4281_pcm_vol =
.get = snd_cs4281_get_volume,
.put = snd_cs4281_put_volume,
.private_value = ((BA0_PPLVC << 16) | BA0_PPRVC),
+ .tlv = { .p = db_scale_dsp },
};
static void snd_cs4281_mixer_free_ac97_bus(struct snd_ac97_bus *bus)
@@ -1386,7 +1391,7 @@ static int __devinit snd_cs4281_create(struct snd_card *card,
return -ENOMEM;
}
- if (request_irq(pci->irq, snd_cs4281_interrupt, SA_INTERRUPT|SA_SHIRQ,
+ if (request_irq(pci->irq, snd_cs4281_interrupt, IRQF_DISABLED|IRQF_SHARED,
"CS4281", chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_cs4281_free(chip);
diff --git a/sound/pci/cs46xx/cs46xx.c b/sound/pci/cs46xx/cs46xx.c
index 772dc52bfeb2..8b6cd144d101 100644
--- a/sound/pci/cs46xx/cs46xx.c
+++ b/sound/pci/cs46xx/cs46xx.c
@@ -65,7 +65,7 @@ MODULE_PARM_DESC(thinkpad, "Force to enable Thinkpad's CLKRUN control.");
module_param_array(mmap_valid, bool, NULL, 0444);
MODULE_PARM_DESC(mmap_valid, "Support OSS mmap.");
-static struct pci_device_id snd_cs46xx_ids[] __devinitdata = {
+static struct pci_device_id snd_cs46xx_ids[] = {
{ 0x1013, 0x6001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* CS4280 */
{ 0x1013, 0x6003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* CS4612 */
{ 0x1013, 0x6004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* CS4615 */
diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c
index 5c2114439204..4851847180d2 100644
--- a/sound/pci/cs46xx/cs46xx_lib.c
+++ b/sound/pci/cs46xx/cs46xx_lib.c
@@ -2317,7 +2317,7 @@ static struct snd_kcontrol_new snd_cs46xx_front_dup_ctl = {
#ifdef CONFIG_SND_CS46XX_NEW_DSP
/* Only available on the Hercules Game Theater XP soundcard */
-static struct snd_kcontrol_new snd_hercules_controls[] __devinitdata = {
+static struct snd_kcontrol_new snd_hercules_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Optical/Coaxial SPDIF Input Switch",
@@ -3458,6 +3458,9 @@ static void hercules_mixer_init (struct snd_cs46xx *chip)
snd_printdd ("initializing Hercules mixer\n");
#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ if (chip->in_suspend)
+ return;
+
for (idx = 0 ; idx < ARRAY_SIZE(snd_hercules_controls); idx++) {
struct snd_kcontrol *kctl;
@@ -3669,6 +3672,7 @@ int snd_cs46xx_suspend(struct pci_dev *pci, pm_message_t state)
int amp_saved;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+ chip->in_suspend = 1;
snd_pcm_suspend_all(chip->pcm);
// chip->ac97_powerdown = snd_cs46xx_codec_read(chip, AC97_POWER_CONTROL);
// chip->ac97_general_purpose = snd_cs46xx_codec_read(chip, BA0_AC97_GENERAL_PURPOSE);
@@ -3722,6 +3726,7 @@ int snd_cs46xx_resume(struct pci_dev *pci)
else
chip->active_ctrl(chip, -1); /* disable CLKRUN */
chip->amplifier = amp_saved;
+ chip->in_suspend = 0;
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
@@ -3853,7 +3858,7 @@ int __devinit snd_cs46xx_create(struct snd_card *card,
}
}
- if (request_irq(pci->irq, snd_cs46xx_interrupt, SA_INTERRUPT|SA_SHIRQ,
+ if (request_irq(pci->irq, snd_cs46xx_interrupt, IRQF_DISABLED|IRQF_SHARED,
"CS46XX", chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_cs46xx_free(chip);
diff --git a/sound/pci/cs46xx/dsp_spos.c b/sound/pci/cs46xx/dsp_spos.c
index 5c9711c0265c..89c402770a1d 100644
--- a/sound/pci/cs46xx/dsp_spos.c
+++ b/sound/pci/cs46xx/dsp_spos.c
@@ -868,35 +868,23 @@ int cs46xx_dsp_proc_done (struct snd_cs46xx *chip)
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
int i;
- if (ins->proc_sym_info_entry) {
- snd_info_unregister(ins->proc_sym_info_entry);
- ins->proc_sym_info_entry = NULL;
- }
-
- if (ins->proc_modules_info_entry) {
- snd_info_unregister(ins->proc_modules_info_entry);
- ins->proc_modules_info_entry = NULL;
- }
-
- if (ins->proc_parameter_dump_info_entry) {
- snd_info_unregister(ins->proc_parameter_dump_info_entry);
- ins->proc_parameter_dump_info_entry = NULL;
- }
-
- if (ins->proc_sample_dump_info_entry) {
- snd_info_unregister(ins->proc_sample_dump_info_entry);
- ins->proc_sample_dump_info_entry = NULL;
- }
-
- if (ins->proc_scb_info_entry) {
- snd_info_unregister(ins->proc_scb_info_entry);
- ins->proc_scb_info_entry = NULL;
- }
-
- if (ins->proc_task_info_entry) {
- snd_info_unregister(ins->proc_task_info_entry);
- ins->proc_task_info_entry = NULL;
- }
+ snd_info_free_entry(ins->proc_sym_info_entry);
+ ins->proc_sym_info_entry = NULL;
+
+ snd_info_free_entry(ins->proc_modules_info_entry);
+ ins->proc_modules_info_entry = NULL;
+
+ snd_info_free_entry(ins->proc_parameter_dump_info_entry);
+ ins->proc_parameter_dump_info_entry = NULL;
+
+ snd_info_free_entry(ins->proc_sample_dump_info_entry);
+ ins->proc_sample_dump_info_entry = NULL;
+
+ snd_info_free_entry(ins->proc_scb_info_entry);
+ ins->proc_scb_info_entry = NULL;
+
+ snd_info_free_entry(ins->proc_task_info_entry);
+ ins->proc_task_info_entry = NULL;
mutex_lock(&chip->spos_mutex);
for (i = 0; i < ins->nscb; ++i) {
@@ -905,10 +893,8 @@ int cs46xx_dsp_proc_done (struct snd_cs46xx *chip)
}
mutex_unlock(&chip->spos_mutex);
- if (ins->proc_dsp_dir) {
- snd_info_unregister (ins->proc_dsp_dir);
- ins->proc_dsp_dir = NULL;
- }
+ snd_info_free_entry(ins->proc_dsp_dir);
+ ins->proc_dsp_dir = NULL;
return 0;
}
diff --git a/sound/pci/cs46xx/dsp_spos_scb_lib.c b/sound/pci/cs46xx/dsp_spos_scb_lib.c
index 3844d18af19c..343f51d5311b 100644
--- a/sound/pci/cs46xx/dsp_spos_scb_lib.c
+++ b/sound/pci/cs46xx/dsp_spos_scb_lib.c
@@ -180,6 +180,7 @@ static void _dsp_clear_sample_buffer (struct snd_cs46xx *chip, u32 sample_buffer
void cs46xx_dsp_remove_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor * scb)
{
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+ unsigned long flags;
/* check integrety */
snd_assert ( (scb->index >= 0 &&
@@ -194,9 +195,9 @@ void cs46xx_dsp_remove_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor *
goto _end);
#endif
- spin_lock(&scb->lock);
+ spin_lock_irqsave(&scb->lock, flags);
_dsp_unlink_scb (chip,scb);
- spin_unlock(&scb->lock);
+ spin_unlock_irqrestore(&scb->lock, flags);
cs46xx_dsp_proc_free_scb_desc(scb);
snd_assert (scb->scb_symbol != NULL, return );
@@ -232,7 +233,7 @@ void cs46xx_dsp_proc_free_scb_desc (struct dsp_scb_descriptor * scb)
snd_printdd("cs46xx_dsp_proc_free_scb_desc: freeing %s\n",scb->scb_name);
- snd_info_unregister(scb->proc_info);
+ snd_info_free_entry(scb->proc_info);
scb->proc_info = NULL;
snd_assert (scb_info != NULL, return);
diff --git a/sound/pci/cs5535audio/Makefile b/sound/pci/cs5535audio/Makefile
index 2911a8adc1f2..ad947b4c04cc 100644
--- a/sound/pci/cs5535audio/Makefile
+++ b/sound/pci/cs5535audio/Makefile
@@ -4,7 +4,7 @@
snd-cs5535audio-objs := cs5535audio.o cs5535audio_pcm.o
-ifdef CONFIG_PM
+ifeq ($(CONFIG_PM),y)
snd-cs5535audio-objs += cs5535audio_pm.o
endif
diff --git a/sound/pci/cs5535audio/cs5535audio.c b/sound/pci/cs5535audio/cs5535audio.c
index 91c18a11fe87..64c7826e8b8c 100644
--- a/sound/pci/cs5535audio/cs5535audio.c
+++ b/sound/pci/cs5535audio/cs5535audio.c
@@ -67,7 +67,7 @@ MODULE_PARM_DESC(id, "ID string for " DRIVER_NAME);
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable " DRIVER_NAME);
-static struct pci_device_id snd_cs5535audio_ids[] __devinitdata = {
+static struct pci_device_id snd_cs5535audio_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_AUDIO) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_AUDIO) },
{}
@@ -321,7 +321,7 @@ static int __devinit snd_cs5535audio_create(struct snd_card *card,
cs5535au->port = pci_resource_start(pci, 0);
if (request_irq(pci->irq, snd_cs5535audio_interrupt,
- SA_INTERRUPT|SA_SHIRQ, "CS5535 Audio", cs5535au)) {
+ IRQF_DISABLED|IRQF_SHARED, "CS5535 Audio", cs5535au)) {
snd_printk("unable to grab IRQ %d\n", pci->irq);
err = -EBUSY;
goto sndfail;
diff --git a/sound/pci/cs5535audio/cs5535audio_pcm.c b/sound/pci/cs5535audio/cs5535audio_pcm.c
index f0a48693d687..5450a9e8f133 100644
--- a/sound/pci/cs5535audio/cs5535audio_pcm.c
+++ b/sound/pci/cs5535audio/cs5535audio_pcm.c
@@ -143,7 +143,7 @@ static int cs5535audio_build_dma_packets(struct cs5535audio *cs5535au,
if (dma->periods == periods && dma->period_bytes == period_bytes)
return 0;
- /* the u32 cast is okay because in snd*create we succesfully told
+ /* the u32 cast is okay because in snd*create we successfully told
pci alloc that we're only 32 bit capable so the uppper will be 0 */
addr = (u32) substream->runtime->dma_addr;
desc_addr = (u32) dma->desc_buf.addr;
diff --git a/sound/pci/echoaudio/Makefile b/sound/pci/echoaudio/Makefile
new file mode 100644
index 000000000000..7b576aeb3f8d
--- /dev/null
+++ b/sound/pci/echoaudio/Makefile
@@ -0,0 +1,30 @@
+#
+# Makefile for ALSA Echoaudio soundcard drivers
+# Copyright (c) 2003 by Giuliano Pochini <pochini@shiny.it>
+#
+
+snd-darla20-objs := darla20.o
+snd-gina20-objs := gina20.o
+snd-layla20-objs := layla20.o
+snd-darla24-objs := darla24.o
+snd-gina24-objs := gina24.o
+snd-layla24-objs := layla24.o
+snd-mona-objs := mona.o
+snd-mia-objs := mia.o
+snd-echo3g-objs := echo3g.o
+snd-indigo-objs := indigo.o
+snd-indigoio-objs := indigoio.o
+snd-indigodj-objs := indigodj.o
+
+obj-$(CONFIG_SND_DARLA20) += snd-darla20.o
+obj-$(CONFIG_SND_GINA20) += snd-gina20.o
+obj-$(CONFIG_SND_LAYLA20) += snd-layla20.o
+obj-$(CONFIG_SND_DARLA24) += snd-darla24.o
+obj-$(CONFIG_SND_GINA24) += snd-gina24.o
+obj-$(CONFIG_SND_LAYLA24) += snd-layla24.o
+obj-$(CONFIG_SND_MONA) += snd-mona.o
+obj-$(CONFIG_SND_MIA) += snd-mia.o
+obj-$(CONFIG_SND_ECHO3G) += snd-echo3g.o
+obj-$(CONFIG_SND_INDIGO) += snd-indigo.o
+obj-$(CONFIG_SND_INDIGOIO) += snd-indigoio.o
+obj-$(CONFIG_SND_INDIGODJ) += snd-indigodj.o
diff --git a/sound/pci/echoaudio/darla20.c b/sound/pci/echoaudio/darla20.c
new file mode 100644
index 000000000000..b7108e29a668
--- /dev/null
+++ b/sound/pci/echoaudio/darla20.c
@@ -0,0 +1,99 @@
+/*
+ * ALSA driver for Echoaudio soundcards.
+ * Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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.
+ */
+
+#define ECHOGALS_FAMILY
+#define ECHOCARD_DARLA20
+#define ECHOCARD_NAME "Darla20"
+#define ECHOCARD_HAS_MONITOR
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT 0 /* 8 */
+#define PX_DIGITAL_OUT 8 /* 0 */
+#define PX_ANALOG_IN 8 /* 2 */
+#define PX_DIGITAL_IN 10 /* 0 */
+#define PX_NUM 10
+
+/* Bus indexes */
+#define BX_ANALOG_OUT 0 /* 8 */
+#define BX_DIGITAL_OUT 8 /* 0 */
+#define BX_ANALOG_IN 8 /* 2 */
+#define BX_DIGITAL_IN 10 /* 0 */
+#define BX_NUM 10
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+#define FW_DARLA20_DSP 0
+
+static const struct firmware card_fw[] = {
+ {0, "darla20_dsp.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+ {0x1057, 0x1801, 0xECC0, 0x0010, 0, 0, 0}, /* DSP 56301 Darla20 rev.0 */
+ {0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_SYNC_START,
+ .formats = SNDRV_PCM_FMTBIT_U8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_S32_BE,
+ .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+ .rate_min = 44100,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = 262144,
+ .period_bytes_min = 32,
+ .period_bytes_max = 131072,
+ .periods_min = 2,
+ .periods_max = 220,
+ /* One page (4k) contains 512 instructions. I don't know if the hw
+ supports lists longer than this. In this case periods_max=220 is a
+ safe limit to make sure the list never exceeds 512 instructions. */
+};
+
+
+#include "darla20_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio.c"
diff --git a/sound/pci/echoaudio/darla20_dsp.c b/sound/pci/echoaudio/darla20_dsp.c
new file mode 100644
index 000000000000..4159e3bc186f
--- /dev/null
+++ b/sound/pci/echoaudio/darla20_dsp.c
@@ -0,0 +1,125 @@
+/***************************************************************************
+
+ Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+ All rights reserved
+ www.echoaudio.com
+
+ This file is part of Echo Digital Audio's generic driver library.
+
+ Echo Digital Audio's generic driver library is free software;
+ you can redistribute it and/or modify it under the terms of
+ the GNU General Public License as published by the Free Software
+ Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ 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.
+
+ *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+ int err;
+
+ DE_INIT(("init_hw() - Darla20\n"));
+ snd_assert((subdevice_id & 0xfff0) == DARLA20, return -ENODEV);
+
+ if ((err = init_dsp_comm_page(chip))) {
+ DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+ return err;
+ }
+
+ chip->device_id = device_id;
+ chip->subdevice_id = subdevice_id;
+ chip->bad_board = TRUE;
+ chip->dsp_code_to_load = &card_fw[FW_DARLA20_DSP];
+ chip->spdif_status = GD_SPDIF_STATUS_UNDEF;
+ chip->clock_state = GD_CLOCK_UNDEF;
+ /* Since this card has no ASIC, mark it as loaded so everything
+ works OK */
+ chip->asic_loaded = TRUE;
+ chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL;
+
+ if ((err = load_firmware(chip)) < 0)
+ return err;
+ chip->bad_board = FALSE;
+
+ if ((err = init_line_levels(chip)) < 0)
+ return err;
+
+ DE_INIT(("init_hw done\n"));
+ return err;
+}
+
+
+
+/* The Darla20 has no external clock sources */
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+ return ECHO_CLOCK_BIT_INTERNAL;
+}
+
+
+
+/* The Darla20 has no ASIC. Just do nothing */
+static int load_asic(struct echoaudio *chip)
+{
+ return 0;
+}
+
+
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+ u8 clock_state, spdif_status;
+
+ if (wait_handshake(chip))
+ return -EIO;
+
+ switch (rate) {
+ case 44100:
+ clock_state = GD_CLOCK_44;
+ spdif_status = GD_SPDIF_STATUS_44;
+ break;
+ case 48000:
+ clock_state = GD_CLOCK_48;
+ spdif_status = GD_SPDIF_STATUS_48;
+ break;
+ default:
+ clock_state = GD_CLOCK_NOCHANGE;
+ spdif_status = GD_SPDIF_STATUS_NOCHANGE;
+ break;
+ }
+
+ if (chip->clock_state == clock_state)
+ clock_state = GD_CLOCK_NOCHANGE;
+ if (spdif_status == chip->spdif_status)
+ spdif_status = GD_SPDIF_STATUS_NOCHANGE;
+
+ chip->comm_page->sample_rate = cpu_to_le32(rate);
+ chip->comm_page->gd_clock_state = clock_state;
+ chip->comm_page->gd_spdif_status = spdif_status;
+ chip->comm_page->gd_resampler_state = 3; /* magic number - should always be 3 */
+
+ /* Save the new audio state if it changed */
+ if (clock_state != GD_CLOCK_NOCHANGE)
+ chip->clock_state = clock_state;
+ if (spdif_status != GD_SPDIF_STATUS_NOCHANGE)
+ chip->spdif_status = spdif_status;
+ chip->sample_rate = rate;
+
+ clear_handshake(chip);
+ return send_vector(chip, DSP_VC_SET_GD_AUDIO_STATE);
+}
diff --git a/sound/pci/echoaudio/darla24.c b/sound/pci/echoaudio/darla24.c
new file mode 100644
index 000000000000..e59a982ee361
--- /dev/null
+++ b/sound/pci/echoaudio/darla24.c
@@ -0,0 +1,106 @@
+/*
+ * ALSA driver for Echoaudio soundcards.
+ * Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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.
+ */
+
+#define ECHOGALS_FAMILY
+#define ECHOCARD_DARLA24
+#define ECHOCARD_NAME "Darla24"
+#define ECHOCARD_HAS_MONITOR
+#define ECHOCARD_HAS_INPUT_NOMINAL_LEVEL
+#define ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL
+#define ECHOCARD_HAS_EXTERNAL_CLOCK
+#define ECHOCARD_HAS_SUPER_INTERLEAVE
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT 0 /* 8 */
+#define PX_DIGITAL_OUT 8 /* 0 */
+#define PX_ANALOG_IN 8 /* 2 */
+#define PX_DIGITAL_IN 10 /* 0 */
+#define PX_NUM 10
+
+/* Bus indexes */
+#define BX_ANALOG_OUT 0 /* 8 */
+#define BX_DIGITAL_OUT 8 /* 0 */
+#define BX_ANALOG_IN 8 /* 2 */
+#define BX_DIGITAL_IN 10 /* 0 */
+#define BX_NUM 10
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+#define FW_DARLA24_DSP 0
+
+static const struct firmware card_fw[] = {
+ {0, "darla24_dsp.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+ {0x1057, 0x1801, 0xECC0, 0x0040, 0, 0, 0}, /* DSP 56301 Darla24 rev.0 */
+ {0x1057, 0x1801, 0xECC0, 0x0041, 0, 0, 0}, /* DSP 56301 Darla24 rev.1 */
+ {0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_SYNC_START,
+ .formats = SNDRV_PCM_FMTBIT_U8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_S32_BE,
+ .rates = SNDRV_PCM_RATE_8000_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .channels_min = 1,
+ .channels_max = 8,
+ .buffer_bytes_max = 262144,
+ .period_bytes_min = 32,
+ .period_bytes_max = 131072,
+ .periods_min = 2,
+ .periods_max = 220,
+ /* One page (4k) contains 512 instructions. I don't know if the hw
+ supports lists longer than this. In this case periods_max=220 is a
+ safe limit to make sure the list never exceeds 512 instructions. */
+};
+
+
+#include "darla24_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio.c"
diff --git a/sound/pci/echoaudio/darla24_dsp.c b/sound/pci/echoaudio/darla24_dsp.c
new file mode 100644
index 000000000000..79938eed7e9c
--- /dev/null
+++ b/sound/pci/echoaudio/darla24_dsp.c
@@ -0,0 +1,156 @@
+/***************************************************************************
+
+ Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+ All rights reserved
+ www.echoaudio.com
+
+ This file is part of Echo Digital Audio's generic driver library.
+
+ Echo Digital Audio's generic driver library is free software;
+ you can redistribute it and/or modify it under the terms of
+ the GNU General Public License as published by the Free Software
+ Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ 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.
+
+ *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+ int err;
+
+ DE_INIT(("init_hw() - Darla24\n"));
+ snd_assert((subdevice_id & 0xfff0) == DARLA24, return -ENODEV);
+
+ if ((err = init_dsp_comm_page(chip))) {
+ DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+ return err;
+ }
+
+ chip->device_id = device_id;
+ chip->subdevice_id = subdevice_id;
+ chip->bad_board = TRUE;
+ chip->dsp_code_to_load = &card_fw[FW_DARLA24_DSP];
+ /* Since this card has no ASIC, mark it as loaded so everything
+ works OK */
+ chip->asic_loaded = TRUE;
+ chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL |
+ ECHO_CLOCK_BIT_ESYNC;
+
+ if ((err = load_firmware(chip)) < 0)
+ return err;
+ chip->bad_board = FALSE;
+
+ if ((err = init_line_levels(chip)) < 0)
+ return err;
+
+ DE_INIT(("init_hw done\n"));
+ return err;
+}
+
+
+
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+ u32 clocks_from_dsp, clock_bits;
+
+ /* Map the DSP clock detect bits to the generic driver clock
+ detect bits */
+ clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks);
+
+ clock_bits = ECHO_CLOCK_BIT_INTERNAL;
+
+ if (clocks_from_dsp & GLDM_CLOCK_DETECT_BIT_ESYNC)
+ clock_bits |= ECHO_CLOCK_BIT_ESYNC;
+
+ return clock_bits;
+}
+
+
+
+/* The Darla24 has no ASIC. Just do nothing */
+static int load_asic(struct echoaudio *chip)
+{
+ return 0;
+}
+
+
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+ u8 clock;
+
+ switch (rate) {
+ case 96000:
+ clock = GD24_96000;
+ break;
+ case 88200:
+ clock = GD24_88200;
+ break;
+ case 48000:
+ clock = GD24_48000;
+ break;
+ case 44100:
+ clock = GD24_44100;
+ break;
+ case 32000:
+ clock = GD24_32000;
+ break;
+ case 22050:
+ clock = GD24_22050;
+ break;
+ case 16000:
+ clock = GD24_16000;
+ break;
+ case 11025:
+ clock = GD24_11025;
+ break;
+ case 8000:
+ clock = GD24_8000;
+ break;
+ default:
+ DE_ACT(("set_sample_rate: Error, invalid sample rate %d\n",
+ rate));
+ return -EINVAL;
+ }
+
+ if (wait_handshake(chip))
+ return -EIO;
+
+ DE_ACT(("set_sample_rate: %d clock %d\n", rate, clock));
+ chip->sample_rate = rate;
+
+ /* Override the sample rate if this card is set to Echo sync. */
+ if (chip->input_clock == ECHO_CLOCK_ESYNC)
+ clock = GD24_EXT_SYNC;
+
+ chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP ? */
+ chip->comm_page->gd_clock_state = clock;
+ clear_handshake(chip);
+ return send_vector(chip, DSP_VC_SET_GD_AUDIO_STATE);
+}
+
+
+
+static int set_input_clock(struct echoaudio *chip, u16 clock)
+{
+ snd_assert(clock == ECHO_CLOCK_INTERNAL ||
+ clock == ECHO_CLOCK_ESYNC, return -EINVAL);
+ chip->input_clock = clock;
+ return set_sample_rate(chip, chip->sample_rate);
+}
+
diff --git a/sound/pci/echoaudio/echo3g.c b/sound/pci/echoaudio/echo3g.c
new file mode 100644
index 000000000000..12099fe1547d
--- /dev/null
+++ b/sound/pci/echoaudio/echo3g.c
@@ -0,0 +1,118 @@
+/*
+ * ALSA driver for Echoaudio soundcards.
+ * Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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.
+ */
+
+#define ECHO3G_FAMILY
+#define ECHOCARD_ECHO3G
+#define ECHOCARD_NAME "Echo3G"
+#define ECHOCARD_HAS_MONITOR
+#define ECHOCARD_HAS_ASIC
+#define ECHOCARD_HAS_INPUT_NOMINAL_LEVEL
+#define ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL
+#define ECHOCARD_HAS_SUPER_INTERLEAVE
+#define ECHOCARD_HAS_DIGITAL_IO
+#define ECHOCARD_HAS_DIGITAL_MODE_SWITCH
+#define ECHOCARD_HAS_ADAT 6
+#define ECHOCARD_HAS_EXTERNAL_CLOCK
+#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+#define ECHOCARD_HAS_MIDI
+#define ECHOCARD_HAS_PHANTOM_POWER
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT 0
+#define PX_DIGITAL_OUT chip->px_digital_out
+#define PX_ANALOG_IN chip->px_analog_in
+#define PX_DIGITAL_IN chip->px_digital_in
+#define PX_NUM chip->px_num
+
+/* Bus indexes */
+#define BX_ANALOG_OUT 0
+#define BX_DIGITAL_OUT chip->bx_digital_out
+#define BX_ANALOG_IN chip->bx_analog_in
+#define BX_DIGITAL_IN chip->bx_digital_in
+#define BX_NUM chip->bx_num
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <sound/rawmidi.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+#define FW_361_LOADER 0
+#define FW_ECHO3G_DSP 1
+#define FW_3G_ASIC 2
+
+static const struct firmware card_fw[] = {
+ {0, "loader_dsp.fw"},
+ {0, "echo3g_dsp.fw"},
+ {0, "3g_asic.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+ {0x1057, 0x3410, 0xECC0, 0x0100, 0, 0, 0}, /* Echo 3G */
+ {0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_SYNC_START,
+ .formats = SNDRV_PCM_FMTBIT_U8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_S32_BE,
+ .rates = SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 32000,
+ .rate_max = 100000,
+ .channels_min = 1,
+ .channels_max = 8,
+ .buffer_bytes_max = 262144,
+ .period_bytes_min = 32,
+ .period_bytes_max = 131072,
+ .periods_min = 2,
+ .periods_max = 220,
+};
+
+#include "echo3g_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio_3g.c"
+#include "echoaudio.c"
+#include "midi.c"
diff --git a/sound/pci/echoaudio/echo3g_dsp.c b/sound/pci/echoaudio/echo3g_dsp.c
new file mode 100644
index 000000000000..d26a1d1f3ed1
--- /dev/null
+++ b/sound/pci/echoaudio/echo3g_dsp.c
@@ -0,0 +1,131 @@
+/****************************************************************************
+
+ Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+ All rights reserved
+ www.echoaudio.com
+
+ This file is part of Echo Digital Audio's generic driver library.
+
+ Echo Digital Audio's generic driver library is free software;
+ you can redistribute it and/or modify it under the terms of
+ the GNU General Public License as published by the Free Software
+ Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ 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.
+
+ *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+static int load_asic(struct echoaudio *chip);
+static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode);
+static int set_digital_mode(struct echoaudio *chip, u8 mode);
+static int check_asic_status(struct echoaudio *chip);
+static int set_sample_rate(struct echoaudio *chip, u32 rate);
+static int set_input_clock(struct echoaudio *chip, u16 clock);
+static int set_professional_spdif(struct echoaudio *chip, char prof);
+static int set_phantom_power(struct echoaudio *chip, char on);
+static int write_control_reg(struct echoaudio *chip, u32 ctl, u32 frq,
+ char force);
+
+#include <linux/irq.h>
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+ int err;
+
+ local_irq_enable();
+ DE_INIT(("init_hw() - Echo3G\n"));
+ snd_assert((subdevice_id & 0xfff0) == ECHO3G, return -ENODEV);
+
+ if ((err = init_dsp_comm_page(chip))) {
+ DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+ return err;
+ }
+
+ chip->comm_page->e3g_frq_register =
+ __constant_cpu_to_le32((E3G_MAGIC_NUMBER / 48000) - 2);
+ chip->device_id = device_id;
+ chip->subdevice_id = subdevice_id;
+ chip->bad_board = TRUE;
+ chip->has_midi = TRUE;
+ chip->dsp_code_to_load = &card_fw[FW_ECHO3G_DSP];
+
+ /* Load the DSP code and the ASIC on the PCI card and get
+ what type of external box is attached */
+ err = load_firmware(chip);
+
+ if (err < 0) {
+ return err;
+ } else if (err == E3G_GINA3G_BOX_TYPE) {
+ chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL |
+ ECHO_CLOCK_BIT_SPDIF |
+ ECHO_CLOCK_BIT_ADAT;
+ chip->card_name = "Gina3G";
+ chip->px_digital_out = chip->bx_digital_out = 6;
+ chip->px_analog_in = chip->bx_analog_in = 14;
+ chip->px_digital_in = chip->bx_digital_in = 16;
+ chip->px_num = chip->bx_num = 24;
+ chip->has_phantom_power = TRUE;
+ chip->hasnt_input_nominal_level = TRUE;
+ } else if (err == E3G_LAYLA3G_BOX_TYPE) {
+ chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL |
+ ECHO_CLOCK_BIT_SPDIF |
+ ECHO_CLOCK_BIT_ADAT |
+ ECHO_CLOCK_BIT_WORD;
+ chip->card_name = "Layla3G";
+ chip->px_digital_out = chip->bx_digital_out = 8;
+ chip->px_analog_in = chip->bx_analog_in = 16;
+ chip->px_digital_in = chip->bx_digital_in = 24;
+ chip->px_num = chip->bx_num = 32;
+ } else {
+ return -ENODEV;
+ }
+
+ chip->digital_modes = ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA |
+ ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL |
+ ECHOCAPS_HAS_DIGITAL_MODE_ADAT;
+ chip->digital_mode = DIGITAL_MODE_SPDIF_RCA;
+ chip->professional_spdif = FALSE;
+ chip->non_audio_spdif = FALSE;
+ chip->bad_board = FALSE;
+
+ if ((err = init_line_levels(chip)) < 0)
+ return err;
+ err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA);
+ snd_assert(err >= 0, return err);
+ err = set_phantom_power(chip, 0);
+ snd_assert(err >= 0, return err);
+ err = set_professional_spdif(chip, TRUE);
+
+ DE_INIT(("init_hw done\n"));
+ return err;
+}
+
+
+
+static int set_phantom_power(struct echoaudio *chip, char on)
+{
+ u32 control_reg = le32_to_cpu(chip->comm_page->control_register);
+
+ if (on)
+ control_reg |= E3G_PHANTOM_POWER;
+ else
+ control_reg &= ~E3G_PHANTOM_POWER;
+
+ chip->phantom_power = on;
+ return write_control_reg(chip, control_reg,
+ le32_to_cpu(chip->comm_page->e3g_frq_register),
+ 0);
+}
diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c
new file mode 100644
index 000000000000..c3dafa29054f
--- /dev/null
+++ b/sound/pci/echoaudio/echoaudio.c
@@ -0,0 +1,2196 @@
+/*
+ * ALSA driver for Echoaudio soundcards.
+ * Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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.
+ */
+
+MODULE_AUTHOR("Giuliano Pochini <pochini@shiny.it>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Echoaudio " ECHOCARD_NAME " soundcards driver");
+MODULE_SUPPORTED_DEVICE("{{Echoaudio," ECHOCARD_NAME "}}");
+MODULE_DEVICE_TABLE(pci, snd_echo_ids);
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for " ECHOCARD_NAME " soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for " ECHOCARD_NAME " soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable " ECHOCARD_NAME " soundcard.");
+
+static unsigned int channels_list[10] = {1, 2, 4, 6, 8, 10, 12, 14, 16, 999999};
+
+static int get_firmware(const struct firmware **fw_entry,
+ const struct firmware *frm, struct echoaudio *chip)
+{
+ int err;
+ char name[30];
+ DE_ACT(("firmware requested: %s\n", frm->data));
+ snprintf(name, sizeof(name), "ea/%s", frm->data);
+ if ((err = request_firmware(fw_entry, name, pci_device(chip))) < 0)
+ snd_printk(KERN_ERR "get_firmware(): Firmware not available (%d)\n", err);
+ return err;
+}
+
+static void free_firmware(const struct firmware *fw_entry)
+{
+ release_firmware(fw_entry);
+ DE_ACT(("firmware released\n"));
+}
+
+
+
+/******************************************************************************
+ PCM interface
+******************************************************************************/
+
+static void audiopipe_free(struct snd_pcm_runtime *runtime)
+{
+ struct audiopipe *pipe = runtime->private_data;
+
+ if (pipe->sgpage.area)
+ snd_dma_free_pages(&pipe->sgpage);
+ kfree(pipe);
+}
+
+
+
+static int hw_rule_capture_format_by_channels(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *c = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ struct snd_mask fmt;
+
+ snd_mask_any(&fmt);
+
+#ifndef ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+ /* >=2 channels cannot be S32_BE */
+ if (c->min == 2) {
+ fmt.bits[0] &= ~SNDRV_PCM_FMTBIT_S32_BE;
+ return snd_mask_refine(f, &fmt);
+ }
+#endif
+ /* > 2 channels cannot be U8 and S32_BE */
+ if (c->min > 2) {
+ fmt.bits[0] &= ~(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_BE);
+ return snd_mask_refine(f, &fmt);
+ }
+ /* Mono is ok with any format */
+ return 0;
+}
+
+
+
+static int hw_rule_capture_channels_by_format(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *c = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ struct snd_interval ch;
+
+ snd_interval_any(&ch);
+
+ /* S32_BE is mono (and stereo) only */
+ if (f->bits[0] == SNDRV_PCM_FMTBIT_S32_BE) {
+ ch.min = 1;
+#ifdef ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+ ch.max = 2;
+#else
+ ch.max = 1;
+#endif
+ ch.integer = 1;
+ return snd_interval_refine(c, &ch);
+ }
+ /* U8 can be only mono or stereo */
+ if (f->bits[0] == SNDRV_PCM_FMTBIT_U8) {
+ ch.min = 1;
+ ch.max = 2;
+ ch.integer = 1;
+ return snd_interval_refine(c, &ch);
+ }
+ /* S16_LE, S24_3LE and S32_LE support any number of channels. */
+ return 0;
+}
+
+
+
+static int hw_rule_playback_format_by_channels(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *c = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ struct snd_mask fmt;
+ u64 fmask;
+ snd_mask_any(&fmt);
+
+ fmask = fmt.bits[0] + ((u64)fmt.bits[1] << 32);
+
+ /* >2 channels must be S16_LE, S24_3LE or S32_LE */
+ if (c->min > 2) {
+ fmask &= SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE;
+ /* 1 channel must be S32_BE or S32_LE */
+ } else if (c->max == 1)
+ fmask &= SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE;
+#ifndef ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+ /* 2 channels cannot be S32_BE */
+ else if (c->min == 2 && c->max == 2)
+ fmask &= ~SNDRV_PCM_FMTBIT_S32_BE;
+#endif
+ else
+ return 0;
+
+ fmt.bits[0] &= (u32)fmask;
+ fmt.bits[1] &= (u32)(fmask >> 32);
+ return snd_mask_refine(f, &fmt);
+}
+
+
+
+static int hw_rule_playback_channels_by_format(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *c = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ struct snd_interval ch;
+ u64 fmask;
+
+ snd_interval_any(&ch);
+ ch.integer = 1;
+ fmask = f->bits[0] + ((u64)f->bits[1] << 32);
+
+ /* S32_BE is mono (and stereo) only */
+ if (fmask == SNDRV_PCM_FMTBIT_S32_BE) {
+ ch.min = 1;
+#ifdef ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+ ch.max = 2;
+#else
+ ch.max = 1;
+#endif
+ /* U8 is stereo only */
+ } else if (fmask == SNDRV_PCM_FMTBIT_U8)
+ ch.min = ch.max = 2;
+ /* S16_LE and S24_3LE must be at least stereo */
+ else if (!(fmask & ~(SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE)))
+ ch.min = 2;
+ else
+ return 0;
+
+ return snd_interval_refine(c, &ch);
+}
+
+
+
+/* Since the sample rate is a global setting, do allow the user to change the
+sample rate only if there is only one pcm device open. */
+static int hw_rule_sample_rate(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct echoaudio *chip = rule->private;
+ struct snd_interval fixed;
+
+ if (!chip->can_set_rate) {
+ snd_interval_any(&fixed);
+ fixed.min = fixed.max = chip->sample_rate;
+ return snd_interval_refine(rate, &fixed);
+ }
+ return 0;
+}
+
+
+static int pcm_open(struct snd_pcm_substream *substream,
+ signed char max_channels)
+{
+ struct echoaudio *chip;
+ struct snd_pcm_runtime *runtime;
+ struct audiopipe *pipe;
+ int err, i;
+
+ if (max_channels <= 0)
+ return -EAGAIN;
+
+ chip = snd_pcm_substream_chip(substream);
+ runtime = substream->runtime;
+
+ pipe = kzalloc(sizeof(struct audiopipe), GFP_KERNEL);
+ if (!pipe)
+ return -ENOMEM;
+ pipe->index = -1; /* Not configured yet */
+
+ /* Set up hw capabilities and contraints */
+ memcpy(&pipe->hw, &pcm_hardware_skel, sizeof(struct snd_pcm_hardware));
+ DE_HWP(("max_channels=%d\n", max_channels));
+ pipe->constr.list = channels_list;
+ pipe->constr.mask = 0;
+ for (i = 0; channels_list[i] <= max_channels; i++);
+ pipe->constr.count = i;
+ if (pipe->hw.channels_max > max_channels)
+ pipe->hw.channels_max = max_channels;
+ if (chip->digital_mode == DIGITAL_MODE_ADAT) {
+ pipe->hw.rate_max = 48000;
+ pipe->hw.rates &= SNDRV_PCM_RATE_8000_48000;
+ }
+
+ runtime->hw = pipe->hw;
+ runtime->private_data = pipe;
+ runtime->private_free = audiopipe_free;
+ snd_pcm_set_sync(substream);
+
+ /* Only mono and any even number of channels are allowed */
+ if ((err = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ &pipe->constr)) < 0)
+ return err;
+
+ /* All periods should have the same size */
+ if ((err = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+ return err;
+
+ /* The hw accesses memory in chunks 32 frames long and they should be
+ 32-bytes-aligned. It's not a requirement, but it seems that IRQs are
+ generated with a resolution of 32 frames. Thus we need the following */
+ if ((err = snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ 32)) < 0)
+ return err;
+ if ((err = snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ 32)) < 0)
+ return err;
+
+ if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ hw_rule_sample_rate, chip,
+ SNDRV_PCM_HW_PARAM_RATE, -1)) < 0)
+ return err;
+
+ /* Finally allocate a page for the scatter-gather list */
+ if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci),
+ PAGE_SIZE, &pipe->sgpage)) < 0) {
+ DE_HWP(("s-g list allocation failed\n"));
+ return err;
+ }
+
+ return 0;
+}
+
+
+
+static int pcm_analog_in_open(struct snd_pcm_substream *substream)
+{
+ struct echoaudio *chip = snd_pcm_substream_chip(substream);
+ int err;
+
+ DE_ACT(("pcm_analog_in_open\n"));
+ if ((err = pcm_open(substream, num_analog_busses_in(chip) -
+ substream->number)) < 0)
+ return err;
+ if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ hw_rule_capture_channels_by_format, NULL,
+ SNDRV_PCM_HW_PARAM_FORMAT, -1)) < 0)
+ return err;
+ if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_FORMAT,
+ hw_rule_capture_format_by_channels, NULL,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1)) < 0)
+ return err;
+ atomic_inc(&chip->opencount);
+ if (atomic_read(&chip->opencount) > 1 && chip->rate_set)
+ chip->can_set_rate=0;
+ DE_HWP(("pcm_analog_in_open cs=%d oc=%d r=%d\n",
+ chip->can_set_rate, atomic_read(&chip->opencount),
+ chip->sample_rate));
+ return 0;
+}
+
+
+
+static int pcm_analog_out_open(struct snd_pcm_substream *substream)
+{
+ struct echoaudio *chip = snd_pcm_substream_chip(substream);
+ int max_channels, err;
+
+#ifdef ECHOCARD_HAS_VMIXER
+ max_channels = num_pipes_out(chip);
+#else
+ max_channels = num_analog_busses_out(chip);
+#endif
+ DE_ACT(("pcm_analog_out_open\n"));
+ if ((err = pcm_open(substream, max_channels - substream->number)) < 0)
+ return err;
+ if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ hw_rule_playback_channels_by_format,
+ NULL,
+ SNDRV_PCM_HW_PARAM_FORMAT, -1)) < 0)
+ return err;
+ if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_FORMAT,
+ hw_rule_playback_format_by_channels,
+ NULL,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1)) < 0)
+ return err;
+ atomic_inc(&chip->opencount);
+ if (atomic_read(&chip->opencount) > 1 && chip->rate_set)
+ chip->can_set_rate=0;
+ DE_HWP(("pcm_analog_out_open cs=%d oc=%d r=%d\n",
+ chip->can_set_rate, atomic_read(&chip->opencount),
+ chip->sample_rate));
+ return 0;
+}
+
+
+
+#ifdef ECHOCARD_HAS_DIGITAL_IO
+
+static int pcm_digital_in_open(struct snd_pcm_substream *substream)
+{
+ struct echoaudio *chip = snd_pcm_substream_chip(substream);
+ int err, max_channels;
+
+ DE_ACT(("pcm_digital_in_open\n"));
+ max_channels = num_digital_busses_in(chip) - substream->number;
+ down(&chip->mode_mutex);
+ if (chip->digital_mode == DIGITAL_MODE_ADAT)
+ err = pcm_open(substream, max_channels);
+ else /* If the card has ADAT, subtract the 6 channels
+ * that S/PDIF doesn't have
+ */
+ err = pcm_open(substream, max_channels - ECHOCARD_HAS_ADAT);
+
+ if (err < 0)
+ goto din_exit;
+
+ if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ hw_rule_capture_channels_by_format, NULL,
+ SNDRV_PCM_HW_PARAM_FORMAT, -1)) < 0)
+ goto din_exit;
+ if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_FORMAT,
+ hw_rule_capture_format_by_channels, NULL,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1)) < 0)
+ goto din_exit;
+
+ atomic_inc(&chip->opencount);
+ if (atomic_read(&chip->opencount) > 1 && chip->rate_set)
+ chip->can_set_rate=0;
+
+din_exit:
+ up(&chip->mode_mutex);
+ return err;
+}
+
+
+
+#ifndef ECHOCARD_HAS_VMIXER /* See the note in snd_echo_new_pcm() */
+
+static int pcm_digital_out_open(struct snd_pcm_substream *substream)
+{
+ struct echoaudio *chip = snd_pcm_substream_chip(substream);
+ int err, max_channels;
+
+ DE_ACT(("pcm_digital_out_open\n"));
+ max_channels = num_digital_busses_out(chip) - substream->number;
+ down(&chip->mode_mutex);
+ if (chip->digital_mode == DIGITAL_MODE_ADAT)
+ err = pcm_open(substream, max_channels);
+ else /* If the card has ADAT, subtract the 6 channels
+ * that S/PDIF doesn't have
+ */
+ err = pcm_open(substream, max_channels - ECHOCARD_HAS_ADAT);
+
+ if (err < 0)
+ goto dout_exit;
+
+ if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ hw_rule_playback_channels_by_format,
+ NULL, SNDRV_PCM_HW_PARAM_FORMAT,
+ -1)) < 0)
+ goto dout_exit;
+ if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_FORMAT,
+ hw_rule_playback_format_by_channels,
+ NULL, SNDRV_PCM_HW_PARAM_CHANNELS,
+ -1)) < 0)
+ goto dout_exit;
+ atomic_inc(&chip->opencount);
+ if (atomic_read(&chip->opencount) > 1 && chip->rate_set)
+ chip->can_set_rate=0;
+dout_exit:
+ up(&chip->mode_mutex);
+ return err;
+}
+
+#endif /* !ECHOCARD_HAS_VMIXER */
+
+#endif /* ECHOCARD_HAS_DIGITAL_IO */
+
+
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+ struct echoaudio *chip = snd_pcm_substream_chip(substream);
+ int oc;
+
+ /* Nothing to do here. Audio is already off and pipe will be
+ * freed by its callback
+ */
+ DE_ACT(("pcm_close\n"));
+
+ atomic_dec(&chip->opencount);
+ oc = atomic_read(&chip->opencount);
+ DE_ACT(("pcm_close oc=%d cs=%d rs=%d\n", oc,
+ chip->can_set_rate, chip->rate_set));
+ if (oc < 2)
+ chip->can_set_rate = 1;
+ if (oc == 0)
+ chip->rate_set = 0;
+ DE_ACT(("pcm_close2 oc=%d cs=%d rs=%d\n", oc,
+ chip->can_set_rate,chip->rate_set));
+
+ return 0;
+}
+
+
+
+/* Channel allocation and scatter-gather list setup */
+static int init_engine(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params,
+ int pipe_index, int interleave)
+{
+ struct echoaudio *chip;
+ int err, per, rest, page, edge, offs;
+ struct snd_sg_buf *sgbuf;
+ struct audiopipe *pipe;
+
+ chip = snd_pcm_substream_chip(substream);
+ pipe = (struct audiopipe *) substream->runtime->private_data;
+
+ /* Sets up che hardware. If it's already initialized, reset and
+ * redo with the new parameters
+ */
+ spin_lock_irq(&chip->lock);
+ if (pipe->index >= 0) {
+ DE_HWP(("hwp_ie free(%d)\n", pipe->index));
+ err = free_pipes(chip, pipe);
+ snd_assert(!err);
+ chip->substream[pipe->index] = NULL;
+ }
+
+ err = allocate_pipes(chip, pipe, pipe_index, interleave);
+ if (err < 0) {
+ spin_unlock_irq(&chip->lock);
+ DE_ACT((KERN_NOTICE "allocate_pipes(%d) err=%d\n",
+ pipe_index, err));
+ return err;
+ }
+ spin_unlock_irq(&chip->lock);
+ DE_ACT((KERN_NOTICE "allocate_pipes()=%d\n", pipe_index));
+
+ DE_HWP(("pcm_hw_params (bufsize=%dB periods=%d persize=%dB)\n",
+ params_buffer_bytes(hw_params), params_periods(hw_params),
+ params_period_bytes(hw_params)));
+ err = snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+ if (err < 0) {
+ snd_printk(KERN_ERR "malloc_pages err=%d\n", err);
+ spin_lock_irq(&chip->lock);
+ free_pipes(chip, pipe);
+ spin_unlock_irq(&chip->lock);
+ pipe->index = -1;
+ return err;
+ }
+
+ sgbuf = snd_pcm_substream_sgbuf(substream);
+
+ DE_HWP(("pcm_hw_params table size=%d pages=%d\n",
+ sgbuf->size, sgbuf->pages));
+ sglist_init(chip, pipe);
+ edge = PAGE_SIZE;
+ for (offs = page = per = 0; offs < params_buffer_bytes(hw_params);
+ per++) {
+ rest = params_period_bytes(hw_params);
+ if (offs + rest > params_buffer_bytes(hw_params))
+ rest = params_buffer_bytes(hw_params) - offs;
+ while (rest) {
+ if (rest <= edge - offs) {
+ sglist_add_mapping(chip, pipe,
+ snd_sgbuf_get_addr(sgbuf, offs),
+ rest);
+ sglist_add_irq(chip, pipe);
+ offs += rest;
+ rest = 0;
+ } else {
+ sglist_add_mapping(chip, pipe,
+ snd_sgbuf_get_addr(sgbuf, offs),
+ edge - offs);
+ rest -= edge - offs;
+ offs = edge;
+ }
+ if (offs == edge) {
+ edge += PAGE_SIZE;
+ page++;
+ }
+ }
+ }
+
+ /* Close the ring buffer */
+ sglist_wrap(chip, pipe);
+
+ /* This stuff is used by the irq handler, so it must be
+ * initialized before chip->substream
+ */
+ chip->last_period[pipe_index] = 0;
+ pipe->last_counter = 0;
+ pipe->position = 0;
+ smp_wmb();
+ chip->substream[pipe_index] = substream;
+ chip->rate_set = 1;
+ spin_lock_irq(&chip->lock);
+ set_sample_rate(chip, hw_params->rate_num / hw_params->rate_den);
+ spin_unlock_irq(&chip->lock);
+ DE_HWP(("pcm_hw_params ok\n"));
+ return 0;
+}
+
+
+
+static int pcm_analog_in_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct echoaudio *chip = snd_pcm_substream_chip(substream);
+
+ return init_engine(substream, hw_params, px_analog_in(chip) +
+ substream->number, params_channels(hw_params));
+}
+
+
+
+static int pcm_analog_out_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ return init_engine(substream, hw_params, substream->number,
+ params_channels(hw_params));
+}
+
+
+
+#ifdef ECHOCARD_HAS_DIGITAL_IO
+
+static int pcm_digital_in_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct echoaudio *chip = snd_pcm_substream_chip(substream);
+
+ return init_engine(substream, hw_params, px_digital_in(chip) +
+ substream->number, params_channels(hw_params));
+}
+
+
+
+#ifndef ECHOCARD_HAS_VMIXER /* See the note in snd_echo_new_pcm() */
+static int pcm_digital_out_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct echoaudio *chip = snd_pcm_substream_chip(substream);
+
+ return init_engine(substream, hw_params, px_digital_out(chip) +
+ substream->number, params_channels(hw_params));
+}
+#endif /* !ECHOCARD_HAS_VMIXER */
+
+#endif /* ECHOCARD_HAS_DIGITAL_IO */
+
+
+
+static int pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct echoaudio *chip;
+ struct audiopipe *pipe;
+
+ chip = snd_pcm_substream_chip(substream);
+ pipe = (struct audiopipe *) substream->runtime->private_data;
+
+ spin_lock_irq(&chip->lock);
+ if (pipe->index >= 0) {
+ DE_HWP(("pcm_hw_free(%d)\n", pipe->index));
+ free_pipes(chip, pipe);
+ chip->substream[pipe->index] = NULL;
+ pipe->index = -1;
+ }
+ spin_unlock_irq(&chip->lock);
+
+ DE_HWP(("pcm_hw_freed\n"));
+ snd_pcm_lib_free_pages(substream);
+ return 0;
+}
+
+
+
+static int pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct echoaudio *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audioformat format;
+ int pipe_index = ((struct audiopipe *)runtime->private_data)->index;
+
+ DE_HWP(("Prepare rate=%d format=%d channels=%d\n",
+ runtime->rate, runtime->format, runtime->channels));
+ format.interleave = runtime->channels;
+ format.data_are_bigendian = 0;
+ format.mono_to_stereo = 0;
+ switch (runtime->format) {
+ case SNDRV_PCM_FORMAT_U8:
+ format.bits_per_sample = 8;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ format.bits_per_sample = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ format.bits_per_sample = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_BE:
+ format.data_are_bigendian = 1;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ format.bits_per_sample = 32;
+ break;
+ default:
+ DE_HWP(("Prepare error: unsupported format %d\n",
+ runtime->format));
+ return -EINVAL;
+ }
+
+ snd_assert(pipe_index < px_num(chip), return -EINVAL);
+ snd_assert(is_pipe_allocated(chip, pipe_index), return -EINVAL);
+ set_audio_format(chip, pipe_index, &format);
+ return 0;
+}
+
+
+
+static int pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct echoaudio *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audiopipe *pipe = runtime->private_data;
+ int i, err;
+ u32 channelmask = 0;
+ struct list_head *pos;
+ struct snd_pcm_substream *s;
+
+ snd_pcm_group_for_each(pos, substream) {
+ s = snd_pcm_group_substream_entry(pos);
+ for (i = 0; i < DSP_MAXPIPES; i++) {
+ if (s == chip->substream[i]) {
+ channelmask |= 1 << i;
+ snd_pcm_trigger_done(s, substream);
+ }
+ }
+ }
+
+ spin_lock(&chip->lock);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ DE_ACT(("pcm_trigger start\n"));
+ for (i = 0; i < DSP_MAXPIPES; i++) {
+ if (channelmask & (1 << i)) {
+ pipe = chip->substream[i]->runtime->private_data;
+ switch (pipe->state) {
+ case PIPE_STATE_STOPPED:
+ chip->last_period[i] = 0;
+ pipe->last_counter = 0;
+ pipe->position = 0;
+ *pipe->dma_counter = 0;
+ case PIPE_STATE_PAUSED:
+ pipe->state = PIPE_STATE_STARTED;
+ break;
+ case PIPE_STATE_STARTED:
+ break;
+ }
+ }
+ }
+ err = start_transport(chip, channelmask,
+ chip->pipe_cyclic_mask);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ DE_ACT(("pcm_trigger stop\n"));
+ for (i = 0; i < DSP_MAXPIPES; i++) {
+ if (channelmask & (1 << i)) {
+ pipe = chip->substream[i]->runtime->private_data;
+ pipe->state = PIPE_STATE_STOPPED;
+ }
+ }
+ err = stop_transport(chip, channelmask);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ DE_ACT(("pcm_trigger pause\n"));
+ for (i = 0; i < DSP_MAXPIPES; i++) {
+ if (channelmask & (1 << i)) {
+ pipe = chip->substream[i]->runtime->private_data;
+ pipe->state = PIPE_STATE_PAUSED;
+ }
+ }
+ err = pause_transport(chip, channelmask);
+ break;
+ default:
+ err = -EINVAL;
+ }
+ spin_unlock(&chip->lock);
+ return err;
+}
+
+
+
+static snd_pcm_uframes_t pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audiopipe *pipe = runtime->private_data;
+ size_t cnt, bufsize, pos;
+
+ cnt = le32_to_cpu(*pipe->dma_counter);
+ pipe->position += cnt - pipe->last_counter;
+ pipe->last_counter = cnt;
+ bufsize = substream->runtime->buffer_size;
+ pos = bytes_to_frames(substream->runtime, pipe->position);
+
+ while (pos >= bufsize) {
+ pipe->position -= frames_to_bytes(substream->runtime, bufsize);
+ pos -= bufsize;
+ }
+ return pos;
+}
+
+
+
+/* pcm *_ops structures */
+static struct snd_pcm_ops analog_playback_ops = {
+ .open = pcm_analog_out_open,
+ .close = pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = pcm_analog_out_hw_params,
+ .hw_free = pcm_hw_free,
+ .prepare = pcm_prepare,
+ .trigger = pcm_trigger,
+ .pointer = pcm_pointer,
+ .page = snd_pcm_sgbuf_ops_page,
+};
+static struct snd_pcm_ops analog_capture_ops = {
+ .open = pcm_analog_in_open,
+ .close = pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = pcm_analog_in_hw_params,
+ .hw_free = pcm_hw_free,
+ .prepare = pcm_prepare,
+ .trigger = pcm_trigger,
+ .pointer = pcm_pointer,
+ .page = snd_pcm_sgbuf_ops_page,
+};
+#ifdef ECHOCARD_HAS_DIGITAL_IO
+#ifndef ECHOCARD_HAS_VMIXER
+static struct snd_pcm_ops digital_playback_ops = {
+ .open = pcm_digital_out_open,
+ .close = pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = pcm_digital_out_hw_params,
+ .hw_free = pcm_hw_free,
+ .prepare = pcm_prepare,
+ .trigger = pcm_trigger,
+ .pointer = pcm_pointer,
+ .page = snd_pcm_sgbuf_ops_page,
+};
+#endif /* !ECHOCARD_HAS_VMIXER */
+static struct snd_pcm_ops digital_capture_ops = {
+ .open = pcm_digital_in_open,
+ .close = pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = pcm_digital_in_hw_params,
+ .hw_free = pcm_hw_free,
+ .prepare = pcm_prepare,
+ .trigger = pcm_trigger,
+ .pointer = pcm_pointer,
+ .page = snd_pcm_sgbuf_ops_page,
+};
+#endif /* ECHOCARD_HAS_DIGITAL_IO */
+
+
+
+/* Preallocate memory only for the first substream because it's the most
+ * used one
+ */
+static int snd_echo_preallocate_pages(struct snd_pcm *pcm, struct device *dev)
+{
+ struct snd_pcm_substream *ss;
+ int stream, err;
+
+ for (stream = 0; stream < 2; stream++)
+ for (ss = pcm->streams[stream].substream; ss; ss = ss->next) {
+ err = snd_pcm_lib_preallocate_pages(ss, SNDRV_DMA_TYPE_DEV_SG,
+ dev,
+ ss->number ? 0 : 128<<10,
+ 256<<10);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+
+
+/*<--snd_echo_probe() */
+static int __devinit snd_echo_new_pcm(struct echoaudio *chip)
+{
+ struct snd_pcm *pcm;
+ int err;
+
+#ifdef ECHOCARD_HAS_VMIXER
+ /* This card has a Vmixer, that is there is no direct mapping from PCM
+ streams to physical outputs. The user can mix the streams as he wishes
+ via control interface and it's possible to send any stream to any
+ output, thus it makes no sense to keep analog and digital outputs
+ separated */
+
+ /* PCM#0 Virtual outputs and analog inputs */
+ if ((err = snd_pcm_new(chip->card, "PCM", 0, num_pipes_out(chip),
+ num_analog_busses_in(chip), &pcm)) < 0)
+ return err;
+ pcm->private_data = chip;
+ chip->analog_pcm = pcm;
+ strcpy(pcm->name, chip->card->shortname);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &analog_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &analog_capture_ops);
+ if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0)
+ return err;
+ DE_INIT(("Analog PCM ok\n"));
+
+#ifdef ECHOCARD_HAS_DIGITAL_IO
+ /* PCM#1 Digital inputs, no outputs */
+ if ((err = snd_pcm_new(chip->card, "Digital PCM", 1, 0,
+ num_digital_busses_in(chip), &pcm)) < 0)
+ return err;
+ pcm->private_data = chip;
+ chip->digital_pcm = pcm;
+ strcpy(pcm->name, chip->card->shortname);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &digital_capture_ops);
+ if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0)
+ return err;
+ DE_INIT(("Digital PCM ok\n"));
+#endif /* ECHOCARD_HAS_DIGITAL_IO */
+
+#else /* ECHOCARD_HAS_VMIXER */
+
+ /* The card can manage substreams formed by analog and digital channels
+ at the same time, but I prefer to keep analog and digital channels
+ separated, because that mixed thing is confusing and useless. So we
+ register two PCM devices: */
+
+ /* PCM#0 Analog i/o */
+ if ((err = snd_pcm_new(chip->card, "Analog PCM", 0,
+ num_analog_busses_out(chip),
+ num_analog_busses_in(chip), &pcm)) < 0)
+ return err;
+ pcm->private_data = chip;
+ chip->analog_pcm = pcm;
+ strcpy(pcm->name, chip->card->shortname);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &analog_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &analog_capture_ops);
+ if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0)
+ return err;
+ DE_INIT(("Analog PCM ok\n"));
+
+#ifdef ECHOCARD_HAS_DIGITAL_IO
+ /* PCM#1 Digital i/o */
+ if ((err = snd_pcm_new(chip->card, "Digital PCM", 1,
+ num_digital_busses_out(chip),
+ num_digital_busses_in(chip), &pcm)) < 0)
+ return err;
+ pcm->private_data = chip;
+ chip->digital_pcm = pcm;
+ strcpy(pcm->name, chip->card->shortname);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &digital_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &digital_capture_ops);
+ if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0)
+ return err;
+ DE_INIT(("Digital PCM ok\n"));
+#endif /* ECHOCARD_HAS_DIGITAL_IO */
+
+#endif /* ECHOCARD_HAS_VMIXER */
+
+ return 0;
+}
+
+
+
+
+/******************************************************************************
+ Control interface
+******************************************************************************/
+
+/******************* PCM output volume *******************/
+static int snd_echo_output_gain_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct echoaudio *chip;
+
+ chip = snd_kcontrol_chip(kcontrol);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = num_busses_out(chip);
+ uinfo->value.integer.min = ECHOGAIN_MINOUT;
+ uinfo->value.integer.max = ECHOGAIN_MAXOUT;
+ return 0;
+}
+
+static int snd_echo_output_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct echoaudio *chip;
+ int c;
+
+ chip = snd_kcontrol_chip(kcontrol);
+ for (c = 0; c < num_busses_out(chip); c++)
+ ucontrol->value.integer.value[c] = chip->output_gain[c];
+ return 0;
+}
+
+static int snd_echo_output_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct echoaudio *chip;
+ int c, changed, gain;
+
+ changed = 0;
+ chip = snd_kcontrol_chip(kcontrol);
+ spin_lock_irq(&chip->lock);
+ for (c = 0; c < num_busses_out(chip); c++) {
+ gain = ucontrol->value.integer.value[c];
+ /* Ignore out of range values */
+ if (gain < ECHOGAIN_MINOUT || gain > ECHOGAIN_MAXOUT)
+ continue;
+ if (chip->output_gain[c] != gain) {
+ set_output_gain(chip, c, gain);
+ changed = 1;
+ }
+ }
+ if (changed)
+ update_output_line_level(chip);
+ spin_unlock_irq(&chip->lock);
+ return changed;
+}
+
+#ifdef ECHOCARD_HAS_VMIXER
+/* On Vmixer cards this one controls the line-out volume */
+static struct snd_kcontrol_new snd_echo_line_output_gain __devinitdata = {
+ .name = "Line Playback Volume",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = snd_echo_output_gain_info,
+ .get = snd_echo_output_gain_get,
+ .put = snd_echo_output_gain_put,
+};
+#else
+static struct snd_kcontrol_new snd_echo_pcm_output_gain __devinitdata = {
+ .name = "PCM Playback Volume",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = snd_echo_output_gain_info,
+ .get = snd_echo_output_gain_get,
+ .put = snd_echo_output_gain_put,
+};
+#endif
+
+
+
+#ifdef ECHOCARD_HAS_INPUT_GAIN
+
+/******************* Analog input volume *******************/
+static int snd_echo_input_gain_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct echoaudio *chip;
+
+ chip = snd_kcontrol_chip(kcontrol);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = num_analog_busses_in(chip);
+ uinfo->value.integer.min = ECHOGAIN_MININP;
+ uinfo->value.integer.max = ECHOGAIN_MAXINP;
+ return 0;
+}
+
+static int snd_echo_input_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct echoaudio *chip;
+ int c;
+
+ chip = snd_kcontrol_chip(kcontrol);
+ for (c = 0; c < num_analog_busses_in(chip); c++)
+ ucontrol->value.integer.value[c] = chip->input_gain[c];
+ return 0;
+}
+
+static int snd_echo_input_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct echoaudio *chip;
+ int c, gain, changed;
+
+ changed = 0;
+ chip = snd_kcontrol_chip(kcontrol);
+ spin_lock_irq(&chip->lock);
+ for (c = 0; c < num_analog_busses_in(chip); c++) {
+ gain = ucontrol->value.integer.value[c];
+ /* Ignore out of range values */
+ if (gain < ECHOGAIN_MININP || gain > ECHOGAIN_MAXINP)
+ continue;
+ if (chip->input_gain[c] != gain) {
+ set_input_gain(chip, c, gain);
+ changed = 1;
+ }
+ }
+ if (changed)
+ update_input_line_level(chip);
+ spin_unlock_irq(&chip->lock);
+ return changed;
+}
+
+static struct snd_kcontrol_new snd_echo_line_input_gain __devinitdata = {
+ .name = "Line Capture Volume",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = snd_echo_input_gain_info,
+ .get = snd_echo_input_gain_get,
+ .put = snd_echo_input_gain_put,
+};
+
+#endif /* ECHOCARD_HAS_INPUT_GAIN */
+
+
+
+#ifdef ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL
+
+/************ Analog output nominal level (+4dBu / -10dBV) ***************/
+static int snd_echo_output_nominal_info (struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct echoaudio *chip;
+
+ chip = snd_kcontrol_chip(kcontrol);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = num_analog_busses_out(chip);
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_echo_output_nominal_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct echoaudio *chip;
+ int c;
+
+ chip = snd_kcontrol_chip(kcontrol);
+ for (c = 0; c < num_analog_busses_out(chip); c++)
+ ucontrol->value.integer.value[c] = chip->nominal_level[c];
+ return 0;
+}
+
+static int snd_echo_output_nominal_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct echoaudio *chip;
+ int c, changed;
+
+ changed = 0;
+ chip = snd_kcontrol_chip(kcontrol);
+ spin_lock_irq(&chip->lock);
+ for (c = 0; c < num_analog_busses_out(chip); c++) {
+ if (chip->nominal_level[c] != ucontrol->value.integer.value[c]) {
+ set_nominal_level(chip, c,
+ ucontrol->value.integer.value[c]);
+ changed = 1;
+ }
+ }
+ if (changed)
+ update_output_line_level(chip);
+ spin_unlock_irq(&chip->lock);
+ return changed;
+}
+
+static struct snd_kcontrol_new snd_echo_output_nominal_level __devinitdata = {
+ .name = "Line Playback Switch (-10dBV)",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = snd_echo_output_nominal_info,
+ .get = snd_echo_output_nominal_get,
+ .put = snd_echo_output_nominal_put,
+};
+
+#endif /* ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL */
+
+
+
+#ifdef ECHOCARD_HAS_INPUT_NOMINAL_LEVEL
+
+/*************** Analog input nominal level (+4dBu / -10dBV) ***************/
+static int snd_echo_input_nominal_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct echoaudio *chip;
+
+ chip = snd_kcontrol_chip(kcontrol);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = num_analog_busses_in(chip);
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_echo_input_nominal_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct echoaudio *chip;
+ int c;
+
+ chip = snd_kcontrol_chip(kcontrol);
+ for (c = 0; c < num_analog_busses_in(chip); c++)
+ ucontrol->value.integer.value[c] =
+ chip->nominal_level[bx_analog_in(chip) + c];
+ return 0;
+}
+
+static int snd_echo_input_nominal_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct echoaudio *chip;
+ int c, changed;
+
+ changed = 0;
+ chip = snd_kcontrol_chip(kcontrol);
+ spin_lock_irq(&chip->lock);
+ for (c = 0; c < num_analog_busses_in(chip); c++) {
+ if (chip->nominal_level[bx_analog_in(chip) + c] !=
+ ucontrol->value.integer.value[c]) {
+ set_nominal_level(chip, bx_analog_in(chip) + c,
+ ucontrol->value.integer.value[c]);
+ changed = 1;
+ }
+ }
+ if (changed)
+ update_output_line_level(chip); /* "Output" is not a mistake
+ * here.
+ */
+ spin_unlock_irq(&chip->lock);
+ return changed;
+}
+
+static struct snd_kcontrol_new snd_echo_intput_nominal_level __devinitdata = {
+ .name = "Line Capture Switch (-10dBV)",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = snd_echo_input_nominal_info,
+ .get = snd_echo_input_nominal_get,
+ .put = snd_echo_input_nominal_put,
+};
+
+#endif /* ECHOCARD_HAS_INPUT_NOMINAL_LEVEL */
+
+
+
+#ifdef ECHOCARD_HAS_MONITOR
+
+/******************* Monitor mixer *******************/
+static int snd_echo_mixer_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct echoaudio *chip;
+
+ chip = snd_kcontrol_chip(kcontrol);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = ECHOGAIN_MINOUT;
+ uinfo->value.integer.max = ECHOGAIN_MAXOUT;
+ uinfo->dimen.d[0] = num_busses_out(chip);
+ uinfo->dimen.d[1] = num_busses_in(chip);
+ return 0;
+}
+
+static int snd_echo_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct echoaudio *chip;
+
+ chip = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.integer.value[0] =
+ chip->monitor_gain[ucontrol->id.index / num_busses_in(chip)]
+ [ucontrol->id.index % num_busses_in(chip)];
+ return 0;
+}
+
+static int snd_echo_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct echoaudio *chip;
+ int changed, gain;
+ short out, in;
+
+ changed = 0;
+ chip = snd_kcontrol_chip(kcontrol);
+ out = ucontrol->id.index / num_busses_in(chip);
+ in = ucontrol->id.index % num_busses_in(chip);
+ gain = ucontrol->value.integer.value[0];
+ if (gain < ECHOGAIN_MINOUT || gain > ECHOGAIN_MAXOUT)
+ return -EINVAL;
+ if (chip->monitor_gain[out][in] != gain) {
+ spin_lock_irq(&chip->lock);
+ set_monitor_gain(chip, out, in, gain);
+ update_output_line_level(chip);
+ spin_unlock_irq(&chip->lock);
+ changed = 1;
+ }
+ return changed;
+}
+
+static struct snd_kcontrol_new snd_echo_monitor_mixer __devinitdata = {
+ .name = "Monitor Mixer Volume",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = snd_echo_mixer_info,
+ .get = snd_echo_mixer_get,
+ .put = snd_echo_mixer_put,
+};
+
+#endif /* ECHOCARD_HAS_MONITOR */
+
+
+
+#ifdef ECHOCARD_HAS_VMIXER
+
+/******************* Vmixer *******************/
+static int snd_echo_vmixer_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct echoaudio *chip;
+
+ chip = snd_kcontrol_chip(kcontrol);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = ECHOGAIN_MINOUT;
+ uinfo->value.integer.max = ECHOGAIN_MAXOUT;
+ uinfo->dimen.d[0] = num_busses_out(chip);
+ uinfo->dimen.d[1] = num_pipes_out(chip);
+ return 0;
+}
+
+static int snd_echo_vmixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct echoaudio *chip;
+
+ chip = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.integer.value[0] =
+ chip->vmixer_gain[ucontrol->id.index / num_pipes_out(chip)]
+ [ucontrol->id.index % num_pipes_out(chip)];
+ return 0;
+}
+
+static int snd_echo_vmixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct echoaudio *chip;
+ int gain, changed;
+ short vch, out;
+
+ changed = 0;
+ chip = snd_kcontrol_chip(kcontrol);
+ out = ucontrol->id.index / num_pipes_out(chip);
+ vch = ucontrol->id.index % num_pipes_out(chip);
+ gain = ucontrol->value.integer.value[0];
+ if (gain < ECHOGAIN_MINOUT || gain > ECHOGAIN_MAXOUT)
+ return -EINVAL;
+ if (chip->vmixer_gain[out][vch] != ucontrol->value.integer.value[0]) {
+ spin_lock_irq(&chip->lock);
+ set_vmixer_gain(chip, out, vch, ucontrol->value.integer.value[0]);
+ update_vmixer_level(chip);
+ spin_unlock_irq(&chip->lock);
+ changed = 1;
+ }
+ return changed;
+}
+
+static struct snd_kcontrol_new snd_echo_vmixer __devinitdata = {
+ .name = "VMixer Volume",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = snd_echo_vmixer_info,
+ .get = snd_echo_vmixer_get,
+ .put = snd_echo_vmixer_put,
+};
+
+#endif /* ECHOCARD_HAS_VMIXER */
+
+
+
+#ifdef ECHOCARD_HAS_DIGITAL_MODE_SWITCH
+
+/******************* Digital mode switch *******************/
+static int snd_echo_digital_mode_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static char *names[4] = {
+ "S/PDIF Coaxial", "S/PDIF Optical", "ADAT Optical",
+ "S/PDIF Cdrom"
+ };
+ struct echoaudio *chip;
+
+ chip = snd_kcontrol_chip(kcontrol);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->value.enumerated.items = chip->num_digital_modes;
+ uinfo->count = 1;
+ if (uinfo->value.enumerated.item >= chip->num_digital_modes)
+ uinfo->value.enumerated.item = chip->num_digital_modes - 1;
+ strcpy(uinfo->value.enumerated.name, names[
+ chip->digital_mode_list[uinfo->value.enumerated.item]]);
+ return 0;
+}
+
+static int snd_echo_digital_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct echoaudio *chip;
+ int i, mode;
+
+ chip = snd_kcontrol_chip(kcontrol);
+ mode = chip->digital_mode;
+ for (i = chip->num_digital_modes - 1; i >= 0; i--)
+ if (mode == chip->digital_mode_list[i]) {
+ ucontrol->value.enumerated.item[0] = i;
+ break;
+ }
+ return 0;
+}
+
+static int snd_echo_digital_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct echoaudio *chip;
+ int changed;
+ unsigned short emode, dmode;
+
+ changed = 0;
+ chip = snd_kcontrol_chip(kcontrol);
+
+ emode = ucontrol->value.enumerated.item[0];
+ if (emode >= chip->num_digital_modes)
+ return -EINVAL;
+ dmode = chip->digital_mode_list[emode];
+
+ if (dmode != chip->digital_mode) {
+ /* mode_mutex is required to make this operation atomic wrt
+ pcm_digital_*_open() and set_input_clock() functions. */
+ down(&chip->mode_mutex);
+
+ /* Do not allow the user to change the digital mode when a pcm
+ device is open because it also changes the number of channels
+ and the allowed sample rates */
+ if (atomic_read(&chip->opencount)) {
+ changed = -EAGAIN;
+ } else {
+ changed = set_digital_mode(chip, dmode);
+ /* If we had to change the clock source, report it */
+ if (changed > 0 && chip->clock_src_ctl) {
+ snd_ctl_notify(chip->card,
+ SNDRV_CTL_EVENT_MASK_VALUE,
+ &chip->clock_src_ctl->id);
+ DE_ACT(("SDM() =%d\n", changed));
+ }
+ if (changed >= 0)
+ changed = 1; /* No errors */
+ }
+ up(&chip->mode_mutex);
+ }
+ return changed;
+}
+
+static struct snd_kcontrol_new snd_echo_digital_mode_switch __devinitdata = {
+ .name = "Digital mode Switch",
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = snd_echo_digital_mode_info,
+ .get = snd_echo_digital_mode_get,
+ .put = snd_echo_digital_mode_put,
+};
+
+#endif /* ECHOCARD_HAS_DIGITAL_MODE_SWITCH */
+
+
+
+#ifdef ECHOCARD_HAS_DIGITAL_IO
+
+/******************* S/PDIF mode switch *******************/
+static int snd_echo_spdif_mode_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static char *names[2] = {"Consumer", "Professional"};
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->value.enumerated.items = 2;
+ uinfo->count = 1;
+ if (uinfo->value.enumerated.item)
+ uinfo->value.enumerated.item = 1;
+ strcpy(uinfo->value.enumerated.name,
+ names[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_echo_spdif_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct echoaudio *chip;
+
+ chip = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.enumerated.item[0] = !!chip->professional_spdif;
+ return 0;
+}
+
+static int snd_echo_spdif_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct echoaudio *chip;
+ int mode;
+
+ chip = snd_kcontrol_chip(kcontrol);
+ mode = !!ucontrol->value.enumerated.item[0];
+ if (mode != chip->professional_spdif) {
+ spin_lock_irq(&chip->lock);
+ set_professional_spdif(chip, mode);
+ spin_unlock_irq(&chip->lock);
+ return 1;
+ }
+ return 0;
+}
+
+static struct snd_kcontrol_new snd_echo_spdif_mode_switch __devinitdata = {
+ .name = "S/PDIF mode Switch",
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = snd_echo_spdif_mode_info,
+ .get = snd_echo_spdif_mode_get,
+ .put = snd_echo_spdif_mode_put,
+};
+
+#endif /* ECHOCARD_HAS_DIGITAL_IO */
+
+
+
+#ifdef ECHOCARD_HAS_EXTERNAL_CLOCK
+
+/******************* Select input clock source *******************/
+static int snd_echo_clock_source_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static char *names[8] = {
+ "Internal", "Word", "Super", "S/PDIF", "ADAT", "ESync",
+ "ESync96", "MTC"
+ };
+ struct echoaudio *chip;
+
+ chip = snd_kcontrol_chip(kcontrol);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->value.enumerated.items = chip->num_clock_sources;
+ uinfo->count = 1;
+ if (uinfo->value.enumerated.item >= chip->num_clock_sources)
+ uinfo->value.enumerated.item = chip->num_clock_sources - 1;
+ strcpy(uinfo->value.enumerated.name, names[
+ chip->clock_source_list[uinfo->value.enumerated.item]]);
+ return 0;
+}
+
+static int snd_echo_clock_source_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct echoaudio *chip;
+ int i, clock;
+
+ chip = snd_kcontrol_chip(kcontrol);
+ clock = chip->input_clock;
+
+ for (i = 0; i < chip->num_clock_sources; i++)
+ if (clock == chip->clock_source_list[i])
+ ucontrol->value.enumerated.item[0] = i;
+
+ return 0;
+}
+
+static int snd_echo_clock_source_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct echoaudio *chip;
+ int changed;
+ unsigned int eclock, dclock;
+
+ changed = 0;
+ chip = snd_kcontrol_chip(kcontrol);
+ eclock = ucontrol->value.enumerated.item[0];
+ if (eclock >= chip->input_clock_types)
+ return -EINVAL;
+ dclock = chip->clock_source_list[eclock];
+ if (chip->input_clock != dclock) {
+ down(&chip->mode_mutex);
+ spin_lock_irq(&chip->lock);
+ if ((changed = set_input_clock(chip, dclock)) == 0)
+ changed = 1; /* no errors */
+ spin_unlock_irq(&chip->lock);
+ up(&chip->mode_mutex);
+ }
+
+ if (changed < 0)
+ DE_ACT(("seticlk val%d err 0x%x\n", dclock, changed));
+
+ return changed;
+}
+
+static struct snd_kcontrol_new snd_echo_clock_source_switch __devinitdata = {
+ .name = "Sample Clock Source",
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .info = snd_echo_clock_source_info,
+ .get = snd_echo_clock_source_get,
+ .put = snd_echo_clock_source_put,
+};
+
+#endif /* ECHOCARD_HAS_EXTERNAL_CLOCK */
+
+
+
+#ifdef ECHOCARD_HAS_PHANTOM_POWER
+
+/******************* Phantom power switch *******************/
+static int snd_echo_phantom_power_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_echo_phantom_power_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct echoaudio *chip = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = chip->phantom_power;
+ return 0;
+}
+
+static int snd_echo_phantom_power_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct echoaudio *chip = snd_kcontrol_chip(kcontrol);
+ int power, changed = 0;
+
+ power = !!ucontrol->value.integer.value[0];
+ if (chip->phantom_power != power) {
+ spin_lock_irq(&chip->lock);
+ changed = set_phantom_power(chip, power);
+ spin_unlock_irq(&chip->lock);
+ if (changed == 0)
+ changed = 1; /* no errors */
+ }
+ return changed;
+}
+
+static struct snd_kcontrol_new snd_echo_phantom_power_switch __devinitdata = {
+ .name = "Phantom power Switch",
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = snd_echo_phantom_power_info,
+ .get = snd_echo_phantom_power_get,
+ .put = snd_echo_phantom_power_put,
+};
+
+#endif /* ECHOCARD_HAS_PHANTOM_POWER */
+
+
+
+#ifdef ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE
+
+/******************* Digital input automute switch *******************/
+static int snd_echo_automute_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_echo_automute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct echoaudio *chip = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = chip->digital_in_automute;
+ return 0;
+}
+
+static int snd_echo_automute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct echoaudio *chip = snd_kcontrol_chip(kcontrol);
+ int automute, changed = 0;
+
+ automute = !!ucontrol->value.integer.value[0];
+ if (chip->digital_in_automute != automute) {
+ spin_lock_irq(&chip->lock);
+ changed = set_input_auto_mute(chip, automute);
+ spin_unlock_irq(&chip->lock);
+ if (changed == 0)
+ changed = 1; /* no errors */
+ }
+ return changed;
+}
+
+static struct snd_kcontrol_new snd_echo_automute_switch __devinitdata = {
+ .name = "Digital Capture Switch (automute)",
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = snd_echo_automute_info,
+ .get = snd_echo_automute_get,
+ .put = snd_echo_automute_put,
+};
+
+#endif /* ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE */
+
+
+
+/******************* VU-meters switch *******************/
+static int snd_echo_vumeters_switch_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct echoaudio *chip;
+
+ chip = snd_kcontrol_chip(kcontrol);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_echo_vumeters_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct echoaudio *chip;
+
+ chip = snd_kcontrol_chip(kcontrol);
+ spin_lock_irq(&chip->lock);
+ set_meters_on(chip, ucontrol->value.integer.value[0]);
+ spin_unlock_irq(&chip->lock);
+ return 1;
+}
+
+static struct snd_kcontrol_new snd_echo_vumeters_switch __devinitdata = {
+ .name = "VU-meters Switch",
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .access = SNDRV_CTL_ELEM_ACCESS_WRITE,
+ .info = snd_echo_vumeters_switch_info,
+ .put = snd_echo_vumeters_switch_put,
+};
+
+
+
+/***** Read VU-meters (input, output, analog and digital together) *****/
+static int snd_echo_vumeters_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct echoaudio *chip;
+
+ chip = snd_kcontrol_chip(kcontrol);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 96;
+ uinfo->value.integer.min = ECHOGAIN_MINOUT;
+ uinfo->value.integer.max = 0;
+#ifdef ECHOCARD_HAS_VMIXER
+ uinfo->dimen.d[0] = 3; /* Out, In, Virt */
+#else
+ uinfo->dimen.d[0] = 2; /* Out, In */
+#endif
+ uinfo->dimen.d[1] = 16; /* 16 channels */
+ uinfo->dimen.d[2] = 2; /* 0=level, 1=peak */
+ return 0;
+}
+
+static int snd_echo_vumeters_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct echoaudio *chip;
+
+ chip = snd_kcontrol_chip(kcontrol);
+ get_audio_meters(chip, ucontrol->value.integer.value);
+ return 0;
+}
+
+static struct snd_kcontrol_new snd_echo_vumeters __devinitdata = {
+ .name = "VU-meters",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_echo_vumeters_info,
+ .get = snd_echo_vumeters_get,
+};
+
+
+
+/*** Channels info - it exports informations about the number of channels ***/
+static int snd_echo_channels_info_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct echoaudio *chip;
+
+ chip = snd_kcontrol_chip(kcontrol);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 6;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1 << ECHO_CLOCK_NUMBER;
+ return 0;
+}
+
+static int snd_echo_channels_info_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct echoaudio *chip;
+ int detected, clocks, bit, src;
+
+ chip = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.integer.value[0] = num_busses_in(chip);
+ ucontrol->value.integer.value[1] = num_analog_busses_in(chip);
+ ucontrol->value.integer.value[2] = num_busses_out(chip);
+ ucontrol->value.integer.value[3] = num_analog_busses_out(chip);
+ ucontrol->value.integer.value[4] = num_pipes_out(chip);
+
+ /* Compute the bitmask of the currently valid input clocks */
+ detected = detect_input_clocks(chip);
+ clocks = 0;
+ src = chip->num_clock_sources - 1;
+ for (bit = ECHO_CLOCK_NUMBER - 1; bit >= 0; bit--)
+ if (detected & (1 << bit))
+ for (; src >= 0; src--)
+ if (bit == chip->clock_source_list[src]) {
+ clocks |= 1 << src;
+ break;
+ }
+ ucontrol->value.integer.value[5] = clocks;
+
+ return 0;
+}
+
+static struct snd_kcontrol_new snd_echo_channels_info __devinitdata = {
+ .name = "Channels info",
+ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_echo_channels_info_info,
+ .get = snd_echo_channels_info_get,
+};
+
+
+
+
+/******************************************************************************
+ IRQ Handler
+******************************************************************************/
+
+static irqreturn_t snd_echo_interrupt(int irq, void *dev_id,
+ struct pt_regs *regs)
+{
+ struct echoaudio *chip = dev_id;
+ struct snd_pcm_substream *substream;
+ int period, ss, st;
+
+ spin_lock(&chip->lock);
+ st = service_irq(chip);
+ if (st < 0) {
+ spin_unlock(&chip->lock);
+ return IRQ_NONE;
+ }
+ /* The hardware doesn't tell us which substream caused the irq,
+ thus we have to check all running substreams. */
+ for (ss = 0; ss < DSP_MAXPIPES; ss++) {
+ if ((substream = chip->substream[ss])) {
+ period = pcm_pointer(substream) /
+ substream->runtime->period_size;
+ if (period != chip->last_period[ss]) {
+ chip->last_period[ss] = period;
+ spin_unlock(&chip->lock);
+ snd_pcm_period_elapsed(substream);
+ spin_lock(&chip->lock);
+ }
+ }
+ }
+ spin_unlock(&chip->lock);
+
+#ifdef ECHOCARD_HAS_MIDI
+ if (st > 0 && chip->midi_in) {
+ snd_rawmidi_receive(chip->midi_in, chip->midi_buffer, st);
+ DE_MID(("rawmidi_iread=%d\n", st));
+ }
+#endif
+ return IRQ_HANDLED;
+}
+
+
+
+
+/******************************************************************************
+ Module construction / destruction
+******************************************************************************/
+
+static int snd_echo_free(struct echoaudio *chip)
+{
+ DE_INIT(("Stop DSP...\n"));
+ if (chip->comm_page) {
+ rest_in_peace(chip);
+ snd_dma_free_pages(&chip->commpage_dma_buf);
+ }
+ DE_INIT(("Stopped.\n"));
+
+ if (chip->irq >= 0)
+ free_irq(chip->irq, (void *)chip);
+
+ if (chip->dsp_registers)
+ iounmap(chip->dsp_registers);
+
+ if (chip->iores)
+ release_and_free_resource(chip->iores);
+
+ DE_INIT(("MMIO freed.\n"));
+
+ pci_disable_device(chip->pci);
+
+ /* release chip data */
+ kfree(chip);
+ DE_INIT(("Chip freed.\n"));
+ return 0;
+}
+
+
+
+static int snd_echo_dev_free(struct snd_device *device)
+{
+ struct echoaudio *chip = device->device_data;
+
+ DE_INIT(("snd_echo_dev_free()...\n"));
+ return snd_echo_free(chip);
+}
+
+
+
+/* <--snd_echo_probe() */
+static __devinit int snd_echo_create(struct snd_card *card,
+ struct pci_dev *pci,
+ struct echoaudio **rchip)
+{
+ struct echoaudio *chip;
+ int err;
+ size_t sz;
+ static struct snd_device_ops ops = {
+ .dev_free = snd_echo_dev_free,
+ };
+
+ *rchip = NULL;
+
+ pci_write_config_byte(pci, PCI_LATENCY_TIMER, 0xC0);
+
+ if ((err = pci_enable_device(pci)) < 0)
+ return err;
+ pci_set_master(pci);
+
+ /* allocate a chip-specific data */
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (!chip) {
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+ DE_INIT(("chip=%p\n", chip));
+
+ spin_lock_init(&chip->lock);
+ chip->card = card;
+ chip->pci = pci;
+ chip->irq = -1;
+
+ /* PCI resource allocation */
+ chip->dsp_registers_phys = pci_resource_start(pci, 0);
+ sz = pci_resource_len(pci, 0);
+ if (sz > PAGE_SIZE)
+ sz = PAGE_SIZE; /* We map only the required part */
+
+ if ((chip->iores = request_mem_region(chip->dsp_registers_phys, sz,
+ ECHOCARD_NAME)) == NULL) {
+ snd_echo_free(chip);
+ snd_printk(KERN_ERR "cannot get memory region\n");
+ return -EBUSY;
+ }
+ chip->dsp_registers = (volatile u32 __iomem *)
+ ioremap_nocache(chip->dsp_registers_phys, sz);
+
+ if (request_irq(pci->irq, snd_echo_interrupt, IRQF_DISABLED | IRQF_SHARED,
+ ECHOCARD_NAME, (void *)chip)) {
+ snd_echo_free(chip);
+ snd_printk(KERN_ERR "cannot grab irq\n");
+ return -EBUSY;
+ }
+ chip->irq = pci->irq;
+ DE_INIT(("pci=%p irq=%d subdev=%04x Init hardware...\n",
+ chip->pci, chip->irq, chip->pci->subsystem_device));
+
+ /* Create the DSP comm page - this is the area of memory used for most
+ of the communication with the DSP, which accesses it via bus mastering */
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+ sizeof(struct comm_page),
+ &chip->commpage_dma_buf) < 0) {
+ snd_echo_free(chip);
+ snd_printk(KERN_ERR "cannot allocate the comm page\n");
+ return -ENOMEM;
+ }
+ chip->comm_page_phys = chip->commpage_dma_buf.addr;
+ chip->comm_page = (struct comm_page *)chip->commpage_dma_buf.area;
+
+ err = init_hw(chip, chip->pci->device, chip->pci->subsystem_device);
+ if (err) {
+ DE_INIT(("init_hw err=%d\n", err));
+ snd_echo_free(chip);
+ return err;
+ }
+ DE_INIT(("Card init OK\n"));
+
+ if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+ snd_echo_free(chip);
+ return err;
+ }
+ atomic_set(&chip->opencount, 0);
+ init_MUTEX(&chip->mode_mutex);
+ chip->can_set_rate = 1;
+ *rchip = chip;
+ /* Init done ! */
+ return 0;
+}
+
+
+
+/* constructor */
+static int __devinit snd_echo_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ struct snd_card *card;
+ struct echoaudio *chip;
+ char *dsp;
+ int i, err;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ DE_INIT(("Echoaudio driver starting...\n"));
+ i = 0;
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ if (card == NULL)
+ return -ENOMEM;
+
+ if ((err = snd_echo_create(card, pci, &chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ strcpy(card->driver, "Echo_" ECHOCARD_NAME);
+ strcpy(card->shortname, chip->card_name);
+
+ dsp = "56301";
+ if (pci_id->device == 0x3410)
+ dsp = "56361";
+
+ sprintf(card->longname, "%s rev.%d (DSP%s) at 0x%lx irq %i",
+ card->shortname, pci_id->subdevice & 0x000f, dsp,
+ chip->dsp_registers_phys, chip->irq);
+
+ if ((err = snd_echo_new_pcm(chip)) < 0) {
+ snd_printk(KERN_ERR "new pcm error %d\n", err);
+ snd_card_free(card);
+ return err;
+ }
+
+#ifdef ECHOCARD_HAS_MIDI
+ if (chip->has_midi) { /* Some Mia's do not have midi */
+ if ((err = snd_echo_midi_create(card, chip)) < 0) {
+ snd_printk(KERN_ERR "new midi error %d\n", err);
+ snd_card_free(card);
+ return err;
+ }
+ }
+#endif
+
+#ifdef ECHOCARD_HAS_VMIXER
+ snd_echo_vmixer.count = num_pipes_out(chip) * num_busses_out(chip);
+ if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_line_output_gain, chip))) < 0)
+ goto ctl_error;
+ if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vmixer, chip))) < 0)
+ goto ctl_error;
+#else
+ if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_pcm_output_gain, chip))) < 0)
+ goto ctl_error;
+#endif
+
+#ifdef ECHOCARD_HAS_INPUT_GAIN
+ if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_line_input_gain, chip))) < 0)
+ goto ctl_error;
+#endif
+
+#ifdef ECHOCARD_HAS_INPUT_NOMINAL_LEVEL
+ if (!chip->hasnt_input_nominal_level)
+ if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_intput_nominal_level, chip))) < 0)
+ goto ctl_error;
+#endif
+
+#ifdef ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL
+ if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_output_nominal_level, chip))) < 0)
+ goto ctl_error;
+#endif
+
+ if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vumeters_switch, chip))) < 0)
+ goto ctl_error;
+
+ if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vumeters, chip))) < 0)
+ goto ctl_error;
+
+#ifdef ECHOCARD_HAS_MONITOR
+ snd_echo_monitor_mixer.count = num_busses_in(chip) * num_busses_out(chip);
+ if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_monitor_mixer, chip))) < 0)
+ goto ctl_error;
+#endif
+
+#ifdef ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE
+ if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_automute_switch, chip))) < 0)
+ goto ctl_error;
+#endif
+
+ if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_channels_info, chip))) < 0)
+ goto ctl_error;
+
+#ifdef ECHOCARD_HAS_DIGITAL_MODE_SWITCH
+ /* Creates a list of available digital modes */
+ chip->num_digital_modes = 0;
+ for (i = 0; i < 6; i++)
+ if (chip->digital_modes & (1 << i))
+ chip->digital_mode_list[chip->num_digital_modes++] = i;
+
+ if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_digital_mode_switch, chip))) < 0)
+ goto ctl_error;
+#endif /* ECHOCARD_HAS_DIGITAL_MODE_SWITCH */
+
+#ifdef ECHOCARD_HAS_EXTERNAL_CLOCK
+ /* Creates a list of available clock sources */
+ chip->num_clock_sources = 0;
+ for (i = 0; i < 10; i++)
+ if (chip->input_clock_types & (1 << i))
+ chip->clock_source_list[chip->num_clock_sources++] = i;
+
+ if (chip->num_clock_sources > 1) {
+ chip->clock_src_ctl = snd_ctl_new1(&snd_echo_clock_source_switch, chip);
+ if ((err = snd_ctl_add(chip->card, chip->clock_src_ctl)) < 0)
+ goto ctl_error;
+ }
+#endif /* ECHOCARD_HAS_EXTERNAL_CLOCK */
+
+#ifdef ECHOCARD_HAS_DIGITAL_IO
+ if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_spdif_mode_switch, chip))) < 0)
+ goto ctl_error;
+#endif
+
+#ifdef ECHOCARD_HAS_PHANTOM_POWER
+ if (chip->has_phantom_power)
+ if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_phantom_power_switch, chip))) < 0)
+ goto ctl_error;
+#endif
+
+ if ((err = snd_card_register(card)) < 0) {
+ snd_card_free(card);
+ goto ctl_error;
+ }
+ snd_printk(KERN_INFO "Card registered: %s\n", card->longname);
+
+ pci_set_drvdata(pci, chip);
+ dev++;
+ return 0;
+
+ctl_error:
+ snd_printk(KERN_ERR "new control error %d\n", err);
+ snd_card_free(card);
+ return err;
+}
+
+
+
+static void __devexit snd_echo_remove(struct pci_dev *pci)
+{
+ struct echoaudio *chip;
+
+ chip = pci_get_drvdata(pci);
+ if (chip)
+ snd_card_free(chip->card);
+ pci_set_drvdata(pci, NULL);
+}
+
+
+
+/******************************************************************************
+ Everything starts and ends here
+******************************************************************************/
+
+/* pci_driver definition */
+static struct pci_driver driver = {
+ .name = "Echoaudio " ECHOCARD_NAME,
+ .id_table = snd_echo_ids,
+ .probe = snd_echo_probe,
+ .remove = __devexit_p(snd_echo_remove),
+};
+
+
+
+/* initialization of the module */
+static int __init alsa_card_echo_init(void)
+{
+ return pci_register_driver(&driver);
+}
+
+
+
+/* clean up the module */
+static void __exit alsa_card_echo_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+
+module_init(alsa_card_echo_init)
+module_exit(alsa_card_echo_exit)
diff --git a/sound/pci/echoaudio/echoaudio.h b/sound/pci/echoaudio/echoaudio.h
new file mode 100644
index 000000000000..7e88c968e22f
--- /dev/null
+++ b/sound/pci/echoaudio/echoaudio.h
@@ -0,0 +1,590 @@
+/****************************************************************************
+
+ Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+ All rights reserved
+ www.echoaudio.com
+
+ This file is part of Echo Digital Audio's generic driver library.
+
+ Echo Digital Audio's generic driver library is free software;
+ you can redistribute it and/or modify it under the terms of
+ the GNU General Public License as published by the Free Software
+ Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ 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.
+
+ ****************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+ ****************************************************************************
+
+
+ Here's a block diagram of how most of the cards work:
+
+ +-----------+
+ record | |<-------------------- Inputs
+ <-------| | |
+ PCI | Transport | |
+ bus | engine | \|/
+ ------->| | +-------+
+ play | |--->|monitor|-------> Outputs
+ +-----------+ | mixer |
+ +-------+
+
+ The lines going to and from the PCI bus represent "pipes". A pipe performs
+ audio transport - moving audio data to and from buffers on the host via
+ bus mastering.
+
+ The inputs and outputs on the right represent input and output "busses."
+ A bus is a physical, real connection to the outside world. An example
+ of a bus would be the 1/4" analog connectors on the back of Layla or
+ an RCA S/PDIF connector.
+
+ For most cards, there is a one-to-one correspondence between outputs
+ and busses; that is, each individual pipe is hard-wired to a single bus.
+
+ Cards that work this way are Darla20, Gina20, Layla20, Darla24, Gina24,
+ Layla24, Mona, and Indigo.
+
+
+ Mia has a feature called "virtual outputs."
+
+
+ +-----------+
+ record | |<----------------------------- Inputs
+ <-------| | |
+ PCI | Transport | |
+ bus | engine | \|/
+ ------->| | +------+ +-------+
+ play | |-->|vmixer|-->|monitor|-------> Outputs
+ +-----------+ +------+ | mixer |
+ +-------+
+
+
+ Obviously, the difference here is the box labeled "vmixer." Vmixer is
+ short for "virtual output mixer." For Mia, pipes are *not* hard-wired
+ to a single bus; the vmixer lets you mix any pipe to any bus in any
+ combination.
+
+ Note, however, that the left-hand side of the diagram is unchanged.
+ Transport works exactly the same way - the difference is in the mixer stage.
+
+
+ Pipes and busses are numbered starting at zero.
+
+
+
+ Pipe index
+ ==========
+
+ A number of calls in CEchoGals refer to a "pipe index". A pipe index is
+ a unique number for a pipe that unambiguously refers to a playback or record
+ pipe. Pipe indices are numbered starting with analog outputs, followed by
+ digital outputs, then analog inputs, then digital inputs.
+
+ Take Gina24 as an example:
+
+ Pipe index
+
+ 0-7 Analog outputs (0 .. FirstDigitalBusOut-1)
+ 8-15 Digital outputs (FirstDigitalBusOut .. NumBussesOut-1)
+ 16-17 Analog inputs
+ 18-25 Digital inputs
+
+
+ You get the pipe index by calling CEchoGals::OpenAudio; the other transport
+ functions take the pipe index as a parameter. If you need a pipe index for
+ some other reason, use the handy Makepipe_index method.
+
+
+ Some calls take a CChannelMask parameter; CChannelMask is a handy way to
+ group pipe indices.
+
+
+
+ Digital mode switch
+ ===================
+
+ Some cards (right now, Gina24, Layla24, and Mona) have a Digital Mode Switch
+ or DMS. Cards with a DMS can be set to one of three mutually exclusive
+ digital modes: S/PDIF RCA, S/PDIF optical, or ADAT optical.
+
+ This may create some confusion since ADAT optical is 8 channels wide and
+ S/PDIF is only two channels wide. Gina24, Layla24, and Mona handle this
+ by acting as if they always have 8 digital outs and ins. If you are in
+ either S/PDIF mode, the last 6 channels don't do anything - data sent
+ out these channels is thrown away and you will always record zeros.
+
+ Note that with Gina24, Layla24, and Mona, sample rates above 50 kHz are
+ only available if you have the card configured for S/PDIF optical or S/PDIF
+ RCA.
+
+
+
+ Double speed mode
+ =================
+
+ Some of the cards support 88.2 kHz and 96 kHz sampling (Darla24, Gina24,
+ Layla24, Mona, Mia, and Indigo). For these cards, the driver sometimes has
+ to worry about "double speed mode"; double speed mode applies whenever the
+ sampling rate is above 50 kHz.
+
+ For instance, Mona and Layla24 support word clock sync. However, they
+ actually support two different word clock modes - single speed (below
+ 50 kHz) and double speed (above 50 kHz). The hardware detects if a single
+ or double speed word clock signal is present; the generic code uses that
+ information to determine which mode to use.
+
+ The generic code takes care of all this for you.
+*/
+
+
+#ifndef _ECHOAUDIO_H_
+#define _ECHOAUDIO_H_
+
+
+#define TRUE 1
+#define FALSE 0
+
+#include "echoaudio_dsp.h"
+
+
+
+/***********************************************************************
+
+ PCI configuration space
+
+***********************************************************************/
+
+/*
+ * PCI vendor ID and device IDs for the hardware
+ */
+#define VENDOR_ID 0x1057
+#define DEVICE_ID_56301 0x1801
+#define DEVICE_ID_56361 0x3410
+#define SUBVENDOR_ID 0xECC0
+
+
+/*
+ * Valid Echo PCI subsystem card IDs
+ */
+#define DARLA20 0x0010
+#define GINA20 0x0020
+#define LAYLA20 0x0030
+#define DARLA24 0x0040
+#define GINA24 0x0050
+#define LAYLA24 0x0060
+#define MONA 0x0070
+#define MIA 0x0080
+#define INDIGO 0x0090
+#define INDIGO_IO 0x00a0
+#define INDIGO_DJ 0x00b0
+#define ECHO3G 0x0100
+
+
+/************************************************************************
+
+ Array sizes and so forth
+
+***********************************************************************/
+
+/*
+ * Sizes
+ */
+#define ECHO_MAXAUDIOINPUTS 32 /* Max audio input channels */
+#define ECHO_MAXAUDIOOUTPUTS 32 /* Max audio output channels */
+#define ECHO_MAXAUDIOPIPES 32 /* Max number of input and output
+ * pipes */
+#define E3G_MAX_OUTPUTS 16
+#define ECHO_MAXMIDIJACKS 1 /* Max MIDI ports */
+#define ECHO_MIDI_QUEUE_SZ 512 /* Max MIDI input queue entries */
+#define ECHO_MTC_QUEUE_SZ 32 /* Max MIDI time code input queue
+ * entries */
+
+/*
+ * MIDI activity indicator timeout
+ */
+#define MIDI_ACTIVITY_TIMEOUT_USEC 200000
+
+
+/****************************************************************************
+
+ Clocks
+
+*****************************************************************************/
+
+/*
+ * Clock numbers
+ */
+#define ECHO_CLOCK_INTERNAL 0
+#define ECHO_CLOCK_WORD 1
+#define ECHO_CLOCK_SUPER 2
+#define ECHO_CLOCK_SPDIF 3
+#define ECHO_CLOCK_ADAT 4
+#define ECHO_CLOCK_ESYNC 5
+#define ECHO_CLOCK_ESYNC96 6
+#define ECHO_CLOCK_MTC 7
+#define ECHO_CLOCK_NUMBER 8
+#define ECHO_CLOCKS 0xffff
+
+/*
+ * Clock bit numbers - used to report capabilities and whatever clocks
+ * are being detected dynamically.
+ */
+#define ECHO_CLOCK_BIT_INTERNAL (1 << ECHO_CLOCK_INTERNAL)
+#define ECHO_CLOCK_BIT_WORD (1 << ECHO_CLOCK_WORD)
+#define ECHO_CLOCK_BIT_SUPER (1 << ECHO_CLOCK_SUPER)
+#define ECHO_CLOCK_BIT_SPDIF (1 << ECHO_CLOCK_SPDIF)
+#define ECHO_CLOCK_BIT_ADAT (1 << ECHO_CLOCK_ADAT)
+#define ECHO_CLOCK_BIT_ESYNC (1 << ECHO_CLOCK_ESYNC)
+#define ECHO_CLOCK_BIT_ESYNC96 (1 << ECHO_CLOCK_ESYNC96)
+#define ECHO_CLOCK_BIT_MTC (1<<ECHO_CLOCK_MTC)
+
+
+/***************************************************************************
+
+ Digital modes
+
+****************************************************************************/
+
+/*
+ * Digital modes for Mona, Layla24, and Gina24
+ */
+#define DIGITAL_MODE_NONE 0xFF
+#define DIGITAL_MODE_SPDIF_RCA 0
+#define DIGITAL_MODE_SPDIF_OPTICAL 1
+#define DIGITAL_MODE_ADAT 2
+#define DIGITAL_MODE_SPDIF_CDROM 3
+#define DIGITAL_MODES 4
+
+/*
+ * Digital mode capability masks
+ */
+#define ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA (1 << DIGITAL_MODE_SPDIF_RCA)
+#define ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL (1 << DIGITAL_MODE_SPDIF_OPTICAL)
+#define ECHOCAPS_HAS_DIGITAL_MODE_ADAT (1 << DIGITAL_MODE_ADAT)
+#define ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_CDROM (1 << DIGITAL_MODE_SPDIF_CDROM)
+
+
+#define EXT_3GBOX_NC 0x01 /* 3G box not connected */
+#define EXT_3GBOX_NOT_SET 0x02 /* 3G box not detected yet */
+
+
+#define ECHOGAIN_MUTED (-128) /* Minimum possible gain */
+#define ECHOGAIN_MINOUT (-128) /* Min output gain (dB) */
+#define ECHOGAIN_MAXOUT (6) /* Max output gain (dB) */
+#define ECHOGAIN_MININP (-50) /* Min input gain (0.5 dB) */
+#define ECHOGAIN_MAXINP (50) /* Max input gain (0.5 dB) */
+
+#define PIPE_STATE_STOPPED 0 /* Pipe has been reset */
+#define PIPE_STATE_PAUSED 1 /* Pipe has been stopped */
+#define PIPE_STATE_STARTED 2 /* Pipe has been started */
+#define PIPE_STATE_PENDING 3 /* Pipe has pending start */
+
+
+/* Debug initialization */
+#ifdef CONFIG_SND_DEBUG
+#define DE_INIT(x) snd_printk x
+#else
+#define DE_INIT(x)
+#endif
+
+/* Debug hw_params callbacks */
+#ifdef CONFIG_SND_DEBUG
+#define DE_HWP(x) snd_printk x
+#else
+#define DE_HWP(x)
+#endif
+
+/* Debug normal activity (open, start, stop...) */
+#ifdef CONFIG_SND_DEBUG
+#define DE_ACT(x) snd_printk x
+#else
+#define DE_ACT(x)
+#endif
+
+/* Debug midi activity */
+#ifdef CONFIG_SND_DEBUG
+#define DE_MID(x) snd_printk x
+#else
+#define DE_MID(x)
+#endif
+
+
+struct audiopipe {
+ volatile u32 *dma_counter; /* Commpage register that contains
+ * the current dma position
+ * (lower 32 bits only)
+ */
+ u32 last_counter; /* The last position, which is used
+ * to compute...
+ */
+ u32 position; /* ...the number of bytes tranferred
+ * by the DMA engine, modulo the
+ * buffer size
+ */
+ short index; /* Index of the first channel or <0
+ * if hw is not configured yet
+ */
+ short interleave;
+ struct snd_dma_buffer sgpage; /* Room for the scatter-gather list */
+ struct snd_pcm_hardware hw;
+ struct snd_pcm_hw_constraint_list constr;
+ short sglist_head;
+ char state; /* pipe state */
+};
+
+
+struct audioformat {
+ u8 interleave; /* How the data is arranged in memory:
+ * mono = 1, stereo = 2, ...
+ */
+ u8 bits_per_sample; /* 8, 16, 24, 32 (24 bits left aligned) */
+ char mono_to_stereo; /* Only used if interleave is 1 and
+ * if this is an output pipe.
+ */
+ char data_are_bigendian; /* 1 = big endian, 0 = little endian */
+};
+
+
+struct echoaudio {
+ spinlock_t lock;
+ struct snd_pcm_substream *substream[DSP_MAXPIPES];
+ int last_period[DSP_MAXPIPES];
+ struct semaphore mode_mutex;
+ u16 num_digital_modes, digital_mode_list[6];
+ u16 num_clock_sources, clock_source_list[10];
+ atomic_t opencount;
+ struct snd_kcontrol *clock_src_ctl;
+ struct snd_pcm *analog_pcm, *digital_pcm;
+ struct snd_card *card;
+ const char *card_name;
+ struct pci_dev *pci;
+ unsigned long dsp_registers_phys;
+ struct resource *iores;
+ struct snd_dma_buffer commpage_dma_buf;
+ int irq;
+#ifdef ECHOCARD_HAS_MIDI
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_substream *midi_in, *midi_out;
+#endif
+ struct timer_list timer;
+ char tinuse; /* Timer in use */
+ char midi_full; /* MIDI output buffer is full */
+ char can_set_rate;
+ char rate_set;
+
+ /* This stuff is used mainly by the lowlevel code */
+ struct comm_page *comm_page; /* Virtual address of the memory
+ * seen by DSP
+ */
+ u32 pipe_alloc_mask; /* Bitmask of allocated pipes */
+ u32 pipe_cyclic_mask; /* Bitmask of pipes with cyclic
+ * buffers
+ */
+ u32 sample_rate; /* Card sample rate in Hz */
+ u8 digital_mode; /* Current digital mode
+ * (see DIGITAL_MODE_*)
+ */
+ u8 spdif_status; /* Gina20, Darla20, Darla24 - only */
+ u8 clock_state; /* Gina20, Darla20, Darla24 - only */
+ u8 input_clock; /* Currently selected sample clock
+ * source
+ */
+ u8 output_clock; /* Layla20 only */
+ char meters_enabled; /* VU-meters status */
+ char asic_loaded; /* Set TRUE when ASIC loaded */
+ char bad_board; /* Set TRUE if DSP won't load */
+ char professional_spdif; /* 0 = consumer; 1 = professional */
+ char non_audio_spdif; /* 3G - only */
+ char digital_in_automute; /* Gina24, Layla24, Mona - only */
+ char has_phantom_power;
+ char hasnt_input_nominal_level; /* Gina3G */
+ char phantom_power; /* Gina3G - only */
+ char has_midi;
+ char midi_input_enabled;
+
+#ifdef ECHOCARD_ECHO3G
+ /* External module -dependent pipe and bus indexes */
+ char px_digital_out, px_analog_in, px_digital_in, px_num;
+ char bx_digital_out, bx_analog_in, bx_digital_in, bx_num;
+#endif
+
+ char nominal_level[ECHO_MAXAUDIOPIPES]; /* True == -10dBV
+ * False == +4dBu */
+ s8 input_gain[ECHO_MAXAUDIOINPUTS]; /* Input level -50..+50
+ * unit is 0.5dB */
+ s8 output_gain[ECHO_MAXAUDIOOUTPUTS]; /* Output level -128..+6 dB
+ * (-128=muted) */
+ s8 monitor_gain[ECHO_MAXAUDIOOUTPUTS][ECHO_MAXAUDIOINPUTS];
+ /* -128..+6 dB */
+ s8 vmixer_gain[ECHO_MAXAUDIOOUTPUTS][ECHO_MAXAUDIOOUTPUTS];
+ /* -128..+6 dB */
+
+ u16 digital_modes; /* Bitmask of supported modes
+ * (see ECHOCAPS_HAS_DIGITAL_MODE_*) */
+ u16 input_clock_types; /* Suppoted input clock types */
+ u16 output_clock_types; /* Suppoted output clock types -
+ * Layla20 only */
+ u16 device_id, subdevice_id;
+ u16 *dsp_code; /* Current DSP code loaded,
+ * NULL if nothing loaded */
+ const struct firmware *dsp_code_to_load;/* DSP code to load */
+ const struct firmware *asic_code; /* Current ASIC code */
+ u32 comm_page_phys; /* Physical address of the
+ * memory seen by DSP */
+ volatile u32 __iomem *dsp_registers; /* DSP's register base */
+ u32 active_mask; /* Chs. active mask or
+ * punks out */
+
+#ifdef ECHOCARD_HAS_MIDI
+ u16 mtc_state; /* State for MIDI input parsing state machine */
+ u8 midi_buffer[MIDI_IN_BUFFER_SIZE];
+#endif
+};
+
+
+static int init_dsp_comm_page(struct echoaudio *chip);
+static int init_line_levels(struct echoaudio *chip);
+static int free_pipes(struct echoaudio *chip, struct audiopipe *pipe);
+static int load_firmware(struct echoaudio *chip);
+static int wait_handshake(struct echoaudio *chip);
+static int send_vector(struct echoaudio *chip, u32 command);
+static int get_firmware(const struct firmware **fw_entry,
+ const struct firmware *frm, struct echoaudio *chip);
+static void free_firmware(const struct firmware *fw_entry);
+
+#ifdef ECHOCARD_HAS_MIDI
+static int enable_midi_input(struct echoaudio *chip, char enable);
+static int midi_service_irq(struct echoaudio *chip);
+static int __devinit snd_echo_midi_create(struct snd_card *card,
+ struct echoaudio *chip);
+#endif
+
+
+static inline void clear_handshake(struct echoaudio *chip)
+{
+ chip->comm_page->handshake = 0;
+}
+
+static inline u32 get_dsp_register(struct echoaudio *chip, u32 index)
+{
+ return readl(&chip->dsp_registers[index]);
+}
+
+static inline void set_dsp_register(struct echoaudio *chip, u32 index,
+ u32 value)
+{
+ writel(value, &chip->dsp_registers[index]);
+}
+
+
+/* Pipe and bus indexes. PX_* and BX_* are defined as chip->px_* and chip->bx_*
+for 3G cards because they depend on the external box. They are integer
+constants for all other cards.
+Never use those defines directly, use the following functions instead. */
+
+static inline int px_digital_out(const struct echoaudio *chip)
+{
+ return PX_DIGITAL_OUT;
+}
+
+static inline int px_analog_in(const struct echoaudio *chip)
+{
+ return PX_ANALOG_IN;
+}
+
+static inline int px_digital_in(const struct echoaudio *chip)
+{
+ return PX_DIGITAL_IN;
+}
+
+static inline int px_num(const struct echoaudio *chip)
+{
+ return PX_NUM;
+}
+
+static inline int bx_digital_out(const struct echoaudio *chip)
+{
+ return BX_DIGITAL_OUT;
+}
+
+static inline int bx_analog_in(const struct echoaudio *chip)
+{
+ return BX_ANALOG_IN;
+}
+
+static inline int bx_digital_in(const struct echoaudio *chip)
+{
+ return BX_DIGITAL_IN;
+}
+
+static inline int bx_num(const struct echoaudio *chip)
+{
+ return BX_NUM;
+}
+
+static inline int num_pipes_out(const struct echoaudio *chip)
+{
+ return px_analog_in(chip);
+}
+
+static inline int num_pipes_in(const struct echoaudio *chip)
+{
+ return px_num(chip) - px_analog_in(chip);
+}
+
+static inline int num_busses_out(const struct echoaudio *chip)
+{
+ return bx_analog_in(chip);
+}
+
+static inline int num_busses_in(const struct echoaudio *chip)
+{
+ return bx_num(chip) - bx_analog_in(chip);
+}
+
+static inline int num_analog_busses_out(const struct echoaudio *chip)
+{
+ return bx_digital_out(chip);
+}
+
+static inline int num_analog_busses_in(const struct echoaudio *chip)
+{
+ return bx_digital_in(chip) - bx_analog_in(chip);
+}
+
+static inline int num_digital_busses_out(const struct echoaudio *chip)
+{
+ return num_busses_out(chip) - num_analog_busses_out(chip);
+}
+
+static inline int num_digital_busses_in(const struct echoaudio *chip)
+{
+ return num_busses_in(chip) - num_analog_busses_in(chip);
+}
+
+/* The monitor array is a one-dimensional array; compute the offset
+ * into the array */
+static inline int monitor_index(const struct echoaudio *chip, int out, int in)
+{
+ return out * num_busses_in(chip) + in;
+}
+
+
+#ifndef pci_device
+#define pci_device(chip) (&chip->pci->dev)
+#endif
+
+
+#endif /* _ECHOAUDIO_H_ */
diff --git a/sound/pci/echoaudio/echoaudio_3g.c b/sound/pci/echoaudio/echoaudio_3g.c
new file mode 100644
index 000000000000..9f439ea459f4
--- /dev/null
+++ b/sound/pci/echoaudio/echoaudio_3g.c
@@ -0,0 +1,431 @@
+/****************************************************************************
+
+ Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+ All rights reserved
+ www.echoaudio.com
+
+ This file is part of Echo Digital Audio's generic driver library.
+
+ Echo Digital Audio's generic driver library is free software;
+ you can redistribute it and/or modify it under the terms of
+ the GNU General Public License as published by the Free Software
+ Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ 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.
+
+ *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+
+/* These functions are common for all "3G" cards */
+
+
+static int check_asic_status(struct echoaudio *chip)
+{
+ u32 box_status;
+
+ if (wait_handshake(chip))
+ return -EIO;
+
+ chip->comm_page->ext_box_status =
+ __constant_cpu_to_le32(E3G_ASIC_NOT_LOADED);
+ chip->asic_loaded = FALSE;
+ clear_handshake(chip);
+ send_vector(chip, DSP_VC_TEST_ASIC);
+
+ if (wait_handshake(chip)) {
+ chip->dsp_code = NULL;
+ return -EIO;
+ }
+
+ box_status = le32_to_cpu(chip->comm_page->ext_box_status);
+ DE_INIT(("box_status=%x\n", box_status));
+ if (box_status == E3G_ASIC_NOT_LOADED)
+ return -ENODEV;
+
+ chip->asic_loaded = TRUE;
+ return box_status & E3G_BOX_TYPE_MASK;
+}
+
+
+
+static inline u32 get_frq_reg(struct echoaudio *chip)
+{
+ return le32_to_cpu(chip->comm_page->e3g_frq_register);
+}
+
+
+
+/* Most configuration of 3G cards is accomplished by writing the control
+register. write_control_reg sends the new control register value to the DSP. */
+static int write_control_reg(struct echoaudio *chip, u32 ctl, u32 frq,
+ char force)
+{
+ if (wait_handshake(chip))
+ return -EIO;
+
+ DE_ACT(("WriteControlReg: Setting 0x%x, 0x%x\n", ctl, frq));
+
+ ctl = cpu_to_le32(ctl);
+ frq = cpu_to_le32(frq);
+
+ if (ctl != chip->comm_page->control_register ||
+ frq != chip->comm_page->e3g_frq_register || force) {
+ chip->comm_page->e3g_frq_register = frq;
+ chip->comm_page->control_register = ctl;
+ clear_handshake(chip);
+ return send_vector(chip, DSP_VC_WRITE_CONTROL_REG);
+ }
+
+ DE_ACT(("WriteControlReg: not written, no change\n"));
+ return 0;
+}
+
+
+
+/* Set the digital mode - currently for Gina24, Layla24, Mona, 3G */
+static int set_digital_mode(struct echoaudio *chip, u8 mode)
+{
+ u8 previous_mode;
+ int err, i, o;
+
+ /* All audio channels must be closed before changing the digital mode */
+ snd_assert(!chip->pipe_alloc_mask, return -EAGAIN);
+
+ snd_assert(chip->digital_modes & (1 << mode), return -EINVAL);
+
+ previous_mode = chip->digital_mode;
+ err = dsp_set_digital_mode(chip, mode);
+
+ /* If we successfully changed the digital mode from or to ADAT,
+ * then make sure all output, input and monitor levels are
+ * updated by the DSP comm object. */
+ if (err >= 0 && previous_mode != mode &&
+ (previous_mode == DIGITAL_MODE_ADAT || mode == DIGITAL_MODE_ADAT)) {
+ spin_lock_irq(&chip->lock);
+ for (o = 0; o < num_busses_out(chip); o++)
+ for (i = 0; i < num_busses_in(chip); i++)
+ set_monitor_gain(chip, o, i,
+ chip->monitor_gain[o][i]);
+
+#ifdef ECHOCARD_HAS_INPUT_GAIN
+ for (i = 0; i < num_busses_in(chip); i++)
+ set_input_gain(chip, i, chip->input_gain[i]);
+ update_input_line_level(chip);
+#endif
+
+ for (o = 0; o < num_busses_out(chip); o++)
+ set_output_gain(chip, o, chip->output_gain[o]);
+ update_output_line_level(chip);
+ spin_unlock_irq(&chip->lock);
+ }
+
+ return err;
+}
+
+
+
+static u32 set_spdif_bits(struct echoaudio *chip, u32 control_reg, u32 rate)
+{
+ control_reg &= E3G_SPDIF_FORMAT_CLEAR_MASK;
+
+ switch (rate) {
+ case 32000 :
+ control_reg |= E3G_SPDIF_SAMPLE_RATE0 | E3G_SPDIF_SAMPLE_RATE1;
+ break;
+ case 44100 :
+ if (chip->professional_spdif)
+ control_reg |= E3G_SPDIF_SAMPLE_RATE0;
+ break;
+ case 48000 :
+ control_reg |= E3G_SPDIF_SAMPLE_RATE1;
+ break;
+ }
+
+ if (chip->professional_spdif)
+ control_reg |= E3G_SPDIF_PRO_MODE;
+
+ if (chip->non_audio_spdif)
+ control_reg |= E3G_SPDIF_NOT_AUDIO;
+
+ control_reg |= E3G_SPDIF_24_BIT | E3G_SPDIF_TWO_CHANNEL |
+ E3G_SPDIF_COPY_PERMIT;
+
+ return control_reg;
+}
+
+
+
+/* Set the S/PDIF output format */
+static int set_professional_spdif(struct echoaudio *chip, char prof)
+{
+ u32 control_reg;
+
+ control_reg = le32_to_cpu(chip->comm_page->control_register);
+ chip->professional_spdif = prof;
+ control_reg = set_spdif_bits(chip, control_reg, chip->sample_rate);
+ return write_control_reg(chip, control_reg, get_frq_reg(chip), 0);
+}
+
+
+
+/* detect_input_clocks() returns a bitmask consisting of all the input clocks
+currently connected to the hardware; this changes as the user connects and
+disconnects clock inputs. You should use this information to determine which
+clocks the user is allowed to select. */
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+ u32 clocks_from_dsp, clock_bits;
+
+ /* Map the DSP clock detect bits to the generic driver clock
+ * detect bits */
+ clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks);
+
+ clock_bits = ECHO_CLOCK_BIT_INTERNAL;
+
+ if (clocks_from_dsp & E3G_CLOCK_DETECT_BIT_WORD)
+ clock_bits |= ECHO_CLOCK_BIT_WORD;
+
+ switch(chip->digital_mode) {
+ case DIGITAL_MODE_SPDIF_RCA:
+ case DIGITAL_MODE_SPDIF_OPTICAL:
+ if (clocks_from_dsp & E3G_CLOCK_DETECT_BIT_SPDIF)
+ clock_bits |= ECHO_CLOCK_BIT_SPDIF;
+ break;
+ case DIGITAL_MODE_ADAT:
+ if (clocks_from_dsp & E3G_CLOCK_DETECT_BIT_ADAT)
+ clock_bits |= ECHO_CLOCK_BIT_ADAT;
+ break;
+ }
+
+ return clock_bits;
+}
+
+
+
+static int load_asic(struct echoaudio *chip)
+{
+ int box_type, err;
+
+ if (chip->asic_loaded)
+ return 0;
+
+ /* Give the DSP a few milliseconds to settle down */
+ mdelay(2);
+
+ err = load_asic_generic(chip, DSP_FNC_LOAD_3G_ASIC,
+ &card_fw[FW_3G_ASIC]);
+ if (err < 0)
+ return err;
+
+ chip->asic_code = &card_fw[FW_3G_ASIC];
+
+ /* Now give the new ASIC a little time to set up */
+ mdelay(2);
+ /* See if it worked */
+ box_type = check_asic_status(chip);
+
+ /* Set up the control register if the load succeeded -
+ * 48 kHz, internal clock, S/PDIF RCA mode */
+ if (box_type >= 0) {
+ err = write_control_reg(chip, E3G_48KHZ,
+ E3G_FREQ_REG_DEFAULT, TRUE);
+ if (err < 0)
+ return err;
+ }
+
+ return box_type;
+}
+
+
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+ u32 control_reg, clock, base_rate, frq_reg;
+
+ /* Only set the clock for internal mode. */
+ if (chip->input_clock != ECHO_CLOCK_INTERNAL) {
+ DE_ACT(("set_sample_rate: Cannot set sample rate - "
+ "clock not set to CLK_CLOCKININTERNAL\n"));
+ /* Save the rate anyhow */
+ chip->comm_page->sample_rate = cpu_to_le32(rate);
+ chip->sample_rate = rate;
+ set_input_clock(chip, chip->input_clock);
+ return 0;
+ }
+
+ snd_assert(rate < 50000 || chip->digital_mode != DIGITAL_MODE_ADAT,
+ return -EINVAL);
+
+ clock = 0;
+ control_reg = le32_to_cpu(chip->comm_page->control_register);
+ control_reg &= E3G_CLOCK_CLEAR_MASK;
+
+ switch (rate) {
+ case 96000:
+ clock = E3G_96KHZ;
+ break;
+ case 88200:
+ clock = E3G_88KHZ;
+ break;
+ case 48000:
+ clock = E3G_48KHZ;
+ break;
+ case 44100:
+ clock = E3G_44KHZ;
+ break;
+ case 32000:
+ clock = E3G_32KHZ;
+ break;
+ default:
+ clock = E3G_CONTINUOUS_CLOCK;
+ if (rate > 50000)
+ clock |= E3G_DOUBLE_SPEED_MODE;
+ break;
+ }
+
+ control_reg |= clock;
+ control_reg = set_spdif_bits(chip, control_reg, rate);
+
+ base_rate = rate;
+ if (base_rate > 50000)
+ base_rate /= 2;
+ if (base_rate < 32000)
+ base_rate = 32000;
+
+ frq_reg = E3G_MAGIC_NUMBER / base_rate - 2;
+ if (frq_reg > E3G_FREQ_REG_MAX)
+ frq_reg = E3G_FREQ_REG_MAX;
+
+ chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP */
+ chip->sample_rate = rate;
+ DE_ACT(("SetSampleRate: %d clock %x\n", rate, control_reg));
+
+ /* Tell the DSP about it - DSP reads both control reg & freq reg */
+ return write_control_reg(chip, control_reg, frq_reg, 0);
+}
+
+
+
+/* Set the sample clock source to internal, S/PDIF, ADAT */
+static int set_input_clock(struct echoaudio *chip, u16 clock)
+{
+ u32 control_reg, clocks_from_dsp;
+
+ DE_ACT(("set_input_clock:\n"));
+
+ /* Mask off the clock select bits */
+ control_reg = le32_to_cpu(chip->comm_page->control_register) &
+ E3G_CLOCK_CLEAR_MASK;
+ clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks);
+
+ switch (clock) {
+ case ECHO_CLOCK_INTERNAL:
+ DE_ACT(("Set Echo3G clock to INTERNAL\n"));
+ chip->input_clock = ECHO_CLOCK_INTERNAL;
+ return set_sample_rate(chip, chip->sample_rate);
+ case ECHO_CLOCK_SPDIF:
+ if (chip->digital_mode == DIGITAL_MODE_ADAT)
+ return -EAGAIN;
+ DE_ACT(("Set Echo3G clock to SPDIF\n"));
+ control_reg |= E3G_SPDIF_CLOCK;
+ if (clocks_from_dsp & E3G_CLOCK_DETECT_BIT_SPDIF96)
+ control_reg |= E3G_DOUBLE_SPEED_MODE;
+ else
+ control_reg &= ~E3G_DOUBLE_SPEED_MODE;
+ break;
+ case ECHO_CLOCK_ADAT:
+ if (chip->digital_mode != DIGITAL_MODE_ADAT)
+ return -EAGAIN;
+ DE_ACT(("Set Echo3G clock to ADAT\n"));
+ control_reg |= E3G_ADAT_CLOCK;
+ control_reg &= ~E3G_DOUBLE_SPEED_MODE;
+ break;
+ case ECHO_CLOCK_WORD:
+ DE_ACT(("Set Echo3G clock to WORD\n"));
+ control_reg |= E3G_WORD_CLOCK;
+ if (clocks_from_dsp & E3G_CLOCK_DETECT_BIT_WORD96)
+ control_reg |= E3G_DOUBLE_SPEED_MODE;
+ else
+ control_reg &= ~E3G_DOUBLE_SPEED_MODE;
+ break;
+ default:
+ DE_ACT(("Input clock 0x%x not supported for Echo3G\n", clock));
+ return -EINVAL;
+ }
+
+ chip->input_clock = clock;
+ return write_control_reg(chip, control_reg, get_frq_reg(chip), 1);
+}
+
+
+
+static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
+{
+ u32 control_reg;
+ int err, incompatible_clock;
+
+ /* Set clock to "internal" if it's not compatible with the new mode */
+ incompatible_clock = FALSE;
+ switch (mode) {
+ case DIGITAL_MODE_SPDIF_OPTICAL:
+ case DIGITAL_MODE_SPDIF_RCA:
+ if (chip->input_clock == ECHO_CLOCK_ADAT)
+ incompatible_clock = TRUE;
+ break;
+ case DIGITAL_MODE_ADAT:
+ if (chip->input_clock == ECHO_CLOCK_SPDIF)
+ incompatible_clock = TRUE;
+ break;
+ default:
+ DE_ACT(("Digital mode not supported: %d\n", mode));
+ return -EINVAL;
+ }
+
+ spin_lock_irq(&chip->lock);
+
+ if (incompatible_clock) {
+ chip->sample_rate = 48000;
+ set_input_clock(chip, ECHO_CLOCK_INTERNAL);
+ }
+
+ /* Clear the current digital mode */
+ control_reg = le32_to_cpu(chip->comm_page->control_register);
+ control_reg &= E3G_DIGITAL_MODE_CLEAR_MASK;
+
+ /* Tweak the control reg */
+ switch (mode) {
+ case DIGITAL_MODE_SPDIF_OPTICAL:
+ control_reg |= E3G_SPDIF_OPTICAL_MODE;
+ break;
+ case DIGITAL_MODE_SPDIF_RCA:
+ /* E3G_SPDIF_OPTICAL_MODE bit cleared */
+ break;
+ case DIGITAL_MODE_ADAT:
+ control_reg |= E3G_ADAT_MODE;
+ control_reg &= ~E3G_DOUBLE_SPEED_MODE; /* @@ useless */
+ break;
+ }
+
+ err = write_control_reg(chip, control_reg, get_frq_reg(chip), 1);
+ spin_unlock_irq(&chip->lock);
+ if (err < 0)
+ return err;
+ chip->digital_mode = mode;
+
+ DE_ACT(("set_digital_mode(%d)\n", chip->digital_mode));
+ return incompatible_clock;
+}
diff --git a/sound/pci/echoaudio/echoaudio_dsp.c b/sound/pci/echoaudio/echoaudio_dsp.c
new file mode 100644
index 000000000000..42afa837d9b4
--- /dev/null
+++ b/sound/pci/echoaudio/echoaudio_dsp.c
@@ -0,0 +1,1125 @@
+/****************************************************************************
+
+ Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+ All rights reserved
+ www.echoaudio.com
+
+ This file is part of Echo Digital Audio's generic driver library.
+
+ Echo Digital Audio's generic driver library is free software;
+ you can redistribute it and/or modify it under the terms of
+ the GNU General Public License as published by the Free Software
+ Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ 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.
+
+ *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+#if PAGE_SIZE < 4096
+#error PAGE_SIZE is < 4k
+#endif
+
+static int restore_dsp_rettings(struct echoaudio *chip);
+
+
+/* Some vector commands involve the DSP reading or writing data to and from the
+comm page; if you send one of these commands to the DSP, it will complete the
+command and then write a non-zero value to the Handshake field in the
+comm page. This function waits for the handshake to show up. */
+static int wait_handshake(struct echoaudio *chip)
+{
+ int i;
+
+ /* Wait up to 10ms for the handshake from the DSP */
+ for (i = 0; i < HANDSHAKE_TIMEOUT; i++) {
+ /* Look for the handshake value */
+ if (chip->comm_page->handshake) {
+ /*if (i) DE_ACT(("Handshake time: %d\n", i));*/
+ return 0;
+ }
+ udelay(1);
+ }
+
+ snd_printk(KERN_ERR "wait_handshake(): Timeout waiting for DSP\n");
+ return -EBUSY;
+}
+
+
+
+/* Much of the interaction between the DSP and the driver is done via vector
+commands; send_vector writes a vector command to the DSP. Typically, this
+causes the DSP to read or write fields in the comm page.
+PCI posting is not required thanks to the handshake logic. */
+static int send_vector(struct echoaudio *chip, u32 command)
+{
+ int i;
+
+ wmb(); /* Flush all pending writes before sending the command */
+
+ /* Wait up to 100ms for the "vector busy" bit to be off */
+ for (i = 0; i < VECTOR_BUSY_TIMEOUT; i++) {
+ if (!(get_dsp_register(chip, CHI32_VECTOR_REG) &
+ CHI32_VECTOR_BUSY)) {
+ set_dsp_register(chip, CHI32_VECTOR_REG, command);
+ /*if (i) DE_ACT(("send_vector time: %d\n", i));*/
+ return 0;
+ }
+ udelay(1);
+ }
+
+ DE_ACT((KERN_ERR "timeout on send_vector\n"));
+ return -EBUSY;
+}
+
+
+
+/* write_dsp writes a 32-bit value to the DSP; this is used almost
+exclusively for loading the DSP. */
+static int write_dsp(struct echoaudio *chip, u32 data)
+{
+ u32 status, i;
+
+ for (i = 0; i < 10000000; i++) { /* timeout = 10s */
+ status = get_dsp_register(chip, CHI32_STATUS_REG);
+ if ((status & CHI32_STATUS_HOST_WRITE_EMPTY) != 0) {
+ set_dsp_register(chip, CHI32_DATA_REG, data);
+ wmb(); /* write it immediately */
+ return 0;
+ }
+ udelay(1);
+ cond_resched();
+ }
+
+ chip->bad_board = TRUE; /* Set TRUE until DSP re-loaded */
+ DE_ACT((KERN_ERR "write_dsp: Set bad_board to TRUE\n"));
+ return -EIO;
+}
+
+
+
+/* read_dsp reads a 32-bit value from the DSP; this is used almost
+exclusively for loading the DSP and checking the status of the ASIC. */
+static int read_dsp(struct echoaudio *chip, u32 *data)
+{
+ u32 status, i;
+
+ for (i = 0; i < READ_DSP_TIMEOUT; i++) {
+ status = get_dsp_register(chip, CHI32_STATUS_REG);
+ if ((status & CHI32_STATUS_HOST_READ_FULL) != 0) {
+ *data = get_dsp_register(chip, CHI32_DATA_REG);
+ return 0;
+ }
+ udelay(1);
+ cond_resched();
+ }
+
+ chip->bad_board = TRUE; /* Set TRUE until DSP re-loaded */
+ DE_INIT((KERN_ERR "read_dsp: Set bad_board to TRUE\n"));
+ return -EIO;
+}
+
+
+
+/****************************************************************************
+ Firmware loading functions
+ ****************************************************************************/
+
+/* This function is used to read back the serial number from the DSP;
+this is triggered by the SET_COMMPAGE_ADDR command.
+Only some early Echogals products have serial numbers in the ROM;
+the serial number is not used, but you still need to do this as
+part of the DSP load process. */
+static int read_sn(struct echoaudio *chip)
+{
+ int i;
+ u32 sn[6];
+
+ for (i = 0; i < 5; i++) {
+ if (read_dsp(chip, &sn[i])) {
+ snd_printk(KERN_ERR "Failed to read serial number\n");
+ return -EIO;
+ }
+ }
+ DE_INIT(("Read serial number %08x %08x %08x %08x %08x\n",
+ sn[0], sn[1], sn[2], sn[3], sn[4]));
+ return 0;
+}
+
+
+
+#ifndef ECHOCARD_HAS_ASIC
+/* This card has no ASIC, just return ok */
+static inline int check_asic_status(struct echoaudio *chip)
+{
+ chip->asic_loaded = TRUE;
+ return 0;
+}
+
+#endif /* !ECHOCARD_HAS_ASIC */
+
+
+
+#ifdef ECHOCARD_HAS_ASIC
+
+/* Load ASIC code - done after the DSP is loaded */
+static int load_asic_generic(struct echoaudio *chip, u32 cmd,
+ const struct firmware *asic)
+{
+ const struct firmware *fw;
+ int err;
+ u32 i, size;
+ u8 *code;
+
+ if ((err = get_firmware(&fw, asic, chip)) < 0) {
+ snd_printk(KERN_WARNING "Firmware not found !\n");
+ return err;
+ }
+
+ code = (u8 *)fw->data;
+ size = fw->size;
+
+ /* Send the "Here comes the ASIC" command */
+ if (write_dsp(chip, cmd) < 0)
+ goto la_error;
+
+ /* Write length of ASIC file in bytes */
+ if (write_dsp(chip, size) < 0)
+ goto la_error;
+
+ for (i = 0; i < size; i++) {
+ if (write_dsp(chip, code[i]) < 0)
+ goto la_error;
+ }
+
+ DE_INIT(("ASIC loaded\n"));
+ free_firmware(fw);
+ return 0;
+
+la_error:
+ DE_INIT(("failed on write_dsp\n"));
+ free_firmware(fw);
+ return -EIO;
+}
+
+#endif /* ECHOCARD_HAS_ASIC */
+
+
+
+#ifdef DSP_56361
+
+/* Install the resident loader for 56361 DSPs; The resident loader is on
+the EPROM on the board for 56301 DSP. The resident loader is a tiny little
+program that is used to load the real DSP code. */
+static int install_resident_loader(struct echoaudio *chip)
+{
+ u32 address;
+ int index, words, i;
+ u16 *code;
+ u32 status;
+ const struct firmware *fw;
+
+ /* 56361 cards only! This check is required by the old 56301-based
+ Mona and Gina24 */
+ if (chip->device_id != DEVICE_ID_56361)
+ return 0;
+
+ /* Look to see if the resident loader is present. If the resident
+ loader is already installed, host flag 5 will be on. */
+ status = get_dsp_register(chip, CHI32_STATUS_REG);
+ if (status & CHI32_STATUS_REG_HF5) {
+ DE_INIT(("Resident loader already installed; status is 0x%x\n",
+ status));
+ return 0;
+ }
+
+ if ((i = get_firmware(&fw, &card_fw[FW_361_LOADER], chip)) < 0) {
+ snd_printk(KERN_WARNING "Firmware not found !\n");
+ return i;
+ }
+
+ /* The DSP code is an array of 16 bit words. The array is divided up
+ into sections. The first word of each section is the size in words,
+ followed by the section type.
+ Since DSP addresses and data are 24 bits wide, they each take up two
+ 16 bit words in the array.
+ This is a lot like the other loader loop, but it's not a loop, you
+ don't write the memory type, and you don't write a zero at the end. */
+
+ /* Set DSP format bits for 24 bit mode */
+ set_dsp_register(chip, CHI32_CONTROL_REG,
+ get_dsp_register(chip, CHI32_CONTROL_REG) | 0x900);
+
+ code = (u16 *)fw->data;
+
+ /* Skip the header section; the first word in the array is the size
+ of the first section, so the first real section of code is pointed
+ to by Code[0]. */
+ index = code[0];
+
+ /* Skip the section size, LRS block type, and DSP memory type */
+ index += 3;
+
+ /* Get the number of DSP words to write */
+ words = code[index++];
+
+ /* Get the DSP address for this block; 24 bits, so build from two words */
+ address = ((u32)code[index] << 16) + code[index + 1];
+ index += 2;
+
+ /* Write the count to the DSP */
+ if (write_dsp(chip, words)) {
+ DE_INIT(("install_resident_loader: Failed to write word count!\n"));
+ goto irl_error;
+ }
+ /* Write the DSP address */
+ if (write_dsp(chip, address)) {
+ DE_INIT(("install_resident_loader: Failed to write DSP address!\n"));
+ goto irl_error;
+ }
+ /* Write out this block of code to the DSP */
+ for (i = 0; i < words; i++) {
+ u32 data;
+
+ data = ((u32)code[index] << 16) + code[index + 1];
+ if (write_dsp(chip, data)) {
+ DE_INIT(("install_resident_loader: Failed to write DSP code\n"));
+ goto irl_error;
+ }
+ index += 2;
+ }
+
+ /* Wait for flag 5 to come up */
+ for (i = 0; i < 200; i++) { /* Timeout is 50us * 200 = 10ms */
+ udelay(50);
+ status = get_dsp_register(chip, CHI32_STATUS_REG);
+ if (status & CHI32_STATUS_REG_HF5)
+ break;
+ }
+
+ if (i == 200) {
+ DE_INIT(("Resident loader failed to set HF5\n"));
+ goto irl_error;
+ }
+
+ DE_INIT(("Resident loader successfully installed\n"));
+ free_firmware(fw);
+ return 0;
+
+irl_error:
+ free_firmware(fw);
+ return -EIO;
+}
+
+#endif /* DSP_56361 */
+
+
+static int load_dsp(struct echoaudio *chip, u16 *code)
+{
+ u32 address, data;
+ int index, words, i;
+
+ if (chip->dsp_code == code) {
+ DE_INIT(("DSP is already loaded!\n"));
+ return 0;
+ }
+ chip->bad_board = TRUE; /* Set TRUE until DSP loaded */
+ chip->dsp_code = NULL; /* Current DSP code not loaded */
+ chip->asic_loaded = FALSE; /* Loading the DSP code will reset the ASIC */
+
+ DE_INIT(("load_dsp: Set bad_board to TRUE\n"));
+
+ /* If this board requires a resident loader, install it. */
+#ifdef DSP_56361
+ if ((i = install_resident_loader(chip)) < 0)
+ return i;
+#endif
+
+ /* Send software reset command */
+ if (send_vector(chip, DSP_VC_RESET) < 0) {
+ DE_INIT(("LoadDsp: send_vector DSP_VC_RESET failed, Critical Failure\n"));
+ return -EIO;
+ }
+ /* Delay 10us */
+ udelay(10);
+
+ /* Wait 10ms for HF3 to indicate that software reset is complete */
+ for (i = 0; i < 1000; i++) { /* Timeout is 10us * 1000 = 10ms */
+ if (get_dsp_register(chip, CHI32_STATUS_REG) &
+ CHI32_STATUS_REG_HF3)
+ break;
+ udelay(10);
+ }
+
+ if (i == 1000) {
+ DE_INIT(("load_dsp: Timeout waiting for CHI32_STATUS_REG_HF3\n"));
+ return -EIO;
+ }
+
+ /* Set DSP format bits for 24 bit mode now that soft reset is done */
+ set_dsp_register(chip, CHI32_CONTROL_REG,
+ get_dsp_register(chip, CHI32_CONTROL_REG) | 0x900);
+
+ /* Main loader loop */
+
+ index = code[0];
+ for (;;) {
+ int block_type, mem_type;
+
+ /* Total Block Size */
+ index++;
+
+ /* Block Type */
+ block_type = code[index];
+ if (block_type == 4) /* We're finished */
+ break;
+
+ index++;
+
+ /* Memory Type P=0,X=1,Y=2 */
+ mem_type = code[index++];
+
+ /* Block Code Size */
+ words = code[index++];
+ if (words == 0) /* We're finished */
+ break;
+
+ /* Start Address */
+ address = ((u32)code[index] << 16) + code[index + 1];
+ index += 2;
+
+ if (write_dsp(chip, words) < 0) {
+ DE_INIT(("load_dsp: failed to write number of DSP words\n"));
+ return -EIO;
+ }
+ if (write_dsp(chip, address) < 0) {
+ DE_INIT(("load_dsp: failed to write DSP address\n"));
+ return -EIO;
+ }
+ if (write_dsp(chip, mem_type) < 0) {
+ DE_INIT(("load_dsp: failed to write DSP memory type\n"));
+ return -EIO;
+ }
+ /* Code */
+ for (i = 0; i < words; i++, index+=2) {
+ data = ((u32)code[index] << 16) + code[index + 1];
+ if (write_dsp(chip, data) < 0) {
+ DE_INIT(("load_dsp: failed to write DSP data\n"));
+ return -EIO;
+ }
+ }
+ }
+
+ if (write_dsp(chip, 0) < 0) { /* We're done!!! */
+ DE_INIT(("load_dsp: Failed to write final zero\n"));
+ return -EIO;
+ }
+ udelay(10);
+
+ for (i = 0; i < 5000; i++) { /* Timeout is 100us * 5000 = 500ms */
+ /* Wait for flag 4 - indicates that the DSP loaded OK */
+ if (get_dsp_register(chip, CHI32_STATUS_REG) &
+ CHI32_STATUS_REG_HF4) {
+ set_dsp_register(chip, CHI32_CONTROL_REG,
+ get_dsp_register(chip, CHI32_CONTROL_REG) & ~0x1b00);
+
+ if (write_dsp(chip, DSP_FNC_SET_COMMPAGE_ADDR) < 0) {
+ DE_INIT(("load_dsp: Failed to write DSP_FNC_SET_COMMPAGE_ADDR\n"));
+ return -EIO;
+ }
+
+ if (write_dsp(chip, chip->comm_page_phys) < 0) {
+ DE_INIT(("load_dsp: Failed to write comm page address\n"));
+ return -EIO;
+ }
+
+ /* Get the serial number via slave mode.
+ This is triggered by the SET_COMMPAGE_ADDR command.
+ We don't actually use the serial number but we have to
+ get it as part of the DSP init voodoo. */
+ if (read_sn(chip) < 0) {
+ DE_INIT(("load_dsp: Failed to read serial number\n"));
+ return -EIO;
+ }
+
+ chip->dsp_code = code; /* Show which DSP code loaded */
+ chip->bad_board = FALSE; /* DSP OK */
+ DE_INIT(("load_dsp: OK!\n"));
+ return 0;
+ }
+ udelay(100);
+ }
+
+ DE_INIT(("load_dsp: DSP load timed out waiting for HF4\n"));
+ return -EIO;
+}
+
+
+
+/* load_firmware takes care of loading the DSP and any ASIC code. */
+static int load_firmware(struct echoaudio *chip)
+{
+ const struct firmware *fw;
+ int box_type, err;
+
+ snd_assert(chip->dsp_code_to_load && chip->comm_page, return -EPERM);
+
+ /* See if the ASIC is present and working - only if the DSP is already loaded */
+ if (chip->dsp_code) {
+ if ((box_type = check_asic_status(chip)) >= 0)
+ return box_type;
+ /* ASIC check failed; force the DSP to reload */
+ chip->dsp_code = NULL;
+ }
+
+ if ((err = get_firmware(&fw, chip->dsp_code_to_load, chip)) < 0)
+ return err;
+ err = load_dsp(chip, (u16 *)fw->data);
+ free_firmware(fw);
+ if (err < 0)
+ return err;
+
+ if ((box_type = load_asic(chip)) < 0)
+ return box_type; /* error */
+
+ if ((err = restore_dsp_rettings(chip)) < 0)
+ return err;
+
+ return box_type;
+}
+
+
+
+/****************************************************************************
+ Mixer functions
+ ****************************************************************************/
+
+#if defined(ECHOCARD_HAS_INPUT_NOMINAL_LEVEL) || \
+ defined(ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL)
+
+/* Set the nominal level for an input or output bus (true = -10dBV, false = +4dBu) */
+static int set_nominal_level(struct echoaudio *chip, u16 index, char consumer)
+{
+ snd_assert(index < num_busses_out(chip) + num_busses_in(chip),
+ return -EINVAL);
+
+ /* Wait for the handshake (OK even if ASIC is not loaded) */
+ if (wait_handshake(chip))
+ return -EIO;
+
+ chip->nominal_level[index] = consumer;
+
+ if (consumer)
+ chip->comm_page->nominal_level_mask |= cpu_to_le32(1 << index);
+ else
+ chip->comm_page->nominal_level_mask &= ~cpu_to_le32(1 << index);
+
+ return 0;
+}
+
+#endif /* ECHOCARD_HAS_*_NOMINAL_LEVEL */
+
+
+
+/* Set the gain for a single physical output channel (dB). */
+static int set_output_gain(struct echoaudio *chip, u16 channel, s8 gain)
+{
+ snd_assert(channel < num_busses_out(chip), return -EINVAL);
+
+ if (wait_handshake(chip))
+ return -EIO;
+
+ /* Save the new value */
+ chip->output_gain[channel] = gain;
+ chip->comm_page->line_out_level[channel] = gain;
+ return 0;
+}
+
+
+
+#ifdef ECHOCARD_HAS_MONITOR
+/* Set the monitor level from an input bus to an output bus. */
+static int set_monitor_gain(struct echoaudio *chip, u16 output, u16 input,
+ s8 gain)
+{
+ snd_assert(output < num_busses_out(chip) &&
+ input < num_busses_in(chip), return -EINVAL);
+
+ if (wait_handshake(chip))
+ return -EIO;
+
+ chip->monitor_gain[output][input] = gain;
+ chip->comm_page->monitors[monitor_index(chip, output, input)] = gain;
+ return 0;
+}
+#endif /* ECHOCARD_HAS_MONITOR */
+
+
+/* Tell the DSP to read and update output, nominal & monitor levels in comm page. */
+static int update_output_line_level(struct echoaudio *chip)
+{
+ if (wait_handshake(chip))
+ return -EIO;
+ clear_handshake(chip);
+ return send_vector(chip, DSP_VC_UPDATE_OUTVOL);
+}
+
+
+
+/* Tell the DSP to read and update input levels in comm page */
+static int update_input_line_level(struct echoaudio *chip)
+{
+ if (wait_handshake(chip))
+ return -EIO;
+ clear_handshake(chip);
+ return send_vector(chip, DSP_VC_UPDATE_INGAIN);
+}
+
+
+
+/* set_meters_on turns the meters on or off. If meters are turned on, the DSP
+will write the meter and clock detect values to the comm page at about 30Hz */
+static void set_meters_on(struct echoaudio *chip, char on)
+{
+ if (on && !chip->meters_enabled) {
+ send_vector(chip, DSP_VC_METERS_ON);
+ chip->meters_enabled = 1;
+ } else if (!on && chip->meters_enabled) {
+ send_vector(chip, DSP_VC_METERS_OFF);
+ chip->meters_enabled = 0;
+ memset((s8 *)chip->comm_page->vu_meter, ECHOGAIN_MUTED,
+ DSP_MAXPIPES);
+ memset((s8 *)chip->comm_page->peak_meter, ECHOGAIN_MUTED,
+ DSP_MAXPIPES);
+ }
+}
+
+
+
+/* Fill out an the given array using the current values in the comm page.
+Meters are written in the comm page by the DSP in this order:
+ Output busses
+ Input busses
+ Output pipes (vmixer cards only)
+
+This function assumes there are no more than 16 in/out busses or pipes
+Meters is an array [3][16][2] of long. */
+static void get_audio_meters(struct echoaudio *chip, long *meters)
+{
+ int i, m, n;
+
+ m = 0;
+ n = 0;
+ for (i = 0; i < num_busses_out(chip); i++, m++) {
+ meters[n++] = chip->comm_page->vu_meter[m];
+ meters[n++] = chip->comm_page->peak_meter[m];
+ }
+ for (; n < 32; n++)
+ meters[n] = 0;
+
+#ifdef ECHOCARD_ECHO3G
+ m = E3G_MAX_OUTPUTS; /* Skip unused meters */
+#endif
+
+ for (i = 0; i < num_busses_in(chip); i++, m++) {
+ meters[n++] = chip->comm_page->vu_meter[m];
+ meters[n++] = chip->comm_page->peak_meter[m];
+ }
+ for (; n < 64; n++)
+ meters[n] = 0;
+
+#ifdef ECHOCARD_HAS_VMIXER
+ for (i = 0; i < num_pipes_out(chip); i++, m++) {
+ meters[n++] = chip->comm_page->vu_meter[m];
+ meters[n++] = chip->comm_page->peak_meter[m];
+ }
+#endif
+ for (; n < 96; n++)
+ meters[n] = 0;
+}
+
+
+
+static int restore_dsp_rettings(struct echoaudio *chip)
+{
+ int err;
+ DE_INIT(("restore_dsp_settings\n"));
+
+ if ((err = check_asic_status(chip)) < 0)
+ return err;
+
+ /* @ Gina20/Darla20 only. Should be harmless for other cards. */
+ chip->comm_page->gd_clock_state = GD_CLOCK_UNDEF;
+ chip->comm_page->gd_spdif_status = GD_SPDIF_STATUS_UNDEF;
+ chip->comm_page->handshake = 0xffffffff;
+
+ if ((err = set_sample_rate(chip, chip->sample_rate)) < 0)
+ return err;
+
+ if (chip->meters_enabled)
+ if (send_vector(chip, DSP_VC_METERS_ON) < 0)
+ return -EIO;
+
+#ifdef ECHOCARD_HAS_EXTERNAL_CLOCK
+ if (set_input_clock(chip, chip->input_clock) < 0)
+ return -EIO;
+#endif
+
+#ifdef ECHOCARD_HAS_OUTPUT_CLOCK_SWITCH
+ if (set_output_clock(chip, chip->output_clock) < 0)
+ return -EIO;
+#endif
+
+ if (update_output_line_level(chip) < 0)
+ return -EIO;
+
+ if (update_input_line_level(chip) < 0)
+ return -EIO;
+
+#ifdef ECHOCARD_HAS_VMIXER
+ if (update_vmixer_level(chip) < 0)
+ return -EIO;
+#endif
+
+ if (wait_handshake(chip) < 0)
+ return -EIO;
+ clear_handshake(chip);
+
+ DE_INIT(("restore_dsp_rettings done\n"));
+ return send_vector(chip, DSP_VC_UPDATE_FLAGS);
+}
+
+
+
+/****************************************************************************
+ Transport functions
+ ****************************************************************************/
+
+/* set_audio_format() sets the format of the audio data in host memory for
+this pipe. Note that _MS_ (mono-to-stereo) playback modes are not used by ALSA
+but they are here because they are just mono while capturing */
+static void set_audio_format(struct echoaudio *chip, u16 pipe_index,
+ const struct audioformat *format)
+{
+ u16 dsp_format;
+
+ dsp_format = DSP_AUDIOFORM_SS_16LE;
+
+ /* Look for super-interleave (no big-endian and 8 bits) */
+ if (format->interleave > 2) {
+ switch (format->bits_per_sample) {
+ case 16:
+ dsp_format = DSP_AUDIOFORM_SUPER_INTERLEAVE_16LE;
+ break;
+ case 24:
+ dsp_format = DSP_AUDIOFORM_SUPER_INTERLEAVE_24LE;
+ break;
+ case 32:
+ dsp_format = DSP_AUDIOFORM_SUPER_INTERLEAVE_32LE;
+ break;
+ }
+ dsp_format |= format->interleave;
+ } else if (format->data_are_bigendian) {
+ /* For big-endian data, only 32 bit samples are supported */
+ switch (format->interleave) {
+ case 1:
+ dsp_format = DSP_AUDIOFORM_MM_32BE;
+ break;
+#ifdef ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+ case 2:
+ dsp_format = DSP_AUDIOFORM_SS_32BE;
+ break;
+#endif
+ }
+ } else if (format->interleave == 1 &&
+ format->bits_per_sample == 32 && !format->mono_to_stereo) {
+ /* 32 bit little-endian mono->mono case */
+ dsp_format = DSP_AUDIOFORM_MM_32LE;
+ } else {
+ /* Handle the other little-endian formats */
+ switch (format->bits_per_sample) {
+ case 8:
+ if (format->interleave == 2)
+ dsp_format = DSP_AUDIOFORM_SS_8;
+ else
+ dsp_format = DSP_AUDIOFORM_MS_8;
+ break;
+ default:
+ case 16:
+ if (format->interleave == 2)
+ dsp_format = DSP_AUDIOFORM_SS_16LE;
+ else
+ dsp_format = DSP_AUDIOFORM_MS_16LE;
+ break;
+ case 24:
+ if (format->interleave == 2)
+ dsp_format = DSP_AUDIOFORM_SS_24LE;
+ else
+ dsp_format = DSP_AUDIOFORM_MS_24LE;
+ break;
+ case 32:
+ if (format->interleave == 2)
+ dsp_format = DSP_AUDIOFORM_SS_32LE;
+ else
+ dsp_format = DSP_AUDIOFORM_MS_32LE;
+ break;
+ }
+ }
+ DE_ACT(("set_audio_format[%d] = %x\n", pipe_index, dsp_format));
+ chip->comm_page->audio_format[pipe_index] = cpu_to_le16(dsp_format);
+}
+
+
+
+/* start_transport starts transport for a set of pipes.
+The bits 1 in channel_mask specify what pipes to start. Only the bit of the
+first channel must be set, regardless its interleave.
+Same thing for pause_ and stop_ -trasport below. */
+static int start_transport(struct echoaudio *chip, u32 channel_mask,
+ u32 cyclic_mask)
+{
+ DE_ACT(("start_transport %x\n", channel_mask));
+
+ if (wait_handshake(chip))
+ return -EIO;
+
+ chip->comm_page->cmd_start |= cpu_to_le32(channel_mask);
+
+ if (chip->comm_page->cmd_start) {
+ clear_handshake(chip);
+ send_vector(chip, DSP_VC_START_TRANSFER);
+ if (wait_handshake(chip))
+ return -EIO;
+ /* Keep track of which pipes are transporting */
+ chip->active_mask |= channel_mask;
+ chip->comm_page->cmd_start = 0;
+ return 0;
+ }
+
+ DE_ACT(("start_transport: No pipes to start!\n"));
+ return -EINVAL;
+}
+
+
+
+static int pause_transport(struct echoaudio *chip, u32 channel_mask)
+{
+ DE_ACT(("pause_transport %x\n", channel_mask));
+
+ if (wait_handshake(chip))
+ return -EIO;
+
+ chip->comm_page->cmd_stop |= cpu_to_le32(channel_mask);
+ chip->comm_page->cmd_reset = 0;
+ if (chip->comm_page->cmd_stop) {
+ clear_handshake(chip);
+ send_vector(chip, DSP_VC_STOP_TRANSFER);
+ if (wait_handshake(chip))
+ return -EIO;
+ /* Keep track of which pipes are transporting */
+ chip->active_mask &= ~channel_mask;
+ chip->comm_page->cmd_stop = 0;
+ chip->comm_page->cmd_reset = 0;
+ return 0;
+ }
+
+ DE_ACT(("pause_transport: No pipes to stop!\n"));
+ return 0;
+}
+
+
+
+static int stop_transport(struct echoaudio *chip, u32 channel_mask)
+{
+ DE_ACT(("stop_transport %x\n", channel_mask));
+
+ if (wait_handshake(chip))
+ return -EIO;
+
+ chip->comm_page->cmd_stop |= cpu_to_le32(channel_mask);
+ chip->comm_page->cmd_reset |= cpu_to_le32(channel_mask);
+ if (chip->comm_page->cmd_reset) {
+ clear_handshake(chip);
+ send_vector(chip, DSP_VC_STOP_TRANSFER);
+ if (wait_handshake(chip))
+ return -EIO;
+ /* Keep track of which pipes are transporting */
+ chip->active_mask &= ~channel_mask;
+ chip->comm_page->cmd_stop = 0;
+ chip->comm_page->cmd_reset = 0;
+ return 0;
+ }
+
+ DE_ACT(("stop_transport: No pipes to stop!\n"));
+ return 0;
+}
+
+
+
+static inline int is_pipe_allocated(struct echoaudio *chip, u16 pipe_index)
+{
+ return (chip->pipe_alloc_mask & (1 << pipe_index));
+}
+
+
+
+/* Stops everything and turns off the DSP. All pipes should be already
+stopped and unallocated. */
+static int rest_in_peace(struct echoaudio *chip)
+{
+ DE_ACT(("rest_in_peace() open=%x\n", chip->pipe_alloc_mask));
+
+ /* Stops all active pipes (just to be sure) */
+ stop_transport(chip, chip->active_mask);
+
+ set_meters_on(chip, FALSE);
+
+#ifdef ECHOCARD_HAS_MIDI
+ enable_midi_input(chip, FALSE);
+#endif
+
+ /* Go to sleep */
+ if (chip->dsp_code) {
+ /* Make load_firmware do a complete reload */
+ chip->dsp_code = NULL;
+ /* Put the DSP to sleep */
+ return send_vector(chip, DSP_VC_GO_COMATOSE);
+ }
+ return 0;
+}
+
+
+
+/* Fills the comm page with default values */
+static int init_dsp_comm_page(struct echoaudio *chip)
+{
+ /* Check if the compiler added extra padding inside the structure */
+ if (offsetof(struct comm_page, midi_output) != 0xbe0) {
+ DE_INIT(("init_dsp_comm_page() - Invalid struct comm_page structure\n"));
+ return -EPERM;
+ }
+
+ /* Init all the basic stuff */
+ chip->card_name = ECHOCARD_NAME;
+ chip->bad_board = TRUE; /* Set TRUE until DSP loaded */
+ chip->dsp_code = NULL; /* Current DSP code not loaded */
+ chip->digital_mode = DIGITAL_MODE_NONE;
+ chip->input_clock = ECHO_CLOCK_INTERNAL;
+ chip->output_clock = ECHO_CLOCK_WORD;
+ chip->asic_loaded = FALSE;
+ memset(chip->comm_page, 0, sizeof(struct comm_page));
+
+ /* Init the comm page */
+ chip->comm_page->comm_size =
+ __constant_cpu_to_le32(sizeof(struct comm_page));
+ chip->comm_page->handshake = 0xffffffff;
+ chip->comm_page->midi_out_free_count =
+ __constant_cpu_to_le32(DSP_MIDI_OUT_FIFO_SIZE);
+ chip->comm_page->sample_rate = __constant_cpu_to_le32(44100);
+ chip->sample_rate = 44100;
+
+ /* Set line levels so we don't blast any inputs on startup */
+ memset(chip->comm_page->monitors, ECHOGAIN_MUTED, MONITOR_ARRAY_SIZE);
+ memset(chip->comm_page->vmixer, ECHOGAIN_MUTED, VMIXER_ARRAY_SIZE);
+
+ return 0;
+}
+
+
+
+/* This function initializes the several volume controls for busses and pipes.
+This MUST be called after the DSP is up and running ! */
+static int init_line_levels(struct echoaudio *chip)
+{
+ int st, i, o;
+
+ DE_INIT(("init_line_levels\n"));
+
+ /* Mute output busses */
+ for (i = 0; i < num_busses_out(chip); i++)
+ if ((st = set_output_gain(chip, i, ECHOGAIN_MUTED)))
+ return st;
+ if ((st = update_output_line_level(chip)))
+ return st;
+
+#ifdef ECHOCARD_HAS_VMIXER
+ /* Mute the Vmixer */
+ for (i = 0; i < num_pipes_out(chip); i++)
+ for (o = 0; o < num_busses_out(chip); o++)
+ if ((st = set_vmixer_gain(chip, o, i, ECHOGAIN_MUTED)))
+ return st;
+ if ((st = update_vmixer_level(chip)))
+ return st;
+#endif /* ECHOCARD_HAS_VMIXER */
+
+#ifdef ECHOCARD_HAS_MONITOR
+ /* Mute the monitor mixer */
+ for (o = 0; o < num_busses_out(chip); o++)
+ for (i = 0; i < num_busses_in(chip); i++)
+ if ((st = set_monitor_gain(chip, o, i, ECHOGAIN_MUTED)))
+ return st;
+ if ((st = update_output_line_level(chip)))
+ return st;
+#endif /* ECHOCARD_HAS_MONITOR */
+
+#ifdef ECHOCARD_HAS_INPUT_GAIN
+ for (i = 0; i < num_busses_in(chip); i++)
+ if ((st = set_input_gain(chip, i, ECHOGAIN_MUTED)))
+ return st;
+ if ((st = update_input_line_level(chip)))
+ return st;
+#endif /* ECHOCARD_HAS_INPUT_GAIN */
+
+ return 0;
+}
+
+
+
+/* This is low level part of the interrupt handler.
+It returns -1 if the IRQ is not ours, or N>=0 if it is, where N is the number
+of midi data in the input queue. */
+static int service_irq(struct echoaudio *chip)
+{
+ int st;
+
+ /* Read the DSP status register and see if this DSP generated this interrupt */
+ if (get_dsp_register(chip, CHI32_STATUS_REG) & CHI32_STATUS_IRQ) {
+ st = 0;
+#ifdef ECHOCARD_HAS_MIDI
+ /* Get and parse midi data if present */
+ if (chip->comm_page->midi_input[0]) /* The count is at index 0 */
+ st = midi_service_irq(chip); /* Returns how many midi bytes we received */
+#endif
+ /* Clear the hardware interrupt */
+ chip->comm_page->midi_input[0] = 0;
+ send_vector(chip, DSP_VC_ACK_INT);
+ return st;
+ }
+ return -1;
+}
+
+
+
+
+/******************************************************************************
+ Functions for opening and closing pipes
+ ******************************************************************************/
+
+/* allocate_pipes is used to reserve audio pipes for your exclusive use.
+The call will fail if some pipes are already allocated. */
+static int allocate_pipes(struct echoaudio *chip, struct audiopipe *pipe,
+ int pipe_index, int interleave)
+{
+ int i;
+ u32 channel_mask;
+ char is_cyclic;
+
+ DE_ACT(("allocate_pipes: ch=%d int=%d\n", pipe_index, interleave));
+
+ if (chip->bad_board)
+ return -EIO;
+
+ is_cyclic = 1; /* This driver uses cyclic buffers only */
+
+ for (channel_mask = i = 0; i < interleave; i++)
+ channel_mask |= 1 << (pipe_index + i);
+ if (chip->pipe_alloc_mask & channel_mask) {
+ DE_ACT(("allocate_pipes: channel already open\n"));
+ return -EAGAIN;
+ }
+
+ chip->comm_page->position[pipe_index] = 0;
+ chip->pipe_alloc_mask |= channel_mask;
+ if (is_cyclic)
+ chip->pipe_cyclic_mask |= channel_mask;
+ pipe->index = pipe_index;
+ pipe->interleave = interleave;
+ pipe->state = PIPE_STATE_STOPPED;
+
+ /* The counter register is where the DSP writes the 32 bit DMA
+ position for a pipe. The DSP is constantly updating this value as
+ it moves data. The DMA counter is in units of bytes, not samples. */
+ pipe->dma_counter = &chip->comm_page->position[pipe_index];
+ *pipe->dma_counter = 0;
+ DE_ACT(("allocate_pipes: ok\n"));
+ return pipe_index;
+}
+
+
+
+static int free_pipes(struct echoaudio *chip, struct audiopipe *pipe)
+{
+ u32 channel_mask;
+ int i;
+
+ DE_ACT(("free_pipes: Pipe %d\n", pipe->index));
+ snd_assert(is_pipe_allocated(chip, pipe->index), return -EINVAL);
+ snd_assert(pipe->state == PIPE_STATE_STOPPED, return -EINVAL);
+
+ for (channel_mask = i = 0; i < pipe->interleave; i++)
+ channel_mask |= 1 << (pipe->index + i);
+
+ chip->pipe_alloc_mask &= ~channel_mask;
+ chip->pipe_cyclic_mask &= ~channel_mask;
+ return 0;
+}
+
+
+
+/******************************************************************************
+ Functions for managing the scatter-gather list
+******************************************************************************/
+
+static int sglist_init(struct echoaudio *chip, struct audiopipe *pipe)
+{
+ pipe->sglist_head = 0;
+ memset(pipe->sgpage.area, 0, PAGE_SIZE);
+ chip->comm_page->sglist_addr[pipe->index].addr =
+ cpu_to_le32(pipe->sgpage.addr);
+ return 0;
+}
+
+
+
+static int sglist_add_mapping(struct echoaudio *chip, struct audiopipe *pipe,
+ dma_addr_t address, size_t length)
+{
+ int head = pipe->sglist_head;
+ struct sg_entry *list = (struct sg_entry *)pipe->sgpage.area;
+
+ if (head < MAX_SGLIST_ENTRIES - 1) {
+ list[head].addr = cpu_to_le32(address);
+ list[head].size = cpu_to_le32(length);
+ pipe->sglist_head++;
+ } else {
+ DE_ACT(("SGlist: too many fragments\n"));
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+
+
+static inline int sglist_add_irq(struct echoaudio *chip, struct audiopipe *pipe)
+{
+ return sglist_add_mapping(chip, pipe, 0, 0);
+}
+
+
+
+static inline int sglist_wrap(struct echoaudio *chip, struct audiopipe *pipe)
+{
+ return sglist_add_mapping(chip, pipe, pipe->sgpage.addr, 0);
+}
diff --git a/sound/pci/echoaudio/echoaudio_dsp.h b/sound/pci/echoaudio/echoaudio_dsp.h
new file mode 100644
index 000000000000..e55ee00991ac
--- /dev/null
+++ b/sound/pci/echoaudio/echoaudio_dsp.h
@@ -0,0 +1,694 @@
+/****************************************************************************
+
+ Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+ All rights reserved
+ www.echoaudio.com
+
+ This file is part of Echo Digital Audio's generic driver library.
+
+ Echo Digital Audio's generic driver library is free software;
+ you can redistribute it and/or modify it under the terms of
+ the GNU General Public License as published by the Free Software
+ Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ 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.
+
+ *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+#ifndef _ECHO_DSP_
+#define _ECHO_DSP_
+
+
+/**** Echogals: Darla20, Gina20, Layla20, and Darla24 ****/
+#if defined(ECHOGALS_FAMILY)
+
+#define NUM_ASIC_TESTS 5
+#define READ_DSP_TIMEOUT 1000000L /* one second */
+
+/**** Echo24: Gina24, Layla24, Mona, Mia, Mia-midi ****/
+#elif defined(ECHO24_FAMILY)
+
+#define DSP_56361 /* Some Echo24 cards use the 56361 DSP */
+#define READ_DSP_TIMEOUT 100000L /* .1 second */
+
+/**** 3G: Gina3G, Layla3G ****/
+#elif defined(ECHO3G_FAMILY)
+
+#define DSP_56361
+#define READ_DSP_TIMEOUT 100000L /* .1 second */
+#define MIN_MTC_1X_RATE 32000
+
+/**** Indigo: Indigo, Indigo IO, Indigo DJ ****/
+#elif defined(INDIGO_FAMILY)
+
+#define DSP_56361
+#define READ_DSP_TIMEOUT 100000L /* .1 second */
+
+#else
+
+#error No family is defined
+
+#endif
+
+
+
+/*
+ *
+ * Max inputs and outputs
+ *
+ */
+
+#define DSP_MAXAUDIOINPUTS 16 /* Max audio input channels */
+#define DSP_MAXAUDIOOUTPUTS 16 /* Max audio output channels */
+#define DSP_MAXPIPES 32 /* Max total pipes (input + output) */
+
+
+/*
+ *
+ * These are the offsets for the memory-mapped DSP registers; the DSP base
+ * address is treated as the start of a u32 array.
+ */
+
+#define CHI32_CONTROL_REG 4
+#define CHI32_STATUS_REG 5
+#define CHI32_VECTOR_REG 6
+#define CHI32_DATA_REG 7
+
+
+/*
+ *
+ * Interesting bits within the DSP registers
+ *
+ */
+
+#define CHI32_VECTOR_BUSY 0x00000001
+#define CHI32_STATUS_REG_HF3 0x00000008
+#define CHI32_STATUS_REG_HF4 0x00000010
+#define CHI32_STATUS_REG_HF5 0x00000020
+#define CHI32_STATUS_HOST_READ_FULL 0x00000004
+#define CHI32_STATUS_HOST_WRITE_EMPTY 0x00000002
+#define CHI32_STATUS_IRQ 0x00000040
+
+
+/*
+ *
+ * DSP commands sent via slave mode; these are sent to the DSP by write_dsp()
+ *
+ */
+
+#define DSP_FNC_SET_COMMPAGE_ADDR 0x02
+#define DSP_FNC_LOAD_LAYLA_ASIC 0xa0
+#define DSP_FNC_LOAD_GINA24_ASIC 0xa0
+#define DSP_FNC_LOAD_MONA_PCI_CARD_ASIC 0xa0
+#define DSP_FNC_LOAD_LAYLA24_PCI_CARD_ASIC 0xa0
+#define DSP_FNC_LOAD_MONA_EXTERNAL_ASIC 0xa1
+#define DSP_FNC_LOAD_LAYLA24_EXTERNAL_ASIC 0xa1
+#define DSP_FNC_LOAD_3G_ASIC 0xa0
+
+
+/*
+ *
+ * Defines to handle the MIDI input state engine; these are used to properly
+ * extract MIDI time code bytes and their timestamps from the MIDI input stream.
+ *
+ */
+
+#define MIDI_IN_STATE_NORMAL 0
+#define MIDI_IN_STATE_TS_HIGH 1
+#define MIDI_IN_STATE_TS_LOW 2
+#define MIDI_IN_STATE_F1_DATA 3
+#define MIDI_IN_SKIP_DATA (-1)
+
+
+/*----------------------------------------------------------------------------
+
+Setting the sample rates on Layla24 is somewhat schizophrenic.
+
+For standard rates, it works exactly like Mona and Gina24. That is, for
+8, 11.025, 16, 22.05, 32, 44.1, 48, 88.2, and 96 kHz, you just set the
+appropriate bits in the control register and write the control register.
+
+In order to support MIDI time code sync (and possibly SMPTE LTC sync in
+the future), Layla24 also has "continuous sample rate mode". In this mode,
+Layla24 can generate any sample rate between 25 and 50 kHz inclusive, or
+50 to 100 kHz inclusive for double speed mode.
+
+To use continuous mode:
+
+-Set the clock select bits in the control register to 0xe (see the #define
+ below)
+
+-Set double-speed mode if you want to use sample rates above 50 kHz
+
+-Write the control register as you would normally
+
+-Now, you need to set the frequency register. First, you need to determine the
+ value for the frequency register. This is given by the following formula:
+
+frequency_reg = (LAYLA24_MAGIC_NUMBER / sample_rate) - 2
+
+Note the #define below for the magic number
+
+-Wait for the DSP handshake
+-Write the frequency_reg value to the .SampleRate field of the comm page
+-Send the vector command SET_LAYLA24_FREQUENCY_REG (see vmonkey.h)
+
+Once you have set the control register up for continuous mode, you can just
+write the frequency register to change the sample rate. This could be
+used for MIDI time code sync. For MTC sync, the control register is set for
+continuous mode. The driver then just keeps writing the
+SET_LAYLA24_FREQUENCY_REG command.
+
+-----------------------------------------------------------------------------*/
+
+#define LAYLA24_MAGIC_NUMBER 677376000
+#define LAYLA24_CONTINUOUS_CLOCK 0x000e
+
+
+/*
+ *
+ * DSP vector commands
+ *
+ */
+
+#define DSP_VC_RESET 0x80ff
+
+#ifndef DSP_56361
+
+#define DSP_VC_ACK_INT 0x8073
+#define DSP_VC_SET_VMIXER_GAIN 0x0000 /* Not used, only for compile */
+#define DSP_VC_START_TRANSFER 0x0075 /* Handshke rqd. */
+#define DSP_VC_METERS_ON 0x0079
+#define DSP_VC_METERS_OFF 0x007b
+#define DSP_VC_UPDATE_OUTVOL 0x007d /* Handshke rqd. */
+#define DSP_VC_UPDATE_INGAIN 0x007f /* Handshke rqd. */
+#define DSP_VC_ADD_AUDIO_BUFFER 0x0081 /* Handshke rqd. */
+#define DSP_VC_TEST_ASIC 0x00eb
+#define DSP_VC_UPDATE_CLOCKS 0x00ef /* Handshke rqd. */
+#define DSP_VC_SET_LAYLA_SAMPLE_RATE 0x00f1 /* Handshke rqd. */
+#define DSP_VC_SET_GD_AUDIO_STATE 0x00f1 /* Handshke rqd. */
+#define DSP_VC_WRITE_CONTROL_REG 0x00f1 /* Handshke rqd. */
+#define DSP_VC_MIDI_WRITE 0x00f5 /* Handshke rqd. */
+#define DSP_VC_STOP_TRANSFER 0x00f7 /* Handshke rqd. */
+#define DSP_VC_UPDATE_FLAGS 0x00fd /* Handshke rqd. */
+#define DSP_VC_GO_COMATOSE 0x00f9
+
+#else /* !DSP_56361 */
+
+/* Vector commands for families that use either the 56301 or 56361 */
+#define DSP_VC_ACK_INT 0x80F5
+#define DSP_VC_SET_VMIXER_GAIN 0x00DB /* Handshke rqd. */
+#define DSP_VC_START_TRANSFER 0x00DD /* Handshke rqd. */
+#define DSP_VC_METERS_ON 0x00EF
+#define DSP_VC_METERS_OFF 0x00F1
+#define DSP_VC_UPDATE_OUTVOL 0x00E3 /* Handshke rqd. */
+#define DSP_VC_UPDATE_INGAIN 0x00E5 /* Handshke rqd. */
+#define DSP_VC_ADD_AUDIO_BUFFER 0x00E1 /* Handshke rqd. */
+#define DSP_VC_TEST_ASIC 0x00ED
+#define DSP_VC_UPDATE_CLOCKS 0x00E9 /* Handshke rqd. */
+#define DSP_VC_SET_LAYLA24_FREQUENCY_REG 0x00E9 /* Handshke rqd. */
+#define DSP_VC_SET_LAYLA_SAMPLE_RATE 0x00EB /* Handshke rqd. */
+#define DSP_VC_SET_GD_AUDIO_STATE 0x00EB /* Handshke rqd. */
+#define DSP_VC_WRITE_CONTROL_REG 0x00EB /* Handshke rqd. */
+#define DSP_VC_MIDI_WRITE 0x00E7 /* Handshke rqd. */
+#define DSP_VC_STOP_TRANSFER 0x00DF /* Handshke rqd. */
+#define DSP_VC_UPDATE_FLAGS 0x00FB /* Handshke rqd. */
+#define DSP_VC_GO_COMATOSE 0x00d9
+
+#endif /* !DSP_56361 */
+
+
+/*
+ *
+ * Timeouts
+ *
+ */
+
+#define HANDSHAKE_TIMEOUT 20000 /* send_vector command timeout (20ms) */
+#define VECTOR_BUSY_TIMEOUT 100000 /* 100ms */
+#define MIDI_OUT_DELAY_USEC 2000 /* How long to wait after MIDI fills up */
+
+
+/*
+ *
+ * Flags for .Flags field in the comm page
+ *
+ */
+
+#define DSP_FLAG_MIDI_INPUT 0x0001 /* Enable MIDI input */
+#define DSP_FLAG_SPDIF_NONAUDIO 0x0002 /* Sets the "non-audio" bit
+ * in the S/PDIF out status
+ * bits. Clear this flag for
+ * audio data;
+ * set it for AC3 or WMA or
+ * some such */
+#define DSP_FLAG_PROFESSIONAL_SPDIF 0x0008 /* 1 Professional, 0 Consumer */
+
+
+/*
+ *
+ * Clock detect bits reported by the DSP for Gina20, Layla20, Darla24, and Mia
+ *
+ */
+
+#define GLDM_CLOCK_DETECT_BIT_WORD 0x0002
+#define GLDM_CLOCK_DETECT_BIT_SUPER 0x0004
+#define GLDM_CLOCK_DETECT_BIT_SPDIF 0x0008
+#define GLDM_CLOCK_DETECT_BIT_ESYNC 0x0010
+
+
+/*
+ *
+ * Clock detect bits reported by the DSP for Gina24, Mona, and Layla24
+ *
+ */
+
+#define GML_CLOCK_DETECT_BIT_WORD96 0x0002
+#define GML_CLOCK_DETECT_BIT_WORD48 0x0004
+#define GML_CLOCK_DETECT_BIT_SPDIF48 0x0008
+#define GML_CLOCK_DETECT_BIT_SPDIF96 0x0010
+#define GML_CLOCK_DETECT_BIT_WORD (GML_CLOCK_DETECT_BIT_WORD96 | GML_CLOCK_DETECT_BIT_WORD48)
+#define GML_CLOCK_DETECT_BIT_SPDIF (GML_CLOCK_DETECT_BIT_SPDIF48 | GML_CLOCK_DETECT_BIT_SPDIF96)
+#define GML_CLOCK_DETECT_BIT_ESYNC 0x0020
+#define GML_CLOCK_DETECT_BIT_ADAT 0x0040
+
+
+/*
+ *
+ * Layla clock numbers to send to DSP
+ *
+ */
+
+#define LAYLA20_CLOCK_INTERNAL 0
+#define LAYLA20_CLOCK_SPDIF 1
+#define LAYLA20_CLOCK_WORD 2
+#define LAYLA20_CLOCK_SUPER 3
+
+
+/*
+ *
+ * Gina/Darla clock states
+ *
+ */
+
+#define GD_CLOCK_NOCHANGE 0
+#define GD_CLOCK_44 1
+#define GD_CLOCK_48 2
+#define GD_CLOCK_SPDIFIN 3
+#define GD_CLOCK_UNDEF 0xff
+
+
+/*
+ *
+ * Gina/Darla S/PDIF status bits
+ *
+ */
+
+#define GD_SPDIF_STATUS_NOCHANGE 0
+#define GD_SPDIF_STATUS_44 1
+#define GD_SPDIF_STATUS_48 2
+#define GD_SPDIF_STATUS_UNDEF 0xff
+
+
+/*
+ *
+ * Layla20 output clocks
+ *
+ */
+
+#define LAYLA20_OUTPUT_CLOCK_SUPER 0
+#define LAYLA20_OUTPUT_CLOCK_WORD 1
+
+
+/****************************************************************************
+
+ Magic constants for the Darla24 hardware
+
+ ****************************************************************************/
+
+#define GD24_96000 0x0
+#define GD24_48000 0x1
+#define GD24_44100 0x2
+#define GD24_32000 0x3
+#define GD24_22050 0x4
+#define GD24_16000 0x5
+#define GD24_11025 0x6
+#define GD24_8000 0x7
+#define GD24_88200 0x8
+#define GD24_EXT_SYNC 0x9
+
+
+/*
+ *
+ * Return values from the DSP when ASIC is loaded
+ *
+ */
+
+#define ASIC_ALREADY_LOADED 0x1
+#define ASIC_NOT_LOADED 0x0
+
+
+/*
+ *
+ * DSP Audio formats
+ *
+ * These are the audio formats that the DSP can transfer
+ * via input and output pipes. LE means little-endian,
+ * BE means big-endian.
+ *
+ * DSP_AUDIOFORM_MS_8
+ *
+ * 8-bit mono unsigned samples. For playback,
+ * mono data is duplicated out the left and right channels
+ * of the output bus. The "MS" part of the name
+ * means mono->stereo.
+ *
+ * DSP_AUDIOFORM_MS_16LE
+ *
+ * 16-bit signed little-endian mono samples. Playback works
+ * like the previous code.
+ *
+ * DSP_AUDIOFORM_MS_24LE
+ *
+ * 24-bit signed little-endian mono samples. Data is packed
+ * three bytes per sample; if you had two samples 0x112233 and 0x445566
+ * they would be stored in memory like this: 33 22 11 66 55 44.
+ *
+ * DSP_AUDIOFORM_MS_32LE
+ *
+ * 24-bit signed little-endian mono samples in a 32-bit
+ * container. In other words, each sample is a 32-bit signed
+ * integer, where the actual audio data is left-justified
+ * in the 32 bits and only the 24 most significant bits are valid.
+ *
+ * DSP_AUDIOFORM_SS_8
+ * DSP_AUDIOFORM_SS_16LE
+ * DSP_AUDIOFORM_SS_24LE
+ * DSP_AUDIOFORM_SS_32LE
+ *
+ * Like the previous ones, except now with stereo interleaved
+ * data. "SS" means stereo->stereo.
+ *
+ * DSP_AUDIOFORM_MM_32LE
+ *
+ * Similar to DSP_AUDIOFORM_MS_32LE, except that the mono
+ * data is not duplicated out both the left and right outputs.
+ * This mode is used by the ASIO driver. Here, "MM" means
+ * mono->mono.
+ *
+ * DSP_AUDIOFORM_MM_32BE
+ *
+ * Just like DSP_AUDIOFORM_MM_32LE, but now the data is
+ * in big-endian format.
+ *
+ */
+
+#define DSP_AUDIOFORM_MS_8 0 /* 8 bit mono */
+#define DSP_AUDIOFORM_MS_16LE 1 /* 16 bit mono */
+#define DSP_AUDIOFORM_MS_24LE 2 /* 24 bit mono */
+#define DSP_AUDIOFORM_MS_32LE 3 /* 32 bit mono */
+#define DSP_AUDIOFORM_SS_8 4 /* 8 bit stereo */
+#define DSP_AUDIOFORM_SS_16LE 5 /* 16 bit stereo */
+#define DSP_AUDIOFORM_SS_24LE 6 /* 24 bit stereo */
+#define DSP_AUDIOFORM_SS_32LE 7 /* 32 bit stereo */
+#define DSP_AUDIOFORM_MM_32LE 8 /* 32 bit mono->mono little-endian */
+#define DSP_AUDIOFORM_MM_32BE 9 /* 32 bit mono->mono big-endian */
+#define DSP_AUDIOFORM_SS_32BE 10 /* 32 bit stereo big endian */
+#define DSP_AUDIOFORM_INVALID 0xFF /* Invalid audio format */
+
+
+/*
+ *
+ * Super-interleave is defined as interleaving by 4 or more. Darla20 and Gina20
+ * do not support super interleave.
+ *
+ * 16 bit, 24 bit, and 32 bit little endian samples are supported for super
+ * interleave. The interleave factor must be even. 16 - way interleave is the
+ * current maximum, so you can interleave by 4, 6, 8, 10, 12, 14, and 16.
+ *
+ * The actual format code is derived by taking the define below and or-ing with
+ * the interleave factor. So, 32 bit interleave by 6 is 0x86 and
+ * 16 bit interleave by 16 is (0x40 | 0x10) = 0x50.
+ *
+ */
+
+#define DSP_AUDIOFORM_SUPER_INTERLEAVE_16LE 0x40
+#define DSP_AUDIOFORM_SUPER_INTERLEAVE_24LE 0xc0
+#define DSP_AUDIOFORM_SUPER_INTERLEAVE_32LE 0x80
+
+
+/*
+ *
+ * Gina24, Mona, and Layla24 control register defines
+ *
+ */
+
+#define GML_CONVERTER_ENABLE 0x0010
+#define GML_SPDIF_PRO_MODE 0x0020 /* Professional S/PDIF == 1,
+ consumer == 0 */
+#define GML_SPDIF_SAMPLE_RATE0 0x0040
+#define GML_SPDIF_SAMPLE_RATE1 0x0080
+#define GML_SPDIF_TWO_CHANNEL 0x0100 /* 1 == two channels,
+ 0 == one channel */
+#define GML_SPDIF_NOT_AUDIO 0x0200
+#define GML_SPDIF_COPY_PERMIT 0x0400
+#define GML_SPDIF_24_BIT 0x0800 /* 1 == 24 bit, 0 == 20 bit */
+#define GML_ADAT_MODE 0x1000 /* 1 == ADAT mode, 0 == S/PDIF mode */
+#define GML_SPDIF_OPTICAL_MODE 0x2000 /* 1 == optical mode, 0 == RCA mode */
+#define GML_SPDIF_CDROM_MODE 0x3000 /* 1 == CDROM mode,
+ * 0 == RCA or optical mode */
+#define GML_DOUBLE_SPEED_MODE 0x4000 /* 1 == double speed,
+ 0 == single speed */
+
+#define GML_DIGITAL_IN_AUTO_MUTE 0x800000
+
+#define GML_96KHZ (0x0 | GML_DOUBLE_SPEED_MODE)
+#define GML_88KHZ (0x1 | GML_DOUBLE_SPEED_MODE)
+#define GML_48KHZ 0x2
+#define GML_44KHZ 0x3
+#define GML_32KHZ 0x4
+#define GML_22KHZ 0x5
+#define GML_16KHZ 0x6
+#define GML_11KHZ 0x7
+#define GML_8KHZ 0x8
+#define GML_SPDIF_CLOCK 0x9
+#define GML_ADAT_CLOCK 0xA
+#define GML_WORD_CLOCK 0xB
+#define GML_ESYNC_CLOCK 0xC
+#define GML_ESYNCx2_CLOCK 0xD
+
+#define GML_CLOCK_CLEAR_MASK 0xffffbff0
+#define GML_SPDIF_RATE_CLEAR_MASK (~(GML_SPDIF_SAMPLE_RATE0|GML_SPDIF_SAMPLE_RATE1))
+#define GML_DIGITAL_MODE_CLEAR_MASK 0xffffcfff
+#define GML_SPDIF_FORMAT_CLEAR_MASK 0xfffff01f
+
+
+/*
+ *
+ * Mia sample rate and clock setting constants
+ *
+ */
+
+#define MIA_32000 0x0040
+#define MIA_44100 0x0042
+#define MIA_48000 0x0041
+#define MIA_88200 0x0142
+#define MIA_96000 0x0141
+
+#define MIA_SPDIF 0x00000044
+#define MIA_SPDIF96 0x00000144
+
+#define MIA_MIDI_REV 1 /* Must be Mia rev 1 for MIDI support */
+
+
+/*
+ *
+ * 3G register bits
+ *
+ */
+
+#define E3G_CONVERTER_ENABLE 0x0010
+#define E3G_SPDIF_PRO_MODE 0x0020 /* Professional S/PDIF == 1,
+ consumer == 0 */
+#define E3G_SPDIF_SAMPLE_RATE0 0x0040
+#define E3G_SPDIF_SAMPLE_RATE1 0x0080
+#define E3G_SPDIF_TWO_CHANNEL 0x0100 /* 1 == two channels,
+ 0 == one channel */
+#define E3G_SPDIF_NOT_AUDIO 0x0200
+#define E3G_SPDIF_COPY_PERMIT 0x0400
+#define E3G_SPDIF_24_BIT 0x0800 /* 1 == 24 bit, 0 == 20 bit */
+#define E3G_DOUBLE_SPEED_MODE 0x4000 /* 1 == double speed,
+ 0 == single speed */
+#define E3G_PHANTOM_POWER 0x8000 /* 1 == phantom power on,
+ 0 == phantom power off */
+
+#define E3G_96KHZ (0x0 | E3G_DOUBLE_SPEED_MODE)
+#define E3G_88KHZ (0x1 | E3G_DOUBLE_SPEED_MODE)
+#define E3G_48KHZ 0x2
+#define E3G_44KHZ 0x3
+#define E3G_32KHZ 0x4
+#define E3G_22KHZ 0x5
+#define E3G_16KHZ 0x6
+#define E3G_11KHZ 0x7
+#define E3G_8KHZ 0x8
+#define E3G_SPDIF_CLOCK 0x9
+#define E3G_ADAT_CLOCK 0xA
+#define E3G_WORD_CLOCK 0xB
+#define E3G_CONTINUOUS_CLOCK 0xE
+
+#define E3G_ADAT_MODE 0x1000
+#define E3G_SPDIF_OPTICAL_MODE 0x2000
+
+#define E3G_CLOCK_CLEAR_MASK 0xbfffbff0
+#define E3G_DIGITAL_MODE_CLEAR_MASK 0xffffcfff
+#define E3G_SPDIF_FORMAT_CLEAR_MASK 0xfffff01f
+
+/* Clock detect bits reported by the DSP */
+#define E3G_CLOCK_DETECT_BIT_WORD96 0x0001
+#define E3G_CLOCK_DETECT_BIT_WORD48 0x0002
+#define E3G_CLOCK_DETECT_BIT_SPDIF48 0x0004
+#define E3G_CLOCK_DETECT_BIT_ADAT 0x0004
+#define E3G_CLOCK_DETECT_BIT_SPDIF96 0x0008
+#define E3G_CLOCK_DETECT_BIT_WORD (E3G_CLOCK_DETECT_BIT_WORD96|E3G_CLOCK_DETECT_BIT_WORD48)
+#define E3G_CLOCK_DETECT_BIT_SPDIF (E3G_CLOCK_DETECT_BIT_SPDIF48|E3G_CLOCK_DETECT_BIT_SPDIF96)
+
+/* Frequency control register */
+#define E3G_MAGIC_NUMBER 677376000
+#define E3G_FREQ_REG_DEFAULT (E3G_MAGIC_NUMBER / 48000 - 2)
+#define E3G_FREQ_REG_MAX 0xffff
+
+/* 3G external box types */
+#define E3G_GINA3G_BOX_TYPE 0x00
+#define E3G_LAYLA3G_BOX_TYPE 0x10
+#define E3G_ASIC_NOT_LOADED 0xffff
+#define E3G_BOX_TYPE_MASK 0xf0
+
+#define EXT_3GBOX_NC 0x01
+#define EXT_3GBOX_NOT_SET 0x02
+
+
+/*
+ *
+ * Gina20 & Layla20 have input gain controls for the analog inputs;
+ * this is the magic number for the hardware that gives you 0 dB at -10.
+ *
+ */
+
+#define GL20_INPUT_GAIN_MAGIC_NUMBER 0xC8
+
+
+/*
+ *
+ * Defines how much time must pass between DSP load attempts
+ *
+ */
+
+#define DSP_LOAD_ATTEMPT_PERIOD 1000000L /* One second */
+
+
+/*
+ *
+ * Size of arrays for the comm page. MAX_PLAY_TAPS and MAX_REC_TAPS are
+ * no longer used, but the sizes must still be right for the DSP to see
+ * the comm page correctly.
+ *
+ */
+
+#define MONITOR_ARRAY_SIZE 0x180
+#define VMIXER_ARRAY_SIZE 0x40
+#define MIDI_OUT_BUFFER_SIZE 32
+#define MIDI_IN_BUFFER_SIZE 256
+#define MAX_PLAY_TAPS 168
+#define MAX_REC_TAPS 192
+#define DSP_MIDI_OUT_FIFO_SIZE 64
+
+
+/* sg_entry is a single entry for the scatter-gather list. The array of struct
+sg_entry struct is read by the DSP, so all values must be little-endian. */
+
+#define MAX_SGLIST_ENTRIES 512
+
+struct sg_entry {
+ u32 addr;
+ u32 size;
+};
+
+
+/****************************************************************************
+
+ The comm page. This structure is read and written by the DSP; the
+ DSP code is a firm believer in the byte offsets written in the comments
+ at the end of each line. This structure should not be changed.
+
+ Any reads from or writes to this structure should be in little-endian format.
+
+ ****************************************************************************/
+
+struct comm_page { /* Base Length*/
+ u32 comm_size; /* size of this object 0x000 4 */
+ u32 flags; /* See Appendix A below 0x004 4 */
+ u32 unused; /* Unused entry 0x008 4 */
+ u32 sample_rate; /* Card sample rate in Hz 0x00c 4 */
+ volatile u32 handshake; /* DSP command handshake 0x010 4 */
+ u32 cmd_start; /* Chs. to start mask 0x014 4 */
+ u32 cmd_stop; /* Chs. to stop mask 0x018 4 */
+ u32 cmd_reset; /* Chs. to reset mask 0x01c 4 */
+ u16 audio_format[DSP_MAXPIPES]; /* Chs. audio format 0x020 32*2 */
+ struct sg_entry sglist_addr[DSP_MAXPIPES];
+ /* Chs. Physical sglist addrs 0x060 32*8 */
+ volatile u32 position[DSP_MAXPIPES];
+ /* Positions for ea. ch. 0x160 32*4 */
+ volatile s8 vu_meter[DSP_MAXPIPES];
+ /* VU meters 0x1e0 32*1 */
+ volatile s8 peak_meter[DSP_MAXPIPES];
+ /* Peak meters 0x200 32*1 */
+ s8 line_out_level[DSP_MAXAUDIOOUTPUTS];
+ /* Output gain 0x220 16*1 */
+ s8 line_in_level[DSP_MAXAUDIOINPUTS];
+ /* Input gain 0x230 16*1 */
+ s8 monitors[MONITOR_ARRAY_SIZE];
+ /* Monitor map 0x240 0x180 */
+ u32 play_coeff[MAX_PLAY_TAPS];
+ /* Gina/Darla play filters - obsolete 0x3c0 168*4 */
+ u32 rec_coeff[MAX_REC_TAPS];
+ /* Gina/Darla record filters - obsolete 0x660 192*4 */
+ volatile u16 midi_input[MIDI_IN_BUFFER_SIZE];
+ /* MIDI input data transfer buffer 0x960 256*2 */
+ u8 gd_clock_state; /* Chg Gina/Darla clock state 0xb60 1 */
+ u8 gd_spdif_status; /* Chg. Gina/Darla S/PDIF state 0xb61 1 */
+ u8 gd_resampler_state; /* Should always be 3 0xb62 1 */
+ u8 filler2; /* 0xb63 1 */
+ u32 nominal_level_mask; /* -10 level enable mask 0xb64 4 */
+ u16 input_clock; /* Chg. Input clock state 0xb68 2 */
+ u16 output_clock; /* Chg. Output clock state 0xb6a 2 */
+ volatile u32 status_clocks;
+ /* Current Input clock state 0xb6c 4 */
+ u32 ext_box_status; /* External box status 0xb70 4 */
+ u32 cmd_add_buffer; /* Pipes to add (obsolete) 0xb74 4 */
+ volatile u32 midi_out_free_count;
+ /* # of bytes free in MIDI output FIFO 0xb78 4 */
+ u32 unused2; /* Cyclic pipes 0xb7c 4 */
+ u32 control_register;
+ /* Mona, Gina24, Layla24, 3G ctrl reg 0xb80 4 */
+ u32 e3g_frq_register; /* 3G frequency register 0xb84 4 */
+ u8 filler[24]; /* filler 0xb88 24*1 */
+ s8 vmixer[VMIXER_ARRAY_SIZE];
+ /* Vmixer levels 0xba0 64*1 */
+ u8 midi_output[MIDI_OUT_BUFFER_SIZE];
+ /* MIDI output data 0xbe0 32*1 */
+};
+
+#endif /* _ECHO_DSP_ */
diff --git a/sound/pci/echoaudio/echoaudio_gml.c b/sound/pci/echoaudio/echoaudio_gml.c
new file mode 100644
index 000000000000..3aa37e76ebab
--- /dev/null
+++ b/sound/pci/echoaudio/echoaudio_gml.c
@@ -0,0 +1,198 @@
+/****************************************************************************
+
+ Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+ All rights reserved
+ www.echoaudio.com
+
+ This file is part of Echo Digital Audio's generic driver library.
+
+ Echo Digital Audio's generic driver library is free software;
+ you can redistribute it and/or modify it under the terms of
+ the GNU General Public License as published by the Free Software
+ Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ 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.
+
+ *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+/* These functions are common for Gina24, Layla24 and Mona cards */
+
+
+/* ASIC status check - some cards have one or two ASICs that need to be
+loaded. Once that load is complete, this function is called to see if
+the load was successful.
+If this load fails, it does not necessarily mean that the hardware is
+defective - the external box may be disconnected or turned off. */
+static int check_asic_status(struct echoaudio *chip)
+{
+ u32 asic_status;
+
+ send_vector(chip, DSP_VC_TEST_ASIC);
+
+ /* The DSP will return a value to indicate whether or not the
+ ASIC is currently loaded */
+ if (read_dsp(chip, &asic_status) < 0) {
+ DE_INIT(("check_asic_status: failed on read_dsp\n"));
+ chip->asic_loaded = FALSE;
+ return -EIO;
+ }
+
+ chip->asic_loaded = (asic_status == ASIC_ALREADY_LOADED);
+ return chip->asic_loaded ? 0 : -EIO;
+}
+
+
+
+/* Most configuration of Gina24, Layla24, or Mona is accomplished by writing
+the control register. write_control_reg sends the new control register
+value to the DSP. */
+static int write_control_reg(struct echoaudio *chip, u32 value, char force)
+{
+ /* Handle the digital input auto-mute */
+ if (chip->digital_in_automute)
+ value |= GML_DIGITAL_IN_AUTO_MUTE;
+ else
+ value &= ~GML_DIGITAL_IN_AUTO_MUTE;
+
+ DE_ACT(("write_control_reg: 0x%x\n", value));
+
+ /* Write the control register */
+ value = cpu_to_le32(value);
+ if (value != chip->comm_page->control_register || force) {
+ if (wait_handshake(chip))
+ return -EIO;
+ chip->comm_page->control_register = value;
+ clear_handshake(chip);
+ return send_vector(chip, DSP_VC_WRITE_CONTROL_REG);
+ }
+ return 0;
+}
+
+
+
+/* Gina24, Layla24, and Mona support digital input auto-mute. If the digital
+input auto-mute is enabled, the DSP will only enable the digital inputs if
+the card is syncing to a valid clock on the ADAT or S/PDIF inputs.
+If the auto-mute is disabled, the digital inputs are enabled regardless of
+what the input clock is set or what is connected. */
+static int set_input_auto_mute(struct echoaudio *chip, int automute)
+{
+ DE_ACT(("set_input_auto_mute %d\n", automute));
+
+ chip->digital_in_automute = automute;
+
+ /* Re-set the input clock to the current value - indirectly causes
+ the auto-mute flag to be sent to the DSP */
+ return set_input_clock(chip, chip->input_clock);
+}
+
+
+
+/* S/PDIF coax / S/PDIF optical / ADAT - switch */
+static int set_digital_mode(struct echoaudio *chip, u8 mode)
+{
+ u8 previous_mode;
+ int err, i, o;
+
+ if (chip->bad_board)
+ return -EIO;
+
+ /* All audio channels must be closed before changing the digital mode */
+ snd_assert(!chip->pipe_alloc_mask, return -EAGAIN);
+
+ snd_assert(chip->digital_modes & (1 << mode), return -EINVAL);
+
+ previous_mode = chip->digital_mode;
+ err = dsp_set_digital_mode(chip, mode);
+
+ /* If we successfully changed the digital mode from or to ADAT,
+ then make sure all output, input and monitor levels are
+ updated by the DSP comm object. */
+ if (err >= 0 && previous_mode != mode &&
+ (previous_mode == DIGITAL_MODE_ADAT || mode == DIGITAL_MODE_ADAT)) {
+ spin_lock_irq(&chip->lock);
+ for (o = 0; o < num_busses_out(chip); o++)
+ for (i = 0; i < num_busses_in(chip); i++)
+ set_monitor_gain(chip, o, i,
+ chip->monitor_gain[o][i]);
+
+#ifdef ECHOCARD_HAS_INPUT_GAIN
+ for (i = 0; i < num_busses_in(chip); i++)
+ set_input_gain(chip, i, chip->input_gain[i]);
+ update_input_line_level(chip);
+#endif
+
+ for (o = 0; o < num_busses_out(chip); o++)
+ set_output_gain(chip, o, chip->output_gain[o]);
+ update_output_line_level(chip);
+ spin_unlock_irq(&chip->lock);
+ }
+
+ return err;
+}
+
+
+
+/* Set the S/PDIF output format */
+static int set_professional_spdif(struct echoaudio *chip, char prof)
+{
+ u32 control_reg;
+ int err;
+
+ /* Clear the current S/PDIF flags */
+ control_reg = le32_to_cpu(chip->comm_page->control_register);
+ control_reg &= GML_SPDIF_FORMAT_CLEAR_MASK;
+
+ /* Set the new S/PDIF flags depending on the mode */
+ control_reg |= GML_SPDIF_TWO_CHANNEL | GML_SPDIF_24_BIT |
+ GML_SPDIF_COPY_PERMIT;
+ if (prof) {
+ /* Professional mode */
+ control_reg |= GML_SPDIF_PRO_MODE;
+
+ switch (chip->sample_rate) {
+ case 32000:
+ control_reg |= GML_SPDIF_SAMPLE_RATE0 |
+ GML_SPDIF_SAMPLE_RATE1;
+ break;
+ case 44100:
+ control_reg |= GML_SPDIF_SAMPLE_RATE0;
+ break;
+ case 48000:
+ control_reg |= GML_SPDIF_SAMPLE_RATE1;
+ break;
+ }
+ } else {
+ /* Consumer mode */
+ switch (chip->sample_rate) {
+ case 32000:
+ control_reg |= GML_SPDIF_SAMPLE_RATE0 |
+ GML_SPDIF_SAMPLE_RATE1;
+ break;
+ case 48000:
+ control_reg |= GML_SPDIF_SAMPLE_RATE1;
+ break;
+ }
+ }
+
+ if ((err = write_control_reg(chip, control_reg, FALSE)))
+ return err;
+ chip->professional_spdif = prof;
+ DE_ACT(("set_professional_spdif to %s\n",
+ prof ? "Professional" : "Consumer"));
+ return 0;
+}
diff --git a/sound/pci/echoaudio/gina20.c b/sound/pci/echoaudio/gina20.c
new file mode 100644
index 000000000000..29d6d12f80ca
--- /dev/null
+++ b/sound/pci/echoaudio/gina20.c
@@ -0,0 +1,103 @@
+/*
+ * ALSA driver for Echoaudio soundcards.
+ * Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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.
+ */
+
+#define ECHOGALS_FAMILY
+#define ECHOCARD_GINA20
+#define ECHOCARD_NAME "Gina20"
+#define ECHOCARD_HAS_MONITOR
+#define ECHOCARD_HAS_INPUT_GAIN
+#define ECHOCARD_HAS_DIGITAL_IO
+#define ECHOCARD_HAS_EXTERNAL_CLOCK
+#define ECHOCARD_HAS_ADAT FALSE
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT 0 /* 8 */
+#define PX_DIGITAL_OUT 8 /* 2 */
+#define PX_ANALOG_IN 10 /* 2 */
+#define PX_DIGITAL_IN 12 /* 2 */
+#define PX_NUM 14
+
+/* Bus indexes */
+#define BX_ANALOG_OUT 0 /* 8 */
+#define BX_DIGITAL_OUT 8 /* 2 */
+#define BX_ANALOG_IN 10 /* 2 */
+#define BX_DIGITAL_IN 12 /* 2 */
+#define BX_NUM 14
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+#define FW_GINA20_DSP 0
+
+static const struct firmware card_fw[] = {
+ {0, "gina20_dsp.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+ {0x1057, 0x1801, 0xECC0, 0x0020, 0, 0, 0}, /* DSP 56301 Gina20 rev.0 */
+ {0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_SYNC_START,
+ .formats = SNDRV_PCM_FMTBIT_U8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_S32_BE,
+ .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+ .rate_min = 44100,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = 262144,
+ .period_bytes_min = 32,
+ .period_bytes_max = 131072,
+ .periods_min = 2,
+ .periods_max = 220,
+ /* One page (4k) contains 512 instructions. I don't know if the hw
+ supports lists longer than this. In this case periods_max=220 is a
+ safe limit to make sure the list never exceeds 512 instructions. */
+};
+
+
+#include "gina20_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio.c"
diff --git a/sound/pci/echoaudio/gina20_dsp.c b/sound/pci/echoaudio/gina20_dsp.c
new file mode 100644
index 000000000000..2757c8960843
--- /dev/null
+++ b/sound/pci/echoaudio/gina20_dsp.c
@@ -0,0 +1,215 @@
+/****************************************************************************
+
+ Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+ All rights reserved
+ www.echoaudio.com
+
+ This file is part of Echo Digital Audio's generic driver library.
+
+ Echo Digital Audio's generic driver library is free software;
+ you can redistribute it and/or modify it under the terms of
+ the GNU General Public License as published by the Free Software
+ Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ 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.
+
+ *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+static int set_professional_spdif(struct echoaudio *chip, char prof);
+static int update_flags(struct echoaudio *chip);
+
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+ int err;
+
+ DE_INIT(("init_hw() - Gina20\n"));
+ snd_assert((subdevice_id & 0xfff0) == GINA20, return -ENODEV);
+
+ if ((err = init_dsp_comm_page(chip))) {
+ DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+ return err;
+ }
+
+ chip->device_id = device_id;
+ chip->subdevice_id = subdevice_id;
+ chip->bad_board = TRUE;
+ chip->dsp_code_to_load = &card_fw[FW_GINA20_DSP];
+ chip->spdif_status = GD_SPDIF_STATUS_UNDEF;
+ chip->clock_state = GD_CLOCK_UNDEF;
+ /* Since this card has no ASIC, mark it as loaded so everything
+ works OK */
+ chip->asic_loaded = TRUE;
+ chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL |
+ ECHO_CLOCK_BIT_SPDIF;
+
+ if ((err = load_firmware(chip)) < 0)
+ return err;
+ chip->bad_board = FALSE;
+
+ if ((err = init_line_levels(chip)) < 0)
+ return err;
+
+ err = set_professional_spdif(chip, TRUE);
+
+ DE_INIT(("init_hw done\n"));
+ return err;
+}
+
+
+
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+ u32 clocks_from_dsp, clock_bits;
+
+ /* Map the DSP clock detect bits to the generic driver clock
+ detect bits */
+ clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks);
+
+ clock_bits = ECHO_CLOCK_BIT_INTERNAL;
+
+ if (clocks_from_dsp & GLDM_CLOCK_DETECT_BIT_SPDIF)
+ clock_bits |= ECHO_CLOCK_BIT_SPDIF;
+
+ return clock_bits;
+}
+
+
+
+/* The Gina20 has no ASIC. Just do nothing */
+static int load_asic(struct echoaudio *chip)
+{
+ return 0;
+}
+
+
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+ u8 clock_state, spdif_status;
+
+ if (wait_handshake(chip))
+ return -EIO;
+
+ switch (rate) {
+ case 44100:
+ clock_state = GD_CLOCK_44;
+ spdif_status = GD_SPDIF_STATUS_44;
+ break;
+ case 48000:
+ clock_state = GD_CLOCK_48;
+ spdif_status = GD_SPDIF_STATUS_48;
+ break;
+ default:
+ clock_state = GD_CLOCK_NOCHANGE;
+ spdif_status = GD_SPDIF_STATUS_NOCHANGE;
+ break;
+ }
+
+ if (chip->clock_state == clock_state)
+ clock_state = GD_CLOCK_NOCHANGE;
+ if (spdif_status == chip->spdif_status)
+ spdif_status = GD_SPDIF_STATUS_NOCHANGE;
+
+ chip->comm_page->sample_rate = cpu_to_le32(rate);
+ chip->comm_page->gd_clock_state = clock_state;
+ chip->comm_page->gd_spdif_status = spdif_status;
+ chip->comm_page->gd_resampler_state = 3; /* magic number - should always be 3 */
+
+ /* Save the new audio state if it changed */
+ if (clock_state != GD_CLOCK_NOCHANGE)
+ chip->clock_state = clock_state;
+ if (spdif_status != GD_SPDIF_STATUS_NOCHANGE)
+ chip->spdif_status = spdif_status;
+ chip->sample_rate = rate;
+
+ clear_handshake(chip);
+ return send_vector(chip, DSP_VC_SET_GD_AUDIO_STATE);
+}
+
+
+
+static int set_input_clock(struct echoaudio *chip, u16 clock)
+{
+ DE_ACT(("set_input_clock:\n"));
+
+ switch (clock) {
+ case ECHO_CLOCK_INTERNAL:
+ /* Reset the audio state to unknown (just in case) */
+ chip->clock_state = GD_CLOCK_UNDEF;
+ chip->spdif_status = GD_SPDIF_STATUS_UNDEF;
+ set_sample_rate(chip, chip->sample_rate);
+ chip->input_clock = clock;
+ DE_ACT(("Set Gina clock to INTERNAL\n"));
+ break;
+ case ECHO_CLOCK_SPDIF:
+ chip->comm_page->gd_clock_state = GD_CLOCK_SPDIFIN;
+ chip->comm_page->gd_spdif_status = GD_SPDIF_STATUS_NOCHANGE;
+ clear_handshake(chip);
+ send_vector(chip, DSP_VC_SET_GD_AUDIO_STATE);
+ chip->clock_state = GD_CLOCK_SPDIFIN;
+ DE_ACT(("Set Gina20 clock to SPDIF\n"));
+ chip->input_clock = clock;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+
+/* Set input bus gain (one unit is 0.5dB !) */
+static int set_input_gain(struct echoaudio *chip, u16 input, int gain)
+{
+ snd_assert(input < num_busses_in(chip), return -EINVAL);
+
+ if (wait_handshake(chip))
+ return -EIO;
+
+ chip->input_gain[input] = gain;
+ gain += GL20_INPUT_GAIN_MAGIC_NUMBER;
+ chip->comm_page->line_in_level[input] = gain;
+ return 0;
+}
+
+
+
+/* Tell the DSP to reread the flags from the comm page */
+static int update_flags(struct echoaudio *chip)
+{
+ if (wait_handshake(chip))
+ return -EIO;
+ clear_handshake(chip);
+ return send_vector(chip, DSP_VC_UPDATE_FLAGS);
+}
+
+
+
+static int set_professional_spdif(struct echoaudio *chip, char prof)
+{
+ DE_ACT(("set_professional_spdif %d\n", prof));
+ if (prof)
+ chip->comm_page->flags |=
+ __constant_cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF);
+ else
+ chip->comm_page->flags &=
+ ~__constant_cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF);
+ chip->professional_spdif = prof;
+ return update_flags(chip);
+}
diff --git a/sound/pci/echoaudio/gina24.c b/sound/pci/echoaudio/gina24.c
new file mode 100644
index 000000000000..e464d720d0bd
--- /dev/null
+++ b/sound/pci/echoaudio/gina24.c
@@ -0,0 +1,123 @@
+/*
+ * ALSA driver for Echoaudio soundcards.
+ * Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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.
+ */
+
+#define ECHO24_FAMILY
+#define ECHOCARD_GINA24
+#define ECHOCARD_NAME "Gina24"
+#define ECHOCARD_HAS_MONITOR
+#define ECHOCARD_HAS_ASIC
+#define ECHOCARD_HAS_INPUT_NOMINAL_LEVEL
+#define ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL
+#define ECHOCARD_HAS_SUPER_INTERLEAVE
+#define ECHOCARD_HAS_DIGITAL_IO
+#define ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE
+#define ECHOCARD_HAS_DIGITAL_MODE_SWITCH
+#define ECHOCARD_HAS_EXTERNAL_CLOCK
+#define ECHOCARD_HAS_ADAT 6
+#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT 0 /* 8 */
+#define PX_DIGITAL_OUT 8 /* 8 */
+#define PX_ANALOG_IN 16 /* 2 */
+#define PX_DIGITAL_IN 18 /* 8 */
+#define PX_NUM 26
+
+/* Bus indexes */
+#define BX_ANALOG_OUT 0 /* 8 */
+#define BX_DIGITAL_OUT 8 /* 8 */
+#define BX_ANALOG_IN 16 /* 2 */
+#define BX_DIGITAL_IN 18 /* 8 */
+#define BX_NUM 26
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+#define FW_361_LOADER 0
+#define FW_GINA24_301_DSP 1
+#define FW_GINA24_361_DSP 2
+#define FW_GINA24_301_ASIC 3
+#define FW_GINA24_361_ASIC 4
+
+static const struct firmware card_fw[] = {
+ {0, "loader_dsp.fw"},
+ {0, "gina24_301_dsp.fw"},
+ {0, "gina24_361_dsp.fw"},
+ {0, "gina24_301_asic.fw"},
+ {0, "gina24_361_asic.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+ {0x1057, 0x1801, 0xECC0, 0x0050, 0, 0, 0}, /* DSP 56301 Gina24 rev.0 */
+ {0x1057, 0x1801, 0xECC0, 0x0051, 0, 0, 0}, /* DSP 56301 Gina24 rev.1 */
+ {0x1057, 0x3410, 0xECC0, 0x0050, 0, 0, 0}, /* DSP 56361 Gina24 rev.0 */
+ {0x1057, 0x3410, 0xECC0, 0x0051, 0, 0, 0}, /* DSP 56361 Gina24 rev.1 */
+ {0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_SYNC_START,
+ .formats = SNDRV_PCM_FMTBIT_U8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_S32_BE,
+ .rates = SNDRV_PCM_RATE_8000_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .channels_min = 1,
+ .channels_max = 8,
+ .buffer_bytes_max = 262144,
+ .period_bytes_min = 32,
+ .period_bytes_max = 131072,
+ .periods_min = 2,
+ .periods_max = 220,
+ /* One page (4k) contains 512 instructions. I don't know if the hw
+ supports lists longer than this. In this case periods_max=220 is a
+ safe limit to make sure the list never exceeds 512 instructions.
+ 220 ~= (512 - 1 - (BUFFER_BYTES_MAX / PAGE_SIZE)) / 2 */
+};
+
+#include "gina24_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio_gml.c"
+#include "echoaudio.c"
diff --git a/sound/pci/echoaudio/gina24_dsp.c b/sound/pci/echoaudio/gina24_dsp.c
new file mode 100644
index 000000000000..144fc567becf
--- /dev/null
+++ b/sound/pci/echoaudio/gina24_dsp.c
@@ -0,0 +1,346 @@
+/****************************************************************************
+
+ Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+ All rights reserved
+ www.echoaudio.com
+
+ This file is part of Echo Digital Audio's generic driver library.
+
+ Echo Digital Audio's generic driver library is free software;
+ you can redistribute it and/or modify it under the terms of
+ the GNU General Public License as published by the Free Software
+ Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ 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.
+
+ *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+static int write_control_reg(struct echoaudio *chip, u32 value, char force);
+static int set_input_clock(struct echoaudio *chip, u16 clock);
+static int set_professional_spdif(struct echoaudio *chip, char prof);
+static int set_digital_mode(struct echoaudio *chip, u8 mode);
+static int load_asic_generic(struct echoaudio *chip, u32 cmd,
+ const struct firmware *asic);
+static int check_asic_status(struct echoaudio *chip);
+
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+ int err;
+
+ DE_INIT(("init_hw() - Gina24\n"));
+ snd_assert((subdevice_id & 0xfff0) == GINA24, return -ENODEV);
+
+ if ((err = init_dsp_comm_page(chip))) {
+ DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+ return err;
+ }
+
+ chip->device_id = device_id;
+ chip->subdevice_id = subdevice_id;
+ chip->bad_board = TRUE;
+ chip->input_clock_types =
+ ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF |
+ ECHO_CLOCK_BIT_ESYNC | ECHO_CLOCK_BIT_ESYNC96 |
+ ECHO_CLOCK_BIT_ADAT;
+ chip->professional_spdif = FALSE;
+ chip->digital_in_automute = TRUE;
+ chip->digital_mode = DIGITAL_MODE_SPDIF_RCA;
+
+ /* Gina24 comes in both '301 and '361 flavors */
+ if (chip->device_id == DEVICE_ID_56361) {
+ chip->dsp_code_to_load = &card_fw[FW_GINA24_361_DSP];
+ chip->digital_modes =
+ ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA |
+ ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL |
+ ECHOCAPS_HAS_DIGITAL_MODE_ADAT;
+ } else {
+ chip->dsp_code_to_load = &card_fw[FW_GINA24_301_DSP];
+ chip->digital_modes =
+ ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA |
+ ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL |
+ ECHOCAPS_HAS_DIGITAL_MODE_ADAT |
+ ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_CDROM;
+ }
+
+ if ((err = load_firmware(chip)) < 0)
+ return err;
+ chip->bad_board = FALSE;
+
+ if ((err = init_line_levels(chip)) < 0)
+ return err;
+ err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA);
+ snd_assert(err >= 0, return err);
+ err = set_professional_spdif(chip, TRUE);
+
+ DE_INIT(("init_hw done\n"));
+ return err;
+}
+
+
+
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+ u32 clocks_from_dsp, clock_bits;
+
+ /* Map the DSP clock detect bits to the generic driver clock
+ detect bits */
+ clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks);
+
+ clock_bits = ECHO_CLOCK_BIT_INTERNAL;
+
+ if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_SPDIF)
+ clock_bits |= ECHO_CLOCK_BIT_SPDIF;
+
+ if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_ADAT)
+ clock_bits |= ECHO_CLOCK_BIT_ADAT;
+
+ if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_ESYNC)
+ clock_bits |= ECHO_CLOCK_BIT_ESYNC | ECHO_CLOCK_BIT_ESYNC96;
+
+ return clock_bits;
+}
+
+
+
+/* Gina24 has an ASIC on the PCI card which must be loaded for anything
+interesting to happen. */
+static int load_asic(struct echoaudio *chip)
+{
+ u32 control_reg;
+ int err;
+ const struct firmware *fw;
+
+ if (chip->asic_loaded)
+ return 1;
+
+ /* Give the DSP a few milliseconds to settle down */
+ mdelay(10);
+
+ /* Pick the correct ASIC for '301 or '361 Gina24 */
+ if (chip->device_id == DEVICE_ID_56361)
+ fw = &card_fw[FW_GINA24_361_ASIC];
+ else
+ fw = &card_fw[FW_GINA24_301_ASIC];
+
+ if ((err = load_asic_generic(chip, DSP_FNC_LOAD_GINA24_ASIC, fw)) < 0)
+ return err;
+
+ chip->asic_code = fw;
+
+ /* Now give the new ASIC a little time to set up */
+ mdelay(10);
+ /* See if it worked */
+ err = check_asic_status(chip);
+
+ /* Set up the control register if the load succeeded -
+ 48 kHz, internal clock, S/PDIF RCA mode */
+ if (!err) {
+ control_reg = GML_CONVERTER_ENABLE | GML_48KHZ;
+ err = write_control_reg(chip, control_reg, TRUE);
+ }
+ DE_INIT(("load_asic() done\n"));
+ return err;
+}
+
+
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+ u32 control_reg, clock;
+
+ snd_assert(rate < 50000 || chip->digital_mode != DIGITAL_MODE_ADAT,
+ return -EINVAL);
+
+ /* Only set the clock for internal mode. */
+ if (chip->input_clock != ECHO_CLOCK_INTERNAL) {
+ DE_ACT(("set_sample_rate: Cannot set sample rate - "
+ "clock not set to CLK_CLOCKININTERNAL\n"));
+ /* Save the rate anyhow */
+ chip->comm_page->sample_rate = cpu_to_le32(rate);
+ chip->sample_rate = rate;
+ return 0;
+ }
+
+ clock = 0;
+
+ control_reg = le32_to_cpu(chip->comm_page->control_register);
+ control_reg &= GML_CLOCK_CLEAR_MASK & GML_SPDIF_RATE_CLEAR_MASK;
+
+ switch (rate) {
+ case 96000:
+ clock = GML_96KHZ;
+ break;
+ case 88200:
+ clock = GML_88KHZ;
+ break;
+ case 48000:
+ clock = GML_48KHZ | GML_SPDIF_SAMPLE_RATE1;
+ break;
+ case 44100:
+ clock = GML_44KHZ;
+ /* Professional mode ? */
+ if (control_reg & GML_SPDIF_PRO_MODE)
+ clock |= GML_SPDIF_SAMPLE_RATE0;
+ break;
+ case 32000:
+ clock = GML_32KHZ | GML_SPDIF_SAMPLE_RATE0 |
+ GML_SPDIF_SAMPLE_RATE1;
+ break;
+ case 22050:
+ clock = GML_22KHZ;
+ break;
+ case 16000:
+ clock = GML_16KHZ;
+ break;
+ case 11025:
+ clock = GML_11KHZ;
+ break;
+ case 8000:
+ clock = GML_8KHZ;
+ break;
+ default:
+ DE_ACT(("set_sample_rate: %d invalid!\n", rate));
+ return -EINVAL;
+ }
+
+ control_reg |= clock;
+
+ chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP */
+ chip->sample_rate = rate;
+ DE_ACT(("set_sample_rate: %d clock %d\n", rate, clock));
+
+ return write_control_reg(chip, control_reg, FALSE);
+}
+
+
+
+static int set_input_clock(struct echoaudio *chip, u16 clock)
+{
+ u32 control_reg, clocks_from_dsp;
+
+ DE_ACT(("set_input_clock:\n"));
+
+ /* Mask off the clock select bits */
+ control_reg = le32_to_cpu(chip->comm_page->control_register) &
+ GML_CLOCK_CLEAR_MASK;
+ clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks);
+
+ switch (clock) {
+ case ECHO_CLOCK_INTERNAL:
+ DE_ACT(("Set Gina24 clock to INTERNAL\n"));
+ chip->input_clock = ECHO_CLOCK_INTERNAL;
+ return set_sample_rate(chip, chip->sample_rate);
+ case ECHO_CLOCK_SPDIF:
+ if (chip->digital_mode == DIGITAL_MODE_ADAT)
+ return -EAGAIN;
+ DE_ACT(("Set Gina24 clock to SPDIF\n"));
+ control_reg |= GML_SPDIF_CLOCK;
+ if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_SPDIF96)
+ control_reg |= GML_DOUBLE_SPEED_MODE;
+ else
+ control_reg &= ~GML_DOUBLE_SPEED_MODE;
+ break;
+ case ECHO_CLOCK_ADAT:
+ if (chip->digital_mode != DIGITAL_MODE_ADAT)
+ return -EAGAIN;
+ DE_ACT(("Set Gina24 clock to ADAT\n"));
+ control_reg |= GML_ADAT_CLOCK;
+ control_reg &= ~GML_DOUBLE_SPEED_MODE;
+ break;
+ case ECHO_CLOCK_ESYNC:
+ DE_ACT(("Set Gina24 clock to ESYNC\n"));
+ control_reg |= GML_ESYNC_CLOCK;
+ control_reg &= ~GML_DOUBLE_SPEED_MODE;
+ break;
+ case ECHO_CLOCK_ESYNC96:
+ DE_ACT(("Set Gina24 clock to ESYNC96\n"));
+ control_reg |= GML_ESYNC_CLOCK | GML_DOUBLE_SPEED_MODE;
+ break;
+ default:
+ DE_ACT(("Input clock 0x%x not supported for Gina24\n", clock));
+ return -EINVAL;
+ }
+
+ chip->input_clock = clock;
+ return write_control_reg(chip, control_reg, TRUE);
+}
+
+
+
+static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
+{
+ u32 control_reg;
+ int err, incompatible_clock;
+
+ /* Set clock to "internal" if it's not compatible with the new mode */
+ incompatible_clock = FALSE;
+ switch (mode) {
+ case DIGITAL_MODE_SPDIF_OPTICAL:
+ case DIGITAL_MODE_SPDIF_CDROM:
+ case DIGITAL_MODE_SPDIF_RCA:
+ if (chip->input_clock == ECHO_CLOCK_ADAT)
+ incompatible_clock = TRUE;
+ break;
+ case DIGITAL_MODE_ADAT:
+ if (chip->input_clock == ECHO_CLOCK_SPDIF)
+ incompatible_clock = TRUE;
+ break;
+ default:
+ DE_ACT(("Digital mode not supported: %d\n", mode));
+ return -EINVAL;
+ }
+
+ spin_lock_irq(&chip->lock);
+
+ if (incompatible_clock) { /* Switch to 48KHz, internal */
+ chip->sample_rate = 48000;
+ set_input_clock(chip, ECHO_CLOCK_INTERNAL);
+ }
+
+ /* Clear the current digital mode */
+ control_reg = le32_to_cpu(chip->comm_page->control_register);
+ control_reg &= GML_DIGITAL_MODE_CLEAR_MASK;
+
+ /* Tweak the control reg */
+ switch (mode) {
+ case DIGITAL_MODE_SPDIF_OPTICAL:
+ control_reg |= GML_SPDIF_OPTICAL_MODE;
+ break;
+ case DIGITAL_MODE_SPDIF_CDROM:
+ /* '361 Gina24 cards do not have the S/PDIF CD-ROM mode */
+ if (chip->device_id == DEVICE_ID_56301)
+ control_reg |= GML_SPDIF_CDROM_MODE;
+ break;
+ case DIGITAL_MODE_SPDIF_RCA:
+ /* GML_SPDIF_OPTICAL_MODE bit cleared */
+ break;
+ case DIGITAL_MODE_ADAT:
+ control_reg |= GML_ADAT_MODE;
+ control_reg &= ~GML_DOUBLE_SPEED_MODE;
+ break;
+ }
+
+ err = write_control_reg(chip, control_reg, TRUE);
+ spin_unlock_irq(&chip->lock);
+ if (err < 0)
+ return err;
+ chip->digital_mode = mode;
+
+ DE_ACT(("set_digital_mode to %d\n", chip->digital_mode));
+ return incompatible_clock;
+}
diff --git a/sound/pci/echoaudio/indigo.c b/sound/pci/echoaudio/indigo.c
new file mode 100644
index 000000000000..bfd2467099ac
--- /dev/null
+++ b/sound/pci/echoaudio/indigo.c
@@ -0,0 +1,104 @@
+/*
+ * ALSA driver for Echoaudio soundcards.
+ * Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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.
+ */
+
+#define INDIGO_FAMILY
+#define ECHOCARD_INDIGO
+#define ECHOCARD_NAME "Indigo"
+#define ECHOCARD_HAS_SUPER_INTERLEAVE
+#define ECHOCARD_HAS_VMIXER
+#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT 0 /* 8 */
+#define PX_DIGITAL_OUT 8 /* 0 */
+#define PX_ANALOG_IN 8 /* 0 */
+#define PX_DIGITAL_IN 8 /* 0 */
+#define PX_NUM 8
+
+/* Bus indexes */
+#define BX_ANALOG_OUT 0 /* 2 */
+#define BX_DIGITAL_OUT 2 /* 0 */
+#define BX_ANALOG_IN 2 /* 0 */
+#define BX_DIGITAL_IN 2 /* 0 */
+#define BX_NUM 2
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+#define FW_361_LOADER 0
+#define FW_INDIGO_DSP 1
+
+static const struct firmware card_fw[] = {
+ {0, "loader_dsp.fw"},
+ {0, "indigo_dsp.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+ {0x1057, 0x3410, 0xECC0, 0x0090, 0, 0, 0}, /* Indigo */
+ {0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_SYNC_START,
+ .formats = SNDRV_PCM_FMTBIT_U8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_S32_BE,
+ .rates = SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 32000,
+ .rate_max = 96000,
+ .channels_min = 1,
+ .channels_max = 8,
+ .buffer_bytes_max = 262144,
+ .period_bytes_min = 32,
+ .period_bytes_max = 131072,
+ .periods_min = 2,
+ .periods_max = 220,
+};
+
+#include "indigo_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio.c"
+
diff --git a/sound/pci/echoaudio/indigo_dsp.c b/sound/pci/echoaudio/indigo_dsp.c
new file mode 100644
index 000000000000..d6ac7734609e
--- /dev/null
+++ b/sound/pci/echoaudio/indigo_dsp.c
@@ -0,0 +1,170 @@
+/****************************************************************************
+
+ Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+ All rights reserved
+ www.echoaudio.com
+
+ This file is part of Echo Digital Audio's generic driver library.
+
+ Echo Digital Audio's generic driver library is free software;
+ you can redistribute it and/or modify it under the terms of
+ the GNU General Public License as published by the Free Software
+ Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ 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.
+
+ *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
+ int gain);
+static int update_vmixer_level(struct echoaudio *chip);
+
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+ int err;
+
+ DE_INIT(("init_hw() - Indigo\n"));
+ snd_assert((subdevice_id & 0xfff0) == INDIGO, return -ENODEV);
+
+ if ((err = init_dsp_comm_page(chip))) {
+ DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+ return err;
+ }
+
+ chip->device_id = device_id;
+ chip->subdevice_id = subdevice_id;
+ chip->bad_board = TRUE;
+ chip->dsp_code_to_load = &card_fw[FW_INDIGO_DSP];
+ /* Since this card has no ASIC, mark it as loaded so everything
+ works OK */
+ chip->asic_loaded = TRUE;
+ chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL;
+
+ if ((err = load_firmware(chip)) < 0)
+ return err;
+ chip->bad_board = FALSE;
+
+ if ((err = init_line_levels(chip)) < 0)
+ return err;
+
+ /* Default routing of the virtual channels: all vchannels are routed
+ to the stereo output */
+ set_vmixer_gain(chip, 0, 0, 0);
+ set_vmixer_gain(chip, 1, 1, 0);
+ set_vmixer_gain(chip, 0, 2, 0);
+ set_vmixer_gain(chip, 1, 3, 0);
+ set_vmixer_gain(chip, 0, 4, 0);
+ set_vmixer_gain(chip, 1, 5, 0);
+ set_vmixer_gain(chip, 0, 6, 0);
+ set_vmixer_gain(chip, 1, 7, 0);
+ err = update_vmixer_level(chip);
+
+ DE_INIT(("init_hw done\n"));
+ return err;
+}
+
+
+
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+ return ECHO_CLOCK_BIT_INTERNAL;
+}
+
+
+
+/* The Indigo has no ASIC. Just do nothing */
+static int load_asic(struct echoaudio *chip)
+{
+ return 0;
+}
+
+
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+ u32 control_reg;
+
+ switch (rate) {
+ case 96000:
+ control_reg = MIA_96000;
+ break;
+ case 88200:
+ control_reg = MIA_88200;
+ break;
+ case 48000:
+ control_reg = MIA_48000;
+ break;
+ case 44100:
+ control_reg = MIA_44100;
+ break;
+ case 32000:
+ control_reg = MIA_32000;
+ break;
+ default:
+ DE_ACT(("set_sample_rate: %d invalid!\n", rate));
+ return -EINVAL;
+ }
+
+ /* Set the control register if it has changed */
+ if (control_reg != le32_to_cpu(chip->comm_page->control_register)) {
+ if (wait_handshake(chip))
+ return -EIO;
+
+ chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP */
+ chip->comm_page->control_register = cpu_to_le32(control_reg);
+ chip->sample_rate = rate;
+
+ clear_handshake(chip);
+ return send_vector(chip, DSP_VC_UPDATE_CLOCKS);
+ }
+ return 0;
+}
+
+
+
+/* This function routes the sound from a virtual channel to a real output */
+static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
+ int gain)
+{
+ int index;
+
+ snd_assert(pipe < num_pipes_out(chip) &&
+ output < num_busses_out(chip), return -EINVAL);
+
+ if (wait_handshake(chip))
+ return -EIO;
+
+ chip->vmixer_gain[output][pipe] = gain;
+ index = output * num_pipes_out(chip) + pipe;
+ chip->comm_page->vmixer[index] = gain;
+
+ DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain));
+ return 0;
+}
+
+
+
+/* Tell the DSP to read and update virtual mixer levels in comm page. */
+static int update_vmixer_level(struct echoaudio *chip)
+{
+ if (wait_handshake(chip))
+ return -EIO;
+ clear_handshake(chip);
+ return send_vector(chip, DSP_VC_SET_VMIXER_GAIN);
+}
+
diff --git a/sound/pci/echoaudio/indigodj.c b/sound/pci/echoaudio/indigodj.c
new file mode 100644
index 000000000000..8ed7ff1fd875
--- /dev/null
+++ b/sound/pci/echoaudio/indigodj.c
@@ -0,0 +1,104 @@
+/*
+ * ALSA driver for Echoaudio soundcards.
+ * Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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.
+ */
+
+#define INDIGO_FAMILY
+#define ECHOCARD_INDIGO_DJ
+#define ECHOCARD_NAME "Indigo DJ"
+#define ECHOCARD_HAS_SUPER_INTERLEAVE
+#define ECHOCARD_HAS_VMIXER
+#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT 0 /* 8 */
+#define PX_DIGITAL_OUT 8 /* 0 */
+#define PX_ANALOG_IN 8 /* 0 */
+#define PX_DIGITAL_IN 8 /* 0 */
+#define PX_NUM 8
+
+/* Bus indexes */
+#define BX_ANALOG_OUT 0 /* 4 */
+#define BX_DIGITAL_OUT 4 /* 0 */
+#define BX_ANALOG_IN 4 /* 0 */
+#define BX_DIGITAL_IN 4 /* 0 */
+#define BX_NUM 4
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+#define FW_361_LOADER 0
+#define FW_INDIGO_DJ_DSP 1
+
+static const struct firmware card_fw[] = {
+ {0, "loader_dsp.fw"},
+ {0, "indigo_dj_dsp.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+ {0x1057, 0x3410, 0xECC0, 0x00B0, 0, 0, 0}, /* Indigo DJ*/
+ {0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_SYNC_START,
+ .formats = SNDRV_PCM_FMTBIT_U8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_S32_BE,
+ .rates = SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 32000,
+ .rate_max = 96000,
+ .channels_min = 1,
+ .channels_max = 4,
+ .buffer_bytes_max = 262144,
+ .period_bytes_min = 32,
+ .period_bytes_max = 131072,
+ .periods_min = 2,
+ .periods_max = 220,
+};
+
+#include "indigodj_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio.c"
+
diff --git a/sound/pci/echoaudio/indigodj_dsp.c b/sound/pci/echoaudio/indigodj_dsp.c
new file mode 100644
index 000000000000..500e150b49fc
--- /dev/null
+++ b/sound/pci/echoaudio/indigodj_dsp.c
@@ -0,0 +1,170 @@
+/****************************************************************************
+
+ Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+ All rights reserved
+ www.echoaudio.com
+
+ This file is part of Echo Digital Audio's generic driver library.
+
+ Echo Digital Audio's generic driver library is free software;
+ you can redistribute it and/or modify it under the terms of
+ the GNU General Public License as published by the Free Software
+ Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ 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.
+
+ *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
+ int gain);
+static int update_vmixer_level(struct echoaudio *chip);
+
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+ int err;
+
+ DE_INIT(("init_hw() - Indigo DJ\n"));
+ snd_assert((subdevice_id & 0xfff0) == INDIGO_DJ, return -ENODEV);
+
+ if ((err = init_dsp_comm_page(chip))) {
+ DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+ return err;
+ }
+
+ chip->device_id = device_id;
+ chip->subdevice_id = subdevice_id;
+ chip->bad_board = TRUE;
+ chip->dsp_code_to_load = &card_fw[FW_INDIGO_DJ_DSP];
+ /* Since this card has no ASIC, mark it as loaded so everything
+ works OK */
+ chip->asic_loaded = TRUE;
+ chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL;
+
+ if ((err = load_firmware(chip)) < 0)
+ return err;
+ chip->bad_board = FALSE;
+
+ if ((err = init_line_levels(chip)) < 0)
+ return err;
+
+ /* Default routing of the virtual channels: vchannels 0-3 and
+ vchannels 4-7 are routed to real channels 0-4 */
+ set_vmixer_gain(chip, 0, 0, 0);
+ set_vmixer_gain(chip, 1, 1, 0);
+ set_vmixer_gain(chip, 2, 2, 0);
+ set_vmixer_gain(chip, 3, 3, 0);
+ set_vmixer_gain(chip, 0, 4, 0);
+ set_vmixer_gain(chip, 1, 5, 0);
+ set_vmixer_gain(chip, 2, 6, 0);
+ set_vmixer_gain(chip, 3, 7, 0);
+ err = update_vmixer_level(chip);
+
+ DE_INIT(("init_hw done\n"));
+ return err;
+}
+
+
+
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+ return ECHO_CLOCK_BIT_INTERNAL;
+}
+
+
+
+/* The IndigoDJ has no ASIC. Just do nothing */
+static int load_asic(struct echoaudio *chip)
+{
+ return 0;
+}
+
+
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+ u32 control_reg;
+
+ switch (rate) {
+ case 96000:
+ control_reg = MIA_96000;
+ break;
+ case 88200:
+ control_reg = MIA_88200;
+ break;
+ case 48000:
+ control_reg = MIA_48000;
+ break;
+ case 44100:
+ control_reg = MIA_44100;
+ break;
+ case 32000:
+ control_reg = MIA_32000;
+ break;
+ default:
+ DE_ACT(("set_sample_rate: %d invalid!\n", rate));
+ return -EINVAL;
+ }
+
+ /* Set the control register if it has changed */
+ if (control_reg != le32_to_cpu(chip->comm_page->control_register)) {
+ if (wait_handshake(chip))
+ return -EIO;
+
+ chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP */
+ chip->comm_page->control_register = cpu_to_le32(control_reg);
+ chip->sample_rate = rate;
+
+ clear_handshake(chip);
+ return send_vector(chip, DSP_VC_UPDATE_CLOCKS);
+ }
+ return 0;
+}
+
+
+
+/* This function routes the sound from a virtual channel to a real output */
+static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
+ int gain)
+{
+ int index;
+
+ snd_assert(pipe < num_pipes_out(chip) &&
+ output < num_busses_out(chip), return -EINVAL);
+
+ if (wait_handshake(chip))
+ return -EIO;
+
+ chip->vmixer_gain[output][pipe] = gain;
+ index = output * num_pipes_out(chip) + pipe;
+ chip->comm_page->vmixer[index] = gain;
+
+ DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain));
+ return 0;
+}
+
+
+
+/* Tell the DSP to read and update virtual mixer levels in comm page. */
+static int update_vmixer_level(struct echoaudio *chip)
+{
+ if (wait_handshake(chip))
+ return -EIO;
+ clear_handshake(chip);
+ return send_vector(chip, DSP_VC_SET_VMIXER_GAIN);
+}
+
diff --git a/sound/pci/echoaudio/indigoio.c b/sound/pci/echoaudio/indigoio.c
new file mode 100644
index 000000000000..a8788e959171
--- /dev/null
+++ b/sound/pci/echoaudio/indigoio.c
@@ -0,0 +1,105 @@
+/*
+ * ALSA driver for Echoaudio soundcards.
+ * Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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.
+ */
+
+#define INDIGO_FAMILY
+#define ECHOCARD_INDIGO_IO
+#define ECHOCARD_NAME "Indigo IO"
+#define ECHOCARD_HAS_MONITOR
+#define ECHOCARD_HAS_SUPER_INTERLEAVE
+#define ECHOCARD_HAS_VMIXER
+#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT 0 /* 8 */
+#define PX_DIGITAL_OUT 8 /* 0 */
+#define PX_ANALOG_IN 8 /* 2 */
+#define PX_DIGITAL_IN 10 /* 0 */
+#define PX_NUM 10
+
+/* Bus indexes */
+#define BX_ANALOG_OUT 0 /* 2 */
+#define BX_DIGITAL_OUT 2 /* 0 */
+#define BX_ANALOG_IN 2 /* 2 */
+#define BX_DIGITAL_IN 4 /* 0 */
+#define BX_NUM 4
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+#define FW_361_LOADER 0
+#define FW_INDIGO_IO_DSP 1
+
+static const struct firmware card_fw[] = {
+ {0, "loader_dsp.fw"},
+ {0, "indigo_io_dsp.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+ {0x1057, 0x3410, 0xECC0, 0x00A0, 0, 0, 0}, /* Indigo IO*/
+ {0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_SYNC_START,
+ .formats = SNDRV_PCM_FMTBIT_U8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_S32_BE,
+ .rates = SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 32000,
+ .rate_max = 96000,
+ .channels_min = 1,
+ .channels_max = 8,
+ .buffer_bytes_max = 262144,
+ .period_bytes_min = 32,
+ .period_bytes_max = 131072,
+ .periods_min = 2,
+ .periods_max = 220,
+};
+
+#include "indigoio_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio.c"
+
diff --git a/sound/pci/echoaudio/indigoio_dsp.c b/sound/pci/echoaudio/indigoio_dsp.c
new file mode 100644
index 000000000000..f3ad13d06be0
--- /dev/null
+++ b/sound/pci/echoaudio/indigoio_dsp.c
@@ -0,0 +1,141 @@
+/****************************************************************************
+
+ Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+ All rights reserved
+ www.echoaudio.com
+
+ This file is part of Echo Digital Audio's generic driver library.
+
+ Echo Digital Audio's generic driver library is free software;
+ you can redistribute it and/or modify it under the terms of
+ the GNU General Public License as published by the Free Software
+ Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ 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.
+
+ *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
+ int gain);
+static int update_vmixer_level(struct echoaudio *chip);
+
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+ int err;
+
+ DE_INIT(("init_hw() - Indigo IO\n"));
+ snd_assert((subdevice_id & 0xfff0) == INDIGO_IO, return -ENODEV);
+
+ if ((err = init_dsp_comm_page(chip))) {
+ DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+ return err;
+ }
+
+ chip->device_id = device_id;
+ chip->subdevice_id = subdevice_id;
+ chip->bad_board = TRUE;
+ chip->dsp_code_to_load = &card_fw[FW_INDIGO_IO_DSP];
+ /* Since this card has no ASIC, mark it as loaded so everything
+ works OK */
+ chip->asic_loaded = TRUE;
+ chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL;
+
+ if ((err = load_firmware(chip)) < 0)
+ return err;
+ chip->bad_board = FALSE;
+
+ if ((err = init_line_levels(chip)) < 0)
+ return err;
+
+ /* Default routing of the virtual channels: all vchannels are routed
+ to the stereo output */
+ set_vmixer_gain(chip, 0, 0, 0);
+ set_vmixer_gain(chip, 1, 1, 0);
+ set_vmixer_gain(chip, 0, 2, 0);
+ set_vmixer_gain(chip, 1, 3, 0);
+ set_vmixer_gain(chip, 0, 4, 0);
+ set_vmixer_gain(chip, 1, 5, 0);
+ set_vmixer_gain(chip, 0, 6, 0);
+ set_vmixer_gain(chip, 1, 7, 0);
+ err = update_vmixer_level(chip);
+
+ DE_INIT(("init_hw done\n"));
+ return err;
+}
+
+
+
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+ return ECHO_CLOCK_BIT_INTERNAL;
+}
+
+
+
+/* The IndigoIO has no ASIC. Just do nothing */
+static int load_asic(struct echoaudio *chip)
+{
+ return 0;
+}
+
+
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+ if (wait_handshake(chip))
+ return -EIO;
+
+ chip->sample_rate = rate;
+ chip->comm_page->sample_rate = cpu_to_le32(rate);
+ clear_handshake(chip);
+ return send_vector(chip, DSP_VC_UPDATE_CLOCKS);
+}
+
+
+
+/* This function routes the sound from a virtual channel to a real output */
+static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
+ int gain)
+{
+ int index;
+
+ snd_assert(pipe < num_pipes_out(chip) &&
+ output < num_busses_out(chip), return -EINVAL);
+
+ if (wait_handshake(chip))
+ return -EIO;
+
+ chip->vmixer_gain[output][pipe] = gain;
+ index = output * num_pipes_out(chip) + pipe;
+ chip->comm_page->vmixer[index] = gain;
+
+ DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain));
+ return 0;
+}
+
+
+
+/* Tell the DSP to read and update virtual mixer levels in comm page. */
+static int update_vmixer_level(struct echoaudio *chip)
+{
+ if (wait_handshake(chip))
+ return -EIO;
+ clear_handshake(chip);
+ return send_vector(chip, DSP_VC_SET_VMIXER_GAIN);
+}
+
diff --git a/sound/pci/echoaudio/layla20.c b/sound/pci/echoaudio/layla20.c
new file mode 100644
index 000000000000..e503d74b3ba9
--- /dev/null
+++ b/sound/pci/echoaudio/layla20.c
@@ -0,0 +1,112 @@
+/*
+ * ALSA driver for Echoaudio soundcards.
+ * Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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.
+ */
+
+#define ECHOGALS_FAMILY
+#define ECHOCARD_LAYLA20
+#define ECHOCARD_NAME "Layla20"
+#define ECHOCARD_HAS_MONITOR
+#define ECHOCARD_HAS_ASIC
+#define ECHOCARD_HAS_INPUT_GAIN
+#define ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL
+#define ECHOCARD_HAS_SUPER_INTERLEAVE
+#define ECHOCARD_HAS_DIGITAL_IO
+#define ECHOCARD_HAS_EXTERNAL_CLOCK
+#define ECHOCARD_HAS_ADAT FALSE
+#define ECHOCARD_HAS_OUTPUT_CLOCK_SWITCH
+#define ECHOCARD_HAS_MIDI
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT 0 /* 10 */
+#define PX_DIGITAL_OUT 10 /* 2 */
+#define PX_ANALOG_IN 12 /* 8 */
+#define PX_DIGITAL_IN 20 /* 2 */
+#define PX_NUM 22
+
+/* Bus indexes */
+#define BX_ANALOG_OUT 0 /* 10 */
+#define BX_DIGITAL_OUT 10 /* 2 */
+#define BX_ANALOG_IN 12 /* 8 */
+#define BX_DIGITAL_IN 20 /* 2 */
+#define BX_NUM 22
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <sound/rawmidi.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+#define FW_LAYLA20_DSP 0
+#define FW_LAYLA20_ASIC 1
+
+static const struct firmware card_fw[] = {
+ {0, "layla20_dsp.fw"},
+ {0, "layla20_asic.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+ {0x1057, 0x1801, 0xECC0, 0x0030, 0, 0, 0}, /* DSP 56301 Layla20 rev.0 */
+ {0x1057, 0x1801, 0xECC0, 0x0031, 0, 0, 0}, /* DSP 56301 Layla20 rev.1 */
+ {0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_SYNC_START,
+ .formats = SNDRV_PCM_FMTBIT_U8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_S32_BE,
+ .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 8000,
+ .rate_max = 50000,
+ .channels_min = 1,
+ .channels_max = 10,
+ .buffer_bytes_max = 262144,
+ .period_bytes_min = 32,
+ .period_bytes_max = 131072,
+ .periods_min = 2,
+ .periods_max = 220,
+ /* One page (4k) contains 512 instructions. I don't know if the hw
+ supports lists longer than this. In this case periods_max=220 is a
+ safe limit to make sure the list never exceeds 512 instructions. */
+};
+
+#include "layla20_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio.c"
+#include "midi.c"
diff --git a/sound/pci/echoaudio/layla20_dsp.c b/sound/pci/echoaudio/layla20_dsp.c
new file mode 100644
index 000000000000..990c9a60a0a8
--- /dev/null
+++ b/sound/pci/echoaudio/layla20_dsp.c
@@ -0,0 +1,290 @@
+/****************************************************************************
+
+ Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+ All rights reserved
+ www.echoaudio.com
+
+ This file is part of Echo Digital Audio's generic driver library.
+
+ Echo Digital Audio's generic driver library is free software;
+ you can redistribute it and/or modify it under the terms of
+ the GNU General Public License as published by the Free Software
+ Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ 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.
+
+ *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+static int read_dsp(struct echoaudio *chip, u32 *data);
+static int set_professional_spdif(struct echoaudio *chip, char prof);
+static int load_asic_generic(struct echoaudio *chip, u32 cmd,
+ const struct firmware *asic);
+static int check_asic_status(struct echoaudio *chip);
+static int update_flags(struct echoaudio *chip);
+
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+ int err;
+
+ DE_INIT(("init_hw() - Layla20\n"));
+ snd_assert((subdevice_id & 0xfff0) == LAYLA20, return -ENODEV);
+
+ if ((err = init_dsp_comm_page(chip))) {
+ DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+ return err;
+ }
+
+ chip->device_id = device_id;
+ chip->subdevice_id = subdevice_id;
+ chip->bad_board = TRUE;
+ chip->has_midi = TRUE;
+ chip->dsp_code_to_load = &card_fw[FW_LAYLA20_DSP];
+ chip->input_clock_types =
+ ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF |
+ ECHO_CLOCK_BIT_WORD | ECHO_CLOCK_BIT_SUPER;
+ chip->output_clock_types =
+ ECHO_CLOCK_BIT_WORD | ECHO_CLOCK_BIT_SUPER;
+
+ if ((err = load_firmware(chip)) < 0)
+ return err;
+ chip->bad_board = FALSE;
+
+ if ((err = init_line_levels(chip)) < 0)
+ return err;
+
+ err = set_professional_spdif(chip, TRUE);
+
+ DE_INIT(("init_hw done\n"));
+ return err;
+}
+
+
+
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+ u32 clocks_from_dsp, clock_bits;
+
+ /* Map the DSP clock detect bits to the generic driver clock detect bits */
+ clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks);
+
+ clock_bits = ECHO_CLOCK_BIT_INTERNAL;
+
+ if (clocks_from_dsp & GLDM_CLOCK_DETECT_BIT_SPDIF)
+ clock_bits |= ECHO_CLOCK_BIT_SPDIF;
+
+ if (clocks_from_dsp & GLDM_CLOCK_DETECT_BIT_WORD) {
+ if (clocks_from_dsp & GLDM_CLOCK_DETECT_BIT_SUPER)
+ clock_bits |= ECHO_CLOCK_BIT_SUPER;
+ else
+ clock_bits |= ECHO_CLOCK_BIT_WORD;
+ }
+
+ return clock_bits;
+}
+
+
+
+/* ASIC status check - some cards have one or two ASICs that need to be
+loaded. Once that load is complete, this function is called to see if
+the load was successful.
+If this load fails, it does not necessarily mean that the hardware is
+defective - the external box may be disconnected or turned off.
+This routine sometimes fails for Layla20; for Layla20, the loop runs
+5 times and succeeds if it wins on three of the loops. */
+static int check_asic_status(struct echoaudio *chip)
+{
+ u32 asic_status;
+ int goodcnt, i;
+
+ chip->asic_loaded = FALSE;
+ for (i = goodcnt = 0; i < 5; i++) {
+ send_vector(chip, DSP_VC_TEST_ASIC);
+
+ /* The DSP will return a value to indicate whether or not
+ the ASIC is currently loaded */
+ if (read_dsp(chip, &asic_status) < 0) {
+ DE_ACT(("check_asic_status: failed on read_dsp\n"));
+ return -EIO;
+ }
+
+ if (asic_status == ASIC_ALREADY_LOADED) {
+ if (++goodcnt == 3) {
+ chip->asic_loaded = TRUE;
+ return 0;
+ }
+ }
+ }
+ return -EIO;
+}
+
+
+
+/* Layla20 has an ASIC in the external box */
+static int load_asic(struct echoaudio *chip)
+{
+ int err;
+
+ if (chip->asic_loaded)
+ return 0;
+
+ err = load_asic_generic(chip, DSP_FNC_LOAD_LAYLA_ASIC,
+ &card_fw[FW_LAYLA20_ASIC]);
+ if (err < 0)
+ return err;
+
+ /* Check if ASIC is alive and well. */
+ return check_asic_status(chip);
+}
+
+
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+ snd_assert(rate >= 8000 && rate <= 50000, return -EINVAL);
+
+ /* Only set the clock for internal mode. Do not return failure,
+ simply treat it as a non-event. */
+ if (chip->input_clock != ECHO_CLOCK_INTERNAL) {
+ DE_ACT(("set_sample_rate: Cannot set sample rate - "
+ "clock not set to CLK_CLOCKININTERNAL\n"));
+ chip->comm_page->sample_rate = cpu_to_le32(rate);
+ chip->sample_rate = rate;
+ return 0;
+ }
+
+ if (wait_handshake(chip))
+ return -EIO;
+
+ DE_ACT(("set_sample_rate(%d)\n", rate));
+ chip->sample_rate = rate;
+ chip->comm_page->sample_rate = cpu_to_le32(rate);
+ clear_handshake(chip);
+ return send_vector(chip, DSP_VC_SET_LAYLA_SAMPLE_RATE);
+}
+
+
+
+static int set_input_clock(struct echoaudio *chip, u16 clock_source)
+{
+ u16 clock;
+ u32 rate;
+
+ DE_ACT(("set_input_clock:\n"));
+ rate = 0;
+ switch (clock_source) {
+ case ECHO_CLOCK_INTERNAL:
+ DE_ACT(("Set Layla20 clock to INTERNAL\n"));
+ rate = chip->sample_rate;
+ clock = LAYLA20_CLOCK_INTERNAL;
+ break;
+ case ECHO_CLOCK_SPDIF:
+ DE_ACT(("Set Layla20 clock to SPDIF\n"));
+ clock = LAYLA20_CLOCK_SPDIF;
+ break;
+ case ECHO_CLOCK_WORD:
+ DE_ACT(("Set Layla20 clock to WORD\n"));
+ clock = LAYLA20_CLOCK_WORD;
+ break;
+ case ECHO_CLOCK_SUPER:
+ DE_ACT(("Set Layla20 clock to SUPER\n"));
+ clock = LAYLA20_CLOCK_SUPER;
+ break;
+ default:
+ DE_ACT(("Input clock 0x%x not supported for Layla24\n",
+ clock_source));
+ return -EINVAL;
+ }
+ chip->input_clock = clock_source;
+
+ chip->comm_page->input_clock = cpu_to_le16(clock);
+ clear_handshake(chip);
+ send_vector(chip, DSP_VC_UPDATE_CLOCKS);
+
+ if (rate)
+ set_sample_rate(chip, rate);
+
+ return 0;
+}
+
+
+
+static int set_output_clock(struct echoaudio *chip, u16 clock)
+{
+ DE_ACT(("set_output_clock: %d\n", clock));
+ switch (clock) {
+ case ECHO_CLOCK_SUPER:
+ clock = LAYLA20_OUTPUT_CLOCK_SUPER;
+ break;
+ case ECHO_CLOCK_WORD:
+ clock = LAYLA20_OUTPUT_CLOCK_WORD;
+ break;
+ default:
+ DE_ACT(("set_output_clock wrong clock\n"));
+ return -EINVAL;
+ }
+
+ if (wait_handshake(chip))
+ return -EIO;
+
+ chip->comm_page->output_clock = cpu_to_le16(clock);
+ chip->output_clock = clock;
+ clear_handshake(chip);
+ return send_vector(chip, DSP_VC_UPDATE_CLOCKS);
+}
+
+
+
+/* Set input bus gain (one unit is 0.5dB !) */
+static int set_input_gain(struct echoaudio *chip, u16 input, int gain)
+{
+ snd_assert(input < num_busses_in(chip), return -EINVAL);
+
+ if (wait_handshake(chip))
+ return -EIO;
+
+ chip->input_gain[input] = gain;
+ gain += GL20_INPUT_GAIN_MAGIC_NUMBER;
+ chip->comm_page->line_in_level[input] = gain;
+ return 0;
+}
+
+
+
+/* Tell the DSP to reread the flags from the comm page */
+static int update_flags(struct echoaudio *chip)
+{
+ if (wait_handshake(chip))
+ return -EIO;
+ clear_handshake(chip);
+ return send_vector(chip, DSP_VC_UPDATE_FLAGS);
+}
+
+
+
+static int set_professional_spdif(struct echoaudio *chip, char prof)
+{
+ DE_ACT(("set_professional_spdif %d\n", prof));
+ if (prof)
+ chip->comm_page->flags |=
+ __constant_cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF);
+ else
+ chip->comm_page->flags &=
+ ~__constant_cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF);
+ chip->professional_spdif = prof;
+ return update_flags(chip);
+}
diff --git a/sound/pci/echoaudio/layla24.c b/sound/pci/echoaudio/layla24.c
new file mode 100644
index 000000000000..d4581fdc841c
--- /dev/null
+++ b/sound/pci/echoaudio/layla24.c
@@ -0,0 +1,121 @@
+/*
+ * ALSA driver for Echoaudio soundcards.
+ * Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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.
+ */
+
+#define ECHO24_FAMILY
+#define ECHOCARD_LAYLA24
+#define ECHOCARD_NAME "Layla24"
+#define ECHOCARD_HAS_MONITOR
+#define ECHOCARD_HAS_ASIC
+#define ECHOCARD_HAS_INPUT_NOMINAL_LEVEL
+#define ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL
+#define ECHOCARD_HAS_SUPER_INTERLEAVE
+#define ECHOCARD_HAS_DIGITAL_IO
+#define ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE
+#define ECHOCARD_HAS_DIGITAL_MODE_SWITCH
+#define ECHOCARD_HAS_EXTERNAL_CLOCK
+#define ECHOCARD_HAS_ADAT 6
+#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+#define ECHOCARD_HAS_MIDI
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT 0 /* 8 */
+#define PX_DIGITAL_OUT 8 /* 8 */
+#define PX_ANALOG_IN 16 /* 8 */
+#define PX_DIGITAL_IN 24 /* 8 */
+#define PX_NUM 32
+
+/* Bus indexes */
+#define BX_ANALOG_OUT 0 /* 8 */
+#define BX_DIGITAL_OUT 8 /* 8 */
+#define BX_ANALOG_IN 16 /* 8 */
+#define BX_DIGITAL_IN 24 /* 8 */
+#define BX_NUM 32
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <sound/rawmidi.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+#define FW_361_LOADER 0
+#define FW_LAYLA24_DSP 1
+#define FW_LAYLA24_1_ASIC 2
+#define FW_LAYLA24_2A_ASIC 3
+#define FW_LAYLA24_2S_ASIC 4
+
+static const struct firmware card_fw[] = {
+ {0, "loader_dsp.fw"},
+ {0, "layla24_dsp.fw"},
+ {0, "layla24_1_asic.fw"},
+ {0, "layla24_2A_asic.fw"},
+ {0, "layla24_2S_asic.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+ {0x1057, 0x3410, 0xECC0, 0x0060, 0, 0, 0}, /* DSP 56361 Layla24 rev.0 */
+ {0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_SYNC_START,
+ .formats = SNDRV_PCM_FMTBIT_U8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_S32_BE,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .rate_min = 8000,
+ .rate_max = 100000,
+ .channels_min = 1,
+ .channels_max = 8,
+ .buffer_bytes_max = 262144,
+ .period_bytes_min = 32,
+ .period_bytes_max = 131072,
+ .periods_min = 2,
+ .periods_max = 220,
+ /* One page (4k) contains 512 instructions. I don't know if the hw
+ supports lists longer than this. In this case periods_max=220 is a
+ safe limit to make sure the list never exceeds 512 instructions. */
+};
+
+
+#include "layla24_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio_gml.c"
+#include "echoaudio.c"
+#include "midi.c"
diff --git a/sound/pci/echoaudio/layla24_dsp.c b/sound/pci/echoaudio/layla24_dsp.c
new file mode 100644
index 000000000000..7ec5b63d0dce
--- /dev/null
+++ b/sound/pci/echoaudio/layla24_dsp.c
@@ -0,0 +1,394 @@
+/****************************************************************************
+
+ Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+ All rights reserved
+ www.echoaudio.com
+
+ This file is part of Echo Digital Audio's generic driver library.
+
+ Echo Digital Audio's generic driver library is free software;
+ you can redistribute it and/or modify it under the terms of
+ the GNU General Public License as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ 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.
+
+ *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+static int write_control_reg(struct echoaudio *chip, u32 value, char force);
+static int set_input_clock(struct echoaudio *chip, u16 clock);
+static int set_professional_spdif(struct echoaudio *chip, char prof);
+static int set_digital_mode(struct echoaudio *chip, u8 mode);
+static int load_asic_generic(struct echoaudio *chip, u32 cmd,
+ const struct firmware *asic);
+static int check_asic_status(struct echoaudio *chip);
+
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+ int err;
+
+ DE_INIT(("init_hw() - Layla24\n"));
+ snd_assert((subdevice_id & 0xfff0) == LAYLA24, return -ENODEV);
+
+ if ((err = init_dsp_comm_page(chip))) {
+ DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+ return err;
+ }
+
+ chip->device_id = device_id;
+ chip->subdevice_id = subdevice_id;
+ chip->bad_board = TRUE;
+ chip->has_midi = TRUE;
+ chip->dsp_code_to_load = &card_fw[FW_LAYLA24_DSP];
+ chip->input_clock_types =
+ ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF |
+ ECHO_CLOCK_BIT_WORD | ECHO_CLOCK_BIT_ADAT;
+ chip->digital_modes =
+ ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA |
+ ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL |
+ ECHOCAPS_HAS_DIGITAL_MODE_ADAT;
+ chip->digital_mode = DIGITAL_MODE_SPDIF_RCA;
+ chip->professional_spdif = FALSE;
+ chip->digital_in_automute = TRUE;
+
+ if ((err = load_firmware(chip)) < 0)
+ return err;
+ chip->bad_board = FALSE;
+
+ if ((err = init_line_levels(chip)) < 0)
+ return err;
+
+ err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA);
+ snd_assert(err >= 0, return err);
+ err = set_professional_spdif(chip, TRUE);
+
+ DE_INIT(("init_hw done\n"));
+ return err;
+}
+
+
+
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+ u32 clocks_from_dsp, clock_bits;
+
+ /* Map the DSP clock detect bits to the generic driver clock detect bits */
+ clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks);
+
+ clock_bits = ECHO_CLOCK_BIT_INTERNAL;
+
+ if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_SPDIF)
+ clock_bits |= ECHO_CLOCK_BIT_SPDIF;
+
+ if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_ADAT)
+ clock_bits |= ECHO_CLOCK_BIT_ADAT;
+
+ if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_WORD)
+ clock_bits |= ECHO_CLOCK_BIT_WORD;
+
+ return clock_bits;
+}
+
+
+
+/* Layla24 has an ASIC on the PCI card and another ASIC in the external box;
+both need to be loaded. */
+static int load_asic(struct echoaudio *chip)
+{
+ int err;
+
+ if (chip->asic_loaded)
+ return 1;
+
+ DE_INIT(("load_asic\n"));
+
+ /* Give the DSP a few milliseconds to settle down */
+ mdelay(10);
+
+ /* Load the ASIC for the PCI card */
+ err = load_asic_generic(chip, DSP_FNC_LOAD_LAYLA24_PCI_CARD_ASIC,
+ &card_fw[FW_LAYLA24_1_ASIC]);
+ if (err < 0)
+ return err;
+
+ chip->asic_code = &card_fw[FW_LAYLA24_2S_ASIC];
+
+ /* Now give the new ASIC a little time to set up */
+ mdelay(10);
+
+ /* Do the external one */
+ err = load_asic_generic(chip, DSP_FNC_LOAD_LAYLA24_EXTERNAL_ASIC,
+ &card_fw[FW_LAYLA24_2S_ASIC]);
+ if (err < 0)
+ return FALSE;
+
+ /* Now give the external ASIC a little time to set up */
+ mdelay(10);
+
+ /* See if it worked */
+ err = check_asic_status(chip);
+
+ /* Set up the control register if the load succeeded -
+ 48 kHz, internal clock, S/PDIF RCA mode */
+ if (!err)
+ err = write_control_reg(chip, GML_CONVERTER_ENABLE | GML_48KHZ,
+ TRUE);
+
+ DE_INIT(("load_asic() done\n"));
+ return err;
+}
+
+
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+ u32 control_reg, clock, base_rate;
+
+ snd_assert(rate < 50000 || chip->digital_mode != DIGITAL_MODE_ADAT,
+ return -EINVAL);
+
+ /* Only set the clock for internal mode. */
+ if (chip->input_clock != ECHO_CLOCK_INTERNAL) {
+ DE_ACT(("set_sample_rate: Cannot set sample rate - "
+ "clock not set to CLK_CLOCKININTERNAL\n"));
+ /* Save the rate anyhow */
+ chip->comm_page->sample_rate = cpu_to_le32(rate);
+ chip->sample_rate = rate;
+ return 0;
+ }
+
+ /* Get the control register & clear the appropriate bits */
+ control_reg = le32_to_cpu(chip->comm_page->control_register);
+ control_reg &= GML_CLOCK_CLEAR_MASK & GML_SPDIF_RATE_CLEAR_MASK;
+
+ clock = 0;
+
+ switch (rate) {
+ case 96000:
+ clock = GML_96KHZ;
+ break;
+ case 88200:
+ clock = GML_88KHZ;
+ break;
+ case 48000:
+ clock = GML_48KHZ | GML_SPDIF_SAMPLE_RATE1;
+ break;
+ case 44100:
+ clock = GML_44KHZ;
+ /* Professional mode */
+ if (control_reg & GML_SPDIF_PRO_MODE)
+ clock |= GML_SPDIF_SAMPLE_RATE0;
+ break;
+ case 32000:
+ clock = GML_32KHZ | GML_SPDIF_SAMPLE_RATE0 |
+ GML_SPDIF_SAMPLE_RATE1;
+ break;
+ case 22050:
+ clock = GML_22KHZ;
+ break;
+ case 16000:
+ clock = GML_16KHZ;
+ break;
+ case 11025:
+ clock = GML_11KHZ;
+ break;
+ case 8000:
+ clock = GML_8KHZ;
+ break;
+ default:
+ /* If this is a non-standard rate, then the driver needs to
+ use Layla24's special "continuous frequency" mode */
+ clock = LAYLA24_CONTINUOUS_CLOCK;
+ if (rate > 50000) {
+ base_rate = rate >> 1;
+ control_reg |= GML_DOUBLE_SPEED_MODE;
+ } else {
+ base_rate = rate;
+ }
+
+ if (base_rate < 25000)
+ base_rate = 25000;
+
+ if (wait_handshake(chip))
+ return -EIO;
+
+ chip->comm_page->sample_rate =
+ cpu_to_le32(LAYLA24_MAGIC_NUMBER / base_rate - 2);
+
+ clear_handshake(chip);
+ send_vector(chip, DSP_VC_SET_LAYLA24_FREQUENCY_REG);
+ }
+
+ control_reg |= clock;
+
+ chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP ? */
+ chip->sample_rate = rate;
+ DE_ACT(("set_sample_rate: %d clock %d\n", rate, control_reg));
+
+ return write_control_reg(chip, control_reg, FALSE);
+}
+
+
+
+static int set_input_clock(struct echoaudio *chip, u16 clock)
+{
+ u32 control_reg, clocks_from_dsp;
+
+ /* Mask off the clock select bits */
+ control_reg = le32_to_cpu(chip->comm_page->control_register) &
+ GML_CLOCK_CLEAR_MASK;
+ clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks);
+
+ /* Pick the new clock */
+ switch (clock) {
+ case ECHO_CLOCK_INTERNAL:
+ DE_ACT(("Set Layla24 clock to INTERNAL\n"));
+ chip->input_clock = ECHO_CLOCK_INTERNAL;
+ return set_sample_rate(chip, chip->sample_rate);
+ case ECHO_CLOCK_SPDIF:
+ if (chip->digital_mode == DIGITAL_MODE_ADAT)
+ return -EAGAIN;
+ control_reg |= GML_SPDIF_CLOCK;
+ /* Layla24 doesn't support 96KHz S/PDIF */
+ control_reg &= ~GML_DOUBLE_SPEED_MODE;
+ DE_ACT(("Set Layla24 clock to SPDIF\n"));
+ break;
+ case ECHO_CLOCK_WORD:
+ control_reg |= GML_WORD_CLOCK;
+ if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_WORD96)
+ control_reg |= GML_DOUBLE_SPEED_MODE;
+ else
+ control_reg &= ~GML_DOUBLE_SPEED_MODE;
+ DE_ACT(("Set Layla24 clock to WORD\n"));
+ break;
+ case ECHO_CLOCK_ADAT:
+ if (chip->digital_mode != DIGITAL_MODE_ADAT)
+ return -EAGAIN;
+ control_reg |= GML_ADAT_CLOCK;
+ control_reg &= ~GML_DOUBLE_SPEED_MODE;
+ DE_ACT(("Set Layla24 clock to ADAT\n"));
+ break;
+ default:
+ DE_ACT(("Input clock 0x%x not supported for Layla24\n", clock));
+ return -EINVAL;
+ }
+
+ chip->input_clock = clock;
+ return write_control_reg(chip, control_reg, TRUE);
+}
+
+
+
+/* Depending on what digital mode you want, Layla24 needs different ASICs
+loaded. This function checks the ASIC needed for the new mode and sees
+if it matches the one already loaded. */
+static int switch_asic(struct echoaudio *chip, const struct firmware *asic)
+{
+ s8 *monitors;
+
+ /* Check to see if this is already loaded */
+ if (asic != chip->asic_code) {
+ monitors = kmalloc(MONITOR_ARRAY_SIZE, GFP_KERNEL);
+ if (! monitors)
+ return -ENOMEM;
+
+ memcpy(monitors, chip->comm_page->monitors, MONITOR_ARRAY_SIZE);
+ memset(chip->comm_page->monitors, ECHOGAIN_MUTED,
+ MONITOR_ARRAY_SIZE);
+
+ /* Load the desired ASIC */
+ if (load_asic_generic(chip, DSP_FNC_LOAD_LAYLA24_EXTERNAL_ASIC,
+ asic) < 0) {
+ memcpy(chip->comm_page->monitors, monitors,
+ MONITOR_ARRAY_SIZE);
+ kfree(monitors);
+ return -EIO;
+ }
+ chip->asic_code = asic;
+ memcpy(chip->comm_page->monitors, monitors, MONITOR_ARRAY_SIZE);
+ kfree(monitors);
+ }
+
+ return 0;
+}
+
+
+
+static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
+{
+ u32 control_reg;
+ int err, incompatible_clock;
+ const struct firmware *asic;
+
+ /* Set clock to "internal" if it's not compatible with the new mode */
+ incompatible_clock = FALSE;
+ switch (mode) {
+ case DIGITAL_MODE_SPDIF_OPTICAL:
+ case DIGITAL_MODE_SPDIF_RCA:
+ if (chip->input_clock == ECHO_CLOCK_ADAT)
+ incompatible_clock = TRUE;
+ asic = &card_fw[FW_LAYLA24_2S_ASIC];
+ break;
+ case DIGITAL_MODE_ADAT:
+ if (chip->input_clock == ECHO_CLOCK_SPDIF)
+ incompatible_clock = TRUE;
+ asic = &card_fw[FW_LAYLA24_2A_ASIC];
+ break;
+ default:
+ DE_ACT(("Digital mode not supported: %d\n", mode));
+ return -EINVAL;
+ }
+
+ if (incompatible_clock) { /* Switch to 48KHz, internal */
+ chip->sample_rate = 48000;
+ spin_lock_irq(&chip->lock);
+ set_input_clock(chip, ECHO_CLOCK_INTERNAL);
+ spin_unlock_irq(&chip->lock);
+ }
+
+ /* switch_asic() can sleep */
+ if (switch_asic(chip, asic) < 0)
+ return -EIO;
+
+ spin_lock_irq(&chip->lock);
+
+ /* Tweak the control register */
+ control_reg = le32_to_cpu(chip->comm_page->control_register);
+ control_reg &= GML_DIGITAL_MODE_CLEAR_MASK;
+
+ switch (mode) {
+ case DIGITAL_MODE_SPDIF_OPTICAL:
+ control_reg |= GML_SPDIF_OPTICAL_MODE;
+ break;
+ case DIGITAL_MODE_SPDIF_RCA:
+ /* GML_SPDIF_OPTICAL_MODE bit cleared */
+ break;
+ case DIGITAL_MODE_ADAT:
+ control_reg |= GML_ADAT_MODE;
+ control_reg &= ~GML_DOUBLE_SPEED_MODE;
+ break;
+ }
+
+ err = write_control_reg(chip, control_reg, TRUE);
+ spin_unlock_irq(&chip->lock);
+ if (err < 0)
+ return err;
+ chip->digital_mode = mode;
+
+ DE_ACT(("set_digital_mode to %d\n", mode));
+ return incompatible_clock;
+}
diff --git a/sound/pci/echoaudio/mia.c b/sound/pci/echoaudio/mia.c
new file mode 100644
index 000000000000..be40c64263d2
--- /dev/null
+++ b/sound/pci/echoaudio/mia.c
@@ -0,0 +1,117 @@
+/*
+ * ALSA driver for Echoaudio soundcards.
+ * Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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.
+ */
+
+#define ECHO24_FAMILY
+#define ECHOCARD_MIA
+#define ECHOCARD_NAME "Mia"
+#define ECHOCARD_HAS_MONITOR
+#define ECHOCARD_HAS_INPUT_NOMINAL_LEVEL
+#define ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL
+#define ECHOCARD_HAS_SUPER_INTERLEAVE
+#define ECHOCARD_HAS_VMIXER
+#define ECHOCARD_HAS_DIGITAL_IO
+#define ECHOCARD_HAS_EXTERNAL_CLOCK
+#define ECHOCARD_HAS_ADAT FALSE
+#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+#define ECHOCARD_HAS_MIDI
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT 0 /* 8 */
+#define PX_DIGITAL_OUT 8 /* 0 */
+#define PX_ANALOG_IN 8 /* 2 */
+#define PX_DIGITAL_IN 10 /* 2 */
+#define PX_NUM 12
+
+/* Bus indexes */
+#define BX_ANALOG_OUT 0 /* 2 */
+#define BX_DIGITAL_OUT 2 /* 2 */
+#define BX_ANALOG_IN 4 /* 2 */
+#define BX_DIGITAL_IN 6 /* 2 */
+#define BX_NUM 8
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <sound/rawmidi.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+#define FW_361_LOADER 0
+#define FW_MIA_DSP 1
+
+static const struct firmware card_fw[] = {
+ {0, "loader_dsp.fw"},
+ {0, "mia_dsp.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+ {0x1057, 0x3410, 0xECC0, 0x0080, 0, 0, 0}, /* DSP 56361 Mia rev.0 */
+ {0x1057, 0x3410, 0xECC0, 0x0081, 0, 0, 0}, /* DSP 56361 Mia rev.1 */
+ {0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_SYNC_START,
+ .formats = SNDRV_PCM_FMTBIT_U8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_S32_BE,
+ .rates = SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .channels_min = 1,
+ .channels_max = 8,
+ .buffer_bytes_max = 262144,
+ .period_bytes_min = 32,
+ .period_bytes_max = 131072,
+ .periods_min = 2,
+ .periods_max = 220,
+ /* One page (4k) contains 512 instructions. I don't know if the hw
+ supports lists longer than this. In this case periods_max=220 is a
+ safe limit to make sure the list never exceeds 512 instructions. */
+};
+
+
+#include "mia_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio.c"
+#include "midi.c"
diff --git a/sound/pci/echoaudio/mia_dsp.c b/sound/pci/echoaudio/mia_dsp.c
new file mode 100644
index 000000000000..891c70519096
--- /dev/null
+++ b/sound/pci/echoaudio/mia_dsp.c
@@ -0,0 +1,229 @@
+/****************************************************************************
+
+ Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+ All rights reserved
+ www.echoaudio.com
+
+ This file is part of Echo Digital Audio's generic driver library.
+
+ Echo Digital Audio's generic driver library is free software;
+ you can redistribute it and/or modify it under the terms of
+ the GNU General Public License as published by the Free Software
+ Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ 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.
+
+ *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+static int set_input_clock(struct echoaudio *chip, u16 clock);
+static int set_professional_spdif(struct echoaudio *chip, char prof);
+static int update_flags(struct echoaudio *chip);
+static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
+ int gain);
+static int update_vmixer_level(struct echoaudio *chip);
+
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+ int err;
+
+ DE_INIT(("init_hw() - Mia\n"));
+ snd_assert((subdevice_id & 0xfff0) == MIA, return -ENODEV);
+
+ if ((err = init_dsp_comm_page(chip))) {
+ DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+ return err;
+ }
+
+ chip->device_id = device_id;
+ chip->subdevice_id = subdevice_id;
+ chip->bad_board = TRUE;
+ chip->dsp_code_to_load = &card_fw[FW_MIA_DSP];
+ /* Since this card has no ASIC, mark it as loaded so everything
+ works OK */
+ chip->asic_loaded = TRUE;
+ if ((subdevice_id & 0x0000f) == MIA_MIDI_REV)
+ chip->has_midi = TRUE;
+ chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL |
+ ECHO_CLOCK_BIT_SPDIF;
+
+ if ((err = load_firmware(chip)) < 0)
+ return err;
+ chip->bad_board = FALSE;
+
+ if ((err = init_line_levels(chip)))
+ return err;
+
+ /* Default routing of the virtual channels: vchannels 0-3 go to analog
+ outputs and vchannels 4-7 go to S/PDIF outputs */
+ set_vmixer_gain(chip, 0, 0, 0);
+ set_vmixer_gain(chip, 1, 1, 0);
+ set_vmixer_gain(chip, 0, 2, 0);
+ set_vmixer_gain(chip, 1, 3, 0);
+ set_vmixer_gain(chip, 2, 4, 0);
+ set_vmixer_gain(chip, 3, 5, 0);
+ set_vmixer_gain(chip, 2, 6, 0);
+ set_vmixer_gain(chip, 3, 7, 0);
+ err = update_vmixer_level(chip);
+
+ DE_INIT(("init_hw done\n"));
+ return err;
+}
+
+
+
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+ u32 clocks_from_dsp, clock_bits;
+
+ /* Map the DSP clock detect bits to the generic driver clock
+ detect bits */
+ clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks);
+
+ clock_bits = ECHO_CLOCK_BIT_INTERNAL;
+
+ if (clocks_from_dsp & GLDM_CLOCK_DETECT_BIT_SPDIF)
+ clock_bits |= ECHO_CLOCK_BIT_SPDIF;
+
+ return clock_bits;
+}
+
+
+
+/* The Mia has no ASIC. Just do nothing */
+static int load_asic(struct echoaudio *chip)
+{
+ return 0;
+}
+
+
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+ u32 control_reg;
+
+ switch (rate) {
+ case 96000:
+ control_reg = MIA_96000;
+ break;
+ case 88200:
+ control_reg = MIA_88200;
+ break;
+ case 48000:
+ control_reg = MIA_48000;
+ break;
+ case 44100:
+ control_reg = MIA_44100;
+ break;
+ case 32000:
+ control_reg = MIA_32000;
+ break;
+ default:
+ DE_ACT(("set_sample_rate: %d invalid!\n", rate));
+ return -EINVAL;
+ }
+
+ /* Override the clock setting if this Mia is set to S/PDIF clock */
+ if (chip->input_clock == ECHO_CLOCK_SPDIF)
+ control_reg |= MIA_SPDIF;
+
+ /* Set the control register if it has changed */
+ if (control_reg != le32_to_cpu(chip->comm_page->control_register)) {
+ if (wait_handshake(chip))
+ return -EIO;
+
+ chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP */
+ chip->comm_page->control_register = cpu_to_le32(control_reg);
+ chip->sample_rate = rate;
+
+ clear_handshake(chip);
+ return send_vector(chip, DSP_VC_UPDATE_CLOCKS);
+ }
+ return 0;
+}
+
+
+
+static int set_input_clock(struct echoaudio *chip, u16 clock)
+{
+ DE_ACT(("set_input_clock(%d)\n", clock));
+ snd_assert(clock == ECHO_CLOCK_INTERNAL || clock == ECHO_CLOCK_SPDIF,
+ return -EINVAL);
+
+ chip->input_clock = clock;
+ return set_sample_rate(chip, chip->sample_rate);
+}
+
+
+
+/* This function routes the sound from a virtual channel to a real output */
+static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
+ int gain)
+{
+ int index;
+
+ snd_assert(pipe < num_pipes_out(chip) &&
+ output < num_busses_out(chip), return -EINVAL);
+
+ if (wait_handshake(chip))
+ return -EIO;
+
+ chip->vmixer_gain[output][pipe] = gain;
+ index = output * num_pipes_out(chip) + pipe;
+ chip->comm_page->vmixer[index] = gain;
+
+ DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain));
+ return 0;
+}
+
+
+
+/* Tell the DSP to read and update virtual mixer levels in comm page. */
+static int update_vmixer_level(struct echoaudio *chip)
+{
+ if (wait_handshake(chip))
+ return -EIO;
+ clear_handshake(chip);
+ return send_vector(chip, DSP_VC_SET_VMIXER_GAIN);
+}
+
+
+
+/* Tell the DSP to reread the flags from the comm page */
+static int update_flags(struct echoaudio *chip)
+{
+ if (wait_handshake(chip))
+ return -EIO;
+ clear_handshake(chip);
+ return send_vector(chip, DSP_VC_UPDATE_FLAGS);
+}
+
+
+
+static int set_professional_spdif(struct echoaudio *chip, char prof)
+{
+ DE_ACT(("set_professional_spdif %d\n", prof));
+ if (prof)
+ chip->comm_page->flags |=
+ __constant_cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF);
+ else
+ chip->comm_page->flags &=
+ ~__constant_cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF);
+ chip->professional_spdif = prof;
+ return update_flags(chip);
+}
+
diff --git a/sound/pci/echoaudio/midi.c b/sound/pci/echoaudio/midi.c
new file mode 100644
index 000000000000..e31f0f11e3a8
--- /dev/null
+++ b/sound/pci/echoaudio/midi.c
@@ -0,0 +1,327 @@
+/****************************************************************************
+
+ Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+ All rights reserved
+ www.echoaudio.com
+
+ This file is part of Echo Digital Audio's generic driver library.
+
+ Echo Digital Audio's generic driver library is free software;
+ you can redistribute it and/or modify it under the terms of
+ the GNU General Public License as published by the Free Software
+ Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ 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.
+
+ *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+/******************************************************************************
+ MIDI lowlevel code
+******************************************************************************/
+
+/* Start and stop Midi input */
+static int enable_midi_input(struct echoaudio *chip, char enable)
+{
+ DE_MID(("enable_midi_input(%d)\n", enable));
+
+ if (wait_handshake(chip))
+ return -EIO;
+
+ if (enable) {
+ chip->mtc_state = MIDI_IN_STATE_NORMAL;
+ chip->comm_page->flags |=
+ __constant_cpu_to_le32(DSP_FLAG_MIDI_INPUT);
+ } else
+ chip->comm_page->flags &=
+ ~__constant_cpu_to_le32(DSP_FLAG_MIDI_INPUT);
+
+ clear_handshake(chip);
+ return send_vector(chip, DSP_VC_UPDATE_FLAGS);
+}
+
+
+
+/* Send a buffer full of MIDI data to the DSP
+Returns how many actually written or < 0 on error */
+static int write_midi(struct echoaudio *chip, u8 *data, int bytes)
+{
+ snd_assert(bytes > 0 && bytes < MIDI_OUT_BUFFER_SIZE, return -EINVAL);
+
+ if (wait_handshake(chip))
+ return -EIO;
+
+ /* HF4 indicates that it is safe to write MIDI output data */
+ if (! (get_dsp_register(chip, CHI32_STATUS_REG) & CHI32_STATUS_REG_HF4))
+ return 0;
+
+ chip->comm_page->midi_output[0] = bytes;
+ memcpy(&chip->comm_page->midi_output[1], data, bytes);
+ chip->comm_page->midi_out_free_count = 0;
+ clear_handshake(chip);
+ send_vector(chip, DSP_VC_MIDI_WRITE);
+ DE_MID(("write_midi: %d\n", bytes));
+ return bytes;
+}
+
+
+
+/* Run the state machine for MIDI input data
+MIDI time code sync isn't supported by this code right now, but you still need
+this state machine to parse the incoming MIDI data stream. Every time the DSP
+sees a 0xF1 byte come in, it adds the DSP sample position to the MIDI data
+stream. The DSP sample position is represented as a 32 bit unsigned value,
+with the high 16 bits first, followed by the low 16 bits. Since these aren't
+real MIDI bytes, the following logic is needed to skip them. */
+static inline int mtc_process_data(struct echoaudio *chip, short midi_byte)
+{
+ switch (chip->mtc_state) {
+ case MIDI_IN_STATE_NORMAL:
+ if (midi_byte == 0xF1)
+ chip->mtc_state = MIDI_IN_STATE_TS_HIGH;
+ break;
+ case MIDI_IN_STATE_TS_HIGH:
+ chip->mtc_state = MIDI_IN_STATE_TS_LOW;
+ return MIDI_IN_SKIP_DATA;
+ break;
+ case MIDI_IN_STATE_TS_LOW:
+ chip->mtc_state = MIDI_IN_STATE_F1_DATA;
+ return MIDI_IN_SKIP_DATA;
+ break;
+ case MIDI_IN_STATE_F1_DATA:
+ chip->mtc_state = MIDI_IN_STATE_NORMAL;
+ break;
+ }
+ return 0;
+}
+
+
+
+/* This function is called from the IRQ handler and it reads the midi data
+from the DSP's buffer. It returns the number of bytes received. */
+static int midi_service_irq(struct echoaudio *chip)
+{
+ short int count, midi_byte, i, received;
+
+ /* The count is at index 0, followed by actual data */
+ count = le16_to_cpu(chip->comm_page->midi_input[0]);
+
+ snd_assert(count < MIDI_IN_BUFFER_SIZE, return 0);
+
+ /* Get the MIDI data from the comm page */
+ i = 1;
+ received = 0;
+ for (i = 1; i <= count; i++) {
+ /* Get the MIDI byte */
+ midi_byte = le16_to_cpu(chip->comm_page->midi_input[i]);
+
+ /* Parse the incoming MIDI stream. The incoming MIDI data
+ consists of MIDI bytes and timestamps for the MIDI time code
+ 0xF1 bytes. mtc_process_data() is a little state machine that
+ parses the stream. If you get MIDI_IN_SKIP_DATA back, then
+ this is a timestamp byte, not a MIDI byte, so don't store it
+ in the MIDI input buffer. */
+ if (mtc_process_data(chip, midi_byte) == MIDI_IN_SKIP_DATA)
+ continue;
+
+ chip->midi_buffer[received++] = (u8)midi_byte;
+ }
+
+ return received;
+}
+
+
+
+
+/******************************************************************************
+ MIDI interface
+******************************************************************************/
+
+static int snd_echo_midi_input_open(struct snd_rawmidi_substream *substream)
+{
+ struct echoaudio *chip = substream->rmidi->private_data;
+
+ chip->midi_in = substream;
+ DE_MID(("rawmidi_iopen\n"));
+ return 0;
+}
+
+
+
+static void snd_echo_midi_input_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct echoaudio *chip = substream->rmidi->private_data;
+
+ if (up != chip->midi_input_enabled) {
+ spin_lock_irq(&chip->lock);
+ enable_midi_input(chip, up);
+ spin_unlock_irq(&chip->lock);
+ chip->midi_input_enabled = up;
+ }
+}
+
+
+
+static int snd_echo_midi_input_close(struct snd_rawmidi_substream *substream)
+{
+ struct echoaudio *chip = substream->rmidi->private_data;
+
+ chip->midi_in = NULL;
+ DE_MID(("rawmidi_iclose\n"));
+ return 0;
+}
+
+
+
+static int snd_echo_midi_output_open(struct snd_rawmidi_substream *substream)
+{
+ struct echoaudio *chip = substream->rmidi->private_data;
+
+ chip->tinuse = 0;
+ chip->midi_full = 0;
+ chip->midi_out = substream;
+ DE_MID(("rawmidi_oopen\n"));
+ return 0;
+}
+
+
+
+static void snd_echo_midi_output_write(unsigned long data)
+{
+ struct echoaudio *chip = (struct echoaudio *)data;
+ unsigned long flags;
+ int bytes, sent, time;
+ unsigned char buf[MIDI_OUT_BUFFER_SIZE - 1];
+
+ DE_MID(("snd_echo_midi_output_write\n"));
+ /* No interrupts are involved: we have to check at regular intervals
+ if the card's output buffer has room for new data. */
+ sent = bytes = 0;
+ spin_lock_irqsave(&chip->lock, flags);
+ chip->midi_full = 0;
+ if (chip->midi_out && !snd_rawmidi_transmit_empty(chip->midi_out)) {
+ bytes = snd_rawmidi_transmit_peek(chip->midi_out, buf,
+ MIDI_OUT_BUFFER_SIZE - 1);
+ DE_MID(("Try to send %d bytes...\n", bytes));
+ sent = write_midi(chip, buf, bytes);
+ if (sent < 0) {
+ snd_printk(KERN_ERR "write_midi() error %d\n", sent);
+ /* retry later */
+ sent = 9000;
+ chip->midi_full = 1;
+ } else if (sent > 0) {
+ DE_MID(("%d bytes sent\n", sent));
+ snd_rawmidi_transmit_ack(chip->midi_out, sent);
+ } else {
+ /* Buffer is full. DSP's internal buffer is 64 (128 ?)
+ bytes long. Let's wait until half of them are sent */
+ DE_MID(("Full\n"));
+ sent = 32;
+ chip->midi_full = 1;
+ }
+ }
+
+ /* We restart the timer only if there is some data left to send */
+ if (!snd_rawmidi_transmit_empty(chip->midi_out) && chip->tinuse) {
+ /* The timer will expire slightly after the data has been
+ sent */
+ time = (sent << 3) / 25 + 1; /* 8/25=0.32ms to send a byte */
+ mod_timer(&chip->timer, jiffies + (time * HZ + 999) / 1000);
+ DE_MID(("Timer armed(%d)\n", ((time * HZ + 999) / 1000)));
+ }
+ spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+
+
+static void snd_echo_midi_output_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct echoaudio *chip = substream->rmidi->private_data;
+
+ DE_MID(("snd_echo_midi_output_trigger(%d)\n", up));
+ spin_lock_irq(&chip->lock);
+ if (up) {
+ if (!chip->tinuse) {
+ init_timer(&chip->timer);
+ chip->timer.function = snd_echo_midi_output_write;
+ chip->timer.data = (unsigned long)chip;
+ chip->tinuse = 1;
+ }
+ } else {
+ if (chip->tinuse) {
+ del_timer(&chip->timer);
+ chip->tinuse = 0;
+ DE_MID(("Timer removed\n"));
+ }
+ }
+ spin_unlock_irq(&chip->lock);
+
+ if (up && !chip->midi_full)
+ snd_echo_midi_output_write((unsigned long)chip);
+}
+
+
+
+static int snd_echo_midi_output_close(struct snd_rawmidi_substream *substream)
+{
+ struct echoaudio *chip = substream->rmidi->private_data;
+
+ chip->midi_out = NULL;
+ DE_MID(("rawmidi_oclose\n"));
+ return 0;
+}
+
+
+
+static struct snd_rawmidi_ops snd_echo_midi_input = {
+ .open = snd_echo_midi_input_open,
+ .close = snd_echo_midi_input_close,
+ .trigger = snd_echo_midi_input_trigger,
+};
+
+static struct snd_rawmidi_ops snd_echo_midi_output = {
+ .open = snd_echo_midi_output_open,
+ .close = snd_echo_midi_output_close,
+ .trigger = snd_echo_midi_output_trigger,
+};
+
+
+
+/* <--snd_echo_probe() */
+static int __devinit snd_echo_midi_create(struct snd_card *card,
+ struct echoaudio *chip)
+{
+ int err;
+
+ if ((err = snd_rawmidi_new(card, card->shortname, 0, 1, 1,
+ &chip->rmidi)) < 0)
+ return err;
+
+ strcpy(chip->rmidi->name, card->shortname);
+ chip->rmidi->private_data = chip;
+
+ snd_rawmidi_set_ops(chip->rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &snd_echo_midi_input);
+ snd_rawmidi_set_ops(chip->rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &snd_echo_midi_output);
+
+ chip->rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
+ SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
+ DE_INIT(("MIDI ok\n"));
+ return 0;
+}
diff --git a/sound/pci/echoaudio/mona.c b/sound/pci/echoaudio/mona.c
new file mode 100644
index 000000000000..5dc512add372
--- /dev/null
+++ b/sound/pci/echoaudio/mona.c
@@ -0,0 +1,129 @@
+/*
+ * ALSA driver for Echoaudio soundcards.
+ * Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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.
+ */
+
+#define ECHO24_FAMILY
+#define ECHOCARD_MONA
+#define ECHOCARD_NAME "Mona"
+#define ECHOCARD_HAS_MONITOR
+#define ECHOCARD_HAS_ASIC
+#define ECHOCARD_HAS_SUPER_INTERLEAVE
+#define ECHOCARD_HAS_DIGITAL_IO
+#define ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE
+#define ECHOCARD_HAS_DIGITAL_MODE_SWITCH
+#define ECHOCARD_HAS_EXTERNAL_CLOCK
+#define ECHOCARD_HAS_ADAT 6
+#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT 0 /* 6 */
+#define PX_DIGITAL_OUT 6 /* 8 */
+#define PX_ANALOG_IN 14 /* 4 */
+#define PX_DIGITAL_IN 18 /* 8 */
+#define PX_NUM 26
+
+/* Bus indexes */
+#define BX_ANALOG_OUT 0 /* 6 */
+#define BX_DIGITAL_OUT 6 /* 8 */
+#define BX_ANALOG_IN 14 /* 4 */
+#define BX_DIGITAL_IN 18 /* 8 */
+#define BX_NUM 26
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+#define FW_361_LOADER 0
+#define FW_MONA_301_DSP 1
+#define FW_MONA_361_DSP 2
+#define FW_MONA_301_1_ASIC48 3
+#define FW_MONA_301_1_ASIC96 4
+#define FW_MONA_361_1_ASIC48 5
+#define FW_MONA_361_1_ASIC96 6
+#define FW_MONA_2_ASIC 7
+
+static const struct firmware card_fw[] = {
+ {0, "loader_dsp.fw"},
+ {0, "mona_301_dsp.fw"},
+ {0, "mona_361_dsp.fw"},
+ {0, "mona_301_1_asic_48.fw"},
+ {0, "mona_301_1_asic_96.fw"},
+ {0, "mona_361_1_asic_48.fw"},
+ {0, "mona_361_1_asic_96.fw"},
+ {0, "mona_2_asic.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+ {0x1057, 0x1801, 0xECC0, 0x0070, 0, 0, 0}, /* DSP 56301 Mona rev.0 */
+ {0x1057, 0x1801, 0xECC0, 0x0071, 0, 0, 0}, /* DSP 56301 Mona rev.1 */
+ {0x1057, 0x1801, 0xECC0, 0x0072, 0, 0, 0}, /* DSP 56301 Mona rev.2 */
+ {0x1057, 0x3410, 0xECC0, 0x0070, 0, 0, 0}, /* DSP 56361 Mona rev.0 */
+ {0x1057, 0x3410, 0xECC0, 0x0071, 0, 0, 0}, /* DSP 56361 Mona rev.1 */
+ {0x1057, 0x3410, 0xECC0, 0x0072, 0, 0, 0}, /* DSP 56361 Mona rev.2 */
+ {0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_SYNC_START,
+ .formats = SNDRV_PCM_FMTBIT_U8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_S32_BE,
+ .rates = SNDRV_PCM_RATE_8000_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .channels_min = 1,
+ .channels_max = 8,
+ .buffer_bytes_max = 262144,
+ .period_bytes_min = 32,
+ .period_bytes_max = 131072,
+ .periods_min = 2,
+ .periods_max = 220,
+ /* One page (4k) contains 512 instructions. I don't know if the hw
+ supports lists longer than this. In this case periods_max=220 is a
+ safe limit to make sure the list never exceeds 512 instructions. */
+};
+
+
+#include "mona_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio_gml.c"
+#include "echoaudio.c"
diff --git a/sound/pci/echoaudio/mona_dsp.c b/sound/pci/echoaudio/mona_dsp.c
new file mode 100644
index 000000000000..c0b4bf0be7d1
--- /dev/null
+++ b/sound/pci/echoaudio/mona_dsp.c
@@ -0,0 +1,428 @@
+/****************************************************************************
+
+ Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+ All rights reserved
+ www.echoaudio.com
+
+ This file is part of Echo Digital Audio's generic driver library.
+
+ Echo Digital Audio's generic driver library is free software;
+ you can redistribute it and/or modify it under the terms of
+ the GNU General Public License as published by the Free Software
+ Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ 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.
+
+ *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+static int write_control_reg(struct echoaudio *chip, u32 value, char force);
+static int set_input_clock(struct echoaudio *chip, u16 clock);
+static int set_professional_spdif(struct echoaudio *chip, char prof);
+static int set_digital_mode(struct echoaudio *chip, u8 mode);
+static int load_asic_generic(struct echoaudio *chip, u32 cmd,
+ const struct firmware *asic);
+static int check_asic_status(struct echoaudio *chip);
+
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+ int err;
+
+ DE_INIT(("init_hw() - Mona\n"));
+ snd_assert((subdevice_id & 0xfff0) == MONA, return -ENODEV);
+
+ if ((err = init_dsp_comm_page(chip))) {
+ DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+ return err;
+ }
+
+ chip->device_id = device_id;
+ chip->subdevice_id = subdevice_id;
+ chip->bad_board = TRUE;
+ chip->input_clock_types =
+ ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF |
+ ECHO_CLOCK_BIT_WORD | ECHO_CLOCK_BIT_ADAT;
+ chip->digital_modes =
+ ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA |
+ ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL |
+ ECHOCAPS_HAS_DIGITAL_MODE_ADAT;
+
+ /* Mona comes in both '301 and '361 flavors */
+ if (chip->device_id == DEVICE_ID_56361)
+ chip->dsp_code_to_load = &card_fw[FW_MONA_361_DSP];
+ else
+ chip->dsp_code_to_load = &card_fw[FW_MONA_301_DSP];
+
+ chip->digital_mode = DIGITAL_MODE_SPDIF_RCA;
+ chip->professional_spdif = FALSE;
+ chip->digital_in_automute = TRUE;
+
+ if ((err = load_firmware(chip)) < 0)
+ return err;
+ chip->bad_board = FALSE;
+
+ if ((err = init_line_levels(chip)) < 0)
+ return err;
+
+ err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA);
+ snd_assert(err >= 0, return err);
+ err = set_professional_spdif(chip, TRUE);
+
+ DE_INIT(("init_hw done\n"));
+ return err;
+}
+
+
+
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+ u32 clocks_from_dsp, clock_bits;
+
+ /* Map the DSP clock detect bits to the generic driver clock
+ detect bits */
+ clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks);
+
+ clock_bits = ECHO_CLOCK_BIT_INTERNAL;
+
+ if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_SPDIF)
+ clock_bits |= ECHO_CLOCK_BIT_SPDIF;
+
+ if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_ADAT)
+ clock_bits |= ECHO_CLOCK_BIT_ADAT;
+
+ if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_WORD)
+ clock_bits |= ECHO_CLOCK_BIT_WORD;
+
+ return clock_bits;
+}
+
+
+
+/* Mona has an ASIC on the PCI card and another ASIC in the external box;
+both need to be loaded. */
+static int load_asic(struct echoaudio *chip)
+{
+ u32 control_reg;
+ int err;
+ const struct firmware *asic;
+
+ if (chip->asic_loaded)
+ return 0;
+
+ mdelay(10);
+
+ if (chip->device_id == DEVICE_ID_56361)
+ asic = &card_fw[FW_MONA_361_1_ASIC48];
+ else
+ asic = &card_fw[FW_MONA_301_1_ASIC48];
+
+ err = load_asic_generic(chip, DSP_FNC_LOAD_MONA_PCI_CARD_ASIC, asic);
+ if (err < 0)
+ return err;
+
+ chip->asic_code = asic;
+ mdelay(10);
+
+ /* Do the external one */
+ err = load_asic_generic(chip, DSP_FNC_LOAD_MONA_EXTERNAL_ASIC,
+ &card_fw[FW_MONA_2_ASIC]);
+ if (err < 0)
+ return err;
+
+ mdelay(10);
+ err = check_asic_status(chip);
+
+ /* Set up the control register if the load succeeded -
+ 48 kHz, internal clock, S/PDIF RCA mode */
+ if (!err) {
+ control_reg = GML_CONVERTER_ENABLE | GML_48KHZ;
+ err = write_control_reg(chip, control_reg, TRUE);
+ }
+
+ return err;
+}
+
+
+
+/* Depending on what digital mode you want, Mona needs different ASICs
+loaded. This function checks the ASIC needed for the new mode and sees
+if it matches the one already loaded. */
+static int switch_asic(struct echoaudio *chip, char double_speed)
+{
+ const struct firmware *asic;
+ int err;
+
+ /* Check the clock detect bits to see if this is
+ a single-speed clock or a double-speed clock; load
+ a new ASIC if necessary. */
+ if (chip->device_id == DEVICE_ID_56361) {
+ if (double_speed)
+ asic = &card_fw[FW_MONA_361_1_ASIC96];
+ else
+ asic = &card_fw[FW_MONA_361_1_ASIC48];
+ } else {
+ if (double_speed)
+ asic = &card_fw[FW_MONA_301_1_ASIC96];
+ else
+ asic = &card_fw[FW_MONA_301_1_ASIC48];
+ }
+
+ if (asic != chip->asic_code) {
+ /* Load the desired ASIC */
+ err = load_asic_generic(chip, DSP_FNC_LOAD_MONA_PCI_CARD_ASIC,
+ asic);
+ if (err < 0)
+ return err;
+ chip->asic_code = asic;
+ }
+
+ return 0;
+}
+
+
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+ u32 control_reg, clock;
+ const struct firmware *asic;
+ char force_write;
+
+ /* Only set the clock for internal mode. */
+ if (chip->input_clock != ECHO_CLOCK_INTERNAL) {
+ DE_ACT(("set_sample_rate: Cannot set sample rate - "
+ "clock not set to CLK_CLOCKININTERNAL\n"));
+ /* Save the rate anyhow */
+ chip->comm_page->sample_rate = cpu_to_le32(rate);
+ chip->sample_rate = rate;
+ return 0;
+ }
+
+ /* Now, check to see if the required ASIC is loaded */
+ if (rate >= 88200) {
+ if (chip->digital_mode == DIGITAL_MODE_ADAT)
+ return -EINVAL;
+ if (chip->device_id == DEVICE_ID_56361)
+ asic = &card_fw[FW_MONA_361_1_ASIC96];
+ else
+ asic = &card_fw[FW_MONA_301_1_ASIC96];
+ } else {
+ if (chip->device_id == DEVICE_ID_56361)
+ asic = &card_fw[FW_MONA_361_1_ASIC48];
+ else
+ asic = &card_fw[FW_MONA_301_1_ASIC48];
+ }
+
+ force_write = 0;
+ if (asic != chip->asic_code) {
+ int err;
+ /* Load the desired ASIC (load_asic_generic() can sleep) */
+ spin_unlock_irq(&chip->lock);
+ err = load_asic_generic(chip, DSP_FNC_LOAD_MONA_PCI_CARD_ASIC,
+ asic);
+ spin_lock_irq(&chip->lock);
+
+ if (err < 0)
+ return err;
+ chip->asic_code = asic;
+ force_write = 1;
+ }
+
+ /* Compute the new control register value */
+ clock = 0;
+ control_reg = le32_to_cpu(chip->comm_page->control_register);
+ control_reg &= GML_CLOCK_CLEAR_MASK;
+ control_reg &= GML_SPDIF_RATE_CLEAR_MASK;
+
+ switch (rate) {
+ case 96000:
+ clock = GML_96KHZ;
+ break;
+ case 88200:
+ clock = GML_88KHZ;
+ break;
+ case 48000:
+ clock = GML_48KHZ | GML_SPDIF_SAMPLE_RATE1;
+ break;
+ case 44100:
+ clock = GML_44KHZ;
+ /* Professional mode */
+ if (control_reg & GML_SPDIF_PRO_MODE)
+ clock |= GML_SPDIF_SAMPLE_RATE0;
+ break;
+ case 32000:
+ clock = GML_32KHZ | GML_SPDIF_SAMPLE_RATE0 |
+ GML_SPDIF_SAMPLE_RATE1;
+ break;
+ case 22050:
+ clock = GML_22KHZ;
+ break;
+ case 16000:
+ clock = GML_16KHZ;
+ break;
+ case 11025:
+ clock = GML_11KHZ;
+ break;
+ case 8000:
+ clock = GML_8KHZ;
+ break;
+ default:
+ DE_ACT(("set_sample_rate: %d invalid!\n", rate));
+ return -EINVAL;
+ }
+
+ control_reg |= clock;
+
+ chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP */
+ chip->sample_rate = rate;
+ DE_ACT(("set_sample_rate: %d clock %d\n", rate, clock));
+
+ return write_control_reg(chip, control_reg, force_write);
+}
+
+
+
+static int set_input_clock(struct echoaudio *chip, u16 clock)
+{
+ u32 control_reg, clocks_from_dsp;
+ int err;
+
+ DE_ACT(("set_input_clock:\n"));
+
+ /* Prevent two simultaneous calls to switch_asic() */
+ if (atomic_read(&chip->opencount))
+ return -EAGAIN;
+
+ /* Mask off the clock select bits */
+ control_reg = le32_to_cpu(chip->comm_page->control_register) &
+ GML_CLOCK_CLEAR_MASK;
+ clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks);
+
+ switch (clock) {
+ case ECHO_CLOCK_INTERNAL:
+ DE_ACT(("Set Mona clock to INTERNAL\n"));
+ chip->input_clock = ECHO_CLOCK_INTERNAL;
+ return set_sample_rate(chip, chip->sample_rate);
+ case ECHO_CLOCK_SPDIF:
+ if (chip->digital_mode == DIGITAL_MODE_ADAT)
+ return -EAGAIN;
+ spin_unlock_irq(&chip->lock);
+ err = switch_asic(chip, clocks_from_dsp &
+ GML_CLOCK_DETECT_BIT_SPDIF96);
+ spin_lock_irq(&chip->lock);
+ if (err < 0)
+ return err;
+ DE_ACT(("Set Mona clock to SPDIF\n"));
+ control_reg |= GML_SPDIF_CLOCK;
+ if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_SPDIF96)
+ control_reg |= GML_DOUBLE_SPEED_MODE;
+ else
+ control_reg &= ~GML_DOUBLE_SPEED_MODE;
+ break;
+ case ECHO_CLOCK_WORD:
+ DE_ACT(("Set Mona clock to WORD\n"));
+ spin_unlock_irq(&chip->lock);
+ err = switch_asic(chip, clocks_from_dsp &
+ GML_CLOCK_DETECT_BIT_WORD96);
+ spin_lock_irq(&chip->lock);
+ if (err < 0)
+ return err;
+ control_reg |= GML_WORD_CLOCK;
+ if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_WORD96)
+ control_reg |= GML_DOUBLE_SPEED_MODE;
+ else
+ control_reg &= ~GML_DOUBLE_SPEED_MODE;
+ break;
+ case ECHO_CLOCK_ADAT:
+ DE_ACT(("Set Mona clock to ADAT\n"));
+ if (chip->digital_mode != DIGITAL_MODE_ADAT)
+ return -EAGAIN;
+ control_reg |= GML_ADAT_CLOCK;
+ control_reg &= ~GML_DOUBLE_SPEED_MODE;
+ break;
+ default:
+ DE_ACT(("Input clock 0x%x not supported for Mona\n", clock));
+ return -EINVAL;
+ }
+
+ chip->input_clock = clock;
+ return write_control_reg(chip, control_reg, TRUE);
+}
+
+
+
+static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
+{
+ u32 control_reg;
+ int err, incompatible_clock;
+
+ /* Set clock to "internal" if it's not compatible with the new mode */
+ incompatible_clock = FALSE;
+ switch (mode) {
+ case DIGITAL_MODE_SPDIF_OPTICAL:
+ case DIGITAL_MODE_SPDIF_RCA:
+ if (chip->input_clock == ECHO_CLOCK_ADAT)
+ incompatible_clock = TRUE;
+ break;
+ case DIGITAL_MODE_ADAT:
+ if (chip->input_clock == ECHO_CLOCK_SPDIF)
+ incompatible_clock = TRUE;
+ break;
+ default:
+ DE_ACT(("Digital mode not supported: %d\n", mode));
+ return -EINVAL;
+ }
+
+ spin_lock_irq(&chip->lock);
+
+ if (incompatible_clock) { /* Switch to 48KHz, internal */
+ chip->sample_rate = 48000;
+ set_input_clock(chip, ECHO_CLOCK_INTERNAL);
+ }
+
+ /* Clear the current digital mode */
+ control_reg = le32_to_cpu(chip->comm_page->control_register);
+ control_reg &= GML_DIGITAL_MODE_CLEAR_MASK;
+
+ /* Tweak the control reg */
+ switch (mode) {
+ case DIGITAL_MODE_SPDIF_OPTICAL:
+ control_reg |= GML_SPDIF_OPTICAL_MODE;
+ break;
+ case DIGITAL_MODE_SPDIF_RCA:
+ /* GML_SPDIF_OPTICAL_MODE bit cleared */
+ break;
+ case DIGITAL_MODE_ADAT:
+ /* If the current ASIC is the 96KHz ASIC, switch the ASIC
+ and set to 48 KHz */
+ if (chip->asic_code == &card_fw[FW_MONA_361_1_ASIC96] ||
+ chip->asic_code == &card_fw[FW_MONA_301_1_ASIC96]) {
+ set_sample_rate(chip, 48000);
+ }
+ control_reg |= GML_ADAT_MODE;
+ control_reg &= ~GML_DOUBLE_SPEED_MODE;
+ break;
+ }
+
+ err = write_control_reg(chip, control_reg, FALSE);
+ spin_unlock_irq(&chip->lock);
+ if (err < 0)
+ return err;
+ chip->digital_mode = mode;
+
+ DE_ACT(("set_digital_mode to %d\n", mode));
+ return incompatible_clock;
+}
diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c
index 549673ea14a9..493ec0816bb3 100644
--- a/sound/pci/emu10k1/emu10k1.c
+++ b/sound/pci/emu10k1/emu10k1.c
@@ -77,7 +77,7 @@ MODULE_PARM_DESC(subsystem, "Force card subsystem model.");
/*
* Class 0401: 1102:0008 (rev 00) Subsystem: 1102:1001 -> Audigy2 Value Model:SB0400
*/
-static struct pci_device_id snd_emu10k1_ids[] __devinitdata = {
+static struct pci_device_id snd_emu10k1_ids[] = {
{ 0x1102, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* EMU10K1 */
{ 0x1102, 0x0004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, /* Audigy */
{ 0x1102, 0x0008, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, /* Audigy 2 Value SB0400 */
@@ -232,7 +232,7 @@ static int snd_emu10k1_suspend(struct pci_dev *pci, pm_message_t state)
return 0;
}
-int snd_emu10k1_resume(struct pci_dev *pci)
+static int snd_emu10k1_resume(struct pci_dev *pci)
{
struct snd_card *card = pci_get_drvdata(pci);
struct snd_emu10k1 *emu = card->private_data;
diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c
index 42a358f989c3..be65d4db8e27 100644
--- a/sound/pci/emu10k1/emu10k1_main.c
+++ b/sound/pci/emu10k1/emu10k1_main.c
@@ -531,7 +531,7 @@ static void snd_emu10k1_ecard_setadcgain(struct snd_emu10k1 * emu,
snd_emu10k1_ecard_write(emu, emu->ecard_ctrl);
}
-static int __devinit snd_emu10k1_ecard_init(struct snd_emu10k1 * emu)
+static int snd_emu10k1_ecard_init(struct snd_emu10k1 * emu)
{
unsigned int hc_value;
@@ -571,7 +571,7 @@ static int __devinit snd_emu10k1_ecard_init(struct snd_emu10k1 * emu)
return 0;
}
-static int __devinit snd_emu10k1_cardbus_init(struct snd_emu10k1 * emu)
+static int snd_emu10k1_cardbus_init(struct snd_emu10k1 * emu)
{
unsigned long special_port;
unsigned int value;
@@ -633,7 +633,7 @@ static int snd_emu1212m_fpga_netlist_write(struct snd_emu10k1 * emu, int reg, in
return 0;
}
-static int __devinit snd_emu10k1_emu1212m_init(struct snd_emu10k1 * emu)
+static int snd_emu10k1_emu1212m_init(struct snd_emu10k1 * emu)
{
unsigned int i;
int tmp;
@@ -927,6 +927,7 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.ca0151_chip = 1,
.spk71 = 1,
.spdif_bug = 1,
+ .adc_1361t = 1, /* 24 bit capture instead of 16bit */
.ac97_chip = 1} ,
{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10051102,
.driver = "Audigy2", .name = "Audigy 2 EX [1005]",
@@ -936,6 +937,17 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.ca0151_chip = 1,
.spk71 = 1,
.spdif_bug = 1} ,
+ /* Dell OEM/Creative Labs Audigy 2 ZS */
+ /* See ALSA bug#1365 */
+ {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10031102,
+ .driver = "Audigy2", .name = "Audigy 2 ZS [SB0353]",
+ .id = "Audigy2",
+ .emu10k2_chip = 1,
+ .ca0102_chip = 1,
+ .ca0151_chip = 1,
+ .spk71 = 1,
+ .spdif_bug = 1,
+ .ac97_chip = 1} ,
{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10021102,
.driver = "Audigy2", .name = "Audigy 2 Platinum [SB0240P]",
.id = "Audigy2",
@@ -1233,7 +1245,7 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
}
emu->port = pci_resource_start(pci, 0);
- if (request_irq(pci->irq, snd_emu10k1_interrupt, SA_INTERRUPT|SA_SHIRQ, "EMU10K1", (void *)emu)) {
+ if (request_irq(pci->irq, snd_emu10k1_interrupt, IRQF_DISABLED|IRQF_SHARED, "EMU10K1", (void *)emu)) {
err = -EBUSY;
goto error;
}
@@ -1430,6 +1442,10 @@ void snd_emu10k1_resume_init(struct snd_emu10k1 *emu)
{
if (emu->card_capabilities->ecard)
snd_emu10k1_ecard_init(emu);
+ else if (emu->card_capabilities->ca_cardbus_chip)
+ snd_emu10k1_cardbus_init(emu);
+ else if (emu->card_capabilities->emu1212m)
+ snd_emu10k1_emu1212m_init(emu);
else
snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE);
snd_emu10k1_init(emu, emu->enable_ir, 1);
diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c
index 0fb27e4be07b..da1610a571b8 100644
--- a/sound/pci/emu10k1/emu10k1x.c
+++ b/sound/pci/emu10k1/emu10k1x.c
@@ -928,7 +928,7 @@ static int __devinit snd_emu10k1x_create(struct snd_card *card,
}
if (request_irq(pci->irq, snd_emu10k1x_interrupt,
- SA_INTERRUPT|SA_SHIRQ, "EMU10K1X",
+ IRQF_DISABLED|IRQF_SHARED, "EMU10K1X",
(void *)chip)) {
snd_printk(KERN_ERR "emu10k1x: cannot grab irq %d\n", pci->irq);
snd_emu10k1x_free(chip);
@@ -1286,7 +1286,7 @@ static void snd_emu10k1x_midi_interrupt(struct emu10k1x *emu, unsigned int statu
do_emu10k1x_midi_interrupt(emu, &emu->midi, status);
}
-static void snd_emu10k1x_midi_cmd(struct emu10k1x * emu,
+static int snd_emu10k1x_midi_cmd(struct emu10k1x * emu,
struct emu10k1x_midi *midi, unsigned char cmd, int ack)
{
unsigned long flags;
@@ -1312,11 +1312,14 @@ static void snd_emu10k1x_midi_cmd(struct emu10k1x * emu,
ok = 1;
}
spin_unlock_irqrestore(&midi->input_lock, flags);
- if (!ok)
+ if (!ok) {
snd_printk(KERN_ERR "midi_cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)!!!\n",
cmd, emu->port,
mpu401_read_stat(emu, midi),
mpu401_read_data(emu, midi));
+ return 1;
+ }
+ return 0;
}
static int snd_emu10k1x_midi_input_open(struct snd_rawmidi_substream *substream)
@@ -1332,12 +1335,17 @@ static int snd_emu10k1x_midi_input_open(struct snd_rawmidi_substream *substream)
midi->substream_input = substream;
if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_OUTPUT)) {
spin_unlock_irqrestore(&midi->open_lock, flags);
- snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 1);
- snd_emu10k1x_midi_cmd(emu, midi, MPU401_ENTER_UART, 1);
+ if (snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 1))
+ goto error_out;
+ if (snd_emu10k1x_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
+ goto error_out;
} else {
spin_unlock_irqrestore(&midi->open_lock, flags);
}
return 0;
+
+error_out:
+ return -EIO;
}
static int snd_emu10k1x_midi_output_open(struct snd_rawmidi_substream *substream)
@@ -1353,12 +1361,17 @@ static int snd_emu10k1x_midi_output_open(struct snd_rawmidi_substream *substream
midi->substream_output = substream;
if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_INPUT)) {
spin_unlock_irqrestore(&midi->open_lock, flags);
- snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 1);
- snd_emu10k1x_midi_cmd(emu, midi, MPU401_ENTER_UART, 1);
+ if (snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 1))
+ goto error_out;
+ if (snd_emu10k1x_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
+ goto error_out;
} else {
spin_unlock_irqrestore(&midi->open_lock, flags);
}
return 0;
+
+error_out:
+ return -EIO;
}
static int snd_emu10k1x_midi_input_close(struct snd_rawmidi_substream *substream)
@@ -1366,6 +1379,7 @@ static int snd_emu10k1x_midi_input_close(struct snd_rawmidi_substream *substream
struct emu10k1x *emu;
struct emu10k1x_midi *midi = substream->rmidi->private_data;
unsigned long flags;
+ int err = 0;
emu = midi->emu;
snd_assert(emu, return -ENXIO);
@@ -1375,11 +1389,11 @@ static int snd_emu10k1x_midi_input_close(struct snd_rawmidi_substream *substream
midi->substream_input = NULL;
if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_OUTPUT)) {
spin_unlock_irqrestore(&midi->open_lock, flags);
- snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 0);
+ err = snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 0);
} else {
spin_unlock_irqrestore(&midi->open_lock, flags);
}
- return 0;
+ return err;
}
static int snd_emu10k1x_midi_output_close(struct snd_rawmidi_substream *substream)
@@ -1387,6 +1401,7 @@ static int snd_emu10k1x_midi_output_close(struct snd_rawmidi_substream *substrea
struct emu10k1x *emu;
struct emu10k1x_midi *midi = substream->rmidi->private_data;
unsigned long flags;
+ int err = 0;
emu = midi->emu;
snd_assert(emu, return -ENXIO);
@@ -1396,11 +1411,11 @@ static int snd_emu10k1x_midi_output_close(struct snd_rawmidi_substream *substrea
midi->substream_output = NULL;
if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_INPUT)) {
spin_unlock_irqrestore(&midi->open_lock, flags);
- snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 0);
+ err = snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 0);
} else {
spin_unlock_irqrestore(&midi->open_lock, flags);
}
- return 0;
+ return err;
}
static void snd_emu10k1x_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
@@ -1594,7 +1609,7 @@ static void __devexit snd_emu10k1x_remove(struct pci_dev *pci)
}
// PCI IDs
-static struct pci_device_id snd_emu10k1x_ids[] __devinitdata = {
+static struct pci_device_id snd_emu10k1x_ids[] = {
{ 0x1102, 0x0006, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* Dell OEM version (EMU10K1) */
{ 0, }
};
@@ -1611,12 +1626,7 @@ static struct pci_driver driver = {
// initialization of the module
static int __init alsa_card_emu10k1x_init(void)
{
- int err;
-
- if ((err = pci_register_driver(&driver)) > 0)
- return err;
-
- return 0;
+ return pci_register_driver(&driver);
}
// clean up the module
diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c
index dfba00230d4d..13cd6ce89811 100644
--- a/sound/pci/emu10k1/emufx.c
+++ b/sound/pci/emu10k1/emufx.c
@@ -35,6 +35,7 @@
#include <linux/mutex.h>
#include <sound/core.h>
+#include <sound/tlv.h>
#include <sound/emu10k1.h>
#if 0 /* for testing purposes - digital out -> capture */
@@ -266,6 +267,7 @@ static const u32 treble_table[41][5] = {
{ 0x37c4448b, 0xa45ef51d, 0x262f3267, 0x081e36dc, 0xfd8f5d14 }
};
+/* dB gain = (float) 20 * log10( float(db_table_value) / 0x8000000 ) */
static const u32 db_table[101] = {
0x00000000, 0x01571f82, 0x01674b41, 0x01783a1b, 0x0189f540,
0x019c8651, 0x01aff763, 0x01c45306, 0x01d9a446, 0x01eff6b8,
@@ -290,6 +292,9 @@ static const u32 db_table[101] = {
0x7fffffff,
};
+/* EMU10k1/EMU10k2 DSP control db gain */
+static DECLARE_TLV_DB_SCALE(snd_emu10k1_db_scale1, -4000, 40, 1);
+
static const u32 onoff_table[2] = {
0x00000000, 0x00000001
};
@@ -755,6 +760,11 @@ static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu,
knew.device = gctl->id.device;
knew.subdevice = gctl->id.subdevice;
knew.info = snd_emu10k1_gpr_ctl_info;
+ if (gctl->tlv.p) {
+ knew.tlv.p = gctl->tlv.p;
+ knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+ }
knew.get = snd_emu10k1_gpr_ctl_get;
knew.put = snd_emu10k1_gpr_ctl_put;
memset(nctl, 0, sizeof(*nctl));
@@ -1013,6 +1023,7 @@ snd_emu10k1_init_mono_control(struct snd_emu10k1_fx8010_control_gpr *ctl,
ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
ctl->min = 0;
ctl->max = 100;
+ ctl->tlv.p = snd_emu10k1_db_scale1;
ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100;
}
@@ -1027,6 +1038,7 @@ snd_emu10k1_init_stereo_control(struct snd_emu10k1_fx8010_control_gpr *ctl,
ctl->gpr[1] = gpr + 1; ctl->value[1] = defval;
ctl->min = 0;
ctl->max = 100;
+ ctl->tlv.p = snd_emu10k1_db_scale1;
ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100;
}
diff --git a/sound/pci/emu10k1/emumpu401.c b/sound/pci/emu10k1/emumpu401.c
index d96eb455103f..950c6bcd6b7d 100644
--- a/sound/pci/emu10k1/emumpu401.c
+++ b/sound/pci/emu10k1/emumpu401.c
@@ -116,7 +116,7 @@ static void snd_emu10k1_midi_interrupt2(struct snd_emu10k1 *emu, unsigned int st
do_emu10k1_midi_interrupt(emu, &emu->midi2, status);
}
-static void snd_emu10k1_midi_cmd(struct snd_emu10k1 * emu, struct snd_emu10k1_midi *midi, unsigned char cmd, int ack)
+static int snd_emu10k1_midi_cmd(struct snd_emu10k1 * emu, struct snd_emu10k1_midi *midi, unsigned char cmd, int ack)
{
unsigned long flags;
int timeout, ok;
@@ -141,11 +141,14 @@ static void snd_emu10k1_midi_cmd(struct snd_emu10k1 * emu, struct snd_emu10k1_mi
ok = 1;
}
spin_unlock_irqrestore(&midi->input_lock, flags);
- if (!ok)
+ if (!ok) {
snd_printk(KERN_ERR "midi_cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)!!!\n",
cmd, emu->port,
mpu401_read_stat(emu, midi),
mpu401_read_data(emu, midi));
+ return 1;
+ }
+ return 0;
}
static int snd_emu10k1_midi_input_open(struct snd_rawmidi_substream *substream)
@@ -161,12 +164,17 @@ static int snd_emu10k1_midi_input_open(struct snd_rawmidi_substream *substream)
midi->substream_input = substream;
if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) {
spin_unlock_irqrestore(&midi->open_lock, flags);
- snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1);
- snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1);
+ if (snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1))
+ goto error_out;
+ if (snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
+ goto error_out;
} else {
spin_unlock_irqrestore(&midi->open_lock, flags);
}
return 0;
+
+error_out:
+ return -EIO;
}
static int snd_emu10k1_midi_output_open(struct snd_rawmidi_substream *substream)
@@ -182,12 +190,17 @@ static int snd_emu10k1_midi_output_open(struct snd_rawmidi_substream *substream)
midi->substream_output = substream;
if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
spin_unlock_irqrestore(&midi->open_lock, flags);
- snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1);
- snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1);
+ if (snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1))
+ goto error_out;
+ if (snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
+ goto error_out;
} else {
spin_unlock_irqrestore(&midi->open_lock, flags);
}
return 0;
+
+error_out:
+ return -EIO;
}
static int snd_emu10k1_midi_input_close(struct snd_rawmidi_substream *substream)
@@ -195,6 +208,7 @@ static int snd_emu10k1_midi_input_close(struct snd_rawmidi_substream *substream)
struct snd_emu10k1 *emu;
struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
unsigned long flags;
+ int err = 0;
emu = midi->emu;
snd_assert(emu, return -ENXIO);
@@ -204,11 +218,11 @@ static int snd_emu10k1_midi_input_close(struct snd_rawmidi_substream *substream)
midi->substream_input = NULL;
if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) {
spin_unlock_irqrestore(&midi->open_lock, flags);
- snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0);
+ err = snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0);
} else {
spin_unlock_irqrestore(&midi->open_lock, flags);
}
- return 0;
+ return err;
}
static int snd_emu10k1_midi_output_close(struct snd_rawmidi_substream *substream)
@@ -216,6 +230,7 @@ static int snd_emu10k1_midi_output_close(struct snd_rawmidi_substream *substream
struct snd_emu10k1 *emu;
struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
unsigned long flags;
+ int err = 0;
emu = midi->emu;
snd_assert(emu, return -ENXIO);
@@ -225,11 +240,11 @@ static int snd_emu10k1_midi_output_close(struct snd_rawmidi_substream *substream
midi->substream_output = NULL;
if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
spin_unlock_irqrestore(&midi->open_lock, flags);
- snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0);
+ err = snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0);
} else {
spin_unlock_irqrestore(&midi->open_lock, flags);
}
- return 0;
+ return err;
}
static void snd_emu10k1_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
diff --git a/sound/pci/emu10k1/irq.c b/sound/pci/emu10k1/irq.c
index a8b31286b6db..1076af4c3669 100644
--- a/sound/pci/emu10k1/irq.c
+++ b/sound/pci/emu10k1/irq.c
@@ -37,9 +37,13 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id, struct pt_regs *regs)
int handled = 0;
while ((status = inl(emu->port + IPR)) != 0) {
- //printk("emu10k1 irq - status = 0x%x\n", status);
+ //snd_printk(KERN_INFO "emu10k1 irq - status = 0x%x\n", status);
orig_status = status;
handled = 1;
+ if ((status & 0xffffffff) == 0xffffffff) {
+ snd_printk(KERN_INFO "snd-emu10k1: Suspected sound card removal\n");
+ break;
+ }
if (status & IPR_PCIERROR) {
snd_printk(KERN_ERR "interrupt: PCI error\n");
snd_emu10k1_intr_disable(emu, INTE_PCIERRORENABLE);
diff --git a/sound/pci/emu10k1/p16v.c b/sound/pci/emu10k1/p16v.c
index 9905651935fb..4e0f95438f47 100644
--- a/sound/pci/emu10k1/p16v.c
+++ b/sound/pci/emu10k1/p16v.c
@@ -100,6 +100,7 @@
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
#include <sound/info.h>
+#include <sound/tlv.h>
#include <sound/emu10k1.h>
#include "p16v.h"
@@ -784,12 +785,16 @@ static int snd_p16v_capture_channel_put(struct snd_kcontrol *kcontrol,
}
return change;
}
+static DECLARE_TLV_DB_SCALE(snd_p16v_db_scale1, -5175, 25, 1);
#define P16V_VOL(xname,xreg,xhl) { \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
.info = snd_p16v_volume_info, \
.get = snd_p16v_volume_get, \
.put = snd_p16v_volume_put, \
+ .tlv = { .p = snd_p16v_db_scale1 }, \
.private_value = ((xreg) | ((xhl) << 8)) \
}
diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c
index 9d46bbee2a40..a8a601fc781f 100644
--- a/sound/pci/ens1370.c
+++ b/sound/pci/ens1370.c
@@ -446,7 +446,7 @@ struct ensoniq {
static irqreturn_t snd_audiopci_interrupt(int irq, void *dev_id, struct pt_regs *regs);
-static struct pci_device_id snd_audiopci_ids[] __devinitdata = {
+static struct pci_device_id snd_audiopci_ids[] = {
#ifdef CHIP1370
{ 0x1274, 0x5000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* ES1370 */
#endif
@@ -2135,7 +2135,7 @@ static int __devinit snd_ensoniq_create(struct snd_card *card,
return err;
}
ensoniq->port = pci_resource_start(pci, 0);
- if (request_irq(pci->irq, snd_audiopci_interrupt, SA_INTERRUPT|SA_SHIRQ,
+ if (request_irq(pci->irq, snd_audiopci_interrupt, IRQF_DISABLED|IRQF_SHARED,
"Ensoniq AudioPCI", ensoniq)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_ensoniq_free(ensoniq);
diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c
index ca6603fe0b11..3ce5a4e7e31f 100644
--- a/sound/pci/es1938.c
+++ b/sound/pci/es1938.c
@@ -62,6 +62,7 @@
#include <sound/opl3.h>
#include <sound/mpu401.h>
#include <sound/initval.h>
+#include <sound/tlv.h>
#include <asm/io.h>
@@ -242,7 +243,7 @@ struct es1938 {
static irqreturn_t snd_es1938_interrupt(int irq, void *dev_id, struct pt_regs *regs);
-static struct pci_device_id snd_es1938_ids[] __devinitdata = {
+static struct pci_device_id snd_es1938_ids[] = {
{ 0x125d, 0x1969, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Solo-1 */
{ 0, }
};
@@ -1164,6 +1165,14 @@ static int snd_es1938_reg_read(struct es1938 *chip, unsigned char reg)
return snd_es1938_read(chip, reg);
}
+#define ES1938_SINGLE_TLV(xname, xindex, reg, shift, mask, invert, xtlv) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,\
+ .name = xname, .index = xindex, \
+ .info = snd_es1938_info_single, \
+ .get = snd_es1938_get_single, .put = snd_es1938_put_single, \
+ .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24), \
+ .tlv = { .p = xtlv } }
#define ES1938_SINGLE(xname, xindex, reg, shift, mask, invert) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
.info = snd_es1938_info_single, \
@@ -1217,6 +1226,14 @@ static int snd_es1938_put_single(struct snd_kcontrol *kcontrol,
return snd_es1938_reg_bits(chip, reg, mask, val) != val;
}
+#define ES1938_DOUBLE_TLV(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert, xtlv) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,\
+ .name = xname, .index = xindex, \
+ .info = snd_es1938_info_double, \
+ .get = snd_es1938_get_double, .put = snd_es1938_put_double, \
+ .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22), \
+ .tlv = { .p = xtlv } }
#define ES1938_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
.info = snd_es1938_info_double, \
@@ -1297,8 +1314,41 @@ static int snd_es1938_put_double(struct snd_kcontrol *kcontrol,
return change;
}
+static unsigned int db_scale_master[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 54, TLV_DB_SCALE_ITEM(-3600, 50, 1),
+ 54, 63, TLV_DB_SCALE_ITEM(-900, 100, 0),
+};
+
+static unsigned int db_scale_audio1[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 8, TLV_DB_SCALE_ITEM(-3300, 300, 1),
+ 8, 15, TLV_DB_SCALE_ITEM(-900, 150, 0),
+};
+
+static unsigned int db_scale_audio2[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 8, TLV_DB_SCALE_ITEM(-3450, 300, 1),
+ 8, 15, TLV_DB_SCALE_ITEM(-1050, 150, 0),
+};
+
+static unsigned int db_scale_mic[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 8, TLV_DB_SCALE_ITEM(-2400, 300, 1),
+ 8, 15, TLV_DB_SCALE_ITEM(0, 150, 0),
+};
+
+static unsigned int db_scale_line[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 8, TLV_DB_SCALE_ITEM(-3150, 300, 1),
+ 8, 15, TLV_DB_SCALE_ITEM(-750, 150, 0),
+};
+
+static DECLARE_TLV_DB_SCALE(db_scale_capture, 0, 150, 0);
+
static struct snd_kcontrol_new snd_es1938_controls[] = {
-ES1938_DOUBLE("Master Playback Volume", 0, 0x60, 0x62, 0, 0, 63, 0),
+ES1938_DOUBLE_TLV("Master Playback Volume", 0, 0x60, 0x62, 0, 0, 63, 0,
+ db_scale_master),
ES1938_DOUBLE("Master Playback Switch", 0, 0x60, 0x62, 6, 6, 1, 1),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1309,19 +1359,27 @@ ES1938_DOUBLE("Master Playback Switch", 0, 0x60, 0x62, 6, 6, 1, 1),
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Hardware Master Playback Switch",
- .access = SNDRV_CTL_ELEM_ACCESS_READ,
.info = snd_es1938_info_hw_switch,
.get = snd_es1938_get_hw_switch,
+ .tlv = { .p = db_scale_master },
},
ES1938_SINGLE("Hardware Volume Split", 0, 0x64, 7, 1, 0),
-ES1938_DOUBLE("Line Playback Volume", 0, 0x3e, 0x3e, 4, 0, 15, 0),
+ES1938_DOUBLE_TLV("Line Playback Volume", 0, 0x3e, 0x3e, 4, 0, 15, 0,
+ db_scale_line),
ES1938_DOUBLE("CD Playback Volume", 0, 0x38, 0x38, 4, 0, 15, 0),
-ES1938_DOUBLE("FM Playback Volume", 0, 0x36, 0x36, 4, 0, 15, 0),
-ES1938_DOUBLE("Mono Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0),
-ES1938_DOUBLE("Mic Playback Volume", 0, 0x1a, 0x1a, 4, 0, 15, 0),
-ES1938_DOUBLE("Aux Playback Volume", 0, 0x3a, 0x3a, 4, 0, 15, 0),
-ES1938_DOUBLE("Capture Volume", 0, 0xb4, 0xb4, 4, 0, 15, 0),
+ES1938_DOUBLE_TLV("FM Playback Volume", 0, 0x36, 0x36, 4, 0, 15, 0,
+ db_scale_mic),
+ES1938_DOUBLE_TLV("Mono Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0,
+ db_scale_line),
+ES1938_DOUBLE_TLV("Mic Playback Volume", 0, 0x1a, 0x1a, 4, 0, 15, 0,
+ db_scale_mic),
+ES1938_DOUBLE_TLV("Aux Playback Volume", 0, 0x3a, 0x3a, 4, 0, 15, 0,
+ db_scale_line),
+ES1938_DOUBLE_TLV("Capture Volume", 0, 0xb4, 0xb4, 4, 0, 15, 0,
+ db_scale_capture),
ES1938_SINGLE("PC Speaker Volume", 0, 0x3c, 0, 7, 0),
ES1938_SINGLE("Record Monitor", 0, 0xa8, 3, 1, 0),
ES1938_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1),
@@ -1332,16 +1390,26 @@ ES1938_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1),
.get = snd_es1938_get_mux,
.put = snd_es1938_put_mux,
},
-ES1938_DOUBLE("Mono Input Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0),
-ES1938_DOUBLE("PCM Capture Volume", 0, 0x69, 0x69, 4, 0, 15, 0),
-ES1938_DOUBLE("Mic Capture Volume", 0, 0x68, 0x68, 4, 0, 15, 0),
-ES1938_DOUBLE("Line Capture Volume", 0, 0x6e, 0x6e, 4, 0, 15, 0),
-ES1938_DOUBLE("FM Capture Volume", 0, 0x6b, 0x6b, 4, 0, 15, 0),
-ES1938_DOUBLE("Mono Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0),
-ES1938_DOUBLE("CD Capture Volume", 0, 0x6a, 0x6a, 4, 0, 15, 0),
-ES1938_DOUBLE("Aux Capture Volume", 0, 0x6c, 0x6c, 4, 0, 15, 0),
-ES1938_DOUBLE("PCM Playback Volume", 0, 0x7c, 0x7c, 4, 0, 15, 0),
-ES1938_DOUBLE("PCM Playback Volume", 1, 0x14, 0x14, 4, 0, 15, 0),
+ES1938_DOUBLE_TLV("Mono Input Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0,
+ db_scale_line),
+ES1938_DOUBLE_TLV("PCM Capture Volume", 0, 0x69, 0x69, 4, 0, 15, 0,
+ db_scale_audio2),
+ES1938_DOUBLE_TLV("Mic Capture Volume", 0, 0x68, 0x68, 4, 0, 15, 0,
+ db_scale_mic),
+ES1938_DOUBLE_TLV("Line Capture Volume", 0, 0x6e, 0x6e, 4, 0, 15, 0,
+ db_scale_line),
+ES1938_DOUBLE_TLV("FM Capture Volume", 0, 0x6b, 0x6b, 4, 0, 15, 0,
+ db_scale_mic),
+ES1938_DOUBLE_TLV("Mono Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0,
+ db_scale_line),
+ES1938_DOUBLE_TLV("CD Capture Volume", 0, 0x6a, 0x6a, 4, 0, 15, 0,
+ db_scale_line),
+ES1938_DOUBLE_TLV("Aux Capture Volume", 0, 0x6c, 0x6c, 4, 0, 15, 0,
+ db_scale_line),
+ES1938_DOUBLE_TLV("PCM Playback Volume", 0, 0x7c, 0x7c, 4, 0, 15, 0,
+ db_scale_audio2),
+ES1938_DOUBLE_TLV("PCM Playback Volume", 1, 0x14, 0x14, 4, 0, 15, 0,
+ db_scale_audio1),
ES1938_SINGLE("3D Control - Level", 0, 0x52, 0, 63, 0),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1429,7 +1497,7 @@ static int es1938_resume(struct pci_dev *pci)
pci_restore_state(pci);
pci_enable_device(pci);
request_irq(pci->irq, snd_es1938_interrupt,
- SA_INTERRUPT|SA_SHIRQ, "ES1938", chip);
+ IRQF_DISABLED|IRQF_SHARED, "ES1938", chip);
chip->irq = pci->irq;
snd_es1938_chip_init(chip);
@@ -1544,7 +1612,7 @@ static int __devinit snd_es1938_create(struct snd_card *card,
chip->vc_port = pci_resource_start(pci, 2);
chip->mpu_port = pci_resource_start(pci, 3);
chip->game_port = pci_resource_start(pci, 4);
- if (request_irq(pci->irq, snd_es1938_interrupt, SA_INTERRUPT|SA_SHIRQ,
+ if (request_irq(pci->irq, snd_es1938_interrupt, IRQF_DISABLED|IRQF_SHARED,
"ES1938", chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_es1938_free(chip);
diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c
index bfa0876e715e..f3c40385c87d 100644
--- a/sound/pci/es1968.c
+++ b/sound/pci/es1968.c
@@ -592,7 +592,7 @@ struct es1968 {
static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id, struct pt_regs *regs);
-static struct pci_device_id snd_es1968_ids[] __devinitdata = {
+static struct pci_device_id snd_es1968_ids[] = {
/* Maestro 1 */
{ 0x1285, 0x0100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, TYPE_MAESTRO },
/* Maestro 2 */
@@ -1905,7 +1905,7 @@ static void es1968_update_hw_volume(unsigned long private_data)
/* Figure out which volume control button was pushed,
based on differences from the default register
values. */
- x = inb(chip->io_port + 0x1c);
+ x = inb(chip->io_port + 0x1c) & 0xee;
/* Reset the volume control registers. */
outb(0x88, chip->io_port + 0x1c);
outb(0x88, chip->io_port + 0x1d);
@@ -1921,7 +1921,8 @@ static void es1968_update_hw_volume(unsigned long private_data)
/* FIXME: we can't call snd_ac97_* functions since here is in tasklet. */
spin_lock_irqsave(&chip->ac97_lock, flags);
val = chip->ac97->regs[AC97_MASTER];
- if (x & 1) {
+ switch (x) {
+ case 0x88:
/* mute */
val ^= 0x8000;
chip->ac97->regs[AC97_MASTER] = val;
@@ -1929,26 +1930,31 @@ static void es1968_update_hw_volume(unsigned long private_data)
outb(AC97_MASTER, chip->io_port + ESM_AC97_INDEX);
snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
&chip->master_switch->id);
- } else {
- val &= 0x7fff;
- if (((x>>1) & 7) > 4) {
- /* volume up */
- if ((val & 0xff) > 0)
- val--;
- if ((val & 0xff00) > 0)
- val -= 0x0100;
- } else {
- /* volume down */
- if ((val & 0xff) < 0x1f)
- val++;
- if ((val & 0xff00) < 0x1f00)
- val += 0x0100;
- }
+ break;
+ case 0xaa:
+ /* volume up */
+ if ((val & 0x7f) > 0)
+ val--;
+ if ((val & 0x7f00) > 0)
+ val -= 0x0100;
+ chip->ac97->regs[AC97_MASTER] = val;
+ outw(val, chip->io_port + ESM_AC97_DATA);
+ outb(AC97_MASTER, chip->io_port + ESM_AC97_INDEX);
+ snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &chip->master_volume->id);
+ break;
+ case 0x66:
+ /* volume down */
+ if ((val & 0x7f) < 0x1f)
+ val++;
+ if ((val & 0x7f00) < 0x1f00)
+ val += 0x0100;
chip->ac97->regs[AC97_MASTER] = val;
outw(val, chip->io_port + ESM_AC97_DATA);
outb(AC97_MASTER, chip->io_port + ESM_AC97_INDEX);
snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
&chip->master_volume->id);
+ break;
}
spin_unlock_irqrestore(&chip->ac97_lock, flags);
}
@@ -2597,7 +2603,7 @@ static int __devinit snd_es1968_create(struct snd_card *card,
return err;
}
chip->io_port = pci_resource_start(pci, 0);
- if (request_irq(pci->irq, snd_es1968_interrupt, SA_INTERRUPT|SA_SHIRQ,
+ if (request_irq(pci->irq, snd_es1968_interrupt, IRQF_DISABLED|IRQF_SHARED,
"ESS Maestro", (void*)chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_es1968_free(chip);
diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c
index 0afa573dd244..bdfda1997d5b 100644
--- a/sound/pci/fm801.c
+++ b/sound/pci/fm801.c
@@ -2,6 +2,7 @@
* The driver for the ForteMedia FM801 based soundcards
* Copyright (c) by Jaroslav Kysela <perex@suse.cz>
*
+ * Support FM only card by Andy Shevchenko <andy@smile.org.ua>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -28,6 +29,7 @@
#include <linux/moduleparam.h>
#include <sound/core.h>
#include <sound/pcm.h>
+#include <sound/tlv.h>
#include <sound/ac97_codec.h>
#include <sound/mpu401.h>
#include <sound/opl3.h>
@@ -35,7 +37,7 @@
#include <asm/io.h>
-#if (defined(CONFIG_SND_FM801_TEA575X) || defined(CONFIG_SND_FM801_TEA575X_MODULE)) && (defined(CONFIG_VIDEO_DEV) || defined(CONFIG_VIDEO_DEV_MODULE))
+#ifdef CONFIG_SND_FM801_TEA575X_BOOL
#include <sound/tea575x-tuner.h>
#define TEA575X_RADIO 1
#endif
@@ -54,6 +56,7 @@ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card *
* 1 = MediaForte 256-PCS
* 2 = MediaForte 256-PCPR
* 3 = MediaForte 64-PCR
+ * 16 = setup tuner only (this is additional bit), i.e. SF-64-PCR FM card
* High 16-bits are video (radio) device number + 1
*/
static int tea575x_tuner[SNDRV_CARDS];
@@ -158,6 +161,7 @@ struct fm801 {
unsigned int multichannel: 1, /* multichannel support */
secondary: 1; /* secondary codec */
unsigned char secondary_addr; /* address of the secondary codec */
+ unsigned int tea575x_tuner; /* tuner flags */
unsigned short ply_ctrl; /* playback control */
unsigned short cap_ctrl; /* capture control */
@@ -199,7 +203,7 @@ struct fm801 {
#endif
};
-static struct pci_device_id snd_fm801_ids[] __devinitdata = {
+static struct pci_device_id snd_fm801_ids[] = {
{ 0x1319, 0x0801, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, }, /* FM801 */
{ 0x5213, 0x0510, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, }, /* Gallant Odyssey Sound 4 */
{ 0, }
@@ -318,10 +322,8 @@ static unsigned int channels[] = {
2, 4, 6
};
-#define CHANNELS sizeof(channels) / sizeof(channels[0])
-
static struct snd_pcm_hw_constraint_list hw_constraints_channels = {
- .count = CHANNELS,
+ .count = ARRAY_SIZE(channels),
.list = channels,
.mask = 0,
};
@@ -1052,6 +1054,13 @@ static int snd_fm801_put_single(struct snd_kcontrol *kcontrol,
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_fm801_info_double, \
.get = snd_fm801_get_double, .put = snd_fm801_put_double, \
.private_value = reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) }
+#define FM801_DOUBLE_TLV(xname, reg, shift_left, shift_right, mask, invert, xtlv) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
+ .name = xname, .info = snd_fm801_info_double, \
+ .get = snd_fm801_get_double, .put = snd_fm801_put_double, \
+ .private_value = reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24), \
+ .tlv = { .p = (xtlv) } }
static int snd_fm801_info_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
@@ -1148,14 +1157,19 @@ static int snd_fm801_put_mux(struct snd_kcontrol *kcontrol,
return snd_fm801_update_bits(chip, FM801_REC_SRC, 7, val);
}
+static DECLARE_TLV_DB_SCALE(db_scale_dsp, -3450, 150, 0);
+
#define FM801_CONTROLS ARRAY_SIZE(snd_fm801_controls)
static struct snd_kcontrol_new snd_fm801_controls[] __devinitdata = {
-FM801_DOUBLE("Wave Playback Volume", FM801_PCM_VOL, 0, 8, 31, 1),
+FM801_DOUBLE_TLV("Wave Playback Volume", FM801_PCM_VOL, 0, 8, 31, 1,
+ db_scale_dsp),
FM801_SINGLE("Wave Playback Switch", FM801_PCM_VOL, 15, 1, 1),
-FM801_DOUBLE("I2S Playback Volume", FM801_I2S_VOL, 0, 8, 31, 1),
+FM801_DOUBLE_TLV("I2S Playback Volume", FM801_I2S_VOL, 0, 8, 31, 1,
+ db_scale_dsp),
FM801_SINGLE("I2S Playback Switch", FM801_I2S_VOL, 15, 1, 1),
-FM801_DOUBLE("FM Playback Volume", FM801_FM_VOL, 0, 8, 31, 1),
+FM801_DOUBLE_TLV("FM Playback Volume", FM801_FM_VOL, 0, 8, 31, 1,
+ db_scale_dsp),
FM801_SINGLE("FM Playback Switch", FM801_FM_VOL, 15, 1, 1),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1253,6 +1267,9 @@ static int snd_fm801_chip_init(struct fm801 *chip, int resume)
int id;
unsigned short cmdw;
+ if (chip->tea575x_tuner & 0x0010)
+ goto __ac97_ok;
+
/* codec cold reset + AC'97 warm reset */
outw((1<<5) | (1<<6), FM801_REG(chip, CODEC_CTRL));
inw(FM801_REG(chip, CODEC_CTRL)); /* flush posting data */
@@ -1290,6 +1307,8 @@ static int snd_fm801_chip_init(struct fm801 *chip, int resume)
wait_for_codec(chip, 0, AC97_VENDOR_ID1, msecs_to_jiffies(750));
}
+ __ac97_ok:
+
/* init volume */
outw(0x0808, FM801_REG(chip, PCM_VOL));
outw(0x9f1f, FM801_REG(chip, FM_VOL));
@@ -1298,9 +1317,12 @@ static int snd_fm801_chip_init(struct fm801 *chip, int resume)
/* I2S control - I2S mode */
outw(0x0003, FM801_REG(chip, I2S_MODE));
- /* interrupt setup - unmask MPU, PLAYBACK & CAPTURE */
+ /* interrupt setup */
cmdw = inw(FM801_REG(chip, IRQ_MASK));
- cmdw &= ~0x0083;
+ if (chip->irq < 0)
+ cmdw |= 0x00c3; /* mask everything, no PCM nor MPU */
+ else
+ cmdw &= ~0x0083; /* unmask MPU, PLAYBACK & CAPTURE */
outw(cmdw, FM801_REG(chip, IRQ_MASK));
/* interrupt clear */
@@ -1365,20 +1387,23 @@ static int __devinit snd_fm801_create(struct snd_card *card,
chip->card = card;
chip->pci = pci;
chip->irq = -1;
+ chip->tea575x_tuner = tea575x_tuner;
if ((err = pci_request_regions(pci, "FM801")) < 0) {
kfree(chip);
pci_disable_device(pci);
return err;
}
chip->port = pci_resource_start(pci, 0);
- if (request_irq(pci->irq, snd_fm801_interrupt, SA_INTERRUPT|SA_SHIRQ,
- "FM801", chip)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", chip->irq);
- snd_fm801_free(chip);
- return -EBUSY;
+ if ((tea575x_tuner & 0x0010) == 0) {
+ if (request_irq(pci->irq, snd_fm801_interrupt, IRQF_DISABLED|IRQF_SHARED,
+ "FM801", chip)) {
+ snd_printk(KERN_ERR "unable to grab IRQ %d\n", chip->irq);
+ snd_fm801_free(chip);
+ return -EBUSY;
+ }
+ chip->irq = pci->irq;
+ pci_set_master(pci);
}
- chip->irq = pci->irq;
- pci_set_master(pci);
pci_read_config_byte(pci, PCI_REVISION_ID, &rev);
if (rev >= 0xb1) /* FM801-AU */
@@ -1394,12 +1419,12 @@ static int __devinit snd_fm801_create(struct snd_card *card,
snd_card_set_dev(card, &pci->dev);
#ifdef TEA575X_RADIO
- if (tea575x_tuner > 0 && (tea575x_tuner & 0xffff) < 4) {
+ if (tea575x_tuner > 0 && (tea575x_tuner & 0x000f) < 4) {
chip->tea.dev_nr = tea575x_tuner >> 16;
chip->tea.card = card;
chip->tea.freq_fixup = 10700;
chip->tea.private_data = chip;
- chip->tea.ops = &snd_fm801_tea_ops[(tea575x_tuner & 0xffff) - 1];
+ chip->tea.ops = &snd_fm801_tea_ops[(tea575x_tuner & 0x000f) - 1];
snd_tea575x_init(&chip->tea);
}
#endif
@@ -1439,6 +1464,9 @@ static int __devinit snd_card_fm801_probe(struct pci_dev *pci,
sprintf(card->longname, "%s at 0x%lx, irq %i",
card->shortname, chip->port, chip->irq);
+ if (tea575x_tuner[dev] & 0x0010)
+ goto __fm801_tuner_only;
+
if ((err = snd_fm801_pcm(chip, 0, NULL)) < 0) {
snd_card_free(card);
return err;
@@ -1465,6 +1493,7 @@ static int __devinit snd_card_fm801_probe(struct pci_dev *pci,
return err;
}
+ __fm801_tuner_only:
if ((err = snd_card_register(card)) < 0) {
snd_card_free(card);
return err;
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 8c2a8174ece1..9c3d7ac08068 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -29,6 +29,7 @@
#include <sound/core.h>
#include "hda_codec.h"
#include <sound/asoundef.h>
+#include <sound/tlv.h>
#include <sound/initval.h>
#include "hda_local.h"
@@ -50,8 +51,10 @@ struct hda_vendor_id {
/* codec vendor labels */
static struct hda_vendor_id hda_vendor_ids[] = {
{ 0x10ec, "Realtek" },
+ { 0x1057, "Motorola" },
{ 0x11d4, "Analog Devices" },
{ 0x13f6, "C-Media" },
+ { 0x14f1, "Conexant" },
{ 0x434d, "C-Media" },
{ 0x8384, "SigmaTel" },
{} /* terminator */
@@ -408,7 +411,9 @@ static const struct hda_codec_preset *find_codec_preset(struct hda_codec *codec)
u32 mask = preset->mask;
if (! mask)
mask = ~0;
- if (preset->id == (codec->vendor_id & mask))
+ if (preset->id == (codec->vendor_id & mask) &&
+ (! preset->rev ||
+ preset->rev == codec->revision_id))
return preset;
}
}
@@ -839,6 +844,31 @@ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
return change;
}
+int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+ unsigned int size, unsigned int __user *_tlv)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int dir = get_amp_direction(kcontrol);
+ u32 caps, val1, val2;
+
+ if (size < 4 * sizeof(unsigned int))
+ return -ENOMEM;
+ caps = query_amp_caps(codec, nid, dir);
+ val2 = (((caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT) + 1) * 25;
+ val1 = -((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT);
+ val1 = ((int)val1) * ((int)val2);
+ if (put_user(SNDRV_CTL_TLVT_DB_SCALE, _tlv))
+ return -EFAULT;
+ if (put_user(2 * sizeof(unsigned int), _tlv + 1))
+ return -EFAULT;
+ if (put_user(val1, _tlv + 2))
+ return -EFAULT;
+ if (put_user(val2, _tlv + 3))
+ return -EFAULT;
+ return 0;
+}
+
/* switch */
int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
@@ -1475,10 +1505,10 @@ int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
formats |= SNDRV_PCM_FMTBIT_S32_LE;
if (val & AC_SUPPCM_BITS_32)
bps = 32;
- else if (val & AC_SUPPCM_BITS_20)
- bps = 20;
else if (val & AC_SUPPCM_BITS_24)
bps = 24;
+ else if (val & AC_SUPPCM_BITS_20)
+ bps = 20;
}
}
else if (streams == AC_SUPFMT_FLOAT32) { /* should be exclusive */
@@ -1914,7 +1944,7 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, struct hda_multi_o
/* front */
snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, 0, format);
- if (mout->hp_nid)
+ if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT])
/* headphone out will just decode front left/right (stereo) */
snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 0, format);
/* extra outputs copied from front */
@@ -1982,7 +2012,7 @@ static int is_in_nid_list(hda_nid_t nid, hda_nid_t *list)
* in the order of front, rear, CLFE, side, ...
*
* If more extra outputs (speaker and headphone) are found, the pins are
- * assisnged to hp_pin and speaker_pins[], respectively. If no line-out jack
+ * assisnged to hp_pins[] and speaker_pins[], respectively. If no line-out jack
* is detected, one of speaker of HP pins is assigned as the primary
* output, i.e. to line_out_pins[0]. So, line_outs is always positive
* if any analog output exists.
@@ -2044,14 +2074,26 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *c
cfg->speaker_outs++;
break;
case AC_JACK_HP_OUT:
- cfg->hp_pin = nid;
+ if (cfg->hp_outs >= ARRAY_SIZE(cfg->hp_pins))
+ continue;
+ cfg->hp_pins[cfg->hp_outs] = nid;
+ cfg->hp_outs++;
break;
- case AC_JACK_MIC_IN:
- if (loc == AC_JACK_LOC_FRONT)
- cfg->input_pins[AUTO_PIN_FRONT_MIC] = nid;
- else
- cfg->input_pins[AUTO_PIN_MIC] = nid;
+ case AC_JACK_MIC_IN: {
+ int preferred, alt;
+ if (loc == AC_JACK_LOC_FRONT) {
+ preferred = AUTO_PIN_FRONT_MIC;
+ alt = AUTO_PIN_MIC;
+ } else {
+ preferred = AUTO_PIN_MIC;
+ alt = AUTO_PIN_FRONT_MIC;
+ }
+ if (!cfg->input_pins[preferred])
+ cfg->input_pins[preferred] = nid;
+ else if (!cfg->input_pins[alt])
+ cfg->input_pins[alt] = nid;
break;
+ }
case AC_JACK_LINE_IN:
if (loc == AC_JACK_LOC_FRONT)
cfg->input_pins[AUTO_PIN_FRONT_LINE] = nid;
@@ -2117,8 +2159,10 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *c
cfg->speaker_outs, cfg->speaker_pins[0],
cfg->speaker_pins[1], cfg->speaker_pins[2],
cfg->speaker_pins[3], cfg->speaker_pins[4]);
- snd_printd(" hp=0x%x, dig_out=0x%x, din_in=0x%x\n",
- cfg->hp_pin, cfg->dig_out_pin, cfg->dig_in_pin);
+ snd_printd(" hp_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
+ cfg->hp_outs, cfg->hp_pins[0],
+ cfg->hp_pins[1], cfg->hp_pins[2],
+ cfg->hp_pins[3], cfg->hp_pins[4]);
snd_printd(" inputs: mic=0x%x, fmic=0x%x, line=0x%x, fline=0x%x,"
" cd=0x%x, aux=0x%x\n",
cfg->input_pins[AUTO_PIN_MIC],
@@ -2139,10 +2183,12 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *c
sizeof(cfg->speaker_pins));
cfg->speaker_outs = 0;
memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins));
- } else if (cfg->hp_pin) {
- cfg->line_outs = 1;
- cfg->line_out_pins[0] = cfg->hp_pin;
- cfg->hp_pin = 0;
+ } else if (cfg->hp_outs) {
+ cfg->line_outs = cfg->hp_outs;
+ memcpy(cfg->line_out_pins, cfg->hp_pins,
+ sizeof(cfg->hp_pins));
+ cfg->hp_outs = 0;
+ memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
}
}
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 40520e9d5a4b..c12bc4e8840f 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -479,7 +479,7 @@ struct hda_codec_ops {
struct hda_amp_info {
u32 key; /* hash key */
u32 amp_caps; /* amp capabilities */
- u16 vol[2]; /* current volume & mute*/
+ u16 vol[2]; /* current volume & mute */
u16 status; /* update flag */
u16 next; /* next link */
};
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 85ad164ada59..97e9af130b71 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -46,11 +46,18 @@ struct hda_gnode {
};
/* patch-specific record */
+
+#define MAX_PCM_VOLS 2
+struct pcm_vol {
+ struct hda_gnode *node; /* Node for PCM volume */
+ unsigned int index; /* connection of PCM volume */
+};
+
struct hda_gspec {
struct hda_gnode *dac_node[2]; /* DAC node */
struct hda_gnode *out_pin_node[2]; /* Output pin (Line-Out) node */
- struct hda_gnode *pcm_vol_node[2]; /* Node for PCM volume */
- unsigned int pcm_vol_index[2]; /* connection of PCM volume */
+ struct pcm_vol pcm_vol[MAX_PCM_VOLS]; /* PCM volumes */
+ unsigned int pcm_vol_nodes; /* number of PCM volumes */
struct hda_gnode *adc_node; /* ADC node */
struct hda_gnode *cap_vol_node; /* Node for capture volume */
@@ -285,9 +292,11 @@ static int parse_output_path(struct hda_codec *codec, struct hda_gspec *spec,
return node == spec->dac_node[dac_idx];
}
spec->dac_node[dac_idx] = node;
- if (node->wid_caps & AC_WCAP_OUT_AMP) {
- spec->pcm_vol_node[dac_idx] = node;
- spec->pcm_vol_index[dac_idx] = 0;
+ if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
+ spec->pcm_vol_nodes < MAX_PCM_VOLS) {
+ spec->pcm_vol[spec->pcm_vol_nodes].node = node;
+ spec->pcm_vol[spec->pcm_vol_nodes].index = 0;
+ spec->pcm_vol_nodes++;
}
return 1; /* found */
}
@@ -307,13 +316,16 @@ static int parse_output_path(struct hda_codec *codec, struct hda_gspec *spec,
select_input_connection(codec, node, i);
unmute_input(codec, node, i);
unmute_output(codec, node);
- if (! spec->pcm_vol_node[dac_idx]) {
- if (node->wid_caps & AC_WCAP_IN_AMP) {
- spec->pcm_vol_node[dac_idx] = node;
- spec->pcm_vol_index[dac_idx] = i;
- } else if (node->wid_caps & AC_WCAP_OUT_AMP) {
- spec->pcm_vol_node[dac_idx] = node;
- spec->pcm_vol_index[dac_idx] = 0;
+ if (spec->dac_node[dac_idx] &&
+ spec->pcm_vol_nodes < MAX_PCM_VOLS &&
+ !(spec->dac_node[dac_idx]->wid_caps &
+ AC_WCAP_OUT_AMP)) {
+ if ((node->wid_caps & AC_WCAP_IN_AMP) ||
+ (node->wid_caps & AC_WCAP_OUT_AMP)) {
+ int n = spec->pcm_vol_nodes;
+ spec->pcm_vol[n].node = node;
+ spec->pcm_vol[n].index = i;
+ spec->pcm_vol_nodes++;
}
}
return 1;
@@ -370,7 +382,9 @@ static struct hda_gnode *parse_output_jack(struct hda_codec *codec,
/* set PIN-Out enable */
snd_hda_codec_write(codec, node->nid, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL,
- AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN);
+ AC_PINCTL_OUT_EN |
+ ((node->pin_caps & AC_PINCAP_HP_DRV) ?
+ AC_PINCTL_HP_EN : 0));
return node;
}
}
@@ -461,14 +475,19 @@ static const char *get_input_type(struct hda_gnode *node, unsigned int *pinctl)
return "Front Line";
return "Line";
case AC_JACK_CD:
+#if 0
if (pinctl)
*pinctl |= AC_PINCTL_VREF_GRD;
+#endif
return "CD";
case AC_JACK_AUX:
if ((location & 0x0f) == AC_JACK_LOC_FRONT)
return "Front Aux";
return "Aux";
case AC_JACK_MIC_IN:
+ if (node->pin_caps &
+ (AC_PINCAP_VREF_80 << AC_PINCAP_VREF_SHIFT))
+ *pinctl |= AC_PINCTL_VREF_80;
if ((location & 0x0f) == AC_JACK_LOC_FRONT)
return "Front Mic";
return "Mic";
@@ -556,6 +575,29 @@ static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec,
return 1; /* found */
}
+/* add a capture source element */
+static void add_cap_src(struct hda_gspec *spec, int idx)
+{
+ struct hda_input_mux_item *csrc;
+ char *buf;
+ int num, ocap;
+
+ num = spec->input_mux.num_items;
+ csrc = &spec->input_mux.items[num];
+ buf = spec->cap_labels[num];
+ for (ocap = 0; ocap < num; ocap++) {
+ if (! strcmp(buf, spec->cap_labels[ocap])) {
+ /* same label already exists,
+ * put the index number to be unique
+ */
+ sprintf(buf, "%s %d", spec->cap_labels[ocap], num);
+ break;
+ }
+ }
+ csrc->index = idx;
+ spec->input_mux.num_items++;
+}
+
/*
* parse input
*/
@@ -576,28 +618,26 @@ static int parse_input_path(struct hda_codec *codec, struct hda_gnode *adc_node)
* if it reaches to a proper input PIN, add the path as the
* input path.
*/
+ /* first, check the direct connections to PIN widgets */
for (i = 0; i < adc_node->nconns; i++) {
node = hda_get_node(spec, adc_node->conn_list[i]);
- if (! node)
- continue;
- err = parse_adc_sub_nodes(codec, spec, node);
- if (err < 0)
- return err;
- else if (err > 0) {
- struct hda_input_mux_item *csrc = &spec->input_mux.items[spec->input_mux.num_items];
- char *buf = spec->cap_labels[spec->input_mux.num_items];
- int ocap;
- for (ocap = 0; ocap < spec->input_mux.num_items; ocap++) {
- if (! strcmp(buf, spec->cap_labels[ocap])) {
- /* same label already exists,
- * put the index number to be unique
- */
- sprintf(buf, "%s %d", spec->cap_labels[ocap],
- spec->input_mux.num_items);
- }
- }
- csrc->index = i;
- spec->input_mux.num_items++;
+ if (node && node->type == AC_WID_PIN) {
+ err = parse_adc_sub_nodes(codec, spec, node);
+ if (err < 0)
+ return err;
+ else if (err > 0)
+ add_cap_src(spec, i);
+ }
+ }
+ /* ... then check the rests, more complicated connections */
+ for (i = 0; i < adc_node->nconns; i++) {
+ node = hda_get_node(spec, adc_node->conn_list[i]);
+ if (node && node->type != AC_WID_PIN) {
+ err = parse_adc_sub_nodes(codec, spec, node);
+ if (err < 0)
+ return err;
+ else if (err > 0)
+ add_cap_src(spec, i);
}
}
@@ -647,9 +687,6 @@ static int parse_input(struct hda_codec *codec)
/*
* create mixer controls if possible
*/
-#define DIR_OUT 0x1
-#define DIR_IN 0x2
-
static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
unsigned int index, const char *type, const char *dir_sfx)
{
@@ -722,49 +759,97 @@ static int check_existing_control(struct hda_codec *codec, const char *type, con
/*
* build output mixer controls
*/
-static int build_output_controls(struct hda_codec *codec)
+static int create_output_mixers(struct hda_codec *codec, const char **names)
{
struct hda_gspec *spec = codec->spec;
- static const char *types[2] = { "Master", "Headphone" };
int i, err;
- for (i = 0; i < 2 && spec->pcm_vol_node[i]; i++) {
- err = create_mixer(codec, spec->pcm_vol_node[i],
- spec->pcm_vol_index[i],
- types[i], "Playback");
+ for (i = 0; i < spec->pcm_vol_nodes; i++) {
+ err = create_mixer(codec, spec->pcm_vol[i].node,
+ spec->pcm_vol[i].index,
+ names[i], "Playback");
if (err < 0)
return err;
}
return 0;
}
+static int build_output_controls(struct hda_codec *codec)
+{
+ struct hda_gspec *spec = codec->spec;
+ static const char *types_speaker[] = { "Speaker", "Headphone" };
+ static const char *types_line[] = { "Front", "Headphone" };
+
+ switch (spec->pcm_vol_nodes) {
+ case 1:
+ return create_mixer(codec, spec->pcm_vol[0].node,
+ spec->pcm_vol[0].index,
+ "Master", "Playback");
+ case 2:
+ if (defcfg_type(spec->out_pin_node[0]) == AC_JACK_SPEAKER)
+ return create_output_mixers(codec, types_speaker);
+ else
+ return create_output_mixers(codec, types_line);
+ }
+ return 0;
+}
+
/* create capture volume/switch */
static int build_input_controls(struct hda_codec *codec)
{
struct hda_gspec *spec = codec->spec;
struct hda_gnode *adc_node = spec->adc_node;
- int err;
-
- if (! adc_node)
+ int i, err;
+ static struct snd_kcontrol_new cap_sel = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .info = capture_source_info,
+ .get = capture_source_get,
+ .put = capture_source_put,
+ };
+
+ if (! adc_node || ! spec->input_mux.num_items)
return 0; /* not found */
+ spec->cur_cap_src = 0;
+ select_input_connection(codec, adc_node,
+ spec->input_mux.items[0].index);
+
/* create capture volume and switch controls if the ADC has an amp */
- err = create_mixer(codec, adc_node, 0, NULL, "Capture");
+ /* do we have only a single item? */
+ if (spec->input_mux.num_items == 1) {
+ err = create_mixer(codec, adc_node,
+ spec->input_mux.items[0].index,
+ NULL, "Capture");
+ if (err < 0)
+ return err;
+ return 0;
+ }
/* create input MUX if multiple sources are available */
- if (spec->input_mux.num_items > 1) {
- static struct snd_kcontrol_new cap_sel = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = capture_source_info,
- .get = capture_source_get,
- .put = capture_source_put,
- };
- if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&cap_sel, codec))) < 0)
+ if ((err = snd_ctl_add(codec->bus->card,
+ snd_ctl_new1(&cap_sel, codec))) < 0)
+ return err;
+
+ /* no volume control? */
+ if (! (adc_node->wid_caps & AC_WCAP_IN_AMP) ||
+ ! (adc_node->amp_in_caps & AC_AMPCAP_NUM_STEPS))
+ return 0;
+
+ for (i = 0; i < spec->input_mux.num_items; i++) {
+ struct snd_kcontrol_new knew;
+ char name[32];
+ sprintf(name, "%s Capture Volume",
+ spec->input_mux.items[i].label);
+ knew = (struct snd_kcontrol_new)
+ HDA_CODEC_VOLUME(name, adc_node->nid,
+ spec->input_mux.items[i].index,
+ HDA_INPUT);
+ if ((err = snd_ctl_add(codec->bus->card,
+ snd_ctl_new1(&knew, codec))) < 0)
return err;
- spec->cur_cap_src = 0;
- select_input_connection(codec, adc_node, spec->input_mux.items[0].index);
}
+
return 0;
}
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 4070b5cd9b6b..e9d4cb4d07e1 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -55,6 +55,7 @@ static char *model;
static int position_fix;
static int probe_mask = -1;
static int single_cmd;
+static int disable_msi;
module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for Intel HD audio interface.");
@@ -68,6 +69,8 @@ module_param(probe_mask, int, 0444);
MODULE_PARM_DESC(probe_mask, "Bitmask to probe codecs (default = -1).");
module_param(single_cmd, bool, 0444);
MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs (for debugging only).");
+module_param(disable_msi, int, 0);
+MODULE_PARM_DESC(disable_msi, "Disable Message Signaled Interrupt (MSI)");
/* just for backward compatibility */
@@ -252,7 +255,7 @@ enum {
struct azx_dev {
u32 *bdl; /* virtual address of the BDL */
dma_addr_t bdl_addr; /* physical address of the BDL */
- volatile u32 *posbuf; /* position buffer pointer */
+ u32 *posbuf; /* position buffer pointer */
unsigned int bufsize; /* size of the play buffer in bytes */
unsigned int fragsize; /* size of each period in bytes */
@@ -271,8 +274,8 @@ struct azx_dev {
/* for sanity check of position buffer */
unsigned int period_intr;
- unsigned int opened: 1;
- unsigned int running: 1;
+ unsigned int opened :1;
+ unsigned int running :1;
};
/* CORB/RIRB */
@@ -330,8 +333,9 @@ struct azx {
/* flags */
int position_fix;
- unsigned int initialized: 1;
- unsigned int single_cmd: 1;
+ unsigned int initialized :1;
+ unsigned int single_cmd :1;
+ unsigned int polling_mode :1;
};
/* driver types */
@@ -516,23 +520,36 @@ static void azx_update_rirb(struct azx *chip)
static unsigned int azx_rirb_get_response(struct hda_codec *codec)
{
struct azx *chip = codec->bus->private_data;
- int timeout = 50;
+ unsigned long timeout;
- while (chip->rirb.cmds) {
- if (! --timeout) {
- snd_printk(KERN_ERR
- "hda_intel: azx_get_response timeout, "
- "switching to single_cmd mode...\n");
- chip->rirb.rp = azx_readb(chip, RIRBWP);
- chip->rirb.cmds = 0;
- /* switch to single_cmd mode */
- chip->single_cmd = 1;
- azx_free_cmd_io(chip);
- return -1;
+ again:
+ timeout = jiffies + msecs_to_jiffies(1000);
+ do {
+ if (chip->polling_mode) {
+ spin_lock_irq(&chip->reg_lock);
+ azx_update_rirb(chip);
+ spin_unlock_irq(&chip->reg_lock);
}
- msleep(1);
+ if (! chip->rirb.cmds)
+ return chip->rirb.res; /* the last value */
+ schedule_timeout_interruptible(1);
+ } while (time_after_eq(timeout, jiffies));
+
+ if (!chip->polling_mode) {
+ snd_printk(KERN_WARNING "hda_intel: azx_get_response timeout, "
+ "switching to polling mode...\n");
+ chip->polling_mode = 1;
+ goto again;
}
- return chip->rirb.res; /* the last value */
+
+ snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, "
+ "switching to single_cmd mode...\n");
+ chip->rirb.rp = azx_readb(chip, RIRBWP);
+ chip->rirb.cmds = 0;
+ /* switch to single_cmd mode */
+ chip->single_cmd = 1;
+ azx_free_cmd_io(chip);
+ return -1;
}
/*
@@ -642,14 +659,14 @@ static int azx_reset(struct azx *chip)
azx_writeb(chip, GCTL, azx_readb(chip, GCTL) | ICH6_GCTL_RESET);
count = 50;
- while (! azx_readb(chip, GCTL) && --count)
+ while (!azx_readb(chip, GCTL) && --count)
msleep(1);
- /* Brent Chartrand said to wait >= 540us for codecs to intialize */
+ /* Brent Chartrand said to wait >= 540us for codecs to initialize */
msleep(1);
/* check to see if controller is ready */
- if (! azx_readb(chip, GCTL)) {
+ if (!azx_readb(chip, GCTL)) {
snd_printd("azx_reset: controller not ready!\n");
return -EBUSY;
}
@@ -658,7 +675,7 @@ static int azx_reset(struct azx *chip)
azx_writel(chip, GCTL, azx_readl(chip, GCTL) | ICH6_GCTL_UREN);
/* detect codecs */
- if (! chip->codec_mask) {
+ if (!chip->codec_mask) {
chip->codec_mask = azx_readw(chip, STATESTS);
snd_printdd("codec_mask = 0x%x\n", chip->codec_mask);
}
@@ -766,7 +783,7 @@ static void azx_init_chip(struct azx *chip)
azx_int_enable(chip);
/* initialize the codec command I/O */
- if (! chip->single_cmd)
+ if (!chip->single_cmd)
azx_init_cmd_io(chip);
/* program the position buffer */
@@ -794,7 +811,7 @@ static void azx_init_chip(struct azx *chip)
/*
* interrupt handler
*/
-static irqreturn_t azx_interrupt(int irq, void* dev_id, struct pt_regs *regs)
+static irqreturn_t azx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct azx *chip = dev_id;
struct azx_dev *azx_dev;
@@ -999,8 +1016,9 @@ static struct snd_pcm_hardware azx_pcm_hw = {
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_PAUSE /*|*/
- /*SNDRV_PCM_INFO_RESUME*/),
+ /* No full-resume yet implemented */
+ /* SNDRV_PCM_INFO_RESUME |*/
+ SNDRV_PCM_INFO_PAUSE),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_48000,
.rate_min = 48000,
@@ -1178,7 +1196,7 @@ static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream)
if (chip->position_fix == POS_FIX_POSBUF ||
chip->position_fix == POS_FIX_AUTO) {
/* use the position buffer */
- pos = *azx_dev->posbuf;
+ pos = le32_to_cpu(*azx_dev->posbuf);
if (chip->position_fix == POS_FIX_AUTO &&
azx_dev->period_intr == 1 && ! pos) {
printk(KERN_WARNING
@@ -1222,7 +1240,12 @@ static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec,
struct snd_pcm *pcm;
struct azx_pcm *apcm;
- snd_assert(cpcm->stream[0].substreams || cpcm->stream[1].substreams, return -EINVAL);
+ /* if no substreams are defined for both playback and capture,
+ * it's just a placeholder. ignore it.
+ */
+ if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams)
+ return 0;
+
snd_assert(cpcm->name, return -EINVAL);
err = snd_pcm_new(chip->card, cpcm->name, pcm_dev,
@@ -1248,7 +1271,8 @@ static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec,
snd_dma_pci_data(chip->pci),
1024 * 64, 1024 * 128);
chip->pcm[pcm_dev] = pcm;
- chip->pcm_devs = pcm_dev + 1;
+ if (chip->pcm_devs < pcm_dev + 1)
+ chip->pcm_devs = pcm_dev + 1;
return 0;
}
@@ -1326,7 +1350,7 @@ static int __devinit azx_init_stream(struct azx *chip)
struct azx_dev *azx_dev = &chip->azx_dev[i];
azx_dev->bdl = (u32 *)(chip->bdl.area + off);
azx_dev->bdl_addr = chip->bdl.addr + off;
- azx_dev->posbuf = (volatile u32 *)(chip->posbuf.area + i * 8);
+ azx_dev->posbuf = (u32 __iomem *)(chip->posbuf.area + i * 8);
/* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
azx_dev->sd_addr = chip->remap_addr + (0x20 * i + 0x80);
/* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */
@@ -1355,6 +1379,10 @@ static int azx_suspend(struct pci_dev *pci, pm_message_t state)
snd_pcm_suspend_all(chip->pcm[i]);
snd_hda_suspend(chip->bus, state);
azx_free_cmd_io(chip);
+ if (chip->irq >= 0)
+ free_irq(chip->irq, chip);
+ if (!disable_msi)
+ pci_disable_msi(chip->pci);
pci_disable_device(pci);
pci_save_state(pci);
return 0;
@@ -1367,6 +1395,12 @@ static int azx_resume(struct pci_dev *pci)
pci_restore_state(pci);
pci_enable_device(pci);
+ if (!disable_msi)
+ pci_enable_msi(pci);
+ /* FIXME: need proper error handling */
+ request_irq(pci->irq, azx_interrupt, IRQF_DISABLED|IRQF_SHARED,
+ "HDA Intel", chip);
+ chip->irq = pci->irq;
pci_set_master(pci);
azx_init_chip(chip);
snd_hda_resume(chip->bus);
@@ -1398,12 +1432,14 @@ static int azx_free(struct azx *chip)
azx_writel(chip, DPLBASE, 0);
azx_writel(chip, DPUBASE, 0);
- /* wait a little for interrupts to finish */
- msleep(1);
+ synchronize_irq(chip->irq);
}
- if (chip->irq >= 0)
+ if (chip->irq >= 0) {
free_irq(chip->irq, (void*)chip);
+ if (!disable_msi)
+ pci_disable_msi(chip->pci);
+ }
if (chip->remap_addr)
iounmap(chip->remap_addr);
@@ -1434,19 +1470,19 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
struct azx **rchip)
{
struct azx *chip;
- int err = 0;
+ int err;
static struct snd_device_ops ops = {
.dev_free = azx_dev_free,
};
*rchip = NULL;
- if ((err = pci_enable_device(pci)) < 0)
+ err = pci_enable_device(pci);
+ if (err < 0)
return err;
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
-
- if (NULL == chip) {
+ if (!chip) {
snd_printk(KERN_ERR SFX "cannot allocate chip\n");
pci_disable_device(pci);
return -ENOMEM;
@@ -1472,13 +1508,14 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
}
#endif
- if ((err = pci_request_regions(pci, "ICH HD audio")) < 0) {
+ err = pci_request_regions(pci, "ICH HD audio");
+ if (err < 0) {
kfree(chip);
pci_disable_device(pci);
return err;
}
- chip->addr = pci_resource_start(pci,0);
+ chip->addr = pci_resource_start(pci, 0);
chip->remap_addr = ioremap_nocache(chip->addr, pci_resource_len(pci,0));
if (chip->remap_addr == NULL) {
snd_printk(KERN_ERR SFX "ioremap error\n");
@@ -1486,7 +1523,10 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
goto errout;
}
- if (request_irq(pci->irq, azx_interrupt, SA_INTERRUPT|SA_SHIRQ,
+ if (!disable_msi)
+ pci_enable_msi(pci);
+
+ if (request_irq(pci->irq, azx_interrupt, IRQF_DISABLED|IRQF_SHARED,
"HDA Intel", (void*)chip)) {
snd_printk(KERN_ERR SFX "unable to grab IRQ %d\n", pci->irq);
err = -EBUSY;
@@ -1519,7 +1559,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
}
chip->num_streams = chip->playback_streams + chip->capture_streams;
chip->azx_dev = kcalloc(chip->num_streams, sizeof(*chip->azx_dev), GFP_KERNEL);
- if (! chip->azx_dev) {
+ if (!chip->azx_dev) {
snd_printk(KERN_ERR "cannot malloc azx_dev\n");
goto errout;
}
@@ -1550,7 +1590,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
chip->initialized = 1;
/* codec detection */
- if (! chip->codec_mask) {
+ if (!chip->codec_mask) {
snd_printk(KERN_ERR SFX "no codecs found!\n");
err = -ENODEV;
goto errout;
@@ -1577,16 +1617,16 @@ static int __devinit azx_probe(struct pci_dev *pci, const struct pci_device_id *
{
struct snd_card *card;
struct azx *chip;
- int err = 0;
+ int err;
card = snd_card_new(index, id, THIS_MODULE, 0);
- if (NULL == card) {
+ if (!card) {
snd_printk(KERN_ERR SFX "Error creating card!\n");
return -ENOMEM;
}
- if ((err = azx_create(card, pci, pci_id->driver_data,
- &chip)) < 0) {
+ err = azx_create(card, pci, pci_id->driver_data, &chip);
+ if (err < 0) {
snd_card_free(card);
return err;
}
@@ -1629,7 +1669,7 @@ static void __devexit azx_remove(struct pci_dev *pci)
}
/* PCI IDs */
-static struct pci_device_id azx_ids[] __devinitdata = {
+static struct pci_device_id azx_ids[] = {
{ 0x8086, 0x2668, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH6 */
{ 0x8086, 0x27d8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH7 */
{ 0x8086, 0x269a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ESB2 */
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 14e8aa2806ed..f9416c36396e 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -30,9 +30,13 @@
/* mono volume with index (index=0,1,...) (channel=1,2) */
#define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \
.info = snd_hda_mixer_amp_volume_info, \
.get = snd_hda_mixer_amp_volume_get, \
.put = snd_hda_mixer_amp_volume_put, \
+ .tlv = { .c = snd_hda_mixer_amp_tlv }, \
.private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) }
/* stereo volume with index */
#define HDA_CODEC_VOLUME_IDX(xname, xcidx, nid, xindex, direction) \
@@ -63,6 +67,7 @@
int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo);
int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag, unsigned int size, unsigned int __user *tlv);
int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo);
int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
@@ -224,7 +229,8 @@ struct auto_pin_cfg {
hda_nid_t line_out_pins[5]; /* sorted in the order of Front/Surr/CLFE/Side */
int speaker_outs;
hda_nid_t speaker_pins[5];
- hda_nid_t hp_pin;
+ int hp_outs;
+ hda_nid_t hp_pins[5];
hda_nid_t input_pins[AUTO_PIN_LAST];
hda_nid_t dig_out_pin;
hda_nid_t dig_in_pin;
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c
index c2f0fe85bf35..d737f17695a3 100644
--- a/sound/pci/hda/hda_proc.c
+++ b/sound/pci/hda/hda_proc.c
@@ -52,10 +52,9 @@ static void print_amp_caps(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid, int dir)
{
unsigned int caps;
- if (dir == HDA_OUTPUT)
- caps = snd_hda_param_read(codec, nid, AC_PAR_AMP_OUT_CAP);
- else
- caps = snd_hda_param_read(codec, nid, AC_PAR_AMP_IN_CAP);
+ caps = snd_hda_param_read(codec, nid,
+ dir == HDA_OUTPUT ?
+ AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
if (caps == -1 || caps == 0) {
snd_iprintf(buffer, "N/A\n");
return;
@@ -74,10 +73,7 @@ static void print_amp_vals(struct snd_info_buffer *buffer,
unsigned int val;
int i;
- if (dir == HDA_OUTPUT)
- dir = AC_AMP_GET_OUTPUT;
- else
- dir = AC_AMP_GET_INPUT;
+ dir = dir == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT;
for (i = 0; i < indices; i++) {
snd_iprintf(buffer, " [");
if (stereo) {
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index dd4e00a82b55..511df07fa2a3 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -488,9 +488,13 @@ static struct snd_kcontrol_new ad1986a_mixers[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,
.info = ad1986a_pcm_amp_vol_info,
.get = ad1986a_pcm_amp_vol_get,
.put = ad1986a_pcm_amp_vol_put,
+ .tlv = { .c = snd_hda_mixer_amp_tlv },
.private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT)
},
{
@@ -637,6 +641,7 @@ static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
.info = snd_hda_mixer_amp_volume_info,
.get = snd_hda_mixer_amp_volume_get,
.put = ad1986a_laptop_master_vol_put,
+ .tlv = { .c = snd_hda_mixer_amp_tlv },
.private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
},
{
@@ -791,6 +796,8 @@ static struct hda_board_config ad1986a_cfg_tbl[] = {
.config = AD1986A_3STACK }, /* ASUS A8N-VM CSM */
{ .pci_subvendor = 0x1043, .pci_subdevice = 0x81b3,
.config = AD1986A_3STACK }, /* ASUS P5RD2-VM / P5GPL-X SE */
+ { .pci_subvendor = 0x1043, .pci_subdevice = 0x81cb,
+ .config = AD1986A_3STACK }, /* ASUS M2NPV-VM */
{ .modelname = "laptop", .config = AD1986A_LAPTOP },
{ .pci_subvendor = 0x144d, .pci_subdevice = 0xc01e,
.config = AD1986A_LAPTOP }, /* FSC V2060 */
@@ -799,8 +806,12 @@ static struct hda_board_config ad1986a_cfg_tbl[] = {
{ .pci_subvendor = 0x1043, .pci_subdevice = 0x818f,
.config = AD1986A_LAPTOP }, /* ASUS P5GV-MX */
{ .modelname = "laptop-eapd", .config = AD1986A_LAPTOP_EAPD },
+ { .pci_subvendor = 0x144d, .pci_subdevice = 0xc023,
+ .config = AD1986A_LAPTOP_EAPD }, /* Samsung X60 Chane */
{ .pci_subvendor = 0x144d, .pci_subdevice = 0xc024,
.config = AD1986A_LAPTOP_EAPD }, /* Samsung R65-T2300 Charis */
+ { .pci_subvendor = 0x144d, .pci_subdevice = 0xc026,
+ .config = AD1986A_LAPTOP_EAPD }, /* Samsung X10-T2300 Culesa */
{ .pci_subvendor = 0x1043, .pci_subdevice = 0x1153,
.config = AD1986A_LAPTOP_EAPD }, /* ASUS M9 */
{ .pci_subvendor = 0x1043, .pci_subdevice = 0x1213,
@@ -1543,6 +1554,9 @@ enum {
/* reivision id to check workarounds */
#define AD1988A_REV2 0x100200
+#define is_rev2(codec) \
+ ((codec)->vendor_id == 0x11d41988 && \
+ (codec)->revision_id == AD1988A_REV2)
/*
* mixers
@@ -1621,10 +1635,12 @@ static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ad198x_spec *spec = codec->spec;
- if (spec->need_dac_fix)
+ int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
+ spec->num_channel_mode,
+ &spec->multiout.max_channels);
+ if (! err && spec->need_dac_fix)
spec->multiout.num_dacs = spec->multiout.max_channels / 2;
- return snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
- spec->num_channel_mode, &spec->multiout.max_channels);
+ return err;
}
/* 6-stack mode */
@@ -1634,6 +1650,7 @@ static struct snd_kcontrol_new ad1988_6stack_mixers1[] = {
HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Side Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
+ { } /* end */
};
static struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = {
@@ -1642,6 +1659,7 @@ static struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = {
HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0a, 2, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Side Playback Volume", 0x06, 0x0, HDA_OUTPUT),
+ { } /* end */
};
static struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
@@ -1680,6 +1698,7 @@ static struct snd_kcontrol_new ad1988_3stack_mixers1[] = {
HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
+ { } /* end */
};
static struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = {
@@ -1687,6 +1706,7 @@ static struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = {
HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x06, 1, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x06, 2, 0x0, HDA_OUTPUT),
+ { } /* end */
};
static struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
@@ -2193,7 +2213,7 @@ static inline hda_nid_t ad1988_idx_to_dac(struct hda_codec *codec, int idx)
/* A B C D E F G H */
0x04, 0x05, 0x0a, 0x04, 0x06, 0x05, 0x0a, 0x06
};
- if (codec->revision_id == AD1988A_REV2)
+ if (is_rev2(codec))
return idx_to_dac_rev2[idx];
else
return idx_to_dac[idx];
@@ -2451,7 +2471,7 @@ static void ad1988_auto_init_extra_out(struct hda_codec *codec)
pin = spec->autocfg.speaker_pins[0];
if (pin) /* connect to front */
ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
- pin = spec->autocfg.hp_pin;
+ pin = spec->autocfg.hp_pins[0];
if (pin) /* connect to front */
ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
}
@@ -2503,7 +2523,7 @@ static int ad1988_parse_auto_config(struct hda_codec *codec)
(err = ad1988_auto_create_extra_out(codec,
spec->autocfg.speaker_pins[0],
"Speaker")) < 0 ||
- (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pin,
+ (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
"Headphone")) < 0 ||
(err = ad1988_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
return err;
@@ -2562,7 +2582,7 @@ static int patch_ad1988(struct hda_codec *codec)
mutex_init(&spec->amp_mutex);
codec->spec = spec;
- if (codec->revision_id == AD1988A_REV2)
+ if (is_rev2(codec))
snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");
board_config = snd_hda_check_board_config(codec, ad1988_cfg_tbl);
@@ -2588,13 +2608,13 @@ static int patch_ad1988(struct hda_codec *codec)
case AD1988_6STACK_DIG:
spec->multiout.max_channels = 8;
spec->multiout.num_dacs = 4;
- if (codec->revision_id == AD1988A_REV2)
+ if (is_rev2(codec))
spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2;
else
spec->multiout.dac_nids = ad1988_6stack_dac_nids;
spec->input_mux = &ad1988_6stack_capture_source;
spec->num_mixers = 2;
- if (codec->revision_id == AD1988A_REV2)
+ if (is_rev2(codec))
spec->mixers[0] = ad1988_6stack_mixers1_rev2;
else
spec->mixers[0] = ad1988_6stack_mixers1;
@@ -2610,7 +2630,7 @@ static int patch_ad1988(struct hda_codec *codec)
case AD1988_3STACK_DIG:
spec->multiout.max_channels = 6;
spec->multiout.num_dacs = 3;
- if (codec->revision_id == AD1988A_REV2)
+ if (is_rev2(codec))
spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2;
else
spec->multiout.dac_nids = ad1988_3stack_dac_nids;
@@ -2618,7 +2638,7 @@ static int patch_ad1988(struct hda_codec *codec)
spec->channel_mode = ad1988_3stack_modes;
spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes);
spec->num_mixers = 2;
- if (codec->revision_id == AD1988A_REV2)
+ if (is_rev2(codec))
spec->mixers[0] = ad1988_3stack_mixers1_rev2;
else
spec->mixers[0] = ad1988_3stack_mixers1;
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 98b9f16c26ff..d08d2e399c8f 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -78,6 +78,8 @@ enum {
enum {
ALC262_BASIC,
ALC262_FUJITSU,
+ ALC262_HP_BPC,
+ ALC262_BENQ_ED8,
ALC262_AUTO,
ALC262_MODEL_LAST /* last tag */
};
@@ -85,8 +87,10 @@ enum {
/* ALC861 models */
enum {
ALC861_3ST,
+ ALC660_3ST,
ALC861_3ST_DIG,
ALC861_6ST_DIG,
+ ALC861_UNIWILL_M31,
ALC861_AUTO,
ALC861_MODEL_LAST,
};
@@ -95,10 +99,23 @@ enum {
enum {
ALC882_3ST_DIG,
ALC882_6ST_DIG,
+ ALC882_ARIMA,
ALC882_AUTO,
ALC882_MODEL_LAST,
};
+/* ALC883 models */
+enum {
+ ALC883_3ST_2ch_DIG,
+ ALC883_3ST_6ch_DIG,
+ ALC883_3ST_6ch,
+ ALC883_6ST_DIG,
+ ALC888_DEMO_BOARD,
+ ALC883_ACER,
+ ALC883_AUTO,
+ ALC883_MODEL_LAST,
+};
+
/* for GPIO Poll */
#define GPIO_MASK 0x03
@@ -108,7 +125,8 @@ struct alc_spec {
unsigned int num_mixers;
const struct hda_verb *init_verbs[5]; /* initialization verbs
- * don't forget NULL termination!
+ * don't forget NULL
+ * termination!
*/
unsigned int num_init_verbs;
@@ -139,6 +157,7 @@ struct alc_spec {
/* channel model */
const struct hda_channel_mode *channel_mode;
int num_channel_mode;
+ int need_dac_fix;
/* PCM information */
struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */
@@ -163,7 +182,9 @@ struct alc_spec {
* configuration template - to be copied to the spec instance
*/
struct alc_config_preset {
- struct snd_kcontrol_new *mixers[5]; /* should be identical size with spec */
+ struct snd_kcontrol_new *mixers[5]; /* should be identical size
+ * with spec
+ */
const struct hda_verb *init_verbs[5];
unsigned int num_dacs;
hda_nid_t *dac_nids;
@@ -174,6 +195,7 @@ struct alc_config_preset {
hda_nid_t dig_in_nid;
unsigned int num_channel_mode;
const struct hda_channel_mode *channel_mode;
+ int need_dac_fix;
unsigned int num_mux_defs;
const struct hda_input_mux *input_mux;
void (*unsol_event)(struct hda_codec *, unsigned int);
@@ -184,7 +206,8 @@ struct alc_config_preset {
/*
* input MUX handling
*/
-static int alc_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static int alc_mux_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct alc_spec *spec = codec->spec;
@@ -194,7 +217,8 @@ static int alc_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
return snd_hda_input_mux_info(&spec->input_mux[mux_idx], uinfo);
}
-static int alc_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int alc_mux_enum_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct alc_spec *spec = codec->spec;
@@ -204,21 +228,24 @@ static int alc_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
return 0;
}
-static int alc_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int alc_mux_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct alc_spec *spec = codec->spec;
unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
unsigned int mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx;
return snd_hda_input_mux_put(codec, &spec->input_mux[mux_idx], ucontrol,
- spec->adc_nids[adc_idx], &spec->cur_mux[adc_idx]);
+ spec->adc_nids[adc_idx],
+ &spec->cur_mux[adc_idx]);
}
/*
* channel mode setting
*/
-static int alc_ch_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static int alc_ch_mode_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct alc_spec *spec = codec->spec;
@@ -226,20 +253,27 @@ static int alc_ch_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_i
spec->num_channel_mode);
}
-static int alc_ch_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int alc_ch_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct alc_spec *spec = codec->spec;
return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
- spec->num_channel_mode, spec->multiout.max_channels);
+ spec->num_channel_mode,
+ spec->multiout.max_channels);
}
-static int alc_ch_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int alc_ch_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct alc_spec *spec = codec->spec;
- return snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
- spec->num_channel_mode, &spec->multiout.max_channels);
+ int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
+ spec->num_channel_mode,
+ &spec->multiout.max_channels);
+ if (! err && spec->need_dac_fix)
+ spec->multiout.num_dacs = spec->multiout.max_channels / 2;
+ return err;
}
/*
@@ -290,7 +324,8 @@ static signed char alc_pin_mode_dir_info[5][2] = {
#define alc_pin_mode_n_items(_dir) \
(alc_pin_mode_max(_dir)-alc_pin_mode_min(_dir)+1)
-static int alc_pin_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static int alc_pin_mode_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
unsigned int item_num = uinfo->value.enumerated.item;
unsigned char dir = (kcontrol->private_value >> 16) & 0xff;
@@ -305,40 +340,46 @@ static int alc_pin_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
return 0;
}
-static int alc_pin_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int alc_pin_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
unsigned int i;
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
hda_nid_t nid = kcontrol->private_value & 0xffff;
unsigned char dir = (kcontrol->private_value >> 16) & 0xff;
long *valp = ucontrol->value.integer.value;
- unsigned int pinctl = snd_hda_codec_read(codec,nid,0,AC_VERB_GET_PIN_WIDGET_CONTROL,0x00);
+ unsigned int pinctl = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL,
+ 0x00);
/* Find enumerated value for current pinctl setting */
i = alc_pin_mode_min(dir);
- while (alc_pin_mode_values[i]!=pinctl && i<=alc_pin_mode_max(dir))
+ while (alc_pin_mode_values[i] != pinctl && i <= alc_pin_mode_max(dir))
i++;
- *valp = i<=alc_pin_mode_max(dir)?i:alc_pin_mode_min(dir);
+ *valp = i <= alc_pin_mode_max(dir) ? i: alc_pin_mode_min(dir);
return 0;
}
-static int alc_pin_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int alc_pin_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
signed int change;
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
hda_nid_t nid = kcontrol->private_value & 0xffff;
unsigned char dir = (kcontrol->private_value >> 16) & 0xff;
long val = *ucontrol->value.integer.value;
- unsigned int pinctl = snd_hda_codec_read(codec,nid,0,AC_VERB_GET_PIN_WIDGET_CONTROL,0x00);
+ unsigned int pinctl = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL,
+ 0x00);
- if (val<alc_pin_mode_min(dir) || val>alc_pin_mode_max(dir))
+ if (val < alc_pin_mode_min(dir) || val > alc_pin_mode_max(dir))
val = alc_pin_mode_min(dir);
change = pinctl != alc_pin_mode_values[val];
if (change) {
/* Set pin mode to that requested */
snd_hda_codec_write(codec,nid,0,AC_VERB_SET_PIN_WIDGET_CONTROL,
- alc_pin_mode_values[val]);
+ alc_pin_mode_values[val]);
/* Also enable the retasking pin's input/output as required
* for the requested pin mode. Enum values of 2 or less are
@@ -351,15 +392,19 @@ static int alc_pin_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
* this turns out to be necessary in the future.
*/
if (val <= 2) {
- snd_hda_codec_write(codec,nid,0,AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_MUTE);
- snd_hda_codec_write(codec,nid,0,AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_UNMUTE(0));
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_MUTE);
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_IN_UNMUTE(0));
} else {
- snd_hda_codec_write(codec,nid,0,AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_MUTE(0));
- snd_hda_codec_write(codec,nid,0,AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_UNMUTE);
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_IN_MUTE(0));
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_UNMUTE);
}
}
return change;
@@ -378,7 +423,8 @@ static int alc_pin_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
* needed for any "production" models.
*/
#ifdef CONFIG_SND_DEBUG
-static int alc_gpio_data_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static int alc_gpio_data_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = 1;
@@ -386,33 +432,38 @@ static int alc_gpio_data_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
uinfo->value.integer.max = 1;
return 0;
}
-static int alc_gpio_data_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int alc_gpio_data_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
hda_nid_t nid = kcontrol->private_value & 0xffff;
unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
long *valp = ucontrol->value.integer.value;
- unsigned int val = snd_hda_codec_read(codec,nid,0,AC_VERB_GET_GPIO_DATA,0x00);
+ unsigned int val = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_GPIO_DATA, 0x00);
*valp = (val & mask) != 0;
return 0;
}
-static int alc_gpio_data_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int alc_gpio_data_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
signed int change;
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
hda_nid_t nid = kcontrol->private_value & 0xffff;
unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
long val = *ucontrol->value.integer.value;
- unsigned int gpio_data = snd_hda_codec_read(codec,nid,0,AC_VERB_GET_GPIO_DATA,0x00);
+ unsigned int gpio_data = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_GPIO_DATA,
+ 0x00);
/* Set/unset the masked GPIO bit(s) as needed */
- change = (val==0?0:mask) != (gpio_data & mask);
- if (val==0)
+ change = (val == 0 ? 0 : mask) != (gpio_data & mask);
+ if (val == 0)
gpio_data &= ~mask;
else
gpio_data |= mask;
- snd_hda_codec_write(codec,nid,0,AC_VERB_SET_GPIO_DATA,gpio_data);
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_GPIO_DATA, gpio_data);
return change;
}
@@ -432,7 +483,8 @@ static int alc_gpio_data_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
* necessary.
*/
#ifdef CONFIG_SND_DEBUG
-static int alc_spdif_ctrl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static int alc_spdif_ctrl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = 1;
@@ -440,33 +492,39 @@ static int alc_spdif_ctrl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
uinfo->value.integer.max = 1;
return 0;
}
-static int alc_spdif_ctrl_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int alc_spdif_ctrl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
hda_nid_t nid = kcontrol->private_value & 0xffff;
unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
long *valp = ucontrol->value.integer.value;
- unsigned int val = snd_hda_codec_read(codec,nid,0,AC_VERB_GET_DIGI_CONVERT,0x00);
+ unsigned int val = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_DIGI_CONVERT, 0x00);
*valp = (val & mask) != 0;
return 0;
}
-static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
signed int change;
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
hda_nid_t nid = kcontrol->private_value & 0xffff;
unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
long val = *ucontrol->value.integer.value;
- unsigned int ctrl_data = snd_hda_codec_read(codec,nid,0,AC_VERB_GET_DIGI_CONVERT,0x00);
+ unsigned int ctrl_data = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_DIGI_CONVERT,
+ 0x00);
/* Set/unset the masked control bit(s) as needed */
- change = (val==0?0:mask) != (ctrl_data & mask);
+ change = (val == 0 ? 0 : mask) != (ctrl_data & mask);
if (val==0)
ctrl_data &= ~mask;
else
ctrl_data |= mask;
- snd_hda_codec_write(codec,nid,0,AC_VERB_SET_DIGI_CONVERT_1,ctrl_data);
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
+ ctrl_data);
return change;
}
@@ -481,17 +539,21 @@ static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
/*
* set up from the preset table
*/
-static void setup_preset(struct alc_spec *spec, const struct alc_config_preset *preset)
+static void setup_preset(struct alc_spec *spec,
+ const struct alc_config_preset *preset)
{
int i;
for (i = 0; i < ARRAY_SIZE(preset->mixers) && preset->mixers[i]; i++)
spec->mixers[spec->num_mixers++] = preset->mixers[i];
- for (i = 0; i < ARRAY_SIZE(preset->init_verbs) && preset->init_verbs[i]; i++)
- spec->init_verbs[spec->num_init_verbs++] = preset->init_verbs[i];
+ for (i = 0; i < ARRAY_SIZE(preset->init_verbs) && preset->init_verbs[i];
+ i++)
+ spec->init_verbs[spec->num_init_verbs++] =
+ preset->init_verbs[i];
spec->channel_mode = preset->channel_mode;
spec->num_channel_mode = preset->num_channel_mode;
+ spec->need_dac_fix = preset->need_dac_fix;
spec->multiout.max_channels = spec->channel_mode[0].channels;
@@ -517,8 +579,8 @@ static void setup_preset(struct alc_spec *spec, const struct alc_config_preset *
* ALC880 3-stack model
*
* DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0e)
- * Pin assignment: Front = 0x14, Line-In/Surr = 0x1a, Mic/CLFE = 0x18, F-Mic = 0x1b
- * HP = 0x19
+ * Pin assignment: Front = 0x14, Line-In/Surr = 0x1a, Mic/CLFE = 0x18,
+ * F-Mic = 0x1b, HP = 0x19
*/
static hda_nid_t alc880_dac_nids[4] = {
@@ -662,7 +724,8 @@ static struct snd_kcontrol_new alc880_capture_alt_mixer[] = {
/*
* ALC880 5-stack model
*
- * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0d), Side = 0x02 (0xd)
+ * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0d),
+ * Side = 0x02 (0xd)
* Pin assignment: Front = 0x14, Surr = 0x17, CLFE = 0x16
* Line-In/Side = 0x1a, Mic = 0x18, F-Mic = 0x1b, HP = 0x19
*/
@@ -700,7 +763,8 @@ static struct hda_channel_mode alc880_fivestack_modes[2] = {
/*
* ALC880 6-stack model
*
- * DAC: Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e), Side = 0x05 (0x0f)
+ * DAC: Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e),
+ * Side = 0x05 (0x0f)
* Pin assignment: Front = 0x14, Surr = 0x15, CLFE = 0x16, Side = 0x17,
* Mic = 0x18, F-Mic = 0x19, Line = 0x1a, HP = 0x1b
*/
@@ -811,7 +875,8 @@ static struct snd_kcontrol_new alc880_w810_base_mixer[] = {
* Z710V model
*
* DAC: Front = 0x02 (0x0c), HP = 0x03 (0x0d)
- * Pin assignment: Front = 0x14, HP = 0x15, Mic = 0x18, Mic2 = 0x19(?), Line = 0x1a
+ * Pin assignment: Front = 0x14, HP = 0x15, Mic = 0x18, Mic2 = 0x19(?),
+ * Line = 0x1a
*/
static hda_nid_t alc880_z71v_dac_nids[1] = {
@@ -966,7 +1031,8 @@ static int alc_build_controls(struct hda_codec *codec)
}
if (spec->multiout.dig_out_nid) {
- err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
+ err = snd_hda_create_spdif_out_ctls(codec,
+ spec->multiout.dig_out_nid);
if (err < 0)
return err;
}
@@ -999,8 +1065,8 @@ static struct hda_verb alc880_volume_init_verbs[] = {
/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
* mixer widget
- * Note: PASD motherboards uses the Line In 2 as the input for front panel
- * mic (mic 2)
+ * Note: PASD motherboards uses the Line In 2 as the input for front
+ * panel mic (mic 2)
*/
/* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
@@ -1154,8 +1220,8 @@ static struct hda_verb alc880_pin_z71v_init_verbs[] = {
/*
* 6-stack pin configuration:
- * front = 0x14, surr = 0x15, clfe = 0x16, side = 0x17, mic = 0x18, f-mic = 0x19,
- * line = 0x1a, HP = 0x1b
+ * front = 0x14, surr = 0x15, clfe = 0x16, side = 0x17, mic = 0x18,
+ * f-mic = 0x19, line = 0x1a, HP = 0x1b
*/
static struct hda_verb alc880_pin_6stack_init_verbs[] = {
{0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
@@ -1292,6 +1358,10 @@ static struct hda_verb alc880_pin_clevo_init_verbs[] = {
};
static struct hda_verb alc880_pin_tcl_S700_init_verbs[] = {
+ /* change to EAPD mode */
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x3060},
+
/* Headphone output */
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
/* Front output*/
@@ -1587,8 +1657,8 @@ static int alc880_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream)
{
struct alc_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
- format, substream);
+ return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
+ stream_tag, format, substream);
}
static int alc880_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
@@ -1640,7 +1710,8 @@ static int alc880_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
{
struct alc_spec *spec = codec->spec;
- snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 0, 0, 0);
+ snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
+ 0, 0, 0);
return 0;
}
@@ -1725,25 +1796,9 @@ static int alc_build_pcms(struct hda_codec *codec)
}
}
- /* If the use of more than one ADC is requested for the current
- * model, configure a second analog capture-only PCM.
- */
- if (spec->num_adc_nids > 1) {
- codec->num_pcms++;
- info++;
- info->name = spec->stream_name_analog;
- /* No playback stream for second PCM */
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = alc_pcm_null_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0;
- if (spec->stream_analog_capture) {
- snd_assert(spec->adc_nids, return -EINVAL);
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[1];
- }
- }
-
+ /* SPDIF for stream index #1 */
if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
- codec->num_pcms++;
+ codec->num_pcms = 2;
info++;
info->name = spec->stream_name_digital;
if (spec->multiout.dig_out_nid &&
@@ -1758,6 +1813,24 @@ static int alc_build_pcms(struct hda_codec *codec)
}
}
+ /* If the use of more than one ADC is requested for the current
+ * model, configure a second analog capture-only PCM.
+ */
+ /* Additional Analaog capture for index #2 */
+ if (spec->num_adc_nids > 1 && spec->stream_analog_capture &&
+ spec->adc_nids) {
+ codec->num_pcms = 3;
+ info++;
+ info->name = spec->stream_name_analog;
+ /* No playback stream for second PCM */
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = alc_pcm_null_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0;
+ if (spec->stream_analog_capture) {
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[1];
+ }
+ }
+
return 0;
}
@@ -1822,7 +1895,8 @@ static struct hda_channel_mode alc880_test_modes[4] = {
{ 8, NULL },
};
-static int alc_test_pin_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static int alc_test_pin_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = {
"N/A", "Line Out", "HP Out",
@@ -1837,7 +1911,8 @@ static int alc_test_pin_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_e
return 0;
}
-static int alc_test_pin_ctl_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int alc_test_pin_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
@@ -1863,7 +1938,8 @@ static int alc_test_pin_ctl_get(struct snd_kcontrol *kcontrol, struct snd_ctl_el
return 0;
}
-static int alc_test_pin_ctl_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int alc_test_pin_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
@@ -1881,15 +1957,18 @@ static int alc_test_pin_ctl_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
new_ctl = ctls[ucontrol->value.enumerated.item[0]];
if (old_ctl != new_ctl) {
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, new_ctl);
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, new_ctl);
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- ucontrol->value.enumerated.item[0] >= 3 ? 0xb080 : 0xb000);
+ (ucontrol->value.enumerated.item[0] >= 3 ?
+ 0xb080 : 0xb000));
return 1;
}
return 0;
}
-static int alc_test_pin_src_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static int alc_test_pin_src_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = {
"Front", "Surround", "CLFE", "Side"
@@ -1903,7 +1982,8 @@ static int alc_test_pin_src_info(struct snd_kcontrol *kcontrol, struct snd_ctl_e
return 0;
}
-static int alc_test_pin_src_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int alc_test_pin_src_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
@@ -1914,7 +1994,8 @@ static int alc_test_pin_src_get(struct snd_kcontrol *kcontrol, struct snd_ctl_el
return 0;
}
-static int alc_test_pin_src_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int alc_test_pin_src_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
@@ -2065,7 +2146,10 @@ static struct hda_board_config alc880_cfg_tbl[] = {
{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe20f, .config = ALC880_3ST },
{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe210, .config = ALC880_3ST },
{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe211, .config = ALC880_3ST },
+ { .pci_subvendor = 0x8086, .pci_subdevice = 0xe212, .config = ALC880_3ST },
+ { .pci_subvendor = 0x8086, .pci_subdevice = 0xe213, .config = ALC880_3ST },
{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe214, .config = ALC880_3ST },
+ { .pci_subvendor = 0x8086, .pci_subdevice = 0xe234, .config = ALC880_3ST },
{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe302, .config = ALC880_3ST },
{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe303, .config = ALC880_3ST },
{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe304, .config = ALC880_3ST },
@@ -2080,6 +2164,7 @@ static struct hda_board_config alc880_cfg_tbl[] = {
{ .pci_subvendor = 0x107b, .pci_subdevice = 0x4040, .config = ALC880_3ST },
{ .pci_subvendor = 0x107b, .pci_subdevice = 0x4041, .config = ALC880_3ST },
/* TCL S700 */
+ { .modelname = "tcl", .config = ALC880_TCL_S700 },
{ .pci_subvendor = 0x19db, .pci_subdevice = 0x4188, .config = ALC880_TCL_S700 },
/* Back 3 jack, front 2 jack (Internal add Aux-In) */
@@ -2091,8 +2176,13 @@ static struct hda_board_config alc880_cfg_tbl[] = {
{ .modelname = "3stack-digout", .config = ALC880_3ST_DIG },
{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe308, .config = ALC880_3ST_DIG },
{ .pci_subvendor = 0x1025, .pci_subdevice = 0x0070, .config = ALC880_3ST_DIG },
- /* Clevo m520G NB */
- { .pci_subvendor = 0x1558, .pci_subdevice = 0x0520, .config = ALC880_CLEVO },
+
+ /* Clevo laptops */
+ { .modelname = "clevo", .config = ALC880_CLEVO },
+ { .pci_subvendor = 0x1558, .pci_subdevice = 0x0520,
+ .config = ALC880_CLEVO }, /* Clevo m520G NB */
+ { .pci_subvendor = 0x1558, .pci_subdevice = 0x0660,
+ .config = ALC880_CLEVO }, /* Clevo m665n */
/* Back 3 jack plus 1 SPDIF out jack, front 2 jack (Internal add Aux-In)*/
{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe305, .config = ALC880_3ST_DIG },
@@ -2157,12 +2247,16 @@ static struct hda_board_config alc880_cfg_tbl[] = {
{ .pci_subvendor = 0x1043, .pci_subdevice = 0x1113, .config = ALC880_ASUS_DIG },
{ .pci_subvendor = 0x1043, .pci_subdevice = 0x1173, .config = ALC880_ASUS_DIG },
{ .pci_subvendor = 0x1043, .pci_subdevice = 0x1993, .config = ALC880_ASUS },
+ { .pci_subvendor = 0x1043, .pci_subdevice = 0x10c2, .config = ALC880_ASUS_DIG }, /* Asus W6A */
{ .pci_subvendor = 0x1043, .pci_subdevice = 0x10c3, .config = ALC880_ASUS_DIG },
{ .pci_subvendor = 0x1043, .pci_subdevice = 0x1133, .config = ALC880_ASUS },
{ .pci_subvendor = 0x1043, .pci_subdevice = 0x1123, .config = ALC880_ASUS_DIG },
{ .pci_subvendor = 0x1043, .pci_subdevice = 0x1143, .config = ALC880_ASUS },
+ { .modelname = "asus-w1v", .config = ALC880_ASUS_W1V },
{ .pci_subvendor = 0x1043, .pci_subdevice = 0x10b3, .config = ALC880_ASUS_W1V },
+ { .modelname = "asus-dig", .config = ALC880_ASUS_DIG },
{ .pci_subvendor = 0x1043, .pci_subdevice = 0x8181, .config = ALC880_ASUS_DIG }, /* ASUS P4GPL-X */
+ { .modelname = "asus-dig2", .config = ALC880_ASUS_DIG2 },
{ .pci_subvendor = 0x1558, .pci_subdevice = 0x5401, .config = ALC880_ASUS_DIG2 },
{ .modelname = "uniwill", .config = ALC880_UNIWILL_DIG },
@@ -2178,6 +2272,7 @@ static struct hda_board_config alc880_cfg_tbl[] = {
{ .modelname = "lg-lw", .config = ALC880_LG_LW },
{ .pci_subvendor = 0x1854, .pci_subdevice = 0x0018, .config = ALC880_LG_LW },
+ { .pci_subvendor = 0x1854, .pci_subdevice = 0x0077, .config = ALC880_LG_LW },
#ifdef CONFIG_SND_DEBUG
{ .modelname = "test", .config = ALC880_TEST },
@@ -2198,6 +2293,7 @@ static struct alc_config_preset alc880_presets[] = {
.dac_nids = alc880_dac_nids,
.num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
.channel_mode = alc880_threestack_modes,
+ .need_dac_fix = 1,
.input_mux = &alc880_capture_source,
},
[ALC880_3ST_DIG] = {
@@ -2208,6 +2304,7 @@ static struct alc_config_preset alc880_presets[] = {
.dig_out_nid = ALC880_DIGOUT_NID,
.num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
.channel_mode = alc880_threestack_modes,
+ .need_dac_fix = 1,
.input_mux = &alc880_capture_source,
},
[ALC880_TCL_S700] = {
@@ -2300,6 +2397,7 @@ static struct alc_config_preset alc880_presets[] = {
.dac_nids = alc880_asus_dac_nids,
.num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
.channel_mode = alc880_asus_modes,
+ .need_dac_fix = 1,
.input_mux = &alc880_capture_source,
},
[ALC880_ASUS_DIG] = {
@@ -2311,6 +2409,7 @@ static struct alc_config_preset alc880_presets[] = {
.dig_out_nid = ALC880_DIGOUT_NID,
.num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
.channel_mode = alc880_asus_modes,
+ .need_dac_fix = 1,
.input_mux = &alc880_capture_source,
},
[ALC880_ASUS_DIG2] = {
@@ -2322,6 +2421,7 @@ static struct alc_config_preset alc880_presets[] = {
.dig_out_nid = ALC880_DIGOUT_NID,
.num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
.channel_mode = alc880_asus_modes,
+ .need_dac_fix = 1,
.input_mux = &alc880_capture_source,
},
[ALC880_ASUS_W1V] = {
@@ -2333,6 +2433,7 @@ static struct alc_config_preset alc880_presets[] = {
.dig_out_nid = ALC880_DIGOUT_NID,
.num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
.channel_mode = alc880_asus_modes,
+ .need_dac_fix = 1,
.input_mux = &alc880_capture_source,
},
[ALC880_UNIWILL_DIG] = {
@@ -2343,6 +2444,7 @@ static struct alc_config_preset alc880_presets[] = {
.dig_out_nid = ALC880_DIGOUT_NID,
.num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
.channel_mode = alc880_asus_modes,
+ .need_dac_fix = 1,
.input_mux = &alc880_capture_source,
},
[ALC880_CLEVO] = {
@@ -2354,6 +2456,7 @@ static struct alc_config_preset alc880_presets[] = {
.hp_nid = 0x03,
.num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
.channel_mode = alc880_threestack_modes,
+ .need_dac_fix = 1,
.input_mux = &alc880_capture_source,
},
[ALC880_LG] = {
@@ -2365,6 +2468,7 @@ static struct alc_config_preset alc880_presets[] = {
.dig_out_nid = ALC880_DIGOUT_NID,
.num_channel_mode = ARRAY_SIZE(alc880_lg_ch_modes),
.channel_mode = alc880_lg_ch_modes,
+ .need_dac_fix = 1,
.input_mux = &alc880_lg_capture_source,
.unsol_event = alc880_lg_unsol_event,
.init_hook = alc880_lg_automute,
@@ -2649,7 +2753,7 @@ static void alc880_auto_init_extra_out(struct hda_codec *codec)
pin = spec->autocfg.speaker_pins[0];
if (pin) /* connect to front */
alc880_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
- pin = spec->autocfg.hp_pin;
+ pin = spec->autocfg.hp_pins[0];
if (pin) /* connect to front */
alc880_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
}
@@ -2690,7 +2794,7 @@ static int alc880_parse_auto_config(struct hda_codec *codec)
(err = alc880_auto_create_extra_out(spec,
spec->autocfg.speaker_pins[0],
"Speaker")) < 0 ||
- (err = alc880_auto_create_extra_out(spec, spec->autocfg.hp_pin,
+ (err = alc880_auto_create_extra_out(spec, spec->autocfg.hp_pins[0],
"Headphone")) < 0 ||
(err = alc880_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
return err;
@@ -2739,7 +2843,8 @@ static int patch_alc880(struct hda_codec *codec)
board_config = snd_hda_check_board_config(codec, alc880_cfg_tbl);
if (board_config < 0 || board_config >= ALC880_MODEL_LAST) {
- printk(KERN_INFO "hda_codec: Unknown model for ALC880, trying auto-probe from BIOS...\n");
+ printk(KERN_INFO "hda_codec: Unknown model for ALC880, "
+ "trying auto-probe from BIOS...\n");
board_config = ALC880_AUTO;
}
@@ -2750,7 +2855,9 @@ static int patch_alc880(struct hda_codec *codec)
alc_free(codec);
return err;
} else if (! err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 3-stack mode...\n");
+ printk(KERN_INFO
+ "hda_codec: Cannot set up configuration "
+ "from BIOS. Using 3-stack mode...\n");
board_config = ALC880_3ST;
}
}
@@ -3629,7 +3736,7 @@ static int alc260_auto_create_multi_out_ctls(struct alc_spec *spec,
return err;
}
- nid = cfg->hp_pin;
+ nid = cfg->hp_pins[0];
if (nid) {
err = alc260_add_playback_controls(spec, nid, "Headphone");
if (err < 0)
@@ -3699,7 +3806,7 @@ static void alc260_auto_init_multi_out(struct hda_codec *codec)
if (nid)
alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0);
- nid = spec->autocfg.hp_pin;
+ nid = spec->autocfg.hp_pins[0];
if (nid)
alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0);
}
@@ -3832,7 +3939,8 @@ static struct hda_board_config alc260_cfg_tbl[] = {
{ .pci_subvendor = 0x152d, .pci_subdevice = 0x0729,
.config = ALC260_BASIC }, /* CTL Travel Master U553W */
{ .modelname = "hp", .config = ALC260_HP },
- { .pci_subvendor = 0x103c, .pci_subdevice = 0x3010, .config = ALC260_HP },
+ { .modelname = "hp-3013", .config = ALC260_HP_3013 },
+ { .pci_subvendor = 0x103c, .pci_subdevice = 0x3010, .config = ALC260_HP_3013 },
{ .pci_subvendor = 0x103c, .pci_subdevice = 0x3011, .config = ALC260_HP },
{ .pci_subvendor = 0x103c, .pci_subdevice = 0x3012, .config = ALC260_HP_3013 },
{ .pci_subvendor = 0x103c, .pci_subdevice = 0x3013, .config = ALC260_HP_3013 },
@@ -3947,7 +4055,8 @@ static int patch_alc260(struct hda_codec *codec)
board_config = snd_hda_check_board_config(codec, alc260_cfg_tbl);
if (board_config < 0 || board_config >= ALC260_MODEL_LAST) {
- snd_printd(KERN_INFO "hda_codec: Unknown model for ALC260\n");
+ snd_printd(KERN_INFO "hda_codec: Unknown model for ALC260, "
+ "trying auto-probe from BIOS...\n");
board_config = ALC260_AUTO;
}
@@ -3958,7 +4067,9 @@ static int patch_alc260(struct hda_codec *codec)
alc_free(codec);
return err;
} else if (! err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using base mode...\n");
+ printk(KERN_INFO
+ "hda_codec: Cannot set up configuration "
+ "from BIOS. Using base mode...\n");
board_config = ALC260_BASIC;
}
}
@@ -4195,6 +4306,13 @@ static struct hda_verb alc882_init_verbs[] = {
{ }
};
+static struct hda_verb alc882_eapd_verbs[] = {
+ /* change to EAPD mode */
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x3060},
+ { }
+};
+
/*
* generic initialization of ADC, input mixers and output mixers
*/
@@ -4320,9 +4438,15 @@ static struct snd_kcontrol_new alc882_capture_mixer[] = {
static struct hda_board_config alc882_cfg_tbl[] = {
{ .modelname = "3stack-dig", .config = ALC882_3ST_DIG },
{ .modelname = "6stack-dig", .config = ALC882_6ST_DIG },
- { .pci_subvendor = 0x1462, .pci_subdevice = 0x6668, .config = ALC882_6ST_DIG }, /* MSI */
- { .pci_subvendor = 0x105b, .pci_subdevice = 0x6668, .config = ALC882_6ST_DIG }, /* Foxconn */
- { .pci_subvendor = 0x1019, .pci_subdevice = 0x6668, .config = ALC882_6ST_DIG }, /* ECS */
+ { .pci_subvendor = 0x1462, .pci_subdevice = 0x6668,
+ .config = ALC882_6ST_DIG }, /* MSI */
+ { .pci_subvendor = 0x105b, .pci_subdevice = 0x6668,
+ .config = ALC882_6ST_DIG }, /* Foxconn */
+ { .pci_subvendor = 0x1019, .pci_subdevice = 0x6668,
+ .config = ALC882_6ST_DIG }, /* ECS to Intel*/
+ { .modelname = "arima", .config = ALC882_ARIMA },
+ { .pci_subvendor = 0x161f, .pci_subdevice = 0x2054,
+ .config = ALC882_ARIMA }, /* Arima W820Di1 */
{ .modelname = "auto", .config = ALC882_AUTO },
{}
};
@@ -4337,6 +4461,7 @@ static struct alc_config_preset alc882_presets[] = {
.dig_in_nid = ALC882_DIGIN_NID,
.num_channel_mode = ARRAY_SIZE(alc882_ch_modes),
.channel_mode = alc882_ch_modes,
+ .need_dac_fix = 1,
.input_mux = &alc882_capture_source,
},
[ALC882_6ST_DIG] = {
@@ -4350,6 +4475,15 @@ static struct alc_config_preset alc882_presets[] = {
.channel_mode = alc882_sixstack_modes,
.input_mux = &alc882_capture_source,
},
+ [ALC882_ARIMA] = {
+ .mixers = { alc882_base_mixer, alc882_chmode_mixer },
+ .init_verbs = { alc882_init_verbs, alc882_eapd_verbs },
+ .num_dacs = ARRAY_SIZE(alc882_dac_nids),
+ .dac_nids = alc882_dac_nids,
+ .num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes),
+ .channel_mode = alc882_sixstack_modes,
+ .input_mux = &alc882_capture_source,
+ },
};
@@ -4392,7 +4526,7 @@ static void alc882_auto_init_hp_out(struct hda_codec *codec)
struct alc_spec *spec = codec->spec;
hda_nid_t pin;
- pin = spec->autocfg.hp_pin;
+ pin = spec->autocfg.hp_pins[0];
if (pin) /* connect to front */
alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); /* use dac 0 */
}
@@ -4439,10 +4573,6 @@ static void alc882_auto_init(struct hda_codec *codec)
alc882_auto_init_analog_input(codec);
}
-/*
- * ALC882 Headphone poll in 3.5.1a or 3.5.2
- */
-
static int patch_alc882(struct hda_codec *codec)
{
struct alc_spec *spec;
@@ -4457,7 +4587,8 @@ static int patch_alc882(struct hda_codec *codec)
board_config = snd_hda_check_board_config(codec, alc882_cfg_tbl);
if (board_config < 0 || board_config >= ALC882_MODEL_LAST) {
- printk(KERN_INFO "hda_codec: Unknown model for ALC882, trying auto-probe from BIOS...\n");
+ printk(KERN_INFO "hda_codec: Unknown model for ALC882, "
+ "trying auto-probe from BIOS...\n");
board_config = ALC882_AUTO;
}
@@ -4468,7 +4599,9 @@ static int patch_alc882(struct hda_codec *codec)
alc_free(codec);
return err;
} else if (! err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using base mode...\n");
+ printk(KERN_INFO
+ "hda_codec: Cannot set up configuration "
+ "from BIOS. Using base mode...\n");
board_config = ALC882_3ST_DIG;
}
}
@@ -4509,6 +4642,680 @@ static int patch_alc882(struct hda_codec *codec)
}
/*
+ * ALC883 support
+ *
+ * ALC883 is almost identical with ALC880 but has cleaner and more flexible
+ * configuration. Each pin widget can choose any input DACs and a mixer.
+ * Each ADC is connected from a mixer of all inputs. This makes possible
+ * 6-channel independent captures.
+ *
+ * In addition, an independent DAC for the multi-playback (not used in this
+ * driver yet).
+ */
+#define ALC883_DIGOUT_NID 0x06
+#define ALC883_DIGIN_NID 0x0a
+
+static hda_nid_t alc883_dac_nids[4] = {
+ /* front, rear, clfe, rear_surr */
+ 0x02, 0x04, 0x03, 0x05
+};
+
+static hda_nid_t alc883_adc_nids[2] = {
+ /* ADC1-2 */
+ 0x08, 0x09,
+};
+/* input MUX */
+/* FIXME: should be a matrix-type input source selection */
+
+static struct hda_input_mux alc883_capture_source = {
+ .num_items = 4,
+ .items = {
+ { "Mic", 0x0 },
+ { "Front Mic", 0x1 },
+ { "Line", 0x2 },
+ { "CD", 0x4 },
+ },
+};
+#define alc883_mux_enum_info alc_mux_enum_info
+#define alc883_mux_enum_get alc_mux_enum_get
+
+static int alc883_mux_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct alc_spec *spec = codec->spec;
+ const struct hda_input_mux *imux = spec->input_mux;
+ unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ static hda_nid_t capture_mixers[3] = { 0x24, 0x23, 0x22 };
+ hda_nid_t nid = capture_mixers[adc_idx];
+ unsigned int *cur_val = &spec->cur_mux[adc_idx];
+ unsigned int i, idx;
+
+ idx = ucontrol->value.enumerated.item[0];
+ if (idx >= imux->num_items)
+ idx = imux->num_items - 1;
+ if (*cur_val == idx && ! codec->in_resume)
+ return 0;
+ for (i = 0; i < imux->num_items; i++) {
+ unsigned int v = (i == idx) ? 0x7000 : 0x7080;
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ v | (imux->items[i].index << 8));
+ }
+ *cur_val = idx;
+ return 1;
+}
+/*
+ * 2ch mode
+ */
+static struct hda_channel_mode alc883_3ST_2ch_modes[1] = {
+ { 2, NULL }
+};
+
+/*
+ * 2ch mode
+ */
+static struct hda_verb alc883_3ST_ch2_init[] = {
+ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+ { } /* end */
+};
+
+/*
+ * 6ch mode
+ */
+static struct hda_verb alc883_3ST_ch6_init[] = {
+ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 },
+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
+ { } /* end */
+};
+
+static struct hda_channel_mode alc883_3ST_6ch_modes[2] = {
+ { 2, alc883_3ST_ch2_init },
+ { 6, alc883_3ST_ch6_init },
+};
+
+/*
+ * 6ch mode
+ */
+static struct hda_verb alc883_sixstack_ch6_init[] = {
+ { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
+ { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { } /* end */
+};
+
+/*
+ * 8ch mode
+ */
+static struct hda_verb alc883_sixstack_ch8_init[] = {
+ { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { } /* end */
+};
+
+static struct hda_channel_mode alc883_sixstack_modes[2] = {
+ { 6, alc883_sixstack_ch6_init },
+ { 8, alc883_sixstack_ch8_init },
+};
+
+/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17
+ * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b
+ */
+
+static struct snd_kcontrol_new alc883_base_mixer[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+ HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 2,
+ .info = alc883_mux_enum_info,
+ .get = alc883_mux_enum_get,
+ .put = alc883_mux_enum_put,
+ },
+ { } /* end */
+};
+
+static struct snd_kcontrol_new alc883_3ST_2ch_mixer[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 2,
+ .info = alc883_mux_enum_info,
+ .get = alc883_mux_enum_get,
+ .put = alc883_mux_enum_put,
+ },
+ { } /* end */
+};
+
+static struct snd_kcontrol_new alc883_3ST_6ch_mixer[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+ HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 2,
+ .info = alc883_mux_enum_info,
+ .get = alc883_mux_enum_get,
+ .put = alc883_mux_enum_put,
+ },
+ { } /* end */
+};
+
+static struct snd_kcontrol_new alc883_chmode_mixer[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Channel Mode",
+ .info = alc_ch_mode_info,
+ .get = alc_ch_mode_get,
+ .put = alc_ch_mode_put,
+ },
+ { } /* end */
+};
+
+static struct hda_verb alc883_init_verbs[] = {
+ /* ADC1: mute amp left and right */
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+ /* ADC2: mute amp left and right */
+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
+ /* Front mixer: unmute input/output amp left and right (volume = 0) */
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ /* Rear mixer */
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ /* CLFE mixer */
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ /* Side mixer */
+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+
+ /* Front Pin: output 0 (0x0c) */
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
+ /* Rear Pin: output 1 (0x0d) */
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
+ /* CLFE Pin: output 2 (0x0e) */
+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x16, AC_VERB_SET_CONNECT_SEL, 0x02},
+ /* Side Pin: output 3 (0x0f) */
+ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
+ /* Mic (rear) pin: input vref at 80% */
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ /* Front Mic pin: input vref at 80% */
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ /* Line In pin: input */
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ /* Line-2 In: Headphone output (output 0 - 0x0c) */
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
+ /* CD pin widget for input */
+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+
+ /* FIXME: use matrix-type input source selection */
+ /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
+ /* Input mixer2 */
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+ /* Input mixer3 */
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+ { }
+};
+
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static struct hda_verb alc883_auto_init_verbs[] = {
+ /*
+ * Unmute ADC0-2 and set the default input to mic-in
+ */
+ {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+ /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+ * mixer widget
+ * Note: PASD motherboards uses the Line In 2 as the input for front panel
+ * mic (mic 2)
+ */
+ /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+
+ /*
+ * Set up output mixers (0x0c - 0x0f)
+ */
+ /* set vol=0 to output mixers */
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ /* set up input amps for analog loopback */
+ /* Amp Indices: DAC = 0, mixer = 1 */
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+
+ /* FIXME: use matrix-type input source selection */
+ /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
+ /* Input mixer1 */
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ //{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+ /* Input mixer2 */
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ //{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+
+ { }
+};
+
+/* capture mixer elements */
+static struct snd_kcontrol_new alc883_capture_mixer[] = {
+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* The multiple "Capture Source" controls confuse alsamixer
+ * So call somewhat different..
+ * FIXME: the controls appear in the "playback" view!
+ */
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 2,
+ .info = alc882_mux_enum_info,
+ .get = alc882_mux_enum_get,
+ .put = alc882_mux_enum_put,
+ },
+ { } /* end */
+};
+
+/* pcm configuration: identiacal with ALC880 */
+#define alc883_pcm_analog_playback alc880_pcm_analog_playback
+#define alc883_pcm_analog_capture alc880_pcm_analog_capture
+#define alc883_pcm_digital_playback alc880_pcm_digital_playback
+#define alc883_pcm_digital_capture alc880_pcm_digital_capture
+
+/*
+ * configuration and preset
+ */
+static struct hda_board_config alc883_cfg_tbl[] = {
+ { .modelname = "3stack-dig", .config = ALC883_3ST_2ch_DIG },
+ { .modelname = "3stack-6ch-dig", .config = ALC883_3ST_6ch_DIG },
+ { .pci_subvendor = 0x1019, .pci_subdevice = 0x6668,
+ .config = ALC883_3ST_6ch_DIG }, /* ECS to Intel*/
+ { .modelname = "3stack-6ch", .config = ALC883_3ST_6ch },
+ { .pci_subvendor = 0x108e, .pci_subdevice = 0x534d,
+ .config = ALC883_3ST_6ch },
+ { .pci_subvendor = 0x8086, .pci_subdevice = 0xd601,
+ .config = ALC883_3ST_6ch }, /* D102GGC */
+ { .modelname = "6stack-dig", .config = ALC883_6ST_DIG },
+ { .pci_subvendor = 0x1462, .pci_subdevice = 0x6668,
+ .config = ALC883_6ST_DIG }, /* MSI */
+ { .pci_subvendor = 0x105b, .pci_subdevice = 0x6668,
+ .config = ALC883_6ST_DIG }, /* Foxconn */
+ { .modelname = "6stack-dig-demo", .config = ALC888_DEMO_BOARD },
+ { .modelname = "acer", .config = ALC883_ACER },
+ { .pci_subvendor = 0x1025, .pci_subdevice = 0/*0x0102*/,
+ .config = ALC883_ACER },
+ { .modelname = "auto", .config = ALC883_AUTO },
+ {}
+};
+
+static struct alc_config_preset alc883_presets[] = {
+ [ALC883_3ST_2ch_DIG] = {
+ .mixers = { alc883_3ST_2ch_mixer },
+ .init_verbs = { alc883_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc883_dac_nids),
+ .dac_nids = alc883_dac_nids,
+ .dig_out_nid = ALC883_DIGOUT_NID,
+ .num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+ .adc_nids = alc883_adc_nids,
+ .dig_in_nid = ALC883_DIGIN_NID,
+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
+ .channel_mode = alc883_3ST_2ch_modes,
+ .input_mux = &alc883_capture_source,
+ },
+ [ALC883_3ST_6ch_DIG] = {
+ .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
+ .init_verbs = { alc883_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc883_dac_nids),
+ .dac_nids = alc883_dac_nids,
+ .dig_out_nid = ALC883_DIGOUT_NID,
+ .num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+ .adc_nids = alc883_adc_nids,
+ .dig_in_nid = ALC883_DIGIN_NID,
+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
+ .channel_mode = alc883_3ST_6ch_modes,
+ .need_dac_fix = 1,
+ .input_mux = &alc883_capture_source,
+ },
+ [ALC883_3ST_6ch] = {
+ .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
+ .init_verbs = { alc883_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc883_dac_nids),
+ .dac_nids = alc883_dac_nids,
+ .num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+ .adc_nids = alc883_adc_nids,
+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
+ .channel_mode = alc883_3ST_6ch_modes,
+ .need_dac_fix = 1,
+ .input_mux = &alc883_capture_source,
+ },
+ [ALC883_6ST_DIG] = {
+ .mixers = { alc883_base_mixer, alc883_chmode_mixer },
+ .init_verbs = { alc883_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc883_dac_nids),
+ .dac_nids = alc883_dac_nids,
+ .dig_out_nid = ALC883_DIGOUT_NID,
+ .num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+ .adc_nids = alc883_adc_nids,
+ .dig_in_nid = ALC883_DIGIN_NID,
+ .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
+ .channel_mode = alc883_sixstack_modes,
+ .input_mux = &alc883_capture_source,
+ },
+ [ALC888_DEMO_BOARD] = {
+ .mixers = { alc883_base_mixer, alc883_chmode_mixer },
+ .init_verbs = { alc883_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc883_dac_nids),
+ .dac_nids = alc883_dac_nids,
+ .dig_out_nid = ALC883_DIGOUT_NID,
+ .num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+ .adc_nids = alc883_adc_nids,
+ .dig_in_nid = ALC883_DIGIN_NID,
+ .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
+ .channel_mode = alc883_sixstack_modes,
+ .input_mux = &alc883_capture_source,
+ },
+ [ALC883_ACER] = {
+ .mixers = { alc883_base_mixer,
+ alc883_chmode_mixer },
+ /* On TravelMate laptops, GPIO 0 enables the internal speaker
+ * and the headphone jack. Turn this on and rely on the
+ * standard mute methods whenever the user wants to turn
+ * these outputs off.
+ */
+ .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc883_dac_nids),
+ .dac_nids = alc883_dac_nids,
+ .num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+ .adc_nids = alc883_adc_nids,
+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
+ .channel_mode = alc883_3ST_2ch_modes,
+ .input_mux = &alc883_capture_source,
+ },
+};
+
+
+/*
+ * BIOS auto configuration
+ */
+static void alc883_auto_set_output_and_unmute(struct hda_codec *codec,
+ hda_nid_t nid, int pin_type,
+ int dac_idx)
+{
+ /* set as output */
+ struct alc_spec *spec = codec->spec;
+ int idx;
+
+ if (spec->multiout.dac_nids[dac_idx] == 0x25)
+ idx = 4;
+ else
+ idx = spec->multiout.dac_nids[dac_idx] - 2;
+
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ pin_type);
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_UNMUTE);
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx);
+
+}
+
+static void alc883_auto_init_multi_out(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i <= HDA_SIDE; i++) {
+ hda_nid_t nid = spec->autocfg.line_out_pins[i];
+ if (nid)
+ alc883_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
+ }
+}
+
+static void alc883_auto_init_hp_out(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t pin;
+
+ pin = spec->autocfg.hp_pins[0];
+ if (pin) /* connect to front */
+ /* use dac 0 */
+ alc883_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
+}
+
+#define alc883_is_input_pin(nid) alc880_is_input_pin(nid)
+#define ALC883_PIN_CD_NID ALC880_PIN_CD_NID
+
+static void alc883_auto_init_analog_input(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < AUTO_PIN_LAST; i++) {
+ hda_nid_t nid = spec->autocfg.input_pins[i];
+ if (alc883_is_input_pin(nid)) {
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ (i <= AUTO_PIN_FRONT_MIC ?
+ PIN_VREF80 : PIN_IN));
+ if (nid != ALC883_PIN_CD_NID)
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_MUTE);
+ }
+ }
+}
+
+/* almost identical with ALC880 parser... */
+static int alc883_parse_auto_config(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int err = alc880_parse_auto_config(codec);
+
+ if (err < 0)
+ return err;
+ else if (err > 0)
+ /* hack - override the init verbs */
+ spec->init_verbs[0] = alc883_auto_init_verbs;
+ spec->mixers[spec->num_mixers] = alc883_capture_mixer;
+ spec->num_mixers++;
+ return err;
+}
+
+/* additional initialization for auto-configuration model */
+static void alc883_auto_init(struct hda_codec *codec)
+{
+ alc883_auto_init_multi_out(codec);
+ alc883_auto_init_hp_out(codec);
+ alc883_auto_init_analog_input(codec);
+}
+
+static int patch_alc883(struct hda_codec *codec)
+{
+ struct alc_spec *spec;
+ int err, board_config;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+
+ board_config = snd_hda_check_board_config(codec, alc883_cfg_tbl);
+ if (board_config < 0 || board_config >= ALC883_MODEL_LAST) {
+ printk(KERN_INFO "hda_codec: Unknown model for ALC883, "
+ "trying auto-probe from BIOS...\n");
+ board_config = ALC883_AUTO;
+ }
+
+ if (board_config == ALC883_AUTO) {
+ /* automatic parse from the BIOS config */
+ err = alc883_parse_auto_config(codec);
+ if (err < 0) {
+ alc_free(codec);
+ return err;
+ } else if (! err) {
+ printk(KERN_INFO
+ "hda_codec: Cannot set up configuration "
+ "from BIOS. Using base mode...\n");
+ board_config = ALC883_3ST_2ch_DIG;
+ }
+ }
+
+ if (board_config != ALC883_AUTO)
+ setup_preset(spec, &alc883_presets[board_config]);
+
+ spec->stream_name_analog = "ALC883 Analog";
+ spec->stream_analog_playback = &alc883_pcm_analog_playback;
+ spec->stream_analog_capture = &alc883_pcm_analog_capture;
+
+ spec->stream_name_digital = "ALC883 Digital";
+ spec->stream_digital_playback = &alc883_pcm_digital_playback;
+ spec->stream_digital_capture = &alc883_pcm_digital_capture;
+
+ if (! spec->adc_nids && spec->input_mux) {
+ spec->adc_nids = alc883_adc_nids;
+ spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids);
+ }
+
+ codec->patch_ops = alc_patch_ops;
+ if (board_config == ALC883_AUTO)
+ spec->init_hook = alc883_auto_init;
+
+ return 0;
+}
+
+/*
* ALC262 support
*/
@@ -4542,6 +5349,28 @@ static struct snd_kcontrol_new alc262_base_mixer[] = {
{ } /* end */
};
+static struct snd_kcontrol_new alc262_HP_BPC_mixer[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
+
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_VOLUME("PC Beep Playback Volume", 0x0b, 0x05, HDA_INPUT),
+ HDA_CODEC_MUTE("PC Beep Playback Switch", 0x0b, 0x05, HDA_INPUT),
+ HDA_CODEC_VOLUME("AUX IN Playback Volume", 0x0b, 0x06, HDA_INPUT),
+ HDA_CODEC_MUTE("AUX IN Playback Switch", 0x0b, 0x06, HDA_INPUT),
+ { } /* end */
+};
+
#define alc262_capture_mixer alc882_capture_mixer
#define alc262_capture_alt_mixer alc882_capture_alt_mixer
@@ -4645,6 +5474,17 @@ static struct hda_input_mux alc262_fujitsu_capture_source = {
},
};
+static struct hda_input_mux alc262_HP_capture_source = {
+ .num_items = 5,
+ .items = {
+ { "Mic", 0x0 },
+ { "Front Mic", 0x3 },
+ { "Line", 0x2 },
+ { "CD", 0x4 },
+ { "AUX IN", 0x6 },
+ },
+};
+
/* mute/unmute internal speaker according to the hp jack and mute state */
static void alc262_fujitsu_automute(struct hda_codec *codec, int force)
{
@@ -4729,6 +5569,7 @@ static struct snd_kcontrol_new alc262_fujitsu_mixer[] = {
.info = snd_hda_mixer_amp_volume_info,
.get = snd_hda_mixer_amp_volume_get,
.put = alc262_fujitsu_master_vol_put,
+ .tlv = { .c = snd_hda_mixer_amp_tlv },
.private_value = HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT),
},
{
@@ -4747,6 +5588,13 @@ static struct snd_kcontrol_new alc262_fujitsu_mixer[] = {
{ } /* end */
};
+/* additional init verbs for Benq laptops */
+static struct hda_verb alc262_EAPD_verbs[] = {
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x3070},
+ {}
+};
+
/* add playback controls from the parsed DAC table */
static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec, const struct auto_pin_cfg *cfg)
{
@@ -4782,7 +5630,7 @@ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec, const struct
return err;
}
}
- nid = cfg->hp_pin;
+ nid = cfg->hp_pins[0];
if (nid) {
/* spec->multiout.hp_nid = 2; */
if (nid == 0x16) {
@@ -4868,6 +5716,93 @@ static struct hda_verb alc262_volume_init_verbs[] = {
{ }
};
+static struct hda_verb alc262_HP_BPC_init_verbs[] = {
+ /*
+ * Unmute ADC0-2 and set the default input to mic-in
+ */
+ {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+ /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+ * mixer widget
+ * Note: PASD motherboards uses the Line In 2 as the input for front panel
+ * mic (mic 2)
+ */
+ /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(6)},
+
+ /*
+ * Set up output mixers (0x0c - 0x0e)
+ */
+ /* set vol=0 to output mixers */
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+
+ /* set up input amps for analog loopback */
+ /* Amp Indices: DAC = 0, mixer = 1 */
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+
+ {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 },
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 },
+ {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
+ {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
+
+
+ /* FIXME: use matrix-type input source selection */
+ /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
+ /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
+ /* Input mixer2 */
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
+ /* Input mixer3 */
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
+
+ { }
+};
+
/* pcm configuration: identiacal with ALC880 */
#define alc262_pcm_analog_playback alc880_pcm_analog_playback
#define alc262_pcm_analog_capture alc880_pcm_analog_capture
@@ -4928,7 +5863,20 @@ static void alc262_auto_init(struct hda_codec *codec)
static struct hda_board_config alc262_cfg_tbl[] = {
{ .modelname = "basic", .config = ALC262_BASIC },
{ .modelname = "fujitsu", .config = ALC262_FUJITSU },
- { .pci_subvendor = 0x10cf, .pci_subdevice = 0x1397, .config = ALC262_FUJITSU },
+ { .pci_subvendor = 0x10cf, .pci_subdevice = 0x1397,
+ .config = ALC262_FUJITSU },
+ { .modelname = "hp-bpc", .config = ALC262_HP_BPC },
+ { .pci_subvendor = 0x103c, .pci_subdevice = 0x208c,
+ .config = ALC262_HP_BPC }, /* xw4400 */
+ { .pci_subvendor = 0x103c, .pci_subdevice = 0x3014,
+ .config = ALC262_HP_BPC }, /* xw6400 */
+ { .pci_subvendor = 0x103c, .pci_subdevice = 0x3015,
+ .config = ALC262_HP_BPC }, /* xw8400 */
+ { .pci_subvendor = 0x103c, .pci_subdevice = 0x12fe,
+ .config = ALC262_HP_BPC }, /* xw9400 */
+ { .modelname = "benq", .config = ALC262_BENQ_ED8 },
+ { .pci_subvendor = 0x17ff, .pci_subdevice = 0x0560,
+ .config = ALC262_BENQ_ED8 },
{ .modelname = "auto", .config = ALC262_AUTO },
{}
};
@@ -4956,6 +5904,26 @@ static struct alc_config_preset alc262_presets[] = {
.input_mux = &alc262_fujitsu_capture_source,
.unsol_event = alc262_fujitsu_unsol_event,
},
+ [ALC262_HP_BPC] = {
+ .mixers = { alc262_HP_BPC_mixer },
+ .init_verbs = { alc262_HP_BPC_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc262_dac_nids),
+ .dac_nids = alc262_dac_nids,
+ .hp_nid = 0x03,
+ .num_channel_mode = ARRAY_SIZE(alc262_modes),
+ .channel_mode = alc262_modes,
+ .input_mux = &alc262_HP_capture_source,
+ },
+ [ALC262_BENQ_ED8] = {
+ .mixers = { alc262_base_mixer },
+ .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs },
+ .num_dacs = ARRAY_SIZE(alc262_dac_nids),
+ .dac_nids = alc262_dac_nids,
+ .hp_nid = 0x03,
+ .num_channel_mode = ARRAY_SIZE(alc262_modes),
+ .channel_mode = alc262_modes,
+ .input_mux = &alc262_capture_source,
+ },
};
static int patch_alc262(struct hda_codec *codec)
@@ -4981,8 +5949,10 @@ static int patch_alc262(struct hda_codec *codec)
#endif
board_config = snd_hda_check_board_config(codec, alc262_cfg_tbl);
+
if (board_config < 0 || board_config >= ALC262_MODEL_LAST) {
- printk(KERN_INFO "hda_codec: Unknown model for ALC262, trying auto-probe from BIOS...\n");
+ printk(KERN_INFO "hda_codec: Unknown model for ALC262, "
+ "trying auto-probe from BIOS...\n");
board_config = ALC262_AUTO;
}
@@ -4993,7 +5963,9 @@ static int patch_alc262(struct hda_codec *codec)
alc_free(codec);
return err;
} else if (! err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using base mode...\n");
+ printk(KERN_INFO
+ "hda_codec: Cannot set up configuration "
+ "from BIOS. Using base mode...\n");
board_config = ALC262_BASIC;
}
}
@@ -5034,7 +6006,6 @@ static int patch_alc262(struct hda_codec *codec)
return 0;
}
-
/*
* ALC861 channel source setting (2/6 channel selection for 3-stack)
*/
@@ -5049,9 +6020,11 @@ static struct hda_verb alc861_threestack_ch2_init[] = {
/* set pin widget 18h (mic1/2) for input, for mic also enable the vref */
{ 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c },
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, //mic
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, //line in
+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c },
+#if 0
+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/
+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, /*line-in*/
+#endif
{ } /* end */
};
/*
@@ -5065,11 +6038,13 @@ static struct hda_verb alc861_threestack_ch6_init[] = {
{ 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
{ 0x0c, AC_VERB_SET_CONNECT_SEL, 0x00 },
- { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00 },
+ { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00 },
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, //mic
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8)) }, //line in
+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
+#if 0
+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, /*mic*/
+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8)) }, /*line in*/
+#endif
{ } /* end */
};
@@ -5077,6 +6052,23 @@ static struct hda_channel_mode alc861_threestack_modes[2] = {
{ 2, alc861_threestack_ch2_init },
{ 6, alc861_threestack_ch6_init },
};
+/* Set mic1 as input and unmute the mixer */
+static struct hda_verb alc861_uniwill_m31_ch2_init[] = {
+ { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, /*mic*/
+ { } /* end */
+};
+/* Set mic1 as output and mute mixer */
+static struct hda_verb alc861_uniwill_m31_ch4_init[] = {
+ { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/
+ { } /* end */
+};
+
+static struct hda_channel_mode alc861_uniwill_m31_modes[2] = {
+ { 2, alc861_uniwill_m31_ch2_init },
+ { 4, alc861_uniwill_m31_ch4_init },
+};
/* patch-ALC861 */
@@ -5155,6 +6147,47 @@ static struct snd_kcontrol_new alc861_3ST_mixer[] = {
},
{ } /* end */
};
+static struct snd_kcontrol_new alc861_uniwill_m31_mixer[] = {
+ /* output mixer control */
+ HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT),
+ /*HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT), */
+
+ /* Input mixer control */
+ /* HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), */
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT),
+
+ /* Capture mixer control */
+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .count = 1,
+ .info = alc_mux_enum_info,
+ .get = alc_mux_enum_get,
+ .put = alc_mux_enum_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Channel Mode",
+ .info = alc_ch_mode_info,
+ .get = alc_ch_mode_get,
+ .put = alc_ch_mode_put,
+ .private_value = ARRAY_SIZE(alc861_uniwill_m31_modes),
+ },
+ { } /* end */
+};
/*
* generic initialization of ADC, input mixers and output mixers
@@ -5283,6 +6316,67 @@ static struct hda_verb alc861_threestack_init_verbs[] = {
{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
{ }
};
+
+static struct hda_verb alc861_uniwill_m31_init_verbs[] = {
+ /*
+ * Unmute ADC0 and set the default input to mic-in
+ */
+ /* port-A for surround (rear panel) */
+ { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
+ /* port-B for mic-in (rear panel) with vref */
+ { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+ /* port-C for line-in (rear panel) */
+ { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+ /* port-D for Front */
+ { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+ { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 },
+ /* port-E for HP out (front panel) */
+ { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, // this has to be set to VREF80
+ /* route front PCM to HP */
+ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x01 },
+ /* port-F for mic-in (front panel) with vref */
+ { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+ /* port-G for CLFE (rear panel) */
+ { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
+ /* port-H for side (rear panel) */
+ { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
+ /* CD-in */
+ { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+ /* route front mic to ADC1*/
+ {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ /* Unmute DAC0~3 & spdif out*/
+ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+
+ /* Unmute Mixer 14 (mic) 1c (Line in)*/
+ {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+
+ /* Unmute Stereo Mixer 15 */
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c }, //Output 0~12 step
+
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, // hp used DAC 3 (Front)
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ { }
+};
+
/*
* generic initialization of ADC, input mixers and output mixers
*/
@@ -5353,6 +6447,11 @@ static hda_nid_t alc861_dac_nids[4] = {
0x03, 0x06, 0x05, 0x04
};
+static hda_nid_t alc660_dac_nids[3] = {
+ /* front, clfe, surround */
+ 0x03, 0x05, 0x06
+};
+
static hda_nid_t alc861_adc_nids[1] = {
/* ADC0-2 */
0x08,
@@ -5531,7 +6630,7 @@ static void alc861_auto_init_hp_out(struct hda_codec *codec)
struct alc_spec *spec = codec->spec;
hda_nid_t pin;
- pin = spec->autocfg.hp_pin;
+ pin = spec->autocfg.hp_pins[0];
if (pin) /* connect to front */
alc861_auto_set_output_and_unmute(codec, pin, PIN_HP, spec->multiout.dac_nids[0]);
}
@@ -5566,7 +6665,7 @@ static int alc861_parse_auto_config(struct hda_codec *codec)
if ((err = alc861_auto_fill_dac_nids(spec, &spec->autocfg)) < 0 ||
(err = alc861_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
- (err = alc861_auto_create_hp_ctls(spec, spec->autocfg.hp_pin)) < 0 ||
+ (err = alc861_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0])) < 0 ||
(err = alc861_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
return err;
@@ -5605,9 +6704,16 @@ static void alc861_auto_init(struct hda_codec *codec)
*/
static struct hda_board_config alc861_cfg_tbl[] = {
{ .modelname = "3stack", .config = ALC861_3ST },
- { .pci_subvendor = 0x8086, .pci_subdevice = 0xd600, .config = ALC861_3ST },
+ { .pci_subvendor = 0x8086, .pci_subdevice = 0xd600,
+ .config = ALC861_3ST },
+ { .modelname = "3stack-660", .config = ALC660_3ST },
+ { .pci_subvendor = 0x1043, .pci_subdevice = 0x81e7,
+ .config = ALC660_3ST },
{ .modelname = "3stack-dig", .config = ALC861_3ST_DIG },
{ .modelname = "6stack-dig", .config = ALC861_6ST_DIG },
+ { .modelname = "uniwill-m31", .config = ALC861_UNIWILL_M31},
+ { .pci_subvendor = 0x1584, .pci_subdevice = 0x9072,
+ .config = ALC861_UNIWILL_M31 },
{ .modelname = "auto", .config = ALC861_AUTO },
{}
};
@@ -5620,6 +6726,7 @@ static struct alc_config_preset alc861_presets[] = {
.dac_nids = alc861_dac_nids,
.num_channel_mode = ARRAY_SIZE(alc861_threestack_modes),
.channel_mode = alc861_threestack_modes,
+ .need_dac_fix = 1,
.num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
.adc_nids = alc861_adc_nids,
.input_mux = &alc861_capture_source,
@@ -5632,6 +6739,7 @@ static struct alc_config_preset alc861_presets[] = {
.dig_out_nid = ALC861_DIGOUT_NID,
.num_channel_mode = ARRAY_SIZE(alc861_threestack_modes),
.channel_mode = alc861_threestack_modes,
+ .need_dac_fix = 1,
.num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
.adc_nids = alc861_adc_nids,
.input_mux = &alc861_capture_source,
@@ -5648,6 +6756,32 @@ static struct alc_config_preset alc861_presets[] = {
.adc_nids = alc861_adc_nids,
.input_mux = &alc861_capture_source,
},
+ [ALC660_3ST] = {
+ .mixers = { alc861_3ST_mixer },
+ .init_verbs = { alc861_threestack_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc660_dac_nids),
+ .dac_nids = alc660_dac_nids,
+ .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes),
+ .channel_mode = alc861_threestack_modes,
+ .need_dac_fix = 1,
+ .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
+ .adc_nids = alc861_adc_nids,
+ .input_mux = &alc861_capture_source,
+ },
+ [ALC861_UNIWILL_M31] = {
+ .mixers = { alc861_uniwill_m31_mixer },
+ .init_verbs = { alc861_uniwill_m31_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc861_dac_nids),
+ .dac_nids = alc861_dac_nids,
+ .dig_out_nid = ALC861_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc861_uniwill_m31_modes),
+ .channel_mode = alc861_uniwill_m31_modes,
+ .need_dac_fix = 1,
+ .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
+ .adc_nids = alc861_adc_nids,
+ .input_mux = &alc861_capture_source,
+ },
+
};
@@ -5664,8 +6798,10 @@ static int patch_alc861(struct hda_codec *codec)
codec->spec = spec;
board_config = snd_hda_check_board_config(codec, alc861_cfg_tbl);
+
if (board_config < 0 || board_config >= ALC861_MODEL_LAST) {
- printk(KERN_INFO "hda_codec: Unknown model for ALC861, trying auto-probe from BIOS...\n");
+ printk(KERN_INFO "hda_codec: Unknown model for ALC861, "
+ "trying auto-probe from BIOS...\n");
board_config = ALC861_AUTO;
}
@@ -5676,7 +6812,9 @@ static int patch_alc861(struct hda_codec *codec)
alc_free(codec);
return err;
} else if (! err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using base mode...\n");
+ printk(KERN_INFO
+ "hda_codec: Cannot set up configuration "
+ "from BIOS. Using base mode...\n");
board_config = ALC861_3ST_DIG;
}
}
@@ -5707,8 +6845,12 @@ struct hda_codec_preset snd_hda_preset_realtek[] = {
{ .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 },
{ .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 },
{ .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 },
- { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc882 },
+ { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc883 },
{ .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 },
- { .id = 0x10ec0861, .name = "ALC861", .patch = patch_alc861 },
+ { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc883 },
+ { .id = 0x10ec0861, .rev = 0x100300, .name = "ALC861",
+ .patch = patch_alc861 },
+ { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660",
+ .patch = patch_alc861 },
{} /* terminator */
};
diff --git a/sound/pci/hda/patch_si3054.c b/sound/pci/hda/patch_si3054.c
index 250242cd6c70..76ec3d75fa9e 100644
--- a/sound/pci/hda/patch_si3054.c
+++ b/sound/pci/hda/patch_si3054.c
@@ -298,6 +298,7 @@ struct hda_codec_preset snd_hda_preset_si3054[] = {
{ .id = 0x163c3055, .name = "Si3054", .patch = patch_si3054 },
{ .id = 0x163c3155, .name = "Si3054", .patch = patch_si3054 },
{ .id = 0x11c13026, .name = "Si3054", .patch = patch_si3054 },
+ { .id = 0x10573057, .name = "Si3054", .patch = patch_si3054 },
{}
};
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 36f199442fdc..731b7b97ee71 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -36,12 +36,15 @@
#define NUM_CONTROL_ALLOC 32
#define STAC_HP_EVENT 0x37
-#define STAC_UNSOL_ENABLE (AC_USRSP_EN | STAC_HP_EVENT)
#define STAC_REF 0
#define STAC_D945GTP3 1
#define STAC_D945GTP5 2
#define STAC_MACMINI 3
+#define STAC_922X_MODELS 4 /* number of 922x models */
+#define STAC_D965_3ST 4
+#define STAC_D965_5ST 5
+#define STAC_927X_MODELS 6 /* number of 922x models */
struct sigmatel_spec {
struct snd_kcontrol_new *mixers[4];
@@ -70,6 +73,7 @@ struct sigmatel_spec {
hda_nid_t *pin_nids;
unsigned int num_pins;
unsigned int *pin_configs;
+ unsigned int *bios_pin_configs;
/* codec specific stuff */
struct hda_verb *init;
@@ -119,8 +123,17 @@ static hda_nid_t stac927x_mux_nids[3] = {
0x15, 0x16, 0x17
};
+static hda_nid_t stac9205_adc_nids[2] = {
+ 0x12, 0x13
+};
+
+static hda_nid_t stac9205_mux_nids[2] = {
+ 0x19, 0x1a
+};
+
static hda_nid_t stac9200_pin_nids[8] = {
- 0x08, 0x09, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
+ 0x08, 0x09, 0x0d, 0x0e,
+ 0x0f, 0x10, 0x11, 0x12,
};
static hda_nid_t stac922x_pin_nids[10] = {
@@ -134,6 +147,13 @@ static hda_nid_t stac927x_pin_nids[14] = {
0x14, 0x21, 0x22, 0x23,
};
+static hda_nid_t stac9205_pin_nids[12] = {
+ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+ 0x0f, 0x14, 0x16, 0x17, 0x18,
+ 0x21, 0x22,
+
+};
+
static int stac92xx_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
@@ -173,12 +193,28 @@ static struct hda_verb stac922x_core_init[] = {
{}
};
+static struct hda_verb d965_core_init[] = {
+ /* set master volume and direct control */
+ { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+ /* unmute node 0x1b */
+ { 0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+ /* select node 0x03 as DAC */
+ { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x01},
+ {}
+};
+
static struct hda_verb stac927x_core_init[] = {
/* set master volume and direct control */
{ 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
{}
};
+static struct hda_verb stac9205_core_init[] = {
+ /* set master volume and direct control */
+ { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+ {}
+};
+
static struct snd_kcontrol_new stac9200_mixer[] = {
HDA_CODEC_VOLUME("Master Playback Volume", 0xb, 0, HDA_OUTPUT),
HDA_CODEC_MUTE("Master Playback Switch", 0xb, 0, HDA_OUTPUT),
@@ -212,6 +248,21 @@ static struct snd_kcontrol_new stac922x_mixer[] = {
{ } /* end */
};
+/* This needs to be generated dynamically based on sequence */
+static struct snd_kcontrol_new stac9227_mixer[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Input Source",
+ .count = 1,
+ .info = stac92xx_mux_enum_info,
+ .get = stac92xx_mux_enum_get,
+ .put = stac92xx_mux_enum_put,
+ },
+ HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x1b, 0x0, HDA_OUTPUT),
+ { } /* end */
+};
+
static snd_kcontrol_new_t stac927x_mixer[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -227,6 +278,21 @@ static snd_kcontrol_new_t stac927x_mixer[] = {
{ } /* end */
};
+static snd_kcontrol_new_t stac9205_mixer[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Input Source",
+ .count = 1,
+ .info = stac92xx_mux_enum_info,
+ .get = stac92xx_mux_enum_get,
+ .put = stac92xx_mux_enum_put,
+ },
+ HDA_CODEC_VOLUME("InMux Capture Volume", 0x19, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("InVol Capture Volume", 0x1b, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("ADCMux Capture Switch", 0x1d, 0x0, HDA_OUTPUT),
+ { } /* end */
+};
+
static int stac92xx_build_controls(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
@@ -291,31 +357,67 @@ static unsigned int d945gtp5_pin_configs[10] = {
0x02a19320, 0x40000100,
};
-static unsigned int *stac922x_brd_tbl[] = {
- ref922x_pin_configs,
- d945gtp3_pin_configs,
- d945gtp5_pin_configs,
- NULL, /* STAC_MACMINI */
+static unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = {
+ [STAC_REF] = ref922x_pin_configs,
+ [STAC_D945GTP3] = d945gtp3_pin_configs,
+ [STAC_D945GTP5] = d945gtp5_pin_configs,
+ [STAC_MACMINI] = d945gtp5_pin_configs,
};
static struct hda_board_config stac922x_cfg_tbl[] = {
+ { .modelname = "5stack", .config = STAC_D945GTP5 },
+ { .modelname = "3stack", .config = STAC_D945GTP3 },
{ .modelname = "ref",
.pci_subvendor = PCI_VENDOR_ID_INTEL,
.pci_subdevice = 0x2668, /* DFI LanParty */
.config = STAC_REF }, /* SigmaTel reference board */
+ /* Intel 945G based systems */
{ .pci_subvendor = PCI_VENDOR_ID_INTEL,
.pci_subdevice = 0x0101,
.config = STAC_D945GTP3 }, /* Intel D945GTP - 3 Stack */
{ .pci_subvendor = PCI_VENDOR_ID_INTEL,
.pci_subdevice = 0x0202,
- .config = STAC_D945GTP3 }, /* Intel D945GNT - 3 Stack, 9221 A1 */
+ .config = STAC_D945GTP3 }, /* Intel D945GNT - 3 Stack */
{ .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x0b0b,
- .config = STAC_D945GTP3 }, /* Intel D945PSN - 3 Stack, 9221 A1 */
+ .pci_subdevice = 0x0606,
+ .config = STAC_D945GTP3 }, /* Intel D945GTP - 3 Stack */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x0601,
+ .config = STAC_D945GTP3 }, /* Intel D945GTP - 3 Stack */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x0111,
+ .config = STAC_D945GTP3 }, /* Intel D945GZP - 3 Stack */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x1115,
+ .config = STAC_D945GTP3 }, /* Intel D945GPM - 3 Stack */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x1116,
+ .config = STAC_D945GTP3 }, /* Intel D945GBO - 3 Stack */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x1117,
+ .config = STAC_D945GTP3 }, /* Intel D945GPM - 3 Stack */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x1118,
+ .config = STAC_D945GTP3 }, /* Intel D945GPM - 3 Stack */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x1119,
+ .config = STAC_D945GTP3 }, /* Intel D945GPM - 3 Stack */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x8826,
+ .config = STAC_D945GTP3 }, /* Intel D945GPM - 3 Stack */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x5049,
+ .config = STAC_D945GTP3 }, /* Intel D945GCZ - 3 Stack */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x5055,
+ .config = STAC_D945GTP3 }, /* Intel D945GCZ - 3 Stack */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x5048,
+ .config = STAC_D945GTP3 }, /* Intel D945GPB - 3 Stack */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x0110,
+ .config = STAC_D945GTP3 }, /* Intel D945GLR - 3 Stack */
{ .pci_subvendor = PCI_VENDOR_ID_INTEL,
- .pci_subdevice = 0x0707,
- .config = STAC_D945GTP5 }, /* Intel D945PSV - 5 Stack */
- { .pci_subvendor = PCI_VENDOR_ID_INTEL,
.pci_subdevice = 0x0404,
.config = STAC_D945GTP5 }, /* Intel D945GTP - 5 Stack */
{ .pci_subvendor = PCI_VENDOR_ID_INTEL,
@@ -327,6 +429,26 @@ static struct hda_board_config stac922x_cfg_tbl[] = {
{ .pci_subvendor = PCI_VENDOR_ID_INTEL,
.pci_subdevice = 0x0417,
.config = STAC_D945GTP5 }, /* Intel D975XBK - 5 Stack */
+ /* Intel 945P based systems */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x0b0b,
+ .config = STAC_D945GTP3 }, /* Intel D945PSN - 3 Stack */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x0112,
+ .config = STAC_D945GTP3 }, /* Intel D945PLN - 3 Stack */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x0d0d,
+ .config = STAC_D945GTP3 }, /* Intel D945PLM - 3 Stack */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x0909,
+ .config = STAC_D945GTP3 }, /* Intel D945PAW - 3 Stack */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x0505,
+ .config = STAC_D945GTP3 }, /* Intel D945PLM - 3 Stack */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x0707,
+ .config = STAC_D945GTP5 }, /* Intel D945PSV - 5 Stack */
+ /* other systems */
{ .pci_subvendor = 0x8384,
.pci_subdevice = 0x7680,
.config = STAC_MACMINI }, /* Apple Mac Mini (early 2006) */
@@ -334,31 +456,187 @@ static struct hda_board_config stac922x_cfg_tbl[] = {
};
static unsigned int ref927x_pin_configs[14] = {
- 0x01813122, 0x01a19021, 0x01014010, 0x01016011,
- 0x01012012, 0x01011014, 0x40000100, 0x40000100,
- 0x40000100, 0x40000100, 0x40000100, 0x01441030,
- 0x01c41030, 0x40000100,
+ 0x02214020, 0x02a19080, 0x0181304e, 0x01014010,
+ 0x01a19040, 0x01011012, 0x01016011, 0x0101201f,
+ 0x183301f0, 0x18a001f0, 0x18a001f0, 0x01442070,
+ 0x01c42190, 0x40000100,
+};
+
+static unsigned int d965_3st_pin_configs[14] = {
+ 0x0221401f, 0x02a19120, 0x40000100, 0x01014011,
+ 0x01a19021, 0x01813024, 0x40000100, 0x40000100,
+ 0x40000100, 0x40000100, 0x40000100, 0x40000100,
+ 0x40000100, 0x40000100
+};
+
+static unsigned int d965_5st_pin_configs[14] = {
+ 0x02214020, 0x02a19080, 0x0181304e, 0x01014010,
+ 0x01a19040, 0x01011012, 0x01016011, 0x40000100,
+ 0x40000100, 0x40000100, 0x40000100, 0x01442070,
+ 0x40000100, 0x40000100
};
-static unsigned int *stac927x_brd_tbl[] = {
- ref927x_pin_configs,
+static unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = {
+ [STAC_REF] = ref927x_pin_configs,
+ [STAC_D965_3ST] = d965_3st_pin_configs,
+ [STAC_D965_5ST] = d965_5st_pin_configs,
};
static struct hda_board_config stac927x_cfg_tbl[] = {
+ { .modelname = "5stack", .config = STAC_D965_5ST },
+ { .modelname = "3stack", .config = STAC_D965_3ST },
+ { .modelname = "ref",
+ .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x2668, /* DFI LanParty */
+ .config = STAC_REF }, /* SigmaTel reference board */
+ /* Intel 946 based systems */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x3d01,
+ .config = STAC_D965_3ST }, /* D946 configuration */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0xa301,
+ .config = STAC_D965_3ST }, /* Intel D946GZT - 3 stack */
+ /* 965 based 3 stack systems */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x2116,
+ .config = STAC_D965_3ST }, /* Intel D965 3Stack config */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x2115,
+ .config = STAC_D965_3ST }, /* Intel DQ965WC - 3 Stack */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x2114,
+ .config = STAC_D965_3ST }, /* Intel D965 3Stack config */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x2113,
+ .config = STAC_D965_3ST }, /* Intel D965 3Stack config */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x2112,
+ .config = STAC_D965_3ST }, /* Intel DG965MS - 3 Stack */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x2111,
+ .config = STAC_D965_3ST }, /* Intel D965 3Stack config */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x2110,
+ .config = STAC_D965_3ST }, /* Intel D965 3Stack config */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x2009,
+ .config = STAC_D965_3ST }, /* Intel D965 3Stack config */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x2008,
+ .config = STAC_D965_3ST }, /* Intel DQ965GF - 3 Stack */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x2007,
+ .config = STAC_D965_3ST }, /* Intel D965 3Stack config */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x2006,
+ .config = STAC_D965_3ST }, /* Intel D965 3Stack config */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x2005,
+ .config = STAC_D965_3ST }, /* Intel D965 3Stack config */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x2004,
+ .config = STAC_D965_3ST }, /* Intel D965 3Stack config */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x2003,
+ .config = STAC_D965_3ST }, /* Intel D965 3Stack config */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x2002,
+ .config = STAC_D965_3ST }, /* Intel D965 3Stack config */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x2001,
+ .config = STAC_D965_3ST }, /* Intel DQ965GF - 3 Stack */
+ /* 965 based 5 stack systems */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x2301,
+ .config = STAC_D965_5ST }, /* Intel DG965 - 5 Stack */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x2302,
+ .config = STAC_D965_5ST }, /* Intel DG965 - 5 Stack */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x2303,
+ .config = STAC_D965_5ST }, /* Intel DG965 - 5 Stack */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x2304,
+ .config = STAC_D965_5ST }, /* Intel DG965 - 5 Stack */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x2305,
+ .config = STAC_D965_5ST }, /* Intel DG965 - 5 Stack */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x2501,
+ .config = STAC_D965_5ST }, /* Intel DG965MQ - 5 Stack */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x2502,
+ .config = STAC_D965_5ST }, /* Intel DG965 - 5 Stack */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x2503,
+ .config = STAC_D965_5ST }, /* Intel DG965 - 5 Stack */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x2504,
+ .config = STAC_D965_5ST }, /* Intel DQ965GF - 5 Stack */
+ {} /* terminator */
+};
+
+static unsigned int ref9205_pin_configs[12] = {
+ 0x40000100, 0x40000100, 0x01016011, 0x01014010,
+ 0x01813122, 0x01a19021, 0x40000100, 0x40000100,
+ 0x40000100, 0x40000100, 0x01441030, 0x01c41030
+};
+
+static unsigned int *stac9205_brd_tbl[] = {
+ ref9205_pin_configs,
+};
+
+static struct hda_board_config stac9205_cfg_tbl[] = {
{ .modelname = "ref",
.pci_subvendor = PCI_VENDOR_ID_INTEL,
.pci_subdevice = 0x2668, /* DFI LanParty */
.config = STAC_REF }, /* SigmaTel reference board */
+ /* Dell laptops have BIOS problem */
+ { .pci_subvendor = PCI_VENDOR_ID_DELL, .pci_subdevice = 0x01b5,
+ .config = STAC_REF }, /* Dell Inspiron 630m */
+ { .pci_subvendor = PCI_VENDOR_ID_DELL, .pci_subdevice = 0x01c2,
+ .config = STAC_REF }, /* Dell Latitude D620 */
+ { .pci_subvendor = PCI_VENDOR_ID_DELL, .pci_subdevice = 0x01cb,
+ .config = STAC_REF }, /* Dell Latitude 120L */
{} /* terminator */
};
+static int stac92xx_save_bios_config_regs(struct hda_codec *codec)
+{
+ int i;
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (! spec->bios_pin_configs) {
+ spec->bios_pin_configs = kcalloc(spec->num_pins,
+ sizeof(*spec->bios_pin_configs), GFP_KERNEL);
+ if (! spec->bios_pin_configs)
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < spec->num_pins; i++) {
+ hda_nid_t nid = spec->pin_nids[i];
+ unsigned int pin_cfg;
+
+ pin_cfg = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_CONFIG_DEFAULT, 0x00);
+ snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x bios pin config %8.8x\n",
+ nid, pin_cfg);
+ spec->bios_pin_configs[i] = pin_cfg;
+ }
+
+ return 0;
+}
+
static void stac92xx_set_config_regs(struct hda_codec *codec)
{
int i;
struct sigmatel_spec *spec = codec->spec;
unsigned int pin_cfg;
- for (i=0; i < spec->num_pins; i++) {
+ if (! spec->pin_nids || ! spec->pin_configs)
+ return;
+
+ for (i = 0; i < spec->num_pins; i++) {
snd_hda_codec_write(codec, spec->pin_nids[i], 0,
AC_VERB_SET_CONFIG_DEFAULT_BYTES_0,
spec->pin_configs[i] & 0x000000ff);
@@ -713,7 +991,8 @@ static int stac92xx_add_dyn_out_pins(struct hda_codec *codec, struct auto_pin_cf
* A and B is not supported.
*/
/* fill in the dac_nids table from the parsed pin configuration */
-static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec, const struct auto_pin_cfg *cfg)
+static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg)
{
struct sigmatel_spec *spec = codec->spec;
hda_nid_t nid;
@@ -731,11 +1010,32 @@ static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec, const struct aut
return 0;
}
-/* add playback controls from the parsed DAC table */
-static int stac92xx_auto_create_multi_out_ctls(struct sigmatel_spec *spec, const struct auto_pin_cfg *cfg)
+/* create volume control/switch for the given prefx type */
+static int create_controls(struct sigmatel_spec *spec, const char *pfx, hda_nid_t nid, int chs)
{
char name[32];
- static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" };
+ int err;
+
+ sprintf(name, "%s Playback Volume", pfx);
+ err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, name,
+ HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ sprintf(name, "%s Playback Switch", pfx);
+ err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, name,
+ HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+/* add playback controls from the parsed DAC table */
+static int stac92xx_auto_create_multi_out_ctls(struct sigmatel_spec *spec,
+ const struct auto_pin_cfg *cfg)
+{
+ static const char *chname[4] = {
+ "Front", "Surround", NULL /*CLFE*/, "Side"
+ };
hda_nid_t nid;
int i, err;
@@ -747,26 +1047,15 @@ static int stac92xx_auto_create_multi_out_ctls(struct sigmatel_spec *spec, const
if (i == 2) {
/* Center/LFE */
- if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, "Center Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT))) < 0)
- return err;
- if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, "LFE Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT))) < 0)
+ err = create_controls(spec, "Center", nid, 1);
+ if (err < 0)
return err;
- if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, "Center Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT))) < 0)
- return err;
- if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, "LFE Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT))) < 0)
+ err = create_controls(spec, "LFE", nid, 2);
+ if (err < 0)
return err;
} else {
- sprintf(name, "%s Playback Volume", chname[i]);
- if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
+ err = create_controls(spec, chname[i], nid, 3);
+ if (err < 0)
return err;
}
}
@@ -782,39 +1071,85 @@ static int stac92xx_auto_create_multi_out_ctls(struct sigmatel_spec *spec, const
return 0;
}
-/* add playback controls for HP output */
-static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec, struct auto_pin_cfg *cfg)
+static int check_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
{
- struct sigmatel_spec *spec = codec->spec;
- hda_nid_t pin = cfg->hp_pin;
- hda_nid_t nid;
- int i, err;
- unsigned int wid_caps;
+ int i;
- if (! pin)
- return 0;
+ for (i = 0; i < spec->multiout.num_dacs; i++) {
+ if (spec->multiout.dac_nids[i] == nid)
+ return 1;
+ }
+ if (spec->multiout.hp_nid == nid)
+ return 1;
+ return 0;
+}
- wid_caps = get_wcaps(codec, pin);
- if (wid_caps & AC_WCAP_UNSOL_CAP)
- spec->hp_detect = 1;
+static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid)
+{
+ if (!spec->multiout.hp_nid)
+ spec->multiout.hp_nid = nid;
+ else if (spec->multiout.num_dacs > 4) {
+ printk(KERN_WARNING "stac92xx: No space for DAC 0x%x\n", nid);
+ return 1;
+ } else {
+ spec->multiout.dac_nids[spec->multiout.num_dacs] = nid;
+ spec->multiout.num_dacs++;
+ }
+ return 0;
+}
- nid = snd_hda_codec_read(codec, pin, 0, AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
- for (i = 0; i < cfg->line_outs; i++) {
- if (! spec->multiout.dac_nids[i])
+/* add playback controls for Speaker and HP outputs */
+static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec,
+ struct auto_pin_cfg *cfg)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ hda_nid_t nid;
+ int i, old_num_dacs, err;
+
+ old_num_dacs = spec->multiout.num_dacs;
+ for (i = 0; i < cfg->hp_outs; i++) {
+ unsigned int wid_caps = get_wcaps(codec, cfg->hp_pins[i]);
+ if (wid_caps & AC_WCAP_UNSOL_CAP)
+ spec->hp_detect = 1;
+ nid = snd_hda_codec_read(codec, cfg->hp_pins[i], 0,
+ AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
+ if (check_in_dac_nids(spec, nid))
+ nid = 0;
+ if (! nid)
continue;
- if (spec->multiout.dac_nids[i] == nid)
- return 0;
+ add_spec_dacs(spec, nid);
+ }
+ for (i = 0; i < cfg->speaker_outs; i++) {
+ nid = snd_hda_codec_read(codec, cfg->speaker_pins[0], 0,
+ AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
+ if (check_in_dac_nids(spec, nid))
+ nid = 0;
+ if (check_in_dac_nids(spec, nid))
+ nid = 0;
+ if (! nid)
+ continue;
+ add_spec_dacs(spec, nid);
}
- spec->multiout.hp_nid = nid;
-
- /* control HP volume/switch on the output mixer amp */
- if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, "Headphone Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
- return err;
- if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, "Headphone Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
- return err;
+ for (i = old_num_dacs; i < spec->multiout.num_dacs; i++) {
+ static const char *pfxs[] = {
+ "Speaker", "External Speaker", "Speaker2",
+ };
+ err = create_controls(spec, pfxs[i - old_num_dacs],
+ spec->multiout.dac_nids[i], 3);
+ if (err < 0)
+ return err;
+ }
+ if (spec->multiout.hp_nid) {
+ const char *pfx;
+ if (old_num_dacs == spec->multiout.num_dacs)
+ pfx = "Master";
+ else
+ pfx = "Headphone";
+ err = create_controls(spec, pfx, spec->multiout.hp_nid, 3);
+ if (err < 0)
+ return err;
+ }
return 0;
}
@@ -828,23 +1163,28 @@ static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const
int i, j, k;
for (i = 0; i < AUTO_PIN_LAST; i++) {
- int index = -1;
- if (cfg->input_pins[i]) {
- imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
-
- for (j=0; j<spec->num_muxes; j++) {
- int num_cons = snd_hda_get_connections(codec, spec->mux_nids[j], con_lst, HDA_MAX_NUM_INPUTS);
- for (k=0; k<num_cons; k++)
- if (con_lst[k] == cfg->input_pins[i]) {
- index = k;
- break;
- }
- if (index >= 0)
- break;
- }
- imux->items[imux->num_items].index = index;
- imux->num_items++;
+ int index;
+
+ if (!cfg->input_pins[i])
+ continue;
+ index = -1;
+ for (j = 0; j < spec->num_muxes; j++) {
+ int num_cons;
+ num_cons = snd_hda_get_connections(codec,
+ spec->mux_nids[j],
+ con_lst,
+ HDA_MAX_NUM_INPUTS);
+ for (k = 0; k < num_cons; k++)
+ if (con_lst[k] == cfg->input_pins[i]) {
+ index = k;
+ goto found;
+ }
}
+ continue;
+ found:
+ imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
+ imux->items[imux->num_items].index = index;
+ imux->num_items++;
}
if (imux->num_items == 1) {
@@ -877,11 +1217,20 @@ static void stac92xx_auto_init_multi_out(struct hda_codec *codec)
static void stac92xx_auto_init_hp_out(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
- hda_nid_t pin;
+ int i;
- pin = spec->autocfg.hp_pin;
- if (pin) /* connect to front */
- stac92xx_auto_set_pinctl(codec, pin, AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN);
+ for (i = 0; i < spec->autocfg.hp_outs; i++) {
+ hda_nid_t pin;
+ pin = spec->autocfg.hp_pins[i];
+ if (pin) /* connect to front */
+ stac92xx_auto_set_pinctl(codec, pin, AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN);
+ }
+ for (i = 0; i < spec->autocfg.speaker_outs; i++) {
+ hda_nid_t pin;
+ pin = spec->autocfg.speaker_pins[i];
+ if (pin) /* connect to front */
+ stac92xx_auto_set_pinctl(codec, pin, AC_PINCTL_OUT_EN);
+ }
}
static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out, hda_nid_t dig_in)
@@ -893,10 +1242,12 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
return err;
if (! spec->autocfg.line_outs)
return 0; /* can't find valid pin config */
+
if ((err = stac92xx_add_dyn_out_pins(codec, &spec->autocfg)) < 0)
return err;
- if ((err = stac92xx_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
- return err;
+ if (spec->multiout.num_dacs == 0)
+ if ((err = stac92xx_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
+ return err;
if ((err = stac92xx_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
(err = stac92xx_auto_create_hp_ctls(codec, &spec->autocfg)) < 0 ||
@@ -925,7 +1276,7 @@ static int stac9200_auto_create_hp_ctls(struct hda_codec *codec,
struct auto_pin_cfg *cfg)
{
struct sigmatel_spec *spec = codec->spec;
- hda_nid_t pin = cfg->hp_pin;
+ hda_nid_t pin = cfg->hp_pins[0];
unsigned int wid_caps;
if (! pin)
@@ -938,6 +1289,57 @@ static int stac9200_auto_create_hp_ctls(struct hda_codec *codec,
return 0;
}
+/* add playback controls for LFE output */
+static int stac9200_auto_create_lfe_ctls(struct hda_codec *codec,
+ struct auto_pin_cfg *cfg)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int err;
+ hda_nid_t lfe_pin = 0x0;
+ int i;
+
+ /*
+ * search speaker outs and line outs for a mono speaker pin
+ * with an amp. If one is found, add LFE controls
+ * for it.
+ */
+ for (i = 0; i < spec->autocfg.speaker_outs && lfe_pin == 0x0; i++) {
+ hda_nid_t pin = spec->autocfg.speaker_pins[i];
+ unsigned long wcaps = get_wcaps(codec, pin);
+ wcaps &= (AC_WCAP_STEREO | AC_WCAP_OUT_AMP);
+ if (wcaps == AC_WCAP_OUT_AMP)
+ /* found a mono speaker with an amp, must be lfe */
+ lfe_pin = pin;
+ }
+
+ /* if speaker_outs is 0, then speakers may be in line_outs */
+ if (lfe_pin == 0 && spec->autocfg.speaker_outs == 0) {
+ for (i = 0; i < spec->autocfg.line_outs && lfe_pin == 0x0; i++) {
+ hda_nid_t pin = spec->autocfg.line_out_pins[i];
+ unsigned long cfg;
+ cfg = snd_hda_codec_read(codec, pin, 0,
+ AC_VERB_GET_CONFIG_DEFAULT,
+ 0x00);
+ if (get_defcfg_device(cfg) == AC_JACK_SPEAKER) {
+ unsigned long wcaps = get_wcaps(codec, pin);
+ wcaps &= (AC_WCAP_STEREO | AC_WCAP_OUT_AMP);
+ if (wcaps == AC_WCAP_OUT_AMP)
+ /* found a mono speaker with an amp,
+ must be lfe */
+ lfe_pin = pin;
+ }
+ }
+ }
+
+ if (lfe_pin) {
+ err = create_controls(spec, "LFE", lfe_pin, 1);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
static int stac9200_parse_auto_config(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
@@ -952,6 +1354,9 @@ static int stac9200_parse_auto_config(struct hda_codec *codec)
if ((err = stac9200_auto_create_hp_ctls(codec, &spec->autocfg)) < 0)
return err;
+ if ((err = stac9200_auto_create_lfe_ctls(codec, &spec->autocfg)) < 0)
+ return err;
+
if (spec->autocfg.dig_out_pin)
spec->multiout.dig_out_nid = 0x05;
if (spec->autocfg.dig_in_pin)
@@ -1004,6 +1409,15 @@ static void stac922x_gpio_mute(struct hda_codec *codec, int pin, int muted)
AC_VERB_SET_GPIO_DATA, gpiostate);
}
+static void enable_pin_detect(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int event)
+{
+ if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP)
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_UNSOLICITED_ENABLE,
+ (AC_USRSP_EN | event));
+}
+
static int stac92xx_init(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
@@ -1015,9 +1429,10 @@ static int stac92xx_init(struct hda_codec *codec)
/* set up pins */
if (spec->hp_detect) {
/* Enable unsolicited responses on the HP widget */
- snd_hda_codec_write(codec, cfg->hp_pin, 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- STAC_UNSOL_ENABLE);
+ for (i = 0; i < cfg->hp_outs; i++)
+ enable_pin_detect(codec, cfg->hp_pins[i],
+ STAC_HP_EVENT);
+ stac92xx_auto_init_hp_out(codec);
/* fake event to set up pins */
codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
} else {
@@ -1062,6 +1477,9 @@ static void stac92xx_free(struct hda_codec *codec)
kfree(spec->kctl_alloc);
}
+ if (spec->bios_pin_configs)
+ kfree(spec->bios_pin_configs);
+
kfree(spec);
}
@@ -1070,6 +1488,8 @@ static void stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid,
{
unsigned int pin_ctl = snd_hda_codec_read(codec, nid,
0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00);
+ if (flag == AC_PINCTL_OUT_EN && (pin_ctl & AC_PINCTL_IN_EN))
+ return;
snd_hda_codec_write(codec, nid, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL,
pin_ctl | flag);
@@ -1085,33 +1505,57 @@ static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid,
pin_ctl & ~flag);
}
-static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
+static int get_pin_presence(struct hda_codec *codec, hda_nid_t nid)
+{
+ if (!nid)
+ return 0;
+ if (snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0x00)
+ & (1 << 31))
+ return 1;
+ return 0;
+}
+
+static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
{
struct sigmatel_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
int i, presence;
- if ((res >> 26) != STAC_HP_EVENT)
- return;
-
- presence = snd_hda_codec_read(codec, cfg->hp_pin, 0,
- AC_VERB_GET_PIN_SENSE, 0x00) >> 31;
+ presence = 0;
+ for (i = 0; i < cfg->hp_outs; i++) {
+ presence = get_pin_presence(codec, cfg->hp_pins[i]);
+ if (presence)
+ break;
+ }
if (presence) {
/* disable lineouts, enable hp */
for (i = 0; i < cfg->line_outs; i++)
stac92xx_reset_pinctl(codec, cfg->line_out_pins[i],
AC_PINCTL_OUT_EN);
- stac92xx_set_pinctl(codec, cfg->hp_pin, AC_PINCTL_OUT_EN);
+ for (i = 0; i < cfg->speaker_outs; i++)
+ stac92xx_reset_pinctl(codec, cfg->speaker_pins[i],
+ AC_PINCTL_OUT_EN);
} else {
/* enable lineouts, disable hp */
for (i = 0; i < cfg->line_outs; i++)
stac92xx_set_pinctl(codec, cfg->line_out_pins[i],
AC_PINCTL_OUT_EN);
- stac92xx_reset_pinctl(codec, cfg->hp_pin, AC_PINCTL_OUT_EN);
+ for (i = 0; i < cfg->speaker_outs; i++)
+ stac92xx_set_pinctl(codec, cfg->speaker_pins[i],
+ AC_PINCTL_OUT_EN);
}
}
+static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+ switch (res >> 26) {
+ case STAC_HP_EVENT:
+ stac92xx_hp_detect(codec, res);
+ break;
+ }
+}
+
#ifdef CONFIG_PM
static int stac92xx_resume(struct hda_codec *codec)
{
@@ -1119,6 +1563,7 @@ static int stac92xx_resume(struct hda_codec *codec)
int i;
stac92xx_init(codec);
+ stac92xx_set_config_regs(codec);
for (i = 0; i < spec->num_mixers; i++)
snd_hda_resume_ctls(codec, spec->mixers[i]);
if (spec->multiout.dig_out_nid)
@@ -1151,12 +1596,18 @@ static int patch_stac9200(struct hda_codec *codec)
return -ENOMEM;
codec->spec = spec;
+ spec->num_pins = 8;
+ spec->pin_nids = stac9200_pin_nids;
spec->board_config = snd_hda_check_board_config(codec, stac9200_cfg_tbl);
- if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9200, using BIOS defaults\n");
- else {
- spec->num_pins = 8;
- spec->pin_nids = stac9200_pin_nids;
+ if (spec->board_config < 0) {
+ snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9200, using BIOS defaults\n");
+ err = stac92xx_save_bios_config_regs(codec);
+ if (err < 0) {
+ stac92xx_free(codec);
+ return err;
+ }
+ spec->pin_configs = spec->bios_pin_configs;
+ } else {
spec->pin_configs = stac9200_brd_tbl[spec->board_config];
stac92xx_set_config_regs(codec);
}
@@ -1192,12 +1643,19 @@ static int patch_stac922x(struct hda_codec *codec)
return -ENOMEM;
codec->spec = spec;
+ spec->num_pins = 10;
+ spec->pin_nids = stac922x_pin_nids;
spec->board_config = snd_hda_check_board_config(codec, stac922x_cfg_tbl);
- if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC922x, using BIOS defaults\n");
- else if (stac922x_brd_tbl[spec->board_config] != NULL) {
- spec->num_pins = 10;
- spec->pin_nids = stac922x_pin_nids;
+ if (spec->board_config < 0) {
+ snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC922x, "
+ "using BIOS defaults\n");
+ err = stac92xx_save_bios_config_regs(codec);
+ if (err < 0) {
+ stac92xx_free(codec);
+ return err;
+ }
+ spec->pin_configs = spec->bios_pin_configs;
+ } else if (stac922x_brd_tbl[spec->board_config] != NULL) {
spec->pin_configs = stac922x_brd_tbl[spec->board_config];
stac92xx_set_config_regs(codec);
}
@@ -1210,7 +1668,7 @@ static int patch_stac922x(struct hda_codec *codec)
spec->mixer = stac922x_mixer;
spec->multiout.dac_nids = spec->dac_nids;
-
+
err = stac92xx_parse_auto_config(codec, 0x08, 0x09);
if (err < 0) {
stac92xx_free(codec);
@@ -1235,26 +1693,94 @@ static int patch_stac927x(struct hda_codec *codec)
return -ENOMEM;
codec->spec = spec;
+ spec->num_pins = 14;
+ spec->pin_nids = stac927x_pin_nids;
spec->board_config = snd_hda_check_board_config(codec, stac927x_cfg_tbl);
- if (spec->board_config < 0)
+ if (spec->board_config < 0) {
snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC927x, using BIOS defaults\n");
- else {
- spec->num_pins = 14;
- spec->pin_nids = stac927x_pin_nids;
+ err = stac92xx_save_bios_config_regs(codec);
+ if (err < 0) {
+ stac92xx_free(codec);
+ return err;
+ }
+ spec->pin_configs = spec->bios_pin_configs;
+ } else if (stac927x_brd_tbl[spec->board_config] != NULL) {
spec->pin_configs = stac927x_brd_tbl[spec->board_config];
stac92xx_set_config_regs(codec);
}
- spec->adc_nids = stac927x_adc_nids;
- spec->mux_nids = stac927x_mux_nids;
+ switch (spec->board_config) {
+ case STAC_D965_3ST:
+ spec->adc_nids = stac927x_adc_nids;
+ spec->mux_nids = stac927x_mux_nids;
+ spec->num_muxes = 3;
+ spec->init = d965_core_init;
+ spec->mixer = stac9227_mixer;
+ break;
+ case STAC_D965_5ST:
+ spec->adc_nids = stac927x_adc_nids;
+ spec->mux_nids = stac927x_mux_nids;
+ spec->num_muxes = 3;
+ spec->init = d965_core_init;
+ spec->mixer = stac9227_mixer;
+ break;
+ default:
+ spec->adc_nids = stac927x_adc_nids;
+ spec->mux_nids = stac927x_mux_nids;
+ spec->num_muxes = 3;
+ spec->init = stac927x_core_init;
+ spec->mixer = stac927x_mixer;
+ }
+
+ spec->multiout.dac_nids = spec->dac_nids;
+
+ err = stac92xx_parse_auto_config(codec, 0x1e, 0x20);
+ if (err < 0) {
+ stac92xx_free(codec);
+ return err;
+ }
+
+ codec->patch_ops = stac92xx_patch_ops;
+
+ return 0;
+}
+
+static int patch_stac9205(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec;
+ int err;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+ spec->num_pins = 14;
+ spec->pin_nids = stac9205_pin_nids;
+ spec->board_config = snd_hda_check_board_config(codec, stac9205_cfg_tbl);
+ if (spec->board_config < 0) {
+ snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9205, using BIOS defaults\n");
+ err = stac92xx_save_bios_config_regs(codec);
+ if (err < 0) {
+ stac92xx_free(codec);
+ return err;
+ }
+ spec->pin_configs = spec->bios_pin_configs;
+ } else {
+ spec->pin_configs = stac9205_brd_tbl[spec->board_config];
+ stac92xx_set_config_regs(codec);
+ }
+
+ spec->adc_nids = stac9205_adc_nids;
+ spec->mux_nids = stac9205_mux_nids;
spec->num_muxes = 3;
- spec->init = stac927x_core_init;
- spec->mixer = stac927x_mixer;
+ spec->init = stac9205_core_init;
+ spec->mixer = stac9205_mixer;
spec->multiout.dac_nids = spec->dac_nids;
- err = stac92xx_parse_auto_config(codec, 0x1e, 0x20);
+ err = stac92xx_parse_auto_config(codec, 0x1f, 0x20);
if (err < 0) {
stac92xx_free(codec);
return err;
@@ -1266,10 +1792,10 @@ static int patch_stac927x(struct hda_codec *codec)
}
/*
- * STAC 7661(?) hack
+ * STAC9872 hack
*/
-/* static config for Sony VAIO FE550G */
+/* static config for Sony VAIO FE550G and Sony VAIO AR */
static hda_nid_t vaio_dacs[] = { 0x2 };
#define VAIO_HP_DAC 0x5
static hda_nid_t vaio_adcs[] = { 0x8 /*,0x6*/ };
@@ -1300,6 +1826,23 @@ static struct hda_verb vaio_init[] = {
{}
};
+static struct hda_verb vaio_ar_init[] = {
+ {0x0a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* HP <- 0x2 */
+ {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Speaker <- 0x5 */
+ {0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? (<- 0x2) */
+ {0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD */
+/* {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },*/ /* Optical Out */
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? */
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x2}, /* mic-sel: 0a,0d,14,02 */
+ {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* HP */
+ {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Speaker */
+/* {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},*/ /* Optical Out */
+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* capture sw/vol -> 0x8 */
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* CD-in -> 0x6 */
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Mic-in -> 0x9 */
+ {}
+};
+
/* bind volumes of both NID 0x02 and 0x05 */
static int vaio_master_vol_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -1345,6 +1888,7 @@ static struct snd_kcontrol_new vaio_mixer[] = {
.info = snd_hda_mixer_amp_volume_info,
.get = snd_hda_mixer_amp_volume_get,
.put = vaio_master_vol_put,
+ .tlv = { .c = snd_hda_mixer_amp_tlv },
.private_value = HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
},
{
@@ -1369,7 +1913,40 @@ static struct snd_kcontrol_new vaio_mixer[] = {
{}
};
-static struct hda_codec_ops stac7661_patch_ops = {
+static struct snd_kcontrol_new vaio_ar_mixer[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Volume",
+ .info = snd_hda_mixer_amp_volume_info,
+ .get = snd_hda_mixer_amp_volume_get,
+ .put = vaio_master_vol_put,
+ .private_value = HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = snd_hda_mixer_amp_switch_info,
+ .get = snd_hda_mixer_amp_switch_get,
+ .put = vaio_master_sw_put,
+ .private_value = HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
+ },
+ /* HDA_CODEC_VOLUME("CD Capture Volume", 0x07, 0, HDA_INPUT), */
+ HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT),
+ /*HDA_CODEC_MUTE("Optical Out Switch", 0x10, 0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Optical Out Volume", 0x10, 0, HDA_OUTPUT),*/
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .count = 1,
+ .info = stac92xx_mux_enum_info,
+ .get = stac92xx_mux_enum_get,
+ .put = stac92xx_mux_enum_put,
+ },
+ {}
+};
+
+static struct hda_codec_ops stac9872_patch_ops = {
.build_controls = stac92xx_build_controls,
.build_pcms = stac92xx_build_pcms,
.init = stac92xx_init,
@@ -1379,23 +1956,34 @@ static struct hda_codec_ops stac7661_patch_ops = {
#endif
};
-enum { STAC7661_VAIO };
-
-static struct hda_board_config stac7661_cfg_tbl[] = {
- { .modelname = "vaio", .config = STAC7661_VAIO },
+enum { /* FE and SZ series. id=0x83847661 and subsys=0x104D0700 or 104D1000. */
+ CXD9872RD_VAIO,
+ /* Unknown. id=0x83847662 and subsys=0x104D1200 or 104D1000. */
+ STAC9872AK_VAIO,
+ /* Unknown. id=0x83847661 and subsys=0x104D1200. */
+ STAC9872K_VAIO,
+ /* AR Series. id=0x83847664 and subsys=104D1300 */
+ CXD9872AKD_VAIO
+ };
+
+static struct hda_board_config stac9872_cfg_tbl[] = {
+ { .modelname = "vaio", .config = CXD9872RD_VAIO },
+ { .modelname = "vaio-ar", .config = CXD9872AKD_VAIO },
{ .pci_subvendor = 0x104d, .pci_subdevice = 0x81e6,
- .config = STAC7661_VAIO },
+ .config = CXD9872RD_VAIO },
{ .pci_subvendor = 0x104d, .pci_subdevice = 0x81ef,
- .config = STAC7661_VAIO },
+ .config = CXD9872RD_VAIO },
+ { .pci_subvendor = 0x104d, .pci_subdevice = 0x81fd,
+ .config = CXD9872AKD_VAIO },
{}
};
-static int patch_stac7661(struct hda_codec *codec)
+static int patch_stac9872(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
int board_config;
- board_config = snd_hda_check_board_config(codec, stac7661_cfg_tbl);
+ board_config = snd_hda_check_board_config(codec, stac9872_cfg_tbl);
if (board_config < 0)
/* unknown config, let generic-parser do its job... */
return snd_hda_parse_generic_codec(codec);
@@ -1406,7 +1994,9 @@ static int patch_stac7661(struct hda_codec *codec)
codec->spec = spec;
switch (board_config) {
- case STAC7661_VAIO:
+ case CXD9872RD_VAIO:
+ case STAC9872AK_VAIO:
+ case STAC9872K_VAIO:
spec->mixer = vaio_mixer;
spec->init = vaio_init;
spec->multiout.max_channels = 2;
@@ -1418,9 +2008,22 @@ static int patch_stac7661(struct hda_codec *codec)
spec->input_mux = &vaio_mux;
spec->mux_nids = vaio_mux_nids;
break;
+
+ case CXD9872AKD_VAIO:
+ spec->mixer = vaio_ar_mixer;
+ spec->init = vaio_ar_init;
+ spec->multiout.max_channels = 2;
+ spec->multiout.num_dacs = ARRAY_SIZE(vaio_dacs);
+ spec->multiout.dac_nids = vaio_dacs;
+ spec->multiout.hp_nid = VAIO_HP_DAC;
+ spec->num_adcs = ARRAY_SIZE(vaio_adcs);
+ spec->adc_nids = vaio_adcs;
+ spec->input_mux = &vaio_mux;
+ spec->mux_nids = vaio_mux_nids;
+ break;
}
- codec->patch_ops = stac7661_patch_ops;
+ codec->patch_ops = stac9872_patch_ops;
return 0;
}
@@ -1436,12 +2039,12 @@ struct hda_codec_preset snd_hda_preset_sigmatel[] = {
{ .id = 0x83847681, .name = "STAC9220D/9223D A2", .patch = patch_stac922x },
{ .id = 0x83847682, .name = "STAC9221 A2", .patch = patch_stac922x },
{ .id = 0x83847683, .name = "STAC9221D A2", .patch = patch_stac922x },
- { .id = 0x83847618, .name = "STAC9227", .patch = patch_stac922x },
- { .id = 0x83847619, .name = "STAC9227", .patch = patch_stac922x },
- { .id = 0x83847616, .name = "STAC9228", .patch = patch_stac922x },
- { .id = 0x83847617, .name = "STAC9228", .patch = patch_stac922x },
- { .id = 0x83847614, .name = "STAC9229", .patch = patch_stac922x },
- { .id = 0x83847615, .name = "STAC9229", .patch = patch_stac922x },
+ { .id = 0x83847618, .name = "STAC9227", .patch = patch_stac927x },
+ { .id = 0x83847619, .name = "STAC9227", .patch = patch_stac927x },
+ { .id = 0x83847616, .name = "STAC9228", .patch = patch_stac927x },
+ { .id = 0x83847617, .name = "STAC9228", .patch = patch_stac927x },
+ { .id = 0x83847614, .name = "STAC9229", .patch = patch_stac927x },
+ { .id = 0x83847615, .name = "STAC9229", .patch = patch_stac927x },
{ .id = 0x83847620, .name = "STAC9274", .patch = patch_stac927x },
{ .id = 0x83847621, .name = "STAC9274D", .patch = patch_stac927x },
{ .id = 0x83847622, .name = "STAC9273X", .patch = patch_stac927x },
@@ -1452,6 +2055,20 @@ struct hda_codec_preset snd_hda_preset_sigmatel[] = {
{ .id = 0x83847627, .name = "STAC9271D", .patch = patch_stac927x },
{ .id = 0x83847628, .name = "STAC9274X5NH", .patch = patch_stac927x },
{ .id = 0x83847629, .name = "STAC9274D5NH", .patch = patch_stac927x },
- { .id = 0x83847661, .name = "STAC7661", .patch = patch_stac7661 },
+ /* The following does not take into account .id=0x83847661 when subsys =
+ * 104D0C00 which is STAC9225s. Because of this, some SZ Notebooks are
+ * currently not fully supported.
+ */
+ { .id = 0x83847661, .name = "CXD9872RD/K", .patch = patch_stac9872 },
+ { .id = 0x83847662, .name = "STAC9872AK", .patch = patch_stac9872 },
+ { .id = 0x83847664, .name = "CXD9872AKD", .patch = patch_stac9872 },
+ { .id = 0x838476a0, .name = "STAC9205", .patch = patch_stac9205 },
+ { .id = 0x838476a1, .name = "STAC9205D", .patch = patch_stac9205 },
+ { .id = 0x838476a2, .name = "STAC9204", .patch = patch_stac9205 },
+ { .id = 0x838476a3, .name = "STAC9204D", .patch = patch_stac9205 },
+ { .id = 0x838476a4, .name = "STAC9255", .patch = patch_stac9205 },
+ { .id = 0x838476a5, .name = "STAC9255D", .patch = patch_stac9205 },
+ { .id = 0x838476a6, .name = "STAC9254", .patch = patch_stac9205 },
+ { .id = 0x838476a7, .name = "STAC9254D", .patch = patch_stac9205 },
{} /* terminator */
};
diff --git a/sound/pci/ice1712/aureon.c b/sound/pci/ice1712/aureon.c
index ca74f5b85f42..9e76cebd2d22 100644
--- a/sound/pci/ice1712/aureon.c
+++ b/sound/pci/ice1712/aureon.c
@@ -60,6 +60,7 @@
#include "ice1712.h"
#include "envy24ht.h"
#include "aureon.h"
+#include <sound/tlv.h>
/* WM8770 registers */
#define WM_DAC_ATTEN 0x00 /* DAC1-8 analog attenuation */
@@ -660,6 +661,12 @@ static int aureon_ac97_mmute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
return change;
}
+static DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1);
+static DECLARE_TLV_DB_SCALE(db_scale_wm_pcm, -6400, 50, 1);
+static DECLARE_TLV_DB_SCALE(db_scale_wm_adc, -1200, 100, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_ac97_master, -4650, 150, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_ac97_gain, -3450, 150, 0);
+
/*
* Logarithmic volume values for WM8770
* Computed as 20 * Log10(255 / x)
@@ -1409,10 +1416,13 @@ static struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Master Playback Volume",
.info = wm_master_vol_info,
.get = wm_master_vol_get,
- .put = wm_master_vol_put
+ .put = wm_master_vol_put,
+ .tlv = { .p = db_scale_wm_dac }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1424,11 +1434,14 @@ static struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Front Playback Volume",
.info = wm_vol_info,
.get = wm_vol_get,
.put = wm_vol_put,
- .private_value = (2 << 8) | 0
+ .private_value = (2 << 8) | 0,
+ .tlv = { .p = db_scale_wm_dac }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1440,11 +1453,14 @@ static struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Rear Playback Volume",
.info = wm_vol_info,
.get = wm_vol_get,
.put = wm_vol_put,
- .private_value = (2 << 8) | 2
+ .private_value = (2 << 8) | 2,
+ .tlv = { .p = db_scale_wm_dac }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1456,11 +1472,14 @@ static struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Center Playback Volume",
.info = wm_vol_info,
.get = wm_vol_get,
.put = wm_vol_put,
- .private_value = (1 << 8) | 4
+ .private_value = (1 << 8) | 4,
+ .tlv = { .p = db_scale_wm_dac }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1472,11 +1491,14 @@ static struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "LFE Playback Volume",
.info = wm_vol_info,
.get = wm_vol_get,
.put = wm_vol_put,
- .private_value = (1 << 8) | 5
+ .private_value = (1 << 8) | 5,
+ .tlv = { .p = db_scale_wm_dac }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1488,11 +1510,14 @@ static struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Side Playback Volume",
.info = wm_vol_info,
.get = wm_vol_get,
.put = wm_vol_put,
- .private_value = (2 << 8) | 6
+ .private_value = (2 << 8) | 6,
+ .tlv = { .p = db_scale_wm_dac }
}
};
@@ -1506,10 +1531,13 @@ static struct snd_kcontrol_new wm_controls[] __devinitdata = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "PCM Playback Volume",
.info = wm_pcm_vol_info,
.get = wm_pcm_vol_get,
- .put = wm_pcm_vol_put
+ .put = wm_pcm_vol_put,
+ .tlv = { .p = db_scale_wm_pcm }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1520,10 +1548,13 @@ static struct snd_kcontrol_new wm_controls[] __devinitdata = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Capture Volume",
.info = wm_adc_vol_info,
.get = wm_adc_vol_get,
- .put = wm_adc_vol_put
+ .put = wm_adc_vol_put,
+ .tlv = { .p = db_scale_wm_adc }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1567,11 +1598,14 @@ static struct snd_kcontrol_new ac97_controls[] __devinitdata = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "AC97 Playback Volume",
.info = aureon_ac97_vol_info,
.get = aureon_ac97_vol_get,
.put = aureon_ac97_vol_put,
- .private_value = AC97_MASTER|AUREON_AC97_STEREO
+ .private_value = AC97_MASTER|AUREON_AC97_STEREO,
+ .tlv = { .p = db_scale_ac97_master }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1583,11 +1617,14 @@ static struct snd_kcontrol_new ac97_controls[] __devinitdata = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "CD Playback Volume",
.info = aureon_ac97_vol_info,
.get = aureon_ac97_vol_get,
.put = aureon_ac97_vol_put,
- .private_value = AC97_CD|AUREON_AC97_STEREO
+ .private_value = AC97_CD|AUREON_AC97_STEREO,
+ .tlv = { .p = db_scale_ac97_gain }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1599,11 +1636,14 @@ static struct snd_kcontrol_new ac97_controls[] __devinitdata = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Aux Playback Volume",
.info = aureon_ac97_vol_info,
.get = aureon_ac97_vol_get,
.put = aureon_ac97_vol_put,
- .private_value = AC97_AUX|AUREON_AC97_STEREO
+ .private_value = AC97_AUX|AUREON_AC97_STEREO,
+ .tlv = { .p = db_scale_ac97_gain }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1615,11 +1655,14 @@ static struct snd_kcontrol_new ac97_controls[] __devinitdata = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Line Playback Volume",
.info = aureon_ac97_vol_info,
.get = aureon_ac97_vol_get,
.put = aureon_ac97_vol_put,
- .private_value = AC97_LINE|AUREON_AC97_STEREO
+ .private_value = AC97_LINE|AUREON_AC97_STEREO,
+ .tlv = { .p = db_scale_ac97_gain }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1631,11 +1674,14 @@ static struct snd_kcontrol_new ac97_controls[] __devinitdata = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Mic Playback Volume",
.info = aureon_ac97_vol_info,
.get = aureon_ac97_vol_get,
.put = aureon_ac97_vol_put,
- .private_value = AC97_MIC
+ .private_value = AC97_MIC,
+ .tlv = { .p = db_scale_ac97_gain }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1657,11 +1703,14 @@ static struct snd_kcontrol_new universe_ac97_controls[] __devinitdata = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "AC97 Playback Volume",
.info = aureon_ac97_vol_info,
.get = aureon_ac97_vol_get,
.put = aureon_ac97_vol_put,
- .private_value = AC97_MASTER|AUREON_AC97_STEREO
+ .private_value = AC97_MASTER|AUREON_AC97_STEREO,
+ .tlv = { .p = db_scale_ac97_master }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1673,11 +1722,14 @@ static struct snd_kcontrol_new universe_ac97_controls[] __devinitdata = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "CD Playback Volume",
.info = aureon_ac97_vol_info,
.get = aureon_ac97_vol_get,
.put = aureon_ac97_vol_put,
- .private_value = AC97_AUX|AUREON_AC97_STEREO
+ .private_value = AC97_AUX|AUREON_AC97_STEREO,
+ .tlv = { .p = db_scale_ac97_gain }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1685,15 +1737,18 @@ static struct snd_kcontrol_new universe_ac97_controls[] __devinitdata = {
.info = aureon_ac97_mute_info,
.get = aureon_ac97_mute_get,
.put = aureon_ac97_mute_put,
- .private_value = AC97_CD,
+ .private_value = AC97_CD
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Phono Playback Volume",
.info = aureon_ac97_vol_info,
.get = aureon_ac97_vol_get,
.put = aureon_ac97_vol_put,
- .private_value = AC97_CD|AUREON_AC97_STEREO
+ .private_value = AC97_CD|AUREON_AC97_STEREO,
+ .tlv = { .p = db_scale_ac97_gain }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1705,11 +1760,14 @@ static struct snd_kcontrol_new universe_ac97_controls[] __devinitdata = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Line Playback Volume",
.info = aureon_ac97_vol_info,
.get = aureon_ac97_vol_get,
.put = aureon_ac97_vol_put,
- .private_value = AC97_LINE|AUREON_AC97_STEREO
+ .private_value = AC97_LINE|AUREON_AC97_STEREO,
+ .tlv = { .p = db_scale_ac97_gain }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1721,11 +1779,14 @@ static struct snd_kcontrol_new universe_ac97_controls[] __devinitdata = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Mic Playback Volume",
.info = aureon_ac97_vol_info,
.get = aureon_ac97_vol_get,
.put = aureon_ac97_vol_put,
- .private_value = AC97_MIC
+ .private_value = AC97_MIC,
+ .tlv = { .p = db_scale_ac97_gain }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1744,11 +1805,14 @@ static struct snd_kcontrol_new universe_ac97_controls[] __devinitdata = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Aux Playback Volume",
.info = aureon_ac97_vol_info,
.get = aureon_ac97_vol_get,
.put = aureon_ac97_vol_put,
- .private_value = AC97_VIDEO|AUREON_AC97_STEREO
+ .private_value = AC97_VIDEO|AUREON_AC97_STEREO,
+ .tlv = { .p = db_scale_ac97_gain }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -2131,7 +2195,7 @@ struct snd_ice1712_card_info snd_vt1724_aureon_cards[] __devinitdata = {
.build_controls = aureon_add_controls,
.eeprom_size = sizeof(aureon71_eeprom),
.eeprom_data = aureon71_eeprom,
- .driver = "Aureon71Universe",
+ .driver = "Aureon71Univ", /* keep in 15 letters */
},
{
.subvendor = VT1724_SUBDEVICE_PRODIGY71,
diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c
index 845907159b74..dc69392eafa3 100644
--- a/sound/pci/ice1712/ice1712.c
+++ b/sound/pci/ice1712/ice1712.c
@@ -62,6 +62,7 @@
#include <sound/cs8427.h>
#include <sound/info.h>
#include <sound/initval.h>
+#include <sound/tlv.h>
#include <sound/asoundef.h>
@@ -106,7 +107,7 @@ module_param_array(dxr_enable, int, NULL, 0444);
MODULE_PARM_DESC(dxr_enable, "Enable DXR support for Terratec DMX6FIRE.");
-static struct pci_device_id snd_ice1712_ids[] __devinitdata = {
+static struct pci_device_id snd_ice1712_ids[] = {
{ PCI_VENDOR_ID_ICE, PCI_DEVICE_ID_ICE_1712, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ICE1712 */
{ 0, }
};
@@ -1377,6 +1378,7 @@ static int snd_ice1712_pro_mixer_volume_put(struct snd_kcontrol *kcontrol, struc
return change;
}
+static DECLARE_TLV_DB_SCALE(db_scale_playback, -14400, 150, 0);
static struct snd_kcontrol_new snd_ice1712_multi_playback_ctrls[] __devinitdata = {
{
@@ -1390,12 +1392,15 @@ static struct snd_kcontrol_new snd_ice1712_multi_playback_ctrls[] __devinitdata
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Multi Playback Volume",
.info = snd_ice1712_pro_mixer_volume_info,
.get = snd_ice1712_pro_mixer_volume_get,
.put = snd_ice1712_pro_mixer_volume_put,
.private_value = 0,
.count = 10,
+ .tlv = { .p = db_scale_playback }
},
};
@@ -1420,11 +1425,14 @@ static struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_switch __devinitd
static struct snd_kcontrol_new snd_ice1712_multi_capture_analog_volume __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "H/W Multi Capture Volume",
.info = snd_ice1712_pro_mixer_volume_info,
.get = snd_ice1712_pro_mixer_volume_get,
.put = snd_ice1712_pro_mixer_volume_put,
.private_value = 10,
+ .tlv = { .p = db_scale_playback }
};
static struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_volume __devinitdata = {
@@ -1857,7 +1865,7 @@ static int snd_ice1712_pro_internal_clock_put(struct snd_kcontrol *kcontrol,
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
static unsigned int xrate[13] = {
- 8000, 9600, 11025, 12000, 1600, 22050, 24000,
+ 8000, 9600, 11025, 12000, 16000, 22050, 24000,
32000, 44100, 48000, 64000, 88200, 96000
};
unsigned char oval;
@@ -1924,7 +1932,7 @@ static int snd_ice1712_pro_internal_clock_default_get(struct snd_kcontrol *kcont
{
int val;
static unsigned int xrate[13] = {
- 8000, 9600, 11025, 12000, 1600, 22050, 24000,
+ 8000, 9600, 11025, 12000, 16000, 22050, 24000,
32000, 44100, 48000, 64000, 88200, 96000
};
@@ -1941,7 +1949,7 @@ static int snd_ice1712_pro_internal_clock_default_put(struct snd_kcontrol *kcont
struct snd_ctl_elem_value *ucontrol)
{
static unsigned int xrate[13] = {
- 8000, 9600, 11025, 12000, 1600, 22050, 24000,
+ 8000, 9600, 11025, 12000, 16000, 22050, 24000,
32000, 44100, 48000, 64000, 88200, 96000
};
unsigned char oval;
@@ -2606,7 +2614,7 @@ static int __devinit snd_ice1712_create(struct snd_card *card,
ice->dmapath_port = pci_resource_start(pci, 2);
ice->profi_port = pci_resource_start(pci, 3);
- if (request_irq(pci->irq, snd_ice1712_interrupt, SA_INTERRUPT|SA_SHIRQ,
+ if (request_irq(pci->irq, snd_ice1712_interrupt, IRQF_DISABLED|IRQF_SHARED,
"ICE1712", ice)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_ice1712_free(ice);
diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c
index 34a58c629f47..71d6aedc0749 100644
--- a/sound/pci/ice1712/ice1724.c
+++ b/sound/pci/ice1712/ice1724.c
@@ -86,7 +86,7 @@ MODULE_PARM_DESC(model, "Use the given board model.");
/* Both VT1720 and VT1724 have the same PCI IDs */
-static struct pci_device_id snd_vt1724_ids[] __devinitdata = {
+static struct pci_device_id snd_vt1724_ids[] = {
{ PCI_VENDOR_ID_ICE, PCI_DEVICE_ID_VT1724, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
{ 0, }
};
@@ -2253,7 +2253,7 @@ static int __devinit snd_vt1724_create(struct snd_card *card,
ice->profi_port = pci_resource_start(pci, 1);
if (request_irq(pci->irq, snd_vt1724_interrupt,
- SA_INTERRUPT|SA_SHIRQ, "ICE1724", ice)) {
+ IRQF_DISABLED|IRQF_SHARED, "ICE1724", ice)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_vt1724_free(ice);
return -EIO;
diff --git a/sound/pci/ice1712/phase.c b/sound/pci/ice1712/phase.c
index 502da1c8b5f7..e08d73f4ff85 100644
--- a/sound/pci/ice1712/phase.c
+++ b/sound/pci/ice1712/phase.c
@@ -46,6 +46,7 @@
#include "ice1712.h"
#include "envy24ht.h"
#include "phase.h"
+#include <sound/tlv.h>
/* WM8770 registers */
#define WM_DAC_ATTEN 0x00 /* DAC1-8 analog attenuation */
@@ -696,6 +697,9 @@ static int phase28_oversampling_put(struct snd_kcontrol *kcontrol, struct snd_ct
return 0;
}
+static DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1);
+static DECLARE_TLV_DB_SCALE(db_scale_wm_pcm, -6400, 50, 1);
+
static struct snd_kcontrol_new phase28_dac_controls[] __devinitdata = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -706,10 +710,13 @@ static struct snd_kcontrol_new phase28_dac_controls[] __devinitdata = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Master Playback Volume",
.info = wm_master_vol_info,
.get = wm_master_vol_get,
- .put = wm_master_vol_put
+ .put = wm_master_vol_put,
+ .tlv = { .p = db_scale_wm_dac }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -721,11 +728,14 @@ static struct snd_kcontrol_new phase28_dac_controls[] __devinitdata = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Front Playback Volume",
.info = wm_vol_info,
.get = wm_vol_get,
.put = wm_vol_put,
- .private_value = (2 << 8) | 0
+ .private_value = (2 << 8) | 0,
+ .tlv = { .p = db_scale_wm_dac }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -737,11 +747,14 @@ static struct snd_kcontrol_new phase28_dac_controls[] __devinitdata = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Rear Playback Volume",
.info = wm_vol_info,
.get = wm_vol_get,
.put = wm_vol_put,
- .private_value = (2 << 8) | 2
+ .private_value = (2 << 8) | 2,
+ .tlv = { .p = db_scale_wm_dac }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -753,11 +766,14 @@ static struct snd_kcontrol_new phase28_dac_controls[] __devinitdata = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Center Playback Volume",
.info = wm_vol_info,
.get = wm_vol_get,
.put = wm_vol_put,
- .private_value = (1 << 8) | 4
+ .private_value = (1 << 8) | 4,
+ .tlv = { .p = db_scale_wm_dac }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -769,11 +785,14 @@ static struct snd_kcontrol_new phase28_dac_controls[] __devinitdata = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "LFE Playback Volume",
.info = wm_vol_info,
.get = wm_vol_get,
.put = wm_vol_put,
- .private_value = (1 << 8) | 5
+ .private_value = (1 << 8) | 5,
+ .tlv = { .p = db_scale_wm_dac }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -785,11 +804,14 @@ static struct snd_kcontrol_new phase28_dac_controls[] __devinitdata = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Side Playback Volume",
.info = wm_vol_info,
.get = wm_vol_get,
.put = wm_vol_put,
- .private_value = (2 << 8) | 6
+ .private_value = (2 << 8) | 6,
+ .tlv = { .p = db_scale_wm_dac }
}
};
@@ -803,10 +825,13 @@ static struct snd_kcontrol_new wm_controls[] __devinitdata = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "PCM Playback Volume",
.info = wm_pcm_vol_info,
.get = wm_pcm_vol_get,
- .put = wm_pcm_vol_put
+ .put = wm_pcm_vol_put,
+ .tlv = { .p = db_scale_wm_pcm }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
diff --git a/sound/pci/ice1712/pontis.c b/sound/pci/ice1712/pontis.c
index 0efcad9260a5..6c74c2d2e7f3 100644
--- a/sound/pci/ice1712/pontis.c
+++ b/sound/pci/ice1712/pontis.c
@@ -31,6 +31,7 @@
#include <sound/core.h>
#include <sound/info.h>
+#include <sound/tlv.h>
#include "ice1712.h"
#include "envy24ht.h"
@@ -564,6 +565,8 @@ static int pontis_gpio_data_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
return changed;
}
+static DECLARE_TLV_DB_SCALE(db_scale_volume, -6400, 50, 1);
+
/*
* mixers
*/
@@ -571,17 +574,23 @@ static int pontis_gpio_data_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
static struct snd_kcontrol_new pontis_controls[] __devinitdata = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "PCM Playback Volume",
.info = wm_dac_vol_info,
.get = wm_dac_vol_get,
.put = wm_dac_vol_put,
+ .tlv = { .p = db_scale_volume },
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Capture Volume",
.info = wm_adc_vol_info,
.get = wm_adc_vol_get,
.put = wm_adc_vol_put,
+ .tlv = { .p = db_scale_volume },
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
diff --git a/sound/pci/ice1712/prodigy192.c b/sound/pci/ice1712/prodigy192.c
index fdb5cb8fac97..41b2605daa3a 100644
--- a/sound/pci/ice1712/prodigy192.c
+++ b/sound/pci/ice1712/prodigy192.c
@@ -35,6 +35,7 @@
#include "envy24ht.h"
#include "prodigy192.h"
#include "stac946x.h"
+#include <sound/tlv.h>
static inline void stac9460_put(struct snd_ice1712 *ice, int reg, unsigned char val)
{
@@ -356,6 +357,9 @@ static int aureon_oversampling_put(struct snd_kcontrol *kcontrol, struct snd_ctl
}
#endif
+static DECLARE_TLV_DB_SCALE(db_scale_dac, -19125, 75, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_adc, 0, 150, 0);
+
/*
* mixers
*/
@@ -368,14 +372,18 @@ static struct snd_kcontrol_new stac_controls[] __devinitdata = {
.get = stac9460_dac_mute_get,
.put = stac9460_dac_mute_put,
.private_value = 1,
+ .tlv = { .p = db_scale_dac }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Master Playback Volume",
.info = stac9460_dac_vol_info,
.get = stac9460_dac_vol_get,
.put = stac9460_dac_vol_put,
.private_value = 1,
+ .tlv = { .p = db_scale_dac }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -387,11 +395,14 @@ static struct snd_kcontrol_new stac_controls[] __devinitdata = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "DAC Volume",
.count = 6,
.info = stac9460_dac_vol_info,
.get = stac9460_dac_vol_get,
.put = stac9460_dac_vol_put,
+ .tlv = { .p = db_scale_dac }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -404,11 +415,14 @@ static struct snd_kcontrol_new stac_controls[] __devinitdata = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "ADC Volume",
.count = 1,
.info = stac9460_adc_vol_info,
.get = stac9460_adc_vol_get,
.put = stac9460_adc_vol_put,
+ .tlv = { .p = db_scale_adc }
},
#if 0
{
diff --git a/sound/pci/ice1712/revo.c b/sound/pci/ice1712/revo.c
index b5754b32b802..bf98ea34feb0 100644
--- a/sound/pci/ice1712/revo.c
+++ b/sound/pci/ice1712/revo.c
@@ -87,12 +87,41 @@ static void revo_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate)
* initialize the chips on M-Audio Revolution cards
*/
+#define AK_DAC(xname,xch) { .name = xname, .num_channels = xch }
+
+static struct snd_akm4xxx_dac_channel revo71_front[] = {
+ AK_DAC("PCM Playback Volume", 2)
+};
+
+static struct snd_akm4xxx_dac_channel revo71_surround[] = {
+ AK_DAC("PCM Center Playback Volume", 1),
+ AK_DAC("PCM LFE Playback Volume", 1),
+ AK_DAC("PCM Side Playback Volume", 2),
+ AK_DAC("PCM Rear Playback Volume", 2),
+};
+
+static struct snd_akm4xxx_dac_channel revo51_dac[] = {
+ AK_DAC("PCM Playback Volume", 2),
+ AK_DAC("PCM Center Playback Volume", 1),
+ AK_DAC("PCM LFE Playback Volume", 1),
+ AK_DAC("PCM Rear Playback Volume", 2),
+};
+
+static struct snd_akm4xxx_adc_channel revo51_adc[] = {
+ {
+ .name = "PCM Capture Volume",
+ .switch_name = "PCM Capture Switch",
+ .num_channels = 2
+ },
+};
+
static struct snd_akm4xxx akm_revo_front __devinitdata = {
.type = SND_AK4381,
.num_dacs = 2,
.ops = {
.set_rate_val = revo_set_rate_val
- }
+ },
+ .dac_info = revo71_front,
};
static struct snd_ak4xxx_private akm_revo_front_priv __devinitdata = {
@@ -113,7 +142,8 @@ static struct snd_akm4xxx akm_revo_surround __devinitdata = {
.num_dacs = 6,
.ops = {
.set_rate_val = revo_set_rate_val
- }
+ },
+ .dac_info = revo71_surround,
};
static struct snd_ak4xxx_private akm_revo_surround_priv __devinitdata = {
@@ -133,7 +163,8 @@ static struct snd_akm4xxx akm_revo51 __devinitdata = {
.num_dacs = 6,
.ops = {
.set_rate_val = revo_set_rate_val
- }
+ },
+ .dac_info = revo51_dac,
};
static struct snd_ak4xxx_private akm_revo51_priv __devinitdata = {
@@ -142,7 +173,25 @@ static struct snd_ak4xxx_private akm_revo51_priv __devinitdata = {
.data_mask = VT1724_REVO_CDOUT,
.clk_mask = VT1724_REVO_CCLK,
.cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2,
- .cs_addr = 0,
+ .cs_addr = VT1724_REVO_CS1 | VT1724_REVO_CS2,
+ .cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2,
+ .add_flags = VT1724_REVO_CCLK, /* high at init */
+ .mask_flags = 0,
+};
+
+static struct snd_akm4xxx akm_revo51_adc __devinitdata = {
+ .type = SND_AK5365,
+ .num_adcs = 2,
+ .adc_info = revo51_adc,
+};
+
+static struct snd_ak4xxx_private akm_revo51_adc_priv __devinitdata = {
+ .caddr = 2,
+ .cif = 0,
+ .data_mask = VT1724_REVO_CDOUT,
+ .clk_mask = VT1724_REVO_CCLK,
+ .cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2,
+ .cs_addr = VT1724_REVO_CS0 | VT1724_REVO_CS2,
.cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2,
.add_flags = VT1724_REVO_CCLK, /* high at init */
.mask_flags = 0,
@@ -185,9 +234,13 @@ static int __devinit revo_init(struct snd_ice1712 *ice)
snd_ice1712_gpio_write_bits(ice, VT1724_REVO_MUTE, VT1724_REVO_MUTE);
break;
case VT1724_SUBDEVICE_REVOLUTION51:
- ice->akm_codecs = 1;
+ ice->akm_codecs = 2;
if ((err = snd_ice1712_akm4xxx_init(ak, &akm_revo51, &akm_revo51_priv, ice)) < 0)
return err;
+ err = snd_ice1712_akm4xxx_init(ak + 1, &akm_revo51_adc,
+ &akm_revo51_adc_priv, ice);
+ if (err < 0)
+ return err;
/* unmute all codecs - needed! */
snd_ice1712_gpio_write_bits(ice, VT1724_REVO_MUTE, VT1724_REVO_MUTE);
break;
diff --git a/sound/pci/ice1712/revo.h b/sound/pci/ice1712/revo.h
index dea52ea219df..efbb86ec3289 100644
--- a/sound/pci/ice1712/revo.h
+++ b/sound/pci/ice1712/revo.h
@@ -42,7 +42,7 @@ extern struct snd_ice1712_card_info snd_vt1724_revo_cards[];
#define VT1724_REVO_CCLK 0x02
#define VT1724_REVO_CDIN 0x04 /* not used */
#define VT1724_REVO_CDOUT 0x08
-#define VT1724_REVO_CS0 0x10 /* not used */
+#define VT1724_REVO_CS0 0x10 /* AK5365 chipselect for Rev. 5.1 */
#define VT1724_REVO_CS1 0x20 /* front AKM4381 chipselect */
#define VT1724_REVO_CS2 0x40 /* surround AKM4355 chipselect */
#define VT1724_REVO_MUTE (1<<22) /* 0 = all mute, 1 = normal operation */
diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c
index edc14475ef82..72dbaedcbdf5 100644
--- a/sound/pci/intel8x0.c
+++ b/sound/pci/intel8x0.c
@@ -413,7 +413,7 @@ struct intel8x0 {
u32 int_sta_mask; /* interrupt status mask */
};
-static struct pci_device_id snd_intel8x0_ids[] __devinitdata = {
+static struct pci_device_id snd_intel8x0_ids[] = {
{ 0x8086, 0x2415, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 82801AA */
{ 0x8086, 0x2425, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 82901AB */
{ 0x8086, 0x2445, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 82801BA */
@@ -1956,6 +1956,12 @@ static struct ac97_quirk ac97_quirks[] __devinitdata = {
.type = AC97_TUNE_HP_ONLY
},
{
+ .subvendor = 0x10f1,
+ .subdevice = 0x2895,
+ .name = "Tyan Thunder K8WE",
+ .type = AC97_TUNE_HP_ONLY
+ },
+ {
.subvendor = 0x110a,
.subdevice = 0x0056,
.name = "Fujitsu-Siemens Scenic", /* AD1981? */
@@ -2245,6 +2251,16 @@ static int snd_intel8x0_ich_chip_init(struct intel8x0 *chip, int probing)
/* ACLink on, 2 channels */
cnt = igetdword(chip, ICHREG(GLOB_CNT));
cnt &= ~(ICH_ACLINK | ICH_PCM_246_MASK);
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+ /* do cold reset - the full ac97 powerdown may leave the controller
+ * in a warm state but actually it cannot communicate with the codec.
+ */
+ iputdword(chip, ICHREG(GLOB_CNT), cnt & ~ICH_AC97COLD);
+ cnt = igetdword(chip, ICHREG(GLOB_CNT));
+ udelay(10);
+ iputdword(chip, ICHREG(GLOB_CNT), cnt | ICH_AC97COLD);
+ msleep(1);
+#else
/* finish cold or do warm reset */
cnt |= (cnt & ICH_AC97COLD) == 0 ? ICH_AC97COLD : ICH_AC97WARM;
iputdword(chip, ICHREG(GLOB_CNT), cnt);
@@ -2259,6 +2275,7 @@ static int snd_intel8x0_ich_chip_init(struct intel8x0 *chip, int probing)
return -EIO;
__ok:
+#endif
if (probing) {
/* wait for any codec ready status.
* Once it becomes ready it should remain ready
@@ -2475,11 +2492,11 @@ static int intel8x0_resume(struct pci_dev *pci)
pci_restore_state(pci);
pci_enable_device(pci);
pci_set_master(pci);
- request_irq(pci->irq, snd_intel8x0_interrupt, SA_INTERRUPT|SA_SHIRQ,
+ request_irq(pci->irq, snd_intel8x0_interrupt, IRQF_DISABLED|IRQF_SHARED,
card->shortname, chip);
chip->irq = pci->irq;
synchronize_irq(chip->irq);
- snd_intel8x0_chip_init(chip, 1);
+ snd_intel8x0_chip_init(chip, 0);
/* re-initialize mixer stuff */
if (chip->device_type == DEVICE_INTEL_ICH4) {
@@ -2609,6 +2626,7 @@ static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip)
/* not 48000Hz, tuning the clock.. */
chip->ac97_bus->clock = (chip->ac97_bus->clock * 48000) / pos;
printk(KERN_INFO "intel8x0: clocking to %d\n", chip->ac97_bus->clock);
+ snd_ac97_update_power(chip->ac97[0], AC97_PCM_FRONT_DAC_RATE, 0);
}
#ifdef CONFIG_PROC_FS
@@ -2848,7 +2866,7 @@ static int __devinit snd_intel8x0_create(struct snd_card *card,
/* request irq after initializaing int_sta_mask, etc */
if (request_irq(pci->irq, snd_intel8x0_interrupt,
- SA_INTERRUPT|SA_SHIRQ, card->shortname, chip)) {
+ IRQF_DISABLED|IRQF_SHARED, card->shortname, chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_intel8x0_free(chip);
return -EBUSY;
diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c
index 24703d75b65a..268e2f7241ea 100644
--- a/sound/pci/intel8x0m.c
+++ b/sound/pci/intel8x0m.c
@@ -224,7 +224,7 @@ struct intel8x0m {
unsigned int pcm_pos_shift;
};
-static struct pci_device_id snd_intel8x0m_ids[] __devinitdata = {
+static struct pci_device_id snd_intel8x0m_ids[] = {
{ 0x8086, 0x2416, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 82801AA */
{ 0x8086, 0x2426, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 82901AB */
{ 0x8086, 0x2446, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 82801BA */
@@ -1045,6 +1045,8 @@ static int intel8x0m_suspend(struct pci_dev *pci, pm_message_t state)
for (i = 0; i < chip->pcm_devs; i++)
snd_pcm_suspend_all(chip->pcm[i]);
snd_ac97_suspend(chip->ac97);
+ if (chip->irq >= 0)
+ free_irq(chip->irq, chip);
pci_disable_device(pci);
pci_save_state(pci);
return 0;
@@ -1058,6 +1060,9 @@ static int intel8x0m_resume(struct pci_dev *pci)
pci_restore_state(pci);
pci_enable_device(pci);
pci_set_master(pci);
+ request_irq(pci->irq, snd_intel8x0_interrupt, IRQF_DISABLED|IRQF_SHARED,
+ card->shortname, chip);
+ chip->irq = pci->irq;
snd_intel8x0_chip_init(chip, 0);
snd_ac97_resume(chip->ac97);
@@ -1185,7 +1190,7 @@ static int __devinit snd_intel8x0m_create(struct snd_card *card,
}
port_inited:
- if (request_irq(pci->irq, snd_intel8x0_interrupt, SA_INTERRUPT|SA_SHIRQ,
+ if (request_irq(pci->irq, snd_intel8x0_interrupt, IRQF_DISABLED|IRQF_SHARED,
card->shortname, chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_intel8x0_free(chip);
diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c
index 6e97932de34f..cfea51f44784 100644
--- a/sound/pci/korg1212/korg1212.c
+++ b/sound/pci/korg1212/korg1212.c
@@ -424,7 +424,7 @@ module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable Korg 1212 soundcard.");
MODULE_AUTHOR("Haroldo Gamal <gamal@alternex.com.br>");
-static struct pci_device_id snd_korg1212_ids[] __devinitdata = {
+static struct pci_device_id snd_korg1212_ids[] = {
{
.vendor = 0x10b5,
.device = 0x906d,
@@ -2237,7 +2237,7 @@ static int __devinit snd_korg1212_create(struct snd_card *card, struct pci_dev *
}
err = request_irq(pci->irq, snd_korg1212_interrupt,
- SA_INTERRUPT|SA_SHIRQ,
+ IRQF_DISABLED|IRQF_SHARED,
"korg1212", korg1212);
if (err) {
diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c
index 1c344fbd964d..45214b3b81be 100644
--- a/sound/pci/maestro3.c
+++ b/sound/pci/maestro3.c
@@ -869,7 +869,7 @@ struct snd_m3 {
/*
* pci ids
*/
-static struct pci_device_id snd_m3_ids[] __devinitdata = {
+static struct pci_device_id snd_m3_ids[] = {
{PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_ALLEGRO_1, PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0},
{PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_ALLEGRO, PCI_ANY_ID, PCI_ANY_ID,
@@ -2137,7 +2137,7 @@ static int __devinit snd_m3_mixer(struct snd_m3 *chip)
* DSP Code images
*/
-static const u16 assp_kernel_image[] __devinitdata = {
+static const u16 assp_kernel_image[] = {
0x7980, 0x0030, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x00FB, 0x7980, 0x00DD, 0x7980, 0x03B4,
0x7980, 0x0332, 0x7980, 0x0287, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4,
0x7980, 0x031A, 0x7980, 0x03B4, 0x7980, 0x022F, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4,
@@ -2224,7 +2224,7 @@ static const u16 assp_kernel_image[] __devinitdata = {
* Mini sample rate converter code image
* that is to be loaded at 0x400 on the DSP.
*/
-static const u16 assp_minisrc_image[] __devinitdata = {
+static const u16 assp_minisrc_image[] = {
0xBF80, 0x101E, 0x906E, 0x006E, 0x8B88, 0x6980, 0xEF88, 0x906F, 0x0D6F, 0x6900, 0xEB08, 0x0412,
0xBC20, 0x696E, 0xB801, 0x906E, 0x7980, 0x0403, 0xB90E, 0x8807, 0xBE43, 0xBF01, 0xBE47, 0xBE41,
@@ -2267,12 +2267,12 @@ static const u16 assp_minisrc_image[] __devinitdata = {
*/
#define MINISRC_LPF_LEN 10
-static const u16 minisrc_lpf[MINISRC_LPF_LEN] __devinitdata = {
+static const u16 minisrc_lpf[MINISRC_LPF_LEN] = {
0X0743, 0X1104, 0X0A4C, 0XF88D, 0X242C,
0X1023, 0X1AA9, 0X0B60, 0XEFDD, 0X186F
};
-static void __devinit snd_m3_assp_init(struct snd_m3 *chip)
+static void snd_m3_assp_init(struct snd_m3 *chip)
{
unsigned int i;
@@ -2760,7 +2760,7 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci,
tasklet_init(&chip->hwvol_tq, snd_m3_update_hw_volume, (unsigned long)chip);
- if (request_irq(pci->irq, snd_m3_interrupt, SA_INTERRUPT|SA_SHIRQ,
+ if (request_irq(pci->irq, snd_m3_interrupt, IRQF_DISABLED|IRQF_SHARED,
card->driver, chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_m3_free(chip);
diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c
index 366c4a7e65c6..216aee5f93e7 100644
--- a/sound/pci/mixart/mixart.c
+++ b/sound/pci/mixart/mixart.c
@@ -61,7 +61,7 @@ MODULE_PARM_DESC(enable, "Enable Digigram " CARD_NAME " soundcard.");
/*
*/
-static struct pci_device_id snd_mixart_ids[] __devinitdata = {
+static struct pci_device_id snd_mixart_ids[] = {
{ 0x1057, 0x0003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* MC8240 */
{ 0, }
};
@@ -1109,13 +1109,13 @@ static long long snd_mixart_BA0_llseek(struct snd_info_entry *entry,
offset = offset & ~3; /* 4 bytes aligned */
switch(orig) {
- case 0: /* SEEK_SET */
+ case SEEK_SET:
file->f_pos = offset;
break;
- case 1: /* SEEK_CUR */
+ case SEEK_CUR:
file->f_pos += offset;
break;
- case 2: /* SEEK_END, offset is negative */
+ case SEEK_END: /* offset is negative */
file->f_pos = MIXART_BA0_SIZE + offset;
break;
default:
@@ -1135,13 +1135,13 @@ static long long snd_mixart_BA1_llseek(struct snd_info_entry *entry,
offset = offset & ~3; /* 4 bytes aligned */
switch(orig) {
- case 0: /* SEEK_SET */
+ case SEEK_SET:
file->f_pos = offset;
break;
- case 1: /* SEEK_CUR */
+ case SEEK_CUR:
file->f_pos += offset;
break;
- case 2: /* SEEK_END, offset is negative */
+ case SEEK_END: /* offset is negative */
file->f_pos = MIXART_BA1_SIZE + offset;
break;
default:
@@ -1319,7 +1319,7 @@ static int __devinit snd_mixart_probe(struct pci_dev *pci,
pci_resource_len(pci, i));
}
- if (request_irq(pci->irq, snd_mixart_interrupt, SA_INTERRUPT|SA_SHIRQ, CARD_NAME, (void *)mgr)) {
+ if (request_irq(pci->irq, snd_mixart_interrupt, IRQF_DISABLED|IRQF_SHARED, CARD_NAME, (void *)mgr)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_mixart_free(mgr);
return -EBUSY;
diff --git a/sound/pci/mixart/mixart_mixer.c b/sound/pci/mixart/mixart_mixer.c
index ed47b732c103..13de0f71d4b7 100644
--- a/sound/pci/mixart/mixart_mixer.c
+++ b/sound/pci/mixart/mixart_mixer.c
@@ -31,6 +31,7 @@
#include "mixart_core.h"
#include "mixart_hwdep.h"
#include <sound/control.h>
+#include <sound/tlv.h>
#include "mixart_mixer.h"
static u32 mixart_analog_level[256] = {
@@ -388,12 +389,17 @@ static int mixart_analog_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
return changed;
}
+static DECLARE_TLV_DB_SCALE(db_scale_analog, -9600, 50, 0);
+
static struct snd_kcontrol_new mixart_control_analog_level = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
/* name will be filled later */
.info = mixart_analog_vol_info,
.get = mixart_analog_vol_get,
.put = mixart_analog_vol_put,
+ .tlv = { .p = db_scale_analog },
};
/* shared */
@@ -866,14 +872,19 @@ static int mixart_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
return changed;
}
+static DECLARE_TLV_DB_SCALE(db_scale_digital, -10950, 50, 0);
+
static struct snd_kcontrol_new snd_mixart_pcm_vol =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
/* name will be filled later */
/* count will be filled later */
.info = mixart_digital_vol_info, /* shared */
.get = mixart_pcm_vol_get,
.put = mixart_pcm_vol_put,
+ .tlv = { .p = db_scale_digital },
};
@@ -984,10 +995,13 @@ static int mixart_monitor_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
static struct snd_kcontrol_new mixart_control_monitor_vol = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Monitoring Volume",
.info = mixart_digital_vol_info, /* shared */
.get = mixart_monitor_vol_get,
.put = mixart_monitor_vol_put,
+ .tlv = { .p = db_scale_digital },
};
/*
diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c
index b92d6600deb9..101eee0aa018 100644
--- a/sound/pci/nm256/nm256.c
+++ b/sound/pci/nm256/nm256.c
@@ -263,7 +263,7 @@ struct nm256 {
/*
* PCI ids
*/
-static struct pci_device_id snd_nm256_ids[] __devinitdata = {
+static struct pci_device_id snd_nm256_ids[] = {
{PCI_VENDOR_ID_NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{PCI_VENDOR_ID_NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{PCI_VENDOR_ID_NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256XL_PLUS_AUDIO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
@@ -465,7 +465,7 @@ static int snd_nm256_acquire_irq(struct nm256 *chip)
{
mutex_lock(&chip->irq_mutex);
if (chip->irq < 0) {
- if (request_irq(chip->pci->irq, chip->interrupt, SA_INTERRUPT|SA_SHIRQ,
+ if (request_irq(chip->pci->irq, chip->interrupt, IRQF_DISABLED|IRQF_SHARED,
chip->card->driver, chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", chip->pci->irq);
mutex_unlock(&chip->irq_mutex);
diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c
index 8198884b51ee..533c672ae8f3 100644
--- a/sound/pci/pcxhr/pcxhr.c
+++ b/sound/pci/pcxhr/pcxhr.c
@@ -73,7 +73,7 @@ enum {
PCI_ID_LAST
};
-static struct pci_device_id pcxhr_ids[] __devinitdata = {
+static struct pci_device_id pcxhr_ids[] = {
{ 0x10b5, 0x9656, 0x1369, 0xb001, 0, 0, PCI_ID_VX882HR, }, /* VX882HR */
{ 0x10b5, 0x9656, 0x1369, 0xb101, 0, 0, PCI_ID_PCX882HR, }, /* PCX882HR */
{ 0x10b5, 0x9656, 0x1369, 0xb201, 0, 0, PCI_ID_VX881HR, }, /* VX881HR */
@@ -1250,7 +1250,7 @@ static int __devinit pcxhr_probe(struct pci_dev *pci, const struct pci_device_id
mgr->pci = pci;
mgr->irq = -1;
- if (request_irq(pci->irq, pcxhr_interrupt, SA_INTERRUPT|SA_SHIRQ,
+ if (request_irq(pci->irq, pcxhr_interrupt, IRQF_DISABLED|IRQF_SHARED,
card_name, mgr)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
pcxhr_free(mgr);
diff --git a/sound/pci/pcxhr/pcxhr_mixer.c b/sound/pci/pcxhr/pcxhr_mixer.c
index 94e63a1e90d9..b133ad9e095e 100644
--- a/sound/pci/pcxhr/pcxhr_mixer.c
+++ b/sound/pci/pcxhr/pcxhr_mixer.c
@@ -31,6 +31,7 @@
#include "pcxhr_hwdep.h"
#include "pcxhr_core.h"
#include <sound/control.h>
+#include <sound/tlv.h>
#include <sound/asoundef.h>
#include "pcxhr_mixer.h"
@@ -43,6 +44,9 @@
#define PCXHR_ANALOG_PLAYBACK_LEVEL_MAX 128 /* 0.0 dB */
#define PCXHR_ANALOG_PLAYBACK_ZERO_LEVEL 104 /* -24.0 dB ( 0.0 dB - fix level +24.0 dB ) */
+static DECLARE_TLV_DB_SCALE(db_scale_analog_capture, -9600, 50, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_analog_playback, -12800, 100, 0);
+
static int pcxhr_update_analog_audio_level(struct snd_pcxhr *chip, int is_capture, int channel)
{
int err, vol;
@@ -130,10 +134,13 @@ static int pcxhr_analog_vol_put(struct snd_kcontrol *kcontrol,
static struct snd_kcontrol_new pcxhr_control_analog_level = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
/* name will be filled later */
.info = pcxhr_analog_vol_info,
.get = pcxhr_analog_vol_get,
.put = pcxhr_analog_vol_put,
+ /* tlv will be filled later */
};
/* shared */
@@ -188,6 +195,7 @@ static struct snd_kcontrol_new pcxhr_control_output_switch = {
#define PCXHR_DIGITAL_LEVEL_MAX 0x1ff /* +18 dB */
#define PCXHR_DIGITAL_ZERO_LEVEL 0x1b7 /* 0 dB */
+static DECLARE_TLV_DB_SCALE(db_scale_digital, -10950, 50, 0);
#define MORE_THAN_ONE_STREAM_LEVEL 0x000001
#define VALID_STREAM_PAN_LEVEL_MASK 0x800000
@@ -343,11 +351,14 @@ static int pcxhr_pcm_vol_put(struct snd_kcontrol *kcontrol,
static struct snd_kcontrol_new snd_pcxhr_pcm_vol =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
/* name will be filled later */
/* count will be filled later */
.info = pcxhr_digital_vol_info, /* shared */
.get = pcxhr_pcm_vol_get,
.put = pcxhr_pcm_vol_put,
+ .tlv = { .p = db_scale_digital },
};
@@ -433,10 +444,13 @@ static int pcxhr_monitor_vol_put(struct snd_kcontrol *kcontrol,
static struct snd_kcontrol_new pcxhr_control_monitor_vol = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Monitoring Volume",
.info = pcxhr_digital_vol_info, /* shared */
.get = pcxhr_monitor_vol_get,
.put = pcxhr_monitor_vol_put,
+ .tlv = { .p = db_scale_digital },
};
/*
@@ -928,6 +942,7 @@ int pcxhr_create_mixer(struct pcxhr_mgr *mgr)
temp = pcxhr_control_analog_level;
temp.name = "Master Playback Volume";
temp.private_value = 0; /* playback */
+ temp.tlv.p = db_scale_analog_playback;
if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
return err;
/* output mute controls */
@@ -963,6 +978,7 @@ int pcxhr_create_mixer(struct pcxhr_mgr *mgr)
temp = pcxhr_control_analog_level;
temp.name = "Master Capture Volume";
temp.private_value = 1; /* capture */
+ temp.tlv.p = db_scale_analog_capture;
if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
return err;
diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c
index 5618ec9740bd..fe210c853442 100644
--- a/sound/pci/riptide/riptide.c
+++ b/sound/pci/riptide/riptide.c
@@ -506,7 +506,7 @@ static int riptide_reset(struct cmdif *cif, struct snd_riptide *chip);
/*
*/
-static struct pci_device_id snd_riptide_ids[] __devinitdata = {
+static struct pci_device_id snd_riptide_ids[] = {
{
.vendor = 0x127a,.device = 0x4310,
.subvendor = PCI_ANY_ID,.subdevice = PCI_ANY_ID,
@@ -673,9 +673,13 @@ static struct lbuspath lbus_rec_path = {
#define FIRMWARE_VERSIONS 1
static union firmware_version firmware_versions[] = {
{
- .firmware.ASIC = 3,.firmware.CODEC = 2,
- .firmware.AUXDSP = 3,.firmware.PROG = 773,
- },
+ .firmware = {
+ .ASIC = 3,
+ .CODEC = 2,
+ .AUXDSP = 3,
+ .PROG = 773,
+ },
+ },
};
static u32 atoh(unsigned char *in, unsigned int len)
@@ -1892,7 +1896,7 @@ snd_riptide_create(struct snd_card *card, struct pci_dev *pci,
UNSET_AIE(hwport);
if (request_irq
- (pci->irq, snd_riptide_interrupt, SA_INTERRUPT | SA_SHIRQ,
+ (pci->irq, snd_riptide_interrupt, IRQF_DISABLED | IRQF_SHARED,
"RIPTIDE", chip)) {
snd_printk(KERN_ERR "Riptide: unable to grab IRQ %d\n",
pci->irq);
diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c
index 2cb9fe98db2f..2a71499242fa 100644
--- a/sound/pci/rme32.c
+++ b/sound/pci/rme32.c
@@ -227,7 +227,7 @@ struct rme32 {
struct snd_kcontrol *spdif_ctl;
};
-static struct pci_device_id snd_rme32_ids[] __devinitdata = {
+static struct pci_device_id snd_rme32_ids[] = {
{PCI_VENDOR_ID_XILINX_RME, PCI_DEVICE_ID_RME_DIGI32,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,},
{PCI_VENDOR_ID_XILINX_RME, PCI_DEVICE_ID_RME_DIGI32_8,
@@ -1374,7 +1374,7 @@ static int __devinit snd_rme32_create(struct rme32 * rme32)
return -ENOMEM;
}
- if (request_irq(pci->irq, snd_rme32_interrupt, SA_INTERRUPT | SA_SHIRQ, "RME32", (void *) rme32)) {
+ if (request_irq(pci->irq, snd_rme32_interrupt, IRQF_DISABLED | IRQF_SHARED, "RME32", (void *) rme32)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
return -EBUSY;
}
diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c
index 991cb18c14f3..f8de7c997017 100644
--- a/sound/pci/rme96.c
+++ b/sound/pci/rme96.c
@@ -232,7 +232,7 @@ struct rme96 {
struct snd_kcontrol *spdif_ctl;
};
-static struct pci_device_id snd_rme96_ids[] __devinitdata = {
+static struct pci_device_id snd_rme96_ids[] = {
{ PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_RME_DIGI96,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
{ PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_RME_DIGI96_8,
@@ -1588,7 +1588,7 @@ snd_rme96_create(struct rme96 *rme96)
return -ENOMEM;
}
- if (request_irq(pci->irq, snd_rme96_interrupt, SA_INTERRUPT|SA_SHIRQ, "RME96", (void *)rme96)) {
+ if (request_irq(pci->irq, snd_rme96_interrupt, IRQF_DISABLED|IRQF_SHARED, "RME96", (void *)rme96)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
return -EBUSY;
}
diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c
index eaf3c22449ad..d3e07de433b0 100644
--- a/sound/pci/rme9652/hdsp.c
+++ b/sound/pci/rme9652/hdsp.c
@@ -568,7 +568,7 @@ static void snd_hammerfall_free_buffer(struct snd_dma_buffer *dmab, struct pci_d
}
-static struct pci_device_id snd_hdsp_ids[] __devinitdata = {
+static struct pci_device_id snd_hdsp_ids[] = {
{
.vendor = PCI_VENDOR_ID_XILINX,
.device = PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP,
@@ -726,22 +726,36 @@ static int hdsp_get_iobox_version (struct hdsp *hdsp)
}
-static int hdsp_check_for_firmware (struct hdsp *hdsp, int show_err)
+#ifdef HDSP_FW_LOADER
+static int __devinit hdsp_request_fw_loader(struct hdsp *hdsp);
+#endif
+
+static int hdsp_check_for_firmware (struct hdsp *hdsp, int load_on_demand)
{
- if (hdsp->io_type == H9652 || hdsp->io_type == H9632) return 0;
+ if (hdsp->io_type == H9652 || hdsp->io_type == H9632)
+ return 0;
if ((hdsp_read (hdsp, HDSP_statusRegister) & HDSP_DllError) != 0) {
- snd_printk(KERN_ERR "Hammerfall-DSP: firmware not present.\n");
hdsp->state &= ~HDSP_FirmwareLoaded;
- if (! show_err)
+ if (! load_on_demand)
return -EIO;
+ snd_printk(KERN_ERR "Hammerfall-DSP: firmware not present.\n");
/* try to load firmware */
- if (hdsp->state & HDSP_FirmwareCached) {
- if (snd_hdsp_load_firmware_from_cache(hdsp) != 0)
- snd_printk(KERN_ERR "Hammerfall-DSP: Firmware loading from cache failed, please upload manually.\n");
- } else {
- snd_printk(KERN_ERR "Hammerfall-DSP: No firmware loaded nor cached, please upload firmware.\n");
+ if (! (hdsp->state & HDSP_FirmwareCached)) {
+#ifdef HDSP_FW_LOADER
+ if (! hdsp_request_fw_loader(hdsp))
+ return 0;
+#endif
+ snd_printk(KERN_ERR
+ "Hammerfall-DSP: No firmware loaded nor "
+ "cached, please upload firmware.\n");
+ return -EIO;
+ }
+ if (snd_hdsp_load_firmware_from_cache(hdsp) != 0) {
+ snd_printk(KERN_ERR
+ "Hammerfall-DSP: Firmware loading from "
+ "cache failed, please upload manually.\n");
+ return -EIO;
}
- return -EIO;
}
return 0;
}
@@ -1356,7 +1370,7 @@ static struct snd_rawmidi_ops snd_hdsp_midi_input =
.trigger = snd_hdsp_midi_input_trigger,
};
-static int __devinit snd_hdsp_create_midi (struct snd_card *card, struct hdsp *hdsp, int id)
+static int snd_hdsp_create_midi (struct snd_card *card, struct hdsp *hdsp, int id)
{
char buf[32];
@@ -3181,8 +3195,16 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
return;
}
} else {
- snd_iprintf(buffer, "No firmware loaded nor cached, please upload firmware.\n");
- return;
+ int err = -EINVAL;
+#ifdef HDSP_FW_LOADER
+ err = hdsp_request_fw_loader(hdsp);
+#endif
+ if (err < 0) {
+ snd_iprintf(buffer,
+ "No firmware loaded nor cached, "
+ "please upload firmware.\n");
+ return;
+ }
}
}
@@ -3851,7 +3873,7 @@ static int snd_hdsp_trigger(struct snd_pcm_substream *substream, int cmd)
if (hdsp_check_for_iobox (hdsp))
return -EIO;
- if (hdsp_check_for_firmware(hdsp, 1))
+ if (hdsp_check_for_firmware(hdsp, 0)) /* no auto-loading in trigger */
return -EIO;
spin_lock(&hdsp->lock);
@@ -4912,7 +4934,7 @@ static int __devinit snd_hdsp_create(struct snd_card *card,
return -EBUSY;
}
- if (request_irq(pci->irq, snd_hdsp_interrupt, SA_INTERRUPT|SA_SHIRQ, "hdsp", (void *)hdsp)) {
+ if (request_irq(pci->irq, snd_hdsp_interrupt, IRQF_DISABLED|IRQF_SHARED, "hdsp", (void *)hdsp)) {
snd_printk(KERN_ERR "Hammerfall-DSP: unable to use IRQ %d\n", pci->irq);
return -EBUSY;
}
diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c
index bba1615504d3..7d03ae066d53 100644
--- a/sound/pci/rme9652/hdspm.c
+++ b/sound/pci/rme9652/hdspm.c
@@ -3497,7 +3497,7 @@ static int __devinit snd_hdspm_create(struct snd_card *card, struct hdspm * hdsp
hdspm->port + io_extent - 1);
if (request_irq(pci->irq, snd_hdspm_interrupt,
- SA_INTERRUPT | SA_SHIRQ, "hdspm",
+ IRQF_DISABLED | IRQF_SHARED, "hdspm",
(void *) hdspm)) {
snd_printk(KERN_ERR "HDSPM: unable to use IRQ %d\n", pci->irq);
return -EBUSY;
diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c
index 3b945e8c1b15..fc15f61ad5d1 100644
--- a/sound/pci/rme9652/rme9652.c
+++ b/sound/pci/rme9652/rme9652.c
@@ -315,7 +315,7 @@ static void snd_hammerfall_free_buffer(struct snd_dma_buffer *dmab, struct pci_d
}
-static struct pci_device_id snd_rme9652_ids[] __devinitdata = {
+static struct pci_device_id snd_rme9652_ids[] = {
{
.vendor = 0x10ee,
.device = 0x3fc4,
@@ -2500,7 +2500,7 @@ static int __devinit snd_rme9652_create(struct snd_card *card,
return -EBUSY;
}
- if (request_irq(pci->irq, snd_rme9652_interrupt, SA_INTERRUPT|SA_SHIRQ, "rme9652", (void *)rme9652)) {
+ if (request_irq(pci->irq, snd_rme9652_interrupt, IRQF_DISABLED|IRQF_SHARED, "rme9652", (void *)rme9652)) {
snd_printk(KERN_ERR "unable to request IRQ %d\n", pci->irq);
return -EBUSY;
}
diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c
index dcf402948347..e5d4def1aa6f 100644
--- a/sound/pci/sonicvibes.c
+++ b/sound/pci/sonicvibes.c
@@ -243,7 +243,7 @@ struct sonicvibes {
#endif
};
-static struct pci_device_id snd_sonic_ids[] __devinitdata = {
+static struct pci_device_id snd_sonic_ids[] = {
{ 0x5333, 0xca00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
{ 0, }
};
@@ -1257,7 +1257,7 @@ static int __devinit snd_sonicvibes_create(struct snd_card *card,
sonic->midi_port = pci_resource_start(pci, 3);
sonic->game_port = pci_resource_start(pci, 4);
- if (request_irq(pci->irq, snd_sonicvibes_interrupt, SA_INTERRUPT|SA_SHIRQ, "S3 SonicVibes", (void *)sonic)) {
+ if (request_irq(pci->irq, snd_sonicvibes_interrupt, IRQF_DISABLED|IRQF_SHARED, "S3 SonicVibes", (void *)sonic)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_sonicvibes_free(sonic);
return -EBUSY;
@@ -1441,10 +1441,10 @@ static int __devinit snd_sonic_probe(struct pci_dev *pci,
strcpy(card->driver, "SonicVibes");
strcpy(card->shortname, "S3 SonicVibes");
- sprintf(card->longname, "%s rev %i at 0x%lx, irq %i",
+ sprintf(card->longname, "%s rev %i at 0x%llx, irq %i",
card->shortname,
sonic->revision,
- pci_resource_start(pci, 1),
+ (unsigned long long)pci_resource_start(pci, 1),
sonic->irq);
if ((err = snd_sonicvibes_pcm(sonic, 0, NULL)) < 0) {
diff --git a/sound/pci/trident/trident.c b/sound/pci/trident/trident.c
index 5629b7eba96d..9145f7c57fb0 100644
--- a/sound/pci/trident/trident.c
+++ b/sound/pci/trident/trident.c
@@ -63,7 +63,7 @@ MODULE_PARM_DESC(pcm_channels, "Number of hardware channels assigned for PCM.");
module_param_array(wavetable_size, int, NULL, 0444);
MODULE_PARM_DESC(wavetable_size, "Maximum memory size in kB for wavetable synth.");
-static struct pci_device_id snd_trident_ids[] __devinitdata = {
+static struct pci_device_id snd_trident_ids[] = {
{PCI_DEVICE(PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_DX),
PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0},
{PCI_DEVICE(PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_NX),
diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c
index d99ed7237750..ebbe12d78d8c 100644
--- a/sound/pci/trident/trident_main.c
+++ b/sound/pci/trident/trident_main.c
@@ -40,6 +40,7 @@
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
+#include <sound/tlv.h>
#include <sound/trident.h>
#include <sound/asoundef.h>
@@ -2627,6 +2628,8 @@ static int snd_trident_vol_control_get(struct snd_kcontrol *kcontrol,
return 0;
}
+static DECLARE_TLV_DB_SCALE(db_scale_gvol, -6375, 25, 0);
+
static int snd_trident_vol_control_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -2653,6 +2656,7 @@ static struct snd_kcontrol_new snd_trident_vol_music_control __devinitdata =
.get = snd_trident_vol_control_get,
.put = snd_trident_vol_control_put,
.private_value = 16,
+ .tlv = { .p = db_scale_gvol },
};
static struct snd_kcontrol_new snd_trident_vol_wave_control __devinitdata =
@@ -2663,6 +2667,7 @@ static struct snd_kcontrol_new snd_trident_vol_wave_control __devinitdata =
.get = snd_trident_vol_control_get,
.put = snd_trident_vol_control_put,
.private_value = 0,
+ .tlv = { .p = db_scale_gvol },
};
/*---------------------------------------------------------------------------
@@ -2730,6 +2735,7 @@ static struct snd_kcontrol_new snd_trident_pcm_vol_control __devinitdata =
.info = snd_trident_pcm_vol_control_info,
.get = snd_trident_pcm_vol_control_get,
.put = snd_trident_pcm_vol_control_put,
+ /* FIXME: no tlv yet */
};
/*---------------------------------------------------------------------------
@@ -2839,6 +2845,8 @@ static int snd_trident_pcm_rvol_control_put(struct snd_kcontrol *kcontrol,
return change;
}
+static DECLARE_TLV_DB_SCALE(db_scale_crvol, -3175, 25, 1);
+
static struct snd_kcontrol_new snd_trident_pcm_rvol_control __devinitdata =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -2848,6 +2856,7 @@ static struct snd_kcontrol_new snd_trident_pcm_rvol_control __devinitdata =
.info = snd_trident_pcm_rvol_control_info,
.get = snd_trident_pcm_rvol_control_get,
.put = snd_trident_pcm_rvol_control_put,
+ .tlv = { .p = db_scale_crvol },
};
/*---------------------------------------------------------------------------
@@ -2903,6 +2912,7 @@ static struct snd_kcontrol_new snd_trident_pcm_cvol_control __devinitdata =
.info = snd_trident_pcm_cvol_control_info,
.get = snd_trident_pcm_cvol_control_get,
.put = snd_trident_pcm_cvol_control_put,
+ .tlv = { .p = db_scale_crvol },
};
static void snd_trident_notify_pcm_change1(struct snd_card *card,
@@ -3599,7 +3609,7 @@ int __devinit snd_trident_create(struct snd_card *card,
}
trident->port = pci_resource_start(pci, 0);
- if (request_irq(pci->irq, snd_trident_interrupt, SA_INTERRUPT|SA_SHIRQ,
+ if (request_irq(pci->irq, snd_trident_interrupt, IRQF_DISABLED|IRQF_SHARED,
"Trident Audio", trident)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_trident_free(trident);
diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c
index 2527bbd958c5..6db3d4cc4d8d 100644
--- a/sound/pci/via82xx.c
+++ b/sound/pci/via82xx.c
@@ -59,6 +59,7 @@
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/info.h>
+#include <sound/tlv.h>
#include <sound/ac97_codec.h>
#include <sound/mpu401.h>
#include <sound/initval.h>
@@ -396,7 +397,7 @@ struct via82xx {
#endif
};
-static struct pci_device_id snd_via82xx_ids[] __devinitdata = {
+static struct pci_device_id snd_via82xx_ids[] = {
/* 0x1106, 0x3058 */
{ PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_CARD_VIA686, }, /* 686A */
/* 0x1106, 0x3059 */
@@ -1277,7 +1278,18 @@ static int snd_via82xx_pcm_close(struct snd_pcm_substream *substream)
if (! ratep->used)
ratep->rate = 0;
spin_unlock_irq(&ratep->lock);
-
+ if (! ratep->rate) {
+ if (! viadev->direction) {
+ snd_ac97_update_power(chip->ac97,
+ AC97_PCM_FRONT_DAC_RATE, 0);
+ snd_ac97_update_power(chip->ac97,
+ AC97_PCM_SURR_DAC_RATE, 0);
+ snd_ac97_update_power(chip->ac97,
+ AC97_PCM_LFE_DAC_RATE, 0);
+ } else
+ snd_ac97_update_power(chip->ac97,
+ AC97_PCM_LR_ADC_RATE, 0);
+ }
viadev->substream = NULL;
return 0;
}
@@ -1687,21 +1699,29 @@ static int snd_via8233_pcmdxs_volume_put(struct snd_kcontrol *kcontrol,
return change;
}
+static DECLARE_TLV_DB_SCALE(db_scale_dxs, -9450, 150, 1);
+
static struct snd_kcontrol_new snd_via8233_pcmdxs_volume_control __devinitdata = {
.name = "PCM Playback Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.info = snd_via8233_dxs_volume_info,
.get = snd_via8233_pcmdxs_volume_get,
.put = snd_via8233_pcmdxs_volume_put,
+ .tlv = { .p = db_scale_dxs }
};
static struct snd_kcontrol_new snd_via8233_dxs_volume_control __devinitdata = {
.name = "VIA DXS Playback Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.count = 4,
.info = snd_via8233_dxs_volume_info,
.get = snd_via8233_dxs_volume_get,
.put = snd_via8233_dxs_volume_put,
+ .tlv = { .p = db_scale_dxs }
};
/*
@@ -2281,7 +2301,7 @@ static int __devinit snd_via82xx_create(struct snd_card *card,
if (request_irq(pci->irq,
chip_type == TYPE_VIA8233 ?
snd_via8233_interrupt : snd_via686_interrupt,
- SA_INTERRUPT|SA_SHIRQ,
+ IRQF_DISABLED|IRQF_SHARED,
card->driver, chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_via82xx_free(chip);
@@ -2393,6 +2413,7 @@ static int __devinit check_dxs_list(struct pci_dev *pci, int revision)
{ .subvendor = 0x16f3, .subdevice = 0x6405, .action = VIA_DXS_SRC }, /* Jetway K8M8MS */
{ .subvendor = 0x1734, .subdevice = 0x1078, .action = VIA_DXS_SRC }, /* FSC Amilo L7300 */
{ .subvendor = 0x1734, .subdevice = 0x1093, .action = VIA_DXS_SRC }, /* FSC */
+ { .subvendor = 0x1734, .subdevice = 0x10ab, .action = VIA_DXS_SRC }, /* FSC */
{ .subvendor = 0x1849, .subdevice = 0x3059, .action = VIA_DXS_NO_VRA }, /* ASRock K7VM2 */
{ .subvendor = 0x1849, .subdevice = 0x9739, .action = VIA_DXS_SRC }, /* ASRock mobo(?) */
{ .subvendor = 0x1849, .subdevice = 0x9761, .action = VIA_DXS_SRC }, /* ASRock mobo(?) */
diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c
index 577a2b03759f..016f9dac253f 100644
--- a/sound/pci/via82xx_modem.c
+++ b/sound/pci/via82xx_modem.c
@@ -261,7 +261,7 @@ struct via82xx_modem {
struct snd_info_entry *proc_entry;
};
-static struct pci_device_id snd_via82xx_modem_ids[] __devinitdata = {
+static struct pci_device_id snd_via82xx_modem_ids[] = {
{ 0x1106, 0x3068, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_CARD_VIA82XX_MODEM, },
{ 0, }
};
@@ -1118,7 +1118,7 @@ static int __devinit snd_via82xx_create(struct snd_card *card,
return err;
}
chip->port = pci_resource_start(pci, 0);
- if (request_irq(pci->irq, snd_via82xx_interrupt, SA_INTERRUPT|SA_SHIRQ,
+ if (request_irq(pci->irq, snd_via82xx_interrupt, IRQF_DISABLED|IRQF_SHARED,
card->driver, chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_via82xx_free(chip);
diff --git a/sound/pci/vx222/vx222.c b/sound/pci/vx222/vx222.c
index 0f1ebb010a5e..e7cd8acab59a 100644
--- a/sound/pci/vx222/vx222.c
+++ b/sound/pci/vx222/vx222.c
@@ -26,6 +26,7 @@
#include <linux/moduleparam.h>
#include <sound/core.h>
#include <sound/initval.h>
+#include <sound/tlv.h>
#include "vx222.h"
#define CARD_NAME "VX222"
@@ -60,7 +61,7 @@ enum {
VX_PCI_VX222_NEW
};
-static struct pci_device_id snd_vx222_ids[] __devinitdata = {
+static struct pci_device_id snd_vx222_ids[] = {
{ 0x10b5, 0x9050, 0x1369, PCI_ANY_ID, 0, 0, VX_PCI_VX222_OLD, }, /* PLX */
{ 0x10b5, 0x9030, 0x1369, PCI_ANY_ID, 0, 0, VX_PCI_VX222_NEW, }, /* PLX */
{ 0, }
@@ -72,6 +73,9 @@ MODULE_DEVICE_TABLE(pci, snd_vx222_ids);
/*
*/
+static DECLARE_TLV_DB_SCALE(db_scale_old_vol, -11350, 50, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_akm, -7350, 50, 0);
+
static struct snd_vx_hardware vx222_old_hw = {
.name = "VX222/Old",
@@ -81,6 +85,7 @@ static struct snd_vx_hardware vx222_old_hw = {
.num_ins = 1,
.num_outs = 1,
.output_level_max = VX_ANALOG_OUT_LEVEL_MAX,
+ .output_level_db_scale = db_scale_old_vol,
};
static struct snd_vx_hardware vx222_v2_hw = {
@@ -92,6 +97,7 @@ static struct snd_vx_hardware vx222_v2_hw = {
.num_ins = 1,
.num_outs = 1,
.output_level_max = VX2_AKM_LEVEL_MAX,
+ .output_level_db_scale = db_scale_akm,
};
static struct snd_vx_hardware vx222_mic_hw = {
@@ -103,6 +109,7 @@ static struct snd_vx_hardware vx222_mic_hw = {
.num_ins = 1,
.num_outs = 1,
.output_level_max = VX2_AKM_LEVEL_MAX,
+ .output_level_db_scale = db_scale_akm,
};
@@ -162,7 +169,7 @@ static int __devinit snd_vx222_create(struct snd_card *card, struct pci_dev *pci
for (i = 0; i < 2; i++)
vx->port[i] = pci_resource_start(pci, i + 1);
- if (request_irq(pci->irq, snd_vx_irq_handler, SA_INTERRUPT|SA_SHIRQ,
+ if (request_irq(pci->irq, snd_vx_irq_handler, IRQF_DISABLED|IRQF_SHARED,
CARD_NAME, (void *) chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_vx222_free(chip);
diff --git a/sound/pci/vx222/vx222_ops.c b/sound/pci/vx222/vx222_ops.c
index 9b6d345b83a6..5e51950e05f9 100644
--- a/sound/pci/vx222/vx222_ops.c
+++ b/sound/pci/vx222/vx222_ops.c
@@ -28,6 +28,7 @@
#include <sound/core.h>
#include <sound/control.h>
+#include <sound/tlv.h>
#include <asm/io.h>
#include "vx222.h"
@@ -845,6 +846,8 @@ static void vx2_set_input_level(struct snd_vx222 *chip)
#define MIC_LEVEL_MAX 0xff
+static DECLARE_TLV_DB_SCALE(db_scale_mic, -6450, 50, 0);
+
/*
* controls API for input levels
*/
@@ -922,18 +925,24 @@ static int vx_mic_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
static struct snd_kcontrol_new vx_control_input_level = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Capture Volume",
.info = vx_input_level_info,
.get = vx_input_level_get,
.put = vx_input_level_put,
+ .tlv = { .p = db_scale_mic },
};
static struct snd_kcontrol_new vx_control_mic_level = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Mic Capture Volume",
.info = vx_mic_level_info,
.get = vx_mic_level_get,
.put = vx_mic_level_put,
+ .tlv = { .p = db_scale_mic },
};
/*
diff --git a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c
index 26aa775b7b69..186453f7abe7 100644
--- a/sound/pci/ymfpci/ymfpci.c
+++ b/sound/pci/ymfpci/ymfpci.c
@@ -70,7 +70,7 @@ MODULE_PARM_DESC(rear_switch, "Enable shared rear/line-in switch");
module_param_array(rear_swap, bool, NULL, 0444);
MODULE_PARM_DESC(rear_swap, "Swap rear channels (must be enabled for correct IEC958 (S/PDIF)) output");
-static struct pci_device_id snd_ymfpci_ids[] __devinitdata = {
+static struct pci_device_id snd_ymfpci_ids[] = {
{ 0x1073, 0x0004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* YMF724 */
{ 0x1073, 0x000d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* YMF724F */
{ 0x1073, 0x000a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* YMF740 */
diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c
index f894752523bb..24f6fc52f898 100644
--- a/sound/pci/ymfpci/ymfpci_main.c
+++ b/sound/pci/ymfpci/ymfpci_main.c
@@ -36,6 +36,7 @@
#include <sound/core.h>
#include <sound/control.h>
#include <sound/info.h>
+#include <sound/tlv.h>
#include <sound/ymfpci.h>
#include <sound/asoundef.h>
#include <sound/mpu401.h>
@@ -1477,11 +1478,15 @@ static int snd_ymfpci_put_single(struct snd_kcontrol *kcontrol,
return change;
}
+static DECLARE_TLV_DB_LINEAR(db_scale_native, TLV_DB_GAIN_MUTE, 0);
+
#define YMFPCI_DOUBLE(xname, xindex, reg) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
.info = snd_ymfpci_info_double, \
.get = snd_ymfpci_get_double, .put = snd_ymfpci_put_double, \
- .private_value = reg }
+ .private_value = reg, \
+ .tlv = { .p = db_scale_native } }
static int snd_ymfpci_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
@@ -2288,7 +2293,7 @@ int __devinit snd_ymfpci_create(struct snd_card *card,
snd_ymfpci_free(chip);
return -EBUSY;
}
- if (request_irq(pci->irq, snd_ymfpci_interrupt, SA_INTERRUPT|SA_SHIRQ, "YMFPCI", (void *) chip)) {
+ if (request_irq(pci->irq, snd_ymfpci_interrupt, IRQF_DISABLED|IRQF_SHARED, "YMFPCI", (void *) chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_ymfpci_free(chip);
return -EBUSY;
diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf.c b/sound/pcmcia/pdaudiocf/pdaudiocf.c
index adfdce7499d1..fd3590fcaedb 100644
--- a/sound/pcmcia/pdaudiocf/pdaudiocf.c
+++ b/sound/pcmcia/pdaudiocf/pdaudiocf.c
@@ -206,7 +206,7 @@ static void snd_pdacf_detach(struct pcmcia_device *link)
snd_pdacf_powerdown(chip);
chip->chip_status |= PDAUDIOCF_STAT_IS_STALE; /* to be sure */
snd_card_disconnect(chip->card);
- snd_card_free_in_thread(chip->card);
+ snd_card_free_when_closed(chip->card);
}
/*
@@ -241,12 +241,13 @@ static int pdacf_config(struct pcmcia_device *link)
CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, parse));
link->conf.ConfigBase = parse->config.base;
link->conf.ConfigIndex = 0x5;
- kfree(parse);
CS_CHECK(RequestIO, pcmcia_request_io(link, &link->io));
CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
+ kfree(parse);
+
if (snd_pdacf_assign_resources(pdacf, link->io.BasePort1, link->irq.AssignedIRQ) < 0)
goto failed;
@@ -254,6 +255,7 @@ static int pdacf_config(struct pcmcia_device *link)
return 0;
cs_failed:
+ kfree(parse);
cs_error(link, last_fn, last_ret);
failed:
pcmcia_disable_device(link);
diff --git a/sound/pcmcia/vx/vxp_mixer.c b/sound/pcmcia/vx/vxp_mixer.c
index e237f6c2018f..bced7b623b12 100644
--- a/sound/pcmcia/vx/vxp_mixer.c
+++ b/sound/pcmcia/vx/vxp_mixer.c
@@ -23,6 +23,7 @@
#include <sound/driver.h>
#include <sound/core.h>
#include <sound/control.h>
+#include <sound/tlv.h>
#include "vxpocket.h"
#define MIC_LEVEL_MIN 0
@@ -63,12 +64,17 @@ static int vx_mic_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
return 0;
}
+static DECLARE_TLV_DB_SCALE(db_scale_mic, -21, 3, 0);
+
static struct snd_kcontrol_new vx_control_mic_level = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Mic Capture Volume",
.info = vx_mic_level_info,
.get = vx_mic_level_get,
.put = vx_mic_level_put,
+ .tlv = { .p = db_scale_mic },
};
/*
diff --git a/sound/pcmcia/vx/vxpocket.c b/sound/pcmcia/vx/vxpocket.c
index cafe6640cc1a..3089fcca800e 100644
--- a/sound/pcmcia/vx/vxpocket.c
+++ b/sound/pcmcia/vx/vxpocket.c
@@ -27,6 +27,7 @@
#include <pcmcia/ciscode.h>
#include <pcmcia/cisreg.h>
#include <sound/initval.h>
+#include <sound/tlv.h>
/*
*/
@@ -65,7 +66,7 @@ static void vxpocket_release(struct pcmcia_device *link)
}
/*
- * destructor, called from snd_card_free_in_thread()
+ * destructor, called from snd_card_free_when_closed()
*/
static int snd_vxpocket_dev_free(struct snd_device *device)
{
@@ -90,6 +91,8 @@ static int snd_vxpocket_dev_free(struct snd_device *device)
* Only output levels can be modified
*/
+static DECLARE_TLV_DB_SCALE(db_scale_old_vol, -11350, 50, 0);
+
static struct snd_vx_hardware vxpocket_hw = {
.name = "VXPocket",
.type = VX_TYPE_VXPOCKET,
@@ -99,6 +102,7 @@ static struct snd_vx_hardware vxpocket_hw = {
.num_ins = 1,
.num_outs = 1,
.output_level_max = VX_ANALOG_OUT_LEVEL_MAX,
+ .output_level_db_scale = db_scale_old_vol,
};
/* VX-pocket 440
@@ -120,6 +124,7 @@ static struct snd_vx_hardware vxp440_hw = {
.num_ins = 2,
.num_outs = 2,
.output_level_max = VX_ANALOG_OUT_LEVEL_MAX,
+ .output_level_db_scale = db_scale_old_vol,
};
@@ -363,7 +368,7 @@ static void vxpocket_detach(struct pcmcia_device *link)
chip->chip_status |= VX_STAT_IS_STALE; /* to be sure */
snd_card_disconnect(chip->card);
vxpocket_release(link);
- snd_card_free_in_thread(chip->card);
+ snd_card_free_when_closed(chip->card);
}
/*
diff --git a/sound/ppc/awacs.c b/sound/ppc/awacs.c
index 82d791be7499..05dabe454658 100644
--- a/sound/ppc/awacs.c
+++ b/sound/ppc/awacs.c
@@ -801,11 +801,10 @@ snd_pmac_awacs_init(struct snd_pmac *chip)
chip->revision = (in_le32(&chip->awacs->codec_stat) >> 12) & 0xf;
#ifdef PMAC_AMP_AVAIL
if (chip->revision == 3 && chip->has_iic && CHECK_CUDA_AMP()) {
- struct awacs_amp *amp = kmalloc(sizeof(*amp), GFP_KERNEL);
+ struct awacs_amp *amp = kzalloc(sizeof(*amp), GFP_KERNEL);
if (! amp)
return -ENOMEM;
chip->mixer_data = amp;
- memset(amp, 0, sizeof(*amp));
chip->mixer_free = awacs_amp_free;
awacs_amp_set_vol(amp, 0, 63, 63, 0); /* mute and zero vol */
awacs_amp_set_vol(amp, 1, 63, 63, 0);
diff --git a/sound/ppc/beep.c b/sound/ppc/beep.c
index 5fec1e58f310..5f38f670102c 100644
--- a/sound/ppc/beep.c
+++ b/sound/ppc/beep.c
@@ -215,15 +215,18 @@ int __init snd_pmac_attach_beep(struct snd_pmac *chip)
{
struct pmac_beep *beep;
struct input_dev *input_dev;
+ struct snd_kcontrol *beep_ctl;
void *dmabuf;
int err = -ENOMEM;
beep = kzalloc(sizeof(*beep), GFP_KERNEL);
+ if (! beep)
+ return -ENOMEM;
dmabuf = dma_alloc_coherent(&chip->pdev->dev, BEEP_BUFLEN * 4,
&beep->addr, GFP_KERNEL);
input_dev = input_allocate_device();
- if (!beep || !dmabuf || !input_dev)
- goto fail;
+ if (! dmabuf || ! input_dev)
+ goto fail1;
/* FIXME: set more better values */
input_dev->name = "PowerMac Beep";
@@ -244,17 +247,24 @@ int __init snd_pmac_attach_beep(struct snd_pmac *chip)
beep->volume = BEEP_VOLUME;
beep->running = 0;
- err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_pmac_beep_mixer, chip));
+ beep_ctl = snd_ctl_new1(&snd_pmac_beep_mixer, chip);
+ err = snd_ctl_add(chip->card, beep_ctl);
if (err < 0)
- goto fail;
+ goto fail1;
+
+ chip->beep = beep;
- chip->beep = beep;
- input_register_device(beep->dev);
-
- return 0;
-
- fail: input_free_device(input_dev);
- kfree(dmabuf);
+ err = input_register_device(beep->dev);
+ if (err)
+ goto fail2;
+
+ return 0;
+
+ fail2: snd_ctl_remove(chip->card, beep_ctl);
+ fail1: input_free_device(input_dev);
+ if (dmabuf)
+ dma_free_coherent(&chip->pdev->dev, BEEP_BUFLEN * 4,
+ dmabuf, beep->addr);
kfree(beep);
return err;
}
diff --git a/sound/ppc/daca.c b/sound/ppc/daca.c
index 46eebf5610e3..57202b0f033e 100644
--- a/sound/ppc/daca.c
+++ b/sound/ppc/daca.c
@@ -258,10 +258,9 @@ int __init snd_pmac_daca_init(struct snd_pmac *chip)
request_module("i2c-powermac");
#endif /* CONFIG_KMOD */
- mix = kmalloc(sizeof(*mix), GFP_KERNEL);
+ mix = kzalloc(sizeof(*mix), GFP_KERNEL);
if (! mix)
return -ENOMEM;
- memset(mix, 0, sizeof(*mix));
chip->mixer_data = mix;
chip->mixer_free = daca_cleanup;
mix->amp_on = 1; /* default on */
diff --git a/sound/ppc/keywest.c b/sound/ppc/keywest.c
index fb05938dcbd9..272ae38e9b18 100644
--- a/sound/ppc/keywest.c
+++ b/sound/ppc/keywest.c
@@ -64,11 +64,10 @@ static int keywest_attach_adapter(struct i2c_adapter *adapter)
if (strncmp(i2c_device_name(adapter), "mac-io", 6))
return 0; /* ignored */
- new_client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
+ new_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
if (! new_client)
return -ENOMEM;
- memset(new_client, 0, sizeof(*new_client));
new_client->addr = keywest_ctx->addr;
i2c_set_clientdata(new_client, keywest_ctx);
new_client->adapter = adapter;
@@ -118,6 +117,9 @@ int __init snd_pmac_tumbler_post_init(void)
{
int err;
+ if (!keywest_ctx || !keywest_ctx->client)
+ return -ENXIO;
+
if ((err = keywest_ctx->init_client(keywest_ctx)) < 0) {
snd_printk(KERN_ERR "tumbler: %i :cannot initialize the MCS\n", err);
return err;
diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c
index b678814975c9..641430631505 100644
--- a/sound/ppc/pmac.c
+++ b/sound/ppc/pmac.c
@@ -1120,6 +1120,7 @@ int __init snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return)
struct snd_pmac *chip;
struct device_node *np;
int i, err;
+ unsigned int irq;
unsigned long ctrl_addr, txdma_addr, rxdma_addr;
static struct snd_device_ops ops = {
.dev_free = snd_pmac_dev_free,
@@ -1153,10 +1154,6 @@ int __init snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return)
if (chip->is_k2) {
static char *rnames[] = {
"Sound Control", "Sound DMA" };
- if (np->n_intrs < 3) {
- err = -ENODEV;
- goto __error;
- }
for (i = 0; i < 2; i ++) {
if (of_address_to_resource(np->parent, i,
&chip->rsrc[i])) {
@@ -1170,9 +1167,10 @@ int __init snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return)
chip->rsrc[i].start + 1,
rnames[i]) == NULL) {
printk(KERN_ERR "snd: can't request rsrc "
- " %d (%s: 0x%08lx:%08lx)\n",
- i, rnames[i], chip->rsrc[i].start,
- chip->rsrc[i].end);
+ " %d (%s: 0x%016llx:%016llx)\n",
+ i, rnames[i],
+ (unsigned long long)chip->rsrc[i].start,
+ (unsigned long long)chip->rsrc[i].end);
err = -ENODEV;
goto __error;
}
@@ -1184,10 +1182,6 @@ int __init snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return)
} else {
static char *rnames[] = {
"Sound Control", "Sound Tx DMA", "Sound Rx DMA" };
- if (np->n_intrs < 3) {
- err = -ENODEV;
- goto __error;
- }
for (i = 0; i < 3; i ++) {
if (of_address_to_resource(np, i,
&chip->rsrc[i])) {
@@ -1201,9 +1195,10 @@ int __init snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return)
chip->rsrc[i].start + 1,
rnames[i]) == NULL) {
printk(KERN_ERR "snd: can't request rsrc "
- " %d (%s: 0x%08lx:%08lx)\n",
- i, rnames[i], chip->rsrc[i].start,
- chip->rsrc[i].end);
+ " %d (%s: 0x%016llx:%016llx)\n",
+ i, rnames[i],
+ (unsigned long long)chip->rsrc[i].start,
+ (unsigned long long)chip->rsrc[i].end);
err = -ENODEV;
goto __error;
}
@@ -1218,28 +1213,30 @@ int __init snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return)
chip->playback.dma = ioremap(txdma_addr, 0x100);
chip->capture.dma = ioremap(rxdma_addr, 0x100);
if (chip->model <= PMAC_BURGUNDY) {
- if (request_irq(np->intrs[0].line, snd_pmac_ctrl_intr, 0,
+ irq = irq_of_parse_and_map(np, 0);
+ if (request_irq(irq, snd_pmac_ctrl_intr, 0,
"PMac", (void*)chip)) {
- snd_printk(KERN_ERR "pmac: unable to grab IRQ %d\n", np->intrs[0].line);
+ snd_printk(KERN_ERR "pmac: unable to grab IRQ %d\n",
+ irq);
err = -EBUSY;
goto __error;
}
- chip->irq = np->intrs[0].line;
+ chip->irq = irq;
}
- if (request_irq(np->intrs[1].line, snd_pmac_tx_intr, 0,
- "PMac Output", (void*)chip)) {
- snd_printk(KERN_ERR "pmac: unable to grab IRQ %d\n", np->intrs[1].line);
+ irq = irq_of_parse_and_map(np, 1);
+ if (request_irq(irq, snd_pmac_tx_intr, 0, "PMac Output", (void*)chip)){
+ snd_printk(KERN_ERR "pmac: unable to grab IRQ %d\n", irq);
err = -EBUSY;
goto __error;
}
- chip->tx_irq = np->intrs[1].line;
- if (request_irq(np->intrs[2].line, snd_pmac_rx_intr, 0,
- "PMac Input", (void*)chip)) {
- snd_printk(KERN_ERR "pmac: unable to grab IRQ %d\n", np->intrs[2].line);
+ chip->tx_irq = irq;
+ irq = irq_of_parse_and_map(np, 2);
+ if (request_irq(irq, snd_pmac_rx_intr, 0, "PMac Input", (void*)chip)) {
+ snd_printk(KERN_ERR "pmac: unable to grab IRQ %d\n", irq);
err = -EBUSY;
goto __error;
}
- chip->rx_irq = np->intrs[2].line;
+ chip->rx_irq = irq;
snd_pmac_sound_feature(chip, 1);
diff --git a/sound/ppc/powermac.c b/sound/ppc/powermac.c
index fa9a44ab487e..2264574fa06b 100644
--- a/sound/ppc/powermac.c
+++ b/sound/ppc/powermac.c
@@ -181,21 +181,14 @@ static int __init alsa_card_pmac_init(void)
if ((err = platform_driver_register(&snd_pmac_driver)) < 0)
return err;
device = platform_device_register_simple(SND_PMAC_DRIVER, -1, NULL, 0);
- if (!IS_ERR(device)) {
- if (platform_get_drvdata(device))
- return 0;
- platform_device_unregister(device);
- err = -ENODEV;
- } else
- err = PTR_ERR(device);
- platform_driver_unregister(&snd_pmac_driver);
- return err;
+ return 0;
}
static void __exit alsa_card_pmac_exit(void)
{
- platform_device_unregister(device);
+ if (!IS_ERR(device))
+ platform_device_unregister(device);
platform_driver_unregister(&snd_pmac_driver);
}
diff --git a/sound/ppc/toonie.c b/sound/ppc/toonie.c
deleted file mode 100644
index e69de29bb2d1..000000000000
--- a/sound/ppc/toonie.c
+++ /dev/null
diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c
index 70e4ebc70260..cdff53e4a17e 100644
--- a/sound/ppc/tumbler.c
+++ b/sound/ppc/tumbler.c
@@ -190,7 +190,7 @@ static int check_audio_gpio(struct pmac_gpio *gp)
ret = do_gpio_read(gp);
- return (ret & 0xd) == (gp->active_val & 0xd);
+ return (ret & 0x1) == (gp->active_val & 0x1);
}
static int read_audio_gpio(struct pmac_gpio *gp)
@@ -198,7 +198,8 @@ static int read_audio_gpio(struct pmac_gpio *gp)
int ret;
if (! gp->addr)
return 0;
- ret = ((do_gpio_read(gp) & 0x02) !=0);
+ ret = do_gpio_read(gp);
+ ret = (ret & 0x02) !=0;
return ret == gp->active_state;
}
@@ -1035,7 +1036,7 @@ static struct device_node *find_audio_device(const char *name)
return NULL;
for (np = np->child; np; np = np->sibling) {
- char *property = get_property(np, "audio-gpio", NULL);
+ const char *property = get_property(np, "audio-gpio", NULL);
if (property && strcmp(property, name) == 0)
return np;
}
@@ -1062,7 +1063,8 @@ static long tumbler_find_device(const char *device, const char *platform,
struct pmac_gpio *gp, int is_compatible)
{
struct device_node *node;
- u32 *base, addr;
+ const u32 *base;
+ u32 addr;
if (is_compatible)
node = find_compatible_audio_device(device);
@@ -1074,9 +1076,9 @@ static long tumbler_find_device(const char *device, const char *platform,
return -ENODEV;
}
- base = (u32 *)get_property(node, "AAPL,address", NULL);
+ base = get_property(node, "AAPL,address", NULL);
if (! base) {
- base = (u32 *)get_property(node, "reg", NULL);
+ base = get_property(node, "reg", NULL);
if (!base) {
DBG("(E) cannot find address for device %s !\n", device);
snd_printd("cannot find address for device %s\n", device);
@@ -1090,13 +1092,13 @@ static long tumbler_find_device(const char *device, const char *platform,
gp->addr = addr & 0x0000ffff;
/* Try to find the active state, default to 0 ! */
- base = (u32 *)get_property(node, "audio-gpio-active-state", NULL);
+ base = get_property(node, "audio-gpio-active-state", NULL);
if (base) {
gp->active_state = *base;
gp->active_val = (*base) ? 0x5 : 0x4;
gp->inactive_val = (*base) ? 0x4 : 0x5;
} else {
- u32 *prop = NULL;
+ const u32 *prop = NULL;
gp->active_state = 0;
gp->active_val = 0x4;
gp->inactive_val = 0x5;
@@ -1105,7 +1107,7 @@ static long tumbler_find_device(const char *device, const char *platform,
* as we don't yet have an interpreter for these things
*/
if (platform)
- prop = (u32 *)get_property(node, platform, NULL);
+ prop = get_property(node, platform, NULL);
if (prop) {
if (prop[3] == 0x9 && prop[4] == 0x9) {
gp->active_val = 0xd;
@@ -1121,7 +1123,7 @@ static long tumbler_find_device(const char *device, const char *platform,
DBG("(I) GPIO device %s found, offset: %x, active state: %d !\n",
device, gp->addr, gp->active_state);
- return (node->n_intrs > 0) ? node->intrs[0].line : 0;
+ return irq_of_parse_and_map(node, 0);
}
/* reset audio */
@@ -1264,16 +1266,16 @@ static int __init tumbler_init(struct snd_pmac *chip)
&mix->line_mute, 1);
irq = tumbler_find_device("headphone-detect",
NULL, &mix->hp_detect, 0);
- if (irq < 0)
+ if (irq <= NO_IRQ)
irq = tumbler_find_device("headphone-detect",
NULL, &mix->hp_detect, 1);
- if (irq < 0)
+ if (irq <= NO_IRQ)
irq = tumbler_find_device("keywest-gpio15",
NULL, &mix->hp_detect, 1);
mix->headphone_irq = irq;
irq = tumbler_find_device("line-output-detect",
NULL, &mix->line_detect, 0);
- if (irq < 0)
+ if (irq <= NO_IRQ)
irq = tumbler_find_device("line-output-detect",
NULL, &mix->line_detect, 1);
mix->lineout_irq = irq;
@@ -1316,10 +1318,9 @@ int __init snd_pmac_tumbler_init(struct snd_pmac *chip)
request_module("i2c-powermac");
#endif /* CONFIG_KMOD */
- mix = kmalloc(sizeof(*mix), GFP_KERNEL);
+ mix = kzalloc(sizeof(*mix), GFP_KERNEL);
if (! mix)
return -ENOMEM;
- memset(mix, 0, sizeof(*mix));
mix->headphone_irq = -1;
chip->mixer_data = mix;
diff --git a/sound/sound_core.c b/sound/sound_core.c
index 6f849720aef3..0b0a016ca6d6 100644
--- a/sound/sound_core.c
+++ b/sound/sound_core.c
@@ -34,7 +34,6 @@
* locking at some point in 2.3.x.
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
@@ -44,7 +43,6 @@
#include <linux/sound.h>
#include <linux/major.h>
#include <linux/kmod.h>
-#include <linux/devfs_fs_kernel.h>
#include <linux/device.h>
#define SOUND_STEP 16
@@ -172,8 +170,6 @@ static int sound_insert_unit(struct sound_unit **list, const struct file_operati
else
sprintf(s->name, "sound/%s%d", name, r / SOUND_STEP);
- devfs_mk_cdev(MKDEV(SOUND_MAJOR, s->unit_minor),
- S_IFCHR | mode, s->name);
class_device_create(sound_class, NULL, MKDEV(SOUND_MAJOR, s->unit_minor),
dev, s->name+6);
return r;
@@ -197,7 +193,6 @@ static void sound_remove_unit(struct sound_unit **list, int unit)
p = __sound_remove_unit(list, unit);
spin_unlock(&sound_loader_lock);
if (p) {
- devfs_remove(p->name);
class_device_destroy(sound_class, MKDEV(SOUND_MAJOR, p->unit_minor));
kfree(p);
}
@@ -556,10 +551,6 @@ int soundcore_open(struct inode *inode, struct file *file)
return -ENODEV;
}
-extern int mod_firmware_load(const char *, char **);
-EXPORT_SYMBOL(mod_firmware_load);
-
-
MODULE_DESCRIPTION("Core sound module");
MODULE_AUTHOR("Alan Cox");
MODULE_LICENSE("GPL");
@@ -570,7 +561,6 @@ static void __exit cleanup_soundcore(void)
/* We have nothing to really do here - we know the lists must be
empty */
unregister_chrdev(SOUND_MAJOR, "sound");
- devfs_remove("sound");
class_destroy(sound_class);
}
@@ -580,7 +570,6 @@ static int __init init_soundcore(void)
printk(KERN_ERR "soundcore: sound device already in use.\n");
return -EBUSY;
}
- devfs_mk_dir ("sound");
sound_class = class_create(THIS_MODULE, "sound");
if (IS_ERR(sound_class))
return PTR_ERR(sound_class);
diff --git a/sound/sound_firmware.c b/sound/sound_firmware.c
index 6ddadfac35ad..3a181d4c0dc6 100644
--- a/sound/sound_firmware.c
+++ b/sound/sound_firmware.c
@@ -4,6 +4,7 @@
#include <linux/mm.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
+#include "oss/sound_firmware.h"
static int do_mod_firmware_load(const char *fn, char **fp)
{
@@ -59,8 +60,7 @@ static int do_mod_firmware_load(const char *fn, char **fp)
* value zero on a failure.
*
* Caution: This API is not recommended. Firmware should be loaded via
- * an ioctl call and a setup application. This function may disappear
- * in future.
+ * request_firmware.
*/
int mod_firmware_load(const char *fn, char **fp)
@@ -73,4 +73,6 @@ int mod_firmware_load(const char *fn, char **fp)
set_fs(fs);
return r;
}
+EXPORT_SYMBOL(mod_firmware_load);
+MODULE_LICENSE("GPL");
diff --git a/sound/sparc/amd7930.c b/sound/sparc/amd7930.c
index dfe9bac7fa32..be0bd503f013 100644
--- a/sound/sparc/amd7930.c
+++ b/sound/sparc/amd7930.c
@@ -46,6 +46,7 @@
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/sbus.h>
+#include <asm/prom.h>
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
@@ -335,7 +336,6 @@ struct snd_amd7930 {
int pgain;
int mgain;
- struct sbus_dev *sdev;
unsigned int irq;
unsigned int regs_size;
struct snd_amd7930 *next;
@@ -755,7 +755,7 @@ static struct snd_pcm_ops snd_amd7930_capture_ops = {
.pointer = snd_amd7930_capture_pointer,
};
-static int __init snd_amd7930_pcm(struct snd_amd7930 *amd)
+static int __devinit snd_amd7930_pcm(struct snd_amd7930 *amd)
{
struct snd_pcm *pcm;
int err;
@@ -870,7 +870,7 @@ static int snd_amd7930_put_volume(struct snd_kcontrol *kctl, struct snd_ctl_elem
return change;
}
-static struct snd_kcontrol_new amd7930_controls[] __initdata = {
+static struct snd_kcontrol_new amd7930_controls[] __devinitdata = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Monitor Volume",
@@ -900,7 +900,7 @@ static struct snd_kcontrol_new amd7930_controls[] __initdata = {
},
};
-static int __init snd_amd7930_mixer(struct snd_amd7930 *amd)
+static int __devinit snd_amd7930_mixer(struct snd_amd7930 *amd)
{
struct snd_card *card;
int idx, err;
@@ -945,13 +945,11 @@ static struct snd_device_ops snd_amd7930_dev_ops = {
.dev_free = snd_amd7930_dev_free,
};
-static int __init snd_amd7930_create(struct snd_card *card,
- struct sbus_dev *sdev,
- struct resource *rp,
- unsigned int reg_size,
- struct linux_prom_irqs *irq_prop,
- int dev,
- struct snd_amd7930 **ramd)
+static int __devinit snd_amd7930_create(struct snd_card *card,
+ struct resource *rp,
+ unsigned int reg_size,
+ int irq, int dev,
+ struct snd_amd7930 **ramd)
{
unsigned long flags;
struct snd_amd7930 *amd;
@@ -964,7 +962,6 @@ static int __init snd_amd7930_create(struct snd_card *card,
spin_lock_init(&amd->lock);
amd->card = card;
- amd->sdev = sdev;
amd->regs_size = reg_size;
amd->regs = sbus_ioremap(rp, 0, amd->regs_size, "amd7930");
@@ -975,15 +972,14 @@ static int __init snd_amd7930_create(struct snd_card *card,
amd7930_idle(amd);
- if (request_irq(irq_prop->pri, snd_amd7930_interrupt,
- SA_INTERRUPT | SA_SHIRQ, "amd7930", amd)) {
+ if (request_irq(irq, snd_amd7930_interrupt,
+ IRQF_DISABLED | IRQF_SHARED, "amd7930", amd)) {
snd_printk("amd7930-%d: Unable to grab IRQ %d\n",
- dev,
- irq_prop->pri);
+ dev, irq);
snd_amd7930_free(amd);
return -EBUSY;
}
- amd->irq = irq_prop->pri;
+ amd->irq = irq;
amd7930_enable_ints(amd);
@@ -1017,60 +1013,35 @@ static int __init snd_amd7930_create(struct snd_card *card,
return 0;
}
-static int __init amd7930_attach(int prom_node, struct sbus_dev *sdev)
+static int __devinit amd7930_attach_common(struct resource *rp, int irq)
{
- static int dev;
- struct linux_prom_registers reg_prop;
- struct linux_prom_irqs irq_prop;
- struct resource res, *rp;
+ static int dev_num;
struct snd_card *card;
struct snd_amd7930 *amd;
int err;
- if (dev >= SNDRV_CARDS)
+ if (dev_num >= SNDRV_CARDS)
return -ENODEV;
- if (!enable[dev]) {
- dev++;
+ if (!enable[dev_num]) {
+ dev_num++;
return -ENOENT;
}
- err = prom_getproperty(prom_node, "intr",
- (char *) &irq_prop, sizeof(irq_prop));
- if (err < 0) {
- snd_printk("amd7930-%d: Firmware node lacks IRQ property.\n", dev);
- return -ENODEV;
- }
-
- err = prom_getproperty(prom_node, "reg",
- (char *) &reg_prop, sizeof(reg_prop));
- if (err < 0) {
- snd_printk("amd7930-%d: Firmware node lacks register property.\n", dev);
- return -ENODEV;
- }
-
- if (sdev) {
- rp = &sdev->resource[0];
- } else {
- rp = &res;
- rp->start = reg_prop.phys_addr;
- rp->end = rp->start + reg_prop.reg_size - 1;
- rp->flags = IORESOURCE_IO | (reg_prop.which_io & 0xff);
- }
-
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ card = snd_card_new(index[dev_num], id[dev_num], THIS_MODULE, 0);
if (card == NULL)
return -ENOMEM;
strcpy(card->driver, "AMD7930");
strcpy(card->shortname, "Sun AMD7930");
- sprintf(card->longname, "%s at 0x%02lx:0x%08lx, irq %d",
+ sprintf(card->longname, "%s at 0x%02lx:0x%08Lx, irq %d",
card->shortname,
rp->flags & 0xffL,
- rp->start,
- irq_prop.pri);
+ (unsigned long long)rp->start,
+ irq);
- if ((err = snd_amd7930_create(card, sdev, rp, reg_prop.reg_size,
- &irq_prop, dev, &amd)) < 0)
+ if ((err = snd_amd7930_create(card, rp,
+ (rp->end - rp->start) + 1,
+ irq, dev_num, &amd)) < 0)
goto out_err;
if ((err = snd_amd7930_pcm(amd)) < 0)
@@ -1085,7 +1056,8 @@ static int __init amd7930_attach(int prom_node, struct sbus_dev *sdev)
amd->next = amd7930_list;
amd7930_list = amd;
- dev++;
+ dev_num++;
+
return 0;
out_err:
@@ -1093,29 +1065,71 @@ out_err:
return err;
}
-static int __init amd7930_init(void)
+static int __devinit amd7930_obio_attach(struct device_node *dp)
+{
+ struct linux_prom_registers *regs;
+ struct linux_prom_irqs *irqp;
+ struct resource res, *rp;
+ int len;
+
+ irqp = of_get_property(dp, "intr", &len);
+ if (!irqp) {
+ snd_printk("%s: Firmware node lacks IRQ property.\n",
+ dp->full_name);
+ return -ENODEV;
+ }
+
+ regs = of_get_property(dp, "reg", &len);
+ if (!regs) {
+ snd_printk("%s: Firmware node lacks register property.\n",
+ dp->full_name);
+ return -ENODEV;
+ }
+
+ rp = &res;
+ rp->start = regs->phys_addr;
+ rp->end = rp->start + regs->reg_size - 1;
+ rp->flags = IORESOURCE_IO | (regs->which_io & 0xff);
+
+ return amd7930_attach_common(rp, irqp->pri);
+}
+
+static int __devinit amd7930_sbus_probe(struct of_device *dev, const struct of_device_id *match)
{
- struct sbus_bus *sbus;
- struct sbus_dev *sdev;
- int node, found;
+ struct sbus_dev *sdev = to_sbus_device(&dev->dev);
- found = 0;
+ return amd7930_attach_common(&sdev->resource[0], sdev->irqs[0]);
+}
+
+static struct of_device_id amd7930_match[] = {
+ {
+ .name = "audio",
+ },
+ {},
+};
+
+static struct of_platform_driver amd7930_sbus_driver = {
+ .name = "audio",
+ .match_table = amd7930_match,
+ .probe = amd7930_sbus_probe,
+};
+
+static int __init amd7930_init(void)
+{
+ struct device_node *dp;
/* Try to find the sun4c "audio" node first. */
- node = prom_getchild(prom_root_node);
- node = prom_searchsiblings(node, "audio");
- if (node && amd7930_attach(node, NULL) == 0)
- found++;
+ dp = of_find_node_by_path("/");
+ dp = dp->child;
+ while (dp) {
+ if (!strcmp(dp->name, "audio"))
+ amd7930_obio_attach(dp);
- /* Probe each SBUS for amd7930 chips. */
- for_all_sbusdev(sdev, sbus) {
- if (!strcmp(sdev->prom_name, "audio")) {
- if (amd7930_attach(sdev->prom_node, sdev) == 0)
- found++;
- }
+ dp = dp->sibling;
}
- return (found > 0) ? 0 : -EIO;
+ /* Probe each SBUS for amd7930 chips. */
+ return of_register_driver(&amd7930_sbus_driver, &sbus_bus_type);
}
static void __exit amd7930_exit(void)
@@ -1131,6 +1145,8 @@ static void __exit amd7930_exit(void)
}
amd7930_list = NULL;
+
+ of_unregister_driver(&amd7930_sbus_driver);
}
module_init(amd7930_init);
diff --git a/sound/sparc/cs4231.c b/sound/sparc/cs4231.c
index b3efc9aa2916..9a06c3bd6944 100644
--- a/sound/sparc/cs4231.c
+++ b/sound/sparc/cs4231.c
@@ -8,7 +8,6 @@
* Copyright (c) by Jaroslav Kysela <perex@suse.cz>
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
@@ -2002,7 +2001,7 @@ static int __init snd_cs4231_sbus_create(struct snd_card *card,
chip->c_dma.preallocate = sbus_dma_preallocate;
if (request_irq(sdev->irqs[0], snd_cs4231_sbus_interrupt,
- SA_SHIRQ, "cs4231", chip)) {
+ IRQF_SHARED, "cs4231", chip)) {
snd_printdd("cs4231-%d: Unable to grab SBUS IRQ %d\n",
dev, sdev->irqs[0]);
snd_cs4231_sbus_free(chip);
@@ -2037,10 +2036,10 @@ static int __init cs4231_sbus_attach(struct sbus_dev *sdev)
if (err)
return err;
- sprintf(card->longname, "%s at 0x%02lx:0x%08lx, irq %d",
+ sprintf(card->longname, "%s at 0x%02lx:0x%016Lx, irq %d",
card->shortname,
rp->flags & 0xffL,
- rp->start,
+ (unsigned long long)rp->start,
sdev->irqs[0]);
if ((err = snd_cs4231_sbus_create(card, sdev, dev, &cp)) < 0) {
@@ -2284,15 +2283,14 @@ static int __init cs4231_init(void)
for_each_ebusdev(edev, ebus) {
int match = 0;
- if (!strcmp(edev->prom_name, "SUNW,CS4231")) {
+ if (!strcmp(edev->prom_node->name, "SUNW,CS4231")) {
match = 1;
- } else if (!strcmp(edev->prom_name, "audio")) {
- char compat[16];
+ } else if (!strcmp(edev->prom_node->name, "audio")) {
+ char *compat;
- prom_getstring(edev->prom_node, "compatible",
- compat, sizeof(compat));
- compat[15] = '\0';
- if (!strcmp(compat, "SUNW,CS4231"))
+ compat = of_get_property(edev->prom_node,
+ "compatible", NULL);
+ if (compat && !strcmp(compat, "SUNW,CS4231"))
match = 1;
}
diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c
index 5eecdd09a79d..5a97be689b40 100644
--- a/sound/sparc/dbri.c
+++ b/sound/sparc/dbri.c
@@ -2,6 +2,8 @@
* Driver for DBRI sound chip found on Sparcs.
* Copyright (C) 2004, 2005 Martin Habets (mhabets@users.sourceforge.net)
*
+ * Converted to ring buffered version by Krzysztof Helt (krzysztof.h1@wp.pl)
+ *
* Based entirely upon drivers/sbus/audio/dbri.c which is:
* Copyright (C) 1997 Rudolf Koenig (rfkoenig@immd4.informatik.uni-erlangen.de)
* Copyright (C) 1998, 1999 Brent Baccala (baccala@freesoft.org)
@@ -34,7 +36,7 @@
* (the second one is a monitor/tee pipe, valid only for serial input).
*
* The mmcodec is connected via the CHI bus and needs the data & some
- * parameters (volume, balance, output selection) timemultiplexed in 8 byte
+ * parameters (volume, output selection) timemultiplexed in 8 byte
* chunks. It also has a control mode, which serves for audio format setting.
*
* Looking at the CS4215 data sheet it is easy to set up 2 or 4 codecs on
@@ -46,7 +48,7 @@
*
* I've tried to stick to the following function naming conventions:
* snd_* ALSA stuff
- * cs4215_* CS4215 codec specfic stuff
+ * cs4215_* CS4215 codec specific stuff
* dbri_* DBRI high-level stuff
* other DBRI low-level stuff
*/
@@ -83,7 +85,7 @@ MODULE_PARM_DESC(id, "ID string for Sun DBRI soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable Sun DBRI soundcard.");
-#define DBRI_DEBUG
+#undef DBRI_DEBUG
#define D_INT (1<<0)
#define D_GEN (1<<1)
@@ -104,17 +106,15 @@ static char *cmds[] = {
#define dprintk(a, x...) if(dbri_debug & a) printk(KERN_DEBUG x)
-#define DBRI_CMD(cmd, intr, value) ((cmd << 28) | \
- (1 << 27) | \
- value)
#else
-#define dprintk(a, x...)
+#define dprintk(a, x...) do { } while (0)
-#define DBRI_CMD(cmd, intr, value) ((cmd << 28) | \
- (intr << 27) | \
- value)
#endif /* DBRI_DEBUG */
+#define DBRI_CMD(cmd, intr, value) ((cmd << 28) | \
+ (intr << 27) | \
+ value)
+
/***************************************************************************
CS4215 specific definitions and structures
****************************************************************************/
@@ -160,7 +160,7 @@ static struct {
/* { NA, (1 << 4), (5 << 3) }, */
{ 48000, (1 << 4), (6 << 3) },
{ 9600, (1 << 4), (7 << 3) },
- { 5513, (2 << 4), (0 << 3) }, /* Actually 5512.5 */
+ { 5512, (2 << 4), (0 << 3) }, /* Actually 5512.5 */
{ 11025, (2 << 4), (1 << 3) },
{ 18900, (2 << 4), (2 << 3) },
{ 22050, (2 << 4), (3 << 3) },
@@ -240,28 +240,21 @@ static struct {
#define REG9 0x24UL /* Interrupt Queue Pointer */
#define DBRI_NO_CMDS 64
-#define DBRI_NO_INTS 1 /* Note: the value of this define was
- * originally 2. The ringbuffer to store
- * interrupts in dma is currently broken.
- * This is a temporary fix until the ringbuffer
- * is fixed.
- */
#define DBRI_INT_BLK 64
#define DBRI_NO_DESCS 64
#define DBRI_NO_PIPES 32
-
-#define DBRI_MM_ONB 1
-#define DBRI_MM_SB 2
+#define DBRI_MAX_PIPE (DBRI_NO_PIPES - 1)
#define DBRI_REC 0
#define DBRI_PLAY 1
#define DBRI_NO_STREAMS 2
/* One transmit/receive descriptor */
+/* When ba != 0 descriptor is used */
struct dbri_mem {
volatile __u32 word1;
- volatile __u32 ba; /* Transmit/Receive Buffer Address */
- volatile __u32 nda; /* Next Descriptor Address */
+ __u32 ba; /* Transmit/Receive Buffer Address */
+ __u32 nda; /* Next Descriptor Address */
volatile __u32 word4;
};
@@ -269,8 +262,8 @@ struct dbri_mem {
* the CPU and the DBRI
*/
struct dbri_dma {
- volatile s32 cmd[DBRI_NO_CMDS]; /* Place for commands */
- volatile s32 intr[DBRI_NO_INTS * DBRI_INT_BLK]; /* Interrupt field */
+ s32 cmd[DBRI_NO_CMDS]; /* Place for commands */
+ volatile s32 intr[DBRI_INT_BLK]; /* Interrupt field */
struct dbri_mem desc[DBRI_NO_DESCS]; /* Xmit/receive descriptors */
};
@@ -282,58 +275,43 @@ enum in_or_out { PIPEinput, PIPEoutput };
struct dbri_pipe {
u32 sdp; /* SDP command word */
- enum in_or_out direction;
int nextpipe; /* Next pipe in linked list */
- int prevpipe;
- int cycle; /* Offset of timeslot (bits) */
int length; /* Length of timeslot (bits) */
int first_desc; /* Index of first descriptor */
int desc; /* Index of active descriptor */
volatile __u32 *recv_fixed_ptr; /* Ptr to receive fixed data */
};
-struct dbri_desc {
- int inuse; /* Boolean flag */
- int next; /* Index of next desc, or -1 */
- unsigned int len;
-};
-
/* Per stream (playback or record) information */
struct dbri_streaminfo {
struct snd_pcm_substream *substream;
u32 dvma_buffer; /* Device view of Alsa DMA buffer */
- int left; /* # of bytes left in DMA buffer */
int size; /* Size of DMA buffer */
size_t offset; /* offset in user buffer */
int pipe; /* Data pipe used */
int left_gain; /* mixer elements */
int right_gain;
- int balance;
};
/* This structure holds the information for both chips (DBRI & CS4215) */
struct snd_dbri {
struct snd_card *card; /* ALSA card */
- struct snd_pcm *pcm;
int regs_size, irq; /* Needed for unload */
struct sbus_dev *sdev; /* SBUS device info */
spinlock_t lock;
- volatile struct dbri_dma *dma; /* Pointer to our DMA block */
+ struct dbri_dma *dma; /* Pointer to our DMA block */
u32 dma_dvma; /* DBRI visible DMA address */
void __iomem *regs; /* dbri HW regs */
- int dbri_version; /* 'e' and up is OK */
int dbri_irqp; /* intr queue pointer */
- int wait_send; /* sequence of command buffers send */
- int wait_ackd; /* sequence of command buffers acknowledged */
struct dbri_pipe pipes[DBRI_NO_PIPES]; /* DBRI's 32 data pipes */
- struct dbri_desc descs[DBRI_NO_DESCS];
+ int next_desc[DBRI_NO_DESCS]; /* Index of next desc, or -1 */
+ spinlock_t cmdlock; /* Protects cmd queue accesses */
+ s32 *cmdptr; /* Pointer to the last queued cmd */
- int chi_in_pipe;
- int chi_out_pipe;
int chi_bpf;
struct cs4215 mm; /* mmcodec special info */
@@ -345,8 +323,6 @@ struct snd_dbri {
#define DBRI_MAX_VOLUME 63 /* Output volume */
#define DBRI_MAX_GAIN 15 /* Input gain */
-#define DBRI_RIGHT_BALANCE 255
-#define DBRI_MID_BALANCE (DBRI_RIGHT_BALANCE >> 1)
/* DBRI Reg0 - Status Control Register - defines. (Page 17) */
#define D_P (1<<15) /* Program command & queue pointer valid */
@@ -569,7 +545,7 @@ struct snd_dbri {
#define DBRI_TD_TBC (1<<0) /* Transmit buffer Complete */
#define DBRI_TD_STATUS(v) ((v)&0xff) /* Transmit status */
/* Maximum buffer size per TD: almost 8Kb */
-#define DBRI_TD_MAXCNT ((1 << 13) - 1)
+#define DBRI_TD_MAXCNT ((1 << 13) - 4)
/* Receive descriptor defines */
#define DBRI_RD_F (1<<31) /* End of Frame */
@@ -633,93 +609,124 @@ The list is terminated with a WAIT command, which generates a
CPU interrupt to signal completion.
Since the DBRI can run in parallel with the CPU, several means of
-synchronization present themselves. The method implemented here is close
-to the original scheme (Rudolf's), and uses 2 counters (wait_send and
-wait_ackd) to synchronize the command buffer between the CPU and the DBRI.
+synchronization present themselves. The method implemented here is only
+use of the dbri_cmdwait() to wait for execution of batch of sent commands.
-A more sophisticated scheme might involve a circular command buffer
-or an array of command buffers. A routine could fill one with
-commands and link it onto a list. When a interrupt signaled
-completion of the current command buffer, look on the list for
-the next one.
+A circular command buffer is used here. A new command is being added
+while another can be executed. The scheme works by adding two WAIT commands
+after each sent batch of commands. When the next batch is prepared it is
+added after the WAIT commands then the WAITs are replaced with single JUMP
+command to the new batch. The the DBRI is forced to reread the last WAIT
+command (replaced by the JUMP by then). If the DBRI is still executing
+previous commands the request to reread the WAIT command is ignored.
Every time a routine wants to write commands to the DBRI, it must
-first call dbri_cmdlock() and get an initial pointer into dbri->dma->cmd
-in return. dbri_cmdlock() will block if the previous commands have not
-been completed yet. After this the commands can be written to the buffer,
-and dbri_cmdsend() is called with the final pointer value to send them
-to the DBRI.
+first call dbri_cmdlock() and get pointer to a free space in
+dbri->dma->cmd buffer. After this, the commands can be written to
+the buffer, and dbri_cmdsend() is called with the final pointer value
+to send them to the DBRI.
*/
-static void dbri_process_interrupt_buffer(struct snd_dbri * dbri);
-
-enum dbri_lock { NoGetLock, GetLock };
-#define MAXLOOPS 10
-
-static volatile s32 *dbri_cmdlock(struct snd_dbri * dbri, enum dbri_lock get)
+#define MAXLOOPS 20
+/*
+ * Wait for the current command string to execute
+ */
+static void dbri_cmdwait(struct snd_dbri *dbri)
{
int maxloops = MAXLOOPS;
-
-#ifndef SMP
- if ((get == GetLock) && spin_is_locked(&dbri->lock)) {
- printk(KERN_ERR "DBRI: cmdlock called while in spinlock.");
- }
-#endif
+ unsigned long flags;
/* Delay if previous commands are still being processed */
- while ((--maxloops) > 0 && (dbri->wait_send != dbri->wait_ackd)) {
+ spin_lock_irqsave(&dbri->lock, flags);
+ while ((--maxloops) > 0 && (sbus_readl(dbri->regs + REG0) & D_P)) {
+ spin_unlock_irqrestore(&dbri->lock, flags);
msleep_interruptible(1);
- /* If dbri_cmdlock() got called from inside the
- * interrupt handler, this will do the processing.
- */
- dbri_process_interrupt_buffer(dbri);
+ spin_lock_irqsave(&dbri->lock, flags);
}
+ spin_unlock_irqrestore(&dbri->lock, flags);
+
if (maxloops == 0) {
- printk(KERN_ERR "DBRI: Chip never completed command buffer %d\n",
- dbri->wait_send);
+ printk(KERN_ERR "DBRI: Chip never completed command buffer\n");
} else {
dprintk(D_CMD, "Chip completed command buffer (%d)\n",
MAXLOOPS - maxloops - 1);
}
+}
+/*
+ * Lock the command queue and returns pointer to a space for len cmd words
+ * It locks the cmdlock spinlock.
+ */
+static s32 *dbri_cmdlock(struct snd_dbri * dbri, int len)
+{
+ /* Space for 2 WAIT cmds (replaced later by 1 JUMP cmd) */
+ len += 2;
+ spin_lock(&dbri->cmdlock);
+ if (dbri->cmdptr - dbri->dma->cmd + len < DBRI_NO_CMDS - 2)
+ return dbri->cmdptr + 2;
+ else if (len < sbus_readl(dbri->regs + REG8) - dbri->dma_dvma)
+ return dbri->dma->cmd;
+ else
+ printk(KERN_ERR "DBRI: no space for commands.");
- /*if (get == GetLock) spin_lock(&dbri->lock); */
- return &dbri->dma->cmd[0];
+ return NULL;
}
-static void dbri_cmdsend(struct snd_dbri * dbri, volatile s32 * cmd)
+/*
+ * Send prepared cmd string. It works by writting a JUMP cmd into
+ * the last WAIT cmd and force DBRI to reread the cmd.
+ * The JUMP cmd points to the new cmd string.
+ * It also releases the cmdlock spinlock.
+ *
+ * Lock must not be held before calling this.
+ */
+static void dbri_cmdsend(struct snd_dbri * dbri, s32 * cmd,int len)
{
- volatile s32 *ptr;
- u32 reg;
+ s32 tmp, addr;
+ static int wait_id = 0;
- for (ptr = &dbri->dma->cmd[0]; ptr < cmd; ptr++) {
- dprintk(D_CMD, "cmd: %lx:%08x\n", (unsigned long)ptr, *ptr);
- }
+ wait_id++;
+ wait_id &= 0xffff; /* restrict it to a 16 bit counter. */
+ *(cmd) = DBRI_CMD(D_WAIT, 1, wait_id);
+ *(cmd+1) = DBRI_CMD(D_WAIT, 1, wait_id);
- if ((cmd - &dbri->dma->cmd[0]) >= DBRI_NO_CMDS - 1) {
- printk(KERN_ERR "DBRI: Command buffer overflow! (bug in driver)\n");
- /* Ignore the last part. */
- cmd = &dbri->dma->cmd[DBRI_NO_CMDS - 3];
- }
+ /* Replace the last command with JUMP */
+ addr = dbri->dma_dvma + (cmd - len - dbri->dma->cmd) * sizeof(s32);
+ *(dbri->cmdptr+1) = addr;
+ *(dbri->cmdptr) = DBRI_CMD(D_JUMP, 0, 0);
- dbri->wait_send++;
- dbri->wait_send &= 0xffff; /* restrict it to a 16 bit counter. */
- *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
- *(cmd++) = DBRI_CMD(D_WAIT, 1, dbri->wait_send);
+#ifdef DBRI_DEBUG
+ if (cmd > dbri->cmdptr) {
+ s32 *ptr;
+
+ for (ptr = dbri->cmdptr; ptr < cmd+2; ptr++)
+ dprintk(D_CMD, "cmd: %lx:%08x\n", (unsigned long)ptr, *ptr);
+ } else {
+ s32 *ptr = dbri->cmdptr;
- /* Set command pointer and signal it is valid. */
- sbus_writel(dbri->dma_dvma, dbri->regs + REG8);
- reg = sbus_readl(dbri->regs + REG0);
- reg |= D_P;
- sbus_writel(reg, dbri->regs + REG0);
+ dprintk(D_CMD, "cmd: %lx:%08x\n", (unsigned long)ptr, *ptr);
+ ptr++;
+ dprintk(D_CMD, "cmd: %lx:%08x\n", (unsigned long)ptr, *ptr);
+ for (ptr = dbri->dma->cmd; ptr < cmd+2; ptr++) {
+ dprintk(D_CMD, "cmd: %lx:%08x\n", (unsigned long)ptr, *ptr);
+ }
+ }
+#endif
+
+ /* Reread the last command */
+ tmp = sbus_readl(dbri->regs + REG0);
+ tmp |= D_P;
+ sbus_writel(tmp, dbri->regs + REG0);
- /*spin_unlock(&dbri->lock); */
+ dbri->cmdptr = cmd;
+ spin_unlock(&dbri->cmdlock);
}
/* Lock must be held when calling this */
static void dbri_reset(struct snd_dbri * dbri)
{
int i;
+ u32 tmp;
dprintk(D_GEN, "reset 0:%x 2:%x 8:%x 9:%x\n",
sbus_readl(dbri->regs + REG0),
@@ -729,13 +736,20 @@ static void dbri_reset(struct snd_dbri * dbri)
sbus_writel(D_R, dbri->regs + REG0); /* Soft Reset */
for (i = 0; (sbus_readl(dbri->regs + REG0) & D_R) && i < 64; i++)
udelay(10);
+
+ /* A brute approach - DBRI falls back to working burst size by itself
+ * On SS20 D_S does not work, so do not try so high. */
+ tmp = sbus_readl(dbri->regs + REG0);
+ tmp |= D_G | D_E;
+ tmp &= ~D_S;
+ sbus_writel(tmp, dbri->regs + REG0);
}
/* Lock must not be held before calling this */
static void dbri_initialize(struct snd_dbri * dbri)
{
- volatile s32 *cmd;
- u32 dma_addr, tmp;
+ s32 *cmd;
+ u32 dma_addr;
unsigned long flags;
int n;
@@ -743,42 +757,34 @@ static void dbri_initialize(struct snd_dbri * dbri)
dbri_reset(dbri);
- cmd = dbri_cmdlock(dbri, NoGetLock);
- dprintk(D_GEN, "init: cmd: %p, int: %p\n",
- &dbri->dma->cmd[0], &dbri->dma->intr[0]);
+ /* Initialize pipes */
+ for (n = 0; n < DBRI_NO_PIPES; n++)
+ dbri->pipes[n].desc = dbri->pipes[n].first_desc = -1;
+ spin_lock_init(&dbri->cmdlock);
/*
* Initialize the interrupt ringbuffer.
*/
- for (n = 0; n < DBRI_NO_INTS - 1; n++) {
- dma_addr = dbri->dma_dvma;
- dma_addr += dbri_dma_off(intr, ((n + 1) & DBRI_INT_BLK));
- dbri->dma->intr[n * DBRI_INT_BLK] = dma_addr;
- }
dma_addr = dbri->dma_dvma + dbri_dma_off(intr, 0);
- dbri->dma->intr[n * DBRI_INT_BLK] = dma_addr;
+ dbri->dma->intr[0] = dma_addr;
dbri->dbri_irqp = 1;
-
- /* Initialize pipes */
- for (n = 0; n < DBRI_NO_PIPES; n++)
- dbri->pipes[n].desc = dbri->pipes[n].first_desc = -1;
-
- /* A brute approach - DBRI falls back to working burst size by itself
- * On SS20 D_S does not work, so do not try so high. */
- tmp = sbus_readl(dbri->regs + REG0);
- tmp |= D_G | D_E;
- tmp &= ~D_S;
- sbus_writel(tmp, dbri->regs + REG0);
-
/*
* Set up the interrupt queue
*/
- dma_addr = dbri->dma_dvma + dbri_dma_off(intr, 0);
+ spin_lock(&dbri->cmdlock);
+ cmd = dbri->cmdptr = dbri->dma->cmd;
*(cmd++) = DBRI_CMD(D_IIQ, 0, 0);
*(cmd++) = dma_addr;
+ *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
+ dbri->cmdptr = cmd;
+ *(cmd++) = DBRI_CMD(D_WAIT, 1, 0);
+ *(cmd++) = DBRI_CMD(D_WAIT, 1, 0);
+ dma_addr = dbri->dma_dvma + dbri_dma_off(cmd, 0);
+ sbus_writel(dma_addr, dbri->regs + REG8);
+ spin_unlock(&dbri->cmdlock);
- dbri_cmdsend(dbri, cmd);
spin_unlock_irqrestore(&dbri->lock, flags);
+ dbri_cmdwait(dbri);
}
/*
@@ -809,9 +815,9 @@ static void reset_pipe(struct snd_dbri * dbri, int pipe)
{
int sdp;
int desc;
- volatile int *cmd;
+ s32 *cmd;
- if (pipe < 0 || pipe > 31) {
+ if (pipe < 0 || pipe > DBRI_MAX_PIPE) {
printk(KERN_ERR "DBRI: reset_pipe called with illegal pipe number\n");
return;
}
@@ -822,25 +828,29 @@ static void reset_pipe(struct snd_dbri * dbri, int pipe)
return;
}
- cmd = dbri_cmdlock(dbri, NoGetLock);
+ cmd = dbri_cmdlock(dbri, 3);
*(cmd++) = DBRI_CMD(D_SDP, 0, sdp | D_SDP_C | D_SDP_P);
*(cmd++) = 0;
- dbri_cmdsend(dbri, cmd);
+ *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
+ dbri_cmdsend(dbri, cmd, 3);
desc = dbri->pipes[pipe].first_desc;
- while (desc != -1) {
- dbri->descs[desc].inuse = 0;
- desc = dbri->descs[desc].next;
- }
+ if ( desc >= 0)
+ do {
+ dbri->dma->desc[desc].nda = dbri->dma->desc[desc].ba = 0;
+ desc = dbri->next_desc[desc];
+ } while (desc != -1 && desc != dbri->pipes[pipe].first_desc);
dbri->pipes[pipe].desc = -1;
dbri->pipes[pipe].first_desc = -1;
}
-/* FIXME: direction as an argument? */
+/*
+ * Lock must be held before calling this.
+ */
static void setup_pipe(struct snd_dbri * dbri, int pipe, int sdp)
{
- if (pipe < 0 || pipe > 31) {
+ if (pipe < 0 || pipe > DBRI_MAX_PIPE) {
printk(KERN_ERR "DBRI: setup_pipe called with illegal pipe number\n");
return;
}
@@ -860,119 +870,87 @@ static void setup_pipe(struct snd_dbri * dbri, int pipe, int sdp)
dbri->pipes[pipe].sdp = sdp;
dbri->pipes[pipe].desc = -1;
dbri->pipes[pipe].first_desc = -1;
- if (sdp & D_SDP_TO_SER)
- dbri->pipes[pipe].direction = PIPEoutput;
- else
- dbri->pipes[pipe].direction = PIPEinput;
reset_pipe(dbri, pipe);
}
-/* FIXME: direction not needed */
+/*
+ * Lock must be held before calling this.
+ */
static void link_time_slot(struct snd_dbri * dbri, int pipe,
- enum in_or_out direction, int basepipe,
+ int prevpipe, int nextpipe,
int length, int cycle)
{
- volatile s32 *cmd;
+ s32 *cmd;
int val;
- int prevpipe;
- int nextpipe;
- if (pipe < 0 || pipe > 31 || basepipe < 0 || basepipe > 31) {
+ if (pipe < 0 || pipe > DBRI_MAX_PIPE
+ || prevpipe < 0 || prevpipe > DBRI_MAX_PIPE
+ || nextpipe < 0 || nextpipe > DBRI_MAX_PIPE) {
printk(KERN_ERR
"DBRI: link_time_slot called with illegal pipe number\n");
return;
}
- if (dbri->pipes[pipe].sdp == 0 || dbri->pipes[basepipe].sdp == 0) {
+ if (dbri->pipes[pipe].sdp == 0
+ || dbri->pipes[prevpipe].sdp == 0
+ || dbri->pipes[nextpipe].sdp == 0) {
printk(KERN_ERR "DBRI: link_time_slot called on uninitialized pipe\n");
return;
}
- /* Deal with CHI special case:
- * "If transmission on edges 0 or 1 is desired, then cycle n
- * (where n = # of bit times per frame...) must be used."
- * - DBRI data sheet, page 11
- */
- if (basepipe == 16 && direction == PIPEoutput && cycle == 0)
- cycle = dbri->chi_bpf;
-
- if (basepipe == pipe) {
- prevpipe = pipe;
- nextpipe = pipe;
- } else {
- /* We're not initializing a new linked list (basepipe != pipe),
- * so run through the linked list and find where this pipe
- * should be sloted in, based on its cycle. CHI confuses
- * things a bit, since it has a single anchor for both its
- * transmit and receive lists.
- */
- if (basepipe == 16) {
- if (direction == PIPEinput) {
- prevpipe = dbri->chi_in_pipe;
- } else {
- prevpipe = dbri->chi_out_pipe;
- }
- } else {
- prevpipe = basepipe;
- }
-
- nextpipe = dbri->pipes[prevpipe].nextpipe;
-
- while (dbri->pipes[nextpipe].cycle < cycle
- && dbri->pipes[nextpipe].nextpipe != basepipe) {
- prevpipe = nextpipe;
- nextpipe = dbri->pipes[nextpipe].nextpipe;
- }
- }
-
- if (prevpipe == 16) {
- if (direction == PIPEinput) {
- dbri->chi_in_pipe = pipe;
- } else {
- dbri->chi_out_pipe = pipe;
- }
- } else {
- dbri->pipes[prevpipe].nextpipe = pipe;
- }
-
+ dbri->pipes[prevpipe].nextpipe = pipe;
dbri->pipes[pipe].nextpipe = nextpipe;
- dbri->pipes[pipe].cycle = cycle;
dbri->pipes[pipe].length = length;
- cmd = dbri_cmdlock(dbri, NoGetLock);
+ cmd = dbri_cmdlock(dbri, 4);
- if (direction == PIPEinput) {
- val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(prevpipe) | pipe;
+ if (dbri->pipes[pipe].sdp & D_SDP_TO_SER) {
+ /* Deal with CHI special case:
+ * "If transmission on edges 0 or 1 is desired, then cycle n
+ * (where n = # of bit times per frame...) must be used."
+ * - DBRI data sheet, page 11
+ */
+ if (prevpipe == 16 && cycle == 0)
+ cycle = dbri->chi_bpf;
+
+ val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(prevpipe) | pipe;
*(cmd++) = DBRI_CMD(D_DTS, 0, val);
+ *(cmd++) = 0;
*(cmd++) =
D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe);
- *(cmd++) = 0;
} else {
- val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(prevpipe) | pipe;
+ val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(prevpipe) | pipe;
*(cmd++) = DBRI_CMD(D_DTS, 0, val);
- *(cmd++) = 0;
*(cmd++) =
D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe);
+ *(cmd++) = 0;
}
+ *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
- dbri_cmdsend(dbri, cmd);
+ dbri_cmdsend(dbri, cmd, 4);
}
+#if 0
+/*
+ * Lock must be held before calling this.
+ */
static void unlink_time_slot(struct snd_dbri * dbri, int pipe,
enum in_or_out direction, int prevpipe,
int nextpipe)
{
- volatile s32 *cmd;
+ s32 *cmd;
int val;
- if (pipe < 0 || pipe > 31 || prevpipe < 0 || prevpipe > 31) {
+ if (pipe < 0 || pipe > DBRI_MAX_PIPE
+ || prevpipe < 0 || prevpipe > DBRI_MAX_PIPE
+ || nextpipe < 0 || nextpipe > DBRI_MAX_PIPE) {
printk(KERN_ERR
"DBRI: unlink_time_slot called with illegal pipe number\n");
return;
}
- cmd = dbri_cmdlock(dbri, NoGetLock);
+ cmd = dbri_cmdlock(dbri, 4);
if (direction == PIPEinput) {
val = D_DTS_VI | D_DTS_DEL | D_DTS_PRVIN(prevpipe) | pipe;
@@ -985,9 +963,11 @@ static void unlink_time_slot(struct snd_dbri * dbri, int pipe,
*(cmd++) = 0;
*(cmd++) = D_TS_NEXT(nextpipe);
}
+ *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
- dbri_cmdsend(dbri, cmd);
+ dbri_cmdsend(dbri, cmd, 4);
}
+#endif
/* xmit_fixed() / recv_fixed()
*
@@ -1001,13 +981,16 @@ static void unlink_time_slot(struct snd_dbri * dbri, int pipe,
* the actual time slot is. The interrupt handler takes care of bit
* ordering and alignment. An 8-bit time slot will always end up
* in the low-order 8 bits, filled either MSB-first or LSB-first,
- * depending on the settings passed to setup_pipe()
+ * depending on the settings passed to setup_pipe().
+ *
+ * Lock must not be held before calling it.
*/
static void xmit_fixed(struct snd_dbri * dbri, int pipe, unsigned int data)
{
- volatile s32 *cmd;
+ s32 *cmd;
+ unsigned long flags;
- if (pipe < 16 || pipe > 31) {
+ if (pipe < 16 || pipe > DBRI_MAX_PIPE) {
printk(KERN_ERR "DBRI: xmit_fixed: Illegal pipe number\n");
return;
}
@@ -1032,17 +1015,22 @@ static void xmit_fixed(struct snd_dbri * dbri, int pipe, unsigned int data)
if (dbri->pipes[pipe].sdp & D_SDP_MSB)
data = reverse_bytes(data, dbri->pipes[pipe].length);
- cmd = dbri_cmdlock(dbri, GetLock);
+ cmd = dbri_cmdlock(dbri, 3);
*(cmd++) = DBRI_CMD(D_SSP, 0, pipe);
*(cmd++) = data;
+ *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
+
+ spin_lock_irqsave(&dbri->lock, flags);
+ dbri_cmdsend(dbri, cmd, 3);
+ spin_unlock_irqrestore(&dbri->lock, flags);
+ dbri_cmdwait(dbri);
- dbri_cmdsend(dbri, cmd);
}
static void recv_fixed(struct snd_dbri * dbri, int pipe, volatile __u32 * ptr)
{
- if (pipe < 16 || pipe > 31) {
+ if (pipe < 16 || pipe > DBRI_MAX_PIPE) {
printk(KERN_ERR "DBRI: recv_fixed called with illegal pipe number\n");
return;
}
@@ -1071,12 +1059,16 @@ static void recv_fixed(struct snd_dbri * dbri, int pipe, volatile __u32 * ptr)
* and work by building chains of descriptors which identify the
* data buffers. Buffers too large for a single descriptor will
* be spread across multiple descriptors.
+ *
+ * All descriptors create a ring buffer.
+ *
+ * Lock must be held before calling this.
*/
static int setup_descs(struct snd_dbri * dbri, int streamno, unsigned int period)
{
struct dbri_streaminfo *info = &dbri->stream_info[streamno];
__u32 dvma_buffer;
- int desc = 0;
+ int desc;
int len;
int first_desc = -1;
int last_desc = -1;
@@ -1119,11 +1111,23 @@ static int setup_descs(struct snd_dbri * dbri, int streamno, unsigned int period
len &= ~3;
}
+ /* Free descriptors if pipe has any */
+ desc = dbri->pipes[info->pipe].first_desc;
+ if ( desc >= 0)
+ do {
+ dbri->dma->desc[desc].nda = dbri->dma->desc[desc].ba = 0;
+ desc = dbri->next_desc[desc];
+ } while (desc != -1 && desc != dbri->pipes[info->pipe].first_desc);
+
+ dbri->pipes[info->pipe].desc = -1;
+ dbri->pipes[info->pipe].first_desc = -1;
+
+ desc = 0;
while (len > 0) {
int mylen;
for (; desc < DBRI_NO_DESCS; desc++) {
- if (!dbri->descs[desc].inuse)
+ if (!dbri->dma->desc[desc].ba)
break;
}
if (desc == DBRI_NO_DESCS) {
@@ -1131,37 +1135,33 @@ static int setup_descs(struct snd_dbri * dbri, int streamno, unsigned int period
return -1;
}
- if (len > DBRI_TD_MAXCNT) {
- mylen = DBRI_TD_MAXCNT; /* 8KB - 1 */
- } else {
+ if (len > DBRI_TD_MAXCNT)
+ mylen = DBRI_TD_MAXCNT; /* 8KB - 4 */
+ else
mylen = len;
- }
- if (mylen > period) {
+
+ if (mylen > period)
mylen = period;
- }
- dbri->descs[desc].inuse = 1;
- dbri->descs[desc].next = -1;
+ dbri->next_desc[desc] = -1;
dbri->dma->desc[desc].ba = dvma_buffer;
dbri->dma->desc[desc].nda = 0;
if (streamno == DBRI_PLAY) {
- dbri->descs[desc].len = mylen;
dbri->dma->desc[desc].word1 = DBRI_TD_CNT(mylen);
dbri->dma->desc[desc].word4 = 0;
- if (first_desc != -1)
- dbri->dma->desc[desc].word1 |= DBRI_TD_M;
+ dbri->dma->desc[desc].word1 |=
+ DBRI_TD_F | DBRI_TD_B;
} else {
- dbri->descs[desc].len = 0;
dbri->dma->desc[desc].word1 = 0;
dbri->dma->desc[desc].word4 =
DBRI_RD_B | DBRI_RD_BCNT(mylen);
}
- if (first_desc == -1) {
+ if (first_desc == -1)
first_desc = desc;
- } else {
- dbri->descs[last_desc].next = desc;
+ else {
+ dbri->next_desc[last_desc] = desc;
dbri->dma->desc[last_desc].nda =
dbri->dma_dvma + dbri_dma_off(desc, desc);
}
@@ -1176,21 +1176,24 @@ static int setup_descs(struct snd_dbri * dbri, int streamno, unsigned int period
return -1;
}
- dbri->dma->desc[last_desc].word1 &= ~DBRI_TD_M;
- if (streamno == DBRI_PLAY) {
- dbri->dma->desc[last_desc].word1 |=
- DBRI_TD_I | DBRI_TD_F | DBRI_TD_B;
- }
+ dbri->dma->desc[last_desc].nda =
+ dbri->dma_dvma + dbri_dma_off(desc, first_desc);
+ dbri->next_desc[last_desc] = first_desc;
dbri->pipes[info->pipe].first_desc = first_desc;
dbri->pipes[info->pipe].desc = first_desc;
- for (desc = first_desc; desc != -1; desc = dbri->descs[desc].next) {
+#ifdef DBRI_DEBUG
+ for (desc = first_desc; desc != -1; ) {
dprintk(D_DESC, "DESC %d: %08x %08x %08x %08x\n",
desc,
dbri->dma->desc[desc].word1,
dbri->dma->desc[desc].ba,
dbri->dma->desc[desc].nda, dbri->dma->desc[desc].word4);
+ desc = dbri->next_desc[desc];
+ if ( desc == first_desc )
+ break;
}
+#endif
return 0;
}
@@ -1207,56 +1210,30 @@ multiplexed serial interface which the DBRI can operate in either master
enum master_or_slave { CHImaster, CHIslave };
+/*
+ * Lock must not be held before calling it.
+ */
static void reset_chi(struct snd_dbri * dbri, enum master_or_slave master_or_slave,
int bits_per_frame)
{
- volatile s32 *cmd;
+ s32 *cmd;
int val;
- static int chi_initialized = 0; /* FIXME: mutex? */
-
- if (!chi_initialized) {
-
- cmd = dbri_cmdlock(dbri, GetLock);
-
- /* Set CHI Anchor: Pipe 16 */
-
- val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(16) | D_PIPE(16);
- *(cmd++) = DBRI_CMD(D_DTS, 0, val);
- *(cmd++) = D_TS_ANCHOR | D_TS_NEXT(16);
- *(cmd++) = 0;
-
- val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(16) | D_PIPE(16);
- *(cmd++) = DBRI_CMD(D_DTS, 0, val);
- *(cmd++) = 0;
- *(cmd++) = D_TS_ANCHOR | D_TS_NEXT(16);
- dbri->pipes[16].sdp = 1;
- dbri->pipes[16].nextpipe = 16;
- dbri->chi_in_pipe = 16;
- dbri->chi_out_pipe = 16;
+ /* Set CHI Anchor: Pipe 16 */
-#if 0
- chi_initialized++;
-#endif
- } else {
- int pipe;
+ cmd = dbri_cmdlock(dbri, 4);
+ val = D_DTS_VO | D_DTS_VI | D_DTS_INS
+ | D_DTS_PRVIN(16) | D_PIPE(16) | D_DTS_PRVOUT(16);
+ *(cmd++) = DBRI_CMD(D_DTS, 0, val);
+ *(cmd++) = D_TS_ANCHOR | D_TS_NEXT(16);
+ *(cmd++) = D_TS_ANCHOR | D_TS_NEXT(16);
+ *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
+ dbri_cmdsend(dbri, cmd, 4);
- for (pipe = dbri->chi_in_pipe;
- pipe != 16; pipe = dbri->pipes[pipe].nextpipe) {
- unlink_time_slot(dbri, pipe, PIPEinput,
- 16, dbri->pipes[pipe].nextpipe);
- }
- for (pipe = dbri->chi_out_pipe;
- pipe != 16; pipe = dbri->pipes[pipe].nextpipe) {
- unlink_time_slot(dbri, pipe, PIPEoutput,
- 16, dbri->pipes[pipe].nextpipe);
- }
+ dbri->pipes[16].sdp = 1;
+ dbri->pipes[16].nextpipe = 16;
- dbri->chi_in_pipe = 16;
- dbri->chi_out_pipe = 16;
-
- cmd = dbri_cmdlock(dbri, GetLock);
- }
+ cmd = dbri_cmdlock(dbri, 4);
if (master_or_slave == CHIslave) {
/* Setup DBRI for CHI Slave - receive clock, frame sync (FS)
@@ -1295,8 +1272,9 @@ static void reset_chi(struct snd_dbri * dbri, enum master_or_slave master_or_sla
*(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
*(cmd++) = DBRI_CMD(D_CDM, 0, D_CDM_XCE | D_CDM_XEN | D_CDM_REN);
+ *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
- dbri_cmdsend(dbri, cmd);
+ dbri_cmdsend(dbri, cmd, 4);
}
/*
@@ -1307,9 +1285,14 @@ static void reset_chi(struct snd_dbri * dbri, enum master_or_slave master_or_sla
In the standard SPARC audio configuration, the CS4215 codec is attached
to the DBRI via the CHI interface and few of the DBRI's PIO pins.
+ * Lock must not be held before calling it.
+
*/
static void cs4215_setup_pipes(struct snd_dbri * dbri)
{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dbri->lock, flags);
/*
* Data mode:
* Pipe 4: Send timeslots 1-4 (audio data)
@@ -1333,6 +1316,9 @@ static void cs4215_setup_pipes(struct snd_dbri * dbri)
setup_pipe(dbri, 17, D_SDP_FIXED | D_SDP_TO_SER | D_SDP_MSB);
setup_pipe(dbri, 18, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB);
setup_pipe(dbri, 19, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB);
+ spin_unlock_irqrestore(&dbri->lock, flags);
+
+ dbri_cmdwait(dbri);
}
static int cs4215_init_data(struct cs4215 *mm)
@@ -1364,7 +1350,7 @@ static int cs4215_init_data(struct cs4215 *mm)
mm->status = 0;
mm->version = 0xff;
mm->precision = 8; /* For ULAW */
- mm->channels = 2;
+ mm->channels = 1;
return 0;
}
@@ -1379,16 +1365,8 @@ static void cs4215_setdata(struct snd_dbri * dbri, int muted)
} else {
/* Start by setting the playback attenuation. */
struct dbri_streaminfo *info = &dbri->stream_info[DBRI_PLAY];
- int left_gain = info->left_gain % 64;
- int right_gain = info->right_gain % 64;
-
- if (info->balance < DBRI_MID_BALANCE) {
- right_gain *= info->balance;
- right_gain /= DBRI_MID_BALANCE;
- } else {
- left_gain *= DBRI_RIGHT_BALANCE - info->balance;
- left_gain /= DBRI_MID_BALANCE;
- }
+ int left_gain = info->left_gain & 0x3f;
+ int right_gain = info->right_gain & 0x3f;
dbri->mm.data[0] &= ~0x3f; /* Reset the volume bits */
dbri->mm.data[1] &= ~0x3f;
@@ -1397,8 +1375,8 @@ static void cs4215_setdata(struct snd_dbri * dbri, int muted)
/* Now set the recording gain. */
info = &dbri->stream_info[DBRI_REC];
- left_gain = info->left_gain % 16;
- right_gain = info->right_gain % 16;
+ left_gain = info->left_gain & 0xf;
+ right_gain = info->right_gain & 0xf;
dbri->mm.data[2] |= CS4215_LG(left_gain);
dbri->mm.data[3] |= CS4215_RG(right_gain);
}
@@ -1413,6 +1391,7 @@ static void cs4215_open(struct snd_dbri * dbri)
{
int data_width;
u32 tmp;
+ unsigned long flags;
dprintk(D_MM, "cs4215_open: %d channels, %d bits\n",
dbri->mm.channels, dbri->mm.precision);
@@ -1437,6 +1416,7 @@ static void cs4215_open(struct snd_dbri * dbri)
* bits. The CS4215, it seems, observes TSIN (the delayed signal)
* even if it's the CHI master. Don't ask me...
*/
+ spin_lock_irqsave(&dbri->lock, flags);
tmp = sbus_readl(dbri->regs + REG0);
tmp &= ~(D_C); /* Disable CHI */
sbus_writel(tmp, dbri->regs + REG0);
@@ -1455,15 +1435,16 @@ static void cs4215_open(struct snd_dbri * dbri)
*/
data_width = dbri->mm.channels * dbri->mm.precision;
- link_time_slot(dbri, 20, PIPEoutput, 16, 32, dbri->mm.offset + 32);
- link_time_slot(dbri, 4, PIPEoutput, 16, data_width, dbri->mm.offset);
- link_time_slot(dbri, 6, PIPEinput, 16, data_width, dbri->mm.offset);
- link_time_slot(dbri, 21, PIPEinput, 16, 16, dbri->mm.offset + 40);
+ link_time_slot(dbri, 4, 16, 16, data_width, dbri->mm.offset);
+ link_time_slot(dbri, 20, 4, 16, 32, dbri->mm.offset + 32);
+ link_time_slot(dbri, 6, 16, 16, data_width, dbri->mm.offset);
+ link_time_slot(dbri, 21, 6, 16, 16, dbri->mm.offset + 40);
/* FIXME: enable CHI after _setdata? */
tmp = sbus_readl(dbri->regs + REG0);
tmp |= D_C; /* Enable CHI */
sbus_writel(tmp, dbri->regs + REG0);
+ spin_unlock_irqrestore(&dbri->lock, flags);
cs4215_setdata(dbri, 0);
}
@@ -1475,6 +1456,7 @@ static int cs4215_setctrl(struct snd_dbri * dbri)
{
int i, val;
u32 tmp;
+ unsigned long flags;
/* FIXME - let the CPU do something useful during these delays */
@@ -1511,6 +1493,7 @@ static int cs4215_setctrl(struct snd_dbri * dbri)
* done in hardware by a TI 248 that delays the DBRI->4215
* frame sync signal by eight clock cycles. Anybody know why?
*/
+ spin_lock_irqsave(&dbri->lock, flags);
tmp = sbus_readl(dbri->regs + REG0);
tmp &= ~D_C; /* Disable CHI */
sbus_writel(tmp, dbri->regs + REG0);
@@ -1524,17 +1507,20 @@ static int cs4215_setctrl(struct snd_dbri * dbri)
* Pipe 19: Receive timeslot 7 (version).
*/
- link_time_slot(dbri, 17, PIPEoutput, 16, 32, dbri->mm.offset);
- link_time_slot(dbri, 18, PIPEinput, 16, 8, dbri->mm.offset);
- link_time_slot(dbri, 19, PIPEinput, 16, 8, dbri->mm.offset + 48);
+ link_time_slot(dbri, 17, 16, 16, 32, dbri->mm.offset);
+ link_time_slot(dbri, 18, 16, 16, 8, dbri->mm.offset);
+ link_time_slot(dbri, 19, 18, 16, 8, dbri->mm.offset + 48);
+ spin_unlock_irqrestore(&dbri->lock, flags);
/* Wait for the chip to echo back CLB (Control Latch Bit) as zero */
dbri->mm.ctrl[0] &= ~CS4215_CLB;
xmit_fixed(dbri, 17, *(int *)dbri->mm.ctrl);
+ spin_lock_irqsave(&dbri->lock, flags);
tmp = sbus_readl(dbri->regs + REG0);
tmp |= D_C; /* Enable CHI */
sbus_writel(tmp, dbri->regs + REG0);
+ spin_unlock_irqrestore(&dbri->lock, flags);
for (i = 10; ((dbri->mm.status & 0xe4) != 0x20); --i) {
msleep_interruptible(1);
@@ -1614,8 +1600,7 @@ static int cs4215_prepare(struct snd_dbri * dbri, unsigned int rate,
CS4215_BSEL_128 | CS4215_FREQ[freq_idx].xtal;
dbri->mm.channels = channels;
- /* Stereo bit: 8 bit stereo not working yet. */
- if ((channels > 1) && (dbri->mm.precision == 16))
+ if (channels == 2)
dbri->mm.ctrl[1] |= CS4215_DFR_STEREO;
ret = cs4215_setctrl(dbri);
@@ -1655,7 +1640,6 @@ static int cs4215_init(struct snd_dbri * dbri)
}
cs4215_setup_pipes(dbri);
-
cs4215_init_data(&dbri->mm);
/* Enable capture of the status & version timeslots. */
@@ -1684,88 +1668,71 @@ buffer and calls dbri_process_one_interrupt() for each interrupt word.
Complicated interrupts are handled by dedicated functions (which
appear first in this file). Any pending interrupts can be serviced by
calling dbri_process_interrupt_buffer(), which works even if the CPU's
-interrupts are disabled. This function is used by dbri_cmdlock()
-to make sure we're synced up with the chip before each command sequence,
-even if we're running cli'ed.
+interrupts are disabled.
*/
/* xmit_descs()
*
- * Transmit the current TD's for recording/playing, if needed.
+ * Starts transmiting the current TD's for recording/playing.
* For playback, ALSA has filled the DMA memory with new data (we hope).
*/
-static void xmit_descs(unsigned long data)
+static void xmit_descs(struct snd_dbri *dbri)
{
- struct snd_dbri *dbri = (struct snd_dbri *) data;
struct dbri_streaminfo *info;
- volatile s32 *cmd;
+ s32 *cmd;
unsigned long flags;
int first_td;
if (dbri == NULL)
return; /* Disabled */
- /* First check the recording stream for buffer overflow */
info = &dbri->stream_info[DBRI_REC];
spin_lock_irqsave(&dbri->lock, flags);
- if ((info->left >= info->size) && (info->pipe >= 0)) {
+ if (info->pipe >= 0) {
first_td = dbri->pipes[info->pipe].first_desc;
dprintk(D_DESC, "xmit_descs rec @ TD %d\n", first_td);
/* Stream could be closed by the time we run. */
- if (first_td < 0) {
- goto play;
- }
-
- cmd = dbri_cmdlock(dbri, NoGetLock);
- *(cmd++) = DBRI_CMD(D_SDP, 0,
- dbri->pipes[info->pipe].sdp
- | D_SDP_P | D_SDP_EVERY | D_SDP_C);
- *(cmd++) = dbri->dma_dvma + dbri_dma_off(desc, first_td);
- dbri_cmdsend(dbri, cmd);
+ if (first_td >= 0) {
+ cmd = dbri_cmdlock(dbri, 2);
+ *(cmd++) = DBRI_CMD(D_SDP, 0,
+ dbri->pipes[info->pipe].sdp
+ | D_SDP_P | D_SDP_EVERY | D_SDP_C);
+ *(cmd++) = dbri->dma_dvma + dbri_dma_off(desc, first_td);
+ dbri_cmdsend(dbri, cmd, 2);
- /* Reset our admin of the pipe & bytes read. */
- dbri->pipes[info->pipe].desc = first_td;
- info->left = 0;
+ /* Reset our admin of the pipe. */
+ dbri->pipes[info->pipe].desc = first_td;
+ }
}
-play:
- spin_unlock_irqrestore(&dbri->lock, flags);
-
- /* Now check the playback stream for buffer underflow */
info = &dbri->stream_info[DBRI_PLAY];
- spin_lock_irqsave(&dbri->lock, flags);
- if ((info->left <= 0) && (info->pipe >= 0)) {
+ if (info->pipe >= 0) {
first_td = dbri->pipes[info->pipe].first_desc;
dprintk(D_DESC, "xmit_descs play @ TD %d\n", first_td);
/* Stream could be closed by the time we run. */
- if (first_td < 0) {
- spin_unlock_irqrestore(&dbri->lock, flags);
- return;
- }
-
- cmd = dbri_cmdlock(dbri, NoGetLock);
- *(cmd++) = DBRI_CMD(D_SDP, 0,
- dbri->pipes[info->pipe].sdp
- | D_SDP_P | D_SDP_EVERY | D_SDP_C);
- *(cmd++) = dbri->dma_dvma + dbri_dma_off(desc, first_td);
- dbri_cmdsend(dbri, cmd);
+ if (first_td >= 0) {
+ cmd = dbri_cmdlock(dbri, 2);
+ *(cmd++) = DBRI_CMD(D_SDP, 0,
+ dbri->pipes[info->pipe].sdp
+ | D_SDP_P | D_SDP_EVERY | D_SDP_C);
+ *(cmd++) = dbri->dma_dvma + dbri_dma_off(desc, first_td);
+ dbri_cmdsend(dbri, cmd, 2);
- /* Reset our admin of the pipe & bytes written. */
- dbri->pipes[info->pipe].desc = first_td;
- info->left = info->size;
+ /* Reset our admin of the pipe. */
+ dbri->pipes[info->pipe].desc = first_td;
+ }
}
+
spin_unlock_irqrestore(&dbri->lock, flags);
}
-static DECLARE_TASKLET(xmit_descs_task, xmit_descs, 0);
-
/* transmission_complete_intr()
*
* Called by main interrupt handler when DBRI signals transmission complete
@@ -1775,9 +1742,9 @@ static DECLARE_TASKLET(xmit_descs_task, xmit_descs, 0);
* them as available. Stops when the first descriptor is found without
* TBC (Transmit Buffer Complete) set, or we've run through them all.
*
- * The DMA buffers are not released, but re-used. Since the transmit buffer
- * descriptors are not clobbered, they can be re-submitted as is. This is
- * done by the xmit_descs() tasklet above since that could take longer.
+ * The DMA buffers are not released. They form a ring buffer and
+ * they are filled by ALSA while others are transmitted by DMA.
+ *
*/
static void transmission_complete_intr(struct snd_dbri * dbri, int pipe)
@@ -1803,21 +1770,9 @@ static void transmission_complete_intr(struct snd_dbri * dbri, int pipe)
dprintk(D_INT, "TD %d, status 0x%02x\n", td, status);
dbri->dma->desc[td].word4 = 0; /* Reset it for next time. */
- info->offset += dbri->descs[td].len;
- info->left -= dbri->descs[td].len;
-
- /* On the last TD, transmit them all again. */
- if (dbri->descs[td].next == -1) {
- if (info->left > 0) {
- printk(KERN_WARNING
- "%d bytes left after last transfer.\n",
- info->left);
- info->left = 0;
- }
- tasklet_schedule(&xmit_descs_task);
- }
+ info->offset += DBRI_RD_CNT(dbri->dma->desc[td].word1);
- td = dbri->descs[td].next;
+ td = dbri->next_desc[td];
dbri->pipes[pipe].desc = td;
}
@@ -1841,30 +1796,18 @@ static void reception_complete_intr(struct snd_dbri * dbri, int pipe)
return;
}
- dbri->descs[rd].inuse = 0;
- dbri->pipes[pipe].desc = dbri->descs[rd].next;
+ dbri->pipes[pipe].desc = dbri->next_desc[rd];
status = dbri->dma->desc[rd].word1;
dbri->dma->desc[rd].word1 = 0; /* Reset it for next time. */
info = &dbri->stream_info[DBRI_REC];
info->offset += DBRI_RD_CNT(status);
- info->left += DBRI_RD_CNT(status);
/* FIXME: Check status */
dprintk(D_INT, "Recv RD %d, status 0x%02x, len %d\n",
rd, DBRI_RD_STATUS(status), DBRI_RD_CNT(status));
- /* On the last TD, transmit them all again. */
- if (dbri->descs[rd].next == -1) {
- if (info->left > info->size) {
- printk(KERN_WARNING
- "%d bytes recorded in %d size buffer.\n",
- info->left, info->size);
- }
- tasklet_schedule(&xmit_descs_task);
- }
-
/* Notify ALSA */
if (spin_is_locked(&dbri->lock)) {
spin_unlock(&dbri->lock);
@@ -1892,16 +1835,11 @@ static void dbri_process_one_interrupt(struct snd_dbri * dbri, int x)
channel, code, rval);
}
- if (channel == D_INTR_CMD && command == D_WAIT) {
- dbri->wait_ackd = val;
- if (dbri->wait_send != val) {
- printk(KERN_ERR "Processing wait command %d when %d was send.\n",
- val, dbri->wait_send);
- }
- return;
- }
-
switch (code) {
+ case D_INTR_CMDI:
+ if (command != D_WAIT)
+ printk(KERN_ERR "DBRI: Command read interrupt\n");
+ break;
case D_INTR_BRDY:
reception_complete_intr(dbri, channel);
break;
@@ -1914,8 +1852,10 @@ static void dbri_process_one_interrupt(struct snd_dbri * dbri, int x)
* resend SDP command with clear pipe bit (C) set
*/
{
- volatile s32 *cmd;
-
+ /* FIXME: do something useful in case of underrun */
+ printk(KERN_ERR "DBRI: Underrun error\n");
+#if 0
+ s32 *cmd;
int pipe = channel;
int td = dbri->pipes[pipe].desc;
@@ -1926,6 +1866,7 @@ static void dbri_process_one_interrupt(struct snd_dbri * dbri, int x)
| D_SDP_P | D_SDP_C | D_SDP_2SAME);
*(cmd++) = dbri->dma_dvma + dbri_dma_off(desc, td);
dbri_cmdsend(dbri, cmd);
+#endif
}
break;
case D_INTR_FXDT:
@@ -1946,9 +1887,7 @@ static void dbri_process_one_interrupt(struct snd_dbri * dbri, int x)
/* dbri_process_interrupt_buffer advances through the DBRI's interrupt
* buffer until it finds a zero word (indicating nothing more to do
* right now). Non-zero words require processing and are handed off
- * to dbri_process_one_interrupt AFTER advancing the pointer. This
- * order is important since we might recurse back into this function
- * and need to make sure the pointer has been advanced first.
+ * to dbri_process_one_interrupt AFTER advancing the pointer.
*/
static void dbri_process_interrupt_buffer(struct snd_dbri * dbri)
{
@@ -1957,10 +1896,8 @@ static void dbri_process_interrupt_buffer(struct snd_dbri * dbri)
while ((x = dbri->dma->intr[dbri->dbri_irqp]) != 0) {
dbri->dma->intr[dbri->dbri_irqp] = 0;
dbri->dbri_irqp++;
- if (dbri->dbri_irqp == (DBRI_NO_INTS * DBRI_INT_BLK))
+ if (dbri->dbri_irqp == DBRI_INT_BLK)
dbri->dbri_irqp = 1;
- else if ((dbri->dbri_irqp & (DBRI_INT_BLK - 1)) == 0)
- dbri->dbri_irqp++;
dbri_process_one_interrupt(dbri, x);
}
@@ -2020,8 +1957,6 @@ static irqreturn_t snd_dbri_interrupt(int irq, void *dev_id,
dbri_process_interrupt_buffer(dbri);
- /* FIXME: Write 0 into regs to ACK interrupt */
-
spin_unlock(&dbri->lock);
return IRQ_HANDLED;
@@ -2039,8 +1974,8 @@ static struct snd_pcm_hardware snd_dbri_pcm_hw = {
SNDRV_PCM_FMTBIT_A_LAW |
SNDRV_PCM_FMTBIT_U8 |
SNDRV_PCM_FMTBIT_S16_BE,
- .rates = SNDRV_PCM_RATE_8000_48000,
- .rate_min = 8000,
+ .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_5512,
+ .rate_min = 5512,
.rate_max = 48000,
.channels_min = 1,
.channels_max = 2,
@@ -2051,6 +1986,39 @@ static struct snd_pcm_hardware snd_dbri_pcm_hw = {
.periods_max = 1024,
};
+static int snd_hw_rule_format(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *c = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ struct snd_mask fmt;
+
+ snd_mask_any(&fmt);
+ if (c->min > 1) {
+ fmt.bits[0] &= SNDRV_PCM_FMTBIT_S16_BE;
+ return snd_mask_refine(f, &fmt);
+ }
+ return 0;
+}
+
+static int snd_hw_rule_channels(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *c = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ struct snd_interval ch;
+
+ snd_interval_any(&ch);
+ if (!(f->bits[0] & SNDRV_PCM_FMTBIT_S16_BE)) {
+ ch.min = ch.max = 1;
+ ch.integer = 1;
+ return snd_interval_refine(c, &ch);
+ }
+ return 0;
+}
+
static int snd_dbri_open(struct snd_pcm_substream *substream)
{
struct snd_dbri *dbri = snd_pcm_substream_chip(substream);
@@ -2063,12 +2031,19 @@ static int snd_dbri_open(struct snd_pcm_substream *substream)
spin_lock_irqsave(&dbri->lock, flags);
info->substream = substream;
- info->left = 0;
info->offset = 0;
info->dvma_buffer = 0;
info->pipe = -1;
spin_unlock_irqrestore(&dbri->lock, flags);
+ snd_pcm_hw_rule_add(runtime,0,SNDRV_PCM_HW_PARAM_CHANNELS,
+ snd_hw_rule_format, NULL, SNDRV_PCM_HW_PARAM_FORMAT,
+ -1);
+ snd_pcm_hw_rule_add(runtime,0,SNDRV_PCM_HW_PARAM_FORMAT,
+ snd_hw_rule_channels, NULL,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ -1);
+
cs4215_open(dbri);
return 0;
@@ -2081,7 +2056,6 @@ static int snd_dbri_close(struct snd_pcm_substream *substream)
dprintk(D_USR, "close audio output.\n");
info->substream = NULL;
- info->left = 0;
info->offset = 0;
return 0;
@@ -2134,6 +2108,7 @@ static int snd_dbri_hw_free(struct snd_pcm_substream *substream)
struct snd_dbri *dbri = snd_pcm_substream_chip(substream);
struct dbri_streaminfo *info = DBRI_STREAM(dbri, substream);
int direction;
+
dprintk(D_USR, "hw_free.\n");
/* hw_free can get called multiple times. Only unmap the DMA once.
@@ -2148,7 +2123,10 @@ static int snd_dbri_hw_free(struct snd_pcm_substream *substream)
substream->runtime->buffer_size, direction);
info->dvma_buffer = 0;
}
- info->pipe = -1;
+ if (info->pipe != -1) {
+ reset_pipe(dbri, info->pipe);
+ info->pipe = -1;
+ }
return snd_pcm_lib_free_pages(substream);
}
@@ -2157,18 +2135,16 @@ static int snd_dbri_prepare(struct snd_pcm_substream *substream)
{
struct snd_dbri *dbri = snd_pcm_substream_chip(substream);
struct dbri_streaminfo *info = DBRI_STREAM(dbri, substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
int ret;
info->size = snd_pcm_lib_buffer_bytes(substream);
if (DBRI_STREAMNO(substream) == DBRI_PLAY)
info->pipe = 4; /* Send pipe */
- else {
+ else
info->pipe = 6; /* Receive pipe */
- info->left = info->size; /* To trigger submittal */
- }
spin_lock_irq(&dbri->lock);
+ info->offset = 0;
/* Setup the all the transmit/receive desciptors to cover the
* whole DMA buffer.
@@ -2176,8 +2152,6 @@ static int snd_dbri_prepare(struct snd_pcm_substream *substream)
ret = setup_descs(dbri, DBRI_STREAMNO(substream),
snd_pcm_lib_period_bytes(substream));
- runtime->stop_threshold = DBRI_TD_MAXCNT / runtime->channels;
-
spin_unlock_irq(&dbri->lock);
dprintk(D_USR, "prepare audio output. %d bytes\n", info->size);
@@ -2194,14 +2168,11 @@ static int snd_dbri_trigger(struct snd_pcm_substream *substream, int cmd)
case SNDRV_PCM_TRIGGER_START:
dprintk(D_USR, "start audio, period is %d bytes\n",
(int)snd_pcm_lib_period_bytes(substream));
- /* Enable & schedule the tasklet that re-submits the TDs. */
- xmit_descs_task.data = (unsigned long)dbri;
- tasklet_schedule(&xmit_descs_task);
+ /* Re-submit the TDs. */
+ xmit_descs(dbri);
break;
case SNDRV_PCM_TRIGGER_STOP:
dprintk(D_USR, "stop audio.\n");
- /* Make the tasklet bail out immediately. */
- xmit_descs_task.data = 0;
reset_pipe(dbri, info->pipe);
break;
default:
@@ -2219,8 +2190,8 @@ static snd_pcm_uframes_t snd_dbri_pointer(struct snd_pcm_substream *substream)
ret = bytes_to_frames(substream->runtime, info->offset)
% substream->runtime->buffer_size;
- dprintk(D_USR, "I/O pointer: %ld frames, %d bytes left.\n",
- ret, info->left);
+ dprintk(D_USR, "I/O pointer: %ld frames of %ld.\n",
+ ret, substream->runtime->buffer_size);
return ret;
}
@@ -2254,7 +2225,6 @@ static int __devinit snd_dbri_pcm(struct snd_dbri * dbri)
pcm->private_data = dbri;
pcm->info_flags = 0;
strcpy(pcm->name, dbri->card->shortname);
- dbri->pcm = pcm;
if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm,
SNDRV_DMA_TYPE_CONTINUOUS,
@@ -2303,7 +2273,6 @@ static int snd_cs4215_put_volume(struct snd_kcontrol *kcontrol,
{
struct snd_dbri *dbri = snd_kcontrol_chip(kcontrol);
struct dbri_streaminfo *info = &dbri->stream_info[kcontrol->private_value];
- unsigned long flags;
int changed = 0;
if (info->left_gain != ucontrol->value.integer.value[0]) {
@@ -2318,13 +2287,9 @@ static int snd_cs4215_put_volume(struct snd_kcontrol *kcontrol,
/* First mute outputs, and wait 1/8000 sec (125 us)
* to make sure this takes. This avoids clicking noises.
*/
- spin_lock_irqsave(&dbri->lock, flags);
-
cs4215_setdata(dbri, 1);
udelay(125);
cs4215_setdata(dbri, 0);
-
- spin_unlock_irqrestore(&dbri->lock, flags);
}
return changed;
}
@@ -2371,7 +2336,6 @@ static int snd_cs4215_put_single(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_dbri *dbri = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int elem = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
int mask = (kcontrol->private_value >> 16) & 0xff;
@@ -2404,13 +2368,9 @@ static int snd_cs4215_put_single(struct snd_kcontrol *kcontrol,
/* First mute outputs, and wait 1/8000 sec (125 us)
* to make sure this takes. This avoids clicking noises.
*/
- spin_lock_irqsave(&dbri->lock, flags);
-
cs4215_setdata(dbri, 1);
udelay(125);
cs4215_setdata(dbri, 0);
-
- spin_unlock_irqrestore(&dbri->lock, flags);
}
return changed;
}
@@ -2452,8 +2412,6 @@ static struct snd_kcontrol_new dbri_controls[] __devinitdata = {
CS4215_SINGLE("Mic boost", 4, 4, 1, 1)
};
-#define NUM_CS4215_CONTROLS (sizeof(dbri_controls)/sizeof(struct snd_kcontrol_new))
-
static int __init snd_dbri_mixer(struct snd_dbri * dbri)
{
struct snd_card *card;
@@ -2464,7 +2422,7 @@ static int __init snd_dbri_mixer(struct snd_dbri * dbri)
card = dbri->card;
strcpy(card->mixername, card->shortname);
- for (idx = 0; idx < NUM_CS4215_CONTROLS; idx++) {
+ for (idx = 0; idx < ARRAY_SIZE(dbri_controls); idx++) {
if ((err = snd_ctl_add(card,
snd_ctl_new1(&dbri_controls[idx], dbri))) < 0)
return err;
@@ -2473,7 +2431,6 @@ static int __init snd_dbri_mixer(struct snd_dbri * dbri)
for (idx = DBRI_REC; idx < DBRI_NO_STREAMS; idx++) {
dbri->stream_info[idx].left_gain = 0;
dbri->stream_info[idx].right_gain = 0;
- dbri->stream_info[idx].balance = DBRI_MID_BALANCE;
}
return 0;
@@ -2505,12 +2462,11 @@ static void dbri_debug_read(struct snd_info_entry * entry,
struct dbri_pipe *pptr = &dbri->pipes[pipe];
snd_iprintf(buffer,
"Pipe %d: %s SDP=0x%x desc=%d, "
- "len=%d @ %d prev: %d next %d\n",
+ "len=%d next %d\n",
pipe,
- (pptr->direction ==
- PIPEinput ? "input" : "output"), pptr->sdp,
- pptr->desc, pptr->length, pptr->cycle,
- pptr->prevpipe, pptr->nextpipe);
+ ((pptr->sdp & D_SDP_TO_SER) ? "output" : "input"),
+ pptr->sdp, pptr->desc,
+ pptr->length, pptr->nextpipe);
}
}
}
@@ -2549,7 +2505,6 @@ static int __init snd_dbri_create(struct snd_card *card,
dbri->card = card;
dbri->sdev = sdev;
dbri->irq = irq->pri;
- dbri->dbri_version = sdev->prom_name[9];
dbri->dma = sbus_alloc_consistent(sdev, sizeof(struct dbri_dma),
&dbri->dma_dvma);
@@ -2569,7 +2524,7 @@ static int __init snd_dbri_create(struct snd_card *card,
return -EIO;
}
- err = request_irq(dbri->irq, snd_dbri_interrupt, SA_SHIRQ,
+ err = request_irq(dbri->irq, snd_dbri_interrupt, IRQF_SHARED,
"DBRI audio", dbri);
if (err) {
printk(KERN_ERR "DBRI: Can't get irq %d\n", dbri->irq);
@@ -2645,9 +2600,9 @@ static int __init dbri_attach(int prom_node, struct sbus_dev *sdev)
strcpy(card->driver, "DBRI");
strcpy(card->shortname, "Sun DBRI");
rp = &sdev->resource[0];
- sprintf(card->longname, "%s at 0x%02lx:0x%08lx, irq %d",
+ sprintf(card->longname, "%s at 0x%02lx:0x%016Lx, irq %d",
card->shortname,
- rp->flags & 0xffL, rp->start, irq.pri);
+ rp->flags & 0xffL, (unsigned long long)rp->start, irq.pri);
if ((err = snd_dbri_create(card, sdev, &irq, dev)) < 0) {
snd_card_free(card);
@@ -2669,7 +2624,7 @@ static int __init dbri_attach(int prom_node, struct sbus_dev *sdev)
printk(KERN_INFO "audio%d at %p (irq %d) is DBRI(%c)+CS4215(%d)\n",
dev, dbri->regs,
- dbri->irq, dbri->dbri_version, dbri->mm.version);
+ dbri->irq, sdev->prom_name[9], dbri->mm.version);
dev++;
return 0;
diff --git a/sound/synth/emux/emux_proc.c b/sound/synth/emux/emux_proc.c
index 58b9601f3ad0..59144ec026e4 100644
--- a/sound/synth/emux/emux_proc.c
+++ b/sound/synth/emux/emux_proc.c
@@ -128,10 +128,8 @@ void snd_emux_proc_init(struct snd_emux *emu, struct snd_card *card, int device)
void snd_emux_proc_free(struct snd_emux *emu)
{
- if (emu->proc) {
- snd_info_unregister(emu->proc);
- emu->proc = NULL;
- }
+ snd_info_free_entry(emu->proc);
+ emu->proc = NULL;
}
#endif /* CONFIG_PROC_FS */
diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c
index 627de9525a32..49248fa7aef4 100644
--- a/sound/usb/usbaudio.c
+++ b/sound/usb/usbaudio.c
@@ -68,7 +68,7 @@ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
static int vid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; /* Vendor ID for this card */
static int pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; /* Product ID for this card */
-static int nrpacks = 4; /* max. number of packets per urb */
+static int nrpacks = 8; /* max. number of packets per urb */
static int async_unlink = 1;
static int device_setup[SNDRV_CARDS]; /* device parameter for this card*/
@@ -100,7 +100,7 @@ MODULE_PARM_DESC(device_setup, "Specific device setup (if needed).");
*
*/
-#define MAX_PACKS 10
+#define MAX_PACKS 20
#define MAX_PACKS_HS (MAX_PACKS * 8) /* in high speed mode */
#define MAX_URBS 8
#define SYNC_URBS 4 /* always four urbs for sync */
@@ -123,6 +123,7 @@ struct audioformat {
unsigned int rate_min, rate_max; /* min/max rates */
unsigned int nr_rates; /* number of rate table entries */
unsigned int *rate_table; /* rate table */
+ unsigned int needs_knot; /* any unusual rates? */
};
struct snd_usb_substream;
@@ -1759,6 +1760,9 @@ static int check_hw_params_convention(struct snd_usb_substream *subs)
}
channels[f->format] |= (1 << f->channels);
rates[f->format] |= f->rates;
+ /* needs knot? */
+ if (f->needs_knot)
+ goto __out;
}
/* check whether channels and rates match for all formats */
cmaster = rmaster = 0;
@@ -1799,6 +1803,38 @@ static int check_hw_params_convention(struct snd_usb_substream *subs)
return err;
}
+/*
+ * If the device supports unusual bit rates, does the request meet these?
+ */
+static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime,
+ struct snd_usb_substream *subs)
+{
+ struct list_head *p;
+ struct snd_pcm_hw_constraint_list constraints_rates;
+ int err;
+
+ list_for_each(p, &subs->fmt_list) {
+ struct audioformat *fp;
+ fp = list_entry(p, struct audioformat, list);
+
+ if (!fp->needs_knot)
+ continue;
+
+ constraints_rates.count = fp->nr_rates;
+ constraints_rates.list = fp->rate_table;
+ constraints_rates.mask = 0;
+
+ err = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
/*
* set up the runtime hardware information.
@@ -1861,6 +1897,8 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre
SNDRV_PCM_HW_PARAM_CHANNELS,
-1)) < 0)
return err;
+ if ((err = snd_usb_pcm_check_knot(runtime, subs)) < 0)
+ return err;
}
return 0;
}
@@ -2049,7 +2087,7 @@ static struct usb_driver usb_audio_driver = {
};
-#if defined(CONFIG_PROCFS) && defined(CONFIG_SND_VERBOSE_PROCFS)
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_SND_VERBOSE_PROCFS)
/*
* proc interface for list the supported pcm formats
@@ -2260,10 +2298,9 @@ static int add_audio_endpoint(struct snd_usb_audio *chip, int stream, struct aud
}
/* create a new pcm */
- as = kmalloc(sizeof(*as), GFP_KERNEL);
+ as = kzalloc(sizeof(*as), GFP_KERNEL);
if (! as)
return -ENOMEM;
- memset(as, 0, sizeof(*as));
as->pcm_index = chip->pcm_devs;
as->chip = chip;
as->fmt_type = fp->fmt_type;
@@ -2407,6 +2444,7 @@ static int parse_audio_format_rates(struct snd_usb_audio *chip, struct audioform
unsigned char *fmt, int offset)
{
int nr_rates = fmt[offset];
+ int found;
if (fmt[0] < offset + 1 + 3 * (nr_rates ? nr_rates : 2)) {
snd_printk(KERN_ERR "%d:%u:%d : invalid FORMAT_TYPE desc\n",
chip->dev->devnum, fp->iface, fp->altsetting);
@@ -2429,6 +2467,7 @@ static int parse_audio_format_rates(struct snd_usb_audio *chip, struct audioform
return -1;
}
+ fp->needs_knot = 0;
fp->nr_rates = nr_rates;
fp->rate_min = fp->rate_max = combine_triple(&fmt[8]);
for (r = 0, idx = offset + 1; r < nr_rates; r++, idx += 3) {
@@ -2437,13 +2476,19 @@ static int parse_audio_format_rates(struct snd_usb_audio *chip, struct audioform
fp->rate_min = rate;
else if (rate > fp->rate_max)
fp->rate_max = rate;
+ found = 0;
for (c = 0; c < (int)ARRAY_SIZE(conv_rates); c++) {
if (rate == conv_rates[c]) {
+ found = 1;
fp->rates |= (1 << c);
break;
}
}
+ if (!found)
+ fp->needs_knot = 1;
}
+ if (fp->needs_knot)
+ fp->rates |= SNDRV_PCM_RATE_KNOT;
} else {
/* continuous rates */
fp->rates = SNDRV_PCM_RATE_CONTINUOUS;
@@ -2633,13 +2678,12 @@ static int parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
csep = NULL;
}
- fp = kmalloc(sizeof(*fp), GFP_KERNEL);
+ fp = kzalloc(sizeof(*fp), GFP_KERNEL);
if (! fp) {
snd_printk(KERN_ERR "cannot malloc\n");
return -ENOMEM;
}
- memset(fp, 0, sizeof(*fp));
fp->iface = iface_no;
fp->altsetting = altno;
fp->altset_idx = i;
@@ -3096,6 +3140,32 @@ static int snd_usb_audigy2nx_boot_quirk(struct usb_device *dev)
}
/*
+ * C-Media CM106/CM106+ have four 16-bit internal registers that are nicely
+ * documented in the device's data sheet.
+ */
+static int snd_usb_cm106_write_int_reg(struct usb_device *dev, int reg, u16 value)
+{
+ u8 buf[4];
+ buf[0] = 0x20;
+ buf[1] = value & 0xff;
+ buf[2] = (value >> 8) & 0xff;
+ buf[3] = reg;
+ return snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_CONFIGURATION,
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT,
+ 0, 0, &buf, 4, 1000);
+}
+
+static int snd_usb_cm106_boot_quirk(struct usb_device *dev)
+{
+ /*
+ * Enable line-out driver mode, set headphone source to front
+ * channels, enable stereo mic.
+ */
+ return snd_usb_cm106_write_int_reg(dev, 2, 0x8004);
+}
+
+
+/*
* Setup quirks
*/
#define AUDIOPHILE_SET 0x01 /* if set, parse device_setup */
@@ -3365,6 +3435,12 @@ static void *snd_usb_audio_probe(struct usb_device *dev,
goto __err_val;
}
+ /* C-Media CM106 / Turtle Beach Audio Advantage Roadie */
+ if (id == USB_ID(0x10f5, 0x0200)) {
+ if (snd_usb_cm106_boot_quirk(dev) < 0)
+ goto __err_val;
+ }
+
/*
* found a config. now register to ALSA
*/
@@ -3469,7 +3545,7 @@ static void snd_usb_audio_disconnect(struct usb_device *dev, void *ptr)
}
usb_chip[chip->index] = NULL;
mutex_unlock(&register_mutex);
- snd_card_free(card);
+ snd_card_free_when_closed(card);
} else {
mutex_unlock(&register_mutex);
}
diff --git a/sound/usb/usbmidi.c b/sound/usb/usbmidi.c
index 5105b6b05748..abe29dadd979 100644
--- a/sound/usb/usbmidi.c
+++ b/sound/usb/usbmidi.c
@@ -181,9 +181,9 @@ static int snd_usbmidi_urb_error(int status)
case -ENODEV:
return -ENODEV;
/* errors that might occur during unplugging */
- case -EPROTO: /* EHCI */
- case -ETIMEDOUT: /* OHCI */
- case -EILSEQ: /* UHCI */
+ case -EPROTO:
+ case -ETIME:
+ case -EILSEQ:
return -EIO;
default:
snd_printk(KERN_ERR "urb status %d\n", status);
diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c
index 491e975a0c87..e516d6adbb22 100644
--- a/sound/usb/usbmixer.c
+++ b/sound/usb/usbmixer.c
@@ -37,6 +37,7 @@
#include <sound/control.h>
#include <sound/hwdep.h>
#include <sound/info.h>
+#include <sound/tlv.h>
#include "usbaudio.h"
@@ -416,6 +417,26 @@ static inline int set_cur_mix_value(struct usb_mixer_elem_info *cval, int channe
return set_ctl_value(cval, SET_CUR, (cval->control << 8) | channel, value);
}
+/*
+ * TLV callback for mixer volume controls
+ */
+static int mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+ unsigned int size, unsigned int __user *_tlv)
+{
+ struct usb_mixer_elem_info *cval = kcontrol->private_data;
+ DECLARE_TLV_DB_SCALE(scale, 0, 0, 0);
+
+ if (size < sizeof(scale))
+ return -ENOMEM;
+ /* USB descriptions contain the dB scale in 1/256 dB unit
+ * while ALSA TLV contains in 1/100 dB unit
+ */
+ scale[2] = (convert_signed_value(cval, cval->min) * 100) / 256;
+ scale[3] = (convert_signed_value(cval, cval->res) * 100) / 256;
+ if (copy_to_user(_tlv, scale, sizeof(scale)))
+ return -EFAULT;
+ return 0;
+}
/*
* parser routines begin here...
@@ -933,6 +954,12 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc,
}
strlcat(kctl->id.name + len, control == USB_FEATURE_MUTE ? " Switch" : " Volume",
sizeof(kctl->id.name));
+ if (control == USB_FEATURE_VOLUME) {
+ kctl->tlv.c = mixer_vol_tlv;
+ kctl->vd[0].access |=
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+ }
break;
default:
diff --git a/sound/usb/usbmixer_maps.c b/sound/usb/usbmixer_maps.c
index 37accb68652d..7c4dcb3f436a 100644
--- a/sound/usb/usbmixer_maps.c
+++ b/sound/usb/usbmixer_maps.c
@@ -234,6 +234,26 @@ static struct usbmix_name_map justlink_map[] = {
{ 0 } /* terminator */
};
+/* TerraTec Aureon 5.1 MkII USB */
+static struct usbmix_name_map aureon_51_2_map[] = {
+ /* 1: IT USB */
+ /* 2: IT Mic */
+ /* 3: IT Line */
+ /* 4: IT SPDIF */
+ /* 5: OT SPDIF */
+ /* 6: OT Speaker */
+ /* 7: OT USB */
+ { 8, "Capture Source" }, /* SU */
+ { 9, "Master Playback" }, /* FU */
+ { 10, "Mic Capture" }, /* FU */
+ { 11, "Line Capture" }, /* FU */
+ { 12, "IEC958 In Capture" }, /* FU */
+ { 13, "Mic Playback" }, /* FU */
+ { 14, "Line Playback" }, /* FU */
+ /* 15: MU */
+ {} /* terminator */
+};
+
/*
* Control map entries
*/
@@ -276,6 +296,10 @@ static struct usbmix_ctl_map usbmix_ctl_maps[] = {
.id = USB_ID(0x0c45, 0x1158),
.map = justlink_map,
},
+ {
+ .id = USB_ID(0x0ccd, 0x0028),
+ .map = aureon_51_2_map,
+ },
{ 0 } /* terminator */
};
diff --git a/sound/usb/usbquirks.h b/sound/usb/usbquirks.h
index 9351846d7a9d..a7e9563a01df 100644
--- a/sound/usb/usbquirks.h
+++ b/sound/usb/usbquirks.h
@@ -123,6 +123,10 @@ YAMAHA_DEVICE(0x103e, NULL),
YAMAHA_DEVICE(0x103f, NULL),
YAMAHA_DEVICE(0x1040, NULL),
YAMAHA_DEVICE(0x1041, NULL),
+YAMAHA_DEVICE(0x1042, NULL),
+YAMAHA_DEVICE(0x1043, NULL),
+YAMAHA_DEVICE(0x1044, NULL),
+YAMAHA_DEVICE(0x1045, NULL),
YAMAHA_DEVICE(0x2000, "DGP-7"),
YAMAHA_DEVICE(0x2001, "DGP-5"),
YAMAHA_DEVICE(0x2002, NULL),
@@ -141,6 +145,7 @@ YAMAHA_DEVICE(0x500b, "DME64N"),
YAMAHA_DEVICE(0x500c, "DME24N"),
YAMAHA_DEVICE(0x500d, NULL),
YAMAHA_DEVICE(0x500e, NULL),
+YAMAHA_DEVICE(0x500f, NULL),
YAMAHA_DEVICE(0x7000, "DTX"),
YAMAHA_DEVICE(0x7010, "UB99"),
#undef YAMAHA_DEVICE
OpenPOWER on IntegriCloud