X-Git-Url: http://pilppa.org/gitweb/?a=blobdiff_plain;f=sound%2Fpci%2Fes1938.c;h=84fac1fbf10399bf9a3c0008945e78e9ad8c1d0f;hb=69ac9da2854bb06083aed4bf1512be0dec93571e;hp=fb25abe68a020a7eff6b8ba9f52e950b38222bf5;hpb=95b00786f3b8fa99f53931361beeb4c10504ad87;p=linux-2.6-omap-h63xx.git diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index fb25abe68a0..84fac1fbf10 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -47,7 +47,6 @@ */ -#include #include #include #include @@ -227,6 +226,7 @@ struct es1938 { unsigned int dma2_start; unsigned int dma1_shift; unsigned int dma2_shift; + unsigned int last_capture_dmaaddr; unsigned int active; spinlock_t reg_lock; @@ -529,6 +529,7 @@ static void snd_es1938_capture_setdma(struct es1938 *chip) outb(1, SLDM_REG(chip, DMAMASK)); outb(0x14, SLDM_REG(chip, DMAMODE)); outl(chip->dma1_start, SLDM_REG(chip, DMAADDR)); + chip->last_capture_dmaaddr = chip->dma1_start; outw(chip->dma1_size - 1, SLDM_REG(chip, DMACOUNT)); /* 3. Unmask DMA */ outb(0, SLDM_REG(chip, DMAMASK)); @@ -770,19 +771,40 @@ static int snd_es1938_playback_prepare(struct snd_pcm_substream *substream) return -EINVAL; } +/* during the incrementing of dma counters the DMA register reads sometimes + returns garbage. To ensure a valid hw pointer, the following checks which + should be very unlikely to fail are used: + - is the current DMA address in the valid DMA range ? + - is the sum of DMA address and DMA counter pointing to the last DMA byte ? + One can argue this could differ by one byte depending on which register is + updated first, so the implementation below allows for that. +*/ static snd_pcm_uframes_t snd_es1938_capture_pointer(struct snd_pcm_substream *substream) { struct es1938 *chip = snd_pcm_substream_chip(substream); size_t ptr; +#if 0 size_t old, new; -#if 1 /* This stuff is *needed*, don't ask why - AB */ old = inw(SLDM_REG(chip, DMACOUNT)); while ((new = inw(SLDM_REG(chip, DMACOUNT))) != old) old = new; ptr = chip->dma1_size - 1 - new; #else - ptr = inl(SLDM_REG(chip, DMAADDR)) - chip->dma1_start; + size_t count; + unsigned int diff; + + ptr = inl(SLDM_REG(chip, DMAADDR)); + count = inw(SLDM_REG(chip, DMACOUNT)); + diff = chip->dma1_start + chip->dma1_size - ptr - count; + + if (diff > 3 || ptr < chip->dma1_start + || ptr >= chip->dma1_start+chip->dma1_size) + ptr = chip->last_capture_dmaaddr; /* bad, use last saved */ + else + chip->last_capture_dmaaddr = ptr; /* good, remember it */ + + ptr -= chip->dma1_start; #endif return ptr >> chip->dma1_shift; } @@ -1466,7 +1488,6 @@ static int es1938_suspend(struct pci_dev *pci, pm_message_t state) outb(0x00, SLIO_REG(chip, IRQCONTROL)); /* disable irqs */ if (chip->irq >= 0) { - synchronize_irq(chip->irq); free_irq(chip->irq, chip); chip->irq = -1; } @@ -1556,10 +1577,8 @@ static int snd_es1938_free(struct es1938 *chip) snd_es1938_free_gameport(chip); - if (chip->irq >= 0) { - synchronize_irq(chip->irq); + if (chip->irq >= 0) free_irq(chip->irq, chip); - } pci_release_regions(chip->pci); pci_disable_device(chip->pci); kfree(chip);