xref: /openbmc/linux/sound/isa/azt2320.c (revision bef7a78d)
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)
343