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