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 <sound/driver.h>
33#include <asm/io.h>
34#include <linux/delay.h>
35#include <linux/init.h>
36#include <linux/time.h>
37#include <linux/wait.h>
38#include <linux/pnp.h>
39#include <linux/moduleparam.h>
40#include <sound/core.h>
41#include <sound/initval.h>
42#include <sound/cs4231.h>
43#include <sound/mpu401.h>
44#include <sound/opl3.h>
45
46#define PFX "azt2320: "
47
48MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
49MODULE_DESCRIPTION("Aztech Systems AZT2320");
50MODULE_LICENSE("GPL");
51MODULE_SUPPORTED_DEVICE("{{Aztech Systems,PRO16V},"
52		"{Aztech Systems,AZT2320},"
53		"{Aztech Systems,AZT3300},"
54		"{Aztech Systems,AZT2320},"
55		"{Aztech Systems,AZT3000}}");
56
57static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
58static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
59static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
60static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
61static long wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
62static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
63static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
64static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* Pnp setup */
65static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* Pnp setup */
66static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* PnP setup */
67static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* PnP setup */
68
69module_param_array(index, int, NULL, 0444);
70MODULE_PARM_DESC(index, "Index value for azt2320 based soundcard.");
71module_param_array(id, charp, NULL, 0444);
72MODULE_PARM_DESC(id, "ID string for azt2320 based soundcard.");
73module_param_array(enable, bool, NULL, 0444);
74MODULE_PARM_DESC(enable, "Enable azt2320 based soundcard.");
75module_param_array(port, long, NULL, 0444);
76MODULE_PARM_DESC(port, "Port # for azt2320 driver.");
77module_param_array(wss_port, long, NULL, 0444);
78MODULE_PARM_DESC(wss_port, "WSS Port # for azt2320 driver.");
79module_param_array(mpu_port, long, NULL, 0444);
80MODULE_PARM_DESC(mpu_port, "MPU-401 port # for azt2320 driver.");
81module_param_array(fm_port, long, NULL, 0444);
82MODULE_PARM_DESC(fm_port, "FM port # for azt2320 driver.");
83module_param_array(irq, int, NULL, 0444);
84MODULE_PARM_DESC(irq, "IRQ # for azt2320 driver.");
85module_param_array(mpu_irq, int, NULL, 0444);
86MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for azt2320 driver.");
87module_param_array(dma1, int, NULL, 0444);
88MODULE_PARM_DESC(dma1, "1st DMA # for azt2320 driver.");
89module_param_array(dma2, int, NULL, 0444);
90MODULE_PARM_DESC(dma2, "2nd DMA # for azt2320 driver.");
91
92struct snd_card_azt2320 {
93	int dev_no;
94	struct pnp_dev *dev;
95	struct pnp_dev *devmpu;
96	struct snd_cs4231 *chip;
97};
98
99static struct pnp_card_device_id snd_azt2320_pnpids[] = {
100	/* PRO16V */
101	{ .id = "AZT1008", .devs = { { "AZT1008" }, { "AZT2001" }, } },
102	/* Aztech Sound Galaxy 16 */
103	{ .id = "AZT2320", .devs = { { "AZT0001" }, { "AZT0002" }, } },
104	/* Packard Bell Sound III 336 AM/SP */
105	{ .id = "AZT3000", .devs = { { "AZT1003" }, { "AZT2001" }, } },
106	/* AT3300 */
107	{ .id = "AZT3002", .devs = { { "AZT1004" }, { "AZT2001" }, } },
108	/* --- */
109	{ .id = "AZT3005", .devs = { { "AZT1003" }, { "AZT2001" }, } },
110	/* --- */
111	{ .id = "AZT3011", .devs = { { "AZT1003" }, { "AZT2001" }, } },
112	{ .id = "" }	/* end */
113};
114
115MODULE_DEVICE_TABLE(pnp_card, snd_azt2320_pnpids);
116
117#define	DRIVER_NAME	"snd-card-azt2320"
118
119static int __devinit snd_card_azt2320_pnp(int dev, struct snd_card_azt2320 *acard,
120					  struct pnp_card_link *card,
121					  const struct pnp_card_device_id *id)
122{
123	struct pnp_dev *pdev;
124	struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL);
125	int err;
126
127	if (!cfg)
128		return -ENOMEM;
129
130	acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
131	if (acard->dev == NULL) {
132		kfree(cfg);
133		return -ENODEV;
134	}
135
136	acard->devmpu = pnp_request_card_device(card, id->devs[1].id, NULL);
137
138	pdev = acard->dev;
139	pnp_init_resource_table(cfg);
140
141	/* override resources */
142	if (port[dev] != SNDRV_AUTO_PORT)
143		pnp_resource_change(&cfg->port_resource[0], port[dev], 16);
144	if (fm_port[dev] != SNDRV_AUTO_PORT)
145		pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4);
146	if (wss_port[dev] != SNDRV_AUTO_PORT)
147		pnp_resource_change(&cfg->port_resource[2], wss_port[dev], 4);
148	if (dma1[dev] != SNDRV_AUTO_DMA)
149		pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1);
150	if (dma2[dev] != SNDRV_AUTO_DMA)
151		pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1);
152	if (irq[dev] != SNDRV_AUTO_IRQ)
153		pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1);
154	if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0)
155		snd_printk(KERN_ERR PFX "AUDIO the requested resources are invalid, using auto config\n");
156
157	err = pnp_activate_dev(pdev);
158	if (err < 0) {
159		snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n");
160		kfree(cfg);
161		return err;
162	}
163	port[dev] = pnp_port_start(pdev, 0);
164	fm_port[dev] = pnp_port_start(pdev, 1);
165	wss_port[dev] = pnp_port_start(pdev, 2);
166	dma1[dev] = pnp_dma(pdev, 0);
167	dma2[dev] = pnp_dma(pdev, 1);
168	irq[dev] = pnp_irq(pdev, 0);
169
170	pdev = acard->devmpu;
171	if (pdev != NULL) {
172		pnp_init_resource_table(cfg);
173		if (mpu_port[dev] != SNDRV_AUTO_PORT)
174			pnp_resource_change(&cfg->port_resource[0], mpu_port[dev], 2);
175		if (mpu_irq[dev] != SNDRV_AUTO_IRQ)
176			pnp_resource_change(&cfg->irq_resource[0], mpu_irq[dev], 1);
177		if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0)
178			snd_printk(KERN_ERR PFX "MPU401 the requested resources are invalid, using auto config\n");
179		err = pnp_activate_dev(pdev);
180		if (err < 0)
181			goto __mpu_error;
182		mpu_port[dev] = pnp_port_start(pdev, 0);
183		mpu_irq[dev] = pnp_irq(pdev, 0);
184	} else {
185	     __mpu_error:
186	     	if (pdev) {
187		     	pnp_release_card_device(pdev);
188	     		snd_printk(KERN_ERR PFX "MPU401 pnp configure failure, skipping\n");
189	     	}
190	     	acard->devmpu = NULL;
191	     	mpu_port[dev] = -1;
192	}
193
194	kfree (cfg);
195	return 0;
196}
197
198/* same of snd_sbdsp_command by Jaroslav Kysela */
199static int __devinit snd_card_azt2320_command(unsigned long port, unsigned char val)
200{
201	int i;
202	unsigned long limit;
203
204	limit = jiffies + HZ / 10;
205	for (i = 50000; i && time_after(limit, jiffies); i--)
206		if (!(inb(port + 0x0c) & 0x80)) {
207			outb(val, port + 0x0c);
208			return 0;
209		}
210	return -EBUSY;
211}
212
213static int __devinit snd_card_azt2320_enable_wss(unsigned long port)
214{
215	int error;
216
217	if ((error = snd_card_azt2320_command(port, 0x09)))
218		return error;
219	if ((error = snd_card_azt2320_command(port, 0x00)))
220		return error;
221
222	mdelay(5);
223	return 0;
224}
225
226static int __devinit snd_card_azt2320_probe(int dev,
227					    struct pnp_card_link *pcard,
228					    const struct pnp_card_device_id *pid)
229{
230	int error;
231	struct snd_card *card;
232	struct snd_card_azt2320 *acard;
233	struct snd_cs4231 *chip;
234	struct snd_opl3 *opl3;
235
236	if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE,
237				 sizeof(struct snd_card_azt2320))) == NULL)
238		return -ENOMEM;
239	acard = (struct snd_card_azt2320 *)card->private_data;
240
241	if ((error = snd_card_azt2320_pnp(dev, acard, pcard, pid))) {
242		snd_card_free(card);
243		return error;
244	}
245	snd_card_set_dev(card, &pcard->card->dev);
246
247	if ((error = snd_card_azt2320_enable_wss(port[dev]))) {
248		snd_card_free(card);
249		return error;
250	}
251
252	if ((error = snd_cs4231_create(card, wss_port[dev], -1,
253				       irq[dev],
254				       dma1[dev],
255				       dma2[dev],
256				       CS4231_HW_DETECT, 0, &chip)) < 0) {
257		snd_card_free(card);
258		return error;
259	}
260
261	strcpy(card->driver, "AZT2320");
262	strcpy(card->shortname, "Aztech AZT2320");
263	sprintf(card->longname, "%s, WSS at 0x%lx, irq %i, dma %i&%i",
264		card->shortname, chip->port, irq[dev], dma1[dev], dma2[dev]);
265
266	if ((error = snd_cs4231_pcm(chip, 0, NULL)) < 0) {
267		snd_card_free(card);
268		return error;
269	}
270	if ((error = snd_cs4231_mixer(chip)) < 0) {
271		snd_card_free(card);
272		return error;
273	}
274	if ((error = snd_cs4231_timer(chip, 0, NULL)) < 0) {
275		snd_card_free(card);
276		return error;
277	}
278
279	if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) {
280		if (snd_mpu401_uart_new(card, 0, MPU401_HW_AZT2320,
281				mpu_port[dev], 0,
282				mpu_irq[dev], IRQF_DISABLED,
283				NULL) < 0)
284			snd_printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n", mpu_port[dev]);
285	}
286
287	if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) {
288		if (snd_opl3_create(card,
289				    fm_port[dev], fm_port[dev] + 2,
290				    OPL3_HW_AUTO, 0, &opl3) < 0) {
291			snd_printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n",
292				   fm_port[dev], fm_port[dev] + 2);
293		} else {
294			if ((error = snd_opl3_timer_new(opl3, 1, 2)) < 0) {
295				snd_card_free(card);
296				return error;
297			}
298			if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
299				snd_card_free(card);
300				return error;
301			}
302		}
303	}
304
305	if ((error = snd_card_register(card)) < 0) {
306		snd_card_free(card);
307		return error;
308	}
309	pnp_set_card_drvdata(pcard, card);
310	return 0;
311}
312
313static unsigned int __devinitdata azt2320_devices;
314
315static int __devinit snd_azt2320_pnp_detect(struct pnp_card_link *card,
316					    const struct pnp_card_device_id *id)
317{
318	static int dev;
319	int res;
320
321	for ( ; dev < SNDRV_CARDS; dev++) {
322		if (!enable[dev])
323			continue;
324		res = snd_card_azt2320_probe(dev, card, id);
325		if (res < 0)
326			return res;
327		dev++;
328		azt2320_devices++;
329		return 0;
330	}
331        return -ENODEV;
332}
333
334static void __devexit snd_azt2320_pnp_remove(struct pnp_card_link * pcard)
335{
336	snd_card_free(pnp_get_card_drvdata(pcard));
337	pnp_set_card_drvdata(pcard, NULL);
338}
339
340#ifdef CONFIG_PM
341static int snd_azt2320_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state)
342{
343	struct snd_card *card = pnp_get_card_drvdata(pcard);
344	struct snd_card_azt2320 *acard = card->private_data;
345	struct snd_cs4231 *chip = acard->chip;
346
347	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
348	chip->suspend(chip);
349	return 0;
350}
351
352static int snd_azt2320_pnp_resume(struct pnp_card_link *pcard)
353{
354	struct snd_card *card = pnp_get_card_drvdata(pcard);
355	struct snd_card_azt2320 *acard = card->private_data;
356	struct snd_cs4231 *chip = acard->chip;
357
358	chip->resume(chip);
359	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
360	return 0;
361}
362#endif
363
364static struct pnp_card_driver azt2320_pnpc_driver = {
365	.flags          = PNP_DRIVER_RES_DISABLE,
366	.name           = "azt2320",
367	.id_table       = snd_azt2320_pnpids,
368	.probe          = snd_azt2320_pnp_detect,
369	.remove         = __devexit_p(snd_azt2320_pnp_remove),
370#ifdef CONFIG_PM
371	.suspend	= snd_azt2320_pnp_suspend,
372	.resume		= snd_azt2320_pnp_resume,
373#endif
374};
375
376static int __init alsa_card_azt2320_init(void)
377{
378	int err;
379
380	err = pnp_register_card_driver(&azt2320_pnpc_driver);
381	if (err)
382		return err;
383
384	if (!azt2320_devices) {
385		pnp_unregister_card_driver(&azt2320_pnpc_driver);
386#ifdef MODULE
387		snd_printk(KERN_ERR "no AZT2320 based soundcards found\n");
388#endif
389		return -ENODEV;
390	}
391	return 0;
392}
393
394static void __exit alsa_card_azt2320_exit(void)
395{
396	pnp_unregister_card_driver(&azt2320_pnpc_driver);
397}
398
399module_init(alsa_card_azt2320_init)
400module_exit(alsa_card_azt2320_exit)
401