18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
48c2ecf20Sopenharmony_ci *                   Uros Bizjak <uros@kss-loka.si>
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci *  Routines for control of 8-bit SoundBlaster cards and clones
78c2ecf20Sopenharmony_ci *  Please note: I don't have access to old SB8 soundcards.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * --
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * Thu Apr 29 20:36:17 BST 1999 George David Morrison <gdm@gedamo.demon.co.uk>
128c2ecf20Sopenharmony_ci *   DSP can't respond to commands whilst in "high speed" mode. Caused
138c2ecf20Sopenharmony_ci *   glitching during playback. Fixed.
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * Wed Jul 12 22:02:55 CEST 2000 Uros Bizjak <uros@kss-loka.si>
168c2ecf20Sopenharmony_ci *   Cleaned up and rewrote lowlevel routines.
178c2ecf20Sopenharmony_ci */
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <linux/io.h>
208c2ecf20Sopenharmony_ci#include <asm/dma.h>
218c2ecf20Sopenharmony_ci#include <linux/init.h>
228c2ecf20Sopenharmony_ci#include <linux/time.h>
238c2ecf20Sopenharmony_ci#include <linux/module.h>
248c2ecf20Sopenharmony_ci#include <sound/core.h>
258c2ecf20Sopenharmony_ci#include <sound/sb.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Uros Bizjak <uros@kss-loka.si>");
288c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Routines for control of 8-bit SoundBlaster cards and clones");
298c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define SB8_CLOCK	1000000
328c2ecf20Sopenharmony_ci#define SB8_DEN(v)	((SB8_CLOCK + (v) / 2) / (v))
338c2ecf20Sopenharmony_ci#define SB8_RATE(v)	(SB8_CLOCK / SB8_DEN(v))
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic const struct snd_ratnum clock = {
368c2ecf20Sopenharmony_ci	.num = SB8_CLOCK,
378c2ecf20Sopenharmony_ci	.den_min = 1,
388c2ecf20Sopenharmony_ci	.den_max = 256,
398c2ecf20Sopenharmony_ci	.den_step = 1,
408c2ecf20Sopenharmony_ci};
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic const struct snd_pcm_hw_constraint_ratnums hw_constraints_clock = {
438c2ecf20Sopenharmony_ci	.nrats = 1,
448c2ecf20Sopenharmony_ci	.rats = &clock,
458c2ecf20Sopenharmony_ci};
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic const struct snd_ratnum stereo_clocks[] = {
488c2ecf20Sopenharmony_ci	{
498c2ecf20Sopenharmony_ci		.num = SB8_CLOCK,
508c2ecf20Sopenharmony_ci		.den_min = SB8_DEN(22050),
518c2ecf20Sopenharmony_ci		.den_max = SB8_DEN(22050),
528c2ecf20Sopenharmony_ci		.den_step = 1,
538c2ecf20Sopenharmony_ci	},
548c2ecf20Sopenharmony_ci	{
558c2ecf20Sopenharmony_ci		.num = SB8_CLOCK,
568c2ecf20Sopenharmony_ci		.den_min = SB8_DEN(11025),
578c2ecf20Sopenharmony_ci		.den_max = SB8_DEN(11025),
588c2ecf20Sopenharmony_ci		.den_step = 1,
598c2ecf20Sopenharmony_ci	}
608c2ecf20Sopenharmony_ci};
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic int snd_sb8_hw_constraint_rate_channels(struct snd_pcm_hw_params *params,
638c2ecf20Sopenharmony_ci					       struct snd_pcm_hw_rule *rule)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
668c2ecf20Sopenharmony_ci	if (c->min > 1) {
678c2ecf20Sopenharmony_ci	  	unsigned int num = 0, den = 0;
688c2ecf20Sopenharmony_ci		int err = snd_interval_ratnum(hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE),
698c2ecf20Sopenharmony_ci					  2, stereo_clocks, &num, &den);
708c2ecf20Sopenharmony_ci		if (err >= 0 && den) {
718c2ecf20Sopenharmony_ci			params->rate_num = num;
728c2ecf20Sopenharmony_ci			params->rate_den = den;
738c2ecf20Sopenharmony_ci		}
748c2ecf20Sopenharmony_ci		return err;
758c2ecf20Sopenharmony_ci	}
768c2ecf20Sopenharmony_ci	return 0;
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic int snd_sb8_hw_constraint_channels_rate(struct snd_pcm_hw_params *params,
808c2ecf20Sopenharmony_ci					       struct snd_pcm_hw_rule *rule)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
838c2ecf20Sopenharmony_ci	if (r->min > SB8_RATE(22050) || r->max <= SB8_RATE(11025)) {
848c2ecf20Sopenharmony_ci		struct snd_interval t = { .min = 1, .max = 1 };
858c2ecf20Sopenharmony_ci		return snd_interval_refine(hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS), &t);
868c2ecf20Sopenharmony_ci	}
878c2ecf20Sopenharmony_ci	return 0;
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic int snd_sb8_playback_prepare(struct snd_pcm_substream *substream)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	unsigned long flags;
938c2ecf20Sopenharmony_ci	struct snd_sb *chip = snd_pcm_substream_chip(substream);
948c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
958c2ecf20Sopenharmony_ci	unsigned int mixreg, rate, size, count;
968c2ecf20Sopenharmony_ci	unsigned char format;
978c2ecf20Sopenharmony_ci	unsigned char stereo = runtime->channels > 1;
988c2ecf20Sopenharmony_ci	int dma;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	rate = runtime->rate;
1018c2ecf20Sopenharmony_ci	switch (chip->hardware) {
1028c2ecf20Sopenharmony_ci	case SB_HW_JAZZ16:
1038c2ecf20Sopenharmony_ci		if (runtime->format == SNDRV_PCM_FORMAT_S16_LE) {
1048c2ecf20Sopenharmony_ci			if (chip->mode & SB_MODE_CAPTURE_16)
1058c2ecf20Sopenharmony_ci				return -EBUSY;
1068c2ecf20Sopenharmony_ci			else
1078c2ecf20Sopenharmony_ci				chip->mode |= SB_MODE_PLAYBACK_16;
1088c2ecf20Sopenharmony_ci		}
1098c2ecf20Sopenharmony_ci		chip->playback_format = SB_DSP_LO_OUTPUT_AUTO;
1108c2ecf20Sopenharmony_ci		break;
1118c2ecf20Sopenharmony_ci	case SB_HW_PRO:
1128c2ecf20Sopenharmony_ci		if (runtime->channels > 1) {
1138c2ecf20Sopenharmony_ci			if (snd_BUG_ON(rate != SB8_RATE(11025) &&
1148c2ecf20Sopenharmony_ci				       rate != SB8_RATE(22050)))
1158c2ecf20Sopenharmony_ci				return -EINVAL;
1168c2ecf20Sopenharmony_ci			chip->playback_format = SB_DSP_HI_OUTPUT_AUTO;
1178c2ecf20Sopenharmony_ci			break;
1188c2ecf20Sopenharmony_ci		}
1198c2ecf20Sopenharmony_ci		fallthrough;
1208c2ecf20Sopenharmony_ci	case SB_HW_201:
1218c2ecf20Sopenharmony_ci		if (rate > 23000) {
1228c2ecf20Sopenharmony_ci			chip->playback_format = SB_DSP_HI_OUTPUT_AUTO;
1238c2ecf20Sopenharmony_ci			break;
1248c2ecf20Sopenharmony_ci		}
1258c2ecf20Sopenharmony_ci		fallthrough;
1268c2ecf20Sopenharmony_ci	case SB_HW_20:
1278c2ecf20Sopenharmony_ci		chip->playback_format = SB_DSP_LO_OUTPUT_AUTO;
1288c2ecf20Sopenharmony_ci		break;
1298c2ecf20Sopenharmony_ci	case SB_HW_10:
1308c2ecf20Sopenharmony_ci		chip->playback_format = SB_DSP_OUTPUT;
1318c2ecf20Sopenharmony_ci		break;
1328c2ecf20Sopenharmony_ci	default:
1338c2ecf20Sopenharmony_ci		return -EINVAL;
1348c2ecf20Sopenharmony_ci	}
1358c2ecf20Sopenharmony_ci	if (chip->mode & SB_MODE_PLAYBACK_16) {
1368c2ecf20Sopenharmony_ci		format = stereo ? SB_DSP_STEREO_16BIT : SB_DSP_MONO_16BIT;
1378c2ecf20Sopenharmony_ci		dma = chip->dma16;
1388c2ecf20Sopenharmony_ci	} else {
1398c2ecf20Sopenharmony_ci		format = stereo ? SB_DSP_STEREO_8BIT : SB_DSP_MONO_8BIT;
1408c2ecf20Sopenharmony_ci		chip->mode |= SB_MODE_PLAYBACK_8;
1418c2ecf20Sopenharmony_ci		dma = chip->dma8;
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci	size = chip->p_dma_size = snd_pcm_lib_buffer_bytes(substream);
1448c2ecf20Sopenharmony_ci	count = chip->p_period_size = snd_pcm_lib_period_bytes(substream);
1458c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->reg_lock, flags);
1468c2ecf20Sopenharmony_ci	snd_sbdsp_command(chip, SB_DSP_SPEAKER_ON);
1478c2ecf20Sopenharmony_ci	if (chip->hardware == SB_HW_JAZZ16)
1488c2ecf20Sopenharmony_ci		snd_sbdsp_command(chip, format);
1498c2ecf20Sopenharmony_ci	else if (stereo) {
1508c2ecf20Sopenharmony_ci		/* set playback stereo mode */
1518c2ecf20Sopenharmony_ci		spin_lock(&chip->mixer_lock);
1528c2ecf20Sopenharmony_ci		mixreg = snd_sbmixer_read(chip, SB_DSP_STEREO_SW);
1538c2ecf20Sopenharmony_ci		snd_sbmixer_write(chip, SB_DSP_STEREO_SW, mixreg | 0x02);
1548c2ecf20Sopenharmony_ci		spin_unlock(&chip->mixer_lock);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci		/* Soundblaster hardware programming reference guide, 3-23 */
1578c2ecf20Sopenharmony_ci		snd_sbdsp_command(chip, SB_DSP_DMA8_EXIT);
1588c2ecf20Sopenharmony_ci		runtime->dma_area[0] = 0x80;
1598c2ecf20Sopenharmony_ci		snd_dma_program(dma, runtime->dma_addr, 1, DMA_MODE_WRITE);
1608c2ecf20Sopenharmony_ci		/* force interrupt */
1618c2ecf20Sopenharmony_ci		snd_sbdsp_command(chip, SB_DSP_OUTPUT);
1628c2ecf20Sopenharmony_ci		snd_sbdsp_command(chip, 0);
1638c2ecf20Sopenharmony_ci		snd_sbdsp_command(chip, 0);
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci	snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE);
1668c2ecf20Sopenharmony_ci	if (stereo) {
1678c2ecf20Sopenharmony_ci		snd_sbdsp_command(chip, 256 - runtime->rate_den / 2);
1688c2ecf20Sopenharmony_ci		spin_lock(&chip->mixer_lock);
1698c2ecf20Sopenharmony_ci		/* save output filter status and turn it off */
1708c2ecf20Sopenharmony_ci		mixreg = snd_sbmixer_read(chip, SB_DSP_PLAYBACK_FILT);
1718c2ecf20Sopenharmony_ci		snd_sbmixer_write(chip, SB_DSP_PLAYBACK_FILT, mixreg | 0x20);
1728c2ecf20Sopenharmony_ci		spin_unlock(&chip->mixer_lock);
1738c2ecf20Sopenharmony_ci		/* just use force_mode16 for temporary storate... */
1748c2ecf20Sopenharmony_ci		chip->force_mode16 = mixreg;
1758c2ecf20Sopenharmony_ci	} else {
1768c2ecf20Sopenharmony_ci		snd_sbdsp_command(chip, 256 - runtime->rate_den);
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci	if (chip->playback_format != SB_DSP_OUTPUT) {
1798c2ecf20Sopenharmony_ci		if (chip->mode & SB_MODE_PLAYBACK_16)
1808c2ecf20Sopenharmony_ci			count /= 2;
1818c2ecf20Sopenharmony_ci		count--;
1828c2ecf20Sopenharmony_ci		snd_sbdsp_command(chip, SB_DSP_BLOCK_SIZE);
1838c2ecf20Sopenharmony_ci		snd_sbdsp_command(chip, count & 0xff);
1848c2ecf20Sopenharmony_ci		snd_sbdsp_command(chip, count >> 8);
1858c2ecf20Sopenharmony_ci	}
1868c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->reg_lock, flags);
1878c2ecf20Sopenharmony_ci	snd_dma_program(dma, runtime->dma_addr,
1888c2ecf20Sopenharmony_ci			size, DMA_MODE_WRITE | DMA_AUTOINIT);
1898c2ecf20Sopenharmony_ci	return 0;
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic int snd_sb8_playback_trigger(struct snd_pcm_substream *substream,
1938c2ecf20Sopenharmony_ci				    int cmd)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	unsigned long flags;
1968c2ecf20Sopenharmony_ci	struct snd_sb *chip = snd_pcm_substream_chip(substream);
1978c2ecf20Sopenharmony_ci	unsigned int count;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->reg_lock, flags);
2008c2ecf20Sopenharmony_ci	switch (cmd) {
2018c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
2028c2ecf20Sopenharmony_ci		snd_sbdsp_command(chip, chip->playback_format);
2038c2ecf20Sopenharmony_ci		if (chip->playback_format == SB_DSP_OUTPUT) {
2048c2ecf20Sopenharmony_ci			count = chip->p_period_size - 1;
2058c2ecf20Sopenharmony_ci			snd_sbdsp_command(chip, count & 0xff);
2068c2ecf20Sopenharmony_ci			snd_sbdsp_command(chip, count >> 8);
2078c2ecf20Sopenharmony_ci		}
2088c2ecf20Sopenharmony_ci		break;
2098c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
2108c2ecf20Sopenharmony_ci		if (chip->playback_format == SB_DSP_HI_OUTPUT_AUTO) {
2118c2ecf20Sopenharmony_ci			struct snd_pcm_runtime *runtime = substream->runtime;
2128c2ecf20Sopenharmony_ci			snd_sbdsp_reset(chip);
2138c2ecf20Sopenharmony_ci			if (runtime->channels > 1) {
2148c2ecf20Sopenharmony_ci				spin_lock(&chip->mixer_lock);
2158c2ecf20Sopenharmony_ci				/* restore output filter and set hardware to mono mode */
2168c2ecf20Sopenharmony_ci				snd_sbmixer_write(chip, SB_DSP_STEREO_SW, chip->force_mode16 & ~0x02);
2178c2ecf20Sopenharmony_ci				spin_unlock(&chip->mixer_lock);
2188c2ecf20Sopenharmony_ci			}
2198c2ecf20Sopenharmony_ci		} else {
2208c2ecf20Sopenharmony_ci			snd_sbdsp_command(chip, SB_DSP_DMA8_OFF);
2218c2ecf20Sopenharmony_ci		}
2228c2ecf20Sopenharmony_ci		snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF);
2238c2ecf20Sopenharmony_ci	}
2248c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->reg_lock, flags);
2258c2ecf20Sopenharmony_ci	return 0;
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistatic int snd_sb8_capture_prepare(struct snd_pcm_substream *substream)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	unsigned long flags;
2318c2ecf20Sopenharmony_ci	struct snd_sb *chip = snd_pcm_substream_chip(substream);
2328c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
2338c2ecf20Sopenharmony_ci	unsigned int mixreg, rate, size, count;
2348c2ecf20Sopenharmony_ci	unsigned char format;
2358c2ecf20Sopenharmony_ci	unsigned char stereo = runtime->channels > 1;
2368c2ecf20Sopenharmony_ci	int dma;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	rate = runtime->rate;
2398c2ecf20Sopenharmony_ci	switch (chip->hardware) {
2408c2ecf20Sopenharmony_ci	case SB_HW_JAZZ16:
2418c2ecf20Sopenharmony_ci		if (runtime->format == SNDRV_PCM_FORMAT_S16_LE) {
2428c2ecf20Sopenharmony_ci			if (chip->mode & SB_MODE_PLAYBACK_16)
2438c2ecf20Sopenharmony_ci				return -EBUSY;
2448c2ecf20Sopenharmony_ci			else
2458c2ecf20Sopenharmony_ci				chip->mode |= SB_MODE_CAPTURE_16;
2468c2ecf20Sopenharmony_ci		}
2478c2ecf20Sopenharmony_ci		chip->capture_format = SB_DSP_LO_INPUT_AUTO;
2488c2ecf20Sopenharmony_ci		break;
2498c2ecf20Sopenharmony_ci	case SB_HW_PRO:
2508c2ecf20Sopenharmony_ci		if (runtime->channels > 1) {
2518c2ecf20Sopenharmony_ci			if (snd_BUG_ON(rate != SB8_RATE(11025) &&
2528c2ecf20Sopenharmony_ci				       rate != SB8_RATE(22050)))
2538c2ecf20Sopenharmony_ci				return -EINVAL;
2548c2ecf20Sopenharmony_ci			chip->capture_format = SB_DSP_HI_INPUT_AUTO;
2558c2ecf20Sopenharmony_ci			break;
2568c2ecf20Sopenharmony_ci		}
2578c2ecf20Sopenharmony_ci		chip->capture_format = (rate > 23000) ? SB_DSP_HI_INPUT_AUTO : SB_DSP_LO_INPUT_AUTO;
2588c2ecf20Sopenharmony_ci		break;
2598c2ecf20Sopenharmony_ci	case SB_HW_201:
2608c2ecf20Sopenharmony_ci		if (rate > 13000) {
2618c2ecf20Sopenharmony_ci			chip->capture_format = SB_DSP_HI_INPUT_AUTO;
2628c2ecf20Sopenharmony_ci			break;
2638c2ecf20Sopenharmony_ci		}
2648c2ecf20Sopenharmony_ci		fallthrough;
2658c2ecf20Sopenharmony_ci	case SB_HW_20:
2668c2ecf20Sopenharmony_ci		chip->capture_format = SB_DSP_LO_INPUT_AUTO;
2678c2ecf20Sopenharmony_ci		break;
2688c2ecf20Sopenharmony_ci	case SB_HW_10:
2698c2ecf20Sopenharmony_ci		chip->capture_format = SB_DSP_INPUT;
2708c2ecf20Sopenharmony_ci		break;
2718c2ecf20Sopenharmony_ci	default:
2728c2ecf20Sopenharmony_ci		return -EINVAL;
2738c2ecf20Sopenharmony_ci	}
2748c2ecf20Sopenharmony_ci	if (chip->mode & SB_MODE_CAPTURE_16) {
2758c2ecf20Sopenharmony_ci		format = stereo ? SB_DSP_STEREO_16BIT : SB_DSP_MONO_16BIT;
2768c2ecf20Sopenharmony_ci		dma = chip->dma16;
2778c2ecf20Sopenharmony_ci	} else {
2788c2ecf20Sopenharmony_ci		format = stereo ? SB_DSP_STEREO_8BIT : SB_DSP_MONO_8BIT;
2798c2ecf20Sopenharmony_ci		chip->mode |= SB_MODE_CAPTURE_8;
2808c2ecf20Sopenharmony_ci		dma = chip->dma8;
2818c2ecf20Sopenharmony_ci	}
2828c2ecf20Sopenharmony_ci	size = chip->c_dma_size = snd_pcm_lib_buffer_bytes(substream);
2838c2ecf20Sopenharmony_ci	count = chip->c_period_size = snd_pcm_lib_period_bytes(substream);
2848c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->reg_lock, flags);
2858c2ecf20Sopenharmony_ci	snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF);
2868c2ecf20Sopenharmony_ci	if (chip->hardware == SB_HW_JAZZ16)
2878c2ecf20Sopenharmony_ci		snd_sbdsp_command(chip, format);
2888c2ecf20Sopenharmony_ci	else if (stereo)
2898c2ecf20Sopenharmony_ci		snd_sbdsp_command(chip, SB_DSP_STEREO_8BIT);
2908c2ecf20Sopenharmony_ci	snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE);
2918c2ecf20Sopenharmony_ci	if (stereo) {
2928c2ecf20Sopenharmony_ci		snd_sbdsp_command(chip, 256 - runtime->rate_den / 2);
2938c2ecf20Sopenharmony_ci		spin_lock(&chip->mixer_lock);
2948c2ecf20Sopenharmony_ci		/* save input filter status and turn it off */
2958c2ecf20Sopenharmony_ci		mixreg = snd_sbmixer_read(chip, SB_DSP_CAPTURE_FILT);
2968c2ecf20Sopenharmony_ci		snd_sbmixer_write(chip, SB_DSP_CAPTURE_FILT, mixreg | 0x20);
2978c2ecf20Sopenharmony_ci		spin_unlock(&chip->mixer_lock);
2988c2ecf20Sopenharmony_ci		/* just use force_mode16 for temporary storate... */
2998c2ecf20Sopenharmony_ci		chip->force_mode16 = mixreg;
3008c2ecf20Sopenharmony_ci	} else {
3018c2ecf20Sopenharmony_ci		snd_sbdsp_command(chip, 256 - runtime->rate_den);
3028c2ecf20Sopenharmony_ci	}
3038c2ecf20Sopenharmony_ci	if (chip->capture_format != SB_DSP_INPUT) {
3048c2ecf20Sopenharmony_ci		if (chip->mode & SB_MODE_PLAYBACK_16)
3058c2ecf20Sopenharmony_ci			count /= 2;
3068c2ecf20Sopenharmony_ci		count--;
3078c2ecf20Sopenharmony_ci		snd_sbdsp_command(chip, SB_DSP_BLOCK_SIZE);
3088c2ecf20Sopenharmony_ci		snd_sbdsp_command(chip, count & 0xff);
3098c2ecf20Sopenharmony_ci		snd_sbdsp_command(chip, count >> 8);
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->reg_lock, flags);
3128c2ecf20Sopenharmony_ci	snd_dma_program(dma, runtime->dma_addr,
3138c2ecf20Sopenharmony_ci			size, DMA_MODE_READ | DMA_AUTOINIT);
3148c2ecf20Sopenharmony_ci	return 0;
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_cistatic int snd_sb8_capture_trigger(struct snd_pcm_substream *substream,
3188c2ecf20Sopenharmony_ci				   int cmd)
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci	unsigned long flags;
3218c2ecf20Sopenharmony_ci	struct snd_sb *chip = snd_pcm_substream_chip(substream);
3228c2ecf20Sopenharmony_ci	unsigned int count;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->reg_lock, flags);
3258c2ecf20Sopenharmony_ci	switch (cmd) {
3268c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
3278c2ecf20Sopenharmony_ci		snd_sbdsp_command(chip, chip->capture_format);
3288c2ecf20Sopenharmony_ci		if (chip->capture_format == SB_DSP_INPUT) {
3298c2ecf20Sopenharmony_ci			count = chip->c_period_size - 1;
3308c2ecf20Sopenharmony_ci			snd_sbdsp_command(chip, count & 0xff);
3318c2ecf20Sopenharmony_ci			snd_sbdsp_command(chip, count >> 8);
3328c2ecf20Sopenharmony_ci		}
3338c2ecf20Sopenharmony_ci		break;
3348c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
3358c2ecf20Sopenharmony_ci		if (chip->capture_format == SB_DSP_HI_INPUT_AUTO) {
3368c2ecf20Sopenharmony_ci			struct snd_pcm_runtime *runtime = substream->runtime;
3378c2ecf20Sopenharmony_ci			snd_sbdsp_reset(chip);
3388c2ecf20Sopenharmony_ci			if (runtime->channels > 1) {
3398c2ecf20Sopenharmony_ci				/* restore input filter status */
3408c2ecf20Sopenharmony_ci				spin_lock(&chip->mixer_lock);
3418c2ecf20Sopenharmony_ci				snd_sbmixer_write(chip, SB_DSP_CAPTURE_FILT, chip->force_mode16);
3428c2ecf20Sopenharmony_ci				spin_unlock(&chip->mixer_lock);
3438c2ecf20Sopenharmony_ci				/* set hardware to mono mode */
3448c2ecf20Sopenharmony_ci				snd_sbdsp_command(chip, SB_DSP_MONO_8BIT);
3458c2ecf20Sopenharmony_ci			}
3468c2ecf20Sopenharmony_ci		} else {
3478c2ecf20Sopenharmony_ci			snd_sbdsp_command(chip, SB_DSP_DMA8_OFF);
3488c2ecf20Sopenharmony_ci		}
3498c2ecf20Sopenharmony_ci		snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF);
3508c2ecf20Sopenharmony_ci	}
3518c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->reg_lock, flags);
3528c2ecf20Sopenharmony_ci	return 0;
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ciirqreturn_t snd_sb8dsp_interrupt(struct snd_sb *chip)
3568c2ecf20Sopenharmony_ci{
3578c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	snd_sb_ack_8bit(chip);
3608c2ecf20Sopenharmony_ci	switch (chip->mode) {
3618c2ecf20Sopenharmony_ci	case SB_MODE_PLAYBACK_16:	/* ok.. playback is active */
3628c2ecf20Sopenharmony_ci		if (chip->hardware != SB_HW_JAZZ16)
3638c2ecf20Sopenharmony_ci			break;
3648c2ecf20Sopenharmony_ci		fallthrough;
3658c2ecf20Sopenharmony_ci	case SB_MODE_PLAYBACK_8:
3668c2ecf20Sopenharmony_ci		substream = chip->playback_substream;
3678c2ecf20Sopenharmony_ci		if (chip->playback_format == SB_DSP_OUTPUT)
3688c2ecf20Sopenharmony_ci		    	snd_sb8_playback_trigger(substream, SNDRV_PCM_TRIGGER_START);
3698c2ecf20Sopenharmony_ci		snd_pcm_period_elapsed(substream);
3708c2ecf20Sopenharmony_ci		break;
3718c2ecf20Sopenharmony_ci	case SB_MODE_CAPTURE_16:
3728c2ecf20Sopenharmony_ci		if (chip->hardware != SB_HW_JAZZ16)
3738c2ecf20Sopenharmony_ci			break;
3748c2ecf20Sopenharmony_ci		fallthrough;
3758c2ecf20Sopenharmony_ci	case SB_MODE_CAPTURE_8:
3768c2ecf20Sopenharmony_ci		substream = chip->capture_substream;
3778c2ecf20Sopenharmony_ci		if (chip->capture_format == SB_DSP_INPUT)
3788c2ecf20Sopenharmony_ci		    	snd_sb8_capture_trigger(substream, SNDRV_PCM_TRIGGER_START);
3798c2ecf20Sopenharmony_ci		snd_pcm_period_elapsed(substream);
3808c2ecf20Sopenharmony_ci		break;
3818c2ecf20Sopenharmony_ci	}
3828c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
3838c2ecf20Sopenharmony_ci}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_sb8_playback_pointer(struct snd_pcm_substream *substream)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	struct snd_sb *chip = snd_pcm_substream_chip(substream);
3888c2ecf20Sopenharmony_ci	size_t ptr;
3898c2ecf20Sopenharmony_ci	int dma;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	if (chip->mode & SB_MODE_PLAYBACK_8)
3928c2ecf20Sopenharmony_ci		dma = chip->dma8;
3938c2ecf20Sopenharmony_ci	else if (chip->mode & SB_MODE_PLAYBACK_16)
3948c2ecf20Sopenharmony_ci		dma = chip->dma16;
3958c2ecf20Sopenharmony_ci	else
3968c2ecf20Sopenharmony_ci		return 0;
3978c2ecf20Sopenharmony_ci	ptr = snd_dma_pointer(dma, chip->p_dma_size);
3988c2ecf20Sopenharmony_ci	return bytes_to_frames(substream->runtime, ptr);
3998c2ecf20Sopenharmony_ci}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_sb8_capture_pointer(struct snd_pcm_substream *substream)
4028c2ecf20Sopenharmony_ci{
4038c2ecf20Sopenharmony_ci	struct snd_sb *chip = snd_pcm_substream_chip(substream);
4048c2ecf20Sopenharmony_ci	size_t ptr;
4058c2ecf20Sopenharmony_ci	int dma;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	if (chip->mode & SB_MODE_CAPTURE_8)
4088c2ecf20Sopenharmony_ci		dma = chip->dma8;
4098c2ecf20Sopenharmony_ci	else if (chip->mode & SB_MODE_CAPTURE_16)
4108c2ecf20Sopenharmony_ci		dma = chip->dma16;
4118c2ecf20Sopenharmony_ci	else
4128c2ecf20Sopenharmony_ci		return 0;
4138c2ecf20Sopenharmony_ci	ptr = snd_dma_pointer(dma, chip->c_dma_size);
4148c2ecf20Sopenharmony_ci	return bytes_to_frames(substream->runtime, ptr);
4158c2ecf20Sopenharmony_ci}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci/*
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci */
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_sb8_playback =
4228c2ecf20Sopenharmony_ci{
4238c2ecf20Sopenharmony_ci	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
4248c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_MMAP_VALID),
4258c2ecf20Sopenharmony_ci	.formats =		 SNDRV_PCM_FMTBIT_U8,
4268c2ecf20Sopenharmony_ci	.rates =		(SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000 |
4278c2ecf20Sopenharmony_ci				 SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_22050),
4288c2ecf20Sopenharmony_ci	.rate_min =		4000,
4298c2ecf20Sopenharmony_ci	.rate_max =		23000,
4308c2ecf20Sopenharmony_ci	.channels_min =		1,
4318c2ecf20Sopenharmony_ci	.channels_max =		1,
4328c2ecf20Sopenharmony_ci	.buffer_bytes_max =	65536,
4338c2ecf20Sopenharmony_ci	.period_bytes_min =	64,
4348c2ecf20Sopenharmony_ci	.period_bytes_max =	65536,
4358c2ecf20Sopenharmony_ci	.periods_min =		1,
4368c2ecf20Sopenharmony_ci	.periods_max =		1024,
4378c2ecf20Sopenharmony_ci	.fifo_size =		0,
4388c2ecf20Sopenharmony_ci};
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_sb8_capture =
4418c2ecf20Sopenharmony_ci{
4428c2ecf20Sopenharmony_ci	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
4438c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_MMAP_VALID),
4448c2ecf20Sopenharmony_ci	.formats =		SNDRV_PCM_FMTBIT_U8,
4458c2ecf20Sopenharmony_ci	.rates =		(SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000 |
4468c2ecf20Sopenharmony_ci				 SNDRV_PCM_RATE_11025),
4478c2ecf20Sopenharmony_ci	.rate_min =		4000,
4488c2ecf20Sopenharmony_ci	.rate_max =		13000,
4498c2ecf20Sopenharmony_ci	.channels_min =		1,
4508c2ecf20Sopenharmony_ci	.channels_max =		1,
4518c2ecf20Sopenharmony_ci	.buffer_bytes_max =	65536,
4528c2ecf20Sopenharmony_ci	.period_bytes_min =	64,
4538c2ecf20Sopenharmony_ci	.period_bytes_max =	65536,
4548c2ecf20Sopenharmony_ci	.periods_min =		1,
4558c2ecf20Sopenharmony_ci	.periods_max =		1024,
4568c2ecf20Sopenharmony_ci	.fifo_size =		0,
4578c2ecf20Sopenharmony_ci};
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci/*
4608c2ecf20Sopenharmony_ci *
4618c2ecf20Sopenharmony_ci */
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_cistatic int snd_sb8_open(struct snd_pcm_substream *substream)
4648c2ecf20Sopenharmony_ci{
4658c2ecf20Sopenharmony_ci	struct snd_sb *chip = snd_pcm_substream_chip(substream);
4668c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
4678c2ecf20Sopenharmony_ci	unsigned long flags;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->open_lock, flags);
4708c2ecf20Sopenharmony_ci	if (chip->open) {
4718c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&chip->open_lock, flags);
4728c2ecf20Sopenharmony_ci		return -EAGAIN;
4738c2ecf20Sopenharmony_ci	}
4748c2ecf20Sopenharmony_ci	chip->open |= SB_OPEN_PCM;
4758c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->open_lock, flags);
4768c2ecf20Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
4778c2ecf20Sopenharmony_ci		chip->playback_substream = substream;
4788c2ecf20Sopenharmony_ci		runtime->hw = snd_sb8_playback;
4798c2ecf20Sopenharmony_ci	} else {
4808c2ecf20Sopenharmony_ci		chip->capture_substream = substream;
4818c2ecf20Sopenharmony_ci		runtime->hw = snd_sb8_capture;
4828c2ecf20Sopenharmony_ci	}
4838c2ecf20Sopenharmony_ci	switch (chip->hardware) {
4848c2ecf20Sopenharmony_ci	case SB_HW_JAZZ16:
4858c2ecf20Sopenharmony_ci		if (chip->dma16 == 5 || chip->dma16 == 7)
4868c2ecf20Sopenharmony_ci			runtime->hw.formats |= SNDRV_PCM_FMTBIT_S16_LE;
4878c2ecf20Sopenharmony_ci		runtime->hw.rates |= SNDRV_PCM_RATE_8000_48000;
4888c2ecf20Sopenharmony_ci		runtime->hw.rate_min = 4000;
4898c2ecf20Sopenharmony_ci		runtime->hw.rate_max = 50000;
4908c2ecf20Sopenharmony_ci		runtime->hw.channels_max = 2;
4918c2ecf20Sopenharmony_ci		break;
4928c2ecf20Sopenharmony_ci	case SB_HW_PRO:
4938c2ecf20Sopenharmony_ci		runtime->hw.rate_max = 44100;
4948c2ecf20Sopenharmony_ci		runtime->hw.channels_max = 2;
4958c2ecf20Sopenharmony_ci		snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
4968c2ecf20Sopenharmony_ci				    snd_sb8_hw_constraint_rate_channels, NULL,
4978c2ecf20Sopenharmony_ci				    SNDRV_PCM_HW_PARAM_CHANNELS,
4988c2ecf20Sopenharmony_ci				    SNDRV_PCM_HW_PARAM_RATE, -1);
4998c2ecf20Sopenharmony_ci		snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
5008c2ecf20Sopenharmony_ci				     snd_sb8_hw_constraint_channels_rate, NULL,
5018c2ecf20Sopenharmony_ci				     SNDRV_PCM_HW_PARAM_RATE, -1);
5028c2ecf20Sopenharmony_ci		break;
5038c2ecf20Sopenharmony_ci	case SB_HW_201:
5048c2ecf20Sopenharmony_ci		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
5058c2ecf20Sopenharmony_ci			runtime->hw.rate_max = 44100;
5068c2ecf20Sopenharmony_ci		} else {
5078c2ecf20Sopenharmony_ci			runtime->hw.rate_max = 15000;
5088c2ecf20Sopenharmony_ci		}
5098c2ecf20Sopenharmony_ci	default:
5108c2ecf20Sopenharmony_ci		break;
5118c2ecf20Sopenharmony_ci	}
5128c2ecf20Sopenharmony_ci	snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
5138c2ecf20Sopenharmony_ci				      &hw_constraints_clock);
5148c2ecf20Sopenharmony_ci	if (chip->dma8 > 3 || chip->dma16 >= 0) {
5158c2ecf20Sopenharmony_ci		snd_pcm_hw_constraint_step(runtime, 0,
5168c2ecf20Sopenharmony_ci					   SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 2);
5178c2ecf20Sopenharmony_ci		snd_pcm_hw_constraint_step(runtime, 0,
5188c2ecf20Sopenharmony_ci					   SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 2);
5198c2ecf20Sopenharmony_ci		runtime->hw.buffer_bytes_max = 128 * 1024 * 1024;
5208c2ecf20Sopenharmony_ci		runtime->hw.period_bytes_max = 128 * 1024 * 1024;
5218c2ecf20Sopenharmony_ci	}
5228c2ecf20Sopenharmony_ci	return 0;
5238c2ecf20Sopenharmony_ci}
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_cistatic int snd_sb8_close(struct snd_pcm_substream *substream)
5268c2ecf20Sopenharmony_ci{
5278c2ecf20Sopenharmony_ci	unsigned long flags;
5288c2ecf20Sopenharmony_ci	struct snd_sb *chip = snd_pcm_substream_chip(substream);
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	chip->playback_substream = NULL;
5318c2ecf20Sopenharmony_ci	chip->capture_substream = NULL;
5328c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->open_lock, flags);
5338c2ecf20Sopenharmony_ci	chip->open &= ~SB_OPEN_PCM;
5348c2ecf20Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
5358c2ecf20Sopenharmony_ci		chip->mode &= ~SB_MODE_PLAYBACK;
5368c2ecf20Sopenharmony_ci	else
5378c2ecf20Sopenharmony_ci		chip->mode &= ~SB_MODE_CAPTURE;
5388c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->open_lock, flags);
5398c2ecf20Sopenharmony_ci	return 0;
5408c2ecf20Sopenharmony_ci}
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci/*
5438c2ecf20Sopenharmony_ci *  Initialization part
5448c2ecf20Sopenharmony_ci */
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_sb8_playback_ops = {
5478c2ecf20Sopenharmony_ci	.open =			snd_sb8_open,
5488c2ecf20Sopenharmony_ci	.close =		snd_sb8_close,
5498c2ecf20Sopenharmony_ci	.prepare =		snd_sb8_playback_prepare,
5508c2ecf20Sopenharmony_ci	.trigger =		snd_sb8_playback_trigger,
5518c2ecf20Sopenharmony_ci	.pointer =		snd_sb8_playback_pointer,
5528c2ecf20Sopenharmony_ci};
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_sb8_capture_ops = {
5558c2ecf20Sopenharmony_ci	.open =			snd_sb8_open,
5568c2ecf20Sopenharmony_ci	.close =		snd_sb8_close,
5578c2ecf20Sopenharmony_ci	.prepare =		snd_sb8_capture_prepare,
5588c2ecf20Sopenharmony_ci	.trigger =		snd_sb8_capture_trigger,
5598c2ecf20Sopenharmony_ci	.pointer =		snd_sb8_capture_pointer,
5608c2ecf20Sopenharmony_ci};
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ciint snd_sb8dsp_pcm(struct snd_sb *chip, int device)
5638c2ecf20Sopenharmony_ci{
5648c2ecf20Sopenharmony_ci	struct snd_card *card = chip->card;
5658c2ecf20Sopenharmony_ci	struct snd_pcm *pcm;
5668c2ecf20Sopenharmony_ci	int err;
5678c2ecf20Sopenharmony_ci	size_t max_prealloc = 64 * 1024;
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	if ((err = snd_pcm_new(card, "SB8 DSP", device, 1, 1, &pcm)) < 0)
5708c2ecf20Sopenharmony_ci		return err;
5718c2ecf20Sopenharmony_ci	sprintf(pcm->name, "DSP v%i.%i", chip->version >> 8, chip->version & 0xff);
5728c2ecf20Sopenharmony_ci	pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
5738c2ecf20Sopenharmony_ci	pcm->private_data = chip;
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sb8_playback_ops);
5768c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sb8_capture_ops);
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	if (chip->dma8 > 3 || chip->dma16 >= 0)
5798c2ecf20Sopenharmony_ci		max_prealloc = 128 * 1024;
5808c2ecf20Sopenharmony_ci	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
5818c2ecf20Sopenharmony_ci				       card->dev, 64*1024, max_prealloc);
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	return 0;
5848c2ecf20Sopenharmony_ci}
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_sb8dsp_pcm);
5878c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_sb8dsp_interrupt);
5888c2ecf20Sopenharmony_ci  /* sb8_midi.c */
5898c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_sb8dsp_midi_interrupt);
5908c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_sb8dsp_midi);
591