1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | |
3 | /* |
4 | card-als100.c - driver for Avance Logic ALS100 based soundcards. |
5 | Copyright (C) 1999-2000 by Massimo Piccioni <dafastidio@libero.it> |
6 | Copyright (C) 1999-2002 by Massimo Piccioni <dafastidio@libero.it> |
7 | |
8 | Thanks to Pierfrancesco 'qM2' Passerini. |
9 | |
10 | Generalised for soundcards based on DT-0196 and ALS-007 chips |
11 | by Jonathan Woithe <jwoithe@just42.net>: June 2002. |
12 | |
13 | */ |
14 | |
15 | #include <linux/init.h> |
16 | #include <linux/wait.h> |
17 | #include <linux/time.h> |
18 | #include <linux/pnp.h> |
19 | #include <linux/module.h> |
20 | #include <sound/core.h> |
21 | #include <sound/initval.h> |
22 | #include <sound/mpu401.h> |
23 | #include <sound/opl3.h> |
24 | #include <sound/sb.h> |
25 | |
26 | #define PFX "als100: " |
27 | |
28 | MODULE_DESCRIPTION("Avance Logic ALS007/ALS1X0" ); |
29 | MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>" ); |
30 | MODULE_LICENSE("GPL" ); |
31 | |
32 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ |
33 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ |
34 | static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ |
35 | static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ |
36 | static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ |
37 | static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ |
38 | static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* PnP setup */ |
39 | static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* PnP setup */ |
40 | static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ |
41 | static int dma16[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ |
42 | |
43 | module_param_array(index, int, NULL, 0444); |
44 | MODULE_PARM_DESC(index, "Index value for Avance Logic based soundcard." ); |
45 | module_param_array(id, charp, NULL, 0444); |
46 | MODULE_PARM_DESC(id, "ID string for Avance Logic based soundcard." ); |
47 | module_param_array(enable, bool, NULL, 0444); |
48 | MODULE_PARM_DESC(enable, "Enable Avance Logic based soundcard." ); |
49 | |
50 | MODULE_ALIAS("snd-dt019x" ); |
51 | |
52 | struct snd_card_als100 { |
53 | struct pnp_dev *dev; |
54 | struct pnp_dev *devmpu; |
55 | struct pnp_dev *devopl; |
56 | struct snd_sb *chip; |
57 | }; |
58 | |
59 | static const struct pnp_card_device_id snd_als100_pnpids[] = { |
60 | /* DT197A30 */ |
61 | { .id = "RWB1688" , |
62 | .devs = { { "@@@0001" }, { "@X@0001" }, { "@H@0001" } }, |
63 | .driver_data = SB_HW_DT019X }, |
64 | /* DT0196 / ALS-007 */ |
65 | { .id = "ALS0007" , |
66 | .devs = { { "@@@0001" }, { "@X@0001" }, { "@H@0001" } }, |
67 | .driver_data = SB_HW_DT019X }, |
68 | /* ALS100 - PRO16PNP */ |
69 | { .id = "ALS0001" , |
70 | .devs = { { "@@@0001" }, { "@X@0001" }, { "@H@0001" } }, |
71 | .driver_data = SB_HW_ALS100 }, |
72 | /* ALS110 - MF1000 - Digimate 3D Sound */ |
73 | { .id = "ALS0110" , |
74 | .devs = { { "@@@1001" }, { "@X@1001" }, { "@H@1001" } }, |
75 | .driver_data = SB_HW_ALS100 }, |
76 | /* ALS120 */ |
77 | { .id = "ALS0120" , |
78 | .devs = { { "@@@2001" }, { "@X@2001" }, { "@H@2001" } }, |
79 | .driver_data = SB_HW_ALS100 }, |
80 | /* ALS200 */ |
81 | { .id = "ALS0200" , |
82 | .devs = { { "@@@0020" }, { "@X@0020" }, { "@H@0001" } }, |
83 | .driver_data = SB_HW_ALS100 }, |
84 | /* ALS200 OEM */ |
85 | { .id = "ALS0200" , |
86 | .devs = { { "@@@0020" }, { "@X@0020" }, { "@H@0020" } }, |
87 | .driver_data = SB_HW_ALS100 }, |
88 | /* RTL3000 */ |
89 | { .id = "RTL3000" , |
90 | .devs = { { "@@@2001" }, { "@X@2001" }, { "@H@2001" } }, |
91 | .driver_data = SB_HW_ALS100 }, |
92 | { .id = "" } /* end */ |
93 | }; |
94 | |
95 | MODULE_DEVICE_TABLE(pnp_card, snd_als100_pnpids); |
96 | |
97 | static int snd_card_als100_pnp(int dev, struct snd_card_als100 *acard, |
98 | struct pnp_card_link *card, |
99 | const struct pnp_card_device_id *id) |
100 | { |
101 | struct pnp_dev *pdev; |
102 | int err; |
103 | |
104 | acard->dev = pnp_request_card_device(clink: card, id: id->devs[0].id, NULL); |
105 | if (acard->dev == NULL) |
106 | return -ENODEV; |
107 | |
108 | acard->devmpu = pnp_request_card_device(clink: card, id: id->devs[1].id, from: acard->dev); |
109 | acard->devopl = pnp_request_card_device(clink: card, id: id->devs[2].id, from: acard->dev); |
110 | |
111 | pdev = acard->dev; |
112 | |
113 | err = pnp_activate_dev(dev: pdev); |
114 | if (err < 0) { |
115 | snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n" ); |
116 | return err; |
117 | } |
118 | port[dev] = pnp_port_start(dev: pdev, bar: 0); |
119 | if (id->driver_data == SB_HW_DT019X) |
120 | dma8[dev] = pnp_dma(dev: pdev, bar: 0); |
121 | else { |
122 | dma8[dev] = pnp_dma(dev: pdev, bar: 1); |
123 | dma16[dev] = pnp_dma(dev: pdev, bar: 0); |
124 | } |
125 | irq[dev] = pnp_irq(dev: pdev, bar: 0); |
126 | |
127 | pdev = acard->devmpu; |
128 | if (pdev != NULL) { |
129 | err = pnp_activate_dev(dev: pdev); |
130 | if (err < 0) |
131 | goto __mpu_error; |
132 | mpu_port[dev] = pnp_port_start(dev: pdev, bar: 0); |
133 | mpu_irq[dev] = pnp_irq(dev: pdev, bar: 0); |
134 | } else { |
135 | __mpu_error: |
136 | if (pdev) { |
137 | pnp_release_card_device(dev: pdev); |
138 | snd_printk(KERN_ERR PFX "MPU401 pnp configure failure, skipping\n" ); |
139 | } |
140 | acard->devmpu = NULL; |
141 | mpu_port[dev] = -1; |
142 | } |
143 | |
144 | pdev = acard->devopl; |
145 | if (pdev != NULL) { |
146 | err = pnp_activate_dev(dev: pdev); |
147 | if (err < 0) |
148 | goto __fm_error; |
149 | fm_port[dev] = pnp_port_start(dev: pdev, bar: 0); |
150 | } else { |
151 | __fm_error: |
152 | if (pdev) { |
153 | pnp_release_card_device(dev: pdev); |
154 | snd_printk(KERN_ERR PFX "OPL3 pnp configure failure, skipping\n" ); |
155 | } |
156 | acard->devopl = NULL; |
157 | fm_port[dev] = -1; |
158 | } |
159 | |
160 | return 0; |
161 | } |
162 | |
163 | static int snd_card_als100_probe(int dev, |
164 | struct pnp_card_link *pcard, |
165 | const struct pnp_card_device_id *pid) |
166 | { |
167 | int error; |
168 | struct snd_sb *chip; |
169 | struct snd_card *card; |
170 | struct snd_card_als100 *acard; |
171 | struct snd_opl3 *opl3; |
172 | |
173 | error = snd_devm_card_new(parent: &pcard->card->dev, |
174 | idx: index[dev], xid: id[dev], THIS_MODULE, |
175 | extra_size: sizeof(struct snd_card_als100), card_ret: &card); |
176 | if (error < 0) |
177 | return error; |
178 | acard = card->private_data; |
179 | |
180 | error = snd_card_als100_pnp(dev, acard, card: pcard, id: pid); |
181 | if (error) |
182 | return error; |
183 | |
184 | if (pid->driver_data == SB_HW_DT019X) |
185 | dma16[dev] = -1; |
186 | |
187 | error = snd_sbdsp_create(card, port: port[dev], irq: irq[dev], |
188 | irq_handler: snd_sb16dsp_interrupt, |
189 | dma8: dma8[dev], dma16: dma16[dev], |
190 | hardware: pid->driver_data, |
191 | r_chip: &chip); |
192 | if (error < 0) |
193 | return error; |
194 | acard->chip = chip; |
195 | |
196 | if (pid->driver_data == SB_HW_DT019X) { |
197 | strcpy(p: card->driver, q: "DT-019X" ); |
198 | strcpy(p: card->shortname, q: "Diamond Tech. DT-019X" ); |
199 | snprintf(buf: card->longname, size: sizeof(card->longname), |
200 | fmt: "Diamond Tech. DT-019X, %s at 0x%lx, irq %d, dma %d" , |
201 | chip->name, chip->port, irq[dev], dma8[dev]); |
202 | } else { |
203 | strcpy(p: card->driver, q: "ALS100" ); |
204 | strcpy(p: card->shortname, q: "Avance Logic ALS100" ); |
205 | snprintf(buf: card->longname, size: sizeof(card->longname), |
206 | fmt: "Avance Logic ALS100, %s at 0x%lx, irq %d, dma %d&%d" , |
207 | chip->name, chip->port, irq[dev], dma8[dev], |
208 | dma16[dev]); |
209 | } |
210 | |
211 | error = snd_sb16dsp_pcm(chip, device: 0); |
212 | if (error < 0) |
213 | return error; |
214 | |
215 | error = snd_sbmixer_new(chip); |
216 | if (error < 0) |
217 | return error; |
218 | |
219 | if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) { |
220 | int mpu_type = MPU401_HW_ALS100; |
221 | |
222 | if (mpu_irq[dev] == SNDRV_AUTO_IRQ) |
223 | mpu_irq[dev] = -1; |
224 | |
225 | if (pid->driver_data == SB_HW_DT019X) |
226 | mpu_type = MPU401_HW_MPU401; |
227 | |
228 | if (snd_mpu401_uart_new(card, device: 0, |
229 | hardware: mpu_type, |
230 | port: mpu_port[dev], info_flags: 0, |
231 | irq: mpu_irq[dev], |
232 | NULL) < 0) |
233 | snd_printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n" , mpu_port[dev]); |
234 | } |
235 | |
236 | if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) { |
237 | if (snd_opl3_create(card, |
238 | l_port: fm_port[dev], r_port: fm_port[dev] + 2, |
239 | OPL3_HW_AUTO, integrated: 0, opl3: &opl3) < 0) { |
240 | snd_printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n" , |
241 | fm_port[dev], fm_port[dev] + 2); |
242 | } else { |
243 | error = snd_opl3_timer_new(opl3, timer1_dev: 0, timer2_dev: 1); |
244 | if (error < 0) |
245 | return error; |
246 | error = snd_opl3_hwdep_new(opl3, device: 0, seq_device: 1, NULL); |
247 | if (error < 0) |
248 | return error; |
249 | } |
250 | } |
251 | |
252 | error = snd_card_register(card); |
253 | if (error < 0) |
254 | return error; |
255 | pnp_set_card_drvdata(pcard, data: card); |
256 | return 0; |
257 | } |
258 | |
259 | static unsigned int als100_devices; |
260 | |
261 | static int snd_als100_pnp_detect(struct pnp_card_link *card, |
262 | const struct pnp_card_device_id *id) |
263 | { |
264 | static int dev; |
265 | int res; |
266 | |
267 | for ( ; dev < SNDRV_CARDS; dev++) { |
268 | if (!enable[dev]) |
269 | continue; |
270 | res = snd_card_als100_probe(dev, pcard: card, pid: id); |
271 | if (res < 0) |
272 | return res; |
273 | dev++; |
274 | als100_devices++; |
275 | return 0; |
276 | } |
277 | return -ENODEV; |
278 | } |
279 | |
280 | #ifdef CONFIG_PM |
281 | static int snd_als100_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state) |
282 | { |
283 | struct snd_card *card = pnp_get_card_drvdata(pcard); |
284 | struct snd_card_als100 *acard = card->private_data; |
285 | struct snd_sb *chip = acard->chip; |
286 | |
287 | snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); |
288 | snd_sbmixer_suspend(chip); |
289 | return 0; |
290 | } |
291 | |
292 | static int snd_als100_pnp_resume(struct pnp_card_link *pcard) |
293 | { |
294 | struct snd_card *card = pnp_get_card_drvdata(pcard); |
295 | struct snd_card_als100 *acard = card->private_data; |
296 | struct snd_sb *chip = acard->chip; |
297 | |
298 | snd_sbdsp_reset(chip); |
299 | snd_sbmixer_resume(chip); |
300 | snd_power_change_state(card, SNDRV_CTL_POWER_D0); |
301 | return 0; |
302 | } |
303 | #endif |
304 | |
305 | static struct pnp_card_driver als100_pnpc_driver = { |
306 | .flags = PNP_DRIVER_RES_DISABLE, |
307 | .name = "als100" , |
308 | .id_table = snd_als100_pnpids, |
309 | .probe = snd_als100_pnp_detect, |
310 | #ifdef CONFIG_PM |
311 | .suspend = snd_als100_pnp_suspend, |
312 | .resume = snd_als100_pnp_resume, |
313 | #endif |
314 | }; |
315 | |
316 | static int __init alsa_card_als100_init(void) |
317 | { |
318 | int err; |
319 | |
320 | err = pnp_register_card_driver(drv: &als100_pnpc_driver); |
321 | if (err) |
322 | return err; |
323 | |
324 | if (!als100_devices) { |
325 | pnp_unregister_card_driver(drv: &als100_pnpc_driver); |
326 | #ifdef MODULE |
327 | snd_printk(KERN_ERR "no Avance Logic based soundcards found\n" ); |
328 | #endif |
329 | return -ENODEV; |
330 | } |
331 | return 0; |
332 | } |
333 | |
334 | static void __exit alsa_card_als100_exit(void) |
335 | { |
336 | pnp_unregister_card_driver(drv: &als100_pnpc_driver); |
337 | } |
338 | |
339 | module_init(alsa_card_als100_init) |
340 | module_exit(alsa_card_als100_exit) |
341 | |