Takashi Iwai 0db6b6
From c1f0616124c455c5c762b6f123e40bba5df759e6 Mon Sep 17 00:00:00 2001
Takashi Iwai 0db6b6
From: Takashi Iwai <tiwai@suse.de>
Takashi Iwai 0db6b6
Date: Sun, 16 May 2021 18:17:55 +0200
Takashi Iwai 0db6b6
Subject: [PATCH] ALSA: intel8x0: Don't update period unless prepared
Takashi Iwai 0db6b6
Git-commit: c1f0616124c455c5c762b6f123e40bba5df759e6
Takashi Iwai 0db6b6
Patch-mainline: v5.13-rc3
Takashi Iwai 0db6b6
References: git-fixes
Takashi Iwai 0db6b6
Takashi Iwai 0db6b6
The interrupt handler of intel8x0 calls snd_intel8x0_update() whenever
Takashi Iwai 0db6b6
the hardware sets the corresponding status bit for each stream.  This
Takashi Iwai 0db6b6
works fine for most cases as long as the hardware behaves properly.
Takashi Iwai 0db6b6
But when the hardware gives a wrong bit set, this leads to a zero-
Takashi Iwai 0db6b6
division Oops, and reportedly, this seems what happened on a VM.
Takashi Iwai 0db6b6
Takashi Iwai 0db6b6
For fixing the crash, this patch adds a internal flag indicating that
Takashi Iwai 0db6b6
the stream is ready to be updated, and check it (as well as the flag
Takashi Iwai 0db6b6
being in suspended) to ignore such spurious update.
Takashi Iwai 0db6b6
Takashi Iwai 0db6b6
Cc: <stable@vger.kernel.org>
Takashi Iwai 0db6b6
Reported-and-tested-by: Sergey Senozhatsky <senozhatsky@chromium.org>
Takashi Iwai 0db6b6
Link: https://lore.kernel.org/r/s5h5yzi7uh0.wl-tiwai@suse.de
Takashi Iwai 0db6b6
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Takashi Iwai 0db6b6
Takashi Iwai 0db6b6
---
Takashi Iwai 0db6b6
 sound/pci/intel8x0.c |    7 +++++++
Takashi Iwai 0db6b6
 1 file changed, 7 insertions(+)
Takashi Iwai 0db6b6
Takashi Iwai 0db6b6
--- a/sound/pci/intel8x0.c
Takashi Iwai 0db6b6
+++ b/sound/pci/intel8x0.c
Takashi Iwai 0db6b6
@@ -354,6 +354,7 @@ struct ichdev {
Takashi Iwai 0db6b6
 	unsigned int ali_slot;			/* ALI DMA slot */
Takashi Iwai 0db6b6
 	struct ac97_pcm *pcm;
Takashi Iwai 0db6b6
 	int pcm_open_flag;
Takashi Iwai 0db6b6
+	unsigned int prepared:1;
Takashi Iwai 0db6b6
 	unsigned int suspended: 1;
Takashi Iwai 0db6b6
 };
Takashi Iwai 0db6b6
 
Takashi Iwai 0db6b6
@@ -714,6 +715,9 @@ static inline void snd_intel8x0_update(s
Takashi Iwai 0db6b6
 	int status, civ, i, step;
Takashi Iwai 0db6b6
 	int ack = 0;
Takashi Iwai 0db6b6
 
Takashi Iwai 0db6b6
+	if (!ichdev->prepared || ichdev->suspended)
Takashi Iwai 0db6b6
+		return;
Takashi Iwai 0db6b6
+
Takashi Iwai 0db6b6
 	spin_lock_irqsave(&chip->reg_lock, flags);
Takashi Iwai 0db6b6
 	status = igetbyte(chip, port + ichdev->roff_sr);
Takashi Iwai 0db6b6
 	civ = igetbyte(chip, port + ICH_REG_OFF_CIV);
Takashi Iwai 0db6b6
@@ -907,6 +911,7 @@ static int snd_intel8x0_hw_params(struct
Takashi Iwai 0db6b6
 	if (ichdev->pcm_open_flag) {
Takashi Iwai 0db6b6
 		snd_ac97_pcm_close(ichdev->pcm);
Takashi Iwai 0db6b6
 		ichdev->pcm_open_flag = 0;
Takashi Iwai 0db6b6
+		ichdev->prepared = 0;
Takashi Iwai 0db6b6
 	}
Takashi Iwai 0db6b6
 	err = snd_ac97_pcm_open(ichdev->pcm, params_rate(hw_params),
Takashi Iwai 0db6b6
 				params_channels(hw_params),
Takashi Iwai 0db6b6
@@ -928,6 +933,7 @@ static int snd_intel8x0_hw_free(struct s
Takashi Iwai 0db6b6
 	if (ichdev->pcm_open_flag) {
Takashi Iwai 0db6b6
 		snd_ac97_pcm_close(ichdev->pcm);
Takashi Iwai 0db6b6
 		ichdev->pcm_open_flag = 0;
Takashi Iwai 0db6b6
+		ichdev->prepared = 0;
Takashi Iwai 0db6b6
 	}
Takashi Iwai 0db6b6
 	return snd_pcm_lib_free_pages(substream);
Takashi Iwai 0db6b6
 }
Takashi Iwai 0db6b6
@@ -1002,6 +1008,7 @@ static int snd_intel8x0_pcm_prepare(stru
Takashi Iwai 0db6b6
 			ichdev->pos_shift = (runtime->sample_bits > 16) ? 2 : 1;
Takashi Iwai 0db6b6
 	}
Takashi Iwai 0db6b6
 	snd_intel8x0_setup_periods(chip, ichdev);
Takashi Iwai 0db6b6
+	ichdev->prepared = 1;
Takashi Iwai 0db6b6
 	return 0;
Takashi Iwai 0db6b6
 }
Takashi Iwai 0db6b6