diff options
author | Joseph Chan <josephchan@via.com.tw> | 2008-08-26 08:38:03 -0400 |
---|---|---|
committer | Jaroslav Kysela <perex@perex.cz> | 2008-08-29 04:06:12 -0400 |
commit | 0e153474f47aee906107a5c1da0ae83553e5ba6a (patch) | |
tree | c9aaf92d3b847a5212549cdb01dcfdbbd7317e7a /sound/pci/hda/hda_intel.c | |
parent | acdfc9e305b8320c5dee714a08be022441b58c44 (diff) |
ALSA: hda - Fix VIA recording problem
Add a workaround for bad DMA-position reporting on VIA chipset.
Signed-off-by: Joseph Chan <josephchan@via.com.tw>
[modified and cleaned up by tiwai]
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
Diffstat (limited to 'sound/pci/hda/hda_intel.c')
-rw-r--r-- | sound/pci/hda/hda_intel.c | 91 |
1 files changed, 88 insertions, 3 deletions
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 4f80248837e..52a3adfac7f 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c | |||
@@ -286,6 +286,11 @@ enum { | |||
286 | #define INTEL_SCH_HDA_DEVC 0x78 | 286 | #define INTEL_SCH_HDA_DEVC 0x78 |
287 | #define INTEL_SCH_HDA_DEVC_NOSNOOP (0x1<<11) | 287 | #define INTEL_SCH_HDA_DEVC_NOSNOOP (0x1<<11) |
288 | 288 | ||
289 | /* Define IN stream 0 FIFO size offset in VIA controller */ | ||
290 | #define VIA_IN_STREAM0_FIFO_SIZE_OFFSET 0x90 | ||
291 | /* Define VIA HD Audio Device ID*/ | ||
292 | #define VIA_HDAC_DEVICE_ID 0x3288 | ||
293 | |||
289 | 294 | ||
290 | /* | 295 | /* |
291 | */ | 296 | */ |
@@ -317,6 +322,12 @@ struct azx_dev { | |||
317 | unsigned int running :1; | 322 | unsigned int running :1; |
318 | unsigned int irq_pending :1; | 323 | unsigned int irq_pending :1; |
319 | unsigned int irq_ignore :1; | 324 | unsigned int irq_ignore :1; |
325 | /* | ||
326 | * For VIA: | ||
327 | * A flag to ensure DMA position is 0 | ||
328 | * when link position is not greater than FIFO size | ||
329 | */ | ||
330 | unsigned int insufficient :1; | ||
320 | }; | 331 | }; |
321 | 332 | ||
322 | /* CORB/RIRB */ | 333 | /* CORB/RIRB */ |
@@ -379,6 +390,7 @@ struct azx { | |||
379 | unsigned int polling_mode :1; | 390 | unsigned int polling_mode :1; |
380 | unsigned int msi :1; | 391 | unsigned int msi :1; |
381 | unsigned int irq_pending_warned :1; | 392 | unsigned int irq_pending_warned :1; |
393 | unsigned int via_dmapos_patch :1; /* enable DMA-position fix for VIA */ | ||
382 | 394 | ||
383 | /* for debugging */ | 395 | /* for debugging */ |
384 | unsigned int last_cmd; /* last issued command (to sync) */ | 396 | unsigned int last_cmd; /* last issued command (to sync) */ |
@@ -818,6 +830,11 @@ static void azx_int_clear(struct azx *chip) | |||
818 | /* start a stream */ | 830 | /* start a stream */ |
819 | static void azx_stream_start(struct azx *chip, struct azx_dev *azx_dev) | 831 | static void azx_stream_start(struct azx *chip, struct azx_dev *azx_dev) |
820 | { | 832 | { |
833 | /* | ||
834 | * Before stream start, initialize parameter | ||
835 | */ | ||
836 | azx_dev->insufficient = 1; | ||
837 | |||
821 | /* enable SIE */ | 838 | /* enable SIE */ |
822 | azx_writeb(chip, INTCTL, | 839 | azx_writeb(chip, INTCTL, |
823 | azx_readb(chip, INTCTL) | (1 << azx_dev->index)); | 840 | azx_readb(chip, INTCTL) | (1 << azx_dev->index)); |
@@ -1148,7 +1165,8 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev) | |||
1148 | 1165 | ||
1149 | /* enable the position buffer */ | 1166 | /* enable the position buffer */ |
1150 | if (chip->position_fix == POS_FIX_POSBUF || | 1167 | if (chip->position_fix == POS_FIX_POSBUF || |
1151 | chip->position_fix == POS_FIX_AUTO) { | 1168 | chip->position_fix == POS_FIX_AUTO || |
1169 | chip->via_dmapos_patch) { | ||
1152 | if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE)) | 1170 | if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE)) |
1153 | azx_writel(chip, DPLBASE, | 1171 | azx_writel(chip, DPLBASE, |
1154 | (u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE); | 1172 | (u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE); |
@@ -1504,13 +1522,71 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | |||
1504 | return 0; | 1522 | return 0; |
1505 | } | 1523 | } |
1506 | 1524 | ||
1525 | /* get the current DMA position with correction on VIA chips */ | ||
1526 | static unsigned int azx_via_get_position(struct azx *chip, | ||
1527 | struct azx_dev *azx_dev) | ||
1528 | { | ||
1529 | unsigned int link_pos, mini_pos, bound_pos; | ||
1530 | unsigned int mod_link_pos, mod_dma_pos, mod_mini_pos; | ||
1531 | unsigned int fifo_size; | ||
1532 | |||
1533 | link_pos = azx_sd_readl(azx_dev, SD_LPIB); | ||
1534 | if (azx_dev->index >= 4) { | ||
1535 | /* Playback, no problem using link position */ | ||
1536 | return link_pos; | ||
1537 | } | ||
1538 | |||
1539 | /* Capture */ | ||
1540 | /* For new chipset, | ||
1541 | * use mod to get the DMA position just like old chipset | ||
1542 | */ | ||
1543 | mod_dma_pos = le32_to_cpu(*azx_dev->posbuf); | ||
1544 | mod_dma_pos %= azx_dev->period_bytes; | ||
1545 | |||
1546 | /* azx_dev->fifo_size can't get FIFO size of in stream. | ||
1547 | * Get from base address + offset. | ||
1548 | */ | ||
1549 | fifo_size = readw(chip->remap_addr + VIA_IN_STREAM0_FIFO_SIZE_OFFSET); | ||
1550 | |||
1551 | if (azx_dev->insufficient) { | ||
1552 | /* Link position never gather than FIFO size */ | ||
1553 | if (link_pos <= fifo_size) | ||
1554 | return 0; | ||
1555 | |||
1556 | azx_dev->insufficient = 0; | ||
1557 | } | ||
1558 | |||
1559 | if (link_pos <= fifo_size) | ||
1560 | mini_pos = azx_dev->bufsize + link_pos - fifo_size; | ||
1561 | else | ||
1562 | mini_pos = link_pos - fifo_size; | ||
1563 | |||
1564 | /* Find nearest previous boudary */ | ||
1565 | mod_mini_pos = mini_pos % azx_dev->period_bytes; | ||
1566 | mod_link_pos = link_pos % azx_dev->period_bytes; | ||
1567 | if (mod_link_pos >= fifo_size) | ||
1568 | bound_pos = link_pos - mod_link_pos; | ||
1569 | else if (mod_dma_pos >= mod_mini_pos) | ||
1570 | bound_pos = mini_pos - mod_mini_pos; | ||
1571 | else { | ||
1572 | bound_pos = mini_pos - mod_mini_pos + azx_dev->period_bytes; | ||
1573 | if (bound_pos >= azx_dev->bufsize) | ||
1574 | bound_pos = 0; | ||
1575 | } | ||
1576 | |||
1577 | /* Calculate real DMA position we want */ | ||
1578 | return bound_pos + mod_dma_pos; | ||
1579 | } | ||
1580 | |||
1507 | static unsigned int azx_get_position(struct azx *chip, | 1581 | static unsigned int azx_get_position(struct azx *chip, |
1508 | struct azx_dev *azx_dev) | 1582 | struct azx_dev *azx_dev) |
1509 | { | 1583 | { |
1510 | unsigned int pos; | 1584 | unsigned int pos; |
1511 | 1585 | ||
1512 | if (chip->position_fix == POS_FIX_POSBUF || | 1586 | if (chip->via_dmapos_patch) |
1513 | chip->position_fix == POS_FIX_AUTO) { | 1587 | pos = azx_via_get_position(chip, azx_dev); |
1588 | else if (chip->position_fix == POS_FIX_POSBUF || | ||
1589 | chip->position_fix == POS_FIX_AUTO) { | ||
1514 | /* use the position buffer */ | 1590 | /* use the position buffer */ |
1515 | pos = le32_to_cpu(*azx_dev->posbuf); | 1591 | pos = le32_to_cpu(*azx_dev->posbuf); |
1516 | } else { | 1592 | } else { |
@@ -1946,6 +2022,15 @@ static int __devinit check_position_fix(struct azx *chip, int fix) | |||
1946 | { | 2022 | { |
1947 | const struct snd_pci_quirk *q; | 2023 | const struct snd_pci_quirk *q; |
1948 | 2024 | ||
2025 | /* Check VIA HD Audio Controller exist */ | ||
2026 | if (chip->pci->vendor == PCI_VENDOR_ID_VIA && | ||
2027 | chip->pci->device == VIA_HDAC_DEVICE_ID) { | ||
2028 | chip->via_dmapos_patch = 1; | ||
2029 | /* Use link position directly, avoid any transfer problem. */ | ||
2030 | return POS_FIX_LPIB; | ||
2031 | } | ||
2032 | chip->via_dmapos_patch = 0; | ||
2033 | |||
1949 | if (fix == POS_FIX_AUTO) { | 2034 | if (fix == POS_FIX_AUTO) { |
1950 | q = snd_pci_quirk_lookup(chip->pci, position_fix_list); | 2035 | q = snd_pci_quirk_lookup(chip->pci, position_fix_list); |
1951 | if (q) { | 2036 | if (q) { |