public inbox for linux-sound@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] ALSA: sc6000: fix board configuration state and resume handling
@ 2026-04-10  3:54 Cássio Gabriel
  2026-04-10  3:54 ` [PATCH 1/2] ALSA: sc6000: Keep the programmed board state in card-private data Cássio Gabriel
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Cássio Gabriel @ 2026-04-10  3:54 UTC (permalink / raw)
  To: Takashi Iwai, Jaroslav Kysela, Krzysztof Helt
  Cc: linux-sound, linux-kernel, Cássio Gabriel

The SC-6000 driver has to program board-level DSP routing before the
WSS codec can operate correctly. Today that setup has two related
problems.

First, the probe path may auto-select IRQ and DMA resources, but the
SC-6000 soft configuration is still derived from the raw module
parameter arrays. When irq=auto or dma=auto is used, the WSS codec can
be created with the selected live resources while the board itself is
programmed with unresolved values.

Second, the driver still lacks suspend/resume support. The WSS layer can
save and restore codec registers, but the SC-6000-specific DSP routing
and MSS initialization are done only during probe and are not replayed
after resume.

This series fixes the probe-time resource mismatch first and then reuses
that cached board state to restore the hardware on resume. Keeping the
board programming in a shared helper also keeps the old/new DSP split in
one place so probe and resume cannot drift apart.

- Patch 1 stores the computed SC-6000 board state in card-private data and
builds it from the live resources selected at probe time.
- Patch 2 wires ISA suspend/resume callbacks and restores the cached
board setup before the WSS codec resume path runs.

Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>
---
Cássio Gabriel (2):
      ALSA: sc6000: Keep the programmed board state in card-private data
      ALSA: sc6000: Restore board setup across suspend

 sound/isa/sc6000.c | 193 ++++++++++++++++++++++++++++++++++++-----------------
 1 file changed, 132 insertions(+), 61 deletions(-)
---
base-commit: 4c468a99786e2cf41bf7b2c95064472e99a6776f
change-id: 20260407-alsa-sc6000-pm-4fd109aaf04f

Best regards,
--  
Cássio Gabriel <cassiogabrielcontato@gmail.com>


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 1/2] ALSA: sc6000: Keep the programmed board state in card-private data
  2026-04-10  3:54 [PATCH 0/2] ALSA: sc6000: fix board configuration state and resume handling Cássio Gabriel
@ 2026-04-10  3:54 ` Cássio Gabriel
  2026-04-10  3:54 ` [PATCH 2/2] ALSA: sc6000: Restore board setup across suspend Cássio Gabriel
  2026-04-10  6:37 ` [PATCH 0/2] ALSA: sc6000: fix board configuration state and resume handling Takashi Iwai
  2 siblings, 0 replies; 4+ messages in thread
From: Cássio Gabriel @ 2026-04-10  3:54 UTC (permalink / raw)
  To: Takashi Iwai, Jaroslav Kysela, Krzysztof Helt
  Cc: linux-sound, linux-kernel, Cássio Gabriel

The driver may auto-select IRQ and DMA resources at probe time, but
sc6000_init_board() still derives the SC-6000 soft configuration from
the module parameter arrays.  When irq=auto or dma=auto is used, the
codec is created with the selected resources while the board is
programmed with the unresolved values.

Store the mapped ports and generated SC-6000 board configuration in
card-private data, build that configuration from the live probe
results instead of the raw module parameters, and keep the probe-time
board programming in a shared helper.

This fixes the resource-programming mismatch and leaves the driver
with a stable board-state block that can be reused by suspend/resume.

Fixes: c282866101bf ("ALSA: sc6000: add support for SC-6600 and SC-7000")
Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>
---
 sound/isa/sc6000.c | 152 ++++++++++++++++++++++++++++++++---------------------
 1 file changed, 92 insertions(+), 60 deletions(-)

diff --git a/sound/isa/sc6000.c b/sound/isa/sc6000.c
index 6d618cc2ba45..9949e06403f6 100644
--- a/sound/isa/sc6000.c
+++ b/sound/isa/sc6000.c
@@ -100,6 +100,15 @@ MODULE_PARM_DESC(joystick, "Enable gameport.");
 #define PFX "sc6000: "
 #define DRV_NAME "SC-6000"
 
+struct snd_sc6000 {
+	char __iomem *vport;
+	char __iomem *vmss_port;
+	u8 mss_config;
+	u8 config;
+	u8 hw_cfg[2];
+	bool old_dsp;
+};
+
 /* hardware dependent functions */
 
 /*
@@ -267,7 +276,7 @@ static int sc6000_dsp_reset(char __iomem *vport)
 
 /* detection and initialization */
 static int sc6000_hw_cfg_write(struct device *devptr,
-			       char __iomem *vport, const int *cfg)
+			       char __iomem *vport, const u8 *cfg)
 {
 	if (sc6000_write(devptr, vport, COMMAND_6C) < 0) {
 		dev_warn(devptr, "CMD 0x%x: failed!\n", COMMAND_6C);
@@ -353,8 +362,7 @@ static int sc6000_init_mss(struct device *devptr,
 	return 0;
 }
 
-static void sc6000_hw_cfg_encode(struct device *devptr,
-				 char __iomem *vport, int *cfg,
+static void sc6000_hw_cfg_encode(struct device *devptr, u8 *cfg,
 				 long xport, long xmpu,
 				 long xmss_port, int joystick)
 {
@@ -376,27 +384,83 @@ static void sc6000_hw_cfg_encode(struct device *devptr,
 	dev_dbg(devptr, "hw cfg %x, %x\n", cfg[0], cfg[1]);
 }
 
-static int sc6000_init_board(struct device *devptr,
-			     char __iomem *vport,
-			     char __iomem *vmss_port, int dev)
+static void sc6000_prepare_board(struct device *devptr,
+				 struct snd_sc6000 *sc6000,
+				 unsigned int dev, int xirq, int xdma)
+{
+	sc6000->mss_config = sc6000_irq_to_softcfg(xirq) |
+			     sc6000_dma_to_softcfg(xdma);
+	sc6000->config = sc6000->mss_config |
+			 sc6000_mpu_irq_to_softcfg(mpu_irq[dev]);
+	sc6000_hw_cfg_encode(devptr, sc6000->hw_cfg, port[dev], mpu_port[dev],
+			     mss_port[dev], joystick[dev]);
+}
+
+static void sc6000_detect_old_dsp(struct device *devptr,
+				  struct snd_sc6000 *sc6000)
+{
+	sc6000_write(devptr, sc6000->vport, COMMAND_5C);
+	sc6000->old_dsp = sc6000_read(sc6000->vport) < 0;
+}
+
+static int sc6000_program_board(struct device *devptr,
+				struct snd_sc6000 *sc6000)
+{
+	int err;
+
+	if (!sc6000->old_dsp) {
+		if (sc6000_hw_cfg_write(devptr, sc6000->vport,
+					sc6000->hw_cfg) < 0) {
+			dev_err(devptr, "sc6000_hw_cfg_write: failed!\n");
+			return -EIO;
+		}
+	}
+
+	err = sc6000_setup_board(devptr, sc6000->vport, sc6000->config);
+	if (err < 0) {
+		dev_err(devptr, "sc6000_setup_board: failed!\n");
+		return -ENODEV;
+	}
+
+	sc6000_dsp_reset(sc6000->vport);
+
+	if (!sc6000->old_dsp) {
+		sc6000_write(devptr, sc6000->vport, COMMAND_60);
+		sc6000_write(devptr, sc6000->vport, 0x02);
+		sc6000_dsp_reset(sc6000->vport);
+	}
+
+	err = sc6000_setup_board(devptr, sc6000->vport, sc6000->config);
+	if (err < 0) {
+		dev_err(devptr, "sc6000_setup_board: failed!\n");
+		return -ENODEV;
+	}
+
+	err = sc6000_init_mss(devptr, sc6000->vport, sc6000->config,
+			      sc6000->vmss_port, sc6000->mss_config);
+	if (err < 0) {
+		dev_err(devptr, "Cannot initialize Microsoft Sound System mode.\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int sc6000_init_board(struct device *devptr, struct snd_sc6000 *sc6000)
 {
 	char answer[15];
 	char version[2];
-	int mss_config = sc6000_irq_to_softcfg(irq[dev]) |
-			 sc6000_dma_to_softcfg(dma[dev]);
-	int config = mss_config |
-		     sc6000_mpu_irq_to_softcfg(mpu_irq[dev]);
 	int err;
-	int old = 0;
 
-	err = sc6000_dsp_reset(vport);
+	err = sc6000_dsp_reset(sc6000->vport);
 	if (err < 0) {
 		dev_err(devptr, "sc6000_dsp_reset: failed!\n");
 		return err;
 	}
 
 	memset(answer, 0, sizeof(answer));
-	err = sc6000_dsp_get_answer(devptr, vport, GET_DSP_COPYRIGHT, answer, 15);
+	err = sc6000_dsp_get_answer(devptr, sc6000->vport, GET_DSP_COPYRIGHT,
+				    answer, 15);
 	if (err <= 0) {
 		dev_err(devptr, "sc6000_dsp_copyright: failed!\n");
 		return -ENODEV;
@@ -408,54 +472,17 @@ static int sc6000_init_board(struct device *devptr,
 	if (strncmp("SC-6000", answer, 7))
 		dev_warn(devptr, "Warning: non SC-6000 audio card!\n");
 
-	if (sc6000_dsp_get_answer(devptr, vport, GET_DSP_VERSION, version, 2) < 2) {
+	if (sc6000_dsp_get_answer(devptr, sc6000->vport,
+				  GET_DSP_VERSION, version, 2) < 2) {
 		dev_err(devptr, "sc6000_dsp_version: failed!\n");
 		return -ENODEV;
 	}
 	dev_info(devptr, "Detected model: %s, DSP version %d.%d\n",
 		answer, version[0], version[1]);
 
-	/* set configuration */
-	sc6000_write(devptr, vport, COMMAND_5C);
-	if (sc6000_read(vport) < 0)
-		old = 1;
-
-	if (!old) {
-		int cfg[2];
-		sc6000_hw_cfg_encode(devptr,
-				     vport, &cfg[0], port[dev], mpu_port[dev],
-				     mss_port[dev], joystick[dev]);
-		if (sc6000_hw_cfg_write(devptr, vport, cfg) < 0) {
-			dev_err(devptr, "sc6000_hw_cfg_write: failed!\n");
-			return -EIO;
-		}
-	}
-	err = sc6000_setup_board(devptr, vport, config);
-	if (err < 0) {
-		dev_err(devptr, "sc6000_setup_board: failed!\n");
-		return -ENODEV;
-	}
-
-	sc6000_dsp_reset(vport);
-
-	if (!old) {
-		sc6000_write(devptr, vport, COMMAND_60);
-		sc6000_write(devptr, vport, 0x02);
-		sc6000_dsp_reset(vport);
-	}
+	sc6000_detect_old_dsp(devptr, sc6000);
 
-	err = sc6000_setup_board(devptr, vport, config);
-	if (err < 0) {
-		dev_err(devptr, "sc6000_setup_board: failed!\n");
-		return -ENODEV;
-	}
-	err = sc6000_init_mss(devptr, vport, config, vmss_port, mss_config);
-	if (err < 0) {
-		dev_err(devptr, "Cannot initialize Microsoft Sound System mode.\n");
-		return -ENODEV;
-	}
-
-	return 0;
+	return sc6000_program_board(devptr, sc6000);
 }
 
 static int snd_sc6000_mixer(struct snd_wss *chip)
@@ -538,10 +565,10 @@ static int snd_sc6000_match(struct device *devptr, unsigned int dev)
 
 static void snd_sc6000_free(struct snd_card *card)
 {
-	char __iomem *vport = (char __force __iomem *)card->private_data;
+	struct snd_sc6000 *sc6000 = card->private_data;
 
-	if (vport)
-		sc6000_setup_board(card->dev, vport, 0);
+	if (sc6000->vport)
+		sc6000_setup_board(card->dev, sc6000->vport, 0);
 }
 
 static int __snd_sc6000_probe(struct device *devptr, unsigned int dev)
@@ -552,15 +579,17 @@ static int __snd_sc6000_probe(struct device *devptr, unsigned int dev)
 	int xirq = irq[dev];
 	int xdma = dma[dev];
 	struct snd_card *card;
+	struct snd_sc6000 *sc6000;
 	struct snd_wss *chip;
 	struct snd_opl3 *opl3;
 	char __iomem *vport;
 	char __iomem *vmss_port;
 
 	err = snd_devm_card_new(devptr, index[dev], id[dev], THIS_MODULE,
-				0, &card);
+				sizeof(*sc6000), &card);
 	if (err < 0)
 		return err;
+	sc6000 = card->private_data;
 
 	if (xirq == SNDRV_AUTO_IRQ) {
 		xirq = snd_legacy_find_free_irq(possible_irqs);
@@ -587,7 +616,7 @@ static int __snd_sc6000_probe(struct device *devptr, unsigned int dev)
 		dev_err(devptr, "I/O port cannot be iomapped.\n");
 		return -EBUSY;
 	}
-	card->private_data = (void __force *)vport;
+	sc6000->vport = vport;
 
 	/* to make it marked as used */
 	if (!devm_request_region(devptr, mss_port[dev], 4, DRV_NAME)) {
@@ -600,12 +629,15 @@ static int __snd_sc6000_probe(struct device *devptr, unsigned int dev)
 		dev_err(devptr, "MSS port I/O cannot be iomapped.\n");
 		return -EBUSY;
 	}
+	sc6000->vmss_port = vmss_port;
 
 	dev_dbg(devptr, "Initializing BASE[0x%lx] IRQ[%d] DMA[%d] MIRQ[%d]\n",
 		port[dev], xirq, xdma,
 		mpu_irq[dev] == SNDRV_AUTO_IRQ ? 0 : mpu_irq[dev]);
 
-	err = sc6000_init_board(devptr, vport, vmss_port, dev);
+	sc6000_prepare_board(devptr, sc6000, dev, xirq, xdma);
+
+	err = sc6000_init_board(devptr, sc6000);
 	if (err < 0)
 		return err;
 	card->private_free = snd_sc6000_free;

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH 2/2] ALSA: sc6000: Restore board setup across suspend
  2026-04-10  3:54 [PATCH 0/2] ALSA: sc6000: fix board configuration state and resume handling Cássio Gabriel
  2026-04-10  3:54 ` [PATCH 1/2] ALSA: sc6000: Keep the programmed board state in card-private data Cássio Gabriel
@ 2026-04-10  3:54 ` Cássio Gabriel
  2026-04-10  6:37 ` [PATCH 0/2] ALSA: sc6000: fix board configuration state and resume handling Takashi Iwai
  2 siblings, 0 replies; 4+ messages in thread
From: Cássio Gabriel @ 2026-04-10  3:54 UTC (permalink / raw)
  To: Takashi Iwai, Jaroslav Kysela, Krzysztof Helt
  Cc: linux-sound, linux-kernel, Cássio Gabriel

snd_wss_resume() restores only the codec register image.  The SC-6000
driver also programs card-specific DSP routing and enters MSS mode
during probe, and that setup is not replayed after suspend.

Cache the WSS chip pointer in the SC-6000 card state and wire ISA
suspend and resume callbacks to the shared board-programming helper,
so the board is reinitialized before the codec state is restored.

This keeps the old/new DSP split in one place and restores the
board-level MSS setup that the codec resume path does not cover.

Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>
---
 sound/isa/sc6000.c | 41 ++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 40 insertions(+), 1 deletion(-)

diff --git a/sound/isa/sc6000.c b/sound/isa/sc6000.c
index 9949e06403f6..cd3a63c7c2a7 100644
--- a/sound/isa/sc6000.c
+++ b/sound/isa/sc6000.c
@@ -103,6 +103,7 @@ MODULE_PARM_DESC(joystick, "Enable gameport.");
 struct snd_sc6000 {
 	char __iomem *vport;
 	char __iomem *vmss_port;
+	struct snd_wss *chip;
 	u8 mss_config;
 	u8 config;
 	u8 hw_cfg[2];
@@ -646,6 +647,7 @@ static int __snd_sc6000_probe(struct device *devptr, unsigned int dev)
 			     WSS_HW_DETECT, 0, &chip);
 	if (err < 0)
 		return err;
+	sc6000->chip = chip;
 
 	err = snd_wss_pcm(chip, 0);
 	if (err < 0) {
@@ -702,10 +704,47 @@ static int snd_sc6000_probe(struct device *devptr, unsigned int dev)
 	return snd_card_free_on_error(devptr, __snd_sc6000_probe(devptr, dev));
 }
 
+#ifdef CONFIG_PM
+static int snd_sc6000_suspend(struct device *devptr, unsigned int dev,
+			      pm_message_t state)
+{
+	struct snd_card *card = dev_get_drvdata(devptr);
+	struct snd_sc6000 *sc6000 = card->private_data;
+
+	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+	sc6000->chip->suspend(sc6000->chip);
+	return 0;
+}
+
+static int snd_sc6000_resume(struct device *devptr, unsigned int dev)
+{
+	struct snd_card *card = dev_get_drvdata(devptr);
+	struct snd_sc6000 *sc6000 = card->private_data;
+	int err;
+
+	err = sc6000_dsp_reset(sc6000->vport);
+	if (err < 0) {
+		dev_err(devptr, "sc6000_dsp_reset: failed!\n");
+		return err;
+	}
+
+	err = sc6000_program_board(devptr, sc6000);
+	if (err < 0)
+		return err;
+
+	sc6000->chip->resume(sc6000->chip);
+	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+	return 0;
+}
+#endif
+
 static struct isa_driver snd_sc6000_driver = {
 	.match		= snd_sc6000_match,
 	.probe		= snd_sc6000_probe,
-	/* FIXME: suspend/resume */
+#ifdef CONFIG_PM
+	.suspend	= snd_sc6000_suspend,
+	.resume		= snd_sc6000_resume,
+#endif
 	.driver		= {
 		.name	= DRV_NAME,
 	},

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH 0/2] ALSA: sc6000: fix board configuration state and resume handling
  2026-04-10  3:54 [PATCH 0/2] ALSA: sc6000: fix board configuration state and resume handling Cássio Gabriel
  2026-04-10  3:54 ` [PATCH 1/2] ALSA: sc6000: Keep the programmed board state in card-private data Cássio Gabriel
  2026-04-10  3:54 ` [PATCH 2/2] ALSA: sc6000: Restore board setup across suspend Cássio Gabriel
@ 2026-04-10  6:37 ` Takashi Iwai
  2 siblings, 0 replies; 4+ messages in thread
From: Takashi Iwai @ 2026-04-10  6:37 UTC (permalink / raw)
  To: Cássio Gabriel
  Cc: Takashi Iwai, Jaroslav Kysela, Krzysztof Helt, linux-sound,
	linux-kernel

On Fri, 10 Apr 2026 05:54:31 +0200,
Cássio Gabriel wrote:
> 
> The SC-6000 driver has to program board-level DSP routing before the
> WSS codec can operate correctly. Today that setup has two related
> problems.
> 
> First, the probe path may auto-select IRQ and DMA resources, but the
> SC-6000 soft configuration is still derived from the raw module
> parameter arrays. When irq=auto or dma=auto is used, the WSS codec can
> be created with the selected live resources while the board itself is
> programmed with unresolved values.
> 
> Second, the driver still lacks suspend/resume support. The WSS layer can
> save and restore codec registers, but the SC-6000-specific DSP routing
> and MSS initialization are done only during probe and are not replayed
> after resume.
> 
> This series fixes the probe-time resource mismatch first and then reuses
> that cached board state to restore the hardware on resume. Keeping the
> board programming in a shared helper also keeps the old/new DSP split in
> one place so probe and resume cannot drift apart.
> 
> - Patch 1 stores the computed SC-6000 board state in card-private data and
> builds it from the live resources selected at probe time.
> - Patch 2 wires ISA suspend/resume callbacks and restores the cached
> board setup before the WSS codec resume path runs.
> 
> Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>
> ---
> Cássio Gabriel (2):
>       ALSA: sc6000: Keep the programmed board state in card-private data
>       ALSA: sc6000: Restore board setup across suspend

Applied both patches now.  Thanks.


Takashi

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-04-10  6:37 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-10  3:54 [PATCH 0/2] ALSA: sc6000: fix board configuration state and resume handling Cássio Gabriel
2026-04-10  3:54 ` [PATCH 1/2] ALSA: sc6000: Keep the programmed board state in card-private data Cássio Gabriel
2026-04-10  3:54 ` [PATCH 2/2] ALSA: sc6000: Restore board setup across suspend Cássio Gabriel
2026-04-10  6:37 ` [PATCH 0/2] ALSA: sc6000: fix board configuration state and resume handling Takashi Iwai

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox