--- /dev/null
+From: Takashi Iwai <tiwai@suse.de>
+Subject: ALSA: hda - Fix DMA pointer calculation on VIA chips
+Patch-mainline: 2.6.28-rc1
+References:
+
+Add a workaround to calculate the DMA position on VIA chips more robustly.
+
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+
+---
+---
+ sound/pci/hda/hda_intel.c | 95 ++++++++++++++++++++++++++++++++++++++++++++--
+ 1 file changed, 91 insertions(+), 4 deletions(-)
+
+--- a/sound/pci/hda/hda_intel.c
++++ b/sound/pci/hda/hda_intel.c
+@@ -287,6 +287,11 @@ enum {
+ #define INTEL_SCH_HDA_DEVC 0x78
+ #define INTEL_SCH_HDA_DEVC_NOSNOOP (0x1<<11)
+
++/* Define IN stream 0 FIFO size offset in VIA controller */
++#define VIA_IN_STREAM0_FIFO_SIZE_OFFSET 0x90
++/* Define VIA HD Audio Device ID*/
++#define VIA_HDAC_DEVICE_ID 0x3288
++
+
+ /*
+ */
+@@ -318,6 +323,12 @@ struct azx_dev {
+ unsigned int running :1;
+ unsigned int irq_pending :1;
+ unsigned int irq_ignore :1;
++ /*
++ * For VIA:
++ * A flag to ensure DMA position is 0
++ * when link position is not greater than FIFO size
++ */
++ unsigned int insufficient :1;
+ };
+
+ /* CORB/RIRB */
+@@ -380,6 +391,7 @@ struct azx {
+ unsigned int polling_mode :1;
+ unsigned int msi :1;
+ unsigned int irq_pending_warned :1;
++ unsigned int via_dmapos_patch :1; /* enable DMA-position fix for VIA */
+
+ /* for debugging */
+ unsigned int last_cmd; /* last issued command (to sync) */
+@@ -823,6 +835,11 @@ static void azx_int_clear(struct azx *ch
+ /* start a stream */
+ static void azx_stream_start(struct azx *chip, struct azx_dev *azx_dev)
+ {
++ /*
++ * Before stream start, initialize parameter
++ */
++ azx_dev->insufficient = 1;
++
+ /* enable SIE */
+ azx_writeb(chip, INTCTL,
+ azx_readb(chip, INTCTL) | (1 << azx_dev->index));
+@@ -1156,7 +1173,8 @@ static int azx_setup_controller(struct a
+
+ /* enable the position buffer */
+ if (chip->position_fix == POS_FIX_POSBUF ||
+- chip->position_fix == POS_FIX_AUTO) {
++ chip->position_fix == POS_FIX_AUTO ||
++ chip->via_dmapos_patch) {
+ if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE))
+ azx_writel(chip, DPLBASE,
+ (u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE);
+@@ -1524,13 +1542,71 @@ static int azx_pcm_trigger(struct snd_pc
+ return 0;
+ }
+
++/* get the current DMA position with correction on VIA chips */
++static unsigned int azx_via_get_position(struct azx *chip,
++ struct azx_dev *azx_dev)
++{
++ unsigned int link_pos, mini_pos, bound_pos;
++ unsigned int mod_link_pos, mod_dma_pos, mod_mini_pos;
++ unsigned int fifo_size;
++
++ link_pos = azx_sd_readl(azx_dev, SD_LPIB);
++ if (azx_dev->index >= 4) {
++ /* Playback, no problem using link position */
++ return link_pos;
++ }
++
++ /* Capture */
++ /* For new chipset,
++ * use mod to get the DMA position just like old chipset
++ */
++ mod_dma_pos = le32_to_cpu(*azx_dev->posbuf);
++ mod_dma_pos %= azx_dev->period_bytes;
++
++ /* azx_dev->fifo_size can't get FIFO size of in stream.
++ * Get from base address + offset.
++ */
++ fifo_size = readw(chip->remap_addr + VIA_IN_STREAM0_FIFO_SIZE_OFFSET);
++
++ if (azx_dev->insufficient) {
++ /* Link position never gather than FIFO size */
++ if (link_pos <= fifo_size)
++ return 0;
++
++ azx_dev->insufficient = 0;
++ }
++
++ if (link_pos <= fifo_size)
++ mini_pos = azx_dev->bufsize + link_pos - fifo_size;
++ else
++ mini_pos = link_pos - fifo_size;
++
++ /* Find nearest previous boudary */
++ mod_mini_pos = mini_pos % azx_dev->period_bytes;
++ mod_link_pos = link_pos % azx_dev->period_bytes;
++ if (mod_link_pos >= fifo_size)
++ bound_pos = link_pos - mod_link_pos;
++ else if (mod_dma_pos >= mod_mini_pos)
++ bound_pos = mini_pos - mod_mini_pos;
++ else {
++ bound_pos = mini_pos - mod_mini_pos + azx_dev->period_bytes;
++ if (bound_pos >= azx_dev->bufsize)
++ bound_pos = 0;
++ }
++
++ /* Calculate real DMA position we want */
++ return bound_pos + mod_dma_pos;
++}
++
+ static unsigned int azx_get_position(struct azx *chip,
+ struct azx_dev *azx_dev)
+ {
+ unsigned int pos;
+
+- if (chip->position_fix == POS_FIX_POSBUF ||
+- chip->position_fix == POS_FIX_AUTO) {
++ if (chip->via_dmapos_patch)
++ pos = azx_via_get_position(chip, azx_dev);
++ else if (chip->position_fix == POS_FIX_POSBUF ||
++ chip->position_fix == POS_FIX_AUTO) {
+ /* use the position buffer */
+ pos = le32_to_cpu(*azx_dev->posbuf);
+ } else {
+@@ -1576,6 +1652,8 @@ static int azx_position_ok(struct azx *c
+ chip->position_fix = POS_FIX_POSBUF;
+ }
+
++ if (!bdl_pos_adj[chip->dev_index])
++ return 1; /* no delayed ack */
+ if (pos % azx_dev->period_bytes > azx_dev->period_bytes / 2)
+ return 0; /* NG - it's below the period boundary */
+ return 1; /* OK, it's fine */
+@@ -1687,7 +1765,7 @@ static int __devinit create_codec_pcm(st
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &azx_pcm_ops);
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ snd_dma_pci_data(chip->pci),
+- 1024 * 64, 1024 * 1024);
++ 1024 * 64, 32 * 1024 * 1024);
+ chip->pcm[cpcm->device] = pcm;
+ return 0;
+ }
+@@ -1987,6 +2065,15 @@ static int __devinit check_position_fix(
+ {
+ const struct snd_pci_quirk *q;
+
++ /* Check VIA HD Audio Controller exist */
++ if (chip->pci->vendor == PCI_VENDOR_ID_VIA &&
++ chip->pci->device == VIA_HDAC_DEVICE_ID) {
++ chip->via_dmapos_patch = 1;
++ /* Use link position directly, avoid any transfer problem. */
++ return POS_FIX_LPIB;
++ }
++ chip->via_dmapos_patch = 0;
++
+ if (fix == POS_FIX_AUTO) {
+ q = snd_pci_quirk_lookup(chip->pci, position_fix_list);
+ if (q) {