ALSA: sscape: Cache per-card resources for board reinitialization
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> Link: https://patch.msgid.link/20260411-alsa-sscape-pm-v2-1-aeb5682e14b0@gmail.com Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
1 parent
86aa1ea1f1
commit
f312f8b598
1 file changed
+139
-95
+139
-95
@@ -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 @@ _error:
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
|
||||
Reference in new issue
Block a user