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