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/smp_lock.h>
37 #include <linux/device.h>
39 #include <asm/atarihw.h>
40 #include <asm/traps.h>
41 #include <asm/uaccess.h> /* For put_user and get_user */
43 #include <asm/dsp56k.h>
46 #define DSP56K_DEV_56001 0 /* The only device so far */
48 #define TIMEOUT 10 /* Host port timeout in number of tries */
49 #define MAXIO 2048 /* Maximum number of words before sleep */
50 #define DSP56K_MAX_BINARY_LENGTH (3*64*1024)
52 #define DSP56K_TX_INT_ON dsp56k_host_interface.icr |= DSP56K_ICR_TREQ
53 #define DSP56K_RX_INT_ON dsp56k_host_interface.icr |= DSP56K_ICR_RREQ
54 #define DSP56K_TX_INT_OFF dsp56k_host_interface.icr &= ~DSP56K_ICR_TREQ
55 #define DSP56K_RX_INT_OFF dsp56k_host_interface.icr &= ~DSP56K_ICR_RREQ
57 #define DSP56K_TRANSMIT (dsp56k_host_interface.isr & DSP56K_ISR_TXDE)
58 #define DSP56K_RECEIVE (dsp56k_host_interface.isr & DSP56K_ISR_RXDF)
60 #define handshake(count, maxio, timeout, ENABLE, f) \
64 m = min_t(unsigned long, count, maxio); \
65 for (i = 0; i < m; i++) { \
66 for (t = 0; t < timeout && !ENABLE; t++) \
73 if (m == maxio) msleep(20); \
80 for(t = 0; t < n && !DSP56K_TRANSMIT; t++) \
82 if(!DSP56K_TRANSMIT) { \
90 for(t = 0; t < n && !DSP56K_RECEIVE; t++) \
92 if(!DSP56K_RECEIVE) { \
97 /* DSP56001 bootstrap code */
98 static char bootstrap[] = {
99 0x0c, 0x00, 0x40, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
118 0x00, 0x00, 0x60, 0xf4, 0x00, 0x00, 0x00, 0x4f, 0x61, 0xf4,
119 0x00, 0x00, 0x7e, 0xa9, 0x06, 0x2e, 0x80, 0x00, 0x00, 0x47,
120 0x07, 0xd8, 0x84, 0x07, 0x59, 0x84, 0x08, 0xf4, 0xa8, 0x00,
121 0x00, 0x04, 0x08, 0xf4, 0xbf, 0x00, 0x0c, 0x00, 0x00, 0xfe,
122 0xb8, 0x0a, 0xf0, 0x80, 0x00, 0x7e, 0xa9, 0x08, 0xf4, 0xa0,
123 0x00, 0x00, 0x01, 0x08, 0xf4, 0xbe, 0x00, 0x00, 0x00, 0x0a,
124 0xa9, 0x80, 0x00, 0x7e, 0xad, 0x08, 0x4e, 0x2b, 0x44, 0xf4,
125 0x00, 0x00, 0x00, 0x03, 0x44, 0xf4, 0x45, 0x00, 0x00, 0x01,
126 0x0e, 0xa0, 0x00, 0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xb5, 0x08,
127 0x50, 0x2b, 0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xb8, 0x08, 0x46,
128 0x2b, 0x44, 0xf4, 0x45, 0x00, 0x00, 0x02, 0x0a, 0xf0, 0xaa,
129 0x00, 0x7e, 0xc9, 0x20, 0x00, 0x45, 0x0a, 0xf0, 0xaa, 0x00,
130 0x7e, 0xd0, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xc6, 0x0a, 0xa9,
131 0x80, 0x00, 0x7e, 0xc4, 0x08, 0x58, 0x6b, 0x0a, 0xf0, 0x80,
132 0x00, 0x7e, 0xad, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xcd, 0x0a,
133 0xa9, 0x80, 0x00, 0x7e, 0xcb, 0x08, 0x58, 0xab, 0x0a, 0xf0,
134 0x80, 0x00, 0x7e, 0xad, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xd4,
135 0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xd2, 0x08, 0x58, 0xeb, 0x0a,
136 0xf0, 0x80, 0x00, 0x7e, 0xad};
137 static int sizeof_bootstrap = 375;
140 static struct dsp56k_device {
143 int tx_wsize, rx_wsize;
146 static struct class *dsp56k_class;
148 static int dsp56k_reset(void)
152 /* Power down the DSP */
153 sound_ym.rd_data_reg_sel = 14;
154 status = sound_ym.rd_data_reg_sel & 0xef;
155 sound_ym.wd_data = status;
156 sound_ym.wd_data = status | 0x10;
160 /* Power up the DSP */
161 sound_ym.rd_data_reg_sel = 14;
162 sound_ym.wd_data = sound_ym.rd_data_reg_sel & 0xef;
167 static int dsp56k_upload(u_char __user *bin, int len)
175 for (i = 0; i < sizeof_bootstrap/3; i++) {
177 dsp56k_host_interface.data.b[1] = *p++;
178 dsp56k_host_interface.data.b[2] = *p++;
179 dsp56k_host_interface.data.b[3] = *p++;
181 for (; i < 512; i++) {
183 dsp56k_host_interface.data.b[1] = 0;
184 dsp56k_host_interface.data.b[2] = 0;
185 dsp56k_host_interface.data.b[3] = 0;
188 for (i = 0; i < len; i++) {
190 get_user(dsp56k_host_interface.data.b[1], bin++);
191 get_user(dsp56k_host_interface.data.b[2], bin++);
192 get_user(dsp56k_host_interface.data.b[3], bin++);
196 dsp56k_host_interface.data.l = 3; /* Magic execute */
201 static ssize_t dsp56k_read(struct file *file, char __user *buf, size_t count,
204 struct inode *inode = file->f_dentry->d_inode;
205 int dev = iminor(inode) & 0x0f;
209 case DSP56K_DEV_56001:
214 /* Don't do anything if nothing is to be done */
215 if (!count) return 0;
218 switch (dsp56k.rx_wsize) {
221 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
222 put_user(dsp56k_host_interface.data.b[3], buf+n++));
230 data = (short __user *) buf;
231 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
232 put_user(dsp56k_host_interface.data.w[1], data+n++));
238 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
239 put_user(dsp56k_host_interface.data.b[1], buf+n++);
240 put_user(dsp56k_host_interface.data.b[2], buf+n++);
241 put_user(dsp56k_host_interface.data.b[3], buf+n++));
249 data = (long __user *) buf;
250 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
251 put_user(dsp56k_host_interface.data.l, data+n++));
259 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
264 static ssize_t dsp56k_write(struct file *file, const char __user *buf, size_t count,
267 struct inode *inode = file->f_dentry->d_inode;
268 int dev = iminor(inode) & 0x0f;
272 case DSP56K_DEV_56001:
276 /* Don't do anything if nothing is to be done */
277 if (!count) return 0;
280 switch (dsp56k.tx_wsize) {
283 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
284 get_user(dsp56k_host_interface.data.b[3], buf+n++));
289 const short __user *data;
292 data = (const short __user *)buf;
293 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
294 get_user(dsp56k_host_interface.data.w[1], data+n++));
300 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
301 get_user(dsp56k_host_interface.data.b[1], buf+n++);
302 get_user(dsp56k_host_interface.data.b[2], buf+n++);
303 get_user(dsp56k_host_interface.data.b[3], buf+n++));
308 const long __user *data;
311 data = (const long __user *)buf;
312 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
313 get_user(dsp56k_host_interface.data.l, data+n++));
321 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
326 static int dsp56k_ioctl(struct inode *inode, struct file *file,
327 unsigned int cmd, unsigned long arg)
329 int dev = iminor(inode) & 0x0f;
330 void __user *argp = (void __user *)arg;
334 case DSP56K_DEV_56001:
341 struct dsp56k_upload __user *binary = argp;
343 if(get_user(len, &binary->len) < 0)
345 if(get_user(bin, &binary->bin) < 0)
349 return -EINVAL; /* nothing to upload?!? */
351 if (len > DSP56K_MAX_BINARY_LENGTH) {
355 r = dsp56k_upload(bin, len);
362 case DSP56K_SET_TX_WSIZE:
363 if (arg > 4 || arg < 1)
365 dsp56k.tx_wsize = (int) arg;
367 case DSP56K_SET_RX_WSIZE:
368 if (arg > 4 || arg < 1)
370 dsp56k.rx_wsize = (int) arg;
372 case DSP56K_HOST_FLAGS:
374 int dir, out, status;
375 struct dsp56k_host_flags __user *hf = argp;
377 if(get_user(dir, &hf->dir) < 0)
379 if(get_user(out, &hf->out) < 0)
382 if ((dir & 0x1) && (out & 0x1))
383 dsp56k_host_interface.icr |= DSP56K_ICR_HF0;
385 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0;
386 if ((dir & 0x2) && (out & 0x2))
387 dsp56k_host_interface.icr |= DSP56K_ICR_HF1;
389 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1;
392 if (dsp56k_host_interface.icr & DSP56K_ICR_HF0) status |= 0x1;
393 if (dsp56k_host_interface.icr & DSP56K_ICR_HF1) status |= 0x2;
394 if (dsp56k_host_interface.isr & DSP56K_ISR_HF2) status |= 0x4;
395 if (dsp56k_host_interface.isr & DSP56K_ISR_HF3) status |= 0x8;
397 return put_user(status, &hf->status);
399 case DSP56K_HOST_CMD:
400 if (arg > 31 || arg < 0)
402 dsp56k_host_interface.cvr = (u_char)((arg & DSP56K_CVR_HV_MASK) |
411 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
416 /* As of 2.1.26 this should be dsp56k_poll,
417 * but how do I then check device minor number?
418 * Do I need this function at all???
421 static unsigned int dsp56k_poll(struct file *file, poll_table *wait)
423 int dev = iminor(file->f_dentry->d_inode) & 0x0f;
427 case DSP56K_DEV_56001:
428 /* poll_wait(file, ???, wait); */
429 return POLLIN | POLLRDNORM | POLLOUT;
432 printk("DSP56k driver: Unknown minor device: %d\n", dev);
438 static int dsp56k_open(struct inode *inode, struct file *file)
440 int dev = iminor(inode) & 0x0f;
444 case DSP56K_DEV_56001:
446 if (test_and_set_bit(0, &dsp56k.in_use))
449 dsp56k.timeout = TIMEOUT;
450 dsp56k.maxio = MAXIO;
451 dsp56k.rx_wsize = dsp56k.tx_wsize = 4;
456 /* Zero host flags */
457 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0;
458 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1;
469 static int dsp56k_release(struct inode *inode, struct file *file)
471 int dev = iminor(inode) & 0x0f;
475 case DSP56K_DEV_56001:
476 clear_bit(0, &dsp56k.in_use);
479 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
486 static const struct file_operations dsp56k_fops = {
487 .owner = THIS_MODULE,
489 .write = dsp56k_write,
490 .ioctl = dsp56k_ioctl,
492 .release = dsp56k_release,
496 /****** Init and module functions ******/
498 static char banner[] __initdata = KERN_INFO "DSP56k driver installed\n";
500 static int __init dsp56k_init_driver(void)
504 if(!MACH_IS_ATARI || !ATARIHW_PRESENT(DSP56K)) {
505 printk("DSP56k driver: Hardware not present\n");
509 if(register_chrdev(DSP56K_MAJOR, "dsp56k", &dsp56k_fops)) {
510 printk("DSP56k driver: Unable to register driver\n");
513 dsp56k_class = class_create(THIS_MODULE, "dsp56k");
514 if (IS_ERR(dsp56k_class)) {
515 err = PTR_ERR(dsp56k_class);
518 class_device_create(dsp56k_class, NULL, MKDEV(DSP56K_MAJOR, 0), NULL, "dsp56k");
524 unregister_chrdev(DSP56K_MAJOR, "dsp56k");
528 module_init(dsp56k_init_driver);
530 static void __exit dsp56k_cleanup_driver(void)
532 class_device_destroy(dsp56k_class, MKDEV(DSP56K_MAJOR, 0));
533 class_destroy(dsp56k_class);
534 unregister_chrdev(DSP56K_MAJOR, "dsp56k");
536 module_exit(dsp56k_cleanup_driver);
538 MODULE_LICENSE("GPL");