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/