2 * The DSP56001 Device Driver, saviour of the Free World(tm)
4 * Authors: Fredrik Noring <noring@nocrew.org>
5 * lars brinkhoff <lars@nocrew.org>
6 * Tomas Berndtsson <tomas@nocrew.org>
8 * First version May 1996
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
17 * Hmm... there must be something here :)
19 * Copyright (C) 1996,1997 Fredrik Noring, lars brinkhoff & Tomas Berndtsson
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
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 */
34 #include <linux/init.h>
35 #include <linux/smp_lock.h>
36 #include <linux/device.h>
38 #include <asm/atarihw.h>
39 #include <asm/traps.h>
40 #include <asm/uaccess.h> /* For put_user and get_user */
42 #include <asm/dsp56k.h>
45 #define DSP56K_DEV_56001 0 /* The only device so far */
47 #define TIMEOUT 10 /* Host port timeout in number of tries */
48 #define MAXIO 2048 /* Maximum number of words before sleep */
49 #define DSP56K_MAX_BINARY_LENGTH (3*64*1024)
51 #define DSP56K_TX_INT_ON dsp56k_host_interface.icr |= DSP56K_ICR_TREQ
52 #define DSP56K_RX_INT_ON dsp56k_host_interface.icr |= DSP56K_ICR_RREQ
53 #define DSP56K_TX_INT_OFF dsp56k_host_interface.icr &= ~DSP56K_ICR_TREQ
54 #define DSP56K_RX_INT_OFF dsp56k_host_interface.icr &= ~DSP56K_ICR_RREQ
56 #define DSP56K_TRANSMIT (dsp56k_host_interface.isr & DSP56K_ISR_TXDE)
57 #define DSP56K_RECEIVE (dsp56k_host_interface.isr & DSP56K_ISR_RXDF)
59 #define handshake(count, maxio, timeout, ENABLE, f) \
63 m = min_t(unsigned long, count, maxio); \
64 for (i = 0; i < m; i++) { \
65 for (t = 0; t < timeout && !ENABLE; t++) \
72 if (m == maxio) msleep(20); \
79 for(t = 0; t < n && !DSP56K_TRANSMIT; t++) \
81 if(!DSP56K_TRANSMIT) { \
89 for(t = 0; t < n && !DSP56K_RECEIVE; t++) \
91 if(!DSP56K_RECEIVE) { \
96 /* DSP56001 bootstrap code */
97 static char bootstrap[] = {
98 0x0c, 0x00, 0x40, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
117 0x00, 0x00, 0x60, 0xf4, 0x00, 0x00, 0x00, 0x4f, 0x61, 0xf4,
118 0x00, 0x00, 0x7e, 0xa9, 0x06, 0x2e, 0x80, 0x00, 0x00, 0x47,
119 0x07, 0xd8, 0x84, 0x07, 0x59, 0x84, 0x08, 0xf4, 0xa8, 0x00,
120 0x00, 0x04, 0x08, 0xf4, 0xbf, 0x00, 0x0c, 0x00, 0x00, 0xfe,
121 0xb8, 0x0a, 0xf0, 0x80, 0x00, 0x7e, 0xa9, 0x08, 0xf4, 0xa0,
122 0x00, 0x00, 0x01, 0x08, 0xf4, 0xbe, 0x00, 0x00, 0x00, 0x0a,
123 0xa9, 0x80, 0x00, 0x7e, 0xad, 0x08, 0x4e, 0x2b, 0x44, 0xf4,
124 0x00, 0x00, 0x00, 0x03, 0x44, 0xf4, 0x45, 0x00, 0x00, 0x01,
125 0x0e, 0xa0, 0x00, 0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xb5, 0x08,
126 0x50, 0x2b, 0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xb8, 0x08, 0x46,
127 0x2b, 0x44, 0xf4, 0x45, 0x00, 0x00, 0x02, 0x0a, 0xf0, 0xaa,
128 0x00, 0x7e, 0xc9, 0x20, 0x00, 0x45, 0x0a, 0xf0, 0xaa, 0x00,
129 0x7e, 0xd0, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xc6, 0x0a, 0xa9,
130 0x80, 0x00, 0x7e, 0xc4, 0x08, 0x58, 0x6b, 0x0a, 0xf0, 0x80,
131 0x00, 0x7e, 0xad, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xcd, 0x0a,
132 0xa9, 0x80, 0x00, 0x7e, 0xcb, 0x08, 0x58, 0xab, 0x0a, 0xf0,
133 0x80, 0x00, 0x7e, 0xad, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xd4,
134 0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xd2, 0x08, 0x58, 0xeb, 0x0a,
135 0xf0, 0x80, 0x00, 0x7e, 0xad};
136 static int sizeof_bootstrap = 375;
139 static struct dsp56k_device {
142 int tx_wsize, rx_wsize;
145 static struct class *dsp56k_class;
147 static int dsp56k_reset(void)
151 /* Power down the DSP */
152 sound_ym.rd_data_reg_sel = 14;
153 status = sound_ym.rd_data_reg_sel & 0xef;
154 sound_ym.wd_data = status;
155 sound_ym.wd_data = status | 0x10;
159 /* Power up the DSP */
160 sound_ym.rd_data_reg_sel = 14;
161 sound_ym.wd_data = sound_ym.rd_data_reg_sel & 0xef;
166 static int dsp56k_upload(u_char __user *bin, int len)
174 for (i = 0; i < sizeof_bootstrap/3; i++) {
176 dsp56k_host_interface.data.b[1] = *p++;
177 dsp56k_host_interface.data.b[2] = *p++;
178 dsp56k_host_interface.data.b[3] = *p++;
180 for (; i < 512; i++) {
182 dsp56k_host_interface.data.b[1] = 0;
183 dsp56k_host_interface.data.b[2] = 0;
184 dsp56k_host_interface.data.b[3] = 0;
187 for (i = 0; i < len; i++) {
189 get_user(dsp56k_host_interface.data.b[1], bin++);
190 get_user(dsp56k_host_interface.data.b[2], bin++);
191 get_user(dsp56k_host_interface.data.b[3], bin++);
195 dsp56k_host_interface.data.l = 3; /* Magic execute */
200 static ssize_t dsp56k_read(struct file *file, char __user *buf, size_t count,
203 struct inode *inode = file->f_path.dentry->d_inode;
204 int dev = iminor(inode) & 0x0f;
208 case DSP56K_DEV_56001:
213 /* Don't do anything if nothing is to be done */
214 if (!count) return 0;
217 switch (dsp56k.rx_wsize) {
220 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
221 put_user(dsp56k_host_interface.data.b[3], buf+n++));
229 data = (short __user *) buf;
230 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
231 put_user(dsp56k_host_interface.data.w[1], data+n++));
237 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
238 put_user(dsp56k_host_interface.data.b[1], buf+n++);
239 put_user(dsp56k_host_interface.data.b[2], buf+n++);
240 put_user(dsp56k_host_interface.data.b[3], buf+n++));
248 data = (long __user *) buf;
249 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
250 put_user(dsp56k_host_interface.data.l, data+n++));
258 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
263 static ssize_t dsp56k_write(struct file *file, const char __user *buf, size_t count,
266 struct inode *inode = file->f_path.dentry->d_inode;
267 int dev = iminor(inode) & 0x0f;
271 case DSP56K_DEV_56001:
275 /* Don't do anything if nothing is to be done */
276 if (!count) return 0;
279 switch (dsp56k.tx_wsize) {
282 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
283 get_user(dsp56k_host_interface.data.b[3], buf+n++));
288 const short __user *data;
291 data = (const short __user *)buf;
292 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
293 get_user(dsp56k_host_interface.data.w[1], data+n++));
299 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
300 get_user(dsp56k_host_interface.data.b[1], buf+n++);
301 get_user(dsp56k_host_interface.data.b[2], buf+n++);
302 get_user(dsp56k_host_interface.data.b[3], buf+n++));
307 const long __user *data;
310 data = (const long __user *)buf;
311 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
312 get_user(dsp56k_host_interface.data.l, data+n++));
320 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
325 static int dsp56k_ioctl(struct inode *inode, struct file *file,
326 unsigned int cmd, unsigned long arg)
328 int dev = iminor(inode) & 0x0f;
329 void __user *argp = (void __user *)arg;
333 case DSP56K_DEV_56001:
340 struct dsp56k_upload __user *binary = argp;
342 if(get_user(len, &binary->len) < 0)
344 if(get_user(bin, &binary->bin) < 0)
348 return -EINVAL; /* nothing to upload?!? */
350 if (len > DSP56K_MAX_BINARY_LENGTH) {
354 r = dsp56k_upload(bin, len);
361 case DSP56K_SET_TX_WSIZE:
362 if (arg > 4 || arg < 1)
364 dsp56k.tx_wsize = (int) arg;
366 case DSP56K_SET_RX_WSIZE:
367 if (arg > 4 || arg < 1)
369 dsp56k.rx_wsize = (int) arg;
371 case DSP56K_HOST_FLAGS:
373 int dir, out, status;
374 struct dsp56k_host_flags __user *hf = argp;
376 if(get_user(dir, &hf->dir) < 0)
378 if(get_user(out, &hf->out) < 0)
381 if ((dir & 0x1) && (out & 0x1))
382 dsp56k_host_interface.icr |= DSP56K_ICR_HF0;
384 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0;
385 if ((dir & 0x2) && (out & 0x2))
386 dsp56k_host_interface.icr |= DSP56K_ICR_HF1;
388 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1;
391 if (dsp56k_host_interface.icr & DSP56K_ICR_HF0) status |= 0x1;
392 if (dsp56k_host_interface.icr & DSP56K_ICR_HF1) status |= 0x2;
393 if (dsp56k_host_interface.isr & DSP56K_ISR_HF2) status |= 0x4;
394 if (dsp56k_host_interface.isr & DSP56K_ISR_HF3) status |= 0x8;
396 return put_user(status, &hf->status);
398 case DSP56K_HOST_CMD:
399 if (arg > 31 || arg < 0)
401 dsp56k_host_interface.cvr = (u_char)((arg & DSP56K_CVR_HV_MASK) |
410 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
415 /* As of 2.1.26 this should be dsp56k_poll,
416 * but how do I then check device minor number?
417 * Do I need this function at all???
420 static unsigned int dsp56k_poll(struct file *file, poll_table *wait)
422 int dev = iminor(file->f_path.dentry->d_inode) & 0x0f;
426 case DSP56K_DEV_56001:
427 /* poll_wait(file, ???, wait); */
428 return POLLIN | POLLRDNORM | POLLOUT;
431 printk("DSP56k driver: Unknown minor device: %d\n", dev);
437 static int dsp56k_open(struct inode *inode, struct file *file)
439 int dev = iminor(inode) & 0x0f;
443 case DSP56K_DEV_56001:
445 if (test_and_set_bit(0, &dsp56k.in_use))
448 dsp56k.timeout = TIMEOUT;
449 dsp56k.maxio = MAXIO;
450 dsp56k.rx_wsize = dsp56k.tx_wsize = 4;
455 /* Zero host flags */
456 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0;
457 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1;
468 static int dsp56k_release(struct inode *inode, struct file *file)
470 int dev = iminor(inode) & 0x0f;
474 case DSP56K_DEV_56001:
475 clear_bit(0, &dsp56k.in_use);
478 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
485 static const struct file_operations dsp56k_fops = {
486 .owner = THIS_MODULE,
488 .write = dsp56k_write,
489 .ioctl = dsp56k_ioctl,
491 .release = dsp56k_release,
495 /****** Init and module functions ******/
497 static char banner[] __initdata = KERN_INFO "DSP56k driver installed\n";
499 static int __init dsp56k_init_driver(void)
503 if(!MACH_IS_ATARI || !ATARIHW_PRESENT(DSP56K)) {
504 printk("DSP56k driver: Hardware not present\n");
508 if(register_chrdev(DSP56K_MAJOR, "dsp56k", &dsp56k_fops)) {
509 printk("DSP56k driver: Unable to register driver\n");
512 dsp56k_class = class_create(THIS_MODULE, "dsp56k");
513 if (IS_ERR(dsp56k_class)) {
514 err = PTR_ERR(dsp56k_class);
517 class_device_create(dsp56k_class, NULL, MKDEV(DSP56K_MAJOR, 0), NULL, "dsp56k");
523 unregister_chrdev(DSP56K_MAJOR, "dsp56k");
527 module_init(dsp56k_init_driver);
529 static void __exit dsp56k_cleanup_driver(void)
531 class_device_destroy(dsp56k_class, MKDEV(DSP56K_MAJOR, 0));
532 class_destroy(dsp56k_class);
533 unregister_chrdev(DSP56K_MAJOR, "dsp56k");
535 module_exit(dsp56k_cleanup_driver);
537 MODULE_LICENSE("GPL");