]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - sound/pci/azt3328.c
Merge branch 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux...
[mv-sheeva.git] / sound / pci / azt3328.c
index f197fbac10ab90bf4aaeaa0b12ed53a96b66339c..15447a3216dd70bea11ed95b3e92e3d168e59337 100644 (file)
  *  in the first place >:-P}),
  *  I was forced to base this driver on reverse engineering
  *  (3 weeks' worth of evenings filled with driver work).
- *  (and no, I did NOT go the easy way: to pick up a PCI128 for 9 Euros)
+ *  (and no, I did NOT go the easy way: to pick up a SB PCI128 for 9 Euros)
  *
  *  The AZF3328 chip (note: AZF3328, *not* AZT3328, that's just the driver name
  *  for compatibility reasons) has the following features:
  *
  *  - builtin AC97 conformant codec (SNR over 80dB)
- *    (really AC97 compliant?? I really doubt it when looking
- *    at the mixer register layout)
+ *    Note that "conformant" != "compliant"!! this chip's mixer register layout
+ *    *differs* from the standard AC97 layout:
+ *    they chose to not implement the headphone register (which is not a
+ *    problem since it's merely optional), yet when doing this, they committed
+ *    the grave sin of letting other registers follow immediately instead of
+ *    keeping a headphone dummy register, thereby shifting the mixer register
+ *    addresses illegally. So far unfortunately it looks like the very flexible
+ *    ALSA AC97 support is still not enough to easily compensate for such a
+ *    grave layout violation despite all tweaks and quirks mechanisms it offers.
  *  - builtin genuine OPL3
  *  - full duplex 16bit playback/record at independent sampling rate
  *  - MPU401 (+ legacy address support) FIXME: how to enable legacy addr??
  *    The standard suspend/resume functionality could probably make use of
  *    some improvement, too...
  *  - figure out what all unknown port bits are responsible for
+ *  - figure out some cleverly evil scheme to possibly make ALSA AC97 code
+ *    fully accept our quite incompatible ""AC97"" mixer and thus save some
+ *    code (but I'm not too optimistic that doing this is possible at all)
  */
 
 #include <sound/driver.h>
@@ -329,10 +339,8 @@ snd_azf3328_mixer_write_volume_gradually(const struct snd_azf3328 *chip, int reg
        else
                dst_vol_left &= ~0x80;
 
-       do
-       {
-               if (!left_done)
-               {
+       do {
+               if (!left_done) {
                        if (curr_vol_left > dst_vol_left)
                                curr_vol_left--;
                        else
@@ -342,8 +350,7 @@ snd_azf3328_mixer_write_volume_gradually(const struct snd_azf3328 *chip, int reg
                            left_done = 1;
                        outb(curr_vol_left, portbase + 1);
                }
-               if (!right_done)
-               {
+               if (!right_done) {
                        if (curr_vol_right > dst_vol_right)
                                curr_vol_right--;
                        else
@@ -358,8 +365,7 @@ snd_azf3328_mixer_write_volume_gradually(const struct snd_azf3328 *chip, int reg
                }
                if (delay)
                        mdelay(delay);
-       }
-       while ((!left_done) || (!right_done));
+       } while ((!left_done) || (!right_done));
        snd_azf3328_dbgcallleave();
 }
 
@@ -526,15 +532,18 @@ snd_azf3328_info_mixer_enum(struct snd_kcontrol *kcontrol,
                            struct snd_ctl_elem_info *uinfo)
 {
        static const char * const texts1[] = {
-               "ModemOut1", "ModemOut2"
+               "Mic1", "Mic2"
        };
        static const char * const texts2[] = {
-               "MonoSelectSource1", "MonoSelectSource2"
+               "Mix", "Mic"
        };
        static const char * const texts3[] = {
                 "Mic", "CD", "Video", "Aux",
                "Line", "Mix", "Mix Mono", "Phone"
         };
+       static const char * const texts4[] = {
+               "pre 3D", "post 3D"
+        };
        struct azf3328_mixer_reg reg;
 
        snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
@@ -543,14 +552,19 @@ snd_azf3328_info_mixer_enum(struct snd_kcontrol *kcontrol,
         uinfo->value.enumerated.items = reg.enum_c;
         if (uinfo->value.enumerated.item > reg.enum_c - 1U)
                 uinfo->value.enumerated.item = reg.enum_c - 1U;
-       if (reg.reg == IDX_MIXER_ADVCTL2)
-       {
-               if (reg.lchan_shift == 8) /* modem out sel */
+       if (reg.reg == IDX_MIXER_ADVCTL2) {
+               switch(reg.lchan_shift) {
+               case 8: /* modem out sel */
                        strcpy(uinfo->value.enumerated.name, texts1[uinfo->value.enumerated.item]);
-               else /* mono sel source */
+                       break;
+               case 9: /* mono sel source */
                        strcpy(uinfo->value.enumerated.name, texts2[uinfo->value.enumerated.item]);
-       }
-       else
+                       break;
+               case 15: /* PCM Out Path */
+                       strcpy(uinfo->value.enumerated.name, texts4[uinfo->value.enumerated.item]);
+                       break;
+               }
+       } else
                strcpy(uinfo->value.enumerated.name, texts3[uinfo->value.enumerated.item]
 );
         return 0;
@@ -566,12 +580,10 @@ snd_azf3328_get_mixer_enum(struct snd_kcontrol *kcontrol,
         
        snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
        val = snd_azf3328_mixer_inw(chip, reg.reg);
-       if (reg.reg == IDX_MIXER_REC_SELECT)
-       {
+       if (reg.reg == IDX_MIXER_REC_SELECT) {
                ucontrol->value.enumerated.item[0] = (val >> 8) & (reg.enum_c - 1);
                ucontrol->value.enumerated.item[1] = (val >> 0) & (reg.enum_c - 1);
-       }
-       else
+       } else
                ucontrol->value.enumerated.item[0] = (val >> reg.lchan_shift) & (reg.enum_c - 1);
 
        snd_azf3328_dbgmixer("get_enum: %02x is %04x -> %d|%d (shift %02d, enum_c %d)\n",
@@ -591,16 +603,13 @@ snd_azf3328_put_mixer_enum(struct snd_kcontrol *kcontrol,
        snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
        oreg = snd_azf3328_mixer_inw(chip, reg.reg);
        val = oreg;
-       if (reg.reg == IDX_MIXER_REC_SELECT)
-       {
+       if (reg.reg == IDX_MIXER_REC_SELECT) {
                if (ucontrol->value.enumerated.item[0] > reg.enum_c - 1U ||
                ucontrol->value.enumerated.item[1] > reg.enum_c - 1U)
                        return -EINVAL;
                val = (ucontrol->value.enumerated.item[0] << 8) |
                      (ucontrol->value.enumerated.item[1] << 0);
-       }
-       else
-       {
+       } else {
                if (ucontrol->value.enumerated.item[0] > reg.enum_c - 1U)
                        return -EINVAL;
                val &= ~((reg.enum_c - 1) << reg.lchan_shift);
@@ -641,13 +650,14 @@ static const struct snd_kcontrol_new snd_azf3328_mixer_controls[] __devinitdata
        AZF3328_MIXER_VOL_MONO("Modem Playback Volume", IDX_MIXER_MODEMOUT, 0x1f, 1),
        AZF3328_MIXER_SWITCH("Modem Capture Switch", IDX_MIXER_MODEMIN, 15, 1),
        AZF3328_MIXER_VOL_MONO("Modem Capture Volume", IDX_MIXER_MODEMIN, 0x1f, 1),
-       AZF3328_MIXER_ENUM("Modem Out Select", IDX_MIXER_ADVCTL2, 2, 8),
-       AZF3328_MIXER_ENUM("Mono Select Source", IDX_MIXER_ADVCTL2, 2, 9),
+       AZF3328_MIXER_ENUM("Mic Select", IDX_MIXER_ADVCTL2, 2, 8),
+       AZF3328_MIXER_ENUM("Mono Output Select", IDX_MIXER_ADVCTL2, 2, 9),
+       AZF3328_MIXER_ENUM("PCM", IDX_MIXER_ADVCTL2, 2, 15), /* PCM Out Path, place in front since it controls *both* 3D and Bass/Treble! */
        AZF3328_MIXER_VOL_SPECIAL("Tone Control - Treble", IDX_MIXER_BASSTREBLE, 0x07, 1, 0),
        AZF3328_MIXER_VOL_SPECIAL("Tone Control - Bass", IDX_MIXER_BASSTREBLE, 0x07, 9, 0),
        AZF3328_MIXER_SWITCH("3D Control - Switch", IDX_MIXER_ADVCTL2, 13, 0),
-       AZF3328_MIXER_VOL_SPECIAL("3D Control - Wide", IDX_MIXER_ADVCTL1, 0x07, 1, 0), /* "3D Width" */
-       AZF3328_MIXER_VOL_SPECIAL("3D Control - Space", IDX_MIXER_ADVCTL1, 0x03, 8, 0), /* "Hifi 3D" */
+       AZF3328_MIXER_VOL_SPECIAL("3D Control - Width", IDX_MIXER_ADVCTL1, 0x07, 1, 0), /* "3D Width" */
+       AZF3328_MIXER_VOL_SPECIAL("3D Control - Depth", IDX_MIXER_ADVCTL1, 0x03, 8, 0), /* "Hifi 3D" */
 #if MIXER_TESTING
        AZF3328_MIXER_SWITCH("0", IDX_MIXER_ADVCTL2, 0, 0),
        AZF3328_MIXER_SWITCH("1", IDX_MIXER_ADVCTL2, 1, 0),
@@ -825,22 +835,18 @@ snd_azf3328_setdmaa(struct snd_azf3328 *chip,
        unsigned int is_running;
 
        snd_azf3328_dbgcallenter();
-       if (do_recording)
-       {
+       if (do_recording) {
                /* access capture registers, i.e. skip playback reg section */
                portbase = chip->codec_port + 0x20;
                is_running = chip->is_recording;
-       }
-       else
-       {
+       } else {
                /* access the playback register section */
                portbase = chip->codec_port + 0x00;
                is_running = chip->is_playing;
        }
 
        /* AZF3328 uses a two buffer pointer DMA playback approach */
-       if (!is_running)
-       {
+       if (!is_running) {
                unsigned long addr_area2;
                unsigned long count_areas, count_tmp; /* width 32bit -- overflow!! */
                count_areas = size/2;
@@ -1203,8 +1209,7 @@ snd_azf3328_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                snd_azf3328_codec_inw(chip, IDX_IO_PLAY_IRQTYPE),
                status);
                
-       if (status & IRQ_TIMER)
-       {
+       if (status & IRQ_TIMER) {
                /* snd_azf3328_dbgplay("timer %ld\n", inl(chip->codec_port+IDX_IO_TIMER_VALUE) & TIMER_VALUE_MASK); */
                if (chip->timer)
                        snd_timer_interrupt(chip->timer, chip->timer->sticks);
@@ -1214,50 +1219,43 @@ snd_azf3328_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                spin_unlock(&chip->reg_lock);
                snd_azf3328_dbgplay("azt3328: timer IRQ\n");
        }
-       if (status & IRQ_PLAYBACK)
-       {
+       if (status & IRQ_PLAYBACK) {
                spin_lock(&chip->reg_lock);
                which = snd_azf3328_codec_inb(chip, IDX_IO_PLAY_IRQTYPE);
                /* ack all IRQ types immediately */
                snd_azf3328_codec_outb(chip, IDX_IO_PLAY_IRQTYPE, which);
                        spin_unlock(&chip->reg_lock);
 
-               if (chip->pcm && chip->playback_substream)
-               {
+               if (chip->pcm && chip->playback_substream) {
                        snd_pcm_period_elapsed(chip->playback_substream);
                        snd_azf3328_dbgplay("PLAY period done (#%x), @ %x\n",
                                which,
                                inl(chip->codec_port+IDX_IO_PLAY_DMA_CURRPOS));
-               }
-               else
+               } else
                        snd_azf3328_dbgplay("azt3328: ouch, irq handler problem!\n");
                if (which & IRQ_PLAY_SOMETHING)
                        snd_azf3328_dbgplay("azt3328: unknown play IRQ type occurred, please report!\n");
        }
-       if (status & IRQ_RECORDING)
-       {
+       if (status & IRQ_RECORDING) {
                 spin_lock(&chip->reg_lock);
                which = snd_azf3328_codec_inb(chip, IDX_IO_REC_IRQTYPE);
                /* ack all IRQ types immediately */
                snd_azf3328_codec_outb(chip, IDX_IO_REC_IRQTYPE, which);
                spin_unlock(&chip->reg_lock);
 
-               if (chip->pcm && chip->capture_substream)
-               {
+               if (chip->pcm && chip->capture_substream) {
                        snd_pcm_period_elapsed(chip->capture_substream);
                        snd_azf3328_dbgplay("REC  period done (#%x), @ %x\n",
                                which,
                                inl(chip->codec_port+IDX_IO_REC_DMA_CURRPOS));
-               }
-               else
+               } else
                        snd_azf3328_dbgplay("azt3328: ouch, irq handler problem!\n");
                if (which & IRQ_REC_SOMETHING)
                        snd_azf3328_dbgplay("azt3328: unknown rec IRQ type occurred, please report!\n");
        }
        /* MPU401 has less critical IRQ requirements
         * than timer and playback/recording, right? */
-       if (status & IRQ_MPU401)
-       {
+       if (status & IRQ_MPU401) {
                snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs);
 
                /* hmm, do we have to ack the IRQ here somehow?
@@ -1551,8 +1549,7 @@ snd_azf3328_timer_start(struct snd_timer *timer)
        snd_azf3328_dbgcallenter();
        chip = snd_timer_chip(timer);
        delay = ((timer->sticks * seqtimer_scaling) - 1) & TIMER_VALUE_MASK;
-       if (delay < 49)
-       {
+       if (delay < 49) {
                /* uhoh, that's not good, since user-space won't know about
                 * this timing tweak
                 * (we need to do it to avoid a lockup, though) */
@@ -1727,7 +1724,7 @@ snd_azf3328_create(struct snd_card *card,
        chip->synth_port = pci_resource_start(pci, 3);
        chip->mixer_port = pci_resource_start(pci, 4);
 
-       if (request_irq(pci->irq, snd_azf3328_interrupt, SA_INTERRUPT|SA_SHIRQ, card->shortname, (void *)chip)) {
+       if (request_irq(pci->irq, snd_azf3328_interrupt, IRQF_DISABLED|IRQF_SHARED, card->shortname, (void *)chip)) {
                snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
                err = -EBUSY;
                goto out_err;
@@ -1809,8 +1806,8 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
        card->private_data = chip;
 
        if ((err = snd_mpu401_uart_new( card, 0, MPU401_HW_MPU401,
-                                       chip->mpu_port, 1, pci->irq, 0,
-                                       &chip->rmidi)) < 0) {
+                                       chip->mpu_port, MPU401_INFO_INTEGRATED,
+                                       pci->irq, 0, &chip->rmidi)) < 0) {
                snd_printk(KERN_ERR "azf3328: no MPU-401 device at 0x%lx?\n", chip->mpu_port);
                goto out_err;
        }