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