162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  ALSA driver for Echoaudio soundcards.
462306a36Sopenharmony_ci *  Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
562306a36Sopenharmony_ci *  Copyright (C) 2020 Mark Hills <mark@xwax.org>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ciMODULE_AUTHOR("Giuliano Pochini <pochini@shiny.it>");
1162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
1262306a36Sopenharmony_ciMODULE_DESCRIPTION("Echoaudio " ECHOCARD_NAME " soundcards driver");
1362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, snd_echo_ids);
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
1662306a36Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
1762306a36Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cimodule_param_array(index, int, NULL, 0444);
2062306a36Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for " ECHOCARD_NAME " soundcard.");
2162306a36Sopenharmony_cimodule_param_array(id, charp, NULL, 0444);
2262306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for " ECHOCARD_NAME " soundcard.");
2362306a36Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444);
2462306a36Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable " ECHOCARD_NAME " soundcard.");
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic const unsigned int channels_list[10] = {1, 2, 4, 6, 8, 10, 12, 14, 16, 999999};
2762306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_output_gain, -12800, 100, 1);
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic int get_firmware(const struct firmware **fw_entry,
3262306a36Sopenharmony_ci			struct echoaudio *chip, const short fw_index)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	int err;
3562306a36Sopenharmony_ci	char name[30];
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
3862306a36Sopenharmony_ci	if (chip->fw_cache[fw_index]) {
3962306a36Sopenharmony_ci		dev_dbg(chip->card->dev,
4062306a36Sopenharmony_ci			"firmware requested: %s is cached\n",
4162306a36Sopenharmony_ci			card_fw[fw_index].data);
4262306a36Sopenharmony_ci		*fw_entry = chip->fw_cache[fw_index];
4362306a36Sopenharmony_ci		return 0;
4462306a36Sopenharmony_ci	}
4562306a36Sopenharmony_ci#endif
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	dev_dbg(chip->card->dev,
4862306a36Sopenharmony_ci		"firmware requested: %s\n", card_fw[fw_index].data);
4962306a36Sopenharmony_ci	snprintf(name, sizeof(name), "ea/%s", card_fw[fw_index].data);
5062306a36Sopenharmony_ci	err = request_firmware(fw_entry, name, &chip->pci->dev);
5162306a36Sopenharmony_ci	if (err < 0)
5262306a36Sopenharmony_ci		dev_err(chip->card->dev,
5362306a36Sopenharmony_ci			"get_firmware(): Firmware not available (%d)\n", err);
5462306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
5562306a36Sopenharmony_ci	else
5662306a36Sopenharmony_ci		chip->fw_cache[fw_index] = *fw_entry;
5762306a36Sopenharmony_ci#endif
5862306a36Sopenharmony_ci	return err;
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic void free_firmware(const struct firmware *fw_entry,
6462306a36Sopenharmony_ci			  struct echoaudio *chip)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
6762306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "firmware not released (kept in cache)\n");
6862306a36Sopenharmony_ci#else
6962306a36Sopenharmony_ci	release_firmware(fw_entry);
7062306a36Sopenharmony_ci#endif
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic void free_firmware_cache(struct echoaudio *chip)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
7862306a36Sopenharmony_ci	int i;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	for (i = 0; i < 8 ; i++)
8162306a36Sopenharmony_ci		if (chip->fw_cache[i]) {
8262306a36Sopenharmony_ci			release_firmware(chip->fw_cache[i]);
8362306a36Sopenharmony_ci			dev_dbg(chip->card->dev, "release_firmware(%d)\n", i);
8462306a36Sopenharmony_ci		}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci#endif
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci/******************************************************************************
9262306a36Sopenharmony_ci	PCM interface
9362306a36Sopenharmony_ci******************************************************************************/
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic void audiopipe_free(struct snd_pcm_runtime *runtime)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	struct audiopipe *pipe = runtime->private_data;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	if (pipe->sgpage.area)
10062306a36Sopenharmony_ci		snd_dma_free_pages(&pipe->sgpage);
10162306a36Sopenharmony_ci	kfree(pipe);
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic int hw_rule_capture_format_by_channels(struct snd_pcm_hw_params *params,
10762306a36Sopenharmony_ci					      struct snd_pcm_hw_rule *rule)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	struct snd_interval *c = hw_param_interval(params,
11062306a36Sopenharmony_ci						   SNDRV_PCM_HW_PARAM_CHANNELS);
11162306a36Sopenharmony_ci	struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
11262306a36Sopenharmony_ci	struct snd_mask fmt;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	snd_mask_any(&fmt);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci#ifndef ECHOCARD_HAS_STEREO_BIG_ENDIAN32
11762306a36Sopenharmony_ci	/* >=2 channels cannot be S32_BE */
11862306a36Sopenharmony_ci	if (c->min == 2) {
11962306a36Sopenharmony_ci		fmt.bits[0] &= ~SNDRV_PCM_FMTBIT_S32_BE;
12062306a36Sopenharmony_ci		return snd_mask_refine(f, &fmt);
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci#endif
12362306a36Sopenharmony_ci	/* > 2 channels cannot be U8 and S32_BE */
12462306a36Sopenharmony_ci	if (c->min > 2) {
12562306a36Sopenharmony_ci		fmt.bits[0] &= ~(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_BE);
12662306a36Sopenharmony_ci		return snd_mask_refine(f, &fmt);
12762306a36Sopenharmony_ci	}
12862306a36Sopenharmony_ci	/* Mono is ok with any format */
12962306a36Sopenharmony_ci	return 0;
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistatic int hw_rule_capture_channels_by_format(struct snd_pcm_hw_params *params,
13562306a36Sopenharmony_ci					      struct snd_pcm_hw_rule *rule)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	struct snd_interval *c = hw_param_interval(params,
13862306a36Sopenharmony_ci						   SNDRV_PCM_HW_PARAM_CHANNELS);
13962306a36Sopenharmony_ci	struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
14062306a36Sopenharmony_ci	struct snd_interval ch;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	snd_interval_any(&ch);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	/* S32_BE is mono (and stereo) only */
14562306a36Sopenharmony_ci	if (f->bits[0] == SNDRV_PCM_FMTBIT_S32_BE) {
14662306a36Sopenharmony_ci		ch.min = 1;
14762306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_STEREO_BIG_ENDIAN32
14862306a36Sopenharmony_ci		ch.max = 2;
14962306a36Sopenharmony_ci#else
15062306a36Sopenharmony_ci		ch.max = 1;
15162306a36Sopenharmony_ci#endif
15262306a36Sopenharmony_ci		ch.integer = 1;
15362306a36Sopenharmony_ci		return snd_interval_refine(c, &ch);
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci	/* U8 can be only mono or stereo */
15662306a36Sopenharmony_ci	if (f->bits[0] == SNDRV_PCM_FMTBIT_U8) {
15762306a36Sopenharmony_ci		ch.min = 1;
15862306a36Sopenharmony_ci		ch.max = 2;
15962306a36Sopenharmony_ci		ch.integer = 1;
16062306a36Sopenharmony_ci		return snd_interval_refine(c, &ch);
16162306a36Sopenharmony_ci	}
16262306a36Sopenharmony_ci	/* S16_LE, S24_3LE and S32_LE support any number of channels. */
16362306a36Sopenharmony_ci	return 0;
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic int hw_rule_playback_format_by_channels(struct snd_pcm_hw_params *params,
16962306a36Sopenharmony_ci					       struct snd_pcm_hw_rule *rule)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	struct snd_interval *c = hw_param_interval(params,
17262306a36Sopenharmony_ci						   SNDRV_PCM_HW_PARAM_CHANNELS);
17362306a36Sopenharmony_ci	struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
17462306a36Sopenharmony_ci	struct snd_mask fmt;
17562306a36Sopenharmony_ci	u64 fmask;
17662306a36Sopenharmony_ci	snd_mask_any(&fmt);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	fmask = fmt.bits[0] + ((u64)fmt.bits[1] << 32);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	/* >2 channels must be S16_LE, S24_3LE or S32_LE */
18162306a36Sopenharmony_ci	if (c->min > 2) {
18262306a36Sopenharmony_ci		fmask &= SNDRV_PCM_FMTBIT_S16_LE |
18362306a36Sopenharmony_ci			 SNDRV_PCM_FMTBIT_S24_3LE |
18462306a36Sopenharmony_ci			 SNDRV_PCM_FMTBIT_S32_LE;
18562306a36Sopenharmony_ci	/* 1 channel must be S32_BE or S32_LE */
18662306a36Sopenharmony_ci	} else if (c->max == 1)
18762306a36Sopenharmony_ci		fmask &= SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE;
18862306a36Sopenharmony_ci#ifndef ECHOCARD_HAS_STEREO_BIG_ENDIAN32
18962306a36Sopenharmony_ci	/* 2 channels cannot be S32_BE */
19062306a36Sopenharmony_ci	else if (c->min == 2 && c->max == 2)
19162306a36Sopenharmony_ci		fmask &= ~SNDRV_PCM_FMTBIT_S32_BE;
19262306a36Sopenharmony_ci#endif
19362306a36Sopenharmony_ci	else
19462306a36Sopenharmony_ci		return 0;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	fmt.bits[0] &= (u32)fmask;
19762306a36Sopenharmony_ci	fmt.bits[1] &= (u32)(fmask >> 32);
19862306a36Sopenharmony_ci	return snd_mask_refine(f, &fmt);
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic int hw_rule_playback_channels_by_format(struct snd_pcm_hw_params *params,
20462306a36Sopenharmony_ci					       struct snd_pcm_hw_rule *rule)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	struct snd_interval *c = hw_param_interval(params,
20762306a36Sopenharmony_ci						   SNDRV_PCM_HW_PARAM_CHANNELS);
20862306a36Sopenharmony_ci	struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
20962306a36Sopenharmony_ci	struct snd_interval ch;
21062306a36Sopenharmony_ci	u64 fmask;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	snd_interval_any(&ch);
21362306a36Sopenharmony_ci	ch.integer = 1;
21462306a36Sopenharmony_ci	fmask = f->bits[0] + ((u64)f->bits[1] << 32);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	/* S32_BE is mono (and stereo) only */
21762306a36Sopenharmony_ci	if (fmask == SNDRV_PCM_FMTBIT_S32_BE) {
21862306a36Sopenharmony_ci		ch.min = 1;
21962306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_STEREO_BIG_ENDIAN32
22062306a36Sopenharmony_ci		ch.max = 2;
22162306a36Sopenharmony_ci#else
22262306a36Sopenharmony_ci		ch.max = 1;
22362306a36Sopenharmony_ci#endif
22462306a36Sopenharmony_ci	/* U8 is stereo only */
22562306a36Sopenharmony_ci	} else if (fmask == SNDRV_PCM_FMTBIT_U8)
22662306a36Sopenharmony_ci		ch.min = ch.max = 2;
22762306a36Sopenharmony_ci	/* S16_LE and S24_3LE must be at least stereo */
22862306a36Sopenharmony_ci	else if (!(fmask & ~(SNDRV_PCM_FMTBIT_S16_LE |
22962306a36Sopenharmony_ci			       SNDRV_PCM_FMTBIT_S24_3LE)))
23062306a36Sopenharmony_ci		ch.min = 2;
23162306a36Sopenharmony_ci	else
23262306a36Sopenharmony_ci		return 0;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	return snd_interval_refine(c, &ch);
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci/* Since the sample rate is a global setting, do allow the user to change the
24062306a36Sopenharmony_cisample rate only if there is only one pcm device open. */
24162306a36Sopenharmony_cistatic int hw_rule_sample_rate(struct snd_pcm_hw_params *params,
24262306a36Sopenharmony_ci			       struct snd_pcm_hw_rule *rule)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	struct snd_interval *rate = hw_param_interval(params,
24562306a36Sopenharmony_ci						      SNDRV_PCM_HW_PARAM_RATE);
24662306a36Sopenharmony_ci	struct echoaudio *chip = rule->private;
24762306a36Sopenharmony_ci	struct snd_interval fixed;
24862306a36Sopenharmony_ci	int err;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	mutex_lock(&chip->mode_mutex);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	if (chip->can_set_rate) {
25362306a36Sopenharmony_ci		err = 0;
25462306a36Sopenharmony_ci	} else {
25562306a36Sopenharmony_ci		snd_interval_any(&fixed);
25662306a36Sopenharmony_ci		fixed.min = fixed.max = chip->sample_rate;
25762306a36Sopenharmony_ci		err = snd_interval_refine(rate, &fixed);
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	mutex_unlock(&chip->mode_mutex);
26162306a36Sopenharmony_ci	return err;
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cistatic int pcm_open(struct snd_pcm_substream *substream,
26662306a36Sopenharmony_ci		    signed char max_channels)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	struct echoaudio *chip;
26962306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime;
27062306a36Sopenharmony_ci	struct audiopipe *pipe;
27162306a36Sopenharmony_ci	int err, i;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	if (max_channels <= 0)
27462306a36Sopenharmony_ci		return -EAGAIN;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	chip = snd_pcm_substream_chip(substream);
27762306a36Sopenharmony_ci	runtime = substream->runtime;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	pipe = kzalloc(sizeof(struct audiopipe), GFP_KERNEL);
28062306a36Sopenharmony_ci	if (!pipe)
28162306a36Sopenharmony_ci		return -ENOMEM;
28262306a36Sopenharmony_ci	pipe->index = -1;		/* Not configured yet */
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	/* Set up hw capabilities and contraints */
28562306a36Sopenharmony_ci	memcpy(&pipe->hw, &pcm_hardware_skel, sizeof(struct snd_pcm_hardware));
28662306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "max_channels=%d\n", max_channels);
28762306a36Sopenharmony_ci	pipe->constr.list = channels_list;
28862306a36Sopenharmony_ci	pipe->constr.mask = 0;
28962306a36Sopenharmony_ci	for (i = 0; channels_list[i] <= max_channels; i++);
29062306a36Sopenharmony_ci	pipe->constr.count = i;
29162306a36Sopenharmony_ci	if (pipe->hw.channels_max > max_channels)
29262306a36Sopenharmony_ci		pipe->hw.channels_max = max_channels;
29362306a36Sopenharmony_ci	if (chip->digital_mode == DIGITAL_MODE_ADAT) {
29462306a36Sopenharmony_ci		pipe->hw.rate_max = 48000;
29562306a36Sopenharmony_ci		pipe->hw.rates &= SNDRV_PCM_RATE_8000_48000;
29662306a36Sopenharmony_ci	}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	runtime->hw = pipe->hw;
29962306a36Sopenharmony_ci	runtime->private_data = pipe;
30062306a36Sopenharmony_ci	runtime->private_free = audiopipe_free;
30162306a36Sopenharmony_ci	snd_pcm_set_sync(substream);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	/* Only mono and any even number of channels are allowed */
30462306a36Sopenharmony_ci	err = snd_pcm_hw_constraint_list(runtime, 0,
30562306a36Sopenharmony_ci					 SNDRV_PCM_HW_PARAM_CHANNELS,
30662306a36Sopenharmony_ci					 &pipe->constr);
30762306a36Sopenharmony_ci	if (err < 0)
30862306a36Sopenharmony_ci		return err;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	/* All periods should have the same size */
31162306a36Sopenharmony_ci	err = snd_pcm_hw_constraint_integer(runtime,
31262306a36Sopenharmony_ci					    SNDRV_PCM_HW_PARAM_PERIODS);
31362306a36Sopenharmony_ci	if (err < 0)
31462306a36Sopenharmony_ci		return err;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	/* The hw accesses memory in chunks 32 frames long and they should be
31762306a36Sopenharmony_ci	32-bytes-aligned. It's not a requirement, but it seems that IRQs are
31862306a36Sopenharmony_ci	generated with a resolution of 32 frames. Thus we need the following */
31962306a36Sopenharmony_ci	err = snd_pcm_hw_constraint_step(runtime, 0,
32062306a36Sopenharmony_ci					 SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 32);
32162306a36Sopenharmony_ci	if (err < 0)
32262306a36Sopenharmony_ci		return err;
32362306a36Sopenharmony_ci	err = snd_pcm_hw_constraint_step(runtime, 0,
32462306a36Sopenharmony_ci					 SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32);
32562306a36Sopenharmony_ci	if (err < 0)
32662306a36Sopenharmony_ci		return err;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	err = snd_pcm_hw_rule_add(substream->runtime, 0,
32962306a36Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_RATE,
33062306a36Sopenharmony_ci				  hw_rule_sample_rate, chip,
33162306a36Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_RATE, -1);
33262306a36Sopenharmony_ci	if (err < 0)
33362306a36Sopenharmony_ci		return err;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	/* Allocate a page for the scatter-gather list */
33662306a36Sopenharmony_ci	err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
33762306a36Sopenharmony_ci				  &chip->pci->dev,
33862306a36Sopenharmony_ci				  PAGE_SIZE, &pipe->sgpage);
33962306a36Sopenharmony_ci	if (err < 0) {
34062306a36Sopenharmony_ci		dev_err(chip->card->dev, "s-g list allocation failed\n");
34162306a36Sopenharmony_ci		return err;
34262306a36Sopenharmony_ci	}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	/*
34562306a36Sopenharmony_ci	 * Sole ownership required to set the rate
34662306a36Sopenharmony_ci	 */
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "pcm_open opencount=%d can_set_rate=%d, rate_set=%d",
34962306a36Sopenharmony_ci		chip->opencount, chip->can_set_rate, chip->rate_set);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	chip->opencount++;
35262306a36Sopenharmony_ci	if (chip->opencount > 1 && chip->rate_set)
35362306a36Sopenharmony_ci		chip->can_set_rate = 0;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	return 0;
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_cistatic int pcm_analog_in_open(struct snd_pcm_substream *substream)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	struct echoaudio *chip = snd_pcm_substream_chip(substream);
36362306a36Sopenharmony_ci	int err;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	err = pcm_open(substream,
36662306a36Sopenharmony_ci		       num_analog_busses_in(chip) - substream->number);
36762306a36Sopenharmony_ci	if (err < 0)
36862306a36Sopenharmony_ci		return err;
36962306a36Sopenharmony_ci	err = snd_pcm_hw_rule_add(substream->runtime, 0,
37062306a36Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_CHANNELS,
37162306a36Sopenharmony_ci				  hw_rule_capture_channels_by_format, NULL,
37262306a36Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_FORMAT, -1);
37362306a36Sopenharmony_ci	if (err < 0)
37462306a36Sopenharmony_ci		return err;
37562306a36Sopenharmony_ci	err = snd_pcm_hw_rule_add(substream->runtime, 0,
37662306a36Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_FORMAT,
37762306a36Sopenharmony_ci				  hw_rule_capture_format_by_channels, NULL,
37862306a36Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
37962306a36Sopenharmony_ci	if (err < 0)
38062306a36Sopenharmony_ci		return err;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	return 0;
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_cistatic int pcm_analog_out_open(struct snd_pcm_substream *substream)
38862306a36Sopenharmony_ci{
38962306a36Sopenharmony_ci	struct echoaudio *chip = snd_pcm_substream_chip(substream);
39062306a36Sopenharmony_ci	int max_channels, err;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_VMIXER
39362306a36Sopenharmony_ci	max_channels = num_pipes_out(chip);
39462306a36Sopenharmony_ci#else
39562306a36Sopenharmony_ci	max_channels = num_analog_busses_out(chip);
39662306a36Sopenharmony_ci#endif
39762306a36Sopenharmony_ci	err = pcm_open(substream, max_channels - substream->number);
39862306a36Sopenharmony_ci	if (err < 0)
39962306a36Sopenharmony_ci		return err;
40062306a36Sopenharmony_ci	err = snd_pcm_hw_rule_add(substream->runtime, 0,
40162306a36Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_CHANNELS,
40262306a36Sopenharmony_ci				  hw_rule_playback_channels_by_format,
40362306a36Sopenharmony_ci				  NULL,
40462306a36Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_FORMAT, -1);
40562306a36Sopenharmony_ci	if (err < 0)
40662306a36Sopenharmony_ci		return err;
40762306a36Sopenharmony_ci	err = snd_pcm_hw_rule_add(substream->runtime, 0,
40862306a36Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_FORMAT,
40962306a36Sopenharmony_ci				  hw_rule_playback_format_by_channels,
41062306a36Sopenharmony_ci				  NULL,
41162306a36Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
41262306a36Sopenharmony_ci	if (err < 0)
41362306a36Sopenharmony_ci		return err;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	return 0;
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_DIGITAL_IO
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cistatic int pcm_digital_in_open(struct snd_pcm_substream *substream)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	struct echoaudio *chip = snd_pcm_substream_chip(substream);
42562306a36Sopenharmony_ci	int err, max_channels;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	max_channels = num_digital_busses_in(chip) - substream->number;
42862306a36Sopenharmony_ci	mutex_lock(&chip->mode_mutex);
42962306a36Sopenharmony_ci	if (chip->digital_mode == DIGITAL_MODE_ADAT)
43062306a36Sopenharmony_ci		err = pcm_open(substream, max_channels);
43162306a36Sopenharmony_ci	else	/* If the card has ADAT, subtract the 6 channels
43262306a36Sopenharmony_ci		 * that S/PDIF doesn't have
43362306a36Sopenharmony_ci		 */
43462306a36Sopenharmony_ci		err = pcm_open(substream, max_channels - ECHOCARD_HAS_ADAT);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	if (err < 0)
43762306a36Sopenharmony_ci		goto din_exit;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	err = snd_pcm_hw_rule_add(substream->runtime, 0,
44062306a36Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_CHANNELS,
44162306a36Sopenharmony_ci				  hw_rule_capture_channels_by_format, NULL,
44262306a36Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_FORMAT, -1);
44362306a36Sopenharmony_ci	if (err < 0)
44462306a36Sopenharmony_ci		goto din_exit;
44562306a36Sopenharmony_ci	err = snd_pcm_hw_rule_add(substream->runtime, 0,
44662306a36Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_FORMAT,
44762306a36Sopenharmony_ci				  hw_rule_capture_format_by_channels, NULL,
44862306a36Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
44962306a36Sopenharmony_ci	if (err < 0)
45062306a36Sopenharmony_ci		goto din_exit;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_cidin_exit:
45362306a36Sopenharmony_ci	mutex_unlock(&chip->mode_mutex);
45462306a36Sopenharmony_ci	return err;
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci#ifndef ECHOCARD_HAS_VMIXER	/* See the note in snd_echo_new_pcm() */
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_cistatic int pcm_digital_out_open(struct snd_pcm_substream *substream)
46262306a36Sopenharmony_ci{
46362306a36Sopenharmony_ci	struct echoaudio *chip = snd_pcm_substream_chip(substream);
46462306a36Sopenharmony_ci	int err, max_channels;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	max_channels = num_digital_busses_out(chip) - substream->number;
46762306a36Sopenharmony_ci	mutex_lock(&chip->mode_mutex);
46862306a36Sopenharmony_ci	if (chip->digital_mode == DIGITAL_MODE_ADAT)
46962306a36Sopenharmony_ci		err = pcm_open(substream, max_channels);
47062306a36Sopenharmony_ci	else	/* If the card has ADAT, subtract the 6 channels
47162306a36Sopenharmony_ci		 * that S/PDIF doesn't have
47262306a36Sopenharmony_ci		 */
47362306a36Sopenharmony_ci		err = pcm_open(substream, max_channels - ECHOCARD_HAS_ADAT);
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	if (err < 0)
47662306a36Sopenharmony_ci		goto dout_exit;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	err = snd_pcm_hw_rule_add(substream->runtime, 0,
47962306a36Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_CHANNELS,
48062306a36Sopenharmony_ci				  hw_rule_playback_channels_by_format,
48162306a36Sopenharmony_ci				  NULL, SNDRV_PCM_HW_PARAM_FORMAT,
48262306a36Sopenharmony_ci				  -1);
48362306a36Sopenharmony_ci	if (err < 0)
48462306a36Sopenharmony_ci		goto dout_exit;
48562306a36Sopenharmony_ci	err = snd_pcm_hw_rule_add(substream->runtime, 0,
48662306a36Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_FORMAT,
48762306a36Sopenharmony_ci				  hw_rule_playback_format_by_channels,
48862306a36Sopenharmony_ci				  NULL, SNDRV_PCM_HW_PARAM_CHANNELS,
48962306a36Sopenharmony_ci				  -1);
49062306a36Sopenharmony_ci	if (err < 0)
49162306a36Sopenharmony_ci		goto dout_exit;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_cidout_exit:
49462306a36Sopenharmony_ci	mutex_unlock(&chip->mode_mutex);
49562306a36Sopenharmony_ci	return err;
49662306a36Sopenharmony_ci}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci#endif /* !ECHOCARD_HAS_VMIXER */
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci#endif /* ECHOCARD_HAS_DIGITAL_IO */
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_cistatic int pcm_close(struct snd_pcm_substream *substream)
50562306a36Sopenharmony_ci{
50662306a36Sopenharmony_ci	struct echoaudio *chip = snd_pcm_substream_chip(substream);
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	/* Nothing to do here. Audio is already off and pipe will be
50962306a36Sopenharmony_ci	 * freed by its callback
51062306a36Sopenharmony_ci	 */
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	mutex_lock(&chip->mode_mutex);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "pcm_open opencount=%d can_set_rate=%d, rate_set=%d",
51562306a36Sopenharmony_ci		chip->opencount, chip->can_set_rate, chip->rate_set);
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	chip->opencount--;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	switch (chip->opencount) {
52062306a36Sopenharmony_ci	case 1:
52162306a36Sopenharmony_ci		chip->can_set_rate = 1;
52262306a36Sopenharmony_ci		break;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	case 0:
52562306a36Sopenharmony_ci		chip->rate_set = 0;
52662306a36Sopenharmony_ci		break;
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	mutex_unlock(&chip->mode_mutex);
53062306a36Sopenharmony_ci	return 0;
53162306a36Sopenharmony_ci}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci/* Channel allocation and scatter-gather list setup */
53662306a36Sopenharmony_cistatic int init_engine(struct snd_pcm_substream *substream,
53762306a36Sopenharmony_ci		       struct snd_pcm_hw_params *hw_params,
53862306a36Sopenharmony_ci		       int pipe_index, int interleave)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	struct echoaudio *chip;
54162306a36Sopenharmony_ci	int err, per, rest, page, edge, offs;
54262306a36Sopenharmony_ci	struct audiopipe *pipe;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	chip = snd_pcm_substream_chip(substream);
54562306a36Sopenharmony_ci	pipe = (struct audiopipe *) substream->runtime->private_data;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	/* Sets up che hardware. If it's already initialized, reset and
54862306a36Sopenharmony_ci	 * redo with the new parameters
54962306a36Sopenharmony_ci	 */
55062306a36Sopenharmony_ci	spin_lock_irq(&chip->lock);
55162306a36Sopenharmony_ci	if (pipe->index >= 0) {
55262306a36Sopenharmony_ci		dev_dbg(chip->card->dev, "hwp_ie free(%d)\n", pipe->index);
55362306a36Sopenharmony_ci		err = free_pipes(chip, pipe);
55462306a36Sopenharmony_ci		snd_BUG_ON(err);
55562306a36Sopenharmony_ci		chip->substream[pipe->index] = NULL;
55662306a36Sopenharmony_ci	}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	err = allocate_pipes(chip, pipe, pipe_index, interleave);
55962306a36Sopenharmony_ci	if (err < 0) {
56062306a36Sopenharmony_ci		spin_unlock_irq(&chip->lock);
56162306a36Sopenharmony_ci		dev_err(chip->card->dev, "allocate_pipes(%d) err=%d\n",
56262306a36Sopenharmony_ci			pipe_index, err);
56362306a36Sopenharmony_ci		return err;
56462306a36Sopenharmony_ci	}
56562306a36Sopenharmony_ci	spin_unlock_irq(&chip->lock);
56662306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "allocate_pipes()=%d\n", pipe_index);
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	dev_dbg(chip->card->dev,
56962306a36Sopenharmony_ci		"pcm_hw_params (bufsize=%dB periods=%d persize=%dB)\n",
57062306a36Sopenharmony_ci		params_buffer_bytes(hw_params), params_periods(hw_params),
57162306a36Sopenharmony_ci		params_period_bytes(hw_params));
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	sglist_init(chip, pipe);
57462306a36Sopenharmony_ci	edge = PAGE_SIZE;
57562306a36Sopenharmony_ci	for (offs = page = per = 0; offs < params_buffer_bytes(hw_params);
57662306a36Sopenharmony_ci	     per++) {
57762306a36Sopenharmony_ci		rest = params_period_bytes(hw_params);
57862306a36Sopenharmony_ci		if (offs + rest > params_buffer_bytes(hw_params))
57962306a36Sopenharmony_ci			rest = params_buffer_bytes(hw_params) - offs;
58062306a36Sopenharmony_ci		while (rest) {
58162306a36Sopenharmony_ci			dma_addr_t addr;
58262306a36Sopenharmony_ci			addr = snd_pcm_sgbuf_get_addr(substream, offs);
58362306a36Sopenharmony_ci			if (rest <= edge - offs) {
58462306a36Sopenharmony_ci				sglist_add_mapping(chip, pipe, addr, rest);
58562306a36Sopenharmony_ci				sglist_add_irq(chip, pipe);
58662306a36Sopenharmony_ci				offs += rest;
58762306a36Sopenharmony_ci				rest = 0;
58862306a36Sopenharmony_ci			} else {
58962306a36Sopenharmony_ci				sglist_add_mapping(chip, pipe, addr,
59062306a36Sopenharmony_ci						   edge - offs);
59162306a36Sopenharmony_ci				rest -= edge - offs;
59262306a36Sopenharmony_ci				offs = edge;
59362306a36Sopenharmony_ci			}
59462306a36Sopenharmony_ci			if (offs == edge) {
59562306a36Sopenharmony_ci				edge += PAGE_SIZE;
59662306a36Sopenharmony_ci				page++;
59762306a36Sopenharmony_ci			}
59862306a36Sopenharmony_ci		}
59962306a36Sopenharmony_ci	}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	/* Close the ring buffer */
60262306a36Sopenharmony_ci	sglist_wrap(chip, pipe);
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	/* This stuff is used by the irq handler, so it must be
60562306a36Sopenharmony_ci	 * initialized before chip->substream
60662306a36Sopenharmony_ci	 */
60762306a36Sopenharmony_ci	pipe->last_period = 0;
60862306a36Sopenharmony_ci	pipe->last_counter = 0;
60962306a36Sopenharmony_ci	pipe->position = 0;
61062306a36Sopenharmony_ci	smp_wmb();
61162306a36Sopenharmony_ci	chip->substream[pipe_index] = substream;
61262306a36Sopenharmony_ci	chip->rate_set = 1;
61362306a36Sopenharmony_ci	spin_lock_irq(&chip->lock);
61462306a36Sopenharmony_ci	set_sample_rate(chip, hw_params->rate_num / hw_params->rate_den);
61562306a36Sopenharmony_ci	spin_unlock_irq(&chip->lock);
61662306a36Sopenharmony_ci	return 0;
61762306a36Sopenharmony_ci}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_cistatic int pcm_analog_in_hw_params(struct snd_pcm_substream *substream,
62262306a36Sopenharmony_ci				   struct snd_pcm_hw_params *hw_params)
62362306a36Sopenharmony_ci{
62462306a36Sopenharmony_ci	struct echoaudio *chip = snd_pcm_substream_chip(substream);
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	return init_engine(substream, hw_params, px_analog_in(chip) +
62762306a36Sopenharmony_ci			substream->number, params_channels(hw_params));
62862306a36Sopenharmony_ci}
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_cistatic int pcm_analog_out_hw_params(struct snd_pcm_substream *substream,
63362306a36Sopenharmony_ci				    struct snd_pcm_hw_params *hw_params)
63462306a36Sopenharmony_ci{
63562306a36Sopenharmony_ci	return init_engine(substream, hw_params, substream->number,
63662306a36Sopenharmony_ci			   params_channels(hw_params));
63762306a36Sopenharmony_ci}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_DIGITAL_IO
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_cistatic int pcm_digital_in_hw_params(struct snd_pcm_substream *substream,
64462306a36Sopenharmony_ci				    struct snd_pcm_hw_params *hw_params)
64562306a36Sopenharmony_ci{
64662306a36Sopenharmony_ci	struct echoaudio *chip = snd_pcm_substream_chip(substream);
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	return init_engine(substream, hw_params, px_digital_in(chip) +
64962306a36Sopenharmony_ci			substream->number, params_channels(hw_params));
65062306a36Sopenharmony_ci}
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci#ifndef ECHOCARD_HAS_VMIXER	/* See the note in snd_echo_new_pcm() */
65562306a36Sopenharmony_cistatic int pcm_digital_out_hw_params(struct snd_pcm_substream *substream,
65662306a36Sopenharmony_ci				     struct snd_pcm_hw_params *hw_params)
65762306a36Sopenharmony_ci{
65862306a36Sopenharmony_ci	struct echoaudio *chip = snd_pcm_substream_chip(substream);
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	return init_engine(substream, hw_params, px_digital_out(chip) +
66162306a36Sopenharmony_ci			substream->number, params_channels(hw_params));
66262306a36Sopenharmony_ci}
66362306a36Sopenharmony_ci#endif /* !ECHOCARD_HAS_VMIXER */
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci#endif /* ECHOCARD_HAS_DIGITAL_IO */
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_cistatic int pcm_hw_free(struct snd_pcm_substream *substream)
67062306a36Sopenharmony_ci{
67162306a36Sopenharmony_ci	struct echoaudio *chip;
67262306a36Sopenharmony_ci	struct audiopipe *pipe;
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	chip = snd_pcm_substream_chip(substream);
67562306a36Sopenharmony_ci	pipe = (struct audiopipe *) substream->runtime->private_data;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	spin_lock_irq(&chip->lock);
67862306a36Sopenharmony_ci	if (pipe->index >= 0) {
67962306a36Sopenharmony_ci		dev_dbg(chip->card->dev, "pcm_hw_free(%d)\n", pipe->index);
68062306a36Sopenharmony_ci		free_pipes(chip, pipe);
68162306a36Sopenharmony_ci		chip->substream[pipe->index] = NULL;
68262306a36Sopenharmony_ci		pipe->index = -1;
68362306a36Sopenharmony_ci	}
68462306a36Sopenharmony_ci	spin_unlock_irq(&chip->lock);
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	return 0;
68762306a36Sopenharmony_ci}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_cistatic int pcm_prepare(struct snd_pcm_substream *substream)
69262306a36Sopenharmony_ci{
69362306a36Sopenharmony_ci	struct echoaudio *chip = snd_pcm_substream_chip(substream);
69462306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
69562306a36Sopenharmony_ci	struct audioformat format;
69662306a36Sopenharmony_ci	int pipe_index = ((struct audiopipe *)runtime->private_data)->index;
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "Prepare rate=%d format=%d channels=%d\n",
69962306a36Sopenharmony_ci		runtime->rate, runtime->format, runtime->channels);
70062306a36Sopenharmony_ci	format.interleave = runtime->channels;
70162306a36Sopenharmony_ci	format.data_are_bigendian = 0;
70262306a36Sopenharmony_ci	format.mono_to_stereo = 0;
70362306a36Sopenharmony_ci	switch (runtime->format) {
70462306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_U8:
70562306a36Sopenharmony_ci		format.bits_per_sample = 8;
70662306a36Sopenharmony_ci		break;
70762306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_S16_LE:
70862306a36Sopenharmony_ci		format.bits_per_sample = 16;
70962306a36Sopenharmony_ci		break;
71062306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_S24_3LE:
71162306a36Sopenharmony_ci		format.bits_per_sample = 24;
71262306a36Sopenharmony_ci		break;
71362306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_S32_BE:
71462306a36Sopenharmony_ci		format.data_are_bigendian = 1;
71562306a36Sopenharmony_ci		fallthrough;
71662306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_S32_LE:
71762306a36Sopenharmony_ci		format.bits_per_sample = 32;
71862306a36Sopenharmony_ci		break;
71962306a36Sopenharmony_ci	default:
72062306a36Sopenharmony_ci		dev_err(chip->card->dev,
72162306a36Sopenharmony_ci			"Prepare error: unsupported format %d\n",
72262306a36Sopenharmony_ci			runtime->format);
72362306a36Sopenharmony_ci		return -EINVAL;
72462306a36Sopenharmony_ci	}
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	if (snd_BUG_ON(pipe_index >= px_num(chip)))
72762306a36Sopenharmony_ci		return -EINVAL;
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	/*
73062306a36Sopenharmony_ci	 * We passed checks we can do independently; now take
73162306a36Sopenharmony_ci	 * exclusive control
73262306a36Sopenharmony_ci	 */
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	spin_lock_irq(&chip->lock);
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	if (snd_BUG_ON(!is_pipe_allocated(chip, pipe_index))) {
73762306a36Sopenharmony_ci		spin_unlock_irq(&chip->lock);
73862306a36Sopenharmony_ci		return -EINVAL;
73962306a36Sopenharmony_ci	}
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	set_audio_format(chip, pipe_index, &format);
74262306a36Sopenharmony_ci	spin_unlock_irq(&chip->lock);
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	return 0;
74562306a36Sopenharmony_ci}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_cistatic int pcm_trigger(struct snd_pcm_substream *substream, int cmd)
75062306a36Sopenharmony_ci{
75162306a36Sopenharmony_ci	struct echoaudio *chip = snd_pcm_substream_chip(substream);
75262306a36Sopenharmony_ci	struct audiopipe *pipe;
75362306a36Sopenharmony_ci	int i, err;
75462306a36Sopenharmony_ci	u32 channelmask = 0;
75562306a36Sopenharmony_ci	struct snd_pcm_substream *s;
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	snd_pcm_group_for_each_entry(s, substream) {
75862306a36Sopenharmony_ci		for (i = 0; i < DSP_MAXPIPES; i++) {
75962306a36Sopenharmony_ci			if (s == chip->substream[i]) {
76062306a36Sopenharmony_ci				channelmask |= 1 << i;
76162306a36Sopenharmony_ci				snd_pcm_trigger_done(s, substream);
76262306a36Sopenharmony_ci			}
76362306a36Sopenharmony_ci		}
76462306a36Sopenharmony_ci	}
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	spin_lock(&chip->lock);
76762306a36Sopenharmony_ci	switch (cmd) {
76862306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
76962306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
77062306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
77162306a36Sopenharmony_ci		for (i = 0; i < DSP_MAXPIPES; i++) {
77262306a36Sopenharmony_ci			if (channelmask & (1 << i)) {
77362306a36Sopenharmony_ci				pipe = chip->substream[i]->runtime->private_data;
77462306a36Sopenharmony_ci				switch (pipe->state) {
77562306a36Sopenharmony_ci				case PIPE_STATE_STOPPED:
77662306a36Sopenharmony_ci					pipe->last_period = 0;
77762306a36Sopenharmony_ci					pipe->last_counter = 0;
77862306a36Sopenharmony_ci					pipe->position = 0;
77962306a36Sopenharmony_ci					*pipe->dma_counter = 0;
78062306a36Sopenharmony_ci					fallthrough;
78162306a36Sopenharmony_ci				case PIPE_STATE_PAUSED:
78262306a36Sopenharmony_ci					pipe->state = PIPE_STATE_STARTED;
78362306a36Sopenharmony_ci					break;
78462306a36Sopenharmony_ci				case PIPE_STATE_STARTED:
78562306a36Sopenharmony_ci					break;
78662306a36Sopenharmony_ci				}
78762306a36Sopenharmony_ci			}
78862306a36Sopenharmony_ci		}
78962306a36Sopenharmony_ci		err = start_transport(chip, channelmask,
79062306a36Sopenharmony_ci				      chip->pipe_cyclic_mask);
79162306a36Sopenharmony_ci		break;
79262306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
79362306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
79462306a36Sopenharmony_ci		for (i = 0; i < DSP_MAXPIPES; i++) {
79562306a36Sopenharmony_ci			if (channelmask & (1 << i)) {
79662306a36Sopenharmony_ci				pipe = chip->substream[i]->runtime->private_data;
79762306a36Sopenharmony_ci				pipe->state = PIPE_STATE_STOPPED;
79862306a36Sopenharmony_ci			}
79962306a36Sopenharmony_ci		}
80062306a36Sopenharmony_ci		err = stop_transport(chip, channelmask);
80162306a36Sopenharmony_ci		break;
80262306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
80362306a36Sopenharmony_ci		for (i = 0; i < DSP_MAXPIPES; i++) {
80462306a36Sopenharmony_ci			if (channelmask & (1 << i)) {
80562306a36Sopenharmony_ci				pipe = chip->substream[i]->runtime->private_data;
80662306a36Sopenharmony_ci				pipe->state = PIPE_STATE_PAUSED;
80762306a36Sopenharmony_ci			}
80862306a36Sopenharmony_ci		}
80962306a36Sopenharmony_ci		err = pause_transport(chip, channelmask);
81062306a36Sopenharmony_ci		break;
81162306a36Sopenharmony_ci	default:
81262306a36Sopenharmony_ci		err = -EINVAL;
81362306a36Sopenharmony_ci	}
81462306a36Sopenharmony_ci	spin_unlock(&chip->lock);
81562306a36Sopenharmony_ci	return err;
81662306a36Sopenharmony_ci}
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_cistatic snd_pcm_uframes_t pcm_pointer(struct snd_pcm_substream *substream)
82162306a36Sopenharmony_ci{
82262306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
82362306a36Sopenharmony_ci	struct audiopipe *pipe = runtime->private_data;
82462306a36Sopenharmony_ci	u32 counter, step;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	/*
82762306a36Sopenharmony_ci	 * IRQ handling runs concurrently. Do not share tracking of
82862306a36Sopenharmony_ci	 * counter with it, which would race or require locking
82962306a36Sopenharmony_ci	 */
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	counter = le32_to_cpu(*pipe->dma_counter);  /* presumed atomic */
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	step = counter - pipe->last_counter;  /* handles wrapping */
83462306a36Sopenharmony_ci	pipe->last_counter = counter;
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	/* counter doesn't neccessarily wrap on a multiple of
83762306a36Sopenharmony_ci	 * buffer_size, so can't derive the position; must
83862306a36Sopenharmony_ci	 * accumulate */
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	pipe->position += step;
84162306a36Sopenharmony_ci	pipe->position %= frames_to_bytes(runtime, runtime->buffer_size); /* wrap */
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	return bytes_to_frames(runtime, pipe->position);
84462306a36Sopenharmony_ci}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci/* pcm *_ops structures */
84962306a36Sopenharmony_cistatic const struct snd_pcm_ops analog_playback_ops = {
85062306a36Sopenharmony_ci	.open = pcm_analog_out_open,
85162306a36Sopenharmony_ci	.close = pcm_close,
85262306a36Sopenharmony_ci	.hw_params = pcm_analog_out_hw_params,
85362306a36Sopenharmony_ci	.hw_free = pcm_hw_free,
85462306a36Sopenharmony_ci	.prepare = pcm_prepare,
85562306a36Sopenharmony_ci	.trigger = pcm_trigger,
85662306a36Sopenharmony_ci	.pointer = pcm_pointer,
85762306a36Sopenharmony_ci};
85862306a36Sopenharmony_cistatic const struct snd_pcm_ops analog_capture_ops = {
85962306a36Sopenharmony_ci	.open = pcm_analog_in_open,
86062306a36Sopenharmony_ci	.close = pcm_close,
86162306a36Sopenharmony_ci	.hw_params = pcm_analog_in_hw_params,
86262306a36Sopenharmony_ci	.hw_free = pcm_hw_free,
86362306a36Sopenharmony_ci	.prepare = pcm_prepare,
86462306a36Sopenharmony_ci	.trigger = pcm_trigger,
86562306a36Sopenharmony_ci	.pointer = pcm_pointer,
86662306a36Sopenharmony_ci};
86762306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_DIGITAL_IO
86862306a36Sopenharmony_ci#ifndef ECHOCARD_HAS_VMIXER
86962306a36Sopenharmony_cistatic const struct snd_pcm_ops digital_playback_ops = {
87062306a36Sopenharmony_ci	.open = pcm_digital_out_open,
87162306a36Sopenharmony_ci	.close = pcm_close,
87262306a36Sopenharmony_ci	.hw_params = pcm_digital_out_hw_params,
87362306a36Sopenharmony_ci	.hw_free = pcm_hw_free,
87462306a36Sopenharmony_ci	.prepare = pcm_prepare,
87562306a36Sopenharmony_ci	.trigger = pcm_trigger,
87662306a36Sopenharmony_ci	.pointer = pcm_pointer,
87762306a36Sopenharmony_ci};
87862306a36Sopenharmony_ci#endif /* !ECHOCARD_HAS_VMIXER */
87962306a36Sopenharmony_cistatic const struct snd_pcm_ops digital_capture_ops = {
88062306a36Sopenharmony_ci	.open = pcm_digital_in_open,
88162306a36Sopenharmony_ci	.close = pcm_close,
88262306a36Sopenharmony_ci	.hw_params = pcm_digital_in_hw_params,
88362306a36Sopenharmony_ci	.hw_free = pcm_hw_free,
88462306a36Sopenharmony_ci	.prepare = pcm_prepare,
88562306a36Sopenharmony_ci	.trigger = pcm_trigger,
88662306a36Sopenharmony_ci	.pointer = pcm_pointer,
88762306a36Sopenharmony_ci};
88862306a36Sopenharmony_ci#endif /* ECHOCARD_HAS_DIGITAL_IO */
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci/* Preallocate memory only for the first substream because it's the most
89362306a36Sopenharmony_ci * used one
89462306a36Sopenharmony_ci */
89562306a36Sopenharmony_cistatic void snd_echo_preallocate_pages(struct snd_pcm *pcm, struct device *dev)
89662306a36Sopenharmony_ci{
89762306a36Sopenharmony_ci	struct snd_pcm_substream *ss;
89862306a36Sopenharmony_ci	int stream;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	for (stream = 0; stream < 2; stream++)
90162306a36Sopenharmony_ci		for (ss = pcm->streams[stream].substream; ss; ss = ss->next)
90262306a36Sopenharmony_ci			snd_pcm_set_managed_buffer(ss, SNDRV_DMA_TYPE_DEV_SG,
90362306a36Sopenharmony_ci						   dev,
90462306a36Sopenharmony_ci						   ss->number ? 0 : 128<<10,
90562306a36Sopenharmony_ci						   256<<10);
90662306a36Sopenharmony_ci}
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci/*<--snd_echo_probe() */
91162306a36Sopenharmony_cistatic int snd_echo_new_pcm(struct echoaudio *chip)
91262306a36Sopenharmony_ci{
91362306a36Sopenharmony_ci	struct snd_pcm *pcm;
91462306a36Sopenharmony_ci	int err;
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_VMIXER
91762306a36Sopenharmony_ci	/* This card has a Vmixer, that is there is no direct mapping from PCM
91862306a36Sopenharmony_ci	streams to physical outputs. The user can mix the streams as he wishes
91962306a36Sopenharmony_ci	via control interface and it's possible to send any stream to any
92062306a36Sopenharmony_ci	output, thus it makes no sense to keep analog and digital outputs
92162306a36Sopenharmony_ci	separated */
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	/* PCM#0 Virtual outputs and analog inputs */
92462306a36Sopenharmony_ci	err = snd_pcm_new(chip->card, "PCM", 0, num_pipes_out(chip),
92562306a36Sopenharmony_ci			  num_analog_busses_in(chip), &pcm);
92662306a36Sopenharmony_ci	if (err < 0)
92762306a36Sopenharmony_ci		return err;
92862306a36Sopenharmony_ci	pcm->private_data = chip;
92962306a36Sopenharmony_ci	chip->analog_pcm = pcm;
93062306a36Sopenharmony_ci	strcpy(pcm->name, chip->card->shortname);
93162306a36Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &analog_playback_ops);
93262306a36Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &analog_capture_ops);
93362306a36Sopenharmony_ci	snd_echo_preallocate_pages(pcm, &chip->pci->dev);
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_DIGITAL_IO
93662306a36Sopenharmony_ci	/* PCM#1 Digital inputs, no outputs */
93762306a36Sopenharmony_ci	err = snd_pcm_new(chip->card, "Digital PCM", 1, 0,
93862306a36Sopenharmony_ci			  num_digital_busses_in(chip), &pcm);
93962306a36Sopenharmony_ci	if (err < 0)
94062306a36Sopenharmony_ci		return err;
94162306a36Sopenharmony_ci	pcm->private_data = chip;
94262306a36Sopenharmony_ci	chip->digital_pcm = pcm;
94362306a36Sopenharmony_ci	strcpy(pcm->name, chip->card->shortname);
94462306a36Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &digital_capture_ops);
94562306a36Sopenharmony_ci	snd_echo_preallocate_pages(pcm, &chip->pci->dev);
94662306a36Sopenharmony_ci#endif /* ECHOCARD_HAS_DIGITAL_IO */
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci#else /* ECHOCARD_HAS_VMIXER */
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	/* The card can manage substreams formed by analog and digital channels
95162306a36Sopenharmony_ci	at the same time, but I prefer to keep analog and digital channels
95262306a36Sopenharmony_ci	separated, because that mixed thing is confusing and useless. So we
95362306a36Sopenharmony_ci	register two PCM devices: */
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	/* PCM#0 Analog i/o */
95662306a36Sopenharmony_ci	err = snd_pcm_new(chip->card, "Analog PCM", 0,
95762306a36Sopenharmony_ci			  num_analog_busses_out(chip),
95862306a36Sopenharmony_ci			  num_analog_busses_in(chip), &pcm);
95962306a36Sopenharmony_ci	if (err < 0)
96062306a36Sopenharmony_ci		return err;
96162306a36Sopenharmony_ci	pcm->private_data = chip;
96262306a36Sopenharmony_ci	chip->analog_pcm = pcm;
96362306a36Sopenharmony_ci	strcpy(pcm->name, chip->card->shortname);
96462306a36Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &analog_playback_ops);
96562306a36Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &analog_capture_ops);
96662306a36Sopenharmony_ci	snd_echo_preallocate_pages(pcm, &chip->pci->dev);
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_DIGITAL_IO
96962306a36Sopenharmony_ci	/* PCM#1 Digital i/o */
97062306a36Sopenharmony_ci	err = snd_pcm_new(chip->card, "Digital PCM", 1,
97162306a36Sopenharmony_ci			  num_digital_busses_out(chip),
97262306a36Sopenharmony_ci			  num_digital_busses_in(chip), &pcm);
97362306a36Sopenharmony_ci	if (err < 0)
97462306a36Sopenharmony_ci		return err;
97562306a36Sopenharmony_ci	pcm->private_data = chip;
97662306a36Sopenharmony_ci	chip->digital_pcm = pcm;
97762306a36Sopenharmony_ci	strcpy(pcm->name, chip->card->shortname);
97862306a36Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &digital_playback_ops);
97962306a36Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &digital_capture_ops);
98062306a36Sopenharmony_ci	snd_echo_preallocate_pages(pcm, &chip->pci->dev);
98162306a36Sopenharmony_ci#endif /* ECHOCARD_HAS_DIGITAL_IO */
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci#endif /* ECHOCARD_HAS_VMIXER */
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	return 0;
98662306a36Sopenharmony_ci}
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci/******************************************************************************
99262306a36Sopenharmony_ci	Control interface
99362306a36Sopenharmony_ci******************************************************************************/
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci#if !defined(ECHOCARD_HAS_VMIXER) || defined(ECHOCARD_HAS_LINE_OUT_GAIN)
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci/******************* PCM output volume *******************/
99862306a36Sopenharmony_cistatic int snd_echo_output_gain_info(struct snd_kcontrol *kcontrol,
99962306a36Sopenharmony_ci				     struct snd_ctl_elem_info *uinfo)
100062306a36Sopenharmony_ci{
100162306a36Sopenharmony_ci	struct echoaudio *chip;
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
100462306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
100562306a36Sopenharmony_ci	uinfo->count = num_busses_out(chip);
100662306a36Sopenharmony_ci	uinfo->value.integer.min = ECHOGAIN_MINOUT;
100762306a36Sopenharmony_ci	uinfo->value.integer.max = ECHOGAIN_MAXOUT;
100862306a36Sopenharmony_ci	return 0;
100962306a36Sopenharmony_ci}
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_cistatic int snd_echo_output_gain_get(struct snd_kcontrol *kcontrol,
101262306a36Sopenharmony_ci				    struct snd_ctl_elem_value *ucontrol)
101362306a36Sopenharmony_ci{
101462306a36Sopenharmony_ci	struct echoaudio *chip;
101562306a36Sopenharmony_ci	int c;
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
101862306a36Sopenharmony_ci	for (c = 0; c < num_busses_out(chip); c++)
101962306a36Sopenharmony_ci		ucontrol->value.integer.value[c] = chip->output_gain[c];
102062306a36Sopenharmony_ci	return 0;
102162306a36Sopenharmony_ci}
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_cistatic int snd_echo_output_gain_put(struct snd_kcontrol *kcontrol,
102462306a36Sopenharmony_ci				    struct snd_ctl_elem_value *ucontrol)
102562306a36Sopenharmony_ci{
102662306a36Sopenharmony_ci	struct echoaudio *chip;
102762306a36Sopenharmony_ci	int c, changed, gain;
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	changed = 0;
103062306a36Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
103162306a36Sopenharmony_ci	spin_lock_irq(&chip->lock);
103262306a36Sopenharmony_ci	for (c = 0; c < num_busses_out(chip); c++) {
103362306a36Sopenharmony_ci		gain = ucontrol->value.integer.value[c];
103462306a36Sopenharmony_ci		/* Ignore out of range values */
103562306a36Sopenharmony_ci		if (gain < ECHOGAIN_MINOUT || gain > ECHOGAIN_MAXOUT)
103662306a36Sopenharmony_ci			continue;
103762306a36Sopenharmony_ci		if (chip->output_gain[c] != gain) {
103862306a36Sopenharmony_ci			set_output_gain(chip, c, gain);
103962306a36Sopenharmony_ci			changed = 1;
104062306a36Sopenharmony_ci		}
104162306a36Sopenharmony_ci	}
104262306a36Sopenharmony_ci	if (changed)
104362306a36Sopenharmony_ci		update_output_line_level(chip);
104462306a36Sopenharmony_ci	spin_unlock_irq(&chip->lock);
104562306a36Sopenharmony_ci	return changed;
104662306a36Sopenharmony_ci}
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_LINE_OUT_GAIN
104962306a36Sopenharmony_ci/* On the Mia this one controls the line-out volume */
105062306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_line_output_gain = {
105162306a36Sopenharmony_ci	.name = "Line Playback Volume",
105262306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
105362306a36Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
105462306a36Sopenharmony_ci		  SNDRV_CTL_ELEM_ACCESS_TLV_READ,
105562306a36Sopenharmony_ci	.info = snd_echo_output_gain_info,
105662306a36Sopenharmony_ci	.get = snd_echo_output_gain_get,
105762306a36Sopenharmony_ci	.put = snd_echo_output_gain_put,
105862306a36Sopenharmony_ci	.tlv = {.p = db_scale_output_gain},
105962306a36Sopenharmony_ci};
106062306a36Sopenharmony_ci#else
106162306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_pcm_output_gain = {
106262306a36Sopenharmony_ci	.name = "PCM Playback Volume",
106362306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
106462306a36Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,
106562306a36Sopenharmony_ci	.info = snd_echo_output_gain_info,
106662306a36Sopenharmony_ci	.get = snd_echo_output_gain_get,
106762306a36Sopenharmony_ci	.put = snd_echo_output_gain_put,
106862306a36Sopenharmony_ci	.tlv = {.p = db_scale_output_gain},
106962306a36Sopenharmony_ci};
107062306a36Sopenharmony_ci#endif
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci#endif /* !ECHOCARD_HAS_VMIXER || ECHOCARD_HAS_LINE_OUT_GAIN */
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_INPUT_GAIN
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci/******************* Analog input volume *******************/
107962306a36Sopenharmony_cistatic int snd_echo_input_gain_info(struct snd_kcontrol *kcontrol,
108062306a36Sopenharmony_ci				    struct snd_ctl_elem_info *uinfo)
108162306a36Sopenharmony_ci{
108262306a36Sopenharmony_ci	struct echoaudio *chip;
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
108562306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
108662306a36Sopenharmony_ci	uinfo->count = num_analog_busses_in(chip);
108762306a36Sopenharmony_ci	uinfo->value.integer.min = ECHOGAIN_MININP;
108862306a36Sopenharmony_ci	uinfo->value.integer.max = ECHOGAIN_MAXINP;
108962306a36Sopenharmony_ci	return 0;
109062306a36Sopenharmony_ci}
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_cistatic int snd_echo_input_gain_get(struct snd_kcontrol *kcontrol,
109362306a36Sopenharmony_ci				   struct snd_ctl_elem_value *ucontrol)
109462306a36Sopenharmony_ci{
109562306a36Sopenharmony_ci	struct echoaudio *chip;
109662306a36Sopenharmony_ci	int c;
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
109962306a36Sopenharmony_ci	for (c = 0; c < num_analog_busses_in(chip); c++)
110062306a36Sopenharmony_ci		ucontrol->value.integer.value[c] = chip->input_gain[c];
110162306a36Sopenharmony_ci	return 0;
110262306a36Sopenharmony_ci}
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_cistatic int snd_echo_input_gain_put(struct snd_kcontrol *kcontrol,
110562306a36Sopenharmony_ci				   struct snd_ctl_elem_value *ucontrol)
110662306a36Sopenharmony_ci{
110762306a36Sopenharmony_ci	struct echoaudio *chip;
110862306a36Sopenharmony_ci	int c, gain, changed;
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	changed = 0;
111162306a36Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
111262306a36Sopenharmony_ci	spin_lock_irq(&chip->lock);
111362306a36Sopenharmony_ci	for (c = 0; c < num_analog_busses_in(chip); c++) {
111462306a36Sopenharmony_ci		gain = ucontrol->value.integer.value[c];
111562306a36Sopenharmony_ci		/* Ignore out of range values */
111662306a36Sopenharmony_ci		if (gain < ECHOGAIN_MININP || gain > ECHOGAIN_MAXINP)
111762306a36Sopenharmony_ci			continue;
111862306a36Sopenharmony_ci		if (chip->input_gain[c] != gain) {
111962306a36Sopenharmony_ci			set_input_gain(chip, c, gain);
112062306a36Sopenharmony_ci			changed = 1;
112162306a36Sopenharmony_ci		}
112262306a36Sopenharmony_ci	}
112362306a36Sopenharmony_ci	if (changed)
112462306a36Sopenharmony_ci		update_input_line_level(chip);
112562306a36Sopenharmony_ci	spin_unlock_irq(&chip->lock);
112662306a36Sopenharmony_ci	return changed;
112762306a36Sopenharmony_ci}
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_input_gain, -2500, 50, 0);
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_line_input_gain = {
113262306a36Sopenharmony_ci	.name = "Line Capture Volume",
113362306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
113462306a36Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,
113562306a36Sopenharmony_ci	.info = snd_echo_input_gain_info,
113662306a36Sopenharmony_ci	.get = snd_echo_input_gain_get,
113762306a36Sopenharmony_ci	.put = snd_echo_input_gain_put,
113862306a36Sopenharmony_ci	.tlv = {.p = db_scale_input_gain},
113962306a36Sopenharmony_ci};
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci#endif /* ECHOCARD_HAS_INPUT_GAIN */
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci/************ Analog output nominal level (+4dBu / -10dBV) ***************/
114862306a36Sopenharmony_cistatic int snd_echo_output_nominal_info (struct snd_kcontrol *kcontrol,
114962306a36Sopenharmony_ci					 struct snd_ctl_elem_info *uinfo)
115062306a36Sopenharmony_ci{
115162306a36Sopenharmony_ci	struct echoaudio *chip;
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
115462306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
115562306a36Sopenharmony_ci	uinfo->count = num_analog_busses_out(chip);
115662306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
115762306a36Sopenharmony_ci	uinfo->value.integer.max = 1;
115862306a36Sopenharmony_ci	return 0;
115962306a36Sopenharmony_ci}
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_cistatic int snd_echo_output_nominal_get(struct snd_kcontrol *kcontrol,
116262306a36Sopenharmony_ci				       struct snd_ctl_elem_value *ucontrol)
116362306a36Sopenharmony_ci{
116462306a36Sopenharmony_ci	struct echoaudio *chip;
116562306a36Sopenharmony_ci	int c;
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
116862306a36Sopenharmony_ci	for (c = 0; c < num_analog_busses_out(chip); c++)
116962306a36Sopenharmony_ci		ucontrol->value.integer.value[c] = chip->nominal_level[c];
117062306a36Sopenharmony_ci	return 0;
117162306a36Sopenharmony_ci}
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_cistatic int snd_echo_output_nominal_put(struct snd_kcontrol *kcontrol,
117462306a36Sopenharmony_ci				       struct snd_ctl_elem_value *ucontrol)
117562306a36Sopenharmony_ci{
117662306a36Sopenharmony_ci	struct echoaudio *chip;
117762306a36Sopenharmony_ci	int c, changed;
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	changed = 0;
118062306a36Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
118162306a36Sopenharmony_ci	spin_lock_irq(&chip->lock);
118262306a36Sopenharmony_ci	for (c = 0; c < num_analog_busses_out(chip); c++) {
118362306a36Sopenharmony_ci		if (chip->nominal_level[c] != ucontrol->value.integer.value[c]) {
118462306a36Sopenharmony_ci			set_nominal_level(chip, c,
118562306a36Sopenharmony_ci					  ucontrol->value.integer.value[c]);
118662306a36Sopenharmony_ci			changed = 1;
118762306a36Sopenharmony_ci		}
118862306a36Sopenharmony_ci	}
118962306a36Sopenharmony_ci	if (changed)
119062306a36Sopenharmony_ci		update_output_line_level(chip);
119162306a36Sopenharmony_ci	spin_unlock_irq(&chip->lock);
119262306a36Sopenharmony_ci	return changed;
119362306a36Sopenharmony_ci}
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_output_nominal_level = {
119662306a36Sopenharmony_ci	.name = "Line Playback Switch (-10dBV)",
119762306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
119862306a36Sopenharmony_ci	.info = snd_echo_output_nominal_info,
119962306a36Sopenharmony_ci	.get = snd_echo_output_nominal_get,
120062306a36Sopenharmony_ci	.put = snd_echo_output_nominal_put,
120162306a36Sopenharmony_ci};
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci#endif /* ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL */
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_INPUT_NOMINAL_LEVEL
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci/*************** Analog input nominal level (+4dBu / -10dBV) ***************/
121062306a36Sopenharmony_cistatic int snd_echo_input_nominal_info(struct snd_kcontrol *kcontrol,
121162306a36Sopenharmony_ci				       struct snd_ctl_elem_info *uinfo)
121262306a36Sopenharmony_ci{
121362306a36Sopenharmony_ci	struct echoaudio *chip;
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
121662306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
121762306a36Sopenharmony_ci	uinfo->count = num_analog_busses_in(chip);
121862306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
121962306a36Sopenharmony_ci	uinfo->value.integer.max = 1;
122062306a36Sopenharmony_ci	return 0;
122162306a36Sopenharmony_ci}
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_cistatic int snd_echo_input_nominal_get(struct snd_kcontrol *kcontrol,
122462306a36Sopenharmony_ci				      struct snd_ctl_elem_value *ucontrol)
122562306a36Sopenharmony_ci{
122662306a36Sopenharmony_ci	struct echoaudio *chip;
122762306a36Sopenharmony_ci	int c;
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
123062306a36Sopenharmony_ci	for (c = 0; c < num_analog_busses_in(chip); c++)
123162306a36Sopenharmony_ci		ucontrol->value.integer.value[c] =
123262306a36Sopenharmony_ci			chip->nominal_level[bx_analog_in(chip) + c];
123362306a36Sopenharmony_ci	return 0;
123462306a36Sopenharmony_ci}
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_cistatic int snd_echo_input_nominal_put(struct snd_kcontrol *kcontrol,
123762306a36Sopenharmony_ci				      struct snd_ctl_elem_value *ucontrol)
123862306a36Sopenharmony_ci{
123962306a36Sopenharmony_ci	struct echoaudio *chip;
124062306a36Sopenharmony_ci	int c, changed;
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	changed = 0;
124362306a36Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
124462306a36Sopenharmony_ci	spin_lock_irq(&chip->lock);
124562306a36Sopenharmony_ci	for (c = 0; c < num_analog_busses_in(chip); c++) {
124662306a36Sopenharmony_ci		if (chip->nominal_level[bx_analog_in(chip) + c] !=
124762306a36Sopenharmony_ci		    ucontrol->value.integer.value[c]) {
124862306a36Sopenharmony_ci			set_nominal_level(chip, bx_analog_in(chip) + c,
124962306a36Sopenharmony_ci					  ucontrol->value.integer.value[c]);
125062306a36Sopenharmony_ci			changed = 1;
125162306a36Sopenharmony_ci		}
125262306a36Sopenharmony_ci	}
125362306a36Sopenharmony_ci	if (changed)
125462306a36Sopenharmony_ci		update_output_line_level(chip);	/* "Output" is not a mistake
125562306a36Sopenharmony_ci						 * here.
125662306a36Sopenharmony_ci						 */
125762306a36Sopenharmony_ci	spin_unlock_irq(&chip->lock);
125862306a36Sopenharmony_ci	return changed;
125962306a36Sopenharmony_ci}
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_intput_nominal_level = {
126262306a36Sopenharmony_ci	.name = "Line Capture Switch (-10dBV)",
126362306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
126462306a36Sopenharmony_ci	.info = snd_echo_input_nominal_info,
126562306a36Sopenharmony_ci	.get = snd_echo_input_nominal_get,
126662306a36Sopenharmony_ci	.put = snd_echo_input_nominal_put,
126762306a36Sopenharmony_ci};
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci#endif /* ECHOCARD_HAS_INPUT_NOMINAL_LEVEL */
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_MONITOR
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_ci/******************* Monitor mixer *******************/
127662306a36Sopenharmony_cistatic int snd_echo_mixer_info(struct snd_kcontrol *kcontrol,
127762306a36Sopenharmony_ci			       struct snd_ctl_elem_info *uinfo)
127862306a36Sopenharmony_ci{
127962306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
128062306a36Sopenharmony_ci	uinfo->count = 1;
128162306a36Sopenharmony_ci	uinfo->value.integer.min = ECHOGAIN_MINOUT;
128262306a36Sopenharmony_ci	uinfo->value.integer.max = ECHOGAIN_MAXOUT;
128362306a36Sopenharmony_ci	return 0;
128462306a36Sopenharmony_ci}
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_cistatic int snd_echo_mixer_get(struct snd_kcontrol *kcontrol,
128762306a36Sopenharmony_ci			      struct snd_ctl_elem_value *ucontrol)
128862306a36Sopenharmony_ci{
128962306a36Sopenharmony_ci	struct echoaudio *chip = snd_kcontrol_chip(kcontrol);
129062306a36Sopenharmony_ci	unsigned int out = ucontrol->id.index / num_busses_in(chip);
129162306a36Sopenharmony_ci	unsigned int in = ucontrol->id.index % num_busses_in(chip);
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	if (out >= ECHO_MAXAUDIOOUTPUTS || in >= ECHO_MAXAUDIOINPUTS)
129462306a36Sopenharmony_ci		return -EINVAL;
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = chip->monitor_gain[out][in];
129762306a36Sopenharmony_ci	return 0;
129862306a36Sopenharmony_ci}
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_cistatic int snd_echo_mixer_put(struct snd_kcontrol *kcontrol,
130162306a36Sopenharmony_ci			      struct snd_ctl_elem_value *ucontrol)
130262306a36Sopenharmony_ci{
130362306a36Sopenharmony_ci	struct echoaudio *chip;
130462306a36Sopenharmony_ci	int changed,  gain;
130562306a36Sopenharmony_ci	unsigned int out, in;
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	changed = 0;
130862306a36Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
130962306a36Sopenharmony_ci	out = ucontrol->id.index / num_busses_in(chip);
131062306a36Sopenharmony_ci	in = ucontrol->id.index % num_busses_in(chip);
131162306a36Sopenharmony_ci	if (out >= ECHO_MAXAUDIOOUTPUTS || in >= ECHO_MAXAUDIOINPUTS)
131262306a36Sopenharmony_ci		return -EINVAL;
131362306a36Sopenharmony_ci	gain = ucontrol->value.integer.value[0];
131462306a36Sopenharmony_ci	if (gain < ECHOGAIN_MINOUT || gain > ECHOGAIN_MAXOUT)
131562306a36Sopenharmony_ci		return -EINVAL;
131662306a36Sopenharmony_ci	if (chip->monitor_gain[out][in] != gain) {
131762306a36Sopenharmony_ci		spin_lock_irq(&chip->lock);
131862306a36Sopenharmony_ci		set_monitor_gain(chip, out, in, gain);
131962306a36Sopenharmony_ci		update_output_line_level(chip);
132062306a36Sopenharmony_ci		spin_unlock_irq(&chip->lock);
132162306a36Sopenharmony_ci		changed = 1;
132262306a36Sopenharmony_ci	}
132362306a36Sopenharmony_ci	return changed;
132462306a36Sopenharmony_ci}
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_cistatic struct snd_kcontrol_new snd_echo_monitor_mixer = {
132762306a36Sopenharmony_ci	.name = "Monitor Mixer Volume",
132862306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
132962306a36Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,
133062306a36Sopenharmony_ci	.info = snd_echo_mixer_info,
133162306a36Sopenharmony_ci	.get = snd_echo_mixer_get,
133262306a36Sopenharmony_ci	.put = snd_echo_mixer_put,
133362306a36Sopenharmony_ci	.tlv = {.p = db_scale_output_gain},
133462306a36Sopenharmony_ci};
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci#endif /* ECHOCARD_HAS_MONITOR */
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_VMIXER
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci/******************* Vmixer *******************/
134362306a36Sopenharmony_cistatic int snd_echo_vmixer_info(struct snd_kcontrol *kcontrol,
134462306a36Sopenharmony_ci				struct snd_ctl_elem_info *uinfo)
134562306a36Sopenharmony_ci{
134662306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
134762306a36Sopenharmony_ci	uinfo->count = 1;
134862306a36Sopenharmony_ci	uinfo->value.integer.min = ECHOGAIN_MINOUT;
134962306a36Sopenharmony_ci	uinfo->value.integer.max = ECHOGAIN_MAXOUT;
135062306a36Sopenharmony_ci	return 0;
135162306a36Sopenharmony_ci}
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_cistatic int snd_echo_vmixer_get(struct snd_kcontrol *kcontrol,
135462306a36Sopenharmony_ci			       struct snd_ctl_elem_value *ucontrol)
135562306a36Sopenharmony_ci{
135662306a36Sopenharmony_ci	struct echoaudio *chip;
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
135962306a36Sopenharmony_ci	ucontrol->value.integer.value[0] =
136062306a36Sopenharmony_ci		chip->vmixer_gain[ucontrol->id.index / num_pipes_out(chip)]
136162306a36Sopenharmony_ci			[ucontrol->id.index % num_pipes_out(chip)];
136262306a36Sopenharmony_ci	return 0;
136362306a36Sopenharmony_ci}
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_cistatic int snd_echo_vmixer_put(struct snd_kcontrol *kcontrol,
136662306a36Sopenharmony_ci			       struct snd_ctl_elem_value *ucontrol)
136762306a36Sopenharmony_ci{
136862306a36Sopenharmony_ci	struct echoaudio *chip;
136962306a36Sopenharmony_ci	int gain, changed;
137062306a36Sopenharmony_ci	short vch, out;
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci	changed = 0;
137362306a36Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
137462306a36Sopenharmony_ci	out = ucontrol->id.index / num_pipes_out(chip);
137562306a36Sopenharmony_ci	vch = ucontrol->id.index % num_pipes_out(chip);
137662306a36Sopenharmony_ci	gain = ucontrol->value.integer.value[0];
137762306a36Sopenharmony_ci	if (gain < ECHOGAIN_MINOUT || gain > ECHOGAIN_MAXOUT)
137862306a36Sopenharmony_ci		return -EINVAL;
137962306a36Sopenharmony_ci	if (chip->vmixer_gain[out][vch] != ucontrol->value.integer.value[0]) {
138062306a36Sopenharmony_ci		spin_lock_irq(&chip->lock);
138162306a36Sopenharmony_ci		set_vmixer_gain(chip, out, vch, ucontrol->value.integer.value[0]);
138262306a36Sopenharmony_ci		update_vmixer_level(chip);
138362306a36Sopenharmony_ci		spin_unlock_irq(&chip->lock);
138462306a36Sopenharmony_ci		changed = 1;
138562306a36Sopenharmony_ci	}
138662306a36Sopenharmony_ci	return changed;
138762306a36Sopenharmony_ci}
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_cistatic struct snd_kcontrol_new snd_echo_vmixer = {
139062306a36Sopenharmony_ci	.name = "VMixer Volume",
139162306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
139262306a36Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,
139362306a36Sopenharmony_ci	.info = snd_echo_vmixer_info,
139462306a36Sopenharmony_ci	.get = snd_echo_vmixer_get,
139562306a36Sopenharmony_ci	.put = snd_echo_vmixer_put,
139662306a36Sopenharmony_ci	.tlv = {.p = db_scale_output_gain},
139762306a36Sopenharmony_ci};
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci#endif /* ECHOCARD_HAS_VMIXER */
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_DIGITAL_MODE_SWITCH
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_ci/******************* Digital mode switch *******************/
140662306a36Sopenharmony_cistatic int snd_echo_digital_mode_info(struct snd_kcontrol *kcontrol,
140762306a36Sopenharmony_ci				      struct snd_ctl_elem_info *uinfo)
140862306a36Sopenharmony_ci{
140962306a36Sopenharmony_ci	static const char * const names[4] = {
141062306a36Sopenharmony_ci		"S/PDIF Coaxial", "S/PDIF Optical", "ADAT Optical",
141162306a36Sopenharmony_ci		"S/PDIF Cdrom"
141262306a36Sopenharmony_ci	};
141362306a36Sopenharmony_ci	struct echoaudio *chip;
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
141662306a36Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, chip->num_digital_modes, names);
141762306a36Sopenharmony_ci}
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_cistatic int snd_echo_digital_mode_get(struct snd_kcontrol *kcontrol,
142062306a36Sopenharmony_ci				     struct snd_ctl_elem_value *ucontrol)
142162306a36Sopenharmony_ci{
142262306a36Sopenharmony_ci	struct echoaudio *chip;
142362306a36Sopenharmony_ci	int i, mode;
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
142662306a36Sopenharmony_ci	mode = chip->digital_mode;
142762306a36Sopenharmony_ci	for (i = chip->num_digital_modes - 1; i >= 0; i--)
142862306a36Sopenharmony_ci		if (mode == chip->digital_mode_list[i]) {
142962306a36Sopenharmony_ci			ucontrol->value.enumerated.item[0] = i;
143062306a36Sopenharmony_ci			break;
143162306a36Sopenharmony_ci		}
143262306a36Sopenharmony_ci	return 0;
143362306a36Sopenharmony_ci}
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_cistatic int snd_echo_digital_mode_put(struct snd_kcontrol *kcontrol,
143662306a36Sopenharmony_ci				     struct snd_ctl_elem_value *ucontrol)
143762306a36Sopenharmony_ci{
143862306a36Sopenharmony_ci	struct echoaudio *chip;
143962306a36Sopenharmony_ci	int changed;
144062306a36Sopenharmony_ci	unsigned short emode, dmode;
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	changed = 0;
144362306a36Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_ci	emode = ucontrol->value.enumerated.item[0];
144662306a36Sopenharmony_ci	if (emode >= chip->num_digital_modes)
144762306a36Sopenharmony_ci		return -EINVAL;
144862306a36Sopenharmony_ci	dmode = chip->digital_mode_list[emode];
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	if (dmode != chip->digital_mode) {
145162306a36Sopenharmony_ci		/* mode_mutex is required to make this operation atomic wrt
145262306a36Sopenharmony_ci		pcm_digital_*_open() and set_input_clock() functions. */
145362306a36Sopenharmony_ci		mutex_lock(&chip->mode_mutex);
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci		/* Do not allow the user to change the digital mode when a pcm
145662306a36Sopenharmony_ci		device is open because it also changes the number of channels
145762306a36Sopenharmony_ci		and the allowed sample rates */
145862306a36Sopenharmony_ci		if (chip->opencount) {
145962306a36Sopenharmony_ci			changed = -EAGAIN;
146062306a36Sopenharmony_ci		} else {
146162306a36Sopenharmony_ci			changed = set_digital_mode(chip, dmode);
146262306a36Sopenharmony_ci			/* If we had to change the clock source, report it */
146362306a36Sopenharmony_ci			if (changed > 0 && chip->clock_src_ctl) {
146462306a36Sopenharmony_ci				snd_ctl_notify(chip->card,
146562306a36Sopenharmony_ci					       SNDRV_CTL_EVENT_MASK_VALUE,
146662306a36Sopenharmony_ci					       &chip->clock_src_ctl->id);
146762306a36Sopenharmony_ci				dev_dbg(chip->card->dev,
146862306a36Sopenharmony_ci					"SDM() =%d\n", changed);
146962306a36Sopenharmony_ci			}
147062306a36Sopenharmony_ci			if (changed >= 0)
147162306a36Sopenharmony_ci				changed = 1;	/* No errors */
147262306a36Sopenharmony_ci		}
147362306a36Sopenharmony_ci		mutex_unlock(&chip->mode_mutex);
147462306a36Sopenharmony_ci	}
147562306a36Sopenharmony_ci	return changed;
147662306a36Sopenharmony_ci}
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_digital_mode_switch = {
147962306a36Sopenharmony_ci	.name = "Digital mode Switch",
148062306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_CARD,
148162306a36Sopenharmony_ci	.info = snd_echo_digital_mode_info,
148262306a36Sopenharmony_ci	.get = snd_echo_digital_mode_get,
148362306a36Sopenharmony_ci	.put = snd_echo_digital_mode_put,
148462306a36Sopenharmony_ci};
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci#endif /* ECHOCARD_HAS_DIGITAL_MODE_SWITCH */
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_DIGITAL_IO
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci/******************* S/PDIF mode switch *******************/
149362306a36Sopenharmony_cistatic int snd_echo_spdif_mode_info(struct snd_kcontrol *kcontrol,
149462306a36Sopenharmony_ci				    struct snd_ctl_elem_info *uinfo)
149562306a36Sopenharmony_ci{
149662306a36Sopenharmony_ci	static const char * const names[2] = {"Consumer", "Professional"};
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, 2, names);
149962306a36Sopenharmony_ci}
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_cistatic int snd_echo_spdif_mode_get(struct snd_kcontrol *kcontrol,
150262306a36Sopenharmony_ci				   struct snd_ctl_elem_value *ucontrol)
150362306a36Sopenharmony_ci{
150462306a36Sopenharmony_ci	struct echoaudio *chip;
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
150762306a36Sopenharmony_ci	ucontrol->value.enumerated.item[0] = !!chip->professional_spdif;
150862306a36Sopenharmony_ci	return 0;
150962306a36Sopenharmony_ci}
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_cistatic int snd_echo_spdif_mode_put(struct snd_kcontrol *kcontrol,
151262306a36Sopenharmony_ci				   struct snd_ctl_elem_value *ucontrol)
151362306a36Sopenharmony_ci{
151462306a36Sopenharmony_ci	struct echoaudio *chip;
151562306a36Sopenharmony_ci	int mode;
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
151862306a36Sopenharmony_ci	mode = !!ucontrol->value.enumerated.item[0];
151962306a36Sopenharmony_ci	if (mode != chip->professional_spdif) {
152062306a36Sopenharmony_ci		spin_lock_irq(&chip->lock);
152162306a36Sopenharmony_ci		set_professional_spdif(chip, mode);
152262306a36Sopenharmony_ci		spin_unlock_irq(&chip->lock);
152362306a36Sopenharmony_ci		return 1;
152462306a36Sopenharmony_ci	}
152562306a36Sopenharmony_ci	return 0;
152662306a36Sopenharmony_ci}
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_spdif_mode_switch = {
152962306a36Sopenharmony_ci	.name = "S/PDIF mode Switch",
153062306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_CARD,
153162306a36Sopenharmony_ci	.info = snd_echo_spdif_mode_info,
153262306a36Sopenharmony_ci	.get = snd_echo_spdif_mode_get,
153362306a36Sopenharmony_ci	.put = snd_echo_spdif_mode_put,
153462306a36Sopenharmony_ci};
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_ci#endif /* ECHOCARD_HAS_DIGITAL_IO */
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_EXTERNAL_CLOCK
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ci/******************* Select input clock source *******************/
154362306a36Sopenharmony_cistatic int snd_echo_clock_source_info(struct snd_kcontrol *kcontrol,
154462306a36Sopenharmony_ci				      struct snd_ctl_elem_info *uinfo)
154562306a36Sopenharmony_ci{
154662306a36Sopenharmony_ci	static const char * const names[8] = {
154762306a36Sopenharmony_ci		"Internal", "Word", "Super", "S/PDIF", "ADAT", "ESync",
154862306a36Sopenharmony_ci		"ESync96", "MTC"
154962306a36Sopenharmony_ci	};
155062306a36Sopenharmony_ci	struct echoaudio *chip;
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
155362306a36Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, chip->num_clock_sources, names);
155462306a36Sopenharmony_ci}
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_cistatic int snd_echo_clock_source_get(struct snd_kcontrol *kcontrol,
155762306a36Sopenharmony_ci				     struct snd_ctl_elem_value *ucontrol)
155862306a36Sopenharmony_ci{
155962306a36Sopenharmony_ci	struct echoaudio *chip;
156062306a36Sopenharmony_ci	int i, clock;
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
156362306a36Sopenharmony_ci	clock = chip->input_clock;
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_ci	for (i = 0; i < chip->num_clock_sources; i++)
156662306a36Sopenharmony_ci		if (clock == chip->clock_source_list[i])
156762306a36Sopenharmony_ci			ucontrol->value.enumerated.item[0] = i;
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_ci	return 0;
157062306a36Sopenharmony_ci}
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_cistatic int snd_echo_clock_source_put(struct snd_kcontrol *kcontrol,
157362306a36Sopenharmony_ci				     struct snd_ctl_elem_value *ucontrol)
157462306a36Sopenharmony_ci{
157562306a36Sopenharmony_ci	struct echoaudio *chip;
157662306a36Sopenharmony_ci	int changed;
157762306a36Sopenharmony_ci	unsigned int eclock, dclock;
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_ci	changed = 0;
158062306a36Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
158162306a36Sopenharmony_ci	eclock = ucontrol->value.enumerated.item[0];
158262306a36Sopenharmony_ci	if (eclock >= chip->input_clock_types)
158362306a36Sopenharmony_ci		return -EINVAL;
158462306a36Sopenharmony_ci	dclock = chip->clock_source_list[eclock];
158562306a36Sopenharmony_ci	if (chip->input_clock != dclock) {
158662306a36Sopenharmony_ci		mutex_lock(&chip->mode_mutex);
158762306a36Sopenharmony_ci		spin_lock_irq(&chip->lock);
158862306a36Sopenharmony_ci		changed = set_input_clock(chip, dclock);
158962306a36Sopenharmony_ci		if (!changed)
159062306a36Sopenharmony_ci			changed = 1;	/* no errors */
159162306a36Sopenharmony_ci		spin_unlock_irq(&chip->lock);
159262306a36Sopenharmony_ci		mutex_unlock(&chip->mode_mutex);
159362306a36Sopenharmony_ci	}
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci	if (changed < 0)
159662306a36Sopenharmony_ci		dev_dbg(chip->card->dev,
159762306a36Sopenharmony_ci			"seticlk val%d err 0x%x\n", dclock, changed);
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci	return changed;
160062306a36Sopenharmony_ci}
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_clock_source_switch = {
160362306a36Sopenharmony_ci	.name = "Sample Clock Source",
160462306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_PCM,
160562306a36Sopenharmony_ci	.info = snd_echo_clock_source_info,
160662306a36Sopenharmony_ci	.get = snd_echo_clock_source_get,
160762306a36Sopenharmony_ci	.put = snd_echo_clock_source_put,
160862306a36Sopenharmony_ci};
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_ci#endif /* ECHOCARD_HAS_EXTERNAL_CLOCK */
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_PHANTOM_POWER
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_ci/******************* Phantom power switch *******************/
161762306a36Sopenharmony_ci#define snd_echo_phantom_power_info	snd_ctl_boolean_mono_info
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_cistatic int snd_echo_phantom_power_get(struct snd_kcontrol *kcontrol,
162062306a36Sopenharmony_ci				      struct snd_ctl_elem_value *ucontrol)
162162306a36Sopenharmony_ci{
162262306a36Sopenharmony_ci	struct echoaudio *chip = snd_kcontrol_chip(kcontrol);
162362306a36Sopenharmony_ci
162462306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = chip->phantom_power;
162562306a36Sopenharmony_ci	return 0;
162662306a36Sopenharmony_ci}
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_cistatic int snd_echo_phantom_power_put(struct snd_kcontrol *kcontrol,
162962306a36Sopenharmony_ci				      struct snd_ctl_elem_value *ucontrol)
163062306a36Sopenharmony_ci{
163162306a36Sopenharmony_ci	struct echoaudio *chip = snd_kcontrol_chip(kcontrol);
163262306a36Sopenharmony_ci	int power, changed = 0;
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_ci	power = !!ucontrol->value.integer.value[0];
163562306a36Sopenharmony_ci	if (chip->phantom_power != power) {
163662306a36Sopenharmony_ci		spin_lock_irq(&chip->lock);
163762306a36Sopenharmony_ci		changed = set_phantom_power(chip, power);
163862306a36Sopenharmony_ci		spin_unlock_irq(&chip->lock);
163962306a36Sopenharmony_ci		if (changed == 0)
164062306a36Sopenharmony_ci			changed = 1;	/* no errors */
164162306a36Sopenharmony_ci	}
164262306a36Sopenharmony_ci	return changed;
164362306a36Sopenharmony_ci}
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_phantom_power_switch = {
164662306a36Sopenharmony_ci	.name = "Phantom power Switch",
164762306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_CARD,
164862306a36Sopenharmony_ci	.info = snd_echo_phantom_power_info,
164962306a36Sopenharmony_ci	.get = snd_echo_phantom_power_get,
165062306a36Sopenharmony_ci	.put = snd_echo_phantom_power_put,
165162306a36Sopenharmony_ci};
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_ci#endif /* ECHOCARD_HAS_PHANTOM_POWER */
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci
165762306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_ci/******************* Digital input automute switch *******************/
166062306a36Sopenharmony_ci#define snd_echo_automute_info		snd_ctl_boolean_mono_info
166162306a36Sopenharmony_ci
166262306a36Sopenharmony_cistatic int snd_echo_automute_get(struct snd_kcontrol *kcontrol,
166362306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
166462306a36Sopenharmony_ci{
166562306a36Sopenharmony_ci	struct echoaudio *chip = snd_kcontrol_chip(kcontrol);
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = chip->digital_in_automute;
166862306a36Sopenharmony_ci	return 0;
166962306a36Sopenharmony_ci}
167062306a36Sopenharmony_ci
167162306a36Sopenharmony_cistatic int snd_echo_automute_put(struct snd_kcontrol *kcontrol,
167262306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
167362306a36Sopenharmony_ci{
167462306a36Sopenharmony_ci	struct echoaudio *chip = snd_kcontrol_chip(kcontrol);
167562306a36Sopenharmony_ci	int automute, changed = 0;
167662306a36Sopenharmony_ci
167762306a36Sopenharmony_ci	automute = !!ucontrol->value.integer.value[0];
167862306a36Sopenharmony_ci	if (chip->digital_in_automute != automute) {
167962306a36Sopenharmony_ci		spin_lock_irq(&chip->lock);
168062306a36Sopenharmony_ci		changed = set_input_auto_mute(chip, automute);
168162306a36Sopenharmony_ci		spin_unlock_irq(&chip->lock);
168262306a36Sopenharmony_ci		if (changed == 0)
168362306a36Sopenharmony_ci			changed = 1;	/* no errors */
168462306a36Sopenharmony_ci	}
168562306a36Sopenharmony_ci	return changed;
168662306a36Sopenharmony_ci}
168762306a36Sopenharmony_ci
168862306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_automute_switch = {
168962306a36Sopenharmony_ci	.name = "Digital Capture Switch (automute)",
169062306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_CARD,
169162306a36Sopenharmony_ci	.info = snd_echo_automute_info,
169262306a36Sopenharmony_ci	.get = snd_echo_automute_get,
169362306a36Sopenharmony_ci	.put = snd_echo_automute_put,
169462306a36Sopenharmony_ci};
169562306a36Sopenharmony_ci
169662306a36Sopenharmony_ci#endif /* ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE */
169762306a36Sopenharmony_ci
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_ci/******************* VU-meters switch *******************/
170162306a36Sopenharmony_ci#define snd_echo_vumeters_switch_info		snd_ctl_boolean_mono_info
170262306a36Sopenharmony_ci
170362306a36Sopenharmony_cistatic int snd_echo_vumeters_switch_put(struct snd_kcontrol *kcontrol,
170462306a36Sopenharmony_ci					struct snd_ctl_elem_value *ucontrol)
170562306a36Sopenharmony_ci{
170662306a36Sopenharmony_ci	struct echoaudio *chip;
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
170962306a36Sopenharmony_ci	spin_lock_irq(&chip->lock);
171062306a36Sopenharmony_ci	set_meters_on(chip, ucontrol->value.integer.value[0]);
171162306a36Sopenharmony_ci	spin_unlock_irq(&chip->lock);
171262306a36Sopenharmony_ci	return 1;
171362306a36Sopenharmony_ci}
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_vumeters_switch = {
171662306a36Sopenharmony_ci	.name = "VU-meters Switch",
171762306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_CARD,
171862306a36Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_WRITE,
171962306a36Sopenharmony_ci	.info = snd_echo_vumeters_switch_info,
172062306a36Sopenharmony_ci	.put = snd_echo_vumeters_switch_put,
172162306a36Sopenharmony_ci};
172262306a36Sopenharmony_ci
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_ci
172562306a36Sopenharmony_ci/***** Read VU-meters (input, output, analog and digital together) *****/
172662306a36Sopenharmony_cistatic int snd_echo_vumeters_info(struct snd_kcontrol *kcontrol,
172762306a36Sopenharmony_ci				  struct snd_ctl_elem_info *uinfo)
172862306a36Sopenharmony_ci{
172962306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
173062306a36Sopenharmony_ci	uinfo->count = 96;
173162306a36Sopenharmony_ci	uinfo->value.integer.min = ECHOGAIN_MINOUT;
173262306a36Sopenharmony_ci	uinfo->value.integer.max = 0;
173362306a36Sopenharmony_ci	return 0;
173462306a36Sopenharmony_ci}
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_cistatic int snd_echo_vumeters_get(struct snd_kcontrol *kcontrol,
173762306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
173862306a36Sopenharmony_ci{
173962306a36Sopenharmony_ci	struct echoaudio *chip;
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
174262306a36Sopenharmony_ci	get_audio_meters(chip, ucontrol->value.integer.value);
174362306a36Sopenharmony_ci	return 0;
174462306a36Sopenharmony_ci}
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_vumeters = {
174762306a36Sopenharmony_ci	.name = "VU-meters",
174862306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
174962306a36Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READ |
175062306a36Sopenharmony_ci		  SNDRV_CTL_ELEM_ACCESS_VOLATILE |
175162306a36Sopenharmony_ci		  SNDRV_CTL_ELEM_ACCESS_TLV_READ,
175262306a36Sopenharmony_ci	.info = snd_echo_vumeters_info,
175362306a36Sopenharmony_ci	.get = snd_echo_vumeters_get,
175462306a36Sopenharmony_ci	.tlv = {.p = db_scale_output_gain},
175562306a36Sopenharmony_ci};
175662306a36Sopenharmony_ci
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci
175962306a36Sopenharmony_ci/*** Channels info - it exports informations about the number of channels ***/
176062306a36Sopenharmony_cistatic int snd_echo_channels_info_info(struct snd_kcontrol *kcontrol,
176162306a36Sopenharmony_ci				       struct snd_ctl_elem_info *uinfo)
176262306a36Sopenharmony_ci{
176362306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
176462306a36Sopenharmony_ci	uinfo->count = 6;
176562306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
176662306a36Sopenharmony_ci	uinfo->value.integer.max = 1 << ECHO_CLOCK_NUMBER;
176762306a36Sopenharmony_ci	return 0;
176862306a36Sopenharmony_ci}
176962306a36Sopenharmony_ci
177062306a36Sopenharmony_cistatic int snd_echo_channels_info_get(struct snd_kcontrol *kcontrol,
177162306a36Sopenharmony_ci				      struct snd_ctl_elem_value *ucontrol)
177262306a36Sopenharmony_ci{
177362306a36Sopenharmony_ci	struct echoaudio *chip;
177462306a36Sopenharmony_ci	int detected, clocks, bit, src;
177562306a36Sopenharmony_ci
177662306a36Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
177762306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = num_busses_in(chip);
177862306a36Sopenharmony_ci	ucontrol->value.integer.value[1] = num_analog_busses_in(chip);
177962306a36Sopenharmony_ci	ucontrol->value.integer.value[2] = num_busses_out(chip);
178062306a36Sopenharmony_ci	ucontrol->value.integer.value[3] = num_analog_busses_out(chip);
178162306a36Sopenharmony_ci	ucontrol->value.integer.value[4] = num_pipes_out(chip);
178262306a36Sopenharmony_ci
178362306a36Sopenharmony_ci	/* Compute the bitmask of the currently valid input clocks */
178462306a36Sopenharmony_ci	detected = detect_input_clocks(chip);
178562306a36Sopenharmony_ci	clocks = 0;
178662306a36Sopenharmony_ci	src = chip->num_clock_sources - 1;
178762306a36Sopenharmony_ci	for (bit = ECHO_CLOCK_NUMBER - 1; bit >= 0; bit--)
178862306a36Sopenharmony_ci		if (detected & (1 << bit))
178962306a36Sopenharmony_ci			for (; src >= 0; src--)
179062306a36Sopenharmony_ci				if (bit == chip->clock_source_list[src]) {
179162306a36Sopenharmony_ci					clocks |= 1 << src;
179262306a36Sopenharmony_ci					break;
179362306a36Sopenharmony_ci				}
179462306a36Sopenharmony_ci	ucontrol->value.integer.value[5] = clocks;
179562306a36Sopenharmony_ci
179662306a36Sopenharmony_ci	return 0;
179762306a36Sopenharmony_ci}
179862306a36Sopenharmony_ci
179962306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_channels_info = {
180062306a36Sopenharmony_ci	.name = "Channels info",
180162306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_HWDEP,
180262306a36Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
180362306a36Sopenharmony_ci	.info = snd_echo_channels_info_info,
180462306a36Sopenharmony_ci	.get = snd_echo_channels_info_get,
180562306a36Sopenharmony_ci};
180662306a36Sopenharmony_ci
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci
181062306a36Sopenharmony_ci/******************************************************************************
181162306a36Sopenharmony_ci	IRQ Handling
181262306a36Sopenharmony_ci******************************************************************************/
181362306a36Sopenharmony_ci/* Check if a period has elapsed since last interrupt
181462306a36Sopenharmony_ci *
181562306a36Sopenharmony_ci * Don't make any updates to state; PCM core handles this with the
181662306a36Sopenharmony_ci * correct locks.
181762306a36Sopenharmony_ci *
181862306a36Sopenharmony_ci * \return true if a period has elapsed, otherwise false
181962306a36Sopenharmony_ci */
182062306a36Sopenharmony_cistatic bool period_has_elapsed(struct snd_pcm_substream *substream)
182162306a36Sopenharmony_ci{
182262306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
182362306a36Sopenharmony_ci	struct audiopipe *pipe = runtime->private_data;
182462306a36Sopenharmony_ci	u32 counter, step;
182562306a36Sopenharmony_ci	size_t period_bytes;
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_ci	if (pipe->state != PIPE_STATE_STARTED)
182862306a36Sopenharmony_ci		return false;
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_ci	period_bytes = frames_to_bytes(runtime, runtime->period_size);
183162306a36Sopenharmony_ci
183262306a36Sopenharmony_ci	counter = le32_to_cpu(*pipe->dma_counter);  /* presumed atomic */
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_ci	step = counter - pipe->last_period;  /* handles wrapping */
183562306a36Sopenharmony_ci	step -= step % period_bytes;  /* acknowledge whole periods only */
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci	if (step == 0)
183862306a36Sopenharmony_ci		return false;  /* haven't advanced a whole period yet */
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_ci	pipe->last_period += step;  /* used exclusively by us */
184162306a36Sopenharmony_ci	return true;
184262306a36Sopenharmony_ci}
184362306a36Sopenharmony_ci
184462306a36Sopenharmony_cistatic irqreturn_t snd_echo_interrupt(int irq, void *dev_id)
184562306a36Sopenharmony_ci{
184662306a36Sopenharmony_ci	struct echoaudio *chip = dev_id;
184762306a36Sopenharmony_ci	int ss, st;
184862306a36Sopenharmony_ci
184962306a36Sopenharmony_ci	spin_lock(&chip->lock);
185062306a36Sopenharmony_ci	st = service_irq(chip);
185162306a36Sopenharmony_ci	if (st < 0) {
185262306a36Sopenharmony_ci		spin_unlock(&chip->lock);
185362306a36Sopenharmony_ci		return IRQ_NONE;
185462306a36Sopenharmony_ci	}
185562306a36Sopenharmony_ci	/* The hardware doesn't tell us which substream caused the irq,
185662306a36Sopenharmony_ci	thus we have to check all running substreams. */
185762306a36Sopenharmony_ci	for (ss = 0; ss < DSP_MAXPIPES; ss++) {
185862306a36Sopenharmony_ci		struct snd_pcm_substream *substream;
185962306a36Sopenharmony_ci
186062306a36Sopenharmony_ci		substream = chip->substream[ss];
186162306a36Sopenharmony_ci		if (substream && period_has_elapsed(substream)) {
186262306a36Sopenharmony_ci			spin_unlock(&chip->lock);
186362306a36Sopenharmony_ci			snd_pcm_period_elapsed(substream);
186462306a36Sopenharmony_ci			spin_lock(&chip->lock);
186562306a36Sopenharmony_ci		}
186662306a36Sopenharmony_ci	}
186762306a36Sopenharmony_ci	spin_unlock(&chip->lock);
186862306a36Sopenharmony_ci
186962306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_MIDI
187062306a36Sopenharmony_ci	if (st > 0 && chip->midi_in) {
187162306a36Sopenharmony_ci		snd_rawmidi_receive(chip->midi_in, chip->midi_buffer, st);
187262306a36Sopenharmony_ci		dev_dbg(chip->card->dev, "rawmidi_iread=%d\n", st);
187362306a36Sopenharmony_ci	}
187462306a36Sopenharmony_ci#endif
187562306a36Sopenharmony_ci	return IRQ_HANDLED;
187662306a36Sopenharmony_ci}
187762306a36Sopenharmony_ci
187862306a36Sopenharmony_ci
187962306a36Sopenharmony_ci
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ci/******************************************************************************
188262306a36Sopenharmony_ci	Module construction / destruction
188362306a36Sopenharmony_ci******************************************************************************/
188462306a36Sopenharmony_ci
188562306a36Sopenharmony_cistatic void snd_echo_free(struct snd_card *card)
188662306a36Sopenharmony_ci{
188762306a36Sopenharmony_ci	struct echoaudio *chip = card->private_data;
188862306a36Sopenharmony_ci
188962306a36Sopenharmony_ci	if (chip->comm_page)
189062306a36Sopenharmony_ci		rest_in_peace(chip);
189162306a36Sopenharmony_ci
189262306a36Sopenharmony_ci	if (chip->irq >= 0)
189362306a36Sopenharmony_ci		free_irq(chip->irq, chip);
189462306a36Sopenharmony_ci
189562306a36Sopenharmony_ci	/* release chip data */
189662306a36Sopenharmony_ci	free_firmware_cache(chip);
189762306a36Sopenharmony_ci}
189862306a36Sopenharmony_ci
189962306a36Sopenharmony_ci/* <--snd_echo_probe() */
190062306a36Sopenharmony_cistatic int snd_echo_create(struct snd_card *card,
190162306a36Sopenharmony_ci			   struct pci_dev *pci)
190262306a36Sopenharmony_ci{
190362306a36Sopenharmony_ci	struct echoaudio *chip = card->private_data;
190462306a36Sopenharmony_ci	int err;
190562306a36Sopenharmony_ci	size_t sz;
190662306a36Sopenharmony_ci
190762306a36Sopenharmony_ci	pci_write_config_byte(pci, PCI_LATENCY_TIMER, 0xC0);
190862306a36Sopenharmony_ci
190962306a36Sopenharmony_ci	err = pcim_enable_device(pci);
191062306a36Sopenharmony_ci	if (err < 0)
191162306a36Sopenharmony_ci		return err;
191262306a36Sopenharmony_ci	pci_set_master(pci);
191362306a36Sopenharmony_ci
191462306a36Sopenharmony_ci	/* Allocate chip if needed */
191562306a36Sopenharmony_ci	spin_lock_init(&chip->lock);
191662306a36Sopenharmony_ci	chip->card = card;
191762306a36Sopenharmony_ci	chip->pci = pci;
191862306a36Sopenharmony_ci	chip->irq = -1;
191962306a36Sopenharmony_ci	chip->opencount = 0;
192062306a36Sopenharmony_ci	mutex_init(&chip->mode_mutex);
192162306a36Sopenharmony_ci	chip->can_set_rate = 1;
192262306a36Sopenharmony_ci
192362306a36Sopenharmony_ci	/* PCI resource allocation */
192462306a36Sopenharmony_ci	err = pci_request_regions(pci, ECHOCARD_NAME);
192562306a36Sopenharmony_ci	if (err < 0)
192662306a36Sopenharmony_ci		return err;
192762306a36Sopenharmony_ci
192862306a36Sopenharmony_ci	chip->dsp_registers_phys = pci_resource_start(pci, 0);
192962306a36Sopenharmony_ci	sz = pci_resource_len(pci, 0);
193062306a36Sopenharmony_ci	if (sz > PAGE_SIZE)
193162306a36Sopenharmony_ci		sz = PAGE_SIZE;		/* We map only the required part */
193262306a36Sopenharmony_ci
193362306a36Sopenharmony_ci	chip->dsp_registers = devm_ioremap(&pci->dev, chip->dsp_registers_phys, sz);
193462306a36Sopenharmony_ci	if (!chip->dsp_registers) {
193562306a36Sopenharmony_ci		dev_err(chip->card->dev, "ioremap failed\n");
193662306a36Sopenharmony_ci		return -ENOMEM;
193762306a36Sopenharmony_ci	}
193862306a36Sopenharmony_ci
193962306a36Sopenharmony_ci	if (request_irq(pci->irq, snd_echo_interrupt, IRQF_SHARED,
194062306a36Sopenharmony_ci			KBUILD_MODNAME, chip)) {
194162306a36Sopenharmony_ci		dev_err(chip->card->dev, "cannot grab irq\n");
194262306a36Sopenharmony_ci		return -EBUSY;
194362306a36Sopenharmony_ci	}
194462306a36Sopenharmony_ci	chip->irq = pci->irq;
194562306a36Sopenharmony_ci	card->sync_irq = chip->irq;
194662306a36Sopenharmony_ci	dev_dbg(card->dev, "pci=%p irq=%d subdev=%04x Init hardware...\n",
194762306a36Sopenharmony_ci		chip->pci, chip->irq, chip->pci->subsystem_device);
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_ci	card->private_free = snd_echo_free;
195062306a36Sopenharmony_ci
195162306a36Sopenharmony_ci	/* Create the DSP comm page - this is the area of memory used for most
195262306a36Sopenharmony_ci	of the communication with the DSP, which accesses it via bus mastering */
195362306a36Sopenharmony_ci	chip->commpage_dma_buf =
195462306a36Sopenharmony_ci		snd_devm_alloc_pages(&pci->dev, SNDRV_DMA_TYPE_DEV,
195562306a36Sopenharmony_ci				     sizeof(struct comm_page));
195662306a36Sopenharmony_ci	if (!chip->commpage_dma_buf)
195762306a36Sopenharmony_ci		return -ENOMEM;
195862306a36Sopenharmony_ci	chip->comm_page_phys = chip->commpage_dma_buf->addr;
195962306a36Sopenharmony_ci	chip->comm_page = (struct comm_page *)chip->commpage_dma_buf->area;
196062306a36Sopenharmony_ci
196162306a36Sopenharmony_ci	err = init_hw(chip, chip->pci->device, chip->pci->subsystem_device);
196262306a36Sopenharmony_ci	if (err >= 0)
196362306a36Sopenharmony_ci		err = set_mixer_defaults(chip);
196462306a36Sopenharmony_ci	if (err < 0) {
196562306a36Sopenharmony_ci		dev_err(card->dev, "init_hw err=%d\n", err);
196662306a36Sopenharmony_ci		return err;
196762306a36Sopenharmony_ci	}
196862306a36Sopenharmony_ci
196962306a36Sopenharmony_ci	return 0;
197062306a36Sopenharmony_ci}
197162306a36Sopenharmony_ci
197262306a36Sopenharmony_ci/* constructor */
197362306a36Sopenharmony_cistatic int __snd_echo_probe(struct pci_dev *pci,
197462306a36Sopenharmony_ci			    const struct pci_device_id *pci_id)
197562306a36Sopenharmony_ci{
197662306a36Sopenharmony_ci	static int dev;
197762306a36Sopenharmony_ci	struct snd_card *card;
197862306a36Sopenharmony_ci	struct echoaudio *chip;
197962306a36Sopenharmony_ci	char *dsp;
198062306a36Sopenharmony_ci	__maybe_unused int i;
198162306a36Sopenharmony_ci	int err;
198262306a36Sopenharmony_ci
198362306a36Sopenharmony_ci	if (dev >= SNDRV_CARDS)
198462306a36Sopenharmony_ci		return -ENODEV;
198562306a36Sopenharmony_ci	if (!enable[dev]) {
198662306a36Sopenharmony_ci		dev++;
198762306a36Sopenharmony_ci		return -ENOENT;
198862306a36Sopenharmony_ci	}
198962306a36Sopenharmony_ci
199062306a36Sopenharmony_ci	i = 0;
199162306a36Sopenharmony_ci	err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
199262306a36Sopenharmony_ci				sizeof(*chip), &card);
199362306a36Sopenharmony_ci	if (err < 0)
199462306a36Sopenharmony_ci		return err;
199562306a36Sopenharmony_ci	chip = card->private_data;
199662306a36Sopenharmony_ci
199762306a36Sopenharmony_ci	err = snd_echo_create(card, pci);
199862306a36Sopenharmony_ci	if (err < 0)
199962306a36Sopenharmony_ci		return err;
200062306a36Sopenharmony_ci
200162306a36Sopenharmony_ci	strcpy(card->driver, "Echo_" ECHOCARD_NAME);
200262306a36Sopenharmony_ci	strcpy(card->shortname, chip->card_name);
200362306a36Sopenharmony_ci
200462306a36Sopenharmony_ci	dsp = "56301";
200562306a36Sopenharmony_ci	if (pci_id->device == 0x3410)
200662306a36Sopenharmony_ci		dsp = "56361";
200762306a36Sopenharmony_ci
200862306a36Sopenharmony_ci	sprintf(card->longname, "%s rev.%d (DSP%s) at 0x%lx irq %i",
200962306a36Sopenharmony_ci		card->shortname, pci_id->subdevice & 0x000f, dsp,
201062306a36Sopenharmony_ci		chip->dsp_registers_phys, chip->irq);
201162306a36Sopenharmony_ci
201262306a36Sopenharmony_ci	err = snd_echo_new_pcm(chip);
201362306a36Sopenharmony_ci	if (err < 0) {
201462306a36Sopenharmony_ci		dev_err(chip->card->dev, "new pcm error %d\n", err);
201562306a36Sopenharmony_ci		return err;
201662306a36Sopenharmony_ci	}
201762306a36Sopenharmony_ci
201862306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_MIDI
201962306a36Sopenharmony_ci	if (chip->has_midi) {	/* Some Mia's do not have midi */
202062306a36Sopenharmony_ci		err = snd_echo_midi_create(card, chip);
202162306a36Sopenharmony_ci		if (err < 0) {
202262306a36Sopenharmony_ci			dev_err(chip->card->dev, "new midi error %d\n", err);
202362306a36Sopenharmony_ci			return err;
202462306a36Sopenharmony_ci		}
202562306a36Sopenharmony_ci	}
202662306a36Sopenharmony_ci#endif
202762306a36Sopenharmony_ci
202862306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_VMIXER
202962306a36Sopenharmony_ci	snd_echo_vmixer.count = num_pipes_out(chip) * num_busses_out(chip);
203062306a36Sopenharmony_ci	err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vmixer, chip));
203162306a36Sopenharmony_ci	if (err < 0)
203262306a36Sopenharmony_ci		return err;
203362306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_LINE_OUT_GAIN
203462306a36Sopenharmony_ci	err = snd_ctl_add(chip->card,
203562306a36Sopenharmony_ci			  snd_ctl_new1(&snd_echo_line_output_gain, chip));
203662306a36Sopenharmony_ci	if (err < 0)
203762306a36Sopenharmony_ci		return err;
203862306a36Sopenharmony_ci#endif
203962306a36Sopenharmony_ci#else /* ECHOCARD_HAS_VMIXER */
204062306a36Sopenharmony_ci	err = snd_ctl_add(chip->card,
204162306a36Sopenharmony_ci			  snd_ctl_new1(&snd_echo_pcm_output_gain, chip));
204262306a36Sopenharmony_ci	if (err < 0)
204362306a36Sopenharmony_ci		return err;
204462306a36Sopenharmony_ci#endif /* ECHOCARD_HAS_VMIXER */
204562306a36Sopenharmony_ci
204662306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_INPUT_GAIN
204762306a36Sopenharmony_ci	err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_line_input_gain, chip));
204862306a36Sopenharmony_ci	if (err < 0)
204962306a36Sopenharmony_ci		return err;
205062306a36Sopenharmony_ci#endif
205162306a36Sopenharmony_ci
205262306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_INPUT_NOMINAL_LEVEL
205362306a36Sopenharmony_ci	if (!chip->hasnt_input_nominal_level) {
205462306a36Sopenharmony_ci		err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_intput_nominal_level, chip));
205562306a36Sopenharmony_ci		if (err < 0)
205662306a36Sopenharmony_ci			return err;
205762306a36Sopenharmony_ci	}
205862306a36Sopenharmony_ci#endif
205962306a36Sopenharmony_ci
206062306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL
206162306a36Sopenharmony_ci	err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_output_nominal_level, chip));
206262306a36Sopenharmony_ci	if (err < 0)
206362306a36Sopenharmony_ci		return err;
206462306a36Sopenharmony_ci#endif
206562306a36Sopenharmony_ci
206662306a36Sopenharmony_ci	err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vumeters_switch, chip));
206762306a36Sopenharmony_ci	if (err < 0)
206862306a36Sopenharmony_ci		return err;
206962306a36Sopenharmony_ci
207062306a36Sopenharmony_ci	err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vumeters, chip));
207162306a36Sopenharmony_ci	if (err < 0)
207262306a36Sopenharmony_ci		return err;
207362306a36Sopenharmony_ci
207462306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_MONITOR
207562306a36Sopenharmony_ci	snd_echo_monitor_mixer.count = num_busses_in(chip) * num_busses_out(chip);
207662306a36Sopenharmony_ci	err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_monitor_mixer, chip));
207762306a36Sopenharmony_ci	if (err < 0)
207862306a36Sopenharmony_ci		return err;
207962306a36Sopenharmony_ci#endif
208062306a36Sopenharmony_ci
208162306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE
208262306a36Sopenharmony_ci	err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_automute_switch, chip));
208362306a36Sopenharmony_ci	if (err < 0)
208462306a36Sopenharmony_ci		return err;
208562306a36Sopenharmony_ci#endif
208662306a36Sopenharmony_ci
208762306a36Sopenharmony_ci	err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_channels_info, chip));
208862306a36Sopenharmony_ci	if (err < 0)
208962306a36Sopenharmony_ci		return err;
209062306a36Sopenharmony_ci
209162306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_DIGITAL_MODE_SWITCH
209262306a36Sopenharmony_ci	/* Creates a list of available digital modes */
209362306a36Sopenharmony_ci	chip->num_digital_modes = 0;
209462306a36Sopenharmony_ci	for (i = 0; i < 6; i++)
209562306a36Sopenharmony_ci		if (chip->digital_modes & (1 << i))
209662306a36Sopenharmony_ci			chip->digital_mode_list[chip->num_digital_modes++] = i;
209762306a36Sopenharmony_ci
209862306a36Sopenharmony_ci	err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_digital_mode_switch, chip));
209962306a36Sopenharmony_ci	if (err < 0)
210062306a36Sopenharmony_ci		return err;
210162306a36Sopenharmony_ci#endif /* ECHOCARD_HAS_DIGITAL_MODE_SWITCH */
210262306a36Sopenharmony_ci
210362306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_EXTERNAL_CLOCK
210462306a36Sopenharmony_ci	/* Creates a list of available clock sources */
210562306a36Sopenharmony_ci	chip->num_clock_sources = 0;
210662306a36Sopenharmony_ci	for (i = 0; i < 10; i++)
210762306a36Sopenharmony_ci		if (chip->input_clock_types & (1 << i))
210862306a36Sopenharmony_ci			chip->clock_source_list[chip->num_clock_sources++] = i;
210962306a36Sopenharmony_ci
211062306a36Sopenharmony_ci	if (chip->num_clock_sources > 1) {
211162306a36Sopenharmony_ci		chip->clock_src_ctl = snd_ctl_new1(&snd_echo_clock_source_switch, chip);
211262306a36Sopenharmony_ci		err = snd_ctl_add(chip->card, chip->clock_src_ctl);
211362306a36Sopenharmony_ci		if (err < 0)
211462306a36Sopenharmony_ci			return err;
211562306a36Sopenharmony_ci	}
211662306a36Sopenharmony_ci#endif /* ECHOCARD_HAS_EXTERNAL_CLOCK */
211762306a36Sopenharmony_ci
211862306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_DIGITAL_IO
211962306a36Sopenharmony_ci	err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_spdif_mode_switch, chip));
212062306a36Sopenharmony_ci	if (err < 0)
212162306a36Sopenharmony_ci		return err;
212262306a36Sopenharmony_ci#endif
212362306a36Sopenharmony_ci
212462306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_PHANTOM_POWER
212562306a36Sopenharmony_ci	if (chip->has_phantom_power) {
212662306a36Sopenharmony_ci		err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_phantom_power_switch, chip));
212762306a36Sopenharmony_ci		if (err < 0)
212862306a36Sopenharmony_ci			return err;
212962306a36Sopenharmony_ci	}
213062306a36Sopenharmony_ci#endif
213162306a36Sopenharmony_ci
213262306a36Sopenharmony_ci	err = snd_card_register(card);
213362306a36Sopenharmony_ci	if (err < 0)
213462306a36Sopenharmony_ci		return err;
213562306a36Sopenharmony_ci	dev_info(card->dev, "Card registered: %s\n", card->longname);
213662306a36Sopenharmony_ci
213762306a36Sopenharmony_ci	pci_set_drvdata(pci, chip);
213862306a36Sopenharmony_ci	dev++;
213962306a36Sopenharmony_ci	return 0;
214062306a36Sopenharmony_ci}
214162306a36Sopenharmony_ci
214262306a36Sopenharmony_cistatic int snd_echo_probe(struct pci_dev *pci,
214362306a36Sopenharmony_ci			  const struct pci_device_id *pci_id)
214462306a36Sopenharmony_ci{
214562306a36Sopenharmony_ci	return snd_card_free_on_error(&pci->dev, __snd_echo_probe(pci, pci_id));
214662306a36Sopenharmony_ci}
214762306a36Sopenharmony_ci
214862306a36Sopenharmony_ci
214962306a36Sopenharmony_ci#if defined(CONFIG_PM_SLEEP)
215062306a36Sopenharmony_ci
215162306a36Sopenharmony_cistatic int snd_echo_suspend(struct device *dev)
215262306a36Sopenharmony_ci{
215362306a36Sopenharmony_ci	struct echoaudio *chip = dev_get_drvdata(dev);
215462306a36Sopenharmony_ci
215562306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_MIDI
215662306a36Sopenharmony_ci	/* This call can sleep */
215762306a36Sopenharmony_ci	if (chip->midi_out)
215862306a36Sopenharmony_ci		snd_echo_midi_output_trigger(chip->midi_out, 0);
215962306a36Sopenharmony_ci#endif
216062306a36Sopenharmony_ci	spin_lock_irq(&chip->lock);
216162306a36Sopenharmony_ci	if (wait_handshake(chip)) {
216262306a36Sopenharmony_ci		spin_unlock_irq(&chip->lock);
216362306a36Sopenharmony_ci		return -EIO;
216462306a36Sopenharmony_ci	}
216562306a36Sopenharmony_ci	clear_handshake(chip);
216662306a36Sopenharmony_ci	if (send_vector(chip, DSP_VC_GO_COMATOSE) < 0) {
216762306a36Sopenharmony_ci		spin_unlock_irq(&chip->lock);
216862306a36Sopenharmony_ci		return -EIO;
216962306a36Sopenharmony_ci	}
217062306a36Sopenharmony_ci	spin_unlock_irq(&chip->lock);
217162306a36Sopenharmony_ci
217262306a36Sopenharmony_ci	chip->dsp_code = NULL;
217362306a36Sopenharmony_ci	free_irq(chip->irq, chip);
217462306a36Sopenharmony_ci	chip->irq = -1;
217562306a36Sopenharmony_ci	chip->card->sync_irq = -1;
217662306a36Sopenharmony_ci	return 0;
217762306a36Sopenharmony_ci}
217862306a36Sopenharmony_ci
217962306a36Sopenharmony_ci
218062306a36Sopenharmony_ci
218162306a36Sopenharmony_cistatic int snd_echo_resume(struct device *dev)
218262306a36Sopenharmony_ci{
218362306a36Sopenharmony_ci	struct pci_dev *pci = to_pci_dev(dev);
218462306a36Sopenharmony_ci	struct echoaudio *chip = dev_get_drvdata(dev);
218562306a36Sopenharmony_ci	struct comm_page *commpage, *commpage_bak;
218662306a36Sopenharmony_ci	u32 pipe_alloc_mask;
218762306a36Sopenharmony_ci	int err;
218862306a36Sopenharmony_ci
218962306a36Sopenharmony_ci	commpage = chip->comm_page;
219062306a36Sopenharmony_ci	commpage_bak = kmemdup(commpage, sizeof(*commpage), GFP_KERNEL);
219162306a36Sopenharmony_ci	if (commpage_bak == NULL)
219262306a36Sopenharmony_ci		return -ENOMEM;
219362306a36Sopenharmony_ci
219462306a36Sopenharmony_ci	err = init_hw(chip, chip->pci->device, chip->pci->subsystem_device);
219562306a36Sopenharmony_ci	if (err < 0) {
219662306a36Sopenharmony_ci		kfree(commpage_bak);
219762306a36Sopenharmony_ci		dev_err(dev, "resume init_hw err=%d\n", err);
219862306a36Sopenharmony_ci		return err;
219962306a36Sopenharmony_ci	}
220062306a36Sopenharmony_ci
220162306a36Sopenharmony_ci	/* Temporarily set chip->pipe_alloc_mask=0 otherwise
220262306a36Sopenharmony_ci	 * restore_dsp_settings() fails.
220362306a36Sopenharmony_ci	 */
220462306a36Sopenharmony_ci	pipe_alloc_mask = chip->pipe_alloc_mask;
220562306a36Sopenharmony_ci	chip->pipe_alloc_mask = 0;
220662306a36Sopenharmony_ci	err = restore_dsp_rettings(chip);
220762306a36Sopenharmony_ci	chip->pipe_alloc_mask = pipe_alloc_mask;
220862306a36Sopenharmony_ci	if (err < 0) {
220962306a36Sopenharmony_ci		kfree(commpage_bak);
221062306a36Sopenharmony_ci		return err;
221162306a36Sopenharmony_ci	}
221262306a36Sopenharmony_ci
221362306a36Sopenharmony_ci	memcpy(&commpage->audio_format, &commpage_bak->audio_format,
221462306a36Sopenharmony_ci		sizeof(commpage->audio_format));
221562306a36Sopenharmony_ci	memcpy(&commpage->sglist_addr, &commpage_bak->sglist_addr,
221662306a36Sopenharmony_ci		sizeof(commpage->sglist_addr));
221762306a36Sopenharmony_ci	memcpy(&commpage->midi_output, &commpage_bak->midi_output,
221862306a36Sopenharmony_ci		sizeof(commpage->midi_output));
221962306a36Sopenharmony_ci	kfree(commpage_bak);
222062306a36Sopenharmony_ci
222162306a36Sopenharmony_ci	if (request_irq(pci->irq, snd_echo_interrupt, IRQF_SHARED,
222262306a36Sopenharmony_ci			KBUILD_MODNAME, chip)) {
222362306a36Sopenharmony_ci		dev_err(chip->card->dev, "cannot grab irq\n");
222462306a36Sopenharmony_ci		return -EBUSY;
222562306a36Sopenharmony_ci	}
222662306a36Sopenharmony_ci	chip->irq = pci->irq;
222762306a36Sopenharmony_ci	chip->card->sync_irq = chip->irq;
222862306a36Sopenharmony_ci	dev_dbg(dev, "resume irq=%d\n", chip->irq);
222962306a36Sopenharmony_ci
223062306a36Sopenharmony_ci#ifdef ECHOCARD_HAS_MIDI
223162306a36Sopenharmony_ci	if (chip->midi_input_enabled)
223262306a36Sopenharmony_ci		enable_midi_input(chip, true);
223362306a36Sopenharmony_ci	if (chip->midi_out)
223462306a36Sopenharmony_ci		snd_echo_midi_output_trigger(chip->midi_out, 1);
223562306a36Sopenharmony_ci#endif
223662306a36Sopenharmony_ci
223762306a36Sopenharmony_ci	return 0;
223862306a36Sopenharmony_ci}
223962306a36Sopenharmony_ci
224062306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(snd_echo_pm, snd_echo_suspend, snd_echo_resume);
224162306a36Sopenharmony_ci#define SND_ECHO_PM_OPS	&snd_echo_pm
224262306a36Sopenharmony_ci#else
224362306a36Sopenharmony_ci#define SND_ECHO_PM_OPS	NULL
224462306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */
224562306a36Sopenharmony_ci
224662306a36Sopenharmony_ci/******************************************************************************
224762306a36Sopenharmony_ci	Everything starts and ends here
224862306a36Sopenharmony_ci******************************************************************************/
224962306a36Sopenharmony_ci
225062306a36Sopenharmony_ci/* pci_driver definition */
225162306a36Sopenharmony_cistatic struct pci_driver echo_driver = {
225262306a36Sopenharmony_ci	.name = KBUILD_MODNAME,
225362306a36Sopenharmony_ci	.id_table = snd_echo_ids,
225462306a36Sopenharmony_ci	.probe = snd_echo_probe,
225562306a36Sopenharmony_ci	.driver = {
225662306a36Sopenharmony_ci		.pm = SND_ECHO_PM_OPS,
225762306a36Sopenharmony_ci	},
225862306a36Sopenharmony_ci};
225962306a36Sopenharmony_ci
226062306a36Sopenharmony_cimodule_pci_driver(echo_driver);
2261