ALSA: es1968,maestro3 - Use work for hw-volume control

Instead of tasklet, use workq for handling the hw-volume control.
This reduces lots of spinlocks.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c
index 3fa4659..99ea932 100644
--- a/sound/pci/es1968.c
+++ b/sound/pci/es1968.c
@@ -554,9 +554,8 @@
 #else
 	struct snd_kcontrol *master_switch; /* for h/w volume control */
 	struct snd_kcontrol *master_volume;
-	spinlock_t ac97_lock;
-	struct tasklet_struct hwvol_tq;
 #endif
+	struct work_struct hwvol_work;
 
 #ifdef CONFIG_SND_ES1968_RADIO
 	struct snd_tea575x tea;
@@ -646,38 +645,23 @@
 static void snd_es1968_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)
 {
 	struct es1968 *chip = ac97->private_data;
-#ifndef CONFIG_SND_ES1968_INPUT
-	unsigned long flags;
-#endif
 
 	snd_es1968_ac97_wait(chip);
 
 	/* Write the bus */
-#ifndef CONFIG_SND_ES1968_INPUT
-	spin_lock_irqsave(&chip->ac97_lock, flags);
-#endif
 	outw(val, chip->io_port + ESM_AC97_DATA);
 	/*msleep(1);*/
 	outb(reg, chip->io_port + ESM_AC97_INDEX);
 	/*msleep(1);*/
-#ifndef CONFIG_SND_ES1968_INPUT
-	spin_unlock_irqrestore(&chip->ac97_lock, flags);
-#endif
 }
 
 static unsigned short snd_es1968_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
 {
 	u16 data = 0;
 	struct es1968 *chip = ac97->private_data;
-#ifndef CONFIG_SND_ES1968_INPUT
-	unsigned long flags;
-#endif
 
 	snd_es1968_ac97_wait(chip);
 
-#ifndef CONFIG_SND_ES1968_INPUT
-	spin_lock_irqsave(&chip->ac97_lock, flags);
-#endif
 	outb(reg | 0x80, chip->io_port + ESM_AC97_INDEX);
 	/*msleep(1);*/
 
@@ -685,9 +669,6 @@
 		data = inw(chip->io_port + ESM_AC97_DATA);
 		/*msleep(1);*/
 	}
-#ifndef CONFIG_SND_ES1968_INPUT
-	spin_unlock_irqrestore(&chip->ac97_lock, flags);
-#endif
 
 	return data;
 }
@@ -1904,13 +1885,10 @@
    (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 es1968_update_hw_volume(unsigned long private_data)
+static void es1968_update_hw_volume(struct work_struct *work)
 {
-	struct es1968 *chip = (struct es1968 *) private_data;
+	struct es1968 *chip = container_of(work, struct es1968, hwvol_work);
 	int x, val;
-#ifndef CONFIG_SND_ES1968_INPUT
-	unsigned long flags;
-#endif
 
 	/* Figure out which volume control button was pushed,
 	   based on differences from the default register
@@ -1929,18 +1907,11 @@
 	if (! chip->master_switch || ! chip->master_volume)
 		return;
 
-	/* FIXME: we can't call snd_ac97_* functions since here is in tasklet. */
-	spin_lock_irqsave(&chip->ac97_lock, flags);
-	val = chip->ac97->regs[AC97_MASTER];
+	val = snd_ac97_read(chip->ac97, AC97_MASTER);
 	switch (x) {
 	case 0x88:
 		/* mute */
 		val ^= 0x8000;
-		chip->ac97->regs[AC97_MASTER] = val;
-		outw(val, chip->io_port + ESM_AC97_DATA);
-		outb(AC97_MASTER, chip->io_port + ESM_AC97_INDEX);
-		snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
-			       &chip->master_switch->id);
 		break;
 	case 0xaa:
 		/* volume up */
@@ -1948,11 +1919,6 @@
 			val--;
 		if ((val & 0x7f00) > 0)
 			val -= 0x0100;
-		chip->ac97->regs[AC97_MASTER] = val;
-		outw(val, chip->io_port + ESM_AC97_DATA);
-		outb(AC97_MASTER, chip->io_port + ESM_AC97_INDEX);
-		snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
-			       &chip->master_volume->id);
 		break;
 	case 0x66:
 		/* volume down */
@@ -1960,14 +1926,11 @@
 			val++;
 		if ((val & 0x7f00) < 0x1f00)
 			val += 0x0100;
-		chip->ac97->regs[AC97_MASTER] = val;
-		outw(val, chip->io_port + ESM_AC97_DATA);
-		outb(AC97_MASTER, chip->io_port + ESM_AC97_INDEX);
-		snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
-			       &chip->master_volume->id);
 		break;
 	}
-	spin_unlock_irqrestore(&chip->ac97_lock, flags);
+	if (snd_ac97_update(chip->ac97, AC97_MASTER, val))
+		snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+			       &chip->master_volume->id);
 #else
 	if (!chip->input_dev)
 		return;
@@ -2013,11 +1976,7 @@
 	outw(inw(chip->io_port + 4) & 1, chip->io_port + 4);
 
 	if (event & ESM_HWVOL_IRQ)
-#ifdef CONFIG_SND_ES1968_INPUT
-		es1968_update_hw_volume((unsigned long)chip);
-#else
-		tasklet_schedule(&chip->hwvol_tq); /* we'll do this later */
-#endif
+		schedule_work(&chip->hwvol_work);
 
 	/* else ack 'em all, i imagine */
 	outb(0xFF, chip->io_port + 0x1A);
@@ -2426,6 +2385,7 @@
 		return 0;
 
 	chip->in_suspend = 1;
+	cancel_work_sync(&chip->hwvol_work);
 	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
 	snd_pcm_suspend_all(chip->pcm);
 	snd_ac97_suspend(chip->ac97);
@@ -2638,6 +2598,7 @@
 
 static int snd_es1968_free(struct es1968 *chip)
 {
+	cancel_work_sync(&chip->hwvol_work);
 #ifdef CONFIG_SND_ES1968_INPUT
 	if (chip->input_dev)
 		input_unregister_device(chip->input_dev);
@@ -2728,10 +2689,7 @@
 	INIT_LIST_HEAD(&chip->buf_list);
 	INIT_LIST_HEAD(&chip->substream_list);
 	mutex_init(&chip->memory_mutex);
-#ifndef CONFIG_SND_ES1968_INPUT
-	spin_lock_init(&chip->ac97_lock);
-	tasklet_init(&chip->hwvol_tq, es1968_update_hw_volume, (unsigned long)chip);
-#endif
+	INIT_WORK(&chip->hwvol_work, es1968_update_hw_volume);
 	chip->card = card;
 	chip->pci = pci;
 	chip->irq = -1;
diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c
index 64f6f62..0378126 100644
--- a/sound/pci/maestro3.c
+++ b/sound/pci/maestro3.c
@@ -850,11 +850,10 @@
 	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
+	struct work_struct hwvol_work;
 
 	unsigned int in_suspend;
 
@@ -1609,13 +1608,10 @@
    (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)
+static void snd_m3_update_hw_volume(struct work_struct *work)
 {
-	struct snd_m3 *chip = (struct snd_m3 *) private_data;
+	struct snd_m3 *chip = container_of(work, struct snd_m3, hwvol_work);
 	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
@@ -1645,21 +1641,13 @@
 	if (!chip->master_switch || !chip->master_volume)
 		return;
 
-	/* FIXME: we can't call snd_ac97_* functions since here is in tasklet. */
-	spin_lock_irqsave(&chip->ac97_lock, flags);
-
-	val = chip->ac97->regs[AC97_MASTER_VOL];
+	val = snd_ac97_read(chip->ac97, AC97_MASTER);
 	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 ^= 0x8000;
-		chip->ac97->regs[AC97_MASTER_VOL] = val;
-		outw(val, chip->iobase + CODEC_DATA);
-		outb(AC97_MASTER_VOL, chip->iobase + CODEC_COMMAND);
-		snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
-			       &chip->master_switch->id);
 		break;
 	case 0xaa:
 		/* counters increased by 1 -> volume up */
@@ -1667,11 +1655,6 @@
 			val--;
 		if ((val & 0x7f00) > 0)
 			val -= 0x0100;
-		chip->ac97->regs[AC97_MASTER_VOL] = val;
-		outw(val, chip->iobase + CODEC_DATA);
-		outb(AC97_MASTER_VOL, chip->iobase + CODEC_COMMAND);
-		snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
-			       &chip->master_volume->id);
 		break;
 	case 0x66:
 		/* counters decreased by 1 -> volume down */
@@ -1679,14 +1662,11 @@
 			val++;
 		if ((val & 0x7f00) < 0x1f00)
 			val += 0x0100;
-		chip->ac97->regs[AC97_MASTER_VOL] = val;
-		outw(val, chip->iobase + CODEC_DATA);
-		outb(AC97_MASTER_VOL, chip->iobase + CODEC_COMMAND);
-		snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
-			       &chip->master_volume->id);
 		break;
 	}
-	spin_unlock_irqrestore(&chip->ac97_lock, flags);
+	if (snd_ac97_update(chip->ac97, AC97_MASTER, val))
+		snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+			       &chip->master_switch->id);
 #else
 	if (!chip->input_dev)
 		return;
@@ -1730,11 +1710,7 @@
 		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
+		schedule_work(&chip->hwvol_work);
 
 	/*
 	 * ack an assp int if its running
@@ -2000,24 +1976,14 @@
 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;
+		goto fail;
 	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;
 }
@@ -2026,20 +1992,11 @@
 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
 }
 
 
@@ -2458,6 +2415,7 @@
 	struct m3_dma *s;
 	int i;
 
+	cancel_work_sync(&chip->hwvol_work);
 #ifdef CONFIG_SND_MAESTRO3_INPUT
 	if (chip->input_dev)
 		input_unregister_device(chip->input_dev);
@@ -2511,6 +2469,7 @@
 		return 0;
 
 	chip->in_suspend = 1;
+	cancel_work_sync(&chip->hwvol_work);
 	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
 	snd_pcm_suspend_all(chip->pcm);
 	snd_ac97_suspend(chip->ac97);
@@ -2667,9 +2626,6 @@
 	}
 
 	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:
@@ -2683,6 +2639,7 @@
 	chip->card = card;
 	chip->pci = pci;
 	chip->irq = -1;
+	INIT_WORK(&chip->hwvol_work, snd_m3_update_hw_volume);
 
 	chip->external_amp = enable_amp;
 	if (amp_gpio >= 0 && amp_gpio <= 0x0f)
@@ -2752,10 +2709,6 @@
 
 	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,
 			KBUILD_MODNAME, chip)) {
 		snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);