18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  ALSA driver for Echoaudio soundcards.
48c2ecf20Sopenharmony_ci *  Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
58c2ecf20Sopenharmony_ci *  Copyright (C) 2020 Mark Hills <mark@xwax.org>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ciMODULE_AUTHOR("Giuliano Pochini <pochini@shiny.it>");
118c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
128c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Echoaudio " ECHOCARD_NAME " soundcards driver");
138c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{Echoaudio," ECHOCARD_NAME "}}");
148c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, snd_echo_ids);
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
178c2ecf20Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
188c2ecf20Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cimodule_param_array(index, int, NULL, 0444);
218c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for " ECHOCARD_NAME " soundcard.");
228c2ecf20Sopenharmony_cimodule_param_array(id, charp, NULL, 0444);
238c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for " ECHOCARD_NAME " soundcard.");
248c2ecf20Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444);
258c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable " ECHOCARD_NAME " soundcard.");
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic const unsigned int channels_list[10] = {1, 2, 4, 6, 8, 10, 12, 14, 16, 999999};
288c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_output_gain, -12800, 100, 1);
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic int get_firmware(const struct firmware **fw_entry,
338c2ecf20Sopenharmony_ci			struct echoaudio *chip, const short fw_index)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	int err;
368c2ecf20Sopenharmony_ci	char name[30];
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
398c2ecf20Sopenharmony_ci	if (chip->fw_cache[fw_index]) {
408c2ecf20Sopenharmony_ci		dev_dbg(chip->card->dev,
418c2ecf20Sopenharmony_ci			"firmware requested: %s is cached\n",
428c2ecf20Sopenharmony_ci			card_fw[fw_index].data);
438c2ecf20Sopenharmony_ci		*fw_entry = chip->fw_cache[fw_index];
448c2ecf20Sopenharmony_ci		return 0;
458c2ecf20Sopenharmony_ci	}
468c2ecf20Sopenharmony_ci#endif
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	dev_dbg(chip->card->dev,
498c2ecf20Sopenharmony_ci		"firmware requested: %s\n", card_fw[fw_index].data);
508c2ecf20Sopenharmony_ci	snprintf(name, sizeof(name), "ea/%s", card_fw[fw_index].data);
518c2ecf20Sopenharmony_ci	err = request_firmware(fw_entry, name, &chip->pci->dev);
528c2ecf20Sopenharmony_ci	if (err < 0)
538c2ecf20Sopenharmony_ci		dev_err(chip->card->dev,
548c2ecf20Sopenharmony_ci			"get_firmware(): Firmware not available (%d)\n", err);
558c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
568c2ecf20Sopenharmony_ci	else
578c2ecf20Sopenharmony_ci		chip->fw_cache[fw_index] = *fw_entry;
588c2ecf20Sopenharmony_ci#endif
598c2ecf20Sopenharmony_ci	return err;
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic void free_firmware(const struct firmware *fw_entry,
658c2ecf20Sopenharmony_ci			  struct echoaudio *chip)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
688c2ecf20Sopenharmony_ci	dev_dbg(chip->card->dev, "firmware not released (kept in cache)\n");
698c2ecf20Sopenharmony_ci#else
708c2ecf20Sopenharmony_ci	release_firmware(fw_entry);
718c2ecf20Sopenharmony_ci#endif
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic void free_firmware_cache(struct echoaudio *chip)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
798c2ecf20Sopenharmony_ci	int i;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	for (i = 0; i < 8 ; i++)
828c2ecf20Sopenharmony_ci		if (chip->fw_cache[i]) {
838c2ecf20Sopenharmony_ci			release_firmware(chip->fw_cache[i]);
848c2ecf20Sopenharmony_ci			dev_dbg(chip->card->dev, "release_firmware(%d)\n", i);
858c2ecf20Sopenharmony_ci		}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci#endif
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci/******************************************************************************
938c2ecf20Sopenharmony_ci	PCM interface
948c2ecf20Sopenharmony_ci******************************************************************************/
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic void audiopipe_free(struct snd_pcm_runtime *runtime)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	struct audiopipe *pipe = runtime->private_data;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	if (pipe->sgpage.area)
1018c2ecf20Sopenharmony_ci		snd_dma_free_pages(&pipe->sgpage);
1028c2ecf20Sopenharmony_ci	kfree(pipe);
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic int hw_rule_capture_format_by_channels(struct snd_pcm_hw_params *params,
1088c2ecf20Sopenharmony_ci					      struct snd_pcm_hw_rule *rule)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	struct snd_interval *c = hw_param_interval(params,
1118c2ecf20Sopenharmony_ci						   SNDRV_PCM_HW_PARAM_CHANNELS);
1128c2ecf20Sopenharmony_ci	struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
1138c2ecf20Sopenharmony_ci	struct snd_mask fmt;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	snd_mask_any(&fmt);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci#ifndef ECHOCARD_HAS_STEREO_BIG_ENDIAN32
1188c2ecf20Sopenharmony_ci	/* >=2 channels cannot be S32_BE */
1198c2ecf20Sopenharmony_ci	if (c->min == 2) {
1208c2ecf20Sopenharmony_ci		fmt.bits[0] &= ~SNDRV_PCM_FMTBIT_S32_BE;
1218c2ecf20Sopenharmony_ci		return snd_mask_refine(f, &fmt);
1228c2ecf20Sopenharmony_ci	}
1238c2ecf20Sopenharmony_ci#endif
1248c2ecf20Sopenharmony_ci	/* > 2 channels cannot be U8 and S32_BE */
1258c2ecf20Sopenharmony_ci	if (c->min > 2) {
1268c2ecf20Sopenharmony_ci		fmt.bits[0] &= ~(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_BE);
1278c2ecf20Sopenharmony_ci		return snd_mask_refine(f, &fmt);
1288c2ecf20Sopenharmony_ci	}
1298c2ecf20Sopenharmony_ci	/* Mono is ok with any format */
1308c2ecf20Sopenharmony_ci	return 0;
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic int hw_rule_capture_channels_by_format(struct snd_pcm_hw_params *params,
1368c2ecf20Sopenharmony_ci					      struct snd_pcm_hw_rule *rule)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	struct snd_interval *c = hw_param_interval(params,
1398c2ecf20Sopenharmony_ci						   SNDRV_PCM_HW_PARAM_CHANNELS);
1408c2ecf20Sopenharmony_ci	struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
1418c2ecf20Sopenharmony_ci	struct snd_interval ch;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	snd_interval_any(&ch);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	/* S32_BE is mono (and stereo) only */
1468c2ecf20Sopenharmony_ci	if (f->bits[0] == SNDRV_PCM_FMTBIT_S32_BE) {
1478c2ecf20Sopenharmony_ci		ch.min = 1;
1488c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_STEREO_BIG_ENDIAN32
1498c2ecf20Sopenharmony_ci		ch.max = 2;
1508c2ecf20Sopenharmony_ci#else
1518c2ecf20Sopenharmony_ci		ch.max = 1;
1528c2ecf20Sopenharmony_ci#endif
1538c2ecf20Sopenharmony_ci		ch.integer = 1;
1548c2ecf20Sopenharmony_ci		return snd_interval_refine(c, &ch);
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci	/* U8 can be only mono or stereo */
1578c2ecf20Sopenharmony_ci	if (f->bits[0] == SNDRV_PCM_FMTBIT_U8) {
1588c2ecf20Sopenharmony_ci		ch.min = 1;
1598c2ecf20Sopenharmony_ci		ch.max = 2;
1608c2ecf20Sopenharmony_ci		ch.integer = 1;
1618c2ecf20Sopenharmony_ci		return snd_interval_refine(c, &ch);
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci	/* S16_LE, S24_3LE and S32_LE support any number of channels. */
1648c2ecf20Sopenharmony_ci	return 0;
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic int hw_rule_playback_format_by_channels(struct snd_pcm_hw_params *params,
1708c2ecf20Sopenharmony_ci					       struct snd_pcm_hw_rule *rule)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	struct snd_interval *c = hw_param_interval(params,
1738c2ecf20Sopenharmony_ci						   SNDRV_PCM_HW_PARAM_CHANNELS);
1748c2ecf20Sopenharmony_ci	struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
1758c2ecf20Sopenharmony_ci	struct snd_mask fmt;
1768c2ecf20Sopenharmony_ci	u64 fmask;
1778c2ecf20Sopenharmony_ci	snd_mask_any(&fmt);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	fmask = fmt.bits[0] + ((u64)fmt.bits[1] << 32);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	/* >2 channels must be S16_LE, S24_3LE or S32_LE */
1828c2ecf20Sopenharmony_ci	if (c->min > 2) {
1838c2ecf20Sopenharmony_ci		fmask &= SNDRV_PCM_FMTBIT_S16_LE |
1848c2ecf20Sopenharmony_ci			 SNDRV_PCM_FMTBIT_S24_3LE |
1858c2ecf20Sopenharmony_ci			 SNDRV_PCM_FMTBIT_S32_LE;
1868c2ecf20Sopenharmony_ci	/* 1 channel must be S32_BE or S32_LE */
1878c2ecf20Sopenharmony_ci	} else if (c->max == 1)
1888c2ecf20Sopenharmony_ci		fmask &= SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE;
1898c2ecf20Sopenharmony_ci#ifndef ECHOCARD_HAS_STEREO_BIG_ENDIAN32
1908c2ecf20Sopenharmony_ci	/* 2 channels cannot be S32_BE */
1918c2ecf20Sopenharmony_ci	else if (c->min == 2 && c->max == 2)
1928c2ecf20Sopenharmony_ci		fmask &= ~SNDRV_PCM_FMTBIT_S32_BE;
1938c2ecf20Sopenharmony_ci#endif
1948c2ecf20Sopenharmony_ci	else
1958c2ecf20Sopenharmony_ci		return 0;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	fmt.bits[0] &= (u32)fmask;
1988c2ecf20Sopenharmony_ci	fmt.bits[1] &= (u32)(fmask >> 32);
1998c2ecf20Sopenharmony_ci	return snd_mask_refine(f, &fmt);
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_cistatic int hw_rule_playback_channels_by_format(struct snd_pcm_hw_params *params,
2058c2ecf20Sopenharmony_ci					       struct snd_pcm_hw_rule *rule)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	struct snd_interval *c = hw_param_interval(params,
2088c2ecf20Sopenharmony_ci						   SNDRV_PCM_HW_PARAM_CHANNELS);
2098c2ecf20Sopenharmony_ci	struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
2108c2ecf20Sopenharmony_ci	struct snd_interval ch;
2118c2ecf20Sopenharmony_ci	u64 fmask;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	snd_interval_any(&ch);
2148c2ecf20Sopenharmony_ci	ch.integer = 1;
2158c2ecf20Sopenharmony_ci	fmask = f->bits[0] + ((u64)f->bits[1] << 32);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	/* S32_BE is mono (and stereo) only */
2188c2ecf20Sopenharmony_ci	if (fmask == SNDRV_PCM_FMTBIT_S32_BE) {
2198c2ecf20Sopenharmony_ci		ch.min = 1;
2208c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_STEREO_BIG_ENDIAN32
2218c2ecf20Sopenharmony_ci		ch.max = 2;
2228c2ecf20Sopenharmony_ci#else
2238c2ecf20Sopenharmony_ci		ch.max = 1;
2248c2ecf20Sopenharmony_ci#endif
2258c2ecf20Sopenharmony_ci	/* U8 is stereo only */
2268c2ecf20Sopenharmony_ci	} else if (fmask == SNDRV_PCM_FMTBIT_U8)
2278c2ecf20Sopenharmony_ci		ch.min = ch.max = 2;
2288c2ecf20Sopenharmony_ci	/* S16_LE and S24_3LE must be at least stereo */
2298c2ecf20Sopenharmony_ci	else if (!(fmask & ~(SNDRV_PCM_FMTBIT_S16_LE |
2308c2ecf20Sopenharmony_ci			       SNDRV_PCM_FMTBIT_S24_3LE)))
2318c2ecf20Sopenharmony_ci		ch.min = 2;
2328c2ecf20Sopenharmony_ci	else
2338c2ecf20Sopenharmony_ci		return 0;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	return snd_interval_refine(c, &ch);
2368c2ecf20Sopenharmony_ci}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci/* Since the sample rate is a global setting, do allow the user to change the
2418c2ecf20Sopenharmony_cisample rate only if there is only one pcm device open. */
2428c2ecf20Sopenharmony_cistatic int hw_rule_sample_rate(struct snd_pcm_hw_params *params,
2438c2ecf20Sopenharmony_ci			       struct snd_pcm_hw_rule *rule)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	struct snd_interval *rate = hw_param_interval(params,
2468c2ecf20Sopenharmony_ci						      SNDRV_PCM_HW_PARAM_RATE);
2478c2ecf20Sopenharmony_ci	struct echoaudio *chip = rule->private;
2488c2ecf20Sopenharmony_ci	struct snd_interval fixed;
2498c2ecf20Sopenharmony_ci	int err;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	mutex_lock(&chip->mode_mutex);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	if (chip->can_set_rate) {
2548c2ecf20Sopenharmony_ci		err = 0;
2558c2ecf20Sopenharmony_ci	} else {
2568c2ecf20Sopenharmony_ci		snd_interval_any(&fixed);
2578c2ecf20Sopenharmony_ci		fixed.min = fixed.max = chip->sample_rate;
2588c2ecf20Sopenharmony_ci		err = snd_interval_refine(rate, &fixed);
2598c2ecf20Sopenharmony_ci	}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mode_mutex);
2628c2ecf20Sopenharmony_ci	return err;
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_cistatic int pcm_open(struct snd_pcm_substream *substream,
2678c2ecf20Sopenharmony_ci		    signed char max_channels)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	struct echoaudio *chip;
2708c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime;
2718c2ecf20Sopenharmony_ci	struct audiopipe *pipe;
2728c2ecf20Sopenharmony_ci	int err, i;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	if (max_channels <= 0)
2758c2ecf20Sopenharmony_ci		return -EAGAIN;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	chip = snd_pcm_substream_chip(substream);
2788c2ecf20Sopenharmony_ci	runtime = substream->runtime;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	pipe = kzalloc(sizeof(struct audiopipe), GFP_KERNEL);
2818c2ecf20Sopenharmony_ci	if (!pipe)
2828c2ecf20Sopenharmony_ci		return -ENOMEM;
2838c2ecf20Sopenharmony_ci	pipe->index = -1;		/* Not configured yet */
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	/* Set up hw capabilities and contraints */
2868c2ecf20Sopenharmony_ci	memcpy(&pipe->hw, &pcm_hardware_skel, sizeof(struct snd_pcm_hardware));
2878c2ecf20Sopenharmony_ci	dev_dbg(chip->card->dev, "max_channels=%d\n", max_channels);
2888c2ecf20Sopenharmony_ci	pipe->constr.list = channels_list;
2898c2ecf20Sopenharmony_ci	pipe->constr.mask = 0;
2908c2ecf20Sopenharmony_ci	for (i = 0; channels_list[i] <= max_channels; i++);
2918c2ecf20Sopenharmony_ci	pipe->constr.count = i;
2928c2ecf20Sopenharmony_ci	if (pipe->hw.channels_max > max_channels)
2938c2ecf20Sopenharmony_ci		pipe->hw.channels_max = max_channels;
2948c2ecf20Sopenharmony_ci	if (chip->digital_mode == DIGITAL_MODE_ADAT) {
2958c2ecf20Sopenharmony_ci		pipe->hw.rate_max = 48000;
2968c2ecf20Sopenharmony_ci		pipe->hw.rates &= SNDRV_PCM_RATE_8000_48000;
2978c2ecf20Sopenharmony_ci	}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	runtime->hw = pipe->hw;
3008c2ecf20Sopenharmony_ci	runtime->private_data = pipe;
3018c2ecf20Sopenharmony_ci	runtime->private_free = audiopipe_free;
3028c2ecf20Sopenharmony_ci	snd_pcm_set_sync(substream);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	/* Only mono and any even number of channels are allowed */
3058c2ecf20Sopenharmony_ci	if ((err = snd_pcm_hw_constraint_list(runtime, 0,
3068c2ecf20Sopenharmony_ci					      SNDRV_PCM_HW_PARAM_CHANNELS,
3078c2ecf20Sopenharmony_ci					      &pipe->constr)) < 0)
3088c2ecf20Sopenharmony_ci		return err;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	/* All periods should have the same size */
3118c2ecf20Sopenharmony_ci	if ((err = snd_pcm_hw_constraint_integer(runtime,
3128c2ecf20Sopenharmony_ci						 SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
3138c2ecf20Sopenharmony_ci		return err;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	/* The hw accesses memory in chunks 32 frames long and they should be
3168c2ecf20Sopenharmony_ci	32-bytes-aligned. It's not a requirement, but it seems that IRQs are
3178c2ecf20Sopenharmony_ci	generated with a resolution of 32 frames. Thus we need the following */
3188c2ecf20Sopenharmony_ci	if ((err = snd_pcm_hw_constraint_step(runtime, 0,
3198c2ecf20Sopenharmony_ci					      SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
3208c2ecf20Sopenharmony_ci					      32)) < 0)
3218c2ecf20Sopenharmony_ci		return err;
3228c2ecf20Sopenharmony_ci	if ((err = snd_pcm_hw_constraint_step(runtime, 0,
3238c2ecf20Sopenharmony_ci					      SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
3248c2ecf20Sopenharmony_ci					      32)) < 0)
3258c2ecf20Sopenharmony_ci		return err;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
3288c2ecf20Sopenharmony_ci				       SNDRV_PCM_HW_PARAM_RATE,
3298c2ecf20Sopenharmony_ci					hw_rule_sample_rate, chip,
3308c2ecf20Sopenharmony_ci				       SNDRV_PCM_HW_PARAM_RATE, -1)) < 0)
3318c2ecf20Sopenharmony_ci		return err;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	/* Allocate a page for the scatter-gather list */
3348c2ecf20Sopenharmony_ci	if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
3358c2ecf20Sopenharmony_ci				       &chip->pci->dev,
3368c2ecf20Sopenharmony_ci				       PAGE_SIZE, &pipe->sgpage)) < 0) {
3378c2ecf20Sopenharmony_ci		dev_err(chip->card->dev, "s-g list allocation failed\n");
3388c2ecf20Sopenharmony_ci		return err;
3398c2ecf20Sopenharmony_ci	}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	/*
3428c2ecf20Sopenharmony_ci	 * Sole ownership required to set the rate
3438c2ecf20Sopenharmony_ci	 */
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	dev_dbg(chip->card->dev, "pcm_open opencount=%d can_set_rate=%d, rate_set=%d",
3468c2ecf20Sopenharmony_ci		chip->opencount, chip->can_set_rate, chip->rate_set);
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	chip->opencount++;
3498c2ecf20Sopenharmony_ci	if (chip->opencount > 1 && chip->rate_set)
3508c2ecf20Sopenharmony_ci		chip->can_set_rate = 0;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	return 0;
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_cistatic int pcm_analog_in_open(struct snd_pcm_substream *substream)
3588c2ecf20Sopenharmony_ci{
3598c2ecf20Sopenharmony_ci	struct echoaudio *chip = snd_pcm_substream_chip(substream);
3608c2ecf20Sopenharmony_ci	int err;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	if ((err = pcm_open(substream, num_analog_busses_in(chip) -
3638c2ecf20Sopenharmony_ci			    substream->number)) < 0)
3648c2ecf20Sopenharmony_ci		return err;
3658c2ecf20Sopenharmony_ci	if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
3668c2ecf20Sopenharmony_ci				       SNDRV_PCM_HW_PARAM_CHANNELS,
3678c2ecf20Sopenharmony_ci				       hw_rule_capture_channels_by_format, NULL,
3688c2ecf20Sopenharmony_ci				       SNDRV_PCM_HW_PARAM_FORMAT, -1)) < 0)
3698c2ecf20Sopenharmony_ci		return err;
3708c2ecf20Sopenharmony_ci	if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
3718c2ecf20Sopenharmony_ci				       SNDRV_PCM_HW_PARAM_FORMAT,
3728c2ecf20Sopenharmony_ci				       hw_rule_capture_format_by_channels, NULL,
3738c2ecf20Sopenharmony_ci				       SNDRV_PCM_HW_PARAM_CHANNELS, -1)) < 0)
3748c2ecf20Sopenharmony_ci		return err;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	return 0;
3778c2ecf20Sopenharmony_ci}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_cistatic int pcm_analog_out_open(struct snd_pcm_substream *substream)
3828c2ecf20Sopenharmony_ci{
3838c2ecf20Sopenharmony_ci	struct echoaudio *chip = snd_pcm_substream_chip(substream);
3848c2ecf20Sopenharmony_ci	int max_channels, err;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_VMIXER
3878c2ecf20Sopenharmony_ci	max_channels = num_pipes_out(chip);
3888c2ecf20Sopenharmony_ci#else
3898c2ecf20Sopenharmony_ci	max_channels = num_analog_busses_out(chip);
3908c2ecf20Sopenharmony_ci#endif
3918c2ecf20Sopenharmony_ci	if ((err = pcm_open(substream, max_channels - substream->number)) < 0)
3928c2ecf20Sopenharmony_ci		return err;
3938c2ecf20Sopenharmony_ci	if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
3948c2ecf20Sopenharmony_ci				       SNDRV_PCM_HW_PARAM_CHANNELS,
3958c2ecf20Sopenharmony_ci				       hw_rule_playback_channels_by_format,
3968c2ecf20Sopenharmony_ci				       NULL,
3978c2ecf20Sopenharmony_ci				       SNDRV_PCM_HW_PARAM_FORMAT, -1)) < 0)
3988c2ecf20Sopenharmony_ci		return err;
3998c2ecf20Sopenharmony_ci	if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
4008c2ecf20Sopenharmony_ci				       SNDRV_PCM_HW_PARAM_FORMAT,
4018c2ecf20Sopenharmony_ci				       hw_rule_playback_format_by_channels,
4028c2ecf20Sopenharmony_ci				       NULL,
4038c2ecf20Sopenharmony_ci				       SNDRV_PCM_HW_PARAM_CHANNELS, -1)) < 0)
4048c2ecf20Sopenharmony_ci		return err;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	return 0;
4078c2ecf20Sopenharmony_ci}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_DIGITAL_IO
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cistatic int pcm_digital_in_open(struct snd_pcm_substream *substream)
4148c2ecf20Sopenharmony_ci{
4158c2ecf20Sopenharmony_ci	struct echoaudio *chip = snd_pcm_substream_chip(substream);
4168c2ecf20Sopenharmony_ci	int err, max_channels;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	max_channels = num_digital_busses_in(chip) - substream->number;
4198c2ecf20Sopenharmony_ci	mutex_lock(&chip->mode_mutex);
4208c2ecf20Sopenharmony_ci	if (chip->digital_mode == DIGITAL_MODE_ADAT)
4218c2ecf20Sopenharmony_ci		err = pcm_open(substream, max_channels);
4228c2ecf20Sopenharmony_ci	else	/* If the card has ADAT, subtract the 6 channels
4238c2ecf20Sopenharmony_ci		 * that S/PDIF doesn't have
4248c2ecf20Sopenharmony_ci		 */
4258c2ecf20Sopenharmony_ci		err = pcm_open(substream, max_channels - ECHOCARD_HAS_ADAT);
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	if (err < 0)
4288c2ecf20Sopenharmony_ci		goto din_exit;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
4318c2ecf20Sopenharmony_ci				       SNDRV_PCM_HW_PARAM_CHANNELS,
4328c2ecf20Sopenharmony_ci				       hw_rule_capture_channels_by_format, NULL,
4338c2ecf20Sopenharmony_ci				       SNDRV_PCM_HW_PARAM_FORMAT, -1)) < 0)
4348c2ecf20Sopenharmony_ci		goto din_exit;
4358c2ecf20Sopenharmony_ci	if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
4368c2ecf20Sopenharmony_ci				       SNDRV_PCM_HW_PARAM_FORMAT,
4378c2ecf20Sopenharmony_ci				       hw_rule_capture_format_by_channels, NULL,
4388c2ecf20Sopenharmony_ci				       SNDRV_PCM_HW_PARAM_CHANNELS, -1)) < 0)
4398c2ecf20Sopenharmony_ci		goto din_exit;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_cidin_exit:
4428c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mode_mutex);
4438c2ecf20Sopenharmony_ci	return err;
4448c2ecf20Sopenharmony_ci}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci#ifndef ECHOCARD_HAS_VMIXER	/* See the note in snd_echo_new_pcm() */
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_cistatic int pcm_digital_out_open(struct snd_pcm_substream *substream)
4518c2ecf20Sopenharmony_ci{
4528c2ecf20Sopenharmony_ci	struct echoaudio *chip = snd_pcm_substream_chip(substream);
4538c2ecf20Sopenharmony_ci	int err, max_channels;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	max_channels = num_digital_busses_out(chip) - substream->number;
4568c2ecf20Sopenharmony_ci	mutex_lock(&chip->mode_mutex);
4578c2ecf20Sopenharmony_ci	if (chip->digital_mode == DIGITAL_MODE_ADAT)
4588c2ecf20Sopenharmony_ci		err = pcm_open(substream, max_channels);
4598c2ecf20Sopenharmony_ci	else	/* If the card has ADAT, subtract the 6 channels
4608c2ecf20Sopenharmony_ci		 * that S/PDIF doesn't have
4618c2ecf20Sopenharmony_ci		 */
4628c2ecf20Sopenharmony_ci		err = pcm_open(substream, max_channels - ECHOCARD_HAS_ADAT);
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	if (err < 0)
4658c2ecf20Sopenharmony_ci		goto dout_exit;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
4688c2ecf20Sopenharmony_ci				       SNDRV_PCM_HW_PARAM_CHANNELS,
4698c2ecf20Sopenharmony_ci				       hw_rule_playback_channels_by_format,
4708c2ecf20Sopenharmony_ci				       NULL, SNDRV_PCM_HW_PARAM_FORMAT,
4718c2ecf20Sopenharmony_ci				       -1)) < 0)
4728c2ecf20Sopenharmony_ci		goto dout_exit;
4738c2ecf20Sopenharmony_ci	if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
4748c2ecf20Sopenharmony_ci				       SNDRV_PCM_HW_PARAM_FORMAT,
4758c2ecf20Sopenharmony_ci				       hw_rule_playback_format_by_channels,
4768c2ecf20Sopenharmony_ci				       NULL, SNDRV_PCM_HW_PARAM_CHANNELS,
4778c2ecf20Sopenharmony_ci				       -1)) < 0)
4788c2ecf20Sopenharmony_ci		goto dout_exit;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_cidout_exit:
4818c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mode_mutex);
4828c2ecf20Sopenharmony_ci	return err;
4838c2ecf20Sopenharmony_ci}
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci#endif /* !ECHOCARD_HAS_VMIXER */
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_DIGITAL_IO */
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_cistatic int pcm_close(struct snd_pcm_substream *substream)
4928c2ecf20Sopenharmony_ci{
4938c2ecf20Sopenharmony_ci	struct echoaudio *chip = snd_pcm_substream_chip(substream);
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	/* Nothing to do here. Audio is already off and pipe will be
4968c2ecf20Sopenharmony_ci	 * freed by its callback
4978c2ecf20Sopenharmony_ci	 */
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	mutex_lock(&chip->mode_mutex);
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	dev_dbg(chip->card->dev, "pcm_open opencount=%d can_set_rate=%d, rate_set=%d",
5028c2ecf20Sopenharmony_ci		chip->opencount, chip->can_set_rate, chip->rate_set);
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	chip->opencount--;
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	switch (chip->opencount) {
5078c2ecf20Sopenharmony_ci	case 1:
5088c2ecf20Sopenharmony_ci		chip->can_set_rate = 1;
5098c2ecf20Sopenharmony_ci		break;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	case 0:
5128c2ecf20Sopenharmony_ci		chip->rate_set = 0;
5138c2ecf20Sopenharmony_ci		break;
5148c2ecf20Sopenharmony_ci	}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mode_mutex);
5178c2ecf20Sopenharmony_ci	return 0;
5188c2ecf20Sopenharmony_ci}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci/* Channel allocation and scatter-gather list setup */
5238c2ecf20Sopenharmony_cistatic int init_engine(struct snd_pcm_substream *substream,
5248c2ecf20Sopenharmony_ci		       struct snd_pcm_hw_params *hw_params,
5258c2ecf20Sopenharmony_ci		       int pipe_index, int interleave)
5268c2ecf20Sopenharmony_ci{
5278c2ecf20Sopenharmony_ci	struct echoaudio *chip;
5288c2ecf20Sopenharmony_ci	int err, per, rest, page, edge, offs;
5298c2ecf20Sopenharmony_ci	struct audiopipe *pipe;
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	chip = snd_pcm_substream_chip(substream);
5328c2ecf20Sopenharmony_ci	pipe = (struct audiopipe *) substream->runtime->private_data;
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	/* Sets up che hardware. If it's already initialized, reset and
5358c2ecf20Sopenharmony_ci	 * redo with the new parameters
5368c2ecf20Sopenharmony_ci	 */
5378c2ecf20Sopenharmony_ci	spin_lock_irq(&chip->lock);
5388c2ecf20Sopenharmony_ci	if (pipe->index >= 0) {
5398c2ecf20Sopenharmony_ci		dev_dbg(chip->card->dev, "hwp_ie free(%d)\n", pipe->index);
5408c2ecf20Sopenharmony_ci		err = free_pipes(chip, pipe);
5418c2ecf20Sopenharmony_ci		snd_BUG_ON(err);
5428c2ecf20Sopenharmony_ci		chip->substream[pipe->index] = NULL;
5438c2ecf20Sopenharmony_ci	}
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	err = allocate_pipes(chip, pipe, pipe_index, interleave);
5468c2ecf20Sopenharmony_ci	if (err < 0) {
5478c2ecf20Sopenharmony_ci		spin_unlock_irq(&chip->lock);
5488c2ecf20Sopenharmony_ci		dev_err(chip->card->dev, "allocate_pipes(%d) err=%d\n",
5498c2ecf20Sopenharmony_ci			pipe_index, err);
5508c2ecf20Sopenharmony_ci		return err;
5518c2ecf20Sopenharmony_ci	}
5528c2ecf20Sopenharmony_ci	spin_unlock_irq(&chip->lock);
5538c2ecf20Sopenharmony_ci	dev_dbg(chip->card->dev, "allocate_pipes()=%d\n", pipe_index);
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	dev_dbg(chip->card->dev,
5568c2ecf20Sopenharmony_ci		"pcm_hw_params (bufsize=%dB periods=%d persize=%dB)\n",
5578c2ecf20Sopenharmony_ci		params_buffer_bytes(hw_params), params_periods(hw_params),
5588c2ecf20Sopenharmony_ci		params_period_bytes(hw_params));
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	sglist_init(chip, pipe);
5618c2ecf20Sopenharmony_ci	edge = PAGE_SIZE;
5628c2ecf20Sopenharmony_ci	for (offs = page = per = 0; offs < params_buffer_bytes(hw_params);
5638c2ecf20Sopenharmony_ci	     per++) {
5648c2ecf20Sopenharmony_ci		rest = params_period_bytes(hw_params);
5658c2ecf20Sopenharmony_ci		if (offs + rest > params_buffer_bytes(hw_params))
5668c2ecf20Sopenharmony_ci			rest = params_buffer_bytes(hw_params) - offs;
5678c2ecf20Sopenharmony_ci		while (rest) {
5688c2ecf20Sopenharmony_ci			dma_addr_t addr;
5698c2ecf20Sopenharmony_ci			addr = snd_pcm_sgbuf_get_addr(substream, offs);
5708c2ecf20Sopenharmony_ci			if (rest <= edge - offs) {
5718c2ecf20Sopenharmony_ci				sglist_add_mapping(chip, pipe, addr, rest);
5728c2ecf20Sopenharmony_ci				sglist_add_irq(chip, pipe);
5738c2ecf20Sopenharmony_ci				offs += rest;
5748c2ecf20Sopenharmony_ci				rest = 0;
5758c2ecf20Sopenharmony_ci			} else {
5768c2ecf20Sopenharmony_ci				sglist_add_mapping(chip, pipe, addr,
5778c2ecf20Sopenharmony_ci						   edge - offs);
5788c2ecf20Sopenharmony_ci				rest -= edge - offs;
5798c2ecf20Sopenharmony_ci				offs = edge;
5808c2ecf20Sopenharmony_ci			}
5818c2ecf20Sopenharmony_ci			if (offs == edge) {
5828c2ecf20Sopenharmony_ci				edge += PAGE_SIZE;
5838c2ecf20Sopenharmony_ci				page++;
5848c2ecf20Sopenharmony_ci			}
5858c2ecf20Sopenharmony_ci		}
5868c2ecf20Sopenharmony_ci	}
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	/* Close the ring buffer */
5898c2ecf20Sopenharmony_ci	sglist_wrap(chip, pipe);
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	/* This stuff is used by the irq handler, so it must be
5928c2ecf20Sopenharmony_ci	 * initialized before chip->substream
5938c2ecf20Sopenharmony_ci	 */
5948c2ecf20Sopenharmony_ci	pipe->last_period = 0;
5958c2ecf20Sopenharmony_ci	pipe->last_counter = 0;
5968c2ecf20Sopenharmony_ci	pipe->position = 0;
5978c2ecf20Sopenharmony_ci	smp_wmb();
5988c2ecf20Sopenharmony_ci	chip->substream[pipe_index] = substream;
5998c2ecf20Sopenharmony_ci	chip->rate_set = 1;
6008c2ecf20Sopenharmony_ci	spin_lock_irq(&chip->lock);
6018c2ecf20Sopenharmony_ci	set_sample_rate(chip, hw_params->rate_num / hw_params->rate_den);
6028c2ecf20Sopenharmony_ci	spin_unlock_irq(&chip->lock);
6038c2ecf20Sopenharmony_ci	return 0;
6048c2ecf20Sopenharmony_ci}
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_cistatic int pcm_analog_in_hw_params(struct snd_pcm_substream *substream,
6098c2ecf20Sopenharmony_ci				   struct snd_pcm_hw_params *hw_params)
6108c2ecf20Sopenharmony_ci{
6118c2ecf20Sopenharmony_ci	struct echoaudio *chip = snd_pcm_substream_chip(substream);
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	return init_engine(substream, hw_params, px_analog_in(chip) +
6148c2ecf20Sopenharmony_ci			substream->number, params_channels(hw_params));
6158c2ecf20Sopenharmony_ci}
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_cistatic int pcm_analog_out_hw_params(struct snd_pcm_substream *substream,
6208c2ecf20Sopenharmony_ci				    struct snd_pcm_hw_params *hw_params)
6218c2ecf20Sopenharmony_ci{
6228c2ecf20Sopenharmony_ci	return init_engine(substream, hw_params, substream->number,
6238c2ecf20Sopenharmony_ci			   params_channels(hw_params));
6248c2ecf20Sopenharmony_ci}
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_DIGITAL_IO
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_cistatic int pcm_digital_in_hw_params(struct snd_pcm_substream *substream,
6318c2ecf20Sopenharmony_ci				    struct snd_pcm_hw_params *hw_params)
6328c2ecf20Sopenharmony_ci{
6338c2ecf20Sopenharmony_ci	struct echoaudio *chip = snd_pcm_substream_chip(substream);
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	return init_engine(substream, hw_params, px_digital_in(chip) +
6368c2ecf20Sopenharmony_ci			substream->number, params_channels(hw_params));
6378c2ecf20Sopenharmony_ci}
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci#ifndef ECHOCARD_HAS_VMIXER	/* See the note in snd_echo_new_pcm() */
6428c2ecf20Sopenharmony_cistatic int pcm_digital_out_hw_params(struct snd_pcm_substream *substream,
6438c2ecf20Sopenharmony_ci				     struct snd_pcm_hw_params *hw_params)
6448c2ecf20Sopenharmony_ci{
6458c2ecf20Sopenharmony_ci	struct echoaudio *chip = snd_pcm_substream_chip(substream);
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	return init_engine(substream, hw_params, px_digital_out(chip) +
6488c2ecf20Sopenharmony_ci			substream->number, params_channels(hw_params));
6498c2ecf20Sopenharmony_ci}
6508c2ecf20Sopenharmony_ci#endif /* !ECHOCARD_HAS_VMIXER */
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_DIGITAL_IO */
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_cistatic int pcm_hw_free(struct snd_pcm_substream *substream)
6578c2ecf20Sopenharmony_ci{
6588c2ecf20Sopenharmony_ci	struct echoaudio *chip;
6598c2ecf20Sopenharmony_ci	struct audiopipe *pipe;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	chip = snd_pcm_substream_chip(substream);
6628c2ecf20Sopenharmony_ci	pipe = (struct audiopipe *) substream->runtime->private_data;
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	spin_lock_irq(&chip->lock);
6658c2ecf20Sopenharmony_ci	if (pipe->index >= 0) {
6668c2ecf20Sopenharmony_ci		dev_dbg(chip->card->dev, "pcm_hw_free(%d)\n", pipe->index);
6678c2ecf20Sopenharmony_ci		free_pipes(chip, pipe);
6688c2ecf20Sopenharmony_ci		chip->substream[pipe->index] = NULL;
6698c2ecf20Sopenharmony_ci		pipe->index = -1;
6708c2ecf20Sopenharmony_ci	}
6718c2ecf20Sopenharmony_ci	spin_unlock_irq(&chip->lock);
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	return 0;
6748c2ecf20Sopenharmony_ci}
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_cistatic int pcm_prepare(struct snd_pcm_substream *substream)
6798c2ecf20Sopenharmony_ci{
6808c2ecf20Sopenharmony_ci	struct echoaudio *chip = snd_pcm_substream_chip(substream);
6818c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
6828c2ecf20Sopenharmony_ci	struct audioformat format;
6838c2ecf20Sopenharmony_ci	int pipe_index = ((struct audiopipe *)runtime->private_data)->index;
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	dev_dbg(chip->card->dev, "Prepare rate=%d format=%d channels=%d\n",
6868c2ecf20Sopenharmony_ci		runtime->rate, runtime->format, runtime->channels);
6878c2ecf20Sopenharmony_ci	format.interleave = runtime->channels;
6888c2ecf20Sopenharmony_ci	format.data_are_bigendian = 0;
6898c2ecf20Sopenharmony_ci	format.mono_to_stereo = 0;
6908c2ecf20Sopenharmony_ci	switch (runtime->format) {
6918c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_U8:
6928c2ecf20Sopenharmony_ci		format.bits_per_sample = 8;
6938c2ecf20Sopenharmony_ci		break;
6948c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S16_LE:
6958c2ecf20Sopenharmony_ci		format.bits_per_sample = 16;
6968c2ecf20Sopenharmony_ci		break;
6978c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S24_3LE:
6988c2ecf20Sopenharmony_ci		format.bits_per_sample = 24;
6998c2ecf20Sopenharmony_ci		break;
7008c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S32_BE:
7018c2ecf20Sopenharmony_ci		format.data_are_bigendian = 1;
7028c2ecf20Sopenharmony_ci		fallthrough;
7038c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S32_LE:
7048c2ecf20Sopenharmony_ci		format.bits_per_sample = 32;
7058c2ecf20Sopenharmony_ci		break;
7068c2ecf20Sopenharmony_ci	default:
7078c2ecf20Sopenharmony_ci		dev_err(chip->card->dev,
7088c2ecf20Sopenharmony_ci			"Prepare error: unsupported format %d\n",
7098c2ecf20Sopenharmony_ci			runtime->format);
7108c2ecf20Sopenharmony_ci		return -EINVAL;
7118c2ecf20Sopenharmony_ci	}
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	if (snd_BUG_ON(pipe_index >= px_num(chip)))
7148c2ecf20Sopenharmony_ci		return -EINVAL;
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	/*
7178c2ecf20Sopenharmony_ci	 * We passed checks we can do independently; now take
7188c2ecf20Sopenharmony_ci	 * exclusive control
7198c2ecf20Sopenharmony_ci	 */
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	spin_lock_irq(&chip->lock);
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!is_pipe_allocated(chip, pipe_index))) {
7248c2ecf20Sopenharmony_ci		spin_unlock_irq(&chip->lock);
7258c2ecf20Sopenharmony_ci		return -EINVAL;
7268c2ecf20Sopenharmony_ci	}
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	set_audio_format(chip, pipe_index, &format);
7298c2ecf20Sopenharmony_ci	spin_unlock_irq(&chip->lock);
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	return 0;
7328c2ecf20Sopenharmony_ci}
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_cistatic int pcm_trigger(struct snd_pcm_substream *substream, int cmd)
7378c2ecf20Sopenharmony_ci{
7388c2ecf20Sopenharmony_ci	struct echoaudio *chip = snd_pcm_substream_chip(substream);
7398c2ecf20Sopenharmony_ci	struct audiopipe *pipe;
7408c2ecf20Sopenharmony_ci	int i, err;
7418c2ecf20Sopenharmony_ci	u32 channelmask = 0;
7428c2ecf20Sopenharmony_ci	struct snd_pcm_substream *s;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	snd_pcm_group_for_each_entry(s, substream) {
7458c2ecf20Sopenharmony_ci		for (i = 0; i < DSP_MAXPIPES; i++) {
7468c2ecf20Sopenharmony_ci			if (s == chip->substream[i]) {
7478c2ecf20Sopenharmony_ci				channelmask |= 1 << i;
7488c2ecf20Sopenharmony_ci				snd_pcm_trigger_done(s, substream);
7498c2ecf20Sopenharmony_ci			}
7508c2ecf20Sopenharmony_ci		}
7518c2ecf20Sopenharmony_ci	}
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	spin_lock(&chip->lock);
7548c2ecf20Sopenharmony_ci	switch (cmd) {
7558c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
7568c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
7578c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
7588c2ecf20Sopenharmony_ci		for (i = 0; i < DSP_MAXPIPES; i++) {
7598c2ecf20Sopenharmony_ci			if (channelmask & (1 << i)) {
7608c2ecf20Sopenharmony_ci				pipe = chip->substream[i]->runtime->private_data;
7618c2ecf20Sopenharmony_ci				switch (pipe->state) {
7628c2ecf20Sopenharmony_ci				case PIPE_STATE_STOPPED:
7638c2ecf20Sopenharmony_ci					pipe->last_period = 0;
7648c2ecf20Sopenharmony_ci					pipe->last_counter = 0;
7658c2ecf20Sopenharmony_ci					pipe->position = 0;
7668c2ecf20Sopenharmony_ci					*pipe->dma_counter = 0;
7678c2ecf20Sopenharmony_ci					fallthrough;
7688c2ecf20Sopenharmony_ci				case PIPE_STATE_PAUSED:
7698c2ecf20Sopenharmony_ci					pipe->state = PIPE_STATE_STARTED;
7708c2ecf20Sopenharmony_ci					break;
7718c2ecf20Sopenharmony_ci				case PIPE_STATE_STARTED:
7728c2ecf20Sopenharmony_ci					break;
7738c2ecf20Sopenharmony_ci				}
7748c2ecf20Sopenharmony_ci			}
7758c2ecf20Sopenharmony_ci		}
7768c2ecf20Sopenharmony_ci		err = start_transport(chip, channelmask,
7778c2ecf20Sopenharmony_ci				      chip->pipe_cyclic_mask);
7788c2ecf20Sopenharmony_ci		break;
7798c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
7808c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
7818c2ecf20Sopenharmony_ci		for (i = 0; i < DSP_MAXPIPES; i++) {
7828c2ecf20Sopenharmony_ci			if (channelmask & (1 << i)) {
7838c2ecf20Sopenharmony_ci				pipe = chip->substream[i]->runtime->private_data;
7848c2ecf20Sopenharmony_ci				pipe->state = PIPE_STATE_STOPPED;
7858c2ecf20Sopenharmony_ci			}
7868c2ecf20Sopenharmony_ci		}
7878c2ecf20Sopenharmony_ci		err = stop_transport(chip, channelmask);
7888c2ecf20Sopenharmony_ci		break;
7898c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
7908c2ecf20Sopenharmony_ci		for (i = 0; i < DSP_MAXPIPES; i++) {
7918c2ecf20Sopenharmony_ci			if (channelmask & (1 << i)) {
7928c2ecf20Sopenharmony_ci				pipe = chip->substream[i]->runtime->private_data;
7938c2ecf20Sopenharmony_ci				pipe->state = PIPE_STATE_PAUSED;
7948c2ecf20Sopenharmony_ci			}
7958c2ecf20Sopenharmony_ci		}
7968c2ecf20Sopenharmony_ci		err = pause_transport(chip, channelmask);
7978c2ecf20Sopenharmony_ci		break;
7988c2ecf20Sopenharmony_ci	default:
7998c2ecf20Sopenharmony_ci		err = -EINVAL;
8008c2ecf20Sopenharmony_ci	}
8018c2ecf20Sopenharmony_ci	spin_unlock(&chip->lock);
8028c2ecf20Sopenharmony_ci	return err;
8038c2ecf20Sopenharmony_ci}
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t pcm_pointer(struct snd_pcm_substream *substream)
8088c2ecf20Sopenharmony_ci{
8098c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
8108c2ecf20Sopenharmony_ci	struct audiopipe *pipe = runtime->private_data;
8118c2ecf20Sopenharmony_ci	u32 counter, step;
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	/*
8148c2ecf20Sopenharmony_ci	 * IRQ handling runs concurrently. Do not share tracking of
8158c2ecf20Sopenharmony_ci	 * counter with it, which would race or require locking
8168c2ecf20Sopenharmony_ci	 */
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	counter = le32_to_cpu(*pipe->dma_counter);  /* presumed atomic */
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	step = counter - pipe->last_counter;  /* handles wrapping */
8218c2ecf20Sopenharmony_ci	pipe->last_counter = counter;
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	/* counter doesn't neccessarily wrap on a multiple of
8248c2ecf20Sopenharmony_ci	 * buffer_size, so can't derive the position; must
8258c2ecf20Sopenharmony_ci	 * accumulate */
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	pipe->position += step;
8288c2ecf20Sopenharmony_ci	pipe->position %= frames_to_bytes(runtime, runtime->buffer_size); /* wrap */
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	return bytes_to_frames(runtime, pipe->position);
8318c2ecf20Sopenharmony_ci}
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci/* pcm *_ops structures */
8368c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops analog_playback_ops = {
8378c2ecf20Sopenharmony_ci	.open = pcm_analog_out_open,
8388c2ecf20Sopenharmony_ci	.close = pcm_close,
8398c2ecf20Sopenharmony_ci	.hw_params = pcm_analog_out_hw_params,
8408c2ecf20Sopenharmony_ci	.hw_free = pcm_hw_free,
8418c2ecf20Sopenharmony_ci	.prepare = pcm_prepare,
8428c2ecf20Sopenharmony_ci	.trigger = pcm_trigger,
8438c2ecf20Sopenharmony_ci	.pointer = pcm_pointer,
8448c2ecf20Sopenharmony_ci};
8458c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops analog_capture_ops = {
8468c2ecf20Sopenharmony_ci	.open = pcm_analog_in_open,
8478c2ecf20Sopenharmony_ci	.close = pcm_close,
8488c2ecf20Sopenharmony_ci	.hw_params = pcm_analog_in_hw_params,
8498c2ecf20Sopenharmony_ci	.hw_free = pcm_hw_free,
8508c2ecf20Sopenharmony_ci	.prepare = pcm_prepare,
8518c2ecf20Sopenharmony_ci	.trigger = pcm_trigger,
8528c2ecf20Sopenharmony_ci	.pointer = pcm_pointer,
8538c2ecf20Sopenharmony_ci};
8548c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_DIGITAL_IO
8558c2ecf20Sopenharmony_ci#ifndef ECHOCARD_HAS_VMIXER
8568c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops digital_playback_ops = {
8578c2ecf20Sopenharmony_ci	.open = pcm_digital_out_open,
8588c2ecf20Sopenharmony_ci	.close = pcm_close,
8598c2ecf20Sopenharmony_ci	.hw_params = pcm_digital_out_hw_params,
8608c2ecf20Sopenharmony_ci	.hw_free = pcm_hw_free,
8618c2ecf20Sopenharmony_ci	.prepare = pcm_prepare,
8628c2ecf20Sopenharmony_ci	.trigger = pcm_trigger,
8638c2ecf20Sopenharmony_ci	.pointer = pcm_pointer,
8648c2ecf20Sopenharmony_ci};
8658c2ecf20Sopenharmony_ci#endif /* !ECHOCARD_HAS_VMIXER */
8668c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops digital_capture_ops = {
8678c2ecf20Sopenharmony_ci	.open = pcm_digital_in_open,
8688c2ecf20Sopenharmony_ci	.close = pcm_close,
8698c2ecf20Sopenharmony_ci	.hw_params = pcm_digital_in_hw_params,
8708c2ecf20Sopenharmony_ci	.hw_free = pcm_hw_free,
8718c2ecf20Sopenharmony_ci	.prepare = pcm_prepare,
8728c2ecf20Sopenharmony_ci	.trigger = pcm_trigger,
8738c2ecf20Sopenharmony_ci	.pointer = pcm_pointer,
8748c2ecf20Sopenharmony_ci};
8758c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_DIGITAL_IO */
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci/* Preallocate memory only for the first substream because it's the most
8808c2ecf20Sopenharmony_ci * used one
8818c2ecf20Sopenharmony_ci */
8828c2ecf20Sopenharmony_cistatic void snd_echo_preallocate_pages(struct snd_pcm *pcm, struct device *dev)
8838c2ecf20Sopenharmony_ci{
8848c2ecf20Sopenharmony_ci	struct snd_pcm_substream *ss;
8858c2ecf20Sopenharmony_ci	int stream;
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	for (stream = 0; stream < 2; stream++)
8888c2ecf20Sopenharmony_ci		for (ss = pcm->streams[stream].substream; ss; ss = ss->next)
8898c2ecf20Sopenharmony_ci			snd_pcm_set_managed_buffer(ss, SNDRV_DMA_TYPE_DEV_SG,
8908c2ecf20Sopenharmony_ci						   dev,
8918c2ecf20Sopenharmony_ci						   ss->number ? 0 : 128<<10,
8928c2ecf20Sopenharmony_ci						   256<<10);
8938c2ecf20Sopenharmony_ci}
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci/*<--snd_echo_probe() */
8988c2ecf20Sopenharmony_cistatic int snd_echo_new_pcm(struct echoaudio *chip)
8998c2ecf20Sopenharmony_ci{
9008c2ecf20Sopenharmony_ci	struct snd_pcm *pcm;
9018c2ecf20Sopenharmony_ci	int err;
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_VMIXER
9048c2ecf20Sopenharmony_ci	/* This card has a Vmixer, that is there is no direct mapping from PCM
9058c2ecf20Sopenharmony_ci	streams to physical outputs. The user can mix the streams as he wishes
9068c2ecf20Sopenharmony_ci	via control interface and it's possible to send any stream to any
9078c2ecf20Sopenharmony_ci	output, thus it makes no sense to keep analog and digital outputs
9088c2ecf20Sopenharmony_ci	separated */
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci	/* PCM#0 Virtual outputs and analog inputs */
9118c2ecf20Sopenharmony_ci	if ((err = snd_pcm_new(chip->card, "PCM", 0, num_pipes_out(chip),
9128c2ecf20Sopenharmony_ci				num_analog_busses_in(chip), &pcm)) < 0)
9138c2ecf20Sopenharmony_ci		return err;
9148c2ecf20Sopenharmony_ci	pcm->private_data = chip;
9158c2ecf20Sopenharmony_ci	chip->analog_pcm = pcm;
9168c2ecf20Sopenharmony_ci	strcpy(pcm->name, chip->card->shortname);
9178c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &analog_playback_ops);
9188c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &analog_capture_ops);
9198c2ecf20Sopenharmony_ci	snd_echo_preallocate_pages(pcm, &chip->pci->dev);
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_DIGITAL_IO
9228c2ecf20Sopenharmony_ci	/* PCM#1 Digital inputs, no outputs */
9238c2ecf20Sopenharmony_ci	if ((err = snd_pcm_new(chip->card, "Digital PCM", 1, 0,
9248c2ecf20Sopenharmony_ci			       num_digital_busses_in(chip), &pcm)) < 0)
9258c2ecf20Sopenharmony_ci		return err;
9268c2ecf20Sopenharmony_ci	pcm->private_data = chip;
9278c2ecf20Sopenharmony_ci	chip->digital_pcm = pcm;
9288c2ecf20Sopenharmony_ci	strcpy(pcm->name, chip->card->shortname);
9298c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &digital_capture_ops);
9308c2ecf20Sopenharmony_ci	snd_echo_preallocate_pages(pcm, &chip->pci->dev);
9318c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_DIGITAL_IO */
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci#else /* ECHOCARD_HAS_VMIXER */
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci	/* The card can manage substreams formed by analog and digital channels
9368c2ecf20Sopenharmony_ci	at the same time, but I prefer to keep analog and digital channels
9378c2ecf20Sopenharmony_ci	separated, because that mixed thing is confusing and useless. So we
9388c2ecf20Sopenharmony_ci	register two PCM devices: */
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	/* PCM#0 Analog i/o */
9418c2ecf20Sopenharmony_ci	if ((err = snd_pcm_new(chip->card, "Analog PCM", 0,
9428c2ecf20Sopenharmony_ci			       num_analog_busses_out(chip),
9438c2ecf20Sopenharmony_ci			       num_analog_busses_in(chip), &pcm)) < 0)
9448c2ecf20Sopenharmony_ci		return err;
9458c2ecf20Sopenharmony_ci	pcm->private_data = chip;
9468c2ecf20Sopenharmony_ci	chip->analog_pcm = pcm;
9478c2ecf20Sopenharmony_ci	strcpy(pcm->name, chip->card->shortname);
9488c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &analog_playback_ops);
9498c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &analog_capture_ops);
9508c2ecf20Sopenharmony_ci	snd_echo_preallocate_pages(pcm, &chip->pci->dev);
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_DIGITAL_IO
9538c2ecf20Sopenharmony_ci	/* PCM#1 Digital i/o */
9548c2ecf20Sopenharmony_ci	if ((err = snd_pcm_new(chip->card, "Digital PCM", 1,
9558c2ecf20Sopenharmony_ci			       num_digital_busses_out(chip),
9568c2ecf20Sopenharmony_ci			       num_digital_busses_in(chip), &pcm)) < 0)
9578c2ecf20Sopenharmony_ci		return err;
9588c2ecf20Sopenharmony_ci	pcm->private_data = chip;
9598c2ecf20Sopenharmony_ci	chip->digital_pcm = pcm;
9608c2ecf20Sopenharmony_ci	strcpy(pcm->name, chip->card->shortname);
9618c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &digital_playback_ops);
9628c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &digital_capture_ops);
9638c2ecf20Sopenharmony_ci	snd_echo_preallocate_pages(pcm, &chip->pci->dev);
9648c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_DIGITAL_IO */
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_VMIXER */
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	return 0;
9698c2ecf20Sopenharmony_ci}
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci/******************************************************************************
9758c2ecf20Sopenharmony_ci	Control interface
9768c2ecf20Sopenharmony_ci******************************************************************************/
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci#if !defined(ECHOCARD_HAS_VMIXER) || defined(ECHOCARD_HAS_LINE_OUT_GAIN)
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci/******************* PCM output volume *******************/
9818c2ecf20Sopenharmony_cistatic int snd_echo_output_gain_info(struct snd_kcontrol *kcontrol,
9828c2ecf20Sopenharmony_ci				     struct snd_ctl_elem_info *uinfo)
9838c2ecf20Sopenharmony_ci{
9848c2ecf20Sopenharmony_ci	struct echoaudio *chip;
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
9878c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
9888c2ecf20Sopenharmony_ci	uinfo->count = num_busses_out(chip);
9898c2ecf20Sopenharmony_ci	uinfo->value.integer.min = ECHOGAIN_MINOUT;
9908c2ecf20Sopenharmony_ci	uinfo->value.integer.max = ECHOGAIN_MAXOUT;
9918c2ecf20Sopenharmony_ci	return 0;
9928c2ecf20Sopenharmony_ci}
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_cistatic int snd_echo_output_gain_get(struct snd_kcontrol *kcontrol,
9958c2ecf20Sopenharmony_ci				    struct snd_ctl_elem_value *ucontrol)
9968c2ecf20Sopenharmony_ci{
9978c2ecf20Sopenharmony_ci	struct echoaudio *chip;
9988c2ecf20Sopenharmony_ci	int c;
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
10018c2ecf20Sopenharmony_ci	for (c = 0; c < num_busses_out(chip); c++)
10028c2ecf20Sopenharmony_ci		ucontrol->value.integer.value[c] = chip->output_gain[c];
10038c2ecf20Sopenharmony_ci	return 0;
10048c2ecf20Sopenharmony_ci}
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_cistatic int snd_echo_output_gain_put(struct snd_kcontrol *kcontrol,
10078c2ecf20Sopenharmony_ci				    struct snd_ctl_elem_value *ucontrol)
10088c2ecf20Sopenharmony_ci{
10098c2ecf20Sopenharmony_ci	struct echoaudio *chip;
10108c2ecf20Sopenharmony_ci	int c, changed, gain;
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci	changed = 0;
10138c2ecf20Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
10148c2ecf20Sopenharmony_ci	spin_lock_irq(&chip->lock);
10158c2ecf20Sopenharmony_ci	for (c = 0; c < num_busses_out(chip); c++) {
10168c2ecf20Sopenharmony_ci		gain = ucontrol->value.integer.value[c];
10178c2ecf20Sopenharmony_ci		/* Ignore out of range values */
10188c2ecf20Sopenharmony_ci		if (gain < ECHOGAIN_MINOUT || gain > ECHOGAIN_MAXOUT)
10198c2ecf20Sopenharmony_ci			continue;
10208c2ecf20Sopenharmony_ci		if (chip->output_gain[c] != gain) {
10218c2ecf20Sopenharmony_ci			set_output_gain(chip, c, gain);
10228c2ecf20Sopenharmony_ci			changed = 1;
10238c2ecf20Sopenharmony_ci		}
10248c2ecf20Sopenharmony_ci	}
10258c2ecf20Sopenharmony_ci	if (changed)
10268c2ecf20Sopenharmony_ci		update_output_line_level(chip);
10278c2ecf20Sopenharmony_ci	spin_unlock_irq(&chip->lock);
10288c2ecf20Sopenharmony_ci	return changed;
10298c2ecf20Sopenharmony_ci}
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_LINE_OUT_GAIN
10328c2ecf20Sopenharmony_ci/* On the Mia this one controls the line-out volume */
10338c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_line_output_gain = {
10348c2ecf20Sopenharmony_ci	.name = "Line Playback Volume",
10358c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
10368c2ecf20Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
10378c2ecf20Sopenharmony_ci		  SNDRV_CTL_ELEM_ACCESS_TLV_READ,
10388c2ecf20Sopenharmony_ci	.info = snd_echo_output_gain_info,
10398c2ecf20Sopenharmony_ci	.get = snd_echo_output_gain_get,
10408c2ecf20Sopenharmony_ci	.put = snd_echo_output_gain_put,
10418c2ecf20Sopenharmony_ci	.tlv = {.p = db_scale_output_gain},
10428c2ecf20Sopenharmony_ci};
10438c2ecf20Sopenharmony_ci#else
10448c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_pcm_output_gain = {
10458c2ecf20Sopenharmony_ci	.name = "PCM Playback Volume",
10468c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
10478c2ecf20Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,
10488c2ecf20Sopenharmony_ci	.info = snd_echo_output_gain_info,
10498c2ecf20Sopenharmony_ci	.get = snd_echo_output_gain_get,
10508c2ecf20Sopenharmony_ci	.put = snd_echo_output_gain_put,
10518c2ecf20Sopenharmony_ci	.tlv = {.p = db_scale_output_gain},
10528c2ecf20Sopenharmony_ci};
10538c2ecf20Sopenharmony_ci#endif
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci#endif /* !ECHOCARD_HAS_VMIXER || ECHOCARD_HAS_LINE_OUT_GAIN */
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_INPUT_GAIN
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci/******************* Analog input volume *******************/
10628c2ecf20Sopenharmony_cistatic int snd_echo_input_gain_info(struct snd_kcontrol *kcontrol,
10638c2ecf20Sopenharmony_ci				    struct snd_ctl_elem_info *uinfo)
10648c2ecf20Sopenharmony_ci{
10658c2ecf20Sopenharmony_ci	struct echoaudio *chip;
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
10688c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
10698c2ecf20Sopenharmony_ci	uinfo->count = num_analog_busses_in(chip);
10708c2ecf20Sopenharmony_ci	uinfo->value.integer.min = ECHOGAIN_MININP;
10718c2ecf20Sopenharmony_ci	uinfo->value.integer.max = ECHOGAIN_MAXINP;
10728c2ecf20Sopenharmony_ci	return 0;
10738c2ecf20Sopenharmony_ci}
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_cistatic int snd_echo_input_gain_get(struct snd_kcontrol *kcontrol,
10768c2ecf20Sopenharmony_ci				   struct snd_ctl_elem_value *ucontrol)
10778c2ecf20Sopenharmony_ci{
10788c2ecf20Sopenharmony_ci	struct echoaudio *chip;
10798c2ecf20Sopenharmony_ci	int c;
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
10828c2ecf20Sopenharmony_ci	for (c = 0; c < num_analog_busses_in(chip); c++)
10838c2ecf20Sopenharmony_ci		ucontrol->value.integer.value[c] = chip->input_gain[c];
10848c2ecf20Sopenharmony_ci	return 0;
10858c2ecf20Sopenharmony_ci}
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_cistatic int snd_echo_input_gain_put(struct snd_kcontrol *kcontrol,
10888c2ecf20Sopenharmony_ci				   struct snd_ctl_elem_value *ucontrol)
10898c2ecf20Sopenharmony_ci{
10908c2ecf20Sopenharmony_ci	struct echoaudio *chip;
10918c2ecf20Sopenharmony_ci	int c, gain, changed;
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci	changed = 0;
10948c2ecf20Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
10958c2ecf20Sopenharmony_ci	spin_lock_irq(&chip->lock);
10968c2ecf20Sopenharmony_ci	for (c = 0; c < num_analog_busses_in(chip); c++) {
10978c2ecf20Sopenharmony_ci		gain = ucontrol->value.integer.value[c];
10988c2ecf20Sopenharmony_ci		/* Ignore out of range values */
10998c2ecf20Sopenharmony_ci		if (gain < ECHOGAIN_MININP || gain > ECHOGAIN_MAXINP)
11008c2ecf20Sopenharmony_ci			continue;
11018c2ecf20Sopenharmony_ci		if (chip->input_gain[c] != gain) {
11028c2ecf20Sopenharmony_ci			set_input_gain(chip, c, gain);
11038c2ecf20Sopenharmony_ci			changed = 1;
11048c2ecf20Sopenharmony_ci		}
11058c2ecf20Sopenharmony_ci	}
11068c2ecf20Sopenharmony_ci	if (changed)
11078c2ecf20Sopenharmony_ci		update_input_line_level(chip);
11088c2ecf20Sopenharmony_ci	spin_unlock_irq(&chip->lock);
11098c2ecf20Sopenharmony_ci	return changed;
11108c2ecf20Sopenharmony_ci}
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_input_gain, -2500, 50, 0);
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_line_input_gain = {
11158c2ecf20Sopenharmony_ci	.name = "Line Capture Volume",
11168c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
11178c2ecf20Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,
11188c2ecf20Sopenharmony_ci	.info = snd_echo_input_gain_info,
11198c2ecf20Sopenharmony_ci	.get = snd_echo_input_gain_get,
11208c2ecf20Sopenharmony_ci	.put = snd_echo_input_gain_put,
11218c2ecf20Sopenharmony_ci	.tlv = {.p = db_scale_input_gain},
11228c2ecf20Sopenharmony_ci};
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_INPUT_GAIN */
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_ci/************ Analog output nominal level (+4dBu / -10dBV) ***************/
11318c2ecf20Sopenharmony_cistatic int snd_echo_output_nominal_info (struct snd_kcontrol *kcontrol,
11328c2ecf20Sopenharmony_ci					 struct snd_ctl_elem_info *uinfo)
11338c2ecf20Sopenharmony_ci{
11348c2ecf20Sopenharmony_ci	struct echoaudio *chip;
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
11378c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
11388c2ecf20Sopenharmony_ci	uinfo->count = num_analog_busses_out(chip);
11398c2ecf20Sopenharmony_ci	uinfo->value.integer.min = 0;
11408c2ecf20Sopenharmony_ci	uinfo->value.integer.max = 1;
11418c2ecf20Sopenharmony_ci	return 0;
11428c2ecf20Sopenharmony_ci}
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_cistatic int snd_echo_output_nominal_get(struct snd_kcontrol *kcontrol,
11458c2ecf20Sopenharmony_ci				       struct snd_ctl_elem_value *ucontrol)
11468c2ecf20Sopenharmony_ci{
11478c2ecf20Sopenharmony_ci	struct echoaudio *chip;
11488c2ecf20Sopenharmony_ci	int c;
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
11518c2ecf20Sopenharmony_ci	for (c = 0; c < num_analog_busses_out(chip); c++)
11528c2ecf20Sopenharmony_ci		ucontrol->value.integer.value[c] = chip->nominal_level[c];
11538c2ecf20Sopenharmony_ci	return 0;
11548c2ecf20Sopenharmony_ci}
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_cistatic int snd_echo_output_nominal_put(struct snd_kcontrol *kcontrol,
11578c2ecf20Sopenharmony_ci				       struct snd_ctl_elem_value *ucontrol)
11588c2ecf20Sopenharmony_ci{
11598c2ecf20Sopenharmony_ci	struct echoaudio *chip;
11608c2ecf20Sopenharmony_ci	int c, changed;
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci	changed = 0;
11638c2ecf20Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
11648c2ecf20Sopenharmony_ci	spin_lock_irq(&chip->lock);
11658c2ecf20Sopenharmony_ci	for (c = 0; c < num_analog_busses_out(chip); c++) {
11668c2ecf20Sopenharmony_ci		if (chip->nominal_level[c] != ucontrol->value.integer.value[c]) {
11678c2ecf20Sopenharmony_ci			set_nominal_level(chip, c,
11688c2ecf20Sopenharmony_ci					  ucontrol->value.integer.value[c]);
11698c2ecf20Sopenharmony_ci			changed = 1;
11708c2ecf20Sopenharmony_ci		}
11718c2ecf20Sopenharmony_ci	}
11728c2ecf20Sopenharmony_ci	if (changed)
11738c2ecf20Sopenharmony_ci		update_output_line_level(chip);
11748c2ecf20Sopenharmony_ci	spin_unlock_irq(&chip->lock);
11758c2ecf20Sopenharmony_ci	return changed;
11768c2ecf20Sopenharmony_ci}
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_output_nominal_level = {
11798c2ecf20Sopenharmony_ci	.name = "Line Playback Switch (-10dBV)",
11808c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
11818c2ecf20Sopenharmony_ci	.info = snd_echo_output_nominal_info,
11828c2ecf20Sopenharmony_ci	.get = snd_echo_output_nominal_get,
11838c2ecf20Sopenharmony_ci	.put = snd_echo_output_nominal_put,
11848c2ecf20Sopenharmony_ci};
11858c2ecf20Sopenharmony_ci
11868c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL */
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_INPUT_NOMINAL_LEVEL
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_ci/*************** Analog input nominal level (+4dBu / -10dBV) ***************/
11938c2ecf20Sopenharmony_cistatic int snd_echo_input_nominal_info(struct snd_kcontrol *kcontrol,
11948c2ecf20Sopenharmony_ci				       struct snd_ctl_elem_info *uinfo)
11958c2ecf20Sopenharmony_ci{
11968c2ecf20Sopenharmony_ci	struct echoaudio *chip;
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
11998c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
12008c2ecf20Sopenharmony_ci	uinfo->count = num_analog_busses_in(chip);
12018c2ecf20Sopenharmony_ci	uinfo->value.integer.min = 0;
12028c2ecf20Sopenharmony_ci	uinfo->value.integer.max = 1;
12038c2ecf20Sopenharmony_ci	return 0;
12048c2ecf20Sopenharmony_ci}
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_cistatic int snd_echo_input_nominal_get(struct snd_kcontrol *kcontrol,
12078c2ecf20Sopenharmony_ci				      struct snd_ctl_elem_value *ucontrol)
12088c2ecf20Sopenharmony_ci{
12098c2ecf20Sopenharmony_ci	struct echoaudio *chip;
12108c2ecf20Sopenharmony_ci	int c;
12118c2ecf20Sopenharmony_ci
12128c2ecf20Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
12138c2ecf20Sopenharmony_ci	for (c = 0; c < num_analog_busses_in(chip); c++)
12148c2ecf20Sopenharmony_ci		ucontrol->value.integer.value[c] =
12158c2ecf20Sopenharmony_ci			chip->nominal_level[bx_analog_in(chip) + c];
12168c2ecf20Sopenharmony_ci	return 0;
12178c2ecf20Sopenharmony_ci}
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_cistatic int snd_echo_input_nominal_put(struct snd_kcontrol *kcontrol,
12208c2ecf20Sopenharmony_ci				      struct snd_ctl_elem_value *ucontrol)
12218c2ecf20Sopenharmony_ci{
12228c2ecf20Sopenharmony_ci	struct echoaudio *chip;
12238c2ecf20Sopenharmony_ci	int c, changed;
12248c2ecf20Sopenharmony_ci
12258c2ecf20Sopenharmony_ci	changed = 0;
12268c2ecf20Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
12278c2ecf20Sopenharmony_ci	spin_lock_irq(&chip->lock);
12288c2ecf20Sopenharmony_ci	for (c = 0; c < num_analog_busses_in(chip); c++) {
12298c2ecf20Sopenharmony_ci		if (chip->nominal_level[bx_analog_in(chip) + c] !=
12308c2ecf20Sopenharmony_ci		    ucontrol->value.integer.value[c]) {
12318c2ecf20Sopenharmony_ci			set_nominal_level(chip, bx_analog_in(chip) + c,
12328c2ecf20Sopenharmony_ci					  ucontrol->value.integer.value[c]);
12338c2ecf20Sopenharmony_ci			changed = 1;
12348c2ecf20Sopenharmony_ci		}
12358c2ecf20Sopenharmony_ci	}
12368c2ecf20Sopenharmony_ci	if (changed)
12378c2ecf20Sopenharmony_ci		update_output_line_level(chip);	/* "Output" is not a mistake
12388c2ecf20Sopenharmony_ci						 * here.
12398c2ecf20Sopenharmony_ci						 */
12408c2ecf20Sopenharmony_ci	spin_unlock_irq(&chip->lock);
12418c2ecf20Sopenharmony_ci	return changed;
12428c2ecf20Sopenharmony_ci}
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_intput_nominal_level = {
12458c2ecf20Sopenharmony_ci	.name = "Line Capture Switch (-10dBV)",
12468c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
12478c2ecf20Sopenharmony_ci	.info = snd_echo_input_nominal_info,
12488c2ecf20Sopenharmony_ci	.get = snd_echo_input_nominal_get,
12498c2ecf20Sopenharmony_ci	.put = snd_echo_input_nominal_put,
12508c2ecf20Sopenharmony_ci};
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_INPUT_NOMINAL_LEVEL */
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_ci
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_MONITOR
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_ci/******************* Monitor mixer *******************/
12598c2ecf20Sopenharmony_cistatic int snd_echo_mixer_info(struct snd_kcontrol *kcontrol,
12608c2ecf20Sopenharmony_ci			       struct snd_ctl_elem_info *uinfo)
12618c2ecf20Sopenharmony_ci{
12628c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
12638c2ecf20Sopenharmony_ci	uinfo->count = 1;
12648c2ecf20Sopenharmony_ci	uinfo->value.integer.min = ECHOGAIN_MINOUT;
12658c2ecf20Sopenharmony_ci	uinfo->value.integer.max = ECHOGAIN_MAXOUT;
12668c2ecf20Sopenharmony_ci	return 0;
12678c2ecf20Sopenharmony_ci}
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_cistatic int snd_echo_mixer_get(struct snd_kcontrol *kcontrol,
12708c2ecf20Sopenharmony_ci			      struct snd_ctl_elem_value *ucontrol)
12718c2ecf20Sopenharmony_ci{
12728c2ecf20Sopenharmony_ci	struct echoaudio *chip = snd_kcontrol_chip(kcontrol);
12738c2ecf20Sopenharmony_ci	unsigned int out = ucontrol->id.index / num_busses_in(chip);
12748c2ecf20Sopenharmony_ci	unsigned int in = ucontrol->id.index % num_busses_in(chip);
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci	if (out >= ECHO_MAXAUDIOOUTPUTS || in >= ECHO_MAXAUDIOINPUTS)
12778c2ecf20Sopenharmony_ci		return -EINVAL;
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = chip->monitor_gain[out][in];
12808c2ecf20Sopenharmony_ci	return 0;
12818c2ecf20Sopenharmony_ci}
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_cistatic int snd_echo_mixer_put(struct snd_kcontrol *kcontrol,
12848c2ecf20Sopenharmony_ci			      struct snd_ctl_elem_value *ucontrol)
12858c2ecf20Sopenharmony_ci{
12868c2ecf20Sopenharmony_ci	struct echoaudio *chip;
12878c2ecf20Sopenharmony_ci	int changed,  gain;
12888c2ecf20Sopenharmony_ci	unsigned int out, in;
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_ci	changed = 0;
12918c2ecf20Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
12928c2ecf20Sopenharmony_ci	out = ucontrol->id.index / num_busses_in(chip);
12938c2ecf20Sopenharmony_ci	in = ucontrol->id.index % num_busses_in(chip);
12948c2ecf20Sopenharmony_ci	if (out >= ECHO_MAXAUDIOOUTPUTS || in >= ECHO_MAXAUDIOINPUTS)
12958c2ecf20Sopenharmony_ci		return -EINVAL;
12968c2ecf20Sopenharmony_ci	gain = ucontrol->value.integer.value[0];
12978c2ecf20Sopenharmony_ci	if (gain < ECHOGAIN_MINOUT || gain > ECHOGAIN_MAXOUT)
12988c2ecf20Sopenharmony_ci		return -EINVAL;
12998c2ecf20Sopenharmony_ci	if (chip->monitor_gain[out][in] != gain) {
13008c2ecf20Sopenharmony_ci		spin_lock_irq(&chip->lock);
13018c2ecf20Sopenharmony_ci		set_monitor_gain(chip, out, in, gain);
13028c2ecf20Sopenharmony_ci		update_output_line_level(chip);
13038c2ecf20Sopenharmony_ci		spin_unlock_irq(&chip->lock);
13048c2ecf20Sopenharmony_ci		changed = 1;
13058c2ecf20Sopenharmony_ci	}
13068c2ecf20Sopenharmony_ci	return changed;
13078c2ecf20Sopenharmony_ci}
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_cistatic struct snd_kcontrol_new snd_echo_monitor_mixer = {
13108c2ecf20Sopenharmony_ci	.name = "Monitor Mixer Volume",
13118c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
13128c2ecf20Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,
13138c2ecf20Sopenharmony_ci	.info = snd_echo_mixer_info,
13148c2ecf20Sopenharmony_ci	.get = snd_echo_mixer_get,
13158c2ecf20Sopenharmony_ci	.put = snd_echo_mixer_put,
13168c2ecf20Sopenharmony_ci	.tlv = {.p = db_scale_output_gain},
13178c2ecf20Sopenharmony_ci};
13188c2ecf20Sopenharmony_ci
13198c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_MONITOR */
13208c2ecf20Sopenharmony_ci
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_VMIXER
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_ci/******************* Vmixer *******************/
13268c2ecf20Sopenharmony_cistatic int snd_echo_vmixer_info(struct snd_kcontrol *kcontrol,
13278c2ecf20Sopenharmony_ci				struct snd_ctl_elem_info *uinfo)
13288c2ecf20Sopenharmony_ci{
13298c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
13308c2ecf20Sopenharmony_ci	uinfo->count = 1;
13318c2ecf20Sopenharmony_ci	uinfo->value.integer.min = ECHOGAIN_MINOUT;
13328c2ecf20Sopenharmony_ci	uinfo->value.integer.max = ECHOGAIN_MAXOUT;
13338c2ecf20Sopenharmony_ci	return 0;
13348c2ecf20Sopenharmony_ci}
13358c2ecf20Sopenharmony_ci
13368c2ecf20Sopenharmony_cistatic int snd_echo_vmixer_get(struct snd_kcontrol *kcontrol,
13378c2ecf20Sopenharmony_ci			       struct snd_ctl_elem_value *ucontrol)
13388c2ecf20Sopenharmony_ci{
13398c2ecf20Sopenharmony_ci	struct echoaudio *chip;
13408c2ecf20Sopenharmony_ci
13418c2ecf20Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
13428c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] =
13438c2ecf20Sopenharmony_ci		chip->vmixer_gain[ucontrol->id.index / num_pipes_out(chip)]
13448c2ecf20Sopenharmony_ci			[ucontrol->id.index % num_pipes_out(chip)];
13458c2ecf20Sopenharmony_ci	return 0;
13468c2ecf20Sopenharmony_ci}
13478c2ecf20Sopenharmony_ci
13488c2ecf20Sopenharmony_cistatic int snd_echo_vmixer_put(struct snd_kcontrol *kcontrol,
13498c2ecf20Sopenharmony_ci			       struct snd_ctl_elem_value *ucontrol)
13508c2ecf20Sopenharmony_ci{
13518c2ecf20Sopenharmony_ci	struct echoaudio *chip;
13528c2ecf20Sopenharmony_ci	int gain, changed;
13538c2ecf20Sopenharmony_ci	short vch, out;
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci	changed = 0;
13568c2ecf20Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
13578c2ecf20Sopenharmony_ci	out = ucontrol->id.index / num_pipes_out(chip);
13588c2ecf20Sopenharmony_ci	vch = ucontrol->id.index % num_pipes_out(chip);
13598c2ecf20Sopenharmony_ci	gain = ucontrol->value.integer.value[0];
13608c2ecf20Sopenharmony_ci	if (gain < ECHOGAIN_MINOUT || gain > ECHOGAIN_MAXOUT)
13618c2ecf20Sopenharmony_ci		return -EINVAL;
13628c2ecf20Sopenharmony_ci	if (chip->vmixer_gain[out][vch] != ucontrol->value.integer.value[0]) {
13638c2ecf20Sopenharmony_ci		spin_lock_irq(&chip->lock);
13648c2ecf20Sopenharmony_ci		set_vmixer_gain(chip, out, vch, ucontrol->value.integer.value[0]);
13658c2ecf20Sopenharmony_ci		update_vmixer_level(chip);
13668c2ecf20Sopenharmony_ci		spin_unlock_irq(&chip->lock);
13678c2ecf20Sopenharmony_ci		changed = 1;
13688c2ecf20Sopenharmony_ci	}
13698c2ecf20Sopenharmony_ci	return changed;
13708c2ecf20Sopenharmony_ci}
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_cistatic struct snd_kcontrol_new snd_echo_vmixer = {
13738c2ecf20Sopenharmony_ci	.name = "VMixer Volume",
13748c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
13758c2ecf20Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,
13768c2ecf20Sopenharmony_ci	.info = snd_echo_vmixer_info,
13778c2ecf20Sopenharmony_ci	.get = snd_echo_vmixer_get,
13788c2ecf20Sopenharmony_ci	.put = snd_echo_vmixer_put,
13798c2ecf20Sopenharmony_ci	.tlv = {.p = db_scale_output_gain},
13808c2ecf20Sopenharmony_ci};
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_VMIXER */
13838c2ecf20Sopenharmony_ci
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_ci
13868c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_DIGITAL_MODE_SWITCH
13878c2ecf20Sopenharmony_ci
13888c2ecf20Sopenharmony_ci/******************* Digital mode switch *******************/
13898c2ecf20Sopenharmony_cistatic int snd_echo_digital_mode_info(struct snd_kcontrol *kcontrol,
13908c2ecf20Sopenharmony_ci				      struct snd_ctl_elem_info *uinfo)
13918c2ecf20Sopenharmony_ci{
13928c2ecf20Sopenharmony_ci	static const char * const names[4] = {
13938c2ecf20Sopenharmony_ci		"S/PDIF Coaxial", "S/PDIF Optical", "ADAT Optical",
13948c2ecf20Sopenharmony_ci		"S/PDIF Cdrom"
13958c2ecf20Sopenharmony_ci	};
13968c2ecf20Sopenharmony_ci	struct echoaudio *chip;
13978c2ecf20Sopenharmony_ci
13988c2ecf20Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
13998c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, chip->num_digital_modes, names);
14008c2ecf20Sopenharmony_ci}
14018c2ecf20Sopenharmony_ci
14028c2ecf20Sopenharmony_cistatic int snd_echo_digital_mode_get(struct snd_kcontrol *kcontrol,
14038c2ecf20Sopenharmony_ci				     struct snd_ctl_elem_value *ucontrol)
14048c2ecf20Sopenharmony_ci{
14058c2ecf20Sopenharmony_ci	struct echoaudio *chip;
14068c2ecf20Sopenharmony_ci	int i, mode;
14078c2ecf20Sopenharmony_ci
14088c2ecf20Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
14098c2ecf20Sopenharmony_ci	mode = chip->digital_mode;
14108c2ecf20Sopenharmony_ci	for (i = chip->num_digital_modes - 1; i >= 0; i--)
14118c2ecf20Sopenharmony_ci		if (mode == chip->digital_mode_list[i]) {
14128c2ecf20Sopenharmony_ci			ucontrol->value.enumerated.item[0] = i;
14138c2ecf20Sopenharmony_ci			break;
14148c2ecf20Sopenharmony_ci		}
14158c2ecf20Sopenharmony_ci	return 0;
14168c2ecf20Sopenharmony_ci}
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_cistatic int snd_echo_digital_mode_put(struct snd_kcontrol *kcontrol,
14198c2ecf20Sopenharmony_ci				     struct snd_ctl_elem_value *ucontrol)
14208c2ecf20Sopenharmony_ci{
14218c2ecf20Sopenharmony_ci	struct echoaudio *chip;
14228c2ecf20Sopenharmony_ci	int changed;
14238c2ecf20Sopenharmony_ci	unsigned short emode, dmode;
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_ci	changed = 0;
14268c2ecf20Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_ci	emode = ucontrol->value.enumerated.item[0];
14298c2ecf20Sopenharmony_ci	if (emode >= chip->num_digital_modes)
14308c2ecf20Sopenharmony_ci		return -EINVAL;
14318c2ecf20Sopenharmony_ci	dmode = chip->digital_mode_list[emode];
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_ci	if (dmode != chip->digital_mode) {
14348c2ecf20Sopenharmony_ci		/* mode_mutex is required to make this operation atomic wrt
14358c2ecf20Sopenharmony_ci		pcm_digital_*_open() and set_input_clock() functions. */
14368c2ecf20Sopenharmony_ci		mutex_lock(&chip->mode_mutex);
14378c2ecf20Sopenharmony_ci
14388c2ecf20Sopenharmony_ci		/* Do not allow the user to change the digital mode when a pcm
14398c2ecf20Sopenharmony_ci		device is open because it also changes the number of channels
14408c2ecf20Sopenharmony_ci		and the allowed sample rates */
14418c2ecf20Sopenharmony_ci		if (chip->opencount) {
14428c2ecf20Sopenharmony_ci			changed = -EAGAIN;
14438c2ecf20Sopenharmony_ci		} else {
14448c2ecf20Sopenharmony_ci			changed = set_digital_mode(chip, dmode);
14458c2ecf20Sopenharmony_ci			/* If we had to change the clock source, report it */
14468c2ecf20Sopenharmony_ci			if (changed > 0 && chip->clock_src_ctl) {
14478c2ecf20Sopenharmony_ci				snd_ctl_notify(chip->card,
14488c2ecf20Sopenharmony_ci					       SNDRV_CTL_EVENT_MASK_VALUE,
14498c2ecf20Sopenharmony_ci					       &chip->clock_src_ctl->id);
14508c2ecf20Sopenharmony_ci				dev_dbg(chip->card->dev,
14518c2ecf20Sopenharmony_ci					"SDM() =%d\n", changed);
14528c2ecf20Sopenharmony_ci			}
14538c2ecf20Sopenharmony_ci			if (changed >= 0)
14548c2ecf20Sopenharmony_ci				changed = 1;	/* No errors */
14558c2ecf20Sopenharmony_ci		}
14568c2ecf20Sopenharmony_ci		mutex_unlock(&chip->mode_mutex);
14578c2ecf20Sopenharmony_ci	}
14588c2ecf20Sopenharmony_ci	return changed;
14598c2ecf20Sopenharmony_ci}
14608c2ecf20Sopenharmony_ci
14618c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_digital_mode_switch = {
14628c2ecf20Sopenharmony_ci	.name = "Digital mode Switch",
14638c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_CARD,
14648c2ecf20Sopenharmony_ci	.info = snd_echo_digital_mode_info,
14658c2ecf20Sopenharmony_ci	.get = snd_echo_digital_mode_get,
14668c2ecf20Sopenharmony_ci	.put = snd_echo_digital_mode_put,
14678c2ecf20Sopenharmony_ci};
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_DIGITAL_MODE_SWITCH */
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_ci
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_DIGITAL_IO
14748c2ecf20Sopenharmony_ci
14758c2ecf20Sopenharmony_ci/******************* S/PDIF mode switch *******************/
14768c2ecf20Sopenharmony_cistatic int snd_echo_spdif_mode_info(struct snd_kcontrol *kcontrol,
14778c2ecf20Sopenharmony_ci				    struct snd_ctl_elem_info *uinfo)
14788c2ecf20Sopenharmony_ci{
14798c2ecf20Sopenharmony_ci	static const char * const names[2] = {"Consumer", "Professional"};
14808c2ecf20Sopenharmony_ci
14818c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, 2, names);
14828c2ecf20Sopenharmony_ci}
14838c2ecf20Sopenharmony_ci
14848c2ecf20Sopenharmony_cistatic int snd_echo_spdif_mode_get(struct snd_kcontrol *kcontrol,
14858c2ecf20Sopenharmony_ci				   struct snd_ctl_elem_value *ucontrol)
14868c2ecf20Sopenharmony_ci{
14878c2ecf20Sopenharmony_ci	struct echoaudio *chip;
14888c2ecf20Sopenharmony_ci
14898c2ecf20Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
14908c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = !!chip->professional_spdif;
14918c2ecf20Sopenharmony_ci	return 0;
14928c2ecf20Sopenharmony_ci}
14938c2ecf20Sopenharmony_ci
14948c2ecf20Sopenharmony_cistatic int snd_echo_spdif_mode_put(struct snd_kcontrol *kcontrol,
14958c2ecf20Sopenharmony_ci				   struct snd_ctl_elem_value *ucontrol)
14968c2ecf20Sopenharmony_ci{
14978c2ecf20Sopenharmony_ci	struct echoaudio *chip;
14988c2ecf20Sopenharmony_ci	int mode;
14998c2ecf20Sopenharmony_ci
15008c2ecf20Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
15018c2ecf20Sopenharmony_ci	mode = !!ucontrol->value.enumerated.item[0];
15028c2ecf20Sopenharmony_ci	if (mode != chip->professional_spdif) {
15038c2ecf20Sopenharmony_ci		spin_lock_irq(&chip->lock);
15048c2ecf20Sopenharmony_ci		set_professional_spdif(chip, mode);
15058c2ecf20Sopenharmony_ci		spin_unlock_irq(&chip->lock);
15068c2ecf20Sopenharmony_ci		return 1;
15078c2ecf20Sopenharmony_ci	}
15088c2ecf20Sopenharmony_ci	return 0;
15098c2ecf20Sopenharmony_ci}
15108c2ecf20Sopenharmony_ci
15118c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_spdif_mode_switch = {
15128c2ecf20Sopenharmony_ci	.name = "S/PDIF mode Switch",
15138c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_CARD,
15148c2ecf20Sopenharmony_ci	.info = snd_echo_spdif_mode_info,
15158c2ecf20Sopenharmony_ci	.get = snd_echo_spdif_mode_get,
15168c2ecf20Sopenharmony_ci	.put = snd_echo_spdif_mode_put,
15178c2ecf20Sopenharmony_ci};
15188c2ecf20Sopenharmony_ci
15198c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_DIGITAL_IO */
15208c2ecf20Sopenharmony_ci
15218c2ecf20Sopenharmony_ci
15228c2ecf20Sopenharmony_ci
15238c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_EXTERNAL_CLOCK
15248c2ecf20Sopenharmony_ci
15258c2ecf20Sopenharmony_ci/******************* Select input clock source *******************/
15268c2ecf20Sopenharmony_cistatic int snd_echo_clock_source_info(struct snd_kcontrol *kcontrol,
15278c2ecf20Sopenharmony_ci				      struct snd_ctl_elem_info *uinfo)
15288c2ecf20Sopenharmony_ci{
15298c2ecf20Sopenharmony_ci	static const char * const names[8] = {
15308c2ecf20Sopenharmony_ci		"Internal", "Word", "Super", "S/PDIF", "ADAT", "ESync",
15318c2ecf20Sopenharmony_ci		"ESync96", "MTC"
15328c2ecf20Sopenharmony_ci	};
15338c2ecf20Sopenharmony_ci	struct echoaudio *chip;
15348c2ecf20Sopenharmony_ci
15358c2ecf20Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
15368c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, chip->num_clock_sources, names);
15378c2ecf20Sopenharmony_ci}
15388c2ecf20Sopenharmony_ci
15398c2ecf20Sopenharmony_cistatic int snd_echo_clock_source_get(struct snd_kcontrol *kcontrol,
15408c2ecf20Sopenharmony_ci				     struct snd_ctl_elem_value *ucontrol)
15418c2ecf20Sopenharmony_ci{
15428c2ecf20Sopenharmony_ci	struct echoaudio *chip;
15438c2ecf20Sopenharmony_ci	int i, clock;
15448c2ecf20Sopenharmony_ci
15458c2ecf20Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
15468c2ecf20Sopenharmony_ci	clock = chip->input_clock;
15478c2ecf20Sopenharmony_ci
15488c2ecf20Sopenharmony_ci	for (i = 0; i < chip->num_clock_sources; i++)
15498c2ecf20Sopenharmony_ci		if (clock == chip->clock_source_list[i])
15508c2ecf20Sopenharmony_ci			ucontrol->value.enumerated.item[0] = i;
15518c2ecf20Sopenharmony_ci
15528c2ecf20Sopenharmony_ci	return 0;
15538c2ecf20Sopenharmony_ci}
15548c2ecf20Sopenharmony_ci
15558c2ecf20Sopenharmony_cistatic int snd_echo_clock_source_put(struct snd_kcontrol *kcontrol,
15568c2ecf20Sopenharmony_ci				     struct snd_ctl_elem_value *ucontrol)
15578c2ecf20Sopenharmony_ci{
15588c2ecf20Sopenharmony_ci	struct echoaudio *chip;
15598c2ecf20Sopenharmony_ci	int changed;
15608c2ecf20Sopenharmony_ci	unsigned int eclock, dclock;
15618c2ecf20Sopenharmony_ci
15628c2ecf20Sopenharmony_ci	changed = 0;
15638c2ecf20Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
15648c2ecf20Sopenharmony_ci	eclock = ucontrol->value.enumerated.item[0];
15658c2ecf20Sopenharmony_ci	if (eclock >= chip->input_clock_types)
15668c2ecf20Sopenharmony_ci		return -EINVAL;
15678c2ecf20Sopenharmony_ci	dclock = chip->clock_source_list[eclock];
15688c2ecf20Sopenharmony_ci	if (chip->input_clock != dclock) {
15698c2ecf20Sopenharmony_ci		mutex_lock(&chip->mode_mutex);
15708c2ecf20Sopenharmony_ci		spin_lock_irq(&chip->lock);
15718c2ecf20Sopenharmony_ci		if ((changed = set_input_clock(chip, dclock)) == 0)
15728c2ecf20Sopenharmony_ci			changed = 1;	/* no errors */
15738c2ecf20Sopenharmony_ci		spin_unlock_irq(&chip->lock);
15748c2ecf20Sopenharmony_ci		mutex_unlock(&chip->mode_mutex);
15758c2ecf20Sopenharmony_ci	}
15768c2ecf20Sopenharmony_ci
15778c2ecf20Sopenharmony_ci	if (changed < 0)
15788c2ecf20Sopenharmony_ci		dev_dbg(chip->card->dev,
15798c2ecf20Sopenharmony_ci			"seticlk val%d err 0x%x\n", dclock, changed);
15808c2ecf20Sopenharmony_ci
15818c2ecf20Sopenharmony_ci	return changed;
15828c2ecf20Sopenharmony_ci}
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_clock_source_switch = {
15858c2ecf20Sopenharmony_ci	.name = "Sample Clock Source",
15868c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_PCM,
15878c2ecf20Sopenharmony_ci	.info = snd_echo_clock_source_info,
15888c2ecf20Sopenharmony_ci	.get = snd_echo_clock_source_get,
15898c2ecf20Sopenharmony_ci	.put = snd_echo_clock_source_put,
15908c2ecf20Sopenharmony_ci};
15918c2ecf20Sopenharmony_ci
15928c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_EXTERNAL_CLOCK */
15938c2ecf20Sopenharmony_ci
15948c2ecf20Sopenharmony_ci
15958c2ecf20Sopenharmony_ci
15968c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_PHANTOM_POWER
15978c2ecf20Sopenharmony_ci
15988c2ecf20Sopenharmony_ci/******************* Phantom power switch *******************/
15998c2ecf20Sopenharmony_ci#define snd_echo_phantom_power_info	snd_ctl_boolean_mono_info
16008c2ecf20Sopenharmony_ci
16018c2ecf20Sopenharmony_cistatic int snd_echo_phantom_power_get(struct snd_kcontrol *kcontrol,
16028c2ecf20Sopenharmony_ci				      struct snd_ctl_elem_value *ucontrol)
16038c2ecf20Sopenharmony_ci{
16048c2ecf20Sopenharmony_ci	struct echoaudio *chip = snd_kcontrol_chip(kcontrol);
16058c2ecf20Sopenharmony_ci
16068c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = chip->phantom_power;
16078c2ecf20Sopenharmony_ci	return 0;
16088c2ecf20Sopenharmony_ci}
16098c2ecf20Sopenharmony_ci
16108c2ecf20Sopenharmony_cistatic int snd_echo_phantom_power_put(struct snd_kcontrol *kcontrol,
16118c2ecf20Sopenharmony_ci				      struct snd_ctl_elem_value *ucontrol)
16128c2ecf20Sopenharmony_ci{
16138c2ecf20Sopenharmony_ci	struct echoaudio *chip = snd_kcontrol_chip(kcontrol);
16148c2ecf20Sopenharmony_ci	int power, changed = 0;
16158c2ecf20Sopenharmony_ci
16168c2ecf20Sopenharmony_ci	power = !!ucontrol->value.integer.value[0];
16178c2ecf20Sopenharmony_ci	if (chip->phantom_power != power) {
16188c2ecf20Sopenharmony_ci		spin_lock_irq(&chip->lock);
16198c2ecf20Sopenharmony_ci		changed = set_phantom_power(chip, power);
16208c2ecf20Sopenharmony_ci		spin_unlock_irq(&chip->lock);
16218c2ecf20Sopenharmony_ci		if (changed == 0)
16228c2ecf20Sopenharmony_ci			changed = 1;	/* no errors */
16238c2ecf20Sopenharmony_ci	}
16248c2ecf20Sopenharmony_ci	return changed;
16258c2ecf20Sopenharmony_ci}
16268c2ecf20Sopenharmony_ci
16278c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_phantom_power_switch = {
16288c2ecf20Sopenharmony_ci	.name = "Phantom power Switch",
16298c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_CARD,
16308c2ecf20Sopenharmony_ci	.info = snd_echo_phantom_power_info,
16318c2ecf20Sopenharmony_ci	.get = snd_echo_phantom_power_get,
16328c2ecf20Sopenharmony_ci	.put = snd_echo_phantom_power_put,
16338c2ecf20Sopenharmony_ci};
16348c2ecf20Sopenharmony_ci
16358c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_PHANTOM_POWER */
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_ci
16388c2ecf20Sopenharmony_ci
16398c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE
16408c2ecf20Sopenharmony_ci
16418c2ecf20Sopenharmony_ci/******************* Digital input automute switch *******************/
16428c2ecf20Sopenharmony_ci#define snd_echo_automute_info		snd_ctl_boolean_mono_info
16438c2ecf20Sopenharmony_ci
16448c2ecf20Sopenharmony_cistatic int snd_echo_automute_get(struct snd_kcontrol *kcontrol,
16458c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
16468c2ecf20Sopenharmony_ci{
16478c2ecf20Sopenharmony_ci	struct echoaudio *chip = snd_kcontrol_chip(kcontrol);
16488c2ecf20Sopenharmony_ci
16498c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = chip->digital_in_automute;
16508c2ecf20Sopenharmony_ci	return 0;
16518c2ecf20Sopenharmony_ci}
16528c2ecf20Sopenharmony_ci
16538c2ecf20Sopenharmony_cistatic int snd_echo_automute_put(struct snd_kcontrol *kcontrol,
16548c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
16558c2ecf20Sopenharmony_ci{
16568c2ecf20Sopenharmony_ci	struct echoaudio *chip = snd_kcontrol_chip(kcontrol);
16578c2ecf20Sopenharmony_ci	int automute, changed = 0;
16588c2ecf20Sopenharmony_ci
16598c2ecf20Sopenharmony_ci	automute = !!ucontrol->value.integer.value[0];
16608c2ecf20Sopenharmony_ci	if (chip->digital_in_automute != automute) {
16618c2ecf20Sopenharmony_ci		spin_lock_irq(&chip->lock);
16628c2ecf20Sopenharmony_ci		changed = set_input_auto_mute(chip, automute);
16638c2ecf20Sopenharmony_ci		spin_unlock_irq(&chip->lock);
16648c2ecf20Sopenharmony_ci		if (changed == 0)
16658c2ecf20Sopenharmony_ci			changed = 1;	/* no errors */
16668c2ecf20Sopenharmony_ci	}
16678c2ecf20Sopenharmony_ci	return changed;
16688c2ecf20Sopenharmony_ci}
16698c2ecf20Sopenharmony_ci
16708c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_automute_switch = {
16718c2ecf20Sopenharmony_ci	.name = "Digital Capture Switch (automute)",
16728c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_CARD,
16738c2ecf20Sopenharmony_ci	.info = snd_echo_automute_info,
16748c2ecf20Sopenharmony_ci	.get = snd_echo_automute_get,
16758c2ecf20Sopenharmony_ci	.put = snd_echo_automute_put,
16768c2ecf20Sopenharmony_ci};
16778c2ecf20Sopenharmony_ci
16788c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE */
16798c2ecf20Sopenharmony_ci
16808c2ecf20Sopenharmony_ci
16818c2ecf20Sopenharmony_ci
16828c2ecf20Sopenharmony_ci/******************* VU-meters switch *******************/
16838c2ecf20Sopenharmony_ci#define snd_echo_vumeters_switch_info		snd_ctl_boolean_mono_info
16848c2ecf20Sopenharmony_ci
16858c2ecf20Sopenharmony_cistatic int snd_echo_vumeters_switch_put(struct snd_kcontrol *kcontrol,
16868c2ecf20Sopenharmony_ci					struct snd_ctl_elem_value *ucontrol)
16878c2ecf20Sopenharmony_ci{
16888c2ecf20Sopenharmony_ci	struct echoaudio *chip;
16898c2ecf20Sopenharmony_ci
16908c2ecf20Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
16918c2ecf20Sopenharmony_ci	spin_lock_irq(&chip->lock);
16928c2ecf20Sopenharmony_ci	set_meters_on(chip, ucontrol->value.integer.value[0]);
16938c2ecf20Sopenharmony_ci	spin_unlock_irq(&chip->lock);
16948c2ecf20Sopenharmony_ci	return 1;
16958c2ecf20Sopenharmony_ci}
16968c2ecf20Sopenharmony_ci
16978c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_vumeters_switch = {
16988c2ecf20Sopenharmony_ci	.name = "VU-meters Switch",
16998c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_CARD,
17008c2ecf20Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_WRITE,
17018c2ecf20Sopenharmony_ci	.info = snd_echo_vumeters_switch_info,
17028c2ecf20Sopenharmony_ci	.put = snd_echo_vumeters_switch_put,
17038c2ecf20Sopenharmony_ci};
17048c2ecf20Sopenharmony_ci
17058c2ecf20Sopenharmony_ci
17068c2ecf20Sopenharmony_ci
17078c2ecf20Sopenharmony_ci/***** Read VU-meters (input, output, analog and digital together) *****/
17088c2ecf20Sopenharmony_cistatic int snd_echo_vumeters_info(struct snd_kcontrol *kcontrol,
17098c2ecf20Sopenharmony_ci				  struct snd_ctl_elem_info *uinfo)
17108c2ecf20Sopenharmony_ci{
17118c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
17128c2ecf20Sopenharmony_ci	uinfo->count = 96;
17138c2ecf20Sopenharmony_ci	uinfo->value.integer.min = ECHOGAIN_MINOUT;
17148c2ecf20Sopenharmony_ci	uinfo->value.integer.max = 0;
17158c2ecf20Sopenharmony_ci	return 0;
17168c2ecf20Sopenharmony_ci}
17178c2ecf20Sopenharmony_ci
17188c2ecf20Sopenharmony_cistatic int snd_echo_vumeters_get(struct snd_kcontrol *kcontrol,
17198c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
17208c2ecf20Sopenharmony_ci{
17218c2ecf20Sopenharmony_ci	struct echoaudio *chip;
17228c2ecf20Sopenharmony_ci
17238c2ecf20Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
17248c2ecf20Sopenharmony_ci	get_audio_meters(chip, ucontrol->value.integer.value);
17258c2ecf20Sopenharmony_ci	return 0;
17268c2ecf20Sopenharmony_ci}
17278c2ecf20Sopenharmony_ci
17288c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_vumeters = {
17298c2ecf20Sopenharmony_ci	.name = "VU-meters",
17308c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
17318c2ecf20Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READ |
17328c2ecf20Sopenharmony_ci		  SNDRV_CTL_ELEM_ACCESS_VOLATILE |
17338c2ecf20Sopenharmony_ci		  SNDRV_CTL_ELEM_ACCESS_TLV_READ,
17348c2ecf20Sopenharmony_ci	.info = snd_echo_vumeters_info,
17358c2ecf20Sopenharmony_ci	.get = snd_echo_vumeters_get,
17368c2ecf20Sopenharmony_ci	.tlv = {.p = db_scale_output_gain},
17378c2ecf20Sopenharmony_ci};
17388c2ecf20Sopenharmony_ci
17398c2ecf20Sopenharmony_ci
17408c2ecf20Sopenharmony_ci
17418c2ecf20Sopenharmony_ci/*** Channels info - it exports informations about the number of channels ***/
17428c2ecf20Sopenharmony_cistatic int snd_echo_channels_info_info(struct snd_kcontrol *kcontrol,
17438c2ecf20Sopenharmony_ci				       struct snd_ctl_elem_info *uinfo)
17448c2ecf20Sopenharmony_ci{
17458c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
17468c2ecf20Sopenharmony_ci	uinfo->count = 6;
17478c2ecf20Sopenharmony_ci	uinfo->value.integer.min = 0;
17488c2ecf20Sopenharmony_ci	uinfo->value.integer.max = 1 << ECHO_CLOCK_NUMBER;
17498c2ecf20Sopenharmony_ci	return 0;
17508c2ecf20Sopenharmony_ci}
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_cistatic int snd_echo_channels_info_get(struct snd_kcontrol *kcontrol,
17538c2ecf20Sopenharmony_ci				      struct snd_ctl_elem_value *ucontrol)
17548c2ecf20Sopenharmony_ci{
17558c2ecf20Sopenharmony_ci	struct echoaudio *chip;
17568c2ecf20Sopenharmony_ci	int detected, clocks, bit, src;
17578c2ecf20Sopenharmony_ci
17588c2ecf20Sopenharmony_ci	chip = snd_kcontrol_chip(kcontrol);
17598c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = num_busses_in(chip);
17608c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[1] = num_analog_busses_in(chip);
17618c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[2] = num_busses_out(chip);
17628c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[3] = num_analog_busses_out(chip);
17638c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[4] = num_pipes_out(chip);
17648c2ecf20Sopenharmony_ci
17658c2ecf20Sopenharmony_ci	/* Compute the bitmask of the currently valid input clocks */
17668c2ecf20Sopenharmony_ci	detected = detect_input_clocks(chip);
17678c2ecf20Sopenharmony_ci	clocks = 0;
17688c2ecf20Sopenharmony_ci	src = chip->num_clock_sources - 1;
17698c2ecf20Sopenharmony_ci	for (bit = ECHO_CLOCK_NUMBER - 1; bit >= 0; bit--)
17708c2ecf20Sopenharmony_ci		if (detected & (1 << bit))
17718c2ecf20Sopenharmony_ci			for (; src >= 0; src--)
17728c2ecf20Sopenharmony_ci				if (bit == chip->clock_source_list[src]) {
17738c2ecf20Sopenharmony_ci					clocks |= 1 << src;
17748c2ecf20Sopenharmony_ci					break;
17758c2ecf20Sopenharmony_ci				}
17768c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[5] = clocks;
17778c2ecf20Sopenharmony_ci
17788c2ecf20Sopenharmony_ci	return 0;
17798c2ecf20Sopenharmony_ci}
17808c2ecf20Sopenharmony_ci
17818c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_echo_channels_info = {
17828c2ecf20Sopenharmony_ci	.name = "Channels info",
17838c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_HWDEP,
17848c2ecf20Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
17858c2ecf20Sopenharmony_ci	.info = snd_echo_channels_info_info,
17868c2ecf20Sopenharmony_ci	.get = snd_echo_channels_info_get,
17878c2ecf20Sopenharmony_ci};
17888c2ecf20Sopenharmony_ci
17898c2ecf20Sopenharmony_ci
17908c2ecf20Sopenharmony_ci
17918c2ecf20Sopenharmony_ci
17928c2ecf20Sopenharmony_ci/******************************************************************************
17938c2ecf20Sopenharmony_ci	IRQ Handling
17948c2ecf20Sopenharmony_ci******************************************************************************/
17958c2ecf20Sopenharmony_ci/* Check if a period has elapsed since last interrupt
17968c2ecf20Sopenharmony_ci *
17978c2ecf20Sopenharmony_ci * Don't make any updates to state; PCM core handles this with the
17988c2ecf20Sopenharmony_ci * correct locks.
17998c2ecf20Sopenharmony_ci *
18008c2ecf20Sopenharmony_ci * \return true if a period has elapsed, otherwise false
18018c2ecf20Sopenharmony_ci */
18028c2ecf20Sopenharmony_cistatic bool period_has_elapsed(struct snd_pcm_substream *substream)
18038c2ecf20Sopenharmony_ci{
18048c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
18058c2ecf20Sopenharmony_ci	struct audiopipe *pipe = runtime->private_data;
18068c2ecf20Sopenharmony_ci	u32 counter, step;
18078c2ecf20Sopenharmony_ci	size_t period_bytes;
18088c2ecf20Sopenharmony_ci
18098c2ecf20Sopenharmony_ci	if (pipe->state != PIPE_STATE_STARTED)
18108c2ecf20Sopenharmony_ci		return false;
18118c2ecf20Sopenharmony_ci
18128c2ecf20Sopenharmony_ci	period_bytes = frames_to_bytes(runtime, runtime->period_size);
18138c2ecf20Sopenharmony_ci
18148c2ecf20Sopenharmony_ci	counter = le32_to_cpu(*pipe->dma_counter);  /* presumed atomic */
18158c2ecf20Sopenharmony_ci
18168c2ecf20Sopenharmony_ci	step = counter - pipe->last_period;  /* handles wrapping */
18178c2ecf20Sopenharmony_ci	step -= step % period_bytes;  /* acknowledge whole periods only */
18188c2ecf20Sopenharmony_ci
18198c2ecf20Sopenharmony_ci	if (step == 0)
18208c2ecf20Sopenharmony_ci		return false;  /* haven't advanced a whole period yet */
18218c2ecf20Sopenharmony_ci
18228c2ecf20Sopenharmony_ci	pipe->last_period += step;  /* used exclusively by us */
18238c2ecf20Sopenharmony_ci	return true;
18248c2ecf20Sopenharmony_ci}
18258c2ecf20Sopenharmony_ci
18268c2ecf20Sopenharmony_cistatic irqreturn_t snd_echo_interrupt(int irq, void *dev_id)
18278c2ecf20Sopenharmony_ci{
18288c2ecf20Sopenharmony_ci	struct echoaudio *chip = dev_id;
18298c2ecf20Sopenharmony_ci	int ss, st;
18308c2ecf20Sopenharmony_ci
18318c2ecf20Sopenharmony_ci	spin_lock(&chip->lock);
18328c2ecf20Sopenharmony_ci	st = service_irq(chip);
18338c2ecf20Sopenharmony_ci	if (st < 0) {
18348c2ecf20Sopenharmony_ci		spin_unlock(&chip->lock);
18358c2ecf20Sopenharmony_ci		return IRQ_NONE;
18368c2ecf20Sopenharmony_ci	}
18378c2ecf20Sopenharmony_ci	/* The hardware doesn't tell us which substream caused the irq,
18388c2ecf20Sopenharmony_ci	thus we have to check all running substreams. */
18398c2ecf20Sopenharmony_ci	for (ss = 0; ss < DSP_MAXPIPES; ss++) {
18408c2ecf20Sopenharmony_ci		struct snd_pcm_substream *substream;
18418c2ecf20Sopenharmony_ci
18428c2ecf20Sopenharmony_ci		substream = chip->substream[ss];
18438c2ecf20Sopenharmony_ci		if (substream && period_has_elapsed(substream)) {
18448c2ecf20Sopenharmony_ci			spin_unlock(&chip->lock);
18458c2ecf20Sopenharmony_ci			snd_pcm_period_elapsed(substream);
18468c2ecf20Sopenharmony_ci			spin_lock(&chip->lock);
18478c2ecf20Sopenharmony_ci		}
18488c2ecf20Sopenharmony_ci	}
18498c2ecf20Sopenharmony_ci	spin_unlock(&chip->lock);
18508c2ecf20Sopenharmony_ci
18518c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_MIDI
18528c2ecf20Sopenharmony_ci	if (st > 0 && chip->midi_in) {
18538c2ecf20Sopenharmony_ci		snd_rawmidi_receive(chip->midi_in, chip->midi_buffer, st);
18548c2ecf20Sopenharmony_ci		dev_dbg(chip->card->dev, "rawmidi_iread=%d\n", st);
18558c2ecf20Sopenharmony_ci	}
18568c2ecf20Sopenharmony_ci#endif
18578c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
18588c2ecf20Sopenharmony_ci}
18598c2ecf20Sopenharmony_ci
18608c2ecf20Sopenharmony_ci
18618c2ecf20Sopenharmony_ci
18628c2ecf20Sopenharmony_ci
18638c2ecf20Sopenharmony_ci/******************************************************************************
18648c2ecf20Sopenharmony_ci	Module construction / destruction
18658c2ecf20Sopenharmony_ci******************************************************************************/
18668c2ecf20Sopenharmony_ci
18678c2ecf20Sopenharmony_cistatic int snd_echo_free(struct echoaudio *chip)
18688c2ecf20Sopenharmony_ci{
18698c2ecf20Sopenharmony_ci	if (chip->comm_page)
18708c2ecf20Sopenharmony_ci		rest_in_peace(chip);
18718c2ecf20Sopenharmony_ci
18728c2ecf20Sopenharmony_ci	if (chip->irq >= 0)
18738c2ecf20Sopenharmony_ci		free_irq(chip->irq, chip);
18748c2ecf20Sopenharmony_ci
18758c2ecf20Sopenharmony_ci	if (chip->comm_page)
18768c2ecf20Sopenharmony_ci		snd_dma_free_pages(&chip->commpage_dma_buf);
18778c2ecf20Sopenharmony_ci
18788c2ecf20Sopenharmony_ci	iounmap(chip->dsp_registers);
18798c2ecf20Sopenharmony_ci	release_and_free_resource(chip->iores);
18808c2ecf20Sopenharmony_ci	pci_disable_device(chip->pci);
18818c2ecf20Sopenharmony_ci
18828c2ecf20Sopenharmony_ci	/* release chip data */
18838c2ecf20Sopenharmony_ci	free_firmware_cache(chip);
18848c2ecf20Sopenharmony_ci	kfree(chip);
18858c2ecf20Sopenharmony_ci	return 0;
18868c2ecf20Sopenharmony_ci}
18878c2ecf20Sopenharmony_ci
18888c2ecf20Sopenharmony_ci
18898c2ecf20Sopenharmony_ci
18908c2ecf20Sopenharmony_cistatic int snd_echo_dev_free(struct snd_device *device)
18918c2ecf20Sopenharmony_ci{
18928c2ecf20Sopenharmony_ci	struct echoaudio *chip = device->device_data;
18938c2ecf20Sopenharmony_ci
18948c2ecf20Sopenharmony_ci	return snd_echo_free(chip);
18958c2ecf20Sopenharmony_ci}
18968c2ecf20Sopenharmony_ci
18978c2ecf20Sopenharmony_ci
18988c2ecf20Sopenharmony_ci
18998c2ecf20Sopenharmony_ci/* <--snd_echo_probe() */
19008c2ecf20Sopenharmony_cistatic int snd_echo_create(struct snd_card *card,
19018c2ecf20Sopenharmony_ci			   struct pci_dev *pci,
19028c2ecf20Sopenharmony_ci			   struct echoaudio **rchip)
19038c2ecf20Sopenharmony_ci{
19048c2ecf20Sopenharmony_ci	struct echoaudio *chip;
19058c2ecf20Sopenharmony_ci	int err;
19068c2ecf20Sopenharmony_ci	size_t sz;
19078c2ecf20Sopenharmony_ci	static const struct snd_device_ops ops = {
19088c2ecf20Sopenharmony_ci		.dev_free = snd_echo_dev_free,
19098c2ecf20Sopenharmony_ci	};
19108c2ecf20Sopenharmony_ci
19118c2ecf20Sopenharmony_ci	*rchip = NULL;
19128c2ecf20Sopenharmony_ci
19138c2ecf20Sopenharmony_ci	pci_write_config_byte(pci, PCI_LATENCY_TIMER, 0xC0);
19148c2ecf20Sopenharmony_ci
19158c2ecf20Sopenharmony_ci	if ((err = pci_enable_device(pci)) < 0)
19168c2ecf20Sopenharmony_ci		return err;
19178c2ecf20Sopenharmony_ci	pci_set_master(pci);
19188c2ecf20Sopenharmony_ci
19198c2ecf20Sopenharmony_ci	/* Allocate chip if needed */
19208c2ecf20Sopenharmony_ci	if (!*rchip) {
19218c2ecf20Sopenharmony_ci		chip = kzalloc(sizeof(*chip), GFP_KERNEL);
19228c2ecf20Sopenharmony_ci		if (!chip) {
19238c2ecf20Sopenharmony_ci			pci_disable_device(pci);
19248c2ecf20Sopenharmony_ci			return -ENOMEM;
19258c2ecf20Sopenharmony_ci		}
19268c2ecf20Sopenharmony_ci		dev_dbg(card->dev, "chip=%p\n", chip);
19278c2ecf20Sopenharmony_ci		spin_lock_init(&chip->lock);
19288c2ecf20Sopenharmony_ci		chip->card = card;
19298c2ecf20Sopenharmony_ci		chip->pci = pci;
19308c2ecf20Sopenharmony_ci		chip->irq = -1;
19318c2ecf20Sopenharmony_ci		chip->opencount = 0;
19328c2ecf20Sopenharmony_ci		mutex_init(&chip->mode_mutex);
19338c2ecf20Sopenharmony_ci		chip->can_set_rate = 1;
19348c2ecf20Sopenharmony_ci	} else {
19358c2ecf20Sopenharmony_ci		/* If this was called from the resume function, chip is
19368c2ecf20Sopenharmony_ci		 * already allocated and it contains current card settings.
19378c2ecf20Sopenharmony_ci		 */
19388c2ecf20Sopenharmony_ci		chip = *rchip;
19398c2ecf20Sopenharmony_ci	}
19408c2ecf20Sopenharmony_ci
19418c2ecf20Sopenharmony_ci	/* PCI resource allocation */
19428c2ecf20Sopenharmony_ci	chip->dsp_registers_phys = pci_resource_start(pci, 0);
19438c2ecf20Sopenharmony_ci	sz = pci_resource_len(pci, 0);
19448c2ecf20Sopenharmony_ci	if (sz > PAGE_SIZE)
19458c2ecf20Sopenharmony_ci		sz = PAGE_SIZE;		/* We map only the required part */
19468c2ecf20Sopenharmony_ci
19478c2ecf20Sopenharmony_ci	if ((chip->iores = request_mem_region(chip->dsp_registers_phys, sz,
19488c2ecf20Sopenharmony_ci					      ECHOCARD_NAME)) == NULL) {
19498c2ecf20Sopenharmony_ci		dev_err(chip->card->dev, "cannot get memory region\n");
19508c2ecf20Sopenharmony_ci		snd_echo_free(chip);
19518c2ecf20Sopenharmony_ci		return -EBUSY;
19528c2ecf20Sopenharmony_ci	}
19538c2ecf20Sopenharmony_ci	chip->dsp_registers = ioremap(chip->dsp_registers_phys, sz);
19548c2ecf20Sopenharmony_ci	if (!chip->dsp_registers) {
19558c2ecf20Sopenharmony_ci		dev_err(chip->card->dev, "ioremap failed\n");
19568c2ecf20Sopenharmony_ci		snd_echo_free(chip);
19578c2ecf20Sopenharmony_ci		return -ENOMEM;
19588c2ecf20Sopenharmony_ci	}
19598c2ecf20Sopenharmony_ci
19608c2ecf20Sopenharmony_ci	if (request_irq(pci->irq, snd_echo_interrupt, IRQF_SHARED,
19618c2ecf20Sopenharmony_ci			KBUILD_MODNAME, chip)) {
19628c2ecf20Sopenharmony_ci		dev_err(chip->card->dev, "cannot grab irq\n");
19638c2ecf20Sopenharmony_ci		snd_echo_free(chip);
19648c2ecf20Sopenharmony_ci		return -EBUSY;
19658c2ecf20Sopenharmony_ci	}
19668c2ecf20Sopenharmony_ci	chip->irq = pci->irq;
19678c2ecf20Sopenharmony_ci	card->sync_irq = chip->irq;
19688c2ecf20Sopenharmony_ci	dev_dbg(card->dev, "pci=%p irq=%d subdev=%04x Init hardware...\n",
19698c2ecf20Sopenharmony_ci		chip->pci, chip->irq, chip->pci->subsystem_device);
19708c2ecf20Sopenharmony_ci
19718c2ecf20Sopenharmony_ci	/* Create the DSP comm page - this is the area of memory used for most
19728c2ecf20Sopenharmony_ci	of the communication with the DSP, which accesses it via bus mastering */
19738c2ecf20Sopenharmony_ci	if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &chip->pci->dev,
19748c2ecf20Sopenharmony_ci				sizeof(struct comm_page),
19758c2ecf20Sopenharmony_ci				&chip->commpage_dma_buf) < 0) {
19768c2ecf20Sopenharmony_ci		dev_err(chip->card->dev, "cannot allocate the comm page\n");
19778c2ecf20Sopenharmony_ci		snd_echo_free(chip);
19788c2ecf20Sopenharmony_ci		return -ENOMEM;
19798c2ecf20Sopenharmony_ci	}
19808c2ecf20Sopenharmony_ci	chip->comm_page_phys = chip->commpage_dma_buf.addr;
19818c2ecf20Sopenharmony_ci	chip->comm_page = (struct comm_page *)chip->commpage_dma_buf.area;
19828c2ecf20Sopenharmony_ci
19838c2ecf20Sopenharmony_ci	err = init_hw(chip, chip->pci->device, chip->pci->subsystem_device);
19848c2ecf20Sopenharmony_ci	if (err >= 0)
19858c2ecf20Sopenharmony_ci		err = set_mixer_defaults(chip);
19868c2ecf20Sopenharmony_ci	if (err < 0) {
19878c2ecf20Sopenharmony_ci		dev_err(card->dev, "init_hw err=%d\n", err);
19888c2ecf20Sopenharmony_ci		snd_echo_free(chip);
19898c2ecf20Sopenharmony_ci		return err;
19908c2ecf20Sopenharmony_ci	}
19918c2ecf20Sopenharmony_ci
19928c2ecf20Sopenharmony_ci	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
19938c2ecf20Sopenharmony_ci		snd_echo_free(chip);
19948c2ecf20Sopenharmony_ci		return err;
19958c2ecf20Sopenharmony_ci	}
19968c2ecf20Sopenharmony_ci	*rchip = chip;
19978c2ecf20Sopenharmony_ci	/* Init done ! */
19988c2ecf20Sopenharmony_ci	return 0;
19998c2ecf20Sopenharmony_ci}
20008c2ecf20Sopenharmony_ci
20018c2ecf20Sopenharmony_ci
20028c2ecf20Sopenharmony_ci
20038c2ecf20Sopenharmony_ci/* constructor */
20048c2ecf20Sopenharmony_cistatic int snd_echo_probe(struct pci_dev *pci,
20058c2ecf20Sopenharmony_ci			  const struct pci_device_id *pci_id)
20068c2ecf20Sopenharmony_ci{
20078c2ecf20Sopenharmony_ci	static int dev;
20088c2ecf20Sopenharmony_ci	struct snd_card *card;
20098c2ecf20Sopenharmony_ci	struct echoaudio *chip;
20108c2ecf20Sopenharmony_ci	char *dsp;
20118c2ecf20Sopenharmony_ci	__maybe_unused int i;
20128c2ecf20Sopenharmony_ci	int err;
20138c2ecf20Sopenharmony_ci
20148c2ecf20Sopenharmony_ci	if (dev >= SNDRV_CARDS)
20158c2ecf20Sopenharmony_ci		return -ENODEV;
20168c2ecf20Sopenharmony_ci	if (!enable[dev]) {
20178c2ecf20Sopenharmony_ci		dev++;
20188c2ecf20Sopenharmony_ci		return -ENOENT;
20198c2ecf20Sopenharmony_ci	}
20208c2ecf20Sopenharmony_ci
20218c2ecf20Sopenharmony_ci	i = 0;
20228c2ecf20Sopenharmony_ci	err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
20238c2ecf20Sopenharmony_ci			   0, &card);
20248c2ecf20Sopenharmony_ci	if (err < 0)
20258c2ecf20Sopenharmony_ci		return err;
20268c2ecf20Sopenharmony_ci
20278c2ecf20Sopenharmony_ci	chip = NULL;	/* Tells snd_echo_create to allocate chip */
20288c2ecf20Sopenharmony_ci	if ((err = snd_echo_create(card, pci, &chip)) < 0) {
20298c2ecf20Sopenharmony_ci		snd_card_free(card);
20308c2ecf20Sopenharmony_ci		return err;
20318c2ecf20Sopenharmony_ci	}
20328c2ecf20Sopenharmony_ci
20338c2ecf20Sopenharmony_ci	strcpy(card->driver, "Echo_" ECHOCARD_NAME);
20348c2ecf20Sopenharmony_ci	strcpy(card->shortname, chip->card_name);
20358c2ecf20Sopenharmony_ci
20368c2ecf20Sopenharmony_ci	dsp = "56301";
20378c2ecf20Sopenharmony_ci	if (pci_id->device == 0x3410)
20388c2ecf20Sopenharmony_ci		dsp = "56361";
20398c2ecf20Sopenharmony_ci
20408c2ecf20Sopenharmony_ci	sprintf(card->longname, "%s rev.%d (DSP%s) at 0x%lx irq %i",
20418c2ecf20Sopenharmony_ci		card->shortname, pci_id->subdevice & 0x000f, dsp,
20428c2ecf20Sopenharmony_ci		chip->dsp_registers_phys, chip->irq);
20438c2ecf20Sopenharmony_ci
20448c2ecf20Sopenharmony_ci	if ((err = snd_echo_new_pcm(chip)) < 0) {
20458c2ecf20Sopenharmony_ci		dev_err(chip->card->dev, "new pcm error %d\n", err);
20468c2ecf20Sopenharmony_ci		snd_card_free(card);
20478c2ecf20Sopenharmony_ci		return err;
20488c2ecf20Sopenharmony_ci	}
20498c2ecf20Sopenharmony_ci
20508c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_MIDI
20518c2ecf20Sopenharmony_ci	if (chip->has_midi) {	/* Some Mia's do not have midi */
20528c2ecf20Sopenharmony_ci		if ((err = snd_echo_midi_create(card, chip)) < 0) {
20538c2ecf20Sopenharmony_ci			dev_err(chip->card->dev, "new midi error %d\n", err);
20548c2ecf20Sopenharmony_ci			snd_card_free(card);
20558c2ecf20Sopenharmony_ci			return err;
20568c2ecf20Sopenharmony_ci		}
20578c2ecf20Sopenharmony_ci	}
20588c2ecf20Sopenharmony_ci#endif
20598c2ecf20Sopenharmony_ci
20608c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_VMIXER
20618c2ecf20Sopenharmony_ci	snd_echo_vmixer.count = num_pipes_out(chip) * num_busses_out(chip);
20628c2ecf20Sopenharmony_ci	if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vmixer, chip))) < 0)
20638c2ecf20Sopenharmony_ci		goto ctl_error;
20648c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_LINE_OUT_GAIN
20658c2ecf20Sopenharmony_ci	err = snd_ctl_add(chip->card,
20668c2ecf20Sopenharmony_ci			  snd_ctl_new1(&snd_echo_line_output_gain, chip));
20678c2ecf20Sopenharmony_ci	if (err < 0)
20688c2ecf20Sopenharmony_ci		goto ctl_error;
20698c2ecf20Sopenharmony_ci#endif
20708c2ecf20Sopenharmony_ci#else /* ECHOCARD_HAS_VMIXER */
20718c2ecf20Sopenharmony_ci	err = snd_ctl_add(chip->card,
20728c2ecf20Sopenharmony_ci			  snd_ctl_new1(&snd_echo_pcm_output_gain, chip));
20738c2ecf20Sopenharmony_ci	if (err < 0)
20748c2ecf20Sopenharmony_ci		goto ctl_error;
20758c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_VMIXER */
20768c2ecf20Sopenharmony_ci
20778c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_INPUT_GAIN
20788c2ecf20Sopenharmony_ci	if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_line_input_gain, chip))) < 0)
20798c2ecf20Sopenharmony_ci		goto ctl_error;
20808c2ecf20Sopenharmony_ci#endif
20818c2ecf20Sopenharmony_ci
20828c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_INPUT_NOMINAL_LEVEL
20838c2ecf20Sopenharmony_ci	if (!chip->hasnt_input_nominal_level)
20848c2ecf20Sopenharmony_ci		if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_intput_nominal_level, chip))) < 0)
20858c2ecf20Sopenharmony_ci			goto ctl_error;
20868c2ecf20Sopenharmony_ci#endif
20878c2ecf20Sopenharmony_ci
20888c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL
20898c2ecf20Sopenharmony_ci	if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_output_nominal_level, chip))) < 0)
20908c2ecf20Sopenharmony_ci		goto ctl_error;
20918c2ecf20Sopenharmony_ci#endif
20928c2ecf20Sopenharmony_ci
20938c2ecf20Sopenharmony_ci	if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vumeters_switch, chip))) < 0)
20948c2ecf20Sopenharmony_ci		goto ctl_error;
20958c2ecf20Sopenharmony_ci
20968c2ecf20Sopenharmony_ci	if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vumeters, chip))) < 0)
20978c2ecf20Sopenharmony_ci		goto ctl_error;
20988c2ecf20Sopenharmony_ci
20998c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_MONITOR
21008c2ecf20Sopenharmony_ci	snd_echo_monitor_mixer.count = num_busses_in(chip) * num_busses_out(chip);
21018c2ecf20Sopenharmony_ci	if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_monitor_mixer, chip))) < 0)
21028c2ecf20Sopenharmony_ci		goto ctl_error;
21038c2ecf20Sopenharmony_ci#endif
21048c2ecf20Sopenharmony_ci
21058c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE
21068c2ecf20Sopenharmony_ci	if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_automute_switch, chip))) < 0)
21078c2ecf20Sopenharmony_ci		goto ctl_error;
21088c2ecf20Sopenharmony_ci#endif
21098c2ecf20Sopenharmony_ci
21108c2ecf20Sopenharmony_ci	if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_channels_info, chip))) < 0)
21118c2ecf20Sopenharmony_ci		goto ctl_error;
21128c2ecf20Sopenharmony_ci
21138c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_DIGITAL_MODE_SWITCH
21148c2ecf20Sopenharmony_ci	/* Creates a list of available digital modes */
21158c2ecf20Sopenharmony_ci	chip->num_digital_modes = 0;
21168c2ecf20Sopenharmony_ci	for (i = 0; i < 6; i++)
21178c2ecf20Sopenharmony_ci		if (chip->digital_modes & (1 << i))
21188c2ecf20Sopenharmony_ci			chip->digital_mode_list[chip->num_digital_modes++] = i;
21198c2ecf20Sopenharmony_ci
21208c2ecf20Sopenharmony_ci	if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_digital_mode_switch, chip))) < 0)
21218c2ecf20Sopenharmony_ci		goto ctl_error;
21228c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_DIGITAL_MODE_SWITCH */
21238c2ecf20Sopenharmony_ci
21248c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_EXTERNAL_CLOCK
21258c2ecf20Sopenharmony_ci	/* Creates a list of available clock sources */
21268c2ecf20Sopenharmony_ci	chip->num_clock_sources = 0;
21278c2ecf20Sopenharmony_ci	for (i = 0; i < 10; i++)
21288c2ecf20Sopenharmony_ci		if (chip->input_clock_types & (1 << i))
21298c2ecf20Sopenharmony_ci			chip->clock_source_list[chip->num_clock_sources++] = i;
21308c2ecf20Sopenharmony_ci
21318c2ecf20Sopenharmony_ci	if (chip->num_clock_sources > 1) {
21328c2ecf20Sopenharmony_ci		chip->clock_src_ctl = snd_ctl_new1(&snd_echo_clock_source_switch, chip);
21338c2ecf20Sopenharmony_ci		if ((err = snd_ctl_add(chip->card, chip->clock_src_ctl)) < 0)
21348c2ecf20Sopenharmony_ci			goto ctl_error;
21358c2ecf20Sopenharmony_ci	}
21368c2ecf20Sopenharmony_ci#endif /* ECHOCARD_HAS_EXTERNAL_CLOCK */
21378c2ecf20Sopenharmony_ci
21388c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_DIGITAL_IO
21398c2ecf20Sopenharmony_ci	if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_spdif_mode_switch, chip))) < 0)
21408c2ecf20Sopenharmony_ci		goto ctl_error;
21418c2ecf20Sopenharmony_ci#endif
21428c2ecf20Sopenharmony_ci
21438c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_PHANTOM_POWER
21448c2ecf20Sopenharmony_ci	if (chip->has_phantom_power)
21458c2ecf20Sopenharmony_ci		if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_phantom_power_switch, chip))) < 0)
21468c2ecf20Sopenharmony_ci			goto ctl_error;
21478c2ecf20Sopenharmony_ci#endif
21488c2ecf20Sopenharmony_ci
21498c2ecf20Sopenharmony_ci	err = snd_card_register(card);
21508c2ecf20Sopenharmony_ci	if (err < 0)
21518c2ecf20Sopenharmony_ci		goto ctl_error;
21528c2ecf20Sopenharmony_ci	dev_info(card->dev, "Card registered: %s\n", card->longname);
21538c2ecf20Sopenharmony_ci
21548c2ecf20Sopenharmony_ci	pci_set_drvdata(pci, chip);
21558c2ecf20Sopenharmony_ci	dev++;
21568c2ecf20Sopenharmony_ci	return 0;
21578c2ecf20Sopenharmony_ci
21588c2ecf20Sopenharmony_cictl_error:
21598c2ecf20Sopenharmony_ci	dev_err(card->dev, "new control error %d\n", err);
21608c2ecf20Sopenharmony_ci	snd_card_free(card);
21618c2ecf20Sopenharmony_ci	return err;
21628c2ecf20Sopenharmony_ci}
21638c2ecf20Sopenharmony_ci
21648c2ecf20Sopenharmony_ci
21658c2ecf20Sopenharmony_ci
21668c2ecf20Sopenharmony_ci#if defined(CONFIG_PM_SLEEP)
21678c2ecf20Sopenharmony_ci
21688c2ecf20Sopenharmony_cistatic int snd_echo_suspend(struct device *dev)
21698c2ecf20Sopenharmony_ci{
21708c2ecf20Sopenharmony_ci	struct echoaudio *chip = dev_get_drvdata(dev);
21718c2ecf20Sopenharmony_ci
21728c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_MIDI
21738c2ecf20Sopenharmony_ci	/* This call can sleep */
21748c2ecf20Sopenharmony_ci	if (chip->midi_out)
21758c2ecf20Sopenharmony_ci		snd_echo_midi_output_trigger(chip->midi_out, 0);
21768c2ecf20Sopenharmony_ci#endif
21778c2ecf20Sopenharmony_ci	spin_lock_irq(&chip->lock);
21788c2ecf20Sopenharmony_ci	if (wait_handshake(chip)) {
21798c2ecf20Sopenharmony_ci		spin_unlock_irq(&chip->lock);
21808c2ecf20Sopenharmony_ci		return -EIO;
21818c2ecf20Sopenharmony_ci	}
21828c2ecf20Sopenharmony_ci	clear_handshake(chip);
21838c2ecf20Sopenharmony_ci	if (send_vector(chip, DSP_VC_GO_COMATOSE) < 0) {
21848c2ecf20Sopenharmony_ci		spin_unlock_irq(&chip->lock);
21858c2ecf20Sopenharmony_ci		return -EIO;
21868c2ecf20Sopenharmony_ci	}
21878c2ecf20Sopenharmony_ci	spin_unlock_irq(&chip->lock);
21888c2ecf20Sopenharmony_ci
21898c2ecf20Sopenharmony_ci	chip->dsp_code = NULL;
21908c2ecf20Sopenharmony_ci	free_irq(chip->irq, chip);
21918c2ecf20Sopenharmony_ci	chip->irq = -1;
21928c2ecf20Sopenharmony_ci	chip->card->sync_irq = -1;
21938c2ecf20Sopenharmony_ci	return 0;
21948c2ecf20Sopenharmony_ci}
21958c2ecf20Sopenharmony_ci
21968c2ecf20Sopenharmony_ci
21978c2ecf20Sopenharmony_ci
21988c2ecf20Sopenharmony_cistatic int snd_echo_resume(struct device *dev)
21998c2ecf20Sopenharmony_ci{
22008c2ecf20Sopenharmony_ci	struct pci_dev *pci = to_pci_dev(dev);
22018c2ecf20Sopenharmony_ci	struct echoaudio *chip = dev_get_drvdata(dev);
22028c2ecf20Sopenharmony_ci	struct comm_page *commpage, *commpage_bak;
22038c2ecf20Sopenharmony_ci	u32 pipe_alloc_mask;
22048c2ecf20Sopenharmony_ci	int err;
22058c2ecf20Sopenharmony_ci
22068c2ecf20Sopenharmony_ci	commpage = chip->comm_page;
22078c2ecf20Sopenharmony_ci	commpage_bak = kmemdup(commpage, sizeof(*commpage), GFP_KERNEL);
22088c2ecf20Sopenharmony_ci	if (commpage_bak == NULL)
22098c2ecf20Sopenharmony_ci		return -ENOMEM;
22108c2ecf20Sopenharmony_ci
22118c2ecf20Sopenharmony_ci	err = init_hw(chip, chip->pci->device, chip->pci->subsystem_device);
22128c2ecf20Sopenharmony_ci	if (err < 0) {
22138c2ecf20Sopenharmony_ci		kfree(commpage_bak);
22148c2ecf20Sopenharmony_ci		dev_err(dev, "resume init_hw err=%d\n", err);
22158c2ecf20Sopenharmony_ci		return err;
22168c2ecf20Sopenharmony_ci	}
22178c2ecf20Sopenharmony_ci
22188c2ecf20Sopenharmony_ci	/* Temporarily set chip->pipe_alloc_mask=0 otherwise
22198c2ecf20Sopenharmony_ci	 * restore_dsp_settings() fails.
22208c2ecf20Sopenharmony_ci	 */
22218c2ecf20Sopenharmony_ci	pipe_alloc_mask = chip->pipe_alloc_mask;
22228c2ecf20Sopenharmony_ci	chip->pipe_alloc_mask = 0;
22238c2ecf20Sopenharmony_ci	err = restore_dsp_rettings(chip);
22248c2ecf20Sopenharmony_ci	chip->pipe_alloc_mask = pipe_alloc_mask;
22258c2ecf20Sopenharmony_ci	if (err < 0) {
22268c2ecf20Sopenharmony_ci		kfree(commpage_bak);
22278c2ecf20Sopenharmony_ci		return err;
22288c2ecf20Sopenharmony_ci	}
22298c2ecf20Sopenharmony_ci
22308c2ecf20Sopenharmony_ci	memcpy(&commpage->audio_format, &commpage_bak->audio_format,
22318c2ecf20Sopenharmony_ci		sizeof(commpage->audio_format));
22328c2ecf20Sopenharmony_ci	memcpy(&commpage->sglist_addr, &commpage_bak->sglist_addr,
22338c2ecf20Sopenharmony_ci		sizeof(commpage->sglist_addr));
22348c2ecf20Sopenharmony_ci	memcpy(&commpage->midi_output, &commpage_bak->midi_output,
22358c2ecf20Sopenharmony_ci		sizeof(commpage->midi_output));
22368c2ecf20Sopenharmony_ci	kfree(commpage_bak);
22378c2ecf20Sopenharmony_ci
22388c2ecf20Sopenharmony_ci	if (request_irq(pci->irq, snd_echo_interrupt, IRQF_SHARED,
22398c2ecf20Sopenharmony_ci			KBUILD_MODNAME, chip)) {
22408c2ecf20Sopenharmony_ci		dev_err(chip->card->dev, "cannot grab irq\n");
22418c2ecf20Sopenharmony_ci		return -EBUSY;
22428c2ecf20Sopenharmony_ci	}
22438c2ecf20Sopenharmony_ci	chip->irq = pci->irq;
22448c2ecf20Sopenharmony_ci	chip->card->sync_irq = chip->irq;
22458c2ecf20Sopenharmony_ci	dev_dbg(dev, "resume irq=%d\n", chip->irq);
22468c2ecf20Sopenharmony_ci
22478c2ecf20Sopenharmony_ci#ifdef ECHOCARD_HAS_MIDI
22488c2ecf20Sopenharmony_ci	if (chip->midi_input_enabled)
22498c2ecf20Sopenharmony_ci		enable_midi_input(chip, true);
22508c2ecf20Sopenharmony_ci	if (chip->midi_out)
22518c2ecf20Sopenharmony_ci		snd_echo_midi_output_trigger(chip->midi_out, 1);
22528c2ecf20Sopenharmony_ci#endif
22538c2ecf20Sopenharmony_ci
22548c2ecf20Sopenharmony_ci	return 0;
22558c2ecf20Sopenharmony_ci}
22568c2ecf20Sopenharmony_ci
22578c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(snd_echo_pm, snd_echo_suspend, snd_echo_resume);
22588c2ecf20Sopenharmony_ci#define SND_ECHO_PM_OPS	&snd_echo_pm
22598c2ecf20Sopenharmony_ci#else
22608c2ecf20Sopenharmony_ci#define SND_ECHO_PM_OPS	NULL
22618c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */
22628c2ecf20Sopenharmony_ci
22638c2ecf20Sopenharmony_ci
22648c2ecf20Sopenharmony_cistatic void snd_echo_remove(struct pci_dev *pci)
22658c2ecf20Sopenharmony_ci{
22668c2ecf20Sopenharmony_ci	struct echoaudio *chip;
22678c2ecf20Sopenharmony_ci
22688c2ecf20Sopenharmony_ci	chip = pci_get_drvdata(pci);
22698c2ecf20Sopenharmony_ci	if (chip)
22708c2ecf20Sopenharmony_ci		snd_card_free(chip->card);
22718c2ecf20Sopenharmony_ci}
22728c2ecf20Sopenharmony_ci
22738c2ecf20Sopenharmony_ci
22748c2ecf20Sopenharmony_ci
22758c2ecf20Sopenharmony_ci/******************************************************************************
22768c2ecf20Sopenharmony_ci	Everything starts and ends here
22778c2ecf20Sopenharmony_ci******************************************************************************/
22788c2ecf20Sopenharmony_ci
22798c2ecf20Sopenharmony_ci/* pci_driver definition */
22808c2ecf20Sopenharmony_cistatic struct pci_driver echo_driver = {
22818c2ecf20Sopenharmony_ci	.name = KBUILD_MODNAME,
22828c2ecf20Sopenharmony_ci	.id_table = snd_echo_ids,
22838c2ecf20Sopenharmony_ci	.probe = snd_echo_probe,
22848c2ecf20Sopenharmony_ci	.remove = snd_echo_remove,
22858c2ecf20Sopenharmony_ci	.driver = {
22868c2ecf20Sopenharmony_ci		.pm = SND_ECHO_PM_OPS,
22878c2ecf20Sopenharmony_ci	},
22888c2ecf20Sopenharmony_ci};
22898c2ecf20Sopenharmony_ci
22908c2ecf20Sopenharmony_cimodule_pci_driver(echo_driver);
2291