linux/sound/isa/msnd/msnd.c
<<
>>
Prefs
   1/*********************************************************************
   2 *
   3 * 2002/06/30 Karsten Wiese:
   4 *      removed kernel-version dependencies.
   5 *      ripped from linux kernel 2.4.18 (OSS Implementation) by me.
   6 *      In the OSS Version, this file is compiled to a separate MODULE,
   7 *      that is used by the pinnacle and the classic driver.
   8 *      since there is no classic driver for alsa yet (i dont have a classic
   9 *      & writing one blindfold is difficult) this file's object is statically
  10 *      linked into the pinnacle-driver-module for now. look for the string
  11 *              "uncomment this to make this a module again"
  12 *      to do guess what.
  13 *
  14 * the following is a copy of the 2.4.18 OSS FREE file-heading comment:
  15 *
  16 * msnd.c - Driver Base
  17 *
  18 * Turtle Beach MultiSound Sound Card Driver for Linux
  19 *
  20 * Copyright (C) 1998 Andrew Veliath
  21 *
  22 * This program is free software; you can redistribute it and/or modify
  23 * it under the terms of the GNU General Public License as published by
  24 * the Free Software Foundation; either version 2 of the License, or
  25 * (at your option) any later version.
  26 *
  27 * This program is distributed in the hope that it will be useful,
  28 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  30 * GNU General Public License for more details.
  31 *
  32 * You should have received a copy of the GNU General Public License
  33 * along with this program; if not, write to the Free Software
  34 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  35 *
  36 ********************************************************************/
  37
  38#include <linux/kernel.h>
  39#include <linux/types.h>
  40#include <linux/interrupt.h>
  41#include <linux/io.h>
  42#include <linux/fs.h>
  43#include <linux/delay.h>
  44
  45#include <sound/core.h>
  46#include <sound/initval.h>
  47#include <sound/pcm.h>
  48#include <sound/pcm_params.h>
  49
  50#include "msnd.h"
  51
  52#define LOGNAME                 "msnd"
  53
  54
  55void snd_msnd_init_queue(void *base, int start, int size)
  56{
  57        writew(PCTODSP_BASED(start), base + JQS_wStart);
  58        writew(PCTODSP_OFFSET(size) - 1, base + JQS_wSize);
  59        writew(0, base + JQS_wHead);
  60        writew(0, base + JQS_wTail);
  61}
  62EXPORT_SYMBOL(snd_msnd_init_queue);
  63
  64static int snd_msnd_wait_TXDE(struct snd_msnd *dev)
  65{
  66        unsigned int io = dev->io;
  67        int timeout = 1000;
  68
  69        while (timeout-- > 0)
  70                if (inb(io + HP_ISR) & HPISR_TXDE)
  71                        return 0;
  72
  73        return -EIO;
  74}
  75
  76static int snd_msnd_wait_HC0(struct snd_msnd *dev)
  77{
  78        unsigned int io = dev->io;
  79        int timeout = 1000;
  80
  81        while (timeout-- > 0)
  82                if (!(inb(io + HP_CVR) & HPCVR_HC))
  83                        return 0;
  84
  85        return -EIO;
  86}
  87
  88int snd_msnd_send_dsp_cmd(struct snd_msnd *dev, u8 cmd)
  89{
  90        unsigned long flags;
  91
  92        spin_lock_irqsave(&dev->lock, flags);
  93        if (snd_msnd_wait_HC0(dev) == 0) {
  94                outb(cmd, dev->io + HP_CVR);
  95                spin_unlock_irqrestore(&dev->lock, flags);
  96                return 0;
  97        }
  98        spin_unlock_irqrestore(&dev->lock, flags);
  99
 100        snd_printd(KERN_ERR LOGNAME ": Send DSP command timeout\n");
 101
 102        return -EIO;
 103}
 104EXPORT_SYMBOL(snd_msnd_send_dsp_cmd);
 105
 106int snd_msnd_send_word(struct snd_msnd *dev, unsigned char high,
 107                   unsigned char mid, unsigned char low)
 108{
 109        unsigned int io = dev->io;
 110
 111        if (snd_msnd_wait_TXDE(dev) == 0) {
 112                outb(high, io + HP_TXH);
 113                outb(mid, io + HP_TXM);
 114                outb(low, io + HP_TXL);
 115                return 0;
 116        }
 117
 118        snd_printd(KERN_ERR LOGNAME ": Send host word timeout\n");
 119
 120        return -EIO;
 121}
 122EXPORT_SYMBOL(snd_msnd_send_word);
 123
 124int snd_msnd_upload_host(struct snd_msnd *dev, const u8 *bin, int len)
 125{
 126        int i;
 127
 128        if (len % 3 != 0) {
 129                snd_printk(KERN_ERR LOGNAME
 130                           ": Upload host data not multiple of 3!\n");
 131                return -EINVAL;
 132        }
 133
 134        for (i = 0; i < len; i += 3)
 135                if (snd_msnd_send_word(dev, bin[i], bin[i + 1], bin[i + 2]))
 136                        return -EIO;
 137
 138        inb(dev->io + HP_RXL);
 139        inb(dev->io + HP_CVR);
 140
 141        return 0;
 142}
 143EXPORT_SYMBOL(snd_msnd_upload_host);
 144
 145int snd_msnd_enable_irq(struct snd_msnd *dev)
 146{
 147        unsigned long flags;
 148
 149        if (dev->irq_ref++)
 150                return 0;
 151
 152        snd_printdd(LOGNAME ": Enabling IRQ\n");
 153
 154        spin_lock_irqsave(&dev->lock, flags);
 155        if (snd_msnd_wait_TXDE(dev) == 0) {
 156                outb(inb(dev->io + HP_ICR) | HPICR_TREQ, dev->io + HP_ICR);
 157                if (dev->type == msndClassic)
 158                        outb(dev->irqid, dev->io + HP_IRQM);
 159
 160                outb(inb(dev->io + HP_ICR) & ~HPICR_TREQ, dev->io + HP_ICR);
 161                outb(inb(dev->io + HP_ICR) | HPICR_RREQ, dev->io + HP_ICR);
 162                enable_irq(dev->irq);
 163                snd_msnd_init_queue(dev->DSPQ, dev->dspq_data_buff,
 164                                    dev->dspq_buff_size);
 165                spin_unlock_irqrestore(&dev->lock, flags);
 166                return 0;
 167        }
 168        spin_unlock_irqrestore(&dev->lock, flags);
 169
 170        snd_printd(KERN_ERR LOGNAME ": Enable IRQ failed\n");
 171
 172        return -EIO;
 173}
 174EXPORT_SYMBOL(snd_msnd_enable_irq);
 175
 176int snd_msnd_disable_irq(struct snd_msnd *dev)
 177{
 178        unsigned long flags;
 179
 180        if (--dev->irq_ref > 0)
 181                return 0;
 182
 183        if (dev->irq_ref < 0)
 184                snd_printd(KERN_WARNING LOGNAME ": IRQ ref count is %d\n",
 185                           dev->irq_ref);
 186
 187        snd_printdd(LOGNAME ": Disabling IRQ\n");
 188
 189        spin_lock_irqsave(&dev->lock, flags);
 190        if (snd_msnd_wait_TXDE(dev) == 0) {
 191                outb(inb(dev->io + HP_ICR) & ~HPICR_RREQ, dev->io + HP_ICR);
 192                if (dev->type == msndClassic)
 193                        outb(HPIRQ_NONE, dev->io + HP_IRQM);
 194                disable_irq(dev->irq);
 195                spin_unlock_irqrestore(&dev->lock, flags);
 196                return 0;
 197        }
 198        spin_unlock_irqrestore(&dev->lock, flags);
 199
 200        snd_printd(KERN_ERR LOGNAME ": Disable IRQ failed\n");
 201
 202        return -EIO;
 203}
 204EXPORT_SYMBOL(snd_msnd_disable_irq);
 205
 206static inline long get_play_delay_jiffies(struct snd_msnd *chip, long size)
 207{
 208        long tmp = (size * HZ * chip->play_sample_size) / 8;
 209        return tmp / (chip->play_sample_rate * chip->play_channels);
 210}
 211
 212static void snd_msnd_dsp_write_flush(struct snd_msnd *chip)
 213{
 214        if (!(chip->mode & FMODE_WRITE) || !test_bit(F_WRITING, &chip->flags))
 215                return;
 216        set_bit(F_WRITEFLUSH, &chip->flags);
 217/*      interruptible_sleep_on_timeout(
 218                &chip->writeflush,
 219                get_play_delay_jiffies(&chip, chip->DAPF.len));*/
 220        clear_bit(F_WRITEFLUSH, &chip->flags);
 221        if (!signal_pending(current))
 222                schedule_timeout_interruptible(
 223                        get_play_delay_jiffies(chip, chip->play_period_bytes));
 224        clear_bit(F_WRITING, &chip->flags);
 225}
 226
 227void snd_msnd_dsp_halt(struct snd_msnd *chip, struct file *file)
 228{
 229        if ((file ? file->f_mode : chip->mode) & FMODE_READ) {
 230                clear_bit(F_READING, &chip->flags);
 231                snd_msnd_send_dsp_cmd(chip, HDEX_RECORD_STOP);
 232                snd_msnd_disable_irq(chip);
 233                if (file) {
 234                        snd_printd(KERN_INFO LOGNAME
 235                                   ": Stopping read for %p\n", file);
 236                        chip->mode &= ~FMODE_READ;
 237                }
 238                clear_bit(F_AUDIO_READ_INUSE, &chip->flags);
 239        }
 240        if ((file ? file->f_mode : chip->mode) & FMODE_WRITE) {
 241                if (test_bit(F_WRITING, &chip->flags)) {
 242                        snd_msnd_dsp_write_flush(chip);
 243                        snd_msnd_send_dsp_cmd(chip, HDEX_PLAY_STOP);
 244                }
 245                snd_msnd_disable_irq(chip);
 246                if (file) {
 247                        snd_printd(KERN_INFO
 248                                   LOGNAME ": Stopping write for %p\n", file);
 249                        chip->mode &= ~FMODE_WRITE;
 250                }
 251                clear_bit(F_AUDIO_WRITE_INUSE, &chip->flags);
 252        }
 253}
 254EXPORT_SYMBOL(snd_msnd_dsp_halt);
 255
 256
 257int snd_msnd_DARQ(struct snd_msnd *chip, int bank)
 258{
 259        int /*size, n,*/ timeout = 3;
 260        u16 wTmp;
 261        /* void *DAQD; */
 262
 263        /* Increment the tail and check for queue wrap */
 264        wTmp = readw(chip->DARQ + JQS_wTail) + PCTODSP_OFFSET(DAQDS__size);
 265        if (wTmp > readw(chip->DARQ + JQS_wSize))
 266                wTmp = 0;
 267        while (wTmp == readw(chip->DARQ + JQS_wHead) && timeout--)
 268                udelay(1);
 269
 270        if (chip->capturePeriods == 2) {
 271                void *pDAQ = chip->mappedbase + DARQ_DATA_BUFF +
 272                             bank * DAQDS__size + DAQDS_wStart;
 273                unsigned short offset = 0x3000 + chip->capturePeriodBytes;
 274
 275                if (readw(pDAQ) != PCTODSP_BASED(0x3000))
 276                        offset = 0x3000;
 277                writew(PCTODSP_BASED(offset), pDAQ);
 278        }
 279
 280        writew(wTmp, chip->DARQ + JQS_wTail);
 281
 282#if 0
 283        /* Get our digital audio queue struct */
 284        DAQD = bank * DAQDS__size + chip->mappedbase + DARQ_DATA_BUFF;
 285
 286        /* Get length of data */
 287        size = readw(DAQD + DAQDS_wSize);
 288
 289        /* Read data from the head (unprotected bank 1 access okay
 290           since this is only called inside an interrupt) */
 291        outb(HPBLKSEL_1, chip->io + HP_BLKS);
 292        n = msnd_fifo_write(&chip->DARF,
 293                            (char *)(chip->base + bank * DAR_BUFF_SIZE),
 294                            size, 0);
 295        if (n <= 0) {
 296                outb(HPBLKSEL_0, chip->io + HP_BLKS);
 297                return n;
 298        }
 299        outb(HPBLKSEL_0, chip->io + HP_BLKS);
 300#endif
 301
 302        return 1;
 303}
 304EXPORT_SYMBOL(snd_msnd_DARQ);
 305
 306int snd_msnd_DAPQ(struct snd_msnd *chip, int start)
 307{
 308        u16     DAPQ_tail;
 309        int     protect = start, nbanks = 0;
 310        void    *DAQD;
 311        static int play_banks_submitted;
 312        /* unsigned long flags;
 313        spin_lock_irqsave(&chip->lock, flags); not necessary */
 314
 315        DAPQ_tail = readw(chip->DAPQ + JQS_wTail);
 316        while (DAPQ_tail != readw(chip->DAPQ + JQS_wHead) || start) {
 317                int bank_num = DAPQ_tail / PCTODSP_OFFSET(DAQDS__size);
 318
 319                if (start) {
 320                        start = 0;
 321                        play_banks_submitted = 0;
 322                }
 323
 324                /* Get our digital audio queue struct */
 325                DAQD = bank_num * DAQDS__size + chip->mappedbase +
 326                        DAPQ_DATA_BUFF;
 327
 328                /* Write size of this bank */
 329                writew(chip->play_period_bytes, DAQD + DAQDS_wSize);
 330                if (play_banks_submitted < 3)
 331                        ++play_banks_submitted;
 332                else if (chip->playPeriods == 2) {
 333                        unsigned short offset = chip->play_period_bytes;
 334
 335                        if (readw(DAQD + DAQDS_wStart) != PCTODSP_BASED(0x0))
 336                                offset = 0;
 337
 338                        writew(PCTODSP_BASED(offset), DAQD + DAQDS_wStart);
 339                }
 340                ++nbanks;
 341
 342                /* Then advance the tail */
 343                /*
 344                if (protect)
 345                        snd_printd(KERN_INFO "B %X %lX\n",
 346                                   bank_num, xtime.tv_usec);
 347                */
 348
 349                DAPQ_tail = (++bank_num % 3) * PCTODSP_OFFSET(DAQDS__size);
 350                writew(DAPQ_tail, chip->DAPQ + JQS_wTail);
 351                /* Tell the DSP to play the bank */
 352                snd_msnd_send_dsp_cmd(chip, HDEX_PLAY_START);
 353                if (protect)
 354                        if (2 == bank_num)
 355                                break;
 356        }
 357        /*
 358        if (protect)
 359                snd_printd(KERN_INFO "%lX\n", xtime.tv_usec);
 360        */
 361        /* spin_unlock_irqrestore(&chip->lock, flags); not necessary */
 362        return nbanks;
 363}
 364EXPORT_SYMBOL(snd_msnd_DAPQ);
 365
 366static void snd_msnd_play_reset_queue(struct snd_msnd *chip,
 367                                      unsigned int pcm_periods,
 368                                      unsigned int pcm_count)
 369{
 370        int     n;
 371        void    *pDAQ = chip->mappedbase + DAPQ_DATA_BUFF;
 372
 373        chip->last_playbank = -1;
 374        chip->playLimit = pcm_count * (pcm_periods - 1);
 375        chip->playPeriods = pcm_periods;
 376        writew(PCTODSP_OFFSET(0 * DAQDS__size), chip->DAPQ + JQS_wHead);
 377        writew(PCTODSP_OFFSET(0 * DAQDS__size), chip->DAPQ + JQS_wTail);
 378
 379        chip->play_period_bytes = pcm_count;
 380
 381        for (n = 0; n < pcm_periods; ++n, pDAQ += DAQDS__size) {
 382                writew(PCTODSP_BASED((u32)(pcm_count * n)),
 383                        pDAQ + DAQDS_wStart);
 384                writew(0, pDAQ + DAQDS_wSize);
 385                writew(1, pDAQ + DAQDS_wFormat);
 386                writew(chip->play_sample_size, pDAQ + DAQDS_wSampleSize);
 387                writew(chip->play_channels, pDAQ + DAQDS_wChannels);
 388                writew(chip->play_sample_rate, pDAQ + DAQDS_wSampleRate);
 389                writew(HIMT_PLAY_DONE * 0x100 + n, pDAQ + DAQDS_wIntMsg);
 390                writew(n, pDAQ + DAQDS_wFlags);
 391        }
 392}
 393
 394static void snd_msnd_capture_reset_queue(struct snd_msnd *chip,
 395                                         unsigned int pcm_periods,
 396                                         unsigned int pcm_count)
 397{
 398        int             n;
 399        void            *pDAQ;
 400        /* unsigned long        flags; */
 401
 402        /* snd_msnd_init_queue(chip->DARQ, DARQ_DATA_BUFF, DARQ_BUFF_SIZE); */
 403
 404        chip->last_recbank = 2;
 405        chip->captureLimit = pcm_count * (pcm_periods - 1);
 406        chip->capturePeriods = pcm_periods;
 407        writew(PCTODSP_OFFSET(0 * DAQDS__size), chip->DARQ + JQS_wHead);
 408        writew(PCTODSP_OFFSET(chip->last_recbank * DAQDS__size),
 409                chip->DARQ + JQS_wTail);
 410
 411#if 0 /* Critical section: bank 1 access. this is how the OSS driver does it:*/
 412        spin_lock_irqsave(&chip->lock, flags);
 413        outb(HPBLKSEL_1, chip->io + HP_BLKS);
 414        memset_io(chip->mappedbase, 0, DAR_BUFF_SIZE * 3);
 415        outb(HPBLKSEL_0, chip->io + HP_BLKS);
 416        spin_unlock_irqrestore(&chip->lock, flags);
 417#endif
 418
 419        chip->capturePeriodBytes = pcm_count;
 420        snd_printdd("snd_msnd_capture_reset_queue() %i\n", pcm_count);
 421
 422        pDAQ = chip->mappedbase + DARQ_DATA_BUFF;
 423
 424        for (n = 0; n < pcm_periods; ++n, pDAQ += DAQDS__size) {
 425                u32 tmp = pcm_count * n;
 426
 427                writew(PCTODSP_BASED(tmp + 0x3000), pDAQ + DAQDS_wStart);
 428                writew(pcm_count, pDAQ + DAQDS_wSize);
 429                writew(1, pDAQ + DAQDS_wFormat);
 430                writew(chip->capture_sample_size, pDAQ + DAQDS_wSampleSize);
 431                writew(chip->capture_channels, pDAQ + DAQDS_wChannels);
 432                writew(chip->capture_sample_rate, pDAQ + DAQDS_wSampleRate);
 433                writew(HIMT_RECORD_DONE * 0x100 + n, pDAQ + DAQDS_wIntMsg);
 434                writew(n, pDAQ + DAQDS_wFlags);
 435        }
 436}
 437
 438static struct snd_pcm_hardware snd_msnd_playback = {
 439        .info =                 SNDRV_PCM_INFO_MMAP |
 440                                SNDRV_PCM_INFO_INTERLEAVED |
 441                                SNDRV_PCM_INFO_MMAP_VALID |
 442                                SNDRV_PCM_INFO_BATCH,
 443        .formats =              SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
 444        .rates =                SNDRV_PCM_RATE_8000_48000,
 445        .rate_min =             8000,
 446        .rate_max =             48000,
 447        .channels_min =         1,
 448        .channels_max =         2,
 449        .buffer_bytes_max =     0x3000,
 450        .period_bytes_min =     0x40,
 451        .period_bytes_max =     0x1800,
 452        .periods_min =          2,
 453        .periods_max =          3,
 454        .fifo_size =            0,
 455};
 456
 457static struct snd_pcm_hardware snd_msnd_capture = {
 458        .info =                 SNDRV_PCM_INFO_MMAP |
 459                                SNDRV_PCM_INFO_INTERLEAVED |
 460                                SNDRV_PCM_INFO_MMAP_VALID |
 461                                SNDRV_PCM_INFO_BATCH,
 462        .formats =              SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
 463        .rates =                SNDRV_PCM_RATE_8000_48000,
 464        .rate_min =             8000,
 465        .rate_max =             48000,
 466        .channels_min =         1,
 467        .channels_max =         2,
 468        .buffer_bytes_max =     0x3000,
 469        .period_bytes_min =     0x40,
 470        .period_bytes_max =     0x1800,
 471        .periods_min =          2,
 472        .periods_max =          3,
 473        .fifo_size =            0,
 474};
 475
 476
 477static int snd_msnd_playback_open(struct snd_pcm_substream *substream)
 478{
 479        struct snd_pcm_runtime *runtime = substream->runtime;
 480        struct snd_msnd *chip = snd_pcm_substream_chip(substream);
 481
 482        set_bit(F_AUDIO_WRITE_INUSE, &chip->flags);
 483        clear_bit(F_WRITING, &chip->flags);
 484        snd_msnd_enable_irq(chip);
 485
 486        runtime->dma_area = chip->mappedbase;
 487        runtime->dma_bytes = 0x3000;
 488
 489        chip->playback_substream = substream;
 490        runtime->hw = snd_msnd_playback;
 491        return 0;
 492}
 493
 494static int snd_msnd_playback_close(struct snd_pcm_substream *substream)
 495{
 496        struct snd_msnd *chip = snd_pcm_substream_chip(substream);
 497
 498        snd_msnd_disable_irq(chip);
 499        clear_bit(F_AUDIO_WRITE_INUSE, &chip->flags);
 500        return 0;
 501}
 502
 503
 504static int snd_msnd_playback_hw_params(struct snd_pcm_substream *substream,
 505                                        struct snd_pcm_hw_params *params)
 506{
 507        int     i;
 508        struct snd_msnd *chip = snd_pcm_substream_chip(substream);
 509        void    *pDAQ = chip->mappedbase + DAPQ_DATA_BUFF;
 510
 511        chip->play_sample_size = snd_pcm_format_width(params_format(params));
 512        chip->play_channels = params_channels(params);
 513        chip->play_sample_rate = params_rate(params);
 514
 515        for (i = 0; i < 3; ++i, pDAQ += DAQDS__size) {
 516                writew(chip->play_sample_size, pDAQ + DAQDS_wSampleSize);
 517                writew(chip->play_channels, pDAQ + DAQDS_wChannels);
 518                writew(chip->play_sample_rate, pDAQ + DAQDS_wSampleRate);
 519        }
 520        /* dont do this here:
 521         * snd_msnd_calibrate_adc(chip->play_sample_rate);
 522         */
 523
 524        return 0;
 525}
 526
 527static int snd_msnd_playback_prepare(struct snd_pcm_substream *substream)
 528{
 529        struct snd_msnd *chip = snd_pcm_substream_chip(substream);
 530        unsigned int pcm_size = snd_pcm_lib_buffer_bytes(substream);
 531        unsigned int pcm_count = snd_pcm_lib_period_bytes(substream);
 532        unsigned int pcm_periods = pcm_size / pcm_count;
 533
 534        snd_msnd_play_reset_queue(chip, pcm_periods, pcm_count);
 535        chip->playDMAPos = 0;
 536        return 0;
 537}
 538
 539static int snd_msnd_playback_trigger(struct snd_pcm_substream *substream,
 540                                     int cmd)
 541{
 542        struct snd_msnd *chip = snd_pcm_substream_chip(substream);
 543        int     result = 0;
 544
 545        if (cmd == SNDRV_PCM_TRIGGER_START) {
 546                snd_printdd("snd_msnd_playback_trigger(START)\n");
 547                chip->banksPlayed = 0;
 548                set_bit(F_WRITING, &chip->flags);
 549                snd_msnd_DAPQ(chip, 1);
 550        } else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
 551                snd_printdd("snd_msnd_playback_trigger(STop)\n");
 552                /* interrupt diagnostic, comment this out later */
 553                clear_bit(F_WRITING, &chip->flags);
 554                snd_msnd_send_dsp_cmd(chip, HDEX_PLAY_STOP);
 555        } else {
 556                snd_printd(KERN_ERR "snd_msnd_playback_trigger(?????)\n");
 557                result = -EINVAL;
 558        }
 559
 560        snd_printdd("snd_msnd_playback_trigger() ENDE\n");
 561        return result;
 562}
 563
 564static snd_pcm_uframes_t
 565snd_msnd_playback_pointer(struct snd_pcm_substream *substream)
 566{
 567        struct snd_msnd *chip = snd_pcm_substream_chip(substream);
 568
 569        return bytes_to_frames(substream->runtime, chip->playDMAPos);
 570}
 571
 572
 573static struct snd_pcm_ops snd_msnd_playback_ops = {
 574        .open =         snd_msnd_playback_open,
 575        .close =        snd_msnd_playback_close,
 576        .ioctl =        snd_pcm_lib_ioctl,
 577        .hw_params =    snd_msnd_playback_hw_params,
 578        .prepare =      snd_msnd_playback_prepare,
 579        .trigger =      snd_msnd_playback_trigger,
 580        .pointer =      snd_msnd_playback_pointer,
 581};
 582
 583static int snd_msnd_capture_open(struct snd_pcm_substream *substream)
 584{
 585        struct snd_pcm_runtime *runtime = substream->runtime;
 586        struct snd_msnd *chip = snd_pcm_substream_chip(substream);
 587
 588        set_bit(F_AUDIO_READ_INUSE, &chip->flags);
 589        snd_msnd_enable_irq(chip);
 590        runtime->dma_area = chip->mappedbase + 0x3000;
 591        runtime->dma_bytes = 0x3000;
 592        memset(runtime->dma_area, 0, runtime->dma_bytes);
 593        chip->capture_substream = substream;
 594        runtime->hw = snd_msnd_capture;
 595        return 0;
 596}
 597
 598static int snd_msnd_capture_close(struct snd_pcm_substream *substream)
 599{
 600        struct snd_msnd *chip = snd_pcm_substream_chip(substream);
 601
 602        snd_msnd_disable_irq(chip);
 603        clear_bit(F_AUDIO_READ_INUSE, &chip->flags);
 604        return 0;
 605}
 606
 607static int snd_msnd_capture_prepare(struct snd_pcm_substream *substream)
 608{
 609        struct snd_msnd *chip = snd_pcm_substream_chip(substream);
 610        unsigned int pcm_size = snd_pcm_lib_buffer_bytes(substream);
 611        unsigned int pcm_count = snd_pcm_lib_period_bytes(substream);
 612        unsigned int pcm_periods = pcm_size / pcm_count;
 613
 614        snd_msnd_capture_reset_queue(chip, pcm_periods, pcm_count);
 615        chip->captureDMAPos = 0;
 616        return 0;
 617}
 618
 619static int snd_msnd_capture_trigger(struct snd_pcm_substream *substream,
 620                                    int cmd)
 621{
 622        struct snd_msnd *chip = snd_pcm_substream_chip(substream);
 623
 624        if (cmd == SNDRV_PCM_TRIGGER_START) {
 625                chip->last_recbank = -1;
 626                set_bit(F_READING, &chip->flags);
 627                if (snd_msnd_send_dsp_cmd(chip, HDEX_RECORD_START) == 0)
 628                        return 0;
 629
 630                clear_bit(F_READING, &chip->flags);
 631        } else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
 632                clear_bit(F_READING, &chip->flags);
 633                snd_msnd_send_dsp_cmd(chip, HDEX_RECORD_STOP);
 634                return 0;
 635        }
 636        return -EINVAL;
 637}
 638
 639
 640static snd_pcm_uframes_t
 641snd_msnd_capture_pointer(struct snd_pcm_substream *substream)
 642{
 643        struct snd_pcm_runtime *runtime = substream->runtime;
 644        struct snd_msnd *chip = snd_pcm_substream_chip(substream);
 645
 646        return bytes_to_frames(runtime, chip->captureDMAPos);
 647}
 648
 649
 650static int snd_msnd_capture_hw_params(struct snd_pcm_substream *substream,
 651                                        struct snd_pcm_hw_params *params)
 652{
 653        int             i;
 654        struct snd_msnd *chip = snd_pcm_substream_chip(substream);
 655        void            *pDAQ = chip->mappedbase + DARQ_DATA_BUFF;
 656
 657        chip->capture_sample_size = snd_pcm_format_width(params_format(params));
 658        chip->capture_channels = params_channels(params);
 659        chip->capture_sample_rate = params_rate(params);
 660
 661        for (i = 0; i < 3; ++i, pDAQ += DAQDS__size) {
 662                writew(chip->capture_sample_size, pDAQ + DAQDS_wSampleSize);
 663                writew(chip->capture_channels, pDAQ + DAQDS_wChannels);
 664                writew(chip->capture_sample_rate, pDAQ + DAQDS_wSampleRate);
 665        }
 666        return 0;
 667}
 668
 669
 670static struct snd_pcm_ops snd_msnd_capture_ops = {
 671        .open =         snd_msnd_capture_open,
 672        .close =        snd_msnd_capture_close,
 673        .ioctl =        snd_pcm_lib_ioctl,
 674        .hw_params =    snd_msnd_capture_hw_params,
 675        .prepare =      snd_msnd_capture_prepare,
 676        .trigger =      snd_msnd_capture_trigger,
 677        .pointer =      snd_msnd_capture_pointer,
 678};
 679
 680
 681int snd_msnd_pcm(struct snd_card *card, int device,
 682                        struct snd_pcm **rpcm)
 683{
 684        struct snd_msnd *chip = card->private_data;
 685        struct snd_pcm  *pcm;
 686        int err;
 687
 688        err = snd_pcm_new(card, "MSNDPINNACLE", device, 1, 1, &pcm);
 689        if (err < 0)
 690                return err;
 691
 692        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_msnd_playback_ops);
 693        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_msnd_capture_ops);
 694
 695        pcm->private_data = chip;
 696        strcpy(pcm->name, "Hurricane");
 697
 698
 699        if (rpcm)
 700                *rpcm = pcm;
 701        return 0;
 702}
 703EXPORT_SYMBOL(snd_msnd_pcm);
 704
 705MODULE_DESCRIPTION("Common routines for Turtle Beach Multisound drivers");
 706MODULE_LICENSE("GPL");
 707
 708