Hi.  I have an Compaq Armada M700 laptop and an ArmadaStation II
laptop.  The laptop has a Maestro 2E sound chipset in it, and the
docking station has the matching ES978 chip which controls
speakers and handles volume up/down/mute buttons on the side of
the docking station.  The maestro.c driver in 2.4.4 doesn't have
support for these, so I wrote some code to do it.  The following
patch adds support.  It does the following:

        * Turns on the docking station speakers and sets the
          playback volume on them to the max.  (Their volume is
          still scaled based on the Maestro's master volume.)

        * Adds basic support for the hardware volume control
          buttons: the up button increases volume by 10%, the
          down button decreases volume by 10%, the mute button
          toggles the master volume on and off.

It doesn't do any of the following:

        * Add the docking station volumes to the mixer--it just
          hard-wires them to the max values.  Should I do this?

        * Make the buttons from the hardware volume control
          available to software.  Should I do this (I assume that
          hard-wiring +/- 10% isn't really acceptable)?  What's
          the best way to do it--create a /dev/es978buttons and a
          daemon, or what?

        * Add any support for the recording features of the
          docking station--just playback.

All comments appreciated.

--- /root/linux-2.4.4.clean/drivers/sound/maestro.c     Fri Mar  2 14:12:11 2001
+++ linux-2.4.5pre1/drivers/sound/maestro.c     Sun May 13 17:14:07 2001
@@ -115,6 +115,8 @@
  *     themselves, but we'll see.  
  *     
  * History
+ *  (still kind of v0.14) May 13 2001 - Ben Pfaff <[EMAIL PROTECTED]>
+ *      Add support for 978 docking and basic hardware volume control
  *  (still kind of v0.14) Nov 23 - Alan Cox <[EMAIL PROTECTED]>
  *     Add clocking= for people with seriously warped hardware
  *  (still v0.14) Nov 10 2000 - Bartlomiej Zolnierkiewicz <[EMAIL PROTECTED]>
@@ -189,7 +191,7 @@
  *     fix bob frequency
  *     endianness
  *     do smart things with ac97 2.0 bits.
- *     docking and dual codecs and 978?
+ *     dual codecs
  *     leave 54->61 open
  *
  *     it also would be fun to have a mode that would not use pci dma at all
@@ -483,8 +485,12 @@
 
        int bob_freq;
        char dsps_open;
+
+       int dock_mute_vol;
 };
 
+static void set_mixer(struct ess_card *card,unsigned int mixer, unsigned int val );
+
 static unsigned 
 ld2(unsigned int x)
 {
@@ -983,6 +989,17 @@
                outw(inw(ioaddr+0x68) | 0x600, ioaddr + 0x68);
                outw(0x0209, ioaddr + 0x60);
        }
+
+       /* Turn on the 978 docking chip.
+          First frob the "master output enable" bit,
+          then set most of the playback volume control registers to max. */
+       outb(inb(ioaddr+0xc0)|(1<<5), ioaddr+0xc0);
+       outb(0xff, ioaddr+0xc3);
+       outb(0xff, ioaddr+0xc4);
+       outb(0xff, ioaddr+0xc6);
+       outb(0xff, ioaddr+0xc8);
+       outb(0x3f, ioaddr+0xcf);
+       outb(0x3f, ioaddr+0xd0);
 }
 /*
  *     Indirect register access. Not all registers are readable so we
@@ -1895,19 +1912,52 @@
        outw(inw(c->iobase+4)&1, c->iobase+4);
 
 /*     M_printk("maestro int: %x\n",event);*/
-
        if(event&(1<<6))
        {
-               /* XXX if we have a hw volume control int enable
-                       all the ints?  doesn't make sense.. */
-               event = inw(c->iobase+0x18);
-               outb(0xFF, c->iobase+0x1A);
-       }
-       else
-       {
-               /* else ack 'em all, i imagine */
-               outb(0xFF, c->iobase+0x1A);
+               int x;
+               enum {UP_EVT, DOWN_EVT, MUTE_EVT} vol_evt;
+               int volume;
+
+               /* Figure out which volume control button was pushed,
+                  based on differences from the default register
+                  values. */
+               x = inb(c->iobase+0x1c);
+               if (x&1) vol_evt = MUTE_EVT;
+               else if (((x>>1)&7) > 4) vol_evt = UP_EVT;
+               else vol_evt = DOWN_EVT;
+
+               /* Reset the volume control registers. */
+               outb(0x88, c->iobase+0x1c);
+               outb(0x88, c->iobase+0x1d);
+               outb(0x88, c->iobase+0x1e);
+               outb(0x88, c->iobase+0x1f);
+
+               /* Deal with the button press in a hammer-handed
+                  manner by adjusting the master mixer volume. */
+               volume = c->mix.mixer_state[0] & 0xff;
+               if (vol_evt == UP_EVT) {
+                       volume += 10;
+                       if (volume > 100)
+                               volume = 100;
+               }
+               else if (vol_evt == DOWN_EVT) {
+                       volume -= 10;
+                       if (volume < 0)
+                               volume = 0;
+               } else {
+                       /* vol_evt == MUTE_EVT */
+                       if (volume == 0)
+                               volume = c->dock_mute_vol;
+                       else {
+                               c->dock_mute_vol = volume;
+                               volume = 0;
+                       }
+               }
+               set_mixer (c, 0, (volume << 8) | volume);
        }
+
+       /* Ack all the interrupts. */
+       outb(0xFF, c->iobase+0x1A);
                
        /*
         *      Update the pointers for all APU's we are running.
@@ -3087,8 +3137,8 @@
        /* XXX how do we know which to use? */
        w&=~(1<<14);            /* External clock */
        
-       w&=~(1<<7);             /* HWV off */
-       w&=~(1<<6);             /* Debounce off */
+       w|= (1<<7);             /* Hardware volume control on */
+       w|= (1<<6);             /* Debounce off: easier to push the HWV buttons. */
        w&=~(1<<5);             /* GPIO 4:5 */
        w|= (1<<4);             /* Disconnect from the CHI.  Enabling this made a dell 
7500 work. */
        w&=~(1<<2);             /* MIDI fix off (undoc) */
@@ -3106,6 +3156,12 @@
         
        pci_write_config_word(pcidev, 0x40, w);
 
+       /* Set up 978 docking control chip. */
+       pci_read_config_word(pcidev, 0x58, &w);
+       w|=1<<2;        /* Enable 978. */
+       w|=1<<3;        /* Turn on 978 hardware volume control. */
+       w&=~(1<<11);    /* Turn on 978 mixer volume control. */
+       pci_write_config_word(pcidev, 0x58, w);
        
        sound_reset(iobase);
 
@@ -3170,7 +3226,7 @@
        outw(w, iobase+0x18);
 
        w=inw(iobase+0x18);
-       w&=~(1<<6);             /* Harpo off */
+       w|=1<<6;                /* Hardware volume control interrupt on. */
        outw(w, iobase+0x18);
        
        w=inw(iobase+0x18);
@@ -3193,6 +3249,13 @@
        w|=(1<<0);              /* SB IRQ on */
        outw(w, iobase+0x18);
 
+       /* Set hardware volume control registers to midpoints.
+          We can tell which button was pushed based on how they change. */
+       outb(0x88, iobase+0x1c);
+       outb(0x88, iobase+0x1d);
+       outb(0x88, iobase+0x1e);
+       outb(0x88, iobase+0x1f);
+
        /* it appears some maestros (dell 7500) only work if these are set,
                regardless of wether we use the assp or not. */
 
@@ -3366,6 +3429,8 @@
        spin_lock_init(&card->lock);
        init_waitqueue_head(&card->suspend_queue);
        devs = card;
+
+       card->dock_mute_vol = 50;
        
        /* init our groups of 6 apus */
        for(i=0;i<NR_DSPS;i++)

-- 
"Unix... is not so much a product
 as it is a painstakingly compiled oral history
 of the hacker subculture."
--Neal Stephenson
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to