// SPDX-License-Identifier: GPL-2.0-only /* * Driver for SiS7019 Audio Accelerator * * Copyright (C) 2004-2007, David Dillow * Written by David Dillow <dave@thedillows.org> * Inspired by the Trident 4D-WaveDX/NX driver. * * All rights reserved.
*/
staticint index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */ staticchar *id = SNDRV_DEFAULT_STR1; /* ID for this card */ staticbool enable = 1; staticint codecs = 1;
module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for SiS7019 Audio Accelerator.");
module_param(id, charp, 0444);
MODULE_PARM_DESC(id, "ID string for SiS7019 Audio Accelerator.");
module_param(enable, bool, 0444);
MODULE_PARM_DESC(enable, "Enable SiS7019 Audio Accelerator.");
module_param(codecs, int, 0444);
MODULE_PARM_DESC(codecs, "Set bit to indicate that codec number is expected to be present (default 1)");
/* There are three timing modes for the voices. * * For both playback and capture, when the buffer is one or two periods long, * we use the hardware's built-in Mid-Loop Interrupt and End-Loop Interrupt * to let us know when the periods have ended. * * When performing playback with more than two periods per buffer, we set * the "Stop Sample Offset" and tell the hardware to interrupt us when we * reach it. We then update the offset and continue on until we are * interrupted for the next period. * * Capture channels do not have a SSO, so we allocate a playback channel to * use as a timer for the capture periods. We use the SSO on the playback * channel to clock out virtual periods, and adjust the virtual period length * to maintain synchronization. This algorithm came from the Trident driver. * * FIXME: It'd be nice to make use of some of the synth features in the * hardware, but a woeful lack of documentation is a significant roadblock.
*/ struct voice {
u16 flags; #define VOICE_IN_USE 1 #define VOICE_CAPTURE 2 #define VOICE_SSO_TIMING 4 #define VOICE_SYNC_TIMING 8
u16 sync_cso;
u16 period_size;
u16 buffer_size;
u16 sync_period_size;
u16 sync_buffer_size;
u32 sso;
u32 vperiod; struct snd_pcm_substream *substream; struct voice *timing; void __iomem *ctrl_base; void __iomem *wave_base; void __iomem *sync_base; int num;
};
/* We need four pages to store our wave parameters during a suspend. If * we're not doing power management, we still need to allocate a page * for the silence buffer.
*/ #define SIS_SUSPEND_PAGES 4
struct sis7019 { unsignedlong ioport; void __iomem *ioaddr; int irq; int codecs_present;
/* Protect against more than one thread hitting the AC97 * registers (in a more polite manner than pounding the hardware * semaphore)
*/ struct mutex ac97_mutex;
/* voice_lock protects allocation/freeing of the voice descriptions
*/
spinlock_t voice_lock;
/* Allocate pages to store the internal wave state during * suspends. When we're operating, this can be used as a silence * buffer for a timing channel.
*/ void *suspend_state[SIS_SUSPEND_PAGES];
int silence_users;
dma_addr_t silence_dma_addr;
};
/* These values are also used by the module param 'codecs' to indicate * which codecs should be present.
*/ #define SIS_PRIMARY_CODEC_PRESENT 0x0001 #define SIS_SECONDARY_CODEC_PRESENT 0x0002 #define SIS_TERTIARY_CODEC_PRESENT 0x0004
/* The HW offset parameters (Loop End, Stop Sample, End Sample) have a * documented range of 8-0xfff8 samples. Given that they are 0-based, * that places our period/buffer range at 9-0xfff9 samples. That makes the * max buffer size 0xfff9 samples * 2 channels * 2 bytes per sample, and * max samples / min samples gives us the max periods in a buffer. * * We'll add a constraint upon open that limits the period and buffer sample * size to values that are legal for the hardware.
*/ staticconststruct snd_pcm_hardware sis_playback_hw_info = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_SYNC_START |
SNDRV_PCM_INFO_RESUME),
.formats = (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE),
.rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_CONTINUOUS,
.rate_min = 4000,
.rate_max = 48000,
.channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = (0xfff9 * 4),
.period_bytes_min = 9,
.period_bytes_max = (0xfff9 * 4),
.periods_min = 1,
.periods_max = (0xfff9 / 9),
};
voice->sso += period; if (voice->sso >= voice->buffer_size)
voice->sso -= voice->buffer_size;
/* Enforce the documented hardware minimum offset */ if (voice->sso < 8)
voice->sso = 8;
/* The SSO is in the upper 16 bits of the register. */
writew(voice->sso & 0xffff, base + SIS_PLAY_DMA_SSO_ESO + 2);
}
staticvoid sis_update_voice(struct voice *voice)
{ if (voice->flags & VOICE_SSO_TIMING) {
sis_update_sso(voice, voice->period_size);
} elseif (voice->flags & VOICE_SYNC_TIMING) { int sync;
/* If we've not hit the end of the virtual period, update * our records and keep going.
*/ if (voice->vperiod > voice->period_size) {
voice->vperiod -= voice->period_size; if (voice->vperiod < voice->period_size)
sis_update_sso(voice, voice->vperiod); else
sis_update_sso(voice, voice->period_size); return;
}
/* Calculate our relative offset between the target and * the actual CSO value. Since we're operating in a loop, * if the value is more than half way around, we can * consider ourselves wrapped.
*/
sync = voice->sync_cso;
sync -= readw(voice->sync_base + SIS_CAPTURE_DMA_FORMAT_CSO); if (sync > (voice->sync_buffer_size / 2))
sync -= voice->sync_buffer_size;
/* If sync is positive, then we interrupted too early, and * we'll need to come back in a few samples and try again. * There's a minimum wait, as it takes some time for the DMA * engine to startup, etc...
*/ if (sync > 0) { if (sync < 16)
sync = 16;
sis_update_sso(voice, sync); return;
}
/* Ok, we interrupted right on time, or (hopefully) just * a bit late. We'll adjst our next waiting period based * on how close we got. * * We need to stay just behind the actual channel to ensure * it really is past a period when we get our interrupt -- * otherwise we'll fall into the early code above and have * a minimum wait time, which makes us quite late here, * eating into the user's time to refresh the buffer, esp. * if using small periods. * * If we're less than 9 samples behind, we're on target. * Otherwise, shorten the next vperiod by the amount we've * been delayed.
*/ if (sync > -9)
voice->vperiod = voice->sync_period_size + 1; else
voice->vperiod = voice->sync_period_size + sync + 10;
/* We only use the DMA interrupts, and we don't enable any other * source of interrupts. But, it is possible to see an interrupt * status that didn't actually interrupt us, so eliminate anything * we're not expecting to avoid falsely claiming an IRQ, and an * ensuing endless loop.
*/
intr = inl(io + SIS_GISR);
intr &= SIS_GISR_AUDIO_PLAY_DMA_IRQ_STATUS |
SIS_GISR_AUDIO_RECORD_DMA_IRQ_STATUS; if (!intr) return IRQ_NONE;
do {
status = inl(io + SIS_PISR_A); if (status) {
sis_voice_irq(status, sis->voices);
outl(status, io + SIS_PISR_A);
}
status = inl(io + SIS_PISR_B); if (status) {
sis_voice_irq(status, &sis->voices[32]);
outl(status, io + SIS_PISR_B);
}
status = inl(io + SIS_RISR); if (status) {
voice = &sis->capture_voice; if (!voice->timing)
snd_pcm_period_elapsed(voice->substream);
/* This was copied from the trident driver, but it seems its gotten * around a bit... nevertheless, it works well. * * We special case 44100 and 8000 since rounding with the equation * does not give us an accurate enough value. For 11025 and 22050 * the equation gives us the best answer. All other frequencies will * also use the equation. JDW
*/ if (rate == 44100)
delta = 0xeb3; elseif (rate == 8000)
delta = 0x2ab; elseif (rate == 48000)
delta = 0x1000; else
delta = DIV_ROUND_CLOSEST(rate << 12, 48000) & 0x0000ffff; return delta;
}
staticvoid __sis_map_silence(struct sis7019 *sis)
{ /* Helper function: must hold sis->voice_lock on entry */ if (!sis->silence_users)
sis->silence_dma_addr = dma_map_single(&sis->pci->dev,
sis->suspend_state[0],
4096, DMA_TO_DEVICE);
sis->silence_users++;
}
staticvoid __sis_unmap_silence(struct sis7019 *sis)
{ /* Helper function: must hold sis->voice_lock on entry */
sis->silence_users--; if (!sis->silence_users)
dma_unmap_single(&sis->pci->dev, sis->silence_dma_addr, 4096,
DMA_TO_DEVICE);
}
/* If there are one or two periods per buffer, we don't need a * timing voice, as we can use the capture channel's interrupts * to clock out the periods.
*/
period_size = params_period_size(hw_params);
buffer_size = params_buffer_size(hw_params);
needed = (period_size != buffer_size &&
period_size != (buffer_size / 2));
/* We rely on the PCM core to ensure that the parameters for this * substream do not change on us while we're programming the HW.
*/
format = 0; if (snd_pcm_format_width(runtime->format) == 8)
format |= SIS_PLAY_DMA_FORMAT_8BIT; if (!snd_pcm_format_signed(runtime->format))
format |= SIS_PLAY_DMA_FORMAT_UNSIGNED; if (runtime->channels == 1)
format |= SIS_PLAY_DMA_FORMAT_MONO;
/* The baseline setup is for a single period per buffer, and * we add bells and whistles as needed from there.
*/
dma_addr = runtime->dma_addr;
leo = runtime->buffer_size - 1;
control = leo | SIS_PLAY_DMA_LOOP | SIS_PLAY_DMA_INTR_AT_LEO;
sso_eso = leo;
/* No locks needed, as the PCM core will hold the locks on the * substreams, and the HW will only start/stop the indicated voices * without changing the state of the others.
*/ switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_RESUME:
starting = 1; break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND:
starting = 0; break; default: return -EINVAL;
}
snd_pcm_group_for_each_entry(s, substream) { /* Make sure it is for us... */
chip = snd_pcm_substream_chip(s); if (chip != sis) continue;
voice = s->runtime->private_data; if (voice->flags & VOICE_CAPTURE) {
record |= 1 << voice->num;
voice = voice->timing;
}
/* voice could be NULL if this a recording stream, and it * doesn't have an external timing channel.
*/ if (voice)
play[voice->num / 32] |= 1 << (voice->num & 0x1f);
snd_pcm_trigger_done(s, substream);
}
if (starting) { if (record)
outl(record, io + SIS_RECORD_START_REG); if (play[0])
outl(play[0], io + SIS_PLAY_START_A_REG); if (play[1])
outl(play[1], io + SIS_PLAY_START_B_REG);
} else { if (record)
outl(record, io + SIS_RECORD_STOP_REG); if (play[0])
outl(play[0], io + SIS_PLAY_STOP_A_REG); if (play[1])
outl(play[1], io + SIS_PLAY_STOP_B_REG);
} return 0;
}
/* FIXME: The driver only supports recording from one channel * at the moment, but it could support more.
*/
spin_lock_irqsave(&sis->voice_lock, flags); if (voice->flags & VOICE_IN_USE)
voice = NULL; else
voice->flags |= VOICE_IN_USE;
spin_unlock_irqrestore(&sis->voice_lock, flags);
/* Set our initial buffer and period as large as we can given a * single page of silence.
*/
buffer_size = 4096 / runtime->channels;
buffer_size /= snd_pcm_format_size(runtime->format, 1);
period_size = buffer_size;
/* Initially, we want to interrupt just a bit behind the end of * the period we're clocking out. 12 samples seems to give a good * delay. * * We want to spread our interrupts throughout the virtual period, * so that we don't end up with two interrupts back to back at the * end -- this helps minimize the effects of any jitter. Adjust our * clocking period size so that the last period is at least a fourth * of a full period. * * This is all moot if we don't need to use virtual periods.
*/
vperiod = runtime->period_size + 12; if (vperiod > period_size) {
u16 tail = vperiod % period_size;
u16 quarter_period = period_size / 4;
sso = period_size - 1;
} else { /* The initial period will fit inside the buffer, so we * don't need to use virtual periods -- disable them.
*/
period_size = runtime->period_size;
sso = vperiod - 1;
vperiod = 0;
}
/* Using unsigned samples with the all-zero silence buffer * forces the output to the lower rail, killing playback. * So ignore unsigned vs signed -- it doesn't change the timing.
*/
format = 0; if (snd_pcm_format_width(runtime->format) == 8)
format = SIS_CAPTURE_DMA_FORMAT_8BIT; if (runtime->channels == 1)
format |= SIS_CAPTURE_DMA_FORMAT_MONO;
control = timing->buffer_size - 1;
control |= SIS_PLAY_DMA_LOOP | SIS_PLAY_DMA_INTR_AT_SSO;
sso_eso = timing->buffer_size - 1;
sso_eso |= timing->sso << 16;
delta = sis_rate_to_delta(runtime->rate);
/* We've done the math, now configure the channel.
*/
writel(format, play_base + SIS_PLAY_DMA_FORMAT_CSO);
writel(sis->silence_dma_addr, play_base + SIS_PLAY_DMA_BASE);
writel(control, play_base + SIS_PLAY_DMA_CONTROL);
writel(sso_eso, play_base + SIS_PLAY_DMA_SSO_ESO);
/* We rely on the PCM core to ensure that the parameters for this * substream do not change on us while we're programming the HW.
*/
format = 0; if (snd_pcm_format_width(runtime->format) == 8)
format = SIS_CAPTURE_DMA_FORMAT_8BIT; if (!snd_pcm_format_signed(runtime->format))
format |= SIS_CAPTURE_DMA_FORMAT_UNSIGNED; if (runtime->channels == 1)
format |= SIS_CAPTURE_DMA_FORMAT_MONO;
dma_addr = runtime->dma_addr;
leo = runtime->buffer_size - 1;
control = leo | SIS_CAPTURE_DMA_LOOP;
/* If we've got more than two periods per buffer, then we have * use a timing voice to clock out the periods. Otherwise, we can * use the capture channel's interrupts.
*/ if (voice->timing) {
sis_prepare_timing_voice(voice, substream);
} else {
control |= SIS_CAPTURE_DMA_INTR_AT_LEO; if (runtime->period_size != runtime->buffer_size)
control |= SIS_CAPTURE_DMA_INTR_AT_MLP;
}
staticint sis_pcm_create(struct sis7019 *sis)
{ struct snd_pcm *pcm; int rc;
/* We have 64 voices, and the driver currently records from * only one channel, though that could change in the future.
*/
rc = snd_pcm_new(sis->card, "SiS7019", 0, 64, 1, &pcm); if (rc) return rc;
/* Try to preallocate some memory, but it's not the end of the * world if this fails.
*/
snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
&sis->pci->dev, 64*1024, 128*1024);
/* ... and wait for any outstanding commands to complete ...
*/
count = 0xffff; do {
status = inw(io + SIS_AC97_STATUS); if ((status & rdy) && !(status & SIS_AC97_STATUS_BUSY)) break;
udelay(1);
} while (--count);
if (!count) goto timeout_sema;
/* ... before sending our command and waiting for it to finish ...
*/
outl(cmd, io + SIS_AC97_CMD);
udelay(10);
/* Command complete, we can let go of the semaphore now.
*/
outl(SIS_AC97_SEMA_RELEASE, io + SIS_AC97_SEMA); if (!count) return -EIO;
/* Now that we've finished the reset, find out what's attached. * There are some codec/board combinations that take an extremely * long time to come up. 350+ ms has been observed in the field, * so we'll give them up to 500ms.
*/
sis->codecs_present = 0;
timeout = msecs_to_jiffies(500) + jiffies; while (time_before_eq(jiffies, timeout)) {
status = inl(io + SIS_AC97_STATUS); if (status & SIS_AC97_STATUS_CODEC_READY)
sis->codecs_present |= SIS_PRIMARY_CODEC_PRESENT; if (status & SIS_AC97_STATUS_CODEC2_READY)
sis->codecs_present |= SIS_SECONDARY_CODEC_PRESENT; if (status & SIS_AC97_STATUS_CODEC3_READY)
sis->codecs_present |= SIS_TERTIARY_CODEC_PRESENT;
if (sis->codecs_present == codecs) break;
msleep(1);
}
/* All done, check for errors.
*/ if (!sis->codecs_present) {
dev_err(&sis->pci->dev, "could not find any codecs\n"); return -EIO;
}
if (sis->codecs_present != codecs) {
dev_warn(&sis->pci->dev, "missing codecs, found %0x, expected %0x\n",
sis->codecs_present, codecs);
}
/* Let the hardware know that the audio driver is alive, * and enable PCM slots on the AC-link for L/R playback (3 & 4) and * record channels. We're going to want to use Variable Rate Audio * for recording, to avoid needlessly resampling from 48kHZ.
*/
outl(SIS_AC97_CONF_AUDIO_ALIVE, io + SIS_AC97_CONF);
outl(SIS_AC97_CONF_AUDIO_ALIVE | SIS_AC97_CONF_PCM_LR_ENABLE |
SIS_AC97_CONF_PCM_CAP_MIC_ENABLE |
SIS_AC97_CONF_PCM_CAP_LR_ENABLE |
SIS_AC97_CONF_CODEC_VRA_ENABLE, io + SIS_AC97_CONF);
/* All AC97 PCM slots should be sourced from sub-mixer 0.
*/
outl(0, io + SIS_AC97_PSR);
/* There is only one valid DMA setup for a PCI environment.
*/
outl(SIS_DMA_CSR_PCI_SETTINGS, io + SIS_DMA_CSR);
/* Reset the synchronization groups for all of the channels * to be asynchronous. If we start doing SPDIF or 5.1 sound, etc. * we'll need to change how we handle these. Until then, we just * assign sub-mixer 0 to all playback channels, and avoid any * attenuation on the audio.
*/
outl(0, io + SIS_PLAY_SYNC_GROUP_A);
outl(0, io + SIS_PLAY_SYNC_GROUP_B);
outl(0, io + SIS_PLAY_SYNC_GROUP_C);
outl(0, io + SIS_PLAY_SYNC_GROUP_D);
outl(0, io + SIS_MIXER_SYNC_GROUP);
for (i = 0; i < 64; i++) {
writel(i, SIS_MIXER_START_ADDR(ioaddr, i));
writel(SIS_MIXER_RIGHT_NO_ATTEN | SIS_MIXER_LEFT_NO_ATTEN |
SIS_MIXER_DEST_0, SIS_MIXER_ADDR(ioaddr, i));
}
/* Don't attenuate any audio set for the wave amplifier. * * FIXME: Maximum attenuation is set for the music amp, which will * need to change if we start using the synth engine.
*/
outl(0xffff0000, io + SIS_WEVCR);
/* Ensure that the wave engine is in normal operating mode.
*/
outl(0, io + SIS_WECCR);
/* Go ahead and enable the DMA interrupts. They won't go live * until we start a channel.
*/
outl(SIS_GIER_AUDIO_PLAY_DMA_IRQ_ENABLE |
SIS_GIER_AUDIO_RECORD_DMA_IRQ_ENABLE, io + SIS_GIER);
if (sis_chip_init(sis)) {
dev_err(&pci->dev, "unable to re-init controller\n"); goto error;
}
if (request_irq(pci->irq, sis_interrupt, IRQF_SHARED,
KBUILD_MODNAME, sis)) {
dev_err(&pci->dev, "unable to regain IRQ %d\n", pci->irq); goto error;
}
/* Restore saved state, then clear out the page we use for the * silence buffer.
*/ for (i = 0; i < 4; i++) {
memcpy_toio(ioaddr, sis->suspend_state[i], 4096);
ioaddr += 4096;
}
memset(sis->suspend_state[0], 0, 4096);
sis->irq = pci->irq;
if (sis->codecs_present & SIS_PRIMARY_CODEC_PRESENT)
snd_ac97_resume(sis->ac97[0]); if (sis->codecs_present & SIS_SECONDARY_CODEC_PRESENT)
snd_ac97_resume(sis->ac97[1]); if (sis->codecs_present & SIS_TERTIARY_CODEC_PRESENT)
snd_ac97_resume(sis->ac97[2]);
staticint sis_alloc_suspend(struct sis7019 *sis)
{ int i;
/* We need 16K to store the internal wave engine state during a * suspend, but we don't need it to be contiguous, so play nice * with the memory system. We'll also use this area for a silence * buffer.
*/ for (i = 0; i < SIS_SUSPEND_PAGES; i++) {
sis->suspend_state[i] = devm_kmalloc(&sis->pci->dev, 4096,
GFP_KERNEL); if (!sis->suspend_state[i]) return -ENOMEM;
}
memset(sis->suspend_state[0], 0, 4096);
return 0;
}
staticint sis_chip_create(struct snd_card *card, struct pci_dev *pci)
{ struct sis7019 *sis = card->private_data; struct voice *voice; int rc; int i;
rc = pcim_enable_device(pci); if (rc) return rc;
rc = dma_set_mask(&pci->dev, DMA_BIT_MASK(30)); if (rc < 0) {
dev_err(&pci->dev, "architecture does not support 30-bit PCI busmaster DMA"); return -ENXIO;
}
/* The user can specify which codecs should be present so that we * can wait for them to show up if they are slow to recover from * the AC97 cold reset. We default to a single codec, the primary. * * We assume that SIS_PRIMARY_*_PRESENT matches bits 0-2.
*/
codecs &= SIS_PRIMARY_CODEC_PRESENT | SIS_SECONDARY_CODEC_PRESENT |
SIS_TERTIARY_CODEC_PRESENT; if (!codecs)
codecs = SIS_PRIMARY_CODEC_PRESENT;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.