162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Dummy soundcard
462306a36Sopenharmony_ci *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/init.h>
862306a36Sopenharmony_ci#include <linux/err.h>
962306a36Sopenharmony_ci#include <linux/platform_device.h>
1062306a36Sopenharmony_ci#include <linux/jiffies.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/time.h>
1362306a36Sopenharmony_ci#include <linux/wait.h>
1462306a36Sopenharmony_ci#include <linux/hrtimer.h>
1562306a36Sopenharmony_ci#include <linux/math64.h>
1662306a36Sopenharmony_ci#include <linux/module.h>
1762306a36Sopenharmony_ci#include <sound/core.h>
1862306a36Sopenharmony_ci#include <sound/control.h>
1962306a36Sopenharmony_ci#include <sound/tlv.h>
2062306a36Sopenharmony_ci#include <sound/pcm.h>
2162306a36Sopenharmony_ci#include <sound/rawmidi.h>
2262306a36Sopenharmony_ci#include <sound/info.h>
2362306a36Sopenharmony_ci#include <sound/initval.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
2662306a36Sopenharmony_ciMODULE_DESCRIPTION("Dummy soundcard (/dev/null)");
2762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define MAX_PCM_DEVICES		4
3062306a36Sopenharmony_ci#define MAX_PCM_SUBSTREAMS	128
3162306a36Sopenharmony_ci#define MAX_MIDI_DEVICES	2
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/* defaults */
3462306a36Sopenharmony_ci#define MAX_BUFFER_SIZE		(64*1024)
3562306a36Sopenharmony_ci#define MIN_PERIOD_SIZE		64
3662306a36Sopenharmony_ci#define MAX_PERIOD_SIZE		MAX_BUFFER_SIZE
3762306a36Sopenharmony_ci#define USE_FORMATS 		(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE)
3862306a36Sopenharmony_ci#define USE_RATE		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000
3962306a36Sopenharmony_ci#define USE_RATE_MIN		5500
4062306a36Sopenharmony_ci#define USE_RATE_MAX		48000
4162306a36Sopenharmony_ci#define USE_CHANNELS_MIN 	1
4262306a36Sopenharmony_ci#define USE_CHANNELS_MAX 	2
4362306a36Sopenharmony_ci#define USE_PERIODS_MIN 	1
4462306a36Sopenharmony_ci#define USE_PERIODS_MAX 	1024
4562306a36Sopenharmony_ci#define USE_MIXER_VOLUME_LEVEL_MIN	-50
4662306a36Sopenharmony_ci#define USE_MIXER_VOLUME_LEVEL_MAX	100
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
4962306a36Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
5062306a36Sopenharmony_cistatic bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
5162306a36Sopenharmony_cistatic char *model[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = NULL};
5262306a36Sopenharmony_cistatic int pcm_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
5362306a36Sopenharmony_cistatic int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8};
5462306a36Sopenharmony_ci//static int midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
5562306a36Sopenharmony_cistatic int mixer_volume_level_min = USE_MIXER_VOLUME_LEVEL_MIN;
5662306a36Sopenharmony_cistatic int mixer_volume_level_max = USE_MIXER_VOLUME_LEVEL_MAX;
5762306a36Sopenharmony_ci#ifdef CONFIG_HIGH_RES_TIMERS
5862306a36Sopenharmony_cistatic bool hrtimer = 1;
5962306a36Sopenharmony_ci#endif
6062306a36Sopenharmony_cistatic bool fake_buffer = 1;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cimodule_param_array(index, int, NULL, 0444);
6362306a36Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for dummy soundcard.");
6462306a36Sopenharmony_cimodule_param_array(id, charp, NULL, 0444);
6562306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for dummy soundcard.");
6662306a36Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444);
6762306a36Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable this dummy soundcard.");
6862306a36Sopenharmony_cimodule_param_array(model, charp, NULL, 0444);
6962306a36Sopenharmony_ciMODULE_PARM_DESC(model, "Soundcard model.");
7062306a36Sopenharmony_cimodule_param_array(pcm_devs, int, NULL, 0444);
7162306a36Sopenharmony_ciMODULE_PARM_DESC(pcm_devs, "PCM devices # (0-4) for dummy driver.");
7262306a36Sopenharmony_cimodule_param_array(pcm_substreams, int, NULL, 0444);
7362306a36Sopenharmony_ciMODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-128) for dummy driver.");
7462306a36Sopenharmony_ci//module_param_array(midi_devs, int, NULL, 0444);
7562306a36Sopenharmony_ci//MODULE_PARM_DESC(midi_devs, "MIDI devices # (0-2) for dummy driver.");
7662306a36Sopenharmony_cimodule_param(mixer_volume_level_min, int, 0444);
7762306a36Sopenharmony_ciMODULE_PARM_DESC(mixer_volume_level_min, "Minimum mixer volume level for dummy driver. Default: -50");
7862306a36Sopenharmony_cimodule_param(mixer_volume_level_max, int, 0444);
7962306a36Sopenharmony_ciMODULE_PARM_DESC(mixer_volume_level_max, "Maximum mixer volume level for dummy driver. Default: 100");
8062306a36Sopenharmony_cimodule_param(fake_buffer, bool, 0444);
8162306a36Sopenharmony_ciMODULE_PARM_DESC(fake_buffer, "Fake buffer allocations.");
8262306a36Sopenharmony_ci#ifdef CONFIG_HIGH_RES_TIMERS
8362306a36Sopenharmony_cimodule_param(hrtimer, bool, 0644);
8462306a36Sopenharmony_ciMODULE_PARM_DESC(hrtimer, "Use hrtimer as the timer source.");
8562306a36Sopenharmony_ci#endif
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic struct platform_device *devices[SNDRV_CARDS];
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci#define MIXER_ADDR_MASTER	0
9062306a36Sopenharmony_ci#define MIXER_ADDR_LINE		1
9162306a36Sopenharmony_ci#define MIXER_ADDR_MIC		2
9262306a36Sopenharmony_ci#define MIXER_ADDR_SYNTH	3
9362306a36Sopenharmony_ci#define MIXER_ADDR_CD		4
9462306a36Sopenharmony_ci#define MIXER_ADDR_LAST		4
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistruct dummy_timer_ops {
9762306a36Sopenharmony_ci	int (*create)(struct snd_pcm_substream *);
9862306a36Sopenharmony_ci	void (*free)(struct snd_pcm_substream *);
9962306a36Sopenharmony_ci	int (*prepare)(struct snd_pcm_substream *);
10062306a36Sopenharmony_ci	int (*start)(struct snd_pcm_substream *);
10162306a36Sopenharmony_ci	int (*stop)(struct snd_pcm_substream *);
10262306a36Sopenharmony_ci	snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *);
10362306a36Sopenharmony_ci};
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci#define get_dummy_ops(substream) \
10662306a36Sopenharmony_ci	(*(const struct dummy_timer_ops **)(substream)->runtime->private_data)
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistruct dummy_model {
10962306a36Sopenharmony_ci	const char *name;
11062306a36Sopenharmony_ci	int (*playback_constraints)(struct snd_pcm_runtime *runtime);
11162306a36Sopenharmony_ci	int (*capture_constraints)(struct snd_pcm_runtime *runtime);
11262306a36Sopenharmony_ci	u64 formats;
11362306a36Sopenharmony_ci	size_t buffer_bytes_max;
11462306a36Sopenharmony_ci	size_t period_bytes_min;
11562306a36Sopenharmony_ci	size_t period_bytes_max;
11662306a36Sopenharmony_ci	unsigned int periods_min;
11762306a36Sopenharmony_ci	unsigned int periods_max;
11862306a36Sopenharmony_ci	unsigned int rates;
11962306a36Sopenharmony_ci	unsigned int rate_min;
12062306a36Sopenharmony_ci	unsigned int rate_max;
12162306a36Sopenharmony_ci	unsigned int channels_min;
12262306a36Sopenharmony_ci	unsigned int channels_max;
12362306a36Sopenharmony_ci};
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistruct snd_dummy {
12662306a36Sopenharmony_ci	struct snd_card *card;
12762306a36Sopenharmony_ci	const struct dummy_model *model;
12862306a36Sopenharmony_ci	struct snd_pcm *pcm;
12962306a36Sopenharmony_ci	struct snd_pcm_hardware pcm_hw;
13062306a36Sopenharmony_ci	spinlock_t mixer_lock;
13162306a36Sopenharmony_ci	int mixer_volume[MIXER_ADDR_LAST+1][2];
13262306a36Sopenharmony_ci	int capture_source[MIXER_ADDR_LAST+1][2];
13362306a36Sopenharmony_ci	int iobox;
13462306a36Sopenharmony_ci	struct snd_kcontrol *cd_volume_ctl;
13562306a36Sopenharmony_ci	struct snd_kcontrol *cd_switch_ctl;
13662306a36Sopenharmony_ci};
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci/*
13962306a36Sopenharmony_ci * card models
14062306a36Sopenharmony_ci */
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic int emu10k1_playback_constraints(struct snd_pcm_runtime *runtime)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	int err;
14562306a36Sopenharmony_ci	err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
14662306a36Sopenharmony_ci	if (err < 0)
14762306a36Sopenharmony_ci		return err;
14862306a36Sopenharmony_ci	err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256, UINT_MAX);
14962306a36Sopenharmony_ci	if (err < 0)
15062306a36Sopenharmony_ci		return err;
15162306a36Sopenharmony_ci	return 0;
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic const struct dummy_model model_emu10k1 = {
15562306a36Sopenharmony_ci	.name = "emu10k1",
15662306a36Sopenharmony_ci	.playback_constraints = emu10k1_playback_constraints,
15762306a36Sopenharmony_ci	.buffer_bytes_max = 128 * 1024,
15862306a36Sopenharmony_ci};
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic const struct dummy_model model_rme9652 = {
16162306a36Sopenharmony_ci	.name = "rme9652",
16262306a36Sopenharmony_ci	.buffer_bytes_max = 26 * 64 * 1024,
16362306a36Sopenharmony_ci	.formats = SNDRV_PCM_FMTBIT_S32_LE,
16462306a36Sopenharmony_ci	.channels_min = 26,
16562306a36Sopenharmony_ci	.channels_max = 26,
16662306a36Sopenharmony_ci	.periods_min = 2,
16762306a36Sopenharmony_ci	.periods_max = 2,
16862306a36Sopenharmony_ci};
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic const struct dummy_model model_ice1712 = {
17162306a36Sopenharmony_ci	.name = "ice1712",
17262306a36Sopenharmony_ci	.buffer_bytes_max = 256 * 1024,
17362306a36Sopenharmony_ci	.formats = SNDRV_PCM_FMTBIT_S32_LE,
17462306a36Sopenharmony_ci	.channels_min = 10,
17562306a36Sopenharmony_ci	.channels_max = 10,
17662306a36Sopenharmony_ci	.periods_min = 1,
17762306a36Sopenharmony_ci	.periods_max = 1024,
17862306a36Sopenharmony_ci};
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic const struct dummy_model model_uda1341 = {
18162306a36Sopenharmony_ci	.name = "uda1341",
18262306a36Sopenharmony_ci	.buffer_bytes_max = 16380,
18362306a36Sopenharmony_ci	.formats = SNDRV_PCM_FMTBIT_S16_LE,
18462306a36Sopenharmony_ci	.channels_min = 2,
18562306a36Sopenharmony_ci	.channels_max = 2,
18662306a36Sopenharmony_ci	.periods_min = 2,
18762306a36Sopenharmony_ci	.periods_max = 255,
18862306a36Sopenharmony_ci};
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic const struct dummy_model model_ac97 = {
19162306a36Sopenharmony_ci	.name = "ac97",
19262306a36Sopenharmony_ci	.formats = SNDRV_PCM_FMTBIT_S16_LE,
19362306a36Sopenharmony_ci	.channels_min = 2,
19462306a36Sopenharmony_ci	.channels_max = 2,
19562306a36Sopenharmony_ci	.rates = SNDRV_PCM_RATE_48000,
19662306a36Sopenharmony_ci	.rate_min = 48000,
19762306a36Sopenharmony_ci	.rate_max = 48000,
19862306a36Sopenharmony_ci};
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic const struct dummy_model model_ca0106 = {
20162306a36Sopenharmony_ci	.name = "ca0106",
20262306a36Sopenharmony_ci	.formats = SNDRV_PCM_FMTBIT_S16_LE,
20362306a36Sopenharmony_ci	.buffer_bytes_max = ((65536-64)*8),
20462306a36Sopenharmony_ci	.period_bytes_max = (65536-64),
20562306a36Sopenharmony_ci	.periods_min = 2,
20662306a36Sopenharmony_ci	.periods_max = 8,
20762306a36Sopenharmony_ci	.channels_min = 2,
20862306a36Sopenharmony_ci	.channels_max = 2,
20962306a36Sopenharmony_ci	.rates = SNDRV_PCM_RATE_48000|SNDRV_PCM_RATE_96000|SNDRV_PCM_RATE_192000,
21062306a36Sopenharmony_ci	.rate_min = 48000,
21162306a36Sopenharmony_ci	.rate_max = 192000,
21262306a36Sopenharmony_ci};
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cistatic const struct dummy_model *dummy_models[] = {
21562306a36Sopenharmony_ci	&model_emu10k1,
21662306a36Sopenharmony_ci	&model_rme9652,
21762306a36Sopenharmony_ci	&model_ice1712,
21862306a36Sopenharmony_ci	&model_uda1341,
21962306a36Sopenharmony_ci	&model_ac97,
22062306a36Sopenharmony_ci	&model_ca0106,
22162306a36Sopenharmony_ci	NULL
22262306a36Sopenharmony_ci};
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci/*
22562306a36Sopenharmony_ci * system timer interface
22662306a36Sopenharmony_ci */
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistruct dummy_systimer_pcm {
22962306a36Sopenharmony_ci	/* ops must be the first item */
23062306a36Sopenharmony_ci	const struct dummy_timer_ops *timer_ops;
23162306a36Sopenharmony_ci	spinlock_t lock;
23262306a36Sopenharmony_ci	struct timer_list timer;
23362306a36Sopenharmony_ci	unsigned long base_time;
23462306a36Sopenharmony_ci	unsigned int frac_pos;	/* fractional sample position (based HZ) */
23562306a36Sopenharmony_ci	unsigned int frac_period_rest;
23662306a36Sopenharmony_ci	unsigned int frac_buffer_size;	/* buffer_size * HZ */
23762306a36Sopenharmony_ci	unsigned int frac_period_size;	/* period_size * HZ */
23862306a36Sopenharmony_ci	unsigned int rate;
23962306a36Sopenharmony_ci	int elapsed;
24062306a36Sopenharmony_ci	struct snd_pcm_substream *substream;
24162306a36Sopenharmony_ci};
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_cistatic void dummy_systimer_rearm(struct dummy_systimer_pcm *dpcm)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	mod_timer(&dpcm->timer, jiffies +
24662306a36Sopenharmony_ci		DIV_ROUND_UP(dpcm->frac_period_rest, dpcm->rate));
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_cistatic void dummy_systimer_update(struct dummy_systimer_pcm *dpcm)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	unsigned long delta;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	delta = jiffies - dpcm->base_time;
25462306a36Sopenharmony_ci	if (!delta)
25562306a36Sopenharmony_ci		return;
25662306a36Sopenharmony_ci	dpcm->base_time += delta;
25762306a36Sopenharmony_ci	delta *= dpcm->rate;
25862306a36Sopenharmony_ci	dpcm->frac_pos += delta;
25962306a36Sopenharmony_ci	while (dpcm->frac_pos >= dpcm->frac_buffer_size)
26062306a36Sopenharmony_ci		dpcm->frac_pos -= dpcm->frac_buffer_size;
26162306a36Sopenharmony_ci	while (dpcm->frac_period_rest <= delta) {
26262306a36Sopenharmony_ci		dpcm->elapsed++;
26362306a36Sopenharmony_ci		dpcm->frac_period_rest += dpcm->frac_period_size;
26462306a36Sopenharmony_ci	}
26562306a36Sopenharmony_ci	dpcm->frac_period_rest -= delta;
26662306a36Sopenharmony_ci}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_cistatic int dummy_systimer_start(struct snd_pcm_substream *substream)
26962306a36Sopenharmony_ci{
27062306a36Sopenharmony_ci	struct dummy_systimer_pcm *dpcm = substream->runtime->private_data;
27162306a36Sopenharmony_ci	spin_lock(&dpcm->lock);
27262306a36Sopenharmony_ci	dpcm->base_time = jiffies;
27362306a36Sopenharmony_ci	dummy_systimer_rearm(dpcm);
27462306a36Sopenharmony_ci	spin_unlock(&dpcm->lock);
27562306a36Sopenharmony_ci	return 0;
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_cistatic int dummy_systimer_stop(struct snd_pcm_substream *substream)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci	struct dummy_systimer_pcm *dpcm = substream->runtime->private_data;
28162306a36Sopenharmony_ci	spin_lock(&dpcm->lock);
28262306a36Sopenharmony_ci	del_timer(&dpcm->timer);
28362306a36Sopenharmony_ci	spin_unlock(&dpcm->lock);
28462306a36Sopenharmony_ci	return 0;
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic int dummy_systimer_prepare(struct snd_pcm_substream *substream)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
29062306a36Sopenharmony_ci	struct dummy_systimer_pcm *dpcm = runtime->private_data;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	dpcm->frac_pos = 0;
29362306a36Sopenharmony_ci	dpcm->rate = runtime->rate;
29462306a36Sopenharmony_ci	dpcm->frac_buffer_size = runtime->buffer_size * HZ;
29562306a36Sopenharmony_ci	dpcm->frac_period_size = runtime->period_size * HZ;
29662306a36Sopenharmony_ci	dpcm->frac_period_rest = dpcm->frac_period_size;
29762306a36Sopenharmony_ci	dpcm->elapsed = 0;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	return 0;
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistatic void dummy_systimer_callback(struct timer_list *t)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	struct dummy_systimer_pcm *dpcm = from_timer(dpcm, t, timer);
30562306a36Sopenharmony_ci	unsigned long flags;
30662306a36Sopenharmony_ci	int elapsed = 0;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	spin_lock_irqsave(&dpcm->lock, flags);
30962306a36Sopenharmony_ci	dummy_systimer_update(dpcm);
31062306a36Sopenharmony_ci	dummy_systimer_rearm(dpcm);
31162306a36Sopenharmony_ci	elapsed = dpcm->elapsed;
31262306a36Sopenharmony_ci	dpcm->elapsed = 0;
31362306a36Sopenharmony_ci	spin_unlock_irqrestore(&dpcm->lock, flags);
31462306a36Sopenharmony_ci	if (elapsed)
31562306a36Sopenharmony_ci		snd_pcm_period_elapsed(dpcm->substream);
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic snd_pcm_uframes_t
31962306a36Sopenharmony_cidummy_systimer_pointer(struct snd_pcm_substream *substream)
32062306a36Sopenharmony_ci{
32162306a36Sopenharmony_ci	struct dummy_systimer_pcm *dpcm = substream->runtime->private_data;
32262306a36Sopenharmony_ci	snd_pcm_uframes_t pos;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	spin_lock(&dpcm->lock);
32562306a36Sopenharmony_ci	dummy_systimer_update(dpcm);
32662306a36Sopenharmony_ci	pos = dpcm->frac_pos / HZ;
32762306a36Sopenharmony_ci	spin_unlock(&dpcm->lock);
32862306a36Sopenharmony_ci	return pos;
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_cistatic int dummy_systimer_create(struct snd_pcm_substream *substream)
33262306a36Sopenharmony_ci{
33362306a36Sopenharmony_ci	struct dummy_systimer_pcm *dpcm;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
33662306a36Sopenharmony_ci	if (!dpcm)
33762306a36Sopenharmony_ci		return -ENOMEM;
33862306a36Sopenharmony_ci	substream->runtime->private_data = dpcm;
33962306a36Sopenharmony_ci	timer_setup(&dpcm->timer, dummy_systimer_callback, 0);
34062306a36Sopenharmony_ci	spin_lock_init(&dpcm->lock);
34162306a36Sopenharmony_ci	dpcm->substream = substream;
34262306a36Sopenharmony_ci	return 0;
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cistatic void dummy_systimer_free(struct snd_pcm_substream *substream)
34662306a36Sopenharmony_ci{
34762306a36Sopenharmony_ci	kfree(substream->runtime->private_data);
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic const struct dummy_timer_ops dummy_systimer_ops = {
35162306a36Sopenharmony_ci	.create =	dummy_systimer_create,
35262306a36Sopenharmony_ci	.free =		dummy_systimer_free,
35362306a36Sopenharmony_ci	.prepare =	dummy_systimer_prepare,
35462306a36Sopenharmony_ci	.start =	dummy_systimer_start,
35562306a36Sopenharmony_ci	.stop =		dummy_systimer_stop,
35662306a36Sopenharmony_ci	.pointer =	dummy_systimer_pointer,
35762306a36Sopenharmony_ci};
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci#ifdef CONFIG_HIGH_RES_TIMERS
36062306a36Sopenharmony_ci/*
36162306a36Sopenharmony_ci * hrtimer interface
36262306a36Sopenharmony_ci */
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_cistruct dummy_hrtimer_pcm {
36562306a36Sopenharmony_ci	/* ops must be the first item */
36662306a36Sopenharmony_ci	const struct dummy_timer_ops *timer_ops;
36762306a36Sopenharmony_ci	ktime_t base_time;
36862306a36Sopenharmony_ci	ktime_t period_time;
36962306a36Sopenharmony_ci	atomic_t running;
37062306a36Sopenharmony_ci	struct hrtimer timer;
37162306a36Sopenharmony_ci	struct snd_pcm_substream *substream;
37262306a36Sopenharmony_ci};
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_cistatic enum hrtimer_restart dummy_hrtimer_callback(struct hrtimer *timer)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	struct dummy_hrtimer_pcm *dpcm;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	dpcm = container_of(timer, struct dummy_hrtimer_pcm, timer);
37962306a36Sopenharmony_ci	if (!atomic_read(&dpcm->running))
38062306a36Sopenharmony_ci		return HRTIMER_NORESTART;
38162306a36Sopenharmony_ci	/*
38262306a36Sopenharmony_ci	 * In cases of XRUN and draining, this calls .trigger to stop PCM
38362306a36Sopenharmony_ci	 * substream.
38462306a36Sopenharmony_ci	 */
38562306a36Sopenharmony_ci	snd_pcm_period_elapsed(dpcm->substream);
38662306a36Sopenharmony_ci	if (!atomic_read(&dpcm->running))
38762306a36Sopenharmony_ci		return HRTIMER_NORESTART;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	hrtimer_forward_now(timer, dpcm->period_time);
39062306a36Sopenharmony_ci	return HRTIMER_RESTART;
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_cistatic int dummy_hrtimer_start(struct snd_pcm_substream *substream)
39462306a36Sopenharmony_ci{
39562306a36Sopenharmony_ci	struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	dpcm->base_time = hrtimer_cb_get_time(&dpcm->timer);
39862306a36Sopenharmony_ci	hrtimer_start(&dpcm->timer, dpcm->period_time, HRTIMER_MODE_REL_SOFT);
39962306a36Sopenharmony_ci	atomic_set(&dpcm->running, 1);
40062306a36Sopenharmony_ci	return 0;
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_cistatic int dummy_hrtimer_stop(struct snd_pcm_substream *substream)
40462306a36Sopenharmony_ci{
40562306a36Sopenharmony_ci	struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	atomic_set(&dpcm->running, 0);
40862306a36Sopenharmony_ci	if (!hrtimer_callback_running(&dpcm->timer))
40962306a36Sopenharmony_ci		hrtimer_cancel(&dpcm->timer);
41062306a36Sopenharmony_ci	return 0;
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_cistatic inline void dummy_hrtimer_sync(struct dummy_hrtimer_pcm *dpcm)
41462306a36Sopenharmony_ci{
41562306a36Sopenharmony_ci	hrtimer_cancel(&dpcm->timer);
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_cistatic snd_pcm_uframes_t
41962306a36Sopenharmony_cidummy_hrtimer_pointer(struct snd_pcm_substream *substream)
42062306a36Sopenharmony_ci{
42162306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
42262306a36Sopenharmony_ci	struct dummy_hrtimer_pcm *dpcm = runtime->private_data;
42362306a36Sopenharmony_ci	u64 delta;
42462306a36Sopenharmony_ci	u32 pos;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	delta = ktime_us_delta(hrtimer_cb_get_time(&dpcm->timer),
42762306a36Sopenharmony_ci			       dpcm->base_time);
42862306a36Sopenharmony_ci	delta = div_u64(delta * runtime->rate + 999999, 1000000);
42962306a36Sopenharmony_ci	div_u64_rem(delta, runtime->buffer_size, &pos);
43062306a36Sopenharmony_ci	return pos;
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_cistatic int dummy_hrtimer_prepare(struct snd_pcm_substream *substream)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
43662306a36Sopenharmony_ci	struct dummy_hrtimer_pcm *dpcm = runtime->private_data;
43762306a36Sopenharmony_ci	unsigned int period, rate;
43862306a36Sopenharmony_ci	long sec;
43962306a36Sopenharmony_ci	unsigned long nsecs;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	dummy_hrtimer_sync(dpcm);
44262306a36Sopenharmony_ci	period = runtime->period_size;
44362306a36Sopenharmony_ci	rate = runtime->rate;
44462306a36Sopenharmony_ci	sec = period / rate;
44562306a36Sopenharmony_ci	period %= rate;
44662306a36Sopenharmony_ci	nsecs = div_u64((u64)period * 1000000000UL + rate - 1, rate);
44762306a36Sopenharmony_ci	dpcm->period_time = ktime_set(sec, nsecs);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	return 0;
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_cistatic int dummy_hrtimer_create(struct snd_pcm_substream *substream)
45362306a36Sopenharmony_ci{
45462306a36Sopenharmony_ci	struct dummy_hrtimer_pcm *dpcm;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
45762306a36Sopenharmony_ci	if (!dpcm)
45862306a36Sopenharmony_ci		return -ENOMEM;
45962306a36Sopenharmony_ci	substream->runtime->private_data = dpcm;
46062306a36Sopenharmony_ci	hrtimer_init(&dpcm->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
46162306a36Sopenharmony_ci	dpcm->timer.function = dummy_hrtimer_callback;
46262306a36Sopenharmony_ci	dpcm->substream = substream;
46362306a36Sopenharmony_ci	atomic_set(&dpcm->running, 0);
46462306a36Sopenharmony_ci	return 0;
46562306a36Sopenharmony_ci}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_cistatic void dummy_hrtimer_free(struct snd_pcm_substream *substream)
46862306a36Sopenharmony_ci{
46962306a36Sopenharmony_ci	struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data;
47062306a36Sopenharmony_ci	dummy_hrtimer_sync(dpcm);
47162306a36Sopenharmony_ci	kfree(dpcm);
47262306a36Sopenharmony_ci}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_cistatic const struct dummy_timer_ops dummy_hrtimer_ops = {
47562306a36Sopenharmony_ci	.create =	dummy_hrtimer_create,
47662306a36Sopenharmony_ci	.free =		dummy_hrtimer_free,
47762306a36Sopenharmony_ci	.prepare =	dummy_hrtimer_prepare,
47862306a36Sopenharmony_ci	.start =	dummy_hrtimer_start,
47962306a36Sopenharmony_ci	.stop =		dummy_hrtimer_stop,
48062306a36Sopenharmony_ci	.pointer =	dummy_hrtimer_pointer,
48162306a36Sopenharmony_ci};
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci#endif /* CONFIG_HIGH_RES_TIMERS */
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci/*
48662306a36Sopenharmony_ci * PCM interface
48762306a36Sopenharmony_ci */
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_cistatic int dummy_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
49062306a36Sopenharmony_ci{
49162306a36Sopenharmony_ci	switch (cmd) {
49262306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
49362306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
49462306a36Sopenharmony_ci		return get_dummy_ops(substream)->start(substream);
49562306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
49662306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
49762306a36Sopenharmony_ci		return get_dummy_ops(substream)->stop(substream);
49862306a36Sopenharmony_ci	}
49962306a36Sopenharmony_ci	return -EINVAL;
50062306a36Sopenharmony_ci}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_cistatic int dummy_pcm_prepare(struct snd_pcm_substream *substream)
50362306a36Sopenharmony_ci{
50462306a36Sopenharmony_ci	return get_dummy_ops(substream)->prepare(substream);
50562306a36Sopenharmony_ci}
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_cistatic snd_pcm_uframes_t dummy_pcm_pointer(struct snd_pcm_substream *substream)
50862306a36Sopenharmony_ci{
50962306a36Sopenharmony_ci	return get_dummy_ops(substream)->pointer(substream);
51062306a36Sopenharmony_ci}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_cistatic const struct snd_pcm_hardware dummy_pcm_hardware = {
51362306a36Sopenharmony_ci	.info =			(SNDRV_PCM_INFO_MMAP |
51462306a36Sopenharmony_ci				 SNDRV_PCM_INFO_INTERLEAVED |
51562306a36Sopenharmony_ci				 SNDRV_PCM_INFO_RESUME |
51662306a36Sopenharmony_ci				 SNDRV_PCM_INFO_MMAP_VALID),
51762306a36Sopenharmony_ci	.formats =		USE_FORMATS,
51862306a36Sopenharmony_ci	.rates =		USE_RATE,
51962306a36Sopenharmony_ci	.rate_min =		USE_RATE_MIN,
52062306a36Sopenharmony_ci	.rate_max =		USE_RATE_MAX,
52162306a36Sopenharmony_ci	.channels_min =		USE_CHANNELS_MIN,
52262306a36Sopenharmony_ci	.channels_max =		USE_CHANNELS_MAX,
52362306a36Sopenharmony_ci	.buffer_bytes_max =	MAX_BUFFER_SIZE,
52462306a36Sopenharmony_ci	.period_bytes_min =	MIN_PERIOD_SIZE,
52562306a36Sopenharmony_ci	.period_bytes_max =	MAX_PERIOD_SIZE,
52662306a36Sopenharmony_ci	.periods_min =		USE_PERIODS_MIN,
52762306a36Sopenharmony_ci	.periods_max =		USE_PERIODS_MAX,
52862306a36Sopenharmony_ci	.fifo_size =		0,
52962306a36Sopenharmony_ci};
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_cistatic int dummy_pcm_hw_params(struct snd_pcm_substream *substream,
53262306a36Sopenharmony_ci			       struct snd_pcm_hw_params *hw_params)
53362306a36Sopenharmony_ci{
53462306a36Sopenharmony_ci	if (fake_buffer) {
53562306a36Sopenharmony_ci		/* runtime->dma_bytes has to be set manually to allow mmap */
53662306a36Sopenharmony_ci		substream->runtime->dma_bytes = params_buffer_bytes(hw_params);
53762306a36Sopenharmony_ci		return 0;
53862306a36Sopenharmony_ci	}
53962306a36Sopenharmony_ci	return 0;
54062306a36Sopenharmony_ci}
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_cistatic int dummy_pcm_open(struct snd_pcm_substream *substream)
54362306a36Sopenharmony_ci{
54462306a36Sopenharmony_ci	struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
54562306a36Sopenharmony_ci	const struct dummy_model *model = dummy->model;
54662306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
54762306a36Sopenharmony_ci	const struct dummy_timer_ops *ops;
54862306a36Sopenharmony_ci	int err;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	ops = &dummy_systimer_ops;
55162306a36Sopenharmony_ci#ifdef CONFIG_HIGH_RES_TIMERS
55262306a36Sopenharmony_ci	if (hrtimer)
55362306a36Sopenharmony_ci		ops = &dummy_hrtimer_ops;
55462306a36Sopenharmony_ci#endif
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	err = ops->create(substream);
55762306a36Sopenharmony_ci	if (err < 0)
55862306a36Sopenharmony_ci		return err;
55962306a36Sopenharmony_ci	get_dummy_ops(substream) = ops;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	runtime->hw = dummy->pcm_hw;
56262306a36Sopenharmony_ci	if (substream->pcm->device & 1) {
56362306a36Sopenharmony_ci		runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED;
56462306a36Sopenharmony_ci		runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED;
56562306a36Sopenharmony_ci	}
56662306a36Sopenharmony_ci	if (substream->pcm->device & 2)
56762306a36Sopenharmony_ci		runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP |
56862306a36Sopenharmony_ci				      SNDRV_PCM_INFO_MMAP_VALID);
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	if (model == NULL)
57162306a36Sopenharmony_ci		return 0;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
57462306a36Sopenharmony_ci		if (model->playback_constraints)
57562306a36Sopenharmony_ci			err = model->playback_constraints(substream->runtime);
57662306a36Sopenharmony_ci	} else {
57762306a36Sopenharmony_ci		if (model->capture_constraints)
57862306a36Sopenharmony_ci			err = model->capture_constraints(substream->runtime);
57962306a36Sopenharmony_ci	}
58062306a36Sopenharmony_ci	if (err < 0) {
58162306a36Sopenharmony_ci		get_dummy_ops(substream)->free(substream);
58262306a36Sopenharmony_ci		return err;
58362306a36Sopenharmony_ci	}
58462306a36Sopenharmony_ci	return 0;
58562306a36Sopenharmony_ci}
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_cistatic int dummy_pcm_close(struct snd_pcm_substream *substream)
58862306a36Sopenharmony_ci{
58962306a36Sopenharmony_ci	get_dummy_ops(substream)->free(substream);
59062306a36Sopenharmony_ci	return 0;
59162306a36Sopenharmony_ci}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci/*
59462306a36Sopenharmony_ci * dummy buffer handling
59562306a36Sopenharmony_ci */
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_cistatic void *dummy_page[2];
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_cistatic void free_fake_buffer(void)
60062306a36Sopenharmony_ci{
60162306a36Sopenharmony_ci	if (fake_buffer) {
60262306a36Sopenharmony_ci		int i;
60362306a36Sopenharmony_ci		for (i = 0; i < 2; i++)
60462306a36Sopenharmony_ci			if (dummy_page[i]) {
60562306a36Sopenharmony_ci				free_page((unsigned long)dummy_page[i]);
60662306a36Sopenharmony_ci				dummy_page[i] = NULL;
60762306a36Sopenharmony_ci			}
60862306a36Sopenharmony_ci	}
60962306a36Sopenharmony_ci}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_cistatic int alloc_fake_buffer(void)
61262306a36Sopenharmony_ci{
61362306a36Sopenharmony_ci	int i;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	if (!fake_buffer)
61662306a36Sopenharmony_ci		return 0;
61762306a36Sopenharmony_ci	for (i = 0; i < 2; i++) {
61862306a36Sopenharmony_ci		dummy_page[i] = (void *)get_zeroed_page(GFP_KERNEL);
61962306a36Sopenharmony_ci		if (!dummy_page[i]) {
62062306a36Sopenharmony_ci			free_fake_buffer();
62162306a36Sopenharmony_ci			return -ENOMEM;
62262306a36Sopenharmony_ci		}
62362306a36Sopenharmony_ci	}
62462306a36Sopenharmony_ci	return 0;
62562306a36Sopenharmony_ci}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_cistatic int dummy_pcm_copy(struct snd_pcm_substream *substream,
62862306a36Sopenharmony_ci			  int channel, unsigned long pos,
62962306a36Sopenharmony_ci			  struct iov_iter *iter, unsigned long bytes)
63062306a36Sopenharmony_ci{
63162306a36Sopenharmony_ci	return 0; /* do nothing */
63262306a36Sopenharmony_ci}
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_cistatic int dummy_pcm_silence(struct snd_pcm_substream *substream,
63562306a36Sopenharmony_ci			     int channel, unsigned long pos,
63662306a36Sopenharmony_ci			     unsigned long bytes)
63762306a36Sopenharmony_ci{
63862306a36Sopenharmony_ci	return 0; /* do nothing */
63962306a36Sopenharmony_ci}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_cistatic struct page *dummy_pcm_page(struct snd_pcm_substream *substream,
64262306a36Sopenharmony_ci				   unsigned long offset)
64362306a36Sopenharmony_ci{
64462306a36Sopenharmony_ci	return virt_to_page(dummy_page[substream->stream]); /* the same page */
64562306a36Sopenharmony_ci}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_cistatic const struct snd_pcm_ops dummy_pcm_ops = {
64862306a36Sopenharmony_ci	.open =		dummy_pcm_open,
64962306a36Sopenharmony_ci	.close =	dummy_pcm_close,
65062306a36Sopenharmony_ci	.hw_params =	dummy_pcm_hw_params,
65162306a36Sopenharmony_ci	.prepare =	dummy_pcm_prepare,
65262306a36Sopenharmony_ci	.trigger =	dummy_pcm_trigger,
65362306a36Sopenharmony_ci	.pointer =	dummy_pcm_pointer,
65462306a36Sopenharmony_ci};
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_cistatic const struct snd_pcm_ops dummy_pcm_ops_no_buf = {
65762306a36Sopenharmony_ci	.open =		dummy_pcm_open,
65862306a36Sopenharmony_ci	.close =	dummy_pcm_close,
65962306a36Sopenharmony_ci	.hw_params =	dummy_pcm_hw_params,
66062306a36Sopenharmony_ci	.prepare =	dummy_pcm_prepare,
66162306a36Sopenharmony_ci	.trigger =	dummy_pcm_trigger,
66262306a36Sopenharmony_ci	.pointer =	dummy_pcm_pointer,
66362306a36Sopenharmony_ci	.copy =		dummy_pcm_copy,
66462306a36Sopenharmony_ci	.fill_silence =	dummy_pcm_silence,
66562306a36Sopenharmony_ci	.page =		dummy_pcm_page,
66662306a36Sopenharmony_ci};
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_cistatic int snd_card_dummy_pcm(struct snd_dummy *dummy, int device,
66962306a36Sopenharmony_ci			      int substreams)
67062306a36Sopenharmony_ci{
67162306a36Sopenharmony_ci	struct snd_pcm *pcm;
67262306a36Sopenharmony_ci	const struct snd_pcm_ops *ops;
67362306a36Sopenharmony_ci	int err;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	err = snd_pcm_new(dummy->card, "Dummy PCM", device,
67662306a36Sopenharmony_ci			       substreams, substreams, &pcm);
67762306a36Sopenharmony_ci	if (err < 0)
67862306a36Sopenharmony_ci		return err;
67962306a36Sopenharmony_ci	dummy->pcm = pcm;
68062306a36Sopenharmony_ci	if (fake_buffer)
68162306a36Sopenharmony_ci		ops = &dummy_pcm_ops_no_buf;
68262306a36Sopenharmony_ci	else
68362306a36Sopenharmony_ci		ops = &dummy_pcm_ops;
68462306a36Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, ops);
68562306a36Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, ops);
68662306a36Sopenharmony_ci	pcm->private_data = dummy;
68762306a36Sopenharmony_ci	pcm->info_flags = 0;
68862306a36Sopenharmony_ci	strcpy(pcm->name, "Dummy PCM");
68962306a36Sopenharmony_ci	if (!fake_buffer) {
69062306a36Sopenharmony_ci		snd_pcm_set_managed_buffer_all(pcm,
69162306a36Sopenharmony_ci			SNDRV_DMA_TYPE_CONTINUOUS,
69262306a36Sopenharmony_ci			NULL,
69362306a36Sopenharmony_ci			0, 64*1024);
69462306a36Sopenharmony_ci	}
69562306a36Sopenharmony_ci	return 0;
69662306a36Sopenharmony_ci}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci/*
69962306a36Sopenharmony_ci * mixer interface
70062306a36Sopenharmony_ci */
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci#define DUMMY_VOLUME(xname, xindex, addr) \
70362306a36Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
70462306a36Sopenharmony_ci  .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
70562306a36Sopenharmony_ci  .name = xname, .index = xindex, \
70662306a36Sopenharmony_ci  .info = snd_dummy_volume_info, \
70762306a36Sopenharmony_ci  .get = snd_dummy_volume_get, .put = snd_dummy_volume_put, \
70862306a36Sopenharmony_ci  .private_value = addr, \
70962306a36Sopenharmony_ci  .tlv = { .p = db_scale_dummy } }
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_cistatic int snd_dummy_volume_info(struct snd_kcontrol *kcontrol,
71262306a36Sopenharmony_ci				 struct snd_ctl_elem_info *uinfo)
71362306a36Sopenharmony_ci{
71462306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
71562306a36Sopenharmony_ci	uinfo->count = 2;
71662306a36Sopenharmony_ci	uinfo->value.integer.min = mixer_volume_level_min;
71762306a36Sopenharmony_ci	uinfo->value.integer.max = mixer_volume_level_max;
71862306a36Sopenharmony_ci	return 0;
71962306a36Sopenharmony_ci}
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_cistatic int snd_dummy_volume_get(struct snd_kcontrol *kcontrol,
72262306a36Sopenharmony_ci				struct snd_ctl_elem_value *ucontrol)
72362306a36Sopenharmony_ci{
72462306a36Sopenharmony_ci	struct snd_dummy *dummy = snd_kcontrol_chip(kcontrol);
72562306a36Sopenharmony_ci	int addr = kcontrol->private_value;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	spin_lock_irq(&dummy->mixer_lock);
72862306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = dummy->mixer_volume[addr][0];
72962306a36Sopenharmony_ci	ucontrol->value.integer.value[1] = dummy->mixer_volume[addr][1];
73062306a36Sopenharmony_ci	spin_unlock_irq(&dummy->mixer_lock);
73162306a36Sopenharmony_ci	return 0;
73262306a36Sopenharmony_ci}
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_cistatic int snd_dummy_volume_put(struct snd_kcontrol *kcontrol,
73562306a36Sopenharmony_ci				struct snd_ctl_elem_value *ucontrol)
73662306a36Sopenharmony_ci{
73762306a36Sopenharmony_ci	struct snd_dummy *dummy = snd_kcontrol_chip(kcontrol);
73862306a36Sopenharmony_ci	int change, addr = kcontrol->private_value;
73962306a36Sopenharmony_ci	int left, right;
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	left = ucontrol->value.integer.value[0];
74262306a36Sopenharmony_ci	if (left < mixer_volume_level_min)
74362306a36Sopenharmony_ci		left = mixer_volume_level_min;
74462306a36Sopenharmony_ci	if (left > mixer_volume_level_max)
74562306a36Sopenharmony_ci		left = mixer_volume_level_max;
74662306a36Sopenharmony_ci	right = ucontrol->value.integer.value[1];
74762306a36Sopenharmony_ci	if (right < mixer_volume_level_min)
74862306a36Sopenharmony_ci		right = mixer_volume_level_min;
74962306a36Sopenharmony_ci	if (right > mixer_volume_level_max)
75062306a36Sopenharmony_ci		right = mixer_volume_level_max;
75162306a36Sopenharmony_ci	spin_lock_irq(&dummy->mixer_lock);
75262306a36Sopenharmony_ci	change = dummy->mixer_volume[addr][0] != left ||
75362306a36Sopenharmony_ci	         dummy->mixer_volume[addr][1] != right;
75462306a36Sopenharmony_ci	dummy->mixer_volume[addr][0] = left;
75562306a36Sopenharmony_ci	dummy->mixer_volume[addr][1] = right;
75662306a36Sopenharmony_ci	spin_unlock_irq(&dummy->mixer_lock);
75762306a36Sopenharmony_ci	return change;
75862306a36Sopenharmony_ci}
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_dummy, -4500, 30, 0);
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci#define DUMMY_CAPSRC(xname, xindex, addr) \
76362306a36Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
76462306a36Sopenharmony_ci  .info = snd_dummy_capsrc_info, \
76562306a36Sopenharmony_ci  .get = snd_dummy_capsrc_get, .put = snd_dummy_capsrc_put, \
76662306a36Sopenharmony_ci  .private_value = addr }
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci#define snd_dummy_capsrc_info	snd_ctl_boolean_stereo_info
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_cistatic int snd_dummy_capsrc_get(struct snd_kcontrol *kcontrol,
77162306a36Sopenharmony_ci				struct snd_ctl_elem_value *ucontrol)
77262306a36Sopenharmony_ci{
77362306a36Sopenharmony_ci	struct snd_dummy *dummy = snd_kcontrol_chip(kcontrol);
77462306a36Sopenharmony_ci	int addr = kcontrol->private_value;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	spin_lock_irq(&dummy->mixer_lock);
77762306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = dummy->capture_source[addr][0];
77862306a36Sopenharmony_ci	ucontrol->value.integer.value[1] = dummy->capture_source[addr][1];
77962306a36Sopenharmony_ci	spin_unlock_irq(&dummy->mixer_lock);
78062306a36Sopenharmony_ci	return 0;
78162306a36Sopenharmony_ci}
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_cistatic int snd_dummy_capsrc_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
78462306a36Sopenharmony_ci{
78562306a36Sopenharmony_ci	struct snd_dummy *dummy = snd_kcontrol_chip(kcontrol);
78662306a36Sopenharmony_ci	int change, addr = kcontrol->private_value;
78762306a36Sopenharmony_ci	int left, right;
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	left = ucontrol->value.integer.value[0] & 1;
79062306a36Sopenharmony_ci	right = ucontrol->value.integer.value[1] & 1;
79162306a36Sopenharmony_ci	spin_lock_irq(&dummy->mixer_lock);
79262306a36Sopenharmony_ci	change = dummy->capture_source[addr][0] != left &&
79362306a36Sopenharmony_ci	         dummy->capture_source[addr][1] != right;
79462306a36Sopenharmony_ci	dummy->capture_source[addr][0] = left;
79562306a36Sopenharmony_ci	dummy->capture_source[addr][1] = right;
79662306a36Sopenharmony_ci	spin_unlock_irq(&dummy->mixer_lock);
79762306a36Sopenharmony_ci	return change;
79862306a36Sopenharmony_ci}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_cistatic int snd_dummy_iobox_info(struct snd_kcontrol *kcontrol,
80162306a36Sopenharmony_ci				struct snd_ctl_elem_info *info)
80262306a36Sopenharmony_ci{
80362306a36Sopenharmony_ci	static const char *const names[] = { "None", "CD Player" };
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	return snd_ctl_enum_info(info, 1, 2, names);
80662306a36Sopenharmony_ci}
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_cistatic int snd_dummy_iobox_get(struct snd_kcontrol *kcontrol,
80962306a36Sopenharmony_ci			       struct snd_ctl_elem_value *value)
81062306a36Sopenharmony_ci{
81162306a36Sopenharmony_ci	struct snd_dummy *dummy = snd_kcontrol_chip(kcontrol);
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	value->value.enumerated.item[0] = dummy->iobox;
81462306a36Sopenharmony_ci	return 0;
81562306a36Sopenharmony_ci}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_cistatic int snd_dummy_iobox_put(struct snd_kcontrol *kcontrol,
81862306a36Sopenharmony_ci			       struct snd_ctl_elem_value *value)
81962306a36Sopenharmony_ci{
82062306a36Sopenharmony_ci	struct snd_dummy *dummy = snd_kcontrol_chip(kcontrol);
82162306a36Sopenharmony_ci	int changed;
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	if (value->value.enumerated.item[0] > 1)
82462306a36Sopenharmony_ci		return -EINVAL;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	changed = value->value.enumerated.item[0] != dummy->iobox;
82762306a36Sopenharmony_ci	if (changed) {
82862306a36Sopenharmony_ci		dummy->iobox = value->value.enumerated.item[0];
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci		if (dummy->iobox) {
83162306a36Sopenharmony_ci			dummy->cd_volume_ctl->vd[0].access &=
83262306a36Sopenharmony_ci				~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
83362306a36Sopenharmony_ci			dummy->cd_switch_ctl->vd[0].access &=
83462306a36Sopenharmony_ci				~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
83562306a36Sopenharmony_ci		} else {
83662306a36Sopenharmony_ci			dummy->cd_volume_ctl->vd[0].access |=
83762306a36Sopenharmony_ci				SNDRV_CTL_ELEM_ACCESS_INACTIVE;
83862306a36Sopenharmony_ci			dummy->cd_switch_ctl->vd[0].access |=
83962306a36Sopenharmony_ci				SNDRV_CTL_ELEM_ACCESS_INACTIVE;
84062306a36Sopenharmony_ci		}
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci		snd_ctl_notify(dummy->card, SNDRV_CTL_EVENT_MASK_INFO,
84362306a36Sopenharmony_ci			       &dummy->cd_volume_ctl->id);
84462306a36Sopenharmony_ci		snd_ctl_notify(dummy->card, SNDRV_CTL_EVENT_MASK_INFO,
84562306a36Sopenharmony_ci			       &dummy->cd_switch_ctl->id);
84662306a36Sopenharmony_ci	}
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	return changed;
84962306a36Sopenharmony_ci}
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_dummy_controls[] = {
85262306a36Sopenharmony_ciDUMMY_VOLUME("Master Volume", 0, MIXER_ADDR_MASTER),
85362306a36Sopenharmony_ciDUMMY_CAPSRC("Master Capture Switch", 0, MIXER_ADDR_MASTER),
85462306a36Sopenharmony_ciDUMMY_VOLUME("Synth Volume", 0, MIXER_ADDR_SYNTH),
85562306a36Sopenharmony_ciDUMMY_CAPSRC("Synth Capture Switch", 0, MIXER_ADDR_SYNTH),
85662306a36Sopenharmony_ciDUMMY_VOLUME("Line Volume", 0, MIXER_ADDR_LINE),
85762306a36Sopenharmony_ciDUMMY_CAPSRC("Line Capture Switch", 0, MIXER_ADDR_LINE),
85862306a36Sopenharmony_ciDUMMY_VOLUME("Mic Volume", 0, MIXER_ADDR_MIC),
85962306a36Sopenharmony_ciDUMMY_CAPSRC("Mic Capture Switch", 0, MIXER_ADDR_MIC),
86062306a36Sopenharmony_ciDUMMY_VOLUME("CD Volume", 0, MIXER_ADDR_CD),
86162306a36Sopenharmony_ciDUMMY_CAPSRC("CD Capture Switch", 0, MIXER_ADDR_CD),
86262306a36Sopenharmony_ci{
86362306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
86462306a36Sopenharmony_ci	.name  = "External I/O Box",
86562306a36Sopenharmony_ci	.info  = snd_dummy_iobox_info,
86662306a36Sopenharmony_ci	.get   = snd_dummy_iobox_get,
86762306a36Sopenharmony_ci	.put   = snd_dummy_iobox_put,
86862306a36Sopenharmony_ci},
86962306a36Sopenharmony_ci};
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_cistatic int snd_card_dummy_new_mixer(struct snd_dummy *dummy)
87262306a36Sopenharmony_ci{
87362306a36Sopenharmony_ci	struct snd_card *card = dummy->card;
87462306a36Sopenharmony_ci	struct snd_kcontrol *kcontrol;
87562306a36Sopenharmony_ci	unsigned int idx;
87662306a36Sopenharmony_ci	int err;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	spin_lock_init(&dummy->mixer_lock);
87962306a36Sopenharmony_ci	strcpy(card->mixername, "Dummy Mixer");
88062306a36Sopenharmony_ci	dummy->iobox = 1;
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	for (idx = 0; idx < ARRAY_SIZE(snd_dummy_controls); idx++) {
88362306a36Sopenharmony_ci		kcontrol = snd_ctl_new1(&snd_dummy_controls[idx], dummy);
88462306a36Sopenharmony_ci		err = snd_ctl_add(card, kcontrol);
88562306a36Sopenharmony_ci		if (err < 0)
88662306a36Sopenharmony_ci			return err;
88762306a36Sopenharmony_ci		if (!strcmp(kcontrol->id.name, "CD Volume"))
88862306a36Sopenharmony_ci			dummy->cd_volume_ctl = kcontrol;
88962306a36Sopenharmony_ci		else if (!strcmp(kcontrol->id.name, "CD Capture Switch"))
89062306a36Sopenharmony_ci			dummy->cd_switch_ctl = kcontrol;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	}
89362306a36Sopenharmony_ci	return 0;
89462306a36Sopenharmony_ci}
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_SND_PROC_FS)
89762306a36Sopenharmony_ci/*
89862306a36Sopenharmony_ci * proc interface
89962306a36Sopenharmony_ci */
90062306a36Sopenharmony_cistatic void print_formats(struct snd_dummy *dummy,
90162306a36Sopenharmony_ci			  struct snd_info_buffer *buffer)
90262306a36Sopenharmony_ci{
90362306a36Sopenharmony_ci	snd_pcm_format_t i;
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	pcm_for_each_format(i) {
90662306a36Sopenharmony_ci		if (dummy->pcm_hw.formats & pcm_format_to_bits(i))
90762306a36Sopenharmony_ci			snd_iprintf(buffer, " %s", snd_pcm_format_name(i));
90862306a36Sopenharmony_ci	}
90962306a36Sopenharmony_ci}
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_cistatic void print_rates(struct snd_dummy *dummy,
91262306a36Sopenharmony_ci			struct snd_info_buffer *buffer)
91362306a36Sopenharmony_ci{
91462306a36Sopenharmony_ci	static const int rates[] = {
91562306a36Sopenharmony_ci		5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000,
91662306a36Sopenharmony_ci		64000, 88200, 96000, 176400, 192000,
91762306a36Sopenharmony_ci	};
91862306a36Sopenharmony_ci	int i;
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	if (dummy->pcm_hw.rates & SNDRV_PCM_RATE_CONTINUOUS)
92162306a36Sopenharmony_ci		snd_iprintf(buffer, " continuous");
92262306a36Sopenharmony_ci	if (dummy->pcm_hw.rates & SNDRV_PCM_RATE_KNOT)
92362306a36Sopenharmony_ci		snd_iprintf(buffer, " knot");
92462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(rates); i++)
92562306a36Sopenharmony_ci		if (dummy->pcm_hw.rates & (1 << i))
92662306a36Sopenharmony_ci			snd_iprintf(buffer, " %d", rates[i]);
92762306a36Sopenharmony_ci}
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci#define get_dummy_int_ptr(dummy, ofs) \
93062306a36Sopenharmony_ci	(unsigned int *)((char *)&((dummy)->pcm_hw) + (ofs))
93162306a36Sopenharmony_ci#define get_dummy_ll_ptr(dummy, ofs) \
93262306a36Sopenharmony_ci	(unsigned long long *)((char *)&((dummy)->pcm_hw) + (ofs))
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_cistruct dummy_hw_field {
93562306a36Sopenharmony_ci	const char *name;
93662306a36Sopenharmony_ci	const char *format;
93762306a36Sopenharmony_ci	unsigned int offset;
93862306a36Sopenharmony_ci	unsigned int size;
93962306a36Sopenharmony_ci};
94062306a36Sopenharmony_ci#define FIELD_ENTRY(item, fmt) {		   \
94162306a36Sopenharmony_ci	.name = #item,				   \
94262306a36Sopenharmony_ci	.format = fmt,				   \
94362306a36Sopenharmony_ci	.offset = offsetof(struct snd_pcm_hardware, item), \
94462306a36Sopenharmony_ci	.size = sizeof(dummy_pcm_hardware.item) }
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_cistatic const struct dummy_hw_field fields[] = {
94762306a36Sopenharmony_ci	FIELD_ENTRY(formats, "%#llx"),
94862306a36Sopenharmony_ci	FIELD_ENTRY(rates, "%#x"),
94962306a36Sopenharmony_ci	FIELD_ENTRY(rate_min, "%d"),
95062306a36Sopenharmony_ci	FIELD_ENTRY(rate_max, "%d"),
95162306a36Sopenharmony_ci	FIELD_ENTRY(channels_min, "%d"),
95262306a36Sopenharmony_ci	FIELD_ENTRY(channels_max, "%d"),
95362306a36Sopenharmony_ci	FIELD_ENTRY(buffer_bytes_max, "%ld"),
95462306a36Sopenharmony_ci	FIELD_ENTRY(period_bytes_min, "%ld"),
95562306a36Sopenharmony_ci	FIELD_ENTRY(period_bytes_max, "%ld"),
95662306a36Sopenharmony_ci	FIELD_ENTRY(periods_min, "%d"),
95762306a36Sopenharmony_ci	FIELD_ENTRY(periods_max, "%d"),
95862306a36Sopenharmony_ci};
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_cistatic void dummy_proc_read(struct snd_info_entry *entry,
96162306a36Sopenharmony_ci			    struct snd_info_buffer *buffer)
96262306a36Sopenharmony_ci{
96362306a36Sopenharmony_ci	struct snd_dummy *dummy = entry->private_data;
96462306a36Sopenharmony_ci	int i;
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(fields); i++) {
96762306a36Sopenharmony_ci		snd_iprintf(buffer, "%s ", fields[i].name);
96862306a36Sopenharmony_ci		if (fields[i].size == sizeof(int))
96962306a36Sopenharmony_ci			snd_iprintf(buffer, fields[i].format,
97062306a36Sopenharmony_ci				*get_dummy_int_ptr(dummy, fields[i].offset));
97162306a36Sopenharmony_ci		else
97262306a36Sopenharmony_ci			snd_iprintf(buffer, fields[i].format,
97362306a36Sopenharmony_ci				*get_dummy_ll_ptr(dummy, fields[i].offset));
97462306a36Sopenharmony_ci		if (!strcmp(fields[i].name, "formats"))
97562306a36Sopenharmony_ci			print_formats(dummy, buffer);
97662306a36Sopenharmony_ci		else if (!strcmp(fields[i].name, "rates"))
97762306a36Sopenharmony_ci			print_rates(dummy, buffer);
97862306a36Sopenharmony_ci		snd_iprintf(buffer, "\n");
97962306a36Sopenharmony_ci	}
98062306a36Sopenharmony_ci}
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_cistatic void dummy_proc_write(struct snd_info_entry *entry,
98362306a36Sopenharmony_ci			     struct snd_info_buffer *buffer)
98462306a36Sopenharmony_ci{
98562306a36Sopenharmony_ci	struct snd_dummy *dummy = entry->private_data;
98662306a36Sopenharmony_ci	char line[64];
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	while (!snd_info_get_line(buffer, line, sizeof(line))) {
98962306a36Sopenharmony_ci		char item[20];
99062306a36Sopenharmony_ci		const char *ptr;
99162306a36Sopenharmony_ci		unsigned long long val;
99262306a36Sopenharmony_ci		int i;
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci		ptr = snd_info_get_str(item, line, sizeof(item));
99562306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(fields); i++) {
99662306a36Sopenharmony_ci			if (!strcmp(item, fields[i].name))
99762306a36Sopenharmony_ci				break;
99862306a36Sopenharmony_ci		}
99962306a36Sopenharmony_ci		if (i >= ARRAY_SIZE(fields))
100062306a36Sopenharmony_ci			continue;
100162306a36Sopenharmony_ci		snd_info_get_str(item, ptr, sizeof(item));
100262306a36Sopenharmony_ci		if (kstrtoull(item, 0, &val))
100362306a36Sopenharmony_ci			continue;
100462306a36Sopenharmony_ci		if (fields[i].size == sizeof(int))
100562306a36Sopenharmony_ci			*get_dummy_int_ptr(dummy, fields[i].offset) = val;
100662306a36Sopenharmony_ci		else
100762306a36Sopenharmony_ci			*get_dummy_ll_ptr(dummy, fields[i].offset) = val;
100862306a36Sopenharmony_ci	}
100962306a36Sopenharmony_ci}
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_cistatic void dummy_proc_init(struct snd_dummy *chip)
101262306a36Sopenharmony_ci{
101362306a36Sopenharmony_ci	snd_card_rw_proc_new(chip->card, "dummy_pcm", chip,
101462306a36Sopenharmony_ci			     dummy_proc_read, dummy_proc_write);
101562306a36Sopenharmony_ci}
101662306a36Sopenharmony_ci#else
101762306a36Sopenharmony_ci#define dummy_proc_init(x)
101862306a36Sopenharmony_ci#endif /* CONFIG_SND_DEBUG && CONFIG_SND_PROC_FS */
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_cistatic int snd_dummy_probe(struct platform_device *devptr)
102162306a36Sopenharmony_ci{
102262306a36Sopenharmony_ci	struct snd_card *card;
102362306a36Sopenharmony_ci	struct snd_dummy *dummy;
102462306a36Sopenharmony_ci	const struct dummy_model *m = NULL, **mdl;
102562306a36Sopenharmony_ci	int idx, err;
102662306a36Sopenharmony_ci	int dev = devptr->id;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	err = snd_devm_card_new(&devptr->dev, index[dev], id[dev], THIS_MODULE,
102962306a36Sopenharmony_ci				sizeof(struct snd_dummy), &card);
103062306a36Sopenharmony_ci	if (err < 0)
103162306a36Sopenharmony_ci		return err;
103262306a36Sopenharmony_ci	dummy = card->private_data;
103362306a36Sopenharmony_ci	dummy->card = card;
103462306a36Sopenharmony_ci	for (mdl = dummy_models; *mdl && model[dev]; mdl++) {
103562306a36Sopenharmony_ci		if (strcmp(model[dev], (*mdl)->name) == 0) {
103662306a36Sopenharmony_ci			printk(KERN_INFO
103762306a36Sopenharmony_ci				"snd-dummy: Using model '%s' for card %i\n",
103862306a36Sopenharmony_ci				(*mdl)->name, card->number);
103962306a36Sopenharmony_ci			m = dummy->model = *mdl;
104062306a36Sopenharmony_ci			break;
104162306a36Sopenharmony_ci		}
104262306a36Sopenharmony_ci	}
104362306a36Sopenharmony_ci	for (idx = 0; idx < MAX_PCM_DEVICES && idx < pcm_devs[dev]; idx++) {
104462306a36Sopenharmony_ci		if (pcm_substreams[dev] < 1)
104562306a36Sopenharmony_ci			pcm_substreams[dev] = 1;
104662306a36Sopenharmony_ci		if (pcm_substreams[dev] > MAX_PCM_SUBSTREAMS)
104762306a36Sopenharmony_ci			pcm_substreams[dev] = MAX_PCM_SUBSTREAMS;
104862306a36Sopenharmony_ci		err = snd_card_dummy_pcm(dummy, idx, pcm_substreams[dev]);
104962306a36Sopenharmony_ci		if (err < 0)
105062306a36Sopenharmony_ci			return err;
105162306a36Sopenharmony_ci	}
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	dummy->pcm_hw = dummy_pcm_hardware;
105462306a36Sopenharmony_ci	if (m) {
105562306a36Sopenharmony_ci		if (m->formats)
105662306a36Sopenharmony_ci			dummy->pcm_hw.formats = m->formats;
105762306a36Sopenharmony_ci		if (m->buffer_bytes_max)
105862306a36Sopenharmony_ci			dummy->pcm_hw.buffer_bytes_max = m->buffer_bytes_max;
105962306a36Sopenharmony_ci		if (m->period_bytes_min)
106062306a36Sopenharmony_ci			dummy->pcm_hw.period_bytes_min = m->period_bytes_min;
106162306a36Sopenharmony_ci		if (m->period_bytes_max)
106262306a36Sopenharmony_ci			dummy->pcm_hw.period_bytes_max = m->period_bytes_max;
106362306a36Sopenharmony_ci		if (m->periods_min)
106462306a36Sopenharmony_ci			dummy->pcm_hw.periods_min = m->periods_min;
106562306a36Sopenharmony_ci		if (m->periods_max)
106662306a36Sopenharmony_ci			dummy->pcm_hw.periods_max = m->periods_max;
106762306a36Sopenharmony_ci		if (m->rates)
106862306a36Sopenharmony_ci			dummy->pcm_hw.rates = m->rates;
106962306a36Sopenharmony_ci		if (m->rate_min)
107062306a36Sopenharmony_ci			dummy->pcm_hw.rate_min = m->rate_min;
107162306a36Sopenharmony_ci		if (m->rate_max)
107262306a36Sopenharmony_ci			dummy->pcm_hw.rate_max = m->rate_max;
107362306a36Sopenharmony_ci		if (m->channels_min)
107462306a36Sopenharmony_ci			dummy->pcm_hw.channels_min = m->channels_min;
107562306a36Sopenharmony_ci		if (m->channels_max)
107662306a36Sopenharmony_ci			dummy->pcm_hw.channels_max = m->channels_max;
107762306a36Sopenharmony_ci	}
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	if (mixer_volume_level_min > mixer_volume_level_max) {
108062306a36Sopenharmony_ci		pr_warn("snd-dummy: Invalid mixer volume level: min=%d, max=%d. Fall back to default value.\n",
108162306a36Sopenharmony_ci		mixer_volume_level_min, mixer_volume_level_max);
108262306a36Sopenharmony_ci		mixer_volume_level_min = USE_MIXER_VOLUME_LEVEL_MIN;
108362306a36Sopenharmony_ci		mixer_volume_level_max = USE_MIXER_VOLUME_LEVEL_MAX;
108462306a36Sopenharmony_ci	}
108562306a36Sopenharmony_ci	err = snd_card_dummy_new_mixer(dummy);
108662306a36Sopenharmony_ci	if (err < 0)
108762306a36Sopenharmony_ci		return err;
108862306a36Sopenharmony_ci	strcpy(card->driver, "Dummy");
108962306a36Sopenharmony_ci	strcpy(card->shortname, "Dummy");
109062306a36Sopenharmony_ci	sprintf(card->longname, "Dummy %i", dev + 1);
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	dummy_proc_init(dummy);
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	err = snd_card_register(card);
109562306a36Sopenharmony_ci	if (err < 0)
109662306a36Sopenharmony_ci		return err;
109762306a36Sopenharmony_ci	platform_set_drvdata(devptr, card);
109862306a36Sopenharmony_ci	return 0;
109962306a36Sopenharmony_ci}
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
110262306a36Sopenharmony_cistatic int snd_dummy_suspend(struct device *pdev)
110362306a36Sopenharmony_ci{
110462306a36Sopenharmony_ci	struct snd_card *card = dev_get_drvdata(pdev);
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
110762306a36Sopenharmony_ci	return 0;
110862306a36Sopenharmony_ci}
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_cistatic int snd_dummy_resume(struct device *pdev)
111162306a36Sopenharmony_ci{
111262306a36Sopenharmony_ci	struct snd_card *card = dev_get_drvdata(pdev);
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
111562306a36Sopenharmony_ci	return 0;
111662306a36Sopenharmony_ci}
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(snd_dummy_pm, snd_dummy_suspend, snd_dummy_resume);
111962306a36Sopenharmony_ci#define SND_DUMMY_PM_OPS	&snd_dummy_pm
112062306a36Sopenharmony_ci#else
112162306a36Sopenharmony_ci#define SND_DUMMY_PM_OPS	NULL
112262306a36Sopenharmony_ci#endif
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci#define SND_DUMMY_DRIVER	"snd_dummy"
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_cistatic struct platform_driver snd_dummy_driver = {
112762306a36Sopenharmony_ci	.probe		= snd_dummy_probe,
112862306a36Sopenharmony_ci	.driver		= {
112962306a36Sopenharmony_ci		.name	= SND_DUMMY_DRIVER,
113062306a36Sopenharmony_ci		.pm	= SND_DUMMY_PM_OPS,
113162306a36Sopenharmony_ci	},
113262306a36Sopenharmony_ci};
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_cistatic void snd_dummy_unregister_all(void)
113562306a36Sopenharmony_ci{
113662306a36Sopenharmony_ci	int i;
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(devices); ++i)
113962306a36Sopenharmony_ci		platform_device_unregister(devices[i]);
114062306a36Sopenharmony_ci	platform_driver_unregister(&snd_dummy_driver);
114162306a36Sopenharmony_ci	free_fake_buffer();
114262306a36Sopenharmony_ci}
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_cistatic int __init alsa_card_dummy_init(void)
114562306a36Sopenharmony_ci{
114662306a36Sopenharmony_ci	int i, cards, err;
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	err = platform_driver_register(&snd_dummy_driver);
114962306a36Sopenharmony_ci	if (err < 0)
115062306a36Sopenharmony_ci		return err;
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	err = alloc_fake_buffer();
115362306a36Sopenharmony_ci	if (err < 0) {
115462306a36Sopenharmony_ci		platform_driver_unregister(&snd_dummy_driver);
115562306a36Sopenharmony_ci		return err;
115662306a36Sopenharmony_ci	}
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	cards = 0;
115962306a36Sopenharmony_ci	for (i = 0; i < SNDRV_CARDS; i++) {
116062306a36Sopenharmony_ci		struct platform_device *device;
116162306a36Sopenharmony_ci		if (! enable[i])
116262306a36Sopenharmony_ci			continue;
116362306a36Sopenharmony_ci		device = platform_device_register_simple(SND_DUMMY_DRIVER,
116462306a36Sopenharmony_ci							 i, NULL, 0);
116562306a36Sopenharmony_ci		if (IS_ERR(device))
116662306a36Sopenharmony_ci			continue;
116762306a36Sopenharmony_ci		if (!platform_get_drvdata(device)) {
116862306a36Sopenharmony_ci			platform_device_unregister(device);
116962306a36Sopenharmony_ci			continue;
117062306a36Sopenharmony_ci		}
117162306a36Sopenharmony_ci		devices[i] = device;
117262306a36Sopenharmony_ci		cards++;
117362306a36Sopenharmony_ci	}
117462306a36Sopenharmony_ci	if (!cards) {
117562306a36Sopenharmony_ci#ifdef MODULE
117662306a36Sopenharmony_ci		printk(KERN_ERR "Dummy soundcard not found or device busy\n");
117762306a36Sopenharmony_ci#endif
117862306a36Sopenharmony_ci		snd_dummy_unregister_all();
117962306a36Sopenharmony_ci		return -ENODEV;
118062306a36Sopenharmony_ci	}
118162306a36Sopenharmony_ci	return 0;
118262306a36Sopenharmony_ci}
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_cistatic void __exit alsa_card_dummy_exit(void)
118562306a36Sopenharmony_ci{
118662306a36Sopenharmony_ci	snd_dummy_unregister_all();
118762306a36Sopenharmony_ci}
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_cimodule_init(alsa_card_dummy_init)
119062306a36Sopenharmony_cimodule_exit(alsa_card_dummy_exit)
1191