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)");
/* We need four pages to store our wave parameters during a suspend. If *we'renotdoingpowermanagement,westillneedtoallocateapage *forthesilencebuffer.
*/ #define SIS_SUSPEND_PAGES 4
struct sis7019 { unsignedlong ioport; void __iomem *ioaddr; int irq; int codecs_present;
/* Allocate pages to store the internal wave state during *suspends.Whenwe'reoperating,thiscanbeusedasasilence *bufferforatimingchannel.
*/ 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 *whichcodecsshouldbepresent.
*/ #define SIS_PRIMARY_CODEC_PRESENT 0x0001 #define SIS_SECONDARY_CODEC_PRESENT 0x0002 #define SIS_TERTIARY_CODEC_PRESENT 0x0004
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 *ourrecordsandkeepgoing.
*/ 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 *theactualCSOvalue.Sincewe'reoperatinginaloop, *ifthevalueismorethanhalfwayaround,wecan *considerourselveswrapped.
*/
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'llneedtocomebackinafewsamplesandtryagain. *There'saminimumwait,asittakessometimefortheDMA *enginetostartup,etc...
*/ if (sync > 0) { if (sync < 16)
sync = 16;
sis_update_sso(voice, sync); return;
}
/* Ok, we interrupted right on time, or (hopefully) just *abitlate.We'lladjstournextwaitingperiodbased *onhowclosewegot. * *Weneedtostayjustbehindtheactualchanneltoensure *itreallyispastaperiodwhenwegetourinterrupt-- *otherwisewe'llfallintotheearlycodeaboveandhave *aminimumwaittime,whichmakesusquitelatehere, *eatingintotheuser'stimetorefreshthebuffer,esp. *ifusingsmallperiods. * *Ifwe'relessthan9samplesbehind,we'reontarget. *Otherwise,shortenthenextvperiodbytheamountwe've *beendelayed.
*/ 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 *sourceofinterrupts.But,itispossibletoseeaninterrupt *statusthatdidn'tactuallyinterruptus,soeliminateanything *we'renotexpectingtoavoidfalselyclaiminganIRQ,andan *ensuingendlessloop.
*/
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);
/* If there are one or two periods per buffer, we don't need a *timingvoice,aswecanusethecapturechannel'sinterrupts *toclockouttheperiods.
*/
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 *substreamdonotchangeonuswhilewe'reprogrammingtheHW.
*/
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 *weaddbellsandwhistlesasneededfromthere.
*/
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,andtheHWwillonlystart/stoptheindicatedvoices *withoutchangingthestateoftheothers.
*/ 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'thaveanexternaltimingchannel.
*/ 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);
} return0;
}
/* Set our initial buffer and period as large as we can given a *singlepageofsilence.
*/
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 *theperiodwe'reclockingout.12samplesseemstogiveagood *delay. * *Wewanttospreadourinterruptsthroughoutthevirtualperiod, *sothatwedon'tendupwithtwointerruptsbacktobackatthe *end--thishelpsminimizetheeffectsofanyjitter.Adjustour *clockingperiodsizesothatthelastperiodisatleastafourth *ofafullperiod. * *Thisisallmootifwedon'tneedtousevirtualperiods.
*/
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'tneedtousevirtualperiods--disablethem.
*/
period_size = runtime->period_size;
sso = vperiod - 1;
vperiod = 0;
}
/* Using unsigned samples with the all-zero silence buffer *forcestheoutputtothelowerrail,killingplayback. *Soignoreunsignedvssigned--itdoesn'tchangethetiming.
*/
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 *substreamdonotchangeonuswhilewe'reprogrammingtheHW.
*/
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 *useatimingvoicetoclockouttheperiods.Otherwise,wecan *usethecapturechannel'sinterrupts.
*/ 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 *onlyonechannel,thoughthatcouldchangeinthefuture.
*/
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 *worldifthisfails.
*/
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. *Therearesomecodec/boardcombinationsthattakeanextremely *longtimetocomeup.350+mshasbeenobservedinthefield, *sowe'llgivethemupto500ms.
*/
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, *andenablePCMslotsontheAC-linkforL/Rplayback(3&4)and *recordchannels.We'regoingtowanttouseVariableRateAudio *forrecording,toavoidneedlesslyresamplingfrom48kHZ.
*/
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 *tobeasynchronous.IfwestartdoingSPDIFor5.1sound,etc. *we'llneedtochangehowwehandlethese.Untilthen,wejust *assignsub-mixer0toallplaybackchannels,andavoidany *attenuationontheaudio.
*/
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:Maximumattenuationissetforthemusicamp,whichwill *needtochangeifwestartusingthesynthengine.
*/
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 *untilwestartachannel.
*/
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 *silencebuffer.
*/ 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,butwedon'tneedittobecontiguous,soplaynice *withthememorysystem.We'llalsousethisareaforasilence *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);
return0;
}
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 *canwaitforthemtoshowupiftheyareslowtorecoverfrom *theAC97coldreset.Wedefaulttoasinglecodec,theprimary. * *WeassumethatSIS_PRIMARY_*_PRESENTmatchesbits0-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.