162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
462306a36Sopenharmony_ci *                   Uros Bizjak <uros@kss-loka.si>
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *  Routines for control of 8-bit SoundBlaster cards and clones
762306a36Sopenharmony_ci *  Please note: I don't have access to old SB8 soundcards.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * --
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Thu Apr 29 20:36:17 BST 1999 George David Morrison <gdm@gedamo.demon.co.uk>
1262306a36Sopenharmony_ci *   DSP can't respond to commands whilst in "high speed" mode. Caused
1362306a36Sopenharmony_ci *   glitching during playback. Fixed.
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * Wed Jul 12 22:02:55 CEST 2000 Uros Bizjak <uros@kss-loka.si>
1662306a36Sopenharmony_ci *   Cleaned up and rewrote lowlevel routines.
1762306a36Sopenharmony_ci */
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <linux/io.h>
2062306a36Sopenharmony_ci#include <asm/dma.h>
2162306a36Sopenharmony_ci#include <linux/init.h>
2262306a36Sopenharmony_ci#include <linux/time.h>
2362306a36Sopenharmony_ci#include <linux/module.h>
2462306a36Sopenharmony_ci#include <sound/core.h>
2562306a36Sopenharmony_ci#include <sound/sb.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Uros Bizjak <uros@kss-loka.si>");
2862306a36Sopenharmony_ciMODULE_DESCRIPTION("Routines for control of 8-bit SoundBlaster cards and clones");
2962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define SB8_CLOCK	1000000
3262306a36Sopenharmony_ci#define SB8_DEN(v)	((SB8_CLOCK + (v) / 2) / (v))
3362306a36Sopenharmony_ci#define SB8_RATE(v)	(SB8_CLOCK / SB8_DEN(v))
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic const struct snd_ratnum clock = {
3662306a36Sopenharmony_ci	.num = SB8_CLOCK,
3762306a36Sopenharmony_ci	.den_min = 1,
3862306a36Sopenharmony_ci	.den_max = 256,
3962306a36Sopenharmony_ci	.den_step = 1,
4062306a36Sopenharmony_ci};
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic const struct snd_pcm_hw_constraint_ratnums hw_constraints_clock = {
4362306a36Sopenharmony_ci	.nrats = 1,
4462306a36Sopenharmony_ci	.rats = &clock,
4562306a36Sopenharmony_ci};
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic const struct snd_ratnum stereo_clocks[] = {
4862306a36Sopenharmony_ci	{
4962306a36Sopenharmony_ci		.num = SB8_CLOCK,
5062306a36Sopenharmony_ci		.den_min = SB8_DEN(22050),
5162306a36Sopenharmony_ci		.den_max = SB8_DEN(22050),
5262306a36Sopenharmony_ci		.den_step = 1,
5362306a36Sopenharmony_ci	},
5462306a36Sopenharmony_ci	{
5562306a36Sopenharmony_ci		.num = SB8_CLOCK,
5662306a36Sopenharmony_ci		.den_min = SB8_DEN(11025),
5762306a36Sopenharmony_ci		.den_max = SB8_DEN(11025),
5862306a36Sopenharmony_ci		.den_step = 1,
5962306a36Sopenharmony_ci	}
6062306a36Sopenharmony_ci};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic int snd_sb8_hw_constraint_rate_channels(struct snd_pcm_hw_params *params,
6362306a36Sopenharmony_ci					       struct snd_pcm_hw_rule *rule)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
6662306a36Sopenharmony_ci	if (c->min > 1) {
6762306a36Sopenharmony_ci	  	unsigned int num = 0, den = 0;
6862306a36Sopenharmony_ci		int err = snd_interval_ratnum(hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE),
6962306a36Sopenharmony_ci					  2, stereo_clocks, &num, &den);
7062306a36Sopenharmony_ci		if (err >= 0 && den) {
7162306a36Sopenharmony_ci			params->rate_num = num;
7262306a36Sopenharmony_ci			params->rate_den = den;
7362306a36Sopenharmony_ci		}
7462306a36Sopenharmony_ci		return err;
7562306a36Sopenharmony_ci	}
7662306a36Sopenharmony_ci	return 0;
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic int snd_sb8_hw_constraint_channels_rate(struct snd_pcm_hw_params *params,
8062306a36Sopenharmony_ci					       struct snd_pcm_hw_rule *rule)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
8362306a36Sopenharmony_ci	if (r->min > SB8_RATE(22050) || r->max <= SB8_RATE(11025)) {
8462306a36Sopenharmony_ci		struct snd_interval t = { .min = 1, .max = 1 };
8562306a36Sopenharmony_ci		return snd_interval_refine(hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS), &t);
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci	return 0;
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic int snd_sb8_playback_prepare(struct snd_pcm_substream *substream)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	unsigned long flags;
9362306a36Sopenharmony_ci	struct snd_sb *chip = snd_pcm_substream_chip(substream);
9462306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
9562306a36Sopenharmony_ci	unsigned int mixreg, rate, size, count;
9662306a36Sopenharmony_ci	unsigned char format;
9762306a36Sopenharmony_ci	unsigned char stereo = runtime->channels > 1;
9862306a36Sopenharmony_ci	int dma;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	rate = runtime->rate;
10162306a36Sopenharmony_ci	switch (chip->hardware) {
10262306a36Sopenharmony_ci	case SB_HW_JAZZ16:
10362306a36Sopenharmony_ci		if (runtime->format == SNDRV_PCM_FORMAT_S16_LE) {
10462306a36Sopenharmony_ci			if (chip->mode & SB_MODE_CAPTURE_16)
10562306a36Sopenharmony_ci				return -EBUSY;
10662306a36Sopenharmony_ci			else
10762306a36Sopenharmony_ci				chip->mode |= SB_MODE_PLAYBACK_16;
10862306a36Sopenharmony_ci		}
10962306a36Sopenharmony_ci		chip->playback_format = SB_DSP_LO_OUTPUT_AUTO;
11062306a36Sopenharmony_ci		break;
11162306a36Sopenharmony_ci	case SB_HW_PRO:
11262306a36Sopenharmony_ci		if (runtime->channels > 1) {
11362306a36Sopenharmony_ci			if (snd_BUG_ON(rate != SB8_RATE(11025) &&
11462306a36Sopenharmony_ci				       rate != SB8_RATE(22050)))
11562306a36Sopenharmony_ci				return -EINVAL;
11662306a36Sopenharmony_ci			chip->playback_format = SB_DSP_HI_OUTPUT_AUTO;
11762306a36Sopenharmony_ci			break;
11862306a36Sopenharmony_ci		}
11962306a36Sopenharmony_ci		fallthrough;
12062306a36Sopenharmony_ci	case SB_HW_201:
12162306a36Sopenharmony_ci		if (rate > 23000) {
12262306a36Sopenharmony_ci			chip->playback_format = SB_DSP_HI_OUTPUT_AUTO;
12362306a36Sopenharmony_ci			break;
12462306a36Sopenharmony_ci		}
12562306a36Sopenharmony_ci		fallthrough;
12662306a36Sopenharmony_ci	case SB_HW_20:
12762306a36Sopenharmony_ci		chip->playback_format = SB_DSP_LO_OUTPUT_AUTO;
12862306a36Sopenharmony_ci		break;
12962306a36Sopenharmony_ci	case SB_HW_10:
13062306a36Sopenharmony_ci		chip->playback_format = SB_DSP_OUTPUT;
13162306a36Sopenharmony_ci		break;
13262306a36Sopenharmony_ci	default:
13362306a36Sopenharmony_ci		return -EINVAL;
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci	if (chip->mode & SB_MODE_PLAYBACK_16) {
13662306a36Sopenharmony_ci		format = stereo ? SB_DSP_STEREO_16BIT : SB_DSP_MONO_16BIT;
13762306a36Sopenharmony_ci		dma = chip->dma16;
13862306a36Sopenharmony_ci	} else {
13962306a36Sopenharmony_ci		format = stereo ? SB_DSP_STEREO_8BIT : SB_DSP_MONO_8BIT;
14062306a36Sopenharmony_ci		chip->mode |= SB_MODE_PLAYBACK_8;
14162306a36Sopenharmony_ci		dma = chip->dma8;
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci	size = chip->p_dma_size = snd_pcm_lib_buffer_bytes(substream);
14462306a36Sopenharmony_ci	count = chip->p_period_size = snd_pcm_lib_period_bytes(substream);
14562306a36Sopenharmony_ci	spin_lock_irqsave(&chip->reg_lock, flags);
14662306a36Sopenharmony_ci	snd_sbdsp_command(chip, SB_DSP_SPEAKER_ON);
14762306a36Sopenharmony_ci	if (chip->hardware == SB_HW_JAZZ16)
14862306a36Sopenharmony_ci		snd_sbdsp_command(chip, format);
14962306a36Sopenharmony_ci	else if (stereo) {
15062306a36Sopenharmony_ci		/* set playback stereo mode */
15162306a36Sopenharmony_ci		spin_lock(&chip->mixer_lock);
15262306a36Sopenharmony_ci		mixreg = snd_sbmixer_read(chip, SB_DSP_STEREO_SW);
15362306a36Sopenharmony_ci		snd_sbmixer_write(chip, SB_DSP_STEREO_SW, mixreg | 0x02);
15462306a36Sopenharmony_ci		spin_unlock(&chip->mixer_lock);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci		/* Soundblaster hardware programming reference guide, 3-23 */
15762306a36Sopenharmony_ci		snd_sbdsp_command(chip, SB_DSP_DMA8_EXIT);
15862306a36Sopenharmony_ci		runtime->dma_area[0] = 0x80;
15962306a36Sopenharmony_ci		snd_dma_program(dma, runtime->dma_addr, 1, DMA_MODE_WRITE);
16062306a36Sopenharmony_ci		/* force interrupt */
16162306a36Sopenharmony_ci		snd_sbdsp_command(chip, SB_DSP_OUTPUT);
16262306a36Sopenharmony_ci		snd_sbdsp_command(chip, 0);
16362306a36Sopenharmony_ci		snd_sbdsp_command(chip, 0);
16462306a36Sopenharmony_ci	}
16562306a36Sopenharmony_ci	snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE);
16662306a36Sopenharmony_ci	if (stereo) {
16762306a36Sopenharmony_ci		snd_sbdsp_command(chip, 256 - runtime->rate_den / 2);
16862306a36Sopenharmony_ci		spin_lock(&chip->mixer_lock);
16962306a36Sopenharmony_ci		/* save output filter status and turn it off */
17062306a36Sopenharmony_ci		mixreg = snd_sbmixer_read(chip, SB_DSP_PLAYBACK_FILT);
17162306a36Sopenharmony_ci		snd_sbmixer_write(chip, SB_DSP_PLAYBACK_FILT, mixreg | 0x20);
17262306a36Sopenharmony_ci		spin_unlock(&chip->mixer_lock);
17362306a36Sopenharmony_ci		/* just use force_mode16 for temporary storate... */
17462306a36Sopenharmony_ci		chip->force_mode16 = mixreg;
17562306a36Sopenharmony_ci	} else {
17662306a36Sopenharmony_ci		snd_sbdsp_command(chip, 256 - runtime->rate_den);
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci	if (chip->playback_format != SB_DSP_OUTPUT) {
17962306a36Sopenharmony_ci		if (chip->mode & SB_MODE_PLAYBACK_16)
18062306a36Sopenharmony_ci			count /= 2;
18162306a36Sopenharmony_ci		count--;
18262306a36Sopenharmony_ci		snd_sbdsp_command(chip, SB_DSP_BLOCK_SIZE);
18362306a36Sopenharmony_ci		snd_sbdsp_command(chip, count & 0xff);
18462306a36Sopenharmony_ci		snd_sbdsp_command(chip, count >> 8);
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->reg_lock, flags);
18762306a36Sopenharmony_ci	snd_dma_program(dma, runtime->dma_addr,
18862306a36Sopenharmony_ci			size, DMA_MODE_WRITE | DMA_AUTOINIT);
18962306a36Sopenharmony_ci	return 0;
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic int snd_sb8_playback_trigger(struct snd_pcm_substream *substream,
19362306a36Sopenharmony_ci				    int cmd)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	unsigned long flags;
19662306a36Sopenharmony_ci	struct snd_sb *chip = snd_pcm_substream_chip(substream);
19762306a36Sopenharmony_ci	unsigned int count;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	spin_lock_irqsave(&chip->reg_lock, flags);
20062306a36Sopenharmony_ci	switch (cmd) {
20162306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
20262306a36Sopenharmony_ci		snd_sbdsp_command(chip, chip->playback_format);
20362306a36Sopenharmony_ci		if (chip->playback_format == SB_DSP_OUTPUT) {
20462306a36Sopenharmony_ci			count = chip->p_period_size - 1;
20562306a36Sopenharmony_ci			snd_sbdsp_command(chip, count & 0xff);
20662306a36Sopenharmony_ci			snd_sbdsp_command(chip, count >> 8);
20762306a36Sopenharmony_ci		}
20862306a36Sopenharmony_ci		break;
20962306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
21062306a36Sopenharmony_ci		if (chip->playback_format == SB_DSP_HI_OUTPUT_AUTO) {
21162306a36Sopenharmony_ci			struct snd_pcm_runtime *runtime = substream->runtime;
21262306a36Sopenharmony_ci			snd_sbdsp_reset(chip);
21362306a36Sopenharmony_ci			if (runtime->channels > 1) {
21462306a36Sopenharmony_ci				spin_lock(&chip->mixer_lock);
21562306a36Sopenharmony_ci				/* restore output filter and set hardware to mono mode */
21662306a36Sopenharmony_ci				snd_sbmixer_write(chip, SB_DSP_STEREO_SW, chip->force_mode16 & ~0x02);
21762306a36Sopenharmony_ci				spin_unlock(&chip->mixer_lock);
21862306a36Sopenharmony_ci			}
21962306a36Sopenharmony_ci		} else {
22062306a36Sopenharmony_ci			snd_sbdsp_command(chip, SB_DSP_DMA8_OFF);
22162306a36Sopenharmony_ci		}
22262306a36Sopenharmony_ci		snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF);
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->reg_lock, flags);
22562306a36Sopenharmony_ci	return 0;
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic int snd_sb8_capture_prepare(struct snd_pcm_substream *substream)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	unsigned long flags;
23162306a36Sopenharmony_ci	struct snd_sb *chip = snd_pcm_substream_chip(substream);
23262306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
23362306a36Sopenharmony_ci	unsigned int mixreg, rate, size, count;
23462306a36Sopenharmony_ci	unsigned char format;
23562306a36Sopenharmony_ci	unsigned char stereo = runtime->channels > 1;
23662306a36Sopenharmony_ci	int dma;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	rate = runtime->rate;
23962306a36Sopenharmony_ci	switch (chip->hardware) {
24062306a36Sopenharmony_ci	case SB_HW_JAZZ16:
24162306a36Sopenharmony_ci		if (runtime->format == SNDRV_PCM_FORMAT_S16_LE) {
24262306a36Sopenharmony_ci			if (chip->mode & SB_MODE_PLAYBACK_16)
24362306a36Sopenharmony_ci				return -EBUSY;
24462306a36Sopenharmony_ci			else
24562306a36Sopenharmony_ci				chip->mode |= SB_MODE_CAPTURE_16;
24662306a36Sopenharmony_ci		}
24762306a36Sopenharmony_ci		chip->capture_format = SB_DSP_LO_INPUT_AUTO;
24862306a36Sopenharmony_ci		break;
24962306a36Sopenharmony_ci	case SB_HW_PRO:
25062306a36Sopenharmony_ci		if (runtime->channels > 1) {
25162306a36Sopenharmony_ci			if (snd_BUG_ON(rate != SB8_RATE(11025) &&
25262306a36Sopenharmony_ci				       rate != SB8_RATE(22050)))
25362306a36Sopenharmony_ci				return -EINVAL;
25462306a36Sopenharmony_ci			chip->capture_format = SB_DSP_HI_INPUT_AUTO;
25562306a36Sopenharmony_ci			break;
25662306a36Sopenharmony_ci		}
25762306a36Sopenharmony_ci		chip->capture_format = (rate > 23000) ? SB_DSP_HI_INPUT_AUTO : SB_DSP_LO_INPUT_AUTO;
25862306a36Sopenharmony_ci		break;
25962306a36Sopenharmony_ci	case SB_HW_201:
26062306a36Sopenharmony_ci		if (rate > 13000) {
26162306a36Sopenharmony_ci			chip->capture_format = SB_DSP_HI_INPUT_AUTO;
26262306a36Sopenharmony_ci			break;
26362306a36Sopenharmony_ci		}
26462306a36Sopenharmony_ci		fallthrough;
26562306a36Sopenharmony_ci	case SB_HW_20:
26662306a36Sopenharmony_ci		chip->capture_format = SB_DSP_LO_INPUT_AUTO;
26762306a36Sopenharmony_ci		break;
26862306a36Sopenharmony_ci	case SB_HW_10:
26962306a36Sopenharmony_ci		chip->capture_format = SB_DSP_INPUT;
27062306a36Sopenharmony_ci		break;
27162306a36Sopenharmony_ci	default:
27262306a36Sopenharmony_ci		return -EINVAL;
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci	if (chip->mode & SB_MODE_CAPTURE_16) {
27562306a36Sopenharmony_ci		format = stereo ? SB_DSP_STEREO_16BIT : SB_DSP_MONO_16BIT;
27662306a36Sopenharmony_ci		dma = chip->dma16;
27762306a36Sopenharmony_ci	} else {
27862306a36Sopenharmony_ci		format = stereo ? SB_DSP_STEREO_8BIT : SB_DSP_MONO_8BIT;
27962306a36Sopenharmony_ci		chip->mode |= SB_MODE_CAPTURE_8;
28062306a36Sopenharmony_ci		dma = chip->dma8;
28162306a36Sopenharmony_ci	}
28262306a36Sopenharmony_ci	size = chip->c_dma_size = snd_pcm_lib_buffer_bytes(substream);
28362306a36Sopenharmony_ci	count = chip->c_period_size = snd_pcm_lib_period_bytes(substream);
28462306a36Sopenharmony_ci	spin_lock_irqsave(&chip->reg_lock, flags);
28562306a36Sopenharmony_ci	snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF);
28662306a36Sopenharmony_ci	if (chip->hardware == SB_HW_JAZZ16)
28762306a36Sopenharmony_ci		snd_sbdsp_command(chip, format);
28862306a36Sopenharmony_ci	else if (stereo)
28962306a36Sopenharmony_ci		snd_sbdsp_command(chip, SB_DSP_STEREO_8BIT);
29062306a36Sopenharmony_ci	snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE);
29162306a36Sopenharmony_ci	if (stereo) {
29262306a36Sopenharmony_ci		snd_sbdsp_command(chip, 256 - runtime->rate_den / 2);
29362306a36Sopenharmony_ci		spin_lock(&chip->mixer_lock);
29462306a36Sopenharmony_ci		/* save input filter status and turn it off */
29562306a36Sopenharmony_ci		mixreg = snd_sbmixer_read(chip, SB_DSP_CAPTURE_FILT);
29662306a36Sopenharmony_ci		snd_sbmixer_write(chip, SB_DSP_CAPTURE_FILT, mixreg | 0x20);
29762306a36Sopenharmony_ci		spin_unlock(&chip->mixer_lock);
29862306a36Sopenharmony_ci		/* just use force_mode16 for temporary storate... */
29962306a36Sopenharmony_ci		chip->force_mode16 = mixreg;
30062306a36Sopenharmony_ci	} else {
30162306a36Sopenharmony_ci		snd_sbdsp_command(chip, 256 - runtime->rate_den);
30262306a36Sopenharmony_ci	}
30362306a36Sopenharmony_ci	if (chip->capture_format != SB_DSP_INPUT) {
30462306a36Sopenharmony_ci		if (chip->mode & SB_MODE_PLAYBACK_16)
30562306a36Sopenharmony_ci			count /= 2;
30662306a36Sopenharmony_ci		count--;
30762306a36Sopenharmony_ci		snd_sbdsp_command(chip, SB_DSP_BLOCK_SIZE);
30862306a36Sopenharmony_ci		snd_sbdsp_command(chip, count & 0xff);
30962306a36Sopenharmony_ci		snd_sbdsp_command(chip, count >> 8);
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->reg_lock, flags);
31262306a36Sopenharmony_ci	snd_dma_program(dma, runtime->dma_addr,
31362306a36Sopenharmony_ci			size, DMA_MODE_READ | DMA_AUTOINIT);
31462306a36Sopenharmony_ci	return 0;
31562306a36Sopenharmony_ci}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_cistatic int snd_sb8_capture_trigger(struct snd_pcm_substream *substream,
31862306a36Sopenharmony_ci				   int cmd)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	unsigned long flags;
32162306a36Sopenharmony_ci	struct snd_sb *chip = snd_pcm_substream_chip(substream);
32262306a36Sopenharmony_ci	unsigned int count;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	spin_lock_irqsave(&chip->reg_lock, flags);
32562306a36Sopenharmony_ci	switch (cmd) {
32662306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
32762306a36Sopenharmony_ci		snd_sbdsp_command(chip, chip->capture_format);
32862306a36Sopenharmony_ci		if (chip->capture_format == SB_DSP_INPUT) {
32962306a36Sopenharmony_ci			count = chip->c_period_size - 1;
33062306a36Sopenharmony_ci			snd_sbdsp_command(chip, count & 0xff);
33162306a36Sopenharmony_ci			snd_sbdsp_command(chip, count >> 8);
33262306a36Sopenharmony_ci		}
33362306a36Sopenharmony_ci		break;
33462306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
33562306a36Sopenharmony_ci		if (chip->capture_format == SB_DSP_HI_INPUT_AUTO) {
33662306a36Sopenharmony_ci			struct snd_pcm_runtime *runtime = substream->runtime;
33762306a36Sopenharmony_ci			snd_sbdsp_reset(chip);
33862306a36Sopenharmony_ci			if (runtime->channels > 1) {
33962306a36Sopenharmony_ci				/* restore input filter status */
34062306a36Sopenharmony_ci				spin_lock(&chip->mixer_lock);
34162306a36Sopenharmony_ci				snd_sbmixer_write(chip, SB_DSP_CAPTURE_FILT, chip->force_mode16);
34262306a36Sopenharmony_ci				spin_unlock(&chip->mixer_lock);
34362306a36Sopenharmony_ci				/* set hardware to mono mode */
34462306a36Sopenharmony_ci				snd_sbdsp_command(chip, SB_DSP_MONO_8BIT);
34562306a36Sopenharmony_ci			}
34662306a36Sopenharmony_ci		} else {
34762306a36Sopenharmony_ci			snd_sbdsp_command(chip, SB_DSP_DMA8_OFF);
34862306a36Sopenharmony_ci		}
34962306a36Sopenharmony_ci		snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF);
35062306a36Sopenharmony_ci	}
35162306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->reg_lock, flags);
35262306a36Sopenharmony_ci	return 0;
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ciirqreturn_t snd_sb8dsp_interrupt(struct snd_sb *chip)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	struct snd_pcm_substream *substream;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	snd_sb_ack_8bit(chip);
36062306a36Sopenharmony_ci	switch (chip->mode) {
36162306a36Sopenharmony_ci	case SB_MODE_PLAYBACK_16:	/* ok.. playback is active */
36262306a36Sopenharmony_ci		if (chip->hardware != SB_HW_JAZZ16)
36362306a36Sopenharmony_ci			break;
36462306a36Sopenharmony_ci		fallthrough;
36562306a36Sopenharmony_ci	case SB_MODE_PLAYBACK_8:
36662306a36Sopenharmony_ci		substream = chip->playback_substream;
36762306a36Sopenharmony_ci		if (chip->playback_format == SB_DSP_OUTPUT)
36862306a36Sopenharmony_ci		    	snd_sb8_playback_trigger(substream, SNDRV_PCM_TRIGGER_START);
36962306a36Sopenharmony_ci		snd_pcm_period_elapsed(substream);
37062306a36Sopenharmony_ci		break;
37162306a36Sopenharmony_ci	case SB_MODE_CAPTURE_16:
37262306a36Sopenharmony_ci		if (chip->hardware != SB_HW_JAZZ16)
37362306a36Sopenharmony_ci			break;
37462306a36Sopenharmony_ci		fallthrough;
37562306a36Sopenharmony_ci	case SB_MODE_CAPTURE_8:
37662306a36Sopenharmony_ci		substream = chip->capture_substream;
37762306a36Sopenharmony_ci		if (chip->capture_format == SB_DSP_INPUT)
37862306a36Sopenharmony_ci		    	snd_sb8_capture_trigger(substream, SNDRV_PCM_TRIGGER_START);
37962306a36Sopenharmony_ci		snd_pcm_period_elapsed(substream);
38062306a36Sopenharmony_ci		break;
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci	return IRQ_HANDLED;
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_sb8_playback_pointer(struct snd_pcm_substream *substream)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	struct snd_sb *chip = snd_pcm_substream_chip(substream);
38862306a36Sopenharmony_ci	size_t ptr;
38962306a36Sopenharmony_ci	int dma;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	if (chip->mode & SB_MODE_PLAYBACK_8)
39262306a36Sopenharmony_ci		dma = chip->dma8;
39362306a36Sopenharmony_ci	else if (chip->mode & SB_MODE_PLAYBACK_16)
39462306a36Sopenharmony_ci		dma = chip->dma16;
39562306a36Sopenharmony_ci	else
39662306a36Sopenharmony_ci		return 0;
39762306a36Sopenharmony_ci	ptr = snd_dma_pointer(dma, chip->p_dma_size);
39862306a36Sopenharmony_ci	return bytes_to_frames(substream->runtime, ptr);
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_sb8_capture_pointer(struct snd_pcm_substream *substream)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	struct snd_sb *chip = snd_pcm_substream_chip(substream);
40462306a36Sopenharmony_ci	size_t ptr;
40562306a36Sopenharmony_ci	int dma;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	if (chip->mode & SB_MODE_CAPTURE_8)
40862306a36Sopenharmony_ci		dma = chip->dma8;
40962306a36Sopenharmony_ci	else if (chip->mode & SB_MODE_CAPTURE_16)
41062306a36Sopenharmony_ci		dma = chip->dma16;
41162306a36Sopenharmony_ci	else
41262306a36Sopenharmony_ci		return 0;
41362306a36Sopenharmony_ci	ptr = snd_dma_pointer(dma, chip->c_dma_size);
41462306a36Sopenharmony_ci	return bytes_to_frames(substream->runtime, ptr);
41562306a36Sopenharmony_ci}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci/*
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci */
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_sb8_playback =
42262306a36Sopenharmony_ci{
42362306a36Sopenharmony_ci	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
42462306a36Sopenharmony_ci				 SNDRV_PCM_INFO_MMAP_VALID),
42562306a36Sopenharmony_ci	.formats =		 SNDRV_PCM_FMTBIT_U8,
42662306a36Sopenharmony_ci	.rates =		(SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000 |
42762306a36Sopenharmony_ci				 SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_22050),
42862306a36Sopenharmony_ci	.rate_min =		4000,
42962306a36Sopenharmony_ci	.rate_max =		23000,
43062306a36Sopenharmony_ci	.channels_min =		1,
43162306a36Sopenharmony_ci	.channels_max =		1,
43262306a36Sopenharmony_ci	.buffer_bytes_max =	65536,
43362306a36Sopenharmony_ci	.period_bytes_min =	64,
43462306a36Sopenharmony_ci	.period_bytes_max =	65536,
43562306a36Sopenharmony_ci	.periods_min =		1,
43662306a36Sopenharmony_ci	.periods_max =		1024,
43762306a36Sopenharmony_ci	.fifo_size =		0,
43862306a36Sopenharmony_ci};
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_sb8_capture =
44162306a36Sopenharmony_ci{
44262306a36Sopenharmony_ci	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
44362306a36Sopenharmony_ci				 SNDRV_PCM_INFO_MMAP_VALID),
44462306a36Sopenharmony_ci	.formats =		SNDRV_PCM_FMTBIT_U8,
44562306a36Sopenharmony_ci	.rates =		(SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000 |
44662306a36Sopenharmony_ci				 SNDRV_PCM_RATE_11025),
44762306a36Sopenharmony_ci	.rate_min =		4000,
44862306a36Sopenharmony_ci	.rate_max =		13000,
44962306a36Sopenharmony_ci	.channels_min =		1,
45062306a36Sopenharmony_ci	.channels_max =		1,
45162306a36Sopenharmony_ci	.buffer_bytes_max =	65536,
45262306a36Sopenharmony_ci	.period_bytes_min =	64,
45362306a36Sopenharmony_ci	.period_bytes_max =	65536,
45462306a36Sopenharmony_ci	.periods_min =		1,
45562306a36Sopenharmony_ci	.periods_max =		1024,
45662306a36Sopenharmony_ci	.fifo_size =		0,
45762306a36Sopenharmony_ci};
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci/*
46062306a36Sopenharmony_ci *
46162306a36Sopenharmony_ci */
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_cistatic int snd_sb8_open(struct snd_pcm_substream *substream)
46462306a36Sopenharmony_ci{
46562306a36Sopenharmony_ci	struct snd_sb *chip = snd_pcm_substream_chip(substream);
46662306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
46762306a36Sopenharmony_ci	unsigned long flags;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	spin_lock_irqsave(&chip->open_lock, flags);
47062306a36Sopenharmony_ci	if (chip->open) {
47162306a36Sopenharmony_ci		spin_unlock_irqrestore(&chip->open_lock, flags);
47262306a36Sopenharmony_ci		return -EAGAIN;
47362306a36Sopenharmony_ci	}
47462306a36Sopenharmony_ci	chip->open |= SB_OPEN_PCM;
47562306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->open_lock, flags);
47662306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
47762306a36Sopenharmony_ci		chip->playback_substream = substream;
47862306a36Sopenharmony_ci		runtime->hw = snd_sb8_playback;
47962306a36Sopenharmony_ci	} else {
48062306a36Sopenharmony_ci		chip->capture_substream = substream;
48162306a36Sopenharmony_ci		runtime->hw = snd_sb8_capture;
48262306a36Sopenharmony_ci	}
48362306a36Sopenharmony_ci	switch (chip->hardware) {
48462306a36Sopenharmony_ci	case SB_HW_JAZZ16:
48562306a36Sopenharmony_ci		if (chip->dma16 == 5 || chip->dma16 == 7)
48662306a36Sopenharmony_ci			runtime->hw.formats |= SNDRV_PCM_FMTBIT_S16_LE;
48762306a36Sopenharmony_ci		runtime->hw.rates |= SNDRV_PCM_RATE_8000_48000;
48862306a36Sopenharmony_ci		runtime->hw.rate_min = 4000;
48962306a36Sopenharmony_ci		runtime->hw.rate_max = 50000;
49062306a36Sopenharmony_ci		runtime->hw.channels_max = 2;
49162306a36Sopenharmony_ci		break;
49262306a36Sopenharmony_ci	case SB_HW_PRO:
49362306a36Sopenharmony_ci		runtime->hw.rate_max = 44100;
49462306a36Sopenharmony_ci		runtime->hw.channels_max = 2;
49562306a36Sopenharmony_ci		snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
49662306a36Sopenharmony_ci				    snd_sb8_hw_constraint_rate_channels, NULL,
49762306a36Sopenharmony_ci				    SNDRV_PCM_HW_PARAM_CHANNELS,
49862306a36Sopenharmony_ci				    SNDRV_PCM_HW_PARAM_RATE, -1);
49962306a36Sopenharmony_ci		snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
50062306a36Sopenharmony_ci				     snd_sb8_hw_constraint_channels_rate, NULL,
50162306a36Sopenharmony_ci				     SNDRV_PCM_HW_PARAM_RATE, -1);
50262306a36Sopenharmony_ci		break;
50362306a36Sopenharmony_ci	case SB_HW_201:
50462306a36Sopenharmony_ci		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
50562306a36Sopenharmony_ci			runtime->hw.rate_max = 44100;
50662306a36Sopenharmony_ci		} else {
50762306a36Sopenharmony_ci			runtime->hw.rate_max = 15000;
50862306a36Sopenharmony_ci		}
50962306a36Sopenharmony_ci		break;
51062306a36Sopenharmony_ci	default:
51162306a36Sopenharmony_ci		break;
51262306a36Sopenharmony_ci	}
51362306a36Sopenharmony_ci	snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
51462306a36Sopenharmony_ci				      &hw_constraints_clock);
51562306a36Sopenharmony_ci	if (chip->dma8 > 3 || chip->dma16 >= 0) {
51662306a36Sopenharmony_ci		snd_pcm_hw_constraint_step(runtime, 0,
51762306a36Sopenharmony_ci					   SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 2);
51862306a36Sopenharmony_ci		snd_pcm_hw_constraint_step(runtime, 0,
51962306a36Sopenharmony_ci					   SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 2);
52062306a36Sopenharmony_ci		runtime->hw.buffer_bytes_max = 128 * 1024 * 1024;
52162306a36Sopenharmony_ci		runtime->hw.period_bytes_max = 128 * 1024 * 1024;
52262306a36Sopenharmony_ci	}
52362306a36Sopenharmony_ci	return 0;
52462306a36Sopenharmony_ci}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_cistatic int snd_sb8_close(struct snd_pcm_substream *substream)
52762306a36Sopenharmony_ci{
52862306a36Sopenharmony_ci	unsigned long flags;
52962306a36Sopenharmony_ci	struct snd_sb *chip = snd_pcm_substream_chip(substream);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	chip->playback_substream = NULL;
53262306a36Sopenharmony_ci	chip->capture_substream = NULL;
53362306a36Sopenharmony_ci	spin_lock_irqsave(&chip->open_lock, flags);
53462306a36Sopenharmony_ci	chip->open &= ~SB_OPEN_PCM;
53562306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
53662306a36Sopenharmony_ci		chip->mode &= ~SB_MODE_PLAYBACK;
53762306a36Sopenharmony_ci	else
53862306a36Sopenharmony_ci		chip->mode &= ~SB_MODE_CAPTURE;
53962306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->open_lock, flags);
54062306a36Sopenharmony_ci	return 0;
54162306a36Sopenharmony_ci}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci/*
54462306a36Sopenharmony_ci *  Initialization part
54562306a36Sopenharmony_ci */
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_sb8_playback_ops = {
54862306a36Sopenharmony_ci	.open =			snd_sb8_open,
54962306a36Sopenharmony_ci	.close =		snd_sb8_close,
55062306a36Sopenharmony_ci	.prepare =		snd_sb8_playback_prepare,
55162306a36Sopenharmony_ci	.trigger =		snd_sb8_playback_trigger,
55262306a36Sopenharmony_ci	.pointer =		snd_sb8_playback_pointer,
55362306a36Sopenharmony_ci};
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_sb8_capture_ops = {
55662306a36Sopenharmony_ci	.open =			snd_sb8_open,
55762306a36Sopenharmony_ci	.close =		snd_sb8_close,
55862306a36Sopenharmony_ci	.prepare =		snd_sb8_capture_prepare,
55962306a36Sopenharmony_ci	.trigger =		snd_sb8_capture_trigger,
56062306a36Sopenharmony_ci	.pointer =		snd_sb8_capture_pointer,
56162306a36Sopenharmony_ci};
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ciint snd_sb8dsp_pcm(struct snd_sb *chip, int device)
56462306a36Sopenharmony_ci{
56562306a36Sopenharmony_ci	struct snd_card *card = chip->card;
56662306a36Sopenharmony_ci	struct snd_pcm *pcm;
56762306a36Sopenharmony_ci	int err;
56862306a36Sopenharmony_ci	size_t max_prealloc = 64 * 1024;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	err = snd_pcm_new(card, "SB8 DSP", device, 1, 1, &pcm);
57162306a36Sopenharmony_ci	if (err < 0)
57262306a36Sopenharmony_ci		return err;
57362306a36Sopenharmony_ci	sprintf(pcm->name, "DSP v%i.%i", chip->version >> 8, chip->version & 0xff);
57462306a36Sopenharmony_ci	pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
57562306a36Sopenharmony_ci	pcm->private_data = chip;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sb8_playback_ops);
57862306a36Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sb8_capture_ops);
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	if (chip->dma8 > 3 || chip->dma16 >= 0)
58162306a36Sopenharmony_ci		max_prealloc = 128 * 1024;
58262306a36Sopenharmony_ci	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
58362306a36Sopenharmony_ci				       card->dev, 64*1024, max_prealloc);
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	return 0;
58662306a36Sopenharmony_ci}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ciEXPORT_SYMBOL(snd_sb8dsp_pcm);
58962306a36Sopenharmony_ciEXPORT_SYMBOL(snd_sb8dsp_interrupt);
59062306a36Sopenharmony_ci  /* sb8_midi.c */
59162306a36Sopenharmony_ciEXPORT_SYMBOL(snd_sb8dsp_midi_interrupt);
59262306a36Sopenharmony_ciEXPORT_SYMBOL(snd_sb8dsp_midi);
593