1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18#include <linux/module.h>
19#include <linux/mm.h>
20#include <linux/init.h>
21#include <linux/ioport.h>
22#include <linux/soundcard.h>
23#include <linux/interrupt.h>
24
25#include <asm/uaccess.h>
26#include <asm/setup.h>
27#include <asm/amigahw.h>
28#include <asm/amigaints.h>
29#include <asm/machdep.h>
30
31#include "dmasound.h"
32
33#define DMASOUND_PAULA_REVISION 0
34#define DMASOUND_PAULA_EDITION 4
35
36#define custom amiga_custom
37
38
39
40
41
42extern volatile u_short amiga_audio_min_period;
43
44
45
46
47
48
49
50extern u_short amiga_audio_period;
51
52
53
54
55
56
57#define AMI_AUDIO_OFF (DMAF_AUD0 | DMAF_AUD1 | DMAF_AUD2 | DMAF_AUD3)
58#define AMI_AUDIO_8 (DMAF_SETCLR | DMAF_MASTER | DMAF_AUD0 | DMAF_AUD1)
59#define AMI_AUDIO_14 (AMI_AUDIO_8 | DMAF_AUD2 | DMAF_AUD3)
60
61
62
63
64
65
66static int write_sq_block_size_half, write_sq_block_size_quarter;
67
68
69
70
71
72static void *AmiAlloc(unsigned int size, gfp_t flags);
73static void AmiFree(void *obj, unsigned int size);
74static int AmiIrqInit(void);
75#ifdef MODULE
76static void AmiIrqCleanUp(void);
77#endif
78static void AmiSilence(void);
79static void AmiInit(void);
80static int AmiSetFormat(int format);
81static int AmiSetVolume(int volume);
82static int AmiSetTreble(int treble);
83static void AmiPlayNextFrame(int index);
84static void AmiPlay(void);
85static irqreturn_t AmiInterrupt(int irq, void *dummy);
86
87#ifdef CONFIG_HEARTBEAT
88
89
90
91
92
93
94static void (*saved_heartbeat)(int) = NULL;
95
96static inline void disable_heartbeat(void)
97{
98 if (mach_heartbeat) {
99 saved_heartbeat = mach_heartbeat;
100 mach_heartbeat = NULL;
101 }
102 AmiSetTreble(dmasound.treble);
103}
104
105static inline void enable_heartbeat(void)
106{
107 if (saved_heartbeat)
108 mach_heartbeat = saved_heartbeat;
109}
110#else
111#define disable_heartbeat() do { } while (0)
112#define enable_heartbeat() do { } while (0)
113#endif
114
115
116
117
118static void AmiMixerInit(void);
119static int AmiMixerIoctl(u_int cmd, u_long arg);
120static int AmiWriteSqSetup(void);
121static int AmiStateInfo(char *buffer, size_t space);
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155static ssize_t ami_ct_s8(const u_char __user *userPtr, size_t userCount,
156 u_char frame[], ssize_t *frameUsed, ssize_t frameLeft)
157{
158 ssize_t count, used;
159
160 if (!dmasound.soft.stereo) {
161 void *p = &frame[*frameUsed];
162 count = min_t(unsigned long, userCount, frameLeft) & ~1;
163 used = count;
164 if (copy_from_user(p, userPtr, count))
165 return -EFAULT;
166 } else {
167 u_char *left = &frame[*frameUsed>>1];
168 u_char *right = left+write_sq_block_size_half;
169 count = min_t(unsigned long, userCount, frameLeft)>>1 & ~1;
170 used = count*2;
171 while (count > 0) {
172 if (get_user(*left++, userPtr++)
173 || get_user(*right++, userPtr++))
174 return -EFAULT;
175 count--;
176 }
177 }
178 *frameUsed += used;
179 return used;
180}
181
182
183
184
185
186
187#define GENERATE_AMI_CT8(funcname, convsample) \
188static ssize_t funcname(const u_char __user *userPtr, size_t userCount, \
189 u_char frame[], ssize_t *frameUsed, \
190 ssize_t frameLeft) \
191{ \
192 ssize_t count, used; \
193 \
194 if (!dmasound.soft.stereo) { \
195 u_char *p = &frame[*frameUsed]; \
196 count = min_t(size_t, userCount, frameLeft) & ~1; \
197 used = count; \
198 while (count > 0) { \
199 u_char data; \
200 if (get_user(data, userPtr++)) \
201 return -EFAULT; \
202 *p++ = convsample(data); \
203 count--; \
204 } \
205 } else { \
206 u_char *left = &frame[*frameUsed>>1]; \
207 u_char *right = left+write_sq_block_size_half; \
208 count = min_t(size_t, userCount, frameLeft)>>1 & ~1; \
209 used = count*2; \
210 while (count > 0) { \
211 u_char data; \
212 if (get_user(data, userPtr++)) \
213 return -EFAULT; \
214 *left++ = convsample(data); \
215 if (get_user(data, userPtr++)) \
216 return -EFAULT; \
217 *right++ = convsample(data); \
218 count--; \
219 } \
220 } \
221 *frameUsed += used; \
222 return used; \
223}
224
225#define AMI_CT_ULAW(x) (dmasound_ulaw2dma8[(x)])
226#define AMI_CT_ALAW(x) (dmasound_alaw2dma8[(x)])
227#define AMI_CT_U8(x) ((x) ^ 0x80)
228
229GENERATE_AMI_CT8(ami_ct_ulaw, AMI_CT_ULAW)
230GENERATE_AMI_CT8(ami_ct_alaw, AMI_CT_ALAW)
231GENERATE_AMI_CT8(ami_ct_u8, AMI_CT_U8)
232
233
234
235
236
237
238#define GENERATE_AMI_CT_16(funcname, convsample) \
239static ssize_t funcname(const u_char __user *userPtr, size_t userCount, \
240 u_char frame[], ssize_t *frameUsed, \
241 ssize_t frameLeft) \
242{ \
243 const u_short __user *ptr = (const u_short __user *)userPtr; \
244 ssize_t count, used; \
245 u_short data; \
246 \
247 if (!dmasound.soft.stereo) { \
248 u_char *high = &frame[*frameUsed>>1]; \
249 u_char *low = high+write_sq_block_size_half; \
250 count = min_t(size_t, userCount, frameLeft)>>1 & ~1; \
251 used = count*2; \
252 while (count > 0) { \
253 if (get_user(data, ptr++)) \
254 return -EFAULT; \
255 data = convsample(data); \
256 *high++ = data>>8; \
257 *low++ = (data>>2) & 0x3f; \
258 count--; \
259 } \
260 } else { \
261 u_char *lefth = &frame[*frameUsed>>2]; \
262 u_char *leftl = lefth+write_sq_block_size_quarter; \
263 u_char *righth = lefth+write_sq_block_size_half; \
264 u_char *rightl = righth+write_sq_block_size_quarter; \
265 count = min_t(size_t, userCount, frameLeft)>>2 & ~1; \
266 used = count*4; \
267 while (count > 0) { \
268 if (get_user(data, ptr++)) \
269 return -EFAULT; \
270 data = convsample(data); \
271 *lefth++ = data>>8; \
272 *leftl++ = (data>>2) & 0x3f; \
273 if (get_user(data, ptr++)) \
274 return -EFAULT; \
275 data = convsample(data); \
276 *righth++ = data>>8; \
277 *rightl++ = (data>>2) & 0x3f; \
278 count--; \
279 } \
280 } \
281 *frameUsed += used; \
282 return used; \
283}
284
285#define AMI_CT_S16BE(x) (x)
286#define AMI_CT_U16BE(x) ((x) ^ 0x8000)
287#define AMI_CT_S16LE(x) (le2be16((x)))
288#define AMI_CT_U16LE(x) (le2be16((x)) ^ 0x8000)
289
290GENERATE_AMI_CT_16(ami_ct_s16be, AMI_CT_S16BE)
291GENERATE_AMI_CT_16(ami_ct_u16be, AMI_CT_U16BE)
292GENERATE_AMI_CT_16(ami_ct_s16le, AMI_CT_S16LE)
293GENERATE_AMI_CT_16(ami_ct_u16le, AMI_CT_U16LE)
294
295
296static TRANS transAmiga = {
297 .ct_ulaw = ami_ct_ulaw,
298 .ct_alaw = ami_ct_alaw,
299 .ct_s8 = ami_ct_s8,
300 .ct_u8 = ami_ct_u8,
301 .ct_s16be = ami_ct_s16be,
302 .ct_u16be = ami_ct_u16be,
303 .ct_s16le = ami_ct_s16le,
304 .ct_u16le = ami_ct_u16le,
305};
306
307
308
309static inline void StopDMA(void)
310{
311 custom.aud[0].audvol = custom.aud[1].audvol = 0;
312 custom.aud[2].audvol = custom.aud[3].audvol = 0;
313 custom.dmacon = AMI_AUDIO_OFF;
314 enable_heartbeat();
315}
316
317static void *AmiAlloc(unsigned int size, gfp_t flags)
318{
319 return amiga_chip_alloc((long)size, "dmasound [Paula]");
320}
321
322static void AmiFree(void *obj, unsigned int size)
323{
324 amiga_chip_free (obj);
325}
326
327static int __init AmiIrqInit(void)
328{
329
330 StopDMA();
331
332
333 if (request_irq(IRQ_AMIGA_AUD0, AmiInterrupt, 0, "DMA sound",
334 AmiInterrupt))
335 return 0;
336 return 1;
337}
338
339#ifdef MODULE
340static void AmiIrqCleanUp(void)
341{
342
343 StopDMA();
344
345 free_irq(IRQ_AMIGA_AUD0, AmiInterrupt);
346}
347#endif
348
349static void AmiSilence(void)
350{
351
352 StopDMA();
353}
354
355
356static void AmiInit(void)
357{
358 int period, i;
359
360 AmiSilence();
361
362 if (dmasound.soft.speed)
363 period = amiga_colorclock/dmasound.soft.speed-1;
364 else
365 period = amiga_audio_min_period;
366 dmasound.hard = dmasound.soft;
367 dmasound.trans_write = &transAmiga;
368
369 if (period < amiga_audio_min_period) {
370
371 period = amiga_audio_min_period;
372 } else if (period > 65535) {
373 period = 65535;
374 }
375 dmasound.hard.speed = amiga_colorclock/(period+1);
376
377 for (i = 0; i < 4; i++)
378 custom.aud[i].audper = period;
379 amiga_audio_period = period;
380}
381
382
383static int AmiSetFormat(int format)
384{
385 int size;
386
387
388
389 switch (format) {
390 case AFMT_QUERY:
391 return dmasound.soft.format;
392 case AFMT_MU_LAW:
393 case AFMT_A_LAW:
394 case AFMT_U8:
395 case AFMT_S8:
396 size = 8;
397 break;
398 case AFMT_S16_BE:
399 case AFMT_U16_BE:
400 case AFMT_S16_LE:
401 case AFMT_U16_LE:
402 size = 16;
403 break;
404 default:
405 size = 8;
406 format = AFMT_S8;
407 }
408
409 dmasound.soft.format = format;
410 dmasound.soft.size = size;
411 if (dmasound.minDev == SND_DEV_DSP) {
412 dmasound.dsp.format = format;
413 dmasound.dsp.size = dmasound.soft.size;
414 }
415 AmiInit();
416
417 return format;
418}
419
420
421#define VOLUME_VOXWARE_TO_AMI(v) \
422 (((v) < 0) ? 0 : ((v) > 100) ? 64 : ((v) * 64)/100)
423#define VOLUME_AMI_TO_VOXWARE(v) ((v)*100/64)
424
425static int AmiSetVolume(int volume)
426{
427 dmasound.volume_left = VOLUME_VOXWARE_TO_AMI(volume & 0xff);
428 custom.aud[0].audvol = dmasound.volume_left;
429 dmasound.volume_right = VOLUME_VOXWARE_TO_AMI((volume & 0xff00) >> 8);
430 custom.aud[1].audvol = dmasound.volume_right;
431 if (dmasound.hard.size == 16) {
432 if (dmasound.volume_left == 64 && dmasound.volume_right == 64) {
433 custom.aud[2].audvol = 1;
434 custom.aud[3].audvol = 1;
435 } else {
436 custom.aud[2].audvol = 0;
437 custom.aud[3].audvol = 0;
438 }
439 }
440 return VOLUME_AMI_TO_VOXWARE(dmasound.volume_left) |
441 (VOLUME_AMI_TO_VOXWARE(dmasound.volume_right) << 8);
442}
443
444static int AmiSetTreble(int treble)
445{
446 dmasound.treble = treble;
447 if (treble < 50)
448 ciaa.pra &= ~0x02;
449 else
450 ciaa.pra |= 0x02;
451 return treble;
452}
453
454
455#define AMI_PLAY_LOADED 1
456#define AMI_PLAY_PLAYING 2
457#define AMI_PLAY_MASK 3
458
459
460static void AmiPlayNextFrame(int index)
461{
462 u_char *start, *ch0, *ch1, *ch2, *ch3;
463 u_long size;
464
465
466
467
468 start = write_sq.buffers[write_sq.front];
469 size = (write_sq.count == index ? write_sq.rear_size
470 : write_sq.block_size)>>1;
471
472 if (dmasound.hard.stereo) {
473 ch0 = start;
474 ch1 = start+write_sq_block_size_half;
475 size >>= 1;
476 } else {
477 ch0 = start;
478 ch1 = start;
479 }
480
481 disable_heartbeat();
482 custom.aud[0].audvol = dmasound.volume_left;
483 custom.aud[1].audvol = dmasound.volume_right;
484 if (dmasound.hard.size == 8) {
485 custom.aud[0].audlc = (u_short *)ZTWO_PADDR(ch0);
486 custom.aud[0].audlen = size;
487 custom.aud[1].audlc = (u_short *)ZTWO_PADDR(ch1);
488 custom.aud[1].audlen = size;
489 custom.dmacon = AMI_AUDIO_8;
490 } else {
491 size >>= 1;
492 custom.aud[0].audlc = (u_short *)ZTWO_PADDR(ch0);
493 custom.aud[0].audlen = size;
494 custom.aud[1].audlc = (u_short *)ZTWO_PADDR(ch1);
495 custom.aud[1].audlen = size;
496 if (dmasound.volume_left == 64 && dmasound.volume_right == 64) {
497
498 ch3 = ch0+write_sq_block_size_quarter;
499 ch2 = ch1+write_sq_block_size_quarter;
500 custom.aud[2].audvol = 1;
501 custom.aud[3].audvol = 1;
502 custom.aud[2].audlc = (u_short *)ZTWO_PADDR(ch2);
503 custom.aud[2].audlen = size;
504 custom.aud[3].audlc = (u_short *)ZTWO_PADDR(ch3);
505 custom.aud[3].audlen = size;
506 custom.dmacon = AMI_AUDIO_14;
507 } else {
508 custom.aud[2].audvol = 0;
509 custom.aud[3].audvol = 0;
510 custom.dmacon = AMI_AUDIO_8;
511 }
512 }
513 write_sq.front = (write_sq.front+1) % write_sq.max_count;
514 write_sq.active |= AMI_PLAY_LOADED;
515}
516
517
518static void AmiPlay(void)
519{
520 int minframes = 1;
521
522 custom.intena = IF_AUD0;
523
524 if (write_sq.active & AMI_PLAY_LOADED) {
525
526 custom.intena = IF_SETCLR | IF_AUD0;
527 return;
528 }
529
530 if (write_sq.active & AMI_PLAY_PLAYING)
531
532 minframes = 2;
533
534 if (write_sq.count < minframes) {
535
536 custom.intena = IF_SETCLR | IF_AUD0;
537 return;
538 }
539
540 if (write_sq.count <= minframes &&
541 write_sq.rear_size < write_sq.block_size && !write_sq.syncing) {
542
543
544
545 custom.intena = IF_SETCLR | IF_AUD0;
546 return;
547 }
548
549 AmiPlayNextFrame(minframes);
550
551 custom.intena = IF_SETCLR | IF_AUD0;
552}
553
554
555static irqreturn_t AmiInterrupt(int irq, void *dummy)
556{
557 int minframes = 1;
558
559 custom.intena = IF_AUD0;
560
561 if (!write_sq.active) {
562
563
564
565 WAKE_UP(write_sq.sync_queue);
566 return IRQ_HANDLED;
567 }
568
569 if (write_sq.active & AMI_PLAY_PLAYING) {
570
571 write_sq.count--;
572 WAKE_UP(write_sq.action_queue);
573 }
574
575 if (write_sq.active & AMI_PLAY_LOADED)
576
577 minframes = 2;
578
579
580 write_sq.active = (write_sq.active<<1) & AMI_PLAY_MASK;
581
582 if (!write_sq.active)
583
584 StopDMA();
585
586 custom.intena = IF_SETCLR | IF_AUD0;
587
588 if (write_sq.count >= minframes)
589
590 AmiPlay();
591
592 if (!write_sq.active)
593
594
595 WAKE_UP(write_sq.sync_queue);
596 return IRQ_HANDLED;
597}
598
599
600
601
602
603
604
605
606static void __init AmiMixerInit(void)
607{
608 dmasound.volume_left = 64;
609 dmasound.volume_right = 64;
610 custom.aud[0].audvol = dmasound.volume_left;
611 custom.aud[3].audvol = 1;
612 custom.aud[1].audvol = dmasound.volume_right;
613 custom.aud[2].audvol = 1;
614 dmasound.treble = 50;
615}
616
617static int AmiMixerIoctl(u_int cmd, u_long arg)
618{
619 int data;
620 switch (cmd) {
621 case SOUND_MIXER_READ_DEVMASK:
622 return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_TREBLE);
623 case SOUND_MIXER_READ_RECMASK:
624 return IOCTL_OUT(arg, 0);
625 case SOUND_MIXER_READ_STEREODEVS:
626 return IOCTL_OUT(arg, SOUND_MASK_VOLUME);
627 case SOUND_MIXER_READ_VOLUME:
628 return IOCTL_OUT(arg,
629 VOLUME_AMI_TO_VOXWARE(dmasound.volume_left) |
630 VOLUME_AMI_TO_VOXWARE(dmasound.volume_right) << 8);
631 case SOUND_MIXER_WRITE_VOLUME:
632 IOCTL_IN(arg, data);
633 return IOCTL_OUT(arg, dmasound_set_volume(data));
634 case SOUND_MIXER_READ_TREBLE:
635 return IOCTL_OUT(arg, dmasound.treble);
636 case SOUND_MIXER_WRITE_TREBLE:
637 IOCTL_IN(arg, data);
638 return IOCTL_OUT(arg, dmasound_set_treble(data));
639 }
640 return -EINVAL;
641}
642
643
644static int AmiWriteSqSetup(void)
645{
646 write_sq_block_size_half = write_sq.block_size>>1;
647 write_sq_block_size_quarter = write_sq_block_size_half>>1;
648 return 0;
649}
650
651
652static int AmiStateInfo(char *buffer, size_t space)
653{
654 int len = 0;
655 len += sprintf(buffer+len, "\tsound.volume_left = %d [0...64]\n",
656 dmasound.volume_left);
657 len += sprintf(buffer+len, "\tsound.volume_right = %d [0...64]\n",
658 dmasound.volume_right);
659 if (len >= space) {
660 printk(KERN_ERR "dmasound_paula: overlowed state buffer alloc.\n") ;
661 len = space ;
662 }
663 return len;
664}
665
666
667
668
669static SETTINGS def_hard = {
670 .format = AFMT_S8,
671 .stereo = 0,
672 .size = 8,
673 .speed = 8000
674} ;
675
676static SETTINGS def_soft = {
677 .format = AFMT_U8,
678 .stereo = 0,
679 .size = 8,
680 .speed = 8000
681} ;
682
683static MACHINE machAmiga = {
684 .name = "Amiga",
685 .name2 = "AMIGA",
686 .owner = THIS_MODULE,
687 .dma_alloc = AmiAlloc,
688 .dma_free = AmiFree,
689 .irqinit = AmiIrqInit,
690#ifdef MODULE
691 .irqcleanup = AmiIrqCleanUp,
692#endif
693 .init = AmiInit,
694 .silence = AmiSilence,
695 .setFormat = AmiSetFormat,
696 .setVolume = AmiSetVolume,
697 .setTreble = AmiSetTreble,
698 .play = AmiPlay,
699 .mixer_init = AmiMixerInit,
700 .mixer_ioctl = AmiMixerIoctl,
701 .write_sq_setup = AmiWriteSqSetup,
702 .state_info = AmiStateInfo,
703 .min_dsp_speed = 8000,
704 .version = ((DMASOUND_PAULA_REVISION<<8) | DMASOUND_PAULA_EDITION),
705 .hardware_afmts = (AFMT_S8 | AFMT_S16_BE),
706 .capabilities = DSP_CAP_BATCH
707};
708
709
710
711
712
713static int __init dmasound_paula_init(void)
714{
715 int err;
716
717 if (MACH_IS_AMIGA && AMIGAHW_PRESENT(AMI_AUDIO)) {
718 if (!request_mem_region(CUSTOM_PHYSADDR+0xa0, 0x40,
719 "dmasound [Paula]"))
720 return -EBUSY;
721 dmasound.mach = machAmiga;
722 dmasound.mach.default_hard = def_hard ;
723 dmasound.mach.default_soft = def_soft ;
724 err = dmasound_init();
725 if (err)
726 release_mem_region(CUSTOM_PHYSADDR+0xa0, 0x40);
727 return err;
728 } else
729 return -ENODEV;
730}
731
732static void __exit dmasound_paula_cleanup(void)
733{
734 dmasound_deinit();
735 release_mem_region(CUSTOM_PHYSADDR+0xa0, 0x40);
736}
737
738module_init(dmasound_paula_init);
739module_exit(dmasound_paula_cleanup);
740MODULE_LICENSE("GPL");
741