Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/sound/oss/dmasound/dmasound_q40.c
10818 views
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;
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 __user *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 __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
{
385
master_outb(0,SAMPLE_ENABLE_REG);
386
free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
387
}
388
#endif /* MODULE */
389
390
391
static void Q40Silence(void)
392
{
393
master_outb(0,SAMPLE_ENABLE_REG);
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
421
master_outb( 0,SAMPLE_ENABLE_REG);
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);
433
master_outb( 1,SAMPLE_CLEAR_REG);
434
master_outb( 1,SAMPLE_ENABLE_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;
465
master_outb(1,SAMPLE_CLEAR_REG);
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 --;
477
master_outb(1,SAMPLE_CLEAR_REG);
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 */
497
master_outb(0,SAMPLE_ENABLE_REG);
498
*DAC_LEFT=*DAC_RIGHT=127;
499
}
500
WAKE_UP(write_sq.action_queue);
501
502
exit:
503
master_outb(1,SAMPLE_CLEAR_REG);
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");
639
640