162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/********************************************************************* 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * 2002/06/30 Karsten Wiese: 562306a36Sopenharmony_ci * removed kernel-version dependencies. 662306a36Sopenharmony_ci * ripped from linux kernel 2.4.18 (OSS Implementation) by me. 762306a36Sopenharmony_ci * In the OSS Version, this file is compiled to a separate MODULE, 862306a36Sopenharmony_ci * that is used by the pinnacle and the classic driver. 962306a36Sopenharmony_ci * since there is no classic driver for alsa yet (i dont have a classic 1062306a36Sopenharmony_ci * & writing one blindfold is difficult) this file's object is statically 1162306a36Sopenharmony_ci * linked into the pinnacle-driver-module for now. look for the string 1262306a36Sopenharmony_ci * "uncomment this to make this a module again" 1362306a36Sopenharmony_ci * to do guess what. 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * the following is a copy of the 2.4.18 OSS FREE file-heading comment: 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * msnd.c - Driver Base 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * Turtle Beach MultiSound Sound Card Driver for Linux 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * Copyright (C) 1998 Andrew Veliath 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci ********************************************************************/ 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <linux/kernel.h> 2662306a36Sopenharmony_ci#include <linux/sched/signal.h> 2762306a36Sopenharmony_ci#include <linux/types.h> 2862306a36Sopenharmony_ci#include <linux/interrupt.h> 2962306a36Sopenharmony_ci#include <linux/io.h> 3062306a36Sopenharmony_ci#include <linux/fs.h> 3162306a36Sopenharmony_ci#include <linux/delay.h> 3262306a36Sopenharmony_ci#include <linux/module.h> 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include <sound/core.h> 3562306a36Sopenharmony_ci#include <sound/initval.h> 3662306a36Sopenharmony_ci#include <sound/pcm.h> 3762306a36Sopenharmony_ci#include <sound/pcm_params.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#include "msnd.h" 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define LOGNAME "msnd" 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_civoid snd_msnd_init_queue(void __iomem *base, int start, int size) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci writew(PCTODSP_BASED(start), base + JQS_wStart); 4762306a36Sopenharmony_ci writew(PCTODSP_OFFSET(size) - 1, base + JQS_wSize); 4862306a36Sopenharmony_ci writew(0, base + JQS_wHead); 4962306a36Sopenharmony_ci writew(0, base + JQS_wTail); 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ciEXPORT_SYMBOL(snd_msnd_init_queue); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic int snd_msnd_wait_TXDE(struct snd_msnd *dev) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci unsigned int io = dev->io; 5662306a36Sopenharmony_ci int timeout = 1000; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci while (timeout-- > 0) 5962306a36Sopenharmony_ci if (inb(io + HP_ISR) & HPISR_TXDE) 6062306a36Sopenharmony_ci return 0; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci return -EIO; 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic int snd_msnd_wait_HC0(struct snd_msnd *dev) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci unsigned int io = dev->io; 6862306a36Sopenharmony_ci int timeout = 1000; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci while (timeout-- > 0) 7162306a36Sopenharmony_ci if (!(inb(io + HP_CVR) & HPCVR_HC)) 7262306a36Sopenharmony_ci return 0; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci return -EIO; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ciint snd_msnd_send_dsp_cmd(struct snd_msnd *dev, u8 cmd) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci unsigned long flags; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci spin_lock_irqsave(&dev->lock, flags); 8262306a36Sopenharmony_ci if (snd_msnd_wait_HC0(dev) == 0) { 8362306a36Sopenharmony_ci outb(cmd, dev->io + HP_CVR); 8462306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 8562306a36Sopenharmony_ci return 0; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci snd_printd(KERN_ERR LOGNAME ": Send DSP command timeout\n"); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci return -EIO; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ciEXPORT_SYMBOL(snd_msnd_send_dsp_cmd); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ciint snd_msnd_send_word(struct snd_msnd *dev, unsigned char high, 9662306a36Sopenharmony_ci unsigned char mid, unsigned char low) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci unsigned int io = dev->io; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci if (snd_msnd_wait_TXDE(dev) == 0) { 10162306a36Sopenharmony_ci outb(high, io + HP_TXH); 10262306a36Sopenharmony_ci outb(mid, io + HP_TXM); 10362306a36Sopenharmony_ci outb(low, io + HP_TXL); 10462306a36Sopenharmony_ci return 0; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci snd_printd(KERN_ERR LOGNAME ": Send host word timeout\n"); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci return -EIO; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ciEXPORT_SYMBOL(snd_msnd_send_word); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ciint snd_msnd_upload_host(struct snd_msnd *dev, const u8 *bin, int len) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci int i; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (len % 3 != 0) { 11862306a36Sopenharmony_ci snd_printk(KERN_ERR LOGNAME 11962306a36Sopenharmony_ci ": Upload host data not multiple of 3!\n"); 12062306a36Sopenharmony_ci return -EINVAL; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci for (i = 0; i < len; i += 3) 12462306a36Sopenharmony_ci if (snd_msnd_send_word(dev, bin[i], bin[i + 1], bin[i + 2])) 12562306a36Sopenharmony_ci return -EIO; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci inb(dev->io + HP_RXL); 12862306a36Sopenharmony_ci inb(dev->io + HP_CVR); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci return 0; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ciEXPORT_SYMBOL(snd_msnd_upload_host); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ciint snd_msnd_enable_irq(struct snd_msnd *dev) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci unsigned long flags; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (dev->irq_ref++) 13962306a36Sopenharmony_ci return 0; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci snd_printdd(LOGNAME ": Enabling IRQ\n"); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci spin_lock_irqsave(&dev->lock, flags); 14462306a36Sopenharmony_ci if (snd_msnd_wait_TXDE(dev) == 0) { 14562306a36Sopenharmony_ci outb(inb(dev->io + HP_ICR) | HPICR_TREQ, dev->io + HP_ICR); 14662306a36Sopenharmony_ci if (dev->type == msndClassic) 14762306a36Sopenharmony_ci outb(dev->irqid, dev->io + HP_IRQM); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci outb(inb(dev->io + HP_ICR) & ~HPICR_TREQ, dev->io + HP_ICR); 15062306a36Sopenharmony_ci outb(inb(dev->io + HP_ICR) | HPICR_RREQ, dev->io + HP_ICR); 15162306a36Sopenharmony_ci enable_irq(dev->irq); 15262306a36Sopenharmony_ci snd_msnd_init_queue(dev->DSPQ, dev->dspq_data_buff, 15362306a36Sopenharmony_ci dev->dspq_buff_size); 15462306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 15562306a36Sopenharmony_ci return 0; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci snd_printd(KERN_ERR LOGNAME ": Enable IRQ failed\n"); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci return -EIO; 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ciEXPORT_SYMBOL(snd_msnd_enable_irq); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ciint snd_msnd_disable_irq(struct snd_msnd *dev) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci unsigned long flags; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if (--dev->irq_ref > 0) 17062306a36Sopenharmony_ci return 0; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (dev->irq_ref < 0) 17362306a36Sopenharmony_ci snd_printd(KERN_WARNING LOGNAME ": IRQ ref count is %d\n", 17462306a36Sopenharmony_ci dev->irq_ref); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci snd_printdd(LOGNAME ": Disabling IRQ\n"); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci spin_lock_irqsave(&dev->lock, flags); 17962306a36Sopenharmony_ci if (snd_msnd_wait_TXDE(dev) == 0) { 18062306a36Sopenharmony_ci outb(inb(dev->io + HP_ICR) & ~HPICR_RREQ, dev->io + HP_ICR); 18162306a36Sopenharmony_ci if (dev->type == msndClassic) 18262306a36Sopenharmony_ci outb(HPIRQ_NONE, dev->io + HP_IRQM); 18362306a36Sopenharmony_ci disable_irq(dev->irq); 18462306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 18562306a36Sopenharmony_ci return 0; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci snd_printd(KERN_ERR LOGNAME ": Disable IRQ failed\n"); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci return -EIO; 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ciEXPORT_SYMBOL(snd_msnd_disable_irq); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic inline long get_play_delay_jiffies(struct snd_msnd *chip, long size) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci long tmp = (size * HZ * chip->play_sample_size) / 8; 19862306a36Sopenharmony_ci return tmp / (chip->play_sample_rate * chip->play_channels); 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic void snd_msnd_dsp_write_flush(struct snd_msnd *chip) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci if (!(chip->mode & FMODE_WRITE) || !test_bit(F_WRITING, &chip->flags)) 20462306a36Sopenharmony_ci return; 20562306a36Sopenharmony_ci set_bit(F_WRITEFLUSH, &chip->flags); 20662306a36Sopenharmony_ci/* interruptible_sleep_on_timeout( 20762306a36Sopenharmony_ci &chip->writeflush, 20862306a36Sopenharmony_ci get_play_delay_jiffies(&chip, chip->DAPF.len));*/ 20962306a36Sopenharmony_ci clear_bit(F_WRITEFLUSH, &chip->flags); 21062306a36Sopenharmony_ci if (!signal_pending(current)) 21162306a36Sopenharmony_ci schedule_timeout_interruptible( 21262306a36Sopenharmony_ci get_play_delay_jiffies(chip, chip->play_period_bytes)); 21362306a36Sopenharmony_ci clear_bit(F_WRITING, &chip->flags); 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_civoid snd_msnd_dsp_halt(struct snd_msnd *chip, struct file *file) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci if ((file ? file->f_mode : chip->mode) & FMODE_READ) { 21962306a36Sopenharmony_ci clear_bit(F_READING, &chip->flags); 22062306a36Sopenharmony_ci snd_msnd_send_dsp_cmd(chip, HDEX_RECORD_STOP); 22162306a36Sopenharmony_ci snd_msnd_disable_irq(chip); 22262306a36Sopenharmony_ci if (file) { 22362306a36Sopenharmony_ci snd_printd(KERN_INFO LOGNAME 22462306a36Sopenharmony_ci ": Stopping read for %p\n", file); 22562306a36Sopenharmony_ci chip->mode &= ~FMODE_READ; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci clear_bit(F_AUDIO_READ_INUSE, &chip->flags); 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci if ((file ? file->f_mode : chip->mode) & FMODE_WRITE) { 23062306a36Sopenharmony_ci if (test_bit(F_WRITING, &chip->flags)) { 23162306a36Sopenharmony_ci snd_msnd_dsp_write_flush(chip); 23262306a36Sopenharmony_ci snd_msnd_send_dsp_cmd(chip, HDEX_PLAY_STOP); 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci snd_msnd_disable_irq(chip); 23562306a36Sopenharmony_ci if (file) { 23662306a36Sopenharmony_ci snd_printd(KERN_INFO 23762306a36Sopenharmony_ci LOGNAME ": Stopping write for %p\n", file); 23862306a36Sopenharmony_ci chip->mode &= ~FMODE_WRITE; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci clear_bit(F_AUDIO_WRITE_INUSE, &chip->flags); 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ciEXPORT_SYMBOL(snd_msnd_dsp_halt); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ciint snd_msnd_DARQ(struct snd_msnd *chip, int bank) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci int /*size, n,*/ timeout = 3; 24962306a36Sopenharmony_ci u16 wTmp; 25062306a36Sopenharmony_ci /* void *DAQD; */ 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* Increment the tail and check for queue wrap */ 25362306a36Sopenharmony_ci wTmp = readw(chip->DARQ + JQS_wTail) + PCTODSP_OFFSET(DAQDS__size); 25462306a36Sopenharmony_ci if (wTmp > readw(chip->DARQ + JQS_wSize)) 25562306a36Sopenharmony_ci wTmp = 0; 25662306a36Sopenharmony_ci while (wTmp == readw(chip->DARQ + JQS_wHead) && timeout--) 25762306a36Sopenharmony_ci udelay(1); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (chip->capturePeriods == 2) { 26062306a36Sopenharmony_ci void __iomem *pDAQ = chip->mappedbase + DARQ_DATA_BUFF + 26162306a36Sopenharmony_ci bank * DAQDS__size + DAQDS_wStart; 26262306a36Sopenharmony_ci unsigned short offset = 0x3000 + chip->capturePeriodBytes; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (readw(pDAQ) != PCTODSP_BASED(0x3000)) 26562306a36Sopenharmony_ci offset = 0x3000; 26662306a36Sopenharmony_ci writew(PCTODSP_BASED(offset), pDAQ); 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci writew(wTmp, chip->DARQ + JQS_wTail); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci#if 0 27262306a36Sopenharmony_ci /* Get our digital audio queue struct */ 27362306a36Sopenharmony_ci DAQD = bank * DAQDS__size + chip->mappedbase + DARQ_DATA_BUFF; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci /* Get length of data */ 27662306a36Sopenharmony_ci size = readw(DAQD + DAQDS_wSize); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* Read data from the head (unprotected bank 1 access okay 27962306a36Sopenharmony_ci since this is only called inside an interrupt) */ 28062306a36Sopenharmony_ci outb(HPBLKSEL_1, chip->io + HP_BLKS); 28162306a36Sopenharmony_ci n = msnd_fifo_write(&chip->DARF, 28262306a36Sopenharmony_ci (char *)(chip->base + bank * DAR_BUFF_SIZE), 28362306a36Sopenharmony_ci size, 0); 28462306a36Sopenharmony_ci if (n <= 0) { 28562306a36Sopenharmony_ci outb(HPBLKSEL_0, chip->io + HP_BLKS); 28662306a36Sopenharmony_ci return n; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci outb(HPBLKSEL_0, chip->io + HP_BLKS); 28962306a36Sopenharmony_ci#endif 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci return 1; 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ciEXPORT_SYMBOL(snd_msnd_DARQ); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ciint snd_msnd_DAPQ(struct snd_msnd *chip, int start) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci u16 DAPQ_tail; 29862306a36Sopenharmony_ci int protect = start, nbanks = 0; 29962306a36Sopenharmony_ci void __iomem *DAQD; 30062306a36Sopenharmony_ci static int play_banks_submitted; 30162306a36Sopenharmony_ci /* unsigned long flags; 30262306a36Sopenharmony_ci spin_lock_irqsave(&chip->lock, flags); not necessary */ 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci DAPQ_tail = readw(chip->DAPQ + JQS_wTail); 30562306a36Sopenharmony_ci while (DAPQ_tail != readw(chip->DAPQ + JQS_wHead) || start) { 30662306a36Sopenharmony_ci int bank_num = DAPQ_tail / PCTODSP_OFFSET(DAQDS__size); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci if (start) { 30962306a36Sopenharmony_ci start = 0; 31062306a36Sopenharmony_ci play_banks_submitted = 0; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci /* Get our digital audio queue struct */ 31462306a36Sopenharmony_ci DAQD = bank_num * DAQDS__size + chip->mappedbase + 31562306a36Sopenharmony_ci DAPQ_DATA_BUFF; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci /* Write size of this bank */ 31862306a36Sopenharmony_ci writew(chip->play_period_bytes, DAQD + DAQDS_wSize); 31962306a36Sopenharmony_ci if (play_banks_submitted < 3) 32062306a36Sopenharmony_ci ++play_banks_submitted; 32162306a36Sopenharmony_ci else if (chip->playPeriods == 2) { 32262306a36Sopenharmony_ci unsigned short offset = chip->play_period_bytes; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (readw(DAQD + DAQDS_wStart) != PCTODSP_BASED(0x0)) 32562306a36Sopenharmony_ci offset = 0; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci writew(PCTODSP_BASED(offset), DAQD + DAQDS_wStart); 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci ++nbanks; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci /* Then advance the tail */ 33262306a36Sopenharmony_ci /* 33362306a36Sopenharmony_ci if (protect) 33462306a36Sopenharmony_ci snd_printd(KERN_INFO "B %X %lX\n", 33562306a36Sopenharmony_ci bank_num, xtime.tv_usec); 33662306a36Sopenharmony_ci */ 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci DAPQ_tail = (++bank_num % 3) * PCTODSP_OFFSET(DAQDS__size); 33962306a36Sopenharmony_ci writew(DAPQ_tail, chip->DAPQ + JQS_wTail); 34062306a36Sopenharmony_ci /* Tell the DSP to play the bank */ 34162306a36Sopenharmony_ci snd_msnd_send_dsp_cmd(chip, HDEX_PLAY_START); 34262306a36Sopenharmony_ci if (protect) 34362306a36Sopenharmony_ci if (2 == bank_num) 34462306a36Sopenharmony_ci break; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci /* 34762306a36Sopenharmony_ci if (protect) 34862306a36Sopenharmony_ci snd_printd(KERN_INFO "%lX\n", xtime.tv_usec); 34962306a36Sopenharmony_ci */ 35062306a36Sopenharmony_ci /* spin_unlock_irqrestore(&chip->lock, flags); not necessary */ 35162306a36Sopenharmony_ci return nbanks; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ciEXPORT_SYMBOL(snd_msnd_DAPQ); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic void snd_msnd_play_reset_queue(struct snd_msnd *chip, 35662306a36Sopenharmony_ci unsigned int pcm_periods, 35762306a36Sopenharmony_ci unsigned int pcm_count) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci int n; 36062306a36Sopenharmony_ci void __iomem *pDAQ = chip->mappedbase + DAPQ_DATA_BUFF; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci chip->last_playbank = -1; 36362306a36Sopenharmony_ci chip->playLimit = pcm_count * (pcm_periods - 1); 36462306a36Sopenharmony_ci chip->playPeriods = pcm_periods; 36562306a36Sopenharmony_ci writew(PCTODSP_OFFSET(0 * DAQDS__size), chip->DAPQ + JQS_wHead); 36662306a36Sopenharmony_ci writew(PCTODSP_OFFSET(0 * DAQDS__size), chip->DAPQ + JQS_wTail); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci chip->play_period_bytes = pcm_count; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci for (n = 0; n < pcm_periods; ++n, pDAQ += DAQDS__size) { 37162306a36Sopenharmony_ci writew(PCTODSP_BASED((u32)(pcm_count * n)), 37262306a36Sopenharmony_ci pDAQ + DAQDS_wStart); 37362306a36Sopenharmony_ci writew(0, pDAQ + DAQDS_wSize); 37462306a36Sopenharmony_ci writew(1, pDAQ + DAQDS_wFormat); 37562306a36Sopenharmony_ci writew(chip->play_sample_size, pDAQ + DAQDS_wSampleSize); 37662306a36Sopenharmony_ci writew(chip->play_channels, pDAQ + DAQDS_wChannels); 37762306a36Sopenharmony_ci writew(chip->play_sample_rate, pDAQ + DAQDS_wSampleRate); 37862306a36Sopenharmony_ci writew(HIMT_PLAY_DONE * 0x100 + n, pDAQ + DAQDS_wIntMsg); 37962306a36Sopenharmony_ci writew(n, pDAQ + DAQDS_wFlags); 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic void snd_msnd_capture_reset_queue(struct snd_msnd *chip, 38462306a36Sopenharmony_ci unsigned int pcm_periods, 38562306a36Sopenharmony_ci unsigned int pcm_count) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci int n; 38862306a36Sopenharmony_ci void __iomem *pDAQ; 38962306a36Sopenharmony_ci /* unsigned long flags; */ 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci /* snd_msnd_init_queue(chip->DARQ, DARQ_DATA_BUFF, DARQ_BUFF_SIZE); */ 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci chip->last_recbank = 2; 39462306a36Sopenharmony_ci chip->captureLimit = pcm_count * (pcm_periods - 1); 39562306a36Sopenharmony_ci chip->capturePeriods = pcm_periods; 39662306a36Sopenharmony_ci writew(PCTODSP_OFFSET(0 * DAQDS__size), chip->DARQ + JQS_wHead); 39762306a36Sopenharmony_ci writew(PCTODSP_OFFSET(chip->last_recbank * DAQDS__size), 39862306a36Sopenharmony_ci chip->DARQ + JQS_wTail); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci#if 0 /* Critical section: bank 1 access. this is how the OSS driver does it:*/ 40162306a36Sopenharmony_ci spin_lock_irqsave(&chip->lock, flags); 40262306a36Sopenharmony_ci outb(HPBLKSEL_1, chip->io + HP_BLKS); 40362306a36Sopenharmony_ci memset_io(chip->mappedbase, 0, DAR_BUFF_SIZE * 3); 40462306a36Sopenharmony_ci outb(HPBLKSEL_0, chip->io + HP_BLKS); 40562306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->lock, flags); 40662306a36Sopenharmony_ci#endif 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci chip->capturePeriodBytes = pcm_count; 40962306a36Sopenharmony_ci snd_printdd("snd_msnd_capture_reset_queue() %i\n", pcm_count); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci pDAQ = chip->mappedbase + DARQ_DATA_BUFF; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci for (n = 0; n < pcm_periods; ++n, pDAQ += DAQDS__size) { 41462306a36Sopenharmony_ci u32 tmp = pcm_count * n; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci writew(PCTODSP_BASED(tmp + 0x3000), pDAQ + DAQDS_wStart); 41762306a36Sopenharmony_ci writew(pcm_count, pDAQ + DAQDS_wSize); 41862306a36Sopenharmony_ci writew(1, pDAQ + DAQDS_wFormat); 41962306a36Sopenharmony_ci writew(chip->capture_sample_size, pDAQ + DAQDS_wSampleSize); 42062306a36Sopenharmony_ci writew(chip->capture_channels, pDAQ + DAQDS_wChannels); 42162306a36Sopenharmony_ci writew(chip->capture_sample_rate, pDAQ + DAQDS_wSampleRate); 42262306a36Sopenharmony_ci writew(HIMT_RECORD_DONE * 0x100 + n, pDAQ + DAQDS_wIntMsg); 42362306a36Sopenharmony_ci writew(n, pDAQ + DAQDS_wFlags); 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_msnd_playback = { 42862306a36Sopenharmony_ci .info = SNDRV_PCM_INFO_MMAP_IOMEM | 42962306a36Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 43062306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 43162306a36Sopenharmony_ci SNDRV_PCM_INFO_BATCH, 43262306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, 43362306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_48000, 43462306a36Sopenharmony_ci .rate_min = 8000, 43562306a36Sopenharmony_ci .rate_max = 48000, 43662306a36Sopenharmony_ci .channels_min = 1, 43762306a36Sopenharmony_ci .channels_max = 2, 43862306a36Sopenharmony_ci .buffer_bytes_max = 0x3000, 43962306a36Sopenharmony_ci .period_bytes_min = 0x40, 44062306a36Sopenharmony_ci .period_bytes_max = 0x1800, 44162306a36Sopenharmony_ci .periods_min = 2, 44262306a36Sopenharmony_ci .periods_max = 3, 44362306a36Sopenharmony_ci .fifo_size = 0, 44462306a36Sopenharmony_ci}; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_msnd_capture = { 44762306a36Sopenharmony_ci .info = SNDRV_PCM_INFO_MMAP_IOMEM | 44862306a36Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 44962306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 45062306a36Sopenharmony_ci SNDRV_PCM_INFO_BATCH, 45162306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, 45262306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_48000, 45362306a36Sopenharmony_ci .rate_min = 8000, 45462306a36Sopenharmony_ci .rate_max = 48000, 45562306a36Sopenharmony_ci .channels_min = 1, 45662306a36Sopenharmony_ci .channels_max = 2, 45762306a36Sopenharmony_ci .buffer_bytes_max = 0x3000, 45862306a36Sopenharmony_ci .period_bytes_min = 0x40, 45962306a36Sopenharmony_ci .period_bytes_max = 0x1800, 46062306a36Sopenharmony_ci .periods_min = 2, 46162306a36Sopenharmony_ci .periods_max = 3, 46262306a36Sopenharmony_ci .fifo_size = 0, 46362306a36Sopenharmony_ci}; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cistatic int snd_msnd_playback_open(struct snd_pcm_substream *substream) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 46962306a36Sopenharmony_ci struct snd_msnd *chip = snd_pcm_substream_chip(substream); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci set_bit(F_AUDIO_WRITE_INUSE, &chip->flags); 47262306a36Sopenharmony_ci clear_bit(F_WRITING, &chip->flags); 47362306a36Sopenharmony_ci snd_msnd_enable_irq(chip); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci runtime->dma_area = (__force void *)chip->mappedbase; 47662306a36Sopenharmony_ci runtime->dma_addr = chip->base; 47762306a36Sopenharmony_ci runtime->dma_bytes = 0x3000; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci chip->playback_substream = substream; 48062306a36Sopenharmony_ci runtime->hw = snd_msnd_playback; 48162306a36Sopenharmony_ci return 0; 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cistatic int snd_msnd_playback_close(struct snd_pcm_substream *substream) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci struct snd_msnd *chip = snd_pcm_substream_chip(substream); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci snd_msnd_disable_irq(chip); 48962306a36Sopenharmony_ci clear_bit(F_AUDIO_WRITE_INUSE, &chip->flags); 49062306a36Sopenharmony_ci return 0; 49162306a36Sopenharmony_ci} 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_cistatic int snd_msnd_playback_hw_params(struct snd_pcm_substream *substream, 49562306a36Sopenharmony_ci struct snd_pcm_hw_params *params) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci int i; 49862306a36Sopenharmony_ci struct snd_msnd *chip = snd_pcm_substream_chip(substream); 49962306a36Sopenharmony_ci void __iomem *pDAQ = chip->mappedbase + DAPQ_DATA_BUFF; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci chip->play_sample_size = snd_pcm_format_width(params_format(params)); 50262306a36Sopenharmony_ci chip->play_channels = params_channels(params); 50362306a36Sopenharmony_ci chip->play_sample_rate = params_rate(params); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci for (i = 0; i < 3; ++i, pDAQ += DAQDS__size) { 50662306a36Sopenharmony_ci writew(chip->play_sample_size, pDAQ + DAQDS_wSampleSize); 50762306a36Sopenharmony_ci writew(chip->play_channels, pDAQ + DAQDS_wChannels); 50862306a36Sopenharmony_ci writew(chip->play_sample_rate, pDAQ + DAQDS_wSampleRate); 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci /* dont do this here: 51162306a36Sopenharmony_ci * snd_msnd_calibrate_adc(chip->play_sample_rate); 51262306a36Sopenharmony_ci */ 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci return 0; 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_cistatic int snd_msnd_playback_prepare(struct snd_pcm_substream *substream) 51862306a36Sopenharmony_ci{ 51962306a36Sopenharmony_ci struct snd_msnd *chip = snd_pcm_substream_chip(substream); 52062306a36Sopenharmony_ci unsigned int pcm_size = snd_pcm_lib_buffer_bytes(substream); 52162306a36Sopenharmony_ci unsigned int pcm_count = snd_pcm_lib_period_bytes(substream); 52262306a36Sopenharmony_ci unsigned int pcm_periods = pcm_size / pcm_count; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci snd_msnd_play_reset_queue(chip, pcm_periods, pcm_count); 52562306a36Sopenharmony_ci chip->playDMAPos = 0; 52662306a36Sopenharmony_ci return 0; 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_cistatic int snd_msnd_playback_trigger(struct snd_pcm_substream *substream, 53062306a36Sopenharmony_ci int cmd) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci struct snd_msnd *chip = snd_pcm_substream_chip(substream); 53362306a36Sopenharmony_ci int result = 0; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci if (cmd == SNDRV_PCM_TRIGGER_START) { 53662306a36Sopenharmony_ci snd_printdd("snd_msnd_playback_trigger(START)\n"); 53762306a36Sopenharmony_ci chip->banksPlayed = 0; 53862306a36Sopenharmony_ci set_bit(F_WRITING, &chip->flags); 53962306a36Sopenharmony_ci snd_msnd_DAPQ(chip, 1); 54062306a36Sopenharmony_ci } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { 54162306a36Sopenharmony_ci snd_printdd("snd_msnd_playback_trigger(STop)\n"); 54262306a36Sopenharmony_ci /* interrupt diagnostic, comment this out later */ 54362306a36Sopenharmony_ci clear_bit(F_WRITING, &chip->flags); 54462306a36Sopenharmony_ci snd_msnd_send_dsp_cmd(chip, HDEX_PLAY_STOP); 54562306a36Sopenharmony_ci } else { 54662306a36Sopenharmony_ci snd_printd(KERN_ERR "snd_msnd_playback_trigger(?????)\n"); 54762306a36Sopenharmony_ci result = -EINVAL; 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci snd_printdd("snd_msnd_playback_trigger() ENDE\n"); 55162306a36Sopenharmony_ci return result; 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_cistatic snd_pcm_uframes_t 55562306a36Sopenharmony_cisnd_msnd_playback_pointer(struct snd_pcm_substream *substream) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci struct snd_msnd *chip = snd_pcm_substream_chip(substream); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci return bytes_to_frames(substream->runtime, chip->playDMAPos); 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_msnd_playback_ops = { 56462306a36Sopenharmony_ci .open = snd_msnd_playback_open, 56562306a36Sopenharmony_ci .close = snd_msnd_playback_close, 56662306a36Sopenharmony_ci .hw_params = snd_msnd_playback_hw_params, 56762306a36Sopenharmony_ci .prepare = snd_msnd_playback_prepare, 56862306a36Sopenharmony_ci .trigger = snd_msnd_playback_trigger, 56962306a36Sopenharmony_ci .pointer = snd_msnd_playback_pointer, 57062306a36Sopenharmony_ci .mmap = snd_pcm_lib_mmap_iomem, 57162306a36Sopenharmony_ci}; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_cistatic int snd_msnd_capture_open(struct snd_pcm_substream *substream) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 57662306a36Sopenharmony_ci struct snd_msnd *chip = snd_pcm_substream_chip(substream); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci set_bit(F_AUDIO_READ_INUSE, &chip->flags); 57962306a36Sopenharmony_ci snd_msnd_enable_irq(chip); 58062306a36Sopenharmony_ci runtime->dma_area = (__force void *)chip->mappedbase + 0x3000; 58162306a36Sopenharmony_ci runtime->dma_addr = chip->base + 0x3000; 58262306a36Sopenharmony_ci runtime->dma_bytes = 0x3000; 58362306a36Sopenharmony_ci memset(runtime->dma_area, 0, runtime->dma_bytes); 58462306a36Sopenharmony_ci chip->capture_substream = substream; 58562306a36Sopenharmony_ci runtime->hw = snd_msnd_capture; 58662306a36Sopenharmony_ci return 0; 58762306a36Sopenharmony_ci} 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_cistatic int snd_msnd_capture_close(struct snd_pcm_substream *substream) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci struct snd_msnd *chip = snd_pcm_substream_chip(substream); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci snd_msnd_disable_irq(chip); 59462306a36Sopenharmony_ci clear_bit(F_AUDIO_READ_INUSE, &chip->flags); 59562306a36Sopenharmony_ci return 0; 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_cistatic int snd_msnd_capture_prepare(struct snd_pcm_substream *substream) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci struct snd_msnd *chip = snd_pcm_substream_chip(substream); 60162306a36Sopenharmony_ci unsigned int pcm_size = snd_pcm_lib_buffer_bytes(substream); 60262306a36Sopenharmony_ci unsigned int pcm_count = snd_pcm_lib_period_bytes(substream); 60362306a36Sopenharmony_ci unsigned int pcm_periods = pcm_size / pcm_count; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci snd_msnd_capture_reset_queue(chip, pcm_periods, pcm_count); 60662306a36Sopenharmony_ci chip->captureDMAPos = 0; 60762306a36Sopenharmony_ci return 0; 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cistatic int snd_msnd_capture_trigger(struct snd_pcm_substream *substream, 61162306a36Sopenharmony_ci int cmd) 61262306a36Sopenharmony_ci{ 61362306a36Sopenharmony_ci struct snd_msnd *chip = snd_pcm_substream_chip(substream); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (cmd == SNDRV_PCM_TRIGGER_START) { 61662306a36Sopenharmony_ci chip->last_recbank = -1; 61762306a36Sopenharmony_ci set_bit(F_READING, &chip->flags); 61862306a36Sopenharmony_ci if (snd_msnd_send_dsp_cmd(chip, HDEX_RECORD_START) == 0) 61962306a36Sopenharmony_ci return 0; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci clear_bit(F_READING, &chip->flags); 62262306a36Sopenharmony_ci } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { 62362306a36Sopenharmony_ci clear_bit(F_READING, &chip->flags); 62462306a36Sopenharmony_ci snd_msnd_send_dsp_cmd(chip, HDEX_RECORD_STOP); 62562306a36Sopenharmony_ci return 0; 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci return -EINVAL; 62862306a36Sopenharmony_ci} 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_cistatic snd_pcm_uframes_t 63262306a36Sopenharmony_cisnd_msnd_capture_pointer(struct snd_pcm_substream *substream) 63362306a36Sopenharmony_ci{ 63462306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 63562306a36Sopenharmony_ci struct snd_msnd *chip = snd_pcm_substream_chip(substream); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci return bytes_to_frames(runtime, chip->captureDMAPos); 63862306a36Sopenharmony_ci} 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_cistatic int snd_msnd_capture_hw_params(struct snd_pcm_substream *substream, 64262306a36Sopenharmony_ci struct snd_pcm_hw_params *params) 64362306a36Sopenharmony_ci{ 64462306a36Sopenharmony_ci int i; 64562306a36Sopenharmony_ci struct snd_msnd *chip = snd_pcm_substream_chip(substream); 64662306a36Sopenharmony_ci void __iomem *pDAQ = chip->mappedbase + DARQ_DATA_BUFF; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci chip->capture_sample_size = snd_pcm_format_width(params_format(params)); 64962306a36Sopenharmony_ci chip->capture_channels = params_channels(params); 65062306a36Sopenharmony_ci chip->capture_sample_rate = params_rate(params); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci for (i = 0; i < 3; ++i, pDAQ += DAQDS__size) { 65362306a36Sopenharmony_ci writew(chip->capture_sample_size, pDAQ + DAQDS_wSampleSize); 65462306a36Sopenharmony_ci writew(chip->capture_channels, pDAQ + DAQDS_wChannels); 65562306a36Sopenharmony_ci writew(chip->capture_sample_rate, pDAQ + DAQDS_wSampleRate); 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci return 0; 65862306a36Sopenharmony_ci} 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_msnd_capture_ops = { 66262306a36Sopenharmony_ci .open = snd_msnd_capture_open, 66362306a36Sopenharmony_ci .close = snd_msnd_capture_close, 66462306a36Sopenharmony_ci .hw_params = snd_msnd_capture_hw_params, 66562306a36Sopenharmony_ci .prepare = snd_msnd_capture_prepare, 66662306a36Sopenharmony_ci .trigger = snd_msnd_capture_trigger, 66762306a36Sopenharmony_ci .pointer = snd_msnd_capture_pointer, 66862306a36Sopenharmony_ci .mmap = snd_pcm_lib_mmap_iomem, 66962306a36Sopenharmony_ci}; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ciint snd_msnd_pcm(struct snd_card *card, int device) 67362306a36Sopenharmony_ci{ 67462306a36Sopenharmony_ci struct snd_msnd *chip = card->private_data; 67562306a36Sopenharmony_ci struct snd_pcm *pcm; 67662306a36Sopenharmony_ci int err; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci err = snd_pcm_new(card, "MSNDPINNACLE", device, 1, 1, &pcm); 67962306a36Sopenharmony_ci if (err < 0) 68062306a36Sopenharmony_ci return err; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_msnd_playback_ops); 68362306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_msnd_capture_ops); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci pcm->private_data = chip; 68662306a36Sopenharmony_ci strcpy(pcm->name, "Hurricane"); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci return 0; 68962306a36Sopenharmony_ci} 69062306a36Sopenharmony_ciEXPORT_SYMBOL(snd_msnd_pcm); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ciMODULE_DESCRIPTION("Common routines for Turtle Beach Multisound drivers"); 69362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 69462306a36Sopenharmony_ci 695