diff options
Diffstat (limited to 'sound/pci/maestro3.c')
| -rw-r--r-- | sound/pci/maestro3.c | 139 | 
1 files changed, 133 insertions, 6 deletions
diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index b56e33676780..3c40d726b46e 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -41,6 +41,7 @@  #include <linux/vmalloc.h>  #include <linux/moduleparam.h>  #include <linux/firmware.h> +#include <linux/input.h>  #include <sound/core.h>  #include <sound/info.h>  #include <sound/control.h> @@ -844,11 +845,17 @@ struct snd_m3 {  	struct m3_dma *substreams;  	spinlock_t reg_lock; -	spinlock_t ac97_lock; +#ifdef CONFIG_SND_MAESTRO3_INPUT +	struct input_dev *input_dev; +	char phys[64];			/* physical device path */ +#else +	spinlock_t ac97_lock;  	struct snd_kcontrol *master_switch;  	struct snd_kcontrol *master_volume;  	struct tasklet_struct hwvol_tq; +#endif +  	unsigned int in_suspend;  #ifdef CONFIG_PM @@ -1598,18 +1605,32 @@ static void snd_m3_update_ptr(struct snd_m3 *chip, struct m3_dma *s)  	}  } +/* The m3's hardware volume works by incrementing / decrementing 2 counters +   (without wrap around) in response to volume button presses and then +   generating an interrupt. The pair of counters is stored in bits 1-3 and 5-7 +   of a byte wide register. The meaning of bits 0 and 4 is unknown. */  static void snd_m3_update_hw_volume(unsigned long private_data)  {  	struct snd_m3 *chip = (struct snd_m3 *) private_data;  	int x, val; +#ifndef CONFIG_SND_MAESTRO3_INPUT  	unsigned long flags; +#endif  	/* Figure out which volume control button was pushed,  	   based on differences from the default register  	   values. */  	x = inb(chip->iobase + SHADOW_MIX_REG_VOICE) & 0xee; -	/* Reset the volume control registers. */ +	/* Reset the volume counters to 4. Tests on the allegro integrated +	   into a Compaq N600C laptop, have revealed that: +	   1) Writing any value will result in the 2 counters being reset to +	      4 so writing 0x88 is not strictly necessary +	   2) Writing to any of the 4 involved registers will reset all 4 +	      of them (and reading them always returns the same value for all +	      of them) +	   It could be that a maestro deviates from this, so leave the code +	   as is. */  	outb(0x88, chip->iobase + SHADOW_MIX_REG_VOICE);  	outb(0x88, chip->iobase + HW_VOL_COUNTER_VOICE);  	outb(0x88, chip->iobase + SHADOW_MIX_REG_MASTER); @@ -1620,6 +1641,7 @@ static void snd_m3_update_hw_volume(unsigned long private_data)  	if (chip->in_suspend)  		return; +#ifndef CONFIG_SND_MAESTRO3_INPUT  	if (!chip->master_switch || !chip->master_volume)  		return; @@ -1629,7 +1651,9 @@ static void snd_m3_update_hw_volume(unsigned long private_data)  	val = chip->ac97->regs[AC97_MASTER_VOL];  	switch (x) {  	case 0x88: -		/* mute */ +		/* The counters have not changed, yet we've received a HV +		   interrupt. According to tests run by various people this +		   happens when pressing the mute button. */  		val ^= 0x8000;  		chip->ac97->regs[AC97_MASTER_VOL] = val;  		outw(val, chip->iobase + CODEC_DATA); @@ -1638,7 +1662,7 @@ static void snd_m3_update_hw_volume(unsigned long private_data)  			       &chip->master_switch->id);  		break;  	case 0xaa: -		/* volume up */ +		/* counters increased by 1 -> volume up */  		if ((val & 0x7f) > 0)  			val--;  		if ((val & 0x7f00) > 0) @@ -1650,7 +1674,7 @@ static void snd_m3_update_hw_volume(unsigned long private_data)  			       &chip->master_volume->id);  		break;  	case 0x66: -		/* volume down */ +		/* counters decreased by 1 -> volume down */  		if ((val & 0x7f) < 0x1f)  			val++;  		if ((val & 0x7f00) < 0x1f00) @@ -1663,6 +1687,35 @@ static void snd_m3_update_hw_volume(unsigned long private_data)  		break;  	}  	spin_unlock_irqrestore(&chip->ac97_lock, flags); +#else +	if (!chip->input_dev) +		return; + +	val = 0; +	switch (x) { +	case 0x88: +		/* The counters have not changed, yet we've received a HV +		   interrupt. According to tests run by various people this +		   happens when pressing the mute button. */ +		val = KEY_MUTE; +		break; +	case 0xaa: +		/* counters increased by 1 -> volume up */ +		val = KEY_VOLUMEUP; +		break; +	case 0x66: +		/* counters decreased by 1 -> volume down */ +		val = KEY_VOLUMEDOWN; +		break; +	} + +	if (val) { +		input_report_key(chip->input_dev, val, 1); +		input_sync(chip->input_dev); +		input_report_key(chip->input_dev, val, 0); +		input_sync(chip->input_dev); +	} +#endif  }  static irqreturn_t snd_m3_interrupt(int irq, void *dev_id) @@ -1677,7 +1730,11 @@ static irqreturn_t snd_m3_interrupt(int irq, void *dev_id)  		return IRQ_NONE;  	if (status & HV_INT_PENDING) +#ifdef CONFIG_SND_MAESTRO3_INPUT +		snd_m3_update_hw_volume((unsigned long)chip); +#else  		tasklet_schedule(&chip->hwvol_tq); +#endif  	/*  	 * ack an assp int if its running @@ -1943,18 +2000,24 @@ static unsigned short  snd_m3_ac97_read(struct snd_ac97 *ac97, unsigned short reg)  {  	struct snd_m3 *chip = ac97->private_data; +#ifndef CONFIG_SND_MAESTRO3_INPUT  	unsigned long flags; +#endif  	unsigned short data = 0xffff;  	if (snd_m3_ac97_wait(chip))  		goto fail; +#ifndef CONFIG_SND_MAESTRO3_INPUT  	spin_lock_irqsave(&chip->ac97_lock, flags); +#endif  	snd_m3_outb(chip, 0x80 | (reg & 0x7f), CODEC_COMMAND);  	if (snd_m3_ac97_wait(chip))  		goto fail_unlock;  	data = snd_m3_inw(chip, CODEC_DATA);  fail_unlock: +#ifndef CONFIG_SND_MAESTRO3_INPUT  	spin_unlock_irqrestore(&chip->ac97_lock, flags); +#endif  fail:  	return data;  } @@ -1963,14 +2026,20 @@ static void  snd_m3_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)  {  	struct snd_m3 *chip = ac97->private_data; +#ifndef CONFIG_SND_MAESTRO3_INPUT  	unsigned long flags; +#endif  	if (snd_m3_ac97_wait(chip))  		return; +#ifndef CONFIG_SND_MAESTRO3_INPUT  	spin_lock_irqsave(&chip->ac97_lock, flags); +#endif  	snd_m3_outw(chip, val, CODEC_DATA);  	snd_m3_outb(chip, reg & 0x7f, CODEC_COMMAND); +#ifndef CONFIG_SND_MAESTRO3_INPUT  	spin_unlock_irqrestore(&chip->ac97_lock, flags); +#endif  } @@ -2077,7 +2146,9 @@ static int __devinit snd_m3_mixer(struct snd_m3 *chip)  {  	struct snd_ac97_bus *pbus;  	struct snd_ac97_template ac97; +#ifndef CONFIG_SND_MAESTRO3_INPUT  	struct snd_ctl_elem_id elem_id; +#endif  	int err;  	static struct snd_ac97_bus_ops ops = {  		.write = snd_m3_ac97_write, @@ -2097,6 +2168,7 @@ static int __devinit snd_m3_mixer(struct snd_m3 *chip)  	schedule_timeout_uninterruptible(msecs_to_jiffies(100));  	snd_ac97_write(chip->ac97, AC97_PCM, 0); +#ifndef CONFIG_SND_MAESTRO3_INPUT  	memset(&elem_id, 0, sizeof(elem_id));  	elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;  	strcpy(elem_id.name, "Master Playback Switch"); @@ -2105,6 +2177,7 @@ static int __devinit snd_m3_mixer(struct snd_m3 *chip)  	elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;  	strcpy(elem_id.name, "Master Playback Volume");  	chip->master_volume = snd_ctl_find_id(chip->card, &elem_id); +#endif  	return 0;  } @@ -2370,6 +2443,7 @@ snd_m3_enable_ints(struct snd_m3 *chip)  	val = ASSP_INT_ENABLE /*| MPU401_INT_ENABLE*/;  	if (chip->hv_config & HV_CTRL_ENABLE)  		val |= HV_INT_ENABLE; +	outb(val, chip->iobase + HOST_INT_STATUS);  	outw(val, io + HOST_INT_CTRL);  	outb(inb(io + ASSP_CONTROL_C) | ASSP_HOST_INT_ENABLE,  	     io + ASSP_CONTROL_C); @@ -2384,6 +2458,11 @@ static int snd_m3_free(struct snd_m3 *chip)  	struct m3_dma *s;  	int i; +#ifdef CONFIG_SND_MAESTRO3_INPUT +	if (chip->input_dev) +		input_unregister_device(chip->input_dev); +#endif +  	if (chip->substreams) {  		spin_lock_irq(&chip->reg_lock);  		for (i = 0; i < chip->num_substreams; i++) { @@ -2510,6 +2589,41 @@ static int m3_resume(struct pci_dev *pci)  }  #endif /* CONFIG_PM */ +#ifdef CONFIG_SND_MAESTRO3_INPUT +static int __devinit snd_m3_input_register(struct snd_m3 *chip) +{ +	struct input_dev *input_dev; +	int err; + +	input_dev = input_allocate_device(); +	if (!input_dev) +		return -ENOMEM; + +	snprintf(chip->phys, sizeof(chip->phys), "pci-%s/input0", +		 pci_name(chip->pci)); + +	input_dev->name = chip->card->driver; +	input_dev->phys = chip->phys; +	input_dev->id.bustype = BUS_PCI; +	input_dev->id.vendor  = chip->pci->vendor; +	input_dev->id.product = chip->pci->device; +	input_dev->dev.parent = &chip->pci->dev; + +	__set_bit(EV_KEY, input_dev->evbit); +	__set_bit(KEY_MUTE, input_dev->keybit); +	__set_bit(KEY_VOLUMEDOWN, input_dev->keybit); +	__set_bit(KEY_VOLUMEUP, input_dev->keybit); + +	err = input_register_device(input_dev); +	if (err) { +		input_free_device(input_dev); +		return err; +	} + +	chip->input_dev = input_dev; +	return 0; +} +#endif /* CONFIG_INPUT */  /*   */ @@ -2553,7 +2667,9 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci,  	}  	spin_lock_init(&chip->reg_lock); +#ifndef CONFIG_SND_MAESTRO3_INPUT  	spin_lock_init(&chip->ac97_lock); +#endif  	switch (pci->device) {  	case PCI_DEVICE_ID_ESS_ALLEGRO: @@ -2636,7 +2752,9 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci,  	snd_m3_hv_init(chip); +#ifndef CONFIG_SND_MAESTRO3_INPUT  	tasklet_init(&chip->hwvol_tq, snd_m3_update_hw_volume, (unsigned long)chip); +#endif  	if (request_irq(pci->irq, snd_m3_interrupt, IRQF_SHARED,  			card->driver, chip)) { @@ -2668,7 +2786,16 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci,  	if ((err = snd_m3_pcm(chip, 0)) < 0)  		return err; -     + +#ifdef CONFIG_SND_MAESTRO3_INPUT +	if (chip->hv_config & HV_CTRL_ENABLE) { +		err = snd_m3_input_register(chip); +		if (err) +			snd_printk(KERN_WARNING "Input device registration " +				"failed with error %i", err); +	} +#endif +  	snd_m3_enable_ints(chip);  	snd_m3_assp_continue(chip);  | 
