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