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