dmasound_paula.c (19130B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * linux/sound/oss/dmasound/dmasound_paula.c 4 * 5 * Amiga `Paula' 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 constraint on state buffer usage. 15 * [0.4] - put in default hard/soft settings 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 * The minimum period for audio depends on htotal (for OCS/ECS/AGA) 41 * (Imported from arch/m68k/amiga/amisound.c) 42 */ 43 44extern volatile u_short amiga_audio_min_period; 45 46 47 /* 48 * amiga_mksound() should be able to restore the period after beeping 49 * (Imported from arch/m68k/amiga/amisound.c) 50 */ 51 52extern u_short amiga_audio_period; 53 54 55 /* 56 * Audio DMA masks 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 * Helper pointers for 16(14)-bit sound 66 */ 67 68static int write_sq_block_size_half, write_sq_block_size_quarter; 69 70 71/*** Low level stuff *********************************************************/ 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 * Heartbeat interferes with sound since the 7 kHz low-pass filter and the 93 * power LED are controlled by the same line. 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 /* !CONFIG_HEARTBEAT */ 113#define disable_heartbeat() do { } while (0) 114#define enable_heartbeat() do { } while (0) 115#endif /* !CONFIG_HEARTBEAT */ 116 117 118/*** Mid level stuff *********************************************************/ 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/*** Translations ************************************************************/ 127 128/* ++TeSche: radically changed for new expanding purposes... 129 * 130 * These two routines now deal with copying/expanding/translating the samples 131 * from user space into our buffer at the right frequency. They take care about 132 * how much data there's actually to read, how much buffer space there is and 133 * to convert samples into the right frequency/encoding. They will only work on 134 * complete samples so it may happen they leave some bytes in the input stream 135 * if the user didn't write a multiple of the current sample size. They both 136 * return the number of bytes they've used from both streams so you may detect 137 * such a situation. Luckily all programs should be able to cope with that. 138 * 139 * I think I've optimized anything as far as one can do in plain C, all 140 * variables should fit in registers and the loops are really short. There's 141 * one loop for every possible situation. Writing a more generalized and thus 142 * parameterized loop would only produce slower code. Feel free to optimize 143 * this in assembler if you like. :) 144 * 145 * I think these routines belong here because they're not yet really hardware 146 * independent, especially the fact that the Falcon can play 16bit samples 147 * only in stereo is hardcoded in both of them! 148 * 149 * ++geert: split in even more functions (one per format) 150 */ 151 152 153 /* 154 * Native format 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 * Copy and convert 8 bit data 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 * Copy and convert 16 bit data 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/*** Low level stuff *********************************************************/ 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 /* turn off DMA for audio channels */ 332 StopDMA(); 333 334 /* Register interrupt handler. */ 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 /* turn off DMA for audio channels */ 345 StopDMA(); 346 /* release the interrupt */ 347 free_irq(IRQ_AMIGA_AUD0, AmiInterrupt); 348} 349#endif /* MODULE */ 350 351static void AmiSilence(void) 352{ 353 /* turn off DMA for audio channels */ 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 /* we would need to squeeze the sound, but we won't do that */ 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 /* Amiga sound DMA supports 8bit and 16bit (pseudo 14 bit) modes */ 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 /* used by AmiPlay() if all doubts whether there really is something 468 * to be played are already wiped out. 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 /* We can play pseudo 14-bit only with the maximum volume */ 500 ch3 = ch0+write_sq_block_size_quarter; 501 ch2 = ch1+write_sq_block_size_quarter; 502 custom.aud[2].audvol = 1; /* we are being affected by the beeps */ 503 custom.aud[3].audvol = 1; /* restoring volume here helps a bit */ 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 /* There's already a frame loaded */ 528 custom.intena = IF_SETCLR | IF_AUD0; 529 return; 530 } 531 532 if (write_sq.active & AMI_PLAY_PLAYING) 533 /* Increase threshold: frame 1 is already being played */ 534 minframes = 2; 535 536 if (write_sq.count < minframes) { 537 /* Nothing to do */ 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 /* hmmm, the only existing frame is not 545 * yet filled and we're not syncing? 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 /* Playing was interrupted and sq_reset() has already cleared 565 * the sq variables, so better don't do anything here. 566 */ 567 WAKE_UP(write_sq.sync_queue); 568 return IRQ_HANDLED; 569 } 570 571 if (write_sq.active & AMI_PLAY_PLAYING) { 572 /* We've just finished a frame */ 573 write_sq.count--; 574 WAKE_UP(write_sq.action_queue); 575 } 576 577 if (write_sq.active & AMI_PLAY_LOADED) 578 /* Increase threshold: frame 1 is already being played */ 579 minframes = 2; 580 581 /* Shift the flags */ 582 write_sq.active = (write_sq.active<<1) & AMI_PLAY_MASK; 583 584 if (!write_sq.active) 585 /* No frame is playing, disable audio DMA */ 586 StopDMA(); 587 588 custom.intena = IF_SETCLR | IF_AUD0; 589 590 if (write_sq.count >= minframes) 591 /* Try to play the next frame */ 592 AmiPlay(); 593 594 if (!write_sq.active) 595 /* Nothing to play anymore. 596 Wake up a process waiting for audio output to drain. */ 597 WAKE_UP(write_sq.sync_queue); 598 return IRQ_HANDLED; 599} 600 601/*** Mid level stuff *********************************************************/ 602 603 604/* 605 * /dev/mixer abstraction 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; /* For pseudo 14bit */ 614 custom.aud[1].audvol = dmasound.volume_right; 615 custom.aud[2].audvol = 1; /* For pseudo 14bit */ 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/*** Machine definitions *****************************************************/ 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 /* MODULE */ 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), /* h'ware-supported formats *only* here */ 708 .capabilities = DSP_CAP_BATCH /* As per SNDCTL_DSP_GETCAPS */ 709}; 710 711 712/*** Config & Setup **********************************************************/ 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");