diff options
| -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 |
