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