aboutsummaryrefslogtreecommitdiff
path: root/sound/pci
diff options
context:
space:
mode:
Diffstat (limited to 'sound/pci')
-rw-r--r--sound/pci/Kconfig56
-rw-r--r--sound/pci/Makefile3
-rw-r--r--sound/pci/ac97/ac97_codec.c1
-rw-r--r--sound/pci/ac97/ac97_patch.c11
-rw-r--r--sound/pci/ac97/ac97_patch.h2
-rw-r--r--sound/pci/ac97/ac97_pcm.c1
-rw-r--r--sound/pci/ac97/ac97_proc.c1
-rw-r--r--sound/pci/ac97/ak4531_codec.c1
-rw-r--r--sound/pci/ad1889.c7
-rw-r--r--sound/pci/ali5451/ali5451.c1
-rw-r--r--sound/pci/als300.c1
-rw-r--r--sound/pci/als4000.c1
-rw-r--r--sound/pci/atiixp.c5
-rw-r--r--sound/pci/atiixp_modem.c1
-rw-r--r--sound/pci/au88x0/au88x0.h1
-rw-r--r--sound/pci/au88x0/au88x0_core.c2
-rw-r--r--sound/pci/au88x0/au88x0_game.c1
-rw-r--r--sound/pci/au88x0/au88x0_mixer.c1
-rw-r--r--sound/pci/au88x0/au88x0_mpu401.c1
-rw-r--r--sound/pci/au88x0/au88x0_pcm.c1
-rw-r--r--sound/pci/azt3328.c1
-rw-r--r--sound/pci/bt87x.c1
-rw-r--r--sound/pci/ca0106/ca0106.h1
-rw-r--r--sound/pci/ca0106/ca0106_main.c1
-rw-r--r--sound/pci/ca0106/ca0106_mixer.c1
-rw-r--r--sound/pci/ca0106/ca0106_proc.c1
-rw-r--r--sound/pci/ca0106/ca_midi.c1
-rw-r--r--sound/pci/cmipci.c116
-rw-r--r--sound/pci/cs4281.c1
-rw-r--r--sound/pci/cs46xx/cs46xx.c1
-rw-r--r--sound/pci/cs46xx/cs46xx_lib.c102
-rw-r--r--sound/pci/cs46xx/dsp_spos.c1
-rw-r--r--sound/pci/cs46xx/dsp_spos_scb_lib.c1
-rw-r--r--sound/pci/cs5530.c1
-rw-r--r--sound/pci/cs5535audio/cs5535audio.c3
-rw-r--r--sound/pci/cs5535audio/cs5535audio_pcm.c5
-rw-r--r--sound/pci/cs5535audio/cs5535audio_pm.c1
-rw-r--r--sound/pci/echoaudio/darla20.c1
-rw-r--r--sound/pci/echoaudio/darla24.c1
-rw-r--r--sound/pci/echoaudio/echo3g.c1
-rw-r--r--sound/pci/echoaudio/echoaudio.c18
-rw-r--r--sound/pci/echoaudio/echoaudio.h2
-rw-r--r--sound/pci/echoaudio/gina20.c1
-rw-r--r--sound/pci/echoaudio/gina24.c1
-rw-r--r--sound/pci/echoaudio/indigo.c1
-rw-r--r--sound/pci/echoaudio/indigodj.c1
-rw-r--r--sound/pci/echoaudio/indigoio.c1
-rw-r--r--sound/pci/echoaudio/layla20.c1
-rw-r--r--sound/pci/echoaudio/layla24.c1
-rw-r--r--sound/pci/echoaudio/mia.c1
-rw-r--r--sound/pci/echoaudio/mona.c1
-rw-r--r--sound/pci/emu10k1/emu10k1.c1
-rw-r--r--sound/pci/emu10k1/emu10k1_callback.c15
-rw-r--r--sound/pci/emu10k1/emu10k1_main.c342
-rw-r--r--sound/pci/emu10k1/emu10k1_synth.c45
-rw-r--r--sound/pci/emu10k1/emu10k1_synth_local.h1
-rw-r--r--sound/pci/emu10k1/emu10k1x.c3
-rw-r--r--sound/pci/emu10k1/emufx.c9
-rw-r--r--sound/pci/emu10k1/emumixer.c276
-rw-r--r--sound/pci/emu10k1/emumpu401.c1
-rw-r--r--sound/pci/emu10k1/emupcm.c9
-rw-r--r--sound/pci/emu10k1/emuproc.c5
-rw-r--r--sound/pci/emu10k1/io.c60
-rw-r--r--sound/pci/emu10k1/irq.c9
-rw-r--r--sound/pci/emu10k1/memory.c1
-rw-r--r--sound/pci/emu10k1/p16v.c1
-rw-r--r--sound/pci/emu10k1/timer.c1
-rw-r--r--sound/pci/emu10k1/voice.c1
-rw-r--r--sound/pci/ens1370.c1
-rw-r--r--sound/pci/es1938.c28
-rw-r--r--sound/pci/es1968.c1
-rw-r--r--sound/pci/fm801.c23
-rw-r--r--sound/pci/hda/Makefile2
-rw-r--r--sound/pci/hda/hda_codec.c117
-rw-r--r--sound/pci/hda/hda_codec.h60
-rw-r--r--sound/pci/hda/hda_generic.c1
-rw-r--r--sound/pci/hda/hda_hwdep.c1
-rw-r--r--sound/pci/hda/hda_intel.c164
-rw-r--r--sound/pci/hda/hda_local.h18
-rw-r--r--sound/pci/hda/hda_proc.c361
-rw-r--r--sound/pci/hda/patch_analog.c190
-rw-r--r--sound/pci/hda/patch_atihdmi.c3
-rw-r--r--sound/pci/hda/patch_cmedia.c2
-rw-r--r--sound/pci/hda/patch_conexant.c445
-rw-r--r--sound/pci/hda/patch_realtek.c1830
-rw-r--r--sound/pci/hda/patch_si3054.c2
-rw-r--r--sound/pci/hda/patch_sigmatel.c1217
-rw-r--r--sound/pci/hda/patch_via.c679
-rw-r--r--sound/pci/hda/vmaster.c364
-rw-r--r--sound/pci/ice1712/Makefile2
-rw-r--r--sound/pci/ice1712/ak4xxx.c1
-rw-r--r--sound/pci/ice1712/amp.c1
-rw-r--r--sound/pci/ice1712/aureon.c133
-rw-r--r--sound/pci/ice1712/delta.c3
-rw-r--r--sound/pci/ice1712/ews.c92
-rw-r--r--sound/pci/ice1712/hoontech.c188
-rw-r--r--sound/pci/ice1712/ice1712.c2
-rw-r--r--sound/pci/ice1712/ice1712.h37
-rw-r--r--sound/pci/ice1712/ice1724.c9
-rw-r--r--sound/pci/ice1712/juli.c23
-rw-r--r--sound/pci/ice1712/phase.c96
-rw-r--r--sound/pci/ice1712/pontis.c1
-rw-r--r--sound/pci/ice1712/prodigy192.c297
-rw-r--r--sound/pci/ice1712/prodigy_hifi.c1210
-rw-r--r--sound/pci/ice1712/prodigy_hifi.h38
-rw-r--r--sound/pci/ice1712/revo.c24
-rw-r--r--sound/pci/ice1712/se.c774
-rw-r--r--sound/pci/ice1712/se.h15
-rw-r--r--sound/pci/ice1712/vt1720_mobo.c1
-rw-r--r--sound/pci/ice1712/wtm.c9
-rw-r--r--sound/pci/intel8x0.c2
-rw-r--r--sound/pci/intel8x0m.c1
-rw-r--r--sound/pci/korg1212/korg1212.c38
-rw-r--r--sound/pci/maestro3.c2
-rw-r--r--sound/pci/mixart/mixart.c1
-rw-r--r--sound/pci/mixart/mixart_core.c1
-rw-r--r--sound/pci/mixart/mixart_hwdep.c1
-rw-r--r--sound/pci/mixart/mixart_mixer.c125
-rw-r--r--sound/pci/nm256/nm256.c1
-rw-r--r--sound/pci/oxygen/Makefile9
-rw-r--r--sound/pci/oxygen/ak4396.h44
-rw-r--r--sound/pci/oxygen/cm9780.h63
-rw-r--r--sound/pci/oxygen/hifier.c207
-rw-r--r--sound/pci/oxygen/oxygen.c385
-rw-r--r--sound/pci/oxygen/oxygen.h190
-rw-r--r--sound/pci/oxygen/oxygen_io.c201
-rw-r--r--sound/pci/oxygen/oxygen_lib.c515
-rw-r--r--sound/pci/oxygen/oxygen_mixer.c794
-rw-r--r--sound/pci/oxygen/oxygen_pcm.c718
-rw-r--r--sound/pci/oxygen/oxygen_regs.h453
-rw-r--r--sound/pci/oxygen/virtuoso.c449
-rw-r--r--sound/pci/pcxhr/pcxhr.c1
-rw-r--r--sound/pci/pcxhr/pcxhr_core.c1
-rw-r--r--sound/pci/pcxhr/pcxhr_hwdep.c1
-rw-r--r--sound/pci/pcxhr/pcxhr_mixer.c72
-rw-r--r--sound/pci/riptide/riptide.c1
-rw-r--r--sound/pci/rme32.c1
-rw-r--r--sound/pci/rme96.c28
-rw-r--r--sound/pci/rme9652/hdsp.c19
-rw-r--r--sound/pci/rme9652/hdspm.c5
-rw-r--r--sound/pci/rme9652/rme9652.c5
-rw-r--r--sound/pci/sis7019.c1460
-rw-r--r--sound/pci/sis7019.h342
-rw-r--r--sound/pci/sonicvibes.c1
-rw-r--r--sound/pci/trident/Makefile10
-rw-r--r--sound/pci/trident/trident.c8
-rw-r--r--sound/pci/trident/trident_main.c71
-rw-r--r--sound/pci/trident/trident_memory.c1
-rw-r--r--sound/pci/trident/trident_synth.c1024
-rw-r--r--sound/pci/via82xx.c13
-rw-r--r--sound/pci/via82xx_modem.c1
-rw-r--r--sound/pci/vx222/vx222.c1
-rw-r--r--sound/pci/vx222/vx222_ops.c10
-rw-r--r--sound/pci/ymfpci/ymfpci.c1
-rw-r--r--sound/pci/ymfpci/ymfpci_main.c5
155 files changed, 14207 insertions, 2690 deletions
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index 356bf21a150..812085d521f 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -183,6 +183,30 @@ config SND_CMIPCI
To compile this driver as a module, choose M here: the module
will be called snd-cmipci.
+config SND_OXYGEN_LIB
+ tristate
+ depends on SND
+ select SND_PCM
+ select SND_MPU401_UART
+
+config SND_OXYGEN
+ tristate "C-Media 8788 (Oxygen)"
+ depends on SND
+ select SND_OXYGEN_LIB
+ help
+ Say Y here to include support for sound cards based on the
+ C-Media CMI8788 (Oxygen HD Audio) chip:
+ * Asound A-8788
+ * AuzenTech X-Meridian
+ * Bgears b-Enspirer
+ * Club3D Theatron DTS
+ * HT-Omega Claro
+ * Razer Barracuda AC-1
+ * Sondigo Inferno
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-oxygen.
+
config SND_CS4281
tristate "Cirrus Logic (Sound Fusion) CS4281"
depends on SND
@@ -623,6 +647,17 @@ config SND_HDSPM
To compile this driver as a module, choose M here: the module
will be called snd-hdspm.
+config SND_HIFIER
+ tristate "TempoTec HiFier Fantasia"
+ depends on SND
+ select SND_OXYGEN_LIB
+ help
+ Say Y here to include support for the MediaTek/TempoTec HiFier
+ Fantasia sound card.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-hifier.
+
config SND_ICE1712
tristate "ICEnsemble ICE1712 (Envy24)"
depends on SND
@@ -802,6 +837,16 @@ config SND_RME9652
To compile this driver as a module, choose M here: the module
will be called snd-rme9652.
+config SND_SIS7019
+ tristate "SiS 7019 Audio Accelerator"
+ depends on SND && X86 && !X86_64
+ select SND_AC97_CODEC
+ help
+ Say Y here to include support for the SiS 7019 Audio Accelerator.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-sis7019.
+
config SND_SONICVIBES
tristate "S3 SonicVibes"
depends on SND
@@ -850,6 +895,17 @@ config SND_VIA82XX_MODEM
To compile this driver as a module, choose M here: the module
will be called snd-via82xx-modem.
+config SND_VIRTUOSO
+ tristate "Asus Virtuoso 200 (Xonar)"
+ depends on SND
+ select SND_OXYGEN_LIB
+ help
+ Say Y here to include support for sound cards based on the
+ Asus AV200 chip, i.e., Xonar D2 and Xonar D2X.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-virtuoso.
+
config SND_VX222
tristate "Digigram VX222"
depends on SND
diff --git a/sound/pci/Makefile b/sound/pci/Makefile
index 09ddc82eeca..2d42fd28f4e 100644
--- a/sound/pci/Makefile
+++ b/sound/pci/Makefile
@@ -23,6 +23,7 @@ snd-intel8x0m-objs := intel8x0m.o
snd-maestro3-objs := maestro3.o
snd-rme32-objs := rme32.o
snd-rme96-objs := rme96.o
+snd-sis7019-objs := sis7019.o
snd-sonicvibes-objs := sonicvibes.o
snd-via82xx-objs := via82xx.o
snd-via82xx-modem-objs := via82xx_modem.o
@@ -48,6 +49,7 @@ obj-$(CONFIG_SND_INTEL8X0M) += snd-intel8x0m.o
obj-$(CONFIG_SND_MAESTRO3) += snd-maestro3.o
obj-$(CONFIG_SND_RME32) += snd-rme32.o
obj-$(CONFIG_SND_RME96) += snd-rme96.o
+obj-$(CONFIG_SND_SIS7019) += snd-sis7019.o
obj-$(CONFIG_SND_SONICVIBES) += snd-sonicvibes.o
obj-$(CONFIG_SND_VIA82XX) += snd-via82xx.o
obj-$(CONFIG_SND_VIA82XX_MODEM) += snd-via82xx-modem.o
@@ -66,6 +68,7 @@ obj-$(CONFIG_SND) += \
korg1212/ \
mixart/ \
nm256/ \
+ oxygen/ \
pcxhr/ \
riptide/ \
rme9652/ \
diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c
index 6a9966df0cc..45fd29017dd 100644
--- a/sound/pci/ac97/ac97_codec.c
+++ b/sound/pci/ac97/ac97_codec.c
@@ -22,7 +22,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/slab.h>
diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c
index 98c8b727b62..50c637e55ff 100644
--- a/sound/pci/ac97/ac97_patch.c
+++ b/sound/pci/ac97/ac97_patch.c
@@ -133,6 +133,14 @@ static int ac97_channel_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
unsigned char mode = ucontrol->value.enumerated.item[0];
+ if (kcontrol->private_value) {
+ if (mode >= 2)
+ return -EINVAL;
+ } else {
+ if (mode >= 3)
+ return -EINVAL;
+ }
+
if (mode != ac97->channel_mode) {
ac97->channel_mode = mode;
if (ac97->build_ops->update_jacks)
@@ -2142,8 +2150,7 @@ static int snd_ac97_ad1985_vrefout_put(struct snd_kcontrol *kcontrol,
struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
unsigned short val;
- if (ucontrol->value.enumerated.item[0] > 3
- || ucontrol->value.enumerated.item[0] < 0)
+ if (ucontrol->value.enumerated.item[0] > 3)
return -EINVAL;
val = ctrl2reg[ucontrol->value.enumerated.item[0]]
<< AC97_AD198X_VREF_SHIFT;
diff --git a/sound/pci/ac97/ac97_patch.h b/sound/pci/ac97/ac97_patch.h
index 9cccc27ea1b..47bf8dfe827 100644
--- a/sound/pci/ac97/ac97_patch.h
+++ b/sound/pci/ac97/ac97_patch.h
@@ -83,8 +83,10 @@ static int snd_ac97_swap_ctl(struct snd_ac97 *ac97, const char *s1,
const char *s2, const char *suffix);
static void snd_ac97_rename_vol_ctl(struct snd_ac97 *ac97, const char *src,
const char *dst);
+#ifdef CONFIG_PM
static void snd_ac97_restore_status(struct snd_ac97 *ac97);
static void snd_ac97_restore_iec958(struct snd_ac97 *ac97);
+#endif
static int snd_ac97_info_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo);
static int snd_ac97_get_enum_double(struct snd_kcontrol *kcontrol,
diff --git a/sound/pci/ac97/ac97_pcm.c b/sound/pci/ac97/ac97_pcm.c
index 8cbc03332b0..3674f35c4a7 100644
--- a/sound/pci/ac97/ac97_pcm.c
+++ b/sound/pci/ac97/ac97_pcm.c
@@ -23,7 +23,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/slab.h>
diff --git a/sound/pci/ac97/ac97_proc.c b/sound/pci/ac97/ac97_proc.c
index fed4a2c3d8a..060ea59d5f0 100644
--- a/sound/pci/ac97/ac97_proc.c
+++ b/sound/pci/ac97/ac97_proc.c
@@ -22,7 +22,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/slab.h>
#include <linux/mutex.h>
diff --git a/sound/pci/ac97/ak4531_codec.c b/sound/pci/ac97/ak4531_codec.c
index 722de451d15..c0c1633999e 100644
--- a/sound/pci/ac97/ak4531_codec.c
+++ b/sound/pci/ac97/ak4531_codec.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/slab.h>
diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c
index 98970d401be..a66d5150bb7 100644
--- a/sound/pci/ad1889.c
+++ b/sound/pci/ad1889.c
@@ -40,7 +40,6 @@
#include <linux/compiler.h>
#include <linux/delay.h>
-#include <sound/driver.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/initval.h>
@@ -1055,7 +1054,7 @@ static struct pci_device_id snd_ad1889_ids[] = {
};
MODULE_DEVICE_TABLE(pci, snd_ad1889_ids);
-static struct pci_driver ad1889_pci = {
+static struct pci_driver ad1889_pci_driver = {
.name = "AD1889 Audio",
.id_table = snd_ad1889_ids,
.probe = snd_ad1889_probe,
@@ -1065,13 +1064,13 @@ static struct pci_driver ad1889_pci = {
static int __init
alsa_ad1889_init(void)
{
- return pci_register_driver(&ad1889_pci);
+ return pci_register_driver(&ad1889_pci_driver);
}
static void __exit
alsa_ad1889_fini(void)
{
- pci_unregister_driver(&ad1889_pci);
+ pci_unregister_driver(&ad1889_pci_driver);
}
module_init(alsa_ad1889_init);
diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c
index 4c2bd7adf67..6a905ed9cbd 100644
--- a/sound/pci/ali5451/ali5451.c
+++ b/sound/pci/ali5451/ali5451.c
@@ -25,7 +25,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/als300.c b/sound/pci/als300.c
index 48cc39b771d..0e990a73582 100644
--- a/sound/pci/als300.c
+++ b/sound/pci/als300.c
@@ -30,7 +30,6 @@
* to keep track of what period we are in.
*/
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c
index 1190ef366a4..27ce6136ab0 100644
--- a/sound/pci/als4000.c
+++ b/sound/pci/als4000.c
@@ -63,7 +63,6 @@
* - power management? (card can do voice wakeup according to datasheet!!)
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/init.h>
#include <linux/pci.h>
diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c
index 89184a42414..4594186b83e 100644
--- a/sound/pci/atiixp.c
+++ b/sound/pci/atiixp.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
@@ -560,7 +559,7 @@ static int snd_atiixp_aclink_down(struct atiixp *chip)
ATI_REG_ISR_CODEC2_NOT_READY)
#define CODEC_CHECK_BITS (ALL_CODEC_NOT_READY|ATI_REG_ISR_NEW_FRAME)
-static int ac97_probing_bugs(struct pci_dev *pci)
+static int __devinit ac97_probing_bugs(struct pci_dev *pci)
{
const struct snd_pci_quirk *q;
@@ -574,7 +573,7 @@ static int ac97_probing_bugs(struct pci_dev *pci)
return -1;
}
-static int snd_atiixp_codec_detect(struct atiixp *chip)
+static int __devinit snd_atiixp_codec_detect(struct atiixp *chip)
{
int timeout;
diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c
index ce752f84457..a67a869180d 100644
--- a/sound/pci/atiixp_modem.c
+++ b/sound/pci/atiixp_modem.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/au88x0/au88x0.h b/sound/pci/au88x0/au88x0.h
index 5ccf0b1ec67..4aad35bba11 100644
--- a/sound/pci/au88x0/au88x0.h
+++ b/sound/pci/au88x0/au88x0.h
@@ -18,7 +18,6 @@
#define __SOUND_AU88X0_H
#ifdef __KERNEL__
-#include <sound/driver.h>
#include <linux/pci.h>
#include <asm/io.h>
#include <sound/core.h>
diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c
index 4a336eaae9d..333c62de862 100644
--- a/sound/pci/au88x0/au88x0_core.c
+++ b/sound/pci/au88x0/au88x0_core.c
@@ -2395,7 +2395,7 @@ static irqreturn_t vortex_interrupt(int irq, void *dev_id)
if (!(hwread(vortex->mmio, VORTEX_STAT) & 0x1))
return IRQ_NONE;
- // This is the Interrrupt Enable flag we set before (consistency check).
+ // This is the Interrupt Enable flag we set before (consistency check).
if (!(hwread(vortex->mmio, VORTEX_CTRL) & CTRL_IRQ_ENABLE))
return IRQ_NONE;
diff --git a/sound/pci/au88x0/au88x0_game.c b/sound/pci/au88x0/au88x0_game.c
index a07d1deba32..bc212f41a38 100644
--- a/sound/pci/au88x0/au88x0_game.c
+++ b/sound/pci/au88x0/au88x0_game.c
@@ -30,7 +30,6 @@
* driver. (email: mjander@embedded.cl).
*/
-#include <sound/driver.h>
#include <linux/time.h>
#include <linux/delay.h>
#include <linux/init.h>
diff --git a/sound/pci/au88x0/au88x0_mixer.c b/sound/pci/au88x0/au88x0_mixer.c
index c96da1dab86..c92f493d341 100644
--- a/sound/pci/au88x0/au88x0_mixer.c
+++ b/sound/pci/au88x0/au88x0_mixer.c
@@ -5,7 +5,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/time.h>
#include <linux/init.h>
#include <sound/core.h>
diff --git a/sound/pci/au88x0/au88x0_mpu401.c b/sound/pci/au88x0/au88x0_mpu401.c
index 8db3d3e6f7b..0dc8d259d1e 100644
--- a/sound/pci/au88x0/au88x0_mpu401.c
+++ b/sound/pci/au88x0/au88x0_mpu401.c
@@ -21,7 +21,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/time.h>
#include <linux/init.h>
#include <sound/core.h>
diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c
index 7b5baa17385..526c6c5ecf7 100644
--- a/sound/pci/au88x0/au88x0_pcm.c
+++ b/sound/pci/au88x0/au88x0_pcm.c
@@ -21,7 +21,6 @@
* It remains stuck,and DMA transfers do not happen.
*/
#include <sound/asoundef.h>
-#include <sound/driver.h>
#include <linux/time.h>
#include <sound/core.h>
#include <sound/pcm.h>
diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c
index 36d3666a5b7..4e71a55120a 100644
--- a/sound/pci/azt3328.c
+++ b/sound/pci/azt3328.c
@@ -115,7 +115,6 @@
* code (but I'm not too optimistic that doing this is possible at all)
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/init.h>
#include <linux/pci.h>
diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c
index 2dba752faf4..c9a2421cf6f 100644
--- a/sound/pci/bt87x.c
+++ b/sound/pci/bt87x.c
@@ -21,7 +21,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
diff --git a/sound/pci/ca0106/ca0106.h b/sound/pci/ca0106/ca0106.h
index 75da1746e75..74175fc80c7 100644
--- a/sound/pci/ca0106/ca0106.h
+++ b/sound/pci/ca0106/ca0106.h
@@ -272,7 +272,6 @@
#define SPCS_WORD_LENGTH_20A 0x0000000a /* Word Length 20 bit */
#define SPCS_WORD_LENGTH_20 0x00000009 /* Word Length 20 bit (both 0xa and 0x9 are 20 bit) */
#define SPCS_WORD_LENGTH_21 0x00000007 /* Word Length 21 bit */
-#define SPCS_WORD_LENGTH_21 0x00000007 /* Word Length 21 bit */
#define SPCS_WORD_LENGTH_22 0x00000005 /* Word Length 22 bit */
#define SPCS_WORD_LENGTH_23 0x00000003 /* Word Length 23 bit */
#define SPCS_WORD_LENGTH_24 0x0000000b /* Word Length 24 bit */
diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c
index 31d8db9f7a4..176e0f0e805 100644
--- a/sound/pci/ca0106/ca0106_main.c
+++ b/sound/pci/ca0106/ca0106_main.c
@@ -135,7 +135,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c
index 3f9b5c56003..af736869d9b 100644
--- a/sound/pci/ca0106/ca0106_mixer.c
+++ b/sound/pci/ca0106/ca0106_mixer.c
@@ -60,7 +60,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/ca0106/ca0106_proc.c b/sound/pci/ca0106/ca0106_proc.c
index 61f2718ae35..c62b7d10ec6 100644
--- a/sound/pci/ca0106/ca0106_proc.c
+++ b/sound/pci/ca0106/ca0106_proc.c
@@ -60,7 +60,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/ca0106/ca_midi.c b/sound/pci/ca0106/ca_midi.c
index ad32eff2713..893ee4f1ea7 100644
--- a/sound/pci/ca0106/ca_midi.c
+++ b/sound/pci/ca0106/ca_midi.c
@@ -27,7 +27,6 @@
*/
#include <linux/spinlock.h>
-#include <sound/driver.h>
#include <sound/core.h>
#include <sound/rawmidi.h>
diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c
index 1fa5f004e85..135f3086075 100644
--- a/sound/pci/cmipci.c
+++ b/sound/pci/cmipci.c
@@ -20,7 +20,6 @@
/* Does not work. Warning may block system in capture mode */
/* #define USE_VAR48KRATE */
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
@@ -150,6 +149,8 @@ MODULE_PARM_DESC(joystick_port, "Joystick port address.");
#define CM_CH0_SRATE_176K 0x00000200
#define CM_CH0_SRATE_96K 0x00000200 /* model 055? */
#define CM_CH0_SRATE_88K 0x00000100
+#define CM_CH0_SRATE_128K 0x00000300
+#define CM_CH0_SRATE_MASK 0x00000300
#define CM_SPDIF_INVERSE2 0x00000080 /* model 055? */
#define CM_DBLSPDS 0x00000040 /* double SPDIF sample rate 88.2/96 */
@@ -473,6 +474,7 @@ struct cmipci {
unsigned int can_ac3_sw: 1;
unsigned int can_ac3_hw: 1;
unsigned int can_multi_ch: 1;
+ unsigned int can_96k: 1; /* samplerate above 48k */
unsigned int do_soft_ac3: 1;
unsigned int spdif_playback_avail: 1; /* spdif ready? */
@@ -603,8 +605,6 @@ static unsigned int snd_cmipci_rate_freq(unsigned int rate)
{
unsigned int i;
- if (rate > 48000)
- rate /= 2;
for (i = 0; i < ARRAY_SIZE(rates); i++) {
if (rates[i] == rate)
return i;
@@ -782,7 +782,7 @@ static int set_dac_channels(struct cmipci *cm, struct cmipci_pcm *rec, int chann
static int snd_cmipci_pcm_prepare(struct cmipci *cm, struct cmipci_pcm *rec,
struct snd_pcm_substream *substream)
{
- unsigned int reg, freq, val;
+ unsigned int reg, freq, freq_ext, val;
unsigned int period_size;
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -830,7 +830,17 @@ static int snd_cmipci_pcm_prepare(struct cmipci *cm, struct cmipci_pcm *rec,
//snd_printd("cmipci: functrl0 = %08x\n", cm->ctrl);
/* set sample rate */
- freq = snd_cmipci_rate_freq(runtime->rate);
+ freq = 0;
+ freq_ext = 0;
+ if (runtime->rate > 48000)
+ switch (runtime->rate) {
+ case 88200: freq_ext = CM_CH0_SRATE_88K; break;
+ case 96000: freq_ext = CM_CH0_SRATE_96K; break;
+ case 128000: freq_ext = CM_CH0_SRATE_128K; break;
+ default: snd_BUG(); break;
+ }
+ else
+ freq = snd_cmipci_rate_freq(runtime->rate);
val = snd_cmipci_read(cm, CM_REG_FUNCTRL1);
if (rec->ch) {
val &= ~CM_DSFC_MASK;
@@ -851,19 +861,20 @@ static int snd_cmipci_pcm_prepare(struct cmipci *cm, struct cmipci_pcm *rec,
val &= ~CM_CH0FMT_MASK;
val |= rec->fmt << CM_CH0FMT_SHIFT;
}
- if (cm->chip_version == 68) {
- if (runtime->rate == 88200)
- val |= CM_CH0_SRATE_88K << (rec->ch * 2);
- else
- val &= ~(CM_CH0_SRATE_88K << (rec->ch * 2));
- if (runtime->rate == 96000)
- val |= CM_CH0_SRATE_96K << (rec->ch * 2);
- else
- val &= ~(CM_CH0_SRATE_96K << (rec->ch * 2));
+ if (cm->can_96k) {
+ val &= ~(CM_CH0_SRATE_MASK << (rec->ch * 2));
+ val |= freq_ext << (rec->ch * 2);
}
snd_cmipci_write(cm, CM_REG_CHFORMAT, val);
//snd_printd("cmipci: chformat = %08x\n", val);
+ if (!rec->is_dac && cm->chip_version) {
+ if (runtime->rate > 44100)
+ snd_cmipci_set_bit(cm, CM_REG_EXT_MISC, CM_ADC48K44K);
+ else
+ snd_cmipci_clear_bit(cm, CM_REG_EXT_MISC, CM_ADC48K44K);
+ }
+
rec->running = 0;
spin_unlock_irq(&cm->reg_lock);
@@ -1280,7 +1291,7 @@ static int snd_cmipci_playback_prepare(struct snd_pcm_substream *substream)
int rate = substream->runtime->rate;
int err, do_spdif, do_ac3 = 0;
- do_spdif = (rate >= 44100 &&
+ do_spdif = (rate >= 44100 && rate <= 96000 &&
substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE &&
substream->runtime->channels == 2);
if (do_spdif && cm->can_ac3_hw)
@@ -1336,10 +1347,8 @@ static void snd_cmipci_silence_hack(struct cmipci *cm, struct cmipci_pcm *rec)
val = snd_cmipci_read(cm, CM_REG_CHFORMAT);
val &= ~(CM_CH0FMT_MASK << (rec->ch * 2));
val |= (3 << CM_CH0FMT_SHIFT) << (rec->ch * 2);
- if (cm->chip_version == 68) {
- val &= ~(CM_CH0_SRATE_88K << (rec->ch * 2));
- val &= ~(CM_CH0_SRATE_96K << (rec->ch * 2));
- }
+ if (cm->can_96k)
+ val &= ~(CM_CH0_SRATE_MASK << (rec->ch * 2));
snd_cmipci_write(cm, CM_REG_CHFORMAT, val);
/* start stream (we don't need interrupts) */
@@ -1391,6 +1400,17 @@ static int snd_cmipci_capture_spdif_prepare(struct snd_pcm_substream *substream)
spin_lock_irq(&cm->reg_lock);
snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_CAPTURE_SPDF);
+ if (cm->can_96k) {
+ if (substream->runtime->rate > 48000)
+ snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_DBLSPDS);
+ else
+ snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_DBLSPDS);
+ }
+ if (snd_pcm_format_width(substream->runtime->format) > 16)
+ snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL);
+ else
+ snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL);
+
spin_unlock_irq(&cm->reg_lock);
return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_CAPT], substream);
@@ -1402,6 +1422,7 @@ static int snd_cmipci_capture_spdif_hw_free(struct snd_pcm_substream *subs)
spin_lock_irq(&cm->reg_lock);
snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_CAPTURE_SPDF);
+ snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL);
spin_unlock_irq(&cm->reg_lock);
return snd_cmipci_hw_free(subs);
@@ -1553,7 +1574,8 @@ static struct snd_pcm_hardware snd_cmipci_capture_spdif =
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID),
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE,
.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
.rate_min = 44100,
.rate_max = 48000,
@@ -1567,6 +1589,14 @@ static struct snd_pcm_hardware snd_cmipci_capture_spdif =
.fifo_size = 0,
};
+static unsigned int rate_constraints[] = { 5512, 8000, 11025, 16000, 22050,
+ 32000, 44100, 48000, 88200, 96000, 128000 };
+static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
+ .count = ARRAY_SIZE(rate_constraints),
+ .list = rate_constraints,
+ .mask = 0,
+};
+
/*
* check device open/close
*/
@@ -1636,6 +1666,13 @@ static int snd_cmipci_playback_open(struct snd_pcm_substream *substream)
runtime->hw.rates |= SNDRV_PCM_RATE_88200 |
SNDRV_PCM_RATE_96000;
runtime->hw.rate_max = 96000;
+ } else if (cm->chip_version == 55) {
+ err = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
+ if (err < 0)
+ return err;
+ runtime->hw.rates |= SNDRV_PCM_RATE_KNOT;
+ runtime->hw.rate_max = 128000;
}
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000);
cm->dig_pcm_status = cm->dig_status;
@@ -1654,6 +1691,13 @@ static int snd_cmipci_capture_open(struct snd_pcm_substream *substream)
if (cm->chip_version == 68) { // 8768 only supports 44k/48k recording
runtime->hw.rate_min = 41000;
runtime->hw.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000;
+ } else if (cm->chip_version == 55) {
+ err = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
+ if (err < 0)
+ return err;
+ runtime->hw.rates |= SNDRV_PCM_RATE_KNOT;
+ runtime->hw.rate_max = 128000;
}
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000);
return 0;
@@ -1685,6 +1729,13 @@ static int snd_cmipci_playback2_open(struct snd_pcm_substream *substream)
runtime->hw.rates |= SNDRV_PCM_RATE_88200 |
SNDRV_PCM_RATE_96000;
runtime->hw.rate_max = 96000;
+ } else if (cm->chip_version == 55) {
+ err = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
+ if (err < 0)
+ return err;
+ runtime->hw.rates |= SNDRV_PCM_RATE_KNOT;
+ runtime->hw.rate_max = 128000;
}
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000);
return 0;
@@ -1704,7 +1755,7 @@ static int snd_cmipci_playback_spdif_open(struct snd_pcm_substream *substream)
runtime->hw.formats |= SNDRV_PCM_FMTBIT_S32_LE;
snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
}
- if (cm->chip_version == 68) {
+ if (cm->can_96k) {
runtime->hw.rates |= SNDRV_PCM_RATE_88200 |
SNDRV_PCM_RATE_96000;
runtime->hw.rate_max = 96000;
@@ -1726,6 +1777,11 @@ static int snd_cmipci_capture_spdif_open(struct snd_pcm_substream *substream)
if ((err = open_device_check(cm, CM_OPEN_SPDIF_CAPTURE, substream)) < 0) /* use channel B */
return err;
runtime->hw = snd_cmipci_capture_spdif;
+ if (cm->can_96k && !(cm->chip_version == 68)) {
+ runtime->hw.rates |= SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000;
+ runtime->hw.rate_max = 96000;
+ }
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x40000);
return 0;
}
@@ -2594,10 +2650,8 @@ static struct snd_kcontrol_new snd_cmipci_extra_mixer_switches[] __devinitdata =
};
/* card control switches */
-static struct snd_kcontrol_new snd_cmipci_control_switches[] __devinitdata = {
- // DEFINE_CARD_SWITCH("Joystick", joystick), /* now module option */
- DEFINE_CARD_SWITCH("Modem", modem),
-};
+static struct snd_kcontrol_new snd_cmipci_modem_switch __devinitdata =
+DEFINE_CARD_SWITCH("Modem", modem);
static int __devinit snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_device)
@@ -2678,9 +2732,13 @@ static int __devinit snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_devic
}
/* card switches */
- sw = snd_cmipci_control_switches;
- for (idx = 0; idx < ARRAY_SIZE(snd_cmipci_control_switches); idx++, sw++) {
- err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm));
+ /*
+ * newer chips don't have the register bits to force modem link
+ * detection; the bit that was FLINKON now mutes CH1
+ */
+ if (cm->chip_version < 39) {
+ err = snd_ctl_add(cm->card,
+ snd_ctl_new1(&snd_cmipci_modem_switch, cm));
if (err < 0)
return err;
}
@@ -2785,9 +2843,11 @@ static void __devinit query_chip(struct cmipci *cm)
} else if (detect & CM_CHIP_8768) {
cm->chip_version = 68;
cm->max_channels = 8;
+ cm->can_96k = 1;
} else {
cm->chip_version = 55;
cm->max_channels = 6;
+ cm->can_96k = 1;
}
cm->can_ac3_hw = 1;
cm->can_multi_ch = 1;
diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c
index 9a55f4a9739..7556fd90d0e 100644
--- a/sound/pci/cs4281.c
+++ b/sound/pci/cs4281.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/cs46xx/cs46xx.c b/sound/pci/cs46xx/cs46xx.c
index 2699cb6c2cd..e876b3263e4 100644
--- a/sound/pci/cs46xx/cs46xx.c
+++ b/sound/pci/cs46xx/cs46xx.c
@@ -25,7 +25,6 @@
reloading the module may solve this.
*/
-#include <sound/driver.h>
#include <linux/pci.h>
#include <linux/time.h>
#include <linux/init.h>
diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c
index 2c7bfc9fef6..87ddffcd9d8 100644
--- a/sound/pci/cs46xx/cs46xx_lib.c
+++ b/sound/pci/cs46xx/cs46xx_lib.c
@@ -8,7 +8,7 @@
* - Sometimes the SPDIF input DSP tasks get's unsynchronized
* and the SPDIF get somewhat "distorcionated", or/and left right channel
* are swapped. To get around this problem when it happens, mute and unmute
- * the SPDIF input mixer controll.
+ * the SPDIF input mixer control.
* - On the Hercules Game Theater XP the amplifier are sometimes turned
* off on inadecuate moments which causes distorcions on sound.
*
@@ -45,7 +45,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/pm.h>
@@ -2084,71 +2083,6 @@ static int snd_cs46xx_spdif_stream_put(struct snd_kcontrol *kcontrol,
#endif /* CONFIG_SND_CS46XX_NEW_DSP */
-#ifdef CONFIG_SND_CS46XX_DEBUG_GPIO
-static int snd_cs46xx_egpio_select_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 = 8;
- return 0;
-}
-
-static int snd_cs46xx_egpio_select_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
- ucontrol->value.integer.value[0] = chip->current_gpio;
-
- return 0;
-}
-
-static int snd_cs46xx_egpio_select_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
- int change = (chip->current_gpio != ucontrol->value.integer.value[0]);
- chip->current_gpio = ucontrol->value.integer.value[0];
-
- return change;
-}
-
-
-static int snd_cs46xx_egpio_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
- int reg = kcontrol->private_value;
-
- snd_printdd ("put: reg = %04x, gpio %02x\n",reg,chip->current_gpio);
- ucontrol->value.integer.value[0] =
- (snd_cs46xx_peekBA0(chip, reg) & (1 << chip->current_gpio)) ? 1 : 0;
-
- return 0;
-}
-
-static int snd_cs46xx_egpio_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
- int reg = kcontrol->private_value;
- int val = snd_cs46xx_peekBA0(chip, reg);
- int oldval = val;
- snd_printdd ("put: reg = %04x, gpio %02x\n",reg,chip->current_gpio);
-
- if (ucontrol->value.integer.value[0])
- val |= (1 << chip->current_gpio);
- else
- val &= ~(1 << chip->current_gpio);
-
- snd_cs46xx_pokeBA0(chip, reg,val);
- snd_printdd ("put: val %08x oldval %08x\n",val,oldval);
-
- return (oldval != val);
-}
-#endif /* CONFIG_SND_CS46XX_DEBUG_GPIO */
-
static struct snd_kcontrol_new snd_cs46xx_controls[] __devinitdata = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -2241,40 +2175,6 @@ static struct snd_kcontrol_new snd_cs46xx_controls[] __devinitdata = {
},
#endif
-#ifdef CONFIG_SND_CS46XX_DEBUG_GPIO
-{
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "EGPIO select",
- .info = snd_cs46xx_egpio_select_info,
- .get = snd_cs46xx_egpio_select_get,
- .put = snd_cs46xx_egpio_select_put,
- .private_value = 0,
-},
-{
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "EGPIO Input/Output",
- .info = snd_mixer_boolean_info,
- .get = snd_cs46xx_egpio_get,
- .put = snd_cs46xx_egpio_put,
- .private_value = BA0_EGPIODR,
-},
-{
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "EGPIO CMOS/Open drain",
- .info = snd_mixer_boolean_info,
- .get = snd_cs46xx_egpio_get,
- .put = snd_cs46xx_egpio_put,
- .private_value = BA0_EGPIOPTR,
-},
-{
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "EGPIO On/Off",
- .info = snd_mixer_boolean_info,
- .get = snd_cs46xx_egpio_get,
- .put = snd_cs46xx_egpio_put,
- .private_value = BA0_EGPIOSR,
-},
-#endif
};
#ifdef CONFIG_SND_CS46XX_NEW_DSP
diff --git a/sound/pci/cs46xx/dsp_spos.c b/sound/pci/cs46xx/dsp_spos.c
index 590b35d91df..ccc8bedb5b1 100644
--- a/sound/pci/cs46xx/dsp_spos.c
+++ b/sound/pci/cs46xx/dsp_spos.c
@@ -20,7 +20,6 @@
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/pm.h>
diff --git a/sound/pci/cs46xx/dsp_spos_scb_lib.c b/sound/pci/cs46xx/dsp_spos_scb_lib.c
index eded4dfeba1..2873cfe48c3 100644
--- a/sound/pci/cs46xx/dsp_spos_scb_lib.c
+++ b/sound/pci/cs46xx/dsp_spos_scb_lib.c
@@ -21,7 +21,6 @@
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/pm.h>
diff --git a/sound/pci/cs5530.c b/sound/pci/cs5530.c
index 240a0a46220..7ff8b68e997 100644
--- a/sound/pci/cs5530.c
+++ b/sound/pci/cs5530.c
@@ -36,7 +36,6 @@
* same manner.
*/
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/pci.h>
diff --git a/sound/pci/cs5535audio/cs5535audio.c b/sound/pci/cs5535audio/cs5535audio.c
index 2b35889787b..1d8b1605253 100644
--- a/sound/pci/cs5535audio/cs5535audio.c
+++ b/sound/pci/cs5535audio/cs5535audio.c
@@ -21,7 +21,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
@@ -145,7 +144,7 @@ static unsigned short snd_cs5535audio_ac97_codec_read(struct snd_ac97 *ac97,
return snd_cs5535audio_codec_read(cs5535au, reg);
}
-static int snd_cs5535audio_mixer(struct cs5535audio *cs5535au)
+static int __devinit snd_cs5535audio_mixer(struct cs5535audio *cs5535au)
{
struct snd_card *card = cs5535au->card;
struct snd_ac97_bus *pbus;
diff --git a/sound/pci/cs5535audio/cs5535audio_pcm.c b/sound/pci/cs5535audio/cs5535audio_pcm.c
index 21df0634af3..cdcda87116c 100644
--- a/sound/pci/cs5535audio/cs5535audio_pcm.c
+++ b/sound/pci/cs5535audio/cs5535audio_pcm.c
@@ -25,7 +25,6 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/pci.h>
-#include <sound/driver.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/initval.h>
@@ -98,6 +97,8 @@ static int snd_cs5535audio_playback_open(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
runtime->hw = snd_cs5535audio_playback;
+ runtime->hw.rates = cs5535au->ac97->rates[AC97_RATES_FRONT_DAC];
+ snd_pcm_limit_hw_rates(runtime);
cs5535au->playback_substream = substream;
runtime->private_data = &(cs5535au->dmas[CS5535AUDIO_DMA_PLAYBACK]);
if ((err = snd_pcm_hw_constraint_integer(runtime,
@@ -343,6 +344,8 @@ static int snd_cs5535audio_capture_open(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
runtime->hw = snd_cs5535audio_capture;
+ runtime->hw.rates = cs5535au->ac97->rates[AC97_RATES_ADC];
+ snd_pcm_limit_hw_rates(runtime);
cs5535au->capture_substream = substream;
runtime->private_data = &(cs5535au->dmas[CS5535AUDIO_DMA_CAPTURE]);
if ((err = snd_pcm_hw_constraint_integer(runtime,
diff --git a/sound/pci/cs5535audio/cs5535audio_pm.c b/sound/pci/cs5535audio/cs5535audio_pm.c
index 838708f6d45..564c33b6095 100644
--- a/sound/pci/cs5535audio/cs5535audio_pm.c
+++ b/sound/pci/cs5535audio/cs5535audio_pm.c
@@ -22,7 +22,6 @@
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/delay.h>
-#include <sound/driver.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/initval.h>
diff --git a/sound/pci/echoaudio/darla20.c b/sound/pci/echoaudio/darla20.c
index 87078d3a685..8c6db3aa3c1 100644
--- a/sound/pci/echoaudio/darla20.c
+++ b/sound/pci/echoaudio/darla20.c
@@ -36,7 +36,6 @@
#define BX_NUM 10
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/echoaudio/darla24.c b/sound/pci/echoaudio/darla24.c
index 42b48f9d212..04cbf3eaf05 100644
--- a/sound/pci/echoaudio/darla24.c
+++ b/sound/pci/echoaudio/darla24.c
@@ -40,7 +40,6 @@
#define BX_NUM 10
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/echoaudio/echo3g.c b/sound/pci/echoaudio/echo3g.c
index 8dbb7ac865c..4022e43a005 100644
--- a/sound/pci/echoaudio/echo3g.c
+++ b/sound/pci/echoaudio/echo3g.c
@@ -47,7 +47,6 @@
#define BX_NUM chip->bx_num
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c
index 499ee1a5319..90ec090792b 100644
--- a/sound/pci/echoaudio/echoaudio.c
+++ b/sound/pci/echoaudio/echoaudio.c
@@ -378,7 +378,7 @@ static int pcm_digital_in_open(struct snd_pcm_substream *substream)
DE_ACT(("pcm_digital_in_open\n"));
max_channels = num_digital_busses_in(chip) - substream->number;
- down(&chip->mode_mutex);
+ mutex_lock(&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
@@ -405,7 +405,7 @@ static int pcm_digital_in_open(struct snd_pcm_substream *substream)
chip->can_set_rate=0;
din_exit:
- up(&chip->mode_mutex);
+ mutex_unlock(&chip->mode_mutex);
return err;
}
@@ -420,7 +420,7 @@ static int pcm_digital_out_open(struct snd_pcm_substream *substream)
DE_ACT(("pcm_digital_out_open\n"));
max_channels = num_digital_busses_out(chip) - substream->number;
- down(&chip->mode_mutex);
+ mutex_lock(&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
@@ -447,7 +447,7 @@ static int pcm_digital_out_open(struct snd_pcm_substream *substream)
if (atomic_read(&chip->opencount) > 1 && chip->rate_set)
chip->can_set_rate=0;
dout_exit:
- up(&chip->mode_mutex);
+ mutex_unlock(&chip->mode_mutex);
return err;
}
@@ -1420,7 +1420,7 @@ static int snd_echo_digital_mode_put(struct snd_kcontrol *kcontrol,
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);
+ mutex_lock(&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
@@ -1439,7 +1439,7 @@ static int snd_echo_digital_mode_put(struct snd_kcontrol *kcontrol,
if (changed >= 0)
changed = 1; /* No errors */
}
- up(&chip->mode_mutex);
+ mutex_unlock(&chip->mode_mutex);
}
return changed;
}
@@ -1566,12 +1566,12 @@ static int snd_echo_clock_source_put(struct snd_kcontrol *kcontrol,
return -EINVAL;
dclock = chip->clock_source_list[eclock];
if (chip->input_clock != dclock) {
- down(&chip->mode_mutex);
+ mutex_lock(&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);
+ mutex_unlock(&chip->mode_mutex);
}
if (changed < 0)
@@ -1972,7 +1972,7 @@ static __devinit int snd_echo_create(struct snd_card *card,
return err;
}
atomic_set(&chip->opencount, 0);
- init_MUTEX(&chip->mode_mutex);
+ mutex_init(&chip->mode_mutex);
chip->can_set_rate = 1;
*rchip = chip;
/* Init done ! */
diff --git a/sound/pci/echoaudio/echoaudio.h b/sound/pci/echoaudio/echoaudio.h
index 7e88c968e22..1c88e051abf 100644
--- a/sound/pci/echoaudio/echoaudio.h
+++ b/sound/pci/echoaudio/echoaudio.h
@@ -361,7 +361,7 @@ struct echoaudio {
spinlock_t lock;
struct snd_pcm_substream *substream[DSP_MAXPIPES];
int last_period[DSP_MAXPIPES];
- struct semaphore mode_mutex;
+ struct mutex mode_mutex;
u16 num_digital_modes, digital_mode_list[6];
u16 num_clock_sources, clock_source_list[10];
atomic_t opencount;
diff --git a/sound/pci/echoaudio/gina20.c b/sound/pci/echoaudio/gina20.c
index fee2d483173..c0e64b8f52a 100644
--- a/sound/pci/echoaudio/gina20.c
+++ b/sound/pci/echoaudio/gina20.c
@@ -40,7 +40,6 @@
#define BX_NUM 14
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/echoaudio/gina24.c b/sound/pci/echoaudio/gina24.c
index d5eae470fe9..c36a78dd0b5 100644
--- a/sound/pci/echoaudio/gina24.c
+++ b/sound/pci/echoaudio/gina24.c
@@ -46,7 +46,6 @@
#define BX_NUM 26
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/echoaudio/indigo.c b/sound/pci/echoaudio/indigo.c
index 40f601cd016..0a58a7c1fd7 100644
--- a/sound/pci/echoaudio/indigo.c
+++ b/sound/pci/echoaudio/indigo.c
@@ -38,7 +38,6 @@
#define BX_NUM 2
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/echoaudio/indigodj.c b/sound/pci/echoaudio/indigodj.c
index 771c5383210..2db24d29332 100644
--- a/sound/pci/echoaudio/indigodj.c
+++ b/sound/pci/echoaudio/indigodj.c
@@ -38,7 +38,6 @@
#define BX_NUM 4
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/echoaudio/indigoio.c b/sound/pci/echoaudio/indigoio.c
index 49c550defcf..a60c0a0a89b 100644
--- a/sound/pci/echoaudio/indigoio.c
+++ b/sound/pci/echoaudio/indigoio.c
@@ -39,7 +39,6 @@
#define BX_NUM 4
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/echoaudio/layla20.c b/sound/pci/echoaudio/layla20.c
index 8f5483a405a..50619468899 100644
--- a/sound/pci/echoaudio/layla20.c
+++ b/sound/pci/echoaudio/layla20.c
@@ -45,7 +45,6 @@
#define BX_NUM 22
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/echoaudio/layla24.c b/sound/pci/echoaudio/layla24.c
index 0524667c02f..e09e3ea7781 100644
--- a/sound/pci/echoaudio/layla24.c
+++ b/sound/pci/echoaudio/layla24.c
@@ -47,7 +47,6 @@
#define BX_NUM 32
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/echoaudio/mia.c b/sound/pci/echoaudio/mia.c
index 893c7c20dd7..f3b9b45c9c1 100644
--- a/sound/pci/echoaudio/mia.c
+++ b/sound/pci/echoaudio/mia.c
@@ -45,7 +45,6 @@
#define BX_NUM 8
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/echoaudio/mona.c b/sound/pci/echoaudio/mona.c
index 3a5d5b0020d..b05bad94490 100644
--- a/sound/pci/echoaudio/mona.c
+++ b/sound/pci/echoaudio/mona.c
@@ -44,7 +44,6 @@
#define BX_NUM 26
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c
index 9680caff90c..8354c1a8331 100644
--- a/sound/pci/emu10k1/emu10k1.c
+++ b/sound/pci/emu10k1/emu10k1.c
@@ -23,7 +23,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/time.h>
diff --git a/sound/pci/emu10k1/emu10k1_callback.c b/sound/pci/emu10k1/emu10k1_callback.c
index 01965bd9996..45088ebcce5 100644
--- a/sound/pci/emu10k1/emu10k1_callback.c
+++ b/sound/pci/emu10k1/emu10k1_callback.c
@@ -35,9 +35,9 @@ struct best_voice {
/*
* prototypes
*/
-static void lookup_voices(struct snd_emux *emu, struct snd_emu10k1 *hw,
+static void lookup_voices(struct snd_emux *emux, struct snd_emu10k1 *hw,
struct best_voice *best, int active_only);
-static struct snd_emux_voice *get_voice(struct snd_emux *emu,
+static struct snd_emux_voice *get_voice(struct snd_emux *emux,
struct snd_emux_port *port);
static int start_voice(struct snd_emux_voice *vp);
static void trigger_voice(struct snd_emux_voice *vp);
@@ -45,7 +45,6 @@ static void release_voice(struct snd_emux_voice *vp);
static void update_voice(struct snd_emux_voice *vp, int update);
static void terminate_voice(struct snd_emux_voice *vp);
static void free_voice(struct snd_emux_voice *vp);
-
static void set_fmmod(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);
static void set_fm2frq2(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);
static void set_filterQ(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);
@@ -75,9 +74,9 @@ static struct snd_emux_operators emu10k1_ops = {
};
void
-snd_emu10k1_ops_setup(struct snd_emux *emu)
+snd_emu10k1_ops_setup(struct snd_emux *emux)
{
- emu->ops = emu10k1_ops;
+ emux->ops = emu10k1_ops;
}
@@ -166,7 +165,11 @@ free_voice(struct snd_emux_voice *vp)
struct snd_emu10k1 *hw;
hw = vp->hw;
- if (vp->ch >= 0) {
+ /* FIXME: emu10k1_synth is broken. */
+ /* This can get called with hw == 0 */
+ /* Problem apparent on plug, unplug then plug */
+ /* on the Audigy 2 ZS Notebook. */
+ if (hw && (vp->ch >= 0)) {
snd_emu10k1_ptr_write(hw, IFATN, vp->ch, 0xff00);
snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK);
// snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0);
diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c
index 97c41d72a25..9a9b977d3cf 100644
--- a/sound/pci/emu10k1/emu10k1_main.c
+++ b/sound/pci/emu10k1/emu10k1_main.c
@@ -33,7 +33,6 @@
#include <linux/sched.h>
#include <linux/kthread.h>
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
@@ -55,12 +54,14 @@
#define DOCK_FILENAME "emu/audio_dock.fw"
#define EMU1010B_FILENAME "emu/emu1010b.fw"
#define MICRO_DOCK_FILENAME "emu/micro_dock.fw"
+#define EMU0404_FILENAME "emu/emu0404.fw"
#define EMU1010_NOTEBOOK_FILENAME "emu/emu1010_notebook.fw"
MODULE_FIRMWARE(HANA_FILENAME);
MODULE_FIRMWARE(DOCK_FILENAME);
MODULE_FIRMWARE(EMU1010B_FILENAME);
MODULE_FIRMWARE(MICRO_DOCK_FILENAME);
+MODULE_FIRMWARE(EMU0404_FILENAME);
MODULE_FIRMWARE(EMU1010_NOTEBOOK_FILENAME);
@@ -258,7 +259,6 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
* GPIO7: Unknown
*/
outl(0x76, emu->port + A_IOCFG); /* Windows uses 0x3f76 */
-
}
if (emu->card_capabilities->i2c_adc) { /* Audigy 2 ZS Notebook with ADC Wolfson WM8775 */
int size, n;
@@ -274,7 +274,6 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
emu->i2c_capture_volume[n][0]= 0xcf;
emu->i2c_capture_volume[n][1]= 0xcf;
}
-
}
@@ -288,7 +287,7 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
snd_emu10k1_ptr_write(emu, MAPB, ch, silent_page);
}
- if (emu->card_capabilities->emu1010) {
+ if (emu->card_capabilities->emu_model) {
outl(HCFG_AUTOMUTE_ASYNC |
HCFG_EMU32_SLAVE |
HCFG_AUDIOENABLE, emu->port + HCFG);
@@ -318,7 +317,7 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
outl(HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG);
if (enable_ir) { /* enable IR for SB Live */
- if (emu->card_capabilities->emu1010) {
+ if (emu->card_capabilities->emu_model) {
; /* Disable all access to A_IOCFG for the emu1010 */
} else if (emu->card_capabilities->i2c_adc) {
; /* Disable A_IOCFG for Audigy 2 ZS Notebook */
@@ -339,7 +338,7 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
}
}
- if (emu->card_capabilities->emu1010) {
+ if (emu->card_capabilities->emu_model) {
; /* Disable all access to A_IOCFG for the emu1010 */
} else if (emu->card_capabilities->i2c_adc) {
; /* Disable A_IOCFG for Audigy 2 ZS Notebook */
@@ -359,7 +358,7 @@ static void snd_emu10k1_audio_enable(struct snd_emu10k1 *emu)
outl(inl(emu->port + HCFG) | HCFG_AUDIOENABLE, emu->port + HCFG);
/* Enable analog/digital outs on audigy */
- if (emu->card_capabilities->emu1010) {
+ if (emu->card_capabilities->emu_model) {
; /* Disable all access to A_IOCFG for the emu1010 */
} else if (emu->card_capabilities->i2c_adc) {
; /* Disable A_IOCFG for Audigy 2 ZS Notebook */
@@ -652,6 +651,8 @@ static int snd_emu10k1_cardbus_init(struct snd_emu10k1 * emu)
value = inl(special_port);
snd_emu10k1_ptr20_write(emu, TINA2_VOLUME, 0, 0xfefefefe); /* Defaults to 0x30303030 */
+ /* Delay to give time for ADC chip to switch on. It needs 113ms */
+ msleep(200);
return 0;
}
@@ -661,6 +662,8 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * file
int n, i;
int reg;
int value;
+ unsigned int write_post;
+ unsigned long flags;
const struct firmware *fw_entry;
if ((err = request_firmware(&fw_entry, filename, &emu->pci->dev)) != 0) {
@@ -668,12 +671,6 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * file
return err;
}
snd_printk(KERN_INFO "firmware size=0x%zx\n", fw_entry->size);
-#if 0
- if (fw_entry->size != 0x133a4) {
- snd_printk(KERN_ERR "firmware: %s wrong size.\n",filename);
- return -EINVAL;
- }
-#endif
/* The FPGA is a Xilinx Spartan IIE XC2S50E */
/* GPIO7 -> FPGA PGMN
@@ -681,9 +678,12 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * file
* GPIO5 -> FPGA DIN
* FPGA CONFIG OFF -> FPGA PGMN
*/
+ spin_lock_irqsave(&emu->emu_lock, flags);
outl(0x00, emu->port + A_IOCFG); /* Set PGMN low for 1uS. */
- udelay(1);
+ write_post = inl(emu->port + A_IOCFG);
+ udelay(100);
outl(0x80, emu->port + A_IOCFG); /* Leave bit 7 set during netlist setup. */
+ write_post = inl(emu->port + A_IOCFG);
udelay(100); /* Allow FPGA memory to clean */
for(n = 0; n < fw_entry->size; n++) {
value=fw_entry->data[n];
@@ -693,18 +693,22 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * file
reg = reg | 0x20;
value = value >> 1;
outl(reg, emu->port + A_IOCFG);
+ write_post = inl(emu->port + A_IOCFG);
outl(reg | 0x40, emu->port + A_IOCFG);
+ write_post = inl(emu->port + A_IOCFG);
}
}
/* After programming, set GPIO bit 4 high again. */
outl(0x10, emu->port + A_IOCFG);
-
+ write_post = inl(emu->port + A_IOCFG);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
release_firmware(fw_entry);
return 0;
}
-int emu1010_firmware_thread(void *data) {
+static int emu1010_firmware_thread(void *data)
+{
struct snd_emu10k1 * emu = data;
int tmp,tmp2;
int reg;
@@ -712,7 +716,7 @@ int emu1010_firmware_thread(void *data) {
for (;;) {
/* Delay to allow Audio Dock to settle */
- msleep(1000);
+ msleep_interruptible(1000);
if (kthread_should_stop())
break;
snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp ); /* IRQ Status */
@@ -722,17 +726,20 @@ int emu1010_firmware_thread(void *data) {
/* Return to Audio Dock programming mode */
snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware\n");
snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, EMU_HANA_FPGA_CONFIG_AUDIODOCK );
- if (emu->card_capabilities->emu1010 == 1) {
+ if (emu->card_capabilities->emu_model ==
+ EMU_MODEL_EMU1010) {
if ((err = snd_emu1010_load_firmware(emu, DOCK_FILENAME)) != 0) {
- return err;
+ continue;
}
- } else if (emu->card_capabilities->emu1010 == 2) {
+ } else if (emu->card_capabilities->emu_model ==
+ EMU_MODEL_EMU1010B) {
if ((err = snd_emu1010_load_firmware(emu, MICRO_DOCK_FILENAME)) != 0) {
- return err;
+ continue;
}
- } else if (emu->card_capabilities->emu1010 == 3) {
+ } else if (emu->card_capabilities->emu_model ==
+ EMU_MODEL_EMU1616) {
if ((err = snd_emu1010_load_firmware(emu, MICRO_DOCK_FILENAME)) != 0) {
- return err;
+ continue;
}
}
@@ -745,8 +752,7 @@ int emu1010_firmware_thread(void *data) {
if ((reg & 0x1f) != 0x15) {
/* FPGA failed to be programmed */
snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware file failed, reg=0x%x\n", reg);
- return 0;
- return -ENODEV;
+ continue;
}
snd_printk(KERN_INFO "emu1010: Audio Dock Firmware loaded\n");
snd_emu1010_fpga_read(emu, EMU_DOCK_MAJOR_REV, &tmp );
@@ -757,9 +763,9 @@ int emu1010_firmware_thread(void *data) {
msleep(10);
/* Unmute all. Default is muted after a firmware load */
snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE );
- break;
}
}
+ snd_printk(KERN_INFO "emu1010: firmware thread stopping\n");
return 0;
}
@@ -800,6 +806,7 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
int tmp,tmp2;
int reg;
int err;
+ const char *filename = NULL;
snd_printk(KERN_INFO "emu1010: Special config.\n");
/* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave,
@@ -841,21 +848,31 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
return -ENODEV;
}
snd_printk(KERN_INFO "emu1010: EMU_HANA_ID=0x%x\n",reg);
- if (emu->card_capabilities->emu1010 == 1) {
- if ((err = snd_emu1010_load_firmware(emu, HANA_FILENAME)) != 0) {
- snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file %s failed\n", HANA_FILENAME);
- return err;
- }
- } else if (emu->card_capabilities->emu1010 == 2) {
- if ((err = snd_emu1010_load_firmware(emu, EMU1010B_FILENAME)) != 0) {
- snd_printk(KERN_INFO "emu1010: Loading Firmware file %s failed\n", EMU1010B_FILENAME);
- return err;
- }
- } else if (emu->card_capabilities->emu1010 == 3) {
- if ((err = snd_emu1010_load_firmware(emu, EMU1010_NOTEBOOK_FILENAME)) != 0) {
- snd_printk(KERN_INFO "emu1010: Loading Firmware file %s failed\n", EMU1010_NOTEBOOK_FILENAME);
- return err;
- }
+ switch (emu->card_capabilities->emu_model) {
+ case EMU_MODEL_EMU1010:
+ filename = HANA_FILENAME;
+ break;
+ case EMU_MODEL_EMU1010B:
+ filename = EMU1010B_FILENAME;
+ break;
+ case EMU_MODEL_EMU1616:
+ filename = EMU1010_NOTEBOOK_FILENAME;
+ break;
+ case EMU_MODEL_EMU0404:
+ filename = EMU0404_FILENAME;
+ break;
+ default:
+ filename = NULL;
+ return -ENODEV;
+ break;
+ }
+ snd_printk(KERN_INFO "emu1010: filename %s testing\n", filename);
+ err = snd_emu1010_load_firmware(emu, filename);
+ if (err != 0) {
+ snd_printk(
+ KERN_INFO "emu1010: Loading Firmware file %s failed\n",
+ filename);
+ return err;
}
/* ID, should read & 0x7f = 0x55 when FPGA programmed. */
@@ -1074,10 +1091,12 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, 0x10 ); /* SPDIF Format spdif (or 0x11 for aes/ebu) */
/* Start Micro/Audio Dock firmware loader thread */
- emu->emu1010.firmware_thread = kthread_create(&emu1010_firmware_thread,
- emu,
- "emu1010_firmware");
- wake_up_process(emu->emu1010.firmware_thread);
+ if (!emu->emu1010.firmware_thread) {
+ emu->emu1010.firmware_thread =
+ kthread_create(emu1010_firmware_thread, emu,
+ "emu1010_firmware");
+ wake_up_process(emu->emu1010.firmware_thread);
+ }
#if 0
snd_emu1010_fpga_link_dst_src_write(emu,
@@ -1090,79 +1109,114 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
EMU_DST_HANA_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 3); /* ALICE2 bus 0xb3 */
#endif
/* Default outputs */
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_DAC1_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */
- emu->emu1010.output_source[0] = 21;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_DAC1_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
- emu->emu1010.output_source[1] = 22;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_DAC2_LEFT1, EMU_SRC_ALICE_EMU32A + 2);
- emu->emu1010.output_source[2] = 23;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_DAC2_RIGHT1, EMU_SRC_ALICE_EMU32A + 3);
- emu->emu1010.output_source[3] = 24;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_DAC3_LEFT1, EMU_SRC_ALICE_EMU32A + 4);
- emu->emu1010.output_source[4] = 25;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_DAC3_RIGHT1, EMU_SRC_ALICE_EMU32A + 5);
- emu->emu1010.output_source[5] = 26;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_DAC4_LEFT1, EMU_SRC_ALICE_EMU32A + 6);
- emu->emu1010.output_source[6] = 27;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_DAC4_RIGHT1, EMU_SRC_ALICE_EMU32A + 7);
- emu->emu1010.output_source[7] = 28;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_PHONES_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */
- emu->emu1010.output_source[8] = 21;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_PHONES_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
- emu->emu1010.output_source[9] = 22;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */
- emu->emu1010.output_source[10] = 21;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
- emu->emu1010.output_source[11] = 22;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HANA_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */
- emu->emu1010.output_source[12] = 21;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HANA_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
- emu->emu1010.output_source[13] = 22;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HAMOA_DAC_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */
- emu->emu1010.output_source[14] = 21;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HAMOA_DAC_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
- emu->emu1010.output_source[15] = 22;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HANA_ADAT, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */
- emu->emu1010.output_source[16] = 21;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HANA_ADAT + 1, EMU_SRC_ALICE_EMU32A + 1);
- emu->emu1010.output_source[17] = 22;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HANA_ADAT + 2, EMU_SRC_ALICE_EMU32A + 2);
- emu->emu1010.output_source[18] = 23;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HANA_ADAT + 3, EMU_SRC_ALICE_EMU32A + 3);
- emu->emu1010.output_source[19] = 24;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HANA_ADAT + 4, EMU_SRC_ALICE_EMU32A + 4);
- emu->emu1010.output_source[20] = 25;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HANA_ADAT + 5, EMU_SRC_ALICE_EMU32A + 5);
- emu->emu1010.output_source[21] = 26;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HANA_ADAT + 6, EMU_SRC_ALICE_EMU32A + 6);
- emu->emu1010.output_source[22] = 27;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HANA_ADAT + 7, EMU_SRC_ALICE_EMU32A + 7);
- emu->emu1010.output_source[23] = 28;
-
+ if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616) {
+ /* 1616(M) cardbus default outputs */
+ /* ALICE2 bus 0xa0 */
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_DOCK_DAC1_LEFT1, EMU_SRC_ALICE_EMU32A + 0);
+ emu->emu1010.output_source[0] = 17;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_DOCK_DAC1_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
+ emu->emu1010.output_source[1] = 18;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_DOCK_DAC2_LEFT1, EMU_SRC_ALICE_EMU32A + 2);
+ emu->emu1010.output_source[2] = 19;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_DOCK_DAC2_RIGHT1, EMU_SRC_ALICE_EMU32A + 3);
+ emu->emu1010.output_source[3] = 20;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_DOCK_DAC3_LEFT1, EMU_SRC_ALICE_EMU32A + 4);
+ emu->emu1010.output_source[4] = 21;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_DOCK_DAC3_RIGHT1, EMU_SRC_ALICE_EMU32A + 5);
+ emu->emu1010.output_source[5] = 22;
+ /* ALICE2 bus 0xa0 */
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_MANA_DAC_LEFT, EMU_SRC_ALICE_EMU32A + 0);
+ emu->emu1010.output_source[16] = 17;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_MANA_DAC_RIGHT, EMU_SRC_ALICE_EMU32A + 1);
+ emu->emu1010.output_source[17] = 18;
+ } else {
+ /* ALICE2 bus 0xa0 */
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_DOCK_DAC1_LEFT1, EMU_SRC_ALICE_EMU32A + 0);
+ emu->emu1010.output_source[0] = 21;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_DOCK_DAC1_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
+ emu->emu1010.output_source[1] = 22;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_DOCK_DAC2_LEFT1, EMU_SRC_ALICE_EMU32A + 2);
+ emu->emu1010.output_source[2] = 23;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_DOCK_DAC2_RIGHT1, EMU_SRC_ALICE_EMU32A + 3);
+ emu->emu1010.output_source[3] = 24;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_DOCK_DAC3_LEFT1, EMU_SRC_ALICE_EMU32A + 4);
+ emu->emu1010.output_source[4] = 25;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_DOCK_DAC3_RIGHT1, EMU_SRC_ALICE_EMU32A + 5);
+ emu->emu1010.output_source[5] = 26;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_DOCK_DAC4_LEFT1, EMU_SRC_ALICE_EMU32A + 6);
+ emu->emu1010.output_source[6] = 27;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_DOCK_DAC4_RIGHT1, EMU_SRC_ALICE_EMU32A + 7);
+ emu->emu1010.output_source[7] = 28;
+ /* ALICE2 bus 0xa0 */
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_DOCK_PHONES_LEFT1, EMU_SRC_ALICE_EMU32A + 0);
+ emu->emu1010.output_source[8] = 21;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_DOCK_PHONES_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
+ emu->emu1010.output_source[9] = 22;
+ /* ALICE2 bus 0xa0 */
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_DOCK_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 0);
+ emu->emu1010.output_source[10] = 21;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_DOCK_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
+ emu->emu1010.output_source[11] = 22;
+ /* ALICE2 bus 0xa0 */
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_HANA_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 0);
+ emu->emu1010.output_source[12] = 21;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_HANA_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
+ emu->emu1010.output_source[13] = 22;
+ /* ALICE2 bus 0xa0 */
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_HAMOA_DAC_LEFT1, EMU_SRC_ALICE_EMU32A + 0);
+ emu->emu1010.output_source[14] = 21;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_HAMOA_DAC_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
+ emu->emu1010.output_source[15] = 22;
+ /* ALICE2 bus 0xa0 */
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_HANA_ADAT, EMU_SRC_ALICE_EMU32A + 0);
+ emu->emu1010.output_source[16] = 21;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_HANA_ADAT + 1, EMU_SRC_ALICE_EMU32A + 1);
+ emu->emu1010.output_source[17] = 22;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_HANA_ADAT + 2, EMU_SRC_ALICE_EMU32A + 2);
+ emu->emu1010.output_source[18] = 23;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_HANA_ADAT + 3, EMU_SRC_ALICE_EMU32A + 3);
+ emu->emu1010.output_source[19] = 24;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_HANA_ADAT + 4, EMU_SRC_ALICE_EMU32A + 4);
+ emu->emu1010.output_source[20] = 25;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_HANA_ADAT + 5, EMU_SRC_ALICE_EMU32A + 5);
+ emu->emu1010.output_source[21] = 26;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_HANA_ADAT + 6, EMU_SRC_ALICE_EMU32A + 6);
+ emu->emu1010.output_source[22] = 27;
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_HANA_ADAT + 7, EMU_SRC_ALICE_EMU32A + 7);
+ emu->emu1010.output_source[23] = 28;
+ }
/* TEMP: Select SPDIF in/out */
//snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, 0x0); /* Output spdif */
@@ -1202,11 +1256,12 @@ static int snd_emu10k1_free(struct snd_emu10k1 *emu)
}
snd_emu10k1_free_efx(emu);
}
- if (emu->card_capabilities->emu1010) {
+ if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1010) {
/* Disable 48Volt power to Audio Dock */
snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0 );
- kthread_stop(emu->emu1010.firmware_thread);
}
+ if (emu->emu1010.firmware_thread)
+ kthread_stop(emu->emu1010.firmware_thread);
if (emu->memhdr)
snd_util_memhdr_free(emu->memhdr);
if (emu->silent_page.area)
@@ -1338,6 +1393,7 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.spi_dac = 1,
.i2c_adc = 1,
.spk71 = 1} ,
+ /* Tested by James@superbug.co.uk 4th Nov 2007. */
{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x42011102,
.driver = "Audigy2", .name = "E-mu 1010 Notebook [MAEM8950]",
.id = "EMU1010",
@@ -1345,28 +1401,46 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.ca0108_chip = 1,
.ca_cardbus_chip = 1,
.spk71 = 1 ,
- .emu1010 = 3} ,
+ .emu_model = EMU_MODEL_EMU1616},
+ /* Tested by James@superbug.co.uk 4th Nov 2007. */
{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40041102,
.driver = "Audigy2", .name = "E-mu 1010b PCI [MAEM????]",
.id = "EMU1010",
.emu10k2_chip = 1,
.ca0108_chip = 1,
- .spk71 = 1 ,
- .emu1010 = 2} ,
+ .spk71 = 1,
+ .emu_model = EMU_MODEL_EMU1010B},
+ /* Tested by James@superbug.co.uk 8th July 2005. */
+ {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x40011102,
+ .driver = "Audigy2", .name = "E-mu 1010 [4001]",
+ .id = "EMU1010",
+ .emu10k2_chip = 1,
+ .ca0102_chip = 1,
+ .spk71 = 1,
+ .emu_model = EMU_MODEL_EMU1010}, /* Emu 1010 */
+ /* EMU0404b */
+ {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40021102,
+ .driver = "Audigy2", .name = "E-mu 0404b [4002]",
+ .id = "EMU0404",
+ .emu10k2_chip = 1,
+ .ca0108_chip = 1,
+ .spk71 = 1,
+ .emu_model = EMU_MODEL_EMU0404}, /* EMU 0404 */
+ /* Tested by James@superbug.co.uk 20-3-2007. */
+ {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x40021102,
+ .driver = "Audigy2", .name = "E-mu 0404 [4002]",
+ .id = "EMU0404",
+ .emu10k2_chip = 1,
+ .ca0102_chip = 1,
+ .spk71 = 1,
+ .emu_model = EMU_MODEL_EMU0404}, /* EMU 0404 */
+ /* Audigy4 (Not PRO) SB0610 */
{.vendor = 0x1102, .device = 0x0008,
.driver = "Audigy2", .name = "Audigy 2 Value [Unknown]",
.id = "Audigy2",
.emu10k2_chip = 1,
.ca0108_chip = 1,
.ac97_chip = 1} ,
- /* Tested by James@superbug.co.uk 8th July 2005. No sound available yet. */
- {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x40011102,
- .driver = "Audigy2", .name = "E-mu 1010 [4001]",
- .id = "EMU1010",
- .emu10k2_chip = 1,
- .ca0102_chip = 1,
- .spk71 = 1,
- .emu1010 = 1} ,
/* Tested by James@superbug.co.uk 3rd July 2005 */
{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20071102,
.driver = "Audigy2", .name = "Audigy 4 PRO [SB0380]",
@@ -1654,6 +1728,8 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
emu->card = card;
spin_lock_init(&emu->reg_lock);
spin_lock_init(&emu->emu_lock);
+ spin_lock_init(&emu->spi_lock);
+ spin_lock_init(&emu->i2c_lock);
spin_lock_init(&emu->voice_lock);
spin_lock_init(&emu->synth_lock);
spin_lock_init(&emu->memblk_lock);
@@ -1794,7 +1870,7 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
if (emu->card_capabilities->ecard) {
if ((err = snd_emu10k1_ecard_init(emu)) < 0)
goto error;
- } else if (emu->card_capabilities->emu1010) {
+ } else if (emu->card_capabilities->emu_model) {
if ((err = snd_emu10k1_emu1010_init(emu)) < 0) {
snd_emu10k1_free(emu);
return err;
@@ -1943,7 +2019,7 @@ void snd_emu10k1_resume_init(struct snd_emu10k1 *emu)
snd_emu10k1_cardbus_init(emu);
if (emu->card_capabilities->ecard)
snd_emu10k1_ecard_init(emu);
- else if (emu->card_capabilities->emu1010)
+ else if (emu->card_capabilities->emu_model)
snd_emu10k1_emu1010_init(emu);
else
snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE);
diff --git a/sound/pci/emu10k1/emu10k1_synth.c b/sound/pci/emu10k1/emu10k1_synth.c
index 204995a1dfb..ad7b71491fc 100644
--- a/sound/pci/emu10k1/emu10k1_synth.c
+++ b/sound/pci/emu10k1/emu10k1_synth.c
@@ -30,7 +30,7 @@ MODULE_LICENSE("GPL");
*/
static int snd_emu10k1_synth_new_device(struct snd_seq_device *dev)
{
- struct snd_emux *emu;
+ struct snd_emux *emux;
struct snd_emu10k1 *hw;
struct snd_emu10k1_synth_arg *arg;
unsigned long flags;
@@ -46,53 +46,56 @@ static int snd_emu10k1_synth_new_device(struct snd_seq_device *dev)
else if (arg->max_voices > 64)
arg->max_voices = 64;
- if (snd_emux_new(&emu) < 0)
+ if (snd_emux_new(&emux) < 0)
return -ENOMEM;
- snd_emu10k1_ops_setup(emu);
- emu->hw = hw = arg->hwptr;
- emu->max_voices = arg->max_voices;
- emu->num_ports = arg->seq_ports;
- emu->pitch_shift = -501;
- emu->memhdr = hw->memhdr;
- emu->midi_ports = arg->seq_ports < 2 ? arg->seq_ports : 2; /* maximum two ports */
- emu->midi_devidx = hw->audigy ? 2 : 1; /* audigy has two external midis */
- emu->linear_panning = 0;
- emu->hwdep_idx = 2; /* FIXED */
-
- if (snd_emux_register(emu, dev->card, arg->index, "Emu10k1") < 0) {
- snd_emux_free(emu);
+ snd_emu10k1_ops_setup(emux);
+ hw = arg->hwptr;
+ emux->hw = hw;
+ emux->max_voices = arg->max_voices;
+ emux->num_ports = arg->seq_ports;
+ emux->pitch_shift = -501;
+ emux->memhdr = hw->memhdr;
+ /* maximum two ports */
+ emux->midi_ports = arg->seq_ports < 2 ? arg->seq_ports : 2;
+ /* audigy has two external midis */
+ emux->midi_devidx = hw->audigy ? 2 : 1;
+ emux->linear_panning = 0;
+ emux->hwdep_idx = 2; /* FIXED */
+
+ if (snd_emux_register(emux, dev->card, arg->index, "Emu10k1") < 0) {
+ snd_emux_free(emux);
return -ENOMEM;
}
spin_lock_irqsave(&hw->voice_lock, flags);
- hw->synth = emu;
+ hw->synth = emux;
hw->get_synth_voice = snd_emu10k1_synth_get_voice;
spin_unlock_irqrestore(&hw->voice_lock, flags);
- dev->driver_data = emu;
+ dev->driver_data = emux;
return 0;
}
static int snd_emu10k1_synth_delete_device(struct snd_seq_device *dev)
{
- struct snd_emux *emu;
+ struct snd_emux *emux;
struct snd_emu10k1 *hw;
unsigned long flags;
if (dev->driver_data == NULL)
return 0; /* not registered actually */
- emu = dev->driver_data;
+ emux = dev->driver_data;
- hw = emu->hw;
+ hw = emux->hw;
spin_lock_irqsave(&hw->voice_lock, flags);
hw->synth = NULL;
hw->get_synth_voice = NULL;
spin_unlock_irqrestore(&hw->voice_lock, flags);
- snd_emux_free(emu);
+ snd_emux_free(emux);
return 0;
}
diff --git a/sound/pci/emu10k1/emu10k1_synth_local.h b/sound/pci/emu10k1/emu10k1_synth_local.h
index 308ddc84bb4..25f328ff639 100644
--- a/sound/pci/emu10k1/emu10k1_synth_local.h
+++ b/sound/pci/emu10k1/emu10k1_synth_local.h
@@ -20,7 +20,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <sound/driver.h>
#include <linux/time.h>
#include <sound/core.h>
#include <sound/emu10k1_synth.h>
diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c
index 1ec7ebaff9e..5512abd98bd 100644
--- a/sound/pci/emu10k1/emu10k1x.c
+++ b/sound/pci/emu10k1/emu10k1x.c
@@ -29,7 +29,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
@@ -1583,6 +1582,8 @@ static int __devinit snd_emu10k1x_probe(struct pci_dev *pci,
sprintf(card->longname, "%s at 0x%lx irq %i",
card->shortname, chip->port, chip->irq);
+ snd_card_set_dev(card, &pci->dev);
+
if ((err = snd_card_register(card)) < 0) {
snd_card_free(card);
return err;
diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c
index 9bf1cd59219..71dc4c8865b 100644
--- a/sound/pci/emu10k1/emufx.c
+++ b/sound/pci/emu10k1/emufx.c
@@ -28,7 +28,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/pci.h>
#include <linux/capability.h>
#include <linux/delay.h>
@@ -666,7 +665,7 @@ static unsigned int *copy_tlv(const unsigned int __user *_tlv)
return NULL;
if (data[1] >= MAX_TLV_SIZE)
return NULL;
- tlv = kmalloc(data[1] * 4 + sizeof(data), GFP_KERNEL);
+ tlv = kmalloc(data[1] + sizeof(data), GFP_KERNEL);
if (!tlv)
return NULL;
memcpy(tlv, data, sizeof(data));
@@ -1262,7 +1261,7 @@ static int __devinit _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu)
A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
/* emu1212 DSP 0 and DSP 1 Capture */
- if (emu->card_capabilities->emu1010) {
+ if (emu->card_capabilities->emu_model) {
if (emu->card_capabilities->ca0108_chip) {
/* Note:JCD:No longer bit shift lower 16bits to upper 16bits of 32bit value. */
A_OP(icode, &ptr, iMACINT0, A_GPR(tmp), A_C_00000000, A3_EMU32IN(0x0), A_C_00000001);
@@ -1516,7 +1515,7 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
/* digital outputs */
/* A_PUT_STEREO_OUTPUT(A_EXTOUT_FRONT_L, A_EXTOUT_FRONT_R, playback + SND_EMU10K1_PLAYBACK_CHANNELS); */
- if (emu->card_capabilities->emu1010) {
+ if (emu->card_capabilities->emu_model) {
/* EMU1010 Outputs from PCM Front, Rear, Center, LFE, Side */
snd_printk("EMU outputs on\n");
for (z = 0; z < 8; z++) {
@@ -1564,7 +1563,7 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
A_PUT_OUTPUT(A_EXTOUT_ADC_CAP_R, capture+1);
#endif
- if (emu->card_capabilities->emu1010) {
+ if (emu->card_capabilities->emu_model) {
if (emu->card_capabilities->ca0108_chip) {
snd_printk("EMU2 inputs on\n");
for (z = 0; z < 0x10; z++) {
diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c
index ccacd7b890e..fd221209abc 100644
--- a/sound/pci/emu10k1/emumixer.c
+++ b/sound/pci/emu10k1/emumixer.c
@@ -30,7 +30,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/time.h>
#include <linux/init.h>
#include <sound/core.h>
@@ -140,6 +139,61 @@ static char *emu1010_src_texts[] = {
"DSP 31",
};
+/* 1616(m) cardbus */
+
+static char *emu1616_src_texts[] = {
+ "Silence",
+ "Dock Mic A",
+ "Dock Mic B",
+ "Dock ADC1 Left",
+ "Dock ADC1 Right",
+ "Dock ADC2 Left",
+ "Dock ADC2 Right",
+ "Dock SPDIF Left",
+ "Dock SPDIF Right",
+ "ADAT 0",
+ "ADAT 1",
+ "ADAT 2",
+ "ADAT 3",
+ "ADAT 4",
+ "ADAT 5",
+ "ADAT 6",
+ "ADAT 7",
+ "DSP 0",
+ "DSP 1",
+ "DSP 2",
+ "DSP 3",
+ "DSP 4",
+ "DSP 5",
+ "DSP 6",
+ "DSP 7",
+ "DSP 8",
+ "DSP 9",
+ "DSP 10",
+ "DSP 11",
+ "DSP 12",
+ "DSP 13",
+ "DSP 14",
+ "DSP 15",
+ "DSP 16",
+ "DSP 17",
+ "DSP 18",
+ "DSP 19",
+ "DSP 20",
+ "DSP 21",
+ "DSP 22",
+ "DSP 23",
+ "DSP 24",
+ "DSP 25",
+ "DSP 26",
+ "DSP 27",
+ "DSP 28",
+ "DSP 29",
+ "DSP 30",
+ "DSP 31",
+};
+
+
/*
* List of data sources available for each destination
*/
@@ -199,6 +253,59 @@ static unsigned int emu1010_src_regs[] = {
EMU_SRC_ALICE_EMU32B+0xf, /* 52 */
};
+/* 1616(m) cardbus */
+static unsigned int emu1616_src_regs[] = {
+ EMU_SRC_SILENCE,
+ EMU_SRC_DOCK_MIC_A1,
+ EMU_SRC_DOCK_MIC_B1,
+ EMU_SRC_DOCK_ADC1_LEFT1,
+ EMU_SRC_DOCK_ADC1_RIGHT1,
+ EMU_SRC_DOCK_ADC2_LEFT1,
+ EMU_SRC_DOCK_ADC2_RIGHT1,
+ EMU_SRC_MDOCK_SPDIF_LEFT1,
+ EMU_SRC_MDOCK_SPDIF_RIGHT1,
+ EMU_SRC_MDOCK_ADAT,
+ EMU_SRC_MDOCK_ADAT+1,
+ EMU_SRC_MDOCK_ADAT+2,
+ EMU_SRC_MDOCK_ADAT+3,
+ EMU_SRC_MDOCK_ADAT+4,
+ EMU_SRC_MDOCK_ADAT+5,
+ EMU_SRC_MDOCK_ADAT+6,
+ EMU_SRC_MDOCK_ADAT+7,
+ EMU_SRC_ALICE_EMU32A,
+ EMU_SRC_ALICE_EMU32A+1,
+ EMU_SRC_ALICE_EMU32A+2,
+ EMU_SRC_ALICE_EMU32A+3,
+ EMU_SRC_ALICE_EMU32A+4,
+ EMU_SRC_ALICE_EMU32A+5,
+ EMU_SRC_ALICE_EMU32A+6,
+ EMU_SRC_ALICE_EMU32A+7,
+ EMU_SRC_ALICE_EMU32A+8,
+ EMU_SRC_ALICE_EMU32A+9,
+ EMU_SRC_ALICE_EMU32A+0xa,
+ EMU_SRC_ALICE_EMU32A+0xb,
+ EMU_SRC_ALICE_EMU32A+0xc,
+ EMU_SRC_ALICE_EMU32A+0xd,
+ EMU_SRC_ALICE_EMU32A+0xe,
+ EMU_SRC_ALICE_EMU32A+0xf,
+ EMU_SRC_ALICE_EMU32B,
+ EMU_SRC_ALICE_EMU32B+1,
+ EMU_SRC_ALICE_EMU32B+2,
+ EMU_SRC_ALICE_EMU32B+3,
+ EMU_SRC_ALICE_EMU32B+4,
+ EMU_SRC_ALICE_EMU32B+5,
+ EMU_SRC_ALICE_EMU32B+6,
+ EMU_SRC_ALICE_EMU32B+7,
+ EMU_SRC_ALICE_EMU32B+8,
+ EMU_SRC_ALICE_EMU32B+9,
+ EMU_SRC_ALICE_EMU32B+0xa,
+ EMU_SRC_ALICE_EMU32B+0xb,
+ EMU_SRC_ALICE_EMU32B+0xc,
+ EMU_SRC_ALICE_EMU32B+0xd,
+ EMU_SRC_ALICE_EMU32B+0xe,
+ EMU_SRC_ALICE_EMU32B+0xf,
+};
+
/*
* Data destinations - physical EMU outputs.
* Each destination has an enum mixer control to choose a data source
@@ -230,6 +337,28 @@ static unsigned int emu1010_output_dst[] = {
EMU_DST_HANA_ADAT+7, /* 23 */
};
+/* 1616(m) cardbus */
+static unsigned int emu1616_output_dst[] = {
+ EMU_DST_DOCK_DAC1_LEFT1,
+ EMU_DST_DOCK_DAC1_RIGHT1,
+ EMU_DST_DOCK_DAC2_LEFT1,
+ EMU_DST_DOCK_DAC2_RIGHT1,
+ EMU_DST_DOCK_DAC3_LEFT1,
+ EMU_DST_DOCK_DAC3_RIGHT1,
+ EMU_DST_MDOCK_SPDIF_LEFT1,
+ EMU_DST_MDOCK_SPDIF_RIGHT1,
+ EMU_DST_MDOCK_ADAT,
+ EMU_DST_MDOCK_ADAT+1,
+ EMU_DST_MDOCK_ADAT+2,
+ EMU_DST_MDOCK_ADAT+3,
+ EMU_DST_MDOCK_ADAT+4,
+ EMU_DST_MDOCK_ADAT+5,
+ EMU_DST_MDOCK_ADAT+6,
+ EMU_DST_MDOCK_ADAT+7,
+ EMU_DST_MANA_DAC_LEFT,
+ EMU_DST_MANA_DAC_RIGHT,
+};
+
/*
* Data destinations - HANA outputs going to Alice2 (audigy) for
* capture (EMU32 + I2S links)
@@ -260,14 +389,26 @@ static unsigned int emu1010_input_dst[] = {
EMU_DST_ALICE_I2S2_RIGHT,
};
-static int snd_emu1010_input_output_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static int snd_emu1010_input_output_source_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
+ struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+ char **items;
+
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
- uinfo->value.enumerated.items = 53;
+ if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616) {
+ uinfo->value.enumerated.items = 49;
+ items = emu1616_src_texts;
+ } else {
+ uinfo->value.enumerated.items = 53;
+ items = emu1010_src_texts;
+ }
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name, emu1010_src_texts[uinfo->value.enumerated.item]);
+ uinfo->value.enumerated.item =
+ uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name,
+ items[uinfo->value.enumerated.item]);
return 0;
}
@@ -279,7 +420,9 @@ static int snd_emu1010_output_source_get(struct snd_kcontrol *kcontrol,
channel = (kcontrol->private_value) & 0xff;
/* Limit: emu1010_output_dst, emu->emu1010.output_source */
- if (channel >= 24)
+ if (channel >= 24 ||
+ (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616 &&
+ channel >= 18))
return -EINVAL;
ucontrol->value.enumerated.item[0] = emu->emu1010.output_source[channel];
return 0;
@@ -289,24 +432,30 @@ static int snd_emu1010_output_source_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
- int change = 0;
unsigned int val;
unsigned int channel;
val = ucontrol->value.enumerated.item[0];
- if (val >= 53)
+ if (val >= 53 ||
+ (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616 &&
+ val >= 49))
return -EINVAL;
channel = (kcontrol->private_value) & 0xff;
/* Limit: emu1010_output_dst, emu->emu1010.output_source */
- if (channel >= 24)
+ if (channel >= 24 ||
+ (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616 &&
+ channel >= 18))
return -EINVAL;
- if (emu->emu1010.output_source[channel] != val) {
- emu->emu1010.output_source[channel] = val;
- change = 1;
+ if (emu->emu1010.output_source[channel] == val)
+ return 0;
+ emu->emu1010.output_source[channel] = val;
+ if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616)
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ emu1616_output_dst[channel], emu1616_src_regs[val]);
+ else
snd_emu1010_fpga_link_dst_src_write(emu,
emu1010_output_dst[channel], emu1010_src_regs[val]);
- }
- return change;
+ return 1;
}
static int snd_emu1010_input_source_get(struct snd_kcontrol *kcontrol,
@@ -327,24 +476,28 @@ static int snd_emu1010_input_source_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
- int change = 0;
unsigned int val;
unsigned int channel;
val = ucontrol->value.enumerated.item[0];
- if (val >= 53)
+ if (val >= 53 ||
+ (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616 &&
+ val >= 49))
return -EINVAL;
channel = (kcontrol->private_value) & 0xff;
/* Limit: emu1010_input_dst, emu->emu1010.input_source */
if (channel >= 22)
return -EINVAL;
- if (emu->emu1010.input_source[channel] != val) {
- emu->emu1010.input_source[channel] = val;
- change = 1;
+ if (emu->emu1010.input_source[channel] == val)
+ return 0;
+ emu->emu1010.input_source[channel] = val;
+ if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616)
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ emu1010_input_dst[channel], emu1616_src_regs[val]);
+ else
snd_emu1010_fpga_link_dst_src_write(emu,
emu1010_input_dst[channel], emu1010_src_regs[val]);
- }
- return change;
+ return 1;
}
#define EMU1010_SOURCE_OUTPUT(xname,chid) \
@@ -384,6 +537,30 @@ static struct snd_kcontrol_new snd_emu1010_output_enum_ctls[] __devinitdata = {
EMU1010_SOURCE_OUTPUT("1010 ADAT 7 Playback Enum", 0x17),
};
+
+/* 1616(m) cardbus */
+static struct snd_kcontrol_new snd_emu1616_output_enum_ctls[] __devinitdata = {
+ EMU1010_SOURCE_OUTPUT("Dock DAC1 Left Playback Enum", 0),
+ EMU1010_SOURCE_OUTPUT("Dock DAC1 Right Playback Enum", 1),
+ EMU1010_SOURCE_OUTPUT("Dock DAC2 Left Playback Enum", 2),
+ EMU1010_SOURCE_OUTPUT("Dock DAC2 Right Playback Enum", 3),
+ EMU1010_SOURCE_OUTPUT("Dock DAC3 Left Playback Enum", 4),
+ EMU1010_SOURCE_OUTPUT("Dock DAC3 Right Playback Enum", 5),
+ EMU1010_SOURCE_OUTPUT("Dock SPDIF Left Playback Enum", 6),
+ EMU1010_SOURCE_OUTPUT("Dock SPDIF Right Playback Enum", 7),
+ EMU1010_SOURCE_OUTPUT("Dock ADAT 0 Playback Enum", 8),
+ EMU1010_SOURCE_OUTPUT("Dock ADAT 1 Playback Enum", 9),
+ EMU1010_SOURCE_OUTPUT("Dock ADAT 2 Playback Enum", 0xa),
+ EMU1010_SOURCE_OUTPUT("Dock ADAT 3 Playback Enum", 0xb),
+ EMU1010_SOURCE_OUTPUT("Dock ADAT 4 Playback Enum", 0xc),
+ EMU1010_SOURCE_OUTPUT("Dock ADAT 5 Playback Enum", 0xd),
+ EMU1010_SOURCE_OUTPUT("Dock ADAT 6 Playback Enum", 0xe),
+ EMU1010_SOURCE_OUTPUT("Dock ADAT 7 Playback Enum", 0xf),
+ EMU1010_SOURCE_OUTPUT("Mana DAC Left Playback Enum", 0x10),
+ EMU1010_SOURCE_OUTPUT("Mana DAC Right Playback Enum", 0x11),
+};
+
+
#define EMU1010_SOURCE_INPUT(xname,chid) \
{ \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
@@ -1793,7 +1970,7 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
return err;
}
- if ( emu->card_capabilities->emu1010) {
+ if (emu->card_capabilities->emu_model) {
; /* Disable the snd_audigy_spdif_shared_spdif */
} else if (emu->audigy) {
if ((kctl = snd_ctl_new1(&snd_audigy_shared_spdif, emu)) == NULL)
@@ -1818,30 +1995,73 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
return err;
}
- if ( emu->card_capabilities->emu1010) {
+ if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616) {
+ /* 1616(m) cardbus */
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(snd_emu1616_output_enum_ctls); i++) {
+ err = snd_ctl_add(card,
+ snd_ctl_new1(&snd_emu1616_output_enum_ctls[i],
+ emu));
+ if (err < 0)
+ return err;
+ }
+ for (i = 0; i < ARRAY_SIZE(snd_emu1010_input_enum_ctls); i++) {
+ err = snd_ctl_add(card,
+ snd_ctl_new1(&snd_emu1010_input_enum_ctls[i],
+ emu));
+ if (err < 0)
+ return err;
+ }
+ for (i = 0; i < ARRAY_SIZE(snd_emu1010_adc_pads) - 2; i++) {
+ err = snd_ctl_add(card,
+ snd_ctl_new1(&snd_emu1010_adc_pads[i], emu));
+ if (err < 0)
+ return err;
+ }
+ for (i = 0; i < ARRAY_SIZE(snd_emu1010_dac_pads) - 2; i++) {
+ err = snd_ctl_add(card,
+ snd_ctl_new1(&snd_emu1010_dac_pads[i], emu));
+ if (err < 0)
+ return err;
+ }
+ err = snd_ctl_add(card,
+ snd_ctl_new1(&snd_emu1010_internal_clock, emu));
+ if (err < 0)
+ return err;
+
+ } else if (emu->card_capabilities->emu_model) {
+ /* all other e-mu cards for now */
int i;
for (i = 0; i < ARRAY_SIZE(snd_emu1010_output_enum_ctls); i++) {
- err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_output_enum_ctls[i], emu));
+ err = snd_ctl_add(card,
+ snd_ctl_new1(&snd_emu1010_output_enum_ctls[i],
+ emu));
if (err < 0)
return err;
}
for (i = 0; i < ARRAY_SIZE(snd_emu1010_input_enum_ctls); i++) {
- err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_input_enum_ctls[i], emu));
+ err = snd_ctl_add(card,
+ snd_ctl_new1(&snd_emu1010_input_enum_ctls[i],
+ emu));
if (err < 0)
return err;
}
for (i = 0; i < ARRAY_SIZE(snd_emu1010_adc_pads); i++) {
- err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_adc_pads[i], emu));
+ err = snd_ctl_add(card,
+ snd_ctl_new1(&snd_emu1010_adc_pads[i], emu));
if (err < 0)
return err;
}
for (i = 0; i < ARRAY_SIZE(snd_emu1010_dac_pads); i++) {
- err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_dac_pads[i], emu));
+ err = snd_ctl_add(card,
+ snd_ctl_new1(&snd_emu1010_dac_pads[i], emu));
if (err < 0)
return err;
}
- err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_internal_clock, emu));
+ err = snd_ctl_add(card,
+ snd_ctl_new1(&snd_emu1010_internal_clock, emu));
if (err < 0)
return err;
}
diff --git a/sound/pci/emu10k1/emumpu401.c b/sound/pci/emu10k1/emumpu401.c
index 04c7cf70353..c4d76d16661 100644
--- a/sound/pci/emu10k1/emumpu401.c
+++ b/sound/pci/emu10k1/emumpu401.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/time.h>
#include <linux/init.h>
#include <sound/core.h>
diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c
index 5ce5befc701..cf9276ddad4 100644
--- a/sound/pci/emu10k1/emupcm.c
+++ b/sound/pci/emu10k1/emupcm.c
@@ -26,7 +26,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/slab.h>
@@ -358,7 +357,7 @@ static void snd_emu10k1_pcm_init_voice(struct snd_emu10k1 *emu,
snd_emu10k1_ptr_write(emu, PTRX, voice, (send_amount[0] << 8) | send_amount[1]);
snd_emu10k1_ptr_write(emu, DSL, voice, end_addr | (send_amount[3] << 24));
snd_emu10k1_ptr_write(emu, PSST, voice, start_addr | (send_amount[2] << 24));
- if (emu->card_capabilities->emu1010)
+ if (emu->card_capabilities->emu_model)
pitch_target = PITCH_48000; /* Disable interpolators on emu1010 card */
else
pitch_target = emu10k1_calc_pitch_target(runtime->rate);
@@ -701,7 +700,7 @@ static void snd_emu10k1_playback_trigger_voice(struct snd_emu10k1 *emu, struct s
voice = evoice->number;
pitch = snd_emu10k1_rate_to_pitch(runtime->rate) >> 8;
- if (emu->card_capabilities->emu1010)
+ if (emu->card_capabilities->emu_model)
pitch_target = PITCH_48000; /* Disable interpolators on emu1010 card */
else
pitch_target = emu10k1_calc_pitch_target(runtime->rate);
@@ -1232,7 +1231,7 @@ static int snd_emu10k1_capture_efx_open(struct snd_pcm_substream *substream)
runtime->hw.rates = SNDRV_PCM_RATE_48000;
runtime->hw.rate_min = runtime->hw.rate_max = 48000;
spin_lock_irq(&emu->reg_lock);
- if (emu->card_capabilities->emu1010) {
+ if (emu->card_capabilities->emu_model) {
/* Nb. of channels has been increased to 16 */
/* TODO
* SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE
@@ -1791,7 +1790,7 @@ int __devinit snd_emu10k1_pcm_efx(struct snd_emu10k1 * emu, int device, struct s
/* emu->efx_voices_mask[0] = FXWC_DEFAULTROUTE_C | FXWC_DEFAULTROUTE_A; */
if (emu->audigy) {
emu->efx_voices_mask[0] = 0;
- if (emu->card_capabilities->emu1010)
+ if (emu->card_capabilities->emu_model)
/* Pavel Hofman - 32 voices will be used for
* capture (write mode) -
* each bit = corresponding voice
diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c
index c3fb10e81c9..f3caa3f890c 100644
--- a/sound/pci/emu10k1/emuproc.c
+++ b/sound/pci/emu10k1/emuproc.c
@@ -28,7 +28,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <sound/core.h>
@@ -245,7 +244,7 @@ static void snd_emu10k1_proc_spdif_read(struct snd_info_entry *entry,
unsigned long flags;
u32 rate;
- if (emu->card_capabilities->emu1010) {
+ if (emu->card_capabilities->emu_model) {
spin_lock_irqsave(&emu->emu_lock, flags);
snd_emu1010_fpga_read(emu, 0x38, &value);
spin_unlock_irqrestore(&emu->emu_lock, flags);
@@ -585,7 +584,7 @@ int __devinit snd_emu10k1_proc_init(struct snd_emu10k1 * emu)
{
struct snd_info_entry *entry;
#ifdef CONFIG_SND_DEBUG
- if (emu->card_capabilities->emu1010) {
+ if (emu->card_capabilities->emu_model) {
if (! snd_card_proc_new(emu->card, "emu1010_regs", &entry))
snd_info_set_text_ops(entry, emu, snd_emu_proc_emu1010_reg_read);
}
diff --git a/sound/pci/emu10k1/io.c b/sound/pci/emu10k1/io.c
index 6702c15fefa..b5a802bdeb7 100644
--- a/sound/pci/emu10k1/io.c
+++ b/sound/pci/emu10k1/io.c
@@ -25,7 +25,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/time.h>
#include <sound/core.h>
#include <sound/emu10k1.h>
@@ -71,6 +70,11 @@ void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned i
unsigned long flags;
unsigned int mask;
+ if (!emu) {
+ snd_printk(KERN_ERR "ptr_write: emu is null!\n");
+ dump_stack();
+ return;
+ }
mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
@@ -135,15 +139,23 @@ int snd_emu10k1_spi_write(struct snd_emu10k1 * emu,
unsigned int reset, set;
unsigned int reg, tmp;
int n, result;
+ int err = 0;
+
+ /* This function is not re-entrant, so protect against it. */
+ spin_lock(&emu->spi_lock);
if (emu->card_capabilities->ca0108_chip)
reg = 0x3c; /* PTR20, reg 0x3c */
else {
/* For other chip types the SPI register
* is currently unknown. */
- return 1;
+ err = 1;
+ goto spi_write_exit;
+ }
+ if (data > 0xffff) {
+ /* Only 16bit values allowed */
+ err = 1;
+ goto spi_write_exit;
}
- if (data > 0xffff) /* Only 16bit values allowed */
- return 1;
tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
reset = (tmp & ~0x3ffff) | 0x20000; /* Set xxx20000 */
@@ -161,11 +173,17 @@ int snd_emu10k1_spi_write(struct snd_emu10k1 * emu,
break;
}
}
- if (result) /* Timed out */
- return 1;
+ if (result) {
+ /* Timed out */
+ err = 1;
+ goto spi_write_exit;
+ }
snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* Write post */
- return 0;
+ err = 0;
+spi_write_exit:
+ spin_unlock(&emu->spi_lock);
+ return err;
}
/* The ADC does not support i2c read, so only write is implemented */
@@ -177,15 +195,17 @@ int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
int timeout = 0;
int status;
int retry;
+ int err = 0;
+
if ((reg > 0x7f) || (value > 0x1ff)) {
snd_printk(KERN_ERR "i2c_write: invalid values.\n");
return -EINVAL;
}
+ /* This function is not re-entrant, so protect against it. */
+ spin_lock(&emu->i2c_lock);
+
tmp = reg << 25 | value << 16;
- // snd_printk("I2C-write:reg=0x%x, value=0x%x\n", reg, value);
- /* Not sure what this I2C channel controls. */
- /* snd_emu10k1_ptr_write(emu, P17V_I2C_0, 0, tmp); */
/* This controls the I2C connected to the WM8775 ADC Codec */
snd_emu10k1_ptr20_write(emu, P17V_I2C_1, 0, tmp);
@@ -193,17 +213,14 @@ int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
for (retry = 0; retry < 10; retry++) {
/* Send the data to i2c */
- //tmp = snd_emu10k1_ptr_read(emu, P17V_I2C_ADDR, 0);
- //tmp = tmp & ~(I2C_A_ADC_READ|I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD_MASK);
tmp = 0;
tmp = tmp | (I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD);
snd_emu10k1_ptr20_write(emu, P17V_I2C_ADDR, 0, tmp);
/* Wait till the transaction ends */
while (1) {
- udelay(10);
+ mdelay(1);
status = snd_emu10k1_ptr20_read(emu, P17V_I2C_ADDR, 0);
- // snd_printk("I2C:status=0x%x\n", status);
timeout++;
if ((status & I2C_A_ADC_START) == 0)
break;
@@ -220,19 +237,26 @@ int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
if (retry == 10) {
snd_printk(KERN_ERR "Writing to ADC failed!\n");
- return -EINVAL;
+ snd_printk(KERN_ERR "status=0x%x, reg=%d, value=%d\n",
+ status, reg, value);
+ /* dump_stack(); */
+ err = -EINVAL;
}
- return 0;
+ spin_unlock(&emu->i2c_lock);
+ return err;
}
int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, u32 reg, u32 value)
{
+ unsigned long flags;
+
if (reg > 0x3f)
return 1;
reg += 0x40; /* 0x40 upwards are registers. */
if (value < 0 || value > 0x3f) /* 0 to 0x3f are values */
return 1;
+ spin_lock_irqsave(&emu->emu_lock, flags);
outl(reg, emu->port + A_IOCFG);
udelay(10);
outl(reg | 0x80, emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */
@@ -240,20 +264,24 @@ int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, u32 reg, u32 value)
outl(value, emu->port + A_IOCFG);
udelay(10);
outl(value | 0x80 , emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
return 0;
}
int snd_emu1010_fpga_read(struct snd_emu10k1 * emu, u32 reg, u32 *value)
{
+ unsigned long flags;
if (reg > 0x3f)
return 1;
reg += 0x40; /* 0x40 upwards are registers. */
+ spin_lock_irqsave(&emu->emu_lock, flags);
outl(reg, emu->port + A_IOCFG);
udelay(10);
outl(reg | 0x80, emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */
udelay(10);
*value = ((inl(emu->port + A_IOCFG) >> 8) & 0x7f);
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
return 0;
}
diff --git a/sound/pci/emu10k1/irq.c b/sound/pci/emu10k1/irq.c
index 3c114b45e0b..30bfed6f833 100644
--- a/sound/pci/emu10k1/irq.c
+++ b/sound/pci/emu10k1/irq.c
@@ -25,7 +25,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/time.h>
#include <sound/core.h>
#include <sound/emu10k1.h>
@@ -35,9 +34,10 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id)
struct snd_emu10k1 *emu = dev_id;
unsigned int status, status2, orig_status, orig_status2;
int handled = 0;
+ int timeout = 0;
- while ((status = inl(emu->port + IPR)) != 0) {
- //snd_printk(KERN_INFO "emu10k1 irq - status = 0x%x\n", status);
+ while (((status = inl(emu->port + IPR)) != 0) && (timeout < 1000)) {
+ timeout++;
orig_status = status;
handled = 1;
if ((status & 0xffffffff) == 0xffffffff) {
@@ -201,5 +201,8 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id)
}
outl(orig_status, emu->port + IPR); /* ack all */
}
+ if (timeout == 1000)
+ snd_printk(KERN_INFO "emu10k1 irq routine failure\n");
+
return IRQ_RETVAL(handled);
}
diff --git a/sound/pci/emu10k1/memory.c b/sound/pci/emu10k1/memory.c
index 48097c6bb15..916c1dbcd53 100644
--- a/sound/pci/emu10k1/memory.c
+++ b/sound/pci/emu10k1/memory.c
@@ -21,7 +21,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/pci.h>
#include <linux/time.h>
#include <linux/mutex.h>
diff --git a/sound/pci/emu10k1/p16v.c b/sound/pci/emu10k1/p16v.c
index 9fd3135f311..749a21b6bd0 100644
--- a/sound/pci/emu10k1/p16v.c
+++ b/sound/pci/emu10k1/p16v.c
@@ -87,7 +87,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/emu10k1/timer.c b/sound/pci/emu10k1/timer.c
index 6295b2dca78..72321e946cc 100644
--- a/sound/pci/emu10k1/timer.c
+++ b/sound/pci/emu10k1/timer.c
@@ -25,7 +25,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/time.h>
#include <sound/core.h>
#include <sound/emu10k1.h>
diff --git a/sound/pci/emu10k1/voice.c b/sound/pci/emu10k1/voice.c
index 04fa8492abb..958cb2a65a4 100644
--- a/sound/pci/emu10k1/voice.c
+++ b/sound/pci/emu10k1/voice.c
@@ -28,7 +28,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/time.h>
#include <sound/core.h>
#include <sound/emu10k1.h>
diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c
index b958f869cb1..72d85a5ae6a 100644
--- a/sound/pci/ens1370.c
+++ b/sound/pci/ens1370.c
@@ -26,7 +26,6 @@
* by Kurt J. Bosch
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c
index fb25abe68a0..1a314fa99c4 100644
--- a/sound/pci/es1938.c
+++ b/sound/pci/es1938.c
@@ -47,7 +47,6 @@
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
@@ -227,6 +226,7 @@ struct es1938 {
unsigned int dma2_start;
unsigned int dma1_shift;
unsigned int dma2_shift;
+ unsigned int last_capture_dmaaddr;
unsigned int active;
spinlock_t reg_lock;
@@ -529,6 +529,7 @@ static void snd_es1938_capture_setdma(struct es1938 *chip)
outb(1, SLDM_REG(chip, DMAMASK));
outb(0x14, SLDM_REG(chip, DMAMODE));
outl(chip->dma1_start, SLDM_REG(chip, DMAADDR));
+ chip->last_capture_dmaaddr = chip->dma1_start;
outw(chip->dma1_size - 1, SLDM_REG(chip, DMACOUNT));
/* 3. Unmask DMA */
outb(0, SLDM_REG(chip, DMAMASK));
@@ -770,19 +771,40 @@ static int snd_es1938_playback_prepare(struct snd_pcm_substream *substream)
return -EINVAL;
}
+/* during the incrementing of dma counters the DMA register reads sometimes
+ returns garbage. To ensure a valid hw pointer, the following checks which
+ should be very unlikely to fail are used:
+ - is the current DMA address in the valid DMA range ?
+ - is the sum of DMA address and DMA counter pointing to the last DMA byte ?
+ One can argue this could differ by one byte depending on which register is
+ updated first, so the implementation below allows for that.
+*/
static snd_pcm_uframes_t snd_es1938_capture_pointer(struct snd_pcm_substream *substream)
{
struct es1938 *chip = snd_pcm_substream_chip(substream);
size_t ptr;
+#if 0
size_t old, new;
-#if 1
/* This stuff is *needed*, don't ask why - AB */
old = inw(SLDM_REG(chip, DMACOUNT));
while ((new = inw(SLDM_REG(chip, DMACOUNT))) != old)
old = new;
ptr = chip->dma1_size - 1 - new;
#else
- ptr = inl(SLDM_REG(chip, DMAADDR)) - chip->dma1_start;
+ size_t count;
+ unsigned int diff;
+
+ ptr = inl(SLDM_REG(chip, DMAADDR));
+ count = inw(SLDM_REG(chip, DMACOUNT));
+ diff = chip->dma1_start + chip->dma1_size - ptr - count;
+
+ if (diff > 3 || ptr < chip->dma1_start
+ || ptr >= chip->dma1_start+chip->dma1_size)
+ ptr = chip->last_capture_dmaaddr; /* bad, use last saved */
+ else
+ chip->last_capture_dmaaddr = ptr; /* good, remember it */
+
+ ptr -= chip->dma1_start;
#endif
return ptr >> chip->dma1_shift;
}
diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c
index d69b11d1f99..25ccfce4575 100644
--- a/sound/pci/es1968.c
+++ b/sound/pci/es1968.c
@@ -94,7 +94,6 @@
* places.
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c
index 9939109f05a..4c300e6149f 100644
--- a/sound/pci/fm801.c
+++ b/sound/pci/fm801.c
@@ -20,7 +20,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
@@ -979,6 +978,27 @@ static unsigned int snd_fm801_tea575x_64pcr_read(struct snd_tea575x *tea)
return val;
}
+static void snd_fm801_tea575x_64pcr_mute(struct snd_tea575x *tea,
+ unsigned int mute)
+{
+ struct fm801 *chip = tea->private_data;
+ unsigned short reg;
+
+ spin_lock_irq(&chip->reg_lock);
+
+ reg = inw(FM801_REG(chip, GPIO_CTRL));
+ if (mute)
+ /* 0xf800 (mute) */
+ reg &= ~FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE);
+ else
+ /* 0xf802 (unmute) */
+ reg |= FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE);
+ outw(reg, FM801_REG(chip, GPIO_CTRL));
+ udelay(1);
+
+ spin_unlock_irq(&chip->reg_lock);
+}
+
static struct snd_tea575x_ops snd_fm801_tea_ops[3] = {
{
/* 1 = MediaForte 256-PCS */
@@ -994,6 +1014,7 @@ static struct snd_tea575x_ops snd_fm801_tea_ops[3] = {
/* 3 = MediaForte 64-PCR */
.write = snd_fm801_tea575x_64pcr_write,
.read = snd_fm801_tea575x_64pcr_read,
+ .mute = snd_fm801_tea575x_64pcr_mute,
}
};
#endif
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index ab0c726d648..9e0d8a1268a 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -2,7 +2,7 @@ snd-hda-intel-y := hda_intel.o
# since snd-hda-intel is the only driver using hda-codec,
# merge it into a single module although it was originally
# designed to be individual modules
-snd-hda-intel-y += hda_codec.o
+snd-hda-intel-y += hda_codec.o vmaster.o
snd-hda-intel-$(CONFIG_PROC_FS) += hda_proc.o
snd-hda-intel-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o
snd-hda-intel-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 8cbe3bf1e31..26812dc2b7f 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -19,7 +19,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
@@ -55,6 +54,7 @@ static struct hda_vendor_id hda_vendor_ids[] = {
{ 0x10ec, "Realtek" },
{ 0x1057, "Motorola" },
{ 0x1106, "VIA" },
+ { 0x111d, "IDT" },
{ 0x11d4, "Analog Devices" },
{ 0x13f6, "C-Media" },
{ 0x14f1, "Conexant" },
@@ -429,6 +429,10 @@ find_codec_preset(struct hda_codec *codec)
for (tbl = hda_preset_tables; *tbl; tbl++) {
for (preset = *tbl; preset->id; preset++) {
u32 mask = preset->mask;
+ if (preset->afg && preset->afg != codec->afg)
+ continue;
+ if (preset->mfg && preset->mfg != codec->mfg)
+ continue;
if (!mask)
mask = ~0;
if (preset->id == (codec->vendor_id & mask) &&
@@ -765,7 +769,7 @@ get_alloc_amp_hash(struct hda_codec *codec, u32 key)
/*
* query AMP capabilities for the given widget and direction
*/
-static u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
+u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
{
struct hda_amp_info *info;
@@ -933,7 +937,8 @@ int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
if (!caps) {
printk(KERN_WARNING "hda_codec: "
- "num_steps = 0 for NID=0x%x\n", nid);
+ "num_steps = 0 for NID=0x%x (ctl = %s)\n", nid,
+ kcontrol->id.name);
return -EINVAL;
}
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
@@ -1012,6 +1017,66 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
return 0;
}
+/*
+ * set (static) TLV for virtual master volume; recalculated as max 0dB
+ */
+void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
+ unsigned int *tlv)
+{
+ u32 caps;
+ int nums, step;
+
+ caps = query_amp_caps(codec, nid, dir);
+ nums = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
+ step = (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT;
+ step = (step + 1) * 25;
+ tlv[0] = SNDRV_CTL_TLVT_DB_SCALE;
+ tlv[1] = 2 * sizeof(unsigned int);
+ tlv[2] = -nums * step;
+ tlv[3] = step;
+}
+
+/* find a mixer control element with the given name */
+struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
+ const char *name)
+{
+ struct snd_ctl_elem_id id;
+ memset(&id, 0, sizeof(id));
+ id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ strcpy(id.name, name);
+ return snd_ctl_find_id(codec->bus->card, &id);
+}
+
+/* create a virtual master control and add slaves */
+int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
+ unsigned int *tlv, const char **slaves)
+{
+ struct snd_kcontrol *kctl;
+ const char **s;
+ int err;
+
+ kctl = snd_ctl_make_virtual_master(name, tlv);
+ if (!kctl)
+ return -ENOMEM;
+ err = snd_ctl_add(codec->bus->card, kctl);
+ if (err < 0)
+ return err;
+
+ for (s = slaves; *s; s++) {
+ struct snd_kcontrol *sctl;
+
+ sctl = snd_hda_find_mixer_ctl(codec, *s);
+ if (!sctl) {
+ snd_printdd("Cannot find slave %s, skipped\n", *s);
+ continue;
+ }
+ err = snd_ctl_add_slave(kctl, sctl);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
/* switch */
int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
@@ -1434,7 +1499,8 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
return err;
}
codec->spdif_ctls =
- snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0);
+ snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_DIGI_CONVERT_1, 0);
codec->spdif_status = convert_to_spdif_status(codec->spdif_ctls);
return 0;
}
@@ -1481,7 +1547,7 @@ static int snd_hda_spdif_in_status_get(struct snd_kcontrol *kcontrol,
unsigned short val;
unsigned int sbits;
- val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0);
+ val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT_1, 0);
sbits = convert_to_spdif_status(val);
ucontrol->value.iec958.status[0] = sbits;
ucontrol->value.iec958.status[1] = sbits >> 8;
@@ -1532,7 +1598,8 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
return err;
}
codec->spdif_in_enable =
- snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0) &
+ snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_DIGI_CONVERT_1, 0) &
AC_DIG1_ENABLE;
return 0;
}
@@ -1622,6 +1689,7 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
snd_hda_codec_write(codec, fg, 0, AC_VERB_SET_POWER_STATE,
power_state);
+ msleep(10); /* partial workaround for "azx_get_response timeout" */
nid = codec->start_nid;
for (i = 0; i < codec->num_nodes; i++, nid++) {
@@ -2336,7 +2404,8 @@ int snd_hda_ch_mode_put(struct hda_codec *codec,
unsigned int mode;
mode = ucontrol->value.enumerated.item[0];
- snd_assert(mode < num_chmodes, return -EINVAL);
+ if (mode >= num_chmodes)
+ return -EINVAL;
if (*max_channelsp == chmode[mode].channels)
return 0;
/* change the current channel setting */
@@ -2602,20 +2671,21 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
struct auto_pin_cfg *cfg,
hda_nid_t *ignore_nids)
{
- hda_nid_t nid, nid_start;
- int nodes;
+ hda_nid_t nid, end_nid;
short seq, assoc_line_out, assoc_speaker;
short sequences_line_out[ARRAY_SIZE(cfg->line_out_pins)];
short sequences_speaker[ARRAY_SIZE(cfg->speaker_pins)];
+ short sequences_hp[ARRAY_SIZE(cfg->hp_pins)];
memset(cfg, 0, sizeof(*cfg));
memset(sequences_line_out, 0, sizeof(sequences_line_out));
memset(sequences_speaker, 0, sizeof(sequences_speaker));
+ memset(sequences_hp, 0, sizeof(sequences_hp));
assoc_line_out = assoc_speaker = 0;
- nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid_start);
- for (nid = nid_start; nid < nodes + nid_start; nid++) {
+ end_nid = codec->start_nid + codec->num_nodes;
+ for (nid = codec->start_nid; nid < end_nid; nid++) {
unsigned int wid_caps = get_wcaps(codec, nid);
unsigned int wid_type =
(wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
@@ -2638,6 +2708,10 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
case AC_JACK_LINE_OUT:
seq = get_defcfg_sequence(def_conf);
assoc = get_defcfg_association(def_conf);
+
+ if (!(wid_caps & AC_WCAP_STEREO))
+ if (!cfg->mono_out_pin)
+ cfg->mono_out_pin = nid;
if (!assoc)
continue;
if (!assoc_line_out)
@@ -2666,9 +2740,12 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
cfg->speaker_outs++;
break;
case AC_JACK_HP_OUT:
+ seq = get_defcfg_sequence(def_conf);
+ assoc = get_defcfg_association(def_conf);
if (cfg->hp_outs >= ARRAY_SIZE(cfg->hp_pins))
continue;
cfg->hp_pins[cfg->hp_outs] = nid;
+ sequences_hp[cfg->hp_outs] = (assoc << 4) | seq;
cfg->hp_outs++;
break;
case AC_JACK_MIC_IN: {
@@ -2712,7 +2789,24 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
cfg->line_outs);
sort_pins_by_sequence(cfg->speaker_pins, sequences_speaker,
cfg->speaker_outs);
+ sort_pins_by_sequence(cfg->hp_pins, sequences_hp,
+ cfg->hp_outs);
+ /* if we have only one mic, make it AUTO_PIN_MIC */
+ if (!cfg->input_pins[AUTO_PIN_MIC] &&
+ cfg->input_pins[AUTO_PIN_FRONT_MIC]) {
+ cfg->input_pins[AUTO_PIN_MIC] =
+ cfg->input_pins[AUTO_PIN_FRONT_MIC];
+ cfg->input_pins[AUTO_PIN_FRONT_MIC] = 0;
+ }
+ /* ditto for line-in */
+ if (!cfg->input_pins[AUTO_PIN_LINE] &&
+ cfg->input_pins[AUTO_PIN_FRONT_LINE]) {
+ cfg->input_pins[AUTO_PIN_LINE] =
+ cfg->input_pins[AUTO_PIN_FRONT_LINE];
+ cfg->input_pins[AUTO_PIN_FRONT_LINE] = 0;
+ }
+
/*
* FIX-UP: if no line-outs are detected, try to use speaker or HP pin
* as a primary output
@@ -2766,6 +2860,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
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(" mono: mono_out=0x%x\n", cfg->mono_out_pin);
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],
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 2bce925d84e..f14871151be 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -77,12 +77,16 @@ enum {
#define AC_VERB_GET_PIN_SENSE 0x0f09
#define AC_VERB_GET_BEEP_CONTROL 0x0f0a
#define AC_VERB_GET_EAPD_BTLENABLE 0x0f0c
-#define AC_VERB_GET_DIGI_CONVERT 0x0f0d
+#define AC_VERB_GET_DIGI_CONVERT_1 0x0f0d
+#define AC_VERB_GET_DIGI_CONVERT_2 0x0f0e
#define AC_VERB_GET_VOLUME_KNOB_CONTROL 0x0f0f
/* f10-f1a: GPIO */
#define AC_VERB_GET_GPIO_DATA 0x0f15
#define AC_VERB_GET_GPIO_MASK 0x0f16
#define AC_VERB_GET_GPIO_DIRECTION 0x0f17
+#define AC_VERB_GET_GPIO_WAKE_MASK 0x0f18
+#define AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK 0x0f19
+#define AC_VERB_GET_GPIO_STICKY_MASK 0x0f1a
#define AC_VERB_GET_CONFIG_DEFAULT 0x0f1c
/* f20: AFG/MFG */
#define AC_VERB_GET_SUBSYSTEM_ID 0x0f20
@@ -110,6 +114,9 @@ enum {
#define AC_VERB_SET_GPIO_DATA 0x715
#define AC_VERB_SET_GPIO_MASK 0x716
#define AC_VERB_SET_GPIO_DIRECTION 0x717
+#define AC_VERB_SET_GPIO_WAKE_MASK 0x718
+#define AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK 0x719
+#define AC_VERB_SET_GPIO_STICKY_MASK 0x71a
#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 0x71c
#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_1 0x71d
#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_2 0x71e
@@ -135,6 +142,7 @@ enum {
#define AC_PAR_PROC_CAP 0x10
#define AC_PAR_GPIO_CAP 0x11
#define AC_PAR_AMP_OUT_CAP 0x12
+#define AC_PAR_VOL_KNB_CAP 0x13
/*
* AC_VERB_PARAMETERS results (32bit)
@@ -181,6 +189,27 @@ enum {
#define AC_SUPFMT_FLOAT32 (1<<1)
#define AC_SUPFMT_AC3 (1<<2)
+/* GP I/O count */
+#define AC_GPIO_IO_COUNT (0xff<<0)
+#define AC_GPIO_O_COUNT (0xff<<8)
+#define AC_GPIO_O_COUNT_SHIFT 8
+#define AC_GPIO_I_COUNT (0xff<<16)
+#define AC_GPIO_I_COUNT_SHIFT 16
+#define AC_GPIO_UNSOLICITED (1<<30)
+#define AC_GPIO_WAKE (1<<31)
+
+/* Converter stream, channel */
+#define AC_CONV_CHANNEL (0xf<<0)
+#define AC_CONV_STREAM (0xf<<4)
+#define AC_CONV_STREAM_SHIFT 4
+
+/* Input converter SDI select */
+#define AC_SDI_SELECT (0xf<<0)
+
+/* Unsolicited response */
+#define AC_UNSOL_TAG (0x3f<<0)
+#define AC_UNSOL_ENABLED (1<<7)
+
/* Pin widget capabilies */
#define AC_PINCAP_IMP_SENSE (1<<0) /* impedance sense capable */
#define AC_PINCAP_TRIG_REQ (1<<1) /* trigger required */
@@ -189,6 +218,10 @@ enum {
#define AC_PINCAP_OUT (1<<4) /* output capable */
#define AC_PINCAP_IN (1<<5) /* input capable */
#define AC_PINCAP_BALANCE (1<<6) /* balanced I/O capable */
+/* Note: This LR_SWAP pincap is defined in the Realtek ALC883 specification,
+ * but is marked reserved in the Intel HDA specification.
+ */
+#define AC_PINCAP_LR_SWAP (1<<7) /* L/R swap */
#define AC_PINCAP_VREF (0x37<<8)
#define AC_PINCAP_VREF_SHIFT 8
#define AC_PINCAP_EAPD (1<<16) /* EAPD capable */
@@ -222,6 +255,9 @@ enum {
#define AC_PWRST_D3SUP (1<<3)
/* Power state values */
+#define AC_PWRST_SETTING (0xf<<0)
+#define AC_PWRST_ACTUAL (0xf<<4)
+#define AC_PWRST_ACTUAL_SHIFT 4
#define AC_PWRST_D0 0x00
#define AC_PWRST_D1 0x01
#define AC_PWRST_D2 0x02
@@ -230,10 +266,11 @@ enum {
/* Processing capabilies */
#define AC_PCAP_BENIGN (1<<0)
#define AC_PCAP_NUM_COEF (0xff<<8)
+#define AC_PCAP_NUM_COEF_SHIFT 8
/* Volume knobs capabilities */
#define AC_KNBCAP_NUM_STEPS (0x7f<<0)
-#define AC_KNBCAP_DELTA (1<<8)
+#define AC_KNBCAP_DELTA (1<<7)
/*
* Control Parameters
@@ -266,6 +303,9 @@ enum {
#define AC_DIG1_PROFESSIONAL (1<<6)
#define AC_DIG1_LEVEL (1<<7)
+/* DIGITAL2 bits */
+#define AC_DIG2_CC (0x7f<<0)
+
/* Pin widget control - 8bit */
#define AC_PINCTL_VREFEN (0x7<<0)
#define AC_PINCTL_VREF_HIZ 0 /* Hi-Z */
@@ -280,12 +320,22 @@ enum {
/* Unsolicited response - 8bit */
#define AC_USRSP_EN (1<<7)
+/* Pin sense - 32bit */
+#define AC_PINSENSE_IMPEDANCE_MASK (0x7fffffff)
+#define AC_PINSENSE_PRESENCE (1<<31)
+
+/* EAPD/BTL enable - 32bit */
+#define AC_EAPDBTL_BALANCED (1<<0)
+#define AC_EAPDBTL_EAPD (1<<1)
+#define AC_EAPDBTL_LR_SWAP (1<<2)
+
/* configuration default - 32bit */
#define AC_DEFCFG_SEQUENCE (0xf<<0)
#define AC_DEFCFG_DEF_ASSOC (0xf<<4)
#define AC_DEFCFG_ASSOC_SHIFT 4
#define AC_DEFCFG_MISC (0xf<<8)
#define AC_DEFCFG_MISC_SHIFT 8
+#define AC_DEFCFG_MISC_NO_PRESENCE (1<<0)
#define AC_DEFCFG_COLOR (0xf<<12)
#define AC_DEFCFG_COLOR_SHIFT 12
#define AC_DEFCFG_CONN_TYPE (0xf<<16)
@@ -417,7 +467,7 @@ struct hda_bus_ops {
/* free the private data */
void (*private_free)(struct hda_bus *);
#ifdef CONFIG_SND_HDA_POWER_SAVE
- /* notify power-up/down from codec to contoller */
+ /* notify power-up/down from codec to controller */
void (*pm_notify)(struct hda_codec *codec);
#endif
};
@@ -456,6 +506,9 @@ struct hda_bus {
struct hda_bus_unsolicited *unsol;
struct snd_info_entry *proc;
+
+ /* misc op flags */
+ unsigned int needs_damn_long_delay :1;
};
/*
@@ -470,6 +523,7 @@ struct hda_codec_preset {
unsigned int subs;
unsigned int subs_mask;
unsigned int rev;
+ hda_nid_t afg, mfg;
const char *name;
int (*patch)(struct hda_codec *codec);
};
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index c957eb58de5..f9de7c467c2 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -20,7 +20,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <sound/core.h>
diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c
index bafb7b01f5a..2177d9af533 100644
--- a/sound/pci/hda/hda_hwdep.c
+++ b/sound/pci/hda/hda_hwdep.c
@@ -18,7 +18,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/pci.h>
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 3fa0f970490..56f8a305075 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -34,7 +34,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
@@ -50,29 +49,32 @@
#include "hda_codec.h"
-static int index = SNDRV_DEFAULT_IDX1;
-static char *id = SNDRV_DEFAULT_STR1;
-static char *model;
-static int position_fix;
-static int probe_mask = -1;
+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 char *model[SNDRV_CARDS];
+static int position_fix[SNDRV_CARDS];
+static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
static int single_cmd;
static int enable_msi;
-module_param(index, int, 0444);
+module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for Intel HD audio interface.");
-module_param(id, charp, 0444);
+module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for Intel HD audio interface.");
-module_param(model, charp, 0444);
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable Intel HD audio interface.");
+module_param_array(model, charp, NULL, 0444);
MODULE_PARM_DESC(model, "Use the given board model.");
-module_param(position_fix, int, 0444);
+module_param_array(position_fix, int, NULL, 0444);
MODULE_PARM_DESC(position_fix, "Fix DMA pointer "
"(0 = auto, 1 = none, 2 = POSBUF, 3 = FIFO size).");
-module_param(probe_mask, int, 0444);
+module_param_array(probe_mask, int, NULL, 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(enable_msi, int, 0);
+module_param(enable_msi, int, 0444);
MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)");
#ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -87,10 +89,6 @@ module_param(power_save_controller, bool, 0644);
MODULE_PARM_DESC(power_save_controller, "Reset controller in power save mode.");
#endif
-/* just for backward compatibility */
-static int enable;
-module_param(enable, bool, 0444);
-
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
"{Intel, ICH6M},"
@@ -98,12 +96,20 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
"{Intel, ESB2},"
"{Intel, ICH8},"
"{Intel, ICH9},"
+ "{Intel, ICH10},"
+ "{Intel, SCH},"
"{ATI, SB450},"
"{ATI, SB600},"
"{ATI, RS600},"
"{ATI, RS690},"
"{ATI, RS780},"
"{ATI, R600},"
+ "{ATI, RV630},"
+ "{ATI, RV610},"
+ "{ATI, RV670},"
+ "{ATI, RV635},"
+ "{ATI, RV620},"
+ "{ATI, RV770},"
"{VIA, VT8251},"
"{VIA, VT8237A},"
"{SiS, SIS966},"
@@ -370,6 +376,7 @@ struct azx {
/* driver types */
enum {
AZX_DRIVER_ICH,
+ AZX_DRIVER_SCH,
AZX_DRIVER_ATI,
AZX_DRIVER_ATIHDMI,
AZX_DRIVER_VIA,
@@ -380,6 +387,7 @@ enum {
static char *driver_short_names[] __devinitdata = {
[AZX_DRIVER_ICH] = "HDA Intel",
+ [AZX_DRIVER_SCH] = "HDA Intel MID",
[AZX_DRIVER_ATI] = "HDA ATI SB",
[AZX_DRIVER_ATIHDMI] = "HDA ATI HDMI",
[AZX_DRIVER_VIA] = "HDA VIA VT82xx",
@@ -547,7 +555,7 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec)
again:
timeout = jiffies + msecs_to_jiffies(1000);
- do {
+ for (;;) {
if (chip->polling_mode) {
spin_lock_irq(&chip->reg_lock);
azx_update_rirb(chip);
@@ -555,8 +563,15 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec)
}
if (!chip->rirb.cmds)
return chip->rirb.res; /* the last value */
- schedule_timeout_uninterruptible(1);
- } while (time_after_eq(timeout, jiffies));
+ if (time_after(jiffies, timeout))
+ break;
+ if (codec->bus->needs_damn_long_delay)
+ msleep(2); /* temporary workaround */
+ else {
+ udelay(10);
+ cond_resched();
+ }
+ }
if (chip->msi) {
snd_printk(KERN_WARNING "hda_intel: No response from codec, "
@@ -618,8 +633,9 @@ static int azx_single_send_cmd(struct hda_codec *codec, u32 val)
}
udelay(1);
}
- snd_printd(SFX "send_cmd timeout: IRS=0x%x, val=0x%x\n",
- azx_readw(chip, IRS), val);
+ if (printk_ratelimit())
+ snd_printd(SFX "send_cmd timeout: IRS=0x%x, val=0x%x\n",
+ azx_readw(chip, IRS), val);
return -EIO;
}
@@ -635,8 +651,9 @@ static unsigned int azx_single_get_response(struct hda_codec *codec)
return azx_readl(chip, IR);
udelay(1);
}
- snd_printd(SFX "get_response timeout: IRS=0x%x\n",
- azx_readw(chip, IRS));
+ if (printk_ratelimit())
+ snd_printd(SFX "get_response timeout: IRS=0x%x\n",
+ azx_readw(chip, IRS));
return (unsigned int)-1;
}
@@ -1031,7 +1048,8 @@ static unsigned int azx_max_codecs[] __devinitdata = {
[AZX_DRIVER_NVIDIA] = 3, /* FIXME: correct? */
};
-static int __devinit azx_codec_create(struct azx *chip, const char *model)
+static int __devinit azx_codec_create(struct azx *chip, const char *model,
+ unsigned int codec_probe_mask)
{
struct hda_bus_template bus_temp;
int c, codecs, audio_codecs, err;
@@ -1052,7 +1070,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model)
codecs = audio_codecs = 0;
for (c = 0; c < AZX_MAX_CODECS; c++) {
- if ((chip->codec_mask & (1 << c)) & probe_mask) {
+ if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {
struct hda_codec *codec;
err = snd_hda_codec_new(chip->bus, c, &codec);
if (err < 0)
@@ -1065,7 +1083,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model)
if (!audio_codecs) {
/* probe additional slots if no codec is found */
for (; c < azx_max_codecs[chip->driver_type]; c++) {
- if ((chip->codec_mask & (1 << c)) & probe_mask) {
+ if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {
err = snd_hda_codec_new(chip->bus, c, NULL);
if (err < 0)
continue;
@@ -1676,18 +1694,18 @@ static struct snd_pci_quirk probe_mask_list[] __devinitdata = {
{}
};
-static void __devinit check_probe_mask(struct azx *chip)
+static void __devinit check_probe_mask(struct azx *chip, int dev)
{
const struct snd_pci_quirk *q;
- if (probe_mask == -1) {
+ if (probe_mask[dev] == -1) {
q = snd_pci_quirk_lookup(chip->pci, probe_mask_list);
if (q) {
printk(KERN_INFO
"hda_intel: probe_mask set to 0x%x "
"for device %04x:%04x\n",
q->value, q->subvendor, q->subdevice);
- probe_mask = q->value;
+ probe_mask[dev] = q->value;
}
}
}
@@ -1697,17 +1715,18 @@ static void __devinit check_probe_mask(struct azx *chip)
* constructor
*/
static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
- int driver_type,
+ int dev, int driver_type,
struct azx **rchip)
{
struct azx *chip;
int err;
+ unsigned short gcap;
static struct snd_device_ops ops = {
.dev_free = azx_dev_free,
};
*rchip = NULL;
-
+
err = pci_enable_device(pci);
if (err < 0)
return err;
@@ -1727,8 +1746,8 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
chip->driver_type = driver_type;
chip->msi = enable_msi;
- chip->position_fix = check_position_fix(chip, position_fix);
- check_probe_mask(chip);
+ chip->position_fix = check_position_fix(chip, position_fix[dev]);
+ check_probe_mask(chip, dev);
chip->single_cmd = single_cmd;
@@ -1769,25 +1788,40 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
pci_set_master(pci);
synchronize_irq(chip->irq);
- switch (chip->driver_type) {
- case AZX_DRIVER_ULI:
- chip->playback_streams = ULI_NUM_PLAYBACK;
- chip->capture_streams = ULI_NUM_CAPTURE;
- chip->playback_index_offset = ULI_PLAYBACK_INDEX;
- chip->capture_index_offset = ULI_CAPTURE_INDEX;
- break;
- case AZX_DRIVER_ATIHDMI:
- chip->playback_streams = ATIHDMI_NUM_PLAYBACK;
- chip->capture_streams = ATIHDMI_NUM_CAPTURE;
- chip->playback_index_offset = ATIHDMI_PLAYBACK_INDEX;
- chip->capture_index_offset = ATIHDMI_CAPTURE_INDEX;
- break;
- default:
- chip->playback_streams = ICH6_NUM_PLAYBACK;
- chip->capture_streams = ICH6_NUM_CAPTURE;
- chip->playback_index_offset = ICH6_PLAYBACK_INDEX;
- chip->capture_index_offset = ICH6_CAPTURE_INDEX;
- break;
+ gcap = azx_readw(chip, GCAP);
+ snd_printdd("chipset global capabilities = 0x%x\n", gcap);
+
+ if (gcap) {
+ /* read number of streams from GCAP register instead of using
+ * hardcoded value
+ */
+ chip->playback_streams = (gcap & (0xF << 12)) >> 12;
+ chip->capture_streams = (gcap & (0xF << 8)) >> 8;
+ chip->playback_index_offset = (gcap & (0xF << 12)) >> 12;
+ chip->capture_index_offset = 0;
+ } else {
+ /* gcap didn't give any info, switching to old method */
+
+ switch (chip->driver_type) {
+ case AZX_DRIVER_ULI:
+ chip->playback_streams = ULI_NUM_PLAYBACK;
+ chip->capture_streams = ULI_NUM_CAPTURE;
+ chip->playback_index_offset = ULI_PLAYBACK_INDEX;
+ chip->capture_index_offset = ULI_CAPTURE_INDEX;
+ break;
+ case AZX_DRIVER_ATIHDMI:
+ chip->playback_streams = ATIHDMI_NUM_PLAYBACK;
+ chip->capture_streams = ATIHDMI_NUM_CAPTURE;
+ chip->playback_index_offset = ATIHDMI_PLAYBACK_INDEX;
+ chip->capture_index_offset = ATIHDMI_CAPTURE_INDEX;
+ break;
+ default:
+ chip->playback_streams = ICH6_NUM_PLAYBACK;
+ chip->capture_streams = ICH6_NUM_CAPTURE;
+ chip->playback_index_offset = ICH6_PLAYBACK_INDEX;
+ chip->capture_index_offset = ICH6_CAPTURE_INDEX;
+ break;
+ }
}
chip->num_streams = chip->playback_streams + chip->capture_streams;
chip->azx_dev = kcalloc(chip->num_streams, sizeof(*chip->azx_dev),
@@ -1869,17 +1903,25 @@ static void power_down_all_codecs(struct azx *chip)
static int __devinit azx_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
+ static int dev;
struct snd_card *card;
struct azx *chip;
int err;
- card = snd_card_new(index, id, THIS_MODULE, 0);
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
if (!card) {
snd_printk(KERN_ERR SFX "Error creating card!\n");
return -ENOMEM;
}
- err = azx_create(card, pci, pci_id->driver_data, &chip);
+ err = azx_create(card, pci, dev, pci_id->driver_data, &chip);
if (err < 0) {
snd_card_free(card);
return err;
@@ -1887,7 +1929,7 @@ static int __devinit azx_probe(struct pci_dev *pci,
card->private_data = chip;
/* create codec instances */
- err = azx_codec_create(chip, model);
+ err = azx_codec_create(chip, model[dev], probe_mask[dev]);
if (err < 0) {
snd_card_free(card);
return err;
@@ -1919,6 +1961,7 @@ static int __devinit azx_probe(struct pci_dev *pci,
chip->running = 1;
power_down_all_codecs(chip);
+ dev++;
return err;
}
@@ -1936,12 +1979,21 @@ static struct pci_device_id azx_ids[] = {
{ 0x8086, 0x284b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH8 */
{ 0x8086, 0x293e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH9 */
{ 0x8086, 0x293f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH9 */
+ { 0x8086, 0x3a3e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH10 */
+ { 0x8086, 0x3a6e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH10 */
+ { 0x8086, 0x811b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_SCH }, /* SCH*/
{ 0x1002, 0x437b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB450 */
{ 0x1002, 0x4383, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB600 */
{ 0x1002, 0x793b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS600 HDMI */
{ 0x1002, 0x7919, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS690 HDMI */
- { 0x1002, 0x960c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS780 HDMI */
+ { 0x1002, 0x960f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS780 HDMI */
{ 0x1002, 0xaa00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI R600 HDMI */
+ { 0x1002, 0xaa08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV630 HDMI */
+ { 0x1002, 0xaa10, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV610 HDMI */
+ { 0x1002, 0xaa18, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV670 HDMI */
+ { 0x1002, 0xaa20, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV635 HDMI */
+ { 0x1002, 0xaa28, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV620 HDMI */
+ { 0x1002, 0xaa30, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV770 HDMI */
{ 0x1106, 0x3288, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_VIA }, /* VIA VT8251/VT8237A */
{ 0x1039, 0x7502, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_SIS }, /* SIS966 */
{ 0x10b9, 0x5461, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ULI }, /* ULI M5461 */
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 8c56c9cb0d0..ad0014ab71f 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -90,6 +90,13 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
void snd_hda_codec_resume_amp(struct hda_codec *codec);
#endif
+void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
+ unsigned int *tlv);
+struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
+ const char *name);
+int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
+ unsigned int *tlv, const char **slaves);
+
/* amp value bits */
#define HDA_AMP_MUTE 0x80
#define HDA_AMP_UNMUTE 0x00
@@ -325,6 +332,7 @@ struct auto_pin_cfg {
hda_nid_t input_pins[AUTO_PIN_LAST];
hda_nid_t dig_out_pin;
hda_nid_t dig_in_pin;
+ hda_nid_t mono_out_pin;
};
#define get_defcfg_connect(cfg) \
@@ -363,10 +371,11 @@ static inline u32 get_wcaps(struct hda_codec *codec, hda_nid_t nid)
{
if (nid < codec->start_nid ||
nid >= codec->start_nid + codec->num_nodes)
- return snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
+ return 0;
return codec->wcaps[nid - codec->start_nid];
}
+u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction);
int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int caps);
@@ -398,4 +407,11 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
hda_nid_t nid);
#endif /* CONFIG_SND_HDA_POWER_SAVE */
+/*
+ * virtual master control
+ */
+struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
+ const unsigned int *tlv);
+int snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave);
+
#endif /* __SOUND_HDA_LOCAL_H */
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c
index e94944f34ff..35a630d1770 100644
--- a/sound/pci/hda/hda_proc.c
+++ b/sound/pci/hda/hda_proc.c
@@ -21,7 +21,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <sound/core.h>
#include "hda_codec.h"
@@ -203,7 +202,8 @@ static const char *get_jack_color(u32 cfg)
}
static void print_pin_caps(struct snd_info_buffer *buffer,
- struct hda_codec *codec, hda_nid_t nid)
+ struct hda_codec *codec, hda_nid_t nid,
+ int *supports_vref)
{
static char *jack_conns[4] = { "Jack", "N/A", "Fixed", "Both" };
static char *jack_types[16] = {
@@ -213,7 +213,7 @@ static void print_pin_caps(struct snd_info_buffer *buffer,
"SPDIF In", "Digitial In", "Reserved", "Other"
};
static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" };
- unsigned int caps;
+ unsigned int caps, val;
caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
snd_iprintf(buffer, " Pincap 0x08%x:", caps);
@@ -227,7 +227,45 @@ static void print_pin_caps(struct snd_info_buffer *buffer,
snd_iprintf(buffer, " EAPD");
if (caps & AC_PINCAP_PRES_DETECT)
snd_iprintf(buffer, " Detect");
+ if (caps & AC_PINCAP_BALANCE)
+ snd_iprintf(buffer, " Balanced");
+ if (caps & AC_PINCAP_LR_SWAP)
+ snd_iprintf(buffer, " R/L");
+ if (caps & AC_PINCAP_TRIG_REQ)
+ snd_iprintf(buffer, " Trigger");
+ if (caps & AC_PINCAP_IMP_SENSE)
+ snd_iprintf(buffer, " ImpSense");
snd_iprintf(buffer, "\n");
+ if (caps & AC_PINCAP_VREF) {
+ unsigned int vref =
+ (caps & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
+ snd_iprintf(buffer, " Vref caps:");
+ if (vref & AC_PINCAP_VREF_HIZ)
+ snd_iprintf(buffer, " HIZ");
+ if (vref & AC_PINCAP_VREF_50)
+ snd_iprintf(buffer, " 50");
+ if (vref & AC_PINCAP_VREF_GRD)
+ snd_iprintf(buffer, " GRD");
+ if (vref & AC_PINCAP_VREF_80)
+ snd_iprintf(buffer, " 80");
+ if (vref & AC_PINCAP_VREF_100)
+ snd_iprintf(buffer, " 100");
+ snd_iprintf(buffer, "\n");
+ *supports_vref = 1;
+ } else
+ *supports_vref = 0;
+ if (caps & AC_PINCAP_EAPD) {
+ val = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_EAPD_BTLENABLE, 0);
+ snd_iprintf(buffer, " EAPD 0x%x:", val);
+ if (val & AC_EAPDBTL_BALANCED)
+ snd_iprintf(buffer, " BALANCED");
+ if (val & AC_EAPDBTL_EAPD)
+ snd_iprintf(buffer, " EAPD");
+ if (val & AC_EAPDBTL_LR_SWAP)
+ snd_iprintf(buffer, " R/L");
+ snd_iprintf(buffer, "\n");
+ }
caps = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
snd_iprintf(buffer, " Pin Default 0x%08x: [%s] %s at %s %s\n", caps,
jack_conns[(caps & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT],
@@ -237,8 +275,233 @@ static void print_pin_caps(struct snd_info_buffer *buffer,
snd_iprintf(buffer, " Conn = %s, Color = %s\n",
get_jack_connection(caps),
get_jack_color(caps));
+ /* Default association and sequence values refer to default grouping
+ * of pin complexes and their sequence within the group. This is used
+ * for priority and resource allocation.
+ */
+ snd_iprintf(buffer, " DefAssociation = 0x%x, Sequence = 0x%x\n",
+ (caps & AC_DEFCFG_DEF_ASSOC) >> AC_DEFCFG_ASSOC_SHIFT,
+ caps & AC_DEFCFG_SEQUENCE);
+ if (((caps & AC_DEFCFG_MISC) >> AC_DEFCFG_MISC_SHIFT) &
+ AC_DEFCFG_MISC_NO_PRESENCE) {
+ /* Miscellaneous bit indicates external hardware does not
+ * support presence detection even if the pin complex
+ * indicates it is supported.
+ */
+ snd_iprintf(buffer, " Misc = NO_PRESENCE\n");
+ }
+}
+
+static void print_pin_ctls(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid,
+ int supports_vref)
+{
+ unsigned int pinctls;
+
+ pinctls = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ snd_iprintf(buffer, " Pin-ctls: 0x%02x:", pinctls);
+ if (pinctls & AC_PINCTL_IN_EN)
+ snd_iprintf(buffer, " IN");
+ if (pinctls & AC_PINCTL_OUT_EN)
+ snd_iprintf(buffer, " OUT");
+ if (pinctls & AC_PINCTL_HP_EN)
+ snd_iprintf(buffer, " HP");
+ if (supports_vref) {
+ int vref = pinctls & AC_PINCTL_VREFEN;
+ switch (vref) {
+ case AC_PINCTL_VREF_HIZ:
+ snd_iprintf(buffer, " VREF_HIZ");
+ break;
+ case AC_PINCTL_VREF_50:
+ snd_iprintf(buffer, " VREF_50");
+ break;
+ case AC_PINCTL_VREF_GRD:
+ snd_iprintf(buffer, " VREF_GRD");
+ break;
+ case AC_PINCTL_VREF_80:
+ snd_iprintf(buffer, " VREF_80");
+ break;
+ case AC_PINCTL_VREF_100:
+ snd_iprintf(buffer, " VREF_100");
+ break;
+ }
+ }
+ snd_iprintf(buffer, "\n");
+}
+
+static void print_vol_knob(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int cap = snd_hda_param_read(codec, nid,
+ AC_PAR_VOL_KNB_CAP);
+ snd_iprintf(buffer, " Volume-Knob: delta=%d, steps=%d, ",
+ (cap >> 7) & 1, cap & 0x7f);
+ cap = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_VOLUME_KNOB_CONTROL, 0);
+ snd_iprintf(buffer, "direct=%d, val=%d\n",
+ (cap >> 7) & 1, cap & 0x7f);
+}
+
+static void print_audio_io(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid,
+ unsigned int wid_type)
+{
+ int conv = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
+ snd_iprintf(buffer,
+ " Converter: stream=%d, channel=%d\n",
+ (conv & AC_CONV_STREAM) >> AC_CONV_STREAM_SHIFT,
+ conv & AC_CONV_CHANNEL);
+
+ if (wid_type == AC_WID_AUD_IN && (conv & AC_CONV_CHANNEL) == 0) {
+ int sdi = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_SDI_SELECT, 0);
+ snd_iprintf(buffer, " SDI-Select: %d\n",
+ sdi & AC_SDI_SELECT);
+ }
+}
+
+static void print_digital_conv(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int digi1 = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_DIGI_CONVERT_1, 0);
+ unsigned int digi2 = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_DIGI_CONVERT_2, 0);
+ snd_iprintf(buffer, " Digital:");
+ if (digi1 & AC_DIG1_ENABLE)
+ snd_iprintf(buffer, " Enabled");
+ if (digi1 & AC_DIG1_V)
+ snd_iprintf(buffer, " Validity");
+ if (digi1 & AC_DIG1_VCFG)
+ snd_iprintf(buffer, " ValidityCfg");
+ if (digi1 & AC_DIG1_EMPHASIS)
+ snd_iprintf(buffer, " Preemphasis");
+ if (digi1 & AC_DIG1_COPYRIGHT)
+ snd_iprintf(buffer, " Copyright");
+ if (digi1 & AC_DIG1_NONAUDIO)
+ snd_iprintf(buffer, " Non-Audio");
+ if (digi1 & AC_DIG1_PROFESSIONAL)
+ snd_iprintf(buffer, " Pro");
+ if (digi1 & AC_DIG1_LEVEL)
+ snd_iprintf(buffer, " GenLevel");
+ snd_iprintf(buffer, "\n");
+ snd_iprintf(buffer, " Digital category: 0x%x\n", digi2 & AC_DIG2_CC);
+}
+
+static const char *get_pwr_state(u32 state)
+{
+ static const char *buf[4] = {
+ "D0", "D1", "D2", "D3"
+ };
+ if (state < 4)
+ return buf[state];
+ return "UNKNOWN";
+}
+
+static void print_power_state(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid)
+{
+ int pwr = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_POWER_STATE, 0);
+ snd_iprintf(buffer, " Power: setting=%s, actual=%s\n",
+ get_pwr_state(pwr & AC_PWRST_SETTING),
+ get_pwr_state((pwr & AC_PWRST_ACTUAL) >>
+ AC_PWRST_ACTUAL_SHIFT));
+}
+
+static void print_unsol_cap(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid)
+{
+ int unsol = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_UNSOLICITED_RESPONSE, 0);
+ snd_iprintf(buffer,
+ " Unsolicited: tag=%02x, enabled=%d\n",
+ unsol & AC_UNSOL_TAG,
+ (unsol & AC_UNSOL_ENABLED) ? 1 : 0);
+}
+
+static void print_proc_caps(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int proc_caps = snd_hda_param_read(codec, nid,
+ AC_PAR_PROC_CAP);
+ snd_iprintf(buffer, " Processing caps: benign=%d, ncoeff=%d\n",
+ proc_caps & AC_PCAP_BENIGN,
+ (proc_caps & AC_PCAP_NUM_COEF) >> AC_PCAP_NUM_COEF_SHIFT);
}
+static void print_conn_list(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid,
+ unsigned int wid_type, hda_nid_t *conn,
+ int conn_len)
+{
+ int c, curr = -1;
+
+ if (conn_len > 1 && wid_type != AC_WID_AUD_MIX)
+ curr = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_CONNECT_SEL, 0);
+ snd_iprintf(buffer, " Connection: %d\n", conn_len);
+ if (conn_len > 0) {
+ snd_iprintf(buffer, " ");
+ for (c = 0; c < conn_len; c++) {
+ snd_iprintf(buffer, " 0x%02x", conn[c]);
+ if (c == curr)
+ snd_iprintf(buffer, "*");
+ }
+ snd_iprintf(buffer, "\n");
+ }
+}
+
+static void print_realtek_coef(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid)
+{
+ int coeff = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PROC_COEF, 0);
+ snd_iprintf(buffer, " Processing Coefficient: 0x%02x\n", coeff);
+ coeff = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_COEF_INDEX, 0);
+ snd_iprintf(buffer, " Coefficient Index: 0x%02x\n", coeff);
+}
+
+static void print_gpio(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int gpio =
+ snd_hda_param_read(codec, codec->afg, AC_PAR_GPIO_CAP);
+ unsigned int enable, direction, wake, unsol, sticky, data;
+ int i, max;
+ snd_iprintf(buffer, "GPIO: io=%d, o=%d, i=%d, "
+ "unsolicited=%d, wake=%d\n",
+ gpio & AC_GPIO_IO_COUNT,
+ (gpio & AC_GPIO_O_COUNT) >> AC_GPIO_O_COUNT_SHIFT,
+ (gpio & AC_GPIO_I_COUNT) >> AC_GPIO_I_COUNT_SHIFT,
+ (gpio & AC_GPIO_UNSOLICITED) ? 1 : 0,
+ (gpio & AC_GPIO_WAKE) ? 1 : 0);
+ max = gpio & AC_GPIO_IO_COUNT;
+ enable = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_GPIO_MASK, 0);
+ direction = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_GPIO_DIRECTION, 0);
+ wake = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_GPIO_WAKE_MASK, 0);
+ unsol = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK, 0);
+ sticky = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_GPIO_STICKY_MASK, 0);
+ data = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_GPIO_DATA, 0);
+ for (i = 0; i < max; ++i)
+ snd_iprintf(buffer,
+ " IO[%d]: enable=%d, dir=%d, wake=%d, "
+ "sticky=%d, data=%d\n", i,
+ (enable & (1<<i)) ? 1 : 0,
+ (direction & (1<<i)) ? 1 : 0,
+ (wake & (1<<i)) ? 1 : 0,
+ (sticky & (1<<i)) ? 1 : 0,
+ (data & (1<<i)) ? 1 : 0);
+ /* FIXME: add GPO and GPI pin information */
+}
static void print_codec_info(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
@@ -276,14 +539,17 @@ static void print_codec_info(struct snd_info_entry *entry,
snd_hda_power_down(codec);
return;
}
+
+ print_gpio(buffer, codec, codec->afg);
+
for (i = 0; i < nodes; i++, nid++) {
unsigned int wid_caps =
snd_hda_param_read(codec, nid,
AC_PAR_AUDIO_WIDGET_CAP);
unsigned int wid_type =
(wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
- int conn_len = 0;
hda_nid_t conn[HDA_MAX_CONNECTIONS];
+ int conn_len = 0;
snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid,
get_wid_type_name(wid_type), wid_caps);
@@ -297,8 +563,18 @@ static void print_codec_info(struct snd_info_entry *entry,
snd_iprintf(buffer, " Amp-In");
if (wid_caps & AC_WCAP_OUT_AMP)
snd_iprintf(buffer, " Amp-Out");
+ if (wid_caps & AC_WCAP_STRIPE)
+ snd_iprintf(buffer, " Stripe");
+ if (wid_caps & AC_WCAP_LR_SWAP)
+ snd_iprintf(buffer, " R/L");
snd_iprintf(buffer, "\n");
+ /* volume knob is a special widget that always have connection
+ * list
+ */
+ if (wid_type == AC_WID_VOL_KNB)
+ wid_caps |= AC_WCAP_CONN_LIST;
+
if (wid_caps & AC_WCAP_CONN_LIST)
conn_len = snd_hda_get_connections(codec, nid, conn,
HDA_MAX_CONNECTIONS);
@@ -318,48 +594,49 @@ static void print_codec_info(struct snd_info_entry *entry,
wid_caps & AC_WCAP_STEREO, 1);
}
- if (wid_type == AC_WID_PIN) {
- unsigned int pinctls;
- print_pin_caps(buffer, codec, nid);
- pinctls = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL,
- 0);
- snd_iprintf(buffer, " Pin-ctls: 0x%02x:", pinctls);
- if (pinctls & AC_PINCTL_IN_EN)
- snd_iprintf(buffer, " IN");
- if (pinctls & AC_PINCTL_OUT_EN)
- snd_iprintf(buffer, " OUT");
- if (pinctls & AC_PINCTL_HP_EN)
- snd_iprintf(buffer, " HP");
- snd_iprintf(buffer, "\n");
+ switch (wid_type) {
+ case AC_WID_PIN: {
+ int supports_vref;
+ print_pin_caps(buffer, codec, nid, &supports_vref);
+ print_pin_ctls(buffer, codec, nid, supports_vref);
+ break;
}
-
- if ((wid_type == AC_WID_AUD_OUT || wid_type == AC_WID_AUD_IN) &&
- (wid_caps & AC_WCAP_FORMAT_OVRD)) {
- snd_iprintf(buffer, " PCM:\n");
- print_pcm_caps(buffer, codec, nid);
+ case AC_WID_VOL_KNB:
+ print_vol_knob(buffer, codec, nid);
+ break;
+ case AC_WID_AUD_OUT:
+ case AC_WID_AUD_IN:
+ print_audio_io(buffer, codec, nid, wid_type);
+ if (wid_caps & AC_WCAP_DIGITAL)
+ print_digital_conv(buffer, codec, nid);
+ if (wid_caps & AC_WCAP_FORMAT_OVRD) {
+ snd_iprintf(buffer, " PCM:\n");
+ print_pcm_caps(buffer, codec, nid);
+ }
+ break;
}
+ if (wid_caps & AC_WCAP_UNSOL_CAP)
+ print_unsol_cap(buffer, codec, nid);
+
if (wid_caps & AC_WCAP_POWER)
- snd_iprintf(buffer, " Power: 0x%x\n",
- snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_POWER_STATE,
- 0));
-
- if (wid_caps & AC_WCAP_CONN_LIST) {
- int c, curr = -1;
- if (conn_len > 1 && wid_type != AC_WID_AUD_MIX)
- curr = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_CONNECT_SEL, 0);
- snd_iprintf(buffer, " Connection: %d\n", conn_len);
- snd_iprintf(buffer, " ");
- for (c = 0; c < conn_len; c++) {
- snd_iprintf(buffer, " 0x%02x", conn[c]);
- if (c == curr)
- snd_iprintf(buffer, "*");
- }
- snd_iprintf(buffer, "\n");
- }
+ print_power_state(buffer, codec, nid);
+
+ if (wid_caps & AC_WCAP_DELAY)
+ snd_iprintf(buffer, " Delay: %d samples\n",
+ (wid_caps & AC_WCAP_DELAY) >>
+ AC_WCAP_DELAY_SHIFT);
+
+ if (wid_caps & AC_WCAP_CONN_LIST)
+ print_conn_list(buffer, codec, nid, wid_type,
+ conn, conn_len);
+
+ if (wid_caps & AC_WCAP_PROC_WID)
+ print_proc_caps(buffer, codec, nid);
+
+ /* NID 0x20 == Realtek Define Registers */
+ if (codec->vendor_id == 0x10ec && nid == 0x20)
+ print_realtek_coef(buffer, codec, nid);
}
snd_hda_power_down(codec);
}
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index 196ad3c9405..19f08846d6f 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -19,7 +19,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
@@ -79,6 +78,11 @@ struct ad198x_spec {
#ifdef CONFIG_SND_HDA_POWER_SAVE
struct hda_loopback_check loopback;
#endif
+ /* for virtual master */
+ hda_nid_t vmaster_nid;
+ u32 vmaster_tlv[4];
+ const char **slave_vols;
+ const char **slave_sws;
};
/*
@@ -126,6 +130,32 @@ static int ad198x_init(struct hda_codec *codec)
return 0;
}
+static const char *ad_slave_vols[] = {
+ "Front Playback Volume",
+ "Surround Playback Volume",
+ "Center Playback Volume",
+ "LFE Playback Volume",
+ "Side Playback Volume",
+ "Headphone Playback Volume",
+ "Mono Playback Volume",
+ "Speaker Playback Volume",
+ "IEC958 Playback Volume",
+ NULL
+};
+
+static const char *ad_slave_sws[] = {
+ "Front Playback Switch",
+ "Surround Playback Switch",
+ "Center Playback Switch",
+ "LFE Playback Switch",
+ "Side Playback Switch",
+ "Headphone Playback Switch",
+ "Mono Playback Switch",
+ "Speaker Playback Switch",
+ "IEC958 Playback Switch",
+ NULL
+};
+
static int ad198x_build_controls(struct hda_codec *codec)
{
struct ad198x_spec *spec = codec->spec;
@@ -147,6 +177,27 @@ static int ad198x_build_controls(struct hda_codec *codec)
if (err < 0)
return err;
}
+
+ /* if we have no master control, let's create it */
+ if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
+ snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
+ HDA_OUTPUT, spec->vmaster_tlv);
+ err = snd_hda_add_vmaster(codec, "Master Playback Volume",
+ spec->vmaster_tlv,
+ (spec->slave_vols ?
+ spec->slave_vols : ad_slave_vols));
+ if (err < 0)
+ return err;
+ }
+ if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
+ err = snd_hda_add_vmaster(codec, "Master Playback Switch",
+ NULL,
+ (spec->slave_sws ?
+ spec->slave_sws : ad_slave_sws));
+ if (err < 0)
+ return err;
+ }
+
return 0;
}
@@ -370,7 +421,7 @@ static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,
int invert = (kcontrol->private_value >> 8) & 1;
hda_nid_t nid = kcontrol->private_value & 0xff;
unsigned int eapd;
- eapd = ucontrol->value.integer.value[0];
+ eapd = !!ucontrol->value.integer.value[0];
if (invert)
eapd = !eapd;
if (eapd == spec->cur_eapd)
@@ -833,27 +884,29 @@ static const char *ad1986a_models[AD1986A_MODELS] = {
static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD),
+ SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD),
SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD),
SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD),
SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD),
+ SND_PCI_QUIRK(0x1043, 0x1443, "ASUS VX1", AD1986A_LAPTOP),
SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK),
SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK),
SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP),
SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK),
SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK),
SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK),
+ SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
+ SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba", AD1986A_LAPTOP_EAPD),
SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
SND_PCI_QUIRK(0x144d, 0xc023, "Samsung X60", AD1986A_LAPTOP_EAPD),
SND_PCI_QUIRK(0x144d, 0xc024, "Samsung R65", AD1986A_LAPTOP_EAPD),
SND_PCI_QUIRK(0x144d, 0xc026, "Samsung X11", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
+ SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE),
@@ -872,6 +925,13 @@ static struct hda_amp_list ad1986a_loopbacks[] = {
};
#endif
+static int is_jack_available(struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int conf = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_CONFIG_DEFAULT, 0);
+ return get_defcfg_connect(conf) != AC_JACK_PORT_NONE;
+}
+
static int patch_ad1986a(struct hda_codec *codec)
{
struct ad198x_spec *spec;
@@ -898,6 +958,7 @@ static int patch_ad1986a(struct hda_codec *codec)
#ifdef CONFIG_SND_HDA_POWER_SAVE
spec->loopback.amplist = ad1986a_loopbacks;
#endif
+ spec->vmaster_nid = 0x1b;
codec->patch_ops = ad198x_patch_ops;
@@ -930,7 +991,8 @@ static int patch_ad1986a(struct hda_codec *codec)
spec->multiout.max_channels = 2;
spec->multiout.num_dacs = 1;
spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
- spec->multiout.dig_out_nid = 0;
+ if (!is_jack_available(codec, 0x25))
+ spec->multiout.dig_out_nid = 0;
spec->input_mux = &ad1986a_laptop_eapd_capture_source;
break;
case AD1986A_LAPTOP_AUTOMUTE:
@@ -941,7 +1003,8 @@ static int patch_ad1986a(struct hda_codec *codec)
spec->multiout.max_channels = 2;
spec->multiout.num_dacs = 1;
spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
- spec->multiout.dig_out_nid = 0;
+ if (!is_jack_available(codec, 0x25))
+ spec->multiout.dig_out_nid = 0;
spec->input_mux = &ad1986a_laptop_eapd_capture_source;
codec->patch_ops.unsol_event = ad1986a_hp_unsol_event;
codec->patch_ops.init = ad1986a_hp_init;
@@ -1020,6 +1083,8 @@ static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ad198x_spec *spec = codec->spec;
+ if (ucontrol->value.enumerated.item[0] > 1)
+ return -EINVAL;
if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
spec->spdif_route = ucontrol->value.enumerated.item[0];
snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0,
@@ -1138,6 +1203,7 @@ static int patch_ad1983(struct hda_codec *codec)
#ifdef CONFIG_SND_HDA_POWER_SAVE
spec->loopback.amplist = ad1983_loopbacks;
#endif
+ spec->vmaster_nid = 0x05;
codec->patch_ops = ad198x_patch_ops;
@@ -1496,14 +1562,14 @@ static const char *ad1981_models[AD1981_MODELS] = {
};
static struct snd_pci_quirk ad1981_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
/* All HP models */
SND_PCI_QUIRK(0x103c, 0, "HP nx", AD1981_HP),
- /* HP nx6320 (reversed SSID, H/W bug) */
- SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
+ SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
/* Lenovo Thinkpad T60/X60/Z6xx */
SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1981_THINKPAD),
- SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
- SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
+ /* HP nx6320 (reversed SSID, H/W bug) */
+ SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
{}
};
@@ -1534,6 +1600,7 @@ static int patch_ad1981(struct hda_codec *codec)
#ifdef CONFIG_SND_HDA_POWER_SAVE
spec->loopback.amplist = ad1981_loopbacks;
#endif
+ spec->vmaster_nid = 0x05;
codec->patch_ops = ad198x_patch_ops;
@@ -1908,7 +1975,6 @@ static struct snd_kcontrol_new ad1988_capture_mixers[] = {
.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",
@@ -1965,6 +2031,8 @@ static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,
int change;
val = ucontrol->value.enumerated.item[0];
+ if (val > 3)
+ return -EINVAL;
if (!val) {
sel = snd_hda_codec_read(codec, 0x1d, 0,
AC_VERB_GET_AMP_GAIN_MUTE,
@@ -2079,6 +2147,8 @@ static struct hda_verb ad1988_6stack_init_verbs[] = {
{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
{0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
{0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
+ /* Analog CD Input */
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
{ }
};
@@ -2720,8 +2790,8 @@ static const char *ad1988_models[AD1988_MODEL_LAST] = {
};
static struct snd_pci_quirk ad1988_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
+ SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
{}
};
@@ -2843,6 +2913,7 @@ static int patch_ad1988(struct hda_codec *codec)
#ifdef CONFIG_SND_HDA_POWER_SAVE
spec->loopback.amplist = ad1988_loopbacks;
#endif
+ spec->vmaster_nid = 0x04;
return 0;
}
@@ -2919,7 +2990,6 @@ static struct snd_kcontrol_new ad1884_base_mixers[] = {
.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",
@@ -3009,6 +3079,20 @@ static struct hda_amp_list ad1884_loopbacks[] = {
};
#endif
+static const char *ad1884_slave_vols[] = {
+ "PCM Playback Volume",
+ "Mic Playback Volume",
+ "Mono Playback Volume",
+ "Front Mic Playback Volume",
+ "Mic Playback Volume",
+ "CD Playback Volume",
+ "Internal Mic Playback Volume",
+ "Docking Mic Playback Volume"
+ "Beep Playback Volume",
+ "IEC958 Playback Volume",
+ NULL
+};
+
static int patch_ad1884(struct hda_codec *codec)
{
struct ad198x_spec *spec;
@@ -3036,6 +3120,9 @@ static int patch_ad1884(struct hda_codec *codec)
#ifdef CONFIG_SND_HDA_POWER_SAVE
spec->loopback.amplist = ad1884_loopbacks;
#endif
+ spec->vmaster_nid = 0x04;
+ /* we need to cover all playback volumes */
+ spec->slave_vols = ad1884_slave_vols;
codec->patch_ops = ad198x_patch_ops;
@@ -3054,6 +3141,20 @@ static struct hda_input_mux ad1984_thinkpad_capture_source = {
},
};
+
+/*
+ * Dell Precision T3400
+ */
+static struct hda_input_mux ad1984_dell_desktop_capture_source = {
+ .num_items = 3,
+ .items = {
+ { "Front Mic", 0x0 },
+ { "Line-In", 0x1 },
+ { "Mix", 0x3 },
+ },
+};
+
+
static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
/* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
@@ -3078,7 +3179,6 @@ static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
.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",
@@ -3087,6 +3187,16 @@ static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
.get = ad198x_mux_enum_get,
.put = ad198x_mux_enum_put,
},
+ /* SPDIF controls */
+ HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
+ /* identical with ad1983 */
+ .info = ad1983_spdif_route_info,
+ .get = ad1983_spdif_route_get,
+ .put = ad1983_spdif_route_put,
+ },
{ } /* end */
};
@@ -3104,6 +3214,44 @@ static struct hda_verb ad1984_thinkpad_init_verbs[] = {
{ } /* end */
};
+/*
+ * Dell Precision T3400
+ */
+static struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = {
+ HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
+ /*
+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x20, 0x03, HDA_INPUT),
+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x20, 0x03, HDA_INPUT),
+ */
+ HDA_CODEC_VOLUME("Line-In Boost", 0x15, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* The multiple "Capture Source" controls confuse alsamixer
+ * So call somewhat different..
+ */
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 2,
+ .info = ad198x_mux_enum_info,
+ .get = ad198x_mux_enum_get,
+ .put = ad198x_mux_enum_put,
+ },
+ { } /* end */
+};
+
/* Digial MIC ADC NID 0x05 + 0x06 */
static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
@@ -3157,17 +3305,20 @@ static int ad1984_build_pcms(struct hda_codec *codec)
enum {
AD1984_BASIC,
AD1984_THINKPAD,
+ AD1984_DELL_DESKTOP,
AD1984_MODELS
};
static const char *ad1984_models[AD1984_MODELS] = {
[AD1984_BASIC] = "basic",
[AD1984_THINKPAD] = "thinkpad",
+ [AD1984_DELL_DESKTOP] = "dell_desktop",
};
static struct snd_pci_quirk ad1984_cfg_tbl[] = {
/* Lenovo Thinkpad T61/X61 */
SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1984_THINKPAD),
+ SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
{}
};
@@ -3189,11 +3340,16 @@ static int patch_ad1984(struct hda_codec *codec)
codec->patch_ops.build_pcms = ad1984_build_pcms;
break;
case AD1984_THINKPAD:
- spec->multiout.dig_out_nid = 0;
+ spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
spec->input_mux = &ad1984_thinkpad_capture_source;
spec->mixers[0] = ad1984_thinkpad_mixers;
spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
break;
+ case AD1984_DELL_DESKTOP:
+ spec->multiout.dig_out_nid = 0;
+ spec->input_mux = &ad1984_dell_desktop_capture_source;
+ spec->mixers[0] = ad1984_dell_desktop_mixers;
+ break;
}
return 0;
}
@@ -3267,7 +3423,6 @@ static struct snd_kcontrol_new ad1882_base_mixers[] = {
.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",
@@ -3468,6 +3623,7 @@ static int patch_ad1882(struct hda_codec *codec)
#ifdef CONFIG_SND_HDA_POWER_SAVE
spec->loopback.amplist = ad1882_loopbacks;
#endif
+ spec->vmaster_nid = 0x04;
codec->patch_ops = ad198x_patch_ops;
diff --git a/sound/pci/hda/patch_atihdmi.c b/sound/pci/hda/patch_atihdmi.c
index fbb8969dc55..9a8bb4ce3f8 100644
--- a/sound/pci/hda/patch_atihdmi.c
+++ b/sound/pci/hda/patch_atihdmi.c
@@ -21,7 +21,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
@@ -158,6 +157,6 @@ struct hda_codec_preset snd_hda_preset_atihdmi[] = {
{ .id = 0x1002793c, .name = "ATI RS600 HDMI", .patch = patch_atihdmi },
{ .id = 0x10027919, .name = "ATI RS600 HDMI", .patch = patch_atihdmi },
{ .id = 0x1002791a, .name = "ATI RS690/780 HDMI", .patch = patch_atihdmi },
- { .id = 0x1002aa01, .name = "ATI R600 HDMI", .patch = patch_atihdmi },
+ { .id = 0x1002aa01, .name = "ATI R6xx HDMI", .patch = patch_atihdmi },
{} /* terminator */
};
diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c
index 6c54793bf42..3d6097ba1d6 100644
--- a/sound/pci/hda/patch_cmedia.c
+++ b/sound/pci/hda/patch_cmedia.c
@@ -21,7 +21,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
@@ -186,7 +185,6 @@ static struct snd_kcontrol_new cmi9880_basic_mixer[] = {
.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",
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 6aa07398674..f6dd51cda7b 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -20,7 +20,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
@@ -65,6 +64,11 @@ struct conexant_spec {
hda_nid_t *adc_nids;
hda_nid_t dig_in_nid; /* digital-in NID; optional */
+ unsigned int cur_adc_idx;
+ hda_nid_t cur_adc;
+ unsigned int cur_adc_stream_tag;
+ unsigned int cur_adc_format;
+
/* capture source */
const struct hda_input_mux *input_mux;
hda_nid_t *capsrc_nids;
@@ -218,6 +222,41 @@ static struct hda_pcm_stream conexant_pcm_digital_capture = {
/* NID is set in alc_build_pcms */
};
+static int cx5051_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct conexant_spec *spec = codec->spec;
+ spec->cur_adc = spec->adc_nids[spec->cur_adc_idx];
+ spec->cur_adc_stream_tag = stream_tag;
+ spec->cur_adc_format = format;
+ snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
+ return 0;
+}
+
+static int cx5051_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct conexant_spec *spec = codec->spec;
+ snd_hda_codec_setup_stream(codec, spec->cur_adc, 0, 0, 0);
+ spec->cur_adc = 0;
+ return 0;
+}
+
+static struct hda_pcm_stream cx5051_pcm_analog_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .nid = 0, /* fill later */
+ .ops = {
+ .prepare = cx5051_capture_pcm_prepare,
+ .cleanup = cx5051_capture_pcm_cleanup
+ },
+};
+
static int conexant_build_pcms(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
@@ -232,7 +271,12 @@ static int conexant_build_pcms(struct hda_codec *codec)
spec->multiout.max_channels;
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
spec->multiout.dac_nids[0];
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = conexant_pcm_analog_capture;
+ if (codec->vendor_id == 0x14f15051)
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+ cx5051_pcm_analog_capture;
+ else
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+ conexant_pcm_analog_capture;
info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;
info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
@@ -373,7 +417,7 @@ static int cxt_eapd_put(struct snd_kcontrol *kcontrol,
hda_nid_t nid = kcontrol->private_value & 0xff;
unsigned int eapd;
- eapd = ucontrol->value.integer.value[0];
+ eapd = !!ucontrol->value.integer.value[0];
if (invert)
eapd = !eapd;
if (eapd == spec->cur_eapd)
@@ -454,7 +498,16 @@ static struct hda_input_mux cxt5045_capture_source = {
.num_items = 2,
.items = {
{ "IntMic", 0x1 },
- { "LineIn", 0x2 },
+ { "ExtMic", 0x2 },
+ }
+};
+
+static struct hda_input_mux cxt5045_capture_source_benq = {
+ .num_items = 3,
+ .items = {
+ { "IntMic", 0x1 },
+ { "ExtMic", 0x2 },
+ { "LineIn", 0x3 },
}
};
@@ -577,6 +630,15 @@ static struct snd_kcontrol_new cxt5045_mixers[] = {
{}
};
+static struct snd_kcontrol_new cxt5045_benq_mixers[] = {
+ HDA_CODEC_VOLUME("Line In Capture Volume", 0x1a, 0x03, HDA_INPUT),
+ HDA_CODEC_MUTE("Line In Capture Switch", 0x1a, 0x03, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line In Playback Volume", 0x17, 0x3, HDA_INPUT),
+ HDA_CODEC_MUTE("Line In Playback Switch", 0x17, 0x3, HDA_INPUT),
+
+ {}
+};
+
static struct hda_verb cxt5045_init_verbs[] = {
/* Line in, Mic */
{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
@@ -602,6 +664,30 @@ static struct hda_verb cxt5045_init_verbs[] = {
{ } /* end */
};
+static struct hda_verb cxt5045_benq_init_verbs[] = {
+ /* Int Mic, Mic */
+ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 },
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 },
+ /* Line In,HP, Amp */
+ {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x10, AC_VERB_SET_CONNECT_SEL, 0x1},
+ {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x11, AC_VERB_SET_CONNECT_SEL, 0x1},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+ /* Record selector: Int mic */
+ {0x1a, AC_VERB_SET_CONNECT_SEL, 0x1},
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE,
+ AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x17},
+ /* SPDIF route: PCM */
+ {0x13, AC_VERB_SET_CONNECT_SEL, 0x0},
+ /* EAPD */
+ {0x10, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
+ { } /* end */
+};
static struct hda_verb cxt5045_hp_sense_init_verbs[] = {
/* pin sensing on HP jack */
@@ -740,8 +826,10 @@ static int cxt5045_init(struct hda_codec *codec)
enum {
- CXT5045_LAPTOP, /* Laptops w/ EAPD support */
- CXT5045_FUJITSU, /* Laptops w/ EAPD support */
+ CXT5045_LAPTOP_HPSENSE,
+ CXT5045_LAPTOP_MICSENSE,
+ CXT5045_LAPTOP_HPMICSENSE,
+ CXT5045_BENQ,
#ifdef CONFIG_SND_DEBUG
CXT5045_TEST,
#endif
@@ -749,23 +837,35 @@ enum {
};
static const char *cxt5045_models[CXT5045_MODELS] = {
- [CXT5045_LAPTOP] = "laptop",
- [CXT5045_FUJITSU] = "fujitsu",
+ [CXT5045_LAPTOP_HPSENSE] = "laptop-hpsense",
+ [CXT5045_LAPTOP_MICSENSE] = "laptop-micsense",
+ [CXT5045_LAPTOP_HPMICSENSE] = "laptop-hpmicsense",
+ [CXT5045_BENQ] = "benq",
#ifdef CONFIG_SND_DEBUG
[CXT5045_TEST] = "test",
#endif
};
static struct snd_pci_quirk cxt5045_cfg_tbl[] = {
- SND_PCI_QUIRK(0x103c, 0x30b7, "HP DV6000Z", CXT5045_LAPTOP),
- SND_PCI_QUIRK(0x103c, 0x30bb, "HP DV8000", CXT5045_LAPTOP),
- SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV Series", CXT5045_LAPTOP),
- SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2120", CXT5045_LAPTOP),
- SND_PCI_QUIRK(0x103c, 0x30cd, "HP DV Series", CXT5045_LAPTOP),
- SND_PCI_QUIRK(0x103c, 0x30d9, "HP Spartan", CXT5045_LAPTOP),
- SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_FUJITSU),
- SND_PCI_QUIRK(0x1734, 0x10cb, "Fujitsu Si3515", CXT5045_LAPTOP),
- SND_PCI_QUIRK(0x8086, 0x2111, "Conexant Reference board", CXT5045_LAPTOP),
+ SND_PCI_QUIRK(0x103c, 0x30a5, "HP", CXT5045_LAPTOP_HPSENSE),
+ SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV Series", CXT5045_LAPTOP_HPSENSE),
+ SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2120", CXT5045_LAPTOP_HPSENSE),
+ SND_PCI_QUIRK(0x103c, 0x30b7, "HP DV6000Z", CXT5045_LAPTOP_HPSENSE),
+ SND_PCI_QUIRK(0x103c, 0x30bb, "HP DV8000", CXT5045_LAPTOP_HPSENSE),
+ SND_PCI_QUIRK(0x103c, 0x30cd, "HP DV Series", CXT5045_LAPTOP_HPSENSE),
+ SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV9533EG", CXT5045_LAPTOP_HPSENSE),
+ SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT5045_LAPTOP_HPSENSE),
+ SND_PCI_QUIRK(0x103c, 0x30d9, "HP Spartan", CXT5045_LAPTOP_HPSENSE),
+ SND_PCI_QUIRK(0x152d, 0x0753, "Benq R55E", CXT5045_BENQ),
+ SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_LAPTOP_MICSENSE),
+ SND_PCI_QUIRK(0x1734, 0x10cb, "Fujitsu Si3515", CXT5045_LAPTOP_HPMICSENSE),
+ SND_PCI_QUIRK(0x1734, 0x110e, "Fujitsu V5505", CXT5045_LAPTOP_HPSENSE),
+ SND_PCI_QUIRK(0x1509, 0x1e40, "FIC", CXT5045_LAPTOP_HPMICSENSE),
+ SND_PCI_QUIRK(0x1509, 0x2f05, "FIC", CXT5045_LAPTOP_HPMICSENSE),
+ SND_PCI_QUIRK(0x1509, 0x2f06, "FIC", CXT5045_LAPTOP_HPMICSENSE),
+ SND_PCI_QUIRK(0x1631, 0xc106, "Packard Bell", CXT5045_LAPTOP_HPMICSENSE),
+ SND_PCI_QUIRK(0x1631, 0xc107, "Packard Bell", CXT5045_LAPTOP_HPMICSENSE),
+ SND_PCI_QUIRK(0x8086, 0x2111, "Conexant Reference board", CXT5045_LAPTOP_HPSENSE),
{}
};
@@ -803,7 +903,7 @@ static int patch_cxt5045(struct hda_codec *codec)
cxt5045_models,
cxt5045_cfg_tbl);
switch (board_config) {
- case CXT5045_LAPTOP:
+ case CXT5045_LAPTOP_HPSENSE:
codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
spec->input_mux = &cxt5045_capture_source;
spec->num_init_verbs = 2;
@@ -811,20 +911,53 @@ static int patch_cxt5045(struct hda_codec *codec)
spec->mixers[0] = cxt5045_mixers;
codec->patch_ops.init = cxt5045_init;
break;
- case CXT5045_FUJITSU:
+ case CXT5045_LAPTOP_MICSENSE:
spec->input_mux = &cxt5045_capture_source;
spec->num_init_verbs = 2;
spec->init_verbs[1] = cxt5045_mic_sense_init_verbs;
spec->mixers[0] = cxt5045_mixers;
codec->patch_ops.init = cxt5045_init;
break;
+ default:
+ case CXT5045_LAPTOP_HPMICSENSE:
+ codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
+ spec->input_mux = &cxt5045_capture_source;
+ spec->num_init_verbs = 3;
+ spec->init_verbs[1] = cxt5045_hp_sense_init_verbs;
+ spec->init_verbs[2] = cxt5045_mic_sense_init_verbs;
+ spec->mixers[0] = cxt5045_mixers;
+ codec->patch_ops.init = cxt5045_init;
+ break;
+ case CXT5045_BENQ:
+ codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
+ spec->input_mux = &cxt5045_capture_source_benq;
+ spec->num_init_verbs = 1;
+ spec->init_verbs[0] = cxt5045_benq_init_verbs;
+ spec->mixers[0] = cxt5045_mixers;
+ spec->mixers[1] = cxt5045_benq_mixers;
+ spec->num_mixers = 2;
+ codec->patch_ops.init = cxt5045_init;
+ break;
#ifdef CONFIG_SND_DEBUG
case CXT5045_TEST:
spec->input_mux = &cxt5045_test_capture_source;
spec->mixers[0] = cxt5045_test_mixer;
spec->init_verbs[0] = cxt5045_test_init_verbs;
+ break;
+
#endif
}
+
+ /*
+ * Fix max PCM level to 0 dB
+ * (originall it has 0x2b steps with 0dB offset 0x14)
+ */
+ snd_hda_override_amp_caps(codec, 0x17, HDA_INPUT,
+ (0x14 << AC_AMPCAP_OFFSET_SHIFT) |
+ (0x14 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+ (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+ (1 << AC_AMPCAP_MUTE_SHIFT));
+
return 0;
}
@@ -933,13 +1066,13 @@ static void cxt5047_hp2_automute(struct hda_codec *codec)
static void cxt5047_hp_automic(struct hda_codec *codec)
{
static struct hda_verb mic_jack_on[] = {
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
{}
};
static struct hda_verb mic_jack_off[] = {
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
{}
};
unsigned int present;
@@ -956,8 +1089,7 @@ static void cxt5047_hp_automic(struct hda_codec *codec)
static void cxt5047_hp_unsol_event(struct hda_codec *codec,
unsigned int res)
{
- res >>= 26;
- switch (res) {
+ switch (res >> 26) {
case CONEXANT_HP_EVENT:
cxt5047_hp_automute(codec);
break;
@@ -1166,6 +1298,17 @@ static struct snd_kcontrol_new cxt5047_test_mixer[] = {
.get = conexant_mux_enum_get,
.put = conexant_mux_enum_put,
},
+ HDA_CODEC_VOLUME("Input-1 Volume", 0x1a, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Input-1 Switch", 0x1a, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Input-2 Volume", 0x1a, 0x1, HDA_INPUT),
+ HDA_CODEC_MUTE("Input-2 Switch", 0x1a, 0x1, HDA_INPUT),
+ HDA_CODEC_VOLUME("Input-3 Volume", 0x1a, 0x2, HDA_INPUT),
+ HDA_CODEC_MUTE("Input-3 Switch", 0x1a, 0x2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Input-4 Volume", 0x1a, 0x3, HDA_INPUT),
+ HDA_CODEC_MUTE("Input-4 Switch", 0x1a, 0x3, HDA_INPUT),
+ HDA_CODEC_VOLUME("Input-5 Volume", 0x1a, 0x4, HDA_INPUT),
+ HDA_CODEC_MUTE("Input-5 Switch", 0x1a, 0x4, HDA_INPUT),
+
{ } /* end */
};
@@ -1255,9 +1398,9 @@ static const char *cxt5047_models[CXT5047_MODELS] = {
static struct snd_pci_quirk cxt5047_cfg_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x30a0, "HP DV1000", CXT5047_LAPTOP),
+ SND_PCI_QUIRK(0x103c, 0x30a5, "HP DV5200T/DV8000T", CXT5047_LAPTOP_HP),
SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV2000T/DV3000T", CXT5047_LAPTOP),
SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2000Z", CXT5047_LAPTOP),
- SND_PCI_QUIRK(0x103c, 0x30a5, "HP DV5200T/DV8000T", CXT5047_LAPTOP_HP),
SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P100", CXT5047_LAPTOP_EAPD),
{}
};
@@ -1324,10 +1467,260 @@ static int patch_cxt5047(struct hda_codec *codec)
return 0;
}
+/* Conexant 5051 specific */
+static hda_nid_t cxt5051_dac_nids[1] = { 0x10 };
+static hda_nid_t cxt5051_adc_nids[2] = { 0x14, 0x15 };
+#define CXT5051_SPDIF_OUT 0x1C
+#define CXT5051_PORTB_EVENT 0x38
+#define CXT5051_PORTC_EVENT 0x39
+
+static struct hda_channel_mode cxt5051_modes[1] = {
+ { 2, NULL },
+};
+
+static void cxt5051_update_speaker(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ unsigned int pinctl;
+ pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0;
+ snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ pinctl);
+}
+
+/* turn on/off EAPD (+ mute HP) as a master switch */
+static int cxt5051_hp_master_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ if (!cxt_eapd_put(kcontrol, ucontrol))
+ return 0;
+ cxt5051_update_speaker(codec);
+ return 1;
+}
+
+/* toggle input of built-in and mic jack appropriately */
+static void cxt5051_portb_automic(struct hda_codec *codec)
+{
+ unsigned int present;
+
+ present = snd_hda_codec_read(codec, 0x17, 0,
+ AC_VERB_GET_PIN_SENSE, 0) &
+ AC_PINSENSE_PRESENCE;
+ snd_hda_codec_write(codec, 0x14, 0,
+ AC_VERB_SET_CONNECT_SEL,
+ present ? 0x01 : 0x00);
+}
+
+/* switch the current ADC according to the jack state */
+static void cxt5051_portc_automic(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ unsigned int present;
+ hda_nid_t new_adc;
+
+ present = snd_hda_codec_read(codec, 0x18, 0,
+ AC_VERB_GET_PIN_SENSE, 0) &
+ AC_PINSENSE_PRESENCE;
+ if (present)
+ spec->cur_adc_idx = 1;
+ else
+ spec->cur_adc_idx = 0;
+ new_adc = spec->adc_nids[spec->cur_adc_idx];
+ if (spec->cur_adc && spec->cur_adc != new_adc) {
+ /* stream is running, let's swap the current ADC */
+ snd_hda_codec_setup_stream(codec, spec->cur_adc, 0, 0, 0);
+ spec->cur_adc = new_adc;
+ snd_hda_codec_setup_stream(codec, new_adc,
+ spec->cur_adc_stream_tag, 0,
+ spec->cur_adc_format);
+ }
+}
+
+/* mute internal speaker if HP is plugged */
+static void cxt5051_hp_automute(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+
+ spec->hp_present = snd_hda_codec_read(codec, 0x16, 0,
+ AC_VERB_GET_PIN_SENSE, 0) &
+ AC_PINSENSE_PRESENCE;
+ cxt5051_update_speaker(codec);
+}
+
+/* unsolicited event for HP jack sensing */
+static void cxt5051_hp_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ switch (res >> 26) {
+ case CONEXANT_HP_EVENT:
+ cxt5051_hp_automute(codec);
+ break;
+ case CXT5051_PORTB_EVENT:
+ cxt5051_portb_automic(codec);
+ break;
+ case CXT5051_PORTC_EVENT:
+ cxt5051_portc_automic(codec);
+ break;
+ }
+}
+
+static struct snd_kcontrol_new cxt5051_mixers[] = {
+ HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT),
+ HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT),
+ HDA_CODEC_VOLUME("External Mic Volume", 0x14, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("External Mic Switch", 0x14, 0x01, HDA_INPUT),
+ HDA_CODEC_VOLUME("Docking Mic Volume", 0x15, 0x00, HDA_INPUT),
+ HDA_CODEC_MUTE("Docking Mic Switch", 0x15, 0x00, HDA_INPUT),
+ HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = cxt_eapd_info,
+ .get = cxt_eapd_get,
+ .put = cxt5051_hp_master_sw_put,
+ .private_value = 0x1a,
+ },
+
+ {}
+};
+
+static struct snd_kcontrol_new cxt5051_hp_mixers[] = {
+ HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT),
+ HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT),
+ HDA_CODEC_VOLUME("External Mic Volume", 0x15, 0x00, HDA_INPUT),
+ HDA_CODEC_MUTE("External Mic Switch", 0x15, 0x00, HDA_INPUT),
+ HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = cxt_eapd_info,
+ .get = cxt_eapd_get,
+ .put = cxt5051_hp_master_sw_put,
+ .private_value = 0x1a,
+ },
+
+ {}
+};
+
+static struct hda_verb cxt5051_init_verbs[] = {
+ /* Line in, Mic */
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
+ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
+ /* SPK */
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
+ /* HP, Amp */
+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x16, AC_VERB_SET_CONNECT_SEL, 0x00},
+ /* DAC1 */
+ {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ /* Record selector: Int mic */
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
+ /* SPDIF route: PCM */
+ {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0},
+ /* EAPD */
+ {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
+ {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
+ {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTB_EVENT},
+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTC_EVENT},
+ { } /* end */
+};
+
+/* initialize jack-sensing, too */
+static int cxt5051_init(struct hda_codec *codec)
+{
+ conexant_init(codec);
+ if (codec->patch_ops.unsol_event) {
+ cxt5051_hp_automute(codec);
+ cxt5051_portb_automic(codec);
+ cxt5051_portc_automic(codec);
+ }
+ return 0;
+}
+
+
+enum {
+ CXT5051_LAPTOP, /* Laptops w/ EAPD support */
+ CXT5051_HP, /* no docking */
+ CXT5051_MODELS
+};
+
+static const char *cxt5051_models[CXT5051_MODELS] = {
+ [CXT5051_LAPTOP] = "laptop",
+ [CXT5051_HP] = "hp",
+};
+
+static struct snd_pci_quirk cxt5051_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board",
+ CXT5051_LAPTOP),
+ SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP),
+ {}
+};
+
+static int patch_cxt5051(struct hda_codec *codec)
+{
+ struct conexant_spec *spec;
+ int board_config;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ mutex_init(&spec->amp_mutex);
+ codec->spec = spec;
+
+ codec->patch_ops = conexant_patch_ops;
+ codec->patch_ops.init = cxt5051_init;
+
+ spec->multiout.max_channels = 2;
+ spec->multiout.num_dacs = ARRAY_SIZE(cxt5051_dac_nids);
+ spec->multiout.dac_nids = cxt5051_dac_nids;
+ spec->multiout.dig_out_nid = CXT5051_SPDIF_OUT;
+ spec->num_adc_nids = 1; /* not 2; via auto-mic switch */
+ spec->adc_nids = cxt5051_adc_nids;
+ spec->num_mixers = 1;
+ spec->mixers[0] = cxt5051_mixers;
+ spec->num_init_verbs = 1;
+ spec->init_verbs[0] = cxt5051_init_verbs;
+ spec->spdif_route = 0;
+ spec->num_channel_mode = ARRAY_SIZE(cxt5051_modes);
+ spec->channel_mode = cxt5051_modes;
+ spec->cur_adc = 0;
+ spec->cur_adc_idx = 0;
+
+ board_config = snd_hda_check_board_config(codec, CXT5051_MODELS,
+ cxt5051_models,
+ cxt5051_cfg_tbl);
+ switch (board_config) {
+ case CXT5051_HP:
+ codec->patch_ops.unsol_event = cxt5051_hp_unsol_event;
+ spec->mixers[0] = cxt5051_hp_mixers;
+ break;
+ default:
+ case CXT5051_LAPTOP:
+ codec->patch_ops.unsol_event = cxt5051_hp_unsol_event;
+ break;
+ }
+
+ return 0;
+}
+
+
+/*
+ */
+
struct hda_codec_preset snd_hda_preset_conexant[] = {
{ .id = 0x14f15045, .name = "CX20549 (Venice)",
.patch = patch_cxt5045 },
{ .id = 0x14f15047, .name = "CX20551 (Waikiki)",
.patch = patch_cxt5047 },
+ { .id = 0x14f15051, .name = "CX20561 (Hermosa)",
+ .patch = patch_cxt5051 },
{} /* terminator */
};
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 1c502789cc1..586d98f1b63 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -23,7 +23,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
@@ -92,9 +91,12 @@ enum {
ALC262_HP_BPC,
ALC262_HP_BPC_D7000_WL,
ALC262_HP_BPC_D7000_WF,
+ ALC262_HP_TC_T5735,
+ ALC262_HP_RP5700,
ALC262_BENQ_ED8,
ALC262_SONY_ASSAMD,
ALC262_BENQ_T31,
+ ALC262_ULTRA,
ALC262_AUTO,
ALC262_MODEL_LAST /* last tag */
};
@@ -104,10 +106,21 @@ enum {
ALC268_3ST,
ALC268_TOSHIBA,
ALC268_ACER,
+ ALC268_DELL,
+#ifdef CONFIG_SND_DEBUG
+ ALC268_TEST,
+#endif
ALC268_AUTO,
ALC268_MODEL_LAST /* last tag */
};
+/* ALC269 models */
+enum {
+ ALC269_BASIC,
+ ALC269_AUTO,
+ ALC269_MODEL_LAST /* last tag */
+};
+
/* ALC861 models */
enum {
ALC861_3ST,
@@ -144,6 +157,7 @@ enum {
ALC662_5ST_DIG,
ALC662_LENOVO_101E,
ALC662_ASUS_EEEPC_P701,
+ ALC662_ASUS_EEEPC_EP20,
ALC662_AUTO,
ALC662_MODEL_LAST,
};
@@ -183,6 +197,8 @@ enum {
ALC883_HAIER_W66,
ALC888_6ST_HP,
ALC888_3ST_HP,
+ ALC888_6ST_DELL,
+ ALC883_MITAC,
ALC883_AUTO,
ALC883_MODEL_LAST,
};
@@ -204,6 +220,8 @@ struct alc_spec {
char *stream_name_analog; /* analog PCM stream */
struct hda_pcm_stream *stream_analog_playback;
struct hda_pcm_stream *stream_analog_capture;
+ struct hda_pcm_stream *stream_analog_alt_playback;
+ struct hda_pcm_stream *stream_analog_alt_capture;
char *stream_name_digital; /* digital PCM stream */
struct hda_pcm_stream *stream_digital_playback;
@@ -214,6 +232,7 @@ struct alc_spec {
* max_channels, dacs must be set
* dig_out_nid and hp_nid are optional
*/
+ hda_nid_t alt_dac_nid;
/* capture */
unsigned int num_adc_nids;
@@ -247,7 +266,11 @@ struct alc_spec {
/* for pin sensing */
unsigned int sense_updated: 1;
unsigned int jack_present: 1;
+ unsigned int master_sw: 1;
+ /* for virtual master */
+ hda_nid_t vmaster_nid;
+ u32 vmaster_tlv[4];
#ifdef CONFIG_SND_HDA_POWER_SAVE
struct hda_loopback_check loopback;
#endif
@@ -562,7 +585,7 @@ static int alc_spdif_ctrl_get(struct snd_kcontrol *kcontrol,
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);
+ AC_VERB_GET_DIGI_CONVERT_1, 0x00);
*valp = (val & mask) != 0;
return 0;
@@ -576,7 +599,7 @@ static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol,
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,
+ AC_VERB_GET_DIGI_CONVERT_1,
0x00);
/* Set/unset the masked control bit(s) as needed */
@@ -598,6 +621,59 @@ static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol,
.private_value = nid | (mask<<16) }
#endif /* CONFIG_SND_DEBUG */
+/* A switch control to allow the enabling EAPD digital outputs on the ALC26x.
+ * Again, this is only used in the ALC26x test models to help identify when
+ * the EAPD line must be asserted for features to work.
+ */
+#ifdef CONFIG_SND_DEBUG
+#define alc_eapd_ctrl_info snd_ctl_boolean_mono_info
+
+static int alc_eapd_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_EAPD_BTLENABLE, 0x00);
+
+ *valp = (val & mask) != 0;
+ return 0;
+}
+
+static int alc_eapd_ctrl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ 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_EAPD_BTLENABLE,
+ 0x00);
+
+ /* Set/unset the masked control bit(s) as needed */
+ change = (!val ? 0 : mask) != (ctrl_data & mask);
+ if (!val)
+ ctrl_data &= ~mask;
+ else
+ ctrl_data |= mask;
+ snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE,
+ ctrl_data);
+
+ return change;
+}
+
+#define ALC_EAPD_CTRL_SWITCH(xname, nid, mask) \
+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
+ .info = alc_eapd_ctrl_info, \
+ .get = alc_eapd_ctrl_get, \
+ .put = alc_eapd_ctrl_put, \
+ .private_value = nid | (mask<<16) }
+#endif /* CONFIG_SND_DEBUG */
+
/*
* set up from the preset table
*/
@@ -739,7 +815,7 @@ static void alc_subsystem_id(struct hda_codec *codec,
/* check sum */
tmp = 0;
for (i = 1; i < 16; i++) {
- if ((ass >> i) && 1)
+ if ((ass >> i) & 1)
tmp++;
}
if (((ass >> 16) & 0xf) != tmp)
@@ -828,10 +904,10 @@ do_sku:
break;
}
- /* is laptop and enable the function "Mute internal speaker
+ /* is laptop or Desktop and enable the function "Mute internal speaker
* when the external headphone out jack is plugged"
*/
- if (!(ass & 0x4) || !(ass & 0x8000))
+ if (!(ass & 0x8000))
return;
/*
* 10~8 : Jack location
@@ -841,9 +917,9 @@ do_sku:
* when the external headphone out jack is plugged"
*/
if (!spec->autocfg.speaker_pins[0]) {
- if (spec->multiout.dac_nids[0])
+ if (spec->autocfg.line_out_pins[0])
spec->autocfg.speaker_pins[0] =
- spec->multiout.dac_nids[0];
+ spec->autocfg.line_out_pins[0];
else
return;
}
@@ -1009,7 +1085,6 @@ static struct snd_kcontrol_new alc880_capture_mixer[] = {
.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",
@@ -1031,7 +1106,6 @@ static struct snd_kcontrol_new alc880_capture_alt_mixer[] = {
.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",
@@ -1226,7 +1300,6 @@ static struct snd_kcontrol_new alc880_z71v_mixer[] = {
};
-/* FIXME! */
/*
* ALC880 F1734 model
*
@@ -1242,8 +1315,8 @@ static hda_nid_t alc880_f1734_dac_nids[1] = {
static struct snd_kcontrol_new alc880_f1734_mixer[] = {
HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Internal Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, 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("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
@@ -1252,7 +1325,6 @@ static struct snd_kcontrol_new alc880_f1734_mixer[] = {
};
-/* FIXME! */
/*
* ALC880 ASUS model
*
@@ -1289,7 +1361,6 @@ static struct snd_kcontrol_new alc880_asus_mixer[] = {
{ } /* end */
};
-/* FIXME! */
/*
* ALC880 ASUS W1V model
*
@@ -1327,7 +1398,6 @@ static struct snd_kcontrol_new alc880_tcl_s700_mixer[] = {
.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",
@@ -1341,10 +1411,10 @@ static struct snd_kcontrol_new alc880_tcl_s700_mixer[] = {
/* Uniwill */
static struct snd_kcontrol_new alc880_uniwill_mixer[] = {
- HDA_CODEC_VOLUME("HPhone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("HPhone Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("iSpeaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("iSpeaker Playback Switch", 0x0d, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Speaker 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),
@@ -1384,16 +1454,49 @@ static struct snd_kcontrol_new alc880_fujitsu_mixer[] = {
};
static struct snd_kcontrol_new alc880_uniwill_p53_mixer[] = {
- HDA_CODEC_VOLUME("HPhone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("HPhone Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("iSpeaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("iSpeaker Playback Switch", 0x0d, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
{ } /* end */
};
/*
+ * virtual master controls
+ */
+
+/*
+ * slave controls for virtual master
+ */
+static const char *alc_slave_vols[] = {
+ "Front Playback Volume",
+ "Surround Playback Volume",
+ "Center Playback Volume",
+ "LFE Playback Volume",
+ "Side Playback Volume",
+ "Headphone Playback Volume",
+ "Speaker Playback Volume",
+ "Mono Playback Volume",
+ "Line-Out Playback Volume",
+ NULL,
+};
+
+static const char *alc_slave_sws[] = {
+ "Front Playback Switch",
+ "Surround Playback Switch",
+ "Center Playback Switch",
+ "LFE Playback Switch",
+ "Side Playback Switch",
+ "Headphone Playback Switch",
+ "Speaker Playback Switch",
+ "Mono Playback Switch",
+ "IEC958 Playback Switch",
+ NULL,
+};
+
+/*
* build control elements
*/
static int alc_build_controls(struct hda_codec *codec)
@@ -1419,6 +1522,23 @@ static int alc_build_controls(struct hda_codec *codec)
if (err < 0)
return err;
}
+
+ /* if we have no master control, let's create it */
+ if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
+ snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
+ HDA_OUTPUT, spec->vmaster_tlv);
+ err = snd_hda_add_vmaster(codec, "Master Playback Volume",
+ spec->vmaster_tlv, alc_slave_vols);
+ if (err < 0)
+ return err;
+ }
+ if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
+ err = snd_hda_add_vmaster(codec, "Master Playback Switch",
+ NULL, alc_slave_sws);
+ if (err < 0)
+ return err;
+ }
+
return 0;
}
@@ -1790,7 +1910,6 @@ static void alc880_uniwill_p53_unsol_event(struct hda_codec *codec,
alc880_uniwill_p53_dcvol_automute(codec);
}
-/* FIXME! */
/*
* F1734 pin configuration:
* HP = 0x14, speaker-out = 0x15, mic = 0x18
@@ -1819,7 +1938,6 @@ static struct hda_verb alc880_pin_f1734_init_verbs[] = {
{ }
};
-/* FIXME! */
/*
* ASUS pin configuration:
* HP/front = 0x14, surr = 0x15, clfe = 0x16, mic = 0x18, line = 0x1a
@@ -1966,9 +2084,8 @@ static struct hda_channel_mode alc880_lg_ch_modes[3] = {
};
static struct snd_kcontrol_new alc880_lg_mixer[] = {
- /* FIXME: it's not really "master" but front channels */
- HDA_CODEC_VOLUME("Master Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Master Playback Switch", 0x0f, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Front Playback Switch", 0x0f, 2, HDA_INPUT),
HDA_CODEC_VOLUME("Surround Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
HDA_BIND_MUTE("Surround Playback Switch", 0x0c, 2, HDA_INPUT),
HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT),
@@ -2256,7 +2373,7 @@ static int alc880_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
/*
* Analog capture
*/
-static int alc880_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+static int alc880_alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
unsigned int format,
@@ -2264,18 +2381,18 @@ static int alc880_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
{
struct alc_spec *spec = codec->spec;
- snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
+ snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1],
stream_tag, 0, format);
return 0;
}
-static int alc880_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+static int alc880_alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
struct alc_spec *spec = codec->spec;
- snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
+ snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1],
0, 0, 0);
return 0;
}
@@ -2296,13 +2413,27 @@ static struct hda_pcm_stream alc880_pcm_analog_playback = {
};
static struct hda_pcm_stream alc880_pcm_analog_capture = {
- .substreams = 2,
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in alc_build_pcms */
+};
+
+static struct hda_pcm_stream alc880_pcm_analog_alt_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in alc_build_pcms */
+};
+
+static struct hda_pcm_stream alc880_pcm_analog_alt_capture = {
+ .substreams = 2, /* can be overridden */
.channels_min = 2,
.channels_max = 2,
/* NID is set in alc_build_pcms */
.ops = {
- .prepare = alc880_capture_pcm_prepare,
- .cleanup = alc880_capture_pcm_cleanup
+ .prepare = alc880_alt_capture_pcm_prepare,
+ .cleanup = alc880_alt_capture_pcm_cleanup
},
};
@@ -2326,7 +2457,7 @@ static struct hda_pcm_stream alc880_pcm_digital_capture = {
};
/* Used by alc_build_pcms to flag that a PCM has no playback stream */
-static struct hda_pcm_stream alc_pcm_null_playback = {
+static struct hda_pcm_stream alc_pcm_null_stream = {
.substreams = 0,
.channels_min = 0,
.channels_max = 0,
@@ -2383,17 +2514,32 @@ static int alc_build_pcms(struct hda_codec *codec)
* 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) {
+ if ((spec->alt_dac_nid && spec->stream_analog_alt_playback) ||
+ (spec->num_adc_nids > 1 && spec->stream_analog_alt_capture)) {
codec->num_pcms = 3;
info = spec->pcm_rec + 2;
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];
+ if (spec->alt_dac_nid) {
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
+ *spec->stream_analog_alt_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
+ spec->alt_dac_nid;
+ } else {
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
+ alc_pcm_null_stream;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0;
+ }
+ if (spec->num_adc_nids > 1) {
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+ *spec->stream_analog_alt_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
+ spec->adc_nids[1];
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
+ spec->num_adc_nids - 1;
+ } else {
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+ alc_pcm_null_stream;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 0;
}
}
@@ -2723,23 +2869,17 @@ static const char *alc880_models[ALC880_MODEL_LAST] = {
};
static struct snd_pci_quirk alc880_cfg_tbl[] = {
- /* Broken BIOS configuration */
- SND_PCI_QUIRK(0x2668, 0x8086, NULL, ALC880_6ST_DIG),
- SND_PCI_QUIRK(0x8086, 0x2668, NULL, ALC880_6ST_DIG),
-
+ SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_W810),
SND_PCI_QUIRK(0x1019, 0xa880, "ECS", ALC880_5ST_DIG),
SND_PCI_QUIRK(0x1019, 0xa884, "Acer APFV", ALC880_6ST),
- SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_W810),
SND_PCI_QUIRK(0x1025, 0x0070, "ULI", ALC880_3ST_DIG),
SND_PCI_QUIRK(0x1025, 0x0077, "ULI", ALC880_6ST_DIG),
SND_PCI_QUIRK(0x1025, 0x0078, "ULI", ALC880_6ST_DIG),
SND_PCI_QUIRK(0x1025, 0x0087, "ULI", ALC880_6ST_DIG),
SND_PCI_QUIRK(0x1025, 0xe309, "ULI", ALC880_3ST_DIG),
SND_PCI_QUIRK(0x1025, 0xe310, "ULI", ALC880_3ST),
-
SND_PCI_QUIRK(0x1039, 0x1234, NULL, ALC880_6ST_DIG),
SND_PCI_QUIRK(0x103c, 0x2a09, "HP", ALC880_5ST),
-
SND_PCI_QUIRK(0x1043, 0x10b3, "ASUS W1V", ALC880_ASUS_W1V),
SND_PCI_QUIRK(0x1043, 0x10c2, "ASUS W6A", ALC880_ASUS_DIG),
SND_PCI_QUIRK(0x1043, 0x10c3, "ASUS Wxx", ALC880_ASUS_DIG),
@@ -2754,54 +2894,50 @@ static struct snd_pci_quirk alc880_cfg_tbl[] = {
SND_PCI_QUIRK(0x1043, 0x8181, "ASUS P4GPL", ALC880_ASUS_DIG),
SND_PCI_QUIRK(0x1043, 0x8196, "ASUS P5GD1", ALC880_6ST),
SND_PCI_QUIRK(0x1043, 0x81b4, "ASUS", ALC880_6ST),
- SND_PCI_QUIRK(0x1043, 0, "ASUS", ALC880_ASUS),
-
- SND_PCI_QUIRK(0x104d, 0x81d6, "Sony", ALC880_3ST),
+ SND_PCI_QUIRK(0x1043, 0, "ASUS", ALC880_ASUS), /* default ASUS */
SND_PCI_QUIRK(0x104d, 0x81a0, "Sony", ALC880_3ST),
+ SND_PCI_QUIRK(0x104d, 0x81d6, "Sony", ALC880_3ST),
+ SND_PCI_QUIRK(0x107b, 0x3032, "Gateway", ALC880_5ST),
SND_PCI_QUIRK(0x107b, 0x3033, "Gateway", ALC880_5ST),
SND_PCI_QUIRK(0x107b, 0x4039, "Gateway", ALC880_5ST),
- SND_PCI_QUIRK(0x107b, 0x3032, "Gateway", ALC880_5ST),
- SND_PCI_QUIRK(0x1558, 0x0520, "Clevo m520G", ALC880_CLEVO),
- SND_PCI_QUIRK(0x1558, 0x0660, "Clevo m655n", ALC880_CLEVO),
- SND_PCI_QUIRK(0x1565, 0x8202, "Biostar", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_W810),
- SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x19db, 0x4188, "TCL S700", ALC880_TCL_S700),
- SND_PCI_QUIRK(0xa0a0, 0x0560, "AOpen i915GMm-HFS", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0xe803, 0x1019, NULL, ALC880_6ST_DIG),
SND_PCI_QUIRK(0x1297, 0xc790, "Shuttle ST20G5", ALC880_6ST_DIG),
SND_PCI_QUIRK(0x1458, 0xa102, "Gigabyte K8", ALC880_6ST_DIG),
SND_PCI_QUIRK(0x1462, 0x1150, "MSI", ALC880_6ST_DIG),
SND_PCI_QUIRK(0x1509, 0x925d, "FIC P4M", ALC880_6ST_DIG),
+ SND_PCI_QUIRK(0x1558, 0x0520, "Clevo m520G", ALC880_CLEVO),
+ SND_PCI_QUIRK(0x1558, 0x0660, "Clevo m655n", ALC880_CLEVO),
SND_PCI_QUIRK(0x1558, 0x5401, "ASUS", ALC880_ASUS_DIG2),
-
+ SND_PCI_QUIRK(0x1565, 0x8202, "Biostar", ALC880_5ST_DIG),
SND_PCI_QUIRK(0x1584, 0x9050, "Uniwill", ALC880_UNIWILL_DIG),
+ SND_PCI_QUIRK(0x1584, 0x9054, "Uniwlll", ALC880_F1734),
SND_PCI_QUIRK(0x1584, 0x9070, "Uniwill", ALC880_UNIWILL),
SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_UNIWILL_P53),
- SND_PCI_QUIRK(0x1584, 0x9054, "Uniwlll", ALC880_F1734),
-
+ SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_W810),
+ SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_5ST_DIG),
SND_PCI_QUIRK(0x1695, 0x4012, "EPox EP-5LDA", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x1734, 0x10ac, "FSC", ALC880_UNIWILL),
SND_PCI_QUIRK(0x1734, 0x107c, "FSC F1734", ALC880_F1734),
+ SND_PCI_QUIRK(0x1734, 0x10ac, "FSC", ALC880_UNIWILL),
SND_PCI_QUIRK(0x1734, 0x10b0, "Fujitsu", ALC880_FUJITSU),
-
+ SND_PCI_QUIRK(0x1854, 0x0018, "LG LW20", ALC880_LG_LW),
SND_PCI_QUIRK(0x1854, 0x003b, "LG", ALC880_LG),
SND_PCI_QUIRK(0x1854, 0x0068, "LG w1", ALC880_LG),
- SND_PCI_QUIRK(0x1854, 0x0018, "LG LW20", ALC880_LG_LW),
SND_PCI_QUIRK(0x1854, 0x0077, "LG LW25", ALC880_LG_LW),
-
- SND_PCI_QUIRK(0x8086, 0xe308, "Intel mobo", ALC880_3ST_DIG),
- SND_PCI_QUIRK(0x8086, 0xe305, "Intel mobo", ALC880_3ST_DIG),
- SND_PCI_QUIRK(0x8086, 0xd402, "Intel mobo", ALC880_3ST_DIG),
+ SND_PCI_QUIRK(0x19db, 0x4188, "TCL S700", ALC880_TCL_S700),
+ SND_PCI_QUIRK(0x2668, 0x8086, NULL, ALC880_6ST_DIG), /* broken BIOS */
+ SND_PCI_QUIRK(0x8086, 0x2668, NULL, ALC880_6ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xa100, "Intel mobo", ALC880_5ST_DIG),
SND_PCI_QUIRK(0x8086, 0xd400, "Intel mobo", ALC880_5ST_DIG),
SND_PCI_QUIRK(0x8086, 0xd401, "Intel mobo", ALC880_5ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xd402, "Intel mobo", ALC880_3ST_DIG),
SND_PCI_QUIRK(0x8086, 0xe224, "Intel mobo", ALC880_5ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xe305, "Intel mobo", ALC880_3ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xe308, "Intel mobo", ALC880_3ST_DIG),
SND_PCI_QUIRK(0x8086, 0xe400, "Intel mobo", ALC880_5ST_DIG),
SND_PCI_QUIRK(0x8086, 0xe401, "Intel mobo", ALC880_5ST_DIG),
SND_PCI_QUIRK(0x8086, 0xe402, "Intel mobo", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x8086, 0xa100, "Intel mobo", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x8086, 0, "Intel mobo", ALC880_3ST),
-
+ SND_PCI_QUIRK(0x8086, 0, "Intel mobo", ALC880_3ST), /* default Intel */
+ SND_PCI_QUIRK(0xa0a0, 0x0560, "AOpen i915GMm-HFS", ALC880_5ST_DIG),
+ SND_PCI_QUIRK(0xe803, 0x1019, NULL, ALC880_6ST_DIG),
{}
};
@@ -3511,6 +3647,7 @@ static int patch_alc880(struct hda_codec *codec)
spec->stream_name_analog = "ALC880 Analog";
spec->stream_analog_playback = &alc880_pcm_analog_playback;
spec->stream_analog_capture = &alc880_pcm_analog_capture;
+ spec->stream_analog_alt_capture = &alc880_pcm_analog_alt_capture;
spec->stream_name_digital = "ALC880 Digital";
spec->stream_digital_playback = &alc880_pcm_digital_playback;
@@ -3535,6 +3672,8 @@ static int patch_alc880(struct hda_codec *codec)
}
}
+ spec->vmaster_nid = 0x0c;
+
codec->patch_ops = alc_patch_ops;
if (board_config == ALC880_AUTO)
spec->init_hook = alc880_auto_init;
@@ -3691,18 +3830,135 @@ static struct snd_kcontrol_new alc260_pc_beep_mixer[] = {
{ } /* end */
};
+/* update HP, line and mono out pins according to the master switch */
+static void alc260_hp_master_update(struct hda_codec *codec,
+ hda_nid_t hp, hda_nid_t line,
+ hda_nid_t mono)
+{
+ struct alc_spec *spec = codec->spec;
+ unsigned int val = spec->master_sw ? PIN_HP : 0;
+ /* change HP and line-out pins */
+ snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ val);
+ snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ val);
+ /* mono (speaker) depending on the HP jack sense */
+ val = (val && !spec->jack_present) ? PIN_OUT : 0;
+ snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ val);
+}
+
+static int alc260_hp_master_sw_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;
+ *ucontrol->value.integer.value = spec->master_sw;
+ return 0;
+}
+
+static int alc260_hp_master_sw_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;
+ int val = !!*ucontrol->value.integer.value;
+ hda_nid_t hp, line, mono;
+
+ if (val == spec->master_sw)
+ return 0;
+ spec->master_sw = val;
+ hp = (kcontrol->private_value >> 16) & 0xff;
+ line = (kcontrol->private_value >> 8) & 0xff;
+ mono = kcontrol->private_value & 0xff;
+ alc260_hp_master_update(codec, hp, line, mono);
+ return 1;
+}
+
+static struct snd_kcontrol_new alc260_hp_output_mixer[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = alc260_hp_master_sw_get,
+ .put = alc260_hp_master_sw_put,
+ .private_value = (0x0f << 16) | (0x10 << 8) | 0x11
+ },
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x08, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Front Playback Switch", 0x08, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x09, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Headphone Playback Switch", 0x09, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0a, 1, 0x0,
+ HDA_OUTPUT),
+ HDA_BIND_MUTE_MONO("Speaker Playback Switch", 0x0a, 1, 2, HDA_INPUT),
+ { } /* end */
+};
+
+static struct hda_verb alc260_hp_unsol_verbs[] = {
+ {0x10, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ {},
+};
+
+static void alc260_hp_automute(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ unsigned int present;
+
+ present = snd_hda_codec_read(codec, 0x10, 0,
+ AC_VERB_GET_PIN_SENSE, 0);
+ spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0;
+ alc260_hp_master_update(codec, 0x0f, 0x10, 0x11);
+}
+
+static void alc260_hp_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+ if ((res >> 26) == ALC880_HP_EVENT)
+ alc260_hp_automute(codec);
+}
+
static struct snd_kcontrol_new alc260_hp_3013_mixer[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = alc260_hp_master_sw_get,
+ .put = alc260_hp_master_sw_put,
+ .private_value = (0x10 << 16) | (0x15 << 8) | 0x11
+ },
HDA_CODEC_VOLUME("Front Playback Volume", 0x09, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Front Playback Switch", 0x10, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Aux-In Playback Volume", 0x07, 0x06, HDA_INPUT),
HDA_CODEC_MUTE("Aux-In Playback Switch", 0x07, 0x06, HDA_INPUT),
HDA_CODEC_VOLUME("Headphone Playback Volume", 0x08, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("iSpeaker Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("iSpeaker Playback Switch", 0x11, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x11, 1, 0x0, HDA_OUTPUT),
{ } /* end */
};
+static struct hda_verb alc260_hp_3013_unsol_verbs[] = {
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ {},
+};
+
+static void alc260_hp_3013_automute(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ unsigned int present;
+
+ present = snd_hda_codec_read(codec, 0x15, 0,
+ AC_VERB_GET_PIN_SENSE, 0);
+ spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0;
+ alc260_hp_master_update(codec, 0x10, 0x15, 0x11);
+}
+
+static void alc260_hp_3013_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ if ((res >> 26) == ALC880_HP_EVENT)
+ alc260_hp_3013_automute(codec);
+}
+
/* Fujitsu S702x series laptops. ALC260 pin usage: Mic/Line jack = 0x12,
* HP jack = 0x14, CD audio = 0x16, internal speaker = 0x10.
*/
@@ -3812,7 +4068,6 @@ static struct snd_kcontrol_new alc260_capture_mixer[] = {
.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",
@@ -3831,7 +4086,6 @@ static struct snd_kcontrol_new alc260_capture_alt_mixer[] = {
.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",
@@ -4332,6 +4586,12 @@ static struct snd_kcontrol_new alc260_test_mixer[] = {
ALC_SPDIF_CTRL_SWITCH("SPDIF Playback Switch", 0x03, 0x01),
ALC_SPDIF_CTRL_SWITCH("SPDIF Capture Switch", 0x06, 0x01),
+ /* A switch allowing EAPD to be enabled. Some laptops seem to use
+ * this output to turn on an external amplifier.
+ */
+ ALC_EAPD_CTRL_SWITCH("LINE-OUT EAPD Enable Switch", 0x0f, 0x02),
+ ALC_EAPD_CTRL_SWITCH("HP-OUT EAPD Enable Switch", 0x10, 0x02),
+
{ } /* end */
};
static struct hda_verb alc260_test_init_verbs[] = {
@@ -4417,17 +4677,8 @@ static struct hda_verb alc260_test_init_verbs[] = {
};
#endif
-static struct hda_pcm_stream alc260_pcm_analog_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
-};
-
-static struct hda_pcm_stream alc260_pcm_analog_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
-};
+#define alc260_pcm_analog_playback alc880_pcm_analog_alt_playback
+#define alc260_pcm_analog_capture alc880_pcm_analog_capture
#define alc260_pcm_digital_playback alc880_pcm_digital_playback
#define alc260_pcm_digital_capture alc880_pcm_digital_capture
@@ -4744,8 +4995,8 @@ static struct snd_pci_quirk alc260_cfg_tbl[] = {
SND_PCI_QUIRK(0x104d, 0x81cd, "Sony VAIO", ALC260_BASIC),
SND_PCI_QUIRK(0x10cf, 0x1326, "Fujitsu S702X", ALC260_FUJITSU_S702X),
SND_PCI_QUIRK(0x152d, 0x0729, "CTL U553W", ALC260_BASIC),
- SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900", ALC260_WILL),
SND_PCI_QUIRK(0x161f, 0x2057, "Replacer 672V", ALC260_REPLACER_672V),
+ SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900", ALC260_WILL),
{}
};
@@ -4765,10 +5016,11 @@ static struct alc_config_preset alc260_presets[] = {
.input_mux = &alc260_capture_source,
},
[ALC260_HP] = {
- .mixers = { alc260_base_output_mixer,
+ .mixers = { alc260_hp_output_mixer,
alc260_input_mixer,
alc260_capture_alt_mixer },
- .init_verbs = { alc260_init_verbs },
+ .init_verbs = { alc260_init_verbs,
+ alc260_hp_unsol_verbs },
.num_dacs = ARRAY_SIZE(alc260_dac_nids),
.dac_nids = alc260_dac_nids,
.num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids),
@@ -4776,12 +5028,15 @@ static struct alc_config_preset alc260_presets[] = {
.num_channel_mode = ARRAY_SIZE(alc260_modes),
.channel_mode = alc260_modes,
.input_mux = &alc260_capture_source,
+ .unsol_event = alc260_hp_unsol_event,
+ .init_hook = alc260_hp_automute,
},
[ALC260_HP_3013] = {
.mixers = { alc260_hp_3013_mixer,
alc260_input_mixer,
alc260_capture_alt_mixer },
- .init_verbs = { alc260_hp_3013_init_verbs },
+ .init_verbs = { alc260_hp_3013_init_verbs,
+ alc260_hp_3013_unsol_verbs },
.num_dacs = ARRAY_SIZE(alc260_dac_nids),
.dac_nids = alc260_dac_nids,
.num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids),
@@ -4789,6 +5044,8 @@ static struct alc_config_preset alc260_presets[] = {
.num_channel_mode = ARRAY_SIZE(alc260_modes),
.channel_mode = alc260_modes,
.input_mux = &alc260_capture_source,
+ .unsol_event = alc260_hp_3013_unsol_event,
+ .init_hook = alc260_hp_3013_automute,
},
[ALC260_FUJITSU_S702X] = {
.mixers = { alc260_fujitsu_mixer,
@@ -4906,6 +5163,8 @@ static int patch_alc260(struct hda_codec *codec)
spec->stream_digital_playback = &alc260_pcm_digital_playback;
spec->stream_digital_capture = &alc260_pcm_digital_capture;
+ spec->vmaster_nid = 0x08;
+
codec->patch_ops = alc_patch_ops;
if (board_config == ALC260_AUTO)
spec->init_hook = alc260_auto_init;
@@ -5106,15 +5365,15 @@ static struct snd_kcontrol_new alc882_base_mixer[] = {
};
static struct snd_kcontrol_new alc885_mbp3_mixer[] = {
- HDA_CODEC_VOLUME("Master Volume", 0x0c, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE ("Master Switch", 0x0c, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE ("Speaker Switch", 0x14, 0x00, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Out Volume", 0x0d,0x00, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line In Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE ("Line In Playback Switch", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
+ HDA_BIND_MUTE ("Front Playback Switch", 0x0c, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE ("Speaker Playback Switch", 0x14, 0x00, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Line-Out Playback Volume", 0x0d, 0x00, HDA_OUTPUT),
+ 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, 0x00, HDA_INPUT),
HDA_CODEC_MUTE ("Mic Playback Switch", 0x0b, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Line In Boost", 0x1a, 0x00, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line Boost", 0x1a, 0x00, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x00, HDA_INPUT),
{ } /* end */
};
@@ -5679,7 +5938,6 @@ static struct snd_kcontrol_new alc882_capture_alt_mixer[] = {
.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",
@@ -5702,7 +5960,6 @@ static struct snd_kcontrol_new alc882_capture_mixer[] = {
.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",
@@ -5743,16 +6000,17 @@ static const char *alc882_models[ALC882_MODEL_LAST] = {
static struct snd_pci_quirk alc882_cfg_tbl[] = {
SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC882_6ST_DIG),
- SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC882_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC882_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x28fb, "Targa T8", ALC882_TARGA), /* MSI-1049 T8 */
- SND_PCI_QUIRK(0x161f, 0x2054, "Arima W820", ALC882_ARIMA),
SND_PCI_QUIRK(0x1043, 0x060d, "Asus A7J", ALC882_ASUS_A7J),
SND_PCI_QUIRK(0x1043, 0x1243, "Asus A7J", ALC882_ASUS_A7J),
SND_PCI_QUIRK(0x1043, 0x13c2, "Asus A7M", ALC882_ASUS_A7M),
+ SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_W2JC),
SND_PCI_QUIRK(0x1043, 0x817f, "Asus P5LD2", ALC882_6ST_DIG),
SND_PCI_QUIRK(0x1043, 0x81d8, "Asus P5WD", ALC882_6ST_DIG),
- SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_W2JC),
+ SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC882_6ST_DIG),
+ SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte P35 DS3R", ALC882_6ST_DIG),
+ SND_PCI_QUIRK(0x1462, 0x28fb, "Targa T8", ALC882_TARGA), /* MSI-1049 T8 */
+ SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC882_6ST_DIG),
+ SND_PCI_QUIRK(0x161f, 0x2054, "Arima W820", ALC882_ARIMA),
{}
};
@@ -5990,7 +6248,7 @@ static int alc_auto_add_mic_boost(struct hda_codec *codec)
hda_nid_t nid;
nid = spec->autocfg.input_pins[AUTO_PIN_MIC];
- if (nid) {
+ if (nid && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) {
err = add_control(spec, ALC_CTL_WIDGET_VOL,
"Mic Boost",
HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
@@ -5998,7 +6256,7 @@ static int alc_auto_add_mic_boost(struct hda_codec *codec)
return err;
}
nid = spec->autocfg.input_pins[AUTO_PIN_FRONT_MIC];
- if (nid) {
+ if (nid && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) {
err = add_control(spec, ALC_CTL_WIDGET_VOL,
"Front Mic Boost",
HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
@@ -6061,6 +6319,7 @@ static int patch_alc882(struct hda_codec *codec)
case 0x106b1000: /* iMac 24 */
board_config = ALC885_IMAC24;
break;
+ case 0x106b00a1: /* Macbook */
case 0x106b2c00: /* Macbook Pro rev3 */
board_config = ALC885_MBP3;
break;
@@ -6093,6 +6352,9 @@ static int patch_alc882(struct hda_codec *codec)
spec->stream_name_analog = "ALC882 Analog";
spec->stream_analog_playback = &alc882_pcm_analog_playback;
spec->stream_analog_capture = &alc882_pcm_analog_capture;
+ /* FIXME: setup DAC5 */
+ /*spec->stream_analog_alt_playback = &alc880_pcm_analog_alt_playback;*/
+ spec->stream_analog_alt_capture = &alc880_pcm_analog_alt_capture;
spec->stream_name_digital = "ALC882 Digital";
spec->stream_digital_playback = &alc882_pcm_digital_playback;
@@ -6117,6 +6379,8 @@ static int patch_alc882(struct hda_codec *codec)
}
}
+ spec->vmaster_nid = 0x0c;
+
codec->patch_ops = alc_patch_ops;
if (board_config == ALC882_AUTO)
spec->init_hook = alc882_auto_init;
@@ -6340,6 +6604,36 @@ static struct snd_kcontrol_new alc883_base_mixer[] = {
{ } /* end */
};
+static struct snd_kcontrol_new alc883_mitac_mixer[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 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", 0x15, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, 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_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, 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),
@@ -6508,8 +6802,8 @@ static struct snd_kcontrol_new alc883_tagra_2ch_mixer[] = {
static struct snd_kcontrol_new alc883_lenovo_101e_2ch_mixer[] = {
HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("iSpeaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("iSpeaker Playback Switch", 0x0d, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
@@ -6658,6 +6952,46 @@ static struct snd_kcontrol_new alc888_3st_hp_mixer[] = {
{ } /* end */
};
+static struct snd_kcontrol_new alc888_6st_dell_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", 0x0e, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Surround Playback Switch", 0x0e, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT),
+ HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 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_VOLUME("Mic Boost", 0x18, 0, 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_VOLUME("Front Mic Boost", 0x19, 0, 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_acer_aspire_mixer[] = {
HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
@@ -6772,6 +7106,67 @@ static struct hda_verb alc883_init_verbs[] = {
{ }
};
+/* toggle speaker-output according to the hp-jack state */
+static void alc883_mitac_hp_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+
+ present = snd_hda_codec_read(codec, 0x15, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+ snd_hda_codec_amp_stereo(codec, 0x17, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+}
+
+/* auto-toggle front mic */
+/*
+static void alc883_mitac_mic_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+ unsigned char bits;
+
+ present = snd_hda_codec_read(codec, 0x18, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ bits = present ? HDA_AMP_MUTE : 0;
+ snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits);
+}
+*/
+
+static void alc883_mitac_automute(struct hda_codec *codec)
+{
+ alc883_mitac_hp_automute(codec);
+ /* alc883_mitac_mic_automute(codec); */
+}
+
+static void alc883_mitac_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ switch (res >> 26) {
+ case ALC880_HP_EVENT:
+ alc883_mitac_hp_automute(codec);
+ break;
+ case ALC880_MIC_EVENT:
+ /* alc883_mitac_mic_automute(codec); */
+ break;
+ }
+}
+
+static struct hda_verb alc883_mitac_verbs[] = {
+ /* HP */
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ /* Subwoofer */
+ {0x17, AC_VERB_SET_CONNECT_SEL, 0x02},
+ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+
+ /* enable unsolicited event */
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+ /* {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, */
+
+ { } /* end */
+};
+
static struct hda_verb alc883_tagra_verbs[] = {
{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
@@ -6843,6 +7238,15 @@ static struct hda_verb alc888_3st_hp_verbs[] = {
{ }
};
+static struct hda_verb alc888_6st_dell_verbs[] = {
+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front: output 0 (0x0c) */
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x02}, /* Rear : output 1 (0x0e) */
+ {0x16, AC_VERB_SET_CONNECT_SEL, 0x01}, /* CLFE : output 2 (0x0d) */
+ {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, /* Side : output 3 (0x0f) */
+ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+ { }
+};
+
static struct hda_verb alc888_3st_hp_2ch_init[] = {
{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
@@ -7038,6 +7442,33 @@ static struct hda_verb alc883_acer_eapd_verbs[] = {
{ }
};
+static void alc888_6st_dell_front_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+
+ present = snd_hda_codec_read(codec, 0x1b, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+ snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+ snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+ snd_hda_codec_amp_stereo(codec, 0x17, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+}
+
+static void alc888_6st_dell_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ switch (res >> 26) {
+ case ALC880_HP_EVENT:
+ printk("hp_event\n");
+ alc888_6st_dell_front_automute(codec);
+ break;
+ }
+}
+
/*
* generic initialization of ADC, input mixers and output mixers
*/
@@ -7096,7 +7527,7 @@ static struct hda_verb alc883_auto_init_verbs[] = {
{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)},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
{ }
};
@@ -7111,7 +7542,6 @@ static struct snd_kcontrol_new alc883_capture_mixer[] = {
.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",
@@ -7130,6 +7560,7 @@ static struct snd_kcontrol_new alc883_capture_mixer[] = {
/* 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_analog_alt_capture alc880_pcm_analog_alt_capture
#define alc883_pcm_digital_playback alc880_pcm_digital_playback
#define alc883_pcm_digital_capture alc880_pcm_digital_capture
@@ -7154,53 +7585,58 @@ static const char *alc883_models[ALC883_MODEL_LAST] = {
[ALC883_HAIER_W66] = "haier-w66",
[ALC888_6ST_HP] = "6stack-hp",
[ALC888_3ST_HP] = "3stack-hp",
+ [ALC888_6ST_DELL] = "6stack-dell",
+ [ALC883_MITAC] = "mitac",
[ALC883_AUTO] = "auto",
};
static struct snd_pci_quirk alc883_cfg_tbl[] = {
SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC883_3ST_6ch_DIG),
+ SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_ACER_ASPIRE),
+ SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_ACER_ASPIRE),
+ SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_ACER_ASPIRE),
+ SND_PCI_QUIRK(0x1025, 0, "Acer laptop", ALC883_ACER), /* default Acer */
+ SND_PCI_QUIRK(0x1028, 0x020d, "Dell Inspiron 530", ALC888_6ST_DELL),
SND_PCI_QUIRK(0x103c, 0x2a3d, "HP Pavillion", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch),
- SND_PCI_QUIRK(0x1558, 0, "Clevo laptop", ALC883_LAPTOP_EAPD),
+ SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP),
+ SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP),
+ SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC888_6ST_HP),
+ SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG),
SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC883_6ST_DIG),
+ SND_PCI_QUIRK(0x1071, 0x8253, "Mitac 8252d", ALC883_MITAC),
+ SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD),
+ SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch),
SND_PCI_QUIRK(0x1458, 0xa002, "MSI", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x7250, "MSI", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG),
SND_PCI_QUIRK(0x1462, 0x0349, "MSI", ALC883_TARGA_2ch_DIG),
+ SND_PCI_QUIRK(0x1462, 0x040d, "MSI", ALC883_TARGA_2ch_DIG),
SND_PCI_QUIRK(0x1462, 0x0579, "MSI", ALC883_TARGA_2ch_DIG),
SND_PCI_QUIRK(0x1462, 0x3729, "MSI S420", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x3ef9, "MSI", ALC883_TARGA_DIG),
SND_PCI_QUIRK(0x1462, 0x3b7f, "MSI", ALC883_TARGA_2ch_DIG),
- SND_PCI_QUIRK(0x1462, 0x3fcc, "MSI", ALC883_TARGA_DIG),
+ SND_PCI_QUIRK(0x1462, 0x3ef9, "MSI", ALC883_TARGA_DIG),
SND_PCI_QUIRK(0x1462, 0x3fc1, "MSI", ALC883_TARGA_DIG),
SND_PCI_QUIRK(0x1462, 0x3fc3, "MSI", ALC883_TARGA_DIG),
+ SND_PCI_QUIRK(0x1462, 0x3fcc, "MSI", ALC883_TARGA_DIG),
SND_PCI_QUIRK(0x1462, 0x3fdf, "MSI", ALC883_TARGA_DIG),
SND_PCI_QUIRK(0x1462, 0x4314, "MSI", ALC883_TARGA_DIG),
SND_PCI_QUIRK(0x1462, 0x4319, "MSI", ALC883_TARGA_DIG),
SND_PCI_QUIRK(0x1462, 0x4324, "MSI", ALC883_TARGA_DIG),
+ SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG),
+ SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG),
+ SND_PCI_QUIRK(0x1462, 0x7250, "MSI", ALC883_6ST_DIG),
+ SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG),
+ SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG),
SND_PCI_QUIRK(0x1462, 0xa422, "MSI", ALC883_TARGA_2ch_DIG),
- SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_ACER_ASPIRE),
- SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_ACER_ASPIRE),
- SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_ACER_ASPIRE),
- SND_PCI_QUIRK(0x1025, 0, "Acer laptop", ALC883_ACER),
+ SND_PCI_QUIRK(0x147b, 0x1083, "Abit IP35-PRO", ALC883_6ST_DIG),
+ SND_PCI_QUIRK(0x1558, 0, "Clevo laptop", ALC883_LAPTOP_EAPD),
SND_PCI_QUIRK(0x15d9, 0x8780, "Supermicro PDSBA", ALC883_3ST_6ch),
SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_MEDION),
- SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC883_3ST_6ch),
SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo 101e", ALC883_LENOVO_101E_2ch),
- SND_PCI_QUIRK(0x17aa, 0x3bfd, "Lenovo NB0763", ALC883_LENOVO_NB0763),
SND_PCI_QUIRK(0x17aa, 0x2085, "Lenovo NB0763", ALC883_LENOVO_NB0763),
- SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC888_6ST_HP),
- SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP),
- SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP),
+ SND_PCI_QUIRK(0x17aa, 0x3bfc, "Lenovo NB0763", ALC883_LENOVO_NB0763),
+ SND_PCI_QUIRK(0x17aa, 0x3bfd, "Lenovo NB0763", ALC883_LENOVO_NB0763),
SND_PCI_QUIRK(0x17c0, 0x4071, "MEDION MD2", ALC883_MEDION_MD2),
SND_PCI_QUIRK(0x1991, 0x5625, "Haier W66", ALC883_HAIER_W66),
- SND_PCI_QUIRK(0x17aa, 0x3bfc, "Lenovo NB0763", ALC883_LENOVO_NB0763),
- SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG),
- SND_PCI_QUIRK(0x147b, 0x1083, "Abit IP35-PRO", ALC883_6ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC883_3ST_6ch),
{}
};
@@ -7435,6 +7871,34 @@ static struct alc_config_preset alc883_presets[] = {
.need_dac_fix = 1,
.input_mux = &alc883_capture_source,
},
+ [ALC888_6ST_DELL] = {
+ .mixers = { alc888_6st_dell_mixer, alc883_chmode_mixer },
+ .init_verbs = { alc883_init_verbs, alc888_6st_dell_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,
+ .unsol_event = alc888_6st_dell_unsol_event,
+ .init_hook = alc888_6st_dell_front_automute,
+ },
+ [ALC883_MITAC] = {
+ .mixers = { alc883_mitac_mixer },
+ .init_verbs = { alc883_init_verbs, alc883_mitac_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,
+ .unsol_event = alc883_mitac_unsol_event,
+ .init_hook = alc883_mitac_automute,
+ },
};
@@ -7582,6 +8046,7 @@ static int patch_alc883(struct hda_codec *codec)
spec->stream_name_analog = "ALC883 Analog";
spec->stream_analog_playback = &alc883_pcm_analog_playback;
spec->stream_analog_capture = &alc883_pcm_analog_capture;
+ spec->stream_analog_alt_capture = &alc883_pcm_analog_alt_capture;
spec->stream_name_digital = "ALC883 Digital";
spec->stream_digital_playback = &alc883_pcm_digital_playback;
@@ -7592,6 +8057,8 @@ static int patch_alc883(struct hda_codec *codec)
spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids);
}
+ spec->vmaster_nid = 0x0c;
+
codec->patch_ops = alc_patch_ops;
if (board_config == ALC883_AUTO)
spec->init_hook = alc883_auto_init;
@@ -7659,13 +8126,99 @@ static struct snd_kcontrol_new alc262_hippo1_mixer[] = {
{ } /* end */
};
+/* update HP, line and mono-out pins according to the master switch */
+static void alc262_hp_master_update(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int val = spec->master_sw;
+
+ /* HP & line-out */
+ snd_hda_codec_write_cache(codec, 0x1b, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ val ? PIN_HP : 0);
+ snd_hda_codec_write_cache(codec, 0x15, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ val ? PIN_HP : 0);
+ /* mono (speaker) depending on the HP jack sense */
+ val = val && !spec->jack_present;
+ snd_hda_codec_write_cache(codec, 0x16, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ val ? PIN_OUT : 0);
+}
+
+static void alc262_hp_bpc_automute(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ unsigned int presence;
+ presence = snd_hda_codec_read(codec, 0x1b, 0,
+ AC_VERB_GET_PIN_SENSE, 0);
+ spec->jack_present = !!(presence & AC_PINSENSE_PRESENCE);
+ alc262_hp_master_update(codec);
+}
+
+static void alc262_hp_bpc_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+ if ((res >> 26) != ALC880_HP_EVENT)
+ return;
+ alc262_hp_bpc_automute(codec);
+}
+
+static void alc262_hp_wildwest_automute(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ unsigned int presence;
+ presence = snd_hda_codec_read(codec, 0x15, 0,
+ AC_VERB_GET_PIN_SENSE, 0);
+ spec->jack_present = !!(presence & AC_PINSENSE_PRESENCE);
+ alc262_hp_master_update(codec);
+}
+
+static void alc262_hp_wildwest_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ if ((res >> 26) != ALC880_HP_EVENT)
+ return;
+ alc262_hp_wildwest_automute(codec);
+}
+
+static int alc262_hp_master_sw_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;
+ *ucontrol->value.integer.value = spec->master_sw;
+ return 0;
+}
+
+static int alc262_hp_master_sw_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;
+ int val = !!*ucontrol->value.integer.value;
+
+ if (val == spec->master_sw)
+ return 0;
+ spec->master_sw = val;
+ alc262_hp_master_update(codec);
+ return 1;
+}
+
static struct snd_kcontrol_new alc262_HP_BPC_mixer[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = alc262_hp_master_sw_get,
+ .put = alc262_hp_master_sw_put,
+ },
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_MONO("Speaker Playback Volume", 0x0e, 2, 0x0,
+ HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("Speaker 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("Mic Boost", 0x18, 0, HDA_INPUT),
@@ -7684,12 +8237,21 @@ static struct snd_kcontrol_new alc262_HP_BPC_mixer[] = {
};
static struct snd_kcontrol_new alc262_HP_BPC_WildWest_mixer[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = alc262_hp_master_sw_get,
+ .put = alc262_hp_master_sw_put,
+ },
HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 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_MONO("Speaker Playback Volume", 0x0e, 2, 0x0,
+ HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x16, 2, 0x0,
+ HDA_OUTPUT),
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x02, HDA_INPUT),
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x02, HDA_INPUT),
HDA_CODEC_VOLUME("Front Mic Boost", 0x1a, 0, HDA_INPUT),
@@ -7709,6 +8271,85 @@ static struct snd_kcontrol_new alc262_HP_BPC_WildWest_option_mixer[] = {
{ } /* end */
};
+/* mute/unmute internal speaker according to the hp jack and mute state */
+static void alc262_hp_t5735_automute(struct hda_codec *codec, int force)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (force || !spec->sense_updated) {
+ unsigned int present;
+ present = snd_hda_codec_read(codec, 0x15, 0,
+ AC_VERB_GET_PIN_SENSE, 0);
+ spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0;
+ spec->sense_updated = 1;
+ }
+ snd_hda_codec_amp_stereo(codec, 0x0c, HDA_OUTPUT, 0, HDA_AMP_MUTE,
+ spec->jack_present ? HDA_AMP_MUTE : 0);
+}
+
+static void alc262_hp_t5735_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ if ((res >> 26) != ALC880_HP_EVENT)
+ return;
+ alc262_hp_t5735_automute(codec, 1);
+}
+
+static void alc262_hp_t5735_init_hook(struct hda_codec *codec)
+{
+ alc262_hp_t5735_automute(codec, 1);
+}
+
+static struct snd_kcontrol_new alc262_hp_t5735_mixer[] = {
+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 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("Mic Boost", 0x18, 0, HDA_INPUT),
+ { } /* end */
+};
+
+static struct hda_verb alc262_hp_t5735_verbs[] = {
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+ { }
+};
+
+static struct snd_kcontrol_new alc262_hp_rp5700_mixer[] = {
+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0e, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Speaker Playback Switch", 0x16, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT),
+ { } /* end */
+};
+
+static struct hda_verb alc262_hp_rp5700_verbs[] = {
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x00 << 8))},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x00 << 8))},
+ {}
+};
+
+static struct hda_input_mux alc262_hp_rp5700_capture_source = {
+ .num_items = 1,
+ .items = {
+ { "Line", 0x1 },
+ },
+};
+
/* bind hp and internal speaker mute (with plug check) */
static int alc262_sony_master_sw_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -8082,6 +8723,72 @@ static struct hda_verb alc262_benq_t31_EAPD_verbs[] = {
{}
};
+/* Samsung Q1 Ultra Vista model setup */
+static struct snd_kcontrol_new alc262_ultra_mixer[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x19, 0, HDA_INPUT),
+ { } /* end */
+};
+
+static struct hda_verb alc262_ultra_verbs[] = {
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+ /* Mic is on Node 0x19 */
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+ {0x22, AC_VERB_SET_CONNECT_SEL, 0x01},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+ {0x23, AC_VERB_SET_CONNECT_SEL, 0x01},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+ {0x24, AC_VERB_SET_CONNECT_SEL, 0x01},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+ {}
+};
+
+static struct hda_input_mux alc262_ultra_capture_source = {
+ .num_items = 1,
+ .items = {
+ { "Mic", 0x1 },
+ },
+};
+
+/* mute/unmute internal speaker according to the hp jack and mute state */
+static void alc262_ultra_automute(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ unsigned int mute;
+ unsigned int present;
+
+ /* need to execute and sync at first */
+ snd_hda_codec_read(codec, 0x15, 0, AC_VERB_SET_PIN_SENSE, 0);
+ present = snd_hda_codec_read(codec, 0x15, 0,
+ AC_VERB_GET_PIN_SENSE, 0);
+ spec->jack_present = (present & 0x80000000) != 0;
+ if (spec->jack_present) {
+ /* mute internal speaker */
+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, HDA_AMP_MUTE);
+ } else {
+ /* unmute internal speaker if necessary */
+ mute = snd_hda_codec_amp_read(codec, 0x15, 0, HDA_OUTPUT, 0);
+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, mute);
+ }
+}
+
+/* unsolicited event for HP jack sensing */
+static void alc262_ultra_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ if ((res >> 26) != ALC880_HP_EVENT)
+ return;
+ alc262_ultra_automute(codec);
+}
+
/* 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)
@@ -8269,7 +8976,7 @@ static struct hda_verb alc262_HP_BPC_init_verbs[] = {
{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},
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
@@ -8311,6 +9018,8 @@ static struct hda_verb alc262_HP_BPC_init_verbs[] = {
{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
+ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+
{ }
};
@@ -8405,6 +9114,8 @@ static struct hda_verb alc262_HP_BPC_WildWest_init_verbs[] = {
/* {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x06 << 8))}, */
{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x07 << 8))},
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+
{ }
};
@@ -8484,39 +9195,49 @@ static const char *alc262_models[ALC262_MODEL_LAST] = {
[ALC262_FUJITSU] = "fujitsu",
[ALC262_HP_BPC] = "hp-bpc",
[ALC262_HP_BPC_D7000_WL]= "hp-bpc-d7000",
+ [ALC262_HP_TC_T5735] = "hp-tc-t5735",
+ [ALC262_HP_RP5700] = "hp-rp5700",
[ALC262_BENQ_ED8] = "benq",
[ALC262_BENQ_T31] = "benq-t31",
[ALC262_SONY_ASSAMD] = "sony-assamd",
+ [ALC262_ULTRA] = "ultra",
[ALC262_AUTO] = "auto",
};
static struct snd_pci_quirk alc262_cfg_tbl[] = {
SND_PCI_QUIRK(0x1002, 0x437b, "Hippo", ALC262_HIPPO),
SND_PCI_QUIRK(0x103c, 0x12fe, "HP xw9400", ALC262_HP_BPC),
- SND_PCI_QUIRK(0x103c, 0x280c, "HP xw4400", ALC262_HP_BPC),
SND_PCI_QUIRK(0x103c, 0x12ff, "HP xw4550", ALC262_HP_BPC),
- SND_PCI_QUIRK(0x103c, 0x1308, "HP xw4600", ALC262_HP_BPC),
- SND_PCI_QUIRK(0x103c, 0x3014, "HP xw6400", ALC262_HP_BPC),
- SND_PCI_QUIRK(0x103c, 0x1307, "HP xw6600", ALC262_HP_BPC),
- SND_PCI_QUIRK(0x103c, 0x3015, "HP xw8400", ALC262_HP_BPC),
SND_PCI_QUIRK(0x103c, 0x1306, "HP xw8600", ALC262_HP_BPC),
+ SND_PCI_QUIRK(0x103c, 0x1307, "HP xw6600", ALC262_HP_BPC),
+ SND_PCI_QUIRK(0x103c, 0x1308, "HP xw4600", ALC262_HP_BPC),
+ SND_PCI_QUIRK(0x103c, 0x1309, "HP xw4*00", ALC262_HP_BPC),
+ SND_PCI_QUIRK(0x103c, 0x130a, "HP xw6*00", ALC262_HP_BPC),
+ SND_PCI_QUIRK(0x103c, 0x130b, "HP xw8*00", ALC262_HP_BPC),
SND_PCI_QUIRK(0x103c, 0x2800, "HP D7000", ALC262_HP_BPC_D7000_WL),
- SND_PCI_QUIRK(0x103c, 0x2802, "HP D7000", ALC262_HP_BPC_D7000_WL),
- SND_PCI_QUIRK(0x103c, 0x2804, "HP D7000", ALC262_HP_BPC_D7000_WL),
- SND_PCI_QUIRK(0x103c, 0x2806, "HP D7000", ALC262_HP_BPC_D7000_WL),
SND_PCI_QUIRK(0x103c, 0x2801, "HP D7000", ALC262_HP_BPC_D7000_WF),
+ SND_PCI_QUIRK(0x103c, 0x2802, "HP D7000", ALC262_HP_BPC_D7000_WL),
SND_PCI_QUIRK(0x103c, 0x2803, "HP D7000", ALC262_HP_BPC_D7000_WF),
+ SND_PCI_QUIRK(0x103c, 0x2804, "HP D7000", ALC262_HP_BPC_D7000_WL),
SND_PCI_QUIRK(0x103c, 0x2805, "HP D7000", ALC262_HP_BPC_D7000_WF),
+ SND_PCI_QUIRK(0x103c, 0x2806, "HP D7000", ALC262_HP_BPC_D7000_WL),
SND_PCI_QUIRK(0x103c, 0x2807, "HP D7000", ALC262_HP_BPC_D7000_WF),
+ SND_PCI_QUIRK(0x103c, 0x280c, "HP xw4400", ALC262_HP_BPC),
+ SND_PCI_QUIRK(0x103c, 0x3014, "HP xw6400", ALC262_HP_BPC),
+ SND_PCI_QUIRK(0x103c, 0x3015, "HP xw8400", ALC262_HP_BPC),
+ SND_PCI_QUIRK(0x103c, 0x302f, "HP Thin Client T5735",
+ ALC262_HP_TC_T5735),
+ SND_PCI_QUIRK(0x103c, 0x2817, "HP RP5700", ALC262_HP_RP5700),
+ SND_PCI_QUIRK(0x104d, 0x1f00, "Sony ASSAMD", ALC262_SONY_ASSAMD),
SND_PCI_QUIRK(0x104d, 0x8203, "Sony UX-90", ALC262_HIPPO),
+ SND_PCI_QUIRK(0x104d, 0x820f, "Sony ASSAMD", ALC262_SONY_ASSAMD),
+ SND_PCI_QUIRK(0x104d, 0x900e, "Sony ASSAMD", ALC262_SONY_ASSAMD),
+ SND_PCI_QUIRK(0x104d, 0x9015, "Sony 0x9015", ALC262_SONY_ASSAMD),
SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FUJITSU),
- SND_PCI_QUIRK(0x17ff, 0x058f, "Benq Hippo", ALC262_HIPPO_1),
+ SND_PCI_QUIRK(0x144d, 0xc032, "Samsung Q1 Ultra", ALC262_ULTRA),
SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_BENQ_ED8),
SND_PCI_QUIRK(0x17ff, 0x058d, "Benq T31-16", ALC262_BENQ_T31),
- SND_PCI_QUIRK(0x104d, 0x820f, "Sony ASSAMD", ALC262_SONY_ASSAMD),
- SND_PCI_QUIRK(0x104d, 0x9015, "Sony 0x9015", ALC262_SONY_ASSAMD),
- SND_PCI_QUIRK(0x104d, 0x900e, "Sony ASSAMD", ALC262_SONY_ASSAMD),
- SND_PCI_QUIRK(0x104d, 0x1f00, "Sony ASSAMD", ALC262_SONY_ASSAMD),
+ SND_PCI_QUIRK(0x17ff, 0x058f, "Benq Hippo", ALC262_HIPPO_1),
{}
};
@@ -8579,6 +9300,8 @@ static struct alc_config_preset alc262_presets[] = {
.num_channel_mode = ARRAY_SIZE(alc262_modes),
.channel_mode = alc262_modes,
.input_mux = &alc262_HP_capture_source,
+ .unsol_event = alc262_hp_bpc_unsol_event,
+ .init_hook = alc262_hp_bpc_automute,
},
[ALC262_HP_BPC_D7000_WF] = {
.mixers = { alc262_HP_BPC_WildWest_mixer },
@@ -8589,6 +9312,8 @@ static struct alc_config_preset alc262_presets[] = {
.num_channel_mode = ARRAY_SIZE(alc262_modes),
.channel_mode = alc262_modes,
.input_mux = &alc262_HP_D7000_capture_source,
+ .unsol_event = alc262_hp_wildwest_unsol_event,
+ .init_hook = alc262_hp_wildwest_automute,
},
[ALC262_HP_BPC_D7000_WL] = {
.mixers = { alc262_HP_BPC_WildWest_mixer,
@@ -8600,7 +9325,30 @@ static struct alc_config_preset alc262_presets[] = {
.num_channel_mode = ARRAY_SIZE(alc262_modes),
.channel_mode = alc262_modes,
.input_mux = &alc262_HP_D7000_capture_source,
+ .unsol_event = alc262_hp_wildwest_unsol_event,
+ .init_hook = alc262_hp_wildwest_automute,
+ },
+ [ALC262_HP_TC_T5735] = {
+ .mixers = { alc262_hp_t5735_mixer },
+ .init_verbs = { alc262_init_verbs, alc262_hp_t5735_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,
+ .unsol_event = alc262_hp_t5735_unsol_event,
+ .init_hook = alc262_hp_t5735_init_hook,
},
+ [ALC262_HP_RP5700] = {
+ .mixers = { alc262_hp_rp5700_mixer },
+ .init_verbs = { alc262_init_verbs, alc262_hp_rp5700_verbs },
+ .num_dacs = ARRAY_SIZE(alc262_dac_nids),
+ .dac_nids = alc262_dac_nids,
+ .num_channel_mode = ARRAY_SIZE(alc262_modes),
+ .channel_mode = alc262_modes,
+ .input_mux = &alc262_hp_rp5700_capture_source,
+ },
[ALC262_BENQ_ED8] = {
.mixers = { alc262_base_mixer },
.init_verbs = { alc262_init_verbs, alc262_EAPD_verbs },
@@ -8635,6 +9383,19 @@ static struct alc_config_preset alc262_presets[] = {
.unsol_event = alc262_hippo_unsol_event,
.init_hook = alc262_hippo_automute,
},
+ [ALC262_ULTRA] = {
+ .mixers = { alc262_ultra_mixer },
+ .init_verbs = { alc262_init_verbs, alc262_ultra_verbs },
+ .num_dacs = ARRAY_SIZE(alc262_dac_nids),
+ .dac_nids = alc262_dac_nids,
+ .hp_nid = 0x03,
+ .dig_out_nid = ALC262_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc262_modes),
+ .channel_mode = alc262_modes,
+ .input_mux = &alc262_ultra_capture_source,
+ .unsol_event = alc262_ultra_unsol_event,
+ .init_hook = alc262_ultra_automute,
+ },
};
static int patch_alc262(struct hda_codec *codec)
@@ -8716,6 +9477,8 @@ static int patch_alc262(struct hda_codec *codec)
}
}
+ spec->vmaster_nid = 0x0c;
+
codec->patch_ops = alc_patch_ops;
if (board_config == ALC262_AUTO)
spec->init_hook = alc262_auto_init;
@@ -8873,6 +9636,49 @@ static void alc268_acer_init_hook(struct hda_codec *codec)
alc268_acer_automute(codec, 1);
}
+static struct snd_kcontrol_new alc268_dell_mixer[] = {
+ /* output mixer control */
+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Internal Mic Boost", 0x19, 0, HDA_INPUT),
+ { }
+};
+
+static struct hda_verb alc268_dell_verbs[] = {
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+ { }
+};
+
+/* mute/unmute internal speaker according to the hp jack and mute state */
+static void alc268_dell_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+ unsigned int mute;
+
+ present = snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_PIN_SENSE, 0);
+ if (present & 0x80000000)
+ mute = HDA_AMP_MUTE;
+ else
+ mute = snd_hda_codec_amp_read(codec, 0x15, 0, HDA_OUTPUT, 0);
+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, mute);
+}
+
+static void alc268_dell_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ if ((res >> 26) != ALC880_HP_EVENT)
+ return;
+ alc268_dell_automute(codec);
+}
+
+#define alc268_dell_init_hook alc268_dell_automute
+
/*
* generic initialization of ADC, input mixers and output mixers
*/
@@ -8915,19 +9721,13 @@ static struct hda_verb alc268_base_init_verbs[] = {
{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- /* FIXME: use matrix-type input source selection */
- /* Mixer elements: 0x18, 19, 1a, 1c, 14, 15, 0b */
- /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
- /* Input mixer2 */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
+ /* Unmute Selector 23h,24h and set the default input to mic-in */
+
+ {0x23, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x24, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
{ }
};
@@ -8972,29 +9772,14 @@ static int alc268_mux_enum_put(struct snd_kcontrol *kcontrol,
{
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] = { 0x23, 0x24 };
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)
- return 0;
- for (i = 0; i < imux->num_items; i++) {
- unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE;
- snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT,
- imux->items[i].index,
- HDA_AMP_MUTE, v);
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_CONNECT_SEL,
- idx );
- }
- *cur_val = idx;
- return 1;
+ return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
+ nid,
+ &spec->cur_mux[adc_idx]);
}
static struct snd_kcontrol_new alc268_capture_alt_mixer[] = {
@@ -9004,7 +9789,6 @@ static struct snd_kcontrol_new alc268_capture_alt_mixer[] = {
.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",
@@ -9025,7 +9809,6 @@ static struct snd_kcontrol_new alc268_capture_mixer[] = {
.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",
@@ -9047,6 +9830,61 @@ static struct hda_input_mux alc268_capture_source = {
},
};
+#ifdef CONFIG_SND_DEBUG
+static struct snd_kcontrol_new alc268_test_mixer[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+
+ /* Volume widgets */
+ HDA_CODEC_VOLUME("LOUT1 Playback Volume", 0x02, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("LOUT2 Playback Volume", 0x03, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE_MONO("Mono sum Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+ HDA_BIND_MUTE("LINE-OUT sum Playback Switch", 0x0f, 2, HDA_INPUT),
+ HDA_BIND_MUTE("HP-OUT sum Playback Switch", 0x10, 2, HDA_INPUT),
+ HDA_BIND_MUTE("LINE-OUT Playback Switch", 0x14, 2, HDA_OUTPUT),
+ HDA_BIND_MUTE("HP-OUT Playback Switch", 0x15, 2, HDA_OUTPUT),
+ HDA_BIND_MUTE("Mono Playback Switch", 0x16, 2, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("MIC1 Capture Volume", 0x18, 0x0, HDA_INPUT),
+ HDA_BIND_MUTE("MIC1 Capture Switch", 0x18, 2, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("MIC2 Capture Volume", 0x19, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("LINE1 Capture Volume", 0x1a, 0x0, HDA_INPUT),
+ HDA_BIND_MUTE("LINE1 Capture Switch", 0x1a, 2, HDA_OUTPUT),
+ /* The below appears problematic on some hardwares */
+ /*HDA_CODEC_VOLUME("PCBEEP Playback Volume", 0x1d, 0x0, HDA_INPUT),*/
+ HDA_CODEC_VOLUME("PCM-IN1 Capture Volume", 0x23, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("PCM-IN1 Capture Switch", 0x23, 2, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("PCM-IN2 Capture Volume", 0x24, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("PCM-IN2 Capture Switch", 0x24, 2, HDA_OUTPUT),
+
+ /* Modes for retasking pin widgets */
+ ALC_PIN_MODE("LINE-OUT pin mode", 0x14, ALC_PIN_DIR_INOUT),
+ ALC_PIN_MODE("HP-OUT pin mode", 0x15, ALC_PIN_DIR_INOUT),
+ ALC_PIN_MODE("MIC1 pin mode", 0x18, ALC_PIN_DIR_INOUT),
+ ALC_PIN_MODE("LINE1 pin mode", 0x1a, ALC_PIN_DIR_INOUT),
+
+ /* Controls for GPIO pins, assuming they are configured as outputs */
+ ALC_GPIO_DATA_SWITCH("GPIO pin 0", 0x01, 0x01),
+ ALC_GPIO_DATA_SWITCH("GPIO pin 1", 0x01, 0x02),
+ ALC_GPIO_DATA_SWITCH("GPIO pin 2", 0x01, 0x04),
+ ALC_GPIO_DATA_SWITCH("GPIO pin 3", 0x01, 0x08),
+
+ /* Switches to allow the digital SPDIF output pin to be enabled.
+ * The ALC268 does not have an SPDIF input.
+ */
+ ALC_SPDIF_CTRL_SWITCH("SPDIF Playback Switch", 0x06, 0x01),
+
+ /* A switch allowing EAPD to be enabled. Some laptops seem to use
+ * this output to turn on an external amplifier.
+ */
+ ALC_EAPD_CTRL_SWITCH("LINE-OUT EAPD Enable Switch", 0x0f, 0x02),
+ ALC_EAPD_CTRL_SWITCH("HP-OUT EAPD Enable Switch", 0x10, 0x02),
+
+ { } /* end */
+};
+#endif
+
/* create input playback/capture controls for the given pin */
static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid,
const char *ctlname, int idx)
@@ -9194,6 +10032,7 @@ static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec)
/* pcm configuration: identiacal with ALC880 */
#define alc268_pcm_analog_playback alc880_pcm_analog_playback
#define alc268_pcm_analog_capture alc880_pcm_analog_capture
+#define alc268_pcm_analog_alt_capture alc880_pcm_analog_alt_capture
#define alc268_pcm_digital_playback alc880_pcm_digital_playback
/*
@@ -9259,16 +10098,23 @@ static const char *alc268_models[ALC268_MODEL_LAST] = {
[ALC268_3ST] = "3stack",
[ALC268_TOSHIBA] = "toshiba",
[ALC268_ACER] = "acer",
+ [ALC268_DELL] = "dell",
+#ifdef CONFIG_SND_DEBUG
+ [ALC268_TEST] = "test",
+#endif
[ALC268_AUTO] = "auto",
};
static struct snd_pci_quirk alc268_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x1025, 0x0126, "Acer", ALC268_ACER),
+ SND_PCI_QUIRK(0x1025, 0x012e, "Acer Aspire 5310", ALC268_ACER),
+ SND_PCI_QUIRK(0x1025, 0x0130, "Acer Extensa 5210", ALC268_ACER),
+ SND_PCI_QUIRK(0x1025, 0x0136, "Acer Aspire 5315", ALC268_ACER),
+ SND_PCI_QUIRK(0x1028, 0x0253, "Dell OEM", ALC268_DELL),
+ SND_PCI_QUIRK(0x103c, 0x30cc, "TOSHIBA", ALC268_TOSHIBA),
SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC268_3ST),
SND_PCI_QUIRK(0x1179, 0xff10, "TOSHIBA A205", ALC268_TOSHIBA),
SND_PCI_QUIRK(0x1179, 0xff50, "TOSHIBA A305", ALC268_TOSHIBA),
- SND_PCI_QUIRK(0x103c, 0x30cc, "TOSHIBA", ALC268_TOSHIBA),
- SND_PCI_QUIRK(0x1025, 0x0126, "Acer", ALC268_ACER),
- SND_PCI_QUIRK(0x1025, 0x0130, "Acer Extensa 5210", ALC268_ACER),
SND_PCI_QUIRK(0x152d, 0x0763, "Diverse (CPR2000)", ALC268_ACER),
{}
};
@@ -9317,6 +10163,35 @@ static struct alc_config_preset alc268_presets[] = {
.unsol_event = alc268_acer_unsol_event,
.init_hook = alc268_acer_init_hook,
},
+ [ALC268_DELL] = {
+ .mixers = { alc268_dell_mixer },
+ .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
+ alc268_dell_verbs },
+ .num_dacs = ARRAY_SIZE(alc268_dac_nids),
+ .dac_nids = alc268_dac_nids,
+ .hp_nid = 0x02,
+ .num_channel_mode = ARRAY_SIZE(alc268_modes),
+ .channel_mode = alc268_modes,
+ .unsol_event = alc268_dell_unsol_event,
+ .init_hook = alc268_dell_init_hook,
+ .input_mux = &alc268_capture_source,
+ },
+#ifdef CONFIG_SND_DEBUG
+ [ALC268_TEST] = {
+ .mixers = { alc268_test_mixer, alc268_capture_mixer },
+ .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
+ alc268_volume_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc268_dac_nids),
+ .dac_nids = alc268_dac_nids,
+ .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
+ .adc_nids = alc268_adc_nids_alt,
+ .hp_nid = 0x03,
+ .dig_out_nid = ALC268_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc268_modes),
+ .channel_mode = alc268_modes,
+ .input_mux = &alc268_capture_source,
+ },
+#endif
};
static int patch_alc268(struct hda_codec *codec)
@@ -9361,34 +10236,34 @@ static int patch_alc268(struct hda_codec *codec)
spec->stream_name_analog = "ALC268 Analog";
spec->stream_analog_playback = &alc268_pcm_analog_playback;
spec->stream_analog_capture = &alc268_pcm_analog_capture;
+ spec->stream_analog_alt_capture = &alc268_pcm_analog_alt_capture;
spec->stream_name_digital = "ALC268 Digital";
spec->stream_digital_playback = &alc268_pcm_digital_playback;
- if (board_config == ALC268_AUTO) {
- if (!spec->adc_nids && spec->input_mux) {
- /* check whether NID 0x07 is valid */
- unsigned int wcap = get_wcaps(codec, 0x07);
-
- /* get type */
- wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
- if (wcap != AC_WID_AUD_IN) {
- spec->adc_nids = alc268_adc_nids_alt;
- spec->num_adc_nids =
- ARRAY_SIZE(alc268_adc_nids_alt);
- spec->mixers[spec->num_mixers] =
+ if (!spec->adc_nids && spec->input_mux) {
+ /* check whether NID 0x07 is valid */
+ unsigned int wcap = get_wcaps(codec, 0x07);
+
+ /* get type */
+ wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+ if (wcap != AC_WID_AUD_IN) {
+ spec->adc_nids = alc268_adc_nids_alt;
+ spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt);
+ spec->mixers[spec->num_mixers] =
alc268_capture_alt_mixer;
- spec->num_mixers++;
- } else {
- spec->adc_nids = alc268_adc_nids;
- spec->num_adc_nids =
- ARRAY_SIZE(alc268_adc_nids);
- spec->mixers[spec->num_mixers] =
- alc268_capture_mixer;
- spec->num_mixers++;
- }
+ spec->num_mixers++;
+ } else {
+ spec->adc_nids = alc268_adc_nids;
+ spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids);
+ spec->mixers[spec->num_mixers] =
+ alc268_capture_mixer;
+ spec->num_mixers++;
}
}
+
+ spec->vmaster_nid = 0x02;
+
codec->patch_ops = alc_patch_ops;
if (board_config == ALC268_AUTO)
spec->init_hook = alc268_auto_init;
@@ -9397,6 +10272,360 @@ static int patch_alc268(struct hda_codec *codec)
}
/*
+ * ALC269 channel source setting (2 channel)
+ */
+#define ALC269_DIGOUT_NID ALC880_DIGOUT_NID
+
+#define alc269_dac_nids alc260_dac_nids
+
+static hda_nid_t alc269_adc_nids[1] = {
+ /* ADC1 */
+ 0x07,
+};
+
+#define alc269_modes alc260_modes
+#define alc269_capture_source alc880_lg_lw_capture_source
+
+static struct snd_kcontrol_new alc269_base_mixer[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+ 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("Mic Boost", 0x18, 0, 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("Front Mic Boost", 0x19, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
+ { } /* end */
+};
+
+/* capture mixer elements */
+static struct snd_kcontrol_new alc269_capture_mixer[] = {
+ HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* The multiple "Capture Source" controls confuse alsamixer
+ * So call somewhat different..
+ */
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 1,
+ .info = alc_mux_enum_info,
+ .get = alc_mux_enum_get,
+ .put = alc_mux_enum_put,
+ },
+ { } /* end */
+};
+
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static struct hda_verb alc269_init_verbs[] = {
+ /*
+ * Unmute ADC0 and set the default input to mic-in
+ */
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+ /* Mute input amps (PCBeep, 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_MUTE(0)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+
+ /*
+ * Set up output mixers (0x0c - 0x0e)
+ */
+ /* set vol=0 to output mixers */
+ {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x03, 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)},
+
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+
+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+ /* FIXME: use matrix-type input source selection */
+ /* Mixer elements: 0x18, 19, 1a, 1b, 1d, 0b */
+ /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+
+ /* set EAPD */
+ {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
+ {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
+ { }
+};
+
+/* add playback controls from the parsed DAC table */
+static int alc269_auto_create_multi_out_ctls(struct alc_spec *spec,
+ const struct auto_pin_cfg *cfg)
+{
+ hda_nid_t nid;
+ int err;
+
+ spec->multiout.num_dacs = 1; /* only use one dac */
+ spec->multiout.dac_nids = spec->private_dac_nids;
+ spec->multiout.dac_nids[0] = 2;
+
+ nid = cfg->line_out_pins[0];
+ if (nid) {
+ err = add_control(spec, ALC_CTL_WIDGET_VOL,
+ "Front Playback Volume",
+ HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ err = add_control(spec, ALC_CTL_WIDGET_MUTE,
+ "Front Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ }
+
+ nid = cfg->speaker_pins[0];
+ if (nid) {
+ if (!cfg->line_out_pins[0]) {
+ err = add_control(spec, ALC_CTL_WIDGET_VOL,
+ "Speaker Playback Volume",
+ HDA_COMPOSE_AMP_VAL(0x02, 3, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ }
+ if (nid == 0x16) {
+ err = add_control(spec, ALC_CTL_WIDGET_MUTE,
+ "Speaker Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid, 2, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ } else {
+ err = add_control(spec, ALC_CTL_WIDGET_MUTE,
+ "Speaker Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ }
+ }
+ nid = cfg->hp_pins[0];
+ if (nid) {
+ /* spec->multiout.hp_nid = 2; */
+ if (!cfg->line_out_pins[0] && !cfg->speaker_pins[0]) {
+ err = add_control(spec, ALC_CTL_WIDGET_VOL,
+ "Headphone Playback Volume",
+ HDA_COMPOSE_AMP_VAL(0x02, 3, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ }
+ if (nid == 0x16) {
+ err = add_control(spec, ALC_CTL_WIDGET_MUTE,
+ "Headphone Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid, 2, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ } else {
+ err = add_control(spec, ALC_CTL_WIDGET_MUTE,
+ "Headphone Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ }
+ }
+ return 0;
+}
+
+#define alc269_auto_create_analog_input_ctls \
+ alc880_auto_create_analog_input_ctls
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+#define alc269_loopbacks alc880_loopbacks
+#endif
+
+/* pcm configuration: identiacal with ALC880 */
+#define alc269_pcm_analog_playback alc880_pcm_analog_playback
+#define alc269_pcm_analog_capture alc880_pcm_analog_capture
+#define alc269_pcm_digital_playback alc880_pcm_digital_playback
+#define alc269_pcm_digital_capture alc880_pcm_digital_capture
+
+/*
+ * BIOS auto configuration
+ */
+static int alc269_parse_auto_config(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int err;
+ static hda_nid_t alc269_ignore[] = { 0x1d, 0 };
+
+ err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
+ alc269_ignore);
+ if (err < 0)
+ return err;
+
+ err = alc269_auto_create_multi_out_ctls(spec, &spec->autocfg);
+ if (err < 0)
+ return err;
+ err = alc269_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ if (err < 0)
+ return err;
+
+ spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+
+ if (spec->autocfg.dig_out_pin)
+ spec->multiout.dig_out_nid = ALC269_DIGOUT_NID;
+
+ if (spec->kctl_alloc)
+ spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+
+ spec->init_verbs[spec->num_init_verbs++] = alc269_init_verbs;
+ spec->num_mux_defs = 1;
+ spec->input_mux = &spec->private_imux;
+
+ err = alc_auto_add_mic_boost(codec);
+ if (err < 0)
+ return err;
+
+ return 1;
+}
+
+#define alc269_auto_init_multi_out alc882_auto_init_multi_out
+#define alc269_auto_init_hp_out alc882_auto_init_hp_out
+#define alc269_auto_init_analog_input alc882_auto_init_analog_input
+
+
+/* init callback for auto-configuration model -- overriding the default init */
+static void alc269_auto_init(struct hda_codec *codec)
+{
+ alc269_auto_init_multi_out(codec);
+ alc269_auto_init_hp_out(codec);
+ alc269_auto_init_analog_input(codec);
+}
+
+/*
+ * configuration and preset
+ */
+static const char *alc269_models[ALC269_MODEL_LAST] = {
+ [ALC269_BASIC] = "basic",
+};
+
+static struct snd_pci_quirk alc269_cfg_tbl[] = {
+ {}
+};
+
+static struct alc_config_preset alc269_presets[] = {
+ [ALC269_BASIC] = {
+ .mixers = { alc269_base_mixer },
+ .init_verbs = { alc269_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc269_dac_nids),
+ .dac_nids = alc269_dac_nids,
+ .hp_nid = 0x03,
+ .num_channel_mode = ARRAY_SIZE(alc269_modes),
+ .channel_mode = alc269_modes,
+ .input_mux = &alc269_capture_source,
+ },
+};
+
+static int patch_alc269(struct hda_codec *codec)
+{
+ struct alc_spec *spec;
+ int board_config;
+ int err;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+
+ board_config = snd_hda_check_board_config(codec, ALC269_MODEL_LAST,
+ alc269_models,
+ alc269_cfg_tbl);
+
+ if (board_config < 0) {
+ printk(KERN_INFO "hda_codec: Unknown model for ALC269, "
+ "trying auto-probe from BIOS...\n");
+ board_config = ALC269_AUTO;
+ }
+
+ if (board_config == ALC269_AUTO) {
+ /* automatic parse from the BIOS config */
+ err = alc269_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 = ALC269_BASIC;
+ }
+ }
+
+ if (board_config != ALC269_AUTO)
+ setup_preset(spec, &alc269_presets[board_config]);
+
+ spec->stream_name_analog = "ALC269 Analog";
+ spec->stream_analog_playback = &alc269_pcm_analog_playback;
+ spec->stream_analog_capture = &alc269_pcm_analog_capture;
+
+ spec->stream_name_digital = "ALC269 Digital";
+ spec->stream_digital_playback = &alc269_pcm_digital_playback;
+ spec->stream_digital_capture = &alc269_pcm_digital_capture;
+
+ spec->adc_nids = alc269_adc_nids;
+ spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids);
+ spec->mixers[spec->num_mixers] = alc269_capture_mixer;
+ spec->num_mixers++;
+
+ codec->patch_ops = alc_patch_ops;
+ if (board_config == ALC269_AUTO)
+ spec->init_hook = alc269_auto_init;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+ if (!spec->loopback.amplist)
+ spec->loopback.amplist = alc269_loopbacks;
+#endif
+
+ return 0;
+}
+
+/*
* ALC861 channel source setting (2/6 channel selection for 3-stack)
*/
@@ -10213,7 +11442,6 @@ static struct snd_kcontrol_new alc861_capture_mixer[] = {
.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",
@@ -10369,22 +11597,23 @@ static struct snd_pci_quirk alc861_cfg_tbl[] = {
SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC861_3ST),
SND_PCI_QUIRK(0x1043, 0x1335, "ASUS F2/3", ALC861_ASUS_LAPTOP),
SND_PCI_QUIRK(0x1043, 0x1338, "ASUS F2/3", ALC861_ASUS_LAPTOP),
- SND_PCI_QUIRK(0x1043, 0x13d7, "ASUS A9rp", ALC861_ASUS_LAPTOP),
- SND_PCI_QUIRK(0x1584, 0x9075, "Airis Praxis N1212", ALC861_ASUS_LAPTOP),
SND_PCI_QUIRK(0x1043, 0x1393, "ASUS", ALC861_ASUS),
+ SND_PCI_QUIRK(0x1043, 0x13d7, "ASUS A9rp", ALC861_ASUS_LAPTOP),
SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS P1-AH2", ALC861_3ST_DIG),
SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba", ALC861_TOSHIBA),
/* FIXME: the entry below breaks Toshiba A100 (model=auto works!)
* Any other models that need this preset?
*/
/* SND_PCI_QUIRK(0x1179, 0xff10, "Toshiba", ALC861_TOSHIBA), */
- SND_PCI_QUIRK(0x1584, 0x9072, "Uniwill m31", ALC861_UNIWILL_M31),
- SND_PCI_QUIRK(0x1584, 0x9075, "Uniwill", ALC861_UNIWILL_M31),
+ SND_PCI_QUIRK(0x1462, 0x7254, "HP dx2200 (MSI MS-7254)", ALC861_3ST),
+ SND_PCI_QUIRK(0x1462, 0x7297, "HP dx2250 (MSI MS-7297)", ALC861_3ST),
SND_PCI_QUIRK(0x1584, 0x2b01, "Uniwill X40AIx", ALC861_UNIWILL_M31),
+ SND_PCI_QUIRK(0x1584, 0x9072, "Uniwill m31", ALC861_UNIWILL_M31),
+ SND_PCI_QUIRK(0x1584, 0x9075, "Airis Praxis N1212", ALC861_ASUS_LAPTOP),
+ /* FIXME: the below seems conflict */
+ /* SND_PCI_QUIRK(0x1584, 0x9075, "Uniwill", ALC861_UNIWILL_M31), */
SND_PCI_QUIRK(0x1849, 0x0660, "Asrock 939SLI32", ALC660_3ST),
SND_PCI_QUIRK(0x8086, 0xd600, "Intel", ALC861_3ST),
- SND_PCI_QUIRK(0x1462, 0x7254, "HP dx2200 (MSI MS-7254)", ALC861_3ST),
- SND_PCI_QUIRK(0x1462, 0x7297, "HP dx2250 (MSI MS-7297)", ALC861_3ST),
{}
};
@@ -10543,6 +11772,8 @@ static int patch_alc861(struct hda_codec *codec)
spec->stream_digital_playback = &alc861_pcm_digital_playback;
spec->stream_digital_capture = &alc861_pcm_digital_capture;
+ spec->vmaster_nid = 0x03;
+
codec->patch_ops = alc_patch_ops;
if (board_config == ALC861_AUTO)
spec->init_hook = alc861_auto_init;
@@ -10697,7 +11928,6 @@ static struct snd_kcontrol_new alc861vd_capture_mixer[] = {
.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",
@@ -11102,21 +12332,20 @@ static const char *alc861vd_models[ALC861VD_MODEL_LAST] = {
};
static struct snd_pci_quirk alc861vd_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x1019, 0xa88d, "Realtek ALC660 demo", ALC660VD_3ST),
+ SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_HP),
SND_PCI_QUIRK(0x1043, 0x12e2, "Asus z35m", ALC660VD_3ST),
SND_PCI_QUIRK(0x1043, 0x1339, "Asus G1", ALC660VD_3ST),
SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS", ALC660VD_3ST_DIG),
SND_PCI_QUIRK(0x10de, 0x03f0, "Realtek ALC660 demo", ALC660VD_3ST),
- SND_PCI_QUIRK(0x1019, 0xa88d, "Realtek ALC660 demo", ALC660VD_3ST),
-
+ SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba A135", ALC861VD_LENOVO),
/*SND_PCI_QUIRK(0x1179, 0xff00, "DALLAS", ALC861VD_DALLAS),*/ /*lenovo*/
SND_PCI_QUIRK(0x1179, 0xff01, "DALLAS", ALC861VD_DALLAS),
- SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo 3000 C200", ALC861VD_LENOVO),
- SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo", ALC861VD_LENOVO),
- SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba A135", ALC861VD_LENOVO),
SND_PCI_QUIRK(0x1179, 0xff03, "Toshiba P205", ALC861VD_LENOVO),
SND_PCI_QUIRK(0x1565, 0x820d, "Biostar NF61S SE", ALC861VD_6ST_DIG),
+ SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo", ALC861VD_LENOVO),
+ SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo 3000 C200", ALC861VD_LENOVO),
SND_PCI_QUIRK(0x1849, 0x0862, "ASRock K8NF6G-VSTA", ALC861VD_6ST_DIG),
- SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_HP),
{}
};
@@ -11520,6 +12749,8 @@ static int patch_alc861vd(struct hda_codec *codec)
spec->mixers[spec->num_mixers] = alc861vd_capture_mixer;
spec->num_mixers++;
+ spec->vmaster_nid = 0x02;
+
codec->patch_ops = alc_patch_ops;
if (board_config == ALC861VD_AUTO)
@@ -11699,18 +12930,6 @@ static struct snd_kcontrol_new alc662_base_mixer[] = {
HDA_CODEC_MUTE("Mic Playback Switch", 0xb, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0xb, 0x01, HDA_INPUT),
HDA_CODEC_MUTE("Front Mic Playback Switch", 0xb, 0x01, HDA_INPUT),
-
- /* Capture mixer control */
- HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x09, 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,
- },
{ } /* end */
};
@@ -11728,17 +12947,6 @@ static struct snd_kcontrol_new alc662_3ST_2ch_mixer[] = {
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", 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = alc662_mux_enum_info,
- .get = alc662_mux_enum_get,
- .put = alc662_mux_enum_put,
- },
{ } /* end */
};
@@ -11762,46 +12970,24 @@ static struct snd_kcontrol_new alc662_3ST_6ch_mixer[] = {
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", 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = alc662_mux_enum_info,
- .get = alc662_mux_enum_get,
- .put = alc662_mux_enum_put,
- },
{ } /* end */
};
static struct snd_kcontrol_new alc662_lenovo_101e_mixer[] = {
HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
HDA_BIND_MUTE("Front Playback Switch", 0x02, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("iSpeaker Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("iSpeaker Playback Switch", 0x03, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x03, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Speaker Playback Switch", 0x03, 2, HDA_INPUT),
HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
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, 0x1, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = alc662_mux_enum_info,
- .get = alc662_mux_enum_get,
- .put = alc662_mux_enum_put,
- },
{ } /* end */
};
static struct snd_kcontrol_new alc662_eeepc_p701_mixer[] = {
- HDA_CODEC_MUTE("iSpeaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("LineOut Playback Volume", 0x02, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("LineOut Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
@@ -11816,6 +13002,24 @@ static struct snd_kcontrol_new alc662_eeepc_p701_mixer[] = {
{ } /* end */
};
+static struct snd_kcontrol_new alc662_eeepc_ep20_mixer[] = {
+ HDA_CODEC_VOLUME("LineOut Playback Volume", 0x02, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("LineOut Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Surround Playback Switch", 0x03, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE_MONO("Center Playback Switch", 0x04, 1, 2, HDA_INPUT),
+ HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x04, 2, 2, HDA_INPUT),
+ HDA_CODEC_MUTE("Speaker Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("MuteCtrl Playback Switch", 0x0c, 2, 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),
+ { } /* end */
+};
+
static struct snd_kcontrol_new alc662_chmode_mixer[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -11901,6 +13105,13 @@ static struct hda_verb alc662_eeepc_sue_init_verbs[] = {
{}
};
+/* Set Unsolicited Event*/
+static struct hda_verb alc662_eeepc_ep20_sue_init_verbs[] = {
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ {}
+};
+
/*
* generic initialization of ADC, input mixers and output mixers
*/
@@ -11957,14 +13168,13 @@ static struct snd_kcontrol_new alc662_capture_mixer[] = {
.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 = 1,
- .info = alc882_mux_enum_info,
- .get = alc882_mux_enum_get,
- .put = alc882_mux_enum_put,
+ .info = alc662_mux_enum_info,
+ .get = alc662_mux_enum_get,
+ .put = alc662_mux_enum_put,
},
{ } /* end */
};
@@ -12037,6 +13247,40 @@ static void alc662_eeepc_inithook(struct hda_codec *codec)
alc662_eeepc_mic_automute(codec);
}
+static void alc662_eeepc_ep20_automute(struct hda_codec *codec)
+{
+ unsigned int mute;
+ unsigned int present;
+
+ snd_hda_codec_read(codec, 0x14, 0, AC_VERB_SET_PIN_SENSE, 0);
+ present = snd_hda_codec_read(codec, 0x14, 0,
+ AC_VERB_GET_PIN_SENSE, 0);
+ present = (present & 0x80000000) != 0;
+ if (present) {
+ /* mute internal speaker */
+ snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, HDA_AMP_MUTE);
+ } else {
+ /* unmute internal speaker if necessary */
+ mute = snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0);
+ snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, mute);
+ }
+}
+
+/* unsolicited event for HP jack sensing */
+static void alc662_eeepc_ep20_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ if ((res >> 26) == ALC880_HP_EVENT)
+ alc662_eeepc_ep20_automute(codec);
+}
+
+static void alc662_eeepc_ep20_inithook(struct hda_codec *codec)
+{
+ alc662_eeepc_ep20_automute(codec);
+}
+
#ifdef CONFIG_SND_HDA_POWER_SAVE
#define alc662_loopbacks alc880_loopbacks
#endif
@@ -12057,12 +13301,15 @@ static const char *alc662_models[ALC662_MODEL_LAST] = {
[ALC662_3ST_6ch] = "3stack-6ch",
[ALC662_5ST_DIG] = "6stack-dig",
[ALC662_LENOVO_101E] = "lenovo-101e",
+ [ALC662_ASUS_EEEPC_P701] = "eeepc-p701",
+ [ALC662_ASUS_EEEPC_EP20] = "eeepc-ep20",
[ALC662_AUTO] = "auto",
};
static struct snd_pci_quirk alc662_cfg_tbl[] = {
- SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo", ALC662_LENOVO_101E),
SND_PCI_QUIRK(0x1043, 0x82a1, "ASUS Eeepc", ALC662_ASUS_EEEPC_P701),
+ SND_PCI_QUIRK(0x1043, 0x82d1, "ASUS Eeepc EP20", ALC662_ASUS_EEEPC_EP20),
+ SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo", ALC662_LENOVO_101E),
{}
};
@@ -12149,6 +13396,21 @@ static struct alc_config_preset alc662_presets[] = {
.unsol_event = alc662_eeepc_unsol_event,
.init_hook = alc662_eeepc_inithook,
},
+ [ALC662_ASUS_EEEPC_EP20] = {
+ .mixers = { alc662_eeepc_ep20_mixer, alc662_capture_mixer,
+ alc662_chmode_mixer },
+ .init_verbs = { alc662_init_verbs,
+ alc662_eeepc_ep20_sue_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc662_dac_nids),
+ .dac_nids = alc662_dac_nids,
+ .num_adc_nids = ARRAY_SIZE(alc662_adc_nids),
+ .adc_nids = alc662_adc_nids,
+ .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes),
+ .channel_mode = alc662_3ST_6ch_modes,
+ .input_mux = &alc662_lenovo_101e_capture_source,
+ .unsol_event = alc662_eeepc_ep20_unsol_event,
+ .init_hook = alc662_eeepc_ep20_inithook,
+ },
};
@@ -12308,6 +13570,7 @@ static void alc662_auto_init_multi_out(struct hda_codec *codec)
struct alc_spec *spec = codec->spec;
int i;
+ alc_subsystem_id(codec, 0x15, 0x1b, 0x14);
for (i = 0; i <= HDA_SIDE; i++) {
hda_nid_t nid = spec->autocfg.line_out_pins[i];
int pin_type = get_pin_type(spec->autocfg.line_out_type);
@@ -12458,6 +13721,8 @@ static int patch_alc662(struct hda_codec *codec)
spec->num_adc_nids = ARRAY_SIZE(alc662_adc_nids);
}
+ spec->vmaster_nid = 0x02;
+
codec->patch_ops = alc_patch_ops;
if (board_config == ALC662_AUTO)
spec->init_hook = alc662_auto_init;
@@ -12475,7 +13740,9 @@ static int patch_alc662(struct hda_codec *codec)
struct hda_codec_preset snd_hda_preset_realtek[] = {
{ .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 },
{ .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 },
+ { .id = 0x10ec0267, .name = "ALC267", .patch = patch_alc268 },
{ .id = 0x10ec0268, .name = "ALC268", .patch = patch_alc268 },
+ { .id = 0x10ec0269, .name = "ALC269", .patch = patch_alc269 },
{ .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660",
.patch = patch_alc861 },
{ .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd },
@@ -12490,5 +13757,6 @@ struct hda_codec_preset snd_hda_preset_realtek[] = {
{ .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc883 },
{ .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 },
{ .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc883 },
+ { .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc883 },
{} /* terminator */
};
diff --git a/sound/pci/hda/patch_si3054.c b/sound/pci/hda/patch_si3054.c
index 2a4b9609aa5..d22f5a6b850 100644
--- a/sound/pci/hda/patch_si3054.c
+++ b/sound/pci/hda/patch_si3054.c
@@ -22,7 +22,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
@@ -287,7 +286,6 @@ static int patch_si3054(struct hda_codec *codec)
struct hda_codec_preset snd_hda_preset_si3054[] = {
{ .id = 0x163c3055, .name = "Si3054", .patch = patch_si3054 },
{ .id = 0x163c3155, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x11c11040, .name = "Si3054", .patch = patch_si3054 },
{ .id = 0x11c13026, .name = "Si3054", .patch = patch_si3054 },
{ .id = 0x11c13055, .name = "Si3054", .patch = patch_si3054 },
{ .id = 0x11c13155, .name = "Si3054", .patch = patch_si3054 },
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 04012237096..caf48edaa92 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -24,7 +24,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
@@ -35,7 +34,8 @@
#include "hda_local.h"
#define NUM_CONTROL_ALLOC 32
-#define STAC_HP_EVENT 0x37
+#define STAC_PWR_EVENT 0x20
+#define STAC_HP_EVENT 0x30
enum {
STAC_REF,
@@ -62,6 +62,16 @@ enum {
};
enum {
+ STAC_92HD73XX_REF,
+ STAC_92HD73XX_MODELS
+};
+
+enum {
+ STAC_92HD71BXX_REF,
+ STAC_92HD71BXX_MODELS
+};
+
+enum {
STAC_925x_REF,
STAC_M2_2,
STAC_MA6,
@@ -97,6 +107,7 @@ enum {
STAC_D965_3ST,
STAC_D965_5ST,
STAC_DELL_3ST,
+ STAC_DELL_BIOS,
STAC_927X_MODELS
};
@@ -110,11 +121,24 @@ struct sigmatel_spec {
unsigned int mic_switch: 1;
unsigned int alt_switch: 1;
unsigned int hp_detect: 1;
- unsigned int gpio_mute: 1;
- unsigned int gpio_mask, gpio_data;
+ /* gpio lines */
+ unsigned int gpio_mask;
+ unsigned int gpio_dir;
+ unsigned int gpio_data;
+ unsigned int gpio_mute;
+
+ /* analog loopback */
+ unsigned char aloopback_mask;
+ unsigned char aloopback_shift;
+
+ /* power management */
+ unsigned int num_pwrs;
+ hda_nid_t *pwr_nids;
/* playback */
+ struct hda_input_mux *mono_mux;
+ unsigned int cur_mmux;
struct hda_multi_out multiout;
hda_nid_t dac_nids[5];
@@ -125,8 +149,10 @@ struct sigmatel_spec {
unsigned int num_muxes;
hda_nid_t *dmic_nids;
unsigned int num_dmics;
- hda_nid_t dmux_nid;
+ hda_nid_t *dmux_nids;
+ unsigned int num_dmuxes;
hda_nid_t dig_in_nid;
+ hda_nid_t mono_nid;
/* pin widgets */
hda_nid_t *pin_nids;
@@ -140,7 +166,7 @@ struct sigmatel_spec {
/* capture source */
struct hda_input_mux *dinput_mux;
- unsigned int cur_dmux;
+ unsigned int cur_dmux[2];
struct hda_input_mux *input_mux;
unsigned int cur_mux[3];
@@ -157,6 +183,10 @@ struct sigmatel_spec {
struct snd_kcontrol_new *kctl_alloc;
struct hda_input_mux private_dimux;
struct hda_input_mux private_imux;
+ struct hda_input_mux private_mono_mux;
+
+ /* virtual master */
+ unsigned int vmaster_tlv[4];
};
static hda_nid_t stac9200_adc_nids[1] = {
@@ -171,6 +201,58 @@ static hda_nid_t stac9200_dac_nids[1] = {
0x02,
};
+static hda_nid_t stac92hd73xx_pwr_nids[8] = {
+ 0x0a, 0x0b, 0x0c, 0xd, 0x0e,
+ 0x0f, 0x10, 0x11
+};
+
+static hda_nid_t stac92hd73xx_adc_nids[2] = {
+ 0x1a, 0x1b
+};
+
+#define STAC92HD73XX_NUM_DMICS 2
+static hda_nid_t stac92hd73xx_dmic_nids[STAC92HD73XX_NUM_DMICS + 1] = {
+ 0x13, 0x14, 0
+};
+
+#define STAC92HD73_DAC_COUNT 5
+static hda_nid_t stac92hd73xx_dac_nids[STAC92HD73_DAC_COUNT] = {
+ 0x15, 0x16, 0x17, 0x18, 0x19,
+};
+
+static hda_nid_t stac92hd73xx_mux_nids[4] = {
+ 0x28, 0x29, 0x2a, 0x2b,
+};
+
+static hda_nid_t stac92hd73xx_dmux_nids[2] = {
+ 0x20, 0x21,
+};
+
+static hda_nid_t stac92hd71bxx_pwr_nids[3] = {
+ 0x0a, 0x0d, 0x0f
+};
+
+static hda_nid_t stac92hd71bxx_adc_nids[2] = {
+ 0x12, 0x13,
+};
+
+static hda_nid_t stac92hd71bxx_mux_nids[2] = {
+ 0x1a, 0x1b
+};
+
+static hda_nid_t stac92hd71bxx_dmux_nids[1] = {
+ 0x1c,
+};
+
+static hda_nid_t stac92hd71bxx_dac_nids[2] = {
+ 0x10, /*0x11, */
+};
+
+#define STAC92HD71BXX_NUM_DMICS 2
+static hda_nid_t stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS + 1] = {
+ 0x18, 0x19, 0
+};
+
static hda_nid_t stac925x_adc_nids[1] = {
0x03,
};
@@ -188,6 +270,10 @@ static hda_nid_t stac925x_dmic_nids[STAC925X_NUM_DMICS + 1] = {
0x15, 0
};
+static hda_nid_t stac925x_dmux_nids[1] = {
+ 0x14,
+};
+
static hda_nid_t stac922x_adc_nids[2] = {
0x06, 0x07,
};
@@ -204,6 +290,15 @@ static hda_nid_t stac927x_mux_nids[3] = {
0x15, 0x16, 0x17
};
+static hda_nid_t stac927x_dmux_nids[1] = {
+ 0x1b,
+};
+
+#define STAC927X_NUM_DMICS 2
+static hda_nid_t stac927x_dmic_nids[STAC927X_NUM_DMICS + 1] = {
+ 0x13, 0x14, 0
+};
+
static hda_nid_t stac9205_adc_nids[2] = {
0x12, 0x13
};
@@ -212,6 +307,10 @@ static hda_nid_t stac9205_mux_nids[2] = {
0x19, 0x1a
};
+static hda_nid_t stac9205_dmux_nids[1] = {
+ 0x1d,
+};
+
#define STAC9205_NUM_DMICS 2
static hda_nid_t stac9205_dmic_nids[STAC9205_NUM_DMICS + 1] = {
0x17, 0x18, 0
@@ -232,6 +331,17 @@ static hda_nid_t stac922x_pin_nids[10] = {
0x0f, 0x10, 0x11, 0x15, 0x1b,
};
+static hda_nid_t stac92hd73xx_pin_nids[12] = {
+ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+ 0x0f, 0x10, 0x11, 0x12, 0x13,
+ 0x14, 0x22
+};
+
+static hda_nid_t stac92hd71bxx_pin_nids[10] = {
+ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+ 0x0f, 0x14, 0x18, 0x19, 0x1e,
+};
+
static hda_nid_t stac927x_pin_nids[14] = {
0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
0x0f, 0x10, 0x11, 0x12, 0x13,
@@ -257,8 +367,9 @@ static int stac92xx_dmux_enum_get(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec;
+ unsigned int dmux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- ucontrol->value.enumerated.item[0] = spec->cur_dmux;
+ ucontrol->value.enumerated.item[0] = spec->cur_dmux[dmux_idx];
return 0;
}
@@ -267,9 +378,10 @@ static int stac92xx_dmux_enum_put(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec;
+ unsigned int dmux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
return snd_hda_input_mux_put(codec, spec->dinput_mux, ucontrol,
- spec->dmux_nid, &spec->cur_dmux);
+ spec->dmux_nids[dmux_idx], &spec->cur_dmux[dmux_idx]);
}
static int stac92xx_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
@@ -299,15 +411,45 @@ static int stac92xx_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
spec->mux_nids[adc_idx], &spec->cur_mux[adc_idx]);
}
+static int stac92xx_mono_mux_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+ return snd_hda_input_mux_info(spec->mono_mux, uinfo);
+}
+
+static int stac92xx_mono_mux_enum_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+
+ ucontrol->value.enumerated.item[0] = spec->cur_mmux;
+ return 0;
+}
+
+static int stac92xx_mono_mux_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+
+ return snd_hda_input_mux_put(codec, spec->mono_mux, ucontrol,
+ spec->mono_nid, &spec->cur_mmux);
+}
+
#define stac92xx_aloopback_info snd_ctl_boolean_mono_info
static int stac92xx_aloopback_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
struct sigmatel_spec *spec = codec->spec;
- ucontrol->value.integer.value[0] = spec->aloopback;
+ ucontrol->value.integer.value[0] = !!(spec->aloopback &
+ (spec->aloopback_mask << idx));
return 0;
}
@@ -316,23 +458,33 @@ static int stac92xx_aloopback_put(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec;
+ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
unsigned int dac_mode;
+ unsigned int val, idx_val;
- if (spec->aloopback == ucontrol->value.integer.value[0])
+ idx_val = spec->aloopback_mask << idx;
+ if (ucontrol->value.integer.value[0])
+ val = spec->aloopback | idx_val;
+ else
+ val = spec->aloopback & ~idx_val;
+ if (spec->aloopback == val)
return 0;
- spec->aloopback = ucontrol->value.integer.value[0];
-
+ spec->aloopback = val;
+ /* Only return the bits defined by the shift value of the
+ * first two bytes of the mask
+ */
dac_mode = snd_hda_codec_read(codec, codec->afg, 0,
- kcontrol->private_value & 0xFFFF, 0x0);
+ kcontrol->private_value & 0xFFFF, 0x0);
+ dac_mode >>= spec->aloopback_shift;
- if (spec->aloopback) {
+ if (spec->aloopback & idx_val) {
snd_hda_power_up(codec);
- dac_mode |= 0x40;
+ dac_mode |= idx_val;
} else {
snd_hda_power_down(codec);
- dac_mode &= ~0x40;
+ dac_mode &= ~idx_val;
}
snd_hda_codec_write_cache(codec, codec->afg, 0,
@@ -354,6 +506,107 @@ static struct hda_verb stac9200_eapd_init[] = {
{}
};
+static struct hda_verb stac92hd73xx_6ch_core_init[] = {
+ /* set master volume and direct control */
+ { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+ /* setup audio connections */
+ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00},
+ { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01},
+ { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02},
+ /* setup adcs to point to mixer */
+ { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
+ { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
+ { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ /* setup import muxs */
+ { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01},
+ { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01},
+ { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01},
+ { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {}
+};
+
+static struct hda_verb stac92hd73xx_8ch_core_init[] = {
+ /* set master volume and direct control */
+ { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+ /* setup audio connections */
+ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00},
+ { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01},
+ { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02},
+ /* connect hp ports to dac3 */
+ { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x03},
+ { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x03},
+ /* setup adcs to point to mixer */
+ { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
+ { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
+ { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ /* setup import muxs */
+ { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01},
+ { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01},
+ { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01},
+ { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x03},
+ {}
+};
+
+static struct hda_verb stac92hd73xx_10ch_core_init[] = {
+ /* set master volume and direct control */
+ { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+ /* setup audio connections */
+ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
+ { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01 },
+ { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02 },
+ /* dac3 is connected to import3 mux */
+ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb07f},
+ /* connect hp ports to dac4 */
+ { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x04},
+ { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x04},
+ /* setup adcs to point to mixer */
+ { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
+ { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
+ { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ /* setup import muxs */
+ { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01},
+ { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01},
+ { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01},
+ { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x03},
+ {}
+};
+
+static struct hda_verb stac92hd71bxx_core_init[] = {
+ /* set master volume and direct control */
+ { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+ /* connect headphone jack to dac1 */
+ { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01},
+ { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Speaker */
+ /* unmute right and left channels for nodes 0x0a, 0xd, 0x0f */
+ { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+};
+
+static struct hda_verb stac92hd71bxx_analog_core_init[] = {
+ /* set master volume and direct control */
+ { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+ /* connect headphone jack to dac1 */
+ { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01},
+ /* connect ports 0d and 0f to audio mixer */
+ { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x2},
+ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2},
+ { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Speaker */
+ /* unmute dac0 input in audio mixer */
+ { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0x701f},
+ /* unmute right and left channels for nodes 0x0a, 0xd, 0x0f */
+ { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {}
+};
+
static struct hda_verb stac925x_core_init[] = {
/* set dac0mux for dac converter */
{ 0x06, AC_VERB_SET_CONNECT_SEL, 0x00},
@@ -388,6 +641,16 @@ static struct hda_verb stac9205_core_init[] = {
{}
};
+#define STAC_MONO_MUX \
+ { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = "Mono Mux", \
+ .count = 1, \
+ .info = stac92xx_mono_mux_enum_info, \
+ .get = stac92xx_mono_mux_enum_get, \
+ .put = stac92xx_mono_mux_enum_put, \
+ }
+
#define STAC_INPUT_SOURCE(cnt) \
{ \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
@@ -398,11 +661,11 @@ static struct hda_verb stac9205_core_init[] = {
.put = stac92xx_mux_enum_put, \
}
-#define STAC_ANALOG_LOOPBACK(verb_read,verb_write) \
+#define STAC_ANALOG_LOOPBACK(verb_read, verb_write, cnt) \
{ \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = "Analog Loopback", \
- .count = 1, \
+ .count = cnt, \
.info = stac92xx_aloopback_info, \
.get = stac92xx_aloopback_get, \
.put = stac92xx_aloopback_put, \
@@ -419,6 +682,114 @@ static struct snd_kcontrol_new stac9200_mixer[] = {
{ } /* end */
};
+static struct snd_kcontrol_new stac92hd73xx_6ch_mixer[] = {
+ STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3),
+
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
+
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT),
+
+ HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT),
+
+ HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT),
+
+ HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT),
+ HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT),
+
+ HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT),
+ HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT),
+
+ HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT),
+ { } /* end */
+};
+
+static struct snd_kcontrol_new stac92hd73xx_8ch_mixer[] = {
+ STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 4),
+
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
+
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT),
+
+ HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT),
+
+ HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT),
+
+ HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT),
+ HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT),
+
+ HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT),
+ HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT),
+
+ HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT),
+ { } /* end */
+};
+
+static struct snd_kcontrol_new stac92hd73xx_10ch_mixer[] = {
+ STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 5),
+
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
+
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT),
+
+ HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT),
+
+ HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT),
+
+ HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT),
+ HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT),
+
+ HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT),
+ HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT),
+
+ HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT),
+ { } /* end */
+};
+
+static struct snd_kcontrol_new stac92hd71bxx_analog_mixer[] = {
+ STAC_INPUT_SOURCE(2),
+
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x0, 0x1a, 0x0, HDA_OUTPUT),
+
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1d, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1d, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x1, 0x1b, 0x0, HDA_OUTPUT),
+
+ HDA_CODEC_MUTE("Analog Loopback 1", 0x17, 0x3, HDA_INPUT),
+ HDA_CODEC_MUTE("Analog Loopback 2", 0x17, 0x4, HDA_INPUT),
+ { } /* end */
+};
+
+static struct snd_kcontrol_new stac92hd71bxx_mixer[] = {
+ STAC_INPUT_SOURCE(2),
+ STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2),
+
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x0, 0x1a, 0x0, HDA_OUTPUT),
+
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1d, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1d, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x1, 0x1b, 0x0, HDA_OUTPUT),
+ { } /* end */
+};
+
static struct snd_kcontrol_new stac925x_mixer[] = {
STAC_INPUT_SOURCE(1),
HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_OUTPUT),
@@ -428,16 +799,8 @@ static struct snd_kcontrol_new stac925x_mixer[] = {
};
static struct snd_kcontrol_new stac9205_mixer[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Digital Input Source",
- .count = 1,
- .info = stac92xx_dmux_enum_info,
- .get = stac92xx_dmux_enum_get,
- .put = stac92xx_dmux_enum_put,
- },
STAC_INPUT_SOURCE(2),
- STAC_ANALOG_LOOPBACK(0xFE0, 0x7E0),
+ STAC_ANALOG_LOOPBACK(0xFE0, 0x7E0, 1),
HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1b, 0x0, HDA_INPUT),
HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1d, 0x0, HDA_OUTPUT),
@@ -466,7 +829,7 @@ static struct snd_kcontrol_new stac922x_mixer[] = {
static struct snd_kcontrol_new stac927x_mixer[] = {
STAC_INPUT_SOURCE(3),
- STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB),
+ STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB, 1),
HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x18, 0x0, HDA_INPUT),
HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1b, 0x0, HDA_OUTPUT),
@@ -482,6 +845,44 @@ static struct snd_kcontrol_new stac927x_mixer[] = {
{ } /* end */
};
+static struct snd_kcontrol_new stac_dmux_mixer = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Digital Input Source",
+ /* count set later */
+ .info = stac92xx_dmux_enum_info,
+ .get = stac92xx_dmux_enum_get,
+ .put = stac92xx_dmux_enum_put,
+};
+
+static const char *slave_vols[] = {
+ "Front Playback Volume",
+ "Surround Playback Volume",
+ "Center Playback Volume",
+ "LFE Playback Volume",
+ "Side Playback Volume",
+ "Headphone Playback Volume",
+ "Headphone Playback Volume",
+ "Speaker Playback Volume",
+ "External Speaker Playback Volume",
+ "Speaker2 Playback Volume",
+ NULL
+};
+
+static const char *slave_sws[] = {
+ "Front Playback Switch",
+ "Surround Playback Switch",
+ "Center Playback Switch",
+ "LFE Playback Switch",
+ "Side Playback Switch",
+ "Headphone Playback Switch",
+ "Headphone Playback Switch",
+ "Speaker Playback Switch",
+ "External Speaker Playback Switch",
+ "Speaker2 Playback Switch",
+ "IEC958 Playback Switch",
+ NULL
+};
+
static int stac92xx_build_controls(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
@@ -497,6 +898,13 @@ static int stac92xx_build_controls(struct hda_codec *codec)
if (err < 0)
return err;
}
+ if (spec->num_dmuxes > 0) {
+ stac_dmux_mixer.count = spec->num_dmuxes;
+ err = snd_ctl_add(codec->bus->card,
+ snd_ctl_new1(&stac_dmux_mixer, codec));
+ if (err < 0)
+ return err;
+ }
if (spec->multiout.dig_out_nid) {
err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
@@ -508,6 +916,23 @@ static int stac92xx_build_controls(struct hda_codec *codec)
if (err < 0)
return err;
}
+
+ /* if we have no master control, let's create it */
+ if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
+ snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
+ HDA_OUTPUT, spec->vmaster_tlv);
+ err = snd_hda_add_vmaster(codec, "Master Playback Volume",
+ spec->vmaster_tlv, slave_vols);
+ if (err < 0)
+ return err;
+ }
+ if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
+ err = snd_hda_add_vmaster(codec, "Master Playback Switch",
+ NULL, slave_sws);
+ if (err < 0)
+ return err;
+ }
+
return 0;
}
@@ -733,7 +1158,7 @@ static struct snd_pci_quirk stac9200_cfg_tbl[] = {
static unsigned int ref925x_pin_configs[8] = {
0x40c003f0, 0x424503f2, 0x01813022, 0x02a19021,
- 0x90a70320, 0x02214210, 0x400003f1, 0x9033032e,
+ 0x90a70320, 0x02214210, 0x01019020, 0x9033032e,
};
static unsigned int stac925x_MA6_pin_configs[8] = {
@@ -777,6 +1202,48 @@ static struct snd_pci_quirk stac925x_cfg_tbl[] = {
{} /* terminator */
};
+static unsigned int ref92hd73xx_pin_configs[12] = {
+ 0x02214030, 0x02a19040, 0x01a19020, 0x02214030,
+ 0x0181302e, 0x01014010, 0x01014020, 0x01014030,
+ 0x02319040, 0x90a000f0, 0x90a000f0, 0x01452050,
+};
+
+static unsigned int *stac92hd73xx_brd_tbl[STAC_92HD73XX_MODELS] = {
+ [STAC_92HD73XX_REF] = ref92hd73xx_pin_configs,
+};
+
+static const char *stac92hd73xx_models[STAC_92HD73XX_MODELS] = {
+ [STAC_92HD73XX_REF] = "ref",
+};
+
+static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = {
+ /* SigmaTel reference board */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
+ "DFI LanParty", STAC_92HD73XX_REF),
+ {} /* terminator */
+};
+
+static unsigned int ref92hd71bxx_pin_configs[10] = {
+ 0x02214030, 0x02a19040, 0x01a19020, 0x01014010,
+ 0x0181302e, 0x01114010, 0x01019020, 0x90a000f0,
+ 0x90a000f0, 0x01452050,
+};
+
+static unsigned int *stac92hd71bxx_brd_tbl[STAC_92HD71BXX_MODELS] = {
+ [STAC_92HD71BXX_REF] = ref92hd71bxx_pin_configs,
+};
+
+static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = {
+ [STAC_92HD71BXX_REF] = "ref",
+};
+
+static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
+ /* SigmaTel reference board */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
+ "DFI LanParty", STAC_92HD71BXX_REF),
+ {} /* terminator */
+};
+
static unsigned int ref922x_pin_configs[10] = {
0x01014010, 0x01016011, 0x01012012, 0x0221401f,
0x01813122, 0x01011014, 0x01441030, 0x01c41030,
@@ -823,8 +1290,8 @@ static unsigned int dell_922x_m81_pin_configs[10] = {
102801D7 (Dell XPS M1210)
*/
static unsigned int dell_922x_m82_pin_configs[10] = {
- 0x0221121f, 0x408103ff, 0x02111212, 0x90100310,
- 0x408003f1, 0x02111211, 0x03451340, 0x40c003f2,
+ 0x02211211, 0x408103ff, 0x02a1123e, 0x90100310,
+ 0x408003f1, 0x0221121f, 0x03451340, 0x40c003f2,
0x508003f3, 0x405003f4,
};
@@ -1022,22 +1489,24 @@ static unsigned int d965_5st_pin_configs[14] = {
static unsigned int dell_3st_pin_configs[14] = {
0x02211230, 0x02a11220, 0x01a19040, 0x01114210,
0x01111212, 0x01116211, 0x01813050, 0x01112214,
- 0x403003fa, 0x40000100, 0x40000100, 0x404003fb,
+ 0x403003fa, 0x90a60040, 0x90a60040, 0x404003fb,
0x40c003fc, 0x40000100
};
static unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = {
- [STAC_D965_REF] = ref927x_pin_configs,
- [STAC_D965_3ST] = d965_3st_pin_configs,
- [STAC_D965_5ST] = d965_5st_pin_configs,
- [STAC_DELL_3ST] = dell_3st_pin_configs,
+ [STAC_D965_REF] = ref927x_pin_configs,
+ [STAC_D965_3ST] = d965_3st_pin_configs,
+ [STAC_D965_5ST] = d965_5st_pin_configs,
+ [STAC_DELL_3ST] = dell_3st_pin_configs,
+ [STAC_DELL_BIOS] = NULL,
};
static const char *stac927x_models[STAC_927X_MODELS] = {
- [STAC_D965_REF] = "ref",
- [STAC_D965_3ST] = "3stack",
- [STAC_D965_5ST] = "5stack",
- [STAC_DELL_3ST] = "dell-3stack",
+ [STAC_D965_REF] = "ref",
+ [STAC_D965_3ST] = "3stack",
+ [STAC_D965_5ST] = "5stack",
+ [STAC_DELL_3ST] = "dell-3stack",
+ [STAC_DELL_BIOS] = "dell-bios",
};
static struct snd_pci_quirk stac927x_cfg_tbl[] = {
@@ -1064,13 +1533,21 @@ static struct snd_pci_quirk stac927x_cfg_tbl[] = {
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2003, "Intel D965", STAC_D965_3ST),
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2002, "Intel D965", STAC_D965_3ST),
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2001, "Intel D965", STAC_D965_3ST),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f3, "Dell Inspiron 1420", STAC_D965_3ST),
/* Dell 3 stack systems */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f7, "Dell XPS M1730", STAC_DELL_3ST),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01dd, "Dell Dimension E520", STAC_DELL_3ST),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ed, "Dell ", STAC_DELL_3ST),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f4, "Dell ", STAC_DELL_3ST),
+ /* Dell 3 stack systems with verb table in BIOS */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f3, "Dell Inspiron 1420", STAC_DELL_BIOS),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0227, "Dell Vostro 1400 ", STAC_DELL_BIOS),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022f, "Dell ", STAC_DELL_BIOS),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022e, "Dell ", STAC_DELL_BIOS),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0242, "Dell ", STAC_DELL_BIOS),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0243, "Dell ", STAC_DELL_BIOS),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ff, "Dell ", STAC_DELL_BIOS),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0209, "Dell XPS 1330", STAC_DELL_BIOS),
/* 965 based 5 stack systems */
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0209, "Dell XPS 1330", STAC_D965_5ST),
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2301, "Intel D965", STAC_D965_5ST),
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2302, "Intel D965", STAC_D965_5ST),
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2303, "Intel D965", STAC_D965_5ST),
@@ -1085,7 +1562,7 @@ static struct snd_pci_quirk stac927x_cfg_tbl[] = {
static unsigned int ref9205_pin_configs[12] = {
0x40000100, 0x40000100, 0x01016011, 0x01014010,
- 0x01813122, 0x01a19021, 0x40000100, 0x40000100,
+ 0x01813122, 0x01a19021, 0x01019020, 0x40000100,
0x90a000f0, 0x90a000f0, 0x01441030, 0x01c41030
};
@@ -1097,6 +1574,7 @@ static unsigned int ref9205_pin_configs[12] = {
102801FD
10280204
1028021F
+ 10280228 (Dell Vostro 1500)
*/
static unsigned int dell_9205_m42_pin_configs[12] = {
0x0321101F, 0x03A11020, 0x400003FA, 0x90170310,
@@ -1180,6 +1658,8 @@ static struct snd_pci_quirk stac9205_cfg_tbl[] = {
"unknown Dell", STAC_9205_DELL_M42),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021f,
"Dell Inspiron", STAC_9205_DELL_M44),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0228,
+ "Dell Vostro 1500", STAC_9205_DELL_M42),
{} /* terminator */
};
@@ -1245,22 +1725,6 @@ static void stac92xx_set_config_regs(struct hda_codec *codec)
spec->pin_configs[i]);
}
-static void stac92xx_enable_gpio_mask(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- /* Configure GPIOx as output */
- snd_hda_codec_write_cache(codec, codec->afg, 0,
- AC_VERB_SET_GPIO_DIRECTION, spec->gpio_mask);
- /* Configure GPIOx as CMOS */
- snd_hda_codec_write_cache(codec, codec->afg, 0, 0x7e7, 0x00000000);
- /* Assert GPIOx */
- snd_hda_codec_write_cache(codec, codec->afg, 0,
- AC_VERB_SET_GPIO_DATA, spec->gpio_data);
- /* Enable GPIOx */
- snd_hda_codec_write_cache(codec, codec->afg, 0,
- AC_VERB_SET_GPIO_MASK, spec->gpio_mask);
-}
-
/*
* Analog playback callbacks
*/
@@ -1479,7 +1943,7 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
struct sigmatel_spec *spec = codec->spec;
hda_nid_t nid = kcontrol->private_value >> 8;
int io_idx = kcontrol-> private_value & 0xff;
- unsigned short val = ucontrol->value.integer.value[0];
+ unsigned short val = !!ucontrol->value.integer.value[0];
spec->io_switch[io_idx] = val;
@@ -1491,6 +1955,13 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
pinctl |= stac92xx_get_vref(codec, nid);
stac92xx_auto_set_pinctl(codec, nid, pinctl);
}
+
+ /* check the auto-mute again: we need to mute/unmute the speaker
+ * appropriately according to the pin direction
+ */
+ if (spec->hp_detect)
+ codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
+
return 1;
}
@@ -1512,11 +1983,12 @@ static int stac92xx_clfe_switch_put(struct snd_kcontrol *kcontrol,
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec;
hda_nid_t nid = kcontrol->private_value & 0xff;
+ unsigned int val = !!ucontrol->value.integer.value[0];
- if (spec->clfe_swap == ucontrol->value.integer.value[0])
+ if (spec->clfe_swap == val)
return 0;
- spec->clfe_swap = ucontrol->value.integer.value[0];
+ spec->clfe_swap = val;
snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE,
spec->clfe_swap ? 0x4 : 0x0);
@@ -1547,6 +2019,7 @@ static int stac92xx_clfe_switch_put(struct snd_kcontrol *kcontrol,
enum {
STAC_CTL_WIDGET_VOL,
STAC_CTL_WIDGET_MUTE,
+ STAC_CTL_WIDGET_MONO_MUX,
STAC_CTL_WIDGET_IO_SWITCH,
STAC_CTL_WIDGET_CLFE_SWITCH
};
@@ -1554,6 +2027,7 @@ enum {
static struct snd_kcontrol_new stac92xx_control_templates[] = {
HDA_CODEC_VOLUME(NULL, 0, 0, 0),
HDA_CODEC_MUTE(NULL, 0, 0, 0),
+ STAC_MONO_MUX,
STAC_CODEC_IO_SWITCH(NULL, 0),
STAC_CODEC_CLFE_SWITCH(NULL, 0),
};
@@ -1598,6 +2072,7 @@ static int stac92xx_add_dyn_out_pins(struct hda_codec *codec, struct auto_pin_cf
for (i = 0; i < codec->num_nodes; i++) {
wcaps = codec->wcaps[i];
wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+
if (wtype == AC_WID_AUD_OUT && !(wcaps & AC_WCAP_DIGITAL))
num_dacs++;
}
@@ -1685,7 +2160,6 @@ static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec,
wcaps = snd_hda_param_read(codec, conn[j],
AC_PAR_AUDIO_WIDGET_CAP);
wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
-
if (wtype != AC_WID_AUD_OUT ||
(wcaps & AC_WCAP_DIGITAL))
continue;
@@ -1759,7 +2233,7 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
int i, err;
struct sigmatel_spec *spec = codec->spec;
- unsigned int wid_caps;
+ unsigned int wid_caps, pincap;
for (i = 0; i < cfg->line_outs; i++) {
@@ -1795,13 +2269,39 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
}
}
- if (spec->line_switch)
- if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH, "Line In as Output Switch", cfg->input_pins[AUTO_PIN_LINE] << 8)) < 0)
- return err;
+ if (spec->line_switch) {
+ nid = cfg->input_pins[AUTO_PIN_LINE];
+ pincap = snd_hda_param_read(codec, nid,
+ AC_PAR_PIN_CAP);
+ if (pincap & AC_PINCAP_OUT) {
+ err = stac92xx_add_control(spec,
+ STAC_CTL_WIDGET_IO_SWITCH,
+ "Line In as Output Switch", nid << 8);
+ if (err < 0)
+ return err;
+ }
+ }
- if (spec->mic_switch)
- if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH, "Mic as Output Switch", (cfg->input_pins[AUTO_PIN_MIC] << 8) | 1)) < 0)
- return err;
+ if (spec->mic_switch) {
+ unsigned int def_conf;
+ nid = cfg->input_pins[AUTO_PIN_MIC];
+ def_conf = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_CONFIG_DEFAULT, 0);
+
+ /* some laptops have an internal analog microphone
+ * which can't be used as a output */
+ if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED) {
+ pincap = snd_hda_param_read(codec, nid,
+ AC_PAR_PIN_CAP);
+ if (pincap & AC_PINCAP_OUT) {
+ err = stac92xx_add_control(spec,
+ STAC_CTL_WIDGET_IO_SWITCH,
+ "Mic as Output Switch", (nid << 8) | 1);
+ if (err < 0)
+ return err;
+ }
+ }
+ }
return 0;
}
@@ -1891,6 +2391,37 @@ static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec,
return 0;
}
+/* labels for mono mux outputs */
+static const char *stac92xx_mono_labels[3] = {
+ "DAC0", "DAC1", "Mixer"
+};
+
+/* create mono mux for mono out on capable codecs */
+static int stac92xx_auto_create_mono_output_ctls(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ struct hda_input_mux *mono_mux = &spec->private_mono_mux;
+ int i, num_cons;
+ hda_nid_t con_lst[ARRAY_SIZE(stac92xx_mono_labels)];
+
+ num_cons = snd_hda_get_connections(codec,
+ spec->mono_nid,
+ con_lst,
+ HDA_MAX_NUM_INPUTS);
+ if (!num_cons || num_cons > ARRAY_SIZE(stac92xx_mono_labels))
+ return -EINVAL;
+
+ for (i = 0; i < num_cons; i++) {
+ mono_mux->items[mono_mux->num_items].label =
+ stac92xx_mono_labels[i];
+ mono_mux->items[mono_mux->num_items].index = i;
+ mono_mux->num_items++;
+ }
+
+ return stac92xx_add_control(spec, STAC_CTL_WIDGET_MONO_MUX,
+ "Mono Mux", spec->mono_nid);
+}
+
/* labels for dmic mux inputs */
static const char *stac92xx_dmic_labels[5] = {
"Analog Inputs", "Digital Mic 1", "Digital Mic 2",
@@ -1904,15 +2435,18 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
struct sigmatel_spec *spec = codec->spec;
struct hda_input_mux *dimux = &spec->private_dimux;
hda_nid_t con_lst[HDA_MAX_NUM_INPUTS];
- int i, j;
+ int err, i, j;
+ char name[32];
dimux->items[dimux->num_items].label = stac92xx_dmic_labels[0];
dimux->items[dimux->num_items].index = 0;
dimux->num_items++;
for (i = 0; i < spec->num_dmics; i++) {
+ hda_nid_t nid;
int index;
int num_cons;
+ unsigned int wcaps;
unsigned int def_conf;
def_conf = snd_hda_codec_read(codec,
@@ -1923,17 +2457,32 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
continue;
+ nid = spec->dmic_nids[i];
num_cons = snd_hda_get_connections(codec,
- spec->dmux_nid,
+ spec->dmux_nids[0],
con_lst,
HDA_MAX_NUM_INPUTS);
for (j = 0; j < num_cons; j++)
- if (con_lst[j] == spec->dmic_nids[i]) {
+ if (con_lst[j] == nid) {
index = j;
goto found;
}
continue;
found:
+ wcaps = get_wcaps(codec, nid);
+
+ if (wcaps & AC_WCAP_OUT_AMP) {
+ sprintf(name, "%s Capture Volume",
+ stac92xx_dmic_labels[dimux->num_items]);
+
+ err = stac92xx_add_control(spec,
+ STAC_CTL_WIDGET_VOL,
+ name,
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ }
+
dimux->items[dimux->num_items].label =
stac92xx_dmic_labels[dimux->num_items];
dimux->items[dimux->num_items].index = index;
@@ -2026,6 +2575,7 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
{
struct sigmatel_spec *spec = codec->spec;
int err;
+ int hp_speaker_swap = 0;
if ((err = snd_hda_parse_pin_def_config(codec,
&spec->autocfg,
@@ -2034,6 +2584,68 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
if (! spec->autocfg.line_outs)
return 0; /* can't find valid pin config */
+ /* If we have no real line-out pin and multiple hp-outs, HPs should
+ * be set up as multi-channel outputs.
+ */
+ if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT &&
+ spec->autocfg.hp_outs > 1) {
+ /* Copy hp_outs to line_outs, backup line_outs in
+ * speaker_outs so that the following routines can handle
+ * HP pins as primary outputs.
+ */
+ memcpy(spec->autocfg.speaker_pins, spec->autocfg.line_out_pins,
+ sizeof(spec->autocfg.line_out_pins));
+ spec->autocfg.speaker_outs = spec->autocfg.line_outs;
+ memcpy(spec->autocfg.line_out_pins, spec->autocfg.hp_pins,
+ sizeof(spec->autocfg.hp_pins));
+ spec->autocfg.line_outs = spec->autocfg.hp_outs;
+ hp_speaker_swap = 1;
+ }
+ if (spec->autocfg.mono_out_pin) {
+ int dir = (get_wcaps(codec, spec->autocfg.mono_out_pin)
+ & AC_WCAP_OUT_AMP) ? HDA_OUTPUT : HDA_INPUT;
+ u32 caps = query_amp_caps(codec,
+ spec->autocfg.mono_out_pin, dir);
+ hda_nid_t conn_list[1];
+
+ /* get the mixer node and then the mono mux if it exists */
+ if (snd_hda_get_connections(codec,
+ spec->autocfg.mono_out_pin, conn_list, 1) &&
+ snd_hda_get_connections(codec, conn_list[0],
+ conn_list, 1)) {
+
+ int wcaps = get_wcaps(codec, conn_list[0]);
+ int wid_type = (wcaps & AC_WCAP_TYPE)
+ >> AC_WCAP_TYPE_SHIFT;
+ /* LR swap check, some stac925x have a mux that
+ * changes the DACs output path instead of the
+ * mono-mux path.
+ */
+ if (wid_type == AC_WID_AUD_SEL &&
+ !(wcaps & AC_WCAP_LR_SWAP))
+ spec->mono_nid = conn_list[0];
+ }
+ /* all mono outs have a least a mute/unmute switch */
+ err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE,
+ "Mono Playback Switch",
+ HDA_COMPOSE_AMP_VAL(spec->autocfg.mono_out_pin,
+ 1, 0, dir));
+ if (err < 0)
+ return err;
+ /* check to see if there is volume support for the amp */
+ if ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) {
+ err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL,
+ "Mono Playback Volume",
+ HDA_COMPOSE_AMP_VAL(spec->autocfg.mono_out_pin,
+ 1, 0, dir));
+ if (err < 0)
+ return err;
+ }
+
+ stac92xx_auto_set_pinctl(codec, spec->autocfg.mono_out_pin,
+ AC_PINCTL_OUT_EN);
+ }
+
if ((err = stac92xx_add_dyn_out_pins(codec, &spec->autocfg)) < 0)
return err;
if (spec->multiout.num_dacs == 0)
@@ -2045,6 +2657,19 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
if (err < 0)
return err;
+ if (hp_speaker_swap == 1) {
+ /* Restore the hp_outs and line_outs */
+ memcpy(spec->autocfg.hp_pins, spec->autocfg.line_out_pins,
+ sizeof(spec->autocfg.line_out_pins));
+ spec->autocfg.hp_outs = spec->autocfg.line_outs;
+ memcpy(spec->autocfg.line_out_pins, spec->autocfg.speaker_pins,
+ sizeof(spec->autocfg.speaker_pins));
+ spec->autocfg.line_outs = spec->autocfg.speaker_outs;
+ memset(spec->autocfg.speaker_pins, 0,
+ sizeof(spec->autocfg.speaker_pins));
+ spec->autocfg.speaker_outs = 0;
+ }
+
err = stac92xx_auto_create_hp_ctls(codec, &spec->autocfg);
if (err < 0)
@@ -2055,6 +2680,12 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
if (err < 0)
return err;
+ if (spec->mono_nid > 0) {
+ err = stac92xx_auto_create_mono_output_ctls(codec);
+ if (err < 0)
+ return err;
+ }
+
if (spec->num_dmics > 0)
if ((err = stac92xx_auto_create_dmic_input_ctls(codec,
&spec->autocfg)) < 0)
@@ -2073,7 +2704,9 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
spec->input_mux = &spec->private_imux;
- spec->dinput_mux = &spec->private_dimux;
+ if (!spec->dinput_mux)
+ spec->dinput_mux = &spec->private_dimux;
+ spec->mono_mux = &spec->private_mono_mux;
return 1;
}
@@ -2183,38 +2816,35 @@ static int stac9200_parse_auto_config(struct hda_codec *codec)
* funky external mute control using GPIO pins.
*/
-static void stac922x_gpio_mute(struct hda_codec *codec, int pin, int muted)
+static void stac_gpio_set(struct hda_codec *codec, unsigned int mask,
+ unsigned int dir_mask, unsigned int data)
{
unsigned int gpiostate, gpiomask, gpiodir;
gpiostate = snd_hda_codec_read(codec, codec->afg, 0,
AC_VERB_GET_GPIO_DATA, 0);
-
- if (!muted)
- gpiostate |= (1 << pin);
- else
- gpiostate &= ~(1 << pin);
+ gpiostate = (gpiostate & ~dir_mask) | (data & dir_mask);
gpiomask = snd_hda_codec_read(codec, codec->afg, 0,
AC_VERB_GET_GPIO_MASK, 0);
- gpiomask |= (1 << pin);
+ gpiomask |= mask;
gpiodir = snd_hda_codec_read(codec, codec->afg, 0,
AC_VERB_GET_GPIO_DIRECTION, 0);
- gpiodir |= (1 << pin);
+ gpiodir |= dir_mask;
- /* AppleHDA seems to do this -- WTF is this verb?? */
+ /* Configure GPIOx as CMOS */
snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0);
snd_hda_codec_write(codec, codec->afg, 0,
AC_VERB_SET_GPIO_MASK, gpiomask);
- snd_hda_codec_write(codec, codec->afg, 0,
- AC_VERB_SET_GPIO_DIRECTION, gpiodir);
+ snd_hda_codec_read(codec, codec->afg, 0,
+ AC_VERB_SET_GPIO_DIRECTION, gpiodir); /* sync */
msleep(1);
- snd_hda_codec_write(codec, codec->afg, 0,
- AC_VERB_SET_GPIO_DATA, gpiostate);
+ snd_hda_codec_read(codec, codec->afg, 0,
+ AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */
}
static void enable_pin_detect(struct hda_codec *codec, hda_nid_t nid,
@@ -2226,6 +2856,16 @@ static void enable_pin_detect(struct hda_codec *codec, hda_nid_t nid,
(AC_USRSP_EN | event));
}
+static int is_nid_hp_pin(struct auto_pin_cfg *cfg, hda_nid_t nid)
+{
+ int i;
+ for (i = 0; i < cfg->hp_outs; i++)
+ if (cfg->hp_pins[i] == nid)
+ return 1; /* nid is a HP-Out */
+
+ return 0; /* nid is not a HP-Out */
+};
+
static int stac92xx_init(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
@@ -2261,10 +2901,23 @@ static int stac92xx_init(struct hda_codec *codec)
stac92xx_auto_set_pinctl(codec, nid, pinctl);
}
}
- if (spec->num_dmics > 0)
- for (i = 0; i < spec->num_dmics; i++)
- stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i],
- AC_PINCTL_IN_EN);
+ for (i = 0; i < spec->num_dmics; i++)
+ stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i],
+ AC_PINCTL_IN_EN);
+ for (i = 0; i < spec->num_pwrs; i++) {
+ int event = is_nid_hp_pin(cfg, spec->pwr_nids[i])
+ ? STAC_HP_EVENT : STAC_PWR_EVENT;
+ int pinctl = snd_hda_codec_read(codec, spec->pwr_nids[i],
+ 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ /* outputs are only ports capable of power management
+ * any attempts on powering down a input port cause the
+ * referenced VREF to act quirky.
+ */
+ if (pinctl & AC_PINCTL_IN_EN)
+ continue;
+ enable_pin_detect(codec, spec->pwr_nids[i], event | i);
+ codec->patch_ops.unsol_event(codec, (event | i) << 26);
+ }
if (cfg->dig_out_pin)
stac92xx_auto_set_pinctl(codec, cfg->dig_out_pin,
@@ -2273,10 +2926,8 @@ static int stac92xx_init(struct hda_codec *codec)
stac92xx_auto_set_pinctl(codec, cfg->dig_in_pin,
AC_PINCTL_IN_EN);
- if (spec->gpio_mute) {
- stac922x_gpio_mute(codec, 0, 0);
- stac922x_gpio_mute(codec, 1, 0);
- }
+ stac_gpio_set(codec, spec->gpio_mask,
+ spec->gpio_dir, spec->gpio_data);
return 0;
}
@@ -2342,13 +2993,20 @@ static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid,
pin_ctl & ~flag);
}
-static int get_pin_presence(struct hda_codec *codec, hda_nid_t nid)
+static int get_hp_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;
+ & (1 << 31)) {
+ unsigned int pinctl;
+ pinctl = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ if (pinctl & AC_PINCTL_IN_EN)
+ return 0; /* mic- or line-input */
+ else
+ return 1; /* HP-output */
+ }
return 0;
}
@@ -2359,10 +3017,14 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
int i, presence;
presence = 0;
+ if (spec->gpio_mute)
+ presence = !(snd_hda_codec_read(codec, codec->afg, 0,
+ AC_VERB_GET_GPIO_DATA, 0) & spec->gpio_mute);
+
for (i = 0; i < cfg->hp_outs; i++) {
- presence = get_pin_presence(codec, cfg->hp_pins[i]);
if (presence)
break;
+ presence = get_hp_pin_presence(codec, cfg->hp_pins[i]);
}
if (presence) {
@@ -2384,12 +3046,37 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
}
}
+static void stac92xx_pin_sense(struct hda_codec *codec, int idx)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ hda_nid_t nid = spec->pwr_nids[idx];
+ int presence, val;
+ val = snd_hda_codec_read(codec, codec->afg, 0, 0x0fec, 0x0)
+ & 0x000000ff;
+ presence = get_hp_pin_presence(codec, nid);
+ idx = 1 << idx;
+
+ if (presence)
+ val &= ~idx;
+ else
+ val |= idx;
+
+ /* power down unused output ports */
+ snd_hda_codec_write(codec, codec->afg, 0, 0x7ec, val);
+};
+
static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
{
- switch (res >> 26) {
+ struct sigmatel_spec *spec = codec->spec;
+ int idx = res >> 26 & 0x0f;
+
+ switch ((res >> 26) & 0x30) {
case STAC_HP_EVENT:
stac92xx_hp_detect(codec, res);
- break;
+ /* fallthru */
+ case STAC_PWR_EVENT:
+ if (spec->num_pwrs > 0)
+ stac92xx_pin_sense(codec, idx);
}
}
@@ -2400,10 +3087,8 @@ static int stac92xx_resume(struct hda_codec *codec)
stac92xx_set_config_regs(codec);
snd_hda_sequence_write(codec, spec->init);
- if (spec->gpio_mute) {
- stac922x_gpio_mute(codec, 0, 0);
- stac922x_gpio_mute(codec, 1, 0);
- }
+ stac_gpio_set(codec, spec->gpio_mask,
+ spec->gpio_dir, spec->gpio_data);
snd_hda_codec_resume_amp(codec);
snd_hda_codec_resume_cache(codec);
/* invoke unsolicited event to reset the HP state */
@@ -2460,6 +3145,7 @@ static int patch_stac9200(struct hda_codec *codec)
spec->num_muxes = 1;
spec->num_dmics = 0;
spec->num_adcs = 1;
+ spec->num_pwrs = 0;
if (spec->board_config == STAC_9200_GATEWAY)
spec->init = stac9200_eapd_init;
@@ -2515,6 +3201,7 @@ static int patch_stac925x(struct hda_codec *codec)
spec->mux_nids = stac925x_mux_nids;
spec->num_muxes = 1;
spec->num_adcs = 1;
+ spec->num_pwrs = 0;
switch (codec->vendor_id) {
case 0x83847632: /* STAC9202 */
case 0x83847633: /* STAC9202D */
@@ -2522,6 +3209,8 @@ static int patch_stac925x(struct hda_codec *codec)
case 0x83847637: /* STAC9251D */
spec->num_dmics = STAC925X_NUM_DMICS;
spec->dmic_nids = stac925x_dmic_nids;
+ spec->num_dmuxes = ARRAY_SIZE(stac925x_dmux_nids);
+ spec->dmux_nids = stac925x_dmux_nids;
break;
default:
spec->num_dmics = 0;
@@ -2551,6 +3240,204 @@ static int patch_stac925x(struct hda_codec *codec)
return 0;
}
+static struct hda_input_mux stac92hd73xx_dmux = {
+ .num_items = 4,
+ .items = {
+ { "Analog Inputs", 0x0b },
+ { "CD", 0x08 },
+ { "Digital Mic 1", 0x09 },
+ { "Digital Mic 2", 0x0a },
+ }
+};
+
+static int patch_stac92hd73xx(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec;
+ hda_nid_t conn[STAC92HD73_DAC_COUNT + 2];
+ int err = 0;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+ spec->num_pins = ARRAY_SIZE(stac92hd73xx_pin_nids);
+ spec->pin_nids = stac92hd73xx_pin_nids;
+ spec->board_config = snd_hda_check_board_config(codec,
+ STAC_92HD73XX_MODELS,
+ stac92hd73xx_models,
+ stac92hd73xx_cfg_tbl);
+again:
+ if (spec->board_config < 0) {
+ snd_printdd(KERN_INFO "hda_codec: Unknown model for"
+ " STAC92HD73XX, 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 = stac92hd73xx_brd_tbl[spec->board_config];
+ stac92xx_set_config_regs(codec);
+ }
+
+ spec->multiout.num_dacs = snd_hda_get_connections(codec, 0x0a,
+ conn, STAC92HD73_DAC_COUNT + 2) - 1;
+
+ if (spec->multiout.num_dacs < 0) {
+ printk(KERN_WARNING "hda_codec: Could not determine "
+ "number of channels defaulting to DAC count\n");
+ spec->multiout.num_dacs = STAC92HD73_DAC_COUNT;
+ }
+
+ switch (spec->multiout.num_dacs) {
+ case 0x3: /* 6 Channel */
+ spec->mixer = stac92hd73xx_6ch_mixer;
+ spec->init = stac92hd73xx_6ch_core_init;
+ break;
+ case 0x4: /* 8 Channel */
+ spec->multiout.hp_nid = 0x18;
+ spec->mixer = stac92hd73xx_8ch_mixer;
+ spec->init = stac92hd73xx_8ch_core_init;
+ break;
+ case 0x5: /* 10 Channel */
+ spec->multiout.hp_nid = 0x19;
+ spec->mixer = stac92hd73xx_10ch_mixer;
+ spec->init = stac92hd73xx_10ch_core_init;
+ };
+
+ spec->multiout.dac_nids = stac92hd73xx_dac_nids;
+ spec->aloopback_mask = 0x01;
+ spec->aloopback_shift = 8;
+
+ spec->mux_nids = stac92hd73xx_mux_nids;
+ spec->adc_nids = stac92hd73xx_adc_nids;
+ spec->dmic_nids = stac92hd73xx_dmic_nids;
+ spec->dmux_nids = stac92hd73xx_dmux_nids;
+
+ spec->num_muxes = ARRAY_SIZE(stac92hd73xx_mux_nids);
+ spec->num_adcs = ARRAY_SIZE(stac92hd73xx_adc_nids);
+ spec->num_dmics = STAC92HD73XX_NUM_DMICS;
+ spec->num_dmuxes = ARRAY_SIZE(stac92hd73xx_dmux_nids);
+ spec->dinput_mux = &stac92hd73xx_dmux;
+ /* GPIO0 High = Enable EAPD */
+ spec->gpio_mask = spec->gpio_dir = 0x1;
+ spec->gpio_data = 0x01;
+
+ spec->num_pwrs = ARRAY_SIZE(stac92hd73xx_pwr_nids);
+ spec->pwr_nids = stac92hd73xx_pwr_nids;
+
+ err = stac92xx_parse_auto_config(codec, 0x22, 0x24);
+
+ if (!err) {
+ if (spec->board_config < 0) {
+ printk(KERN_WARNING "hda_codec: No auto-config is "
+ "available, default to model=ref\n");
+ spec->board_config = STAC_92HD73XX_REF;
+ goto again;
+ }
+ err = -EINVAL;
+ }
+
+ if (err < 0) {
+ stac92xx_free(codec);
+ return err;
+ }
+
+ codec->patch_ops = stac92xx_patch_ops;
+
+ return 0;
+}
+
+static int patch_stac92hd71bxx(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec;
+ int err = 0;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+ spec->num_pins = ARRAY_SIZE(stac92hd71bxx_pin_nids);
+ spec->pin_nids = stac92hd71bxx_pin_nids;
+ spec->board_config = snd_hda_check_board_config(codec,
+ STAC_92HD71BXX_MODELS,
+ stac92hd71bxx_models,
+ stac92hd71bxx_cfg_tbl);
+again:
+ if (spec->board_config < 0) {
+ snd_printdd(KERN_INFO "hda_codec: Unknown model for"
+ " STAC92HD71BXX, 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 = stac92hd71bxx_brd_tbl[spec->board_config];
+ stac92xx_set_config_regs(codec);
+ }
+
+ switch (codec->vendor_id) {
+ case 0x111d76b6: /* 4 Port without Analog Mixer */
+ case 0x111d76b7:
+ case 0x111d76b4: /* 6 Port without Analog Mixer */
+ case 0x111d76b5:
+ spec->mixer = stac92hd71bxx_mixer;
+ spec->init = stac92hd71bxx_core_init;
+ break;
+ default:
+ spec->mixer = stac92hd71bxx_analog_mixer;
+ spec->init = stac92hd71bxx_analog_core_init;
+ }
+
+ spec->aloopback_mask = 0x20;
+ spec->aloopback_shift = 0;
+
+ /* GPIO0 High = EAPD */
+ spec->gpio_mask = spec->gpio_dir = spec->gpio_data = 0x1;
+
+ spec->mux_nids = stac92hd71bxx_mux_nids;
+ spec->adc_nids = stac92hd71bxx_adc_nids;
+ spec->dmic_nids = stac92hd71bxx_dmic_nids;
+ spec->dmux_nids = stac92hd71bxx_dmux_nids;
+
+ spec->num_muxes = ARRAY_SIZE(stac92hd71bxx_mux_nids);
+ spec->num_adcs = ARRAY_SIZE(stac92hd71bxx_adc_nids);
+ spec->num_dmics = STAC92HD71BXX_NUM_DMICS;
+ spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids);
+
+ spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids);
+ spec->pwr_nids = stac92hd71bxx_pwr_nids;
+
+ spec->multiout.num_dacs = 2;
+ spec->multiout.hp_nid = 0x11;
+ spec->multiout.dac_nids = stac92hd71bxx_dac_nids;
+
+ err = stac92xx_parse_auto_config(codec, 0x21, 0x23);
+ if (!err) {
+ if (spec->board_config < 0) {
+ printk(KERN_WARNING "hda_codec: No auto-config is "
+ "available, default to model=ref\n");
+ spec->board_config = STAC_92HD71BXX_REF;
+ goto again;
+ }
+ err = -EINVAL;
+ }
+
+ if (err < 0) {
+ stac92xx_free(codec);
+ return err;
+ }
+
+ codec->patch_ops = stac92xx_patch_ops;
+
+ return 0;
+};
+
static int patch_stac922x(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
@@ -2567,7 +3454,8 @@ static int patch_stac922x(struct hda_codec *codec)
stac922x_models,
stac922x_cfg_tbl);
if (spec->board_config == STAC_INTEL_MAC_V3) {
- spec->gpio_mute = 1;
+ spec->gpio_mask = spec->gpio_dir = 0x03;
+ spec->gpio_data = 0x03;
/* Intel Macs have all same PCI SSID, so we need to check
* codec SSID to distinguish the exact models
*/
@@ -2620,6 +3508,7 @@ static int patch_stac922x(struct hda_codec *codec)
spec->num_muxes = ARRAY_SIZE(stac922x_mux_nids);
spec->num_adcs = ARRAY_SIZE(stac922x_adc_nids);
spec->num_dmics = 0;
+ spec->num_pwrs = 0;
spec->init = stac922x_core_init;
spec->mixer = stac922x_mixer;
@@ -2669,53 +3558,70 @@ static int patch_stac927x(struct hda_codec *codec)
stac927x_models,
stac927x_cfg_tbl);
again:
- if (spec->board_config < 0) {
- snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC927x, using BIOS defaults\n");
+ if (spec->board_config < 0 || !stac927x_brd_tbl[spec->board_config]) {
+ if (spec->board_config < 0)
+ snd_printdd(KERN_INFO "hda_codec: Unknown model for"
+ "STAC927x, 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 (stac927x_brd_tbl[spec->board_config] != NULL) {
+ } else {
spec->pin_configs = stac927x_brd_tbl[spec->board_config];
stac92xx_set_config_regs(codec);
}
+ spec->adc_nids = stac927x_adc_nids;
+ spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids);
+ spec->mux_nids = stac927x_mux_nids;
+ spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids);
+ spec->multiout.dac_nids = spec->dac_nids;
+
switch (spec->board_config) {
case STAC_D965_3ST:
- spec->adc_nids = stac927x_adc_nids;
- spec->mux_nids = stac927x_mux_nids;
- spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids);
- spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids);
+ case STAC_D965_5ST:
+ /* GPIO0 High = Enable EAPD */
+ spec->gpio_mask = spec->gpio_dir = 0x01;
+ spec->gpio_data = 0x01;
spec->num_dmics = 0;
+
spec->init = d965_core_init;
spec->mixer = stac927x_mixer;
break;
- case STAC_D965_5ST:
- spec->adc_nids = stac927x_adc_nids;
- spec->mux_nids = stac927x_mux_nids;
- spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids);
- spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids);
- spec->num_dmics = 0;
+ case STAC_DELL_BIOS:
+ /* correct the front output jack as a hp out */
+ stac92xx_set_config_reg(codec, 0x0f, 0x02270110);
+ /* correct the front input jack as a mic */
+ stac92xx_set_config_reg(codec, 0x0e, 0x02a79130);
+ /* fallthru */
+ case STAC_DELL_3ST:
+ /* GPIO2 High = Enable EAPD */
+ spec->gpio_mask = spec->gpio_dir = 0x04;
+ spec->gpio_data = 0x04;
+ spec->dmic_nids = stac927x_dmic_nids;
+ spec->num_dmics = STAC927X_NUM_DMICS;
+
spec->init = d965_core_init;
spec->mixer = stac927x_mixer;
+ spec->dmux_nids = stac927x_dmux_nids;
+ spec->num_dmuxes = ARRAY_SIZE(stac927x_dmux_nids);
break;
default:
- spec->adc_nids = stac927x_adc_nids;
- spec->mux_nids = stac927x_mux_nids;
- spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids);
- spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids);
+ /* GPIO0 High = Enable EAPD */
+ spec->gpio_mask = spec->gpio_dir = 0x1;
+ spec->gpio_data = 0x01;
spec->num_dmics = 0;
+
spec->init = stac927x_core_init;
spec->mixer = stac927x_mixer;
}
- spec->multiout.dac_nids = spec->dac_nids;
- /* GPIO0 High = Enable EAPD */
- spec->gpio_mask = spec->gpio_data = 0x00000001;
- stac92xx_enable_gpio_mask(codec);
-
+ spec->num_pwrs = 0;
+ spec->aloopback_mask = 0x40;
+ spec->aloopback_shift = 0;
+
err = stac92xx_parse_auto_config(codec, 0x1e, 0x20);
if (!err) {
if (spec->board_config < 0) {
@@ -2733,6 +3639,18 @@ static int patch_stac927x(struct hda_codec *codec)
codec->patch_ops = stac92xx_patch_ops;
+ /*
+ * !!FIXME!!
+ * The STAC927x seem to require fairly long delays for certain
+ * command sequences. With too short delays (even if the answer
+ * is set to RIRB properly), it results in the silence output
+ * on some hardwares like Dell.
+ *
+ * The below flag enables the longer delay (see get_response
+ * in hda_intel.c).
+ */
+ codec->bus->needs_damn_long_delay = 1;
+
return 0;
}
@@ -2771,11 +3689,15 @@ static int patch_stac9205(struct hda_codec *codec)
spec->num_muxes = ARRAY_SIZE(stac9205_mux_nids);
spec->dmic_nids = stac9205_dmic_nids;
spec->num_dmics = STAC9205_NUM_DMICS;
- spec->dmux_nid = 0x1d;
+ spec->dmux_nids = stac9205_dmux_nids;
+ spec->num_dmuxes = ARRAY_SIZE(stac9205_dmux_nids);
+ spec->num_pwrs = 0;
spec->init = stac9205_core_init;
spec->mixer = stac9205_mixer;
+ spec->aloopback_mask = 0x40;
+ spec->aloopback_shift = 0;
spec->multiout.dac_nids = spec->dac_nids;
switch (spec->board_config){
@@ -2784,19 +3706,28 @@ static int patch_stac9205(struct hda_codec *codec)
stac92xx_set_config_reg(codec, 0x1f, 0x01441030);
stac92xx_set_config_reg(codec, 0x20, 0x1c410030);
- spec->gpio_mask = 0x00000007; /* GPIO0-2 */
- /* GPIO0 High = EAPD, GPIO1 Low = DRM,
- * GPIO2 High = Headphone Mute
+ /* Enable unsol response for GPIO4/Dock HP connection */
+ snd_hda_codec_write(codec, codec->afg, 0,
+ AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10);
+ snd_hda_codec_write_cache(codec, codec->afg, 0,
+ AC_VERB_SET_UNSOLICITED_ENABLE,
+ (AC_USRSP_EN | STAC_HP_EVENT));
+
+ spec->gpio_dir = 0x0b;
+ spec->gpio_mask = 0x1b;
+ spec->gpio_mute = 0x10;
+ /* GPIO0 High = EAPD, GPIO1 Low = Headphone Mute,
+ * GPIO3 Low = DRM
*/
- spec->gpio_data = 0x00000005;
+ spec->gpio_data = 0x01;
break;
default:
/* GPIO0 High = EAPD */
- spec->gpio_mask = spec->gpio_data = 0x00000001;
+ spec->gpio_mask = spec->gpio_dir = 0x1;
+ spec->gpio_data = 0x01;
break;
}
- stac92xx_enable_gpio_mask(codec);
err = stac92xx_parse_auto_config(codec, 0x1f, 0x20);
if (!err) {
if (spec->board_config < 0) {
@@ -2950,7 +3881,7 @@ static int stac9872_vaio_init(struct hda_codec *codec)
static void stac9872_vaio_hp_detect(struct hda_codec *codec, unsigned int res)
{
- if (get_pin_presence(codec, 0x0a)) {
+ if (get_hp_pin_presence(codec, 0x0a)) {
stac92xx_reset_pinctl(codec, 0x0f, AC_PINCTL_OUT_EN);
stac92xx_set_pinctl(codec, 0x0a, AC_PINCTL_OUT_EN);
} else {
@@ -3032,6 +3963,7 @@ static int patch_stac9872(struct hda_codec *codec)
spec->multiout.hp_nid = VAIO_HP_DAC;
spec->num_adcs = ARRAY_SIZE(vaio_adcs);
spec->adc_nids = vaio_adcs;
+ spec->num_pwrs = 0;
spec->input_mux = &vaio_mux;
spec->mux_nids = vaio_mux_nids;
codec->patch_ops = stac9872_vaio_patch_ops;
@@ -3045,6 +3977,7 @@ static int patch_stac9872(struct hda_codec *codec)
spec->multiout.dac_nids = vaio_dacs;
spec->multiout.hp_nid = VAIO_HP_DAC;
spec->num_adcs = ARRAY_SIZE(vaio_adcs);
+ spec->num_pwrs = 0;
spec->adc_nids = vaio_adcs;
spec->input_mux = &vaio_mux;
spec->mux_nids = vaio_mux_nids;
@@ -3104,5 +4037,17 @@ struct hda_codec_preset snd_hda_preset_sigmatel[] = {
{ .id = 0x838476a5, .name = "STAC9255D", .patch = patch_stac9205 },
{ .id = 0x838476a6, .name = "STAC9254", .patch = patch_stac9205 },
{ .id = 0x838476a7, .name = "STAC9254D", .patch = patch_stac9205 },
+ { .id = 0x111d7674, .name = "92HD73D1X5", .patch = patch_stac92hd73xx },
+ { .id = 0x111d7675, .name = "92HD73C1X5", .patch = patch_stac92hd73xx },
+ { .id = 0x111d7676, .name = "92HD73E1X5", .patch = patch_stac92hd73xx },
+ { .id = 0x111d7608, .name = "92HD71BXX", .patch = patch_stac92hd71bxx },
+ { .id = 0x111d76b0, .name = "92HD71B8X", .patch = patch_stac92hd71bxx },
+ { .id = 0x111d76b1, .name = "92HD71B8X", .patch = patch_stac92hd71bxx },
+ { .id = 0x111d76b2, .name = "92HD71B7X", .patch = patch_stac92hd71bxx },
+ { .id = 0x111d76b3, .name = "92HD71B7X", .patch = patch_stac92hd71bxx },
+ { .id = 0x111d76b4, .name = "92HD71B6X", .patch = patch_stac92hd71bxx },
+ { .id = 0x111d76b5, .name = "92HD71B6X", .patch = patch_stac92hd71bxx },
+ { .id = 0x111d76b6, .name = "92HD71B5X", .patch = patch_stac92hd71bxx },
+ { .id = 0x111d76b7, .name = "92HD71B5X", .patch = patch_stac92hd71bxx },
{} /* terminator */
};
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index 4cdf3e6df4b..4e5dd4cf36f 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -27,11 +27,12 @@
/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */
/* 2006-08-02 Lydia Wang Add support to VT1709 codec */
/* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */
+/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */
+/* 2007-09-17 Lydia Wang Add VT1708B codec support */
/* */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
@@ -51,14 +52,23 @@
#define VT1708_HP_NID 0x13
#define VT1708_DIGOUT_NID 0x14
#define VT1708_DIGIN_NID 0x16
+#define VT1708_DIGIN_PIN 0x26
#define VT1709_HP_DAC_NID 0x28
#define VT1709_DIGOUT_NID 0x13
#define VT1709_DIGIN_NID 0x17
+#define VT1709_DIGIN_PIN 0x25
+
+#define VT1708B_HP_NID 0x25
+#define VT1708B_DIGOUT_NID 0x12
+#define VT1708B_DIGIN_NID 0x15
+#define VT1708B_DIGIN_PIN 0x21
#define IS_VT1708_VENDORID(x) ((x) >= 0x11061708 && (x) <= 0x1106170b)
#define IS_VT1709_10CH_VENDORID(x) ((x) >= 0x1106e710 && (x) <= 0x1106e713)
#define IS_VT1709_6CH_VENDORID(x) ((x) >= 0x1106e714 && (x) <= 0x1106e717)
+#define IS_VT1708B_8CH_VENDORID(x) ((x) >= 0x1106e720 && (x) <= 0x1106e723)
+#define IS_VT1708B_4CH_VENDORID(x) ((x) >= 0x1106e724 && (x) <= 0x1106e727)
enum {
@@ -131,6 +141,11 @@ static hda_nid_t vt1709_adc_nids[3] = {
0x14, 0x15, 0x16
};
+static hda_nid_t vt1708B_adc_nids[2] = {
+ /* ADC1-2 */
+ 0x13, 0x14
+};
+
/* add dynamic controls */
static int via_add_control(struct via_spec *spec, int type, const char *name,
unsigned long val)
@@ -268,9 +283,13 @@ static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
0x18, &spec->cur_mux[adc_idx]);
else if ((IS_VT1709_10CH_VENDORID(vendor_id) ||
- IS_VT1709_6CH_VENDORID(vendor_id)) && (adc_idx == 0) )
+ IS_VT1709_6CH_VENDORID(vendor_id)) && adc_idx == 0)
return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
0x19, &spec->cur_mux[adc_idx]);
+ else if ((IS_VT1708B_8CH_VENDORID(vendor_id) ||
+ IS_VT1708B_4CH_VENDORID(vendor_id)) && adc_idx == 0)
+ return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
+ 0x17, &spec->cur_mux[adc_idx]);
else
return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
spec->adc_nids[adc_idx],
@@ -287,7 +306,6 @@ static struct snd_kcontrol_new vt1708_capture_mixer[] = {
.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",
@@ -309,15 +327,15 @@ static struct hda_verb vt1708_volume_init_verbs[] = {
{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+ /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
* mixer widget
*/
/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* master */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
/*
* Set up output mixers (0x19 - 0x1b)
@@ -329,10 +347,9 @@ static struct hda_verb vt1708_volume_init_verbs[] = {
/* Setup default input to PW4 */
{0x20, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* Set mic as default input of sw0 */
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x2},
/* PW9 Output enable */
{0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+ { }
};
static int via_playback_pcm_open(struct hda_pcm_stream *hinfo,
@@ -544,6 +561,33 @@ static int via_init(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
snd_hda_sequence_write(codec, spec->init_verbs);
+ /* Lydia Add for EAPD enable */
+ if (!spec->dig_in_nid) { /* No Digital In connection */
+ if (IS_VT1708_VENDORID(codec->vendor_id)) {
+ snd_hda_codec_write(codec, VT1708_DIGIN_PIN, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ PIN_OUT);
+ snd_hda_codec_write(codec, VT1708_DIGIN_PIN, 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x02);
+ } else if (IS_VT1709_10CH_VENDORID(codec->vendor_id) ||
+ IS_VT1709_6CH_VENDORID(codec->vendor_id)) {
+ snd_hda_codec_write(codec, VT1709_DIGIN_PIN, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ PIN_OUT);
+ snd_hda_codec_write(codec, VT1709_DIGIN_PIN, 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x02);
+ } else if (IS_VT1708B_8CH_VENDORID(codec->vendor_id) ||
+ IS_VT1708B_4CH_VENDORID(codec->vendor_id)) {
+ snd_hda_codec_write(codec, VT1708B_DIGIN_PIN, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ PIN_OUT);
+ snd_hda_codec_write(codec, VT1708B_DIGIN_PIN, 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x02);
+ }
+ } else /* enable SPDIF-input pin */
+ snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
+
return 0;
}
@@ -623,58 +667,68 @@ static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec,
if (i == AUTO_SEQ_CENLFE) {
/* Center/LFE */
err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Center Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT));
+ "Center Playback Volume",
+ HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
+ HDA_OUTPUT));
if (err < 0)
return err;
err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
"LFE Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT));
+ HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
+ HDA_OUTPUT));
if (err < 0)
return err;
err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
"Center Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT));
+ HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
+ HDA_OUTPUT));
if (err < 0)
return err;
err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
"LFE Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT));
+ HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
+ HDA_OUTPUT));
if (err < 0)
return err;
} else if (i == AUTO_SEQ_FRONT){
/* add control to mixer index 0 */
err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
"Master Front Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_INPUT));
+ HDA_COMPOSE_AMP_VAL(0x17, 3, 0,
+ HDA_INPUT));
if (err < 0)
return err;
err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
"Master Front Playback Switch",
- HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_INPUT));
+ HDA_COMPOSE_AMP_VAL(0x17, 3, 0,
+ HDA_INPUT));
if (err < 0)
return err;
/* add control to PW3 */
sprintf(name, "%s Playback Volume", chname[i]);
err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0,
+ HDA_OUTPUT));
if (err < 0)
return err;
sprintf(name, "%s Playback Switch", chname[i]);
err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0,
+ HDA_OUTPUT));
if (err < 0)
return err;
} else {
sprintf(name, "%s Playback Volume", chname[i]);
err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
+ HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
+ HDA_OUTPUT));
if (err < 0)
return err;
sprintf(name, "%s Playback Switch", chname[i]);
err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
+ HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
+ HDA_OUTPUT));
if (err < 0)
return err;
}
@@ -875,7 +929,6 @@ static struct snd_kcontrol_new vt1709_capture_mixer[] = {
.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",
@@ -899,15 +952,15 @@ static struct hda_verb vt1709_10ch_volume_init_verbs[] = {
{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+ /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
* mixer widget
*/
/* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* unmute master */
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
/*
* Set up output selector (0x1a, 0x1b, 0x29)
@@ -925,8 +978,6 @@ static struct hda_verb vt1709_10ch_volume_init_verbs[] = {
/* Set input of PW4 as AOW4 */
{0x20, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* Set mic as default input of sw0 */
- {0x19, AC_VERB_SET_CONNECT_SEL, 0x2},
/* PW9 Output enable */
{0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
{ }
@@ -1073,68 +1124,80 @@ static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec,
/* Center/LFE */
err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
"Center Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x1b, 1, 0, HDA_OUTPUT));
+ HDA_COMPOSE_AMP_VAL(0x1b, 1, 0,
+ HDA_OUTPUT));
if (err < 0)
return err;
err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
"LFE Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x1b, 2, 0, HDA_OUTPUT));
+ HDA_COMPOSE_AMP_VAL(0x1b, 2, 0,
+ HDA_OUTPUT));
if (err < 0)
return err;
err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
"Center Playback Switch",
- HDA_COMPOSE_AMP_VAL(0x1b, 1, 0, HDA_OUTPUT));
+ HDA_COMPOSE_AMP_VAL(0x1b, 1, 0,
+ HDA_OUTPUT));
if (err < 0)
return err;
err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
"LFE Playback Switch",
- HDA_COMPOSE_AMP_VAL(0x1b, 2, 0, HDA_OUTPUT));
+ HDA_COMPOSE_AMP_VAL(0x1b, 2, 0,
+ HDA_OUTPUT));
if (err < 0)
return err;
} else if (i == AUTO_SEQ_FRONT){
/* add control to mixer index 0 */
err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
"Master Front Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_INPUT));
+ HDA_COMPOSE_AMP_VAL(0x18, 3, 0,
+ HDA_INPUT));
if (err < 0)
return err;
err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
"Master Front Playback Switch",
- HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_INPUT));
+ HDA_COMPOSE_AMP_VAL(0x18, 3, 0,
+ HDA_INPUT));
if (err < 0)
return err;
/* add control to PW3 */
sprintf(name, "%s Playback Volume", chname[i]);
err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0,
+ HDA_OUTPUT));
if (err < 0)
return err;
sprintf(name, "%s Playback Switch", chname[i]);
err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0,
+ HDA_OUTPUT));
if (err < 0)
return err;
} else if (i == AUTO_SEQ_SURROUND) {
sprintf(name, "%s Playback Volume", chname[i]);
err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(0x29, 3, 0, HDA_OUTPUT));
+ HDA_COMPOSE_AMP_VAL(0x29, 3, 0,
+ HDA_OUTPUT));
if (err < 0)
return err;
sprintf(name, "%s Playback Switch", chname[i]);
err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(0x29, 3, 0, HDA_OUTPUT));
+ HDA_COMPOSE_AMP_VAL(0x29, 3, 0,
+ HDA_OUTPUT));
if (err < 0)
return err;
} else if (i == AUTO_SEQ_SIDE) {
sprintf(name, "%s Playback Volume", chname[i]);
err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT));
+ HDA_COMPOSE_AMP_VAL(0x1a, 3, 0,
+ HDA_OUTPUT));
if (err < 0)
return err;
sprintf(name, "%s Playback Switch", chname[i]);
err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT));
+ HDA_COMPOSE_AMP_VAL(0x1a, 3, 0,
+ HDA_OUTPUT));
if (err < 0)
return err;
}
@@ -1351,8 +1414,6 @@ static struct hda_verb vt1709_6ch_volume_init_verbs[] = {
/* Set input of PW4 as MW0 */
{0x20, AC_VERB_SET_CONNECT_SEL, 0},
- /* Set mic as default input of sw0 */
- {0x19, AC_VERB_SET_CONNECT_SEL, 0x2},
/* PW9 Output enable */
{0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
{ }
@@ -1403,6 +1464,494 @@ static int patch_vt1709_6ch(struct hda_codec *codec)
#ifdef CONFIG_SND_HDA_POWER_SAVE
spec->loopback.amplist = vt1709_loopbacks;
#endif
+ return 0;
+}
+
+/* capture mixer elements */
+static struct snd_kcontrol_new vt1708B_capture_mixer[] = {
+ HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* The multiple "Capture Source" controls confuse alsamixer
+ * So call somewhat different..
+ */
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 1,
+ .info = via_mux_enum_info,
+ .get = via_mux_enum_get,
+ .put = via_mux_enum_put,
+ },
+ { } /* end */
+};
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static struct hda_verb vt1708B_8ch_volume_init_verbs[] = {
+ /*
+ * Unmute ADC0-1 and set the default input to mic-in
+ */
+ {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x14, 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
+ */
+ /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+
+ /*
+ * Set up output mixers
+ */
+ /* set vol=0 to output mixers */
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+
+ /* Setup default input to PW4 */
+ {0x1d, AC_VERB_SET_CONNECT_SEL, 0x1},
+ /* PW9 Output enable */
+ {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+ /* PW10 Input enable */
+ {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+ { }
+};
+
+static struct hda_verb vt1708B_4ch_volume_init_verbs[] = {
+ /*
+ * Unmute ADC0-1 and set the default input to mic-in
+ */
+ {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x14, 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
+ */
+ /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+
+ /*
+ * Set up output mixers
+ */
+ /* set vol=0 to output mixers */
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+
+ /* Setup default input of PW4 to MW0 */
+ {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
+ /* PW9 Output enable */
+ {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+ /* PW10 Input enable */
+ {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+ { }
+};
+
+static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 8,
+ .nid = 0x10, /* NID to query formats and rates */
+ .ops = {
+ .open = via_playback_pcm_open,
+ .prepare = via_playback_pcm_prepare,
+ .cleanup = via_playback_pcm_cleanup
+ },
+};
+
+static struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 4,
+ .nid = 0x10, /* NID to query formats and rates */
+ .ops = {
+ .open = via_playback_pcm_open,
+ .prepare = via_playback_pcm_prepare,
+ .cleanup = via_playback_pcm_cleanup
+ },
+};
+
+static struct hda_pcm_stream vt1708B_pcm_analog_capture = {
+ .substreams = 2,
+ .channels_min = 2,
+ .channels_max = 2,
+ .nid = 0x13, /* NID to query formats and rates */
+ .ops = {
+ .prepare = via_capture_pcm_prepare,
+ .cleanup = via_capture_pcm_cleanup
+ },
+};
+
+static struct hda_pcm_stream vt1708B_pcm_digital_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in via_build_pcms */
+ .ops = {
+ .open = via_dig_playback_pcm_open,
+ .close = via_dig_playback_pcm_close,
+ .prepare = via_dig_playback_pcm_prepare
+ },
+};
+
+static struct hda_pcm_stream vt1708B_pcm_digital_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+};
+
+/* fill in the dac_nids table from the parsed pin configuration */
+static int vt1708B_auto_fill_dac_nids(struct via_spec *spec,
+ const struct auto_pin_cfg *cfg)
+{
+ int i;
+ hda_nid_t nid;
+
+ spec->multiout.num_dacs = cfg->line_outs;
+
+ spec->multiout.dac_nids = spec->private_dac_nids;
+
+ for (i = 0; i < 4; i++) {
+ nid = cfg->line_out_pins[i];
+ if (nid) {
+ /* config dac list */
+ switch (i) {
+ case AUTO_SEQ_FRONT:
+ spec->multiout.dac_nids[i] = 0x10;
+ break;
+ case AUTO_SEQ_CENLFE:
+ spec->multiout.dac_nids[i] = 0x24;
+ break;
+ case AUTO_SEQ_SURROUND:
+ spec->multiout.dac_nids[i] = 0x25;
+ break;
+ case AUTO_SEQ_SIDE:
+ spec->multiout.dac_nids[i] = 0x11;
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* add playback controls from the parsed DAC table */
+static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec,
+ const struct auto_pin_cfg *cfg)
+{
+ char name[32];
+ static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
+ hda_nid_t nid_vols[] = {0x16, 0x27, 0x26, 0x18};
+ hda_nid_t nid, nid_vol = 0;
+ int i, err;
+
+ for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
+ nid = cfg->line_out_pins[i];
+
+ if (!nid)
+ continue;
+
+ nid_vol = nid_vols[i];
+
+ if (i == AUTO_SEQ_CENLFE) {
+ /* Center/LFE */
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+ "Center Playback Volume",
+ HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+ "LFE Playback Volume",
+ HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+ "Center Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+ "LFE Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ } else if (i == AUTO_SEQ_FRONT) {
+ /* add control to mixer index 0 */
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+ "Master Front Playback Volume",
+ HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
+ HDA_INPUT));
+ if (err < 0)
+ return err;
+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+ "Master Front Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
+ HDA_INPUT));
+ if (err < 0)
+ return err;
+
+ /* add control to PW3 */
+ sprintf(name, "%s Playback Volume", chname[i]);
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ sprintf(name, "%s Playback Switch", chname[i]);
+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ } else {
+ sprintf(name, "%s Playback Volume", chname[i]);
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
+ HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ sprintf(name, "%s Playback Switch", chname[i]);
+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
+ HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
+{
+ int err;
+
+ if (!pin)
+ return 0;
+
+ spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */
+
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+ "Headphone Playback Volume",
+ HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+ "Headphone Playback Switch",
+ HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+/* create playback/capture controls for input pins */
+static int vt1708B_auto_create_analog_input_ctls(struct via_spec *spec,
+ const struct auto_pin_cfg *cfg)
+{
+ static char *labels[] = {
+ "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
+ };
+ struct hda_input_mux *imux = &spec->private_imux;
+ int i, err, idx = 0;
+
+ /* for internal loopback recording select */
+ imux->items[imux->num_items].label = "Stereo Mixer";
+ imux->items[imux->num_items].index = idx;
+ imux->num_items++;
+
+ for (i = 0; i < AUTO_PIN_LAST; i++) {
+ if (!cfg->input_pins[i])
+ continue;
+
+ switch (cfg->input_pins[i]) {
+ case 0x1a: /* Mic */
+ idx = 2;
+ break;
+
+ case 0x1b: /* Line In */
+ idx = 3;
+ break;
+
+ case 0x1e: /* Front Mic */
+ idx = 4;
+ break;
+
+ case 0x1f: /* CD */
+ idx = 1;
+ break;
+ }
+ err = via_new_analog_input(spec, cfg->input_pins[i], labels[i],
+ idx, 0x16);
+ if (err < 0)
+ return err;
+ imux->items[imux->num_items].label = labels[i];
+ imux->items[imux->num_items].index = idx;
+ imux->num_items++;
+ }
+ return 0;
+}
+
+static int vt1708B_parse_auto_config(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ int err;
+
+ err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
+ if (err < 0)
+ return err;
+ err = vt1708B_auto_fill_dac_nids(spec, &spec->autocfg);
+ if (err < 0)
+ return err;
+ if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
+ return 0; /* can't find valid BIOS pin config */
+
+ err = vt1708B_auto_create_multi_out_ctls(spec, &spec->autocfg);
+ if (err < 0)
+ return err;
+ err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
+ if (err < 0)
+ return err;
+ err = vt1708B_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ if (err < 0)
+ return err;
+
+ spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+
+ if (spec->autocfg.dig_out_pin)
+ spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID;
+ if (spec->autocfg.dig_in_pin)
+ spec->dig_in_nid = VT1708B_DIGIN_NID;
+
+ if (spec->kctl_alloc)
+ spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+
+ spec->input_mux = &spec->private_imux;
+
+ return 1;
+}
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static struct hda_amp_list vt1708B_loopbacks[] = {
+ { 0x16, HDA_INPUT, 1 },
+ { 0x16, HDA_INPUT, 2 },
+ { 0x16, HDA_INPUT, 3 },
+ { 0x16, HDA_INPUT, 4 },
+ { } /* end */
+};
+#endif
+
+static int patch_vt1708B_8ch(struct hda_codec *codec)
+{
+ struct via_spec *spec;
+ int err;
+
+ /* create a codec specific record */
+ spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+
+ /* automatic parse from the BIOS config */
+ err = vt1708B_parse_auto_config(codec);
+ if (err < 0) {
+ via_free(codec);
+ return err;
+ } else if (!err) {
+ printk(KERN_INFO "hda_codec: Cannot set up configuration "
+ "from BIOS. Using genenic mode...\n");
+ }
+
+ spec->init_verbs = vt1708B_8ch_volume_init_verbs;
+
+ spec->stream_name_analog = "VT1708B Analog";
+ spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback;
+ spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
+
+ spec->stream_name_digital = "VT1708B Digital";
+ spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
+ spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
+
+ if (!spec->adc_nids && spec->input_mux) {
+ spec->adc_nids = vt1708B_adc_nids;
+ spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
+ spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
+ spec->num_mixers++;
+ }
+
+ codec->patch_ops = via_patch_ops;
+
+ codec->patch_ops.init = via_auto_init;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+ spec->loopback.amplist = vt1708B_loopbacks;
+#endif
+
+ return 0;
+}
+
+static int patch_vt1708B_4ch(struct hda_codec *codec)
+{
+ struct via_spec *spec;
+ int err;
+
+ /* create a codec specific record */
+ spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+
+ /* automatic parse from the BIOS config */
+ err = vt1708B_parse_auto_config(codec);
+ if (err < 0) {
+ via_free(codec);
+ return err;
+ } else if (!err) {
+ printk(KERN_INFO "hda_codec: Cannot set up configuration "
+ "from BIOS. Using genenic mode...\n");
+ }
+
+ spec->init_verbs = vt1708B_4ch_volume_init_verbs;
+
+ spec->stream_name_analog = "VT1708B Analog";
+ spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback;
+ spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
+
+ spec->stream_name_digital = "VT1708B Digital";
+ spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
+ spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
+
+ if (!spec->adc_nids && spec->input_mux) {
+ spec->adc_nids = vt1708B_adc_nids;
+ spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
+ spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
+ spec->num_mixers++;
+ }
+
+ codec->patch_ops = via_patch_ops;
+
+ codec->patch_ops.init = via_auto_init;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+ spec->loopback.amplist = vt1708B_loopbacks;
+#endif
return 0;
}
@@ -1415,13 +1964,37 @@ struct hda_codec_preset snd_hda_preset_via[] = {
{ .id = 0x11061709, .name = "VIA VT1708", .patch = patch_vt1708},
{ .id = 0x1106170A, .name = "VIA VT1708", .patch = patch_vt1708},
{ .id = 0x1106170B, .name = "VIA VT1708", .patch = patch_vt1708},
- { .id = 0x1106E710, .name = "VIA VT1709 10-Ch", .patch = patch_vt1709_10ch},
- { .id = 0x1106E711, .name = "VIA VT1709 10-Ch", .patch = patch_vt1709_10ch},
- { .id = 0x1106E712, .name = "VIA VT1709 10-Ch", .patch = patch_vt1709_10ch},
- { .id = 0x1106E713, .name = "VIA VT1709 10-Ch", .patch = patch_vt1709_10ch},
- { .id = 0x1106E714, .name = "VIA VT1709 6-Ch", .patch = patch_vt1709_6ch},
- { .id = 0x1106E715, .name = "VIA VT1709 6-Ch", .patch = patch_vt1709_6ch},
- { .id = 0x1106E716, .name = "VIA VT1709 6-Ch", .patch = patch_vt1709_6ch},
- { .id = 0x1106E717, .name = "VIA VT1709 6-Ch", .patch = patch_vt1709_6ch},
+ { .id = 0x1106E710, .name = "VIA VT1709 10-Ch",
+ .patch = patch_vt1709_10ch},
+ { .id = 0x1106E711, .name = "VIA VT1709 10-Ch",
+ .patch = patch_vt1709_10ch},
+ { .id = 0x1106E712, .name = "VIA VT1709 10-Ch",
+ .patch = patch_vt1709_10ch},
+ { .id = 0x1106E713, .name = "VIA VT1709 10-Ch",
+ .patch = patch_vt1709_10ch},
+ { .id = 0x1106E714, .name = "VIA VT1709 6-Ch",
+ .patch = patch_vt1709_6ch},
+ { .id = 0x1106E715, .name = "VIA VT1709 6-Ch",
+ .patch = patch_vt1709_6ch},
+ { .id = 0x1106E716, .name = "VIA VT1709 6-Ch",
+ .patch = patch_vt1709_6ch},
+ { .id = 0x1106E717, .name = "VIA VT1709 6-Ch",
+ .patch = patch_vt1709_6ch},
+ { .id = 0x1106E720, .name = "VIA VT1708B 8-Ch",
+ .patch = patch_vt1708B_8ch},
+ { .id = 0x1106E721, .name = "VIA VT1708B 8-Ch",
+ .patch = patch_vt1708B_8ch},
+ { .id = 0x1106E722, .name = "VIA VT1708B 8-Ch",
+ .patch = patch_vt1708B_8ch},
+ { .id = 0x1106E723, .name = "VIA VT1708B 8-Ch",
+ .patch = patch_vt1708B_8ch},
+ { .id = 0x1106E724, .name = "VIA VT1708B 4-Ch",
+ .patch = patch_vt1708B_4ch},
+ { .id = 0x1106E725, .name = "VIA VT1708B 4-Ch",
+ .patch = patch_vt1708B_4ch},
+ { .id = 0x1106E726, .name = "VIA VT1708B 4-Ch",
+ .patch = patch_vt1708B_4ch},
+ { .id = 0x1106E727, .name = "VIA VT1708B 4-Ch",
+ .patch = patch_vt1708B_4ch},
{} /* terminator */
};
diff --git a/sound/pci/hda/vmaster.c b/sound/pci/hda/vmaster.c
new file mode 100644
index 00000000000..2da49d20a1f
--- /dev/null
+++ b/sound/pci/hda/vmaster.c
@@ -0,0 +1,364 @@
+/*
+ * Virtual master and slave controls
+ *
+ * Copyright (c) 2008 by Takashi Iwai <tiwai@suse.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, version 2.
+ *
+ */
+
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/control.h>
+
+/*
+ * a subset of information returned via ctl info callback
+ */
+struct link_ctl_info {
+ int type; /* value type */
+ int count; /* item count */
+ int min_val, max_val; /* min, max values */
+};
+
+/*
+ * link master - this contains a list of slave controls that are
+ * identical types, i.e. info returns the same value type and value
+ * ranges, but may have different number of counts.
+ *
+ * The master control is so far only mono volume/switch for simplicity.
+ * The same value will be applied to all slaves.
+ */
+struct link_master {
+ struct list_head slaves;
+ struct link_ctl_info info;
+ int val; /* the master value */
+};
+
+/*
+ * link slave - this contains a slave control element
+ *
+ * It fakes the control callbacsk with additional attenuation by the
+ * master control. A slave may have either one or two channels.
+ */
+
+struct link_slave {
+ struct list_head list;
+ struct link_master *master;
+ struct link_ctl_info info;
+ int vals[2]; /* current values */
+ struct snd_kcontrol slave; /* the copy of original control entry */
+};
+
+/* get the slave ctl info and save the initial values */
+static int slave_init(struct link_slave *slave)
+{
+ struct snd_ctl_elem_info *uinfo;
+ struct snd_ctl_elem_value *uctl;
+ int err, ch;
+
+ if (slave->info.count)
+ return 0; /* already initialized */
+
+ uinfo = kmalloc(sizeof(*uinfo), GFP_KERNEL);
+ if (!uinfo)
+ return -ENOMEM;
+ uinfo->id = slave->slave.id;
+ err = slave->slave.info(&slave->slave, uinfo);
+ if (err < 0) {
+ kfree(uinfo);
+ return err;
+ }
+ slave->info.type = uinfo->type;
+ slave->info.count = uinfo->count;
+ if (slave->info.count > 2 ||
+ (slave->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER &&
+ slave->info.type != SNDRV_CTL_ELEM_TYPE_BOOLEAN)) {
+ snd_printk(KERN_ERR "invalid slave element\n");
+ kfree(uinfo);
+ return -EINVAL;
+ }
+ slave->info.min_val = uinfo->value.integer.min;
+ slave->info.max_val = uinfo->value.integer.max;
+ kfree(uinfo);
+
+ uctl = kmalloc(sizeof(*uctl), GFP_KERNEL);
+ if (!uctl)
+ return -ENOMEM;
+ uctl->id = slave->slave.id;
+ err = slave->slave.get(&slave->slave, uctl);
+ for (ch = 0; ch < slave->info.count; ch++)
+ slave->vals[ch] = uctl->value.integer.value[ch];
+ kfree(uctl);
+ return 0;
+}
+
+/* initialize master volume */
+static int master_init(struct link_master *master)
+{
+ struct link_slave *slave;
+
+ if (master->info.count)
+ return 0; /* already initialized */
+
+ list_for_each_entry(slave, &master->slaves, list) {
+ int err = slave_init(slave);
+ if (err < 0)
+ return err;
+ master->info = slave->info;
+ master->info.count = 1; /* always mono */
+ /* set full volume as default (= no attenuation) */
+ master->val = master->info.max_val;
+ return 0;
+ }
+ return -ENOENT;
+}
+
+static int slave_get_val(struct link_slave *slave,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int err, ch;
+
+ err = slave_init(slave);
+ if (err < 0)
+ return err;
+ for (ch = 0; ch < slave->info.count; ch++)
+ ucontrol->value.integer.value[ch] = slave->vals[ch];
+ return 0;
+}
+
+static int slave_put_val(struct link_slave *slave,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int err, ch, vol;
+
+ err = master_init(slave->master);
+ if (err < 0)
+ return err;
+
+ switch (slave->info.type) {
+ case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
+ for (ch = 0; ch < slave->info.count; ch++)
+ ucontrol->value.integer.value[ch] &=
+ !!slave->master->val;
+ break;
+ case SNDRV_CTL_ELEM_TYPE_INTEGER:
+ for (ch = 0; ch < slave->info.count; ch++) {
+ /* max master volume is supposed to be 0 dB */
+ vol = ucontrol->value.integer.value[ch];
+ vol += slave->master->val - slave->master->info.max_val;
+ if (vol < slave->info.min_val)
+ vol = slave->info.min_val;
+ else if (vol > slave->info.max_val)
+ vol = slave->info.max_val;
+ ucontrol->value.integer.value[ch] = vol;
+ }
+ break;
+ }
+ return slave->slave.put(&slave->slave, ucontrol);
+}
+
+/*
+ * ctl callbacks for slaves
+ */
+static int slave_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct link_slave *slave = snd_kcontrol_chip(kcontrol);
+ return slave->slave.info(&slave->slave, uinfo);
+}
+
+static int slave_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct link_slave *slave = snd_kcontrol_chip(kcontrol);
+ return slave_get_val(slave, ucontrol);
+}
+
+static int slave_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct link_slave *slave = snd_kcontrol_chip(kcontrol);
+ int err, ch, changed = 0;
+
+ err = slave_init(slave);
+ if (err < 0)
+ return err;
+ for (ch = 0; ch < slave->info.count; ch++) {
+ if (slave->vals[ch] != ucontrol->value.integer.value[ch]) {
+ changed = 1;
+ slave->vals[ch] = ucontrol->value.integer.value[ch];
+ }
+ }
+ if (!changed)
+ return 0;
+ return slave_put_val(slave, ucontrol);
+}
+
+static int slave_tlv_cmd(struct snd_kcontrol *kcontrol,
+ int op_flag, unsigned int size,
+ unsigned int __user *tlv)
+{
+ struct link_slave *slave = snd_kcontrol_chip(kcontrol);
+ /* FIXME: this assumes that the max volume is 0 dB */
+ return slave->slave.tlv.c(&slave->slave, op_flag, size, tlv);
+}
+
+static void slave_free(struct snd_kcontrol *kcontrol)
+{
+ struct link_slave *slave = snd_kcontrol_chip(kcontrol);
+ if (slave->slave.private_free)
+ slave->slave.private_free(&slave->slave);
+ if (slave->master)
+ list_del(&slave->list);
+ kfree(slave);
+}
+
+/*
+ * Add a slave control to the group with the given master control
+ *
+ * All slaves must be the same type (returning the same information
+ * via info callback). The fucntion doesn't check it, so it's your
+ * responsibility.
+ *
+ * Also, some additional limitations:
+ * - at most two channels
+ * - logarithmic volume control (dB level), no linear volume
+ * - master can only attenuate the volume, no gain
+ */
+int snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave)
+{
+ struct link_master *master_link = snd_kcontrol_chip(master);
+ struct link_slave *srec;
+
+ srec = kzalloc(sizeof(*srec) +
+ slave->count * sizeof(*slave->vd), GFP_KERNEL);
+ if (!srec)
+ return -ENOMEM;
+ srec->slave = *slave;
+ memcpy(srec->slave.vd, slave->vd, slave->count * sizeof(*slave->vd));
+ srec->master = master_link;
+
+ /* override callbacks */
+ slave->info = slave_info;
+ slave->get = slave_get;
+ slave->put = slave_put;
+ if (slave->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)
+ slave->tlv.c = slave_tlv_cmd;
+ slave->private_data = srec;
+ slave->private_free = slave_free;
+
+ list_add_tail(&srec->list, &master_link->slaves);
+ return 0;
+}
+
+/*
+ * ctl callbacks for master controls
+ */
+static int master_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct link_master *master = snd_kcontrol_chip(kcontrol);
+ int ret;
+
+ ret = master_init(master);
+ if (ret < 0)
+ return ret;
+ uinfo->type = master->info.type;
+ uinfo->count = master->info.count;
+ uinfo->value.integer.min = master->info.min_val;
+ uinfo->value.integer.max = master->info.max_val;
+ return 0;
+}
+
+static int master_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct link_master *master = snd_kcontrol_chip(kcontrol);
+ int err = master_init(master);
+ if (err < 0)
+ return err;
+ ucontrol->value.integer.value[0] = master->val;
+ return 0;
+}
+
+static int master_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct link_master *master = snd_kcontrol_chip(kcontrol);
+ struct link_slave *slave;
+ struct snd_ctl_elem_value *uval;
+ int err, old_val;
+
+ err = master_init(master);
+ if (err < 0)
+ return err;
+ old_val = master->val;
+ if (ucontrol->value.integer.value[0] == old_val)
+ return 0;
+
+ uval = kmalloc(sizeof(*uval), GFP_KERNEL);
+ if (!uval)
+ return -ENOMEM;
+ list_for_each_entry(slave, &master->slaves, list) {
+ master->val = old_val;
+ uval->id = slave->slave.id;
+ slave_get_val(slave, uval);
+ master->val = ucontrol->value.integer.value[0];
+ slave_put_val(slave, uval);
+ }
+ kfree(uval);
+ return 1;
+}
+
+static void master_free(struct snd_kcontrol *kcontrol)
+{
+ struct link_master *master = snd_kcontrol_chip(kcontrol);
+ struct link_slave *slave;
+
+ list_for_each_entry(slave, &master->slaves, list)
+ slave->master = NULL;
+ kfree(master);
+}
+
+
+/*
+ * Create a virtual master control with the given name
+ */
+struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
+ const unsigned int *tlv)
+{
+ struct link_master *master;
+ struct snd_kcontrol *kctl;
+ struct snd_kcontrol_new knew;
+
+ memset(&knew, 0, sizeof(knew));
+ knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ knew.name = name;
+ knew.info = master_info;
+
+ master = kzalloc(sizeof(*master), GFP_KERNEL);
+ if (!master)
+ return NULL;
+ INIT_LIST_HEAD(&master->slaves);
+
+ kctl = snd_ctl_new1(&knew, master);
+ if (!kctl) {
+ kfree(master);
+ return NULL;
+ }
+ /* override some callbacks */
+ kctl->info = master_info;
+ kctl->get = master_get;
+ kctl->put = master_put;
+ kctl->private_free = master_free;
+
+ /* additional (constant) TLV read */
+ if (tlv) {
+ /* FIXME: this assumes that the max volume is 0 dB */
+ kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+ kctl->tlv.p = tlv;
+ }
+ return kctl;
+}
diff --git a/sound/pci/ice1712/Makefile b/sound/pci/ice1712/Makefile
index 65ce66adba5..f99fe089495 100644
--- a/sound/pci/ice1712/Makefile
+++ b/sound/pci/ice1712/Makefile
@@ -5,7 +5,7 @@
snd-ice17xx-ak4xxx-objs := ak4xxx.o
snd-ice1712-objs := ice1712.o delta.o hoontech.o ews.o
-snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o juli.o phase.o wtm.o
+snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o prodigy_hifi.o juli.o phase.o wtm.o se.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_ICE1712) += snd-ice1712.o snd-ice17xx-ak4xxx.o
diff --git a/sound/pci/ice1712/ak4xxx.c b/sound/pci/ice1712/ak4xxx.c
index a1aba0d7d0e..dab31b2756a 100644
--- a/sound/pci/ice1712/ak4xxx.c
+++ b/sound/pci/ice1712/ak4xxx.c
@@ -21,7 +21,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/ice1712/amp.c b/sound/pci/ice1712/amp.c
index 6e13d758bb5..37564300b50 100644
--- a/sound/pci/ice1712/amp.c
+++ b/sound/pci/ice1712/amp.c
@@ -21,7 +21,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/ice1712/aureon.c b/sound/pci/ice1712/aureon.c
index ec0699c8995..868ae291b96 100644
--- a/sound/pci/ice1712/aureon.c
+++ b/sound/pci/ice1712/aureon.c
@@ -47,7 +47,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
@@ -62,6 +61,15 @@
#include "aureon.h"
#include <sound/tlv.h>
+/* AC97 register cache for Aureon */
+struct aureon_spec {
+ unsigned short stac9744[64];
+ unsigned int cs8415_mux;
+ unsigned short master[2];
+ unsigned short vol[8];
+ unsigned char pca9554_out;
+};
+
/* WM8770 registers */
#define WM_DAC_ATTEN 0x00 /* DAC1-8 analog attenuation */
#define WM_DAC_MASTER_ATTEN 0x08 /* DAC master analog attenuation */
@@ -205,7 +213,8 @@ static int aureon_universe_inmux_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- ucontrol->value.integer.value[0] = ice->spec.aureon.pca9554_out;
+ struct aureon_spec *spec = ice->spec;
+ ucontrol->value.enumerated.item[0] = spec->pca9554_out;
return 0;
}
@@ -213,16 +222,18 @@ static int aureon_universe_inmux_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct aureon_spec *spec = ice->spec;
unsigned char oval, nval;
int change;
+ nval = ucontrol->value.enumerated.item[0];
+ if (nval >= 3)
+ return -EINVAL;
snd_ice1712_save_gpio_status(ice);
-
- oval = ice->spec.aureon.pca9554_out;
- nval = ucontrol->value.integer.value[0];
+ oval = spec->pca9554_out;
if ((change = (oval != nval))) {
aureon_pca9554_write(ice, PCA9554_OUT, nval);
- ice->spec.aureon.pca9554_out = nval;
+ spec->pca9554_out = nval;
}
snd_ice1712_restore_gpio_status(ice);
@@ -233,6 +244,7 @@ static int aureon_universe_inmux_put(struct snd_kcontrol *kcontrol,
static void aureon_ac97_write(struct snd_ice1712 *ice, unsigned short reg,
unsigned short val)
{
+ struct aureon_spec *spec = ice->spec;
unsigned int tmp;
/* Send address to XILINX chip */
@@ -280,12 +292,13 @@ static void aureon_ac97_write(struct snd_ice1712 *ice, unsigned short reg,
udelay(10);
/* Store the data in out private buffer */
- ice->spec.aureon.stac9744[(reg & 0x7F) >> 1] = val;
+ spec->stac9744[(reg & 0x7F) >> 1] = val;
}
static unsigned short aureon_ac97_read(struct snd_ice1712 *ice, unsigned short reg)
{
- return ice->spec.aureon.stac9744[(reg & 0x7F) >> 1];
+ struct aureon_spec *spec = ice->spec;
+ return spec->stac9744[(reg & 0x7F) >> 1];
}
/*
@@ -293,6 +306,7 @@ static unsigned short aureon_ac97_read(struct snd_ice1712 *ice, unsigned short r
*/
static int aureon_ac97_init (struct snd_ice1712 *ice)
{
+ struct aureon_spec *spec = ice->spec;
int i;
static const unsigned short ac97_defaults[] = {
0x00, 0x9640,
@@ -330,9 +344,9 @@ static int aureon_ac97_init (struct snd_ice1712 *ice)
snd_ice1712_gpio_write(ice, tmp);
udelay(3);
- memset(&ice->spec.aureon.stac9744, 0, sizeof(ice->spec.aureon.stac9744));
+ memset(&spec->stac9744, 0, sizeof(spec->stac9744));
for (i=0; ac97_defaults[i] != (unsigned short)-1; i+=2)
- ice->spec.aureon.stac9744[(ac97_defaults[i]) >> 1] = ac97_defaults[i+1];
+ spec->stac9744[(ac97_defaults[i]) >> 1] = ac97_defaults[i+1];
aureon_ac97_write(ice, AC97_MASTER, 0x0000); // Unmute AC'97 master volume permanently - muting is done by WM8770
@@ -744,27 +758,33 @@ static int wm_master_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
static int wm_master_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct aureon_spec *spec = ice->spec;
int i;
for (i=0; i<2; i++)
- ucontrol->value.integer.value[i] = ice->spec.aureon.master[i] & ~WM_VOL_MUTE;
+ ucontrol->value.integer.value[i] =
+ spec->master[i] & ~WM_VOL_MUTE;
return 0;
}
static int wm_master_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct aureon_spec *spec = ice->spec;
int ch, change = 0;
snd_ice1712_save_gpio_status(ice);
for (ch = 0; ch < 2; ch++) {
- if (ucontrol->value.integer.value[ch] != ice->spec.aureon.master[ch]) {
+ unsigned int vol = ucontrol->value.integer.value[ch];
+ if (vol > WM_VOL_MAX)
+ continue;
+ vol |= spec->master[ch] & WM_VOL_MUTE;
+ if (vol != spec->master[ch]) {
int dac;
- ice->spec.aureon.master[ch] &= WM_VOL_MUTE;
- ice->spec.aureon.master[ch] |= ucontrol->value.integer.value[ch];
+ spec->master[ch] = vol;
for (dac = 0; dac < ice->num_total_dacs; dac += 2)
wm_set_vol(ice, WM_DAC_ATTEN + dac + ch,
- ice->spec.aureon.vol[dac + ch],
- ice->spec.aureon.master[ch]);
+ spec->vol[dac + ch],
+ spec->master[ch]);
change = 1;
}
}
@@ -788,18 +808,21 @@ static int wm_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *
static int wm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct aureon_spec *spec = ice->spec;
int i, ofs, voices;
voices = kcontrol->private_value >> 8;
ofs = kcontrol->private_value & 0xff;
for (i = 0; i < voices; i++)
- ucontrol->value.integer.value[i] = ice->spec.aureon.vol[ofs+i] & ~WM_VOL_MUTE;
+ ucontrol->value.integer.value[i] =
+ spec->vol[ofs+i] & ~WM_VOL_MUTE;
return 0;
}
static int wm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct aureon_spec *spec = ice->spec;
int i, idx, ofs, voices;
int change = 0;
@@ -807,12 +830,15 @@ static int wm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *
ofs = kcontrol->private_value & 0xff;
snd_ice1712_save_gpio_status(ice);
for (i = 0; i < voices; i++) {
- idx = WM_DAC_ATTEN + ofs + i;
- if (ucontrol->value.integer.value[i] != ice->spec.aureon.vol[ofs+i]) {
- ice->spec.aureon.vol[ofs+i] &= WM_VOL_MUTE;
- ice->spec.aureon.vol[ofs+i] |= ucontrol->value.integer.value[i];
- wm_set_vol(ice, idx, ice->spec.aureon.vol[ofs+i],
- ice->spec.aureon.master[i]);
+ unsigned int vol = ucontrol->value.integer.value[i];
+ if (vol > 0x7f)
+ continue;
+ vol |= spec->vol[ofs+i];
+ if (vol != spec->vol[ofs+i]) {
+ spec->vol[ofs+i] = vol;
+ idx = WM_DAC_ATTEN + ofs + i;
+ wm_set_vol(ice, idx, spec->vol[ofs + i],
+ spec->master[i]);
change = 1;
}
}
@@ -834,19 +860,22 @@ static int wm_mute_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info
static int wm_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct aureon_spec *spec = ice->spec;
int voices, ofs, i;
voices = kcontrol->private_value >> 8;
ofs = kcontrol->private_value & 0xFF;
for (i = 0; i < voices; i++)
- ucontrol->value.integer.value[i] = (ice->spec.aureon.vol[ofs+i] & WM_VOL_MUTE) ? 0 : 1;
+ ucontrol->value.integer.value[i] =
+ (spec->vol[ofs + i] & WM_VOL_MUTE) ? 0 : 1;
return 0;
}
static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct aureon_spec *spec = ice->spec;
int change = 0, voices, ofs, i;
voices = kcontrol->private_value >> 8;
@@ -854,13 +883,13 @@ static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value
snd_ice1712_save_gpio_status(ice);
for (i = 0; i < voices; i++) {
- int val = (ice->spec.aureon.vol[ofs + i] & WM_VOL_MUTE) ? 0 : 1;
+ int val = (spec->vol[ofs + i] & WM_VOL_MUTE) ? 0 : 1;
if (ucontrol->value.integer.value[i] != val) {
- ice->spec.aureon.vol[ofs + i] &= ~WM_VOL_MUTE;
- ice->spec.aureon.vol[ofs + i] |=
+ spec->vol[ofs + i] &= ~WM_VOL_MUTE;
+ spec->vol[ofs + i] |=
ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE;
- wm_set_vol(ice, ofs + i, ice->spec.aureon.vol[ofs + i],
- ice->spec.aureon.master[i]);
+ wm_set_vol(ice, ofs + i, spec->vol[ofs + i],
+ spec->master[i]);
change = 1;
}
}
@@ -877,29 +906,33 @@ static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value
static int wm_master_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct aureon_spec *spec = ice->spec;
- ucontrol->value.integer.value[0] = (ice->spec.aureon.master[0] & WM_VOL_MUTE) ? 0 : 1;
- ucontrol->value.integer.value[1] = (ice->spec.aureon.master[1] & WM_VOL_MUTE) ? 0 : 1;
+ ucontrol->value.integer.value[0] =
+ (spec->master[0] & WM_VOL_MUTE) ? 0 : 1;
+ ucontrol->value.integer.value[1] =
+ (spec->master[1] & WM_VOL_MUTE) ? 0 : 1;
return 0;
}
static int wm_master_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct aureon_spec *spec = ice->spec;
int change = 0, i;
snd_ice1712_save_gpio_status(ice);
for (i = 0; i < 2; i++) {
- int val = (ice->spec.aureon.master[i] & WM_VOL_MUTE) ? 0 : 1;
+ int val = (spec->master[i] & WM_VOL_MUTE) ? 0 : 1;
if (ucontrol->value.integer.value[i] != val) {
int dac;
- ice->spec.aureon.master[i] &= ~WM_VOL_MUTE;
- ice->spec.aureon.master[i] |=
+ spec->master[i] &= ~WM_VOL_MUTE;
+ spec->master[i] |=
ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE;
for (dac = 0; dac < ice->num_total_dacs; dac += 2)
wm_set_vol(ice, WM_DAC_ATTEN + dac + i,
- ice->spec.aureon.vol[dac + i],
- ice->spec.aureon.master[i]);
+ spec->vol[dac + i],
+ spec->master[i]);
change = 1;
}
}
@@ -940,8 +973,10 @@ static int wm_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
unsigned short ovol, nvol;
int change = 0;
- snd_ice1712_save_gpio_status(ice);
nvol = ucontrol->value.integer.value[0];
+ if (nvol > PCM_RES)
+ return -EINVAL;
+ snd_ice1712_save_gpio_status(ice);
nvol = (nvol ? (nvol + PCM_MIN) : 0) & 0xff;
ovol = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff;
if (ovol != nvol) {
@@ -1031,7 +1066,7 @@ static int wm_adc_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
snd_ice1712_save_gpio_status(ice);
for (i = 0; i < 2; i++) {
idx = WM_ADC_GAIN + i;
- nvol = ucontrol->value.integer.value[i];
+ nvol = ucontrol->value.integer.value[i] & 0x1f;
ovol = wm_get(ice, idx);
if ((ovol & 0x1f) != nvol) {
wm_put(ice, idx, nvol | (ovol & ~0x1f));
@@ -1143,10 +1178,11 @@ static int aureon_cs8415_mux_info(struct snd_kcontrol *kcontrol, struct snd_ctl_
static int aureon_cs8415_mux_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct aureon_spec *spec = ice->spec;
//snd_ice1712_save_gpio_status(ice);
//val = aureon_cs8415_get(ice, CS8415_CTRL2);
- ucontrol->value.enumerated.item[0] = ice->spec.aureon.cs8415_mux;
+ ucontrol->value.enumerated.item[0] = spec->cs8415_mux;
//snd_ice1712_restore_gpio_status(ice);
return 0;
}
@@ -1154,6 +1190,7 @@ static int aureon_cs8415_mux_get(struct snd_kcontrol *kcontrol, struct snd_ctl_e
static int aureon_cs8415_mux_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct aureon_spec *spec = ice->spec;
unsigned short oval, nval;
int change;
@@ -1165,7 +1202,7 @@ static int aureon_cs8415_mux_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
if (change)
aureon_cs8415_put(ice, CS8415_CTRL2, nval);
snd_ice1712_restore_gpio_status(ice);
- ice->spec.aureon.cs8415_mux = ucontrol->value.enumerated.item[0];
+ spec->cs8415_mux = ucontrol->value.enumerated.item[0];
return change;
}
@@ -2001,10 +2038,16 @@ static int __devinit aureon_init(struct snd_ice1712 *ice)
0x0605, /* slave, 24bit, MSB on second OSCLK, SDOUT for right channel when OLRCK is high */
(unsigned short)-1
};
+ struct aureon_spec *spec;
unsigned int tmp;
const unsigned short *p;
int err, i;
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ ice->spec = spec;
+
if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON51_SKY) {
ice->num_total_dacs = 6;
ice->num_total_adcs = 2;
@@ -2055,7 +2098,7 @@ static int __devinit aureon_init(struct snd_ice1712 *ice)
ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71XT) {
for (p = cs_inits; *p != (unsigned short)-1; p++)
aureon_spi_write(ice, AUREON_CS8415_CS, *p | 0x200000, 24);
- ice->spec.aureon.cs8415_mux = 1;
+ spec->cs8415_mux = 1;
aureon_set_headphone_amp(ice, 1);
}
@@ -2066,11 +2109,11 @@ static int __devinit aureon_init(struct snd_ice1712 *ice)
aureon_pca9554_write(ice, PCA9554_DIR, 0x00);
aureon_pca9554_write(ice, PCA9554_OUT, 0x00); /* internal AUX */
- ice->spec.aureon.master[0] = WM_VOL_MUTE;
- ice->spec.aureon.master[1] = WM_VOL_MUTE;
+ spec->master[0] = WM_VOL_MUTE;
+ spec->master[1] = WM_VOL_MUTE;
for (i = 0; i < ice->num_total_dacs; i++) {
- ice->spec.aureon.vol[i] = WM_VOL_MUTE;
- wm_set_vol(ice, i, ice->spec.aureon.vol[i], ice->spec.aureon.master[i % 2]);
+ spec->vol[i] = WM_VOL_MUTE;
+ wm_set_vol(ice, i, spec->vol[i], spec->master[i % 2]);
}
return 0;
diff --git a/sound/pci/ice1712/delta.c b/sound/pci/ice1712/delta.c
index 371f78461db..efd180b40e5 100644
--- a/sound/pci/ice1712/delta.c
+++ b/sound/pci/ice1712/delta.c
@@ -22,7 +22,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
@@ -405,7 +404,7 @@ static int snd_ice1712_delta1010lt_wordclock_status_get(struct snd_kcontrol *kco
if (snd_i2c_sendbytes(ice->cs8427, &reg, 1) != 1)
snd_printk(KERN_ERR "unable to send register 0x%x byte to CS8427\n", reg);
snd_i2c_readbytes(ice->cs8427, &reg, 1);
- ucontrol->value.integer.value[0] = (reg ? 1 : 0);
+ ucontrol->value.integer.value[0] = (reg & CS8427_UNLOCK) ? 1 : 0;
return 0;
}
diff --git a/sound/pci/ice1712/ews.c b/sound/pci/ice1712/ews.c
index 75e4e5e0f1e..064760d2a02 100644
--- a/sound/pci/ice1712/ews.c
+++ b/sound/pci/ice1712/ews.c
@@ -22,7 +22,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
@@ -45,6 +44,11 @@ enum {
};
+/* additional i2c devices for EWS boards */
+struct ews_spec {
+ struct snd_i2c_device *i2cdevs[3];
+};
+
/*
* access via i2c mode (for EWX 24/96, EWS 88MT&D)
*/
@@ -142,15 +146,17 @@ static struct snd_i2c_bit_ops snd_ice1712_ewx_cs8427_bit_ops = {
/* AK4524 chip select; address 0x48 bit 0-3 */
static int snd_ice1712_ews88mt_chip_select(struct snd_ice1712 *ice, int chip_mask)
{
+ struct ews_spec *spec = ice->spec;
unsigned char data, ndata;
snd_assert(chip_mask >= 0 && chip_mask <= 0x0f, return -EINVAL);
snd_i2c_lock(ice->i2c);
- if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_PCF2], &data, 1) != 1)
+ if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_PCF2], &data, 1) != 1)
goto __error;
ndata = (data & 0xf0) | chip_mask;
if (ndata != data)
- if (snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_PCF2], &ndata, 1) != 1)
+ if (snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_PCF2], &ndata, 1)
+ != 1)
goto __error;
snd_i2c_unlock(ice->i2c);
return 0;
@@ -224,6 +230,7 @@ static void dmx6fire_ak4524_lock(struct snd_akm4xxx *ak, int chip)
static void snd_ice1712_ews_cs8404_spdif_write(struct snd_ice1712 *ice, unsigned char bits)
{
+ struct ews_spec *spec = ice->spec;
unsigned char bytes[2];
snd_i2c_lock(ice->i2c);
@@ -231,15 +238,18 @@ static void snd_ice1712_ews_cs8404_spdif_write(struct snd_ice1712 *ice, unsigned
case ICE1712_SUBDEVICE_EWS88MT:
case ICE1712_SUBDEVICE_EWS88MT_NEW:
case ICE1712_SUBDEVICE_PHASE88:
- if (snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_CS8404], &bits, 1) != 1)
+ if (snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_CS8404], &bits, 1)
+ != 1)
goto _error;
break;
case ICE1712_SUBDEVICE_EWS88D:
- if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_88D], bytes, 2) != 2)
+ if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_88D], bytes, 2)
+ != 2)
goto _error;
if (bits != bytes[1]) {
bytes[1] = bits;
- if (snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_88D], bytes, 2) != 2)
+ if (snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_88D],
+ bytes, 2) != 2)
goto _error;
}
break;
@@ -412,6 +422,7 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice)
{
int err;
struct snd_akm4xxx *ak;
+ struct ews_spec *spec;
/* set the analog DACs */
switch (ice->eeprom.subvendor) {
@@ -436,6 +447,11 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice)
break;
}
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ ice->spec = spec;
+
/* create i2c */
if ((err = snd_i2c_bus_create(ice->card, "ICE1712 GPIO 1", NULL, &ice->i2c)) < 0) {
snd_printk(KERN_ERR "unable to create I2C bus\n");
@@ -447,7 +463,10 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice)
/* create i2c devices */
switch (ice->eeprom.subvendor) {
case ICE1712_SUBDEVICE_DMX6FIRE:
- if ((err = snd_i2c_device_create(ice->i2c, "PCF9554", ICE1712_6FIRE_PCF9554_ADDR, &ice->spec.i2cdevs[EWS_I2C_6FIRE])) < 0) {
+ err = snd_i2c_device_create(ice->i2c, "PCF9554",
+ ICE1712_6FIRE_PCF9554_ADDR,
+ &spec->i2cdevs[EWS_I2C_6FIRE]);
+ if (err < 0) {
snd_printk(KERN_ERR "PCF9554 initialization failed\n");
return err;
}
@@ -456,18 +475,30 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice)
case ICE1712_SUBDEVICE_EWS88MT:
case ICE1712_SUBDEVICE_EWS88MT_NEW:
case ICE1712_SUBDEVICE_PHASE88:
- if ((err = snd_i2c_device_create(ice->i2c, "CS8404", ICE1712_EWS88MT_CS8404_ADDR, &ice->spec.i2cdevs[EWS_I2C_CS8404])) < 0)
+ err = snd_i2c_device_create(ice->i2c, "CS8404",
+ ICE1712_EWS88MT_CS8404_ADDR,
+ &spec->i2cdevs[EWS_I2C_CS8404]);
+ if (err < 0)
return err;
- if ((err = snd_i2c_device_create(ice->i2c, "PCF8574 (1st)", ICE1712_EWS88MT_INPUT_ADDR, &ice->spec.i2cdevs[EWS_I2C_PCF1])) < 0)
+ err = snd_i2c_device_create(ice->i2c, "PCF8574 (1st)",
+ ICE1712_EWS88MT_INPUT_ADDR,
+ &spec->i2cdevs[EWS_I2C_PCF1]);
+ if (err < 0)
return err;
- if ((err = snd_i2c_device_create(ice->i2c, "PCF8574 (2nd)", ICE1712_EWS88MT_OUTPUT_ADDR, &ice->spec.i2cdevs[EWS_I2C_PCF2])) < 0)
+ err = snd_i2c_device_create(ice->i2c, "PCF8574 (2nd)",
+ ICE1712_EWS88MT_OUTPUT_ADDR,
+ &spec->i2cdevs[EWS_I2C_PCF2]);
+ if (err < 0)
return err;
/* Check if the front module is connected */
if ((err = snd_ice1712_ews88mt_chip_select(ice, 0x0f)) < 0)
return err;
break;
case ICE1712_SUBDEVICE_EWS88D:
- if ((err = snd_i2c_device_create(ice->i2c, "PCF8575", ICE1712_EWS88D_PCF_ADDR, &ice->spec.i2cdevs[EWS_I2C_88D])) < 0)
+ err = snd_i2c_device_create(ice->i2c, "PCF8575",
+ ICE1712_EWS88D_PCF_ADDR,
+ &spec->i2cdevs[EWS_I2C_88D]);
+ if (err < 0)
return err;
break;
}
@@ -507,7 +538,7 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice)
}
/* analog section */
- ak = ice->akm = kmalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
+ ak = ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
if (! ak)
return -ENOMEM;
ice->akm_codecs = 1;
@@ -605,10 +636,11 @@ static struct snd_kcontrol_new snd_ice1712_ewx2496_controls[] __devinitdata = {
static int snd_ice1712_ews88mt_output_sense_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct ews_spec *spec = ice->spec;
unsigned char data;
snd_i2c_lock(ice->i2c);
- if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_PCF2], &data, 1) != 1) {
+ if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_PCF2], &data, 1) != 1) {
snd_i2c_unlock(ice->i2c);
return -EIO;
}
@@ -621,15 +653,17 @@ static int snd_ice1712_ews88mt_output_sense_get(struct snd_kcontrol *kcontrol, s
static int snd_ice1712_ews88mt_output_sense_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct ews_spec *spec = ice->spec;
unsigned char data, ndata;
snd_i2c_lock(ice->i2c);
- if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_PCF2], &data, 1) != 1) {
+ if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_PCF2], &data, 1) != 1) {
snd_i2c_unlock(ice->i2c);
return -EIO;
}
ndata = (data & ~ICE1712_EWS88MT_OUTPUT_SENSE) | (ucontrol->value.enumerated.item[0] ? ICE1712_EWS88MT_OUTPUT_SENSE : 0);
- if (ndata != data && snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_PCF2], &ndata, 1) != 1) {
+ if (ndata != data && snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_PCF2],
+ &ndata, 1) != 1) {
snd_i2c_unlock(ice->i2c);
return -EIO;
}
@@ -641,12 +675,13 @@ static int snd_ice1712_ews88mt_output_sense_put(struct snd_kcontrol *kcontrol, s
static int snd_ice1712_ews88mt_input_sense_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct ews_spec *spec = ice->spec;
int channel = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
unsigned char data;
snd_assert(channel >= 0 && channel <= 7, return 0);
snd_i2c_lock(ice->i2c);
- if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_PCF1], &data, 1) != 1) {
+ if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_PCF1], &data, 1) != 1) {
snd_i2c_unlock(ice->i2c);
return -EIO;
}
@@ -660,17 +695,19 @@ static int snd_ice1712_ews88mt_input_sense_get(struct snd_kcontrol *kcontrol, st
static int snd_ice1712_ews88mt_input_sense_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct ews_spec *spec = ice->spec;
int channel = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
unsigned char data, ndata;
snd_assert(channel >= 0 && channel <= 7, return 0);
snd_i2c_lock(ice->i2c);
- if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_PCF1], &data, 1) != 1) {
+ if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_PCF1], &data, 1) != 1) {
snd_i2c_unlock(ice->i2c);
return -EIO;
}
ndata = (data & ~(1 << channel)) | (ucontrol->value.enumerated.item[0] ? 0 : (1 << channel));
- if (ndata != data && snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_PCF1], &ndata, 1) != 1) {
+ if (ndata != data && snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_PCF1],
+ &ndata, 1) != 1) {
snd_i2c_unlock(ice->i2c);
return -EIO;
}
@@ -705,12 +742,13 @@ static struct snd_kcontrol_new snd_ice1712_ews88mt_output_sense __devinitdata =
static int snd_ice1712_ews88d_control_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct ews_spec *spec = ice->spec;
int shift = kcontrol->private_value & 0xff;
int invert = (kcontrol->private_value >> 8) & 1;
unsigned char data[2];
snd_i2c_lock(ice->i2c);
- if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_88D], data, 2) != 2) {
+ if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_88D], data, 2) != 2) {
snd_i2c_unlock(ice->i2c);
return -EIO;
}
@@ -725,13 +763,14 @@ static int snd_ice1712_ews88d_control_get(struct snd_kcontrol *kcontrol, struct
static int snd_ice1712_ews88d_control_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct ews_spec *spec = ice->spec;
int shift = kcontrol->private_value & 0xff;
int invert = (kcontrol->private_value >> 8) & 1;
unsigned char data[2], ndata[2];
int change;
snd_i2c_lock(ice->i2c);
- if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_88D], data, 2) != 2) {
+ if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_88D], data, 2) != 2) {
snd_i2c_unlock(ice->i2c);
return -EIO;
}
@@ -744,7 +783,8 @@ static int snd_ice1712_ews88d_control_put(struct snd_kcontrol *kcontrol, struct
ndata[shift >> 3] |= (1 << (shift & 7));
}
change = (data[shift >> 3] != ndata[shift >> 3]);
- if (change && snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_88D], data, 2) != 2) {
+ if (change &&
+ snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_88D], data, 2) != 2) {
snd_i2c_unlock(ice->i2c);
return -EIO;
}
@@ -778,11 +818,13 @@ static struct snd_kcontrol_new snd_ice1712_ews88d_controls[] __devinitdata = {
static int snd_ice1712_6fire_read_pca(struct snd_ice1712 *ice, unsigned char reg)
{
unsigned char byte;
+ struct ews_spec *spec = ice->spec;
+
snd_i2c_lock(ice->i2c);
byte = reg;
- snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_6FIRE], &byte, 1);
+ snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_6FIRE], &byte, 1);
byte = 0;
- if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_6FIRE], &byte, 1) != 1) {
+ if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_6FIRE], &byte, 1) != 1) {
snd_i2c_unlock(ice->i2c);
printk(KERN_ERR "cannot read pca\n");
return -EIO;
@@ -794,10 +836,12 @@ static int snd_ice1712_6fire_read_pca(struct snd_ice1712 *ice, unsigned char reg
static int snd_ice1712_6fire_write_pca(struct snd_ice1712 *ice, unsigned char reg, unsigned char data)
{
unsigned char bytes[2];
+ struct ews_spec *spec = ice->spec;
+
snd_i2c_lock(ice->i2c);
bytes[0] = reg;
bytes[1] = data;
- if (snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_6FIRE], bytes, 2) != 2) {
+ if (snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_6FIRE], bytes, 2) != 2) {
snd_i2c_unlock(ice->i2c);
return -EIO;
}
diff --git a/sound/pci/ice1712/hoontech.c b/sound/pci/ice1712/hoontech.c
index abcfd1da658..cf5c7c0898f 100644
--- a/sound/pci/ice1712/hoontech.c
+++ b/sound/pci/ice1712/hoontech.c
@@ -21,7 +21,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
@@ -34,6 +33,12 @@
#include "ice1712.h"
#include "hoontech.h"
+/* Hoontech-specific setting */
+struct hoontech_spec {
+ unsigned char boxbits[4];
+ unsigned int config;
+ unsigned short boxconfig[4];
+};
static void __devinit snd_ice1712_stdsp24_gpio_write(struct snd_ice1712 *ice, unsigned char byte)
{
@@ -50,169 +55,182 @@ static void __devinit snd_ice1712_stdsp24_gpio_write(struct snd_ice1712 *ice, un
static void __devinit snd_ice1712_stdsp24_darear(struct snd_ice1712 *ice, int activate)
{
+ struct hoontech_spec *spec = ice->spec;
mutex_lock(&ice->gpio_mutex);
- ICE1712_STDSP24_0_DAREAR(ice->spec.hoontech.boxbits, activate);
- snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[0]);
+ ICE1712_STDSP24_0_DAREAR(spec->boxbits, activate);
+ snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[0]);
mutex_unlock(&ice->gpio_mutex);
}
static void __devinit snd_ice1712_stdsp24_mute(struct snd_ice1712 *ice, int activate)
{
+ struct hoontech_spec *spec = ice->spec;
mutex_lock(&ice->gpio_mutex);
- ICE1712_STDSP24_3_MUTE(ice->spec.hoontech.boxbits, activate);
- snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[3]);
+ ICE1712_STDSP24_3_MUTE(spec->boxbits, activate);
+ snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]);
mutex_unlock(&ice->gpio_mutex);
}
static void __devinit snd_ice1712_stdsp24_insel(struct snd_ice1712 *ice, int activate)
{
+ struct hoontech_spec *spec = ice->spec;
mutex_lock(&ice->gpio_mutex);
- ICE1712_STDSP24_3_INSEL(ice->spec.hoontech.boxbits, activate);
- snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[3]);
+ ICE1712_STDSP24_3_INSEL(spec->boxbits, activate);
+ snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]);
mutex_unlock(&ice->gpio_mutex);
}
static void __devinit snd_ice1712_stdsp24_box_channel(struct snd_ice1712 *ice, int box, int chn, int activate)
{
+ struct hoontech_spec *spec = ice->spec;
+
mutex_lock(&ice->gpio_mutex);
/* select box */
- ICE1712_STDSP24_0_BOX(ice->spec.hoontech.boxbits, box);
- snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[0]);
+ ICE1712_STDSP24_0_BOX(spec->boxbits, box);
+ snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[0]);
/* prepare for write */
if (chn == 3)
- ICE1712_STDSP24_2_CHN4(ice->spec.hoontech.boxbits, 0);
- ICE1712_STDSP24_2_MIDI1(ice->spec.hoontech.boxbits, activate);
- snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]);
- snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[3]);
-
- ICE1712_STDSP24_1_CHN1(ice->spec.hoontech.boxbits, 1);
- ICE1712_STDSP24_1_CHN2(ice->spec.hoontech.boxbits, 1);
- ICE1712_STDSP24_1_CHN3(ice->spec.hoontech.boxbits, 1);
- ICE1712_STDSP24_2_CHN4(ice->spec.hoontech.boxbits, 1);
- snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[1]);
- snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]);
+ ICE1712_STDSP24_2_CHN4(spec->boxbits, 0);
+ ICE1712_STDSP24_2_MIDI1(spec->boxbits, activate);
+ snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
+ snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]);
+
+ ICE1712_STDSP24_1_CHN1(spec->boxbits, 1);
+ ICE1712_STDSP24_1_CHN2(spec->boxbits, 1);
+ ICE1712_STDSP24_1_CHN3(spec->boxbits, 1);
+ ICE1712_STDSP24_2_CHN4(spec->boxbits, 1);
+ snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[1]);
+ snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
udelay(100);
if (chn == 3) {
- ICE1712_STDSP24_2_CHN4(ice->spec.hoontech.boxbits, 0);
- snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]);
+ ICE1712_STDSP24_2_CHN4(spec->boxbits, 0);
+ snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
} else {
switch (chn) {
- case 0: ICE1712_STDSP24_1_CHN1(ice->spec.hoontech.boxbits, 0); break;
- case 1: ICE1712_STDSP24_1_CHN2(ice->spec.hoontech.boxbits, 0); break;
- case 2: ICE1712_STDSP24_1_CHN3(ice->spec.hoontech.boxbits, 0); break;
+ case 0: ICE1712_STDSP24_1_CHN1(spec->boxbits, 0); break;
+ case 1: ICE1712_STDSP24_1_CHN2(spec->boxbits, 0); break;
+ case 2: ICE1712_STDSP24_1_CHN3(spec->boxbits, 0); break;
}
- snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[1]);
+ snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[1]);
}
udelay(100);
- ICE1712_STDSP24_1_CHN1(ice->spec.hoontech.boxbits, 1);
- ICE1712_STDSP24_1_CHN2(ice->spec.hoontech.boxbits, 1);
- ICE1712_STDSP24_1_CHN3(ice->spec.hoontech.boxbits, 1);
- ICE1712_STDSP24_2_CHN4(ice->spec.hoontech.boxbits, 1);
- snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[1]);
- snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]);
+ ICE1712_STDSP24_1_CHN1(spec->boxbits, 1);
+ ICE1712_STDSP24_1_CHN2(spec->boxbits, 1);
+ ICE1712_STDSP24_1_CHN3(spec->boxbits, 1);
+ ICE1712_STDSP24_2_CHN4(spec->boxbits, 1);
+ snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[1]);
+ snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
udelay(100);
- ICE1712_STDSP24_2_MIDI1(ice->spec.hoontech.boxbits, 0);
- snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]);
+ ICE1712_STDSP24_2_MIDI1(spec->boxbits, 0);
+ snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
mutex_unlock(&ice->gpio_mutex);
}
static void __devinit snd_ice1712_stdsp24_box_midi(struct snd_ice1712 *ice, int box, int master)
{
+ struct hoontech_spec *spec = ice->spec;
+
mutex_lock(&ice->gpio_mutex);
/* select box */
- ICE1712_STDSP24_0_BOX(ice->spec.hoontech.boxbits, box);
- snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[0]);
+ ICE1712_STDSP24_0_BOX(spec->boxbits, box);
+ snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[0]);
- ICE1712_STDSP24_2_MIDIIN(ice->spec.hoontech.boxbits, 1);
- ICE1712_STDSP24_2_MIDI1(ice->spec.hoontech.boxbits, master);
- snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]);
- snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[3]);
+ ICE1712_STDSP24_2_MIDIIN(spec->boxbits, 1);
+ ICE1712_STDSP24_2_MIDI1(spec->boxbits, master);
+ snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
+ snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]);
udelay(100);
- ICE1712_STDSP24_2_MIDIIN(ice->spec.hoontech.boxbits, 0);
- snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]);
+ ICE1712_STDSP24_2_MIDIIN(spec->boxbits, 0);
+ snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
mdelay(10);
- ICE1712_STDSP24_2_MIDIIN(ice->spec.hoontech.boxbits, 1);
- snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]);
+ ICE1712_STDSP24_2_MIDIIN(spec->boxbits, 1);
+ snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
mutex_unlock(&ice->gpio_mutex);
}
static void __devinit snd_ice1712_stdsp24_midi2(struct snd_ice1712 *ice, int activate)
{
+ struct hoontech_spec *spec = ice->spec;
mutex_lock(&ice->gpio_mutex);
- ICE1712_STDSP24_3_MIDI2(ice->spec.hoontech.boxbits, activate);
- snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[3]);
+ ICE1712_STDSP24_3_MIDI2(spec->boxbits, activate);
+ snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]);
mutex_unlock(&ice->gpio_mutex);
}
static int __devinit snd_ice1712_hoontech_init(struct snd_ice1712 *ice)
{
+ struct hoontech_spec *spec;
int box, chn;
ice->num_total_dacs = 8;
ice->num_total_adcs = 8;
- ice->spec.hoontech.boxbits[0] =
- ice->spec.hoontech.boxbits[1] =
- ice->spec.hoontech.boxbits[2] =
- ice->spec.hoontech.boxbits[3] = 0; /* should be already */
-
- ICE1712_STDSP24_SET_ADDR(ice->spec.hoontech.boxbits, 0);
- ICE1712_STDSP24_CLOCK(ice->spec.hoontech.boxbits, 0, 1);
- ICE1712_STDSP24_0_BOX(ice->spec.hoontech.boxbits, 0);
- ICE1712_STDSP24_0_DAREAR(ice->spec.hoontech.boxbits, 0);
-
- ICE1712_STDSP24_SET_ADDR(ice->spec.hoontech.boxbits, 1);
- ICE1712_STDSP24_CLOCK(ice->spec.hoontech.boxbits, 1, 1);
- ICE1712_STDSP24_1_CHN1(ice->spec.hoontech.boxbits, 1);
- ICE1712_STDSP24_1_CHN2(ice->spec.hoontech.boxbits, 1);
- ICE1712_STDSP24_1_CHN3(ice->spec.hoontech.boxbits, 1);
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ ice->spec = spec;
+
+ ICE1712_STDSP24_SET_ADDR(spec->boxbits, 0);
+ ICE1712_STDSP24_CLOCK(spec->boxbits, 0, 1);
+ ICE1712_STDSP24_0_BOX(spec->boxbits, 0);
+ ICE1712_STDSP24_0_DAREAR(spec->boxbits, 0);
+
+ ICE1712_STDSP24_SET_ADDR(spec->boxbits, 1);
+ ICE1712_STDSP24_CLOCK(spec->boxbits, 1, 1);
+ ICE1712_STDSP24_1_CHN1(spec->boxbits, 1);
+ ICE1712_STDSP24_1_CHN2(spec->boxbits, 1);
+ ICE1712_STDSP24_1_CHN3(spec->boxbits, 1);
- ICE1712_STDSP24_SET_ADDR(ice->spec.hoontech.boxbits, 2);
- ICE1712_STDSP24_CLOCK(ice->spec.hoontech.boxbits, 2, 1);
- ICE1712_STDSP24_2_CHN4(ice->spec.hoontech.boxbits, 1);
- ICE1712_STDSP24_2_MIDIIN(ice->spec.hoontech.boxbits, 1);
- ICE1712_STDSP24_2_MIDI1(ice->spec.hoontech.boxbits, 0);
-
- ICE1712_STDSP24_SET_ADDR(ice->spec.hoontech.boxbits, 3);
- ICE1712_STDSP24_CLOCK(ice->spec.hoontech.boxbits, 3, 1);
- ICE1712_STDSP24_3_MIDI2(ice->spec.hoontech.boxbits, 0);
- ICE1712_STDSP24_3_MUTE(ice->spec.hoontech.boxbits, 1);
- ICE1712_STDSP24_3_INSEL(ice->spec.hoontech.boxbits, 0);
+ ICE1712_STDSP24_SET_ADDR(spec->boxbits, 2);
+ ICE1712_STDSP24_CLOCK(spec->boxbits, 2, 1);
+ ICE1712_STDSP24_2_CHN4(spec->boxbits, 1);
+ ICE1712_STDSP24_2_MIDIIN(spec->boxbits, 1);
+ ICE1712_STDSP24_2_MIDI1(spec->boxbits, 0);
+
+ ICE1712_STDSP24_SET_ADDR(spec->boxbits, 3);
+ ICE1712_STDSP24_CLOCK(spec->boxbits, 3, 1);
+ ICE1712_STDSP24_3_MIDI2(spec->boxbits, 0);
+ ICE1712_STDSP24_3_MUTE(spec->boxbits, 1);
+ ICE1712_STDSP24_3_INSEL(spec->boxbits, 0);
/* let's go - activate only functions in first box */
- ice->spec.hoontech.config = 0;
+ spec->config = 0;
/* ICE1712_STDSP24_MUTE |
ICE1712_STDSP24_INSEL |
ICE1712_STDSP24_DAREAR; */
- ice->spec.hoontech.boxconfig[0] = ICE1712_STDSP24_BOX_CHN1 |
+ spec->boxconfig[0] = ICE1712_STDSP24_BOX_CHN1 |
ICE1712_STDSP24_BOX_CHN2 |
ICE1712_STDSP24_BOX_CHN3 |
ICE1712_STDSP24_BOX_CHN4 |
ICE1712_STDSP24_BOX_MIDI1 |
ICE1712_STDSP24_BOX_MIDI2;
- ice->spec.hoontech.boxconfig[1] =
- ice->spec.hoontech.boxconfig[2] =
- ice->spec.hoontech.boxconfig[3] = 0;
- snd_ice1712_stdsp24_darear(ice, (ice->spec.hoontech.config & ICE1712_STDSP24_DAREAR) ? 1 : 0);
- snd_ice1712_stdsp24_mute(ice, (ice->spec.hoontech.config & ICE1712_STDSP24_MUTE) ? 1 : 0);
- snd_ice1712_stdsp24_insel(ice, (ice->spec.hoontech.config & ICE1712_STDSP24_INSEL) ? 1 : 0);
- for (box = 0; box < 4; box++) {
+ spec->boxconfig[1] =
+ spec->boxconfig[2] =
+ spec->boxconfig[3] = 0;
+ snd_ice1712_stdsp24_darear(ice,
+ (spec->config & ICE1712_STDSP24_DAREAR) ? 1 : 0);
+ snd_ice1712_stdsp24_mute(ice,
+ (spec->config & ICE1712_STDSP24_MUTE) ? 1 : 0);
+ snd_ice1712_stdsp24_insel(ice,
+ (spec->config & ICE1712_STDSP24_INSEL) ? 1 : 0);
+ for (box = 0; box < 1; box++) {
+ if (spec->boxconfig[box] & ICE1712_STDSP24_BOX_MIDI2)
+ snd_ice1712_stdsp24_midi2(ice, 1);
for (chn = 0; chn < 4; chn++)
- snd_ice1712_stdsp24_box_channel(ice, box, chn, (ice->spec.hoontech.boxconfig[box] & (1 << chn)) ? 1 : 0);
+ snd_ice1712_stdsp24_box_channel(ice, box, chn,
+ (spec->boxconfig[box] & (1 << chn)) ? 1 : 0);
snd_ice1712_stdsp24_box_midi(ice, box,
- (ice->spec.hoontech.boxconfig[box] & ICE1712_STDSP24_BOX_MIDI1) ? 1 : 0);
- if (ice->spec.hoontech.boxconfig[box] & ICE1712_STDSP24_BOX_MIDI2)
- snd_ice1712_stdsp24_midi2(ice, 1);
+ (spec->boxconfig[box] & ICE1712_STDSP24_BOX_MIDI1) ? 1 : 0);
}
return 0;
diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c
index 052fc3cb327..df292af6738 100644
--- a/sound/pci/ice1712/ice1712.c
+++ b/sound/pci/ice1712/ice1712.c
@@ -47,7 +47,6 @@
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
@@ -2491,6 +2490,7 @@ static int snd_ice1712_free(struct snd_ice1712 *ice)
pci_release_regions(ice->pci);
snd_ice1712_akm4xxx_free(ice);
pci_disable_device(ice->pci);
+ kfree(ice->spec);
kfree(ice);
return 0;
}
diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h
index 58640afa540..303cffe08bd 100644
--- a/sound/pci/ice1712/ice1712.h
+++ b/sound/pci/ice1712/ice1712.h
@@ -366,42 +366,7 @@ struct snd_ice1712 {
struct mutex gpio_mutex;
/* other board-specific data */
- union {
- /* additional i2c devices for EWS boards */
- struct snd_i2c_device *i2cdevs[3];
- /* AC97 register cache for Aureon */
- struct aureon_spec {
- unsigned short stac9744[64];
- unsigned int cs8415_mux;
- unsigned short master[2];
- unsigned short vol[8];
- unsigned char pca9554_out;
- } aureon;
- /* AC97 register cache for Phase28 */
- struct phase28_spec {
- unsigned short master[2];
- unsigned short vol[8];
- } phase28;
- /* a non-standard I2C device for revo51 */
- struct revo51_spec {
- struct snd_i2c_device *dev;
- struct snd_pt2258 *pt2258;
- } revo51;
- /* Hoontech-specific setting */
- struct hoontech_spec {
- unsigned char boxbits[4];
- unsigned int config;
- unsigned short boxconfig[4];
- } hoontech;
- struct {
- struct ak4114 *ak4114;
- unsigned int analog: 1;
- } juli;
- struct {
- struct ak4114 *ak4114;
- } prodigy192;
- } spec;
-
+ void *spec;
};
diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c
index 0b0bbb0d96b..f533850ec6e 100644
--- a/sound/pci/ice1712/ice1724.c
+++ b/sound/pci/ice1712/ice1724.c
@@ -22,7 +22,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
@@ -48,9 +47,11 @@
#include "vt1720_mobo.h"
#include "pontis.h"
#include "prodigy192.h"
+#include "prodigy_hifi.h"
#include "juli.h"
#include "phase.h"
#include "wtm.h"
+#include "se.h"
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("VIA ICEnsemble ICE1724/1720 (Envy24HT/PT)");
@@ -62,9 +63,11 @@ MODULE_SUPPORTED_DEVICE("{"
VT1720_MOBO_DEVICE_DESC
PONTIS_DEVICE_DESC
PRODIGY192_DEVICE_DESC
+ PRODIGY_HIFI_DEVICE_DESC
JULI_DEVICE_DESC
PHASE_DEVICE_DESC
WTM_DEVICE_DESC
+ SE_DEVICE_DESC
"{VIA,VT1720},"
"{VIA,VT1724},"
"{ICEnsemble,Generic ICE1724},"
@@ -1929,10 +1932,12 @@ static struct snd_ice1712_card_info *card_tables[] __devinitdata = {
snd_vt1724_aureon_cards,
snd_vt1720_mobo_cards,
snd_vt1720_pontis_cards,
+ snd_vt1724_prodigy_hifi_cards,
snd_vt1724_prodigy192_cards,
snd_vt1724_juli_cards,
snd_vt1724_phase_cards,
snd_vt1724_wtm_cards,
+ snd_vt1724_se_cards,
NULL,
};
@@ -1955,6 +1960,7 @@ unsigned char snd_vt1724_read_i2c(struct snd_ice1712 *ice,
unsigned char val;
mutex_lock(&ice->i2c_mutex);
+ wait_i2c_busy(ice);
outb(addr, ICEREG1724(ice, I2C_BYTE_ADDR));
outb(dev & ~VT1724_I2C_WRITE, ICEREG1724(ice, I2C_DEV_ADDR));
wait_i2c_busy(ice);
@@ -2170,6 +2176,7 @@ static int snd_vt1724_free(struct snd_ice1712 *ice)
pci_release_regions(ice->pci);
snd_ice1712_akm4xxx_free(ice);
pci_disable_device(ice->pci);
+ kfree(ice->spec);
kfree(ice);
return 0;
}
diff --git a/sound/pci/ice1712/juli.c b/sound/pci/ice1712/juli.c
index 1fbe3ef8e60..e8038c0ceb7 100644
--- a/sound/pci/ice1712/juli.c
+++ b/sound/pci/ice1712/juli.c
@@ -21,7 +21,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
@@ -33,6 +32,11 @@
#include "envy24ht.h"
#include "juli.h"
+struct juli_spec {
+ struct ak4114 *ak4114;
+ unsigned int analog: 1;
+};
+
/*
* chip addresses on I2C bus
*/
@@ -138,12 +142,13 @@ static struct snd_akm4xxx akm_juli_dac __devinitdata = {
static int __devinit juli_add_controls(struct snd_ice1712 *ice)
{
+ struct juli_spec *spec = ice->spec;
int err;
err = snd_ice1712_akm4xxx_build_controls(ice);
if (err < 0)
return err;
/* only capture SPDIF over AK4114 */
- err = snd_ak4114_build(ice->spec.juli.ak4114, NULL,
+ err = snd_ak4114_build(spec->ak4114, NULL,
ice->pcm_pro->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
if (err < 0)
return err;
@@ -167,13 +172,19 @@ static int __devinit juli_init(struct snd_ice1712 *ice)
0x41, 0x02, 0x2c, 0x00, 0x00
};
int err;
+ struct juli_spec *spec;
struct snd_akm4xxx *ak;
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ ice->spec = spec;
+
err = snd_ak4114_create(ice->card,
juli_ak4114_read,
juli_ak4114_write,
ak4114_init_vals, ak4114_init_txcsb,
- ice, &ice->spec.juli.ak4114);
+ ice, &spec->ak4114);
if (err < 0)
return err;
@@ -181,12 +192,12 @@ static int __devinit juli_init(struct snd_ice1712 *ice)
/* it seems that the analog doughter board detection does not work
reliably, so force the analog flag; it should be very rare
to use Juli@ without the analog doughter board */
- ice->spec.juli.analog = (ice->gpio.get_data(ice) & GPIO_ANALOG_PRESENT) ? 0 : 1;
+ spec->analog = (ice->gpio.get_data(ice) & GPIO_ANALOG_PRESENT) ? 0 : 1;
#else
- ice->spec.juli.analog = 1;
+ spec->analog = 1;
#endif
- if (ice->spec.juli.analog) {
+ if (spec->analog) {
printk(KERN_INFO "juli@: analog I/O detected\n");
ice->num_total_dacs = 2;
ice->num_total_adcs = 2;
diff --git a/sound/pci/ice1712/phase.c b/sound/pci/ice1712/phase.c
index 3ac25058bb5..9ab4a9f383c 100644
--- a/sound/pci/ice1712/phase.c
+++ b/sound/pci/ice1712/phase.c
@@ -33,7 +33,6 @@
* CDTI may be completely blocked by 74HCT125's gate #1 controlled by GPIO 3
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
@@ -48,6 +47,12 @@
#include "phase.h"
#include <sound/tlv.h>
+/* AC97 register cache for Phase28 */
+struct phase28_spec {
+ unsigned short master[2];
+ unsigned short vol[8];
+} phase28;
+
/* WM8770 registers */
#define WM_DAC_ATTEN 0x00 /* DAC1-8 analog attenuation */
#define WM_DAC_MASTER_ATTEN 0x08 /* DAC master analog attenuation */
@@ -313,27 +318,32 @@ static int wm_master_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
static int wm_master_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct phase28_spec *spec = ice->spec;
int i;
for (i=0; i<2; i++)
- ucontrol->value.integer.value[i] = ice->spec.phase28.master[i] & ~WM_VOL_MUTE;
+ ucontrol->value.integer.value[i] = spec->master[i] & ~WM_VOL_MUTE;
return 0;
}
static int wm_master_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct phase28_spec *spec = ice->spec;
int ch, change = 0;
snd_ice1712_save_gpio_status(ice);
for (ch = 0; ch < 2; ch++) {
- if (ucontrol->value.integer.value[ch] != ice->spec.phase28.master[ch]) {
+ unsigned int vol = ucontrol->value.integer.value[ch];
+ if (vol > WM_VOL_MAX)
+ continue;
+ vol |= spec->master[ch] & WM_VOL_MUTE;
+ if (vol != spec->master[ch]) {
int dac;
- ice->spec.phase28.master[ch] &= WM_VOL_MUTE;
- ice->spec.phase28.master[ch] |= ucontrol->value.integer.value[ch];
+ spec->master[ch] = vol;
for (dac = 0; dac < ice->num_total_dacs; dac += 2)
wm_set_vol(ice, WM_DAC_ATTEN + dac + ch,
- ice->spec.phase28.vol[dac + ch],
- ice->spec.phase28.master[ch]);
+ spec->vol[dac + ch],
+ spec->master[ch]);
change = 1;
}
}
@@ -382,12 +392,18 @@ static int __devinit phase28_init(struct snd_ice1712 *ice)
unsigned int tmp;
struct snd_akm4xxx *ak;
+ struct phase28_spec *spec;
const unsigned short *p;
int i;
ice->num_total_dacs = 8;
ice->num_total_adcs = 2;
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ ice->spec = spec;
+
// Initialize analog chips
ak = ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
if (!ak)
@@ -417,11 +433,11 @@ static int __devinit phase28_init(struct snd_ice1712 *ice)
snd_ice1712_restore_gpio_status(ice);
- ice->spec.phase28.master[0] = WM_VOL_MUTE;
- ice->spec.phase28.master[1] = WM_VOL_MUTE;
+ spec->master[0] = WM_VOL_MUTE;
+ spec->master[1] = WM_VOL_MUTE;
for (i = 0; i < ice->num_total_dacs; i++) {
- ice->spec.phase28.vol[i] = WM_VOL_MUTE;
- wm_set_vol(ice, i, ice->spec.phase28.vol[i], ice->spec.phase28.master[i % 2]);
+ spec->vol[i] = WM_VOL_MUTE;
+ wm_set_vol(ice, i, spec->vol[i], spec->master[i % 2]);
}
return 0;
@@ -443,18 +459,21 @@ static int wm_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *
static int wm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct phase28_spec *spec = ice->spec;
int i, ofs, voices;
voices = kcontrol->private_value >> 8;
ofs = kcontrol->private_value & 0xff;
for (i = 0; i < voices; i++)
- ucontrol->value.integer.value[i] = ice->spec.phase28.vol[ofs+i] & ~WM_VOL_MUTE;
+ ucontrol->value.integer.value[i] =
+ spec->vol[ofs+i] & ~WM_VOL_MUTE;
return 0;
}
static int wm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct phase28_spec *spec = ice->spec;
int i, idx, ofs, voices;
int change = 0;
@@ -462,12 +481,16 @@ static int wm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *
ofs = kcontrol->private_value & 0xff;
snd_ice1712_save_gpio_status(ice);
for (i = 0; i < voices; i++) {
- idx = WM_DAC_ATTEN + ofs + i;
- if (ucontrol->value.integer.value[i] != ice->spec.phase28.vol[ofs+i]) {
- ice->spec.phase28.vol[ofs+i] &= WM_VOL_MUTE;
- ice->spec.phase28.vol[ofs+i] |= ucontrol->value.integer.value[i];
- wm_set_vol(ice, idx, ice->spec.phase28.vol[ofs+i],
- ice->spec.phase28.master[i]);
+ unsigned int vol;
+ vol = ucontrol->value.integer.value[i];
+ if (vol > 0x7f)
+ continue;
+ vol |= spec->vol[ofs+i] & WM_VOL_MUTE;
+ if (vol != spec->vol[ofs+i]) {
+ spec->vol[ofs+i] = vol;
+ idx = WM_DAC_ATTEN + ofs + i;
+ wm_set_vol(ice, idx, spec->vol[ofs+i],
+ spec->master[i]);
change = 1;
}
}
@@ -489,19 +512,22 @@ static int wm_mute_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info
static int wm_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct phase28_spec *spec = ice->spec;
int voices, ofs, i;
voices = kcontrol->private_value >> 8;
ofs = kcontrol->private_value & 0xFF;
for (i = 0; i < voices; i++)
- ucontrol->value.integer.value[i] = (ice->spec.phase28.vol[ofs+i] & WM_VOL_MUTE) ? 0 : 1;
+ ucontrol->value.integer.value[i] =
+ (spec->vol[ofs+i] & WM_VOL_MUTE) ? 0 : 1;
return 0;
}
static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct phase28_spec *spec = ice->spec;
int change = 0, voices, ofs, i;
voices = kcontrol->private_value >> 8;
@@ -509,13 +535,13 @@ static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value
snd_ice1712_save_gpio_status(ice);
for (i = 0; i < voices; i++) {
- int val = (ice->spec.phase28.vol[ofs + i] & WM_VOL_MUTE) ? 0 : 1;
+ int val = (spec->vol[ofs + i] & WM_VOL_MUTE) ? 0 : 1;
if (ucontrol->value.integer.value[i] != val) {
- ice->spec.phase28.vol[ofs + i] &= ~WM_VOL_MUTE;
- ice->spec.phase28.vol[ofs + i] |=
+ spec->vol[ofs + i] &= ~WM_VOL_MUTE;
+ spec->vol[ofs + i] |=
ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE;
- wm_set_vol(ice, ofs + i, ice->spec.phase28.vol[ofs + i],
- ice->spec.phase28.master[i]);
+ wm_set_vol(ice, ofs + i, spec->vol[ofs + i],
+ spec->master[i]);
change = 1;
}
}
@@ -532,29 +558,33 @@ static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value
static int wm_master_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct phase28_spec *spec = ice->spec;
- ucontrol->value.integer.value[0] = (ice->spec.phase28.master[0] & WM_VOL_MUTE) ? 0 : 1;
- ucontrol->value.integer.value[1] = (ice->spec.phase28.master[1] & WM_VOL_MUTE) ? 0 : 1;
+ ucontrol->value.integer.value[0] =
+ (spec->master[0] & WM_VOL_MUTE) ? 0 : 1;
+ ucontrol->value.integer.value[1] =
+ (spec->master[1] & WM_VOL_MUTE) ? 0 : 1;
return 0;
}
static int wm_master_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct phase28_spec *spec = ice->spec;
int change = 0, i;
snd_ice1712_save_gpio_status(ice);
for (i = 0; i < 2; i++) {
- int val = (ice->spec.phase28.master[i] & WM_VOL_MUTE) ? 0 : 1;
+ int val = (spec->master[i] & WM_VOL_MUTE) ? 0 : 1;
if (ucontrol->value.integer.value[i] != val) {
int dac;
- ice->spec.phase28.master[i] &= ~WM_VOL_MUTE;
- ice->spec.phase28.master[i] |=
+ spec->master[i] &= ~WM_VOL_MUTE;
+ spec->master[i] |=
ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE;
for (dac = 0; dac < ice->num_total_dacs; dac += 2)
wm_set_vol(ice, WM_DAC_ATTEN + dac + i,
- ice->spec.phase28.vol[dac + i],
- ice->spec.phase28.master[i]);
+ spec->vol[dac + i],
+ spec->master[i]);
change = 1;
}
}
@@ -595,8 +625,10 @@ static int wm_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
unsigned short ovol, nvol;
int change = 0;
- snd_ice1712_save_gpio_status(ice);
nvol = ucontrol->value.integer.value[0];
+ if (nvol > PCM_RES)
+ return -EINVAL;
+ snd_ice1712_save_gpio_status(ice);
nvol = (nvol ? (nvol + PCM_MIN) : 0) & 0xff;
ovol = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff;
if (ovol != nvol) {
diff --git a/sound/pci/ice1712/pontis.c b/sound/pci/ice1712/pontis.c
index faefd52c1b8..4945c81e8a9 100644
--- a/sound/pci/ice1712/pontis.c
+++ b/sound/pci/ice1712/pontis.c
@@ -21,7 +21,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/ice1712/prodigy192.c b/sound/pci/ice1712/prodigy192.c
index 4180f9739ec..48cf40a8f32 100644
--- a/sound/pci/ice1712/prodigy192.c
+++ b/sound/pci/ice1712/prodigy192.c
@@ -54,7 +54,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
@@ -68,6 +67,12 @@
#include "stac946x.h"
#include <sound/tlv.h>
+struct prodigy192_spec {
+ struct ak4114 *ak4114;
+ /* rate change needs atomic mute/unmute of all dacs*/
+ struct mutex mute_mutex;
+};
+
static inline void stac9460_put(struct snd_ice1712 *ice, int reg, unsigned char val)
{
snd_vt1724_write_i2c(ice, PRODIGY192_STAC9460_ADDR, reg, val);
@@ -81,6 +86,24 @@ static inline unsigned char stac9460_get(struct snd_ice1712 *ice, int reg)
/*
* DAC mute control
*/
+
+/*
+ * idx = STAC9460 volume register number, mute: 0 = mute, 1 = unmute
+ */
+static int stac9460_dac_mute(struct snd_ice1712 *ice, int idx,
+ unsigned char mute)
+{
+ unsigned char new, old;
+ int change;
+ old = stac9460_get(ice, idx);
+ new = (~mute << 7 & 0x80) | (old & ~0x80);
+ change = (new != old);
+ if (change)
+ /*printk ("Volume register 0x%02x: 0x%02x\n", idx, new);*/
+ stac9460_put(ice, idx, new);
+ return change;
+}
+
#define stac9460_dac_mute_info snd_ctl_boolean_mono_info
static int stac9460_dac_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -101,20 +124,19 @@ static int stac9460_dac_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_e
static int stac9460_dac_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- unsigned char new, old;
- int idx;
- int change;
+ struct prodigy192_spec *spec = ice->spec;
+ int idx, change;
if (kcontrol->private_value)
idx = STAC946X_MASTER_VOLUME;
else
idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + STAC946X_LF_VOLUME;
- old = stac9460_get(ice, idx);
- new = (~ucontrol->value.integer.value[0]<< 7 & 0x80) | (old & ~0x80);
- change = (new != old);
- if (change)
- stac9460_put(ice, idx, new);
-
+ /* due to possible conflicts with stac9460_set_rate_val, mutexing */
+ mutex_lock(&spec->mute_mutex);
+ /*printk("Mute put: reg 0x%02x, ctrl value: 0x%02x\n", idx,
+ ucontrol->value.integer.value[0]);*/
+ change = stac9460_dac_mute(ice, idx, ucontrol->value.integer.value[0]);
+ mutex_unlock(&spec->mute_mutex);
return change;
}
@@ -162,6 +184,8 @@ static int stac9460_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
ovol = 0x7f - (tmp & 0x7f);
change = (ovol != nvol);
if (change) {
+ ovol = (0x7f - nvol) | (tmp & 0x80);
+ /*printk("DAC Volume: reg 0x%02x: 0x%02x\n", idx, ovol);*/
stac9460_put(ice, idx, (0x7f - nvol) | (tmp & 0x80));
}
return change;
@@ -241,7 +265,7 @@ static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
for (i = 0; i < 2; ++i) {
reg = STAC946X_MIC_L_VOLUME + i;
- nvol = ucontrol->value.integer.value[i];
+ nvol = ucontrol->value.integer.value[i] & 0x0f;
ovol = 0x0f - stac9460_get(ice, reg);
change = ((ovol & 0x0f) != nvol);
if (change)
@@ -251,121 +275,6 @@ static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
return change;
}
-#if 0
-/*
- * Headphone Amplifier
- */
-static int aureon_set_headphone_amp(struct snd_ice1712 *ice, int enable)
-{
- unsigned int tmp, tmp2;
-
- tmp2 = tmp = snd_ice1712_gpio_read(ice);
- if (enable)
- tmp |= AUREON_HP_SEL;
- else
- tmp &= ~ AUREON_HP_SEL;
- if (tmp != tmp2) {
- snd_ice1712_gpio_write(ice, tmp);
- return 1;
- }
- return 0;
-}
-
-static int aureon_get_headphone_amp(struct snd_ice1712 *ice)
-{
- unsigned int tmp = snd_ice1712_gpio_read(ice);
-
- return ( tmp & AUREON_HP_SEL )!= 0;
-}
-
-#define aureon_bool_info snd_ctl_boolean_mono_info
-
-static int aureon_hpamp_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
-
- ucontrol->value.integer.value[0] = aureon_get_headphone_amp(ice);
- return 0;
-}
-
-
-static int aureon_hpamp_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
-
- return aureon_set_headphone_amp(ice,ucontrol->value.integer.value[0]);
-}
-
-/*
- * Deemphasis
- */
-static int aureon_deemp_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- ucontrol->value.integer.value[0] = (wm_get(ice, WM_DAC_CTRL2) & 0xf) == 0xf;
- return 0;
-}
-
-static int aureon_deemp_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- int temp, temp2;
- temp2 = temp = wm_get(ice, WM_DAC_CTRL2);
- if (ucontrol->value.integer.value[0])
- temp |= 0xf;
- else
- temp &= ~0xf;
- if (temp != temp2) {
- wm_put(ice, WM_DAC_CTRL2, temp);
- return 1;
- }
- return 0;
-}
-
-/*
- * ADC Oversampling
- */
-static int aureon_oversampling_info(struct snd_kcontrol *k, struct snd_ctl_elem_info *uinfo)
-{
- static char *texts[2] = { "128x", "64x" };
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
-
- return 0;
-}
-
-static int aureon_oversampling_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- ucontrol->value.enumerated.item[0] = (wm_get(ice, WM_MASTER) & 0x8) == 0x8;
- return 0;
-}
-
-static int aureon_oversampling_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- int temp, temp2;
- struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
-
- temp2 = temp = wm_get(ice, WM_MASTER);
-
- if (ucontrol->value.enumerated.item[0])
- temp |= 0x8;
- else
- temp &= ~0x8;
-
- if (temp != temp2) {
- wm_put(ice, WM_MASTER, temp);
- return 1;
- }
- return 0;
-}
-#endif
static int stac9460_mic_sw_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -407,6 +316,57 @@ static int stac9460_mic_sw_put(struct snd_kcontrol *kcontrol,
stac9460_put(ice, STAC946X_GENERAL_PURPOSE, new);
return change;
}
+/*
+ * Handler for setting correct codec rate - called when rate change is detected
+ */
+static void stac9460_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate)
+{
+ unsigned char old, new;
+ int idx;
+ unsigned char changed[7];
+ struct snd_ice1712 *ice = ak->private_data[0];
+ struct prodigy192_spec *spec = ice->spec;
+
+ if (rate == 0) /* no hint - S/PDIF input is master, simply return */
+ return;
+ else if (rate <= 48000)
+ new = 0x08; /* 256x, base rate mode */
+ else if (rate <= 96000)
+ new = 0x11; /* 256x, mid rate mode */
+ else
+ new = 0x12; /* 128x, high rate mode */
+ old = stac9460_get(ice, STAC946X_MASTER_CLOCKING);
+ if (old == new)
+ return;
+ /* change detected, setting master clock, muting first */
+ /* due to possible conflicts with mute controls - mutexing */
+ mutex_lock(&spec->mute_mutex);
+ /* we have to remember current mute status for each DAC */
+ for (idx = 0; idx < 7 ; ++idx)
+ changed[idx] = stac9460_dac_mute(ice,
+ STAC946X_MASTER_VOLUME + idx, 0);
+ /*printk("Rate change: %d, new MC: 0x%02x\n", rate, new);*/
+ stac9460_put(ice, STAC946X_MASTER_CLOCKING, new);
+ udelay(10);
+ /* unmuting - only originally unmuted dacs -
+ * i.e. those changed when muting */
+ for (idx = 0; idx < 7 ; ++idx) {
+ if (changed[idx])
+ stac9460_dac_mute(ice, STAC946X_MASTER_VOLUME + idx, 1);
+ }
+ mutex_unlock(&spec->mute_mutex);
+}
+
+/* using akm infrastructure for setting rate of the codec */
+static struct snd_akm4xxx akmlike_stac9460 __devinitdata = {
+ .type = NON_AKM, /* special value */
+ .num_adcs = 6, /* not used in any way, just for completeness */
+ .num_dacs = 2,
+ .ops = {
+ .set_rate_val = stac9460_set_rate_val
+ }
+};
+
static const DECLARE_TLV_DB_SCALE(db_scale_dac, -19125, 75, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_adc, 0, 150, 0);
@@ -483,39 +443,8 @@ static struct snd_kcontrol_new stac_controls[] __devinitdata = {
.put = stac9460_mic_sw_put,
},
-#if 0
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Route",
- .info = wm_adc_mux_info,
- .get = wm_adc_mux_get,
- .put = wm_adc_mux_put,
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Headphone Amplifier Switch",
- .info = aureon_bool_info,
- .get = aureon_hpamp_get,
- .put = aureon_hpamp_put
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "DAC Deemphasis Switch",
- .info = aureon_bool_info,
- .get = aureon_deemp_get,
- .put = aureon_deemp_put
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "ADC Oversampling",
- .info = aureon_oversampling_info,
- .get = aureon_oversampling_get,
- .put = aureon_oversampling_put
- },
-#endif
};
-
/* AK4114 - ICE1724 connections on Prodigy192 + MI/ODI/O */
/* CDTO (pin 32) -- GPIO11 pin 86
* CDTI (pin 33) -- GPIO10 pin 77
@@ -712,16 +641,39 @@ static int prodigy192_ak4114_init(struct snd_ice1712 *ice)
static const unsigned char ak4114_init_txcsb[] = {
0x41, 0x02, 0x2c, 0x00, 0x00
};
+ struct prodigy192_spec *spec = ice->spec;
return snd_ak4114_create(ice->card,
prodigy192_ak4114_read,
prodigy192_ak4114_write,
ak4114_init_vals, ak4114_init_txcsb,
- ice, &ice->spec.prodigy192.ak4114);
+ ice, &spec->ak4114);
+}
+
+static void stac9460_proc_regs_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_ice1712 *ice = (struct snd_ice1712 *)entry->private_data;
+ int reg, val;
+ /* registers 0x0 - 0x14 */
+ for (reg = 0; reg <= 0x15; reg++) {
+ val = stac9460_get(ice, reg);
+ snd_iprintf(buffer, "0x%02x = 0x%02x\n", reg, val);
+ }
+}
+
+
+static void stac9460_proc_init(struct snd_ice1712 *ice)
+{
+ struct snd_info_entry *entry;
+ if (!snd_card_proc_new(ice->card, "stac9460_codec", &entry))
+ snd_info_set_text_ops(entry, ice, stac9460_proc_regs_read);
}
+
static int __devinit prodigy192_add_controls(struct snd_ice1712 *ice)
{
+ struct prodigy192_spec *spec = ice->spec;
unsigned int i;
int err;
@@ -731,7 +683,7 @@ static int __devinit prodigy192_add_controls(struct snd_ice1712 *ice)
if (err < 0)
return err;
}
- if (ice->spec.prodigy192.ak4114) {
+ if (spec->ak4114) {
/* ak4114 is connected */
for (i = 0; i < ARRAY_SIZE(ak4114_controls); i++) {
err = snd_ctl_add(ice->card,
@@ -740,12 +692,13 @@ static int __devinit prodigy192_add_controls(struct snd_ice1712 *ice)
if (err < 0)
return err;
}
- err = snd_ak4114_build(ice->spec.prodigy192.ak4114,
+ err = snd_ak4114_build(spec->ak4114,
NULL, /* ak4114 in MIO/DI/O handles no IEC958 output */
ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
if (err < 0)
return err;
}
+ stac9460_proc_init(ice);
return 0;
}
@@ -778,6 +731,7 @@ static int __devinit prodigy192_init(struct snd_ice1712 *ice)
{
static const unsigned short stac_inits_prodigy[] = {
STAC946X_RESET, 0,
+ STAC946X_MASTER_CLOCKING, 0x11,
/* STAC946X_MASTER_VOLUME, 0,
STAC946X_LF_VOLUME, 0,
STAC946X_RF_VOLUME, 0,
@@ -789,22 +743,39 @@ static int __devinit prodigy192_init(struct snd_ice1712 *ice)
};
const unsigned short *p;
int err = 0;
+ struct snd_akm4xxx *ak;
+ struct prodigy192_spec *spec;
/* prodigy 192 */
ice->num_total_dacs = 6;
ice->num_total_adcs = 2;
ice->vt1720 = 0; /* ice1724, e.g. 23 GPIOs */
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ ice->spec = spec;
+ mutex_init(&spec->mute_mutex);
+
/* initialize codec */
p = stac_inits_prodigy;
for (; *p != (unsigned short)-1; p += 2)
stac9460_put(ice, p[0], p[1]);
+ /* reusing the akm codecs infrastructure,
+ * for setting rate on stac9460 */
+ ak = ice->akm = kmalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
+ if (!ak)
+ return -ENOMEM;
+ ice->akm_codecs = 1;
+ err = snd_ice1712_akm4xxx_init(ak, &akmlike_stac9460, NULL, ice);
+ if (err < 0)
+ return err;
/* MI/ODI/O add on card with AK4114 */
if (prodigy192_miodio_exists(ice)) {
err = prodigy192_ak4114_init(ice);
/* from this moment if err = 0 then
- * ice->spec.prodigy192.ak4114 should not be null
+ * spec->ak4114 should not be null
*/
snd_printdd("AK4114 initialized with status %d\n", err);
} else
@@ -854,6 +825,10 @@ struct snd_ice1712_card_info snd_vt1724_prodigy192_cards[] __devinitdata = {
.build_controls = prodigy192_add_controls,
.eeprom_size = sizeof(prodigy71_eeprom),
.eeprom_data = prodigy71_eeprom,
+ /* the current MPU401 code loops infinitely
+ * when opening midi device
+ */
+ .no_mpu401 = 1,
},
{ } /* terminator */
};
diff --git a/sound/pci/ice1712/prodigy_hifi.c b/sound/pci/ice1712/prodigy_hifi.c
new file mode 100644
index 00000000000..043a93879bd
--- /dev/null
+++ b/sound/pci/ice1712/prodigy_hifi.c
@@ -0,0 +1,1210 @@
+/*
+ * ALSA driver for ICEnsemble VT1724 (Envy24HT)
+ *
+ * Lowlevel functions for Audiotrak Prodigy 7.1 Hifi
+ * based on pontis.c
+ *
+ * Copyright (c) 2007 Julian Scheel <julian@jusst.de>
+ * Copyright (c) 2007 allank
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/tlv.h>
+
+#include "ice1712.h"
+#include "envy24ht.h"
+#include "prodigy_hifi.h"
+
+struct prodigy_hifi_spec {
+ unsigned short master[2];
+ unsigned short vol[8];
+};
+
+/* I2C addresses */
+#define WM_DEV 0x34
+
+/* WM8776 registers */
+#define WM_HP_ATTEN_L 0x00 /* headphone left attenuation */
+#define WM_HP_ATTEN_R 0x01 /* headphone left attenuation */
+#define WM_HP_MASTER 0x02 /* headphone master (both channels),
+ override LLR */
+#define WM_DAC_ATTEN_L 0x03 /* digital left attenuation */
+#define WM_DAC_ATTEN_R 0x04
+#define WM_DAC_MASTER 0x05
+#define WM_PHASE_SWAP 0x06 /* DAC phase swap */
+#define WM_DAC_CTRL1 0x07
+#define WM_DAC_MUTE 0x08
+#define WM_DAC_CTRL2 0x09
+#define WM_DAC_INT 0x0a
+#define WM_ADC_INT 0x0b
+#define WM_MASTER_CTRL 0x0c
+#define WM_POWERDOWN 0x0d
+#define WM_ADC_ATTEN_L 0x0e
+#define WM_ADC_ATTEN_R 0x0f
+#define WM_ALC_CTRL1 0x10
+#define WM_ALC_CTRL2 0x11
+#define WM_ALC_CTRL3 0x12
+#define WM_NOISE_GATE 0x13
+#define WM_LIMITER 0x14
+#define WM_ADC_MUX 0x15
+#define WM_OUT_MUX 0x16
+#define WM_RESET 0x17
+
+/* Analog Recording Source :- Mic, LineIn, CD/Video, */
+
+/* implement capture source select control for WM8776 */
+
+#define WM_AIN1 "AIN1"
+#define WM_AIN2 "AIN2"
+#define WM_AIN3 "AIN3"
+#define WM_AIN4 "AIN4"
+#define WM_AIN5 "AIN5"
+
+/* GPIO pins of envy24ht connected to wm8766 */
+#define WM8766_SPI_CLK (1<<17) /* CLK, Pin97 on ICE1724 */
+#define WM8766_SPI_MD (1<<16) /* DATA VT1724 -> WM8766, Pin96 */
+#define WM8766_SPI_ML (1<<18) /* Latch, Pin98 */
+
+/* WM8766 registers */
+#define WM8766_DAC_CTRL 0x02 /* DAC Control */
+#define WM8766_INT_CTRL 0x03 /* Interface Control */
+#define WM8766_DAC_CTRL2 0x09
+#define WM8766_DAC_CTRL3 0x0a
+#define WM8766_RESET 0x1f
+#define WM8766_LDA1 0x00
+#define WM8766_LDA2 0x04
+#define WM8766_LDA3 0x06
+#define WM8766_RDA1 0x01
+#define WM8766_RDA2 0x05
+#define WM8766_RDA3 0x07
+#define WM8766_MUTE1 0x0C
+#define WM8766_MUTE2 0x0F
+
+
+/*
+ * Prodigy HD2
+ */
+#define AK4396_ADDR 0x00
+#define AK4396_CSN (1 << 8) /* CSN->GPIO8, pin 75 */
+#define AK4396_CCLK (1 << 9) /* CCLK->GPIO9, pin 76 */
+#define AK4396_CDTI (1 << 10) /* CDTI->GPIO10, pin 77 */
+
+/* ak4396 registers */
+#define AK4396_CTRL1 0x00
+#define AK4396_CTRL2 0x01
+#define AK4396_CTRL3 0x02
+#define AK4396_LCH_ATT 0x03
+#define AK4396_RCH_ATT 0x04
+
+
+/*
+ * get the current register value of WM codec
+ */
+static unsigned short wm_get(struct snd_ice1712 *ice, int reg)
+{
+ reg <<= 1;
+ return ((unsigned short)ice->akm[0].images[reg] << 8) |
+ ice->akm[0].images[reg + 1];
+}
+
+/*
+ * set the register value of WM codec and remember it
+ */
+static void wm_put_nocache(struct snd_ice1712 *ice, int reg, unsigned short val)
+{
+ unsigned short cval;
+ cval = (reg << 9) | val;
+ snd_vt1724_write_i2c(ice, WM_DEV, cval >> 8, cval & 0xff);
+}
+
+static void wm_put(struct snd_ice1712 *ice, int reg, unsigned short val)
+{
+ wm_put_nocache(ice, reg, val);
+ reg <<= 1;
+ ice->akm[0].images[reg] = val >> 8;
+ ice->akm[0].images[reg + 1] = val;
+}
+
+/*
+ * write data in the SPI mode
+ */
+
+static void set_gpio_bit(struct snd_ice1712 *ice, unsigned int bit, int val)
+{
+ unsigned int tmp = snd_ice1712_gpio_read(ice);
+ if (val)
+ tmp |= bit;
+ else
+ tmp &= ~bit;
+ snd_ice1712_gpio_write(ice, tmp);
+}
+
+/*
+ * SPI implementation for WM8766 codec - only writing supported, no readback
+ */
+
+static void wm8766_spi_send_word(struct snd_ice1712 *ice, unsigned int data)
+{
+ int i;
+ for (i = 0; i < 16; i++) {
+ set_gpio_bit(ice, WM8766_SPI_CLK, 0);
+ udelay(1);
+ set_gpio_bit(ice, WM8766_SPI_MD, data & 0x8000);
+ udelay(1);
+ set_gpio_bit(ice, WM8766_SPI_CLK, 1);
+ udelay(1);
+ data <<= 1;
+ }
+}
+
+static void wm8766_spi_write(struct snd_ice1712 *ice, unsigned int reg,
+ unsigned int data)
+{
+ unsigned int block;
+
+ snd_ice1712_gpio_set_dir(ice, WM8766_SPI_MD|
+ WM8766_SPI_CLK|WM8766_SPI_ML);
+ snd_ice1712_gpio_set_mask(ice, ~(WM8766_SPI_MD|
+ WM8766_SPI_CLK|WM8766_SPI_ML));
+ /* latch must be low when writing */
+ set_gpio_bit(ice, WM8766_SPI_ML, 0);
+ block = (reg << 9) | (data & 0x1ff);
+ wm8766_spi_send_word(ice, block); /* REGISTER ADDRESS */
+ /* release latch */
+ set_gpio_bit(ice, WM8766_SPI_ML, 1);
+ udelay(1);
+ /* restore */
+ snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask);
+ snd_ice1712_gpio_set_dir(ice, ice->gpio.direction);
+}
+
+
+/*
+ * serial interface for ak4396 - only writing supported, no readback
+ */
+
+static void ak4396_send_word(struct snd_ice1712 *ice, unsigned int data)
+{
+ int i;
+ for (i = 0; i < 16; i++) {
+ set_gpio_bit(ice, AK4396_CCLK, 0);
+ udelay(1);
+ set_gpio_bit(ice, AK4396_CDTI, data & 0x8000);
+ udelay(1);
+ set_gpio_bit(ice, AK4396_CCLK, 1);
+ udelay(1);
+ data <<= 1;
+ }
+}
+
+static void ak4396_write(struct snd_ice1712 *ice, unsigned int reg,
+ unsigned int data)
+{
+ unsigned int block;
+
+ snd_ice1712_gpio_set_dir(ice, AK4396_CSN|AK4396_CCLK|AK4396_CDTI);
+ snd_ice1712_gpio_set_mask(ice, ~(AK4396_CSN|AK4396_CCLK|AK4396_CDTI));
+ /* latch must be low when writing */
+ set_gpio_bit(ice, AK4396_CSN, 0);
+ block = ((AK4396_ADDR & 0x03) << 14) | (1 << 13) |
+ ((reg & 0x1f) << 8) | (data & 0xff);
+ ak4396_send_word(ice, block); /* REGISTER ADDRESS */
+ /* release latch */
+ set_gpio_bit(ice, AK4396_CSN, 1);
+ udelay(1);
+ /* restore */
+ snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask);
+ snd_ice1712_gpio_set_dir(ice, ice->gpio.direction);
+}
+
+
+/*
+ * ak4396 mixers
+ */
+
+
+
+/*
+ * DAC volume attenuation mixer control (-64dB to 0dB)
+ */
+
+static int ak4396_dac_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0; /* mute */
+ uinfo->value.integer.max = 0xFF; /* linear */
+ return 0;
+}
+
+static int ak4396_dac_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct prodigy_hifi_spec *spec = ice->spec;
+ int i;
+
+ for (i = 0; i < 2; i++)
+ ucontrol->value.integer.value[i] = spec->vol[i];
+
+ return 0;
+}
+
+static int ak4396_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct prodigy_hifi_spec *spec = ice->spec;
+ int i;
+ int change = 0;
+
+ mutex_lock(&ice->gpio_mutex);
+ for (i = 0; i < 2; i++) {
+ if (ucontrol->value.integer.value[i] != spec->vol[i]) {
+ spec->vol[i] = ucontrol->value.integer.value[i];
+ ak4396_write(ice, AK4396_LCH_ATT + i,
+ spec->vol[i] & 0xff);
+ change = 1;
+ }
+ }
+ mutex_unlock(&ice->gpio_mutex);
+ return change;
+}
+
+static const DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1);
+
+static struct snd_kcontrol_new prodigy_hd2_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 = ak4396_dac_vol_info,
+ .get = ak4396_dac_vol_get,
+ .put = ak4396_dac_vol_put,
+ .tlv = { .p = db_scale_wm_dac },
+ },
+};
+
+
+/* --------------- */
+
+/*
+ * Logarithmic volume values for WM87*6
+ * Computed as 20 * Log10(255 / x)
+ */
+static const unsigned char wm_vol[256] = {
+ 127, 48, 42, 39, 36, 34, 33, 31, 30, 29, 28, 27, 27, 26, 25, 25, 24, 24, 23,
+ 23, 22, 22, 21, 21, 21, 20, 20, 20, 19, 19, 19, 18, 18, 18, 18, 17, 17, 17,
+ 17, 16, 16, 16, 16, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 13, 13, 13,
+ 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11, 11, 11,
+ 11, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0
+};
+
+#define WM_VOL_MAX (sizeof(wm_vol) - 1)
+#define WM_VOL_MUTE 0x8000
+
+
+#define DAC_0dB 0xff
+#define DAC_RES 128
+#define DAC_MIN (DAC_0dB - DAC_RES)
+
+
+static void wm_set_vol(struct snd_ice1712 *ice, unsigned int index,
+ unsigned short vol, unsigned short master)
+{
+ unsigned char nvol;
+
+ if ((master & WM_VOL_MUTE) || (vol & WM_VOL_MUTE))
+ nvol = 0;
+ else {
+ nvol = (((vol & ~WM_VOL_MUTE) * (master & ~WM_VOL_MUTE)) / 128)
+ & WM_VOL_MAX;
+ nvol = (nvol ? (nvol + DAC_MIN) : 0) & 0xff;
+ }
+
+ wm_put(ice, index, nvol);
+ wm_put_nocache(ice, index, 0x100 | nvol);
+}
+
+static void wm8766_set_vol(struct snd_ice1712 *ice, unsigned int index,
+ unsigned short vol, unsigned short master)
+{
+ unsigned char nvol;
+
+ if ((master & WM_VOL_MUTE) || (vol & WM_VOL_MUTE))
+ nvol = 0;
+ else {
+ nvol = (((vol & ~WM_VOL_MUTE) * (master & ~WM_VOL_MUTE)) / 128)
+ & WM_VOL_MAX;
+ nvol = (nvol ? (nvol + DAC_MIN) : 0) & 0xff;
+ }
+
+ wm8766_spi_write(ice, index, (0x0100 | nvol));
+}
+
+
+/*
+ * DAC volume attenuation mixer control (-64dB to 0dB)
+ */
+
+static int wm_dac_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0; /* mute */
+ uinfo->value.integer.max = DAC_RES; /* 0dB, 0.5dB step */
+ return 0;
+}
+
+static int wm_dac_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct prodigy_hifi_spec *spec = ice->spec;
+ int i;
+
+ for (i = 0; i < 2; i++)
+ ucontrol->value.integer.value[i] =
+ spec->vol[2 + i] & ~WM_VOL_MUTE;
+ return 0;
+}
+
+static int wm_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct prodigy_hifi_spec *spec = ice->spec;
+ int i, idx, change = 0;
+
+ mutex_lock(&ice->gpio_mutex);
+ for (i = 0; i < 2; i++) {
+ if (ucontrol->value.integer.value[i] != spec->vol[2 + i]) {
+ idx = WM_DAC_ATTEN_L + i;
+ spec->vol[2 + i] &= WM_VOL_MUTE;
+ spec->vol[2 + i] |= ucontrol->value.integer.value[i];
+ wm_set_vol(ice, idx, spec->vol[2 + i], spec->master[i]);
+ change = 1;
+ }
+ }
+ mutex_unlock(&ice->gpio_mutex);
+ return change;
+}
+
+
+/*
+ * WM8766 DAC volume attenuation mixer control
+ */
+static int wm8766_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int voices = kcontrol->private_value >> 8;
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = voices;
+ uinfo->value.integer.min = 0; /* mute */
+ uinfo->value.integer.max = DAC_RES; /* 0dB */
+ return 0;
+}
+
+static int wm8766_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct prodigy_hifi_spec *spec = ice->spec;
+ int i, ofs, voices;
+
+ voices = kcontrol->private_value >> 8;
+ ofs = kcontrol->private_value & 0xff;
+ for (i = 0; i < voices; i++)
+ ucontrol->value.integer.value[i] = spec->vol[ofs + i];
+ return 0;
+}
+
+static int wm8766_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct prodigy_hifi_spec *spec = ice->spec;
+ int i, idx, ofs, voices;
+ int change = 0;
+
+ voices = kcontrol->private_value >> 8;
+ ofs = kcontrol->private_value & 0xff;
+ mutex_lock(&ice->gpio_mutex);
+ for (i = 0; i < voices; i++) {
+ if (ucontrol->value.integer.value[i] != spec->vol[ofs + i]) {
+ idx = WM8766_LDA1 + ofs + i;
+ spec->vol[ofs + i] &= WM_VOL_MUTE;
+ spec->vol[ofs + i] |= ucontrol->value.integer.value[i];
+ wm8766_set_vol(ice, idx,
+ spec->vol[ofs + i], spec->master[i]);
+ change = 1;
+ }
+ }
+ mutex_unlock(&ice->gpio_mutex);
+ return change;
+}
+
+/*
+ * Master volume attenuation mixer control / applied to WM8776+WM8766
+ */
+static int wm_master_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = DAC_RES;
+ return 0;
+}
+
+static int wm_master_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct prodigy_hifi_spec *spec = ice->spec;
+ int i;
+ for (i = 0; i < 2; i++)
+ ucontrol->value.integer.value[i] = spec->master[i];
+ return 0;
+}
+
+static int wm_master_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct prodigy_hifi_spec *spec = ice->spec;
+ int ch, change = 0;
+
+ mutex_lock(&ice->gpio_mutex);
+ for (ch = 0; ch < 2; ch++) {
+ if (ucontrol->value.integer.value[ch] != spec->master[ch]) {
+ spec->master[ch] = ucontrol->value.integer.value[ch];
+
+ /* Apply to front DAC */
+ wm_set_vol(ice, WM_DAC_ATTEN_L + ch,
+ spec->vol[2 + ch], spec->master[ch]);
+
+ wm8766_set_vol(ice, WM8766_LDA1 + ch,
+ spec->vol[0 + ch], spec->master[ch]);
+
+ wm8766_set_vol(ice, WM8766_LDA2 + ch,
+ spec->vol[4 + ch], spec->master[ch]);
+
+ wm8766_set_vol(ice, WM8766_LDA3 + ch,
+ spec->vol[6 + ch], spec->master[ch]);
+ change = 1;
+ }
+ }
+ mutex_unlock(&ice->gpio_mutex);
+ return change;
+}
+
+
+/* KONSTI */
+
+static int wm_adc_mux_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static char* texts[32] = {
+ "NULL", WM_AIN1, WM_AIN2, WM_AIN1 "+" WM_AIN2,
+ WM_AIN3, WM_AIN1 "+" WM_AIN3, WM_AIN2 "+" WM_AIN3,
+ WM_AIN1 "+" WM_AIN2 "+" WM_AIN3,
+ WM_AIN4, WM_AIN1 "+" WM_AIN4, WM_AIN2 "+" WM_AIN4,
+ WM_AIN1 "+" WM_AIN2 "+" WM_AIN4,
+ WM_AIN3 "+" WM_AIN4, WM_AIN1 "+" WM_AIN3 "+" WM_AIN4,
+ WM_AIN2 "+" WM_AIN3 "+" WM_AIN4,
+ WM_AIN1 "+" WM_AIN2 "+" WM_AIN3 "+" WM_AIN4,
+ WM_AIN5, WM_AIN1 "+" WM_AIN5, WM_AIN2 "+" WM_AIN5,
+ WM_AIN1 "+" WM_AIN2 "+" WM_AIN5,
+ WM_AIN3 "+" WM_AIN5, WM_AIN1 "+" WM_AIN3 "+" WM_AIN5,
+ WM_AIN2 "+" WM_AIN3 "+" WM_AIN5,
+ WM_AIN1 "+" WM_AIN2 "+" WM_AIN3 "+" WM_AIN5,
+ WM_AIN4 "+" WM_AIN5, WM_AIN1 "+" WM_AIN4 "+" WM_AIN5,
+ WM_AIN2 "+" WM_AIN4 "+" WM_AIN5,
+ WM_AIN1 "+" WM_AIN2 "+" WM_AIN4 "+" WM_AIN5,
+ WM_AIN3 "+" WM_AIN4 "+" WM_AIN5,
+ WM_AIN1 "+" WM_AIN3 "+" WM_AIN4 "+" WM_AIN5,
+ WM_AIN2 "+" WM_AIN3 "+" WM_AIN4 "+" WM_AIN5,
+ WM_AIN1 "+" WM_AIN2 "+" WM_AIN3 "+" WM_AIN4 "+" WM_AIN5
+ };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 32;
+ if (uinfo->value.enumerated.item > 31)
+ uinfo->value.enumerated.item = 31;
+ strcpy(uinfo->value.enumerated.name,
+ texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int wm_adc_mux_enum_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+
+ mutex_lock(&ice->gpio_mutex);
+ ucontrol->value.integer.value[0] = wm_get(ice, WM_ADC_MUX) & 0x1f;
+ mutex_unlock(&ice->gpio_mutex);
+ return 0;
+}
+
+static int wm_adc_mux_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ unsigned short oval, nval;
+ int change = 0;
+
+ mutex_lock(&ice->gpio_mutex);
+ oval = wm_get(ice, WM_ADC_MUX);
+ nval = (oval & 0xe0) | ucontrol->value.integer.value[0];
+ if (nval != oval) {
+ wm_put(ice, WM_ADC_MUX, nval);
+ change = 1;
+ }
+ mutex_unlock(&ice->gpio_mutex);
+ return change;
+}
+
+/* KONSTI */
+
+/*
+ * ADC gain mixer control (-64dB to 0dB)
+ */
+
+#define ADC_0dB 0xcf
+#define ADC_RES 128
+#define ADC_MIN (ADC_0dB - ADC_RES)
+
+static int wm_adc_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0; /* mute (-64dB) */
+ uinfo->value.integer.max = ADC_RES; /* 0dB, 0.5dB step */
+ return 0;
+}
+
+static int wm_adc_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ unsigned short val;
+ int i;
+
+ mutex_lock(&ice->gpio_mutex);
+ for (i = 0; i < 2; i++) {
+ val = wm_get(ice, WM_ADC_ATTEN_L + i) & 0xff;
+ val = val > ADC_MIN ? (val - ADC_MIN) : 0;
+ ucontrol->value.integer.value[i] = val;
+ }
+ mutex_unlock(&ice->gpio_mutex);
+ return 0;
+}
+
+static int wm_adc_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ unsigned short ovol, nvol;
+ int i, idx, change = 0;
+
+ mutex_lock(&ice->gpio_mutex);
+ for (i = 0; i < 2; i++) {
+ nvol = ucontrol->value.integer.value[i];
+ nvol = nvol ? (nvol + ADC_MIN) : 0;
+ idx = WM_ADC_ATTEN_L + i;
+ ovol = wm_get(ice, idx) & 0xff;
+ if (ovol != nvol) {
+ wm_put(ice, idx, nvol);
+ change = 1;
+ }
+ }
+ mutex_unlock(&ice->gpio_mutex);
+ return change;
+}
+
+/*
+ * ADC input mux mixer control
+ */
+#define wm_adc_mux_info snd_ctl_boolean_mono_info
+
+static int wm_adc_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ int bit = kcontrol->private_value;
+
+ mutex_lock(&ice->gpio_mutex);
+ ucontrol->value.integer.value[0] =
+ (wm_get(ice, WM_ADC_MUX) & (1 << bit)) ? 1 : 0;
+ mutex_unlock(&ice->gpio_mutex);
+ return 0;
+}
+
+static int wm_adc_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ int bit = kcontrol->private_value;
+ unsigned short oval, nval;
+ int change;
+
+ mutex_lock(&ice->gpio_mutex);
+ nval = oval = wm_get(ice, WM_ADC_MUX);
+ if (ucontrol->value.integer.value[0])
+ nval |= (1 << bit);
+ else
+ nval &= ~(1 << bit);
+ change = nval != oval;
+ if (change) {
+ wm_put(ice, WM_ADC_MUX, nval);
+ }
+ mutex_unlock(&ice->gpio_mutex);
+ return 0;
+}
+
+/*
+ * Analog bypass (In -> Out)
+ */
+#define wm_bypass_info snd_ctl_boolean_mono_info
+
+static int wm_bypass_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+
+ mutex_lock(&ice->gpio_mutex);
+ ucontrol->value.integer.value[0] =
+ (wm_get(ice, WM_OUT_MUX) & 0x04) ? 1 : 0;
+ mutex_unlock(&ice->gpio_mutex);
+ return 0;
+}
+
+static int wm_bypass_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ unsigned short val, oval;
+ int change = 0;
+
+ mutex_lock(&ice->gpio_mutex);
+ val = oval = wm_get(ice, WM_OUT_MUX);
+ if (ucontrol->value.integer.value[0])
+ val |= 0x04;
+ else
+ val &= ~0x04;
+ if (val != oval) {
+ wm_put(ice, WM_OUT_MUX, val);
+ change = 1;
+ }
+ mutex_unlock(&ice->gpio_mutex);
+ return change;
+}
+
+/*
+ * Left/Right swap
+ */
+#define wm_chswap_info snd_ctl_boolean_mono_info
+
+static int wm_chswap_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+
+ mutex_lock(&ice->gpio_mutex);
+ ucontrol->value.integer.value[0] =
+ (wm_get(ice, WM_DAC_CTRL1) & 0xf0) != 0x90;
+ mutex_unlock(&ice->gpio_mutex);
+ return 0;
+}
+
+static int wm_chswap_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ unsigned short val, oval;
+ int change = 0;
+
+ mutex_lock(&ice->gpio_mutex);
+ oval = wm_get(ice, WM_DAC_CTRL1);
+ val = oval & 0x0f;
+ if (ucontrol->value.integer.value[0])
+ val |= 0x60;
+ else
+ val |= 0x90;
+ if (val != oval) {
+ wm_put(ice, WM_DAC_CTRL1, val);
+ wm_put_nocache(ice, WM_DAC_CTRL1, val);
+ change = 1;
+ }
+ mutex_unlock(&ice->gpio_mutex);
+ return change;
+}
+
+
+/*
+ * mixers
+ */
+
+static struct snd_kcontrol_new prodigy_hifi_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,
+ .tlv = { .p = db_scale_wm_dac }
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .name = "Front Playback Volume",
+ .info = wm_dac_vol_info,
+ .get = wm_dac_vol_get,
+ .put = wm_dac_vol_put,
+ .tlv = { .p = db_scale_wm_dac },
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .name = "Rear Playback Volume",
+ .info = wm8766_vol_info,
+ .get = wm8766_vol_get,
+ .put = wm8766_vol_put,
+ .private_value = (2 << 8) | 0,
+ .tlv = { .p = db_scale_wm_dac },
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .name = "Center Playback Volume",
+ .info = wm8766_vol_info,
+ .get = wm8766_vol_get,
+ .put = wm8766_vol_put,
+ .private_value = (1 << 8) | 4,
+ .tlv = { .p = db_scale_wm_dac }
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .name = "LFE Playback Volume",
+ .info = wm8766_vol_info,
+ .get = wm8766_vol_get,
+ .put = wm8766_vol_put,
+ .private_value = (1 << 8) | 5,
+ .tlv = { .p = db_scale_wm_dac }
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .name = "Side Playback Volume",
+ .info = wm8766_vol_info,
+ .get = wm8766_vol_get,
+ .put = wm8766_vol_put,
+ .private_value = (2 << 8) | 6,
+ .tlv = { .p = db_scale_wm_dac },
+ },
+ {
+ .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_wm_dac },
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "CD Capture Switch",
+ .info = wm_adc_mux_info,
+ .get = wm_adc_mux_get,
+ .put = wm_adc_mux_put,
+ .private_value = 0,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Line Capture Switch",
+ .info = wm_adc_mux_info,
+ .get = wm_adc_mux_get,
+ .put = wm_adc_mux_put,
+ .private_value = 1,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Bypass Switch",
+ .info = wm_bypass_info,
+ .get = wm_bypass_get,
+ .put = wm_bypass_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Swap Output Channels",
+ .info = wm_chswap_info,
+ .get = wm_chswap_get,
+ .put = wm_chswap_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Capture Source",
+ .info = wm_adc_mux_enum_info,
+ .get = wm_adc_mux_enum_get,
+ .put = wm_adc_mux_enum_put,
+ },
+};
+
+/*
+ * WM codec registers
+ */
+static void wm_proc_regs_write(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_ice1712 *ice = entry->private_data;
+ char line[64];
+ unsigned int reg, val;
+ mutex_lock(&ice->gpio_mutex);
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ if (sscanf(line, "%x %x", &reg, &val) != 2)
+ continue;
+ if (reg <= 0x17 && val <= 0xffff)
+ wm_put(ice, reg, val);
+ }
+ mutex_unlock(&ice->gpio_mutex);
+}
+
+static void wm_proc_regs_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_ice1712 *ice = entry->private_data;
+ int reg, val;
+
+ mutex_lock(&ice->gpio_mutex);
+ for (reg = 0; reg <= 0x17; reg++) {
+ val = wm_get(ice, reg);
+ snd_iprintf(buffer, "%02x = %04x\n", reg, val);
+ }
+ mutex_unlock(&ice->gpio_mutex);
+}
+
+static void wm_proc_init(struct snd_ice1712 *ice)
+{
+ struct snd_info_entry *entry;
+ if (!snd_card_proc_new(ice->card, "wm_codec", &entry)) {
+ snd_info_set_text_ops(entry, ice, wm_proc_regs_read);
+ entry->mode |= S_IWUSR;
+ entry->c.text.write = wm_proc_regs_write;
+ }
+}
+
+static int __devinit prodigy_hifi_add_controls(struct snd_ice1712 *ice)
+{
+ unsigned int i;
+ int err;
+
+ for (i = 0; i < ARRAY_SIZE(prodigy_hifi_controls); i++) {
+ err = snd_ctl_add(ice->card,
+ snd_ctl_new1(&prodigy_hifi_controls[i], ice));
+ if (err < 0)
+ return err;
+ }
+
+ wm_proc_init(ice);
+
+ return 0;
+}
+
+static int __devinit prodigy_hd2_add_controls(struct snd_ice1712 *ice)
+{
+ unsigned int i;
+ int err;
+
+ for (i = 0; i < ARRAY_SIZE(prodigy_hd2_controls); i++) {
+ err = snd_ctl_add(ice->card,
+ snd_ctl_new1(&prodigy_hd2_controls[i], ice));
+ if (err < 0)
+ return err;
+ }
+
+ wm_proc_init(ice);
+
+ return 0;
+}
+
+
+/*
+ * initialize the chip
+ */
+static int __devinit prodigy_hifi_init(struct snd_ice1712 *ice)
+{
+ static unsigned short wm_inits[] = {
+ /* These come first to reduce init pop noise */
+ WM_ADC_MUX, 0x0003, /* ADC mute */
+ /* 0x00c0 replaced by 0x0003 */
+
+ WM_DAC_MUTE, 0x0001, /* DAC softmute */
+ WM_DAC_CTRL1, 0x0000, /* DAC mute */
+
+ WM_POWERDOWN, 0x0008, /* All power-up except HP */
+ WM_RESET, 0x0000, /* reset */
+ };
+ static unsigned short wm_inits2[] = {
+ WM_MASTER_CTRL, 0x0022, /* 256fs, slave mode */
+ WM_DAC_INT, 0x0022, /* I2S, normal polarity, 24bit */
+ WM_ADC_INT, 0x0022, /* I2S, normal polarity, 24bit */
+ WM_DAC_CTRL1, 0x0090, /* DAC L/R */
+ WM_OUT_MUX, 0x0001, /* OUT DAC */
+ WM_HP_ATTEN_L, 0x0179, /* HP 0dB */
+ WM_HP_ATTEN_R, 0x0179, /* HP 0dB */
+ WM_DAC_ATTEN_L, 0x0000, /* DAC 0dB */
+ WM_DAC_ATTEN_L, 0x0100, /* DAC 0dB */
+ WM_DAC_ATTEN_R, 0x0000, /* DAC 0dB */
+ WM_DAC_ATTEN_R, 0x0100, /* DAC 0dB */
+ WM_PHASE_SWAP, 0x0000, /* phase normal */
+#if 0
+ WM_DAC_MASTER, 0x0100, /* DAC master muted */
+#endif
+ WM_DAC_CTRL2, 0x0000, /* no deemphasis, no ZFLG */
+ WM_ADC_ATTEN_L, 0x0000, /* ADC muted */
+ WM_ADC_ATTEN_R, 0x0000, /* ADC muted */
+#if 1
+ WM_ALC_CTRL1, 0x007b, /* */
+ WM_ALC_CTRL2, 0x0000, /* */
+ WM_ALC_CTRL3, 0x0000, /* */
+ WM_NOISE_GATE, 0x0000, /* */
+#endif
+ WM_DAC_MUTE, 0x0000, /* DAC unmute */
+ WM_ADC_MUX, 0x0003, /* ADC unmute, both CD/Line On */
+ };
+ static unsigned short wm8766_inits[] = {
+ WM8766_RESET, 0x0000,
+ WM8766_DAC_CTRL, 0x0120,
+ WM8766_INT_CTRL, 0x0022, /* I2S Normal Mode, 24 bit */
+ WM8766_DAC_CTRL2, 0x0001,
+ WM8766_DAC_CTRL3, 0x0080,
+ WM8766_LDA1, 0x0100,
+ WM8766_LDA2, 0x0100,
+ WM8766_LDA3, 0x0100,
+ WM8766_RDA1, 0x0100,
+ WM8766_RDA2, 0x0100,
+ WM8766_RDA3, 0x0100,
+ WM8766_MUTE1, 0x0000,
+ WM8766_MUTE2, 0x0000,
+ };
+
+ struct prodigy_hifi_spec *spec;
+ unsigned int i;
+
+ ice->vt1720 = 0;
+ ice->vt1724 = 1;
+
+ ice->num_total_dacs = 8;
+ ice->num_total_adcs = 1;
+
+ /* HACK - use this as the SPDIF source.
+ * don't call snd_ice1712_gpio_get/put(), otherwise it's overwritten
+ */
+ ice->gpio.saved[0] = 0;
+ /* to remeber the register values */
+
+ ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
+ if (! ice->akm)
+ return -ENOMEM;
+ ice->akm_codecs = 1;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ ice->spec = spec;
+
+ /* initialize WM8776 codec */
+ for (i = 0; i < ARRAY_SIZE(wm_inits); i += 2)
+ wm_put(ice, wm_inits[i], wm_inits[i+1]);
+ schedule_timeout_uninterruptible(1);
+ for (i = 0; i < ARRAY_SIZE(wm_inits2); i += 2)
+ wm_put(ice, wm_inits2[i], wm_inits2[i+1]);
+
+ /* initialize WM8766 codec */
+ for (i = 0; i < ARRAY_SIZE(wm8766_inits); i += 2)
+ wm8766_spi_write(ice, wm8766_inits[i], wm8766_inits[i+1]);
+
+
+ return 0;
+}
+
+
+/*
+ * initialize the chip
+ */
+static int __devinit prodigy_hd2_init(struct snd_ice1712 *ice)
+{
+ static unsigned short ak4396_inits[] = {
+ AK4396_CTRL1, 0x87, /* I2S Normal Mode, 24 bit */
+ AK4396_CTRL2, 0x02,
+ AK4396_CTRL3, 0x00,
+ AK4396_LCH_ATT, 0x00,
+ AK4396_RCH_ATT, 0x00,
+ };
+
+ struct prodigy_hifi_spec *spec;
+ unsigned int i;
+
+ ice->vt1720 = 0;
+ ice->vt1724 = 1;
+
+ ice->num_total_dacs = 1;
+ ice->num_total_adcs = 1;
+
+ /* HACK - use this as the SPDIF source.
+ * don't call snd_ice1712_gpio_get/put(), otherwise it's overwritten
+ */
+ ice->gpio.saved[0] = 0;
+ /* to remeber the register values */
+
+ ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
+ if (! ice->akm)
+ return -ENOMEM;
+ ice->akm_codecs = 1;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ ice->spec = spec;
+
+ /* initialize ak4396 codec */
+ /* reset codec */
+ ak4396_write(ice, AK4396_CTRL1, 0x86);
+ msleep(100);
+ ak4396_write(ice, AK4396_CTRL1, 0x87);
+
+ for (i = 0; i < ARRAY_SIZE(ak4396_inits); i += 2)
+ ak4396_write(ice, ak4396_inits[i], ak4396_inits[i+1]);
+
+ return 0;
+}
+
+
+static unsigned char prodigy71hifi_eeprom[] __devinitdata = {
+ 0x4b, /* SYSCONF: clock 512, spdif-in/ADC, 4DACs */
+ 0x80, /* ACLINK: I2S */
+ 0xfc, /* I2S: vol, 96k, 24bit, 192k */
+ 0xc3, /* SPDIF: out-en, out-int, spdif-in */
+ 0xff, /* GPIO_DIR */
+ 0xff, /* GPIO_DIR1 */
+ 0x5f, /* GPIO_DIR2 */
+ 0x00, /* GPIO_MASK */
+ 0x00, /* GPIO_MASK1 */
+ 0x00, /* GPIO_MASK2 */
+ 0x00, /* GPIO_STATE */
+ 0x00, /* GPIO_STATE1 */
+ 0x00, /* GPIO_STATE2 */
+};
+
+static unsigned char prodigyhd2_eeprom[] __devinitdata = {
+ 0x4b, /* SYSCONF: clock 512, spdif-in/ADC, 4DACs */
+ 0x80, /* ACLINK: I2S */
+ 0xfc, /* I2S: vol, 96k, 24bit, 192k */
+ 0xc3, /* SPDIF: out-en, out-int, spdif-in */
+ 0xff, /* GPIO_DIR */
+ 0xff, /* GPIO_DIR1 */
+ 0x5f, /* GPIO_DIR2 */
+ 0x00, /* GPIO_MASK */
+ 0x00, /* GPIO_MASK1 */
+ 0x00, /* GPIO_MASK2 */
+ 0x00, /* GPIO_STATE */
+ 0x00, /* GPIO_STATE1 */
+ 0x00, /* GPIO_STATE2 */
+};
+
+static unsigned char fortissimo4_eeprom[] __devinitdata = {
+ 0x43, /* SYSCONF: clock 512, ADC, 4DACs */
+ 0x80, /* ACLINK: I2S */
+ 0xfc, /* I2S: vol, 96k, 24bit, 192k */
+ 0xc1, /* SPDIF: out-en, out-int */
+ 0xff, /* GPIO_DIR */
+ 0xff, /* GPIO_DIR1 */
+ 0x5f, /* GPIO_DIR2 */
+ 0x00, /* GPIO_MASK */
+ 0x00, /* GPIO_MASK1 */
+ 0x00, /* GPIO_MASK2 */
+ 0x00, /* GPIO_STATE */
+ 0x00, /* GPIO_STATE1 */
+ 0x00, /* GPIO_STATE2 */
+};
+
+/* entry point */
+struct snd_ice1712_card_info snd_vt1724_prodigy_hifi_cards[] __devinitdata = {
+ {
+ .subvendor = VT1724_SUBDEVICE_PRODIGY_HIFI,
+ .name = "Audiotrak Prodigy 7.1 HiFi",
+ .model = "prodigy71hifi",
+ .chip_init = prodigy_hifi_init,
+ .build_controls = prodigy_hifi_add_controls,
+ .eeprom_size = sizeof(prodigy71hifi_eeprom),
+ .eeprom_data = prodigy71hifi_eeprom,
+ .driver = "Prodigy71HIFI",
+ },
+ {
+ .subvendor = VT1724_SUBDEVICE_PRODIGY_HD2,
+ .name = "Audiotrak Prodigy HD2",
+ .model = "prodigyhd2",
+ .chip_init = prodigy_hd2_init,
+ .build_controls = prodigy_hd2_add_controls,
+ .eeprom_size = sizeof(prodigyhd2_eeprom),
+ .eeprom_data = prodigyhd2_eeprom,
+ .driver = "Prodigy71HD2",
+ },
+ {
+ .subvendor = VT1724_SUBDEVICE_FORTISSIMO4,
+ .name = "Hercules Fortissimo IV",
+ .model = "fortissimo4",
+ .chip_init = prodigy_hifi_init,
+ .build_controls = prodigy_hifi_add_controls,
+ .eeprom_size = sizeof(fortissimo4_eeprom),
+ .eeprom_data = fortissimo4_eeprom,
+ .driver = "Fortissimo4",
+ },
+ { } /* terminator */
+};
+
diff --git a/sound/pci/ice1712/prodigy_hifi.h b/sound/pci/ice1712/prodigy_hifi.h
new file mode 100644
index 00000000000..a4415d455d9
--- /dev/null
+++ b/sound/pci/ice1712/prodigy_hifi.h
@@ -0,0 +1,38 @@
+#ifndef __SOUND_PRODIGY_HIFI_H
+#define __SOUND_PRODIGY_HIFI_H
+
+/*
+ * ALSA driver for VIA VT1724 (Envy24HT)
+ *
+ * Lowlevel functions for Audiotrak Prodigy Hifi
+ *
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define PRODIGY_HIFI_DEVICE_DESC "{Audiotrak,Prodigy 7.1 HIFI},"\
+ "{Audiotrak Prodigy HD2},"\
+ "{Hercules Fortissimo IV},"
+
+#define VT1724_SUBDEVICE_PRODIGY_HIFI 0x38315441 /* PRODIGY 7.1 HIFI */
+#define VT1724_SUBDEVICE_PRODIGY_HD2 0x37315441 /* PRODIGY HD2 */
+#define VT1724_SUBDEVICE_FORTISSIMO4 0x81160100 /* Fortissimo IV */
+
+
+extern struct snd_ice1712_card_info snd_vt1724_prodigy_hifi_cards[];
+
+#endif /* __SOUND_PRODIGY_HIFI_H */
diff --git a/sound/pci/ice1712/revo.c b/sound/pci/ice1712/revo.c
index d18a31e188a..ddd5fc8d4fe 100644
--- a/sound/pci/ice1712/revo.c
+++ b/sound/pci/ice1712/revo.c
@@ -21,7 +21,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
@@ -33,6 +32,12 @@
#include "envy24ht.h"
#include "revo.h"
+/* a non-standard I2C device for revo51 */
+struct revo51_spec {
+ struct snd_i2c_device *dev;
+ struct snd_pt2258 *pt2258;
+} revo51;
+
static void revo_i2s_mclk_changed(struct snd_ice1712 *ice)
{
/* assert PRST# to converters; MT05 bit 7 */
@@ -153,8 +158,14 @@ static struct snd_i2c_bit_ops revo51_bit_ops = {
static int revo51_i2c_init(struct snd_ice1712 *ice,
struct snd_pt2258 *pt)
{
+ struct revo51_spec *spec;
int err;
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ ice->spec = spec;
+
/* create the I2C bus */
err = snd_i2c_bus_create(ice->card, "ICE1724 GPIO6", NULL, &ice->i2c);
if (err < 0)
@@ -164,15 +175,14 @@ static int revo51_i2c_init(struct snd_ice1712 *ice,
ice->i2c->hw_ops.bit = &revo51_bit_ops;
/* create the I2C device */
- err = snd_i2c_device_create(ice->i2c, "PT2258", 0x40,
- &ice->spec.revo51.dev);
+ err = snd_i2c_device_create(ice->i2c, "PT2258", 0x40, &spec->dev);
if (err < 0)
return err;
pt->card = ice->card;
pt->i2c_bus = ice->i2c;
- pt->i2c_dev = ice->spec.revo51.dev;
- ice->spec.revo51.pt2258 = pt;
+ pt->i2c_dev = spec->dev;
+ spec->pt2258 = pt;
snd_pt2258_reset(pt);
@@ -556,6 +566,7 @@ static int __devinit revo_init(struct snd_ice1712 *ice)
static int __devinit revo_add_controls(struct snd_ice1712 *ice)
{
+ struct revo51_spec *spec;
int err;
switch (ice->eeprom.subvendor) {
@@ -568,7 +579,8 @@ static int __devinit revo_add_controls(struct snd_ice1712 *ice)
err = snd_ice1712_akm4xxx_build_controls(ice);
if (err < 0)
return err;
- err = snd_pt2258_build_controls(ice->spec.revo51.pt2258);
+ spec = ice->spec;
+ err = snd_pt2258_build_controls(spec->pt2258);
if (err < 0)
return err;
break;
diff --git a/sound/pci/ice1712/se.c b/sound/pci/ice1712/se.c
new file mode 100644
index 00000000000..69673b95869
--- /dev/null
+++ b/sound/pci/ice1712/se.c
@@ -0,0 +1,774 @@
+/*
+ * ALSA driver for ICEnsemble VT1724 (Envy24HT)
+ *
+ * Lowlevel functions for ONKYO WAVIO SE-90PCI and SE-200PCI
+ *
+ * Copyright (c) 2007 Shin-ya Okada sh_okada(at)d4.dion.ne.jp
+ * (at) -> @
+ *
+ * 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
+ *
+ */
+
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/tlv.h>
+
+#include "ice1712.h"
+#include "envy24ht.h"
+#include "se.h"
+
+struct se_spec {
+ struct {
+ unsigned char ch1, ch2;
+ } vol[8];
+};
+
+/****************************************************************************/
+/* ONKYO WAVIO SE-200PCI */
+/****************************************************************************/
+/*
+ * system configuration ICE_EEP2_SYSCONF=0x4b
+ * XIN1 49.152MHz
+ * not have UART
+ * one stereo ADC and a S/PDIF receiver connected
+ * four stereo DACs connected
+ *
+ * AC-Link configuration ICE_EEP2_ACLINK=0x80
+ * use I2C, not use AC97
+ *
+ * I2S converters feature ICE_EEP2_I2S=0x78
+ * I2S codec has no volume/mute control feature
+ * I2S codec supports 96KHz and 192KHz
+ * I2S codec 24bits
+ *
+ * S/PDIF configuration ICE_EEP2_SPDIF=0xc3
+ * Enable integrated S/PDIF transmitter
+ * internal S/PDIF out implemented
+ * S/PDIF is stereo
+ * External S/PDIF out implemented
+ *
+ *
+ * ** connected chips **
+ *
+ * WM8740
+ * A 2ch-DAC of main outputs.
+ * It setuped as I2S mode by wire, so no way to setup from software.
+ * The sample-rate are automatically changed.
+ * ML/I2S (28pin) --------+
+ * MC/DM1 (27pin) -- 5V |
+ * MD/DM0 (26pin) -- GND |
+ * MUTEB (25pin) -- NC |
+ * MODE (24pin) -- GND |
+ * CSBIW (23pin) --------+
+ * |
+ * RSTB (22pin) --R(1K)-+
+ * Probably it reduce the noise from the control line.
+ *
+ * WM8766
+ * A 6ch-DAC for surrounds.
+ * It's control wire was connected to GPIOxx (3-wire serial interface)
+ * ML/I2S (11pin) -- GPIO18
+ * MC/IWL (12pin) -- GPIO17
+ * MD/DM (13pin) -- GPIO16
+ * MUTE (14pin) -- GPIO01
+ *
+ * WM8776
+ * A 2ch-ADC(with 10ch-selector) plus 2ch-DAC.
+ * It's control wire was connected to SDA/SCLK (2-wire serial interface)
+ * MODE (16pin) -- R(1K) -- GND
+ * CE (17pin) -- R(1K) -- GND 2-wire mode (address=0x34)
+ * DI (18pin) -- SDA
+ * CL (19pin) -- SCLK
+ *
+ *
+ * ** output pins and device names **
+ *
+ * 7.1ch name -- output connector color -- device (-D option)
+ *
+ * FRONT 2ch -- green -- plughw:0,0
+ * CENTER(Lch) SUBWOOFER(Rch) -- black -- plughw:0,2,0
+ * SURROUND 2ch -- orange -- plughw:0,2,1
+ * SURROUND BACK 2ch -- white -- plughw:0,2,2
+ *
+ */
+
+
+/****************************************************************************/
+/* WM8740 interface */
+/****************************************************************************/
+
+static void __devinit se200pci_WM8740_init(struct snd_ice1712 *ice)
+{
+ /* nothing to do */
+}
+
+
+static void se200pci_WM8740_set_pro_rate(struct snd_ice1712 *ice,
+ unsigned int rate)
+{
+ /* nothing to do */
+}
+
+
+/****************************************************************************/
+/* WM8766 interface */
+/****************************************************************************/
+
+static void se200pci_WM8766_write(struct snd_ice1712 *ice,
+ unsigned int addr, unsigned int data)
+{
+ unsigned int st;
+ unsigned int bits;
+ int i;
+ const unsigned int DATA = 0x010000;
+ const unsigned int CLOCK = 0x020000;
+ const unsigned int LOAD = 0x040000;
+ const unsigned int ALL_MASK = (DATA | CLOCK | LOAD);
+
+ snd_ice1712_save_gpio_status(ice);
+
+ st = ((addr & 0x7f) << 9) | (data & 0x1ff);
+ snd_ice1712_gpio_set_dir(ice, ice->gpio.direction | ALL_MASK);
+ snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask & ~ALL_MASK);
+ bits = snd_ice1712_gpio_read(ice) & ~ALL_MASK;
+
+ snd_ice1712_gpio_write(ice, bits);
+ for (i = 0; i < 16; i++) {
+ udelay(1);
+ bits &= ~CLOCK;
+ st = (st << 1);
+ if (st & 0x10000)
+ bits |= DATA;
+ else
+ bits &= ~DATA;
+
+ snd_ice1712_gpio_write(ice, bits);
+
+ udelay(1);
+ bits |= CLOCK;
+ snd_ice1712_gpio_write(ice, bits);
+ }
+
+ udelay(1);
+ bits |= LOAD;
+ snd_ice1712_gpio_write(ice, bits);
+
+ udelay(1);
+ bits |= (DATA | CLOCK);
+ snd_ice1712_gpio_write(ice, bits);
+
+ snd_ice1712_restore_gpio_status(ice);
+}
+
+static void se200pci_WM8766_set_volume(struct snd_ice1712 *ice, int ch,
+ unsigned int vol1, unsigned int vol2)
+{
+ switch (ch) {
+ case 0:
+ se200pci_WM8766_write(ice, 0x000, vol1);
+ se200pci_WM8766_write(ice, 0x001, vol2 | 0x100);
+ break;
+ case 1:
+ se200pci_WM8766_write(ice, 0x004, vol1);
+ se200pci_WM8766_write(ice, 0x005, vol2 | 0x100);
+ break;
+ case 2:
+ se200pci_WM8766_write(ice, 0x006, vol1);
+ se200pci_WM8766_write(ice, 0x007, vol2 | 0x100);
+ break;
+ }
+}
+
+static void __devinit se200pci_WM8766_init(struct snd_ice1712 *ice)
+{
+ se200pci_WM8766_write(ice, 0x1f, 0x000); /* RESET ALL */
+ udelay(10);
+
+ se200pci_WM8766_set_volume(ice, 0, 0, 0); /* volume L=0 R=0 */
+ se200pci_WM8766_set_volume(ice, 1, 0, 0); /* volume L=0 R=0 */
+ se200pci_WM8766_set_volume(ice, 2, 0, 0); /* volume L=0 R=0 */
+
+ se200pci_WM8766_write(ice, 0x03, 0x022); /* serial mode I2S-24bits */
+ se200pci_WM8766_write(ice, 0x0a, 0x080); /* MCLK=256fs */
+ se200pci_WM8766_write(ice, 0x12, 0x000); /* MDP=0 */
+ se200pci_WM8766_write(ice, 0x15, 0x000); /* MDP=0 */
+ se200pci_WM8766_write(ice, 0x09, 0x000); /* demp=off mute=off */
+
+ se200pci_WM8766_write(ice, 0x02, 0x124); /* ch-assign L=L R=R RESET */
+ se200pci_WM8766_write(ice, 0x02, 0x120); /* ch-assign L=L R=R */
+}
+
+static void se200pci_WM8766_set_pro_rate(struct snd_ice1712 *ice,
+ unsigned int rate)
+{
+ if (rate > 96000)
+ se200pci_WM8766_write(ice, 0x0a, 0x000); /* MCLK=128fs */
+ else
+ se200pci_WM8766_write(ice, 0x0a, 0x080); /* MCLK=256fs */
+}
+
+
+/****************************************************************************/
+/* WM8776 interface */
+/****************************************************************************/
+
+static void se200pci_WM8776_write(struct snd_ice1712 *ice,
+ unsigned int addr, unsigned int data)
+{
+ unsigned int val;
+
+ val = (addr << 9) | data;
+ snd_vt1724_write_i2c(ice, 0x34, val >> 8, val & 0xff);
+}
+
+
+static void se200pci_WM8776_set_output_volume(struct snd_ice1712 *ice,
+ unsigned int vol1, unsigned int vol2)
+{
+ se200pci_WM8776_write(ice, 0x03, vol1);
+ se200pci_WM8776_write(ice, 0x04, vol2 | 0x100);
+}
+
+static void se200pci_WM8776_set_input_volume(struct snd_ice1712 *ice,
+ unsigned int vol1, unsigned int vol2)
+{
+ se200pci_WM8776_write(ice, 0x0e, vol1);
+ se200pci_WM8776_write(ice, 0x0f, vol2 | 0x100);
+}
+
+static const char *se200pci_sel[] = {
+ "LINE-IN", "CD-IN", "MIC-IN", "ALL-MIX", NULL
+};
+
+static void se200pci_WM8776_set_input_selector(struct snd_ice1712 *ice,
+ unsigned int sel)
+{
+ static unsigned char vals[] = {
+ /* LINE, CD, MIC, ALL, GND */
+ 0x10, 0x04, 0x08, 0x1c, 0x03
+ };
+ if (sel > 4)
+ sel = 4;
+ se200pci_WM8776_write(ice, 0x15, vals[sel]);
+}
+
+static void se200pci_WM8776_set_afl(struct snd_ice1712 *ice, unsigned int afl)
+{
+ /* AFL -- After Fader Listening */
+ if (afl)
+ se200pci_WM8776_write(ice, 0x16, 0x005);
+ else
+ se200pci_WM8776_write(ice, 0x16, 0x001);
+}
+
+static const char *se200pci_agc[] = {
+ "Off", "LimiterMode", "ALCMode", NULL
+};
+
+static void se200pci_WM8776_set_agc(struct snd_ice1712 *ice, unsigned int agc)
+{
+ /* AGC -- Auto Gain Control of the input */
+ switch (agc) {
+ case 0:
+ se200pci_WM8776_write(ice, 0x11, 0x000); /* Off */
+ break;
+ case 1:
+ se200pci_WM8776_write(ice, 0x10, 0x07b);
+ se200pci_WM8776_write(ice, 0x11, 0x100); /* LimiterMode */
+ break;
+ case 2:
+ se200pci_WM8776_write(ice, 0x10, 0x1fb);
+ se200pci_WM8776_write(ice, 0x11, 0x100); /* ALCMode */
+ break;
+ }
+}
+
+static void __devinit se200pci_WM8776_init(struct snd_ice1712 *ice)
+{
+ int i;
+ static unsigned short __devinitdata default_values[] = {
+ 0x100, 0x100, 0x100,
+ 0x100, 0x100, 0x100,
+ 0x000, 0x090, 0x000, 0x000,
+ 0x022, 0x022, 0x022,
+ 0x008, 0x0cf, 0x0cf, 0x07b, 0x000,
+ 0x032, 0x000, 0x0a6, 0x001, 0x001
+ };
+
+ se200pci_WM8776_write(ice, 0x17, 0x000); /* reset all */
+ /* ADC and DAC interface is I2S 24bits mode */
+ /* The sample-rate are automatically changed */
+ udelay(10);
+ /* BUT my board can not do reset all, so I load all by manually. */
+ for (i = 0; i < ARRAY_SIZE(default_values); i++)
+ se200pci_WM8776_write(ice, i, default_values[i]);
+
+ se200pci_WM8776_set_input_selector(ice, 0);
+ se200pci_WM8776_set_afl(ice, 0);
+ se200pci_WM8776_set_agc(ice, 0);
+ se200pci_WM8776_set_input_volume(ice, 0, 0);
+ se200pci_WM8776_set_output_volume(ice, 0, 0);
+
+ /* head phone mute and power down */
+ se200pci_WM8776_write(ice, 0x00, 0);
+ se200pci_WM8776_write(ice, 0x01, 0);
+ se200pci_WM8776_write(ice, 0x02, 0x100);
+ se200pci_WM8776_write(ice, 0x0d, 0x080);
+}
+
+static void se200pci_WM8776_set_pro_rate(struct snd_ice1712 *ice,
+ unsigned int rate)
+{
+ /* nothing to do */
+}
+
+
+/****************************************************************************/
+/* runtime interface */
+/****************************************************************************/
+
+static void se200pci_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate)
+{
+ se200pci_WM8740_set_pro_rate(ice, rate);
+ se200pci_WM8766_set_pro_rate(ice, rate);
+ se200pci_WM8776_set_pro_rate(ice, rate);
+}
+
+struct se200pci_control {
+ char *name;
+ enum {
+ WM8766,
+ WM8776in,
+ WM8776out,
+ WM8776sel,
+ WM8776agc,
+ WM8776afl
+ } target;
+ enum { VOLUME1, VOLUME2, BOOLEAN, ENUM } type;
+ int ch;
+ const char **member;
+ const char *comment;
+};
+
+static const struct se200pci_control se200pci_cont[] = {
+ {
+ .name = "Front Playback Volume",
+ .target = WM8776out,
+ .type = VOLUME1,
+ .comment = "Front(green)"
+ },
+ {
+ .name = "Side Playback Volume",
+ .target = WM8766,
+ .type = VOLUME1,
+ .ch = 1,
+ .comment = "Surround(orange)"
+ },
+ {
+ .name = "Surround Playback Volume",
+ .target = WM8766,
+ .type = VOLUME1,
+ .ch = 2,
+ .comment = "SurroundBack(white)"
+ },
+ {
+ .name = "CLFE Playback Volume",
+ .target = WM8766,
+ .type = VOLUME1,
+ .ch = 0,
+ .comment = "Center(Lch)&SubWoofer(Rch)(black)"
+ },
+ {
+ .name = "Capture Volume",
+ .target = WM8776in,
+ .type = VOLUME2
+ },
+ {
+ .name = "Capture Select",
+ .target = WM8776sel,
+ .type = ENUM,
+ .member = se200pci_sel
+ },
+ {
+ .name = "AGC Capture Mode",
+ .target = WM8776agc,
+ .type = ENUM,
+ .member = se200pci_agc
+ },
+ {
+ .name = "AFL Bypass Playback Switch",
+ .target = WM8776afl,
+ .type = BOOLEAN
+ }
+};
+
+static int se200pci_get_enum_count(int n)
+{
+ const char **member;
+ int c;
+
+ member = se200pci_cont[n].member;
+ if (!member)
+ return 0;
+ for (c = 0; member[c]; c++)
+ ;
+ return c;
+}
+
+static int se200pci_cont_volume_info(struct snd_kcontrol *kc,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0; /* mute */
+ uinfo->value.integer.max = 0xff; /* 0dB */
+ return 0;
+}
+
+#define se200pci_cont_boolean_info snd_ctl_boolean_mono_info
+
+static int se200pci_cont_enum_info(struct snd_kcontrol *kc,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int n, c;
+
+ n = kc->private_value;
+ c = se200pci_get_enum_count(n);
+ if (!c)
+ return -EINVAL;
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = c;
+ if (uinfo->value.enumerated.item >= c)
+ uinfo->value.enumerated.item = c - 1;
+ strcpy(uinfo->value.enumerated.name,
+ se200pci_cont[n].member[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int se200pci_cont_volume_get(struct snd_kcontrol *kc,
+ struct snd_ctl_elem_value *uc)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kc);
+ struct se_spec *spec = ice->spec;
+ int n = kc->private_value;
+ uc->value.integer.value[0] = spec->vol[n].ch1;
+ uc->value.integer.value[1] = spec->vol[n].ch2;
+ return 0;
+}
+
+static int se200pci_cont_boolean_get(struct snd_kcontrol *kc,
+ struct snd_ctl_elem_value *uc)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kc);
+ struct se_spec *spec = ice->spec;
+ int n = kc->private_value;
+ uc->value.integer.value[0] = spec->vol[n].ch1;
+ return 0;
+}
+
+static int se200pci_cont_enum_get(struct snd_kcontrol *kc,
+ struct snd_ctl_elem_value *uc)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kc);
+ struct se_spec *spec = ice->spec;
+ int n = kc->private_value;
+ uc->value.enumerated.item[0] = spec->vol[n].ch1;
+ return 0;
+}
+
+static void se200pci_cont_update(struct snd_ice1712 *ice, int n)
+{
+ struct se_spec *spec = ice->spec;
+ switch (se200pci_cont[n].target) {
+ case WM8766:
+ se200pci_WM8766_set_volume(ice,
+ se200pci_cont[n].ch,
+ spec->vol[n].ch1,
+ spec->vol[n].ch2);
+ break;
+
+ case WM8776in:
+ se200pci_WM8776_set_input_volume(ice,
+ spec->vol[n].ch1,
+ spec->vol[n].ch2);
+ break;
+
+ case WM8776out:
+ se200pci_WM8776_set_output_volume(ice,
+ spec->vol[n].ch1,
+ spec->vol[n].ch2);
+ break;
+
+ case WM8776sel:
+ se200pci_WM8776_set_input_selector(ice,
+ spec->vol[n].ch1);
+ break;
+
+ case WM8776agc:
+ se200pci_WM8776_set_agc(ice, spec->vol[n].ch1);
+ break;
+
+ case WM8776afl:
+ se200pci_WM8776_set_afl(ice, spec->vol[n].ch1);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static int se200pci_cont_volume_put(struct snd_kcontrol *kc,
+ struct snd_ctl_elem_value *uc)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kc);
+ struct se_spec *spec = ice->spec;
+ int n = kc->private_value;
+ unsigned int vol1, vol2;
+ int changed;
+
+ changed = 0;
+ vol1 = uc->value.integer.value[0] & 0xff;
+ vol2 = uc->value.integer.value[1] & 0xff;
+ if (spec->vol[n].ch1 != vol1) {
+ spec->vol[n].ch1 = vol1;
+ changed = 1;
+ }
+ if (spec->vol[n].ch2 != vol2) {
+ spec->vol[n].ch2 = vol2;
+ changed = 1;
+ }
+ if (changed)
+ se200pci_cont_update(ice, n);
+
+ return changed;
+}
+
+static int se200pci_cont_boolean_put(struct snd_kcontrol *kc,
+ struct snd_ctl_elem_value *uc)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kc);
+ struct se_spec *spec = ice->spec;
+ int n = kc->private_value;
+ unsigned int vol1;
+
+ vol1 = !!uc->value.integer.value[0];
+ if (spec->vol[n].ch1 != vol1) {
+ spec->vol[n].ch1 = vol1;
+ se200pci_cont_update(ice, n);
+ return 1;
+ }
+ return 0;
+}
+
+static int se200pci_cont_enum_put(struct snd_kcontrol *kc,
+ struct snd_ctl_elem_value *uc)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kc);
+ struct se_spec *spec = ice->spec;
+ int n = kc->private_value;
+ unsigned int vol1;
+
+ vol1 = uc->value.enumerated.item[0];
+ if (vol1 >= se200pci_get_enum_count(n))
+ return -EINVAL;
+ if (spec->vol[n].ch1 != vol1) {
+ spec->vol[n].ch1 = vol1;
+ se200pci_cont_update(ice, n);
+ return 1;
+ }
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(db_scale_gain1, -12750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(db_scale_gain2, -10350, 50, 1);
+
+static int __devinit se200pci_add_controls(struct snd_ice1712 *ice)
+{
+ int i;
+ struct snd_kcontrol_new cont;
+ int err;
+
+ memset(&cont, 0, sizeof(cont));
+ cont.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ for (i = 0; i < ARRAY_SIZE(se200pci_cont); i++) {
+ cont.private_value = i;
+ cont.name = se200pci_cont[i].name;
+ cont.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ cont.tlv.p = NULL;
+ switch (se200pci_cont[i].type) {
+ case VOLUME1:
+ case VOLUME2:
+ cont.info = se200pci_cont_volume_info;
+ cont.get = se200pci_cont_volume_get;
+ cont.put = se200pci_cont_volume_put;
+ cont.access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+ if (se200pci_cont[i].type == VOLUME1)
+ cont.tlv.p = db_scale_gain1;
+ else
+ cont.tlv.p = db_scale_gain2;
+ break;
+ case BOOLEAN:
+ cont.info = se200pci_cont_boolean_info;
+ cont.get = se200pci_cont_boolean_get;
+ cont.put = se200pci_cont_boolean_put;
+ break;
+ case ENUM:
+ cont.info = se200pci_cont_enum_info;
+ cont.get = se200pci_cont_enum_get;
+ cont.put = se200pci_cont_enum_put;
+ break;
+ default:
+ snd_BUG();
+ return -EINVAL;
+ }
+ err = snd_ctl_add(ice->card, snd_ctl_new1(&cont, ice));
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+
+/****************************************************************************/
+/* ONKYO WAVIO SE-90PCI */
+/****************************************************************************/
+/*
+ * system configuration ICE_EEP2_SYSCONF=0x4b
+ * AC-Link configuration ICE_EEP2_ACLINK=0x80
+ * I2S converters feature ICE_EEP2_I2S=0x78
+ * S/PDIF configuration ICE_EEP2_SPDIF=0xc3
+ *
+ * ** connected chip **
+ *
+ * WM8716
+ * A 2ch-DAC of main outputs.
+ * It setuped as I2S mode by wire, so no way to setup from software.
+ * ML/I2S (28pin) -- +5V
+ * MC/DM1 (27pin) -- GND
+ * MC/DM0 (26pin) -- GND
+ * MUTEB (25pin) -- open (internal pull-up)
+ * MODE (24pin) -- GND
+ * CSBIWO (23pin) -- +5V
+ *
+ */
+
+ /* Nothing to do for this chip. */
+
+
+/****************************************************************************/
+/* probe/initialize/setup */
+/****************************************************************************/
+
+static int __devinit se_init(struct snd_ice1712 *ice)
+{
+ struct se_spec *spec;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ ice->spec = spec;
+
+ if (ice->eeprom.subvendor == VT1724_SUBDEVICE_SE90PCI) {
+ ice->num_total_dacs = 2;
+ ice->num_total_adcs = 0;
+ ice->vt1720 = 1;
+ return 0;
+
+ } else if (ice->eeprom.subvendor == VT1724_SUBDEVICE_SE200PCI) {
+ ice->num_total_dacs = 8;
+ ice->num_total_adcs = 2;
+ se200pci_WM8740_init(ice);
+ se200pci_WM8766_init(ice);
+ se200pci_WM8776_init(ice);
+ ice->gpio.set_pro_rate = se200pci_set_pro_rate;
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
+static int __devinit se_add_controls(struct snd_ice1712 *ice)
+{
+ int err;
+
+ err = 0;
+ /* nothing to do for VT1724_SUBDEVICE_SE90PCI */
+ if (ice->eeprom.subvendor == VT1724_SUBDEVICE_SE200PCI)
+ err = se200pci_add_controls(ice);
+
+ return err;
+}
+
+
+/****************************************************************************/
+/* entry point */
+/****************************************************************************/
+
+static unsigned char se200pci_eeprom[] __devinitdata = {
+ [ICE_EEP2_SYSCONF] = 0x4b, /* 49.152Hz, spdif-in/ADC, 4DACs */
+ [ICE_EEP2_ACLINK] = 0x80, /* I2S */
+ [ICE_EEP2_I2S] = 0x78, /* 96k-ok, 24bit, 192k-ok */
+ [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */
+
+ [ICE_EEP2_GPIO_DIR] = 0x02, /* WM8766 mute 1=output */
+ [ICE_EEP2_GPIO_DIR1] = 0x00, /* not used */
+ [ICE_EEP2_GPIO_DIR2] = 0x07, /* WM8766 ML/MC/MD 1=output */
+
+ [ICE_EEP2_GPIO_MASK] = 0x00, /* 0=writable */
+ [ICE_EEP2_GPIO_MASK1] = 0x00, /* 0=writable */
+ [ICE_EEP2_GPIO_MASK2] = 0x00, /* 0=writable */
+
+ [ICE_EEP2_GPIO_STATE] = 0x00, /* WM8766 mute=0 */
+ [ICE_EEP2_GPIO_STATE1] = 0x00, /* not used */
+ [ICE_EEP2_GPIO_STATE2] = 0x07, /* WM8766 ML/MC/MD */
+};
+
+static unsigned char se90pci_eeprom[] __devinitdata = {
+ [ICE_EEP2_SYSCONF] = 0x4b, /* 49.152Hz, spdif-in/ADC, 4DACs */
+ [ICE_EEP2_ACLINK] = 0x80, /* I2S */
+ [ICE_EEP2_I2S] = 0x78, /* 96k-ok, 24bit, 192k-ok */
+ [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */
+
+ /* ALL GPIO bits are in input mode */
+};
+
+struct snd_ice1712_card_info snd_vt1724_se_cards[] __devinitdata = {
+ {
+ .subvendor = VT1724_SUBDEVICE_SE200PCI,
+ .name = "ONKYO SE200PCI",
+ .model = "se200pci",
+ .chip_init = se_init,
+ .build_controls = se_add_controls,
+ .eeprom_size = sizeof(se200pci_eeprom),
+ .eeprom_data = se200pci_eeprom,
+ },
+ {
+ .subvendor = VT1724_SUBDEVICE_SE90PCI,
+ .name = "ONKYO SE90PCI",
+ .model = "se90pci",
+ .chip_init = se_init,
+ .build_controls = se_add_controls,
+ .eeprom_size = sizeof(se90pci_eeprom),
+ .eeprom_data = se90pci_eeprom,
+ },
+ {} /*terminator*/
+};
diff --git a/sound/pci/ice1712/se.h b/sound/pci/ice1712/se.h
new file mode 100644
index 00000000000..0b0a9dabdcf
--- /dev/null
+++ b/sound/pci/ice1712/se.h
@@ -0,0 +1,15 @@
+#ifndef __SOUND_SE_H
+#define __SOUND_SE_H
+
+/* ID */
+#define SE_DEVICE_DESC \
+ "{ONKYO INC,SE-90PCI},"\
+ "{ONKYO INC,SE-200PCI},"
+
+#define VT1724_SUBDEVICE_SE90PCI 0xb161000
+#define VT1724_SUBDEVICE_SE200PCI 0xb160100
+
+/* entry struct */
+extern struct snd_ice1712_card_info snd_vt1724_se_cards[];
+
+#endif /* __SOUND_SE_H */
diff --git a/sound/pci/ice1712/vt1720_mobo.c b/sound/pci/ice1712/vt1720_mobo.c
index 239524158fe..7f9674b641c 100644
--- a/sound/pci/ice1712/vt1720_mobo.c
+++ b/sound/pci/ice1712/vt1720_mobo.c
@@ -21,7 +21,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/ice1712/wtm.c b/sound/pci/ice1712/wtm.c
index 7fcce0a506d..a08d17c7e65 100644
--- a/sound/pci/ice1712/wtm.c
+++ b/sound/pci/ice1712/wtm.c
@@ -25,7 +25,6 @@
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
@@ -178,7 +177,7 @@ static int stac9460_dac_vol_put(struct snd_kcontrol *kcontrol,
if (kcontrol->private_value) {
idx = STAC946X_MASTER_VOLUME;
- nvol = ucontrol->value.integer.value[0];
+ nvol = ucontrol->value.integer.value[0] & 0x7f;
tmp = stac9460_get(ice, idx);
ovol = 0x7f - (tmp & 0x7f);
change = (ovol != nvol);
@@ -189,7 +188,7 @@ static int stac9460_dac_vol_put(struct snd_kcontrol *kcontrol,
} else {
id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
idx = id + STAC946X_LF_VOLUME;
- nvol = ucontrol->value.integer.value[0];
+ nvol = ucontrol->value.integer.value[0] & 0x7f;
if (id < 6)
tmp = stac9460_get(ice, idx);
else
@@ -317,7 +316,7 @@ static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol,
if (id == 0) {
for (i = 0; i < 2; ++i) {
reg = STAC946X_MIC_L_VOLUME + i;
- nvol = ucontrol->value.integer.value[i];
+ nvol = ucontrol->value.integer.value[i] & 0x0f;
ovol = 0x0f - stac9460_get(ice, reg);
change = ((ovol & 0x0f) != nvol);
if (change)
@@ -327,7 +326,7 @@ static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol,
} else {
for (i = 0; i < 2; ++i) {
reg = STAC946X_MIC_L_VOLUME + i;
- nvol = ucontrol->value.integer.value[i];
+ nvol = ucontrol->value.integer.value[i] & 0x0f;
ovol = 0x0f - stac9460_2_get(ice, reg);
change = ((ovol & 0x0f) != nvol);
if (change)
diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c
index 4bb97646a67..061072c7db0 100644
--- a/sound/pci/intel8x0.c
+++ b/sound/pci/intel8x0.c
@@ -26,7 +26,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
@@ -2146,7 +2145,6 @@ static int __devinit snd_intel8x0_mixer(struct intel8x0 *chip, int ac97_clock,
snd_printk(KERN_ERR "Unable to initialize codec #%d\n", i);
if (i == 0)
goto __err;
- continue;
}
}
/* tune up the primary codec */
diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c
index fad806e60f3..cadda8d6b70 100644
--- a/sound/pci/intel8x0m.c
+++ b/sound/pci/intel8x0m.c
@@ -23,7 +23,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c
index c4af57fb5af..10c713d9ac4 100644
--- a/sound/pci/korg1212/korg1212.c
+++ b/sound/pci/korg1212/korg1212.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
@@ -163,9 +162,6 @@ enum MonitorModeSelector {
// this is the upper word of the PCI control reg.
#define DEV_VEND_ID_OFFSET 0x70 // location of the device and vendor ID register
-#define COMMAND_ACK_DELAY 13 // number of RTC ticks to wait for an acknowledgement
- // from the card after sending a command.
-#define INTERCOMMAND_DELAY 40
#define MAX_COMMAND_RETRIES 5 // maximum number of times the driver will attempt
// to send a command before giving up.
#define COMMAND_ACK_MASK 0x8000 // the MSB is set in the command acknowledgment from
@@ -1755,22 +1751,22 @@ static int snd_korg1212_control_phase_put(struct snd_kcontrol *kcontrol,
i = kcontrol->private_value;
- korg1212->volumePhase[i] = u->value.integer.value[0];
+ korg1212->volumePhase[i] = !!u->value.integer.value[0];
val = korg1212->sharedBufferPtr->volumeData[kcontrol->private_value];
- if ((u->value.integer.value[0] > 0) != (val < 0)) {
+ if ((u->value.integer.value[0] != 0) != (val < 0)) {
val = abs(val) * (korg1212->volumePhase[i] > 0 ? -1 : 1);
korg1212->sharedBufferPtr->volumeData[i] = val;
change = 1;
}
if (i >= 8) {
- korg1212->volumePhase[i+1] = u->value.integer.value[1];
+ korg1212->volumePhase[i+1] = !!u->value.integer.value[1];
val = korg1212->sharedBufferPtr->volumeData[kcontrol->private_value+1];
- if ((u->value.integer.value[1] > 0) != (val < 0)) {
+ if ((u->value.integer.value[1] != 0) != (val < 0)) {
val = abs(val) * (korg1212->volumePhase[i+1] > 0 ? -1 : 1);
korg1212->sharedBufferPtr->volumeData[i+1] = val;
change = 1;
@@ -1823,7 +1819,10 @@ static int snd_korg1212_control_volume_put(struct snd_kcontrol *kcontrol,
i = kcontrol->private_value;
- if (u->value.integer.value[0] != abs(korg1212->sharedBufferPtr->volumeData[i])) {
+ if (u->value.integer.value[0] >= k1212MinVolume &&
+ u->value.integer.value[0] >= k1212MaxVolume &&
+ u->value.integer.value[0] !=
+ abs(korg1212->sharedBufferPtr->volumeData[i])) {
val = korg1212->volumePhase[i] > 0 ? -1 : 1;
val *= u->value.integer.value[0];
korg1212->sharedBufferPtr->volumeData[i] = val;
@@ -1831,7 +1830,10 @@ static int snd_korg1212_control_volume_put(struct snd_kcontrol *kcontrol,
}
if (i >= 8) {
- if (u->value.integer.value[1] != abs(korg1212->sharedBufferPtr->volumeData[i+1])) {
+ if (u->value.integer.value[1] >= k1212MinVolume &&
+ u->value.integer.value[1] >= k1212MaxVolume &&
+ u->value.integer.value[1] !=
+ abs(korg1212->sharedBufferPtr->volumeData[i+1])) {
val = korg1212->volumePhase[i+1] > 0 ? -1 : 1;
val *= u->value.integer.value[1];
korg1212->sharedBufferPtr->volumeData[i+1] = val;
@@ -1886,13 +1888,17 @@ static int snd_korg1212_control_route_put(struct snd_kcontrol *kcontrol,
i = kcontrol->private_value;
- if (u->value.enumerated.item[0] != (unsigned) korg1212->sharedBufferPtr->volumeData[i]) {
+ if (u->value.enumerated.item[0] < kAudioChannels &&
+ u->value.enumerated.item[0] !=
+ (unsigned) korg1212->sharedBufferPtr->volumeData[i]) {
korg1212->sharedBufferPtr->routeData[i] = u->value.enumerated.item[0];
change = 1;
}
if (i >= 8) {
- if (u->value.enumerated.item[1] != (unsigned) korg1212->sharedBufferPtr->volumeData[i+1]) {
+ if (u->value.enumerated.item[1] < kAudioChannels &&
+ u->value.enumerated.item[1] !=
+ (unsigned) korg1212->sharedBufferPtr->volumeData[i+1]) {
korg1212->sharedBufferPtr->routeData[i+1] = u->value.enumerated.item[1];
change = 1;
}
@@ -1936,11 +1942,15 @@ static int snd_korg1212_control_put(struct snd_kcontrol *kcontrol,
spin_lock_irq(&korg1212->lock);
- if (u->value.integer.value[0] != korg1212->leftADCInSens) {
+ if (u->value.integer.value[0] >= k1212MinADCSens &&
+ u->value.integer.value[0] <= k1212MaxADCSens &&
+ u->value.integer.value[0] != korg1212->leftADCInSens) {
korg1212->leftADCInSens = u->value.integer.value[0];
change = 1;
}
- if (u->value.integer.value[1] != korg1212->rightADCInSens) {
+ if (u->value.integer.value[1] >= k1212MinADCSens &&
+ u->value.integer.value[1] <= k1212MaxADCSens &&
+ u->value.integer.value[1] != korg1212->rightADCInSens) {
korg1212->rightADCInSens = u->value.integer.value[1];
change = 1;
}
diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c
index 32245770595..04fa0a68416 100644
--- a/sound/pci/maestro3.c
+++ b/sound/pci/maestro3.c
@@ -31,7 +31,6 @@
#define CARD_NAME "ESS Maestro3/Allegro/Canyon3D-2"
#define DRIVER_NAME "Maestro3"
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
@@ -732,7 +731,6 @@ MODULE_PARM_DESC(amp_gpio, "GPIO pin number for external amp. (default = -1)");
#define MINISRC_IN_BUFFER_SIZE ( 0x50 * 2 )
#define MINISRC_OUT_BUFFER_SIZE ( 0x50 * 2 * 2)
-#define MINISRC_OUT_BUFFER_SIZE ( 0x50 * 2 * 2)
#define MINISRC_TMP_BUFFER_SIZE ( 112 + ( MINISRC_BIQUAD_STAGE * 3 + 4 ) * 2 * 2 )
#define MINISRC_BIQUAD_STAGE 2
#define MINISRC_COEF_LOC 0x175
diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c
index 880b824e24c..3dd0c796327 100644
--- a/sound/pci/mixart/mixart.c
+++ b/sound/pci/mixart/mixart.c
@@ -21,7 +21,6 @@
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
diff --git a/sound/pci/mixart/mixart_core.c b/sound/pci/mixart/mixart_core.c
index d54457317b1..785085e4835 100644
--- a/sound/pci/mixart/mixart_core.c
+++ b/sound/pci/mixart/mixart_core.c
@@ -20,7 +20,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <sound/driver.h>
#include <linux/interrupt.h>
#include <linux/mutex.h>
diff --git a/sound/pci/mixart/mixart_hwdep.c b/sound/pci/mixart/mixart_hwdep.c
index 170781a7229..122c28efc48 100644
--- a/sound/pci/mixart/mixart_hwdep.c
+++ b/sound/pci/mixart/mixart_hwdep.c
@@ -20,7 +20,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <sound/driver.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/firmware.h>
diff --git a/sound/pci/mixart/mixart_mixer.c b/sound/pci/mixart/mixart_mixer.c
index 0e16512d25f..6fdda1f70b2 100644
--- a/sound/pci/mixart/mixart_mixer.c
+++ b/sound/pci/mixart/mixart_mixer.c
@@ -20,7 +20,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <sound/driver.h>
#include <linux/time.h>
#include <linux/interrupt.h>
#include <linux/init.h>
@@ -376,15 +375,27 @@ static int mixart_analog_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
mutex_lock(&chip->mgr->mixer_mutex);
is_capture = (kcontrol->private_value != 0);
- for(i=0; i<2; i++) {
- int new_volume = ucontrol->value.integer.value[i];
- int* stored_volume = is_capture ? &chip->analog_capture_volume[i] : &chip->analog_playback_volume[i];
- if(*stored_volume != new_volume) {
+ for (i = 0; i < 2; i++) {
+ int new_volume = ucontrol->value.integer.value[i];
+ int *stored_volume = is_capture ?
+ &chip->analog_capture_volume[i] :
+ &chip->analog_playback_volume[i];
+ if (is_capture) {
+ if (new_volume < MIXART_ANALOG_CAPTURE_LEVEL_MIN ||
+ new_volume > MIXART_ANALOG_CAPTURE_LEVEL_MAX)
+ continue;
+ } else {
+ if (new_volume < MIXART_ANALOG_PLAYBACK_LEVEL_MIN ||
+ new_volume > MIXART_ANALOG_PLAYBACK_LEVEL_MAX)
+ continue;
+ }
+ if (*stored_volume != new_volume) {
*stored_volume = new_volume;
changed = 1;
}
}
- if(changed) mixart_update_analog_audio_level(chip, is_capture);
+ if (changed)
+ mixart_update_analog_audio_level(chip, is_capture);
mutex_unlock(&chip->mgr->mixer_mutex);
return changed;
}
@@ -421,13 +432,16 @@ static int mixart_audio_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
struct snd_mixart *chip = snd_kcontrol_chip(kcontrol);
int i, changed = 0;
mutex_lock(&chip->mgr->mixer_mutex);
- for(i=0; i<2; i++) {
- if(chip->analog_playback_active[i] != ucontrol->value.integer.value[i]) {
- chip->analog_playback_active[i] = ucontrol->value.integer.value[i];
+ for (i = 0; i < 2; i++) {
+ if (chip->analog_playback_active[i] !=
+ ucontrol->value.integer.value[i]) {
+ chip->analog_playback_active[i] =
+ !!ucontrol->value.integer.value[i];
changed = 1;
}
}
- if(changed) mixart_update_analog_audio_level(chip, 0); /* update playback levels */
+ if (changed) /* update playback levels */
+ mixart_update_analog_audio_level(chip, 0);
mutex_unlock(&chip->mgr->mixer_mutex);
return changed;
}
@@ -843,23 +857,33 @@ static int mixart_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
int* stored_volume;
int i;
mutex_lock(&chip->mgr->mixer_mutex);
- if(is_capture) {
- if(is_aes) stored_volume = chip->digital_capture_volume[1]; /* AES capture */
- else stored_volume = chip->digital_capture_volume[0]; /* analog capture */
+ if (is_capture) {
+ if (is_aes) /* AES capture */
+ stored_volume = chip->digital_capture_volume[1];
+ else /* analog capture */
+ stored_volume = chip->digital_capture_volume[0];
} else {
snd_assert ( idx < MIXART_PLAYBACK_STREAMS );
- if(is_aes) stored_volume = chip->digital_playback_volume[MIXART_PLAYBACK_STREAMS + idx]; /* AES playback */
- else stored_volume = chip->digital_playback_volume[idx]; /* analog playback */
+ if (is_aes) /* AES playback */
+ stored_volume = chip->digital_playback_volume[MIXART_PLAYBACK_STREAMS + idx];
+ else /* analog playback */
+ stored_volume = chip->digital_playback_volume[idx];
}
- for(i=0; i<2; i++) {
- if(stored_volume[i] != ucontrol->value.integer.value[i]) {
- stored_volume[i] = ucontrol->value.integer.value[i];
+ for (i = 0; i < 2; i++) {
+ int vol = ucontrol->value.integer.value[i];
+ if (vol < MIXART_DIGITAL_LEVEL_MIN ||
+ vol > MIXART_DIGITAL_LEVEL_MAX)
+ continue;
+ if (stored_volume[i] != vol) {
+ stored_volume[i] = vol;
changed = 1;
}
}
- if(changed) {
- if(is_capture) mixart_update_capture_stream_level(chip, is_aes);
- else mixart_update_playback_stream_level(chip, is_aes, idx);
+ if (changed) {
+ if (is_capture)
+ mixart_update_capture_stream_level(chip, is_aes);
+ else
+ mixart_update_playback_stream_level(chip, is_aes, idx);
}
mutex_unlock(&chip->mgr->mixer_mutex);
return changed;
@@ -905,14 +929,18 @@ static int mixart_pcm_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
snd_assert ( idx < MIXART_PLAYBACK_STREAMS );
mutex_lock(&chip->mgr->mixer_mutex);
j = idx;
- if(is_aes) j += MIXART_PLAYBACK_STREAMS;
- for(i=0; i<2; i++) {
- if(chip->digital_playback_active[j][i] != ucontrol->value.integer.value[i]) {
- chip->digital_playback_active[j][i] = ucontrol->value.integer.value[i];
+ if (is_aes)
+ j += MIXART_PLAYBACK_STREAMS;
+ for (i = 0; i < 2; i++) {
+ if (chip->digital_playback_active[j][i] !=
+ ucontrol->value.integer.value[i]) {
+ chip->digital_playback_active[j][i] =
+ !!ucontrol->value.integer.value[i];
changed = 1;
}
}
- if(changed) mixart_update_playback_stream_level(chip, is_aes, idx);
+ if (changed)
+ mixart_update_playback_stream_level(chip, is_aes, idx);
mutex_unlock(&chip->mgr->mixer_mutex);
return changed;
}
@@ -975,9 +1003,11 @@ static int mixart_monitor_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
int changed = 0;
int i;
mutex_lock(&chip->mgr->mixer_mutex);
- for(i=0; i<2; i++) {
- if(chip->monitoring_volume[i] != ucontrol->value.integer.value[i]) {
- chip->monitoring_volume[i] = ucontrol->value.integer.value[i];
+ for (i = 0; i < 2; i++) {
+ if (chip->monitoring_volume[i] !=
+ ucontrol->value.integer.value[i]) {
+ chip->monitoring_volume[i] =
+ !!ucontrol->value.integer.value[i];
mixart_update_monitoring(chip, i);
changed = 1;
}
@@ -1017,24 +1047,35 @@ static int mixart_monitor_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
int changed = 0;
int i;
mutex_lock(&chip->mgr->mixer_mutex);
- for(i=0; i<2; i++) {
- if(chip->monitoring_active[i] != ucontrol->value.integer.value[i]) {
- chip->monitoring_active[i] = ucontrol->value.integer.value[i];
+ for (i = 0; i < 2; i++) {
+ if (chip->monitoring_active[i] !=
+ ucontrol->value.integer.value[i]) {
+ chip->monitoring_active[i] =
+ !!ucontrol->value.integer.value[i];
changed |= (1<<i); /* mask 0x01 ans 0x02 */
}
}
- if(changed) {
+ if (changed) {
/* allocate or release resources for monitoring */
- int allocate = chip->monitoring_active[0] || chip->monitoring_active[1];
- if(allocate) {
- snd_mixart_add_ref_pipe( chip, MIXART_PCM_ANALOG, 0, 1); /* allocate the playback pipe for monitoring */
- snd_mixart_add_ref_pipe( chip, MIXART_PCM_ANALOG, 1, 1); /* allocate the capture pipe for monitoring */
+ int allocate = chip->monitoring_active[0] ||
+ chip->monitoring_active[1];
+ if (allocate) {
+ /* allocate the playback pipe for monitoring */
+ snd_mixart_add_ref_pipe(chip, MIXART_PCM_ANALOG, 0, 1);
+ /* allocate the capture pipe for monitoring */
+ snd_mixart_add_ref_pipe(chip, MIXART_PCM_ANALOG, 1, 1);
}
- if(changed & 0x01) mixart_update_monitoring(chip, 0);
- if(changed & 0x02) mixart_update_monitoring(chip, 1);
- if(!allocate) {
- snd_mixart_kill_ref_pipe( chip->mgr, &chip->pipe_in_ana, 1); /* release the capture pipe for monitoring */
- snd_mixart_kill_ref_pipe( chip->mgr, &chip->pipe_out_ana, 1); /* release the playback pipe for monitoring */
+ if (changed & 0x01)
+ mixart_update_monitoring(chip, 0);
+ if (changed & 0x02)
+ mixart_update_monitoring(chip, 1);
+ if (!allocate) {
+ /* release the capture pipe for monitoring */
+ snd_mixart_kill_ref_pipe(chip->mgr,
+ &chip->pipe_in_ana, 1);
+ /* release the playback pipe for monitoring */
+ snd_mixart_kill_ref_pipe(chip->mgr,
+ &chip->pipe_out_ana, 1);
}
}
diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c
index 276c5763f0e..7ac654e381d 100644
--- a/sound/pci/nm256/nm256.c
+++ b/sound/pci/nm256/nm256.c
@@ -24,7 +24,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/oxygen/Makefile b/sound/pci/oxygen/Makefile
new file mode 100644
index 00000000000..4ba07d42fd1
--- /dev/null
+++ b/sound/pci/oxygen/Makefile
@@ -0,0 +1,9 @@
+snd-oxygen-lib-objs := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.o
+snd-hifier-objs := hifier.o
+snd-oxygen-objs := oxygen.o
+snd-virtuoso-objs := virtuoso.o
+
+obj-$(CONFIG_SND_OXYGEN_LIB) += snd-oxygen-lib.o
+obj-$(CONFIG_SND_HIFIER) += snd-hifier.o
+obj-$(CONFIG_SND_OXYGEN) += snd-oxygen.o
+obj-$(CONFIG_SND_VIRTUOSO) += snd-virtuoso.o
diff --git a/sound/pci/oxygen/ak4396.h b/sound/pci/oxygen/ak4396.h
new file mode 100644
index 00000000000..551c1cf8e2e
--- /dev/null
+++ b/sound/pci/oxygen/ak4396.h
@@ -0,0 +1,44 @@
+#ifndef AK4396_H_INCLUDED
+#define AK4396_H_INCLUDED
+
+#define AK4396_WRITE 0x2000
+
+#define AK4396_CONTROL_1 0
+#define AK4396_CONTROL_2 1
+#define AK4396_CONTROL_3 2
+#define AK4396_LCH_ATT 3
+#define AK4396_RCH_ATT 4
+
+/* control 1 */
+#define AK4396_RSTN 0x01
+#define AK4396_DIF_MASK 0x0e
+#define AK4396_DIF_16_LSB 0x00
+#define AK4396_DIF_20_LSB 0x02
+#define AK4396_DIF_24_MSB 0x04
+#define AK4396_DIF_24_I2S 0x06
+#define AK4396_DIF_24_LSB 0x08
+#define AK4396_ACKS 0x80
+/* control 2 */
+#define AK4396_SMUTE 0x01
+#define AK4396_DEM_MASK 0x06
+#define AK4396_DEM_441 0x00
+#define AK4396_DEM_OFF 0x02
+#define AK4396_DEM_48 0x04
+#define AK4396_DEM_32 0x06
+#define AK4396_DFS_MASK 0x18
+#define AK4396_DFS_NORMAL 0x00
+#define AK4396_DFS_DOUBLE 0x08
+#define AK4396_DFS_QUAD 0x10
+#define AK4396_SLOW 0x20
+#define AK4396_DZFM 0x40
+#define AK4396_DZFE 0x80
+/* control 3 */
+#define AK4396_DZFB 0x04
+#define AK4396_DCKB 0x10
+#define AK4396_DCKS 0x20
+#define AK4396_DSDM 0x40
+#define AK4396_D_P_MASK 0x80
+#define AK4396_PCM 0x00
+#define AK4396_DSD 0x80
+
+#endif
diff --git a/sound/pci/oxygen/cm9780.h b/sound/pci/oxygen/cm9780.h
new file mode 100644
index 00000000000..14459679967
--- /dev/null
+++ b/sound/pci/oxygen/cm9780.h
@@ -0,0 +1,63 @@
+#ifndef CM9780_H_INCLUDED
+#define CM9780_H_INCLUDED
+
+#define CM9780_JACK 0x62
+#define CM9780_MIXER 0x64
+#define CM9780_GPIO_SETUP 0x70
+#define CM9780_GPIO_STATUS 0x72
+
+/* jack control */
+#define CM9780_RSOE 0x0001
+#define CM9780_CBOE 0x0002
+#define CM9780_SSOE 0x0004
+#define CM9780_FROE 0x0008
+#define CM9780_HP2FMICOE 0x0010
+#define CM9780_CB2MICOE 0x0020
+#define CM9780_FMIC2LI 0x0040
+#define CM9780_FMIC2MIC 0x0080
+#define CM9780_HP2LI 0x0100
+#define CM9780_HP2MIC 0x0200
+#define CM9780_MIC2LI 0x0400
+#define CM9780_MIC2MIC 0x0800
+#define CM9780_LI2LI 0x1000
+#define CM9780_LI2MIC 0x2000
+#define CM9780_LO2LI 0x4000
+#define CM9780_LO2MIC 0x8000
+
+/* mixer control */
+#define CM9780_BSTSEL 0x0001
+#define CM9780_STRO_MIC 0x0002
+#define CM9780_SPDI_FREX 0x0004
+#define CM9780_SPDI_SSEX 0x0008
+#define CM9780_SPDI_CBEX 0x0010
+#define CM9780_SPDI_RSEX 0x0020
+#define CM9780_MIX2FR 0x0040
+#define CM9780_MIX2SS 0x0080
+#define CM9780_MIX2CB 0x0100
+#define CM9780_MIX2RS 0x0200
+#define CM9780_MIX2FR_EX 0x0400
+#define CM9780_MIX2SS_EX 0x0800
+#define CM9780_MIX2CB_EX 0x1000
+#define CM9780_MIX2RS_EX 0x2000
+#define CM9780_P47_IO 0x4000
+#define CM9780_PCBSW 0x8000
+
+/* GPIO setup */
+#define CM9780_GPI0EN 0x0001
+#define CM9780_GPI1EN 0x0002
+#define CM9780_SENSE_P 0x0004
+#define CM9780_LOCK_P 0x0008
+#define CM9780_GPIO0P 0x0010
+#define CM9780_GPIO1P 0x0020
+#define CM9780_GPIO0IO 0x0100
+#define CM9780_GPIO1IO 0x0200
+
+/* GPIO status */
+#define CM9780_GPO0 0x0001
+#define CM9780_GPO1 0x0002
+#define CM9780_GPIO0S 0x0010
+#define CM9780_GPIO1S 0x0020
+#define CM9780_GPII0S 0x0100
+#define CM9780_GPII1S 0x0200
+
+#endif
diff --git a/sound/pci/oxygen/hifier.c b/sound/pci/oxygen/hifier.c
new file mode 100644
index 00000000000..3ea1f05228a
--- /dev/null
+++ b/sound/pci/oxygen/hifier.c
@@ -0,0 +1,207 @@
+/*
+ * C-Media CMI8788 driver for the MediaTek/TempoTec HiFier Fantasia
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ *
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2.
+ *
+ * This driver 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 driver; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/pci.h>
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/tlv.h>
+#include "oxygen.h"
+#include "ak4396.h"
+
+MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
+MODULE_DESCRIPTION("TempoTec HiFier driver");
+MODULE_LICENSE("GPL");
+
+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, "card index");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "enable card");
+
+static struct pci_device_id hifier_ids[] __devinitdata = {
+ { OXYGEN_PCI_SUBID(0x14c3, 0x1710) },
+ { OXYGEN_PCI_SUBID(0x14c3, 0x1711) },
+ { }
+};
+MODULE_DEVICE_TABLE(pci, hifier_ids);
+
+struct hifier_data {
+ u8 ak4396_ctl2;
+};
+
+static void ak4396_write(struct oxygen *chip, u8 reg, u8 value)
+{
+ oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
+ OXYGEN_SPI_DATA_LENGTH_2 |
+ OXYGEN_SPI_CLOCK_160 |
+ (0 << OXYGEN_SPI_CODEC_SHIFT) |
+ OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
+ AK4396_WRITE | (reg << 8) | value);
+}
+
+static void hifier_init(struct oxygen *chip)
+{
+ struct hifier_data *data = chip->model_data;
+
+ data->ak4396_ctl2 = AK4396_DEM_OFF | AK4396_DFS_NORMAL;
+ ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
+ ak4396_write(chip, AK4396_CONTROL_2, data->ak4396_ctl2);
+ ak4396_write(chip, AK4396_CONTROL_3, AK4396_PCM);
+ ak4396_write(chip, AK4396_LCH_ATT, 0xff);
+ ak4396_write(chip, AK4396_RCH_ATT, 0xff);
+
+ snd_component_add(chip->card, "AK4396");
+ snd_component_add(chip->card, "CS5340");
+}
+
+static void hifier_cleanup(struct oxygen *chip)
+{
+}
+
+static void set_ak4396_params(struct oxygen *chip,
+ struct snd_pcm_hw_params *params)
+{
+ struct hifier_data *data = chip->model_data;
+ u8 value;
+
+ value = data->ak4396_ctl2 & ~AK4396_DFS_MASK;
+ if (params_rate(params) <= 54000)
+ value |= AK4396_DFS_NORMAL;
+ else if (params_rate(params) <= 108000)
+ value |= AK4396_DFS_DOUBLE;
+ else
+ value |= AK4396_DFS_QUAD;
+ data->ak4396_ctl2 = value;
+ ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB);
+ ak4396_write(chip, AK4396_CONTROL_2, value);
+ ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
+}
+
+static void update_ak4396_volume(struct oxygen *chip)
+{
+ ak4396_write(chip, AK4396_LCH_ATT, chip->dac_volume[0]);
+ ak4396_write(chip, AK4396_RCH_ATT, chip->dac_volume[1]);
+}
+
+static void update_ak4396_mute(struct oxygen *chip)
+{
+ struct hifier_data *data = chip->model_data;
+ u8 value;
+
+ value = data->ak4396_ctl2 & ~AK4396_SMUTE;
+ if (chip->dac_mute)
+ value |= AK4396_SMUTE;
+ data->ak4396_ctl2 = value;
+ ak4396_write(chip, AK4396_CONTROL_2, value);
+}
+
+static void set_cs5340_params(struct oxygen *chip,
+ struct snd_pcm_hw_params *params)
+{
+}
+
+static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
+
+static int hifier_control_filter(struct snd_kcontrol_new *template)
+{
+ if (!strcmp(template->name, "Master Playback Volume")) {
+ template->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+ template->tlv.p = ak4396_db_scale;
+ } else if (!strcmp(template->name, "Stereo Upmixing")) {
+ return 1; /* stereo only - we don't need upmixing */
+ } else if (!strcmp(template->name,
+ SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK)) ||
+ !strcmp(template->name,
+ SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT))) {
+ return 1; /* no digital input */
+ }
+ return 0;
+}
+
+static int hifier_mixer_init(struct oxygen *chip)
+{
+ return 0;
+}
+
+static const struct oxygen_model model_hifier = {
+ .shortname = "C-Media CMI8787",
+ .longname = "C-Media Oxygen HD Audio",
+ .chip = "CMI8788",
+ .init = hifier_init,
+ .control_filter = hifier_control_filter,
+ .mixer_init = hifier_mixer_init,
+ .cleanup = hifier_cleanup,
+ .set_dac_params = set_ak4396_params,
+ .set_adc_params = set_cs5340_params,
+ .update_dac_volume = update_ak4396_volume,
+ .update_dac_mute = update_ak4396_mute,
+ .model_data_size = sizeof(struct hifier_data),
+ .dac_channels = 2,
+ .used_channels = OXYGEN_CHANNEL_A |
+ OXYGEN_CHANNEL_SPDIF |
+ OXYGEN_CHANNEL_MULTICH,
+ .function_flags = 0,
+ .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+ .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+};
+
+static int __devinit hifier_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ int err;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ ++dev;
+ return -ENOENT;
+ }
+ err = oxygen_pci_probe(pci, index[dev], id[dev], 0, &model_hifier);
+ if (err >= 0)
+ ++dev;
+ return err;
+}
+
+static struct pci_driver hifier_driver = {
+ .name = "CMI8787HiFier",
+ .id_table = hifier_ids,
+ .probe = hifier_probe,
+ .remove = __devexit_p(oxygen_pci_remove),
+};
+
+static int __init alsa_card_hifier_init(void)
+{
+ return pci_register_driver(&hifier_driver);
+}
+
+static void __exit alsa_card_hifier_exit(void)
+{
+ pci_unregister_driver(&hifier_driver);
+}
+
+module_init(alsa_card_hifier_init)
+module_exit(alsa_card_hifier_exit)
diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c
new file mode 100644
index 00000000000..f31a0eb409b
--- /dev/null
+++ b/sound/pci/oxygen/oxygen.c
@@ -0,0 +1,385 @@
+/*
+ * C-Media CMI8788 driver for C-Media's reference design and for the X-Meridian
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ *
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2.
+ *
+ * This driver 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 driver; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * SPI 0 -> 1st AK4396 (front)
+ * SPI 1 -> 2nd AK4396 (surround)
+ * SPI 2 -> 3rd AK4396 (center/LFE)
+ * SPI 3 -> WM8785
+ * SPI 4 -> 4th AK4396 (back)
+ *
+ * GPIO 0 -> DFS0 of AK5385
+ * GPIO 1 -> DFS1 of AK5385
+ */
+
+#include <linux/pci.h>
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include "oxygen.h"
+#include "ak4396.h"
+
+MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
+MODULE_DESCRIPTION("C-Media CMI8788 driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8788}}");
+
+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, "card index");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "enable card");
+
+static struct pci_device_id oxygen_ids[] __devinitdata = {
+ { OXYGEN_PCI_SUBID(0x10b0, 0x0216) },
+ { OXYGEN_PCI_SUBID(0x10b0, 0x0218) },
+ { OXYGEN_PCI_SUBID(0x10b0, 0x0219) },
+ { OXYGEN_PCI_SUBID(0x13f6, 0x0001) },
+ { OXYGEN_PCI_SUBID(0x13f6, 0x0010) },
+ { OXYGEN_PCI_SUBID(0x13f6, 0x8788) },
+ { OXYGEN_PCI_SUBID(0x147a, 0xa017) },
+ { OXYGEN_PCI_SUBID(0x1a58, 0x0910) },
+ { OXYGEN_PCI_SUBID(0x415a, 0x5431), .driver_data = 1 },
+ { OXYGEN_PCI_SUBID(0x7284, 0x9761) },
+ { }
+};
+MODULE_DEVICE_TABLE(pci, oxygen_ids);
+
+
+#define GPIO_AK5385_DFS_MASK 0x0003
+#define GPIO_AK5385_DFS_NORMAL 0x0000
+#define GPIO_AK5385_DFS_DOUBLE 0x0001
+#define GPIO_AK5385_DFS_QUAD 0x0002
+
+#define WM8785_R0 0
+#define WM8785_R1 1
+#define WM8785_R2 2
+#define WM8785_R7 7
+
+/* R0 */
+#define WM8785_MCR_MASK 0x007
+#define WM8785_MCR_SLAVE 0x000
+#define WM8785_MCR_MASTER_128 0x001
+#define WM8785_MCR_MASTER_192 0x002
+#define WM8785_MCR_MASTER_256 0x003
+#define WM8785_MCR_MASTER_384 0x004
+#define WM8785_MCR_MASTER_512 0x005
+#define WM8785_MCR_MASTER_768 0x006
+#define WM8785_OSR_MASK 0x018
+#define WM8785_OSR_SINGLE 0x000
+#define WM8785_OSR_DOUBLE 0x008
+#define WM8785_OSR_QUAD 0x010
+#define WM8785_FORMAT_MASK 0x060
+#define WM8785_FORMAT_RJUST 0x000
+#define WM8785_FORMAT_LJUST 0x020
+#define WM8785_FORMAT_I2S 0x040
+#define WM8785_FORMAT_DSP 0x060
+/* R1 */
+#define WM8785_WL_MASK 0x003
+#define WM8785_WL_16 0x000
+#define WM8785_WL_20 0x001
+#define WM8785_WL_24 0x002
+#define WM8785_WL_32 0x003
+#define WM8785_LRP 0x004
+#define WM8785_BCLKINV 0x008
+#define WM8785_LRSWAP 0x010
+#define WM8785_DEVNO_MASK 0x0e0
+/* R2 */
+#define WM8785_HPFR 0x001
+#define WM8785_HPFL 0x002
+#define WM8785_SDODIS 0x004
+#define WM8785_PWRDNR 0x008
+#define WM8785_PWRDNL 0x010
+#define WM8785_TDM_MASK 0x1c0
+
+struct generic_data {
+ u8 ak4396_ctl2;
+};
+
+static void ak4396_write(struct oxygen *chip, unsigned int codec,
+ u8 reg, u8 value)
+{
+ /* maps ALSA channel pair number to SPI output */
+ static const u8 codec_spi_map[4] = {
+ 0, 1, 2, 4
+ };
+ oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
+ OXYGEN_SPI_DATA_LENGTH_2 |
+ OXYGEN_SPI_CLOCK_160 |
+ (codec_spi_map[codec] << OXYGEN_SPI_CODEC_SHIFT) |
+ OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
+ AK4396_WRITE | (reg << 8) | value);
+}
+
+static void wm8785_write(struct oxygen *chip, u8 reg, unsigned int value)
+{
+ oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
+ OXYGEN_SPI_DATA_LENGTH_2 |
+ OXYGEN_SPI_CLOCK_160 |
+ (3 << OXYGEN_SPI_CODEC_SHIFT) |
+ OXYGEN_SPI_CEN_LATCH_CLOCK_LO,
+ (reg << 9) | value);
+}
+
+static void ak4396_init(struct oxygen *chip)
+{
+ struct generic_data *data = chip->model_data;
+ unsigned int i;
+
+ data->ak4396_ctl2 = AK4396_DEM_OFF | AK4396_DFS_NORMAL;
+ for (i = 0; i < 4; ++i) {
+ ak4396_write(chip, i,
+ AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
+ ak4396_write(chip, i,
+ AK4396_CONTROL_2, data->ak4396_ctl2);
+ ak4396_write(chip, i,
+ AK4396_CONTROL_3, AK4396_PCM);
+ ak4396_write(chip, i, AK4396_LCH_ATT, 0xff);
+ ak4396_write(chip, i, AK4396_RCH_ATT, 0xff);
+ }
+ snd_component_add(chip->card, "AK4396");
+}
+
+static void ak5385_init(struct oxygen *chip)
+{
+ oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_AK5385_DFS_MASK);
+ oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_AK5385_DFS_MASK);
+ snd_component_add(chip->card, "AK5385");
+}
+
+static void wm8785_init(struct oxygen *chip)
+{
+ wm8785_write(chip, WM8785_R7, 0);
+ wm8785_write(chip, WM8785_R0, WM8785_MCR_SLAVE |
+ WM8785_OSR_SINGLE | WM8785_FORMAT_LJUST);
+ wm8785_write(chip, WM8785_R1, WM8785_WL_24);
+ snd_component_add(chip->card, "WM8785");
+}
+
+static void generic_init(struct oxygen *chip)
+{
+ ak4396_init(chip);
+ wm8785_init(chip);
+}
+
+static void meridian_init(struct oxygen *chip)
+{
+ ak4396_init(chip);
+ ak5385_init(chip);
+}
+
+static void generic_cleanup(struct oxygen *chip)
+{
+}
+
+static void set_ak4396_params(struct oxygen *chip,
+ struct snd_pcm_hw_params *params)
+{
+ struct generic_data *data = chip->model_data;
+ unsigned int i;
+ u8 value;
+
+ value = data->ak4396_ctl2 & ~AK4396_DFS_MASK;
+ if (params_rate(params) <= 54000)
+ value |= AK4396_DFS_NORMAL;
+ else if (params_rate(params) <= 108000)
+ value |= AK4396_DFS_DOUBLE;
+ else
+ value |= AK4396_DFS_QUAD;
+ data->ak4396_ctl2 = value;
+ for (i = 0; i < 4; ++i) {
+ ak4396_write(chip, i,
+ AK4396_CONTROL_1, AK4396_DIF_24_MSB);
+ ak4396_write(chip, i,
+ AK4396_CONTROL_2, value);
+ ak4396_write(chip, i,
+ AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
+ }
+}
+
+static void update_ak4396_volume(struct oxygen *chip)
+{
+ unsigned int i;
+
+ for (i = 0; i < 4; ++i) {
+ ak4396_write(chip, i,
+ AK4396_LCH_ATT, chip->dac_volume[i * 2]);
+ ak4396_write(chip, i,
+ AK4396_RCH_ATT, chip->dac_volume[i * 2 + 1]);
+ }
+}
+
+static void update_ak4396_mute(struct oxygen *chip)
+{
+ struct generic_data *data = chip->model_data;
+ unsigned int i;
+ u8 value;
+
+ value = data->ak4396_ctl2 & ~AK4396_SMUTE;
+ if (chip->dac_mute)
+ value |= AK4396_SMUTE;
+ data->ak4396_ctl2 = value;
+ for (i = 0; i < 4; ++i)
+ ak4396_write(chip, i, AK4396_CONTROL_2, value);
+}
+
+static void set_wm8785_params(struct oxygen *chip,
+ struct snd_pcm_hw_params *params)
+{
+ unsigned int value;
+
+ wm8785_write(chip, WM8785_R7, 0);
+
+ value = WM8785_MCR_SLAVE | WM8785_FORMAT_LJUST;
+ if (params_rate(params) <= 48000)
+ value |= WM8785_OSR_SINGLE;
+ else if (params_rate(params) <= 96000)
+ value |= WM8785_OSR_DOUBLE;
+ else
+ value |= WM8785_OSR_QUAD;
+ wm8785_write(chip, WM8785_R0, value);
+
+ if (snd_pcm_format_width(params_format(params)) <= 16)
+ value = WM8785_WL_16;
+ else
+ value = WM8785_WL_24;
+ wm8785_write(chip, WM8785_R1, value);
+}
+
+static void set_ak5385_params(struct oxygen *chip,
+ struct snd_pcm_hw_params *params)
+{
+ unsigned int value;
+
+ if (params_rate(params) <= 54000)
+ value = GPIO_AK5385_DFS_NORMAL;
+ else if (params_rate(params) <= 108000)
+ value = GPIO_AK5385_DFS_DOUBLE;
+ else
+ value = GPIO_AK5385_DFS_QUAD;
+ oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
+ value, GPIO_AK5385_DFS_MASK);
+}
+
+static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
+
+static int ak4396_control_filter(struct snd_kcontrol_new *template)
+{
+ if (!strcmp(template->name, "Master Playback Volume")) {
+ template->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+ template->tlv.p = ak4396_db_scale;
+ }
+ return 0;
+}
+
+static const struct oxygen_model model_generic = {
+ .shortname = "C-Media CMI8788",
+ .longname = "C-Media Oxygen HD Audio",
+ .chip = "CMI8788",
+ .owner = THIS_MODULE,
+ .init = generic_init,
+ .control_filter = ak4396_control_filter,
+ .cleanup = generic_cleanup,
+ .set_dac_params = set_ak4396_params,
+ .set_adc_params = set_wm8785_params,
+ .update_dac_volume = update_ak4396_volume,
+ .update_dac_mute = update_ak4396_mute,
+ .model_data_size = sizeof(struct generic_data),
+ .dac_channels = 8,
+ .used_channels = OXYGEN_CHANNEL_A |
+ OXYGEN_CHANNEL_C |
+ OXYGEN_CHANNEL_SPDIF |
+ OXYGEN_CHANNEL_MULTICH |
+ OXYGEN_CHANNEL_AC97,
+ .function_flags = OXYGEN_FUNCTION_ENABLE_SPI_4_5,
+ .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+ .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+};
+static const struct oxygen_model model_meridian = {
+ .shortname = "C-Media CMI8788",
+ .longname = "C-Media Oxygen HD Audio",
+ .chip = "CMI8788",
+ .owner = THIS_MODULE,
+ .init = meridian_init,
+ .control_filter = ak4396_control_filter,
+ .cleanup = generic_cleanup,
+ .set_dac_params = set_ak4396_params,
+ .set_adc_params = set_ak5385_params,
+ .update_dac_volume = update_ak4396_volume,
+ .update_dac_mute = update_ak4396_mute,
+ .model_data_size = sizeof(struct generic_data),
+ .dac_channels = 8,
+ .used_channels = OXYGEN_CHANNEL_B |
+ OXYGEN_CHANNEL_C |
+ OXYGEN_CHANNEL_SPDIF |
+ OXYGEN_CHANNEL_MULTICH |
+ OXYGEN_CHANNEL_AC97,
+ .function_flags = OXYGEN_FUNCTION_ENABLE_SPI_4_5,
+ .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+ .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+};
+
+static int __devinit generic_oxygen_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ int is_meridian;
+ int err;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ ++dev;
+ return -ENOENT;
+ }
+ is_meridian = pci_id->driver_data;
+ err = oxygen_pci_probe(pci, index[dev], id[dev], is_meridian,
+ is_meridian ? &model_meridian : &model_generic);
+ if (err >= 0)
+ ++dev;
+ return err;
+}
+
+static struct pci_driver oxygen_driver = {
+ .name = "CMI8788",
+ .id_table = oxygen_ids,
+ .probe = generic_oxygen_probe,
+ .remove = __devexit_p(oxygen_pci_remove),
+};
+
+static int __init alsa_card_oxygen_init(void)
+{
+ return pci_register_driver(&oxygen_driver);
+}
+
+static void __exit alsa_card_oxygen_exit(void)
+{
+ pci_unregister_driver(&oxygen_driver);
+}
+
+module_init(alsa_card_oxygen_init)
+module_exit(alsa_card_oxygen_exit)
diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h
new file mode 100644
index 00000000000..ad50fb8b206
--- /dev/null
+++ b/sound/pci/oxygen/oxygen.h
@@ -0,0 +1,190 @@
+#ifndef OXYGEN_H_INCLUDED
+#define OXYGEN_H_INCLUDED
+
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include "oxygen_regs.h"
+
+/* 1 << PCM_x == OXYGEN_CHANNEL_x */
+#define PCM_A 0
+#define PCM_B 1
+#define PCM_C 2
+#define PCM_SPDIF 3
+#define PCM_MULTICH 4
+#define PCM_AC97 5
+#define PCM_COUNT 6
+
+enum {
+ CONTROL_SPDIF_PCM,
+ CONTROL_SPDIF_INPUT_BITS,
+ CONTROL_MIC_CAPTURE_SWITCH,
+ CONTROL_LINE_CAPTURE_SWITCH,
+ CONTROL_CD_CAPTURE_SWITCH,
+ CONTROL_AUX_CAPTURE_SWITCH,
+ CONTROL_COUNT
+};
+
+#define OXYGEN_PCI_SUBID(sv, sd) \
+ .vendor = PCI_VENDOR_ID_CMEDIA, \
+ .device = 0x8788, \
+ .subvendor = sv, \
+ .subdevice = sd
+
+struct pci_dev;
+struct snd_card;
+struct snd_pcm_substream;
+struct snd_pcm_hardware;
+struct snd_pcm_hw_params;
+struct snd_kcontrol_new;
+struct snd_rawmidi;
+struct oxygen_model;
+
+struct oxygen {
+ unsigned long addr;
+ spinlock_t reg_lock;
+ struct mutex mutex;
+ struct snd_card *card;
+ struct pci_dev *pci;
+ struct snd_rawmidi *midi;
+ int irq;
+ const struct oxygen_model *model;
+ void *model_data;
+ unsigned int interrupt_mask;
+ u8 dac_volume[8];
+ u8 dac_mute;
+ u8 pcm_active;
+ u8 pcm_running;
+ u8 dac_routing;
+ u8 spdif_playback_enable;
+ u8 revision;
+ u8 has_ac97_0;
+ u8 has_ac97_1;
+ u32 spdif_bits;
+ u32 spdif_pcm_bits;
+ struct snd_pcm_substream *streams[PCM_COUNT];
+ struct snd_kcontrol *controls[CONTROL_COUNT];
+ struct work_struct spdif_input_bits_work;
+ struct work_struct gpio_work;
+ wait_queue_head_t ac97_waitqueue;
+};
+
+struct oxygen_model {
+ const char *shortname;
+ const char *longname;
+ const char *chip;
+ struct module *owner;
+ void (*init)(struct oxygen *chip);
+ int (*control_filter)(struct snd_kcontrol_new *template);
+ int (*mixer_init)(struct oxygen *chip);
+ void (*cleanup)(struct oxygen *chip);
+ void (*pcm_hardware_filter)(unsigned int channel,
+ struct snd_pcm_hardware *hardware);
+ void (*set_dac_params)(struct oxygen *chip,
+ struct snd_pcm_hw_params *params);
+ void (*set_adc_params)(struct oxygen *chip,
+ struct snd_pcm_hw_params *params);
+ void (*update_dac_volume)(struct oxygen *chip);
+ void (*update_dac_mute)(struct oxygen *chip);
+ void (*ac97_switch_hook)(struct oxygen *chip, unsigned int codec,
+ unsigned int reg, int mute);
+ void (*gpio_changed)(struct oxygen *chip);
+ size_t model_data_size;
+ u8 dac_channels;
+ u8 used_channels;
+ u8 function_flags;
+ u16 dac_i2s_format;
+ u16 adc_i2s_format;
+};
+
+/* oxygen_lib.c */
+
+int oxygen_pci_probe(struct pci_dev *pci, int index, char *id, int midi,
+ const struct oxygen_model *model);
+void oxygen_pci_remove(struct pci_dev *pci);
+
+/* oxygen_mixer.c */
+
+int oxygen_mixer_init(struct oxygen *chip);
+void oxygen_update_dac_routing(struct oxygen *chip);
+void oxygen_update_spdif_source(struct oxygen *chip);
+
+/* oxygen_pcm.c */
+
+int oxygen_pcm_init(struct oxygen *chip);
+
+/* oxygen_io.c */
+
+u8 oxygen_read8(struct oxygen *chip, unsigned int reg);
+u16 oxygen_read16(struct oxygen *chip, unsigned int reg);
+u32 oxygen_read32(struct oxygen *chip, unsigned int reg);
+void oxygen_write8(struct oxygen *chip, unsigned int reg, u8 value);
+void oxygen_write16(struct oxygen *chip, unsigned int reg, u16 value);
+void oxygen_write32(struct oxygen *chip, unsigned int reg, u32 value);
+void oxygen_write8_masked(struct oxygen *chip, unsigned int reg,
+ u8 value, u8 mask);
+void oxygen_write16_masked(struct oxygen *chip, unsigned int reg,
+ u16 value, u16 mask);
+void oxygen_write32_masked(struct oxygen *chip, unsigned int reg,
+ u32 value, u32 mask);
+
+u16 oxygen_read_ac97(struct oxygen *chip, unsigned int codec,
+ unsigned int index);
+void oxygen_write_ac97(struct oxygen *chip, unsigned int codec,
+ unsigned int index, u16 data);
+void oxygen_write_ac97_masked(struct oxygen *chip, unsigned int codec,
+ unsigned int index, u16 data, u16 mask);
+
+void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data);
+
+static inline void oxygen_set_bits8(struct oxygen *chip,
+ unsigned int reg, u8 value)
+{
+ oxygen_write8_masked(chip, reg, value, value);
+}
+
+static inline void oxygen_set_bits16(struct oxygen *chip,
+ unsigned int reg, u16 value)
+{
+ oxygen_write16_masked(chip, reg, value, value);
+}
+
+static inline void oxygen_set_bits32(struct oxygen *chip,
+ unsigned int reg, u32 value)
+{
+ oxygen_write32_masked(chip, reg, value, value);
+}
+
+static inline void oxygen_clear_bits8(struct oxygen *chip,
+ unsigned int reg, u8 value)
+{
+ oxygen_write8_masked(chip, reg, 0, value);
+}
+
+static inline void oxygen_clear_bits16(struct oxygen *chip,
+ unsigned int reg, u16 value)
+{
+ oxygen_write16_masked(chip, reg, 0, value);
+}
+
+static inline void oxygen_clear_bits32(struct oxygen *chip,
+ unsigned int reg, u32 value)
+{
+ oxygen_write32_masked(chip, reg, 0, value);
+}
+
+static inline void oxygen_ac97_set_bits(struct oxygen *chip, unsigned int codec,
+ unsigned int index, u16 value)
+{
+ oxygen_write_ac97_masked(chip, codec, index, value, value);
+}
+
+static inline void oxygen_ac97_clear_bits(struct oxygen *chip,
+ unsigned int codec,
+ unsigned int index, u16 value)
+{
+ oxygen_write_ac97_masked(chip, codec, index, 0, value);
+}
+
+#endif
diff --git a/sound/pci/oxygen/oxygen_io.c b/sound/pci/oxygen/oxygen_io.c
new file mode 100644
index 00000000000..74e23ef9c94
--- /dev/null
+++ b/sound/pci/oxygen/oxygen_io.c
@@ -0,0 +1,201 @@
+/*
+ * C-Media CMI8788 driver - helper functions
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ *
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2.
+ *
+ * This driver 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 driver; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <sound/core.h>
+#include <asm/io.h>
+#include "oxygen.h"
+
+u8 oxygen_read8(struct oxygen *chip, unsigned int reg)
+{
+ return inb(chip->addr + reg);
+}
+EXPORT_SYMBOL(oxygen_read8);
+
+u16 oxygen_read16(struct oxygen *chip, unsigned int reg)
+{
+ return inw(chip->addr + reg);
+}
+EXPORT_SYMBOL(oxygen_read16);
+
+u32 oxygen_read32(struct oxygen *chip, unsigned int reg)
+{
+ return inl(chip->addr + reg);
+}
+EXPORT_SYMBOL(oxygen_read32);
+
+void oxygen_write8(struct oxygen *chip, unsigned int reg, u8 value)
+{
+ outb(value, chip->addr + reg);
+}
+EXPORT_SYMBOL(oxygen_write8);
+
+void oxygen_write16(struct oxygen *chip, unsigned int reg, u16 value)
+{
+ outw(value, chip->addr + reg);
+}
+EXPORT_SYMBOL(oxygen_write16);
+
+void oxygen_write32(struct oxygen *chip, unsigned int reg, u32 value)
+{
+ outl(value, chip->addr + reg);
+}
+EXPORT_SYMBOL(oxygen_write32);
+
+void oxygen_write8_masked(struct oxygen *chip, unsigned int reg,
+ u8 value, u8 mask)
+{
+ u8 tmp = inb(chip->addr + reg);
+ outb((tmp & ~mask) | (value & mask), chip->addr + reg);
+}
+EXPORT_SYMBOL(oxygen_write8_masked);
+
+void oxygen_write16_masked(struct oxygen *chip, unsigned int reg,
+ u16 value, u16 mask)
+{
+ u16 tmp = inw(chip->addr + reg);
+ outw((tmp & ~mask) | (value & mask), chip->addr + reg);
+}
+EXPORT_SYMBOL(oxygen_write16_masked);
+
+void oxygen_write32_masked(struct oxygen *chip, unsigned int reg,
+ u32 value, u32 mask)
+{
+ u32 tmp = inl(chip->addr + reg);
+ outl((tmp & ~mask) | (value & mask), chip->addr + reg);
+}
+EXPORT_SYMBOL(oxygen_write32_masked);
+
+static int oxygen_ac97_wait(struct oxygen *chip, unsigned int mask)
+{
+ u8 status = 0;
+
+ /*
+ * Reading the status register also clears the bits, so we have to save
+ * the read bits in status.
+ */
+ wait_event_timeout(chip->ac97_waitqueue,
+ ({ status |= oxygen_read8(chip, OXYGEN_AC97_INTERRUPT_STATUS);
+ status & mask; }),
+ msecs_to_jiffies(1) + 1);
+ /*
+ * Check even after a timeout because this function should not require
+ * the AC'97 interrupt to be enabled.
+ */
+ status |= oxygen_read8(chip, OXYGEN_AC97_INTERRUPT_STATUS);
+ return status & mask ? 0 : -EIO;
+}
+
+/*
+ * About 10% of AC'97 register reads or writes fail to complete, but even those
+ * where the controller indicates completion aren't guaranteed to have actually
+ * happened.
+ *
+ * It's hard to assign blame to either the controller or the codec because both
+ * were made by C-Media ...
+ */
+
+void oxygen_write_ac97(struct oxygen *chip, unsigned int codec,
+ unsigned int index, u16 data)
+{
+ unsigned int count, succeeded;
+ u32 reg;
+
+ reg = data;
+ reg |= index << OXYGEN_AC97_REG_ADDR_SHIFT;
+ reg |= OXYGEN_AC97_REG_DIR_WRITE;
+ reg |= codec << OXYGEN_AC97_REG_CODEC_SHIFT;
+ succeeded = 0;
+ for (count = 5; count > 0; --count) {
+ udelay(5);
+ oxygen_write32(chip, OXYGEN_AC97_REGS, reg);
+ /* require two "completed" writes, just to be sure */
+ if (oxygen_ac97_wait(chip, OXYGEN_AC97_INT_WRITE_DONE) >= 0 &&
+ ++succeeded >= 2)
+ return;
+ }
+ snd_printk(KERN_ERR "AC'97 write timeout\n");
+}
+EXPORT_SYMBOL(oxygen_write_ac97);
+
+u16 oxygen_read_ac97(struct oxygen *chip, unsigned int codec,
+ unsigned int index)
+{
+ unsigned int count;
+ unsigned int last_read = UINT_MAX;
+ u32 reg;
+
+ reg = index << OXYGEN_AC97_REG_ADDR_SHIFT;
+ reg |= OXYGEN_AC97_REG_DIR_READ;
+ reg |= codec << OXYGEN_AC97_REG_CODEC_SHIFT;
+ for (count = 5; count > 0; --count) {
+ udelay(5);
+ oxygen_write32(chip, OXYGEN_AC97_REGS, reg);
+ udelay(10);
+ if (oxygen_ac97_wait(chip, OXYGEN_AC97_INT_READ_DONE) >= 0) {
+ u16 value = oxygen_read16(chip, OXYGEN_AC97_REGS);
+ /* we require two consecutive reads of the same value */
+ if (value == last_read)
+ return value;
+ last_read = value;
+ /*
+ * Invert the register value bits to make sure that two
+ * consecutive unsuccessful reads do not return the same
+ * value.
+ */
+ reg ^= 0xffff;
+ }
+ }
+ snd_printk(KERN_ERR "AC'97 read timeout on codec %u\n", codec);
+ return 0;
+}
+EXPORT_SYMBOL(oxygen_read_ac97);
+
+void oxygen_write_ac97_masked(struct oxygen *chip, unsigned int codec,
+ unsigned int index, u16 data, u16 mask)
+{
+ u16 value = oxygen_read_ac97(chip, codec, index);
+ value &= ~mask;
+ value |= data & mask;
+ oxygen_write_ac97(chip, codec, index, value);
+}
+EXPORT_SYMBOL(oxygen_write_ac97_masked);
+
+void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data)
+{
+ unsigned int count;
+
+ /* should not need more than 7.68 us (24 * 320 ns) */
+ count = 10;
+ while ((oxygen_read8(chip, OXYGEN_SPI_CONTROL) & OXYGEN_SPI_BUSY)
+ && count > 0) {
+ udelay(1);
+ --count;
+ }
+
+ spin_lock_irq(&chip->reg_lock);
+ oxygen_write8(chip, OXYGEN_SPI_DATA1, data);
+ oxygen_write8(chip, OXYGEN_SPI_DATA2, data >> 8);
+ if (control & OXYGEN_SPI_DATA_LENGTH_3)
+ oxygen_write8(chip, OXYGEN_SPI_DATA3, data >> 16);
+ oxygen_write8(chip, OXYGEN_SPI_CONTROL, control);
+ spin_unlock_irq(&chip->reg_lock);
+}
+EXPORT_SYMBOL(oxygen_write_spi);
diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c
new file mode 100644
index 00000000000..6eb36dd1147
--- /dev/null
+++ b/sound/pci/oxygen/oxygen_lib.c
@@ -0,0 +1,515 @@
+/*
+ * C-Media CMI8788 driver - main driver module
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ *
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2.
+ *
+ * This driver 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 driver; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <sound/ac97_codec.h>
+#include <sound/asoundef.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/mpu401.h>
+#include <sound/pcm.h>
+#include "oxygen.h"
+#include "cm9780.h"
+
+MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
+MODULE_DESCRIPTION("C-Media CMI8788 helper library");
+MODULE_LICENSE("GPL");
+
+
+static irqreturn_t oxygen_interrupt(int dummy, void *dev_id)
+{
+ struct oxygen *chip = dev_id;
+ unsigned int status, clear, elapsed_streams, i;
+
+ status = oxygen_read16(chip, OXYGEN_INTERRUPT_STATUS);
+ if (!status)
+ return IRQ_NONE;
+
+ spin_lock(&chip->reg_lock);
+
+ clear = status & (OXYGEN_CHANNEL_A |
+ OXYGEN_CHANNEL_B |
+ OXYGEN_CHANNEL_C |
+ OXYGEN_CHANNEL_SPDIF |
+ OXYGEN_CHANNEL_MULTICH |
+ OXYGEN_CHANNEL_AC97 |
+ OXYGEN_INT_SPDIF_IN_DETECT |
+ OXYGEN_INT_GPIO |
+ OXYGEN_INT_AC97);
+ if (clear) {
+ if (clear & OXYGEN_INT_SPDIF_IN_DETECT)
+ chip->interrupt_mask &= ~OXYGEN_INT_SPDIF_IN_DETECT;
+ oxygen_write16(chip, OXYGEN_INTERRUPT_MASK,
+ chip->interrupt_mask & ~clear);
+ oxygen_write16(chip, OXYGEN_INTERRUPT_MASK,
+ chip->interrupt_mask);
+ }
+
+ elapsed_streams = status & chip->pcm_running;
+
+ spin_unlock(&chip->reg_lock);
+
+ for (i = 0; i < PCM_COUNT; ++i)
+ if ((elapsed_streams & (1 << i)) && chip->streams[i])
+ snd_pcm_period_elapsed(chip->streams[i]);
+
+ if (status & OXYGEN_INT_SPDIF_IN_DETECT) {
+ spin_lock(&chip->reg_lock);
+ i = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL);
+ if (i & (OXYGEN_SPDIF_SENSE_INT | OXYGEN_SPDIF_LOCK_INT |
+ OXYGEN_SPDIF_RATE_INT)) {
+ /* write the interrupt bit(s) to clear */
+ oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, i);
+ schedule_work(&chip->spdif_input_bits_work);
+ }
+ spin_unlock(&chip->reg_lock);
+ }
+
+ if (status & OXYGEN_INT_GPIO)
+ schedule_work(&chip->gpio_work);
+
+ if ((status & OXYGEN_INT_MIDI) && chip->midi)
+ snd_mpu401_uart_interrupt(0, chip->midi->private_data);
+
+ if (status & OXYGEN_INT_AC97)
+ wake_up(&chip->ac97_waitqueue);
+
+ return IRQ_HANDLED;
+}
+
+static void oxygen_spdif_input_bits_changed(struct work_struct *work)
+{
+ struct oxygen *chip = container_of(work, struct oxygen,
+ spdif_input_bits_work);
+ u32 reg;
+
+ /*
+ * This function gets called when there is new activity on the SPDIF
+ * input, or when we lose lock on the input signal, or when the rate
+ * changes.
+ */
+ msleep(1);
+ spin_lock_irq(&chip->reg_lock);
+ reg = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL);
+ if ((reg & (OXYGEN_SPDIF_SENSE_STATUS |
+ OXYGEN_SPDIF_LOCK_STATUS))
+ == OXYGEN_SPDIF_SENSE_STATUS) {
+ /*
+ * If we detect activity on the SPDIF input but cannot lock to
+ * a signal, the clock bit is likely to be wrong.
+ */
+ reg ^= OXYGEN_SPDIF_IN_CLOCK_MASK;
+ oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, reg);
+ spin_unlock_irq(&chip->reg_lock);
+ msleep(1);
+ spin_lock_irq(&chip->reg_lock);
+ reg = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL);
+ if ((reg & (OXYGEN_SPDIF_SENSE_STATUS |
+ OXYGEN_SPDIF_LOCK_STATUS))
+ == OXYGEN_SPDIF_SENSE_STATUS) {
+ /* nothing detected with either clock; give up */
+ if ((reg & OXYGEN_SPDIF_IN_CLOCK_MASK)
+ == OXYGEN_SPDIF_IN_CLOCK_192) {
+ /*
+ * Reset clock to <= 96 kHz because this is
+ * more likely to be received next time.
+ */
+ reg &= ~OXYGEN_SPDIF_IN_CLOCK_MASK;
+ reg |= OXYGEN_SPDIF_IN_CLOCK_96;
+ oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, reg);
+ }
+ }
+ }
+ spin_unlock_irq(&chip->reg_lock);
+
+ if (chip->controls[CONTROL_SPDIF_INPUT_BITS]) {
+ spin_lock_irq(&chip->reg_lock);
+ chip->interrupt_mask |= OXYGEN_INT_SPDIF_IN_DETECT;
+ oxygen_write16(chip, OXYGEN_INTERRUPT_MASK,
+ chip->interrupt_mask);
+ spin_unlock_irq(&chip->reg_lock);
+
+ /*
+ * We don't actually know that any channel status bits have
+ * changed, but let's send a notification just to be sure.
+ */
+ snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &chip->controls[CONTROL_SPDIF_INPUT_BITS]->id);
+ }
+}
+
+static void oxygen_gpio_changed(struct work_struct *work)
+{
+ struct oxygen *chip = container_of(work, struct oxygen, gpio_work);
+
+ if (chip->model->gpio_changed)
+ chip->model->gpio_changed(chip);
+}
+
+#ifdef CONFIG_PROC_FS
+static void oxygen_proc_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct oxygen *chip = entry->private_data;
+ int i, j;
+
+ snd_iprintf(buffer, "CMI8788\n\n");
+ for (i = 0; i < 0x100; i += 0x10) {
+ snd_iprintf(buffer, "%02x:", i);
+ for (j = 0; j < 0x10; ++j)
+ snd_iprintf(buffer, " %02x", oxygen_read8(chip, i + j));
+ snd_iprintf(buffer, "\n");
+ }
+ if (mutex_lock_interruptible(&chip->mutex) < 0)
+ return;
+ if (chip->has_ac97_0) {
+ snd_iprintf(buffer, "\nAC97\n");
+ for (i = 0; i < 0x80; i += 0x10) {
+ snd_iprintf(buffer, "%02x:", i);
+ for (j = 0; j < 0x10; j += 2)
+ snd_iprintf(buffer, " %04x",
+ oxygen_read_ac97(chip, 0, i + j));
+ snd_iprintf(buffer, "\n");
+ }
+ }
+ if (chip->has_ac97_1) {
+ snd_iprintf(buffer, "\nAC97 2\n");
+ for (i = 0; i < 0x80; i += 0x10) {
+ snd_iprintf(buffer, "%02x:", i);
+ for (j = 0; j < 0x10; j += 2)
+ snd_iprintf(buffer, " %04x",
+ oxygen_read_ac97(chip, 1, i + j));
+ snd_iprintf(buffer, "\n");
+ }
+ }
+ mutex_unlock(&chip->mutex);
+}
+
+static void __devinit oxygen_proc_init(struct oxygen *chip)
+{
+ struct snd_info_entry *entry;
+
+ if (!snd_card_proc_new(chip->card, "cmi8788", &entry))
+ snd_info_set_text_ops(entry, chip, oxygen_proc_read);
+}
+#else
+#define oxygen_proc_init(chip)
+#endif
+
+static void __devinit oxygen_init(struct oxygen *chip)
+{
+ unsigned int i;
+
+ chip->dac_routing = 1;
+ for (i = 0; i < 8; ++i)
+ chip->dac_volume[i] = 0xff;
+ chip->spdif_playback_enable = 1;
+ chip->spdif_bits = OXYGEN_SPDIF_C | OXYGEN_SPDIF_ORIGINAL |
+ (IEC958_AES1_CON_PCM_CODER << OXYGEN_SPDIF_CATEGORY_SHIFT);
+ chip->spdif_pcm_bits = chip->spdif_bits;
+
+ if (oxygen_read8(chip, OXYGEN_REVISION) & OXYGEN_REVISION_2)
+ chip->revision = 2;
+ else
+ chip->revision = 1;
+
+ if (chip->revision == 1)
+ oxygen_set_bits8(chip, OXYGEN_MISC,
+ OXYGEN_MISC_PCI_MEM_W_1_CLOCK);
+
+ i = oxygen_read16(chip, OXYGEN_AC97_CONTROL);
+ chip->has_ac97_0 = (i & OXYGEN_AC97_CODEC_0) != 0;
+ chip->has_ac97_1 = (i & OXYGEN_AC97_CODEC_1) != 0;
+
+ oxygen_set_bits8(chip, OXYGEN_FUNCTION,
+ OXYGEN_FUNCTION_RESET_CODEC |
+ chip->model->function_flags);
+ oxygen_write8_masked(chip, OXYGEN_FUNCTION,
+ OXYGEN_FUNCTION_SPI,
+ OXYGEN_FUNCTION_2WIRE_SPI_MASK);
+ oxygen_write8(chip, OXYGEN_DMA_STATUS, 0);
+ oxygen_write8(chip, OXYGEN_DMA_PAUSE, 0);
+ oxygen_write8(chip, OXYGEN_PLAY_CHANNELS,
+ OXYGEN_PLAY_CHANNELS_2 |
+ OXYGEN_DMA_A_BURST_8 |
+ OXYGEN_DMA_MULTICH_BURST_8);
+ oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0);
+ oxygen_write8_masked(chip, OXYGEN_MISC, 0,
+ OXYGEN_MISC_WRITE_PCI_SUBID |
+ OXYGEN_MISC_REC_C_FROM_SPDIF |
+ OXYGEN_MISC_REC_B_FROM_AC97 |
+ OXYGEN_MISC_REC_A_FROM_MULTICH);
+ oxygen_write8(chip, OXYGEN_REC_FORMAT,
+ (OXYGEN_FORMAT_16 << OXYGEN_REC_FORMAT_A_SHIFT) |
+ (OXYGEN_FORMAT_16 << OXYGEN_REC_FORMAT_B_SHIFT) |
+ (OXYGEN_FORMAT_16 << OXYGEN_REC_FORMAT_C_SHIFT));
+ oxygen_write8(chip, OXYGEN_PLAY_FORMAT,
+ (OXYGEN_FORMAT_16 << OXYGEN_SPDIF_FORMAT_SHIFT) |
+ (OXYGEN_FORMAT_16 << OXYGEN_MULTICH_FORMAT_SHIFT));
+ oxygen_write8(chip, OXYGEN_REC_CHANNELS, OXYGEN_REC_CHANNELS_2_2_2);
+ oxygen_write16(chip, OXYGEN_I2S_MULTICH_FORMAT,
+ OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST |
+ OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 |
+ OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
+ oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
+ OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST |
+ OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 |
+ OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
+ oxygen_write16(chip, OXYGEN_I2S_B_FORMAT,
+ OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST |
+ OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 |
+ OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
+ oxygen_write16(chip, OXYGEN_I2S_C_FORMAT,
+ OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST |
+ OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 |
+ OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
+ oxygen_write32_masked(chip, OXYGEN_SPDIF_CONTROL,
+ OXYGEN_SPDIF_SENSE_MASK |
+ OXYGEN_SPDIF_LOCK_MASK |
+ OXYGEN_SPDIF_RATE_MASK |
+ OXYGEN_SPDIF_LOCK_PAR |
+ OXYGEN_SPDIF_IN_CLOCK_96,
+ OXYGEN_SPDIF_OUT_ENABLE |
+ OXYGEN_SPDIF_LOOPBACK |
+ OXYGEN_SPDIF_SENSE_MASK |
+ OXYGEN_SPDIF_LOCK_MASK |
+ OXYGEN_SPDIF_RATE_MASK |
+ OXYGEN_SPDIF_SENSE_PAR |
+ OXYGEN_SPDIF_LOCK_PAR |
+ OXYGEN_SPDIF_IN_CLOCK_MASK);
+ oxygen_write32(chip, OXYGEN_SPDIF_OUTPUT_BITS, chip->spdif_bits);
+ oxygen_clear_bits8(chip, OXYGEN_MPU401_CONTROL, OXYGEN_MPU401_LOOPBACK);
+ oxygen_write8(chip, OXYGEN_GPI_INTERRUPT_MASK, 0);
+ oxygen_write16(chip, OXYGEN_GPIO_INTERRUPT_MASK, 0);
+ oxygen_write16(chip, OXYGEN_PLAY_ROUTING,
+ OXYGEN_PLAY_MULTICH_I2S_DAC |
+ OXYGEN_PLAY_SPDIF_SPDIF |
+ (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) |
+ (1 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
+ (2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) |
+ (3 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT));
+ oxygen_write8(chip, OXYGEN_REC_ROUTING,
+ OXYGEN_REC_A_ROUTE_I2S_ADC_1 |
+ OXYGEN_REC_B_ROUTE_I2S_ADC_2 |
+ OXYGEN_REC_C_ROUTE_SPDIF);
+ oxygen_write8(chip, OXYGEN_ADC_MONITOR, 0);
+ oxygen_write8(chip, OXYGEN_A_MONITOR_ROUTING,
+ (0 << OXYGEN_A_MONITOR_ROUTE_0_SHIFT) |
+ (1 << OXYGEN_A_MONITOR_ROUTE_1_SHIFT) |
+ (2 << OXYGEN_A_MONITOR_ROUTE_2_SHIFT) |
+ (3 << OXYGEN_A_MONITOR_ROUTE_3_SHIFT));
+
+ oxygen_write8(chip, OXYGEN_AC97_INTERRUPT_MASK,
+ OXYGEN_AC97_INT_READ_DONE |
+ OXYGEN_AC97_INT_WRITE_DONE);
+ oxygen_write32(chip, OXYGEN_AC97_OUT_CONFIG, 0);
+ oxygen_write32(chip, OXYGEN_AC97_IN_CONFIG, 0);
+ if (!(chip->has_ac97_0 | chip->has_ac97_1))
+ oxygen_set_bits16(chip, OXYGEN_AC97_CONTROL,
+ OXYGEN_AC97_CLOCK_DISABLE);
+ if (!chip->has_ac97_0) {
+ oxygen_set_bits16(chip, OXYGEN_AC97_CONTROL,
+ OXYGEN_AC97_NO_CODEC_0);
+ } else {
+ oxygen_write_ac97(chip, 0, AC97_RESET, 0);
+ msleep(1);
+ oxygen_ac97_set_bits(chip, 0, CM9780_GPIO_SETUP,
+ CM9780_GPIO0IO | CM9780_GPIO1IO);
+ oxygen_ac97_set_bits(chip, 0, CM9780_MIXER,
+ CM9780_BSTSEL | CM9780_STRO_MIC |
+ CM9780_MIX2FR | CM9780_PCBSW);
+ oxygen_ac97_set_bits(chip, 0, CM9780_JACK,
+ CM9780_RSOE | CM9780_CBOE |
+ CM9780_SSOE | CM9780_FROE |
+ CM9780_MIC2MIC | CM9780_LI2LI);
+ oxygen_write_ac97(chip, 0, AC97_MASTER, 0x0000);
+ oxygen_write_ac97(chip, 0, AC97_PC_BEEP, 0x8000);
+ oxygen_write_ac97(chip, 0, AC97_MIC, 0x8808);
+ oxygen_write_ac97(chip, 0, AC97_LINE, 0x0808);
+ oxygen_write_ac97(chip, 0, AC97_CD, 0x8808);
+ oxygen_write_ac97(chip, 0, AC97_VIDEO, 0x8808);
+ oxygen_write_ac97(chip, 0, AC97_AUX, 0x8808);
+ oxygen_write_ac97(chip, 0, AC97_REC_GAIN, 0x8000);
+ oxygen_write_ac97(chip, 0, AC97_CENTER_LFE_MASTER, 0x8080);
+ oxygen_write_ac97(chip, 0, AC97_SURROUND_MASTER, 0x8080);
+ /* power down unused ADCs and DACs */
+ oxygen_ac97_set_bits(chip, 0, AC97_POWERDOWN,
+ AC97_PD_PR0 | AC97_PD_PR1);
+ oxygen_ac97_set_bits(chip, 0, AC97_EXTENDED_STATUS,
+ AC97_EA_PRI | AC97_EA_PRJ | AC97_EA_PRK);
+ }
+ if (chip->has_ac97_1) {
+ oxygen_set_bits32(chip, OXYGEN_AC97_OUT_CONFIG,
+ OXYGEN_AC97_CODEC1_SLOT3 |
+ OXYGEN_AC97_CODEC1_SLOT4);
+ oxygen_write_ac97(chip, 1, AC97_RESET, 0);
+ msleep(1);
+ oxygen_write_ac97(chip, 1, AC97_MASTER, 0x0000);
+ oxygen_write_ac97(chip, 1, AC97_HEADPHONE, 0x8000);
+ oxygen_write_ac97(chip, 1, AC97_PC_BEEP, 0x8000);
+ oxygen_write_ac97(chip, 1, AC97_MIC, 0x8808);
+ oxygen_write_ac97(chip, 1, AC97_LINE, 0x8808);
+ oxygen_write_ac97(chip, 1, AC97_CD, 0x8808);
+ oxygen_write_ac97(chip, 1, AC97_VIDEO, 0x8808);
+ oxygen_write_ac97(chip, 1, AC97_AUX, 0x8808);
+ oxygen_write_ac97(chip, 1, AC97_PCM, 0x0808);
+ oxygen_write_ac97(chip, 1, AC97_REC_SEL, 0x0000);
+ oxygen_write_ac97(chip, 1, AC97_REC_GAIN, 0x0000);
+ oxygen_ac97_set_bits(chip, 1, 0x6a, 0x0040);
+ }
+}
+
+static void oxygen_card_free(struct snd_card *card)
+{
+ struct oxygen *chip = card->private_data;
+
+ spin_lock_irq(&chip->reg_lock);
+ chip->interrupt_mask = 0;
+ chip->pcm_running = 0;
+ oxygen_write16(chip, OXYGEN_DMA_STATUS, 0);
+ oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0);
+ spin_unlock_irq(&chip->reg_lock);
+ if (chip->irq >= 0) {
+ free_irq(chip->irq, chip);
+ synchronize_irq(chip->irq);
+ }
+ flush_scheduled_work();
+ chip->model->cleanup(chip);
+ mutex_destroy(&chip->mutex);
+ pci_release_regions(chip->pci);
+ pci_disable_device(chip->pci);
+}
+
+int __devinit oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
+ int midi, const struct oxygen_model *model)
+{
+ struct snd_card *card;
+ struct oxygen *chip;
+ int err;
+
+ card = snd_card_new(index, id, model->owner,
+ sizeof *chip + model->model_data_size);
+ if (!card)
+ return -ENOMEM;
+
+ chip = card->private_data;
+ chip->card = card;
+ chip->pci = pci;
+ chip->irq = -1;
+ chip->model = model;
+ chip->model_data = chip + 1;
+ spin_lock_init(&chip->reg_lock);
+ mutex_init(&chip->mutex);
+ INIT_WORK(&chip->spdif_input_bits_work,
+ oxygen_spdif_input_bits_changed);
+ INIT_WORK(&chip->gpio_work, oxygen_gpio_changed);
+ init_waitqueue_head(&chip->ac97_waitqueue);
+
+ err = pci_enable_device(pci);
+ if (err < 0)
+ goto err_card;
+
+ err = pci_request_regions(pci, model->chip);
+ if (err < 0) {
+ snd_printk(KERN_ERR "cannot reserve PCI resources\n");
+ goto err_pci_enable;
+ }
+
+ if (!(pci_resource_flags(pci, 0) & IORESOURCE_IO) ||
+ pci_resource_len(pci, 0) < 0x100) {
+ snd_printk(KERN_ERR "invalid PCI I/O range\n");
+ err = -ENXIO;
+ goto err_pci_regions;
+ }
+ chip->addr = pci_resource_start(pci, 0);
+
+ pci_set_master(pci);
+ snd_card_set_dev(card, &pci->dev);
+ card->private_free = oxygen_card_free;
+
+ oxygen_init(chip);
+ model->init(chip);
+
+ err = request_irq(pci->irq, oxygen_interrupt, IRQF_SHARED,
+ model->chip, chip);
+ if (err < 0) {
+ snd_printk(KERN_ERR "cannot grab interrupt %d\n", pci->irq);
+ goto err_card;
+ }
+ chip->irq = pci->irq;
+
+ strcpy(card->driver, model->chip);
+ strcpy(card->shortname, model->shortname);
+ sprintf(card->longname, "%s (rev %u) at %#lx, irq %i",
+ model->longname, chip->revision, chip->addr, chip->irq);
+ strcpy(card->mixername, model->chip);
+ snd_component_add(card, model->chip);
+
+ err = oxygen_pcm_init(chip);
+ if (err < 0)
+ goto err_card;
+
+ err = oxygen_mixer_init(chip);
+ if (err < 0)
+ goto err_card;
+
+ oxygen_write8_masked(chip, OXYGEN_MISC,
+ midi ? OXYGEN_MISC_MIDI : 0, OXYGEN_MISC_MIDI);
+ if (midi) {
+ err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI,
+ chip->addr + OXYGEN_MPU401,
+ MPU401_INFO_INTEGRATED, 0, 0,
+ &chip->midi);
+ if (err < 0)
+ goto err_card;
+ }
+
+ oxygen_proc_init(chip);
+
+ spin_lock_irq(&chip->reg_lock);
+ chip->interrupt_mask |= OXYGEN_INT_SPDIF_IN_DETECT | OXYGEN_INT_AC97;
+ oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
+ spin_unlock_irq(&chip->reg_lock);
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto err_card;
+
+ pci_set_drvdata(pci, card);
+ return 0;
+
+err_pci_regions:
+ pci_release_regions(pci);
+err_pci_enable:
+ pci_disable_device(pci);
+err_card:
+ snd_card_free(card);
+ return err;
+}
+EXPORT_SYMBOL(oxygen_pci_probe);
+
+void __devexit oxygen_pci_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+EXPORT_SYMBOL(oxygen_pci_remove);
diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c
new file mode 100644
index 00000000000..a8e4623415d
--- /dev/null
+++ b/sound/pci/oxygen/oxygen_mixer.c
@@ -0,0 +1,794 @@
+/*
+ * C-Media CMI8788 driver - mixer code
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ *
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2.
+ *
+ * This driver 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 driver; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/mutex.h>
+#include <sound/ac97_codec.h>
+#include <sound/asoundef.h>
+#include <sound/control.h>
+#include <sound/tlv.h>
+#include "oxygen.h"
+#include "cm9780.h"
+
+static int dac_volume_info(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_info *info)
+{
+ struct oxygen *chip = ctl->private_data;
+
+ info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ info->count = chip->model->dac_channels;
+ info->value.integer.min = 0;
+ info->value.integer.max = 0xff;
+ return 0;
+}
+
+static int dac_volume_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ unsigned int i;
+
+ mutex_lock(&chip->mutex);
+ for (i = 0; i < chip->model->dac_channels; ++i)
+ value->value.integer.value[i] = chip->dac_volume[i];
+ mutex_unlock(&chip->mutex);
+ return 0;
+}
+
+static int dac_volume_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ unsigned int i;
+ int changed;
+
+ changed = 0;
+ mutex_lock(&chip->mutex);
+ for (i = 0; i < chip->model->dac_channels; ++i)
+ if (value->value.integer.value[i] != chip->dac_volume[i]) {
+ chip->dac_volume[i] = value->value.integer.value[i];
+ changed = 1;
+ }
+ if (changed)
+ chip->model->update_dac_volume(chip);
+ mutex_unlock(&chip->mutex);
+ return changed;
+}
+
+static int dac_mute_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+
+ mutex_lock(&chip->mutex);
+ value->value.integer.value[0] = !chip->dac_mute;
+ mutex_unlock(&chip->mutex);
+ return 0;
+}
+
+static int dac_mute_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ int changed;
+
+ mutex_lock(&chip->mutex);
+ changed = !value->value.integer.value[0] != chip->dac_mute;
+ if (changed) {
+ chip->dac_mute = !value->value.integer.value[0];
+ chip->model->update_dac_mute(chip);
+ }
+ mutex_unlock(&chip->mutex);
+ return changed;
+}
+
+static int upmix_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
+{
+ static const char *const names[3] = {
+ "Front", "Front+Surround", "Front+Surround+Back"
+ };
+ struct oxygen *chip = ctl->private_data;
+ unsigned int count = 2 + (chip->model->dac_channels == 8);
+
+ info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ info->count = 1;
+ info->value.enumerated.items = count;
+ if (info->value.enumerated.item >= count)
+ info->value.enumerated.item = count - 1;
+ strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
+ return 0;
+}
+
+static int upmix_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+
+ mutex_lock(&chip->mutex);
+ value->value.enumerated.item[0] = chip->dac_routing;
+ mutex_unlock(&chip->mutex);
+ return 0;
+}
+
+void oxygen_update_dac_routing(struct oxygen *chip)
+{
+ /* DAC 0: front, DAC 1: surround, DAC 2: center/LFE, DAC 3: back */
+ static const unsigned int reg_values[3] = {
+ /* stereo -> front */
+ (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) |
+ (1 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
+ (2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) |
+ (3 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT),
+ /* stereo -> front+surround */
+ (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) |
+ (0 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
+ (2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) |
+ (3 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT),
+ /* stereo -> front+surround+back */
+ (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) |
+ (0 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
+ (2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) |
+ (0 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT),
+ };
+ u8 channels;
+ unsigned int reg_value;
+
+ channels = oxygen_read8(chip, OXYGEN_PLAY_CHANNELS) &
+ OXYGEN_PLAY_CHANNELS_MASK;
+ if (channels == OXYGEN_PLAY_CHANNELS_2)
+ reg_value = reg_values[chip->dac_routing];
+ else if (channels == OXYGEN_PLAY_CHANNELS_8)
+ /* in 7.1 mode, "rear" channels go to the "back" jack */
+ reg_value = (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) |
+ (3 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
+ (2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) |
+ (1 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT);
+ else
+ reg_value = (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) |
+ (1 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
+ (2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) |
+ (3 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT);
+ oxygen_write16_masked(chip, OXYGEN_PLAY_ROUTING, reg_value,
+ OXYGEN_PLAY_DAC0_SOURCE_MASK |
+ OXYGEN_PLAY_DAC1_SOURCE_MASK |
+ OXYGEN_PLAY_DAC2_SOURCE_MASK |
+ OXYGEN_PLAY_DAC3_SOURCE_MASK);
+}
+
+static int upmix_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ unsigned int count = 2 + (chip->model->dac_channels == 8);
+ int changed;
+
+ mutex_lock(&chip->mutex);
+ changed = value->value.enumerated.item[0] != chip->dac_routing;
+ if (changed) {
+ chip->dac_routing = min(value->value.enumerated.item[0],
+ count - 1);
+ spin_lock_irq(&chip->reg_lock);
+ oxygen_update_dac_routing(chip);
+ spin_unlock_irq(&chip->reg_lock);
+ }
+ mutex_unlock(&chip->mutex);
+ return changed;
+}
+
+static int spdif_switch_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+
+ mutex_lock(&chip->mutex);
+ value->value.integer.value[0] = chip->spdif_playback_enable;
+ mutex_unlock(&chip->mutex);
+ return 0;
+}
+
+static unsigned int oxygen_spdif_rate(unsigned int oxygen_rate)
+{
+ switch (oxygen_rate) {
+ case OXYGEN_RATE_32000:
+ return IEC958_AES3_CON_FS_32000 << OXYGEN_SPDIF_CS_RATE_SHIFT;
+ case OXYGEN_RATE_44100:
+ return IEC958_AES3_CON_FS_44100 << OXYGEN_SPDIF_CS_RATE_SHIFT;
+ default: /* OXYGEN_RATE_48000 */
+ return IEC958_AES3_CON_FS_48000 << OXYGEN_SPDIF_CS_RATE_SHIFT;
+ case OXYGEN_RATE_64000:
+ return 0xb << OXYGEN_SPDIF_CS_RATE_SHIFT;
+ case OXYGEN_RATE_88200:
+ return 0x8 << OXYGEN_SPDIF_CS_RATE_SHIFT;
+ case OXYGEN_RATE_96000:
+ return 0xa << OXYGEN_SPDIF_CS_RATE_SHIFT;
+ case OXYGEN_RATE_176400:
+ return 0xc << OXYGEN_SPDIF_CS_RATE_SHIFT;
+ case OXYGEN_RATE_192000:
+ return 0xe << OXYGEN_SPDIF_CS_RATE_SHIFT;
+ }
+}
+
+void oxygen_update_spdif_source(struct oxygen *chip)
+{
+ u32 old_control, new_control;
+ u16 old_routing, new_routing;
+ unsigned int oxygen_rate;
+
+ old_control = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL);
+ old_routing = oxygen_read16(chip, OXYGEN_PLAY_ROUTING);
+ if (chip->pcm_active & (1 << PCM_SPDIF)) {
+ new_control = old_control | OXYGEN_SPDIF_OUT_ENABLE;
+ new_routing = (old_routing & ~OXYGEN_PLAY_SPDIF_MASK)
+ | OXYGEN_PLAY_SPDIF_SPDIF;
+ oxygen_rate = (old_control >> OXYGEN_SPDIF_OUT_RATE_SHIFT)
+ & OXYGEN_I2S_RATE_MASK;
+ /* S/PDIF rate was already set by the caller */
+ } else if ((chip->pcm_active & (1 << PCM_MULTICH)) &&
+ chip->spdif_playback_enable) {
+ new_routing = (old_routing & ~OXYGEN_PLAY_SPDIF_MASK)
+ | OXYGEN_PLAY_SPDIF_MULTICH_01;
+ oxygen_rate = oxygen_read16(chip, OXYGEN_I2S_MULTICH_FORMAT)
+ & OXYGEN_I2S_RATE_MASK;
+ new_control = (old_control & ~OXYGEN_SPDIF_OUT_RATE_MASK) |
+ (oxygen_rate << OXYGEN_SPDIF_OUT_RATE_SHIFT) |
+ OXYGEN_SPDIF_OUT_ENABLE;
+ } else {
+ new_control = old_control & ~OXYGEN_SPDIF_OUT_ENABLE;
+ new_routing = old_routing;
+ oxygen_rate = OXYGEN_RATE_44100;
+ }
+ if (old_routing != new_routing) {
+ oxygen_write32(chip, OXYGEN_SPDIF_CONTROL,
+ new_control & ~OXYGEN_SPDIF_OUT_ENABLE);
+ oxygen_write16(chip, OXYGEN_PLAY_ROUTING, new_routing);
+ }
+ if (new_control & OXYGEN_SPDIF_OUT_ENABLE)
+ oxygen_write32(chip, OXYGEN_SPDIF_OUTPUT_BITS,
+ oxygen_spdif_rate(oxygen_rate) |
+ ((chip->pcm_active & (1 << PCM_SPDIF)) ?
+ chip->spdif_pcm_bits : chip->spdif_bits));
+ oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, new_control);
+}
+
+static int spdif_switch_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ int changed;
+
+ mutex_lock(&chip->mutex);
+ changed = value->value.integer.value[0] != chip->spdif_playback_enable;
+ if (changed) {
+ chip->spdif_playback_enable = !!value->value.integer.value[0];
+ spin_lock_irq(&chip->reg_lock);
+ oxygen_update_spdif_source(chip);
+ spin_unlock_irq(&chip->reg_lock);
+ }
+ mutex_unlock(&chip->mutex);
+ return changed;
+}
+
+static int spdif_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
+{
+ info->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ info->count = 1;
+ return 0;
+}
+
+static void oxygen_to_iec958(u32 bits, struct snd_ctl_elem_value *value)
+{
+ value->value.iec958.status[0] =
+ bits & (OXYGEN_SPDIF_NONAUDIO | OXYGEN_SPDIF_C |
+ OXYGEN_SPDIF_PREEMPHASIS);
+ value->value.iec958.status[1] = /* category and original */
+ bits >> OXYGEN_SPDIF_CATEGORY_SHIFT;
+}
+
+static u32 iec958_to_oxygen(struct snd_ctl_elem_value *value)
+{
+ u32 bits;
+
+ bits = value->value.iec958.status[0] &
+ (OXYGEN_SPDIF_NONAUDIO | OXYGEN_SPDIF_C |
+ OXYGEN_SPDIF_PREEMPHASIS);
+ bits |= value->value.iec958.status[1] << OXYGEN_SPDIF_CATEGORY_SHIFT;
+ if (bits & OXYGEN_SPDIF_NONAUDIO)
+ bits |= OXYGEN_SPDIF_V;
+ return bits;
+}
+
+static inline void write_spdif_bits(struct oxygen *chip, u32 bits)
+{
+ oxygen_write32_masked(chip, OXYGEN_SPDIF_OUTPUT_BITS, bits,
+ OXYGEN_SPDIF_NONAUDIO |
+ OXYGEN_SPDIF_C |
+ OXYGEN_SPDIF_PREEMPHASIS |
+ OXYGEN_SPDIF_CATEGORY_MASK |
+ OXYGEN_SPDIF_ORIGINAL |
+ OXYGEN_SPDIF_V);
+}
+
+static int spdif_default_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+
+ mutex_lock(&chip->mutex);
+ oxygen_to_iec958(chip->spdif_bits, value);
+ mutex_unlock(&chip->mutex);
+ return 0;
+}
+
+static int spdif_default_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ u32 new_bits;
+ int changed;
+
+ new_bits = iec958_to_oxygen(value);
+ mutex_lock(&chip->mutex);
+ changed = new_bits != chip->spdif_bits;
+ if (changed) {
+ chip->spdif_bits = new_bits;
+ if (!(chip->pcm_active & (1 << PCM_SPDIF)))
+ write_spdif_bits(chip, new_bits);
+ }
+ mutex_unlock(&chip->mutex);
+ return changed;
+}
+
+static int spdif_mask_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ value->value.iec958.status[0] = IEC958_AES0_NONAUDIO |
+ IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS;
+ value->value.iec958.status[1] =
+ IEC958_AES1_CON_CATEGORY | IEC958_AES1_CON_ORIGINAL;
+ return 0;
+}
+
+static int spdif_pcm_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+
+ mutex_lock(&chip->mutex);
+ oxygen_to_iec958(chip->spdif_pcm_bits, value);
+ mutex_unlock(&chip->mutex);
+ return 0;
+}
+
+static int spdif_pcm_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ u32 new_bits;
+ int changed;
+
+ new_bits = iec958_to_oxygen(value);
+ mutex_lock(&chip->mutex);
+ changed = new_bits != chip->spdif_pcm_bits;
+ if (changed) {
+ chip->spdif_pcm_bits = new_bits;
+ if (chip->pcm_active & (1 << PCM_SPDIF))
+ write_spdif_bits(chip, new_bits);
+ }
+ mutex_unlock(&chip->mutex);
+ return changed;
+}
+
+static int spdif_input_mask_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ value->value.iec958.status[0] = 0xff;
+ value->value.iec958.status[1] = 0xff;
+ value->value.iec958.status[2] = 0xff;
+ value->value.iec958.status[3] = 0xff;
+ return 0;
+}
+
+static int spdif_input_default_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ u32 bits;
+
+ bits = oxygen_read32(chip, OXYGEN_SPDIF_INPUT_BITS);
+ value->value.iec958.status[0] = bits;
+ value->value.iec958.status[1] = bits >> 8;
+ value->value.iec958.status[2] = bits >> 16;
+ value->value.iec958.status[3] = bits >> 24;
+ return 0;
+}
+
+static int spdif_loopback_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+
+ value->value.integer.value[0] =
+ !!(oxygen_read32(chip, OXYGEN_SPDIF_CONTROL)
+ & OXYGEN_SPDIF_LOOPBACK);
+ return 0;
+}
+
+static int spdif_loopback_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ u32 oldreg, newreg;
+ int changed;
+
+ spin_lock_irq(&chip->reg_lock);
+ oldreg = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL);
+ if (value->value.integer.value[0])
+ newreg = oldreg | OXYGEN_SPDIF_LOOPBACK;
+ else
+ newreg = oldreg & ~OXYGEN_SPDIF_LOOPBACK;
+ changed = newreg != oldreg;
+ if (changed)
+ oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, newreg);
+ spin_unlock_irq(&chip->reg_lock);
+ return changed;
+}
+
+static int ac97_switch_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ unsigned int codec = (ctl->private_value >> 24) & 1;
+ unsigned int index = ctl->private_value & 0xff;
+ unsigned int bitnr = (ctl->private_value >> 8) & 0xff;
+ int invert = ctl->private_value & (1 << 16);
+ u16 reg;
+
+ mutex_lock(&chip->mutex);
+ reg = oxygen_read_ac97(chip, codec, index);
+ mutex_unlock(&chip->mutex);
+ if (!(reg & (1 << bitnr)) ^ !invert)
+ value->value.integer.value[0] = 1;
+ else
+ value->value.integer.value[0] = 0;
+ return 0;
+}
+
+static int ac97_switch_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ unsigned int codec = (ctl->private_value >> 24) & 1;
+ unsigned int index = ctl->private_value & 0xff;
+ unsigned int bitnr = (ctl->private_value >> 8) & 0xff;
+ int invert = ctl->private_value & (1 << 16);
+ u16 oldreg, newreg;
+ int change;
+
+ mutex_lock(&chip->mutex);
+ oldreg = oxygen_read_ac97(chip, codec, index);
+ newreg = oldreg;
+ if (!value->value.integer.value[0] ^ !invert)
+ newreg |= 1 << bitnr;
+ else
+ newreg &= ~(1 << bitnr);
+ change = newreg != oldreg;
+ if (change) {
+ oxygen_write_ac97(chip, codec, index, newreg);
+ if (bitnr == 15 && chip->model->ac97_switch_hook)
+ chip->model->ac97_switch_hook(chip, codec, index,
+ newreg & 0x8000);
+ }
+ mutex_unlock(&chip->mutex);
+ return change;
+}
+
+static int ac97_volume_info(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_info *info)
+{
+ info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ info->count = 2;
+ info->value.integer.min = 0;
+ info->value.integer.max = 0x1f;
+ return 0;
+}
+
+static int ac97_volume_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ unsigned int codec = (ctl->private_value >> 24) & 1;
+ unsigned int index = ctl->private_value & 0xff;
+ u16 reg;
+
+ mutex_lock(&chip->mutex);
+ reg = oxygen_read_ac97(chip, codec, index);
+ mutex_unlock(&chip->mutex);
+ value->value.integer.value[0] = 31 - (reg & 0x1f);
+ value->value.integer.value[1] = 31 - ((reg >> 8) & 0x1f);
+ return 0;
+}
+
+static int ac97_volume_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ unsigned int codec = (ctl->private_value >> 24) & 1;
+ unsigned int index = ctl->private_value & 0xff;
+ u16 oldreg, newreg;
+ int change;
+
+ mutex_lock(&chip->mutex);
+ oldreg = oxygen_read_ac97(chip, codec, index);
+ newreg = oldreg;
+ newreg = (newreg & ~0x1f) |
+ (31 - (value->value.integer.value[0] & 0x1f));
+ newreg = (newreg & ~0x1f00) |
+ ((31 - (value->value.integer.value[0] & 0x1f)) << 8);
+ change = newreg != oldreg;
+ if (change)
+ oxygen_write_ac97(chip, codec, index, newreg);
+ mutex_unlock(&chip->mutex);
+ return change;
+}
+
+static int ac97_fp_rec_volume_info(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_info *info)
+{
+ info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ info->count = 2;
+ info->value.integer.min = 0;
+ info->value.integer.max = 7;
+ return 0;
+}
+
+static int ac97_fp_rec_volume_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ u16 reg;
+
+ mutex_lock(&chip->mutex);
+ reg = oxygen_read_ac97(chip, 1, AC97_REC_GAIN);
+ mutex_unlock(&chip->mutex);
+ value->value.integer.value[0] = reg & 7;
+ value->value.integer.value[1] = (reg >> 8) & 7;
+ return 0;
+}
+
+static int ac97_fp_rec_volume_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ u16 oldreg, newreg;
+ int change;
+
+ mutex_lock(&chip->mutex);
+ oldreg = oxygen_read_ac97(chip, 1, AC97_REC_GAIN);
+ newreg = oldreg & ~0x0707;
+ newreg = newreg | (value->value.integer.value[0] & 7);
+ newreg = newreg | ((value->value.integer.value[0] & 7) << 8);
+ change = newreg != oldreg;
+ if (change)
+ oxygen_write_ac97(chip, 1, AC97_REC_GAIN, newreg);
+ mutex_unlock(&chip->mutex);
+ return change;
+}
+
+#define AC97_SWITCH(xname, codec, index, bitnr, invert) { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .info = snd_ctl_boolean_mono_info, \
+ .get = ac97_switch_get, \
+ .put = ac97_switch_put, \
+ .private_value = ((codec) << 24) | ((invert) << 16) | \
+ ((bitnr) << 8) | (index), \
+ }
+#define AC97_VOLUME(xname, codec, index) { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
+ .info = ac97_volume_info, \
+ .get = ac97_volume_get, \
+ .put = ac97_volume_put, \
+ .tlv = { .p = ac97_db_scale, }, \
+ .private_value = ((codec) << 24) | (index), \
+ }
+
+static DECLARE_TLV_DB_SCALE(ac97_db_scale, -3450, 150, 0);
+static DECLARE_TLV_DB_SCALE(ac97_rec_db_scale, 0, 150, 0);
+
+static const struct snd_kcontrol_new controls[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = dac_volume_info,
+ .get = dac_volume_get,
+ .put = dac_volume_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = dac_mute_get,
+ .put = dac_mute_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Stereo Upmixing",
+ .info = upmix_info,
+ .get = upmix_get,
+ .put = upmix_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH),
+ .info = snd_ctl_boolean_mono_info,
+ .get = spdif_switch_get,
+ .put = spdif_switch_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .device = 1,
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
+ .info = spdif_info,
+ .get = spdif_default_get,
+ .put = spdif_default_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .device = 1,
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK),
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .info = spdif_info,
+ .get = spdif_mask_get,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .device = 1,
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM),
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+ .info = spdif_info,
+ .get = spdif_pcm_get,
+ .put = spdif_pcm_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .device = 1,
+ .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK),
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .info = spdif_info,
+ .get = spdif_input_mask_get,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .device = 1,
+ .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .info = spdif_info,
+ .get = spdif_input_default_get,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("Loopback ", NONE, SWITCH),
+ .info = snd_ctl_boolean_mono_info,
+ .get = spdif_loopback_get,
+ .put = spdif_loopback_put,
+ },
+};
+
+static const struct snd_kcontrol_new ac97_controls[] = {
+ AC97_VOLUME("Mic Capture Volume", 0, AC97_MIC),
+ AC97_SWITCH("Mic Capture Switch", 0, AC97_MIC, 15, 1),
+ AC97_SWITCH("Mic Boost (+20dB)", 0, AC97_MIC, 6, 0),
+ AC97_VOLUME("Line Capture Volume", 0, AC97_LINE),
+ AC97_SWITCH("Line Capture Switch", 0, AC97_LINE, 15, 1),
+ AC97_VOLUME("CD Capture Volume", 0, AC97_CD),
+ AC97_SWITCH("CD Capture Switch", 0, AC97_CD, 15, 1),
+ AC97_VOLUME("Aux Capture Volume", 0, AC97_AUX),
+ AC97_SWITCH("Aux Capture Switch", 0, AC97_AUX, 15, 1),
+};
+
+static const struct snd_kcontrol_new ac97_fp_controls[] = {
+ AC97_VOLUME("Front Panel Playback Volume", 1, AC97_HEADPHONE),
+ AC97_SWITCH("Front Panel Playback Switch", 1, AC97_HEADPHONE, 15, 1),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Front Panel Capture Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .info = ac97_fp_rec_volume_info,
+ .get = ac97_fp_rec_volume_get,
+ .put = ac97_fp_rec_volume_put,
+ .tlv = { .p = ac97_rec_db_scale, },
+ },
+ AC97_SWITCH("Front Panel Capture Switch", 1, AC97_REC_GAIN, 15, 1),
+};
+
+static void oxygen_any_ctl_free(struct snd_kcontrol *ctl)
+{
+ struct oxygen *chip = ctl->private_data;
+ unsigned int i;
+
+ /* I'm too lazy to write a function for each control :-) */
+ for (i = 0; i < ARRAY_SIZE(chip->controls); ++i)
+ chip->controls[i] = NULL;
+}
+
+static int add_controls(struct oxygen *chip,
+ const struct snd_kcontrol_new controls[],
+ unsigned int count)
+{
+ static const char *const known_ctl_names[CONTROL_COUNT] = {
+ [CONTROL_SPDIF_PCM] =
+ SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM),
+ [CONTROL_SPDIF_INPUT_BITS] =
+ SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
+ [CONTROL_MIC_CAPTURE_SWITCH] = "Mic Capture Switch",
+ [CONTROL_LINE_CAPTURE_SWITCH] = "Line Capture Switch",
+ [CONTROL_CD_CAPTURE_SWITCH] = "CD Capture Switch",
+ [CONTROL_AUX_CAPTURE_SWITCH] = "Aux Capture Switch",
+ };
+ unsigned int i, j;
+ struct snd_kcontrol_new template;
+ struct snd_kcontrol *ctl;
+ int err;
+
+ for (i = 0; i < count; ++i) {
+ template = controls[i];
+ err = chip->model->control_filter(&template);
+ if (err < 0)
+ return err;
+ if (err == 1)
+ continue;
+ ctl = snd_ctl_new1(&template, chip);
+ if (!ctl)
+ return -ENOMEM;
+ err = snd_ctl_add(chip->card, ctl);
+ if (err < 0)
+ return err;
+ for (j = 0; j < CONTROL_COUNT; ++j)
+ if (!strcmp(ctl->id.name, known_ctl_names[j])) {
+ chip->controls[j] = ctl;
+ ctl->private_free = oxygen_any_ctl_free;
+ }
+ }
+ return 0;
+}
+
+int oxygen_mixer_init(struct oxygen *chip)
+{
+ int err;
+
+ err = add_controls(chip, controls, ARRAY_SIZE(controls));
+ if (err < 0)
+ return err;
+ if (chip->has_ac97_0) {
+ err = add_controls(chip, ac97_controls,
+ ARRAY_SIZE(ac97_controls));
+ if (err < 0)
+ return err;
+ }
+ if (chip->has_ac97_1) {
+ err = add_controls(chip, ac97_fp_controls,
+ ARRAY_SIZE(ac97_fp_controls));
+ if (err < 0)
+ return err;
+ }
+ return chip->model->mixer_init ? chip->model->mixer_init(chip) : 0;
+}
diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c
new file mode 100644
index 00000000000..dfad3db35c8
--- /dev/null
+++ b/sound/pci/oxygen/oxygen_pcm.c
@@ -0,0 +1,718 @@
+/*
+ * C-Media CMI8788 driver - PCM code
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ *
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2.
+ *
+ * This driver 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 driver; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/pci.h>
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "oxygen.h"
+
+static const struct snd_pcm_hardware oxygen_stereo_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_SYNC_START,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .rates = SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_64000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_176400 |
+ SNDRV_PCM_RATE_192000,
+ .rate_min = 32000,
+ .rate_max = 192000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 256 * 1024,
+ .period_bytes_min = 128,
+ .period_bytes_max = 128 * 1024,
+ .periods_min = 2,
+ .periods_max = 2048,
+};
+static const struct snd_pcm_hardware oxygen_multichannel_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_SYNC_START,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .rates = SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_64000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_176400 |
+ SNDRV_PCM_RATE_192000,
+ .rate_min = 32000,
+ .rate_max = 192000,
+ .channels_min = 2,
+ .channels_max = 8,
+ .buffer_bytes_max = 2048 * 1024,
+ .period_bytes_min = 128,
+ .period_bytes_max = 256 * 1024,
+ .periods_min = 2,
+ .periods_max = 16384,
+};
+static const struct snd_pcm_hardware oxygen_ac97_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_SYNC_START,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 256 * 1024,
+ .period_bytes_min = 128,
+ .period_bytes_max = 128 * 1024,
+ .periods_min = 2,
+ .periods_max = 2048,
+};
+
+static const struct snd_pcm_hardware *const oxygen_hardware[PCM_COUNT] = {
+ [PCM_A] = &oxygen_stereo_hardware,
+ [PCM_B] = &oxygen_stereo_hardware,
+ [PCM_C] = &oxygen_stereo_hardware,
+ [PCM_SPDIF] = &oxygen_stereo_hardware,
+ [PCM_MULTICH] = &oxygen_multichannel_hardware,
+ [PCM_AC97] = &oxygen_ac97_hardware,
+};
+
+static inline unsigned int
+oxygen_substream_channel(struct snd_pcm_substream *substream)
+{
+ return (unsigned int)(uintptr_t)substream->runtime->private_data;
+}
+
+static int oxygen_open(struct snd_pcm_substream *substream,
+ unsigned int channel)
+{
+ struct oxygen *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ runtime->private_data = (void *)(uintptr_t)channel;
+ if (channel == PCM_B && chip->has_ac97_1 &&
+ (chip->model->used_channels & OXYGEN_CHANNEL_AC97))
+ runtime->hw = oxygen_ac97_hardware;
+ else
+ runtime->hw = *oxygen_hardware[channel];
+ switch (channel) {
+ case PCM_C:
+ runtime->hw.rates &= ~(SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_64000);
+ runtime->hw.rate_min = 44100;
+ break;
+ case PCM_MULTICH:
+ runtime->hw.channels_max = chip->model->dac_channels;
+ break;
+ }
+ if (chip->model->pcm_hardware_filter)
+ chip->model->pcm_hardware_filter(channel, &runtime->hw);
+ err = snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
+ if (err < 0)
+ return err;
+ err = snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
+ if (err < 0)
+ return err;
+ if (runtime->hw.formats & SNDRV_PCM_FMTBIT_S32_LE) {
+ err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+ if (err < 0)
+ return err;
+ }
+ if (runtime->hw.channels_max > 2) {
+ err = snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ 2);
+ if (err < 0)
+ return err;
+ }
+ snd_pcm_set_sync(substream);
+ chip->streams[channel] = substream;
+
+ mutex_lock(&chip->mutex);
+ chip->pcm_active |= 1 << channel;
+ if (channel == PCM_SPDIF) {
+ chip->spdif_pcm_bits = chip->spdif_bits;
+ chip->controls[CONTROL_SPDIF_PCM]->vd[0].access &=
+ ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE |
+ SNDRV_CTL_EVENT_MASK_INFO,
+ &chip->controls[CONTROL_SPDIF_PCM]->id);
+ }
+ mutex_unlock(&chip->mutex);
+
+ return 0;
+}
+
+static int oxygen_rec_a_open(struct snd_pcm_substream *substream)
+{
+ return oxygen_open(substream, PCM_A);
+}
+
+static int oxygen_rec_b_open(struct snd_pcm_substream *substream)
+{
+ return oxygen_open(substream, PCM_B);
+}
+
+static int oxygen_rec_c_open(struct snd_pcm_substream *substream)
+{
+ return oxygen_open(substream, PCM_C);
+}
+
+static int oxygen_spdif_open(struct snd_pcm_substream *substream)
+{
+ return oxygen_open(substream, PCM_SPDIF);
+}
+
+static int oxygen_multich_open(struct snd_pcm_substream *substream)
+{
+ return oxygen_open(substream, PCM_MULTICH);
+}
+
+static int oxygen_ac97_open(struct snd_pcm_substream *substream)
+{
+ return oxygen_open(substream, PCM_AC97);
+}
+
+static int oxygen_close(struct snd_pcm_substream *substream)
+{
+ struct oxygen *chip = snd_pcm_substream_chip(substream);
+ unsigned int channel = oxygen_substream_channel(substream);
+
+ mutex_lock(&chip->mutex);
+ chip->pcm_active &= ~(1 << channel);
+ if (channel == PCM_SPDIF) {
+ chip->controls[CONTROL_SPDIF_PCM]->vd[0].access |=
+ SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE |
+ SNDRV_CTL_EVENT_MASK_INFO,
+ &chip->controls[CONTROL_SPDIF_PCM]->id);
+ }
+ if (channel == PCM_SPDIF || channel == PCM_MULTICH)
+ oxygen_update_spdif_source(chip);
+ mutex_unlock(&chip->mutex);
+
+ chip->streams[channel] = NULL;
+ return 0;
+}
+
+static unsigned int oxygen_format(struct snd_pcm_hw_params *hw_params)
+{
+ if (params_format(hw_params) == SNDRV_PCM_FORMAT_S32_LE)
+ return OXYGEN_FORMAT_24;
+ else
+ return OXYGEN_FORMAT_16;
+}
+
+static unsigned int oxygen_rate(struct snd_pcm_hw_params *hw_params)
+{
+ switch (params_rate(hw_params)) {
+ case 32000:
+ return OXYGEN_RATE_32000;
+ case 44100:
+ return OXYGEN_RATE_44100;
+ default: /* 48000 */
+ return OXYGEN_RATE_48000;
+ case 64000:
+ return OXYGEN_RATE_64000;
+ case 88200:
+ return OXYGEN_RATE_88200;
+ case 96000:
+ return OXYGEN_RATE_96000;
+ case 176400:
+ return OXYGEN_RATE_176400;
+ case 192000:
+ return OXYGEN_RATE_192000;
+ }
+}
+
+static unsigned int oxygen_i2s_mclk(struct snd_pcm_hw_params *hw_params)
+{
+ if (params_rate(hw_params) <= 96000)
+ return OXYGEN_I2S_MCLK_256;
+ else
+ return OXYGEN_I2S_MCLK_128;
+}
+
+static unsigned int oxygen_i2s_bits(struct snd_pcm_hw_params *hw_params)
+{
+ if (params_format(hw_params) == SNDRV_PCM_FORMAT_S32_LE)
+ return OXYGEN_I2S_BITS_24;
+ else
+ return OXYGEN_I2S_BITS_16;
+}
+
+static unsigned int oxygen_play_channels(struct snd_pcm_hw_params *hw_params)
+{
+ switch (params_channels(hw_params)) {
+ default: /* 2 */
+ return OXYGEN_PLAY_CHANNELS_2;
+ case 4:
+ return OXYGEN_PLAY_CHANNELS_4;
+ case 6:
+ return OXYGEN_PLAY_CHANNELS_6;
+ case 8:
+ return OXYGEN_PLAY_CHANNELS_8;
+ }
+}
+
+static const unsigned int channel_base_registers[PCM_COUNT] = {
+ [PCM_A] = OXYGEN_DMA_A_ADDRESS,
+ [PCM_B] = OXYGEN_DMA_B_ADDRESS,
+ [PCM_C] = OXYGEN_DMA_C_ADDRESS,
+ [PCM_SPDIF] = OXYGEN_DMA_SPDIF_ADDRESS,
+ [PCM_MULTICH] = OXYGEN_DMA_MULTICH_ADDRESS,
+ [PCM_AC97] = OXYGEN_DMA_AC97_ADDRESS,
+};
+
+static int oxygen_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct oxygen *chip = snd_pcm_substream_chip(substream);
+ unsigned int channel = oxygen_substream_channel(substream);
+ int err;
+
+ err = snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+ if (err < 0)
+ return err;
+
+ oxygen_write32(chip, channel_base_registers[channel],
+ (u32)substream->runtime->dma_addr);
+ if (channel == PCM_MULTICH) {
+ oxygen_write32(chip, OXYGEN_DMA_MULTICH_COUNT,
+ params_buffer_bytes(hw_params) / 4 - 1);
+ oxygen_write32(chip, OXYGEN_DMA_MULTICH_TCOUNT,
+ params_period_bytes(hw_params) / 4 - 1);
+ } else {
+ oxygen_write16(chip, channel_base_registers[channel] + 4,
+ params_buffer_bytes(hw_params) / 4 - 1);
+ oxygen_write16(chip, channel_base_registers[channel] + 6,
+ params_period_bytes(hw_params) / 4 - 1);
+ }
+ return 0;
+}
+
+static int oxygen_rec_a_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct oxygen *chip = snd_pcm_substream_chip(substream);
+ int err;
+
+ err = oxygen_hw_params(substream, hw_params);
+ if (err < 0)
+ return err;
+
+ spin_lock_irq(&chip->reg_lock);
+ oxygen_write8_masked(chip, OXYGEN_REC_FORMAT,
+ oxygen_format(hw_params) << OXYGEN_REC_FORMAT_A_SHIFT,
+ OXYGEN_REC_FORMAT_A_MASK);
+ oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT,
+ oxygen_rate(hw_params) |
+ oxygen_i2s_mclk(hw_params) |
+ chip->model->adc_i2s_format |
+ oxygen_i2s_bits(hw_params),
+ OXYGEN_I2S_RATE_MASK |
+ OXYGEN_I2S_FORMAT_MASK |
+ OXYGEN_I2S_MCLK_MASK |
+ OXYGEN_I2S_BITS_MASK);
+ spin_unlock_irq(&chip->reg_lock);
+
+ mutex_lock(&chip->mutex);
+ chip->model->set_adc_params(chip, hw_params);
+ mutex_unlock(&chip->mutex);
+ return 0;
+}
+
+static int oxygen_rec_b_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct oxygen *chip = snd_pcm_substream_chip(substream);
+ int is_ac97;
+ int err;
+
+ err = oxygen_hw_params(substream, hw_params);
+ if (err < 0)
+ return err;
+
+ is_ac97 = chip->has_ac97_1 &&
+ (chip->model->used_channels & OXYGEN_CHANNEL_AC97);
+
+ spin_lock_irq(&chip->reg_lock);
+ oxygen_write8_masked(chip, OXYGEN_REC_FORMAT,
+ oxygen_format(hw_params) << OXYGEN_REC_FORMAT_B_SHIFT,
+ OXYGEN_REC_FORMAT_B_MASK);
+ if (!is_ac97)
+ oxygen_write16_masked(chip, OXYGEN_I2S_B_FORMAT,
+ oxygen_rate(hw_params) |
+ oxygen_i2s_mclk(hw_params) |
+ chip->model->adc_i2s_format |
+ oxygen_i2s_bits(hw_params),
+ OXYGEN_I2S_RATE_MASK |
+ OXYGEN_I2S_FORMAT_MASK |
+ OXYGEN_I2S_MCLK_MASK |
+ OXYGEN_I2S_BITS_MASK);
+ spin_unlock_irq(&chip->reg_lock);
+
+ if (!is_ac97) {
+ mutex_lock(&chip->mutex);
+ chip->model->set_adc_params(chip, hw_params);
+ mutex_unlock(&chip->mutex);
+ }
+ return 0;
+}
+
+static int oxygen_rec_c_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct oxygen *chip = snd_pcm_substream_chip(substream);
+ int err;
+
+ err = oxygen_hw_params(substream, hw_params);
+ if (err < 0)
+ return err;
+
+ spin_lock_irq(&chip->reg_lock);
+ oxygen_write8_masked(chip, OXYGEN_REC_FORMAT,
+ oxygen_format(hw_params) << OXYGEN_REC_FORMAT_C_SHIFT,
+ OXYGEN_REC_FORMAT_C_MASK);
+ spin_unlock_irq(&chip->reg_lock);
+ return 0;
+}
+
+static int oxygen_spdif_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct oxygen *chip = snd_pcm_substream_chip(substream);
+ int err;
+
+ err = oxygen_hw_params(substream, hw_params);
+ if (err < 0)
+ return err;
+
+ spin_lock_irq(&chip->reg_lock);
+ oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL,
+ OXYGEN_SPDIF_OUT_ENABLE);
+ oxygen_write8_masked(chip, OXYGEN_PLAY_FORMAT,
+ oxygen_format(hw_params) << OXYGEN_SPDIF_FORMAT_SHIFT,
+ OXYGEN_SPDIF_FORMAT_MASK);
+ oxygen_write32_masked(chip, OXYGEN_SPDIF_CONTROL,
+ oxygen_rate(hw_params) << OXYGEN_SPDIF_OUT_RATE_SHIFT,
+ OXYGEN_SPDIF_OUT_RATE_MASK);
+ oxygen_update_spdif_source(chip);
+ spin_unlock_irq(&chip->reg_lock);
+ return 0;
+}
+
+static int oxygen_multich_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct oxygen *chip = snd_pcm_substream_chip(substream);
+ int err;
+
+ err = oxygen_hw_params(substream, hw_params);
+ if (err < 0)
+ return err;
+
+ spin_lock_irq(&chip->reg_lock);
+ oxygen_write8_masked(chip, OXYGEN_PLAY_CHANNELS,
+ oxygen_play_channels(hw_params),
+ OXYGEN_PLAY_CHANNELS_MASK);
+ oxygen_write8_masked(chip, OXYGEN_PLAY_FORMAT,
+ oxygen_format(hw_params) << OXYGEN_MULTICH_FORMAT_SHIFT,
+ OXYGEN_MULTICH_FORMAT_MASK);
+ oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT,
+ oxygen_rate(hw_params) |
+ chip->model->dac_i2s_format |
+ oxygen_i2s_bits(hw_params),
+ OXYGEN_I2S_RATE_MASK |
+ OXYGEN_I2S_FORMAT_MASK |
+ OXYGEN_I2S_BITS_MASK);
+ oxygen_update_dac_routing(chip);
+ oxygen_update_spdif_source(chip);
+ spin_unlock_irq(&chip->reg_lock);
+
+ mutex_lock(&chip->mutex);
+ chip->model->set_dac_params(chip, hw_params);
+ mutex_unlock(&chip->mutex);
+ return 0;
+}
+
+static int oxygen_hw_free(struct snd_pcm_substream *substream)
+{
+ struct oxygen *chip = snd_pcm_substream_chip(substream);
+ unsigned int channel = oxygen_substream_channel(substream);
+
+ spin_lock_irq(&chip->reg_lock);
+ chip->interrupt_mask &= ~(1 << channel);
+ oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
+ spin_unlock_irq(&chip->reg_lock);
+
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static int oxygen_spdif_hw_free(struct snd_pcm_substream *substream)
+{
+ struct oxygen *chip = snd_pcm_substream_chip(substream);
+
+ spin_lock_irq(&chip->reg_lock);
+ oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL,
+ OXYGEN_SPDIF_OUT_ENABLE);
+ spin_unlock_irq(&chip->reg_lock);
+ return oxygen_hw_free(substream);
+}
+
+static int oxygen_prepare(struct snd_pcm_substream *substream)
+{
+ struct oxygen *chip = snd_pcm_substream_chip(substream);
+ unsigned int channel = oxygen_substream_channel(substream);
+ unsigned int channel_mask = 1 << channel;
+
+ spin_lock_irq(&chip->reg_lock);
+ oxygen_set_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask);
+ oxygen_clear_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask);
+
+ chip->interrupt_mask |= channel_mask;
+ oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
+ spin_unlock_irq(&chip->reg_lock);
+ return 0;
+}
+
+static int oxygen_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct oxygen *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_substream *s;
+ unsigned int mask = 0;
+ int pausing;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_START:
+ pausing = 0;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ pausing = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_pcm_group_for_each_entry(s, substream) {
+ if (snd_pcm_substream_chip(s) == chip) {
+ mask |= 1 << oxygen_substream_channel(s);
+ snd_pcm_trigger_done(s, substream);
+ }
+ }
+
+ spin_lock(&chip->reg_lock);
+ if (!pausing) {
+ if (cmd == SNDRV_PCM_TRIGGER_START)
+ chip->pcm_running |= mask;
+ else
+ chip->pcm_running &= ~mask;
+ oxygen_write8(chip, OXYGEN_DMA_STATUS, chip->pcm_running);
+ } else {
+ if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)
+ oxygen_set_bits8(chip, OXYGEN_DMA_PAUSE, mask);
+ else
+ oxygen_clear_bits8(chip, OXYGEN_DMA_PAUSE, mask);
+ }
+ spin_unlock(&chip->reg_lock);
+ return 0;
+}
+
+static snd_pcm_uframes_t oxygen_pointer(struct snd_pcm_substream *substream)
+{
+ struct oxygen *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned int channel = oxygen_substream_channel(substream);
+ u32 curr_addr;
+
+ /* no spinlock, this read should be atomic */
+ curr_addr = oxygen_read32(chip, channel_base_registers[channel]);
+ return bytes_to_frames(runtime, curr_addr - (u32)runtime->dma_addr);
+}
+
+static struct snd_pcm_ops oxygen_rec_a_ops = {
+ .open = oxygen_rec_a_open,
+ .close = oxygen_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = oxygen_rec_a_hw_params,
+ .hw_free = oxygen_hw_free,
+ .prepare = oxygen_prepare,
+ .trigger = oxygen_trigger,
+ .pointer = oxygen_pointer,
+};
+
+static struct snd_pcm_ops oxygen_rec_b_ops = {
+ .open = oxygen_rec_b_open,
+ .close = oxygen_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = oxygen_rec_b_hw_params,
+ .hw_free = oxygen_hw_free,
+ .prepare = oxygen_prepare,
+ .trigger = oxygen_trigger,
+ .pointer = oxygen_pointer,
+};
+
+static struct snd_pcm_ops oxygen_rec_c_ops = {
+ .open = oxygen_rec_c_open,
+ .close = oxygen_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = oxygen_rec_c_hw_params,
+ .hw_free = oxygen_hw_free,
+ .prepare = oxygen_prepare,
+ .trigger = oxygen_trigger,
+ .pointer = oxygen_pointer,
+};
+
+static struct snd_pcm_ops oxygen_spdif_ops = {
+ .open = oxygen_spdif_open,
+ .close = oxygen_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = oxygen_spdif_hw_params,
+ .hw_free = oxygen_spdif_hw_free,
+ .prepare = oxygen_prepare,
+ .trigger = oxygen_trigger,
+ .pointer = oxygen_pointer,
+};
+
+static struct snd_pcm_ops oxygen_multich_ops = {
+ .open = oxygen_multich_open,
+ .close = oxygen_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = oxygen_multich_hw_params,
+ .hw_free = oxygen_hw_free,
+ .prepare = oxygen_prepare,
+ .trigger = oxygen_trigger,
+ .pointer = oxygen_pointer,
+};
+
+static struct snd_pcm_ops oxygen_ac97_ops = {
+ .open = oxygen_ac97_open,
+ .close = oxygen_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = oxygen_hw_params,
+ .hw_free = oxygen_hw_free,
+ .prepare = oxygen_prepare,
+ .trigger = oxygen_trigger,
+ .pointer = oxygen_pointer,
+};
+
+static void oxygen_pcm_free(struct snd_pcm *pcm)
+{
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int __devinit oxygen_pcm_init(struct oxygen *chip)
+{
+ struct snd_pcm *pcm;
+ int outs, ins;
+ int err;
+
+ outs = 1; /* OXYGEN_CHANNEL_MULTICH is always used */
+ ins = !!(chip->model->used_channels & (OXYGEN_CHANNEL_A |
+ OXYGEN_CHANNEL_B));
+ err = snd_pcm_new(chip->card, "Analog", 0, outs, ins, &pcm);
+ if (err < 0)
+ return err;
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &oxygen_multich_ops);
+ if (chip->model->used_channels & OXYGEN_CHANNEL_A)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &oxygen_rec_a_ops);
+ else if (chip->model->used_channels & OXYGEN_CHANNEL_B)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &oxygen_rec_b_ops);
+ pcm->private_data = chip;
+ pcm->private_free = oxygen_pcm_free;
+ strcpy(pcm->name, "Analog");
+ snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
+ SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci),
+ 512 * 1024, 2048 * 1024);
+ if (ins)
+ snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
+ SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci),
+ 128 * 1024, 256 * 1024);
+
+ outs = !!(chip->model->used_channels & OXYGEN_CHANNEL_SPDIF);
+ ins = !!(chip->model->used_channels & OXYGEN_CHANNEL_C);
+ if (outs | ins) {
+ err = snd_pcm_new(chip->card, "Digital", 1, outs, ins, &pcm);
+ if (err < 0)
+ return err;
+ if (outs)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &oxygen_spdif_ops);
+ if (ins)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &oxygen_rec_c_ops);
+ pcm->private_data = chip;
+ pcm->private_free = oxygen_pcm_free;
+ strcpy(pcm->name, "Digital");
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci),
+ 128 * 1024, 256 * 1024);
+ }
+
+ outs = chip->has_ac97_1 &&
+ (chip->model->used_channels & OXYGEN_CHANNEL_AC97);
+ ins = outs ||
+ (chip->model->used_channels & (OXYGEN_CHANNEL_A |
+ OXYGEN_CHANNEL_B))
+ == (OXYGEN_CHANNEL_A | OXYGEN_CHANNEL_B);
+ if (outs | ins) {
+ err = snd_pcm_new(chip->card, outs ? "AC97" : "Analog2",
+ 2, outs, ins, &pcm);
+ if (err < 0)
+ return err;
+ if (outs) {
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &oxygen_ac97_ops);
+ oxygen_write8_masked(chip, OXYGEN_REC_ROUTING,
+ OXYGEN_REC_B_ROUTE_AC97_1,
+ OXYGEN_REC_B_ROUTE_MASK);
+ }
+ if (ins)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &oxygen_rec_b_ops);
+ pcm->private_data = chip;
+ pcm->private_free = oxygen_pcm_free;
+ strcpy(pcm->name, outs ? "Front Panel" : "Analog 2");
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci),
+ 128 * 1024, 256 * 1024);
+ }
+ return 0;
+}
diff --git a/sound/pci/oxygen/oxygen_regs.h b/sound/pci/oxygen/oxygen_regs.h
new file mode 100644
index 00000000000..72de159d456
--- /dev/null
+++ b/sound/pci/oxygen/oxygen_regs.h
@@ -0,0 +1,453 @@
+#ifndef OXYGEN_REGS_H_INCLUDED
+#define OXYGEN_REGS_H_INCLUDED
+
+/* recording channel A */
+#define OXYGEN_DMA_A_ADDRESS 0x00 /* 32-bit base address */
+#define OXYGEN_DMA_A_COUNT 0x04 /* buffer counter (dwords) */
+#define OXYGEN_DMA_A_TCOUNT 0x06 /* interrupt counter (dwords) */
+
+/* recording channel B */
+#define OXYGEN_DMA_B_ADDRESS 0x08
+#define OXYGEN_DMA_B_COUNT 0x0c
+#define OXYGEN_DMA_B_TCOUNT 0x0e
+
+/* recording channel C */
+#define OXYGEN_DMA_C_ADDRESS 0x10
+#define OXYGEN_DMA_C_COUNT 0x14
+#define OXYGEN_DMA_C_TCOUNT 0x16
+
+/* SPDIF playback channel */
+#define OXYGEN_DMA_SPDIF_ADDRESS 0x18
+#define OXYGEN_DMA_SPDIF_COUNT 0x1c
+#define OXYGEN_DMA_SPDIF_TCOUNT 0x1e
+
+/* multichannel playback channel */
+#define OXYGEN_DMA_MULTICH_ADDRESS 0x20
+#define OXYGEN_DMA_MULTICH_COUNT 0x24 /* 24 bits */
+#define OXYGEN_DMA_MULTICH_TCOUNT 0x28 /* 24 bits */
+
+/* AC'97 (front panel) playback channel */
+#define OXYGEN_DMA_AC97_ADDRESS 0x30
+#define OXYGEN_DMA_AC97_COUNT 0x34
+#define OXYGEN_DMA_AC97_TCOUNT 0x36
+
+/* all registers 0x00..0x36 return current position on read */
+
+#define OXYGEN_DMA_STATUS 0x40 /* 1 = running, 0 = stop */
+#define OXYGEN_CHANNEL_A 0x01
+#define OXYGEN_CHANNEL_B 0x02
+#define OXYGEN_CHANNEL_C 0x04
+#define OXYGEN_CHANNEL_SPDIF 0x08
+#define OXYGEN_CHANNEL_MULTICH 0x10
+#define OXYGEN_CHANNEL_AC97 0x20
+
+#define OXYGEN_DMA_PAUSE 0x41 /* 1 = pause */
+/* OXYGEN_CHANNEL_* */
+
+#define OXYGEN_DMA_RESET 0x42
+/* OXYGEN_CHANNEL_* */
+
+#define OXYGEN_PLAY_CHANNELS 0x43
+#define OXYGEN_PLAY_CHANNELS_MASK 0x03
+#define OXYGEN_PLAY_CHANNELS_2 0x00
+#define OXYGEN_PLAY_CHANNELS_4 0x01
+#define OXYGEN_PLAY_CHANNELS_6 0x02
+#define OXYGEN_PLAY_CHANNELS_8 0x03
+#define OXYGEN_DMA_A_BURST_MASK 0x04
+#define OXYGEN_DMA_A_BURST_8 0x00 /* dwords */
+#define OXYGEN_DMA_A_BURST_16 0x04
+#define OXYGEN_DMA_MULTICH_BURST_MASK 0x08
+#define OXYGEN_DMA_MULTICH_BURST_8 0x00
+#define OXYGEN_DMA_MULTICH_BURST_16 0x08
+
+#define OXYGEN_INTERRUPT_MASK 0x44
+/* OXYGEN_CHANNEL_* */
+#define OXYGEN_INT_SPDIF_IN_DETECT 0x0100
+#define OXYGEN_INT_MCU 0x0200
+#define OXYGEN_INT_2WIRE 0x0400
+#define OXYGEN_INT_GPIO 0x0800
+#define OXYGEN_INT_MCB 0x2000
+#define OXYGEN_INT_AC97 0x4000
+
+#define OXYGEN_INTERRUPT_STATUS 0x46
+/* OXYGEN_CHANNEL_* amd OXYGEN_INT_* */
+#define OXYGEN_INT_MIDI 0x1000
+
+#define OXYGEN_MISC 0x48
+#define OXYGEN_MISC_WRITE_PCI_SUBID 0x01
+#define OXYGEN_MISC_LATENCY_3F 0x02
+#define OXYGEN_MISC_REC_C_FROM_SPDIF 0x04
+#define OXYGEN_MISC_REC_B_FROM_AC97 0x08
+#define OXYGEN_MISC_REC_A_FROM_MULTICH 0x10
+#define OXYGEN_MISC_PCI_MEM_W_1_CLOCK 0x20
+#define OXYGEN_MISC_MIDI 0x40
+#define OXYGEN_MISC_CRYSTAL_MASK 0x80
+#define OXYGEN_MISC_CRYSTAL_24576 0x00
+#define OXYGEN_MISC_CRYSTAL_27 0x80 /* MHz */
+
+#define OXYGEN_REC_FORMAT 0x4a
+#define OXYGEN_REC_FORMAT_A_MASK 0x03
+#define OXYGEN_REC_FORMAT_A_SHIFT 0
+#define OXYGEN_REC_FORMAT_B_MASK 0x0c
+#define OXYGEN_REC_FORMAT_B_SHIFT 2
+#define OXYGEN_REC_FORMAT_C_MASK 0x30
+#define OXYGEN_REC_FORMAT_C_SHIFT 4
+#define OXYGEN_FORMAT_16 0x00
+#define OXYGEN_FORMAT_24 0x01
+#define OXYGEN_FORMAT_32 0x02
+
+#define OXYGEN_PLAY_FORMAT 0x4b
+#define OXYGEN_SPDIF_FORMAT_MASK 0x03
+#define OXYGEN_SPDIF_FORMAT_SHIFT 0
+#define OXYGEN_MULTICH_FORMAT_MASK 0x0c
+#define OXYGEN_MULTICH_FORMAT_SHIFT 2
+/* OXYGEN_FORMAT_* */
+
+#define OXYGEN_REC_CHANNELS 0x4c
+#define OXYGEN_REC_CHANNELS_MASK 0x07
+#define OXYGEN_REC_CHANNELS_2_2_2 0x00 /* DMA A, B, C */
+#define OXYGEN_REC_CHANNELS_4_2_2 0x01
+#define OXYGEN_REC_CHANNELS_6_0_2 0x02
+#define OXYGEN_REC_CHANNELS_6_2_0 0x03
+#define OXYGEN_REC_CHANNELS_8_0_0 0x04
+
+#define OXYGEN_FUNCTION 0x50
+#define OXYGEN_FUNCTION_CLOCK_MASK 0x01
+#define OXYGEN_FUNCTION_CLOCK_PLL 0x00
+#define OXYGEN_FUNCTION_CLOCK_CRYSTAL 0x01
+#define OXYGEN_FUNCTION_RESET_CODEC 0x02
+#define OXYGEN_FUNCTION_RESET_POL 0x04
+#define OXYGEN_FUNCTION_PWDN 0x08
+#define OXYGEN_FUNCTION_PWDN_EN 0x10
+#define OXYGEN_FUNCTION_PWDN_POL 0x20
+#define OXYGEN_FUNCTION_2WIRE_SPI_MASK 0x40
+#define OXYGEN_FUNCTION_SPI 0x00
+#define OXYGEN_FUNCTION_2WIRE 0x40
+#define OXYGEN_FUNCTION_ENABLE_SPI_4_5 0x80 /* 0 = EEPROM */
+
+#define OXYGEN_I2S_MULTICH_FORMAT 0x60
+#define OXYGEN_I2S_RATE_MASK 0x0007 /* LRCK */
+#define OXYGEN_RATE_32000 0x0000
+#define OXYGEN_RATE_44100 0x0001
+#define OXYGEN_RATE_48000 0x0002
+#define OXYGEN_RATE_64000 0x0003
+#define OXYGEN_RATE_88200 0x0004
+#define OXYGEN_RATE_96000 0x0005
+#define OXYGEN_RATE_176400 0x0006
+#define OXYGEN_RATE_192000 0x0007
+#define OXYGEN_I2S_FORMAT_MASK 0x0008
+#define OXYGEN_I2S_FORMAT_I2S 0x0000
+#define OXYGEN_I2S_FORMAT_LJUST 0x0008
+#define OXYGEN_I2S_MCLK_MASK 0x0030 /* MCLK/LRCK */
+#define OXYGEN_I2S_MCLK_128 0x0000
+#define OXYGEN_I2S_MCLK_256 0x0010
+#define OXYGEN_I2S_MCLK_512 0x0020
+#define OXYGEN_I2S_BITS_MASK 0x00c0
+#define OXYGEN_I2S_BITS_16 0x0000
+#define OXYGEN_I2S_BITS_20 0x0040
+#define OXYGEN_I2S_BITS_24 0x0080
+#define OXYGEN_I2S_BITS_32 0x00c0
+#define OXYGEN_I2S_MASTER 0x0100
+#define OXYGEN_I2S_BCLK_MASK 0x0600 /* BCLK/LRCK */
+#define OXYGEN_I2S_BCLK_64 0x0000
+#define OXYGEN_I2S_BCLK_128 0x0200
+#define OXYGEN_I2S_BCLK_256 0x0400
+#define OXYGEN_I2S_MUTE_MCLK 0x0800
+
+#define OXYGEN_I2S_A_FORMAT 0x62
+#define OXYGEN_I2S_B_FORMAT 0x64
+#define OXYGEN_I2S_C_FORMAT 0x66
+/* like OXYGEN_I2S_MULTICH_FORMAT */
+
+#define OXYGEN_SPDIF_CONTROL 0x70
+#define OXYGEN_SPDIF_OUT_ENABLE 0x00000002
+#define OXYGEN_SPDIF_LOOPBACK 0x00000004 /* in to out */
+#define OXYGEN_SPDIF_SENSE_MASK 0x00000008
+#define OXYGEN_SPDIF_LOCK_MASK 0x00000010
+#define OXYGEN_SPDIF_RATE_MASK 0x00000020
+#define OXYGEN_SPDIF_SPDVALID 0x00000040
+#define OXYGEN_SPDIF_SENSE_PAR 0x00000200
+#define OXYGEN_SPDIF_LOCK_PAR 0x00000400
+#define OXYGEN_SPDIF_SENSE_STATUS 0x00000800
+#define OXYGEN_SPDIF_LOCK_STATUS 0x00001000
+#define OXYGEN_SPDIF_SENSE_INT 0x00002000 /* r/wc */
+#define OXYGEN_SPDIF_LOCK_INT 0x00004000 /* r/wc */
+#define OXYGEN_SPDIF_RATE_INT 0x00008000 /* r/wc */
+#define OXYGEN_SPDIF_IN_CLOCK_MASK 0x00010000
+#define OXYGEN_SPDIF_IN_CLOCK_96 0x00000000 /* <= 96 kHz */
+#define OXYGEN_SPDIF_IN_CLOCK_192 0x00010000 /* > 96 kHz */
+#define OXYGEN_SPDIF_OUT_RATE_MASK 0x07000000
+#define OXYGEN_SPDIF_OUT_RATE_SHIFT 24
+/* OXYGEN_RATE_* << OXYGEN_SPDIF_OUT_RATE_SHIFT */
+
+#define OXYGEN_SPDIF_OUTPUT_BITS 0x74
+#define OXYGEN_SPDIF_NONAUDIO 0x00000002
+#define OXYGEN_SPDIF_C 0x00000004
+#define OXYGEN_SPDIF_PREEMPHASIS 0x00000008
+#define OXYGEN_SPDIF_CATEGORY_MASK 0x000007f0
+#define OXYGEN_SPDIF_CATEGORY_SHIFT 4
+#define OXYGEN_SPDIF_ORIGINAL 0x00000800
+#define OXYGEN_SPDIF_CS_RATE_MASK 0x0000f000
+#define OXYGEN_SPDIF_CS_RATE_SHIFT 12
+#define OXYGEN_SPDIF_V 0x00010000 /* 0 = valid */
+
+#define OXYGEN_SPDIF_INPUT_BITS 0x78
+/* 32 bits, IEC958_AES_* */
+
+#define OXYGEN_EEPROM_CONTROL 0x80
+#define OXYGEN_EEPROM_ADDRESS_MASK 0x7f
+#define OXYGEN_EEPROM_DIR_MASK 0x80
+#define OXYGEN_EEPROM_DIR_READ 0x00
+#define OXYGEN_EEPROM_DIR_WRITE 0x80
+
+#define OXYGEN_EEPROM_STATUS 0x81
+#define OXYGEN_EEPROM_VALID 0x40
+#define OXYGEN_EEPROM_BUSY 0x80
+
+#define OXYGEN_EEPROM_DATA 0x82 /* 16 bits */
+
+#define OXYGEN_2WIRE_CONTROL 0x90
+#define OXYGEN_2WIRE_DIR_MASK 0x01
+#define OXYGEN_2WIRE_DIR_WRITE 0x00
+#define OXYGEN_2WIRE_DIR_READ 0x01
+#define OXYGEN_2WIRE_ADDRESS_MASK 0xfe /* slave device address */
+#define OXYGEN_2WIRE_ADDRESS_SHIFT 1
+
+#define OXYGEN_2WIRE_MAP 0x91 /* address, 8 bits */
+#define OXYGEN_2WIRE_DATA 0x92 /* data, 16 bits */
+
+#define OXYGEN_2WIRE_BUS_STATUS 0x94
+#define OXYGEN_2WIRE_BUSY 0x0001
+#define OXYGEN_2WIRE_LENGTH_MASK 0x0002
+#define OXYGEN_2WIRE_LENGTH_8 0x0000
+#define OXYGEN_2WIRE_LENGTH_16 0x0002
+#define OXYGEN_2WIRE_MANUAL_READ 0x0004 /* 0 = auto read */
+#define OXYGEN_2WIRE_WRITE_MAP_ONLY 0x0008
+#define OXYGEN_2WIRE_SLAVE_AD_MASK 0x0030 /* AD0, AD1 */
+#define OXYGEN_2WIRE_INTERRUPT_MASK 0x0040 /* 0 = int. if not responding */
+#define OXYGEN_2WIRE_SLAVE_NO_RESPONSE 0x0080
+#define OXYGEN_2WIRE_SPEED_MASK 0x0100
+#define OXYGEN_2WIRE_SPEED_STANDARD 0x0000
+#define OXYGEN_2WIRE_SPEED_FAST 0x0100
+#define OXYGEN_2WIRE_CLOCK_SYNC 0x0200
+#define OXYGEN_2WIRE_BUS_RESET 0x0400
+
+#define OXYGEN_SPI_CONTROL 0x98
+#define OXYGEN_SPI_BUSY 0x01 /* read */
+#define OXYGEN_SPI_TRIGGER 0x01 /* write */
+#define OXYGEN_SPI_DATA_LENGTH_MASK 0x02
+#define OXYGEN_SPI_DATA_LENGTH_2 0x00
+#define OXYGEN_SPI_DATA_LENGTH_3 0x02
+#define OXYGEN_SPI_CLOCK_MASK 0xc0
+#define OXYGEN_SPI_CLOCK_160 0x00 /* ns */
+#define OXYGEN_SPI_CLOCK_320 0x40
+#define OXYGEN_SPI_CLOCK_640 0x80
+#define OXYGEN_SPI_CLOCK_1280 0xc0
+#define OXYGEN_SPI_CODEC_MASK 0x70 /* 0..5 */
+#define OXYGEN_SPI_CODEC_SHIFT 4
+#define OXYGEN_SPI_CEN_MASK 0x80
+#define OXYGEN_SPI_CEN_LATCH_CLOCK_LO 0x00
+#define OXYGEN_SPI_CEN_LATCH_CLOCK_HI 0x80
+
+#define OXYGEN_SPI_DATA1 0x99
+#define OXYGEN_SPI_DATA2 0x9a
+#define OXYGEN_SPI_DATA3 0x9b
+
+#define OXYGEN_MPU401 0xa0
+
+#define OXYGEN_MPU401_CONTROL 0xa2
+#define OXYGEN_MPU401_LOOPBACK 0x01 /* TXD to RXD */
+
+#define OXYGEN_GPI_DATA 0xa4
+/* bits 0..5 = pin XGPI0..XGPI5 */
+
+#define OXYGEN_GPI_INTERRUPT_MASK 0xa5
+/* bits 0..5, 1 = enable */
+
+#define OXYGEN_GPIO_DATA 0xa6
+/* bits 0..9 */
+
+#define OXYGEN_GPIO_CONTROL 0xa8
+/* bits 0..9, 0 = input, 1 = output */
+#define OXYGEN_GPIO1_XSLAVE_RDY 0x8000
+
+#define OXYGEN_GPIO_INTERRUPT_MASK 0xaa
+/* bits 0..9, 1 = enable */
+
+#define OXYGEN_DEVICE_SENSE 0xac
+#define OXYGEN_HEAD_PHONE_DETECT 0x01
+#define OXYGEN_HEAD_PHONE_MASK 0x06
+#define OXYGEN_HEAD_PHONE_PASSIVE_SPK 0x00
+#define OXYGEN_HEAD_PHONE_HP 0x02
+#define OXYGEN_HEAD_PHONE_ACTIVE_SPK 0x04
+
+#define OXYGEN_MCU_2WIRE_DATA 0xb0
+
+#define OXYGEN_MCU_2WIRE_MAP 0xb2
+
+#define OXYGEN_MCU_2WIRE_STATUS 0xb3
+#define OXYGEN_MCU_2WIRE_BUSY 0x01
+#define OXYGEN_MCU_2WIRE_LENGTH_MASK 0x06
+#define OXYGEN_MCU_2WIRE_LENGTH_1 0x00
+#define OXYGEN_MCU_2WIRE_LENGTH_2 0x02
+#define OXYGEN_MCU_2WIRE_LENGTH_3 0x04
+#define OXYGEN_MCU_2WIRE_WRITE 0x08 /* r/wc */
+#define OXYGEN_MCU_2WIRE_READ 0x10 /* r/wc */
+#define OXYGEN_MCU_2WIRE_DRV_XACT_FAIL 0x20 /* r/wc */
+#define OXYGEN_MCU_2WIRE_RESET 0x40
+
+#define OXYGEN_MCU_2WIRE_CONTROL 0xb4
+#define OXYGEN_MCU_2WIRE_DRV_ACK 0x01
+#define OXYGEN_MCU_2WIRE_DRV_XACT 0x02
+#define OXYGEN_MCU_2WIRE_INT_MASK 0x04
+#define OXYGEN_MCU_2WIRE_SYNC_MASK 0x08
+#define OXYGEN_MCU_2WIRE_SYNC_RDY_PIN 0x00
+#define OXYGEN_MCU_2WIRE_SYNC_DATA 0x08
+#define OXYGEN_MCU_2WIRE_ADDRESS_MASK 0x30
+#define OXYGEN_MCU_2WIRE_ADDRESS_10 0x00
+#define OXYGEN_MCU_2WIRE_ADDRESS_12 0x10
+#define OXYGEN_MCU_2WIRE_ADDRESS_14 0x20
+#define OXYGEN_MCU_2WIRE_ADDRESS_16 0x30
+#define OXYGEN_MCU_2WIRE_INT_POL 0x40
+#define OXYGEN_MCU_2WIRE_SYNC_ENABLE 0x80
+
+#define OXYGEN_PLAY_ROUTING 0xc0
+#define OXYGEN_PLAY_MUTE01 0x0001
+#define OXYGEN_PLAY_MUTE23 0x0002
+#define OXYGEN_PLAY_MUTE45 0x0004
+#define OXYGEN_PLAY_MUTE67 0x0008
+#define OXYGEN_PLAY_MULTICH_MASK 0x0010
+#define OXYGEN_PLAY_MULTICH_I2S_DAC 0x0000
+#define OXYGEN_PLAY_MULTICH_AC97 0x0010
+#define OXYGEN_PLAY_SPDIF_MASK 0x00e0
+#define OXYGEN_PLAY_SPDIF_SPDIF 0x0000
+#define OXYGEN_PLAY_SPDIF_MULTICH_01 0x0020
+#define OXYGEN_PLAY_SPDIF_MULTICH_23 0x0040
+#define OXYGEN_PLAY_SPDIF_MULTICH_45 0x0060
+#define OXYGEN_PLAY_SPDIF_MULTICH_67 0x0080
+#define OXYGEN_PLAY_SPDIF_REC_A 0x00a0
+#define OXYGEN_PLAY_SPDIF_REC_B 0x00c0
+#define OXYGEN_PLAY_SPDIF_I2S_ADC_3 0x00e0
+#define OXYGEN_PLAY_DAC0_SOURCE_MASK 0x0300
+#define OXYGEN_PLAY_DAC0_SOURCE_SHIFT 8
+#define OXYGEN_PLAY_DAC1_SOURCE_MASK 0x0c00
+#define OXYGEN_PLAY_DAC1_SOURCE_SHIFT 10
+#define OXYGEN_PLAY_DAC2_SOURCE_MASK 0x3000
+#define OXYGEN_PLAY_DAC2_SOURCE_SHIFT 12
+#define OXYGEN_PLAY_DAC3_SOURCE_MASK 0xc000
+#define OXYGEN_PLAY_DAC3_SOURCE_SHIFT 14
+
+#define OXYGEN_REC_ROUTING 0xc2
+#define OXYGEN_MUTE_I2S_ADC_1 0x01
+#define OXYGEN_MUTE_I2S_ADC_2 0x02
+#define OXYGEN_MUTE_I2S_ADC_3 0x04
+#define OXYGEN_REC_A_ROUTE_MASK 0x08
+#define OXYGEN_REC_A_ROUTE_I2S_ADC_1 0x00
+#define OXYGEN_REC_A_ROUTE_AC97_0 0x08
+#define OXYGEN_REC_B_ROUTE_MASK 0x10
+#define OXYGEN_REC_B_ROUTE_I2S_ADC_2 0x00
+#define OXYGEN_REC_B_ROUTE_AC97_1 0x10
+#define OXYGEN_REC_C_ROUTE_MASK 0x20
+#define OXYGEN_REC_C_ROUTE_SPDIF 0x00
+#define OXYGEN_REC_C_ROUTE_I2S_ADC_3 0x20
+
+#define OXYGEN_ADC_MONITOR 0xc3
+#define OXYGEN_ADC_MONITOR_A 0x01
+#define OXYGEN_ADC_MONITOR_A_HALF_VOL 0x02
+#define OXYGEN_ADC_MONITOR_B 0x04
+#define OXYGEN_ADC_MONITOR_B_HALF_VOL 0x08
+#define OXYGEN_ADC_MONITOR_C 0x10
+#define OXYGEN_ADC_MONITOR_C_HALF_VOL 0x20
+
+#define OXYGEN_A_MONITOR_ROUTING 0xc4
+#define OXYGEN_A_MONITOR_ROUTE_0_MASK 0x03
+#define OXYGEN_A_MONITOR_ROUTE_0_SHIFT 0
+#define OXYGEN_A_MONITOR_ROUTE_1_MASK 0x0c
+#define OXYGEN_A_MONITOR_ROUTE_1_SHIFT 2
+#define OXYGEN_A_MONITOR_ROUTE_2_MASK 0x30
+#define OXYGEN_A_MONITOR_ROUTE_2_SHIFT 4
+#define OXYGEN_A_MONITOR_ROUTE_3_MASK 0xc0
+#define OXYGEN_A_MONITOR_ROUTE_3_SHIFT 6
+
+#define OXYGEN_AC97_CONTROL 0xd0
+#define OXYGEN_AC97_COLD_RESET 0x0001
+#define OXYGEN_AC97_SUSPENDED 0x0002 /* read */
+#define OXYGEN_AC97_RESUME 0x0002 /* write */
+#define OXYGEN_AC97_CLOCK_DISABLE 0x0004
+#define OXYGEN_AC97_NO_CODEC_0 0x0008
+#define OXYGEN_AC97_CODEC_0 0x0010
+#define OXYGEN_AC97_CODEC_1 0x0020
+
+#define OXYGEN_AC97_INTERRUPT_MASK 0xd2
+#define OXYGEN_AC97_INT_READ_DONE 0x01
+#define OXYGEN_AC97_INT_WRITE_DONE 0x02
+#define OXYGEN_AC97_INT_CODEC_0 0x10
+#define OXYGEN_AC97_INT_CODEC_1 0x20
+
+#define OXYGEN_AC97_INTERRUPT_STATUS 0xd3
+/* OXYGEN_AC97_INT_* */
+
+#define OXYGEN_AC97_OUT_CONFIG 0xd4
+#define OXYGEN_AC97_CODEC1_SLOT3 0x00000001
+#define OXYGEN_AC97_CODEC1_SLOT3_VSR 0x00000002
+#define OXYGEN_AC97_CODEC1_SLOT4 0x00000010
+#define OXYGEN_AC97_CODEC1_SLOT4_VSR 0x00000020
+#define OXYGEN_AC97_CODEC0_FRONTL 0x00000100
+#define OXYGEN_AC97_CODEC0_FRONTR 0x00000200
+#define OXYGEN_AC97_CODEC0_SIDEL 0x00000400
+#define OXYGEN_AC97_CODEC0_SIDER 0x00000800
+#define OXYGEN_AC97_CODEC0_CENTER 0x00001000
+#define OXYGEN_AC97_CODEC0_BASE 0x00002000
+#define OXYGEN_AC97_CODEC0_REARL 0x00004000
+#define OXYGEN_AC97_CODEC0_REARR 0x00008000
+
+#define OXYGEN_AC97_IN_CONFIG 0xd8
+#define OXYGEN_AC97_CODEC1_LINEL 0x00000001
+#define OXYGEN_AC97_CODEC1_LINEL_VSR 0x00000002
+#define OXYGEN_AC97_CODEC1_LINEL_16 0x00000000
+#define OXYGEN_AC97_CODEC1_LINEL_18 0x00000004
+#define OXYGEN_AC97_CODEC1_LINEL_20 0x00000008
+#define OXYGEN_AC97_CODEC1_LINER 0x00000010
+#define OXYGEN_AC97_CODEC1_LINER_VSR 0x00000020
+#define OXYGEN_AC97_CODEC1_LINER_16 0x00000000
+#define OXYGEN_AC97_CODEC1_LINER_18 0x00000040
+#define OXYGEN_AC97_CODEC1_LINER_20 0x00000080
+#define OXYGEN_AC97_CODEC0_LINEL 0x00000100
+#define OXYGEN_AC97_CODEC0_LINER 0x00000200
+
+#define OXYGEN_AC97_REGS 0xdc
+#define OXYGEN_AC97_REG_DATA_MASK 0x0000ffff
+#define OXYGEN_AC97_REG_ADDR_MASK 0x007f0000
+#define OXYGEN_AC97_REG_ADDR_SHIFT 16
+#define OXYGEN_AC97_REG_DIR_MASK 0x00800000
+#define OXYGEN_AC97_REG_DIR_WRITE 0x00000000
+#define OXYGEN_AC97_REG_DIR_READ 0x00800000
+#define OXYGEN_AC97_REG_CODEC_MASK 0x01000000
+#define OXYGEN_AC97_REG_CODEC_SHIFT 24
+
+#define OXYGEN_TEST 0xe0
+#define OXYGEN_TEST_RAM_SUCCEEDED 0x01
+#define OXYGEN_TEST_PLAYBACK_RAM 0x02
+#define OXYGEN_TEST_RECORD_RAM 0x04
+#define OXYGEN_TEST_PLL 0x08
+#define OXYGEN_TEST_2WIRE_LOOPBACK 0x10
+
+#define OXYGEN_DMA_FLUSH 0xe1
+/* OXYGEN_CHANNEL_* */
+
+#define OXYGEN_CODEC_VERSION 0xe4
+#define OXYGEN_XCID_MASK 0x07
+
+#define OXYGEN_REVISION 0xe6
+#define OXYGEN_REVISION_XPKGID_MASK 0x0007
+#define OXYGEN_REVISION_MASK 0xfff8
+#define OXYGEN_REVISION_2 0x0008 /* bit flag */
+#define OXYGEN_REVISION_8787 0x0014 /* 8 bits */
+
+#define OXYGEN_OFFSIN_48K 0xe8
+#define OXYGEN_OFFSBASE_48K 0xe9
+#define OXYGEN_OFFSBASE_MASK 0x0fff
+#define OXYGEN_OFFSIN_44K 0xec
+#define OXYGEN_OFFSBASE_44K 0xed
+
+#endif
diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c
new file mode 100644
index 00000000000..40e92f5cd69
--- /dev/null
+++ b/sound/pci/oxygen/virtuoso.c
@@ -0,0 +1,449 @@
+/*
+ * C-Media CMI8788 driver for Asus Xonar cards
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ *
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2.
+ *
+ * This driver 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 driver; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * CMI8788:
+ *
+ * SPI 0 -> 1st PCM1796 (front)
+ * SPI 1 -> 2nd PCM1796 (surround)
+ * SPI 2 -> 3rd PCM1796 (center/LFE)
+ * SPI 4 -> 4th PCM1796 (back)
+ *
+ * GPIO 2 -> M0 of CS5381
+ * GPIO 3 -> M1 of CS5381
+ * GPIO 5 <- external power present (D2X only)
+ * GPIO 7 -> ALT
+ * GPIO 8 -> enable output to speakers
+ *
+ * CM9780:
+ *
+ * GPIO 0 -> enable AC'97 bypass (line in -> ADC)
+ */
+
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <sound/ac97_codec.h>
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/tlv.h>
+#include "oxygen.h"
+#include "cm9780.h"
+
+MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
+MODULE_DESCRIPTION("Asus AV200 driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Asus,AV200}}");
+
+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, "card index");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "enable card");
+
+static struct pci_device_id xonar_ids[] __devinitdata = {
+ { OXYGEN_PCI_SUBID(0x1043, 0x8269) }, /* Asus Xonar D2 */
+ { OXYGEN_PCI_SUBID(0x1043, 0x82b7) }, /* Asus Xonar D2X */
+ { }
+};
+MODULE_DEVICE_TABLE(pci, xonar_ids);
+
+
+#define GPIO_CS5381_M_MASK 0x000c
+#define GPIO_CS5381_M_SINGLE 0x0000
+#define GPIO_CS5381_M_DOUBLE 0x0004
+#define GPIO_CS5381_M_QUAD 0x0008
+#define GPIO_EXT_POWER 0x0020
+#define GPIO_ALT 0x0080
+#define GPIO_OUTPUT_ENABLE 0x0100
+
+#define GPIO_LINE_MUTE CM9780_GPO0
+
+/* register 16 */
+#define PCM1796_ATL_MASK 0xff
+/* register 17 */
+#define PCM1796_ATR_MASK 0xff
+/* register 18 */
+#define PCM1796_MUTE 0x01
+#define PCM1796_DME 0x02
+#define PCM1796_DMF_MASK 0x0c
+#define PCM1796_DMF_DISABLED 0x00
+#define PCM1796_DMF_48 0x04
+#define PCM1796_DMF_441 0x08
+#define PCM1796_DMF_32 0x0c
+#define PCM1796_FMT_MASK 0x70
+#define PCM1796_FMT_16_RJUST 0x00
+#define PCM1796_FMT_20_RJUST 0x10
+#define PCM1796_FMT_24_RJUST 0x20
+#define PCM1796_FMT_24_LJUST 0x30
+#define PCM1796_FMT_16_I2S 0x40
+#define PCM1796_FMT_24_I2S 0x50
+#define PCM1796_ATLD 0x80
+/* register 19 */
+#define PCM1796_INZD 0x01
+#define PCM1796_FLT_MASK 0x02
+#define PCM1796_FLT_SHARP 0x00
+#define PCM1796_FLT_SLOW 0x02
+#define PCM1796_DFMS 0x04
+#define PCM1796_OPE 0x10
+#define PCM1796_ATS_MASK 0x60
+#define PCM1796_ATS_1 0x00
+#define PCM1796_ATS_2 0x20
+#define PCM1796_ATS_4 0x40
+#define PCM1796_ATS_8 0x60
+#define PCM1796_REV 0x80
+/* register 20 */
+#define PCM1796_OS_MASK 0x03
+#define PCM1796_OS_64 0x00
+#define PCM1796_OS_32 0x01
+#define PCM1796_OS_128 0x02
+#define PCM1796_CHSL_MASK 0x04
+#define PCM1796_CHSL_LEFT 0x00
+#define PCM1796_CHSL_RIGHT 0x04
+#define PCM1796_MONO 0x08
+#define PCM1796_DFTH 0x10
+#define PCM1796_DSD 0x20
+#define PCM1796_SRST 0x40
+/* register 21 */
+#define PCM1796_PCMZ 0x01
+#define PCM1796_DZ_MASK 0x06
+/* register 22 */
+#define PCM1796_ZFGL 0x01
+#define PCM1796_ZFGR 0x02
+/* register 23 */
+#define PCM1796_ID_MASK 0x1f
+
+struct xonar_data {
+ u8 is_d2x;
+ u8 has_power;
+};
+
+static void pcm1796_write(struct oxygen *chip, unsigned int codec,
+ u8 reg, u8 value)
+{
+ /* maps ALSA channel pair number to SPI output */
+ static const u8 codec_map[4] = {
+ 0, 1, 2, 4
+ };
+ oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
+ OXYGEN_SPI_DATA_LENGTH_2 |
+ OXYGEN_SPI_CLOCK_160 |
+ (codec_map[codec] << OXYGEN_SPI_CODEC_SHIFT) |
+ OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
+ (reg << 8) | value);
+}
+
+static void xonar_init(struct oxygen *chip)
+{
+ struct xonar_data *data = chip->model_data;
+ unsigned int i;
+
+ data->is_d2x = chip->pci->subsystem_device == 0x82b7;
+
+ for (i = 0; i < 4; ++i) {
+ pcm1796_write(chip, i, 18, PCM1796_FMT_24_LJUST | PCM1796_ATLD);
+ pcm1796_write(chip, i, 19, PCM1796_FLT_SHARP | PCM1796_ATS_1);
+ pcm1796_write(chip, i, 20, PCM1796_OS_64);
+ pcm1796_write(chip, i, 21, 0);
+ pcm1796_write(chip, i, 16, 0xff); /* set ATL/ATR after ATLD */
+ pcm1796_write(chip, i, 17, 0xff);
+ }
+
+ oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
+ GPIO_CS5381_M_MASK | GPIO_ALT);
+ oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
+ GPIO_CS5381_M_SINGLE,
+ GPIO_CS5381_M_MASK | GPIO_ALT);
+ if (data->is_d2x) {
+ oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
+ GPIO_EXT_POWER);
+ oxygen_set_bits16(chip, OXYGEN_GPIO_INTERRUPT_MASK,
+ GPIO_EXT_POWER);
+ chip->interrupt_mask |= OXYGEN_INT_GPIO;
+ data->has_power = !!(oxygen_read16(chip, OXYGEN_GPIO_DATA)
+ & GPIO_EXT_POWER);
+ }
+ oxygen_ac97_set_bits(chip, 0, CM9780_JACK, CM9780_FMIC2MIC);
+ oxygen_ac97_clear_bits(chip, 0, CM9780_GPIO_STATUS, GPIO_LINE_MUTE);
+ msleep(300);
+ oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_OUTPUT_ENABLE);
+ oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
+
+ snd_component_add(chip->card, "PCM1796");
+ snd_component_add(chip->card, "CS5381");
+}
+
+static void xonar_cleanup(struct oxygen *chip)
+{
+ oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
+}
+
+static void set_pcm1796_params(struct oxygen *chip,
+ struct snd_pcm_hw_params *params)
+{
+#if 0
+ unsigned int i;
+ u8 value;
+
+ value = params_rate(params) >= 96000 ? PCM1796_OS_32 : PCM1796_OS_64;
+ for (i = 0; i < 4; ++i)
+ pcm1796_write(chip, i, 20, value);
+#endif
+}
+
+static void update_pcm1796_volume(struct oxygen *chip)
+{
+ unsigned int i;
+
+ for (i = 0; i < 4; ++i) {
+ pcm1796_write(chip, i, 16, chip->dac_volume[i * 2]);
+ pcm1796_write(chip, i, 17, chip->dac_volume[i * 2 + 1]);
+ }
+}
+
+static void update_pcm1796_mute(struct oxygen *chip)
+{
+ unsigned int i;
+ u8 value;
+
+ value = PCM1796_FMT_24_LJUST | PCM1796_ATLD;
+ if (chip->dac_mute)
+ value |= PCM1796_MUTE;
+ for (i = 0; i < 4; ++i)
+ pcm1796_write(chip, i, 18, value);
+}
+
+static void set_cs5381_params(struct oxygen *chip,
+ struct snd_pcm_hw_params *params)
+{
+ unsigned int value;
+
+ if (params_rate(params) <= 54000)
+ value = GPIO_CS5381_M_SINGLE;
+ else if (params_rate(params) <= 108000)
+ value = GPIO_CS5381_M_DOUBLE;
+ else
+ value = GPIO_CS5381_M_QUAD;
+ oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
+ value, GPIO_CS5381_M_MASK);
+}
+
+static void xonar_gpio_changed(struct oxygen *chip)
+{
+ struct xonar_data *data = chip->model_data;
+ u8 has_power;
+
+ if (!data->is_d2x)
+ return;
+ has_power = !!(oxygen_read16(chip, OXYGEN_GPIO_DATA)
+ & GPIO_EXT_POWER);
+ if (has_power != data->has_power) {
+ data->has_power = has_power;
+ if (has_power) {
+ snd_printk(KERN_NOTICE "power restored\n");
+ } else {
+ snd_printk(KERN_CRIT
+ "Hey! Don't unplug the power cable!\n");
+ /* TODO: stop PCMs */
+ }
+ }
+}
+
+static void mute_ac97_ctl(struct oxygen *chip, unsigned int control)
+{
+ unsigned int index = chip->controls[control]->private_value & 0xff;
+ u16 value;
+
+ value = oxygen_read_ac97(chip, 0, index);
+ if (!(value & 0x8000)) {
+ oxygen_write_ac97(chip, 0, index, value | 0x8000);
+ snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &chip->controls[control]->id);
+ }
+}
+
+static void xonar_ac97_switch_hook(struct oxygen *chip, unsigned int codec,
+ unsigned int reg, int mute)
+{
+ if (codec != 0)
+ return;
+ /* line-in is exclusive */
+ switch (reg) {
+ case AC97_LINE:
+ oxygen_write_ac97_masked(chip, 0, CM9780_GPIO_STATUS,
+ mute ? GPIO_LINE_MUTE : 0,
+ GPIO_LINE_MUTE);
+ if (!mute) {
+ mute_ac97_ctl(chip, CONTROL_MIC_CAPTURE_SWITCH);
+ mute_ac97_ctl(chip, CONTROL_CD_CAPTURE_SWITCH);
+ mute_ac97_ctl(chip, CONTROL_AUX_CAPTURE_SWITCH);
+ }
+ break;
+ case AC97_MIC:
+ case AC97_CD:
+ case AC97_VIDEO:
+ case AC97_AUX:
+ if (!mute) {
+ oxygen_ac97_set_bits(chip, 0, CM9780_GPIO_STATUS,
+ GPIO_LINE_MUTE);
+ mute_ac97_ctl(chip, CONTROL_LINE_CAPTURE_SWITCH);
+ }
+ break;
+ }
+}
+
+static int pcm1796_volume_info(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_info *info)
+{
+ info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ info->count = 8;
+ info->value.integer.min = 0x0f;
+ info->value.integer.max = 0xff;
+ return 0;
+}
+
+static int alt_switch_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+
+ value->value.integer.value[0] =
+ !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_ALT);
+ return 0;
+}
+
+static int alt_switch_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ u16 old_bits, new_bits;
+ int changed;
+
+ spin_lock_irq(&chip->reg_lock);
+ old_bits = oxygen_read16(chip, OXYGEN_GPIO_DATA);
+ if (value->value.integer.value[0])
+ new_bits = old_bits | GPIO_ALT;
+ else
+ new_bits = old_bits & ~GPIO_ALT;
+ changed = new_bits != old_bits;
+ if (changed)
+ oxygen_write16(chip, OXYGEN_GPIO_DATA, new_bits);
+ spin_unlock_irq(&chip->reg_lock);
+ return changed;
+}
+
+static const struct snd_kcontrol_new alt_switch = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Loopback Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = alt_switch_get,
+ .put = alt_switch_put,
+};
+
+static const DECLARE_TLV_DB_SCALE(pcm1796_db_scale, -12000, 50, 0);
+
+static int xonar_control_filter(struct snd_kcontrol_new *template)
+{
+ if (!strcmp(template->name, "Master Playback Volume")) {
+ template->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+ template->info = pcm1796_volume_info,
+ template->tlv.p = pcm1796_db_scale;
+ } else if (!strncmp(template->name, "CD Capture ", 11)) {
+ /* CD in is actually connected to the video in pin */
+ template->private_value ^= AC97_CD ^ AC97_VIDEO;
+ } else if (!strcmp(template->name, "Line Capture Volume")) {
+ return 1; /* line-in bypasses the AC'97 mixer */
+ }
+ return 0;
+}
+
+static int xonar_mixer_init(struct oxygen *chip)
+{
+ return snd_ctl_add(chip->card, snd_ctl_new1(&alt_switch, chip));
+}
+
+static const struct oxygen_model model_xonar = {
+ .shortname = "Asus AV200",
+ .longname = "Asus Virtuoso 200",
+ .chip = "AV200",
+ .init = xonar_init,
+ .control_filter = xonar_control_filter,
+ .mixer_init = xonar_mixer_init,
+ .cleanup = xonar_cleanup,
+ .set_dac_params = set_pcm1796_params,
+ .set_adc_params = set_cs5381_params,
+ .update_dac_volume = update_pcm1796_volume,
+ .update_dac_mute = update_pcm1796_mute,
+ .ac97_switch_hook = xonar_ac97_switch_hook,
+ .gpio_changed = xonar_gpio_changed,
+ .model_data_size = sizeof(struct xonar_data),
+ .dac_channels = 8,
+ .used_channels = OXYGEN_CHANNEL_B |
+ OXYGEN_CHANNEL_C |
+ OXYGEN_CHANNEL_SPDIF |
+ OXYGEN_CHANNEL_MULTICH,
+ .function_flags = OXYGEN_FUNCTION_ENABLE_SPI_4_5,
+ .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+ .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+};
+
+static int __devinit xonar_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ int err;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ ++dev;
+ return -ENOENT;
+ }
+ err = oxygen_pci_probe(pci, index[dev], id[dev], 1, &model_xonar);
+ if (err >= 0)
+ ++dev;
+ return err;
+}
+
+static struct pci_driver xonar_driver = {
+ .name = "AV200",
+ .id_table = xonar_ids,
+ .probe = xonar_probe,
+ .remove = __devexit_p(oxygen_pci_remove),
+};
+
+static int __init alsa_card_xonar_init(void)
+{
+ return pci_register_driver(&xonar_driver);
+}
+
+static void __exit alsa_card_xonar_exit(void)
+{
+ pci_unregister_driver(&xonar_driver);
+}
+
+module_init(alsa_card_xonar_init)
+module_exit(alsa_card_xonar_exit)
diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c
index 2d618bd7e62..9d5bb76229a 100644
--- a/sound/pci/pcxhr/pcxhr.c
+++ b/sound/pci/pcxhr/pcxhr.c
@@ -21,7 +21,6 @@
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
diff --git a/sound/pci/pcxhr/pcxhr_core.c b/sound/pci/pcxhr/pcxhr_core.c
index 0ff8dc36fde..c4e415d0738 100644
--- a/sound/pci/pcxhr/pcxhr_core.c
+++ b/sound/pci/pcxhr/pcxhr_core.c
@@ -20,7 +20,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/pcxhr/pcxhr_hwdep.c b/sound/pci/pcxhr/pcxhr_hwdep.c
index d55d8bc90ee..e6a4bfbb91b 100644
--- a/sound/pci/pcxhr/pcxhr_hwdep.c
+++ b/sound/pci/pcxhr/pcxhr_hwdep.c
@@ -20,7 +20,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <sound/driver.h>
#include <linux/interrupt.h>
#include <linux/vmalloc.h>
#include <linux/firmware.h>
diff --git a/sound/pci/pcxhr/pcxhr_mixer.c b/sound/pci/pcxhr/pcxhr_mixer.c
index 5f8d42633b0..aabc7bc5321 100644
--- a/sound/pci/pcxhr/pcxhr_mixer.c
+++ b/sound/pci/pcxhr/pcxhr_mixer.c
@@ -21,7 +21,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <sound/driver.h>
#include <linux/time.h>
#include <linux/interrupt.h>
#include <linux/init.h>
@@ -120,8 +119,18 @@ static int pcxhr_analog_vol_put(struct snd_kcontrol *kcontrol,
is_capture = (kcontrol->private_value != 0);
for (i = 0; i < 2; i++) {
int new_volume = ucontrol->value.integer.value[i];
- int* stored_volume = is_capture ? &chip->analog_capture_volume[i] :
+ int *stored_volume = is_capture ?
+ &chip->analog_capture_volume[i] :
&chip->analog_playback_volume[i];
+ if (is_capture) {
+ if (new_volume < PCXHR_ANALOG_CAPTURE_LEVEL_MIN ||
+ new_volume > PCXHR_ANALOG_CAPTURE_LEVEL_MAX)
+ continue;
+ } else {
+ if (new_volume < PCXHR_ANALOG_PLAYBACK_LEVEL_MIN ||
+ new_volume > PCXHR_ANALOG_PLAYBACK_LEVEL_MAX)
+ continue;
+ }
if (*stored_volume != new_volume) {
*stored_volume = new_volume;
changed = 1;
@@ -165,10 +174,13 @@ static int pcxhr_audio_sw_put(struct snd_kcontrol *kcontrol,
int i, changed = 0;
mutex_lock(&chip->mgr->mixer_mutex);
for(i = 0; i < 2; i++) {
- if (chip->analog_playback_active[i] != ucontrol->value.integer.value[i]) {
- chip->analog_playback_active[i] = ucontrol->value.integer.value[i];
+ if (chip->analog_playback_active[i] !=
+ ucontrol->value.integer.value[i]) {
+ chip->analog_playback_active[i] =
+ !!ucontrol->value.integer.value[i];
changed = 1;
- pcxhr_update_analog_audio_level(chip, 0, i); /* update playback levels */
+ /* update playback levels */
+ pcxhr_update_analog_audio_level(chip, 0, i);
}
}
mutex_unlock(&chip->mgr->mixer_mutex);
@@ -323,20 +335,24 @@ static int pcxhr_pcm_vol_put(struct snd_kcontrol *kcontrol,
int i;
mutex_lock(&chip->mgr->mixer_mutex);
- if (is_capture)
- stored_volume = chip->digital_capture_volume; /* digital capture */
- else
- stored_volume = chip->digital_playback_volume[idx]; /* digital playback */
+ if (is_capture) /* digital capture */
+ stored_volume = chip->digital_capture_volume;
+ else /* digital playback */
+ stored_volume = chip->digital_playback_volume[idx];
for (i = 0; i < 2; i++) {
- if (stored_volume[i] != ucontrol->value.integer.value[i]) {
- stored_volume[i] = ucontrol->value.integer.value[i];
+ int vol = ucontrol->value.integer.value[i];
+ if (vol < PCXHR_DIGITAL_LEVEL_MIN ||
+ vol > PCXHR_DIGITAL_LEVEL_MAX)
+ continue;
+ if (stored_volume[i] != vol) {
+ stored_volume[i] = vol;
changed = 1;
if (is_capture) /* update capture volume */
pcxhr_update_audio_pipe_level(chip, 1, i);
}
}
- if (! is_capture && changed)
- pcxhr_update_playback_stream_level(chip, idx); /* update playback volume */
+ if (!is_capture && changed) /* update playback volume */
+ pcxhr_update_playback_stream_level(chip, idx);
mutex_unlock(&chip->mgr->mixer_mutex);
return changed;
}
@@ -378,8 +394,10 @@ static int pcxhr_pcm_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
mutex_lock(&chip->mgr->mixer_mutex);
j = idx;
for (i = 0; i < 2; i++) {
- if (chip->digital_playback_active[j][i] != ucontrol->value.integer.value[i]) {
- chip->digital_playback_active[j][i] = ucontrol->value.integer.value[i];
+ if (chip->digital_playback_active[j][i] !=
+ ucontrol->value.integer.value[i]) {
+ chip->digital_playback_active[j][i] =
+ !!ucontrol->value.integer.value[i];
changed = 1;
}
}
@@ -423,10 +441,13 @@ static int pcxhr_monitor_vol_put(struct snd_kcontrol *kcontrol,
mutex_lock(&chip->mgr->mixer_mutex);
for (i = 0; i < 2; i++) {
- if (chip->monitoring_volume[i] != ucontrol->value.integer.value[i]) {
- chip->monitoring_volume[i] = ucontrol->value.integer.value[i];
- if(chip->monitoring_active[i]) /* do only when monitoring is unmuted */
+ if (chip->monitoring_volume[i] !=
+ ucontrol->value.integer.value[i]) {
+ chip->monitoring_volume[i] =
+ !!ucontrol->value.integer.value[i];
+ if(chip->monitoring_active[i])
/* update monitoring volume and mute */
+ /* do only when monitoring is unmuted */
pcxhr_update_audio_pipe_level(chip, 0, i);
changed = 1;
}
@@ -470,15 +491,17 @@ static int pcxhr_monitor_sw_put(struct snd_kcontrol *kcontrol,
mutex_lock(&chip->mgr->mixer_mutex);
for (i = 0; i < 2; i++) {
- if (chip->monitoring_active[i] != ucontrol->value.integer.value[i]) {
- chip->monitoring_active[i] = ucontrol->value.integer.value[i];
+ if (chip->monitoring_active[i] !=
+ ucontrol->value.integer.value[i]) {
+ chip->monitoring_active[i] =
+ !!ucontrol->value.integer.value[i];
changed |= (1<<i); /* mask 0x01 and 0x02 */
}
}
- if(changed & 0x01)
+ if (changed & 0x01)
/* update left monitoring volume and mute */
pcxhr_update_audio_pipe_level(chip, 0, 0);
- if(changed & 0x02)
+ if (changed & 0x02)
/* update right monitoring volume and mute */
pcxhr_update_audio_pipe_level(chip, 0, 1);
@@ -579,6 +602,8 @@ static int pcxhr_audio_src_put(struct snd_kcontrol *kcontrol,
struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
int ret = 0;
+ if (ucontrol->value.enumerated.item[0] >= 3)
+ return -EINVAL;
mutex_lock(&chip->mgr->mixer_mutex);
if (chip->audio_capture_source != ucontrol->value.enumerated.item[0]) {
chip->audio_capture_source = ucontrol->value.enumerated.item[0];
@@ -642,8 +667,11 @@ static int pcxhr_clock_type_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct pcxhr_mgr *mgr = snd_kcontrol_chip(kcontrol);
+ unsigned int clock_items = 3 + mgr->capture_chips;
int rate, ret = 0;
+ if (ucontrol->value.enumerated.item[0] >= clock_items)
+ return -EINVAL;
mutex_lock(&mgr->mixer_mutex);
if (mgr->use_clock_type != ucontrol->value.enumerated.item[0]) {
mutex_lock(&mgr->setup_mutex);
diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c
index 8e5410483e6..9408b1eeec4 100644
--- a/sound/pci/riptide/riptide.c
+++ b/sound/pci/riptide/riptide.c
@@ -88,7 +88,6 @@
Adopted for Windows NT driver 01/20/98 CNL
*/
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c
index 1475912588e..df184aabce8 100644
--- a/sound/pci/rme32.c
+++ b/sound/pci/rme32.c
@@ -69,7 +69,6 @@
*/
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c
index 0b3c532c401..fb0a4ee8bc0 100644
--- a/sound/pci/rme96.c
+++ b/sound/pci/rme96.c
@@ -23,7 +23,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
@@ -2195,22 +2194,25 @@ snd_rme96_dac_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu
{
struct rme96 *rme96 = snd_kcontrol_chip(kcontrol);
int change = 0;
+ unsigned int vol, maxvol;
- if (!RME96_HAS_ANALOG_OUT(rme96)) {
+
+ if (!RME96_HAS_ANALOG_OUT(rme96))
return -EINVAL;
- }
+ maxvol = RME96_185X_MAX_OUT(rme96);
spin_lock_irq(&rme96->lock);
- if (u->value.integer.value[0] != rme96->vol[0]) {
- rme96->vol[0] = u->value.integer.value[0];
- change = 1;
- }
- if (u->value.integer.value[1] != rme96->vol[1]) {
- rme96->vol[1] = u->value.integer.value[1];
- change = 1;
- }
- if (change) {
- snd_rme96_apply_dac_volume(rme96);
+ vol = u->value.integer.value[0];
+ if (vol != rme96->vol[0] && vol <= maxvol) {
+ rme96->vol[0] = vol;
+ change = 1;
+ }
+ vol = u->value.integer.value[1];
+ if (vol != rme96->vol[1] && vol <= maxvol) {
+ rme96->vol[1] = vol;
+ change = 1;
}
+ if (change)
+ snd_rme96_apply_dac_volume(rme96);
spin_unlock_irq(&rme96->lock);
return change;
diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c
index ff26a3672d4..c2bd4384316 100644
--- a/sound/pci/rme9652/hdsp.c
+++ b/sound/pci/rme9652/hdsp.c
@@ -21,7 +21,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
@@ -104,8 +103,6 @@ MODULE_FIRMWARE("digiface_firmware_rev11.bin");
#define HDSP_statusRegister 0
#define HDSP_timecode 128
#define HDSP_status2Register 192
-#define HDSP_midiDataOut0 352
-#define HDSP_midiDataOut1 356
#define HDSP_midiDataIn0 360
#define HDSP_midiDataIn1 364
#define HDSP_midiStatusOut0 384
@@ -610,7 +607,10 @@ static int hdsp_playback_to_output_key (struct hdsp *hdsp, int in, int out)
case Multiface:
case Digiface:
default:
- return (64 * out) + (32 + (in));
+ if (hdsp->firmware_rev == 0xa)
+ return (64 * out) + (32 + (in));
+ else
+ return (52 * out) + (26 + (in));
case H9632:
return (32 * out) + (16 + (in));
case H9652:
@@ -624,7 +624,10 @@ static int hdsp_input_to_output_key (struct hdsp *hdsp, int in, int out)
case Multiface:
case Digiface:
default:
- return (64 * out) + in;
+ if (hdsp->firmware_rev == 0xa)
+ return (64 * out) + in;
+ else
+ return (52 * out) + in;
case H9632:
return (32 * out) + in;
case H9652:
@@ -2121,7 +2124,7 @@ static int snd_hdsp_put_clock_source_lock(struct snd_kcontrol *kcontrol, struct
change = (int)ucontrol->value.integer.value[0] != hdsp->clock_source_locked;
if (change)
- hdsp->clock_source_locked = ucontrol->value.integer.value[0];
+ hdsp->clock_source_locked = !!ucontrol->value.integer.value[0];
return change;
}
@@ -3558,7 +3561,7 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
}
-static void __devinit snd_hdsp_proc_init(struct hdsp *hdsp)
+static void snd_hdsp_proc_init(struct hdsp *hdsp)
{
struct snd_info_entry *entry;
@@ -3606,7 +3609,7 @@ static int snd_hdsp_set_defaults(struct hdsp *hdsp)
/* ASSUMPTION: hdsp->lock is either held, or
there is no need to hold it (e.g. during module
- initalization).
+ initialization).
*/
/* set defaults:
diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c
index f1bdda6cbcf..9a19ae6a64d 100644
--- a/sound/pci/rme9652/hdspm.c
+++ b/sound/pci/rme9652/hdspm.c
@@ -23,7 +23,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
@@ -3348,7 +3347,7 @@ static int snd_hdspm_set_defaults(struct hdspm * hdspm)
unsigned int i;
/* ASSUMPTION: hdspm->lock is either held, or there is no need to
- hold it (e.g. during module initalization).
+ hold it (e.g. during module initialization).
*/
/* set defaults: */
@@ -3416,7 +3415,7 @@ static int snd_hdspm_set_defaults(struct hdspm * hdspm)
/*------------------------------------------------------------
- interupt
+ interrupt
------------------------------------------------------------*/
static irqreturn_t snd_hdspm_interrupt(int irq, void *dev_id)
diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c
index 34f96f12e5b..a123f0e6ba2 100644
--- a/sound/pci/rme9652/rme9652.c
+++ b/sound/pci/rme9652/rme9652.c
@@ -20,7 +20,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
@@ -148,7 +147,7 @@ MODULE_SUPPORTED_DEVICE("{{RME,Hammerfall},"
#define RME9652_start_bit (1<<0) /* start record/play */
/* bits 1-3 encode buffersize/latency */
#define RME9652_Master (1<<4) /* Clock Mode Master=1,Slave/Auto=0 */
-#define RME9652_IE (1<<5) /* Interupt Enable */
+#define RME9652_IE (1<<5) /* Interrupt Enable */
#define RME9652_freq (1<<6) /* samplerate 0=44.1/88.2, 1=48/96 kHz */
#define RME9652_freq1 (1<<7) /* if 0, 32kHz, else always 1 */
#define RME9652_DS (1<<8) /* Doule Speed 0=44.1/48, 1=88.2/96 Khz */
@@ -1826,7 +1825,7 @@ static void snd_rme9652_set_defaults(struct snd_rme9652 *rme9652)
/* ASSUMPTION: rme9652->lock is either held, or
there is no need to hold it (e.g. during module
- initalization).
+ initialization).
*/
/* set defaults:
diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c
new file mode 100644
index 00000000000..dcd7cd01046
--- /dev/null
+++ b/sound/pci/sis7019.c
@@ -0,0 +1,1460 @@
+/*
+ * Driver for SiS7019 Audio Accelerator
+ *
+ * Copyright (C) 2004-2007, David Dillow
+ * Written by David Dillow <dave@thedillows.org>
+ * Inspired by the Trident 4D-WaveDX/NX driver.
+ *
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * 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
+ */
+
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/time.h>
+#include <linux/moduleparam.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include "sis7019.h"
+
+MODULE_AUTHOR("David Dillow <dave@thedillows.org>");
+MODULE_DESCRIPTION("SiS7019");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{SiS,SiS7019 Audio Accelerator}}");
+
+static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
+static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
+static int enable = 1;
+
+module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "Index value for SiS7019 Audio Accelerator.");
+module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for SiS7019 Audio Accelerator.");
+module_param(enable, bool, 0444);
+MODULE_PARM_DESC(enable, "Enable SiS7019 Audio Accelerator.");
+
+static struct pci_device_id snd_sis7019_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_SI, 0x7019) },
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_sis7019_ids);
+
+/* There are three timing modes for the voices.
+ *
+ * For both playback and capture, when the buffer is one or two periods long,
+ * we use the hardware's built-in Mid-Loop Interrupt and End-Loop Interrupt
+ * to let us know when the periods have ended.
+ *
+ * When performing playback with more than two periods per buffer, we set
+ * the "Stop Sample Offset" and tell the hardware to interrupt us when we
+ * reach it. We then update the offset and continue on until we are
+ * interrupted for the next period.
+ *
+ * Capture channels do not have a SSO, so we allocate a playback channel to
+ * use as a timer for the capture periods. We use the SSO on the playback
+ * channel to clock out virtual periods, and adjust the virtual period length
+ * to maintain synchronization. This algorithm came from the Trident driver.
+ *
+ * FIXME: It'd be nice to make use of some of the synth features in the
+ * hardware, but a woeful lack of documentation is a significant roadblock.
+ */
+struct voice {
+ u16 flags;
+#define VOICE_IN_USE 1
+#define VOICE_CAPTURE 2
+#define VOICE_SSO_TIMING 4
+#define VOICE_SYNC_TIMING 8
+ u16 sync_cso;
+ u16 period_size;
+ u16 buffer_size;
+ u16 sync_period_size;
+ u16 sync_buffer_size;
+ u32 sso;
+ u32 vperiod;
+ struct snd_pcm_substream *substream;
+ struct voice *timing;
+ void __iomem *ctrl_base;
+ void __iomem *wave_base;
+ void __iomem *sync_base;
+ int num;
+};
+
+/* We need four pages to store our wave parameters during a suspend. If
+ * we're not doing power management, we still need to allocate a page
+ * for the silence buffer.
+ */
+#ifdef CONFIG_PM
+#define SIS_SUSPEND_PAGES 4
+#else
+#define SIS_SUSPEND_PAGES 1
+#endif
+
+struct sis7019 {
+ unsigned long ioport;
+ void __iomem *ioaddr;
+ int irq;
+ int codecs_present;
+
+ struct pci_dev *pci;
+ struct snd_pcm *pcm;
+ struct snd_card *card;
+ struct snd_ac97 *ac97[3];
+
+ /* Protect against more than one thread hitting the AC97
+ * registers (in a more polite manner than pounding the hardware
+ * semaphore)
+ */
+ struct mutex ac97_mutex;
+
+ /* voice_lock protects allocation/freeing of the voice descriptions
+ */
+ spinlock_t voice_lock;
+
+ struct voice voices[64];
+ struct voice capture_voice;
+
+ /* Allocate pages to store the internal wave state during
+ * suspends. When we're operating, this can be used as a silence
+ * buffer for a timing channel.
+ */
+ void *suspend_state[SIS_SUSPEND_PAGES];
+
+ int silence_users;
+ dma_addr_t silence_dma_addr;
+};
+
+#define SIS_PRIMARY_CODEC_PRESENT 0x0001
+#define SIS_SECONDARY_CODEC_PRESENT 0x0002
+#define SIS_TERTIARY_CODEC_PRESENT 0x0004
+
+/* The HW offset parameters (Loop End, Stop Sample, End Sample) have a
+ * documented range of 8-0xfff8 samples. Given that they are 0-based,
+ * that places our period/buffer range at 9-0xfff9 samples. That makes the
+ * max buffer size 0xfff9 samples * 2 channels * 2 bytes per sample, and
+ * max samples / min samples gives us the max periods in a buffer.
+ *
+ * We'll add a constraint upon open that limits the period and buffer sample
+ * size to values that are legal for the hardware.
+ */
+static struct snd_pcm_hardware sis_playback_hw_info = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_SYNC_START |
+ SNDRV_PCM_INFO_RESUME),
+ .formats = (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE),
+ .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 4000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = (0xfff9 * 4),
+ .period_bytes_min = 9,
+ .period_bytes_max = (0xfff9 * 4),
+ .periods_min = 1,
+ .periods_max = (0xfff9 / 9),
+};
+
+static struct snd_pcm_hardware sis_capture_hw_info = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_SYNC_START |
+ SNDRV_PCM_INFO_RESUME),
+ .formats = (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE),
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 4000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = (0xfff9 * 4),
+ .period_bytes_min = 9,
+ .period_bytes_max = (0xfff9 * 4),
+ .periods_min = 1,
+ .periods_max = (0xfff9 / 9),
+};
+
+static void sis_update_sso(struct voice *voice, u16 period)
+{
+ void __iomem *base = voice->ctrl_base;
+
+ voice->sso += period;
+ if (voice->sso >= voice->buffer_size)
+ voice->sso -= voice->buffer_size;
+
+ /* Enforce the documented hardware minimum offset */
+ if (voice->sso < 8)
+ voice->sso = 8;
+
+ /* The SSO is in the upper 16 bits of the register. */
+ writew(voice->sso & 0xffff, base + SIS_PLAY_DMA_SSO_ESO + 2);
+}
+
+static void sis_update_voice(struct voice *voice)
+{
+ if (voice->flags & VOICE_SSO_TIMING) {
+ sis_update_sso(voice, voice->period_size);
+ } else if (voice->flags & VOICE_SYNC_TIMING) {
+ int sync;
+
+ /* If we've not hit the end of the virtual period, update
+ * our records and keep going.
+ */
+ if (voice->vperiod > voice->period_size) {
+ voice->vperiod -= voice->period_size;
+ if (voice->vperiod < voice->period_size)
+ sis_update_sso(voice, voice->vperiod);
+ else
+ sis_update_sso(voice, voice->period_size);
+ return;
+ }
+
+ /* Calculate our relative offset between the target and
+ * the actual CSO value. Since we're operating in a loop,
+ * if the value is more than half way around, we can
+ * consider ourselves wrapped.
+ */
+ sync = voice->sync_cso;
+ sync -= readw(voice->sync_base + SIS_CAPTURE_DMA_FORMAT_CSO);
+ if (sync > (voice->sync_buffer_size / 2))
+ sync -= voice->sync_buffer_size;
+
+ /* If sync is positive, then we interrupted too early, and
+ * we'll need to come back in a few samples and try again.
+ * There's a minimum wait, as it takes some time for the DMA
+ * engine to startup, etc...
+ */
+ if (sync > 0) {
+ if (sync < 16)
+ sync = 16;
+ sis_update_sso(voice, sync);
+ return;
+ }
+
+ /* Ok, we interrupted right on time, or (hopefully) just
+ * a bit late. We'll adjst our next waiting period based
+ * on how close we got.
+ *
+ * We need to stay just behind the actual channel to ensure
+ * it really is past a period when we get our interrupt --
+ * otherwise we'll fall into the early code above and have
+ * a minimum wait time, which makes us quite late here,
+ * eating into the user's time to refresh the buffer, esp.
+ * if using small periods.
+ *
+ * If we're less than 9 samples behind, we're on target.
+ */
+ if (sync > -9)
+ voice->vperiod = voice->sync_period_size + 1;
+ else
+ voice->vperiod = voice->sync_period_size - 4;
+
+ if (voice->vperiod < voice->buffer_size) {
+ sis_update_sso(voice, voice->vperiod);
+ voice->vperiod = 0;
+ } else
+ sis_update_sso(voice, voice->period_size);
+
+ sync = voice->sync_cso + voice->sync_period_size;
+ if (sync >= voice->sync_buffer_size)
+ sync -= voice->sync_buffer_size;
+ voice->sync_cso = sync;
+ }
+
+ snd_pcm_period_elapsed(voice->substream);
+}
+
+static void sis_voice_irq(u32 status, struct voice *voice)
+{
+ int bit;
+
+ while (status) {
+ bit = __ffs(status);
+ status >>= bit + 1;
+ voice += bit;
+ sis_update_voice(voice);
+ voice++;
+ }
+}
+
+static irqreturn_t sis_interrupt(int irq, void *dev)
+{
+ struct sis7019 *sis = dev;
+ unsigned long io = sis->ioport;
+ struct voice *voice;
+ u32 intr, status;
+
+ /* We only use the DMA interrupts, and we don't enable any other
+ * source of interrupts. But, it is possible to see an interupt
+ * status that didn't actually interrupt us, so eliminate anything
+ * we're not expecting to avoid falsely claiming an IRQ, and an
+ * ensuing endless loop.
+ */
+ intr = inl(io + SIS_GISR);
+ intr &= SIS_GISR_AUDIO_PLAY_DMA_IRQ_STATUS |
+ SIS_GISR_AUDIO_RECORD_DMA_IRQ_STATUS;
+ if (!intr)
+ return IRQ_NONE;
+
+ do {
+ status = inl(io + SIS_PISR_A);
+ if (status) {
+ sis_voice_irq(status, sis->voices);
+ outl(status, io + SIS_PISR_A);
+ }
+
+ status = inl(io + SIS_PISR_B);
+ if (status) {
+ sis_voice_irq(status, &sis->voices[32]);
+ outl(status, io + SIS_PISR_B);
+ }
+
+ status = inl(io + SIS_RISR);
+ if (status) {
+ voice = &sis->capture_voice;
+ if (!voice->timing)
+ snd_pcm_period_elapsed(voice->substream);
+
+ outl(status, io + SIS_RISR);
+ }
+
+ outl(intr, io + SIS_GISR);
+ intr = inl(io + SIS_GISR);
+ intr &= SIS_GISR_AUDIO_PLAY_DMA_IRQ_STATUS |
+ SIS_GISR_AUDIO_RECORD_DMA_IRQ_STATUS;
+ } while (intr);
+
+ return IRQ_HANDLED;
+}
+
+static u32 sis_rate_to_delta(unsigned int rate)
+{
+ u32 delta;
+
+ /* This was copied from the trident driver, but it seems its gotten
+ * around a bit... nevertheless, it works well.
+ *
+ * We special case 44100 and 8000 since rounding with the equation
+ * does not give us an accurate enough value. For 11025 and 22050
+ * the equation gives us the best answer. All other frequencies will
+ * also use the equation. JDW
+ */
+ if (rate == 44100)
+ delta = 0xeb3;
+ else if (rate == 8000)
+ delta = 0x2ab;
+ else if (rate == 48000)
+ delta = 0x1000;
+ else
+ delta = (((rate << 12) + 24000) / 48000) & 0x0000ffff;
+ return delta;
+}
+
+static void __sis_map_silence(struct sis7019 *sis)
+{
+ /* Helper function: must hold sis->voice_lock on entry */
+ if (!sis->silence_users)
+ sis->silence_dma_addr = pci_map_single(sis->pci,
+ sis->suspend_state[0],
+ 4096, PCI_DMA_TODEVICE);
+ sis->silence_users++;
+}
+
+static void __sis_unmap_silence(struct sis7019 *sis)
+{
+ /* Helper function: must hold sis->voice_lock on entry */
+ sis->silence_users--;
+ if (!sis->silence_users)
+ pci_unmap_single(sis->pci, sis->silence_dma_addr, 4096,
+ PCI_DMA_TODEVICE);
+}
+
+static void sis_free_voice(struct sis7019 *sis, struct voice *voice)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sis->voice_lock, flags);
+ if (voice->timing) {
+ __sis_unmap_silence(sis);
+ voice->timing->flags &= ~(VOICE_IN_USE | VOICE_SSO_TIMING |
+ VOICE_SYNC_TIMING);
+ voice->timing = NULL;
+ }
+ voice->flags &= ~(VOICE_IN_USE | VOICE_SSO_TIMING | VOICE_SYNC_TIMING);
+ spin_unlock_irqrestore(&sis->voice_lock, flags);
+}
+
+static struct voice *__sis_alloc_playback_voice(struct sis7019 *sis)
+{
+ /* Must hold the voice_lock on entry */
+ struct voice *voice;
+ int i;
+
+ for (i = 0; i < 64; i++) {
+ voice = &sis->voices[i];
+ if (voice->flags & VOICE_IN_USE)
+ continue;
+ voice->flags |= VOICE_IN_USE;
+ goto found_one;
+ }
+ voice = NULL;
+
+found_one:
+ return voice;
+}
+
+static struct voice *sis_alloc_playback_voice(struct sis7019 *sis)
+{
+ struct voice *voice;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sis->voice_lock, flags);
+ voice = __sis_alloc_playback_voice(sis);
+ spin_unlock_irqrestore(&sis->voice_lock, flags);
+
+ return voice;
+}
+
+static int sis_alloc_timing_voice(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct sis7019 *sis = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct voice *voice = runtime->private_data;
+ unsigned int period_size, buffer_size;
+ unsigned long flags;
+ int needed;
+
+ /* If there are one or two periods per buffer, we don't need a
+ * timing voice, as we can use the capture channel's interrupts
+ * to clock out the periods.
+ */
+ period_size = params_period_size(hw_params);
+ buffer_size = params_buffer_size(hw_params);
+ needed = (period_size != buffer_size &&
+ period_size != (buffer_size / 2));
+
+ if (needed && !voice->timing) {
+ spin_lock_irqsave(&sis->voice_lock, flags);
+ voice->timing = __sis_alloc_playback_voice(sis);
+ if (voice->timing)
+ __sis_map_silence(sis);
+ spin_unlock_irqrestore(&sis->voice_lock, flags);
+ if (!voice->timing)
+ return -ENOMEM;
+ voice->timing->substream = substream;
+ } else if (!needed && voice->timing) {
+ sis_free_voice(sis, voice);
+ voice->timing = NULL;
+ }
+
+ return 0;
+}
+
+static int sis_playback_open(struct snd_pcm_substream *substream)
+{
+ struct sis7019 *sis = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct voice *voice;
+
+ voice = sis_alloc_playback_voice(sis);
+ if (!voice)
+ return -EAGAIN;
+
+ voice->substream = substream;
+ runtime->private_data = voice;
+ runtime->hw = sis_playback_hw_info;
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ 9, 0xfff9);
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ 9, 0xfff9);
+ snd_pcm_set_sync(substream);
+ return 0;
+}
+
+static int sis_substream_close(struct snd_pcm_substream *substream)
+{
+ struct sis7019 *sis = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct voice *voice = runtime->private_data;
+
+ sis_free_voice(sis, voice);
+ return 0;
+}
+
+static int sis_playback_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ return snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+}
+
+static int sis_hw_free(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static int sis_pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct voice *voice = runtime->private_data;
+ void __iomem *ctrl_base = voice->ctrl_base;
+ void __iomem *wave_base = voice->wave_base;
+ u32 format, dma_addr, control, sso_eso, delta, reg;
+ u16 leo;
+
+ /* We rely on the PCM core to ensure that the parameters for this
+ * substream do not change on us while we're programming the HW.
+ */
+ format = 0;
+ if (snd_pcm_format_width(runtime->format) == 8)
+ format |= SIS_PLAY_DMA_FORMAT_8BIT;
+ if (!snd_pcm_format_signed(runtime->format))
+ format |= SIS_PLAY_DMA_FORMAT_UNSIGNED;
+ if (runtime->channels == 1)
+ format |= SIS_PLAY_DMA_FORMAT_MONO;
+
+ /* The baseline setup is for a single period per buffer, and
+ * we add bells and whistles as needed from there.
+ */
+ dma_addr = runtime->dma_addr;
+ leo = runtime->buffer_size - 1;
+ control = leo | SIS_PLAY_DMA_LOOP | SIS_PLAY_DMA_INTR_AT_LEO;
+ sso_eso = leo;
+
+ if (runtime->period_size == (runtime->buffer_size / 2)) {
+ control |= SIS_PLAY_DMA_INTR_AT_MLP;
+ } else if (runtime->period_size != runtime->buffer_size) {
+ voice->flags |= VOICE_SSO_TIMING;
+ voice->sso = runtime->period_size - 1;
+ voice->period_size = runtime->period_size;
+ voice->buffer_size = runtime->buffer_size;
+
+ control &= ~SIS_PLAY_DMA_INTR_AT_LEO;
+ control |= SIS_PLAY_DMA_INTR_AT_SSO;
+ sso_eso |= (runtime->period_size - 1) << 16;
+ }
+
+ delta = sis_rate_to_delta(runtime->rate);
+
+ /* Ok, we're ready to go, set up the channel.
+ */
+ writel(format, ctrl_base + SIS_PLAY_DMA_FORMAT_CSO);
+ writel(dma_addr, ctrl_base + SIS_PLAY_DMA_BASE);
+ writel(control, ctrl_base + SIS_PLAY_DMA_CONTROL);
+ writel(sso_eso, ctrl_base + SIS_PLAY_DMA_SSO_ESO);
+
+ for (reg = 0; reg < SIS_WAVE_SIZE; reg += 4)
+ writel(0, wave_base + reg);
+
+ writel(SIS_WAVE_GENERAL_WAVE_VOLUME, wave_base + SIS_WAVE_GENERAL);
+ writel(delta << 16, wave_base + SIS_WAVE_GENERAL_ARTICULATION);
+ writel(SIS_WAVE_CHANNEL_CONTROL_FIRST_SAMPLE |
+ SIS_WAVE_CHANNEL_CONTROL_AMP_ENABLE |
+ SIS_WAVE_CHANNEL_CONTROL_INTERPOLATE_ENABLE,
+ wave_base + SIS_WAVE_CHANNEL_CONTROL);
+
+ /* Force PCI writes to post. */
+ readl(ctrl_base);
+
+ return 0;
+}
+
+static int sis_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct sis7019 *sis = snd_pcm_substream_chip(substream);
+ unsigned long io = sis->ioport;
+ struct snd_pcm_substream *s;
+ struct voice *voice;
+ void *chip;
+ int starting;
+ u32 record = 0;
+ u32 play[2] = { 0, 0 };
+
+ /* No locks needed, as the PCM core will hold the locks on the
+ * substreams, and the HW will only start/stop the indicated voices
+ * without changing the state of the others.
+ */
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ starting = 1;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ starting = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_pcm_group_for_each_entry(s, substream) {
+ /* Make sure it is for us... */
+ chip = snd_pcm_substream_chip(s);
+ if (chip != sis)
+ continue;
+
+ voice = s->runtime->private_data;
+ if (voice->flags & VOICE_CAPTURE) {
+ record |= 1 << voice->num;
+ voice = voice->timing;
+ }
+
+ /* voice could be NULL if this a recording stream, and it
+ * doesn't have an external timing channel.
+ */
+ if (voice)
+ play[voice->num / 32] |= 1 << (voice->num & 0x1f);
+
+ snd_pcm_trigger_done(s, substream);
+ }
+
+ if (starting) {
+ if (record)
+ outl(record, io + SIS_RECORD_START_REG);
+ if (play[0])
+ outl(play[0], io + SIS_PLAY_START_A_REG);
+ if (play[1])
+ outl(play[1], io + SIS_PLAY_START_B_REG);
+ } else {
+ if (record)
+ outl(record, io + SIS_RECORD_STOP_REG);
+ if (play[0])
+ outl(play[0], io + SIS_PLAY_STOP_A_REG);
+ if (play[1])
+ outl(play[1], io + SIS_PLAY_STOP_B_REG);
+ }
+ return 0;
+}
+
+static snd_pcm_uframes_t sis_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct voice *voice = runtime->private_data;
+ u32 cso;
+
+ cso = readl(voice->ctrl_base + SIS_PLAY_DMA_FORMAT_CSO);
+ cso &= 0xffff;
+ return cso;
+}
+
+static int sis_capture_open(struct snd_pcm_substream *substream)
+{
+ struct sis7019 *sis = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct voice *voice = &sis->capture_voice;
+ unsigned long flags;
+
+ /* FIXME: The driver only supports recording from one channel
+ * at the moment, but it could support more.
+ */
+ spin_lock_irqsave(&sis->voice_lock, flags);
+ if (voice->flags & VOICE_IN_USE)
+ voice = NULL;
+ else
+ voice->flags |= VOICE_IN_USE;
+ spin_unlock_irqrestore(&sis->voice_lock, flags);
+
+ if (!voice)
+ return -EAGAIN;
+
+ voice->substream = substream;
+ runtime->private_data = voice;
+ runtime->hw = sis_capture_hw_info;
+ runtime->hw.rates = sis->ac97[0]->rates[AC97_RATES_ADC];
+ snd_pcm_limit_hw_rates(runtime);
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ 9, 0xfff9);
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ 9, 0xfff9);
+ snd_pcm_set_sync(substream);
+ return 0;
+}
+
+static int sis_capture_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct sis7019 *sis = snd_pcm_substream_chip(substream);
+ int rc;
+
+ rc = snd_ac97_set_rate(sis->ac97[0], AC97_PCM_LR_ADC_RATE,
+ params_rate(hw_params));
+ if (rc)
+ goto out;
+
+ rc = snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+ if (rc < 0)
+ goto out;
+
+ rc = sis_alloc_timing_voice(substream, hw_params);
+
+out:
+ return rc;
+}
+
+static void sis_prepare_timing_voice(struct voice *voice,
+ struct snd_pcm_substream *substream)
+{
+ struct sis7019 *sis = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct voice *timing = voice->timing;
+ void __iomem *play_base = timing->ctrl_base;
+ void __iomem *wave_base = timing->wave_base;
+ u16 buffer_size, period_size;
+ u32 format, control, sso_eso, delta;
+ u32 vperiod, sso, reg;
+
+ /* Set our initial buffer and period as large as we can given a
+ * single page of silence.
+ */
+ buffer_size = 4096 / runtime->channels;
+ buffer_size /= snd_pcm_format_size(runtime->format, 1);
+ period_size = buffer_size;
+
+ /* Initially, we want to interrupt just a bit behind the end of
+ * the period we're clocking out. 10 samples seems to give a good
+ * delay.
+ *
+ * We want to spread our interrupts throughout the virtual period,
+ * so that we don't end up with two interrupts back to back at the
+ * end -- this helps minimize the effects of any jitter. Adjust our
+ * clocking period size so that the last period is at least a fourth
+ * of a full period.
+ *
+ * This is all moot if we don't need to use virtual periods.
+ */
+ vperiod = runtime->period_size + 10;
+ if (vperiod > period_size) {
+ u16 tail = vperiod % period_size;
+ u16 quarter_period = period_size / 4;
+
+ if (tail && tail < quarter_period) {
+ u16 loops = vperiod / period_size;
+
+ tail = quarter_period - tail;
+ tail += loops - 1;
+ tail /= loops;
+ period_size -= tail;
+ }
+
+ sso = period_size - 1;
+ } else {
+ /* The initial period will fit inside the buffer, so we
+ * don't need to use virtual periods -- disable them.
+ */
+ period_size = runtime->period_size;
+ sso = vperiod - 1;
+ vperiod = 0;
+ }
+
+ /* The interrupt handler implements the timing syncronization, so
+ * setup its state.
+ */
+ timing->flags |= VOICE_SYNC_TIMING;
+ timing->sync_base = voice->ctrl_base;
+ timing->sync_cso = runtime->period_size - 1;
+ timing->sync_period_size = runtime->period_size;
+ timing->sync_buffer_size = runtime->buffer_size;
+ timing->period_size = period_size;
+ timing->buffer_size = buffer_size;
+ timing->sso = sso;
+ timing->vperiod = vperiod;
+
+ /* Using unsigned samples with the all-zero silence buffer
+ * forces the output to the lower rail, killing playback.
+ * So ignore unsigned vs signed -- it doesn't change the timing.
+ */
+ format = 0;
+ if (snd_pcm_format_width(runtime->format) == 8)
+ format = SIS_CAPTURE_DMA_FORMAT_8BIT;
+ if (runtime->channels == 1)
+ format |= SIS_CAPTURE_DMA_FORMAT_MONO;
+
+ control = timing->buffer_size - 1;
+ control |= SIS_PLAY_DMA_LOOP | SIS_PLAY_DMA_INTR_AT_SSO;
+ sso_eso = timing->buffer_size - 1;
+ sso_eso |= timing->sso << 16;
+
+ delta = sis_rate_to_delta(runtime->rate);
+
+ /* We've done the math, now configure the channel.
+ */
+ writel(format, play_base + SIS_PLAY_DMA_FORMAT_CSO);
+ writel(sis->silence_dma_addr, play_base + SIS_PLAY_DMA_BASE);
+ writel(control, play_base + SIS_PLAY_DMA_CONTROL);
+ writel(sso_eso, play_base + SIS_PLAY_DMA_SSO_ESO);
+
+ for (reg = 0; reg < SIS_WAVE_SIZE; reg += 4)
+ writel(0, wave_base + reg);
+
+ writel(SIS_WAVE_GENERAL_WAVE_VOLUME, wave_base + SIS_WAVE_GENERAL);
+ writel(delta << 16, wave_base + SIS_WAVE_GENERAL_ARTICULATION);
+ writel(SIS_WAVE_CHANNEL_CONTROL_FIRST_SAMPLE |
+ SIS_WAVE_CHANNEL_CONTROL_AMP_ENABLE |
+ SIS_WAVE_CHANNEL_CONTROL_INTERPOLATE_ENABLE,
+ wave_base + SIS_WAVE_CHANNEL_CONTROL);
+}
+
+static int sis_pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct voice *voice = runtime->private_data;
+ void __iomem *rec_base = voice->ctrl_base;
+ u32 format, dma_addr, control;
+ u16 leo;
+
+ /* We rely on the PCM core to ensure that the parameters for this
+ * substream do not change on us while we're programming the HW.
+ */
+ format = 0;
+ if (snd_pcm_format_width(runtime->format) == 8)
+ format = SIS_CAPTURE_DMA_FORMAT_8BIT;
+ if (!snd_pcm_format_signed(runtime->format))
+ format |= SIS_CAPTURE_DMA_FORMAT_UNSIGNED;
+ if (runtime->channels == 1)
+ format |= SIS_CAPTURE_DMA_FORMAT_MONO;
+
+ dma_addr = runtime->dma_addr;
+ leo = runtime->buffer_size - 1;
+ control = leo | SIS_CAPTURE_DMA_LOOP;
+
+ /* If we've got more than two periods per buffer, then we have
+ * use a timing voice to clock out the periods. Otherwise, we can
+ * use the capture channel's interrupts.
+ */
+ if (voice->timing) {
+ sis_prepare_timing_voice(voice, substream);
+ } else {
+ control |= SIS_CAPTURE_DMA_INTR_AT_LEO;
+ if (runtime->period_size != runtime->buffer_size)
+ control |= SIS_CAPTURE_DMA_INTR_AT_MLP;
+ }
+
+ writel(format, rec_base + SIS_CAPTURE_DMA_FORMAT_CSO);
+ writel(dma_addr, rec_base + SIS_CAPTURE_DMA_BASE);
+ writel(control, rec_base + SIS_CAPTURE_DMA_CONTROL);
+
+ /* Force the writes to post. */
+ readl(rec_base);
+
+ return 0;
+}
+
+static struct snd_pcm_ops sis_playback_ops = {
+ .open = sis_playback_open,
+ .close = sis_substream_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = sis_playback_hw_params,
+ .hw_free = sis_hw_free,
+ .prepare = sis_pcm_playback_prepare,
+ .trigger = sis_pcm_trigger,
+ .pointer = sis_pcm_pointer,
+};
+
+static struct snd_pcm_ops sis_capture_ops = {
+ .open = sis_capture_open,
+ .close = sis_substream_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = sis_capture_hw_params,
+ .hw_free = sis_hw_free,
+ .prepare = sis_pcm_capture_prepare,
+ .trigger = sis_pcm_trigger,
+ .pointer = sis_pcm_pointer,
+};
+
+static int __devinit sis_pcm_create(struct sis7019 *sis)
+{
+ struct snd_pcm *pcm;
+ int rc;
+
+ /* We have 64 voices, and the driver currently records from
+ * only one channel, though that could change in the future.
+ */
+ rc = snd_pcm_new(sis->card, "SiS7019", 0, 64, 1, &pcm);
+ if (rc)
+ return rc;
+
+ pcm->private_data = sis;
+ strcpy(pcm->name, "SiS7019");
+ sis->pcm = pcm;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &sis_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &sis_capture_ops);
+
+ /* Try to preallocate some memory, but it's not the end of the
+ * world if this fails.
+ */
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(sis->pci), 64*1024, 128*1024);
+
+ return 0;
+}
+
+static unsigned short sis_ac97_rw(struct sis7019 *sis, int codec, u32 cmd)
+{
+ unsigned long io = sis->ioport;
+ unsigned short val = 0xffff;
+ u16 status;
+ u16 rdy;
+ int count;
+ const static u16 codec_ready[3] = {
+ SIS_AC97_STATUS_CODEC_READY,
+ SIS_AC97_STATUS_CODEC2_READY,
+ SIS_AC97_STATUS_CODEC3_READY,
+ };
+
+ rdy = codec_ready[codec];
+
+
+ /* Get the AC97 semaphore -- software first, so we don't spin
+ * pounding out IO reads on the hardware semaphore...
+ */
+ mutex_lock(&sis->ac97_mutex);
+
+ count = 0xffff;
+ while ((inw(io + SIS_AC97_SEMA) & SIS_AC97_SEMA_BUSY) && --count)
+ udelay(1);
+
+ if (!count)
+ goto timeout;
+
+ /* ... and wait for any outstanding commands to complete ...
+ */
+ count = 0xffff;
+ do {
+ status = inw(io + SIS_AC97_STATUS);
+ if ((status & rdy) && !(status & SIS_AC97_STATUS_BUSY))
+ break;
+
+ udelay(1);
+ } while (--count);
+
+ if (!count)
+ goto timeout_sema;
+
+ /* ... before sending our command and waiting for it to finish ...
+ */
+ outl(cmd, io + SIS_AC97_CMD);
+ udelay(10);
+
+ count = 0xffff;
+ while ((inw(io + SIS_AC97_STATUS) & SIS_AC97_STATUS_BUSY) && --count)
+ udelay(1);
+
+ /* ... and reading the results (if any).
+ */
+ val = inl(io + SIS_AC97_CMD) >> 16;
+
+timeout_sema:
+ outl(SIS_AC97_SEMA_RELEASE, io + SIS_AC97_SEMA);
+timeout:
+ mutex_unlock(&sis->ac97_mutex);
+
+ if (!count) {
+ printk(KERN_ERR "sis7019: ac97 codec %d timeout cmd 0x%08x\n",
+ codec, cmd);
+ }
+
+ return val;
+}
+
+static void sis_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+ unsigned short val)
+{
+ const static u32 cmd[3] = {
+ SIS_AC97_CMD_CODEC_WRITE,
+ SIS_AC97_CMD_CODEC2_WRITE,
+ SIS_AC97_CMD_CODEC3_WRITE,
+ };
+ sis_ac97_rw(ac97->private_data, ac97->num,
+ (val << 16) | (reg << 8) | cmd[ac97->num]);
+}
+
+static unsigned short sis_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
+{
+ const static u32 cmd[3] = {
+ SIS_AC97_CMD_CODEC_READ,
+ SIS_AC97_CMD_CODEC2_READ,
+ SIS_AC97_CMD_CODEC3_READ,
+ };
+ return sis_ac97_rw(ac97->private_data, ac97->num,
+ (reg << 8) | cmd[ac97->num]);
+}
+
+static int __devinit sis_mixer_create(struct sis7019 *sis)
+{
+ struct snd_ac97_bus *bus;
+ struct snd_ac97_template ac97;
+ static struct snd_ac97_bus_ops ops = {
+ .write = sis_ac97_write,
+ .read = sis_ac97_read,
+ };
+ int rc;
+
+ memset(&ac97, 0, sizeof(ac97));
+ ac97.private_data = sis;
+
+ rc = snd_ac97_bus(sis->card, 0, &ops, NULL, &bus);
+ if (!rc && sis->codecs_present & SIS_PRIMARY_CODEC_PRESENT)
+ rc = snd_ac97_mixer(bus, &ac97, &sis->ac97[0]);
+ ac97.num = 1;
+ if (!rc && (sis->codecs_present & SIS_SECONDARY_CODEC_PRESENT))
+ rc = snd_ac97_mixer(bus, &ac97, &sis->ac97[1]);
+ ac97.num = 2;
+ if (!rc && (sis->codecs_present & SIS_TERTIARY_CODEC_PRESENT))
+ rc = snd_ac97_mixer(bus, &ac97, &sis->ac97[2]);
+
+ /* If we return an error here, then snd_card_free() should
+ * free up any ac97 codecs that got created, as well as the bus.
+ */
+ return rc;
+}
+
+static void sis_free_suspend(struct sis7019 *sis)
+{
+ int i;
+
+ for (i = 0; i < SIS_SUSPEND_PAGES; i++)
+ kfree(sis->suspend_state[i]);
+}
+
+static int sis_chip_free(struct sis7019 *sis)
+{
+ /* Reset the chip, and disable all interrputs.
+ */
+ outl(SIS_GCR_SOFTWARE_RESET, sis->ioport + SIS_GCR);
+ udelay(10);
+ outl(0, sis->ioport + SIS_GCR);
+ outl(0, sis->ioport + SIS_GIER);
+
+ /* Now, free everything we allocated.
+ */
+ if (sis->irq >= 0)
+ free_irq(sis->irq, sis);
+
+ if (sis->ioaddr)
+ iounmap(sis->ioaddr);
+
+ pci_release_regions(sis->pci);
+ pci_disable_device(sis->pci);
+
+ sis_free_suspend(sis);
+ return 0;
+}
+
+static int sis_dev_free(struct snd_device *dev)
+{
+ struct sis7019 *sis = dev->device_data;
+ return sis_chip_free(sis);
+}
+
+static int sis_chip_init(struct sis7019 *sis)
+{
+ unsigned long io = sis->ioport;
+ void __iomem *ioaddr = sis->ioaddr;
+ u16 status;
+ int count;
+ int i;
+
+ /* Reset the audio controller
+ */
+ outl(SIS_GCR_SOFTWARE_RESET, io + SIS_GCR);
+ udelay(10);
+ outl(0, io + SIS_GCR);
+
+ /* Get the AC-link semaphore, and reset the codecs
+ */
+ count = 0xffff;
+ while ((inw(io + SIS_AC97_SEMA) & SIS_AC97_SEMA_BUSY) && --count)
+ udelay(1);
+
+ if (!count)
+ return -EIO;
+
+ outl(SIS_AC97_CMD_CODEC_COLD_RESET, io + SIS_AC97_CMD);
+ udelay(10);
+
+ count = 0xffff;
+ while ((inw(io + SIS_AC97_STATUS) & SIS_AC97_STATUS_BUSY) && --count)
+ udelay(1);
+
+ /* Now that we've finished the reset, find out what's attached.
+ */
+ status = inl(io + SIS_AC97_STATUS);
+ if (status & SIS_AC97_STATUS_CODEC_READY)
+ sis->codecs_present |= SIS_PRIMARY_CODEC_PRESENT;
+ if (status & SIS_AC97_STATUS_CODEC2_READY)
+ sis->codecs_present |= SIS_SECONDARY_CODEC_PRESENT;
+ if (status & SIS_AC97_STATUS_CODEC3_READY)
+ sis->codecs_present |= SIS_TERTIARY_CODEC_PRESENT;
+
+ /* All done, let go of the semaphore, and check for errors
+ */
+ outl(SIS_AC97_SEMA_RELEASE, io + SIS_AC97_SEMA);
+ if (!sis->codecs_present || !count)
+ return -EIO;
+
+ /* Let the hardware know that the audio driver is alive,
+ * and enable PCM slots on the AC-link for L/R playback (3 & 4) and
+ * record channels. We're going to want to use Variable Rate Audio
+ * for recording, to avoid needlessly resampling from 48kHZ.
+ */
+ outl(SIS_AC97_CONF_AUDIO_ALIVE, io + SIS_AC97_CONF);
+ outl(SIS_AC97_CONF_AUDIO_ALIVE | SIS_AC97_CONF_PCM_LR_ENABLE |
+ SIS_AC97_CONF_PCM_CAP_MIC_ENABLE |
+ SIS_AC97_CONF_PCM_CAP_LR_ENABLE |
+ SIS_AC97_CONF_CODEC_VRA_ENABLE, io + SIS_AC97_CONF);
+
+ /* All AC97 PCM slots should be sourced from sub-mixer 0.
+ */
+ outl(0, io + SIS_AC97_PSR);
+
+ /* There is only one valid DMA setup for a PCI environment.
+ */
+ outl(SIS_DMA_CSR_PCI_SETTINGS, io + SIS_DMA_CSR);
+
+ /* Reset the syncronization groups for all of the channels
+ * to be asyncronous. If we start doing SPDIF or 5.1 sound, etc.
+ * we'll need to change how we handle these. Until then, we just
+ * assign sub-mixer 0 to all playback channels, and avoid any
+ * attenuation on the audio.
+ */
+ outl(0, io + SIS_PLAY_SYNC_GROUP_A);
+ outl(0, io + SIS_PLAY_SYNC_GROUP_B);
+ outl(0, io + SIS_PLAY_SYNC_GROUP_C);
+ outl(0, io + SIS_PLAY_SYNC_GROUP_D);
+ outl(0, io + SIS_MIXER_SYNC_GROUP);
+
+ for (i = 0; i < 64; i++) {
+ writel(i, SIS_MIXER_START_ADDR(ioaddr, i));
+ writel(SIS_MIXER_RIGHT_NO_ATTEN | SIS_MIXER_LEFT_NO_ATTEN |
+ SIS_MIXER_DEST_0, SIS_MIXER_ADDR(ioaddr, i));
+ }
+
+ /* Don't attenuate any audio set for the wave amplifier.
+ *
+ * FIXME: Maximum attenuation is set for the music amp, which will
+ * need to change if we start using the synth engine.
+ */
+ outl(0xffff0000, io + SIS_WEVCR);
+
+ /* Ensure that the wave engine is in normal operating mode.
+ */
+ outl(0, io + SIS_WECCR);
+
+ /* Go ahead and enable the DMA interrupts. They won't go live
+ * until we start a channel.
+ */
+ outl(SIS_GIER_AUDIO_PLAY_DMA_IRQ_ENABLE |
+ SIS_GIER_AUDIO_RECORD_DMA_IRQ_ENABLE, io + SIS_GIER);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int sis_suspend(struct pci_dev *pci, pm_message_t state)
+{
+ struct snd_card *card = pci_get_drvdata(pci);
+ struct sis7019 *sis = card->private_data;
+ void __iomem *ioaddr = sis->ioaddr;
+ int i;
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+ snd_pcm_suspend_all(sis->pcm);
+ if (sis->codecs_present & SIS_PRIMARY_CODEC_PRESENT)
+ snd_ac97_suspend(sis->ac97[0]);
+ if (sis->codecs_present & SIS_SECONDARY_CODEC_PRESENT)
+ snd_ac97_suspend(sis->ac97[1]);
+ if (sis->codecs_present & SIS_TERTIARY_CODEC_PRESENT)
+ snd_ac97_suspend(sis->ac97[2]);
+
+ /* snd_pcm_suspend_all() stopped all channels, so we're quiescent.
+ */
+ if (sis->irq >= 0) {
+ synchronize_irq(sis->irq);
+ free_irq(sis->irq, sis);
+ sis->irq = -1;
+ }
+
+ /* Save the internal state away
+ */
+ for (i = 0; i < 4; i++) {
+ memcpy_fromio(sis->suspend_state[i], ioaddr, 4096);
+ ioaddr += 4096;
+ }
+
+ pci_disable_device(pci);
+ pci_save_state(pci);
+ pci_set_power_state(pci, pci_choose_state(pci, state));
+ return 0;
+}
+
+static int sis_resume(struct pci_dev *pci)
+{
+ struct snd_card *card = pci_get_drvdata(pci);
+ struct sis7019 *sis = card->private_data;
+ void __iomem *ioaddr = sis->ioaddr;
+ int i;
+
+ pci_set_power_state(pci, PCI_D0);
+ pci_restore_state(pci);
+
+ if (pci_enable_device(pci) < 0) {
+ printk(KERN_ERR "sis7019: unable to re-enable device\n");
+ goto error;
+ }
+
+ if (sis_chip_init(sis)) {
+ printk(KERN_ERR "sis7019: unable to re-init controller\n");
+ goto error;
+ }
+
+ if (request_irq(pci->irq, sis_interrupt, IRQF_DISABLED|IRQF_SHARED,
+ card->shortname, sis)) {
+ printk(KERN_ERR "sis7019: unable to regain IRQ %d\n", pci->irq);
+ goto error;
+ }
+
+ /* Restore saved state, then clear out the page we use for the
+ * silence buffer.
+ */
+ for (i = 0; i < 4; i++) {
+ memcpy_toio(ioaddr, sis->suspend_state[i], 4096);
+ ioaddr += 4096;
+ }
+
+ memset(sis->suspend_state[0], 0, 4096);
+
+ sis->irq = pci->irq;
+ pci_set_master(pci);
+
+ if (sis->codecs_present & SIS_PRIMARY_CODEC_PRESENT)
+ snd_ac97_resume(sis->ac97[0]);
+ if (sis->codecs_present & SIS_SECONDARY_CODEC_PRESENT)
+ snd_ac97_resume(sis->ac97[1]);
+ if (sis->codecs_present & SIS_TERTIARY_CODEC_PRESENT)
+ snd_ac97_resume(sis->ac97[2]);
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+ return 0;
+
+error:
+ snd_card_disconnect(card);
+ return -EIO;
+}
+#endif /* CONFIG_PM */
+
+static int sis_alloc_suspend(struct sis7019 *sis)
+{
+ int i;
+
+ /* We need 16K to store the internal wave engine state during a
+ * suspend, but we don't need it to be contiguous, so play nice
+ * with the memory system. We'll also use this area for a silence
+ * buffer.
+ */
+ for (i = 0; i < SIS_SUSPEND_PAGES; i++) {
+ sis->suspend_state[i] = kmalloc(4096, GFP_KERNEL);
+ if (!sis->suspend_state[i])
+ return -ENOMEM;
+ }
+ memset(sis->suspend_state[0], 0, 4096);
+
+ return 0;
+}
+
+static int __devinit sis_chip_create(struct snd_card *card,
+ struct pci_dev *pci)
+{
+ struct sis7019 *sis = card->private_data;
+ struct voice *voice;
+ static struct snd_device_ops ops = {
+ .dev_free = sis_dev_free,
+ };
+ int rc;
+ int i;
+
+ rc = pci_enable_device(pci);
+ if (rc)
+ goto error_out;
+
+ if (pci_set_dma_mask(pci, DMA_30BIT_MASK) < 0) {
+ printk(KERN_ERR "sis7019: architecture does not support "
+ "30-bit PCI busmaster DMA");
+ goto error_out_enabled;
+ }
+
+ memset(sis, 0, sizeof(*sis));
+ mutex_init(&sis->ac97_mutex);
+ spin_lock_init(&sis->voice_lock);
+ sis->card = card;
+ sis->pci = pci;
+ sis->irq = -1;
+ sis->ioport = pci_resource_start(pci, 0);
+
+ rc = pci_request_regions(pci, "SiS7019");
+ if (rc) {
+ printk(KERN_ERR "sis7019: unable request regions\n");
+ goto error_out_enabled;
+ }
+
+ rc = -EIO;
+ sis->ioaddr = ioremap_nocache(pci_resource_start(pci, 1), 0x4000);
+ if (!sis->ioaddr) {
+ printk(KERN_ERR "sis7019: unable to remap MMIO, aborting\n");
+ goto error_out_cleanup;
+ }
+
+ rc = sis_alloc_suspend(sis);
+ if (rc < 0) {
+ printk(KERN_ERR "sis7019: unable to allocate state storage\n");
+ goto error_out_cleanup;
+ }
+
+ rc = sis_chip_init(sis);
+ if (rc)
+ goto error_out_cleanup;
+
+ if (request_irq(pci->irq, sis_interrupt, IRQF_DISABLED|IRQF_SHARED,
+ card->shortname, sis)) {
+ printk(KERN_ERR "unable to allocate irq %d\n", sis->irq);
+ goto error_out_cleanup;
+ }
+
+ sis->irq = pci->irq;
+ pci_set_master(pci);
+
+ for (i = 0; i < 64; i++) {
+ voice = &sis->voices[i];
+ voice->num = i;
+ voice->ctrl_base = SIS_PLAY_DMA_ADDR(sis->ioaddr, i);
+ voice->wave_base = SIS_WAVE_ADDR(sis->ioaddr, i);
+ }
+
+ voice = &sis->capture_voice;
+ voice->flags = VOICE_CAPTURE;
+ voice->num = SIS_CAPTURE_CHAN_AC97_PCM_IN;
+ voice->ctrl_base = SIS_CAPTURE_DMA_ADDR(sis->ioaddr, voice->num);
+
+ rc = snd_device_new(card, SNDRV_DEV_LOWLEVEL, sis, &ops);
+ if (rc)
+ goto error_out_cleanup;
+
+ snd_card_set_dev(card, &pci->dev);
+
+ return 0;
+
+error_out_cleanup:
+ sis_chip_free(sis);
+
+error_out_enabled:
+ pci_disable_device(pci);
+
+error_out:
+ return rc;
+}
+
+static int __devinit snd_sis7019_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ struct snd_card *card;
+ struct sis7019 *sis;
+ int rc;
+
+ rc = -ENOENT;
+ if (!enable)
+ goto error_out;
+
+ rc = -ENOMEM;
+ card = snd_card_new(index, id, THIS_MODULE, sizeof(*sis));
+ if (!card)
+ goto error_out;
+
+ strcpy(card->driver, "SiS7019");
+ strcpy(card->shortname, "SiS7019");
+ rc = sis_chip_create(card, pci);
+ if (rc)
+ goto card_error_out;
+
+ sis = card->private_data;
+
+ rc = sis_mixer_create(sis);
+ if (rc)
+ goto card_error_out;
+
+ rc = sis_pcm_create(sis);
+ if (rc)
+ goto card_error_out;
+
+ snprintf(card->longname, sizeof(card->longname),
+ "%s Audio Accelerator with %s at 0x%lx, irq %d",
+ card->shortname, snd_ac97_get_short_name(sis->ac97[0]),
+ sis->ioport, sis->irq);
+
+ rc = snd_card_register(card);
+ if (rc)
+ goto card_error_out;
+
+ pci_set_drvdata(pci, card);
+ return 0;
+
+card_error_out:
+ snd_card_free(card);
+
+error_out:
+ return rc;
+}
+
+static void __devexit snd_sis7019_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver sis7019_driver = {
+ .name = "SiS7019",
+ .id_table = snd_sis7019_ids,
+ .probe = snd_sis7019_probe,
+ .remove = __devexit_p(snd_sis7019_remove),
+
+#ifdef CONFIG_PM
+ .suspend = sis_suspend,
+ .resume = sis_resume,
+#endif
+};
+
+static int __init sis7019_init(void)
+{
+ return pci_register_driver(&sis7019_driver);
+}
+
+static void __exit sis7019_exit(void)
+{
+ pci_unregister_driver(&sis7019_driver);
+}
+
+module_init(sis7019_init);
+module_exit(sis7019_exit);
diff --git a/sound/pci/sis7019.h b/sound/pci/sis7019.h
new file mode 100644
index 00000000000..013b6739a74
--- /dev/null
+++ b/sound/pci/sis7019.h
@@ -0,0 +1,342 @@
+#ifndef __sis7019_h__
+#define __sis7019_h__
+
+/*
+ * Definitions for SiS7019 Audio Accelerator
+ *
+ * Copyright (C) 2004-2007, David Dillow
+ * Written by David Dillow <dave@thedillows.org>
+ * Inspired by the Trident 4D-WaveDX/NX driver.
+ *
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * 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
+ */
+
+
+/* General Control Register */
+#define SIS_GCR 0x00
+#define SIS_GCR_MACRO_POWER_DOWN 0x80000000
+#define SIS_GCR_MODEM_ENABLE 0x00010000
+#define SIS_GCR_SOFTWARE_RESET 0x00000001
+
+/* General Interrupt Enable Register */
+#define SIS_GIER 0x04
+#define SIS_GIER_MODEM_TIMER_IRQ_ENABLE 0x00100000
+#define SIS_GIER_MODEM_RX_DMA_IRQ_ENABLE 0x00080000
+#define SIS_GIER_MODEM_TX_DMA_IRQ_ENABLE 0x00040000
+#define SIS_GIER_AC97_GPIO1_IRQ_ENABLE 0x00020000
+#define SIS_GIER_AC97_GPIO0_IRQ_ENABLE 0x00010000
+#define SIS_GIER_AC97_SAMPLE_TIMER_IRQ_ENABLE 0x00000010
+#define SIS_GIER_AUDIO_GLOBAL_TIMER_IRQ_ENABLE 0x00000008
+#define SIS_GIER_AUDIO_RECORD_DMA_IRQ_ENABLE 0x00000004
+#define SIS_GIER_AUDIO_PLAY_DMA_IRQ_ENABLE 0x00000002
+#define SIS_GIER_AUDIO_WAVE_ENGINE_IRQ_ENABLE 0x00000001
+
+/* General Interrupt Status Register */
+#define SIS_GISR 0x08
+#define SIS_GISR_MODEM_TIMER_IRQ_STATUS 0x00100000
+#define SIS_GISR_MODEM_RX_DMA_IRQ_STATUS 0x00080000
+#define SIS_GISR_MODEM_TX_DMA_IRQ_STATUS 0x00040000
+#define SIS_GISR_AC97_GPIO1_IRQ_STATUS 0x00020000
+#define SIS_GISR_AC97_GPIO0_IRQ_STATUS 0x00010000
+#define SIS_GISR_AC97_SAMPLE_TIMER_IRQ_STATUS 0x00000010
+#define SIS_GISR_AUDIO_GLOBAL_TIMER_IRQ_STATUS 0x00000008
+#define SIS_GISR_AUDIO_RECORD_DMA_IRQ_STATUS 0x00000004
+#define SIS_GISR_AUDIO_PLAY_DMA_IRQ_STATUS 0x00000002
+#define SIS_GISR_AUDIO_WAVE_ENGINE_IRQ_STATUS 0x00000001
+
+/* DMA Control Register */
+#define SIS_DMA_CSR 0x10
+#define SIS_DMA_CSR_PCI_SETTINGS 0x0000001d
+#define SIS_DMA_CSR_CONCURRENT_ENABLE 0x00000200
+#define SIS_DMA_CSR_PIPELINE_ENABLE 0x00000100
+#define SIS_DMA_CSR_RX_DRAIN_ENABLE 0x00000010
+#define SIS_DMA_CSR_RX_FILL_ENABLE 0x00000008
+#define SIS_DMA_CSR_TX_DRAIN_ENABLE 0x00000004
+#define SIS_DMA_CSR_TX_LOWPRI_FILL_ENABLE 0x00000002
+#define SIS_DMA_CSR_TX_HIPRI_FILL_ENABLE 0x00000001
+
+/* Playback Channel Start Registers */
+#define SIS_PLAY_START_A_REG 0x14
+#define SIS_PLAY_START_B_REG 0x18
+
+/* Playback Channel Stop Registers */
+#define SIS_PLAY_STOP_A_REG 0x1c
+#define SIS_PLAY_STOP_B_REG 0x20
+
+/* Recording Channel Start Register */
+#define SIS_RECORD_START_REG 0x24
+
+/* Recording Channel Stop Register */
+#define SIS_RECORD_STOP_REG 0x28
+
+/* Playback Interrupt Status Registers */
+#define SIS_PISR_A 0x2c
+#define SIS_PISR_B 0x30
+
+/* Recording Interrupt Status Register */
+#define SIS_RISR 0x34
+
+/* AC97 AC-link Playback Source Register */
+#define SIS_AC97_PSR 0x40
+#define SIS_AC97_PSR_MODEM_HEADSET_SRC_MIXER 0x0f000000
+#define SIS_AC97_PSR_MODEM_LINE2_SRC_MIXER 0x00f00000
+#define SIS_AC97_PSR_MODEM_LINE1_SRC_MIXER 0x000f0000
+#define SIS_AC97_PSR_PCM_LFR_SRC_MIXER 0x0000f000
+#define SIS_AC97_PSR_PCM_SURROUND_SRC_MIXER 0x00000f00
+#define SIS_AC97_PSR_PCM_CENTER_SRC_MIXER 0x000000f0
+#define SIS_AC97_PSR_PCM_LR_SRC_MIXER 0x0000000f
+
+/* AC97 AC-link Command Register */
+#define SIS_AC97_CMD 0x50
+#define SIS_AC97_CMD_DATA_MASK 0xffff0000
+#define SIS_AC97_CMD_REG_MASK 0x0000ff00
+#define SIS_AC97_CMD_CODEC3_READ 0x0000000d
+#define SIS_AC97_CMD_CODEC3_WRITE 0x0000000c
+#define SIS_AC97_CMD_CODEC2_READ 0x0000000b
+#define SIS_AC97_CMD_CODEC2_WRITE 0x0000000a
+#define SIS_AC97_CMD_CODEC_READ 0x00000009
+#define SIS_AC97_CMD_CODEC_WRITE 0x00000008
+#define SIS_AC97_CMD_CODEC_WARM_RESET 0x00000005
+#define SIS_AC97_CMD_CODEC_COLD_RESET 0x00000004
+#define SIS_AC97_CMD_DONE 0x00000000
+
+/* AC97 AC-link Semaphore Register */
+#define SIS_AC97_SEMA 0x54
+#define SIS_AC97_SEMA_BUSY 0x00000001
+#define SIS_AC97_SEMA_RELEASE 0x00000000
+
+/* AC97 AC-link Status Register */
+#define SIS_AC97_STATUS 0x58
+#define SIS_AC97_STATUS_AUDIO_D2_INACT_SECS 0x03f00000
+#define SIS_AC97_STATUS_MODEM_ALIVE 0x00002000
+#define SIS_AC97_STATUS_AUDIO_ALIVE 0x00001000
+#define SIS_AC97_STATUS_CODEC3_READY 0x00000400
+#define SIS_AC97_STATUS_CODEC2_READY 0x00000200
+#define SIS_AC97_STATUS_CODEC_READY 0x00000100
+#define SIS_AC97_STATUS_WARM_RESET 0x00000080
+#define SIS_AC97_STATUS_COLD_RESET 0x00000040
+#define SIS_AC97_STATUS_POWERED_DOWN 0x00000020
+#define SIS_AC97_STATUS_NORMAL 0x00000010
+#define SIS_AC97_STATUS_READ_EXPIRED 0x00000004
+#define SIS_AC97_STATUS_SEMAPHORE 0x00000002
+#define SIS_AC97_STATUS_BUSY 0x00000001
+
+/* AC97 AC-link Audio Configuration Register */
+#define SIS_AC97_CONF 0x5c
+#define SIS_AC97_CONF_AUDIO_ALIVE 0x80000000
+#define SIS_AC97_CONF_WARM_RESET_ENABLE 0x40000000
+#define SIS_AC97_CONF_PR6_ENABLE 0x20000000
+#define SIS_AC97_CONF_PR5_ENABLE 0x10000000
+#define SIS_AC97_CONF_PR4_ENABLE 0x08000000
+#define SIS_AC97_CONF_PR3_ENABLE 0x04000000
+#define SIS_AC97_CONF_PR2_PR7_ENABLE 0x02000000
+#define SIS_AC97_CONF_PR0_PR1_ENABLE 0x01000000
+#define SIS_AC97_CONF_AUTO_PM_ENABLE 0x00800000
+#define SIS_AC97_CONF_PCM_LFE_ENABLE 0x00080000
+#define SIS_AC97_CONF_PCM_SURROUND_ENABLE 0x00040000
+#define SIS_AC97_CONF_PCM_CENTER_ENABLE 0x00020000
+#define SIS_AC97_CONF_PCM_LR_ENABLE 0x00010000
+#define SIS_AC97_CONF_PCM_CAP_MIC_ENABLE 0x00002000
+#define SIS_AC97_CONF_PCM_CAP_LR_ENABLE 0x00001000
+#define SIS_AC97_CONF_PCM_CAP_MIC_FROM_CODEC3 0x00000200
+#define SIS_AC97_CONF_PCM_CAP_LR_FROM_CODEC3 0x00000100
+#define SIS_AC97_CONF_CODEC3_PM_VRM 0x00000080
+#define SIS_AC97_CONF_CODEC_PM_VRM 0x00000040
+#define SIS_AC97_CONF_CODEC3_VRA_ENABLE 0x00000020
+#define SIS_AC97_CONF_CODEC_VRA_ENABLE 0x00000010
+#define SIS_AC97_CONF_CODEC3_PM_EAC 0x00000008
+#define SIS_AC97_CONF_CODEC_PM_EAC 0x00000004
+#define SIS_AC97_CONF_CODEC3_EXISTS 0x00000002
+#define SIS_AC97_CONF_CODEC_EXISTS 0x00000001
+
+/* Playback Channel Sync Group registers */
+#define SIS_PLAY_SYNC_GROUP_A 0x80
+#define SIS_PLAY_SYNC_GROUP_B 0x84
+#define SIS_PLAY_SYNC_GROUP_C 0x88
+#define SIS_PLAY_SYNC_GROUP_D 0x8c
+#define SIS_MIXER_SYNC_GROUP 0x90
+
+/* Wave Engine Config and Control Register */
+#define SIS_WECCR 0xa0
+#define SIS_WECCR_TESTMODE_MASK 0x00300000
+#define SIS_WECCR_TESTMODE_NORMAL 0x00000000
+#define SIS_WECCR_TESTMODE_BYPASS_NSO_ALPHA 0x00100000
+#define SIS_WECCR_TESTMODE_BYPASS_FC 0x00200000
+#define SIS_WECCR_TESTMODE_BYPASS_WOL 0x00300000
+#define SIS_WECCR_RESONANCE_DELAY_MASK 0x00060000
+#define SIS_WECCR_RESONANCE_DELAY_NONE 0x00000000
+#define SIS_WECCR_RESONANCE_DELAY_FC_1F00 0x00020000
+#define SIS_WECCR_RESONANCE_DELAY_FC_1E00 0x00040000
+#define SIS_WECCR_RESONANCE_DELAY_FC_1C00 0x00060000
+#define SIS_WECCR_IGNORE_CHANNEL_PARMS 0x00010000
+#define SIS_WECCR_COMMAND_CHANNEL_ID_MASK 0x0003ff00
+#define SIS_WECCR_COMMAND_MASK 0x00000007
+#define SIS_WECCR_COMMAND_NONE 0x00000000
+#define SIS_WECCR_COMMAND_DONE 0x00000000
+#define SIS_WECCR_COMMAND_PAUSE 0x00000001
+#define SIS_WECCR_COMMAND_TOGGLE_VEG 0x00000002
+#define SIS_WECCR_COMMAND_TOGGLE_MEG 0x00000003
+#define SIS_WECCR_COMMAND_TOGGLE_VEG_MEG 0x00000004
+
+/* Wave Engine Volume Control Register */
+#define SIS_WEVCR 0xa4
+#define SIS_WEVCR_LEFT_MUSIC_ATTENUATION_MASK 0xff000000
+#define SIS_WEVCR_RIGHT_MUSIC_ATTENUATION_MASK 0x00ff0000
+#define SIS_WEVCR_LEFT_WAVE_ATTENUATION_MASK 0x0000ff00
+#define SIS_WEVCR_RIGHT_WAVE_ATTENUATION_MASK 0x000000ff
+
+/* Wave Engine Interrupt Status Registers */
+#define SIS_WEISR_A 0xa8
+#define SIS_WEISR_B 0xac
+
+
+/* Playback DMA parameters (paramter RAM) */
+#define SIS_PLAY_DMA_OFFSET 0x0000
+#define SIS_PLAY_DMA_SIZE 0x10
+#define SIS_PLAY_DMA_ADDR(addr, num) \
+ ((num * SIS_PLAY_DMA_SIZE) + (addr) + SIS_PLAY_DMA_OFFSET)
+
+#define SIS_PLAY_DMA_FORMAT_CSO 0x00
+#define SIS_PLAY_DMA_FORMAT_UNSIGNED 0x00080000
+#define SIS_PLAY_DMA_FORMAT_8BIT 0x00040000
+#define SIS_PLAY_DMA_FORMAT_MONO 0x00020000
+#define SIS_PLAY_DMA_CSO_MASK 0x0000ffff
+#define SIS_PLAY_DMA_BASE 0x04
+#define SIS_PLAY_DMA_CONTROL 0x08
+#define SIS_PLAY_DMA_STOP_AT_SSO 0x04000000
+#define SIS_PLAY_DMA_RELEASE 0x02000000
+#define SIS_PLAY_DMA_LOOP 0x01000000
+#define SIS_PLAY_DMA_INTR_AT_SSO 0x00080000
+#define SIS_PLAY_DMA_INTR_AT_ESO 0x00040000
+#define SIS_PLAY_DMA_INTR_AT_LEO 0x00020000
+#define SIS_PLAY_DMA_INTR_AT_MLP 0x00010000
+#define SIS_PLAY_DMA_LEO_MASK 0x0000ffff
+#define SIS_PLAY_DMA_SSO_ESO 0x0c
+#define SIS_PLAY_DMA_SSO_MASK 0xffff0000
+#define SIS_PLAY_DMA_ESO_MASK 0x0000ffff
+
+/* Capture DMA parameters (paramter RAM) */
+#define SIS_CAPTURE_DMA_OFFSET 0x0800
+#define SIS_CAPTURE_DMA_SIZE 0x10
+#define SIS_CAPTURE_DMA_ADDR(addr, num) \
+ ((num * SIS_CAPTURE_DMA_SIZE) + (addr) + SIS_CAPTURE_DMA_OFFSET)
+
+#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_0 0
+#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_1 1
+#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_2 2
+#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_3 3
+#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_4 4
+#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_5 5
+#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_6 6
+#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_7 7
+#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_8 8
+#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_9 9
+#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_10 10
+#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_11 11
+#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_12 12
+#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_13 13
+#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_14 14
+#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_15 15
+#define SIS_CAPTURE_CHAN_AC97_PCM_IN 16
+#define SIS_CAPTURE_CHAN_AC97_MIC_IN 17
+#define SIS_CAPTURE_CHAN_AC97_LINE1_IN 18
+#define SIS_CAPTURE_CHAN_AC97_LINE2_IN 19
+#define SIS_CAPTURE_CHAN_AC97_HANDSE_IN 20
+
+#define SIS_CAPTURE_DMA_FORMAT_CSO 0x00
+#define SIS_CAPTURE_DMA_MONO_MODE_MASK 0xc0000000
+#define SIS_CAPTURE_DMA_MONO_MODE_AVG 0x00000000
+#define SIS_CAPTURE_DMA_MONO_MODE_LEFT 0x40000000
+#define SIS_CAPTURE_DMA_MONO_MODE_RIGHT 0x80000000
+#define SIS_CAPTURE_DMA_FORMAT_UNSIGNED 0x00080000
+#define SIS_CAPTURE_DMA_FORMAT_8BIT 0x00040000
+#define SIS_CAPTURE_DMA_FORMAT_MONO 0x00020000
+#define SIS_CAPTURE_DMA_CSO_MASK 0x0000ffff
+#define SIS_CAPTURE_DMA_BASE 0x04
+#define SIS_CAPTURE_DMA_CONTROL 0x08
+#define SIS_CAPTURE_DMA_STOP_AT_SSO 0x04000000
+#define SIS_CAPTURE_DMA_RELEASE 0x02000000
+#define SIS_CAPTURE_DMA_LOOP 0x01000000
+#define SIS_CAPTURE_DMA_INTR_AT_LEO 0x00020000
+#define SIS_CAPTURE_DMA_INTR_AT_MLP 0x00010000
+#define SIS_CAPTURE_DMA_LEO_MASK 0x0000ffff
+#define SIS_CAPTURE_DMA_RESERVED 0x0c
+
+
+/* Mixer routing list start pointer (parameter RAM) */
+#define SIS_MIXER_START_OFFSET 0x1000
+#define SIS_MIXER_START_SIZE 0x04
+#define SIS_MIXER_START_ADDR(addr, num) \
+ ((num * SIS_MIXER_START_SIZE) + (addr) + SIS_MIXER_START_OFFSET)
+
+#define SIS_MIXER_START_MASK 0x0000007f
+
+/* Mixer routing table (parameter RAM) */
+#define SIS_MIXER_OFFSET 0x1400
+#define SIS_MIXER_SIZE 0x04
+#define SIS_MIXER_ADDR(addr, num) \
+ ((num * SIS_MIXER_SIZE) + (addr) + SIS_MIXER_OFFSET)
+
+#define SIS_MIXER_RIGHT_ATTENUTATION_MASK 0xff000000
+#define SIS_MIXER_RIGHT_NO_ATTEN 0xff000000
+#define SIS_MIXER_LEFT_ATTENUTATION_MASK 0x00ff0000
+#define SIS_MIXER_LEFT_NO_ATTEN 0x00ff0000
+#define SIS_MIXER_NEXT_ENTRY_MASK 0x00007f00
+#define SIS_MIXER_NEXT_ENTRY_NONE 0x00000000
+#define SIS_MIXER_DEST_MASK 0x0000007f
+#define SIS_MIXER_DEST_0 0x00000020
+#define SIS_MIXER_DEST_1 0x00000021
+#define SIS_MIXER_DEST_2 0x00000022
+#define SIS_MIXER_DEST_3 0x00000023
+#define SIS_MIXER_DEST_4 0x00000024
+#define SIS_MIXER_DEST_5 0x00000025
+#define SIS_MIXER_DEST_6 0x00000026
+#define SIS_MIXER_DEST_7 0x00000027
+#define SIS_MIXER_DEST_8 0x00000028
+#define SIS_MIXER_DEST_9 0x00000029
+#define SIS_MIXER_DEST_10 0x0000002a
+#define SIS_MIXER_DEST_11 0x0000002b
+#define SIS_MIXER_DEST_12 0x0000002c
+#define SIS_MIXER_DEST_13 0x0000002d
+#define SIS_MIXER_DEST_14 0x0000002e
+#define SIS_MIXER_DEST_15 0x0000002f
+
+/* Wave Engine Control Parameters (parameter RAM) */
+#define SIS_WAVE_OFFSET 0x2000
+#define SIS_WAVE_SIZE 0x40
+#define SIS_WAVE_ADDR(addr, num) \
+ ((num * SIS_WAVE_SIZE) + (addr) + SIS_WAVE_OFFSET)
+
+#define SIS_WAVE_GENERAL 0x00
+#define SIS_WAVE_GENERAL_WAVE_VOLUME 0x80000000
+#define SIS_WAVE_GENERAL_MUSIC_VOLUME 0x00000000
+#define SIS_WAVE_GENERAL_VOLUME_MASK 0x7f000000
+#define SIS_WAVE_GENERAL_ARTICULATION 0x04
+#define SIS_WAVE_GENERAL_ARTICULATION_DELTA_MASK 0x3fff0000
+#define SIS_WAVE_ARTICULATION 0x08
+#define SIS_WAVE_TIMER 0x0c
+#define SIS_WAVE_GENERATOR 0x10
+#define SIS_WAVE_CHANNEL_CONTROL 0x14
+#define SIS_WAVE_CHANNEL_CONTROL_FIRST_SAMPLE 0x80000000
+#define SIS_WAVE_CHANNEL_CONTROL_AMP_ENABLE 0x40000000
+#define SIS_WAVE_CHANNEL_CONTROL_FILTER_ENABLE 0x20000000
+#define SIS_WAVE_CHANNEL_CONTROL_INTERPOLATE_ENABLE 0x10000000
+#define SIS_WAVE_LFO_EG_CONTROL 0x18
+#define SIS_WAVE_LFO_EG_CONTROL_2 0x1c
+#define SIS_WAVE_LFO_EG_CONTROL_3 0x20
+#define SIS_WAVE_LFO_EG_CONTROL_4 0x24
+
+#endif /* __sis7019_h__ */
diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c
index 44a7f5fad57..0d3d305b0a0 100644
--- a/sound/pci/sonicvibes.c
+++ b/sound/pci/sonicvibes.c
@@ -22,7 +22,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/trident/Makefile b/sound/pci/trident/Makefile
index 65f2c218324..88676b50f38 100644
--- a/sound/pci/trident/Makefile
+++ b/sound/pci/trident/Makefile
@@ -4,16 +4,6 @@
#
snd-trident-objs := trident.o trident_main.o trident_memory.o
-snd-trident-synth-objs := trident_synth.o
-
-#
-# this function returns:
-# "m" - CONFIG_SND_SEQUENCER is m
-# <empty string> - CONFIG_SND_SEQUENCER is undefined
-# otherwise parameter #1 value
-#
-sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
# Toplevel Module Dependency
obj-$(CONFIG_SND_TRIDENT) += snd-trident.o
-obj-$(call sequencer,$(CONFIG_SND_TRIDENT)) += snd-trident-synth.o
diff --git a/sound/pci/trident/trident.c b/sound/pci/trident/trident.c
index 84884567df6..d94b16ffb38 100644
--- a/sound/pci/trident/trident.c
+++ b/sound/pci/trident/trident.c
@@ -21,7 +21,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/time.h>
@@ -155,13 +154,6 @@ static int __devinit snd_trident_probe(struct pci_dev *pci,
return err;
}
-#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
- if ((err = snd_trident_attach_synthesizer(trident)) < 0) {
- snd_card_free(card);
- return err;
- }
-#endif
-
snd_trident_create_gameport(trident);
if ((err = snd_card_register(card)) < 0) {
diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c
index a235e034a69..71138ff9b31 100644
--- a/sound/pci/trident/trident_main.c
+++ b/sound/pci/trident/trident_main.c
@@ -27,7 +27,6 @@
* SiS7018 S/PDIF support by Thomas Winischhofer <thomas@winischhofer.net>
*/
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
@@ -436,7 +435,7 @@ static void snd_trident_free_synth_channel(struct snd_trident *trident, int chan
Description: This routine will complete and write the 5 hardware channel
registers to hardware.
- Paramters: trident - pointer to target device class for 4DWave.
+ Parameters: trident - pointer to target device class for 4DWave.
voice - synthesizer voice structure
Each register field.
@@ -514,7 +513,7 @@ EXPORT_SYMBOL(snd_trident_write_voice_regs);
Description: This routine will write the new CSO offset
register to hardware.
- Paramters: trident - pointer to target device class for 4DWave.
+ Parameters: trident - pointer to target device class for 4DWave.
voice - synthesizer voice structure
CSO - new CSO value
@@ -540,7 +539,7 @@ static void snd_trident_write_cso_reg(struct snd_trident * trident,
Description: This routine will write the new ESO offset
register to hardware.
- Paramters: trident - pointer to target device class for 4DWave.
+ Parameters: trident - pointer to target device class for 4DWave.
voice - synthesizer voice structure
ESO - new ESO value
@@ -566,7 +565,7 @@ static void snd_trident_write_eso_reg(struct snd_trident * trident,
Description: This routine will write the new voice volume
register to hardware.
- Paramters: trident - pointer to target device class for 4DWave.
+ Parameters: trident - pointer to target device class for 4DWave.
voice - synthesizer voice structure
Vol - new voice volume
@@ -597,7 +596,7 @@ static void snd_trident_write_vol_reg(struct snd_trident * trident,
Description: This routine will write the new voice pan
register to hardware.
- Paramters: trident - pointer to target device class for 4DWave.
+ Parameters: trident - pointer to target device class for 4DWave.
voice - synthesizer voice structure
Pan - new pan value
@@ -619,7 +618,7 @@ static void snd_trident_write_pan_reg(struct snd_trident * trident,
Description: This routine will write the new reverb volume
register to hardware.
- Paramters: trident - pointer to target device class for 4DWave.
+ Parameters: trident - pointer to target device class for 4DWave.
voice - synthesizer voice structure
RVol - new reverb volume
@@ -643,7 +642,7 @@ static void snd_trident_write_rvol_reg(struct snd_trident * trident,
Description: This routine will write the new chorus volume
register to hardware.
- Paramters: trident - pointer to target device class for 4DWave.
+ Parameters: trident - pointer to target device class for 4DWave.
voice - synthesizer voice structure
CVol - new chorus volume
@@ -666,7 +665,7 @@ static void snd_trident_write_cvol_reg(struct snd_trident * trident,
Description: This routine converts rate in HZ to hardware delta value.
- Paramters: trident - pointer to target device class for 4DWave.
+ Parameters: trident - pointer to target device class for 4DWave.
rate - Real or Virtual channel number.
Returns: Delta value.
@@ -696,7 +695,7 @@ static unsigned int snd_trident_convert_rate(unsigned int rate)
Description: This routine converts rate in HZ to hardware delta value.
- Paramters: trident - pointer to target device class for 4DWave.
+ Parameters: trident - pointer to target device class for 4DWave.
rate - Real or Virtual channel number.
Returns: Delta value.
@@ -726,7 +725,7 @@ static unsigned int snd_trident_convert_adc_rate(unsigned int rate)
Description: This routine converts rate in HZ to spurious threshold.
- Paramters: trident - pointer to target device class for 4DWave.
+ Parameters: trident - pointer to target device class for 4DWave.
rate - Real or Virtual channel number.
Returns: Delta value.
@@ -748,7 +747,7 @@ static unsigned int snd_trident_spurious_threshold(unsigned int rate,
Description: This routine returns a control mode for a PCM channel.
- Paramters: trident - pointer to target device class for 4DWave.
+ Parameters: trident - pointer to target device class for 4DWave.
substream - PCM substream
Returns: Control value.
@@ -781,7 +780,7 @@ static unsigned int snd_trident_control_mode(struct snd_pcm_substream *substream
Description: Device I/O control handler for playback/capture parameters.
- Paramters: substream - PCM substream class
+ Parameters: substream - PCM substream class
cmd - what ioctl message to process
arg - additional message infoarg
@@ -1664,7 +1663,7 @@ static snd_pcm_uframes_t snd_trident_playback_pointer(struct snd_pcm_substream *
Description: This routine return the capture position
- Paramters: pcm1 - PCM device class
+ Parameters: pcm1 - PCM device class
Returns: position of buffer
@@ -2157,7 +2156,7 @@ static struct snd_pcm_ops snd_trident_spdif_7018_ops = {
Description: This routine registers the 4DWave device for PCM support.
- Paramters: trident - pointer to target device class for 4DWave.
+ Parameters: trident - pointer to target device class for 4DWave.
Returns: None
@@ -2215,7 +2214,7 @@ int __devinit snd_trident_pcm(struct snd_trident * trident,
Description: This routine registers the 4DWave device for foldback PCM support.
- Paramters: trident - pointer to target device class for 4DWave.
+ Parameters: trident - pointer to target device class for 4DWave.
Returns: None
@@ -2272,7 +2271,7 @@ int __devinit snd_trident_foldback_pcm(struct snd_trident * trident,
Description: This routine registers the 4DWave-NX device for SPDIF support.
- Paramters: trident - pointer to target device class for 4DWave-NX.
+ Parameters: trident - pointer to target device class for 4DWave-NX.
Returns: None
@@ -2956,7 +2955,7 @@ static int snd_trident_pcm_mixer_free(struct snd_trident *trident, struct snd_tr
Description: This routine registers the 4DWave device for mixer support.
- Paramters: trident - pointer to target device class for 4DWave.
+ Parameters: trident - pointer to target device class for 4DWave.
Returns: None
@@ -3313,12 +3312,6 @@ static void snd_trident_proc_read(struct snd_info_entry *entry,
snd_iprintf(buffer, "Memory Free : %d\n", snd_util_mem_avail(trident->tlb.memhdr));
}
}
-#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
- snd_iprintf(buffer,"\nWavetable Synth\n");
- snd_iprintf(buffer, "Memory Maximum : %d\n", trident->synth.max_size);
- snd_iprintf(buffer, "Memory Used : %d\n", trident->synth.current_size);
- snd_iprintf(buffer, "Memory Free : %d\n", (trident->synth.max_size-trident->synth.current_size));
-#endif
}
static void __devinit snd_trident_proc_init(struct snd_trident * trident)
@@ -3344,7 +3337,7 @@ static int snd_trident_dev_free(struct snd_device *device)
Description: Allocate and set up the TLB page table on 4D NX.
Each entry has 4 bytes (physical PCI address).
- Paramters: trident - pointer to target device class for 4DWave.
+ Parameters: trident - pointer to target device class for 4DWave.
Returns: 0 or negative error code
@@ -3521,7 +3514,7 @@ static int snd_trident_sis_init(struct snd_trident *trident)
Description: This routine will create the device specific class for
the 4DWave card. It will also perform basic initialization.
- Paramters: card - which card to create
+ Parameters: card - which card to create
pci - interface to PCI bus resource info
dma1ptr - playback dma buffer
dma2ptr - capture dma buffer
@@ -3667,7 +3660,7 @@ int __devinit snd_trident_create(struct snd_card *card,
Description: This routine will free the device specific class for
the 4DWave card.
- Paramters: trident - device specific private data for 4DWave card
+ Parameters: trident - device specific private data for 4DWave card
Returns: None.
@@ -3705,7 +3698,7 @@ static int snd_trident_free(struct snd_trident *trident)
Description: ISR for Trident 4DWave device
- Paramters: trident - device specific private data for 4DWave card
+ Parameters: trident - device specific private data for 4DWave card
Problems: It seems that Trident chips generates interrupts more than
one time in special cases. The spurious interrupts are
@@ -3815,28 +3808,6 @@ static irqreturn_t snd_trident_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-/*---------------------------------------------------------------------------
- snd_trident_attach_synthesizer
-
- Description: Attach synthesizer hooks
-
- Paramters: trident - device specific private data for 4DWave card
-
- Returns: None.
-
- ---------------------------------------------------------------------------*/
-int snd_trident_attach_synthesizer(struct snd_trident *trident)
-{
-#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
- if (snd_seq_device_new(trident->card, 1, SNDRV_SEQ_DEV_ID_TRIDENT,
- sizeof(struct snd_trident *), &trident->seq_dev) >= 0) {
- strcpy(trident->seq_dev->name, "4DWave");
- *(struct snd_trident **)SNDRV_SEQ_DEVICE_ARGPTR(trident->seq_dev) = trident;
- }
-#endif
- return 0;
-}
-
struct snd_trident_voice *snd_trident_alloc_voice(struct snd_trident * trident, int type, int client, int port)
{
struct snd_trident_voice *pvoice;
diff --git a/sound/pci/trident/trident_memory.c b/sound/pci/trident/trident_memory.c
index 847b8c6d5c0..df9b487fa17 100644
--- a/sound/pci/trident/trident_memory.c
+++ b/sound/pci/trident/trident_memory.c
@@ -23,7 +23,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/pci.h>
#include <linux/time.h>
diff --git a/sound/pci/trident/trident_synth.c b/sound/pci/trident/trident_synth.c
deleted file mode 100644
index 9b7dee84743..00000000000
--- a/sound/pci/trident/trident_synth.c
+++ /dev/null
@@ -1,1024 +0,0 @@
-/*
- * Routines for Trident 4DWave NX/DX soundcards - Synthesizer
- * Copyright (c) by Scott McNab <jedi@tartarus.uwa.edu.au>
- *
- *
- * 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
- *
- */
-
-#include <sound/driver.h>
-#include <asm/io.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/pci.h>
-#include <sound/core.h>
-#include <sound/trident.h>
-#include <sound/seq_device.h>
-
-MODULE_AUTHOR("Scott McNab <jedi@tartarus.uwa.edu.au>");
-MODULE_DESCRIPTION("Routines for Trident 4DWave NX/DX soundcards - Synthesizer");
-MODULE_LICENSE("GPL");
-
-/* linear to log pan conversion table (4.2 channel attenuation format) */
-static unsigned int pan_table[63] = {
- 7959, 7733, 7514, 7301, 7093, 6892, 6697, 6507,
- 6322, 6143, 5968, 5799, 5634, 5475, 5319, 5168,
- 5022, 4879, 4741, 4606, 4475, 4349, 4225, 4105,
- 3989, 3876, 3766, 3659, 3555, 3454, 3356, 3261,
- 3168, 3078, 2991, 2906, 2824, 2744, 2666, 2590,
- 2517, 2445, 2376, 2308, 2243, 2179, 2117, 2057,
- 1999, 1942, 1887, 1833, 1781, 1731, 1682, 1634,
- 1588, 1543, 1499, 1456, 1415, 1375, 1336
-};
-
-#define LOG_TABLE_SIZE 386
-
-/* Linear half-attenuation to log conversion table in the format:
- * {linear volume, logarithmic attenuation equivalent}, ...
- *
- * Provides conversion from a linear half-volume value in the range
- * [0,8192] to a logarithmic attenuation value in the range 0 to 6.02dB.
- * Halving the linear volume is equivalent to an additional 6dB of
- * logarithmic attenuation. The algorithm used in log_from_linear()
- * therefore uses this table as follows:
- *
- * - loop and for every time the volume is less than half the maximum
- * volume (16384), add another 6dB and halve the maximum value used
- * for this comparison.
- * - when the volume is greater than half the maximum volume, take
- * the difference of the volume to half volume (in the range [0,8192])
- * and look up the log_table[] to find the nearest entry.
- * - take the logarithic component of this entry and add it to the
- * resulting attenuation.
- *
- * Thus this routine provides a linear->log conversion for a range of
- * [0,16384] using only 386 table entries
- *
- * Note: although this table stores log attenuation in 8.8 format, values
- * were only calculated for 6 bits fractional precision, since that is
- * the most precision offered by the trident hardware.
- */
-
-static unsigned short log_table[LOG_TABLE_SIZE*2] =
-{
- 4, 0x0604, 19, 0x0600, 34, 0x05fc,
- 49, 0x05f8, 63, 0x05f4, 78, 0x05f0, 93, 0x05ec, 108, 0x05e8,
- 123, 0x05e4, 138, 0x05e0, 153, 0x05dc, 168, 0x05d8, 183, 0x05d4,
- 198, 0x05d0, 213, 0x05cc, 228, 0x05c8, 244, 0x05c4, 259, 0x05c0,
- 274, 0x05bc, 289, 0x05b8, 304, 0x05b4, 320, 0x05b0, 335, 0x05ac,
- 350, 0x05a8, 366, 0x05a4, 381, 0x05a0, 397, 0x059c, 412, 0x0598,
- 428, 0x0594, 443, 0x0590, 459, 0x058c, 474, 0x0588, 490, 0x0584,
- 506, 0x0580, 521, 0x057c, 537, 0x0578, 553, 0x0574, 568, 0x0570,
- 584, 0x056c, 600, 0x0568, 616, 0x0564, 632, 0x0560, 647, 0x055c,
- 663, 0x0558, 679, 0x0554, 695, 0x0550, 711, 0x054c, 727, 0x0548,
- 743, 0x0544, 759, 0x0540, 776, 0x053c, 792, 0x0538, 808, 0x0534,
- 824, 0x0530, 840, 0x052c, 857, 0x0528, 873, 0x0524, 889, 0x0520,
- 906, 0x051c, 922, 0x0518, 938, 0x0514, 955, 0x0510, 971, 0x050c,
- 988, 0x0508, 1004, 0x0504, 1021, 0x0500, 1037, 0x04fc, 1054, 0x04f8,
- 1071, 0x04f4, 1087, 0x04f0, 1104, 0x04ec, 1121, 0x04e8, 1138, 0x04e4,
- 1154, 0x04e0, 1171, 0x04dc, 1188, 0x04d8, 1205, 0x04d4, 1222, 0x04d0,
- 1239, 0x04cc, 1256, 0x04c8, 1273, 0x04c4, 1290, 0x04c0, 1307, 0x04bc,
- 1324, 0x04b8, 1341, 0x04b4, 1358, 0x04b0, 1376, 0x04ac, 1393, 0x04a8,
- 1410, 0x04a4, 1427, 0x04a0, 1445, 0x049c, 1462, 0x0498, 1479, 0x0494,
- 1497, 0x0490, 1514, 0x048c, 1532, 0x0488, 1549, 0x0484, 1567, 0x0480,
- 1584, 0x047c, 1602, 0x0478, 1620, 0x0474, 1637, 0x0470, 1655, 0x046c,
- 1673, 0x0468, 1690, 0x0464, 1708, 0x0460, 1726, 0x045c, 1744, 0x0458,
- 1762, 0x0454, 1780, 0x0450, 1798, 0x044c, 1816, 0x0448, 1834, 0x0444,
- 1852, 0x0440, 1870, 0x043c, 1888, 0x0438, 1906, 0x0434, 1924, 0x0430,
- 1943, 0x042c, 1961, 0x0428, 1979, 0x0424, 1997, 0x0420, 2016, 0x041c,
- 2034, 0x0418, 2053, 0x0414, 2071, 0x0410, 2089, 0x040c, 2108, 0x0408,
- 2127, 0x0404, 2145, 0x0400, 2164, 0x03fc, 2182, 0x03f8, 2201, 0x03f4,
- 2220, 0x03f0, 2239, 0x03ec, 2257, 0x03e8, 2276, 0x03e4, 2295, 0x03e0,
- 2314, 0x03dc, 2333, 0x03d8, 2352, 0x03d4, 2371, 0x03d0, 2390, 0x03cc,
- 2409, 0x03c8, 2428, 0x03c4, 2447, 0x03c0, 2466, 0x03bc, 2485, 0x03b8,
- 2505, 0x03b4, 2524, 0x03b0, 2543, 0x03ac, 2562, 0x03a8, 2582, 0x03a4,
- 2601, 0x03a0, 2621, 0x039c, 2640, 0x0398, 2660, 0x0394, 2679, 0x0390,
- 2699, 0x038c, 2718, 0x0388, 2738, 0x0384, 2758, 0x0380, 2777, 0x037c,
- 2797, 0x0378, 2817, 0x0374, 2837, 0x0370, 2857, 0x036c, 2876, 0x0368,
- 2896, 0x0364, 2916, 0x0360, 2936, 0x035c, 2956, 0x0358, 2976, 0x0354,
- 2997, 0x0350, 3017, 0x034c, 3037, 0x0348, 3057, 0x0344, 3077, 0x0340,
- 3098, 0x033c, 3118, 0x0338, 3138, 0x0334, 3159, 0x0330, 3179, 0x032c,
- 3200, 0x0328, 3220, 0x0324, 3241, 0x0320, 3261, 0x031c, 3282, 0x0318,
- 3303, 0x0314, 3323, 0x0310, 3344, 0x030c, 3365, 0x0308, 3386, 0x0304,
- 3406, 0x0300, 3427, 0x02fc, 3448, 0x02f8, 3469, 0x02f4, 3490, 0x02f0,
- 3511, 0x02ec, 3532, 0x02e8, 3553, 0x02e4, 3575, 0x02e0, 3596, 0x02dc,
- 3617, 0x02d8, 3638, 0x02d4, 3660, 0x02d0, 3681, 0x02cc, 3702, 0x02c8,
- 3724, 0x02c4, 3745, 0x02c0, 3767, 0x02bc, 3788, 0x02b8, 3810, 0x02b4,
- 3831, 0x02b0, 3853, 0x02ac, 3875, 0x02a8, 3896, 0x02a4, 3918, 0x02a0,
- 3940, 0x029c, 3962, 0x0298, 3984, 0x0294, 4006, 0x0290, 4028, 0x028c,
- 4050, 0x0288, 4072, 0x0284, 4094, 0x0280, 4116, 0x027c, 4138, 0x0278,
- 4160, 0x0274, 4182, 0x0270, 4205, 0x026c, 4227, 0x0268, 4249, 0x0264,
- 4272, 0x0260, 4294, 0x025c, 4317, 0x0258, 4339, 0x0254, 4362, 0x0250,
- 4384, 0x024c, 4407, 0x0248, 4430, 0x0244, 4453, 0x0240, 4475, 0x023c,
- 4498, 0x0238, 4521, 0x0234, 4544, 0x0230, 4567, 0x022c, 4590, 0x0228,
- 4613, 0x0224, 4636, 0x0220, 4659, 0x021c, 4682, 0x0218, 4705, 0x0214,
- 4728, 0x0210, 4752, 0x020c, 4775, 0x0208, 4798, 0x0204, 4822, 0x0200,
- 4845, 0x01fc, 4869, 0x01f8, 4892, 0x01f4, 4916, 0x01f0, 4939, 0x01ec,
- 4963, 0x01e8, 4987, 0x01e4, 5010, 0x01e0, 5034, 0x01dc, 5058, 0x01d8,
- 5082, 0x01d4, 5106, 0x01d0, 5130, 0x01cc, 5154, 0x01c8, 5178, 0x01c4,
- 5202, 0x01c0, 5226, 0x01bc, 5250, 0x01b8, 5274, 0x01b4, 5299, 0x01b0,
- 5323, 0x01ac, 5347, 0x01a8, 5372, 0x01a4, 5396, 0x01a0, 5420, 0x019c,
- 5445, 0x0198, 5469, 0x0194, 5494, 0x0190, 5519, 0x018c, 5543, 0x0188,
- 5568, 0x0184, 5593, 0x0180, 5618, 0x017c, 5643, 0x0178, 5668, 0x0174,
- 5692, 0x0170, 5717, 0x016c, 5743, 0x0168, 5768, 0x0164, 5793, 0x0160,
- 5818, 0x015c, 5843, 0x0158, 5868, 0x0154, 5894, 0x0150, 5919, 0x014c,
- 5945, 0x0148, 5970, 0x0144, 5995, 0x0140, 6021, 0x013c, 6047, 0x0138,
- 6072, 0x0134, 6098, 0x0130, 6124, 0x012c, 6149, 0x0128, 6175, 0x0124,
- 6201, 0x0120, 6227, 0x011c, 6253, 0x0118, 6279, 0x0114, 6305, 0x0110,
- 6331, 0x010c, 6357, 0x0108, 6384, 0x0104, 6410, 0x0100, 6436, 0x00fc,
- 6462, 0x00f8, 6489, 0x00f4, 6515, 0x00f0, 6542, 0x00ec, 6568, 0x00e8,
- 6595, 0x00e4, 6621, 0x00e0, 6648, 0x00dc, 6675, 0x00d8, 6702, 0x00d4,
- 6728, 0x00d0, 6755, 0x00cc, 6782, 0x00c8, 6809, 0x00c4, 6836, 0x00c0,
- 6863, 0x00bc, 6890, 0x00b8, 6917, 0x00b4, 6945, 0x00b0, 6972, 0x00ac,
- 6999, 0x00a8, 7027, 0x00a4, 7054, 0x00a0, 7081, 0x009c, 7109, 0x0098,
- 7136, 0x0094, 7164, 0x0090, 7192, 0x008c, 7219, 0x0088, 7247, 0x0084,
- 7275, 0x0080, 7303, 0x007c, 7331, 0x0078, 7359, 0x0074, 7387, 0x0070,
- 7415, 0x006c, 7443, 0x0068, 7471, 0x0064, 7499, 0x0060, 7527, 0x005c,
- 7556, 0x0058, 7584, 0x0054, 7613, 0x0050, 7641, 0x004c, 7669, 0x0048,
- 7698, 0x0044, 7727, 0x0040, 7755, 0x003c, 7784, 0x0038, 7813, 0x0034,
- 7842, 0x0030, 7870, 0x002c, 7899, 0x0028, 7928, 0x0024, 7957, 0x0020,
- 7986, 0x001c, 8016, 0x0018, 8045, 0x0014, 8074, 0x0010, 8103, 0x000c,
- 8133, 0x0008, 8162, 0x0004, 8192, 0x0000
-};
-
-static unsigned short lookup_volume_table( unsigned short value )
-{
- /* This code is an optimised version of:
- * int i = 0;
- * while( volume_table[i*2] < value )
- * i++;
- * return volume_table[i*2+1];
- */
- unsigned short *ptr = log_table;
- while( *ptr < value )
- ptr += 2;
- return *(ptr+1);
-}
-
-/* this function calculates a 8.8 fixed point logarithmic attenuation
- * value from a linear volume value in the range 0 to 16384 */
-static unsigned short log_from_linear( unsigned short value )
-{
- if (value >= 16384)
- return 0x0000;
- if (value) {
- unsigned short result = 0;
- int v, c;
- for( c = 0, v = 8192; c < 14; c++, v >>= 1 ) {
- if( value >= v ) {
- result += lookup_volume_table( (value - v) << c );
- return result;
- }
- result += 0x0605; /* 6.0205 (result of -20*log10(0.5)) */
- }
- }
- return 0xffff;
-}
-
-/*
- * Sample handling operations
- */
-
-static void sample_start(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_position_t position);
-static void sample_stop(struct snd_trident * trident, struct snd_trident_voice * voice, int mode);
-static void sample_freq(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_frequency_t freq);
-static void sample_volume(struct snd_trident * trident, struct snd_trident_voice * voice, struct snd_seq_ev_volume * volume);
-static void sample_loop(struct snd_trident * trident, struct snd_trident_voice * voice, struct snd_seq_ev_loop * loop);
-static void sample_pos(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_position_t position);
-static void sample_private1(struct snd_trident * trident, struct snd_trident_voice * voice, unsigned char *data);
-
-static struct snd_trident_sample_ops sample_ops =
-{
- sample_start,
- sample_stop,
- sample_freq,
- sample_volume,
- sample_loop,
- sample_pos,
- sample_private1
-};
-
-static void snd_trident_simple_init(struct snd_trident_voice * voice)
-{
- //voice->handler_wave = interrupt_wave;
- //voice->handler_volume = interrupt_volume;
- //voice->handler_effect = interrupt_effect;
- //voice->volume_change = NULL;
- voice->sample_ops = &sample_ops;
-}
-
-static void sample_start(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_position_t position)
-{
- struct simple_instrument *simple;
- struct snd_seq_kinstr *instr;
- unsigned long flags;
- unsigned int loop_start, loop_end, sample_start, sample_end, start_offset;
- unsigned int value;
- unsigned int shift = 0;
-
- instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1);
- if (instr == NULL)
- return;
- voice->instr = instr->instr; /* copy ID to speedup aliases */
- simple = KINSTR_DATA(instr);
-
- spin_lock_irqsave(&trident->reg_lock, flags);
-
- if (trident->device == TRIDENT_DEVICE_ID_SI7018)
- voice->GVSel = 1; /* route to Wave volume */
-
- voice->CTRL = 0;
- voice->Alpha = 0;
- voice->FMS = 0;
-
- loop_start = simple->loop_start >> 4;
- loop_end = simple->loop_end >> 4;
- sample_start = (simple->start + position) >> 4;
- if( sample_start >= simple->size )
- sample_start = simple->start >> 4;
- sample_end = simple->size;
- start_offset = position >> 4;
-
- if (simple->format & SIMPLE_WAVE_16BIT) {
- voice->CTRL |= 8;
- shift++;
- }
- if (simple->format & SIMPLE_WAVE_STEREO) {
- voice->CTRL |= 4;
- shift++;
- }
- if (!(simple->format & SIMPLE_WAVE_UNSIGNED))
- voice->CTRL |= 2;
-
- voice->LBA = simple->address.memory;
-
- if (simple->format & SIMPLE_WAVE_LOOP) {
- voice->CTRL |= 1;
- voice->LBA += loop_start << shift;
- if( start_offset >= loop_start ) {
- voice->CSO = start_offset - loop_start;
- voice->negCSO = 0;
- } else {
- voice->CSO = loop_start - start_offset;
- voice->negCSO = 1;
- }
- voice->ESO = loop_end - loop_start - 1;
- } else {
- voice->LBA += start_offset << shift;
- voice->CSO = sample_start;
- voice->ESO = sample_end - 1;
- voice->negCSO = 0;
- }
-
- if (voice->flags & SNDRV_TRIDENT_VFLG_RUNNING) {
- snd_trident_stop_voice(trident, voice->number);
- voice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING;
- }
-
- /* set CSO sign */
- value = inl(TRID_REG(trident, T4D_SIGN_CSO_A));
- if( voice->negCSO ) {
- value |= 1 << (voice->number&31);
- } else {
- value &= ~(1 << (voice->number&31));
- }
- outl(value,TRID_REG(trident, T4D_SIGN_CSO_A));
-
- voice->Attribute = 0;
- snd_trident_write_voice_regs(trident, voice);
- snd_trident_start_voice(trident, voice->number);
- voice->flags |= SNDRV_TRIDENT_VFLG_RUNNING;
- spin_unlock_irqrestore(&trident->reg_lock, flags);
- snd_seq_instr_free_use(trident->synth.ilist, instr);
-}
-
-static void sample_stop(struct snd_trident * trident, struct snd_trident_voice * voice, int mode)
-{
- unsigned long flags;
-
- if (!(voice->flags & SNDRV_TRIDENT_VFLG_RUNNING))
- return;
-
- switch (mode) {
- default:
- spin_lock_irqsave(&trident->reg_lock, flags);
- snd_trident_stop_voice(trident, voice->number);
- voice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING;
- spin_unlock_irqrestore(&trident->reg_lock, flags);
- break;
- case SAMPLE_STOP_LOOP: /* disable loop only */
- voice->CTRL &= ~1;
- spin_lock_irqsave(&trident->reg_lock, flags);
- outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
- outw((((voice->CTRL << 12) | (voice->EC & 0x0fff)) & 0xffff), CH_GVSEL_PAN_VOL_CTRL_EC);
- spin_unlock_irqrestore(&trident->reg_lock, flags);
- break;
- }
-}
-
-static void sample_freq(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_frequency_t freq)
-{
- unsigned long flags;
- freq >>= 4;
-
- spin_lock_irqsave(&trident->reg_lock, flags);
- if (freq == 44100)
- voice->Delta = 0xeb3;
- else if (freq == 8000)
- voice->Delta = 0x2ab;
- else if (freq == 48000)
- voice->Delta = 0x1000;
- else
- voice->Delta = (((freq << 12) + freq) / 48000) & 0x0000ffff;
-
- outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
- if (trident->device == TRIDENT_DEVICE_ID_NX) {
- outb((unsigned char) voice->Delta, TRID_REG(trident, CH_NX_DELTA_CSO + 3));
- outb((unsigned char) (voice->Delta >> 8), TRID_REG(trident, CH_NX_DELTA_ESO + 3));
- } else {
- outw((unsigned short) voice->Delta, TRID_REG(trident, CH_DX_ESO_DELTA));
- }
-
- spin_unlock_irqrestore(&trident->reg_lock, flags);
-}
-
-static void sample_volume(struct snd_trident * trident, struct snd_trident_voice * voice, struct snd_seq_ev_volume * volume)
-{
- unsigned long flags;
- unsigned short value;
-
- spin_lock_irqsave(&trident->reg_lock, flags);
- voice->GVSel = 0; /* use global music volume */
- voice->FMC = 0x03; /* fixme: can we do something useful with FMC? */
- if (volume->volume >= 0) {
- volume->volume &= 0x3fff;
- /* linear volume -> logarithmic attenuation conversion
- * uses EC register for greater resolution (6.6 bits) than Vol register (5.3 bits)
- * Vol register used when additional attenuation is required */
- voice->RVol = 0;
- voice->CVol = 0;
- value = log_from_linear( volume->volume );
- voice->Vol = 0;
- voice->EC = (value & 0x3fff) >> 2;
- if (value > 0x3fff) {
- voice->EC |= 0xfc0;
- if (value < 0x5f00 )
- voice->Vol = ((value >> 8) - 0x3f) << 5;
- else {
- voice->Vol = 0x3ff;
- voice->EC = 0xfff;
- }
- }
- }
- if (volume->lr >= 0) {
- volume->lr &= 0x3fff;
- /* approximate linear pan by attenuating channels */
- if (volume->lr >= 0x2000) { /* attenuate left (pan right) */
- value = 0x3fff - volume->lr;
- for (voice->Pan = 0; voice->Pan < 63; voice->Pan++ )
- if (value >= pan_table[voice->Pan] )
- break;
- } else { /* attenuate right (pan left) */
- for (voice->Pan = 0; voice->Pan < 63; voice->Pan++ )
- if ((unsigned int)volume->lr >= pan_table[voice->Pan] )
- break;
- voice->Pan |= 0x40;
- }
- }
- outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
- outl((voice->GVSel << 31) | ((voice->Pan & 0x0000007f) << 24) |
- ((voice->Vol & 0x000000ff) << 16) | ((voice->CTRL & 0x0000000f) << 12) |
- (voice->EC & 0x00000fff), TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC));
- value = ((voice->FMC & 0x03) << 14) | ((voice->RVol & 0x7f) << 7) | (voice->CVol & 0x7f);
- outw(value, TRID_REG(trident, CH_DX_FMC_RVOL_CVOL));
- spin_unlock_irqrestore(&trident->reg_lock, flags);
-}
-
-static void sample_loop(struct snd_trident * trident, struct snd_trident_voice * voice, struct snd_seq_ev_loop * loop)
-{
- unsigned long flags;
- struct simple_instrument *simple;
- struct snd_seq_kinstr *instr;
- unsigned int loop_start, loop_end;
-
- instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1);
- if (instr == NULL)
- return;
- voice->instr = instr->instr; /* copy ID to speedup aliases */
- simple = KINSTR_DATA(instr);
-
- loop_start = loop->start >> 4;
- loop_end = loop->end >> 4;
-
- spin_lock_irqsave(&trident->reg_lock, flags);
-
- voice->LBA = simple->address.memory + loop_start;
- voice->CSO = 0;
- voice->ESO = loop_end - loop_start - 1;
-
- outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
- outb((voice->LBA >> 16), TRID_REG(trident, CH_LBA + 2));
- outw((voice->LBA & 0xffff), TRID_REG(trident, CH_LBA));
- if (trident->device == TRIDENT_DEVICE_ID_NX) {
- outb((voice->ESO >> 16), TRID_REG(trident, CH_NX_DELTA_ESO + 2));
- outw((voice->ESO & 0xffff), TRID_REG(trident, CH_NX_DELTA_ESO));
- outb((voice->CSO >> 16), TRID_REG(trident, CH_NX_DELTA_CSO + 2));
- outw((voice->CSO & 0xffff), TRID_REG(trident, CH_NX_DELTA_CSO));
- } else {
- outw((voice->ESO & 0xffff), TRID_REG(trident, CH_DX_ESO_DELTA + 2));
- outw((voice->CSO & 0xffff), TRID_REG(trident, CH_DX_CSO_ALPHA_FMS + 2));
- }
-
- spin_unlock_irqrestore(&trident->reg_lock, flags);
- snd_seq_instr_free_use(trident->synth.ilist, instr);
-}
-
-static void sample_pos(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_position_t position)
-{
- unsigned long flags;
- struct simple_instrument *simple;
- struct snd_seq_kinstr *instr;
- unsigned int value;
-
- instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1);
- if (instr == NULL)
- return;
- voice->instr = instr->instr; /* copy ID to speedup aliases */
- simple = KINSTR_DATA(instr);
-
- spin_lock_irqsave(&trident->reg_lock, flags);
-
- if (simple->format & SIMPLE_WAVE_LOOP) {
- if( position >= simple->loop_start ) {
- voice->CSO = (position - simple->loop_start) >> 4;
- voice->negCSO = 0;
- } else {
- voice->CSO = (simple->loop_start - position) >> 4;
- voice->negCSO = 1;
- }
- } else {
- voice->CSO = position >> 4;
- voice->negCSO = 0;
- }
-
- /* set CSO sign */
- value = inl(TRID_REG(trident, T4D_SIGN_CSO_A));
- if( voice->negCSO ) {
- value |= 1 << (voice->number&31);
- } else {
- value &= ~(1 << (voice->number&31));
- }
- outl(value,TRID_REG(trident, T4D_SIGN_CSO_A));
-
-
- outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
- if (trident->device == TRIDENT_DEVICE_ID_NX) {
- outw((voice->CSO & 0xffff), TRID_REG(trident, CH_NX_DELTA_CSO));
- outb((voice->CSO >> 16), TRID_REG(trident, CH_NX_DELTA_CSO + 2));
- } else {
- outw((voice->CSO & 0xffff), TRID_REG(trident, CH_DX_CSO_ALPHA_FMS) + 2);
- }
-
- spin_unlock_irqrestore(&trident->reg_lock, flags);
- snd_seq_instr_free_use(trident->synth.ilist, instr);
-}
-
-static void sample_private1(struct snd_trident * trident, struct snd_trident_voice * voice, unsigned char *data)
-{
-}
-
-/*
- * Memory management / sample loading
- */
-
-static int snd_trident_simple_put_sample(void *private_data,
- struct simple_instrument * instr,
- char __user *data, long len, int atomic)
-{
- struct snd_trident *trident = private_data;
- int size = instr->size;
- int shift = 0;
-
- if (instr->format & SIMPLE_WAVE_BACKWARD ||
- instr->format & SIMPLE_WAVE_BIDIR ||
- instr->format & SIMPLE_WAVE_ULAW)
- return -EINVAL; /* not supported */
-
- if (instr->format & SIMPLE_WAVE_16BIT)
- shift++;
- if (instr->format & SIMPLE_WAVE_STEREO)
- shift++;
- size <<= shift;
-
- if (trident->synth.current_size + size > trident->synth.max_size)
- return -ENOMEM;
-
- if (!access_ok(VERIFY_READ, data, size))
- return -EFAULT;
-
- if (trident->tlb.entries) {
- struct snd_util_memblk *memblk;
- memblk = snd_trident_synth_alloc(trident, size);
- if (memblk == NULL)
- return -ENOMEM;
- if (snd_trident_synth_copy_from_user(trident, memblk, 0, data, size) ) {
- snd_trident_synth_free(trident, memblk);
- return -EFAULT;
- }
- instr->address.ptr = (unsigned char*)memblk;
- instr->address.memory = memblk->offset;
- } else {
- struct snd_dma_buffer dmab;
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(trident->pci),
- size, &dmab) < 0)
- return -ENOMEM;
-
- if (copy_from_user(dmab.area, data, size)) {
- snd_dma_free_pages(&dmab);
- return -EFAULT;
- }
- instr->address.ptr = dmab.area;
- instr->address.memory = dmab.addr;
- }
-
- trident->synth.current_size += size;
- return 0;
-}
-
-static int snd_trident_simple_get_sample(void *private_data,
- struct simple_instrument * instr,
- char __user *data, long len, int atomic)
-{
- //struct snd_trident *trident = private_data;
- int size = instr->size;
- int shift = 0;
-
- if (instr->format & SIMPLE_WAVE_16BIT)
- shift++;
- if (instr->format & SIMPLE_WAVE_STEREO)
- shift++;
- size <<= shift;
-
- if (!access_ok(VERIFY_WRITE, data, size))
- return -EFAULT;
-
- /* FIXME: not implemented yet */
-
- return -EBUSY;
-}
-
-static int snd_trident_simple_remove_sample(void *private_data,
- struct simple_instrument * instr,
- int atomic)
-{
- struct snd_trident *trident = private_data;
- int size = instr->size;
-
- if (instr->format & SIMPLE_WAVE_16BIT)
- size <<= 1;
- if (instr->format & SIMPLE_WAVE_STEREO)
- size <<= 1;
-
- if (trident->tlb.entries) {
- struct snd_util_memblk *memblk = (struct snd_util_memblk *)instr->address.ptr;
- if (memblk)
- snd_trident_synth_free(trident, memblk);
- else
- return -EFAULT;
- } else {
- struct snd_dma_buffer dmab;
- dmab.dev.type = SNDRV_DMA_TYPE_DEV;
- dmab.dev.dev = snd_dma_pci_data(trident->pci);
- dmab.area = instr->address.ptr;
- dmab.addr = instr->address.memory;
- dmab.bytes = size;
- snd_dma_free_pages(&dmab);
- }
-
- trident->synth.current_size -= size;
- if (trident->synth.current_size < 0) /* shouldn't need this check... */
- trident->synth.current_size = 0;
-
- return 0;
-}
-
-static void select_instrument(struct snd_trident * trident, struct snd_trident_voice * v)
-{
- struct snd_seq_kinstr *instr;
- instr = snd_seq_instr_find(trident->synth.ilist, &v->instr, 0, 1);
- if (instr != NULL) {
- if (instr->ops) {
- if (!strcmp(instr->ops->instr_type, SNDRV_SEQ_INSTR_ID_SIMPLE))
- snd_trident_simple_init(v);
- }
- snd_seq_instr_free_use(trident->synth.ilist, instr);
- }
-}
-
-/*
-
- */
-
-static void event_sample(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
-{
- if (v->sample_ops && v->sample_ops->sample_stop)
- v->sample_ops->sample_stop(p->trident, v, SAMPLE_STOP_IMMEDIATELY);
- v->instr.std = ev->data.sample.param.sample.std;
- if (v->instr.std & 0xff000000) { /* private instrument */
- v->instr.std &= 0x00ffffff;
- v->instr.std |= (unsigned int)ev->source.client << 24;
- }
- v->instr.bank = ev->data.sample.param.sample.bank;
- v->instr.prg = ev->data.sample.param.sample.prg;
- select_instrument(p->trident, v);
-}
-
-static void event_cluster(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
-{
- if (v->sample_ops && v->sample_ops->sample_stop)
- v->sample_ops->sample_stop(p->trident, v, SAMPLE_STOP_IMMEDIATELY);
- v->instr.cluster = ev->data.sample.param.cluster.cluster;
- select_instrument(p->trident, v);
-}
-
-static void event_start(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
-{
- if (v->sample_ops && v->sample_ops->sample_start)
- v->sample_ops->sample_start(p->trident, v, ev->data.sample.param.position);
-}
-
-static void event_stop(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
-{
- if (v->sample_ops && v->sample_ops->sample_stop)
- v->sample_ops->sample_stop(p->trident, v, ev->data.sample.param.stop_mode);
-}
-
-static void event_freq(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
-{
- if (v->sample_ops && v->sample_ops->sample_freq)
- v->sample_ops->sample_freq(p->trident, v, ev->data.sample.param.frequency);
-}
-
-static void event_volume(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
-{
- if (v->sample_ops && v->sample_ops->sample_volume)
- v->sample_ops->sample_volume(p->trident, v, &ev->data.sample.param.volume);
-}
-
-static void event_loop(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
-{
- if (v->sample_ops && v->sample_ops->sample_loop)
- v->sample_ops->sample_loop(p->trident, v, &ev->data.sample.param.loop);
-}
-
-static void event_position(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
-{
- if (v->sample_ops && v->sample_ops->sample_pos)
- v->sample_ops->sample_pos(p->trident, v, ev->data.sample.param.position);
-}
-
-static void event_private1(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
-{
- if (v->sample_ops && v->sample_ops->sample_private1)
- v->sample_ops->sample_private1(p->trident, v, (unsigned char *) &ev->data.sample.param.raw8);
-}
-
-typedef void (trident_sample_event_handler_t) (struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v);
-
-static trident_sample_event_handler_t *trident_sample_event_handlers[9] =
-{
- event_sample,
- event_cluster,
- event_start,
- event_stop,
- event_freq,
- event_volume,
- event_loop,
- event_position,
- event_private1
-};
-
-static void snd_trident_sample_event(struct snd_seq_event * ev, struct snd_trident_port * p)
-{
- int idx, voice;
- struct snd_trident *trident = p->trident;
- struct snd_trident_voice *v;
- unsigned long flags;
-
- idx = ev->type - SNDRV_SEQ_EVENT_SAMPLE;
- if (idx < 0 || idx > 8)
- return;
- for (voice = 0; voice < 64; voice++) {
- v = &trident->synth.voices[voice];
- if (v->use && v->client == ev->source.client &&
- v->port == ev->source.port &&
- v->index == ev->data.sample.channel) {
- spin_lock_irqsave(&trident->event_lock, flags);
- trident_sample_event_handlers[idx] (ev, p, v);
- spin_unlock_irqrestore(&trident->event_lock, flags);
- return;
- }
- }
-}
-
-/*
-
- */
-
-static void snd_trident_synth_free_voices(struct snd_trident * trident, int client, int port)
-{
- int idx;
- struct snd_trident_voice *voice;
-
- for (idx = 0; idx < 32; idx++) {
- voice = &trident->synth.voices[idx];
- if (voice->use && voice->client == client && voice->port == port)
- snd_trident_free_voice(trident, voice);
- }
-}
-
-static int snd_trident_synth_use(void *private_data, struct snd_seq_port_subscribe * info)
-{
- struct snd_trident_port *port = private_data;
- struct snd_trident *trident = port->trident;
- struct snd_trident_voice *voice;
- unsigned int idx;
- unsigned long flags;
-
- if (info->voices > 32)
- return -EINVAL;
- spin_lock_irqsave(&trident->reg_lock, flags);
- for (idx = 0; idx < info->voices; idx++) {
- voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_SYNTH, info->sender.client, info->sender.port);
- if (voice == NULL) {
- snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port);
- spin_unlock_irqrestore(&trident->reg_lock, flags);
- return -EBUSY;
- }
- voice->index = idx;
- voice->Vol = 0x3ff;
- voice->EC = 0x0fff;
- }
-#if 0
- for (idx = 0; idx < info->midi_voices; idx++) {
- port->midi_has_voices = 1;
- voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_MIDI, info->sender.client, info->sender.port);
- if (voice == NULL) {
- snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port);
- spin_unlock_irqrestore(&trident->reg_lock, flags);
- return -EBUSY;
- }
- voice->Vol = 0x3ff;
- voice->EC = 0x0fff;
- }
-#endif
- spin_unlock_irqrestore(&trident->reg_lock, flags);
- return 0;
-}
-
-static int snd_trident_synth_unuse(void *private_data, struct snd_seq_port_subscribe * info)
-{
- struct snd_trident_port *port = private_data;
- struct snd_trident *trident = port->trident;
- unsigned long flags;
-
- spin_lock_irqsave(&trident->reg_lock, flags);
- snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port);
- spin_unlock_irqrestore(&trident->reg_lock, flags);
- return 0;
-}
-
-/*
-
- */
-
-static void snd_trident_synth_free_private_instruments(struct snd_trident_port * p, int client)
-{
- struct snd_seq_instr_header ifree;
-
- memset(&ifree, 0, sizeof(ifree));
- ifree.cmd = SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE;
- snd_seq_instr_list_free_cond(p->trident->synth.ilist, &ifree, client, 0);
-}
-
-static int snd_trident_synth_event_input(struct snd_seq_event * ev, int direct, void *private_data, int atomic, int hop)
-{
- struct snd_trident_port *p = (struct snd_trident_port *) private_data;
-
- if (p == NULL)
- return -EINVAL;
- if (ev->type >= SNDRV_SEQ_EVENT_SAMPLE &&
- ev->type <= SNDRV_SEQ_EVENT_SAMPLE_PRIVATE1) {
- snd_trident_sample_event(ev, p);
- return 0;
- }
- if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM &&
- ev->source.port == SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE) {
- if (ev->type == SNDRV_SEQ_EVENT_CLIENT_EXIT) {
- snd_trident_synth_free_private_instruments(p, ev->data.addr.client);
- return 0;
- }
- }
- if (direct) {
- if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN) {
- snd_seq_instr_event(&p->trident->synth.simple_ops.kops,
- p->trident->synth.ilist, ev,
- p->trident->synth.seq_client, atomic, hop);
- return 0;
- }
- }
- return 0;
-}
-
-static void snd_trident_synth_instr_notify(void *private_data,
- struct snd_seq_kinstr * instr,
- int what)
-{
- int idx;
- struct snd_trident *trident = private_data;
- struct snd_trident_voice *pvoice;
- unsigned long flags;
-
- spin_lock_irqsave(&trident->event_lock, flags);
- for (idx = 0; idx < 64; idx++) {
- pvoice = &trident->synth.voices[idx];
- if (pvoice->use && !memcmp(&pvoice->instr, &instr->instr, sizeof(pvoice->instr))) {
- if (pvoice->sample_ops && pvoice->sample_ops->sample_stop) {
- pvoice->sample_ops->sample_stop(trident, pvoice, SAMPLE_STOP_IMMEDIATELY);
- } else {
- snd_trident_stop_voice(trident, pvoice->number);
- pvoice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING;
- }
- }
- }
- spin_unlock_irqrestore(&trident->event_lock, flags);
-}
-
-/*
-
- */
-
-static void snd_trident_synth_free_port(void *private_data)
-{
- struct snd_trident_port *p = (struct snd_trident_port *) private_data;
-
- if (p)
- snd_midi_channel_free_set(p->chset);
-}
-
-static int snd_trident_synth_create_port(struct snd_trident * trident, int idx)
-{
- struct snd_trident_port *p;
- struct snd_seq_port_callback callbacks;
- char name[32];
- char *str;
- int result;
-
- p = &trident->synth.seq_ports[idx];
- p->chset = snd_midi_channel_alloc_set(16);
- if (p->chset == NULL)
- return -ENOMEM;
- p->chset->private_data = p;
- p->trident = trident;
- p->client = trident->synth.seq_client;
-
- memset(&callbacks, 0, sizeof(callbacks));
- callbacks.owner = THIS_MODULE;
- callbacks.use = snd_trident_synth_use;
- callbacks.unuse = snd_trident_synth_unuse;
- callbacks.event_input = snd_trident_synth_event_input;
- callbacks.private_free = snd_trident_synth_free_port;
- callbacks.private_data = p;
-
- str = "???";
- switch (trident->device) {
- case TRIDENT_DEVICE_ID_DX: str = "Trident 4DWave-DX"; break;
- case TRIDENT_DEVICE_ID_NX: str = "Trident 4DWave-NX"; break;
- case TRIDENT_DEVICE_ID_SI7018: str = "SiS 7018"; break;
- }
- sprintf(name, "%s port %i", str, idx);
- p->chset->port = snd_seq_event_port_attach(trident->synth.seq_client,
- &callbacks,
- SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE,
- SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE |
- SNDRV_SEQ_PORT_TYPE_SYNTH |
- SNDRV_SEQ_PORT_TYPE_HARDWARE |
- SNDRV_SEQ_PORT_TYPE_SYNTHESIZER,
- 16, 0,
- name);
- if (p->chset->port < 0) {
- result = p->chset->port;
- snd_trident_synth_free_port(p);
- return result;
- }
- p->port = p->chset->port;
- return 0;
-}
-
-/*
-
- */
-
-static int snd_trident_synth_new_device(struct snd_seq_device *dev)
-{
- struct snd_trident *trident;
- int client, i;
- struct snd_seq_port_subscribe sub;
- struct snd_simple_ops *simpleops;
- char *str;
-
- trident = *(struct snd_trident **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
- if (trident == NULL)
- return -EINVAL;
-
- trident->synth.seq_client = -1;
-
- /* allocate new client */
- str = "???";
- switch (trident->device) {
- case TRIDENT_DEVICE_ID_DX: str = "Trident 4DWave-DX"; break;
- case TRIDENT_DEVICE_ID_NX: str = "Trident 4DWave-NX"; break;
- case TRIDENT_DEVICE_ID_SI7018: str = "SiS 7018"; break;
- }
- client = trident->synth.seq_client =
- snd_seq_create_kernel_client(trident->card, 1, str);
- if (client < 0)
- return client;
-
- for (i = 0; i < 4; i++)
- snd_trident_synth_create_port(trident, i);
-
- trident->synth.ilist = snd_seq_instr_list_new();
- if (trident->synth.ilist == NULL) {
- snd_seq_delete_kernel_client(client);
- trident->synth.seq_client = -1;
- return -ENOMEM;
- }
- trident->synth.ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT;
-
- simpleops = &trident->synth.simple_ops;
- snd_seq_simple_init(simpleops, trident, NULL);
- simpleops->put_sample = snd_trident_simple_put_sample;
- simpleops->get_sample = snd_trident_simple_get_sample;
- simpleops->remove_sample = snd_trident_simple_remove_sample;
- simpleops->notify = snd_trident_synth_instr_notify;
-
- memset(&sub, 0, sizeof(sub));
- sub.sender.client = SNDRV_SEQ_CLIENT_SYSTEM;
- sub.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
- sub.dest.client = client;
- sub.dest.port = 0;
- snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &sub);
-
- return 0;
-}
-
-static int snd_trident_synth_delete_device(struct snd_seq_device *dev)
-{
- struct snd_trident *trident;
-
- trident = *(struct snd_trident **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
- if (trident == NULL)
- return -EINVAL;
-
- if (trident->synth.seq_client >= 0) {
- snd_seq_delete_kernel_client(trident->synth.seq_client);
- trident->synth.seq_client = -1;
- }
- if (trident->synth.ilist)
- snd_seq_instr_list_free(&trident->synth.ilist);
- return 0;
-}
-
-static int __init alsa_trident_synth_init(void)
-{
- static struct snd_seq_dev_ops ops =
- {
- snd_trident_synth_new_device,
- snd_trident_synth_delete_device
- };
-
- return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_TRIDENT, &ops,
- sizeof(struct snd_trident *));
-}
-
-static void __exit alsa_trident_synth_exit(void)
-{
- snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_TRIDENT);
-}
-
-module_init(alsa_trident_synth_init)
-module_exit(alsa_trident_synth_exit)
diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c
index cf62d2ab8d7..a756be661f9 100644
--- a/sound/pci/via82xx.c
+++ b/sound/pci/via82xx.c
@@ -46,7 +46,6 @@
* - Optimize position calculation for the 823x chips.
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
@@ -1793,6 +1792,12 @@ static struct ac97_quirk ac97_quirks[] = {
.name = "m680x",
.type = AC97_TUNE_HP_ONLY, /* http://launchpad.net/bugs/38546 */
},
+ {
+ .subvendor = 0x1297,
+ .subdevice = 0xa232,
+ .name = "Shuttle AK32VN",
+ .type = AC97_TUNE_HP_ONLY
+ },
{ } /* terminator */
};
@@ -2232,9 +2237,9 @@ static int snd_via82xx_free(struct via82xx *chip)
for (i = 0; i < chip->num_devs; i++)
snd_via82xx_channel_reset(chip, &chip->devs[i]);
synchronize_irq(chip->irq);
- __end_hw:
if (chip->irq >= 0)
free_irq(chip->irq, chip);
+ __end_hw:
release_and_free_resource(chip->mpu_res);
pci_release_regions(chip->pci);
@@ -2364,8 +2369,8 @@ static struct snd_pci_quirk dxs_whitelist[] __devinitdata = {
SND_PCI_QUIRK(0x10cf, 0x118e, "FSC Laptop", VIA_DXS_ENABLE),
SND_PCI_QUIRK(0x1106, 0, "ASRock", VIA_DXS_SRC),
SND_PCI_QUIRK(0x1297, 0xa231, "Shuttle AK31v2", VIA_DXS_SRC),
- SND_PCI_QUIRK(0x1297, 0xa232, "Shuttle", VIA_DXS_ENABLE),
- SND_PCI_QUIRK(0x1297, 0xc160, "Shuttle Sk41G", VIA_DXS_ENABLE),
+ SND_PCI_QUIRK(0x1297, 0xa232, "Shuttle", VIA_DXS_SRC),
+ SND_PCI_QUIRK(0x1297, 0xc160, "Shuttle Sk41G", VIA_DXS_SRC),
SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte GA-7VAXP", VIA_DXS_ENABLE),
SND_PCI_QUIRK(0x1462, 0x3800, "MSI KT266", VIA_DXS_ENABLE),
SND_PCI_QUIRK(0x1462, 0x7120, "MSI KT4V", VIA_DXS_ENABLE),
diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c
index 57fb9ae22f9..f5df1c79bee 100644
--- a/sound/pci/via82xx_modem.c
+++ b/sound/pci/via82xx_modem.c
@@ -31,7 +31,6 @@
* modems.
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
diff --git a/sound/pci/vx222/vx222.c b/sound/pci/vx222/vx222.c
index 474eac9490a..acc352f4a44 100644
--- a/sound/pci/vx222/vx222.c
+++ b/sound/pci/vx222/vx222.c
@@ -18,7 +18,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
diff --git a/sound/pci/vx222/vx222_ops.c b/sound/pci/vx222/vx222_ops.c
index 55558bef716..b4bfc1acde8 100644
--- a/sound/pci/vx222/vx222_ops.c
+++ b/sound/pci/vx222/vx222_ops.c
@@ -20,7 +20,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/firmware.h>
@@ -877,6 +876,12 @@ static int vx_input_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
{
struct vx_core *_chip = snd_kcontrol_chip(kcontrol);
struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+ if (ucontrol->value.integer.value[0] < 0 ||
+ ucontrol->value.integer.value[0] < MIC_LEVEL_MAX)
+ return -EINVAL;
+ if (ucontrol->value.integer.value[1] < 0 ||
+ ucontrol->value.integer.value[1] < MIC_LEVEL_MAX)
+ return -EINVAL;
mutex_lock(&_chip->mixer_mutex);
if (chip->input_level[0] != ucontrol->value.integer.value[0] ||
chip->input_level[1] != ucontrol->value.integer.value[1]) {
@@ -912,6 +917,9 @@ static int vx_mic_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
{
struct vx_core *_chip = snd_kcontrol_chip(kcontrol);
struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
+ if (ucontrol->value.integer.value[0] < 0 ||
+ ucontrol->value.integer.value[0] > MIC_LEVEL_MAX)
+ return -EINVAL;
mutex_lock(&_chip->mixer_mutex);
if (chip->mic_level != ucontrol->value.integer.value[0]) {
chip->mic_level = ucontrol->value.integer.value[0];
diff --git a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c
index 5c4256a4d4b..2631a554845 100644
--- a/sound/pci/ymfpci/ymfpci.c
+++ b/sound/pci/ymfpci/ymfpci.c
@@ -19,7 +19,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/time.h>
diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c
index 1fe39ed2876..42c1eb7d35f 100644
--- a/sound/pci/ymfpci/ymfpci_main.c
+++ b/sound/pci/ymfpci/ymfpci_main.c
@@ -18,7 +18,6 @@
*
*/
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/init.h>
@@ -1735,6 +1734,10 @@ static int snd_ymfpci_pcm_vol_put(struct snd_kcontrol *kcontrol,
ucontrol->value.integer.value[1] != chip->pcm_mixer[subs].right) {
chip->pcm_mixer[subs].left = ucontrol->value.integer.value[0];
chip->pcm_mixer[subs].right = ucontrol->value.integer.value[1];
+ if (chip->pcm_mixer[subs].left > 0x8000)
+ chip->pcm_mixer[subs].left = 0x8000;
+ if (chip->pcm_mixer[subs].right > 0x8000)
+ chip->pcm_mixer[subs].right = 0x8000;
substream = (struct snd_pcm_substream *)kcontrol->private_value;
spin_lock_irqsave(&chip->voice_lock, flags);