Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
dmasound_q40.c
Go to the documentation of this file.
1 /*
2  * linux/sound/oss/dmasound/dmasound_q40.c
3  *
4  * Q40 DMA Sound Driver
5  *
6  * See linux/sound/oss/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, gfp_t 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);
52 static irqreturn_t Q40MonoInterrupt(int irq, void *dummy);
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 __user *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;
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 __user *userPtr, size_t userCount,
83  u_char frame[], ssize_t *frameUsed,
84  ssize_t frameLeft)
85 {
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 __user *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 __user *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 __user *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 __user *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 __user *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 __user *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 __user *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, gfp_t 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  if (request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0,
375  "DMA sound", Q40Interrupt))
376  return 0;
377 
378  return(1);
379 }
380 
381 
382 #ifdef MODULE
383 static void Q40IrqCleanUp(void)
384 {
386  free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
387 }
388 #endif /* MODULE */
389 
390 
391 static void Q40Silence(void)
392 {
394  *DAC_LEFT=*DAC_RIGHT=127;
395 }
396 
397 static char *q40_pp;
398 static unsigned int q40_sc;
399 
400 static void Q40PlayNextFrame(int index)
401 {
402  u_char *start;
403  u_long size;
404  u_char speed;
405  int error;
406 
407  /* used by Q40Play() if all doubts whether there really is something
408  * to be played are already wiped out.
409  */
410  start = write_sq.buffers[write_sq.front];
411  size = (write_sq.count == index ? write_sq.rear_size : write_sq.block_size);
412 
413  q40_pp=start;
414  q40_sc=size;
415 
416  write_sq.front = (write_sq.front+1) % write_sq.max_count;
417  write_sq.active++;
418 
419  speed=(dmasound.hard.speed==10000 ? 0 : 1);
420 
422  free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
423  if (dmasound.soft.stereo)
424  error = request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0,
425  "Q40 sound", Q40Interrupt);
426  else
427  error = request_irq(Q40_IRQ_SAMPLE, Q40MonoInterrupt, 0,
428  "Q40 sound", Q40Interrupt);
429  if (error && printk_ratelimit())
430  pr_err("Couldn't register sound interrupt\n");
431 
432  master_outb( speed, SAMPLE_RATE_REG);
435 }
436 
437 static void Q40Play(void)
438 {
439  unsigned long flags;
440 
441  if (write_sq.active || write_sq.count<=0 ) {
442  /* There's already a frame loaded */
443  return;
444  }
445 
446  /* nothing in the queue */
447  if (write_sq.count <= 1 && write_sq.rear_size < write_sq.block_size && !write_sq.syncing) {
448  /* hmmm, the only existing frame is not
449  * yet filled and we're not syncing?
450  */
451  return;
452  }
453  spin_lock_irqsave(&dmasound.lock, flags);
454  Q40PlayNextFrame(1);
455  spin_unlock_irqrestore(&dmasound.lock, flags);
456 }
457 
458 static irqreturn_t Q40StereoInterrupt(int irq, void *dummy)
459 {
460  spin_lock(&dmasound.lock);
461  if (q40_sc>1){
462  *DAC_LEFT=*q40_pp++;
463  *DAC_RIGHT=*q40_pp++;
464  q40_sc -=2;
466  }else Q40Interrupt();
467  spin_unlock(&dmasound.lock);
468  return IRQ_HANDLED;
469 }
470 static irqreturn_t Q40MonoInterrupt(int irq, void *dummy)
471 {
472  spin_lock(&dmasound.lock);
473  if (q40_sc>0){
474  *DAC_LEFT=*q40_pp;
475  *DAC_RIGHT=*q40_pp++;
476  q40_sc --;
478  }else Q40Interrupt();
479  spin_unlock(&dmasound.lock);
480  return IRQ_HANDLED;
481 }
482 static void Q40Interrupt(void)
483 {
484  if (!write_sq.active) {
485  /* playing was interrupted and sq_reset() has already cleared
486  * the sq variables, so better don't do anything here.
487  */
488  WAKE_UP(write_sq.sync_queue);
489  master_outb(0,SAMPLE_ENABLE_REG); /* better safe */
490  goto exit;
491  } else write_sq.active=0;
492  write_sq.count--;
493  Q40Play();
494 
495  if (q40_sc<2)
496  { /* there was nothing to play, disable irq */
498  *DAC_LEFT=*DAC_RIGHT=127;
499  }
500  WAKE_UP(write_sq.action_queue);
501 
502  exit:
504 }
505 
506 
507 static void Q40Init(void)
508 {
509  int i, idx;
510  const int freq[] = {10000, 20000};
511 
512  /* search a frequency that fits into the allowed error range */
513 
514  idx = -1;
515  for (i = 0; i < 2; i++)
516  if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) <= catchRadius)
517  idx = i;
518 
519  dmasound.hard = dmasound.soft;
520  /*sound.hard.stereo=1;*/ /* no longer true */
521  dmasound.hard.size=8;
522 
523  if (idx > -1) {
524  dmasound.soft.speed = freq[idx];
525  dmasound.trans_write = &transQ40Normal;
526  } else
527  dmasound.trans_write = &transQ40Expanding;
528 
529  Q40Silence();
530 
531  if (dmasound.hard.speed > 20200) {
532  /* squeeze the sound, we do that */
533  dmasound.hard.speed = 20000;
534  dmasound.trans_write = &transQ40Compressing;
535  } else if (dmasound.hard.speed > 10000) {
536  dmasound.hard.speed = 20000;
537  } else {
538  dmasound.hard.speed = 10000;
539  }
540  expand_bal = -dmasound.soft.speed;
541 }
542 
543 
544 static int Q40SetFormat(int format)
545 {
546  /* Q40 sound supports only 8bit modes */
547 
548  switch (format) {
549  case AFMT_QUERY:
550  return(dmasound.soft.format);
551  case AFMT_MU_LAW:
552  case AFMT_A_LAW:
553  case AFMT_S8:
554  case AFMT_U8:
555  break;
556  default:
557  format = AFMT_S8;
558  }
559 
560  dmasound.soft.format = format;
561  dmasound.soft.size = 8;
562  if (dmasound.minDev == SND_DEV_DSP) {
563  dmasound.dsp.format = format;
564  dmasound.dsp.size = 8;
565  }
566  Q40Init();
567 
568  return(format);
569 }
570 
571 static int Q40SetVolume(int volume)
572 {
573  return 0;
574 }
575 
576 
577 /*** Machine definitions *****************************************************/
578 
579 static SETTINGS def_hard = {
580  .format = AFMT_U8,
581  .stereo = 0,
582  .size = 8,
583  .speed = 10000
584 } ;
585 
586 static SETTINGS def_soft = {
587  .format = AFMT_U8,
588  .stereo = 0,
589  .size = 8,
590  .speed = 8000
591 } ;
592 
593 static MACHINE machQ40 = {
594  .name = "Q40",
595  .name2 = "Q40",
596  .owner = THIS_MODULE,
597  .dma_alloc = Q40Alloc,
598  .dma_free = Q40Free,
599  .irqinit = Q40IrqInit,
600 #ifdef MODULE
601  .irqcleanup = Q40IrqCleanUp,
602 #endif /* MODULE */
603  .init = Q40Init,
604  .silence = Q40Silence,
605  .setFormat = Q40SetFormat,
606  .setVolume = Q40SetVolume,
607  .play = Q40Play,
608  .min_dsp_speed = 10000,
609  .version = ((DMASOUND_Q40_REVISION<<8) | DMASOUND_Q40_EDITION),
610  .hardware_afmts = AFMT_U8, /* h'ware-supported formats *only* here */
611  .capabilities = DSP_CAP_BATCH /* As per SNDCTL_DSP_GETCAPS */
612 };
613 
614 
615 /*** Config & Setup **********************************************************/
616 
617 
618 static int __init dmasound_q40_init(void)
619 {
620  if (MACH_IS_Q40) {
621  dmasound.mach = machQ40;
622  dmasound.mach.default_hard = def_hard ;
623  dmasound.mach.default_soft = def_soft ;
624  return dmasound_init();
625  } else
626  return -ENODEV;
627 }
628 
629 static void __exit dmasound_q40_cleanup(void)
630 {
631  dmasound_deinit();
632 }
633 
634 module_init(dmasound_q40_init);
635 module_exit(dmasound_q40_cleanup);
636 
637 MODULE_DESCRIPTION("Q40/Q60 sound driver");
638 MODULE_LICENSE("GPL");