Merge with /pub/scm/linux/kernel/git/torvalds/linux-2.6.git
[sfrench/cifs-2.6.git] / sound / oss / msnd.c
1 /*********************************************************************
2  *
3  * msnd.c - Driver Base
4  *
5  * Turtle Beach MultiSound Sound Card Driver for Linux
6  *
7  * Copyright (C) 1998 Andrew Veliath
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  *
23  * $Id: msnd.c,v 1.17 1999/03/21 16:50:09 andrewtv Exp $
24  *
25  ********************************************************************/
26
27 #include <linux/module.h>
28 #include <linux/kernel.h>
29 #include <linux/slab.h>
30 #include <linux/vmalloc.h>
31 #include <linux/types.h>
32 #include <linux/delay.h>
33 #include <linux/mm.h>
34 #include <linux/init.h>
35 #include <linux/interrupt.h>
36
37 #include <asm/io.h>
38 #include <asm/uaccess.h>
39 #include <linux/spinlock.h>
40 #include <asm/irq.h>
41 #include "msnd.h"
42
43 #define LOGNAME                 "msnd"
44
45 #define MSND_MAX_DEVS           4
46
47 static multisound_dev_t         *devs[MSND_MAX_DEVS];
48 static int                      num_devs;
49
50 int __init msnd_register(multisound_dev_t *dev)
51 {
52         int i;
53
54         for (i = 0; i < MSND_MAX_DEVS; ++i)
55                 if (devs[i] == NULL)
56                         break;
57
58         if (i == MSND_MAX_DEVS)
59                 return -ENOMEM;
60
61         devs[i] = dev;
62         ++num_devs;
63         return 0;
64 }
65
66 void msnd_unregister(multisound_dev_t *dev)
67 {
68         int i;
69
70         for (i = 0; i < MSND_MAX_DEVS; ++i)
71                 if (devs[i] == dev)
72                         break;
73
74         if (i == MSND_MAX_DEVS) {
75                 printk(KERN_WARNING LOGNAME ": Unregistering unknown device\n");
76                 return;
77         }
78
79         devs[i] = NULL;
80         --num_devs;
81 }
82
83 void msnd_init_queue(void __iomem *base, int start, int size)
84 {
85         writew(PCTODSP_BASED(start), base + JQS_wStart);
86         writew(PCTODSP_OFFSET(size) - 1, base + JQS_wSize);
87         writew(0, base + JQS_wHead);
88         writew(0, base + JQS_wTail);
89 }
90
91 void msnd_fifo_init(msnd_fifo *f)
92 {
93         f->data = NULL;
94 }
95
96 void msnd_fifo_free(msnd_fifo *f)
97 {
98         if (f->data) {
99                 vfree(f->data);
100                 f->data = NULL;
101         }
102 }
103
104 int msnd_fifo_alloc(msnd_fifo *f, size_t n)
105 {
106         msnd_fifo_free(f);
107         f->data = (char *)vmalloc(n);
108         f->n = n;
109         f->tail = 0;
110         f->head = 0;
111         f->len = 0;
112
113         if (!f->data)
114                 return -ENOMEM;
115
116         return 0;
117 }
118
119 void msnd_fifo_make_empty(msnd_fifo *f)
120 {
121         f->len = f->tail = f->head = 0;
122 }
123
124 int msnd_fifo_write_io(msnd_fifo *f, char __iomem *buf, size_t len)
125 {
126         int count = 0;
127
128         while ((count < len) && (f->len != f->n)) {
129
130                 int nwritten;
131
132                 if (f->head <= f->tail) {
133                         nwritten = len - count;
134                         if (nwritten > f->n - f->tail)
135                                 nwritten = f->n - f->tail;
136                 }
137                 else {
138                         nwritten = f->head - f->tail;
139                         if (nwritten > len - count)
140                                 nwritten = len - count;
141                 }
142
143                 memcpy_fromio(f->data + f->tail, buf, nwritten);
144
145                 count += nwritten;
146                 buf += nwritten;
147                 f->len += nwritten;
148                 f->tail += nwritten;
149                 f->tail %= f->n;
150         }
151
152         return count;
153 }
154
155 int msnd_fifo_write(msnd_fifo *f, const char *buf, size_t len)
156 {
157         int count = 0;
158
159         while ((count < len) && (f->len != f->n)) {
160
161                 int nwritten;
162
163                 if (f->head <= f->tail) {
164                         nwritten = len - count;
165                         if (nwritten > f->n - f->tail)
166                                 nwritten = f->n - f->tail;
167                 }
168                 else {
169                         nwritten = f->head - f->tail;
170                         if (nwritten > len - count)
171                                 nwritten = len - count;
172                 }
173
174                 memcpy(f->data + f->tail, buf, nwritten);
175
176                 count += nwritten;
177                 buf += nwritten;
178                 f->len += nwritten;
179                 f->tail += nwritten;
180                 f->tail %= f->n;
181         }
182
183         return count;
184 }
185
186 int msnd_fifo_read_io(msnd_fifo *f, char __iomem *buf, size_t len)
187 {
188         int count = 0;
189
190         while ((count < len) && (f->len > 0)) {
191
192                 int nread;
193
194                 if (f->tail <= f->head) {
195                         nread = len - count;
196                         if (nread > f->n - f->head)
197                                 nread = f->n - f->head;
198                 }
199                 else {
200                         nread = f->tail - f->head;
201                         if (nread > len - count)
202                                 nread = len - count;
203                 }
204
205                 memcpy_toio(buf, f->data + f->head, nread);
206
207                 count += nread;
208                 buf += nread;
209                 f->len -= nread;
210                 f->head += nread;
211                 f->head %= f->n;
212         }
213
214         return count;
215 }
216
217 int msnd_fifo_read(msnd_fifo *f, char *buf, size_t len)
218 {
219         int count = 0;
220
221         while ((count < len) && (f->len > 0)) {
222
223                 int nread;
224
225                 if (f->tail <= f->head) {
226                         nread = len - count;
227                         if (nread > f->n - f->head)
228                                 nread = f->n - f->head;
229                 }
230                 else {
231                         nread = f->tail - f->head;
232                         if (nread > len - count)
233                                 nread = len - count;
234                 }
235
236                 memcpy(buf, f->data + f->head, nread);
237
238                 count += nread;
239                 buf += nread;
240                 f->len -= nread;
241                 f->head += nread;
242                 f->head %= f->n;
243         }
244
245         return count;
246 }
247
248 static int msnd_wait_TXDE(multisound_dev_t *dev)
249 {
250         register unsigned int io = dev->io;
251         register int timeout = 1000;
252     
253         while(timeout-- > 0)
254                 if (msnd_inb(io + HP_ISR) & HPISR_TXDE)
255                         return 0;
256
257         return -EIO;
258 }
259
260 static int msnd_wait_HC0(multisound_dev_t *dev)
261 {
262         register unsigned int io = dev->io;
263         register int timeout = 1000;
264
265         while(timeout-- > 0)
266                 if (!(msnd_inb(io + HP_CVR) & HPCVR_HC))
267                         return 0;
268
269         return -EIO;
270 }
271
272 int msnd_send_dsp_cmd(multisound_dev_t *dev, BYTE cmd)
273 {
274         unsigned long flags;
275
276         spin_lock_irqsave(&dev->lock, flags);
277         if (msnd_wait_HC0(dev) == 0) {
278                 msnd_outb(cmd, dev->io + HP_CVR);
279                 spin_unlock_irqrestore(&dev->lock, flags);
280                 return 0;
281         }
282         spin_unlock_irqrestore(&dev->lock, flags);
283
284         printk(KERN_DEBUG LOGNAME ": Send DSP command timeout\n");
285
286         return -EIO;
287 }
288
289 int msnd_send_word(multisound_dev_t *dev, unsigned char high,
290                    unsigned char mid, unsigned char low)
291 {
292         register unsigned int io = dev->io;
293
294         if (msnd_wait_TXDE(dev) == 0) {
295                 msnd_outb(high, io + HP_TXH);
296                 msnd_outb(mid, io + HP_TXM);
297                 msnd_outb(low, io + HP_TXL);
298                 return 0;
299         }
300
301         printk(KERN_DEBUG LOGNAME ": Send host word timeout\n");
302
303         return -EIO;
304 }
305
306 int msnd_upload_host(multisound_dev_t *dev, char *bin, int len)
307 {
308         int i;
309
310         if (len % 3 != 0) {
311                 printk(KERN_WARNING LOGNAME ": Upload host data not multiple of 3!\n");         
312                 return -EINVAL;
313         }
314
315         for (i = 0; i < len; i += 3)
316                 if (msnd_send_word(dev, bin[i], bin[i + 1], bin[i + 2]) != 0)
317                         return -EIO;
318
319         msnd_inb(dev->io + HP_RXL);
320         msnd_inb(dev->io + HP_CVR);
321
322         return 0;
323 }
324
325 int msnd_enable_irq(multisound_dev_t *dev)
326 {
327         unsigned long flags;
328
329         if (dev->irq_ref++)
330                 return 0;
331
332         printk(KERN_DEBUG LOGNAME ": Enabling IRQ\n");
333
334         spin_lock_irqsave(&dev->lock, flags);
335         if (msnd_wait_TXDE(dev) == 0) {
336                 msnd_outb(msnd_inb(dev->io + HP_ICR) | HPICR_TREQ, dev->io + HP_ICR);
337                 if (dev->type == msndClassic)
338                         msnd_outb(dev->irqid, dev->io + HP_IRQM);
339                 msnd_outb(msnd_inb(dev->io + HP_ICR) & ~HPICR_TREQ, dev->io + HP_ICR);
340                 msnd_outb(msnd_inb(dev->io + HP_ICR) | HPICR_RREQ, dev->io + HP_ICR);
341                 enable_irq(dev->irq);
342                 msnd_init_queue(dev->DSPQ, dev->dspq_data_buff, dev->dspq_buff_size);
343                 spin_unlock_irqrestore(&dev->lock, flags);
344                 return 0;
345         }
346         spin_unlock_irqrestore(&dev->lock, flags);
347
348         printk(KERN_DEBUG LOGNAME ": Enable IRQ failed\n");
349
350         return -EIO;
351 }
352
353 int msnd_disable_irq(multisound_dev_t *dev)
354 {
355         unsigned long flags;
356
357         if (--dev->irq_ref > 0)
358                 return 0;
359
360         if (dev->irq_ref < 0)
361                 printk(KERN_DEBUG LOGNAME ": IRQ ref count is %d\n", dev->irq_ref);
362
363         printk(KERN_DEBUG LOGNAME ": Disabling IRQ\n");
364
365         spin_lock_irqsave(&dev->lock, flags);
366         if (msnd_wait_TXDE(dev) == 0) {
367                 msnd_outb(msnd_inb(dev->io + HP_ICR) & ~HPICR_RREQ, dev->io + HP_ICR);
368                 if (dev->type == msndClassic)
369                         msnd_outb(HPIRQ_NONE, dev->io + HP_IRQM);
370                 disable_irq(dev->irq);
371                 spin_unlock_irqrestore(&dev->lock, flags);
372                 return 0;
373         }
374         spin_unlock_irqrestore(&dev->lock, flags);
375
376         printk(KERN_DEBUG LOGNAME ": Disable IRQ failed\n");
377
378         return -EIO;
379 }
380
381 #ifndef LINUX20
382 EXPORT_SYMBOL(msnd_register);
383 EXPORT_SYMBOL(msnd_unregister);
384
385 EXPORT_SYMBOL(msnd_init_queue);
386
387 EXPORT_SYMBOL(msnd_fifo_init);
388 EXPORT_SYMBOL(msnd_fifo_free);
389 EXPORT_SYMBOL(msnd_fifo_alloc);
390 EXPORT_SYMBOL(msnd_fifo_make_empty);
391 EXPORT_SYMBOL(msnd_fifo_write_io);
392 EXPORT_SYMBOL(msnd_fifo_read_io);
393 EXPORT_SYMBOL(msnd_fifo_write);
394 EXPORT_SYMBOL(msnd_fifo_read);
395
396 EXPORT_SYMBOL(msnd_send_dsp_cmd);
397 EXPORT_SYMBOL(msnd_send_word);
398 EXPORT_SYMBOL(msnd_upload_host);
399
400 EXPORT_SYMBOL(msnd_enable_irq);
401 EXPORT_SYMBOL(msnd_disable_irq);
402 #endif
403
404 #ifdef MODULE
405 MODULE_AUTHOR                           ("Andrew Veliath <andrewtv@usa.net>");
406 MODULE_DESCRIPTION                      ("Turtle Beach MultiSound Driver Base");
407 MODULE_LICENSE("GPL");
408
409
410 int init_module(void)
411 {
412         return 0;
413 }
414
415 void cleanup_module(void)
416 {
417 }
418 #endif