Linux 2.6.21.1
[linux/fpc-iii.git] / drivers / char / dsp56k.c
blobdb984e481d4ca42908fffee5fb426528d119e82b
1 /*
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
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
16 * BUGS:
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
23 * for more details.
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/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>
44 /* minor devices */
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) \
60 { \
61 long i, t, m; \
62 while (count > 0) { \
63 m = min_t(unsigned long, count, maxio); \
64 for (i = 0; i < m; i++) { \
65 for (t = 0; t < timeout && !ENABLE; t++) \
66 msleep(20); \
67 if(!ENABLE) \
68 return -EIO; \
69 f; \
70 } \
71 count -= m; \
72 if (m == maxio) msleep(20); \
73 } \
76 #define tx_wait(n) \
77 { \
78 int t; \
79 for(t = 0; t < n && !DSP56K_TRANSMIT; t++) \
80 msleep(10); \
81 if(!DSP56K_TRANSMIT) { \
82 return -EIO; \
83 } \
86 #define rx_wait(n) \
87 { \
88 int t; \
89 for(t = 0; t < n && !DSP56K_RECEIVE; t++) \
90 msleep(10); \
91 if(!DSP56K_RECEIVE) { \
92 return -EIO; \
93 } \
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 {
140 long in_use;
141 long maxio, timeout;
142 int tx_wsize, rx_wsize;
143 } dsp56k;
145 static struct class *dsp56k_class;
147 static int dsp56k_reset(void)
149 u_char status;
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;
157 udelay(10);
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;
163 return 0;
166 static int dsp56k_upload(u_char __user *bin, int len)
168 int i;
169 u_char *p;
171 dsp56k_reset();
173 p = bootstrap;
174 for (i = 0; i < sizeof_bootstrap/3; i++) {
175 /* tx_wait(10); */
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++) {
181 /* tx_wait(10); */
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++) {
188 tx_wait(10);
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++);
194 tx_wait(10);
195 dsp56k_host_interface.data.l = 3; /* Magic execute */
197 return 0;
200 static ssize_t dsp56k_read(struct file *file, char __user *buf, size_t count,
201 loff_t *ppos)
203 struct inode *inode = file->f_path.dentry->d_inode;
204 int dev = iminor(inode) & 0x0f;
206 switch(dev)
208 case DSP56K_DEV_56001:
211 long n;
213 /* Don't do anything if nothing is to be done */
214 if (!count) return 0;
216 n = 0;
217 switch (dsp56k.rx_wsize) {
218 case 1: /* 8 bit */
220 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
221 put_user(dsp56k_host_interface.data.b[3], buf+n++));
222 return n;
224 case 2: /* 16 bit */
226 short __user *data;
228 count /= 2;
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++));
232 return 2*n;
234 case 3: /* 24 bit */
236 count /= 3;
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++));
241 return 3*n;
243 case 4: /* 32 bit */
245 long __user *data;
247 count /= 4;
248 data = (long __user *) buf;
249 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
250 put_user(dsp56k_host_interface.data.l, data+n++));
251 return 4*n;
254 return -EFAULT;
257 default:
258 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
259 return -ENXIO;
263 static ssize_t dsp56k_write(struct file *file, const char __user *buf, size_t count,
264 loff_t *ppos)
266 struct inode *inode = file->f_path.dentry->d_inode;
267 int dev = iminor(inode) & 0x0f;
269 switch(dev)
271 case DSP56K_DEV_56001:
273 long n;
275 /* Don't do anything if nothing is to be done */
276 if (!count) return 0;
278 n = 0;
279 switch (dsp56k.tx_wsize) {
280 case 1: /* 8 bit */
282 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
283 get_user(dsp56k_host_interface.data.b[3], buf+n++));
284 return n;
286 case 2: /* 16 bit */
288 const short __user *data;
290 count /= 2;
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++));
294 return 2*n;
296 case 3: /* 24 bit */
298 count /= 3;
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++));
303 return 3*n;
305 case 4: /* 32 bit */
307 const long __user *data;
309 count /= 4;
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++));
313 return 4*n;
317 return -EFAULT;
319 default:
320 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
321 return -ENXIO;
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;
331 switch(dev)
333 case DSP56K_DEV_56001:
335 switch(cmd) {
336 case DSP56K_UPLOAD:
338 char __user *bin;
339 int r, len;
340 struct dsp56k_upload __user *binary = argp;
342 if(get_user(len, &binary->len) < 0)
343 return -EFAULT;
344 if(get_user(bin, &binary->bin) < 0)
345 return -EFAULT;
347 if (len == 0) {
348 return -EINVAL; /* nothing to upload?!? */
350 if (len > DSP56K_MAX_BINARY_LENGTH) {
351 return -EINVAL;
354 r = dsp56k_upload(bin, len);
355 if (r < 0) {
356 return r;
359 break;
361 case DSP56K_SET_TX_WSIZE:
362 if (arg > 4 || arg < 1)
363 return -EINVAL;
364 dsp56k.tx_wsize = (int) arg;
365 break;
366 case DSP56K_SET_RX_WSIZE:
367 if (arg > 4 || arg < 1)
368 return -EINVAL;
369 dsp56k.rx_wsize = (int) arg;
370 break;
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)
377 return -EFAULT;
378 if(get_user(out, &hf->out) < 0)
379 return -EFAULT;
381 if ((dir & 0x1) && (out & 0x1))
382 dsp56k_host_interface.icr |= DSP56K_ICR_HF0;
383 else if (dir & 0x1)
384 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0;
385 if ((dir & 0x2) && (out & 0x2))
386 dsp56k_host_interface.icr |= DSP56K_ICR_HF1;
387 else if (dir & 0x2)
388 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1;
390 status = 0;
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)
400 return -EINVAL;
401 dsp56k_host_interface.cvr = (u_char)((arg & DSP56K_CVR_HV_MASK) |
402 DSP56K_CVR_HC);
403 break;
404 default:
405 return -EINVAL;
407 return 0;
409 default:
410 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
411 return -ENXIO;
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???
419 #if 0
420 static unsigned int dsp56k_poll(struct file *file, poll_table *wait)
422 int dev = iminor(file->f_path.dentry->d_inode) & 0x0f;
424 switch(dev)
426 case DSP56K_DEV_56001:
427 /* poll_wait(file, ???, wait); */
428 return POLLIN | POLLRDNORM | POLLOUT;
430 default:
431 printk("DSP56k driver: Unknown minor device: %d\n", dev);
432 return 0;
435 #endif
437 static int dsp56k_open(struct inode *inode, struct file *file)
439 int dev = iminor(inode) & 0x0f;
441 switch(dev)
443 case DSP56K_DEV_56001:
445 if (test_and_set_bit(0, &dsp56k.in_use))
446 return -EBUSY;
448 dsp56k.timeout = TIMEOUT;
449 dsp56k.maxio = MAXIO;
450 dsp56k.rx_wsize = dsp56k.tx_wsize = 4;
452 DSP56K_TX_INT_OFF;
453 DSP56K_RX_INT_OFF;
455 /* Zero host flags */
456 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0;
457 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1;
459 break;
461 default:
462 return -ENODEV;
465 return 0;
468 static int dsp56k_release(struct inode *inode, struct file *file)
470 int dev = iminor(inode) & 0x0f;
472 switch(dev)
474 case DSP56K_DEV_56001:
475 clear_bit(0, &dsp56k.in_use);
476 break;
477 default:
478 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
479 return -ENXIO;
482 return 0;
485 static const struct file_operations dsp56k_fops = {
486 .owner = THIS_MODULE,
487 .read = dsp56k_read,
488 .write = dsp56k_write,
489 .ioctl = dsp56k_ioctl,
490 .open = dsp56k_open,
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)
501 int err = 0;
503 if(!MACH_IS_ATARI || !ATARIHW_PRESENT(DSP56K)) {
504 printk("DSP56k driver: Hardware not present\n");
505 return -ENODEV;
508 if(register_chrdev(DSP56K_MAJOR, "dsp56k", &dsp56k_fops)) {
509 printk("DSP56k driver: Unable to register driver\n");
510 return -ENODEV;
512 dsp56k_class = class_create(THIS_MODULE, "dsp56k");
513 if (IS_ERR(dsp56k_class)) {
514 err = PTR_ERR(dsp56k_class);
515 goto out_chrdev;
517 class_device_create(dsp56k_class, NULL, MKDEV(DSP56K_MAJOR, 0), NULL, "dsp56k");
519 printk(banner);
520 goto out;
522 out_chrdev:
523 unregister_chrdev(DSP56K_MAJOR, "dsp56k");
524 out:
525 return err;
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");