123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404 |
- #include <linux/init.h>
- #include <linux/pci.h>
- #include <linux/time.h>
- #include <linux/slab.h>
- #include <linux/module.h>
- #include <linux/interrupt.h>
- #include <linux/delay.h>
- #include <sound/core.h>
- #include <sound/ac97_codec.h>
- #include <sound/initval.h>
- #include "sis7019.h"
- MODULE_AUTHOR("David Dillow <dave@thedillows.org>");
- MODULE_DESCRIPTION("SiS7019");
- MODULE_LICENSE("GPL");
- static int index = SNDRV_DEFAULT_IDX1;
- static char *id = SNDRV_DEFAULT_STR1;
- static bool enable = 1;
- static int 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)");
- static const struct pci_device_id snd_sis7019_ids[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_SI, 0x7019) },
- { 0, }
- };
- MODULE_DEVICE_TABLE(pci, snd_sis7019_ids);
- 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;
- };
- #ifdef CONFIG_PM_SLEEP
- #define SIS_SUSPEND_PAGES 4
- #else
- #define SIS_SUSPEND_PAGES 1
- #endif
- struct sis7019 {
- unsigned long ioport;
- void __iomem *ioaddr;
- int irq;
- int codecs_present;
- struct pci_dev *pci;
- struct snd_pcm *pcm;
- struct snd_card *card;
- struct snd_ac97 *ac97[3];
-
- struct mutex ac97_mutex;
-
- spinlock_t voice_lock;
- struct voice voices[64];
- struct voice capture_voice;
-
- void *suspend_state[SIS_SUSPEND_PAGES];
- int silence_users;
- dma_addr_t silence_dma_addr;
- };
- #define SIS_PRIMARY_CODEC_PRESENT 0x0001
- #define SIS_SECONDARY_CODEC_PRESENT 0x0002
- #define SIS_TERTIARY_CODEC_PRESENT 0x0004
- static const struct 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),
- };
- static const struct snd_pcm_hardware sis_capture_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_48000,
- .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),
- };
- static void sis_update_sso(struct voice *voice, u16 period)
- {
- void __iomem *base = voice->ctrl_base;
- voice->sso += period;
- if (voice->sso >= voice->buffer_size)
- voice->sso -= voice->buffer_size;
-
- if (voice->sso < 8)
- voice->sso = 8;
-
- writew(voice->sso & 0xffff, base + SIS_PLAY_DMA_SSO_ESO + 2);
- }
- static void sis_update_voice(struct voice *voice)
- {
- if (voice->flags & VOICE_SSO_TIMING) {
- sis_update_sso(voice, voice->period_size);
- } else if (voice->flags & VOICE_SYNC_TIMING) {
- int sync;
-
- 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;
- }
-
- 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 > 0) {
- if (sync < 16)
- sync = 16;
- sis_update_sso(voice, sync);
- return;
- }
-
- if (sync > -9)
- voice->vperiod = voice->sync_period_size + 1;
- else
- voice->vperiod = voice->sync_period_size + sync + 10;
- if (voice->vperiod < voice->buffer_size) {
- sis_update_sso(voice, voice->vperiod);
- voice->vperiod = 0;
- } else
- sis_update_sso(voice, voice->period_size);
- sync = voice->sync_cso + voice->sync_period_size;
- if (sync >= voice->sync_buffer_size)
- sync -= voice->sync_buffer_size;
- voice->sync_cso = sync;
- }
- snd_pcm_period_elapsed(voice->substream);
- }
- static void sis_voice_irq(u32 status, struct voice *voice)
- {
- int bit;
- while (status) {
- bit = __ffs(status);
- status >>= bit + 1;
- voice += bit;
- sis_update_voice(voice);
- voice++;
- }
- }
- static irqreturn_t sis_interrupt(int irq, void *dev)
- {
- struct sis7019 *sis = dev;
- unsigned long io = sis->ioport;
- struct voice *voice;
- u32 intr, status;
-
- 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);
- outl(status, io + SIS_RISR);
- }
- outl(intr, io + SIS_GISR);
- intr = inl(io + SIS_GISR);
- intr &= SIS_GISR_AUDIO_PLAY_DMA_IRQ_STATUS |
- SIS_GISR_AUDIO_RECORD_DMA_IRQ_STATUS;
- } while (intr);
- return IRQ_HANDLED;
- }
- static u32 sis_rate_to_delta(unsigned int rate)
- {
- u32 delta;
-
- if (rate == 44100)
- delta = 0xeb3;
- else if (rate == 8000)
- delta = 0x2ab;
- else if (rate == 48000)
- delta = 0x1000;
- else
- delta = DIV_ROUND_CLOSEST(rate << 12, 48000) & 0x0000ffff;
- return delta;
- }
- static void __sis_map_silence(struct sis7019 *sis)
- {
-
- 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++;
- }
- static void __sis_unmap_silence(struct sis7019 *sis)
- {
-
- sis->silence_users--;
- if (!sis->silence_users)
- dma_unmap_single(&sis->pci->dev, sis->silence_dma_addr, 4096,
- DMA_TO_DEVICE);
- }
- static void sis_free_voice(struct sis7019 *sis, struct voice *voice)
- {
- unsigned long flags;
- spin_lock_irqsave(&sis->voice_lock, flags);
- if (voice->timing) {
- __sis_unmap_silence(sis);
- voice->timing->flags &= ~(VOICE_IN_USE | VOICE_SSO_TIMING |
- VOICE_SYNC_TIMING);
- voice->timing = NULL;
- }
- voice->flags &= ~(VOICE_IN_USE | VOICE_SSO_TIMING | VOICE_SYNC_TIMING);
- spin_unlock_irqrestore(&sis->voice_lock, flags);
- }
- static struct voice *__sis_alloc_playback_voice(struct sis7019 *sis)
- {
-
- struct voice *voice;
- int i;
- for (i = 0; i < 64; i++) {
- voice = &sis->voices[i];
- if (voice->flags & VOICE_IN_USE)
- continue;
- voice->flags |= VOICE_IN_USE;
- goto found_one;
- }
- voice = NULL;
- found_one:
- return voice;
- }
- static struct voice *sis_alloc_playback_voice(struct sis7019 *sis)
- {
- struct voice *voice;
- unsigned long flags;
- spin_lock_irqsave(&sis->voice_lock, flags);
- voice = __sis_alloc_playback_voice(sis);
- spin_unlock_irqrestore(&sis->voice_lock, flags);
- return voice;
- }
- static int sis_alloc_timing_voice(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
- {
- struct sis7019 *sis = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct voice *voice = runtime->private_data;
- unsigned int period_size, buffer_size;
- unsigned long flags;
- int needed;
-
- period_size = params_period_size(hw_params);
- buffer_size = params_buffer_size(hw_params);
- needed = (period_size != buffer_size &&
- period_size != (buffer_size / 2));
- if (needed && !voice->timing) {
- spin_lock_irqsave(&sis->voice_lock, flags);
- voice->timing = __sis_alloc_playback_voice(sis);
- if (voice->timing)
- __sis_map_silence(sis);
- spin_unlock_irqrestore(&sis->voice_lock, flags);
- if (!voice->timing)
- return -ENOMEM;
- voice->timing->substream = substream;
- } else if (!needed && voice->timing) {
- sis_free_voice(sis, voice);
- voice->timing = NULL;
- }
- return 0;
- }
- static int sis_playback_open(struct snd_pcm_substream *substream)
- {
- struct sis7019 *sis = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct voice *voice;
- voice = sis_alloc_playback_voice(sis);
- if (!voice)
- return -EAGAIN;
- voice->substream = substream;
- runtime->private_data = voice;
- runtime->hw = sis_playback_hw_info;
- snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
- 9, 0xfff9);
- snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
- 9, 0xfff9);
- snd_pcm_set_sync(substream);
- return 0;
- }
- static int sis_substream_close(struct snd_pcm_substream *substream)
- {
- struct sis7019 *sis = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct voice *voice = runtime->private_data;
- sis_free_voice(sis, voice);
- return 0;
- }
- static int sis_pcm_playback_prepare(struct snd_pcm_substream *substream)
- {
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct voice *voice = runtime->private_data;
- void __iomem *ctrl_base = voice->ctrl_base;
- void __iomem *wave_base = voice->wave_base;
- u32 format, dma_addr, control, sso_eso, delta, reg;
- u16 leo;
-
- 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;
-
- 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;
- if (runtime->period_size == (runtime->buffer_size / 2)) {
- control |= SIS_PLAY_DMA_INTR_AT_MLP;
- } else if (runtime->period_size != runtime->buffer_size) {
- voice->flags |= VOICE_SSO_TIMING;
- voice->sso = runtime->period_size - 1;
- voice->period_size = runtime->period_size;
- voice->buffer_size = runtime->buffer_size;
- control &= ~SIS_PLAY_DMA_INTR_AT_LEO;
- control |= SIS_PLAY_DMA_INTR_AT_SSO;
- sso_eso |= (runtime->period_size - 1) << 16;
- }
- delta = sis_rate_to_delta(runtime->rate);
-
- writel(format, ctrl_base + SIS_PLAY_DMA_FORMAT_CSO);
- writel(dma_addr, ctrl_base + SIS_PLAY_DMA_BASE);
- writel(control, ctrl_base + SIS_PLAY_DMA_CONTROL);
- writel(sso_eso, ctrl_base + SIS_PLAY_DMA_SSO_ESO);
- for (reg = 0; reg < SIS_WAVE_SIZE; reg += 4)
- writel(0, wave_base + reg);
- writel(SIS_WAVE_GENERAL_WAVE_VOLUME, wave_base + SIS_WAVE_GENERAL);
- writel(delta << 16, wave_base + SIS_WAVE_GENERAL_ARTICULATION);
- writel(SIS_WAVE_CHANNEL_CONTROL_FIRST_SAMPLE |
- SIS_WAVE_CHANNEL_CONTROL_AMP_ENABLE |
- SIS_WAVE_CHANNEL_CONTROL_INTERPOLATE_ENABLE,
- wave_base + SIS_WAVE_CHANNEL_CONTROL);
-
- readl(ctrl_base);
- return 0;
- }
- static int sis_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
- {
- struct sis7019 *sis = snd_pcm_substream_chip(substream);
- unsigned long io = sis->ioport;
- struct snd_pcm_substream *s;
- struct voice *voice;
- void *chip;
- int starting;
- u32 record = 0;
- u32 play[2] = { 0, 0 };
-
- 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) {
-
- 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;
- }
-
- 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;
- }
- static snd_pcm_uframes_t sis_pcm_pointer(struct snd_pcm_substream *substream)
- {
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct voice *voice = runtime->private_data;
- u32 cso;
- cso = readl(voice->ctrl_base + SIS_PLAY_DMA_FORMAT_CSO);
- cso &= 0xffff;
- return cso;
- }
- static int sis_capture_open(struct snd_pcm_substream *substream)
- {
- struct sis7019 *sis = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct voice *voice = &sis->capture_voice;
- unsigned long flags;
-
- 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);
- if (!voice)
- return -EAGAIN;
- voice->substream = substream;
- runtime->private_data = voice;
- runtime->hw = sis_capture_hw_info;
- runtime->hw.rates = sis->ac97[0]->rates[AC97_RATES_ADC];
- snd_pcm_limit_hw_rates(runtime);
- snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
- 9, 0xfff9);
- snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
- 9, 0xfff9);
- snd_pcm_set_sync(substream);
- return 0;
- }
- static int sis_capture_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
- {
- struct sis7019 *sis = snd_pcm_substream_chip(substream);
- int rc;
- rc = snd_ac97_set_rate(sis->ac97[0], AC97_PCM_LR_ADC_RATE,
- params_rate(hw_params));
- if (rc)
- goto out;
- rc = sis_alloc_timing_voice(substream, hw_params);
- out:
- return rc;
- }
- static void sis_prepare_timing_voice(struct voice *voice,
- struct snd_pcm_substream *substream)
- {
- struct sis7019 *sis = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct voice *timing = voice->timing;
- void __iomem *play_base = timing->ctrl_base;
- void __iomem *wave_base = timing->wave_base;
- u16 buffer_size, period_size;
- u32 format, control, sso_eso, delta;
- u32 vperiod, sso, reg;
-
- buffer_size = 4096 / runtime->channels;
- buffer_size /= snd_pcm_format_size(runtime->format, 1);
- period_size = buffer_size;
-
- vperiod = runtime->period_size + 12;
- if (vperiod > period_size) {
- u16 tail = vperiod % period_size;
- u16 quarter_period = period_size / 4;
- if (tail && tail < quarter_period) {
- u16 loops = vperiod / period_size;
- tail = quarter_period - tail;
- tail += loops - 1;
- tail /= loops;
- period_size -= tail;
- }
- sso = period_size - 1;
- } else {
-
- period_size = runtime->period_size;
- sso = vperiod - 1;
- vperiod = 0;
- }
-
- timing->flags |= VOICE_SYNC_TIMING;
- timing->sync_base = voice->ctrl_base;
- timing->sync_cso = runtime->period_size;
- timing->sync_period_size = runtime->period_size;
- timing->sync_buffer_size = runtime->buffer_size;
- timing->period_size = period_size;
- timing->buffer_size = buffer_size;
- timing->sso = sso;
- timing->vperiod = vperiod;
-
- 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);
-
- 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);
- for (reg = 0; reg < SIS_WAVE_SIZE; reg += 4)
- writel(0, wave_base + reg);
- writel(SIS_WAVE_GENERAL_WAVE_VOLUME, wave_base + SIS_WAVE_GENERAL);
- writel(delta << 16, wave_base + SIS_WAVE_GENERAL_ARTICULATION);
- writel(SIS_WAVE_CHANNEL_CONTROL_FIRST_SAMPLE |
- SIS_WAVE_CHANNEL_CONTROL_AMP_ENABLE |
- SIS_WAVE_CHANNEL_CONTROL_INTERPOLATE_ENABLE,
- wave_base + SIS_WAVE_CHANNEL_CONTROL);
- }
- static int sis_pcm_capture_prepare(struct snd_pcm_substream *substream)
- {
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct voice *voice = runtime->private_data;
- void __iomem *rec_base = voice->ctrl_base;
- u32 format, dma_addr, control;
- u16 leo;
-
- 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 (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;
- }
- writel(format, rec_base + SIS_CAPTURE_DMA_FORMAT_CSO);
- writel(dma_addr, rec_base + SIS_CAPTURE_DMA_BASE);
- writel(control, rec_base + SIS_CAPTURE_DMA_CONTROL);
-
- readl(rec_base);
- return 0;
- }
- static const struct snd_pcm_ops sis_playback_ops = {
- .open = sis_playback_open,
- .close = sis_substream_close,
- .prepare = sis_pcm_playback_prepare,
- .trigger = sis_pcm_trigger,
- .pointer = sis_pcm_pointer,
- };
- static const struct snd_pcm_ops sis_capture_ops = {
- .open = sis_capture_open,
- .close = sis_substream_close,
- .hw_params = sis_capture_hw_params,
- .prepare = sis_pcm_capture_prepare,
- .trigger = sis_pcm_trigger,
- .pointer = sis_pcm_pointer,
- };
- static int sis_pcm_create(struct sis7019 *sis)
- {
- struct snd_pcm *pcm;
- int rc;
-
- rc = snd_pcm_new(sis->card, "SiS7019", 0, 64, 1, &pcm);
- if (rc)
- return rc;
- pcm->private_data = sis;
- strcpy(pcm->name, "SiS7019");
- sis->pcm = pcm;
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &sis_playback_ops);
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &sis_capture_ops);
-
- snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
- &sis->pci->dev, 64*1024, 128*1024);
- return 0;
- }
- static unsigned short sis_ac97_rw(struct sis7019 *sis, int codec, u32 cmd)
- {
- unsigned long io = sis->ioport;
- unsigned short val = 0xffff;
- u16 status;
- u16 rdy;
- int count;
- static const u16 codec_ready[3] = {
- SIS_AC97_STATUS_CODEC_READY,
- SIS_AC97_STATUS_CODEC2_READY,
- SIS_AC97_STATUS_CODEC3_READY,
- };
- rdy = codec_ready[codec];
-
- mutex_lock(&sis->ac97_mutex);
- count = 0xffff;
- while ((inw(io + SIS_AC97_SEMA) & SIS_AC97_SEMA_BUSY) && --count)
- udelay(1);
- if (!count)
- goto timeout;
-
- 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;
-
- outl(cmd, io + SIS_AC97_CMD);
- udelay(10);
- count = 0xffff;
- while ((inw(io + SIS_AC97_STATUS) & SIS_AC97_STATUS_BUSY) && --count)
- udelay(1);
-
- val = inl(io + SIS_AC97_CMD) >> 16;
- timeout_sema:
- outl(SIS_AC97_SEMA_RELEASE, io + SIS_AC97_SEMA);
- timeout:
- mutex_unlock(&sis->ac97_mutex);
- if (!count) {
- dev_err(&sis->pci->dev, "ac97 codec %d timeout cmd 0x%08x\n",
- codec, cmd);
- }
- return val;
- }
- static void sis_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
- unsigned short val)
- {
- static const u32 cmd[3] = {
- SIS_AC97_CMD_CODEC_WRITE,
- SIS_AC97_CMD_CODEC2_WRITE,
- SIS_AC97_CMD_CODEC3_WRITE,
- };
- sis_ac97_rw(ac97->private_data, ac97->num,
- (val << 16) | (reg << 8) | cmd[ac97->num]);
- }
- static unsigned short sis_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
- {
- static const u32 cmd[3] = {
- SIS_AC97_CMD_CODEC_READ,
- SIS_AC97_CMD_CODEC2_READ,
- SIS_AC97_CMD_CODEC3_READ,
- };
- return sis_ac97_rw(ac97->private_data, ac97->num,
- (reg << 8) | cmd[ac97->num]);
- }
- static int sis_mixer_create(struct sis7019 *sis)
- {
- struct snd_ac97_bus *bus;
- struct snd_ac97_template ac97;
- static const struct snd_ac97_bus_ops ops = {
- .write = sis_ac97_write,
- .read = sis_ac97_read,
- };
- int rc;
- memset(&ac97, 0, sizeof(ac97));
- ac97.private_data = sis;
- rc = snd_ac97_bus(sis->card, 0, &ops, NULL, &bus);
- if (!rc && sis->codecs_present & SIS_PRIMARY_CODEC_PRESENT)
- rc = snd_ac97_mixer(bus, &ac97, &sis->ac97[0]);
- ac97.num = 1;
- if (!rc && (sis->codecs_present & SIS_SECONDARY_CODEC_PRESENT))
- rc = snd_ac97_mixer(bus, &ac97, &sis->ac97[1]);
- ac97.num = 2;
- if (!rc && (sis->codecs_present & SIS_TERTIARY_CODEC_PRESENT))
- rc = snd_ac97_mixer(bus, &ac97, &sis->ac97[2]);
-
- return rc;
- }
- static void sis_chip_free(struct snd_card *card)
- {
- struct sis7019 *sis = card->private_data;
-
- outl(SIS_GCR_SOFTWARE_RESET, sis->ioport + SIS_GCR);
- udelay(25);
- outl(0, sis->ioport + SIS_GCR);
- outl(0, sis->ioport + SIS_GIER);
-
- if (sis->irq >= 0)
- free_irq(sis->irq, sis);
- }
- static int sis_chip_init(struct sis7019 *sis)
- {
- unsigned long io = sis->ioport;
- void __iomem *ioaddr = sis->ioaddr;
- unsigned long timeout;
- u16 status;
- int count;
- int i;
-
- outl(SIS_GCR_SOFTWARE_RESET, io + SIS_GCR);
- udelay(25);
- outl(0, io + SIS_GCR);
-
- count = 0xffff;
- while ((inw(io + SIS_AC97_SEMA) & SIS_AC97_SEMA_BUSY) && --count)
- udelay(1);
- if (!count)
- return -EIO;
- outl(SIS_AC97_CMD_CODEC_COLD_RESET, io + SIS_AC97_CMD);
- udelay(250);
- count = 0xffff;
- while ((inw(io + SIS_AC97_STATUS) & SIS_AC97_STATUS_BUSY) && --count)
- udelay(1);
-
- outl(SIS_AC97_SEMA_RELEASE, io + SIS_AC97_SEMA);
- if (!count)
- return -EIO;
-
- 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);
- }
-
- 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);
- }
-
- 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);
-
- outl(0, io + SIS_AC97_PSR);
-
- outl(SIS_DMA_CSR_PCI_SETTINGS, io + SIS_DMA_CSR);
-
- 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));
- }
-
- outl(0xffff0000, io + SIS_WEVCR);
-
- outl(0, io + SIS_WECCR);
-
- outl(SIS_GIER_AUDIO_PLAY_DMA_IRQ_ENABLE |
- SIS_GIER_AUDIO_RECORD_DMA_IRQ_ENABLE, io + SIS_GIER);
- return 0;
- }
- #ifdef CONFIG_PM_SLEEP
- static int sis_suspend(struct device *dev)
- {
- struct snd_card *card = dev_get_drvdata(dev);
- struct sis7019 *sis = card->private_data;
- void __iomem *ioaddr = sis->ioaddr;
- int i;
- snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- if (sis->codecs_present & SIS_PRIMARY_CODEC_PRESENT)
- snd_ac97_suspend(sis->ac97[0]);
- if (sis->codecs_present & SIS_SECONDARY_CODEC_PRESENT)
- snd_ac97_suspend(sis->ac97[1]);
- if (sis->codecs_present & SIS_TERTIARY_CODEC_PRESENT)
- snd_ac97_suspend(sis->ac97[2]);
-
- if (sis->irq >= 0) {
- free_irq(sis->irq, sis);
- sis->irq = -1;
- }
-
- for (i = 0; i < 4; i++) {
- memcpy_fromio(sis->suspend_state[i], ioaddr, 4096);
- ioaddr += 4096;
- }
- return 0;
- }
- static int sis_resume(struct device *dev)
- {
- struct pci_dev *pci = to_pci_dev(dev);
- struct snd_card *card = dev_get_drvdata(dev);
- struct sis7019 *sis = card->private_data;
- void __iomem *ioaddr = sis->ioaddr;
- int i;
- 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;
- }
-
- 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]);
- snd_power_change_state(card, SNDRV_CTL_POWER_D0);
- return 0;
- error:
- snd_card_disconnect(card);
- return -EIO;
- }
- static SIMPLE_DEV_PM_OPS(sis_pm, sis_suspend, sis_resume);
- #define SIS_PM_OPS &sis_pm
- #else
- #define SIS_PM_OPS NULL
- #endif
- static int sis_alloc_suspend(struct sis7019 *sis)
- {
- int i;
-
- 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;
- }
- static int 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;
- }
- mutex_init(&sis->ac97_mutex);
- spin_lock_init(&sis->voice_lock);
- sis->card = card;
- sis->pci = pci;
- sis->irq = -1;
- sis->ioport = pci_resource_start(pci, 0);
- rc = pci_request_regions(pci, "SiS7019");
- if (rc) {
- dev_err(&pci->dev, "unable request regions\n");
- return rc;
- }
- sis->ioaddr = devm_ioremap(&pci->dev, pci_resource_start(pci, 1), 0x4000);
- if (!sis->ioaddr) {
- dev_err(&pci->dev, "unable to remap MMIO, aborting\n");
- return -EIO;
- }
- rc = sis_alloc_suspend(sis);
- if (rc < 0) {
- dev_err(&pci->dev, "unable to allocate state storage\n");
- return rc;
- }
- rc = sis_chip_init(sis);
- if (rc)
- return rc;
- card->private_free = sis_chip_free;
- rc = request_irq(pci->irq, sis_interrupt, IRQF_SHARED, KBUILD_MODNAME,
- sis);
- if (rc) {
- dev_err(&pci->dev, "unable to allocate irq %d\n", sis->irq);
- return rc;
- }
- sis->irq = pci->irq;
- card->sync_irq = sis->irq;
- pci_set_master(pci);
- for (i = 0; i < 64; i++) {
- voice = &sis->voices[i];
- voice->num = i;
- voice->ctrl_base = SIS_PLAY_DMA_ADDR(sis->ioaddr, i);
- voice->wave_base = SIS_WAVE_ADDR(sis->ioaddr, i);
- }
- voice = &sis->capture_voice;
- voice->flags = VOICE_CAPTURE;
- voice->num = SIS_CAPTURE_CHAN_AC97_PCM_IN;
- voice->ctrl_base = SIS_CAPTURE_DMA_ADDR(sis->ioaddr, voice->num);
- return 0;
- }
- static int __snd_sis7019_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
- {
- struct snd_card *card;
- struct sis7019 *sis;
- int rc;
- if (!enable)
- return -ENOENT;
-
- codecs &= SIS_PRIMARY_CODEC_PRESENT | SIS_SECONDARY_CODEC_PRESENT |
- SIS_TERTIARY_CODEC_PRESENT;
- if (!codecs)
- codecs = SIS_PRIMARY_CODEC_PRESENT;
- rc = snd_devm_card_new(&pci->dev, index, id, THIS_MODULE,
- sizeof(*sis), &card);
- if (rc < 0)
- return rc;
- strcpy(card->driver, "SiS7019");
- strcpy(card->shortname, "SiS7019");
- rc = sis_chip_create(card, pci);
- if (rc)
- return rc;
- sis = card->private_data;
- rc = sis_mixer_create(sis);
- if (rc)
- return rc;
- rc = sis_pcm_create(sis);
- if (rc)
- return rc;
- snprintf(card->longname, sizeof(card->longname),
- "%s Audio Accelerator with %s at 0x%lx, irq %d",
- card->shortname, snd_ac97_get_short_name(sis->ac97[0]),
- sis->ioport, sis->irq);
- rc = snd_card_register(card);
- if (rc)
- return rc;
- pci_set_drvdata(pci, card);
- return 0;
- }
- static int snd_sis7019_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
- {
- return snd_card_free_on_error(&pci->dev, __snd_sis7019_probe(pci, pci_id));
- }
- static struct pci_driver sis7019_driver = {
- .name = KBUILD_MODNAME,
- .id_table = snd_sis7019_ids,
- .probe = snd_sis7019_probe,
- .driver = {
- .pm = SIS_PM_OPS,
- },
- };
- module_pci_driver(sis7019_driver);
|