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