]>
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 | ||
8e1e84ff DC |
94 | tmo = 6; |
95 | while ((RD(ctx, AC97_STATUS) & STAT_CP) && --tmo) | |
b3c70c9e ML |
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; | |
8e1e84ff | 108 | while ((RD(ctx, AC97_STATUS) & STAT_CP) && --tmo) |
b3c70c9e ML |
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 */ | |
b047e1cc | 182 | static struct snd_ac97_bus_ops ac97c_bus_ops = { |
b3c70c9e ML |
183 | .read = au1xac97c_ac97_read, |
184 | .write = au1xac97c_ac97_write, | |
185 | .reset = au1xac97c_ac97_cold_reset, | |
186 | .warm_reset = au1xac97c_ac97_warm_reset, | |
187 | }; | |
b3c70c9e ML |
188 | |
189 | static int alchemy_ac97c_startup(struct snd_pcm_substream *substream, | |
190 | struct snd_soc_dai *dai) | |
191 | { | |
192 | struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(dai); | |
193 | snd_soc_dai_set_dma_data(dai, substream, &ctx->dmaids[0]); | |
194 | return 0; | |
195 | } | |
196 | ||
85e7652d | 197 | static const struct snd_soc_dai_ops alchemy_ac97c_ops = { |
b3c70c9e ML |
198 | .startup = alchemy_ac97c_startup, |
199 | }; | |
200 | ||
201 | static int au1xac97c_dai_probe(struct snd_soc_dai *dai) | |
202 | { | |
203 | return ac97c_workdata ? 0 : -ENODEV; | |
204 | } | |
205 | ||
206 | static struct snd_soc_dai_driver au1xac97c_dai_driver = { | |
207 | .name = "alchemy-ac97c", | |
bc263214 | 208 | .bus_control = true, |
b3c70c9e ML |
209 | .probe = au1xac97c_dai_probe, |
210 | .playback = { | |
211 | .rates = AC97_RATES, | |
212 | .formats = AC97_FMTS, | |
213 | .channels_min = 2, | |
214 | .channels_max = 2, | |
215 | }, | |
216 | .capture = { | |
217 | .rates = AC97_RATES, | |
218 | .formats = AC97_FMTS, | |
219 | .channels_min = 2, | |
220 | .channels_max = 2, | |
221 | }, | |
222 | .ops = &alchemy_ac97c_ops, | |
223 | }; | |
224 | ||
bbedf1b2 KM |
225 | static const struct snd_soc_component_driver au1xac97c_component = { |
226 | .name = "au1xac97c", | |
227 | }; | |
228 | ||
5c658be0 | 229 | static int au1xac97c_drvprobe(struct platform_device *pdev) |
b3c70c9e ML |
230 | { |
231 | int ret; | |
226d0f22 | 232 | struct resource *iores, *dmares; |
b3c70c9e ML |
233 | struct au1xpsc_audio_data *ctx; |
234 | ||
6065abf5 | 235 | ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); |
b3c70c9e ML |
236 | if (!ctx) |
237 | return -ENOMEM; | |
238 | ||
239 | mutex_init(&ctx->lock); | |
240 | ||
226d0f22 | 241 | iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
6065abf5 JL |
242 | if (!iores) |
243 | return -ENODEV; | |
b3c70c9e | 244 | |
6065abf5 JL |
245 | if (!devm_request_mem_region(&pdev->dev, iores->start, |
246 | resource_size(iores), | |
247 | pdev->name)) | |
248 | return -EBUSY; | |
b3c70c9e | 249 | |
6065abf5 JL |
250 | ctx->mmio = devm_ioremap_nocache(&pdev->dev, iores->start, |
251 | resource_size(iores)); | |
b3c70c9e | 252 | if (!ctx->mmio) |
6065abf5 | 253 | return -EBUSY; |
b3c70c9e | 254 | |
226d0f22 JL |
255 | dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0); |
256 | if (!dmares) | |
6065abf5 | 257 | return -EBUSY; |
226d0f22 | 258 | ctx->dmaids[SNDRV_PCM_STREAM_PLAYBACK] = dmares->start; |
b3c70c9e | 259 | |
226d0f22 JL |
260 | dmares = platform_get_resource(pdev, IORESOURCE_DMA, 1); |
261 | if (!dmares) | |
6065abf5 | 262 | return -EBUSY; |
226d0f22 | 263 | ctx->dmaids[SNDRV_PCM_STREAM_CAPTURE] = dmares->start; |
b3c70c9e ML |
264 | |
265 | /* switch it on */ | |
266 | WR(ctx, AC97_ENABLE, EN_D | EN_CE); | |
267 | WR(ctx, AC97_ENABLE, EN_CE); | |
268 | ||
269 | ctx->cfg = CFG_RC(3) | CFG_XS(3); | |
270 | WR(ctx, AC97_CONFIG, ctx->cfg); | |
271 | ||
272 | platform_set_drvdata(pdev, ctx); | |
273 | ||
b047e1cc MB |
274 | ret = snd_soc_set_ac97_ops(&ac97c_bus_ops); |
275 | if (ret) | |
276 | return ret; | |
277 | ||
bbedf1b2 KM |
278 | ret = snd_soc_register_component(&pdev->dev, &au1xac97c_component, |
279 | &au1xac97c_dai_driver, 1); | |
b3c70c9e | 280 | if (ret) |
6065abf5 | 281 | return ret; |
b3c70c9e ML |
282 | |
283 | ac97c_workdata = ctx; | |
284 | return 0; | |
b3c70c9e ML |
285 | } |
286 | ||
5c658be0 | 287 | static int au1xac97c_drvremove(struct platform_device *pdev) |
b3c70c9e ML |
288 | { |
289 | struct au1xpsc_audio_data *ctx = platform_get_drvdata(pdev); | |
b3c70c9e | 290 | |
bbedf1b2 | 291 | snd_soc_unregister_component(&pdev->dev); |
b3c70c9e ML |
292 | |
293 | WR(ctx, AC97_ENABLE, EN_D); /* clock off, disable */ | |
294 | ||
b3c70c9e ML |
295 | ac97c_workdata = NULL; /* MDEV */ |
296 | ||
297 | return 0; | |
298 | } | |
299 | ||
300 | #ifdef CONFIG_PM | |
301 | static int au1xac97c_drvsuspend(struct device *dev) | |
302 | { | |
303 | struct au1xpsc_audio_data *ctx = dev_get_drvdata(dev); | |
304 | ||
305 | WR(ctx, AC97_ENABLE, EN_D); /* clock off, disable */ | |
306 | ||
307 | return 0; | |
308 | } | |
309 | ||
310 | static int au1xac97c_drvresume(struct device *dev) | |
311 | { | |
312 | struct au1xpsc_audio_data *ctx = dev_get_drvdata(dev); | |
313 | ||
314 | WR(ctx, AC97_ENABLE, EN_D | EN_CE); | |
315 | WR(ctx, AC97_ENABLE, EN_CE); | |
316 | WR(ctx, AC97_CONFIG, ctx->cfg); | |
317 | ||
318 | return 0; | |
319 | } | |
320 | ||
321 | static const struct dev_pm_ops au1xpscac97_pmops = { | |
322 | .suspend = au1xac97c_drvsuspend, | |
323 | .resume = au1xac97c_drvresume, | |
324 | }; | |
325 | ||
326 | #define AU1XPSCAC97_PMOPS (&au1xpscac97_pmops) | |
327 | ||
328 | #else | |
329 | ||
330 | #define AU1XPSCAC97_PMOPS NULL | |
331 | ||
332 | #endif | |
333 | ||
334 | static struct platform_driver au1xac97c_driver = { | |
335 | .driver = { | |
336 | .name = "alchemy-ac97c", | |
b3c70c9e ML |
337 | .pm = AU1XPSCAC97_PMOPS, |
338 | }, | |
339 | .probe = au1xac97c_drvprobe, | |
5c658be0 | 340 | .remove = au1xac97c_drvremove, |
b3c70c9e ML |
341 | }; |
342 | ||
d2ee88d0 | 343 | module_platform_driver(au1xac97c_driver); |
b3c70c9e ML |
344 | |
345 | MODULE_LICENSE("GPL"); | |
346 | MODULE_DESCRIPTION("Au1000/1500/1100 AC97C ASoC driver"); | |
347 | MODULE_AUTHOR("Manuel Lauss"); |