Linux-libre 5.4.49-gnu
[librecmc/linux-libre.git] / sound / isa / azt2320.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3     card-azt2320.c - driver for Aztech Systems AZT2320 based soundcards.
4     Copyright (C) 1999-2000 by Massimo Piccioni <dafastidio@libero.it>
5
6 */
7
8 /*
9     This driver should provide support for most Aztech AZT2320 based cards.
10     Several AZT2316 chips are also supported/tested, but autoprobe doesn't
11     work: all module option have to be set.
12
13     No docs available for us at Aztech headquarters !!!   Unbelievable ...
14     No other help obtained.
15
16     Thanks to Rainer Wiesner <rainer.wiesner@01019freenet.de> for the WSS
17     activation method (full-duplex audio!).
18 */
19
20 #include <linux/io.h>
21 #include <linux/delay.h>
22 #include <linux/init.h>
23 #include <linux/time.h>
24 #include <linux/wait.h>
25 #include <linux/pnp.h>
26 #include <linux/module.h>
27 #include <sound/core.h>
28 #include <sound/initval.h>
29 #include <sound/wss.h>
30 #include <sound/mpu401.h>
31 #include <sound/opl3.h>
32
33 #define PFX "azt2320: "
34
35 MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
36 MODULE_DESCRIPTION("Aztech Systems AZT2320");
37 MODULE_LICENSE("GPL");
38 MODULE_SUPPORTED_DEVICE("{{Aztech Systems,PRO16V},"
39                 "{Aztech Systems,AZT2320},"
40                 "{Aztech Systems,AZT3300},"
41                 "{Aztech Systems,AZT2320},"
42                 "{Aztech Systems,AZT3000}}");
43
44 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;      /* Index 0-MAX */
45 static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;       /* ID for this card */
46 static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
47 static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;     /* PnP setup */
48 static long wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
49 static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
50 static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;  /* PnP setup */
51 static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;        /* Pnp setup */
52 static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;    /* Pnp setup */
53 static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;       /* PnP setup */
54 static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;       /* PnP setup */
55
56 module_param_array(index, int, NULL, 0444);
57 MODULE_PARM_DESC(index, "Index value for azt2320 based soundcard.");
58 module_param_array(id, charp, NULL, 0444);
59 MODULE_PARM_DESC(id, "ID string for azt2320 based soundcard.");
60 module_param_array(enable, bool, NULL, 0444);
61 MODULE_PARM_DESC(enable, "Enable azt2320 based soundcard.");
62
63 struct snd_card_azt2320 {
64         int dev_no;
65         struct pnp_dev *dev;
66         struct pnp_dev *devmpu;
67         struct snd_wss *chip;
68 };
69
70 static const struct pnp_card_device_id snd_azt2320_pnpids[] = {
71         /* PRO16V */
72         { .id = "AZT1008", .devs = { { "AZT1008" }, { "AZT2001" }, } },
73         /* Aztech Sound Galaxy 16 */
74         { .id = "AZT2320", .devs = { { "AZT0001" }, { "AZT0002" }, } },
75         /* Packard Bell Sound III 336 AM/SP */
76         { .id = "AZT3000", .devs = { { "AZT1003" }, { "AZT2001" }, } },
77         /* AT3300 */
78         { .id = "AZT3002", .devs = { { "AZT1004" }, { "AZT2001" }, } },
79         /* --- */
80         { .id = "AZT3005", .devs = { { "AZT1003" }, { "AZT2001" }, } },
81         /* --- */
82         { .id = "AZT3011", .devs = { { "AZT1003" }, { "AZT2001" }, } },
83         { .id = "" }    /* end */
84 };
85
86 MODULE_DEVICE_TABLE(pnp_card, snd_azt2320_pnpids);
87
88 #define DRIVER_NAME     "snd-card-azt2320"
89
90 static int snd_card_azt2320_pnp(int dev, struct snd_card_azt2320 *acard,
91                                 struct pnp_card_link *card,
92                                 const struct pnp_card_device_id *id)
93 {
94         struct pnp_dev *pdev;
95         int err;
96
97         acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
98         if (acard->dev == NULL)
99                 return -ENODEV;
100
101         acard->devmpu = pnp_request_card_device(card, id->devs[1].id, NULL);
102
103         pdev = acard->dev;
104
105         err = pnp_activate_dev(pdev);
106         if (err < 0) {
107                 snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n");
108                 return err;
109         }
110         port[dev] = pnp_port_start(pdev, 0);
111         fm_port[dev] = pnp_port_start(pdev, 1);
112         wss_port[dev] = pnp_port_start(pdev, 2);
113         dma1[dev] = pnp_dma(pdev, 0);
114         dma2[dev] = pnp_dma(pdev, 1);
115         irq[dev] = pnp_irq(pdev, 0);
116
117         pdev = acard->devmpu;
118         if (pdev != NULL) {
119                 err = pnp_activate_dev(pdev);
120                 if (err < 0)
121                         goto __mpu_error;
122                 mpu_port[dev] = pnp_port_start(pdev, 0);
123                 mpu_irq[dev] = pnp_irq(pdev, 0);
124         } else {
125              __mpu_error:
126                 if (pdev) {
127                         pnp_release_card_device(pdev);
128                         snd_printk(KERN_ERR PFX "MPU401 pnp configure failure, skipping\n");
129                 }
130                 acard->devmpu = NULL;
131                 mpu_port[dev] = -1;
132         }
133
134         return 0;
135 }
136
137 /* same of snd_sbdsp_command by Jaroslav Kysela */
138 static int snd_card_azt2320_command(unsigned long port, unsigned char val)
139 {
140         int i;
141         unsigned long limit;
142
143         limit = jiffies + HZ / 10;
144         for (i = 50000; i && time_after(limit, jiffies); i--)
145                 if (!(inb(port + 0x0c) & 0x80)) {
146                         outb(val, port + 0x0c);
147                         return 0;
148                 }
149         return -EBUSY;
150 }
151
152 static int snd_card_azt2320_enable_wss(unsigned long port)
153 {
154         int error;
155
156         if ((error = snd_card_azt2320_command(port, 0x09)))
157                 return error;
158         if ((error = snd_card_azt2320_command(port, 0x00)))
159                 return error;
160
161         mdelay(5);
162         return 0;
163 }
164
165 static int snd_card_azt2320_probe(int dev,
166                                   struct pnp_card_link *pcard,
167                                   const struct pnp_card_device_id *pid)
168 {
169         int error;
170         struct snd_card *card;
171         struct snd_card_azt2320 *acard;
172         struct snd_wss *chip;
173         struct snd_opl3 *opl3;
174
175         error = snd_card_new(&pcard->card->dev,
176                              index[dev], id[dev], THIS_MODULE,
177                              sizeof(struct snd_card_azt2320), &card);
178         if (error < 0)
179                 return error;
180         acard = card->private_data;
181
182         if ((error = snd_card_azt2320_pnp(dev, acard, pcard, pid))) {
183                 snd_card_free(card);
184                 return error;
185         }
186
187         if ((error = snd_card_azt2320_enable_wss(port[dev]))) {
188                 snd_card_free(card);
189                 return error;
190         }
191
192         error = snd_wss_create(card, wss_port[dev], -1,
193                                irq[dev],
194                                dma1[dev], dma2[dev],
195                                WSS_HW_DETECT, 0, &chip);
196         if (error < 0) {
197                 snd_card_free(card);
198                 return error;
199         }
200
201         strcpy(card->driver, "AZT2320");
202         strcpy(card->shortname, "Aztech AZT2320");
203         sprintf(card->longname, "%s, WSS at 0x%lx, irq %i, dma %i&%i",
204                 card->shortname, chip->port, irq[dev], dma1[dev], dma2[dev]);
205
206         error = snd_wss_pcm(chip, 0);
207         if (error < 0) {
208                 snd_card_free(card);
209                 return error;
210         }
211         error = snd_wss_mixer(chip);
212         if (error < 0) {
213                 snd_card_free(card);
214                 return error;
215         }
216         error = snd_wss_timer(chip, 0);
217         if (error < 0) {
218                 snd_card_free(card);
219                 return error;
220         }
221
222         if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) {
223                 if (snd_mpu401_uart_new(card, 0, MPU401_HW_AZT2320,
224                                 mpu_port[dev], 0,
225                                 mpu_irq[dev], NULL) < 0)
226                         snd_printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n", mpu_port[dev]);
227         }
228
229         if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) {
230                 if (snd_opl3_create(card,
231                                     fm_port[dev], fm_port[dev] + 2,
232                                     OPL3_HW_AUTO, 0, &opl3) < 0) {
233                         snd_printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n",
234                                    fm_port[dev], fm_port[dev] + 2);
235                 } else {
236                         if ((error = snd_opl3_timer_new(opl3, 1, 2)) < 0) {
237                                 snd_card_free(card);
238                                 return error;
239                         }
240                         if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
241                                 snd_card_free(card);
242                                 return error;
243                         }
244                 }
245         }
246
247         if ((error = snd_card_register(card)) < 0) {
248                 snd_card_free(card);
249                 return error;
250         }
251         pnp_set_card_drvdata(pcard, card);
252         return 0;
253 }
254
255 static unsigned int azt2320_devices;
256
257 static int snd_azt2320_pnp_detect(struct pnp_card_link *card,
258                                   const struct pnp_card_device_id *id)
259 {
260         static int dev;
261         int res;
262
263         for ( ; dev < SNDRV_CARDS; dev++) {
264                 if (!enable[dev])
265                         continue;
266                 res = snd_card_azt2320_probe(dev, card, id);
267                 if (res < 0)
268                         return res;
269                 dev++;
270                 azt2320_devices++;
271                 return 0;
272         }
273         return -ENODEV;
274 }
275
276 static void snd_azt2320_pnp_remove(struct pnp_card_link *pcard)
277 {
278         snd_card_free(pnp_get_card_drvdata(pcard));
279         pnp_set_card_drvdata(pcard, NULL);
280 }
281
282 #ifdef CONFIG_PM
283 static int snd_azt2320_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state)
284 {
285         struct snd_card *card = pnp_get_card_drvdata(pcard);
286         struct snd_card_azt2320 *acard = card->private_data;
287         struct snd_wss *chip = acard->chip;
288
289         snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
290         chip->suspend(chip);
291         return 0;
292 }
293
294 static int snd_azt2320_pnp_resume(struct pnp_card_link *pcard)
295 {
296         struct snd_card *card = pnp_get_card_drvdata(pcard);
297         struct snd_card_azt2320 *acard = card->private_data;
298         struct snd_wss *chip = acard->chip;
299
300         chip->resume(chip);
301         snd_power_change_state(card, SNDRV_CTL_POWER_D0);
302         return 0;
303 }
304 #endif
305
306 static struct pnp_card_driver azt2320_pnpc_driver = {
307         .flags          = PNP_DRIVER_RES_DISABLE,
308         .name           = "azt2320",
309         .id_table       = snd_azt2320_pnpids,
310         .probe          = snd_azt2320_pnp_detect,
311         .remove         = snd_azt2320_pnp_remove,
312 #ifdef CONFIG_PM
313         .suspend        = snd_azt2320_pnp_suspend,
314         .resume         = snd_azt2320_pnp_resume,
315 #endif
316 };
317
318 static int __init alsa_card_azt2320_init(void)
319 {
320         int err;
321
322         err = pnp_register_card_driver(&azt2320_pnpc_driver);
323         if (err)
324                 return err;
325
326         if (!azt2320_devices) {
327                 pnp_unregister_card_driver(&azt2320_pnpc_driver);
328 #ifdef MODULE
329                 snd_printk(KERN_ERR "no AZT2320 based soundcards found\n");
330 #endif
331                 return -ENODEV;
332         }
333         return 0;
334 }
335
336 static void __exit alsa_card_azt2320_exit(void)
337 {
338         pnp_unregister_card_driver(&azt2320_pnpc_driver);
339 }
340
341 module_init(alsa_card_azt2320_init)
342 module_exit(alsa_card_azt2320_exit)