ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / drivers / char / dsp56k.c
1 /*
2  * The DSP56001 Device Driver, saviour of the Free World(tm)
3  *
4  * Authors: Fredrik Noring   <noring@nocrew.org>
5  *          lars brinkhoff   <lars@nocrew.org>
6  *          Tomas Berndtsson <tomas@nocrew.org>
7  *
8  * First version May 1996
9  *
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
15  *
16  * BUGS:
17  *  Hmm... there must be something here :)
18  *
19  * Copyright (C) 1996,1997 Fredrik Noring, lars brinkhoff & Tomas Berndtsson
20  *
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.
24  */
25
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>
39
40 #include <asm/atarihw.h>
41 #include <asm/traps.h>
42 #include <asm/uaccess.h>        /* For put_user and get_user */
43
44 #include <asm/dsp56k.h>
45
46 /* minor devices */
47 #define DSP56K_DEV_56001        0    /* The only device so far */
48
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)
52
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
57
58 #define DSP56K_TRANSMIT         (dsp56k_host_interface.isr & DSP56K_ISR_TXDE)
59 #define DSP56K_RECEIVE          (dsp56k_host_interface.isr & DSP56K_ISR_RXDF)
60
61 #define wait_some(n) \
62 { \
63         set_current_state(TASK_INTERRUPTIBLE); \
64         schedule_timeout(n); \
65 }
66
67 #define handshake(count, maxio, timeout, ENABLE, f) \
68 { \
69         long i, t, m; \
70         while (count > 0) { \
71                 m = min_t(unsigned long, count, maxio); \
72                 for (i = 0; i < m; i++) { \
73                         for (t = 0; t < timeout && !ENABLE; t++) \
74                                 wait_some(HZ/50); \
75                         if(!ENABLE) \
76                                 return -EIO; \
77                         f; \
78                 } \
79                 count -= m; \
80                 if (m == maxio) wait_some(HZ/50); \
81         } \
82 }
83
84 #define tx_wait(n) \
85 { \
86         int t; \
87         for(t = 0; t < n && !DSP56K_TRANSMIT; t++) \
88                 wait_some(HZ/100); \
89         if(!DSP56K_TRANSMIT) { \
90                 return -EIO; \
91         } \
92 }
93
94 #define rx_wait(n) \
95 { \
96         int t; \
97         for(t = 0; t < n && !DSP56K_RECEIVE; t++) \
98                 wait_some(HZ/100); \
99         if(!DSP56K_RECEIVE) { \
100                 return -EIO; \
101         } \
102 }
103
104 /* DSP56001 bootstrap code */
105 static char bootstrap[] = {
106         0x0c, 0x00, 0x40, 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, 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, 0x60, 0xf4, 0x00, 0x00, 0x00, 0x4f, 0x61, 0xf4,
126         0x00, 0x00, 0x7e, 0xa9, 0x06, 0x2e, 0x80, 0x00, 0x00, 0x47,
127         0x07, 0xd8, 0x84, 0x07, 0x59, 0x84, 0x08, 0xf4, 0xa8, 0x00,
128         0x00, 0x04, 0x08, 0xf4, 0xbf, 0x00, 0x0c, 0x00, 0x00, 0xfe,
129         0xb8, 0x0a, 0xf0, 0x80, 0x00, 0x7e, 0xa9, 0x08, 0xf4, 0xa0,
130         0x00, 0x00, 0x01, 0x08, 0xf4, 0xbe, 0x00, 0x00, 0x00, 0x0a,
131         0xa9, 0x80, 0x00, 0x7e, 0xad, 0x08, 0x4e, 0x2b, 0x44, 0xf4,
132         0x00, 0x00, 0x00, 0x03, 0x44, 0xf4, 0x45, 0x00, 0x00, 0x01,
133         0x0e, 0xa0, 0x00, 0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xb5, 0x08,
134         0x50, 0x2b, 0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xb8, 0x08, 0x46,
135         0x2b, 0x44, 0xf4, 0x45, 0x00, 0x00, 0x02, 0x0a, 0xf0, 0xaa,
136         0x00, 0x7e, 0xc9, 0x20, 0x00, 0x45, 0x0a, 0xf0, 0xaa, 0x00,
137         0x7e, 0xd0, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xc6, 0x0a, 0xa9,
138         0x80, 0x00, 0x7e, 0xc4, 0x08, 0x58, 0x6b, 0x0a, 0xf0, 0x80,
139         0x00, 0x7e, 0xad, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xcd, 0x0a,
140         0xa9, 0x80, 0x00, 0x7e, 0xcb, 0x08, 0x58, 0xab, 0x0a, 0xf0,
141         0x80, 0x00, 0x7e, 0xad, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xd4,
142         0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xd2, 0x08, 0x58, 0xeb, 0x0a,
143         0xf0, 0x80, 0x00, 0x7e, 0xad};
144 static int sizeof_bootstrap = 375;
145
146
147 static struct dsp56k_device {
148         long in_use;
149         long maxio, timeout;
150         int tx_wsize, rx_wsize;
151 } dsp56k;
152
153 static struct class_simple *dsp56k_class;
154
155 static int dsp56k_reset(void)
156 {
157         u_char status;
158         
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;
164   
165         udelay(10);
166   
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;
170
171         return 0;
172 }
173
174 static int dsp56k_upload(u_char *bin, int len)
175 {
176         int i;
177         u_char *p;
178         
179         dsp56k_reset();
180   
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++;
187         }
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;
193         }
194   
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++);
200         }
201
202         tx_wait(10);
203         dsp56k_host_interface.data.l = 3;    /* Magic execute */
204
205         return 0;
206 }
207
208 static ssize_t dsp56k_read(struct file *file, char *buf, size_t count,
209                            loff_t *ppos)
210 {
211         struct inode *inode = file->f_dentry->d_inode;
212         int dev = iminor(inode) & 0x0f;
213
214         switch(dev)
215         {
216         case DSP56K_DEV_56001:
217         {
218
219                 long n;
220
221                 /* Don't do anything if nothing is to be done */
222                 if (!count) return 0;
223
224                 n = 0;
225                 switch (dsp56k.rx_wsize) {
226                 case 1:  /* 8 bit */
227                 {
228                         handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
229                                   put_user(dsp56k_host_interface.data.b[3], buf+n++));
230                         return n;
231                 }
232                 case 2:  /* 16 bit */
233                 {
234                         short *data;
235
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;
241                 }
242                 case 3:  /* 24 bit */
243                 {
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;
250                 }
251                 case 4:  /* 32 bit */
252                 {
253                         long *data;
254
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;
260                 }
261                 }
262                 return -EFAULT;
263         }
264
265         default:
266                 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
267                 return -ENXIO;
268         }
269 }
270
271 static ssize_t dsp56k_write(struct file *file, const char *buf, size_t count,
272                             loff_t *ppos)
273 {
274         struct inode *inode = file->f_dentry->d_inode;
275         int dev = iminor(inode) & 0x0f;
276
277         switch(dev)
278         {
279         case DSP56K_DEV_56001:
280         {
281                 long n;
282
283                 /* Don't do anything if nothing is to be done */
284                 if (!count) return 0;
285
286                 n = 0;
287                 switch (dsp56k.tx_wsize) {
288                 case 1:  /* 8 bit */
289                 {
290                         handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
291                                   get_user(dsp56k_host_interface.data.b[3], buf+n++));
292                         return n;
293                 }
294                 case 2:  /* 16 bit */
295                 {
296                         short *data;
297
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;
303                 }
304                 case 3:  /* 24 bit */
305                 {
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;
312                 }
313                 case 4:  /* 32 bit */
314                 {
315                         long *data;
316
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;
322                 }
323                 }
324
325                 return -EFAULT;
326         }
327         default:
328                 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
329                 return -ENXIO;
330         }
331 }
332
333 static int dsp56k_ioctl(struct inode *inode, struct file *file,
334                         unsigned int cmd, unsigned long arg)
335 {
336         int dev = iminor(inode) & 0x0f;
337
338         switch(dev)
339         {
340         case DSP56K_DEV_56001:
341
342                 switch(cmd) {
343                 case DSP56K_UPLOAD:
344                 {
345                         char *bin;
346                         int r, len;
347                         struct dsp56k_upload *binary = (struct dsp56k_upload *) arg;
348     
349                         if(get_user(len, &binary->len) < 0)
350                                 return -EFAULT;
351                         if(get_user(bin, &binary->bin) < 0)
352                                 return -EFAULT;
353                 
354                         if (len == 0) {
355                                 return -EINVAL;      /* nothing to upload?!? */
356                         }
357                         if (len > DSP56K_MAX_BINARY_LENGTH) {
358                                 return -EINVAL;
359                         }
360     
361                         r = dsp56k_upload(bin, len);
362                         if (r < 0) {
363                                 return r;
364                         }
365     
366                         break;
367                 }
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:
379                 {
380                         int dir, out, status;
381                         struct dsp56k_host_flags *hf = (struct dsp56k_host_flags*) arg;
382     
383                         if(get_user(dir, &hf->dir) < 0)
384                                 return -EFAULT;
385                         if(get_user(out, &hf->out) < 0)
386                                 return -EFAULT;
387
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;
396
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;
402
403                         return put_user(status, &hf->status);
404                 }
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;
413                 }
414                 return 0;
415
416         default:
417                 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
418                 return -ENXIO;
419         }
420 }
421
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???
425  */
426 #if 0
427 static unsigned int dsp56k_poll(struct file *file, poll_table *wait)
428 {
429         int dev = iminor(file->f_dentry->d_inode) & 0x0f;
430
431         switch(dev)
432         {
433         case DSP56K_DEV_56001:
434                 /* poll_wait(file, ???, wait); */
435                 return POLLIN | POLLRDNORM | POLLOUT;
436
437         default:
438                 printk("DSP56k driver: Unknown minor device: %d\n", dev);
439                 return 0;
440         }
441 }
442 #endif
443
444 static int dsp56k_open(struct inode *inode, struct file *file)
445 {
446         int dev = iminor(inode) & 0x0f;
447
448         switch(dev)
449         {
450         case DSP56K_DEV_56001:
451
452                 if (test_and_set_bit(0, &dsp56k.in_use))
453                         return -EBUSY;
454
455                 dsp56k.timeout = TIMEOUT;
456                 dsp56k.maxio = MAXIO;
457                 dsp56k.rx_wsize = dsp56k.tx_wsize = 4; 
458
459                 DSP56K_TX_INT_OFF;
460                 DSP56K_RX_INT_OFF;
461
462                 /* Zero host flags */
463                 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0;
464                 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1;
465
466                 break;
467
468         default:
469                 return -ENODEV;
470         }
471
472         return 0;
473 }
474
475 static int dsp56k_release(struct inode *inode, struct file *file)
476 {
477         int dev = iminor(inode) & 0x0f;
478
479         switch(dev)
480         {
481         case DSP56K_DEV_56001:
482                 clear_bit(0, &dsp56k.in_use);
483                 break;
484         default:
485                 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
486                 return -ENXIO;
487         }
488
489         return 0;
490 }
491
492 static struct file_operations dsp56k_fops = {
493         .owner          = THIS_MODULE,
494         .read           = dsp56k_read,
495         .write          = dsp56k_write,
496         .ioctl          = dsp56k_ioctl,
497         .open           = dsp56k_open,
498         .release        = dsp56k_release,
499 };
500
501
502 /****** Init and module functions ******/
503
504 static char banner[] __initdata = KERN_INFO "DSP56k driver installed\n";
505
506 static int __init dsp56k_init_driver(void)
507 {
508         int err = 0;
509
510         if(!MACH_IS_ATARI || !ATARIHW_PRESENT(DSP56K)) {
511                 printk("DSP56k driver: Hardware not present\n");
512                 return -ENODEV;
513         }
514
515         if(register_chrdev(DSP56K_MAJOR, "dsp56k", &dsp56k_fops)) {
516                 printk("DSP56k driver: Unable to register driver\n");
517                 return -ENODEV;
518         }
519         dsp56k_class = class_simple_create(THIS_MODULE, "dsp56k");
520         if (IS_ERR(dsp56k_class)) {
521                 err = PTR_ERR(dsp56k_class);
522                 goto out_chrdev;
523         }
524         class_simple_device_add(dsp56k_class, MKDEV(DSP56K_MAJOR, 0), NULL, "dsp56k");
525
526         err = devfs_mk_cdev(MKDEV(DSP56K_MAJOR, 0),
527                       S_IFCHR | S_IRUSR | S_IWUSR, "dsp56k");
528         if(err)
529                 goto out_class;
530
531         printk(banner);
532         goto out;
533
534 out_class:
535         class_simple_device_remove(MKDEV(DSP56K_MAJOR, 0));
536         class_simple_destroy(dsp56k_class);
537 out_chrdev:
538         unregister_chrdev(DSP56K_MAJOR, "dsp56k");
539 out:
540         return err;
541 }
542 module_init(dsp56k_init_driver);
543
544 static void __exit dsp56k_cleanup_driver(void)
545 {
546         class_simple_device_remove(MKDEV(DSP56K_MAJOR, 0));
547         class_simple_destroy(dsp56k_class);
548         unregister_chrdev(DSP56K_MAJOR, "dsp56k");
549         devfs_remove("dsp56k");
550 }
551 module_exit(dsp56k_cleanup_driver);
552
553 MODULE_LICENSE("GPL");