1/*
2 * sound/maui.c
3 *
4 * The low level driver for Turtle Beach Maui and Tropez.
5 *
6 *
7 * Copyright (C) by Hannu Savolainen 1993-1997
8 *
9 * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
10 * Version 2 (June 1991). See the "COPYING" file distributed with this software
11 * for more info.
12 *
13 *	Changes:
14 *		Alan Cox		General clean up, use kernel IRQ
15 *					system
16 *		Christoph Hellwig	Adapted to module_init/module_exit
17 *		Bartlomiej Zolnierkiewicz
18 *					Added __init to download_code()
19 *
20 *	Status:
21 *		Andrew J. Kroll		Tested 06/01/1999 with:
22 *					* OSWF.MOT File Version: 1.15
23 *					* OSWF.MOT File Dated: 09/12/94
24 *					* Older versions will cause problems.
25 */
26
27#include <linux/config.h>
28#include <linux/module.h>
29#include <linux/init.h>
30
31#define USE_SEQ_MACROS
32#define USE_SIMPLE_MACROS
33
34#include "sound_config.h"
35#include "sound_firmware.h"
36
37#include "mpu401.h"
38
39static int      maui_base = 0x330;
40
41static volatile int irq_ok = 0;
42static int     *maui_osp;
43
44#define HOST_DATA_PORT	(maui_base + 2)
45#define HOST_STAT_PORT	(maui_base + 3)
46#define HOST_CTRL_PORT	(maui_base + 3)
47
48#define STAT_TX_INTR	0x40
49#define STAT_TX_AVAIL	0x20
50#define STAT_TX_IENA	0x10
51#define STAT_RX_INTR	0x04
52#define STAT_RX_AVAIL	0x02
53#define STAT_RX_IENA	0x01
54
55static int      (*orig_load_patch) (int dev, int format, const char *addr,
56			      int offs, int count, int pmgr_flag) = NULL;
57
58#include "maui_boot.h"
59
60static int maui_wait(int mask)
61{
62	int i;
63
64	/*
65	 * Perform a short initial wait without sleeping
66	 */
67
68	for (i = 0; i < 100; i++)
69		if (inb(HOST_STAT_PORT) & mask)
70			return 1;
71
72	/*
73	 * Wait up to 15 seconds with sleeping
74	 */
75
76	for (i = 0; i < 150; i++) {
77		if (inb(HOST_STAT_PORT) & mask)
78			return 1;
79		current->state = TASK_INTERRUPTIBLE;
80		schedule_timeout(HZ / 10);
81		if (signal_pending(current))
82			return 0;
83	}
84	return 0;
85}
86
87static int maui_read(void)
88{
89	if (maui_wait(STAT_RX_AVAIL))
90		return inb(HOST_DATA_PORT);
91	return -1;
92}
93
94static int maui_write(unsigned char data)
95{
96	if (maui_wait(STAT_TX_AVAIL)) {
97		outb((data), HOST_DATA_PORT);
98		return 1;
99	}
100	printk(KERN_WARNING "Maui: Write timeout\n");
101	return 0;
102}
103
104static void mauiintr(int irq, void *dev_id, struct pt_regs *dummy)
105{
106	irq_ok = 1;
107}
108
109static int __init download_code(void)
110{
111	int i, lines = 0;
112	int eol_seen = 0, done = 0;
113	int skip = 1;
114
115	printk(KERN_INFO "Code download (%d bytes): ", maui_osLen);
116
117	for (i = 0; i < maui_osLen; i++) {
118		if (maui_os[i] != '\r') {
119			if (!skip || (maui_os[i] == 'S' && (i == 0 || maui_os[i - 1] == '\n'))) {
120				skip = 0;
121
122				if (maui_os[i] == '\n')
123					eol_seen = skip = 1;
124				else if (maui_os[i] == 'S') {
125					if (maui_os[i + 1] == '8')
126						done = 1;
127					if (!maui_write(0xF1))
128						goto failure;
129					if (!maui_write('S'))
130						goto failure;
131				} else {
132					if (!maui_write(maui_os[i]))
133						goto failure;
134				}
135
136				if (eol_seen) {
137					int c = 0;
138					int n;
139
140					eol_seen = 0;
141
142					for (n = 0; n < 2; n++) {
143						if (maui_wait(STAT_RX_AVAIL)) {
144							c = inb(HOST_DATA_PORT);
145							break;
146						}
147					}
148					if (c != 0x80) {
149						printk("Download not acknowledged\n");
150						return 0;
151					}
152					else if (!(lines++ % 10))
153						printk(".");
154
155					if (done) {
156						printk("\n");
157						printk(KERN_INFO "Download complete\n");
158						return 1;
159					}
160				}
161			}
162		}
163	}
164
165failure:
166	printk("\n");
167	printk(KERN_ERR "Download failed!!!\n");
168	return 0;
169}
170
171static int __init maui_init(int irq)
172{
173	unsigned char bits;
174
175	switch (irq) {
176		case 9:
177			bits = 0x00;
178			break;
179		case 5:
180			bits = 0x08;
181			break;
182		case 12:
183			bits = 0x10;
184			break;
185		case 15:
186			bits = 0x18;
187			break;
188
189		default:
190			printk(KERN_ERR "Maui: Invalid IRQ %d\n", irq);
191			return 0;
192	}
193	outb((0x00), HOST_CTRL_PORT);	/* Reset */
194	outb((bits), HOST_DATA_PORT);	/* Set the IRQ bits */
195	outb((bits | 0x80), HOST_DATA_PORT);	/* Set the IRQ bits again? */
196	outb((0x80), HOST_CTRL_PORT);	/* Leave reset */
197	outb((0x80), HOST_CTRL_PORT);	/* Leave reset */
198	outb((0xD0), HOST_CTRL_PORT);	/* Cause interrupt */
199
200#ifdef CONFIG_SMP
201	{
202		int i;
203		for (i = 0; i < 1000000 && !irq_ok; i++)
204			;
205		if (!irq_ok)
206			return 0;
207	}
208#endif
209	outb((0x80), HOST_CTRL_PORT);	/* Leave reset */
210
211	printk(KERN_INFO "Turtle Beach Maui initialization\n");
212
213	if (!download_code())
214		return 0;
215
216	outb((0xE0), HOST_CTRL_PORT);	/* Normal operation */
217
218	/* Select mpu401 mode */
219
220	maui_write(0xf0);
221	maui_write(1);
222	if (maui_read() != 0x80) {
223		maui_write(0xf0);
224		maui_write(1);
225		if (maui_read() != 0x80)
226			printk(KERN_ERR "Maui didn't acknowledge set HW mode command\n");
227	}
228	printk(KERN_INFO "Maui initialized OK\n");
229	return 1;
230}
231
232static int maui_short_wait(int mask) {
233	int i;
234
235	for (i = 0; i < 1000; i++) {
236		if (inb(HOST_STAT_PORT) & mask) {
237			return 1;
238		}
239	}
240	return 0;
241}
242
243static int maui_load_patch(int dev, int format, const char *addr,
244		int offs, int count, int pmgr_flag)
245{
246
247	struct sysex_info header;
248	unsigned long left, src_offs;
249	int hdr_size = (unsigned long) &header.data[0] - (unsigned long) &header;
250	int i;
251
252	if (format == SYSEX_PATCH)	/* Handled by midi_synth.c */
253		return orig_load_patch(dev, format, addr, offs, count, pmgr_flag);
254
255	if (format != MAUI_PATCH)
256	{
257		  printk(KERN_WARNING "Maui: Unknown patch format\n");
258	}
259	if (count < hdr_size) {
260/*		  printk("Maui error: Patch header too short\n");*/
261		  return -EINVAL;
262	}
263	count -= hdr_size;
264
265	/*
266	 * Copy the header from user space but ignore the first bytes which have
267	 * been transferred already.
268	 */
269
270	if(copy_from_user(&((char *) &header)[offs], &(addr)[offs], hdr_size - offs))
271		return -EFAULT;
272
273	if (count < header.len) {
274		  printk(KERN_ERR "Maui warning: Host command record too short (%d<%d)\n", count, (int) header.len);
275		  header.len = count;
276	}
277	left = header.len;
278	src_offs = 0;
279
280	for (i = 0; i < left; i++) {
281		unsigned char   data;
282
283		if(get_user(*(unsigned char *) &data, (unsigned char *) &((addr)[hdr_size + i])))
284			return -EFAULT;
285		if (i == 0 && !(data & 0x80))
286			return -EINVAL;
287
288		if (maui_write(data) == -1)
289			return -EIO;
290	}
291
292	if ((i = maui_read()) != 0x80) {
293		if (i != -1)
294			printk("Maui: Error status %02x\n", i);
295		return -EIO;
296	}
297	return 0;
298}
299
300static int __init probe_maui(struct address_info *hw_config)
301{
302	int i;
303	int tmp1, tmp2, ret;
304
305	if (check_region(hw_config->io_base, 8))
306		return 0;
307
308	maui_base = hw_config->io_base;
309	maui_osp = hw_config->osp;
310
311	if (request_irq(hw_config->irq, mauiintr, 0, "Maui", NULL) < 0)
312		return 0;
313
314	/*
315	 * Initialize the processor if necessary
316	 */
317
318	if (maui_osLen > 0) {
319		if (!(inb(HOST_STAT_PORT) & STAT_TX_AVAIL) ||
320			!maui_write(0x9F) ||	/* Report firmware version */
321			!maui_short_wait(STAT_RX_AVAIL) ||
322			maui_read() == -1 || maui_read() == -1)
323			if (!maui_init(hw_config->irq)) {
324				free_irq(hw_config->irq, NULL);
325				return 0;
326			}
327	}
328	if (!maui_write(0xCF))	/* Report hardware version */ {
329		printk(KERN_ERR "No WaveFront firmware detected (card uninitialized?)\n");
330		free_irq(hw_config->irq, NULL);
331		return 0;
332	}
333	if ((tmp1 = maui_read()) == -1 || (tmp2 = maui_read()) == -1) {
334		printk(KERN_ERR "No WaveFront firmware detected (card uninitialized?)\n");
335		free_irq(hw_config->irq, NULL);
336		return 0;
337	}
338	if (tmp1 == 0xff || tmp2 == 0xff) {
339		free_irq(hw_config->irq, NULL);
340		return 0;
341	}
342	printk(KERN_DEBUG "WaveFront hardware version %d.%d\n", tmp1, tmp2);
343
344	if (!maui_write(0x9F))	/* Report firmware version */
345		return 0;
346	if ((tmp1 = maui_read()) == -1 || (tmp2 = maui_read()) == -1)
347		return 0;
348
349	printk(KERN_DEBUG "WaveFront firmware version %d.%d\n", tmp1, tmp2);
350
351	if (!maui_write(0x85))	/* Report free DRAM */
352		return 0;
353	tmp1 = 0;
354	for (i = 0; i < 4; i++) {
355		tmp1 |= maui_read() << (7 * i);
356	}
357	printk(KERN_DEBUG "Available DRAM %dk\n", tmp1 / 1024);
358
359	for (i = 0; i < 1000; i++)
360		if (probe_mpu401(hw_config))
361			break;
362
363	ret = probe_mpu401(hw_config);
364
365	if (ret)
366		request_region(hw_config->io_base + 2, 6, "Maui");
367
368	return ret;
369}
370
371static void __init attach_maui(struct address_info *hw_config)
372{
373	int this_dev;
374
375	conf_printf("Maui", hw_config);
376
377	hw_config->irq *= -1;
378	hw_config->name = "Maui";
379	attach_mpu401(hw_config, THIS_MODULE);
380
381	if (hw_config->slots[1] != -1)	/* The MPU401 driver installed itself */ {
382		struct synth_operations *synth;
383
384		this_dev = hw_config->slots[1];
385
386		/*
387		 * Intercept patch loading calls so that they can be handled
388		 * by the Maui driver.
389		 */
390
391		synth = midi_devs[this_dev]->converter;
392		synth->id = "MAUI";
393
394		if (synth != NULL) {
395			orig_load_patch = synth->load_patch;
396			synth->load_patch = &maui_load_patch;
397		} else
398			printk(KERN_ERR "Maui: Can't install patch loader\n");
399	}
400}
401
402static void __exit unload_maui(struct address_info *hw_config)
403{
404	int irq = hw_config->irq;
405	release_region(hw_config->io_base + 2, 6);
406	unload_mpu401(hw_config);
407
408	if (irq < 0)
409		irq = -irq;
410	if (irq > 0)
411		free_irq(irq, NULL);
412}
413
414static int fw_load = 0;
415
416static struct address_info cfg;
417
418static int __initdata io = -1;
419static int __initdata irq = -1;
420
421MODULE_PARM(io,"i");
422MODULE_PARM(irq,"i");
423
424/*
425 *	Install a Maui card. Needs mpu401 loaded already.
426 */
427
428static int __init init_maui(void)
429{
430	printk(KERN_INFO "Turtle beach Maui and Tropez driver, Copyright (C) by Hannu Savolainen 1993-1996\n");
431
432	cfg.io_base = io;
433	cfg.irq = irq;
434
435	if (cfg.io_base == -1 || cfg.irq == -1) {
436		printk(KERN_INFO "maui: irq and io must be set.\n");
437		return -EINVAL;
438	}
439
440	if (maui_os == NULL) {
441		fw_load = 1;
442		maui_osLen = mod_firmware_load("/etc/sound/oswf.mot", (char **) &maui_os);
443	}
444	if (probe_maui(&cfg) == 0)
445		return -ENODEV;
446	attach_maui(&cfg);
447
448	return 0;
449}
450
451static void __exit cleanup_maui(void)
452{
453	if (fw_load && maui_os)
454		vfree(maui_os);
455	unload_maui(&cfg);
456}
457
458module_init(init_maui);
459module_exit(cleanup_maui);
460
461#ifndef MODULE
462static int __init setup_maui(char *str)
463{
464        /* io, irq */
465	int ints[3];
466
467	str = get_options(str, ARRAY_SIZE(ints), ints);
468
469	io = ints[1];
470	irq = ints[2];
471
472	return 1;
473}
474
475__setup("maui=", setup_maui);
476#endif
477MODULE_LICENSE("GPL");
478