/*
- * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Abramo Bagnara <abramo@alsa-project.org>
* Cirrus Logic, Inc.
* Routines for control of Cirrus Logic CS461x chips
* - Sometimes the SPDIF input DSP tasks get's unsynchronized
* and the SPDIF get somewhat "distorcionated", or/and left right channel
* are swapped. To get around this problem when it happens, mute and unmute
- * the SPDIF input mixer controll.
+ * the SPDIF input mixer control.
* - On the Hercules Game Theater XP the amplifier are sometimes turned
* off on inadecuate moments which causes distorcions on sound.
*
*
*/
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/pm.h>
}
#endif
-static int snd_mixer_boolean_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
- uinfo->count = 1;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = 1;
- return 0;
-}
+#define snd_mixer_boolean_info snd_ctl_boolean_mono_info
static int snd_cs46xx_iec958_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
#endif /* CONFIG_SND_CS46XX_NEW_DSP */
-#ifdef CONFIG_SND_CS46XX_DEBUG_GPIO
-static int snd_cs46xx_egpio_select_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 1;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = 8;
- return 0;
-}
-
-static int snd_cs46xx_egpio_select_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
- ucontrol->value.integer.value[0] = chip->current_gpio;
-
- return 0;
-}
-
-static int snd_cs46xx_egpio_select_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
- int change = (chip->current_gpio != ucontrol->value.integer.value[0]);
- chip->current_gpio = ucontrol->value.integer.value[0];
-
- return change;
-}
-
-
-static int snd_cs46xx_egpio_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
- int reg = kcontrol->private_value;
-
- snd_printdd ("put: reg = %04x, gpio %02x\n",reg,chip->current_gpio);
- ucontrol->value.integer.value[0] =
- (snd_cs46xx_peekBA0(chip, reg) & (1 << chip->current_gpio)) ? 1 : 0;
-
- return 0;
-}
-
-static int snd_cs46xx_egpio_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
- int reg = kcontrol->private_value;
- int val = snd_cs46xx_peekBA0(chip, reg);
- int oldval = val;
- snd_printdd ("put: reg = %04x, gpio %02x\n",reg,chip->current_gpio);
-
- if (ucontrol->value.integer.value[0])
- val |= (1 << chip->current_gpio);
- else
- val &= ~(1 << chip->current_gpio);
-
- snd_cs46xx_pokeBA0(chip, reg,val);
- snd_printdd ("put: val %08x oldval %08x\n",val,oldval);
-
- return (oldval != val);
-}
-#endif /* CONFIG_SND_CS46XX_DEBUG_GPIO */
-
static struct snd_kcontrol_new snd_cs46xx_controls[] __devinitdata = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.put = snd_cs46xx_spdif_stream_put
},
-#endif
-#ifdef CONFIG_SND_CS46XX_DEBUG_GPIO
-{
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "EGPIO select",
- .info = snd_cs46xx_egpio_select_info,
- .get = snd_cs46xx_egpio_select_get,
- .put = snd_cs46xx_egpio_select_put,
- .private_value = 0,
-},
-{
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "EGPIO Input/Output",
- .info = snd_mixer_boolean_info,
- .get = snd_cs46xx_egpio_get,
- .put = snd_cs46xx_egpio_put,
- .private_value = BA0_EGPIODR,
-},
-{
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "EGPIO CMOS/Open drain",
- .info = snd_mixer_boolean_info,
- .get = snd_cs46xx_egpio_get,
- .put = snd_cs46xx_egpio_put,
- .private_value = BA0_EGPIOPTR,
-},
-{
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "EGPIO On/Off",
- .info = snd_mixer_boolean_info,
- .get = snd_cs46xx_egpio_get,
- .put = snd_cs46xx_egpio_put,
- .private_value = BA0_EGPIOSR,
-},
#endif
};
}
#endif
+#ifdef CONFIG_PM
+ kfree(chip->saved_regs);
+#endif
+
pci_disable_device(chip->pci);
kfree(chip);
return 0;
snd_printk(KERN_ERR "ERROR: snd-cs46xx: never read ISV3 & ISV4 from AC'97\n");
snd_printk(KERN_ERR " Try reloading the ALSA driver, if you find something\n");
snd_printk(KERN_ERR " broken or not working on your soundcard upon\n");
- snd_printk(KERN_ERR " this message please report to alsa-devel@lists.sourceforge.net\n");
+ snd_printk(KERN_ERR " this message please report to alsa-devel@alsa-project.org\n");
return -EIO;
#endif
/*
* start and load DSP
*/
+
+static void cs46xx_enable_stream_irqs(struct snd_cs46xx *chip)
+{
+ unsigned int tmp;
+
+ snd_cs46xx_pokeBA0(chip, BA0_HICR, HICR_IEV | HICR_CHGM);
+
+ tmp = snd_cs46xx_peek(chip, BA1_PFIE);
+ tmp &= ~0x0000f03f;
+ snd_cs46xx_poke(chip, BA1_PFIE, tmp); /* playback interrupt enable */
+
+ tmp = snd_cs46xx_peek(chip, BA1_CIE);
+ tmp &= ~0x0000003f;
+ tmp |= 0x00000001;
+ snd_cs46xx_poke(chip, BA1_CIE, tmp); /* capture interrupt enable */
+}
+
int __devinit snd_cs46xx_start_dsp(struct snd_cs46xx *chip)
{
unsigned int tmp;
snd_cs46xx_proc_start(chip);
- /*
- * Enable interrupts on the part.
- */
- snd_cs46xx_pokeBA0(chip, BA0_HICR, HICR_IEV | HICR_CHGM);
-
- tmp = snd_cs46xx_peek(chip, BA1_PFIE);
- tmp &= ~0x0000f03f;
- snd_cs46xx_poke(chip, BA1_PFIE, tmp); /* playback interrupt enable */
-
- tmp = snd_cs46xx_peek(chip, BA1_CIE);
- tmp &= ~0x0000003f;
- tmp |= 0x00000001;
- snd_cs46xx_poke(chip, BA1_CIE, tmp); /* capture interrupt enable */
+ cs46xx_enable_stream_irqs(chip);
#ifndef CONFIG_SND_CS46XX_NEW_DSP
/* set the attenuation to 0dB */
* APM support
*/
#ifdef CONFIG_PM
+static unsigned int saved_regs[] = {
+ BA0_ACOSV,
+ BA0_ASER_FADDR,
+ BA0_ASER_MASTER,
+ BA1_PVOL,
+ BA1_CVOL,
+};
+
int snd_cs46xx_suspend(struct pci_dev *pci, pm_message_t state)
{
struct snd_card *card = pci_get_drvdata(pci);
struct snd_cs46xx *chip = card->private_data;
- int amp_saved;
+ int i, amp_saved;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
chip->in_suspend = 1;
snd_ac97_suspend(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]);
snd_ac97_suspend(chip->ac97[CS46XX_SECONDARY_CODEC_INDEX]);
+ /* save some registers */
+ for (i = 0; i < ARRAY_SIZE(saved_regs); i++)
+ chip->saved_regs[i] = snd_cs46xx_peekBA0(chip, saved_regs[i]);
+
amp_saved = chip->amplifier;
/* turn off amp */
chip->amplifier_ctrl(chip, -chip->amplifier);
{
struct snd_card *card = pci_get_drvdata(pci);
struct snd_cs46xx *chip = card->private_data;
- int amp_saved;
+ int i, amp_saved;
pci_set_power_state(pci, PCI_D0);
pci_restore_state(pci);
snd_cs46xx_chip_init(chip);
+ snd_cs46xx_reset(chip);
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ cs46xx_dsp_resume(chip);
+ /* restore some registers */
+ for (i = 0; i < ARRAY_SIZE(saved_regs); i++)
+ snd_cs46xx_pokeBA0(chip, saved_regs[i], chip->saved_regs[i]);
+#else
+ snd_cs46xx_download_image(chip);
+#endif
+
#if 0
snd_cs46xx_codec_write(chip, BA0_AC97_GENERAL_PURPOSE,
chip->ac97_general_purpose);
snd_ac97_resume(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]);
snd_ac97_resume(chip->ac97[CS46XX_SECONDARY_CODEC_INDEX]);
+ /* reset playback/capture */
+ snd_cs46xx_set_play_sample_rate(chip, 8000);
+ snd_cs46xx_set_capture_sample_rate(chip, 8000);
+ snd_cs46xx_proc_start(chip);
+
+ cs46xx_enable_stream_irqs(chip);
+
if (amp_saved)
chip->amplifier_ctrl(chip, 1); /* turn amp on */
else
snd_cs46xx_proc_init(card, chip);
+#ifdef CONFIG_PM
+ chip->saved_regs = kmalloc(sizeof(*chip->saved_regs) *
+ ARRAY_SIZE(saved_regs), GFP_KERNEL);
+ if (!chip->saved_regs) {
+ snd_cs46xx_free(chip);
+ return -ENOMEM;
+ }
+#endif
+
chip->active_ctrl(chip, -1); /* disable CLKRUN */
snd_card_set_dev(card, &pci->dev);