Merge branch 'master' into upstream
[pandora-kernel.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/major.h>
29 #include <linux/types.h>
30 #include <linux/errno.h>
31 #include <linux/delay.h>        /* guess what */
32 #include <linux/fs.h>
33 #include <linux/mm.h>
34 #include <linux/init.h>
35 #include <linux/smp_lock.h>
36 #include <linux/device.h>
37
38 #include <asm/atarihw.h>
39 #include <asm/traps.h>
40 #include <asm/uaccess.h>        /* For put_user and get_user */
41
42 #include <asm/dsp56k.h>
43
44 /* minor devices */
45 #define DSP56K_DEV_56001        0    /* The only device so far */
46
47 #define TIMEOUT    10   /* Host port timeout in number of tries */
48 #define MAXIO    2048   /* Maximum number of words before sleep */
49 #define DSP56K_MAX_BINARY_LENGTH (3*64*1024)
50
51 #define DSP56K_TX_INT_ON        dsp56k_host_interface.icr |=  DSP56K_ICR_TREQ
52 #define DSP56K_RX_INT_ON        dsp56k_host_interface.icr |=  DSP56K_ICR_RREQ
53 #define DSP56K_TX_INT_OFF       dsp56k_host_interface.icr &= ~DSP56K_ICR_TREQ
54 #define DSP56K_RX_INT_OFF       dsp56k_host_interface.icr &= ~DSP56K_ICR_RREQ
55
56 #define DSP56K_TRANSMIT         (dsp56k_host_interface.isr & DSP56K_ISR_TXDE)
57 #define DSP56K_RECEIVE          (dsp56k_host_interface.isr & DSP56K_ISR_RXDF)
58
59 #define handshake(count, maxio, timeout, ENABLE, f) \
60 { \
61         long i, t, m; \
62         while (count > 0) { \
63                 m = min_t(unsigned long, count, maxio); \
64                 for (i = 0; i < m; i++) { \
65                         for (t = 0; t < timeout && !ENABLE; t++) \
66                                 msleep(20); \
67                         if(!ENABLE) \
68                                 return -EIO; \
69                         f; \
70                 } \
71                 count -= m; \
72                 if (m == maxio) msleep(20); \
73         } \
74 }
75
76 #define tx_wait(n) \
77 { \
78         int t; \
79         for(t = 0; t < n && !DSP56K_TRANSMIT; t++) \
80                 msleep(10); \
81         if(!DSP56K_TRANSMIT) { \
82                 return -EIO; \
83         } \
84 }
85
86 #define rx_wait(n) \
87 { \
88         int t; \
89         for(t = 0; t < n && !DSP56K_RECEIVE; t++) \
90                 msleep(10); \
91         if(!DSP56K_RECEIVE) { \
92                 return -EIO; \
93         } \
94 }
95
96 /* DSP56001 bootstrap code */
97 static char bootstrap[] = {
98         0x0c, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
99         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
100         0x00, 0x00, 0x00, 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, 0x60, 0xf4, 0x00, 0x00, 0x00, 0x4f, 0x61, 0xf4,
118         0x00, 0x00, 0x7e, 0xa9, 0x06, 0x2e, 0x80, 0x00, 0x00, 0x47,
119         0x07, 0xd8, 0x84, 0x07, 0x59, 0x84, 0x08, 0xf4, 0xa8, 0x00,
120         0x00, 0x04, 0x08, 0xf4, 0xbf, 0x00, 0x0c, 0x00, 0x00, 0xfe,
121         0xb8, 0x0a, 0xf0, 0x80, 0x00, 0x7e, 0xa9, 0x08, 0xf4, 0xa0,
122         0x00, 0x00, 0x01, 0x08, 0xf4, 0xbe, 0x00, 0x00, 0x00, 0x0a,
123         0xa9, 0x80, 0x00, 0x7e, 0xad, 0x08, 0x4e, 0x2b, 0x44, 0xf4,
124         0x00, 0x00, 0x00, 0x03, 0x44, 0xf4, 0x45, 0x00, 0x00, 0x01,
125         0x0e, 0xa0, 0x00, 0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xb5, 0x08,
126         0x50, 0x2b, 0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xb8, 0x08, 0x46,
127         0x2b, 0x44, 0xf4, 0x45, 0x00, 0x00, 0x02, 0x0a, 0xf0, 0xaa,
128         0x00, 0x7e, 0xc9, 0x20, 0x00, 0x45, 0x0a, 0xf0, 0xaa, 0x00,
129         0x7e, 0xd0, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xc6, 0x0a, 0xa9,
130         0x80, 0x00, 0x7e, 0xc4, 0x08, 0x58, 0x6b, 0x0a, 0xf0, 0x80,
131         0x00, 0x7e, 0xad, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xcd, 0x0a,
132         0xa9, 0x80, 0x00, 0x7e, 0xcb, 0x08, 0x58, 0xab, 0x0a, 0xf0,
133         0x80, 0x00, 0x7e, 0xad, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xd4,
134         0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xd2, 0x08, 0x58, 0xeb, 0x0a,
135         0xf0, 0x80, 0x00, 0x7e, 0xad};
136 static int sizeof_bootstrap = 375;
137
138
139 static struct dsp56k_device {
140         long in_use;
141         long maxio, timeout;
142         int tx_wsize, rx_wsize;
143 } dsp56k;
144
145 static struct class *dsp56k_class;
146
147 static int dsp56k_reset(void)
148 {
149         u_char status;
150         
151         /* Power down the DSP */
152         sound_ym.rd_data_reg_sel = 14;
153         status = sound_ym.rd_data_reg_sel & 0xef;
154         sound_ym.wd_data = status;
155         sound_ym.wd_data = status | 0x10;
156   
157         udelay(10);
158   
159         /* Power up the DSP */
160         sound_ym.rd_data_reg_sel = 14;
161         sound_ym.wd_data = sound_ym.rd_data_reg_sel & 0xef;
162
163         return 0;
164 }
165
166 static int dsp56k_upload(u_char __user *bin, int len)
167 {
168         int i;
169         u_char *p;
170         
171         dsp56k_reset();
172   
173         p = bootstrap;
174         for (i = 0; i < sizeof_bootstrap/3; i++) {
175                 /* tx_wait(10); */
176                 dsp56k_host_interface.data.b[1] = *p++;
177                 dsp56k_host_interface.data.b[2] = *p++;
178                 dsp56k_host_interface.data.b[3] = *p++;
179         }
180         for (; i < 512; i++) {
181                 /* tx_wait(10); */
182                 dsp56k_host_interface.data.b[1] = 0;
183                 dsp56k_host_interface.data.b[2] = 0;
184                 dsp56k_host_interface.data.b[3] = 0;
185         }
186   
187         for (i = 0; i < len; i++) {
188                 tx_wait(10);
189                 get_user(dsp56k_host_interface.data.b[1], bin++);
190                 get_user(dsp56k_host_interface.data.b[2], bin++);
191                 get_user(dsp56k_host_interface.data.b[3], bin++);
192         }
193
194         tx_wait(10);
195         dsp56k_host_interface.data.l = 3;    /* Magic execute */
196
197         return 0;
198 }
199
200 static ssize_t dsp56k_read(struct file *file, char __user *buf, size_t count,
201                            loff_t *ppos)
202 {
203         struct inode *inode = file->f_path.dentry->d_inode;
204         int dev = iminor(inode) & 0x0f;
205
206         switch(dev)
207         {
208         case DSP56K_DEV_56001:
209         {
210
211                 long n;
212
213                 /* Don't do anything if nothing is to be done */
214                 if (!count) return 0;
215
216                 n = 0;
217                 switch (dsp56k.rx_wsize) {
218                 case 1:  /* 8 bit */
219                 {
220                         handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
221                                   put_user(dsp56k_host_interface.data.b[3], buf+n++));
222                         return n;
223                 }
224                 case 2:  /* 16 bit */
225                 {
226                         short __user *data;
227
228                         count /= 2;
229                         data = (short __user *) buf;
230                         handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
231                                   put_user(dsp56k_host_interface.data.w[1], data+n++));
232                         return 2*n;
233                 }
234                 case 3:  /* 24 bit */
235                 {
236                         count /= 3;
237                         handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
238                                   put_user(dsp56k_host_interface.data.b[1], buf+n++);
239                                   put_user(dsp56k_host_interface.data.b[2], buf+n++);
240                                   put_user(dsp56k_host_interface.data.b[3], buf+n++));
241                         return 3*n;
242                 }
243                 case 4:  /* 32 bit */
244                 {
245                         long __user *data;
246
247                         count /= 4;
248                         data = (long __user *) buf;
249                         handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
250                                   put_user(dsp56k_host_interface.data.l, data+n++));
251                         return 4*n;
252                 }
253                 }
254                 return -EFAULT;
255         }
256
257         default:
258                 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
259                 return -ENXIO;
260         }
261 }
262
263 static ssize_t dsp56k_write(struct file *file, const char __user *buf, size_t count,
264                             loff_t *ppos)
265 {
266         struct inode *inode = file->f_path.dentry->d_inode;
267         int dev = iminor(inode) & 0x0f;
268
269         switch(dev)
270         {
271         case DSP56K_DEV_56001:
272         {
273                 long n;
274
275                 /* Don't do anything if nothing is to be done */
276                 if (!count) return 0;
277
278                 n = 0;
279                 switch (dsp56k.tx_wsize) {
280                 case 1:  /* 8 bit */
281                 {
282                         handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
283                                   get_user(dsp56k_host_interface.data.b[3], buf+n++));
284                         return n;
285                 }
286                 case 2:  /* 16 bit */
287                 {
288                         const short __user *data;
289
290                         count /= 2;
291                         data = (const short __user *)buf;
292                         handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
293                                   get_user(dsp56k_host_interface.data.w[1], data+n++));
294                         return 2*n;
295                 }
296                 case 3:  /* 24 bit */
297                 {
298                         count /= 3;
299                         handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
300                                   get_user(dsp56k_host_interface.data.b[1], buf+n++);
301                                   get_user(dsp56k_host_interface.data.b[2], buf+n++);
302                                   get_user(dsp56k_host_interface.data.b[3], buf+n++));
303                         return 3*n;
304                 }
305                 case 4:  /* 32 bit */
306                 {
307                         const long __user *data;
308
309                         count /= 4;
310                         data = (const long __user *)buf;
311                         handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
312                                   get_user(dsp56k_host_interface.data.l, data+n++));
313                         return 4*n;
314                 }
315                 }
316
317                 return -EFAULT;
318         }
319         default:
320                 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
321                 return -ENXIO;
322         }
323 }
324
325 static int dsp56k_ioctl(struct inode *inode, struct file *file,
326                         unsigned int cmd, unsigned long arg)
327 {
328         int dev = iminor(inode) & 0x0f;
329         void __user *argp = (void __user *)arg;
330
331         switch(dev)
332         {
333         case DSP56K_DEV_56001:
334
335                 switch(cmd) {
336                 case DSP56K_UPLOAD:
337                 {
338                         char __user *bin;
339                         int r, len;
340                         struct dsp56k_upload __user *binary = argp;
341     
342                         if(get_user(len, &binary->len) < 0)
343                                 return -EFAULT;
344                         if(get_user(bin, &binary->bin) < 0)
345                                 return -EFAULT;
346                 
347                         if (len == 0) {
348                                 return -EINVAL;      /* nothing to upload?!? */
349                         }
350                         if (len > DSP56K_MAX_BINARY_LENGTH) {
351                                 return -EINVAL;
352                         }
353     
354                         r = dsp56k_upload(bin, len);
355                         if (r < 0) {
356                                 return r;
357                         }
358     
359                         break;
360                 }
361                 case DSP56K_SET_TX_WSIZE:
362                         if (arg > 4 || arg < 1)
363                                 return -EINVAL;
364                         dsp56k.tx_wsize = (int) arg;
365                         break;
366                 case DSP56K_SET_RX_WSIZE:
367                         if (arg > 4 || arg < 1)
368                                 return -EINVAL;
369                         dsp56k.rx_wsize = (int) arg;
370                         break;
371                 case DSP56K_HOST_FLAGS:
372                 {
373                         int dir, out, status;
374                         struct dsp56k_host_flags __user *hf = argp;
375     
376                         if(get_user(dir, &hf->dir) < 0)
377                                 return -EFAULT;
378                         if(get_user(out, &hf->out) < 0)
379                                 return -EFAULT;
380
381                         if ((dir & 0x1) && (out & 0x1))
382                                 dsp56k_host_interface.icr |= DSP56K_ICR_HF0;
383                         else if (dir & 0x1)
384                                 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0;
385                         if ((dir & 0x2) && (out & 0x2))
386                                 dsp56k_host_interface.icr |= DSP56K_ICR_HF1;
387                         else if (dir & 0x2)
388                                 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1;
389
390                         status = 0;
391                         if (dsp56k_host_interface.icr & DSP56K_ICR_HF0) status |= 0x1;
392                         if (dsp56k_host_interface.icr & DSP56K_ICR_HF1) status |= 0x2;
393                         if (dsp56k_host_interface.isr & DSP56K_ISR_HF2) status |= 0x4;
394                         if (dsp56k_host_interface.isr & DSP56K_ISR_HF3) status |= 0x8;
395
396                         return put_user(status, &hf->status);
397                 }
398                 case DSP56K_HOST_CMD:
399                         if (arg > 31 || arg < 0)
400                                 return -EINVAL;
401                         dsp56k_host_interface.cvr = (u_char)((arg & DSP56K_CVR_HV_MASK) |
402                                                              DSP56K_CVR_HC);
403                         break;
404                 default:
405                         return -EINVAL;
406                 }
407                 return 0;
408
409         default:
410                 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
411                 return -ENXIO;
412         }
413 }
414
415 /* As of 2.1.26 this should be dsp56k_poll,
416  * but how do I then check device minor number?
417  * Do I need this function at all???
418  */
419 #if 0
420 static unsigned int dsp56k_poll(struct file *file, poll_table *wait)
421 {
422         int dev = iminor(file->f_path.dentry->d_inode) & 0x0f;
423
424         switch(dev)
425         {
426         case DSP56K_DEV_56001:
427                 /* poll_wait(file, ???, wait); */
428                 return POLLIN | POLLRDNORM | POLLOUT;
429
430         default:
431                 printk("DSP56k driver: Unknown minor device: %d\n", dev);
432                 return 0;
433         }
434 }
435 #endif
436
437 static int dsp56k_open(struct inode *inode, struct file *file)
438 {
439         int dev = iminor(inode) & 0x0f;
440
441         switch(dev)
442         {
443         case DSP56K_DEV_56001:
444
445                 if (test_and_set_bit(0, &dsp56k.in_use))
446                         return -EBUSY;
447
448                 dsp56k.timeout = TIMEOUT;
449                 dsp56k.maxio = MAXIO;
450                 dsp56k.rx_wsize = dsp56k.tx_wsize = 4; 
451
452                 DSP56K_TX_INT_OFF;
453                 DSP56K_RX_INT_OFF;
454
455                 /* Zero host flags */
456                 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0;
457                 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1;
458
459                 break;
460
461         default:
462                 return -ENODEV;
463         }
464
465         return 0;
466 }
467
468 static int dsp56k_release(struct inode *inode, struct file *file)
469 {
470         int dev = iminor(inode) & 0x0f;
471
472         switch(dev)
473         {
474         case DSP56K_DEV_56001:
475                 clear_bit(0, &dsp56k.in_use);
476                 break;
477         default:
478                 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
479                 return -ENXIO;
480         }
481
482         return 0;
483 }
484
485 static const struct file_operations dsp56k_fops = {
486         .owner          = THIS_MODULE,
487         .read           = dsp56k_read,
488         .write          = dsp56k_write,
489         .ioctl          = dsp56k_ioctl,
490         .open           = dsp56k_open,
491         .release        = dsp56k_release,
492 };
493
494
495 /****** Init and module functions ******/
496
497 static char banner[] __initdata = KERN_INFO "DSP56k driver installed\n";
498
499 static int __init dsp56k_init_driver(void)
500 {
501         int err = 0;
502
503         if(!MACH_IS_ATARI || !ATARIHW_PRESENT(DSP56K)) {
504                 printk("DSP56k driver: Hardware not present\n");
505                 return -ENODEV;
506         }
507
508         if(register_chrdev(DSP56K_MAJOR, "dsp56k", &dsp56k_fops)) {
509                 printk("DSP56k driver: Unable to register driver\n");
510                 return -ENODEV;
511         }
512         dsp56k_class = class_create(THIS_MODULE, "dsp56k");
513         if (IS_ERR(dsp56k_class)) {
514                 err = PTR_ERR(dsp56k_class);
515                 goto out_chrdev;
516         }
517         class_device_create(dsp56k_class, NULL, MKDEV(DSP56K_MAJOR, 0), NULL, "dsp56k");
518
519         printk(banner);
520         goto out;
521
522 out_chrdev:
523         unregister_chrdev(DSP56K_MAJOR, "dsp56k");
524 out:
525         return err;
526 }
527 module_init(dsp56k_init_driver);
528
529 static void __exit dsp56k_cleanup_driver(void)
530 {
531         class_device_destroy(dsp56k_class, MKDEV(DSP56K_MAJOR, 0));
532         class_destroy(dsp56k_class);
533         unregister_chrdev(DSP56K_MAJOR, "dsp56k");
534 }
535 module_exit(dsp56k_cleanup_driver);
536
537 MODULE_LICENSE("GPL");