]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * linux/sound/pxa2xx-ac97.c -- AC97 support for the Intel PXA2xx chip. | |
3 | * | |
4 | * Author: Nicolas Pitre | |
5 | * Created: Dec 02, 2004 | |
6 | * Copyright: MontaVista Software Inc. | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License version 2 as | |
10 | * published by the Free Software Foundation. | |
11 | */ | |
12 | ||
13 | #include <linux/init.h> | |
14 | #include <linux/io.h> | |
15 | #include <linux/module.h> | |
16 | #include <linux/platform_device.h> | |
17 | #include <linux/dmaengine.h> | |
18 | #include <linux/dma/pxa-dma.h> | |
19 | ||
20 | #include <sound/core.h> | |
21 | #include <sound/pcm.h> | |
22 | #include <sound/ac97_codec.h> | |
23 | #include <sound/initval.h> | |
24 | #include <sound/pxa2xx-lib.h> | |
25 | #include <sound/dmaengine_pcm.h> | |
26 | ||
27 | #include <mach/regs-ac97.h> | |
28 | #include <mach/audio.h> | |
29 | ||
30 | #include "pxa2xx-pcm.h" | |
31 | ||
32 | static void pxa2xx_ac97_legacy_reset(struct snd_ac97 *ac97) | |
33 | { | |
34 | if (!pxa2xx_ac97_try_cold_reset()) | |
35 | pxa2xx_ac97_try_warm_reset(); | |
36 | ||
37 | pxa2xx_ac97_finish_reset(); | |
38 | } | |
39 | ||
40 | static unsigned short pxa2xx_ac97_legacy_read(struct snd_ac97 *ac97, | |
41 | unsigned short reg) | |
42 | { | |
43 | int ret; | |
44 | ||
45 | ret = pxa2xx_ac97_read(ac97->num, reg); | |
46 | if (ret < 0) | |
47 | return 0; | |
48 | else | |
49 | return (unsigned short)(ret & 0xffff); | |
50 | } | |
51 | ||
52 | static void pxa2xx_ac97_legacy_write(struct snd_ac97 *ac97, | |
53 | unsigned short reg, unsigned short val) | |
54 | { | |
55 | int __always_unused ret; | |
56 | ||
57 | ret = pxa2xx_ac97_write(ac97->num, reg, val); | |
58 | } | |
59 | ||
60 | static struct snd_ac97_bus_ops pxa2xx_ac97_ops = { | |
61 | .read = pxa2xx_ac97_legacy_read, | |
62 | .write = pxa2xx_ac97_legacy_write, | |
63 | .reset = pxa2xx_ac97_legacy_reset, | |
64 | }; | |
65 | ||
66 | static struct pxad_param pxa2xx_ac97_pcm_out_req = { | |
67 | .prio = PXAD_PRIO_LOWEST, | |
68 | .drcmr = 12, | |
69 | }; | |
70 | ||
71 | static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_out = { | |
72 | .addr = __PREG(PCDR), | |
73 | .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, | |
74 | .maxburst = 32, | |
75 | .filter_data = &pxa2xx_ac97_pcm_out_req, | |
76 | }; | |
77 | ||
78 | static struct pxad_param pxa2xx_ac97_pcm_in_req = { | |
79 | .prio = PXAD_PRIO_LOWEST, | |
80 | .drcmr = 11, | |
81 | }; | |
82 | ||
83 | static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_in = { | |
84 | .addr = __PREG(PCDR), | |
85 | .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, | |
86 | .maxburst = 32, | |
87 | .filter_data = &pxa2xx_ac97_pcm_in_req, | |
88 | }; | |
89 | ||
90 | static struct snd_pcm *pxa2xx_ac97_pcm; | |
91 | static struct snd_ac97 *pxa2xx_ac97_ac97; | |
92 | ||
93 | static int pxa2xx_ac97_pcm_startup(struct snd_pcm_substream *substream) | |
94 | { | |
95 | struct snd_pcm_runtime *runtime = substream->runtime; | |
96 | pxa2xx_audio_ops_t *platform_ops; | |
97 | int r; | |
98 | ||
99 | runtime->hw.channels_min = 2; | |
100 | runtime->hw.channels_max = 2; | |
101 | ||
102 | r = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? | |
103 | AC97_RATES_FRONT_DAC : AC97_RATES_ADC; | |
104 | runtime->hw.rates = pxa2xx_ac97_ac97->rates[r]; | |
105 | snd_pcm_limit_hw_rates(runtime); | |
106 | ||
107 | platform_ops = substream->pcm->card->dev->platform_data; | |
108 | if (platform_ops && platform_ops->startup) | |
109 | return platform_ops->startup(substream, platform_ops->priv); | |
110 | else | |
111 | return 0; | |
112 | } | |
113 | ||
114 | static void pxa2xx_ac97_pcm_shutdown(struct snd_pcm_substream *substream) | |
115 | { | |
116 | pxa2xx_audio_ops_t *platform_ops; | |
117 | ||
118 | platform_ops = substream->pcm->card->dev->platform_data; | |
119 | if (platform_ops && platform_ops->shutdown) | |
120 | platform_ops->shutdown(substream, platform_ops->priv); | |
121 | } | |
122 | ||
123 | static int pxa2xx_ac97_pcm_prepare(struct snd_pcm_substream *substream) | |
124 | { | |
125 | struct snd_pcm_runtime *runtime = substream->runtime; | |
126 | int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? | |
127 | AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE; | |
128 | return snd_ac97_set_rate(pxa2xx_ac97_ac97, reg, runtime->rate); | |
129 | } | |
130 | ||
131 | static struct pxa2xx_pcm_client pxa2xx_ac97_pcm_client = { | |
132 | .playback_params = &pxa2xx_ac97_pcm_out, | |
133 | .capture_params = &pxa2xx_ac97_pcm_in, | |
134 | .startup = pxa2xx_ac97_pcm_startup, | |
135 | .shutdown = pxa2xx_ac97_pcm_shutdown, | |
136 | .prepare = pxa2xx_ac97_pcm_prepare, | |
137 | }; | |
138 | ||
139 | #ifdef CONFIG_PM_SLEEP | |
140 | ||
141 | static int pxa2xx_ac97_do_suspend(struct snd_card *card) | |
142 | { | |
143 | pxa2xx_audio_ops_t *platform_ops = card->dev->platform_data; | |
144 | ||
145 | snd_power_change_state(card, SNDRV_CTL_POWER_D3cold); | |
146 | snd_pcm_suspend_all(pxa2xx_ac97_pcm); | |
147 | snd_ac97_suspend(pxa2xx_ac97_ac97); | |
148 | if (platform_ops && platform_ops->suspend) | |
149 | platform_ops->suspend(platform_ops->priv); | |
150 | ||
151 | return pxa2xx_ac97_hw_suspend(); | |
152 | } | |
153 | ||
154 | static int pxa2xx_ac97_do_resume(struct snd_card *card) | |
155 | { | |
156 | pxa2xx_audio_ops_t *platform_ops = card->dev->platform_data; | |
157 | int rc; | |
158 | ||
159 | rc = pxa2xx_ac97_hw_resume(); | |
160 | if (rc) | |
161 | return rc; | |
162 | ||
163 | if (platform_ops && platform_ops->resume) | |
164 | platform_ops->resume(platform_ops->priv); | |
165 | snd_ac97_resume(pxa2xx_ac97_ac97); | |
166 | snd_power_change_state(card, SNDRV_CTL_POWER_D0); | |
167 | ||
168 | return 0; | |
169 | } | |
170 | ||
171 | static int pxa2xx_ac97_suspend(struct device *dev) | |
172 | { | |
173 | struct snd_card *card = dev_get_drvdata(dev); | |
174 | int ret = 0; | |
175 | ||
176 | if (card) | |
177 | ret = pxa2xx_ac97_do_suspend(card); | |
178 | ||
179 | return ret; | |
180 | } | |
181 | ||
182 | static int pxa2xx_ac97_resume(struct device *dev) | |
183 | { | |
184 | struct snd_card *card = dev_get_drvdata(dev); | |
185 | int ret = 0; | |
186 | ||
187 | if (card) | |
188 | ret = pxa2xx_ac97_do_resume(card); | |
189 | ||
190 | return ret; | |
191 | } | |
192 | ||
193 | static SIMPLE_DEV_PM_OPS(pxa2xx_ac97_pm_ops, pxa2xx_ac97_suspend, pxa2xx_ac97_resume); | |
194 | #endif | |
195 | ||
196 | static int pxa2xx_ac97_probe(struct platform_device *dev) | |
197 | { | |
198 | struct snd_card *card; | |
199 | struct snd_ac97_bus *ac97_bus; | |
200 | struct snd_ac97_template ac97_template; | |
201 | int ret; | |
202 | pxa2xx_audio_ops_t *pdata = dev->dev.platform_data; | |
203 | ||
204 | if (dev->id >= 0) { | |
205 | dev_err(&dev->dev, "PXA2xx has only one AC97 port.\n"); | |
206 | ret = -ENXIO; | |
207 | goto err_dev; | |
208 | } | |
209 | ||
210 | ret = snd_card_new(&dev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, | |
211 | THIS_MODULE, 0, &card); | |
212 | if (ret < 0) | |
213 | goto err; | |
214 | ||
215 | strlcpy(card->driver, dev->dev.driver->name, sizeof(card->driver)); | |
216 | ||
217 | ret = pxa2xx_pcm_new(card, &pxa2xx_ac97_pcm_client, &pxa2xx_ac97_pcm); | |
218 | if (ret) | |
219 | goto err; | |
220 | ||
221 | ret = pxa2xx_ac97_hw_probe(dev); | |
222 | if (ret) | |
223 | goto err; | |
224 | ||
225 | ret = snd_ac97_bus(card, 0, &pxa2xx_ac97_ops, NULL, &ac97_bus); | |
226 | if (ret) | |
227 | goto err_remove; | |
228 | memset(&ac97_template, 0, sizeof(ac97_template)); | |
229 | ret = snd_ac97_mixer(ac97_bus, &ac97_template, &pxa2xx_ac97_ac97); | |
230 | if (ret) | |
231 | goto err_remove; | |
232 | ||
233 | snprintf(card->shortname, sizeof(card->shortname), | |
234 | "%s", snd_ac97_get_short_name(pxa2xx_ac97_ac97)); | |
235 | snprintf(card->longname, sizeof(card->longname), | |
236 | "%s (%s)", dev->dev.driver->name, card->mixername); | |
237 | ||
238 | if (pdata && pdata->codec_pdata[0]) | |
239 | snd_ac97_dev_add_pdata(ac97_bus->codec[0], pdata->codec_pdata[0]); | |
240 | ret = snd_card_register(card); | |
241 | if (ret == 0) { | |
242 | platform_set_drvdata(dev, card); | |
243 | return 0; | |
244 | } | |
245 | ||
246 | err_remove: | |
247 | pxa2xx_ac97_hw_remove(dev); | |
248 | err: | |
249 | if (card) | |
250 | snd_card_free(card); | |
251 | err_dev: | |
252 | return ret; | |
253 | } | |
254 | ||
255 | static int pxa2xx_ac97_remove(struct platform_device *dev) | |
256 | { | |
257 | struct snd_card *card = platform_get_drvdata(dev); | |
258 | ||
259 | if (card) { | |
260 | snd_card_free(card); | |
261 | pxa2xx_ac97_hw_remove(dev); | |
262 | } | |
263 | ||
264 | return 0; | |
265 | } | |
266 | ||
267 | static struct platform_driver pxa2xx_ac97_driver = { | |
268 | .probe = pxa2xx_ac97_probe, | |
269 | .remove = pxa2xx_ac97_remove, | |
270 | .driver = { | |
271 | .name = "pxa2xx-ac97", | |
272 | #ifdef CONFIG_PM_SLEEP | |
273 | .pm = &pxa2xx_ac97_pm_ops, | |
274 | #endif | |
275 | }, | |
276 | }; | |
277 | ||
278 | module_platform_driver(pxa2xx_ac97_driver); | |
279 | ||
280 | MODULE_AUTHOR("Nicolas Pitre"); | |
281 | MODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip"); | |
282 | MODULE_LICENSE("GPL"); | |
283 | MODULE_ALIAS("platform:pxa2xx-ac97"); |