1/*
2 * The DSP56001 Device Driver, saviour of the Free World(tm)
3 *
4 * Authors: Fredrik Noring   <noring@nocrew.org>
5 *          lars brinkhoff   <lars@nocrew.org>
6 *          Tomas Berndtsson <tomas@nocrew.org>
7 *
8 * First version May 1996
9 *
10 * History:
11 *  97-01-29   Tomas Berndtsson,
12 *               Integrated with Linux 2.1.21 kernel sources.
13 *  97-02-15   Tomas Berndtsson,
14 *               Fixed for kernel 2.1.26
15 *
16 * BUGS:
17 *  Hmm... there must be something here :)
18 *
19 * Copyright (C) 1996,1997 Fredrik Noring, lars brinkhoff & Tomas Berndtsson
20 *
21 * This file is subject to the terms and conditions of the GNU General Public
22 * License.  See the file COPYING in the main directory of this archive
23 * for more details.
24 */
25
26#include <linux/module.h>
27#include <linux/slab.h>	/* for kmalloc() and kfree() */
28#include <linux/major.h>
29#include <linux/types.h>
30#include <linux/errno.h>
31#include <linux/delay.h>	/* guess what */
32#include <linux/fs.h>
33#include <linux/mm.h>
34#include <linux/init.h>
35#include <linux/device.h>
36
37#include <asm/atarihw.h>
38#include <asm/traps.h>
39#include <asm/uaccess.h>	/* For put_user and get_user */
40
41#include <asm/dsp56k.h>
42
43/* minor devices */
44#define DSP56K_DEV_56001        0    /* The only device so far */
45
46#define TIMEOUT    10   /* Host port timeout in number of tries */
47#define MAXIO    2048   /* Maximum number of words before sleep */
48#define DSP56K_MAX_BINARY_LENGTH (3*64*1024)
49
50#define DSP56K_TX_INT_ON	dsp56k_host_interface.icr |=  DSP56K_ICR_TREQ
51#define DSP56K_RX_INT_ON	dsp56k_host_interface.icr |=  DSP56K_ICR_RREQ
52#define DSP56K_TX_INT_OFF	dsp56k_host_interface.icr &= ~DSP56K_ICR_TREQ
53#define DSP56K_RX_INT_OFF	dsp56k_host_interface.icr &= ~DSP56K_ICR_RREQ
54
55#define DSP56K_TRANSMIT		(dsp56k_host_interface.isr & DSP56K_ISR_TXDE)
56#define DSP56K_RECEIVE		(dsp56k_host_interface.isr & DSP56K_ISR_RXDF)
57
58#define handshake(count, maxio, timeout, ENABLE, f) \
59{ \
60	long i, t, m; \
61	while (count > 0) { \
62		m = min_t(unsigned long, count, maxio); \
63		for (i = 0; i < m; i++) { \
64			for (t = 0; t < timeout && !ENABLE; t++) \
65				msleep(20); \
66			if(!ENABLE) \
67				return -EIO; \
68			f; \
69		} \
70		count -= m; \
71		if (m == maxio) msleep(20); \
72	} \
73}
74
75#define tx_wait(n) \
76{ \
77	int t; \
78	for(t = 0; t < n && !DSP56K_TRANSMIT; t++) \
79		msleep(10); \
80	if(!DSP56K_TRANSMIT) { \
81		return -EIO; \
82	} \
83}
84
85#define rx_wait(n) \
86{ \
87	int t; \
88	for(t = 0; t < n && !DSP56K_RECEIVE; t++) \
89		msleep(10); \
90	if(!DSP56K_RECEIVE) { \
91		return -EIO; \
92	} \
93}
94
95/* DSP56001 bootstrap code */
96static char bootstrap[] = {
97	0x0c, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
98	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
99	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
100	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
101	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
102	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
103	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
104	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
105	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
106	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
107	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
108	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
109	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
110	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
111	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
112	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
113	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
114	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
115	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
116	0x00, 0x00, 0x60, 0xf4, 0x00, 0x00, 0x00, 0x4f, 0x61, 0xf4,
117	0x00, 0x00, 0x7e, 0xa9, 0x06, 0x2e, 0x80, 0x00, 0x00, 0x47,
118	0x07, 0xd8, 0x84, 0x07, 0x59, 0x84, 0x08, 0xf4, 0xa8, 0x00,
119	0x00, 0x04, 0x08, 0xf4, 0xbf, 0x00, 0x0c, 0x00, 0x00, 0xfe,
120	0xb8, 0x0a, 0xf0, 0x80, 0x00, 0x7e, 0xa9, 0x08, 0xf4, 0xa0,
121	0x00, 0x00, 0x01, 0x08, 0xf4, 0xbe, 0x00, 0x00, 0x00, 0x0a,
122	0xa9, 0x80, 0x00, 0x7e, 0xad, 0x08, 0x4e, 0x2b, 0x44, 0xf4,
123	0x00, 0x00, 0x00, 0x03, 0x44, 0xf4, 0x45, 0x00, 0x00, 0x01,
124	0x0e, 0xa0, 0x00, 0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xb5, 0x08,
125	0x50, 0x2b, 0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xb8, 0x08, 0x46,
126	0x2b, 0x44, 0xf4, 0x45, 0x00, 0x00, 0x02, 0x0a, 0xf0, 0xaa,
127	0x00, 0x7e, 0xc9, 0x20, 0x00, 0x45, 0x0a, 0xf0, 0xaa, 0x00,
128	0x7e, 0xd0, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xc6, 0x0a, 0xa9,
129	0x80, 0x00, 0x7e, 0xc4, 0x08, 0x58, 0x6b, 0x0a, 0xf0, 0x80,
130	0x00, 0x7e, 0xad, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xcd, 0x0a,
131	0xa9, 0x80, 0x00, 0x7e, 0xcb, 0x08, 0x58, 0xab, 0x0a, 0xf0,
132	0x80, 0x00, 0x7e, 0xad, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xd4,
133	0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xd2, 0x08, 0x58, 0xeb, 0x0a,
134	0xf0, 0x80, 0x00, 0x7e, 0xad};
135static int sizeof_bootstrap = 375;
136
137
138static struct dsp56k_device {
139	long in_use;
140	long maxio, timeout;
141	int tx_wsize, rx_wsize;
142} dsp56k;
143
144static struct class *dsp56k_class;
145
146static int dsp56k_reset(void)
147{
148	u_char status;
149
150	/* Power down the DSP */
151	sound_ym.rd_data_reg_sel = 14;
152	status = sound_ym.rd_data_reg_sel & 0xef;
153	sound_ym.wd_data = status;
154	sound_ym.wd_data = status | 0x10;
155
156	udelay(10);
157
158	/* Power up the DSP */
159	sound_ym.rd_data_reg_sel = 14;
160	sound_ym.wd_data = sound_ym.rd_data_reg_sel & 0xef;
161
162	return 0;
163}
164
165static int dsp56k_upload(u_char __user *bin, int len)
166{
167	int i;
168	u_char *p;
169
170	dsp56k_reset();
171
172	p = bootstrap;
173	for (i = 0; i < sizeof_bootstrap/3; i++) {
174		/* tx_wait(10); */
175		dsp56k_host_interface.data.b[1] = *p++;
176		dsp56k_host_interface.data.b[2] = *p++;
177		dsp56k_host_interface.data.b[3] = *p++;
178	}
179	for (; i < 512; i++) {
180		/* tx_wait(10); */
181		dsp56k_host_interface.data.b[1] = 0;
182		dsp56k_host_interface.data.b[2] = 0;
183		dsp56k_host_interface.data.b[3] = 0;
184	}
185
186	for (i = 0; i < len; i++) {
187		tx_wait(10);
188		get_user(dsp56k_host_interface.data.b[1], bin++);
189		get_user(dsp56k_host_interface.data.b[2], bin++);
190		get_user(dsp56k_host_interface.data.b[3], bin++);
191	}
192
193	tx_wait(10);
194	dsp56k_host_interface.data.l = 3;    /* Magic execute */
195
196	return 0;
197}
198
199static ssize_t dsp56k_read(struct file *file, char __user *buf, size_t count,
200			   loff_t *ppos)
201{
202	struct inode *inode = file->f_path.dentry->d_inode;
203	int dev = iminor(inode) & 0x0f;
204
205	switch(dev)
206	{
207	case DSP56K_DEV_56001:
208	{
209
210		long n;
211
212		/* Don't do anything if nothing is to be done */
213		if (!count) return 0;
214
215		n = 0;
216		switch (dsp56k.rx_wsize) {
217		case 1:  /* 8 bit */
218		{
219			handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
220				  put_user(dsp56k_host_interface.data.b[3], buf+n++));
221			return n;
222		}
223		case 2:  /* 16 bit */
224		{
225			short __user *data;
226
227			count /= 2;
228			data = (short __user *) buf;
229			handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
230				  put_user(dsp56k_host_interface.data.w[1], data+n++));
231			return 2*n;
232		}
233		case 3:  /* 24 bit */
234		{
235			count /= 3;
236			handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
237				  put_user(dsp56k_host_interface.data.b[1], buf+n++);
238				  put_user(dsp56k_host_interface.data.b[2], buf+n++);
239				  put_user(dsp56k_host_interface.data.b[3], buf+n++));
240			return 3*n;
241		}
242		case 4:  /* 32 bit */
243		{
244			long __user *data;
245
246			count /= 4;
247			data = (long __user *) buf;
248			handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
249				  put_user(dsp56k_host_interface.data.l, data+n++));
250			return 4*n;
251		}
252		}
253		return -EFAULT;
254	}
255
256	default:
257		printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
258		return -ENXIO;
259	}
260}
261
262static ssize_t dsp56k_write(struct file *file, const char __user *buf, size_t count,
263			    loff_t *ppos)
264{
265	struct inode *inode = file->f_path.dentry->d_inode;
266	int dev = iminor(inode) & 0x0f;
267
268	switch(dev)
269	{
270	case DSP56K_DEV_56001:
271	{
272		long n;
273
274		/* Don't do anything if nothing is to be done */
275		if (!count) return 0;
276
277		n = 0;
278		switch (dsp56k.tx_wsize) {
279		case 1:  /* 8 bit */
280		{
281			handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
282				  get_user(dsp56k_host_interface.data.b[3], buf+n++));
283			return n;
284		}
285		case 2:  /* 16 bit */
286		{
287			const short __user *data;
288
289			count /= 2;
290			data = (const short __user *)buf;
291			handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
292				  get_user(dsp56k_host_interface.data.w[1], data+n++));
293			return 2*n;
294		}
295		case 3:  /* 24 bit */
296		{
297			count /= 3;
298			handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
299				  get_user(dsp56k_host_interface.data.b[1], buf+n++);
300				  get_user(dsp56k_host_interface.data.b[2], buf+n++);
301				  get_user(dsp56k_host_interface.data.b[3], buf+n++));
302			return 3*n;
303		}
304		case 4:  /* 32 bit */
305		{
306			const long __user *data;
307
308			count /= 4;
309			data = (const long __user *)buf;
310			handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
311				  get_user(dsp56k_host_interface.data.l, data+n++));
312			return 4*n;
313		}
314		}
315
316		return -EFAULT;
317	}
318	default:
319		printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
320		return -ENXIO;
321	}
322}
323
324static int dsp56k_ioctl(struct inode *inode, struct file *file,
325			unsigned int cmd, unsigned long arg)
326{
327	int dev = iminor(inode) & 0x0f;
328	void __user *argp = (void __user *)arg;
329
330	switch(dev)
331	{
332	case DSP56K_DEV_56001:
333
334		switch(cmd) {
335		case DSP56K_UPLOAD:
336		{
337			char __user *bin;
338			int r, len;
339			struct dsp56k_upload __user *binary = argp;
340
341			if(get_user(len, &binary->len) < 0)
342				return -EFAULT;
343			if(get_user(bin, &binary->bin) < 0)
344				return -EFAULT;
345
346			if (len == 0) {
347				return -EINVAL;      /* nothing to upload?!? */
348			}
349			if (len > DSP56K_MAX_BINARY_LENGTH) {
350				return -EINVAL;
351			}
352
353			r = dsp56k_upload(bin, len);
354			if (r < 0) {
355				return r;
356			}
357
358			break;
359		}
360		case DSP56K_SET_TX_WSIZE:
361			if (arg > 4 || arg < 1)
362				return -EINVAL;
363			dsp56k.tx_wsize = (int) arg;
364			break;
365		case DSP56K_SET_RX_WSIZE:
366			if (arg > 4 || arg < 1)
367				return -EINVAL;
368			dsp56k.rx_wsize = (int) arg;
369			break;
370		case DSP56K_HOST_FLAGS:
371		{
372			int dir, out, status;
373			struct dsp56k_host_flags __user *hf = argp;
374
375			if(get_user(dir, &hf->dir) < 0)
376				return -EFAULT;
377			if(get_user(out, &hf->out) < 0)
378				return -EFAULT;
379
380			if ((dir & 0x1) && (out & 0x1))
381				dsp56k_host_interface.icr |= DSP56K_ICR_HF0;
382			else if (dir & 0x1)
383				dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0;
384			if ((dir & 0x2) && (out & 0x2))
385				dsp56k_host_interface.icr |= DSP56K_ICR_HF1;
386			else if (dir & 0x2)
387				dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1;
388
389			status = 0;
390			if (dsp56k_host_interface.icr & DSP56K_ICR_HF0) status |= 0x1;
391			if (dsp56k_host_interface.icr & DSP56K_ICR_HF1) status |= 0x2;
392			if (dsp56k_host_interface.isr & DSP56K_ISR_HF2) status |= 0x4;
393			if (dsp56k_host_interface.isr & DSP56K_ISR_HF3) status |= 0x8;
394
395			return put_user(status, &hf->status);
396		}
397		case DSP56K_HOST_CMD:
398			if (arg > 31 || arg < 0)
399				return -EINVAL;
400			dsp56k_host_interface.cvr = (u_char)((arg & DSP56K_CVR_HV_MASK) |
401							     DSP56K_CVR_HC);
402			break;
403		default:
404			return -EINVAL;
405		}
406		return 0;
407
408	default:
409		printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
410		return -ENXIO;
411	}
412}
413
414/* As of 2.1.26 this should be dsp56k_poll,
415 * but how do I then check device minor number?
416 * Do I need this function at all???
417 */
418
419static int dsp56k_open(struct inode *inode, struct file *file)
420{
421	int dev = iminor(inode) & 0x0f;
422
423	switch(dev)
424	{
425	case DSP56K_DEV_56001:
426
427		if (test_and_set_bit(0, &dsp56k.in_use))
428			return -EBUSY;
429
430		dsp56k.timeout = TIMEOUT;
431		dsp56k.maxio = MAXIO;
432		dsp56k.rx_wsize = dsp56k.tx_wsize = 4;
433
434		DSP56K_TX_INT_OFF;
435		DSP56K_RX_INT_OFF;
436
437		/* Zero host flags */
438		dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0;
439		dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1;
440
441		break;
442
443	default:
444		return -ENODEV;
445	}
446
447	return 0;
448}
449
450static int dsp56k_release(struct inode *inode, struct file *file)
451{
452	int dev = iminor(inode) & 0x0f;
453
454	switch(dev)
455	{
456	case DSP56K_DEV_56001:
457		clear_bit(0, &dsp56k.in_use);
458		break;
459	default:
460		printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
461		return -ENXIO;
462	}
463
464	return 0;
465}
466
467static const struct file_operations dsp56k_fops = {
468	.owner		= THIS_MODULE,
469	.read		= dsp56k_read,
470	.write		= dsp56k_write,
471	.ioctl		= dsp56k_ioctl,
472	.open		= dsp56k_open,
473	.release	= dsp56k_release,
474};
475
476
477/****** Init and module functions ******/
478
479static char banner[] __initdata = KERN_INFO "DSP56k driver installed\n";
480
481static int __init dsp56k_init_driver(void)
482{
483	int err = 0;
484
485	if(!MACH_IS_ATARI || !ATARIHW_PRESENT(DSP56K)) {
486		printk("DSP56k driver: Hardware not present\n");
487		return -ENODEV;
488	}
489
490	if(register_chrdev(DSP56K_MAJOR, "dsp56k", &dsp56k_fops)) {
491		printk("DSP56k driver: Unable to register driver\n");
492		return -ENODEV;
493	}
494	dsp56k_class = class_create(THIS_MODULE, "dsp56k");
495	if (IS_ERR(dsp56k_class)) {
496		err = PTR_ERR(dsp56k_class);
497		goto out_chrdev;
498	}
499	class_device_create(dsp56k_class, NULL, MKDEV(DSP56K_MAJOR, 0), NULL, "dsp56k");
500
501	printk(banner);
502	goto out;
503
504out_chrdev:
505	unregister_chrdev(DSP56K_MAJOR, "dsp56k");
506out:
507	return err;
508}
509module_init(dsp56k_init_driver);
510
511static void __exit dsp56k_cleanup_driver(void)
512{
513	class_device_destroy(dsp56k_class, MKDEV(DSP56K_MAJOR, 0));
514	class_destroy(dsp56k_class);
515	unregister_chrdev(DSP56K_MAJOR, "dsp56k");
516}
517module_exit(dsp56k_cleanup_driver);
518
519MODULE_LICENSE("GPL");
520