linux/sound/oss/dmasound/dmasound_paula.c
<<
>>
Prefs
   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");
 740