0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019 #include <linux/module.h>
0020 #include <linux/mm.h>
0021 #include <linux/init.h>
0022 #include <linux/ioport.h>
0023 #include <linux/soundcard.h>
0024 #include <linux/interrupt.h>
0025 #include <linux/platform_device.h>
0026
0027 #include <linux/uaccess.h>
0028 #include <asm/setup.h>
0029 #include <asm/amigahw.h>
0030 #include <asm/amigaints.h>
0031 #include <asm/machdep.h>
0032
0033 #include "dmasound.h"
0034
0035 #define DMASOUND_PAULA_REVISION 0
0036 #define DMASOUND_PAULA_EDITION 4
0037
0038 #define custom amiga_custom
0039
0040
0041
0042
0043
0044 extern volatile u_short amiga_audio_min_period;
0045
0046
0047
0048
0049
0050
0051
0052 extern u_short amiga_audio_period;
0053
0054
0055
0056
0057
0058
0059 #define AMI_AUDIO_OFF (DMAF_AUD0 | DMAF_AUD1 | DMAF_AUD2 | DMAF_AUD3)
0060 #define AMI_AUDIO_8 (DMAF_SETCLR | DMAF_MASTER | DMAF_AUD0 | DMAF_AUD1)
0061 #define AMI_AUDIO_14 (AMI_AUDIO_8 | DMAF_AUD2 | DMAF_AUD3)
0062
0063
0064
0065
0066
0067
0068 static int write_sq_block_size_half, write_sq_block_size_quarter;
0069
0070
0071
0072
0073
0074 static void *AmiAlloc(unsigned int size, gfp_t flags);
0075 static void AmiFree(void *obj, unsigned int size);
0076 static int AmiIrqInit(void);
0077 #ifdef MODULE
0078 static void AmiIrqCleanUp(void);
0079 #endif
0080 static void AmiSilence(void);
0081 static void AmiInit(void);
0082 static int AmiSetFormat(int format);
0083 static int AmiSetVolume(int volume);
0084 static int AmiSetTreble(int treble);
0085 static void AmiPlayNextFrame(int index);
0086 static void AmiPlay(void);
0087 static irqreturn_t AmiInterrupt(int irq, void *dummy);
0088
0089 #ifdef CONFIG_HEARTBEAT
0090
0091
0092
0093
0094
0095
0096 static void (*saved_heartbeat)(int) = NULL;
0097
0098 static inline void disable_heartbeat(void)
0099 {
0100 if (mach_heartbeat) {
0101 saved_heartbeat = mach_heartbeat;
0102 mach_heartbeat = NULL;
0103 }
0104 AmiSetTreble(dmasound.treble);
0105 }
0106
0107 static inline void enable_heartbeat(void)
0108 {
0109 if (saved_heartbeat)
0110 mach_heartbeat = saved_heartbeat;
0111 }
0112 #else
0113 #define disable_heartbeat() do { } while (0)
0114 #define enable_heartbeat() do { } while (0)
0115 #endif
0116
0117
0118
0119
0120 static void AmiMixerInit(void);
0121 static int AmiMixerIoctl(u_int cmd, u_long arg);
0122 static int AmiWriteSqSetup(void);
0123 static int AmiStateInfo(char *buffer, size_t space);
0124
0125
0126
0127
0128
0129
0130
0131
0132
0133
0134
0135
0136
0137
0138
0139
0140
0141
0142
0143
0144
0145
0146
0147
0148
0149
0150
0151
0152
0153
0154
0155
0156
0157 static ssize_t ami_ct_s8(const u_char __user *userPtr, size_t userCount,
0158 u_char frame[], ssize_t *frameUsed, ssize_t frameLeft)
0159 {
0160 ssize_t count, used;
0161
0162 if (!dmasound.soft.stereo) {
0163 void *p = &frame[*frameUsed];
0164 count = min_t(unsigned long, userCount, frameLeft) & ~1;
0165 used = count;
0166 if (copy_from_user(p, userPtr, count))
0167 return -EFAULT;
0168 } else {
0169 u_char *left = &frame[*frameUsed>>1];
0170 u_char *right = left+write_sq_block_size_half;
0171 count = min_t(unsigned long, userCount, frameLeft)>>1 & ~1;
0172 used = count*2;
0173 while (count > 0) {
0174 if (get_user(*left++, userPtr++)
0175 || get_user(*right++, userPtr++))
0176 return -EFAULT;
0177 count--;
0178 }
0179 }
0180 *frameUsed += used;
0181 return used;
0182 }
0183
0184
0185
0186
0187
0188
0189 #define GENERATE_AMI_CT8(funcname, convsample) \
0190 static ssize_t funcname(const u_char __user *userPtr, size_t userCount, \
0191 u_char frame[], ssize_t *frameUsed, \
0192 ssize_t frameLeft) \
0193 { \
0194 ssize_t count, used; \
0195 \
0196 if (!dmasound.soft.stereo) { \
0197 u_char *p = &frame[*frameUsed]; \
0198 count = min_t(size_t, userCount, frameLeft) & ~1; \
0199 used = count; \
0200 while (count > 0) { \
0201 u_char data; \
0202 if (get_user(data, userPtr++)) \
0203 return -EFAULT; \
0204 *p++ = convsample(data); \
0205 count--; \
0206 } \
0207 } else { \
0208 u_char *left = &frame[*frameUsed>>1]; \
0209 u_char *right = left+write_sq_block_size_half; \
0210 count = min_t(size_t, userCount, frameLeft)>>1 & ~1; \
0211 used = count*2; \
0212 while (count > 0) { \
0213 u_char data; \
0214 if (get_user(data, userPtr++)) \
0215 return -EFAULT; \
0216 *left++ = convsample(data); \
0217 if (get_user(data, userPtr++)) \
0218 return -EFAULT; \
0219 *right++ = convsample(data); \
0220 count--; \
0221 } \
0222 } \
0223 *frameUsed += used; \
0224 return used; \
0225 }
0226
0227 #define AMI_CT_ULAW(x) (dmasound_ulaw2dma8[(x)])
0228 #define AMI_CT_ALAW(x) (dmasound_alaw2dma8[(x)])
0229 #define AMI_CT_U8(x) ((x) ^ 0x80)
0230
0231 GENERATE_AMI_CT8(ami_ct_ulaw, AMI_CT_ULAW)
0232 GENERATE_AMI_CT8(ami_ct_alaw, AMI_CT_ALAW)
0233 GENERATE_AMI_CT8(ami_ct_u8, AMI_CT_U8)
0234
0235
0236
0237
0238
0239
0240 #define GENERATE_AMI_CT_16(funcname, convsample) \
0241 static ssize_t funcname(const u_char __user *userPtr, size_t userCount, \
0242 u_char frame[], ssize_t *frameUsed, \
0243 ssize_t frameLeft) \
0244 { \
0245 const u_short __user *ptr = (const u_short __user *)userPtr; \
0246 ssize_t count, used; \
0247 u_short data; \
0248 \
0249 if (!dmasound.soft.stereo) { \
0250 u_char *high = &frame[*frameUsed>>1]; \
0251 u_char *low = high+write_sq_block_size_half; \
0252 count = min_t(size_t, userCount, frameLeft)>>1 & ~1; \
0253 used = count*2; \
0254 while (count > 0) { \
0255 if (get_user(data, ptr++)) \
0256 return -EFAULT; \
0257 data = convsample(data); \
0258 *high++ = data>>8; \
0259 *low++ = (data>>2) & 0x3f; \
0260 count--; \
0261 } \
0262 } else { \
0263 u_char *lefth = &frame[*frameUsed>>2]; \
0264 u_char *leftl = lefth+write_sq_block_size_quarter; \
0265 u_char *righth = lefth+write_sq_block_size_half; \
0266 u_char *rightl = righth+write_sq_block_size_quarter; \
0267 count = min_t(size_t, userCount, frameLeft)>>2 & ~1; \
0268 used = count*4; \
0269 while (count > 0) { \
0270 if (get_user(data, ptr++)) \
0271 return -EFAULT; \
0272 data = convsample(data); \
0273 *lefth++ = data>>8; \
0274 *leftl++ = (data>>2) & 0x3f; \
0275 if (get_user(data, ptr++)) \
0276 return -EFAULT; \
0277 data = convsample(data); \
0278 *righth++ = data>>8; \
0279 *rightl++ = (data>>2) & 0x3f; \
0280 count--; \
0281 } \
0282 } \
0283 *frameUsed += used; \
0284 return used; \
0285 }
0286
0287 #define AMI_CT_S16BE(x) (x)
0288 #define AMI_CT_U16BE(x) ((x) ^ 0x8000)
0289 #define AMI_CT_S16LE(x) (le2be16((x)))
0290 #define AMI_CT_U16LE(x) (le2be16((x)) ^ 0x8000)
0291
0292 GENERATE_AMI_CT_16(ami_ct_s16be, AMI_CT_S16BE)
0293 GENERATE_AMI_CT_16(ami_ct_u16be, AMI_CT_U16BE)
0294 GENERATE_AMI_CT_16(ami_ct_s16le, AMI_CT_S16LE)
0295 GENERATE_AMI_CT_16(ami_ct_u16le, AMI_CT_U16LE)
0296
0297
0298 static TRANS transAmiga = {
0299 .ct_ulaw = ami_ct_ulaw,
0300 .ct_alaw = ami_ct_alaw,
0301 .ct_s8 = ami_ct_s8,
0302 .ct_u8 = ami_ct_u8,
0303 .ct_s16be = ami_ct_s16be,
0304 .ct_u16be = ami_ct_u16be,
0305 .ct_s16le = ami_ct_s16le,
0306 .ct_u16le = ami_ct_u16le,
0307 };
0308
0309
0310
0311 static inline void StopDMA(void)
0312 {
0313 custom.aud[0].audvol = custom.aud[1].audvol = 0;
0314 custom.aud[2].audvol = custom.aud[3].audvol = 0;
0315 custom.dmacon = AMI_AUDIO_OFF;
0316 enable_heartbeat();
0317 }
0318
0319 static void *AmiAlloc(unsigned int size, gfp_t flags)
0320 {
0321 return amiga_chip_alloc((long)size, "dmasound [Paula]");
0322 }
0323
0324 static void AmiFree(void *obj, unsigned int size)
0325 {
0326 amiga_chip_free (obj);
0327 }
0328
0329 static int __init AmiIrqInit(void)
0330 {
0331
0332 StopDMA();
0333
0334
0335 if (request_irq(IRQ_AMIGA_AUD0, AmiInterrupt, 0, "DMA sound",
0336 AmiInterrupt))
0337 return 0;
0338 return 1;
0339 }
0340
0341 #ifdef MODULE
0342 static void AmiIrqCleanUp(void)
0343 {
0344
0345 StopDMA();
0346
0347 free_irq(IRQ_AMIGA_AUD0, AmiInterrupt);
0348 }
0349 #endif
0350
0351 static void AmiSilence(void)
0352 {
0353
0354 StopDMA();
0355 }
0356
0357
0358 static void AmiInit(void)
0359 {
0360 int period, i;
0361
0362 AmiSilence();
0363
0364 if (dmasound.soft.speed)
0365 period = amiga_colorclock/dmasound.soft.speed-1;
0366 else
0367 period = amiga_audio_min_period;
0368 dmasound.hard = dmasound.soft;
0369 dmasound.trans_write = &transAmiga;
0370
0371 if (period < amiga_audio_min_period) {
0372
0373 period = amiga_audio_min_period;
0374 } else if (period > 65535) {
0375 period = 65535;
0376 }
0377 dmasound.hard.speed = amiga_colorclock/(period+1);
0378
0379 for (i = 0; i < 4; i++)
0380 custom.aud[i].audper = period;
0381 amiga_audio_period = period;
0382 }
0383
0384
0385 static int AmiSetFormat(int format)
0386 {
0387 int size;
0388
0389
0390
0391 switch (format) {
0392 case AFMT_QUERY:
0393 return dmasound.soft.format;
0394 case AFMT_MU_LAW:
0395 case AFMT_A_LAW:
0396 case AFMT_U8:
0397 case AFMT_S8:
0398 size = 8;
0399 break;
0400 case AFMT_S16_BE:
0401 case AFMT_U16_BE:
0402 case AFMT_S16_LE:
0403 case AFMT_U16_LE:
0404 size = 16;
0405 break;
0406 default:
0407 size = 8;
0408 format = AFMT_S8;
0409 }
0410
0411 dmasound.soft.format = format;
0412 dmasound.soft.size = size;
0413 if (dmasound.minDev == SND_DEV_DSP) {
0414 dmasound.dsp.format = format;
0415 dmasound.dsp.size = dmasound.soft.size;
0416 }
0417 AmiInit();
0418
0419 return format;
0420 }
0421
0422
0423 #define VOLUME_VOXWARE_TO_AMI(v) \
0424 (((v) < 0) ? 0 : ((v) > 100) ? 64 : ((v) * 64)/100)
0425 #define VOLUME_AMI_TO_VOXWARE(v) ((v)*100/64)
0426
0427 static int AmiSetVolume(int volume)
0428 {
0429 dmasound.volume_left = VOLUME_VOXWARE_TO_AMI(volume & 0xff);
0430 custom.aud[0].audvol = dmasound.volume_left;
0431 dmasound.volume_right = VOLUME_VOXWARE_TO_AMI((volume & 0xff00) >> 8);
0432 custom.aud[1].audvol = dmasound.volume_right;
0433 if (dmasound.hard.size == 16) {
0434 if (dmasound.volume_left == 64 && dmasound.volume_right == 64) {
0435 custom.aud[2].audvol = 1;
0436 custom.aud[3].audvol = 1;
0437 } else {
0438 custom.aud[2].audvol = 0;
0439 custom.aud[3].audvol = 0;
0440 }
0441 }
0442 return VOLUME_AMI_TO_VOXWARE(dmasound.volume_left) |
0443 (VOLUME_AMI_TO_VOXWARE(dmasound.volume_right) << 8);
0444 }
0445
0446 static int AmiSetTreble(int treble)
0447 {
0448 dmasound.treble = treble;
0449 if (treble < 50)
0450 ciaa.pra &= ~0x02;
0451 else
0452 ciaa.pra |= 0x02;
0453 return treble;
0454 }
0455
0456
0457 #define AMI_PLAY_LOADED 1
0458 #define AMI_PLAY_PLAYING 2
0459 #define AMI_PLAY_MASK 3
0460
0461
0462 static void AmiPlayNextFrame(int index)
0463 {
0464 u_char *start, *ch0, *ch1, *ch2, *ch3;
0465 u_long size;
0466
0467
0468
0469
0470 start = write_sq.buffers[write_sq.front];
0471 size = (write_sq.count == index ? write_sq.rear_size
0472 : write_sq.block_size)>>1;
0473
0474 if (dmasound.hard.stereo) {
0475 ch0 = start;
0476 ch1 = start+write_sq_block_size_half;
0477 size >>= 1;
0478 } else {
0479 ch0 = start;
0480 ch1 = start;
0481 }
0482
0483 disable_heartbeat();
0484 custom.aud[0].audvol = dmasound.volume_left;
0485 custom.aud[1].audvol = dmasound.volume_right;
0486 if (dmasound.hard.size == 8) {
0487 custom.aud[0].audlc = (u_short *)ZTWO_PADDR(ch0);
0488 custom.aud[0].audlen = size;
0489 custom.aud[1].audlc = (u_short *)ZTWO_PADDR(ch1);
0490 custom.aud[1].audlen = size;
0491 custom.dmacon = AMI_AUDIO_8;
0492 } else {
0493 size >>= 1;
0494 custom.aud[0].audlc = (u_short *)ZTWO_PADDR(ch0);
0495 custom.aud[0].audlen = size;
0496 custom.aud[1].audlc = (u_short *)ZTWO_PADDR(ch1);
0497 custom.aud[1].audlen = size;
0498 if (dmasound.volume_left == 64 && dmasound.volume_right == 64) {
0499
0500 ch3 = ch0+write_sq_block_size_quarter;
0501 ch2 = ch1+write_sq_block_size_quarter;
0502 custom.aud[2].audvol = 1;
0503 custom.aud[3].audvol = 1;
0504 custom.aud[2].audlc = (u_short *)ZTWO_PADDR(ch2);
0505 custom.aud[2].audlen = size;
0506 custom.aud[3].audlc = (u_short *)ZTWO_PADDR(ch3);
0507 custom.aud[3].audlen = size;
0508 custom.dmacon = AMI_AUDIO_14;
0509 } else {
0510 custom.aud[2].audvol = 0;
0511 custom.aud[3].audvol = 0;
0512 custom.dmacon = AMI_AUDIO_8;
0513 }
0514 }
0515 write_sq.front = (write_sq.front+1) % write_sq.max_count;
0516 write_sq.active |= AMI_PLAY_LOADED;
0517 }
0518
0519
0520 static void AmiPlay(void)
0521 {
0522 int minframes = 1;
0523
0524 custom.intena = IF_AUD0;
0525
0526 if (write_sq.active & AMI_PLAY_LOADED) {
0527
0528 custom.intena = IF_SETCLR | IF_AUD0;
0529 return;
0530 }
0531
0532 if (write_sq.active & AMI_PLAY_PLAYING)
0533
0534 minframes = 2;
0535
0536 if (write_sq.count < minframes) {
0537
0538 custom.intena = IF_SETCLR | IF_AUD0;
0539 return;
0540 }
0541
0542 if (write_sq.count <= minframes &&
0543 write_sq.rear_size < write_sq.block_size && !write_sq.syncing) {
0544
0545
0546
0547 custom.intena = IF_SETCLR | IF_AUD0;
0548 return;
0549 }
0550
0551 AmiPlayNextFrame(minframes);
0552
0553 custom.intena = IF_SETCLR | IF_AUD0;
0554 }
0555
0556
0557 static irqreturn_t AmiInterrupt(int irq, void *dummy)
0558 {
0559 int minframes = 1;
0560
0561 custom.intena = IF_AUD0;
0562
0563 if (!write_sq.active) {
0564
0565
0566
0567 WAKE_UP(write_sq.sync_queue);
0568 return IRQ_HANDLED;
0569 }
0570
0571 if (write_sq.active & AMI_PLAY_PLAYING) {
0572
0573 write_sq.count--;
0574 WAKE_UP(write_sq.action_queue);
0575 }
0576
0577 if (write_sq.active & AMI_PLAY_LOADED)
0578
0579 minframes = 2;
0580
0581
0582 write_sq.active = (write_sq.active<<1) & AMI_PLAY_MASK;
0583
0584 if (!write_sq.active)
0585
0586 StopDMA();
0587
0588 custom.intena = IF_SETCLR | IF_AUD0;
0589
0590 if (write_sq.count >= minframes)
0591
0592 AmiPlay();
0593
0594 if (!write_sq.active)
0595
0596
0597 WAKE_UP(write_sq.sync_queue);
0598 return IRQ_HANDLED;
0599 }
0600
0601
0602
0603
0604
0605
0606
0607
0608 static void __init AmiMixerInit(void)
0609 {
0610 dmasound.volume_left = 64;
0611 dmasound.volume_right = 64;
0612 custom.aud[0].audvol = dmasound.volume_left;
0613 custom.aud[3].audvol = 1;
0614 custom.aud[1].audvol = dmasound.volume_right;
0615 custom.aud[2].audvol = 1;
0616 dmasound.treble = 50;
0617 }
0618
0619 static int AmiMixerIoctl(u_int cmd, u_long arg)
0620 {
0621 int data;
0622 switch (cmd) {
0623 case SOUND_MIXER_READ_DEVMASK:
0624 return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_TREBLE);
0625 case SOUND_MIXER_READ_RECMASK:
0626 return IOCTL_OUT(arg, 0);
0627 case SOUND_MIXER_READ_STEREODEVS:
0628 return IOCTL_OUT(arg, SOUND_MASK_VOLUME);
0629 case SOUND_MIXER_READ_VOLUME:
0630 return IOCTL_OUT(arg,
0631 VOLUME_AMI_TO_VOXWARE(dmasound.volume_left) |
0632 VOLUME_AMI_TO_VOXWARE(dmasound.volume_right) << 8);
0633 case SOUND_MIXER_WRITE_VOLUME:
0634 IOCTL_IN(arg, data);
0635 return IOCTL_OUT(arg, dmasound_set_volume(data));
0636 case SOUND_MIXER_READ_TREBLE:
0637 return IOCTL_OUT(arg, dmasound.treble);
0638 case SOUND_MIXER_WRITE_TREBLE:
0639 IOCTL_IN(arg, data);
0640 return IOCTL_OUT(arg, dmasound_set_treble(data));
0641 }
0642 return -EINVAL;
0643 }
0644
0645
0646 static int AmiWriteSqSetup(void)
0647 {
0648 write_sq_block_size_half = write_sq.block_size>>1;
0649 write_sq_block_size_quarter = write_sq_block_size_half>>1;
0650 return 0;
0651 }
0652
0653
0654 static int AmiStateInfo(char *buffer, size_t space)
0655 {
0656 int len = 0;
0657 len += sprintf(buffer+len, "\tsound.volume_left = %d [0...64]\n",
0658 dmasound.volume_left);
0659 len += sprintf(buffer+len, "\tsound.volume_right = %d [0...64]\n",
0660 dmasound.volume_right);
0661 if (len >= space) {
0662 printk(KERN_ERR "dmasound_paula: overflowed state buffer alloc.\n") ;
0663 len = space ;
0664 }
0665 return len;
0666 }
0667
0668
0669
0670
0671 static SETTINGS def_hard = {
0672 .format = AFMT_S8,
0673 .stereo = 0,
0674 .size = 8,
0675 .speed = 8000
0676 } ;
0677
0678 static SETTINGS def_soft = {
0679 .format = AFMT_U8,
0680 .stereo = 0,
0681 .size = 8,
0682 .speed = 8000
0683 } ;
0684
0685 static MACHINE machAmiga = {
0686 .name = "Amiga",
0687 .name2 = "AMIGA",
0688 .owner = THIS_MODULE,
0689 .dma_alloc = AmiAlloc,
0690 .dma_free = AmiFree,
0691 .irqinit = AmiIrqInit,
0692 #ifdef MODULE
0693 .irqcleanup = AmiIrqCleanUp,
0694 #endif
0695 .init = AmiInit,
0696 .silence = AmiSilence,
0697 .setFormat = AmiSetFormat,
0698 .setVolume = AmiSetVolume,
0699 .setTreble = AmiSetTreble,
0700 .play = AmiPlay,
0701 .mixer_init = AmiMixerInit,
0702 .mixer_ioctl = AmiMixerIoctl,
0703 .write_sq_setup = AmiWriteSqSetup,
0704 .state_info = AmiStateInfo,
0705 .min_dsp_speed = 8000,
0706 .version = ((DMASOUND_PAULA_REVISION<<8) | DMASOUND_PAULA_EDITION),
0707 .hardware_afmts = (AFMT_S8 | AFMT_S16_BE),
0708 .capabilities = DSP_CAP_BATCH
0709 };
0710
0711
0712
0713
0714
0715 static int __init amiga_audio_probe(struct platform_device *pdev)
0716 {
0717 dmasound.mach = machAmiga;
0718 dmasound.mach.default_hard = def_hard ;
0719 dmasound.mach.default_soft = def_soft ;
0720 return dmasound_init();
0721 }
0722
0723 static int __exit amiga_audio_remove(struct platform_device *pdev)
0724 {
0725 dmasound_deinit();
0726 return 0;
0727 }
0728
0729 static struct platform_driver amiga_audio_driver = {
0730 .remove = __exit_p(amiga_audio_remove),
0731 .driver = {
0732 .name = "amiga-audio",
0733 },
0734 };
0735
0736 module_platform_driver_probe(amiga_audio_driver, amiga_audio_probe);
0737
0738 MODULE_LICENSE("GPL");
0739 MODULE_ALIAS("platform:amiga-audio");