Commit | Line | Data |
---|---|---|
b3c70c9e ML |
1 | /* |
2 | * Au1000/Au1500/Au1100 AC97C controller driver for ASoC | |
3 | * | |
4 | * (c) 2011 Manuel Lauss <manuel.lauss@googlemail.com> | |
5 | * | |
6 | * based on the old ALSA driver originally written by | |
7 | * Charles Eidsness <charles@cooper-street.com> | |
8 | */ | |
9 | ||
10 | #include <linux/init.h> | |
11 | #include <linux/module.h> | |
12 | #include <linux/slab.h> | |
13 | #include <linux/device.h> | |
14 | #include <linux/delay.h> | |
15 | #include <linux/mutex.h> | |
16 | #include <linux/platform_device.h> | |
17 | #include <linux/suspend.h> | |
18 | #include <sound/core.h> | |
19 | #include <sound/pcm.h> | |
20 | #include <sound/initval.h> | |
21 | #include <sound/soc.h> | |
22 | #include <asm/mach-au1x00/au1000.h> | |
23 | ||
24 | #include "psc.h" | |
25 | ||
26 | /* register offsets and bits */ | |
27 | #define AC97_CONFIG 0x00 | |
28 | #define AC97_STATUS 0x04 | |
29 | #define AC97_DATA 0x08 | |
30 | #define AC97_CMDRESP 0x0c | |
31 | #define AC97_ENABLE 0x10 | |
32 | ||
33 | #define CFG_RC(x) (((x) & 0x3ff) << 13) /* valid rx slots mask */ | |
34 | #define CFG_XS(x) (((x) & 0x3ff) << 3) /* valid tx slots mask */ | |
35 | #define CFG_SG (1 << 2) /* sync gate */ | |
36 | #define CFG_SN (1 << 1) /* sync control */ | |
37 | #define CFG_RS (1 << 0) /* acrst# control */ | |
38 | #define STAT_XU (1 << 11) /* tx underflow */ | |
39 | #define STAT_XO (1 << 10) /* tx overflow */ | |
40 | #define STAT_RU (1 << 9) /* rx underflow */ | |
41 | #define STAT_RO (1 << 8) /* rx overflow */ | |
42 | #define STAT_RD (1 << 7) /* codec ready */ | |
43 | #define STAT_CP (1 << 6) /* command pending */ | |
44 | #define STAT_TE (1 << 4) /* tx fifo empty */ | |
45 | #define STAT_TF (1 << 3) /* tx fifo full */ | |
46 | #define STAT_RE (1 << 1) /* rx fifo empty */ | |
47 | #define STAT_RF (1 << 0) /* rx fifo full */ | |
48 | #define CMD_SET_DATA(x) (((x) & 0xffff) << 16) | |
49 | #define CMD_GET_DATA(x) ((x) & 0xffff) | |
50 | #define CMD_READ (1 << 7) | |
51 | #define CMD_WRITE (0 << 7) | |
52 | #define CMD_IDX(x) ((x) & 0x7f) | |
53 | #define EN_D (1 << 1) /* DISable bit */ | |
54 | #define EN_CE (1 << 0) /* clock enable bit */ | |
55 | ||
56 | /* how often to retry failed codec register reads/writes */ | |
57 | #define AC97_RW_RETRIES 5 | |
58 | ||
59 | #define AC97_RATES \ | |
60 | SNDRV_PCM_RATE_CONTINUOUS | |
61 | ||
62 | #define AC97_FMTS \ | |
63 | (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE) | |
64 | ||
65 | /* instance data. There can be only one, MacLeod!!!!, fortunately there IS only | |
66 | * once AC97C on early Alchemy chips. The newer ones aren't so lucky. | |
67 | */ | |
68 | static struct au1xpsc_audio_data *ac97c_workdata; | |
69 | #define ac97_to_ctx(x) ac97c_workdata | |
70 | ||
71 | static inline unsigned long RD(struct au1xpsc_audio_data *ctx, int reg) | |
72 | { | |
73 | return __raw_readl(ctx->mmio + reg); | |
74 | } | |
75 | ||
76 | static inline void WR(struct au1xpsc_audio_data *ctx, int reg, unsigned long v) | |
77 | { | |
78 | __raw_writel(v, ctx->mmio + reg); | |
79 | wmb(); | |
80 | } | |
81 | ||
82 | static unsigned short au1xac97c_ac97_read(struct snd_ac97 *ac97, | |
83 | unsigned short r) | |
84 | { | |
85 | struct au1xpsc_audio_data *ctx = ac97_to_ctx(ac97); | |
86 | unsigned int tmo, retry; | |
87 | unsigned long data; | |
88 | ||
89 | data = ~0; | |
90 | retry = AC97_RW_RETRIES; | |
91 | do { | |
92 | mutex_lock(&ctx->lock); | |
93 | ||
94 | tmo = 5; | |
95 | while ((RD(ctx, AC97_STATUS) & STAT_CP) && tmo--) | |
96 | udelay(21); /* wait an ac97 frame time */ | |
97 | if (!tmo) { | |
98 | pr_debug("ac97rd timeout #1\n"); | |
99 | goto next; | |
100 | } | |
101 | ||
102 | WR(ctx, AC97_CMDRESP, CMD_IDX(r) | CMD_READ); | |
103 | ||
104 | /* stupid errata: data is only valid for 21us, so | |
105 | * poll, Forrest, poll... | |
106 | */ | |
107 | tmo = 0x10000; | |
108 | while ((RD(ctx, AC97_STATUS) & STAT_CP) && tmo--) | |
109 | asm volatile ("nop"); | |
110 | data = RD(ctx, AC97_CMDRESP); | |
111 | ||
112 | if (!tmo) | |
113 | pr_debug("ac97rd timeout #2\n"); | |
114 | ||
115 | next: | |
116 | mutex_unlock(&ctx->lock); | |
117 | } while (--retry && !tmo); | |
118 | ||
119 | pr_debug("AC97RD %04x %04lx %d\n", r, data, retry); | |
120 | ||
121 | return retry ? data & 0xffff : 0xffff; | |
122 | } | |
123 | ||
124 | static void au1xac97c_ac97_write(struct snd_ac97 *ac97, unsigned short r, | |
125 | unsigned short v) | |
126 | { | |
127 | struct au1xpsc_audio_data *ctx = ac97_to_ctx(ac97); | |
128 | unsigned int tmo, retry; | |
129 | ||
130 | retry = AC97_RW_RETRIES; | |
131 | do { | |
132 | mutex_lock(&ctx->lock); | |
133 | ||
134 | for (tmo = 5; (RD(ctx, AC97_STATUS) & STAT_CP) && tmo; tmo--) | |
135 | udelay(21); | |
136 | if (!tmo) { | |
137 | pr_debug("ac97wr timeout #1\n"); | |
138 | goto next; | |
139 | } | |
140 | ||
141 | WR(ctx, AC97_CMDRESP, CMD_WRITE | CMD_IDX(r) | CMD_SET_DATA(v)); | |
142 | ||
143 | for (tmo = 10; (RD(ctx, AC97_STATUS) & STAT_CP) && tmo; tmo--) | |
144 | udelay(21); | |
145 | if (!tmo) | |
146 | pr_debug("ac97wr timeout #2\n"); | |
147 | next: | |
148 | mutex_unlock(&ctx->lock); | |
149 | } while (--retry && !tmo); | |
150 | ||
151 | pr_debug("AC97WR %04x %04x %d\n", r, v, retry); | |
152 | } | |
153 | ||
154 | static void au1xac97c_ac97_warm_reset(struct snd_ac97 *ac97) | |
155 | { | |
156 | struct au1xpsc_audio_data *ctx = ac97_to_ctx(ac97); | |
157 | ||
158 | WR(ctx, AC97_CONFIG, ctx->cfg | CFG_SG | CFG_SN); | |
159 | msleep(20); | |
160 | WR(ctx, AC97_CONFIG, ctx->cfg | CFG_SG); | |
161 | WR(ctx, AC97_CONFIG, ctx->cfg); | |
162 | } | |
163 | ||
164 | static void au1xac97c_ac97_cold_reset(struct snd_ac97 *ac97) | |
165 | { | |
166 | struct au1xpsc_audio_data *ctx = ac97_to_ctx(ac97); | |
167 | int i; | |
168 | ||
169 | WR(ctx, AC97_CONFIG, ctx->cfg | CFG_RS); | |
170 | msleep(500); | |
171 | WR(ctx, AC97_CONFIG, ctx->cfg); | |
172 | ||
173 | /* wait for codec ready */ | |
174 | i = 50; | |
175 | while (((RD(ctx, AC97_STATUS) & STAT_RD) == 0) && --i) | |
176 | msleep(20); | |
177 | if (!i) | |
178 | printk(KERN_ERR "ac97c: codec not ready after cold reset\n"); | |
179 | } | |
180 | ||
181 | /* AC97 controller operations */ | |
182 | struct snd_ac97_bus_ops soc_ac97_ops = { | |
183 | .read = au1xac97c_ac97_read, | |
184 | .write = au1xac97c_ac97_write, | |
185 | .reset = au1xac97c_ac97_cold_reset, | |
186 | .warm_reset = au1xac97c_ac97_warm_reset, | |
187 | }; | |
188 | EXPORT_SYMBOL_GPL(soc_ac97_ops); /* globals be gone! */ | |
189 | ||
190 | static int alchemy_ac97c_startup(struct snd_pcm_substream *substream, | |
191 | struct snd_soc_dai *dai) | |
192 | { | |
193 | struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(dai); | |
194 | snd_soc_dai_set_dma_data(dai, substream, &ctx->dmaids[0]); | |
195 | return 0; | |
196 | } | |
197 | ||
198 | static struct snd_soc_dai_ops alchemy_ac97c_ops = { | |
199 | .startup = alchemy_ac97c_startup, | |
200 | }; | |
201 | ||
202 | static int au1xac97c_dai_probe(struct snd_soc_dai *dai) | |
203 | { | |
204 | return ac97c_workdata ? 0 : -ENODEV; | |
205 | } | |
206 | ||
207 | static struct snd_soc_dai_driver au1xac97c_dai_driver = { | |
208 | .name = "alchemy-ac97c", | |
209 | .ac97_control = 1, | |
210 | .probe = au1xac97c_dai_probe, | |
211 | .playback = { | |
212 | .rates = AC97_RATES, | |
213 | .formats = AC97_FMTS, | |
214 | .channels_min = 2, | |
215 | .channels_max = 2, | |
216 | }, | |
217 | .capture = { | |
218 | .rates = AC97_RATES, | |
219 | .formats = AC97_FMTS, | |
220 | .channels_min = 2, | |
221 | .channels_max = 2, | |
222 | }, | |
223 | .ops = &alchemy_ac97c_ops, | |
224 | }; | |
225 | ||
226 | static int __devinit au1xac97c_drvprobe(struct platform_device *pdev) | |
227 | { | |
228 | int ret; | |
226d0f22 | 229 | struct resource *iores, *dmares; |
b3c70c9e ML |
230 | struct au1xpsc_audio_data *ctx; |
231 | ||
232 | ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); | |
233 | if (!ctx) | |
234 | return -ENOMEM; | |
235 | ||
236 | mutex_init(&ctx->lock); | |
237 | ||
226d0f22 JL |
238 | iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
239 | if (!iores) { | |
b3c70c9e ML |
240 | ret = -ENODEV; |
241 | goto out0; | |
242 | } | |
243 | ||
244 | ret = -EBUSY; | |
226d0f22 JL |
245 | if (!request_mem_region(iores->start, resource_size(iores), |
246 | pdev->name)) | |
b3c70c9e ML |
247 | goto out0; |
248 | ||
226d0f22 | 249 | ctx->mmio = ioremap_nocache(iores->start, resource_size(iores)); |
b3c70c9e ML |
250 | if (!ctx->mmio) |
251 | goto out1; | |
252 | ||
226d0f22 JL |
253 | dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0); |
254 | if (!dmares) | |
255 | goto out2; | |
256 | ctx->dmaids[SNDRV_PCM_STREAM_PLAYBACK] = dmares->start; | |
b3c70c9e | 257 | |
226d0f22 JL |
258 | dmares = platform_get_resource(pdev, IORESOURCE_DMA, 1); |
259 | if (!dmares) | |
260 | goto out2; | |
261 | ctx->dmaids[SNDRV_PCM_STREAM_CAPTURE] = dmares->start; | |
b3c70c9e ML |
262 | |
263 | /* switch it on */ | |
264 | WR(ctx, AC97_ENABLE, EN_D | EN_CE); | |
265 | WR(ctx, AC97_ENABLE, EN_CE); | |
266 | ||
267 | ctx->cfg = CFG_RC(3) | CFG_XS(3); | |
268 | WR(ctx, AC97_CONFIG, ctx->cfg); | |
269 | ||
270 | platform_set_drvdata(pdev, ctx); | |
271 | ||
272 | ret = snd_soc_register_dai(&pdev->dev, &au1xac97c_dai_driver); | |
273 | if (ret) | |
226d0f22 | 274 | goto out2; |
b3c70c9e ML |
275 | |
276 | ac97c_workdata = ctx; | |
277 | return 0; | |
278 | ||
226d0f22 JL |
279 | out2: |
280 | iounmap(ctx->mmio); | |
b3c70c9e | 281 | out1: |
226d0f22 | 282 | release_mem_region(iores->start, resource_size(iores)); |
b3c70c9e ML |
283 | out0: |
284 | kfree(ctx); | |
285 | return ret; | |
286 | } | |
287 | ||
288 | static int __devexit au1xac97c_drvremove(struct platform_device *pdev) | |
289 | { | |
290 | struct au1xpsc_audio_data *ctx = platform_get_drvdata(pdev); | |
291 | struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
292 | ||
293 | snd_soc_unregister_dai(&pdev->dev); | |
294 | ||
295 | WR(ctx, AC97_ENABLE, EN_D); /* clock off, disable */ | |
296 | ||
297 | iounmap(ctx->mmio); | |
298 | release_mem_region(r->start, resource_size(r)); | |
299 | kfree(ctx); | |
300 | ||
301 | ac97c_workdata = NULL; /* MDEV */ | |
302 | ||
303 | return 0; | |
304 | } | |
305 | ||
306 | #ifdef CONFIG_PM | |
307 | static int au1xac97c_drvsuspend(struct device *dev) | |
308 | { | |
309 | struct au1xpsc_audio_data *ctx = dev_get_drvdata(dev); | |
310 | ||
311 | WR(ctx, AC97_ENABLE, EN_D); /* clock off, disable */ | |
312 | ||
313 | return 0; | |
314 | } | |
315 | ||
316 | static int au1xac97c_drvresume(struct device *dev) | |
317 | { | |
318 | struct au1xpsc_audio_data *ctx = dev_get_drvdata(dev); | |
319 | ||
320 | WR(ctx, AC97_ENABLE, EN_D | EN_CE); | |
321 | WR(ctx, AC97_ENABLE, EN_CE); | |
322 | WR(ctx, AC97_CONFIG, ctx->cfg); | |
323 | ||
324 | return 0; | |
325 | } | |
326 | ||
327 | static const struct dev_pm_ops au1xpscac97_pmops = { | |
328 | .suspend = au1xac97c_drvsuspend, | |
329 | .resume = au1xac97c_drvresume, | |
330 | }; | |
331 | ||
332 | #define AU1XPSCAC97_PMOPS (&au1xpscac97_pmops) | |
333 | ||
334 | #else | |
335 | ||
336 | #define AU1XPSCAC97_PMOPS NULL | |
337 | ||
338 | #endif | |
339 | ||
340 | static struct platform_driver au1xac97c_driver = { | |
341 | .driver = { | |
342 | .name = "alchemy-ac97c", | |
343 | .owner = THIS_MODULE, | |
344 | .pm = AU1XPSCAC97_PMOPS, | |
345 | }, | |
346 | .probe = au1xac97c_drvprobe, | |
347 | .remove = __devexit_p(au1xac97c_drvremove), | |
348 | }; | |
349 | ||
350 | static int __init au1xac97c_load(void) | |
351 | { | |
352 | ac97c_workdata = NULL; | |
353 | return platform_driver_register(&au1xac97c_driver); | |
354 | } | |
355 | ||
356 | static void __exit au1xac97c_unload(void) | |
357 | { | |
358 | platform_driver_unregister(&au1xac97c_driver); | |
359 | } | |
360 | ||
361 | module_init(au1xac97c_load); | |
362 | module_exit(au1xac97c_unload); | |
363 | ||
364 | MODULE_LICENSE("GPL"); | |
365 | MODULE_DESCRIPTION("Au1000/1500/1100 AC97C ASoC driver"); | |
366 | MODULE_AUTHOR("Manuel Lauss"); |