diff options
author | Manuel Lauss <manuel.lauss@googlemail.com> | 2009-09-08 13:45:17 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2009-09-08 14:21:27 -0400 |
commit | cdc65fbe18aef15e92d2ebb410a189fbf956fb06 (patch) | |
tree | d121aea788bf52467683a9072ff3d15b89bd5c08 /sound/soc/au1x | |
parent | 87831cb660954356d68cebdb1406f3be09e784e9 (diff) |
ASoC: au1x: PSC-AC97 bugfixes
This patch fixes the following bugs:
- only reprogram bitdepth if it has changed since last call to hw_params.
- add locking inside ac97_read/write functions:
When reprogramming sample depth, the ac97 unit has to be disabled,
which should not be done in the middle of codec register accesses.
- retry timed-out codec register accesses.
- wait for status bits to set/clear when starting/stopping various
functional blocks; very important after reenabling AC97 unit else
sound may be distorted (e.g. high-pitch noise in 1kHz sine wave).
- clear fifos before/after starting/stopping RX/TX.
- longer timeouts waiting for PSC/AC97 ready after cold reset
with certain codecs this can take ridiculous amounts of time.
Run-tested on various Au1200 platforms with various codecs.
Signed-off-by: Manuel Lauss <manuel.lauss@gmail.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/au1x')
-rw-r--r-- | sound/soc/au1x/psc-ac97.c | 129 | ||||
-rw-r--r-- | sound/soc/au1x/psc.h | 1 |
2 files changed, 97 insertions, 33 deletions
diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c index 479d7bdf1865..a521aa90ddee 100644 --- a/sound/soc/au1x/psc-ac97.c +++ b/sound/soc/au1x/psc-ac97.c | |||
@@ -1,8 +1,8 @@ | |||
1 | /* | 1 | /* |
2 | * Au12x0/Au1550 PSC ALSA ASoC audio support. | 2 | * Au12x0/Au1550 PSC ALSA ASoC audio support. |
3 | * | 3 | * |
4 | * (c) 2007-2008 MSC Vertriebsges.m.b.H., | 4 | * (c) 2007-2009 MSC Vertriebsges.m.b.H., |
5 | * Manuel Lauss <mano@roarinelk.homelinux.net> | 5 | * Manuel Lauss <manuel.lauss@gmail.com> |
6 | * | 6 | * |
7 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License version 2 as | 8 | * it under the terms of the GNU General Public License version 2 as |
@@ -19,6 +19,7 @@ | |||
19 | #include <linux/module.h> | 19 | #include <linux/module.h> |
20 | #include <linux/device.h> | 20 | #include <linux/device.h> |
21 | #include <linux/delay.h> | 21 | #include <linux/delay.h> |
22 | #include <linux/mutex.h> | ||
22 | #include <linux/suspend.h> | 23 | #include <linux/suspend.h> |
23 | #include <sound/core.h> | 24 | #include <sound/core.h> |
24 | #include <sound/pcm.h> | 25 | #include <sound/pcm.h> |
@@ -29,6 +30,9 @@ | |||
29 | 30 | ||
30 | #include "psc.h" | 31 | #include "psc.h" |
31 | 32 | ||
33 | /* how often to retry failed codec register reads/writes */ | ||
34 | #define AC97_RW_RETRIES 5 | ||
35 | |||
32 | #define AC97_DIR \ | 36 | #define AC97_DIR \ |
33 | (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE) | 37 | (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE) |
34 | 38 | ||
@@ -45,6 +49,9 @@ | |||
45 | #define AC97PCR_CLRFIFO(stype) \ | 49 | #define AC97PCR_CLRFIFO(stype) \ |
46 | ((stype) == PCM_TX ? PSC_AC97PCR_TC : PSC_AC97PCR_RC) | 50 | ((stype) == PCM_TX ? PSC_AC97PCR_TC : PSC_AC97PCR_RC) |
47 | 51 | ||
52 | #define AC97STAT_BUSY(stype) \ | ||
53 | ((stype) == PCM_TX ? PSC_AC97STAT_TB : PSC_AC97STAT_RB) | ||
54 | |||
48 | /* instance data. There can be only one, MacLeod!!!! */ | 55 | /* instance data. There can be only one, MacLeod!!!! */ |
49 | static struct au1xpsc_audio_data *au1xpsc_ac97_workdata; | 56 | static struct au1xpsc_audio_data *au1xpsc_ac97_workdata; |
50 | 57 | ||
@@ -54,24 +61,33 @@ static unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97, | |||
54 | { | 61 | { |
55 | /* FIXME */ | 62 | /* FIXME */ |
56 | struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata; | 63 | struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata; |
57 | unsigned short data, tmo; | 64 | unsigned short data, retry, tmo; |
58 | 65 | ||
59 | au_writel(PSC_AC97CDC_RD | PSC_AC97CDC_INDX(reg), AC97_CDC(pscdata)); | 66 | au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata)); |
60 | au_sync(); | 67 | au_sync(); |
61 | 68 | ||
62 | tmo = 1000; | 69 | retry = AC97_RW_RETRIES; |
63 | while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) && --tmo) | 70 | do { |
64 | udelay(2); | 71 | mutex_lock(&pscdata->lock); |
72 | |||
73 | au_writel(PSC_AC97CDC_RD | PSC_AC97CDC_INDX(reg), | ||
74 | AC97_CDC(pscdata)); | ||
75 | au_sync(); | ||
76 | |||
77 | tmo = 2000; | ||
78 | while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) | ||
79 | && --tmo) | ||
80 | udelay(2); | ||
65 | 81 | ||
66 | if (!tmo) | ||
67 | data = 0xffff; | ||
68 | else | ||
69 | data = au_readl(AC97_CDC(pscdata)) & 0xffff; | 82 | data = au_readl(AC97_CDC(pscdata)) & 0xffff; |
70 | 83 | ||
71 | au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata)); | 84 | au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata)); |
72 | au_sync(); | 85 | au_sync(); |
86 | |||
87 | mutex_unlock(&pscdata->lock); | ||
88 | } while (--retry && !tmo); | ||
73 | 89 | ||
74 | return data; | 90 | return retry ? data : 0xffff; |
75 | } | 91 | } |
76 | 92 | ||
77 | /* AC97 controller writes to codec register */ | 93 | /* AC97 controller writes to codec register */ |
@@ -80,16 +96,29 @@ static void au1xpsc_ac97_write(struct snd_ac97 *ac97, unsigned short reg, | |||
80 | { | 96 | { |
81 | /* FIXME */ | 97 | /* FIXME */ |
82 | struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata; | 98 | struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata; |
83 | unsigned int tmo; | 99 | unsigned int tmo, retry; |
84 | 100 | ||
85 | au_writel(PSC_AC97CDC_INDX(reg) | (val & 0xffff), AC97_CDC(pscdata)); | 101 | au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata)); |
86 | au_sync(); | 102 | au_sync(); |
87 | tmo = 1000; | 103 | |
88 | while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) && --tmo) | 104 | retry = AC97_RW_RETRIES; |
105 | do { | ||
106 | mutex_lock(&pscdata->lock); | ||
107 | |||
108 | au_writel(PSC_AC97CDC_INDX(reg) | (val & 0xffff), | ||
109 | AC97_CDC(pscdata)); | ||
89 | au_sync(); | 110 | au_sync(); |
90 | 111 | ||
91 | au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata)); | 112 | tmo = 2000; |
92 | au_sync(); | 113 | while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) |
114 | && --tmo) | ||
115 | udelay(2); | ||
116 | |||
117 | au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata)); | ||
118 | au_sync(); | ||
119 | |||
120 | mutex_unlock(&pscdata->lock); | ||
121 | } while (--retry && !tmo); | ||
93 | } | 122 | } |
94 | 123 | ||
95 | /* AC97 controller asserts a warm reset */ | 124 | /* AC97 controller asserts a warm reset */ |
@@ -129,9 +158,9 @@ static void au1xpsc_ac97_cold_reset(struct snd_ac97 *ac97) | |||
129 | au_sync(); | 158 | au_sync(); |
130 | 159 | ||
131 | /* wait for PSC to indicate it's ready */ | 160 | /* wait for PSC to indicate it's ready */ |
132 | i = 100000; | 161 | i = 1000; |
133 | while (!((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_SR)) && (--i)) | 162 | while (!((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_SR)) && (--i)) |
134 | au_sync(); | 163 | msleep(1); |
135 | 164 | ||
136 | if (i == 0) { | 165 | if (i == 0) { |
137 | printk(KERN_ERR "au1xpsc-ac97: PSC not ready!\n"); | 166 | printk(KERN_ERR "au1xpsc-ac97: PSC not ready!\n"); |
@@ -143,9 +172,9 @@ static void au1xpsc_ac97_cold_reset(struct snd_ac97 *ac97) | |||
143 | au_sync(); | 172 | au_sync(); |
144 | 173 | ||
145 | /* wait for AC97 core to become ready */ | 174 | /* wait for AC97 core to become ready */ |
146 | i = 100000; | 175 | i = 1000; |
147 | while (!((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) && (--i)) | 176 | while (!((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) && (--i)) |
148 | au_sync(); | 177 | msleep(1); |
149 | if (i == 0) | 178 | if (i == 0) |
150 | printk(KERN_ERR "au1xpsc-ac97: AC97 ctrl not ready\n"); | 179 | printk(KERN_ERR "au1xpsc-ac97: AC97 ctrl not ready\n"); |
151 | } | 180 | } |
@@ -165,12 +194,12 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream, | |||
165 | { | 194 | { |
166 | /* FIXME */ | 195 | /* FIXME */ |
167 | struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata; | 196 | struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata; |
168 | unsigned long r, stat; | 197 | unsigned long r, ro, stat; |
169 | int chans, stype = SUBSTREAM_TYPE(substream); | 198 | int chans, stype = SUBSTREAM_TYPE(substream); |
170 | 199 | ||
171 | chans = params_channels(params); | 200 | chans = params_channels(params); |
172 | 201 | ||
173 | r = au_readl(AC97_CFG(pscdata)); | 202 | r = ro = au_readl(AC97_CFG(pscdata)); |
174 | stat = au_readl(AC97_STAT(pscdata)); | 203 | stat = au_readl(AC97_STAT(pscdata)); |
175 | 204 | ||
176 | /* already active? */ | 205 | /* already active? */ |
@@ -180,9 +209,6 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream, | |||
180 | (pscdata->rate != params_rate(params))) | 209 | (pscdata->rate != params_rate(params))) |
181 | return -EINVAL; | 210 | return -EINVAL; |
182 | } else { | 211 | } else { |
183 | /* disable AC97 device controller first */ | ||
184 | au_writel(r & ~PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata)); | ||
185 | au_sync(); | ||
186 | 212 | ||
187 | /* set sample bitdepth: REG[24:21]=(BITS-2)/2 */ | 213 | /* set sample bitdepth: REG[24:21]=(BITS-2)/2 */ |
188 | r &= ~PSC_AC97CFG_LEN_MASK; | 214 | r &= ~PSC_AC97CFG_LEN_MASK; |
@@ -199,14 +225,40 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream, | |||
199 | r |= PSC_AC97CFG_RXSLOT_ENA(4); | 225 | r |= PSC_AC97CFG_RXSLOT_ENA(4); |
200 | } | 226 | } |
201 | 227 | ||
202 | /* finally enable the AC97 controller again */ | 228 | /* do we need to poke the hardware? */ |
229 | if (!(r ^ ro)) | ||
230 | goto out; | ||
231 | |||
232 | /* ac97 engine is about to be disabled */ | ||
233 | mutex_lock(&pscdata->lock); | ||
234 | |||
235 | /* disable AC97 device controller first... */ | ||
236 | au_writel(r & ~PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata)); | ||
237 | au_sync(); | ||
238 | |||
239 | /* ...wait for it... */ | ||
240 | while (au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR) | ||
241 | asm volatile ("nop"); | ||
242 | |||
243 | /* ...write config... */ | ||
244 | au_writel(r, AC97_CFG(pscdata)); | ||
245 | au_sync(); | ||
246 | |||
247 | /* ...enable the AC97 controller again... */ | ||
203 | au_writel(r | PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata)); | 248 | au_writel(r | PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata)); |
204 | au_sync(); | 249 | au_sync(); |
205 | 250 | ||
251 | /* ...and wait for ready bit */ | ||
252 | while (!(au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) | ||
253 | asm volatile ("nop"); | ||
254 | |||
255 | mutex_unlock(&pscdata->lock); | ||
256 | |||
206 | pscdata->cfg = r; | 257 | pscdata->cfg = r; |
207 | pscdata->rate = params_rate(params); | 258 | pscdata->rate = params_rate(params); |
208 | } | 259 | } |
209 | 260 | ||
261 | out: | ||
210 | return 0; | 262 | return 0; |
211 | } | 263 | } |
212 | 264 | ||
@@ -222,6 +274,8 @@ static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream, | |||
222 | switch (cmd) { | 274 | switch (cmd) { |
223 | case SNDRV_PCM_TRIGGER_START: | 275 | case SNDRV_PCM_TRIGGER_START: |
224 | case SNDRV_PCM_TRIGGER_RESUME: | 276 | case SNDRV_PCM_TRIGGER_RESUME: |
277 | au_writel(AC97PCR_CLRFIFO(stype), AC97_PCR(pscdata)); | ||
278 | au_sync(); | ||
225 | au_writel(AC97PCR_START(stype), AC97_PCR(pscdata)); | 279 | au_writel(AC97PCR_START(stype), AC97_PCR(pscdata)); |
226 | au_sync(); | 280 | au_sync(); |
227 | break; | 281 | break; |
@@ -229,6 +283,13 @@ static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream, | |||
229 | case SNDRV_PCM_TRIGGER_SUSPEND: | 283 | case SNDRV_PCM_TRIGGER_SUSPEND: |
230 | au_writel(AC97PCR_STOP(stype), AC97_PCR(pscdata)); | 284 | au_writel(AC97PCR_STOP(stype), AC97_PCR(pscdata)); |
231 | au_sync(); | 285 | au_sync(); |
286 | |||
287 | while (au_readl(AC97_STAT(pscdata)) & AC97STAT_BUSY(stype)) | ||
288 | asm volatile ("nop"); | ||
289 | |||
290 | au_writel(AC97PCR_CLRFIFO(stype), AC97_PCR(pscdata)); | ||
291 | au_sync(); | ||
292 | |||
232 | break; | 293 | break; |
233 | default: | 294 | default: |
234 | ret = -EINVAL; | 295 | ret = -EINVAL; |
@@ -251,6 +312,8 @@ static int au1xpsc_ac97_probe(struct platform_device *pdev, | |||
251 | if (!au1xpsc_ac97_workdata) | 312 | if (!au1xpsc_ac97_workdata) |
252 | return -ENOMEM; | 313 | return -ENOMEM; |
253 | 314 | ||
315 | mutex_init(&au1xpsc_ac97_workdata->lock); | ||
316 | |||
254 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 317 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
255 | if (!r) { | 318 | if (!r) { |
256 | ret = -ENODEV; | 319 | ret = -ENODEV; |
@@ -269,9 +332,9 @@ static int au1xpsc_ac97_probe(struct platform_device *pdev, | |||
269 | goto out1; | 332 | goto out1; |
270 | 333 | ||
271 | /* configuration: max dma trigger threshold, enable ac97 */ | 334 | /* configuration: max dma trigger threshold, enable ac97 */ |
272 | au1xpsc_ac97_workdata->cfg = PSC_AC97CFG_RT_FIFO8 | | 335 | au1xpsc_ac97_workdata->cfg = PSC_AC97CFG_RT_FIFO8 | |
273 | PSC_AC97CFG_TT_FIFO8 | | 336 | PSC_AC97CFG_TT_FIFO8 | |
274 | PSC_AC97CFG_DE_ENABLE; | 337 | PSC_AC97CFG_DE_ENABLE; |
275 | 338 | ||
276 | /* preserve PSC clock source set up by platform (dev.platform_data | 339 | /* preserve PSC clock source set up by platform (dev.platform_data |
277 | * is already occupied by soc layer) | 340 | * is already occupied by soc layer) |
@@ -386,4 +449,4 @@ module_exit(au1xpsc_ac97_exit); | |||
386 | 449 | ||
387 | MODULE_LICENSE("GPL"); | 450 | MODULE_LICENSE("GPL"); |
388 | MODULE_DESCRIPTION("Au12x0/Au1550 PSC AC97 ALSA ASoC audio driver"); | 451 | MODULE_DESCRIPTION("Au12x0/Au1550 PSC AC97 ALSA ASoC audio driver"); |
389 | MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>"); | 452 | MODULE_AUTHOR("Manuel Lauss <manuel.lauss@gmail.com>"); |
diff --git a/sound/soc/au1x/psc.h b/sound/soc/au1x/psc.h index 8fdb1a04a07b..3f474e8ed4f6 100644 --- a/sound/soc/au1x/psc.h +++ b/sound/soc/au1x/psc.h | |||
@@ -29,6 +29,7 @@ struct au1xpsc_audio_data { | |||
29 | 29 | ||
30 | unsigned long pm[2]; | 30 | unsigned long pm[2]; |
31 | struct resource *ioarea; | 31 | struct resource *ioarea; |
32 | struct mutex lock; | ||
32 | }; | 33 | }; |
33 | 34 | ||
34 | #define PCM_TX 0 | 35 | #define PCM_TX 0 |