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/sched.h> /* for struct wait_queue etc */
29 #include <linux/major.h>
30 #include <linux/types.h>
31 #include <linux/errno.h>
32 #include <linux/delay.h> /* guess what */
35 #include <linux/init.h>
36 #include <linux/devfs_fs_kernel.h>
37 #include <linux/smp_lock.h>
38 #include <linux/device.h>
40 #include <asm/atarihw.h>
41 #include <asm/traps.h>
42 #include <asm/uaccess.h> /* For put_user and get_user */
44 #include <asm/dsp56k.h>
47 #define DSP56K_DEV_56001 0 /* The only device so far */
49 #define TIMEOUT 10 /* Host port timeout in number of tries */
50 #define MAXIO 2048 /* Maximum number of words before sleep */
51 #define DSP56K_MAX_BINARY_LENGTH (3*64*1024)
53 #define DSP56K_TX_INT_ON dsp56k_host_interface.icr |= DSP56K_ICR_TREQ
54 #define DSP56K_RX_INT_ON dsp56k_host_interface.icr |= DSP56K_ICR_RREQ
55 #define DSP56K_TX_INT_OFF dsp56k_host_interface.icr &= ~DSP56K_ICR_TREQ
56 #define DSP56K_RX_INT_OFF dsp56k_host_interface.icr &= ~DSP56K_ICR_RREQ
58 #define DSP56K_TRANSMIT (dsp56k_host_interface.isr & DSP56K_ISR_TXDE)
59 #define DSP56K_RECEIVE (dsp56k_host_interface.isr & DSP56K_ISR_RXDF)
61 #define handshake(count, maxio, timeout, ENABLE, f) \
65 m = min_t(unsigned long, count, maxio); \
66 for (i = 0; i < m; i++) { \
67 for (t = 0; t < timeout && !ENABLE; t++) \
74 if (m == maxio) msleep(20); \
81 for(t = 0; t < n && !DSP56K_TRANSMIT; t++) \
83 if(!DSP56K_TRANSMIT) { \
91 for(t = 0; t < n && !DSP56K_RECEIVE; t++) \
93 if(!DSP56K_RECEIVE) { \
98 /* DSP56001 bootstrap code */
99 static char bootstrap[] = {
100 0x0c, 0x00, 0x40, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
118 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
119 0x00, 0x00, 0x60, 0xf4, 0x00, 0x00, 0x00, 0x4f, 0x61, 0xf4,
120 0x00, 0x00, 0x7e, 0xa9, 0x06, 0x2e, 0x80, 0x00, 0x00, 0x47,
121 0x07, 0xd8, 0x84, 0x07, 0x59, 0x84, 0x08, 0xf4, 0xa8, 0x00,
122 0x00, 0x04, 0x08, 0xf4, 0xbf, 0x00, 0x0c, 0x00, 0x00, 0xfe,
123 0xb8, 0x0a, 0xf0, 0x80, 0x00, 0x7e, 0xa9, 0x08, 0xf4, 0xa0,
124 0x00, 0x00, 0x01, 0x08, 0xf4, 0xbe, 0x00, 0x00, 0x00, 0x0a,
125 0xa9, 0x80, 0x00, 0x7e, 0xad, 0x08, 0x4e, 0x2b, 0x44, 0xf4,
126 0x00, 0x00, 0x00, 0x03, 0x44, 0xf4, 0x45, 0x00, 0x00, 0x01,
127 0x0e, 0xa0, 0x00, 0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xb5, 0x08,
128 0x50, 0x2b, 0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xb8, 0x08, 0x46,
129 0x2b, 0x44, 0xf4, 0x45, 0x00, 0x00, 0x02, 0x0a, 0xf0, 0xaa,
130 0x00, 0x7e, 0xc9, 0x20, 0x00, 0x45, 0x0a, 0xf0, 0xaa, 0x00,
131 0x7e, 0xd0, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xc6, 0x0a, 0xa9,
132 0x80, 0x00, 0x7e, 0xc4, 0x08, 0x58, 0x6b, 0x0a, 0xf0, 0x80,
133 0x00, 0x7e, 0xad, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xcd, 0x0a,
134 0xa9, 0x80, 0x00, 0x7e, 0xcb, 0x08, 0x58, 0xab, 0x0a, 0xf0,
135 0x80, 0x00, 0x7e, 0xad, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xd4,
136 0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xd2, 0x08, 0x58, 0xeb, 0x0a,
137 0xf0, 0x80, 0x00, 0x7e, 0xad};
138 static int sizeof_bootstrap = 375;
141 static struct dsp56k_device {
144 int tx_wsize, rx_wsize;
147 static struct class *dsp56k_class;
149 static int dsp56k_reset(void)
153 /* Power down the DSP */
154 sound_ym.rd_data_reg_sel = 14;
155 status = sound_ym.rd_data_reg_sel & 0xef;
156 sound_ym.wd_data = status;
157 sound_ym.wd_data = status | 0x10;
161 /* Power up the DSP */
162 sound_ym.rd_data_reg_sel = 14;
163 sound_ym.wd_data = sound_ym.rd_data_reg_sel & 0xef;
168 static int dsp56k_upload(u_char __user *bin, int len)
176 for (i = 0; i < sizeof_bootstrap/3; i++) {
178 dsp56k_host_interface.data.b[1] = *p++;
179 dsp56k_host_interface.data.b[2] = *p++;
180 dsp56k_host_interface.data.b[3] = *p++;
182 for (; i < 512; i++) {
184 dsp56k_host_interface.data.b[1] = 0;
185 dsp56k_host_interface.data.b[2] = 0;
186 dsp56k_host_interface.data.b[3] = 0;
189 for (i = 0; i < len; i++) {
191 get_user(dsp56k_host_interface.data.b[1], bin++);
192 get_user(dsp56k_host_interface.data.b[2], bin++);
193 get_user(dsp56k_host_interface.data.b[3], bin++);
197 dsp56k_host_interface.data.l = 3; /* Magic execute */
202 static ssize_t dsp56k_read(struct file *file, char __user *buf, size_t count,
205 struct inode *inode = file->f_dentry->d_inode;
206 int dev = iminor(inode) & 0x0f;
210 case DSP56K_DEV_56001:
215 /* Don't do anything if nothing is to be done */
216 if (!count) return 0;
219 switch (dsp56k.rx_wsize) {
222 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
223 put_user(dsp56k_host_interface.data.b[3], buf+n++));
231 data = (short __user *) buf;
232 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
233 put_user(dsp56k_host_interface.data.w[1], data+n++));
239 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
240 put_user(dsp56k_host_interface.data.b[1], buf+n++);
241 put_user(dsp56k_host_interface.data.b[2], buf+n++);
242 put_user(dsp56k_host_interface.data.b[3], buf+n++));
250 data = (long __user *) buf;
251 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
252 put_user(dsp56k_host_interface.data.l, data+n++));
260 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
265 static ssize_t dsp56k_write(struct file *file, const char __user *buf, size_t count,
268 struct inode *inode = file->f_dentry->d_inode;
269 int dev = iminor(inode) & 0x0f;
273 case DSP56K_DEV_56001:
277 /* Don't do anything if nothing is to be done */
278 if (!count) return 0;
281 switch (dsp56k.tx_wsize) {
284 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
285 get_user(dsp56k_host_interface.data.b[3], buf+n++));
290 const short __user *data;
293 data = (const short __user *)buf;
294 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
295 get_user(dsp56k_host_interface.data.w[1], data+n++));
301 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
302 get_user(dsp56k_host_interface.data.b[1], buf+n++);
303 get_user(dsp56k_host_interface.data.b[2], buf+n++);
304 get_user(dsp56k_host_interface.data.b[3], buf+n++));
309 const long __user *data;
312 data = (const long __user *)buf;
313 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
314 get_user(dsp56k_host_interface.data.l, data+n++));
322 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
327 static int dsp56k_ioctl(struct inode *inode, struct file *file,
328 unsigned int cmd, unsigned long arg)
330 int dev = iminor(inode) & 0x0f;
331 void __user *argp = (void __user *)arg;
335 case DSP56K_DEV_56001:
342 struct dsp56k_upload __user *binary = argp;
344 if(get_user(len, &binary->len) < 0)
346 if(get_user(bin, &binary->bin) < 0)
350 return -EINVAL; /* nothing to upload?!? */
352 if (len > DSP56K_MAX_BINARY_LENGTH) {
356 r = dsp56k_upload(bin, len);
363 case DSP56K_SET_TX_WSIZE:
364 if (arg > 4 || arg < 1)
366 dsp56k.tx_wsize = (int) arg;
368 case DSP56K_SET_RX_WSIZE:
369 if (arg > 4 || arg < 1)
371 dsp56k.rx_wsize = (int) arg;
373 case DSP56K_HOST_FLAGS:
375 int dir, out, status;
376 struct dsp56k_host_flags __user *hf = argp;
378 if(get_user(dir, &hf->dir) < 0)
380 if(get_user(out, &hf->out) < 0)
383 if ((dir & 0x1) && (out & 0x1))
384 dsp56k_host_interface.icr |= DSP56K_ICR_HF0;
386 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0;
387 if ((dir & 0x2) && (out & 0x2))
388 dsp56k_host_interface.icr |= DSP56K_ICR_HF1;
390 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1;
393 if (dsp56k_host_interface.icr & DSP56K_ICR_HF0) status |= 0x1;
394 if (dsp56k_host_interface.icr & DSP56K_ICR_HF1) status |= 0x2;
395 if (dsp56k_host_interface.isr & DSP56K_ISR_HF2) status |= 0x4;
396 if (dsp56k_host_interface.isr & DSP56K_ISR_HF3) status |= 0x8;
398 return put_user(status, &hf->status);
400 case DSP56K_HOST_CMD:
401 if (arg > 31 || arg < 0)
403 dsp56k_host_interface.cvr = (u_char)((arg & DSP56K_CVR_HV_MASK) |
412 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
417 /* As of 2.1.26 this should be dsp56k_poll,
418 * but how do I then check device minor number?
419 * Do I need this function at all???
422 static unsigned int dsp56k_poll(struct file *file, poll_table *wait)
424 int dev = iminor(file->f_dentry->d_inode) & 0x0f;
428 case DSP56K_DEV_56001:
429 /* poll_wait(file, ???, wait); */
430 return POLLIN | POLLRDNORM | POLLOUT;
433 printk("DSP56k driver: Unknown minor device: %d\n", dev);
439 static int dsp56k_open(struct inode *inode, struct file *file)
441 int dev = iminor(inode) & 0x0f;
445 case DSP56K_DEV_56001:
447 if (test_and_set_bit(0, &dsp56k.in_use))
450 dsp56k.timeout = TIMEOUT;
451 dsp56k.maxio = MAXIO;
452 dsp56k.rx_wsize = dsp56k.tx_wsize = 4;
457 /* Zero host flags */
458 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0;
459 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1;
470 static int dsp56k_release(struct inode *inode, struct file *file)
472 int dev = iminor(inode) & 0x0f;
476 case DSP56K_DEV_56001:
477 clear_bit(0, &dsp56k.in_use);
480 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
487 static struct file_operations dsp56k_fops = {
488 .owner = THIS_MODULE,
490 .write = dsp56k_write,
491 .ioctl = dsp56k_ioctl,
493 .release = dsp56k_release,
497 /****** Init and module functions ******/
499 static char banner[] __initdata = KERN_INFO "DSP56k driver installed\n";
501 static int __init dsp56k_init_driver(void)
505 if(!MACH_IS_ATARI || !ATARIHW_PRESENT(DSP56K)) {
506 printk("DSP56k driver: Hardware not present\n");
510 if(register_chrdev(DSP56K_MAJOR, "dsp56k", &dsp56k_fops)) {
511 printk("DSP56k driver: Unable to register driver\n");
514 dsp56k_class = class_create(THIS_MODULE, "dsp56k");
515 if (IS_ERR(dsp56k_class)) {
516 err = PTR_ERR(dsp56k_class);
519 class_device_create(dsp56k_class, NULL, MKDEV(DSP56K_MAJOR, 0), NULL, "dsp56k");
521 err = devfs_mk_cdev(MKDEV(DSP56K_MAJOR, 0),
522 S_IFCHR | S_IRUSR | S_IWUSR, "dsp56k");
530 class_device_destroy(dsp56k_class, MKDEV(DSP56K_MAJOR, 0));
531 class_destroy(dsp56k_class);
533 unregister_chrdev(DSP56K_MAJOR, "dsp56k");
537 module_init(dsp56k_init_driver);
539 static void __exit dsp56k_cleanup_driver(void)
541 class_device_destroy(dsp56k_class, MKDEV(DSP56K_MAJOR, 0));
542 class_destroy(dsp56k_class);
543 unregister_chrdev(DSP56K_MAJOR, "dsp56k");
544 devfs_remove("dsp56k");
546 module_exit(dsp56k_cleanup_driver);
548 MODULE_LICENSE("GPL");