Import 2.1.118
[davej-history.git] / drivers / char / dsp56k.c
blobc9c22f6952cd4f20dd8d7855aca3ca81ab42ebb8
1 /*
2 * The DSP56001 Device Driver, saviour of the Free World(tm)
4 * Authors: Fredrik Noring <noring@lysator.liu.se>
5 * lars brinkhoff <f93labr@dd.chalmers.se>
6 * Tomas Berndtsson <tobe@lysator.liu.se>
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/version.h>
28 #include <linux/malloc.h> /* for kmalloc() and kfree() */
29 #include <linux/sched.h> /* for struct wait_queue etc */
30 #include <linux/major.h>
31 #include <linux/types.h>
32 #include <linux/errno.h>
33 #include <linux/delay.h> /* guess what */
34 #include <linux/fs.h>
35 #include <linux/mm.h>
36 #include <linux/init.h>
38 #include <asm/segment.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>
45 /* minor devices */
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 max(a,b) ((a) > (b) ? (a) : (b))
61 #define min(a,b) ((a) < (b) ? (a) : (b))
63 #define wait_some(n) \
64 { \
65 current->state = TASK_INTERRUPTIBLE; \
66 current->timeout = jiffies + n; \
67 schedule(); \
70 #define handshake(count, maxio, timeout, ENABLE, f) \
71 { \
72 long i, t, m; \
73 while (count > 0) { \
74 m = min(count, maxio); \
75 for (i = 0; i < m; i++) { \
76 for (t = 0; t < timeout && !ENABLE; t++) \
77 wait_some(2); \
78 if(!ENABLE) \
79 return -EIO; \
80 f; \
81 } \
82 count -= m; \
83 if (m == maxio) wait_some(2); \
84 } \
87 #define tx_wait(n) \
88 { \
89 int t; \
90 for(t = 0; t < n && !DSP56K_TRANSMIT; t++) \
91 wait_some(1); \
92 if(!DSP56K_TRANSMIT) { \
93 return -EIO; \
94 } \
97 #define rx_wait(n) \
98 { \
99 int t; \
100 for(t = 0; t < n && !DSP56K_RECEIVE; t++) \
101 wait_some(1); \
102 if(!DSP56K_RECEIVE) { \
103 return -EIO; \
107 /* DSP56001 bootstrap code */
108 static char bootstrap[] = {
109 0x0c, 0x00, 0x40, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
120 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
121 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
122 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
123 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
124 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
125 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
126 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
127 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
128 0x00, 0x00, 0x60, 0xf4, 0x00, 0x00, 0x00, 0x4f, 0x61, 0xf4,
129 0x00, 0x00, 0x7e, 0xa9, 0x06, 0x2e, 0x80, 0x00, 0x00, 0x47,
130 0x07, 0xd8, 0x84, 0x07, 0x59, 0x84, 0x08, 0xf4, 0xa8, 0x00,
131 0x00, 0x04, 0x08, 0xf4, 0xbf, 0x00, 0x0c, 0x00, 0x00, 0xfe,
132 0xb8, 0x0a, 0xf0, 0x80, 0x00, 0x7e, 0xa9, 0x08, 0xf4, 0xa0,
133 0x00, 0x00, 0x01, 0x08, 0xf4, 0xbe, 0x00, 0x00, 0x00, 0x0a,
134 0xa9, 0x80, 0x00, 0x7e, 0xad, 0x08, 0x4e, 0x2b, 0x44, 0xf4,
135 0x00, 0x00, 0x00, 0x03, 0x44, 0xf4, 0x45, 0x00, 0x00, 0x01,
136 0x0e, 0xa0, 0x00, 0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xb5, 0x08,
137 0x50, 0x2b, 0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xb8, 0x08, 0x46,
138 0x2b, 0x44, 0xf4, 0x45, 0x00, 0x00, 0x02, 0x0a, 0xf0, 0xaa,
139 0x00, 0x7e, 0xc9, 0x20, 0x00, 0x45, 0x0a, 0xf0, 0xaa, 0x00,
140 0x7e, 0xd0, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xc6, 0x0a, 0xa9,
141 0x80, 0x00, 0x7e, 0xc4, 0x08, 0x58, 0x6b, 0x0a, 0xf0, 0x80,
142 0x00, 0x7e, 0xad, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xcd, 0x0a,
143 0xa9, 0x80, 0x00, 0x7e, 0xcb, 0x08, 0x58, 0xab, 0x0a, 0xf0,
144 0x80, 0x00, 0x7e, 0xad, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xd4,
145 0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xd2, 0x08, 0x58, 0xeb, 0x0a,
146 0xf0, 0x80, 0x00, 0x7e, 0xad};
147 static int sizeof_bootstrap = 375;
150 static struct dsp56k_device {
151 int in_use;
152 long maxio, timeout;
153 int tx_wsize, rx_wsize;
154 } dsp56k;
156 static int dsp56k_reset(void)
158 u_char status;
160 /* Power down the DSP */
161 sound_ym.rd_data_reg_sel = 14;
162 status = sound_ym.rd_data_reg_sel & 0xef;
163 sound_ym.wd_data = status;
164 sound_ym.wd_data = status | 0x10;
166 udelay(10);
168 /* Power up the DSP */
169 sound_ym.rd_data_reg_sel = 14;
170 sound_ym.wd_data = sound_ym.rd_data_reg_sel & 0xef;
172 return 0;
175 static int dsp56k_upload(u_char *bin, int len)
177 int i;
178 u_char *p;
180 dsp56k_reset();
182 p = bootstrap;
183 for (i = 0; i < sizeof_bootstrap/3; i++) {
184 /* tx_wait(10); */
185 dsp56k_host_interface.data.b[1] = *p++;
186 dsp56k_host_interface.data.b[2] = *p++;
187 dsp56k_host_interface.data.b[3] = *p++;
189 for (; i < 512; i++) {
190 /* tx_wait(10); */
191 dsp56k_host_interface.data.b[1] = 0;
192 dsp56k_host_interface.data.b[2] = 0;
193 dsp56k_host_interface.data.b[3] = 0;
196 for (i = 0; i < len; i++) {
197 tx_wait(10);
198 get_user(dsp56k_host_interface.data.b[1], bin++);
199 get_user(dsp56k_host_interface.data.b[2], bin++);
200 get_user(dsp56k_host_interface.data.b[3], bin++);
203 tx_wait(10);
204 dsp56k_host_interface.data.l = 3; /* Magic execute */
206 return 0;
209 static ssize_t dsp56k_read(struct file *file, char *buf, size_t count,
210 loff_t *ppos)
212 struct inode *inode = file->f_dentry->d_inode;
213 int dev = MINOR(inode->i_rdev) & 0x0f;
215 switch(dev)
217 case DSP56K_DEV_56001:
220 long n;
222 /* Don't do anything if nothing is to be done */
223 if (!count) return 0;
225 n = 0;
226 switch (dsp56k.rx_wsize) {
227 case 1: /* 8 bit */
229 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
230 put_user(dsp56k_host_interface.data.b[3], buf+n++));
231 return n;
233 case 2: /* 16 bit */
235 short *data;
237 count /= 2;
238 data = (short*) buf;
239 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
240 put_user(dsp56k_host_interface.data.w[1], data+n++));
241 return 2*n;
243 case 3: /* 24 bit */
245 count /= 3;
246 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
247 put_user(dsp56k_host_interface.data.b[1], buf+n++);
248 put_user(dsp56k_host_interface.data.b[2], buf+n++);
249 put_user(dsp56k_host_interface.data.b[3], buf+n++));
250 return 3*n;
252 case 4: /* 32 bit */
254 long *data;
256 count /= 4;
257 data = (long*) buf;
258 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
259 put_user(dsp56k_host_interface.data.l, data+n++));
260 return 4*n;
263 return -EFAULT;
266 default:
267 printk("DSP56k driver: Unknown minor device: %d\n", dev);
268 return -ENXIO;
272 static ssize_t dsp56k_write(struct file *file, const char *buf, size_t count,
273 loff_t *ppos)
275 struct inode *inode = file->f_dentry->d_inode;
276 int dev = MINOR(inode->i_rdev) & 0x0f;
278 switch(dev)
280 case DSP56K_DEV_56001:
282 long n;
284 /* Don't do anything if nothing is to be done */
285 if (!count) return 0;
287 n = 0;
288 switch (dsp56k.tx_wsize) {
289 case 1: /* 8 bit */
291 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
292 get_user(dsp56k_host_interface.data.b[3], buf+n++));
293 return n;
295 case 2: /* 16 bit */
297 short *data;
299 count /= 2;
300 data = (short*) buf;
301 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
302 get_user(dsp56k_host_interface.data.w[1], data+n++));
303 return 2*n;
305 case 3: /* 24 bit */
307 count /= 3;
308 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
309 get_user(dsp56k_host_interface.data.b[1], buf+n++);
310 get_user(dsp56k_host_interface.data.b[2], buf+n++);
311 get_user(dsp56k_host_interface.data.b[3], buf+n++));
312 return 3*n;
314 case 4: /* 32 bit */
316 long *data;
318 count /= 4;
319 data = (long*) buf;
320 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
321 get_user(dsp56k_host_interface.data.l, data+n++));
322 return 4*n;
326 return -EFAULT;
328 default:
329 printk("DSP56k driver: Unknown minor device: %d\n", dev);
330 return -ENXIO;
334 static int dsp56k_ioctl(struct inode *inode, struct file *file,
335 unsigned int cmd, unsigned long arg)
337 int dev = MINOR(inode->i_rdev) & 0x0f;
339 switch(dev)
341 case DSP56K_DEV_56001:
343 switch(cmd) {
344 case DSP56K_UPLOAD:
346 char *bin;
347 int r, len;
348 struct dsp56k_upload *binary = (struct dsp56k_upload *) arg;
350 if(get_user(len, &binary->len) < 0)
351 return -EFAULT;
352 if(get_user(bin, &binary->bin) < 0)
353 return -EFAULT;
355 if (len == 0) {
356 return -EINVAL; /* nothing to upload?!? */
358 if (len > DSP56K_MAX_BINARY_LENGTH) {
359 return -EINVAL;
362 r = dsp56k_upload(bin, len);
363 if (r < 0) {
364 return r;
367 break;
369 case DSP56K_SET_TX_WSIZE:
370 if (arg > 4 || arg < 1)
371 return -EINVAL;
372 dsp56k.tx_wsize = (int) arg;
373 break;
374 case DSP56K_SET_RX_WSIZE:
375 if (arg > 4 || arg < 1)
376 return -EINVAL;
377 dsp56k.rx_wsize = (int) arg;
378 break;
379 case DSP56K_HOST_FLAGS:
381 int dir, out, status;
382 struct dsp56k_host_flags *hf = (struct dsp56k_host_flags*) arg;
384 if(get_user(dir, &hf->dir) < 0)
385 return -EFAULT;
386 if(get_user(out, &hf->out) < 0)
387 return -EFAULT;
389 if ((dir & 0x1) && (out & 0x1))
390 dsp56k_host_interface.icr |= DSP56K_ICR_HF0;
391 else if (dir & 0x1)
392 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0;
393 if ((dir & 0x2) && (out & 0x2))
394 dsp56k_host_interface.icr |= DSP56K_ICR_HF1;
395 else if (dir & 0x2)
396 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1;
398 status = 0;
399 if (dsp56k_host_interface.icr & DSP56K_ICR_HF0) status |= 0x1;
400 if (dsp56k_host_interface.icr & DSP56K_ICR_HF1) status |= 0x2;
401 if (dsp56k_host_interface.isr & DSP56K_ISR_HF2) status |= 0x4;
402 if (dsp56k_host_interface.isr & DSP56K_ISR_HF3) status |= 0x8;
404 return put_user(status, &hf->status);
406 case DSP56K_HOST_CMD:
407 if (arg > 31 || arg < 0)
408 return -EINVAL;
409 dsp56k_host_interface.cvr = (u_char)((arg & DSP56K_CVR_HV_MASK) |
410 DSP56K_CVR_HC);
411 break;
412 default:
413 return -EINVAL;
415 return 0;
417 default:
418 printk("DSP56k driver: Unknown minor device: %d\n", dev);
419 return -ENXIO;
423 /* As of 2.1.26 this should be dsp56k_poll,
424 * but how do I then check device minor number?
425 * Do I need this function at all???
427 #if 0
428 static unsigned int dsp56k_poll(struct file *file, poll_table *wait)
430 int dev = MINOR(file->f_dentry->d_inode->i_rdev) & 0x0f;
432 switch(dev)
434 case DSP56K_DEV_56001:
435 /* poll_wait(file, ???, wait); */
436 return POLLIN | POLLRDNORM | POLLOUT;
438 default:
439 printk("DSP56k driver: Unknown minor device: %d\n", dev);
440 return 0;
443 #endif
445 static int dsp56k_open(struct inode *inode, struct file *file)
447 int dev = MINOR(inode->i_rdev) & 0x0f;
449 switch(dev)
451 case DSP56K_DEV_56001:
453 if (dsp56k.in_use)
454 return -EBUSY;
456 dsp56k.in_use = 1;
457 dsp56k.timeout = TIMEOUT;
458 dsp56k.maxio = MAXIO;
459 dsp56k.rx_wsize = dsp56k.tx_wsize = 4;
461 DSP56K_TX_INT_OFF;
462 DSP56K_RX_INT_OFF;
464 /* Zero host flags */
465 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0;
466 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1;
468 break;
470 default:
471 printk("DSP56k driver: Unknown minor device: %d\n", dev);
472 return -ENXIO;
475 #ifdef MODULE
476 MOD_INC_USE_COUNT;
477 #endif /* MODULE */
479 return 0;
482 static int dsp56k_release(struct inode *inode, struct file *file)
484 int dev = MINOR(inode->i_rdev) & 0x0f;
486 switch(dev)
488 case DSP56K_DEV_56001:
490 dsp56k.in_use = 0;
492 break;
493 default:
494 printk("DSP56k driver: Unknown minor device: %d\n", dev);
495 return -ENXIO;
498 #ifdef MODULE
499 MOD_DEC_USE_COUNT;
500 #endif
501 return 0;
504 static struct file_operations dsp56k_fops = {
505 NULL, /* no special dsp56k_lseek */
506 dsp56k_read,
507 dsp56k_write,
508 NULL, /* no special dsp56k_readdir */
509 NULL, /* dsp56k_poll? */
510 dsp56k_ioctl,
511 NULL, /* no special dsp56k_mmap */
512 dsp56k_open,
513 NULL, /* flush */
514 dsp56k_release,
515 NULL, /* no special dsp56k_fsync */
516 NULL, /* no special dsp56k_fasync */
517 NULL, /* no special dsp56k_check_media_change */
518 NULL /* no special dsp56k_revalidate */
522 /****** Init and module functions ******/
524 __initfunc(int dsp56k_init(void))
526 if(!MACH_IS_ATARI || !ATARIHW_PRESENT(DSP56K)) {
527 printk("DSP56k driver: Hardware not present\n");
528 return -ENODEV;
531 if(register_chrdev(DSP56K_MAJOR, "dsp56k", &dsp56k_fops)) {
532 printk("DSP56k driver: Unable to register driver\n");
533 return -ENODEV;
536 dsp56k.in_use = 0;
538 printk("DSP56k driver installed\n");
541 #ifdef MODULE
542 int init_module(void)
544 return dsp56k_init();
547 void cleanup_module(void)
549 unregister_chrdev(DSP56K_MAJOR, "dsp56k");
551 #endif /* MODULE */