blob: e233cf280bc024498c3fdd637d219e8159bb6f2a [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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 */
99static 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};
138static int sizeof_bootstrap = 375;
139
140
141static struct dsp56k_device {
142 long in_use;
143 long maxio, timeout;
144 int tx_wsize, rx_wsize;
145} dsp56k;
146
gregkh@suse.deca8eca62005-03-23 09:53:09 -0800147static struct class *dsp56k_class;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148
149static 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
Al Virod85f6892006-01-12 01:06:31 -0800168static int dsp56k_upload(u_char __user *bin, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169{
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
Al Virod85f6892006-01-12 01:06:31 -0800202static ssize_t dsp56k_read(struct file *file, char __user *buf, size_t count,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203 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 {
Al Virod85f6892006-01-12 01:06:31 -0800228 short __user *data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229
230 count /= 2;
Al Virod85f6892006-01-12 01:06:31 -0800231 data = (short __user *) buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232 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 {
Al Virod85f6892006-01-12 01:06:31 -0800247 long __user *data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248
249 count /= 4;
Al Virod85f6892006-01-12 01:06:31 -0800250 data = (long __user *) buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251 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
Al Virod85f6892006-01-12 01:06:31 -0800265static ssize_t dsp56k_write(struct file *file, const char __user *buf, size_t count,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266 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 {
Al Virod85f6892006-01-12 01:06:31 -0800290 const short __user *data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291
292 count /= 2;
Al Virod85f6892006-01-12 01:06:31 -0800293 data = (const short __user *)buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294 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 {
Al Virod85f6892006-01-12 01:06:31 -0800309 const long __user *data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310
311 count /= 4;
Al Virod85f6892006-01-12 01:06:31 -0800312 data = (const long __user *)buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313 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
327static int dsp56k_ioctl(struct inode *inode, struct file *file,
328 unsigned int cmd, unsigned long arg)
329{
330 int dev = iminor(inode) & 0x0f;
Al Virod85f6892006-01-12 01:06:31 -0800331 void __user *argp = (void __user *)arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332
333 switch(dev)
334 {
335 case DSP56K_DEV_56001:
336
337 switch(cmd) {
338 case DSP56K_UPLOAD:
339 {
Al Virod85f6892006-01-12 01:06:31 -0800340 char __user *bin;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 int r, len;
Al Virod85f6892006-01-12 01:06:31 -0800342 struct dsp56k_upload __user *binary = argp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343
344 if(get_user(len, &binary->len) < 0)
345 return -EFAULT;
346 if(get_user(bin, &binary->bin) < 0)
347 return -EFAULT;
348
349 if (len == 0) {
350 return -EINVAL; /* nothing to upload?!? */
351 }
352 if (len > DSP56K_MAX_BINARY_LENGTH) {
353 return -EINVAL;
354 }
355
356 r = dsp56k_upload(bin, len);
357 if (r < 0) {
358 return r;
359 }
360
361 break;
362 }
363 case DSP56K_SET_TX_WSIZE:
364 if (arg > 4 || arg < 1)
365 return -EINVAL;
366 dsp56k.tx_wsize = (int) arg;
367 break;
368 case DSP56K_SET_RX_WSIZE:
369 if (arg > 4 || arg < 1)
370 return -EINVAL;
371 dsp56k.rx_wsize = (int) arg;
372 break;
373 case DSP56K_HOST_FLAGS:
374 {
375 int dir, out, status;
Al Virod85f6892006-01-12 01:06:31 -0800376 struct dsp56k_host_flags __user *hf = argp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377
378 if(get_user(dir, &hf->dir) < 0)
379 return -EFAULT;
380 if(get_user(out, &hf->out) < 0)
381 return -EFAULT;
382
383 if ((dir & 0x1) && (out & 0x1))
384 dsp56k_host_interface.icr |= DSP56K_ICR_HF0;
385 else if (dir & 0x1)
386 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0;
387 if ((dir & 0x2) && (out & 0x2))
388 dsp56k_host_interface.icr |= DSP56K_ICR_HF1;
389 else if (dir & 0x2)
390 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1;
391
392 status = 0;
393 if (dsp56k_host_interface.icr & DSP56K_ICR_HF0) status |= 0x1;
394 if (dsp56k_host_interface.icr & DSP56K_ICR_HF1) status |= 0x2;
395 if (dsp56k_host_interface.isr & DSP56K_ISR_HF2) status |= 0x4;
396 if (dsp56k_host_interface.isr & DSP56K_ISR_HF3) status |= 0x8;
397
398 return put_user(status, &hf->status);
399 }
400 case DSP56K_HOST_CMD:
401 if (arg > 31 || arg < 0)
402 return -EINVAL;
403 dsp56k_host_interface.cvr = (u_char)((arg & DSP56K_CVR_HV_MASK) |
404 DSP56K_CVR_HC);
405 break;
406 default:
407 return -EINVAL;
408 }
409 return 0;
410
411 default:
412 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
413 return -ENXIO;
414 }
415}
416
417/* As of 2.1.26 this should be dsp56k_poll,
418 * but how do I then check device minor number?
419 * Do I need this function at all???
420 */
421#if 0
422static unsigned int dsp56k_poll(struct file *file, poll_table *wait)
423{
424 int dev = iminor(file->f_dentry->d_inode) & 0x0f;
425
426 switch(dev)
427 {
428 case DSP56K_DEV_56001:
429 /* poll_wait(file, ???, wait); */
430 return POLLIN | POLLRDNORM | POLLOUT;
431
432 default:
433 printk("DSP56k driver: Unknown minor device: %d\n", dev);
434 return 0;
435 }
436}
437#endif
438
439static int dsp56k_open(struct inode *inode, struct file *file)
440{
441 int dev = iminor(inode) & 0x0f;
442
443 switch(dev)
444 {
445 case DSP56K_DEV_56001:
446
447 if (test_and_set_bit(0, &dsp56k.in_use))
448 return -EBUSY;
449
450 dsp56k.timeout = TIMEOUT;
451 dsp56k.maxio = MAXIO;
452 dsp56k.rx_wsize = dsp56k.tx_wsize = 4;
453
454 DSP56K_TX_INT_OFF;
455 DSP56K_RX_INT_OFF;
456
457 /* Zero host flags */
458 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0;
459 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1;
460
461 break;
462
463 default:
464 return -ENODEV;
465 }
466
467 return 0;
468}
469
470static int dsp56k_release(struct inode *inode, struct file *file)
471{
472 int dev = iminor(inode) & 0x0f;
473
474 switch(dev)
475 {
476 case DSP56K_DEV_56001:
477 clear_bit(0, &dsp56k.in_use);
478 break;
479 default:
480 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
481 return -ENXIO;
482 }
483
484 return 0;
485}
486
487static struct file_operations dsp56k_fops = {
488 .owner = THIS_MODULE,
489 .read = dsp56k_read,
490 .write = dsp56k_write,
491 .ioctl = dsp56k_ioctl,
492 .open = dsp56k_open,
493 .release = dsp56k_release,
494};
495
496
497/****** Init and module functions ******/
498
499static char banner[] __initdata = KERN_INFO "DSP56k driver installed\n";
500
501static int __init dsp56k_init_driver(void)
502{
503 int err = 0;
504
505 if(!MACH_IS_ATARI || !ATARIHW_PRESENT(DSP56K)) {
506 printk("DSP56k driver: Hardware not present\n");
507 return -ENODEV;
508 }
509
510 if(register_chrdev(DSP56K_MAJOR, "dsp56k", &dsp56k_fops)) {
511 printk("DSP56k driver: Unable to register driver\n");
512 return -ENODEV;
513 }
gregkh@suse.deca8eca62005-03-23 09:53:09 -0800514 dsp56k_class = class_create(THIS_MODULE, "dsp56k");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 if (IS_ERR(dsp56k_class)) {
516 err = PTR_ERR(dsp56k_class);
517 goto out_chrdev;
518 }
Greg Kroah-Hartman53f46542005-10-27 22:25:43 -0700519 class_device_create(dsp56k_class, NULL, MKDEV(DSP56K_MAJOR, 0), NULL, "dsp56k");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520
521 err = devfs_mk_cdev(MKDEV(DSP56K_MAJOR, 0),
522 S_IFCHR | S_IRUSR | S_IWUSR, "dsp56k");
523 if(err)
524 goto out_class;
525
526 printk(banner);
527 goto out;
528
529out_class:
gregkh@suse.deca8eca62005-03-23 09:53:09 -0800530 class_device_destroy(dsp56k_class, MKDEV(DSP56K_MAJOR, 0));
531 class_destroy(dsp56k_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532out_chrdev:
533 unregister_chrdev(DSP56K_MAJOR, "dsp56k");
534out:
535 return err;
536}
537module_init(dsp56k_init_driver);
538
539static void __exit dsp56k_cleanup_driver(void)
540{
gregkh@suse.deca8eca62005-03-23 09:53:09 -0800541 class_device_destroy(dsp56k_class, MKDEV(DSP56K_MAJOR, 0));
542 class_destroy(dsp56k_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 unregister_chrdev(DSP56K_MAJOR, "dsp56k");
544 devfs_remove("dsp56k");
545}
546module_exit(dsp56k_cleanup_driver);
547
548MODULE_LICENSE("GPL");