diff options
Diffstat (limited to 'sound/pci')
117 files changed, 12492 insertions, 535 deletions
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index a2081803a82..23e54cedfd4 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -216,18 +216,160 @@ config SND_CS46XX_NEW_DSP This works better than the old code, so say Y. config SND_CS5535AUDIO - tristate "CS5535 Audio" + tristate "CS5535/CS5536 Audio" depends on SND && X86 && !X86_64 select SND_PCM select SND_AC97_CODEC help Say Y here to include support for audio on CS5535 chips. It is referred to as NS CS5535 IO or AMD CS5535 IO companion in - various literature. + various literature. This driver also supports the CS5536 audio + device. However, for both chips, on certain boards, you may + need to use ac97_quirk=hp_only if your board has physically + mapped headphone out to master output. If that works for you, + send lspci -vvv output to the mailing list so that your board + can be identified in the quirks list. To compile this driver as a module, choose M here: the module will be called snd-cs5535audio. +config SND_DARLA20 + tristate "(Echoaudio) Darla20" + depends on SND + depends on FW_LOADER + select SND_PCM + help + Say 'Y' or 'M' to include support for Echoaudio Darla. + + To compile this driver as a module, choose M here: the module + will be called snd-darla20 + +config SND_GINA20 + tristate "(Echoaudio) Gina20" + depends on SND + depends on FW_LOADER + select SND_PCM + help + Say 'Y' or 'M' to include support for Echoaudio Gina. + + To compile this driver as a module, choose M here: the module + will be called snd-gina20 + +config SND_LAYLA20 + tristate "(Echoaudio) Layla20" + depends on SND + depends on FW_LOADER + select SND_RAWMIDI + select SND_PCM + help + Say 'Y' or 'M' to include support for Echoaudio Layla. + + To compile this driver as a module, choose M here: the module + will be called snd-layla20 + +config SND_DARLA24 + tristate "(Echoaudio) Darla24" + depends on SND + depends on FW_LOADER + select SND_PCM + help + Say 'Y' or 'M' to include support for Echoaudio Darla24. + + To compile this driver as a module, choose M here: the module + will be called snd-darla24 + +config SND_GINA24 + tristate "(Echoaudio) Gina24" + depends on SND + depends on FW_LOADER + select SND_PCM + help + Say 'Y' or 'M' to include support for Echoaudio Gina24. + + To compile this driver as a module, choose M here: the module + will be called snd-gina24 + +config SND_LAYLA24 + tristate "(Echoaudio) Layla24" + depends on SND + depends on FW_LOADER + select SND_RAWMIDI + select SND_PCM + help + Say 'Y' or 'M' to include support for Echoaudio Layla24. + + To compile this driver as a module, choose M here: the module + will be called snd-layla24 + +config SND_MONA + tristate "(Echoaudio) Mona" + depends on SND + depends on FW_LOADER + select SND_RAWMIDI + select SND_PCM + help + Say 'Y' or 'M' to include support for Echoaudio Mona. + + To compile this driver as a module, choose M here: the module + will be called snd-mona + +config SND_MIA + tristate "(Echoaudio) Mia" + depends on SND + depends on FW_LOADER + select SND_RAWMIDI + select SND_PCM + help + Say 'Y' or 'M' to include support for Echoaudio Mia and Mia-midi. + + To compile this driver as a module, choose M here: the module + will be called snd-mia + +config SND_ECHO3G + tristate "(Echoaudio) 3G cards" + depends on SND + depends on FW_LOADER + select SND_RAWMIDI + select SND_PCM + help + Say 'Y' or 'M' to include support for Echoaudio Gina3G and Layla3G. + + To compile this driver as a module, choose M here: the module + will be called snd-echo3g + +config SND_INDIGO + tristate "(Echoaudio) Indigo" + depends on SND + depends on FW_LOADER + select SND_PCM + help + Say 'Y' or 'M' to include support for Echoaudio Indigo. + + To compile this driver as a module, choose M here: the module + will be called snd-indigo + +config SND_INDIGOIO + tristate "(Echoaudio) Indigo IO" + depends on SND + depends on FW_LOADER + select SND_PCM + help + Say 'Y' or 'M' to include support for Echoaudio Indigo IO. + + To compile this driver as a module, choose M here: the module + will be called snd-indigoio + +config SND_INDIGODJ + tristate "(Echoaudio) Indigo DJ" + depends on SND + depends on FW_LOADER + select SND_PCM + help + Say 'Y' or 'M' to include support for Echoaudio Indigo DJ. + + To compile this driver as a module, choose M here: the module + will be called snd-indigodj + config SND_EMU10K1 tristate "Emu10k1 (SB Live!, Audigy, E-mu APS)" depends on SND @@ -415,8 +557,8 @@ config SND_INTEL8X0 will be called snd-intel8x0. config SND_INTEL8X0M - tristate "Intel/SiS/nVidia/AMD MC97 Modem (EXPERIMENTAL)" - depends on SND && EXPERIMENTAL + tristate "Intel/SiS/nVidia/AMD MC97 Modem" + depends on SND select SND_AC97_CODEC help Say Y here to include support for the integrated MC97 modem on diff --git a/sound/pci/Makefile b/sound/pci/Makefile index cba5105aafe..e06736da9ef 100644 --- a/sound/pci/Makefile +++ b/sound/pci/Makefile @@ -57,6 +57,7 @@ obj-$(CONFIG_SND) += \ ca0106/ \ cs46xx/ \ cs5535audio/ \ + echoaudio/ \ emu10k1/ \ hda/ \ ice1712/ \ diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index d05200741ac..0abf2808d59 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -253,6 +253,8 @@ void snd_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short va ac97->bus->ops->write(ac97, reg, value); } +EXPORT_SYMBOL(snd_ac97_write); + /** * snd_ac97_read - read a value from the given register * @@ -281,6 +283,8 @@ static inline unsigned short snd_ac97_read_cache(struct snd_ac97 *ac97, unsigned return ac97->regs[reg]; } +EXPORT_SYMBOL(snd_ac97_read); + /** * snd_ac97_write_cache - write a value on the given register and update the cache * @ac97: the ac97 instance @@ -302,6 +306,8 @@ void snd_ac97_write_cache(struct snd_ac97 *ac97, unsigned short reg, unsigned sh mutex_unlock(&ac97->reg_mutex); } +EXPORT_SYMBOL(snd_ac97_write_cache); + /** * snd_ac97_update - update the value on the given register * @ac97: the ac97 instance @@ -331,6 +337,8 @@ int snd_ac97_update(struct snd_ac97 *ac97, unsigned short reg, unsigned short va return change; } +EXPORT_SYMBOL(snd_ac97_update); + /** * snd_ac97_update_bits - update the bits on the given register * @ac97: the ac97 instance @@ -356,6 +364,8 @@ int snd_ac97_update_bits(struct snd_ac97 *ac97, unsigned short reg, unsigned sho return change; } +EXPORT_SYMBOL(snd_ac97_update_bits); + /* no lock version - see snd_ac97_updat_bits() */ int snd_ac97_update_bits_nolock(struct snd_ac97 *ac97, unsigned short reg, unsigned short mask, unsigned short value) @@ -563,7 +573,7 @@ AC97_SINGLE("PC Speaker Playback Volume", AC97_PC_BEEP, 1, 15, 1) }; static const struct snd_kcontrol_new snd_ac97_controls_mic_boost = - AC97_SINGLE("Mic Boost (+20dB)", AC97_MIC, 6, 1, 0); + AC97_SINGLE("Mic Boost (+20dB) Switch", AC97_MIC, 6, 1, 0); static const char* std_rec_sel[] = {"Mic", "CD", "Video", "Aux", "Line", "Mix", "Mix Mono", "Phone"}; @@ -605,7 +615,7 @@ AC97_SINGLE("Simulated Stereo Enhancement", AC97_GENERAL_PURPOSE, 14, 1, 0), AC97_SINGLE("3D Control - Switch", AC97_GENERAL_PURPOSE, 13, 1, 0), AC97_SINGLE("Loudness (bass boost)", AC97_GENERAL_PURPOSE, 12, 1, 0), AC97_ENUM("Mono Output Select", std_enum[2]), -AC97_ENUM("Mic Select", std_enum[3]), +AC97_ENUM("Mic Select Capture Switch", std_enum[3]), AC97_SINGLE("ADC/DAC Loopback", AC97_GENERAL_PURPOSE, 7, 1, 0) }; @@ -1226,7 +1236,8 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97) ac97->regs[AC97_CENTER_LFE_MASTER] = 0x8080; /* build center controls */ - if (snd_ac97_try_volume_mix(ac97, AC97_CENTER_LFE_MASTER)) { + if ((snd_ac97_try_volume_mix(ac97, AC97_CENTER_LFE_MASTER)) + && !(ac97->flags & AC97_AD_MULTI)) { if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_center[0], ac97))) < 0) return err; if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_center[1], ac97))) < 0) @@ -1238,7 +1249,8 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97) } /* build LFE controls */ - if (snd_ac97_try_volume_mix(ac97, AC97_CENTER_LFE_MASTER+1)) { + if ((snd_ac97_try_volume_mix(ac97, AC97_CENTER_LFE_MASTER+1)) + && !(ac97->flags & AC97_AD_MULTI)) { if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_lfe[0], ac97))) < 0) return err; if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_lfe[1], ac97))) < 0) @@ -1250,7 +1262,8 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97) } /* build surround controls */ - if (snd_ac97_try_volume_mix(ac97, AC97_SURROUND_MASTER)) { + if ((snd_ac97_try_volume_mix(ac97, AC97_SURROUND_MASTER)) + && !(ac97->flags & AC97_AD_MULTI)) { /* Surround Master (0x38) is with stereo mutes */ if ((err = snd_ac97_cmix_new_stereo(card, "Surround Playback", AC97_SURROUND_MASTER, 1, ac97)) < 0) return err; @@ -1335,9 +1348,11 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97) } /* build Aux controls */ - if (snd_ac97_try_volume_mix(ac97, AC97_AUX)) { - if ((err = snd_ac97_cmix_new(card, "Aux Playback", AC97_AUX, ac97)) < 0) - return err; + if (!(ac97->flags & AC97_HAS_NO_AUX)) { + if (snd_ac97_try_volume_mix(ac97, AC97_AUX)) { + if ((err = snd_ac97_cmix_new(card, "Aux Playback", AC97_AUX, ac97)) < 0) + return err; + } } /* build PCM controls */ @@ -1682,6 +1697,7 @@ const char *snd_ac97_get_short_name(struct snd_ac97 *ac97) return "unknown codec"; } +EXPORT_SYMBOL(snd_ac97_get_short_name); /* wait for a while until registers are accessible after RESET * return 0 if ok, negative not ready @@ -1774,6 +1790,8 @@ int snd_ac97_bus(struct snd_card *card, int num, struct snd_ac97_bus_ops *ops, return 0; } +EXPORT_SYMBOL(snd_ac97_bus); + /* stop no dev release warning */ static void ac97_device_release(struct device * dev) { @@ -2117,6 +2135,7 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template, return 0; } +EXPORT_SYMBOL(snd_ac97_mixer); /* * Power down the chip. @@ -2166,6 +2185,8 @@ void snd_ac97_suspend(struct snd_ac97 *ac97) snd_ac97_powerdown(ac97); } +EXPORT_SYMBOL(snd_ac97_suspend); + /* * restore ac97 status */ @@ -2267,6 +2288,8 @@ __reset_ready: snd_ac97_restore_iec958(ac97); } } + +EXPORT_SYMBOL(snd_ac97_resume); #endif @@ -2590,29 +2613,7 @@ int snd_ac97_tune_hardware(struct snd_ac97 *ac97, struct ac97_quirk *quirk, cons return 0; } - -/* - * Exported symbols - */ - -EXPORT_SYMBOL(snd_ac97_write); -EXPORT_SYMBOL(snd_ac97_read); -EXPORT_SYMBOL(snd_ac97_write_cache); -EXPORT_SYMBOL(snd_ac97_update); -EXPORT_SYMBOL(snd_ac97_update_bits); -EXPORT_SYMBOL(snd_ac97_get_short_name); -EXPORT_SYMBOL(snd_ac97_bus); -EXPORT_SYMBOL(snd_ac97_mixer); -EXPORT_SYMBOL(snd_ac97_pcm_assign); -EXPORT_SYMBOL(snd_ac97_pcm_open); -EXPORT_SYMBOL(snd_ac97_pcm_close); -EXPORT_SYMBOL(snd_ac97_pcm_double_rate_rules); EXPORT_SYMBOL(snd_ac97_tune_hardware); -EXPORT_SYMBOL(snd_ac97_set_rate); -#ifdef CONFIG_PM -EXPORT_SYMBOL(snd_ac97_resume); -EXPORT_SYMBOL(snd_ac97_suspend); -#endif /* * INIT part diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index 4d9cf37300f..094cfc1f3a1 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -464,6 +464,10 @@ int patch_wolfson05(struct snd_ac97 * ac97) { /* WM9705, WM9710 */ ac97->build_ops = &patch_wolfson_wm9705_ops; +#ifdef CONFIG_TOUCHSCREEN_WM9705 + /* WM9705 touchscreen uses AUX and VIDEO for touch */ + ac97->flags |=3D AC97_HAS_NO_VIDEO | AC97_HAS_NO_AUX; +#endif return 0; } @@ -1367,6 +1371,13 @@ static void ad18xx_resume(struct snd_ac97 *ac97) snd_ac97_restore_iec958(ac97); } + +static void ad1888_resume(struct snd_ac97 *ac97) +{ + ad18xx_resume(ac97); + snd_ac97_write_cache(ac97, AC97_CODEC_CLASS_REV, 0x8080); +} + #endif int patch_ad1819(struct snd_ac97 * ac97) @@ -1627,6 +1638,7 @@ static const struct snd_kcontrol_new snd_ac97_ad1981x_jack_sense[] = { * (SS vendor << 16 | device) */ static unsigned int ad1981_jacks_blacklist[] = { + 0x10140537, /* Thinkpad T41p */ 0x10140554, /* Thinkpad T42p/R50p */ 0 /* end */ }; @@ -1812,6 +1824,8 @@ static const struct snd_kcontrol_new snd_ac97_ad1888_controls[] = { .get = snd_ac97_ad1888_lohpsel_get, .put = snd_ac97_ad1888_lohpsel_put }, + AC97_SINGLE("V_REFOUT Enable", AC97_AD_MISC, 2, 1, 1), + AC97_SINGLE("High Pass Filter Enable", AC97_AD_TEST2, 12, 1, 1), AC97_SINGLE("Spread Front to Surround and Center/LFE", AC97_AD_MISC, 7, 1, 0), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -1839,7 +1853,7 @@ static struct snd_ac97_build_ops patch_ad1888_build_ops = { .build_post_spdif = patch_ad198x_post_spdif, .build_specific = patch_ad1888_specific, #ifdef CONFIG_PM - .resume = ad18xx_resume, + .resume = ad1888_resume, #endif .update_jacks = ad1888_update_jacks, }; @@ -2048,7 +2062,10 @@ int patch_alc650(struct snd_ac97 * ac97) /* Enable SPDIF-IN only on Rev.E and above */ val = snd_ac97_read(ac97, AC97_ALC650_CLOCK); /* SPDIF IN with pin 47 */ - if (ac97->spec.dev_flags) + if (ac97->spec.dev_flags && + /* ASUS A6KM requires EAPD */ + ! (ac97->subsystem_vendor == 0x1043 && + ac97->subsystem_device == 0x1103)) val |= 0x03; /* enable */ else val &= ~0x03; /* disable */ diff --git a/sound/pci/ac97/ac97_pcm.c b/sound/pci/ac97/ac97_pcm.c index 512a3583b0c..f684aa2c006 100644 --- a/sound/pci/ac97/ac97_pcm.c +++ b/sound/pci/ac97/ac97_pcm.c @@ -317,6 +317,8 @@ int snd_ac97_set_rate(struct snd_ac97 *ac97, int reg, unsigned int rate) return 0; } +EXPORT_SYMBOL(snd_ac97_set_rate); + static unsigned short get_pslots(struct snd_ac97 *ac97, unsigned char *rate_table, unsigned short *spdif_slots) { if (!ac97_is_audio(ac97)) @@ -550,6 +552,8 @@ int snd_ac97_pcm_assign(struct snd_ac97_bus *bus, return 0; } +EXPORT_SYMBOL(snd_ac97_pcm_assign); + /** * snd_ac97_pcm_open - opens the given AC97 pcm * @pcm: the ac97 pcm instance @@ -633,6 +637,8 @@ int snd_ac97_pcm_open(struct ac97_pcm *pcm, unsigned int rate, return err; } +EXPORT_SYMBOL(snd_ac97_pcm_open); + /** * snd_ac97_pcm_close - closes the given AC97 pcm * @pcm: the ac97 pcm instance @@ -658,6 +664,8 @@ int snd_ac97_pcm_close(struct ac97_pcm *pcm) return 0; } +EXPORT_SYMBOL(snd_ac97_pcm_close); + static int double_rate_hw_constraint_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { @@ -709,3 +717,5 @@ int snd_ac97_pcm_double_rate_rules(struct snd_pcm_runtime *runtime) SNDRV_PCM_HW_PARAM_RATE, -1); return err; } + +EXPORT_SYMBOL(snd_ac97_pcm_double_rate_rules); diff --git a/sound/pci/ac97/ac97_proc.c b/sound/pci/ac97/ac97_proc.c index 4d523df79cc..2118df50b9d 100644 --- a/sound/pci/ac97/ac97_proc.c +++ b/sound/pci/ac97/ac97_proc.c @@ -433,7 +433,7 @@ void snd_ac97_proc_init(struct snd_ac97 * ac97) prefix = ac97_is_audio(ac97) ? "ac97" : "mc97"; sprintf(name, "%s#%d-%d", prefix, ac97->addr, ac97->num); if ((entry = snd_info_create_card_entry(ac97->bus->card, name, ac97->bus->proc)) != NULL) { - snd_info_set_text_ops(entry, ac97, 1024, snd_ac97_proc_read); + snd_info_set_text_ops(entry, ac97, snd_ac97_proc_read); if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); entry = NULL; @@ -442,10 +442,9 @@ void snd_ac97_proc_init(struct snd_ac97 * ac97) ac97->proc = entry; sprintf(name, "%s#%d-%d+regs", prefix, ac97->addr, ac97->num); if ((entry = snd_info_create_card_entry(ac97->bus->card, name, ac97->bus->proc)) != NULL) { - snd_info_set_text_ops(entry, ac97, 1024, snd_ac97_proc_regs_read); + snd_info_set_text_ops(entry, ac97, snd_ac97_proc_regs_read); #ifdef CONFIG_SND_DEBUG entry->mode |= S_IWUSR; - entry->c.text.write_size = 1024; entry->c.text.write = snd_ac97_proc_regs_write; #endif if (snd_info_register(entry) < 0) { diff --git a/sound/pci/ac97/ak4531_codec.c b/sound/pci/ac97/ak4531_codec.c index 0fb7b340731..94c26ec0588 100644 --- a/sound/pci/ac97/ak4531_codec.c +++ b/sound/pci/ac97/ak4531_codec.c @@ -453,7 +453,7 @@ static void snd_ak4531_proc_init(struct snd_card *card, struct snd_ak4531 *ak453 struct snd_info_entry *entry; if (! snd_card_proc_new(card, "ak4531", &entry)) - snd_info_set_text_ops(entry, ak4531, 1024, snd_ak4531_proc_read); + snd_info_set_text_ops(entry, ak4531, snd_ak4531_proc_read); } #endif diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c index eece1c7e55a..d42bf457036 100644 --- a/sound/pci/ad1889.c +++ b/sound/pci/ad1889.c @@ -753,7 +753,7 @@ snd_ad1889_proc_init(struct snd_ad1889 *chip) struct snd_info_entry *entry; if (!snd_card_proc_new(chip->card, chip->card->driver, &entry)) - snd_info_set_text_ops(entry, chip, 1024, snd_ad1889_proc_read); + snd_info_set_text_ops(entry, chip, snd_ad1889_proc_read); } static struct ac97_quirk ac97_quirks[] = { diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c index e2dbc211890..5dfdbf6657f 100644 --- a/sound/pci/ali5451/ali5451.c +++ b/sound/pci/ali5451/ali5451.c @@ -49,7 +49,7 @@ MODULE_SUPPORTED_DEVICE("{{ALI,M5451,pci},{ALI,M5451}}"); static int index = SNDRV_DEFAULT_IDX1; /* Index */ static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */ static int pcm_channels = 32; -static int spdif = 0; +static int spdif; module_param(index, int, 0444); MODULE_PARM_DESC(index, "Index value for ALI M5451 PCI Audio."); @@ -2173,7 +2173,7 @@ static void __devinit snd_ali_proc_init(struct snd_ali *codec) { struct snd_info_entry *entry; if(!snd_card_proc_new(codec->card, "ali5451", &entry)) - snd_info_set_text_ops(entry, codec, 1024, snd_ali_proc_read); + snd_info_set_text_ops(entry, codec, snd_ali_proc_read); } static int __devinit snd_ali_resources(struct snd_ali *codec) diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c index 60423b1c678..a9f08066459 100644 --- a/sound/pci/als4000.c +++ b/sound/pci/als4000.c @@ -746,8 +746,8 @@ static int __devinit snd_card_als4000_probe(struct pci_dev *pci, card->shortname, chip->alt_port, chip->irq); if ((err = snd_mpu401_uart_new( card, 0, MPU401_HW_ALS4000, - gcr+0x30, 1, pci->irq, 0, - &chip->rmidi)) < 0) { + gcr+0x30, MPU401_INFO_INTEGRATED, + pci->irq, 0, &chip->rmidi)) < 0) { printk(KERN_ERR "als4000: no MPU-401 device at 0x%lx?\n", gcr+0x30); goto out_err; } diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index d0f759d86d3..f18a8c0e468 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -1504,7 +1504,7 @@ static void __devinit snd_atiixp_proc_init(struct atiixp *chip) struct snd_info_entry *entry; if (! snd_card_proc_new(chip->card, "atiixp", &entry)) - snd_info_set_text_ops(entry, chip, 1024, snd_atiixp_proc_read); + snd_info_set_text_ops(entry, chip, snd_atiixp_proc_read); } #else /* !CONFIG_PROC_FS */ #define snd_atiixp_proc_init(chip) diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c index 12a34c39caa..40739057076 100644 --- a/sound/pci/atiixp_modem.c +++ b/sound/pci/atiixp_modem.c @@ -1177,7 +1177,7 @@ static void __devinit snd_atiixp_proc_init(struct atiixp_modem *chip) struct snd_info_entry *entry; if (! snd_card_proc_new(chip->card, "atiixp-modem", &entry)) - snd_info_set_text_ops(entry, chip, 1024, snd_atiixp_proc_read); + snd_info_set_text_ops(entry, chip, snd_atiixp_proc_read); } #else #define snd_atiixp_proc_init(chip) diff --git a/sound/pci/au88x0/au88x0.c b/sound/pci/au88x0/au88x0.c index 126870ec063..8a3b118989b 100644 --- a/sound/pci/au88x0/au88x0.c +++ b/sound/pci/au88x0/au88x0.c @@ -261,6 +261,13 @@ snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) return err; } snd_vortex_workaround(pci, pcifix[dev]); + + // Card details needed in snd_vortex_midi + strcpy(card->driver, CARD_NAME_SHORT); + sprintf(card->shortname, "Aureal Vortex %s", CARD_NAME_SHORT); + sprintf(card->longname, "%s at 0x%lx irq %i", + card->shortname, chip->io, chip->irq); + // (4) Alloc components. // ADB pcm. if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_ADB, NR_ADB)) < 0) { @@ -323,11 +330,6 @@ snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) #endif // (5) - strcpy(card->driver, CARD_NAME_SHORT); - strcpy(card->shortname, CARD_NAME_SHORT); - sprintf(card->longname, "%s at 0x%lx irq %i", - card->shortname, chip->io, chip->irq); - if ((err = pci_read_config_word(pci, PCI_DEVICE_ID, &(chip->device))) < 0) { snd_card_free(card); diff --git a/sound/pci/au88x0/au88x0_mpu401.c b/sound/pci/au88x0/au88x0_mpu401.c index 873f486b07b..c75d368ea08 100644 --- a/sound/pci/au88x0/au88x0_mpu401.c +++ b/sound/pci/au88x0/au88x0_mpu401.c @@ -47,7 +47,7 @@ static int __devinit snd_vortex_midi(vortex_t * vortex) struct snd_rawmidi *rmidi; int temp, mode; struct snd_mpu401 *mpu; - int port; + unsigned long port; #ifdef VORTEX_MPU401_LEGACY /* EnableHardCodedMPU401Port() */ @@ -70,9 +70,6 @@ static int __devinit snd_vortex_midi(vortex_t * vortex) temp |= (MIDI_CLOCK_DIV << 8) | ((mode >> 24) & 0xff) << 4; hwwrite(vortex->mmio, VORTEX_CTRL2, temp); hwwrite(vortex->mmio, VORTEX_MIDI_CMD, MPU401_RESET); - /* Set some kind of mode */ - if (mode) - hwwrite(vortex->mmio, VORTEX_MIDI_CMD, MPU401_ENTER_UART); /* Check if anything is OK. */ temp = hwread(vortex->mmio, VORTEX_MIDI_DATA); @@ -98,7 +95,8 @@ static int __devinit snd_vortex_midi(vortex_t * vortex) port = (unsigned long)(vortex->mmio + VORTEX_MIDI_DATA); if ((temp = snd_mpu401_uart_new(vortex->card, 0, MPU401_HW_AUREAL, port, - 1, 0, 0, &rmidi)) != 0) { + MPU401_INFO_INTEGRATED | MPU401_INFO_MMIO, + 0, 0, &rmidi)) != 0) { hwwrite(vortex->mmio, VORTEX_CTRL, (hwread(vortex->mmio, VORTEX_CTRL) & ~CTRL_MIDI_PORT) & ~CTRL_MIDI_EN); @@ -107,6 +105,9 @@ static int __devinit snd_vortex_midi(vortex_t * vortex) mpu = rmidi->private_data; mpu->cport = (unsigned long)(vortex->mmio + VORTEX_MIDI_CMD); #endif + /* Overwrite MIDI name */ + snprintf(rmidi->name, sizeof(rmidi->name), "%s MIDI %d", CARD_NAME_SHORT , vortex->card->number); + vortex->rmidi = rmidi; return 0; } diff --git a/sound/pci/au88x0/au88x0_xtalk.c b/sound/pci/au88x0/au88x0_xtalk.c index 4534e1882ad..b4151e208b7 100644 --- a/sound/pci/au88x0/au88x0_xtalk.c +++ b/sound/pci/au88x0/au88x0_xtalk.c @@ -66,31 +66,20 @@ static xtalk_gains_t const asXtalkGainsAllChan = { 0 //0x7FFF,0x7FFF,0x7FFF,0x7FFF,0x7fff,0x7FFF,0x7FFF,0x7FFF,0x7FFF,0x7fff }; -static xtalk_gains_t const asXtalkGainsZeros = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; +static xtalk_gains_t const asXtalkGainsZeros; -static xtalk_dline_t const alXtalkDlineZeros = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0 -}; +static xtalk_dline_t const alXtalkDlineZeros; static xtalk_dline_t const alXtalkDlineTest = { 0xFC18, 0x03E8FFFF, 0x186A0, 0x7960FFFE, 1, 0xFFFFFFFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -static xtalk_instate_t const asXtalkInStateZeros = { 0, 0, 0, 0 }; +static xtalk_instate_t const asXtalkInStateZeros; static xtalk_instate_t const asXtalkInStateTest = { 0xFF80, 0x0080, 0xFFFF, 0x0001 }; -static xtalk_state_t const asXtalkOutStateZeros = { - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0} -}; +static xtalk_state_t const asXtalkOutStateZeros; + static short const sDiamondKLeftEq = 0x401d; static short const sDiamondKRightEq = 0x401d; static short const sDiamondKLeftXt = 0xF90E; @@ -162,13 +151,7 @@ static xtalk_coefs_t const asXtalkNarrowCoefsRightXt = { {0, 0, 0, 0, 0} }; -static xtalk_coefs_t const asXtalkCoefsZeros = { - {0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0} -}; +static xtalk_coefs_t const asXtalkCoefsZeros; static xtalk_coefs_t const asXtalkCoefsPipe = { {0, 0, 0x0FA0, 0, 0}, {0, 0, 0x0FA0, 0, 0}, diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index 52a36452426..6e62dafb66c 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -33,14 +33,21 @@ * in the first place >:-P}), * I was forced to base this driver on reverse engineering * (3 weeks' worth of evenings filled with driver work). - * (and no, I did NOT go the easy way: to pick up a PCI128 for 9 Euros) + * (and no, I did NOT go the easy way: to pick up a SB PCI128 for 9 Euros) * * The AZF3328 chip (note: AZF3328, *not* AZT3328, that's just the driver name * for compatibility reasons) has the following features: * * - builtin AC97 conformant codec (SNR over 80dB) - * (really AC97 compliant?? I really doubt it when looking - * at the mixer register layout) + * Note that "conformant" != "compliant"!! this chip's mixer register layout + * *differs* from the standard AC97 layout: + * they chose to not implement the headphone register (which is not a + * problem since it's merely optional), yet when doing this, they committed + * the grave sin of letting other registers follow immediately instead of + * keeping a headphone dummy register, thereby shifting the mixer register + * addresses illegally. So far unfortunately it looks like the very flexible + * ALSA AC97 support is still not enough to easily compensate for such a + * grave layout violation despite all tweaks and quirks mechanisms it offers. * - builtin genuine OPL3 * - full duplex 16bit playback/record at independent sampling rate * - MPU401 (+ legacy address support) FIXME: how to enable legacy addr?? @@ -90,10 +97,15 @@ * * TODO * - test MPU401 MIDI playback etc. - * - power management. See e.g. intel8x0 or cs4281. - * This would be nice since the chip runs a bit hot, and it's *required* - * anyway for proper ACPI power management. + * - add some power micro-management (disable various units of the card + * as long as they're unused). However this requires I/O ports which I + * haven't figured out yet and which thus might not even exist... + * The standard suspend/resume functionality could probably make use of + * some improvement, too... * - figure out what all unknown port bits are responsible for + * - figure out some cleverly evil scheme to possibly make ALSA AC97 code + * fully accept our quite incompatible ""AC97"" mixer and thus save some + * code (but I'm not too optimistic that doing this is possible at all) */ #include <sound/driver.h> @@ -214,6 +226,16 @@ struct snd_azf3328 { struct pci_dev *pci; int irq; + +#ifdef CONFIG_PM + /* register value containers for power management + * Note: not always full I/O range preserved (just like Win driver!) */ + u16 saved_regs_codec [AZF_IO_SIZE_CODEC_PM / 2]; + u16 saved_regs_io2 [AZF_IO_SIZE_IO2_PM / 2]; + u16 saved_regs_mpu [AZF_IO_SIZE_MPU_PM / 2]; + u16 saved_regs_synth[AZF_IO_SIZE_SYNTH_PM / 2]; + u16 saved_regs_mixer[AZF_IO_SIZE_MIXER_PM / 2]; +#endif }; static const struct pci_device_id snd_azf3328_ids[] __devinitdata = { @@ -317,10 +339,8 @@ snd_azf3328_mixer_write_volume_gradually(const struct snd_azf3328 *chip, int reg else dst_vol_left &= ~0x80; - do - { - if (!left_done) - { + do { + if (!left_done) { if (curr_vol_left > dst_vol_left) curr_vol_left--; else @@ -330,8 +350,7 @@ snd_azf3328_mixer_write_volume_gradually(const struct snd_azf3328 *chip, int reg left_done = 1; outb(curr_vol_left, portbase + 1); } - if (!right_done) - { + if (!right_done) { if (curr_vol_right > dst_vol_right) curr_vol_right--; else @@ -346,8 +365,7 @@ snd_azf3328_mixer_write_volume_gradually(const struct snd_azf3328 *chip, int reg } if (delay) mdelay(delay); - } - while ((!left_done) || (!right_done)); + } while ((!left_done) || (!right_done)); snd_azf3328_dbgcallleave(); } @@ -514,15 +532,18 @@ snd_azf3328_info_mixer_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { static const char * const texts1[] = { - "ModemOut1", "ModemOut2" + "Mic1", "Mic2" }; static const char * const texts2[] = { - "MonoSelectSource1", "MonoSelectSource2" + "Mix", "Mic" }; static const char * const texts3[] = { "Mic", "CD", "Video", "Aux", "Line", "Mix", "Mix Mono", "Phone" }; + static const char * const texts4[] = { + "pre 3D", "post 3D" + }; struct azf3328_mixer_reg reg; snd_azf3328_mixer_reg_decode(®, kcontrol->private_value); @@ -531,14 +552,19 @@ snd_azf3328_info_mixer_enum(struct snd_kcontrol *kcontrol, uinfo->value.enumerated.items = reg.enum_c; if (uinfo->value.enumerated.item > reg.enum_c - 1U) uinfo->value.enumerated.item = reg.enum_c - 1U; - if (reg.reg == IDX_MIXER_ADVCTL2) - { - if (reg.lchan_shift == 8) /* modem out sel */ + if (reg.reg == IDX_MIXER_ADVCTL2) { + switch(reg.lchan_shift) { + case 8: /* modem out sel */ strcpy(uinfo->value.enumerated.name, texts1[uinfo->value.enumerated.item]); - else /* mono sel source */ + break; + case 9: /* mono sel source */ strcpy(uinfo->value.enumerated.name, texts2[uinfo->value.enumerated.item]); - } - else + break; + case 15: /* PCM Out Path */ + strcpy(uinfo->value.enumerated.name, texts4[uinfo->value.enumerated.item]); + break; + } + } else strcpy(uinfo->value.enumerated.name, texts3[uinfo->value.enumerated.item] ); return 0; @@ -554,12 +580,10 @@ snd_azf3328_get_mixer_enum(struct snd_kcontrol *kcontrol, snd_azf3328_mixer_reg_decode(®, kcontrol->private_value); val = snd_azf3328_mixer_inw(chip, reg.reg); - if (reg.reg == IDX_MIXER_REC_SELECT) - { + if (reg.reg == IDX_MIXER_REC_SELECT) { ucontrol->value.enumerated.item[0] = (val >> 8) & (reg.enum_c - 1); ucontrol->value.enumerated.item[1] = (val >> 0) & (reg.enum_c - 1); - } - else + } else ucontrol->value.enumerated.item[0] = (val >> reg.lchan_shift) & (reg.enum_c - 1); snd_azf3328_dbgmixer("get_enum: %02x is %04x -> %d|%d (shift %02d, enum_c %d)\n", @@ -579,16 +603,13 @@ snd_azf3328_put_mixer_enum(struct snd_kcontrol *kcontrol, snd_azf3328_mixer_reg_decode(®, kcontrol->private_value); oreg = snd_azf3328_mixer_inw(chip, reg.reg); val = oreg; - if (reg.reg == IDX_MIXER_REC_SELECT) - { + if (reg.reg == IDX_MIXER_REC_SELECT) { if (ucontrol->value.enumerated.item[0] > reg.enum_c - 1U || ucontrol->value.enumerated.item[1] > reg.enum_c - 1U) return -EINVAL; val = (ucontrol->value.enumerated.item[0] << 8) | (ucontrol->value.enumerated.item[1] << 0); - } - else - { + } else { if (ucontrol->value.enumerated.item[0] > reg.enum_c - 1U) return -EINVAL; val &= ~((reg.enum_c - 1) << reg.lchan_shift); @@ -629,13 +650,14 @@ static const struct snd_kcontrol_new snd_azf3328_mixer_controls[] __devinitdata AZF3328_MIXER_VOL_MONO("Modem Playback Volume", IDX_MIXER_MODEMOUT, 0x1f, 1), AZF3328_MIXER_SWITCH("Modem Capture Switch", IDX_MIXER_MODEMIN, 15, 1), AZF3328_MIXER_VOL_MONO("Modem Capture Volume", IDX_MIXER_MODEMIN, 0x1f, 1), - AZF3328_MIXER_ENUM("Modem Out Select", IDX_MIXER_ADVCTL2, 2, 8), - AZF3328_MIXER_ENUM("Mono Select Source", IDX_MIXER_ADVCTL2, 2, 9), + AZF3328_MIXER_ENUM("Mic Select", IDX_MIXER_ADVCTL2, 2, 8), + AZF3328_MIXER_ENUM("Mono Output Select", IDX_MIXER_ADVCTL2, 2, 9), + AZF3328_MIXER_ENUM("PCM", IDX_MIXER_ADVCTL2, 2, 15), /* PCM Out Path, place in front since it controls *both* 3D and Bass/Treble! */ AZF3328_MIXER_VOL_SPECIAL("Tone Control - Treble", IDX_MIXER_BASSTREBLE, 0x07, 1, 0), AZF3328_MIXER_VOL_SPECIAL("Tone Control - Bass", IDX_MIXER_BASSTREBLE, 0x07, 9, 0), AZF3328_MIXER_SWITCH("3D Control - Switch", IDX_MIXER_ADVCTL2, 13, 0), - AZF3328_MIXER_VOL_SPECIAL("3D Control - Wide", IDX_MIXER_ADVCTL1, 0x07, 1, 0), /* "3D Width" */ - AZF3328_MIXER_VOL_SPECIAL("3D Control - Space", IDX_MIXER_ADVCTL1, 0x03, 8, 0), /* "Hifi 3D" */ + AZF3328_MIXER_VOL_SPECIAL("3D Control - Width", IDX_MIXER_ADVCTL1, 0x07, 1, 0), /* "3D Width" */ + AZF3328_MIXER_VOL_SPECIAL("3D Control - Depth", IDX_MIXER_ADVCTL1, 0x03, 8, 0), /* "Hifi 3D" */ #if MIXER_TESTING AZF3328_MIXER_SWITCH("0", IDX_MIXER_ADVCTL2, 0, 0), AZF3328_MIXER_SWITCH("1", IDX_MIXER_ADVCTL2, 1, 0), @@ -813,22 +835,18 @@ snd_azf3328_setdmaa(struct snd_azf3328 *chip, unsigned int is_running; snd_azf3328_dbgcallenter(); - if (do_recording) - { + if (do_recording) { /* access capture registers, i.e. skip playback reg section */ portbase = chip->codec_port + 0x20; is_running = chip->is_recording; - } - else - { + } else { /* access the playback register section */ portbase = chip->codec_port + 0x00; is_running = chip->is_playing; } /* AZF3328 uses a two buffer pointer DMA playback approach */ - if (!is_running) - { + if (!is_running) { unsigned long addr_area2; unsigned long count_areas, count_tmp; /* width 32bit -- overflow!! */ count_areas = size/2; @@ -961,6 +979,13 @@ snd_azf3328_playback_trigger(struct snd_pcm_substream *substream, int cmd) chip->is_playing = 1; snd_azf3328_dbgplay("STARTED PLAYBACK\n"); break; + case SNDRV_PCM_TRIGGER_RESUME: + snd_azf3328_dbgplay("RESUME PLAYBACK\n"); + /* resume playback if we were active */ + if (chip->is_playing) + snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, + snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS) | DMA_RESUME); + break; case SNDRV_PCM_TRIGGER_STOP: snd_azf3328_dbgplay("STOP PLAYBACK\n"); @@ -988,6 +1013,12 @@ snd_azf3328_playback_trigger(struct snd_pcm_substream *substream, int cmd) chip->is_playing = 0; snd_azf3328_dbgplay("STOPPED PLAYBACK\n"); break; + case SNDRV_PCM_TRIGGER_SUSPEND: + snd_azf3328_dbgplay("SUSPEND PLAYBACK\n"); + /* make sure playback is stopped */ + snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, + snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS) & ~DMA_RESUME); + break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_PUSH NIY!\n"); break; @@ -995,6 +1026,7 @@ snd_azf3328_playback_trigger(struct snd_pcm_substream *substream, int cmd) snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!\n"); break; default: + printk(KERN_ERR "FIXME: unknown trigger mode!\n"); return -EINVAL; } @@ -1068,6 +1100,13 @@ snd_azf3328_capture_trigger(struct snd_pcm_substream *substream, int cmd) chip->is_recording = 1; snd_azf3328_dbgplay("STARTED CAPTURE\n"); break; + case SNDRV_PCM_TRIGGER_RESUME: + snd_azf3328_dbgplay("RESUME CAPTURE\n"); + /* resume recording if we were active */ + if (chip->is_recording) + snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, + snd_azf3328_codec_inw(chip, IDX_IO_REC_FLAGS) | DMA_RESUME); + break; case SNDRV_PCM_TRIGGER_STOP: snd_azf3328_dbgplay("STOP CAPTURE\n"); @@ -1088,6 +1127,12 @@ snd_azf3328_capture_trigger(struct snd_pcm_substream *substream, int cmd) chip->is_recording = 0; snd_azf3328_dbgplay("STOPPED CAPTURE\n"); break; + case SNDRV_PCM_TRIGGER_SUSPEND: + snd_azf3328_dbgplay("SUSPEND CAPTURE\n"); + /* make sure recording is stopped */ + snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, + snd_azf3328_codec_inw(chip, IDX_IO_REC_FLAGS) & ~DMA_RESUME); + break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_PUSH NIY!\n"); break; @@ -1095,6 +1140,7 @@ snd_azf3328_capture_trigger(struct snd_pcm_substream *substream, int cmd) snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!\n"); break; default: + printk(KERN_ERR "FIXME: unknown trigger mode!\n"); return -EINVAL; } @@ -1163,8 +1209,7 @@ snd_azf3328_interrupt(int irq, void *dev_id, struct pt_regs *regs) snd_azf3328_codec_inw(chip, IDX_IO_PLAY_IRQTYPE), status); - if (status & IRQ_TIMER) - { + if (status & IRQ_TIMER) { /* snd_azf3328_dbgplay("timer %ld\n", inl(chip->codec_port+IDX_IO_TIMER_VALUE) & TIMER_VALUE_MASK); */ if (chip->timer) snd_timer_interrupt(chip->timer, chip->timer->sticks); @@ -1174,50 +1219,43 @@ snd_azf3328_interrupt(int irq, void *dev_id, struct pt_regs *regs) spin_unlock(&chip->reg_lock); snd_azf3328_dbgplay("azt3328: timer IRQ\n"); } - if (status & IRQ_PLAYBACK) - { + if (status & IRQ_PLAYBACK) { spin_lock(&chip->reg_lock); which = snd_azf3328_codec_inb(chip, IDX_IO_PLAY_IRQTYPE); /* ack all IRQ types immediately */ snd_azf3328_codec_outb(chip, IDX_IO_PLAY_IRQTYPE, which); spin_unlock(&chip->reg_lock); - if (chip->pcm && chip->playback_substream) - { + if (chip->pcm && chip->playback_substream) { snd_pcm_period_elapsed(chip->playback_substream); snd_azf3328_dbgplay("PLAY period done (#%x), @ %x\n", which, inl(chip->codec_port+IDX_IO_PLAY_DMA_CURRPOS)); - } - else + } else snd_azf3328_dbgplay("azt3328: ouch, irq handler problem!\n"); if (which & IRQ_PLAY_SOMETHING) snd_azf3328_dbgplay("azt3328: unknown play IRQ type occurred, please report!\n"); } - if (status & IRQ_RECORDING) - { + if (status & IRQ_RECORDING) { spin_lock(&chip->reg_lock); which = snd_azf3328_codec_inb(chip, IDX_IO_REC_IRQTYPE); /* ack all IRQ types immediately */ snd_azf3328_codec_outb(chip, IDX_IO_REC_IRQTYPE, which); spin_unlock(&chip->reg_lock); - if (chip->pcm && chip->capture_substream) - { + if (chip->pcm && chip->capture_substream) { snd_pcm_period_elapsed(chip->capture_substream); snd_azf3328_dbgplay("REC period done (#%x), @ %x\n", which, inl(chip->codec_port+IDX_IO_REC_DMA_CURRPOS)); - } - else + } else snd_azf3328_dbgplay("azt3328: ouch, irq handler problem!\n"); if (which & IRQ_REC_SOMETHING) snd_azf3328_dbgplay("azt3328: unknown rec IRQ type occurred, please report!\n"); } /* MPU401 has less critical IRQ requirements * than timer and playback/recording, right? */ - if (status & IRQ_MPU401) - { + if (status & IRQ_MPU401) { snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs); /* hmm, do we have to ack the IRQ here somehow? @@ -1511,8 +1549,7 @@ snd_azf3328_timer_start(struct snd_timer *timer) snd_azf3328_dbgcallenter(); chip = snd_timer_chip(timer); delay = ((timer->sticks * seqtimer_scaling) - 1) & TIMER_VALUE_MASK; - if (delay < 49) - { + if (delay < 49) { /* uhoh, that's not good, since user-space won't know about * this timing tweak * (we need to do it to avoid a lockup, though) */ @@ -1766,9 +1803,11 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) goto out_err; } + card->private_data = chip; + if ((err = snd_mpu401_uart_new( card, 0, MPU401_HW_MPU401, - chip->mpu_port, 1, pci->irq, 0, - &chip->rmidi)) < 0) { + chip->mpu_port, MPU401_INFO_INTEGRATED, + pci->irq, 0, &chip->rmidi)) < 0) { snd_printk(KERN_ERR "azf3328: no MPU-401 device at 0x%lx?\n", chip->mpu_port); goto out_err; } @@ -1791,6 +1830,8 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) } } + opl3->private_data = chip; + sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, chip->codec_port, chip->irq); @@ -1834,11 +1875,80 @@ snd_azf3328_remove(struct pci_dev *pci) snd_azf3328_dbgcallleave(); } +#ifdef CONFIG_PM +static int +snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state) +{ + struct snd_card *card = pci_get_drvdata(pci); + struct snd_azf3328 *chip = card->private_data; + int reg; + + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + + snd_pcm_suspend_all(chip->pcm); + + for (reg = 0; reg < AZF_IO_SIZE_MIXER_PM / 2; reg++) + chip->saved_regs_mixer[reg] = inw(chip->mixer_port + reg * 2); + + /* make sure to disable master volume etc. to prevent looping sound */ + snd_azf3328_mixer_set_mute(chip, IDX_MIXER_PLAY_MASTER, 1); + snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1); + + for (reg = 0; reg < AZF_IO_SIZE_CODEC_PM / 2; reg++) + chip->saved_regs_codec[reg] = inw(chip->codec_port + reg * 2); + for (reg = 0; reg < AZF_IO_SIZE_IO2_PM / 2; reg++) + chip->saved_regs_io2[reg] = inw(chip->io2_port + reg * 2); + for (reg = 0; reg < AZF_IO_SIZE_MPU_PM / 2; reg++) + chip->saved_regs_mpu[reg] = inw(chip->mpu_port + reg * 2); + for (reg = 0; reg < AZF_IO_SIZE_SYNTH_PM / 2; reg++) + chip->saved_regs_synth[reg] = inw(chip->synth_port + reg * 2); + + pci_set_power_state(pci, PCI_D3hot); + pci_disable_device(pci); + pci_save_state(pci); + return 0; +} + +static int +snd_azf3328_resume(struct pci_dev *pci) +{ + struct snd_card *card = pci_get_drvdata(pci); + struct snd_azf3328 *chip = card->private_data; + int reg; + + pci_restore_state(pci); + pci_enable_device(pci); + pci_set_power_state(pci, PCI_D0); + pci_set_master(pci); + + for (reg = 0; reg < AZF_IO_SIZE_IO2_PM / 2; reg++) + outw(chip->saved_regs_io2[reg], chip->io2_port + reg * 2); + for (reg = 0; reg < AZF_IO_SIZE_MPU_PM / 2; reg++) + outw(chip->saved_regs_mpu[reg], chip->mpu_port + reg * 2); + for (reg = 0; reg < AZF_IO_SIZE_SYNTH_PM / 2; reg++) + outw(chip->saved_regs_synth[reg], chip->synth_port + reg * 2); + for (reg = 0; reg < AZF_IO_SIZE_MIXER_PM / 2; reg++) + outw(chip->saved_regs_mixer[reg], chip->mixer_port + reg * 2); + for (reg = 0; reg < AZF_IO_SIZE_CODEC_PM / 2; reg++) + outw(chip->saved_regs_codec[reg], chip->codec_port + reg * 2); + + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + return 0; +} +#endif + + + + static struct pci_driver driver = { .name = "AZF3328", .id_table = snd_azf3328_ids, .probe = snd_azf3328_probe, .remove = __devexit_p(snd_azf3328_remove), +#ifdef CONFIG_PM + .suspend = snd_azf3328_suspend, + .resume = snd_azf3328_resume, +#endif }; static int __init diff --git a/sound/pci/azt3328.h b/sound/pci/azt3328.h index f489bdaf6d4..b4f3e3cd006 100644 --- a/sound/pci/azt3328.h +++ b/sound/pci/azt3328.h @@ -5,6 +5,9 @@ /*** main I/O area port indices ***/ /* (only 0x70 of 0x80 bytes saved/restored by Windows driver) */ +#define AZF_IO_SIZE_CODEC 0x80 +#define AZF_IO_SIZE_CODEC_PM 0x70 + /* the driver initialisation suggests a layout of 4 main areas: * from 0x00 (playback), from 0x20 (recording) and from 0x40 (maybe MPU401??). * And another area from 0x60 to 0x6f (DirectX timer, IRQ management, @@ -87,7 +90,7 @@ #define IDX_IO_REC_DMA_CURROFS 0x34 /* PU:0x00000000 */ #define IDX_IO_REC_SOUNDFORMAT 0x36 /* PU:0x0000 */ -/** hmm, what is this I/O area for? MPU401?? (after playback, recording, ???, timer) **/ +/** hmm, what is this I/O area for? MPU401?? or external DAC via I2S?? (after playback, recording, ???, timer) **/ #define IDX_IO_SOMETHING_FLAGS 0x40 /* gets set to 0x34 just like port 0x0 and 0x20 on card init, PU:0x0000 */ /* general */ #define IDX_IO_42H 0x42 /* PU:0x0001 */ @@ -107,7 +110,8 @@ #define IRQ_UNKNOWN2 0x0080 /* probably unused */ #define IDX_IO_66H 0x66 /* writing 0xffff returns 0x0000 */ #define IDX_IO_SOME_VALUE 0x68 /* this is set to e.g. 0x3ff or 0x300, and writable; maybe some buffer limit, but I couldn't find out more, PU:0x00ff */ -#define IDX_IO_6AH 0x6A /* this WORD can be set to have bits 0x0028 activated; actually inhibits PCM playback!!! maybe power management?? */ +#define IDX_IO_6AH 0x6A /* this WORD can be set to have bits 0x0028 activated (FIXME: correct??); actually inhibits PCM playback!!! maybe power management?? */ + #define IO_6A_PAUSE_PLAYBACK 0x0200 /* bit 9; sure, this pauses playback, but what the heck is this really about?? */ #define IDX_IO_6CH 0x6C #define IDX_IO_6EH 0x6E /* writing 0xffff returns 0x83fe */ /* further I/O indices not saved/restored, so probably not used */ @@ -115,15 +119,25 @@ /*** I/O 2 area port indices ***/ /* (only 0x06 of 0x08 bytes saved/restored by Windows driver) */ +#define AZF_IO_SIZE_IO2 0x08 +#define AZF_IO_SIZE_IO2_PM 0x06 + #define IDX_IO2_LEGACY_ADDR 0x04 #define LEGACY_SOMETHING 0x01 /* OPL3?? */ #define LEGACY_JOY 0x08 +#define AZF_IO_SIZE_MPU 0x04 +#define AZF_IO_SIZE_MPU_PM 0x04 + +#define AZF_IO_SIZE_SYNTH 0x08 +#define AZF_IO_SIZE_SYNTH_PM 0x06 /*** mixer I/O area port indices ***/ /* (only 0x22 of 0x40 bytes saved/restored by Windows driver) - * generally spoken: AC97 register index = AZF3328 mixer reg index + 2 - * (in other words: AZF3328 NOT fully AC97 compliant) */ + * UNFORTUNATELY azf3328 is NOT truly AC97 compliant: see main file intro */ +#define AZF_IO_SIZE_MIXER 0x40 +#define AZF_IO_SIZE_MIXER_PM 0x22 + #define MIXER_VOLUME_RIGHT_MASK 0x001f #define MIXER_VOLUME_LEFT_MASK 0x1f00 #define MIXER_MUTE_MASK 0x8000 @@ -156,14 +170,14 @@ #define IDX_MIXER_ADVCTL1 0x1e /* unlisted bits are unmodifiable */ #define MIXER_ADVCTL1_3DWIDTH_MASK 0x000e - #define MIXER_ADVCTL1_HIFI3D_MASK 0x0300 -#define IDX_MIXER_ADVCTL2 0x20 /* resembles AC97_GENERAL_PURPOSE reg! */ + #define MIXER_ADVCTL1_HIFI3D_MASK 0x0300 /* yup, this is missing the high bit that official AC97 contains, plus it doesn't have linear bit value range behaviour but instead acts weirdly (possibly we're dealing with two *different* 3D settings here??) */ +#define IDX_MIXER_ADVCTL2 0x20 /* subset of AC97_GENERAL_PURPOSE reg! */ /* unlisted bits are unmodifiable */ - #define MIXER_ADVCTL2_BIT7 0x0080 /* WaveOut 3D Bypass? mutes WaveOut at LineOut */ - #define MIXER_ADVCTL2_BIT8 0x0100 /* is this Modem Out Select? */ - #define MIXER_ADVCTL2_BIT9 0x0200 /* Mono Select Source? */ - #define MIXER_ADVCTL2_BIT13 0x2000 /* 3D enable? */ - #define MIXER_ADVCTL2_BIT15 0x8000 /* unknown */ + #define MIXER_ADVCTL2_LPBK 0x0080 /* Loopback mode -- Win driver: "WaveOut3DBypass"? mutes WaveOut at LineOut */ + #define MIXER_ADVCTL2_MS 0x0100 /* Mic Select 0=Mic1, 1=Mic2 -- Win driver: "ModemOutSelect"?? */ + #define MIXER_ADVCTL2_MIX 0x0200 /* Mono output select 0=Mix, 1=Mic; Win driver: "MonoSelectSource"?? */ + #define MIXER_ADVCTL2_3D 0x2000 /* 3D Enhancement 1=on */ + #define MIXER_ADVCTL2_POP 0x8000 /* Pcm Out Path, 0=pre 3D, 1=post 3D */ #define IDX_MIXER_SOMETHING30H 0x30 /* used, but unknown??? */ diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c index 9ee07d4aac1..497ed6b2006 100644 --- a/sound/pci/bt87x.c +++ b/sound/pci/bt87x.c @@ -44,7 +44,7 @@ MODULE_SUPPORTED_DEVICE("{{Brooktree,Bt878}," static int index[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -2}; /* Exclude the first card */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ -static int digital_rate[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = 0 }; /* digital input rate */ +static int digital_rate[SNDRV_CARDS]; /* digital input rate */ static int load_all; /* allow to load the non-whitelisted cards */ module_param_array(index, int, NULL, 0444); @@ -781,10 +781,12 @@ static struct pci_device_id snd_bt87x_ids[] __devinitdata = { BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_879, 0x0070, 0x13eb, 32000), /* Viewcast Osprey 200 */ BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x0070, 0xff01, 44100), - /* AVerMedia Studio No. 103, 203, ...? */ - BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x1461, 0x0003, 48000), /* Leadtek Winfast tv 2000xp delux */ BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x107d, 0x6606, 32000), + /* Voodoo TV 200 */ + BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x121a, 0x3000, 32000), + /* AVerMedia Studio No. 103, 203, ...? */ + BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x1461, 0x0003, 48000), { } }; MODULE_DEVICE_TABLE(pci, snd_bt87x_ids); @@ -886,8 +888,9 @@ static int __devinit snd_bt87x_probe(struct pci_dev *pci, strcpy(card->driver, "Bt87x"); sprintf(card->shortname, "Brooktree Bt%x", pci->device); - sprintf(card->longname, "%s at %#lx, irq %i", - card->shortname, pci_resource_start(pci, 0), chip->irq); + sprintf(card->longname, "%s at %#llx, irq %i", + card->shortname, (unsigned long long)pci_resource_start(pci, 0), + chip->irq); strcpy(card->mixername, "Bt87x"); err = snd_card_register(card); diff --git a/sound/pci/ca0106/ca0106.h b/sound/pci/ca0106/ca0106.h index c8131ea92ed..9cb66c59f52 100644 --- a/sound/pci/ca0106/ca0106.h +++ b/sound/pci/ca0106/ca0106.h @@ -537,9 +537,9 @@ #endif #define ADC_MUX_MASK 0x0000000f //Mask for ADC Mux +#define ADC_MUX_PHONE 0x00000001 //Value to select TAD at ADC Mux (Not used) #define ADC_MUX_MIC 0x00000002 //Value to select Mic at ADC Mux #define ADC_MUX_LINEIN 0x00000004 //Value to select LineIn at ADC Mux -#define ADC_MUX_PHONE 0x00000001 //Value to select TAD at ADC Mux (Not used) #define ADC_MUX_AUX 0x00000008 //Value to select Aux at ADC Mux #define SET_CHANNEL 0 /* Testing channel outputs 0=Front, 1=Center/LFE, 2=Unknown, 3=Rear */ @@ -604,6 +604,8 @@ struct snd_ca0106 { u32 spdif_bits[4]; /* s/pdif out setup */ int spdif_enable; int capture_source; + int i2c_capture_source; + u8 i2c_capture_volume[4][2]; int capture_mic_line_in; struct snd_dma_buffer buffer; diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c index fd8bfebfbd5..59bf9bd0253 100644 --- a/sound/pci/ca0106/ca0106_main.c +++ b/sound/pci/ca0106/ca0106_main.c @@ -186,8 +186,8 @@ static struct snd_ca0106_details ca0106_chip_details[] = { /* New Audigy SE. Has a different DAC. */ /* SB0570: * CTRL:CA0106-DAT - * ADC: WM8768GEDS - * DAC: WM8775EDS + * ADC: WM8775EDS + * DAC: WM8768GEDS */ { .serial = 0x100a1102, .name = "Audigy SE [SB0570]", @@ -195,9 +195,14 @@ static struct snd_ca0106_details ca0106_chip_details[] = { .i2c_adc = 1, .spi_dac = 1 } , /* MSI K8N Diamond Motherboard with onboard SB Live 24bit without AC97 */ + /* SB0438 + * CTRL:CA0106-DAT + * ADC: WM8775SEDS + * DAC: CS4382-KQZ + */ { .serial = 0x10091462, .name = "MSI K8N Diamond MB [SB0438]", - .gpio_type = 1, + .gpio_type = 2, .i2c_adc = 1 } , /* Shuttle XPC SD31P which has an onboard Creative Labs * Sound Blaster Live! 24-bit EAX @@ -326,6 +331,7 @@ int snd_ca0106_spi_write(struct snd_ca0106 * emu, return 0; } +/* The ADC does not support i2c read, so only write is implemented */ int snd_ca0106_i2c_write(struct snd_ca0106 *emu, u32 reg, u32 value) @@ -340,6 +346,7 @@ int snd_ca0106_i2c_write(struct snd_ca0106 *emu, } 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_ca0106_ptr_write(emu, I2C_D0, 0, tmp); */ @@ -348,8 +355,9 @@ int snd_ca0106_i2c_write(struct snd_ca0106 *emu, for (retry = 0; retry < 10; retry++) { /* Send the data to i2c */ - tmp = snd_ca0106_ptr_read(emu, I2C_A, 0); - tmp = tmp & ~(I2C_A_ADC_READ|I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD_MASK); + //tmp = snd_ca0106_ptr_read(emu, I2C_A, 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_ca0106_ptr_write(emu, I2C_A, 0, tmp); @@ -1181,7 +1189,7 @@ static unsigned int spi_dac_init[] = { 0x02ff, 0x0400, 0x0520, - 0x0600, + 0x0620, /* Set 24 bit. Was 0x0600 */ 0x08ff, 0x0aff, 0x0cff, @@ -1200,6 +1208,22 @@ static unsigned int spi_dac_init[] = { 0x1400, }; +static unsigned int i2c_adc_init[][2] = { + { 0x17, 0x00 }, /* Reset */ + { 0x07, 0x00 }, /* Timeout */ + { 0x0b, 0x22 }, /* Interface control */ + { 0x0c, 0x22 }, /* Master mode control */ + { 0x0d, 0x08 }, /* Powerdown control */ + { 0x0e, 0xcf }, /* Attenuation Left 0x01 = -103dB, 0xff = 24dB */ + { 0x0f, 0xcf }, /* Attenuation Right 0.5dB steps */ + { 0x10, 0x7b }, /* ALC Control 1 */ + { 0x11, 0x00 }, /* ALC Control 2 */ + { 0x12, 0x32 }, /* ALC Control 3 */ + { 0x13, 0x00 }, /* Noise gate control */ + { 0x14, 0xa6 }, /* Limiter control */ + { 0x15, ADC_MUX_LINEIN }, /* ADC Mixer control */ +}; + static int __devinit snd_ca0106_create(struct snd_card *card, struct pci_dev *pci, struct snd_ca0106 **rchip) @@ -1361,7 +1385,12 @@ static int __devinit snd_ca0106_create(struct snd_card *card, snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x333300e4); /* Select MIC, Line in, TAD in, AUX in */ chip->capture_source = 3; /* Set CAPTURE_SOURCE */ - if (chip->details->gpio_type == 1) { /* The SB0410 and SB0413 use GPIO differently. */ + if (chip->details->gpio_type == 2) { /* The SB0438 use GPIO differently. */ + /* FIXME: Still need to find out what the other GPIO bits do. E.g. For digital spdif out. */ + outl(0x0, chip->port+GPIO); + //outl(0x00f0e000, chip->port+GPIO); /* Analog */ + outl(0x005f5301, chip->port+GPIO); /* Analog */ + } else if (chip->details->gpio_type == 1) { /* The SB0410 and SB0413 use GPIO differently. */ /* FIXME: Still need to find out what the other GPIO bits do. E.g. For digital spdif out. */ outl(0x0, chip->port+GPIO); //outl(0x00f0e000, chip->port+GPIO); /* Analog */ @@ -1379,7 +1408,19 @@ static int __devinit snd_ca0106_create(struct snd_card *card, outl(HCFG_AC97 | HCFG_AUDIOENABLE, chip->port+HCFG); /* AC97 2.0, Enable outputs. */ if (chip->details->i2c_adc == 1) { /* The SB0410 and SB0413 use I2C to control ADC. */ - snd_ca0106_i2c_write(chip, ADC_MUX, ADC_MUX_LINEIN); /* Enable Line-in capture. MIC in currently untested. */ + int size, n; + + size = ARRAY_SIZE(i2c_adc_init); + //snd_printk("I2C:array size=0x%x\n", size); + for (n=0; n < size; n++) { + snd_ca0106_i2c_write(chip, i2c_adc_init[n][0], i2c_adc_init[n][1]); + } + for (n=0; n < 4; n++) { + chip->i2c_capture_volume[n][0]= 0xcf; + chip->i2c_capture_volume[n][1]= 0xcf; + } + chip->i2c_capture_source=2; /* Line in */ + //snd_ca0106_i2c_write(chip, ADC_MUX, ADC_MUX_LINEIN); /* Enable Line-in capture. MIC in currently untested. */ } if (chip->details->spi_dac == 1) { /* The SB0570 use SPI to control DAC. */ int size, n; diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c index 06fe055674f..146eed70dce 100644 --- a/sound/pci/ca0106/ca0106_mixer.c +++ b/sound/pci/ca0106/ca0106_mixer.c @@ -171,6 +171,76 @@ static int snd_ca0106_capture_source_put(struct snd_kcontrol *kcontrol, return change; } +static int snd_ca0106_i2c_capture_source_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *texts[6] = { + "Phone", "Mic", "Line in", "Aux" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item > 3) + uinfo->value.enumerated.item = 3; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_ca0106_i2c_capture_source_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = emu->i2c_capture_source; + return 0; +} + +static int snd_ca0106_i2c_capture_source_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); + unsigned int source_id; + unsigned int ngain, ogain; + int change = 0; + u32 source; + /* If the capture source has changed, + * update the capture volume from the cached value + * for the particular source. + */ + source_id = ucontrol->value.enumerated.item[0] ; + change = (emu->i2c_capture_source != source_id); + if (change) { + snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */ + ngain = emu->i2c_capture_volume[source_id][0]; /* Left */ + ogain = emu->i2c_capture_volume[emu->i2c_capture_source][0]; /* Left */ + if (ngain != ogain) + snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff)); + ngain = emu->i2c_capture_volume[source_id][1]; /* Left */ + ogain = emu->i2c_capture_volume[emu->i2c_capture_source][1]; /* Left */ + if (ngain != ogain) + snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff)); + source = 1 << source_id; + snd_ca0106_i2c_write(emu, ADC_MUX, source); /* Set source */ + emu->i2c_capture_source = source_id; + } + return change; +} + +static int snd_ca0106_capture_line_in_side_out_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *texts[2] = { "Side out", "Line in" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + if (uinfo->value.enumerated.item > 1) + uinfo->value.enumerated.item = 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + static int snd_ca0106_capture_mic_line_in_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -207,16 +277,16 @@ static int snd_ca0106_capture_mic_line_in_put(struct snd_kcontrol *kcontrol, if (change) { emu->capture_mic_line_in = val; if (val) { - snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_PHONE); /* Mute input */ + //snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */ tmp = inl(emu->port+GPIO) & ~0x400; tmp = tmp | 0x400; outl(tmp, emu->port+GPIO); - snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC); + //snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC); } else { - snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_PHONE); /* Mute input */ + //snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */ tmp = inl(emu->port+GPIO) & ~0x400; outl(tmp, emu->port+GPIO); - snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN); + //snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN); } } return change; @@ -225,12 +295,22 @@ static int snd_ca0106_capture_mic_line_in_put(struct snd_kcontrol *kcontrol, static struct snd_kcontrol_new snd_ca0106_capture_mic_line_in __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Mic/Line in Capture", + .name = "Shared Mic/Line in Capture Switch", .info = snd_ca0106_capture_mic_line_in_info, .get = snd_ca0106_capture_mic_line_in_get, .put = snd_ca0106_capture_mic_line_in_put }; +static struct snd_kcontrol_new snd_ca0106_capture_line_in_side_out __devinitdata = +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Shared Line in/Side out Capture Switch", + .info = snd_ca0106_capture_line_in_side_out_info, + .get = snd_ca0106_capture_mic_line_in_get, + .put = snd_ca0106_capture_mic_line_in_put +}; + + static int snd_ca0106_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -329,15 +409,81 @@ static int snd_ca0106_volume_put(struct snd_kcontrol *kcontrol, return 1; } +static int snd_ca0106_i2c_volume_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 = 255; + return 0; +} + +static int snd_ca0106_i2c_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); + int source_id; + + source_id = kcontrol->private_value; + + ucontrol->value.integer.value[0] = emu->i2c_capture_volume[source_id][0]; + ucontrol->value.integer.value[1] = emu->i2c_capture_volume[source_id][1]; + return 0; +} + +static int snd_ca0106_i2c_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); + unsigned int ogain; + unsigned int ngain; + int source_id; + int change = 0; + + source_id = kcontrol->private_value; + ogain = emu->i2c_capture_volume[source_id][0]; /* Left */ + ngain = ucontrol->value.integer.value[0]; + if (ngain > 0xff) + return 0; + if (ogain != ngain) { + if (emu->i2c_capture_source == source_id) + snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff) ); + emu->i2c_capture_volume[source_id][0] = ucontrol->value.integer.value[0]; + change = 1; + } + ogain = emu->i2c_capture_volume[source_id][1]; /* Right */ + ngain = ucontrol->value.integer.value[1]; + if (ngain > 0xff) + return 0; + if (ogain != ngain) { + if (emu->i2c_capture_source == source_id) + snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff)); + emu->i2c_capture_volume[source_id][1] = ucontrol->value.integer.value[1]; + change = 1; + } + + return change; +} + #define CA_VOLUME(xname,chid,reg) \ { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ - .info = snd_ca0106_volume_info, \ - .get = snd_ca0106_volume_get, \ - .put = snd_ca0106_volume_put, \ + .info = snd_ca0106_volume_info, \ + .get = snd_ca0106_volume_get, \ + .put = snd_ca0106_volume_put, \ .private_value = ((chid) << 8) | (reg) \ } +#define I2C_VOLUME(xname,chid) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_ca0106_i2c_volume_info, \ + .get = snd_ca0106_i2c_volume_get, \ + .put = snd_ca0106_i2c_volume_put, \ + .private_value = chid \ +} + static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = { CA_VOLUME("Analog Front Playback Volume", @@ -361,6 +507,11 @@ static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = { CA_VOLUME("CAPTURE feedback Playback Volume", 1, CAPTURE_CONTROL), + I2C_VOLUME("Phone Capture Volume", 0), + I2C_VOLUME("Mic Capture Volume", 1), + I2C_VOLUME("Line in Capture Volume", 2), + I2C_VOLUME("Aux Capture Volume", 3), + { .access = SNDRV_CTL_ELEM_ACCESS_READ, .iface = SNDRV_CTL_ELEM_IFACE_PCM, @@ -378,12 +529,19 @@ static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = { }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", + .name = "Digital Capture Source", .info = snd_ca0106_capture_source_info, .get = snd_ca0106_capture_source_get, .put = snd_ca0106_capture_source_put }, { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = snd_ca0106_i2c_capture_source_info, + .get = snd_ca0106_i2c_capture_source_get, + .put = snd_ca0106_i2c_capture_source_put + }, + { .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), .count = 4, @@ -477,7 +635,10 @@ int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu) return err; } if (emu->details->i2c_adc == 1) { - err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_mic_line_in, emu)); + if (emu->details->gpio_type == 1) + err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_mic_line_in, emu)); + else /* gpio_type == 2 */ + err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_line_in_side_out, emu)); if (err < 0) return err; } diff --git a/sound/pci/ca0106/ca0106_proc.c b/sound/pci/ca0106/ca0106_proc.c index 63757273bfb..75ca421eb3a 100644 --- a/sound/pci/ca0106/ca0106_proc.c +++ b/sound/pci/ca0106/ca0106_proc.c @@ -431,33 +431,30 @@ int __devinit snd_ca0106_proc_init(struct snd_ca0106 * emu) struct snd_info_entry *entry; if(! snd_card_proc_new(emu->card, "iec958", &entry)) - snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_iec958); + snd_info_set_text_ops(entry, emu, snd_ca0106_proc_iec958); if(! snd_card_proc_new(emu->card, "ca0106_reg32", &entry)) { - snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read32); - entry->c.text.write_size = 64; + snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read32); entry->c.text.write = snd_ca0106_proc_reg_write32; entry->mode |= S_IWUSR; } if(! snd_card_proc_new(emu->card, "ca0106_reg16", &entry)) - snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read16); + snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read16); if(! snd_card_proc_new(emu->card, "ca0106_reg8", &entry)) - snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read8); + snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read8); if(! snd_card_proc_new(emu->card, "ca0106_regs1", &entry)) { - snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read1); - entry->c.text.write_size = 64; + snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read1); entry->c.text.write = snd_ca0106_proc_reg_write; entry->mode |= S_IWUSR; // entry->private_data = emu; } if(! snd_card_proc_new(emu->card, "ca0106_i2c", &entry)) { - snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_i2c_write); - entry->c.text.write_size = 64; + snd_info_set_text_ops(entry, emu, snd_ca0106_proc_i2c_write); entry->c.text.write = snd_ca0106_proc_i2c_write; entry->mode |= S_IWUSR; // entry->private_data = emu; } if(! snd_card_proc_new(emu->card, "ca0106_regs2", &entry)) - snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read2); + snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read2); return 0; } diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index e5ce2dabd08..0938c158b5c 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -2121,7 +2121,7 @@ static struct snd_kcontrol_new snd_cmipci_mixers[] __devinitdata = { CMIPCI_MIXER_VOL_MONO("Mic Capture Volume", CM_REG_MIXER2, CM_VADMIC_SHIFT, 7), CMIPCI_SB_VOL_MONO("Phone Playback Volume", CM_REG_EXTENT_IND, 5, 7), CMIPCI_DOUBLE("Phone Playback Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 4, 4, 1, 0, 0), - CMIPCI_DOUBLE("PC Speaker Playnack Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 3, 3, 1, 0, 0), + CMIPCI_DOUBLE("PC Speaker Playback Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 3, 3, 1, 0, 0), CMIPCI_DOUBLE("Mic Boost Capture Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 0, 0, 1, 0, 0), }; @@ -2602,7 +2602,7 @@ static void __devinit snd_cmipci_proc_init(struct cmipci *cm) struct snd_info_entry *entry; if (! snd_card_proc_new(cm->card, "cmipci", &entry)) - snd_info_set_text_ops(entry, cm, 1024, snd_cmipci_proc_read); + snd_info_set_text_ops(entry, cm, snd_cmipci_proc_read); } #else /* !CONFIG_PROC_FS */ static inline void snd_cmipci_proc_init(struct cmipci *cm) {} @@ -2932,7 +2932,7 @@ static int __devinit snd_cmipci_create(struct snd_card *card, struct pci_dev *pc } integrated_midi = snd_cmipci_read_b(cm, CM_REG_MPU_PCI) != 0xff; - if (integrated_midi) + if (integrated_midi && mpu_port[dev] == 1) iomidi = cm->iobase + CM_REG_MPU_PCI; else { iomidi = mpu_port[dev]; @@ -2981,7 +2981,9 @@ static int __devinit snd_cmipci_create(struct snd_card *card, struct pci_dev *pc if (iomidi > 0) { if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI, - iomidi, integrated_midi, + iomidi, + (integrated_midi ? + MPU401_INFO_INTEGRATED : 0), cm->irq, 0, &cm->rmidi)) < 0) { printk(KERN_ERR "cmipci: no UART401 device at 0x%lx\n", iomidi); } diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index b3c94d83450..e77a4ce314b 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -1184,7 +1184,7 @@ static void __devinit snd_cs4281_proc_init(struct cs4281 * chip) struct snd_info_entry *entry; if (! snd_card_proc_new(chip->card, "cs4281", &entry)) - snd_info_set_text_ops(entry, chip, 1024, snd_cs4281_proc_read); + snd_info_set_text_ops(entry, chip, snd_cs4281_proc_read); if (! snd_card_proc_new(chip->card, "cs4281_BA0", &entry)) { entry->content = SNDRV_INFO_CONTENT_DATA; entry->private_data = chip; @@ -1379,6 +1379,13 @@ static int __devinit snd_cs4281_create(struct snd_card *card, chip->ba0_addr = pci_resource_start(pci, 0); chip->ba1_addr = pci_resource_start(pci, 1); + chip->ba0 = ioremap_nocache(chip->ba0_addr, pci_resource_len(pci, 0)); + chip->ba1 = ioremap_nocache(chip->ba1_addr, pci_resource_len(pci, 1)); + if (!chip->ba0 || !chip->ba1) { + snd_cs4281_free(chip); + return -ENOMEM; + } + if (request_irq(pci->irq, snd_cs4281_interrupt, SA_INTERRUPT|SA_SHIRQ, "CS4281", chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); @@ -1387,13 +1394,6 @@ static int __devinit snd_cs4281_create(struct snd_card *card, } chip->irq = pci->irq; - chip->ba0 = ioremap_nocache(chip->ba0_addr, pci_resource_len(pci, 0)); - chip->ba1 = ioremap_nocache(chip->ba1_addr, pci_resource_len(pci, 1)); - if (!chip->ba0 || !chip->ba1) { - snd_cs4281_free(chip); - return -ENOMEM; - } - tmp = snd_cs4281_chip_init(chip); if (tmp) { snd_cs4281_free(chip); diff --git a/sound/pci/cs46xx/cs46xx.c b/sound/pci/cs46xx/cs46xx.c index 848d772ae3c..772dc52bfeb 100644 --- a/sound/pci/cs46xx/cs46xx.c +++ b/sound/pci/cs46xx/cs46xx.c @@ -48,8 +48,8 @@ MODULE_SUPPORTED_DEVICE("{{Cirrus Logic,Sound Fusion (CS4280)}," static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ -static int external_amp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; -static int thinkpad[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +static int external_amp[SNDRV_CARDS]; +static int thinkpad[SNDRV_CARDS]; static int mmap_valid[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; module_param_array(index, int, NULL, 0444); diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index 69dbf542a6d..5c211443920 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -2877,14 +2877,15 @@ static int snd_cs46xx_free(struct snd_cs46xx *chip) if (chip->region.idx[0].resource) snd_cs46xx_hw_stop(chip); + if (chip->irq >= 0) + free_irq(chip->irq, chip); + for (idx = 0; idx < 5; idx++) { struct snd_cs46xx_region *region = &chip->region.idx[idx]; if (region->remap_addr) iounmap(region->remap_addr); release_and_free_resource(region->resource); } - if (chip->irq >= 0) - free_irq(chip->irq, chip); if (chip->active_ctrl) chip->active_ctrl(chip, -chip->amplifier); diff --git a/sound/pci/cs46xx/dsp_spos.c b/sound/pci/cs46xx/dsp_spos.c index f407d2a5ce3..5c9711c0265 100644 --- a/sound/pci/cs46xx/dsp_spos.c +++ b/sound/pci/cs46xx/dsp_spos.c @@ -767,7 +767,6 @@ int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip) if ((entry = snd_info_create_card_entry(card, "dsp", card->proc_root)) != NULL) { entry->content = SNDRV_INFO_CONTENT_TEXT; entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; - entry->c.text.read_size = 512; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); @@ -784,7 +783,6 @@ int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip) entry->content = SNDRV_INFO_CONTENT_TEXT; entry->private_data = chip; entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 512; entry->c.text.read = cs46xx_dsp_proc_symbol_table_read; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); @@ -797,7 +795,6 @@ int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip) entry->content = SNDRV_INFO_CONTENT_TEXT; entry->private_data = chip; entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 512; entry->c.text.read = cs46xx_dsp_proc_modules_read; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); @@ -810,7 +807,6 @@ int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip) entry->content = SNDRV_INFO_CONTENT_TEXT; entry->private_data = chip; entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 512; entry->c.text.read = cs46xx_dsp_proc_parameter_dump_read; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); @@ -823,7 +819,6 @@ int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip) entry->content = SNDRV_INFO_CONTENT_TEXT; entry->private_data = chip; entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 512; entry->c.text.read = cs46xx_dsp_proc_sample_dump_read; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); @@ -836,7 +831,6 @@ int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip) entry->content = SNDRV_INFO_CONTENT_TEXT; entry->private_data = chip; entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 512; entry->c.text.read = cs46xx_dsp_proc_task_tree_read; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); @@ -849,7 +843,6 @@ int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip) entry->content = SNDRV_INFO_CONTENT_TEXT; entry->private_data = chip; entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 1024; entry->c.text.read = cs46xx_dsp_proc_scb_read; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); diff --git a/sound/pci/cs46xx/dsp_spos_scb_lib.c b/sound/pci/cs46xx/dsp_spos_scb_lib.c index 2c4ee45fe10..3844d18af19 100644 --- a/sound/pci/cs46xx/dsp_spos_scb_lib.c +++ b/sound/pci/cs46xx/dsp_spos_scb_lib.c @@ -267,7 +267,6 @@ void cs46xx_dsp_proc_register_scb_desc (struct snd_cs46xx *chip, entry->private_data = scb_info; entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 512; entry->c.text.read = cs46xx_dsp_proc_scb_info_read; if (snd_info_register(entry) < 0) { diff --git a/sound/pci/cs5535audio/Makefile b/sound/pci/cs5535audio/Makefile index 08d8ee6547d..2911a8adc1f 100644 --- a/sound/pci/cs5535audio/Makefile +++ b/sound/pci/cs5535audio/Makefile @@ -4,5 +4,9 @@ snd-cs5535audio-objs := cs5535audio.o cs5535audio_pcm.o +ifdef CONFIG_PM +snd-cs5535audio-objs += cs5535audio_pm.o +endif + # Toplevel Module Dependency obj-$(CONFIG_SND_CS5535AUDIO) += snd-cs5535audio.o diff --git a/sound/pci/cs5535audio/cs5535audio.c b/sound/pci/cs5535audio/cs5535audio.c index 2c1213a35dc..91c18a11fe8 100644 --- a/sound/pci/cs5535audio/cs5535audio.c +++ b/sound/pci/cs5535audio/cs5535audio.c @@ -1,5 +1,5 @@ /* - * Driver for audio on multifunction CS5535 companion device + * Driver for audio on multifunction CS5535/6 companion device * Copyright (C) Jaya Kumar * * Based on Jaroslav Kysela and Takashi Iwai's examples. @@ -40,16 +40,36 @@ #define DRIVER_NAME "cs5535audio" +static char *ac97_quirk; +module_param(ac97_quirk, charp, 0444); +MODULE_PARM_DESC(ac97_quirk, "AC'97 board specific workarounds."); + +static struct ac97_quirk ac97_quirks[] __devinitdata = { +#if 0 /* Not yet confirmed if all 5536 boards are HP only */ + { + .subvendor = PCI_VENDOR_ID_AMD, + .subdevice = PCI_DEVICE_ID_AMD_CS5536_AUDIO, + .name = "AMD RDK", + .type = AC97_TUNE_HP_ONLY + }, +#endif + {} +}; static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for " DRIVER_NAME); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string for " DRIVER_NAME); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable " DRIVER_NAME); + static struct pci_device_id snd_cs5535audio_ids[] __devinitdata = { - { PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_AUDIO, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, - { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_AUDIO, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, + { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_AUDIO) }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_AUDIO) }, {} }; @@ -90,7 +110,8 @@ static unsigned short snd_cs5535audio_codec_read(struct cs5535audio *cs5535au, udelay(1); } while (--timeout); if (!timeout) - snd_printk(KERN_ERR "Failure reading cs5535 codec\n"); + snd_printk(KERN_ERR "Failure reading codec reg 0x%x," + "Last value=0x%x\n", reg, val); return (unsigned short) val; } @@ -148,6 +169,8 @@ static int snd_cs5535audio_mixer(struct cs5535audio *cs5535au) return err; } + snd_ac97_tune_hardware(cs5535au->ac97, ac97_quirks, ac97_quirk); + return 0; } @@ -347,6 +370,8 @@ static int __devinit snd_cs5535audio_probe(struct pci_dev *pci, if ((err = snd_cs5535audio_create(card, pci, &cs5535au)) < 0) goto probefail_out; + card->private_data = cs5535au; + if ((err = snd_cs5535audio_mixer(cs5535au)) < 0) goto probefail_out; @@ -383,6 +408,10 @@ static struct pci_driver driver = { .id_table = snd_cs5535audio_ids, .probe = snd_cs5535audio_probe, .remove = __devexit_p(snd_cs5535audio_remove), +#ifdef CONFIG_PM + .suspend = snd_cs5535audio_suspend, + .resume = snd_cs5535audio_resume, +#endif }; static int __init alsa_card_cs5535audio_init(void) diff --git a/sound/pci/cs5535audio/cs5535audio.h b/sound/pci/cs5535audio/cs5535audio.h index 5e55a1a1ed6..4fd1f31a6cf 100644 --- a/sound/pci/cs5535audio/cs5535audio.h +++ b/sound/pci/cs5535audio/cs5535audio.h @@ -74,6 +74,8 @@ #define PRM_RDY_STS 0x00800000 #define ACC_CODEC_CNTL_WR_CMD (~0x80000000) #define ACC_CODEC_CNTL_RD_CMD 0x80000000 +#define ACC_CODEC_CNTL_LNK_SHUTDOWN 0x00040000 +#define ACC_CODEC_CNTL_LNK_WRM_RST 0x00020000 #define PRD_JMP 0x2000 #define PRD_EOP 0x4000 #define PRD_EOT 0x8000 @@ -88,6 +90,7 @@ struct cs5535audio_dma_ops { void (*disable_dma)(struct cs5535audio *cs5535au); void (*pause_dma)(struct cs5535audio *cs5535au); void (*setup_prd)(struct cs5535audio *cs5535au, u32 prd_addr); + u32 (*read_prd)(struct cs5535audio *cs5535au); u32 (*read_dma_pntr)(struct cs5535audio *cs5535au); }; @@ -103,11 +106,14 @@ struct cs5535audio_dma { struct snd_pcm_substream *substream; unsigned int buf_addr, buf_bytes; unsigned int period_bytes, periods; + int suspended; + u32 saved_prd; }; struct cs5535audio { struct snd_card *card; struct snd_ac97 *ac97; + struct snd_pcm *pcm; int irq; struct pci_dev *pci; unsigned long port; @@ -117,6 +123,8 @@ struct cs5535audio { struct cs5535audio_dma dmas[NUM_CS5535AUDIO_DMAS]; }; +int snd_cs5535audio_suspend(struct pci_dev *pci, pm_message_t state); +int snd_cs5535audio_resume(struct pci_dev *pci); int __devinit snd_cs5535audio_pcm(struct cs5535audio *cs5535audio); #endif /* __SOUND_CS5535AUDIO_H */ diff --git a/sound/pci/cs5535audio/cs5535audio_pcm.c b/sound/pci/cs5535audio/cs5535audio_pcm.c index 60bb82b2ff4..5450a9e8f13 100644 --- a/sound/pci/cs5535audio/cs5535audio_pcm.c +++ b/sound/pci/cs5535audio/cs5535audio_pcm.c @@ -43,7 +43,8 @@ static struct snd_pcm_hardware snd_cs5535audio_playback = SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_SYNC_START + SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_RESUME ), .formats = ( SNDRV_PCM_FMTBIT_S16_LE @@ -142,7 +143,7 @@ static int cs5535audio_build_dma_packets(struct cs5535audio *cs5535au, if (dma->periods == periods && dma->period_bytes == period_bytes) return 0; - /* the u32 cast is okay because in snd*create we succesfully told + /* the u32 cast is okay because in snd*create we successfully told pci alloc that we're only 32 bit capable so the uppper will be 0 */ addr = (u32) substream->runtime->dma_addr; desc_addr = (u32) dma->desc_buf.addr; @@ -193,6 +194,11 @@ static void cs5535audio_playback_setup_prd(struct cs5535audio *cs5535au, cs_writel(cs5535au, ACC_BM0_PRD, prd_addr); } +static u32 cs5535audio_playback_read_prd(struct cs5535audio *cs5535au) +{ + return cs_readl(cs5535au, ACC_BM0_PRD); +} + static u32 cs5535audio_playback_read_dma_pntr(struct cs5535audio *cs5535au) { return cs_readl(cs5535au, ACC_BM0_PNTR); @@ -219,6 +225,11 @@ static void cs5535audio_capture_setup_prd(struct cs5535audio *cs5535au, cs_writel(cs5535au, ACC_BM1_PRD, prd_addr); } +static u32 cs5535audio_capture_read_prd(struct cs5535audio *cs5535au) +{ + return cs_readl(cs5535au, ACC_BM1_PRD); +} + static u32 cs5535audio_capture_read_dma_pntr(struct cs5535audio *cs5535au) { return cs_readl(cs5535au, ACC_BM1_PNTR); @@ -285,9 +296,17 @@ static int snd_cs5535audio_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_START: dma->ops->enable_dma(cs5535au); break; + case SNDRV_PCM_TRIGGER_RESUME: + dma->ops->enable_dma(cs5535au); + dma->suspended = 0; + break; case SNDRV_PCM_TRIGGER_STOP: dma->ops->disable_dma(cs5535au); break; + case SNDRV_PCM_TRIGGER_SUSPEND: + dma->ops->disable_dma(cs5535au); + dma->suspended = 1; + break; default: snd_printk(KERN_ERR "unhandled trigger\n"); err = -EINVAL; @@ -375,6 +394,7 @@ static struct cs5535audio_dma_ops snd_cs5535audio_playback_dma_ops = { .enable_dma = cs5535audio_playback_enable_dma, .disable_dma = cs5535audio_playback_disable_dma, .setup_prd = cs5535audio_playback_setup_prd, + .read_prd = cs5535audio_playback_read_prd, .pause_dma = cs5535audio_playback_pause_dma, .read_dma_pntr = cs5535audio_playback_read_dma_pntr, }; @@ -384,6 +404,7 @@ static struct cs5535audio_dma_ops snd_cs5535audio_capture_dma_ops = { .enable_dma = cs5535audio_capture_enable_dma, .disable_dma = cs5535audio_capture_disable_dma, .setup_prd = cs5535audio_capture_setup_prd, + .read_prd = cs5535audio_capture_read_prd, .pause_dma = cs5535audio_capture_pause_dma, .read_dma_pntr = cs5535audio_capture_read_dma_pntr, }; @@ -413,6 +434,7 @@ int __devinit snd_cs5535audio_pcm(struct cs5535audio *cs5535au) snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(cs5535au->pci), 64*1024, 128*1024); + cs5535au->pcm = pcm; return 0; } diff --git a/sound/pci/cs5535audio/cs5535audio_pm.c b/sound/pci/cs5535audio/cs5535audio_pm.c new file mode 100644 index 00000000000..aad0e69db9c --- /dev/null +++ b/sound/pci/cs5535audio/cs5535audio_pm.c @@ -0,0 +1,123 @@ +/* + * Power management for audio on multifunction CS5535 companion device + * Copyright (C) Jaya Kumar + * + * 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 <linux/init.h> +#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> +#include <sound/asoundef.h> +#include <sound/pcm.h> +#include <sound/ac97_codec.h> +#include "cs5535audio.h" + +static void snd_cs5535audio_stop_hardware(struct cs5535audio *cs5535au) +{ + /* + we depend on snd_ac97_suspend to tell the + AC97 codec to shutdown. the amd spec suggests + that the LNK_SHUTDOWN be done at the same time + that the codec power-down is issued. instead, + we do it just after rather than at the same + time. excluding codec specific build_ops->suspend + ac97 powerdown hits: + 0x8000 EAPD + 0x4000 Headphone amplifier + 0x0300 ADC & DAC + 0x0400 Analog Mixer powerdown (Vref on) + I am not sure if this is the best that we can do. + The remainder to be investigated are: + - analog mixer (vref off) 0x0800 + - AC-link powerdown 0x1000 + - codec internal clock 0x2000 + */ + + /* set LNK_SHUTDOWN to shutdown AC link */ + cs_writel(cs5535au, ACC_CODEC_CNTL, ACC_CODEC_CNTL_LNK_SHUTDOWN); + +} + +int snd_cs5535audio_suspend(struct pci_dev *pci, pm_message_t state) +{ + struct snd_card *card = pci_get_drvdata(pci); + struct cs5535audio *cs5535au = card->private_data; + int i; + + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + for (i = 0; i < NUM_CS5535AUDIO_DMAS; i++) { + struct cs5535audio_dma *dma = &cs5535au->dmas[i]; + if (dma && dma->substream && !dma->suspended) + dma->saved_prd = dma->ops->read_prd(cs5535au); + } + snd_pcm_suspend_all(cs5535au->pcm); + snd_ac97_suspend(cs5535au->ac97); + /* save important regs, then disable aclink in hw */ + snd_cs5535audio_stop_hardware(cs5535au); + pci_disable_device(pci); + pci_save_state(pci); + + return 0; +} + +int snd_cs5535audio_resume(struct pci_dev *pci) +{ + struct snd_card *card = pci_get_drvdata(pci); + struct cs5535audio *cs5535au = card->private_data; + u32 tmp; + int timeout; + int i; + + pci_restore_state(pci); + pci_enable_device(pci); + pci_set_master(pci); + + /* set LNK_WRM_RST to reset AC link */ + cs_writel(cs5535au, ACC_CODEC_CNTL, ACC_CODEC_CNTL_LNK_WRM_RST); + + timeout = 50; + do { + tmp = cs_readl(cs5535au, ACC_CODEC_STATUS); + if (tmp & PRM_RDY_STS) + break; + udelay(1); + } while (--timeout); + + if (!timeout) + snd_printk(KERN_ERR "Failure getting AC Link ready\n"); + + /* we depend on ac97 to perform the codec power up */ + snd_ac97_resume(cs5535au->ac97); + /* set up rate regs, dma. actual initiation is done in trig */ + for (i = 0; i < NUM_CS5535AUDIO_DMAS; i++) { + struct cs5535audio_dma *dma = &cs5535au->dmas[i]; + if (dma && dma->substream && dma->suspended) { + dma->substream->ops->prepare(dma->substream); + dma->ops->setup_prd(cs5535au, dma->saved_prd); + } + } + + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + + return 0; +} + diff --git a/sound/pci/echoaudio/Makefile b/sound/pci/echoaudio/Makefile new file mode 100644 index 00000000000..7b576aeb3f8 --- /dev/null +++ b/sound/pci/echoaudio/Makefile @@ -0,0 +1,30 @@ +# +# Makefile for ALSA Echoaudio soundcard drivers +# Copyright (c) 2003 by Giuliano Pochini <pochini@shiny.it> +# + +snd-darla20-objs := darla20.o +snd-gina20-objs := gina20.o +snd-layla20-objs := layla20.o +snd-darla24-objs := darla24.o +snd-gina24-objs := gina24.o +snd-layla24-objs := layla24.o +snd-mona-objs := mona.o +snd-mia-objs := mia.o +snd-echo3g-objs := echo3g.o +snd-indigo-objs := indigo.o +snd-indigoio-objs := indigoio.o +snd-indigodj-objs := indigodj.o + +obj-$(CONFIG_SND_DARLA20) += snd-darla20.o +obj-$(CONFIG_SND_GINA20) += snd-gina20.o +obj-$(CONFIG_SND_LAYLA20) += snd-layla20.o +obj-$(CONFIG_SND_DARLA24) += snd-darla24.o +obj-$(CONFIG_SND_GINA24) += snd-gina24.o +obj-$(CONFIG_SND_LAYLA24) += snd-layla24.o +obj-$(CONFIG_SND_MONA) += snd-mona.o +obj-$(CONFIG_SND_MIA) += snd-mia.o +obj-$(CONFIG_SND_ECHO3G) += snd-echo3g.o +obj-$(CONFIG_SND_INDIGO) += snd-indigo.o +obj-$(CONFIG_SND_INDIGOIO) += snd-indigoio.o +obj-$(CONFIG_SND_INDIGODJ) += snd-indigodj.o diff --git a/sound/pci/echoaudio/darla20.c b/sound/pci/echoaudio/darla20.c new file mode 100644 index 00000000000..b7108e29a66 --- /dev/null +++ b/sound/pci/echoaudio/darla20.c @@ -0,0 +1,99 @@ +/* + * ALSA driver for Echoaudio soundcards. + * Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define ECHOGALS_FAMILY +#define ECHOCARD_DARLA20 +#define ECHOCARD_NAME "Darla20" +#define ECHOCARD_HAS_MONITOR + +/* Pipe indexes */ +#define PX_ANALOG_OUT 0 /* 8 */ +#define PX_DIGITAL_OUT 8 /* 0 */ +#define PX_ANALOG_IN 8 /* 2 */ +#define PX_DIGITAL_IN 10 /* 0 */ +#define PX_NUM 10 + +/* Bus indexes */ +#define BX_ANALOG_OUT 0 /* 8 */ +#define BX_DIGITAL_OUT 8 /* 0 */ +#define BX_ANALOG_IN 8 /* 2 */ +#define BX_DIGITAL_IN 10 /* 0 */ +#define BX_NUM 10 + + +#include <sound/driver.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/slab.h> +#include <linux/moduleparam.h> +#include <linux/firmware.h> +#include <sound/core.h> +#include <sound/info.h> +#include <sound/control.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/asoundef.h> +#include <sound/initval.h> +#include <asm/io.h> +#include <asm/atomic.h> +#include "echoaudio.h" + +#define FW_DARLA20_DSP 0 + +static const struct firmware card_fw[] = { + {0, "darla20_dsp.fw"} +}; + +static struct pci_device_id snd_echo_ids[] = { + {0x1057, 0x1801, 0xECC0, 0x0010, 0, 0, 0}, /* DSP 56301 Darla20 rev.0 */ + {0,} +}; + +static struct snd_pcm_hardware pcm_hardware_skel = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START, + .formats = SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S32_BE, + .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, + .rate_min = 44100, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = 262144, + .period_bytes_min = 32, + .period_bytes_max = 131072, + .periods_min = 2, + .periods_max = 220, + /* One page (4k) contains 512 instructions. I don't know if the hw + supports lists longer than this. In this case periods_max=220 is a + safe limit to make sure the list never exceeds 512 instructions. */ +}; + + +#include "darla20_dsp.c" +#include "echoaudio_dsp.c" +#include "echoaudio.c" diff --git a/sound/pci/echoaudio/darla20_dsp.c b/sound/pci/echoaudio/darla20_dsp.c new file mode 100644 index 00000000000..4159e3bc186 --- /dev/null +++ b/sound/pci/echoaudio/darla20_dsp.c @@ -0,0 +1,125 @@ +/*************************************************************************** + + Copyright Echo Digital Audio Corporation (c) 1998 - 2004 + All rights reserved + www.echoaudio.com + + This file is part of Echo Digital Audio's generic driver library. + + Echo Digital Audio's generic driver library is free software; + you can redistribute it and/or modify it under the terms of + the GNU General Public License as published by the Free Software + Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + ************************************************************************* + + Translation from C++ and adaptation for use in ALSA-Driver + were made by Giuliano Pochini <pochini@shiny.it> + +****************************************************************************/ + + +static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) +{ + int err; + + DE_INIT(("init_hw() - Darla20\n")); + snd_assert((subdevice_id & 0xfff0) == DARLA20, return -ENODEV); + + if ((err = init_dsp_comm_page(chip))) { + DE_INIT(("init_hw - could not initialize DSP comm page\n")); + return err; + } + + chip->device_id = device_id; + chip->subdevice_id = subdevice_id; + chip->bad_board = TRUE; + chip->dsp_code_to_load = &card_fw[FW_DARLA20_DSP]; + chip->spdif_status = GD_SPDIF_STATUS_UNDEF; + chip->clock_state = GD_CLOCK_UNDEF; + /* Since this card has no ASIC, mark it as loaded so everything + works OK */ + chip->asic_loaded = TRUE; + chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL; + + if ((err = load_firmware(chip)) < 0) + return err; + chip->bad_board = FALSE; + + if ((err = init_line_levels(chip)) < 0) + return err; + + DE_INIT(("init_hw done\n")); + return err; +} + + + +/* The Darla20 has no external clock sources */ +static u32 detect_input_clocks(const struct echoaudio *chip) +{ + return ECHO_CLOCK_BIT_INTERNAL; +} + + + +/* The Darla20 has no ASIC. Just do nothing */ +static int load_asic(struct echoaudio *chip) +{ + return 0; +} + + + +static int set_sample_rate(struct echoaudio *chip, u32 rate) +{ + u8 clock_state, spdif_status; + + if (wait_handshake(chip)) + return -EIO; + + switch (rate) { + case 44100: + clock_state = GD_CLOCK_44; + spdif_status = GD_SPDIF_STATUS_44; + break; + case 48000: + clock_state = GD_CLOCK_48; + spdif_status = GD_SPDIF_STATUS_48; + break; + default: + clock_state = GD_CLOCK_NOCHANGE; + spdif_status = GD_SPDIF_STATUS_NOCHANGE; + break; + } + + if (chip->clock_state == clock_state) + clock_state = GD_CLOCK_NOCHANGE; + if (spdif_status == chip->spdif_status) + spdif_status = GD_SPDIF_STATUS_NOCHANGE; + + chip->comm_page->sample_rate = cpu_to_le32(rate); + chip->comm_page->gd_clock_state = clock_state; + chip->comm_page->gd_spdif_status = spdif_status; + chip->comm_page->gd_resampler_state = 3; /* magic number - should always be 3 */ + + /* Save the new audio state if it changed */ + if (clock_state != GD_CLOCK_NOCHANGE) + chip->clock_state = clock_state; + if (spdif_status != GD_SPDIF_STATUS_NOCHANGE) + chip->spdif_status = spdif_status; + chip->sample_rate = rate; + + clear_handshake(chip); + return send_vector(chip, DSP_VC_SET_GD_AUDIO_STATE); +} diff --git a/sound/pci/echoaudio/darla24.c b/sound/pci/echoaudio/darla24.c new file mode 100644 index 00000000000..e59a982ee36 --- /dev/null +++ b/sound/pci/echoaudio/darla24.c @@ -0,0 +1,106 @@ +/* + * ALSA driver for Echoaudio soundcards. + * Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define ECHOGALS_FAMILY +#define ECHOCARD_DARLA24 +#define ECHOCARD_NAME "Darla24" +#define ECHOCARD_HAS_MONITOR +#define ECHOCARD_HAS_INPUT_NOMINAL_LEVEL +#define ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL +#define ECHOCARD_HAS_EXTERNAL_CLOCK +#define ECHOCARD_HAS_SUPER_INTERLEAVE + +/* Pipe indexes */ +#define PX_ANALOG_OUT 0 /* 8 */ +#define PX_DIGITAL_OUT 8 /* 0 */ +#define PX_ANALOG_IN 8 /* 2 */ +#define PX_DIGITAL_IN 10 /* 0 */ +#define PX_NUM 10 + +/* Bus indexes */ +#define BX_ANALOG_OUT 0 /* 8 */ +#define BX_DIGITAL_OUT 8 /* 0 */ +#define BX_ANALOG_IN 8 /* 2 */ +#define BX_DIGITAL_IN 10 /* 0 */ +#define BX_NUM 10 + + +#include <sound/driver.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/slab.h> +#include <linux/moduleparam.h> +#include <linux/firmware.h> +#include <sound/core.h> +#include <sound/info.h> +#include <sound/control.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/asoundef.h> +#include <sound/initval.h> +#include <asm/io.h> +#include <asm/atomic.h> +#include "echoaudio.h" + +#define FW_DARLA24_DSP 0 + +static const struct firmware card_fw[] = { + {0, "darla24_dsp.fw"} +}; + +static struct pci_device_id snd_echo_ids[] = { + {0x1057, 0x1801, 0xECC0, 0x0040, 0, 0, 0}, /* DSP 56301 Darla24 rev.0 */ + {0x1057, 0x1801, 0xECC0, 0x0041, 0, 0, 0}, /* DSP 56301 Darla24 rev.1 */ + {0,} +}; + +static struct snd_pcm_hardware pcm_hardware_skel = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START, + .formats = SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S32_BE, + .rates = SNDRV_PCM_RATE_8000_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000, + .rate_min = 8000, + .rate_max = 96000, + .channels_min = 1, + .channels_max = 8, + .buffer_bytes_max = 262144, + .period_bytes_min = 32, + .period_bytes_max = 131072, + .periods_min = 2, + .periods_max = 220, + /* One page (4k) contains 512 instructions. I don't know if the hw + supports lists longer than this. In this case periods_max=220 is a + safe limit to make sure the list never exceeds 512 instructions. */ +}; + + +#include "darla24_dsp.c" +#include "echoaudio_dsp.c" +#include "echoaudio.c" diff --git a/sound/pci/echoaudio/darla24_dsp.c b/sound/pci/echoaudio/darla24_dsp.c new file mode 100644 index 00000000000..79938eed7e9 --- /dev/null +++ b/sound/pci/echoaudio/darla24_dsp.c @@ -0,0 +1,156 @@ +/*************************************************************************** + + Copyright Echo Digital Audio Corporation (c) 1998 - 2004 + All rights reserved + www.echoaudio.com + + This file is part of Echo Digital Audio's generic driver library. + + Echo Digital Audio's generic driver library is free software; + you can redistribute it and/or modify it under the terms of + the GNU General Public License as published by the Free Software + Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + ************************************************************************* + + Translation from C++ and adaptation for use in ALSA-Driver + were made by Giuliano Pochini <pochini@shiny.it> + +****************************************************************************/ + + +static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) +{ + int err; + + DE_INIT(("init_hw() - Darla24\n")); + snd_assert((subdevice_id & 0xfff0) == DARLA24, return -ENODEV); + + if ((err = init_dsp_comm_page(chip))) { + DE_INIT(("init_hw - could not initialize DSP comm page\n")); + return err; + } + + chip->device_id = device_id; + chip->subdevice_id = subdevice_id; + chip->bad_board = TRUE; + chip->dsp_code_to_load = &card_fw[FW_DARLA24_DSP]; + /* Since this card has no ASIC, mark it as loaded so everything + works OK */ + chip->asic_loaded = TRUE; + chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL | + ECHO_CLOCK_BIT_ESYNC; + + if ((err = load_firmware(chip)) < 0) + return err; + chip->bad_board = FALSE; + + if ((err = init_line_levels(chip)) < 0) + return err; + + DE_INIT(("init_hw done\n")); + return err; +} + + + +static u32 detect_input_clocks(const struct echoaudio *chip) +{ + u32 clocks_from_dsp, clock_bits; + + /* Map the DSP clock detect bits to the generic driver clock + detect bits */ + clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks); + + clock_bits = ECHO_CLOCK_BIT_INTERNAL; + + if (clocks_from_dsp & GLDM_CLOCK_DETECT_BIT_ESYNC) + clock_bits |= ECHO_CLOCK_BIT_ESYNC; + + return clock_bits; +} + + + +/* The Darla24 has no ASIC. Just do nothing */ +static int load_asic(struct echoaudio *chip) +{ + return 0; +} + + + +static int set_sample_rate(struct echoaudio *chip, u32 rate) +{ + u8 clock; + + switch (rate) { + case 96000: + clock = GD24_96000; + break; + case 88200: + clock = GD24_88200; + break; + case 48000: + clock = GD24_48000; + break; + case 44100: + clock = GD24_44100; + break; + case 32000: + clock = GD24_32000; + break; + case 22050: + clock = GD24_22050; + break; + case 16000: + clock = GD24_16000; + break; + case 11025: + clock = GD24_11025; + break; + case 8000: + clock = GD24_8000; + break; + default: + DE_ACT(("set_sample_rate: Error, invalid sample rate %d\n", + rate)); + return -EINVAL; + } + + if (wait_handshake(chip)) + return -EIO; + + DE_ACT(("set_sample_rate: %d clock %d\n", rate, clock)); + chip->sample_rate = rate; + + /* Override the sample rate if this card is set to Echo sync. */ + if (chip->input_clock == ECHO_CLOCK_ESYNC) + clock = GD24_EXT_SYNC; + + chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP ? */ + chip->comm_page->gd_clock_state = clock; + clear_handshake(chip); + return send_vector(chip, DSP_VC_SET_GD_AUDIO_STATE); +} + + + +static int set_input_clock(struct echoaudio *chip, u16 clock) +{ + snd_assert(clock == ECHO_CLOCK_INTERNAL || + clock == ECHO_CLOCK_ESYNC, return -EINVAL); + chip->input_clock = clock; + return set_sample_rate(chip, chip->sample_rate); +} + diff --git a/sound/pci/echoaudio/echo3g.c b/sound/pci/echoaudio/echo3g.c new file mode 100644 index 00000000000..12099fe1547 --- /dev/null +++ b/sound/pci/echoaudio/echo3g.c @@ -0,0 +1,118 @@ +/* + * ALSA driver for Echoaudio soundcards. + * Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define ECHO3G_FAMILY +#define ECHOCARD_ECHO3G +#define ECHOCARD_NAME "Echo3G" +#define ECHOCARD_HAS_MONITOR +#define ECHOCARD_HAS_ASIC +#define ECHOCARD_HAS_INPUT_NOMINAL_LEVEL +#define ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL +#define ECHOCARD_HAS_SUPER_INTERLEAVE +#define ECHOCARD_HAS_DIGITAL_IO +#define ECHOCARD_HAS_DIGITAL_MODE_SWITCH +#define ECHOCARD_HAS_ADAT 6 +#define ECHOCARD_HAS_EXTERNAL_CLOCK +#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32 +#define ECHOCARD_HAS_MIDI +#define ECHOCARD_HAS_PHANTOM_POWER + +/* Pipe indexes */ +#define PX_ANALOG_OUT 0 +#define PX_DIGITAL_OUT chip->px_digital_out +#define PX_ANALOG_IN chip->px_analog_in +#define PX_DIGITAL_IN chip->px_digital_in +#define PX_NUM chip->px_num + +/* Bus indexes */ +#define BX_ANALOG_OUT 0 +#define BX_DIGITAL_OUT chip->bx_digital_out +#define BX_ANALOG_IN chip->bx_analog_in +#define BX_DIGITAL_IN chip->bx_digital_in +#define BX_NUM chip->bx_num + + +#include <sound/driver.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/slab.h> +#include <linux/moduleparam.h> +#include <linux/firmware.h> +#include <sound/core.h> +#include <sound/info.h> +#include <sound/control.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/asoundef.h> +#include <sound/initval.h> +#include <sound/rawmidi.h> +#include <asm/io.h> +#include <asm/atomic.h> +#include "echoaudio.h" + +#define FW_361_LOADER 0 +#define FW_ECHO3G_DSP 1 +#define FW_3G_ASIC 2 + +static const struct firmware card_fw[] = { + {0, "loader_dsp.fw"}, + {0, "echo3g_dsp.fw"}, + {0, "3g_asic.fw"} +}; + +static struct pci_device_id snd_echo_ids[] = { + {0x1057, 0x3410, 0xECC0, 0x0100, 0, 0, 0}, /* Echo 3G */ + {0,} +}; + +static struct snd_pcm_hardware pcm_hardware_skel = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START, + .formats = SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S32_BE, + .rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 32000, + .rate_max = 100000, + .channels_min = 1, + .channels_max = 8, + .buffer_bytes_max = 262144, + .period_bytes_min = 32, + .period_bytes_max = 131072, + .periods_min = 2, + .periods_max = 220, +}; + +#include "echo3g_dsp.c" +#include "echoaudio_dsp.c" +#include "echoaudio_3g.c" +#include "echoaudio.c" +#include "midi.c" diff --git a/sound/pci/echoaudio/echo3g_dsp.c b/sound/pci/echoaudio/echo3g_dsp.c new file mode 100644 index 00000000000..d26a1d1f3ed --- /dev/null +++ b/sound/pci/echoaudio/echo3g_dsp.c @@ -0,0 +1,131 @@ +/**************************************************************************** + + Copyright Echo Digital Audio Corporation (c) 1998 - 2004 + All rights reserved + www.echoaudio.com + + This file is part of Echo Digital Audio's generic driver library. + + Echo Digital Audio's generic driver library is free software; + you can redistribute it and/or modify it under the terms of + the GNU General Public License as published by the Free Software + Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + ************************************************************************* + + Translation from C++ and adaptation for use in ALSA-Driver + were made by Giuliano Pochini <pochini@shiny.it> + +****************************************************************************/ + +static int load_asic(struct echoaudio *chip); +static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode); +static int set_digital_mode(struct echoaudio *chip, u8 mode); +static int check_asic_status(struct echoaudio *chip); +static int set_sample_rate(struct echoaudio *chip, u32 rate); +static int set_input_clock(struct echoaudio *chip, u16 clock); +static int set_professional_spdif(struct echoaudio *chip, char prof); +static int set_phantom_power(struct echoaudio *chip, char on); +static int write_control_reg(struct echoaudio *chip, u32 ctl, u32 frq, + char force); + +#include <linux/irq.h> + +static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) +{ + int err; + + local_irq_enable(); + DE_INIT(("init_hw() - Echo3G\n")); + snd_assert((subdevice_id & 0xfff0) == ECHO3G, return -ENODEV); + + if ((err = init_dsp_comm_page(chip))) { + DE_INIT(("init_hw - could not initialize DSP comm page\n")); + return err; + } + + chip->comm_page->e3g_frq_register = + __constant_cpu_to_le32((E3G_MAGIC_NUMBER / 48000) - 2); + chip->device_id = device_id; + chip->subdevice_id = subdevice_id; + chip->bad_board = TRUE; + chip->has_midi = TRUE; + chip->dsp_code_to_load = &card_fw[FW_ECHO3G_DSP]; + + /* Load the DSP code and the ASIC on the PCI card and get + what type of external box is attached */ + err = load_firmware(chip); + + if (err < 0) { + return err; + } else if (err == E3G_GINA3G_BOX_TYPE) { + chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL | + ECHO_CLOCK_BIT_SPDIF | + ECHO_CLOCK_BIT_ADAT; + chip->card_name = "Gina3G"; + chip->px_digital_out = chip->bx_digital_out = 6; + chip->px_analog_in = chip->bx_analog_in = 14; + chip->px_digital_in = chip->bx_digital_in = 16; + chip->px_num = chip->bx_num = 24; + chip->has_phantom_power = TRUE; + chip->hasnt_input_nominal_level = TRUE; + } else if (err == E3G_LAYLA3G_BOX_TYPE) { + chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL | + ECHO_CLOCK_BIT_SPDIF | + ECHO_CLOCK_BIT_ADAT | + ECHO_CLOCK_BIT_WORD; + chip->card_name = "Layla3G"; + chip->px_digital_out = chip->bx_digital_out = 8; + chip->px_analog_in = chip->bx_analog_in = 16; + chip->px_digital_in = chip->bx_digital_in = 24; + chip->px_num = chip->bx_num = 32; + } else { + return -ENODEV; + } + + chip->digital_modes = ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA | + ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL | + ECHOCAPS_HAS_DIGITAL_MODE_ADAT; + chip->digital_mode = DIGITAL_MODE_SPDIF_RCA; + chip->professional_spdif = FALSE; + chip->non_audio_spdif = FALSE; + chip->bad_board = FALSE; + + if ((err = init_line_levels(chip)) < 0) + return err; + err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA); + snd_assert(err >= 0, return err); + err = set_phantom_power(chip, 0); + snd_assert(err >= 0, return err); + err = set_professional_spdif(chip, TRUE); + + DE_INIT(("init_hw done\n")); + return err; +} + + + +static int set_phantom_power(struct echoaudio *chip, char on) +{ + u32 control_reg = le32_to_cpu(chip->comm_page->control_register); + + if (on) + control_reg |= E3G_PHANTOM_POWER; + else + control_reg &= ~E3G_PHANTOM_POWER; + + chip->phantom_power = on; + return write_control_reg(chip, control_reg, + le32_to_cpu(chip->comm_page->e3g_frq_register), + 0); +} diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c new file mode 100644 index 00000000000..43b408ada1d --- /dev/null +++ b/sound/pci/echoaudio/echoaudio.c @@ -0,0 +1,2196 @@ +/* + * ALSA driver for Echoaudio soundcards. + * Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +MODULE_AUTHOR("Giuliano Pochini <pochini@shiny.it>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Echoaudio " ECHOCARD_NAME " soundcards driver"); +MODULE_SUPPORTED_DEVICE("{{Echoaudio," ECHOCARD_NAME "}}"); +MODULE_DEVICE_TABLE(pci, snd_echo_ids); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for " ECHOCARD_NAME " soundcard."); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string for " ECHOCARD_NAME " soundcard."); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable " ECHOCARD_NAME " soundcard."); + +static unsigned int channels_list[10] = {1, 2, 4, 6, 8, 10, 12, 14, 16, 999999}; + +static int get_firmware(const struct firmware **fw_entry, + const struct firmware *frm, struct echoaudio *chip) +{ + int err; + char name[30]; + DE_ACT(("firmware requested: %s\n", frm->data)); + snprintf(name, sizeof(name), "ea/%s", frm->data); + if ((err = request_firmware(fw_entry, name, pci_device(chip))) < 0) + snd_printk(KERN_ERR "get_firmware(): Firmware not available (%d)\n", err); + return err; +} + +static void free_firmware(const struct firmware *fw_entry) +{ + release_firmware(fw_entry); + DE_ACT(("firmware released\n")); +} + + + +/****************************************************************************** + PCM interface +******************************************************************************/ + +static void audiopipe_free(struct snd_pcm_runtime *runtime) +{ + struct audiopipe *pipe = runtime->private_data; + + if (pipe->sgpage.area) + snd_dma_free_pages(&pipe->sgpage); + kfree(pipe); +} + + + +static int hw_rule_capture_format_by_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *c = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + struct snd_mask fmt; + + snd_mask_any(&fmt); + +#ifndef ECHOCARD_HAS_STEREO_BIG_ENDIAN32 + /* >=2 channels cannot be S32_BE */ + if (c->min == 2) { + fmt.bits[0] &= ~SNDRV_PCM_FMTBIT_S32_BE; + return snd_mask_refine(f, &fmt); + } +#endif + /* > 2 channels cannot be U8 and S32_BE */ + if (c->min > 2) { + fmt.bits[0] &= ~(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_BE); + return snd_mask_refine(f, &fmt); + } + /* Mono is ok with any format */ + return 0; +} + + + +static int hw_rule_capture_channels_by_format(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *c = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + struct snd_interval ch; + + snd_interval_any(&ch); + + /* S32_BE is mono (and stereo) only */ + if (f->bits[0] == SNDRV_PCM_FMTBIT_S32_BE) { + ch.min = 1; +#ifdef ECHOCARD_HAS_STEREO_BIG_ENDIAN32 + ch.max = 2; +#else + ch.max = 1; +#endif + ch.integer = 1; + return snd_interval_refine(c, &ch); + } + /* U8 can be only mono or stereo */ + if (f->bits[0] == SNDRV_PCM_FMTBIT_U8) { + ch.min = 1; + ch.max = 2; + ch.integer = 1; + return snd_interval_refine(c, &ch); + } + /* S16_LE, S24_3LE and S32_LE support any number of channels. */ + return 0; +} + + + +static int hw_rule_playback_format_by_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *c = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + struct snd_mask fmt; + u64 fmask; + snd_mask_any(&fmt); + + fmask = fmt.bits[0] + ((u64)fmt.bits[1] << 32); + + /* >2 channels must be S16_LE, S24_3LE or S32_LE */ + if (c->min > 2) { + fmask &= SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE; + /* 1 channel must be S32_BE or S32_LE */ + } else if (c->max == 1) + fmask &= SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE; +#ifndef ECHOCARD_HAS_STEREO_BIG_ENDIAN32 + /* 2 channels cannot be S32_BE */ + else if (c->min == 2 && c->max == 2) + fmask &= ~SNDRV_PCM_FMTBIT_S32_BE; +#endif + else + return 0; + + fmt.bits[0] &= (u32)fmask; + fmt.bits[1] &= (u32)(fmask >> 32); + return snd_mask_refine(f, &fmt); +} + + + +static int hw_rule_playback_channels_by_format(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *c = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + struct snd_interval ch; + u64 fmask; + + snd_interval_any(&ch); + ch.integer = 1; + fmask = f->bits[0] + ((u64)f->bits[1] << 32); + + /* S32_BE is mono (and stereo) only */ + if (fmask == SNDRV_PCM_FMTBIT_S32_BE) { + ch.min = 1; +#ifdef ECHOCARD_HAS_STEREO_BIG_ENDIAN32 + ch.max = 2; +#else + ch.max = 1; +#endif + /* U8 is stereo only */ + } else if (fmask == SNDRV_PCM_FMTBIT_U8) + ch.min = ch.max = 2; + /* S16_LE and S24_3LE must be at least stereo */ + else if (!(fmask & ~(SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_3LE))) + ch.min = 2; + else + return 0; + + return snd_interval_refine(c, &ch); +} + + + +/* Since the sample rate is a global setting, do allow the user to change the +sample rate only if there is only one pcm device open. */ +static int hw_rule_sample_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct echoaudio *chip = rule->private; + struct snd_interval fixed; + + if (!chip->can_set_rate) { + snd_interval_any(&fixed); + fixed.min = fixed.max = chip->sample_rate; + return snd_interval_refine(rate, &fixed); + } + return 0; +} + + +static int pcm_open(struct snd_pcm_substream *substream, + signed char max_channels) +{ + struct echoaudio *chip; + struct snd_pcm_runtime *runtime; + struct audiopipe *pipe; + int err, i; + + if (max_channels <= 0) + return -EAGAIN; + + chip = snd_pcm_substream_chip(substream); + runtime = substream->runtime; + + if (!(pipe = kmalloc(sizeof(struct audiopipe), GFP_KERNEL))) + return -ENOMEM; + memset(pipe, 0, sizeof(struct audiopipe)); + pipe->index = -1; /* Not configured yet */ + + /* Set up hw capabilities and contraints */ + memcpy(&pipe->hw, &pcm_hardware_skel, sizeof(struct snd_pcm_hardware)); + DE_HWP(("max_channels=%d\n", max_channels)); + pipe->constr.list = channels_list; + pipe->constr.mask = 0; + for (i = 0; channels_list[i] <= max_channels; i++); + pipe->constr.count = i; + if (pipe->hw.channels_max > max_channels) + pipe->hw.channels_max = max_channels; + if (chip->digital_mode == DIGITAL_MODE_ADAT) { + pipe->hw.rate_max = 48000; + pipe->hw.rates &= SNDRV_PCM_RATE_8000_48000; + } + + runtime->hw = pipe->hw; + runtime->private_data = pipe; + runtime->private_free = audiopipe_free; + snd_pcm_set_sync(substream); + + /* Only mono and any even number of channels are allowed */ + if ((err = snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + &pipe->constr)) < 0) + return err; + + /* All periods should have the same size */ + if ((err = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS)) < 0) + return err; + + /* The hw accesses memory in chunks 32 frames long and they should be + 32-bytes-aligned. It's not a requirement, but it seems that IRQs are + generated with a resolution of 32 frames. Thus we need the following */ + if ((err = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + 32)) < 0) + return err; + if ((err = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + 32)) < 0) + return err; + + if ((err = snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + hw_rule_sample_rate, chip, + SNDRV_PCM_HW_PARAM_RATE, -1)) < 0) + return err; + + /* Finally allocate a page for the scatter-gather list */ + if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), + PAGE_SIZE, &pipe->sgpage)) < 0) { + DE_HWP(("s-g list allocation failed\n")); + return err; + } + + return 0; +} + + + +static int pcm_analog_in_open(struct snd_pcm_substream *substream) +{ + struct echoaudio *chip = snd_pcm_substream_chip(substream); + int err; + + DE_ACT(("pcm_analog_in_open\n")); + if ((err = pcm_open(substream, num_analog_busses_in(chip) - + substream->number)) < 0) + return err; + if ((err = snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + hw_rule_capture_channels_by_format, NULL, + SNDRV_PCM_HW_PARAM_FORMAT, -1)) < 0) + return err; + if ((err = snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_FORMAT, + hw_rule_capture_format_by_channels, NULL, + SNDRV_PCM_HW_PARAM_CHANNELS, -1)) < 0) + return err; + atomic_inc(&chip->opencount); + if (atomic_read(&chip->opencount) > 1 && chip->rate_set) + chip->can_set_rate=0; + DE_HWP(("pcm_analog_in_open cs=%d oc=%d r=%d\n", + chip->can_set_rate, atomic_read(&chip->opencount), + chip->sample_rate)); + return 0; +} + + + +static int pcm_analog_out_open(struct snd_pcm_substream *substream) +{ + struct echoaudio *chip = snd_pcm_substream_chip(substream); + int max_channels, err; + +#ifdef ECHOCARD_HAS_VMIXER + max_channels = num_pipes_out(chip); +#else + max_channels = num_analog_busses_out(chip); +#endif + DE_ACT(("pcm_analog_out_open\n")); + if ((err = pcm_open(substream, max_channels - substream->number)) < 0) + return err; + if ((err = snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + hw_rule_playback_channels_by_format, + NULL, + SNDRV_PCM_HW_PARAM_FORMAT, -1)) < 0) + return err; + if ((err = snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_FORMAT, + hw_rule_playback_format_by_channels, + NULL, + SNDRV_PCM_HW_PARAM_CHANNELS, -1)) < 0) + return err; + atomic_inc(&chip->opencount); + if (atomic_read(&chip->opencount) > 1 && chip->rate_set) + chip->can_set_rate=0; + DE_HWP(("pcm_analog_out_open cs=%d oc=%d r=%d\n", + chip->can_set_rate, atomic_read(&chip->opencount), + chip->sample_rate)); + return 0; +} + + + +#ifdef ECHOCARD_HAS_DIGITAL_IO + +static int pcm_digital_in_open(struct snd_pcm_substream *substream) +{ + struct echoaudio *chip = snd_pcm_substream_chip(substream); + int err, max_channels; + + DE_ACT(("pcm_digital_in_open\n")); + max_channels = num_digital_busses_in(chip) - substream->number; + down(&chip->mode_mutex); + if (chip->digital_mode == DIGITAL_MODE_ADAT) + err = pcm_open(substream, max_channels); + else /* If the card has ADAT, subtract the 6 channels + * that S/PDIF doesn't have + */ + err = pcm_open(substream, max_channels - ECHOCARD_HAS_ADAT); + + if (err < 0) + goto din_exit; + + if ((err = snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + hw_rule_capture_channels_by_format, NULL, + SNDRV_PCM_HW_PARAM_FORMAT, -1)) < 0) + goto din_exit; + if ((err = snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_FORMAT, + hw_rule_capture_format_by_channels, NULL, + SNDRV_PCM_HW_PARAM_CHANNELS, -1)) < 0) + goto din_exit; + + atomic_inc(&chip->opencount); + if (atomic_read(&chip->opencount) > 1 && chip->rate_set) + chip->can_set_rate=0; + +din_exit: + up(&chip->mode_mutex); + return err; +} + + + +#ifndef ECHOCARD_HAS_VMIXER /* See the note in snd_echo_new_pcm() */ + +static int pcm_digital_out_open(struct snd_pcm_substream *substream) +{ + struct echoaudio *chip = snd_pcm_substream_chip(substream); + int err, max_channels; + + DE_ACT(("pcm_digital_out_open\n")); + max_channels = num_digital_busses_out(chip) - substream->number; + down(&chip->mode_mutex); + if (chip->digital_mode == DIGITAL_MODE_ADAT) + err = pcm_open(substream, max_channels); + else /* If the card has ADAT, subtract the 6 channels + * that S/PDIF doesn't have + */ + err = pcm_open(substream, max_channels - ECHOCARD_HAS_ADAT); + + if (err < 0) + goto dout_exit; + + if ((err = snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + hw_rule_playback_channels_by_format, + NULL, SNDRV_PCM_HW_PARAM_FORMAT, + -1)) < 0) + goto dout_exit; + if ((err = snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_FORMAT, + hw_rule_playback_format_by_channels, + NULL, SNDRV_PCM_HW_PARAM_CHANNELS, + -1)) < 0) + goto dout_exit; + atomic_inc(&chip->opencount); + if (atomic_read(&chip->opencount) > 1 && chip->rate_set) + chip->can_set_rate=0; +dout_exit: + up(&chip->mode_mutex); + return err; +} + +#endif /* !ECHOCARD_HAS_VMIXER */ + +#endif /* ECHOCARD_HAS_DIGITAL_IO */ + + + +static int pcm_close(struct snd_pcm_substream *substream) +{ + struct echoaudio *chip = snd_pcm_substream_chip(substream); + int oc; + + /* Nothing to do here. Audio is already off and pipe will be + * freed by its callback + */ + DE_ACT(("pcm_close\n")); + + atomic_dec(&chip->opencount); + oc = atomic_read(&chip->opencount); + DE_ACT(("pcm_close oc=%d cs=%d rs=%d\n", oc, + chip->can_set_rate, chip->rate_set)); + if (oc < 2) + chip->can_set_rate = 1; + if (oc == 0) + chip->rate_set = 0; + DE_ACT(("pcm_close2 oc=%d cs=%d rs=%d\n", oc, + chip->can_set_rate,chip->rate_set)); + + return 0; +} + + + +/* Channel allocation and scatter-gather list setup */ +static int init_engine(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params, + int pipe_index, int interleave) +{ + struct echoaudio *chip; + int err, per, rest, page, edge, offs; + struct snd_sg_buf *sgbuf; + struct audiopipe *pipe; + + chip = snd_pcm_substream_chip(substream); + pipe = (struct audiopipe *) substream->runtime->private_data; + + /* Sets up che hardware. If it's already initialized, reset and + * redo with the new parameters + */ + spin_lock_irq(&chip->lock); + if (pipe->index >= 0) { + DE_HWP(("hwp_ie free(%d)\n", pipe->index)); + err = free_pipes(chip, pipe); + snd_assert(!err); + chip->substream[pipe->index] = NULL; + } + + err = allocate_pipes(chip, pipe, pipe_index, interleave); + if (err < 0) { + spin_unlock_irq(&chip->lock); + DE_ACT((KERN_NOTICE "allocate_pipes(%d) err=%d\n", + pipe_index, err)); + return err; + } + spin_unlock_irq(&chip->lock); + DE_ACT((KERN_NOTICE "allocate_pipes()=%d\n", pipe_index)); + + DE_HWP(("pcm_hw_params (bufsize=%dB periods=%d persize=%dB)\n", + params_buffer_bytes(hw_params), params_periods(hw_params), + params_period_bytes(hw_params))); + err = snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); + if (err < 0) { + snd_printk(KERN_ERR "malloc_pages err=%d\n", err); + spin_lock_irq(&chip->lock); + free_pipes(chip, pipe); + spin_unlock_irq(&chip->lock); + pipe->index = -1; + return err; + } + + sgbuf = snd_pcm_substream_sgbuf(substream); + + DE_HWP(("pcm_hw_params table size=%d pages=%d\n", + sgbuf->size, sgbuf->pages)); + sglist_init(chip, pipe); + edge = PAGE_SIZE; + for (offs = page = per = 0; offs < params_buffer_bytes(hw_params); + per++) { + rest = params_period_bytes(hw_params); + if (offs + rest > params_buffer_bytes(hw_params)) + rest = params_buffer_bytes(hw_params) - offs; + while (rest) { + if (rest <= edge - offs) { + sglist_add_mapping(chip, pipe, + snd_sgbuf_get_addr(sgbuf, offs), + rest); + sglist_add_irq(chip, pipe); + offs += rest; + rest = 0; + } else { + sglist_add_mapping(chip, pipe, + snd_sgbuf_get_addr(sgbuf, offs), + edge - offs); + rest -= edge - offs; + offs = edge; + } + if (offs == edge) { + edge += PAGE_SIZE; + page++; + } + } + } + + /* Close the ring buffer */ + sglist_wrap(chip, pipe); + + /* This stuff is used by the irq handler, so it must be + * initialized before chip->substream + */ + chip->last_period[pipe_index] = 0; + pipe->last_counter = 0; + pipe->position = 0; + smp_wmb(); + chip->substream[pipe_index] = substream; + chip->rate_set = 1; + spin_lock_irq(&chip->lock); + set_sample_rate(chip, hw_params->rate_num / hw_params->rate_den); + spin_unlock_irq(&chip->lock); + DE_HWP(("pcm_hw_params ok\n")); + return 0; +} + + + +static int pcm_analog_in_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct echoaudio *chip = snd_pcm_substream_chip(substream); + + return init_engine(substream, hw_params, px_analog_in(chip) + + substream->number, params_channels(hw_params)); +} + + + +static int pcm_analog_out_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + return init_engine(substream, hw_params, substream->number, + params_channels(hw_params)); +} + + + +#ifdef ECHOCARD_HAS_DIGITAL_IO + +static int pcm_digital_in_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct echoaudio *chip = snd_pcm_substream_chip(substream); + + return init_engine(substream, hw_params, px_digital_in(chip) + + substream->number, params_channels(hw_params)); +} + + + +#ifndef ECHOCARD_HAS_VMIXER /* See the note in snd_echo_new_pcm() */ +static int pcm_digital_out_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct echoaudio *chip = snd_pcm_substream_chip(substream); + + return init_engine(substream, hw_params, px_digital_out(chip) + + substream->number, params_channels(hw_params)); +} +#endif /* !ECHOCARD_HAS_VMIXER */ + +#endif /* ECHOCARD_HAS_DIGITAL_IO */ + + + +static int pcm_hw_free(struct snd_pcm_substream *substream) +{ + struct echoaudio *chip; + struct audiopipe *pipe; + + chip = snd_pcm_substream_chip(substream); + pipe = (struct audiopipe *) substream->runtime->private_data; + + spin_lock_irq(&chip->lock); + if (pipe->index >= 0) { + DE_HWP(("pcm_hw_free(%d)\n", pipe->index)); + free_pipes(chip, pipe); + chip->substream[pipe->index] = NULL; + pipe->index = -1; + } + spin_unlock_irq(&chip->lock); + + DE_HWP(("pcm_hw_freed\n")); + snd_pcm_lib_free_pages(substream); + return 0; +} + + + +static int pcm_prepare(struct snd_pcm_substream *substream) +{ + struct echoaudio *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct audioformat format; + int pipe_index = ((struct audiopipe *)runtime->private_data)->index; + + DE_HWP(("Prepare rate=%d format=%d channels=%d\n", + runtime->rate, runtime->format, runtime->channels)); + format.interleave = runtime->channels; + format.data_are_bigendian = 0; + format.mono_to_stereo = 0; + switch (runtime->format) { + case SNDRV_PCM_FORMAT_U8: + format.bits_per_sample = 8; + break; + case SNDRV_PCM_FORMAT_S16_LE: + format.bits_per_sample = 16; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + format.bits_per_sample = 24; + break; + case SNDRV_PCM_FORMAT_S32_BE: + format.data_are_bigendian = 1; + case SNDRV_PCM_FORMAT_S32_LE: + format.bits_per_sample = 32; + break; + default: + DE_HWP(("Prepare error: unsupported format %d\n", + runtime->format)); + return -EINVAL; + } + + snd_assert(pipe_index < px_num(chip), return -EINVAL); + snd_assert(is_pipe_allocated(chip, pipe_index), return -EINVAL); + set_audio_format(chip, pipe_index, &format); + return 0; +} + + + +static int pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct echoaudio *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct audiopipe *pipe = runtime->private_data; + int i, err; + u32 channelmask = 0; + struct list_head *pos; + struct snd_pcm_substream *s; + + snd_pcm_group_for_each(pos, substream) { + s = snd_pcm_group_substream_entry(pos); + for (i = 0; i < DSP_MAXPIPES; i++) { + if (s == chip->substream[i]) { + channelmask |= 1 << i; + snd_pcm_trigger_done(s, substream); + } + } + } + + spin_lock(&chip->lock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + DE_ACT(("pcm_trigger start\n")); + for (i = 0; i < DSP_MAXPIPES; i++) { + if (channelmask & (1 << i)) { + pipe = chip->substream[i]->runtime->private_data; + switch (pipe->state) { + case PIPE_STATE_STOPPED: + chip->last_period[i] = 0; + pipe->last_counter = 0; + pipe->position = 0; + *pipe->dma_counter = 0; + case PIPE_STATE_PAUSED: + pipe->state = PIPE_STATE_STARTED; + break; + case PIPE_STATE_STARTED: + break; + } + } + } + err = start_transport(chip, channelmask, + chip->pipe_cyclic_mask); + break; + case SNDRV_PCM_TRIGGER_STOP: + DE_ACT(("pcm_trigger stop\n")); + for (i = 0; i < DSP_MAXPIPES; i++) { + if (channelmask & (1 << i)) { + pipe = chip->substream[i]->runtime->private_data; + pipe->state = PIPE_STATE_STOPPED; + } + } + err = stop_transport(chip, channelmask); + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + DE_ACT(("pcm_trigger pause\n")); + for (i = 0; i < DSP_MAXPIPES; i++) { + if (channelmask & (1 << i)) { + pipe = chip->substream[i]->runtime->private_data; + pipe->state = PIPE_STATE_PAUSED; + } + } + err = pause_transport(chip, channelmask); + break; + default: + err = -EINVAL; + } + spin_unlock(&chip->lock); + return err; +} + + + +static snd_pcm_uframes_t pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct audiopipe *pipe = runtime->private_data; + size_t cnt, bufsize, pos; + + cnt = le32_to_cpu(*pipe->dma_counter); + pipe->position += cnt - pipe->last_counter; + pipe->last_counter = cnt; + bufsize = substream->runtime->buffer_size; + pos = bytes_to_frames(substream->runtime, pipe->position); + + while (pos >= bufsize) { + pipe->position -= frames_to_bytes(substream->runtime, bufsize); + pos -= bufsize; + } + return pos; +} + + + +/* pcm *_ops structures */ +static struct snd_pcm_ops analog_playback_ops = { + .open = pcm_analog_out_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_analog_out_hw_params, + .hw_free = pcm_hw_free, + .prepare = pcm_prepare, + .trigger = pcm_trigger, + .pointer = pcm_pointer, + .page = snd_pcm_sgbuf_ops_page, +}; +static struct snd_pcm_ops analog_capture_ops = { + .open = pcm_analog_in_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_analog_in_hw_params, + .hw_free = pcm_hw_free, + .prepare = pcm_prepare, + .trigger = pcm_trigger, + .pointer = pcm_pointer, + .page = snd_pcm_sgbuf_ops_page, +}; +#ifdef ECHOCARD_HAS_DIGITAL_IO +#ifndef ECHOCARD_HAS_VMIXER +static struct snd_pcm_ops digital_playback_ops = { + .open = pcm_digital_out_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_digital_out_hw_params, + .hw_free = pcm_hw_free, + .prepare = pcm_prepare, + .trigger = pcm_trigger, + .pointer = pcm_pointer, + .page = snd_pcm_sgbuf_ops_page, +}; +#endif /* !ECHOCARD_HAS_VMIXER */ +static struct snd_pcm_ops digital_capture_ops = { + .open = pcm_digital_in_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_digital_in_hw_params, + .hw_free = pcm_hw_free, + .prepare = pcm_prepare, + .trigger = pcm_trigger, + .pointer = pcm_pointer, + .page = snd_pcm_sgbuf_ops_page, +}; +#endif /* ECHOCARD_HAS_DIGITAL_IO */ + + + +/* Preallocate memory only for the first substream because it's the most + * used one + */ +static int snd_echo_preallocate_pages(struct snd_pcm *pcm, struct device *dev) +{ + struct snd_pcm_substream *ss; + int stream, err; + + for (stream = 0; stream < 2; stream++) + for (ss = pcm->streams[stream].substream; ss; ss = ss->next) { + err = snd_pcm_lib_preallocate_pages(ss, SNDRV_DMA_TYPE_DEV_SG, + dev, + ss->number ? 0 : 128<<10, + 256<<10); + if (err < 0) + return err; + } + return 0; +} + + + +/*<--snd_echo_probe() */ +static int __devinit snd_echo_new_pcm(struct echoaudio *chip) +{ + struct snd_pcm *pcm; + int err; + +#ifdef ECHOCARD_HAS_VMIXER + /* This card has a Vmixer, that is there is no direct mapping from PCM + streams to physical outputs. The user can mix the streams as he wishes + via control interface and it's possible to send any stream to any + output, thus it makes no sense to keep analog and digital outputs + separated */ + + /* PCM#0 Virtual outputs and analog inputs */ + if ((err = snd_pcm_new(chip->card, "PCM", 0, num_pipes_out(chip), + num_analog_busses_in(chip), &pcm)) < 0) + return err; + pcm->private_data = chip; + chip->analog_pcm = pcm; + strcpy(pcm->name, chip->card->shortname); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &analog_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &analog_capture_ops); + if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0) + return err; + DE_INIT(("Analog PCM ok\n")); + +#ifdef ECHOCARD_HAS_DIGITAL_IO + /* PCM#1 Digital inputs, no outputs */ + if ((err = snd_pcm_new(chip->card, "Digital PCM", 1, 0, + num_digital_busses_in(chip), &pcm)) < 0) + return err; + pcm->private_data = chip; + chip->digital_pcm = pcm; + strcpy(pcm->name, chip->card->shortname); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &digital_capture_ops); + if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0) + return err; + DE_INIT(("Digital PCM ok\n")); +#endif /* ECHOCARD_HAS_DIGITAL_IO */ + +#else /* ECHOCARD_HAS_VMIXER */ + + /* The card can manage substreams formed by analog and digital channels + at the same time, but I prefer to keep analog and digital channels + separated, because that mixed thing is confusing and useless. So we + register two PCM devices: */ + + /* PCM#0 Analog i/o */ + if ((err = snd_pcm_new(chip->card, "Analog PCM", 0, + num_analog_busses_out(chip), + num_analog_busses_in(chip), &pcm)) < 0) + return err; + pcm->private_data = chip; + chip->analog_pcm = pcm; + strcpy(pcm->name, chip->card->shortname); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &analog_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &analog_capture_ops); + if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0) + return err; + DE_INIT(("Analog PCM ok\n")); + +#ifdef ECHOCARD_HAS_DIGITAL_IO + /* PCM#1 Digital i/o */ + if ((err = snd_pcm_new(chip->card, "Digital PCM", 1, + num_digital_busses_out(chip), + num_digital_busses_in(chip), &pcm)) < 0) + return err; + pcm->private_data = chip; + chip->digital_pcm = pcm; + strcpy(pcm->name, chip->card->shortname); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &digital_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &digital_capture_ops); + if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0) + return err; + DE_INIT(("Digital PCM ok\n")); +#endif /* ECHOCARD_HAS_DIGITAL_IO */ + +#endif /* ECHOCARD_HAS_VMIXER */ + + return 0; +} + + + + +/****************************************************************************** + Control interface +******************************************************************************/ + +/******************* PCM output volume *******************/ +static int snd_echo_output_gain_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct echoaudio *chip; + + chip = snd_kcontrol_chip(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = num_busses_out(chip); + uinfo->value.integer.min = ECHOGAIN_MINOUT; + uinfo->value.integer.max = ECHOGAIN_MAXOUT; + return 0; +} + +static int snd_echo_output_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + int c; + + chip = snd_kcontrol_chip(kcontrol); + for (c = 0; c < num_busses_out(chip); c++) + ucontrol->value.integer.value[c] = chip->output_gain[c]; + return 0; +} + +static int snd_echo_output_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + int c, changed, gain; + + changed = 0; + chip = snd_kcontrol_chip(kcontrol); + spin_lock_irq(&chip->lock); + for (c = 0; c < num_busses_out(chip); c++) { + gain = ucontrol->value.integer.value[c]; + /* Ignore out of range values */ + if (gain < ECHOGAIN_MINOUT || gain > ECHOGAIN_MAXOUT) + continue; + if (chip->output_gain[c] != gain) { + set_output_gain(chip, c, gain); + changed = 1; + } + } + if (changed) + update_output_line_level(chip); + spin_unlock_irq(&chip->lock); + return changed; +} + +#ifdef ECHOCARD_HAS_VMIXER +/* On Vmixer cards this one controls the line-out volume */ +static struct snd_kcontrol_new snd_echo_line_output_gain __devinitdata = { + .name = "Line Playback Volume", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_echo_output_gain_info, + .get = snd_echo_output_gain_get, + .put = snd_echo_output_gain_put, +}; +#else +static struct snd_kcontrol_new snd_echo_pcm_output_gain __devinitdata = { + .name = "PCM Playback Volume", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_echo_output_gain_info, + .get = snd_echo_output_gain_get, + .put = snd_echo_output_gain_put, +}; +#endif + + + +#ifdef ECHOCARD_HAS_INPUT_GAIN + +/******************* Analog input volume *******************/ +static int snd_echo_input_gain_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct echoaudio *chip; + + chip = snd_kcontrol_chip(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = num_analog_busses_in(chip); + uinfo->value.integer.min = ECHOGAIN_MININP; + uinfo->value.integer.max = ECHOGAIN_MAXINP; + return 0; +} + +static int snd_echo_input_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + int c; + + chip = snd_kcontrol_chip(kcontrol); + for (c = 0; c < num_analog_busses_in(chip); c++) + ucontrol->value.integer.value[c] = chip->input_gain[c]; + return 0; +} + +static int snd_echo_input_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + int c, gain, changed; + + changed = 0; + chip = snd_kcontrol_chip(kcontrol); + spin_lock_irq(&chip->lock); + for (c = 0; c < num_analog_busses_in(chip); c++) { + gain = ucontrol->value.integer.value[c]; + /* Ignore out of range values */ + if (gain < ECHOGAIN_MININP || gain > ECHOGAIN_MAXINP) + continue; + if (chip->input_gain[c] != gain) { + set_input_gain(chip, c, gain); + changed = 1; + } + } + if (changed) + update_input_line_level(chip); + spin_unlock_irq(&chip->lock); + return changed; +} + +static struct snd_kcontrol_new snd_echo_line_input_gain __devinitdata = { + .name = "Line Capture Volume", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_echo_input_gain_info, + .get = snd_echo_input_gain_get, + .put = snd_echo_input_gain_put, +}; + +#endif /* ECHOCARD_HAS_INPUT_GAIN */ + + + +#ifdef ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL + +/************ Analog output nominal level (+4dBu / -10dBV) ***************/ +static int snd_echo_output_nominal_info (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct echoaudio *chip; + + chip = snd_kcontrol_chip(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = num_analog_busses_out(chip); + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_echo_output_nominal_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + int c; + + chip = snd_kcontrol_chip(kcontrol); + for (c = 0; c < num_analog_busses_out(chip); c++) + ucontrol->value.integer.value[c] = chip->nominal_level[c]; + return 0; +} + +static int snd_echo_output_nominal_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + int c, changed; + + changed = 0; + chip = snd_kcontrol_chip(kcontrol); + spin_lock_irq(&chip->lock); + for (c = 0; c < num_analog_busses_out(chip); c++) { + if (chip->nominal_level[c] != ucontrol->value.integer.value[c]) { + set_nominal_level(chip, c, + ucontrol->value.integer.value[c]); + changed = 1; + } + } + if (changed) + update_output_line_level(chip); + spin_unlock_irq(&chip->lock); + return changed; +} + +static struct snd_kcontrol_new snd_echo_output_nominal_level __devinitdata = { + .name = "Line Playback Switch (-10dBV)", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_echo_output_nominal_info, + .get = snd_echo_output_nominal_get, + .put = snd_echo_output_nominal_put, +}; + +#endif /* ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL */ + + + +#ifdef ECHOCARD_HAS_INPUT_NOMINAL_LEVEL + +/*************** Analog input nominal level (+4dBu / -10dBV) ***************/ +static int snd_echo_input_nominal_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct echoaudio *chip; + + chip = snd_kcontrol_chip(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = num_analog_busses_in(chip); + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_echo_input_nominal_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + int c; + + chip = snd_kcontrol_chip(kcontrol); + for (c = 0; c < num_analog_busses_in(chip); c++) + ucontrol->value.integer.value[c] = + chip->nominal_level[bx_analog_in(chip) + c]; + return 0; +} + +static int snd_echo_input_nominal_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + int c, changed; + + changed = 0; + chip = snd_kcontrol_chip(kcontrol); + spin_lock_irq(&chip->lock); + for (c = 0; c < num_analog_busses_in(chip); c++) { + if (chip->nominal_level[bx_analog_in(chip) + c] != + ucontrol->value.integer.value[c]) { + set_nominal_level(chip, bx_analog_in(chip) + c, + ucontrol->value.integer.value[c]); + changed = 1; + } + } + if (changed) + update_output_line_level(chip); /* "Output" is not a mistake + * here. + */ + spin_unlock_irq(&chip->lock); + return changed; +} + +static struct snd_kcontrol_new snd_echo_intput_nominal_level __devinitdata = { + .name = "Line Capture Switch (-10dBV)", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_echo_input_nominal_info, + .get = snd_echo_input_nominal_get, + .put = snd_echo_input_nominal_put, +}; + +#endif /* ECHOCARD_HAS_INPUT_NOMINAL_LEVEL */ + + + +#ifdef ECHOCARD_HAS_MONITOR + +/******************* Monitor mixer *******************/ +static int snd_echo_mixer_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct echoaudio *chip; + + chip = snd_kcontrol_chip(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = ECHOGAIN_MINOUT; + uinfo->value.integer.max = ECHOGAIN_MAXOUT; + uinfo->dimen.d[0] = num_busses_out(chip); + uinfo->dimen.d[1] = num_busses_in(chip); + return 0; +} + +static int snd_echo_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + + chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = + chip->monitor_gain[ucontrol->id.index / num_busses_in(chip)] + [ucontrol->id.index % num_busses_in(chip)]; + return 0; +} + +static int snd_echo_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + int changed, gain; + short out, in; + + changed = 0; + chip = snd_kcontrol_chip(kcontrol); + out = ucontrol->id.index / num_busses_in(chip); + in = ucontrol->id.index % num_busses_in(chip); + gain = ucontrol->value.integer.value[0]; + if (gain < ECHOGAIN_MINOUT || gain > ECHOGAIN_MAXOUT) + return -EINVAL; + if (chip->monitor_gain[out][in] != gain) { + spin_lock_irq(&chip->lock); + set_monitor_gain(chip, out, in, gain); + update_output_line_level(chip); + spin_unlock_irq(&chip->lock); + changed = 1; + } + return changed; +} + +static struct snd_kcontrol_new snd_echo_monitor_mixer __devinitdata = { + .name = "Monitor Mixer Volume", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_echo_mixer_info, + .get = snd_echo_mixer_get, + .put = snd_echo_mixer_put, +}; + +#endif /* ECHOCARD_HAS_MONITOR */ + + + +#ifdef ECHOCARD_HAS_VMIXER + +/******************* Vmixer *******************/ +static int snd_echo_vmixer_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct echoaudio *chip; + + chip = snd_kcontrol_chip(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = ECHOGAIN_MINOUT; + uinfo->value.integer.max = ECHOGAIN_MAXOUT; + uinfo->dimen.d[0] = num_busses_out(chip); + uinfo->dimen.d[1] = num_pipes_out(chip); + return 0; +} + +static int snd_echo_vmixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + + chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = + chip->vmixer_gain[ucontrol->id.index / num_pipes_out(chip)] + [ucontrol->id.index % num_pipes_out(chip)]; + return 0; +} + +static int snd_echo_vmixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + int gain, changed; + short vch, out; + + changed = 0; + chip = snd_kcontrol_chip(kcontrol); + out = ucontrol->id.index / num_pipes_out(chip); + vch = ucontrol->id.index % num_pipes_out(chip); + gain = ucontrol->value.integer.value[0]; + if (gain < ECHOGAIN_MINOUT || gain > ECHOGAIN_MAXOUT) + return -EINVAL; + if (chip->vmixer_gain[out][vch] != ucontrol->value.integer.value[0]) { + spin_lock_irq(&chip->lock); + set_vmixer_gain(chip, out, vch, ucontrol->value.integer.value[0]); + update_vmixer_level(chip); + spin_unlock_irq(&chip->lock); + changed = 1; + } + return changed; +} + +static struct snd_kcontrol_new snd_echo_vmixer __devinitdata = { + .name = "VMixer Volume", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_echo_vmixer_info, + .get = snd_echo_vmixer_get, + .put = snd_echo_vmixer_put, +}; + +#endif /* ECHOCARD_HAS_VMIXER */ + + + +#ifdef ECHOCARD_HAS_DIGITAL_MODE_SWITCH + +/******************* Digital mode switch *******************/ +static int snd_echo_digital_mode_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *names[4] = { + "S/PDIF Coaxial", "S/PDIF Optical", "ADAT Optical", + "S/PDIF Cdrom" + }; + struct echoaudio *chip; + + chip = snd_kcontrol_chip(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->value.enumerated.items = chip->num_digital_modes; + uinfo->count = 1; + if (uinfo->value.enumerated.item >= chip->num_digital_modes) + uinfo->value.enumerated.item = chip->num_digital_modes - 1; + strcpy(uinfo->value.enumerated.name, names[ + chip->digital_mode_list[uinfo->value.enumerated.item]]); + return 0; +} + +static int snd_echo_digital_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + int i, mode; + + chip = snd_kcontrol_chip(kcontrol); + mode = chip->digital_mode; + for (i = chip->num_digital_modes - 1; i >= 0; i--) + if (mode == chip->digital_mode_list[i]) { + ucontrol->value.enumerated.item[0] = i; + break; + } + return 0; +} + +static int snd_echo_digital_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + int changed; + unsigned short emode, dmode; + + changed = 0; + chip = snd_kcontrol_chip(kcontrol); + + emode = ucontrol->value.enumerated.item[0]; + if (emode >= chip->num_digital_modes) + return -EINVAL; + dmode = chip->digital_mode_list[emode]; + + if (dmode != chip->digital_mode) { + /* mode_mutex is required to make this operation atomic wrt + pcm_digital_*_open() and set_input_clock() functions. */ + down(&chip->mode_mutex); + + /* Do not allow the user to change the digital mode when a pcm + device is open because it also changes the number of channels + and the allowed sample rates */ + if (atomic_read(&chip->opencount)) { + changed = -EAGAIN; + } else { + changed = set_digital_mode(chip, dmode); + /* If we had to change the clock source, report it */ + if (changed > 0 && chip->clock_src_ctl) { + snd_ctl_notify(chip->card, + SNDRV_CTL_EVENT_MASK_VALUE, + &chip->clock_src_ctl->id); + DE_ACT(("SDM() =%d\n", changed)); + } + if (changed >= 0) + changed = 1; /* No errors */ + } + up(&chip->mode_mutex); + } + return changed; +} + +static struct snd_kcontrol_new snd_echo_digital_mode_switch __devinitdata = { + .name = "Digital mode Switch", + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .info = snd_echo_digital_mode_info, + .get = snd_echo_digital_mode_get, + .put = snd_echo_digital_mode_put, +}; + +#endif /* ECHOCARD_HAS_DIGITAL_MODE_SWITCH */ + + + +#ifdef ECHOCARD_HAS_DIGITAL_IO + +/******************* S/PDIF mode switch *******************/ +static int snd_echo_spdif_mode_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *names[2] = {"Consumer", "Professional"}; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->value.enumerated.items = 2; + uinfo->count = 1; + if (uinfo->value.enumerated.item) + uinfo->value.enumerated.item = 1; + strcpy(uinfo->value.enumerated.name, + names[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_echo_spdif_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + + chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.enumerated.item[0] = !!chip->professional_spdif; + return 0; +} + +static int snd_echo_spdif_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + int mode; + + chip = snd_kcontrol_chip(kcontrol); + mode = !!ucontrol->value.enumerated.item[0]; + if (mode != chip->professional_spdif) { + spin_lock_irq(&chip->lock); + set_professional_spdif(chip, mode); + spin_unlock_irq(&chip->lock); + return 1; + } + return 0; +} + +static struct snd_kcontrol_new snd_echo_spdif_mode_switch __devinitdata = { + .name = "S/PDIF mode Switch", + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .info = snd_echo_spdif_mode_info, + .get = snd_echo_spdif_mode_get, + .put = snd_echo_spdif_mode_put, +}; + +#endif /* ECHOCARD_HAS_DIGITAL_IO */ + + + +#ifdef ECHOCARD_HAS_EXTERNAL_CLOCK + +/******************* Select input clock source *******************/ +static int snd_echo_clock_source_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *names[8] = { + "Internal", "Word", "Super", "S/PDIF", "ADAT", "ESync", + "ESync96", "MTC" + }; + struct echoaudio *chip; + + chip = snd_kcontrol_chip(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->value.enumerated.items = chip->num_clock_sources; + uinfo->count = 1; + if (uinfo->value.enumerated.item >= chip->num_clock_sources) + uinfo->value.enumerated.item = chip->num_clock_sources - 1; + strcpy(uinfo->value.enumerated.name, names[ + chip->clock_source_list[uinfo->value.enumerated.item]]); + return 0; +} + +static int snd_echo_clock_source_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + int i, clock; + + chip = snd_kcontrol_chip(kcontrol); + clock = chip->input_clock; + + for (i = 0; i < chip->num_clock_sources; i++) + if (clock == chip->clock_source_list[i]) + ucontrol->value.enumerated.item[0] = i; + + return 0; +} + +static int snd_echo_clock_source_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + int changed; + unsigned int eclock, dclock; + + changed = 0; + chip = snd_kcontrol_chip(kcontrol); + eclock = ucontrol->value.enumerated.item[0]; + if (eclock >= chip->input_clock_types) + return -EINVAL; + dclock = chip->clock_source_list[eclock]; + if (chip->input_clock != dclock) { + down(&chip->mode_mutex); + spin_lock_irq(&chip->lock); + if ((changed = set_input_clock(chip, dclock)) == 0) + changed = 1; /* no errors */ + spin_unlock_irq(&chip->lock); + up(&chip->mode_mutex); + } + + if (changed < 0) + DE_ACT(("seticlk val%d err 0x%x\n", dclock, changed)); + + return changed; +} + +static struct snd_kcontrol_new snd_echo_clock_source_switch __devinitdata = { + .name = "Sample Clock Source", + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .info = snd_echo_clock_source_info, + .get = snd_echo_clock_source_get, + .put = snd_echo_clock_source_put, +}; + +#endif /* ECHOCARD_HAS_EXTERNAL_CLOCK */ + + + +#ifdef ECHOCARD_HAS_PHANTOM_POWER + +/******************* Phantom power switch *******************/ +static int snd_echo_phantom_power_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_echo_phantom_power_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = chip->phantom_power; + return 0; +} + +static int snd_echo_phantom_power_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip = snd_kcontrol_chip(kcontrol); + int power, changed = 0; + + power = !!ucontrol->value.integer.value[0]; + if (chip->phantom_power != power) { + spin_lock_irq(&chip->lock); + changed = set_phantom_power(chip, power); + spin_unlock_irq(&chip->lock); + if (changed == 0) + changed = 1; /* no errors */ + } + return changed; +} + +static struct snd_kcontrol_new snd_echo_phantom_power_switch __devinitdata = { + .name = "Phantom power Switch", + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .info = snd_echo_phantom_power_info, + .get = snd_echo_phantom_power_get, + .put = snd_echo_phantom_power_put, +}; + +#endif /* ECHOCARD_HAS_PHANTOM_POWER */ + + + +#ifdef ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE + +/******************* Digital input automute switch *******************/ +static int snd_echo_automute_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_echo_automute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = chip->digital_in_automute; + return 0; +} + +static int snd_echo_automute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip = snd_kcontrol_chip(kcontrol); + int automute, changed = 0; + + automute = !!ucontrol->value.integer.value[0]; + if (chip->digital_in_automute != automute) { + spin_lock_irq(&chip->lock); + changed = set_input_auto_mute(chip, automute); + spin_unlock_irq(&chip->lock); + if (changed == 0) + changed = 1; /* no errors */ + } + return changed; +} + +static struct snd_kcontrol_new snd_echo_automute_switch __devinitdata = { + .name = "Digital Capture Switch (automute)", + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .info = snd_echo_automute_info, + .get = snd_echo_automute_get, + .put = snd_echo_automute_put, +}; + +#endif /* ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE */ + + + +/******************* VU-meters switch *******************/ +static int snd_echo_vumeters_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct echoaudio *chip; + + chip = snd_kcontrol_chip(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_echo_vumeters_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + + chip = snd_kcontrol_chip(kcontrol); + spin_lock_irq(&chip->lock); + set_meters_on(chip, ucontrol->value.integer.value[0]); + spin_unlock_irq(&chip->lock); + return 1; +} + +static struct snd_kcontrol_new snd_echo_vumeters_switch __devinitdata = { + .name = "VU-meters Switch", + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .access = SNDRV_CTL_ELEM_ACCESS_WRITE, + .info = snd_echo_vumeters_switch_info, + .put = snd_echo_vumeters_switch_put, +}; + + + +/***** Read VU-meters (input, output, analog and digital together) *****/ +static int snd_echo_vumeters_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct echoaudio *chip; + + chip = snd_kcontrol_chip(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 96; + uinfo->value.integer.min = ECHOGAIN_MINOUT; + uinfo->value.integer.max = 0; +#ifdef ECHOCARD_HAS_VMIXER + uinfo->dimen.d[0] = 3; /* Out, In, Virt */ +#else + uinfo->dimen.d[0] = 2; /* Out, In */ +#endif + uinfo->dimen.d[1] = 16; /* 16 channels */ + uinfo->dimen.d[2] = 2; /* 0=level, 1=peak */ + return 0; +} + +static int snd_echo_vumeters_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + + chip = snd_kcontrol_chip(kcontrol); + get_audio_meters(chip, ucontrol->value.integer.value); + return 0; +} + +static struct snd_kcontrol_new snd_echo_vumeters __devinitdata = { + .name = "VU-meters", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_echo_vumeters_info, + .get = snd_echo_vumeters_get, +}; + + + +/*** Channels info - it exports informations about the number of channels ***/ +static int snd_echo_channels_info_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct echoaudio *chip; + + chip = snd_kcontrol_chip(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 6; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1 << ECHO_CLOCK_NUMBER; + return 0; +} + +static int snd_echo_channels_info_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + int detected, clocks, bit, src; + + chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = num_busses_in(chip); + ucontrol->value.integer.value[1] = num_analog_busses_in(chip); + ucontrol->value.integer.value[2] = num_busses_out(chip); + ucontrol->value.integer.value[3] = num_analog_busses_out(chip); + ucontrol->value.integer.value[4] = num_pipes_out(chip); + + /* Compute the bitmask of the currently valid input clocks */ + detected = detect_input_clocks(chip); + clocks = 0; + src = chip->num_clock_sources - 1; + for (bit = ECHO_CLOCK_NUMBER - 1; bit >= 0; bit--) + if (detected & (1 << bit)) + for (; src >= 0; src--) + if (bit == chip->clock_source_list[src]) { + clocks |= 1 << src; + break; + } + ucontrol->value.integer.value[5] = clocks; + + return 0; +} + +static struct snd_kcontrol_new snd_echo_channels_info __devinitdata = { + .name = "Channels info", + .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_echo_channels_info_info, + .get = snd_echo_channels_info_get, +}; + + + + +/****************************************************************************** + IRQ Handler +******************************************************************************/ + +static irqreturn_t snd_echo_interrupt(int irq, void *dev_id, + struct pt_regs *regs) +{ + struct echoaudio *chip = dev_id; + struct snd_pcm_substream *substream; + int period, ss, st; + + spin_lock(&chip->lock); + st = service_irq(chip); + if (st < 0) { + spin_unlock(&chip->lock); + return IRQ_NONE; + } + /* The hardware doesn't tell us which substream caused the irq, + thus we have to check all running substreams. */ + for (ss = 0; ss < DSP_MAXPIPES; ss++) { + if ((substream = chip->substream[ss])) { + period = pcm_pointer(substream) / + substream->runtime->period_size; + if (period != chip->last_period[ss]) { + chip->last_period[ss] = period; + spin_unlock(&chip->lock); + snd_pcm_period_elapsed(substream); + spin_lock(&chip->lock); + } + } + } + spin_unlock(&chip->lock); + +#ifdef ECHOCARD_HAS_MIDI + if (st > 0 && chip->midi_in) { + snd_rawmidi_receive(chip->midi_in, chip->midi_buffer, st); + DE_MID(("rawmidi_iread=%d\n", st)); + } +#endif + return IRQ_HANDLED; +} + + + + +/****************************************************************************** + Module construction / destruction +******************************************************************************/ + +static int snd_echo_free(struct echoaudio *chip) +{ + DE_INIT(("Stop DSP...\n")); + if (chip->comm_page) { + rest_in_peace(chip); + snd_dma_free_pages(&chip->commpage_dma_buf); + } + DE_INIT(("Stopped.\n")); + + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + + if (chip->dsp_registers) + iounmap(chip->dsp_registers); + + if (chip->iores) + release_and_free_resource(chip->iores); + + DE_INIT(("MMIO freed.\n")); + + pci_disable_device(chip->pci); + + /* release chip data */ + kfree(chip); + DE_INIT(("Chip freed.\n")); + return 0; +} + + + +static int snd_echo_dev_free(struct snd_device *device) +{ + struct echoaudio *chip = device->device_data; + + DE_INIT(("snd_echo_dev_free()...\n")); + return snd_echo_free(chip); +} + + + +/* <--snd_echo_probe() */ +static __devinit int snd_echo_create(struct snd_card *card, + struct pci_dev *pci, + struct echoaudio **rchip) +{ + struct echoaudio *chip; + int err; + size_t sz; + static struct snd_device_ops ops = { + .dev_free = snd_echo_dev_free, + }; + + *rchip = NULL; + + pci_write_config_byte(pci, PCI_LATENCY_TIMER, 0xC0); + + if ((err = pci_enable_device(pci)) < 0) + return err; + pci_set_master(pci); + + /* allocate a chip-specific data */ + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) { + pci_disable_device(pci); + return -ENOMEM; + } + DE_INIT(("chip=%p\n", chip)); + + spin_lock_init(&chip->lock); + chip->card = card; + chip->pci = pci; + chip->irq = -1; + + /* PCI resource allocation */ + chip->dsp_registers_phys = pci_resource_start(pci, 0); + sz = pci_resource_len(pci, 0); + if (sz > PAGE_SIZE) + sz = PAGE_SIZE; /* We map only the required part */ + + if ((chip->iores = request_mem_region(chip->dsp_registers_phys, sz, + ECHOCARD_NAME)) == NULL) { + snd_echo_free(chip); + snd_printk(KERN_ERR "cannot get memory region\n"); + return -EBUSY; + } + chip->dsp_registers = (volatile u32 __iomem *) + ioremap_nocache(chip->dsp_registers_phys, sz); + + if (request_irq(pci->irq, snd_echo_interrupt, SA_INTERRUPT | SA_SHIRQ, + ECHOCARD_NAME, (void *)chip)) { + snd_echo_free(chip); + snd_printk(KERN_ERR "cannot grab irq\n"); + return -EBUSY; + } + chip->irq = pci->irq; + DE_INIT(("pci=%p irq=%d subdev=%04x Init hardware...\n", + chip->pci, chip->irq, chip->pci->subsystem_device)); + + /* Create the DSP comm page - this is the area of memory used for most + of the communication with the DSP, which accesses it via bus mastering */ + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), + sizeof(struct comm_page), + &chip->commpage_dma_buf) < 0) { + snd_echo_free(chip); + snd_printk(KERN_ERR "cannot allocate the comm page\n"); + return -ENOMEM; + } + chip->comm_page_phys = chip->commpage_dma_buf.addr; + chip->comm_page = (struct comm_page *)chip->commpage_dma_buf.area; + + err = init_hw(chip, chip->pci->device, chip->pci->subsystem_device); + if (err) { + DE_INIT(("init_hw err=%d\n", err)); + snd_echo_free(chip); + return err; + } + DE_INIT(("Card init OK\n")); + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_echo_free(chip); + return err; + } + atomic_set(&chip->opencount, 0); + init_MUTEX(&chip->mode_mutex); + chip->can_set_rate = 1; + *rchip = chip; + /* Init done ! */ + return 0; +} + + + +/* constructor */ +static int __devinit snd_echo_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + struct snd_card *card; + struct echoaudio *chip; + char *dsp; + int i, err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + DE_INIT(("Echoaudio driver starting...\n")); + i = 0; + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + if ((err = snd_echo_create(card, pci, &chip)) < 0) { + snd_card_free(card); + return err; + } + + strcpy(card->driver, "Echo_" ECHOCARD_NAME); + strcpy(card->shortname, chip->card_name); + + dsp = "56301"; + if (pci_id->device == 0x3410) + dsp = "56361"; + + sprintf(card->longname, "%s rev.%d (DSP%s) at 0x%lx irq %i", + card->shortname, pci_id->subdevice & 0x000f, dsp, + chip->dsp_registers_phys, chip->irq); + + if ((err = snd_echo_new_pcm(chip)) < 0) { + snd_printk(KERN_ERR "new pcm error %d\n", err); + snd_card_free(card); + return err; + } + +#ifdef ECHOCARD_HAS_MIDI + if (chip->has_midi) { /* Some Mia's do not have midi */ + if ((err = snd_echo_midi_create(card, chip)) < 0) { + snd_printk(KERN_ERR "new midi error %d\n", err); + snd_card_free(card); + return err; + } + } +#endif + +#ifdef ECHOCARD_HAS_VMIXER + snd_echo_vmixer.count = num_pipes_out(chip) * num_busses_out(chip); + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_line_output_gain, chip))) < 0) + goto ctl_error; + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vmixer, chip))) < 0) + goto ctl_error; +#else + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_pcm_output_gain, chip))) < 0) + goto ctl_error; +#endif + +#ifdef ECHOCARD_HAS_INPUT_GAIN + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_line_input_gain, chip))) < 0) + goto ctl_error; +#endif + +#ifdef ECHOCARD_HAS_INPUT_NOMINAL_LEVEL + if (!chip->hasnt_input_nominal_level) + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_intput_nominal_level, chip))) < 0) + goto ctl_error; +#endif + +#ifdef ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_output_nominal_level, chip))) < 0) + goto ctl_error; +#endif + + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vumeters_switch, chip))) < 0) + goto ctl_error; + + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vumeters, chip))) < 0) + goto ctl_error; + +#ifdef ECHOCARD_HAS_MONITOR + snd_echo_monitor_mixer.count = num_busses_in(chip) * num_busses_out(chip); + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_monitor_mixer, chip))) < 0) + goto ctl_error; +#endif + +#ifdef ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_automute_switch, chip))) < 0) + goto ctl_error; +#endif + + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_channels_info, chip))) < 0) + goto ctl_error; + +#ifdef ECHOCARD_HAS_DIGITAL_MODE_SWITCH + /* Creates a list of available digital modes */ + chip->num_digital_modes = 0; + for (i = 0; i < 6; i++) + if (chip->digital_modes & (1 << i)) + chip->digital_mode_list[chip->num_digital_modes++] = i; + + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_digital_mode_switch, chip))) < 0) + goto ctl_error; +#endif /* ECHOCARD_HAS_DIGITAL_MODE_SWITCH */ + +#ifdef ECHOCARD_HAS_EXTERNAL_CLOCK + /* Creates a list of available clock sources */ + chip->num_clock_sources = 0; + for (i = 0; i < 10; i++) + if (chip->input_clock_types & (1 << i)) + chip->clock_source_list[chip->num_clock_sources++] = i; + + if (chip->num_clock_sources > 1) { + chip->clock_src_ctl = snd_ctl_new1(&snd_echo_clock_source_switch, chip); + if ((err = snd_ctl_add(chip->card, chip->clock_src_ctl)) < 0) + goto ctl_error; + } +#endif /* ECHOCARD_HAS_EXTERNAL_CLOCK */ + +#ifdef ECHOCARD_HAS_DIGITAL_IO + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_spdif_mode_switch, chip))) < 0) + goto ctl_error; +#endif + +#ifdef ECHOCARD_HAS_PHANTOM_POWER + if (chip->has_phantom_power) + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_phantom_power_switch, chip))) < 0) + goto ctl_error; +#endif + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + goto ctl_error; + } + snd_printk(KERN_INFO "Card registered: %s\n", card->longname); + + pci_set_drvdata(pci, chip); + dev++; + return 0; + +ctl_error: + snd_printk(KERN_ERR "new control error %d\n", err); + snd_card_free(card); + return err; +} + + + +static void __devexit snd_echo_remove(struct pci_dev *pci) +{ + struct echoaudio *chip; + + chip = pci_get_drvdata(pci); + if (chip) + snd_card_free(chip->card); + pci_set_drvdata(pci, NULL); +} + + + +/****************************************************************************** + Everything starts and ends here +******************************************************************************/ + +/* pci_driver definition */ +static struct pci_driver driver = { + .name = "Echoaudio " ECHOCARD_NAME, + .id_table = snd_echo_ids, + .probe = snd_echo_probe, + .remove = __devexit_p(snd_echo_remove), +}; + + + +/* initialization of the module */ +static int __init alsa_card_echo_init(void) +{ + return pci_register_driver(&driver); +} + + + +/* clean up the module */ +static void __exit alsa_card_echo_exit(void) +{ + pci_unregister_driver(&driver); +} + + +module_init(alsa_card_echo_init) +module_exit(alsa_card_echo_exit) diff --git a/sound/pci/echoaudio/echoaudio.h b/sound/pci/echoaudio/echoaudio.h new file mode 100644 index 00000000000..7e88c968e22 --- /dev/null +++ b/sound/pci/echoaudio/echoaudio.h @@ -0,0 +1,590 @@ +/**************************************************************************** + + Copyright Echo Digital Audio Corporation (c) 1998 - 2004 + All rights reserved + www.echoaudio.com + + This file is part of Echo Digital Audio's generic driver library. + + Echo Digital Audio's generic driver library is free software; + you can redistribute it and/or modify it under the terms of + the GNU General Public License as published by the Free Software + Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + **************************************************************************** + + Translation from C++ and adaptation for use in ALSA-Driver + were made by Giuliano Pochini <pochini@shiny.it> + + **************************************************************************** + + + Here's a block diagram of how most of the cards work: + + +-----------+ + record | |<-------------------- Inputs + <-------| | | + PCI | Transport | | + bus | engine | \|/ + ------->| | +-------+ + play | |--->|monitor|-------> Outputs + +-----------+ | mixer | + +-------+ + + The lines going to and from the PCI bus represent "pipes". A pipe performs + audio transport - moving audio data to and from buffers on the host via + bus mastering. + + The inputs and outputs on the right represent input and output "busses." + A bus is a physical, real connection to the outside world. An example + of a bus would be the 1/4" analog connectors on the back of Layla or + an RCA S/PDIF connector. + + For most cards, there is a one-to-one correspondence between outputs + and busses; that is, each individual pipe is hard-wired to a single bus. + + Cards that work this way are Darla20, Gina20, Layla20, Darla24, Gina24, + Layla24, Mona, and Indigo. + + + Mia has a feature called "virtual outputs." + + + +-----------+ + record | |<----------------------------- Inputs + <-------| | | + PCI | Transport | | + bus | engine | \|/ + ------->| | +------+ +-------+ + play | |-->|vmixer|-->|monitor|-------> Outputs + +-----------+ +------+ | mixer | + +-------+ + + + Obviously, the difference here is the box labeled "vmixer." Vmixer is + short for "virtual output mixer." For Mia, pipes are *not* hard-wired + to a single bus; the vmixer lets you mix any pipe to any bus in any + combination. + + Note, however, that the left-hand side of the diagram is unchanged. + Transport works exactly the same way - the difference is in the mixer stage. + + + Pipes and busses are numbered starting at zero. + + + + Pipe index + ========== + + A number of calls in CEchoGals refer to a "pipe index". A pipe index is + a unique number for a pipe that unambiguously refers to a playback or record + pipe. Pipe indices are numbered starting with analog outputs, followed by + digital outputs, then analog inputs, then digital inputs. + + Take Gina24 as an example: + + Pipe index + + 0-7 Analog outputs (0 .. FirstDigitalBusOut-1) + 8-15 Digital outputs (FirstDigitalBusOut .. NumBussesOut-1) + 16-17 Analog inputs + 18-25 Digital inputs + + + You get the pipe index by calling CEchoGals::OpenAudio; the other transport + functions take the pipe index as a parameter. If you need a pipe index for + some other reason, use the handy Makepipe_index method. + + + Some calls take a CChannelMask parameter; CChannelMask is a handy way to + group pipe indices. + + + + Digital mode switch + =================== + + Some cards (right now, Gina24, Layla24, and Mona) have a Digital Mode Switch + or DMS. Cards with a DMS can be set to one of three mutually exclusive + digital modes: S/PDIF RCA, S/PDIF optical, or ADAT optical. + + This may create some confusion since ADAT optical is 8 channels wide and + S/PDIF is only two channels wide. Gina24, Layla24, and Mona handle this + by acting as if they always have 8 digital outs and ins. If you are in + either S/PDIF mode, the last 6 channels don't do anything - data sent + out these channels is thrown away and you will always record zeros. + + Note that with Gina24, Layla24, and Mona, sample rates above 50 kHz are + only available if you have the card configured for S/PDIF optical or S/PDIF + RCA. + + + + Double speed mode + ================= + + Some of the cards support 88.2 kHz and 96 kHz sampling (Darla24, Gina24, + Layla24, Mona, Mia, and Indigo). For these cards, the driver sometimes has + to worry about "double speed mode"; double speed mode applies whenever the + sampling rate is above 50 kHz. + + For instance, Mona and Layla24 support word clock sync. However, they + actually support two different word clock modes - single speed (below + 50 kHz) and double speed (above 50 kHz). The hardware detects if a single + or double speed word clock signal is present; the generic code uses that + information to determine which mode to use. + + The generic code takes care of all this for you. +*/ + + +#ifndef _ECHOAUDIO_H_ +#define _ECHOAUDIO_H_ + + +#define TRUE 1 +#define FALSE 0 + +#include "echoaudio_dsp.h" + + + +/*********************************************************************** + + PCI configuration space + +***********************************************************************/ + +/* + * PCI vendor ID and device IDs for the hardware + */ +#define VENDOR_ID 0x1057 +#define DEVICE_ID_56301 0x1801 +#define DEVICE_ID_56361 0x3410 +#define SUBVENDOR_ID 0xECC0 + + +/* + * Valid Echo PCI subsystem card IDs + */ +#define DARLA20 0x0010 +#define GINA20 0x0020 +#define LAYLA20 0x0030 +#define DARLA24 0x0040 +#define GINA24 0x0050 +#define LAYLA24 0x0060 +#define MONA 0x0070 +#define MIA 0x0080 +#define INDIGO 0x0090 +#define INDIGO_IO 0x00a0 +#define INDIGO_DJ 0x00b0 +#define ECHO3G 0x0100 + + +/************************************************************************ + + Array sizes and so forth + +***********************************************************************/ + +/* + * Sizes + */ +#define ECHO_MAXAUDIOINPUTS 32 /* Max audio input channels */ +#define ECHO_MAXAUDIOOUTPUTS 32 /* Max audio output channels */ +#define ECHO_MAXAUDIOPIPES 32 /* Max number of input and output + * pipes */ +#define E3G_MAX_OUTPUTS 16 +#define ECHO_MAXMIDIJACKS 1 /* Max MIDI ports */ +#define ECHO_MIDI_QUEUE_SZ 512 /* Max MIDI input queue entries */ +#define ECHO_MTC_QUEUE_SZ 32 /* Max MIDI time code input queue + * entries */ + +/* + * MIDI activity indicator timeout + */ +#define MIDI_ACTIVITY_TIMEOUT_USEC 200000 + + +/**************************************************************************** + + Clocks + +*****************************************************************************/ + +/* + * Clock numbers + */ +#define ECHO_CLOCK_INTERNAL 0 +#define ECHO_CLOCK_WORD 1 +#define ECHO_CLOCK_SUPER 2 +#define ECHO_CLOCK_SPDIF 3 +#define ECHO_CLOCK_ADAT 4 +#define ECHO_CLOCK_ESYNC 5 +#define ECHO_CLOCK_ESYNC96 6 +#define ECHO_CLOCK_MTC 7 +#define ECHO_CLOCK_NUMBER 8 +#define ECHO_CLOCKS 0xffff + +/* + * Clock bit numbers - used to report capabilities and whatever clocks + * are being detected dynamically. + */ +#define ECHO_CLOCK_BIT_INTERNAL (1 << ECHO_CLOCK_INTERNAL) +#define ECHO_CLOCK_BIT_WORD (1 << ECHO_CLOCK_WORD) +#define ECHO_CLOCK_BIT_SUPER (1 << ECHO_CLOCK_SUPER) +#define ECHO_CLOCK_BIT_SPDIF (1 << ECHO_CLOCK_SPDIF) +#define ECHO_CLOCK_BIT_ADAT (1 << ECHO_CLOCK_ADAT) +#define ECHO_CLOCK_BIT_ESYNC (1 << ECHO_CLOCK_ESYNC) +#define ECHO_CLOCK_BIT_ESYNC96 (1 << ECHO_CLOCK_ESYNC96) +#define ECHO_CLOCK_BIT_MTC (1<<ECHO_CLOCK_MTC) + + +/*************************************************************************** + + Digital modes + +****************************************************************************/ + +/* + * Digital modes for Mona, Layla24, and Gina24 + */ +#define DIGITAL_MODE_NONE 0xFF +#define DIGITAL_MODE_SPDIF_RCA 0 +#define DIGITAL_MODE_SPDIF_OPTICAL 1 +#define DIGITAL_MODE_ADAT 2 +#define DIGITAL_MODE_SPDIF_CDROM 3 +#define DIGITAL_MODES 4 + +/* + * Digital mode capability masks + */ +#define ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA (1 << DIGITAL_MODE_SPDIF_RCA) +#define ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL (1 << DIGITAL_MODE_SPDIF_OPTICAL) +#define ECHOCAPS_HAS_DIGITAL_MODE_ADAT (1 << DIGITAL_MODE_ADAT) +#define ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_CDROM (1 << DIGITAL_MODE_SPDIF_CDROM) + + +#define EXT_3GBOX_NC 0x01 /* 3G box not connected */ +#define EXT_3GBOX_NOT_SET 0x02 /* 3G box not detected yet */ + + +#define ECHOGAIN_MUTED (-128) /* Minimum possible gain */ +#define ECHOGAIN_MINOUT (-128) /* Min output gain (dB) */ +#define ECHOGAIN_MAXOUT (6) /* Max output gain (dB) */ +#define ECHOGAIN_MININP (-50) /* Min input gain (0.5 dB) */ +#define ECHOGAIN_MAXINP (50) /* Max input gain (0.5 dB) */ + +#define PIPE_STATE_STOPPED 0 /* Pipe has been reset */ +#define PIPE_STATE_PAUSED 1 /* Pipe has been stopped */ +#define PIPE_STATE_STARTED 2 /* Pipe has been started */ +#define PIPE_STATE_PENDING 3 /* Pipe has pending start */ + + +/* Debug initialization */ +#ifdef CONFIG_SND_DEBUG +#define DE_INIT(x) snd_printk x +#else +#define DE_INIT(x) +#endif + +/* Debug hw_params callbacks */ +#ifdef CONFIG_SND_DEBUG +#define DE_HWP(x) snd_printk x +#else +#define DE_HWP(x) +#endif + +/* Debug normal activity (open, start, stop...) */ +#ifdef CONFIG_SND_DEBUG +#define DE_ACT(x) snd_printk x +#else +#define DE_ACT(x) +#endif + +/* Debug midi activity */ +#ifdef CONFIG_SND_DEBUG +#define DE_MID(x) snd_printk x +#else +#define DE_MID(x) +#endif + + +struct audiopipe { + volatile u32 *dma_counter; /* Commpage register that contains + * the current dma position + * (lower 32 bits only) + */ + u32 last_counter; /* The last position, which is used + * to compute... + */ + u32 position; /* ...the number of bytes tranferred + * by the DMA engine, modulo the + * buffer size + */ + short index; /* Index of the first channel or <0 + * if hw is not configured yet + */ + short interleave; + struct snd_dma_buffer sgpage; /* Room for the scatter-gather list */ + struct snd_pcm_hardware hw; + struct snd_pcm_hw_constraint_list constr; + short sglist_head; + char state; /* pipe state */ +}; + + +struct audioformat { + u8 interleave; /* How the data is arranged in memory: + * mono = 1, stereo = 2, ... + */ + u8 bits_per_sample; /* 8, 16, 24, 32 (24 bits left aligned) */ + char mono_to_stereo; /* Only used if interleave is 1 and + * if this is an output pipe. + */ + char data_are_bigendian; /* 1 = big endian, 0 = little endian */ +}; + + +struct echoaudio { + spinlock_t lock; + struct snd_pcm_substream *substream[DSP_MAXPIPES]; + int last_period[DSP_MAXPIPES]; + struct semaphore mode_mutex; + u16 num_digital_modes, digital_mode_list[6]; + u16 num_clock_sources, clock_source_list[10]; + atomic_t opencount; + struct snd_kcontrol *clock_src_ctl; + struct snd_pcm *analog_pcm, *digital_pcm; + struct snd_card *card; + const char *card_name; + struct pci_dev *pci; + unsigned long dsp_registers_phys; + struct resource *iores; + struct snd_dma_buffer commpage_dma_buf; + int irq; +#ifdef ECHOCARD_HAS_MIDI + struct snd_rawmidi *rmidi; + struct snd_rawmidi_substream *midi_in, *midi_out; +#endif + struct timer_list timer; + char tinuse; /* Timer in use */ + char midi_full; /* MIDI output buffer is full */ + char can_set_rate; + char rate_set; + + /* This stuff is used mainly by the lowlevel code */ + struct comm_page *comm_page; /* Virtual address of the memory + * seen by DSP + */ + u32 pipe_alloc_mask; /* Bitmask of allocated pipes */ + u32 pipe_cyclic_mask; /* Bitmask of pipes with cyclic + * buffers + */ + u32 sample_rate; /* Card sample rate in Hz */ + u8 digital_mode; /* Current digital mode + * (see DIGITAL_MODE_*) + */ + u8 spdif_status; /* Gina20, Darla20, Darla24 - only */ + u8 clock_state; /* Gina20, Darla20, Darla24 - only */ + u8 input_clock; /* Currently selected sample clock + * source + */ + u8 output_clock; /* Layla20 only */ + char meters_enabled; /* VU-meters status */ + char asic_loaded; /* Set TRUE when ASIC loaded */ + char bad_board; /* Set TRUE if DSP won't load */ + char professional_spdif; /* 0 = consumer; 1 = professional */ + char non_audio_spdif; /* 3G - only */ + char digital_in_automute; /* Gina24, Layla24, Mona - only */ + char has_phantom_power; + char hasnt_input_nominal_level; /* Gina3G */ + char phantom_power; /* Gina3G - only */ + char has_midi; + char midi_input_enabled; + +#ifdef ECHOCARD_ECHO3G + /* External module -dependent pipe and bus indexes */ + char px_digital_out, px_analog_in, px_digital_in, px_num; + char bx_digital_out, bx_analog_in, bx_digital_in, bx_num; +#endif + + char nominal_level[ECHO_MAXAUDIOPIPES]; /* True == -10dBV + * False == +4dBu */ + s8 input_gain[ECHO_MAXAUDIOINPUTS]; /* Input level -50..+50 + * unit is 0.5dB */ + s8 output_gain[ECHO_MAXAUDIOOUTPUTS]; /* Output level -128..+6 dB + * (-128=muted) */ + s8 monitor_gain[ECHO_MAXAUDIOOUTPUTS][ECHO_MAXAUDIOINPUTS]; + /* -128..+6 dB */ + s8 vmixer_gain[ECHO_MAXAUDIOOUTPUTS][ECHO_MAXAUDIOOUTPUTS]; + /* -128..+6 dB */ + + u16 digital_modes; /* Bitmask of supported modes + * (see ECHOCAPS_HAS_DIGITAL_MODE_*) */ + u16 input_clock_types; /* Suppoted input clock types */ + u16 output_clock_types; /* Suppoted output clock types - + * Layla20 only */ + u16 device_id, subdevice_id; + u16 *dsp_code; /* Current DSP code loaded, + * NULL if nothing loaded */ + const struct firmware *dsp_code_to_load;/* DSP code to load */ + const struct firmware *asic_code; /* Current ASIC code */ + u32 comm_page_phys; /* Physical address of the + * memory seen by DSP */ + volatile u32 __iomem *dsp_registers; /* DSP's register base */ + u32 active_mask; /* Chs. active mask or + * punks out */ + +#ifdef ECHOCARD_HAS_MIDI + u16 mtc_state; /* State for MIDI input parsing state machine */ + u8 midi_buffer[MIDI_IN_BUFFER_SIZE]; +#endif +}; + + +static int init_dsp_comm_page(struct echoaudio *chip); +static int init_line_levels(struct echoaudio *chip); +static int free_pipes(struct echoaudio *chip, struct audiopipe *pipe); +static int load_firmware(struct echoaudio *chip); +static int wait_handshake(struct echoaudio *chip); +static int send_vector(struct echoaudio *chip, u32 command); +static int get_firmware(const struct firmware **fw_entry, + const struct firmware *frm, struct echoaudio *chip); +static void free_firmware(const struct firmware *fw_entry); + +#ifdef ECHOCARD_HAS_MIDI +static int enable_midi_input(struct echoaudio *chip, char enable); +static int midi_service_irq(struct echoaudio *chip); +static int __devinit snd_echo_midi_create(struct snd_card *card, + struct echoaudio *chip); +#endif + + +static inline void clear_handshake(struct echoaudio *chip) +{ + chip->comm_page->handshake = 0; +} + +static inline u32 get_dsp_register(struct echoaudio *chip, u32 index) +{ + return readl(&chip->dsp_registers[index]); +} + +static inline void set_dsp_register(struct echoaudio *chip, u32 index, + u32 value) +{ + writel(value, &chip->dsp_registers[index]); +} + + +/* Pipe and bus indexes. PX_* and BX_* are defined as chip->px_* and chip->bx_* +for 3G cards because they depend on the external box. They are integer +constants for all other cards. +Never use those defines directly, use the following functions instead. */ + +static inline int px_digital_out(const struct echoaudio *chip) +{ + return PX_DIGITAL_OUT; +} + +static inline int px_analog_in(const struct echoaudio *chip) +{ + return PX_ANALOG_IN; +} + +static inline int px_digital_in(const struct echoaudio *chip) +{ + return PX_DIGITAL_IN; +} + +static inline int px_num(const struct echoaudio *chip) +{ + return PX_NUM; +} + +static inline int bx_digital_out(const struct echoaudio *chip) +{ + return BX_DIGITAL_OUT; +} + +static inline int bx_analog_in(const struct echoaudio *chip) +{ + return BX_ANALOG_IN; +} + +static inline int bx_digital_in(const struct echoaudio *chip) +{ + return BX_DIGITAL_IN; +} + +static inline int bx_num(const struct echoaudio *chip) +{ + return BX_NUM; +} + +static inline int num_pipes_out(const struct echoaudio *chip) +{ + return px_analog_in(chip); +} + +static inline int num_pipes_in(const struct echoaudio *chip) +{ + return px_num(chip) - px_analog_in(chip); +} + +static inline int num_busses_out(const struct echoaudio *chip) +{ + return bx_analog_in(chip); +} + +static inline int num_busses_in(const struct echoaudio *chip) +{ + return bx_num(chip) - bx_analog_in(chip); +} + +static inline int num_analog_busses_out(const struct echoaudio *chip) +{ + return bx_digital_out(chip); +} + +static inline int num_analog_busses_in(const struct echoaudio *chip) +{ + return bx_digital_in(chip) - bx_analog_in(chip); +} + +static inline int num_digital_busses_out(const struct echoaudio *chip) +{ + return num_busses_out(chip) - num_analog_busses_out(chip); +} + +static inline int num_digital_busses_in(const struct echoaudio *chip) +{ + return num_busses_in(chip) - num_analog_busses_in(chip); +} + +/* The monitor array is a one-dimensional array; compute the offset + * into the array */ +static inline int monitor_index(const struct echoaudio *chip, int out, int in) +{ + return out * num_busses_in(chip) + in; +} + + +#ifndef pci_device +#define pci_device(chip) (&chip->pci->dev) +#endif + + +#endif /* _ECHOAUDIO_H_ */ diff --git a/sound/pci/echoaudio/echoaudio_3g.c b/sound/pci/echoaudio/echoaudio_3g.c new file mode 100644 index 00000000000..9f439ea459f --- /dev/null +++ b/sound/pci/echoaudio/echoaudio_3g.c @@ -0,0 +1,431 @@ +/**************************************************************************** + + Copyright Echo Digital Audio Corporation (c) 1998 - 2004 + All rights reserved + www.echoaudio.com + + This file is part of Echo Digital Audio's generic driver library. + + Echo Digital Audio's generic driver library is free software; + you can redistribute it and/or modify it under the terms of + the GNU General Public License as published by the Free Software + Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + ************************************************************************* + + Translation from C++ and adaptation for use in ALSA-Driver + were made by Giuliano Pochini <pochini@shiny.it> + +****************************************************************************/ + + + +/* These functions are common for all "3G" cards */ + + +static int check_asic_status(struct echoaudio *chip) +{ + u32 box_status; + + if (wait_handshake(chip)) + return -EIO; + + chip->comm_page->ext_box_status = + __constant_cpu_to_le32(E3G_ASIC_NOT_LOADED); + chip->asic_loaded = FALSE; + clear_handshake(chip); + send_vector(chip, DSP_VC_TEST_ASIC); + + if (wait_handshake(chip)) { + chip->dsp_code = NULL; + return -EIO; + } + + box_status = le32_to_cpu(chip->comm_page->ext_box_status); + DE_INIT(("box_status=%x\n", box_status)); + if (box_status == E3G_ASIC_NOT_LOADED) + return -ENODEV; + + chip->asic_loaded = TRUE; + return box_status & E3G_BOX_TYPE_MASK; +} + + + +static inline u32 get_frq_reg(struct echoaudio *chip) +{ + return le32_to_cpu(chip->comm_page->e3g_frq_register); +} + + + +/* Most configuration of 3G cards is accomplished by writing the control +register. write_control_reg sends the new control register value to the DSP. */ +static int write_control_reg(struct echoaudio *chip, u32 ctl, u32 frq, + char force) +{ + if (wait_handshake(chip)) + return -EIO; + + DE_ACT(("WriteControlReg: Setting 0x%x, 0x%x\n", ctl, frq)); + + ctl = cpu_to_le32(ctl); + frq = cpu_to_le32(frq); + + if (ctl != chip->comm_page->control_register || + frq != chip->comm_page->e3g_frq_register || force) { + chip->comm_page->e3g_frq_register = frq; + chip->comm_page->control_register = ctl; + clear_handshake(chip); + return send_vector(chip, DSP_VC_WRITE_CONTROL_REG); + } + + DE_ACT(("WriteControlReg: not written, no change\n")); + return 0; +} + + + +/* Set the digital mode - currently for Gina24, Layla24, Mona, 3G */ +static int set_digital_mode(struct echoaudio *chip, u8 mode) +{ + u8 previous_mode; + int err, i, o; + + /* All audio channels must be closed before changing the digital mode */ + snd_assert(!chip->pipe_alloc_mask, return -EAGAIN); + + snd_assert(chip->digital_modes & (1 << mode), return -EINVAL); + + previous_mode = chip->digital_mode; + err = dsp_set_digital_mode(chip, mode); + + /* If we successfully changed the digital mode from or to ADAT, + * then make sure all output, input and monitor levels are + * updated by the DSP comm object. */ + if (err >= 0 && previous_mode != mode && + (previous_mode == DIGITAL_MODE_ADAT || mode == DIGITAL_MODE_ADAT)) { + spin_lock_irq(&chip->lock); + for (o = 0; o < num_busses_out(chip); o++) + for (i = 0; i < num_busses_in(chip); i++) + set_monitor_gain(chip, o, i, + chip->monitor_gain[o][i]); + +#ifdef ECHOCARD_HAS_INPUT_GAIN + for (i = 0; i < num_busses_in(chip); i++) + set_input_gain(chip, i, chip->input_gain[i]); + update_input_line_level(chip); +#endif + + for (o = 0; o < num_busses_out(chip); o++) + set_output_gain(chip, o, chip->output_gain[o]); + update_output_line_level(chip); + spin_unlock_irq(&chip->lock); + } + + return err; +} + + + +static u32 set_spdif_bits(struct echoaudio *chip, u32 control_reg, u32 rate) +{ + control_reg &= E3G_SPDIF_FORMAT_CLEAR_MASK; + + switch (rate) { + case 32000 : + control_reg |= E3G_SPDIF_SAMPLE_RATE0 | E3G_SPDIF_SAMPLE_RATE1; + break; + case 44100 : + if (chip->professional_spdif) + control_reg |= E3G_SPDIF_SAMPLE_RATE0; + break; + case 48000 : + control_reg |= E3G_SPDIF_SAMPLE_RATE1; + break; + } + + if (chip->professional_spdif) + control_reg |= E3G_SPDIF_PRO_MODE; + + if (chip->non_audio_spdif) + control_reg |= E3G_SPDIF_NOT_AUDIO; + + control_reg |= E3G_SPDIF_24_BIT | E3G_SPDIF_TWO_CHANNEL | + E3G_SPDIF_COPY_PERMIT; + + return control_reg; +} + + + +/* Set the S/PDIF output format */ +static int set_professional_spdif(struct echoaudio *chip, char prof) +{ + u32 control_reg; + + control_reg = le32_to_cpu(chip->comm_page->control_register); + chip->professional_spdif = prof; + control_reg = set_spdif_bits(chip, control_reg, chip->sample_rate); + return write_control_reg(chip, control_reg, get_frq_reg(chip), 0); +} + + + +/* detect_input_clocks() returns a bitmask consisting of all the input clocks +currently connected to the hardware; this changes as the user connects and +disconnects clock inputs. You should use this information to determine which +clocks the user is allowed to select. */ +static u32 detect_input_clocks(const struct echoaudio *chip) +{ + u32 clocks_from_dsp, clock_bits; + + /* Map the DSP clock detect bits to the generic driver clock + * detect bits */ + clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks); + + clock_bits = ECHO_CLOCK_BIT_INTERNAL; + + if (clocks_from_dsp & E3G_CLOCK_DETECT_BIT_WORD) + clock_bits |= ECHO_CLOCK_BIT_WORD; + + switch(chip->digital_mode) { + case DIGITAL_MODE_SPDIF_RCA: + case DIGITAL_MODE_SPDIF_OPTICAL: + if (clocks_from_dsp & E3G_CLOCK_DETECT_BIT_SPDIF) + clock_bits |= ECHO_CLOCK_BIT_SPDIF; + break; + case DIGITAL_MODE_ADAT: + if (clocks_from_dsp & E3G_CLOCK_DETECT_BIT_ADAT) + clock_bits |= ECHO_CLOCK_BIT_ADAT; + break; + } + + return clock_bits; +} + + + +static int load_asic(struct echoaudio *chip) +{ + int box_type, err; + + if (chip->asic_loaded) + return 0; + + /* Give the DSP a few milliseconds to settle down */ + mdelay(2); + + err = load_asic_generic(chip, DSP_FNC_LOAD_3G_ASIC, + &card_fw[FW_3G_ASIC]); + if (err < 0) + return err; + + chip->asic_code = &card_fw[FW_3G_ASIC]; + + /* Now give the new ASIC a little time to set up */ + mdelay(2); + /* See if it worked */ + box_type = check_asic_status(chip); + + /* Set up the control register if the load succeeded - + * 48 kHz, internal clock, S/PDIF RCA mode */ + if (box_type >= 0) { + err = write_control_reg(chip, E3G_48KHZ, + E3G_FREQ_REG_DEFAULT, TRUE); + if (err < 0) + return err; + } + + return box_type; +} + + + +static int set_sample_rate(struct echoaudio *chip, u32 rate) +{ + u32 control_reg, clock, base_rate, frq_reg; + + /* Only set the clock for internal mode. */ + if (chip->input_clock != ECHO_CLOCK_INTERNAL) { + DE_ACT(("set_sample_rate: Cannot set sample rate - " + "clock not set to CLK_CLOCKININTERNAL\n")); + /* Save the rate anyhow */ + chip->comm_page->sample_rate = cpu_to_le32(rate); + chip->sample_rate = rate; + set_input_clock(chip, chip->input_clock); + return 0; + } + + snd_assert(rate < 50000 || chip->digital_mode != DIGITAL_MODE_ADAT, + return -EINVAL); + + clock = 0; + control_reg = le32_to_cpu(chip->comm_page->control_register); + control_reg &= E3G_CLOCK_CLEAR_MASK; + + switch (rate) { + case 96000: + clock = E3G_96KHZ; + break; + case 88200: + clock = E3G_88KHZ; + break; + case 48000: + clock = E3G_48KHZ; + break; + case 44100: + clock = E3G_44KHZ; + break; + case 32000: + clock = E3G_32KHZ; + break; + default: + clock = E3G_CONTINUOUS_CLOCK; + if (rate > 50000) + clock |= E3G_DOUBLE_SPEED_MODE; + break; + } + + control_reg |= clock; + control_reg = set_spdif_bits(chip, control_reg, rate); + + base_rate = rate; + if (base_rate > 50000) + base_rate /= 2; + if (base_rate < 32000) + base_rate = 32000; + + frq_reg = E3G_MAGIC_NUMBER / base_rate - 2; + if (frq_reg > E3G_FREQ_REG_MAX) + frq_reg = E3G_FREQ_REG_MAX; + + chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP */ + chip->sample_rate = rate; + DE_ACT(("SetSampleRate: %d clock %x\n", rate, control_reg)); + + /* Tell the DSP about it - DSP reads both control reg & freq reg */ + return write_control_reg(chip, control_reg, frq_reg, 0); +} + + + +/* Set the sample clock source to internal, S/PDIF, ADAT */ +static int set_input_clock(struct echoaudio *chip, u16 clock) +{ + u32 control_reg, clocks_from_dsp; + + DE_ACT(("set_input_clock:\n")); + + /* Mask off the clock select bits */ + control_reg = le32_to_cpu(chip->comm_page->control_register) & + E3G_CLOCK_CLEAR_MASK; + clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks); + + switch (clock) { + case ECHO_CLOCK_INTERNAL: + DE_ACT(("Set Echo3G clock to INTERNAL\n")); + chip->input_clock = ECHO_CLOCK_INTERNAL; + return set_sample_rate(chip, chip->sample_rate); + case ECHO_CLOCK_SPDIF: + if (chip->digital_mode == DIGITAL_MODE_ADAT) + return -EAGAIN; + DE_ACT(("Set Echo3G clock to SPDIF\n")); + control_reg |= E3G_SPDIF_CLOCK; + if (clocks_from_dsp & E3G_CLOCK_DETECT_BIT_SPDIF96) + control_reg |= E3G_DOUBLE_SPEED_MODE; + else + control_reg &= ~E3G_DOUBLE_SPEED_MODE; + break; + case ECHO_CLOCK_ADAT: + if (chip->digital_mode != DIGITAL_MODE_ADAT) + return -EAGAIN; + DE_ACT(("Set Echo3G clock to ADAT\n")); + control_reg |= E3G_ADAT_CLOCK; + control_reg &= ~E3G_DOUBLE_SPEED_MODE; + break; + case ECHO_CLOCK_WORD: + DE_ACT(("Set Echo3G clock to WORD\n")); + control_reg |= E3G_WORD_CLOCK; + if (clocks_from_dsp & E3G_CLOCK_DETECT_BIT_WORD96) + control_reg |= E3G_DOUBLE_SPEED_MODE; + else + control_reg &= ~E3G_DOUBLE_SPEED_MODE; + break; + default: + DE_ACT(("Input clock 0x%x not supported for Echo3G\n", clock)); + return -EINVAL; + } + + chip->input_clock = clock; + return write_control_reg(chip, control_reg, get_frq_reg(chip), 1); +} + + + +static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode) +{ + u32 control_reg; + int err, incompatible_clock; + + /* Set clock to "internal" if it's not compatible with the new mode */ + incompatible_clock = FALSE; + switch (mode) { + case DIGITAL_MODE_SPDIF_OPTICAL: + case DIGITAL_MODE_SPDIF_RCA: + if (chip->input_clock == ECHO_CLOCK_ADAT) + incompatible_clock = TRUE; + break; + case DIGITAL_MODE_ADAT: + if (chip->input_clock == ECHO_CLOCK_SPDIF) + incompatible_clock = TRUE; + break; + default: + DE_ACT(("Digital mode not supported: %d\n", mode)); + return -EINVAL; + } + + spin_lock_irq(&chip->lock); + + if (incompatible_clock) { + chip->sample_rate = 48000; + set_input_clock(chip, ECHO_CLOCK_INTERNAL); + } + + /* Clear the current digital mode */ + control_reg = le32_to_cpu(chip->comm_page->control_register); + control_reg &= E3G_DIGITAL_MODE_CLEAR_MASK; + + /* Tweak the control reg */ + switch (mode) { + case DIGITAL_MODE_SPDIF_OPTICAL: + control_reg |= E3G_SPDIF_OPTICAL_MODE; + break; + case DIGITAL_MODE_SPDIF_RCA: + /* E3G_SPDIF_OPTICAL_MODE bit cleared */ + break; + case DIGITAL_MODE_ADAT: + control_reg |= E3G_ADAT_MODE; + control_reg &= ~E3G_DOUBLE_SPEED_MODE; /* @@ useless */ + break; + } + + err = write_control_reg(chip, control_reg, get_frq_reg(chip), 1); + spin_unlock_irq(&chip->lock); + if (err < 0) + return err; + chip->digital_mode = mode; + + DE_ACT(("set_digital_mode(%d)\n", chip->digital_mode)); + return incompatible_clock; +} diff --git a/sound/pci/echoaudio/echoaudio_dsp.c b/sound/pci/echoaudio/echoaudio_dsp.c new file mode 100644 index 00000000000..42afa837d9b --- /dev/null +++ b/sound/pci/echoaudio/echoaudio_dsp.c @@ -0,0 +1,1125 @@ +/**************************************************************************** + + Copyright Echo Digital Audio Corporation (c) 1998 - 2004 + All rights reserved + www.echoaudio.com + + This file is part of Echo Digital Audio's generic driver library. + + Echo Digital Audio's generic driver library is free software; + you can redistribute it and/or modify it under the terms of + the GNU General Public License as published by the Free Software + Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + ************************************************************************* + + Translation from C++ and adaptation for use in ALSA-Driver + were made by Giuliano Pochini <pochini@shiny.it> + +****************************************************************************/ + +#if PAGE_SIZE < 4096 +#error PAGE_SIZE is < 4k +#endif + +static int restore_dsp_rettings(struct echoaudio *chip); + + +/* Some vector commands involve the DSP reading or writing data to and from the +comm page; if you send one of these commands to the DSP, it will complete the +command and then write a non-zero value to the Handshake field in the +comm page. This function waits for the handshake to show up. */ +static int wait_handshake(struct echoaudio *chip) +{ + int i; + + /* Wait up to 10ms for the handshake from the DSP */ + for (i = 0; i < HANDSHAKE_TIMEOUT; i++) { + /* Look for the handshake value */ + if (chip->comm_page->handshake) { + /*if (i) DE_ACT(("Handshake time: %d\n", i));*/ + return 0; + } + udelay(1); + } + + snd_printk(KERN_ERR "wait_handshake(): Timeout waiting for DSP\n"); + return -EBUSY; +} + + + +/* Much of the interaction between the DSP and the driver is done via vector +commands; send_vector writes a vector command to the DSP. Typically, this +causes the DSP to read or write fields in the comm page. +PCI posting is not required thanks to the handshake logic. */ +static int send_vector(struct echoaudio *chip, u32 command) +{ + int i; + + wmb(); /* Flush all pending writes before sending the command */ + + /* Wait up to 100ms for the "vector busy" bit to be off */ + for (i = 0; i < VECTOR_BUSY_TIMEOUT; i++) { + if (!(get_dsp_register(chip, CHI32_VECTOR_REG) & + CHI32_VECTOR_BUSY)) { + set_dsp_register(chip, CHI32_VECTOR_REG, command); + /*if (i) DE_ACT(("send_vector time: %d\n", i));*/ + return 0; + } + udelay(1); + } + + DE_ACT((KERN_ERR "timeout on send_vector\n")); + return -EBUSY; +} + + + +/* write_dsp writes a 32-bit value to the DSP; this is used almost +exclusively for loading the DSP. */ +static int write_dsp(struct echoaudio *chip, u32 data) +{ + u32 status, i; + + for (i = 0; i < 10000000; i++) { /* timeout = 10s */ + status = get_dsp_register(chip, CHI32_STATUS_REG); + if ((status & CHI32_STATUS_HOST_WRITE_EMPTY) != 0) { + set_dsp_register(chip, CHI32_DATA_REG, data); + wmb(); /* write it immediately */ + return 0; + } + udelay(1); + cond_resched(); + } + + chip->bad_board = TRUE; /* Set TRUE until DSP re-loaded */ + DE_ACT((KERN_ERR "write_dsp: Set bad_board to TRUE\n")); + return -EIO; +} + + + +/* read_dsp reads a 32-bit value from the DSP; this is used almost +exclusively for loading the DSP and checking the status of the ASIC. */ +static int read_dsp(struct echoaudio *chip, u32 *data) +{ + u32 status, i; + + for (i = 0; i < READ_DSP_TIMEOUT; i++) { + status = get_dsp_register(chip, CHI32_STATUS_REG); + if ((status & CHI32_STATUS_HOST_READ_FULL) != 0) { + *data = get_dsp_register(chip, CHI32_DATA_REG); + return 0; + } + udelay(1); + cond_resched(); + } + + chip->bad_board = TRUE; /* Set TRUE until DSP re-loaded */ + DE_INIT((KERN_ERR "read_dsp: Set bad_board to TRUE\n")); + return -EIO; +} + + + +/**************************************************************************** + Firmware loading functions + ****************************************************************************/ + +/* This function is used to read back the serial number from the DSP; +this is triggered by the SET_COMMPAGE_ADDR command. +Only some early Echogals products have serial numbers in the ROM; +the serial number is not used, but you still need to do this as +part of the DSP load process. */ +static int read_sn(struct echoaudio *chip) +{ + int i; + u32 sn[6]; + + for (i = 0; i < 5; i++) { + if (read_dsp(chip, &sn[i])) { + snd_printk(KERN_ERR "Failed to read serial number\n"); + return -EIO; + } + } + DE_INIT(("Read serial number %08x %08x %08x %08x %08x\n", + sn[0], sn[1], sn[2], sn[3], sn[4])); + return 0; +} + + + +#ifndef ECHOCARD_HAS_ASIC +/* This card has no ASIC, just return ok */ +static inline int check_asic_status(struct echoaudio *chip) +{ + chip->asic_loaded = TRUE; + return 0; +} + +#endif /* !ECHOCARD_HAS_ASIC */ + + + +#ifdef ECHOCARD_HAS_ASIC + +/* Load ASIC code - done after the DSP is loaded */ +static int load_asic_generic(struct echoaudio *chip, u32 cmd, + const struct firmware *asic) +{ + const struct firmware *fw; + int err; + u32 i, size; + u8 *code; + + if ((err = get_firmware(&fw, asic, chip)) < 0) { + snd_printk(KERN_WARNING "Firmware not found !\n"); + return err; + } + + code = (u8 *)fw->data; + size = fw->size; + + /* Send the "Here comes the ASIC" command */ + if (write_dsp(chip, cmd) < 0) + goto la_error; + + /* Write length of ASIC file in bytes */ + if (write_dsp(chip, size) < 0) + goto la_error; + + for (i = 0; i < size; i++) { + if (write_dsp(chip, code[i]) < 0) + goto la_error; + } + + DE_INIT(("ASIC loaded\n")); + free_firmware(fw); + return 0; + +la_error: + DE_INIT(("failed on write_dsp\n")); + free_firmware(fw); + return -EIO; +} + +#endif /* ECHOCARD_HAS_ASIC */ + + + +#ifdef DSP_56361 + +/* Install the resident loader for 56361 DSPs; The resident loader is on +the EPROM on the board for 56301 DSP. The resident loader is a tiny little +program that is used to load the real DSP code. */ +static int install_resident_loader(struct echoaudio *chip) +{ + u32 address; + int index, words, i; + u16 *code; + u32 status; + const struct firmware *fw; + + /* 56361 cards only! This check is required by the old 56301-based + Mona and Gina24 */ + if (chip->device_id != DEVICE_ID_56361) + return 0; + + /* Look to see if the resident loader is present. If the resident + loader is already installed, host flag 5 will be on. */ + status = get_dsp_register(chip, CHI32_STATUS_REG); + if (status & CHI32_STATUS_REG_HF5) { + DE_INIT(("Resident loader already installed; status is 0x%x\n", + status)); + return 0; + } + + if ((i = get_firmware(&fw, &card_fw[FW_361_LOADER], chip)) < 0) { + snd_printk(KERN_WARNING "Firmware not found !\n"); + return i; + } + + /* The DSP code is an array of 16 bit words. The array is divided up + into sections. The first word of each section is the size in words, + followed by the section type. + Since DSP addresses and data are 24 bits wide, they each take up two + 16 bit words in the array. + This is a lot like the other loader loop, but it's not a loop, you + don't write the memory type, and you don't write a zero at the end. */ + + /* Set DSP format bits for 24 bit mode */ + set_dsp_register(chip, CHI32_CONTROL_REG, + get_dsp_register(chip, CHI32_CONTROL_REG) | 0x900); + + code = (u16 *)fw->data; + + /* Skip the header section; the first word in the array is the size + of the first section, so the first real section of code is pointed + to by Code[0]. */ + index = code[0]; + + /* Skip the section size, LRS block type, and DSP memory type */ + index += 3; + + /* Get the number of DSP words to write */ + words = code[index++]; + + /* Get the DSP address for this block; 24 bits, so build from two words */ + address = ((u32)code[index] << 16) + code[index + 1]; + index += 2; + + /* Write the count to the DSP */ + if (write_dsp(chip, words)) { + DE_INIT(("install_resident_loader: Failed to write word count!\n")); + goto irl_error; + } + /* Write the DSP address */ + if (write_dsp(chip, address)) { + DE_INIT(("install_resident_loader: Failed to write DSP address!\n")); + goto irl_error; + } + /* Write out this block of code to the DSP */ + for (i = 0; i < words; i++) { + u32 data; + + data = ((u32)code[index] << 16) + code[index + 1]; + if (write_dsp(chip, data)) { + DE_INIT(("install_resident_loader: Failed to write DSP code\n")); + goto irl_error; + } + index += 2; + } + + /* Wait for flag 5 to come up */ + for (i = 0; i < 200; i++) { /* Timeout is 50us * 200 = 10ms */ + udelay(50); + status = get_dsp_register(chip, CHI32_STATUS_REG); + if (status & CHI32_STATUS_REG_HF5) + break; + } + + if (i == 200) { + DE_INIT(("Resident loader failed to set HF5\n")); + goto irl_error; + } + + DE_INIT(("Resident loader successfully installed\n")); + free_firmware(fw); + return 0; + +irl_error: + free_firmware(fw); + return -EIO; +} + +#endif /* DSP_56361 */ + + +static int load_dsp(struct echoaudio *chip, u16 *code) +{ + u32 address, data; + int index, words, i; + + if (chip->dsp_code == code) { + DE_INIT(("DSP is already loaded!\n")); + return 0; + } + chip->bad_board = TRUE; /* Set TRUE until DSP loaded */ + chip->dsp_code = NULL; /* Current DSP code not loaded */ + chip->asic_loaded = FALSE; /* Loading the DSP code will reset the ASIC */ + + DE_INIT(("load_dsp: Set bad_board to TRUE\n")); + + /* If this board requires a resident loader, install it. */ +#ifdef DSP_56361 + if ((i = install_resident_loader(chip)) < 0) + return i; +#endif + + /* Send software reset command */ + if (send_vector(chip, DSP_VC_RESET) < 0) { + DE_INIT(("LoadDsp: send_vector DSP_VC_RESET failed, Critical Failure\n")); + return -EIO; + } + /* Delay 10us */ + udelay(10); + + /* Wait 10ms for HF3 to indicate that software reset is complete */ + for (i = 0; i < 1000; i++) { /* Timeout is 10us * 1000 = 10ms */ + if (get_dsp_register(chip, CHI32_STATUS_REG) & + CHI32_STATUS_REG_HF3) + break; + udelay(10); + } + + if (i == 1000) { + DE_INIT(("load_dsp: Timeout waiting for CHI32_STATUS_REG_HF3\n")); + return -EIO; + } + + /* Set DSP format bits for 24 bit mode now that soft reset is done */ + set_dsp_register(chip, CHI32_CONTROL_REG, + get_dsp_register(chip, CHI32_CONTROL_REG) | 0x900); + + /* Main loader loop */ + + index = code[0]; + for (;;) { + int block_type, mem_type; + + /* Total Block Size */ + index++; + + /* Block Type */ + block_type = code[index]; + if (block_type == 4) /* We're finished */ + break; + + index++; + + /* Memory Type P=0,X=1,Y=2 */ + mem_type = code[index++]; + + /* Block Code Size */ + words = code[index++]; + if (words == 0) /* We're finished */ + break; + + /* Start Address */ + address = ((u32)code[index] << 16) + code[index + 1]; + index += 2; + + if (write_dsp(chip, words) < 0) { + DE_INIT(("load_dsp: failed to write number of DSP words\n")); + return -EIO; + } + if (write_dsp(chip, address) < 0) { + DE_INIT(("load_dsp: failed to write DSP address\n")); + return -EIO; + } + if (write_dsp(chip, mem_type) < 0) { + DE_INIT(("load_dsp: failed to write DSP memory type\n")); + return -EIO; + } + /* Code */ + for (i = 0; i < words; i++, index+=2) { + data = ((u32)code[index] << 16) + code[index + 1]; + if (write_dsp(chip, data) < 0) { + DE_INIT(("load_dsp: failed to write DSP data\n")); + return -EIO; + } + } + } + + if (write_dsp(chip, 0) < 0) { /* We're done!!! */ + DE_INIT(("load_dsp: Failed to write final zero\n")); + return -EIO; + } + udelay(10); + + for (i = 0; i < 5000; i++) { /* Timeout is 100us * 5000 = 500ms */ + /* Wait for flag 4 - indicates that the DSP loaded OK */ + if (get_dsp_register(chip, CHI32_STATUS_REG) & + CHI32_STATUS_REG_HF4) { + set_dsp_register(chip, CHI32_CONTROL_REG, + get_dsp_register(chip, CHI32_CONTROL_REG) & ~0x1b00); + + if (write_dsp(chip, DSP_FNC_SET_COMMPAGE_ADDR) < 0) { + DE_INIT(("load_dsp: Failed to write DSP_FNC_SET_COMMPAGE_ADDR\n")); + return -EIO; + } + + if (write_dsp(chip, chip->comm_page_phys) < 0) { + DE_INIT(("load_dsp: Failed to write comm page address\n")); + return -EIO; + } + + /* Get the serial number via slave mode. + This is triggered by the SET_COMMPAGE_ADDR command. + We don't actually use the serial number but we have to + get it as part of the DSP init voodoo. */ + if (read_sn(chip) < 0) { + DE_INIT(("load_dsp: Failed to read serial number\n")); + return -EIO; + } + + chip->dsp_code = code; /* Show which DSP code loaded */ + chip->bad_board = FALSE; /* DSP OK */ + DE_INIT(("load_dsp: OK!\n")); + return 0; + } + udelay(100); + } + + DE_INIT(("load_dsp: DSP load timed out waiting for HF4\n")); + return -EIO; +} + + + +/* load_firmware takes care of loading the DSP and any ASIC code. */ +static int load_firmware(struct echoaudio *chip) +{ + const struct firmware *fw; + int box_type, err; + + snd_assert(chip->dsp_code_to_load && chip->comm_page, return -EPERM); + + /* See if the ASIC is present and working - only if the DSP is already loaded */ + if (chip->dsp_code) { + if ((box_type = check_asic_status(chip)) >= 0) + return box_type; + /* ASIC check failed; force the DSP to reload */ + chip->dsp_code = NULL; + } + + if ((err = get_firmware(&fw, chip->dsp_code_to_load, chip)) < 0) + return err; + err = load_dsp(chip, (u16 *)fw->data); + free_firmware(fw); + if (err < 0) + return err; + + if ((box_type = load_asic(chip)) < 0) + return box_type; /* error */ + + if ((err = restore_dsp_rettings(chip)) < 0) + return err; + + return box_type; +} + + + +/**************************************************************************** + Mixer functions + ****************************************************************************/ + +#if defined(ECHOCARD_HAS_INPUT_NOMINAL_LEVEL) || \ + defined(ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL) + +/* Set the nominal level for an input or output bus (true = -10dBV, false = +4dBu) */ +static int set_nominal_level(struct echoaudio *chip, u16 index, char consumer) +{ + snd_assert(index < num_busses_out(chip) + num_busses_in(chip), + return -EINVAL); + + /* Wait for the handshake (OK even if ASIC is not loaded) */ + if (wait_handshake(chip)) + return -EIO; + + chip->nominal_level[index] = consumer; + + if (consumer) + chip->comm_page->nominal_level_mask |= cpu_to_le32(1 << index); + else + chip->comm_page->nominal_level_mask &= ~cpu_to_le32(1 << index); + + return 0; +} + +#endif /* ECHOCARD_HAS_*_NOMINAL_LEVEL */ + + + +/* Set the gain for a single physical output channel (dB). */ +static int set_output_gain(struct echoaudio *chip, u16 channel, s8 gain) +{ + snd_assert(channel < num_busses_out(chip), return -EINVAL); + + if (wait_handshake(chip)) + return -EIO; + + /* Save the new value */ + chip->output_gain[channel] = gain; + chip->comm_page->line_out_level[channel] = gain; + return 0; +} + + + +#ifdef ECHOCARD_HAS_MONITOR +/* Set the monitor level from an input bus to an output bus. */ +static int set_monitor_gain(struct echoaudio *chip, u16 output, u16 input, + s8 gain) +{ + snd_assert(output < num_busses_out(chip) && + input < num_busses_in(chip), return -EINVAL); + + if (wait_handshake(chip)) + return -EIO; + + chip->monitor_gain[output][input] = gain; + chip->comm_page->monitors[monitor_index(chip, output, input)] = gain; + return 0; +} +#endif /* ECHOCARD_HAS_MONITOR */ + + +/* Tell the DSP to read and update output, nominal & monitor levels in comm page. */ +static int update_output_line_level(struct echoaudio *chip) +{ + if (wait_handshake(chip)) + return -EIO; + clear_handshake(chip); + return send_vector(chip, DSP_VC_UPDATE_OUTVOL); +} + + + +/* Tell the DSP to read and update input levels in comm page */ +static int update_input_line_level(struct echoaudio *chip) +{ + if (wait_handshake(chip)) + return -EIO; + clear_handshake(chip); + return send_vector(chip, DSP_VC_UPDATE_INGAIN); +} + + + +/* set_meters_on turns the meters on or off. If meters are turned on, the DSP +will write the meter and clock detect values to the comm page at about 30Hz */ +static void set_meters_on(struct echoaudio *chip, char on) +{ + if (on && !chip->meters_enabled) { + send_vector(chip, DSP_VC_METERS_ON); + chip->meters_enabled = 1; + } else if (!on && chip->meters_enabled) { + send_vector(chip, DSP_VC_METERS_OFF); + chip->meters_enabled = 0; + memset((s8 *)chip->comm_page->vu_meter, ECHOGAIN_MUTED, + DSP_MAXPIPES); + memset((s8 *)chip->comm_page->peak_meter, ECHOGAIN_MUTED, + DSP_MAXPIPES); + } +} + + + +/* Fill out an the given array using the current values in the comm page. +Meters are written in the comm page by the DSP in this order: + Output busses + Input busses + Output pipes (vmixer cards only) + +This function assumes there are no more than 16 in/out busses or pipes +Meters is an array [3][16][2] of long. */ +static void get_audio_meters(struct echoaudio *chip, long *meters) +{ + int i, m, n; + + m = 0; + n = 0; + for (i = 0; i < num_busses_out(chip); i++, m++) { + meters[n++] = chip->comm_page->vu_meter[m]; + meters[n++] = chip->comm_page->peak_meter[m]; + } + for (; n < 32; n++) + meters[n] = 0; + +#ifdef ECHOCARD_ECHO3G + m = E3G_MAX_OUTPUTS; /* Skip unused meters */ +#endif + + for (i = 0; i < num_busses_in(chip); i++, m++) { + meters[n++] = chip->comm_page->vu_meter[m]; + meters[n++] = chip->comm_page->peak_meter[m]; + } + for (; n < 64; n++) + meters[n] = 0; + +#ifdef ECHOCARD_HAS_VMIXER + for (i = 0; i < num_pipes_out(chip); i++, m++) { + meters[n++] = chip->comm_page->vu_meter[m]; + meters[n++] = chip->comm_page->peak_meter[m]; + } +#endif + for (; n < 96; n++) + meters[n] = 0; +} + + + +static int restore_dsp_rettings(struct echoaudio *chip) +{ + int err; + DE_INIT(("restore_dsp_settings\n")); + + if ((err = check_asic_status(chip)) < 0) + return err; + + /* @ Gina20/Darla20 only. Should be harmless for other cards. */ + chip->comm_page->gd_clock_state = GD_CLOCK_UNDEF; + chip->comm_page->gd_spdif_status = GD_SPDIF_STATUS_UNDEF; + chip->comm_page->handshake = 0xffffffff; + + if ((err = set_sample_rate(chip, chip->sample_rate)) < 0) + return err; + + if (chip->meters_enabled) + if (send_vector(chip, DSP_VC_METERS_ON) < 0) + return -EIO; + +#ifdef ECHOCARD_HAS_EXTERNAL_CLOCK + if (set_input_clock(chip, chip->input_clock) < 0) + return -EIO; +#endif + +#ifdef ECHOCARD_HAS_OUTPUT_CLOCK_SWITCH + if (set_output_clock(chip, chip->output_clock) < 0) + return -EIO; +#endif + + if (update_output_line_level(chip) < 0) + return -EIO; + + if (update_input_line_level(chip) < 0) + return -EIO; + +#ifdef ECHOCARD_HAS_VMIXER + if (update_vmixer_level(chip) < 0) + return -EIO; +#endif + + if (wait_handshake(chip) < 0) + return -EIO; + clear_handshake(chip); + + DE_INIT(("restore_dsp_rettings done\n")); + return send_vector(chip, DSP_VC_UPDATE_FLAGS); +} + + + +/**************************************************************************** + Transport functions + ****************************************************************************/ + +/* set_audio_format() sets the format of the audio data in host memory for +this pipe. Note that _MS_ (mono-to-stereo) playback modes are not used by ALSA +but they are here because they are just mono while capturing */ +static void set_audio_format(struct echoaudio *chip, u16 pipe_index, + const struct audioformat *format) +{ + u16 dsp_format; + + dsp_format = DSP_AUDIOFORM_SS_16LE; + + /* Look for super-interleave (no big-endian and 8 bits) */ + if (format->interleave > 2) { + switch (format->bits_per_sample) { + case 16: + dsp_format = DSP_AUDIOFORM_SUPER_INTERLEAVE_16LE; + break; + case 24: + dsp_format = DSP_AUDIOFORM_SUPER_INTERLEAVE_24LE; + break; + case 32: + dsp_format = DSP_AUDIOFORM_SUPER_INTERLEAVE_32LE; + break; + } + dsp_format |= format->interleave; + } else if (format->data_are_bigendian) { + /* For big-endian data, only 32 bit samples are supported */ + switch (format->interleave) { + case 1: + dsp_format = DSP_AUDIOFORM_MM_32BE; + break; +#ifdef ECHOCARD_HAS_STEREO_BIG_ENDIAN32 + case 2: + dsp_format = DSP_AUDIOFORM_SS_32BE; + break; +#endif + } + } else if (format->interleave == 1 && + format->bits_per_sample == 32 && !format->mono_to_stereo) { + /* 32 bit little-endian mono->mono case */ + dsp_format = DSP_AUDIOFORM_MM_32LE; + } else { + /* Handle the other little-endian formats */ + switch (format->bits_per_sample) { + case 8: + if (format->interleave == 2) + dsp_format = DSP_AUDIOFORM_SS_8; + else + dsp_format = DSP_AUDIOFORM_MS_8; + break; + default: + case 16: + if (format->interleave == 2) + dsp_format = DSP_AUDIOFORM_SS_16LE; + else + dsp_format = DSP_AUDIOFORM_MS_16LE; + break; + case 24: + if (format->interleave == 2) + dsp_format = DSP_AUDIOFORM_SS_24LE; + else + dsp_format = DSP_AUDIOFORM_MS_24LE; + break; + case 32: + if (format->interleave == 2) + dsp_format = DSP_AUDIOFORM_SS_32LE; + else + dsp_format = DSP_AUDIOFORM_MS_32LE; + break; + } + } + DE_ACT(("set_audio_format[%d] = %x\n", pipe_index, dsp_format)); + chip->comm_page->audio_format[pipe_index] = cpu_to_le16(dsp_format); +} + + + +/* start_transport starts transport for a set of pipes. +The bits 1 in channel_mask specify what pipes to start. Only the bit of the +first channel must be set, regardless its interleave. +Same thing for pause_ and stop_ -trasport below. */ +static int start_transport(struct echoaudio *chip, u32 channel_mask, + u32 cyclic_mask) +{ + DE_ACT(("start_transport %x\n", channel_mask)); + + if (wait_handshake(chip)) + return -EIO; + + chip->comm_page->cmd_start |= cpu_to_le32(channel_mask); + + if (chip->comm_page->cmd_start) { + clear_handshake(chip); + send_vector(chip, DSP_VC_START_TRANSFER); + if (wait_handshake(chip)) + return -EIO; + /* Keep track of which pipes are transporting */ + chip->active_mask |= channel_mask; + chip->comm_page->cmd_start = 0; + return 0; + } + + DE_ACT(("start_transport: No pipes to start!\n")); + return -EINVAL; +} + + + +static int pause_transport(struct echoaudio *chip, u32 channel_mask) +{ + DE_ACT(("pause_transport %x\n", channel_mask)); + + if (wait_handshake(chip)) + return -EIO; + + chip->comm_page->cmd_stop |= cpu_to_le32(channel_mask); + chip->comm_page->cmd_reset = 0; + if (chip->comm_page->cmd_stop) { + clear_handshake(chip); + send_vector(chip, DSP_VC_STOP_TRANSFER); + if (wait_handshake(chip)) + return -EIO; + /* Keep track of which pipes are transporting */ + chip->active_mask &= ~channel_mask; + chip->comm_page->cmd_stop = 0; + chip->comm_page->cmd_reset = 0; + return 0; + } + + DE_ACT(("pause_transport: No pipes to stop!\n")); + return 0; +} + + + +static int stop_transport(struct echoaudio *chip, u32 channel_mask) +{ + DE_ACT(("stop_transport %x\n", channel_mask)); + + if (wait_handshake(chip)) + return -EIO; + + chip->comm_page->cmd_stop |= cpu_to_le32(channel_mask); + chip->comm_page->cmd_reset |= cpu_to_le32(channel_mask); + if (chip->comm_page->cmd_reset) { + clear_handshake(chip); + send_vector(chip, DSP_VC_STOP_TRANSFER); + if (wait_handshake(chip)) + return -EIO; + /* Keep track of which pipes are transporting */ + chip->active_mask &= ~channel_mask; + chip->comm_page->cmd_stop = 0; + chip->comm_page->cmd_reset = 0; + return 0; + } + + DE_ACT(("stop_transport: No pipes to stop!\n")); + return 0; +} + + + +static inline int is_pipe_allocated(struct echoaudio *chip, u16 pipe_index) +{ + return (chip->pipe_alloc_mask & (1 << pipe_index)); +} + + + +/* Stops everything and turns off the DSP. All pipes should be already +stopped and unallocated. */ +static int rest_in_peace(struct echoaudio *chip) +{ + DE_ACT(("rest_in_peace() open=%x\n", chip->pipe_alloc_mask)); + + /* Stops all active pipes (just to be sure) */ + stop_transport(chip, chip->active_mask); + + set_meters_on(chip, FALSE); + +#ifdef ECHOCARD_HAS_MIDI + enable_midi_input(chip, FALSE); +#endif + + /* Go to sleep */ + if (chip->dsp_code) { + /* Make load_firmware do a complete reload */ + chip->dsp_code = NULL; + /* Put the DSP to sleep */ + return send_vector(chip, DSP_VC_GO_COMATOSE); + } + return 0; +} + + + +/* Fills the comm page with default values */ +static int init_dsp_comm_page(struct echoaudio *chip) +{ + /* Check if the compiler added extra padding inside the structure */ + if (offsetof(struct comm_page, midi_output) != 0xbe0) { + DE_INIT(("init_dsp_comm_page() - Invalid struct comm_page structure\n")); + return -EPERM; + } + + /* Init all the basic stuff */ + chip->card_name = ECHOCARD_NAME; + chip->bad_board = TRUE; /* Set TRUE until DSP loaded */ + chip->dsp_code = NULL; /* Current DSP code not loaded */ + chip->digital_mode = DIGITAL_MODE_NONE; + chip->input_clock = ECHO_CLOCK_INTERNAL; + chip->output_clock = ECHO_CLOCK_WORD; + chip->asic_loaded = FALSE; + memset(chip->comm_page, 0, sizeof(struct comm_page)); + + /* Init the comm page */ + chip->comm_page->comm_size = + __constant_cpu_to_le32(sizeof(struct comm_page)); + chip->comm_page->handshake = 0xffffffff; + chip->comm_page->midi_out_free_count = + __constant_cpu_to_le32(DSP_MIDI_OUT_FIFO_SIZE); + chip->comm_page->sample_rate = __constant_cpu_to_le32(44100); + chip->sample_rate = 44100; + + /* Set line levels so we don't blast any inputs on startup */ + memset(chip->comm_page->monitors, ECHOGAIN_MUTED, MONITOR_ARRAY_SIZE); + memset(chip->comm_page->vmixer, ECHOGAIN_MUTED, VMIXER_ARRAY_SIZE); + + return 0; +} + + + +/* This function initializes the several volume controls for busses and pipes. +This MUST be called after the DSP is up and running ! */ +static int init_line_levels(struct echoaudio *chip) +{ + int st, i, o; + + DE_INIT(("init_line_levels\n")); + + /* Mute output busses */ + for (i = 0; i < num_busses_out(chip); i++) + if ((st = set_output_gain(chip, i, ECHOGAIN_MUTED))) + return st; + if ((st = update_output_line_level(chip))) + return st; + +#ifdef ECHOCARD_HAS_VMIXER + /* Mute the Vmixer */ + for (i = 0; i < num_pipes_out(chip); i++) + for (o = 0; o < num_busses_out(chip); o++) + if ((st = set_vmixer_gain(chip, o, i, ECHOGAIN_MUTED))) + return st; + if ((st = update_vmixer_level(chip))) + return st; +#endif /* ECHOCARD_HAS_VMIXER */ + +#ifdef ECHOCARD_HAS_MONITOR + /* Mute the monitor mixer */ + for (o = 0; o < num_busses_out(chip); o++) + for (i = 0; i < num_busses_in(chip); i++) + if ((st = set_monitor_gain(chip, o, i, ECHOGAIN_MUTED))) + return st; + if ((st = update_output_line_level(chip))) + return st; +#endif /* ECHOCARD_HAS_MONITOR */ + +#ifdef ECHOCARD_HAS_INPUT_GAIN + for (i = 0; i < num_busses_in(chip); i++) + if ((st = set_input_gain(chip, i, ECHOGAIN_MUTED))) + return st; + if ((st = update_input_line_level(chip))) + return st; +#endif /* ECHOCARD_HAS_INPUT_GAIN */ + + return 0; +} + + + +/* This is low level part of the interrupt handler. +It returns -1 if the IRQ is not ours, or N>=0 if it is, where N is the number +of midi data in the input queue. */ +static int service_irq(struct echoaudio *chip) +{ + int st; + + /* Read the DSP status register and see if this DSP generated this interrupt */ + if (get_dsp_register(chip, CHI32_STATUS_REG) & CHI32_STATUS_IRQ) { + st = 0; +#ifdef ECHOCARD_HAS_MIDI + /* Get and parse midi data if present */ + if (chip->comm_page->midi_input[0]) /* The count is at index 0 */ + st = midi_service_irq(chip); /* Returns how many midi bytes we received */ +#endif + /* Clear the hardware interrupt */ + chip->comm_page->midi_input[0] = 0; + send_vector(chip, DSP_VC_ACK_INT); + return st; + } + return -1; +} + + + + +/****************************************************************************** + Functions for opening and closing pipes + ******************************************************************************/ + +/* allocate_pipes is used to reserve audio pipes for your exclusive use. +The call will fail if some pipes are already allocated. */ +static int allocate_pipes(struct echoaudio *chip, struct audiopipe *pipe, + int pipe_index, int interleave) +{ + int i; + u32 channel_mask; + char is_cyclic; + + DE_ACT(("allocate_pipes: ch=%d int=%d\n", pipe_index, interleave)); + + if (chip->bad_board) + return -EIO; + + is_cyclic = 1; /* This driver uses cyclic buffers only */ + + for (channel_mask = i = 0; i < interleave; i++) + channel_mask |= 1 << (pipe_index + i); + if (chip->pipe_alloc_mask & channel_mask) { + DE_ACT(("allocate_pipes: channel already open\n")); + return -EAGAIN; + } + + chip->comm_page->position[pipe_index] = 0; + chip->pipe_alloc_mask |= channel_mask; + if (is_cyclic) + chip->pipe_cyclic_mask |= channel_mask; + pipe->index = pipe_index; + pipe->interleave = interleave; + pipe->state = PIPE_STATE_STOPPED; + + /* The counter register is where the DSP writes the 32 bit DMA + position for a pipe. The DSP is constantly updating this value as + it moves data. The DMA counter is in units of bytes, not samples. */ + pipe->dma_counter = &chip->comm_page->position[pipe_index]; + *pipe->dma_counter = 0; + DE_ACT(("allocate_pipes: ok\n")); + return pipe_index; +} + + + +static int free_pipes(struct echoaudio *chip, struct audiopipe *pipe) +{ + u32 channel_mask; + int i; + + DE_ACT(("free_pipes: Pipe %d\n", pipe->index)); + snd_assert(is_pipe_allocated(chip, pipe->index), return -EINVAL); + snd_assert(pipe->state == PIPE_STATE_STOPPED, return -EINVAL); + + for (channel_mask = i = 0; i < pipe->interleave; i++) + channel_mask |= 1 << (pipe->index + i); + + chip->pipe_alloc_mask &= ~channel_mask; + chip->pipe_cyclic_mask &= ~channel_mask; + return 0; +} + + + +/****************************************************************************** + Functions for managing the scatter-gather list +******************************************************************************/ + +static int sglist_init(struct echoaudio *chip, struct audiopipe *pipe) +{ + pipe->sglist_head = 0; + memset(pipe->sgpage.area, 0, PAGE_SIZE); + chip->comm_page->sglist_addr[pipe->index].addr = + cpu_to_le32(pipe->sgpage.addr); + return 0; +} + + + +static int sglist_add_mapping(struct echoaudio *chip, struct audiopipe *pipe, + dma_addr_t address, size_t length) +{ + int head = pipe->sglist_head; + struct sg_entry *list = (struct sg_entry *)pipe->sgpage.area; + + if (head < MAX_SGLIST_ENTRIES - 1) { + list[head].addr = cpu_to_le32(address); + list[head].size = cpu_to_le32(length); + pipe->sglist_head++; + } else { + DE_ACT(("SGlist: too many fragments\n")); + return -ENOMEM; + } + return 0; +} + + + +static inline int sglist_add_irq(struct echoaudio *chip, struct audiopipe *pipe) +{ + return sglist_add_mapping(chip, pipe, 0, 0); +} + + + +static inline int sglist_wrap(struct echoaudio *chip, struct audiopipe *pipe) +{ + return sglist_add_mapping(chip, pipe, pipe->sgpage.addr, 0); +} diff --git a/sound/pci/echoaudio/echoaudio_dsp.h b/sound/pci/echoaudio/echoaudio_dsp.h new file mode 100644 index 00000000000..e55ee00991a --- /dev/null +++ b/sound/pci/echoaudio/echoaudio_dsp.h @@ -0,0 +1,694 @@ +/**************************************************************************** + + Copyright Echo Digital Audio Corporation (c) 1998 - 2004 + All rights reserved + www.echoaudio.com + + This file is part of Echo Digital Audio's generic driver library. + + Echo Digital Audio's generic driver library is free software; + you can redistribute it and/or modify it under the terms of + the GNU General Public License as published by the Free Software + Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + ************************************************************************* + + Translation from C++ and adaptation for use in ALSA-Driver + were made by Giuliano Pochini <pochini@shiny.it> + +****************************************************************************/ + +#ifndef _ECHO_DSP_ +#define _ECHO_DSP_ + + +/**** Echogals: Darla20, Gina20, Layla20, and Darla24 ****/ +#if defined(ECHOGALS_FAMILY) + +#define NUM_ASIC_TESTS 5 +#define READ_DSP_TIMEOUT 1000000L /* one second */ + +/**** Echo24: Gina24, Layla24, Mona, Mia, Mia-midi ****/ +#elif defined(ECHO24_FAMILY) + +#define DSP_56361 /* Some Echo24 cards use the 56361 DSP */ +#define READ_DSP_TIMEOUT 100000L /* .1 second */ + +/**** 3G: Gina3G, Layla3G ****/ +#elif defined(ECHO3G_FAMILY) + +#define DSP_56361 +#define READ_DSP_TIMEOUT 100000L /* .1 second */ +#define MIN_MTC_1X_RATE 32000 + +/**** Indigo: Indigo, Indigo IO, Indigo DJ ****/ +#elif defined(INDIGO_FAMILY) + +#define DSP_56361 +#define READ_DSP_TIMEOUT 100000L /* .1 second */ + +#else + +#error No family is defined + +#endif + + + +/* + * + * Max inputs and outputs + * + */ + +#define DSP_MAXAUDIOINPUTS 16 /* Max audio input channels */ +#define DSP_MAXAUDIOOUTPUTS 16 /* Max audio output channels */ +#define DSP_MAXPIPES 32 /* Max total pipes (input + output) */ + + +/* + * + * These are the offsets for the memory-mapped DSP registers; the DSP base + * address is treated as the start of a u32 array. + */ + +#define CHI32_CONTROL_REG 4 +#define CHI32_STATUS_REG 5 +#define CHI32_VECTOR_REG 6 +#define CHI32_DATA_REG 7 + + +/* + * + * Interesting bits within the DSP registers + * + */ + +#define CHI32_VECTOR_BUSY 0x00000001 +#define CHI32_STATUS_REG_HF3 0x00000008 +#define CHI32_STATUS_REG_HF4 0x00000010 +#define CHI32_STATUS_REG_HF5 0x00000020 +#define CHI32_STATUS_HOST_READ_FULL 0x00000004 +#define CHI32_STATUS_HOST_WRITE_EMPTY 0x00000002 +#define CHI32_STATUS_IRQ 0x00000040 + + +/* + * + * DSP commands sent via slave mode; these are sent to the DSP by write_dsp() + * + */ + +#define DSP_FNC_SET_COMMPAGE_ADDR 0x02 +#define DSP_FNC_LOAD_LAYLA_ASIC 0xa0 +#define DSP_FNC_LOAD_GINA24_ASIC 0xa0 +#define DSP_FNC_LOAD_MONA_PCI_CARD_ASIC 0xa0 +#define DSP_FNC_LOAD_LAYLA24_PCI_CARD_ASIC 0xa0 +#define DSP_FNC_LOAD_MONA_EXTERNAL_ASIC 0xa1 +#define DSP_FNC_LOAD_LAYLA24_EXTERNAL_ASIC 0xa1 +#define DSP_FNC_LOAD_3G_ASIC 0xa0 + + +/* + * + * Defines to handle the MIDI input state engine; these are used to properly + * extract MIDI time code bytes and their timestamps from the MIDI input stream. + * + */ + +#define MIDI_IN_STATE_NORMAL 0 +#define MIDI_IN_STATE_TS_HIGH 1 +#define MIDI_IN_STATE_TS_LOW 2 +#define MIDI_IN_STATE_F1_DATA 3 +#define MIDI_IN_SKIP_DATA (-1) + + +/*---------------------------------------------------------------------------- + +Setting the sample rates on Layla24 is somewhat schizophrenic. + +For standard rates, it works exactly like Mona and Gina24. That is, for +8, 11.025, 16, 22.05, 32, 44.1, 48, 88.2, and 96 kHz, you just set the +appropriate bits in the control register and write the control register. + +In order to support MIDI time code sync (and possibly SMPTE LTC sync in +the future), Layla24 also has "continuous sample rate mode". In this mode, +Layla24 can generate any sample rate between 25 and 50 kHz inclusive, or +50 to 100 kHz inclusive for double speed mode. + +To use continuous mode: + +-Set the clock select bits in the control register to 0xe (see the #define + below) + +-Set double-speed mode if you want to use sample rates above 50 kHz + +-Write the control register as you would normally + +-Now, you need to set the frequency register. First, you need to determine the + value for the frequency register. This is given by the following formula: + +frequency_reg = (LAYLA24_MAGIC_NUMBER / sample_rate) - 2 + +Note the #define below for the magic number + +-Wait for the DSP handshake +-Write the frequency_reg value to the .SampleRate field of the comm page +-Send the vector command SET_LAYLA24_FREQUENCY_REG (see vmonkey.h) + +Once you have set the control register up for continuous mode, you can just +write the frequency register to change the sample rate. This could be +used for MIDI time code sync. For MTC sync, the control register is set for +continuous mode. The driver then just keeps writing the +SET_LAYLA24_FREQUENCY_REG command. + +-----------------------------------------------------------------------------*/ + +#define LAYLA24_MAGIC_NUMBER 677376000 +#define LAYLA24_CONTINUOUS_CLOCK 0x000e + + +/* + * + * DSP vector commands + * + */ + +#define DSP_VC_RESET 0x80ff + +#ifndef DSP_56361 + +#define DSP_VC_ACK_INT 0x8073 +#define DSP_VC_SET_VMIXER_GAIN 0x0000 /* Not used, only for compile */ +#define DSP_VC_START_TRANSFER 0x0075 /* Handshke rqd. */ +#define DSP_VC_METERS_ON 0x0079 +#define DSP_VC_METERS_OFF 0x007b +#define DSP_VC_UPDATE_OUTVOL 0x007d /* Handshke rqd. */ +#define DSP_VC_UPDATE_INGAIN 0x007f /* Handshke rqd. */ +#define DSP_VC_ADD_AUDIO_BUFFER 0x0081 /* Handshke rqd. */ +#define DSP_VC_TEST_ASIC 0x00eb +#define DSP_VC_UPDATE_CLOCKS 0x00ef /* Handshke rqd. */ +#define DSP_VC_SET_LAYLA_SAMPLE_RATE 0x00f1 /* Handshke rqd. */ +#define DSP_VC_SET_GD_AUDIO_STATE 0x00f1 /* Handshke rqd. */ +#define DSP_VC_WRITE_CONTROL_REG 0x00f1 /* Handshke rqd. */ +#define DSP_VC_MIDI_WRITE 0x00f5 /* Handshke rqd. */ +#define DSP_VC_STOP_TRANSFER 0x00f7 /* Handshke rqd. */ +#define DSP_VC_UPDATE_FLAGS 0x00fd /* Handshke rqd. */ +#define DSP_VC_GO_COMATOSE 0x00f9 + +#else /* !DSP_56361 */ + +/* Vector commands for families that use either the 56301 or 56361 */ +#define DSP_VC_ACK_INT 0x80F5 +#define DSP_VC_SET_VMIXER_GAIN 0x00DB /* Handshke rqd. */ +#define DSP_VC_START_TRANSFER 0x00DD /* Handshke rqd. */ +#define DSP_VC_METERS_ON 0x00EF +#define DSP_VC_METERS_OFF 0x00F1 +#define DSP_VC_UPDATE_OUTVOL 0x00E3 /* Handshke rqd. */ +#define DSP_VC_UPDATE_INGAIN 0x00E5 /* Handshke rqd. */ +#define DSP_VC_ADD_AUDIO_BUFFER 0x00E1 /* Handshke rqd. */ +#define DSP_VC_TEST_ASIC 0x00ED +#define DSP_VC_UPDATE_CLOCKS 0x00E9 /* Handshke rqd. */ +#define DSP_VC_SET_LAYLA24_FREQUENCY_REG 0x00E9 /* Handshke rqd. */ +#define DSP_VC_SET_LAYLA_SAMPLE_RATE 0x00EB /* Handshke rqd. */ +#define DSP_VC_SET_GD_AUDIO_STATE 0x00EB /* Handshke rqd. */ +#define DSP_VC_WRITE_CONTROL_REG 0x00EB /* Handshke rqd. */ +#define DSP_VC_MIDI_WRITE 0x00E7 /* Handshke rqd. */ +#define DSP_VC_STOP_TRANSFER 0x00DF /* Handshke rqd. */ +#define DSP_VC_UPDATE_FLAGS 0x00FB /* Handshke rqd. */ +#define DSP_VC_GO_COMATOSE 0x00d9 + +#endif /* !DSP_56361 */ + + +/* + * + * Timeouts + * + */ + +#define HANDSHAKE_TIMEOUT 20000 /* send_vector command timeout (20ms) */ +#define VECTOR_BUSY_TIMEOUT 100000 /* 100ms */ +#define MIDI_OUT_DELAY_USEC 2000 /* How long to wait after MIDI fills up */ + + +/* + * + * Flags for .Flags field in the comm page + * + */ + +#define DSP_FLAG_MIDI_INPUT 0x0001 /* Enable MIDI input */ +#define DSP_FLAG_SPDIF_NONAUDIO 0x0002 /* Sets the "non-audio" bit + * in the S/PDIF out status + * bits. Clear this flag for + * audio data; + * set it for AC3 or WMA or + * some such */ +#define DSP_FLAG_PROFESSIONAL_SPDIF 0x0008 /* 1 Professional, 0 Consumer */ + + +/* + * + * Clock detect bits reported by the DSP for Gina20, Layla20, Darla24, and Mia + * + */ + +#define GLDM_CLOCK_DETECT_BIT_WORD 0x0002 +#define GLDM_CLOCK_DETECT_BIT_SUPER 0x0004 +#define GLDM_CLOCK_DETECT_BIT_SPDIF 0x0008 +#define GLDM_CLOCK_DETECT_BIT_ESYNC 0x0010 + + +/* + * + * Clock detect bits reported by the DSP for Gina24, Mona, and Layla24 + * + */ + +#define GML_CLOCK_DETECT_BIT_WORD96 0x0002 +#define GML_CLOCK_DETECT_BIT_WORD48 0x0004 +#define GML_CLOCK_DETECT_BIT_SPDIF48 0x0008 +#define GML_CLOCK_DETECT_BIT_SPDIF96 0x0010 +#define GML_CLOCK_DETECT_BIT_WORD (GML_CLOCK_DETECT_BIT_WORD96 | GML_CLOCK_DETECT_BIT_WORD48) +#define GML_CLOCK_DETECT_BIT_SPDIF (GML_CLOCK_DETECT_BIT_SPDIF48 | GML_CLOCK_DETECT_BIT_SPDIF96) +#define GML_CLOCK_DETECT_BIT_ESYNC 0x0020 +#define GML_CLOCK_DETECT_BIT_ADAT 0x0040 + + +/* + * + * Layla clock numbers to send to DSP + * + */ + +#define LAYLA20_CLOCK_INTERNAL 0 +#define LAYLA20_CLOCK_SPDIF 1 +#define LAYLA20_CLOCK_WORD 2 +#define LAYLA20_CLOCK_SUPER 3 + + +/* + * + * Gina/Darla clock states + * + */ + +#define GD_CLOCK_NOCHANGE 0 +#define GD_CLOCK_44 1 +#define GD_CLOCK_48 2 +#define GD_CLOCK_SPDIFIN 3 +#define GD_CLOCK_UNDEF 0xff + + +/* + * + * Gina/Darla S/PDIF status bits + * + */ + +#define GD_SPDIF_STATUS_NOCHANGE 0 +#define GD_SPDIF_STATUS_44 1 +#define GD_SPDIF_STATUS_48 2 +#define GD_SPDIF_STATUS_UNDEF 0xff + + +/* + * + * Layla20 output clocks + * + */ + +#define LAYLA20_OUTPUT_CLOCK_SUPER 0 +#define LAYLA20_OUTPUT_CLOCK_WORD 1 + + +/**************************************************************************** + + Magic constants for the Darla24 hardware + + ****************************************************************************/ + +#define GD24_96000 0x0 +#define GD24_48000 0x1 +#define GD24_44100 0x2 +#define GD24_32000 0x3 +#define GD24_22050 0x4 +#define GD24_16000 0x5 +#define GD24_11025 0x6 +#define GD24_8000 0x7 +#define GD24_88200 0x8 +#define GD24_EXT_SYNC 0x9 + + +/* + * + * Return values from the DSP when ASIC is loaded + * + */ + +#define ASIC_ALREADY_LOADED 0x1 +#define ASIC_NOT_LOADED 0x0 + + +/* + * + * DSP Audio formats + * + * These are the audio formats that the DSP can transfer + * via input and output pipes. LE means little-endian, + * BE means big-endian. + * + * DSP_AUDIOFORM_MS_8 + * + * 8-bit mono unsigned samples. For playback, + * mono data is duplicated out the left and right channels + * of the output bus. The "MS" part of the name + * means mono->stereo. + * + * DSP_AUDIOFORM_MS_16LE + * + * 16-bit signed little-endian mono samples. Playback works + * like the previous code. + * + * DSP_AUDIOFORM_MS_24LE + * + * 24-bit signed little-endian mono samples. Data is packed + * three bytes per sample; if you had two samples 0x112233 and 0x445566 + * they would be stored in memory like this: 33 22 11 66 55 44. + * + * DSP_AUDIOFORM_MS_32LE + * + * 24-bit signed little-endian mono samples in a 32-bit + * container. In other words, each sample is a 32-bit signed + * integer, where the actual audio data is left-justified + * in the 32 bits and only the 24 most significant bits are valid. + * + * DSP_AUDIOFORM_SS_8 + * DSP_AUDIOFORM_SS_16LE + * DSP_AUDIOFORM_SS_24LE + * DSP_AUDIOFORM_SS_32LE + * + * Like the previous ones, except now with stereo interleaved + * data. "SS" means stereo->stereo. + * + * DSP_AUDIOFORM_MM_32LE + * + * Similar to DSP_AUDIOFORM_MS_32LE, except that the mono + * data is not duplicated out both the left and right outputs. + * This mode is used by the ASIO driver. Here, "MM" means + * mono->mono. + * + * DSP_AUDIOFORM_MM_32BE + * + * Just like DSP_AUDIOFORM_MM_32LE, but now the data is + * in big-endian format. + * + */ + +#define DSP_AUDIOFORM_MS_8 0 /* 8 bit mono */ +#define DSP_AUDIOFORM_MS_16LE 1 /* 16 bit mono */ +#define DSP_AUDIOFORM_MS_24LE 2 /* 24 bit mono */ +#define DSP_AUDIOFORM_MS_32LE 3 /* 32 bit mono */ +#define DSP_AUDIOFORM_SS_8 4 /* 8 bit stereo */ +#define DSP_AUDIOFORM_SS_16LE 5 /* 16 bit stereo */ +#define DSP_AUDIOFORM_SS_24LE 6 /* 24 bit stereo */ +#define DSP_AUDIOFORM_SS_32LE 7 /* 32 bit stereo */ +#define DSP_AUDIOFORM_MM_32LE 8 /* 32 bit mono->mono little-endian */ +#define DSP_AUDIOFORM_MM_32BE 9 /* 32 bit mono->mono big-endian */ +#define DSP_AUDIOFORM_SS_32BE 10 /* 32 bit stereo big endian */ +#define DSP_AUDIOFORM_INVALID 0xFF /* Invalid audio format */ + + +/* + * + * Super-interleave is defined as interleaving by 4 or more. Darla20 and Gina20 + * do not support super interleave. + * + * 16 bit, 24 bit, and 32 bit little endian samples are supported for super + * interleave. The interleave factor must be even. 16 - way interleave is the + * current maximum, so you can interleave by 4, 6, 8, 10, 12, 14, and 16. + * + * The actual format code is derived by taking the define below and or-ing with + * the interleave factor. So, 32 bit interleave by 6 is 0x86 and + * 16 bit interleave by 16 is (0x40 | 0x10) = 0x50. + * + */ + +#define DSP_AUDIOFORM_SUPER_INTERLEAVE_16LE 0x40 +#define DSP_AUDIOFORM_SUPER_INTERLEAVE_24LE 0xc0 +#define DSP_AUDIOFORM_SUPER_INTERLEAVE_32LE 0x80 + + +/* + * + * Gina24, Mona, and Layla24 control register defines + * + */ + +#define GML_CONVERTER_ENABLE 0x0010 +#define GML_SPDIF_PRO_MODE 0x0020 /* Professional S/PDIF == 1, + consumer == 0 */ +#define GML_SPDIF_SAMPLE_RATE0 0x0040 +#define GML_SPDIF_SAMPLE_RATE1 0x0080 +#define GML_SPDIF_TWO_CHANNEL 0x0100 /* 1 == two channels, + 0 == one channel */ +#define GML_SPDIF_NOT_AUDIO 0x0200 +#define GML_SPDIF_COPY_PERMIT 0x0400 +#define GML_SPDIF_24_BIT 0x0800 /* 1 == 24 bit, 0 == 20 bit */ +#define GML_ADAT_MODE 0x1000 /* 1 == ADAT mode, 0 == S/PDIF mode */ +#define GML_SPDIF_OPTICAL_MODE 0x2000 /* 1 == optical mode, 0 == RCA mode */ +#define GML_SPDIF_CDROM_MODE 0x3000 /* 1 == CDROM mode, + * 0 == RCA or optical mode */ +#define GML_DOUBLE_SPEED_MODE 0x4000 /* 1 == double speed, + 0 == single speed */ + +#define GML_DIGITAL_IN_AUTO_MUTE 0x800000 + +#define GML_96KHZ (0x0 | GML_DOUBLE_SPEED_MODE) +#define GML_88KHZ (0x1 | GML_DOUBLE_SPEED_MODE) +#define GML_48KHZ 0x2 +#define GML_44KHZ 0x3 +#define GML_32KHZ 0x4 +#define GML_22KHZ 0x5 +#define GML_16KHZ 0x6 +#define GML_11KHZ 0x7 +#define GML_8KHZ 0x8 +#define GML_SPDIF_CLOCK 0x9 +#define GML_ADAT_CLOCK 0xA +#define GML_WORD_CLOCK 0xB +#define GML_ESYNC_CLOCK 0xC +#define GML_ESYNCx2_CLOCK 0xD + +#define GML_CLOCK_CLEAR_MASK 0xffffbff0 +#define GML_SPDIF_RATE_CLEAR_MASK (~(GML_SPDIF_SAMPLE_RATE0|GML_SPDIF_SAMPLE_RATE1)) +#define GML_DIGITAL_MODE_CLEAR_MASK 0xffffcfff +#define GML_SPDIF_FORMAT_CLEAR_MASK 0xfffff01f + + +/* + * + * Mia sample rate and clock setting constants + * + */ + +#define MIA_32000 0x0040 +#define MIA_44100 0x0042 +#define MIA_48000 0x0041 +#define MIA_88200 0x0142 +#define MIA_96000 0x0141 + +#define MIA_SPDIF 0x00000044 +#define MIA_SPDIF96 0x00000144 + +#define MIA_MIDI_REV 1 /* Must be Mia rev 1 for MIDI support */ + + +/* + * + * 3G register bits + * + */ + +#define E3G_CONVERTER_ENABLE 0x0010 +#define E3G_SPDIF_PRO_MODE 0x0020 /* Professional S/PDIF == 1, + consumer == 0 */ +#define E3G_SPDIF_SAMPLE_RATE0 0x0040 +#define E3G_SPDIF_SAMPLE_RATE1 0x0080 +#define E3G_SPDIF_TWO_CHANNEL 0x0100 /* 1 == two channels, + 0 == one channel */ +#define E3G_SPDIF_NOT_AUDIO 0x0200 +#define E3G_SPDIF_COPY_PERMIT 0x0400 +#define E3G_SPDIF_24_BIT 0x0800 /* 1 == 24 bit, 0 == 20 bit */ +#define E3G_DOUBLE_SPEED_MODE 0x4000 /* 1 == double speed, + 0 == single speed */ +#define E3G_PHANTOM_POWER 0x8000 /* 1 == phantom power on, + 0 == phantom power off */ + +#define E3G_96KHZ (0x0 | E3G_DOUBLE_SPEED_MODE) +#define E3G_88KHZ (0x1 | E3G_DOUBLE_SPEED_MODE) +#define E3G_48KHZ 0x2 +#define E3G_44KHZ 0x3 +#define E3G_32KHZ 0x4 +#define E3G_22KHZ 0x5 +#define E3G_16KHZ 0x6 +#define E3G_11KHZ 0x7 +#define E3G_8KHZ 0x8 +#define E3G_SPDIF_CLOCK 0x9 +#define E3G_ADAT_CLOCK 0xA +#define E3G_WORD_CLOCK 0xB +#define E3G_CONTINUOUS_CLOCK 0xE + +#define E3G_ADAT_MODE 0x1000 +#define E3G_SPDIF_OPTICAL_MODE 0x2000 + +#define E3G_CLOCK_CLEAR_MASK 0xbfffbff0 +#define E3G_DIGITAL_MODE_CLEAR_MASK 0xffffcfff +#define E3G_SPDIF_FORMAT_CLEAR_MASK 0xfffff01f + +/* Clock detect bits reported by the DSP */ +#define E3G_CLOCK_DETECT_BIT_WORD96 0x0001 +#define E3G_CLOCK_DETECT_BIT_WORD48 0x0002 +#define E3G_CLOCK_DETECT_BIT_SPDIF48 0x0004 +#define E3G_CLOCK_DETECT_BIT_ADAT 0x0004 +#define E3G_CLOCK_DETECT_BIT_SPDIF96 0x0008 +#define E3G_CLOCK_DETECT_BIT_WORD (E3G_CLOCK_DETECT_BIT_WORD96|E3G_CLOCK_DETECT_BIT_WORD48) +#define E3G_CLOCK_DETECT_BIT_SPDIF (E3G_CLOCK_DETECT_BIT_SPDIF48|E3G_CLOCK_DETECT_BIT_SPDIF96) + +/* Frequency control register */ +#define E3G_MAGIC_NUMBER 677376000 +#define E3G_FREQ_REG_DEFAULT (E3G_MAGIC_NUMBER / 48000 - 2) +#define E3G_FREQ_REG_MAX 0xffff + +/* 3G external box types */ +#define E3G_GINA3G_BOX_TYPE 0x00 +#define E3G_LAYLA3G_BOX_TYPE 0x10 +#define E3G_ASIC_NOT_LOADED 0xffff +#define E3G_BOX_TYPE_MASK 0xf0 + +#define EXT_3GBOX_NC 0x01 +#define EXT_3GBOX_NOT_SET 0x02 + + +/* + * + * Gina20 & Layla20 have input gain controls for the analog inputs; + * this is the magic number for the hardware that gives you 0 dB at -10. + * + */ + +#define GL20_INPUT_GAIN_MAGIC_NUMBER 0xC8 + + +/* + * + * Defines how much time must pass between DSP load attempts + * + */ + +#define DSP_LOAD_ATTEMPT_PERIOD 1000000L /* One second */ + + +/* + * + * Size of arrays for the comm page. MAX_PLAY_TAPS and MAX_REC_TAPS are + * no longer used, but the sizes must still be right for the DSP to see + * the comm page correctly. + * + */ + +#define MONITOR_ARRAY_SIZE 0x180 +#define VMIXER_ARRAY_SIZE 0x40 +#define MIDI_OUT_BUFFER_SIZE 32 +#define MIDI_IN_BUFFER_SIZE 256 +#define MAX_PLAY_TAPS 168 +#define MAX_REC_TAPS 192 +#define DSP_MIDI_OUT_FIFO_SIZE 64 + + +/* sg_entry is a single entry for the scatter-gather list. The array of struct +sg_entry struct is read by the DSP, so all values must be little-endian. */ + +#define MAX_SGLIST_ENTRIES 512 + +struct sg_entry { + u32 addr; + u32 size; +}; + + +/**************************************************************************** + + The comm page. This structure is read and written by the DSP; the + DSP code is a firm believer in the byte offsets written in the comments + at the end of each line. This structure should not be changed. + + Any reads from or writes to this structure should be in little-endian format. + + ****************************************************************************/ + +struct comm_page { /* Base Length*/ + u32 comm_size; /* size of this object 0x000 4 */ + u32 flags; /* See Appendix A below 0x004 4 */ + u32 unused; /* Unused entry 0x008 4 */ + u32 sample_rate; /* Card sample rate in Hz 0x00c 4 */ + volatile u32 handshake; /* DSP command handshake 0x010 4 */ + u32 cmd_start; /* Chs. to start mask 0x014 4 */ + u32 cmd_stop; /* Chs. to stop mask 0x018 4 */ + u32 cmd_reset; /* Chs. to reset mask 0x01c 4 */ + u16 audio_format[DSP_MAXPIPES]; /* Chs. audio format 0x020 32*2 */ + struct sg_entry sglist_addr[DSP_MAXPIPES]; + /* Chs. Physical sglist addrs 0x060 32*8 */ + volatile u32 position[DSP_MAXPIPES]; + /* Positions for ea. ch. 0x160 32*4 */ + volatile s8 vu_meter[DSP_MAXPIPES]; + /* VU meters 0x1e0 32*1 */ + volatile s8 peak_meter[DSP_MAXPIPES]; + /* Peak meters 0x200 32*1 */ + s8 line_out_level[DSP_MAXAUDIOOUTPUTS]; + /* Output gain 0x220 16*1 */ + s8 line_in_level[DSP_MAXAUDIOINPUTS]; + /* Input gain 0x230 16*1 */ + s8 monitors[MONITOR_ARRAY_SIZE]; + /* Monitor map 0x240 0x180 */ + u32 play_coeff[MAX_PLAY_TAPS]; + /* Gina/Darla play filters - obsolete 0x3c0 168*4 */ + u32 rec_coeff[MAX_REC_TAPS]; + /* Gina/Darla record filters - obsolete 0x660 192*4 */ + volatile u16 midi_input[MIDI_IN_BUFFER_SIZE]; + /* MIDI input data transfer buffer 0x960 256*2 */ + u8 gd_clock_state; /* Chg Gina/Darla clock state 0xb60 1 */ + u8 gd_spdif_status; /* Chg. Gina/Darla S/PDIF state 0xb61 1 */ + u8 gd_resampler_state; /* Should always be 3 0xb62 1 */ + u8 filler2; /* 0xb63 1 */ + u32 nominal_level_mask; /* -10 level enable mask 0xb64 4 */ + u16 input_clock; /* Chg. Input clock state 0xb68 2 */ + u16 output_clock; /* Chg. Output clock state 0xb6a 2 */ + volatile u32 status_clocks; + /* Current Input clock state 0xb6c 4 */ + u32 ext_box_status; /* External box status 0xb70 4 */ + u32 cmd_add_buffer; /* Pipes to add (obsolete) 0xb74 4 */ + volatile u32 midi_out_free_count; + /* # of bytes free in MIDI output FIFO 0xb78 4 */ + u32 unused2; /* Cyclic pipes 0xb7c 4 */ + u32 control_register; + /* Mona, Gina24, Layla24, 3G ctrl reg 0xb80 4 */ + u32 e3g_frq_register; /* 3G frequency register 0xb84 4 */ + u8 filler[24]; /* filler 0xb88 24*1 */ + s8 vmixer[VMIXER_ARRAY_SIZE]; + /* Vmixer levels 0xba0 64*1 */ + u8 midi_output[MIDI_OUT_BUFFER_SIZE]; + /* MIDI output data 0xbe0 32*1 */ +}; + +#endif /* _ECHO_DSP_ */ diff --git a/sound/pci/echoaudio/echoaudio_gml.c b/sound/pci/echoaudio/echoaudio_gml.c new file mode 100644 index 00000000000..3aa37e76eba --- /dev/null +++ b/sound/pci/echoaudio/echoaudio_gml.c @@ -0,0 +1,198 @@ +/**************************************************************************** + + Copyright Echo Digital Audio Corporation (c) 1998 - 2004 + All rights reserved + www.echoaudio.com + + This file is part of Echo Digital Audio's generic driver library. + + Echo Digital Audio's generic driver library is free software; + you can redistribute it and/or modify it under the terms of + the GNU General Public License as published by the Free Software + Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + ************************************************************************* + + Translation from C++ and adaptation for use in ALSA-Driver + were made by Giuliano Pochini <pochini@shiny.it> + +****************************************************************************/ + + +/* These functions are common for Gina24, Layla24 and Mona cards */ + + +/* ASIC status check - some cards have one or two ASICs that need to be +loaded. Once that load is complete, this function is called to see if +the load was successful. +If this load fails, it does not necessarily mean that the hardware is +defective - the external box may be disconnected or turned off. */ +static int check_asic_status(struct echoaudio *chip) +{ + u32 asic_status; + + send_vector(chip, DSP_VC_TEST_ASIC); + + /* The DSP will return a value to indicate whether or not the + ASIC is currently loaded */ + if (read_dsp(chip, &asic_status) < 0) { + DE_INIT(("check_asic_status: failed on read_dsp\n")); + chip->asic_loaded = FALSE; + return -EIO; + } + + chip->asic_loaded = (asic_status == ASIC_ALREADY_LOADED); + return chip->asic_loaded ? 0 : -EIO; +} + + + +/* Most configuration of Gina24, Layla24, or Mona is accomplished by writing +the control register. write_control_reg sends the new control register +value to the DSP. */ +static int write_control_reg(struct echoaudio *chip, u32 value, char force) +{ + /* Handle the digital input auto-mute */ + if (chip->digital_in_automute) + value |= GML_DIGITAL_IN_AUTO_MUTE; + else + value &= ~GML_DIGITAL_IN_AUTO_MUTE; + + DE_ACT(("write_control_reg: 0x%x\n", value)); + + /* Write the control register */ + value = cpu_to_le32(value); + if (value != chip->comm_page->control_register || force) { + if (wait_handshake(chip)) + return -EIO; + chip->comm_page->control_register = value; + clear_handshake(chip); + return send_vector(chip, DSP_VC_WRITE_CONTROL_REG); + } + return 0; +} + + + +/* Gina24, Layla24, and Mona support digital input auto-mute. If the digital +input auto-mute is enabled, the DSP will only enable the digital inputs if +the card is syncing to a valid clock on the ADAT or S/PDIF inputs. +If the auto-mute is disabled, the digital inputs are enabled regardless of +what the input clock is set or what is connected. */ +static int set_input_auto_mute(struct echoaudio *chip, int automute) +{ + DE_ACT(("set_input_auto_mute %d\n", automute)); + + chip->digital_in_automute = automute; + + /* Re-set the input clock to the current value - indirectly causes + the auto-mute flag to be sent to the DSP */ + return set_input_clock(chip, chip->input_clock); +} + + + +/* S/PDIF coax / S/PDIF optical / ADAT - switch */ +static int set_digital_mode(struct echoaudio *chip, u8 mode) +{ + u8 previous_mode; + int err, i, o; + + if (chip->bad_board) + return -EIO; + + /* All audio channels must be closed before changing the digital mode */ + snd_assert(!chip->pipe_alloc_mask, return -EAGAIN); + + snd_assert(chip->digital_modes & (1 << mode), return -EINVAL); + + previous_mode = chip->digital_mode; + err = dsp_set_digital_mode(chip, mode); + + /* If we successfully changed the digital mode from or to ADAT, + then make sure all output, input and monitor levels are + updated by the DSP comm object. */ + if (err >= 0 && previous_mode != mode && + (previous_mode == DIGITAL_MODE_ADAT || mode == DIGITAL_MODE_ADAT)) { + spin_lock_irq(&chip->lock); + for (o = 0; o < num_busses_out(chip); o++) + for (i = 0; i < num_busses_in(chip); i++) + set_monitor_gain(chip, o, i, + chip->monitor_gain[o][i]); + +#ifdef ECHOCARD_HAS_INPUT_GAIN + for (i = 0; i < num_busses_in(chip); i++) + set_input_gain(chip, i, chip->input_gain[i]); + update_input_line_level(chip); +#endif + + for (o = 0; o < num_busses_out(chip); o++) + set_output_gain(chip, o, chip->output_gain[o]); + update_output_line_level(chip); + spin_unlock_irq(&chip->lock); + } + + return err; +} + + + +/* Set the S/PDIF output format */ +static int set_professional_spdif(struct echoaudio *chip, char prof) +{ + u32 control_reg; + int err; + + /* Clear the current S/PDIF flags */ + control_reg = le32_to_cpu(chip->comm_page->control_register); + control_reg &= GML_SPDIF_FORMAT_CLEAR_MASK; + + /* Set the new S/PDIF flags depending on the mode */ + control_reg |= GML_SPDIF_TWO_CHANNEL | GML_SPDIF_24_BIT | + GML_SPDIF_COPY_PERMIT; + if (prof) { + /* Professional mode */ + control_reg |= GML_SPDIF_PRO_MODE; + + switch (chip->sample_rate) { + case 32000: + control_reg |= GML_SPDIF_SAMPLE_RATE0 | + GML_SPDIF_SAMPLE_RATE1; + break; + case 44100: + control_reg |= GML_SPDIF_SAMPLE_RATE0; + break; + case 48000: + control_reg |= GML_SPDIF_SAMPLE_RATE1; + break; + } + } else { + /* Consumer mode */ + switch (chip->sample_rate) { + case 32000: + control_reg |= GML_SPDIF_SAMPLE_RATE0 | + GML_SPDIF_SAMPLE_RATE1; + break; + case 48000: + control_reg |= GML_SPDIF_SAMPLE_RATE1; + break; + } + } + + if ((err = write_control_reg(chip, control_reg, FALSE))) + return err; + chip->professional_spdif = prof; + DE_ACT(("set_professional_spdif to %s\n", + prof ? "Professional" : "Consumer")); + return 0; +} diff --git a/sound/pci/echoaudio/gina20.c b/sound/pci/echoaudio/gina20.c new file mode 100644 index 00000000000..29d6d12f80c --- /dev/null +++ b/sound/pci/echoaudio/gina20.c @@ -0,0 +1,103 @@ +/* + * ALSA driver for Echoaudio soundcards. + * Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define ECHOGALS_FAMILY +#define ECHOCARD_GINA20 +#define ECHOCARD_NAME "Gina20" +#define ECHOCARD_HAS_MONITOR +#define ECHOCARD_HAS_INPUT_GAIN +#define ECHOCARD_HAS_DIGITAL_IO +#define ECHOCARD_HAS_EXTERNAL_CLOCK +#define ECHOCARD_HAS_ADAT FALSE + +/* Pipe indexes */ +#define PX_ANALOG_OUT 0 /* 8 */ +#define PX_DIGITAL_OUT 8 /* 2 */ +#define PX_ANALOG_IN 10 /* 2 */ +#define PX_DIGITAL_IN 12 /* 2 */ +#define PX_NUM 14 + +/* Bus indexes */ +#define BX_ANALOG_OUT 0 /* 8 */ +#define BX_DIGITAL_OUT 8 /* 2 */ +#define BX_ANALOG_IN 10 /* 2 */ +#define BX_DIGITAL_IN 12 /* 2 */ +#define BX_NUM 14 + + +#include <sound/driver.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/slab.h> +#include <linux/moduleparam.h> +#include <linux/firmware.h> +#include <sound/core.h> +#include <sound/info.h> +#include <sound/control.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/asoundef.h> +#include <sound/initval.h> +#include <asm/io.h> +#include <asm/atomic.h> +#include "echoaudio.h" + +#define FW_GINA20_DSP 0 + +static const struct firmware card_fw[] = { + {0, "gina20_dsp.fw"} +}; + +static struct pci_device_id snd_echo_ids[] = { + {0x1057, 0x1801, 0xECC0, 0x0020, 0, 0, 0}, /* DSP 56301 Gina20 rev.0 */ + {0,} +}; + +static struct snd_pcm_hardware pcm_hardware_skel = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START, + .formats = SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S32_BE, + .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, + .rate_min = 44100, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = 262144, + .period_bytes_min = 32, + .period_bytes_max = 131072, + .periods_min = 2, + .periods_max = 220, + /* One page (4k) contains 512 instructions. I don't know if the hw + supports lists longer than this. In this case periods_max=220 is a + safe limit to make sure the list never exceeds 512 instructions. */ +}; + + +#include "gina20_dsp.c" +#include "echoaudio_dsp.c" +#include "echoaudio.c" diff --git a/sound/pci/echoaudio/gina20_dsp.c b/sound/pci/echoaudio/gina20_dsp.c new file mode 100644 index 00000000000..2757c896084 --- /dev/null +++ b/sound/pci/echoaudio/gina20_dsp.c @@ -0,0 +1,215 @@ +/**************************************************************************** + + Copyright Echo Digital Audio Corporation (c) 1998 - 2004 + All rights reserved + www.echoaudio.com + + This file is part of Echo Digital Audio's generic driver library. + + Echo Digital Audio's generic driver library is free software; + you can redistribute it and/or modify it under the terms of + the GNU General Public License as published by the Free Software + Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + ************************************************************************* + + Translation from C++ and adaptation for use in ALSA-Driver + were made by Giuliano Pochini <pochini@shiny.it> + +****************************************************************************/ + + +static int set_professional_spdif(struct echoaudio *chip, char prof); +static int update_flags(struct echoaudio *chip); + + +static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) +{ + int err; + + DE_INIT(("init_hw() - Gina20\n")); + snd_assert((subdevice_id & 0xfff0) == GINA20, return -ENODEV); + + if ((err = init_dsp_comm_page(chip))) { + DE_INIT(("init_hw - could not initialize DSP comm page\n")); + return err; + } + + chip->device_id = device_id; + chip->subdevice_id = subdevice_id; + chip->bad_board = TRUE; + chip->dsp_code_to_load = &card_fw[FW_GINA20_DSP]; + chip->spdif_status = GD_SPDIF_STATUS_UNDEF; + chip->clock_state = GD_CLOCK_UNDEF; + /* Since this card has no ASIC, mark it as loaded so everything + works OK */ + chip->asic_loaded = TRUE; + chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL | + ECHO_CLOCK_BIT_SPDIF; + + if ((err = load_firmware(chip)) < 0) + return err; + chip->bad_board = FALSE; + + if ((err = init_line_levels(chip)) < 0) + return err; + + err = set_professional_spdif(chip, TRUE); + + DE_INIT(("init_hw done\n")); + return err; +} + + + +static u32 detect_input_clocks(const struct echoaudio *chip) +{ + u32 clocks_from_dsp, clock_bits; + + /* Map the DSP clock detect bits to the generic driver clock + detect bits */ + clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks); + + clock_bits = ECHO_CLOCK_BIT_INTERNAL; + + if (clocks_from_dsp & GLDM_CLOCK_DETECT_BIT_SPDIF) + clock_bits |= ECHO_CLOCK_BIT_SPDIF; + + return clock_bits; +} + + + +/* The Gina20 has no ASIC. Just do nothing */ +static int load_asic(struct echoaudio *chip) +{ + return 0; +} + + + +static int set_sample_rate(struct echoaudio *chip, u32 rate) +{ + u8 clock_state, spdif_status; + + if (wait_handshake(chip)) + return -EIO; + + switch (rate) { + case 44100: + clock_state = GD_CLOCK_44; + spdif_status = GD_SPDIF_STATUS_44; + break; + case 48000: + clock_state = GD_CLOCK_48; + spdif_status = GD_SPDIF_STATUS_48; + break; + default: + clock_state = GD_CLOCK_NOCHANGE; + spdif_status = GD_SPDIF_STATUS_NOCHANGE; + break; + } + + if (chip->clock_state == clock_state) + clock_state = GD_CLOCK_NOCHANGE; + if (spdif_status == chip->spdif_status) + spdif_status = GD_SPDIF_STATUS_NOCHANGE; + + chip->comm_page->sample_rate = cpu_to_le32(rate); + chip->comm_page->gd_clock_state = clock_state; + chip->comm_page->gd_spdif_status = spdif_status; + chip->comm_page->gd_resampler_state = 3; /* magic number - should always be 3 */ + + /* Save the new audio state if it changed */ + if (clock_state != GD_CLOCK_NOCHANGE) + chip->clock_state = clock_state; + if (spdif_status != GD_SPDIF_STATUS_NOCHANGE) + chip->spdif_status = spdif_status; + chip->sample_rate = rate; + + clear_handshake(chip); + return send_vector(chip, DSP_VC_SET_GD_AUDIO_STATE); +} + + + +static int set_input_clock(struct echoaudio *chip, u16 clock) +{ + DE_ACT(("set_input_clock:\n")); + + switch (clock) { + case ECHO_CLOCK_INTERNAL: + /* Reset the audio state to unknown (just in case) */ + chip->clock_state = GD_CLOCK_UNDEF; + chip->spdif_status = GD_SPDIF_STATUS_UNDEF; + set_sample_rate(chip, chip->sample_rate); + chip->input_clock = clock; + DE_ACT(("Set Gina clock to INTERNAL\n")); + break; + case ECHO_CLOCK_SPDIF: + chip->comm_page->gd_clock_state = GD_CLOCK_SPDIFIN; + chip->comm_page->gd_spdif_status = GD_SPDIF_STATUS_NOCHANGE; + clear_handshake(chip); + send_vector(chip, DSP_VC_SET_GD_AUDIO_STATE); + chip->clock_state = GD_CLOCK_SPDIFIN; + DE_ACT(("Set Gina20 clock to SPDIF\n")); + chip->input_clock = clock; + break; + default: + return -EINVAL; + } + + return 0; +} + + + +/* Set input bus gain (one unit is 0.5dB !) */ +static int set_input_gain(struct echoaudio *chip, u16 input, int gain) +{ + snd_assert(input < num_busses_in(chip), return -EINVAL); + + if (wait_handshake(chip)) + return -EIO; + + chip->input_gain[input] = gain; + gain += GL20_INPUT_GAIN_MAGIC_NUMBER; + chip->comm_page->line_in_level[input] = gain; + return 0; +} + + + +/* Tell the DSP to reread the flags from the comm page */ +static int update_flags(struct echoaudio *chip) +{ + if (wait_handshake(chip)) + return -EIO; + clear_handshake(chip); + return send_vector(chip, DSP_VC_UPDATE_FLAGS); +} + + + +static int set_professional_spdif(struct echoaudio *chip, char prof) +{ + DE_ACT(("set_professional_spdif %d\n", prof)); + if (prof) + chip->comm_page->flags |= + __constant_cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF); + else + chip->comm_page->flags &= + ~__constant_cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF); + chip->professional_spdif = prof; + return update_flags(chip); +} diff --git a/sound/pci/echoaudio/gina24.c b/sound/pci/echoaudio/gina24.c new file mode 100644 index 00000000000..e464d720d0b --- /dev/null +++ b/sound/pci/echoaudio/gina24.c @@ -0,0 +1,123 @@ +/* + * ALSA driver for Echoaudio soundcards. + * Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define ECHO24_FAMILY +#define ECHOCARD_GINA24 +#define ECHOCARD_NAME "Gina24" +#define ECHOCARD_HAS_MONITOR +#define ECHOCARD_HAS_ASIC +#define ECHOCARD_HAS_INPUT_NOMINAL_LEVEL +#define ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL +#define ECHOCARD_HAS_SUPER_INTERLEAVE +#define ECHOCARD_HAS_DIGITAL_IO +#define ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE +#define ECHOCARD_HAS_DIGITAL_MODE_SWITCH +#define ECHOCARD_HAS_EXTERNAL_CLOCK +#define ECHOCARD_HAS_ADAT 6 +#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32 + +/* Pipe indexes */ +#define PX_ANALOG_OUT 0 /* 8 */ +#define PX_DIGITAL_OUT 8 /* 8 */ +#define PX_ANALOG_IN 16 /* 2 */ +#define PX_DIGITAL_IN 18 /* 8 */ +#define PX_NUM 26 + +/* Bus indexes */ +#define BX_ANALOG_OUT 0 /* 8 */ +#define BX_DIGITAL_OUT 8 /* 8 */ +#define BX_ANALOG_IN 16 /* 2 */ +#define BX_DIGITAL_IN 18 /* 8 */ +#define BX_NUM 26 + + +#include <sound/driver.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/slab.h> +#include <linux/moduleparam.h> +#include <linux/firmware.h> +#include <sound/core.h> +#include <sound/info.h> +#include <sound/control.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/asoundef.h> +#include <sound/initval.h> +#include <asm/io.h> +#include <asm/atomic.h> +#include "echoaudio.h" + +#define FW_361_LOADER 0 +#define FW_GINA24_301_DSP 1 +#define FW_GINA24_361_DSP 2 +#define FW_GINA24_301_ASIC 3 +#define FW_GINA24_361_ASIC 4 + +static const struct firmware card_fw[] = { + {0, "loader_dsp.fw"}, + {0, "gina24_301_dsp.fw"}, + {0, "gina24_361_dsp.fw"}, + {0, "gina24_301_asic.fw"}, + {0, "gina24_361_asic.fw"} +}; + +static struct pci_device_id snd_echo_ids[] = { + {0x1057, 0x1801, 0xECC0, 0x0050, 0, 0, 0}, /* DSP 56301 Gina24 rev.0 */ + {0x1057, 0x1801, 0xECC0, 0x0051, 0, 0, 0}, /* DSP 56301 Gina24 rev.1 */ + {0x1057, 0x3410, 0xECC0, 0x0050, 0, 0, 0}, /* DSP 56361 Gina24 rev.0 */ + {0x1057, 0x3410, 0xECC0, 0x0051, 0, 0, 0}, /* DSP 56361 Gina24 rev.1 */ + {0,} +}; + +static struct snd_pcm_hardware pcm_hardware_skel = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START, + .formats = SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S32_BE, + .rates = SNDRV_PCM_RATE_8000_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000, + .rate_min = 8000, + .rate_max = 96000, + .channels_min = 1, + .channels_max = 8, + .buffer_bytes_max = 262144, + .period_bytes_min = 32, + .period_bytes_max = 131072, + .periods_min = 2, + .periods_max = 220, + /* One page (4k) contains 512 instructions. I don't know if the hw + supports lists longer than this. In this case periods_max=220 is a + safe limit to make sure the list never exceeds 512 instructions. + 220 ~= (512 - 1 - (BUFFER_BYTES_MAX / PAGE_SIZE)) / 2 */ +}; + +#include "gina24_dsp.c" +#include "echoaudio_dsp.c" +#include "echoaudio_gml.c" +#include "echoaudio.c" diff --git a/sound/pci/echoaudio/gina24_dsp.c b/sound/pci/echoaudio/gina24_dsp.c new file mode 100644 index 00000000000..144fc567bec --- /dev/null +++ b/sound/pci/echoaudio/gina24_dsp.c @@ -0,0 +1,346 @@ +/**************************************************************************** + + Copyright Echo Digital Audio Corporation (c) 1998 - 2004 + All rights reserved + www.echoaudio.com + + This file is part of Echo Digital Audio's generic driver library. + + Echo Digital Audio's generic driver library is free software; + you can redistribute it and/or modify it under the terms of + the GNU General Public License as published by the Free Software + Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + ************************************************************************* + + Translation from C++ and adaptation for use in ALSA-Driver + were made by Giuliano Pochini <pochini@shiny.it> + +****************************************************************************/ + + +static int write_control_reg(struct echoaudio *chip, u32 value, char force); +static int set_input_clock(struct echoaudio *chip, u16 clock); +static int set_professional_spdif(struct echoaudio *chip, char prof); +static int set_digital_mode(struct echoaudio *chip, u8 mode); +static int load_asic_generic(struct echoaudio *chip, u32 cmd, + const struct firmware *asic); +static int check_asic_status(struct echoaudio *chip); + + +static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) +{ + int err; + + DE_INIT(("init_hw() - Gina24\n")); + snd_assert((subdevice_id & 0xfff0) == GINA24, return -ENODEV); + + if ((err = init_dsp_comm_page(chip))) { + DE_INIT(("init_hw - could not initialize DSP comm page\n")); + return err; + } + + chip->device_id = device_id; + chip->subdevice_id = subdevice_id; + chip->bad_board = TRUE; + chip->input_clock_types = + ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF | + ECHO_CLOCK_BIT_ESYNC | ECHO_CLOCK_BIT_ESYNC96 | + ECHO_CLOCK_BIT_ADAT; + chip->professional_spdif = FALSE; + chip->digital_in_automute = TRUE; + chip->digital_mode = DIGITAL_MODE_SPDIF_RCA; + + /* Gina24 comes in both '301 and '361 flavors */ + if (chip->device_id == DEVICE_ID_56361) { + chip->dsp_code_to_load = &card_fw[FW_GINA24_361_DSP]; + chip->digital_modes = + ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA | + ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL | + ECHOCAPS_HAS_DIGITAL_MODE_ADAT; + } else { + chip->dsp_code_to_load = &card_fw[FW_GINA24_301_DSP]; + chip->digital_modes = + ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA | + ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL | + ECHOCAPS_HAS_DIGITAL_MODE_ADAT | + ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_CDROM; + } + + if ((err = load_firmware(chip)) < 0) + return err; + chip->bad_board = FALSE; + + if ((err = init_line_levels(chip)) < 0) + return err; + err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA); + snd_assert(err >= 0, return err); + err = set_professional_spdif(chip, TRUE); + + DE_INIT(("init_hw done\n")); + return err; +} + + + +static u32 detect_input_clocks(const struct echoaudio *chip) +{ + u32 clocks_from_dsp, clock_bits; + + /* Map the DSP clock detect bits to the generic driver clock + detect bits */ + clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks); + + clock_bits = ECHO_CLOCK_BIT_INTERNAL; + + if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_SPDIF) + clock_bits |= ECHO_CLOCK_BIT_SPDIF; + + if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_ADAT) + clock_bits |= ECHO_CLOCK_BIT_ADAT; + + if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_ESYNC) + clock_bits |= ECHO_CLOCK_BIT_ESYNC | ECHO_CLOCK_BIT_ESYNC96; + + return clock_bits; +} + + + +/* Gina24 has an ASIC on the PCI card which must be loaded for anything +interesting to happen. */ +static int load_asic(struct echoaudio *chip) +{ + u32 control_reg; + int err; + const struct firmware *fw; + + if (chip->asic_loaded) + return 1; + + /* Give the DSP a few milliseconds to settle down */ + mdelay(10); + + /* Pick the correct ASIC for '301 or '361 Gina24 */ + if (chip->device_id == DEVICE_ID_56361) + fw = &card_fw[FW_GINA24_361_ASIC]; + else + fw = &card_fw[FW_GINA24_301_ASIC]; + + if ((err = load_asic_generic(chip, DSP_FNC_LOAD_GINA24_ASIC, fw)) < 0) + return err; + + chip->asic_code = fw; + + /* Now give the new ASIC a little time to set up */ + mdelay(10); + /* See if it worked */ + err = check_asic_status(chip); + + /* Set up the control register if the load succeeded - + 48 kHz, internal clock, S/PDIF RCA mode */ + if (!err) { + control_reg = GML_CONVERTER_ENABLE | GML_48KHZ; + err = write_control_reg(chip, control_reg, TRUE); + } + DE_INIT(("load_asic() done\n")); + return err; +} + + + +static int set_sample_rate(struct echoaudio *chip, u32 rate) +{ + u32 control_reg, clock; + + snd_assert(rate < 50000 || chip->digital_mode != DIGITAL_MODE_ADAT, + return -EINVAL); + + /* Only set the clock for internal mode. */ + if (chip->input_clock != ECHO_CLOCK_INTERNAL) { + DE_ACT(("set_sample_rate: Cannot set sample rate - " + "clock not set to CLK_CLOCKININTERNAL\n")); + /* Save the rate anyhow */ + chip->comm_page->sample_rate = cpu_to_le32(rate); + chip->sample_rate = rate; + return 0; + } + + clock = 0; + + control_reg = le32_to_cpu(chip->comm_page->control_register); + control_reg &= GML_CLOCK_CLEAR_MASK & GML_SPDIF_RATE_CLEAR_MASK; + + switch (rate) { + case 96000: + clock = GML_96KHZ; + break; + case 88200: + clock = GML_88KHZ; + break; + case 48000: + clock = GML_48KHZ | GML_SPDIF_SAMPLE_RATE1; + break; + case 44100: + clock = GML_44KHZ; + /* Professional mode ? */ + if (control_reg & GML_SPDIF_PRO_MODE) + clock |= GML_SPDIF_SAMPLE_RATE0; + break; + case 32000: + clock = GML_32KHZ | GML_SPDIF_SAMPLE_RATE0 | + GML_SPDIF_SAMPLE_RATE1; + break; + case 22050: + clock = GML_22KHZ; + break; + case 16000: + clock = GML_16KHZ; + break; + case 11025: + clock = GML_11KHZ; + break; + case 8000: + clock = GML_8KHZ; + break; + default: + DE_ACT(("set_sample_rate: %d invalid!\n", rate)); + return -EINVAL; + } + + control_reg |= clock; + + chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP */ + chip->sample_rate = rate; + DE_ACT(("set_sample_rate: %d clock %d\n", rate, clock)); + + return write_control_reg(chip, control_reg, FALSE); +} + + + +static int set_input_clock(struct echoaudio *chip, u16 clock) +{ + u32 control_reg, clocks_from_dsp; + + DE_ACT(("set_input_clock:\n")); + + /* Mask off the clock select bits */ + control_reg = le32_to_cpu(chip->comm_page->control_register) & + GML_CLOCK_CLEAR_MASK; + clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks); + + switch (clock) { + case ECHO_CLOCK_INTERNAL: + DE_ACT(("Set Gina24 clock to INTERNAL\n")); + chip->input_clock = ECHO_CLOCK_INTERNAL; + return set_sample_rate(chip, chip->sample_rate); + case ECHO_CLOCK_SPDIF: + if (chip->digital_mode == DIGITAL_MODE_ADAT) + return -EAGAIN; + DE_ACT(("Set Gina24 clock to SPDIF\n")); + control_reg |= GML_SPDIF_CLOCK; + if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_SPDIF96) + control_reg |= GML_DOUBLE_SPEED_MODE; + else + control_reg &= ~GML_DOUBLE_SPEED_MODE; + break; + case ECHO_CLOCK_ADAT: + if (chip->digital_mode != DIGITAL_MODE_ADAT) + return -EAGAIN; + DE_ACT(("Set Gina24 clock to ADAT\n")); + control_reg |= GML_ADAT_CLOCK; + control_reg &= ~GML_DOUBLE_SPEED_MODE; + break; + case ECHO_CLOCK_ESYNC: + DE_ACT(("Set Gina24 clock to ESYNC\n")); + control_reg |= GML_ESYNC_CLOCK; + control_reg &= ~GML_DOUBLE_SPEED_MODE; + break; + case ECHO_CLOCK_ESYNC96: + DE_ACT(("Set Gina24 clock to ESYNC96\n")); + control_reg |= GML_ESYNC_CLOCK | GML_DOUBLE_SPEED_MODE; + break; + default: + DE_ACT(("Input clock 0x%x not supported for Gina24\n", clock)); + return -EINVAL; + } + + chip->input_clock = clock; + return write_control_reg(chip, control_reg, TRUE); +} + + + +static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode) +{ + u32 control_reg; + int err, incompatible_clock; + + /* Set clock to "internal" if it's not compatible with the new mode */ + incompatible_clock = FALSE; + switch (mode) { + case DIGITAL_MODE_SPDIF_OPTICAL: + case DIGITAL_MODE_SPDIF_CDROM: + case DIGITAL_MODE_SPDIF_RCA: + if (chip->input_clock == ECHO_CLOCK_ADAT) + incompatible_clock = TRUE; + break; + case DIGITAL_MODE_ADAT: + if (chip->input_clock == ECHO_CLOCK_SPDIF) + incompatible_clock = TRUE; + break; + default: + DE_ACT(("Digital mode not supported: %d\n", mode)); + return -EINVAL; + } + + spin_lock_irq(&chip->lock); + + if (incompatible_clock) { /* Switch to 48KHz, internal */ + chip->sample_rate = 48000; + set_input_clock(chip, ECHO_CLOCK_INTERNAL); + } + + /* Clear the current digital mode */ + control_reg = le32_to_cpu(chip->comm_page->control_register); + control_reg &= GML_DIGITAL_MODE_CLEAR_MASK; + + /* Tweak the control reg */ + switch (mode) { + case DIGITAL_MODE_SPDIF_OPTICAL: + control_reg |= GML_SPDIF_OPTICAL_MODE; + break; + case DIGITAL_MODE_SPDIF_CDROM: + /* '361 Gina24 cards do not have the S/PDIF CD-ROM mode */ + if (chip->device_id == DEVICE_ID_56301) + control_reg |= GML_SPDIF_CDROM_MODE; + break; + case DIGITAL_MODE_SPDIF_RCA: + /* GML_SPDIF_OPTICAL_MODE bit cleared */ + break; + case DIGITAL_MODE_ADAT: + control_reg |= GML_ADAT_MODE; + control_reg &= ~GML_DOUBLE_SPEED_MODE; + break; + } + + err = write_control_reg(chip, control_reg, TRUE); + spin_unlock_irq(&chip->lock); + if (err < 0) + return err; + chip->digital_mode = mode; + + DE_ACT(("set_digital_mode to %d\n", chip->digital_mode)); + return incompatible_clock; +} diff --git a/sound/pci/echoaudio/indigo.c b/sound/pci/echoaudio/indigo.c new file mode 100644 index 00000000000..bfd2467099a --- /dev/null +++ b/sound/pci/echoaudio/indigo.c @@ -0,0 +1,104 @@ +/* + * ALSA driver for Echoaudio soundcards. + * Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define INDIGO_FAMILY +#define ECHOCARD_INDIGO +#define ECHOCARD_NAME "Indigo" +#define ECHOCARD_HAS_SUPER_INTERLEAVE +#define ECHOCARD_HAS_VMIXER +#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32 + +/* Pipe indexes */ +#define PX_ANALOG_OUT 0 /* 8 */ +#define PX_DIGITAL_OUT 8 /* 0 */ +#define PX_ANALOG_IN 8 /* 0 */ +#define PX_DIGITAL_IN 8 /* 0 */ +#define PX_NUM 8 + +/* Bus indexes */ +#define BX_ANALOG_OUT 0 /* 2 */ +#define BX_DIGITAL_OUT 2 /* 0 */ +#define BX_ANALOG_IN 2 /* 0 */ +#define BX_DIGITAL_IN 2 /* 0 */ +#define BX_NUM 2 + + +#include <sound/driver.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/slab.h> +#include <linux/moduleparam.h> +#include <linux/firmware.h> +#include <sound/core.h> +#include <sound/info.h> +#include <sound/control.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/asoundef.h> +#include <sound/initval.h> +#include <asm/io.h> +#include <asm/atomic.h> +#include "echoaudio.h" + +#define FW_361_LOADER 0 +#define FW_INDIGO_DSP 1 + +static const struct firmware card_fw[] = { + {0, "loader_dsp.fw"}, + {0, "indigo_dsp.fw"} +}; + +static struct pci_device_id snd_echo_ids[] = { + {0x1057, 0x3410, 0xECC0, 0x0090, 0, 0, 0}, /* Indigo */ + {0,} +}; + +static struct snd_pcm_hardware pcm_hardware_skel = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START, + .formats = SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S32_BE, + .rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000, + .rate_min = 32000, + .rate_max = 96000, + .channels_min = 1, + .channels_max = 8, + .buffer_bytes_max = 262144, + .period_bytes_min = 32, + .period_bytes_max = 131072, + .periods_min = 2, + .periods_max = 220, +}; + +#include "indigo_dsp.c" +#include "echoaudio_dsp.c" +#include "echoaudio.c" + diff --git a/sound/pci/echoaudio/indigo_dsp.c b/sound/pci/echoaudio/indigo_dsp.c new file mode 100644 index 00000000000..d6ac7734609 --- /dev/null +++ b/sound/pci/echoaudio/indigo_dsp.c @@ -0,0 +1,170 @@ +/**************************************************************************** + + Copyright Echo Digital Audio Corporation (c) 1998 - 2004 + All rights reserved + www.echoaudio.com + + This file is part of Echo Digital Audio's generic driver library. + + Echo Digital Audio's generic driver library is free software; + you can redistribute it and/or modify it under the terms of + the GNU General Public License as published by the Free Software + Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + ************************************************************************* + + Translation from C++ and adaptation for use in ALSA-Driver + were made by Giuliano Pochini <pochini@shiny.it> + +****************************************************************************/ + + +static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe, + int gain); +static int update_vmixer_level(struct echoaudio *chip); + + +static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) +{ + int err; + + DE_INIT(("init_hw() - Indigo\n")); + snd_assert((subdevice_id & 0xfff0) == INDIGO, return -ENODEV); + + if ((err = init_dsp_comm_page(chip))) { + DE_INIT(("init_hw - could not initialize DSP comm page\n")); + return err; + } + + chip->device_id = device_id; + chip->subdevice_id = subdevice_id; + chip->bad_board = TRUE; + chip->dsp_code_to_load = &card_fw[FW_INDIGO_DSP]; + /* Since this card has no ASIC, mark it as loaded so everything + works OK */ + chip->asic_loaded = TRUE; + chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL; + + if ((err = load_firmware(chip)) < 0) + return err; + chip->bad_board = FALSE; + + if ((err = init_line_levels(chip)) < 0) + return err; + + /* Default routing of the virtual channels: all vchannels are routed + to the stereo output */ + set_vmixer_gain(chip, 0, 0, 0); + set_vmixer_gain(chip, 1, 1, 0); + set_vmixer_gain(chip, 0, 2, 0); + set_vmixer_gain(chip, 1, 3, 0); + set_vmixer_gain(chip, 0, 4, 0); + set_vmixer_gain(chip, 1, 5, 0); + set_vmixer_gain(chip, 0, 6, 0); + set_vmixer_gain(chip, 1, 7, 0); + err = update_vmixer_level(chip); + + DE_INIT(("init_hw done\n")); + return err; +} + + + +static u32 detect_input_clocks(const struct echoaudio *chip) +{ + return ECHO_CLOCK_BIT_INTERNAL; +} + + + +/* The Indigo has no ASIC. Just do nothing */ +static int load_asic(struct echoaudio *chip) +{ + return 0; +} + + + +static int set_sample_rate(struct echoaudio *chip, u32 rate) +{ + u32 control_reg; + + switch (rate) { + case 96000: + control_reg = MIA_96000; + break; + case 88200: + control_reg = MIA_88200; + break; + case 48000: + control_reg = MIA_48000; + break; + case 44100: + control_reg = MIA_44100; + break; + case 32000: + control_reg = MIA_32000; + break; + default: + DE_ACT(("set_sample_rate: %d invalid!\n", rate)); + return -EINVAL; + } + + /* Set the control register if it has changed */ + if (control_reg != le32_to_cpu(chip->comm_page->control_register)) { + if (wait_handshake(chip)) + return -EIO; + + chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP */ + chip->comm_page->control_register = cpu_to_le32(control_reg); + chip->sample_rate = rate; + + clear_handshake(chip); + return send_vector(chip, DSP_VC_UPDATE_CLOCKS); + } + return 0; +} + + + +/* This function routes the sound from a virtual channel to a real output */ +static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe, + int gain) +{ + int index; + + snd_assert(pipe < num_pipes_out(chip) && + output < num_busses_out(chip), return -EINVAL); + + if (wait_handshake(chip)) + return -EIO; + + chip->vmixer_gain[output][pipe] = gain; + index = output * num_pipes_out(chip) + pipe; + chip->comm_page->vmixer[index] = gain; + + DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain)); + return 0; +} + + + +/* Tell the DSP to read and update virtual mixer levels in comm page. */ +static int update_vmixer_level(struct echoaudio *chip) +{ + if (wait_handshake(chip)) + return -EIO; + clear_handshake(chip); + return send_vector(chip, DSP_VC_SET_VMIXER_GAIN); +} + diff --git a/sound/pci/echoaudio/indigodj.c b/sound/pci/echoaudio/indigodj.c new file mode 100644 index 00000000000..8ed7ff1fd87 --- /dev/null +++ b/sound/pci/echoaudio/indigodj.c @@ -0,0 +1,104 @@ +/* + * ALSA driver for Echoaudio soundcards. + * Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define INDIGO_FAMILY +#define ECHOCARD_INDIGO_DJ +#define ECHOCARD_NAME "Indigo DJ" +#define ECHOCARD_HAS_SUPER_INTERLEAVE +#define ECHOCARD_HAS_VMIXER +#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32 + +/* Pipe indexes */ +#define PX_ANALOG_OUT 0 /* 8 */ +#define PX_DIGITAL_OUT 8 /* 0 */ +#define PX_ANALOG_IN 8 /* 0 */ +#define PX_DIGITAL_IN 8 /* 0 */ +#define PX_NUM 8 + +/* Bus indexes */ +#define BX_ANALOG_OUT 0 /* 4 */ +#define BX_DIGITAL_OUT 4 /* 0 */ +#define BX_ANALOG_IN 4 /* 0 */ +#define BX_DIGITAL_IN 4 /* 0 */ +#define BX_NUM 4 + + +#include <sound/driver.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/slab.h> +#include <linux/moduleparam.h> +#include <linux/firmware.h> +#include <sound/core.h> +#include <sound/info.h> +#include <sound/control.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/asoundef.h> +#include <sound/initval.h> +#include <asm/io.h> +#include <asm/atomic.h> +#include "echoaudio.h" + +#define FW_361_LOADER 0 +#define FW_INDIGO_DJ_DSP 1 + +static const struct firmware card_fw[] = { + {0, "loader_dsp.fw"}, + {0, "indigo_dj_dsp.fw"} +}; + +static struct pci_device_id snd_echo_ids[] = { + {0x1057, 0x3410, 0xECC0, 0x00B0, 0, 0, 0}, /* Indigo DJ*/ + {0,} +}; + +static struct snd_pcm_hardware pcm_hardware_skel = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START, + .formats = SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S32_BE, + .rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000, + .rate_min = 32000, + .rate_max = 96000, + .channels_min = 1, + .channels_max = 4, + .buffer_bytes_max = 262144, + .period_bytes_min = 32, + .period_bytes_max = 131072, + .periods_min = 2, + .periods_max = 220, +}; + +#include "indigodj_dsp.c" +#include "echoaudio_dsp.c" +#include "echoaudio.c" + diff --git a/sound/pci/echoaudio/indigodj_dsp.c b/sound/pci/echoaudio/indigodj_dsp.c new file mode 100644 index 00000000000..500e150b49f --- /dev/null +++ b/sound/pci/echoaudio/indigodj_dsp.c @@ -0,0 +1,170 @@ +/**************************************************************************** + + Copyright Echo Digital Audio Corporation (c) 1998 - 2004 + All rights reserved + www.echoaudio.com + + This file is part of Echo Digital Audio's generic driver library. + + Echo Digital Audio's generic driver library is free software; + you can redistribute it and/or modify it under the terms of + the GNU General Public License as published by the Free Software + Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + ************************************************************************* + + Translation from C++ and adaptation for use in ALSA-Driver + were made by Giuliano Pochini <pochini@shiny.it> + +****************************************************************************/ + + +static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe, + int gain); +static int update_vmixer_level(struct echoaudio *chip); + + +static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) +{ + int err; + + DE_INIT(("init_hw() - Indigo DJ\n")); + snd_assert((subdevice_id & 0xfff0) == INDIGO_DJ, return -ENODEV); + + if ((err = init_dsp_comm_page(chip))) { + DE_INIT(("init_hw - could not initialize DSP comm page\n")); + return err; + } + + chip->device_id = device_id; + chip->subdevice_id = subdevice_id; + chip->bad_board = TRUE; + chip->dsp_code_to_load = &card_fw[FW_INDIGO_DJ_DSP]; + /* Since this card has no ASIC, mark it as loaded so everything + works OK */ + chip->asic_loaded = TRUE; + chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL; + + if ((err = load_firmware(chip)) < 0) + return err; + chip->bad_board = FALSE; + + if ((err = init_line_levels(chip)) < 0) + return err; + + /* Default routing of the virtual channels: vchannels 0-3 and + vchannels 4-7 are routed to real channels 0-4 */ + set_vmixer_gain(chip, 0, 0, 0); + set_vmixer_gain(chip, 1, 1, 0); + set_vmixer_gain(chip, 2, 2, 0); + set_vmixer_gain(chip, 3, 3, 0); + set_vmixer_gain(chip, 0, 4, 0); + set_vmixer_gain(chip, 1, 5, 0); + set_vmixer_gain(chip, 2, 6, 0); + set_vmixer_gain(chip, 3, 7, 0); + err = update_vmixer_level(chip); + + DE_INIT(("init_hw done\n")); + return err; +} + + + +static u32 detect_input_clocks(const struct echoaudio *chip) +{ + return ECHO_CLOCK_BIT_INTERNAL; +} + + + +/* The IndigoDJ has no ASIC. Just do nothing */ +static int load_asic(struct echoaudio *chip) +{ + return 0; +} + + + +static int set_sample_rate(struct echoaudio *chip, u32 rate) +{ + u32 control_reg; + + switch (rate) { + case 96000: + control_reg = MIA_96000; + break; + case 88200: + control_reg = MIA_88200; + break; + case 48000: + control_reg = MIA_48000; + break; + case 44100: + control_reg = MIA_44100; + break; + case 32000: + control_reg = MIA_32000; + break; + default: + DE_ACT(("set_sample_rate: %d invalid!\n", rate)); + return -EINVAL; + } + + /* Set the control register if it has changed */ + if (control_reg != le32_to_cpu(chip->comm_page->control_register)) { + if (wait_handshake(chip)) + return -EIO; + + chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP */ + chip->comm_page->control_register = cpu_to_le32(control_reg); + chip->sample_rate = rate; + + clear_handshake(chip); + return send_vector(chip, DSP_VC_UPDATE_CLOCKS); + } + return 0; +} + + + +/* This function routes the sound from a virtual channel to a real output */ +static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe, + int gain) +{ + int index; + + snd_assert(pipe < num_pipes_out(chip) && + output < num_busses_out(chip), return -EINVAL); + + if (wait_handshake(chip)) + return -EIO; + + chip->vmixer_gain[output][pipe] = gain; + index = output * num_pipes_out(chip) + pipe; + chip->comm_page->vmixer[index] = gain; + + DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain)); + return 0; +} + + + +/* Tell the DSP to read and update virtual mixer levels in comm page. */ +static int update_vmixer_level(struct echoaudio *chip) +{ + if (wait_handshake(chip)) + return -EIO; + clear_handshake(chip); + return send_vector(chip, DSP_VC_SET_VMIXER_GAIN); +} + diff --git a/sound/pci/echoaudio/indigoio.c b/sound/pci/echoaudio/indigoio.c new file mode 100644 index 00000000000..a8788e95917 --- /dev/null +++ b/sound/pci/echoaudio/indigoio.c @@ -0,0 +1,105 @@ +/* + * ALSA driver for Echoaudio soundcards. + * Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define INDIGO_FAMILY +#define ECHOCARD_INDIGO_IO +#define ECHOCARD_NAME "Indigo IO" +#define ECHOCARD_HAS_MONITOR +#define ECHOCARD_HAS_SUPER_INTERLEAVE +#define ECHOCARD_HAS_VMIXER +#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32 + +/* Pipe indexes */ +#define PX_ANALOG_OUT 0 /* 8 */ +#define PX_DIGITAL_OUT 8 /* 0 */ +#define PX_ANALOG_IN 8 /* 2 */ +#define PX_DIGITAL_IN 10 /* 0 */ +#define PX_NUM 10 + +/* Bus indexes */ +#define BX_ANALOG_OUT 0 /* 2 */ +#define BX_DIGITAL_OUT 2 /* 0 */ +#define BX_ANALOG_IN 2 /* 2 */ +#define BX_DIGITAL_IN 4 /* 0 */ +#define BX_NUM 4 + + +#include <sound/driver.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/slab.h> +#include <linux/moduleparam.h> +#include <linux/firmware.h> +#include <sound/core.h> +#include <sound/info.h> +#include <sound/control.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/asoundef.h> +#include <sound/initval.h> +#include <asm/io.h> +#include <asm/atomic.h> +#include "echoaudio.h" + +#define FW_361_LOADER 0 +#define FW_INDIGO_IO_DSP 1 + +static const struct firmware card_fw[] = { + {0, "loader_dsp.fw"}, + {0, "indigo_io_dsp.fw"} +}; + +static struct pci_device_id snd_echo_ids[] = { + {0x1057, 0x3410, 0xECC0, 0x00A0, 0, 0, 0}, /* Indigo IO*/ + {0,} +}; + +static struct snd_pcm_hardware pcm_hardware_skel = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START, + .formats = SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S32_BE, + .rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000, + .rate_min = 32000, + .rate_max = 96000, + .channels_min = 1, + .channels_max = 8, + .buffer_bytes_max = 262144, + .period_bytes_min = 32, + .period_bytes_max = 131072, + .periods_min = 2, + .periods_max = 220, +}; + +#include "indigoio_dsp.c" +#include "echoaudio_dsp.c" +#include "echoaudio.c" + diff --git a/sound/pci/echoaudio/indigoio_dsp.c b/sound/pci/echoaudio/indigoio_dsp.c new file mode 100644 index 00000000000..f3ad13d06be --- /dev/null +++ b/sound/pci/echoaudio/indigoio_dsp.c @@ -0,0 +1,141 @@ +/**************************************************************************** + + Copyright Echo Digital Audio Corporation (c) 1998 - 2004 + All rights reserved + www.echoaudio.com + + This file is part of Echo Digital Audio's generic driver library. + + Echo Digital Audio's generic driver library is free software; + you can redistribute it and/or modify it under the terms of + the GNU General Public License as published by the Free Software + Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + ************************************************************************* + + Translation from C++ and adaptation for use in ALSA-Driver + were made by Giuliano Pochini <pochini@shiny.it> + +****************************************************************************/ + + +static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe, + int gain); +static int update_vmixer_level(struct echoaudio *chip); + + +static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) +{ + int err; + + DE_INIT(("init_hw() - Indigo IO\n")); + snd_assert((subdevice_id & 0xfff0) == INDIGO_IO, return -ENODEV); + + if ((err = init_dsp_comm_page(chip))) { + DE_INIT(("init_hw - could not initialize DSP comm page\n")); + return err; + } + + chip->device_id = device_id; + chip->subdevice_id = subdevice_id; + chip->bad_board = TRUE; + chip->dsp_code_to_load = &card_fw[FW_INDIGO_IO_DSP]; + /* Since this card has no ASIC, mark it as loaded so everything + works OK */ + chip->asic_loaded = TRUE; + chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL; + + if ((err = load_firmware(chip)) < 0) + return err; + chip->bad_board = FALSE; + + if ((err = init_line_levels(chip)) < 0) + return err; + + /* Default routing of the virtual channels: all vchannels are routed + to the stereo output */ + set_vmixer_gain(chip, 0, 0, 0); + set_vmixer_gain(chip, 1, 1, 0); + set_vmixer_gain(chip, 0, 2, 0); + set_vmixer_gain(chip, 1, 3, 0); + set_vmixer_gain(chip, 0, 4, 0); + set_vmixer_gain(chip, 1, 5, 0); + set_vmixer_gain(chip, 0, 6, 0); + set_vmixer_gain(chip, 1, 7, 0); + err = update_vmixer_level(chip); + + DE_INIT(("init_hw done\n")); + return err; +} + + + +static u32 detect_input_clocks(const struct echoaudio *chip) +{ + return ECHO_CLOCK_BIT_INTERNAL; +} + + + +/* The IndigoIO has no ASIC. Just do nothing */ +static int load_asic(struct echoaudio *chip) +{ + return 0; +} + + + +static int set_sample_rate(struct echoaudio *chip, u32 rate) +{ + if (wait_handshake(chip)) + return -EIO; + + chip->sample_rate = rate; + chip->comm_page->sample_rate = cpu_to_le32(rate); + clear_handshake(chip); + return send_vector(chip, DSP_VC_UPDATE_CLOCKS); +} + + + +/* This function routes the sound from a virtual channel to a real output */ +static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe, + int gain) +{ + int index; + + snd_assert(pipe < num_pipes_out(chip) && + output < num_busses_out(chip), return -EINVAL); + + if (wait_handshake(chip)) + return -EIO; + + chip->vmixer_gain[output][pipe] = gain; + index = output * num_pipes_out(chip) + pipe; + chip->comm_page->vmixer[index] = gain; + + DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain)); + return 0; +} + + + +/* Tell the DSP to read and update virtual mixer levels in comm page. */ +static int update_vmixer_level(struct echoaudio *chip) +{ + if (wait_handshake(chip)) + return -EIO; + clear_handshake(chip); + return send_vector(chip, DSP_VC_SET_VMIXER_GAIN); +} + diff --git a/sound/pci/echoaudio/layla20.c b/sound/pci/echoaudio/layla20.c new file mode 100644 index 00000000000..e503d74b3ba --- /dev/null +++ b/sound/pci/echoaudio/layla20.c @@ -0,0 +1,112 @@ +/* + * ALSA driver for Echoaudio soundcards. + * Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define ECHOGALS_FAMILY +#define ECHOCARD_LAYLA20 +#define ECHOCARD_NAME "Layla20" +#define ECHOCARD_HAS_MONITOR +#define ECHOCARD_HAS_ASIC +#define ECHOCARD_HAS_INPUT_GAIN +#define ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL +#define ECHOCARD_HAS_SUPER_INTERLEAVE +#define ECHOCARD_HAS_DIGITAL_IO +#define ECHOCARD_HAS_EXTERNAL_CLOCK +#define ECHOCARD_HAS_ADAT FALSE +#define ECHOCARD_HAS_OUTPUT_CLOCK_SWITCH +#define ECHOCARD_HAS_MIDI + +/* Pipe indexes */ +#define PX_ANALOG_OUT 0 /* 10 */ +#define PX_DIGITAL_OUT 10 /* 2 */ +#define PX_ANALOG_IN 12 /* 8 */ +#define PX_DIGITAL_IN 20 /* 2 */ +#define PX_NUM 22 + +/* Bus indexes */ +#define BX_ANALOG_OUT 0 /* 10 */ +#define BX_DIGITAL_OUT 10 /* 2 */ +#define BX_ANALOG_IN 12 /* 8 */ +#define BX_DIGITAL_IN 20 /* 2 */ +#define BX_NUM 22 + + +#include <sound/driver.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/slab.h> +#include <linux/moduleparam.h> +#include <linux/firmware.h> +#include <sound/core.h> +#include <sound/info.h> +#include <sound/control.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/asoundef.h> +#include <sound/initval.h> +#include <sound/rawmidi.h> +#include <asm/io.h> +#include <asm/atomic.h> +#include "echoaudio.h" + +#define FW_LAYLA20_DSP 0 +#define FW_LAYLA20_ASIC 1 + +static const struct firmware card_fw[] = { + {0, "layla20_dsp.fw"}, + {0, "layla20_asic.fw"} +}; + +static struct pci_device_id snd_echo_ids[] = { + {0x1057, 0x1801, 0xECC0, 0x0030, 0, 0, 0}, /* DSP 56301 Layla20 rev.0 */ + {0x1057, 0x1801, 0xECC0, 0x0031, 0, 0, 0}, /* DSP 56301 Layla20 rev.1 */ + {0,} +}; + +static struct snd_pcm_hardware pcm_hardware_skel = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START, + .formats = SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S32_BE, + .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 8000, + .rate_max = 50000, + .channels_min = 1, + .channels_max = 10, + .buffer_bytes_max = 262144, + .period_bytes_min = 32, + .period_bytes_max = 131072, + .periods_min = 2, + .periods_max = 220, + /* One page (4k) contains 512 instructions. I don't know if the hw + supports lists longer than this. In this case periods_max=220 is a + safe limit to make sure the list never exceeds 512 instructions. */ +}; + +#include "layla20_dsp.c" +#include "echoaudio_dsp.c" +#include "echoaudio.c" +#include "midi.c" diff --git a/sound/pci/echoaudio/layla20_dsp.c b/sound/pci/echoaudio/layla20_dsp.c new file mode 100644 index 00000000000..990c9a60a0a --- /dev/null +++ b/sound/pci/echoaudio/layla20_dsp.c @@ -0,0 +1,290 @@ +/**************************************************************************** + + Copyright Echo Digital Audio Corporation (c) 1998 - 2004 + All rights reserved + www.echoaudio.com + + This file is part of Echo Digital Audio's generic driver library. + + Echo Digital Audio's generic driver library is free software; + you can redistribute it and/or modify it under the terms of + the GNU General Public License as published by the Free Software + Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + ************************************************************************* + + Translation from C++ and adaptation for use in ALSA-Driver + were made by Giuliano Pochini <pochini@shiny.it> + +****************************************************************************/ + + +static int read_dsp(struct echoaudio *chip, u32 *data); +static int set_professional_spdif(struct echoaudio *chip, char prof); +static int load_asic_generic(struct echoaudio *chip, u32 cmd, + const struct firmware *asic); +static int check_asic_status(struct echoaudio *chip); +static int update_flags(struct echoaudio *chip); + + +static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) +{ + int err; + + DE_INIT(("init_hw() - Layla20\n")); + snd_assert((subdevice_id & 0xfff0) == LAYLA20, return -ENODEV); + + if ((err = init_dsp_comm_page(chip))) { + DE_INIT(("init_hw - could not initialize DSP comm page\n")); + return err; + } + + chip->device_id = device_id; + chip->subdevice_id = subdevice_id; + chip->bad_board = TRUE; + chip->has_midi = TRUE; + chip->dsp_code_to_load = &card_fw[FW_LAYLA20_DSP]; + chip->input_clock_types = + ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF | + ECHO_CLOCK_BIT_WORD | ECHO_CLOCK_BIT_SUPER; + chip->output_clock_types = + ECHO_CLOCK_BIT_WORD | ECHO_CLOCK_BIT_SUPER; + + if ((err = load_firmware(chip)) < 0) + return err; + chip->bad_board = FALSE; + + if ((err = init_line_levels(chip)) < 0) + return err; + + err = set_professional_spdif(chip, TRUE); + + DE_INIT(("init_hw done\n")); + return err; +} + + + +static u32 detect_input_clocks(const struct echoaudio *chip) +{ + u32 clocks_from_dsp, clock_bits; + + /* Map the DSP clock detect bits to the generic driver clock detect bits */ + clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks); + + clock_bits = ECHO_CLOCK_BIT_INTERNAL; + + if (clocks_from_dsp & GLDM_CLOCK_DETECT_BIT_SPDIF) + clock_bits |= ECHO_CLOCK_BIT_SPDIF; + + if (clocks_from_dsp & GLDM_CLOCK_DETECT_BIT_WORD) { + if (clocks_from_dsp & GLDM_CLOCK_DETECT_BIT_SUPER) + clock_bits |= ECHO_CLOCK_BIT_SUPER; + else + clock_bits |= ECHO_CLOCK_BIT_WORD; + } + + return clock_bits; +} + + + +/* ASIC status check - some cards have one or two ASICs that need to be +loaded. Once that load is complete, this function is called to see if +the load was successful. +If this load fails, it does not necessarily mean that the hardware is +defective - the external box may be disconnected or turned off. +This routine sometimes fails for Layla20; for Layla20, the loop runs +5 times and succeeds if it wins on three of the loops. */ +static int check_asic_status(struct echoaudio *chip) +{ + u32 asic_status; + int goodcnt, i; + + chip->asic_loaded = FALSE; + for (i = goodcnt = 0; i < 5; i++) { + send_vector(chip, DSP_VC_TEST_ASIC); + + /* The DSP will return a value to indicate whether or not + the ASIC is currently loaded */ + if (read_dsp(chip, &asic_status) < 0) { + DE_ACT(("check_asic_status: failed on read_dsp\n")); + return -EIO; + } + + if (asic_status == ASIC_ALREADY_LOADED) { + if (++goodcnt == 3) { + chip->asic_loaded = TRUE; + return 0; + } + } + } + return -EIO; +} + + + +/* Layla20 has an ASIC in the external box */ +static int load_asic(struct echoaudio *chip) +{ + int err; + + if (chip->asic_loaded) + return 0; + + err = load_asic_generic(chip, DSP_FNC_LOAD_LAYLA_ASIC, + &card_fw[FW_LAYLA20_ASIC]); + if (err < 0) + return err; + + /* Check if ASIC is alive and well. */ + return check_asic_status(chip); +} + + + +static int set_sample_rate(struct echoaudio *chip, u32 rate) +{ + snd_assert(rate >= 8000 && rate <= 50000, return -EINVAL); + + /* Only set the clock for internal mode. Do not return failure, + simply treat it as a non-event. */ + if (chip->input_clock != ECHO_CLOCK_INTERNAL) { + DE_ACT(("set_sample_rate: Cannot set sample rate - " + "clock not set to CLK_CLOCKININTERNAL\n")); + chip->comm_page->sample_rate = cpu_to_le32(rate); + chip->sample_rate = rate; + return 0; + } + + if (wait_handshake(chip)) + return -EIO; + + DE_ACT(("set_sample_rate(%d)\n", rate)); + chip->sample_rate = rate; + chip->comm_page->sample_rate = cpu_to_le32(rate); + clear_handshake(chip); + return send_vector(chip, DSP_VC_SET_LAYLA_SAMPLE_RATE); +} + + + +static int set_input_clock(struct echoaudio *chip, u16 clock_source) +{ + u16 clock; + u32 rate; + + DE_ACT(("set_input_clock:\n")); + rate = 0; + switch (clock_source) { + case ECHO_CLOCK_INTERNAL: + DE_ACT(("Set Layla20 clock to INTERNAL\n")); + rate = chip->sample_rate; + clock = LAYLA20_CLOCK_INTERNAL; + break; + case ECHO_CLOCK_SPDIF: + DE_ACT(("Set Layla20 clock to SPDIF\n")); + clock = LAYLA20_CLOCK_SPDIF; + break; + case ECHO_CLOCK_WORD: + DE_ACT(("Set Layla20 clock to WORD\n")); + clock = LAYLA20_CLOCK_WORD; + break; + case ECHO_CLOCK_SUPER: + DE_ACT(("Set Layla20 clock to SUPER\n")); + clock = LAYLA20_CLOCK_SUPER; + break; + default: + DE_ACT(("Input clock 0x%x not supported for Layla24\n", + clock_source)); + return -EINVAL; + } + chip->input_clock = clock_source; + + chip->comm_page->input_clock = cpu_to_le16(clock); + clear_handshake(chip); + send_vector(chip, DSP_VC_UPDATE_CLOCKS); + + if (rate) + set_sample_rate(chip, rate); + + return 0; +} + + + +static int set_output_clock(struct echoaudio *chip, u16 clock) +{ + DE_ACT(("set_output_clock: %d\n", clock)); + switch (clock) { + case ECHO_CLOCK_SUPER: + clock = LAYLA20_OUTPUT_CLOCK_SUPER; + break; + case ECHO_CLOCK_WORD: + clock = LAYLA20_OUTPUT_CLOCK_WORD; + break; + default: + DE_ACT(("set_output_clock wrong clock\n")); + return -EINVAL; + } + + if (wait_handshake(chip)) + return -EIO; + + chip->comm_page->output_clock = cpu_to_le16(clock); + chip->output_clock = clock; + clear_handshake(chip); + return send_vector(chip, DSP_VC_UPDATE_CLOCKS); +} + + + +/* Set input bus gain (one unit is 0.5dB !) */ +static int set_input_gain(struct echoaudio *chip, u16 input, int gain) +{ + snd_assert(input < num_busses_in(chip), return -EINVAL); + + if (wait_handshake(chip)) + return -EIO; + + chip->input_gain[input] = gain; + gain += GL20_INPUT_GAIN_MAGIC_NUMBER; + chip->comm_page->line_in_level[input] = gain; + return 0; +} + + + +/* Tell the DSP to reread the flags from the comm page */ +static int update_flags(struct echoaudio *chip) +{ + if (wait_handshake(chip)) + return -EIO; + clear_handshake(chip); + return send_vector(chip, DSP_VC_UPDATE_FLAGS); +} + + + +static int set_professional_spdif(struct echoaudio *chip, char prof) +{ + DE_ACT(("set_professional_spdif %d\n", prof)); + if (prof) + chip->comm_page->flags |= + __constant_cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF); + else + chip->comm_page->flags &= + ~__constant_cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF); + chip->professional_spdif = prof; + return update_flags(chip); +} diff --git a/sound/pci/echoaudio/layla24.c b/sound/pci/echoaudio/layla24.c new file mode 100644 index 00000000000..d4581fdc841 --- /dev/null +++ b/sound/pci/echoaudio/layla24.c @@ -0,0 +1,121 @@ +/* + * ALSA driver for Echoaudio soundcards. + * Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define ECHO24_FAMILY +#define ECHOCARD_LAYLA24 +#define ECHOCARD_NAME "Layla24" +#define ECHOCARD_HAS_MONITOR +#define ECHOCARD_HAS_ASIC +#define ECHOCARD_HAS_INPUT_NOMINAL_LEVEL +#define ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL +#define ECHOCARD_HAS_SUPER_INTERLEAVE +#define ECHOCARD_HAS_DIGITAL_IO +#define ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE +#define ECHOCARD_HAS_DIGITAL_MODE_SWITCH +#define ECHOCARD_HAS_EXTERNAL_CLOCK +#define ECHOCARD_HAS_ADAT 6 +#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32 +#define ECHOCARD_HAS_MIDI + +/* Pipe indexes */ +#define PX_ANALOG_OUT 0 /* 8 */ +#define PX_DIGITAL_OUT 8 /* 8 */ +#define PX_ANALOG_IN 16 /* 8 */ +#define PX_DIGITAL_IN 24 /* 8 */ +#define PX_NUM 32 + +/* Bus indexes */ +#define BX_ANALOG_OUT 0 /* 8 */ +#define BX_DIGITAL_OUT 8 /* 8 */ +#define BX_ANALOG_IN 16 /* 8 */ +#define BX_DIGITAL_IN 24 /* 8 */ +#define BX_NUM 32 + + +#include <sound/driver.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/slab.h> +#include <linux/moduleparam.h> +#include <linux/firmware.h> +#include <sound/core.h> +#include <sound/info.h> +#include <sound/control.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/asoundef.h> +#include <sound/initval.h> +#include <sound/rawmidi.h> +#include <asm/io.h> +#include <asm/atomic.h> +#include "echoaudio.h" + +#define FW_361_LOADER 0 +#define FW_LAYLA24_DSP 1 +#define FW_LAYLA24_1_ASIC 2 +#define FW_LAYLA24_2A_ASIC 3 +#define FW_LAYLA24_2S_ASIC 4 + +static const struct firmware card_fw[] = { + {0, "loader_dsp.fw"}, + {0, "layla24_dsp.fw"}, + {0, "layla24_1_asic.fw"}, + {0, "layla24_2A_asic.fw"}, + {0, "layla24_2S_asic.fw"} +}; + +static struct pci_device_id snd_echo_ids[] = { + {0x1057, 0x3410, 0xECC0, 0x0060, 0, 0, 0}, /* DSP 56361 Layla24 rev.0 */ + {0,} +}; + +static struct snd_pcm_hardware pcm_hardware_skel = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START, + .formats = SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S32_BE, + .rates = SNDRV_PCM_RATE_8000_96000, + .rate_min = 8000, + .rate_max = 100000, + .channels_min = 1, + .channels_max = 8, + .buffer_bytes_max = 262144, + .period_bytes_min = 32, + .period_bytes_max = 131072, + .periods_min = 2, + .periods_max = 220, + /* One page (4k) contains 512 instructions. I don't know if the hw + supports lists longer than this. In this case periods_max=220 is a + safe limit to make sure the list never exceeds 512 instructions. */ +}; + + +#include "layla24_dsp.c" +#include "echoaudio_dsp.c" +#include "echoaudio_gml.c" +#include "echoaudio.c" +#include "midi.c" diff --git a/sound/pci/echoaudio/layla24_dsp.c b/sound/pci/echoaudio/layla24_dsp.c new file mode 100644 index 00000000000..7ec5b63d0dc --- /dev/null +++ b/sound/pci/echoaudio/layla24_dsp.c @@ -0,0 +1,394 @@ +/**************************************************************************** + + Copyright Echo Digital Audio Corporation (c) 1998 - 2004 + All rights reserved + www.echoaudio.com + + This file is part of Echo Digital Audio's generic driver library. + + Echo Digital Audio's generic driver library is free software; + you can redistribute it and/or modify it under the terms of + the GNU General Public License as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + ************************************************************************* + + Translation from C++ and adaptation for use in ALSA-Driver + were made by Giuliano Pochini <pochini@shiny.it> + +****************************************************************************/ + + +static int write_control_reg(struct echoaudio *chip, u32 value, char force); +static int set_input_clock(struct echoaudio *chip, u16 clock); +static int set_professional_spdif(struct echoaudio *chip, char prof); +static int set_digital_mode(struct echoaudio *chip, u8 mode); +static int load_asic_generic(struct echoaudio *chip, u32 cmd, + const struct firmware *asic); +static int check_asic_status(struct echoaudio *chip); + + +static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) +{ + int err; + + DE_INIT(("init_hw() - Layla24\n")); + snd_assert((subdevice_id & 0xfff0) == LAYLA24, return -ENODEV); + + if ((err = init_dsp_comm_page(chip))) { + DE_INIT(("init_hw - could not initialize DSP comm page\n")); + return err; + } + + chip->device_id = device_id; + chip->subdevice_id = subdevice_id; + chip->bad_board = TRUE; + chip->has_midi = TRUE; + chip->dsp_code_to_load = &card_fw[FW_LAYLA24_DSP]; + chip->input_clock_types = + ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF | + ECHO_CLOCK_BIT_WORD | ECHO_CLOCK_BIT_ADAT; + chip->digital_modes = + ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA | + ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL | + ECHOCAPS_HAS_DIGITAL_MODE_ADAT; + chip->digital_mode = DIGITAL_MODE_SPDIF_RCA; + chip->professional_spdif = FALSE; + chip->digital_in_automute = TRUE; + + if ((err = load_firmware(chip)) < 0) + return err; + chip->bad_board = FALSE; + + if ((err = init_line_levels(chip)) < 0) + return err; + + err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA); + snd_assert(err >= 0, return err); + err = set_professional_spdif(chip, TRUE); + + DE_INIT(("init_hw done\n")); + return err; +} + + + +static u32 detect_input_clocks(const struct echoaudio *chip) +{ + u32 clocks_from_dsp, clock_bits; + + /* Map the DSP clock detect bits to the generic driver clock detect bits */ + clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks); + + clock_bits = ECHO_CLOCK_BIT_INTERNAL; + + if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_SPDIF) + clock_bits |= ECHO_CLOCK_BIT_SPDIF; + + if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_ADAT) + clock_bits |= ECHO_CLOCK_BIT_ADAT; + + if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_WORD) + clock_bits |= ECHO_CLOCK_BIT_WORD; + + return clock_bits; +} + + + +/* Layla24 has an ASIC on the PCI card and another ASIC in the external box; +both need to be loaded. */ +static int load_asic(struct echoaudio *chip) +{ + int err; + + if (chip->asic_loaded) + return 1; + + DE_INIT(("load_asic\n")); + + /* Give the DSP a few milliseconds to settle down */ + mdelay(10); + + /* Load the ASIC for the PCI card */ + err = load_asic_generic(chip, DSP_FNC_LOAD_LAYLA24_PCI_CARD_ASIC, + &card_fw[FW_LAYLA24_1_ASIC]); + if (err < 0) + return err; + + chip->asic_code = &card_fw[FW_LAYLA24_2S_ASIC]; + + /* Now give the new ASIC a little time to set up */ + mdelay(10); + + /* Do the external one */ + err = load_asic_generic(chip, DSP_FNC_LOAD_LAYLA24_EXTERNAL_ASIC, + &card_fw[FW_LAYLA24_2S_ASIC]); + if (err < 0) + return FALSE; + + /* Now give the external ASIC a little time to set up */ + mdelay(10); + + /* See if it worked */ + err = check_asic_status(chip); + + /* Set up the control register if the load succeeded - + 48 kHz, internal clock, S/PDIF RCA mode */ + if (!err) + err = write_control_reg(chip, GML_CONVERTER_ENABLE | GML_48KHZ, + TRUE); + + DE_INIT(("load_asic() done\n")); + return err; +} + + + +static int set_sample_rate(struct echoaudio *chip, u32 rate) +{ + u32 control_reg, clock, base_rate; + + snd_assert(rate < 50000 || chip->digital_mode != DIGITAL_MODE_ADAT, + return -EINVAL); + + /* Only set the clock for internal mode. */ + if (chip->input_clock != ECHO_CLOCK_INTERNAL) { + DE_ACT(("set_sample_rate: Cannot set sample rate - " + "clock not set to CLK_CLOCKININTERNAL\n")); + /* Save the rate anyhow */ + chip->comm_page->sample_rate = cpu_to_le32(rate); + chip->sample_rate = rate; + return 0; + } + + /* Get the control register & clear the appropriate bits */ + control_reg = le32_to_cpu(chip->comm_page->control_register); + control_reg &= GML_CLOCK_CLEAR_MASK & GML_SPDIF_RATE_CLEAR_MASK; + + clock = 0; + + switch (rate) { + case 96000: + clock = GML_96KHZ; + break; + case 88200: + clock = GML_88KHZ; + break; + case 48000: + clock = GML_48KHZ | GML_SPDIF_SAMPLE_RATE1; + break; + case 44100: + clock = GML_44KHZ; + /* Professional mode */ + if (control_reg & GML_SPDIF_PRO_MODE) + clock |= GML_SPDIF_SAMPLE_RATE0; + break; + case 32000: + clock = GML_32KHZ | GML_SPDIF_SAMPLE_RATE0 | + GML_SPDIF_SAMPLE_RATE1; + break; + case 22050: + clock = GML_22KHZ; + break; + case 16000: + clock = GML_16KHZ; + break; + case 11025: + clock = GML_11KHZ; + break; + case 8000: + clock = GML_8KHZ; + break; + default: + /* If this is a non-standard rate, then the driver needs to + use Layla24's special "continuous frequency" mode */ + clock = LAYLA24_CONTINUOUS_CLOCK; + if (rate > 50000) { + base_rate = rate >> 1; + control_reg |= GML_DOUBLE_SPEED_MODE; + } else { + base_rate = rate; + } + + if (base_rate < 25000) + base_rate = 25000; + + if (wait_handshake(chip)) + return -EIO; + + chip->comm_page->sample_rate = + cpu_to_le32(LAYLA24_MAGIC_NUMBER / base_rate - 2); + + clear_handshake(chip); + send_vector(chip, DSP_VC_SET_LAYLA24_FREQUENCY_REG); + } + + control_reg |= clock; + + chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP ? */ + chip->sample_rate = rate; + DE_ACT(("set_sample_rate: %d clock %d\n", rate, control_reg)); + + return write_control_reg(chip, control_reg, FALSE); +} + + + +static int set_input_clock(struct echoaudio *chip, u16 clock) +{ + u32 control_reg, clocks_from_dsp; + + /* Mask off the clock select bits */ + control_reg = le32_to_cpu(chip->comm_page->control_register) & + GML_CLOCK_CLEAR_MASK; + clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks); + + /* Pick the new clock */ + switch (clock) { + case ECHO_CLOCK_INTERNAL: + DE_ACT(("Set Layla24 clock to INTERNAL\n")); + chip->input_clock = ECHO_CLOCK_INTERNAL; + return set_sample_rate(chip, chip->sample_rate); + case ECHO_CLOCK_SPDIF: + if (chip->digital_mode == DIGITAL_MODE_ADAT) + return -EAGAIN; + control_reg |= GML_SPDIF_CLOCK; + /* Layla24 doesn't support 96KHz S/PDIF */ + control_reg &= ~GML_DOUBLE_SPEED_MODE; + DE_ACT(("Set Layla24 clock to SPDIF\n")); + break; + case ECHO_CLOCK_WORD: + control_reg |= GML_WORD_CLOCK; + if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_WORD96) + control_reg |= GML_DOUBLE_SPEED_MODE; + else + control_reg &= ~GML_DOUBLE_SPEED_MODE; + DE_ACT(("Set Layla24 clock to WORD\n")); + break; + case ECHO_CLOCK_ADAT: + if (chip->digital_mode != DIGITAL_MODE_ADAT) + return -EAGAIN; + control_reg |= GML_ADAT_CLOCK; + control_reg &= ~GML_DOUBLE_SPEED_MODE; + DE_ACT(("Set Layla24 clock to ADAT\n")); + break; + default: + DE_ACT(("Input clock 0x%x not supported for Layla24\n", clock)); + return -EINVAL; + } + + chip->input_clock = clock; + return write_control_reg(chip, control_reg, TRUE); +} + + + +/* Depending on what digital mode you want, Layla24 needs different ASICs +loaded. This function checks the ASIC needed for the new mode and sees +if it matches the one already loaded. */ +static int switch_asic(struct echoaudio *chip, const struct firmware *asic) +{ + s8 *monitors; + + /* Check to see if this is already loaded */ + if (asic != chip->asic_code) { + monitors = kmalloc(MONITOR_ARRAY_SIZE, GFP_KERNEL); + if (! monitors) + return -ENOMEM; + + memcpy(monitors, chip->comm_page->monitors, MONITOR_ARRAY_SIZE); + memset(chip->comm_page->monitors, ECHOGAIN_MUTED, + MONITOR_ARRAY_SIZE); + + /* Load the desired ASIC */ + if (load_asic_generic(chip, DSP_FNC_LOAD_LAYLA24_EXTERNAL_ASIC, + asic) < 0) { + memcpy(chip->comm_page->monitors, monitors, + MONITOR_ARRAY_SIZE); + kfree(monitors); + return -EIO; + } + chip->asic_code = asic; + memcpy(chip->comm_page->monitors, monitors, MONITOR_ARRAY_SIZE); + kfree(monitors); + } + + return 0; +} + + + +static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode) +{ + u32 control_reg; + int err, incompatible_clock; + const struct firmware *asic; + + /* Set clock to "internal" if it's not compatible with the new mode */ + incompatible_clock = FALSE; + switch (mode) { + case DIGITAL_MODE_SPDIF_OPTICAL: + case DIGITAL_MODE_SPDIF_RCA: + if (chip->input_clock == ECHO_CLOCK_ADAT) + incompatible_clock = TRUE; + asic = &card_fw[FW_LAYLA24_2S_ASIC]; + break; + case DIGITAL_MODE_ADAT: + if (chip->input_clock == ECHO_CLOCK_SPDIF) + incompatible_clock = TRUE; + asic = &card_fw[FW_LAYLA24_2A_ASIC]; + break; + default: + DE_ACT(("Digital mode not supported: %d\n", mode)); + return -EINVAL; + } + + if (incompatible_clock) { /* Switch to 48KHz, internal */ + chip->sample_rate = 48000; + spin_lock_irq(&chip->lock); + set_input_clock(chip, ECHO_CLOCK_INTERNAL); + spin_unlock_irq(&chip->lock); + } + + /* switch_asic() can sleep */ + if (switch_asic(chip, asic) < 0) + return -EIO; + + spin_lock_irq(&chip->lock); + + /* Tweak the control register */ + control_reg = le32_to_cpu(chip->comm_page->control_register); + control_reg &= GML_DIGITAL_MODE_CLEAR_MASK; + + switch (mode) { + case DIGITAL_MODE_SPDIF_OPTICAL: + control_reg |= GML_SPDIF_OPTICAL_MODE; + break; + case DIGITAL_MODE_SPDIF_RCA: + /* GML_SPDIF_OPTICAL_MODE bit cleared */ + break; + case DIGITAL_MODE_ADAT: + control_reg |= GML_ADAT_MODE; + control_reg &= ~GML_DOUBLE_SPEED_MODE; + break; + } + + err = write_control_reg(chip, control_reg, TRUE); + spin_unlock_irq(&chip->lock); + if (err < 0) + return err; + chip->digital_mode = mode; + + DE_ACT(("set_digital_mode to %d\n", mode)); + return incompatible_clock; +} diff --git a/sound/pci/echoaudio/mia.c b/sound/pci/echoaudio/mia.c new file mode 100644 index 00000000000..be40c64263d --- /dev/null +++ b/sound/pci/echoaudio/mia.c @@ -0,0 +1,117 @@ +/* + * ALSA driver for Echoaudio soundcards. + * Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define ECHO24_FAMILY +#define ECHOCARD_MIA +#define ECHOCARD_NAME "Mia" +#define ECHOCARD_HAS_MONITOR +#define ECHOCARD_HAS_INPUT_NOMINAL_LEVEL +#define ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL +#define ECHOCARD_HAS_SUPER_INTERLEAVE +#define ECHOCARD_HAS_VMIXER +#define ECHOCARD_HAS_DIGITAL_IO +#define ECHOCARD_HAS_EXTERNAL_CLOCK +#define ECHOCARD_HAS_ADAT FALSE +#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32 +#define ECHOCARD_HAS_MIDI + +/* Pipe indexes */ +#define PX_ANALOG_OUT 0 /* 8 */ +#define PX_DIGITAL_OUT 8 /* 0 */ +#define PX_ANALOG_IN 8 /* 2 */ +#define PX_DIGITAL_IN 10 /* 2 */ +#define PX_NUM 12 + +/* Bus indexes */ +#define BX_ANALOG_OUT 0 /* 2 */ +#define BX_DIGITAL_OUT 2 /* 2 */ +#define BX_ANALOG_IN 4 /* 2 */ +#define BX_DIGITAL_IN 6 /* 2 */ +#define BX_NUM 8 + + +#include <sound/driver.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/slab.h> +#include <linux/moduleparam.h> +#include <linux/firmware.h> +#include <sound/core.h> +#include <sound/info.h> +#include <sound/control.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/asoundef.h> +#include <sound/initval.h> +#include <sound/rawmidi.h> +#include <asm/io.h> +#include <asm/atomic.h> +#include "echoaudio.h" + +#define FW_361_LOADER 0 +#define FW_MIA_DSP 1 + +static const struct firmware card_fw[] = { + {0, "loader_dsp.fw"}, + {0, "mia_dsp.fw"} +}; + +static struct pci_device_id snd_echo_ids[] = { + {0x1057, 0x3410, 0xECC0, 0x0080, 0, 0, 0}, /* DSP 56361 Mia rev.0 */ + {0x1057, 0x3410, 0xECC0, 0x0081, 0, 0, 0}, /* DSP 56361 Mia rev.1 */ + {0,} +}; + +static struct snd_pcm_hardware pcm_hardware_skel = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START, + .formats = SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S32_BE, + .rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000, + .rate_min = 8000, + .rate_max = 96000, + .channels_min = 1, + .channels_max = 8, + .buffer_bytes_max = 262144, + .period_bytes_min = 32, + .period_bytes_max = 131072, + .periods_min = 2, + .periods_max = 220, + /* One page (4k) contains 512 instructions. I don't know if the hw + supports lists longer than this. In this case periods_max=220 is a + safe limit to make sure the list never exceeds 512 instructions. */ +}; + + +#include "mia_dsp.c" +#include "echoaudio_dsp.c" +#include "echoaudio.c" +#include "midi.c" diff --git a/sound/pci/echoaudio/mia_dsp.c b/sound/pci/echoaudio/mia_dsp.c new file mode 100644 index 00000000000..891c7051909 --- /dev/null +++ b/sound/pci/echoaudio/mia_dsp.c @@ -0,0 +1,229 @@ +/**************************************************************************** + + Copyright Echo Digital Audio Corporation (c) 1998 - 2004 + All rights reserved + www.echoaudio.com + + This file is part of Echo Digital Audio's generic driver library. + + Echo Digital Audio's generic driver library is free software; + you can redistribute it and/or modify it under the terms of + the GNU General Public License as published by the Free Software + Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + ************************************************************************* + + Translation from C++ and adaptation for use in ALSA-Driver + were made by Giuliano Pochini <pochini@shiny.it> + +****************************************************************************/ + + +static int set_input_clock(struct echoaudio *chip, u16 clock); +static int set_professional_spdif(struct echoaudio *chip, char prof); +static int update_flags(struct echoaudio *chip); +static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe, + int gain); +static int update_vmixer_level(struct echoaudio *chip); + + +static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) +{ + int err; + + DE_INIT(("init_hw() - Mia\n")); + snd_assert((subdevice_id & 0xfff0) == MIA, return -ENODEV); + + if ((err = init_dsp_comm_page(chip))) { + DE_INIT(("init_hw - could not initialize DSP comm page\n")); + return err; + } + + chip->device_id = device_id; + chip->subdevice_id = subdevice_id; + chip->bad_board = TRUE; + chip->dsp_code_to_load = &card_fw[FW_MIA_DSP]; + /* Since this card has no ASIC, mark it as loaded so everything + works OK */ + chip->asic_loaded = TRUE; + if ((subdevice_id & 0x0000f) == MIA_MIDI_REV) + chip->has_midi = TRUE; + chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL | + ECHO_CLOCK_BIT_SPDIF; + + if ((err = load_firmware(chip)) < 0) + return err; + chip->bad_board = FALSE; + + if ((err = init_line_levels(chip))) + return err; + + /* Default routing of the virtual channels: vchannels 0-3 go to analog + outputs and vchannels 4-7 go to S/PDIF outputs */ + set_vmixer_gain(chip, 0, 0, 0); + set_vmixer_gain(chip, 1, 1, 0); + set_vmixer_gain(chip, 0, 2, 0); + set_vmixer_gain(chip, 1, 3, 0); + set_vmixer_gain(chip, 2, 4, 0); + set_vmixer_gain(chip, 3, 5, 0); + set_vmixer_gain(chip, 2, 6, 0); + set_vmixer_gain(chip, 3, 7, 0); + err = update_vmixer_level(chip); + + DE_INIT(("init_hw done\n")); + return err; +} + + + +static u32 detect_input_clocks(const struct echoaudio *chip) +{ + u32 clocks_from_dsp, clock_bits; + + /* Map the DSP clock detect bits to the generic driver clock + detect bits */ + clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks); + + clock_bits = ECHO_CLOCK_BIT_INTERNAL; + + if (clocks_from_dsp & GLDM_CLOCK_DETECT_BIT_SPDIF) + clock_bits |= ECHO_CLOCK_BIT_SPDIF; + + return clock_bits; +} + + + +/* The Mia has no ASIC. Just do nothing */ +static int load_asic(struct echoaudio *chip) +{ + return 0; +} + + + +static int set_sample_rate(struct echoaudio *chip, u32 rate) +{ + u32 control_reg; + + switch (rate) { + case 96000: + control_reg = MIA_96000; + break; + case 88200: + control_reg = MIA_88200; + break; + case 48000: + control_reg = MIA_48000; + break; + case 44100: + control_reg = MIA_44100; + break; + case 32000: + control_reg = MIA_32000; + break; + default: + DE_ACT(("set_sample_rate: %d invalid!\n", rate)); + return -EINVAL; + } + + /* Override the clock setting if this Mia is set to S/PDIF clock */ + if (chip->input_clock == ECHO_CLOCK_SPDIF) + control_reg |= MIA_SPDIF; + + /* Set the control register if it has changed */ + if (control_reg != le32_to_cpu(chip->comm_page->control_register)) { + if (wait_handshake(chip)) + return -EIO; + + chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP */ + chip->comm_page->control_register = cpu_to_le32(control_reg); + chip->sample_rate = rate; + + clear_handshake(chip); + return send_vector(chip, DSP_VC_UPDATE_CLOCKS); + } + return 0; +} + + + +static int set_input_clock(struct echoaudio *chip, u16 clock) +{ + DE_ACT(("set_input_clock(%d)\n", clock)); + snd_assert(clock == ECHO_CLOCK_INTERNAL || clock == ECHO_CLOCK_SPDIF, + return -EINVAL); + + chip->input_clock = clock; + return set_sample_rate(chip, chip->sample_rate); +} + + + +/* This function routes the sound from a virtual channel to a real output */ +static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe, + int gain) +{ + int index; + + snd_assert(pipe < num_pipes_out(chip) && + output < num_busses_out(chip), return -EINVAL); + + if (wait_handshake(chip)) + return -EIO; + + chip->vmixer_gain[output][pipe] = gain; + index = output * num_pipes_out(chip) + pipe; + chip->comm_page->vmixer[index] = gain; + + DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain)); + return 0; +} + + + +/* Tell the DSP to read and update virtual mixer levels in comm page. */ +static int update_vmixer_level(struct echoaudio *chip) +{ + if (wait_handshake(chip)) + return -EIO; + clear_handshake(chip); + return send_vector(chip, DSP_VC_SET_VMIXER_GAIN); +} + + + +/* Tell the DSP to reread the flags from the comm page */ +static int update_flags(struct echoaudio *chip) +{ + if (wait_handshake(chip)) + return -EIO; + clear_handshake(chip); + return send_vector(chip, DSP_VC_UPDATE_FLAGS); +} + + + +static int set_professional_spdif(struct echoaudio *chip, char prof) +{ + DE_ACT(("set_professional_spdif %d\n", prof)); + if (prof) + chip->comm_page->flags |= + __constant_cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF); + else + chip->comm_page->flags &= + ~__constant_cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF); + chip->professional_spdif = prof; + return update_flags(chip); +} + diff --git a/sound/pci/echoaudio/midi.c b/sound/pci/echoaudio/midi.c new file mode 100644 index 00000000000..e31f0f11e3a --- /dev/null +++ b/sound/pci/echoaudio/midi.c @@ -0,0 +1,327 @@ +/**************************************************************************** + + Copyright Echo Digital Audio Corporation (c) 1998 - 2004 + All rights reserved + www.echoaudio.com + + This file is part of Echo Digital Audio's generic driver library. + + Echo Digital Audio's generic driver library is free software; + you can redistribute it and/or modify it under the terms of + the GNU General Public License as published by the Free Software + Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + ************************************************************************* + + Translation from C++ and adaptation for use in ALSA-Driver + were made by Giuliano Pochini <pochini@shiny.it> + +****************************************************************************/ + + +/****************************************************************************** + MIDI lowlevel code +******************************************************************************/ + +/* Start and stop Midi input */ +static int enable_midi_input(struct echoaudio *chip, char enable) +{ + DE_MID(("enable_midi_input(%d)\n", enable)); + + if (wait_handshake(chip)) + return -EIO; + + if (enable) { + chip->mtc_state = MIDI_IN_STATE_NORMAL; + chip->comm_page->flags |= + __constant_cpu_to_le32(DSP_FLAG_MIDI_INPUT); + } else + chip->comm_page->flags &= + ~__constant_cpu_to_le32(DSP_FLAG_MIDI_INPUT); + + clear_handshake(chip); + return send_vector(chip, DSP_VC_UPDATE_FLAGS); +} + + + +/* Send a buffer full of MIDI data to the DSP +Returns how many actually written or < 0 on error */ +static int write_midi(struct echoaudio *chip, u8 *data, int bytes) +{ + snd_assert(bytes > 0 && bytes < MIDI_OUT_BUFFER_SIZE, return -EINVAL); + + if (wait_handshake(chip)) + return -EIO; + + /* HF4 indicates that it is safe to write MIDI output data */ + if (! (get_dsp_register(chip, CHI32_STATUS_REG) & CHI32_STATUS_REG_HF4)) + return 0; + + chip->comm_page->midi_output[0] = bytes; + memcpy(&chip->comm_page->midi_output[1], data, bytes); + chip->comm_page->midi_out_free_count = 0; + clear_handshake(chip); + send_vector(chip, DSP_VC_MIDI_WRITE); + DE_MID(("write_midi: %d\n", bytes)); + return bytes; +} + + + +/* Run the state machine for MIDI input data +MIDI time code sync isn't supported by this code right now, but you still need +this state machine to parse the incoming MIDI data stream. Every time the DSP +sees a 0xF1 byte come in, it adds the DSP sample position to the MIDI data +stream. The DSP sample position is represented as a 32 bit unsigned value, +with the high 16 bits first, followed by the low 16 bits. Since these aren't +real MIDI bytes, the following logic is needed to skip them. */ +static inline int mtc_process_data(struct echoaudio *chip, short midi_byte) +{ + switch (chip->mtc_state) { + case MIDI_IN_STATE_NORMAL: + if (midi_byte == 0xF1) + chip->mtc_state = MIDI_IN_STATE_TS_HIGH; + break; + case MIDI_IN_STATE_TS_HIGH: + chip->mtc_state = MIDI_IN_STATE_TS_LOW; + return MIDI_IN_SKIP_DATA; + break; + case MIDI_IN_STATE_TS_LOW: + chip->mtc_state = MIDI_IN_STATE_F1_DATA; + return MIDI_IN_SKIP_DATA; + break; + case MIDI_IN_STATE_F1_DATA: + chip->mtc_state = MIDI_IN_STATE_NORMAL; + break; + } + return 0; +} + + + +/* This function is called from the IRQ handler and it reads the midi data +from the DSP's buffer. It returns the number of bytes received. */ +static int midi_service_irq(struct echoaudio *chip) +{ + short int count, midi_byte, i, received; + + /* The count is at index 0, followed by actual data */ + count = le16_to_cpu(chip->comm_page->midi_input[0]); + + snd_assert(count < MIDI_IN_BUFFER_SIZE, return 0); + + /* Get the MIDI data from the comm page */ + i = 1; + received = 0; + for (i = 1; i <= count; i++) { + /* Get the MIDI byte */ + midi_byte = le16_to_cpu(chip->comm_page->midi_input[i]); + + /* Parse the incoming MIDI stream. The incoming MIDI data + consists of MIDI bytes and timestamps for the MIDI time code + 0xF1 bytes. mtc_process_data() is a little state machine that + parses the stream. If you get MIDI_IN_SKIP_DATA back, then + this is a timestamp byte, not a MIDI byte, so don't store it + in the MIDI input buffer. */ + if (mtc_process_data(chip, midi_byte) == MIDI_IN_SKIP_DATA) + continue; + + chip->midi_buffer[received++] = (u8)midi_byte; + } + + return received; +} + + + + +/****************************************************************************** + MIDI interface +******************************************************************************/ + +static int snd_echo_midi_input_open(struct snd_rawmidi_substream *substream) +{ + struct echoaudio *chip = substream->rmidi->private_data; + + chip->midi_in = substream; + DE_MID(("rawmidi_iopen\n")); + return 0; +} + + + +static void snd_echo_midi_input_trigger(struct snd_rawmidi_substream *substream, + int up) +{ + struct echoaudio *chip = substream->rmidi->private_data; + + if (up != chip->midi_input_enabled) { + spin_lock_irq(&chip->lock); + enable_midi_input(chip, up); + spin_unlock_irq(&chip->lock); + chip->midi_input_enabled = up; + } +} + + + +static int snd_echo_midi_input_close(struct snd_rawmidi_substream *substream) +{ + struct echoaudio *chip = substream->rmidi->private_data; + + chip->midi_in = NULL; + DE_MID(("rawmidi_iclose\n")); + return 0; +} + + + +static int snd_echo_midi_output_open(struct snd_rawmidi_substream *substream) +{ + struct echoaudio *chip = substream->rmidi->private_data; + + chip->tinuse = 0; + chip->midi_full = 0; + chip->midi_out = substream; + DE_MID(("rawmidi_oopen\n")); + return 0; +} + + + +static void snd_echo_midi_output_write(unsigned long data) +{ + struct echoaudio *chip = (struct echoaudio *)data; + unsigned long flags; + int bytes, sent, time; + unsigned char buf[MIDI_OUT_BUFFER_SIZE - 1]; + + DE_MID(("snd_echo_midi_output_write\n")); + /* No interrupts are involved: we have to check at regular intervals + if the card's output buffer has room for new data. */ + sent = bytes = 0; + spin_lock_irqsave(&chip->lock, flags); + chip->midi_full = 0; + if (chip->midi_out && !snd_rawmidi_transmit_empty(chip->midi_out)) { + bytes = snd_rawmidi_transmit_peek(chip->midi_out, buf, + MIDI_OUT_BUFFER_SIZE - 1); + DE_MID(("Try to send %d bytes...\n", bytes)); + sent = write_midi(chip, buf, bytes); + if (sent < 0) { + snd_printk(KERN_ERR "write_midi() error %d\n", sent); + /* retry later */ + sent = 9000; + chip->midi_full = 1; + } else if (sent > 0) { + DE_MID(("%d bytes sent\n", sent)); + snd_rawmidi_transmit_ack(chip->midi_out, sent); + } else { + /* Buffer is full. DSP's internal buffer is 64 (128 ?) + bytes long. Let's wait until half of them are sent */ + DE_MID(("Full\n")); + sent = 32; + chip->midi_full = 1; + } + } + + /* We restart the timer only if there is some data left to send */ + if (!snd_rawmidi_transmit_empty(chip->midi_out) && chip->tinuse) { + /* The timer will expire slightly after the data has been + sent */ + time = (sent << 3) / 25 + 1; /* 8/25=0.32ms to send a byte */ + mod_timer(&chip->timer, jiffies + (time * HZ + 999) / 1000); + DE_MID(("Timer armed(%d)\n", ((time * HZ + 999) / 1000))); + } + spin_unlock_irqrestore(&chip->lock, flags); +} + + + +static void snd_echo_midi_output_trigger(struct snd_rawmidi_substream *substream, + int up) +{ + struct echoaudio *chip = substream->rmidi->private_data; + + DE_MID(("snd_echo_midi_output_trigger(%d)\n", up)); + spin_lock_irq(&chip->lock); + if (up) { + if (!chip->tinuse) { + init_timer(&chip->timer); + chip->timer.function = snd_echo_midi_output_write; + chip->timer.data = (unsigned long)chip; + chip->tinuse = 1; + } + } else { + if (chip->tinuse) { + del_timer(&chip->timer); + chip->tinuse = 0; + DE_MID(("Timer removed\n")); + } + } + spin_unlock_irq(&chip->lock); + + if (up && !chip->midi_full) + snd_echo_midi_output_write((unsigned long)chip); +} + + + +static int snd_echo_midi_output_close(struct snd_rawmidi_substream *substream) +{ + struct echoaudio *chip = substream->rmidi->private_data; + + chip->midi_out = NULL; + DE_MID(("rawmidi_oclose\n")); + return 0; +} + + + +static struct snd_rawmidi_ops snd_echo_midi_input = { + .open = snd_echo_midi_input_open, + .close = snd_echo_midi_input_close, + .trigger = snd_echo_midi_input_trigger, +}; + +static struct snd_rawmidi_ops snd_echo_midi_output = { + .open = snd_echo_midi_output_open, + .close = snd_echo_midi_output_close, + .trigger = snd_echo_midi_output_trigger, +}; + + + +/* <--snd_echo_probe() */ +static int __devinit snd_echo_midi_create(struct snd_card *card, + struct echoaudio *chip) +{ + int err; + + if ((err = snd_rawmidi_new(card, card->shortname, 0, 1, 1, + &chip->rmidi)) < 0) + return err; + + strcpy(chip->rmidi->name, card->shortname); + chip->rmidi->private_data = chip; + + snd_rawmidi_set_ops(chip->rmidi, SNDRV_RAWMIDI_STREAM_INPUT, + &snd_echo_midi_input); + snd_rawmidi_set_ops(chip->rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, + &snd_echo_midi_output); + + chip->rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | + SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; + DE_INIT(("MIDI ok\n")); + return 0; +} diff --git a/sound/pci/echoaudio/mona.c b/sound/pci/echoaudio/mona.c new file mode 100644 index 00000000000..5dc512add37 --- /dev/null +++ b/sound/pci/echoaudio/mona.c @@ -0,0 +1,129 @@ +/* + * ALSA driver for Echoaudio soundcards. + * Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define ECHO24_FAMILY +#define ECHOCARD_MONA +#define ECHOCARD_NAME "Mona" +#define ECHOCARD_HAS_MONITOR +#define ECHOCARD_HAS_ASIC +#define ECHOCARD_HAS_SUPER_INTERLEAVE +#define ECHOCARD_HAS_DIGITAL_IO +#define ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE +#define ECHOCARD_HAS_DIGITAL_MODE_SWITCH +#define ECHOCARD_HAS_EXTERNAL_CLOCK +#define ECHOCARD_HAS_ADAT 6 +#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32 + +/* Pipe indexes */ +#define PX_ANALOG_OUT 0 /* 6 */ +#define PX_DIGITAL_OUT 6 /* 8 */ +#define PX_ANALOG_IN 14 /* 4 */ +#define PX_DIGITAL_IN 18 /* 8 */ +#define PX_NUM 26 + +/* Bus indexes */ +#define BX_ANALOG_OUT 0 /* 6 */ +#define BX_DIGITAL_OUT 6 /* 8 */ +#define BX_ANALOG_IN 14 /* 4 */ +#define BX_DIGITAL_IN 18 /* 8 */ +#define BX_NUM 26 + + +#include <sound/driver.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/slab.h> +#include <linux/moduleparam.h> +#include <linux/firmware.h> +#include <sound/core.h> +#include <sound/info.h> +#include <sound/control.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/asoundef.h> +#include <sound/initval.h> +#include <asm/io.h> +#include <asm/atomic.h> +#include "echoaudio.h" + +#define FW_361_LOADER 0 +#define FW_MONA_301_DSP 1 +#define FW_MONA_361_DSP 2 +#define FW_MONA_301_1_ASIC48 3 +#define FW_MONA_301_1_ASIC96 4 +#define FW_MONA_361_1_ASIC48 5 +#define FW_MONA_361_1_ASIC96 6 +#define FW_MONA_2_ASIC 7 + +static const struct firmware card_fw[] = { + {0, "loader_dsp.fw"}, + {0, "mona_301_dsp.fw"}, + {0, "mona_361_dsp.fw"}, + {0, "mona_301_1_asic_48.fw"}, + {0, "mona_301_1_asic_96.fw"}, + {0, "mona_361_1_asic_48.fw"}, + {0, "mona_361_1_asic_96.fw"}, + {0, "mona_2_asic.fw"} +}; + +static struct pci_device_id snd_echo_ids[] = { + {0x1057, 0x1801, 0xECC0, 0x0070, 0, 0, 0}, /* DSP 56301 Mona rev.0 */ + {0x1057, 0x1801, 0xECC0, 0x0071, 0, 0, 0}, /* DSP 56301 Mona rev.1 */ + {0x1057, 0x1801, 0xECC0, 0x0072, 0, 0, 0}, /* DSP 56301 Mona rev.2 */ + {0x1057, 0x3410, 0xECC0, 0x0070, 0, 0, 0}, /* DSP 56361 Mona rev.0 */ + {0x1057, 0x3410, 0xECC0, 0x0071, 0, 0, 0}, /* DSP 56361 Mona rev.1 */ + {0x1057, 0x3410, 0xECC0, 0x0072, 0, 0, 0}, /* DSP 56361 Mona rev.2 */ + {0,} +}; + +static struct snd_pcm_hardware pcm_hardware_skel = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START, + .formats = SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S32_BE, + .rates = SNDRV_PCM_RATE_8000_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000, + .rate_min = 8000, + .rate_max = 96000, + .channels_min = 1, + .channels_max = 8, + .buffer_bytes_max = 262144, + .period_bytes_min = 32, + .period_bytes_max = 131072, + .periods_min = 2, + .periods_max = 220, + /* One page (4k) contains 512 instructions. I don't know if the hw + supports lists longer than this. In this case periods_max=220 is a + safe limit to make sure the list never exceeds 512 instructions. */ +}; + + +#include "mona_dsp.c" +#include "echoaudio_dsp.c" +#include "echoaudio_gml.c" +#include "echoaudio.c" diff --git a/sound/pci/echoaudio/mona_dsp.c b/sound/pci/echoaudio/mona_dsp.c new file mode 100644 index 00000000000..c0b4bf0be7d --- /dev/null +++ b/sound/pci/echoaudio/mona_dsp.c @@ -0,0 +1,428 @@ +/**************************************************************************** + + Copyright Echo Digital Audio Corporation (c) 1998 - 2004 + All rights reserved + www.echoaudio.com + + This file is part of Echo Digital Audio's generic driver library. + + Echo Digital Audio's generic driver library is free software; + you can redistribute it and/or modify it under the terms of + the GNU General Public License as published by the Free Software + Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + ************************************************************************* + + Translation from C++ and adaptation for use in ALSA-Driver + were made by Giuliano Pochini <pochini@shiny.it> + +****************************************************************************/ + + +static int write_control_reg(struct echoaudio *chip, u32 value, char force); +static int set_input_clock(struct echoaudio *chip, u16 clock); +static int set_professional_spdif(struct echoaudio *chip, char prof); +static int set_digital_mode(struct echoaudio *chip, u8 mode); +static int load_asic_generic(struct echoaudio *chip, u32 cmd, + const struct firmware *asic); +static int check_asic_status(struct echoaudio *chip); + + +static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) +{ + int err; + + DE_INIT(("init_hw() - Mona\n")); + snd_assert((subdevice_id & 0xfff0) == MONA, return -ENODEV); + + if ((err = init_dsp_comm_page(chip))) { + DE_INIT(("init_hw - could not initialize DSP comm page\n")); + return err; + } + + chip->device_id = device_id; + chip->subdevice_id = subdevice_id; + chip->bad_board = TRUE; + chip->input_clock_types = + ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF | + ECHO_CLOCK_BIT_WORD | ECHO_CLOCK_BIT_ADAT; + chip->digital_modes = + ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA | + ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL | + ECHOCAPS_HAS_DIGITAL_MODE_ADAT; + + /* Mona comes in both '301 and '361 flavors */ + if (chip->device_id == DEVICE_ID_56361) + chip->dsp_code_to_load = &card_fw[FW_MONA_361_DSP]; + else + chip->dsp_code_to_load = &card_fw[FW_MONA_301_DSP]; + + chip->digital_mode = DIGITAL_MODE_SPDIF_RCA; + chip->professional_spdif = FALSE; + chip->digital_in_automute = TRUE; + + if ((err = load_firmware(chip)) < 0) + return err; + chip->bad_board = FALSE; + + if ((err = init_line_levels(chip)) < 0) + return err; + + err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA); + snd_assert(err >= 0, return err); + err = set_professional_spdif(chip, TRUE); + + DE_INIT(("init_hw done\n")); + return err; +} + + + +static u32 detect_input_clocks(const struct echoaudio *chip) +{ + u32 clocks_from_dsp, clock_bits; + + /* Map the DSP clock detect bits to the generic driver clock + detect bits */ + clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks); + + clock_bits = ECHO_CLOCK_BIT_INTERNAL; + + if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_SPDIF) + clock_bits |= ECHO_CLOCK_BIT_SPDIF; + + if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_ADAT) + clock_bits |= ECHO_CLOCK_BIT_ADAT; + + if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_WORD) + clock_bits |= ECHO_CLOCK_BIT_WORD; + + return clock_bits; +} + + + +/* Mona has an ASIC on the PCI card and another ASIC in the external box; +both need to be loaded. */ +static int load_asic(struct echoaudio *chip) +{ + u32 control_reg; + int err; + const struct firmware *asic; + + if (chip->asic_loaded) + return 0; + + mdelay(10); + + if (chip->device_id == DEVICE_ID_56361) + asic = &card_fw[FW_MONA_361_1_ASIC48]; + else + asic = &card_fw[FW_MONA_301_1_ASIC48]; + + err = load_asic_generic(chip, DSP_FNC_LOAD_MONA_PCI_CARD_ASIC, asic); + if (err < 0) + return err; + + chip->asic_code = asic; + mdelay(10); + + /* Do the external one */ + err = load_asic_generic(chip, DSP_FNC_LOAD_MONA_EXTERNAL_ASIC, + &card_fw[FW_MONA_2_ASIC]); + if (err < 0) + return err; + + mdelay(10); + err = check_asic_status(chip); + + /* Set up the control register if the load succeeded - + 48 kHz, internal clock, S/PDIF RCA mode */ + if (!err) { + control_reg = GML_CONVERTER_ENABLE | GML_48KHZ; + err = write_control_reg(chip, control_reg, TRUE); + } + + return err; +} + + + +/* Depending on what digital mode you want, Mona needs different ASICs +loaded. This function checks the ASIC needed for the new mode and sees +if it matches the one already loaded. */ +static int switch_asic(struct echoaudio *chip, char double_speed) +{ + const struct firmware *asic; + int err; + + /* Check the clock detect bits to see if this is + a single-speed clock or a double-speed clock; load + a new ASIC if necessary. */ + if (chip->device_id == DEVICE_ID_56361) { + if (double_speed) + asic = &card_fw[FW_MONA_361_1_ASIC96]; + else + asic = &card_fw[FW_MONA_361_1_ASIC48]; + } else { + if (double_speed) + asic = &card_fw[FW_MONA_301_1_ASIC96]; + else + asic = &card_fw[FW_MONA_301_1_ASIC48]; + } + + if (asic != chip->asic_code) { + /* Load the desired ASIC */ + err = load_asic_generic(chip, DSP_FNC_LOAD_MONA_PCI_CARD_ASIC, + asic); + if (err < 0) + return err; + chip->asic_code = asic; + } + + return 0; +} + + + +static int set_sample_rate(struct echoaudio *chip, u32 rate) +{ + u32 control_reg, clock; + const struct firmware *asic; + char force_write; + + /* Only set the clock for internal mode. */ + if (chip->input_clock != ECHO_CLOCK_INTERNAL) { + DE_ACT(("set_sample_rate: Cannot set sample rate - " + "clock not set to CLK_CLOCKININTERNAL\n")); + /* Save the rate anyhow */ + chip->comm_page->sample_rate = cpu_to_le32(rate); + chip->sample_rate = rate; + return 0; + } + + /* Now, check to see if the required ASIC is loaded */ + if (rate >= 88200) { + if (chip->digital_mode == DIGITAL_MODE_ADAT) + return -EINVAL; + if (chip->device_id == DEVICE_ID_56361) + asic = &card_fw[FW_MONA_361_1_ASIC96]; + else + asic = &card_fw[FW_MONA_301_1_ASIC96]; + } else { + if (chip->device_id == DEVICE_ID_56361) + asic = &card_fw[FW_MONA_361_1_ASIC48]; + else + asic = &card_fw[FW_MONA_301_1_ASIC48]; + } + + force_write = 0; + if (asic != chip->asic_code) { + int err; + /* Load the desired ASIC (load_asic_generic() can sleep) */ + spin_unlock_irq(&chip->lock); + err = load_asic_generic(chip, DSP_FNC_LOAD_MONA_PCI_CARD_ASIC, + asic); + spin_lock_irq(&chip->lock); + + if (err < 0) + return err; + chip->asic_code = asic; + force_write = 1; + } + + /* Compute the new control register value */ + clock = 0; + control_reg = le32_to_cpu(chip->comm_page->control_register); + control_reg &= GML_CLOCK_CLEAR_MASK; + control_reg &= GML_SPDIF_RATE_CLEAR_MASK; + + switch (rate) { + case 96000: + clock = GML_96KHZ; + break; + case 88200: + clock = GML_88KHZ; + break; + case 48000: + clock = GML_48KHZ | GML_SPDIF_SAMPLE_RATE1; + break; + case 44100: + clock = GML_44KHZ; + /* Professional mode */ + if (control_reg & GML_SPDIF_PRO_MODE) + clock |= GML_SPDIF_SAMPLE_RATE0; + break; + case 32000: + clock = GML_32KHZ | GML_SPDIF_SAMPLE_RATE0 | + GML_SPDIF_SAMPLE_RATE1; + break; + case 22050: + clock = GML_22KHZ; + break; + case 16000: + clock = GML_16KHZ; + break; + case 11025: + clock = GML_11KHZ; + break; + case 8000: + clock = GML_8KHZ; + break; + default: + DE_ACT(("set_sample_rate: %d invalid!\n", rate)); + return -EINVAL; + } + + control_reg |= clock; + + chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP */ + chip->sample_rate = rate; + DE_ACT(("set_sample_rate: %d clock %d\n", rate, clock)); + + return write_control_reg(chip, control_reg, force_write); +} + + + +static int set_input_clock(struct echoaudio *chip, u16 clock) +{ + u32 control_reg, clocks_from_dsp; + int err; + + DE_ACT(("set_input_clock:\n")); + + /* Prevent two simultaneous calls to switch_asic() */ + if (atomic_read(&chip->opencount)) + return -EAGAIN; + + /* Mask off the clock select bits */ + control_reg = le32_to_cpu(chip->comm_page->control_register) & + GML_CLOCK_CLEAR_MASK; + clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks); + + switch (clock) { + case ECHO_CLOCK_INTERNAL: + DE_ACT(("Set Mona clock to INTERNAL\n")); + chip->input_clock = ECHO_CLOCK_INTERNAL; + return set_sample_rate(chip, chip->sample_rate); + case ECHO_CLOCK_SPDIF: + if (chip->digital_mode == DIGITAL_MODE_ADAT) + return -EAGAIN; + spin_unlock_irq(&chip->lock); + err = switch_asic(chip, clocks_from_dsp & + GML_CLOCK_DETECT_BIT_SPDIF96); + spin_lock_irq(&chip->lock); + if (err < 0) + return err; + DE_ACT(("Set Mona clock to SPDIF\n")); + control_reg |= GML_SPDIF_CLOCK; + if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_SPDIF96) + control_reg |= GML_DOUBLE_SPEED_MODE; + else + control_reg &= ~GML_DOUBLE_SPEED_MODE; + break; + case ECHO_CLOCK_WORD: + DE_ACT(("Set Mona clock to WORD\n")); + spin_unlock_irq(&chip->lock); + err = switch_asic(chip, clocks_from_dsp & + GML_CLOCK_DETECT_BIT_WORD96); + spin_lock_irq(&chip->lock); + if (err < 0) + return err; + control_reg |= GML_WORD_CLOCK; + if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_WORD96) + control_reg |= GML_DOUBLE_SPEED_MODE; + else + control_reg &= ~GML_DOUBLE_SPEED_MODE; + break; + case ECHO_CLOCK_ADAT: + DE_ACT(("Set Mona clock to ADAT\n")); + if (chip->digital_mode != DIGITAL_MODE_ADAT) + return -EAGAIN; + control_reg |= GML_ADAT_CLOCK; + control_reg &= ~GML_DOUBLE_SPEED_MODE; + break; + default: + DE_ACT(("Input clock 0x%x not supported for Mona\n", clock)); + return -EINVAL; + } + + chip->input_clock = clock; + return write_control_reg(chip, control_reg, TRUE); +} + + + +static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode) +{ + u32 control_reg; + int err, incompatible_clock; + + /* Set clock to "internal" if it's not compatible with the new mode */ + incompatible_clock = FALSE; + switch (mode) { + case DIGITAL_MODE_SPDIF_OPTICAL: + case DIGITAL_MODE_SPDIF_RCA: + if (chip->input_clock == ECHO_CLOCK_ADAT) + incompatible_clock = TRUE; + break; + case DIGITAL_MODE_ADAT: + if (chip->input_clock == ECHO_CLOCK_SPDIF) + incompatible_clock = TRUE; + break; + default: + DE_ACT(("Digital mode not supported: %d\n", mode)); + return -EINVAL; + } + + spin_lock_irq(&chip->lock); + + if (incompatible_clock) { /* Switch to 48KHz, internal */ + chip->sample_rate = 48000; + set_input_clock(chip, ECHO_CLOCK_INTERNAL); + } + + /* Clear the current digital mode */ + control_reg = le32_to_cpu(chip->comm_page->control_register); + control_reg &= GML_DIGITAL_MODE_CLEAR_MASK; + + /* Tweak the control reg */ + switch (mode) { + case DIGITAL_MODE_SPDIF_OPTICAL: + control_reg |= GML_SPDIF_OPTICAL_MODE; + break; + case DIGITAL_MODE_SPDIF_RCA: + /* GML_SPDIF_OPTICAL_MODE bit cleared */ + break; + case DIGITAL_MODE_ADAT: + /* If the current ASIC is the 96KHz ASIC, switch the ASIC + and set to 48 KHz */ + if (chip->asic_code == &card_fw[FW_MONA_361_1_ASIC96] || + chip->asic_code == &card_fw[FW_MONA_301_1_ASIC96]) { + set_sample_rate(chip, 48000); + } + control_reg |= GML_ADAT_MODE; + control_reg &= ~GML_DOUBLE_SPEED_MODE; + break; + } + + err = write_control_reg(chip, control_reg, FALSE); + spin_unlock_irq(&chip->lock); + if (err < 0) + return err; + chip->digital_mode = mode; + + DE_ACT(("set_digital_mode to %d\n", mode)); + return incompatible_clock; +} diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c index 42b11ba1d21..549673ea14a 100644 --- a/sound/pci/emu10k1/emu10k1.c +++ b/sound/pci/emu10k1/emu10k1.c @@ -46,13 +46,13 @@ MODULE_SUPPORTED_DEVICE("{{Creative Labs,SB Live!/PCI512/E-mu APS}," static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ -static int extin[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; -static int extout[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +static int extin[SNDRV_CARDS]; +static int extout[SNDRV_CARDS]; static int seq_ports[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4}; static int max_synth_voices[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 64}; static int max_buffer_size[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 128}; -static int enable_ir[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; -static uint subsystem[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; /* Force card subsystem model */ +static int enable_ir[SNDRV_CARDS]; +static uint subsystem[SNDRV_CARDS]; /* Force card subsystem model */ module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for the EMU10K1 soundcard."); diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index 6bfa08436ef..42a358f989c 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -777,14 +777,6 @@ static int snd_emu10k1_dev_free(struct snd_device *device) static struct snd_emu_chip_details emu_chip_details[] = { /* Audigy 2 Value AC3 out does not work yet. Need to find out how to turn off interpolators.*/ - /* Audigy4 SB0400 */ - {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x10211102, - .driver = "Audigy2", .name = "Audigy 4 [SB0400]", - .id = "Audigy2", - .emu10k2_chip = 1, - .ca0108_chip = 1, - .spk71 = 1, - .ac97_chip = 1} , /* Tested by James@superbug.co.uk 3rd July 2005 */ /* DSP: CA0108-IAT * DAC: CS4382-KQ @@ -799,13 +791,59 @@ static struct snd_emu_chip_details emu_chip_details[] = { .ca0108_chip = 1, .spk71 = 1, .ac97_chip = 1} , + /* Audigy4 (Not PRO) SB0610 */ + /* Tested by James@superbug.co.uk 4th April 2006 */ + /* A_IOCFG bits + * Output + * 0: ? + * 1: ? + * 2: ? + * 3: 0 - Digital Out, 1 - Line in + * 4: ? + * 5: ? + * 6: ? + * 7: ? + * Input + * 8: ? + * 9: ? + * A: Green jack sense (Front) + * B: ? + * C: Black jack sense (Rear/Side Right) + * D: Yellow jack sense (Center/LFE/Side Left) + * E: ? + * F: ? + * + * Digital Out/Line in switch using A_IOCFG bit 3 (0x08) + * 0 - Digital Out + * 1 - Line in + */ + /* Mic input not tested. + * Analog CD input not tested + * Digital Out not tested. + * Line in working. + * Audio output 5.1 working. Side outputs not working. + */ + /* DSP: CA10300-IAT LF + * DAC: Cirrus Logic CS4382-KQZ + * ADC: Philips 1361T + * AC97: Sigmatel STAC9750 + * CA0151: None + */ + {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x10211102, + .driver = "Audigy2", .name = "Audigy 4 [SB0610]", + .id = "Audigy2", + .emu10k2_chip = 1, + .ca0108_chip = 1, + .spk71 = 1, + .adc_1361t = 1, /* 24 bit capture instead of 16bit */ + .ac97_chip = 1} , /* Audigy 2 ZS Notebook Cardbus card.*/ /* Tested by James@superbug.co.uk 22th December 2005 */ /* Audio output 7.1/Headphones working. * Digital output working. (AC3 not checked, only PCM) * Audio inputs not tested. */ - /* DSP: Tiny2 + /* DSP: Tina2 * DAC: Wolfson WM8768/WM8568 * ADC: Wolfson WM8775 * AC97: None @@ -1421,16 +1459,3 @@ void snd_emu10k1_resume_regs(struct snd_emu10k1 *emu) } } #endif - -/* memory.c */ -EXPORT_SYMBOL(snd_emu10k1_synth_alloc); -EXPORT_SYMBOL(snd_emu10k1_synth_free); -EXPORT_SYMBOL(snd_emu10k1_synth_bzero); -EXPORT_SYMBOL(snd_emu10k1_synth_copy_from_user); -EXPORT_SYMBOL(snd_emu10k1_memblk_map); -/* voice.c */ -EXPORT_SYMBOL(snd_emu10k1_voice_alloc); -EXPORT_SYMBOL(snd_emu10k1_voice_free); -/* io.c */ -EXPORT_SYMBOL(snd_emu10k1_ptr_read); -EXPORT_SYMBOL(snd_emu10k1_ptr_write); diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c index d51290c1816..0fb27e4be07 100644 --- a/sound/pci/emu10k1/emu10k1x.c +++ b/sound/pci/emu10k1/emu10k1x.c @@ -1055,8 +1055,7 @@ static int __devinit snd_emu10k1x_proc_init(struct emu10k1x * emu) struct snd_info_entry *entry; if(! snd_card_proc_new(emu->card, "emu10k1x_regs", &entry)) { - snd_info_set_text_ops(entry, emu, 1024, snd_emu10k1x_proc_reg_read); - entry->c.text.write_size = 64; + snd_info_set_text_ops(entry, emu, snd_emu10k1x_proc_reg_read); entry->c.text.write = snd_emu10k1x_proc_reg_write; entry->mode |= S_IWUSR; entry->private_data = emu; diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c index 2a9d12d1068..c31f3d0877f 100644 --- a/sound/pci/emu10k1/emumixer.c +++ b/sound/pci/emu10k1/emumixer.c @@ -777,6 +777,8 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu, }; static char *audigy_remove_ctls[] = { /* Master/PCM controls on ac97 of Audigy has no effect */ + /* On the Audigy2 the AC97 playback is piped into + * the Philips ADC for 24bit capture */ "PCM Playback Switch", "PCM Playback Volume", "Master Mono Playback Switch", @@ -804,6 +806,47 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu, "AMic Playback Volume", "Mic Playback Volume", NULL }; + static char *audigy_remove_ctls_1361t_adc[] = { + /* On the Audigy2 the AC97 playback is piped into + * the Philips ADC for 24bit capture */ + "PCM Playback Switch", + "PCM Playback Volume", + "Master Mono Playback Switch", + "Master Mono Playback Volume", + "Capture Source", + "Capture Switch", + "Capture Volume", + "Mic Capture Volume", + "Headphone Playback Switch", + "Headphone Playback Volume", + "3D Control - Center", + "3D Control - Depth", + "3D Control - Switch", + "Line2 Playback Volume", + "Line2 Capture Volume", + NULL + }; + static char *audigy_rename_ctls_1361t_adc[] = { + "Master Playback Switch", "Master Capture Switch", + "Master Playback Volume", "Master Capture Volume", + "Wave Master Playback Volume", "Master Playback Volume", + "PC Speaker Playback Switch", "PC Speaker Capture Switch", + "PC Speaker Playback Volume", "PC Speaker Capture Volume", + "Phone Playback Switch", "Phone Capture Switch", + "Phone Playback Volume", "Phone Capture Volume", + "Mic Playback Switch", "Mic Capture Switch", + "Mic Playback Volume", "Mic Capture Volume", + "Line Playback Switch", "Line Capture Switch", + "Line Playback Volume", "Line Capture Volume", + "CD Playback Switch", "CD Capture Switch", + "CD Playback Volume", "CD Capture Volume", + "Aux Playback Switch", "Aux Capture Switch", + "Aux Playback Volume", "Aux Capture Volume", + "Video Playback Switch", "Video Capture Switch", + "Video Playback Volume", "Video Capture Volume", + + NULL + }; if (emu->card_capabilities->ac97_chip) { struct snd_ac97_bus *pbus; @@ -834,7 +877,10 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu, snd_ac97_write_cache(emu->ac97, AC97_MASTER, 0x0000); /* set capture source to mic */ snd_ac97_write_cache(emu->ac97, AC97_REC_SEL, 0x0000); - c = audigy_remove_ctls; + if (emu->card_capabilities->adc_1361t) + c = audigy_remove_ctls_1361t_adc; + else + c = audigy_remove_ctls; } else { /* * Credits for cards based on STAC9758: @@ -863,11 +909,15 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu, } if (emu->audigy) - c = audigy_rename_ctls; + if (emu->card_capabilities->adc_1361t) + c = audigy_rename_ctls_1361t_adc; + else + c = audigy_rename_ctls; else c = emu10k1_rename_ctls; for (; *c; c += 2) rename_ctl(card, c[0], c[1]); + if (emu->card_capabilities->subsystem == 0x20071102) { /* Audigy 4 Pro */ rename_ctl(card, "Line2 Capture Volume", "Line1/Mic Capture Volume"); rename_ctl(card, "Analog Mix Capture Volume", "Line2 Capture Volume"); diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c index 90f1c52703a..b939e03aaed 100644 --- a/sound/pci/emu10k1/emuproc.c +++ b/sound/pci/emu10k1/emuproc.c @@ -532,57 +532,51 @@ int __devinit snd_emu10k1_proc_init(struct snd_emu10k1 * emu) struct snd_info_entry *entry; #ifdef CONFIG_SND_DEBUG if (! snd_card_proc_new(emu->card, "io_regs", &entry)) { - snd_info_set_text_ops(entry, emu, 1024, snd_emu_proc_io_reg_read); - entry->c.text.write_size = 64; + snd_info_set_text_ops(entry, emu, snd_emu_proc_io_reg_read); entry->c.text.write = snd_emu_proc_io_reg_write; entry->mode |= S_IWUSR; } if (! snd_card_proc_new(emu->card, "ptr_regs00a", &entry)) { - snd_info_set_text_ops(entry, emu, 65536, snd_emu_proc_ptr_reg_read00a); - entry->c.text.write_size = 64; + snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read00a); entry->c.text.write = snd_emu_proc_ptr_reg_write00; entry->mode |= S_IWUSR; } if (! snd_card_proc_new(emu->card, "ptr_regs00b", &entry)) { - snd_info_set_text_ops(entry, emu, 65536, snd_emu_proc_ptr_reg_read00b); - entry->c.text.write_size = 64; + snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read00b); entry->c.text.write = snd_emu_proc_ptr_reg_write00; entry->mode |= S_IWUSR; } if (! snd_card_proc_new(emu->card, "ptr_regs20a", &entry)) { - snd_info_set_text_ops(entry, emu, 65536, snd_emu_proc_ptr_reg_read20a); - entry->c.text.write_size = 64; + snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read20a); entry->c.text.write = snd_emu_proc_ptr_reg_write20; entry->mode |= S_IWUSR; } if (! snd_card_proc_new(emu->card, "ptr_regs20b", &entry)) { - snd_info_set_text_ops(entry, emu, 65536, snd_emu_proc_ptr_reg_read20b); - entry->c.text.write_size = 64; + snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read20b); entry->c.text.write = snd_emu_proc_ptr_reg_write20; entry->mode |= S_IWUSR; } if (! snd_card_proc_new(emu->card, "ptr_regs20c", &entry)) { - snd_info_set_text_ops(entry, emu, 65536, snd_emu_proc_ptr_reg_read20c); - entry->c.text.write_size = 64; + snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read20c); entry->c.text.write = snd_emu_proc_ptr_reg_write20; entry->mode |= S_IWUSR; } #endif if (! snd_card_proc_new(emu->card, "emu10k1", &entry)) - snd_info_set_text_ops(entry, emu, 2048, snd_emu10k1_proc_read); + snd_info_set_text_ops(entry, emu, snd_emu10k1_proc_read); if (emu->card_capabilities->emu10k2_chip) { if (! snd_card_proc_new(emu->card, "spdif-in", &entry)) - snd_info_set_text_ops(entry, emu, 2048, snd_emu10k1_proc_spdif_read); + snd_info_set_text_ops(entry, emu, snd_emu10k1_proc_spdif_read); } if (emu->card_capabilities->ca0151_chip) { if (! snd_card_proc_new(emu->card, "capture-rates", &entry)) - snd_info_set_text_ops(entry, emu, 2048, snd_emu10k1_proc_rates_read); + snd_info_set_text_ops(entry, emu, snd_emu10k1_proc_rates_read); } if (! snd_card_proc_new(emu->card, "voices", &entry)) - snd_info_set_text_ops(entry, emu, 2048, snd_emu10k1_proc_voices_read); + snd_info_set_text_ops(entry, emu, snd_emu10k1_proc_voices_read); if (! snd_card_proc_new(emu->card, "fx8010_gpr", &entry)) { entry->content = SNDRV_INFO_CONTENT_DATA; @@ -616,7 +610,6 @@ int __devinit snd_emu10k1_proc_init(struct snd_emu10k1 * emu) entry->content = SNDRV_INFO_CONTENT_TEXT; entry->private_data = emu; entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/; - entry->c.text.read_size = 128*1024; entry->c.text.read = snd_emu10k1_proc_acode_read; } return 0; diff --git a/sound/pci/emu10k1/io.c b/sound/pci/emu10k1/io.c index ef5304df8c1..029e7856c43 100644 --- a/sound/pci/emu10k1/io.c +++ b/sound/pci/emu10k1/io.c @@ -62,6 +62,8 @@ unsigned int snd_emu10k1_ptr_read(struct snd_emu10k1 * emu, unsigned int reg, un } } +EXPORT_SYMBOL(snd_emu10k1_ptr_read); + void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned int chn, unsigned int data) { unsigned int regptr; @@ -92,6 +94,8 @@ void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned i } } +EXPORT_SYMBOL(snd_emu10k1_ptr_write); + unsigned int snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu, unsigned int reg, unsigned int chn) diff --git a/sound/pci/emu10k1/memory.c b/sound/pci/emu10k1/memory.c index e7ec98649f0..4fcaefe5a3c 100644 --- a/sound/pci/emu10k1/memory.c +++ b/sound/pci/emu10k1/memory.c @@ -287,6 +287,8 @@ int snd_emu10k1_memblk_map(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *b return err; } +EXPORT_SYMBOL(snd_emu10k1_memblk_map); + /* * page allocation for DMA */ @@ -387,6 +389,7 @@ snd_emu10k1_synth_alloc(struct snd_emu10k1 *hw, unsigned int size) return (struct snd_util_memblk *)blk; } +EXPORT_SYMBOL(snd_emu10k1_synth_alloc); /* * free a synth sample area @@ -409,6 +412,7 @@ snd_emu10k1_synth_free(struct snd_emu10k1 *emu, struct snd_util_memblk *memblk) return 0; } +EXPORT_SYMBOL(snd_emu10k1_synth_free); /* check new allocation range */ static void get_single_page_range(struct snd_util_memhdr *hdr, @@ -540,6 +544,8 @@ int snd_emu10k1_synth_bzero(struct snd_emu10k1 *emu, struct snd_util_memblk *blk return 0; } +EXPORT_SYMBOL(snd_emu10k1_synth_bzero); + /* * copy_from_user(blk + offset, data, size) */ @@ -568,3 +574,5 @@ int snd_emu10k1_synth_copy_from_user(struct snd_emu10k1 *emu, struct snd_util_me } while (offset < end_offset); return 0; } + +EXPORT_SYMBOL(snd_emu10k1_synth_copy_from_user); diff --git a/sound/pci/emu10k1/p17v.h b/sound/pci/emu10k1/p17v.h new file mode 100644 index 00000000000..7ddb5be632c --- /dev/null +++ b/sound/pci/emu10k1/p17v.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk> + * Driver p17v chips + * Version: 0.01 + * + * 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 + * + */ + +/******************************************************************************/ +/* Audigy2Value Tina (P17V) pointer-offset register set, + * accessed through the PTR20 and DATA24 registers */ +/******************************************************************************/ + +/* 00 - 07: Not used */ +#define P17V_PLAYBACK_FIFO_PTR 0x08 /* Current playback fifo pointer + * and number of sound samples in cache. + */ +/* 09 - 12: Not used */ +#define P17V_CAPTURE_FIFO_PTR 0x13 /* Current capture fifo pointer + * and number of sound samples in cache. + */ +/* 14 - 17: Not used */ +#define P17V_PB_CHN_SEL 0x18 /* P17v playback channel select */ +#define P17V_SE_SLOT_SEL_L 0x19 /* Sound Engine slot select low */ +#define P17V_SE_SLOT_SEL_H 0x1a /* Sound Engine slot select high */ +/* 1b - 1f: Not used */ +/* 20 - 2f: Not used */ +/* 30 - 3b: Not used */ +#define P17V_SPI 0x3c /* SPI interface register */ +#define P17V_I2C_ADDR 0x3d /* I2C Address */ +#define P17V_I2C_0 0x3e /* I2C Data */ +#define P17V_I2C_1 0x3f /* I2C Data */ + +#define P17V_START_AUDIO 0x40 /* Start Audio bit */ +/* 41 - 47: Reserved */ +#define P17V_START_CAPTURE 0x48 /* Start Capture bit */ +#define P17V_CAPTURE_FIFO_BASE 0x49 /* Record FIFO base address */ +#define P17V_CAPTURE_FIFO_SIZE 0x4a /* Record FIFO buffer size */ +#define P17V_CAPTURE_FIFO_INDEX 0x4b /* Record FIFO capture index */ +#define P17V_CAPTURE_VOL_H 0x4c /* P17v capture volume control */ +#define P17V_CAPTURE_VOL_L 0x4d /* P17v capture volume control */ +/* 4e - 4f: Not used */ +/* 50 - 5f: Not used */ +#define P17V_SRCSel 0x60 /* SRC48 and SRCMulti sample rate select + * and output select + */ +#define P17V_MIXER_AC97_10K1_VOL_L 0x61 /* 10K to Mixer_AC97 input volume control */ +#define P17V_MIXER_AC97_10K1_VOL_H 0x62 /* 10K to Mixer_AC97 input volume control */ +#define P17V_MIXER_AC97_P17V_VOL_L 0x63 /* P17V to Mixer_AC97 input volume control */ +#define P17V_MIXER_AC97_P17V_VOL_H 0x64 /* P17V to Mixer_AC97 input volume control */ +#define P17V_MIXER_AC97_SRP_REC_VOL_L 0x65 /* SRP Record to Mixer_AC97 input volume control */ +#define P17V_MIXER_AC97_SRP_REC_VOL_H 0x66 /* SRP Record to Mixer_AC97 input volume control */ +/* 67 - 68: Reserved */ +#define P17V_MIXER_Spdif_10K1_VOL_L 0x69 /* 10K to Mixer_Spdif input volume control */ +#define P17V_MIXER_Spdif_10K1_VOL_H 0x6A /* 10K to Mixer_Spdif input volume control */ +#define P17V_MIXER_Spdif_P17V_VOL_L 0x6B /* P17V to Mixer_Spdif input volume control */ +#define P17V_MIXER_Spdif_P17V_VOL_H 0x6C /* P17V to Mixer_Spdif input volume control */ +#define P17V_MIXER_Spdif_SRP_REC_VOL_L 0x6D /* SRP Record to Mixer_Spdif input volume control */ +#define P17V_MIXER_Spdif_SRP_REC_VOL_H 0x6E /* SRP Record to Mixer_Spdif input volume control */ +/* 6f - 70: Reserved */ +#define P17V_MIXER_I2S_10K1_VOL_L 0x71 /* 10K to Mixer_I2S input volume control */ +#define P17V_MIXER_I2S_10K1_VOL_H 0x72 /* 10K to Mixer_I2S input volume control */ +#define P17V_MIXER_I2S_P17V_VOL_L 0x73 /* P17V to Mixer_I2S input volume control */ +#define P17V_MIXER_I2S_P17V_VOL_H 0x74 /* P17V to Mixer_I2S input volume control */ +#define P17V_MIXER_I2S_SRP_REC_VOL_L 0x75 /* SRP Record to Mixer_I2S input volume control */ +#define P17V_MIXER_I2S_SRP_REC_VOL_H 0x76 /* SRP Record to Mixer_I2S input volume control */ +/* 77 - 78: Reserved */ +#define P17V_MIXER_AC97_ENABLE 0x79 /* Mixer AC97 input audio enable */ +#define P17V_MIXER_SPDIF_ENABLE 0x7A /* Mixer SPDIF input audio enable */ +#define P17V_MIXER_I2S_ENABLE 0x7B /* Mixer I2S input audio enable */ +#define P17V_AUDIO_OUT_ENABLE 0x7C /* Audio out enable */ +#define P17V_MIXER_ATT 0x7D /* SRP Mixer Attenuation Select */ +#define P17V_SRP_RECORD_SRR 0x7E /* SRP Record channel source Select */ +#define P17V_SOFT_RESET_SRP_MIXER 0x7F /* SRP and mixer soft reset */ + +#define P17V_AC97_OUT_MASTER_VOL_L 0x80 /* AC97 Output master volume control */ +#define P17V_AC97_OUT_MASTER_VOL_H 0x81 /* AC97 Output master volume control */ +#define P17V_SPDIF_OUT_MASTER_VOL_L 0x82 /* SPDIF Output master volume control */ +#define P17V_SPDIF_OUT_MASTER_VOL_H 0x83 /* SPDIF Output master volume control */ +#define P17V_I2S_OUT_MASTER_VOL_L 0x84 /* I2S Output master volume control */ +#define P17V_I2S_OUT_MASTER_VOL_H 0x85 /* I2S Output master volume control */ +/* 86 - 87: Not used */ +#define P17V_I2S_CHANNEL_SWAP_PHASE_INVERSE 0x88 /* I2S out mono channel swap + * and phase inverse */ +#define P17V_SPDIF_CHANNEL_SWAP_PHASE_INVERSE 0x89 /* SPDIF out mono channel swap + * and phase inverse */ +/* 8A: Not used */ +#define P17V_SRP_P17V_ESR 0x8B /* SRP_P17V estimated sample rate and rate lock */ +#define P17V_SRP_REC_ESR 0x8C /* SRP_REC estimated sample rate and rate lock */ +#define P17V_SRP_BYPASS 0x8D /* srps channel bypass and srps bypass */ +/* 8E - 92: Not used */ +#define P17V_I2S_SRC_SEL 0x93 /* I2SIN mode sel */ + + + + + + diff --git a/sound/pci/emu10k1/tina2.h b/sound/pci/emu10k1/tina2.h index 5c43abf03e8..f2d8eb6c89e 100644 --- a/sound/pci/emu10k1/tina2.h +++ b/sound/pci/emu10k1/tina2.h @@ -1,11 +1,7 @@ /* * Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk> - * Driver p16v chips - * Version: 0.21 - * - * - * This code was initally based on code from ALSA's emu10k1x.c which is: - * Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com> + * Driver tina2 chips + * Version: 0.1 * * 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 diff --git a/sound/pci/emu10k1/voice.c b/sound/pci/emu10k1/voice.c index 56ffb7dc3ee..94eca82dd4f 100644 --- a/sound/pci/emu10k1/voice.c +++ b/sound/pci/emu10k1/voice.c @@ -139,6 +139,8 @@ int snd_emu10k1_voice_alloc(struct snd_emu10k1 *emu, int type, int number, return result; } +EXPORT_SYMBOL(snd_emu10k1_voice_alloc); + int snd_emu10k1_voice_free(struct snd_emu10k1 *emu, struct snd_emu10k1_voice *pvoice) { @@ -153,3 +155,5 @@ int snd_emu10k1_voice_free(struct snd_emu10k1 *emu, spin_unlock_irqrestore(&emu->voice_lock, flags); return 0; } + +EXPORT_SYMBOL(snd_emu10k1_voice_free); diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index ca9e34e88f6..9d46bbee2a4 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -1915,7 +1915,7 @@ static void __devinit snd_ensoniq_proc_init(struct ensoniq * ensoniq) struct snd_info_entry *entry; if (! snd_card_proc_new(ensoniq->card, "audiopci", &entry)) - snd_info_set_text_ops(entry, ensoniq, 1024, snd_ensoniq_proc_read); + snd_info_set_text_ops(entry, ensoniq, snd_ensoniq_proc_read); } /* diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index 6f9094ca4fb..ca6603fe0b1 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -1756,7 +1756,8 @@ static int __devinit snd_es1938_probe(struct pci_dev *pci, } } if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, - chip->mpu_port, 1, chip->irq, 0, &chip->rmidi) < 0) { + chip->mpu_port, MPU401_INFO_INTEGRATED, + chip->irq, 0, &chip->rmidi) < 0) { printk(KERN_ERR "es1938: unable to initialize MPU-401\n"); } else { // this line is vital for MIDI interrupt handling on ess-solo1 diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index 5ff4175c7b6..bfa0876e715 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -132,7 +132,7 @@ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card * static int total_bufsize[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1024 }; static int pcm_substreams_p[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4 }; static int pcm_substreams_c[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1 }; -static int clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +static int clock[SNDRV_CARDS]; static int use_pm[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; static int enable_mpu[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; #ifdef SUPPORT_JOYSTICK @@ -2727,7 +2727,8 @@ static int __devinit snd_es1968_probe(struct pci_dev *pci, } if (enable_mpu[dev]) { if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, - chip->io_port + ESM_MPU401_PORT, 1, + chip->io_port + ESM_MPU401_PORT, + MPU401_INFO_INTEGRATED, chip->irq, 0, &chip->rmidi)) < 0) { printk(KERN_WARNING "es1968: skipping MPU-401 MIDI support..\n"); } diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index d72fc28c580..0afa573dd24 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -56,7 +56,7 @@ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card * * 3 = MediaForte 64-PCR * High 16-bits are video (radio) device number + 1 */ -static int tea575x_tuner[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = 0 }; +static int tea575x_tuner[SNDRV_CARDS]; module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for the FM801 soundcard."); @@ -1448,7 +1448,8 @@ static int __devinit snd_card_fm801_probe(struct pci_dev *pci, return err; } if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_FM801, - FM801_REG(chip, MPU401_DATA), 1, + FM801_REG(chip, MPU401_DATA), + MPU401_INFO_INTEGRATED, chip->irq, 0, &chip->rmidi)) < 0) { snd_card_free(card); return err; diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index ddfb5ff7fb8..dbacba6177d 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -1,5 +1,5 @@ snd-hda-intel-objs := hda_intel.o -snd-hda-codec-objs := hda_codec.o hda_generic.o patch_realtek.o patch_cmedia.o patch_analog.o patch_sigmatel.o patch_si3054.o +snd-hda-codec-objs := hda_codec.o hda_generic.o patch_realtek.o patch_cmedia.o patch_analog.o patch_sigmatel.o patch_si3054.o patch_atihdmi.o ifdef CONFIG_PROC_FS snd-hda-codec-objs += hda_proc.o endif diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 5bee3b53647..23201f3eeb1 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -86,6 +86,8 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, int dire return res; } +EXPORT_SYMBOL(snd_hda_codec_read); + /** * snd_hda_codec_write - send a single command without waiting for response * @codec: the HDA codec @@ -108,6 +110,8 @@ int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct, return err; } +EXPORT_SYMBOL(snd_hda_codec_write); + /** * snd_hda_sequence_write - sequence writes * @codec: the HDA codec @@ -122,6 +126,8 @@ void snd_hda_sequence_write(struct hda_codec *codec, const struct hda_verb *seq) snd_hda_codec_write(codec, seq->nid, 0, seq->verb, seq->param); } +EXPORT_SYMBOL(snd_hda_sequence_write); + /** * snd_hda_get_sub_nodes - get the range of sub nodes * @codec: the HDA codec @@ -140,6 +146,8 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *sta return (int)(parm & 0x7fff); } +EXPORT_SYMBOL(snd_hda_get_sub_nodes); + /** * snd_hda_get_connections - get connection list * @codec: the HDA codec @@ -256,6 +264,8 @@ int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex) return 0; } +EXPORT_SYMBOL(snd_hda_queue_unsol_event); + /* * process queueud unsolicited events */ @@ -384,6 +394,7 @@ int snd_hda_bus_new(struct snd_card *card, const struct hda_bus_template *temp, return 0; } +EXPORT_SYMBOL(snd_hda_bus_new); /* * find a matching codec preset @@ -397,7 +408,9 @@ static const struct hda_codec_preset *find_codec_preset(struct hda_codec *codec) u32 mask = preset->mask; if (! mask) mask = ~0; - if (preset->id == (codec->vendor_id & mask)) + if (preset->id == (codec->vendor_id & mask) && + (! preset->rev || + preset->rev == codec->revision_id)) return preset; } } @@ -587,6 +600,8 @@ int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr, return 0; } +EXPORT_SYMBOL(snd_hda_codec_new); + /** * snd_hda_codec_setup_stream - set up the codec for streaming * @codec: the CODEC to set up @@ -609,6 +624,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, u32 stre snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, format); } +EXPORT_SYMBOL(snd_hda_codec_setup_stream); /* * amp access functions @@ -1294,6 +1310,7 @@ int snd_hda_build_controls(struct hda_bus *bus) return 0; } +EXPORT_SYMBOL(snd_hda_build_controls); /* * stream formats @@ -1382,6 +1399,8 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate, return val; } +EXPORT_SYMBOL(snd_hda_calc_stream_format); + /** * snd_hda_query_supported_pcm - query the supported PCM rates and formats * @codec: the HDA codec @@ -1663,6 +1682,7 @@ int snd_hda_build_pcms(struct hda_bus *bus) return 0; } +EXPORT_SYMBOL(snd_hda_build_pcms); /** * snd_hda_check_board_config - compare the current codec with the config table @@ -2165,6 +2185,8 @@ int snd_hda_suspend(struct hda_bus *bus, pm_message_t state) return 0; } +EXPORT_SYMBOL(snd_hda_suspend); + /** * snd_hda_resume - resume the codecs * @bus: the HDA bus @@ -2187,6 +2209,8 @@ int snd_hda_resume(struct hda_bus *bus) return 0; } +EXPORT_SYMBOL(snd_hda_resume); + /** * snd_hda_resume_ctls - resume controls in the new control list * @codec: the HDA codec @@ -2247,25 +2271,6 @@ int snd_hda_resume_spdif_in(struct hda_codec *codec) #endif /* - * symbols exported for controller modules - */ -EXPORT_SYMBOL(snd_hda_codec_read); -EXPORT_SYMBOL(snd_hda_codec_write); -EXPORT_SYMBOL(snd_hda_sequence_write); -EXPORT_SYMBOL(snd_hda_get_sub_nodes); -EXPORT_SYMBOL(snd_hda_queue_unsol_event); -EXPORT_SYMBOL(snd_hda_bus_new); -EXPORT_SYMBOL(snd_hda_codec_new); -EXPORT_SYMBOL(snd_hda_codec_setup_stream); -EXPORT_SYMBOL(snd_hda_calc_stream_format); -EXPORT_SYMBOL(snd_hda_build_pcms); -EXPORT_SYMBOL(snd_hda_build_controls); -#ifdef CONFIG_PM -EXPORT_SYMBOL(snd_hda_suspend); -EXPORT_SYMBOL(snd_hda_resume); -#endif - -/* * INIT part */ diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index e821d65afa1..4070b5cd9b6 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -82,6 +82,7 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6}," "{Intel, ICH8}," "{ATI, SB450}," "{ATI, SB600}," + "{ATI, RS600}," "{VIA, VT8251}," "{VIA, VT8237A}," "{SiS, SIS966}," @@ -167,6 +168,12 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; #define ULI_PLAYBACK_INDEX 5 #define ULI_NUM_PLAYBACK 6 +/* ATI HDMI has 1 playback and 0 capture */ +#define ATIHDMI_CAPTURE_INDEX 0 +#define ATIHDMI_NUM_CAPTURE 0 +#define ATIHDMI_PLAYBACK_INDEX 0 +#define ATIHDMI_NUM_PLAYBACK 1 + /* this number is statically defined for simplicity */ #define MAX_AZX_DEV 16 @@ -331,6 +338,7 @@ struct azx { enum { AZX_DRIVER_ICH, AZX_DRIVER_ATI, + AZX_DRIVER_ATIHDMI, AZX_DRIVER_VIA, AZX_DRIVER_SIS, AZX_DRIVER_ULI, @@ -340,6 +348,7 @@ enum { static char *driver_short_names[] __devinitdata = { [AZX_DRIVER_ICH] = "HDA Intel", [AZX_DRIVER_ATI] = "HDA ATI SB", + [AZX_DRIVER_ATIHDMI] = "HDA ATI HDMI", [AZX_DRIVER_VIA] = "HDA VIA VT82xx", [AZX_DRIVER_SIS] = "HDA SIS966", [AZX_DRIVER_ULI] = "HDA ULI M5461", @@ -1393,10 +1402,10 @@ static int azx_free(struct azx *chip) msleep(1); } - if (chip->remap_addr) - iounmap(chip->remap_addr); if (chip->irq >= 0) free_irq(chip->irq, (void*)chip); + if (chip->remap_addr) + iounmap(chip->remap_addr); if (chip->bdl.area) snd_dma_free_pages(&chip->bdl); @@ -1495,6 +1504,12 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, 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; @@ -1621,6 +1636,7 @@ static struct pci_device_id azx_ids[] __devinitdata = { { 0x8086, 0x284b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH8 */ { 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 */ { 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_patch.h b/sound/pci/hda/hda_patch.h index acaef3c811b..0b668793fac 100644 --- a/sound/pci/hda/hda_patch.h +++ b/sound/pci/hda/hda_patch.h @@ -12,6 +12,8 @@ extern struct hda_codec_preset snd_hda_preset_analog[]; extern struct hda_codec_preset snd_hda_preset_sigmatel[]; /* SiLabs 3054/3055 modem codecs */ extern struct hda_codec_preset snd_hda_preset_si3054[]; +/* ATI HDMI codecs */ +extern struct hda_codec_preset snd_hda_preset_atihdmi[]; static const struct hda_codec_preset *hda_preset_tables[] = { snd_hda_preset_realtek, @@ -19,5 +21,6 @@ static const struct hda_codec_preset *hda_preset_tables[] = { snd_hda_preset_analog, snd_hda_preset_sigmatel, snd_hda_preset_si3054, + snd_hda_preset_atihdmi, NULL }; diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index ca514a6a587..c2f0fe85bf3 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -182,6 +182,10 @@ static void print_pin_caps(struct snd_info_buffer *buffer, snd_iprintf(buffer, " OUT"); if (caps & AC_PINCAP_HP_DRV) snd_iprintf(buffer, " HP"); + if (caps & AC_PINCAP_EAPD) + snd_iprintf(buffer, " EAPD"); + if (caps & AC_PINCAP_PRES_DETECT) + snd_iprintf(buffer, " Detect"); 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, @@ -318,7 +322,7 @@ int snd_hda_codec_proc_new(struct hda_codec *codec) if (err < 0) return err; - snd_info_set_text_ops(entry, codec, 32 * 1024, print_codec_info); + snd_info_set_text_ops(entry, codec, print_codec_info); return 0; } diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 40f000ba136..33b7d580646 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -789,6 +789,8 @@ static struct hda_board_config ad1986a_cfg_tbl[] = { { .modelname = "3stack", .config = AD1986A_3STACK }, { .pci_subvendor = 0x10de, .pci_subdevice = 0xcb84, .config = AD1986A_3STACK }, /* ASUS A8N-VM CSM */ + { .pci_subvendor = 0x1043, .pci_subdevice = 0x81b3, + .config = AD1986A_3STACK }, /* ASUS P5RD2-VM / P5GPL-X SE */ { .modelname = "laptop", .config = AD1986A_LAPTOP }, { .pci_subvendor = 0x144d, .pci_subdevice = 0xc01e, .config = AD1986A_LAPTOP }, /* FSC V2060 */ @@ -797,6 +799,8 @@ static struct hda_board_config ad1986a_cfg_tbl[] = { { .pci_subvendor = 0x1043, .pci_subdevice = 0x818f, .config = AD1986A_LAPTOP }, /* ASUS P5GV-MX */ { .modelname = "laptop-eapd", .config = AD1986A_LAPTOP_EAPD }, + { .pci_subvendor = 0x144d, .pci_subdevice = 0xc023, + .config = AD1986A_LAPTOP_EAPD }, /* Samsung X60 Chane */ { .pci_subvendor = 0x144d, .pci_subdevice = 0xc024, .config = AD1986A_LAPTOP_EAPD }, /* Samsung R65-T2300 Charis */ { .pci_subvendor = 0x1043, .pci_subdevice = 0x1153, @@ -809,6 +813,8 @@ static struct hda_board_config ad1986a_cfg_tbl[] = { .config = AD1986A_LAPTOP_EAPD }, /* ASUS Z62F */ { .pci_subvendor = 0x103c, .pci_subdevice = 0x30af, .config = AD1986A_LAPTOP_EAPD }, /* HP Compaq Presario B2800 */ + { .pci_subvendor = 0x17aa, .pci_subdevice = 0x2066, + .config = AD1986A_LAPTOP_EAPD }, /* Lenovo 3000 N100-07684JU */ {} }; @@ -963,7 +969,7 @@ static struct snd_kcontrol_new ad1983_mixers[] = { }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Route", + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", .info = ad1983_spdif_route_info, .get = ad1983_spdif_route_get, .put = ad1983_spdif_route_put, @@ -1103,7 +1109,7 @@ static struct snd_kcontrol_new ad1981_mixers[] = { /* identical with AD1983 */ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Route", + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", .info = ad1983_spdif_route_info, .get = ad1983_spdif_route_get, .put = ad1983_spdif_route_put, @@ -1329,13 +1335,60 @@ static int ad1981_hp_init(struct hda_codec *codec) return 0; } +/* configuration for Lenovo Thinkpad T60 */ +static struct snd_kcontrol_new ad1981_thinkpad_mixers[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x05, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Master Playback Switch", 0x05, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = ad198x_mux_enum_info, + .get = ad198x_mux_enum_get, + .put = ad198x_mux_enum_put, + }, + /* identical with AD1983 */ + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", + .info = ad1983_spdif_route_info, + .get = ad1983_spdif_route_get, + .put = ad1983_spdif_route_put, + }, + { } /* end */ +}; + +static struct hda_input_mux ad1981_thinkpad_capture_source = { + .num_items = 3, + .items = { + { "Mic", 0x0 }, + { "Mix", 0x2 }, + { "CD", 0x4 }, + }, +}; + /* models */ -enum { AD1981_BASIC, AD1981_HP }; +enum { AD1981_BASIC, AD1981_HP, AD1981_THINKPAD }; static struct hda_board_config ad1981_cfg_tbl[] = { { .modelname = "hp", .config = AD1981_HP }, /* All HP models */ { .pci_subvendor = 0x103c, .config = AD1981_HP }, + { .pci_subvendor = 0x30b0, .pci_subdevice = 0x103c, + .config = AD1981_HP }, /* HP nx6320 (reversed SSID, H/W bug) */ + { .modelname = "thinkpad", .config = AD1981_THINKPAD }, + /* Lenovo Thinkpad T60/X60/Z6xx */ + { .pci_subvendor = 0x17aa, .config = AD1981_THINKPAD }, + { .pci_subvendor = 0x1014, .pci_subdevice = 0x0597, + .config = AD1981_THINKPAD }, /* Z60m/t */ { .modelname = "basic", .config = AD1981_BASIC }, {} }; @@ -1381,6 +1434,10 @@ static int patch_ad1981(struct hda_codec *codec) codec->patch_ops.init = ad1981_hp_init; codec->patch_ops.unsol_event = ad1981_hp_unsol_event; break; + case AD1981_THINKPAD: + spec->mixers[0] = ad1981_thinkpad_mixers; + spec->input_mux = &ad1981_thinkpad_capture_source; + break; } return 0; diff --git a/sound/pci/hda/patch_atihdmi.c b/sound/pci/hda/patch_atihdmi.c new file mode 100644 index 00000000000..a27440ffd1c --- /dev/null +++ b/sound/pci/hda/patch_atihdmi.c @@ -0,0 +1,165 @@ +/* + * Universal Interface for Intel High Definition Audio Codec + * + * HD audio interface patch for ATI HDMI codecs + * + * Copyright (c) 2006 ATI Technologies Inc. + * + * + * This driver 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 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 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 <linux/init.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/pci.h> +#include <sound/core.h> +#include "hda_codec.h" +#include "hda_local.h" + +struct atihdmi_spec { + struct hda_multi_out multiout; + + struct hda_pcm pcm_rec; +}; + +static struct hda_verb atihdmi_basic_init[] = { + /* enable digital output on pin widget */ + { 0x03, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + {} /* terminator */ +}; + +/* + * Controls + */ +static int atihdmi_build_controls(struct hda_codec *codec) +{ + struct atihdmi_spec *spec = codec->spec; + int err; + + err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); + if (err < 0) + return err; + + return 0; +} + +static int atihdmi_init(struct hda_codec *codec) +{ + snd_hda_sequence_write(codec, atihdmi_basic_init); + return 0; +} + +#ifdef CONFIG_PM +/* + * resume + */ +static int atihdmi_resume(struct hda_codec *codec) +{ + atihdmi_init(codec); + snd_hda_resume_spdif_out(codec); + + return 0; +} +#endif + +/* + * Digital out + */ +static int atihdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct atihdmi_spec *spec = codec->spec; + return snd_hda_multi_out_dig_open(codec, &spec->multiout); +} + +static int atihdmi_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct atihdmi_spec *spec = codec->spec; + return snd_hda_multi_out_dig_close(codec, &spec->multiout); +} + +static struct hda_pcm_stream atihdmi_pcm_digital_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .nid = 0x2, /* NID to query formats and rates and setup streams */ + .ops = { + .open = atihdmi_dig_playback_pcm_open, + .close = atihdmi_dig_playback_pcm_close + }, +}; + +static int atihdmi_build_pcms(struct hda_codec *codec) +{ + struct atihdmi_spec *spec = codec->spec; + struct hda_pcm *info = &spec->pcm_rec; + + codec->num_pcms = 1; + codec->pcm_info = info; + + info->name = "ATI HDMI"; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = atihdmi_pcm_digital_playback; + + return 0; +} + +static void atihdmi_free(struct hda_codec *codec) +{ + kfree(codec->spec); +} + +static struct hda_codec_ops atihdmi_patch_ops = { + .build_controls = atihdmi_build_controls, + .build_pcms = atihdmi_build_pcms, + .init = atihdmi_init, + .free = atihdmi_free, +#ifdef CONFIG_PM + .resume = atihdmi_resume, +#endif +}; + +static int patch_atihdmi(struct hda_codec *codec) +{ + struct atihdmi_spec *spec; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + + spec->multiout.num_dacs = 0; /* no analog */ + spec->multiout.max_channels = 2; + spec->multiout.dig_out_nid = 0x2; /* NID for copying analog to digital, + * seems to be unused in pure-digital + * case. */ + + codec->patch_ops = atihdmi_patch_ops; + + return 0; +} + +/* + * patch entries + */ +struct hda_codec_preset snd_hda_preset_atihdmi[] = { + { .id = 0x1002793c, .name = "ATI RS600 HDMI", .patch = patch_atihdmi }, + {} /* terminator */ +}; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index f0e9a9c9078..18d105263fe 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -78,6 +78,7 @@ enum { enum { ALC262_BASIC, ALC262_FUJITSU, + ALC262_HP_BPC, ALC262_AUTO, ALC262_MODEL_LAST /* last tag */ }; @@ -85,6 +86,7 @@ enum { /* ALC861 models */ enum { ALC861_3ST, + ALC660_3ST, ALC861_3ST_DIG, ALC861_6ST_DIG, ALC861_AUTO, @@ -99,6 +101,17 @@ enum { ALC882_MODEL_LAST, }; +/* ALC883 models */ +enum { + ALC883_3ST_2ch_DIG, + ALC883_3ST_6ch_DIG, + ALC883_3ST_6ch, + ALC883_6ST_DIG, + ALC888_DEMO_BOARD, + ALC883_AUTO, + ALC883_MODEL_LAST, +}; + /* for GPIO Poll */ #define GPIO_MASK 0x03 @@ -108,7 +121,8 @@ struct alc_spec { unsigned int num_mixers; const struct hda_verb *init_verbs[5]; /* initialization verbs - * don't forget NULL termination! + * don't forget NULL + * termination! */ unsigned int num_init_verbs; @@ -163,7 +177,9 @@ struct alc_spec { * configuration template - to be copied to the spec instance */ struct alc_config_preset { - struct snd_kcontrol_new *mixers[5]; /* should be identical size with spec */ + struct snd_kcontrol_new *mixers[5]; /* should be identical size + * with spec + */ const struct hda_verb *init_verbs[5]; unsigned int num_dacs; hda_nid_t *dac_nids; @@ -184,7 +200,8 @@ struct alc_config_preset { /* * input MUX handling */ -static int alc_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +static int alc_mux_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; @@ -194,7 +211,8 @@ static int alc_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_ return snd_hda_input_mux_info(&spec->input_mux[mux_idx], uinfo); } -static int alc_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int alc_mux_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; @@ -204,21 +222,24 @@ static int alc_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v return 0; } -static int alc_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int alc_mux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); unsigned int mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx; return snd_hda_input_mux_put(codec, &spec->input_mux[mux_idx], ucontrol, - spec->adc_nids[adc_idx], &spec->cur_mux[adc_idx]); + spec->adc_nids[adc_idx], + &spec->cur_mux[adc_idx]); } /* * channel mode setting */ -static int alc_ch_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +static int alc_ch_mode_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; @@ -226,20 +247,24 @@ static int alc_ch_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_i spec->num_channel_mode); } -static int alc_ch_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int alc_ch_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode, - spec->num_channel_mode, spec->multiout.max_channels); + spec->num_channel_mode, + spec->multiout.max_channels); } -static int alc_ch_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int alc_ch_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; return snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode, - spec->num_channel_mode, &spec->multiout.max_channels); + spec->num_channel_mode, + &spec->multiout.max_channels); } /* @@ -290,7 +315,8 @@ static signed char alc_pin_mode_dir_info[5][2] = { #define alc_pin_mode_n_items(_dir) \ (alc_pin_mode_max(_dir)-alc_pin_mode_min(_dir)+1) -static int alc_pin_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +static int alc_pin_mode_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { unsigned int item_num = uinfo->value.enumerated.item; unsigned char dir = (kcontrol->private_value >> 16) & 0xff; @@ -305,40 +331,46 @@ static int alc_pin_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_ return 0; } -static int alc_pin_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int alc_pin_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { unsigned int i; struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = kcontrol->private_value & 0xffff; unsigned char dir = (kcontrol->private_value >> 16) & 0xff; long *valp = ucontrol->value.integer.value; - unsigned int pinctl = snd_hda_codec_read(codec,nid,0,AC_VERB_GET_PIN_WIDGET_CONTROL,0x00); + unsigned int pinctl = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, + 0x00); /* Find enumerated value for current pinctl setting */ i = alc_pin_mode_min(dir); - while (alc_pin_mode_values[i]!=pinctl && i<=alc_pin_mode_max(dir)) + while (alc_pin_mode_values[i] != pinctl && i <= alc_pin_mode_max(dir)) i++; - *valp = i<=alc_pin_mode_max(dir)?i:alc_pin_mode_min(dir); + *valp = i <= alc_pin_mode_max(dir) ? i: alc_pin_mode_min(dir); return 0; } -static int alc_pin_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int alc_pin_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { signed int change; struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = kcontrol->private_value & 0xffff; unsigned char dir = (kcontrol->private_value >> 16) & 0xff; long val = *ucontrol->value.integer.value; - unsigned int pinctl = snd_hda_codec_read(codec,nid,0,AC_VERB_GET_PIN_WIDGET_CONTROL,0x00); + unsigned int pinctl = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, + 0x00); - if (val<alc_pin_mode_min(dir) || val>alc_pin_mode_max(dir)) + if (val < alc_pin_mode_min(dir) || val > alc_pin_mode_max(dir)) val = alc_pin_mode_min(dir); change = pinctl != alc_pin_mode_values[val]; if (change) { /* Set pin mode to that requested */ snd_hda_codec_write(codec,nid,0,AC_VERB_SET_PIN_WIDGET_CONTROL, - alc_pin_mode_values[val]); + alc_pin_mode_values[val]); /* Also enable the retasking pin's input/output as required * for the requested pin mode. Enum values of 2 or less are @@ -351,15 +383,19 @@ static int alc_pin_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v * this turns out to be necessary in the future. */ if (val <= 2) { - snd_hda_codec_write(codec,nid,0,AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_MUTE); - snd_hda_codec_write(codec,nid,0,AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(0)); + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_MUTE); + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(0)); } else { - snd_hda_codec_write(codec,nid,0,AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_MUTE(0)); - snd_hda_codec_write(codec,nid,0,AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_MUTE(0)); + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_UNMUTE); } } return change; @@ -378,7 +414,8 @@ static int alc_pin_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v * needed for any "production" models. */ #ifdef CONFIG_SND_DEBUG -static int alc_gpio_data_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +static int alc_gpio_data_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; uinfo->count = 1; @@ -386,33 +423,38 @@ static int alc_gpio_data_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem uinfo->value.integer.max = 1; return 0; } -static int alc_gpio_data_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int alc_gpio_data_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = kcontrol->private_value & 0xffff; unsigned char mask = (kcontrol->private_value >> 16) & 0xff; long *valp = ucontrol->value.integer.value; - unsigned int val = snd_hda_codec_read(codec,nid,0,AC_VERB_GET_GPIO_DATA,0x00); + unsigned int val = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_GPIO_DATA, 0x00); *valp = (val & mask) != 0; return 0; } -static int alc_gpio_data_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int alc_gpio_data_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { signed int change; struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = kcontrol->private_value & 0xffff; unsigned char mask = (kcontrol->private_value >> 16) & 0xff; long val = *ucontrol->value.integer.value; - unsigned int gpio_data = snd_hda_codec_read(codec,nid,0,AC_VERB_GET_GPIO_DATA,0x00); + unsigned int gpio_data = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_GPIO_DATA, + 0x00); /* Set/unset the masked GPIO bit(s) as needed */ - change = (val==0?0:mask) != (gpio_data & mask); - if (val==0) + change = (val == 0 ? 0 : mask) != (gpio_data & mask); + if (val == 0) gpio_data &= ~mask; else gpio_data |= mask; - snd_hda_codec_write(codec,nid,0,AC_VERB_SET_GPIO_DATA,gpio_data); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_GPIO_DATA, gpio_data); return change; } @@ -432,7 +474,8 @@ static int alc_gpio_data_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_ * necessary. */ #ifdef CONFIG_SND_DEBUG -static int alc_spdif_ctrl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +static int alc_spdif_ctrl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; uinfo->count = 1; @@ -440,33 +483,39 @@ static int alc_spdif_ctrl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_ele uinfo->value.integer.max = 1; return 0; } -static int alc_spdif_ctrl_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int alc_spdif_ctrl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = kcontrol->private_value & 0xffff; unsigned char mask = (kcontrol->private_value >> 16) & 0xff; long *valp = ucontrol->value.integer.value; - unsigned int val = snd_hda_codec_read(codec,nid,0,AC_VERB_GET_DIGI_CONVERT,0x00); + unsigned int val = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_DIGI_CONVERT, 0x00); *valp = (val & mask) != 0; return 0; } -static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { signed int change; struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = kcontrol->private_value & 0xffff; unsigned char mask = (kcontrol->private_value >> 16) & 0xff; long val = *ucontrol->value.integer.value; - unsigned int ctrl_data = snd_hda_codec_read(codec,nid,0,AC_VERB_GET_DIGI_CONVERT,0x00); + unsigned int ctrl_data = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_DIGI_CONVERT, + 0x00); /* Set/unset the masked control bit(s) as needed */ - change = (val==0?0:mask) != (ctrl_data & mask); + change = (val == 0 ? 0 : mask) != (ctrl_data & mask); if (val==0) ctrl_data &= ~mask; else ctrl_data |= mask; - snd_hda_codec_write(codec,nid,0,AC_VERB_SET_DIGI_CONVERT_1,ctrl_data); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, + ctrl_data); return change; } @@ -481,14 +530,17 @@ static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem /* * set up from the preset table */ -static void setup_preset(struct alc_spec *spec, const struct alc_config_preset *preset) +static void setup_preset(struct alc_spec *spec, + const struct alc_config_preset *preset) { int i; for (i = 0; i < ARRAY_SIZE(preset->mixers) && preset->mixers[i]; i++) spec->mixers[spec->num_mixers++] = preset->mixers[i]; - for (i = 0; i < ARRAY_SIZE(preset->init_verbs) && preset->init_verbs[i]; i++) - spec->init_verbs[spec->num_init_verbs++] = preset->init_verbs[i]; + for (i = 0; i < ARRAY_SIZE(preset->init_verbs) && preset->init_verbs[i]; + i++) + spec->init_verbs[spec->num_init_verbs++] = + preset->init_verbs[i]; spec->channel_mode = preset->channel_mode; spec->num_channel_mode = preset->num_channel_mode; @@ -517,8 +569,8 @@ static void setup_preset(struct alc_spec *spec, const struct alc_config_preset * * ALC880 3-stack model * * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0e) - * Pin assignment: Front = 0x14, Line-In/Surr = 0x1a, Mic/CLFE = 0x18, F-Mic = 0x1b - * HP = 0x19 + * Pin assignment: Front = 0x14, Line-In/Surr = 0x1a, Mic/CLFE = 0x18, + * F-Mic = 0x1b, HP = 0x19 */ static hda_nid_t alc880_dac_nids[4] = { @@ -662,7 +714,8 @@ static struct snd_kcontrol_new alc880_capture_alt_mixer[] = { /* * ALC880 5-stack model * - * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0d), Side = 0x02 (0xd) + * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0d), + * Side = 0x02 (0xd) * Pin assignment: Front = 0x14, Surr = 0x17, CLFE = 0x16 * Line-In/Side = 0x1a, Mic = 0x18, F-Mic = 0x1b, HP = 0x19 */ @@ -700,7 +753,8 @@ static struct hda_channel_mode alc880_fivestack_modes[2] = { /* * ALC880 6-stack model * - * DAC: Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e), Side = 0x05 (0x0f) + * DAC: Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e), + * Side = 0x05 (0x0f) * Pin assignment: Front = 0x14, Surr = 0x15, CLFE = 0x16, Side = 0x17, * Mic = 0x18, F-Mic = 0x19, Line = 0x1a, HP = 0x1b */ @@ -811,7 +865,8 @@ static struct snd_kcontrol_new alc880_w810_base_mixer[] = { * Z710V model * * DAC: Front = 0x02 (0x0c), HP = 0x03 (0x0d) - * Pin assignment: Front = 0x14, HP = 0x15, Mic = 0x18, Mic2 = 0x19(?), Line = 0x1a + * Pin assignment: Front = 0x14, HP = 0x15, Mic = 0x18, Mic2 = 0x19(?), + * Line = 0x1a */ static hda_nid_t alc880_z71v_dac_nids[1] = { @@ -966,7 +1021,8 @@ static int alc_build_controls(struct hda_codec *codec) } if (spec->multiout.dig_out_nid) { - err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); + err = snd_hda_create_spdif_out_ctls(codec, + spec->multiout.dig_out_nid); if (err < 0) return err; } @@ -999,8 +1055,8 @@ static struct hda_verb alc880_volume_init_verbs[] = { /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback * mixer widget - * Note: PASD motherboards uses the Line In 2 as the input for front panel - * mic (mic 2) + * Note: PASD motherboards uses the Line In 2 as the input for front + * panel mic (mic 2) */ /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, @@ -1154,8 +1210,8 @@ static struct hda_verb alc880_pin_z71v_init_verbs[] = { /* * 6-stack pin configuration: - * front = 0x14, surr = 0x15, clfe = 0x16, side = 0x17, mic = 0x18, f-mic = 0x19, - * line = 0x1a, HP = 0x1b + * front = 0x14, surr = 0x15, clfe = 0x16, side = 0x17, mic = 0x18, + * f-mic = 0x19, line = 0x1a, HP = 0x1b */ static struct hda_verb alc880_pin_6stack_init_verbs[] = { {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ @@ -1587,8 +1643,8 @@ static int alc880_playback_pcm_prepare(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct alc_spec *spec = codec->spec; - return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, - format, substream); + return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, + stream_tag, format, substream); } static int alc880_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, @@ -1640,7 +1696,8 @@ static int alc880_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, { struct alc_spec *spec = codec->spec; - snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 0, 0, 0); + snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], + 0, 0, 0); return 0; } @@ -1822,7 +1879,8 @@ static struct hda_channel_mode alc880_test_modes[4] = { { 8, NULL }, }; -static int alc_test_pin_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +static int alc_test_pin_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { static char *texts[] = { "N/A", "Line Out", "HP Out", @@ -1837,7 +1895,8 @@ static int alc_test_pin_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_e return 0; } -static int alc_test_pin_ctl_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int alc_test_pin_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = (hda_nid_t)kcontrol->private_value; @@ -1863,7 +1922,8 @@ static int alc_test_pin_ctl_get(struct snd_kcontrol *kcontrol, struct snd_ctl_el return 0; } -static int alc_test_pin_ctl_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int alc_test_pin_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = (hda_nid_t)kcontrol->private_value; @@ -1881,15 +1941,18 @@ static int alc_test_pin_ctl_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el AC_VERB_GET_PIN_WIDGET_CONTROL, 0); new_ctl = ctls[ucontrol->value.enumerated.item[0]]; if (old_ctl != new_ctl) { - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, new_ctl); + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, new_ctl); snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - ucontrol->value.enumerated.item[0] >= 3 ? 0xb080 : 0xb000); + (ucontrol->value.enumerated.item[0] >= 3 ? + 0xb080 : 0xb000)); return 1; } return 0; } -static int alc_test_pin_src_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +static int alc_test_pin_src_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { static char *texts[] = { "Front", "Surround", "CLFE", "Side" @@ -1903,7 +1966,8 @@ static int alc_test_pin_src_info(struct snd_kcontrol *kcontrol, struct snd_ctl_e return 0; } -static int alc_test_pin_src_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int alc_test_pin_src_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = (hda_nid_t)kcontrol->private_value; @@ -1914,7 +1978,8 @@ static int alc_test_pin_src_get(struct snd_kcontrol *kcontrol, struct snd_ctl_el return 0; } -static int alc_test_pin_src_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int alc_test_pin_src_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = (hda_nid_t)kcontrol->private_value; @@ -2174,6 +2239,7 @@ static struct hda_board_config alc880_cfg_tbl[] = { { .modelname = "lg", .config = ALC880_LG }, { .pci_subvendor = 0x1854, .pci_subdevice = 0x003b, .config = ALC880_LG }, + { .pci_subvendor = 0x1854, .pci_subdevice = 0x0068, .config = ALC880_LG }, { .modelname = "lg-lw", .config = ALC880_LG_LW }, { .pci_subvendor = 0x1854, .pci_subdevice = 0x0018, .config = ALC880_LG_LW }, @@ -2738,7 +2804,8 @@ static int patch_alc880(struct hda_codec *codec) board_config = snd_hda_check_board_config(codec, alc880_cfg_tbl); if (board_config < 0 || board_config >= ALC880_MODEL_LAST) { - printk(KERN_INFO "hda_codec: Unknown model for ALC880, trying auto-probe from BIOS...\n"); + printk(KERN_INFO "hda_codec: Unknown model for ALC880, " + "trying auto-probe from BIOS...\n"); board_config = ALC880_AUTO; } @@ -2749,7 +2816,9 @@ static int patch_alc880(struct hda_codec *codec) alc_free(codec); return err; } else if (! err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 3-stack mode...\n"); + printk(KERN_INFO + "hda_codec: Cannot set up configuration " + "from BIOS. Using 3-stack mode...\n"); board_config = ALC880_3ST; } } @@ -3105,6 +3174,7 @@ static struct hda_verb alc260_init_verbs[] = { { } }; +#if 0 /* should be identical with alc260_init_verbs? */ static struct hda_verb alc260_hp_init_verbs[] = { /* Headphone and output */ {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, @@ -3151,6 +3221,7 @@ static struct hda_verb alc260_hp_init_verbs[] = { {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, { } }; +#endif static struct hda_verb alc260_hp_3013_init_verbs[] = { /* Line out and output */ @@ -3822,12 +3893,16 @@ static struct hda_board_config alc260_cfg_tbl[] = { { .modelname = "basic", .config = ALC260_BASIC }, { .pci_subvendor = 0x104d, .pci_subdevice = 0x81bb, .config = ALC260_BASIC }, /* Sony VAIO */ + { .pci_subvendor = 0x104d, .pci_subdevice = 0x81cc, + .config = ALC260_BASIC }, /* Sony VAIO VGN-S3HP */ + { .pci_subvendor = 0x104d, .pci_subdevice = 0x81cd, + .config = ALC260_BASIC }, /* Sony VAIO */ { .pci_subvendor = 0x152d, .pci_subdevice = 0x0729, .config = ALC260_BASIC }, /* CTL Travel Master U553W */ { .modelname = "hp", .config = ALC260_HP }, { .pci_subvendor = 0x103c, .pci_subdevice = 0x3010, .config = ALC260_HP }, { .pci_subvendor = 0x103c, .pci_subdevice = 0x3011, .config = ALC260_HP }, - { .pci_subvendor = 0x103c, .pci_subdevice = 0x3012, .config = ALC260_HP }, + { .pci_subvendor = 0x103c, .pci_subdevice = 0x3012, .config = ALC260_HP_3013 }, { .pci_subvendor = 0x103c, .pci_subdevice = 0x3013, .config = ALC260_HP_3013 }, { .pci_subvendor = 0x103c, .pci_subdevice = 0x3014, .config = ALC260_HP }, { .pci_subvendor = 0x103c, .pci_subdevice = 0x3015, .config = ALC260_HP }, @@ -3862,7 +3937,7 @@ static struct alc_config_preset alc260_presets[] = { .mixers = { alc260_base_output_mixer, alc260_input_mixer, alc260_capture_alt_mixer }, - .init_verbs = { alc260_hp_init_verbs }, + .init_verbs = { alc260_init_verbs }, .num_dacs = ARRAY_SIZE(alc260_dac_nids), .dac_nids = alc260_dac_nids, .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids), @@ -3940,7 +4015,8 @@ static int patch_alc260(struct hda_codec *codec) board_config = snd_hda_check_board_config(codec, alc260_cfg_tbl); if (board_config < 0 || board_config >= ALC260_MODEL_LAST) { - snd_printd(KERN_INFO "hda_codec: Unknown model for ALC260\n"); + snd_printd(KERN_INFO "hda_codec: Unknown model for ALC260, " + "trying auto-probe from BIOS...\n"); board_config = ALC260_AUTO; } @@ -3951,7 +4027,9 @@ static int patch_alc260(struct hda_codec *codec) alc_free(codec); return err; } else if (! err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using base mode...\n"); + printk(KERN_INFO + "hda_codec: Cannot set up configuration " + "from BIOS. Using base mode...\n"); board_config = ALC260_BASIC; } } @@ -4094,21 +4172,6 @@ static struct snd_kcontrol_new alc882_base_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", 0x07, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 3, - .info = alc882_mux_enum_info, - .get = alc882_mux_enum_get, - .put = alc882_mux_enum_put, - }, { } /* end */ }; @@ -4328,9 +4391,12 @@ static struct snd_kcontrol_new alc882_capture_mixer[] = { static struct hda_board_config alc882_cfg_tbl[] = { { .modelname = "3stack-dig", .config = ALC882_3ST_DIG }, { .modelname = "6stack-dig", .config = ALC882_6ST_DIG }, - { .pci_subvendor = 0x1462, .pci_subdevice = 0x6668, .config = ALC882_6ST_DIG }, /* MSI */ - { .pci_subvendor = 0x105b, .pci_subdevice = 0x6668, .config = ALC882_6ST_DIG }, /* Foxconn */ - { .pci_subvendor = 0x1019, .pci_subdevice = 0x6668, .config = ALC882_6ST_DIG }, /* ECS */ + { .pci_subvendor = 0x1462, .pci_subdevice = 0x6668, + .config = ALC882_6ST_DIG }, /* MSI */ + { .pci_subvendor = 0x105b, .pci_subdevice = 0x6668, + .config = ALC882_6ST_DIG }, /* Foxconn */ + { .pci_subvendor = 0x1019, .pci_subdevice = 0x6668, + .config = ALC882_6ST_DIG }, /* ECS to Intel*/ { .modelname = "auto", .config = ALC882_AUTO }, {} }; @@ -4342,8 +4408,6 @@ static struct alc_config_preset alc882_presets[] = { .num_dacs = ARRAY_SIZE(alc882_dac_nids), .dac_nids = alc882_dac_nids, .dig_out_nid = ALC882_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc882_adc_nids), - .adc_nids = alc882_adc_nids, .dig_in_nid = ALC882_DIGIN_NID, .num_channel_mode = ARRAY_SIZE(alc882_ch_modes), .channel_mode = alc882_ch_modes, @@ -4355,8 +4419,6 @@ static struct alc_config_preset alc882_presets[] = { .num_dacs = ARRAY_SIZE(alc882_dac_nids), .dac_nids = alc882_dac_nids, .dig_out_nid = ALC882_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc882_adc_nids), - .adc_nids = alc882_adc_nids, .dig_in_nid = ALC882_DIGIN_NID, .num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes), .channel_mode = alc882_sixstack_modes, @@ -4451,10 +4513,6 @@ static void alc882_auto_init(struct hda_codec *codec) alc882_auto_init_analog_input(codec); } -/* - * ALC882 Headphone poll in 3.5.1a or 3.5.2 - */ - static int patch_alc882(struct hda_codec *codec) { struct alc_spec *spec; @@ -4469,7 +4527,8 @@ static int patch_alc882(struct hda_codec *codec) board_config = snd_hda_check_board_config(codec, alc882_cfg_tbl); if (board_config < 0 || board_config >= ALC882_MODEL_LAST) { - printk(KERN_INFO "hda_codec: Unknown model for ALC882, trying auto-probe from BIOS...\n"); + printk(KERN_INFO "hda_codec: Unknown model for ALC882, " + "trying auto-probe from BIOS...\n"); board_config = ALC882_AUTO; } @@ -4480,7 +4539,9 @@ static int patch_alc882(struct hda_codec *codec) alc_free(codec); return err; } else if (! err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using base mode...\n"); + printk(KERN_INFO + "hda_codec: Cannot set up configuration " + "from BIOS. Using base mode...\n"); board_config = ALC882_3ST_DIG; } } @@ -4521,6 +4582,652 @@ static int patch_alc882(struct hda_codec *codec) } /* + * ALC883 support + * + * ALC883 is almost identical with ALC880 but has cleaner and more flexible + * configuration. Each pin widget can choose any input DACs and a mixer. + * Each ADC is connected from a mixer of all inputs. This makes possible + * 6-channel independent captures. + * + * In addition, an independent DAC for the multi-playback (not used in this + * driver yet). + */ +#define ALC883_DIGOUT_NID 0x06 +#define ALC883_DIGIN_NID 0x0a + +static hda_nid_t alc883_dac_nids[4] = { + /* front, rear, clfe, rear_surr */ + 0x02, 0x04, 0x03, 0x05 +}; + +static hda_nid_t alc883_adc_nids[2] = { + /* ADC1-2 */ + 0x08, 0x09, +}; +/* input MUX */ +/* FIXME: should be a matrix-type input source selection */ + +static struct hda_input_mux alc883_capture_source = { + .num_items = 4, + .items = { + { "Mic", 0x0 }, + { "Front Mic", 0x1 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + }, +}; +#define alc883_mux_enum_info alc_mux_enum_info +#define alc883_mux_enum_get alc_mux_enum_get + +static int alc883_mux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + const struct hda_input_mux *imux = spec->input_mux; + unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + static hda_nid_t capture_mixers[3] = { 0x24, 0x23, 0x22 }; + hda_nid_t nid = capture_mixers[adc_idx]; + unsigned int *cur_val = &spec->cur_mux[adc_idx]; + unsigned int i, idx; + + idx = ucontrol->value.enumerated.item[0]; + if (idx >= imux->num_items) + idx = imux->num_items - 1; + if (*cur_val == idx && ! codec->in_resume) + return 0; + for (i = 0; i < imux->num_items; i++) { + unsigned int v = (i == idx) ? 0x7000 : 0x7080; + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + v | (imux->items[i].index << 8)); + } + *cur_val = idx; + return 1; +} +/* + * 2ch mode + */ +static struct hda_channel_mode alc883_3ST_2ch_modes[1] = { + { 2, NULL } +}; + +/* + * 2ch mode + */ +static struct hda_verb alc883_3ST_ch2_init[] = { + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { } /* end */ +}; + +/* + * 6ch mode + */ +static struct hda_verb alc883_3ST_ch6_init[] = { + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { } /* end */ +}; + +static struct hda_channel_mode alc883_3ST_6ch_modes[2] = { + { 2, alc883_3ST_ch2_init }, + { 6, alc883_3ST_ch6_init }, +}; + +/* + * 6ch mode + */ +static struct hda_verb alc883_sixstack_ch6_init[] = { + { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, + { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { } /* end */ +}; + +/* + * 8ch mode + */ +static struct hda_verb alc883_sixstack_ch8_init[] = { + { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { } /* end */ +}; + +static struct hda_channel_mode alc883_sixstack_modes[2] = { + { 6, alc883_sixstack_ch6_init }, + { 8, alc883_sixstack_ch8_init }, +}; + +/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17 + * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b + */ + +static struct snd_kcontrol_new alc883_base_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = alc883_mux_enum_info, + .get = alc883_mux_enum_get, + .put = alc883_mux_enum_put, + }, + { } /* end */ +}; + +static struct snd_kcontrol_new alc883_3ST_2ch_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = alc883_mux_enum_info, + .get = alc883_mux_enum_get, + .put = alc883_mux_enum_put, + }, + { } /* end */ +}; + +static struct snd_kcontrol_new alc883_3ST_6ch_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = alc883_mux_enum_info, + .get = alc883_mux_enum_get, + .put = alc883_mux_enum_put, + }, + { } /* end */ +}; + +static struct snd_kcontrol_new alc883_chmode_mixer[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Channel Mode", + .info = alc_ch_mode_info, + .get = alc_ch_mode_get, + .put = alc_ch_mode_put, + }, + { } /* end */ +}; + +static struct hda_verb alc883_init_verbs[] = { + /* ADC1: mute amp left and right */ + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* ADC2: mute amp left and right */ + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* Front mixer: unmute input/output amp left and right (volume = 0) */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* Rear mixer */ + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* CLFE mixer */ + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* Side mixer */ + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + + /* Front Pin: output 0 (0x0c) */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* Rear Pin: output 1 (0x0d) */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, + /* CLFE Pin: output 2 (0x0e) */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x16, AC_VERB_SET_CONNECT_SEL, 0x02}, + /* Side Pin: output 3 (0x0f) */ + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, + /* Mic (rear) pin: input vref at 80% */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Front Mic pin: input vref at 80% */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Line In pin: input */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Line-2 In: Headphone output (output 0 - 0x0c) */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* CD pin widget for input */ + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + + /* FIXME: use matrix-type input source selection */ + /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ + /* Input mixer2 */ + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + /* Input mixer3 */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + { } +}; + +/* + * generic initialization of ADC, input mixers and output mixers + */ +static struct hda_verb alc883_auto_init_verbs[] = { + /* + * Unmute ADC0-2 and set the default input to mic-in + */ + {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback + * mixer widget + * Note: PASD motherboards uses the Line In 2 as the input for front panel + * mic (mic 2) + */ + /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + + /* + * Set up output mixers (0x0c - 0x0f) + */ + /* set vol=0 to output mixers */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + /* set up input amps for analog loopback */ + /* Amp Indices: DAC = 0, mixer = 1 */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + /* FIXME: use matrix-type input source selection */ + /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ + /* Input mixer1 */ + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + //{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + /* Input mixer2 */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + //{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + + { } +}; + +/* capture mixer elements */ +static struct snd_kcontrol_new alc883_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* The multiple "Capture Source" controls confuse alsamixer + * So call somewhat different.. + * FIXME: the controls appear in the "playback" view! + */ + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = alc882_mux_enum_info, + .get = alc882_mux_enum_get, + .put = alc882_mux_enum_put, + }, + { } /* end */ +}; + +/* pcm configuration: identiacal with ALC880 */ +#define alc883_pcm_analog_playback alc880_pcm_analog_playback +#define alc883_pcm_analog_capture alc880_pcm_analog_capture +#define alc883_pcm_digital_playback alc880_pcm_digital_playback +#define alc883_pcm_digital_capture alc880_pcm_digital_capture + +/* + * configuration and preset + */ +static struct hda_board_config alc883_cfg_tbl[] = { + { .modelname = "3stack-dig", .config = ALC883_3ST_2ch_DIG }, + { .modelname = "6stack-dig", .config = ALC883_6ST_DIG }, + { .modelname = "6stack-dig-demo", .config = ALC888_DEMO_BOARD }, + { .pci_subvendor = 0x1462, .pci_subdevice = 0x6668, + .config = ALC883_6ST_DIG }, /* MSI */ + { .pci_subvendor = 0x105b, .pci_subdevice = 0x6668, + .config = ALC883_6ST_DIG }, /* Foxconn */ + { .pci_subvendor = 0x1019, .pci_subdevice = 0x6668, + .config = ALC883_3ST_6ch_DIG }, /* ECS to Intel*/ + { .pci_subvendor = 0x108e, .pci_subdevice = 0x534d, + .config = ALC883_3ST_6ch }, + { .modelname = "auto", .config = ALC883_AUTO }, + {} +}; + +static struct alc_config_preset alc883_presets[] = { + [ALC883_3ST_2ch_DIG] = { + .mixers = { alc883_3ST_2ch_mixer }, + .init_verbs = { alc883_init_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), + .adc_nids = alc883_adc_nids, + .dig_in_nid = ALC883_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_capture_source, + }, + [ALC883_3ST_6ch_DIG] = { + .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), + .adc_nids = alc883_adc_nids, + .dig_in_nid = ALC883_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), + .channel_mode = alc883_3ST_6ch_modes, + .input_mux = &alc883_capture_source, + }, + [ALC883_3ST_6ch] = { + .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), + .adc_nids = alc883_adc_nids, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), + .channel_mode = alc883_3ST_6ch_modes, + .input_mux = &alc883_capture_source, + }, + [ALC883_6ST_DIG] = { + .mixers = { alc883_base_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), + .adc_nids = alc883_adc_nids, + .dig_in_nid = ALC883_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), + .channel_mode = alc883_sixstack_modes, + .input_mux = &alc883_capture_source, + }, + [ALC888_DEMO_BOARD] = { + .mixers = { alc883_base_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), + .adc_nids = alc883_adc_nids, + .dig_in_nid = ALC883_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), + .channel_mode = alc883_sixstack_modes, + .input_mux = &alc883_capture_source, + }, +}; + + +/* + * BIOS auto configuration + */ +static void alc883_auto_set_output_and_unmute(struct hda_codec *codec, + hda_nid_t nid, int pin_type, + int dac_idx) +{ + /* set as output */ + struct alc_spec *spec = codec->spec; + int idx; + + if (spec->multiout.dac_nids[dac_idx] == 0x25) + idx = 4; + else + idx = spec->multiout.dac_nids[dac_idx] - 2; + + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + pin_type); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_UNMUTE); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx); + +} + +static void alc883_auto_init_multi_out(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int i; + + for (i = 0; i <= HDA_SIDE; i++) { + hda_nid_t nid = spec->autocfg.line_out_pins[i]; + if (nid) + alc883_auto_set_output_and_unmute(codec, nid, PIN_OUT, i); + } +} + +static void alc883_auto_init_hp_out(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t pin; + + pin = spec->autocfg.hp_pin; + if (pin) /* connect to front */ + /* use dac 0 */ + alc883_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); +} + +#define alc883_is_input_pin(nid) alc880_is_input_pin(nid) +#define ALC883_PIN_CD_NID ALC880_PIN_CD_NID + +static void alc883_auto_init_analog_input(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int i; + + for (i = 0; i < AUTO_PIN_LAST; i++) { + hda_nid_t nid = spec->autocfg.input_pins[i]; + if (alc883_is_input_pin(nid)) { + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + (i <= AUTO_PIN_FRONT_MIC ? + PIN_VREF80 : PIN_IN)); + if (nid != ALC883_PIN_CD_NID) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_MUTE); + } + } +} + +/* almost identical with ALC880 parser... */ +static int alc883_parse_auto_config(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int err = alc880_parse_auto_config(codec); + + if (err < 0) + return err; + else if (err > 0) + /* hack - override the init verbs */ + spec->init_verbs[0] = alc883_auto_init_verbs; + spec->mixers[spec->num_mixers] = alc883_capture_mixer; + spec->num_mixers++; + return err; +} + +/* additional initialization for auto-configuration model */ +static void alc883_auto_init(struct hda_codec *codec) +{ + alc883_auto_init_multi_out(codec); + alc883_auto_init_hp_out(codec); + alc883_auto_init_analog_input(codec); +} + +static int patch_alc883(struct hda_codec *codec) +{ + struct alc_spec *spec; + int err, board_config; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + + board_config = snd_hda_check_board_config(codec, alc883_cfg_tbl); + if (board_config < 0 || board_config >= ALC883_MODEL_LAST) { + printk(KERN_INFO "hda_codec: Unknown model for ALC883, " + "trying auto-probe from BIOS...\n"); + board_config = ALC883_AUTO; + } + + if (board_config == ALC883_AUTO) { + /* automatic parse from the BIOS config */ + err = alc883_parse_auto_config(codec); + if (err < 0) { + alc_free(codec); + return err; + } else if (! err) { + printk(KERN_INFO + "hda_codec: Cannot set up configuration " + "from BIOS. Using base mode...\n"); + board_config = ALC883_3ST_2ch_DIG; + } + } + + if (board_config != ALC883_AUTO) + setup_preset(spec, &alc883_presets[board_config]); + + spec->stream_name_analog = "ALC883 Analog"; + spec->stream_analog_playback = &alc883_pcm_analog_playback; + spec->stream_analog_capture = &alc883_pcm_analog_capture; + + spec->stream_name_digital = "ALC883 Digital"; + spec->stream_digital_playback = &alc883_pcm_digital_playback; + spec->stream_digital_capture = &alc883_pcm_digital_capture; + + spec->adc_nids = alc883_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids); + + codec->patch_ops = alc_patch_ops; + if (board_config == ALC883_AUTO) + spec->init_hook = alc883_auto_init; + + return 0; +} + +/* * ALC262 support */ @@ -4554,6 +5261,28 @@ static struct snd_kcontrol_new alc262_base_mixer[] = { { } /* end */ }; +static struct snd_kcontrol_new alc262_HP_BPC_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("PC Beep Playback Volume", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_MUTE("PC Beep Playback Switch", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_VOLUME("AUX IN Playback Volume", 0x0b, 0x06, HDA_INPUT), + HDA_CODEC_MUTE("AUX IN Playback Switch", 0x0b, 0x06, HDA_INPUT), + { } /* end */ +}; + #define alc262_capture_mixer alc882_capture_mixer #define alc262_capture_alt_mixer alc882_capture_alt_mixer @@ -4657,6 +5386,17 @@ static struct hda_input_mux alc262_fujitsu_capture_source = { }, }; +static struct hda_input_mux alc262_HP_capture_source = { + .num_items = 5, + .items = { + { "Mic", 0x0 }, + { "Front Mic", 0x3 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + { "AUX IN", 0x6 }, + }, +}; + /* mute/unmute internal speaker according to the hp jack and mute state */ static void alc262_fujitsu_automute(struct hda_codec *codec, int force) { @@ -4880,6 +5620,93 @@ static struct hda_verb alc262_volume_init_verbs[] = { { } }; +static struct hda_verb alc262_HP_BPC_init_verbs[] = { + /* + * Unmute ADC0-2 and set the default input to mic-in + */ + {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback + * mixer widget + * Note: PASD motherboards uses the Line In 2 as the input for front panel + * mic (mic 2) + */ + /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(6)}, + + /* + * Set up output mixers (0x0c - 0x0e) + */ + /* set vol=0 to output mixers */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + + /* set up input amps for analog loopback */ + /* Amp Indices: DAC = 0, mixer = 1 */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 }, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 }, + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, + {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, + + + /* FIXME: use matrix-type input source selection */ + /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ + /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, + /* Input mixer2 */ + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, + /* Input mixer3 */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, + + { } +}; + /* pcm configuration: identiacal with ALC880 */ #define alc262_pcm_analog_playback alc880_pcm_analog_playback #define alc262_pcm_analog_capture alc880_pcm_analog_capture @@ -4940,7 +5767,16 @@ static void alc262_auto_init(struct hda_codec *codec) static struct hda_board_config alc262_cfg_tbl[] = { { .modelname = "basic", .config = ALC262_BASIC }, { .modelname = "fujitsu", .config = ALC262_FUJITSU }, - { .pci_subvendor = 0x10cf, .pci_subdevice = 0x1397, .config = ALC262_FUJITSU }, + { .pci_subvendor = 0x10cf, .pci_subdevice = 0x1397, + .config = ALC262_FUJITSU }, + { .pci_subvendor = 0x103c, .pci_subdevice = 0x208c, + .config = ALC262_HP_BPC }, /* xw4400 */ + { .pci_subvendor = 0x103c, .pci_subdevice = 0x3014, + .config = ALC262_HP_BPC }, /* xw6400 */ + { .pci_subvendor = 0x103c, .pci_subdevice = 0x3015, + .config = ALC262_HP_BPC }, /* xw8400 */ + { .pci_subvendor = 0x103c, .pci_subdevice = 0x12fe, + .config = ALC262_HP_BPC }, /* xw9400 */ { .modelname = "auto", .config = ALC262_AUTO }, {} }; @@ -4968,6 +5804,16 @@ static struct alc_config_preset alc262_presets[] = { .input_mux = &alc262_fujitsu_capture_source, .unsol_event = alc262_fujitsu_unsol_event, }, + [ALC262_HP_BPC] = { + .mixers = { alc262_HP_BPC_mixer }, + .init_verbs = { alc262_HP_BPC_init_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_HP_capture_source, + }, }; static int patch_alc262(struct hda_codec *codec) @@ -4993,8 +5839,10 @@ static int patch_alc262(struct hda_codec *codec) #endif board_config = snd_hda_check_board_config(codec, alc262_cfg_tbl); + if (board_config < 0 || board_config >= ALC262_MODEL_LAST) { - printk(KERN_INFO "hda_codec: Unknown model for ALC262, trying auto-probe from BIOS...\n"); + printk(KERN_INFO "hda_codec: Unknown model for ALC262, " + "trying auto-probe from BIOS...\n"); board_config = ALC262_AUTO; } @@ -5005,7 +5853,9 @@ static int patch_alc262(struct hda_codec *codec) alc_free(codec); return err; } else if (! err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using base mode...\n"); + printk(KERN_INFO + "hda_codec: Cannot set up configuration " + "from BIOS. Using base mode...\n"); board_config = ALC262_BASIC; } } @@ -5046,7 +5896,6 @@ static int patch_alc262(struct hda_codec *codec) return 0; } - /* * ALC861 channel source setting (2/6 channel selection for 3-stack) */ @@ -5061,9 +5910,11 @@ static struct hda_verb alc861_threestack_ch2_init[] = { /* set pin widget 18h (mic1/2) for input, for mic also enable the vref */ { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c }, - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, //mic - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, //line in + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c }, +#if 0 + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/ + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, /*line-in*/ +#endif { } /* end */ }; /* @@ -5077,11 +5928,13 @@ static struct hda_verb alc861_threestack_ch6_init[] = { { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, { 0x0c, AC_VERB_SET_CONNECT_SEL, 0x00 }, - { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00 }, + { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00 }, - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 }, - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, //mic - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8)) }, //line in + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 }, +#if 0 + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, /*mic*/ + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8)) }, /*line in*/ +#endif { } /* end */ }; @@ -5365,6 +6218,11 @@ static hda_nid_t alc861_dac_nids[4] = { 0x03, 0x06, 0x05, 0x04 }; +static hda_nid_t alc660_dac_nids[3] = { + /* front, clfe, surround */ + 0x03, 0x05, 0x06 +}; + static hda_nid_t alc861_adc_nids[1] = { /* ADC0-2 */ 0x08, @@ -5617,7 +6475,10 @@ static void alc861_auto_init(struct hda_codec *codec) */ static struct hda_board_config alc861_cfg_tbl[] = { { .modelname = "3stack", .config = ALC861_3ST }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xd600, .config = ALC861_3ST }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xd600, + .config = ALC861_3ST }, + { .pci_subvendor = 0x1043, .pci_subdevice = 0x81e7, + .config = ALC660_3ST }, { .modelname = "3stack-dig", .config = ALC861_3ST_DIG }, { .modelname = "6stack-dig", .config = ALC861_6ST_DIG }, { .modelname = "auto", .config = ALC861_AUTO }, @@ -5660,6 +6521,17 @@ static struct alc_config_preset alc861_presets[] = { .adc_nids = alc861_adc_nids, .input_mux = &alc861_capture_source, }, + [ALC660_3ST] = { + .mixers = { alc861_3ST_mixer }, + .init_verbs = { alc861_threestack_init_verbs }, + .num_dacs = ARRAY_SIZE(alc660_dac_nids), + .dac_nids = alc660_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes), + .channel_mode = alc861_threestack_modes, + .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), + .adc_nids = alc861_adc_nids, + .input_mux = &alc861_capture_source, + }, }; @@ -5676,8 +6548,10 @@ static int patch_alc861(struct hda_codec *codec) codec->spec = spec; board_config = snd_hda_check_board_config(codec, alc861_cfg_tbl); + if (board_config < 0 || board_config >= ALC861_MODEL_LAST) { - printk(KERN_INFO "hda_codec: Unknown model for ALC861, trying auto-probe from BIOS...\n"); + printk(KERN_INFO "hda_codec: Unknown model for ALC861, " + "trying auto-probe from BIOS...\n"); board_config = ALC861_AUTO; } @@ -5688,7 +6562,9 @@ static int patch_alc861(struct hda_codec *codec) alc_free(codec); return err; } else if (! err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using base mode...\n"); + printk(KERN_INFO + "hda_codec: Cannot set up configuration " + "from BIOS. Using base mode...\n"); board_config = ALC861_3ST_DIG; } } @@ -5719,8 +6595,12 @@ struct hda_codec_preset snd_hda_preset_realtek[] = { { .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 }, { .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 }, { .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 }, - { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc882 }, + { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc883 }, { .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 }, - { .id = 0x10ec0861, .name = "ALC861", .patch = patch_alc861 }, + { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc883 }, + { .id = 0x10ec0861, .rev = 0x100300, .name = "ALC861", + .patch = patch_alc861 }, + { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660", + .patch = patch_alc861 }, {} /* terminator */ }; diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 8c440fb9860..fb4bed0759d 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -41,6 +41,10 @@ #define STAC_REF 0 #define STAC_D945GTP3 1 #define STAC_D945GTP5 2 +#define STAC_MACMINI 3 +#define STAC_D965_2112 4 +#define STAC_D965_284B 5 +#define STAC_922X_MODELS 6 /* number of 922x models */ struct sigmatel_spec { struct snd_kcontrol_new *mixers[4]; @@ -52,6 +56,7 @@ struct sigmatel_spec { unsigned int mic_switch: 1; unsigned int alt_switch: 1; unsigned int hp_detect: 1; + unsigned int gpio_mute: 1; /* playback */ struct hda_multi_out multiout; @@ -105,10 +110,24 @@ static hda_nid_t stac922x_adc_nids[2] = { 0x06, 0x07, }; +static hda_nid_t stac9227_adc_nids[2] = { + 0x07, 0x08, +}; + +#if 0 +static hda_nid_t d965_2112_dac_nids[3] = { + 0x02, 0x03, 0x05, +}; +#endif + static hda_nid_t stac922x_mux_nids[2] = { 0x12, 0x13, }; +static hda_nid_t stac9227_mux_nids[2] = { + 0x15, 0x16, +}; + static hda_nid_t stac927x_adc_nids[3] = { 0x07, 0x08, 0x09 }; @@ -171,6 +190,24 @@ static struct hda_verb stac922x_core_init[] = { {} }; +static struct hda_verb stac9227_core_init[] = { + /* set master volume and direct control */ + { 0x16, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, + /* unmute node 0x1b */ + { 0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + {} +}; + +static struct hda_verb d965_2112_core_init[] = { + /* set master volume and direct control */ + { 0x16, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, + /* unmute node 0x1b */ + { 0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + /* select node 0x03 as DAC */ + { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x01}, + {} +}; + static struct hda_verb stac927x_core_init[] = { /* set master volume and direct control */ { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, @@ -210,6 +247,21 @@ static struct snd_kcontrol_new stac922x_mixer[] = { { } /* end */ }; +/* This needs to be generated dynamically based on sequence */ +static struct snd_kcontrol_new stac9227_mixer[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Input Source", + .count = 1, + .info = stac92xx_mux_enum_info, + .get = stac92xx_mux_enum_get, + .put = stac92xx_mux_enum_put, + }, + HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x1b, 0x0, HDA_OUTPUT), + { } /* end */ +}; + static snd_kcontrol_new_t stac927x_mixer[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -289,10 +341,17 @@ static unsigned int d945gtp5_pin_configs[10] = { 0x02a19320, 0x40000100, }; -static unsigned int *stac922x_brd_tbl[] = { - ref922x_pin_configs, - d945gtp3_pin_configs, - d945gtp5_pin_configs, +static unsigned int d965_2112_pin_configs[10] = { + 0x0221401f, 0x40000100, 0x40000100, 0x01014011, + 0x01a19021, 0x01813024, 0x01452130, 0x40000100, + 0x02a19320, 0x40000100, +}; + +static unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = { + [STAC_REF] = ref922x_pin_configs, + [STAC_D945GTP3] = d945gtp3_pin_configs, + [STAC_D945GTP5] = d945gtp5_pin_configs, + [STAC_D965_2112] = d965_2112_pin_configs, }; static struct hda_board_config stac922x_cfg_tbl[] = { @@ -324,6 +383,15 @@ static struct hda_board_config stac922x_cfg_tbl[] = { { .pci_subvendor = PCI_VENDOR_ID_INTEL, .pci_subdevice = 0x0417, .config = STAC_D945GTP5 }, /* Intel D975XBK - 5 Stack */ + { .pci_subvendor = 0x8384, + .pci_subdevice = 0x7680, + .config = STAC_MACMINI }, /* Apple Mac Mini (early 2006) */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x2112, + .config = STAC_D965_2112 }, + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x284b, + .config = STAC_D965_284B }, {} /* terminator */ }; @@ -707,7 +775,8 @@ static int stac92xx_add_dyn_out_pins(struct hda_codec *codec, struct auto_pin_cf * A and B is not supported. */ /* fill in the dac_nids table from the parsed pin configuration */ -static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec, const struct auto_pin_cfg *cfg) +static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec, + const struct auto_pin_cfg *cfg) { struct sigmatel_spec *spec = codec->spec; hda_nid_t nid; @@ -726,10 +795,13 @@ static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec, const struct aut } /* add playback controls from the parsed DAC table */ -static int stac92xx_auto_create_multi_out_ctls(struct sigmatel_spec *spec, const struct auto_pin_cfg *cfg) +static int stac92xx_auto_create_multi_out_ctls(struct sigmatel_spec *spec, + const struct auto_pin_cfg *cfg) { char name[32]; - static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" }; + static const char *chname[4] = { + "Front", "Surround", NULL /*CLFE*/, "Side" + }; hda_nid_t nid; int i, err; @@ -841,6 +913,19 @@ static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const } } + if (imux->num_items == 1) { + /* + * Set the current input for the muxes. + * The STAC9221 has two input muxes with identical source + * NID lists. Hopefully this won't get confused. + */ + for (i = 0; i < spec->num_muxes; i++) { + snd_hda_codec_write(codec, spec->mux_nids[i], 0, + AC_VERB_SET_CONNECT_SEL, + imux->items[0].index); + } + } + return 0; } @@ -874,10 +959,12 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out return err; if (! spec->autocfg.line_outs) return 0; /* can't find valid pin config */ + if ((err = stac92xx_add_dyn_out_pins(codec, &spec->autocfg)) < 0) return err; - if ((err = stac92xx_auto_fill_dac_nids(codec, &spec->autocfg)) < 0) - return err; + if (spec->multiout.num_dacs == 0) + if ((err = stac92xx_auto_fill_dac_nids(codec, &spec->autocfg)) < 0) + return err; if ((err = stac92xx_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 || (err = stac92xx_auto_create_hp_ctls(codec, &spec->autocfg)) < 0 || @@ -946,6 +1033,45 @@ static int stac9200_parse_auto_config(struct hda_codec *codec) return 1; } +/* + * Early 2006 Intel Macintoshes with STAC9220X5 codecs seem to have a + * funky external mute control using GPIO pins. + */ + +static void stac922x_gpio_mute(struct hda_codec *codec, int pin, int muted) +{ + 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); + + gpiomask = snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_GET_GPIO_MASK, 0); + gpiomask |= (1 << pin); + + gpiodir = snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_GET_GPIO_DIRECTION, 0); + gpiodir |= (1 << pin); + + /* AppleHDA seems to do this -- WTF is this verb?? */ + 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); + + msleep(1); + + snd_hda_codec_write(codec, codec->afg, 0, + AC_VERB_SET_GPIO_DATA, gpiostate); +} + static int stac92xx_init(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; @@ -982,6 +1108,11 @@ 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); + } + return 0; } @@ -1131,8 +1262,9 @@ static int patch_stac922x(struct hda_codec *codec) codec->spec = spec; spec->board_config = snd_hda_check_board_config(codec, stac922x_cfg_tbl); if (spec->board_config < 0) - snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC922x, using BIOS defaults\n"); - else { + snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC922x, " + "using BIOS defaults\n"); + else if (stac922x_brd_tbl[spec->board_config] != NULL) { spec->num_pins = 10; spec->pin_nids = stac922x_pin_nids; spec->pin_configs = stac922x_brd_tbl[spec->board_config]; @@ -1147,6 +1279,25 @@ static int patch_stac922x(struct hda_codec *codec) spec->mixer = stac922x_mixer; spec->multiout.dac_nids = spec->dac_nids; + + switch (spec->board_config) { + case STAC_D965_2112: + spec->adc_nids = stac9227_adc_nids; + spec->mux_nids = stac9227_mux_nids; +#if 0 + spec->multiout.dac_nids = d965_2112_dac_nids; + spec->multiout.num_dacs = ARRAY_SIZE(d965_2112_dac_nids); +#endif + spec->init = d965_2112_core_init; + spec->mixer = stac9227_mixer; + break; + case STAC_D965_284B: + spec->adc_nids = stac9227_adc_nids; + spec->mux_nids = stac9227_mux_nids; + spec->init = stac9227_core_init; + spec->mixer = stac9227_mixer; + break; + } err = stac92xx_parse_auto_config(codec, 0x08, 0x09); if (err < 0) { @@ -1154,6 +1305,9 @@ static int patch_stac922x(struct hda_codec *codec) return err; } + if (spec->board_config == STAC_MACMINI) + spec->gpio_mute = 1; + codec->patch_ops = stac92xx_patch_ops; return 0; @@ -1262,13 +1416,13 @@ static int vaio_master_sw_put(struct snd_kcontrol *kcontrol, int change; change = snd_hda_codec_amp_update(codec, 0x02, 0, HDA_OUTPUT, 0, - 0x80, valp[0] & 0x80); + 0x80, (valp[0] ? 0 : 0x80)); change |= snd_hda_codec_amp_update(codec, 0x02, 1, HDA_OUTPUT, 0, - 0x80, valp[1] & 0x80); + 0x80, (valp[1] ? 0 : 0x80)); snd_hda_codec_amp_update(codec, 0x05, 0, HDA_OUTPUT, 0, - 0x80, valp[0] & 0x80); + 0x80, (valp[0] ? 0 : 0x80)); snd_hda_codec_amp_update(codec, 0x05, 1, HDA_OUTPUT, 0, - 0x80, valp[1] & 0x80); + 0x80, (valp[1] ? 0 : 0x80)); return change; } @@ -1370,6 +1524,12 @@ struct hda_codec_preset snd_hda_preset_sigmatel[] = { { .id = 0x83847681, .name = "STAC9220D/9223D A2", .patch = patch_stac922x }, { .id = 0x83847682, .name = "STAC9221 A2", .patch = patch_stac922x }, { .id = 0x83847683, .name = "STAC9221D A2", .patch = patch_stac922x }, + { .id = 0x83847618, .name = "STAC9227", .patch = patch_stac922x }, + { .id = 0x83847619, .name = "STAC9227", .patch = patch_stac922x }, + { .id = 0x83847616, .name = "STAC9228", .patch = patch_stac922x }, + { .id = 0x83847617, .name = "STAC9228", .patch = patch_stac922x }, + { .id = 0x83847614, .name = "STAC9229", .patch = patch_stac922x }, + { .id = 0x83847615, .name = "STAC9229", .patch = patch_stac922x }, { .id = 0x83847620, .name = "STAC9274", .patch = patch_stac927x }, { .id = 0x83847621, .name = "STAC9274D", .patch = patch_stac927x }, { .id = 0x83847622, .name = "STAC9273X", .patch = patch_stac927x }, diff --git a/sound/pci/ice1712/aureon.c b/sound/pci/ice1712/aureon.c index 336dc489aee..ca74f5b85f4 100644 --- a/sound/pci/ice1712/aureon.c +++ b/sound/pci/ice1712/aureon.c @@ -1281,9 +1281,15 @@ static int aureon_set_headphone_amp(struct snd_ice1712 *ice, int enable) tmp2 = tmp = snd_ice1712_gpio_read(ice); if (enable) - tmp |= AUREON_HP_SEL; + if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT) + tmp |= AUREON_HP_SEL; + else + tmp |= PRODIGY_HP_SEL; else - tmp &= ~ AUREON_HP_SEL; + if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT) + tmp &= ~ AUREON_HP_SEL; + else + tmp &= ~ PRODIGY_HP_SEL; if (tmp != tmp2) { snd_ice1712_gpio_write(ice, tmp); return 1; @@ -2079,16 +2085,16 @@ static unsigned char prodigy71_eeprom[] __devinitdata = { }; static unsigned char prodigy71lt_eeprom[] __devinitdata = { - 0x0b, /* SYSCINF: clock 512, spdif-in/ADC, 4DACs */ + 0x4b, /* SYSCINF: clock 512, spdif-in/ADC, 4DACs */ 0x80, /* ACLINK: I2S */ 0xfc, /* I2S: vol, 96k, 24bit, 192k */ - 0xc3, /* SPDUF: out-en, out-int */ - 0x00, /* GPIO_DIR */ - 0x07, /* GPIO_DIR1 */ - 0x00, /* GPIO_DIR2 */ - 0xff, /* GPIO_MASK */ - 0xf8, /* GPIO_MASK1 */ - 0xff, /* GPIO_MASK2 */ + 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 */ diff --git a/sound/pci/ice1712/aureon.h b/sound/pci/ice1712/aureon.h index 98a6752280f..3b7bea656c5 100644 --- a/sound/pci/ice1712/aureon.h +++ b/sound/pci/ice1712/aureon.h @@ -58,5 +58,6 @@ extern struct snd_ice1712_card_info snd_vt1724_aureon_cards[]; #define PRODIGY_WM_CS (1 << 8) #define PRODIGY_SPI_MOSI (1 << 10) #define PRODIGY_SPI_CLK (1 << 9) +#define PRODIGY_HP_SEL (1 << 5) #endif /* __SOUND_AUREON_H */ diff --git a/sound/pci/ice1712/ews.c b/sound/pci/ice1712/ews.c index 2c529e74138..b135389fec6 100644 --- a/sound/pci/ice1712/ews.c +++ b/sound/pci/ice1712/ews.c @@ -1031,6 +1031,9 @@ struct snd_ice1712_card_info snd_ice1712_ews_cards[] __devinitdata = { .model = "dmx6fire", .chip_init = snd_ice1712_ews_init, .build_controls = snd_ice1712_ews_add_controls, + .mpu401_1_name = "MIDI-Front DMX6fire", + .mpu401_2_name = "Wavetable DMX6fire", + .mpu401_2_info_flags = MPU401_INFO_OUTPUT, }, { } /* terminator */ }; diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index c56793b381e..845907159b7 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -61,7 +61,6 @@ #include <sound/core.h> #include <sound/cs8427.h> #include <sound/info.h> -#include <sound/mpu401.h> #include <sound/initval.h> #include <sound/asoundef.h> @@ -1596,7 +1595,7 @@ static void __devinit snd_ice1712_proc_init(struct snd_ice1712 * ice) struct snd_info_entry *entry; if (! snd_card_proc_new(ice->card, "ice1712", &entry)) - snd_info_set_text_ops(entry, ice, 1024, snd_ice1712_proc_read); + snd_info_set_text_ops(entry, ice, snd_ice1712_proc_read); } /* @@ -2398,13 +2397,14 @@ static int __devinit snd_ice1712_chip_init(struct snd_ice1712 *ice) udelay(200); outb(ICE1712_NATIVE, ICEREG(ice, CONTROL)); udelay(200); - if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_DMX6FIRE && !ice->dxr_enable) { - /* Limit active ADCs and DACs to 6; */ - /* Note: DXR extension not supported */ - pci_write_config_byte(ice->pci, 0x60, 0x2a); - } else { - pci_write_config_byte(ice->pci, 0x60, ice->eeprom.data[ICE_EEP1_CODEC]); - } + if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_DMX6FIRE && + !ice->dxr_enable) + /* Set eeprom value to limit active ADCs and DACs to 6; + * Also disable AC97 as no hardware in standard 6fire card/box + * Note: DXR extensions are not currently supported + */ + ice->eeprom.data[ICE_EEP1_CODEC] = 0x3a; + pci_write_config_byte(ice->pci, 0x60, ice->eeprom.data[ICE_EEP1_CODEC]); pci_write_config_byte(ice->pci, 0x61, ice->eeprom.data[ICE_EEP1_ACLINK]); pci_write_config_byte(ice->pci, 0x62, ice->eeprom.data[ICE_EEP1_I2SID]); pci_write_config_byte(ice->pci, 0x63, ice->eeprom.data[ICE_EEP1_SPDIF]); @@ -2737,21 +2737,38 @@ static int __devinit snd_ice1712_probe(struct pci_dev *pci, if (! c->no_mpu401) { if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712, - ICEREG(ice, MPU1_CTRL), 1, + ICEREG(ice, MPU1_CTRL), + (c->mpu401_1_info_flags | + MPU401_INFO_INTEGRATED), ice->irq, 0, &ice->rmidi[0])) < 0) { snd_card_free(card); return err; } - - if (ice->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_2xMPU401) + if (c->mpu401_1_name) + /* Prefered name available in card_info */ + snprintf(ice->rmidi[0]->name, + sizeof(ice->rmidi[0]->name), + "%s %d", c->mpu401_1_name, card->number); + + if (ice->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_2xMPU401) { + /* 2nd port used */ if ((err = snd_mpu401_uart_new(card, 1, MPU401_HW_ICE1712, - ICEREG(ice, MPU2_CTRL), 1, + ICEREG(ice, MPU2_CTRL), + (c->mpu401_2_info_flags | + MPU401_INFO_INTEGRATED), ice->irq, 0, &ice->rmidi[1])) < 0) { snd_card_free(card); return err; } + if (c->mpu401_2_name) + /* Prefered name available in card_info */ + snprintf(ice->rmidi[1]->name, + sizeof(ice->rmidi[1]->name), + "%s %d", c->mpu401_2_name, + card->number); + } } snd_ice1712_set_input_clock_source(ice, 0); diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h index 053f8e56fd6..ce27eac40d4 100644 --- a/sound/pci/ice1712/ice1712.h +++ b/sound/pci/ice1712/ice1712.h @@ -29,6 +29,7 @@ #include <sound/ak4xxx-adda.h> #include <sound/ak4114.h> #include <sound/pcm.h> +#include <sound/mpu401.h> /* @@ -495,6 +496,10 @@ struct snd_ice1712_card_info { int (*chip_init)(struct snd_ice1712 *); int (*build_controls)(struct snd_ice1712 *); unsigned int no_mpu401: 1; + unsigned int mpu401_1_info_flags; + unsigned int mpu401_2_info_flags; + const char *mpu401_1_name; + const char *mpu401_2_name; unsigned int eeprom_size; unsigned char *eeprom_data; }; diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index b1c007e022d..34a58c629f4 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -1293,7 +1293,7 @@ static void __devinit snd_vt1724_proc_init(struct snd_ice1712 * ice) struct snd_info_entry *entry; if (! snd_card_proc_new(ice->card, "ice1724", &entry)) - snd_info_set_text_ops(entry, ice, 1024, snd_vt1724_proc_read); + snd_info_set_text_ops(entry, ice, snd_vt1724_proc_read); } /* @@ -2388,7 +2388,8 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci, if (! c->no_mpu401) { if (ice->eeprom.data[ICE_EEP2_SYSCONF] & VT1724_CFG_MPU401) { if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712, - ICEREG1724(ice, MPU_CTRL), 1, + ICEREG1724(ice, MPU_CTRL), + MPU401_INFO_INTEGRATED, ice->irq, 0, &ice->rmidi[0])) < 0) { snd_card_free(card); diff --git a/sound/pci/ice1712/pontis.c b/sound/pci/ice1712/pontis.c index d23fb3fc213..0efcad9260a 100644 --- a/sound/pci/ice1712/pontis.c +++ b/sound/pci/ice1712/pontis.c @@ -680,9 +680,8 @@ 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, 1024, wm_proc_regs_read); + snd_info_set_text_ops(entry, ice, wm_proc_regs_read); entry->mode |= S_IWUSR; - entry->c.text.write_size = 1024; entry->c.text.write = wm_proc_regs_write; } } @@ -705,9 +704,8 @@ static void cs_proc_regs_read(struct snd_info_entry *entry, struct snd_info_buff static void cs_proc_init(struct snd_ice1712 *ice) { struct snd_info_entry *entry; - if (! snd_card_proc_new(ice->card, "cs_codec", &entry)) { - snd_info_set_text_ops(entry, ice, 1024, cs_proc_regs_read); - } + if (! snd_card_proc_new(ice->card, "cs_codec", &entry)) + snd_info_set_text_ops(entry, ice, cs_proc_regs_read); } diff --git a/sound/pci/ice1712/revo.c b/sound/pci/ice1712/revo.c index b5754b32b80..fec9440cb31 100644 --- a/sound/pci/ice1712/revo.c +++ b/sound/pci/ice1712/revo.c @@ -87,12 +87,25 @@ static void revo_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate) * initialize the chips on M-Audio Revolution cards */ +static unsigned int revo71_num_stereo_front[] = {2}; +static char *revo71_channel_names_front[] = {"PCM Playback Volume"}; + +static unsigned int revo71_num_stereo_surround[] = {1, 1, 2, 2}; +static char *revo71_channel_names_surround[] = {"PCM Center Playback Volume", "PCM LFE Playback Volume", + "PCM Side Playback Volume", "PCM Rear Playback Volume"}; + +static unsigned int revo51_num_stereo[] = {2, 1, 1, 2}; +static char *revo51_channel_names[] = {"PCM Playback Volume", "PCM Center Playback Volume", + "PCM LFE Playback Volume", "PCM Rear Playback Volume"}; + static struct snd_akm4xxx akm_revo_front __devinitdata = { .type = SND_AK4381, .num_dacs = 2, .ops = { .set_rate_val = revo_set_rate_val - } + }, + .num_stereo = revo71_num_stereo_front, + .channel_names = revo71_channel_names_front }; static struct snd_ak4xxx_private akm_revo_front_priv __devinitdata = { @@ -113,7 +126,9 @@ static struct snd_akm4xxx akm_revo_surround __devinitdata = { .num_dacs = 6, .ops = { .set_rate_val = revo_set_rate_val - } + }, + .num_stereo = revo71_num_stereo_surround, + .channel_names = revo71_channel_names_surround }; static struct snd_ak4xxx_private akm_revo_surround_priv __devinitdata = { @@ -133,7 +148,9 @@ static struct snd_akm4xxx akm_revo51 __devinitdata = { .num_dacs = 6, .ops = { .set_rate_val = revo_set_rate_val - } + }, + .num_stereo = revo51_num_stereo, + .channel_names = revo51_channel_names }; static struct snd_ak4xxx_private akm_revo51_priv __devinitdata = { diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 0df7602568e..edc14475ef8 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -66,7 +66,7 @@ MODULE_SUPPORTED_DEVICE("{{Intel,82801AA-ICH}," static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */ static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */ -static int ac97_clock = 0; +static int ac97_clock; static char *ac97_quirk; static int buggy_semaphore; static int buggy_irq = -1; /* auto-check */ @@ -1807,6 +1807,12 @@ static struct ac97_quirk ac97_quirks[] __devinitdata = { }, { .subvendor = 0x1028, + .subdevice = 0x014e, + .name = "Dell D800", /* STAC9750/51 */ + .type = AC97_TUNE_HP_ONLY + }, + { + .subvendor = 0x1028, .subdevice = 0x0163, .name = "Dell Unknown", /* STAC9750/51 */ .type = AC97_TUNE_HP_ONLY @@ -2645,7 +2651,7 @@ static void __devinit snd_intel8x0_proc_init(struct intel8x0 * chip) struct snd_info_entry *entry; if (! snd_card_proc_new(chip->card, "intel8x0", &entry)) - snd_info_set_text_ops(entry, chip, 1024, snd_intel8x0_proc_read); + snd_info_set_text_ops(entry, chip, snd_intel8x0_proc_read); } #else #define snd_intel8x0_proc_init(x) diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c index 720635f0cb8..24703d75b65 100644 --- a/sound/pci/intel8x0m.c +++ b/sound/pci/intel8x0m.c @@ -59,7 +59,7 @@ MODULE_SUPPORTED_DEVICE("{{Intel,82801AA-ICH}," static int index = -2; /* Exclude the first card */ static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */ -static int ac97_clock = 0; +static int ac97_clock; module_param(index, int, 0444); MODULE_PARM_DESC(index, "Index value for Intel i8x0 modemcard."); @@ -1092,7 +1092,7 @@ static void __devinit snd_intel8x0m_proc_init(struct intel8x0m * chip) struct snd_info_entry *entry; if (! snd_card_proc_new(chip->card, "intel8x0m", &entry)) - snd_info_set_text_ops(entry, chip, 1024, snd_intel8x0m_proc_read); + snd_info_set_text_ops(entry, chip, snd_intel8x0m_proc_read); } #else /* !CONFIG_PROC_FS */ #define snd_intel8x0m_proc_init(chip) diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c index e39fad1a420..6e97932de34 100644 --- a/sound/pci/korg1212/korg1212.c +++ b/sound/pci/korg1212/korg1212.c @@ -2085,7 +2085,7 @@ static void __devinit snd_korg1212_proc_init(struct snd_korg1212 *korg1212) struct snd_info_entry *entry; if (! snd_card_proc_new(korg1212->card, "korg1212", &entry)) - snd_info_set_text_ops(entry, korg1212, 1024, snd_korg1212_proc_read); + snd_info_set_text_ops(entry, korg1212, snd_korg1212_proc_read); } static int diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index 1928e06b6d8..1c344fbd964 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -2861,7 +2861,8 @@ snd_m3_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) #if 0 /* TODO: not supported yet */ /* TODO enable MIDI IRQ and I/O */ err = snd_mpu401_uart_new(chip->card, 0, MPU401_HW_MPU401, - chip->iobase + MPU401_DATA_PORT, 1, + chip->iobase + MPU401_DATA_PORT, + MPU401_INFO_INTEGRATED, chip->irq, 0, &chip->rmidi); if (err < 0) printk(KERN_WARNING "maestro3: no MIDI support.\n"); diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c index 09cc0786495..366c4a7e65c 100644 --- a/sound/pci/mixart/mixart.c +++ b/sound/pci/mixart/mixart.c @@ -1244,7 +1244,6 @@ static void __devinit snd_mixart_proc_init(struct snd_mixart *chip) /* text interface to read perf and temp meters */ if (! snd_card_proc_new(chip->card, "board_info", &entry)) { entry->private_data = chip; - entry->c.text.read_size = 1024; entry->c.text.read = snd_mixart_proc_read; } diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c index dafa2235aba..8198884b51e 100644 --- a/sound/pci/pcxhr/pcxhr.c +++ b/sound/pci/pcxhr/pcxhr.c @@ -1150,9 +1150,9 @@ static void __devinit pcxhr_proc_init(struct snd_pcxhr *chip) struct snd_info_entry *entry; if (! snd_card_proc_new(chip->card, "info", &entry)) - snd_info_set_text_ops(entry, chip, 1024, pcxhr_proc_info); + snd_info_set_text_ops(entry, chip, pcxhr_proc_info); if (! snd_card_proc_new(chip->card, "sync", &entry)) - snd_info_set_text_ops(entry, chip, 1024, pcxhr_proc_sync); + snd_info_set_text_ops(entry, chip, pcxhr_proc_sync); } /* end of proc interface */ diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c index d8cc985d724..5618ec9740b 100644 --- a/sound/pci/riptide/riptide.c +++ b/sound/pci/riptide/riptide.c @@ -1836,11 +1836,11 @@ static int snd_riptide_free(struct snd_riptide *chip) UNSET_GRESET(cif->hwport); kfree(chip->cif); } + if (chip->irq >= 0) + free_irq(chip->irq, chip); if (chip->fw_entry) release_firmware(chip->fw_entry); release_and_free_resource(chip->res_port); - if (chip->irq >= 0) - free_irq(chip->irq, chip); kfree(chip); return 0; } @@ -1992,7 +1992,7 @@ static void __devinit snd_riptide_proc_init(struct snd_riptide *chip) struct snd_info_entry *entry; if (!snd_card_proc_new(chip->card, "riptide", &entry)) - snd_info_set_text_ops(entry, chip, 4096, snd_riptide_proc_read); + snd_info_set_text_ops(entry, chip, snd_riptide_proc_read); } static int __devinit snd_riptide_mixer(struct snd_riptide *chip) diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c index 55b1d4838d9..2cb9fe98db2 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -1368,18 +1368,18 @@ static int __devinit snd_rme32_create(struct rme32 * rme32) return err; rme32->port = pci_resource_start(rme32->pci, 0); - if (request_irq(pci->irq, snd_rme32_interrupt, SA_INTERRUPT | SA_SHIRQ, "RME32", (void *) rme32)) { - snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); - return -EBUSY; - } - rme32->irq = pci->irq; - if ((rme32->iobase = ioremap_nocache(rme32->port, RME32_IO_SIZE)) == 0) { snd_printk(KERN_ERR "unable to remap memory region 0x%lx-0x%lx\n", rme32->port, rme32->port + RME32_IO_SIZE - 1); return -ENOMEM; } + if (request_irq(pci->irq, snd_rme32_interrupt, SA_INTERRUPT | SA_SHIRQ, "RME32", (void *) rme32)) { + snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); + return -EBUSY; + } + rme32->irq = pci->irq; + /* read the card's revision number */ pci_read_config_byte(pci, 8, &rme32->rev); @@ -1578,7 +1578,7 @@ static void __devinit snd_rme32_proc_init(struct rme32 * rme32) struct snd_info_entry *entry; if (! snd_card_proc_new(rme32->card, "rme32", &entry)) - snd_info_set_text_ops(entry, rme32, 1024, snd_rme32_proc_read); + snd_info_set_text_ops(entry, rme32, snd_rme32_proc_read); } /* diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index 3c1bc533d51..991cb18c14f 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -1151,6 +1151,25 @@ static struct snd_pcm_hw_constraint_list hw_constraints_period_bytes = { .mask = 0 }; +static void +rme96_set_buffer_size_constraint(struct rme96 *rme96, + struct snd_pcm_runtime *runtime) +{ + unsigned int size; + + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); + if ((size = rme96->playback_periodsize) != 0 || + (size = rme96->capture_periodsize) != 0) + snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + size, size); + else + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + &hw_constraints_period_bytes); +} + static int snd_rme96_playback_spdif_open(struct snd_pcm_substream *substream) { @@ -1180,8 +1199,7 @@ snd_rme96_playback_spdif_open(struct snd_pcm_substream *substream) runtime->hw.rate_min = rate; runtime->hw.rate_max = rate; } - snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); - snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); + rme96_set_buffer_size_constraint(rme96, runtime); rme96->wcreg_spdif_stream = rme96->wcreg_spdif; rme96->spdif_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; @@ -1219,9 +1237,7 @@ snd_rme96_capture_spdif_open(struct snd_pcm_substream *substream) rme96->capture_substream = substream; spin_unlock_irq(&rme96->lock); - snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); - snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); - + rme96_set_buffer_size_constraint(rme96, runtime); return 0; } @@ -1254,8 +1270,7 @@ snd_rme96_playback_adat_open(struct snd_pcm_substream *substream) runtime->hw.rate_min = rate; runtime->hw.rate_max = rate; } - snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); - snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); + rme96_set_buffer_size_constraint(rme96, runtime); return 0; } @@ -1291,8 +1306,7 @@ snd_rme96_capture_adat_open(struct snd_pcm_substream *substream) rme96->capture_substream = substream; spin_unlock_irq(&rme96->lock); - snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); - snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); + rme96_set_buffer_size_constraint(rme96, runtime); return 0; } @@ -1569,17 +1583,17 @@ snd_rme96_create(struct rme96 *rme96) return err; rme96->port = pci_resource_start(rme96->pci, 0); + if ((rme96->iobase = ioremap_nocache(rme96->port, RME96_IO_SIZE)) == 0) { + snd_printk(KERN_ERR "unable to remap memory region 0x%lx-0x%lx\n", rme96->port, rme96->port + RME96_IO_SIZE - 1); + return -ENOMEM; + } + if (request_irq(pci->irq, snd_rme96_interrupt, SA_INTERRUPT|SA_SHIRQ, "RME96", (void *)rme96)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); return -EBUSY; } rme96->irq = pci->irq; - if ((rme96->iobase = ioremap_nocache(rme96->port, RME96_IO_SIZE)) == 0) { - snd_printk(KERN_ERR "unable to remap memory region 0x%lx-0x%lx\n", rme96->port, rme96->port + RME96_IO_SIZE - 1); - return -ENOMEM; - } - /* read the card's revision number */ pci_read_config_byte(pci, 8, &rme96->rev); @@ -1805,7 +1819,7 @@ snd_rme96_proc_init(struct rme96 *rme96) struct snd_info_entry *entry; if (! snd_card_proc_new(rme96->card, "rme96", &entry)) - snd_info_set_text_ops(entry, rme96, 1024, snd_rme96_proc_read); + snd_info_set_text_ops(entry, rme96, snd_rme96_proc_read); } /* diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 61f82f0d5cc..eaf3c22449a 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -389,7 +389,7 @@ MODULE_SUPPORTED_DEVICE("{{RME Hammerfall-DSP}," /* use hotplug firmeare loader? */ #if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE) -#ifndef HDSP_USE_HWDEP_LOADER +#if !defined(HDSP_USE_HWDEP_LOADER) && !defined(CONFIG_SND_HDSP) #define HDSP_FW_LOADER #endif #endif @@ -3169,9 +3169,10 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) char *clock_source; int x; - if (hdsp_check_for_iobox (hdsp)) + if (hdsp_check_for_iobox (hdsp)) { snd_iprintf(buffer, "No I/O box connected.\nPlease connect one and upload firmware.\n"); return; + } if (hdsp_check_for_firmware(hdsp, 0)) { if (hdsp->state & HDSP_FirmwareCached) { @@ -3470,7 +3471,7 @@ static void __devinit snd_hdsp_proc_init(struct hdsp *hdsp) struct snd_info_entry *entry; if (! snd_card_proc_new(hdsp->card, "hdsp", &entry)) - snd_info_set_text_ops(entry, hdsp, 1024, snd_hdsp_proc_read); + snd_info_set_text_ops(entry, hdsp, snd_hdsp_proc_read); } static void snd_hdsp_free_buffers(struct hdsp *hdsp) diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 722b9e6ce54..bba1615504d 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -2489,7 +2489,7 @@ static void __devinit snd_hdspm_proc_init(struct hdspm * hdspm) struct snd_info_entry *entry; if (!snd_card_proc_new(hdspm->card, "hdspm", &entry)) - snd_info_set_text_ops(entry, hdspm, 1024, + snd_info_set_text_ops(entry, hdspm, snd_hdspm_proc_read); } diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c index 75d6406303d..3b945e8c1b1 100644 --- a/sound/pci/rme9652/rme9652.c +++ b/sound/pci/rme9652/rme9652.c @@ -41,7 +41,7 @@ static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ -static int precise_ptr[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = 0 }; /* Enable precise pointer */ +static int precise_ptr[SNDRV_CARDS]; /* Enable precise pointer */ module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for RME Digi9652 (Hammerfall) soundcard."); @@ -1787,7 +1787,7 @@ static void __devinit snd_rme9652_proc_init(struct snd_rme9652 *rme9652) struct snd_info_entry *entry; if (! snd_card_proc_new(rme9652->card, "rme9652", &entry)) - snd_info_set_text_ops(entry, rme9652, 1024, snd_rme9652_proc_read); + snd_info_set_text_ops(entry, rme9652, snd_rme9652_proc_read); } static void snd_rme9652_free_buffers(struct snd_rme9652 *rme9652) diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c index 91f8bf3ae9f..e5511606af0 100644 --- a/sound/pci/sonicvibes.c +++ b/sound/pci/sonicvibes.c @@ -54,8 +54,8 @@ MODULE_SUPPORTED_DEVICE("{{S3,SonicVibes PCI}}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ -static int reverb[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; -static int mge[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +static int reverb[SNDRV_CARDS]; +static int mge[SNDRV_CARDS]; static unsigned int dmaio = 0x7a00; /* DDMA i/o address */ module_param_array(index, int, NULL, 0444); @@ -1144,7 +1144,7 @@ static void __devinit snd_sonicvibes_proc_init(struct sonicvibes * sonic) struct snd_info_entry *entry; if (! snd_card_proc_new(sonic->card, "sonicvibes", &entry)) - snd_info_set_text_ops(entry, sonic, 1024, snd_sonicvibes_proc_read); + snd_info_set_text_ops(entry, sonic, snd_sonicvibes_proc_read); } /* @@ -1441,10 +1441,10 @@ static int __devinit snd_sonic_probe(struct pci_dev *pci, strcpy(card->driver, "SonicVibes"); strcpy(card->shortname, "S3 SonicVibes"); - sprintf(card->longname, "%s rev %i at 0x%lx, irq %i", + sprintf(card->longname, "%s rev %i at 0x%llx, irq %i", card->shortname, sonic->revision, - pci_resource_start(pci, 1), + (unsigned long long)pci_resource_start(pci, 1), sonic->irq); if ((err = snd_sonicvibes_pcm(sonic, 0, NULL)) < 0) { @@ -1456,7 +1456,7 @@ static int __devinit snd_sonic_probe(struct pci_dev *pci, return err; } if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_SONICVIBES, - sonic->midi_port, 1, + sonic->midi_port, MPU401_INFO_INTEGRATED, sonic->irq, 0, &midi_uart)) < 0) { snd_card_free(card); diff --git a/sound/pci/trident/trident.c b/sound/pci/trident/trident.c index 9624a5f2b87..5629b7eba96 100644 --- a/sound/pci/trident/trident.c +++ b/sound/pci/trident/trident.c @@ -148,7 +148,8 @@ static int __devinit snd_trident_probe(struct pci_dev *pci, } if (trident->device != TRIDENT_DEVICE_ID_SI7018 && (err = snd_mpu401_uart_new(card, 0, MPU401_HW_TRID4DWAVE, - trident->midi_port, 1, + trident->midi_port, + MPU401_INFO_INTEGRATED, trident->irq, 0, &trident->rmidi)) < 0) { snd_card_free(card); return err; diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c index 52178b8ad49..d99ed723775 100644 --- a/sound/pci/trident/trident_main.c +++ b/sound/pci/trident/trident_main.c @@ -306,6 +306,8 @@ void snd_trident_start_voice(struct snd_trident * trident, unsigned int voice) outl(mask, TRID_REG(trident, reg)); } +EXPORT_SYMBOL(snd_trident_start_voice); + /*--------------------------------------------------------------------------- void snd_trident_stop_voice(struct snd_trident * trident, unsigned int voice) @@ -328,6 +330,8 @@ void snd_trident_stop_voice(struct snd_trident * trident, unsigned int voice) outl(mask, TRID_REG(trident, reg)); } +EXPORT_SYMBOL(snd_trident_stop_voice); + /*--------------------------------------------------------------------------- int snd_trident_allocate_pcm_channel(struct snd_trident *trident) @@ -502,6 +506,8 @@ void snd_trident_write_voice_regs(struct snd_trident * trident, #endif } +EXPORT_SYMBOL(snd_trident_write_voice_regs); + /*--------------------------------------------------------------------------- snd_trident_write_cso_reg @@ -3332,7 +3338,7 @@ static void __devinit snd_trident_proc_init(struct snd_trident * trident) if (trident->device == TRIDENT_DEVICE_ID_SI7018) s = "sis7018"; if (! snd_card_proc_new(trident->card, s, &entry)) - snd_info_set_text_ops(entry, trident, 1024, snd_trident_proc_read); + snd_info_set_text_ops(entry, trident, snd_trident_proc_read); } static int snd_trident_dev_free(struct snd_device *device) @@ -3884,6 +3890,8 @@ struct snd_trident_voice *snd_trident_alloc_voice(struct snd_trident * trident, return NULL; } +EXPORT_SYMBOL(snd_trident_alloc_voice); + void snd_trident_free_voice(struct snd_trident * trident, struct snd_trident_voice *voice) { unsigned long flags; @@ -3912,6 +3920,8 @@ void snd_trident_free_voice(struct snd_trident * trident, struct snd_trident_voi private_free(voice); } +EXPORT_SYMBOL(snd_trident_free_voice); + static void snd_trident_clear_voices(struct snd_trident * trident, unsigned short v_min, unsigned short v_max) { unsigned int i, val, mask[2] = { 0, 0 }; @@ -3993,13 +4003,3 @@ int snd_trident_resume(struct pci_dev *pci) return 0; } #endif /* CONFIG_PM */ - -EXPORT_SYMBOL(snd_trident_alloc_voice); -EXPORT_SYMBOL(snd_trident_free_voice); -EXPORT_SYMBOL(snd_trident_start_voice); -EXPORT_SYMBOL(snd_trident_stop_voice); -EXPORT_SYMBOL(snd_trident_write_voice_regs); -/* trident_memory.c symbols */ -EXPORT_SYMBOL(snd_trident_synth_alloc); -EXPORT_SYMBOL(snd_trident_synth_free); -EXPORT_SYMBOL(snd_trident_synth_copy_from_user); diff --git a/sound/pci/trident/trident_memory.c b/sound/pci/trident/trident_memory.c index 46c6982c9e8..aff3f874131 100644 --- a/sound/pci/trident/trident_memory.c +++ b/sound/pci/trident/trident_memory.c @@ -349,6 +349,7 @@ snd_trident_synth_alloc(struct snd_trident *hw, unsigned int size) return blk; } +EXPORT_SYMBOL(snd_trident_synth_alloc); /* * free a synth sample area @@ -365,6 +366,7 @@ snd_trident_synth_free(struct snd_trident *hw, struct snd_util_memblk *blk) return 0; } +EXPORT_SYMBOL(snd_trident_synth_free); /* * reset TLB entry and free kernel page @@ -486,3 +488,4 @@ int snd_trident_synth_copy_from_user(struct snd_trident *trident, return 0; } +EXPORT_SYMBOL(snd_trident_synth_copy_from_user); diff --git a/sound/pci/trident/trident_synth.c b/sound/pci/trident/trident_synth.c index cc7af8bc55a..9b7dee84743 100644 --- a/sound/pci/trident/trident_synth.c +++ b/sound/pci/trident/trident_synth.c @@ -914,7 +914,9 @@ static int snd_trident_synth_create_port(struct snd_trident * trident, int idx) &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_SYNTH | + SNDRV_SEQ_PORT_TYPE_HARDWARE | + SNDRV_SEQ_PORT_TYPE_SYNTHESIZER, 16, 0, name); if (p->chset->port < 0) { diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index 39daf62d2ba..2527bbd958c 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -1775,6 +1775,12 @@ static struct ac97_quirk ac97_quirks[] = { .name = "Targa Traveller 811", .type = AC97_TUNE_HP_ONLY, }, + { + .subvendor = 0x161f, + .subdevice = 0x2032, + .name = "m680x", + .type = AC97_TUNE_HP_ONLY, /* http://launchpad.net/bugs/38546 */ + }, { } /* terminator */ }; @@ -1973,7 +1979,7 @@ static int __devinit snd_via686_init_misc(struct via82xx *chip) pci_write_config_byte(chip->pci, VIA_PNP_CONTROL, legacy_cfg); if (chip->mpu_res) { if (snd_mpu401_uart_new(chip->card, 0, MPU401_HW_VIA686A, - mpu_port, 1, + mpu_port, MPU401_INFO_INTEGRATED, chip->irq, 0, &chip->rmidi) < 0) { printk(KERN_WARNING "unable to initialize MPU-401" " at 0x%lx, skipping\n", mpu_port); @@ -2015,7 +2021,7 @@ static void __devinit snd_via82xx_proc_init(struct via82xx *chip) struct snd_info_entry *entry; if (! snd_card_proc_new(chip->card, "via82xx", &entry)) - snd_info_set_text_ops(entry, chip, 1024, snd_via82xx_proc_read); + snd_info_set_text_ops(entry, chip, snd_via82xx_proc_read); } /* @@ -2365,7 +2371,7 @@ static int __devinit check_dxs_list(struct pci_dev *pci, int revision) { .subvendor = 0x1462, .subdevice = 0x0470, .action = VIA_DXS_SRC }, /* MSI KT880 Delta-FSR */ { .subvendor = 0x1462, .subdevice = 0x3800, .action = VIA_DXS_ENABLE }, /* MSI KT266 */ { .subvendor = 0x1462, .subdevice = 0x5901, .action = VIA_DXS_NO_VRA }, /* MSI KT6 Delta-SR */ - { .subvendor = 0x1462, .subdevice = 0x7023, .action = VIA_DXS_NO_VRA }, /* MSI K8T Neo2-FI */ + { .subvendor = 0x1462, .subdevice = 0x7023, .action = VIA_DXS_SRC }, /* MSI K8T Neo2-FI */ { .subvendor = 0x1462, .subdevice = 0x7120, .action = VIA_DXS_ENABLE }, /* MSI KT4V */ { .subvendor = 0x1462, .subdevice = 0x7142, .action = VIA_DXS_ENABLE }, /* MSI K8MM-V */ { .subvendor = 0x1462, .subdevice = 0xb012, .action = VIA_DXS_SRC }, /* P4M800/VIA8237R */ diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c index ef97e50cd6c..577a2b03759 100644 --- a/sound/pci/via82xx_modem.c +++ b/sound/pci/via82xx_modem.c @@ -929,7 +929,7 @@ static void __devinit snd_via82xx_proc_init(struct via82xx_modem *chip) struct snd_info_entry *entry; if (! snd_card_proc_new(chip->card, "via82xx", &entry)) - snd_info_set_text_ops(entry, chip, 1024, snd_via82xx_proc_read); + snd_info_set_text_ops(entry, chip, snd_via82xx_proc_read); } /* diff --git a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c index 65ebf5f1933..26aa775b7b6 100644 --- a/sound/pci/ymfpci/ymfpci.c +++ b/sound/pci/ymfpci/ymfpci.c @@ -308,7 +308,8 @@ static int __devinit snd_card_ymfpci_probe(struct pci_dev *pci, } if (chip->mpu_res) { if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_YMFPCI, - mpu_port[dev], 1, + mpu_port[dev], + MPU401_INFO_INTEGRATED, pci->irq, 0, &chip->rawmidi)) < 0) { printk(KERN_WARNING "ymfpci: cannot initialize MPU401 at 0x%lx, skipping...\n", mpu_port[dev]); legacy_ctrl &= ~YMFPCI_LEGACY_MIEN; /* disable MPU401 irq */ diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index 8ac5ab50b5c..f894752523b 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -1919,7 +1919,7 @@ static int __devinit snd_ymfpci_proc_init(struct snd_card *card, struct snd_ymfp struct snd_info_entry *entry; if (! snd_card_proc_new(card, "ymfpci", &entry)) - snd_info_set_text_ops(entry, chip, 1024, snd_ymfpci_proc_read); + snd_info_set_text_ops(entry, chip, snd_ymfpci_proc_read); return 0; } |