Import 2.3.13
[davej-history.git] / drivers / char / dsp56k.c
blob30a76a04ea6c2674dcf21ce88590f66ed194b2b1
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 schedule_timeout(n); \
69 #define handshake(count, maxio, timeout, ENABLE, f) \
70 { \
71 long i, t, m; \
72 while (count > 0) { \
73 m = min(count, maxio); \
74 for (i = 0; i < m; i++) { \
75 for (t = 0; t < timeout && !ENABLE; t++) \
76 wait_some(2); \
77 if(!ENABLE) \
78 return -EIO; \
79 f; \
80 } \
81 count -= m; \
82 if (m == maxio) wait_some(2); \
83 } \
86 #define tx_wait(n) \
87 { \
88 int t; \
89 for(t = 0; t < n && !DSP56K_TRANSMIT; t++) \
90 wait_some(1); \
91 if(!DSP56K_TRANSMIT) { \
92 return -EIO; \
93 } \
96 #define rx_wait(n) \
97 { \
98 int t; \
99 for(t = 0; t < n && !DSP56K_RECEIVE; t++) \
100 wait_some(1); \
101 if(!DSP56K_RECEIVE) { \
102 return -EIO; \
106 /* DSP56001 bootstrap code */
107 static char bootstrap[] = {
108 0x0c, 0x00, 0x40, 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, 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, 0x60, 0xf4, 0x00, 0x00, 0x00, 0x4f, 0x61, 0xf4,
128 0x00, 0x00, 0x7e, 0xa9, 0x06, 0x2e, 0x80, 0x00, 0x00, 0x47,
129 0x07, 0xd8, 0x84, 0x07, 0x59, 0x84, 0x08, 0xf4, 0xa8, 0x00,
130 0x00, 0x04, 0x08, 0xf4, 0xbf, 0x00, 0x0c, 0x00, 0x00, 0xfe,
131 0xb8, 0x0a, 0xf0, 0x80, 0x00, 0x7e, 0xa9, 0x08, 0xf4, 0xa0,
132 0x00, 0x00, 0x01, 0x08, 0xf4, 0xbe, 0x00, 0x00, 0x00, 0x0a,
133 0xa9, 0x80, 0x00, 0x7e, 0xad, 0x08, 0x4e, 0x2b, 0x44, 0xf4,
134 0x00, 0x00, 0x00, 0x03, 0x44, 0xf4, 0x45, 0x00, 0x00, 0x01,
135 0x0e, 0xa0, 0x00, 0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xb5, 0x08,
136 0x50, 0x2b, 0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xb8, 0x08, 0x46,
137 0x2b, 0x44, 0xf4, 0x45, 0x00, 0x00, 0x02, 0x0a, 0xf0, 0xaa,
138 0x00, 0x7e, 0xc9, 0x20, 0x00, 0x45, 0x0a, 0xf0, 0xaa, 0x00,
139 0x7e, 0xd0, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xc6, 0x0a, 0xa9,
140 0x80, 0x00, 0x7e, 0xc4, 0x08, 0x58, 0x6b, 0x0a, 0xf0, 0x80,
141 0x00, 0x7e, 0xad, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xcd, 0x0a,
142 0xa9, 0x80, 0x00, 0x7e, 0xcb, 0x08, 0x58, 0xab, 0x0a, 0xf0,
143 0x80, 0x00, 0x7e, 0xad, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xd4,
144 0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xd2, 0x08, 0x58, 0xeb, 0x0a,
145 0xf0, 0x80, 0x00, 0x7e, 0xad};
146 static int sizeof_bootstrap = 375;
149 static struct dsp56k_device {
150 int in_use;
151 long maxio, timeout;
152 int tx_wsize, rx_wsize;
153 } dsp56k;
155 static int dsp56k_reset(void)
157 u_char status;
159 /* Power down the DSP */
160 sound_ym.rd_data_reg_sel = 14;
161 status = sound_ym.rd_data_reg_sel & 0xef;
162 sound_ym.wd_data = status;
163 sound_ym.wd_data = status | 0x10;
165 udelay(10);
167 /* Power up the DSP */
168 sound_ym.rd_data_reg_sel = 14;
169 sound_ym.wd_data = sound_ym.rd_data_reg_sel & 0xef;
171 return 0;
174 static int dsp56k_upload(u_char *bin, int len)
176 int i;
177 u_char *p;
179 dsp56k_reset();
181 p = bootstrap;
182 for (i = 0; i < sizeof_bootstrap/3; i++) {
183 /* tx_wait(10); */
184 dsp56k_host_interface.data.b[1] = *p++;
185 dsp56k_host_interface.data.b[2] = *p++;
186 dsp56k_host_interface.data.b[3] = *p++;
188 for (; i < 512; i++) {
189 /* tx_wait(10); */
190 dsp56k_host_interface.data.b[1] = 0;
191 dsp56k_host_interface.data.b[2] = 0;
192 dsp56k_host_interface.data.b[3] = 0;
195 for (i = 0; i < len; i++) {
196 tx_wait(10);
197 get_user(dsp56k_host_interface.data.b[1], bin++);
198 get_user(dsp56k_host_interface.data.b[2], bin++);
199 get_user(dsp56k_host_interface.data.b[3], bin++);
202 tx_wait(10);
203 dsp56k_host_interface.data.l = 3; /* Magic execute */
205 return 0;
208 static ssize_t dsp56k_read(struct file *file, char *buf, size_t count,
209 loff_t *ppos)
211 struct inode *inode = file->f_dentry->d_inode;
212 int dev = MINOR(inode->i_rdev) & 0x0f;
214 switch(dev)
216 case DSP56K_DEV_56001:
219 long n;
221 /* Don't do anything if nothing is to be done */
222 if (!count) return 0;
224 n = 0;
225 switch (dsp56k.rx_wsize) {
226 case 1: /* 8 bit */
228 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
229 put_user(dsp56k_host_interface.data.b[3], buf+n++));
230 return n;
232 case 2: /* 16 bit */
234 short *data;
236 count /= 2;
237 data = (short*) buf;
238 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
239 put_user(dsp56k_host_interface.data.w[1], data+n++));
240 return 2*n;
242 case 3: /* 24 bit */
244 count /= 3;
245 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
246 put_user(dsp56k_host_interface.data.b[1], buf+n++);
247 put_user(dsp56k_host_interface.data.b[2], buf+n++);
248 put_user(dsp56k_host_interface.data.b[3], buf+n++));
249 return 3*n;
251 case 4: /* 32 bit */
253 long *data;
255 count /= 4;
256 data = (long*) buf;
257 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
258 put_user(dsp56k_host_interface.data.l, data+n++));
259 return 4*n;
262 return -EFAULT;
265 default:
266 printk("DSP56k driver: Unknown minor device: %d\n", dev);
267 return -ENXIO;
271 static ssize_t dsp56k_write(struct file *file, const char *buf, size_t count,
272 loff_t *ppos)
274 struct inode *inode = file->f_dentry->d_inode;
275 int dev = MINOR(inode->i_rdev) & 0x0f;
277 switch(dev)
279 case DSP56K_DEV_56001:
281 long n;
283 /* Don't do anything if nothing is to be done */
284 if (!count) return 0;
286 n = 0;
287 switch (dsp56k.tx_wsize) {
288 case 1: /* 8 bit */
290 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
291 get_user(dsp56k_host_interface.data.b[3], buf+n++));
292 return n;
294 case 2: /* 16 bit */
296 short *data;
298 count /= 2;
299 data = (short*) buf;
300 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
301 get_user(dsp56k_host_interface.data.w[1], data+n++));
302 return 2*n;
304 case 3: /* 24 bit */
306 count /= 3;
307 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
308 get_user(dsp56k_host_interface.data.b[1], buf+n++);
309 get_user(dsp56k_host_interface.data.b[2], buf+n++);
310 get_user(dsp56k_host_interface.data.b[3], buf+n++));
311 return 3*n;
313 case 4: /* 32 bit */
315 long *data;
317 count /= 4;
318 data = (long*) buf;
319 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
320 get_user(dsp56k_host_interface.data.l, data+n++));
321 return 4*n;
325 return -EFAULT;
327 default:
328 printk("DSP56k driver: Unknown minor device: %d\n", dev);
329 return -ENXIO;
333 static int dsp56k_ioctl(struct inode *inode, struct file *file,
334 unsigned int cmd, unsigned long arg)
336 int dev = MINOR(inode->i_rdev) & 0x0f;
338 switch(dev)
340 case DSP56K_DEV_56001:
342 switch(cmd) {
343 case DSP56K_UPLOAD:
345 char *bin;
346 int r, len;
347 struct dsp56k_upload *binary = (struct dsp56k_upload *) arg;
349 if(get_user(len, &binary->len) < 0)
350 return -EFAULT;
351 if(get_user(bin, &binary->bin) < 0)
352 return -EFAULT;
354 if (len == 0) {
355 return -EINVAL; /* nothing to upload?!? */
357 if (len > DSP56K_MAX_BINARY_LENGTH) {
358 return -EINVAL;
361 r = dsp56k_upload(bin, len);
362 if (r < 0) {
363 return r;
366 break;
368 case DSP56K_SET_TX_WSIZE:
369 if (arg > 4 || arg < 1)
370 return -EINVAL;
371 dsp56k.tx_wsize = (int) arg;
372 break;
373 case DSP56K_SET_RX_WSIZE:
374 if (arg > 4 || arg < 1)
375 return -EINVAL;
376 dsp56k.rx_wsize = (int) arg;
377 break;
378 case DSP56K_HOST_FLAGS:
380 int dir, out, status;
381 struct dsp56k_host_flags *hf = (struct dsp56k_host_flags*) arg;
383 if(get_user(dir, &hf->dir) < 0)
384 return -EFAULT;
385 if(get_user(out, &hf->out) < 0)
386 return -EFAULT;
388 if ((dir & 0x1) && (out & 0x1))
389 dsp56k_host_interface.icr |= DSP56K_ICR_HF0;
390 else if (dir & 0x1)
391 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0;
392 if ((dir & 0x2) && (out & 0x2))
393 dsp56k_host_interface.icr |= DSP56K_ICR_HF1;
394 else if (dir & 0x2)
395 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1;
397 status = 0;
398 if (dsp56k_host_interface.icr & DSP56K_ICR_HF0) status |= 0x1;
399 if (dsp56k_host_interface.icr & DSP56K_ICR_HF1) status |= 0x2;
400 if (dsp56k_host_interface.isr & DSP56K_ISR_HF2) status |= 0x4;
401 if (dsp56k_host_interface.isr & DSP56K_ISR_HF3) status |= 0x8;
403 return put_user(status, &hf->status);
405 case DSP56K_HOST_CMD:
406 if (arg > 31 || arg < 0)
407 return -EINVAL;
408 dsp56k_host_interface.cvr = (u_char)((arg & DSP56K_CVR_HV_MASK) |
409 DSP56K_CVR_HC);
410 break;
411 default:
412 return -EINVAL;
414 return 0;
416 default:
417 printk("DSP56k driver: Unknown minor device: %d\n", dev);
418 return -ENXIO;
422 /* As of 2.1.26 this should be dsp56k_poll,
423 * but how do I then check device minor number?
424 * Do I need this function at all???
426 #if 0
427 static unsigned int dsp56k_poll(struct file *file, poll_table *wait)
429 int dev = MINOR(file->f_dentry->d_inode->i_rdev) & 0x0f;
431 switch(dev)
433 case DSP56K_DEV_56001:
434 /* poll_wait(file, ???, wait); */
435 return POLLIN | POLLRDNORM | POLLOUT;
437 default:
438 printk("DSP56k driver: Unknown minor device: %d\n", dev);
439 return 0;
442 #endif
444 static int dsp56k_open(struct inode *inode, struct file *file)
446 int dev = MINOR(inode->i_rdev) & 0x0f;
448 switch(dev)
450 case DSP56K_DEV_56001:
452 if (dsp56k.in_use)
453 return -EBUSY;
455 dsp56k.in_use = 1;
456 dsp56k.timeout = TIMEOUT;
457 dsp56k.maxio = MAXIO;
458 dsp56k.rx_wsize = dsp56k.tx_wsize = 4;
460 DSP56K_TX_INT_OFF;
461 DSP56K_RX_INT_OFF;
463 /* Zero host flags */
464 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0;
465 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1;
467 break;
469 default:
470 printk("DSP56k driver: Unknown minor device: %d\n", dev);
471 return -ENXIO;
474 #ifdef MODULE
475 MOD_INC_USE_COUNT;
476 #endif /* MODULE */
478 return 0;
481 static int dsp56k_release(struct inode *inode, struct file *file)
483 int dev = MINOR(inode->i_rdev) & 0x0f;
485 switch(dev)
487 case DSP56K_DEV_56001:
489 dsp56k.in_use = 0;
491 break;
492 default:
493 printk("DSP56k driver: Unknown minor device: %d\n", dev);
494 return -ENXIO;
497 #ifdef MODULE
498 MOD_DEC_USE_COUNT;
499 #endif
500 return 0;
503 static struct file_operations dsp56k_fops = {
504 NULL, /* no special dsp56k_lseek */
505 dsp56k_read,
506 dsp56k_write,
507 NULL, /* no special dsp56k_readdir */
508 NULL, /* dsp56k_poll? */
509 dsp56k_ioctl,
510 NULL, /* no special dsp56k_mmap */
511 dsp56k_open,
512 NULL, /* flush */
513 dsp56k_release,
514 NULL, /* no special dsp56k_fsync */
515 NULL, /* no special dsp56k_fasync */
516 NULL, /* no special dsp56k_check_media_change */
517 NULL /* no special dsp56k_revalidate */
521 /****** Init and module functions ******/
522 int __init dsp56k_init(void)
524 if(!MACH_IS_ATARI || !ATARIHW_PRESENT(DSP56K)) {
525 printk("DSP56k driver: Hardware not present\n");
526 return -ENODEV;
529 if(register_chrdev(DSP56K_MAJOR, "dsp56k", &dsp56k_fops)) {
530 printk("DSP56k driver: Unable to register driver\n");
531 return -ENODEV;
534 dsp56k.in_use = 0;
536 printk("DSP56k driver installed\n");
538 return 0;
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 */