Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/oss/dmasound/dmasound_q40.c
26424 views
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
641