]>
Commit | Line | Data |
---|---|---|
2cb7cef9 BS |
1 | From: Takashi Iwai <tiwai@suse.de> |
2 | Subject: ALSA: hda - Fix DMA pointer calculation on VIA chips | |
3 | Patch-mainline: 2.6.28-rc1 | |
4 | References: | |
5 | ||
6 | Add a workaround to calculate the DMA position on VIA chips more robustly. | |
7 | ||
8 | Signed-off-by: Takashi Iwai <tiwai@suse.de> | |
9 | ||
10 | --- | |
11 | --- | |
12 | sound/pci/hda/hda_intel.c | 95 ++++++++++++++++++++++++++++++++++++++++++++-- | |
13 | 1 file changed, 91 insertions(+), 4 deletions(-) | |
14 | ||
15 | --- a/sound/pci/hda/hda_intel.c | |
16 | +++ b/sound/pci/hda/hda_intel.c | |
17 | @@ -287,6 +287,11 @@ enum { | |
18 | #define INTEL_SCH_HDA_DEVC 0x78 | |
19 | #define INTEL_SCH_HDA_DEVC_NOSNOOP (0x1<<11) | |
20 | ||
21 | +/* Define IN stream 0 FIFO size offset in VIA controller */ | |
22 | +#define VIA_IN_STREAM0_FIFO_SIZE_OFFSET 0x90 | |
23 | +/* Define VIA HD Audio Device ID*/ | |
24 | +#define VIA_HDAC_DEVICE_ID 0x3288 | |
25 | + | |
26 | ||
27 | /* | |
28 | */ | |
29 | @@ -318,6 +323,12 @@ struct azx_dev { | |
30 | unsigned int running :1; | |
31 | unsigned int irq_pending :1; | |
32 | unsigned int irq_ignore :1; | |
33 | + /* | |
34 | + * For VIA: | |
35 | + * A flag to ensure DMA position is 0 | |
36 | + * when link position is not greater than FIFO size | |
37 | + */ | |
38 | + unsigned int insufficient :1; | |
39 | }; | |
40 | ||
41 | /* CORB/RIRB */ | |
42 | @@ -380,6 +391,7 @@ struct azx { | |
43 | unsigned int polling_mode :1; | |
44 | unsigned int msi :1; | |
45 | unsigned int irq_pending_warned :1; | |
46 | + unsigned int via_dmapos_patch :1; /* enable DMA-position fix for VIA */ | |
47 | ||
48 | /* for debugging */ | |
49 | unsigned int last_cmd; /* last issued command (to sync) */ | |
50 | @@ -823,6 +835,11 @@ static void azx_int_clear(struct azx *ch | |
51 | /* start a stream */ | |
52 | static void azx_stream_start(struct azx *chip, struct azx_dev *azx_dev) | |
53 | { | |
54 | + /* | |
55 | + * Before stream start, initialize parameter | |
56 | + */ | |
57 | + azx_dev->insufficient = 1; | |
58 | + | |
59 | /* enable SIE */ | |
60 | azx_writeb(chip, INTCTL, | |
61 | azx_readb(chip, INTCTL) | (1 << azx_dev->index)); | |
62 | @@ -1156,7 +1173,8 @@ static int azx_setup_controller(struct a | |
63 | ||
64 | /* enable the position buffer */ | |
65 | if (chip->position_fix == POS_FIX_POSBUF || | |
66 | - chip->position_fix == POS_FIX_AUTO) { | |
67 | + chip->position_fix == POS_FIX_AUTO || | |
68 | + chip->via_dmapos_patch) { | |
69 | if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE)) | |
70 | azx_writel(chip, DPLBASE, | |
71 | (u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE); | |
72 | @@ -1524,13 +1542,71 @@ static int azx_pcm_trigger(struct snd_pc | |
73 | return 0; | |
74 | } | |
75 | ||
76 | +/* get the current DMA position with correction on VIA chips */ | |
77 | +static unsigned int azx_via_get_position(struct azx *chip, | |
78 | + struct azx_dev *azx_dev) | |
79 | +{ | |
80 | + unsigned int link_pos, mini_pos, bound_pos; | |
81 | + unsigned int mod_link_pos, mod_dma_pos, mod_mini_pos; | |
82 | + unsigned int fifo_size; | |
83 | + | |
84 | + link_pos = azx_sd_readl(azx_dev, SD_LPIB); | |
85 | + if (azx_dev->index >= 4) { | |
86 | + /* Playback, no problem using link position */ | |
87 | + return link_pos; | |
88 | + } | |
89 | + | |
90 | + /* Capture */ | |
91 | + /* For new chipset, | |
92 | + * use mod to get the DMA position just like old chipset | |
93 | + */ | |
94 | + mod_dma_pos = le32_to_cpu(*azx_dev->posbuf); | |
95 | + mod_dma_pos %= azx_dev->period_bytes; | |
96 | + | |
97 | + /* azx_dev->fifo_size can't get FIFO size of in stream. | |
98 | + * Get from base address + offset. | |
99 | + */ | |
100 | + fifo_size = readw(chip->remap_addr + VIA_IN_STREAM0_FIFO_SIZE_OFFSET); | |
101 | + | |
102 | + if (azx_dev->insufficient) { | |
103 | + /* Link position never gather than FIFO size */ | |
104 | + if (link_pos <= fifo_size) | |
105 | + return 0; | |
106 | + | |
107 | + azx_dev->insufficient = 0; | |
108 | + } | |
109 | + | |
110 | + if (link_pos <= fifo_size) | |
111 | + mini_pos = azx_dev->bufsize + link_pos - fifo_size; | |
112 | + else | |
113 | + mini_pos = link_pos - fifo_size; | |
114 | + | |
115 | + /* Find nearest previous boudary */ | |
116 | + mod_mini_pos = mini_pos % azx_dev->period_bytes; | |
117 | + mod_link_pos = link_pos % azx_dev->period_bytes; | |
118 | + if (mod_link_pos >= fifo_size) | |
119 | + bound_pos = link_pos - mod_link_pos; | |
120 | + else if (mod_dma_pos >= mod_mini_pos) | |
121 | + bound_pos = mini_pos - mod_mini_pos; | |
122 | + else { | |
123 | + bound_pos = mini_pos - mod_mini_pos + azx_dev->period_bytes; | |
124 | + if (bound_pos >= azx_dev->bufsize) | |
125 | + bound_pos = 0; | |
126 | + } | |
127 | + | |
128 | + /* Calculate real DMA position we want */ | |
129 | + return bound_pos + mod_dma_pos; | |
130 | +} | |
131 | + | |
132 | static unsigned int azx_get_position(struct azx *chip, | |
133 | struct azx_dev *azx_dev) | |
134 | { | |
135 | unsigned int pos; | |
136 | ||
137 | - if (chip->position_fix == POS_FIX_POSBUF || | |
138 | - chip->position_fix == POS_FIX_AUTO) { | |
139 | + if (chip->via_dmapos_patch) | |
140 | + pos = azx_via_get_position(chip, azx_dev); | |
141 | + else if (chip->position_fix == POS_FIX_POSBUF || | |
142 | + chip->position_fix == POS_FIX_AUTO) { | |
143 | /* use the position buffer */ | |
144 | pos = le32_to_cpu(*azx_dev->posbuf); | |
145 | } else { | |
146 | @@ -1576,6 +1652,8 @@ static int azx_position_ok(struct azx *c | |
147 | chip->position_fix = POS_FIX_POSBUF; | |
148 | } | |
149 | ||
150 | + if (!bdl_pos_adj[chip->dev_index]) | |
151 | + return 1; /* no delayed ack */ | |
152 | if (pos % azx_dev->period_bytes > azx_dev->period_bytes / 2) | |
153 | return 0; /* NG - it's below the period boundary */ | |
154 | return 1; /* OK, it's fine */ | |
155 | @@ -1687,7 +1765,7 @@ static int __devinit create_codec_pcm(st | |
156 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &azx_pcm_ops); | |
157 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, | |
158 | snd_dma_pci_data(chip->pci), | |
159 | - 1024 * 64, 1024 * 1024); | |
160 | + 1024 * 64, 32 * 1024 * 1024); | |
161 | chip->pcm[cpcm->device] = pcm; | |
162 | return 0; | |
163 | } | |
164 | @@ -1987,6 +2065,15 @@ static int __devinit check_position_fix( | |
165 | { | |
166 | const struct snd_pci_quirk *q; | |
167 | ||
168 | + /* Check VIA HD Audio Controller exist */ | |
169 | + if (chip->pci->vendor == PCI_VENDOR_ID_VIA && | |
170 | + chip->pci->device == VIA_HDAC_DEVICE_ID) { | |
171 | + chip->via_dmapos_patch = 1; | |
172 | + /* Use link position directly, avoid any transfer problem. */ | |
173 | + return POS_FIX_LPIB; | |
174 | + } | |
175 | + chip->via_dmapos_patch = 0; | |
176 | + | |
177 | if (fix == POS_FIX_AUTO) { | |
178 | q = snd_pci_quirk_lookup(chip->pci, position_fix_list); | |
179 | if (q) { |