Linux Audio

Check our new training course

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