1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | |
3 | /* |
4 | card-ad1816a.c - driver for ADI SoundPort AD1816A based soundcards. |
5 | Copyright (C) 2000 by Massimo Piccioni <dafastidio@libero.it> |
6 | |
7 | */ |
8 | |
9 | #include <linux/init.h> |
10 | #include <linux/time.h> |
11 | #include <linux/wait.h> |
12 | #include <linux/pnp.h> |
13 | #include <linux/module.h> |
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 | |
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 */ |
28 | static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ |
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 */ |
36 | static int clockfreq[SNDRV_CARDS]; |
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." ); |
44 | module_param_array(clockfreq, int, NULL, 0444); |
45 | MODULE_PARM_DESC(clockfreq, "Clock frequency for ad1816a driver (default = 0)." ); |
46 | |
47 | static const struct pnp_card_device_id snd_ad1816a_pnpids[] = { |
48 | /* Analog Devices AD1815 */ |
49 | { .id = "ADS7150" , .devs = { { .id = "ADS7150" }, { .id = "ADS7151" } } }, |
50 | /* Analog Devices AD1816? */ |
51 | { .id = "ADS7180" , .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } }, |
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" } } }, |
62 | /* Analog Devices AD1816A - Terratec AudioSystem EWS64 S */ |
63 | { .id = "TER1112" , .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } }, |
64 | /* Analog Devices AD1816A - Terratec AudioSystem EWS64 S */ |
65 | { .id = "TER1112" , .devs = { { .id = "TER1100" }, { .id = "TER1101" } } }, |
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 | |
78 | static int snd_card_ad1816a_pnp(int dev, struct pnp_card_link *card, |
79 | const struct pnp_card_device_id *id) |
80 | { |
81 | struct pnp_dev *pdev; |
82 | int err; |
83 | |
84 | pdev = pnp_request_card_device(clink: card, id: id->devs[0].id, NULL); |
85 | if (pdev == NULL) |
86 | return -EBUSY; |
87 | |
88 | err = pnp_activate_dev(dev: pdev); |
89 | if (err < 0) { |
90 | printk(KERN_ERR PFX "AUDIO PnP configure failure\n" ); |
91 | return -EBUSY; |
92 | } |
93 | |
94 | port[dev] = pnp_port_start(dev: pdev, bar: 2); |
95 | fm_port[dev] = pnp_port_start(dev: pdev, bar: 1); |
96 | dma1[dev] = pnp_dma(dev: pdev, bar: 0); |
97 | dma2[dev] = pnp_dma(dev: pdev, bar: 1); |
98 | irq[dev] = pnp_irq(dev: pdev, bar: 0); |
99 | |
100 | pdev = pnp_request_card_device(clink: card, id: 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" ); |
104 | return 0; |
105 | } |
106 | |
107 | err = pnp_activate_dev(dev: pdev); |
108 | if (err < 0) { |
109 | printk(KERN_ERR PFX "MPU401 PnP configure failure\n" ); |
110 | mpu_port[dev] = -1; |
111 | } else { |
112 | mpu_port[dev] = pnp_port_start(dev: pdev, bar: 0); |
113 | mpu_irq[dev] = pnp_irq(dev: pdev, bar: 0); |
114 | } |
115 | |
116 | return 0; |
117 | } |
118 | |
119 | static int snd_card_ad1816a_probe(int dev, struct pnp_card_link *pcard, |
120 | const struct pnp_card_device_id *pid) |
121 | { |
122 | int error; |
123 | struct snd_card *card; |
124 | struct snd_ad1816a *chip; |
125 | struct snd_opl3 *opl3; |
126 | |
127 | error = snd_devm_card_new(parent: &pcard->card->dev, |
128 | idx: index[dev], xid: id[dev], THIS_MODULE, |
129 | extra_size: sizeof(struct snd_ad1816a), card_ret: &card); |
130 | if (error < 0) |
131 | return error; |
132 | chip = card->private_data; |
133 | |
134 | error = snd_card_ad1816a_pnp(dev, card: pcard, id: pid); |
135 | if (error) |
136 | return error; |
137 | |
138 | error = snd_ad1816a_create(card, port: port[dev], |
139 | irq: irq[dev], |
140 | dma1: dma1[dev], |
141 | dma2: dma2[dev], |
142 | chip); |
143 | if (error) |
144 | return error; |
145 | if (clockfreq[dev] >= 5000 && clockfreq[dev] <= 100000) |
146 | chip->clock_freq = clockfreq[dev]; |
147 | |
148 | strcpy(p: card->driver, q: "AD1816A" ); |
149 | strcpy(p: card->shortname, q: "ADI SoundPort AD1816A" ); |
150 | sprintf(buf: card->longname, fmt: "%s, SS at 0x%lx, irq %d, dma %d&%d" , |
151 | card->shortname, chip->port, irq[dev], dma1[dev], dma2[dev]); |
152 | |
153 | error = snd_ad1816a_pcm(chip, device: 0); |
154 | if (error < 0) |
155 | return error; |
156 | |
157 | error = snd_ad1816a_mixer(chip); |
158 | if (error < 0) |
159 | return error; |
160 | |
161 | error = snd_ad1816a_timer(chip, device: 0); |
162 | if (error < 0) |
163 | return error; |
164 | |
165 | if (mpu_port[dev] > 0) { |
166 | if (snd_mpu401_uart_new(card, device: 0, MPU401_HW_MPU401, |
167 | port: mpu_port[dev], info_flags: 0, irq: mpu_irq[dev], |
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 | l_port: fm_port[dev], r_port: fm_port[dev] + 2, |
175 | OPL3_HW_AUTO, integrated: 0, opl3: &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 { |
178 | error = snd_opl3_hwdep_new(opl3, device: 0, seq_device: 1, NULL); |
179 | if (error < 0) |
180 | return error; |
181 | } |
182 | } |
183 | |
184 | error = snd_card_register(card); |
185 | if (error < 0) |
186 | return error; |
187 | pnp_set_card_drvdata(pcard, data: card); |
188 | return 0; |
189 | } |
190 | |
191 | static unsigned int ad1816a_devices; |
192 | |
193 | static int snd_ad1816a_pnp_detect(struct pnp_card_link *card, |
194 | const struct pnp_card_device_id *id) |
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, pcard: card, pid: id); |
203 | if (res < 0) |
204 | return res; |
205 | dev++; |
206 | ad1816a_devices++; |
207 | return 0; |
208 | } |
209 | return -ENODEV; |
210 | } |
211 | |
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(chip: 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(chip: card->private_data); |
228 | snd_power_change_state(card, SNDRV_CTL_POWER_D0); |
229 | return 0; |
230 | } |
231 | #endif |
232 | |
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, |
238 | #ifdef CONFIG_PM |
239 | .suspend = snd_ad1816a_pnp_suspend, |
240 | .resume = snd_ad1816a_pnp_resume, |
241 | #endif |
242 | }; |
243 | |
244 | static int __init alsa_card_ad1816a_init(void) |
245 | { |
246 | int err; |
247 | |
248 | err = pnp_register_card_driver(drv: &ad1816a_pnpc_driver); |
249 | if (err) |
250 | return err; |
251 | |
252 | if (!ad1816a_devices) { |
253 | pnp_unregister_card_driver(drv: &ad1816a_pnpc_driver); |
254 | #ifdef MODULE |
255 | printk(KERN_ERR "no AD1816A based soundcards found.\n" ); |
256 | #endif /* MODULE */ |
257 | return -ENODEV; |
258 | } |
259 | return 0; |
260 | } |
261 | |
262 | static void __exit alsa_card_ad1816a_exit(void) |
263 | { |
264 | pnp_unregister_card_driver(drv: &ad1816a_pnpc_driver); |
265 | } |
266 | |
267 | module_init(alsa_card_ad1816a_init) |
268 | module_exit(alsa_card_ad1816a_exit) |
269 | |