]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Driver for generic ESS AudioDrive ESx688 soundcards | |
c1017a4c | 3 | * Copyright (c) by Jaroslav Kysela <perex@perex.cz> |
1da177e4 LT |
4 | * |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, write to the Free Software | |
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
19 | * | |
20 | */ | |
21 | ||
1da177e4 | 22 | #include <linux/init.h> |
2a571ed1 | 23 | #include <linux/err.h> |
ae596186 | 24 | #include <linux/isa.h> |
a20971b2 | 25 | #include <linux/isapnp.h> |
1da177e4 LT |
26 | #include <linux/time.h> |
27 | #include <linux/wait.h> | |
65a77217 | 28 | #include <linux/module.h> |
2a571ed1 | 29 | #include <asm/dma.h> |
1da177e4 LT |
30 | #include <sound/core.h> |
31 | #include <sound/es1688.h> | |
32 | #include <sound/mpu401.h> | |
33 | #include <sound/opl3.h> | |
1da177e4 LT |
34 | #define SNDRV_LEGACY_FIND_FREE_IRQ |
35 | #define SNDRV_LEGACY_FIND_FREE_DMA | |
36 | #include <sound/initval.h> | |
37 | ||
ae596186 RH |
38 | #define CRD_NAME "Generic ESS ES1688/ES688 AudioDrive" |
39 | #define DEV_NAME "es1688" | |
40 | ||
41 | MODULE_DESCRIPTION(CRD_NAME); | |
c1017a4c | 42 | MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); |
1da177e4 LT |
43 | MODULE_LICENSE("GPL"); |
44 | MODULE_SUPPORTED_DEVICE("{{ESS,ES688 PnP AudioDrive,pnp:ESS0100}," | |
45 | "{ESS,ES1688 PnP AudioDrive,pnp:ESS0102}," | |
46 | "{ESS,ES688 AudioDrive,pnp:ESS6881}," | |
47 | "{ESS,ES1688 AudioDrive,pnp:ESS1681}}"); | |
48 | ||
a20971b2 KH |
49 | MODULE_ALIAS("snd_es968"); |
50 | ||
1da177e4 LT |
51 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ |
52 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | |
a20971b2 | 53 | #ifdef CONFIG_PNP |
a67ff6a5 | 54 | static bool isapnp[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; |
a20971b2 | 55 | #endif |
a67ff6a5 | 56 | static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ |
1da177e4 | 57 | static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260 */ |
880abd42 | 58 | static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* Usually 0x388 */ |
1da177e4 LT |
59 | static long mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1}; |
60 | static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ | |
61 | static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ | |
62 | static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ | |
63 | ||
64 | module_param_array(index, int, NULL, 0444); | |
ae596186 | 65 | MODULE_PARM_DESC(index, "Index value for " CRD_NAME " soundcard."); |
1da177e4 | 66 | module_param_array(id, charp, NULL, 0444); |
ae596186 | 67 | MODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard."); |
1da177e4 | 68 | module_param_array(enable, bool, NULL, 0444); |
a20971b2 KH |
69 | #ifdef CONFIG_PNP |
70 | module_param_array(isapnp, bool, NULL, 0444); | |
71 | MODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard."); | |
72 | #endif | |
ae596186 | 73 | MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard."); |
e992ef57 | 74 | module_param_hw_array(port, long, ioport, NULL, 0444); |
ae596186 | 75 | MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver."); |
e992ef57 | 76 | module_param_hw_array(mpu_port, long, ioport, NULL, 0444); |
ae596186 | 77 | MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " CRD_NAME " driver."); |
e992ef57 DH |
78 | module_param_hw_array(irq, int, irq, NULL, 0444); |
79 | module_param_hw_array(fm_port, long, ioport, NULL, 0444); | |
880abd42 | 80 | MODULE_PARM_DESC(fm_port, "FM port # for ES1688 driver."); |
ae596186 | 81 | MODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver."); |
e992ef57 | 82 | module_param_hw_array(mpu_irq, int, irq, NULL, 0444); |
ae596186 | 83 | MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " CRD_NAME " driver."); |
e992ef57 | 84 | module_param_hw_array(dma8, int, dma, NULL, 0444); |
ae596186 | 85 | MODULE_PARM_DESC(dma8, "8-bit DMA # for " CRD_NAME " driver."); |
f7a9275d | 86 | |
a20971b2 KH |
87 | #ifdef CONFIG_PNP |
88 | #define is_isapnp_selected(dev) isapnp[dev] | |
89 | #else | |
90 | #define is_isapnp_selected(dev) 0 | |
91 | #endif | |
92 | ||
1bff292e | 93 | static int snd_es1688_match(struct device *dev, unsigned int n) |
ae596186 | 94 | { |
a20971b2 | 95 | return enable[n] && !is_isapnp_selected(n); |
ae596186 | 96 | } |
1da177e4 | 97 | |
1bff292e BP |
98 | static int snd_es1688_legacy_create(struct snd_card *card, |
99 | struct device *dev, unsigned int n) | |
1da177e4 | 100 | { |
a20971b2 | 101 | struct snd_es1688 *chip = card->private_data; |
019608b6 | 102 | static long possible_ports[] = {0x220, 0x240, 0x260}; |
1da177e4 LT |
103 | static int possible_irqs[] = {5, 9, 10, 7, -1}; |
104 | static int possible_dmas[] = {1, 3, 0, -1}; | |
ae596186 | 105 | |
019608b6 RH |
106 | int i, error; |
107 | ||
108 | if (irq[n] == SNDRV_AUTO_IRQ) { | |
109 | irq[n] = snd_legacy_find_free_irq(possible_irqs); | |
110 | if (irq[n] < 0) { | |
0418ff0c | 111 | dev_err(dev, "unable to find a free IRQ\n"); |
019608b6 RH |
112 | return -EBUSY; |
113 | } | |
114 | } | |
115 | if (dma8[n] == SNDRV_AUTO_DMA) { | |
116 | dma8[n] = snd_legacy_find_free_dma(possible_dmas); | |
117 | if (dma8[n] < 0) { | |
0418ff0c | 118 | dev_err(dev, "unable to find a free DMA\n"); |
019608b6 RH |
119 | return -EBUSY; |
120 | } | |
121 | } | |
122 | ||
123 | if (port[n] != SNDRV_AUTO_PORT) | |
396fa827 KH |
124 | return snd_es1688_create(card, chip, port[n], mpu_port[n], |
125 | irq[n], mpu_irq[n], dma8[n], ES1688_HW_AUTO); | |
019608b6 RH |
126 | |
127 | i = 0; | |
128 | do { | |
129 | port[n] = possible_ports[i]; | |
396fa827 KH |
130 | error = snd_es1688_create(card, chip, port[n], mpu_port[n], |
131 | irq[n], mpu_irq[n], dma8[n], ES1688_HW_AUTO); | |
019608b6 RH |
132 | } while (error < 0 && ++i < ARRAY_SIZE(possible_ports)); |
133 | ||
134 | return error; | |
135 | } | |
136 | ||
1bff292e | 137 | static int snd_es1688_probe(struct snd_card *card, unsigned int n) |
019608b6 | 138 | { |
a20971b2 | 139 | struct snd_es1688 *chip = card->private_data; |
d3a7e476 | 140 | struct snd_opl3 *opl3; |
ae596186 RH |
141 | int error; |
142 | ||
4b8ab885 | 143 | error = snd_es1688_pcm(card, chip, 0); |
ae596186 | 144 | if (error < 0) |
a20971b2 | 145 | return error; |
43bcd973 | 146 | |
396fa827 | 147 | error = snd_es1688_mixer(card, chip); |
ae596186 | 148 | if (error < 0) |
a20971b2 | 149 | return error; |
1da177e4 | 150 | |
b0fb75ad | 151 | strlcpy(card->driver, "ES1688", sizeof(card->driver)); |
4b8ab885 | 152 | strlcpy(card->shortname, chip->pcm->name, sizeof(card->shortname)); |
b0fb75ad | 153 | snprintf(card->longname, sizeof(card->longname), |
4b8ab885 | 154 | "%s at 0x%lx, irq %i, dma %i", chip->pcm->name, chip->port, |
b0fb75ad | 155 | chip->irq, chip->dma8); |
1da177e4 | 156 | |
880abd42 KH |
157 | if (fm_port[n] == SNDRV_AUTO_PORT) |
158 | fm_port[n] = port[n]; /* share the same port */ | |
159 | ||
160 | if (fm_port[n] > 0) { | |
161 | if (snd_opl3_create(card, fm_port[n], fm_port[n] + 2, | |
162 | OPL3_HW_OPL3, 0, &opl3) < 0) | |
a20971b2 | 163 | dev_warn(card->dev, |
880abd42 KH |
164 | "opl3 not detected at 0x%lx\n", fm_port[n]); |
165 | else { | |
166 | error = snd_opl3_hwdep_new(opl3, 0, 1, NULL); | |
167 | if (error < 0) | |
a20971b2 | 168 | return error; |
880abd42 | 169 | } |
1da177e4 LT |
170 | } |
171 | ||
019608b6 RH |
172 | if (mpu_irq[n] >= 0 && mpu_irq[n] != SNDRV_AUTO_IRQ && |
173 | chip->mpu_port > 0) { | |
174 | error = snd_mpu401_uart_new(card, 0, MPU401_HW_ES1688, | |
175 | chip->mpu_port, 0, | |
dba8b469 | 176 | mpu_irq[n], NULL); |
ae596186 | 177 | if (error < 0) |
a20971b2 | 178 | return error; |
1da177e4 | 179 | } |
43bcd973 | 180 | |
a20971b2 KH |
181 | return snd_card_register(card); |
182 | } | |
183 | ||
1bff292e | 184 | static int snd_es1688_isa_probe(struct device *dev, unsigned int n) |
a20971b2 KH |
185 | { |
186 | struct snd_card *card; | |
187 | int error; | |
188 | ||
4323cc4d TI |
189 | error = snd_card_new(dev, index[n], id[n], THIS_MODULE, |
190 | sizeof(struct snd_es1688), &card); | |
a20971b2 KH |
191 | if (error < 0) |
192 | return error; | |
193 | ||
194 | error = snd_es1688_legacy_create(card, dev, n); | |
195 | if (error < 0) | |
196 | goto out; | |
197 | ||
a20971b2 | 198 | error = snd_es1688_probe(card, n); |
ae596186 RH |
199 | if (error < 0) |
200 | goto out; | |
43bcd973 | 201 | |
ae596186 | 202 | dev_set_drvdata(dev, card); |
1da177e4 | 203 | |
a20971b2 KH |
204 | return 0; |
205 | out: | |
206 | snd_card_free(card); | |
ae596186 | 207 | return error; |
1da177e4 LT |
208 | } |
209 | ||
1bff292e | 210 | static int snd_es1688_isa_remove(struct device *dev, unsigned int n) |
1da177e4 | 211 | { |
ae596186 | 212 | snd_card_free(dev_get_drvdata(dev)); |
2a571ed1 | 213 | return 0; |
1da177e4 LT |
214 | } |
215 | ||
ae596186 RH |
216 | static struct isa_driver snd_es1688_driver = { |
217 | .match = snd_es1688_match, | |
a20971b2 | 218 | .probe = snd_es1688_isa_probe, |
1bff292e | 219 | .remove = snd_es1688_isa_remove, |
ae596186 RH |
220 | #if 0 /* FIXME */ |
221 | .suspend = snd_es1688_suspend, | |
222 | .resume = snd_es1688_resume, | |
223 | #endif | |
2a571ed1 | 224 | .driver = { |
ae596186 RH |
225 | .name = DEV_NAME |
226 | } | |
2a571ed1 TI |
227 | }; |
228 | ||
a20971b2 KH |
229 | static int snd_es968_pnp_is_probed; |
230 | ||
231 | #ifdef CONFIG_PNP | |
1bff292e BP |
232 | static int snd_card_es968_pnp(struct snd_card *card, unsigned int n, |
233 | struct pnp_card_link *pcard, | |
234 | const struct pnp_card_device_id *pid) | |
a20971b2 KH |
235 | { |
236 | struct snd_es1688 *chip = card->private_data; | |
237 | struct pnp_dev *pdev; | |
238 | int error; | |
239 | ||
240 | pdev = pnp_request_card_device(pcard, pid->devs[0].id, NULL); | |
241 | if (pdev == NULL) | |
242 | return -ENODEV; | |
243 | ||
244 | error = pnp_activate_dev(pdev); | |
245 | if (error < 0) { | |
246 | snd_printk(KERN_ERR "ES968 pnp configure failure\n"); | |
247 | return error; | |
248 | } | |
249 | port[n] = pnp_port_start(pdev, 0); | |
250 | dma8[n] = pnp_dma(pdev, 0); | |
251 | irq[n] = pnp_irq(pdev, 0); | |
252 | ||
253 | return snd_es1688_create(card, chip, port[n], mpu_port[n], irq[n], | |
254 | mpu_irq[n], dma8[n], ES1688_HW_AUTO); | |
255 | } | |
256 | ||
1bff292e BP |
257 | static int snd_es968_pnp_detect(struct pnp_card_link *pcard, |
258 | const struct pnp_card_device_id *pid) | |
a20971b2 KH |
259 | { |
260 | struct snd_card *card; | |
261 | static unsigned int dev; | |
262 | int error; | |
263 | struct snd_es1688 *chip; | |
264 | ||
265 | if (snd_es968_pnp_is_probed) | |
266 | return -EBUSY; | |
267 | for ( ; dev < SNDRV_CARDS; dev++) { | |
268 | if (enable[dev] && isapnp[dev]) | |
269 | break; | |
270 | } | |
b0fb75ad DC |
271 | if (dev == SNDRV_CARDS) |
272 | return -ENODEV; | |
a20971b2 | 273 | |
4323cc4d TI |
274 | error = snd_card_new(&pcard->card->dev, |
275 | index[dev], id[dev], THIS_MODULE, | |
276 | sizeof(struct snd_es1688), &card); | |
a20971b2 KH |
277 | if (error < 0) |
278 | return error; | |
279 | chip = card->private_data; | |
280 | ||
281 | error = snd_card_es968_pnp(card, dev, pcard, pid); | |
282 | if (error < 0) { | |
283 | snd_card_free(card); | |
284 | return error; | |
285 | } | |
a20971b2 KH |
286 | error = snd_es1688_probe(card, dev); |
287 | if (error < 0) | |
288 | return error; | |
289 | pnp_set_card_drvdata(pcard, card); | |
290 | snd_es968_pnp_is_probed = 1; | |
291 | return 0; | |
292 | } | |
293 | ||
1bff292e | 294 | static void snd_es968_pnp_remove(struct pnp_card_link *pcard) |
a20971b2 KH |
295 | { |
296 | snd_card_free(pnp_get_card_drvdata(pcard)); | |
297 | pnp_set_card_drvdata(pcard, NULL); | |
298 | snd_es968_pnp_is_probed = 0; | |
299 | } | |
300 | ||
301 | #ifdef CONFIG_PM | |
302 | static int snd_es968_pnp_suspend(struct pnp_card_link *pcard, | |
303 | pm_message_t state) | |
304 | { | |
305 | struct snd_card *card = pnp_get_card_drvdata(pcard); | |
306 | struct snd_es1688 *chip = card->private_data; | |
307 | ||
308 | snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); | |
309 | snd_pcm_suspend_all(chip->pcm); | |
310 | return 0; | |
311 | } | |
312 | ||
313 | static int snd_es968_pnp_resume(struct pnp_card_link *pcard) | |
314 | { | |
315 | struct snd_card *card = pnp_get_card_drvdata(pcard); | |
316 | struct snd_es1688 *chip = card->private_data; | |
317 | ||
318 | snd_es1688_reset(chip); | |
319 | snd_power_change_state(card, SNDRV_CTL_POWER_D0); | |
320 | return 0; | |
321 | } | |
322 | #endif | |
323 | ||
4f624a1c | 324 | static const struct pnp_card_device_id snd_es968_pnpids[] = { |
a20971b2 KH |
325 | { .id = "ESS0968", .devs = { { "@@@0968" }, } }, |
326 | { .id = "ESS0968", .devs = { { "ESS0968" }, } }, | |
327 | { .id = "", } /* end */ | |
328 | }; | |
329 | ||
330 | MODULE_DEVICE_TABLE(pnp_card, snd_es968_pnpids); | |
331 | ||
332 | static struct pnp_card_driver es968_pnpc_driver = { | |
333 | .flags = PNP_DRIVER_RES_DISABLE, | |
334 | .name = DEV_NAME " PnP", | |
335 | .id_table = snd_es968_pnpids, | |
336 | .probe = snd_es968_pnp_detect, | |
1bff292e | 337 | .remove = snd_es968_pnp_remove, |
a20971b2 KH |
338 | #ifdef CONFIG_PM |
339 | .suspend = snd_es968_pnp_suspend, | |
340 | .resume = snd_es968_pnp_resume, | |
341 | #endif | |
342 | }; | |
343 | #endif | |
344 | ||
1da177e4 LT |
345 | static int __init alsa_card_es1688_init(void) |
346 | { | |
a20971b2 KH |
347 | #ifdef CONFIG_PNP |
348 | pnp_register_card_driver(&es968_pnpc_driver); | |
349 | if (snd_es968_pnp_is_probed) | |
350 | return 0; | |
351 | pnp_unregister_card_driver(&es968_pnpc_driver); | |
352 | #endif | |
ae596186 | 353 | return isa_register_driver(&snd_es1688_driver, SNDRV_CARDS); |
1da177e4 LT |
354 | } |
355 | ||
356 | static void __exit alsa_card_es1688_exit(void) | |
357 | { | |
a20971b2 KH |
358 | if (!snd_es968_pnp_is_probed) { |
359 | isa_unregister_driver(&snd_es1688_driver); | |
360 | return; | |
361 | } | |
362 | #ifdef CONFIG_PNP | |
363 | pnp_unregister_card_driver(&es968_pnpc_driver); | |
364 | #endif | |
1da177e4 LT |
365 | } |
366 | ||
ae596186 RH |
367 | module_init(alsa_card_es1688_init); |
368 | module_exit(alsa_card_es1688_exit); |