blob: db984e481d4ca42908fffee5fb426528d119e82b [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() */
Linus Torvalds1da177e2005-04-16 15:20:36 -070028#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>
Linus Torvalds1da177e2005-04-16 15:20:36 -070035#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 */
97static 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};
136static int sizeof_bootstrap = 375;
137
138
139static struct dsp56k_device {
140 long in_use;
141 long maxio, timeout;
142 int tx_wsize, rx_wsize;
143} dsp56k;
144
gregkh@suse.deca8eca62005-03-23 09:53:09 -0800145static struct class *dsp56k_class;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146
147static 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
Al Virod85f6892006-01-12 01:06:31 -0800166static int dsp56k_upload(u_char __user *bin, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167{
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
Al Virod85f6892006-01-12 01:06:31 -0800200static ssize_t dsp56k_read(struct file *file, char __user *buf, size_t count,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 loff_t *ppos)
202{
Josef Sipeka7113a92006-12-08 02:36:55 -0800203 struct inode *inode = file->f_path.dentry->d_inode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 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 {
Al Virod85f6892006-01-12 01:06:31 -0800226 short __user *data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227
228 count /= 2;
Al Virod85f6892006-01-12 01:06:31 -0800229 data = (short __user *) buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 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 {
Al Virod85f6892006-01-12 01:06:31 -0800245 long __user *data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246
247 count /= 4;
Al Virod85f6892006-01-12 01:06:31 -0800248 data = (long __user *) buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249 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
Al Virod85f6892006-01-12 01:06:31 -0800263static ssize_t dsp56k_write(struct file *file, const char __user *buf, size_t count,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 loff_t *ppos)
265{
Josef Sipeka7113a92006-12-08 02:36:55 -0800266 struct inode *inode = file->f_path.dentry->d_inode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267 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 {
Al Virod85f6892006-01-12 01:06:31 -0800288 const short __user *data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289
290 count /= 2;
Al Virod85f6892006-01-12 01:06:31 -0800291 data = (const short __user *)buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 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 {
Al Virod85f6892006-01-12 01:06:31 -0800307 const long __user *data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308
309 count /= 4;
Al Virod85f6892006-01-12 01:06:31 -0800310 data = (const long __user *)buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 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
325static int dsp56k_ioctl(struct inode *inode, struct file *file,
326 unsigned int cmd, unsigned long arg)
327{
328 int dev = iminor(inode) & 0x0f;
Al Virod85f6892006-01-12 01:06:31 -0800329 void __user *argp = (void __user *)arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330
331 switch(dev)
332 {
333 case DSP56K_DEV_56001:
334
335 switch(cmd) {
336 case DSP56K_UPLOAD:
337 {
Al Virod85f6892006-01-12 01:06:31 -0800338 char __user *bin;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 int r, len;
Al Virod85f6892006-01-12 01:06:31 -0800340 struct dsp56k_upload __user *binary = argp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341
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;
Al Virod85f6892006-01-12 01:06:31 -0800374 struct dsp56k_host_flags __user *hf = argp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375
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
420static unsigned int dsp56k_poll(struct file *file, poll_table *wait)
421{
Josef Sipeka7113a92006-12-08 02:36:55 -0800422 int dev = iminor(file->f_path.dentry->d_inode) & 0x0f;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423
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
437static 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
468static 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
Arjan van de Ven62322d22006-07-03 00:24:21 -0700485static const struct file_operations dsp56k_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486 .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
497static char banner[] __initdata = KERN_INFO "DSP56k driver installed\n";
498
499static 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 }
gregkh@suse.deca8eca62005-03-23 09:53:09 -0800512 dsp56k_class = class_create(THIS_MODULE, "dsp56k");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 if (IS_ERR(dsp56k_class)) {
514 err = PTR_ERR(dsp56k_class);
515 goto out_chrdev;
516 }
Greg Kroah-Hartman53f46542005-10-27 22:25:43 -0700517 class_device_create(dsp56k_class, NULL, MKDEV(DSP56K_MAJOR, 0), NULL, "dsp56k");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519 printk(banner);
520 goto out;
521
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522out_chrdev:
523 unregister_chrdev(DSP56K_MAJOR, "dsp56k");
524out:
525 return err;
526}
527module_init(dsp56k_init_driver);
528
529static void __exit dsp56k_cleanup_driver(void)
530{
gregkh@suse.deca8eca62005-03-23 09:53:09 -0800531 class_device_destroy(dsp56k_class, MKDEV(DSP56K_MAJOR, 0));
532 class_destroy(dsp56k_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533 unregister_chrdev(DSP56K_MAJOR, "dsp56k");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534}
535module_exit(dsp56k_cleanup_driver);
536
537MODULE_LICENSE("GPL");