aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci/via82xx.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/pci/via82xx.c')
-rw-r--r--sound/pci/via82xx.c170
1 files changed, 131 insertions, 39 deletions
diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c
index 7d46bee828c7..eb35b446235c 100644
--- a/sound/pci/via82xx.c
+++ b/sound/pci/via82xx.c
@@ -41,6 +41,9 @@
41 * device for applications. 41 * device for applications.
42 * - clean up the code, separate low-level initialization 42 * - clean up the code, separate low-level initialization
43 * routines for each chipset. 43 * routines for each chipset.
44 *
45 * Sep. 26, 2005 Karsten Wiese <annabellesgarden@yahoo.de>
46 * - Optimize position calculation for the 823x chips.
44 */ 47 */
45 48
46#include <sound/driver.h> 49#include <sound/driver.h>
@@ -131,6 +134,7 @@ module_param(enable, int, 0444);
131/* common offsets */ 134/* common offsets */
132#define VIA_REG_OFFSET_STATUS 0x00 /* byte - channel status */ 135#define VIA_REG_OFFSET_STATUS 0x00 /* byte - channel status */
133#define VIA_REG_STAT_ACTIVE 0x80 /* RO */ 136#define VIA_REG_STAT_ACTIVE 0x80 /* RO */
137#define VIA8233_SHADOW_STAT_ACTIVE 0x08 /* RO */
134#define VIA_REG_STAT_PAUSED 0x40 /* RO */ 138#define VIA_REG_STAT_PAUSED 0x40 /* RO */
135#define VIA_REG_STAT_TRIGGER_QUEUED 0x08 /* RO */ 139#define VIA_REG_STAT_TRIGGER_QUEUED 0x08 /* RO */
136#define VIA_REG_STAT_STOPPED 0x04 /* RWC */ 140#define VIA_REG_STAT_STOPPED 0x04 /* RWC */
@@ -329,6 +333,9 @@ struct via_dev {
329 unsigned int fragsize; 333 unsigned int fragsize;
330 unsigned int bufsize; 334 unsigned int bufsize;
331 unsigned int bufsize2; 335 unsigned int bufsize2;
336 int hwptr_done; /* processed frame position in the buffer */
337 int in_interrupt;
338 int shadow_shift;
332}; 339};
333 340
334 341
@@ -395,8 +402,10 @@ struct _snd_via82xx {
395}; 402};
396 403
397static struct pci_device_id snd_via82xx_ids[] = { 404static struct pci_device_id snd_via82xx_ids[] = {
398 { 0x1106, 0x3058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_CARD_VIA686, }, /* 686A */ 405 /* 0x1106, 0x3058 */
399 { 0x1106, 0x3059, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_CARD_VIA8233, }, /* VT8233 */ 406 { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_CARD_VIA686, }, /* 686A */
407 /* 0x1106, 0x3059 */
408 { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8233_5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_CARD_VIA8233, }, /* VT8233 */
400 { 0, } 409 { 0, }
401}; 410};
402 411
@@ -550,7 +559,7 @@ static void snd_via82xx_codec_write(ac97_t *ac97,
550{ 559{
551 via82xx_t *chip = ac97->private_data; 560 via82xx_t *chip = ac97->private_data;
552 unsigned int xval; 561 unsigned int xval;
553 562
554 xval = !ac97->num ? VIA_REG_AC97_CODEC_ID_PRIMARY : VIA_REG_AC97_CODEC_ID_SECONDARY; 563 xval = !ac97->num ? VIA_REG_AC97_CODEC_ID_PRIMARY : VIA_REG_AC97_CODEC_ID_SECONDARY;
555 xval <<= VIA_REG_AC97_CODEC_ID_SHIFT; 564 xval <<= VIA_REG_AC97_CODEC_ID_SHIFT;
556 xval |= reg << VIA_REG_AC97_CMD_SHIFT; 565 xval |= reg << VIA_REG_AC97_CMD_SHIFT;
@@ -598,14 +607,15 @@ static void snd_via82xx_channel_reset(via82xx_t *chip, viadev_t *viadev)
598 outb(0x00, VIADEV_REG(viadev, OFFSET_TYPE)); /* for via686 */ 607 outb(0x00, VIADEV_REG(viadev, OFFSET_TYPE)); /* for via686 */
599 // outl(0, VIADEV_REG(viadev, OFFSET_CURR_PTR)); 608 // outl(0, VIADEV_REG(viadev, OFFSET_CURR_PTR));
600 viadev->lastpos = 0; 609 viadev->lastpos = 0;
610 viadev->hwptr_done = 0;
601} 611}
602 612
603 613
604/* 614/*
605 * Interrupt handler 615 * Interrupt handler
616 * Used for 686 and 8233A
606 */ 617 */
607 618static irqreturn_t snd_via686_interrupt(int irq, void *dev_id, struct pt_regs *regs)
608static irqreturn_t snd_via82xx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
609{ 619{
610 via82xx_t *chip = dev_id; 620 via82xx_t *chip = dev_id;
611 unsigned int status; 621 unsigned int status;
@@ -624,13 +634,23 @@ static irqreturn_t snd_via82xx_interrupt(int irq, void *dev_id, struct pt_regs *
624 for (i = 0; i < chip->num_devs; i++) { 634 for (i = 0; i < chip->num_devs; i++) {
625 viadev_t *viadev = &chip->devs[i]; 635 viadev_t *viadev = &chip->devs[i];
626 unsigned char c_status = inb(VIADEV_REG(viadev, OFFSET_STATUS)); 636 unsigned char c_status = inb(VIADEV_REG(viadev, OFFSET_STATUS));
627 c_status &= (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG|VIA_REG_STAT_STOPPED); 637 if (! (c_status & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG|VIA_REG_STAT_STOPPED)))
628 if (! c_status)
629 continue; 638 continue;
630 if (viadev->substream && viadev->running) { 639 if (viadev->substream && viadev->running) {
640 /*
641 * Update hwptr_done based on 'period elapsed'
642 * interrupts. We'll use it, when the chip returns 0
643 * for OFFSET_CURR_COUNT.
644 */
645 if (c_status & VIA_REG_STAT_EOL)
646 viadev->hwptr_done = 0;
647 else
648 viadev->hwptr_done += viadev->fragsize;
649 viadev->in_interrupt = c_status;
631 spin_unlock(&chip->reg_lock); 650 spin_unlock(&chip->reg_lock);
632 snd_pcm_period_elapsed(viadev->substream); 651 snd_pcm_period_elapsed(viadev->substream);
633 spin_lock(&chip->reg_lock); 652 spin_lock(&chip->reg_lock);
653 viadev->in_interrupt = 0;
634 } 654 }
635 outb(c_status, VIADEV_REG(viadev, OFFSET_STATUS)); /* ack */ 655 outb(c_status, VIADEV_REG(viadev, OFFSET_STATUS)); /* ack */
636 } 656 }
@@ -639,6 +659,60 @@ static irqreturn_t snd_via82xx_interrupt(int irq, void *dev_id, struct pt_regs *
639} 659}
640 660
641/* 661/*
662 * Interrupt handler
663 */
664static irqreturn_t snd_via8233_interrupt(int irq, void *dev_id, struct pt_regs *regs)
665{
666 via82xx_t *chip = dev_id;
667 unsigned int status;
668 unsigned int i;
669 int irqreturn = 0;
670
671 /* check status for each stream */
672 spin_lock(&chip->reg_lock);
673 status = inl(VIAREG(chip, SGD_SHADOW));
674
675 for (i = 0; i < chip->num_devs; i++) {
676 viadev_t *viadev = &chip->devs[i];
677 snd_pcm_substream_t *substream;
678 unsigned char c_status, shadow_status;
679
680 shadow_status = (status >> viadev->shadow_shift) &
681 (VIA8233_SHADOW_STAT_ACTIVE|VIA_REG_STAT_EOL|
682 VIA_REG_STAT_FLAG);
683 c_status = shadow_status & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG);
684 if (!c_status)
685 continue;
686
687 substream = viadev->substream;
688 if (substream && viadev->running) {
689 /*
690 * Update hwptr_done based on 'period elapsed'
691 * interrupts. We'll use it, when the chip returns 0
692 * for OFFSET_CURR_COUNT.
693 */
694 if (c_status & VIA_REG_STAT_EOL)
695 viadev->hwptr_done = 0;
696 else
697 viadev->hwptr_done += viadev->fragsize;
698 viadev->in_interrupt = c_status;
699 if (shadow_status & VIA8233_SHADOW_STAT_ACTIVE)
700 viadev->in_interrupt |= VIA_REG_STAT_ACTIVE;
701 spin_unlock(&chip->reg_lock);
702
703 snd_pcm_period_elapsed(substream);
704
705 spin_lock(&chip->reg_lock);
706 viadev->in_interrupt = 0;
707 }
708 outb(c_status, VIADEV_REG(viadev, OFFSET_STATUS)); /* ack */
709 irqreturn = 1;
710 }
711 spin_unlock(&chip->reg_lock);
712 return IRQ_RETVAL(irqreturn);
713}
714
715/*
642 * PCM callbacks 716 * PCM callbacks
643 */ 717 */
644 718
@@ -701,6 +775,8 @@ static inline unsigned int calc_linear_pos(viadev_t *viadev, unsigned int idx, u
701 size = viadev->idx_table[idx].size; 775 size = viadev->idx_table[idx].size;
702 base = viadev->idx_table[idx].offset; 776 base = viadev->idx_table[idx].offset;
703 res = base + size - count; 777 res = base + size - count;
778 if (res >= viadev->bufsize)
779 res -= viadev->bufsize;
704 780
705 /* check the validity of the calculated position */ 781 /* check the validity of the calculated position */
706 if (size < count) { 782 if (size < count) {
@@ -730,9 +806,6 @@ static inline unsigned int calc_linear_pos(viadev_t *viadev, unsigned int idx, u
730 } 806 }
731 } 807 }
732 } 808 }
733 viadev->lastpos = res; /* remember the last position */
734 if (res >= viadev->bufsize)
735 res -= viadev->bufsize;
736 return res; 809 return res;
737} 810}
738 811
@@ -760,6 +833,7 @@ static snd_pcm_uframes_t snd_via686_pcm_pointer(snd_pcm_substream_t *substream)
760 else /* CURR_PTR holds the address + 8 */ 833 else /* CURR_PTR holds the address + 8 */
761 idx = ((ptr - (unsigned int)viadev->table.addr) / 8 - 1) % viadev->tbl_entries; 834 idx = ((ptr - (unsigned int)viadev->table.addr) / 8 - 1) % viadev->tbl_entries;
762 res = calc_linear_pos(viadev, idx, count); 835 res = calc_linear_pos(viadev, idx, count);
836 viadev->lastpos = res; /* remember the last position */
763 spin_unlock(&chip->reg_lock); 837 spin_unlock(&chip->reg_lock);
764 838
765 return bytes_to_frames(substream->runtime, res); 839 return bytes_to_frames(substream->runtime, res);
@@ -773,30 +847,44 @@ static snd_pcm_uframes_t snd_via8233_pcm_pointer(snd_pcm_substream_t *substream)
773 via82xx_t *chip = snd_pcm_substream_chip(substream); 847 via82xx_t *chip = snd_pcm_substream_chip(substream);
774 viadev_t *viadev = (viadev_t *)substream->runtime->private_data; 848 viadev_t *viadev = (viadev_t *)substream->runtime->private_data;
775 unsigned int idx, count, res; 849 unsigned int idx, count, res;
776 int timeout = 5000; 850 int status;
777 851
778 snd_assert(viadev->tbl_entries, return 0); 852 snd_assert(viadev->tbl_entries, return 0);
779 if (!(inb(VIADEV_REG(viadev, OFFSET_STATUS)) & VIA_REG_STAT_ACTIVE)) 853
780 return 0;
781 spin_lock(&chip->reg_lock); 854 spin_lock(&chip->reg_lock);
782 do { 855 count = inl(VIADEV_REG(viadev, OFFSET_CURR_COUNT));
783 count = inl(VIADEV_REG(viadev, OFFSET_CURR_COUNT)); 856 status = viadev->in_interrupt;
784 /* some mobos read 0 count */ 857 if (!status)
785 if ((count & 0xffffff) || ! viadev->running) 858 status = inb(VIADEV_REG(viadev, OFFSET_STATUS));
786 break; 859
787 } while (--timeout); 860 if (!(status & VIA_REG_STAT_ACTIVE)) {
788 if (! timeout) 861 res = 0;
789 snd_printd(KERN_ERR "zero position is read\n"); 862 goto unlock;
790 idx = count >> 24; 863 }
791 if (idx >= viadev->tbl_entries) { 864 if (count & 0xffffff) {
865 idx = count >> 24;
866 if (idx >= viadev->tbl_entries) {
792#ifdef POINTER_DEBUG 867#ifdef POINTER_DEBUG
793 printk("fail: invalid idx = %i/%i\n", idx, viadev->tbl_entries); 868 printk("fail: invalid idx = %i/%i\n", idx, viadev->tbl_entries);
794#endif 869#endif
795 res = viadev->lastpos; 870 res = viadev->lastpos;
871 } else {
872 count &= 0xffffff;
873 res = calc_linear_pos(viadev, idx, count);
874 }
796 } else { 875 } else {
797 count &= 0xffffff; 876 res = viadev->hwptr_done;
798 res = calc_linear_pos(viadev, idx, count); 877 if (!viadev->in_interrupt) {
799 } 878 if (status & VIA_REG_STAT_EOL) {
879 res = 0;
880 } else
881 if (status & VIA_REG_STAT_FLAG) {
882 res += viadev->fragsize;
883 }
884 }
885 }
886unlock:
887 viadev->lastpos = res;
800 spin_unlock(&chip->reg_lock); 888 spin_unlock(&chip->reg_lock);
801 889
802 return bytes_to_frames(substream->runtime, res); 890 return bytes_to_frames(substream->runtime, res);
@@ -1241,9 +1329,10 @@ static snd_pcm_ops_t snd_via8233_capture_ops = {
1241}; 1329};
1242 1330
1243 1331
1244static void init_viadev(via82xx_t *chip, int idx, unsigned int reg_offset, int direction) 1332static void init_viadev(via82xx_t *chip, int idx, unsigned int reg_offset, int shadow_pos, int direction)
1245{ 1333{
1246 chip->devs[idx].reg_offset = reg_offset; 1334 chip->devs[idx].reg_offset = reg_offset;
1335 chip->devs[idx].shadow_shift = shadow_pos * 4;
1247 chip->devs[idx].direction = direction; 1336 chip->devs[idx].direction = direction;
1248 chip->devs[idx].port = chip->port + reg_offset; 1337 chip->devs[idx].port = chip->port + reg_offset;
1249} 1338}
@@ -1273,9 +1362,9 @@ static int __devinit snd_via8233_pcm_new(via82xx_t *chip)
1273 chip->pcms[0] = pcm; 1362 chip->pcms[0] = pcm;
1274 /* set up playbacks */ 1363 /* set up playbacks */
1275 for (i = 0; i < 4; i++) 1364 for (i = 0; i < 4; i++)
1276 init_viadev(chip, i, 0x10 * i, 0); 1365 init_viadev(chip, i, 0x10 * i, i, 0);
1277 /* capture */ 1366 /* capture */
1278 init_viadev(chip, chip->capture_devno, VIA_REG_CAPTURE_8233_STATUS, 1); 1367 init_viadev(chip, chip->capture_devno, VIA_REG_CAPTURE_8233_STATUS, 6, 1);
1279 1368
1280 if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, 1369 if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
1281 snd_dma_pci_data(chip->pci), 64*1024, 128*1024)) < 0) 1370 snd_dma_pci_data(chip->pci), 64*1024, 128*1024)) < 0)
@@ -1291,9 +1380,9 @@ static int __devinit snd_via8233_pcm_new(via82xx_t *chip)
1291 strcpy(pcm->name, chip->card->shortname); 1380 strcpy(pcm->name, chip->card->shortname);
1292 chip->pcms[1] = pcm; 1381 chip->pcms[1] = pcm;
1293 /* set up playback */ 1382 /* set up playback */
1294 init_viadev(chip, chip->multi_devno, VIA_REG_MULTPLAY_STATUS, 0); 1383 init_viadev(chip, chip->multi_devno, VIA_REG_MULTPLAY_STATUS, 4, 0);
1295 /* set up capture */ 1384 /* set up capture */
1296 init_viadev(chip, chip->capture_devno + 1, VIA_REG_CAPTURE_8233_STATUS + 0x10, 1); 1385 init_viadev(chip, chip->capture_devno + 1, VIA_REG_CAPTURE_8233_STATUS + 0x10, 7, 1);
1297 1386
1298 if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, 1387 if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
1299 snd_dma_pci_data(chip->pci), 64*1024, 128*1024)) < 0) 1388 snd_dma_pci_data(chip->pci), 64*1024, 128*1024)) < 0)
@@ -1326,9 +1415,9 @@ static int __devinit snd_via8233a_pcm_new(via82xx_t *chip)
1326 strcpy(pcm->name, chip->card->shortname); 1415 strcpy(pcm->name, chip->card->shortname);
1327 chip->pcms[0] = pcm; 1416 chip->pcms[0] = pcm;
1328 /* set up playback */ 1417 /* set up playback */
1329 init_viadev(chip, chip->multi_devno, VIA_REG_MULTPLAY_STATUS, 0); 1418 init_viadev(chip, chip->multi_devno, VIA_REG_MULTPLAY_STATUS, 4, 0);
1330 /* capture */ 1419 /* capture */
1331 init_viadev(chip, chip->capture_devno, VIA_REG_CAPTURE_8233_STATUS, 1); 1420 init_viadev(chip, chip->capture_devno, VIA_REG_CAPTURE_8233_STATUS, 6, 1);
1332 1421
1333 if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, 1422 if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
1334 snd_dma_pci_data(chip->pci), 64*1024, 128*1024)) < 0) 1423 snd_dma_pci_data(chip->pci), 64*1024, 128*1024)) < 0)
@@ -1347,7 +1436,7 @@ static int __devinit snd_via8233a_pcm_new(via82xx_t *chip)
1347 strcpy(pcm->name, chip->card->shortname); 1436 strcpy(pcm->name, chip->card->shortname);
1348 chip->pcms[1] = pcm; 1437 chip->pcms[1] = pcm;
1349 /* set up playback */ 1438 /* set up playback */
1350 init_viadev(chip, chip->playback_devno, 0x30, 0); 1439 init_viadev(chip, chip->playback_devno, 0x30, 3, 0);
1351 1440
1352 if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, 1441 if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
1353 snd_dma_pci_data(chip->pci), 64*1024, 128*1024)) < 0) 1442 snd_dma_pci_data(chip->pci), 64*1024, 128*1024)) < 0)
@@ -1377,8 +1466,8 @@ static int __devinit snd_via686_pcm_new(via82xx_t *chip)
1377 pcm->private_data = chip; 1466 pcm->private_data = chip;
1378 strcpy(pcm->name, chip->card->shortname); 1467 strcpy(pcm->name, chip->card->shortname);
1379 chip->pcms[0] = pcm; 1468 chip->pcms[0] = pcm;
1380 init_viadev(chip, 0, VIA_REG_PLAYBACK_STATUS, 0); 1469 init_viadev(chip, 0, VIA_REG_PLAYBACK_STATUS, 0, 0);
1381 init_viadev(chip, 1, VIA_REG_CAPTURE_STATUS, 1); 1470 init_viadev(chip, 1, VIA_REG_CAPTURE_STATUS, 0, 1);
1382 1471
1383 if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, 1472 if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
1384 snd_dma_pci_data(chip->pci), 64*1024, 128*1024)) < 0) 1473 snd_dma_pci_data(chip->pci), 64*1024, 128*1024)) < 0)
@@ -2134,7 +2223,10 @@ static int __devinit snd_via82xx_create(snd_card_t * card,
2134 return err; 2223 return err;
2135 } 2224 }
2136 chip->port = pci_resource_start(pci, 0); 2225 chip->port = pci_resource_start(pci, 0);
2137 if (request_irq(pci->irq, snd_via82xx_interrupt, SA_INTERRUPT|SA_SHIRQ, 2226 if (request_irq(pci->irq,
2227 chip_type == TYPE_VIA8233 ?
2228 snd_via8233_interrupt : snd_via686_interrupt,
2229 SA_INTERRUPT|SA_SHIRQ,
2138 card->driver, (void *)chip)) { 2230 card->driver, (void *)chip)) {
2139 snd_printk("unable to grab IRQ %d\n", pci->irq); 2231 snd_printk("unable to grab IRQ %d\n", pci->irq);
2140 snd_via82xx_free(chip); 2232 snd_via82xx_free(chip);