]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - sound/pci/cmipci.c
[ALSA] cmipci: add msbits constraint for 24-bit format
[linux-2.6.git] / sound / pci / cmipci.c
index 79bc60a5204e440e95f96b6e2c0f76cd062d6206..78a23984eac7d21239dfe0584c89058b9535110b 100644 (file)
@@ -57,7 +57,7 @@ static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;    /* Index 0-MAX */
 static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;      /* ID for this card */
 static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;     /* Enable switches */
 static long mpu_port[SNDRV_CARDS];
-static long fm_port[SNDRV_CARDS];
+static long fm_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)]=1};
 static int soft_ac3[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)]=1};
 #ifdef SUPPORT_JOYSTICK
 static int joystick_port[SNDRV_CARDS];
@@ -135,11 +135,14 @@ MODULE_PARM_DESC(joystick_port, "Joystick port address.");
 #define CM_ADCDACLEN_280       0x00003000
 
 #define CM_CH1_SRATE_176K      0x00000800
+#define CM_CH1_SRATE_96K       0x00000800      /* model 055? */
 #define CM_CH1_SRATE_88K       0x00000400
 #define CM_CH0_SRATE_176K      0x00000200
+#define CM_CH0_SRATE_96K       0x00000200      /* model 055? */
 #define CM_CH0_SRATE_88K       0x00000100
 
 #define CM_SPDIF_INVERSE2      0x00000080      /* model 055? */
+#define CM_DBLSPDS             0x00000040
 
 #define CM_CH1FMT_MASK         0x0000000C
 #define CM_CH1FMT_SHIFT                2
@@ -424,7 +427,6 @@ struct cmipci {
 
        int chip_version;
        int max_channels;
-       unsigned int has_dual_dac: 1;
        unsigned int can_ac3_sw: 1;
        unsigned int can_ac3_hw: 1;
        unsigned int can_multi_ch: 1;
@@ -813,6 +815,16 @@ static int snd_cmipci_pcm_prepare(struct cmipci *cm, struct cmipci_pcm *rec,
                val &= ~CM_CH0FMT_MASK;
                val |= rec->fmt << CM_CH0FMT_SHIFT;
        }
+       if (cm->chip_version == 68) {
+               if (runtime->rate == 88200)
+                       val |= CM_CH0_SRATE_88K << (rec->ch * 2);
+               else
+                       val &= ~(CM_CH0_SRATE_88K << (rec->ch * 2));
+               if (runtime->rate == 96000)
+                       val |= CM_CH0_SRATE_96K << (rec->ch * 2);
+               else
+                       val &= ~(CM_CH0_SRATE_96K << (rec->ch * 2));
+       }
        snd_cmipci_write(cm, CM_REG_CHFORMAT, val);
        //snd_printd("cmipci: chformat = %08x\n", val);
 
@@ -1199,15 +1211,19 @@ static int setup_spdif_playback(struct cmipci *cm, struct snd_pcm_substream *sub
                        snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF);
                setup_ac3(cm, subs, do_ac3, rate);
 
-               if (rate == 48000)
+               if (rate == 48000 || rate == 96000)
                        snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPDIF48K | CM_SPDF_AC97);
                else
                        snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPDIF48K | CM_SPDF_AC97);
-
+               if (rate > 48000)
+                       snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_DBLSPDS);
+               else
+                       snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_DBLSPDS);
        } else {
                /* they are controlled via "IEC958 Output Switch" */
                /* snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_ENSPDOUT); */
                /* snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_SPDO2DAC); */
+               snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_DBLSPDS);
                snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF);
                setup_ac3(cm, subs, 0, 0);
        }
@@ -1227,7 +1243,7 @@ static int snd_cmipci_playback_prepare(struct snd_pcm_substream *substream)
        int rate = substream->runtime->rate;
        int err, do_spdif, do_ac3 = 0;
 
-       do_spdif = ((rate == 44100 || rate == 48000) &&
+       do_spdif = (rate >= 44100 &&
                    substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE &&
                    substream->runtime->channels == 2);
        if (do_spdif && cm->can_ac3_hw) 
@@ -1294,7 +1310,7 @@ static int snd_cmipci_capture_spdif_hw_free(struct snd_pcm_substream *subs)
 /*
  * interrupt handler
  */
-static irqreturn_t snd_cmipci_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static irqreturn_t snd_cmipci_interrupt(int irq, void *dev_id)
 {
        struct cmipci *cm = dev_id;
        unsigned int status, mask = 0;
@@ -1315,7 +1331,7 @@ static irqreturn_t snd_cmipci_interrupt(int irq, void *dev_id, struct pt_regs *r
        spin_unlock(&cm->reg_lock);
 
        if (cm->rmidi && (status & CM_UARTINT))
-               snd_mpu401_uart_interrupt(irq, cm->rmidi->private_data, regs);
+               snd_mpu401_uart_interrupt(irq, cm->rmidi->private_data);
 
        if (cm->pcm) {
                if ((status & CM_CHINT0) && cm->channel[0].running)
@@ -1515,7 +1531,11 @@ static int snd_cmipci_playback_open(struct snd_pcm_substream *substream)
        if ((err = open_device_check(cm, CM_OPEN_PLAYBACK, substream)) < 0)
                return err;
        runtime->hw = snd_cmipci_playback;
-       runtime->hw.channels_max = cm->max_channels;
+       if (cm->chip_version == 68) {
+               runtime->hw.rates |= SNDRV_PCM_RATE_88200 |
+                                    SNDRV_PCM_RATE_96000;
+               runtime->hw.rate_max = 96000;
+       }
        snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000);
        cm->dig_pcm_status = cm->dig_status;
        return 0;
@@ -1558,6 +1578,11 @@ static int snd_cmipci_playback2_open(struct snd_pcm_substream *substream)
                        else if (cm->max_channels == 8)
                                snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels_8);
                }
+               if (cm->chip_version == 68) {
+                       runtime->hw.rates |= SNDRV_PCM_RATE_88200 |
+                                            SNDRV_PCM_RATE_96000;
+                       runtime->hw.rate_max = 96000;
+               }
                snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000);
        }
        mutex_unlock(&cm->open_mutex);
@@ -1574,8 +1599,15 @@ static int snd_cmipci_playback_spdif_open(struct snd_pcm_substream *substream)
                return err;
        if (cm->can_ac3_hw) {
                runtime->hw = snd_cmipci_playback_spdif;
-               if (cm->chip_version >= 37)
+               if (cm->chip_version >= 37) {
                        runtime->hw.formats |= SNDRV_PCM_FMTBIT_S32_LE;
+                       snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+               }
+               if (cm->chip_version == 68) {
+                       runtime->hw.rates |= SNDRV_PCM_RATE_88200 |
+                                            SNDRV_PCM_RATE_96000;
+                       runtime->hw.rate_max = 96000;
+               }
        } else {
                runtime->hw = snd_cmipci_playback_iec958_subframe;
        }
@@ -2121,7 +2153,7 @@ static struct snd_kcontrol_new snd_cmipci_mixers[] __devinitdata = {
        CMIPCI_MIXER_VOL_MONO("Mic Capture Volume", CM_REG_MIXER2, CM_VADMIC_SHIFT, 7),
        CMIPCI_SB_VOL_MONO("Phone Playback Volume", CM_REG_EXTENT_IND, 5, 7),
        CMIPCI_DOUBLE("Phone Playback Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 4, 4, 1, 0, 0),
-       CMIPCI_DOUBLE("PC Speaker Playnack Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 3, 3, 1, 0, 0),
+       CMIPCI_DOUBLE("PC Speaker Playback Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 3, 3, 1, 0, 0),
        CMIPCI_DOUBLE("Mic Boost Capture Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 0, 0, 1, 0, 0),
 };
 
@@ -2139,15 +2171,7 @@ struct cmipci_switch_args {
                                         */
 };
 
-static int snd_cmipci_uswitch_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_cmipci_uswitch_info                snd_ctl_boolean_mono_info
 
 static int _snd_cmipci_uswitch_get(struct snd_kcontrol *kcontrol,
                                   struct snd_ctl_elem_value *ucontrol,
@@ -2198,7 +2222,8 @@ static int _snd_cmipci_uswitch_put(struct snd_kcontrol *kcontrol,
                val = inb(cm->iobase + args->reg);
        else
                val = snd_cmipci_read(cm, args->reg);
-       change = (val & args->mask) != (ucontrol->value.integer.value[0] ? args->mask : 0);
+       change = (val & args->mask) != (ucontrol->value.integer.value[0] ? 
+                       args->mask_on : (args->mask & ~args->mask_on));
        if (change) {
                val &= ~args->mask;
                if (ucontrol->value.integer.value[0])
@@ -2609,7 +2634,7 @@ static inline void snd_cmipci_proc_init(struct cmipci *cm) {}
 #endif
 
 
-static struct pci_device_id snd_cmipci_ids[] __devinitdata = {
+static struct pci_device_id snd_cmipci_ids[] = {
        {PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
        {PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
        {PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
@@ -2632,46 +2657,40 @@ static void __devinit query_chip(struct cmipci *cm)
        if (! detect) {
                /* check reg 08h, bit 24-28 */
                detect = snd_cmipci_read(cm, CM_REG_CHFORMAT) & CM_CHIP_MASK1;
-               if (! detect) {
+               switch (detect) {
+               case 0:
                        cm->chip_version = 33;
-                       cm->max_channels = 2;
                        if (cm->do_soft_ac3)
                                cm->can_ac3_sw = 1;
                        else
                                cm->can_ac3_hw = 1;
-                       cm->has_dual_dac = 1;
-               } else {
+                       break;
+               case 1:
                        cm->chip_version = 37;
-                       cm->max_channels = 2;
                        cm->can_ac3_hw = 1;
-                       cm->has_dual_dac = 1;
+                       break;
+               default:
+                       cm->chip_version = 39;
+                       cm->can_ac3_hw = 1;
+                       break;
                }
+               cm->max_channels = 2;
        } else {
-               /* check reg 0Ch, bit 26 */
-               if (detect & CM_CHIP_8768) {
-                       cm->chip_version = 68;
-                       cm->max_channels = 8;
-                       cm->can_ac3_hw = 1;
-                       cm->has_dual_dac = 1;
-                       cm->can_multi_ch = 1;
-               } else if (detect & CM_CHIP_055) {
-                       cm->chip_version = 55;
-                       cm->max_channels = 6;
-                       cm->can_ac3_hw = 1;
-                       cm->has_dual_dac = 1;
-                       cm->can_multi_ch = 1;
-               } else if (detect & CM_CHIP_039) {
+               if (detect & CM_CHIP_039) {
                        cm->chip_version = 39;
                        if (detect & CM_CHIP_039_6CH) /* 4 or 6 channels */
                                cm->max_channels = 6;
                        else
                                cm->max_channels = 4;
-                       cm->can_ac3_hw = 1;
-                       cm->has_dual_dac = 1;
-                       cm->can_multi_ch = 1;
+               } else if (detect & CM_CHIP_8768) {
+                       cm->chip_version = 68;
+                       cm->max_channels = 8;
                } else {
-                       printk(KERN_ERR "chip %x version not supported\n", detect);
+                       cm->chip_version = 55;
+                       cm->max_channels = 6;
                }
+               cm->can_ac3_hw = 1;
+               cm->can_multi_ch = 1;
        }
 }
 
@@ -2778,10 +2797,17 @@ static int __devinit snd_cmipci_create_fm(struct cmipci *cm, long fm_port)
        struct snd_opl3 *opl3;
        int err;
 
-       /* first try FM regs in PCI port range */
-       iosynth = cm->iobase + CM_REG_FM_PCI;
-       err = snd_opl3_create(cm->card, iosynth, iosynth + 2,
-                             OPL3_HW_OPL3, 1, &opl3);
+       if (!fm_port)
+               goto disable_fm;
+
+       if (cm->chip_version >= 39) {
+               /* first try FM regs in PCI port range */
+               iosynth = cm->iobase + CM_REG_FM_PCI;
+               err = snd_opl3_create(cm->card, iosynth, iosynth + 2,
+                                     OPL3_HW_OPL3, 1, &opl3);
+       } else {
+               err = -EIO;
+       }
        if (err < 0) {
                /* then try legacy ports */
                val = snd_cmipci_read(cm, CM_REG_LEGACY_CTRL) & ~CM_FMSEL_MASK;
@@ -2792,7 +2818,7 @@ static int __devinit snd_cmipci_create_fm(struct cmipci *cm, long fm_port)
                case 0x3C8: val |= CM_FMSEL_3C8; break;
                case 0x388: val |= CM_FMSEL_388; break;
                default:
-                           return 0;
+                       goto disable_fm;
                }
                snd_cmipci_write(cm, CM_REG_LEGACY_CTRL, val);
                /* enable FM */
@@ -2802,11 +2828,7 @@ static int __devinit snd_cmipci_create_fm(struct cmipci *cm, long fm_port)
                                    OPL3_HW_OPL3, 0, &opl3) < 0) {
                        printk(KERN_ERR "cmipci: no OPL device at %#lx, "
                               "skipping...\n", iosynth);
-                       /* disable FM */
-                       snd_cmipci_write(cm, CM_REG_LEGACY_CTRL,
-                                        val & ~CM_FMSEL_MASK);
-                       snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_FM_EN);
-                       return 0;
+                       goto disable_fm;
                }
        }
        if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
@@ -2814,6 +2836,11 @@ static int __devinit snd_cmipci_create_fm(struct cmipci *cm, long fm_port)
                return err;
        }
        return 0;
+
+ disable_fm:
+       snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_FMSEL_MASK);
+       snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_FM_EN);
+       return 0;
 }
 
 static int __devinit snd_cmipci_create(struct snd_card *card, struct pci_dev *pci,
@@ -2824,9 +2851,10 @@ static int __devinit snd_cmipci_create(struct snd_card *card, struct pci_dev *pc
        static struct snd_device_ops ops = {
                .dev_free =     snd_cmipci_dev_free,
        };
-       unsigned int val = 0;
+       unsigned int val;
        long iomidi;
-       int integrated_midi;
+       int integrated_midi = 0;
+       char modelstr[16];
        int pcm_index, pcm_spdif_index;
        static struct pci_device_id intel_82437vx[] = {
                { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437VX) },
@@ -2862,7 +2890,7 @@ static int __devinit snd_cmipci_create(struct snd_card *card, struct pci_dev *pc
        cm->iobase = pci_resource_start(pci, 0);
 
        if (request_irq(pci->irq, snd_cmipci_interrupt,
-                       SA_INTERRUPT|SA_SHIRQ, card->driver, cm)) {
+                       IRQF_SHARED, card->driver, cm)) {
                snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
                snd_cmipci_free(cm);
                return -EBUSY;
@@ -2926,15 +2954,55 @@ static int __devinit snd_cmipci_create(struct snd_card *card, struct pci_dev *pc
                break;
        }
 
+       if (cm->chip_version < 68) {
+               val = pci->device < 0x110 ? 8338 : 8738;
+       } else {
+               switch (snd_cmipci_read_b(cm, CM_REG_INT_HLDCLR + 3) & 0x03) {
+               case 0:
+                       val = 8769;
+                       break;
+               case 2:
+                       val = 8762;
+                       break;
+               default:
+                       switch ((pci->subsystem_vendor << 16) |
+                               pci->subsystem_device) {
+                       case 0x13f69761:
+                       case 0x584d3741:
+                       case 0x584d3751:
+                       case 0x584d3761:
+                       case 0x584d3771:
+                       case 0x72848384:
+                               val = 8770;
+                               break;
+                       default:
+                               val = 8768;
+                               break;
+                       }
+               }
+       }
+       sprintf(card->shortname, "C-Media CMI%d", val);
+       if (cm->chip_version < 68)
+               sprintf(modelstr, " (model %d)", cm->chip_version);
+       else
+               modelstr[0] = '\0';
+       sprintf(card->longname, "%s%s at %#lx, irq %i",
+               card->shortname, modelstr, cm->iobase, cm->irq);
+
        if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, cm, &ops)) < 0) {
                snd_cmipci_free(cm);
                return err;
        }
 
-       integrated_midi = snd_cmipci_read_b(cm, CM_REG_MPU_PCI) != 0xff;
-       if (integrated_midi && mpu_port[dev] == 1)
-               iomidi = cm->iobase + CM_REG_MPU_PCI;
-       else {
+       if (cm->chip_version >= 39) {
+               val = snd_cmipci_read_b(cm, CM_REG_MPU_PCI + 1);
+               if (val != 0x00 && val != 0xff) {
+                       iomidi = cm->iobase + CM_REG_MPU_PCI;
+                       integrated_midi = 1;
+               }
+       }
+       if (!integrated_midi) {
+               val = 0;
                iomidi = mpu_port[dev];
                switch (iomidi) {
                case 0x320: val = CM_VMPU_320; break;
@@ -2948,11 +3016,21 @@ static int __devinit snd_cmipci_create(struct snd_card *card, struct pci_dev *pc
                        snd_cmipci_write(cm, CM_REG_LEGACY_CTRL, val);
                        /* enable UART */
                        snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_UART_EN);
+                       if (inb(iomidi + 1) == 0xff) {
+                               snd_printk(KERN_ERR "cannot enable MPU-401 port"
+                                          " at %#lx\n", iomidi);
+                               snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1,
+                                                    CM_UART_EN);
+                               iomidi = 0;
+                       }
                }
        }
 
-       if ((err = snd_cmipci_create_fm(cm, fm_port[dev])) < 0)
-               return err;
+       if (cm->chip_version < 68) {
+               err = snd_cmipci_create_fm(cm, fm_port[dev]);
+               if (err < 0)
+                       return err;
+       }
 
        /* reset mixer */
        snd_cmipci_mixer_write(cm, 0, 0);
@@ -2964,11 +3042,9 @@ static int __devinit snd_cmipci_create(struct snd_card *card, struct pci_dev *pc
        if ((err = snd_cmipci_pcm_new(cm, pcm_index)) < 0)
                return err;
        pcm_index++;
-       if (cm->has_dual_dac) {
-               if ((err = snd_cmipci_pcm2_new(cm, pcm_index)) < 0)
-                       return err;
-               pcm_index++;
-       }
+       if ((err = snd_cmipci_pcm2_new(cm, pcm_index)) < 0)
+               return err;
+       pcm_index++;
        if (cm->can_ac3_hw || cm->can_ac3_sw) {
                pcm_spdif_index = pcm_index;
                if ((err = snd_cmipci_pcm_spdif_new(cm, pcm_index)) < 0)
@@ -3052,15 +3128,6 @@ static int __devinit snd_cmipci_probe(struct pci_dev *pci,
        }
        card->private_data = cm;
 
-       sprintf(card->shortname, "C-Media PCI %s", card->driver);
-       sprintf(card->longname, "%s (model %d) at 0x%lx, irq %i",
-               card->shortname,
-               cm->chip_version,
-               cm->iobase,
-               cm->irq);
-
-       //snd_printd("%s is detected\n", card->longname);
-
        if ((err = snd_card_register(card)) < 0) {
                snd_card_free(card);
                return err;
@@ -3122,9 +3189,9 @@ static int snd_cmipci_suspend(struct pci_dev *pci, pm_message_t state)
        /* disable ints */
        snd_cmipci_write(cm, CM_REG_INT_HLDCLR, 0);
 
-       pci_set_power_state(pci, PCI_D3hot);
        pci_disable_device(pci);
        pci_save_state(pci);
+       pci_set_power_state(pci, pci_choose_state(pci, state));
        return 0;
 }
 
@@ -3134,9 +3201,14 @@ static int snd_cmipci_resume(struct pci_dev *pci)
        struct cmipci *cm = card->private_data;
        int i;
 
-       pci_restore_state(pci);
-       pci_enable_device(pci);
        pci_set_power_state(pci, PCI_D0);
+       pci_restore_state(pci);
+       if (pci_enable_device(pci) < 0) {
+               printk(KERN_ERR "cmipci: pci_enable_device failed, "
+                      "disabling device\n");
+               snd_card_disconnect(card);
+               return -EIO;
+       }
        pci_set_master(pci);
 
        /* reset / initialize to a sane state */