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

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

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

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

kernel.org | git.kernel.org | LWN.net | Project Home | 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.

sflogo.php