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