~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/oss/dmasound/dmasound_q40.c

Version: ~ [ linux-5.2-rc1 ] ~ [ linux-5.1.2 ] ~ [ linux-5.0.16 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.43 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.119 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.176 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.179 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.139 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.67 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ linux-2.6.39.4 ] ~ [ linux-2.6.38.8 ] ~ [ linux-2.6.37.6 ] ~ [ linux-2.6.36.4 ] ~ [ linux-2.6.35.14 ] ~ [ linux-2.6.34.15 ] ~ [ linux-2.6.33.20 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  *  linux/drivers/sound/dmasound/dmasound_q40.c
  3  *
  4  *  Q40 DMA Sound Driver
  5  *
  6  *  See linux/drivers/sound/dmasound/dmasound_core.c for copyright and credits
  7  *  prior to 28/01/2001
  8  *
  9  *  28/01/2001 [0.1] Iain Sandoe
 10  *                   - added versioning
 11  *                   - put in and populated the hardware_afmts field.
 12  *             [0.2] - put in SNDCTL_DSP_GETCAPS value.
 13  *             [0.3] - put in default hard/soft settings.
 14  */
 15 
 16 
 17 #include <linux/module.h>
 18 #include <linux/init.h>
 19 #include <linux/slab.h>
 20 #include <linux/soundcard.h>
 21 #include <linux/interrupt.h>
 22 
 23 #include <asm/uaccess.h>
 24 #include <asm/q40ints.h>
 25 #include <asm/q40_master.h>
 26 
 27 #include "dmasound.h"
 28 
 29 #define DMASOUND_Q40_REVISION 0
 30 #define DMASOUND_Q40_EDITION 3
 31 
 32 static int expand_bal;  /* Balance factor for expanding (not volume!) */
 33 static int expand_data; /* Data for expanding */
 34 
 35 
 36 /*** Low level stuff *********************************************************/
 37 
 38 
 39 static void *Q40Alloc(unsigned int size, int flags);
 40 static void Q40Free(void *, unsigned int);
 41 static int Q40IrqInit(void);
 42 #ifdef MODULE
 43 static void Q40IrqCleanUp(void);
 44 #endif
 45 static void Q40Silence(void);
 46 static void Q40Init(void);
 47 static int Q40SetFormat(int format);
 48 static int Q40SetVolume(int volume);
 49 static void Q40PlayNextFrame(int index);
 50 static void Q40Play(void);
 51 static irqreturn_t Q40StereoInterrupt(int irq, void *dummy, struct pt_regs *fp);
 52 static irqreturn_t Q40MonoInterrupt(int irq, void *dummy, struct pt_regs *fp);
 53 static void Q40Interrupt(void);
 54 
 55 
 56 /*** Mid level stuff *********************************************************/
 57 
 58 
 59 
 60 /* userCount, frameUsed, frameLeft == byte counts */
 61 static ssize_t q40_ct_law(const u_char *userPtr, size_t userCount,
 62                            u_char frame[], ssize_t *frameUsed,
 63                            ssize_t frameLeft)
 64 {
 65         char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8;
 66         ssize_t count, used;
 67         u_char *p = (u_char *) &frame[*frameUsed];
 68 
 69         used = count = min_t(size_t, userCount, frameLeft);
 70         if (copy_from_user(p,userPtr,count))
 71           return -EFAULT;
 72         while (count > 0) {
 73                 *p = table[*p]+128;
 74                 p++;
 75                 count--;
 76         }
 77         *frameUsed += used ;
 78         return used;
 79 }
 80 
 81 
 82 static ssize_t q40_ct_s8(const u_char *userPtr, size_t userCount,
 83                           u_char frame[], ssize_t *frameUsed,
 84                           ssize_t frameLeft)
 85 {
 86         ssize_t count, used;
 87         u_char *p = (u_char *) &frame[*frameUsed];
 88 
 89         used = count = min_t(size_t, userCount, frameLeft);
 90         if (copy_from_user(p,userPtr,count))
 91           return -EFAULT;
 92         while (count > 0) {
 93                 *p = *p + 128;
 94                 p++;
 95                 count--;
 96         }
 97         *frameUsed += used;
 98         return used;
 99 }
100 
101 static ssize_t q40_ct_u8(const u_char *userPtr, size_t userCount,
102                           u_char frame[], ssize_t *frameUsed,
103                           ssize_t frameLeft)
104 {
105         ssize_t count, used;
106         u_char *p = (u_char *) &frame[*frameUsed];
107 
108         used = count = min_t(size_t, userCount, frameLeft);
109         if (copy_from_user(p,userPtr,count))
110           return -EFAULT;
111         *frameUsed += used;
112         return used;
113 }
114 
115 
116 /* a bit too complicated to optimise right now ..*/
117 static ssize_t q40_ctx_law(const u_char *userPtr, size_t userCount,
118                             u_char frame[], ssize_t *frameUsed,
119                             ssize_t frameLeft)
120 {
121         unsigned char *table = (unsigned char *)
122                 (dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8);
123         unsigned int data = expand_data;
124         u_char *p = (u_char *) &frame[*frameUsed];
125         int bal = expand_bal;
126         int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
127         int utotal, ftotal;
128 
129         ftotal = frameLeft;
130         utotal = userCount;
131         while (frameLeft) {
132                 u_char c;
133                 if (bal < 0) {
134                         if (userCount == 0)
135                                 break;
136                         if (get_user(c, userPtr++))
137                                 return -EFAULT;
138                         data = table[c];
139                         data += 0x80;
140                         userCount--;
141                         bal += hSpeed;
142                 }
143                 *p++ = data;
144                 frameLeft--;
145                 bal -= sSpeed;
146         }
147         expand_bal = bal;
148         expand_data = data;
149         *frameUsed += (ftotal - frameLeft);
150         utotal -= userCount;
151         return utotal;
152 }
153 
154 
155 static ssize_t q40_ctx_s8(const u_char *userPtr, size_t userCount,
156                            u_char frame[], ssize_t *frameUsed,
157                            ssize_t frameLeft)
158 {
159         u_char *p = (u_char *) &frame[*frameUsed];
160         unsigned int data = expand_data;
161         int bal = expand_bal;
162         int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
163         int utotal, ftotal;
164 
165 
166         ftotal = frameLeft;
167         utotal = userCount;
168         while (frameLeft) {
169                 u_char c;
170                 if (bal < 0) {
171                         if (userCount == 0)
172                                 break;
173                         if (get_user(c, userPtr++))
174                                 return -EFAULT;
175                         data = c ;
176                         data += 0x80;
177                         userCount--;
178                         bal += hSpeed;
179                 }
180                 *p++ = data;
181                 frameLeft--;
182                 bal -= sSpeed;
183         }
184         expand_bal = bal;
185         expand_data = data;
186         *frameUsed += (ftotal - frameLeft);
187         utotal -= userCount;
188         return utotal;
189 }
190 
191 
192 static ssize_t q40_ctx_u8(const u_char *userPtr, size_t userCount,
193                            u_char frame[], ssize_t *frameUsed,
194                            ssize_t frameLeft)
195 {
196         u_char *p = (u_char *) &frame[*frameUsed];
197         unsigned int data = expand_data;
198         int bal = expand_bal;
199         int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
200         int utotal, ftotal;
201 
202         ftotal = frameLeft;
203         utotal = userCount;
204         while (frameLeft) {
205                 u_char c;
206                 if (bal < 0) {
207                         if (userCount == 0)
208                                 break;
209                         if (get_user(c, userPtr++))
210                                 return -EFAULT;
211                         data = c ;
212                         userCount--;
213                         bal += hSpeed;
214                 }
215                 *p++ = data;
216                 frameLeft--;
217                 bal -= sSpeed;
218         }
219         expand_bal = bal;
220         expand_data = data;
221         *frameUsed += (ftotal - frameLeft) ;
222         utotal -= userCount;
223         return utotal;
224 }
225 
226 /* compressing versions */
227 static ssize_t q40_ctc_law(const u_char *userPtr, size_t userCount,
228                             u_char frame[], ssize_t *frameUsed,
229                             ssize_t frameLeft)
230 {
231         unsigned char *table = (unsigned char *)
232                 (dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8);
233         unsigned int data = expand_data;
234         u_char *p = (u_char *) &frame[*frameUsed];
235         int bal = expand_bal;
236         int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
237         int utotal, ftotal;
238  
239         ftotal = frameLeft;
240         utotal = userCount;
241         while (frameLeft) {
242                 u_char c;
243                 while(bal<0) {
244                         if (userCount == 0)
245                                 goto lout;
246                         if (!(bal<(-hSpeed))) {
247                                 if (get_user(c, userPtr))
248                                         return -EFAULT;
249                                 data = 0x80 + table[c];
250                         }
251                         userPtr++;
252                         userCount--;
253                         bal += hSpeed;
254                 }
255                 *p++ = data;
256                 frameLeft--;
257                 bal -= sSpeed;
258         }
259  lout:
260         expand_bal = bal;
261         expand_data = data;
262         *frameUsed += (ftotal - frameLeft);
263         utotal -= userCount;
264         return utotal;
265 }
266 
267 
268 static ssize_t q40_ctc_s8(const u_char *userPtr, size_t userCount,
269                            u_char frame[], ssize_t *frameUsed,
270                            ssize_t frameLeft)
271 {
272         u_char *p = (u_char *) &frame[*frameUsed];
273         unsigned int data = expand_data;
274         int bal = expand_bal;
275         int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
276         int utotal, ftotal;
277 
278         ftotal = frameLeft;
279         utotal = userCount;
280         while (frameLeft) {
281                 u_char c;
282                 while (bal < 0) {
283                         if (userCount == 0)
284                                 goto lout;
285                         if (!(bal<(-hSpeed))) {
286                                 if (get_user(c, userPtr))
287                                         return -EFAULT;
288                                 data = c + 0x80;
289                         }
290                         userPtr++;
291                         userCount--;
292                         bal += hSpeed;
293                 }
294                 *p++ = data;
295                 frameLeft--;
296                 bal -= sSpeed;
297         }
298  lout:
299         expand_bal = bal;
300         expand_data = data;
301         *frameUsed += (ftotal - frameLeft);
302         utotal -= userCount;
303         return utotal;
304 }
305 
306 
307 static ssize_t q40_ctc_u8(const u_char *userPtr, size_t userCount,
308                            u_char frame[], ssize_t *frameUsed,
309                            ssize_t frameLeft)
310 {
311         u_char *p = (u_char *) &frame[*frameUsed];
312         unsigned int data = expand_data;
313         int bal = expand_bal;
314         int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
315         int utotal, ftotal;
316 
317         ftotal = frameLeft;
318         utotal = userCount;
319         while (frameLeft) {
320                 u_char c;
321                 while (bal < 0) {
322                         if (userCount == 0)
323                                 goto lout;
324                         if (!(bal<(-hSpeed))) {
325                                 if (get_user(c, userPtr))
326                                         return -EFAULT;
327                                 data = c ;
328                         }
329                         userPtr++;
330                         userCount--;
331                         bal += hSpeed;
332                 }
333                 *p++ = data;
334                 frameLeft--;
335                 bal -= sSpeed;
336         }
337  lout:
338         expand_bal = bal;
339         expand_data = data;
340         *frameUsed += (ftotal - frameLeft) ;
341         utotal -= userCount;
342         return utotal;
343 }
344 
345 
346 static TRANS transQ40Normal = {
347         q40_ct_law, q40_ct_law, q40_ct_s8, q40_ct_u8, NULL, NULL, NULL, NULL
348 };
349 
350 static TRANS transQ40Expanding = {
351         q40_ctx_law, q40_ctx_law, q40_ctx_s8, q40_ctx_u8, NULL, NULL, NULL, NULL
352 };
353 
354 static TRANS transQ40Compressing = {
355         q40_ctc_law, q40_ctc_law, q40_ctc_s8, q40_ctc_u8, NULL, NULL, NULL, NULL
356 };
357 
358 
359 /*** Low level stuff *********************************************************/
360 
361 static void *Q40Alloc(unsigned int size, int flags)
362 {
363          return kmalloc(size, flags); /* change to vmalloc */
364 }
365 
366 static void Q40Free(void *ptr, unsigned int size)
367 {
368         kfree(ptr);
369 }
370 
371 static int __init Q40IrqInit(void)
372 {
373         /* Register interrupt handler. */
374         request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0,
375                     "DMA sound", Q40Interrupt);
376 
377         return(1);
378 }
379 
380 
381 #ifdef MODULE
382 static void Q40IrqCleanUp(void)
383 {
384         master_outb(0,SAMPLE_ENABLE_REG);
385         free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
386 }
387 #endif /* MODULE */
388 
389 
390 static void Q40Silence(void)
391 {
392         master_outb(0,SAMPLE_ENABLE_REG);
393         *DAC_LEFT=*DAC_RIGHT=127;
394 }
395 
396 static char *q40_pp;
397 static unsigned int q40_sc;
398 
399 static void Q40PlayNextFrame(int index)
400 {
401         u_char *start;
402         u_long size;
403         u_char speed;
404 
405         /* used by Q40Play() if all doubts whether there really is something
406          * to be played are already wiped out.
407          */
408         start = write_sq.buffers[write_sq.front];
409         size = (write_sq.count == index ? write_sq.rear_size : write_sq.block_size);
410 
411         q40_pp=start;
412         q40_sc=size;
413 
414         write_sq.front = (write_sq.front+1) % write_sq.max_count;
415         write_sq.active++;
416 
417         speed=(dmasound.hard.speed==10000 ? 0 : 1);
418 
419         master_outb( 0,SAMPLE_ENABLE_REG);
420         free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
421         if (dmasound.soft.stereo)
422                 request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0,
423                     "Q40 sound", Q40Interrupt);
424           else
425                 request_irq(Q40_IRQ_SAMPLE, Q40MonoInterrupt, 0,
426                     "Q40 sound", Q40Interrupt);
427 
428         master_outb( speed, SAMPLE_RATE_REG);
429         master_outb( 1,SAMPLE_CLEAR_REG);
430         master_outb( 1,SAMPLE_ENABLE_REG);
431 }
432 
433 static void Q40Play(void)
434 {
435         unsigned long flags;
436 
437         if (write_sq.active || write_sq.count<=0 ) {
438                 /* There's already a frame loaded */
439                 return;
440         }
441 
442         /* nothing in the queue */
443         if (write_sq.count <= 1 && write_sq.rear_size < write_sq.block_size && !write_sq.syncing) {
444                  /* hmmm, the only existing frame is not
445                   * yet filled and we're not syncing?
446                   */
447                  return;
448         }
449         spin_lock_irqsave(&dmasound.lock, flags);
450         Q40PlayNextFrame(1);
451         spin_unlock_irqrestore(&dmasound.lock, flags);
452 }
453 
454 static irqreturn_t Q40StereoInterrupt(int irq, void *dummy, struct pt_regs *fp)
455 {
456         spin_lock(&dmasound.lock);
457         if (q40_sc>1){
458             *DAC_LEFT=*q40_pp++;
459             *DAC_RIGHT=*q40_pp++;
460             q40_sc -=2;
461             master_outb(1,SAMPLE_CLEAR_REG);
462         }else Q40Interrupt();
463         spin_unlock(&dmasound.lock);
464         return IRQ_HANDLED;
465 }
466 static irqreturn_t Q40MonoInterrupt(int irq, void *dummy, struct pt_regs *fp)
467 {
468         spin_lock(&dmasound.lock);
469         if (q40_sc>0){
470             *DAC_LEFT=*q40_pp;
471             *DAC_RIGHT=*q40_pp++;
472             q40_sc --;
473             master_outb(1,SAMPLE_CLEAR_REG);
474         }else Q40Interrupt();
475         spin_unlock(&dmasound.lock);
476         return IRQ_HANDLED;
477 }
478 static void Q40Interrupt(void)
479 {
480         if (!write_sq.active) {
481                   /* playing was interrupted and sq_reset() has already cleared
482                    * the sq variables, so better don't do anything here.
483                    */
484                    WAKE_UP(write_sq.sync_queue);
485                    master_outb(0,SAMPLE_ENABLE_REG); /* better safe */
486                    goto exit;
487         } else write_sq.active=0;
488         write_sq.count--;
489         Q40Play();
490 
491         if (q40_sc<2)
492               { /* there was nothing to play, disable irq */
493                 master_outb(0,SAMPLE_ENABLE_REG);
494                 *DAC_LEFT=*DAC_RIGHT=127;
495               }
496         WAKE_UP(write_sq.action_queue);
497 
498  exit:
499         master_outb(1,SAMPLE_CLEAR_REG);
500 }
501 
502 
503 static void Q40Init(void)
504 {
505         int i, idx;
506         const int freq[] = {10000, 20000};
507 
508         /* search a frequency that fits into the allowed error range */
509 
510         idx = -1;
511         for (i = 0; i < 2; i++)
512                 if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) <= catchRadius)
513                         idx = i;
514 
515         dmasound.hard = dmasound.soft;
516         /*sound.hard.stereo=1;*/ /* no longer true */
517         dmasound.hard.size=8;
518 
519         if (idx > -1) {
520                 dmasound.soft.speed = freq[idx];
521                 dmasound.trans_write = &transQ40Normal;
522         } else
523                 dmasound.trans_write = &transQ40Expanding;
524 
525         Q40Silence();
526 
527         if (dmasound.hard.speed > 20200) {
528                 /* squeeze the sound, we do that */
529                 dmasound.hard.speed = 20000;
530                 dmasound.trans_write = &transQ40Compressing;
531         } else if (dmasound.hard.speed > 10000) {
532                 dmasound.hard.speed = 20000;
533         } else {
534                 dmasound.hard.speed = 10000;
535         }
536         expand_bal = -dmasound.soft.speed;
537 }
538 
539 
540 static int Q40SetFormat(int format)
541 {
542         /* Q40 sound supports only 8bit modes */
543 
544         switch (format) {
545         case AFMT_QUERY:
546                 return(dmasound.soft.format);
547         case AFMT_MU_LAW:
548         case AFMT_A_LAW:
549         case AFMT_S8:
550         case AFMT_U8:
551                 break;
552         default:
553                 format = AFMT_S8;
554         }
555 
556         dmasound.soft.format = format;
557         dmasound.soft.size = 8;
558         if (dmasound.minDev == SND_DEV_DSP) {
559                 dmasound.dsp.format = format;
560                 dmasound.dsp.size = 8;
561         }
562         Q40Init();
563 
564         return(format);
565 }
566 
567 static int Q40SetVolume(int volume)
568 {
569     return 0;
570 }
571 
572 
573 /*** Machine definitions *****************************************************/
574 
575 static SETTINGS def_hard = {
576         .format = AFMT_U8,
577         .stereo = 0,
578         .size   = 8,
579         .speed  = 10000
580 } ;
581 
582 static SETTINGS def_soft = {
583         .format = AFMT_U8,
584         .stereo = 0,
585         .size   = 8,
586         .speed  = 8000
587 } ;
588 
589 static MACHINE machQ40 = {
590         .name           = "Q40",
591         .name2          = "Q40",
592         .owner          = THIS_MODULE,
593         .dma_alloc      = Q40Alloc,
594         .dma_free       = Q40Free,
595         .irqinit        = Q40IrqInit,
596 #ifdef MODULE
597         .irqcleanup     = Q40IrqCleanUp,
598 #endif /* MODULE */
599         .init           = Q40Init,
600         .silence        = Q40Silence,
601         .setFormat      = Q40SetFormat,
602         .setVolume      = Q40SetVolume,
603         .play           = Q40Play,
604         .min_dsp_speed  = 10000,
605         .version        = ((DMASOUND_Q40_REVISION<<8) | DMASOUND_Q40_EDITION),
606         .hardware_afmts = AFMT_U8, /* h'ware-supported formats *only* here */
607         .capabilities   = DSP_CAP_BATCH  /* As per SNDCTL_DSP_GETCAPS */
608 };
609 
610 
611 /*** Config & Setup **********************************************************/
612 
613 
614 int __init dmasound_q40_init(void)
615 {
616         if (MACH_IS_Q40) {
617             dmasound.mach = machQ40;
618             dmasound.mach.default_hard = def_hard ;
619             dmasound.mach.default_soft = def_soft ;
620             return dmasound_init();
621         } else
622             return -ENODEV;
623 }
624 
625 static void __exit dmasound_q40_cleanup(void)
626 {
627         dmasound_deinit();
628 }
629 
630 module_init(dmasound_q40_init);
631 module_exit(dmasound_q40_cleanup);
632 
633 MODULE_DESCRIPTION("Q40/Q60 sound driver");
634 MODULE_LICENSE("GPL");
635 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp