Linux-2.6.12-rc2
[linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git] / drivers / char / dsp56k.c
blob37d6649011ad29900d11d6a2483db08d6bae768d
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/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 */
33 #include <linux/fs.h>
34 #include <linux/mm.h>
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>
46 /* minor devices */
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) \
62 { \
63 long i, t, m; \
64 while (count > 0) { \
65 m = min_t(unsigned long, count, maxio); \
66 for (i = 0; i < m; i++) { \
67 for (t = 0; t < timeout && !ENABLE; t++) \
68 msleep(20); \
69 if(!ENABLE) \
70 return -EIO; \
71 f; \
72 } \
73 count -= m; \
74 if (m == maxio) msleep(20); \
75 } \
78 #define tx_wait(n) \
79 { \
80 int t; \
81 for(t = 0; t < n && !DSP56K_TRANSMIT; t++) \
82 msleep(10); \
83 if(!DSP56K_TRANSMIT) { \
84 return -EIO; \
85 } \
88 #define rx_wait(n) \
89 { \
90 int t; \
91 for(t = 0; t < n && !DSP56K_RECEIVE; t++) \
92 msleep(10); \
93 if(!DSP56K_RECEIVE) { \
94 return -EIO; \
95 } \
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 {
142 long in_use;
143 long maxio, timeout;
144 int tx_wsize, rx_wsize;
145 } dsp56k;
147 static struct class_simple *dsp56k_class;
149 static int dsp56k_reset(void)
151 u_char status;
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;
159 udelay(10);
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;
165 return 0;
168 static int dsp56k_upload(u_char *bin, int len)
170 int i;
171 u_char *p;
173 dsp56k_reset();
175 p = bootstrap;
176 for (i = 0; i < sizeof_bootstrap/3; i++) {
177 /* tx_wait(10); */
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++) {
183 /* tx_wait(10); */
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++) {
190 tx_wait(10);
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++);
196 tx_wait(10);
197 dsp56k_host_interface.data.l = 3; /* Magic execute */
199 return 0;
202 static ssize_t dsp56k_read(struct file *file, char *buf, size_t count,
203 loff_t *ppos)
205 struct inode *inode = file->f_dentry->d_inode;
206 int dev = iminor(inode) & 0x0f;
208 switch(dev)
210 case DSP56K_DEV_56001:
213 long n;
215 /* Don't do anything if nothing is to be done */
216 if (!count) return 0;
218 n = 0;
219 switch (dsp56k.rx_wsize) {
220 case 1: /* 8 bit */
222 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
223 put_user(dsp56k_host_interface.data.b[3], buf+n++));
224 return n;
226 case 2: /* 16 bit */
228 short *data;
230 count /= 2;
231 data = (short*) buf;
232 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
233 put_user(dsp56k_host_interface.data.w[1], data+n++));
234 return 2*n;
236 case 3: /* 24 bit */
238 count /= 3;
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++));
243 return 3*n;
245 case 4: /* 32 bit */
247 long *data;
249 count /= 4;
250 data = (long*) buf;
251 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
252 put_user(dsp56k_host_interface.data.l, data+n++));
253 return 4*n;
256 return -EFAULT;
259 default:
260 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
261 return -ENXIO;
265 static ssize_t dsp56k_write(struct file *file, const char *buf, size_t count,
266 loff_t *ppos)
268 struct inode *inode = file->f_dentry->d_inode;
269 int dev = iminor(inode) & 0x0f;
271 switch(dev)
273 case DSP56K_DEV_56001:
275 long n;
277 /* Don't do anything if nothing is to be done */
278 if (!count) return 0;
280 n = 0;
281 switch (dsp56k.tx_wsize) {
282 case 1: /* 8 bit */
284 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
285 get_user(dsp56k_host_interface.data.b[3], buf+n++));
286 return n;
288 case 2: /* 16 bit */
290 const short *data;
292 count /= 2;
293 data = (const short *)buf;
294 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
295 get_user(dsp56k_host_interface.data.w[1], data+n++));
296 return 2*n;
298 case 3: /* 24 bit */
300 count /= 3;
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++));
305 return 3*n;
307 case 4: /* 32 bit */
309 const long *data;
311 count /= 4;
312 data = (const long *)buf;
313 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
314 get_user(dsp56k_host_interface.data.l, data+n++));
315 return 4*n;
319 return -EFAULT;
321 default:
322 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
323 return -ENXIO;
327 static int dsp56k_ioctl(struct inode *inode, struct file *file,
328 unsigned int cmd, unsigned long arg)
330 int dev = iminor(inode) & 0x0f;
332 switch(dev)
334 case DSP56K_DEV_56001:
336 switch(cmd) {
337 case DSP56K_UPLOAD:
339 char *bin;
340 int r, len;
341 struct dsp56k_upload *binary = (struct dsp56k_upload *) arg;
343 if(get_user(len, &binary->len) < 0)
344 return -EFAULT;
345 if(get_user(bin, &binary->bin) < 0)
346 return -EFAULT;
348 if (len == 0) {
349 return -EINVAL; /* nothing to upload?!? */
351 if (len > DSP56K_MAX_BINARY_LENGTH) {
352 return -EINVAL;
355 r = dsp56k_upload(bin, len);
356 if (r < 0) {
357 return r;
360 break;
362 case DSP56K_SET_TX_WSIZE:
363 if (arg > 4 || arg < 1)
364 return -EINVAL;
365 dsp56k.tx_wsize = (int) arg;
366 break;
367 case DSP56K_SET_RX_WSIZE:
368 if (arg > 4 || arg < 1)
369 return -EINVAL;
370 dsp56k.rx_wsize = (int) arg;
371 break;
372 case DSP56K_HOST_FLAGS:
374 int dir, out, status;
375 struct dsp56k_host_flags *hf = (struct dsp56k_host_flags*) arg;
377 if(get_user(dir, &hf->dir) < 0)
378 return -EFAULT;
379 if(get_user(out, &hf->out) < 0)
380 return -EFAULT;
382 if ((dir & 0x1) && (out & 0x1))
383 dsp56k_host_interface.icr |= DSP56K_ICR_HF0;
384 else if (dir & 0x1)
385 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0;
386 if ((dir & 0x2) && (out & 0x2))
387 dsp56k_host_interface.icr |= DSP56K_ICR_HF1;
388 else if (dir & 0x2)
389 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1;
391 status = 0;
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)
401 return -EINVAL;
402 dsp56k_host_interface.cvr = (u_char)((arg & DSP56K_CVR_HV_MASK) |
403 DSP56K_CVR_HC);
404 break;
405 default:
406 return -EINVAL;
408 return 0;
410 default:
411 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
412 return -ENXIO;
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???
420 #if 0
421 static unsigned int dsp56k_poll(struct file *file, poll_table *wait)
423 int dev = iminor(file->f_dentry->d_inode) & 0x0f;
425 switch(dev)
427 case DSP56K_DEV_56001:
428 /* poll_wait(file, ???, wait); */
429 return POLLIN | POLLRDNORM | POLLOUT;
431 default:
432 printk("DSP56k driver: Unknown minor device: %d\n", dev);
433 return 0;
436 #endif
438 static int dsp56k_open(struct inode *inode, struct file *file)
440 int dev = iminor(inode) & 0x0f;
442 switch(dev)
444 case DSP56K_DEV_56001:
446 if (test_and_set_bit(0, &dsp56k.in_use))
447 return -EBUSY;
449 dsp56k.timeout = TIMEOUT;
450 dsp56k.maxio = MAXIO;
451 dsp56k.rx_wsize = dsp56k.tx_wsize = 4;
453 DSP56K_TX_INT_OFF;
454 DSP56K_RX_INT_OFF;
456 /* Zero host flags */
457 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0;
458 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1;
460 break;
462 default:
463 return -ENODEV;
466 return 0;
469 static int dsp56k_release(struct inode *inode, struct file *file)
471 int dev = iminor(inode) & 0x0f;
473 switch(dev)
475 case DSP56K_DEV_56001:
476 clear_bit(0, &dsp56k.in_use);
477 break;
478 default:
479 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
480 return -ENXIO;
483 return 0;
486 static struct file_operations dsp56k_fops = {
487 .owner = THIS_MODULE,
488 .read = dsp56k_read,
489 .write = dsp56k_write,
490 .ioctl = dsp56k_ioctl,
491 .open = dsp56k_open,
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)
502 int err = 0;
504 if(!MACH_IS_ATARI || !ATARIHW_PRESENT(DSP56K)) {
505 printk("DSP56k driver: Hardware not present\n");
506 return -ENODEV;
509 if(register_chrdev(DSP56K_MAJOR, "dsp56k", &dsp56k_fops)) {
510 printk("DSP56k driver: Unable to register driver\n");
511 return -ENODEV;
513 dsp56k_class = class_simple_create(THIS_MODULE, "dsp56k");
514 if (IS_ERR(dsp56k_class)) {
515 err = PTR_ERR(dsp56k_class);
516 goto out_chrdev;
518 class_simple_device_add(dsp56k_class, MKDEV(DSP56K_MAJOR, 0), NULL, "dsp56k");
520 err = devfs_mk_cdev(MKDEV(DSP56K_MAJOR, 0),
521 S_IFCHR | S_IRUSR | S_IWUSR, "dsp56k");
522 if(err)
523 goto out_class;
525 printk(banner);
526 goto out;
528 out_class:
529 class_simple_device_remove(MKDEV(DSP56K_MAJOR, 0));
530 class_simple_destroy(dsp56k_class);
531 out_chrdev:
532 unregister_chrdev(DSP56K_MAJOR, "dsp56k");
533 out:
534 return err;
536 module_init(dsp56k_init_driver);
538 static void __exit dsp56k_cleanup_driver(void)
540 class_simple_device_remove(MKDEV(DSP56K_MAJOR, 0));
541 class_simple_destroy(dsp56k_class);
542 unregister_chrdev(DSP56K_MAJOR, "dsp56k");
543 devfs_remove("dsp56k");
545 module_exit(dsp56k_cleanup_driver);
547 MODULE_LICENSE("GPL");