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