diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2008-04-24 08:41:44 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-04-24 08:41:44 -0700 |
commit | 38ccc197eb85cad594eb5863ba5a408655da0062 (patch) | |
tree | 0066a0160e5dd28280a8db2a5814af7111ae3e1b /sound/pci | |
parent | d02aacff4467806ee56f147ac8eff6911d95811a (diff) | |
parent | 3a841d519f91463361bbbe7addc24a0c1b2e9f99 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6: (250 commits)
[ALSA] ice1724 - Fix IRQ lock-up with MPU access
[ALSA] Define MPU401 registers in sound/mpu401_uart.h
[ALSA] pcsp: fix wording in DEBUG_PAGEALLOC warning
[ALSA] pcsp - Fix dependency in Kconfig
[ALSA] soc - ac97 - Clean up checkpatch warnings
[ALSA] soc - wm8750 - Clean up checkpatch warnings
[ALSA] soc - wm8731 - Clean up checkpatch warnings
[ALSA] soc - pxa2xx-pcm - Fix checkpatch warnings
[ALSA] soc - spitz - Fix checkpatch warnings
[ALSA] soc - poodle - Fix checkpatch warnings
[ALSA] soc - corgi - Fix checkpatch warnings
[ALSA] soc - s3c24xx-i2s - Add missing spaces
[ALSA] soc - s3c24xx-i2s - Fix tab/space breakage
[ALSA] soc - s3c24xx-i2s - Use linux/io.h
[ALSA] hda - Fix Thinkpad X300 digital mic
pcsp - Don't build pcspkr when snd-pcsp is enabled
[ALSA] hda - Fix model for Acer Aspire 5720z
[ALSA] soc - s3c24xx - Declare suspend and resume static
[ALSA] soc - s3c24xx - Improve diagnostic output
[ALSA] Fix possible races at free_irq in PCI drivers
...
Diffstat (limited to 'sound/pci')
87 files changed, 6134 insertions, 2105 deletions
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 812085d521f..581debf37dc 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -122,6 +122,21 @@ config SND_AU8830 To compile this driver as a module, choose M here: the module will be called snd-au8830. +config SND_AW2 + tristate "Emagic Audiowerk 2" + depends on SND + help + Say Y here to include support for Emagic Audiowerk 2 soundcards. + + Supported features: Analog and SPDIF output. Analog or SPDIF input. + Note: Switch between analog and digital input does not always work. + It can produce continuous noise. The workaround is to switch again + (and again) between digital and analog input until it works. + + To compile this driver as a module, choose M here: the module + will be called snd-aw2. + + config SND_AZT3328 tristate "Aztech AZF3328 / PCI168 (EXPERIMENTAL)" depends on SND && EXPERIMENTAL @@ -162,6 +177,7 @@ config SND_CA0106 depends on SND select SND_AC97_CODEC select SND_RAWMIDI + select SND_VMASTER help Say Y here to include support for the Sound Blaster Audigy LS and Live 24bit. @@ -517,6 +533,7 @@ config SND_HDA_INTEL tristate "Intel HD Audio" depends on SND select SND_PCM + select SND_VMASTER help Say Y here to include support for Intel "High Definition Audio" (Azalia) motherboard devices. @@ -680,6 +697,7 @@ config SND_ICE1724 depends on SND select SND_MPU401_UART select SND_AC97_CODEC + select SND_VMASTER help Say Y here to include support for soundcards based on ICE/VT1724/1720 (Envy24HT/PT) chips. @@ -896,12 +914,12 @@ config SND_VIA82XX_MODEM will be called snd-via82xx-modem. config SND_VIRTUOSO - tristate "Asus Virtuoso 200 (Xonar)" + tristate "Asus Virtuoso 100/200 (Xonar)" depends on SND select SND_OXYGEN_LIB help Say Y here to include support for sound cards based on the - Asus AV200 chip, i.e., Xonar D2 and Xonar D2X. + Asus AV100/AV200 chips, i.e., Xonar D2, DX and D2X. To compile this driver as a module, choose M here: the module will be called snd-virtuoso. diff --git a/sound/pci/Makefile b/sound/pci/Makefile index 2d42fd28f4e..85ef14bc805 100644 --- a/sound/pci/Makefile +++ b/sound/pci/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_SND) += \ ac97/ \ ali5451/ \ au88x0/ \ + aw2/ \ ca0106/ \ cs46xx/ \ cs5535audio/ \ diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index 50c637e55ff..39198e505b1 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -114,10 +114,9 @@ static int ac97_surround_jack_mode_put(struct snd_kcontrol *kcontrol, struct snd static int ac97_channel_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static const char *texts[] = { "2ch", "4ch", "6ch" }; - if (kcontrol->private_value) - return ac97_enum_text_info(kcontrol, uinfo, texts, 2); /* 4ch only */ - return ac97_enum_text_info(kcontrol, uinfo, texts, 3); + static const char *texts[] = { "2ch", "4ch", "6ch", "8ch" }; + return ac97_enum_text_info(kcontrol, uinfo, texts, + kcontrol->private_value); } static int ac97_channel_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -133,13 +132,8 @@ static int ac97_channel_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol); unsigned char mode = ucontrol->value.enumerated.item[0]; - if (kcontrol->private_value) { - if (mode >= 2) - return -EINVAL; - } else { - if (mode >= 3) - return -EINVAL; - } + if (mode >= kcontrol->private_value) + return -EINVAL; if (mode != ac97->channel_mode) { ac97->channel_mode = mode; @@ -158,6 +152,7 @@ static int ac97_channel_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e .get = ac97_surround_jack_mode_get, \ .put = ac97_surround_jack_mode_put, \ } +/* 6ch */ #define AC97_CHANNEL_MODE_CTL \ { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ @@ -165,7 +160,9 @@ static int ac97_channel_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e .info = ac97_channel_mode_info, \ .get = ac97_channel_mode_get, \ .put = ac97_channel_mode_put, \ + .private_value = 3, \ } +/* 4ch */ #define AC97_CHANNEL_MODE_4CH_CTL \ { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ @@ -173,7 +170,17 @@ static int ac97_channel_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e .info = ac97_channel_mode_info, \ .get = ac97_channel_mode_get, \ .put = ac97_channel_mode_put, \ - .private_value = 1, \ + .private_value = 2, \ + } +/* 8ch */ +#define AC97_CHANNEL_MODE_8CH_CTL \ + { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = "Channel Mode", \ + .info = ac97_channel_mode_info, \ + .get = ac97_channel_mode_get, \ + .put = ac97_channel_mode_put, \ + .private_value = 4, \ } static inline int is_surround_on(struct snd_ac97 *ac97) @@ -210,6 +217,10 @@ static inline int is_shared_micin(struct snd_ac97 *ac97) return !ac97->indep_surround && !is_clfe_on(ac97); } +static inline int alc850_is_aux_back_surround(struct snd_ac97 *ac97) +{ + return is_surround_on(ac97); +} /* The following snd_ac97_ymf753_... items added by David Shust (dshust@shustring.com) */ /* Modified for YMF743 by Keita Maehara <maehara@debian.org> */ @@ -2816,10 +2827,12 @@ static int patch_alc655(struct snd_ac97 * ac97) #define AC97_ALC850_JACK_SELECT 0x76 #define AC97_ALC850_MISC1 0x7a +#define AC97_ALC850_MULTICH 0x6a static void alc850_update_jacks(struct snd_ac97 *ac97) { int shared; + int aux_is_back_surround; /* shared Line-In / Surround Out */ shared = is_shared_surrout(ac97); @@ -2837,13 +2850,18 @@ static void alc850_update_jacks(struct snd_ac97 *ac97) /* MIC-IN = 1, CENTER-LFE = 5 */ snd_ac97_update_bits(ac97, AC97_ALC850_JACK_SELECT, 7 << 4, shared ? (5<<4) : (1<<4)); + + aux_is_back_surround = alc850_is_aux_back_surround(ac97); + /* Aux is Back Surround */ + snd_ac97_update_bits(ac97, AC97_ALC850_MULTICH, 1 << 10, + aux_is_back_surround ? (1<<10) : (0<<10)); } static const struct snd_kcontrol_new snd_ac97_controls_alc850[] = { AC97_PAGE_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1, 0, 0), AC97_SINGLE("Mic Front Input Switch", AC97_ALC850_JACK_SELECT, 15, 1, 1), AC97_SURROUND_JACK_MODE_CTL, - AC97_CHANNEL_MODE_CTL, + AC97_CHANNEL_MODE_8CH_CTL, }; static int patch_alc850_specific(struct snd_ac97 *ac97) @@ -2869,6 +2887,7 @@ static int patch_alc850(struct snd_ac97 *ac97) ac97->build_ops = &patch_alc850_ops; ac97->spec.dev_flags = 0; /* for IEC958 playback route - ALC655 compatible */ + ac97->flags |= AC97_HAS_8CH; /* assume only page 0 for writing cache */ snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, AC97_PAGE_VENDOR); @@ -2878,6 +2897,7 @@ static int patch_alc850(struct snd_ac97 *ac97) spdif-in monitor off, spdif-in PCM off center on mic off, surround on line-in off duplicate front off + NB default bit 10=0 = Aux is Capture, not Back Surround */ snd_ac97_write_cache(ac97, AC97_ALC650_MULTICH, 1<<15); /* SURR_OUT: on, Surr 1kOhm: on, Surr Amp: off, Front 1kOhm: off diff --git a/sound/pci/ac97/ac97_pcm.c b/sound/pci/ac97/ac97_pcm.c index 3674f35c4a7..48cbda9378c 100644 --- a/sound/pci/ac97/ac97_pcm.c +++ b/sound/pci/ac97/ac97_pcm.c @@ -574,7 +574,6 @@ int snd_ac97_pcm_open(struct ac97_pcm *pcm, unsigned int rate, r = rate > 48000; bus = pcm->bus; if (cfg == AC97_PCM_CFG_SPDIF) { - int err; for (cidx = 0; cidx < 4; cidx++) if (bus->codec[cidx] && (bus->codec[cidx]->ext_id & AC97_EI_SPDIF)) { err = set_spdif_rate(bus->codec[cidx], rate); diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c index a66d5150bb7..39ec55b57b1 100644 --- a/sound/pci/ad1889.c +++ b/sound/pci/ad1889.c @@ -264,10 +264,10 @@ snd_ad1889_ac97_ready(struct snd_ad1889 *chip) mdelay(1); if (!retry) { snd_printk(KERN_ERR PFX "[%s] Link is not ready.\n", - __FUNCTION__); + __func__); return -EIO; } - ad1889_debug("[%s] ready after %d ms\n", __FUNCTION__, 400 - retry); + ad1889_debug("[%s] ready after %d ms\n", __func__, 400 - retry); return 0; } @@ -854,8 +854,6 @@ snd_ad1889_free(struct snd_ad1889 *chip) spin_unlock_irq(&chip->lock); - synchronize_irq(chip->irq); - if (chip->irq >= 0) free_irq(chip->irq, chip); diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c index 6a905ed9cbd..1a0fd65ec28 100644 --- a/sound/pci/ali5451/ali5451.c +++ b/sound/pci/ali5451/ali5451.c @@ -1809,26 +1809,26 @@ static int snd_ali5451_spdif_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ali *codec = kcontrol->private_data; - unsigned int enable; + unsigned int spdif_enable; - enable = ucontrol->value.integer.value[0] ? 1 : 0; + spdif_enable = ucontrol->value.integer.value[0] ? 1 : 0; spin_lock_irq(&codec->reg_lock); switch (kcontrol->private_value) { case 0: - enable = (codec->spdif_mask & 0x02) ? 1 : 0; + spdif_enable = (codec->spdif_mask & 0x02) ? 1 : 0; break; case 1: - enable = ((codec->spdif_mask & 0x02) && + spdif_enable = ((codec->spdif_mask & 0x02) && (codec->spdif_mask & 0x04)) ? 1 : 0; break; case 2: - enable = (codec->spdif_mask & 0x01) ? 1 : 0; + spdif_enable = (codec->spdif_mask & 0x01) ? 1 : 0; break; default: break; } - ucontrol->value.integer.value[0] = enable; + ucontrol->value.integer.value[0] = spdif_enable; spin_unlock_irq(&codec->reg_lock); return 0; } @@ -1837,17 +1837,17 @@ static int snd_ali5451_spdif_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ali *codec = kcontrol->private_data; - unsigned int change = 0, enable = 0; + unsigned int change = 0, spdif_enable = 0; - enable = ucontrol->value.integer.value[0] ? 1 : 0; + spdif_enable = ucontrol->value.integer.value[0] ? 1 : 0; spin_lock_irq(&codec->reg_lock); switch (kcontrol->private_value) { case 0: change = (codec->spdif_mask & 0x02) ? 1 : 0; - change = change ^ enable; + change = change ^ spdif_enable; if (change) { - if (enable) { + if (spdif_enable) { codec->spdif_mask |= 0x02; snd_ali_enable_spdif_out(codec); } else { @@ -1859,9 +1859,9 @@ static int snd_ali5451_spdif_put(struct snd_kcontrol *kcontrol, break; case 1: change = (codec->spdif_mask & 0x04) ? 1 : 0; - change = change ^ enable; + change = change ^ spdif_enable; if (change && (codec->spdif_mask & 0x02)) { - if (enable) { + if (spdif_enable) { codec->spdif_mask |= 0x04; snd_ali_enable_spdif_chnout(codec); } else { @@ -1872,9 +1872,9 @@ static int snd_ali5451_spdif_put(struct snd_kcontrol *kcontrol, break; case 2: change = (codec->spdif_mask & 0x01) ? 1 : 0; - change = change ^ enable; + change = change ^ spdif_enable; if (change) { - if (enable) { + if (spdif_enable) { codec->spdif_mask |= 0x01; snd_ali_enable_spdif_in(codec); } else { @@ -2047,10 +2047,8 @@ static int snd_ali_free(struct snd_ali * codec) { if (codec->hw_initialized) snd_ali_disable_address_interrupt(codec); - if (codec->irq >= 0) { - synchronize_irq(codec->irq); + if (codec->irq >= 0) free_irq(codec->irq, codec); - } if (codec->port) pci_release_regions(codec->pci); pci_disable_device(codec->pci); diff --git a/sound/pci/als300.c b/sound/pci/als300.c index 0e990a73582..8df6824b51c 100644 --- a/sound/pci/als300.c +++ b/sound/pci/als300.c @@ -92,8 +92,8 @@ #if DEBUG_CALLS #define snd_als300_dbgcalls(format, args...) printk(format, ##args) -#define snd_als300_dbgcallenter() printk(KERN_ERR "--> %s\n", __FUNCTION__) -#define snd_als300_dbgcallleave() printk(KERN_ERR "<-- %s\n", __FUNCTION__) +#define snd_als300_dbgcallenter() printk(KERN_ERR "--> %s\n", __func__) +#define snd_als300_dbgcallleave() printk(KERN_ERR "<-- %s\n", __func__) #else #define snd_als300_dbgcalls(format, args...) #define snd_als300_dbgcallenter() diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index 4594186b83e..457228fb22a 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -1553,7 +1553,7 @@ static int snd_atiixp_free(struct atiixp *chip) if (chip->irq < 0) goto __hw_end; snd_atiixp_chip_stop(chip); - synchronize_irq(chip->irq); + __hw_end: if (chip->irq >= 0) free_irq(chip->irq, chip); diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c index a67a869180d..d457a32a793 100644 --- a/sound/pci/atiixp_modem.c +++ b/sound/pci/atiixp_modem.c @@ -1197,7 +1197,7 @@ static int snd_atiixp_free(struct atiixp_modem *chip) if (chip->irq < 0) goto __hw_end; snd_atiixp_chip_stop(chip); - synchronize_irq(chip->irq); + __hw_end: if (chip->irq >= 0) free_irq(chip->irq, chip); diff --git a/sound/pci/au88x0/au88x0.c b/sound/pci/au88x0/au88x0.c index 26819e2f576..68368e49007 100644 --- a/sound/pci/au88x0/au88x0.c +++ b/sound/pci/au88x0/au88x0.c @@ -126,7 +126,6 @@ static int snd_vortex_dev_free(struct snd_device *device) vortex_gameport_unregister(vortex); vortex_core_shutdown(vortex); // Take down PCI interface. - synchronize_irq(vortex->irq); free_irq(vortex->irq, vortex); iounmap(vortex->mmio); pci_release_regions(vortex->pci_dev); @@ -220,7 +219,6 @@ snd_vortex_create(struct snd_card *card, struct pci_dev *pci, vortex_t ** rchip) return 0; alloc_out: - synchronize_irq(chip->irq); free_irq(chip->irq, chip); irq_out: vortex_core_shutdown(chip); diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c index 526c6c5ecf7..f9a58b4a30e 100644 --- a/sound/pci/au88x0/au88x0_pcm.c +++ b/sound/pci/au88x0/au88x0_pcm.c @@ -498,14 +498,14 @@ static struct snd_kcontrol_new snd_vortex_mixer_spdif[] __devinitdata = { }; /* create a pcm device */ -static int __devinit snd_vortex_new_pcm(vortex_t * chip, int idx, int nr) +static int __devinit snd_vortex_new_pcm(vortex_t *chip, int idx, int nr) { struct snd_pcm *pcm; struct snd_kcontrol *kctl; int i; int err, nr_capt; - if ((chip == 0) || (idx < 0) || (idx >= VORTEX_PCM_LAST)) + if (!chip || idx < 0 || idx >= VORTEX_PCM_LAST) return -ENODEV; /* idx indicates which kind of PCM device. ADB, SPDIF, I2S and A3D share the @@ -514,9 +514,9 @@ static int __devinit snd_vortex_new_pcm(vortex_t * chip, int idx, int nr) nr_capt = nr; else nr_capt = 0; - if ((err = - snd_pcm_new(chip->card, vortex_pcm_prettyname[idx], idx, nr, - nr_capt, &pcm)) < 0) + err = snd_pcm_new(chip->card, vortex_pcm_prettyname[idx], idx, nr, + nr_capt, &pcm); + if (err < 0) return err; strcpy(pcm->name, vortex_pcm_name[idx]); chip->pcm[idx] = pcm; diff --git a/sound/pci/aw2/Makefile b/sound/pci/aw2/Makefile new file mode 100644 index 00000000000..842335d3b73 --- /dev/null +++ b/sound/pci/aw2/Makefile @@ -0,0 +1,3 @@ +snd-aw2-objs := aw2-alsa.o aw2-saa7146.o + +obj-$(CONFIG_SND_AW2) += snd-aw2.o diff --git a/sound/pci/aw2/aw2-alsa.c b/sound/pci/aw2/aw2-alsa.c new file mode 100644 index 00000000000..56f87cd33c1 --- /dev/null +++ b/sound/pci/aw2/aw2-alsa.c @@ -0,0 +1,794 @@ +/***************************************************************************** + * + * Copyright (C) 2008 Cedric Bregardis <cedric.bregardis@free.fr> and + * Jean-Christian Hassler <jhassler@free.fr> + * + * This file is part of the Audiowerk2 ALSA driver + * + * The Audiowerk2 ALSA 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; version 2. + * + * The Audiowerk2 ALSA 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 the Audiowerk2 ALSA driver; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + *****************************************************************************/ +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/dma-mapping.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <asm/io.h> +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/control.h> + +#include "saa7146.h" +#include "aw2-saa7146.h" + +MODULE_AUTHOR("Cedric Bregardis <cedric.bregardis@free.fr>, " + "Jean-Christian Hassler <jhassler@free.fr>"); +MODULE_DESCRIPTION("Emagic Audiowerk 2 sound driver"); +MODULE_LICENSE("GPL"); + +/********************************* + * DEFINES + ********************************/ +#define PCI_VENDOR_ID_SAA7146 0x1131 +#define PCI_DEVICE_ID_SAA7146 0x7146 + +#define CTL_ROUTE_ANALOG 0 +#define CTL_ROUTE_DIGITAL 1 + +/********************************* + * TYPEDEFS + ********************************/ + /* hardware definition */ +static struct snd_pcm_hardware snd_aw2_playback_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_44100, + .rate_min = 44100, + .rate_max = 44100, + .channels_min = 2, + .channels_max = 4, + .buffer_bytes_max = 32768, + .period_bytes_min = 4096, + .period_bytes_max = 32768, + .periods_min = 1, + .periods_max = 1024, +}; + +static struct snd_pcm_hardware snd_aw2_capture_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_44100, + .rate_min = 44100, + .rate_max = 44100, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 32768, + .period_bytes_min = 4096, + .period_bytes_max = 32768, + .periods_min = 1, + .periods_max = 1024, +}; + +struct aw2_pcm_device { + struct snd_pcm *pcm; + unsigned int stream_number; + struct aw2 *chip; +}; + +struct aw2 { + struct snd_aw2_saa7146 saa7146; + + struct pci_dev *pci; + int irq; + spinlock_t reg_lock; + struct mutex mtx; + + unsigned long iobase_phys; + void __iomem *iobase_virt; + + struct snd_card *card; + + struct aw2_pcm_device device_playback[NB_STREAM_PLAYBACK]; + struct aw2_pcm_device device_capture[NB_STREAM_CAPTURE]; +}; + +/********************************* + * FUNCTION DECLARATIONS + ********************************/ +static int __init alsa_card_aw2_init(void); +static void __exit alsa_card_aw2_exit(void); +static int snd_aw2_dev_free(struct snd_device *device); +static int __devinit snd_aw2_create(struct snd_card *card, + struct pci_dev *pci, struct aw2 **rchip); +static int __devinit snd_aw2_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id); +static void __devexit snd_aw2_remove(struct pci_dev *pci); +static int snd_aw2_pcm_playback_open(struct snd_pcm_substream *substream); +static int snd_aw2_pcm_playback_close(struct snd_pcm_substream *substream); +static int snd_aw2_pcm_capture_open(struct snd_pcm_substream *substream); +static int snd_aw2_pcm_capture_close(struct snd_pcm_substream *substream); +static int snd_aw2_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params); +static int snd_aw2_pcm_hw_free(struct snd_pcm_substream *substream); +static int snd_aw2_pcm_prepare_playback(struct snd_pcm_substream *substream); +static int snd_aw2_pcm_prepare_capture(struct snd_pcm_substream *substream); +static int snd_aw2_pcm_trigger_playback(struct snd_pcm_substream *substream, + int cmd); +static int snd_aw2_pcm_trigger_capture(struct snd_pcm_substream *substream, + int cmd); +static snd_pcm_uframes_t snd_aw2_pcm_pointer_playback(struct snd_pcm_substream + *substream); +static snd_pcm_uframes_t snd_aw2_pcm_pointer_capture(struct snd_pcm_substream + *substream); +static int __devinit snd_aw2_new_pcm(struct aw2 *chip); + +static int snd_aw2_control_switch_capture_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo); +static int snd_aw2_control_switch_capture_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value + *ucontrol); +static int snd_aw2_control_switch_capture_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value + *ucontrol); + +/********************************* + * VARIABLES + ********************************/ +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 Audiowerk2 soundcard."); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string for the Audiowerk2 soundcard."); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable Audiowerk2 soundcard."); + +static struct pci_device_id snd_aw2_ids[] = { + {PCI_VENDOR_ID_SAA7146, PCI_DEVICE_ID_SAA7146, PCI_ANY_ID, PCI_ANY_ID, + 0, 0, 0}, + {0} +}; + +MODULE_DEVICE_TABLE(pci, snd_aw2_ids); + +/* pci_driver definition */ +static struct pci_driver driver = { + .name = "Emagic Audiowerk 2", + .id_table = snd_aw2_ids, + .probe = snd_aw2_probe, + .remove = __devexit_p(snd_aw2_remove), +}; + +/* operators for playback PCM alsa interface */ +static struct snd_pcm_ops snd_aw2_playback_ops = { + .open = snd_aw2_pcm_playback_open, + .close = snd_aw2_pcm_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_aw2_pcm_hw_params, + .hw_free = snd_aw2_pcm_hw_free, + .prepare = snd_aw2_pcm_prepare_playback, + .trigger = snd_aw2_pcm_trigger_playback, + .pointer = snd_aw2_pcm_pointer_playback, +}; + +/* operators for capture PCM alsa interface */ +static struct snd_pcm_ops snd_aw2_capture_ops = { + .open = snd_aw2_pcm_capture_open, + .close = snd_aw2_pcm_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_aw2_pcm_hw_params, + .hw_free = snd_aw2_pcm_hw_free, + .prepare = snd_aw2_pcm_prepare_capture, + .trigger = snd_aw2_pcm_trigger_capture, + .pointer = snd_aw2_pcm_pointer_capture, +}; + +static struct snd_kcontrol_new aw2_control __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Capture Route", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .private_value = 0xffff, + .info = snd_aw2_control_switch_capture_info, + .get = snd_aw2_control_switch_capture_get, + .put = snd_aw2_control_switch_capture_put +}; + +/********************************* + * FUNCTION IMPLEMENTATIONS + ********************************/ + +/* initialization of the module */ +static int __init alsa_card_aw2_init(void) +{ + snd_printdd(KERN_DEBUG "aw2: Load aw2 module\n"); + return pci_register_driver(&driver); +} + +/* clean up the module */ +static void __exit alsa_card_aw2_exit(void) +{ + snd_printdd(KERN_DEBUG "aw2: Unload aw2 module\n"); + pci_unregister_driver(&driver); +} + +module_init(alsa_card_aw2_init); +module_exit(alsa_card_aw2_exit); + +/* component-destructor */ +static int snd_aw2_dev_free(struct snd_device *device) +{ + struct aw2 *chip = device->device_data; + + /* Free hardware */ + snd_aw2_saa7146_free(&chip->saa7146); + + /* release the irq */ + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + /* release the i/o ports & memory */ + if (chip->iobase_virt) + iounmap(chip->iobase_virt); + + pci_release_regions(chip->pci); + /* disable the PCI entry */ + pci_disable_device(chip->pci); + /* release the data */ + kfree(chip); + + return 0; +} + +/* chip-specific constructor */ +static int __devinit snd_aw2_create(struct snd_card *card, + struct pci_dev *pci, struct aw2 **rchip) +{ + struct aw2 *chip; + int err; + static struct snd_device_ops ops = { + .dev_free = snd_aw2_dev_free, + }; + + *rchip = NULL; + + /* initialize the PCI entry */ + err = pci_enable_device(pci); + if (err < 0) + return err; + pci_set_master(pci); + + /* check PCI availability (32bit DMA) */ + if ((pci_set_dma_mask(pci, DMA_32BIT_MASK) < 0) || + (pci_set_consistent_dma_mask(pci, DMA_32BIT_MASK) < 0)) { + printk(KERN_ERR "aw2: Impossible to set 32bit mask DMA\n"); + pci_disable_device(pci); + return -ENXIO; + } + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (chip == NULL) { + pci_disable_device(pci); + return -ENOMEM; + } + + /* initialize the stuff */ + chip->card = card; + chip->pci = pci; + chip->irq = -1; + + /* (1) PCI resource allocation */ + err = pci_request_regions(pci, "Audiowerk2"); + if (err < 0) { + pci_disable_device(pci); + kfree(chip); + return err; + } + chip->iobase_phys = pci_resource_start(pci, 0); + chip->iobase_virt = + ioremap_nocache(chip->iobase_phys, + pci_resource_len(pci, 0)); + + if (chip->iobase_virt == NULL) { + printk(KERN_ERR "aw2: unable to remap memory region"); + pci_release_regions(pci); + pci_disable_device(pci); + kfree(chip); + return -ENOMEM; + } + + + if (request_irq(pci->irq, snd_aw2_saa7146_interrupt, + IRQF_SHARED, "Audiowerk2", chip)) { + printk(KERN_ERR "aw2: Cannot grab irq %d\n", pci->irq); + + iounmap(chip->iobase_virt); + pci_release_regions(chip->pci); + pci_disable_device(chip->pci); + kfree(chip); + return -EBUSY; + } + chip->irq = pci->irq; + + /* (2) initialization of the chip hardware */ + snd_aw2_saa7146_setup(&chip->saa7146, chip->iobase_virt); + err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); + if (err < 0) { + free_irq(chip->irq, (void *)chip); + iounmap(chip->iobase_virt); + pci_release_regions(chip->pci); + pci_disable_device(chip->pci); + kfree(chip); + return err; + } + + snd_card_set_dev(card, &pci->dev); + *rchip = chip; + + printk(KERN_INFO + "Audiowerk 2 sound card (saa7146 chipset) detected and " + "managed\n"); + return 0; +} + +/* constructor */ +static int __devinit snd_aw2_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + struct snd_card *card; + struct aw2 *chip; + int err; + + /* (1) Continue if device is not enabled, else inc dev */ + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + /* (2) Create card instance */ + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + /* (3) Create main component */ + err = snd_aw2_create(card, pci, &chip); + if (err < 0) { + snd_card_free(card); + return err; + } + + /* initialize mutex */ + mutex_init(&chip->mtx); + /* init spinlock */ + spin_lock_init(&chip->reg_lock); + /* (4) Define driver ID and name string */ + strcpy(card->driver, "aw2"); + strcpy(card->shortname, "Audiowerk2"); + + sprintf(card->longname, "%s with SAA7146 irq %i", + card->shortname, chip->irq); + + /* (5) Create other components */ + snd_aw2_new_pcm(chip); + + /* (6) Register card instance */ + err = snd_card_register(card); + if (err < 0) { + snd_card_free(card); + return err; + } + + /* (7) Set PCI driver data */ + pci_set_drvdata(pci, card); + + dev++; + return 0; +} + +/* destructor */ +static void __devexit snd_aw2_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +/* open callback */ +static int snd_aw2_pcm_playback_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + snd_printdd(KERN_DEBUG "aw2: Playback_open \n"); + runtime->hw = snd_aw2_playback_hw; + return 0; +} + +/* close callback */ +static int snd_aw2_pcm_playback_close(struct snd_pcm_substream *substream) +{ + return 0; + +} + +static int snd_aw2_pcm_capture_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + snd_printdd(KERN_DEBUG "aw2: Capture_open \n"); + runtime->hw = snd_aw2_capture_hw; + return 0; +} + +/* close callback */ +static int snd_aw2_pcm_capture_close(struct snd_pcm_substream *substream) +{ + /* TODO: something to do ? */ + return 0; +} + + /* hw_params callback */ +static int snd_aw2_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); +} + +/* hw_free callback */ +static int snd_aw2_pcm_hw_free(struct snd_pcm_substream *substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +/* prepare callback for playback */ +static int snd_aw2_pcm_prepare_playback(struct snd_pcm_substream *substream) +{ + struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream); + struct aw2 *chip = pcm_device->chip; + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned long period_size, buffer_size; + + mutex_lock(&chip->mtx); + + period_size = snd_pcm_lib_period_bytes(substream); + buffer_size = snd_pcm_lib_buffer_bytes(substream); + + snd_aw2_saa7146_pcm_init_playback(&chip->saa7146, + pcm_device->stream_number, + runtime->dma_addr, period_size, + buffer_size); + + /* Define Interrupt callback */ + snd_aw2_saa7146_define_it_playback_callback(pcm_device->stream_number, + (snd_aw2_saa7146_it_cb) + snd_pcm_period_elapsed, + (void *)substream); + + mutex_unlock(&chip->mtx); + + return 0; +} + +/* prepare callback for capture */ +static int snd_aw2_pcm_prepare_capture(struct snd_pcm_substream *substream) +{ + struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream); + struct aw2 *chip = pcm_device->chip; + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned long period_size, buffer_size; + + mutex_lock(&chip->mtx); + + period_size = snd_pcm_lib_period_bytes(substream); + buffer_size = snd_pcm_lib_buffer_bytes(substream); + + snd_aw2_saa7146_pcm_init_capture(&chip->saa7146, + pcm_device->stream_number, + runtime->dma_addr, period_size, + buffer_size); + + /* Define Interrupt callback */ + snd_aw2_saa7146_define_it_capture_callback(pcm_device->stream_number, + (snd_aw2_saa7146_it_cb) + snd_pcm_period_elapsed, + (void *)substream); + + mutex_unlock(&chip->mtx); + + return 0; +} + +/* playback trigger callback */ +static int snd_aw2_pcm_trigger_playback(struct snd_pcm_substream *substream, + int cmd) +{ + int status = 0; + struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream); + struct aw2 *chip = pcm_device->chip; + spin_lock(&chip->reg_lock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + snd_aw2_saa7146_pcm_trigger_start_playback(&chip->saa7146, + pcm_device-> + stream_number); + break; + case SNDRV_PCM_TRIGGER_STOP: + snd_aw2_saa7146_pcm_trigger_stop_playback(&chip->saa7146, + pcm_device-> + stream_number); + break; + default: + status = -EINVAL; + } + spin_unlock(&chip->reg_lock); + return status; +} + +/* capture trigger callback */ +static int snd_aw2_pcm_trigger_capture(struct snd_pcm_substream *substream, + int cmd) +{ + int status = 0; + struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream); + struct aw2 *chip = pcm_device->chip; + spin_lock(&chip->reg_lock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + snd_aw2_saa7146_pcm_trigger_start_capture(&chip->saa7146, + pcm_device-> + stream_number); + break; + case SNDRV_PCM_TRIGGER_STOP: + snd_aw2_saa7146_pcm_trigger_stop_capture(&chip->saa7146, + pcm_device-> + stream_number); + break; + default: + status = -EINVAL; + } + spin_unlock(&chip->reg_lock); + return status; +} + +/* playback pointer callback */ +static snd_pcm_uframes_t snd_aw2_pcm_pointer_playback(struct snd_pcm_substream + *substream) +{ + struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream); + struct aw2 *chip = pcm_device->chip; + unsigned int current_ptr; + + /* get the current hardware pointer */ + struct snd_pcm_runtime *runtime = substream->runtime; + current_ptr = + snd_aw2_saa7146_get_hw_ptr_playback(&chip->saa7146, + pcm_device->stream_number, + runtime->dma_area, + runtime->buffer_size); + + return bytes_to_frames(substream->runtime, current_ptr); +} + +/* capture pointer callback */ +static snd_pcm_uframes_t snd_aw2_pcm_pointer_capture(struct snd_pcm_substream + *substream) +{ + struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream); + struct aw2 *chip = pcm_device->chip; + unsigned int current_ptr; + + /* get the current hardware pointer */ + struct snd_pcm_runtime *runtime = substream->runtime; + current_ptr = + snd_aw2_saa7146_get_hw_ptr_capture(&chip->saa7146, + pcm_device->stream_number, + runtime->dma_area, + runtime->buffer_size); + + return bytes_to_frames(substream->runtime, current_ptr); +} + +/* create a pcm device */ +static int __devinit snd_aw2_new_pcm(struct aw2 *chip) +{ + struct snd_pcm *pcm_playback_ana; + struct snd_pcm *pcm_playback_num; + struct snd_pcm *pcm_capture; + struct aw2_pcm_device *pcm_device; + int err = 0; + + /* Create new Alsa PCM device */ + + err = snd_pcm_new(chip->card, "Audiowerk2 analog playback", 0, 1, 0, + &pcm_playback_ana); + if (err < 0) { + printk(KERN_ERR "aw2: snd_pcm_new error (0x%X)\n", err); + return err; + } + + /* Creation ok */ + pcm_device = &chip->device_playback[NUM_STREAM_PLAYBACK_ANA]; + + /* Set PCM device name */ + strcpy(pcm_playback_ana->name, "Analog playback"); + /* Associate private data to PCM device */ + pcm_playback_ana->private_data = pcm_device; + /* set operators of PCM device */ + snd_pcm_set_ops(pcm_playback_ana, SNDRV_PCM_STREAM_PLAYBACK, + &snd_aw2_playback_ops); + /* store PCM device */ + pcm_device->pcm = pcm_playback_ana; + /* give base chip pointer to our internal pcm device + structure */ + pcm_device->chip = chip; + /* Give stream number to PCM device */ + pcm_device->stream_number = NUM_STREAM_PLAYBACK_ANA; + + /* pre-allocation of buffers */ + /* Preallocate continuous pages. */ + err = snd_pcm_lib_preallocate_pages_for_all(pcm_playback_ana, + SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data + (chip->pci), + 64 * 1024, 64 * 1024); + if (err) + printk(KERN_ERR "aw2: snd_pcm_lib_preallocate_pages_for_all " + "error (0x%X)\n", err); + + err = snd_pcm_new(chip->card, "Audiowerk2 digital playback", 1, 1, 0, + &pcm_playback_num); + + if (err < 0) { + printk(KERN_ERR "aw2: snd_pcm_new error (0x%X)\n", err); + return err; + } + /* Creation ok */ + pcm_device = &chip->device_playback[NUM_STREAM_PLAYBACK_DIG]; + + /* Set PCM device name */ + strcpy(pcm_playback_num->name, "Digital playback"); + /* Associate private data to PCM device */ + pcm_playback_num->private_data = pcm_device; + /* set operators of PCM device */ + snd_pcm_set_ops(pcm_playback_num, SNDRV_PCM_STREAM_PLAYBACK, + &snd_aw2_playback_ops); + /* store PCM device */ + pcm_device->pcm = pcm_playback_num; + /* give base chip pointer to our internal pcm device + structure */ + pcm_device->chip = chip; + /* Give stream number to PCM device */ + pcm_device->stream_number = NUM_STREAM_PLAYBACK_DIG; + + /* pre-allocation of buffers */ + /* Preallocate continuous pages. */ + err = snd_pcm_lib_preallocate_pages_for_all(pcm_playback_num, + SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data + (chip->pci), + 64 * 1024, 64 * 1024); + if (err) + printk(KERN_ERR + "aw2: snd_pcm_lib_preallocate_pages_for_all error " + "(0x%X)\n", err); + + + + err = snd_pcm_new(chip->card, "Audiowerk2 capture", 2, 0, 1, + &pcm_capture); + + if (err < 0) { + printk(KERN_ERR "aw2: snd_pcm_new error (0x%X)\n", err); + return err; + } + + /* Creation ok */ + pcm_device = &chip->device_capture[NUM_STREAM_CAPTURE_ANA]; + + /* Set PCM device name */ + strcpy(pcm_capture->name, "Capture"); + /* Associate private data to PCM device */ + pcm_capture->private_data = pcm_device; + /* set operators of PCM device */ + snd_pcm_set_ops(pcm_capture, SNDRV_PCM_STREAM_CAPTURE, + &snd_aw2_capture_ops); + /* store PCM device */ + pcm_device->pcm = pcm_capture; + /* give base chip pointer to our internal pcm device + structure */ + pcm_device->chip = chip; + /* Give stream number to PCM device */ + pcm_device->stream_number = NUM_STREAM_CAPTURE_ANA; + + /* pre-allocation of buffers */ + /* Preallocate continuous pages. */ + err = snd_pcm_lib_preallocate_pages_for_all(pcm_capture, + SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data + (chip->pci), + 64 * 1024, 64 * 1024); + if (err) + printk(KERN_ERR + "aw2: snd_pcm_lib_preallocate_pages_for_all error " + "(0x%X)\n", err); + + + /* Create control */ + err = snd_ctl_add(chip->card, snd_ctl_new1(&aw2_control, chip)); + if (err < 0) { + printk(KERN_ERR "aw2: snd_ctl_add error (0x%X)\n", err); + return err; + } + + return 0; +} + +static int snd_aw2_control_switch_capture_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *texts[2] = { + "Analog", "Digital" + }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) { + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + } + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_aw2_control_switch_capture_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value + *ucontrol) +{ + struct aw2 *chip = snd_kcontrol_chip(kcontrol); + if (snd_aw2_saa7146_is_using_digital_input(&chip->saa7146)) + ucontrol->value.enumerated.item[0] = CTL_ROUTE_DIGITAL; + else + ucontrol->value.enumerated.item[0] = CTL_ROUTE_ANALOG; + return 0; +} + +static int snd_aw2_control_switch_capture_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value + *ucontrol) +{ + struct aw2 *chip = snd_kcontrol_chip(kcontrol); + int changed = 0; + int is_disgital = + snd_aw2_saa7146_is_using_digital_input(&chip->saa7146); + + if (((ucontrol->value.integer.value[0] == CTL_ROUTE_DIGITAL) + && !is_disgital) + || ((ucontrol->value.integer.value[0] == CTL_ROUTE_ANALOG) + && is_disgital)) { + snd_aw2_saa7146_use_digital_input(&chip->saa7146, !is_disgital); + changed = 1; + } + return changed; +} diff --git a/sound/pci/aw2/aw2-saa7146.c b/sound/pci/aw2/aw2-saa7146.c new file mode 100644 index 00000000000..6a3891ab69d --- /dev/null +++ b/sound/pci/aw2/aw2-saa7146.c @@ -0,0 +1,465 @@ +/***************************************************************************** + * + * Copyright (C) 2008 Cedric Bregardis <cedric.bregardis@free.fr> and + * Jean-Christian Hassler <jhassler@free.fr> + * + * This file is part of the Audiowerk2 ALSA driver + * + * The Audiowerk2 ALSA 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; version 2. + * + * The Audiowerk2 ALSA 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 the Audiowerk2 ALSA driver; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + *****************************************************************************/ + +#define AW2_SAA7146_M + +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <asm/system.h> +#include <asm/io.h> +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> + +#include "saa7146.h" +#include "aw2-saa7146.h" + +#include "aw2-tsl.c" + +#define WRITEREG(value, addr) writel((value), chip->base_addr + (addr)) +#define READREG(addr) readl(chip->base_addr + (addr)) + +static struct snd_aw2_saa7146_cb_param + arr_substream_it_playback_cb[NB_STREAM_PLAYBACK]; +static struct snd_aw2_saa7146_cb_param + arr_substream_it_capture_cb[NB_STREAM_CAPTURE]; + +static int snd_aw2_saa7146_get_limit(int size); + +/* chip-specific destructor */ +int snd_aw2_saa7146_free(struct snd_aw2_saa7146 *chip) +{ + /* disable all irqs */ + WRITEREG(0, IER); + + /* reset saa7146 */ + WRITEREG((MRST_N << 16), MC1); + + /* Unset base addr */ + chip->base_addr = NULL; + + return 0; +} + +void snd_aw2_saa7146_setup(struct snd_aw2_saa7146 *chip, + void __iomem *pci_base_addr) +{ + /* set PCI burst/threshold + + Burst length definition + VALUE BURST LENGTH + 000 1 Dword + 001 2 Dwords + 010 4 Dwords + 011 8 Dwords + 100 16 Dwords + 101 32 Dwords + 110 64 Dwords + 111 128 Dwords + + Threshold definition + VALUE WRITE MODE READ MODE + 00 1 Dword of valid data 1 empty Dword + 01 4 Dwords of valid data 4 empty Dwords + 10 8 Dwords of valid data 8 empty Dwords + 11 16 Dwords of valid data 16 empty Dwords */ + + unsigned int acon2; + unsigned int acon1 = 0; + int i; + + /* Set base addr */ + chip->base_addr = pci_base_addr; + + /* disable all irqs */ + WRITEREG(0, IER); + + /* reset saa7146 */ + WRITEREG((MRST_N << 16), MC1); + + /* enable audio interface */ +#ifdef __BIG_ENDIAN + acon1 |= A1_SWAP; + acon1 |= A2_SWAP; +#endif + /* WS0_CTRL, WS0_SYNC: input TSL1, I2S */ + + /* At initialization WS1 and WS2 are disbaled (configured as input */ + acon1 |= 0 * WS1_CTRL; + acon1 |= 0 * WS2_CTRL; + + /* WS4 is not used. So it must not restart A2. + This is why it is configured as output (force to low) */ + acon1 |= 3 * WS4_CTRL; + + /* WS3_CTRL, WS3_SYNC: output TSL2, I2S */ + acon1 |= 2 * WS3_CTRL; + + /* A1 and A2 are active and asynchronous */ + acon1 |= 3 * AUDIO_MODE; + WRITEREG(acon1, ACON1); + + /* The following comes from original windows driver. + It is needed to have a correct behavior of input and output + simultenously, but I don't know why ! */ + WRITEREG(3 * (BurstA1_in) + 3 * (ThreshA1_in) + + 3 * (BurstA1_out) + 3 * (ThreshA1_out) + + 3 * (BurstA2_out) + 3 * (ThreshA2_out), PCI_BT_A); + + /* enable audio port pins */ + WRITEREG((EAP << 16) | EAP, MC1); + + /* enable I2C */ + WRITEREG((EI2C << 16) | EI2C, MC1); + /* enable interrupts */ + WRITEREG(A1_out | A2_out | A1_in | IIC_S | IIC_E, IER); + + /* audio configuration */ + acon2 = A2_CLKSRC | BCLK1_OEN; + WRITEREG(acon2, ACON2); + + /* By default use analog input */ + snd_aw2_saa7146_use_digital_input(chip, 0); + + /* TSL setup */ + for (i = 0; i < 8; ++i) { + WRITEREG(tsl1[i], TSL1 + (i * 4)); + WRITEREG(tsl2[i], TSL2 + (i * 4)); + } + +} + +void snd_aw2_saa7146_pcm_init_playback(struct snd_aw2_saa7146 *chip, + int stream_number, + unsigned long dma_addr, + unsigned long period_size, + unsigned long buffer_size) +{ + unsigned long dw_page, dw_limit; + + /* Configure DMA for substream + Configuration informations: ALSA has allocated continuous memory + pages. So we don't need to use MMU of saa7146. + */ + + /* No MMU -> nothing to do with PageA1, we only configure the limit of + PageAx_out register */ + /* Disable MMU */ + dw_page = (0L << 11); + + /* Configure Limit for DMA access. + The limit register defines an address limit, which generates + an interrupt if passed by the actual PCI address pointer. + '0001' means an interrupt will be generated if the lower + 6 bits (64 bytes) of the PCI address are zero. '0010' + defines a limit of 128 bytes, '0011' one of 256 bytes, and + so on up to 1 Mbyte defined by '1111'. This interrupt range + can be calculated as follows: + Range = 2^(5 + Limit) bytes. + */ + dw_limit = snd_aw2_saa7146_get_limit(period_size); + dw_page |= (dw_limit << 4); + + if (stream_number == 0) { + WRITEREG(dw_page, PageA2_out); + + /* Base address for DMA transfert. */ + /* This address has been reserved by ALSA. */ + /* This is a physical address */ + WRITEREG(dma_addr, BaseA2_out); + + /* Define upper limit for DMA access */ + WRITEREG(dma_addr + buffer_size, ProtA2_out); + + } else if (stream_number == 1) { + WRITEREG(dw_page, PageA1_out); + + /* Base address for DMA transfert. */ + /* This address has been reserved by ALSA. */ + /* This is a physical address */ + WRITEREG(dma_addr, BaseA1_out); + + /* Define upper limit for DMA access */ + WRITEREG(dma_addr + buffer_size, ProtA1_out); + } else { + printk(KERN_ERR + "aw2: snd_aw2_saa7146_pcm_init_playback: " + "Substream number is not 0 or 1 -> not managed\n"); + } +} + +void snd_aw2_saa7146_pcm_init_capture(struct snd_aw2_saa7146 *chip, + int stream_number, unsigned long dma_addr, + unsigned long period_size, + unsigned long buffer_size) +{ + unsigned long dw_page, dw_limit; + + /* Configure DMA for substream + Configuration informations: ALSA has allocated continuous memory + pages. So we don't need to use MMU of saa7146. + */ + + /* No MMU -> nothing to do with PageA1, we only configure the limit of + PageAx_out register */ + /* Disable MMU */ + dw_page = (0L << 11); + + /* Configure Limit for DMA access. + The limit register defines an address limit, which generates + an interrupt if passed by the actual PCI address pointer. + '0001' means an interrupt will be generated if the lower + 6 bits (64 bytes) of the PCI address are zero. '0010' + defines a limit of 128 bytes, '0011' one of 256 bytes, and + so on up to 1 Mbyte defined by '1111'. This interrupt range + can be calculated as follows: + Range = 2^(5 + Limit) bytes. + */ + dw_limit = snd_aw2_saa7146_get_limit(period_size); + dw_page |= (dw_limit << 4); + + if (stream_number == 0) { + WRITEREG(dw_page, PageA1_in); + + /* Base address for DMA transfert. */ + /* This address has been reserved by ALSA. */ + /* This is a physical address */ + WRITEREG(dma_addr, BaseA1_in); + + /* Define upper limit for DMA access */ + WRITEREG(dma_addr + buffer_size, ProtA1_in); + } else { + printk(KERN_ERR + "aw2: snd_aw2_saa7146_pcm_init_capture: " + "Substream number is not 0 -> not managed\n"); + } +} + +void snd_aw2_saa7146_define_it_playback_callback(unsigned int stream_number, + snd_aw2_saa7146_it_cb + p_it_callback, + void *p_callback_param) +{ + if (stream_number < NB_STREAM_PLAYBACK) { + arr_substream_it_playback_cb[stream_number].p_it_callback = + (snd_aw2_saa7146_it_cb) p_it_callback; + arr_substream_it_playback_cb[stream_number].p_callback_param = + (void *)p_callback_param; + } +} + +void snd_aw2_saa7146_define_it_capture_callback(unsigned int stream_number, + snd_aw2_saa7146_it_cb + p_it_callback, + void *p_callback_param) +{ + if (stream_number < NB_STREAM_CAPTURE) { + arr_substream_it_capture_cb[stream_number].p_it_callback = + (snd_aw2_saa7146_it_cb) p_it_callback; + arr_substream_it_capture_cb[stream_number].p_callback_param = + (void *)p_callback_param; + } +} + +void snd_aw2_saa7146_pcm_trigger_start_playback(struct snd_aw2_saa7146 *chip, + int stream_number) +{ + unsigned int acon1 = 0; + /* In aw8 driver, dma transfert is always active. It is + started and stopped in a larger "space" */ + acon1 = READREG(ACON1); + if (stream_number == 0) { + WRITEREG((TR_E_A2_OUT << 16) | TR_E_A2_OUT, MC1); + + /* WS2_CTRL, WS2_SYNC: output TSL2, I2S */ + acon1 |= 2 * WS2_CTRL; + WRITEREG(acon1, ACON1); + + } else if (stream_number == 1) { + WRITEREG((TR_E_A1_OUT << 16) | TR_E_A1_OUT, MC1); + + /* WS1_CTRL, WS1_SYNC: output TSL1, I2S */ + acon1 |= 1 * WS1_CTRL; + WRITEREG(acon1, ACON1); + } +} + +void snd_aw2_saa7146_pcm_trigger_stop_playback(struct snd_aw2_saa7146 *chip, + int stream_number) +{ + unsigned int acon1 = 0; + acon1 = READREG(ACON1); + if (stream_number == 0) { + /* WS2_CTRL, WS2_SYNC: output TSL2, I2S */ + acon1 &= ~(3 * WS2_CTRL); + WRITEREG(acon1, ACON1); + + WRITEREG((TR_E_A2_OUT << 16), MC1); + } else if (stream_number == 1) { + /* WS1_CTRL, WS1_SYNC: output TSL1, I2S */ + acon1 &= ~(3 * WS1_CTRL); + WRITEREG(acon1, ACON1); + + WRITEREG((TR_E_A1_OUT << 16), MC1); + } +} + +void snd_aw2_saa7146_pcm_trigger_start_capture(struct snd_aw2_saa7146 *chip, + int stream_number) +{ + /* In aw8 driver, dma transfert is always active. It is + started and stopped in a larger "space" */ + if (stream_number == 0) + WRITEREG((TR_E_A1_IN << 16) | TR_E_A1_IN, MC1); +} + +void snd_aw2_saa7146_pcm_trigger_stop_capture(struct snd_aw2_saa7146 *chip, + int stream_number) +{ + if (stream_number == 0) + WRITEREG((TR_E_A1_IN << 16), MC1); +} + +irqreturn_t snd_aw2_saa7146_interrupt(int irq, void *dev_id) +{ + unsigned int isr; + unsigned int iicsta; + struct snd_aw2_saa7146 *chip = dev_id; + + isr = READREG(ISR); + if (!isr) + return IRQ_NONE; + + WRITEREG(isr, ISR); + + if (isr & (IIC_S | IIC_E)) { + iicsta = READREG(IICSTA); + WRITEREG(0x100, IICSTA); + } + + if (isr & A1_out) { + if (arr_substream_it_playback_cb[1].p_it_callback != NULL) { + arr_substream_it_playback_cb[1]. + p_it_callback(arr_substream_it_playback_cb[1]. + p_callback_param); + } + } + if (isr & A2_out) { + if (arr_substream_it_playback_cb[0].p_it_callback != NULL) { + arr_substream_it_playback_cb[0]. + p_it_callback(arr_substream_it_playback_cb[0]. + p_callback_param); + } + + } + if (isr & A1_in) { + if (arr_substream_it_capture_cb[0].p_it_callback != NULL) { + arr_substream_it_capture_cb[0]. + p_it_callback(arr_substream_it_capture_cb[0]. + p_callback_param); + } + } + return IRQ_HANDLED; +} + +unsigned int snd_aw2_saa7146_get_hw_ptr_playback(struct snd_aw2_saa7146 *chip, + int stream_number, + unsigned char *start_addr, + unsigned int buffer_size) +{ + long pci_adp = 0; + size_t ptr = 0; + + if (stream_number == 0) { + pci_adp = READREG(PCI_ADP3); + ptr = pci_adp - (long)start_addr; + + if (ptr == buffer_size) + ptr = 0; + } + if (stream_number == 1) { + pci_adp = READREG(PCI_ADP1); + ptr = pci_adp - (size_t) start_addr; + + if (ptr == buffer_size) + ptr = 0; + } + return ptr; +} + +unsigned int snd_aw2_saa7146_get_hw_ptr_capture(struct snd_aw2_saa7146 *chip, + int stream_number, + unsigned char *start_addr, + unsigned int buffer_size) +{ + size_t pci_adp = 0; + size_t ptr = 0; + if (stream_number == 0) { + pci_adp = READREG(PCI_ADP2); + ptr = pci_adp - (size_t) start_addr; + + if (ptr == buffer_size) + ptr = 0; + } + return ptr; +} + +void snd_aw2_saa7146_use_digital_input(struct snd_aw2_saa7146 *chip, + int use_digital) +{ + /* FIXME: switch between analog and digital input does not always work. + It can produce a kind of white noise. It seams that received data + are inverted sometime (endian inversion). Why ? I don't know, maybe + a problem of synchronization... However for the time being I have + not found the problem. Workaround: switch again (and again) between + digital and analog input until it works. */ + if (use_digital) + WRITEREG(0x40, GPIO_CTRL); + else + WRITEREG(0x50, GPIO_CTRL); +} + +int snd_aw2_saa7146_is_using_digital_input(struct snd_aw2_saa7146 *chip) +{ + unsigned int reg_val = READREG(GPIO_CTRL); + if ((reg_val & 0xFF) == 0x40) + return 1; + else + return 0; +} + + +static int snd_aw2_saa7146_get_limit(int size) +{ + int limitsize = 32; + int limit = 0; + while (limitsize < size) { + limitsize *= 2; + limit++; + } + return limit; +} diff --git a/sound/pci/aw2/aw2-saa7146.h b/sound/pci/aw2/aw2-saa7146.h new file mode 100644 index 00000000000..5b35e358937 --- /dev/null +++ b/sound/pci/aw2/aw2-saa7146.h @@ -0,0 +1,105 @@ +/***************************************************************************** + * + * Copyright (C) 2008 Cedric Bregardis <cedric.bregardis@free.fr> and + * Jean-Christian Hassler <jhassler@free.fr> + * + * This file is part of the Audiowerk2 ALSA driver + * + * The Audiowerk2 ALSA 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; version 2. + * + * The Audiowerk2 ALSA 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 the Audiowerk2 ALSA driver; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + *****************************************************************************/ + +#ifndef AW2_SAA7146_H +#define AW2_SAA7146_H + +#define NB_STREAM_PLAYBACK 2 +#define NB_STREAM_CAPTURE 1 + +#define NUM_STREAM_PLAYBACK_ANA 0 +#define NUM_STREAM_PLAYBACK_DIG 1 + +#define NUM_STREAM_CAPTURE_ANA 0 + +typedef void (*snd_aw2_saa7146_it_cb) (void *); + +struct snd_aw2_saa7146_cb_param { + snd_aw2_saa7146_it_cb p_it_callback; + void *p_callback_param; +}; + +/* definition of the chip-specific record */ + +struct snd_aw2_saa7146 { + void __iomem *base_addr; +}; + +extern void snd_aw2_saa7146_setup(struct snd_aw2_saa7146 *chip, + void __iomem *pci_base_addr); +extern int snd_aw2_saa7146_free(struct snd_aw2_saa7146 *chip); + +extern void snd_aw2_saa7146_pcm_init_playback(struct snd_aw2_saa7146 *chip, + int stream_number, + unsigned long dma_addr, + unsigned long period_size, + unsigned long buffer_size); +extern void snd_aw2_saa7146_pcm_init_capture(struct snd_aw2_saa7146 *chip, + int stream_number, + unsigned long dma_addr, + unsigned long period_size, + unsigned long buffer_size); +extern void snd_aw2_saa7146_define_it_playback_callback(unsigned int + stream_number, + snd_aw2_saa7146_it_cb + p_it_callback, + void *p_callback_param); +extern void snd_aw2_saa7146_define_it_capture_callback(unsigned int + stream_number, + snd_aw2_saa7146_it_cb + p_it_callback, + void *p_callback_param); +extern void snd_aw2_saa7146_pcm_trigger_start_capture(struct snd_aw2_saa7146 + *chip, int stream_number); +extern void snd_aw2_saa7146_pcm_trigger_stop_capture(struct snd_aw2_saa7146 + *chip, int stream_number); + +extern void snd_aw2_saa7146_pcm_trigger_start_playback(struct snd_aw2_saa7146 + *chip, + int stream_number); +extern void snd_aw2_saa7146_pcm_trigger_stop_playback(struct snd_aw2_saa7146 + *chip, int stream_number); + +extern irqreturn_t snd_aw2_saa7146_interrupt(int irq, void *dev_id); +extern unsigned int snd_aw2_saa7146_get_hw_ptr_playback(struct snd_aw2_saa7146 + *chip, + int stream_number, + unsigned char + *start_addr, + unsigned int + buffer_size); +extern unsigned int snd_aw2_saa7146_get_hw_ptr_capture(struct snd_aw2_saa7146 + *chip, + int stream_number, + unsigned char + *start_addr, + unsigned int + buffer_size); + +extern void snd_aw2_saa7146_use_digital_input(struct snd_aw2_saa7146 *chip, + int use_digital); + +extern int snd_aw2_saa7146_is_using_digital_input(struct snd_aw2_saa7146 + *chip); + +#endif diff --git a/sound/pci/aw2/aw2-tsl.c b/sound/pci/aw2/aw2-tsl.c new file mode 100644 index 00000000000..459b0311ea3 --- /dev/null +++ b/sound/pci/aw2/aw2-tsl.c @@ -0,0 +1,110 @@ +/***************************************************************************** + * + * Copyright (C) 2008 Cedric Bregardis <cedric.bregardis@free.fr> and + * Jean-Christian Hassler <jhassler@free.fr> + * Copyright 1998 Emagic Soft- und Hardware GmbH + * Copyright 2002 Martijn Sipkema + * + * This file is part of the Audiowerk2 ALSA driver + * + * The Audiowerk2 ALSA 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; version 2. + * + * The Audiowerk2 ALSA 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 the Audiowerk2 ALSA driver; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + *****************************************************************************/ + +#define TSL_WS0 (1UL << 31) +#define TSL_WS1 (1UL << 30) +#define TSL_WS2 (1UL << 29) +#define TSL_WS3 (1UL << 28) +#define TSL_WS4 (1UL << 27) +#define TSL_DIS_A1 (1UL << 24) +#define TSL_SDW_A1 (1UL << 23) +#define TSL_SIB_A1 (1UL << 22) +#define TSL_SF_A1 (1UL << 21) +#define TSL_LF_A1 (1UL << 20) +#define TSL_BSEL_A1 (1UL << 17) +#define TSL_DOD_A1 (1UL << 15) +#define TSL_LOW_A1 (1UL << 14) +#define TSL_DIS_A2 (1UL << 11) +#define TSL_SDW_A2 (1UL << 10) +#define TSL_SIB_A2 (1UL << 9) +#define TSL_SF_A2 (1UL << 8) +#define TSL_LF_A2 (1UL << 7) +#define TSL_BSEL_A2 (1UL << 4) +#define TSL_DOD_A2 (1UL << 2) +#define TSL_LOW_A2 (1UL << 1) +#define TSL_EOS (1UL << 0) + + /* Audiowerk8 hardware setup: */ + /* WS0, SD4, TSL1 - Analog/ digital in */ + /* WS1, SD0, TSL1 - Analog out #1, digital out */ + /* WS2, SD2, TSL1 - Analog out #2 */ + /* WS3, SD1, TSL2 - Analog out #3 */ + /* WS4, SD3, TSL2 - Analog out #4 */ + + /* Audiowerk8 timing: */ + /* Timeslot: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | ... */ + + /* A1_INPUT: */ + /* SD4: <_ADC-L_>-------<_ADC-R_>-------< */ + /* WS0: _______________/---------------\_ */ + + /* A1_OUTPUT: */ + /* SD0: <_1-L___>-------<_1-R___>-------< */ + /* WS1: _______________/---------------\_ */ + /* SD2: >-------<_2-L___>-------<_2-R___> */ + /* WS2: -------\_______________/--------- */ + + /* A2_OUTPUT: */ + /* SD1: <_3-L___>-------<_3-R___>-------< */ + /* WS3: _______________/---------------\_ */ + /* SD3: >-------<_4-L___>-------<_4-R___> */ + /* WS4: -------\_______________/--------- */ + +static int tsl1[8] = { + 1 * TSL_SDW_A1 | 3 * TSL_BSEL_A1 | + 0 * TSL_DIS_A1 | 0 * TSL_DOD_A1 | TSL_LF_A1, + + 1 * TSL_SDW_A1 | 2 * TSL_BSEL_A1 | + 0 * TSL_DIS_A1 | 0 * TSL_DOD_A1, + + 0 * TSL_SDW_A1 | 3 * TSL_BSEL_A1 | + 0 * TSL_DIS_A1 | 0 * TSL_DOD_A1, + + 0 * TSL_SDW_A1 | 2 * TSL_BSEL_A1 | + 0 * TSL_DIS_A1 | 0 * TSL_DOD_A1, + + 1 * TSL_SDW_A1 | 1 * TSL_BSEL_A1 | + 0 * TSL_DIS_A1 | 0 * TSL_DOD_A1 | TSL_WS1 | TSL_WS0, + + 1 * TSL_SDW_A1 | 0 * TSL_BSEL_A1 | + 0 * TSL_DIS_A1 | 0 * TSL_DOD_A1 | TSL_WS1 | TSL_WS0, + + 0 * TSL_SDW_A1 | 1 * TSL_BSEL_A1 | + 0 * TSL_DIS_A1 | 0 * TSL_DOD_A1 | TSL_WS1 | TSL_WS0, + + 0 * TSL_SDW_A1 | 0 * TSL_BSEL_A1 | 0 * TSL_DIS_A1 | + 0 * TSL_DOD_A1 | TSL_WS1 | TSL_WS0 | TSL_SF_A1 | TSL_EOS, +}; + +static int tsl2[8] = { + 0 * TSL_SDW_A2 | 3 * TSL_BSEL_A2 | 2 * TSL_DOD_A2 | TSL_LF_A2, + 0 * TSL_SDW_A2 | 2 * TSL_BSEL_A2 | 2 * TSL_DOD_A2, + 0 * TSL_SDW_A2 | 3 * TSL_BSEL_A2 | 2 * TSL_DOD_A2, + 0 * TSL_SDW_A2 | 2 * TSL_BSEL_A2 | 2 * TSL_DOD_A2, + 0 * TSL_SDW_A2 | 1 * TSL_BSEL_A2 | 2 * TSL_DOD_A2 | TSL_WS2, + 0 * TSL_SDW_A2 | 0 * TSL_BSEL_A2 | 2 * TSL_DOD_A2 | TSL_WS2, + 0 * TSL_SDW_A2 | 1 * TSL_BSEL_A2 | 2 * TSL_DOD_A2 | TSL_WS2, + 0 * TSL_SDW_A2 | 0 * TSL_BSEL_A2 | 2 * TSL_DOD_A2 | TSL_WS2 | TSL_EOS +}; diff --git a/sound/pci/aw2/saa7146.h b/sound/pci/aw2/saa7146.h new file mode 100644 index 00000000000..ce0ab5f9ee9 --- /dev/null +++ b/sound/pci/aw2/saa7146.h @@ -0,0 +1,168 @@ +/***************************************************************************** + * + * Copyright (C) 2008 Cedric Bregardis <cedric.bregardis@free.fr> and + * Jean-Christian Hassler <jhassler@free.fr> + * + * This file is part of the Audiowerk2 ALSA driver + * + * The Audiowerk2 ALSA 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; version 2. + * + * The Audiowerk2 ALSA 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 the Audiowerk2 ALSA driver; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + *****************************************************************************/ + +/* SAA7146 registers */ +#define PCI_BT_A 0x4C +#define IICTFR 0x8C +#define IICSTA 0x90 +#define BaseA1_in 0x94 +#define ProtA1_in 0x98 +#define PageA1_in 0x9C +#define BaseA1_out 0xA0 +#define ProtA1_out 0xA4 +#define PageA1_out 0xA8 +#define BaseA2_in 0xAC +#define ProtA2_in 0xB0 +#define PageA2_in 0xB4 +#define BaseA2_out 0xB8 +#define ProtA2_out 0xBC +#define PageA2_out 0xC0 +#define IER 0xDC +#define GPIO_CTRL 0xE0 +#define ACON1 0xF4 +#define ACON2 0xF8 +#define MC1 0xFC +#define MC2 0x100 +#define ISR 0x10C +#define PSR 0x110 +#define SSR 0x114 +#define PCI_ADP1 0x12C +#define PCI_ADP2 0x130 +#define PCI_ADP3 0x134 +#define PCI_ADP4 0x138 +#define LEVEL_REP 0x140 +#define FB_BUFFER1 0x144 +#define FB_BUFFER2 0x148 +#define TSL1 0x180 +#define TSL2 0x1C0 + +#define ME (1UL << 11) +#define LIMIT (1UL << 4) +#define PV (1UL << 3) + +/* PSR/ISR/IER */ +#define PPEF (1UL << 31) +#define PABO (1UL << 30) +#define IIC_S (1UL << 17) +#define IIC_E (1UL << 16) +#define A2_in (1UL << 15) +#define A2_out (1UL << 14) +#define A1_in (1UL << 13) +#define A1_out (1UL << 12) +#define AFOU (1UL << 11) +#define PIN3 (1UL << 6) +#define PIN2 (1UL << 5) +#define PIN1 (1UL << 4) +#define PIN0 (1UL << 3) +#define ECS (1UL << 2) +#define EC3S (1UL << 1) +#define EC0S (1UL << 0) + +/* SSR */ +#define PRQ (1UL << 31) +#define PMA (1UL << 30) +#define IIC_EA (1UL << 21) +#define IIC_EW (1UL << 20) +#define IIC_ER (1UL << 19) +#define IIC_EL (1UL << 18) +#define IIC_EF (1UL << 17) +#define AF2_in (1UL << 10) +#define AF2_out (1UL << 9) +#define AF1_in (1UL << 8) +#define AF1_out (1UL << 7) +#define EC5S (1UL << 3) +#define EC4S (1UL << 2) +#define EC2S (1UL << 1) +#define EC1S (1UL << 0) + +/* PCI_BT_A */ +#define BurstA1_in (1UL << 26) +#define ThreshA1_in (1UL << 24) +#define BurstA1_out (1UL << 18) +#define ThreshA1_out (1UL << 16) +#define BurstA2_in (1UL << 10) +#define ThreshA2_in (1UL << 8) +#define BurstA2_out (1UL << 2) +#define ThreshA2_out (1UL << 0) + +/* MC1 */ +#define MRST_N (1UL << 15) +#define EAP (1UL << 9) +#define EI2C (1UL << 8) +#define TR_E_A2_OUT (1UL << 3) +#define TR_E_A2_IN (1UL << 2) +#define TR_E_A1_OUT (1UL << 1) +#define TR_E_A1_IN (1UL << 0) + +/* MC2 */ +#define UPLD_IIC (1UL << 0) + +/* ACON1 */ +#define AUDIO_MODE (1UL << 29) +#define MAXLEVEL (1UL << 22) +#define A1_SWAP (1UL << 21) +#define A2_SWAP (1UL << 20) +#define WS0_CTRL (1UL << 18) +#define WS0_SYNC (1UL << 16) +#define WS1_CTRL (1UL << 14) +#define WS1_SYNC (1UL << 12) +#define WS2_CTRL (1UL << 10) +#define WS2_SYNC (1UL << 8) +#define WS3_CTRL (1UL << 6) +#define WS3_SYNC (1UL << 4) +#define WS4_CTRL (1UL << 2) +#define WS4_SYNC (1UL << 0) + +/* ACON2 */ +#define A1_CLKSRC (1UL << 27) +#define A2_CLKSRC (1UL << 22) +#define INVERT_BCLK1 (1UL << 21) +#define INVERT_BCLK2 (1UL << 20) +#define BCLK1_OEN (1UL << 19) +#define BCLK2_OEN (1UL << 18) + +/* IICSTA */ +#define IICCC (1UL << 8) +#define ABORT (1UL << 7) +#define SPERR (1UL << 6) +#define APERR (1UL << 5) +#define DTERR (1UL << 4) +#define DRERR (1UL << 3) +#define AL (1UL << 2) +#define ERR (1UL << 1) +#define BUSY (1UL << 0) + +/* IICTFR */ +#define BYTE2 (1UL << 24) +#define BYTE1 (1UL << 16) +#define BYTE0 (1UL << 8) +#define ATRR2 (1UL << 6) +#define ATRR1 (1UL << 4) +#define ATRR0 (1UL << 2) +#define ERR (1UL << 1) +#define BUSY (1UL << 0) + +#define START 3 +#define CONT 2 +#define STOP 1 +#define NOP 0 diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index 4e71a55120a..5f63af6b88a 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -157,8 +157,8 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}"); #if DEBUG_CALLS #define snd_azf3328_dbgcalls(format, args...) printk(format, ##args) -#define snd_azf3328_dbgcallenter() printk(KERN_ERR "--> %s\n", __FUNCTION__) -#define snd_azf3328_dbgcallleave() printk(KERN_ERR "<-- %s\n", __FUNCTION__) +#define snd_azf3328_dbgcallenter() printk(KERN_ERR "--> %s\n", __func__) +#define snd_azf3328_dbgcallleave() printk(KERN_ERR "<-- %s\n", __func__) #else #define snd_azf3328_dbgcalls(format, args...) #define snd_azf3328_dbgcallenter() @@ -1514,7 +1514,8 @@ snd_azf3328_free(struct snd_azf3328 *chip) /* well, at least we know how to disable the timer IRQ */ snd_azf3328_codec_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x00); - synchronize_irq(chip->irq); + if (chip->irq >= 0) + synchronize_irq(chip->irq); __end_hw: snd_azf3328_free_joystick(chip); if (chip->irq >= 0) diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c index 176e0f0e805..ecbe79b67e4 100644 --- a/sound/pci/ca0106/ca0106_main.c +++ b/sound/pci/ca0106/ca0106_main.c @@ -435,22 +435,22 @@ int snd_ca0106_i2c_write(struct snd_ca0106 *emu, static void snd_ca0106_intr_enable(struct snd_ca0106 *emu, unsigned int intrenb) { unsigned long flags; - unsigned int enable; - + unsigned int intr_enable; + spin_lock_irqsave(&emu->emu_lock, flags); - enable = inl(emu->port + INTE) | intrenb; - outl(enable, emu->port + INTE); + intr_enable = inl(emu->port + INTE) | intrenb; + outl(intr_enable, emu->port + INTE); spin_unlock_irqrestore(&emu->emu_lock, flags); } static void snd_ca0106_intr_disable(struct snd_ca0106 *emu, unsigned int intrenb) { unsigned long flags; - unsigned int enable; - + unsigned int intr_enable; + spin_lock_irqsave(&emu->emu_lock, flags); - enable = inl(emu->port + INTE) & ~intrenb; - outl(enable, emu->port + INTE); + intr_enable = inl(emu->port + INTE) & ~intrenb; + outl(intr_enable, emu->port + INTE); spin_unlock_irqrestore(&emu->emu_lock, flags); } @@ -1114,6 +1114,8 @@ static int snd_ca0106_free(struct snd_ca0106 *chip) * So we can fix: snd-malloc: Memory leak? pages not freed = 8 */ } + if (chip->irq >= 0) + free_irq(chip->irq, chip); // release the data #if 1 if (chip->buffer.area) @@ -1123,9 +1125,6 @@ static int snd_ca0106_free(struct snd_ca0106 *chip) // release the i/o port release_and_free_resource(chip->res_port); - // release the irq - if (chip->irq >= 0) - free_irq(chip->irq, chip); pci_disable_device(chip->pci); kfree(chip); return 0; diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c index af736869d9b..3025ed1b6e1 100644 --- a/sound/pci/ca0106/ca0106_mixer.c +++ b/sound/pci/ca0106/ca0106_mixer.c @@ -650,19 +650,55 @@ static int __devinit rename_ctl(struct snd_card *card, const char *src, const ch #define ADD_CTLS(emu, ctls) \ do { \ - int i, err; \ + int i, _err; \ for (i = 0; i < ARRAY_SIZE(ctls); i++) { \ - err = snd_ctl_add(card, snd_ctl_new1(&ctls[i], emu)); \ - if (err < 0) \ - return err; \ + _err = snd_ctl_add(card, snd_ctl_new1(&ctls[i], emu)); \ + if (_err < 0) \ + return _err; \ } \ } while (0) +static __devinitdata +DECLARE_TLV_DB_SCALE(snd_ca0106_master_db_scale, -6375, 50, 1); + +static char *slave_vols[] __devinitdata = { + "Analog Front Playback Volume", + "Analog Rear Playback Volume", + "Analog Center/LFE Playback Volume", + "Analog Side Playback Volume", + "IEC958 Front Playback Volume", + "IEC958 Rear Playback Volume", + "IEC958 Center/LFE Playback Volume", + "IEC958 Unknown Playback Volume", + "CAPTURE feedback Playback Volume", + NULL +}; + +static char *slave_sws[] __devinitdata = { + "Analog Front Playback Switch", + "Analog Rear Playback Switch", + "Analog Center/LFE Playback Switch", + "Analog Side Playback Switch", + "IEC958 Playback Switch", + NULL +}; + +static void __devinit add_slaves(struct snd_card *card, + struct snd_kcontrol *master, char **list) +{ + for (; *list; list++) { + struct snd_kcontrol *slave = ctl_find(card, *list); + if (slave) + snd_ctl_add_slave(master, slave); + } +} + int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu) { int err; struct snd_card *card = emu->card; char **c; + struct snd_kcontrol *vmaster; static char *ca0106_remove_ctls[] = { "Master Mono Playback Switch", "Master Mono Playback Volume", @@ -719,6 +755,21 @@ int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu) } if (emu->details->spi_dac == 1) ADD_CTLS(emu, snd_ca0106_volume_spi_dac_ctls); + + /* Create virtual master controls */ + vmaster = snd_ctl_make_virtual_master("Master Playback Volume", + snd_ca0106_master_db_scale); + if (!vmaster) + return -ENOMEM; + add_slaves(card, vmaster, slave_vols); + + if (emu->details->spi_dac == 1) { + vmaster = snd_ctl_make_virtual_master("Master Playback Switch", + NULL); + if (!vmaster) + return -ENOMEM; + add_slaves(card, vmaster, slave_sws); + } return 0; } diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index 135f3086075..9971b5b7735 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -2744,12 +2744,13 @@ static int __devinit snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_devic } for (idx = 0; idx < CM_SAVED_MIXERS; idx++) { - struct snd_ctl_elem_id id; + struct snd_ctl_elem_id elem_id; struct snd_kcontrol *ctl; - memset(&id, 0, sizeof(id)); - id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(id.name, cm_saved_mixer[idx].name); - if ((ctl = snd_ctl_find_id(cm->card, &id)) != NULL) + memset(&elem_id, 0, sizeof(elem_id)); + elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(elem_id.name, cm_saved_mixer[idx].name); + ctl = snd_ctl_find_id(cm->card, &elem_id); + if (ctl) cm->mixer_res_ctl[idx] = ctl; } @@ -2932,8 +2933,6 @@ static int snd_cmipci_free(struct cmipci *cm) /* reset mixer */ snd_cmipci_mixer_write(cm, 0, 0); - synchronize_irq(cm->irq); - free_irq(cm->irq, cm); } diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index 87ddffcd9d8..e214e567dec 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -2772,6 +2772,9 @@ static int snd_cs46xx_free(struct snd_cs46xx *chip) if (chip->irq >= 0) free_irq(chip->irq, chip); + if (chip->active_ctrl) + chip->active_ctrl(chip, -chip->amplifier); + for (idx = 0; idx < 5; idx++) { struct snd_cs46xx_region *region = &chip->region.idx[idx]; if (region->remap_addr) @@ -2779,9 +2782,6 @@ static int snd_cs46xx_free(struct snd_cs46xx *chip) release_and_free_resource(region->resource); } - if (chip->active_ctrl) - chip->active_ctrl(chip, -chip->amplifier); - #ifdef CONFIG_SND_CS46XX_NEW_DSP if (chip->dsp_spos_instance) { cs46xx_dsp_spos_destroy(chip); diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index 90ec090792b..e16dc92e82f 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -1852,15 +1852,16 @@ static irqreturn_t snd_echo_interrupt(int irq, void *dev_id) static int snd_echo_free(struct echoaudio *chip) { DE_INIT(("Stop DSP...\n")); - if (chip->comm_page) { + 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, chip); + if (chip->comm_page) + snd_dma_free_pages(&chip->commpage_dma_buf); + if (chip->dsp_registers) iounmap(chip->dsp_registers); diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index 9a9b977d3cf..abde5b90188 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -1249,11 +1249,6 @@ static int snd_emu10k1_free(struct snd_emu10k1 *emu) if (emu->port) { /* avoid access to already used hardware */ snd_emu10k1_fx8010_tram_setup(emu, 0); snd_emu10k1_done(emu); - /* remove reserved page */ - if (emu->reserved_page) { - snd_emu10k1_synth_free(emu, (struct snd_util_memblk *)emu->reserved_page); - emu->reserved_page = NULL; - } snd_emu10k1_free_efx(emu); } if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1010) { @@ -1262,6 +1257,14 @@ static int snd_emu10k1_free(struct snd_emu10k1 *emu) } if (emu->emu1010.firmware_thread) kthread_stop(emu->emu1010.firmware_thread); + if (emu->irq >= 0) + free_irq(emu->irq, emu); + /* remove reserved page */ + if (emu->reserved_page) { + snd_emu10k1_synth_free(emu, + (struct snd_util_memblk *)emu->reserved_page); + emu->reserved_page = NULL; + } if (emu->memhdr) snd_util_memhdr_free(emu->memhdr); if (emu->silent_page.area) @@ -1273,8 +1276,6 @@ static int snd_emu10k1_free(struct snd_emu10k1 *emu) #ifdef CONFIG_PM free_pm_buffer(emu); #endif - if (emu->irq >= 0) - free_irq(emu->irq, emu); if (emu->port) pci_release_regions(emu->pci); if (emu->card_capabilities->ca0151_chip) /* P16V */ diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c index 5512abd98bd..491a4a50f86 100644 --- a/sound/pci/emu10k1/emu10k1x.c +++ b/sound/pci/emu10k1/emu10k1x.c @@ -327,22 +327,22 @@ static void snd_emu10k1x_ptr_write(struct emu10k1x *emu, static void snd_emu10k1x_intr_enable(struct emu10k1x *emu, unsigned int intrenb) { unsigned long flags; - unsigned int enable; - + unsigned int intr_enable; + spin_lock_irqsave(&emu->emu_lock, flags); - enable = inl(emu->port + INTE) | intrenb; - outl(enable, emu->port + INTE); + intr_enable = inl(emu->port + INTE) | intrenb; + outl(intr_enable, emu->port + INTE); spin_unlock_irqrestore(&emu->emu_lock, flags); } static void snd_emu10k1x_intr_disable(struct emu10k1x *emu, unsigned int intrenb) { unsigned long flags; - unsigned int enable; - + unsigned int intr_enable; + spin_lock_irqsave(&emu->emu_lock, flags); - enable = inl(emu->port + INTE) & ~intrenb; - outl(enable, emu->port + INTE); + intr_enable = inl(emu->port + INTE) & ~intrenb; + outl(intr_enable, emu->port + INTE); spin_unlock_irqrestore(&emu->emu_lock, flags); } @@ -754,13 +754,13 @@ static int snd_emu10k1x_free(struct emu10k1x *chip) // disable audio outl(HCFG_LOCKSOUNDCACHE, chip->port + HCFG); - // release the i/o port - release_and_free_resource(chip->res_port); - - // release the irq + /* release the irq */ if (chip->irq >= 0) free_irq(chip->irq, chip); + // release the i/o port + release_and_free_resource(chip->res_port); + // release the DMA if (chip->dma_buffer.area) { snd_dma_free_pages(&chip->dma_buffer); @@ -795,9 +795,9 @@ static irqreturn_t snd_emu10k1x_interrupt(int irq, void *dev_id) // capture interrupt if (status & (IPR_CAP_0_LOOP | IPR_CAP_0_HALF_LOOP)) { - struct emu10k1x_voice *pvoice = &chip->capture_voice; - if (pvoice->use) - snd_emu10k1x_pcm_interrupt(chip, pvoice); + struct emu10k1x_voice *cap_voice = &chip->capture_voice; + if (cap_voice->use) + snd_emu10k1x_pcm_interrupt(chip, cap_voice); else snd_emu10k1x_intr_disable(chip, INTE_CAP_0_LOOP | diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c index f3caa3f890c..216f9748aff 100644 --- a/sound/pci/emu10k1/emuproc.c +++ b/sound/pci/emu10k1/emuproc.c @@ -412,7 +412,7 @@ static void snd_emu_proc_emu1010_reg_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct snd_emu10k1 *emu = entry->private_data; - int value; + u32 value; unsigned long flags; int i; snd_iprintf(buffer, "EMU1010 Registers:\n\n"); diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index 72d85a5ae6a..fbf1124f7c7 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -1635,20 +1635,20 @@ static int __devinit snd_ensoniq_1371_mixer(struct ensoniq *ensoniq, if (has_spdif > 0 || (!has_spdif && es1371_quirk_lookup(ensoniq, es1371_spdif_present))) { struct snd_kcontrol *kctl; - int i, index = 0; + int i, is_spdif = 0; ensoniq->spdif_default = ensoniq->spdif_stream = SNDRV_PCM_DEFAULT_CON_SPDIF; outl(ensoniq->spdif_default, ES_REG(ensoniq, CHANNEL_STATUS)); if (ensoniq->u.es1371.ac97->ext_id & AC97_EI_SPDIF) - index++; + is_spdif++; for (i = 0; i < ARRAY_SIZE(snd_es1371_mixer_spdif); i++) { kctl = snd_ctl_new1(&snd_es1371_mixer_spdif[i], ensoniq); if (!kctl) return -ENOMEM; - kctl->id.index = index; + kctl->id.index = is_spdif; err = snd_ctl_add(card, kctl); if (err < 0) return err; @@ -1910,7 +1910,8 @@ static int snd_ensoniq_free(struct ensoniq *ensoniq) outl(0, ES_REG(ensoniq, CONTROL)); /* switch everything off */ outl(0, ES_REG(ensoniq, SERIAL)); /* clear serial interface */ #endif - synchronize_irq(ensoniq->irq); + if (ensoniq->irq >= 0) + synchronize_irq(ensoniq->irq); pci_set_power_state(ensoniq->pci, 3); __hw_end: #ifdef CHIP1370 diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index 1a314fa99c4..84fac1fbf10 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -1488,7 +1488,6 @@ static int es1938_suspend(struct pci_dev *pci, pm_message_t state) outb(0x00, SLIO_REG(chip, IRQCONTROL)); /* disable irqs */ if (chip->irq >= 0) { - synchronize_irq(chip->irq); free_irq(chip->irq, chip); chip->irq = -1; } @@ -1578,10 +1577,8 @@ static int snd_es1938_free(struct es1938 *chip) snd_es1938_free_gameport(chip); - if (chip->irq >= 0) { - synchronize_irq(chip->irq); + if (chip->irq >= 0) free_irq(chip->irq, chip); - } pci_release_regions(chip->pci); pci_disable_device(chip->pci); kfree(chip); diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index 7d911a18c08..1bf298d214b 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -1827,6 +1827,22 @@ snd_es1968_pcm(struct es1968 *chip, int device) return 0; } +/* + * suppress jitter on some maestros when playing stereo + */ +static void snd_es1968_suppress_jitter(struct es1968 *chip, struct esschan *es) +{ + unsigned int cp1; + unsigned int cp2; + unsigned int diff; + + cp1 = __apu_get_register(chip, 0, 5); + cp2 = __apu_get_register(chip, 1, 5); + diff = (cp1 > cp2 ? cp1 - cp2 : cp2 - cp1); + + if (diff > 1) + __maestro_write(chip, IDR0_DATA_PORT, cp1); +} /* * update pointer @@ -1948,8 +1964,11 @@ static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id) struct esschan *es; spin_lock(&chip->substream_lock); list_for_each_entry(es, &chip->substream_list, list) { - if (es->running) + if (es->running) { snd_es1968_update_pcm(chip, es); + if (es->fmt & ESS_FMT_STEREO) + snd_es1968_suppress_jitter(chip, es); + } } spin_unlock(&chip->substream_lock); if (chip->in_measurement) { @@ -1972,7 +1991,7 @@ snd_es1968_mixer(struct es1968 *chip) { struct snd_ac97_bus *pbus; struct snd_ac97_template ac97; - struct snd_ctl_elem_id id; + struct snd_ctl_elem_id elem_id; int err; static struct snd_ac97_bus_ops ops = { .write = snd_es1968_ac97_write, @@ -1989,14 +2008,14 @@ snd_es1968_mixer(struct es1968 *chip) return err; /* attach master switch / volumes for h/w volume control */ - memset(&id, 0, sizeof(id)); - id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(id.name, "Master Playback Switch"); - chip->master_switch = snd_ctl_find_id(chip->card, &id); - memset(&id, 0, sizeof(id)); - id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(id.name, "Master Playback Volume"); - chip->master_volume = snd_ctl_find_id(chip->card, &id); + memset(&elem_id, 0, sizeof(elem_id)); + elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(elem_id.name, "Master Playback Switch"); + chip->master_switch = snd_ctl_find_id(chip->card, &elem_id); + memset(&elem_id, 0, sizeof(elem_id)); + elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(elem_id.name, "Master Playback Volume"); + chip->master_volume = snd_ctl_find_id(chip->card, &elem_id); return 0; } @@ -2456,7 +2475,8 @@ static inline void snd_es1968_free_gameport(struct es1968 *chip) { } static int snd_es1968_free(struct es1968 *chip) { if (chip->io_port) { - synchronize_irq(chip->irq); + if (chip->irq >= 0) + synchronize_irq(chip->irq); outw(1, chip->io_port + 0x04); /* clear WP interrupts */ outw(0, chip->io_port + ESM_PORT_HOST_IRQ); /* disable IRQ */ } diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index 4c300e6149f..c129f9e2072 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -1285,7 +1285,6 @@ static int wait_for_codec(struct fm801 *chip, unsigned int codec_id, static int snd_fm801_chip_init(struct fm801 *chip, int resume) { - int id; unsigned short cmdw; if (chip->tea575x_tuner & 0x0010) @@ -1310,13 +1309,14 @@ static int snd_fm801_chip_init(struct fm801 *chip, int resume) } else { /* my card has the secondary codec */ /* at address #3, so the loop is inverted */ - for (id = 3; id > 0; id--) { - if (! wait_for_codec(chip, id, AC97_VENDOR_ID1, + int i; + for (i = 3; i > 0; i--) { + if (!wait_for_codec(chip, i, AC97_VENDOR_ID1, msecs_to_jiffies(50))) { cmdw = inw(FM801_REG(chip, AC97_DATA)); if (cmdw != 0xffff && cmdw != 0) { chip->secondary = 1; - chip->secondary_addr = id; + chip->secondary_addr = i; break; } } diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index 9e0d8a1268a..ab0c726d648 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -2,7 +2,7 @@ snd-hda-intel-y := hda_intel.o # since snd-hda-intel is the only driver using hda-codec, # merge it into a single module although it was originally # designed to be individual modules -snd-hda-intel-y += hda_codec.o vmaster.o +snd-hda-intel-y += hda_codec.o snd-hda-intel-$(CONFIG_PROC_FS) += hda_proc.o snd-hda-intel-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o snd-hda-intel-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 37c413923db..a6be6e3e871 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -31,6 +31,7 @@ #include <sound/initval.h> #include "hda_local.h" #include <sound/hda_hwdep.h> +#include "hda_patch.h" /* codec presets */ #ifdef CONFIG_SND_HDA_POWER_SAVE /* define this option here to hide as static */ @@ -51,21 +52,50 @@ struct hda_vendor_id { /* codec vendor labels */ static struct hda_vendor_id hda_vendor_ids[] = { - { 0x10ec, "Realtek" }, + { 0x1002, "ATI" }, { 0x1057, "Motorola" }, + { 0x1095, "Silicon Image" }, + { 0x10ec, "Realtek" }, { 0x1106, "VIA" }, { 0x111d, "IDT" }, + { 0x11c1, "LSI" }, { 0x11d4, "Analog Devices" }, { 0x13f6, "C-Media" }, { 0x14f1, "Conexant" }, + { 0x17e8, "Chrontel" }, + { 0x1854, "LG" }, { 0x434d, "C-Media" }, { 0x8384, "SigmaTel" }, {} /* terminator */ }; -/* codec presets */ -#include "hda_patch.h" - +static const struct hda_codec_preset *hda_preset_tables[] = { +#ifdef CONFIG_SND_HDA_CODEC_REALTEK + snd_hda_preset_realtek, +#endif +#ifdef CONFIG_SND_HDA_CODEC_CMEDIA + snd_hda_preset_cmedia, +#endif +#ifdef CONFIG_SND_HDA_CODEC_ANALOG + snd_hda_preset_analog, +#endif +#ifdef CONFIG_SND_HDA_CODEC_SIGMATEL + snd_hda_preset_sigmatel, +#endif +#ifdef CONFIG_SND_HDA_CODEC_SI3054 + snd_hda_preset_si3054, +#endif +#ifdef CONFIG_SND_HDA_CODEC_ATIHDMI + snd_hda_preset_atihdmi, +#endif +#ifdef CONFIG_SND_HDA_CODEC_CONEXANT + snd_hda_preset_conexant, +#endif +#ifdef CONFIG_SND_HDA_CODEC_VIA + snd_hda_preset_via, +#endif + NULL +}; #ifdef CONFIG_SND_HDA_POWER_SAVE static void hda_power_work(struct work_struct *work); @@ -690,6 +720,19 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, format); } +void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid) +{ + if (!nid) + return; + + snd_printdd("hda_codec_cleanup_stream: NID=0x%x\n", nid); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0); +#if 0 /* keep the format */ + msleep(1); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0); +#endif +} + /* * amp access functions */ @@ -1037,16 +1080,24 @@ void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir, } /* find a mixer control element with the given name */ -struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, - const char *name) +static struct snd_kcontrol * +_snd_hda_find_mixer_ctl(struct hda_codec *codec, + const char *name, int idx) { struct snd_ctl_elem_id id; memset(&id, 0, sizeof(id)); id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + id.index = idx; strcpy(id.name, name); return snd_ctl_find_id(codec->bus->card, &id); } +struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, + const char *name) +{ + return _snd_hda_find_mixer_ctl(codec, name, 0); +} + /* create a virtual master control and add slaves */ int snd_hda_add_vmaster(struct hda_codec *codec, char *name, unsigned int *tlv, const char **slaves) @@ -1481,6 +1532,8 @@ static struct snd_kcontrol_new dig_mixes[] = { { } /* end */ }; +#define SPDIF_MAX_IDX 4 /* 4 instances should be enough to probe */ + /** * snd_hda_create_spdif_out_ctls - create Output SPDIF-related controls * @codec: the HDA codec @@ -1496,9 +1549,20 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid) int err; struct snd_kcontrol *kctl; struct snd_kcontrol_new *dig_mix; + int idx; + for (idx = 0; idx < SPDIF_MAX_IDX; idx++) { + if (!_snd_hda_find_mixer_ctl(codec, "IEC958 Playback Switch", + idx)) + break; + } + if (idx >= SPDIF_MAX_IDX) { + printk(KERN_ERR "hda_codec: too many IEC958 outputs\n"); + return -EBUSY; + } for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) { kctl = snd_ctl_new1(dig_mix, codec); + kctl->id.index = idx; kctl->private_value = nid; err = snd_ctl_add(codec->bus->card, kctl); if (err < 0) @@ -1512,6 +1576,43 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid) } /* + * SPDIF sharing with analog output + */ +static int spdif_share_sw_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_multi_out *mout = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = mout->share_spdif; + return 0; +} + +static int spdif_share_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_multi_out *mout = snd_kcontrol_chip(kcontrol); + mout->share_spdif = !!ucontrol->value.integer.value[0]; + return 0; +} + +static struct snd_kcontrol_new spdif_share_sw = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "IEC958 Default PCM Playback Switch", + .info = snd_ctl_boolean_mono_info, + .get = spdif_share_sw_get, + .put = spdif_share_sw_put, +}; + +int snd_hda_create_spdif_share_sw(struct hda_codec *codec, + struct hda_multi_out *mout) +{ + if (!mout->dig_out_nid) + return 0; + /* ATTENTION: here mout is passed as private_data, instead of codec */ + return snd_ctl_add(codec->bus->card, + snd_ctl_new1(&spdif_share_sw, mout)); +} + +/* * SPDIF input */ @@ -1595,7 +1696,17 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid) int err; struct snd_kcontrol *kctl; struct snd_kcontrol_new *dig_mix; + int idx; + for (idx = 0; idx < SPDIF_MAX_IDX; idx++) { + if (!_snd_hda_find_mixer_ctl(codec, "IEC958 Capture Switch", + idx)) + break; + } + if (idx >= SPDIF_MAX_IDX) { + printk(KERN_ERR "hda_codec: too many IEC958 inputs\n"); + return -EBUSY; + } for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) { kctl = snd_ctl_new1(dig_mix, codec); kctl->private_value = nid; @@ -2106,7 +2217,7 @@ static int hda_pcm_default_cleanup(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { - snd_hda_codec_setup_stream(codec, hinfo->nid, 0, 0, 0); + snd_hda_codec_cleanup_stream(codec, hinfo->nid); return 0; } @@ -2491,7 +2602,7 @@ int snd_hda_multi_out_dig_open(struct hda_codec *codec, mutex_lock(&codec->spdif_mutex); if (mout->dig_out_used == HDA_DIG_ANALOG_DUP) /* already opened as analog dup; reset it once */ - snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 0, 0, 0); + snd_hda_codec_cleanup_stream(codec, mout->dig_out_nid); mout->dig_out_used = HDA_DIG_EXCLUSIVE; mutex_unlock(&codec->spdif_mutex); return 0; @@ -2526,9 +2637,36 @@ int snd_hda_multi_out_dig_close(struct hda_codec *codec, */ int snd_hda_multi_out_analog_open(struct hda_codec *codec, struct hda_multi_out *mout, - struct snd_pcm_substream *substream) -{ - substream->runtime->hw.channels_max = mout->max_channels; + struct snd_pcm_substream *substream, + struct hda_pcm_stream *hinfo) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + runtime->hw.channels_max = mout->max_channels; + if (mout->dig_out_nid) { + if (!mout->analog_rates) { + mout->analog_rates = hinfo->rates; + mout->analog_formats = hinfo->formats; + mout->analog_maxbps = hinfo->maxbps; + } else { + runtime->hw.rates = mout->analog_rates; + runtime->hw.formats = mout->analog_formats; + hinfo->maxbps = mout->analog_maxbps; + } + if (!mout->spdif_rates) { + snd_hda_query_supported_pcm(codec, mout->dig_out_nid, + &mout->spdif_rates, + &mout->spdif_formats, + &mout->spdif_maxbps); + } + mutex_lock(&codec->spdif_mutex); + if (mout->share_spdif) { + runtime->hw.rates &= mout->spdif_rates; + runtime->hw.formats &= mout->spdif_formats; + if (mout->spdif_maxbps < hinfo->maxbps) + hinfo->maxbps = mout->spdif_maxbps; + } + mutex_unlock(&codec->spdif_mutex); + } return snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, 2); } @@ -2548,7 +2686,8 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, int i; mutex_lock(&codec->spdif_mutex); - if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) { + if (mout->dig_out_nid && mout->share_spdif && + mout->dig_out_used != HDA_DIG_EXCLUSIVE) { if (chs == 2 && snd_hda_is_supported_format(codec, mout->dig_out_nid, format) && @@ -2558,8 +2697,7 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, stream_tag, format); } else { mout->dig_out_used = 0; - snd_hda_codec_setup_stream(codec, mout->dig_out_nid, - 0, 0, 0); + snd_hda_codec_cleanup_stream(codec, mout->dig_out_nid); } } mutex_unlock(&codec->spdif_mutex); @@ -2601,17 +2739,16 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, int i; for (i = 0; i < mout->num_dacs; i++) - snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0); + snd_hda_codec_cleanup_stream(codec, nids[i]); if (mout->hp_nid) - snd_hda_codec_setup_stream(codec, mout->hp_nid, 0, 0, 0); + snd_hda_codec_cleanup_stream(codec, mout->hp_nid); for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) if (mout->extra_out_nid[i]) - snd_hda_codec_setup_stream(codec, - mout->extra_out_nid[i], - 0, 0, 0); + snd_hda_codec_cleanup_stream(codec, + mout->extra_out_nid[i]); mutex_lock(&codec->spdif_mutex); if (mout->dig_out_nid && mout->dig_out_used == HDA_DIG_ANALOG_DUP) { - snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 0, 0, 0); + snd_hda_codec_cleanup_stream(codec, mout->dig_out_nid); mout->dig_out_used = 0; } mutex_unlock(&codec->spdif_mutex); @@ -2790,6 +2927,30 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, } } + /* FIX-UP: + * If no line-out is defined but multiple HPs are found, + * some of them might be the real line-outs. + */ + if (!cfg->line_outs && cfg->hp_outs > 1) { + int i = 0; + while (i < cfg->hp_outs) { + /* The real HPs should have the sequence 0x0f */ + if ((sequences_hp[i] & 0x0f) == 0x0f) { + i++; + continue; + } + /* Move it to the line-out table */ + cfg->line_out_pins[cfg->line_outs] = cfg->hp_pins[i]; + sequences_line_out[cfg->line_outs] = sequences_hp[i]; + cfg->line_outs++; + cfg->hp_outs--; + memmove(cfg->hp_pins + i, cfg->hp_pins + i + 1, + sizeof(cfg->hp_pins[0]) * (cfg->hp_outs - i)); + memmove(sequences_hp + i - 1, sequences_hp + i, + sizeof(sequences_hp[0]) * (cfg->hp_outs - i)); + } + } + /* sort by sequence */ sort_pins_by_sequence(cfg->line_out_pins, sequences_line_out, cfg->line_outs); diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index f14871151be..dcd390b2bba 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -590,11 +590,21 @@ struct hda_pcm_stream { struct hda_pcm_ops ops; }; +/* PCM types */ +enum { + HDA_PCM_TYPE_AUDIO, + HDA_PCM_TYPE_SPDIF, + HDA_PCM_TYPE_HDMI, + HDA_PCM_TYPE_MODEM, + HDA_PCM_NTYPES +}; + /* for PCM creation */ struct hda_pcm { char *name; struct hda_pcm_stream stream[2]; - unsigned int is_modem; /* modem codec? */ + unsigned int pcm_type; /* HDA_PCM_TYPE_XXX */ + int device; /* assigned device number */ }; /* codec information */ @@ -712,6 +722,7 @@ int snd_hda_build_pcms(struct hda_bus *bus); void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, u32 stream_tag, int channel_id, int format); +void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid); unsigned int snd_hda_calc_stream_format(unsigned int rate, unsigned int channels, unsigned int format, diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index f9de7c467c2..59e4389c94a 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1007,8 +1007,8 @@ static int generic_pcm2_cleanup(struct hda_pcm_stream *hinfo, { struct hda_gspec *spec = codec->spec; - snd_hda_codec_setup_stream(codec, hinfo->nid, 0, 0, 0); - snd_hda_codec_setup_stream(codec, spec->dac_node[1]->nid, 0, 0, 0); + snd_hda_codec_cleanup_stream(codec, hinfo->nid); + snd_hda_codec_cleanup_stream(codec, spec->dac_node[1]->nid); return 0; } diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 4be36c84b36..b3a618eb42c 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -39,6 +39,7 @@ #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/dma-mapping.h> #include <linux/moduleparam.h> #include <linux/init.h> #include <linux/slab.h> @@ -185,35 +186,28 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; /* max number of SDs */ /* ICH, ATI and VIA have 4 playback and 4 capture */ -#define ICH6_CAPTURE_INDEX 0 #define ICH6_NUM_CAPTURE 4 -#define ICH6_PLAYBACK_INDEX 4 #define ICH6_NUM_PLAYBACK 4 /* ULI has 6 playback and 5 capture */ -#define ULI_CAPTURE_INDEX 0 #define ULI_NUM_CAPTURE 5 -#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 /* max number of fragments - we may use more if allocating more pages for BDL */ -#define BDL_SIZE PAGE_ALIGN(8192) -#define AZX_MAX_FRAG (BDL_SIZE / (MAX_AZX_DEV * 16)) +#define BDL_SIZE 4096 +#define AZX_MAX_BDL_ENTRIES (BDL_SIZE / 16) +#define AZX_MAX_FRAG 32 /* max buffer size - no h/w limit, you can increase as you like */ #define AZX_MAX_BUF_SIZE (1024*1024*1024) /* max number of PCM devics per card */ -#define AZX_MAX_AUDIO_PCMS 6 -#define AZX_MAX_MODEM_PCMS 2 -#define AZX_MAX_PCMS (AZX_MAX_AUDIO_PCMS + AZX_MAX_MODEM_PCMS) +#define AZX_MAX_PCMS 8 /* RIRB int mask: overrun[2], response[0] */ #define RIRB_INT_RESPONSE 0x01 @@ -227,6 +221,9 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; /* SD_CTL bits */ #define SD_CTL_STREAM_RESET 0x01 /* stream reset bit */ #define SD_CTL_DMA_START 0x02 /* stream DMA start bit */ +#define SD_CTL_STRIPE (3 << 16) /* stripe control */ +#define SD_CTL_TRAFFIC_PRIO (1 << 18) /* traffic priority */ +#define SD_CTL_DIR (1 << 19) /* bi-directional stream */ #define SD_CTL_STREAM_TAG_MASK (0xf << 20) #define SD_CTL_STREAM_TAG_SHIFT 20 @@ -284,12 +281,10 @@ enum { */ struct azx_dev { - u32 *bdl; /* virtual address of the BDL */ - dma_addr_t bdl_addr; /* physical address of the BDL */ + struct snd_dma_buffer bdl; /* BDL buffer */ u32 *posbuf; /* position buffer pointer */ unsigned int bufsize; /* size of the play buffer in bytes */ - unsigned int fragsize; /* size of each period in bytes */ unsigned int frags; /* number for period in the play buffer */ unsigned int fifo_size; /* FIFO size */ @@ -350,7 +345,6 @@ struct azx { struct azx_dev *azx_dev; /* PCM */ - unsigned int pcm_devs; struct snd_pcm *pcm[AZX_MAX_PCMS]; /* HD codec */ @@ -361,8 +355,7 @@ struct azx { struct azx_rb corb; struct azx_rb rirb; - /* BDL, CORB/RIRB and position buffers */ - struct snd_dma_buffer bdl; + /* CORB/RIRB and position buffers */ struct snd_dma_buffer rb; struct snd_dma_buffer posbuf; @@ -546,8 +539,9 @@ static void azx_update_rirb(struct azx *chip) if (res_ex & ICH6_RIRB_EX_UNSOL_EV) snd_hda_queue_unsol_event(chip->bus, res, res_ex); else if (chip->rirb.cmds) { - chip->rirb.cmds--; chip->rirb.res = res; + smp_wmb(); + chip->rirb.cmds--; } } } @@ -566,8 +560,10 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec) azx_update_rirb(chip); spin_unlock_irq(&chip->reg_lock); } - if (!chip->rirb.cmds) + if (!chip->rirb.cmds) { + smp_rmb(); return chip->rirb.res; /* the last value */ + } if (time_after(jiffies, timeout)) break; if (codec->bus->needs_damn_long_delay) @@ -965,30 +961,57 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id) /* * set up BDL entries */ -static void azx_setup_periods(struct azx_dev *azx_dev) +static int azx_setup_periods(struct snd_pcm_substream *substream, + struct azx_dev *azx_dev) { - u32 *bdl = azx_dev->bdl; - dma_addr_t dma_addr = azx_dev->substream->runtime->dma_addr; - int idx; + struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream); + u32 *bdl; + int i, ofs, periods, period_bytes; /* reset BDL address */ azx_sd_writel(azx_dev, SD_BDLPL, 0); azx_sd_writel(azx_dev, SD_BDLPU, 0); + period_bytes = snd_pcm_lib_period_bytes(substream); + periods = azx_dev->bufsize / period_bytes; + /* program the initial BDL entries */ - for (idx = 0; idx < azx_dev->frags; idx++) { - unsigned int off = idx << 2; /* 4 dword step */ - dma_addr_t addr = dma_addr + idx * azx_dev->fragsize; - /* program the address field of the BDL entry */ - bdl[off] = cpu_to_le32((u32)addr); - bdl[off+1] = cpu_to_le32(upper_32bit(addr)); - - /* program the size field of the BDL entry */ - bdl[off+2] = cpu_to_le32(azx_dev->fragsize); - - /* program the IOC to enable interrupt when buffer completes */ - bdl[off+3] = cpu_to_le32(0x01); + bdl = (u32 *)azx_dev->bdl.area; + ofs = 0; + azx_dev->frags = 0; + for (i = 0; i < periods; i++) { + int size, rest; + if (i >= AZX_MAX_BDL_ENTRIES) { + snd_printk(KERN_ERR "Too many BDL entries: " + "buffer=%d, period=%d\n", + azx_dev->bufsize, period_bytes); + /* reset */ + azx_sd_writel(azx_dev, SD_BDLPL, 0); + azx_sd_writel(azx_dev, SD_BDLPU, 0); + return -EINVAL; + } + rest = period_bytes; + do { + dma_addr_t addr = snd_pcm_sgbuf_get_addr(sgbuf, ofs); + /* program the address field of the BDL entry */ + bdl[0] = cpu_to_le32((u32)addr); + bdl[1] = cpu_to_le32(upper_32bit(addr)); + /* program the size field of the BDL entry */ + size = PAGE_SIZE - (ofs % PAGE_SIZE); + if (rest < size) + size = rest; + bdl[2] = cpu_to_le32(size); + /* program the IOC to enable interrupt + * only when the whole fragment is processed + */ + rest -= size; + bdl[3] = rest ? 0 : cpu_to_le32(0x01); + bdl += 4; + azx_dev->frags++; + ofs += size; + } while (rest > 0); } + return 0; } /* @@ -1037,14 +1060,17 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev) /* program the BDL address */ /* lower BDL address */ - azx_sd_writel(azx_dev, SD_BDLPL, (u32)azx_dev->bdl_addr); + azx_sd_writel(azx_dev, SD_BDLPL, (u32)azx_dev->bdl.addr); /* upper BDL address */ - azx_sd_writel(azx_dev, SD_BDLPU, upper_32bit(azx_dev->bdl_addr)); + azx_sd_writel(azx_dev, SD_BDLPU, upper_32bit(azx_dev->bdl.addr)); /* enable the position buffer */ - if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE)) - azx_writel(chip, DPLBASE, - (u32)chip->posbuf.addr |ICH6_DPLBASE_ENABLE); + if (chip->position_fix == POS_FIX_POSBUF || + chip->position_fix == POS_FIX_AUTO) { + if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE)) + azx_writel(chip, DPLBASE, + (u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE); + } /* set the interrupt enable bits in the descriptor control register */ azx_sd_writel(azx_dev, SD_CTL, @@ -1157,7 +1183,8 @@ static struct snd_pcm_hardware azx_pcm_hw = { SNDRV_PCM_INFO_MMAP_VALID | /* No full-resume yet implemented */ /* SNDRV_PCM_INFO_RESUME |*/ - SNDRV_PCM_INFO_PAUSE), + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START), .formats = SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_48000, .rate_min = 48000, @@ -1219,6 +1246,7 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) spin_unlock_irqrestore(&chip->reg_lock, flags); runtime->private_data = azx_dev; + snd_pcm_set_sync(substream); mutex_unlock(&chip->open_mutex); return 0; } @@ -1275,8 +1303,6 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; azx_dev->bufsize = snd_pcm_lib_buffer_bytes(substream); - azx_dev->fragsize = snd_pcm_lib_period_bytes(substream); - azx_dev->frags = azx_dev->bufsize / azx_dev->fragsize; azx_dev->format_val = snd_hda_calc_stream_format(runtime->rate, runtime->channels, runtime->format, @@ -1288,10 +1314,10 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream) return -EINVAL; } - snd_printdd("azx_pcm_prepare: bufsize=0x%x, fragsize=0x%x, " - "format=0x%x\n", - azx_dev->bufsize, azx_dev->fragsize, azx_dev->format_val); - azx_setup_periods(azx_dev); + snd_printdd("azx_pcm_prepare: bufsize=0x%x, format=0x%x\n", + azx_dev->bufsize, azx_dev->format_val); + if (azx_setup_periods(substream, azx_dev) < 0) + return -EINVAL; azx_setup_controller(chip, azx_dev); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) azx_dev->fifo_size = azx_sd_readw(azx_dev, SD_FIFOSIZE) + 1; @@ -1305,37 +1331,94 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream) static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct azx_pcm *apcm = snd_pcm_substream_chip(substream); - struct azx_dev *azx_dev = get_azx_dev(substream); struct azx *chip = apcm->chip; - int err = 0; + struct azx_dev *azx_dev; + struct snd_pcm_substream *s; + int start, nsync = 0, sbits = 0; + int nwait, timeout; - spin_lock(&chip->reg_lock); switch (cmd) { case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_START: - azx_stream_start(chip, azx_dev); - azx_dev->running = 1; + start = 1; break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: - azx_stream_stop(chip, azx_dev); - azx_dev->running = 0; + start = 0; break; default: - err = -EINVAL; + return -EINVAL; + } + + snd_pcm_group_for_each_entry(s, substream) { + if (s->pcm->card != substream->pcm->card) + continue; + azx_dev = get_azx_dev(s); + sbits |= 1 << azx_dev->index; + nsync++; + snd_pcm_trigger_done(s, substream); + } + + spin_lock(&chip->reg_lock); + if (nsync > 1) { + /* first, set SYNC bits of corresponding streams */ + azx_writel(chip, SYNC, azx_readl(chip, SYNC) | sbits); + } + snd_pcm_group_for_each_entry(s, substream) { + if (s->pcm->card != substream->pcm->card) + continue; + azx_dev = get_azx_dev(s); + if (start) + azx_stream_start(chip, azx_dev); + else + azx_stream_stop(chip, azx_dev); + azx_dev->running = start; } spin_unlock(&chip->reg_lock); - if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH || - cmd == SNDRV_PCM_TRIGGER_SUSPEND || - cmd == SNDRV_PCM_TRIGGER_STOP) { - int timeout = 5000; - while ((azx_sd_readb(azx_dev, SD_CTL) & SD_CTL_DMA_START) && - --timeout) - ; + if (start) { + if (nsync == 1) + return 0; + /* wait until all FIFOs get ready */ + for (timeout = 5000; timeout; timeout--) { + nwait = 0; + snd_pcm_group_for_each_entry(s, substream) { + if (s->pcm->card != substream->pcm->card) + continue; + azx_dev = get_azx_dev(s); + if (!(azx_sd_readb(azx_dev, SD_STS) & + SD_STS_FIFO_READY)) + nwait++; + } + if (!nwait) + break; + cpu_relax(); + } + } else { + /* wait until all RUN bits are cleared */ + for (timeout = 5000; timeout; timeout--) { + nwait = 0; + snd_pcm_group_for_each_entry(s, substream) { + if (s->pcm->card != substream->pcm->card) + continue; + azx_dev = get_azx_dev(s); + if (azx_sd_readb(azx_dev, SD_CTL) & + SD_CTL_DMA_START) + nwait++; + } + if (!nwait) + break; + cpu_relax(); + } } - return err; + if (nsync > 1) { + spin_lock(&chip->reg_lock); + /* reset SYNC bits */ + azx_writel(chip, SYNC, azx_readl(chip, SYNC) & ~sbits); + spin_unlock(&chip->reg_lock); + } + return 0; } static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream) @@ -1378,6 +1461,7 @@ static struct snd_pcm_ops azx_pcm_ops = { .prepare = azx_pcm_prepare, .trigger = azx_pcm_trigger, .pointer = azx_pcm_pointer, + .page = snd_pcm_sgbuf_ops_page, }; static void azx_pcm_free(struct snd_pcm *pcm) @@ -1386,7 +1470,7 @@ static void azx_pcm_free(struct snd_pcm *pcm) } static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec, - struct hda_pcm *cpcm, int pcm_dev) + struct hda_pcm *cpcm) { int err; struct snd_pcm *pcm; @@ -1400,7 +1484,7 @@ static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec, snd_assert(cpcm->name, return -EINVAL); - err = snd_pcm_new(chip->card, cpcm->name, pcm_dev, + err = snd_pcm_new(chip->card, cpcm->name, cpcm->device, cpcm->stream[0].substreams, cpcm->stream[1].substreams, &pcm); @@ -1420,62 +1504,70 @@ static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec, snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &azx_pcm_ops); if (cpcm->stream[1].substreams) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &azx_pcm_ops); - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, snd_dma_pci_data(chip->pci), 1024 * 64, 1024 * 1024); - chip->pcm[pcm_dev] = pcm; - if (chip->pcm_devs < pcm_dev + 1) - chip->pcm_devs = pcm_dev + 1; - + chip->pcm[cpcm->device] = pcm; return 0; } static int __devinit azx_pcm_create(struct azx *chip) { + static const char *dev_name[HDA_PCM_NTYPES] = { + "Audio", "SPDIF", "HDMI", "Modem" + }; + /* starting device index for each PCM type */ + static int dev_idx[HDA_PCM_NTYPES] = { + [HDA_PCM_TYPE_AUDIO] = 0, + [HDA_PCM_TYPE_SPDIF] = 1, + [HDA_PCM_TYPE_HDMI] = 3, + [HDA_PCM_TYPE_MODEM] = 6 + }; + /* normal audio device indices; not linear to keep compatibility */ + static int audio_idx[4] = { 0, 2, 4, 5 }; struct hda_codec *codec; int c, err; - int pcm_dev; + int num_devs[HDA_PCM_NTYPES]; err = snd_hda_build_pcms(chip->bus); if (err < 0) return err; /* create audio PCMs */ - pcm_dev = 0; - list_for_each_entry(codec, &chip->bus->codec_list, list) { - for (c = 0; c < codec->num_pcms; c++) { - if (codec->pcm_info[c].is_modem) - continue; /* create later */ - if (pcm_dev >= AZX_MAX_AUDIO_PCMS) { - snd_printk(KERN_ERR SFX - "Too many audio PCMs\n"); - return -EINVAL; - } - err = create_codec_pcm(chip, codec, - &codec->pcm_info[c], pcm_dev); - if (err < 0) - return err; - pcm_dev++; - } - } - - /* create modem PCMs */ - pcm_dev = AZX_MAX_AUDIO_PCMS; + memset(num_devs, 0, sizeof(num_devs)); list_for_each_entry(codec, &chip->bus->codec_list, list) { for (c = 0; c < codec->num_pcms; c++) { - if (!codec->pcm_info[c].is_modem) - continue; /* already created */ - if (pcm_dev >= AZX_MAX_PCMS) { - snd_printk(KERN_ERR SFX - "Too many modem PCMs\n"); - return -EINVAL; + struct hda_pcm *cpcm = &codec->pcm_info[c]; + int type = cpcm->pcm_type; + switch (type) { + case HDA_PCM_TYPE_AUDIO: + if (num_devs[type] >= ARRAY_SIZE(audio_idx)) { + snd_printk(KERN_WARNING + "Too many audio devices\n"); + continue; + } + cpcm->device = audio_idx[num_devs[type]]; + break; + case HDA_PCM_TYPE_SPDIF: + case HDA_PCM_TYPE_HDMI: + case HDA_PCM_TYPE_MODEM: + if (num_devs[type]) { + snd_printk(KERN_WARNING + "%s already defined\n", + dev_name[type]); + continue; + } + cpcm->device = dev_idx[type]; + break; + default: + snd_printk(KERN_WARNING + "Invalid PCM type %d\n", type); + continue; } - err = create_codec_pcm(chip, codec, - &codec->pcm_info[c], pcm_dev); + num_devs[type]++; + err = create_codec_pcm(chip, codec, cpcm); if (err < 0) return err; - chip->pcm[pcm_dev]->dev_class = SNDRV_PCM_CLASS_MODEM; - pcm_dev++; } } return 0; @@ -1502,10 +1594,7 @@ static int __devinit azx_init_stream(struct azx *chip) * and initialize */ for (i = 0; i < chip->num_streams; i++) { - unsigned int off = sizeof(u32) * (i * AZX_MAX_FRAG * 4); struct azx_dev *azx_dev = &chip->azx_dev[i]; - azx_dev->bdl = (u32 *)(chip->bdl.area + off); - azx_dev->bdl_addr = chip->bdl.addr + off; azx_dev->posbuf = (u32 __iomem *)(chip->posbuf.area + i * 8); /* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */ azx_dev->sd_addr = chip->remap_addr + (0x20 * i + 0x80); @@ -1587,13 +1676,12 @@ static int azx_suspend(struct pci_dev *pci, pm_message_t state) int i; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - for (i = 0; i < chip->pcm_devs; i++) + for (i = 0; i < AZX_MAX_PCMS; i++) snd_pcm_suspend_all(chip->pcm[i]); if (chip->initialized) snd_hda_suspend(chip->bus, state); azx_stop_chip(chip); if (chip->irq >= 0) { - synchronize_irq(chip->irq); free_irq(chip->irq, chip); chip->irq = -1; } @@ -1641,24 +1729,26 @@ static int azx_resume(struct pci_dev *pci) */ static int azx_free(struct azx *chip) { + int i; + if (chip->initialized) { - int i; for (i = 0; i < chip->num_streams; i++) azx_stream_stop(chip, &chip->azx_dev[i]); azx_stop_chip(chip); } - if (chip->irq >= 0) { - synchronize_irq(chip->irq); + if (chip->irq >= 0) free_irq(chip->irq, (void*)chip); - } if (chip->msi) pci_disable_msi(chip->pci); if (chip->remap_addr) iounmap(chip->remap_addr); - if (chip->bdl.area) - snd_dma_free_pages(&chip->bdl); + if (chip->azx_dev) { + for (i = 0; i < chip->num_streams; i++) + if (chip->azx_dev[i].bdl.area) + snd_dma_free_pages(&chip->azx_dev[i].bdl); + } if (chip->rb.area) snd_dma_free_pages(&chip->rb); if (chip->posbuf.area) @@ -1682,6 +1772,7 @@ static int azx_dev_free(struct snd_device *device) static struct snd_pci_quirk position_fix_list[] __devinitdata = { SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_NONE), SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_NONE), + SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_NONE), {} }; @@ -1740,7 +1831,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, struct azx **rchip) { struct azx *chip; - int err; + int i, err; unsigned short gcap; static struct snd_device_ops ops = { .dev_free = azx_dev_free, @@ -1812,38 +1903,35 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, gcap = azx_readw(chip, GCAP); snd_printdd("chipset global capabilities = 0x%x\n", gcap); - if (gcap) { - /* read number of streams from GCAP register instead of using - * hardcoded value - */ - chip->playback_streams = (gcap & (0xF << 12)) >> 12; - chip->capture_streams = (gcap & (0xF << 8)) >> 8; - chip->playback_index_offset = chip->capture_streams; - chip->capture_index_offset = 0; - } else { + /* allow 64bit DMA address if supported by H/W */ + if ((gcap & 0x01) && !pci_set_dma_mask(pci, DMA_64BIT_MASK)) + pci_set_consistent_dma_mask(pci, DMA_64BIT_MASK); + + /* read number of streams from GCAP register instead of using + * hardcoded value + */ + chip->capture_streams = (gcap >> 8) & 0x0f; + chip->playback_streams = (gcap >> 12) & 0x0f; + if (!chip->playback_streams && !chip->capture_streams) { /* gcap didn't give any info, switching to old method */ switch (chip->driver_type) { case AZX_DRIVER_ULI: chip->playback_streams = ULI_NUM_PLAYBACK; chip->capture_streams = ULI_NUM_CAPTURE; - chip->playback_index_offset = ULI_PLAYBACK_INDEX; - chip->capture_index_offset = ULI_CAPTURE_INDEX; break; case AZX_DRIVER_ATIHDMI: chip->playback_streams = ATIHDMI_NUM_PLAYBACK; chip->capture_streams = ATIHDMI_NUM_CAPTURE; - chip->playback_index_offset = ATIHDMI_PLAYBACK_INDEX; - chip->capture_index_offset = ATIHDMI_CAPTURE_INDEX; break; default: chip->playback_streams = ICH6_NUM_PLAYBACK; chip->capture_streams = ICH6_NUM_CAPTURE; - chip->playback_index_offset = ICH6_PLAYBACK_INDEX; - chip->capture_index_offset = ICH6_CAPTURE_INDEX; break; } } + chip->capture_index_offset = 0; + chip->playback_index_offset = chip->capture_streams; chip->num_streams = chip->playback_streams + chip->capture_streams; chip->azx_dev = kcalloc(chip->num_streams, sizeof(*chip->azx_dev), GFP_KERNEL); @@ -1852,13 +1940,15 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, goto errout; } - /* allocate memory for the BDL for each stream */ - err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, - snd_dma_pci_data(chip->pci), - BDL_SIZE, &chip->bdl); - if (err < 0) { - snd_printk(KERN_ERR SFX "cannot allocate BDL\n"); - goto errout; + for (i = 0; i < chip->num_streams; i++) { + /* allocate memory for the BDL for each stream */ + err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), + BDL_SIZE, &chip->azx_dev[i].bdl); + if (err < 0) { + snd_printk(KERN_ERR SFX "cannot allocate BDL\n"); + goto errout; + } } /* allocate memory for the position buffer */ err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, @@ -1994,48 +2084,63 @@ static void __devexit azx_remove(struct pci_dev *pci) /* PCI IDs */ static struct pci_device_id azx_ids[] = { - { 0x8086, 0x2668, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH6 */ - { 0x8086, 0x27d8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH7 */ - { 0x8086, 0x269a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ESB2 */ - { 0x8086, 0x284b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH8 */ - { 0x8086, 0x293e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH9 */ - { 0x8086, 0x293f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH9 */ - { 0x8086, 0x3a3e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH10 */ - { 0x8086, 0x3a6e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH10 */ - { 0x8086, 0x811b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_SCH }, /* SCH*/ - { 0x1002, 0x437b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB450 */ - { 0x1002, 0x4383, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB600 */ - { 0x1002, 0x793b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS600 HDMI */ - { 0x1002, 0x7919, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS690 HDMI */ - { 0x1002, 0x960f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS780 HDMI */ - { 0x1002, 0xaa00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI R600 HDMI */ - { 0x1002, 0xaa08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV630 HDMI */ - { 0x1002, 0xaa10, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV610 HDMI */ - { 0x1002, 0xaa18, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV670 HDMI */ - { 0x1002, 0xaa20, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV635 HDMI */ - { 0x1002, 0xaa28, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV620 HDMI */ - { 0x1002, 0xaa30, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV770 HDMI */ - { 0x1106, 0x3288, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_VIA }, /* VIA VT8251/VT8237A */ - { 0x1039, 0x7502, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_SIS }, /* SIS966 */ - { 0x10b9, 0x5461, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ULI }, /* ULI M5461 */ - { 0x10de, 0x026c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP51 */ - { 0x10de, 0x0371, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP55 */ - { 0x10de, 0x03e4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP61 */ - { 0x10de, 0x03f0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP61 */ - { 0x10de, 0x044a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP65 */ - { 0x10de, 0x044b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP65 */ - { 0x10de, 0x055c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP67 */ - { 0x10de, 0x055d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP67 */ - { 0x10de, 0x07fc, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP73 */ - { 0x10de, 0x07fd, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP73 */ - { 0x10de, 0x0774, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */ - { 0x10de, 0x0775, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */ - { 0x10de, 0x0776, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */ - { 0x10de, 0x0777, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */ - { 0x10de, 0x0ac0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP79 */ - { 0x10de, 0x0ac1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP79 */ - { 0x10de, 0x0ac2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP79 */ - { 0x10de, 0x0ac3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP79 */ + /* ICH 6..10 */ + { PCI_DEVICE(0x8086, 0x2668), .driver_data = AZX_DRIVER_ICH }, + { PCI_DEVICE(0x8086, 0x27d8), .driver_data = AZX_DRIVER_ICH }, + { PCI_DEVICE(0x8086, 0x269a), .driver_data = AZX_DRIVER_ICH }, + { PCI_DEVICE(0x8086, 0x284b), .driver_data = AZX_DRIVER_ICH }, + { PCI_DEVICE(0x8086, 0x293e), .driver_data = AZX_DRIVER_ICH }, + { PCI_DEVICE(0x8086, 0x293f), .driver_data = AZX_DRIVER_ICH }, + { PCI_DEVICE(0x8086, 0x3a3e), .driver_data = AZX_DRIVER_ICH }, + { PCI_DEVICE(0x8086, 0x3a6e), .driver_data = AZX_DRIVER_ICH }, + /* SCH */ + { PCI_DEVICE(0x8086, 0x811b), .driver_data = AZX_DRIVER_SCH }, + /* ATI SB 450/600 */ + { PCI_DEVICE(0x1002, 0x437b), .driver_data = AZX_DRIVER_ATI }, + { PCI_DEVICE(0x1002, 0x4383), .driver_data = AZX_DRIVER_ATI }, + /* ATI HDMI */ + { PCI_DEVICE(0x1002, 0x793b), .driver_data = AZX_DRIVER_ATIHDMI }, + { PCI_DEVICE(0x1002, 0x7919), .driver_data = AZX_DRIVER_ATIHDMI }, + { PCI_DEVICE(0x1002, 0x960f), .driver_data = AZX_DRIVER_ATIHDMI }, + { PCI_DEVICE(0x1002, 0xaa00), .driver_data = AZX_DRIVER_ATIHDMI }, + { PCI_DEVICE(0x1002, 0xaa08), .driver_data = AZX_DRIVER_ATIHDMI }, + { PCI_DEVICE(0x1002, 0xaa10), .driver_data = AZX_DRIVER_ATIHDMI }, + { PCI_DEVICE(0x1002, 0xaa18), .driver_data = AZX_DRIVER_ATIHDMI }, + { PCI_DEVICE(0x1002, 0xaa20), .driver_data = AZX_DRIVER_ATIHDMI }, + { PCI_DEVICE(0x1002, 0xaa28), .driver_data = AZX_DRIVER_ATIHDMI }, + { PCI_DEVICE(0x1002, 0xaa30), .driver_data = AZX_DRIVER_ATIHDMI }, + { PCI_DEVICE(0x1002, 0xaa38), .driver_data = AZX_DRIVER_ATIHDMI }, + { PCI_DEVICE(0x1002, 0xaa40), .driver_data = AZX_DRIVER_ATIHDMI }, + { PCI_DEVICE(0x1002, 0xaa48), .driver_data = AZX_DRIVER_ATIHDMI }, + /* VIA VT8251/VT8237A */ + { PCI_DEVICE(0x1106, 0x3288), .driver_data = AZX_DRIVER_VIA }, + /* SIS966 */ + { PCI_DEVICE(0x1039, 0x7502), .driver_data = AZX_DRIVER_SIS }, + /* ULI M5461 */ + { PCI_DEVICE(0x10b9, 0x5461), .driver_data = AZX_DRIVER_ULI }, + /* NVIDIA MCP */ + { PCI_DEVICE(0x10de, 0x026c), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x0371), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x03e4), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x03f0), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x044a), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x044b), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x055c), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x055d), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x0774), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x0775), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x0776), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x0777), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x07fc), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x07fd), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x0ac0), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x0ac1), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x0ac2), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x0ac3), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x0bd4), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x0bd5), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x0bd6), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x0bd7), .driver_data = AZX_DRIVER_NVIDIA }, { 0, } }; MODULE_DEVICE_TABLE(pci, azx_ids); diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index ad0014ab71f..5c9e578f7f2 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -228,8 +228,18 @@ struct hda_multi_out { int max_channels; /* currently supported analog channels */ int dig_out_used; /* current usage of digital out (HDA_DIG_XXX) */ int no_share_stream; /* don't share a stream with multiple pins */ + int share_spdif; /* share SPDIF pin */ + /* PCM information for both analog and SPDIF DACs */ + unsigned int analog_rates; + unsigned int analog_maxbps; + u64 analog_formats; + unsigned int spdif_rates; + unsigned int spdif_maxbps; + u64 spdif_formats; }; +int snd_hda_create_spdif_share_sw(struct hda_codec *codec, + struct hda_multi_out *mout); int snd_hda_multi_out_dig_open(struct hda_codec *codec, struct hda_multi_out *mout); int snd_hda_multi_out_dig_close(struct hda_codec *codec, @@ -241,7 +251,8 @@ int snd_hda_multi_out_dig_prepare(struct hda_codec *codec, struct snd_pcm_substream *substream); int snd_hda_multi_out_analog_open(struct hda_codec *codec, struct hda_multi_out *mout, - struct snd_pcm_substream *substream); + struct snd_pcm_substream *substream, + struct hda_pcm_stream *hinfo); int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, struct hda_multi_out *mout, unsigned int stream_tag, @@ -407,11 +418,4 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec, hda_nid_t nid); #endif /* CONFIG_SND_HDA_POWER_SAVE */ -/* - * virtual master control - */ -struct snd_kcontrol *snd_ctl_make_virtual_master(char *name, - const unsigned int *tlv); -int snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave); - #endif /* __SOUND_HDA_LOCAL_H */ diff --git a/sound/pci/hda/hda_patch.h b/sound/pci/hda/hda_patch.h index f5c23bb16d7..2fdf2358dbc 100644 --- a/sound/pci/hda/hda_patch.h +++ b/sound/pci/hda/hda_patch.h @@ -18,31 +18,3 @@ extern struct hda_codec_preset snd_hda_preset_atihdmi[]; extern struct hda_codec_preset snd_hda_preset_conexant[]; /* VIA codecs */ extern struct hda_codec_preset snd_hda_preset_via[]; - -static const struct hda_codec_preset *hda_preset_tables[] = { -#ifdef CONFIG_SND_HDA_CODEC_REALTEK - snd_hda_preset_realtek, -#endif -#ifdef CONFIG_SND_HDA_CODEC_CMEDIA - snd_hda_preset_cmedia, -#endif -#ifdef CONFIG_SND_HDA_CODEC_ANALOG - snd_hda_preset_analog, -#endif -#ifdef CONFIG_SND_HDA_CODEC_SIGMATEL - snd_hda_preset_sigmatel, -#endif -#ifdef CONFIG_SND_HDA_CODEC_SI3054 - snd_hda_preset_si3054, -#endif -#ifdef CONFIG_SND_HDA_CODEC_ATIHDMI - snd_hda_preset_atihdmi, -#endif -#ifdef CONFIG_SND_HDA_CODEC_CONEXANT - snd_hda_preset_conexant, -#endif -#ifdef CONFIG_SND_HDA_CODEC_VIA - snd_hda_preset_via, -#endif - NULL -}; diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index c8649282c2c..e0a605adde4 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -28,6 +28,7 @@ #include <sound/core.h> #include "hda_codec.h" #include "hda_local.h" +#include "hda_patch.h" struct ad198x_spec { struct snd_kcontrol_new *mixers[5]; @@ -80,7 +81,6 @@ struct ad198x_spec { #endif /* for virtual master */ hda_nid_t vmaster_nid; - u32 vmaster_tlv[4]; const char **slave_vols; const char **slave_sws; }; @@ -171,6 +171,11 @@ static int ad198x_build_controls(struct hda_codec *codec) err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); if (err < 0) return err; + err = snd_hda_create_spdif_share_sw(codec, + &spec->multiout); + if (err < 0) + return err; + spec->multiout.share_spdif = 1; } if (spec->dig_in_nid) { err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); @@ -180,10 +185,11 @@ static int ad198x_build_controls(struct hda_codec *codec) /* if we have no master control, let's create it */ if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { + unsigned int vmaster_tlv[4]; snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, - HDA_OUTPUT, spec->vmaster_tlv); + HDA_OUTPUT, vmaster_tlv); err = snd_hda_add_vmaster(codec, "Master Playback Volume", - spec->vmaster_tlv, + vmaster_tlv, (spec->slave_vols ? spec->slave_vols : ad_slave_vols)); if (err < 0) @@ -217,7 +223,8 @@ static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct ad198x_spec *spec = codec->spec; - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream); + return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, + hinfo); } static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo, @@ -289,8 +296,7 @@ static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct ad198x_spec *spec = codec->spec; - snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], - 0, 0, 0); + snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); return 0; } @@ -359,6 +365,7 @@ static int ad198x_build_pcms(struct hda_codec *codec) info++; codec->num_pcms++; info->name = "AD198x Digital"; + info->pcm_type = HDA_PCM_TYPE_SPDIF; info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid; if (spec->dig_in_nid) { @@ -611,13 +618,19 @@ static struct hda_input_mux ad1986a_laptop_eapd_capture_source = { }, }; +static struct hda_input_mux ad1986a_automic_capture_source = { + .num_items = 2, + .items = { + { "Mic", 0x0 }, + { "Mix", 0x5 }, + }, +}; + static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = { HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol), HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw), HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT), @@ -641,6 +654,33 @@ static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = { { } /* end */ }; +/* re-connect the mic boost input according to the jack sensing */ +static void ad1986a_automic(struct hda_codec *codec) +{ + unsigned int present; + present = snd_hda_codec_read(codec, 0x1f, 0, AC_VERB_GET_PIN_SENSE, 0); + /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */ + snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL, + (present & AC_PINSENSE_PRESENCE) ? 0 : 2); +} + +#define AD1986A_MIC_EVENT 0x36 + +static void ad1986a_automic_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) != AD1986A_MIC_EVENT) + return; + ad1986a_automic(codec); +} + +static int ad1986a_automic_init(struct hda_codec *codec) +{ + ad198x_init(codec); + ad1986a_automic(codec); + return 0; +} + /* laptop-automute - 2ch only */ static void ad1986a_update_hp(struct hda_codec *codec) @@ -844,6 +884,15 @@ static struct hda_verb ad1986a_eapd_init_verbs[] = { {} }; +static struct hda_verb ad1986a_automic_verbs[] = { + {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + /*{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},*/ + {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0}, + {0x1f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_MIC_EVENT}, + {} +}; + /* Ultra initialization */ static struct hda_verb ad1986a_ultra_init[] = { /* eapd initialization */ @@ -986,14 +1035,17 @@ static int patch_ad1986a(struct hda_codec *codec) break; case AD1986A_LAPTOP_EAPD: spec->mixers[0] = ad1986a_laptop_eapd_mixers; - spec->num_init_verbs = 2; + spec->num_init_verbs = 3; spec->init_verbs[1] = ad1986a_eapd_init_verbs; + spec->init_verbs[2] = ad1986a_automic_verbs; spec->multiout.max_channels = 2; spec->multiout.num_dacs = 1; spec->multiout.dac_nids = ad1986a_laptop_dac_nids; if (!is_jack_available(codec, 0x25)) spec->multiout.dig_out_nid = 0; - spec->input_mux = &ad1986a_laptop_eapd_capture_source; + spec->input_mux = &ad1986a_automic_capture_source; + codec->patch_ops.unsol_event = ad1986a_automic_unsol_event; + codec->patch_ops.init = ad1986a_automic_init; break; case AD1986A_LAPTOP_AUTOMUTE: spec->mixers[0] = ad1986a_laptop_automute_mixers; @@ -1365,7 +1417,10 @@ static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol, if (! ad198x_eapd_put(kcontrol, ucontrol)) return 0; - + /* change speaker pin appropriately */ + snd_hda_codec_write(codec, 0x05, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + spec->cur_eapd ? PIN_OUT : 0); /* toggle HP mute appropriately */ snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0, HDA_AMP_MUTE, @@ -2087,6 +2142,10 @@ static struct snd_kcontrol_new ad1988_spdif_in_mixers[] = { { } /* end */ }; +static struct snd_kcontrol_new ad1989_spdif_out_mixers[] = { + HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT), + { } /* end */ +}; /* * initialization verbs @@ -2187,6 +2246,13 @@ static struct hda_verb ad1988_spdif_init_verbs[] = { { } }; +/* AD1989 has no ADC -> SPDIF route */ +static struct hda_verb ad1989_spdif_init_verbs[] = { + /* SPDIF out pin */ + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */ + { } +}; + /* * verbs for 3stack (+dig) */ @@ -2894,10 +2960,19 @@ static int patch_ad1988(struct hda_codec *codec) spec->mixers[spec->num_mixers++] = ad1988_capture_mixers; spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs; if (spec->multiout.dig_out_nid) { - spec->mixers[spec->num_mixers++] = ad1988_spdif_out_mixers; - spec->init_verbs[spec->num_init_verbs++] = ad1988_spdif_init_verbs; + if (codec->vendor_id >= 0x11d4989a) { + spec->mixers[spec->num_mixers++] = + ad1989_spdif_out_mixers; + spec->init_verbs[spec->num_init_verbs++] = + ad1989_spdif_init_verbs; + } else { + spec->mixers[spec->num_mixers++] = + ad1988_spdif_out_mixers; + spec->init_verbs[spec->num_init_verbs++] = + ad1988_spdif_init_verbs; + } } - if (spec->dig_in_nid) + if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a) spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers; codec->patch_ops = ad198x_patch_ops; @@ -3133,11 +3208,12 @@ static int patch_ad1884(struct hda_codec *codec) * Lenovo Thinkpad T61/X61 */ static struct hda_input_mux ad1984_thinkpad_capture_source = { - .num_items = 3, + .num_items = 4, .items = { { "Mic", 0x0 }, { "Internal Mic", 0x1 }, { "Mix", 0x3 }, + { "Docking-Station", 0x4 }, }, }; @@ -3268,8 +3344,7 @@ static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { - snd_hda_codec_setup_stream(codec, 0x05 + substream->number, - 0, 0, 0); + snd_hda_codec_cleanup_stream(codec, 0x05 + substream->number); return 0; } @@ -3356,6 +3431,472 @@ static int patch_ad1984(struct hda_codec *codec) /* + * AD1883 / AD1884A / AD1984A / AD1984B + * + * port-B (0x14) - front mic-in + * port-E (0x1c) - rear mic-in + * port-F (0x16) - CD / ext out + * port-C (0x15) - rear line-in + * port-D (0x12) - rear line-out + * port-A (0x11) - front hp-out + * + * AD1984A = AD1884A + digital-mic + * AD1883 = equivalent with AD1984A + * AD1984B = AD1984A + extra SPDIF-out + * + * FIXME: + * We share the single DAC for both HP and line-outs (see AD1884/1984). + */ + +static hda_nid_t ad1884a_dac_nids[1] = { + 0x03, +}; + +#define ad1884a_adc_nids ad1884_adc_nids +#define ad1884a_capsrc_nids ad1884_capsrc_nids + +#define AD1884A_SPDIF_OUT 0x02 + +static struct hda_input_mux ad1884a_capture_source = { + .num_items = 5, + .items = { + { "Front Mic", 0x0 }, + { "Mic", 0x4 }, + { "Line", 0x1 }, + { "CD", 0x2 }, + { "Mix", 0x3 }, + }, +}; + +static struct snd_kcontrol_new ad1884a_base_mixers[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), + HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT), + HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Line Boost", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* The multiple "Capture Source" controls confuse alsamixer + * So call somewhat different.. + */ + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = ad198x_mux_enum_info, + .get = ad198x_mux_enum_get, + .put = ad198x_mux_enum_put, + }, + /* SPDIF controls */ + HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", + /* identical with ad1983 */ + .info = ad1983_spdif_route_info, + .get = ad1983_spdif_route_get, + .put = ad1983_spdif_route_put, + }, + { } /* end */ +}; + +/* + * initialization verbs + */ +static struct hda_verb ad1884a_init_verbs[] = { + /* DACs; unmute as default */ + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ + /* Port-A (HP) mixer - route only from analog mixer */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* Port-A pin */ + {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Port-D (Line-out) mixer - route only from analog mixer */ + {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* Port-D pin */ + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Mono-out mixer - route only from analog mixer */ + {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* Mono-out pin */ + {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Port-B (front mic) pin */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Port-C (rear line-in) pin */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Port-E (rear mic) pin */ + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* no boost */ + /* Port-F (CD) pin */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Analog mixer; mute as default */ + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* aux */ + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, + /* Analog Mix output amp */ + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* capture sources */ + {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* SPDIF output amp */ + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */ + { } /* end */ +}; + +#ifdef CONFIG_SND_HDA_POWER_SAVE +static struct hda_amp_list ad1884a_loopbacks[] = { + { 0x20, HDA_INPUT, 0 }, /* Front Mic */ + { 0x20, HDA_INPUT, 1 }, /* Mic */ + { 0x20, HDA_INPUT, 2 }, /* CD */ + { 0x20, HDA_INPUT, 4 }, /* Docking */ + { } /* end */ +}; +#endif + +/* + * Laptop model + * + * Port A: Headphone jack + * Port B: MIC jack + * Port C: Internal MIC + * Port D: Dock Line Out (if enabled) + * Port E: Dock Line In (if enabled) + * Port F: Internal speakers + */ + +static struct hda_input_mux ad1884a_laptop_capture_source = { + .num_items = 4, + .items = { + { "Mic", 0x0 }, /* port-B */ + { "Internal Mic", 0x1 }, /* port-C */ + { "Dock Mic", 0x4 }, /* port-E */ + { "Mix", 0x3 }, + }, +}; + +static struct snd_kcontrol_new ad1884a_laptop_mixers[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), + HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT), + HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* The multiple "Capture Source" controls confuse alsamixer + * So call somewhat different.. + */ + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = ad198x_mux_enum_info, + .get = ad198x_mux_enum_get, + .put = ad198x_mux_enum_put, + }, + { } /* end */ +}; + +static struct hda_input_mux ad1884a_mobile_capture_source = { + .num_items = 2, + .items = { + { "Mic", 0x1 }, /* port-C */ + { "Mix", 0x3 }, + }, +}; + +static struct snd_kcontrol_new ad1884a_mobile_mixers[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), + HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT), + HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x0c, 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, + }, + { } /* end */ +}; + +/* mute internal speaker if HP is plugged */ +static void ad1884a_hp_automute(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_codec_read(codec, 0x11, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE, + present ? 0x00 : 0x02); +} + +#define AD1884A_HP_EVENT 0x37 + +/* unsolicited event for HP jack sensing */ +static void ad1884a_hp_unsol_event(struct hda_codec *codec, unsigned int res) +{ + if ((res >> 26) != AD1884A_HP_EVENT) + return; + ad1884a_hp_automute(codec); +} + +/* initialize jack-sensing, too */ +static int ad1884a_hp_init(struct hda_codec *codec) +{ + ad198x_init(codec); + ad1884a_hp_automute(codec); + return 0; +} + +/* additional verbs for laptop model */ +static struct hda_verb ad1884a_laptop_verbs[] = { + /* Port-A (HP) pin - always unmuted */ + {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Port-F (int speaker) mixer - route only from analog mixer */ + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* Port-F pin */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* analog mix */ + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + /* unsolicited event for pin-sense */ + {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, + { } /* end */ +}; + +/* + * Thinkpad X300 + * 0x11 - HP + * 0x12 - speaker + * 0x14 - mic-in + * 0x17 - built-in mic + */ + +static struct hda_verb ad1984a_thinkpad_verbs[] = { + /* HP unmute */ + {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* analog mix */ + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + /* turn on EAPD */ + {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, + /* unsolicited event for pin-sense */ + {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, + /* internal mic - dmic */ + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + /* set magic COEFs for dmic */ + {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7}, + {0x01, AC_VERB_SET_PROC_COEF, 0x08}, + { } /* end */ +}; + +static struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), + HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT), + HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x0c, 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, + }, + { } /* end */ +}; + +static struct hda_input_mux ad1984a_thinkpad_capture_source = { + .num_items = 3, + .items = { + { "Mic", 0x0 }, + { "Internal Mic", 0x5 }, + { "Mix", 0x3 }, + }, +}; + +/* mute internal speaker if HP is plugged */ +static void ad1984a_thinkpad_automute(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0) + & AC_PINSENSE_PRESENCE; + snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); +} + +/* unsolicited event for HP jack sensing */ +static void ad1984a_thinkpad_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) != AD1884A_HP_EVENT) + return; + ad1984a_thinkpad_automute(codec); +} + +/* initialize jack-sensing, too */ +static int ad1984a_thinkpad_init(struct hda_codec *codec) +{ + ad198x_init(codec); + ad1984a_thinkpad_automute(codec); + return 0; +} + +/* + */ + +enum { + AD1884A_DESKTOP, + AD1884A_LAPTOP, + AD1884A_MOBILE, + AD1884A_THINKPAD, + AD1884A_MODELS +}; + +static const char *ad1884a_models[AD1884A_MODELS] = { + [AD1884A_DESKTOP] = "desktop", + [AD1884A_LAPTOP] = "laptop", + [AD1884A_MOBILE] = "mobile", + [AD1884A_THINKPAD] = "thinkpad", +}; + +static struct snd_pci_quirk ad1884a_cfg_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE), + SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD), + {} +}; + +static int patch_ad1884a(struct hda_codec *codec) +{ + struct ad198x_spec *spec; + int board_config; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + mutex_init(&spec->amp_mutex); + codec->spec = spec; + + spec->multiout.max_channels = 2; + spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids); + spec->multiout.dac_nids = ad1884a_dac_nids; + spec->multiout.dig_out_nid = AD1884A_SPDIF_OUT; + spec->num_adc_nids = ARRAY_SIZE(ad1884a_adc_nids); + spec->adc_nids = ad1884a_adc_nids; + spec->capsrc_nids = ad1884a_capsrc_nids; + spec->input_mux = &ad1884a_capture_source; + spec->num_mixers = 1; + spec->mixers[0] = ad1884a_base_mixers; + spec->num_init_verbs = 1; + spec->init_verbs[0] = ad1884a_init_verbs; + spec->spdif_route = 0; +#ifdef CONFIG_SND_HDA_POWER_SAVE + spec->loopback.amplist = ad1884a_loopbacks; +#endif + codec->patch_ops = ad198x_patch_ops; + + /* override some parameters */ + board_config = snd_hda_check_board_config(codec, AD1884A_MODELS, + ad1884a_models, + ad1884a_cfg_tbl); + switch (board_config) { + case AD1884A_LAPTOP: + spec->mixers[0] = ad1884a_laptop_mixers; + spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs; + spec->multiout.dig_out_nid = 0; + spec->input_mux = &ad1884a_laptop_capture_source; + codec->patch_ops.unsol_event = ad1884a_hp_unsol_event; + codec->patch_ops.init = ad1884a_hp_init; + break; + case AD1884A_MOBILE: + spec->mixers[0] = ad1884a_mobile_mixers; + spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs; + spec->multiout.dig_out_nid = 0; + spec->input_mux = &ad1884a_mobile_capture_source; + codec->patch_ops.unsol_event = ad1884a_hp_unsol_event; + codec->patch_ops.init = ad1884a_hp_init; + break; + case AD1884A_THINKPAD: + spec->mixers[0] = ad1984a_thinkpad_mixers; + spec->init_verbs[spec->num_init_verbs++] = + ad1984a_thinkpad_verbs; + spec->multiout.dig_out_nid = 0; + spec->input_mux = &ad1984a_thinkpad_capture_source; + codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event; + codec->patch_ops.init = ad1984a_thinkpad_init; + break; + } + + return 0; +} + + +/* * AD1882 * * port-A - front hp-out @@ -3654,13 +4195,19 @@ static int patch_ad1882(struct hda_codec *codec) * patch entries */ struct hda_codec_preset snd_hda_preset_analog[] = { + { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a }, { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 }, + { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a }, { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 }, + { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884a }, + { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884a }, { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 }, { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 }, { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 }, { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a }, { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 }, { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 }, + { .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 }, + { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 }, {} /* terminator */ }; diff --git a/sound/pci/hda/patch_atihdmi.c b/sound/pci/hda/patch_atihdmi.c index 9a8bb4ce3f8..12272508b11 100644 --- a/sound/pci/hda/patch_atihdmi.c +++ b/sound/pci/hda/patch_atihdmi.c @@ -27,6 +27,7 @@ #include <sound/core.h> #include "hda_codec.h" #include "hda_local.h" +#include "hda_patch.h" struct atihdmi_spec { struct hda_multi_out multiout; @@ -58,6 +59,10 @@ static int atihdmi_build_controls(struct hda_codec *codec) static int atihdmi_init(struct hda_codec *codec) { snd_hda_sequence_write(codec, atihdmi_basic_init); + /* SI codec requires to unmute the pin */ + if (get_wcaps(codec, 0x03) & AC_WCAP_OUT_AMP) + snd_hda_codec_write(codec, 0x03, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_UNMUTE); return 0; } @@ -112,6 +117,7 @@ static int atihdmi_build_pcms(struct hda_codec *codec) codec->pcm_info = info; info->name = "ATI HDMI"; + info->pcm_type = HDA_PCM_TYPE_HDMI; info->stream[SNDRV_PCM_STREAM_PLAYBACK] = atihdmi_pcm_digital_playback; return 0; @@ -158,5 +164,7 @@ struct hda_codec_preset snd_hda_preset_atihdmi[] = { { .id = 0x10027919, .name = "ATI RS600 HDMI", .patch = patch_atihdmi }, { .id = 0x1002791a, .name = "ATI RS690/780 HDMI", .patch = patch_atihdmi }, { .id = 0x1002aa01, .name = "ATI R6xx HDMI", .patch = patch_atihdmi }, + { .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_atihdmi }, + { .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_atihdmi }, {} /* terminator */ }; diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c index 3d6097ba1d6..c73ce074a6e 100644 --- a/sound/pci/hda/patch_cmedia.c +++ b/sound/pci/hda/patch_cmedia.c @@ -28,6 +28,7 @@ #include <sound/core.h> #include "hda_codec.h" #include "hda_local.h" +#include "hda_patch.h" #define NUM_PINS 11 @@ -329,6 +330,11 @@ static int cmi9880_build_controls(struct hda_codec *codec) err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); if (err < 0) return err; + err = snd_hda_create_spdif_share_sw(codec, + &spec->multiout); + if (err < 0) + return err; + spec->multiout.share_spdif = 1; } if (spec->dig_in_nid) { err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); @@ -432,7 +438,8 @@ static int cmi9880_playback_pcm_open(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct cmi_spec *spec = codec->spec; - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream); + return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, + hinfo); } static int cmi9880_playback_pcm_prepare(struct hda_pcm_stream *hinfo, @@ -506,7 +513,7 @@ static int cmi9880_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, { struct cmi_spec *spec = codec->spec; - snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 0, 0, 0); + snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); return 0; } @@ -571,6 +578,7 @@ static int cmi9880_build_pcms(struct hda_codec *codec) codec->num_pcms++; info++; info->name = "CMI9880 Digital"; + info->pcm_type = HDA_PCM_TYPE_SPDIF; if (spec->multiout.dig_out_nid) { info->stream[SNDRV_PCM_STREAM_PLAYBACK] = cmi9880_pcm_digital_playback; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid; @@ -603,6 +611,7 @@ static const char *cmi9880_models[CMI_MODELS] = { static struct snd_pci_quirk cmi9880_cfg_tbl[] = { SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", CMI_FULL_DIG), + SND_PCI_QUIRK(0x1854, 0x0032, "LG", CMI_FULL_DIG), {} /* terminator */ }; diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 7206b30cbf9..36fd8526003 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -27,6 +27,7 @@ #include <sound/core.h> #include "hda_codec.h" #include "hda_local.h" +#include "hda_patch.h" #define CXT_PIN_DIR_IN 0x00 #define CXT_PIN_DIR_OUT 0x01 @@ -98,7 +99,8 @@ static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct conexant_spec *spec = codec->spec; - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream); + return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, + hinfo); } static int conexant_playback_pcm_prepare(struct hda_pcm_stream *hinfo, @@ -172,8 +174,7 @@ static int conexant_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct conexant_spec *spec = codec->spec; - snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], - 0, 0, 0); + snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); return 0; } @@ -241,7 +242,7 @@ static int cx5051_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct conexant_spec *spec = codec->spec; - snd_hda_codec_setup_stream(codec, spec->cur_adc, 0, 0, 0); + snd_hda_codec_cleanup_stream(codec, spec->cur_adc); spec->cur_adc = 0; return 0; } @@ -284,6 +285,7 @@ static int conexant_build_pcms(struct hda_codec *codec) info++; codec->num_pcms++; info->name = "Conexant Digital"; + info->pcm_type = HDA_PCM_TYPE_SPDIF; info->stream[SNDRV_PCM_STREAM_PLAYBACK] = conexant_pcm_digital_playback; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = @@ -371,6 +373,11 @@ static int conexant_build_controls(struct hda_codec *codec) spec->multiout.dig_out_nid); if (err < 0) return err; + err = snd_hda_create_spdif_share_sw(codec, + &spec->multiout); + if (err < 0) + return err; + spec->multiout.share_spdif = 1; } if (spec->dig_in_nid) { err = snd_hda_create_spdif_in_ctls(codec,spec->dig_in_nid); @@ -511,6 +518,14 @@ static struct hda_input_mux cxt5045_capture_source_benq = { } }; +static struct hda_input_mux cxt5045_capture_source_hp530 = { + .num_items = 2, + .items = { + { "ExtMic", 0x1 }, + { "IntMic", 0x2 }, + } +}; + /* turn on/off EAPD (+ mute HP) as a master switch */ static int cxt5045_hp_master_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -639,6 +654,37 @@ static struct snd_kcontrol_new cxt5045_benq_mixers[] = { {} }; +static struct snd_kcontrol_new cxt5045_mixers_hp530[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = conexant_mux_enum_info, + .get = conexant_mux_enum_get, + .put = conexant_mux_enum_put + }, + HDA_CODEC_VOLUME("Int Mic Capture Volume", 0x1a, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Int Mic Capture Switch", 0x1a, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Ext Mic Capture Volume", 0x1a, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Ext Mic Capture Switch", 0x1a, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("PCM Playback Volume", 0x17, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("PCM Playback Switch", 0x17, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x17, 0x2, HDA_INPUT), + HDA_CODEC_MUTE("Int Mic Playback Switch", 0x17, 0x2, HDA_INPUT), + HDA_CODEC_VOLUME("Ext Mic Playback Volume", 0x17, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Ext Mic Playback Switch", 0x17, 0x1, HDA_INPUT), + HDA_BIND_VOL("Master Playback Volume", &cxt5045_hp_bind_master_vol), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = cxt_eapd_info, + .get = cxt_eapd_get, + .put = cxt5045_hp_master_sw_put, + .private_value = 0x10, + }, + + {} +}; + static struct hda_verb cxt5045_init_verbs[] = { /* Line in, Mic */ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, @@ -833,6 +879,7 @@ enum { CXT5045_LAPTOP_MICSENSE, CXT5045_LAPTOP_HPMICSENSE, CXT5045_BENQ, + CXT5045_LAPTOP_HP530, #ifdef CONFIG_SND_DEBUG CXT5045_TEST, #endif @@ -844,6 +891,7 @@ static const char *cxt5045_models[CXT5045_MODELS] = { [CXT5045_LAPTOP_MICSENSE] = "laptop-micsense", [CXT5045_LAPTOP_HPMICSENSE] = "laptop-hpmicsense", [CXT5045_BENQ] = "benq", + [CXT5045_LAPTOP_HP530] = "laptop-hp530", #ifdef CONFIG_SND_DEBUG [CXT5045_TEST] = "test", #endif @@ -857,7 +905,7 @@ static struct snd_pci_quirk cxt5045_cfg_tbl[] = { SND_PCI_QUIRK(0x103c, 0x30bb, "HP DV8000", CXT5045_LAPTOP_HPSENSE), SND_PCI_QUIRK(0x103c, 0x30cd, "HP DV Series", CXT5045_LAPTOP_HPSENSE), SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV9533EG", CXT5045_LAPTOP_HPSENSE), - SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT5045_LAPTOP_HPSENSE), + SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT5045_LAPTOP_HP530), SND_PCI_QUIRK(0x103c, 0x30d9, "HP Spartan", CXT5045_LAPTOP_HPSENSE), SND_PCI_QUIRK(0x152d, 0x0753, "Benq R55E", CXT5045_BENQ), SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_LAPTOP_MICSENSE), @@ -941,6 +989,14 @@ static int patch_cxt5045(struct hda_codec *codec) spec->num_mixers = 2; codec->patch_ops.init = cxt5045_init; break; + case CXT5045_LAPTOP_HP530: + codec->patch_ops.unsol_event = cxt5045_hp_unsol_event; + spec->input_mux = &cxt5045_capture_source_hp530; + spec->num_init_verbs = 2; + spec->init_verbs[1] = cxt5045_hp_sense_init_verbs; + spec->mixers[0] = cxt5045_mixers_hp530; + codec->patch_ops.init = cxt5045_init; + break; #ifdef CONFIG_SND_DEBUG case CXT5045_TEST: spec->input_mux = &cxt5045_test_capture_source; @@ -1537,7 +1593,7 @@ static void cxt5051_portc_automic(struct hda_codec *codec) new_adc = spec->adc_nids[spec->cur_adc_idx]; if (spec->cur_adc && spec->cur_adc != new_adc) { /* stream is running, let's swap the current ADC */ - snd_hda_codec_setup_stream(codec, spec->cur_adc, 0, 0, 0); + snd_hda_codec_cleanup_stream(codec, spec->cur_adc); spec->cur_adc = new_adc; snd_hda_codec_setup_stream(codec, new_adc, spec->cur_adc_stream_tag, 0, diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 33282f9c01c..cdda64b02f4 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -30,6 +30,7 @@ #include <sound/core.h> #include "hda_codec.h" #include "hda_local.h" +#include "hda_patch.h" #define ALC880_FRONT_EVENT 0x01 #define ALC880_DCVOL_EVENT 0x02 @@ -97,16 +98,19 @@ enum { ALC262_SONY_ASSAMD, ALC262_BENQ_T31, ALC262_ULTRA, + ALC262_LENOVO_3000, ALC262_AUTO, ALC262_MODEL_LAST /* last tag */ }; /* ALC268 models */ enum { + ALC267_QUANTA_IL1, ALC268_3ST, ALC268_TOSHIBA, ALC268_ACER, ALC268_DELL, + ALC268_ZEPTO, #ifdef CONFIG_SND_DEBUG ALC268_TEST, #endif @@ -195,10 +199,11 @@ enum { ALC883_LENOVO_NB0763, ALC888_LENOVO_MS7195_DIG, ALC883_HAIER_W66, - ALC888_6ST_HP, ALC888_3ST_HP, ALC888_6ST_DELL, ALC883_MITAC, + ALC883_CLEVO_M720, + ALC883_FUJITSU_PI2515, ALC883_AUTO, ALC883_MODEL_LAST, }; @@ -237,6 +242,7 @@ struct alc_spec { /* capture */ unsigned int num_adc_nids; hda_nid_t *adc_nids; + hda_nid_t *capsrc_nids; hda_nid_t dig_in_nid; /* digital-in NID; optional */ /* capture source */ @@ -270,7 +276,6 @@ struct alc_spec { /* for virtual master */ hda_nid_t vmaster_nid; - u32 vmaster_tlv[4]; #ifdef CONFIG_SND_HDA_POWER_SAVE struct hda_loopback_check loopback; #endif @@ -290,6 +295,7 @@ struct alc_config_preset { hda_nid_t hp_nid; /* optional */ unsigned int num_adc_nids; hda_nid_t *adc_nids; + hda_nid_t *capsrc_nids; hda_nid_t dig_in_nid; unsigned int num_channel_mode; const struct hda_channel_mode *channel_mode; @@ -336,9 +342,10 @@ static int alc_mux_enum_put(struct snd_kcontrol *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; + hda_nid_t nid = spec->capsrc_nids ? + spec->capsrc_nids[adc_idx] : spec->adc_nids[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]); + nid, &spec->cur_mux[adc_idx]); } @@ -707,6 +714,7 @@ static void setup_preset(struct alc_spec *spec, spec->num_adc_nids = preset->num_adc_nids; spec->adc_nids = preset->adc_nids; + spec->capsrc_nids = preset->capsrc_nids; spec->dig_in_nid = preset->dig_in_nid; spec->unsol_event = preset->unsol_event; @@ -741,7 +749,6 @@ static struct hda_verb alc_gpio3_init_verbs[] = { static void alc_sku_automute(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - unsigned int mute; unsigned int present; unsigned int hp_nid = spec->autocfg.hp_pins[0]; unsigned int sp_nid = spec->autocfg.speaker_pins[0]; @@ -751,16 +758,8 @@ static void alc_sku_automute(struct hda_codec *codec) present = snd_hda_codec_read(codec, hp_nid, 0, AC_VERB_GET_PIN_SENSE, 0); spec->jack_present = (present & 0x80000000) != 0; - if (spec->jack_present) { - /* mute internal speaker */ - snd_hda_codec_amp_stereo(codec, sp_nid, HDA_OUTPUT, 0, - HDA_AMP_MUTE, HDA_AMP_MUTE); - } else { - /* unmute internal speaker if necessary */ - mute = snd_hda_codec_amp_read(codec, hp_nid, 0, HDA_OUTPUT, 0); - snd_hda_codec_amp_stereo(codec, sp_nid, HDA_OUTPUT, 0, - HDA_AMP_MUTE, mute); - } + snd_hda_codec_write(codec, sp_nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + spec->jack_present ? 0 : PIN_OUT); } /* unsolicited event for HP jack sensing */ @@ -1319,11 +1318,19 @@ static struct snd_kcontrol_new alc880_f1734_mixer[] = { HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), { } /* end */ }; +static struct hda_input_mux alc880_f1734_capture_source = { + .num_items = 2, + .items = { + { "Mic", 0x1 }, + { "CD", 0x4 }, + }, +}; + /* * ALC880 ASUS model @@ -1516,6 +1523,11 @@ static int alc_build_controls(struct hda_codec *codec) spec->multiout.dig_out_nid); if (err < 0) return err; + err = snd_hda_create_spdif_share_sw(codec, + &spec->multiout); + if (err < 0) + return err; + spec->multiout.share_spdif = 1; } if (spec->dig_in_nid) { err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); @@ -1525,10 +1537,11 @@ static int alc_build_controls(struct hda_codec *codec) /* if we have no master control, let's create it */ if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { + unsigned int vmaster_tlv[4]; snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, - HDA_OUTPUT, spec->vmaster_tlv); + HDA_OUTPUT, vmaster_tlv); err = snd_hda_add_vmaster(codec, "Master Playback Volume", - spec->vmaster_tlv, alc_slave_vols); + vmaster_tlv, alc_slave_vols); if (err < 0) return err; } @@ -1882,7 +1895,7 @@ static void alc880_uniwill_p53_hp_automute(struct hda_codec *codec) present = snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; bits = present ? HDA_AMP_MUTE : 0; - snd_hda_codec_amp_stereo(codec, 0x15, HDA_INPUT, 0, HDA_AMP_MUTE, bits); + snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, HDA_AMP_MUTE, bits); } static void alc880_uniwill_p53_dcvol_automute(struct hda_codec *codec) @@ -1915,6 +1928,7 @@ static void alc880_uniwill_p53_unsol_event(struct hda_codec *codec, * HP = 0x14, speaker-out = 0x15, mic = 0x18 */ static struct hda_verb alc880_pin_f1734_init_verbs[] = { + {0x07, AC_VERB_SET_CONNECT_SEL, 0x01}, {0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, {0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, {0x12, AC_VERB_SET_CONNECT_SEL, 0x01}, @@ -1927,7 +1941,7 @@ static struct hda_verb alc880_pin_f1734_init_verbs[] = { {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50}, {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, @@ -1935,6 +1949,9 @@ static struct hda_verb alc880_pin_f1734_init_verbs[] = { {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_HP_EVENT}, + {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_DCVOL_EVENT}, + { } }; @@ -2318,7 +2335,8 @@ static int alc880_playback_pcm_open(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct alc_spec *spec = codec->spec; - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream); + return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, + hinfo); } static int alc880_playback_pcm_prepare(struct hda_pcm_stream *hinfo, @@ -2392,8 +2410,8 @@ static int alc880_alt_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 + 1], - 0, 0, 0); + snd_hda_codec_cleanup_stream(codec, + spec->adc_nids[substream->number + 1]); return 0; } @@ -2498,6 +2516,7 @@ static int alc_build_pcms(struct hda_codec *codec) codec->num_pcms = 2; info = spec->pcm_rec + 1; info->name = spec->stream_name_digital; + info->pcm_type = HDA_PCM_TYPE_SPDIF; if (spec->multiout.dig_out_nid && spec->stream_digital_playback) { info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_digital_playback); @@ -2560,6 +2579,7 @@ static void alc_free(struct hda_codec *codec) kfree(spec->kctl_alloc); } kfree(spec); + codec->spec = NULL; /* to be sure */ } /* @@ -3057,7 +3077,9 @@ static struct alc_config_preset alc880_presets[] = { .hp_nid = 0x02, .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes), .channel_mode = alc880_2_jack_modes, - .input_mux = &alc880_capture_source, + .input_mux = &alc880_f1734_capture_source, + .unsol_event = alc880_uniwill_p53_unsol_event, + .init_hook = alc880_uniwill_p53_hp_automute, }, [ALC880_ASUS] = { .mixers = { alc880_asus_mixer }, @@ -3467,15 +3489,21 @@ static int alc880_auto_create_analog_input_ctls(struct alc_spec *spec, return 0; } -static void alc880_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t nid, int pin_type, - int dac_idx) +static void alc_set_pin_output(struct hda_codec *codec, hda_nid_t nid, + unsigned int pin_type) { - /* set as output */ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type); + /* unmute pin */ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); +} + +static void alc880_auto_set_output_and_unmute(struct hda_codec *codec, + hda_nid_t nid, int pin_type, + int dac_idx) +{ + alc_set_pin_output(codec, nid, pin_type); /* need the manual connection? */ if (alc880_is_multi_pin(nid)) { struct alc_spec *spec = codec->spec; @@ -3597,9 +3625,12 @@ static int alc880_parse_auto_config(struct hda_codec *codec) /* additional initialization for auto-configuration model */ static void alc880_auto_init(struct hda_codec *codec) { + struct alc_spec *spec = codec->spec; alc880_auto_init_multi_out(codec); alc880_auto_init_extra_out(codec); alc880_auto_init_analog_input(codec); + if (spec->unsol_event) + alc_sku_automute(codec); } /* @@ -4795,11 +4826,7 @@ static void alc260_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t nid, int pin_type, int sel_idx) { - /* set as output */ - 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); + alc_set_pin_output(codec, nid, pin_type); /* need the manual connection? */ if (nid >= 0x12) { int idx = nid - 0x12; @@ -4929,7 +4956,7 @@ static int alc260_parse_auto_config(struct hda_codec *codec) /* check whether NID 0x04 is valid */ wcap = get_wcaps(codec, 0x04); wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; /* get type */ - if (wcap != AC_WID_AUD_IN) { + if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) { spec->adc_nids = alc260_adc_nids_alt; spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt); spec->mixers[spec->num_mixers] = alc260_capture_alt_mixer; @@ -4946,8 +4973,11 @@ static int alc260_parse_auto_config(struct hda_codec *codec) /* additional initialization for auto-configuration model */ static void alc260_auto_init(struct hda_codec *codec) { + struct alc_spec *spec = codec->spec; alc260_auto_init_multi_out(codec); alc260_auto_init_analog_input(codec); + if (spec->unsol_event) + alc_sku_automute(codec); } #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -5204,6 +5234,9 @@ static hda_nid_t alc882_dac_nids[4] = { #define alc882_adc_nids alc880_adc_nids #define alc882_adc_nids_alt alc880_adc_nids_alt +static hda_nid_t alc882_capsrc_nids[3] = { 0x24, 0x23, 0x22 }; +static hda_nid_t alc882_capsrc_nids_alt[2] = { 0x23, 0x22 }; + /* input MUX */ /* FIXME: should be a matrix-type input source selection */ @@ -5226,15 +5259,11 @@ static int alc882_mux_enum_put(struct snd_kcontrol *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; + hda_nid_t nid = spec->capsrc_nids ? + spec->capsrc_nids[adc_idx] : spec->adc_nids[adc_idx]; unsigned int *cur_val = &spec->cur_mux[adc_idx]; unsigned int i, idx; - if (spec->num_adc_nids < 3) - nid = capture_mixers[adc_idx + 1]; - else - nid = capture_mixers[adc_idx]; idx = ucontrol->value.enumerated.item[0]; if (idx >= imux->num_items) idx = imux->num_items - 1; @@ -6111,6 +6140,7 @@ static struct alc_config_preset alc882_presets[] = { .dig_out_nid = ALC882_DIGOUT_NID, .num_adc_nids = ARRAY_SIZE(alc882_adc_nids), .adc_nids = alc882_adc_nids, + .capsrc_nids = alc882_capsrc_nids, .num_channel_mode = ARRAY_SIZE(alc882_3ST_6ch_modes), .channel_mode = alc882_3ST_6ch_modes, .need_dac_fix = 1, @@ -6127,6 +6157,7 @@ static struct alc_config_preset alc882_presets[] = { .dig_out_nid = ALC882_DIGOUT_NID, .num_adc_nids = ARRAY_SIZE(alc882_adc_nids), .adc_nids = alc882_adc_nids, + .capsrc_nids = alc882_capsrc_nids, .num_channel_mode = ARRAY_SIZE(alc882_3ST_6ch_modes), .channel_mode = alc882_3ST_6ch_modes, .need_dac_fix = 1, @@ -6182,15 +6213,11 @@ static void alc882_auto_set_output_and_unmute(struct hda_codec *codec, struct alc_spec *spec = codec->spec; int idx; + alc_set_pin_output(codec, nid, pin_type); 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); } @@ -6219,6 +6246,9 @@ static void alc882_auto_init_hp_out(struct hda_codec *codec) if (pin) /* connect to front */ /* use dac 0 */ alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); + pin = spec->autocfg.speaker_pins[0]; + if (pin) + alc882_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); } #define alc882_is_input_pin(nid) alc880_is_input_pin(nid) @@ -6231,16 +6261,21 @@ static void alc882_auto_init_analog_input(struct hda_codec *codec) for (i = 0; i < AUTO_PIN_LAST; i++) { hda_nid_t nid = spec->autocfg.input_pins[i]; - if (alc882_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 != ALC882_PIN_CD_NID) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_MUTE); + unsigned int vref; + if (!nid) + continue; + vref = PIN_IN; + if (1 /*i <= AUTO_PIN_FRONT_MIC*/) { + if (snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP) & + AC_PINCAP_VREF_80) + vref = PIN_VREF80; } + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, vref); + if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_MUTE); } } @@ -6294,11 +6329,16 @@ static int alc882_parse_auto_config(struct hda_codec *codec) /* additional initialization for auto-configuration model */ static void alc882_auto_init(struct hda_codec *codec) { + struct alc_spec *spec = codec->spec; alc882_auto_init_multi_out(codec); alc882_auto_init_hp_out(codec); alc882_auto_init_analog_input(codec); + if (spec->unsol_event) + alc_sku_automute(codec); } +static int patch_alc883(struct hda_codec *codec); /* called in patch_alc882() */ + static int patch_alc882(struct hda_codec *codec) { struct alc_spec *spec; @@ -6328,6 +6368,11 @@ static int patch_alc882(struct hda_codec *codec) board_config = ALC885_MBP3; break; default: + /* ALC889A is handled better as ALC888-compatible */ + if (codec->revision_id == 0x100103) { + alc_free(codec); + return patch_alc883(codec); + } printk(KERN_INFO "hda_codec: Unknown model for ALC882, " "trying auto-probe from BIOS...\n"); board_config = ALC882_AUTO; @@ -6372,12 +6417,14 @@ static int patch_alc882(struct hda_codec *codec) if (wcap != AC_WID_AUD_IN) { spec->adc_nids = alc882_adc_nids_alt; spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids_alt); + spec->capsrc_nids = alc882_capsrc_nids_alt; spec->mixers[spec->num_mixers] = alc882_capture_alt_mixer; spec->num_mixers++; } else { spec->adc_nids = alc882_adc_nids; spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids); + spec->capsrc_nids = alc882_capsrc_nids; spec->mixers[spec->num_mixers] = alc882_capture_mixer; spec->num_mixers++; } @@ -6412,7 +6459,7 @@ static int patch_alc882(struct hda_codec *codec) static hda_nid_t alc883_dac_nids[4] = { /* front, rear, clfe, rear_surr */ - 0x02, 0x04, 0x03, 0x05 + 0x02, 0x03, 0x04, 0x05 }; static hda_nid_t alc883_adc_nids[2] = { @@ -6420,6 +6467,8 @@ static hda_nid_t alc883_adc_nids[2] = { 0x08, 0x09, }; +static hda_nid_t alc883_capsrc_nids[2] = { 0x23, 0x22 }; + /* input MUX */ /* FIXME: should be a matrix-type input source selection */ @@ -6451,35 +6500,18 @@ static struct hda_input_mux alc883_lenovo_nb0763_capture_source = { }, }; +static struct hda_input_mux alc883_fujitsu_pi2515_capture_source = { + .num_items = 2, + .items = { + { "Mic", 0x0 }, + { "Int Mic", 0x1 }, + }, +}; + #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[2] = { 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) - return 0; - for (i = 0; i < imux->num_items; i++) { - unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE; - snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, - imux->items[i].index, - HDA_AMP_MUTE, v); - } - *cur_val = idx; - return 1; -} +/* ALC883 has the ALC882-type input selection */ +#define alc883_mux_enum_put alc882_mux_enum_put /* * 2ch mode @@ -6638,6 +6670,60 @@ static struct snd_kcontrol_new alc883_mitac_mixer[] = { { } /* end */ }; +static struct snd_kcontrol_new alc883_clevo_m720_mixer[] = { + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = alc883_mux_enum_info, + .get = alc883_mux_enum_get, + .put = alc883_mux_enum_put, + }, + { } /* end */ +}; + +static struct snd_kcontrol_new alc883_2ch_fujitsu_pi2515_mixer[] = { + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = alc883_mux_enum_info, + .get = alc883_mux_enum_get, + .put = alc883_mux_enum_put, + }, + { } /* end */ +}; + static struct snd_kcontrol_new alc883_3ST_2ch_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), @@ -6787,6 +6873,9 @@ static struct snd_kcontrol_new alc883_tagra_2ch_mixer[] = { HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), @@ -6878,124 +6967,6 @@ static struct snd_kcontrol_new alc883_medion_md2_mixer[] = { { } /* end */ }; -static struct snd_kcontrol_new alc888_6st_hp_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0e, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0e, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), - HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = alc883_mux_enum_info, - .get = alc883_mux_enum_get, - .put = alc883_mux_enum_put, - }, - { } /* end */ -}; - -static struct snd_kcontrol_new alc888_3st_hp_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0e, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0e, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), - HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = alc883_mux_enum_info, - .get = alc883_mux_enum_get, - .put = alc883_mux_enum_put, - }, - { } /* end */ -}; - -static struct snd_kcontrol_new alc888_6st_dell_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0e, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0e, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), - HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = alc883_mux_enum_info, - .get = alc883_mux_enum_get, - .put = alc883_mux_enum_put, - }, - { } /* end */ -}; - static struct snd_kcontrol_new alc883_acer_aspire_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), @@ -7171,6 +7142,35 @@ static struct hda_verb alc883_mitac_verbs[] = { { } /* end */ }; +static struct hda_verb alc883_clevo_m720_verbs[] = { + /* HP */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + /* Int speaker */ + {0x14, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + + /* enable unsolicited event */ + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, + + { } /* end */ +}; + +static struct hda_verb alc883_2ch_fujitsu_pi2515_verbs[] = { + /* HP */ + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + /* Subwoofer */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + + /* enable unsolicited event */ + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + + { } /* end */ +}; + static struct hda_verb alc883_tagra_verbs[] = { {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, @@ -7227,26 +7227,14 @@ static struct hda_verb alc883_haier_w66_verbs[] = { { } /* end */ }; -static struct hda_verb alc888_6st_hp_verbs[] = { - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front: output 0 (0x0c) */ - {0x15, AC_VERB_SET_CONNECT_SEL, 0x02}, /* Rear : output 2 (0x0e) */ - {0x16, AC_VERB_SET_CONNECT_SEL, 0x01}, /* CLFE : output 1 (0x0d) */ - {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, /* Side : output 3 (0x0f) */ - { } -}; - static struct hda_verb alc888_3st_hp_verbs[] = { {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front: output 0 (0x0c) */ - {0x18, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Rear : output 1 (0x0d) */ - {0x16, AC_VERB_SET_CONNECT_SEL, 0x02}, /* CLFE : output 2 (0x0e) */ + {0x16, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Rear : output 1 (0x0d) */ + {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* CLFE : output 2 (0x0e) */ { } }; static struct hda_verb alc888_6st_dell_verbs[] = { - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front: output 0 (0x0c) */ - {0x15, AC_VERB_SET_CONNECT_SEL, 0x02}, /* Rear : output 1 (0x0e) */ - {0x16, AC_VERB_SET_CONNECT_SEL, 0x01}, /* CLFE : output 2 (0x0d) */ - {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, /* Side : output 3 (0x0f) */ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, { } }; @@ -7354,6 +7342,68 @@ static void alc883_tagra_unsol_event(struct hda_codec *codec, unsigned int res) alc883_tagra_automute(codec); } +/* toggle speaker-output according to the hp-jack state */ +static void alc883_clevo_m720_hp_automute(struct hda_codec *codec) +{ + unsigned int present; + unsigned char bits; + + present = snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_PIN_SENSE, 0) + & AC_PINSENSE_PRESENCE; + bits = present ? HDA_AMP_MUTE : 0; + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, bits); +} + +static void alc883_clevo_m720_mic_automute(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_codec_read(codec, 0x18, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); +} + +static void alc883_clevo_m720_automute(struct hda_codec *codec) +{ + alc883_clevo_m720_hp_automute(codec); + alc883_clevo_m720_mic_automute(codec); +} + +static void alc883_clevo_m720_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + switch (res >> 26) { + case ALC880_HP_EVENT: + alc883_clevo_m720_hp_automute(codec); + break; + case ALC880_MIC_EVENT: + alc883_clevo_m720_mic_automute(codec); + break; + } +} + +/* toggle speaker-output according to the hp-jack state */ +static void alc883_2ch_fujitsu_pi2515_automute(struct hda_codec *codec) +{ + unsigned int present; + unsigned char bits; + + present = snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0) + & AC_PINSENSE_PRESENCE; + bits = present ? HDA_AMP_MUTE : 0; + snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, + HDA_AMP_MUTE, bits); +} + +static void alc883_2ch_fujitsu_pi2515_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) == ALC880_HP_EVENT) + alc883_2ch_fujitsu_pi2515_automute(codec); +} + static void alc883_haier_w66_automute(struct hda_codec *codec) { unsigned int present; @@ -7587,10 +7637,11 @@ static const char *alc883_models[ALC883_MODEL_LAST] = { [ALC883_LENOVO_NB0763] = "lenovo-nb0763", [ALC888_LENOVO_MS7195_DIG] = "lenovo-ms7195-dig", [ALC883_HAIER_W66] = "haier-w66", - [ALC888_6ST_HP] = "6stack-hp", [ALC888_3ST_HP] = "3stack-hp", [ALC888_6ST_DELL] = "6stack-dell", [ALC883_MITAC] = "mitac", + [ALC883_CLEVO_M720] = "clevo-m720", + [ALC883_FUJITSU_PI2515] = "fujitsu-pi2515", [ALC883_AUTO] = "auto", }; @@ -7604,7 +7655,7 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = { SND_PCI_QUIRK(0x103c, 0x2a3d, "HP Pavillion", ALC883_6ST_DIG), SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP), SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP), - SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC888_6ST_HP), + SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG), SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1071, 0x8253, "Mitac 8252d", ALC883_MITAC), @@ -7614,7 +7665,9 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = { SND_PCI_QUIRK(0x1462, 0x0349, "MSI", ALC883_TARGA_2ch_DIG), SND_PCI_QUIRK(0x1462, 0x040d, "MSI", ALC883_TARGA_2ch_DIG), SND_PCI_QUIRK(0x1462, 0x0579, "MSI", ALC883_TARGA_2ch_DIG), + SND_PCI_QUIRK(0x1462, 0x2fb3, "MSI", ALC883_TARGA_2ch_DIG), SND_PCI_QUIRK(0x1462, 0x3729, "MSI S420", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x3783, "NEC S970", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0x3b7f, "MSI", ALC883_TARGA_2ch_DIG), SND_PCI_QUIRK(0x1462, 0x3ef9, "MSI", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0x3fc1, "MSI", ALC883_TARGA_DIG), @@ -7627,13 +7680,17 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = { SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1462, 0x7250, "MSI", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x7267, "MSI", ALC883_3ST_6ch_DIG), SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1462, 0xa422, "MSI", ALC883_TARGA_2ch_DIG), SND_PCI_QUIRK(0x147b, 0x1083, "Abit IP35-PRO", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1558, 0x0721, "Clevo laptop M720R", ALC883_CLEVO_M720), + SND_PCI_QUIRK(0x1558, 0x0722, "Clevo laptop M720SR", ALC883_CLEVO_M720), SND_PCI_QUIRK(0x1558, 0, "Clevo laptop", ALC883_LAPTOP_EAPD), SND_PCI_QUIRK(0x15d9, 0x8780, "Supermicro PDSBA", ALC883_3ST_6ch), SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_MEDION), + SND_PCI_QUIRK(0x1734, 0x1108, "Fujitsu AMILO Pi2515", ALC883_FUJITSU_PI2515), SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo 101e", ALC883_LENOVO_101E_2ch), SND_PCI_QUIRK(0x17aa, 0x2085, "Lenovo NB0763", ALC883_LENOVO_NB0763), SND_PCI_QUIRK(0x17aa, 0x3bfc, "Lenovo NB0763", ALC883_LENOVO_NB0763), @@ -7652,8 +7709,6 @@ static struct alc_config_preset alc883_presets[] = { .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, @@ -7665,8 +7720,6 @@ static struct alc_config_preset alc883_presets[] = { .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, @@ -7678,8 +7731,6 @@ static struct alc_config_preset alc883_presets[] = { .init_verbs = { alc883_init_verbs }, .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .adc_nids = alc883_adc_nids, .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), .channel_mode = alc883_3ST_6ch_modes, .need_dac_fix = 1, @@ -7691,8 +7742,6 @@ static struct alc_config_preset alc883_presets[] = { .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, @@ -7704,8 +7753,6 @@ static struct alc_config_preset alc883_presets[] = { .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, .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), .channel_mode = alc883_3ST_6ch_modes, .need_dac_fix = 1, @@ -7719,8 +7766,6 @@ static struct alc_config_preset alc883_presets[] = { .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, .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), .channel_mode = alc883_3ST_2ch_modes, .input_mux = &alc883_capture_source, @@ -7737,8 +7782,6 @@ static struct alc_config_preset alc883_presets[] = { .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs }, .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .adc_nids = alc883_adc_nids, .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), .channel_mode = alc883_3ST_2ch_modes, .input_mux = &alc883_capture_source, @@ -7749,8 +7792,6 @@ static struct alc_config_preset alc883_presets[] = { .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, .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), .channel_mode = alc883_3ST_2ch_modes, .input_mux = &alc883_capture_source, @@ -7764,8 +7805,6 @@ static struct alc_config_preset alc883_presets[] = { alc883_medion_eapd_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_sixstack_modes), .channel_mode = alc883_sixstack_modes, .input_mux = &alc883_capture_source, @@ -7776,8 +7815,6 @@ static struct alc_config_preset alc883_presets[] = { .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, .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), .channel_mode = alc883_3ST_2ch_modes, .input_mux = &alc883_capture_source, @@ -7789,19 +7826,27 @@ static struct alc_config_preset alc883_presets[] = { .init_verbs = { alc883_init_verbs, alc882_eapd_verbs }, .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .adc_nids = alc883_adc_nids, .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), .channel_mode = alc883_3ST_2ch_modes, .input_mux = &alc883_capture_source, }, + [ALC883_CLEVO_M720] = { + .mixers = { alc883_clevo_m720_mixer }, + .init_verbs = { alc883_init_verbs, alc883_clevo_m720_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_capture_source, + .unsol_event = alc883_clevo_m720_unsol_event, + .init_hook = alc883_clevo_m720_automute, + }, [ALC883_LENOVO_101E_2ch] = { .mixers = { alc883_lenovo_101e_2ch_mixer}, .init_verbs = { alc883_init_verbs, alc883_lenovo_101e_verbs}, .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .adc_nids = alc883_adc_nids, .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), .channel_mode = alc883_3ST_2ch_modes, .input_mux = &alc883_lenovo_101e_capture_source, @@ -7813,8 +7858,6 @@ static struct alc_config_preset alc883_presets[] = { .init_verbs = { alc883_init_verbs, alc883_lenovo_nb0763_verbs}, .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .adc_nids = alc883_adc_nids, .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), .channel_mode = alc883_3ST_2ch_modes, .need_dac_fix = 1, @@ -7828,8 +7871,6 @@ static struct alc_config_preset alc883_presets[] = { .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, .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), .channel_mode = alc883_3ST_6ch_modes, .need_dac_fix = 1, @@ -7843,47 +7884,28 @@ static struct alc_config_preset alc883_presets[] = { .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, .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), .channel_mode = alc883_3ST_2ch_modes, .input_mux = &alc883_capture_source, .unsol_event = alc883_haier_w66_unsol_event, .init_hook = alc883_haier_w66_automute, - }, - [ALC888_6ST_HP] = { - .mixers = { alc888_6st_hp_mixer, alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs, alc888_6st_hp_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_3ST_HP] = { - .mixers = { alc888_3st_hp_mixer, alc883_chmode_mixer }, + .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, .init_verbs = { alc883_init_verbs, alc888_3st_hp_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(alc888_3st_hp_modes), .channel_mode = alc888_3st_hp_modes, .need_dac_fix = 1, .input_mux = &alc883_capture_source, }, [ALC888_6ST_DELL] = { - .mixers = { alc888_6st_dell_mixer, alc883_chmode_mixer }, + .mixers = { alc883_base_mixer, alc883_chmode_mixer }, .init_verbs = { alc883_init_verbs, alc888_6st_dell_verbs }, .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, .dig_out_nid = ALC883_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .adc_nids = alc883_adc_nids, .dig_in_nid = ALC883_DIGIN_NID, .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), .channel_mode = alc883_sixstack_modes, @@ -7896,14 +7918,25 @@ static struct alc_config_preset alc883_presets[] = { .init_verbs = { alc883_init_verbs, alc883_mitac_verbs }, .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .adc_nids = alc883_adc_nids, .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), .channel_mode = alc883_3ST_2ch_modes, .input_mux = &alc883_capture_source, .unsol_event = alc883_mitac_unsol_event, .init_hook = alc883_mitac_automute, }, + [ALC883_FUJITSU_PI2515] = { + .mixers = { alc883_2ch_fujitsu_pi2515_mixer }, + .init_verbs = { alc883_init_verbs, + alc883_2ch_fujitsu_pi2515_verbs}, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_fujitsu_pi2515_capture_source, + .unsol_event = alc883_2ch_fujitsu_pi2515_unsol_event, + .init_hook = alc883_2ch_fujitsu_pi2515_automute, + }, }; @@ -7918,15 +7951,11 @@ static void alc883_auto_set_output_and_unmute(struct hda_codec *codec, struct alc_spec *spec = codec->spec; int idx; + alc_set_pin_output(codec, nid, pin_type); 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); } @@ -7955,6 +7984,9 @@ static void alc883_auto_init_hp_out(struct hda_codec *codec) if (pin) /* connect to front */ /* use dac 0 */ alc883_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); + pin = spec->autocfg.speaker_pins[0]; + if (pin) + alc883_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); } #define alc883_is_input_pin(nid) alc880_is_input_pin(nid) @@ -8006,9 +8038,12 @@ static int alc883_parse_auto_config(struct hda_codec *codec) /* additional initialization for auto-configuration model */ static void alc883_auto_init(struct hda_codec *codec) { + struct alc_spec *spec = codec->spec; alc883_auto_init_multi_out(codec); alc883_auto_init_hp_out(codec); alc883_auto_init_analog_input(codec); + if (spec->unsol_event) + alc_sku_automute(codec); } static int patch_alc883(struct hda_codec *codec) @@ -8057,10 +8092,9 @@ static int patch_alc883(struct hda_codec *codec) spec->stream_digital_playback = &alc883_pcm_digital_playback; spec->stream_digital_capture = &alc883_pcm_digital_capture; - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = alc883_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids); - } + spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids); + spec->adc_nids = alc883_adc_nids; + spec->capsrc_nids = alc883_capsrc_nids; spec->vmaster_nid = 0x0c; @@ -8085,6 +8119,8 @@ static int patch_alc883(struct hda_codec *codec) #define alc262_dac_nids alc260_dac_nids #define alc262_adc_nids alc882_adc_nids #define alc262_adc_nids_alt alc882_adc_nids_alt +#define alc262_capsrc_nids alc882_capsrc_nids +#define alc262_capsrc_nids_alt alc882_capsrc_nids_alt #define alc262_modes alc260_modes #define alc262_capture_source alc882_capture_source @@ -8585,7 +8621,8 @@ static void alc262_hippo1_unsol_event(struct hda_codec *codec, /* * fujitsu model - * 0x14 = headphone/spdif-out, 0x15 = internal speaker + * 0x14 = headphone/spdif-out, 0x15 = internal speaker, + * 0x1b = port replicator headphone out */ #define ALC_HP_EVENT 0x37 @@ -8593,6 +8630,14 @@ static void alc262_hippo1_unsol_event(struct hda_codec *codec, static struct hda_verb alc262_fujitsu_unsol_verbs[] = { {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {} +}; + +static struct hda_verb alc262_lenovo_3000_unsol_verbs[] = { + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, {} }; @@ -8633,12 +8678,16 @@ static void alc262_fujitsu_automute(struct hda_codec *codec, int force) unsigned int mute; if (force || !spec->sense_updated) { - unsigned int present; + unsigned int present_int_hp, present_dock_hp; /* need to execute and sync at first */ snd_hda_codec_read(codec, 0x14, 0, AC_VERB_SET_PIN_SENSE, 0); - present = snd_hda_codec_read(codec, 0x14, 0, - AC_VERB_GET_PIN_SENSE, 0); - spec->jack_present = (present & 0x80000000) != 0; + present_int_hp = snd_hda_codec_read(codec, 0x14, 0, + AC_VERB_GET_PIN_SENSE, 0); + snd_hda_codec_read(codec, 0x1B, 0, AC_VERB_SET_PIN_SENSE, 0); + present_dock_hp = snd_hda_codec_read(codec, 0x1b, 0, + AC_VERB_GET_PIN_SENSE, 0); + spec->jack_present = (present_int_hp & 0x80000000) != 0; + spec->jack_present |= (present_dock_hp & 0x80000000) != 0; spec->sense_updated = 1; } if (spec->jack_present) { @@ -8672,6 +8721,46 @@ static struct hda_bind_ctls alc262_fujitsu_bind_master_vol = { }, }; +/* mute/unmute internal speaker according to the hp jack and mute state */ +static void alc262_lenovo_3000_automute(struct hda_codec *codec, int force) +{ + struct alc_spec *spec = codec->spec; + unsigned int mute; + + if (force || !spec->sense_updated) { + unsigned int present_int_hp; + /* need to execute and sync at first */ + snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0); + present_int_hp = snd_hda_codec_read(codec, 0x1b, 0, + AC_VERB_GET_PIN_SENSE, 0); + spec->jack_present = (present_int_hp & 0x80000000) != 0; + spec->sense_updated = 1; + } + if (spec->jack_present) { + /* mute internal speaker */ + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, HDA_AMP_MUTE); + snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, + HDA_AMP_MUTE, HDA_AMP_MUTE); + } else { + /* unmute internal speaker if necessary */ + mute = snd_hda_codec_amp_read(codec, 0x1b, 0, HDA_OUTPUT, 0); + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, mute); + snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, + HDA_AMP_MUTE, mute); + } +} + +/* unsolicited event for HP jack sensing */ +static void alc262_lenovo_3000_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) != ALC_HP_EVENT) + return; + alc262_lenovo_3000_automute(codec, 1); +} + /* bind hp and internal speaker mute (with plug check) */ static int alc262_fujitsu_master_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -8680,12 +8769,13 @@ static int alc262_fujitsu_master_sw_put(struct snd_kcontrol *kcontrol, long *valp = ucontrol->value.integer.value; int change; - change = snd_hda_codec_amp_update(codec, 0x14, 0, HDA_OUTPUT, 0, - HDA_AMP_MUTE, - valp[0] ? 0 : HDA_AMP_MUTE); - change |= snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0, - HDA_AMP_MUTE, - valp[1] ? 0 : HDA_AMP_MUTE); + change = snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, + valp ? 0 : HDA_AMP_MUTE); + change |= snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0, + HDA_AMP_MUTE, + valp ? 0 : HDA_AMP_MUTE); + if (change) alc262_fujitsu_automute(codec, 0); return change; @@ -8703,6 +8793,46 @@ static struct snd_kcontrol_new alc262_fujitsu_mixer[] = { }, HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("PC Speaker Volume", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_MUTE("PC Speaker Switch", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, 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("Int Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +/* bind hp and internal speaker mute (with plug check) */ +static int alc262_lenovo_3000_master_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + long *valp = ucontrol->value.integer.value; + int change; + + change = snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0, + HDA_AMP_MUTE, + valp ? 0 : HDA_AMP_MUTE); + + if (change) + alc262_lenovo_3000_automute(codec, 0); + return change; +} + +static struct snd_kcontrol_new alc262_lenovo_3000_mixer[] = { + HDA_BIND_VOL("Master Playback Volume", &alc262_fujitsu_bind_master_vol), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = snd_hda_mixer_amp_switch_info, + .get = snd_hda_mixer_amp_switch_get, + .put = alc262_lenovo_3000_master_sw_put, + .private_value = HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, 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("Mic Boost", 0x18, 0, HDA_INPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), @@ -8730,59 +8860,72 @@ static struct hda_verb alc262_benq_t31_EAPD_verbs[] = { /* Samsung Q1 Ultra Vista model setup */ static struct snd_kcontrol_new alc262_ultra_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, HDA_INPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), HDA_CODEC_VOLUME("Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Headphone Mic Boost", 0x15, 0, HDA_INPUT), { } /* end */ }; static struct hda_verb alc262_ultra_verbs[] = { - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, + /* output mixer */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + /* speaker */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* HP */ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - /* Mic is on Node 0x19 */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - {0x22, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - {0x23, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - {0x24, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, + /* internal mic */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* ADC, choose mic */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(8)}, {} }; -static struct hda_input_mux alc262_ultra_capture_source = { - .num_items = 1, - .items = { - { "Mic", 0x1 }, - }, -}; - /* mute/unmute internal speaker according to the hp jack and mute state */ static void alc262_ultra_automute(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; unsigned int mute; - unsigned int present; - /* need to execute and sync at first */ - snd_hda_codec_read(codec, 0x15, 0, AC_VERB_SET_PIN_SENSE, 0); - present = snd_hda_codec_read(codec, 0x15, 0, - AC_VERB_GET_PIN_SENSE, 0); - spec->jack_present = (present & 0x80000000) != 0; - if (spec->jack_present) { - /* mute internal speaker */ - snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, - HDA_AMP_MUTE, HDA_AMP_MUTE); - } else { - /* unmute internal speaker if necessary */ - mute = snd_hda_codec_amp_read(codec, 0x15, 0, HDA_OUTPUT, 0); - snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, - HDA_AMP_MUTE, mute); + mute = 0; + /* auto-mute only when HP is used as HP */ + if (!spec->cur_mux[0]) { + unsigned int present; + /* need to execute and sync at first */ + snd_hda_codec_read(codec, 0x15, 0, AC_VERB_SET_PIN_SENSE, 0); + present = snd_hda_codec_read(codec, 0x15, 0, + AC_VERB_GET_PIN_SENSE, 0); + spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0; + if (spec->jack_present) + mute = HDA_AMP_MUTE; } + /* mute/unmute internal speaker */ + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, mute); + /* mute/unmute HP */ + snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, + HDA_AMP_MUTE, mute ? 0 : HDA_AMP_MUTE); } /* unsolicited event for HP jack sensing */ @@ -8794,6 +8937,45 @@ static void alc262_ultra_unsol_event(struct hda_codec *codec, alc262_ultra_automute(codec); } +static struct hda_input_mux alc262_ultra_capture_source = { + .num_items = 2, + .items = { + { "Mic", 0x1 }, + { "Headphone", 0x7 }, + }, +}; + +static int alc262_ultra_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; + int ret; + + ret = alc882_mux_enum_put(kcontrol, ucontrol); + if (!ret) + return 0; + /* reprogram the HP pin as mic or HP according to the input source */ + snd_hda_codec_write_cache(codec, 0x15, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + spec->cur_mux[0] ? PIN_VREF80 : PIN_HP); + alc262_ultra_automute(codec); /* mute/unmute HP */ + return ret; +} + +static struct snd_kcontrol_new alc262_ultra_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = alc882_mux_enum_info, + .get = alc882_mux_enum_get, + .put = alc262_ultra_mux_enum_put, + }, + { } /* end */ +}; + /* add playback controls from the parsed DAC table */ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec, const struct auto_pin_cfg *cfg) @@ -9185,9 +9367,12 @@ static int alc262_parse_auto_config(struct hda_codec *codec) /* init callback for auto-configuration model -- overriding the default init */ static void alc262_auto_init(struct hda_codec *codec) { + struct alc_spec *spec = codec->spec; alc262_auto_init_multi_out(codec); alc262_auto_init_hp_out(codec); alc262_auto_init_analog_input(codec); + if (spec->unsol_event) + alc_sku_automute(codec); } /* @@ -9206,6 +9391,7 @@ static const char *alc262_models[ALC262_MODEL_LAST] = { [ALC262_BENQ_T31] = "benq-t31", [ALC262_SONY_ASSAMD] = "sony-assamd", [ALC262_ULTRA] = "ultra", + [ALC262_LENOVO_3000] = "lenovo-3000", [ALC262_AUTO] = "auto", }; @@ -9241,6 +9427,8 @@ static struct snd_pci_quirk alc262_cfg_tbl[] = { SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FUJITSU), SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FUJITSU), SND_PCI_QUIRK(0x144d, 0xc032, "Samsung Q1 Ultra", ALC262_ULTRA), + SND_PCI_QUIRK(0x144d, 0xc039, "Samsung Q1U EL", ALC262_ULTRA), + SND_PCI_QUIRK(0x17aa, 0x384e, "Lenovo 3000 y410", ALC262_LENOVO_3000), SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_BENQ_ED8), SND_PCI_QUIRK(0x17ff, 0x058d, "Benq T31-16", ALC262_BENQ_T31), SND_PCI_QUIRK(0x17ff, 0x058f, "Benq Hippo", ALC262_HIPPO_1), @@ -9390,18 +9578,32 @@ static struct alc_config_preset alc262_presets[] = { .init_hook = alc262_hippo_automute, }, [ALC262_ULTRA] = { - .mixers = { alc262_ultra_mixer }, - .init_verbs = { alc262_init_verbs, alc262_ultra_verbs }, + .mixers = { alc262_ultra_mixer, alc262_ultra_capture_mixer }, + .init_verbs = { alc262_ultra_verbs }, .num_dacs = ARRAY_SIZE(alc262_dac_nids), .dac_nids = alc262_dac_nids, - .hp_nid = 0x03, - .dig_out_nid = ALC262_DIGOUT_NID, .num_channel_mode = ARRAY_SIZE(alc262_modes), .channel_mode = alc262_modes, .input_mux = &alc262_ultra_capture_source, + .adc_nids = alc262_adc_nids, /* ADC0 */ + .capsrc_nids = alc262_capsrc_nids, + .num_adc_nids = 1, /* single ADC */ .unsol_event = alc262_ultra_unsol_event, .init_hook = alc262_ultra_automute, }, + [ALC262_LENOVO_3000] = { + .mixers = { alc262_lenovo_3000_mixer }, + .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs, + alc262_lenovo_3000_unsol_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .dig_out_nid = ALC262_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_fujitsu_capture_source, + .unsol_event = alc262_lenovo_3000_unsol_event, + }, }; static int patch_alc262(struct hda_codec *codec) @@ -9472,12 +9674,14 @@ static int patch_alc262(struct hda_codec *codec) if (wcap != AC_WID_AUD_IN) { spec->adc_nids = alc262_adc_nids_alt; spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids_alt); + spec->capsrc_nids = alc262_capsrc_nids_alt; spec->mixers[spec->num_mixers] = alc262_capture_alt_mixer; spec->num_mixers++; } else { spec->adc_nids = alc262_adc_nids; spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids); + spec->capsrc_nids = alc262_capsrc_nids; spec->mixers[spec->num_mixers] = alc262_capture_mixer; spec->num_mixers++; } @@ -9517,6 +9721,8 @@ static hda_nid_t alc268_adc_nids_alt[1] = { 0x08 }; +static hda_nid_t alc268_capsrc_nids[2] = { 0x23, 0x24 }; + static struct snd_kcontrol_new alc268_base_mixer[] = { /* output mixer control */ HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT), @@ -9529,6 +9735,22 @@ static struct snd_kcontrol_new alc268_base_mixer[] = { { } }; +/* bind Beep switches of both NID 0x0f and 0x10 */ +static struct hda_bind_ctls alc268_bind_beep_sw = { + .ops = &snd_hda_bind_sw, + .values = { + HDA_COMPOSE_AMP_VAL(0x0f, 3, 1, HDA_INPUT), + HDA_COMPOSE_AMP_VAL(0x10, 3, 1, HDA_INPUT), + 0 + }, +}; + +static struct snd_kcontrol_new alc268_beep_mixer[] = { + HDA_CODEC_VOLUME("Beep Playback Volume", 0x1d, 0x0, HDA_INPUT), + HDA_BIND_SW("Beep Playback Switch", &alc268_bind_beep_sw), + { } +}; + static struct hda_verb alc268_eapd_verbs[] = { {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, @@ -9613,8 +9835,12 @@ static struct snd_kcontrol_new alc268_acer_mixer[] = { }; static struct hda_verb alc268_acer_verbs[] = { + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* internal dmic? */ + {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, { } @@ -9685,6 +9911,64 @@ static void alc268_dell_unsol_event(struct hda_codec *codec, #define alc268_dell_init_hook alc268_dell_automute +static struct snd_kcontrol_new alc267_quanta_il1_mixer[] = { + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x2, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Capture Volume", 0x23, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Mic Capture Switch", 0x23, 2, HDA_OUTPUT), + HDA_CODEC_VOLUME("Ext Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT), + { } +}; + +static struct hda_verb alc267_quanta_il1_verbs[] = { + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, + { } +}; + +static void alc267_quanta_il1_hp_automute(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_PIN_SENSE, 0) + & AC_PINSENSE_PRESENCE; + snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + present ? 0 : PIN_OUT); +} + +static void alc267_quanta_il1_mic_automute(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_codec_read(codec, 0x18, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + snd_hda_codec_write(codec, 0x23, 0, + AC_VERB_SET_CONNECT_SEL, + present ? 0x00 : 0x01); +} + +static void alc267_quanta_il1_automute(struct hda_codec *codec) +{ + alc267_quanta_il1_hp_automute(codec); + alc267_quanta_il1_mic_automute(codec); +} + +static void alc267_quanta_il1_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + switch (res >> 26) { + case ALC880_HP_EVENT: + alc267_quanta_il1_hp_automute(codec); + break; + case ALC880_MIC_EVENT: + alc267_quanta_il1_mic_automute(codec); + break; + } +} + /* * generic initialization of ADC, input mixers and output mixers */ @@ -9725,7 +10009,11 @@ static struct hda_verb alc268_base_init_verbs[] = { {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + + /* set PCBEEP vol = 0, mute connections */ + {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* Unmute Selector 23h,24h and set the default input to mic-in */ @@ -9764,29 +10052,17 @@ static struct hda_verb alc268_volume_init_verbs[] = { {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* set PCBEEP vol = 0 */ - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, (0xb000 | (0x00 << 8))}, + /* set PCBEEP vol = 0, mute connections */ + {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, { } }; #define alc268_mux_enum_info alc_mux_enum_info #define alc268_mux_enum_get alc_mux_enum_get - -static int alc268_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); - static hda_nid_t capture_mixers[3] = { 0x23, 0x24 }; - hda_nid_t nid = capture_mixers[adc_idx]; - - return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, - nid, - &spec->cur_mux[adc_idx]); -} +#define alc268_mux_enum_put alc_mux_enum_put static struct snd_kcontrol_new alc268_capture_alt_mixer[] = { HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT), @@ -9836,13 +10112,17 @@ static struct hda_input_mux alc268_capture_source = { }, }; +static struct hda_input_mux alc268_acer_capture_source = { + .num_items = 3, + .items = { + { "Mic", 0x0 }, + { "Internal Mic", 0x6 }, + { "Line", 0x2 }, + }, +}; + #ifdef CONFIG_SND_DEBUG static struct snd_kcontrol_new alc268_test_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - /* Volume widgets */ HDA_CODEC_VOLUME("LOUT1 Playback Volume", 0x02, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("LOUT2 Playback Volume", 0x03, 0x0, HDA_OUTPUT), @@ -9981,6 +10261,10 @@ static int alc268_auto_create_analog_input_ctls(struct alc_spec *spec, case 0x1c: idx1 = 3; /* CD */ break; + case 0x12: + case 0x13: + idx1 = 6; /* digital mics */ + break; default: continue; } @@ -10073,6 +10357,9 @@ static int alc268_parse_auto_config(struct hda_codec *codec) if (spec->kctl_alloc) spec->mixers[spec->num_mixers++] = spec->kctl_alloc; + if (spec->autocfg.speaker_pins[0] != 0x1d) + spec->mixers[spec->num_mixers++] = alc268_beep_mixer; + spec->init_verbs[spec->num_init_verbs++] = alc268_volume_init_verbs; spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux; @@ -10091,20 +10378,25 @@ static int alc268_parse_auto_config(struct hda_codec *codec) /* init callback for auto-configuration model -- overriding the default init */ static void alc268_auto_init(struct hda_codec *codec) { + struct alc_spec *spec = codec->spec; alc268_auto_init_multi_out(codec); alc268_auto_init_hp_out(codec); alc268_auto_init_mono_speaker_out(codec); alc268_auto_init_analog_input(codec); + if (spec->unsol_event) + alc_sku_automute(codec); } /* * configuration and preset */ static const char *alc268_models[ALC268_MODEL_LAST] = { + [ALC267_QUANTA_IL1] = "quanta-il1", [ALC268_3ST] = "3stack", [ALC268_TOSHIBA] = "toshiba", [ALC268_ACER] = "acer", [ALC268_DELL] = "dell", + [ALC268_ZEPTO] = "zepto", #ifdef CONFIG_SND_DEBUG [ALC268_TEST] = "test", #endif @@ -10112,6 +10404,7 @@ static const char *alc268_models[ALC268_MODEL_LAST] = { }; static struct snd_pci_quirk alc268_cfg_tbl[] = { + SND_PCI_QUIRK(0x1025, 0x011e, "Acer Aspire 5720z", ALC268_ACER), SND_PCI_QUIRK(0x1025, 0x0126, "Acer", ALC268_ACER), SND_PCI_QUIRK(0x1025, 0x012e, "Acer Aspire 5310", ALC268_ACER), SND_PCI_QUIRK(0x1025, 0x0130, "Acer Extensa 5210", ALC268_ACER), @@ -10122,17 +10415,36 @@ static struct snd_pci_quirk alc268_cfg_tbl[] = { SND_PCI_QUIRK(0x1179, 0xff10, "TOSHIBA A205", ALC268_TOSHIBA), SND_PCI_QUIRK(0x1179, 0xff50, "TOSHIBA A305", ALC268_TOSHIBA), SND_PCI_QUIRK(0x152d, 0x0763, "Diverse (CPR2000)", ALC268_ACER), + SND_PCI_QUIRK(0x152d, 0x0771, "Quanta IL1", ALC267_QUANTA_IL1), + SND_PCI_QUIRK(0x1170, 0x0040, "ZEPTO", ALC268_ZEPTO), {} }; static struct alc_config_preset alc268_presets[] = { + [ALC267_QUANTA_IL1] = { + .mixers = { alc267_quanta_il1_mixer }, + .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, + alc267_quanta_il1_verbs }, + .num_dacs = ARRAY_SIZE(alc268_dac_nids), + .dac_nids = alc268_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), + .adc_nids = alc268_adc_nids_alt, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc268_modes), + .channel_mode = alc268_modes, + .input_mux = &alc268_capture_source, + .unsol_event = alc267_quanta_il1_unsol_event, + .init_hook = alc267_quanta_il1_automute, + }, [ALC268_3ST] = { - .mixers = { alc268_base_mixer, alc268_capture_alt_mixer }, + .mixers = { alc268_base_mixer, alc268_capture_alt_mixer, + alc268_beep_mixer }, .init_verbs = { alc268_base_init_verbs }, .num_dacs = ARRAY_SIZE(alc268_dac_nids), .dac_nids = alc268_dac_nids, .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), .adc_nids = alc268_adc_nids_alt, + .capsrc_nids = alc268_capsrc_nids, .hp_nid = 0x03, .dig_out_nid = ALC268_DIGOUT_NID, .num_channel_mode = ARRAY_SIZE(alc268_modes), @@ -10140,13 +10452,15 @@ static struct alc_config_preset alc268_presets[] = { .input_mux = &alc268_capture_source, }, [ALC268_TOSHIBA] = { - .mixers = { alc268_base_mixer, alc268_capture_alt_mixer }, + .mixers = { alc268_base_mixer, alc268_capture_alt_mixer, + alc268_beep_mixer }, .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, alc268_toshiba_verbs }, .num_dacs = ARRAY_SIZE(alc268_dac_nids), .dac_nids = alc268_dac_nids, .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), .adc_nids = alc268_adc_nids_alt, + .capsrc_nids = alc268_capsrc_nids, .hp_nid = 0x03, .num_channel_mode = ARRAY_SIZE(alc268_modes), .channel_mode = alc268_modes, @@ -10155,22 +10469,24 @@ static struct alc_config_preset alc268_presets[] = { .init_hook = alc268_toshiba_automute, }, [ALC268_ACER] = { - .mixers = { alc268_acer_mixer, alc268_capture_alt_mixer }, + .mixers = { alc268_acer_mixer, alc268_capture_alt_mixer, + alc268_beep_mixer }, .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, alc268_acer_verbs }, .num_dacs = ARRAY_SIZE(alc268_dac_nids), .dac_nids = alc268_dac_nids, .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), .adc_nids = alc268_adc_nids_alt, + .capsrc_nids = alc268_capsrc_nids, .hp_nid = 0x02, .num_channel_mode = ARRAY_SIZE(alc268_modes), .channel_mode = alc268_modes, - .input_mux = &alc268_capture_source, + .input_mux = &alc268_acer_capture_source, .unsol_event = alc268_acer_unsol_event, .init_hook = alc268_acer_init_hook, }, [ALC268_DELL] = { - .mixers = { alc268_dell_mixer }, + .mixers = { alc268_dell_mixer, alc268_beep_mixer }, .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, alc268_dell_verbs }, .num_dacs = ARRAY_SIZE(alc268_dac_nids), @@ -10182,6 +10498,24 @@ static struct alc_config_preset alc268_presets[] = { .init_hook = alc268_dell_init_hook, .input_mux = &alc268_capture_source, }, + [ALC268_ZEPTO] = { + .mixers = { alc268_base_mixer, alc268_capture_alt_mixer, + alc268_beep_mixer }, + .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, + alc268_toshiba_verbs }, + .num_dacs = ARRAY_SIZE(alc268_dac_nids), + .dac_nids = alc268_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), + .adc_nids = alc268_adc_nids_alt, + .capsrc_nids = alc268_capsrc_nids, + .hp_nid = 0x03, + .dig_out_nid = ALC268_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc268_modes), + .channel_mode = alc268_modes, + .input_mux = &alc268_capture_source, + .unsol_event = alc268_toshiba_unsol_event, + .init_hook = alc268_toshiba_automute + }, #ifdef CONFIG_SND_DEBUG [ALC268_TEST] = { .mixers = { alc268_test_mixer, alc268_capture_mixer }, @@ -10191,6 +10525,7 @@ static struct alc_config_preset alc268_presets[] = { .dac_nids = alc268_dac_nids, .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), .adc_nids = alc268_adc_nids_alt, + .capsrc_nids = alc268_capsrc_nids, .hp_nid = 0x03, .dig_out_nid = ALC268_DIGOUT_NID, .num_channel_mode = ARRAY_SIZE(alc268_modes), @@ -10247,13 +10582,22 @@ static int patch_alc268(struct hda_codec *codec) spec->stream_name_digital = "ALC268 Digital"; spec->stream_digital_playback = &alc268_pcm_digital_playback; + if (!query_amp_caps(codec, 0x1d, HDA_INPUT)) + /* override the amp caps for beep generator */ + snd_hda_override_amp_caps(codec, 0x1d, HDA_INPUT, + (0x0c << AC_AMPCAP_OFFSET_SHIFT) | + (0x0c << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x07 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (0 << AC_AMPCAP_MUTE_SHIFT)); + if (!spec->adc_nids && spec->input_mux) { /* check whether NID 0x07 is valid */ unsigned int wcap = get_wcaps(codec, 0x07); + int i; /* get type */ wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; - if (wcap != AC_WID_AUD_IN) { + if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) { spec->adc_nids = alc268_adc_nids_alt; spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt); spec->mixers[spec->num_mixers] = @@ -10266,6 +10610,12 @@ static int patch_alc268(struct hda_codec *codec) alc268_capture_mixer; spec->num_mixers++; } + spec->capsrc_nids = alc268_capsrc_nids; + /* set default input source */ + for (i = 0; i < spec->num_adc_nids; i++) + snd_hda_codec_write_cache(codec, alc268_capsrc_nids[i], + 0, AC_VERB_SET_CONNECT_SEL, + spec->input_mux->items[0].index); } spec->vmaster_nid = 0x02; @@ -10539,9 +10889,12 @@ static int alc269_parse_auto_config(struct hda_codec *codec) /* init callback for auto-configuration model -- overriding the default init */ static void alc269_auto_init(struct hda_codec *codec) { + struct alc_spec *spec = codec->spec; alc269_auto_init_multi_out(codec); alc269_auto_init_hp_out(codec); alc269_auto_init_analog_input(codec); + if (spec->unsol_event) + alc_sku_automute(codec); } /* @@ -11463,13 +11816,7 @@ static void alc861_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t nid, int pin_type, int dac_idx) { - /* set as output */ - - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pin_type); - snd_hda_codec_write(codec, dac_idx, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); - + alc_set_pin_output(codec, nid, pin_type); } static void alc861_auto_init_multi_out(struct hda_codec *codec) @@ -11496,6 +11843,9 @@ static void alc861_auto_init_hp_out(struct hda_codec *codec) if (pin) /* connect to front */ alc861_auto_set_output_and_unmute(codec, pin, PIN_HP, spec->multiout.dac_nids[0]); + pin = spec->autocfg.speaker_pins[0]; + if (pin) + alc861_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); } static void alc861_auto_init_analog_input(struct hda_codec *codec) @@ -11568,9 +11918,12 @@ static int alc861_parse_auto_config(struct hda_codec *codec) /* additional initialization for auto-configuration model */ static void alc861_auto_init(struct hda_codec *codec) { + struct alc_spec *spec = codec->spec; alc861_auto_init_multi_out(codec); alc861_auto_init_hp_out(codec); alc861_auto_init_analog_input(codec); + if (spec->unsol_event) + alc_sku_automute(codec); } #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -11822,6 +12175,8 @@ static hda_nid_t alc861vd_adc_nids[1] = { 0x09, }; +static hda_nid_t alc861vd_capsrc_nids[1] = { 0x22 }; + /* input MUX */ /* FIXME: should be a matrix-type input source selection */ static struct hda_input_mux alc861vd_capture_source = { @@ -11835,11 +12190,10 @@ static struct hda_input_mux alc861vd_capture_source = { }; static struct hda_input_mux alc861vd_dallas_capture_source = { - .num_items = 3, + .num_items = 2, .items = { - { "Front Mic", 0x0 }, - { "ATAPI Mic", 0x1 }, - { "Line In", 0x5 }, + { "Ext Mic", 0x0 }, + { "Int Mic", 0x1 }, }, }; @@ -11853,33 +12207,8 @@ static struct hda_input_mux alc861vd_hp_capture_source = { #define alc861vd_mux_enum_info alc_mux_enum_info #define alc861vd_mux_enum_get alc_mux_enum_get - -static int alc861vd_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[1] = { 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) - return 0; - for (i = 0; i < imux->num_items; i++) { - unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE; - snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, - imux->items[i].index, - HDA_AMP_MUTE, v); - } - *cur_val = idx; - return 1; -} +/* ALC861VD has the ALC882-type input selection (but has only one ADC) */ +#define alc861vd_mux_enum_put alc882_mux_enum_put /* * 2ch mode @@ -12034,20 +12363,22 @@ static struct snd_kcontrol_new alc861vd_lenovo_mixer[] = { { } /* end */ }; -/* Pin assignment: Front=0x14, HP = 0x15, - * Front Mic=0x18, ATAPI Mic = 0x19, Line In = 0x1d +/* Pin assignment: Speaker=0x14, HP = 0x15, + * Ext Mic=0x18, Int Mic = 0x19, CD = 0x1c, PC Beep = 0x1d */ static struct snd_kcontrol_new alc861vd_dallas_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x0c, 2, HDA_INPUT), HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), HDA_BIND_MUTE("Headphone Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x05, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_VOLUME("Ext Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Ext Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Ext Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("PC Beep Volume", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_MUTE("PC Beep Switch", 0x0b, 0x05, HDA_INPUT), { } /* end */ }; @@ -12348,6 +12679,7 @@ static struct snd_pci_quirk alc861vd_cfg_tbl[] = { /*SND_PCI_QUIRK(0x1179, 0xff00, "DALLAS", ALC861VD_DALLAS),*/ /*lenovo*/ SND_PCI_QUIRK(0x1179, 0xff01, "DALLAS", ALC861VD_DALLAS), SND_PCI_QUIRK(0x1179, 0xff03, "Toshiba P205", ALC861VD_LENOVO), + SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba L30-149", ALC861VD_DALLAS), SND_PCI_QUIRK(0x1565, 0x820d, "Biostar NF61S SE", ALC861VD_6ST_DIG), SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo", ALC861VD_LENOVO), SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo 3000 C200", ALC861VD_LENOVO), @@ -12362,8 +12694,6 @@ static struct alc_config_preset alc861vd_presets[] = { alc861vd_3stack_init_verbs }, .num_dacs = ARRAY_SIZE(alc660vd_dac_nids), .dac_nids = alc660vd_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids), - .adc_nids = alc861vd_adc_nids, .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), .channel_mode = alc861vd_3stack_2ch_modes, .input_mux = &alc861vd_capture_source, @@ -12375,8 +12705,6 @@ static struct alc_config_preset alc861vd_presets[] = { .num_dacs = ARRAY_SIZE(alc660vd_dac_nids), .dac_nids = alc660vd_dac_nids, .dig_out_nid = ALC861VD_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids), - .adc_nids = alc861vd_adc_nids, .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), .channel_mode = alc861vd_3stack_2ch_modes, .input_mux = &alc861vd_capture_source, @@ -12421,8 +12749,6 @@ static struct alc_config_preset alc861vd_presets[] = { alc861vd_lenovo_unsol_verbs }, .num_dacs = ARRAY_SIZE(alc660vd_dac_nids), .dac_nids = alc660vd_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids), - .adc_nids = alc861vd_adc_nids, .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), .channel_mode = alc861vd_3stack_2ch_modes, .input_mux = &alc861vd_capture_source, @@ -12434,8 +12760,6 @@ static struct alc_config_preset alc861vd_presets[] = { .init_verbs = { alc861vd_dallas_verbs }, .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), .dac_nids = alc861vd_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids), - .adc_nids = alc861vd_adc_nids, .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), .channel_mode = alc861vd_3stack_2ch_modes, .input_mux = &alc861vd_dallas_capture_source, @@ -12447,9 +12771,7 @@ static struct alc_config_preset alc861vd_presets[] = { .init_verbs = { alc861vd_dallas_verbs, alc861vd_eapd_verbs }, .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), .dac_nids = alc861vd_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids), .dig_out_nid = ALC861VD_DIGOUT_NID, - .adc_nids = alc861vd_adc_nids, .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), .channel_mode = alc861vd_3stack_2ch_modes, .input_mux = &alc861vd_hp_capture_source, @@ -12464,11 +12786,7 @@ static struct alc_config_preset alc861vd_presets[] = { static void alc861vd_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t nid, int pin_type, int dac_idx) { - /* set as output */ - 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); + alc_set_pin_output(codec, nid, pin_type); } static void alc861vd_auto_init_multi_out(struct hda_codec *codec) @@ -12495,6 +12813,9 @@ static void alc861vd_auto_init_hp_out(struct hda_codec *codec) pin = spec->autocfg.hp_pins[0]; if (pin) /* connect to front and use dac 0 */ alc861vd_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); + pin = spec->autocfg.speaker_pins[0]; + if (pin) + alc861vd_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); } #define alc861vd_is_input_pin(nid) alc880_is_input_pin(nid) @@ -12698,9 +13019,12 @@ static int alc861vd_parse_auto_config(struct hda_codec *codec) /* additional initialization for auto-configuration model */ static void alc861vd_auto_init(struct hda_codec *codec) { + struct alc_spec *spec = codec->spec; alc861vd_auto_init_multi_out(codec); alc861vd_auto_init_hp_out(codec); alc861vd_auto_init_analog_input(codec); + if (spec->unsol_event) + alc_sku_automute(codec); } static int patch_alc861vd(struct hda_codec *codec) @@ -12751,6 +13075,7 @@ static int patch_alc861vd(struct hda_codec *codec) spec->adc_nids = alc861vd_adc_nids; spec->num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids); + spec->capsrc_nids = alc861vd_capsrc_nids; spec->mixers[spec->num_mixers] = alc861vd_capture_mixer; spec->num_mixers++; @@ -12792,9 +13117,11 @@ static hda_nid_t alc662_adc_nids[1] = { /* ADC1-2 */ 0x09, }; + +static hda_nid_t alc662_capsrc_nids[1] = { 0x22 }; + /* input MUX */ /* FIXME: should be a matrix-type input source selection */ - static struct hda_input_mux alc662_capture_source = { .num_items = 4, .items = { @@ -12823,33 +13150,8 @@ static struct hda_input_mux alc662_eeepc_capture_source = { #define alc662_mux_enum_info alc_mux_enum_info #define alc662_mux_enum_get alc_mux_enum_get +#define alc662_mux_enum_put alc882_mux_enum_put -static int alc662_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[2] = { 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) - return 0; - for (i = 0; i < imux->num_items; i++) { - unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE; - snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, - imux->items[i].index, - HDA_AMP_MUTE, v); - } - *cur_val = idx; - return 1; -} /* * 2ch mode */ @@ -12918,13 +13220,13 @@ static struct hda_channel_mode alc662_5stack_modes[2] = { static struct snd_kcontrol_new alc662_base_mixer[] = { /* output mixer control */ HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x0c, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Surround Playback Volume", 0x3, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Surround Playback Switch", 0x03, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Surround Playback Switch", 0x0d, 0x0, HDA_INPUT), HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x04, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x04, 2, 2, HDA_INPUT), + HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x0e, 1, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 0x0, HDA_INPUT), HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), /*Input mixer control */ @@ -12941,7 +13243,7 @@ static struct snd_kcontrol_new alc662_base_mixer[] = { static struct snd_kcontrol_new alc662_3ST_2ch_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x02, 2, HDA_INPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x0c, 0x0, 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), @@ -12958,13 +13260,13 @@ static struct snd_kcontrol_new alc662_3ST_2ch_mixer[] = { static struct snd_kcontrol_new alc662_3ST_6ch_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x02, 2, HDA_INPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x0c, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x03, 2, HDA_INPUT), + HDA_CODEC_MUTE("Surround Playback Switch", 0x0d, 0x0, HDA_INPUT), HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x04, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x04, 2, 2, HDA_INPUT), + HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x0e, 1, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 0x0, 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), @@ -13313,6 +13615,7 @@ static const char *alc662_models[ALC662_MODEL_LAST] = { }; static struct snd_pci_quirk alc662_cfg_tbl[] = { + SND_PCI_QUIRK(0x1043, 0x8290, "ASUS P5GC-MX", ALC662_3ST_6ch_DIG), SND_PCI_QUIRK(0x1043, 0x82a1, "ASUS Eeepc", ALC662_ASUS_EEEPC_P701), SND_PCI_QUIRK(0x1043, 0x82d1, "ASUS Eeepc EP20", ALC662_ASUS_EEEPC_EP20), SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo", ALC662_LENOVO_101E), @@ -13326,8 +13629,6 @@ static struct alc_config_preset alc662_presets[] = { .num_dacs = ARRAY_SIZE(alc662_dac_nids), .dac_nids = alc662_dac_nids, .dig_out_nid = ALC662_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc662_adc_nids), - .adc_nids = alc662_adc_nids, .dig_in_nid = ALC662_DIGIN_NID, .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), .channel_mode = alc662_3ST_2ch_modes, @@ -13340,8 +13641,6 @@ static struct alc_config_preset alc662_presets[] = { .num_dacs = ARRAY_SIZE(alc662_dac_nids), .dac_nids = alc662_dac_nids, .dig_out_nid = ALC662_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc662_adc_nids), - .adc_nids = alc662_adc_nids, .dig_in_nid = ALC662_DIGIN_NID, .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes), .channel_mode = alc662_3ST_6ch_modes, @@ -13354,8 +13653,6 @@ static struct alc_config_preset alc662_presets[] = { .init_verbs = { alc662_init_verbs }, .num_dacs = ARRAY_SIZE(alc662_dac_nids), .dac_nids = alc662_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc662_adc_nids), - .adc_nids = alc662_adc_nids, .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes), .channel_mode = alc662_3ST_6ch_modes, .need_dac_fix = 1, @@ -13368,8 +13665,6 @@ static struct alc_config_preset alc662_presets[] = { .num_dacs = ARRAY_SIZE(alc662_dac_nids), .dac_nids = alc662_dac_nids, .dig_out_nid = ALC662_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc662_adc_nids), - .adc_nids = alc662_adc_nids, .dig_in_nid = ALC662_DIGIN_NID, .num_channel_mode = ARRAY_SIZE(alc662_5stack_modes), .channel_mode = alc662_5stack_modes, @@ -13380,8 +13675,6 @@ static struct alc_config_preset alc662_presets[] = { .init_verbs = { alc662_init_verbs, alc662_sue_init_verbs }, .num_dacs = ARRAY_SIZE(alc662_dac_nids), .dac_nids = alc662_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc662_adc_nids), - .adc_nids = alc662_adc_nids, .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), .channel_mode = alc662_3ST_2ch_modes, .input_mux = &alc662_lenovo_101e_capture_source, @@ -13394,8 +13687,6 @@ static struct alc_config_preset alc662_presets[] = { alc662_eeepc_sue_init_verbs }, .num_dacs = ARRAY_SIZE(alc662_dac_nids), .dac_nids = alc662_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids), - .adc_nids = alc662_adc_nids, .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), .channel_mode = alc662_3ST_2ch_modes, .input_mux = &alc662_eeepc_capture_source, @@ -13409,8 +13700,6 @@ static struct alc_config_preset alc662_presets[] = { alc662_eeepc_ep20_sue_init_verbs }, .num_dacs = ARRAY_SIZE(alc662_dac_nids), .dac_nids = alc662_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc662_adc_nids), - .adc_nids = alc662_adc_nids, .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes), .channel_mode = alc662_3ST_6ch_modes, .input_mux = &alc662_lenovo_101e_capture_source, @@ -13556,11 +13845,7 @@ static void alc662_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t nid, int pin_type, int dac_idx) { - /* set as output */ - 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); + alc_set_pin_output(codec, nid, pin_type); /* need the manual connection? */ if (alc880_is_multi_pin(nid)) { struct alc_spec *spec = codec->spec; @@ -13595,6 +13880,9 @@ static void alc662_auto_init_hp_out(struct hda_codec *codec) if (pin) /* connect to front */ /* use dac 0 */ alc662_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); + pin = spec->autocfg.speaker_pins[0]; + if (pin) + alc662_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); } #define alc662_is_input_pin(nid) alc880_is_input_pin(nid) @@ -13672,9 +13960,12 @@ static int alc662_parse_auto_config(struct hda_codec *codec) /* additional initialization for auto-configuration model */ static void alc662_auto_init(struct hda_codec *codec) { + struct alc_spec *spec = codec->spec; alc662_auto_init_multi_out(codec); alc662_auto_init_hp_out(codec); alc662_auto_init_analog_input(codec); + if (spec->unsol_event) + alc_sku_automute(codec); } static int patch_alc662(struct hda_codec *codec) @@ -13722,10 +14013,9 @@ static int patch_alc662(struct hda_codec *codec) spec->stream_digital_playback = &alc662_pcm_digital_playback; spec->stream_digital_capture = &alc662_pcm_digital_capture; - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = alc662_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(alc662_adc_nids); - } + spec->adc_nids = alc662_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(alc662_adc_nids); + spec->capsrc_nids = alc662_capsrc_nids; spec->vmaster_nid = 0x02; @@ -13761,6 +14051,8 @@ struct hda_codec_preset snd_hda_preset_realtek[] = { { .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 }, { .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 }, { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc883 }, + { .id = 0x10ec0885, .rev = 0x100103, .name = "ALC889A", + .patch = patch_alc882 }, /* should be patch_alc883() in future */ { .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 }, { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc883 }, { .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc883 }, diff --git a/sound/pci/hda/patch_si3054.c b/sound/pci/hda/patch_si3054.c index d22f5a6b850..9332b63e406 100644 --- a/sound/pci/hda/patch_si3054.c +++ b/sound/pci/hda/patch_si3054.c @@ -28,7 +28,7 @@ #include <sound/core.h> #include "hda_codec.h" #include "hda_local.h" - +#include "hda_patch.h" /* si3054 verbs */ #define SI3054_VERB_READ_NODE 0x900 @@ -206,7 +206,7 @@ static int si3054_build_pcms(struct hda_codec *codec) info->name = "Si3054 Modem"; info->stream[SNDRV_PCM_STREAM_PLAYBACK] = si3054_pcm; info->stream[SNDRV_PCM_STREAM_CAPTURE] = si3054_pcm; - info->is_modem = 1; + info->pcm_type = HDA_PCM_TYPE_MODEM; return 0; } diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index caf48edaa92..b3a15d61687 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -32,6 +32,7 @@ #include <sound/asoundef.h> #include "hda_codec.h" #include "hda_local.h" +#include "hda_patch.h" #define NUM_CONTROL_ALLOC 32 #define STAC_PWR_EVENT 0x20 @@ -39,6 +40,7 @@ enum { STAC_REF, + STAC_9200_OQO, STAC_9200_DELL_D21, STAC_9200_DELL_D22, STAC_9200_DELL_D23, @@ -50,6 +52,7 @@ enum { STAC_9200_DELL_M26, STAC_9200_DELL_M27, STAC_9200_GATEWAY, + STAC_9200_PANASONIC, STAC_9200_MODELS }; @@ -63,11 +66,14 @@ enum { enum { STAC_92HD73XX_REF, + STAC_DELL_M6, STAC_92HD73XX_MODELS }; enum { STAC_92HD71BXX_REF, + STAC_DELL_M4_1, + STAC_DELL_M4_2, STAC_92HD71BXX_MODELS }; @@ -123,6 +129,7 @@ struct sigmatel_spec { unsigned int hp_detect: 1; /* gpio lines */ + unsigned int eapd_mask; unsigned int gpio_mask; unsigned int gpio_dir; unsigned int gpio_data; @@ -135,6 +142,7 @@ struct sigmatel_spec { /* power management */ unsigned int num_pwrs; hda_nid_t *pwr_nids; + hda_nid_t *dac_list; /* playback */ struct hda_input_mux *mono_mux; @@ -173,6 +181,7 @@ struct sigmatel_spec { /* i/o switches */ unsigned int io_switch[2]; unsigned int clfe_swap; + unsigned int hp_switch; unsigned int aloopback; struct hda_pcm pcm_rec[2]; /* PCM information */ @@ -184,9 +193,6 @@ struct sigmatel_spec { struct hda_input_mux private_dimux; struct hda_input_mux private_imux; struct hda_input_mux private_mono_mux; - - /* virtual master */ - unsigned int vmaster_tlv[4]; }; static hda_nid_t stac9200_adc_nids[1] = { @@ -244,7 +250,7 @@ static hda_nid_t stac92hd71bxx_dmux_nids[1] = { 0x1c, }; -static hda_nid_t stac92hd71bxx_dac_nids[2] = { +static hda_nid_t stac92hd71bxx_dac_nids[1] = { 0x10, /*0x11, */ }; @@ -290,6 +296,10 @@ static hda_nid_t stac927x_mux_nids[3] = { 0x15, 0x16, 0x17 }; +static hda_nid_t stac927x_dac_nids[6] = { + 0x02, 0x03, 0x04, 0x05, 0x06, 0 +}; + static hda_nid_t stac927x_dmux_nids[1] = { 0x1b, }; @@ -331,10 +341,10 @@ static hda_nid_t stac922x_pin_nids[10] = { 0x0f, 0x10, 0x11, 0x15, 0x1b, }; -static hda_nid_t stac92hd73xx_pin_nids[12] = { +static hda_nid_t stac92hd73xx_pin_nids[13] = { 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, - 0x14, 0x22 + 0x14, 0x1e, 0x22 }; static hda_nid_t stac92hd71bxx_pin_nids[10] = { @@ -527,6 +537,43 @@ static struct hda_verb stac92hd73xx_6ch_core_init[] = { {} }; +static struct hda_verb dell_eq_core_init[] = { + /* set master volume to max value without distortion + * and direct control */ + { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xec}, + /* setup audio connections */ + { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00}, + { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x02}, + /* setup adcs to point to mixer */ + { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b}, + { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b}, + /* setup import muxs */ + { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x00}, + {} +}; + +static struct hda_verb dell_m6_core_init[] = { + /* set master volume and direct control */ + { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, + /* setup audio connections */ + { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00}, + { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x02}, + /* setup adcs to point to mixer */ + { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b}, + { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b}, + /* setup import muxs */ + { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x00}, + {} +}; + static struct hda_verb stac92hd73xx_8ch_core_init[] = { /* set master volume and direct control */ { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, @@ -910,6 +957,11 @@ static int stac92xx_build_controls(struct hda_codec *codec) err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); if (err < 0) return err; + err = snd_hda_create_spdif_share_sw(codec, + &spec->multiout); + if (err < 0) + return err; + spec->multiout.share_spdif = 1; } if (spec->dig_in_nid) { err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); @@ -919,10 +971,11 @@ static int stac92xx_build_controls(struct hda_codec *codec) /* if we have no master control, let's create it */ if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { + unsigned int vmaster_tlv[4]; snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0], - HDA_OUTPUT, spec->vmaster_tlv); + HDA_OUTPUT, vmaster_tlv); err = snd_hda_add_vmaster(codec, "Master Playback Volume", - spec->vmaster_tlv, slave_vols); + vmaster_tlv, slave_vols); if (err < 0) return err; } @@ -1052,9 +1105,15 @@ static unsigned int dell9200_m27_pin_configs[8] = { 0x90170310, 0x04a11020, 0x90170310, 0x40f003fc, }; +static unsigned int oqo9200_pin_configs[8] = { + 0x40c000f0, 0x404000f1, 0x0221121f, 0x02211210, + 0x90170111, 0x90a70120, 0x400000f2, 0x400000f3, +}; + static unsigned int *stac9200_brd_tbl[STAC_9200_MODELS] = { [STAC_REF] = ref9200_pin_configs, + [STAC_9200_OQO] = oqo9200_pin_configs, [STAC_9200_DELL_D21] = dell9200_d21_pin_configs, [STAC_9200_DELL_D22] = dell9200_d22_pin_configs, [STAC_9200_DELL_D23] = dell9200_d23_pin_configs, @@ -1065,10 +1124,12 @@ static unsigned int *stac9200_brd_tbl[STAC_9200_MODELS] = { [STAC_9200_DELL_M25] = dell9200_m25_pin_configs, [STAC_9200_DELL_M26] = dell9200_m26_pin_configs, [STAC_9200_DELL_M27] = dell9200_m27_pin_configs, + [STAC_9200_PANASONIC] = ref9200_pin_configs, }; static const char *stac9200_models[STAC_9200_MODELS] = { [STAC_REF] = "ref", + [STAC_9200_OQO] = "oqo", [STAC_9200_DELL_D21] = "dell-d21", [STAC_9200_DELL_D22] = "dell-d22", [STAC_9200_DELL_D23] = "dell-d23", @@ -1080,6 +1141,7 @@ static const char *stac9200_models[STAC_9200_MODELS] = { [STAC_9200_DELL_M26] = "dell-m26", [STAC_9200_DELL_M27] = "dell-m27", [STAC_9200_GATEWAY] = "gateway", + [STAC_9200_PANASONIC] = "panasonic", }; static struct snd_pci_quirk stac9200_cfg_tbl[] = { @@ -1146,13 +1208,15 @@ static struct snd_pci_quirk stac9200_cfg_tbl[] = { SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f6, "unknown Dell", STAC_9200_DELL_M26), /* Panasonic */ - SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-74", STAC_REF), + SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-74", STAC_9200_PANASONIC), /* Gateway machines needs EAPD to be set on resume */ SND_PCI_QUIRK(0x107b, 0x0205, "Gateway S-7110M", STAC_9200_GATEWAY), SND_PCI_QUIRK(0x107b, 0x0317, "Gateway MT3423, MX341*", STAC_9200_GATEWAY), SND_PCI_QUIRK(0x107b, 0x0318, "Gateway ML3019, MT3707", STAC_9200_GATEWAY), + /* OQO Mobile */ + SND_PCI_QUIRK(0x1106, 0x3288, "OQO Model 2", STAC_9200_OQO), {} /* terminator */ }; @@ -1202,24 +1266,48 @@ static struct snd_pci_quirk stac925x_cfg_tbl[] = { {} /* terminator */ }; -static unsigned int ref92hd73xx_pin_configs[12] = { +static unsigned int ref92hd73xx_pin_configs[13] = { 0x02214030, 0x02a19040, 0x01a19020, 0x02214030, 0x0181302e, 0x01014010, 0x01014020, 0x01014030, 0x02319040, 0x90a000f0, 0x90a000f0, 0x01452050, + 0x01452050, +}; + +static unsigned int dell_m6_pin_configs[13] = { + 0x0321101f, 0x4f00000f, 0x4f0000f0, 0x90170110, + 0x03a11020, 0x0321101f, 0x4f0000f0, 0x4f0000f0, + 0x4f0000f0, 0x90a60160, 0x4f0000f0, 0x4f0000f0, + 0x4f0000f0, }; static unsigned int *stac92hd73xx_brd_tbl[STAC_92HD73XX_MODELS] = { - [STAC_92HD73XX_REF] = ref92hd73xx_pin_configs, + [STAC_92HD73XX_REF] = ref92hd73xx_pin_configs, + [STAC_DELL_M6] = dell_m6_pin_configs, }; static const char *stac92hd73xx_models[STAC_92HD73XX_MODELS] = { [STAC_92HD73XX_REF] = "ref", + [STAC_DELL_M6] = "dell-m6", }; static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, - "DFI LanParty", STAC_92HD73XX_REF), + "DFI LanParty", STAC_92HD73XX_REF), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0254, + "unknown Dell", STAC_DELL_M6), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0255, + "unknown Dell", STAC_DELL_M6), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0256, + "unknown Dell", STAC_DELL_M6), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0257, + "unknown Dell", STAC_DELL_M6), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x025e, + "unknown Dell", STAC_DELL_M6), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x025f, + "unknown Dell", STAC_DELL_M6), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0271, + "unknown Dell", STAC_DELL_M6), {} /* terminator */ }; @@ -1229,18 +1317,56 @@ static unsigned int ref92hd71bxx_pin_configs[10] = { 0x90a000f0, 0x01452050, }; +static unsigned int dell_m4_1_pin_configs[13] = { + 0x0421101f, 0x04a11221, 0x40f000f0, 0x90170110, + 0x23a1902e, 0x23014250, 0x40f000f0, 0x90a000f0, + 0x40f000f0, 0x4f0000f0, +}; + +static unsigned int dell_m4_2_pin_configs[13] = { + 0x0421101f, 0x04a11221, 0x90a70330, 0x90170110, + 0x23a1902e, 0x23014250, 0x40f000f0, 0x40f000f0, + 0x40f000f0, 0x044413b0, +}; + static unsigned int *stac92hd71bxx_brd_tbl[STAC_92HD71BXX_MODELS] = { [STAC_92HD71BXX_REF] = ref92hd71bxx_pin_configs, + [STAC_DELL_M4_1] = dell_m4_1_pin_configs, + [STAC_DELL_M4_2] = dell_m4_2_pin_configs, }; static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = { [STAC_92HD71BXX_REF] = "ref", + [STAC_DELL_M4_1] = "dell-m4-1", + [STAC_DELL_M4_2] = "dell-m4-2", }; static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_92HD71BXX_REF), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233, + "unknown Dell", STAC_DELL_M4_1), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0234, + "unknown Dell", STAC_DELL_M4_1), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0250, + "unknown Dell", STAC_DELL_M4_1), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x024f, + "unknown Dell", STAC_DELL_M4_1), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x024d, + "unknown Dell", STAC_DELL_M4_1), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0251, + "unknown Dell", STAC_DELL_M4_1), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0277, + "unknown Dell", STAC_DELL_M4_1), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0263, + "unknown Dell", STAC_DELL_M4_2), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0265, + "unknown Dell", STAC_DELL_M4_2), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0262, + "unknown Dell", STAC_DELL_M4_2), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0264, + "unknown Dell", STAC_DELL_M4_2), {} /* terminator */ }; @@ -1733,7 +1859,8 @@ static int stac92xx_playback_pcm_open(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct sigmatel_spec *spec = codec->spec; - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream); + return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, + hinfo); } static int stac92xx_playback_pcm_prepare(struct hda_pcm_stream *hinfo, @@ -1807,7 +1934,7 @@ static int stac92xx_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, { struct sigmatel_spec *spec = codec->spec; - snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 0, 0, 0); + snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); return 0; } @@ -1889,6 +2016,7 @@ static int stac92xx_build_pcms(struct hda_codec *codec) codec->num_pcms++; info++; info->name = "STAC92xx Digital"; + info->pcm_type = HDA_PCM_TYPE_SPDIF; if (spec->multiout.dig_out_nid) { info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_digital_playback; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid; @@ -1925,6 +2053,34 @@ static void stac92xx_auto_set_pinctl(struct hda_codec *codec, hda_nid_t nid, int AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type); } +#define stac92xx_hp_switch_info snd_ctl_boolean_mono_info + +static int stac92xx_hp_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + + ucontrol->value.integer.value[0] = spec->hp_switch; + return 0; +} + +static int stac92xx_hp_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + + spec->hp_switch = ucontrol->value.integer.value[0]; + + /* check to be sure that the ports are upto date with + * switch changes + */ + codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26); + + return 1; +} + #define stac92xx_io_switch_info snd_ctl_boolean_mono_info static int stac92xx_io_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1996,6 +2152,15 @@ static int stac92xx_clfe_switch_put(struct snd_kcontrol *kcontrol, return 1; } +#define STAC_CODEC_HP_SWITCH(xname) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = 0, \ + .info = stac92xx_hp_switch_info, \ + .get = stac92xx_hp_switch_get, \ + .put = stac92xx_hp_switch_put, \ + } + #define STAC_CODEC_IO_SWITCH(xname, xpval) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -2020,6 +2185,7 @@ enum { STAC_CTL_WIDGET_VOL, STAC_CTL_WIDGET_MUTE, STAC_CTL_WIDGET_MONO_MUX, + STAC_CTL_WIDGET_HP_SWITCH, STAC_CTL_WIDGET_IO_SWITCH, STAC_CTL_WIDGET_CLFE_SWITCH }; @@ -2028,6 +2194,7 @@ static struct snd_kcontrol_new stac92xx_control_templates[] = { HDA_CODEC_VOLUME(NULL, 0, 0, 0), HDA_CODEC_MUTE(NULL, 0, 0, 0), STAC_MONO_MUX, + STAC_CODEC_HP_SWITCH(NULL), STAC_CODEC_IO_SWITCH(NULL, 0), STAC_CODEC_CLFE_SWITCH(NULL, 0), }; @@ -2222,6 +2389,29 @@ static int create_controls(struct sigmatel_spec *spec, const char *pfx, hda_nid_ return 0; } +static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid) +{ + if (!spec->multiout.hp_nid) + spec->multiout.hp_nid = nid; + else if (spec->multiout.num_dacs > 4) { + printk(KERN_WARNING "stac92xx: No space for DAC 0x%x\n", nid); + return 1; + } else { + spec->multiout.dac_nids[spec->multiout.num_dacs] = nid; + spec->multiout.num_dacs++; + } + return 0; +} + +static int check_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid) +{ + if (is_in_dac_nids(spec, nid)) + return 1; + if (spec->multiout.hp_nid == nid) + return 1; + return 0; +} + /* add playback controls from the parsed DAC table */ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) @@ -2236,7 +2426,7 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, unsigned int wid_caps, pincap; - for (i = 0; i < cfg->line_outs; i++) { + for (i = 0; i < cfg->line_outs && i < spec->multiout.num_dacs; i++) { if (!spec->multiout.dac_nids[i]) continue; @@ -2269,6 +2459,14 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, } } + if (cfg->hp_outs > 1) { + err = stac92xx_add_control(spec, + STAC_CTL_WIDGET_HP_SWITCH, + "Headphone as Line Out Switch", 0); + if (err < 0) + return err; + } + if (spec->line_switch) { nid = cfg->input_pins[AUTO_PIN_LINE]; pincap = snd_hda_param_read(codec, nid, @@ -2284,10 +2482,11 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, if (spec->mic_switch) { unsigned int def_conf; - nid = cfg->input_pins[AUTO_PIN_MIC]; + unsigned int mic_pin = AUTO_PIN_MIC; +again: + nid = cfg->input_pins[mic_pin]; def_conf = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); - /* some laptops have an internal analog microphone * which can't be used as a output */ if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED) { @@ -2297,38 +2496,22 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH, "Mic as Output Switch", (nid << 8) | 1); + nid = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_CONNECT_LIST, 0) & 0xff; + if (!check_in_dac_nids(spec, nid)) + add_spec_dacs(spec, nid); if (err < 0) return err; } + } else if (mic_pin == AUTO_PIN_MIC) { + mic_pin = AUTO_PIN_FRONT_MIC; + goto again; } } return 0; } -static int check_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid) -{ - if (is_in_dac_nids(spec, nid)) - return 1; - if (spec->multiout.hp_nid == nid) - return 1; - return 0; -} - -static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid) -{ - if (!spec->multiout.hp_nid) - spec->multiout.hp_nid = nid; - else if (spec->multiout.num_dacs > 4) { - printk(KERN_WARNING "stac92xx: No space for DAC 0x%x\n", nid); - return 1; - } else { - spec->multiout.dac_nids[spec->multiout.num_dacs] = nid; - spec->multiout.num_dacs++; - } - return 0; -} - /* add playback controls for Speaker and HP outputs */ static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec, struct auto_pin_cfg *cfg) @@ -2378,12 +2561,8 @@ static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec, return err; } if (spec->multiout.hp_nid) { - const char *pfx; - if (old_num_dacs == spec->multiout.num_dacs) - pfx = "Master"; - else - pfx = "Headphone"; - err = create_controls(spec, pfx, spec->multiout.hp_nid, 3); + err = create_controls(spec, "Headphone", + spec->multiout.hp_nid, 3); if (err < 0) return err; } @@ -2745,7 +2924,7 @@ static int stac9200_auto_create_lfe_ctls(struct hda_codec *codec, */ for (i = 0; i < spec->autocfg.speaker_outs && lfe_pin == 0x0; i++) { hda_nid_t pin = spec->autocfg.speaker_pins[i]; - unsigned long wcaps = get_wcaps(codec, pin); + unsigned int wcaps = get_wcaps(codec, pin); wcaps &= (AC_WCAP_STEREO | AC_WCAP_OUT_AMP); if (wcaps == AC_WCAP_OUT_AMP) /* found a mono speaker with an amp, must be lfe */ @@ -2756,12 +2935,12 @@ static int stac9200_auto_create_lfe_ctls(struct hda_codec *codec, if (lfe_pin == 0 && spec->autocfg.speaker_outs == 0) { for (i = 0; i < spec->autocfg.line_outs && lfe_pin == 0x0; i++) { hda_nid_t pin = spec->autocfg.line_out_pins[i]; - unsigned long cfg; - cfg = snd_hda_codec_read(codec, pin, 0, + unsigned int defcfg; + defcfg = snd_hda_codec_read(codec, pin, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00); - if (get_defcfg_device(cfg) == AC_JACK_SPEAKER) { - unsigned long wcaps = get_wcaps(codec, pin); + if (get_defcfg_device(defcfg) == AC_JACK_SPEAKER) { + unsigned int wcaps = get_wcaps(codec, pin); wcaps &= (AC_WCAP_STEREO | AC_WCAP_OUT_AMP); if (wcaps == AC_WCAP_OUT_AMP) /* found a mono speaker with an amp, @@ -2866,6 +3045,19 @@ static int is_nid_hp_pin(struct auto_pin_cfg *cfg, hda_nid_t nid) return 0; /* nid is not a HP-Out */ }; +static void stac92xx_power_down(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + + /* power down inactive DACs */ + hda_nid_t *dac; + for (dac = spec->dac_list; *dac; dac++) + if (!is_in_dac_nids(spec, *dac) && + spec->multiout.hp_nid != *dac) + snd_hda_codec_write_cache(codec, *dac, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); +} + static int stac92xx_init(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; @@ -2909,16 +3101,21 @@ static int stac92xx_init(struct hda_codec *codec) ? STAC_HP_EVENT : STAC_PWR_EVENT; int pinctl = snd_hda_codec_read(codec, spec->pwr_nids[i], 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + int def_conf = snd_hda_codec_read(codec, spec->pwr_nids[i], + 0, AC_VERB_GET_CONFIG_DEFAULT, 0); /* outputs are only ports capable of power management * any attempts on powering down a input port cause the * referenced VREF to act quirky. */ if (pinctl & AC_PINCTL_IN_EN) continue; + if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED) + continue; enable_pin_detect(codec, spec->pwr_nids[i], event | i); codec->patch_ops.unsol_event(codec, (event | i) << 26); } - + if (spec->dac_list) + stac92xx_power_down(codec); if (cfg->dig_out_pin) stac92xx_auto_set_pinctl(codec, cfg->dig_out_pin, AC_PINCTL_OUT_EN); @@ -3014,6 +3211,7 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res) { struct sigmatel_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; + int nid = cfg->hp_pins[cfg->hp_outs - 1]; int i, presence; presence = 0; @@ -3024,26 +3222,42 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res) for (i = 0; i < cfg->hp_outs; i++) { if (presence) break; + if (spec->hp_switch && cfg->hp_pins[i] == nid) + break; presence = get_hp_pin_presence(codec, cfg->hp_pins[i]); } if (presence) { /* disable lineouts, enable hp */ + if (spec->hp_switch) + stac92xx_reset_pinctl(codec, nid, AC_PINCTL_OUT_EN); for (i = 0; i < cfg->line_outs; i++) stac92xx_reset_pinctl(codec, cfg->line_out_pins[i], AC_PINCTL_OUT_EN); for (i = 0; i < cfg->speaker_outs; i++) stac92xx_reset_pinctl(codec, cfg->speaker_pins[i], AC_PINCTL_OUT_EN); + if (spec->eapd_mask) + stac_gpio_set(codec, spec->gpio_mask, + spec->gpio_dir, spec->gpio_data & + ~spec->eapd_mask); } else { /* enable lineouts, disable hp */ + if (spec->hp_switch) + stac92xx_set_pinctl(codec, nid, AC_PINCTL_OUT_EN); for (i = 0; i < cfg->line_outs; i++) stac92xx_set_pinctl(codec, cfg->line_out_pins[i], AC_PINCTL_OUT_EN); for (i = 0; i < cfg->speaker_outs; i++) stac92xx_set_pinctl(codec, cfg->speaker_pins[i], AC_PINCTL_OUT_EN); + if (spec->eapd_mask) + stac_gpio_set(codec, spec->gpio_mask, + spec->gpio_dir, spec->gpio_data | + spec->eapd_mask); } + if (!spec->hp_switch && cfg->hp_outs > 1 && presence) + stac92xx_set_pinctl(codec, nid, AC_PINCTL_OUT_EN); } static void stac92xx_pin_sense(struct hda_codec *codec, int idx) @@ -3091,6 +3305,9 @@ static int stac92xx_resume(struct hda_codec *codec) spec->gpio_dir, spec->gpio_data); snd_hda_codec_resume_amp(codec); snd_hda_codec_resume_cache(codec); + /* power down inactive DACs */ + if (spec->dac_list) + stac92xx_power_down(codec); /* invoke unsolicited event to reset the HP state */ if (spec->hp_detect) codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26); @@ -3147,12 +3364,18 @@ static int patch_stac9200(struct hda_codec *codec) spec->num_adcs = 1; spec->num_pwrs = 0; - if (spec->board_config == STAC_9200_GATEWAY) + if (spec->board_config == STAC_9200_GATEWAY || + spec->board_config == STAC_9200_OQO) spec->init = stac9200_eapd_init; else spec->init = stac9200_core_init; spec->mixer = stac9200_mixer; + if (spec->board_config == STAC_9200_PANASONIC) { + spec->gpio_mask = spec->gpio_dir = 0x09; + spec->gpio_data = 0x00; + } + err = stac9200_parse_auto_config(codec); if (err < 0) { stac92xx_free(codec); @@ -3293,6 +3516,7 @@ again: switch (spec->multiout.num_dacs) { case 0x3: /* 6 Channel */ + spec->multiout.hp_nid = 0x17; spec->mixer = stac92hd73xx_6ch_mixer; spec->init = stac92hd73xx_6ch_core_init; break; @@ -3318,13 +3542,42 @@ again: spec->num_muxes = ARRAY_SIZE(stac92hd73xx_mux_nids); spec->num_adcs = ARRAY_SIZE(stac92hd73xx_adc_nids); - spec->num_dmics = STAC92HD73XX_NUM_DMICS; spec->num_dmuxes = ARRAY_SIZE(stac92hd73xx_dmux_nids); spec->dinput_mux = &stac92hd73xx_dmux; /* GPIO0 High = Enable EAPD */ - spec->gpio_mask = spec->gpio_dir = 0x1; + spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1; spec->gpio_data = 0x01; + switch (spec->board_config) { + case STAC_DELL_M6: + spec->init = dell_eq_core_init; + switch (codec->subsystem_id) { + case 0x1028025e: /* Analog Mics */ + case 0x1028025f: + stac92xx_set_config_reg(codec, 0x0b, 0x90A70170); + spec->num_dmics = 0; + break; + case 0x10280271: /* Digital Mics */ + case 0x10280272: + spec->init = dell_m6_core_init; + /* fall-through */ + case 0x10280254: + case 0x10280255: + stac92xx_set_config_reg(codec, 0x13, 0x90A60160); + spec->num_dmics = 1; + break; + case 0x10280256: /* Both */ + case 0x10280057: + stac92xx_set_config_reg(codec, 0x0b, 0x90A70170); + stac92xx_set_config_reg(codec, 0x13, 0x90A60160); + spec->num_dmics = 1; + break; + } + break; + default: + spec->num_dmics = STAC92HD73XX_NUM_DMICS; + } + spec->num_pwrs = ARRAY_SIZE(stac92hd73xx_pwr_nids); spec->pwr_nids = stac92hd73xx_pwr_nids; @@ -3398,7 +3651,10 @@ again: spec->aloopback_shift = 0; /* GPIO0 High = EAPD */ - spec->gpio_mask = spec->gpio_dir = spec->gpio_data = 0x1; + spec->gpio_mask = 0x01; + spec->gpio_dir = 0x01; + spec->gpio_mask = 0x01; + spec->gpio_data = 0x01; spec->mux_nids = stac92hd71bxx_mux_nids; spec->adc_nids = stac92hd71bxx_adc_nids; @@ -3413,7 +3669,7 @@ again: spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids); spec->pwr_nids = stac92hd71bxx_pwr_nids; - spec->multiout.num_dacs = 2; + spec->multiout.num_dacs = 1; spec->multiout.hp_nid = 0x11; spec->multiout.dac_nids = stac92hd71bxx_dac_nids; @@ -3577,13 +3833,14 @@ static int patch_stac927x(struct hda_codec *codec) spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids); spec->mux_nids = stac927x_mux_nids; spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids); + spec->dac_list = stac927x_dac_nids; spec->multiout.dac_nids = spec->dac_nids; switch (spec->board_config) { case STAC_D965_3ST: case STAC_D965_5ST: /* GPIO0 High = Enable EAPD */ - spec->gpio_mask = spec->gpio_dir = 0x01; + spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x01; spec->gpio_data = 0x01; spec->num_dmics = 0; @@ -3591,14 +3848,23 @@ static int patch_stac927x(struct hda_codec *codec) spec->mixer = stac927x_mixer; break; case STAC_DELL_BIOS: + switch (codec->subsystem_id) { + case 0x10280209: + case 0x1028022e: + /* correct the device field to SPDIF out */ + stac92xx_set_config_reg(codec, 0x21, 0x01442070); + break; + }; + /* configure the analog microphone on some laptops */ + stac92xx_set_config_reg(codec, 0x0c, 0x90a79130); /* correct the front output jack as a hp out */ - stac92xx_set_config_reg(codec, 0x0f, 0x02270110); + stac92xx_set_config_reg(codec, 0x0f, 0x0227011f); /* correct the front input jack as a mic */ stac92xx_set_config_reg(codec, 0x0e, 0x02a79130); /* fallthru */ case STAC_DELL_3ST: /* GPIO2 High = Enable EAPD */ - spec->gpio_mask = spec->gpio_dir = 0x04; + spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x04; spec->gpio_data = 0x04; spec->dmic_nids = stac927x_dmic_nids; spec->num_dmics = STAC927X_NUM_DMICS; @@ -3610,7 +3876,7 @@ static int patch_stac927x(struct hda_codec *codec) break; default: /* GPIO0 High = Enable EAPD */ - spec->gpio_mask = spec->gpio_dir = 0x1; + spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1; spec->gpio_data = 0x01; spec->num_dmics = 0; @@ -3714,6 +3980,7 @@ static int patch_stac9205(struct hda_codec *codec) (AC_USRSP_EN | STAC_HP_EVENT)); spec->gpio_dir = 0x0b; + spec->eapd_mask = 0x01; spec->gpio_mask = 0x1b; spec->gpio_mute = 0x10; /* GPIO0 High = EAPD, GPIO1 Low = Headphone Mute, @@ -3723,7 +3990,7 @@ static int patch_stac9205(struct hda_codec *codec) break; default: /* GPIO0 High = EAPD */ - spec->gpio_mask = spec->gpio_dir = 0x1; + spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1; spec->gpio_data = 0x01; break; } diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 4e5dd4cf36f..52b1d81a26f 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -39,7 +39,7 @@ #include <sound/core.h> #include "hda_codec.h" #include "hda_local.h" - +#include "hda_patch.h" /* amp values */ #define AMP_VAL_IDX_SHIFT 19 @@ -357,7 +357,8 @@ static int via_playback_pcm_open(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct via_spec *spec = codec->spec; - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream); + return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, + hinfo); } static int via_playback_pcm_prepare(struct hda_pcm_stream *hinfo, @@ -430,8 +431,7 @@ static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct via_spec *spec = codec->spec; - snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], - 0, 0, 0); + snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); return 0; } @@ -493,6 +493,11 @@ static int via_build_controls(struct hda_codec *codec) spec->multiout.dig_out_nid); if (err < 0) return err; + err = snd_hda_create_spdif_share_sw(codec, + &spec->multiout); + if (err < 0) + return err; + spec->multiout.share_spdif = 1; } if (spec->dig_in_nid) { err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); @@ -523,6 +528,7 @@ static int via_build_pcms(struct hda_codec *codec) codec->num_pcms++; info++; info->name = spec->stream_name_digital; + info->pcm_type = HDA_PCM_TYPE_SPDIF; if (spec->multiout.dig_out_nid) { info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_digital_playback); diff --git a/sound/pci/hda/vmaster.c b/sound/pci/hda/vmaster.c deleted file mode 100644 index 2da49d20a1f..00000000000 --- a/sound/pci/hda/vmaster.c +++ /dev/null @@ -1,364 +0,0 @@ -/* - * Virtual master and slave controls - * - * Copyright (c) 2008 by Takashi Iwai <tiwai@suse.de> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2. - * - */ - -#include <linux/slab.h> -#include <sound/core.h> -#include <sound/control.h> - -/* - * a subset of information returned via ctl info callback - */ -struct link_ctl_info { - int type; /* value type */ - int count; /* item count */ - int min_val, max_val; /* min, max values */ -}; - -/* - * link master - this contains a list of slave controls that are - * identical types, i.e. info returns the same value type and value - * ranges, but may have different number of counts. - * - * The master control is so far only mono volume/switch for simplicity. - * The same value will be applied to all slaves. - */ -struct link_master { - struct list_head slaves; - struct link_ctl_info info; - int val; /* the master value */ -}; - -/* - * link slave - this contains a slave control element - * - * It fakes the control callbacsk with additional attenuation by the - * master control. A slave may have either one or two channels. - */ - -struct link_slave { - struct list_head list; - struct link_master *master; - struct link_ctl_info info; - int vals[2]; /* current values */ - struct snd_kcontrol slave; /* the copy of original control entry */ -}; - -/* get the slave ctl info and save the initial values */ -static int slave_init(struct link_slave *slave) -{ - struct snd_ctl_elem_info *uinfo; - struct snd_ctl_elem_value *uctl; - int err, ch; - - if (slave->info.count) - return 0; /* already initialized */ - - uinfo = kmalloc(sizeof(*uinfo), GFP_KERNEL); - if (!uinfo) - return -ENOMEM; - uinfo->id = slave->slave.id; - err = slave->slave.info(&slave->slave, uinfo); - if (err < 0) { - kfree(uinfo); - return err; - } - slave->info.type = uinfo->type; - slave->info.count = uinfo->count; - if (slave->info.count > 2 || - (slave->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER && - slave->info.type != SNDRV_CTL_ELEM_TYPE_BOOLEAN)) { - snd_printk(KERN_ERR "invalid slave element\n"); - kfree(uinfo); - return -EINVAL; - } - slave->info.min_val = uinfo->value.integer.min; - slave->info.max_val = uinfo->value.integer.max; - kfree(uinfo); - - uctl = kmalloc(sizeof(*uctl), GFP_KERNEL); - if (!uctl) - return -ENOMEM; - uctl->id = slave->slave.id; - err = slave->slave.get(&slave->slave, uctl); - for (ch = 0; ch < slave->info.count; ch++) - slave->vals[ch] = uctl->value.integer.value[ch]; - kfree(uctl); - return 0; -} - -/* initialize master volume */ -static int master_init(struct link_master *master) -{ - struct link_slave *slave; - - if (master->info.count) - return 0; /* already initialized */ - - list_for_each_entry(slave, &master->slaves, list) { - int err = slave_init(slave); - if (err < 0) - return err; - master->info = slave->info; - master->info.count = 1; /* always mono */ - /* set full volume as default (= no attenuation) */ - master->val = master->info.max_val; - return 0; - } - return -ENOENT; -} - -static int slave_get_val(struct link_slave *slave, - struct snd_ctl_elem_value *ucontrol) -{ - int err, ch; - - err = slave_init(slave); - if (err < 0) - return err; - for (ch = 0; ch < slave->info.count; ch++) - ucontrol->value.integer.value[ch] = slave->vals[ch]; - return 0; -} - -static int slave_put_val(struct link_slave *slave, - struct snd_ctl_elem_value *ucontrol) -{ - int err, ch, vol; - - err = master_init(slave->master); - if (err < 0) - return err; - - switch (slave->info.type) { - case SNDRV_CTL_ELEM_TYPE_BOOLEAN: - for (ch = 0; ch < slave->info.count; ch++) - ucontrol->value.integer.value[ch] &= - !!slave->master->val; - break; - case SNDRV_CTL_ELEM_TYPE_INTEGER: - for (ch = 0; ch < slave->info.count; ch++) { - /* max master volume is supposed to be 0 dB */ - vol = ucontrol->value.integer.value[ch]; - vol += slave->master->val - slave->master->info.max_val; - if (vol < slave->info.min_val) - vol = slave->info.min_val; - else if (vol > slave->info.max_val) - vol = slave->info.max_val; - ucontrol->value.integer.value[ch] = vol; - } - break; - } - return slave->slave.put(&slave->slave, ucontrol); -} - -/* - * ctl callbacks for slaves - */ -static int slave_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct link_slave *slave = snd_kcontrol_chip(kcontrol); - return slave->slave.info(&slave->slave, uinfo); -} - -static int slave_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct link_slave *slave = snd_kcontrol_chip(kcontrol); - return slave_get_val(slave, ucontrol); -} - -static int slave_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct link_slave *slave = snd_kcontrol_chip(kcontrol); - int err, ch, changed = 0; - - err = slave_init(slave); - if (err < 0) - return err; - for (ch = 0; ch < slave->info.count; ch++) { - if (slave->vals[ch] != ucontrol->value.integer.value[ch]) { - changed = 1; - slave->vals[ch] = ucontrol->value.integer.value[ch]; - } - } - if (!changed) - return 0; - return slave_put_val(slave, ucontrol); -} - -static int slave_tlv_cmd(struct snd_kcontrol *kcontrol, - int op_flag, unsigned int size, - unsigned int __user *tlv) -{ - struct link_slave *slave = snd_kcontrol_chip(kcontrol); - /* FIXME: this assumes that the max volume is 0 dB */ - return slave->slave.tlv.c(&slave->slave, op_flag, size, tlv); -} - -static void slave_free(struct snd_kcontrol *kcontrol) -{ - struct link_slave *slave = snd_kcontrol_chip(kcontrol); - if (slave->slave.private_free) - slave->slave.private_free(&slave->slave); - if (slave->master) - list_del(&slave->list); - kfree(slave); -} - -/* - * Add a slave control to the group with the given master control - * - * All slaves must be the same type (returning the same information - * via info callback). The fucntion doesn't check it, so it's your - * responsibility. - * - * Also, some additional limitations: - * - at most two channels - * - logarithmic volume control (dB level), no linear volume - * - master can only attenuate the volume, no gain - */ -int snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave) -{ - struct link_master *master_link = snd_kcontrol_chip(master); - struct link_slave *srec; - - srec = kzalloc(sizeof(*srec) + - slave->count * sizeof(*slave->vd), GFP_KERNEL); - if (!srec) - return -ENOMEM; - srec->slave = *slave; - memcpy(srec->slave.vd, slave->vd, slave->count * sizeof(*slave->vd)); - srec->master = master_link; - - /* override callbacks */ - slave->info = slave_info; - slave->get = slave_get; - slave->put = slave_put; - if (slave->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) - slave->tlv.c = slave_tlv_cmd; - slave->private_data = srec; - slave->private_free = slave_free; - - list_add_tail(&srec->list, &master_link->slaves); - return 0; -} - -/* - * ctl callbacks for master controls - */ -static int master_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct link_master *master = snd_kcontrol_chip(kcontrol); - int ret; - - ret = master_init(master); - if (ret < 0) - return ret; - uinfo->type = master->info.type; - uinfo->count = master->info.count; - uinfo->value.integer.min = master->info.min_val; - uinfo->value.integer.max = master->info.max_val; - return 0; -} - -static int master_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct link_master *master = snd_kcontrol_chip(kcontrol); - int err = master_init(master); - if (err < 0) - return err; - ucontrol->value.integer.value[0] = master->val; - return 0; -} - -static int master_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct link_master *master = snd_kcontrol_chip(kcontrol); - struct link_slave *slave; - struct snd_ctl_elem_value *uval; - int err, old_val; - - err = master_init(master); - if (err < 0) - return err; - old_val = master->val; - if (ucontrol->value.integer.value[0] == old_val) - return 0; - - uval = kmalloc(sizeof(*uval), GFP_KERNEL); - if (!uval) - return -ENOMEM; - list_for_each_entry(slave, &master->slaves, list) { - master->val = old_val; - uval->id = slave->slave.id; - slave_get_val(slave, uval); - master->val = ucontrol->value.integer.value[0]; - slave_put_val(slave, uval); - } - kfree(uval); - return 1; -} - -static void master_free(struct snd_kcontrol *kcontrol) -{ - struct link_master *master = snd_kcontrol_chip(kcontrol); - struct link_slave *slave; - - list_for_each_entry(slave, &master->slaves, list) - slave->master = NULL; - kfree(master); -} - - -/* - * Create a virtual master control with the given name - */ -struct snd_kcontrol *snd_ctl_make_virtual_master(char *name, - const unsigned int *tlv) -{ - struct link_master *master; - struct snd_kcontrol *kctl; - struct snd_kcontrol_new knew; - - memset(&knew, 0, sizeof(knew)); - knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - knew.name = name; - knew.info = master_info; - - master = kzalloc(sizeof(*master), GFP_KERNEL); - if (!master) - return NULL; - INIT_LIST_HEAD(&master->slaves); - - kctl = snd_ctl_new1(&knew, master); - if (!kctl) { - kfree(master); - return NULL; - } - /* override some callbacks */ - kctl->info = master_info; - kctl->get = master_get; - kctl->put = master_put; - kctl->private_free = master_free; - - /* additional (constant) TLV read */ - if (tlv) { - /* FIXME: this assumes that the max volume is 0 dB */ - kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; - kctl->tlv.p = tlv; - } - return kctl; -} diff --git a/sound/pci/ice1712/delta.c b/sound/pci/ice1712/delta.c index efd180b40e5..0ed96c17805 100644 --- a/sound/pci/ice1712/delta.c +++ b/sound/pci/ice1712/delta.c @@ -1,8 +1,8 @@ /* * ALSA driver for ICEnsemble ICE1712 (Envy24) * - * Lowlevel functions for M-Audio Delta 1010, 44, 66, Dio2496, Audiophile - * Digigram VX442 + * Lowlevel functions for M-Audio Delta 1010, 1010E, 44, 66, 66E, Dio2496, + * Audiophile, Digigram VX442 * * Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz> * @@ -86,6 +86,7 @@ static unsigned char ap_cs8427_codec_select(struct snd_ice1712 *ice) unsigned char tmp; tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA); switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010E: case ICE1712_SUBDEVICE_DELTA1010LT: tmp &= ~ICE1712_DELTA_1010LT_CS; tmp |= ICE1712_DELTA_1010LT_CCLK | ICE1712_DELTA_1010LT_CS_CS8427; @@ -109,6 +110,7 @@ static unsigned char ap_cs8427_codec_select(struct snd_ice1712 *ice) static void ap_cs8427_codec_deassert(struct snd_ice1712 *ice, unsigned char tmp) { switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010E: case ICE1712_SUBDEVICE_DELTA1010LT: tmp &= ~ICE1712_DELTA_1010LT_CS; tmp |= ICE1712_DELTA_1010LT_CS_NONE; @@ -534,6 +536,14 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice) int err; struct snd_akm4xxx *ak; + if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_DELTA1010 && + ice->eeprom.gpiodir == 0x7b) + ice->eeprom.subvendor = ICE1712_SUBDEVICE_DELTA1010E; + + if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_DELTA66 && + ice->eeprom.gpiodir == 0xfb) + ice->eeprom.subvendor = ICE1712_SUBDEVICE_DELTA66E; + /* determine I2C, DACs and ADCs */ switch (ice->eeprom.subvendor) { case ICE1712_SUBDEVICE_AUDIOPHILE: @@ -550,6 +560,7 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice) ice->num_total_adcs = ice->omni ? 8 : 4; break; case ICE1712_SUBDEVICE_DELTA1010: + case ICE1712_SUBDEVICE_DELTA1010E: case ICE1712_SUBDEVICE_DELTA1010LT: case ICE1712_SUBDEVICE_MEDIASTATION: ice->num_total_dacs = 8; @@ -559,6 +570,7 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice) ice->num_total_dacs = 4; /* two AK4324 codecs */ break; case ICE1712_SUBDEVICE_VX442: + case ICE1712_SUBDEVICE_DELTA66E: /* omni not suported yet */ ice->num_total_dacs = 4; ice->num_total_adcs = 4; break; @@ -568,8 +580,10 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice) switch (ice->eeprom.subvendor) { case ICE1712_SUBDEVICE_AUDIOPHILE: case ICE1712_SUBDEVICE_DELTA410: + case ICE1712_SUBDEVICE_DELTA1010E: case ICE1712_SUBDEVICE_DELTA1010LT: case ICE1712_SUBDEVICE_VX442: + case ICE1712_SUBDEVICE_DELTA66E: if ((err = snd_i2c_bus_create(ice->card, "ICE1712 GPIO 1", NULL, &ice->i2c)) < 0) { snd_printk(KERN_ERR "unable to create I2C bus\n"); return err; @@ -601,6 +615,7 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice) /* no analog? */ switch (ice->eeprom.subvendor) { case ICE1712_SUBDEVICE_DELTA1010: + case ICE1712_SUBDEVICE_DELTA1010E: case ICE1712_SUBDEVICE_DELTADIO2496: case ICE1712_SUBDEVICE_MEDIASTATION: return 0; @@ -627,6 +642,7 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice) err = snd_ice1712_akm4xxx_init(ak, &akm_delta44, &akm_delta44_priv, ice); break; case ICE1712_SUBDEVICE_VX442: + case ICE1712_SUBDEVICE_DELTA66E: err = snd_ice1712_akm4xxx_init(ak, &akm_vx442, &akm_vx442_priv, ice); break; default: @@ -674,6 +690,7 @@ static int __devinit snd_ice1712_delta_add_controls(struct snd_ice1712 *ice) if (err < 0) return err; break; + case ICE1712_SUBDEVICE_DELTA1010E: case ICE1712_SUBDEVICE_DELTA1010LT: err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_delta1010lt_wordclock_select, ice)); if (err < 0) @@ -716,6 +733,7 @@ static int __devinit snd_ice1712_delta_add_controls(struct snd_ice1712 *ice) case ICE1712_SUBDEVICE_DELTA44: case ICE1712_SUBDEVICE_DELTA66: case ICE1712_SUBDEVICE_VX442: + case ICE1712_SUBDEVICE_DELTA66E: err = snd_ice1712_akm4xxx_build_controls(ice); if (err < 0) return err; diff --git a/sound/pci/ice1712/delta.h b/sound/pci/ice1712/delta.h index 26ea05a32f5..ea7116c304c 100644 --- a/sound/pci/ice1712/delta.h +++ b/sound/pci/ice1712/delta.h @@ -36,8 +36,10 @@ "{Lionstracs,Mediastation}," #define ICE1712_SUBDEVICE_DELTA1010 0x121430d6 +#define ICE1712_SUBDEVICE_DELTA1010E 0xff1430d6 #define ICE1712_SUBDEVICE_DELTADIO2496 0x121431d6 #define ICE1712_SUBDEVICE_DELTA66 0x121432d6 +#define ICE1712_SUBDEVICE_DELTA66E 0xff1432d6 #define ICE1712_SUBDEVICE_DELTA44 0x121433d6 #define ICE1712_SUBDEVICE_AUDIOPHILE 0x121434d6 #define ICE1712_SUBDEVICE_DELTA410 0x121438d6 diff --git a/sound/pci/ice1712/ews.c b/sound/pci/ice1712/ews.c index 064760d2a02..013fc4f0482 100644 --- a/sound/pci/ice1712/ews.c +++ b/sound/pci/ice1712/ews.c @@ -238,6 +238,7 @@ static void snd_ice1712_ews_cs8404_spdif_write(struct snd_ice1712 *ice, unsigned case ICE1712_SUBDEVICE_EWS88MT: case ICE1712_SUBDEVICE_EWS88MT_NEW: case ICE1712_SUBDEVICE_PHASE88: + case ICE1712_SUBDEVICE_TS88: if (snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_CS8404], &bits, 1) != 1) goto _error; @@ -433,6 +434,7 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice) case ICE1712_SUBDEVICE_EWS88MT: case ICE1712_SUBDEVICE_EWS88MT_NEW: case ICE1712_SUBDEVICE_PHASE88: + case ICE1712_SUBDEVICE_TS88: ice->num_total_dacs = 8; ice->num_total_adcs = 8; break; @@ -475,6 +477,8 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice) case ICE1712_SUBDEVICE_EWS88MT: case ICE1712_SUBDEVICE_EWS88MT_NEW: case ICE1712_SUBDEVICE_PHASE88: + case ICE1712_SUBDEVICE_TS88: + err = snd_i2c_device_create(ice->i2c, "CS8404", ICE1712_EWS88MT_CS8404_ADDR, &spec->i2cdevs[EWS_I2C_CS8404]); @@ -518,6 +522,7 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice) case ICE1712_SUBDEVICE_EWS88MT: case ICE1712_SUBDEVICE_EWS88MT_NEW: case ICE1712_SUBDEVICE_PHASE88: + case ICE1712_SUBDEVICE_TS88: case ICE1712_SUBDEVICE_EWS88D: /* set up CS8404 */ ice->spdif.ops.open = ews88_open_spdif; @@ -547,6 +552,7 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice) case ICE1712_SUBDEVICE_EWS88MT: case ICE1712_SUBDEVICE_EWS88MT_NEW: case ICE1712_SUBDEVICE_PHASE88: + case ICE1712_SUBDEVICE_TS88: err = snd_ice1712_akm4xxx_init(ak, &akm_ews88mt, &akm_ews88mt_priv, ice); break; case ICE1712_SUBDEVICE_EWX2496: @@ -973,6 +979,7 @@ static int __devinit snd_ice1712_ews_add_controls(struct snd_ice1712 *ice) case ICE1712_SUBDEVICE_EWS88MT: case ICE1712_SUBDEVICE_EWS88MT_NEW: case ICE1712_SUBDEVICE_PHASE88: + case ICE1712_SUBDEVICE_TS88: case ICE1712_SUBDEVICE_DMX6FIRE: err = snd_ice1712_akm4xxx_build_controls(ice); if (err < 0) @@ -992,6 +999,7 @@ static int __devinit snd_ice1712_ews_add_controls(struct snd_ice1712 *ice) case ICE1712_SUBDEVICE_EWS88MT: case ICE1712_SUBDEVICE_EWS88MT_NEW: case ICE1712_SUBDEVICE_PHASE88: + case ICE1712_SUBDEVICE_TS88: err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ews88mt_input_sense, ice)); if (err < 0) return err; @@ -1049,6 +1057,13 @@ struct snd_ice1712_card_info snd_ice1712_ews_cards[] __devinitdata = { .build_controls = snd_ice1712_ews_add_controls, }, { + .subvendor = ICE1712_SUBDEVICE_TS88, + .name = "terrasoniq TS88", + .model = "phase88", + .chip_init = snd_ice1712_ews_init, + .build_controls = snd_ice1712_ews_add_controls, + }, + { .subvendor = ICE1712_SUBDEVICE_EWS88D, .name = "TerraTec EWS88D", .model = "ews88d", diff --git a/sound/pci/ice1712/ews.h b/sound/pci/ice1712/ews.h index e4ed1b475b0..1c443718af0 100644 --- a/sound/pci/ice1712/ews.h +++ b/sound/pci/ice1712/ews.h @@ -30,7 +30,8 @@ "{TerraTec,EWS 88MT},"\ "{TerraTec,EWS 88D},"\ "{TerraTec,DMX 6Fire},"\ - "{TerraTec,Phase 88}," + "{TerraTec,Phase 88}," \ + "{terrasoniq,TS 88}," #define ICE1712_SUBDEVICE_EWX2496 0x3b153011 #define ICE1712_SUBDEVICE_EWS88MT 0x3b151511 @@ -38,6 +39,7 @@ #define ICE1712_SUBDEVICE_EWS88D 0x3b152b11 #define ICE1712_SUBDEVICE_DMX6FIRE 0x3b153811 #define ICE1712_SUBDEVICE_PHASE88 0x3b155111 +#define ICE1712_SUBDEVICE_TS88 0x3b157c11 /* entry point */ extern struct snd_ice1712_card_info snd_ice1712_ews_cards[]; diff --git a/sound/pci/ice1712/hoontech.c b/sound/pci/ice1712/hoontech.c index cf5c7c0898f..6914189073a 100644 --- a/sound/pci/ice1712/hoontech.c +++ b/sound/pci/ice1712/hoontech.c @@ -208,6 +208,19 @@ static int __devinit snd_ice1712_hoontech_init(struct snd_ice1712 *ice) /* ICE1712_STDSP24_MUTE | ICE1712_STDSP24_INSEL | ICE1712_STDSP24_DAREAR; */ + /* These boxconfigs have caused problems in the past. + * The code is not optimal, but should now enable a working config to + * be achieved. + * ** MIDI IN can only be configured on one box ** + * ICE1712_STDSP24_BOX_MIDI1 needs to be set for that box. + * Tests on a ADAC2000 box suggest the box config flags do not + * work as would be expected, and the inputs are crossed. + * Setting ICE1712_STDSP24_BOX_MIDI1 and ICE1712_STDSP24_BOX_MIDI2 + * on the same box connects MIDI-In to both 401 uarts; both outputs + * are then active on all boxes. + * The default config here sets up everything on the first box. + * Alan Horstmann 5.2.2008 + */ spec->boxconfig[0] = ICE1712_STDSP24_BOX_CHN1 | ICE1712_STDSP24_BOX_CHN2 | ICE1712_STDSP24_BOX_CHN3 | @@ -223,14 +236,14 @@ static int __devinit snd_ice1712_hoontech_init(struct snd_ice1712 *ice) (spec->config & ICE1712_STDSP24_MUTE) ? 1 : 0); snd_ice1712_stdsp24_insel(ice, (spec->config & ICE1712_STDSP24_INSEL) ? 1 : 0); - for (box = 0; box < 1; box++) { + for (box = 0; box < 4; box++) { if (spec->boxconfig[box] & ICE1712_STDSP24_BOX_MIDI2) snd_ice1712_stdsp24_midi2(ice, 1); for (chn = 0; chn < 4; chn++) snd_ice1712_stdsp24_box_channel(ice, box, chn, (spec->boxconfig[box] & (1 << chn)) ? 1 : 0); - snd_ice1712_stdsp24_box_midi(ice, box, - (spec->boxconfig[box] & ICE1712_STDSP24_BOX_MIDI1) ? 1 : 0); + if (spec->boxconfig[box] & ICE1712_STDSP24_BOX_MIDI1) + snd_ice1712_stdsp24_box_midi(ice, box, 1); } return 0; @@ -322,6 +335,8 @@ struct snd_ice1712_card_info snd_ice1712_hoontech_cards[] __devinitdata = { .name = "Hoontech SoundTrack Audio DSP24", .model = "dsp24", .chip_init = snd_ice1712_hoontech_init, + .mpu401_1_name = "MIDI-1 Hoontech/STA DSP24", + .mpu401_2_name = "MIDI-2 Hoontech/STA DSP24", }, { .subvendor = ICE1712_SUBDEVICE_STDSP24_VALUE, /* a dummy id */ diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index df292af6738..29d449d73c9 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -1297,11 +1297,14 @@ static void snd_ice1712_update_volume(struct snd_ice1712 *ice, int index) static int snd_ice1712_pro_mixer_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); - int index = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + kcontrol->private_value; + int priv_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + + kcontrol->private_value; spin_lock_irq(&ice->reg_lock); - ucontrol->value.integer.value[0] = !((ice->pro_volumes[index] >> 15) & 1); - ucontrol->value.integer.value[1] = !((ice->pro_volumes[index] >> 31) & 1); + ucontrol->value.integer.value[0] = + !((ice->pro_volumes[priv_idx] >> 15) & 1); + ucontrol->value.integer.value[1] = + !((ice->pro_volumes[priv_idx] >> 31) & 1); spin_unlock_irq(&ice->reg_lock); return 0; } @@ -1309,16 +1312,17 @@ static int snd_ice1712_pro_mixer_switch_get(struct snd_kcontrol *kcontrol, struc static int snd_ice1712_pro_mixer_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); - int index = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + kcontrol->private_value; + int priv_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + + kcontrol->private_value; unsigned int nval, change; nval = (ucontrol->value.integer.value[0] ? 0 : 0x00008000) | (ucontrol->value.integer.value[1] ? 0 : 0x80000000); spin_lock_irq(&ice->reg_lock); - nval |= ice->pro_volumes[index] & ~0x80008000; - change = nval != ice->pro_volumes[index]; - ice->pro_volumes[index] = nval; - snd_ice1712_update_volume(ice, index); + nval |= ice->pro_volumes[priv_idx] & ~0x80008000; + change = nval != ice->pro_volumes[priv_idx]; + ice->pro_volumes[priv_idx] = nval; + snd_ice1712_update_volume(ice, priv_idx); spin_unlock_irq(&ice->reg_lock); return change; } @@ -1335,11 +1339,14 @@ static int snd_ice1712_pro_mixer_volume_info(struct snd_kcontrol *kcontrol, stru static int snd_ice1712_pro_mixer_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); - int index = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + kcontrol->private_value; + int priv_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + + kcontrol->private_value; spin_lock_irq(&ice->reg_lock); - ucontrol->value.integer.value[0] = (ice->pro_volumes[index] >> 0) & 127; - ucontrol->value.integer.value[1] = (ice->pro_volumes[index] >> 16) & 127; + ucontrol->value.integer.value[0] = + (ice->pro_volumes[priv_idx] >> 0) & 127; + ucontrol->value.integer.value[1] = + (ice->pro_volumes[priv_idx] >> 16) & 127; spin_unlock_irq(&ice->reg_lock); return 0; } @@ -1347,16 +1354,17 @@ static int snd_ice1712_pro_mixer_volume_get(struct snd_kcontrol *kcontrol, struc static int snd_ice1712_pro_mixer_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); - int index = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + kcontrol->private_value; + int priv_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + + kcontrol->private_value; unsigned int nval, change; nval = (ucontrol->value.integer.value[0] & 127) | ((ucontrol->value.integer.value[1] & 127) << 16); spin_lock_irq(&ice->reg_lock); - nval |= ice->pro_volumes[index] & ~0x007f007f; - change = nval != ice->pro_volumes[index]; - ice->pro_volumes[index] = nval; - snd_ice1712_update_volume(ice, index); + nval |= ice->pro_volumes[priv_idx] & ~0x007f007f; + change = nval != ice->pro_volumes[priv_idx]; + ice->pro_volumes[priv_idx] = nval; + snd_ice1712_update_volume(ice, priv_idx); spin_unlock_irq(&ice->reg_lock); return change; } @@ -2482,10 +2490,9 @@ static int snd_ice1712_free(struct snd_ice1712 *ice) outb(0xff, ICEREG(ice, IRQMASK)); /* --- */ __hw_end: - if (ice->irq >= 0) { - synchronize_irq(ice->irq); + if (ice->irq >= 0) free_irq(ice->irq, ice); - } + if (ice->port) pci_release_regions(ice->pci); snd_ice1712_akm4xxx_free(ice); diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h index 303cffe08bd..3208901c740 100644 --- a/sound/pci/ice1712/ice1712.h +++ b/sound/pci/ice1712/ice1712.h @@ -367,6 +367,15 @@ struct snd_ice1712 { /* other board-specific data */ void *spec; + + /* VT172x specific */ + int pro_rate_default; + int (*is_spdif_master)(struct snd_ice1712 *ice); + unsigned int (*get_rate)(struct snd_ice1712 *ice); + void (*set_rate)(struct snd_ice1712 *ice, unsigned int rate); + unsigned char (*set_mclk)(struct snd_ice1712 *ice, unsigned int rate); + void (*set_spdif_clock)(struct snd_ice1712 *ice); + }; @@ -429,10 +438,14 @@ int snd_ice1712_gpio_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu static inline void snd_ice1712_gpio_write_bits(struct snd_ice1712 *ice, unsigned int mask, unsigned int bits) { + unsigned val; + ice->gpio.direction |= mask; snd_ice1712_gpio_set_dir(ice, ice->gpio.direction); - snd_ice1712_gpio_set_mask(ice, ~mask); - snd_ice1712_gpio_write(ice, mask & bits); + val = snd_ice1712_gpio_read(ice); + val &= ~mask; + val |= mask & bits; + snd_ice1712_gpio_write(ice, val); } static inline int snd_ice1712_gpio_read_bits(struct snd_ice1712 *ice, diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index f533850ec6e..4490422fb93 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -106,15 +106,19 @@ static unsigned int PRO_RATE_DEFAULT = 44100; * Basic I/O */ +/* + * default rates, default clock routines + */ + /* check whether the clock mode is spdif-in */ -static inline int is_spdif_master(struct snd_ice1712 *ice) +static inline int stdclock_is_spdif_master(struct snd_ice1712 *ice) { return (inb(ICEMT1724(ice, RATE)) & VT1724_SPDIF_MASTER) ? 1 : 0; } static inline int is_pro_rate_locked(struct snd_ice1712 *ice) { - return is_spdif_master(ice) || PRO_RATE_LOCKED; + return ice->is_spdif_master(ice) || PRO_RATE_LOCKED; } /* @@ -219,6 +223,32 @@ static unsigned int snd_vt1724_get_gpio_data(struct snd_ice1712 *ice) } /* + * MPU401 accessor + */ +static unsigned char snd_vt1724_mpu401_read(struct snd_mpu401 *mpu, + unsigned long addr) +{ + /* fix status bits to the standard position */ + /* only RX_EMPTY and TX_FULL are checked */ + if (addr == MPU401C(mpu)) + return (inb(addr) & 0x0c) << 4; + else + return inb(addr); +} + +static void snd_vt1724_mpu401_write(struct snd_mpu401 *mpu, + unsigned char data, unsigned long addr) +{ + if (addr == MPU401C(mpu)) { + if (data == MPU401_ENTER_UART) + outb(0x01, addr); + /* what else? */ + } else + outb(data, addr); +} + + +/* * Interrupt handler */ @@ -226,24 +256,53 @@ static irqreturn_t snd_vt1724_interrupt(int irq, void *dev_id) { struct snd_ice1712 *ice = dev_id; unsigned char status; + unsigned char status_mask = + VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX | VT1724_IRQ_MTPCM; int handled = 0; +#ifdef CONFIG_SND_DEBUG + int timeout = 0; +#endif while (1) { status = inb(ICEREG1724(ice, IRQSTAT)); + status &= status_mask; if (status == 0) break; - +#ifdef CONFIG_SND_DEBUG + if (++timeout > 10) { + printk(KERN_ERR + "ice1724: Too long irq loop, status = 0x%x\n", + status); + break; + } +#endif handled = 1; - /* these should probably be separated at some point, - * but as we don't currently have MPU support on the board - * I will leave it - */ - if ((status & VT1724_IRQ_MPU_RX)||(status & VT1724_IRQ_MPU_TX)) { + if (status & VT1724_IRQ_MPU_TX) { + if (ice->rmidi[0]) + snd_mpu401_uart_interrupt_tx(irq, + ice->rmidi[0]->private_data); + else /* disable TX to be sure */ + outb(inb(ICEREG1724(ice, IRQMASK)) | + VT1724_IRQ_MPU_TX, + ICEREG1724(ice, IRQMASK)); + /* Due to mysterical reasons, MPU_TX is always + * generated (and can't be cleared) when a PCM + * playback is going. So let's ignore at the + * next loop. + */ + status_mask &= ~VT1724_IRQ_MPU_TX; + } + if (status & VT1724_IRQ_MPU_RX) { if (ice->rmidi[0]) - snd_mpu401_uart_interrupt(irq, ice->rmidi[0]->private_data); - outb(status & (VT1724_IRQ_MPU_RX|VT1724_IRQ_MPU_TX), ICEREG1724(ice, IRQSTAT)); - status &= ~(VT1724_IRQ_MPU_RX|VT1724_IRQ_MPU_TX); + snd_mpu401_uart_interrupt(irq, + ice->rmidi[0]->private_data); + else /* disable RX to be sure */ + outb(inb(ICEREG1724(ice, IRQMASK)) | + VT1724_IRQ_MPU_RX, + ICEREG1724(ice, IRQMASK)); } + /* ack MPU irq */ + outb(status, ICEREG1724(ice, IRQSTAT)); if (status & VT1724_IRQ_MTPCM) { /* * Multi-track PCM @@ -391,51 +450,61 @@ static int snd_vt1724_pcm_trigger(struct snd_pcm_substream *substream, int cmd) #define DMA_PAUSES (VT1724_RDMA0_PAUSE|VT1724_PDMA0_PAUSE|VT1724_RDMA1_PAUSE|\ VT1724_PDMA1_PAUSE|VT1724_PDMA2_PAUSE|VT1724_PDMA3_PAUSE|VT1724_PDMA4_PAUSE) -static int get_max_rate(struct snd_ice1712 *ice) +static const unsigned int stdclock_rate_list[16] = { + 48000, 24000, 12000, 9600, 32000, 16000, 8000, 96000, 44100, + 22050, 11025, 88200, 176400, 0, 192000, 64000 +}; + +static unsigned int stdclock_get_rate(struct snd_ice1712 *ice) { + unsigned int rate; + rate = stdclock_rate_list[inb(ICEMT1724(ice, RATE)) & 15]; + return rate; +} + +static void stdclock_set_rate(struct snd_ice1712 *ice, unsigned int rate) +{ + int i; + for (i = 0; i < ARRAY_SIZE(stdclock_rate_list); i++) { + if (stdclock_rate_list[i] == rate) { + outb(i, ICEMT1724(ice, RATE)); + return; + } + } +} + +static unsigned char stdclock_set_mclk(struct snd_ice1712 *ice, + unsigned int rate) +{ + unsigned char val, old; + /* check MT02 */ if (ice->eeprom.data[ICE_EEP2_ACLINK] & VT1724_CFG_PRO_I2S) { - if ((ice->eeprom.data[ICE_EEP2_I2S] & 0x08) && !ice->vt1720) - return 192000; + val = old = inb(ICEMT1724(ice, I2S_FORMAT)); + if (rate > 96000) + val |= VT1724_MT_I2S_MCLK_128X; /* 128x MCLK */ else - return 96000; - } else - return 48000; + val &= ~VT1724_MT_I2S_MCLK_128X; /* 256x MCLK */ + if (val != old) { + outb(val, ICEMT1724(ice, I2S_FORMAT)); + /* master clock changed */ + return 1; + } + } + /* no change in master clock */ + return 0; } static void snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate, int force) { unsigned long flags; - unsigned char val, old; - unsigned int i, mclk_change; + unsigned char mclk_change; + unsigned int i, old_rate; - if (rate > get_max_rate(ice)) + if (rate > ice->hw_rates->list[ice->hw_rates->count - 1]) return; - - switch (rate) { - case 8000: val = 6; break; - case 9600: val = 3; break; - case 11025: val = 10; break; - case 12000: val = 2; break; - case 16000: val = 5; break; - case 22050: val = 9; break; - case 24000: val = 1; break; - case 32000: val = 4; break; - case 44100: val = 8; break; - case 48000: val = 0; break; - case 64000: val = 15; break; - case 88200: val = 11; break; - case 96000: val = 7; break; - case 176400: val = 12; break; - case 192000: val = 14; break; - default: - snd_BUG(); - val = 0; - break; - } - spin_lock_irqsave(&ice->reg_lock, flags); - if ((inb(ICEMT1724(ice, DMA_CONTROL)) & DMA_STARTS) || + if ((inb(ICEMT1724(ice, DMA_CONTROL)) & DMA_STARTS) || (inb(ICEMT1724(ice, DMA_PAUSE)) & DMA_PAUSES)) { /* running? we cannot change the rate now... */ spin_unlock_irqrestore(&ice->reg_lock, flags); @@ -446,9 +515,9 @@ static void snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate, return; } - old = inb(ICEMT1724(ice, RATE)); - if (force || old != val) - outb(val, ICEMT1724(ice, RATE)); + old_rate = ice->get_rate(ice); + if (force || (old_rate != rate)) + ice->set_rate(ice, rate); else if (rate == ice->cur_rate) { spin_unlock_irqrestore(&ice->reg_lock, flags); return; @@ -456,19 +525,9 @@ static void snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate, ice->cur_rate = rate; - /* check MT02 */ - mclk_change = 0; - if (ice->eeprom.data[ICE_EEP2_ACLINK] & VT1724_CFG_PRO_I2S) { - val = old = inb(ICEMT1724(ice, I2S_FORMAT)); - if (rate > 96000) - val |= VT1724_MT_I2S_MCLK_128X; /* 128x MCLK */ - else - val &= ~VT1724_MT_I2S_MCLK_128X; /* 256x MCLK */ - if (val != old) { - outb(val, ICEMT1724(ice, I2S_FORMAT)); - mclk_change = 1; - } - } + /* setting master clock */ + mclk_change = ice->set_mclk(ice, rate); + spin_unlock_irqrestore(&ice->reg_lock, flags); if (mclk_change && ice->gpio.i2s_mclk_changed) @@ -727,43 +786,32 @@ static const struct snd_pcm_hardware snd_vt1724_2ch_stereo = /* * set rate constraints */ -static int set_rate_constraints(struct snd_ice1712 *ice, - struct snd_pcm_substream *substream) +static void set_std_hw_rates(struct snd_ice1712 *ice) { - struct snd_pcm_runtime *runtime = substream->runtime; - if (ice->hw_rates) { - /* hardware specific */ - runtime->hw.rate_min = ice->hw_rates->list[0]; - runtime->hw.rate_max = ice->hw_rates->list[ice->hw_rates->count - 1]; - runtime->hw.rates = SNDRV_PCM_RATE_KNOT; - return snd_pcm_hw_constraint_list(runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, - ice->hw_rates); - } if (ice->eeprom.data[ICE_EEP2_ACLINK] & VT1724_CFG_PRO_I2S) { /* I2S */ /* VT1720 doesn't support more than 96kHz */ if ((ice->eeprom.data[ICE_EEP2_I2S] & 0x08) && !ice->vt1720) - return snd_pcm_hw_constraint_list(runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, - &hw_constraints_rates_192); - else { - runtime->hw.rates = SNDRV_PCM_RATE_KNOT | - SNDRV_PCM_RATE_8000_96000; - runtime->hw.rate_max = 96000; - return snd_pcm_hw_constraint_list(runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, - &hw_constraints_rates_96); - } - } else if (ice->ac97) { + ice->hw_rates = &hw_constraints_rates_192; + else + ice->hw_rates = &hw_constraints_rates_96; + } else { /* ACLINK */ - runtime->hw.rate_max = 48000; - runtime->hw.rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000; - return snd_pcm_hw_constraint_list(runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, - &hw_constraints_rates_48); + ice->hw_rates = &hw_constraints_rates_48; } - return 0; +} + +static int set_rate_constraints(struct snd_ice1712 *ice, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + runtime->hw.rate_min = ice->hw_rates->list[0]; + runtime->hw.rate_max = ice->hw_rates->list[ice->hw_rates->count - 1]; + runtime->hw.rates = SNDRV_PCM_RATE_KNOT; + return snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + ice->hw_rates); } /* multi-channel playback needs alignment 8x32bit regardless of the channels @@ -824,7 +872,7 @@ static int snd_vt1724_playback_pro_close(struct snd_pcm_substream *substream) struct snd_ice1712 *ice = snd_pcm_substream_chip(substream); if (PRO_RATE_RESET) - snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 0); + snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 0); ice->playback_pro_substream = NULL; return 0; @@ -835,7 +883,7 @@ static int snd_vt1724_capture_pro_close(struct snd_pcm_substream *substream) struct snd_ice1712 *ice = snd_pcm_substream_chip(substream); if (PRO_RATE_RESET) - snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 0); + snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 0); ice->capture_pro_substream = NULL; return 0; } @@ -970,6 +1018,8 @@ static int snd_vt1724_playback_spdif_open(struct snd_pcm_substream *substream) VT1724_BUFFER_ALIGN); snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, VT1724_BUFFER_ALIGN); + if (ice->spdif.ops.open) + ice->spdif.ops.open(ice, substream); return 0; } @@ -978,8 +1028,10 @@ static int snd_vt1724_playback_spdif_close(struct snd_pcm_substream *substream) struct snd_ice1712 *ice = snd_pcm_substream_chip(substream); if (PRO_RATE_RESET) - snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 0); + snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 0); ice->playback_con_substream = NULL; + if (ice->spdif.ops.close) + ice->spdif.ops.close(ice, substream); return 0; } @@ -1002,6 +1054,8 @@ static int snd_vt1724_capture_spdif_open(struct snd_pcm_substream *substream) VT1724_BUFFER_ALIGN); snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, VT1724_BUFFER_ALIGN); + if (ice->spdif.ops.open) + ice->spdif.ops.open(ice, substream); return 0; } @@ -1010,8 +1064,10 @@ static int snd_vt1724_capture_spdif_close(struct snd_pcm_substream *substream) struct snd_ice1712 *ice = snd_pcm_substream_chip(substream); if (PRO_RATE_RESET) - snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 0); + snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 0); ice->capture_con_substream = NULL; + if (ice->spdif.ops.close) + ice->spdif.ops.close(ice, substream); return 0; } @@ -1154,7 +1210,7 @@ static int snd_vt1724_playback_indep_close(struct snd_pcm_substream *substream) struct snd_ice1712 *ice = snd_pcm_substream_chip(substream); if (PRO_RATE_RESET) - snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 0); + snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 0); ice->playback_con_substream_ds[substream->number] = NULL; ice->pcm_reserved[substream->number] = NULL; @@ -1572,50 +1628,18 @@ int snd_ice1712_gpio_put(struct snd_kcontrol *kcontrol, static int snd_vt1724_pro_internal_clock_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static const char * const texts_1724[] = { - "8000", /* 0: 6 */ - "9600", /* 1: 3 */ - "11025", /* 2: 10 */ - "12000", /* 3: 2 */ - "16000", /* 4: 5 */ - "22050", /* 5: 9 */ - "24000", /* 6: 1 */ - "32000", /* 7: 4 */ - "44100", /* 8: 8 */ - "48000", /* 9: 0 */ - "64000", /* 10: 15 */ - "88200", /* 11: 11 */ - "96000", /* 12: 7 */ - "176400", /* 13: 12 */ - "192000", /* 14: 14 */ - "IEC958 Input", /* 15: -- */ - }; - static const char * const texts_1720[] = { - "8000", /* 0: 6 */ - "9600", /* 1: 3 */ - "11025", /* 2: 10 */ - "12000", /* 3: 2 */ - "16000", /* 4: 5 */ - "22050", /* 5: 9 */ - "24000", /* 6: 1 */ - "32000", /* 7: 4 */ - "44100", /* 8: 8 */ - "48000", /* 9: 0 */ - "64000", /* 10: 15 */ - "88200", /* 11: 11 */ - "96000", /* 12: 7 */ - "IEC958 Input", /* 13: -- */ - }; struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; - uinfo->value.enumerated.items = ice->vt1720 ? 14 : 16; + uinfo->value.enumerated.items = ice->hw_rates->count + 1; if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, - ice->vt1720 ? texts_1720[uinfo->value.enumerated.item] : - texts_1724[uinfo->value.enumerated.item]); + if (uinfo->value.enumerated.item == uinfo->value.enumerated.items - 1) + strcpy(uinfo->value.enumerated.name, "IEC958 Input"); + else + sprintf(uinfo->value.enumerated.name, "%d", + ice->hw_rates->list[uinfo->value.enumerated.item]); return 0; } @@ -1623,68 +1647,79 @@ static int snd_vt1724_pro_internal_clock_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); - static const unsigned char xlate[16] = { - 9, 6, 3, 1, 7, 4, 0, 12, 8, 5, 2, 11, 13, 255, 14, 10 - }; - unsigned char val; + unsigned int i, rate; spin_lock_irq(&ice->reg_lock); - if (is_spdif_master(ice)) { - ucontrol->value.enumerated.item[0] = ice->vt1720 ? 13 : 15; + if (ice->is_spdif_master(ice)) { + ucontrol->value.enumerated.item[0] = ice->hw_rates->count; } else { - val = xlate[inb(ICEMT1724(ice, RATE)) & 15]; - if (val == 255) { - snd_BUG(); - val = 0; + rate = ice->get_rate(ice); + ucontrol->value.enumerated.item[0] = 0; + for (i = 0; i < ice->hw_rates->count; i++) { + if (ice->hw_rates->list[i] == rate) { + ucontrol->value.enumerated.item[0] = i; + break; + } } - ucontrol->value.enumerated.item[0] = val; } spin_unlock_irq(&ice->reg_lock); return 0; } +/* setting clock to external - SPDIF */ +static void stdclock_set_spdif_clock(struct snd_ice1712 *ice) +{ + unsigned char oval; + unsigned char i2s_oval; + oval = inb(ICEMT1724(ice, RATE)); + outb(oval | VT1724_SPDIF_MASTER, ICEMT1724(ice, RATE)); + /* setting 256fs */ + i2s_oval = inb(ICEMT1724(ice, I2S_FORMAT)); + outb(i2s_oval & ~VT1724_MT_I2S_MCLK_128X, ICEMT1724(ice, I2S_FORMAT)); +} + static int snd_vt1724_pro_internal_clock_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); - unsigned char oval; - int rate; - int change = 0; - int spdif = ice->vt1720 ? 13 : 15; + unsigned int old_rate, new_rate; + unsigned int item = ucontrol->value.enumerated.item[0]; + unsigned int spdif = ice->hw_rates->count; + + if (item > spdif) + return -EINVAL; spin_lock_irq(&ice->reg_lock); - oval = inb(ICEMT1724(ice, RATE)); - if (ucontrol->value.enumerated.item[0] == spdif) { - unsigned char i2s_oval; - outb(oval | VT1724_SPDIF_MASTER, ICEMT1724(ice, RATE)); - /* setting 256fs */ - i2s_oval = inb(ICEMT1724(ice, I2S_FORMAT)); - outb(i2s_oval & ~VT1724_MT_I2S_MCLK_128X, - ICEMT1724(ice, I2S_FORMAT)); + if (ice->is_spdif_master(ice)) + old_rate = 0; + else + old_rate = ice->get_rate(ice); + if (item == spdif) { + /* switching to external clock via SPDIF */ + ice->set_spdif_clock(ice); + new_rate = 0; } else { - rate = rates[ucontrol->value.integer.value[0] % 15]; - if (rate <= get_max_rate(ice)) { - PRO_RATE_DEFAULT = rate; - spin_unlock_irq(&ice->reg_lock); - snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 1); - spin_lock_irq(&ice->reg_lock); - } + /* internal on-card clock */ + new_rate = ice->hw_rates->list[item]; + ice->pro_rate_default = new_rate; + spin_unlock_irq(&ice->reg_lock); + snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 1); + spin_lock_irq(&ice->reg_lock); } - change = inb(ICEMT1724(ice, RATE)) != oval; spin_unlock_irq(&ice->reg_lock); - if ((oval & VT1724_SPDIF_MASTER) != - (inb(ICEMT1724(ice, RATE)) & VT1724_SPDIF_MASTER)) { + /* the first reset to the SPDIF master mode? */ + if (old_rate != new_rate && !new_rate) { /* notify akm chips as well */ - if (is_spdif_master(ice)) { - unsigned int i; - for (i = 0; i < ice->akm_codecs; i++) { - if (ice->akm[i].ops.set_rate_val) - ice->akm[i].ops.set_rate_val(&ice->akm[i], 0); - } + unsigned int i; + if (ice->gpio.set_pro_rate) + ice->gpio.set_pro_rate(ice, 0); + for (i = 0; i < ice->akm_codecs; i++) { + if (ice->akm[i].ops.set_rate_val) + ice->akm[i].ops.set_rate_val(&ice->akm[i], 0); } } - return change; + return old_rate != new_rate; } static struct snd_kcontrol_new snd_vt1724_pro_internal_clock __devinitdata = { @@ -2065,12 +2100,16 @@ static int __devinit snd_vt1724_read_eeprom(struct snd_ice1712 *ice, -static int __devinit snd_vt1724_chip_init(struct snd_ice1712 *ice) +static void __devinit snd_vt1724_chip_reset(struct snd_ice1712 *ice) { outb(VT1724_RESET , ICEREG1724(ice, CONTROL)); - udelay(200); + msleep(10); outb(0, ICEREG1724(ice, CONTROL)); - udelay(200); + msleep(10); +} + +static int __devinit snd_vt1724_chip_init(struct snd_ice1712 *ice) +{ outb(ice->eeprom.data[ICE_EEP2_SYSCONF], ICEREG1724(ice, SYS_CFG)); outb(ice->eeprom.data[ICE_EEP2_ACLINK], ICEREG1724(ice, AC97_CFG)); outb(ice->eeprom.data[ICE_EEP2_I2S], ICEREG1724(ice, I2S_FEATURES)); @@ -2169,10 +2208,8 @@ static int snd_vt1724_free(struct snd_ice1712 *ice) outb(0xff, ICEREG1724(ice, IRQMASK)); /* --- */ __hw_end: - if (ice->irq >= 0) { - synchronize_irq(ice->irq); + if (ice->irq >= 0) free_irq(ice->irq, ice); - } pci_release_regions(ice->pci); snd_ice1712_akm4xxx_free(ice); pci_disable_device(ice->pci); @@ -2243,6 +2280,7 @@ static int __devinit snd_vt1724_create(struct snd_card *card, ice->irq = pci->irq; + snd_vt1724_chip_reset(ice); if (snd_vt1724_read_eeprom(ice, modelname) < 0) { snd_vt1724_free(ice); return -EIO; @@ -2253,10 +2291,7 @@ static int __devinit snd_vt1724_create(struct snd_card *card, } /* unmask used interrupts */ - if (! (ice->eeprom.data[ICE_EEP2_SYSCONF] & VT1724_CFG_MPU401)) - mask = VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX; - else - mask = 0; + mask = VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX; outb(mask, ICEREG1724(ice, IRQMASK)); /* don't handle FIFO overrun/underruns (just yet), * since they cause machine lockups @@ -2335,6 +2370,19 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci, * was called so in ice1712 driver, and vt1724 driver is derived from * ice1712 driver. */ + ice->pro_rate_default = PRO_RATE_DEFAULT; + if (!ice->is_spdif_master) + ice->is_spdif_master = stdclock_is_spdif_master; + if (!ice->get_rate) + ice->get_rate = stdclock_get_rate; + if (!ice->set_rate) + ice->set_rate = stdclock_set_rate; + if (!ice->set_mclk) + ice->set_mclk = stdclock_set_mclk; + if (!ice->set_spdif_clock) + ice->set_spdif_clock = stdclock_set_spdif_clock; + if (!ice->hw_rates) + set_std_hw_rates(ice); if ((err = snd_vt1724_pcm_profi(ice, pcm_dev++)) < 0) { snd_card_free(card); @@ -2377,14 +2425,29 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci, if (! c->no_mpu401) { if (ice->eeprom.data[ICE_EEP2_SYSCONF] & VT1724_CFG_MPU401) { + struct snd_mpu401 *mpu; if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712, ICEREG1724(ice, MPU_CTRL), - MPU401_INFO_INTEGRATED, + (MPU401_INFO_INTEGRATED | + MPU401_INFO_TX_IRQ), ice->irq, 0, &ice->rmidi[0])) < 0) { snd_card_free(card); return err; } + mpu = ice->rmidi[0]->private_data; + mpu->read = snd_vt1724_mpu401_read; + mpu->write = snd_vt1724_mpu401_write; + /* unmask MPU RX/TX irqs */ + outb(inb(ICEREG1724(ice, IRQMASK)) & + ~(VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX), + ICEREG1724(ice, IRQMASK)); +#if 0 /* for testing */ + /* set watermarks */ + outb(VT1724_MPU_RX_FIFO | 0x1, + ICEREG1724(ice, MPU_FIFO_WM)); + outb(0x1, ICEREG1724(ice, MPU_FIFO_WM)); +#endif } } diff --git a/sound/pci/ice1712/juli.c b/sound/pci/ice1712/juli.c index e8038c0ceb7..b4e0c16852a 100644 --- a/sound/pci/ice1712/juli.c +++ b/sound/pci/ice1712/juli.c @@ -4,6 +4,8 @@ * Lowlevel functions for ESI Juli@ cards * * Copyright (c) 2004 Jaroslav Kysela <perex@perex.cz> + * 2008 Pavel Hofman <dustin@seznam.cz> + * * * 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 @@ -27,11 +29,11 @@ #include <linux/init.h> #include <linux/slab.h> #include <sound/core.h> +#include <sound/tlv.h> #include "ice1712.h" #include "envy24ht.h" #include "juli.h" - struct juli_spec { struct ak4114 *ak4114; unsigned int analog: 1; @@ -44,6 +46,32 @@ struct juli_spec { #define AK4358_ADDR 0x22 /* DAC */ /* + * Juli does not use the standard ICE1724 clock scheme. Juli's ice1724 chip is + * supplied by external clock provided by Xilinx array and MK73-1 PLL frequency + * multiplier. Actual frequency is set by ice1724 GPIOs hooked to the Xilinx. + * + * The clock circuitry is supplied by the two ice1724 crystals. This + * arrangement allows to generate independent clock signal for AK4114's input + * rate detection circuit. As a result, Juli, unlike most other + * ice1724+ak4114-based cards, detects spdif input rate correctly. + * This fact is applied in the driver, allowing to modify PCM stream rate + * parameter according to the actual input rate. + * + * Juli uses the remaining three stereo-channels of its DAC to optionally + * monitor analog input, digital input, and digital output. The corresponding + * I2S signals are routed by Xilinx, controlled by GPIOs. + * + * The master mute is implemented using output muting transistors (GPIO) in + * combination with smuting the DAC. + * + * The card itself has no HW master volume control, implemented using the + * vmaster control. + * + * TODO: + * researching and fixing the input monitors + */ + +/* * GPIO pins */ #define GPIO_FREQ_MASK (3<<0) @@ -55,17 +83,82 @@ struct juli_spec { #define GPIO_MULTI_2X (1<<2) #define GPIO_MULTI_1X (2<<2) /* also external */ #define GPIO_MULTI_HALF (3<<2) -#define GPIO_INTERNAL_CLOCK (1<<4) +#define GPIO_INTERNAL_CLOCK (1<<4) /* 0 = external, 1 = internal */ +#define GPIO_CLOCK_MASK (1<<4) #define GPIO_ANALOG_PRESENT (1<<5) /* RO only: 0 = present */ #define GPIO_RXMCLK_SEL (1<<7) /* must be 0 */ #define GPIO_AK5385A_CKS0 (1<<8) -#define GPIO_AK5385A_DFS0 (1<<9) /* swapped with DFS1 according doc? */ -#define GPIO_AK5385A_DFS1 (1<<10) +#define GPIO_AK5385A_DFS1 (1<<9) +#define GPIO_AK5385A_DFS0 (1<<10) #define GPIO_DIGOUT_MONITOR (1<<11) /* 1 = active */ #define GPIO_DIGIN_MONITOR (1<<12) /* 1 = active */ #define GPIO_ANAIN_MONITOR (1<<13) /* 1 = active */ -#define GPIO_AK5385A_MCLK (1<<14) /* must be 0 */ -#define GPIO_MUTE_CONTROL (1<<15) /* 0 = off, 1 = on */ +#define GPIO_AK5385A_CKS1 (1<<14) /* must be 0 */ +#define GPIO_MUTE_CONTROL (1<<15) /* output mute, 1 = muted */ + +#define GPIO_RATE_MASK (GPIO_FREQ_MASK | GPIO_MULTI_MASK | \ + GPIO_CLOCK_MASK) +#define GPIO_AK5385A_MASK (GPIO_AK5385A_CKS0 | GPIO_AK5385A_DFS0 | \ + GPIO_AK5385A_DFS1 | GPIO_AK5385A_CKS1) + +#define JULI_PCM_RATE (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \ + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000) + +#define GPIO_RATE_16000 (GPIO_FREQ_32KHZ | GPIO_MULTI_HALF | \ + GPIO_INTERNAL_CLOCK) +#define GPIO_RATE_22050 (GPIO_FREQ_44KHZ | GPIO_MULTI_HALF | \ + GPIO_INTERNAL_CLOCK) +#define GPIO_RATE_24000 (GPIO_FREQ_48KHZ | GPIO_MULTI_HALF | \ + GPIO_INTERNAL_CLOCK) +#define GPIO_RATE_32000 (GPIO_FREQ_32KHZ | GPIO_MULTI_1X | \ + GPIO_INTERNAL_CLOCK) +#define GPIO_RATE_44100 (GPIO_FREQ_44KHZ | GPIO_MULTI_1X | \ + GPIO_INTERNAL_CLOCK) +#define GPIO_RATE_48000 (GPIO_FREQ_48KHZ | GPIO_MULTI_1X | \ + GPIO_INTERNAL_CLOCK) +#define GPIO_RATE_64000 (GPIO_FREQ_32KHZ | GPIO_MULTI_2X | \ + GPIO_INTERNAL_CLOCK) +#define GPIO_RATE_88200 (GPIO_FREQ_44KHZ | GPIO_MULTI_2X | \ + GPIO_INTERNAL_CLOCK) +#define GPIO_RATE_96000 (GPIO_FREQ_48KHZ | GPIO_MULTI_2X | \ + GPIO_INTERNAL_CLOCK) +#define GPIO_RATE_176400 (GPIO_FREQ_44KHZ | GPIO_MULTI_4X | \ + GPIO_INTERNAL_CLOCK) +#define GPIO_RATE_192000 (GPIO_FREQ_48KHZ | GPIO_MULTI_4X | \ + GPIO_INTERNAL_CLOCK) + +/* + * Initial setup of the conversion array GPIO <-> rate + */ +static unsigned int juli_rates[] = { + 16000, 22050, 24000, 32000, + 44100, 48000, 64000, 88200, + 96000, 176400, 192000, +}; + +static unsigned int gpio_vals[] = { + GPIO_RATE_16000, GPIO_RATE_22050, GPIO_RATE_24000, GPIO_RATE_32000, + GPIO_RATE_44100, GPIO_RATE_48000, GPIO_RATE_64000, GPIO_RATE_88200, + GPIO_RATE_96000, GPIO_RATE_176400, GPIO_RATE_192000, +}; + +static struct snd_pcm_hw_constraint_list juli_rates_info = { + .count = ARRAY_SIZE(juli_rates), + .list = juli_rates, + .mask = 0, +}; + +static int get_gpio_val(int rate) +{ + int i; + for (i = 0; i < ARRAY_SIZE(juli_rates); i++) + if (juli_rates[i] == rate) + return gpio_vals[i]; + return 0; +} static void juli_ak4114_write(void *private_data, unsigned char reg, unsigned char val) { @@ -78,6 +171,27 @@ static unsigned char juli_ak4114_read(void *private_data, unsigned char reg) } /* + * If SPDIF capture and slaved to SPDIF-IN, setting runtime rate + * to the external rate + */ +static void juli_spdif_in_open(struct snd_ice1712 *ice, + struct snd_pcm_substream *substream) +{ + struct juli_spec *spec = ice->spec; + struct snd_pcm_runtime *runtime = substream->runtime; + int rate; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK || + !ice->is_spdif_master(ice)) + return; + rate = snd_ak4114_external_rate(spec->ak4114); + if (rate >= runtime->hw.rate_min && rate <= runtime->hw.rate_max) { + runtime->hw.rate_min = rate; + runtime->hw.rate_max = rate; + } +} + +/* * AK4358 section */ @@ -99,57 +213,285 @@ static void juli_akm_write(struct snd_akm4xxx *ak, int chip, } /* - * change the rate of envy24HT, AK4358 + * change the rate of envy24HT, AK4358, AK5385 */ static void juli_akm_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate) { - unsigned char old, tmp, dfs; + unsigned char old, tmp, ak4358_dfs; + unsigned int ak5385_pins, old_gpio, new_gpio; + struct snd_ice1712 *ice = ak->private_data[0]; + struct juli_spec *spec = ice->spec; - if (rate == 0) /* no hint - S/PDIF input is master, simply return */ + if (rate == 0) /* no hint - S/PDIF input is master or the new spdif + input rate undetected, simply return */ return; - + /* adjust DFS on codecs */ - if (rate > 96000) - dfs = 2; - else if (rate > 48000) - dfs = 1; - else - dfs = 0; - + if (rate > 96000) { + ak4358_dfs = 2; + ak5385_pins = GPIO_AK5385A_DFS1 | GPIO_AK5385A_CKS0; + } else if (rate > 48000) { + ak4358_dfs = 1; + ak5385_pins = GPIO_AK5385A_DFS0; + } else { + ak4358_dfs = 0; + ak5385_pins = 0; + } + /* AK5385 first, since it requires cold reset affecting both codecs */ + old_gpio = ice->gpio.get_data(ice); + new_gpio = (old_gpio & ~GPIO_AK5385A_MASK) | ak5385_pins; + /* printk(KERN_DEBUG "JULI - ak5385 set_rate_val: new gpio 0x%x\n", + new_gpio); */ + ice->gpio.set_data(ice, new_gpio); + + /* cold reset */ + old = inb(ICEMT1724(ice, AC97_CMD)); + outb(old | VT1724_AC97_COLD, ICEMT1724(ice, AC97_CMD)); + udelay(1); + outb(old & ~VT1724_AC97_COLD, ICEMT1724(ice, AC97_CMD)); + + /* AK4358 */ + /* set new value, reset DFS */ tmp = snd_akm4xxx_get(ak, 0, 2); - old = (tmp >> 4) & 0x03; - if (old == dfs) - return; - /* reset DFS */ snd_akm4xxx_reset(ak, 1); tmp = snd_akm4xxx_get(ak, 0, 2); tmp &= ~(0x03 << 4); - tmp |= dfs << 4; + tmp |= ak4358_dfs << 4; snd_akm4xxx_set(ak, 0, 2, tmp); snd_akm4xxx_reset(ak, 0); + + /* reinit ak4114 */ + snd_ak4114_reinit(spec->ak4114); } +#define AK_DAC(xname, xch) { .name = xname, .num_channels = xch } +#define PCM_VOLUME "PCM Playback Volume" +#define MONITOR_AN_IN_VOLUME "Monitor Analog In Volume" +#define MONITOR_DIG_IN_VOLUME "Monitor Digital In Volume" +#define MONITOR_DIG_OUT_VOLUME "Monitor Digital Out Volume" + +static const struct snd_akm4xxx_dac_channel juli_dac[] = { + AK_DAC(PCM_VOLUME, 2), + AK_DAC(MONITOR_AN_IN_VOLUME, 2), + AK_DAC(MONITOR_DIG_OUT_VOLUME, 2), + AK_DAC(MONITOR_DIG_IN_VOLUME, 2), +}; + + static struct snd_akm4xxx akm_juli_dac __devinitdata = { .type = SND_AK4358, - .num_dacs = 2, + .num_dacs = 8, /* DAC1 - analog out + DAC2 - analog in monitor + DAC3 - digital out monitor + DAC4 - digital in monitor + */ .ops = { .lock = juli_akm_lock, .unlock = juli_akm_unlock, .write = juli_akm_write, .set_rate_val = juli_akm_set_rate_val + }, + .dac_info = juli_dac, +}; + +#define juli_mute_info snd_ctl_boolean_mono_info + +static int juli_mute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + unsigned int val; + val = ice->gpio.get_data(ice) & (unsigned int) kcontrol->private_value; + if (kcontrol->private_value == GPIO_MUTE_CONTROL) + /* val 0 = signal on */ + ucontrol->value.integer.value[0] = (val) ? 0 : 1; + else + /* val 1 = signal on */ + ucontrol->value.integer.value[0] = (val) ? 1 : 0; + return 0; +} + +static int juli_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + unsigned int old_gpio, new_gpio; + old_gpio = ice->gpio.get_data(ice); + if (ucontrol->value.integer.value[0]) { + /* unmute */ + if (kcontrol->private_value == GPIO_MUTE_CONTROL) { + /* 0 = signal on */ + new_gpio = old_gpio & ~GPIO_MUTE_CONTROL; + /* un-smuting DAC */ + snd_akm4xxx_write(ice->akm, 0, 0x01, 0x01); + } else + /* 1 = signal on */ + new_gpio = old_gpio | + (unsigned int) kcontrol->private_value; + } else { + /* mute */ + if (kcontrol->private_value == GPIO_MUTE_CONTROL) { + /* 1 = signal off */ + new_gpio = old_gpio | GPIO_MUTE_CONTROL; + /* smuting DAC */ + snd_akm4xxx_write(ice->akm, 0, 0x01, 0x03); + } else + /* 0 = signal off */ + new_gpio = old_gpio & + ~((unsigned int) kcontrol->private_value); + } + /* printk("JULI - mute/unmute: control_value: 0x%x, old_gpio: 0x%x, \ + new_gpio 0x%x\n", + (unsigned int)ucontrol->value.integer.value[0], old_gpio, + new_gpio); */ + if (old_gpio != new_gpio) { + ice->gpio.set_data(ice, new_gpio); + return 1; + } + /* no change */ + return 0; +} + +static struct snd_kcontrol_new juli_mute_controls[] __devinitdata = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = juli_mute_info, + .get = juli_mute_get, + .put = juli_mute_put, + .private_value = GPIO_MUTE_CONTROL, + }, + /* Although the following functionality respects the succint NDA'd + * documentation from the card manufacturer, and the same way of + * operation is coded in OSS Juli driver, only Digital Out monitor + * seems to work. Surprisingly, Analog input monitor outputs Digital + * output data. The two are independent, as enabling both doubles + * volume of the monitor sound. + * + * Checking traces on the board suggests the functionality described + * by the manufacturer is correct - I2S from ADC and AK4114 + * go to ICE as well as to Xilinx, I2S inputs of DAC2,3,4 (the monitor + * inputs) are fed from Xilinx. + * + * I even checked traces on board and coded a support in driver for + * an alternative possiblity - the unused I2S ICE output channels + * switched to HW-IN/SPDIF-IN and providing the monitoring signal to + * the DAC - to no avail. The I2S outputs seem to be unconnected. + * + * The windows driver supports the monitoring correctly. + */ + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Monitor Analog In Switch", + .info = juli_mute_info, + .get = juli_mute_get, + .put = juli_mute_put, + .private_value = GPIO_ANAIN_MONITOR, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Monitor Digital Out Switch", + .info = juli_mute_info, + .get = juli_mute_get, + .put = juli_mute_put, + .private_value = GPIO_DIGOUT_MONITOR, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Monitor Digital In Switch", + .info = juli_mute_info, + .get = juli_mute_get, + .put = juli_mute_put, + .private_value = GPIO_DIGIN_MONITOR, + }, +}; + + +static void ak4358_proc_regs_read(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct snd_ice1712 *ice = (struct snd_ice1712 *)entry->private_data; + int reg, val; + for (reg = 0; reg <= 0xf; reg++) { + val = snd_akm4xxx_get(ice->akm, 0, reg); + snd_iprintf(buffer, "0x%02x = 0x%02x\n", reg, val); } +} + +static void ak4358_proc_init(struct snd_ice1712 *ice) +{ + struct snd_info_entry *entry; + if (!snd_card_proc_new(ice->card, "ak4358_codec", &entry)) + snd_info_set_text_ops(entry, ice, ak4358_proc_regs_read); +} + +static char *slave_vols[] __devinitdata = { + PCM_VOLUME, + MONITOR_AN_IN_VOLUME, + MONITOR_DIG_IN_VOLUME, + MONITOR_DIG_OUT_VOLUME, + NULL }; +static __devinitdata +DECLARE_TLV_DB_SCALE(juli_master_db_scale, -6350, 50, 1); + +static struct snd_kcontrol __devinit *ctl_find(struct snd_card *card, + const char *name) +{ + struct snd_ctl_elem_id sid; + memset(&sid, 0, sizeof(sid)); + /* FIXME: strcpy is bad. */ + strcpy(sid.name, name); + sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + return snd_ctl_find_id(card, &sid); +} + +static void __devinit add_slaves(struct snd_card *card, + struct snd_kcontrol *master, char **list) +{ + for (; *list; list++) { + struct snd_kcontrol *slave = ctl_find(card, *list); + /* printk(KERN_DEBUG "add_slaves - %s\n", *list); */ + if (slave) { + /* printk(KERN_DEBUG "slave %s found\n", *list); */ + snd_ctl_add_slave(master, slave); + } + } +} + static int __devinit juli_add_controls(struct snd_ice1712 *ice) { struct juli_spec *spec = ice->spec; int err; + unsigned int i; + struct snd_kcontrol *vmaster; + err = snd_ice1712_akm4xxx_build_controls(ice); if (err < 0) return err; + + for (i = 0; i < ARRAY_SIZE(juli_mute_controls); i++) { + err = snd_ctl_add(ice->card, + snd_ctl_new1(&juli_mute_controls[i], ice)); + if (err < 0) + return err; + } + /* Create virtual master control */ + vmaster = snd_ctl_make_virtual_master("Master Playback Volume", + juli_master_db_scale); + if (!vmaster) + return -ENOMEM; + add_slaves(ice->card, vmaster, slave_vols); + err = snd_ctl_add(ice->card, vmaster); + if (err < 0) + return err; + /* only capture SPDIF over AK4114 */ err = snd_ak4114_build(spec->ak4114, NULL, - ice->pcm_pro->streams[SNDRV_PCM_STREAM_CAPTURE].substream); + ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream); + + ak4358_proc_init(ice); if (err < 0) return err; return 0; @@ -158,6 +500,74 @@ static int __devinit juli_add_controls(struct snd_ice1712 *ice) /* * initialize the chip */ + +static inline int juli_is_spdif_master(struct snd_ice1712 *ice) +{ + return (ice->gpio.get_data(ice) & GPIO_INTERNAL_CLOCK) ? 0 : 1; +} + +static unsigned int juli_get_rate(struct snd_ice1712 *ice) +{ + int i; + unsigned char result; + + result = ice->gpio.get_data(ice) & GPIO_RATE_MASK; + for (i = 0; i < ARRAY_SIZE(gpio_vals); i++) + if (gpio_vals[i] == result) + return juli_rates[i]; + return 0; +} + +/* setting new rate */ +static void juli_set_rate(struct snd_ice1712 *ice, unsigned int rate) +{ + unsigned int old, new; + unsigned char val; + + old = ice->gpio.get_data(ice); + new = (old & ~GPIO_RATE_MASK) | get_gpio_val(rate); + /* printk(KERN_DEBUG "JULI - set_rate: old %x, new %x\n", + old & GPIO_RATE_MASK, + new & GPIO_RATE_MASK); */ + + ice->gpio.set_data(ice, new); + /* switching to external clock - supplied by external circuits */ + val = inb(ICEMT1724(ice, RATE)); + outb(val | VT1724_SPDIF_MASTER, ICEMT1724(ice, RATE)); +} + +static inline unsigned char juli_set_mclk(struct snd_ice1712 *ice, + unsigned int rate) +{ + /* no change in master clock */ + return 0; +} + +/* setting clock to external - SPDIF */ +static void juli_set_spdif_clock(struct snd_ice1712 *ice) +{ + unsigned int old; + old = ice->gpio.get_data(ice); + /* external clock (= 0), multiply 1x, 48kHz */ + ice->gpio.set_data(ice, (old & ~GPIO_RATE_MASK) | GPIO_MULTI_1X | + GPIO_FREQ_48KHZ); +} + +/* Called when ak4114 detects change in the input SPDIF stream */ +static void juli_ak4114_change(struct ak4114 *ak4114, unsigned char c0, + unsigned char c1) +{ + struct snd_ice1712 *ice = ak4114->change_callback_private; + int rate; + if (ice->is_spdif_master(ice) && c1) { + /* only for SPDIF master mode, rate was changed */ + rate = snd_ak4114_external_rate(ak4114); + /* printk(KERN_DEBUG "ak4114 - input rate changed to %d\n", + rate); */ + juli_akm_set_rate_val(ice->akm, rate); + } +} + static int __devinit juli_init(struct snd_ice1712 *ice) { static const unsigned char ak4114_init_vals[] = { @@ -187,6 +597,11 @@ static int __devinit juli_init(struct snd_ice1712 *ice) ice, &spec->ak4114); if (err < 0) return err; + /* callback for codecs rate setting */ + spec->ak4114->change_callback = juli_ak4114_change; + spec->ak4114->change_callback_private = ice; + /* AK4114 in Juli can detect external rate correctly */ + spec->ak4114->check_flags = 0; #if 0 /* it seems that the analog doughter board detection does not work @@ -210,6 +625,15 @@ static int __devinit juli_init(struct snd_ice1712 *ice) return err; } + /* juli is clocked by Xilinx array */ + ice->hw_rates = &juli_rates_info; + ice->is_spdif_master = juli_is_spdif_master; + ice->get_rate = juli_get_rate; + ice->set_rate = juli_set_rate; + ice->set_mclk = juli_set_mclk; + ice->set_spdif_clock = juli_set_spdif_clock; + + ice->spdif.ops.open = juli_spdif_in_open; return 0; } @@ -220,18 +644,20 @@ static int __devinit juli_init(struct snd_ice1712 *ice) */ static unsigned char juli_eeprom[] __devinitdata = { - [ICE_EEP2_SYSCONF] = 0x20, /* clock 512, mpu401, 1xADC, 1xDACs */ + [ICE_EEP2_SYSCONF] = 0x2b, /* clock 512, mpu401, 1xADC, 1xDACs, + SPDIF in */ [ICE_EEP2_ACLINK] = 0x80, /* I2S */ [ICE_EEP2_I2S] = 0xf8, /* vol, 96k, 24bit, 192k */ [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */ - [ICE_EEP2_GPIO_DIR] = 0x9f, + [ICE_EEP2_GPIO_DIR] = 0x9f, /* 5, 6:inputs; 7, 4-0 outputs*/ [ICE_EEP2_GPIO_DIR1] = 0xff, [ICE_EEP2_GPIO_DIR2] = 0x7f, - [ICE_EEP2_GPIO_MASK] = 0x9f, - [ICE_EEP2_GPIO_MASK1] = 0xff, + [ICE_EEP2_GPIO_MASK] = 0x60, /* 5, 6: locked; 7, 4-0 writable */ + [ICE_EEP2_GPIO_MASK1] = 0x00, /* 0-7 writable */ [ICE_EEP2_GPIO_MASK2] = 0x7f, - [ICE_EEP2_GPIO_STATE] = 0x16, /* internal clock, multiple 1x, 48kHz */ - [ICE_EEP2_GPIO_STATE1] = 0x80, /* mute */ + [ICE_EEP2_GPIO_STATE] = GPIO_FREQ_48KHZ | GPIO_MULTI_1X | + GPIO_INTERNAL_CLOCK, /* internal clock, multiple 1x, 48kHz*/ + [ICE_EEP2_GPIO_STATE1] = 0x00, /* unmuted */ [ICE_EEP2_GPIO_STATE2] = 0x00, }; diff --git a/sound/pci/ice1712/pontis.c b/sound/pci/ice1712/pontis.c index 4945c81e8a9..203cdc1bf8d 100644 --- a/sound/pci/ice1712/pontis.c +++ b/sound/pci/ice1712/pontis.c @@ -246,7 +246,7 @@ static int wm_adc_mux_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val wm_put(ice, WM_ADC_MUX, nval); } mutex_unlock(&ice->gpio_mutex); - return 0; + return change; } /* @@ -450,7 +450,7 @@ static int cs_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu change = 1; } mutex_unlock(&ice->gpio_mutex); - return 0; + return change; } diff --git a/sound/pci/ice1712/prodigy192.c b/sound/pci/ice1712/prodigy192.c index 48cf40a8f32..48d3679292a 100644 --- a/sound/pci/ice1712/prodigy192.c +++ b/sound/pci/ice1712/prodigy192.c @@ -319,12 +319,11 @@ static int stac9460_mic_sw_put(struct snd_kcontrol *kcontrol, /* * Handler for setting correct codec rate - called when rate change is detected */ -static void stac9460_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate) +static void stac9460_set_rate_val(struct snd_ice1712 *ice, unsigned int rate) { unsigned char old, new; int idx; unsigned char changed[7]; - struct snd_ice1712 *ice = ak->private_data[0]; struct prodigy192_spec *spec = ice->spec; if (rate == 0) /* no hint - S/PDIF input is master, simply return */ @@ -357,16 +356,6 @@ static void stac9460_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate) mutex_unlock(&spec->mute_mutex); } -/* using akm infrastructure for setting rate of the codec */ -static struct snd_akm4xxx akmlike_stac9460 __devinitdata = { - .type = NON_AKM, /* special value */ - .num_adcs = 6, /* not used in any way, just for completeness */ - .num_dacs = 2, - .ops = { - .set_rate_val = stac9460_set_rate_val - } -}; - static const DECLARE_TLV_DB_SCALE(db_scale_dac, -19125, 75, 0); static const DECLARE_TLV_DB_SCALE(db_scale_adc, 0, 150, 0); @@ -642,12 +631,19 @@ static int prodigy192_ak4114_init(struct snd_ice1712 *ice) 0x41, 0x02, 0x2c, 0x00, 0x00 }; struct prodigy192_spec *spec = ice->spec; + int err; - return snd_ak4114_create(ice->card, + err = snd_ak4114_create(ice->card, prodigy192_ak4114_read, prodigy192_ak4114_write, ak4114_init_vals, ak4114_init_txcsb, ice, &spec->ak4114); + if (err < 0) + return err; + /* AK4114 in Prodigy192 cannot detect external rate correctly. + * No reason to stop capture stream due to incorrect checks */ + spec->ak4114->check_flags = AK4114_CHECK_NO_RATE; + return 0; } static void stac9460_proc_regs_read(struct snd_info_entry *entry, @@ -743,7 +739,6 @@ static int __devinit prodigy192_init(struct snd_ice1712 *ice) }; const unsigned short *p; int err = 0; - struct snd_akm4xxx *ak; struct prodigy192_spec *spec; /* prodigy 192 */ @@ -761,15 +756,7 @@ static int __devinit prodigy192_init(struct snd_ice1712 *ice) p = stac_inits_prodigy; for (; *p != (unsigned short)-1; p += 2) stac9460_put(ice, p[0], p[1]); - /* reusing the akm codecs infrastructure, - * for setting rate on stac9460 */ - ak = ice->akm = kmalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL); - if (!ak) - return -ENOMEM; - ice->akm_codecs = 1; - err = snd_ice1712_akm4xxx_init(ak, &akmlike_stac9460, NULL, ice); - if (err < 0) - return err; + ice->gpio.set_pro_rate = stac9460_set_rate_val; /* MI/ODI/O add on card with AK4114 */ if (prodigy192_miodio_exists(ice)) { @@ -825,10 +812,6 @@ struct snd_ice1712_card_info snd_vt1724_prodigy192_cards[] __devinitdata = { .build_controls = prodigy192_add_controls, .eeprom_size = sizeof(prodigy71_eeprom), .eeprom_data = prodigy71_eeprom, - /* the current MPU401 code loops infinitely - * when opening midi device - */ - .no_mpu401 = 1, }, { } /* terminator */ }; diff --git a/sound/pci/ice1712/revo.c b/sound/pci/ice1712/revo.c index 301bf929acd..4d2631434dc 100644 --- a/sound/pci/ice1712/revo.c +++ b/sound/pci/ice1712/revo.c @@ -322,17 +322,23 @@ static struct snd_pt2258 ptc_revo51_volume; static void ap192_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate) { struct snd_ice1712 *ice = ak->private_data[0]; + int dfs; revo_set_rate_val(ak, rate); -#if 1 /* FIXME: do we need this procedure? */ - /* reset DFS pin of AK5385A for ADC, too */ - /* DFS0 (pin 18) -- GPIO10 pin 77 */ - snd_ice1712_save_gpio_status(ice); - snd_ice1712_gpio_write_bits(ice, 1 << 10, - rate > 48000 ? (1 << 10) : 0); - snd_ice1712_restore_gpio_status(ice); -#endif + /* reset CKS */ + snd_ice1712_gpio_write_bits(ice, 1 << 8, rate > 96000 ? 1 << 8 : 0); + /* reset DFS pins of AK5385A for ADC, too */ + if (rate > 96000) + dfs = 2; + else if (rate > 48000) + dfs = 1; + else + dfs = 0; + snd_ice1712_gpio_write_bits(ice, 3 << 9, dfs << 9); + /* reset ADC */ + snd_ice1712_gpio_write_bits(ice, 1 << 11, 0); + snd_ice1712_gpio_write_bits(ice, 1 << 11, 1 << 11); } static const struct snd_akm4xxx_dac_channel ap192_dac[] = { @@ -353,28 +359,20 @@ static struct snd_ak4xxx_private akm_ap192_priv __devinitdata = { .cif = 0, .data_mask = VT1724_REVO_CDOUT, .clk_mask = VT1724_REVO_CCLK, - .cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS3, - .cs_addr = VT1724_REVO_CS3, - .cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS3, + .cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1, + .cs_addr = VT1724_REVO_CS1, + .cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1, .add_flags = VT1724_REVO_CCLK, /* high at init */ .mask_flags = 0, }; -#if 0 -/* FIXME: ak4114 makes the sound much lower due to some confliction, - * so let's disable it right now... - */ -#define BUILD_AK4114_AP192 -#endif - -#ifdef BUILD_AK4114_AP192 /* AK4114 support on Audiophile 192 */ /* CDTO (pin 32) -- GPIO2 pin 52 * CDTI (pin 33) -- GPIO3 pin 53 (shared with AK4358) * CCLK (pin 34) -- GPIO1 pin 51 (shared with AK4358) * CSN (pin 35) -- GPIO7 pin 59 */ -#define AK4114_ADDR 0x00 +#define AK4114_ADDR 0x02 static void write_data(struct snd_ice1712 *ice, unsigned int gpio, unsigned int data, int idx) @@ -428,7 +426,7 @@ static unsigned int ap192_4wire_start(struct snd_ice1712 *ice) tmp = snd_ice1712_gpio_read(ice); tmp |= VT1724_REVO_CCLK; /* high at init */ tmp |= VT1724_REVO_CS0; - tmp &= ~VT1724_REVO_CS3; + tmp &= ~VT1724_REVO_CS1; snd_ice1712_gpio_write(ice, tmp); udelay(1); return tmp; @@ -436,7 +434,7 @@ static unsigned int ap192_4wire_start(struct snd_ice1712 *ice) static void ap192_4wire_finish(struct snd_ice1712 *ice, unsigned int tmp) { - tmp |= VT1724_REVO_CS3; + tmp |= VT1724_REVO_CS1; tmp |= VT1724_REVO_CS0; snd_ice1712_gpio_write(ice, tmp); udelay(1); @@ -485,13 +483,17 @@ static int __devinit ap192_ak4114_init(struct snd_ice1712 *ice) struct ak4114 *ak; int err; - return snd_ak4114_create(ice->card, + err = snd_ak4114_create(ice->card, ap192_ak4114_read, ap192_ak4114_write, ak4114_init_vals, ak4114_init_txcsb, ice, &ak); + /* AK4114 in Revo cannot detect external rate correctly. + * No reason to stop capture stream due to incorrect checks */ + ak->check_flags = AK4114_CHECK_NO_RATE; + + return 0; /* error ignored; it's no fatal error */ } -#endif /* BUILD_AK4114_AP192 */ static int __devinit revo_init(struct snd_ice1712 *ice) { @@ -557,6 +559,9 @@ static int __devinit revo_init(struct snd_ice1712 *ice) if (err < 0) return err; + /* unmute all codecs */ + snd_ice1712_gpio_write_bits(ice, VT1724_REVO_MUTE, + VT1724_REVO_MUTE); break; } @@ -588,11 +593,9 @@ static int __devinit revo_add_controls(struct snd_ice1712 *ice) err = snd_ice1712_akm4xxx_build_controls(ice); if (err < 0) return err; -#ifdef BUILD_AK4114_AP192 err = ap192_ak4114_init(ice); if (err < 0) return err; -#endif break; } return 0; diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index c52abd0bf22..048d99e25ab 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -155,7 +155,8 @@ DEFINE_REGSET(SP, 0x60); /* SPDIF out */ #define ICH_PCM_SPDIF_69 0x80000000 /* s/pdif pcm on slots 6&9 */ #define ICH_PCM_SPDIF_1011 0xc0000000 /* s/pdif pcm on slots 10&11 */ #define ICH_PCM_20BIT 0x00400000 /* 20-bit samples (ICH4) */ -#define ICH_PCM_246_MASK 0x00300000 /* 6 channels (not all chips) */ +#define ICH_PCM_246_MASK 0x00300000 /* chan mask (not all chips) */ +#define ICH_PCM_8 0x00300000 /* 8 channels (not all chips) */ #define ICH_PCM_6 0x00200000 /* 6 channels (not all chips) */ #define ICH_PCM_4 0x00100000 /* 4 channels (not all chips) */ #define ICH_PCM_2 0x00000000 /* 2 channels (stereo) */ @@ -382,6 +383,7 @@ struct intel8x0 { unsigned multi4: 1, multi6: 1, + multi8 :1, dra: 1, smp20bit: 1; unsigned in_ac97_init: 1, @@ -997,6 +999,8 @@ static void snd_intel8x0_setup_pcm_out(struct intel8x0 *chip, cnt |= ICH_PCM_4; else if (runtime->channels == 6) cnt |= ICH_PCM_6; + else if (runtime->channels == 8) + cnt |= ICH_PCM_8; if (chip->device_type == DEVICE_NFORCE) { /* reset to 2ch once to keep the 6 channel data in alignment, * to start from Front Left always @@ -1106,6 +1110,16 @@ static struct snd_pcm_hw_constraint_list hw_constraints_channels6 = { .mask = 0, }; +static unsigned int channels8[] = { + 2, 4, 6, 8, +}; + +static struct snd_pcm_hw_constraint_list hw_constraints_channels8 = { + .count = ARRAY_SIZE(channels8), + .list = channels8, + .mask = 0, +}; + static int snd_intel8x0_pcm_open(struct snd_pcm_substream *substream, struct ichdev *ichdev) { struct intel8x0 *chip = snd_pcm_substream_chip(substream); @@ -1136,7 +1150,12 @@ static int snd_intel8x0_playback_open(struct snd_pcm_substream *substream) if (err < 0) return err; - if (chip->multi6) { + if (chip->multi8) { + runtime->hw.channels_max = 8; + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + &hw_constraints_channels8); + } else if (chip->multi6) { runtime->hw.channels_max = 6; snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels6); @@ -2203,8 +2222,11 @@ static int __devinit snd_intel8x0_mixer(struct intel8x0 *chip, int ac97_clock, } if (pbus->pcms[0].r[0].slots & (1 << AC97_SLOT_PCM_SLEFT)) { chip->multi4 = 1; - if (pbus->pcms[0].r[0].slots & (1 << AC97_SLOT_LFE)) + if (pbus->pcms[0].r[0].slots & (1 << AC97_SLOT_LFE)) { chip->multi6 = 1; + if (chip->ac97[0]->flags & AC97_HAS_8CH) + chip->multi8 = 1; + } } if (pbus->pcms[0].r[1].rslots[0]) { chip->dra = 1; @@ -2446,7 +2468,7 @@ static int snd_intel8x0_free(struct intel8x0 *chip) pci_write_config_dword(chip->pci, 0x4c, val); } /* --- */ - synchronize_irq(chip->irq); + __hw_end: if (chip->irq >= 0) free_irq(chip->irq, chip); @@ -2495,7 +2517,6 @@ static int intel8x0_suspend(struct pci_dev *pci, pm_message_t state) chip->sdm_saved = igetbyte(chip, ICHREG(SDM)); if (chip->irq >= 0) { - synchronize_irq(chip->irq); free_irq(chip->irq, chip); chip->irq = -1; } @@ -2648,7 +2669,7 @@ static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip) t = stop_time.tv_sec - start_time.tv_sec; t *= 1000000; t += stop_time.tv_usec - start_time.tv_usec; - printk(KERN_INFO "%s: measured %lu usecs\n", __FUNCTION__, t); + printk(KERN_INFO "%s: measured %lu usecs\n", __func__, t); if (t == 0) { snd_printk(KERN_ERR "?? calculation error..\n"); return; diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c index cadda8d6b70..faf674e671a 100644 --- a/sound/pci/intel8x0m.c +++ b/sound/pci/intel8x0m.c @@ -985,17 +985,15 @@ static int snd_intel8x0_free(struct intel8x0m *chip) /* reset channels */ for (i = 0; i < chip->bdbars_count; i++) iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, ICH_RESETREGS); - /* --- */ - synchronize_irq(chip->irq); - __hw_end: + __hw_end: + if (chip->irq >= 0) + free_irq(chip->irq, chip); if (chip->bdbars.area) snd_dma_free_pages(&chip->bdbars); if (chip->addr) pci_iounmap(chip->pci, chip->addr); if (chip->bmaddr) pci_iounmap(chip->pci, chip->bmaddr); - if (chip->irq >= 0) - free_irq(chip->irq, chip); pci_release_regions(chip->pci); pci_disable_device(chip->pci); kfree(chip); @@ -1017,7 +1015,6 @@ static int intel8x0m_suspend(struct pci_dev *pci, pm_message_t state) snd_pcm_suspend_all(chip->pcm[i]); snd_ac97_suspend(chip->ac97); if (chip->irq >= 0) { - synchronize_irq(chip->irq); free_irq(chip->irq, chip); chip->irq = -1; } diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c index 10c713d9ac4..f4c85b52bde 100644 --- a/sound/pci/korg1212/korg1212.c +++ b/sound/pci/korg1212/korg1212.c @@ -2102,7 +2102,6 @@ snd_korg1212_free(struct snd_korg1212 *korg1212) snd_korg1212_TurnOffIdleMonitor(korg1212); if (korg1212->irq >= 0) { - synchronize_irq(korg1212->irq); snd_korg1212_DisableCardInterrupts(korg1212); free_irq(korg1212->irq, korg1212); korg1212->irq = -1; diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index 04fa0a68416..a536c59fbea 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -2068,7 +2068,7 @@ static int __devinit snd_m3_mixer(struct snd_m3 *chip) { struct snd_ac97_bus *pbus; struct snd_ac97_template ac97; - struct snd_ctl_elem_id id; + struct snd_ctl_elem_id elem_id; int err; static struct snd_ac97_bus_ops ops = { .write = snd_m3_ac97_write, @@ -2088,14 +2088,14 @@ static int __devinit snd_m3_mixer(struct snd_m3 *chip) schedule_timeout_uninterruptible(msecs_to_jiffies(100)); snd_ac97_write(chip->ac97, AC97_PCM, 0); - memset(&id, 0, sizeof(id)); - id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(id.name, "Master Playback Switch"); - chip->master_switch = snd_ctl_find_id(chip->card, &id); - memset(&id, 0, sizeof(id)); - id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(id.name, "Master Playback Volume"); - chip->master_volume = snd_ctl_find_id(chip->card, &id); + memset(&elem_id, 0, sizeof(elem_id)); + elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(elem_id.name, "Master Playback Switch"); + chip->master_switch = snd_ctl_find_id(chip->card, &elem_id); + memset(&elem_id, 0, sizeof(elem_id)); + elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(elem_id.name, "Master Playback Volume"); + chip->master_volume = snd_ctl_find_id(chip->card, &elem_id); return 0; } @@ -2542,10 +2542,8 @@ static int snd_m3_free(struct snd_m3 *chip) vfree(chip->suspend_mem); #endif - if (chip->irq >= 0) { - synchronize_irq(chip->irq); + if (chip->irq >= 0) free_irq(chip->irq, chip); - } if (chip->iobase) pci_release_regions(chip->pci); @@ -2569,7 +2567,7 @@ static int m3_suspend(struct pci_dev *pci, pm_message_t state) { struct snd_card *card = pci_get_drvdata(pci); struct snd_m3 *chip = card->private_data; - int i, index; + int i, dsp_index; if (chip->suspend_mem == NULL) return 0; @@ -2583,12 +2581,12 @@ static int m3_suspend(struct pci_dev *pci, pm_message_t state) snd_m3_assp_halt(chip); /* save dsp image */ - index = 0; + dsp_index = 0; for (i = REV_B_CODE_MEMORY_BEGIN; i <= REV_B_CODE_MEMORY_END; i++) - chip->suspend_mem[index++] = + chip->suspend_mem[dsp_index++] = snd_m3_assp_read(chip, MEMTYPE_INTERNAL_CODE, i); for (i = REV_B_DATA_MEMORY_BEGIN ; i <= REV_B_DATA_MEMORY_END; i++) - chip->suspend_mem[index++] = + chip->suspend_mem[dsp_index++] = snd_m3_assp_read(chip, MEMTYPE_INTERNAL_DATA, i); pci_disable_device(pci); @@ -2601,7 +2599,7 @@ static int m3_resume(struct pci_dev *pci) { struct snd_card *card = pci_get_drvdata(pci); struct snd_m3 *chip = card->private_data; - int i, index; + int i, dsp_index; if (chip->suspend_mem == NULL) return 0; @@ -2625,13 +2623,13 @@ static int m3_resume(struct pci_dev *pci) snd_m3_ac97_reset(chip); /* restore dsp image */ - index = 0; + dsp_index = 0; for (i = REV_B_CODE_MEMORY_BEGIN; i <= REV_B_CODE_MEMORY_END; i++) snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE, i, - chip->suspend_mem[index++]); + chip->suspend_mem[dsp_index++]); for (i = REV_B_DATA_MEMORY_BEGIN ; i <= REV_B_DATA_MEMORY_END; i++) snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, i, - chip->suspend_mem[index++]); + chip->suspend_mem[dsp_index++]); /* tell the dma engine to restart itself */ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c index 7ac654e381d..7efb838d18a 100644 --- a/sound/pci/nm256/nm256.c +++ b/sound/pci/nm256/nm256.c @@ -1439,7 +1439,7 @@ static int snd_nm256_free(struct nm256 *chip) snd_nm256_capture_stop(chip); if (chip->irq >= 0) - synchronize_irq(chip->irq); + free_irq(chip->irq, chip); if (chip->cport) iounmap(chip->cport); @@ -1447,8 +1447,6 @@ static int snd_nm256_free(struct nm256 *chip) iounmap(chip->buffer); release_and_free_resource(chip->res_cport); release_and_free_resource(chip->res_buffer); - if (chip->irq >= 0) - free_irq(chip->irq, chip); pci_disable_device(chip->pci); kfree(chip->ac97_regs); diff --git a/sound/pci/oxygen/cs4362a.h b/sound/pci/oxygen/cs4362a.h new file mode 100644 index 00000000000..6a4fedf5e1e --- /dev/null +++ b/sound/pci/oxygen/cs4362a.h @@ -0,0 +1,69 @@ +/* register 01h */ +#define CS4362A_PDN 0x01 +#define CS4362A_DAC1_DIS 0x02 +#define CS4362A_DAC2_DIS 0x04 +#define CS4362A_DAC3_DIS 0x08 +#define CS4362A_MCLKDIV 0x20 +#define CS4362A_FREEZE 0x40 +#define CS4362A_CPEN 0x80 +/* register 02h */ +#define CS4362A_DIF_MASK 0x70 +#define CS4362A_DIF_LJUST 0x00 +#define CS4362A_DIF_I2S 0x10 +#define CS4362A_DIF_RJUST_16 0x20 +#define CS4362A_DIF_RJUST_24 0x30 +#define CS4362A_DIF_RJUST_20 0x40 +#define CS4362A_DIF_RJUST_18 0x50 +/* register 03h */ +#define CS4362A_MUTEC_MASK 0x03 +#define CS4362A_MUTEC_6 0x00 +#define CS4362A_MUTEC_1 0x01 +#define CS4362A_MUTEC_3 0x03 +#define CS4362A_AMUTE 0x04 +#define CS4362A_MUTEC_POL 0x08 +#define CS4362A_RMP_UP 0x10 +#define CS4362A_SNGLVOL 0x20 +#define CS4362A_ZERO_CROSS 0x40 +#define CS4362A_SOFT_RAMP 0x80 +/* register 04h */ +#define CS4362A_RMP_DN 0x01 +#define CS4362A_DEM_MASK 0x06 +#define CS4362A_DEM_NONE 0x00 +#define CS4362A_DEM_44100 0x02 +#define CS4362A_DEM_48000 0x04 +#define CS4362A_DEM_32000 0x06 +#define CS4362A_FILT_SEL 0x10 +/* register 05h */ +#define CS4362A_INV_A1 0x01 +#define CS4362A_INV_B1 0x02 +#define CS4362A_INV_A2 0x04 +#define CS4362A_INV_B2 0x08 +#define CS4362A_INV_A3 0x10 +#define CS4362A_INV_B3 0x20 +/* register 06h */ +#define CS4362A_FM_MASK 0x03 +#define CS4362A_FM_SINGLE 0x00 +#define CS4362A_FM_DOUBLE 0x01 +#define CS4362A_FM_QUAD 0x02 +#define CS4362A_FM_DSD 0x03 +#define CS4362A_ATAPI_MASK 0x7c +#define CS4362A_ATAPI_B_MUTE 0x00 +#define CS4362A_ATAPI_B_R 0x04 +#define CS4362A_ATAPI_B_L 0x08 +#define CS4362A_ATAPI_B_LR 0x0c +#define CS4362A_ATAPI_A_MUTE 0x00 +#define CS4362A_ATAPI_A_R 0x10 +#define CS4362A_ATAPI_A_L 0x20 +#define CS4362A_ATAPI_A_LR 0x30 +#define CS4362A_ATAPI_MIX_LR_VOL 0x40 +#define CS4362A_A_EQ_B 0x80 +/* register 07h */ +#define CS4362A_VOL_MASK 0x7f +#define CS4362A_MUTE 0x80 +/* register 08h: like 07h */ +/* registers 09h..0Bh: like 06h..08h */ +/* registers 0Ch..0Eh: like 06h..08h */ +/* register 12h */ +#define CS4362A_REV_MASK 0x07 +#define CS4362A_PART_MASK 0xf8 +#define CS4362A_PART_CS4362A 0x50 diff --git a/sound/pci/oxygen/cs4398.h b/sound/pci/oxygen/cs4398.h new file mode 100644 index 00000000000..5faf5efc882 --- /dev/null +++ b/sound/pci/oxygen/cs4398.h @@ -0,0 +1,69 @@ +/* register 1 */ +#define CS4398_REV_MASK 0x07 +#define CS4398_PART_MASK 0xf8 +#define CS4398_PART_CS4398 0x70 +/* register 2 */ +#define CS4398_FM_MASK 0x03 +#define CS4398_FM_SINGLE 0x00 +#define CS4398_FM_DOUBLE 0x01 +#define CS4398_FM_QUAD 0x02 +#define CS4398_FM_DSD 0x03 +#define CS4398_DEM_MASK 0x0c +#define CS4398_DEM_NONE 0x00 +#define CS4398_DEM_44100 0x04 +#define CS4398_DEM_48000 0x08 +#define CS4398_DEM_32000 0x0c +#define CS4398_DIF_MASK 0x70 +#define CS4398_DIF_LJUST 0x00 +#define CS4398_DIF_I2S 0x10 +#define CS4398_DIF_RJUST_16 0x20 +#define CS4398_DIF_RJUST_24 0x30 +#define CS4398_DIF_RJUST_20 0x40 +#define CS4398_DIF_RJUST_18 0x50 +#define CS4398_DSD_SRC 0x80 +/* register 3 */ +#define CS4398_ATAPI_MASK 0x1f +#define CS4398_ATAPI_B_MUTE 0x00 +#define CS4398_ATAPI_B_R 0x01 +#define CS4398_ATAPI_B_L 0x02 +#define CS4398_ATAPI_B_LR 0x03 +#define CS4398_ATAPI_A_MUTE 0x00 +#define CS4398_ATAPI_A_R 0x04 +#define CS4398_ATAPI_A_L 0x08 +#define CS4398_ATAPI_A_LR 0x0c +#define CS4398_ATAPI_MIX_LR_VOL 0x10 +#define CS4398_INVERT_B 0x20 +#define CS4398_INVERT_A 0x40 +#define CS4398_VOL_B_EQ_A 0x80 +/* register 4 */ +#define CS4398_MUTEP_MASK 0x03 +#define CS4398_MUTEP_AUTO 0x00 +#define CS4398_MUTEP_LOW 0x02 +#define CS4398_MUTEP_HIGH 0x03 +#define CS4398_MUTE_B 0x08 +#define CS4398_MUTE_A 0x10 +#define CS4398_MUTEC_A_EQ_B 0x20 +#define CS4398_DAMUTE 0x40 +#define CS4398_PAMUTE 0x80 +/* register 5 */ +#define CS4398_VOL_A_MASK 0xff +/* register 6 */ +#define CS4398_VOL_B_MASK 0xff +/* register 7 */ +#define CS4398_DIR_DSD 0x01 +#define CS4398_FILT_SEL 0x04 +#define CS4398_RMP_DN 0x10 +#define CS4398_RMP_UP 0x20 +#define CS4398_ZERO_CROSS 0x40 +#define CS4398_SOFT_RAMP 0x80 +/* register 8 */ +#define CS4398_MCLKDIV3 0x08 +#define CS4398_MCLKDIV2 0x10 +#define CS4398_FREEZE 0x20 +#define CS4398_CPEN 0x40 +#define CS4398_PDN 0x80 +/* register 9 */ +#define CS4398_DSD_PM_EN 0x01 +#define CS4398_DSD_PM_MODE 0x02 +#define CS4398_INVALID_DSD 0x04 +#define CS4398_STATIC_DSD 0x08 diff --git a/sound/pci/oxygen/hifier.c b/sound/pci/oxygen/hifier.c index 666f69a3312..090dd4354a2 100644 --- a/sound/pci/oxygen/hifier.c +++ b/sound/pci/oxygen/hifier.c @@ -66,12 +66,12 @@ static void hifier_init(struct oxygen *chip) { struct hifier_data *data = chip->model_data; - data->ak4396_ctl2 = AK4396_DEM_OFF | AK4396_DFS_NORMAL; + data->ak4396_ctl2 = AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL; ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN); ak4396_write(chip, AK4396_CONTROL_2, data->ak4396_ctl2); ak4396_write(chip, AK4396_CONTROL_3, AK4396_PCM); - ak4396_write(chip, AK4396_LCH_ATT, 0xff); - ak4396_write(chip, AK4396_RCH_ATT, 0xff); + ak4396_write(chip, AK4396_LCH_ATT, 0); + ak4396_write(chip, AK4396_RCH_ATT, 0); snd_component_add(chip->card, "AK4396"); snd_component_add(chip->card, "CS5340"); @@ -127,22 +127,8 @@ static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0); static int hifier_control_filter(struct snd_kcontrol_new *template) { - if (!strcmp(template->name, "Master Playback Volume")) { - template->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; - template->tlv.p = ak4396_db_scale; - } else if (!strcmp(template->name, "Stereo Upmixing")) { + if (!strcmp(template->name, "Stereo Upmixing")) return 1; /* stereo only - we don't need upmixing */ - } else if (!strcmp(template->name, - SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK)) || - !strcmp(template->name, - SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT))) { - return 1; /* no digital input */ - } - return 0; -} - -static int hifier_mixer_init(struct oxygen *chip) -{ return 0; } @@ -153,18 +139,20 @@ static const struct oxygen_model model_hifier = { .owner = THIS_MODULE, .init = hifier_init, .control_filter = hifier_control_filter, - .mixer_init = hifier_mixer_init, .cleanup = hifier_cleanup, .set_dac_params = set_ak4396_params, .set_adc_params = set_cs5340_params, .update_dac_volume = update_ak4396_volume, .update_dac_mute = update_ak4396_mute, + .dac_tlv = ak4396_db_scale, .model_data_size = sizeof(struct hifier_data), + .pcm_dev_cfg = PLAYBACK_0_TO_I2S | + PLAYBACK_1_TO_SPDIF | + CAPTURE_0_FROM_I2S_1, .dac_channels = 2, - .used_channels = OXYGEN_CHANNEL_A | - OXYGEN_CHANNEL_SPDIF | - OXYGEN_CHANNEL_MULTICH, - .function_flags = 0, + .dac_volume_min = 0, + .dac_volume_max = 255, + .function_flags = OXYGEN_FUNCTION_SPI, .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, }; @@ -181,7 +169,7 @@ static int __devinit hifier_probe(struct pci_dev *pci, ++dev; return -ENOENT; } - err = oxygen_pci_probe(pci, index[dev], id[dev], 0, &model_hifier); + err = oxygen_pci_probe(pci, index[dev], id[dev], &model_hifier); if (err >= 0) ++dev; return err; diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c index 9a9941bb046..63f185c1ed1 100644 --- a/sound/pci/oxygen/oxygen.c +++ b/sound/pci/oxygen/oxygen.c @@ -39,7 +39,7 @@ #include <sound/tlv.h> #include "oxygen.h" #include "ak4396.h" -#include "cm9780.h" +#include "wm8785.h" MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); MODULE_DESCRIPTION("C-Media CMI8788 driver"); @@ -78,49 +78,6 @@ MODULE_DEVICE_TABLE(pci, oxygen_ids); #define GPIO_AK5385_DFS_DOUBLE 0x0001 #define GPIO_AK5385_DFS_QUAD 0x0002 -#define GPIO_LINE_MUTE CM9780_GPO0 - -#define WM8785_R0 0 -#define WM8785_R1 1 -#define WM8785_R2 2 -#define WM8785_R7 7 - -/* R0 */ -#define WM8785_MCR_MASK 0x007 -#define WM8785_MCR_SLAVE 0x000 -#define WM8785_MCR_MASTER_128 0x001 -#define WM8785_MCR_MASTER_192 0x002 -#define WM8785_MCR_MASTER_256 0x003 -#define WM8785_MCR_MASTER_384 0x004 -#define WM8785_MCR_MASTER_512 0x005 -#define WM8785_MCR_MASTER_768 0x006 -#define WM8785_OSR_MASK 0x018 -#define WM8785_OSR_SINGLE 0x000 -#define WM8785_OSR_DOUBLE 0x008 -#define WM8785_OSR_QUAD 0x010 -#define WM8785_FORMAT_MASK 0x060 -#define WM8785_FORMAT_RJUST 0x000 -#define WM8785_FORMAT_LJUST 0x020 -#define WM8785_FORMAT_I2S 0x040 -#define WM8785_FORMAT_DSP 0x060 -/* R1 */ -#define WM8785_WL_MASK 0x003 -#define WM8785_WL_16 0x000 -#define WM8785_WL_20 0x001 -#define WM8785_WL_24 0x002 -#define WM8785_WL_32 0x003 -#define WM8785_LRP 0x004 -#define WM8785_BCLKINV 0x008 -#define WM8785_LRSWAP 0x010 -#define WM8785_DEVNO_MASK 0x0e0 -/* R2 */ -#define WM8785_HPFR 0x001 -#define WM8785_HPFL 0x002 -#define WM8785_SDODIS 0x004 -#define WM8785_PWRDNR 0x008 -#define WM8785_PWRDNL 0x010 -#define WM8785_TDM_MASK 0x1c0 - struct generic_data { u8 ak4396_ctl2; }; @@ -155,7 +112,7 @@ static void ak4396_init(struct oxygen *chip) struct generic_data *data = chip->model_data; unsigned int i; - data->ak4396_ctl2 = AK4396_DEM_OFF | AK4396_DFS_NORMAL; + data->ak4396_ctl2 = AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL; for (i = 0; i < 4; ++i) { ak4396_write(chip, i, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN); @@ -163,8 +120,8 @@ static void ak4396_init(struct oxygen *chip) AK4396_CONTROL_2, data->ak4396_ctl2); ak4396_write(chip, i, AK4396_CONTROL_3, AK4396_PCM); - ak4396_write(chip, i, AK4396_LCH_ATT, 0xff); - ak4396_write(chip, i, AK4396_RCH_ATT, 0xff); + ak4396_write(chip, i, AK4396_LCH_ATT, 0); + ak4396_write(chip, i, AK4396_RCH_ATT, 0); } snd_component_add(chip->card, "AK4396"); } @@ -185,23 +142,16 @@ static void wm8785_init(struct oxygen *chip) snd_component_add(chip->card, "WM8785"); } -static void cmi9780_init(struct oxygen *chip) -{ - oxygen_ac97_clear_bits(chip, 0, CM9780_GPIO_STATUS, GPIO_LINE_MUTE); -} - static void generic_init(struct oxygen *chip) { ak4396_init(chip); wm8785_init(chip); - cmi9780_init(chip); } static void meridian_init(struct oxygen *chip) { ak4396_init(chip); ak5385_init(chip); - cmi9780_init(chip); } static void generic_cleanup(struct oxygen *chip) @@ -297,59 +247,32 @@ static void set_ak5385_params(struct oxygen *chip, value, GPIO_AK5385_DFS_MASK); } -static void cmi9780_switch_hook(struct oxygen *chip, unsigned int codec, - unsigned int reg, int mute) -{ - if (codec != 0) - return; - switch (reg) { - case AC97_LINE: - oxygen_write_ac97_masked(chip, 0, CM9780_GPIO_STATUS, - mute ? GPIO_LINE_MUTE : 0, - GPIO_LINE_MUTE); - break; - case AC97_MIC: - case AC97_CD: - case AC97_AUX: - if (!mute) - oxygen_ac97_set_bits(chip, 0, CM9780_GPIO_STATUS, - GPIO_LINE_MUTE); - break; - } -} - static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0); -static int ak4396_control_filter(struct snd_kcontrol_new *template) -{ - if (!strcmp(template->name, "Master Playback Volume")) { - template->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; - template->tlv.p = ak4396_db_scale; - } - return 0; -} - static const struct oxygen_model model_generic = { .shortname = "C-Media CMI8788", .longname = "C-Media Oxygen HD Audio", .chip = "CMI8788", .owner = THIS_MODULE, .init = generic_init, - .control_filter = ak4396_control_filter, .cleanup = generic_cleanup, .set_dac_params = set_ak4396_params, .set_adc_params = set_wm8785_params, .update_dac_volume = update_ak4396_volume, .update_dac_mute = update_ak4396_mute, - .ac97_switch_hook = cmi9780_switch_hook, + .dac_tlv = ak4396_db_scale, .model_data_size = sizeof(struct generic_data), + .pcm_dev_cfg = PLAYBACK_0_TO_I2S | + PLAYBACK_1_TO_SPDIF | + PLAYBACK_2_TO_AC97_1 | + CAPTURE_0_FROM_I2S_1 | + CAPTURE_1_FROM_SPDIF | + CAPTURE_2_FROM_AC97_1, .dac_channels = 8, - .used_channels = OXYGEN_CHANNEL_A | - OXYGEN_CHANNEL_C | - OXYGEN_CHANNEL_SPDIF | - OXYGEN_CHANNEL_MULTICH | - OXYGEN_CHANNEL_AC97, - .function_flags = OXYGEN_FUNCTION_ENABLE_SPI_4_5, + .dac_volume_min = 0, + .dac_volume_max = 255, + .function_flags = OXYGEN_FUNCTION_SPI | + OXYGEN_FUNCTION_ENABLE_SPI_4_5, .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, }; @@ -359,21 +282,25 @@ static const struct oxygen_model model_meridian = { .chip = "CMI8788", .owner = THIS_MODULE, .init = meridian_init, - .control_filter = ak4396_control_filter, .cleanup = generic_cleanup, .set_dac_params = set_ak4396_params, .set_adc_params = set_ak5385_params, .update_dac_volume = update_ak4396_volume, .update_dac_mute = update_ak4396_mute, - .ac97_switch_hook = cmi9780_switch_hook, + .dac_tlv = ak4396_db_scale, .model_data_size = sizeof(struct generic_data), + .pcm_dev_cfg = PLAYBACK_0_TO_I2S | + PLAYBACK_1_TO_SPDIF | + PLAYBACK_2_TO_AC97_1 | + CAPTURE_0_FROM_I2S_2 | + CAPTURE_1_FROM_SPDIF | + CAPTURE_2_FROM_AC97_1, .dac_channels = 8, - .used_channels = OXYGEN_CHANNEL_B | - OXYGEN_CHANNEL_C | - OXYGEN_CHANNEL_SPDIF | - OXYGEN_CHANNEL_MULTICH | - OXYGEN_CHANNEL_AC97, - .function_flags = OXYGEN_FUNCTION_ENABLE_SPI_4_5, + .dac_volume_min = 0, + .dac_volume_max = 255, + .misc_flags = OXYGEN_MISC_MIDI, + .function_flags = OXYGEN_FUNCTION_SPI | + OXYGEN_FUNCTION_ENABLE_SPI_4_5, .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, }; @@ -392,7 +319,7 @@ static int __devinit generic_oxygen_probe(struct pci_dev *pci, return -ENOENT; } is_meridian = pci_id->driver_data; - err = oxygen_pci_probe(pci, index[dev], id[dev], is_meridian, + err = oxygen_pci_probe(pci, index[dev], id[dev], is_meridian ? &model_meridian : &model_generic); if (err >= 0) ++dev; diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h index ad50fb8b206..a71c6e05926 100644 --- a/sound/pci/oxygen/oxygen.h +++ b/sound/pci/oxygen/oxygen.h @@ -16,6 +16,16 @@ #define PCM_AC97 5 #define PCM_COUNT 6 +/* model-specific configuration of outputs/inputs */ +#define PLAYBACK_0_TO_I2S 0x001 +#define PLAYBACK_1_TO_SPDIF 0x004 +#define PLAYBACK_2_TO_AC97_1 0x008 +#define CAPTURE_0_FROM_I2S_1 0x010 +#define CAPTURE_0_FROM_I2S_2 0x020 +#define CAPTURE_1_FROM_SPDIF 0x080 +#define CAPTURE_2_FROM_I2S_2 0x100 +#define CAPTURE_2_FROM_AC97_1 0x200 + enum { CONTROL_SPDIF_PCM, CONTROL_SPDIF_INPUT_BITS, @@ -87,12 +97,16 @@ struct oxygen_model { struct snd_pcm_hw_params *params); void (*update_dac_volume)(struct oxygen *chip); void (*update_dac_mute)(struct oxygen *chip); - void (*ac97_switch_hook)(struct oxygen *chip, unsigned int codec, - unsigned int reg, int mute); void (*gpio_changed)(struct oxygen *chip); + void (*ac97_switch)(struct oxygen *chip, + unsigned int reg, unsigned int mute); + const unsigned int *dac_tlv; size_t model_data_size; + unsigned int pcm_dev_cfg; u8 dac_channels; - u8 used_channels; + u8 dac_volume_min; + u8 dac_volume_max; + u8 misc_flags; u8 function_flags; u16 dac_i2s_format; u16 adc_i2s_format; @@ -100,7 +114,7 @@ struct oxygen_model { /* oxygen_lib.c */ -int oxygen_pci_probe(struct pci_dev *pci, int index, char *id, int midi, +int oxygen_pci_probe(struct pci_dev *pci, int index, char *id, const struct oxygen_model *model); void oxygen_pci_remove(struct pci_dev *pci); @@ -137,6 +151,7 @@ void oxygen_write_ac97_masked(struct oxygen *chip, unsigned int codec, unsigned int index, u16 data, u16 mask); void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data); +void oxygen_write_i2c(struct oxygen *chip, u8 device, u8 map, u8 data); static inline void oxygen_set_bits8(struct oxygen *chip, unsigned int reg, u8 value) diff --git a/sound/pci/oxygen/oxygen_io.c b/sound/pci/oxygen/oxygen_io.c index 74e23ef9c94..5569606ee87 100644 --- a/sound/pci/oxygen/oxygen_io.c +++ b/sound/pci/oxygen/oxygen_io.c @@ -190,12 +190,31 @@ void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data) --count; } - spin_lock_irq(&chip->reg_lock); oxygen_write8(chip, OXYGEN_SPI_DATA1, data); oxygen_write8(chip, OXYGEN_SPI_DATA2, data >> 8); if (control & OXYGEN_SPI_DATA_LENGTH_3) oxygen_write8(chip, OXYGEN_SPI_DATA3, data >> 16); oxygen_write8(chip, OXYGEN_SPI_CONTROL, control); - spin_unlock_irq(&chip->reg_lock); } EXPORT_SYMBOL(oxygen_write_spi); + +void oxygen_write_i2c(struct oxygen *chip, u8 device, u8 map, u8 data) +{ + unsigned long timeout; + + /* should not need more than about 300 us */ + timeout = jiffies + msecs_to_jiffies(1); + do { + if (!(oxygen_read16(chip, OXYGEN_2WIRE_BUS_STATUS) + & OXYGEN_2WIRE_BUSY)) + break; + udelay(1); + cond_resched(); + } while (time_after_eq(timeout, jiffies)); + + oxygen_write8(chip, OXYGEN_2WIRE_MAP, map); + oxygen_write8(chip, OXYGEN_2WIRE_DATA, data); + oxygen_write8(chip, OXYGEN_2WIRE_CONTROL, + device | OXYGEN_2WIRE_DIR_WRITE); +} +EXPORT_SYMBOL(oxygen_write_i2c); diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c index 78c21155218..897697d4350 100644 --- a/sound/pci/oxygen/oxygen_lib.c +++ b/sound/pci/oxygen/oxygen_lib.c @@ -221,7 +221,8 @@ static void oxygen_init(struct oxygen *chip) chip->dac_routing = 1; for (i = 0; i < 8; ++i) - chip->dac_volume[i] = 0xff; + chip->dac_volume[i] = chip->model->dac_volume_min; + chip->dac_mute = 1; chip->spdif_playback_enable = 1; chip->spdif_bits = OXYGEN_SPDIF_C | OXYGEN_SPDIF_ORIGINAL | (IEC958_AES1_CON_PCM_CODER << OXYGEN_SPDIF_CATEGORY_SHIFT); @@ -240,12 +241,12 @@ static void oxygen_init(struct oxygen *chip) chip->has_ac97_0 = (i & OXYGEN_AC97_CODEC_0) != 0; chip->has_ac97_1 = (i & OXYGEN_AC97_CODEC_1) != 0; - oxygen_set_bits8(chip, OXYGEN_FUNCTION, - OXYGEN_FUNCTION_RESET_CODEC | - chip->model->function_flags); oxygen_write8_masked(chip, OXYGEN_FUNCTION, - OXYGEN_FUNCTION_SPI, - OXYGEN_FUNCTION_2WIRE_SPI_MASK); + OXYGEN_FUNCTION_RESET_CODEC | + chip->model->function_flags, + OXYGEN_FUNCTION_RESET_CODEC | + OXYGEN_FUNCTION_2WIRE_SPI_MASK | + OXYGEN_FUNCTION_ENABLE_SPI_4_5); oxygen_write8(chip, OXYGEN_DMA_STATUS, 0); oxygen_write8(chip, OXYGEN_DMA_PAUSE, 0); oxygen_write8(chip, OXYGEN_PLAY_CHANNELS, @@ -253,11 +254,13 @@ static void oxygen_init(struct oxygen *chip) OXYGEN_DMA_A_BURST_8 | OXYGEN_DMA_MULTICH_BURST_8); oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0); - oxygen_write8_masked(chip, OXYGEN_MISC, 0, + oxygen_write8_masked(chip, OXYGEN_MISC, + chip->model->misc_flags, OXYGEN_MISC_WRITE_PCI_SUBID | OXYGEN_MISC_REC_C_FROM_SPDIF | OXYGEN_MISC_REC_B_FROM_AC97 | - OXYGEN_MISC_REC_A_FROM_MULTICH); + OXYGEN_MISC_REC_A_FROM_MULTICH | + OXYGEN_MISC_MIDI); oxygen_write8(chip, OXYGEN_REC_FORMAT, (OXYGEN_FORMAT_16 << OXYGEN_REC_FORMAT_A_SHIFT) | (OXYGEN_FORMAT_16 << OXYGEN_REC_FORMAT_B_SHIFT) | @@ -267,35 +270,49 @@ static void oxygen_init(struct oxygen *chip) (OXYGEN_FORMAT_16 << OXYGEN_MULTICH_FORMAT_SHIFT)); oxygen_write8(chip, OXYGEN_REC_CHANNELS, OXYGEN_REC_CHANNELS_2_2_2); oxygen_write16(chip, OXYGEN_I2S_MULTICH_FORMAT, - OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST | - OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 | - OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64); - oxygen_write16(chip, OXYGEN_I2S_A_FORMAT, - OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST | - OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 | - OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64); - oxygen_write16(chip, OXYGEN_I2S_B_FORMAT, - OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST | - OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 | + OXYGEN_RATE_48000 | chip->model->dac_i2s_format | + OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 | OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64); + if (chip->model->pcm_dev_cfg & CAPTURE_0_FROM_I2S_1) + oxygen_write16(chip, OXYGEN_I2S_A_FORMAT, + OXYGEN_RATE_48000 | chip->model->adc_i2s_format | + OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 | + OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64); + else + oxygen_write16(chip, OXYGEN_I2S_A_FORMAT, + OXYGEN_I2S_MASTER | OXYGEN_I2S_MUTE_MCLK); + if (chip->model->pcm_dev_cfg & (CAPTURE_0_FROM_I2S_2 | + CAPTURE_2_FROM_I2S_2)) + oxygen_write16(chip, OXYGEN_I2S_B_FORMAT, + OXYGEN_RATE_48000 | chip->model->adc_i2s_format | + OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 | + OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64); + else + oxygen_write16(chip, OXYGEN_I2S_B_FORMAT, + OXYGEN_I2S_MASTER | OXYGEN_I2S_MUTE_MCLK); oxygen_write16(chip, OXYGEN_I2S_C_FORMAT, - OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST | - OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 | - OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64); - oxygen_write32_masked(chip, OXYGEN_SPDIF_CONTROL, - OXYGEN_SPDIF_SENSE_MASK | - OXYGEN_SPDIF_LOCK_MASK | - OXYGEN_SPDIF_RATE_MASK | - OXYGEN_SPDIF_LOCK_PAR | - OXYGEN_SPDIF_IN_CLOCK_96, - OXYGEN_SPDIF_OUT_ENABLE | - OXYGEN_SPDIF_LOOPBACK | - OXYGEN_SPDIF_SENSE_MASK | - OXYGEN_SPDIF_LOCK_MASK | - OXYGEN_SPDIF_RATE_MASK | - OXYGEN_SPDIF_SENSE_PAR | - OXYGEN_SPDIF_LOCK_PAR | - OXYGEN_SPDIF_IN_CLOCK_MASK); + OXYGEN_I2S_MASTER | OXYGEN_I2S_MUTE_MCLK); + oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL, + OXYGEN_SPDIF_OUT_ENABLE | + OXYGEN_SPDIF_LOOPBACK); + if (chip->model->pcm_dev_cfg & CAPTURE_1_FROM_SPDIF) + oxygen_write32_masked(chip, OXYGEN_SPDIF_CONTROL, + OXYGEN_SPDIF_SENSE_MASK | + OXYGEN_SPDIF_LOCK_MASK | + OXYGEN_SPDIF_RATE_MASK | + OXYGEN_SPDIF_LOCK_PAR | + OXYGEN_SPDIF_IN_CLOCK_96, + OXYGEN_SPDIF_SENSE_MASK | + OXYGEN_SPDIF_LOCK_MASK | + OXYGEN_SPDIF_RATE_MASK | + OXYGEN_SPDIF_SENSE_PAR | + OXYGEN_SPDIF_LOCK_PAR | + OXYGEN_SPDIF_IN_CLOCK_MASK); + else + oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL, + OXYGEN_SPDIF_SENSE_MASK | + OXYGEN_SPDIF_LOCK_MASK | + OXYGEN_SPDIF_RATE_MASK); oxygen_write32(chip, OXYGEN_SPDIF_OUTPUT_BITS, chip->spdif_bits); oxygen_clear_bits8(chip, OXYGEN_MPU401_CONTROL, OXYGEN_MPU401_LOOPBACK); oxygen_write8(chip, OXYGEN_GPI_INTERRUPT_MASK, 0); @@ -318,9 +335,12 @@ static void oxygen_init(struct oxygen *chip) (2 << OXYGEN_A_MONITOR_ROUTE_2_SHIFT) | (3 << OXYGEN_A_MONITOR_ROUTE_3_SHIFT)); - oxygen_write8(chip, OXYGEN_AC97_INTERRUPT_MASK, - OXYGEN_AC97_INT_READ_DONE | - OXYGEN_AC97_INT_WRITE_DONE); + if (chip->has_ac97_0 | chip->has_ac97_1) + oxygen_write8(chip, OXYGEN_AC97_INTERRUPT_MASK, + OXYGEN_AC97_INT_READ_DONE | + OXYGEN_AC97_INT_WRITE_DONE); + else + oxygen_write8(chip, OXYGEN_AC97_INTERRUPT_MASK, 0); oxygen_write32(chip, OXYGEN_AC97_OUT_CONFIG, 0); oxygen_write32(chip, OXYGEN_AC97_IN_CONFIG, 0); if (!(chip->has_ac97_0 | chip->has_ac97_1)) @@ -351,6 +371,8 @@ static void oxygen_init(struct oxygen *chip) oxygen_write_ac97(chip, 0, AC97_REC_GAIN, 0x8000); oxygen_write_ac97(chip, 0, AC97_CENTER_LFE_MASTER, 0x8080); oxygen_write_ac97(chip, 0, AC97_SURROUND_MASTER, 0x8080); + oxygen_ac97_clear_bits(chip, 0, CM9780_GPIO_STATUS, + CM9780_GPO0); /* power down unused ADCs and DACs */ oxygen_ac97_set_bits(chip, 0, AC97_POWERDOWN, AC97_PD_PR0 | AC97_PD_PR1); @@ -388,10 +410,8 @@ static void oxygen_card_free(struct snd_card *card) oxygen_write16(chip, OXYGEN_DMA_STATUS, 0); oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0); spin_unlock_irq(&chip->reg_lock); - if (chip->irq >= 0) { + if (chip->irq >= 0) free_irq(chip->irq, chip); - synchronize_irq(chip->irq); - } flush_scheduled_work(); chip->model->cleanup(chip); mutex_destroy(&chip->mutex); @@ -400,7 +420,7 @@ static void oxygen_card_free(struct snd_card *card) } int oxygen_pci_probe(struct pci_dev *pci, int index, char *id, - int midi, const struct oxygen_model *model) + const struct oxygen_model *model) { struct snd_card *card; struct oxygen *chip; @@ -472,9 +492,7 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id, if (err < 0) goto err_card; - oxygen_write8_masked(chip, OXYGEN_MISC, - midi ? OXYGEN_MISC_MIDI : 0, OXYGEN_MISC_MIDI); - if (midi) { + if (model->misc_flags & OXYGEN_MISC_MIDI) { err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI, chip->addr + OXYGEN_MPU401, MPU401_INFO_INTEGRATED, 0, 0, @@ -486,7 +504,10 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id, oxygen_proc_init(chip); spin_lock_irq(&chip->reg_lock); - chip->interrupt_mask |= OXYGEN_INT_SPDIF_IN_DETECT | OXYGEN_INT_AC97; + if (chip->model->pcm_dev_cfg & CAPTURE_1_FROM_SPDIF) + chip->interrupt_mask |= OXYGEN_INT_SPDIF_IN_DETECT; + if (chip->has_ac97_0 | chip->has_ac97_1) + chip->interrupt_mask |= OXYGEN_INT_AC97; oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask); spin_unlock_irq(&chip->reg_lock); diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c index a8e4623415d..cc0cddadd58 100644 --- a/sound/pci/oxygen/oxygen_mixer.c +++ b/sound/pci/oxygen/oxygen_mixer.c @@ -32,8 +32,8 @@ static int dac_volume_info(struct snd_kcontrol *ctl, info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; info->count = chip->model->dac_channels; - info->value.integer.min = 0; - info->value.integer.max = 0xff; + info->value.integer.min = chip->model->dac_volume_min; + info->value.integer.max = chip->model->dac_volume_max; return 0; } @@ -446,6 +446,50 @@ static int spdif_loopback_put(struct snd_kcontrol *ctl, return changed; } +static int monitor_volume_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) +{ + info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + info->count = 1; + info->value.integer.min = 0; + info->value.integer.max = 1; + return 0; +} + +static int monitor_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + u8 bit = ctl->private_value; + int invert = ctl->private_value & (1 << 8); + + value->value.integer.value[0] = + !!invert ^ !!(oxygen_read8(chip, OXYGEN_ADC_MONITOR) & bit); + return 0; +} + +static int monitor_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + u8 bit = ctl->private_value; + int invert = ctl->private_value & (1 << 8); + u8 oldreg, newreg; + int changed; + + spin_lock_irq(&chip->reg_lock); + oldreg = oxygen_read8(chip, OXYGEN_ADC_MONITOR); + if ((!!value->value.integer.value[0] ^ !!invert) != 0) + newreg = oldreg | bit; + else + newreg = oldreg & ~bit; + changed = newreg != oldreg; + if (changed) + oxygen_write8(chip, OXYGEN_ADC_MONITOR, newreg); + spin_unlock_irq(&chip->reg_lock); + return changed; +} + static int ac97_switch_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) { @@ -466,6 +510,21 @@ static int ac97_switch_get(struct snd_kcontrol *ctl, return 0; } +static void mute_ac97_ctl(struct oxygen *chip, unsigned int control) +{ + unsigned int priv_idx = chip->controls[control]->private_value & 0xff; + u16 value; + + value = oxygen_read_ac97(chip, 0, priv_idx); + if (!(value & 0x8000)) { + oxygen_write_ac97(chip, 0, priv_idx, value | 0x8000); + if (chip->model->ac97_switch) + chip->model->ac97_switch(chip, priv_idx, 0x8000); + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &chip->controls[control]->id); + } +} + static int ac97_switch_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) { @@ -487,9 +546,24 @@ static int ac97_switch_put(struct snd_kcontrol *ctl, change = newreg != oldreg; if (change) { oxygen_write_ac97(chip, codec, index, newreg); - if (bitnr == 15 && chip->model->ac97_switch_hook) - chip->model->ac97_switch_hook(chip, codec, index, - newreg & 0x8000); + if (codec == 0 && chip->model->ac97_switch) + chip->model->ac97_switch(chip, index, newreg & 0x8000); + if (index == AC97_LINE) { + oxygen_write_ac97_masked(chip, 0, CM9780_GPIO_STATUS, + newreg & 0x8000 ? + CM9780_GPO0 : 0, CM9780_GPO0); + if (!(newreg & 0x8000)) { + mute_ac97_ctl(chip, CONTROL_MIC_CAPTURE_SWITCH); + mute_ac97_ctl(chip, CONTROL_CD_CAPTURE_SWITCH); + mute_ac97_ctl(chip, CONTROL_AUX_CAPTURE_SWITCH); + } + } else if ((index == AC97_MIC || index == AC97_CD || + index == AC97_VIDEO || index == AC97_AUX) && + bitnr == 15 && !(newreg & 0x8000)) { + mute_ac97_ctl(chip, CONTROL_LINE_CAPTURE_SWITCH); + oxygen_write_ac97_masked(chip, 0, CM9780_GPIO_STATUS, + CM9780_GPO0, CM9780_GPO0); + } } mutex_unlock(&chip->mutex); return change; @@ -608,6 +682,7 @@ static int ac97_fp_rec_volume_put(struct snd_kcontrol *ctl, .private_value = ((codec) << 24) | (index), \ } +static DECLARE_TLV_DB_SCALE(monitor_db_scale, -1000, 1000, 0); static DECLARE_TLV_DB_SCALE(ac97_db_scale, -3450, 150, 0); static DECLARE_TLV_DB_SCALE(ac97_rec_db_scale, 0, 150, 0); @@ -667,6 +742,9 @@ static const struct snd_kcontrol_new controls[] = { .get = spdif_pcm_get, .put = spdif_pcm_put, }, +}; + +static const struct snd_kcontrol_new spdif_input_controls[] = { { .iface = SNDRV_CTL_ELEM_IFACE_PCM, .device = 1, @@ -692,11 +770,118 @@ static const struct snd_kcontrol_new controls[] = { }, }; +static const struct { + unsigned int pcm_dev; + struct snd_kcontrol_new controls[2]; +} monitor_controls[] = { + { + .pcm_dev = CAPTURE_0_FROM_I2S_1, + .controls = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Input Monitor Switch", + .info = snd_ctl_boolean_mono_info, + .get = monitor_get, + .put = monitor_put, + .private_value = OXYGEN_ADC_MONITOR_A, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Input Monitor Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .info = monitor_volume_info, + .get = monitor_get, + .put = monitor_put, + .private_value = OXYGEN_ADC_MONITOR_A_HALF_VOL + | (1 << 8), + .tlv = { .p = monitor_db_scale, }, + }, + }, + }, + { + .pcm_dev = CAPTURE_0_FROM_I2S_2, + .controls = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Input Monitor Switch", + .info = snd_ctl_boolean_mono_info, + .get = monitor_get, + .put = monitor_put, + .private_value = OXYGEN_ADC_MONITOR_B, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Input Monitor Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .info = monitor_volume_info, + .get = monitor_get, + .put = monitor_put, + .private_value = OXYGEN_ADC_MONITOR_B_HALF_VOL + | (1 << 8), + .tlv = { .p = monitor_db_scale, }, + }, + }, + }, + { + .pcm_dev = CAPTURE_2_FROM_I2S_2, + .controls = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Input Monitor Switch", + .index = 1, + .info = snd_ctl_boolean_mono_info, + .get = monitor_get, + .put = monitor_put, + .private_value = OXYGEN_ADC_MONITOR_B, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Input Monitor Volume", + .index = 1, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .info = monitor_volume_info, + .get = monitor_get, + .put = monitor_put, + .private_value = OXYGEN_ADC_MONITOR_B_HALF_VOL + | (1 << 8), + .tlv = { .p = monitor_db_scale, }, + }, + }, + }, + { + .pcm_dev = CAPTURE_1_FROM_SPDIF, + .controls = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Digital Input Monitor Switch", + .info = snd_ctl_boolean_mono_info, + .get = monitor_get, + .put = monitor_put, + .private_value = OXYGEN_ADC_MONITOR_C, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Digital Input Monitor Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .info = monitor_volume_info, + .get = monitor_get, + .put = monitor_put, + .private_value = OXYGEN_ADC_MONITOR_C_HALF_VOL + | (1 << 8), + .tlv = { .p = monitor_db_scale, }, + }, + }, + }, +}; + static const struct snd_kcontrol_new ac97_controls[] = { AC97_VOLUME("Mic Capture Volume", 0, AC97_MIC), AC97_SWITCH("Mic Capture Switch", 0, AC97_MIC, 15, 1), AC97_SWITCH("Mic Boost (+20dB)", 0, AC97_MIC, 6, 0), - AC97_VOLUME("Line Capture Volume", 0, AC97_LINE), AC97_SWITCH("Line Capture Switch", 0, AC97_LINE, 15, 1), AC97_VOLUME("CD Capture Volume", 0, AC97_CD), AC97_SWITCH("CD Capture Switch", 0, AC97_CD, 15, 1), @@ -756,6 +941,11 @@ static int add_controls(struct oxygen *chip, return err; if (err == 1) continue; + if (!strcmp(template.name, "Master Playback Volume") && + chip->model->dac_tlv) { + template.tlv.p = chip->model->dac_tlv; + template.access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; + } ctl = snd_ctl_new1(&template, chip); if (!ctl) return -ENOMEM; @@ -773,11 +963,26 @@ static int add_controls(struct oxygen *chip, int oxygen_mixer_init(struct oxygen *chip) { + unsigned int i; int err; err = add_controls(chip, controls, ARRAY_SIZE(controls)); if (err < 0) return err; + if (chip->model->pcm_dev_cfg & CAPTURE_1_FROM_SPDIF) { + err = add_controls(chip, spdif_input_controls, + ARRAY_SIZE(spdif_input_controls)); + if (err < 0) + return err; + } + for (i = 0; i < ARRAY_SIZE(monitor_controls); ++i) { + if (!(chip->model->pcm_dev_cfg & monitor_controls[i].pcm_dev)) + continue; + err = add_controls(chip, monitor_controls[i].controls, + ARRAY_SIZE(monitor_controls[i].controls)); + if (err < 0) + return err; + } if (chip->has_ac97_0) { err = add_controls(chip, ac97_controls, ARRAY_SIZE(ac97_controls)); diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c index b70046aca65..b17c405e069 100644 --- a/sound/pci/oxygen/oxygen_pcm.c +++ b/sound/pci/oxygen/oxygen_pcm.c @@ -119,7 +119,7 @@ static int oxygen_open(struct snd_pcm_substream *substream, runtime->private_data = (void *)(uintptr_t)channel; if (channel == PCM_B && chip->has_ac97_1 && - (chip->model->used_channels & OXYGEN_CHANNEL_AC97)) + (chip->model->pcm_dev_cfg & CAPTURE_2_FROM_AC97_1)) runtime->hw = oxygen_ac97_hardware; else runtime->hw = *oxygen_hardware[channel]; @@ -365,7 +365,7 @@ static int oxygen_rec_b_hw_params(struct snd_pcm_substream *substream, return err; is_ac97 = chip->has_ac97_1 && - (chip->model->used_channels & OXYGEN_CHANNEL_AC97); + (chip->model->pcm_dev_cfg & CAPTURE_2_FROM_AC97_1); spin_lock_irq(&chip->reg_lock); oxygen_write8_masked(chip, OXYGEN_REC_FORMAT, @@ -640,34 +640,39 @@ int oxygen_pcm_init(struct oxygen *chip) int outs, ins; int err; - outs = 1; /* OXYGEN_CHANNEL_MULTICH is always used */ - ins = !!(chip->model->used_channels & (OXYGEN_CHANNEL_A | - OXYGEN_CHANNEL_B)); - err = snd_pcm_new(chip->card, "Analog", 0, outs, ins, &pcm); - if (err < 0) - return err; - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &oxygen_multich_ops); - if (chip->model->used_channels & OXYGEN_CHANNEL_A) - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, - &oxygen_rec_a_ops); - else if (chip->model->used_channels & OXYGEN_CHANNEL_B) - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, - &oxygen_rec_b_ops); - pcm->private_data = chip; - pcm->private_free = oxygen_pcm_free; - strcpy(pcm->name, "Analog"); - snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream, - SNDRV_DMA_TYPE_DEV, - snd_dma_pci_data(chip->pci), - 512 * 1024, 2048 * 1024); - if (ins) - snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, - SNDRV_DMA_TYPE_DEV, - snd_dma_pci_data(chip->pci), - 128 * 1024, 256 * 1024); - - outs = !!(chip->model->used_channels & OXYGEN_CHANNEL_SPDIF); - ins = !!(chip->model->used_channels & OXYGEN_CHANNEL_C); + outs = !!(chip->model->pcm_dev_cfg & PLAYBACK_0_TO_I2S); + ins = !!(chip->model->pcm_dev_cfg & (CAPTURE_0_FROM_I2S_1 | + CAPTURE_0_FROM_I2S_2)); + if (outs | ins) { + err = snd_pcm_new(chip->card, "Analog", 0, outs, ins, &pcm); + if (err < 0) + return err; + if (outs) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &oxygen_multich_ops); + if (chip->model->pcm_dev_cfg & CAPTURE_0_FROM_I2S_1) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &oxygen_rec_a_ops); + else if (chip->model->pcm_dev_cfg & CAPTURE_0_FROM_I2S_2) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &oxygen_rec_b_ops); + pcm->private_data = chip; + pcm->private_free = oxygen_pcm_free; + strcpy(pcm->name, "Analog"); + if (outs) + snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream, + SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), + 512 * 1024, 2048 * 1024); + if (ins) + snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, + SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), + 128 * 1024, 256 * 1024); + } + + outs = !!(chip->model->pcm_dev_cfg & PLAYBACK_1_TO_SPDIF); + ins = !!(chip->model->pcm_dev_cfg & CAPTURE_1_FROM_SPDIF); if (outs | ins) { err = snd_pcm_new(chip->card, "Digital", 1, outs, ins, &pcm); if (err < 0) @@ -686,12 +691,13 @@ int oxygen_pcm_init(struct oxygen *chip) 128 * 1024, 256 * 1024); } - outs = chip->has_ac97_1 && - (chip->model->used_channels & OXYGEN_CHANNEL_AC97); - ins = outs || - (chip->model->used_channels & (OXYGEN_CHANNEL_A | - OXYGEN_CHANNEL_B)) - == (OXYGEN_CHANNEL_A | OXYGEN_CHANNEL_B); + if (chip->has_ac97_1) { + outs = !!(chip->model->pcm_dev_cfg & PLAYBACK_2_TO_AC97_1); + ins = !!(chip->model->pcm_dev_cfg & CAPTURE_2_FROM_AC97_1); + } else { + outs = 0; + ins = !!(chip->model->pcm_dev_cfg & CAPTURE_2_FROM_I2S_2); + } if (outs | ins) { err = snd_pcm_new(chip->card, outs ? "AC97" : "Analog2", 2, outs, ins, &pcm); diff --git a/sound/pci/oxygen/pcm1796.h b/sound/pci/oxygen/pcm1796.h new file mode 100644 index 00000000000..698bf46c710 --- /dev/null +++ b/sound/pci/oxygen/pcm1796.h @@ -0,0 +1,58 @@ +#ifndef PCM1796_H_INCLUDED +#define PCM1796_H_INCLUDED + +/* register 16 */ +#define PCM1796_ATL_MASK 0xff +/* register 17 */ +#define PCM1796_ATR_MASK 0xff +/* register 18 */ +#define PCM1796_MUTE 0x01 +#define PCM1796_DME 0x02 +#define PCM1796_DMF_MASK 0x0c +#define PCM1796_DMF_DISABLED 0x00 +#define PCM1796_DMF_48 0x04 +#define PCM1796_DMF_441 0x08 +#define PCM1796_DMF_32 0x0c +#define PCM1796_FMT_MASK 0x70 +#define PCM1796_FMT_16_RJUST 0x00 +#define PCM1796_FMT_20_RJUST 0x10 +#define PCM1796_FMT_24_RJUST 0x20 +#define PCM1796_FMT_24_LJUST 0x30 +#define PCM1796_FMT_16_I2S 0x40 +#define PCM1796_FMT_24_I2S 0x50 +#define PCM1796_ATLD 0x80 +/* register 19 */ +#define PCM1796_INZD 0x01 +#define PCM1796_FLT_MASK 0x02 +#define PCM1796_FLT_SHARP 0x00 +#define PCM1796_FLT_SLOW 0x02 +#define PCM1796_DFMS 0x04 +#define PCM1796_OPE 0x10 +#define PCM1796_ATS_MASK 0x60 +#define PCM1796_ATS_1 0x00 +#define PCM1796_ATS_2 0x20 +#define PCM1796_ATS_4 0x40 +#define PCM1796_ATS_8 0x60 +#define PCM1796_REV 0x80 +/* register 20 */ +#define PCM1796_OS_MASK 0x03 +#define PCM1796_OS_64 0x00 +#define PCM1796_OS_32 0x01 +#define PCM1796_OS_128 0x02 +#define PCM1796_CHSL_MASK 0x04 +#define PCM1796_CHSL_LEFT 0x00 +#define PCM1796_CHSL_RIGHT 0x04 +#define PCM1796_MONO 0x08 +#define PCM1796_DFTH 0x10 +#define PCM1796_DSD 0x20 +#define PCM1796_SRST 0x40 +/* register 21 */ +#define PCM1796_PCMZ 0x01 +#define PCM1796_DZ_MASK 0x06 +/* register 22 */ +#define PCM1796_ZFGL 0x01 +#define PCM1796_ZFGR 0x02 +/* register 23 */ +#define PCM1796_ID_MASK 0x1f + +#endif diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c index d163397b85c..7f84fa5deca 100644 --- a/sound/pci/oxygen/virtuoso.c +++ b/sound/pci/oxygen/virtuoso.c @@ -18,6 +18,9 @@ */ /* + * Xonar D2/D2X + * ------------ + * * CMI8788: * * SPI 0 -> 1st PCM1796 (front) @@ -30,10 +33,33 @@ * GPIO 5 <- external power present (D2X only) * GPIO 7 -> ALT * GPIO 8 -> enable output to speakers + */ + +/* + * Xonar DX + * -------- + * + * CMI8788: + * + * I²C <-> CS4398 (front) + * <-> CS4362A (surround, center/LFE, back) + * + * GPI 0 <- external power present * - * CM9780: + * GPIO 0 -> enable output to speakers + * GPIO 1 -> enable front panel I/O + * GPIO 2 -> M0 of CS5361 + * GPIO 3 -> M1 of CS5361 + * GPIO 8 -> route input jack to line-in (0) or mic-in (1) * - * GPIO 0 -> enable AC'97 bypass (line in -> ADC) + * CS4398: + * + * AD0 <- 1 + * AD1 <- 1 + * + * CS4362A: + * + * AD0 <- 0 */ #include <linux/pci.h> @@ -47,11 +73,14 @@ #include <sound/tlv.h> #include "oxygen.h" #include "cm9780.h" +#include "pcm1796.h" +#include "cs4398.h" +#include "cs4362a.h" MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); -MODULE_DESCRIPTION("Asus AV200 driver"); +MODULE_DESCRIPTION("Asus AVx00 driver"); MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE("{{Asus,AV200}}"); +MODULE_SUPPORTED_DEVICE("{{Asus,AV100},{Asus,AV200}}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; @@ -64,80 +93,44 @@ MODULE_PARM_DESC(id, "ID string"); module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "enable card"); +enum { + MODEL_D2, + MODEL_D2X, + MODEL_DX, +}; + static struct pci_device_id xonar_ids[] __devinitdata = { - { OXYGEN_PCI_SUBID(0x1043, 0x8269) }, /* Asus Xonar D2 */ - { OXYGEN_PCI_SUBID(0x1043, 0x82b7) }, /* Asus Xonar D2X */ + { OXYGEN_PCI_SUBID(0x1043, 0x8269), .driver_data = MODEL_D2 }, + { OXYGEN_PCI_SUBID(0x1043, 0x8275), .driver_data = MODEL_DX }, + { OXYGEN_PCI_SUBID(0x1043, 0x82b7), .driver_data = MODEL_D2X }, { } }; MODULE_DEVICE_TABLE(pci, xonar_ids); -#define GPIO_CS5381_M_MASK 0x000c -#define GPIO_CS5381_M_SINGLE 0x0000 -#define GPIO_CS5381_M_DOUBLE 0x0004 -#define GPIO_CS5381_M_QUAD 0x0008 -#define GPIO_EXT_POWER 0x0020 -#define GPIO_ALT 0x0080 -#define GPIO_OUTPUT_ENABLE 0x0100 - -#define GPIO_LINE_MUTE CM9780_GPO0 - -/* register 16 */ -#define PCM1796_ATL_MASK 0xff -/* register 17 */ -#define PCM1796_ATR_MASK 0xff -/* register 18 */ -#define PCM1796_MUTE 0x01 -#define PCM1796_DME 0x02 -#define PCM1796_DMF_MASK 0x0c -#define PCM1796_DMF_DISABLED 0x00 -#define PCM1796_DMF_48 0x04 -#define PCM1796_DMF_441 0x08 -#define PCM1796_DMF_32 0x0c -#define PCM1796_FMT_MASK 0x70 -#define PCM1796_FMT_16_RJUST 0x00 -#define PCM1796_FMT_20_RJUST 0x10 -#define PCM1796_FMT_24_RJUST 0x20 -#define PCM1796_FMT_24_LJUST 0x30 -#define PCM1796_FMT_16_I2S 0x40 -#define PCM1796_FMT_24_I2S 0x50 -#define PCM1796_ATLD 0x80 -/* register 19 */ -#define PCM1796_INZD 0x01 -#define PCM1796_FLT_MASK 0x02 -#define PCM1796_FLT_SHARP 0x00 -#define PCM1796_FLT_SLOW 0x02 -#define PCM1796_DFMS 0x04 -#define PCM1796_OPE 0x10 -#define PCM1796_ATS_MASK 0x60 -#define PCM1796_ATS_1 0x00 -#define PCM1796_ATS_2 0x20 -#define PCM1796_ATS_4 0x40 -#define PCM1796_ATS_8 0x60 -#define PCM1796_REV 0x80 -/* register 20 */ -#define PCM1796_OS_MASK 0x03 -#define PCM1796_OS_64 0x00 -#define PCM1796_OS_32 0x01 -#define PCM1796_OS_128 0x02 -#define PCM1796_CHSL_MASK 0x04 -#define PCM1796_CHSL_LEFT 0x00 -#define PCM1796_CHSL_RIGHT 0x04 -#define PCM1796_MONO 0x08 -#define PCM1796_DFTH 0x10 -#define PCM1796_DSD 0x20 -#define PCM1796_SRST 0x40 -/* register 21 */ -#define PCM1796_PCMZ 0x01 -#define PCM1796_DZ_MASK 0x06 -/* register 22 */ -#define PCM1796_ZFGL 0x01 -#define PCM1796_ZFGR 0x02 -/* register 23 */ -#define PCM1796_ID_MASK 0x1f +#define GPIO_CS53x1_M_MASK 0x000c +#define GPIO_CS53x1_M_SINGLE 0x0000 +#define GPIO_CS53x1_M_DOUBLE 0x0004 +#define GPIO_CS53x1_M_QUAD 0x0008 + +#define GPIO_D2X_EXT_POWER 0x0020 +#define GPIO_D2_ALT 0x0080 +#define GPIO_D2_OUTPUT_ENABLE 0x0100 + +#define GPI_DX_EXT_POWER 0x01 +#define GPIO_DX_OUTPUT_ENABLE 0x0001 +#define GPIO_DX_FRONT_PANEL 0x0002 +#define GPIO_DX_INPUT_ROUTE 0x0100 + +#define I2C_DEVICE_CS4398 0x9e /* 10011, AD1=1, AD0=1, /W=0 */ +#define I2C_DEVICE_CS4362A 0x30 /* 001100, AD0=0, /W=0 */ struct xonar_data { - u8 is_d2x; + unsigned int anti_pop_delay; + u16 output_enable_bit; + u8 ext_power_reg; + u8 ext_power_int_reg; + u8 ext_power_bit; u8 has_power; }; @@ -156,62 +149,157 @@ static void pcm1796_write(struct oxygen *chip, unsigned int codec, (reg << 8) | value); } -static void xonar_init(struct oxygen *chip) +static void cs4398_write(struct oxygen *chip, u8 reg, u8 value) +{ + oxygen_write_i2c(chip, I2C_DEVICE_CS4398, reg, value); +} + +static void cs4362a_write(struct oxygen *chip, u8 reg, u8 value) +{ + oxygen_write_i2c(chip, I2C_DEVICE_CS4362A, reg, value); +} + +static void xonar_common_init(struct oxygen *chip) +{ + struct xonar_data *data = chip->model_data; + + if (data->ext_power_reg) { + oxygen_set_bits8(chip, data->ext_power_int_reg, + data->ext_power_bit); + chip->interrupt_mask |= OXYGEN_INT_GPIO; + data->has_power = !!(oxygen_read8(chip, data->ext_power_reg) + & data->ext_power_bit); + } + oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CS53x1_M_MASK); + oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, + GPIO_CS53x1_M_SINGLE, GPIO_CS53x1_M_MASK); + oxygen_ac97_set_bits(chip, 0, CM9780_JACK, CM9780_FMIC2MIC); + msleep(data->anti_pop_delay); + oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, data->output_enable_bit); + oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit); +} + +static void xonar_d2_init(struct oxygen *chip) { struct xonar_data *data = chip->model_data; unsigned int i; - data->is_d2x = chip->pci->subsystem_device == 0x82b7; + data->anti_pop_delay = 300; + data->output_enable_bit = GPIO_D2_OUTPUT_ENABLE; for (i = 0; i < 4; ++i) { - pcm1796_write(chip, i, 18, PCM1796_FMT_24_LJUST | PCM1796_ATLD); + pcm1796_write(chip, i, 18, PCM1796_MUTE | PCM1796_DMF_DISABLED | + PCM1796_FMT_24_LJUST | PCM1796_ATLD); pcm1796_write(chip, i, 19, PCM1796_FLT_SHARP | PCM1796_ATS_1); pcm1796_write(chip, i, 20, PCM1796_OS_64); pcm1796_write(chip, i, 21, 0); - pcm1796_write(chip, i, 16, 0xff); /* set ATL/ATR after ATLD */ - pcm1796_write(chip, i, 17, 0xff); + pcm1796_write(chip, i, 16, 0x0f); /* set ATL/ATR after ATLD */ + pcm1796_write(chip, i, 17, 0x0f); } - oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, - GPIO_CS5381_M_MASK | GPIO_ALT); - oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, - GPIO_CS5381_M_SINGLE, - GPIO_CS5381_M_MASK | GPIO_ALT); - if (data->is_d2x) { - oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, - GPIO_EXT_POWER); - oxygen_set_bits16(chip, OXYGEN_GPIO_INTERRUPT_MASK, - GPIO_EXT_POWER); - chip->interrupt_mask |= OXYGEN_INT_GPIO; - data->has_power = !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) - & GPIO_EXT_POWER); - } - oxygen_ac97_set_bits(chip, 0, CM9780_JACK, CM9780_FMIC2MIC); - oxygen_ac97_clear_bits(chip, 0, CM9780_GPIO_STATUS, GPIO_LINE_MUTE); - msleep(300); - oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_OUTPUT_ENABLE); - oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE); + oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_D2_ALT); + oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_D2_ALT); + + xonar_common_init(chip); snd_component_add(chip->card, "PCM1796"); snd_component_add(chip->card, "CS5381"); } +static void xonar_d2x_init(struct oxygen *chip) +{ + struct xonar_data *data = chip->model_data; + + data->ext_power_reg = OXYGEN_GPIO_DATA; + data->ext_power_int_reg = OXYGEN_GPIO_INTERRUPT_MASK; + data->ext_power_bit = GPIO_D2X_EXT_POWER; + oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_D2X_EXT_POWER); + xonar_d2_init(chip); +} + +static void xonar_dx_init(struct oxygen *chip) +{ + struct xonar_data *data = chip->model_data; + + data->anti_pop_delay = 800; + data->output_enable_bit = GPIO_DX_OUTPUT_ENABLE; + data->ext_power_reg = OXYGEN_GPI_DATA; + data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK; + data->ext_power_bit = GPI_DX_EXT_POWER; + + oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS, + OXYGEN_2WIRE_LENGTH_8 | + OXYGEN_2WIRE_INTERRUPT_MASK | + OXYGEN_2WIRE_SPEED_FAST); + + /* set CPEN (control port mode) and power down */ + cs4398_write(chip, 8, CS4398_CPEN | CS4398_PDN); + cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN); + /* configure */ + cs4398_write(chip, 2, CS4398_FM_SINGLE | + CS4398_DEM_NONE | CS4398_DIF_LJUST); + cs4398_write(chip, 3, CS4398_ATAPI_B_R | CS4398_ATAPI_A_L); + cs4398_write(chip, 4, CS4398_MUTEP_LOW | CS4398_PAMUTE); + cs4398_write(chip, 5, 0xfe); + cs4398_write(chip, 6, 0xfe); + cs4398_write(chip, 7, CS4398_RMP_DN | CS4398_RMP_UP | + CS4398_ZERO_CROSS | CS4398_SOFT_RAMP); + cs4362a_write(chip, 0x02, CS4362A_DIF_LJUST); + cs4362a_write(chip, 0x03, CS4362A_MUTEC_6 | CS4362A_AMUTE | + CS4362A_RMP_UP | CS4362A_ZERO_CROSS | CS4362A_SOFT_RAMP); + cs4362a_write(chip, 0x04, CS4362A_RMP_DN | CS4362A_DEM_NONE); + cs4362a_write(chip, 0x05, 0); + cs4362a_write(chip, 0x06, CS4362A_FM_SINGLE | + CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L); + cs4362a_write(chip, 0x07, 0x7f | CS4362A_MUTE); + cs4362a_write(chip, 0x08, 0x7f | CS4362A_MUTE); + cs4362a_write(chip, 0x09, CS4362A_FM_SINGLE | + CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L); + cs4362a_write(chip, 0x0a, 0x7f | CS4362A_MUTE); + cs4362a_write(chip, 0x0b, 0x7f | CS4362A_MUTE); + cs4362a_write(chip, 0x0c, CS4362A_FM_SINGLE | + CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L); + cs4362a_write(chip, 0x0d, 0x7f | CS4362A_MUTE); + cs4362a_write(chip, 0x0e, 0x7f | CS4362A_MUTE); + /* clear power down */ + cs4398_write(chip, 8, CS4398_CPEN); + cs4362a_write(chip, 0x01, CS4362A_CPEN); + + oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, + GPIO_DX_FRONT_PANEL | GPIO_DX_INPUT_ROUTE); + oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, + GPIO_DX_FRONT_PANEL | GPIO_DX_INPUT_ROUTE); + + xonar_common_init(chip); + + snd_component_add(chip->card, "CS4398"); + snd_component_add(chip->card, "CS4362A"); + snd_component_add(chip->card, "CS5361"); +} + static void xonar_cleanup(struct oxygen *chip) { - oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE); + struct xonar_data *data = chip->model_data; + + oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit); +} + +static void xonar_dx_cleanup(struct oxygen *chip) +{ + xonar_cleanup(chip); + cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN); + oxygen_clear_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC); } static void set_pcm1796_params(struct oxygen *chip, struct snd_pcm_hw_params *params) { -#if 0 unsigned int i; u8 value; value = params_rate(params) >= 96000 ? PCM1796_OS_32 : PCM1796_OS_64; for (i = 0; i < 4; ++i) pcm1796_write(chip, i, 20, value); -#endif } static void update_pcm1796_volume(struct oxygen *chip) @@ -236,19 +324,73 @@ static void update_pcm1796_mute(struct oxygen *chip) pcm1796_write(chip, i, 18, value); } -static void set_cs5381_params(struct oxygen *chip, +static void set_cs53x1_params(struct oxygen *chip, struct snd_pcm_hw_params *params) { unsigned int value; if (params_rate(params) <= 54000) - value = GPIO_CS5381_M_SINGLE; + value = GPIO_CS53x1_M_SINGLE; else if (params_rate(params) <= 108000) - value = GPIO_CS5381_M_DOUBLE; + value = GPIO_CS53x1_M_DOUBLE; else - value = GPIO_CS5381_M_QUAD; + value = GPIO_CS53x1_M_QUAD; oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, - value, GPIO_CS5381_M_MASK); + value, GPIO_CS53x1_M_MASK); +} + +static void set_cs43xx_params(struct oxygen *chip, + struct snd_pcm_hw_params *params) +{ + u8 fm_cs4398, fm_cs4362a; + + fm_cs4398 = CS4398_DEM_NONE | CS4398_DIF_LJUST; + fm_cs4362a = CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L; + if (params_rate(params) <= 50000) { + fm_cs4398 |= CS4398_FM_SINGLE; + fm_cs4362a |= CS4362A_FM_SINGLE; + } else if (params_rate(params) <= 100000) { + fm_cs4398 |= CS4398_FM_DOUBLE; + fm_cs4362a |= CS4362A_FM_DOUBLE; + } else { + fm_cs4398 |= CS4398_FM_QUAD; + fm_cs4362a |= CS4362A_FM_QUAD; + } + cs4398_write(chip, 2, fm_cs4398); + cs4362a_write(chip, 0x06, fm_cs4362a); + cs4362a_write(chip, 0x09, fm_cs4362a); + cs4362a_write(chip, 0x0c, fm_cs4362a); +} + +static void update_cs4362a_volumes(struct oxygen *chip) +{ + u8 mute; + + mute = chip->dac_mute ? CS4362A_MUTE : 0; + cs4362a_write(chip, 7, (127 - chip->dac_volume[2]) | mute); + cs4362a_write(chip, 8, (127 - chip->dac_volume[3]) | mute); + cs4362a_write(chip, 10, (127 - chip->dac_volume[4]) | mute); + cs4362a_write(chip, 11, (127 - chip->dac_volume[5]) | mute); + cs4362a_write(chip, 13, (127 - chip->dac_volume[6]) | mute); + cs4362a_write(chip, 14, (127 - chip->dac_volume[7]) | mute); +} + +static void update_cs43xx_volume(struct oxygen *chip) +{ + cs4398_write(chip, 5, (127 - chip->dac_volume[0]) * 2); + cs4398_write(chip, 6, (127 - chip->dac_volume[1]) * 2); + update_cs4362a_volumes(chip); +} + +static void update_cs43xx_mute(struct oxygen *chip) +{ + u8 reg; + + reg = CS4398_MUTEP_LOW | CS4398_PAMUTE; + if (chip->dac_mute) + reg |= CS4398_MUTE_B | CS4398_MUTE_A; + cs4398_write(chip, 4, reg); + update_cs4362a_volumes(chip); } static void xonar_gpio_changed(struct oxygen *chip) @@ -256,10 +398,8 @@ static void xonar_gpio_changed(struct oxygen *chip) struct xonar_data *data = chip->model_data; u8 has_power; - if (!data->is_d2x) - return; - has_power = !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) - & GPIO_EXT_POWER); + has_power = !!(oxygen_read8(chip, data->ext_power_reg) + & data->ext_power_bit); if (has_power != data->has_power) { data->has_power = has_power; if (has_power) { @@ -272,66 +412,13 @@ static void xonar_gpio_changed(struct oxygen *chip) } } -static void mute_ac97_ctl(struct oxygen *chip, unsigned int control) -{ - unsigned int index = chip->controls[control]->private_value & 0xff; - u16 value; - - value = oxygen_read_ac97(chip, 0, index); - if (!(value & 0x8000)) { - oxygen_write_ac97(chip, 0, index, value | 0x8000); - snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, - &chip->controls[control]->id); - } -} - -static void xonar_ac97_switch_hook(struct oxygen *chip, unsigned int codec, - unsigned int reg, int mute) -{ - if (codec != 0) - return; - /* line-in is exclusive */ - switch (reg) { - case AC97_LINE: - oxygen_write_ac97_masked(chip, 0, CM9780_GPIO_STATUS, - mute ? GPIO_LINE_MUTE : 0, - GPIO_LINE_MUTE); - if (!mute) { - mute_ac97_ctl(chip, CONTROL_MIC_CAPTURE_SWITCH); - mute_ac97_ctl(chip, CONTROL_CD_CAPTURE_SWITCH); - mute_ac97_ctl(chip, CONTROL_AUX_CAPTURE_SWITCH); - } - break; - case AC97_MIC: - case AC97_CD: - case AC97_VIDEO: - case AC97_AUX: - if (!mute) { - oxygen_ac97_set_bits(chip, 0, CM9780_GPIO_STATUS, - GPIO_LINE_MUTE); - mute_ac97_ctl(chip, CONTROL_LINE_CAPTURE_SWITCH); - } - break; - } -} - -static int pcm1796_volume_info(struct snd_kcontrol *ctl, - struct snd_ctl_elem_info *info) -{ - info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - info->count = 8; - info->value.integer.min = 0x0f; - info->value.integer.max = 0xff; - return 0; -} - static int alt_switch_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) { struct oxygen *chip = ctl->private_data; value->value.integer.value[0] = - !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_ALT); + !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_D2_ALT); return 0; } @@ -345,9 +432,9 @@ static int alt_switch_put(struct snd_kcontrol *ctl, spin_lock_irq(&chip->reg_lock); old_bits = oxygen_read16(chip, OXYGEN_GPIO_DATA); if (value->value.integer.value[0]) - new_bits = old_bits | GPIO_ALT; + new_bits = old_bits | GPIO_D2_ALT; else - new_bits = old_bits & ~GPIO_ALT; + new_bits = old_bits & ~GPIO_D2_ALT; changed = new_bits != old_bits; if (changed) oxygen_write16(chip, OXYGEN_GPIO_DATA, new_bits); @@ -363,20 +450,68 @@ static const struct snd_kcontrol_new alt_switch = { .put = alt_switch_put, }; +static int front_panel_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + + value->value.integer.value[0] = + !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_DX_FRONT_PANEL); + return 0; +} + +static int front_panel_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + u16 old_reg, new_reg; + + spin_lock_irq(&chip->reg_lock); + old_reg = oxygen_read16(chip, OXYGEN_GPIO_DATA); + if (value->value.integer.value[0]) + new_reg = old_reg | GPIO_DX_FRONT_PANEL; + else + new_reg = old_reg & ~GPIO_DX_FRONT_PANEL; + oxygen_write16(chip, OXYGEN_GPIO_DATA, new_reg); + spin_unlock_irq(&chip->reg_lock); + return old_reg != new_reg; +} + +static const struct snd_kcontrol_new front_panel_switch = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Front Panel Switch", + .info = snd_ctl_boolean_mono_info, + .get = front_panel_get, + .put = front_panel_put, +}; + +static void xonar_dx_ac97_switch(struct oxygen *chip, + unsigned int reg, unsigned int mute) +{ + if (reg == AC97_LINE) { + spin_lock_irq(&chip->reg_lock); + oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, + mute ? GPIO_DX_INPUT_ROUTE : 0, + GPIO_DX_INPUT_ROUTE); + spin_unlock_irq(&chip->reg_lock); + } +} + static const DECLARE_TLV_DB_SCALE(pcm1796_db_scale, -12000, 50, 0); +static const DECLARE_TLV_DB_SCALE(cs4362a_db_scale, -12700, 100, 0); -static int xonar_control_filter(struct snd_kcontrol_new *template) +static int xonar_d2_control_filter(struct snd_kcontrol_new *template) { - if (!strcmp(template->name, "Master Playback Volume")) { - template->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; - template->info = pcm1796_volume_info, - template->tlv.p = pcm1796_db_scale; - } else if (!strncmp(template->name, "CD Capture ", 11)) { + if (!strncmp(template->name, "CD Capture ", 11)) /* CD in is actually connected to the video in pin */ template->private_value ^= AC97_CD ^ AC97_VIDEO; - } else if (!strcmp(template->name, "Line Capture Volume")) { - return 1; /* line-in bypasses the AC'97 mixer */ - } + return 0; +} + +static int xonar_dx_control_filter(struct snd_kcontrol_new *template) +{ + if (!strncmp(template->name, "CD Capture ", 11)) + return 1; /* no CD input */ return 0; } @@ -385,30 +520,96 @@ static int xonar_mixer_init(struct oxygen *chip) return snd_ctl_add(chip->card, snd_ctl_new1(&alt_switch, chip)); } -static const struct oxygen_model model_xonar = { - .shortname = "Asus AV200", - .longname = "Asus Virtuoso 200", - .chip = "AV200", - .owner = THIS_MODULE, - .init = xonar_init, - .control_filter = xonar_control_filter, - .mixer_init = xonar_mixer_init, - .cleanup = xonar_cleanup, - .set_dac_params = set_pcm1796_params, - .set_adc_params = set_cs5381_params, - .update_dac_volume = update_pcm1796_volume, - .update_dac_mute = update_pcm1796_mute, - .ac97_switch_hook = xonar_ac97_switch_hook, - .gpio_changed = xonar_gpio_changed, - .model_data_size = sizeof(struct xonar_data), - .dac_channels = 8, - .used_channels = OXYGEN_CHANNEL_B | - OXYGEN_CHANNEL_C | - OXYGEN_CHANNEL_SPDIF | - OXYGEN_CHANNEL_MULTICH, - .function_flags = OXYGEN_FUNCTION_ENABLE_SPI_4_5, - .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, - .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, +static int xonar_dx_mixer_init(struct oxygen *chip) +{ + return snd_ctl_add(chip->card, snd_ctl_new1(&front_panel_switch, chip)); +} + +static const struct oxygen_model xonar_models[] = { + [MODEL_D2] = { + .shortname = "Xonar D2", + .longname = "Asus Virtuoso 200", + .chip = "AV200", + .owner = THIS_MODULE, + .init = xonar_d2_init, + .control_filter = xonar_d2_control_filter, + .mixer_init = xonar_mixer_init, + .cleanup = xonar_cleanup, + .set_dac_params = set_pcm1796_params, + .set_adc_params = set_cs53x1_params, + .update_dac_volume = update_pcm1796_volume, + .update_dac_mute = update_pcm1796_mute, + .dac_tlv = pcm1796_db_scale, + .model_data_size = sizeof(struct xonar_data), + .pcm_dev_cfg = PLAYBACK_0_TO_I2S | + PLAYBACK_1_TO_SPDIF | + CAPTURE_0_FROM_I2S_2 | + CAPTURE_1_FROM_SPDIF, + .dac_channels = 8, + .dac_volume_min = 0x0f, + .dac_volume_max = 0xff, + .misc_flags = OXYGEN_MISC_MIDI, + .function_flags = OXYGEN_FUNCTION_SPI | + OXYGEN_FUNCTION_ENABLE_SPI_4_5, + .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, + .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, + }, + [MODEL_D2X] = { + .shortname = "Xonar D2X", + .longname = "Asus Virtuoso 200", + .chip = "AV200", + .owner = THIS_MODULE, + .init = xonar_d2x_init, + .control_filter = xonar_d2_control_filter, + .mixer_init = xonar_mixer_init, + .cleanup = xonar_cleanup, + .set_dac_params = set_pcm1796_params, + .set_adc_params = set_cs53x1_params, + .update_dac_volume = update_pcm1796_volume, + .update_dac_mute = update_pcm1796_mute, + .gpio_changed = xonar_gpio_changed, + .dac_tlv = pcm1796_db_scale, + .model_data_size = sizeof(struct xonar_data), + .pcm_dev_cfg = PLAYBACK_0_TO_I2S | + PLAYBACK_1_TO_SPDIF | + CAPTURE_0_FROM_I2S_2 | + CAPTURE_1_FROM_SPDIF, + .dac_channels = 8, + .dac_volume_min = 0x0f, + .dac_volume_max = 0xff, + .misc_flags = OXYGEN_MISC_MIDI, + .function_flags = OXYGEN_FUNCTION_SPI | + OXYGEN_FUNCTION_ENABLE_SPI_4_5, + .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, + .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, + }, + [MODEL_DX] = { + .shortname = "Xonar DX", + .longname = "Asus Virtuoso 100", + .chip = "AV200", + .owner = THIS_MODULE, + .init = xonar_dx_init, + .control_filter = xonar_dx_control_filter, + .mixer_init = xonar_dx_mixer_init, + .cleanup = xonar_dx_cleanup, + .set_dac_params = set_cs43xx_params, + .set_adc_params = set_cs53x1_params, + .update_dac_volume = update_cs43xx_volume, + .update_dac_mute = update_cs43xx_mute, + .gpio_changed = xonar_gpio_changed, + .ac97_switch = xonar_dx_ac97_switch, + .dac_tlv = cs4362a_db_scale, + .model_data_size = sizeof(struct xonar_data), + .pcm_dev_cfg = PLAYBACK_0_TO_I2S | + PLAYBACK_1_TO_SPDIF | + CAPTURE_0_FROM_I2S_2, + .dac_channels = 8, + .dac_volume_min = 0, + .dac_volume_max = 127, + .function_flags = OXYGEN_FUNCTION_2WIRE, + .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, + .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, + }, }; static int __devinit xonar_probe(struct pci_dev *pci, @@ -423,7 +624,8 @@ static int __devinit xonar_probe(struct pci_dev *pci, ++dev; return -ENOENT; } - err = oxygen_pci_probe(pci, index[dev], id[dev], 1, &model_xonar); + err = oxygen_pci_probe(pci, index[dev], id[dev], + &xonar_models[pci_id->driver_data]); if (err >= 0) ++dev; return err; diff --git a/sound/pci/oxygen/wm8785.h b/sound/pci/oxygen/wm8785.h new file mode 100644 index 00000000000..8c23e315ae6 --- /dev/null +++ b/sound/pci/oxygen/wm8785.h @@ -0,0 +1,45 @@ +#ifndef WM8785_H_INCLUDED +#define WM8785_H_INCLUDED + +#define WM8785_R0 0 +#define WM8785_R1 1 +#define WM8785_R2 2 +#define WM8785_R7 7 + +/* R0 */ +#define WM8785_MCR_MASK 0x007 +#define WM8785_MCR_SLAVE 0x000 +#define WM8785_MCR_MASTER_128 0x001 +#define WM8785_MCR_MASTER_192 0x002 +#define WM8785_MCR_MASTER_256 0x003 +#define WM8785_MCR_MASTER_384 0x004 +#define WM8785_MCR_MASTER_512 0x005 +#define WM8785_MCR_MASTER_768 0x006 +#define WM8785_OSR_MASK 0x018 +#define WM8785_OSR_SINGLE 0x000 +#define WM8785_OSR_DOUBLE 0x008 +#define WM8785_OSR_QUAD 0x010 +#define WM8785_FORMAT_MASK 0x060 +#define WM8785_FORMAT_RJUST 0x000 +#define WM8785_FORMAT_LJUST 0x020 +#define WM8785_FORMAT_I2S 0x040 +#define WM8785_FORMAT_DSP 0x060 +/* R1 */ +#define WM8785_WL_MASK 0x003 +#define WM8785_WL_16 0x000 +#define WM8785_WL_20 0x001 +#define WM8785_WL_24 0x002 +#define WM8785_WL_32 0x003 +#define WM8785_LRP 0x004 +#define WM8785_BCLKINV 0x008 +#define WM8785_LRSWAP 0x010 +#define WM8785_DEVNO_MASK 0x0e0 +/* R2 */ +#define WM8785_HPFR 0x001 +#define WM8785_HPFL 0x002 +#define WM8785_SDODIS 0x004 +#define WM8785_PWRDNR 0x008 +#define WM8785_PWRDNL 0x010 +#define WM8785_TDM_MASK 0x1c0 + +#endif diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c index 9d5bb76229a..7fdcdc8c6b6 100644 --- a/sound/pci/pcxhr/pcxhr.c +++ b/sound/pci/pcxhr/pcxhr.c @@ -458,7 +458,7 @@ static int pcxhr_update_r_buffer(struct pcxhr_stream *stream) snd_printdd("pcxhr_update_r_buffer(pcm%c%d) : addr(%p) bytes(%zx) subs(%d)\n", is_capture ? 'c' : 'p', - chip->chip_idx, (void*)subs->runtime->dma_addr, + chip->chip_idx, (void *)(long)subs->runtime->dma_addr, subs->runtime->dma_bytes, subs->number); pcxhr_init_rmh(&rmh, CMD_UPDATE_R_BUFFERS); @@ -626,7 +626,7 @@ static void pcxhr_trigger_tasklet(unsigned long arg) #ifdef CONFIG_SND_DEBUG_DETECT do_gettimeofday(&my_tv2); snd_printdd("***TRIGGER TASKLET*** TIME = %ld (err = %x)\n", - my_tv2.tv_usec - my_tv1.tv_usec, err); + (long)(my_tv2.tv_usec - my_tv1.tv_usec), err); #endif } @@ -846,7 +846,6 @@ static int pcxhr_open(struct snd_pcm_substream *subs) struct pcxhr_mgr *mgr = chip->mgr; struct snd_pcm_runtime *runtime = subs->runtime; struct pcxhr_stream *stream; - int is_capture; mutex_lock(&mgr->setup_mutex); @@ -856,12 +855,10 @@ static int pcxhr_open(struct snd_pcm_substream *subs) if( subs->stream == SNDRV_PCM_STREAM_PLAYBACK ) { snd_printdd("pcxhr_open playback chip%d subs%d\n", chip->chip_idx, subs->number); - is_capture = 0; stream = &chip->playback_stream[subs->number]; } else { snd_printdd("pcxhr_open capture chip%d subs%d\n", chip->chip_idx, subs->number); - is_capture = 1; if (mgr->mono_capture) runtime->hw.channels_max = 1; else diff --git a/sound/pci/pcxhr/pcxhr_core.c b/sound/pci/pcxhr/pcxhr_core.c index c4e415d0738..78aa81feaa4 100644 --- a/sound/pci/pcxhr/pcxhr_core.c +++ b/sound/pci/pcxhr/pcxhr_core.c @@ -897,7 +897,7 @@ int pcxhr_set_pipe_state(struct pcxhr_mgr *mgr, int playback_mask, int capture_m #ifdef CONFIG_SND_DEBUG_DETECT do_gettimeofday(&my_tv2); snd_printdd("***SET PIPE STATE*** TIME = %ld (err = %x)\n", - my_tv2.tv_usec - my_tv1.tv_usec, err); + (long)(my_tv2.tv_usec - my_tv1.tv_usec), err); #endif return 0; } @@ -1005,30 +1005,37 @@ void pcxhr_msg_tasklet(unsigned long arg) int nb_stream = (prmh->stat[i] >> (2*FIELD_SIZE)) & MASK_FIRST_FIELD; int pipe = prmh->stat[i] & MASK_FIRST_FIELD; int is_capture = prmh->stat[i] & 0x400000; - u32 err; + u32 err2; if (prmh->stat[i] & 0x800000) { /* if BIT_END */ snd_printdd("TASKLET : End%sPipe %d\n", is_capture ? "Record" : "Play", pipe); } i++; - err = prmh->stat[i] ? prmh->stat[i] : prmh->stat[i+1]; - if (err) - pcxhr_handle_async_err(mgr, err, PCXHR_ERR_PIPE, + err2 = prmh->stat[i] ? prmh->stat[i] : prmh->stat[i+1]; + if (err2) + pcxhr_handle_async_err(mgr, err2, + PCXHR_ERR_PIPE, pipe, is_capture); i += 2; for (j = 0; j < nb_stream; j++) { - err = prmh->stat[i] ? prmh->stat[i] : prmh->stat[i+1]; - if (err) - pcxhr_handle_async_err(mgr, err, PCXHR_ERR_STREAM, - pipe, is_capture); + err2 = prmh->stat[i] ? + prmh->stat[i] : prmh->stat[i+1]; + if (err2) + pcxhr_handle_async_err(mgr, err2, + PCXHR_ERR_STREAM, + pipe, + is_capture); i += 2; } for (j = 0; j < nb_audio; j++) { - err = prmh->stat[i] ? prmh->stat[i] : prmh->stat[i+1]; - if (err) - pcxhr_handle_async_err(mgr, err, PCXHR_ERR_AUDIO, - pipe, is_capture); + err2 = prmh->stat[i] ? + prmh->stat[i] : prmh->stat[i+1]; + if (err2) + pcxhr_handle_async_err(mgr, err2, + PCXHR_ERR_AUDIO, + pipe, + is_capture); i += 2; } } diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c index 9408b1eeec4..979f7da641c 100644 --- a/sound/pci/riptide/riptide.c +++ b/sound/pci/riptide/riptide.c @@ -1630,14 +1630,14 @@ static int snd_riptide_playback_open(struct snd_pcm_substream *substream) struct snd_riptide *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct pcmhw *data; - int index = substream->number; + int sub_num = substream->number; - chip->playback_substream[index] = substream; + chip->playback_substream[sub_num] = substream; runtime->hw = snd_riptide_playback; data = kzalloc(sizeof(struct pcmhw), GFP_KERNEL); - data->paths = lbus_play_paths[index]; - data->id = play_ids[index]; - data->source = play_sources[index]; + data->paths = lbus_play_paths[sub_num]; + data->id = play_ids[sub_num]; + data->source = play_sources[sub_num]; data->intdec[0] = 0xff; data->intdec[1] = 0xff; data->state = ST_STOP; @@ -1670,10 +1670,10 @@ static int snd_riptide_playback_close(struct snd_pcm_substream *substream) { struct snd_riptide *chip = snd_pcm_substream_chip(substream); struct pcmhw *data = get_pcmhwdev(substream); - int index = substream->number; + int sub_num = substream->number; substream->runtime->private_data = NULL; - chip->playback_substream[index] = NULL; + chip->playback_substream[sub_num] = NULL; kfree(data); return 0; } diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c index df184aabce8..e7ef3a1a25a 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -1350,7 +1350,8 @@ static int __devinit snd_rme32_create(struct rme32 * rme32) return err; rme32->port = pci_resource_start(rme32->pci, 0); - if ((rme32->iobase = ioremap_nocache(rme32->port, RME32_IO_SIZE)) == 0) { + rme32->iobase = ioremap_nocache(rme32->port, RME32_IO_SIZE); + if (!rme32->iobase) { snd_printk(KERN_ERR "unable to remap memory region 0x%lx-0x%lx\n", rme32->port, rme32->port + RME32_IO_SIZE - 1); return -ENOMEM; diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index fb0a4ee8bc0..3fdd488d097 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -1559,7 +1559,8 @@ 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) { + rme96->iobase = ioremap_nocache(rme96->port, RME96_IO_SIZE); + if (!rme96->iobase) { snd_printk(KERN_ERR "unable to remap memory region 0x%lx-0x%lx\n", rme96->port, rme96->port + RME96_IO_SIZE - 1); return -ENOMEM; } diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 1be84f22d0d..4d6fbb36ab8 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -318,6 +318,10 @@ MODULE_FIRMWARE("digiface_firmware_rev11.bin"); #define HDSP_midi1IRQPending (1<<31) #define HDSP_spdifFrequencyMask (HDSP_spdifFrequency0|HDSP_spdifFrequency1|HDSP_spdifFrequency2) +#define HDSP_spdifFrequencyMask_9632 (HDSP_spdifFrequency0|\ + HDSP_spdifFrequency1|\ + HDSP_spdifFrequency2|\ + HDSP_spdifFrequency3) #define HDSP_spdifFrequency32KHz (HDSP_spdifFrequency0) #define HDSP_spdifFrequency44_1KHz (HDSP_spdifFrequency1) @@ -328,7 +332,9 @@ MODULE_FIRMWARE("digiface_firmware_rev11.bin"); #define HDSP_spdifFrequency96KHz (HDSP_spdifFrequency2|HDSP_spdifFrequency1) /* This is for H9632 cards */ -#define HDSP_spdifFrequency128KHz HDSP_spdifFrequencyMask +#define HDSP_spdifFrequency128KHz (HDSP_spdifFrequency0|\ + HDSP_spdifFrequency1|\ + HDSP_spdifFrequency2) #define HDSP_spdifFrequency176_4KHz HDSP_spdifFrequency3 #define HDSP_spdifFrequency192KHz (HDSP_spdifFrequency3|HDSP_spdifFrequency0) @@ -885,28 +891,15 @@ static int snd_hdsp_use_is_exclusive(struct hdsp *hdsp) return ret; } -static int hdsp_external_sample_rate (struct hdsp *hdsp) -{ - unsigned int status2 = hdsp_read(hdsp, HDSP_status2Register); - unsigned int rate_bits = status2 & HDSP_systemFrequencyMask; - - switch (rate_bits) { - case HDSP_systemFrequency32: return 32000; - case HDSP_systemFrequency44_1: return 44100; - case HDSP_systemFrequency48: return 48000; - case HDSP_systemFrequency64: return 64000; - case HDSP_systemFrequency88_2: return 88200; - case HDSP_systemFrequency96: return 96000; - default: - return 0; - } -} - static int hdsp_spdif_sample_rate(struct hdsp *hdsp) { unsigned int status = hdsp_read(hdsp, HDSP_statusRegister); unsigned int rate_bits = (status & HDSP_spdifFrequencyMask); + /* For the 9632, the mask is different */ + if (hdsp->io_type == H9632) + rate_bits = (status & HDSP_spdifFrequencyMask_9632); + if (status & HDSP_SPDIFErrorFlag) return 0; @@ -933,6 +926,31 @@ static int hdsp_spdif_sample_rate(struct hdsp *hdsp) return 0; } +static int hdsp_external_sample_rate(struct hdsp *hdsp) +{ + unsigned int status2 = hdsp_read(hdsp, HDSP_status2Register); + unsigned int rate_bits = status2 & HDSP_systemFrequencyMask; + + /* For the 9632 card, there seems to be no bit for indicating external + * sample rate greater than 96kHz. The card reports the corresponding + * single speed. So the best means seems to get spdif rate when + * autosync reference is spdif */ + if (hdsp->io_type == H9632 && + hdsp_autosync_ref(hdsp) == HDSP_AUTOSYNC_FROM_SPDIF) + return hdsp_spdif_sample_rate(hdsp); + + switch (rate_bits) { + case HDSP_systemFrequency32: return 32000; + case HDSP_systemFrequency44_1: return 44100; + case HDSP_systemFrequency48: return 48000; + case HDSP_systemFrequency64: return 64000; + case HDSP_systemFrequency88_2: return 88200; + case HDSP_systemFrequency96: return 96000; + default: + return 0; + } +} + static void hdsp_compute_period_size(struct hdsp *hdsp) { hdsp->period_bytes = 1 << ((hdsp_decode_latency(hdsp->control_register) + 8)); diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 9a19ae6a64d..ab423bc8234 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -540,7 +540,8 @@ static void hdspm_set_sgbuf(struct hdspm * hdspm, struct snd_sg_buf *sgbuf, static inline int HDSPM_bit2freq(int n) { - static int bit2freq_tab[] = { 0, 32000, 44100, 48000, 64000, 88200, + static const int bit2freq_tab[] = { + 0, 32000, 44100, 48000, 64000, 88200, 96000, 128000, 176400, 192000 }; if (n < 1 || n > 9) return 0; @@ -582,7 +583,7 @@ static inline int hdspm_read_pb_gain(struct hdspm * hdspm, unsigned int chan, return hdspm->mixer->ch[chan].pb[pb]; } -static inline int hdspm_write_in_gain(struct hdspm * hdspm, unsigned int chan, +static int hdspm_write_in_gain(struct hdspm *hdspm, unsigned int chan, unsigned int in, unsigned short data) { if (chan >= HDSPM_MIXER_CHANNELS || in >= HDSPM_MIXER_CHANNELS) @@ -595,7 +596,7 @@ static inline int hdspm_write_in_gain(struct hdspm * hdspm, unsigned int chan, return 0; } -static inline int hdspm_write_pb_gain(struct hdspm * hdspm, unsigned int chan, +static int hdspm_write_pb_gain(struct hdspm *hdspm, unsigned int chan, unsigned int pb, unsigned short data) { if (chan >= HDSPM_MIXER_CHANNELS || pb >= HDSPM_MIXER_CHANNELS) @@ -621,7 +622,7 @@ static inline void snd_hdspm_enable_out(struct hdspm * hdspm, int i, int v) } /* check if same process is writing and reading */ -static inline int snd_hdspm_use_is_exclusive(struct hdspm * hdspm) +static int snd_hdspm_use_is_exclusive(struct hdspm *hdspm) { unsigned long flags; int ret = 1; @@ -636,7 +637,7 @@ static inline int snd_hdspm_use_is_exclusive(struct hdspm * hdspm) } /* check for external sample rate */ -static inline int hdspm_external_sample_rate(struct hdspm * hdspm) +static int hdspm_external_sample_rate(struct hdspm *hdspm) { if (hdspm->is_aes32) { unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2); @@ -787,7 +788,7 @@ static inline void hdspm_stop_audio(struct hdspm * s) } /* should I silence all or only opened ones ? doit all for first even is 4MB*/ -static inline void hdspm_silence_playback(struct hdspm * hdspm) +static void hdspm_silence_playback(struct hdspm *hdspm) { int i; int n = hdspm->period_bytes; @@ -1028,9 +1029,9 @@ static inline void snd_hdspm_midi_write_byte (struct hdspm *hdspm, int id, { /* the hardware already does the relevant bit-mask with 0xff */ if (id) - return hdspm_write(hdspm, HDSPM_midiDataOut1, val); + hdspm_write(hdspm, HDSPM_midiDataOut1, val); else - return hdspm_write(hdspm, HDSPM_midiDataOut0, val); + hdspm_write(hdspm, HDSPM_midiDataOut0, val); } static inline int snd_hdspm_midi_input_available (struct hdspm *hdspm, int id) @@ -1057,7 +1058,7 @@ static inline int snd_hdspm_midi_output_possible (struct hdspm *hdspm, int id) return 0; } -static inline void snd_hdspm_flush_midi_input (struct hdspm *hdspm, int id) +static void snd_hdspm_flush_midi_input(struct hdspm *hdspm, int id) { while (snd_hdspm_midi_input_available (hdspm, id)) snd_hdspm_midi_read_byte (hdspm, id); diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c index 742f1180c39..df2007e3be7 100644 --- a/sound/pci/sis7019.c +++ b/sound/pci/sis7019.c @@ -1194,7 +1194,6 @@ static int sis_suspend(struct pci_dev *pci, pm_message_t state) /* snd_pcm_suspend_all() stopped all channels, so we're quiescent. */ if (sis->irq >= 0) { - synchronize_irq(sis->irq); free_irq(sis->irq, sis); sis->irq = -1; } diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c index 71138ff9b31..bbcee2c09ae 100644 --- a/sound/pci/trident/trident_main.c +++ b/sound/pci/trident/trident_main.c @@ -3676,6 +3676,8 @@ static int snd_trident_free(struct snd_trident *trident) else if (trident->device == TRIDENT_DEVICE_ID_SI7018) { outl(0, TRID_REG(trident, SI_SERIAL_INTF_CTRL)); } + if (trident->irq >= 0) + free_irq(trident->irq, trident); if (trident->tlb.buffer.area) { outl(0, TRID_REG(trident, NX_TLBC)); if (trident->tlb.memhdr) @@ -3685,8 +3687,6 @@ static int snd_trident_free(struct snd_trident *trident) vfree(trident->tlb.shadow_entries); snd_dma_free_pages(&trident->tlb.buffer); } - if (trident->irq >= 0) - free_irq(trident->irq, trident); pci_release_regions(trident->pci); pci_disable_device(trident->pci); kfree(trident); diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index a756be661f9..b585cc3e4c4 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -2236,7 +2236,7 @@ static int snd_via82xx_free(struct via82xx *chip) /* disable interrupts */ for (i = 0; i < chip->num_devs; i++) snd_via82xx_channel_reset(chip, &chip->devs[i]); - synchronize_irq(chip->irq); + if (chip->irq >= 0) free_irq(chip->irq, chip); __end_hw: diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c index f5df1c79bee..31f64ee3988 100644 --- a/sound/pci/via82xx_modem.c +++ b/sound/pci/via82xx_modem.c @@ -1075,7 +1075,7 @@ static int snd_via82xx_free(struct via82xx_modem *chip) /* disable interrupts */ for (i = 0; i < chip->num_devs; i++) snd_via82xx_channel_reset(chip, &chip->devs[i]); - synchronize_irq(chip->irq); + __end_hw: if (chip->irq >= 0) free_irq(chip->irq, chip); diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index 42c1eb7d35f..29b3056c510 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -2249,6 +2249,8 @@ static int snd_ymfpci_free(struct snd_ymfpci *chip) #ifdef CONFIG_PM vfree(chip->saved_regs); #endif + if (chip->irq >= 0) + free_irq(chip->irq, chip); release_and_free_resource(chip->mpu_res); release_and_free_resource(chip->fm_res); snd_ymfpci_free_gameport(chip); @@ -2257,8 +2259,6 @@ static int snd_ymfpci_free(struct snd_ymfpci *chip) if (chip->work_ptr.area) snd_dma_free_pages(&chip->work_ptr); - if (chip->irq >= 0) - free_irq(chip->irq, chip); release_and_free_resource(chip->res_reg_area); pci_write_config_word(chip->pci, 0x40, chip->old_legacy_ctrl); |