[PATCH 1/2] ALSA: sscape: Cache per-card resources for board reinitialization

Cássio Gabriel
2026-04-11T04:54:41Z

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 | 210 +++++++++++++++++++++++++++++++----------------------
1 file changed, 125 insertions(+), 85 deletions(-)

diff --git a/sound/isa/sscape.c b/sound/isa/sscape.c
index a31ca75774a6..3e9465bc64ed 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
@@ -560,6 +581,28 @@ 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)
+{
+ int err = 1;
+
+ guard(spinlock_irqsave)(&sscape->lock);
+ set_host_mode_unsafe(sscape->io_base);
+ err &= host_write_ctrl_unsafe(sscape->io_base, CMD_SET_MIDI_VOL, 100);
+ err &= host_write_ctrl_unsafe(sscape->io_base, sscape->midi_vol, 100);
+ err &= host_write_ctrl_unsafe(sscape->io_base, CMD_XXX_MIDI_VOL, 100);
+ err &= host_write_ctrl_unsafe(sscape->io_base, sscape->midi_vol, 100);
+ err &= host_write_ctrl_unsafe(sscape->io_base, CMD_SET_EXTMIDI, 100);
+ err &= host_write_ctrl_unsafe(sscape->io_base, 0, 100);
+ err &= host_write_ctrl_unsafe(sscape->io_base, CMD_ACK, 100);
+ set_midi_mode_unsafe(sscape->io_base);
+
+ return err ? 0 : -EIO;
+}
+
/*
* Mixer control for the SoundScape's MIDI device.
*/
@@ -660,6 +703,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 +986,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 +1020,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 +1055,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 +1093,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 +1150,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 +1170,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 +1250,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