public inbox for linux-sound@vger.kernel.org
 help / color / mirror / Atom feed
From: "Cássio Gabriel" <cassiogabrielcontato@gmail.com>
To: Takashi Iwai <tiwai@suse.com>, Jaroslav Kysela <perex@perex.cz>
Cc: linux-sound@vger.kernel.org, linux-kernel@vger.kernel.org,
	"Cássio Gabriel" <cassiogabrielcontato@gmail.com>
Subject: [PATCH v2 1/2] ALSA: sscape: Cache per-card resources for board reinitialization
Date: Sat, 11 Apr 2026 15:14:40 -0300	[thread overview]
Message-ID: <20260411-alsa-sscape-pm-v2-1-aeb5682e14b0@gmail.com> (raw)
In-Reply-To: <20260411-alsa-sscape-pm-v2-0-aeb5682e14b0@gmail.com>

The SoundScape driver programs the gate-array directly from the global
resource arrays during probe. That is sufficient for initial bring-up,
but a PM resume path also needs the resolved per-card IRQ, DMA, MPU IRQ
and joystick settings after probe has finished.

Store the resolved resources in struct soundscape and move the board
setup into a reusable helper. Also factor the MIDI state programming so
the same sequence can be reused by a later PM resume path.

This is preparatory work for suspend/resume support and is not intended
to change runtime behaviour.

Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>
---
 sound/isa/sscape.c | 234 +++++++++++++++++++++++++++++++----------------------
 1 file changed, 139 insertions(+), 95 deletions(-)

diff --git a/sound/isa/sscape.c b/sound/isa/sscape.c
index a31ca75774a6..6e951b3c0080 100644
--- a/sound/isa/sscape.c
+++ b/sound/isa/sscape.c
@@ -131,6 +131,11 @@ enum card_type {
 struct soundscape {
 	spinlock_t lock;
 	unsigned io_base;
+	unsigned long wss_base;
+	int irq;
+	int mpu_irq;
+	int dma1;
+	int dma2;
 	int ic_type;
 	enum card_type type;
 	struct resource *io_res;
@@ -138,6 +143,7 @@ struct soundscape {
 	struct snd_wss *chip;
 
 	unsigned char midi_vol;
+	bool joystick;
 	struct device *dev;
 };
 
@@ -149,6 +155,21 @@ static inline struct soundscape *get_card_soundscape(struct snd_card *c)
 	return (struct soundscape *) (c->private_data);
 }
 
+/*
+ * Store the resolved board settings in the per-card state so that
+ * the same configuration can be replayed later if necessary.
+ */
+static void sscape_store_settings(struct soundscape *sscape, int dev)
+{
+	sscape->io_base = port[dev];
+	sscape->wss_base = wss_port[dev];
+	sscape->irq = irq[dev];
+	sscape->mpu_irq = mpu_irq[dev];
+	sscape->dma1 = dma[dev];
+	sscape->dma2 = dma2[dev];
+	sscape->joystick = joystick[dev];
+}
+
 /*
  * Allocates some kernel memory that we can use for DMA.
  * I think this means that the memory has to map to
@@ -263,34 +284,36 @@ static int host_read_ctrl_unsafe(unsigned io_base, unsigned timeout)
 
 /*
  * Write to the SoundScape's host-mode control registers, but
- * leave any locking issues to the caller ...
+ * leave any locking issues to the caller. Returns true if
+ * the write succeeded.
  */
-static inline int host_write_unsafe(unsigned io_base, unsigned char data)
+static inline bool host_write_unsafe(unsigned int io_base, unsigned char data)
 {
 	if ((inb(HOST_CTRL_IO(io_base)) & TX_READY) != 0) {
 		outb(data, HOST_DATA_IO(io_base));
-		return 1;
+		return true;
 	}
 
-	return 0;
+	return false;
 }
 
 /*
  * Write to the SoundScape's host-mode control registers, performing
  * a limited amount of busy-waiting if the register isn't ready.
- * Also leaves all locking-issues to the caller ...
+ * Also leaves all locking-issues to the caller. Returns true if
+ * the write succeeded before timing out.
  */
-static int host_write_ctrl_unsafe(unsigned io_base, unsigned char data,
-				  unsigned timeout)
+static bool host_write_ctrl_unsafe(unsigned int io_base, unsigned char data,
+				   unsigned int timeout)
 {
-	int err;
+	bool written;
 
-	while (!(err = host_write_unsafe(io_base, data)) && (timeout != 0)) {
+	while (!(written = host_write_unsafe(io_base, data)) && timeout != 0) {
 		udelay(100);
 		--timeout;
 	} /* while */
 
-	return err;
+	return written;
 }
 
 
@@ -560,6 +583,30 @@ static int sscape_upload_microcode(struct snd_card *card, int version)
 	return err;
 }
 
+/*
+ * Restore the SoundScape's MIDI control state after the firmware
+ * upload has made the host interface available again.
+ */
+static int sscape_restore_midi_state(struct soundscape *sscape)
+{
+	bool success;
+
+	guard(spinlock_irqsave)(&sscape->lock);
+	set_host_mode_unsafe(sscape->io_base);
+
+	success = host_write_ctrl_unsafe(sscape->io_base, CMD_SET_MIDI_VOL, 100) &&
+		  host_write_ctrl_unsafe(sscape->io_base, sscape->midi_vol, 100) &&
+		  host_write_ctrl_unsafe(sscape->io_base, CMD_XXX_MIDI_VOL, 100) &&
+		  host_write_ctrl_unsafe(sscape->io_base, sscape->midi_vol, 100) &&
+		  host_write_ctrl_unsafe(sscape->io_base, CMD_SET_EXTMIDI, 100) &&
+		  host_write_ctrl_unsafe(sscape->io_base, 0, 100) &&
+		  host_write_ctrl_unsafe(sscape->io_base, CMD_ACK, 100);
+
+	set_midi_mode_unsafe(sscape->io_base);
+
+	return success ? 0 : -EIO;
+}
+
 /*
  * Mixer control for the SoundScape's MIDI device.
  */
@@ -660,6 +707,59 @@ static unsigned get_irq_config(int sscape_type, int irq)
 	return INVALID_IRQ;
 }
 
+/*
+ * Program the SoundScape's board-specific routing and enable the
+ * codec path using the resolved IRQ, DMA and joystick settings.
+ */
+static int sscape_configure_board(struct soundscape *sscape)
+{
+	unsigned int dma_cfg;
+	unsigned int irq_cfg;
+	unsigned int mpu_irq_cfg;
+	int val;
+
+	irq_cfg = get_irq_config(sscape->type, sscape->irq);
+	if (irq_cfg == INVALID_IRQ)
+		return -ENXIO;
+
+	mpu_irq_cfg = get_irq_config(sscape->type, sscape->mpu_irq);
+	if (mpu_irq_cfg == INVALID_IRQ)
+		return -ENXIO;
+
+	scoped_guard(spinlock_irqsave, &sscape->lock) {
+		if (sscape->ic_type == IC_OPUS)
+			activate_ad1845_unsafe(sscape->io_base);
+
+		sscape_write_unsafe(sscape->io_base, GA_SMCFGA_REG, 0x2e);
+		sscape_write_unsafe(sscape->io_base, GA_SMCFGB_REG, 0x00);
+
+		/*
+		 * Enable and configure the DMA channels ...
+		 */
+		sscape_write_unsafe(sscape->io_base, GA_DMACFG_REG, 0x50);
+		dma_cfg = (sscape->ic_type == IC_OPUS ? 0x40 : 0x70);
+		sscape_write_unsafe(sscape->io_base, GA_DMAA_REG, dma_cfg);
+		sscape_write_unsafe(sscape->io_base, GA_DMAB_REG, 0x20);
+
+		mpu_irq_cfg |= mpu_irq_cfg << 2;
+		val = sscape_read_unsafe(sscape->io_base, GA_HMCTL_REG) & 0xf7;
+		if (sscape->joystick)
+			val |= 0x08;
+		sscape_write_unsafe(sscape->io_base, GA_HMCTL_REG, val | 0xd0);
+		sscape_write_unsafe(sscape->io_base, GA_INTCFG_REG,
+				    0xf0 | mpu_irq_cfg);
+		sscape_write_unsafe(sscape->io_base, GA_CDCFG_REG,
+				    0x09 | DMA_8BIT |
+				    (sscape->dma1 << 4) | (irq_cfg << 1));
+		/*
+		 * Enable the master IRQ ...
+		 */
+		sscape_write_unsafe(sscape->io_base, GA_INTENA_REG, 0x80);
+	}
+
+	return 0;
+}
+
 /*
  * Perform certain arcane port-checks to see whether there
  * is a SoundScape board lurking behind the given ports.
@@ -890,37 +990,33 @@ static int create_ad1845(struct snd_card *card, unsigned port,
 
 /*
  * Create an ALSA soundcard entry for the SoundScape, using
- * the given list of port, IRQ and DMA resources.
+ * the resolved port, IRQ and DMA resources.
  */
-static int create_sscape(int dev, struct snd_card *card)
+static int create_sscape(struct snd_card *card)
 {
 	struct soundscape *sscape = get_card_soundscape(card);
-	unsigned dma_cfg;
-	unsigned irq_cfg;
-	unsigned mpu_irq_cfg;
 	struct resource *io_res;
 	struct resource *wss_res;
 	int err;
-	int val;
 	const char *name;
 
 	/*
 	 * Grab IO ports that we will need to probe so that we
 	 * can detect and control this hardware ...
 	 */
-	io_res = devm_request_region(card->dev, port[dev], 8, "SoundScape");
+	io_res = devm_request_region(card->dev, sscape->io_base, 8, "SoundScape");
 	if (!io_res) {
 		dev_err(card->dev,
-			"sscape: can't grab port 0x%lx\n", port[dev]);
+			"sscape: can't grab port 0x%x\n", sscape->io_base);
 		return -EBUSY;
 	}
 	wss_res = NULL;
 	if (sscape->type == SSCAPE_VIVO) {
-		wss_res = devm_request_region(card->dev, wss_port[dev], 4,
+		wss_res = devm_request_region(card->dev, sscape->wss_base, 4,
 					      "SoundScape");
 		if (!wss_res) {
 			dev_err(card->dev, "sscape: can't grab port 0x%lx\n",
-				wss_port[dev]);
+				sscape->wss_base);
 			return -EBUSY;
 		}
 	}
@@ -928,18 +1024,17 @@ static int create_sscape(int dev, struct snd_card *card)
 	/*
 	 * Grab one DMA channel ...
 	 */
-	err = snd_devm_request_dma(card->dev, dma[dev], "SoundScape");
+	err = snd_devm_request_dma(card->dev, sscape->dma1, "SoundScape");
 	if (err < 0) {
-		dev_err(card->dev, "sscape: can't grab DMA %d\n", dma[dev]);
+		dev_err(card->dev, "sscape: can't grab DMA %d\n", sscape->dma1);
 		return err;
 	}
 
 	spin_lock_init(&sscape->lock);
 	sscape->io_res = io_res;
 	sscape->wss_res = wss_res;
-	sscape->io_base = port[dev];
 
-	if (!detect_sscape(sscape, wss_port[dev])) {
+	if (!detect_sscape(sscape, sscape->wss_base)) {
 		dev_err(card->dev, "sscape: hardware not detected at 0x%x\n",
 			sscape->io_base);
 		return -ENODEV;
@@ -964,66 +1059,28 @@ static int create_sscape(int dev, struct snd_card *card)
 	}
 
 	dev_info(card->dev, "sscape: %s card detected at 0x%x, using IRQ %d, DMA %d\n",
-		 name, sscape->io_base, irq[dev], dma[dev]);
-
-	/*
-	 * Check that the user didn't pass us garbage data ...
-	 */
-	irq_cfg = get_irq_config(sscape->type, irq[dev]);
-	if (irq_cfg == INVALID_IRQ) {
-		dev_err(card->dev, "sscape: Invalid IRQ %d\n", irq[dev]);
-		return -ENXIO;
-	}
-
-	mpu_irq_cfg = get_irq_config(sscape->type, mpu_irq[dev]);
-	if (mpu_irq_cfg == INVALID_IRQ) {
-		dev_err(card->dev, "sscape: Invalid IRQ %d\n", mpu_irq[dev]);
-		return -ENXIO;
-	}
+		 name, sscape->io_base, sscape->irq, sscape->dma1);
 
 	/*
 	 * Tell the on-board devices where their resources are (I think -
 	 * I can't be sure without a datasheet ... So many magic values!)
 	 */
-	scoped_guard(spinlock_irqsave, &sscape->lock) {
-
-		sscape_write_unsafe(sscape->io_base, GA_SMCFGA_REG, 0x2e);
-		sscape_write_unsafe(sscape->io_base, GA_SMCFGB_REG, 0x00);
-
-		/*
-		 * Enable and configure the DMA channels ...
-		 */
-		sscape_write_unsafe(sscape->io_base, GA_DMACFG_REG, 0x50);
-		dma_cfg = (sscape->ic_type == IC_OPUS ? 0x40 : 0x70);
-		sscape_write_unsafe(sscape->io_base, GA_DMAA_REG, dma_cfg);
-		sscape_write_unsafe(sscape->io_base, GA_DMAB_REG, 0x20);
-
-		mpu_irq_cfg |= mpu_irq_cfg << 2;
-		val = sscape_read_unsafe(sscape->io_base, GA_HMCTL_REG) & 0xF7;
-		if (joystick[dev])
-			val |= 8;
-		sscape_write_unsafe(sscape->io_base, GA_HMCTL_REG, val | 0x10);
-		sscape_write_unsafe(sscape->io_base, GA_INTCFG_REG, 0xf0 | mpu_irq_cfg);
-		sscape_write_unsafe(sscape->io_base,
-				    GA_CDCFG_REG, 0x09 | DMA_8BIT
-				    | (dma[dev] << 4) | (irq_cfg << 1));
-		/*
-		 * Enable the master IRQ ...
-		 */
-		sscape_write_unsafe(sscape->io_base, GA_INTENA_REG, 0x80);
-
+	err = sscape_configure_board(sscape);
+	if (err < 0) {
+		dev_err(card->dev, "sscape: Invalid IRQ configuration\n");
+		return err;
 	}
 
 	/*
 	 * We have now enabled the codec chip, and so we should
 	 * detect the AD1845 device ...
 	 */
-	err = create_ad1845(card, wss_port[dev], irq[dev],
-			    dma[dev], dma2[dev]);
+	err = create_ad1845(card, sscape->wss_base, sscape->irq,
+			    sscape->dma1, sscape->dma2);
 	if (err < 0) {
 		dev_err(card->dev,
 			"sscape: No AD1845 device at 0x%lx, IRQ %d\n",
-			wss_port[dev], irq[dev]);
+			sscape->wss_base, sscape->irq);
 		return err;
 	}
 	strscpy(card->driver, "SoundScape");
@@ -1040,35 +1097,21 @@ static int create_sscape(int dev, struct snd_card *card)
 			err = sscape_upload_microcode(card, err);
 
 		if (err == 0) {
-			err = create_mpu401(card, MIDI_DEVNUM, port[dev],
-					    mpu_irq[dev]);
+			err = create_mpu401(card, MIDI_DEVNUM, sscape->io_base,
+					    sscape->mpu_irq);
 			if (err < 0) {
 				dev_err(card->dev,
 					"sscape: Failed to create MPU-401 device at 0x%lx\n",
-					port[dev]);
+					(unsigned long)sscape->io_base);
 				return err;
 			}
 
-			/*
-			 * Initialize mixer
-			 */
-			guard(spinlock_irqsave)(&sscape->lock);
 			sscape->midi_vol = 0;
-			host_write_ctrl_unsafe(sscape->io_base,
-						CMD_SET_MIDI_VOL, 100);
-			host_write_ctrl_unsafe(sscape->io_base,
-						sscape->midi_vol, 100);
-			host_write_ctrl_unsafe(sscape->io_base,
-						CMD_XXX_MIDI_VOL, 100);
-			host_write_ctrl_unsafe(sscape->io_base,
-						sscape->midi_vol, 100);
-			host_write_ctrl_unsafe(sscape->io_base,
-						CMD_SET_EXTMIDI, 100);
-			host_write_ctrl_unsafe(sscape->io_base,
-						0, 100);
-			host_write_ctrl_unsafe(sscape->io_base, CMD_ACK, 100);
-
-			set_midi_mode_unsafe(sscape->io_base);
+			err = sscape_restore_midi_state(sscape);
+			if (err < 0)
+				dev_warn(card->dev,
+					 "sscape: MIDI init incomplete: %d\n",
+					 err);
 		}
 	}
 
@@ -1111,8 +1154,9 @@ static int snd_sscape_probe(struct device *pdev, unsigned int dev)
 	sscape->type = SSCAPE;
 
 	dma[dev] &= 0x03;
+	sscape_store_settings(sscape, dev);
 
-	ret = create_sscape(dev, card);
+	ret = create_sscape(card);
 	if (ret < 0)
 		return ret;
 
@@ -1130,7 +1174,6 @@ static int snd_sscape_probe(struct device *pdev, unsigned int dev)
 static struct isa_driver snd_sscape_driver = {
 	.match		= snd_sscape_match,
 	.probe		= snd_sscape_probe,
-	/* FIXME: suspend/resume */
 	.driver		= {
 		.name	= DEV_NAME
 	},
@@ -1211,8 +1254,9 @@ static int sscape_pnp_detect(struct pnp_card_link *pcard,
 		wss_port[idx] = pnp_port_start(dev, 1);
 		dma2[idx] = pnp_dma(dev, 1);
 	}
+	sscape_store_settings(sscape, idx);
 
-	ret = create_sscape(idx, card);
+	ret = create_sscape(card);
 	if (ret < 0)
 		return ret;
 

-- 
2.53.0


  reply	other threads:[~2026-04-11 18:14 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-11 18:14 [PATCH v2 0/2] ALSA: sscape: add suspend/resume support Cássio Gabriel
2026-04-11 18:14 ` Cássio Gabriel [this message]
2026-04-11 18:14 ` [PATCH v2 2/2] ALSA: sscape: Add suspend and resume support Cássio Gabriel
2026-04-12  8:01 ` [PATCH v2 0/2] ALSA: sscape: add suspend/resume support Takashi Iwai

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260411-alsa-sscape-pm-v2-1-aeb5682e14b0@gmail.com \
    --to=cassiogabrielcontato@gmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-sound@vger.kernel.org \
    --cc=perex@perex.cz \
    --cc=tiwai@suse.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox