]>
Commit | Line | Data |
---|---|---|
1a59d1b8 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
1da177e4 LT |
2 | |
3 | /* | |
4 | card-ad1816a.c - driver for ADI SoundPort AD1816A based soundcards. | |
5 | Copyright (C) 2000 by Massimo Piccioni <dafastidio@libero.it> | |
6 | ||
1da177e4 LT |
7 | */ |
8 | ||
1da177e4 LT |
9 | #include <linux/init.h> |
10 | #include <linux/time.h> | |
11 | #include <linux/wait.h> | |
12 | #include <linux/pnp.h> | |
65a77217 | 13 | #include <linux/module.h> |
1da177e4 LT |
14 | #include <sound/core.h> |
15 | #include <sound/initval.h> | |
16 | #include <sound/ad1816a.h> | |
17 | #include <sound/mpu401.h> | |
18 | #include <sound/opl3.h> | |
19 | ||
20 | #define PFX "ad1816a: " | |
21 | ||
22 | MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>"); | |
23 | MODULE_DESCRIPTION("AD1816A, AD1815"); | |
24 | MODULE_LICENSE("GPL"); | |
25 | MODULE_SUPPORTED_DEVICE("{{Highscreen,Sound-Boostar 16 3D}," | |
26 | "{Analog Devices,AD1815}," | |
27 | "{Analog Devices,AD1816A}," | |
28 | "{TerraTec,Base 64}," | |
29 | "{TerraTec,AudioSystem EWS64S}," | |
30 | "{Aztech/Newcom SC-16 3D}," | |
31 | "{Shark Predator ISA}}"); | |
32 | ||
33 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 1-MAX */ | |
34 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | |
a67ff6a5 | 35 | static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ |
1da177e4 LT |
36 | static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ |
37 | static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ | |
38 | static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ | |
39 | static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */ | |
40 | static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */ | |
41 | static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ | |
42 | static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ | |
5b8f7f73 | 43 | static int clockfreq[SNDRV_CARDS]; |
1da177e4 LT |
44 | |
45 | module_param_array(index, int, NULL, 0444); | |
46 | MODULE_PARM_DESC(index, "Index value for ad1816a based soundcard."); | |
47 | module_param_array(id, charp, NULL, 0444); | |
48 | MODULE_PARM_DESC(id, "ID string for ad1816a based soundcard."); | |
49 | module_param_array(enable, bool, NULL, 0444); | |
50 | MODULE_PARM_DESC(enable, "Enable ad1816a based soundcard."); | |
5b8f7f73 TI |
51 | module_param_array(clockfreq, int, NULL, 0444); |
52 | MODULE_PARM_DESC(clockfreq, "Clock frequency for ad1816a driver (default = 0)."); | |
1da177e4 | 53 | |
287b546c | 54 | static const struct pnp_card_device_id snd_ad1816a_pnpids[] = { |
1da177e4 LT |
55 | /* Analog Devices AD1815 */ |
56 | { .id = "ADS7150", .devs = { { .id = "ADS7150" }, { .id = "ADS7151" } } }, | |
92bb010c TI |
57 | /* Analog Device AD1816? */ |
58 | { .id = "ADS7180", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } }, | |
1da177e4 LT |
59 | /* Analog Devices AD1816A - added by Kenneth Platz <kxp@atl.hp.com> */ |
60 | { .id = "ADS7181", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } }, | |
61 | /* Analog Devices AD1816A - Aztech/Newcom SC-16 3D */ | |
62 | { .id = "AZT1022", .devs = { { .id = "AZT1018" }, { .id = "AZT2002" } } }, | |
63 | /* Highscreen Sound-Boostar 16 3D - added by Stefan Behnel */ | |
64 | { .id = "LWC1061", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } }, | |
65 | /* Highscreen Sound-Boostar 16 3D */ | |
66 | { .id = "MDK1605", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } }, | |
67 | /* Shark Predator ISA - added by Ken Arromdee */ | |
68 | { .id = "SMM7180", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } }, | |
36463a96 | 69 | /* Analog Devices AD1816A - Terratec AudioSystem EWS64 S */ |
1da177e4 | 70 | { .id = "TER1112", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } }, |
36463a96 RH |
71 | /* Analog Devices AD1816A - Terratec AudioSystem EWS64 S */ |
72 | { .id = "TER1112", .devs = { { .id = "TER1100" }, { .id = "TER1101" } } }, | |
1da177e4 LT |
73 | /* Analog Devices AD1816A - Terratec Base 64 */ |
74 | { .id = "TER1411", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } }, | |
75 | /* end */ | |
76 | { .id = "" } | |
77 | }; | |
78 | ||
79 | MODULE_DEVICE_TABLE(pnp_card, snd_ad1816a_pnpids); | |
80 | ||
81 | ||
82 | #define DRIVER_NAME "snd-card-ad1816a" | |
83 | ||
84 | ||
1bff292e BP |
85 | static int snd_card_ad1816a_pnp(int dev, struct pnp_card_link *card, |
86 | const struct pnp_card_device_id *id) | |
1da177e4 LT |
87 | { |
88 | struct pnp_dev *pdev; | |
1da177e4 LT |
89 | int err; |
90 | ||
c86b6b45 OZ |
91 | pdev = pnp_request_card_device(card, id->devs[0].id, NULL); |
92 | if (pdev == NULL) | |
1da177e4 | 93 | return -EBUSY; |
109c53f8 | 94 | |
1da177e4 LT |
95 | err = pnp_activate_dev(pdev); |
96 | if (err < 0) { | |
97 | printk(KERN_ERR PFX "AUDIO PnP configure failure\n"); | |
1da177e4 LT |
98 | return -EBUSY; |
99 | } | |
100 | ||
101 | port[dev] = pnp_port_start(pdev, 2); | |
102 | fm_port[dev] = pnp_port_start(pdev, 1); | |
103 | dma1[dev] = pnp_dma(pdev, 0); | |
104 | dma2[dev] = pnp_dma(pdev, 1); | |
105 | irq[dev] = pnp_irq(pdev, 0); | |
106 | ||
c86b6b45 OZ |
107 | pdev = pnp_request_card_device(card, id->devs[1].id, NULL); |
108 | if (pdev == NULL) { | |
109 | mpu_port[dev] = -1; | |
110 | snd_printk(KERN_WARNING PFX "MPU401 device busy, skipping.\n"); | |
2944275b | 111 | return 0; |
c86b6b45 | 112 | } |
1da177e4 | 113 | |
1da177e4 LT |
114 | err = pnp_activate_dev(pdev); |
115 | if (err < 0) { | |
116 | printk(KERN_ERR PFX "MPU401 PnP configure failure\n"); | |
117 | mpu_port[dev] = -1; | |
1da177e4 LT |
118 | } else { |
119 | mpu_port[dev] = pnp_port_start(pdev, 0); | |
120 | mpu_irq[dev] = pnp_irq(pdev, 0); | |
121 | } | |
122 | ||
1da177e4 LT |
123 | return 0; |
124 | } | |
125 | ||
1bff292e BP |
126 | static int snd_card_ad1816a_probe(int dev, struct pnp_card_link *pcard, |
127 | const struct pnp_card_device_id *pid) | |
1da177e4 LT |
128 | { |
129 | int error; | |
cbdd0dd1 | 130 | struct snd_card *card; |
cbdd0dd1 TI |
131 | struct snd_ad1816a *chip; |
132 | struct snd_opl3 *opl3; | |
1da177e4 | 133 | |
4323cc4d TI |
134 | error = snd_card_new(&pcard->card->dev, |
135 | index[dev], id[dev], THIS_MODULE, | |
136 | sizeof(struct snd_ad1816a), &card); | |
c95eadd2 TI |
137 | if (error < 0) |
138 | return error; | |
c86b6b45 | 139 | chip = card->private_data; |
1da177e4 | 140 | |
c86b6b45 | 141 | if ((error = snd_card_ad1816a_pnp(dev, pcard, pid))) { |
1da177e4 LT |
142 | snd_card_free(card); |
143 | return error; | |
144 | } | |
1da177e4 LT |
145 | |
146 | if ((error = snd_ad1816a_create(card, port[dev], | |
147 | irq[dev], | |
148 | dma1[dev], | |
149 | dma2[dev], | |
c86b6b45 | 150 | chip)) < 0) { |
1da177e4 LT |
151 | snd_card_free(card); |
152 | return error; | |
153 | } | |
5b8f7f73 TI |
154 | if (clockfreq[dev] >= 5000 && clockfreq[dev] <= 100000) |
155 | chip->clock_freq = clockfreq[dev]; | |
1da177e4 LT |
156 | |
157 | strcpy(card->driver, "AD1816A"); | |
158 | strcpy(card->shortname, "ADI SoundPort AD1816A"); | |
159 | sprintf(card->longname, "%s, SS at 0x%lx, irq %d, dma %d&%d", | |
160 | card->shortname, chip->port, irq[dev], dma1[dev], dma2[dev]); | |
161 | ||
7f605418 | 162 | if ((error = snd_ad1816a_pcm(chip, 0)) < 0) { |
1da177e4 LT |
163 | snd_card_free(card); |
164 | return error; | |
165 | } | |
166 | ||
167 | if ((error = snd_ad1816a_mixer(chip)) < 0) { | |
168 | snd_card_free(card); | |
169 | return error; | |
170 | } | |
171 | ||
7f605418 | 172 | error = snd_ad1816a_timer(chip, 0); |
a17ac45a KH |
173 | if (error < 0) { |
174 | snd_card_free(card); | |
175 | return error; | |
176 | } | |
177 | ||
1da177e4 LT |
178 | if (mpu_port[dev] > 0) { |
179 | if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, | |
dba8b469 | 180 | mpu_port[dev], 0, mpu_irq[dev], |
1da177e4 LT |
181 | NULL) < 0) |
182 | printk(KERN_ERR PFX "no MPU-401 device at 0x%lx.\n", mpu_port[dev]); | |
183 | } | |
184 | ||
185 | if (fm_port[dev] > 0) { | |
186 | if (snd_opl3_create(card, | |
187 | fm_port[dev], fm_port[dev] + 2, | |
188 | OPL3_HW_AUTO, 0, &opl3) < 0) { | |
189 | printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx.\n", fm_port[dev], fm_port[dev] + 2); | |
190 | } else { | |
aa9c293a KH |
191 | error = snd_opl3_hwdep_new(opl3, 0, 1, NULL); |
192 | if (error < 0) { | |
1da177e4 LT |
193 | snd_card_free(card); |
194 | return error; | |
195 | } | |
196 | } | |
197 | } | |
198 | ||
199 | if ((error = snd_card_register(card)) < 0) { | |
200 | snd_card_free(card); | |
201 | return error; | |
202 | } | |
203 | pnp_set_card_drvdata(pcard, card); | |
204 | return 0; | |
205 | } | |
206 | ||
1bff292e | 207 | static unsigned int ad1816a_devices; |
5f53f4e2 | 208 | |
1bff292e BP |
209 | static int snd_ad1816a_pnp_detect(struct pnp_card_link *card, |
210 | const struct pnp_card_device_id *id) | |
1da177e4 LT |
211 | { |
212 | static int dev; | |
213 | int res; | |
214 | ||
215 | for ( ; dev < SNDRV_CARDS; dev++) { | |
216 | if (!enable[dev]) | |
217 | continue; | |
218 | res = snd_card_ad1816a_probe(dev, card, id); | |
219 | if (res < 0) | |
220 | return res; | |
221 | dev++; | |
5f53f4e2 | 222 | ad1816a_devices++; |
1da177e4 LT |
223 | return 0; |
224 | } | |
225 | return -ENODEV; | |
226 | } | |
227 | ||
1bff292e | 228 | static void snd_ad1816a_pnp_remove(struct pnp_card_link *pcard) |
1da177e4 | 229 | { |
175cdcfb TI |
230 | snd_card_free(pnp_get_card_drvdata(pcard)); |
231 | pnp_set_card_drvdata(pcard, NULL); | |
1da177e4 LT |
232 | } |
233 | ||
6f0fa660 OZ |
234 | #ifdef CONFIG_PM |
235 | static int snd_ad1816a_pnp_suspend(struct pnp_card_link *pcard, | |
236 | pm_message_t state) | |
237 | { | |
238 | struct snd_card *card = pnp_get_card_drvdata(pcard); | |
239 | ||
240 | snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); | |
241 | snd_ad1816a_suspend(card->private_data); | |
242 | return 0; | |
243 | } | |
244 | ||
245 | static int snd_ad1816a_pnp_resume(struct pnp_card_link *pcard) | |
246 | { | |
247 | struct snd_card *card = pnp_get_card_drvdata(pcard); | |
248 | ||
249 | snd_ad1816a_resume(card->private_data); | |
250 | snd_power_change_state(card, SNDRV_CTL_POWER_D0); | |
251 | return 0; | |
252 | } | |
253 | #endif | |
254 | ||
1da177e4 LT |
255 | static struct pnp_card_driver ad1816a_pnpc_driver = { |
256 | .flags = PNP_DRIVER_RES_DISABLE, | |
257 | .name = "ad1816a", | |
258 | .id_table = snd_ad1816a_pnpids, | |
259 | .probe = snd_ad1816a_pnp_detect, | |
1bff292e | 260 | .remove = snd_ad1816a_pnp_remove, |
6f0fa660 OZ |
261 | #ifdef CONFIG_PM |
262 | .suspend = snd_ad1816a_pnp_suspend, | |
263 | .resume = snd_ad1816a_pnp_resume, | |
264 | #endif | |
1da177e4 LT |
265 | }; |
266 | ||
267 | static int __init alsa_card_ad1816a_init(void) | |
268 | { | |
5f53f4e2 BH |
269 | int err; |
270 | ||
271 | err = pnp_register_card_driver(&ad1816a_pnpc_driver); | |
272 | if (err) | |
273 | return err; | |
1da177e4 | 274 | |
5f53f4e2 | 275 | if (!ad1816a_devices) { |
1da177e4 | 276 | pnp_unregister_card_driver(&ad1816a_pnpc_driver); |
175cdcfb | 277 | #ifdef MODULE |
1da177e4 | 278 | printk(KERN_ERR "no AD1816A based soundcards found.\n"); |
1da177e4 | 279 | #endif /* MODULE */ |
175cdcfb TI |
280 | return -ENODEV; |
281 | } | |
282 | return 0; | |
1da177e4 LT |
283 | } |
284 | ||
285 | static void __exit alsa_card_ad1816a_exit(void) | |
286 | { | |
287 | pnp_unregister_card_driver(&ad1816a_pnpc_driver); | |
288 | } | |
289 | ||
290 | module_init(alsa_card_ad1816a_init) | |
291 | module_exit(alsa_card_ad1816a_exit) |