Linux-2.6.12-rc2
[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 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         } \
76 }
77
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         } \
86 }
87
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         } \
96 }
97
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;
139
140
141 static struct dsp56k_device {
142         long in_use;
143         long maxio, timeout;
144         int tx_wsize, rx_wsize;
145 } dsp56k;
146
147 static struct class_simple *dsp56k_class;
148
149 static int dsp56k_reset(void)
150 {
151         u_char status;
152         
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;
158   
159         udelay(10);
160   
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;
164
165         return 0;
166 }
167
168 static int dsp56k_upload(u_char *bin, int len)
169 {
170         int i;
171         u_char *p;
172         
173         dsp56k_reset();
174   
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++;
181         }
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;
187         }
188   
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++);
194         }
195
196         tx_wait(10);
197         dsp56k_host_interface.data.l = 3;    /* Magic execute */
198
199         return 0;
200 }
201
202 static ssize_t dsp56k_read(struct file *file, char *buf, size_t count,
203                            loff_t *ppos)
204 {
205         struct inode *inode = file->f_dentry->d_inode;
206         int dev = iminor(inode) & 0x0f;
207
208         switch(dev)
209         {
210         case DSP56K_DEV_56001:
211         {
212
213                 long n;
214
215                 /* Don't do anything if nothing is to be done */
216                 if (!count) return 0;
217
218                 n = 0;
219                 switch (dsp56k.rx_wsize) {
220                 case 1:  /* 8 bit */
221                 {
222                         handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
223                                   put_user(dsp56k_host_interface.data.b[3], buf+n++));
224                         return n;
225                 }
226                 case 2:  /* 16 bit */
227                 {
228                         short *data;
229
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;
235                 }
236                 case 3:  /* 24 bit */
237                 {
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;
244                 }
245                 case 4:  /* 32 bit */
246                 {
247                         long *data;
248
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;
254                 }
255                 }
256                 return -EFAULT;
257         }
258
259         default:
260                 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
261                 return -ENXIO;
262         }
263 }
264
265 static ssize_t dsp56k_write(struct file *file, const char *buf, size_t count,
266                             loff_t *ppos)
267 {
268         struct inode *inode = file->f_dentry->d_inode;
269         int dev = iminor(inode) & 0x0f;
270
271         switch(dev)
272         {
273         case DSP56K_DEV_56001:
274         {
275                 long n;
276
277                 /* Don't do anything if nothing is to be done */
278                 if (!count) return 0;
279
280                 n = 0;
281                 switch (dsp56k.tx_wsize) {
282                 case 1:  /* 8 bit */
283                 {
284                         handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
285                                   get_user(dsp56k_host_interface.data.b[3], buf+n++));
286                         return n;
287                 }
288                 case 2:  /* 16 bit */
289                 {
290                         const short *data;
291
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;
297                 }
298                 case 3:  /* 24 bit */
299                 {
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;
306                 }
307                 case 4:  /* 32 bit */
308                 {
309                         const long *data;
310
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;
316                 }
317                 }
318
319                 return -EFAULT;
320         }
321         default:
322                 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
323                 return -ENXIO;
324         }
325 }
326
327 static int dsp56k_ioctl(struct inode *inode, struct file *file,
328                         unsigned int cmd, unsigned long arg)
329 {
330         int dev = iminor(inode) & 0x0f;
331
332         switch(dev)
333         {
334         case DSP56K_DEV_56001:
335
336                 switch(cmd) {
337                 case DSP56K_UPLOAD:
338                 {
339                         char *bin;
340                         int r, len;
341                         struct dsp56k_upload *binary = (struct dsp56k_upload *) arg;
342     
343                         if(get_user(len, &binary->len) < 0)
344                                 return -EFAULT;
345                         if(get_user(bin, &binary->bin) < 0)
346                                 return -EFAULT;
347                 
348                         if (len == 0) {
349                                 return -EINVAL;      /* nothing to upload?!? */
350                         }
351                         if (len > DSP56K_MAX_BINARY_LENGTH) {
352                                 return -EINVAL;
353                         }
354     
355                         r = dsp56k_upload(bin, len);
356                         if (r < 0) {
357                                 return r;
358                         }
359     
360                         break;
361                 }
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:
373                 {
374                         int dir, out, status;
375                         struct dsp56k_host_flags *hf = (struct dsp56k_host_flags*) arg;
376     
377                         if(get_user(dir, &hf->dir) < 0)
378                                 return -EFAULT;
379                         if(get_user(out, &hf->out) < 0)
380                                 return -EFAULT;
381
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;
390
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;
396
397                         return put_user(status, &hf->status);
398                 }
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;
407                 }
408                 return 0;
409
410         default:
411                 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
412                 return -ENXIO;
413         }
414 }
415
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???
419  */
420 #if 0
421 static unsigned int dsp56k_poll(struct file *file, poll_table *wait)
422 {
423         int dev = iminor(file->f_dentry->d_inode) & 0x0f;
424
425         switch(dev)
426         {
427         case DSP56K_DEV_56001:
428                 /* poll_wait(file, ???, wait); */
429                 return POLLIN | POLLRDNORM | POLLOUT;
430
431         default:
432                 printk("DSP56k driver: Unknown minor device: %d\n", dev);
433                 return 0;
434         }
435 }
436 #endif
437
438 static int dsp56k_open(struct inode *inode, struct file *file)
439 {
440         int dev = iminor(inode) & 0x0f;
441
442         switch(dev)
443         {
444         case DSP56K_DEV_56001:
445
446                 if (test_and_set_bit(0, &dsp56k.in_use))
447                         return -EBUSY;
448
449                 dsp56k.timeout = TIMEOUT;
450                 dsp56k.maxio = MAXIO;
451                 dsp56k.rx_wsize = dsp56k.tx_wsize = 4; 
452
453                 DSP56K_TX_INT_OFF;
454                 DSP56K_RX_INT_OFF;
455
456                 /* Zero host flags */
457                 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0;
458                 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1;
459
460                 break;
461
462         default:
463                 return -ENODEV;
464         }
465
466         return 0;
467 }
468
469 static int dsp56k_release(struct inode *inode, struct file *file)
470 {
471         int dev = iminor(inode) & 0x0f;
472
473         switch(dev)
474         {
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;
481         }
482
483         return 0;
484 }
485
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,
493 };
494
495
496 /****** Init and module functions ******/
497
498 static char banner[] __initdata = KERN_INFO "DSP56k driver installed\n";
499
500 static int __init dsp56k_init_driver(void)
501 {
502         int err = 0;
503
504         if(!MACH_IS_ATARI || !ATARIHW_PRESENT(DSP56K)) {
505                 printk("DSP56k driver: Hardware not present\n");
506                 return -ENODEV;
507         }
508
509         if(register_chrdev(DSP56K_MAJOR, "dsp56k", &dsp56k_fops)) {
510                 printk("DSP56k driver: Unable to register driver\n");
511                 return -ENODEV;
512         }
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;
517         }
518         class_simple_device_add(dsp56k_class, MKDEV(DSP56K_MAJOR, 0), NULL, "dsp56k");
519
520         err = devfs_mk_cdev(MKDEV(DSP56K_MAJOR, 0),
521                       S_IFCHR | S_IRUSR | S_IWUSR, "dsp56k");
522         if(err)
523                 goto out_class;
524
525         printk(banner);
526         goto out;
527
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;
535 }
536 module_init(dsp56k_init_driver);
537
538 static void __exit dsp56k_cleanup_driver(void)
539 {
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");
544 }
545 module_exit(dsp56k_cleanup_driver);
546
547 MODULE_LICENSE("GPL");