diff options
author | Atsushi Nemoto <anemo@mba.ocn.ne.jp> | 2009-05-19 09:12:15 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2009-05-19 14:54:28 -0400 |
commit | e24805dd85283ac0912b9c400768a4d171b400ff (patch) | |
tree | e67bb07cab9358ac46f77bff9f5a0aef490271a5 /sound/soc/txx9 | |
parent | 11a728110633320d95935a1ba79c038db303596f (diff) |
ASoC: Add TXx9 AC link controller driver (v3)
This patch adds support for the integrated ACLC of the TXx9 family.
Signed-off-by: Atsushi Nemoto <anemo@mba.ocn.ne.jp>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/txx9')
-rw-r--r-- | sound/soc/txx9/Kconfig | 29 | ||||
-rw-r--r-- | sound/soc/txx9/Makefile | 11 | ||||
-rw-r--r-- | sound/soc/txx9/txx9aclc-ac97.c | 255 | ||||
-rw-r--r-- | sound/soc/txx9/txx9aclc-generic.c | 98 | ||||
-rw-r--r-- | sound/soc/txx9/txx9aclc.c | 430 | ||||
-rw-r--r-- | sound/soc/txx9/txx9aclc.h | 83 |
6 files changed, 906 insertions, 0 deletions
diff --git a/sound/soc/txx9/Kconfig b/sound/soc/txx9/Kconfig new file mode 100644 index 000000000000..ebc9327eae71 --- /dev/null +++ b/sound/soc/txx9/Kconfig | |||
@@ -0,0 +1,29 @@ | |||
1 | ## | ||
2 | ## TXx9 ACLC | ||
3 | ## | ||
4 | config SND_SOC_TXX9ACLC | ||
5 | tristate "SoC Audio for TXx9" | ||
6 | depends on HAS_TXX9_ACLC && TXX9_DMAC | ||
7 | help | ||
8 | This option enables support for the AC Link Controllers in TXx9 SoC. | ||
9 | |||
10 | config HAS_TXX9_ACLC | ||
11 | bool | ||
12 | |||
13 | config SND_SOC_TXX9ACLC_AC97 | ||
14 | tristate | ||
15 | select AC97_BUS | ||
16 | select SND_AC97_CODEC | ||
17 | select SND_SOC_AC97_BUS | ||
18 | |||
19 | |||
20 | ## | ||
21 | ## Boards | ||
22 | ## | ||
23 | config SND_SOC_TXX9ACLC_GENERIC | ||
24 | tristate "Generic TXx9 ACLC sound machine" | ||
25 | depends on SND_SOC_TXX9ACLC | ||
26 | select SND_SOC_TXX9ACLC_AC97 | ||
27 | select SND_SOC_AC97_CODEC | ||
28 | help | ||
29 | This is a generic AC97 sound machine for use in TXx9 based systems. | ||
diff --git a/sound/soc/txx9/Makefile b/sound/soc/txx9/Makefile new file mode 100644 index 000000000000..551f16c0c4f9 --- /dev/null +++ b/sound/soc/txx9/Makefile | |||
@@ -0,0 +1,11 @@ | |||
1 | # Platform | ||
2 | snd-soc-txx9aclc-objs := txx9aclc.o | ||
3 | snd-soc-txx9aclc-ac97-objs := txx9aclc-ac97.o | ||
4 | |||
5 | obj-$(CONFIG_SND_SOC_TXX9ACLC) += snd-soc-txx9aclc.o | ||
6 | obj-$(CONFIG_SND_SOC_TXX9ACLC_AC97) += snd-soc-txx9aclc-ac97.o | ||
7 | |||
8 | # Machine | ||
9 | snd-soc-txx9aclc-generic-objs := txx9aclc-generic.o | ||
10 | |||
11 | obj-$(CONFIG_SND_SOC_TXX9ACLC_GENERIC) += snd-soc-txx9aclc-generic.o | ||
diff --git a/sound/soc/txx9/txx9aclc-ac97.c b/sound/soc/txx9/txx9aclc-ac97.c new file mode 100644 index 000000000000..0f83bdb9b16f --- /dev/null +++ b/sound/soc/txx9/txx9aclc-ac97.c | |||
@@ -0,0 +1,255 @@ | |||
1 | /* | ||
2 | * TXx9 ACLC AC97 driver | ||
3 | * | ||
4 | * Copyright (C) 2009 Atsushi Nemoto | ||
5 | * | ||
6 | * Based on RBTX49xx patch from CELF patch archive. | ||
7 | * (C) Copyright TOSHIBA CORPORATION 2004-2006 | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | |||
14 | #include <linux/init.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/delay.h> | ||
17 | #include <linux/interrupt.h> | ||
18 | #include <linux/io.h> | ||
19 | #include <sound/core.h> | ||
20 | #include <sound/pcm.h> | ||
21 | #include <sound/soc.h> | ||
22 | #include "txx9aclc.h" | ||
23 | |||
24 | #define AC97_DIR \ | ||
25 | (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE) | ||
26 | |||
27 | #define AC97_RATES \ | ||
28 | SNDRV_PCM_RATE_8000_48000 | ||
29 | |||
30 | #ifdef __BIG_ENDIAN | ||
31 | #define AC97_FMTS SNDRV_PCM_FMTBIT_S16_BE | ||
32 | #else | ||
33 | #define AC97_FMTS SNDRV_PCM_FMTBIT_S16_LE | ||
34 | #endif | ||
35 | |||
36 | static DECLARE_WAIT_QUEUE_HEAD(ac97_waitq); | ||
37 | |||
38 | /* REVISIT: How to find txx9aclc_soc_device from snd_ac97? */ | ||
39 | static struct txx9aclc_soc_device *txx9aclc_soc_dev; | ||
40 | |||
41 | static int txx9aclc_regready(struct txx9aclc_soc_device *dev) | ||
42 | { | ||
43 | struct txx9aclc_plat_drvdata *drvdata = txx9aclc_get_plat_drvdata(dev); | ||
44 | |||
45 | return __raw_readl(drvdata->base + ACINTSTS) & ACINT_REGACCRDY; | ||
46 | } | ||
47 | |||
48 | /* AC97 controller reads codec register */ | ||
49 | static unsigned short txx9aclc_ac97_read(struct snd_ac97 *ac97, | ||
50 | unsigned short reg) | ||
51 | { | ||
52 | struct txx9aclc_soc_device *dev = txx9aclc_soc_dev; | ||
53 | struct txx9aclc_plat_drvdata *drvdata = txx9aclc_get_plat_drvdata(dev); | ||
54 | void __iomem *base = drvdata->base; | ||
55 | u32 dat; | ||
56 | |||
57 | if (!(__raw_readl(base + ACINTSTS) & ACINT_CODECRDY(ac97->num))) | ||
58 | return 0xffff; | ||
59 | reg |= ac97->num << 7; | ||
60 | dat = (reg << ACREGACC_REG_SHIFT) | ACREGACC_READ; | ||
61 | __raw_writel(dat, base + ACREGACC); | ||
62 | __raw_writel(ACINT_REGACCRDY, base + ACINTEN); | ||
63 | if (!wait_event_timeout(ac97_waitq, txx9aclc_regready(dev), HZ)) { | ||
64 | __raw_writel(ACINT_REGACCRDY, base + ACINTDIS); | ||
65 | dev_err(dev->soc_dev.dev, "ac97 read timeout (reg %#x)\n", reg); | ||
66 | dat = 0xffff; | ||
67 | goto done; | ||
68 | } | ||
69 | dat = __raw_readl(base + ACREGACC); | ||
70 | if (((dat >> ACREGACC_REG_SHIFT) & 0xff) != reg) { | ||
71 | dev_err(dev->soc_dev.dev, "reg mismatch %x with %x\n", | ||
72 | dat, reg); | ||
73 | dat = 0xffff; | ||
74 | goto done; | ||
75 | } | ||
76 | dat = (dat >> ACREGACC_DAT_SHIFT) & 0xffff; | ||
77 | done: | ||
78 | __raw_writel(ACINT_REGACCRDY, base + ACINTDIS); | ||
79 | return dat; | ||
80 | } | ||
81 | |||
82 | /* AC97 controller writes to codec register */ | ||
83 | static void txx9aclc_ac97_write(struct snd_ac97 *ac97, unsigned short reg, | ||
84 | unsigned short val) | ||
85 | { | ||
86 | struct txx9aclc_soc_device *dev = txx9aclc_soc_dev; | ||
87 | struct txx9aclc_plat_drvdata *drvdata = txx9aclc_get_plat_drvdata(dev); | ||
88 | void __iomem *base = drvdata->base; | ||
89 | |||
90 | __raw_writel(((reg | (ac97->num << 7)) << ACREGACC_REG_SHIFT) | | ||
91 | (val << ACREGACC_DAT_SHIFT), | ||
92 | base + ACREGACC); | ||
93 | __raw_writel(ACINT_REGACCRDY, base + ACINTEN); | ||
94 | if (!wait_event_timeout(ac97_waitq, txx9aclc_regready(dev), HZ)) { | ||
95 | dev_err(dev->soc_dev.dev, | ||
96 | "ac97 write timeout (reg %#x)\n", reg); | ||
97 | } | ||
98 | __raw_writel(ACINT_REGACCRDY, base + ACINTDIS); | ||
99 | } | ||
100 | |||
101 | static void txx9aclc_ac97_cold_reset(struct snd_ac97 *ac97) | ||
102 | { | ||
103 | struct txx9aclc_soc_device *dev = txx9aclc_soc_dev; | ||
104 | struct txx9aclc_plat_drvdata *drvdata = txx9aclc_get_plat_drvdata(dev); | ||
105 | void __iomem *base = drvdata->base; | ||
106 | u32 ready = ACINT_CODECRDY(ac97->num) | ACINT_REGACCRDY; | ||
107 | |||
108 | __raw_writel(ACCTL_ENLINK, base + ACCTLDIS); | ||
109 | mmiowb(); | ||
110 | udelay(1); | ||
111 | __raw_writel(ACCTL_ENLINK, base + ACCTLEN); | ||
112 | /* wait for primary codec ready status */ | ||
113 | __raw_writel(ready, base + ACINTEN); | ||
114 | if (!wait_event_timeout(ac97_waitq, | ||
115 | (__raw_readl(base + ACINTSTS) & ready) == ready, | ||
116 | HZ)) { | ||
117 | dev_err(&ac97->dev, "primary codec is not ready " | ||
118 | "(status %#x)\n", | ||
119 | __raw_readl(base + ACINTSTS)); | ||
120 | } | ||
121 | __raw_writel(ACINT_REGACCRDY, base + ACINTSTS); | ||
122 | __raw_writel(ready, base + ACINTDIS); | ||
123 | } | ||
124 | |||
125 | /* AC97 controller operations */ | ||
126 | struct snd_ac97_bus_ops soc_ac97_ops = { | ||
127 | .read = txx9aclc_ac97_read, | ||
128 | .write = txx9aclc_ac97_write, | ||
129 | .reset = txx9aclc_ac97_cold_reset, | ||
130 | }; | ||
131 | EXPORT_SYMBOL_GPL(soc_ac97_ops); | ||
132 | |||
133 | static irqreturn_t txx9aclc_ac97_irq(int irq, void *dev_id) | ||
134 | { | ||
135 | struct txx9aclc_plat_drvdata *drvdata = dev_id; | ||
136 | void __iomem *base = drvdata->base; | ||
137 | |||
138 | __raw_writel(__raw_readl(base + ACINTMSTS), base + ACINTDIS); | ||
139 | wake_up(&ac97_waitq); | ||
140 | return IRQ_HANDLED; | ||
141 | } | ||
142 | |||
143 | static int txx9aclc_ac97_probe(struct platform_device *pdev, | ||
144 | struct snd_soc_dai *dai) | ||
145 | { | ||
146 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
147 | struct txx9aclc_soc_device *dev = | ||
148 | container_of(socdev, struct txx9aclc_soc_device, soc_dev); | ||
149 | |||
150 | dev->aclc_pdev = to_platform_device(dai->dev); | ||
151 | txx9aclc_soc_dev = dev; | ||
152 | return 0; | ||
153 | } | ||
154 | |||
155 | static void txx9aclc_ac97_remove(struct platform_device *pdev, | ||
156 | struct snd_soc_dai *dai) | ||
157 | { | ||
158 | struct platform_device *aclc_pdev = to_platform_device(dai->dev); | ||
159 | struct txx9aclc_plat_drvdata *drvdata = platform_get_drvdata(aclc_pdev); | ||
160 | |||
161 | /* disable AC-link */ | ||
162 | __raw_writel(ACCTL_ENLINK, drvdata->base + ACCTLDIS); | ||
163 | txx9aclc_soc_dev = NULL; | ||
164 | } | ||
165 | |||
166 | struct snd_soc_dai txx9aclc_ac97_dai = { | ||
167 | .name = "txx9aclc_ac97", | ||
168 | .ac97_control = 1, | ||
169 | .probe = txx9aclc_ac97_probe, | ||
170 | .remove = txx9aclc_ac97_remove, | ||
171 | .playback = { | ||
172 | .rates = AC97_RATES, | ||
173 | .formats = AC97_FMTS, | ||
174 | .channels_min = 2, | ||
175 | .channels_max = 2, | ||
176 | }, | ||
177 | .capture = { | ||
178 | .rates = AC97_RATES, | ||
179 | .formats = AC97_FMTS, | ||
180 | .channels_min = 2, | ||
181 | .channels_max = 2, | ||
182 | }, | ||
183 | }; | ||
184 | EXPORT_SYMBOL_GPL(txx9aclc_ac97_dai); | ||
185 | |||
186 | static int __devinit txx9aclc_ac97_dev_probe(struct platform_device *pdev) | ||
187 | { | ||
188 | struct txx9aclc_plat_drvdata *drvdata; | ||
189 | struct resource *r; | ||
190 | int err; | ||
191 | int irq; | ||
192 | |||
193 | irq = platform_get_irq(pdev, 0); | ||
194 | if (irq < 0) | ||
195 | return irq; | ||
196 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
197 | if (!r) | ||
198 | return -EBUSY; | ||
199 | |||
200 | if (!devm_request_mem_region(&pdev->dev, r->start, resource_size(r), | ||
201 | dev_name(&pdev->dev))) | ||
202 | return -EBUSY; | ||
203 | |||
204 | drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL); | ||
205 | if (!drvdata) | ||
206 | return -ENOMEM; | ||
207 | platform_set_drvdata(pdev, drvdata); | ||
208 | drvdata->physbase = r->start; | ||
209 | if (sizeof(drvdata->physbase) > sizeof(r->start) && | ||
210 | r->start >= TXX9_DIRECTMAP_BASE && | ||
211 | r->start < TXX9_DIRECTMAP_BASE + 0x400000) | ||
212 | drvdata->physbase |= 0xf00000000ull; | ||
213 | drvdata->base = devm_ioremap(&pdev->dev, r->start, resource_size(r)); | ||
214 | if (!drvdata->base) | ||
215 | return -EBUSY; | ||
216 | err = devm_request_irq(&pdev->dev, irq, txx9aclc_ac97_irq, | ||
217 | IRQF_DISABLED, dev_name(&pdev->dev), drvdata); | ||
218 | if (err < 0) | ||
219 | return err; | ||
220 | |||
221 | txx9aclc_ac97_dai.dev = &pdev->dev; | ||
222 | return snd_soc_register_dai(&txx9aclc_ac97_dai); | ||
223 | } | ||
224 | |||
225 | static int __devexit txx9aclc_ac97_dev_remove(struct platform_device *pdev) | ||
226 | { | ||
227 | snd_soc_unregister_dai(&txx9aclc_ac97_dai); | ||
228 | return 0; | ||
229 | } | ||
230 | |||
231 | static struct platform_driver txx9aclc_ac97_driver = { | ||
232 | .probe = txx9aclc_ac97_dev_probe, | ||
233 | .remove = __devexit_p(txx9aclc_ac97_dev_remove), | ||
234 | .driver = { | ||
235 | .name = "txx9aclc-ac97", | ||
236 | .owner = THIS_MODULE, | ||
237 | }, | ||
238 | }; | ||
239 | |||
240 | static int __init txx9aclc_ac97_init(void) | ||
241 | { | ||
242 | return platform_driver_register(&txx9aclc_ac97_driver); | ||
243 | } | ||
244 | |||
245 | static void __exit txx9aclc_ac97_exit(void) | ||
246 | { | ||
247 | platform_driver_unregister(&txx9aclc_ac97_driver); | ||
248 | } | ||
249 | |||
250 | module_init(txx9aclc_ac97_init); | ||
251 | module_exit(txx9aclc_ac97_exit); | ||
252 | |||
253 | MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>"); | ||
254 | MODULE_DESCRIPTION("TXx9 ACLC AC97 driver"); | ||
255 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/txx9/txx9aclc-generic.c b/sound/soc/txx9/txx9aclc-generic.c new file mode 100644 index 000000000000..3175de9a92cb --- /dev/null +++ b/sound/soc/txx9/txx9aclc-generic.c | |||
@@ -0,0 +1,98 @@ | |||
1 | /* | ||
2 | * Generic TXx9 ACLC machine driver | ||
3 | * | ||
4 | * Copyright (C) 2009 Atsushi Nemoto | ||
5 | * | ||
6 | * Based on RBTX49xx patch from CELF patch archive. | ||
7 | * (C) Copyright TOSHIBA CORPORATION 2004-2006 | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | * | ||
13 | * This is a very generic AC97 sound machine driver for boards which | ||
14 | * have (AC97) audio at ACLC (e.g. RBTX49XX boards). | ||
15 | */ | ||
16 | |||
17 | #include <linux/module.h> | ||
18 | #include <linux/platform_device.h> | ||
19 | #include <sound/core.h> | ||
20 | #include <sound/pcm.h> | ||
21 | #include <sound/soc.h> | ||
22 | #include "../codecs/ac97.h" | ||
23 | #include "txx9aclc.h" | ||
24 | |||
25 | static struct snd_soc_dai_link txx9aclc_generic_dai = { | ||
26 | .name = "AC97", | ||
27 | .stream_name = "AC97 HiFi", | ||
28 | .cpu_dai = &txx9aclc_ac97_dai, | ||
29 | .codec_dai = &ac97_dai, | ||
30 | }; | ||
31 | |||
32 | static struct snd_soc_card txx9aclc_generic_card = { | ||
33 | .name = "Generic TXx9 ACLC Audio", | ||
34 | .platform = &txx9aclc_soc_platform, | ||
35 | .dai_link = &txx9aclc_generic_dai, | ||
36 | .num_links = 1, | ||
37 | }; | ||
38 | |||
39 | static struct txx9aclc_soc_device txx9aclc_generic_soc_device = { | ||
40 | .soc_dev = { | ||
41 | .card = &txx9aclc_generic_card, | ||
42 | .codec_dev = &soc_codec_dev_ac97, | ||
43 | }, | ||
44 | }; | ||
45 | |||
46 | static int __init txx9aclc_generic_probe(struct platform_device *pdev) | ||
47 | { | ||
48 | struct txx9aclc_soc_device *dev = &txx9aclc_generic_soc_device; | ||
49 | struct platform_device *soc_pdev; | ||
50 | int ret; | ||
51 | |||
52 | soc_pdev = platform_device_alloc("soc-audio", -1); | ||
53 | if (!soc_pdev) | ||
54 | return -ENOMEM; | ||
55 | platform_set_drvdata(soc_pdev, &dev->soc_dev); | ||
56 | dev->soc_dev.dev = &soc_pdev->dev; | ||
57 | ret = platform_device_add(soc_pdev); | ||
58 | if (ret) { | ||
59 | platform_device_put(soc_pdev); | ||
60 | return ret; | ||
61 | } | ||
62 | platform_set_drvdata(pdev, soc_pdev); | ||
63 | return 0; | ||
64 | } | ||
65 | |||
66 | static int __exit txx9aclc_generic_remove(struct platform_device *pdev) | ||
67 | { | ||
68 | struct platform_device *soc_pdev = platform_get_drvdata(pdev); | ||
69 | |||
70 | platform_device_unregister(soc_pdev); | ||
71 | return 0; | ||
72 | } | ||
73 | |||
74 | static struct platform_driver txx9aclc_generic_driver = { | ||
75 | .remove = txx9aclc_generic_remove, | ||
76 | .driver = { | ||
77 | .name = "txx9aclc-generic", | ||
78 | .owner = THIS_MODULE, | ||
79 | }, | ||
80 | }; | ||
81 | |||
82 | static int __init txx9aclc_generic_init(void) | ||
83 | { | ||
84 | return platform_driver_probe(&txx9aclc_generic_driver, | ||
85 | txx9aclc_generic_probe); | ||
86 | } | ||
87 | |||
88 | static void __exit txx9aclc_generic_exit(void) | ||
89 | { | ||
90 | platform_driver_unregister(&txx9aclc_generic_driver); | ||
91 | } | ||
92 | |||
93 | module_init(txx9aclc_generic_init); | ||
94 | module_exit(txx9aclc_generic_exit); | ||
95 | |||
96 | MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>"); | ||
97 | MODULE_DESCRIPTION("Generic TXx9 ACLC ALSA SoC audio driver"); | ||
98 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/txx9/txx9aclc.c b/sound/soc/txx9/txx9aclc.c new file mode 100644 index 000000000000..fa336616152e --- /dev/null +++ b/sound/soc/txx9/txx9aclc.c | |||
@@ -0,0 +1,430 @@ | |||
1 | /* | ||
2 | * Generic TXx9 ACLC platform driver | ||
3 | * | ||
4 | * Copyright (C) 2009 Atsushi Nemoto | ||
5 | * | ||
6 | * Based on RBTX49xx patch from CELF patch archive. | ||
7 | * (C) Copyright TOSHIBA CORPORATION 2004-2006 | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | |||
14 | #include <linux/module.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/platform_device.h> | ||
17 | #include <linux/scatterlist.h> | ||
18 | #include <sound/core.h> | ||
19 | #include <sound/pcm.h> | ||
20 | #include <sound/pcm_params.h> | ||
21 | #include <sound/soc.h> | ||
22 | #include "txx9aclc.h" | ||
23 | |||
24 | static const struct snd_pcm_hardware txx9aclc_pcm_hardware = { | ||
25 | /* | ||
26 | * REVISIT: SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | ||
27 | * needs more works for noncoherent MIPS. | ||
28 | */ | ||
29 | .info = SNDRV_PCM_INFO_INTERLEAVED | | ||
30 | SNDRV_PCM_INFO_BATCH | | ||
31 | SNDRV_PCM_INFO_PAUSE, | ||
32 | #ifdef __BIG_ENDIAN | ||
33 | .formats = SNDRV_PCM_FMTBIT_S16_BE, | ||
34 | #else | ||
35 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
36 | #endif | ||
37 | .period_bytes_min = 1024, | ||
38 | .period_bytes_max = 8 * 1024, | ||
39 | .periods_min = 2, | ||
40 | .periods_max = 4096, | ||
41 | .buffer_bytes_max = 32 * 1024, | ||
42 | }; | ||
43 | |||
44 | static int txx9aclc_pcm_hw_params(struct snd_pcm_substream *substream, | ||
45 | struct snd_pcm_hw_params *params) | ||
46 | { | ||
47 | struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); | ||
48 | struct snd_soc_device *socdev = rtd->socdev; | ||
49 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
50 | struct txx9aclc_dmadata *dmadata = runtime->private_data; | ||
51 | int ret; | ||
52 | |||
53 | ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); | ||
54 | if (ret < 0) | ||
55 | return ret; | ||
56 | |||
57 | dev_dbg(socdev->dev, | ||
58 | "runtime->dma_area = %#lx dma_addr = %#lx dma_bytes = %zd " | ||
59 | "runtime->min_align %ld\n", | ||
60 | (unsigned long)runtime->dma_area, | ||
61 | (unsigned long)runtime->dma_addr, runtime->dma_bytes, | ||
62 | runtime->min_align); | ||
63 | dev_dbg(socdev->dev, | ||
64 | "periods %d period_bytes %d stream %d\n", | ||
65 | params_periods(params), params_period_bytes(params), | ||
66 | substream->stream); | ||
67 | |||
68 | dmadata->substream = substream; | ||
69 | dmadata->pos = 0; | ||
70 | return 0; | ||
71 | } | ||
72 | |||
73 | static int txx9aclc_pcm_hw_free(struct snd_pcm_substream *substream) | ||
74 | { | ||
75 | return snd_pcm_lib_free_pages(substream); | ||
76 | } | ||
77 | |||
78 | static int txx9aclc_pcm_prepare(struct snd_pcm_substream *substream) | ||
79 | { | ||
80 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
81 | struct txx9aclc_dmadata *dmadata = runtime->private_data; | ||
82 | |||
83 | dmadata->dma_addr = runtime->dma_addr; | ||
84 | dmadata->buffer_bytes = snd_pcm_lib_buffer_bytes(substream); | ||
85 | dmadata->period_bytes = snd_pcm_lib_period_bytes(substream); | ||
86 | |||
87 | if (dmadata->buffer_bytes == dmadata->period_bytes) { | ||
88 | dmadata->frag_bytes = dmadata->period_bytes >> 1; | ||
89 | dmadata->frags = 2; | ||
90 | } else { | ||
91 | dmadata->frag_bytes = dmadata->period_bytes; | ||
92 | dmadata->frags = dmadata->buffer_bytes / dmadata->period_bytes; | ||
93 | } | ||
94 | dmadata->frag_count = 0; | ||
95 | dmadata->pos = 0; | ||
96 | return 0; | ||
97 | } | ||
98 | |||
99 | static void txx9aclc_dma_complete(void *arg) | ||
100 | { | ||
101 | struct txx9aclc_dmadata *dmadata = arg; | ||
102 | unsigned long flags; | ||
103 | |||
104 | /* dma completion handler cannot submit new operations */ | ||
105 | spin_lock_irqsave(&dmadata->dma_lock, flags); | ||
106 | if (dmadata->frag_count >= 0) { | ||
107 | dmadata->dmacount--; | ||
108 | BUG_ON(dmadata->dmacount < 0); | ||
109 | tasklet_schedule(&dmadata->tasklet); | ||
110 | } | ||
111 | spin_unlock_irqrestore(&dmadata->dma_lock, flags); | ||
112 | } | ||
113 | |||
114 | static struct dma_async_tx_descriptor * | ||
115 | txx9aclc_dma_submit(struct txx9aclc_dmadata *dmadata, dma_addr_t buf_dma_addr) | ||
116 | { | ||
117 | struct dma_chan *chan = dmadata->dma_chan; | ||
118 | struct dma_async_tx_descriptor *desc; | ||
119 | struct scatterlist sg; | ||
120 | |||
121 | sg_init_table(&sg, 1); | ||
122 | sg_set_page(&sg, pfn_to_page(PFN_DOWN(buf_dma_addr)), | ||
123 | dmadata->frag_bytes, buf_dma_addr & (PAGE_SIZE - 1)); | ||
124 | sg_dma_address(&sg) = buf_dma_addr; | ||
125 | desc = chan->device->device_prep_slave_sg(chan, &sg, 1, | ||
126 | dmadata->substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? | ||
127 | DMA_TO_DEVICE : DMA_FROM_DEVICE, | ||
128 | DMA_PREP_INTERRUPT | DMA_CTRL_ACK); | ||
129 | if (!desc) { | ||
130 | dev_err(&chan->dev->device, "cannot prepare slave dma\n"); | ||
131 | return NULL; | ||
132 | } | ||
133 | desc->callback = txx9aclc_dma_complete; | ||
134 | desc->callback_param = dmadata; | ||
135 | desc->tx_submit(desc); | ||
136 | return desc; | ||
137 | } | ||
138 | |||
139 | #define NR_DMA_CHAIN 2 | ||
140 | |||
141 | static void txx9aclc_dma_tasklet(unsigned long data) | ||
142 | { | ||
143 | struct txx9aclc_dmadata *dmadata = (struct txx9aclc_dmadata *)data; | ||
144 | struct dma_chan *chan = dmadata->dma_chan; | ||
145 | struct dma_async_tx_descriptor *desc; | ||
146 | struct snd_pcm_substream *substream = dmadata->substream; | ||
147 | u32 ctlbit = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? | ||
148 | ACCTL_AUDODMA : ACCTL_AUDIDMA; | ||
149 | int i; | ||
150 | unsigned long flags; | ||
151 | |||
152 | spin_lock_irqsave(&dmadata->dma_lock, flags); | ||
153 | if (dmadata->frag_count < 0) { | ||
154 | struct txx9aclc_soc_device *dev = | ||
155 | container_of(dmadata, struct txx9aclc_soc_device, | ||
156 | dmadata[substream->stream]); | ||
157 | struct txx9aclc_plat_drvdata *drvdata = | ||
158 | txx9aclc_get_plat_drvdata(dev); | ||
159 | void __iomem *base = drvdata->base; | ||
160 | |||
161 | spin_unlock_irqrestore(&dmadata->dma_lock, flags); | ||
162 | chan->device->device_terminate_all(chan); | ||
163 | /* first time */ | ||
164 | for (i = 0; i < NR_DMA_CHAIN; i++) { | ||
165 | desc = txx9aclc_dma_submit(dmadata, | ||
166 | dmadata->dma_addr + i * dmadata->frag_bytes); | ||
167 | if (!desc) | ||
168 | return; | ||
169 | } | ||
170 | dmadata->dmacount = NR_DMA_CHAIN; | ||
171 | chan->device->device_issue_pending(chan); | ||
172 | spin_lock_irqsave(&dmadata->dma_lock, flags); | ||
173 | __raw_writel(ctlbit, base + ACCTLEN); | ||
174 | dmadata->frag_count = NR_DMA_CHAIN % dmadata->frags; | ||
175 | spin_unlock_irqrestore(&dmadata->dma_lock, flags); | ||
176 | return; | ||
177 | } | ||
178 | BUG_ON(dmadata->dmacount >= NR_DMA_CHAIN); | ||
179 | while (dmadata->dmacount < NR_DMA_CHAIN) { | ||
180 | dmadata->dmacount++; | ||
181 | spin_unlock_irqrestore(&dmadata->dma_lock, flags); | ||
182 | desc = txx9aclc_dma_submit(dmadata, | ||
183 | dmadata->dma_addr + | ||
184 | dmadata->frag_count * dmadata->frag_bytes); | ||
185 | if (!desc) | ||
186 | return; | ||
187 | chan->device->device_issue_pending(chan); | ||
188 | |||
189 | spin_lock_irqsave(&dmadata->dma_lock, flags); | ||
190 | dmadata->frag_count++; | ||
191 | dmadata->frag_count %= dmadata->frags; | ||
192 | dmadata->pos += dmadata->frag_bytes; | ||
193 | dmadata->pos %= dmadata->buffer_bytes; | ||
194 | if ((dmadata->frag_count * dmadata->frag_bytes) % | ||
195 | dmadata->period_bytes == 0) | ||
196 | snd_pcm_period_elapsed(substream); | ||
197 | } | ||
198 | spin_unlock_irqrestore(&dmadata->dma_lock, flags); | ||
199 | } | ||
200 | |||
201 | static int txx9aclc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | ||
202 | { | ||
203 | struct txx9aclc_dmadata *dmadata = substream->runtime->private_data; | ||
204 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
205 | struct txx9aclc_soc_device *dev = | ||
206 | container_of(rtd->socdev, struct txx9aclc_soc_device, soc_dev); | ||
207 | struct txx9aclc_plat_drvdata *drvdata = txx9aclc_get_plat_drvdata(dev); | ||
208 | void __iomem *base = drvdata->base; | ||
209 | unsigned long flags; | ||
210 | int ret = 0; | ||
211 | u32 ctlbit = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? | ||
212 | ACCTL_AUDODMA : ACCTL_AUDIDMA; | ||
213 | |||
214 | spin_lock_irqsave(&dmadata->dma_lock, flags); | ||
215 | switch (cmd) { | ||
216 | case SNDRV_PCM_TRIGGER_START: | ||
217 | dmadata->frag_count = -1; | ||
218 | tasklet_schedule(&dmadata->tasklet); | ||
219 | break; | ||
220 | case SNDRV_PCM_TRIGGER_STOP: | ||
221 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
222 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
223 | __raw_writel(ctlbit, base + ACCTLDIS); | ||
224 | break; | ||
225 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
226 | case SNDRV_PCM_TRIGGER_RESUME: | ||
227 | __raw_writel(ctlbit, base + ACCTLEN); | ||
228 | break; | ||
229 | default: | ||
230 | ret = -EINVAL; | ||
231 | } | ||
232 | spin_unlock_irqrestore(&dmadata->dma_lock, flags); | ||
233 | return ret; | ||
234 | } | ||
235 | |||
236 | static snd_pcm_uframes_t | ||
237 | txx9aclc_pcm_pointer(struct snd_pcm_substream *substream) | ||
238 | { | ||
239 | struct txx9aclc_dmadata *dmadata = substream->runtime->private_data; | ||
240 | |||
241 | return bytes_to_frames(substream->runtime, dmadata->pos); | ||
242 | } | ||
243 | |||
244 | static int txx9aclc_pcm_open(struct snd_pcm_substream *substream) | ||
245 | { | ||
246 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
247 | struct txx9aclc_soc_device *dev = | ||
248 | container_of(rtd->socdev, struct txx9aclc_soc_device, soc_dev); | ||
249 | struct txx9aclc_dmadata *dmadata = &dev->dmadata[substream->stream]; | ||
250 | int ret; | ||
251 | |||
252 | ret = snd_soc_set_runtime_hwparams(substream, &txx9aclc_pcm_hardware); | ||
253 | if (ret) | ||
254 | return ret; | ||
255 | /* ensure that buffer size is a multiple of period size */ | ||
256 | ret = snd_pcm_hw_constraint_integer(substream->runtime, | ||
257 | SNDRV_PCM_HW_PARAM_PERIODS); | ||
258 | if (ret < 0) | ||
259 | return ret; | ||
260 | substream->runtime->private_data = dmadata; | ||
261 | return 0; | ||
262 | } | ||
263 | |||
264 | static int txx9aclc_pcm_close(struct snd_pcm_substream *substream) | ||
265 | { | ||
266 | struct txx9aclc_dmadata *dmadata = substream->runtime->private_data; | ||
267 | struct dma_chan *chan = dmadata->dma_chan; | ||
268 | |||
269 | dmadata->frag_count = -1; | ||
270 | chan->device->device_terminate_all(chan); | ||
271 | return 0; | ||
272 | } | ||
273 | |||
274 | static struct snd_pcm_ops txx9aclc_pcm_ops = { | ||
275 | .open = txx9aclc_pcm_open, | ||
276 | .close = txx9aclc_pcm_close, | ||
277 | .ioctl = snd_pcm_lib_ioctl, | ||
278 | .hw_params = txx9aclc_pcm_hw_params, | ||
279 | .hw_free = txx9aclc_pcm_hw_free, | ||
280 | .prepare = txx9aclc_pcm_prepare, | ||
281 | .trigger = txx9aclc_pcm_trigger, | ||
282 | .pointer = txx9aclc_pcm_pointer, | ||
283 | }; | ||
284 | |||
285 | static void txx9aclc_pcm_free_dma_buffers(struct snd_pcm *pcm) | ||
286 | { | ||
287 | snd_pcm_lib_preallocate_free_for_all(pcm); | ||
288 | } | ||
289 | |||
290 | static int txx9aclc_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, | ||
291 | struct snd_pcm *pcm) | ||
292 | { | ||
293 | return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, | ||
294 | card->dev, 64 * 1024, 4 * 1024 * 1024); | ||
295 | } | ||
296 | |||
297 | static bool filter(struct dma_chan *chan, void *param) | ||
298 | { | ||
299 | struct txx9aclc_dmadata *dmadata = param; | ||
300 | char devname[BUS_ID_SIZE + 2]; | ||
301 | |||
302 | sprintf(devname, "%s.%d", dmadata->dma_res->name, | ||
303 | (int)dmadata->dma_res->start); | ||
304 | if (strcmp(dev_name(chan->device->dev), devname) == 0) { | ||
305 | chan->private = &dmadata->dma_slave; | ||
306 | return true; | ||
307 | } | ||
308 | return false; | ||
309 | } | ||
310 | |||
311 | static int txx9aclc_dma_init(struct txx9aclc_soc_device *dev, | ||
312 | struct txx9aclc_dmadata *dmadata) | ||
313 | { | ||
314 | struct txx9aclc_plat_drvdata *drvdata = txx9aclc_get_plat_drvdata(dev); | ||
315 | struct txx9dmac_slave *ds = &dmadata->dma_slave; | ||
316 | dma_cap_mask_t mask; | ||
317 | |||
318 | spin_lock_init(&dmadata->dma_lock); | ||
319 | |||
320 | ds->reg_width = sizeof(u32); | ||
321 | if (dmadata->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
322 | ds->tx_reg = drvdata->physbase + ACAUDODAT; | ||
323 | ds->rx_reg = 0; | ||
324 | } else { | ||
325 | ds->tx_reg = 0; | ||
326 | ds->rx_reg = drvdata->physbase + ACAUDIDAT; | ||
327 | } | ||
328 | |||
329 | /* Try to grab a DMA channel */ | ||
330 | dma_cap_zero(mask); | ||
331 | dma_cap_set(DMA_SLAVE, mask); | ||
332 | dmadata->dma_chan = dma_request_channel(mask, filter, dmadata); | ||
333 | if (!dmadata->dma_chan) { | ||
334 | dev_err(dev->soc_dev.dev, | ||
335 | "DMA channel for %s is not available\n", | ||
336 | dmadata->stream == SNDRV_PCM_STREAM_PLAYBACK ? | ||
337 | "playback" : "capture"); | ||
338 | return -EBUSY; | ||
339 | } | ||
340 | tasklet_init(&dmadata->tasklet, txx9aclc_dma_tasklet, | ||
341 | (unsigned long)dmadata); | ||
342 | return 0; | ||
343 | } | ||
344 | |||
345 | static int txx9aclc_pcm_probe(struct platform_device *pdev) | ||
346 | { | ||
347 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
348 | struct txx9aclc_soc_device *dev = | ||
349 | container_of(socdev, struct txx9aclc_soc_device, soc_dev); | ||
350 | struct resource *r; | ||
351 | int i; | ||
352 | int ret; | ||
353 | |||
354 | dev->dmadata[0].stream = SNDRV_PCM_STREAM_PLAYBACK; | ||
355 | dev->dmadata[1].stream = SNDRV_PCM_STREAM_CAPTURE; | ||
356 | for (i = 0; i < 2; i++) { | ||
357 | r = platform_get_resource(dev->aclc_pdev, IORESOURCE_DMA, i); | ||
358 | if (!r) { | ||
359 | ret = -EBUSY; | ||
360 | goto exit; | ||
361 | } | ||
362 | dev->dmadata[i].dma_res = r; | ||
363 | ret = txx9aclc_dma_init(dev, &dev->dmadata[i]); | ||
364 | if (ret) | ||
365 | goto exit; | ||
366 | } | ||
367 | return 0; | ||
368 | |||
369 | exit: | ||
370 | for (i = 0; i < 2; i++) { | ||
371 | if (dev->dmadata[i].dma_chan) | ||
372 | dma_release_channel(dev->dmadata[i].dma_chan); | ||
373 | dev->dmadata[i].dma_chan = NULL; | ||
374 | } | ||
375 | return ret; | ||
376 | } | ||
377 | |||
378 | static int txx9aclc_pcm_remove(struct platform_device *pdev) | ||
379 | { | ||
380 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
381 | struct txx9aclc_soc_device *dev = | ||
382 | container_of(socdev, struct txx9aclc_soc_device, soc_dev); | ||
383 | struct txx9aclc_plat_drvdata *drvdata = txx9aclc_get_plat_drvdata(dev); | ||
384 | void __iomem *base = drvdata->base; | ||
385 | int i; | ||
386 | |||
387 | /* disable all FIFO DMAs */ | ||
388 | __raw_writel(ACCTL_AUDODMA | ACCTL_AUDIDMA, base + ACCTLDIS); | ||
389 | /* dummy R/W to clear pending DMAREQ if any */ | ||
390 | __raw_writel(__raw_readl(base + ACAUDIDAT), base + ACAUDODAT); | ||
391 | |||
392 | for (i = 0; i < 2; i++) { | ||
393 | struct txx9aclc_dmadata *dmadata = &dev->dmadata[i]; | ||
394 | struct dma_chan *chan = dmadata->dma_chan; | ||
395 | if (chan) { | ||
396 | dmadata->frag_count = -1; | ||
397 | chan->device->device_terminate_all(chan); | ||
398 | dma_release_channel(chan); | ||
399 | } | ||
400 | dev->dmadata[i].dma_chan = NULL; | ||
401 | } | ||
402 | return 0; | ||
403 | } | ||
404 | |||
405 | struct snd_soc_platform txx9aclc_soc_platform = { | ||
406 | .name = "txx9aclc-audio", | ||
407 | .probe = txx9aclc_pcm_probe, | ||
408 | .remove = txx9aclc_pcm_remove, | ||
409 | .pcm_ops = &txx9aclc_pcm_ops, | ||
410 | .pcm_new = txx9aclc_pcm_new, | ||
411 | .pcm_free = txx9aclc_pcm_free_dma_buffers, | ||
412 | }; | ||
413 | EXPORT_SYMBOL_GPL(txx9aclc_soc_platform); | ||
414 | |||
415 | static int __init txx9aclc_soc_platform_init(void) | ||
416 | { | ||
417 | return snd_soc_register_platform(&txx9aclc_soc_platform); | ||
418 | } | ||
419 | |||
420 | static void __exit txx9aclc_soc_platform_exit(void) | ||
421 | { | ||
422 | snd_soc_unregister_platform(&txx9aclc_soc_platform); | ||
423 | } | ||
424 | |||
425 | module_init(txx9aclc_soc_platform_init); | ||
426 | module_exit(txx9aclc_soc_platform_exit); | ||
427 | |||
428 | MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>"); | ||
429 | MODULE_DESCRIPTION("TXx9 ACLC Audio DMA driver"); | ||
430 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/txx9/txx9aclc.h b/sound/soc/txx9/txx9aclc.h new file mode 100644 index 000000000000..6769aab41b33 --- /dev/null +++ b/sound/soc/txx9/txx9aclc.h | |||
@@ -0,0 +1,83 @@ | |||
1 | /* | ||
2 | * TXx9 SoC AC Link Controller | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #ifndef __TXX9ACLC_H | ||
10 | #define __TXX9ACLC_H | ||
11 | |||
12 | #include <linux/interrupt.h> | ||
13 | #include <asm/txx9/dmac.h> | ||
14 | |||
15 | #define ACCTLEN 0x00 /* control enable */ | ||
16 | #define ACCTLDIS 0x04 /* control disable */ | ||
17 | #define ACCTL_ENLINK 0x00000001 /* enable/disable AC-link */ | ||
18 | #define ACCTL_AUDODMA 0x00000100 /* AUDODMA enable/disable */ | ||
19 | #define ACCTL_AUDIDMA 0x00001000 /* AUDIDMA enable/disable */ | ||
20 | #define ACCTL_AUDOEHLT 0x00010000 /* AUDO error halt | ||
21 | enable/disable */ | ||
22 | #define ACCTL_AUDIEHLT 0x00100000 /* AUDI error halt | ||
23 | enable/disable */ | ||
24 | #define ACREGACC 0x08 /* codec register access */ | ||
25 | #define ACREGACC_DAT_SHIFT 0 /* data field */ | ||
26 | #define ACREGACC_REG_SHIFT 16 /* address field */ | ||
27 | #define ACREGACC_CODECID_SHIFT 24 /* CODEC ID field */ | ||
28 | #define ACREGACC_READ 0x80000000 /* CODEC read */ | ||
29 | #define ACREGACC_WRITE 0x00000000 /* CODEC write */ | ||
30 | #define ACINTSTS 0x10 /* interrupt status */ | ||
31 | #define ACINTMSTS 0x14 /* interrupt masked status */ | ||
32 | #define ACINTEN 0x18 /* interrupt enable */ | ||
33 | #define ACINTDIS 0x1c /* interrupt disable */ | ||
34 | #define ACINT_CODECRDY(n) (0x00000001 << (n)) /* CODECn ready */ | ||
35 | #define ACINT_REGACCRDY 0x00000010 /* ACREGACC ready */ | ||
36 | #define ACINT_AUDOERR 0x00000100 /* AUDO underrun error */ | ||
37 | #define ACINT_AUDIERR 0x00001000 /* AUDI overrun error */ | ||
38 | #define ACDMASTS 0x80 /* DMA request status */ | ||
39 | #define ACDMA_AUDO 0x00000001 /* AUDODMA pending */ | ||
40 | #define ACDMA_AUDI 0x00000010 /* AUDIDMA pending */ | ||
41 | #define ACAUDODAT 0xa0 /* audio out data */ | ||
42 | #define ACAUDIDAT 0xb0 /* audio in data */ | ||
43 | #define ACREVID 0xfc /* revision ID */ | ||
44 | |||
45 | struct txx9aclc_dmadata { | ||
46 | struct resource *dma_res; | ||
47 | struct txx9dmac_slave dma_slave; | ||
48 | struct dma_chan *dma_chan; | ||
49 | struct tasklet_struct tasklet; | ||
50 | spinlock_t dma_lock; | ||
51 | int stream; /* SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE */ | ||
52 | struct snd_pcm_substream *substream; | ||
53 | unsigned long pos; | ||
54 | dma_addr_t dma_addr; | ||
55 | unsigned long buffer_bytes; | ||
56 | unsigned long period_bytes; | ||
57 | unsigned long frag_bytes; | ||
58 | int frags; | ||
59 | int frag_count; | ||
60 | int dmacount; | ||
61 | }; | ||
62 | |||
63 | struct txx9aclc_plat_drvdata { | ||
64 | void __iomem *base; | ||
65 | u64 physbase; | ||
66 | }; | ||
67 | |||
68 | struct txx9aclc_soc_device { | ||
69 | struct snd_soc_device soc_dev; | ||
70 | struct platform_device *aclc_pdev; /* for ioresources, drvdata */ | ||
71 | struct txx9aclc_dmadata dmadata[2]; | ||
72 | }; | ||
73 | |||
74 | static inline struct txx9aclc_plat_drvdata *txx9aclc_get_plat_drvdata( | ||
75 | struct txx9aclc_soc_device *sdev) | ||
76 | { | ||
77 | return platform_get_drvdata(sdev->aclc_pdev); | ||
78 | } | ||
79 | |||
80 | extern struct snd_soc_platform txx9aclc_soc_platform; | ||
81 | extern struct snd_soc_dai txx9aclc_ac97_dai; | ||
82 | |||
83 | #endif /* __TXX9ACLC_H */ | ||