vserver 1.9.5.x5
[linux-2.6.git] / sound / pci / cs46xx / cs46xx_lib.c
index 9b18681..0dcfee8 100644 (file)
 
 static void amp_voyetra(cs46xx_t *chip, int change);
 
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+static snd_pcm_ops_t snd_cs46xx_playback_rear_ops;
+static snd_pcm_ops_t snd_cs46xx_playback_indirect_rear_ops;
+static snd_pcm_ops_t snd_cs46xx_playback_clfe_ops;
+static snd_pcm_ops_t snd_cs46xx_playback_indirect_clfe_ops;
+static snd_pcm_ops_t snd_cs46xx_playback_iec958_ops;
+static snd_pcm_ops_t snd_cs46xx_playback_indirect_iec958_ops;
+#endif
+
+static snd_pcm_ops_t snd_cs46xx_playback_ops;
+static snd_pcm_ops_t snd_cs46xx_playback_indirect_ops;
+static snd_pcm_ops_t snd_cs46xx_capture_ops;
+static snd_pcm_ops_t snd_cs46xx_capture_indirect_ops;
+
 static unsigned short snd_cs46xx_codec_read(cs46xx_t *chip,
                                            unsigned short reg,
                                            int codec_index)
@@ -99,7 +113,7 @@ static unsigned short snd_cs46xx_codec_read(cs46xx_t *chip,
        if ((tmp & ACCTL_VFRM) == 0) {
                snd_printk(KERN_WARNING  "cs46xx: ACCTL_VFRM not set 0x%x\n",tmp);
                snd_cs46xx_pokeBA0(chip, BA0_ACCTL, (tmp & (~ACCTL_ESYN)) | ACCTL_VFRM );
-               mdelay(50);
+               msleep(50);
                tmp = snd_cs46xx_peekBA0(chip, BA0_ACCTL + offset);
                snd_cs46xx_pokeBA0(chip, BA0_ACCTL, tmp | ACCTL_ESYN | ACCTL_VFRM );
 
@@ -203,12 +217,6 @@ static unsigned short snd_cs46xx_ac97_read(ac97_t * ac97,
 
        val = snd_cs46xx_codec_read(chip, reg, codec_index);
 
-       /* HACK: voyetra uses EAPD bit in the reverse way.
-        * we flip the bit to show the mixer status correctly
-        */
-       if (reg == AC97_POWERDOWN && chip->amplifier_ctrl == amp_voyetra)
-               val ^= 0x8000;
-
        return val;
 }
 
@@ -289,12 +297,6 @@ static void snd_cs46xx_ac97_write(ac97_t *ac97,
                   codec_index == CS46XX_SECONDARY_CODEC_INDEX,
                   return);
 
-       /* HACK: voyetra uses EAPD bit in the reverse way.
-        * we flip the bit to show the mixer status correctly
-        */
-       if (reg == AC97_POWERDOWN && chip->amplifier_ctrl == amp_voyetra)
-               val ^= 0x8000;
-
        snd_cs46xx_codec_write(chip, reg, val, codec_index);
 }
 
@@ -308,7 +310,7 @@ int snd_cs46xx_download(cs46xx_t *chip,
                         unsigned long offset,
                         unsigned long len)
 {
-       unsigned long dst;
+       void __iomem *dst;
        unsigned int bank = offset >> 16;
        offset = offset & 0xffff;
 
@@ -336,7 +338,7 @@ int snd_cs46xx_clear_BA1(cs46xx_t *chip,
                          unsigned long offset,
                          unsigned long len) 
 {
-       unsigned long dst;
+       void __iomem *dst;
        unsigned int bank = offset >> 16;
        offset = offset & 0xffff;
 
@@ -1458,7 +1460,7 @@ static int snd_cs46xx_capture_close(snd_pcm_substream_t * substream)
 }
 
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
-snd_pcm_ops_t snd_cs46xx_playback_rear_ops = {
+static snd_pcm_ops_t snd_cs46xx_playback_rear_ops = {
        .open =                 snd_cs46xx_playback_open_rear,
        .close =                snd_cs46xx_playback_close,
        .ioctl =                snd_pcm_lib_ioctl,
@@ -1469,7 +1471,7 @@ snd_pcm_ops_t snd_cs46xx_playback_rear_ops = {
        .pointer =              snd_cs46xx_playback_direct_pointer,
 };
 
-snd_pcm_ops_t snd_cs46xx_playback_indirect_rear_ops = {
+static snd_pcm_ops_t snd_cs46xx_playback_indirect_rear_ops = {
        .open =                 snd_cs46xx_playback_open_rear,
        .close =                snd_cs46xx_playback_close,
        .ioctl =                snd_pcm_lib_ioctl,
@@ -1481,7 +1483,7 @@ snd_pcm_ops_t snd_cs46xx_playback_indirect_rear_ops = {
        .ack =                  snd_cs46xx_playback_transfer,
 };
 
-snd_pcm_ops_t snd_cs46xx_playback_clfe_ops = {
+static snd_pcm_ops_t snd_cs46xx_playback_clfe_ops = {
        .open =                 snd_cs46xx_playback_open_clfe,
        .close =                snd_cs46xx_playback_close,
        .ioctl =                snd_pcm_lib_ioctl,
@@ -1492,7 +1494,7 @@ snd_pcm_ops_t snd_cs46xx_playback_clfe_ops = {
        .pointer =              snd_cs46xx_playback_direct_pointer,
 };
 
-snd_pcm_ops_t snd_cs46xx_playback_indirect_clfe_ops = {
+static snd_pcm_ops_t snd_cs46xx_playback_indirect_clfe_ops = {
        .open =                 snd_cs46xx_playback_open_clfe,
        .close =                snd_cs46xx_playback_close,
        .ioctl =                snd_pcm_lib_ioctl,
@@ -1504,7 +1506,7 @@ snd_pcm_ops_t snd_cs46xx_playback_indirect_clfe_ops = {
        .ack =                  snd_cs46xx_playback_transfer,
 };
 
-snd_pcm_ops_t snd_cs46xx_playback_iec958_ops = {
+static snd_pcm_ops_t snd_cs46xx_playback_iec958_ops = {
        .open =                 snd_cs46xx_playback_open_iec958,
        .close =                snd_cs46xx_playback_close_iec958,
        .ioctl =                snd_pcm_lib_ioctl,
@@ -1515,7 +1517,7 @@ snd_pcm_ops_t snd_cs46xx_playback_iec958_ops = {
        .pointer =              snd_cs46xx_playback_direct_pointer,
 };
 
-snd_pcm_ops_t snd_cs46xx_playback_indirect_iec958_ops = {
+static snd_pcm_ops_t snd_cs46xx_playback_indirect_iec958_ops = {
        .open =                 snd_cs46xx_playback_open_iec958,
        .close =                snd_cs46xx_playback_close_iec958,
        .ioctl =                snd_pcm_lib_ioctl,
@@ -1529,7 +1531,7 @@ snd_pcm_ops_t snd_cs46xx_playback_indirect_iec958_ops = {
 
 #endif
 
-snd_pcm_ops_t snd_cs46xx_playback_ops = {
+static snd_pcm_ops_t snd_cs46xx_playback_ops = {
        .open =                 snd_cs46xx_playback_open,
        .close =                snd_cs46xx_playback_close,
        .ioctl =                snd_pcm_lib_ioctl,
@@ -1540,7 +1542,7 @@ snd_pcm_ops_t snd_cs46xx_playback_ops = {
        .pointer =              snd_cs46xx_playback_direct_pointer,
 };
 
-snd_pcm_ops_t snd_cs46xx_playback_indirect_ops = {
+static snd_pcm_ops_t snd_cs46xx_playback_indirect_ops = {
        .open =                 snd_cs46xx_playback_open,
        .close =                snd_cs46xx_playback_close,
        .ioctl =                snd_pcm_lib_ioctl,
@@ -1552,7 +1554,7 @@ snd_pcm_ops_t snd_cs46xx_playback_indirect_ops = {
        .ack =                  snd_cs46xx_playback_transfer,
 };
 
-snd_pcm_ops_t snd_cs46xx_capture_ops = {
+static snd_pcm_ops_t snd_cs46xx_capture_ops = {
        .open =                 snd_cs46xx_capture_open,
        .close =                snd_cs46xx_capture_close,
        .ioctl =                snd_pcm_lib_ioctl,
@@ -1563,7 +1565,7 @@ snd_pcm_ops_t snd_cs46xx_capture_ops = {
        .pointer =              snd_cs46xx_capture_direct_pointer,
 };
 
-snd_pcm_ops_t snd_cs46xx_capture_indirect_ops = {
+static snd_pcm_ops_t snd_cs46xx_capture_indirect_ops = {
        .open =                 snd_cs46xx_capture_open,
        .close =                snd_cs46xx_capture_close,
        .ioctl =                snd_pcm_lib_ioctl,
@@ -2316,6 +2318,36 @@ static snd_kcontrol_new_t snd_cs46xx_controls[] __devinitdata = {
 #endif
 };
 
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+/* set primary cs4294 codec into Extended Audio Mode */
+static int snd_cs46xx_front_dup_get(snd_kcontrol_t *kcontrol, 
+                                   snd_ctl_elem_value_t *ucontrol)
+{
+       cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
+       unsigned short val;
+       val = snd_ac97_read(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX], AC97_CSR_ACMODE);
+       ucontrol->value.integer.value[0] = (val & 0x200) ? 0 : 1;
+       return 0;
+}
+
+static int snd_cs46xx_front_dup_put(snd_kcontrol_t *kcontrol, 
+                                   snd_ctl_elem_value_t *ucontrol)
+{
+       cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
+       return snd_ac97_update_bits(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX],
+                                   AC97_CSR_ACMODE, 0x200,
+                                   ucontrol->value.integer.value[0] ? 0 : 0x200);
+}
+
+static snd_kcontrol_new_t snd_cs46xx_front_dup_ctl = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "Duplicate Front",
+       .info = snd_mixer_boolean_info,
+       .get = snd_cs46xx_front_dup_get,
+       .put = snd_cs46xx_front_dup_put,
+};
+#endif
+
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
 /* Only available on the Hercules Game Theater XP soundcard */
 static snd_kcontrol_new_t snd_hercules_controls[] __devinitdata = {
@@ -2333,16 +2365,15 @@ static void snd_cs46xx_codec_reset (ac97_t * ac97)
 {
        unsigned long end_time;
        int err;
-       cs46xx_t * chip = ac97->private_data;
 
        /* reset to defaults */
        snd_ac97_write(ac97, AC97_RESET, 0);    
 
        /* set the desired CODEC mode */
-       if (chip->nr_ac97_codecs == 0) {
+       if (ac97->num == CS46XX_PRIMARY_CODEC_INDEX) {
                snd_printdd("cs46xx: CODOEC1 mode %04x\n",0x0);
                snd_cs46xx_ac97_write(ac97,AC97_CSR_ACMODE,0x0);
-       } else if (chip->nr_ac97_codecs == 1) {
+       } else if (ac97->num == CS46XX_SECONDARY_CODEC_INDEX) {
                snd_printdd("cs46xx: CODOEC2 mode %04x\n",0x3);
                snd_cs46xx_ac97_write(ac97,AC97_CSR_ACMODE,0x3);
        } else {
@@ -2380,10 +2411,43 @@ static void snd_cs46xx_codec_reset (ac97_t * ac97)
 }
 #endif
 
+static int __devinit cs46xx_detect_codec(cs46xx_t *chip, int codec)
+{
+       int idx, err;
+       ac97_template_t ac97;
+
+       memset(&ac97, 0, sizeof(ac97));
+       ac97.private_data = chip;
+       ac97.private_free = snd_cs46xx_mixer_free_ac97;
+       ac97.num = codec;
+       if (chip->amplifier_ctrl == amp_voyetra)
+               ac97.scaps = AC97_SCAP_INV_EAPD;
+
+       if (codec == CS46XX_SECONDARY_CODEC_INDEX) {
+               snd_cs46xx_codec_write(chip, AC97_RESET, 0, codec);
+               udelay(10);
+               if (snd_cs46xx_codec_read(chip, AC97_RESET, codec) & 0x8000) {
+                       snd_printdd("snd_cs46xx: seconadry codec not present\n");
+                       return -ENXIO;
+               }
+       }
+
+       snd_cs46xx_codec_write(chip, AC97_MASTER, 0x8000, codec);
+       for (idx = 0; idx < 100; ++idx) {
+               if (snd_cs46xx_codec_read(chip, AC97_MASTER, codec) == 0x8000) {
+                       err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97[codec]);
+                       return err;
+               }
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule_timeout(HZ/100);
+       }
+       snd_printdd("snd_cs46xx: codec %d detection timeout\n", codec);
+       return -ENXIO;
+}
+
 int __devinit snd_cs46xx_mixer(cs46xx_t *chip)
 {
        snd_card_t *card = chip->card;
-       ac97_template_t ac97;
        snd_ctl_elem_id_t id;
        int err;
        unsigned int idx;
@@ -2402,70 +2466,15 @@ int __devinit snd_cs46xx_mixer(cs46xx_t *chip)
                return err;
        chip->ac97_bus->private_free = snd_cs46xx_mixer_free_ac97_bus;
 
-       memset(&ac97, 0, sizeof(ac97));
-       ac97.private_data = chip;
-       ac97.private_free = snd_cs46xx_mixer_free_ac97;
-
-       snd_cs46xx_codec_write(chip, AC97_MASTER, 0x8000,
-                              CS46XX_PRIMARY_CODEC_INDEX);
-       for (idx = 0; idx < 100; ++idx) {
-               if (snd_cs46xx_codec_read(chip, AC97_MASTER,
-                                         CS46XX_PRIMARY_CODEC_INDEX) == 0x8000)
-                       goto _ok;
-               set_current_state(TASK_INTERRUPTIBLE);
-               schedule_timeout(HZ/100);
-       }
-       return -ENXIO;
-
- _ok:
-       if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97[CS46XX_PRIMARY_CODEC_INDEX])) < 0)
-               return err;
-       snd_printdd("snd_cs46xx: primary codec phase one\n");
+       if (cs46xx_detect_codec(chip, CS46XX_PRIMARY_CODEC_INDEX) < 0)
+               return -ENXIO;
        chip->nr_ac97_codecs = 1;
 
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
        snd_printdd("snd_cs46xx: detecting seconadry codec\n");
        /* try detect a secondary codec */
-       memset(&ac97, 0, sizeof(ac97));    
-       ac97.private_data = chip;
-       ac97.private_free = snd_cs46xx_mixer_free_ac97;
-       ac97.num = CS46XX_SECONDARY_CODEC_INDEX;
-
-       snd_cs46xx_codec_write(chip, AC97_RESET, 0,
-                              CS46XX_SECONDARY_CODEC_INDEX);
-       udelay(10);
-
-       if (snd_cs46xx_codec_read(chip, AC97_RESET,
-                                 CS46XX_SECONDARY_CODEC_INDEX) & 0x8000) {
-               snd_printdd("snd_cs46xx: seconadry codec not present\n");
-               goto _no_sec_codec;
-       }
-
-       snd_cs46xx_codec_write(chip, AC97_MASTER, 0x8000,
-                              CS46XX_SECONDARY_CODEC_INDEX);
-       for (idx = 0; idx < 100; ++idx) {
-               if (snd_cs46xx_codec_read(chip, AC97_MASTER,
-                                         CS46XX_SECONDARY_CODEC_INDEX) == 0x8000) {
-                       goto _ok2;
-               }
-               set_current_state(TASK_INTERRUPTIBLE);
-               schedule_timeout(HZ/100);
-       }
-
- _no_sec_codec:
-       snd_printdd("snd_cs46xx: secondary codec did not respond ...\n");
-
-       chip->nr_ac97_codecs = 1;
-    
-       /* well, one codec only ... */
-       goto _end;
- _ok2:
-       if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97[CS46XX_SECONDARY_CODEC_INDEX])) < 0)
-               return err;
-       chip->nr_ac97_codecs = 2;
-
- _end:
-
+       if (! cs46xx_detect_codec(chip, CS46XX_SECONDARY_CODEC_INDEX))
+               chip->nr_ac97_codecs = 2;
 #endif /* CONFIG_SND_CS46XX_NEW_DSP */
 
        /* add cs4630 mixer controls */
@@ -2483,15 +2492,15 @@ int __devinit snd_cs46xx_mixer(cs46xx_t *chip)
        chip->eapd_switch = snd_ctl_find_id(chip->card, &id);
     
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
-       if (chip->nr_ac97_codecs == 1 && 
-           (snd_cs46xx_codec_read(chip, AC97_VENDOR_ID2,
-                                 CS46XX_PRIMARY_CODEC_INDEX) == 0x592b ||
-            snd_cs46xx_codec_read(chip, AC97_VENDOR_ID2,
-                                  CS46XX_PRIMARY_CODEC_INDEX) == 0x592d)) {
-               /* set primary cs4294 codec into Extended Audio Mode */
-               snd_printdd("setting EAM bit on cs4294 CODEC\n");
-               snd_cs46xx_codec_write(chip, AC97_CSR_ACMODE, 0x200,
-                                      CS46XX_PRIMARY_CODEC_INDEX);
+       if (chip->nr_ac97_codecs == 1) {
+               unsigned int id2 = chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]->id & 0xffff;
+               if (id2 == 0x592b || id2 == 0x592d) {
+                       err = snd_ctl_add(card, snd_ctl_new1(&snd_cs46xx_front_dup_ctl, chip));
+                       if (err < 0)
+                               return err;
+                       snd_ac97_write_cache(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX],
+                                            AC97_CSR_ACMODE, 0x200);
+               }
        }
        /* do soundcard specific mixer setup */
        if (chip->mixer_init) {
@@ -2902,7 +2911,7 @@ static int snd_cs46xx_free(cs46xx_t *chip)
        for (idx = 0; idx < 5; idx++) {
                snd_cs46xx_region_t *region = &chip->region.idx[idx];
                if (region->remap_addr)
-                       iounmap((void *) region->remap_addr);
+                       iounmap(region->remap_addr);
                if (region->resource) {
                        release_resource(region->resource);
                        kfree_nocheck(region->resource);
@@ -2921,6 +2930,7 @@ static int snd_cs46xx_free(cs46xx_t *chip)
        }
 #endif
        
+       pci_disable_device(chip->pci);
        kfree(chip);
        return 0;
 }
@@ -3714,7 +3724,7 @@ static int snd_cs46xx_suspend(snd_card_t *card, unsigned int state)
        /* disable CLKRUN */
        chip->active_ctrl(chip, -chip->amplifier);
        chip->amplifier = amp_saved; /* restore the status */
-       snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+       pci_disable_device(chip->pci);
        return 0;
 }
 
@@ -3724,6 +3734,7 @@ static int snd_cs46xx_resume(snd_card_t *card, unsigned int state)
        int amp_saved;
 
        pci_enable_device(chip->pci);
+       pci_set_master(chip->pci);
        amp_saved = chip->amplifier;
        chip->amplifier = 0;
        chip->active_ctrl(chip, 1); /* force to on */
@@ -3750,7 +3761,6 @@ static int snd_cs46xx_resume(snd_card_t *card, unsigned int state)
        else
                chip->active_ctrl(chip, -1); /* disable CLKRUN */
        chip->amplifier = amp_saved;
-       snd_power_change_state(card, SNDRV_CTL_POWER_D0);
        return 0;
 }
 #endif /* CONFIG_PM */
@@ -3780,8 +3790,10 @@ int __devinit snd_cs46xx_create(snd_card_t * card,
                return err;
 
        chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
-       if (chip == NULL)
+       if (chip == NULL) {
+               pci_disable_device(pci);
                return -ENOMEM;
+       }
        spin_lock_init(&chip->reg_lock);
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
        init_MUTEX(&chip->spos_mutex);
@@ -3868,8 +3880,8 @@ int __devinit snd_cs46xx_create(snd_card_t * card,
                        snd_cs46xx_free(chip);
                        return -EBUSY;
                }
-               region->remap_addr = (unsigned long) ioremap_nocache(region->base, region->size);
-               if (region->remap_addr == 0) {
+               region->remap_addr = ioremap_nocache(region->base, region->size);
+               if (region->remap_addr == NULL) {
                        snd_printk("%s ioremap problem\n", region->name);
                        snd_cs46xx_free(chip);
                        return -ENOMEM;
@@ -3897,15 +3909,15 @@ int __devinit snd_cs46xx_create(snd_card_t * card,
                return err;
        }
 
-       snd_cs46xx_proc_init(card, chip);
-
-       snd_card_set_pm_callback(card, snd_cs46xx_suspend, snd_cs46xx_resume, chip);
-
        if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
                snd_cs46xx_free(chip);
                return err;
        }
        
+       snd_cs46xx_proc_init(card, chip);
+
+       snd_card_set_pm_callback(card, snd_cs46xx_suspend, snd_cs46xx_resume, chip);
+
        chip->active_ctrl(chip, -1); /* disable CLKRUN */
 
        snd_card_set_dev(card, &pci->dev);