162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Loopback soundcard 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Original code: 662306a36Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * More accurate positioning and full-duplex support: 962306a36Sopenharmony_ci * Copyright (c) Ahmet İnan <ainan at mathematik.uni-freiburg.de> 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Major (almost complete) rewrite: 1262306a36Sopenharmony_ci * Copyright (c) by Takashi Iwai <tiwai@suse.de> 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * A next major update in 2010 (separate timers for playback and capture): 1562306a36Sopenharmony_ci * Copyright (c) Jaroslav Kysela <perex@perex.cz> 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/init.h> 1962306a36Sopenharmony_ci#include <linux/jiffies.h> 2062306a36Sopenharmony_ci#include <linux/slab.h> 2162306a36Sopenharmony_ci#include <linux/time.h> 2262306a36Sopenharmony_ci#include <linux/wait.h> 2362306a36Sopenharmony_ci#include <linux/module.h> 2462306a36Sopenharmony_ci#include <linux/platform_device.h> 2562306a36Sopenharmony_ci#include <sound/core.h> 2662306a36Sopenharmony_ci#include <sound/control.h> 2762306a36Sopenharmony_ci#include <sound/pcm.h> 2862306a36Sopenharmony_ci#include <sound/pcm_params.h> 2962306a36Sopenharmony_ci#include <sound/info.h> 3062306a36Sopenharmony_ci#include <sound/initval.h> 3162306a36Sopenharmony_ci#include <sound/timer.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); 3462306a36Sopenharmony_ciMODULE_DESCRIPTION("A loopback soundcard"); 3562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define MAX_PCM_SUBSTREAMS 8 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ 4062306a36Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ 4162306a36Sopenharmony_cistatic bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0}; 4262306a36Sopenharmony_cistatic int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8}; 4362306a36Sopenharmony_cistatic int pcm_notify[SNDRV_CARDS]; 4462306a36Sopenharmony_cistatic char *timer_source[SNDRV_CARDS]; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cimodule_param_array(index, int, NULL, 0444); 4762306a36Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for loopback soundcard."); 4862306a36Sopenharmony_cimodule_param_array(id, charp, NULL, 0444); 4962306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for loopback soundcard."); 5062306a36Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444); 5162306a36Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable this loopback soundcard."); 5262306a36Sopenharmony_cimodule_param_array(pcm_substreams, int, NULL, 0444); 5362306a36Sopenharmony_ciMODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-8) for loopback driver."); 5462306a36Sopenharmony_cimodule_param_array(pcm_notify, int, NULL, 0444); 5562306a36Sopenharmony_ciMODULE_PARM_DESC(pcm_notify, "Break capture when PCM format/rate/channels changes."); 5662306a36Sopenharmony_cimodule_param_array(timer_source, charp, NULL, 0444); 5762306a36Sopenharmony_ciMODULE_PARM_DESC(timer_source, "Sound card name or number and device/subdevice number of timer to be used. Empty string for jiffies timer [default]."); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#define NO_PITCH 100000 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#define CABLE_VALID_PLAYBACK BIT(SNDRV_PCM_STREAM_PLAYBACK) 6262306a36Sopenharmony_ci#define CABLE_VALID_CAPTURE BIT(SNDRV_PCM_STREAM_CAPTURE) 6362306a36Sopenharmony_ci#define CABLE_VALID_BOTH (CABLE_VALID_PLAYBACK | CABLE_VALID_CAPTURE) 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistruct loopback_cable; 6662306a36Sopenharmony_cistruct loopback_pcm; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistruct loopback_ops { 6962306a36Sopenharmony_ci /* optional 7062306a36Sopenharmony_ci * call in loopback->cable_lock 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_ci int (*open)(struct loopback_pcm *dpcm); 7362306a36Sopenharmony_ci /* required 7462306a36Sopenharmony_ci * call in cable->lock 7562306a36Sopenharmony_ci */ 7662306a36Sopenharmony_ci int (*start)(struct loopback_pcm *dpcm); 7762306a36Sopenharmony_ci /* required 7862306a36Sopenharmony_ci * call in cable->lock 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_ci int (*stop)(struct loopback_pcm *dpcm); 8162306a36Sopenharmony_ci /* optional */ 8262306a36Sopenharmony_ci int (*stop_sync)(struct loopback_pcm *dpcm); 8362306a36Sopenharmony_ci /* optional */ 8462306a36Sopenharmony_ci int (*close_substream)(struct loopback_pcm *dpcm); 8562306a36Sopenharmony_ci /* optional 8662306a36Sopenharmony_ci * call in loopback->cable_lock 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_ci int (*close_cable)(struct loopback_pcm *dpcm); 8962306a36Sopenharmony_ci /* optional 9062306a36Sopenharmony_ci * call in cable->lock 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_ci unsigned int (*pos_update)(struct loopback_cable *cable); 9362306a36Sopenharmony_ci /* optional */ 9462306a36Sopenharmony_ci void (*dpcm_info)(struct loopback_pcm *dpcm, 9562306a36Sopenharmony_ci struct snd_info_buffer *buffer); 9662306a36Sopenharmony_ci}; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistruct loopback_cable { 9962306a36Sopenharmony_ci spinlock_t lock; 10062306a36Sopenharmony_ci struct loopback_pcm *streams[2]; 10162306a36Sopenharmony_ci struct snd_pcm_hardware hw; 10262306a36Sopenharmony_ci /* flags */ 10362306a36Sopenharmony_ci unsigned int valid; 10462306a36Sopenharmony_ci unsigned int running; 10562306a36Sopenharmony_ci unsigned int pause; 10662306a36Sopenharmony_ci /* timer specific */ 10762306a36Sopenharmony_ci const struct loopback_ops *ops; 10862306a36Sopenharmony_ci /* If sound timer is used */ 10962306a36Sopenharmony_ci struct { 11062306a36Sopenharmony_ci int stream; 11162306a36Sopenharmony_ci struct snd_timer_id id; 11262306a36Sopenharmony_ci struct work_struct event_work; 11362306a36Sopenharmony_ci struct snd_timer_instance *instance; 11462306a36Sopenharmony_ci } snd_timer; 11562306a36Sopenharmony_ci}; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistruct loopback_setup { 11862306a36Sopenharmony_ci unsigned int notify: 1; 11962306a36Sopenharmony_ci unsigned int rate_shift; 12062306a36Sopenharmony_ci snd_pcm_format_t format; 12162306a36Sopenharmony_ci unsigned int rate; 12262306a36Sopenharmony_ci unsigned int channels; 12362306a36Sopenharmony_ci struct snd_ctl_elem_id active_id; 12462306a36Sopenharmony_ci struct snd_ctl_elem_id format_id; 12562306a36Sopenharmony_ci struct snd_ctl_elem_id rate_id; 12662306a36Sopenharmony_ci struct snd_ctl_elem_id channels_id; 12762306a36Sopenharmony_ci}; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistruct loopback { 13062306a36Sopenharmony_ci struct snd_card *card; 13162306a36Sopenharmony_ci struct mutex cable_lock; 13262306a36Sopenharmony_ci struct loopback_cable *cables[MAX_PCM_SUBSTREAMS][2]; 13362306a36Sopenharmony_ci struct snd_pcm *pcm[2]; 13462306a36Sopenharmony_ci struct loopback_setup setup[MAX_PCM_SUBSTREAMS][2]; 13562306a36Sopenharmony_ci const char *timer_source; 13662306a36Sopenharmony_ci}; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistruct loopback_pcm { 13962306a36Sopenharmony_ci struct loopback *loopback; 14062306a36Sopenharmony_ci struct snd_pcm_substream *substream; 14162306a36Sopenharmony_ci struct loopback_cable *cable; 14262306a36Sopenharmony_ci unsigned int pcm_buffer_size; 14362306a36Sopenharmony_ci unsigned int buf_pos; /* position in buffer */ 14462306a36Sopenharmony_ci unsigned int silent_size; 14562306a36Sopenharmony_ci /* PCM parameters */ 14662306a36Sopenharmony_ci unsigned int pcm_period_size; 14762306a36Sopenharmony_ci unsigned int pcm_bps; /* bytes per second */ 14862306a36Sopenharmony_ci unsigned int pcm_salign; /* bytes per sample * channels */ 14962306a36Sopenharmony_ci unsigned int pcm_rate_shift; /* rate shift value */ 15062306a36Sopenharmony_ci /* flags */ 15162306a36Sopenharmony_ci unsigned int period_update_pending :1; 15262306a36Sopenharmony_ci /* timer stuff */ 15362306a36Sopenharmony_ci unsigned int irq_pos; /* fractional IRQ position in jiffies 15462306a36Sopenharmony_ci * ticks 15562306a36Sopenharmony_ci */ 15662306a36Sopenharmony_ci unsigned int period_size_frac; /* period size in jiffies ticks */ 15762306a36Sopenharmony_ci unsigned int last_drift; 15862306a36Sopenharmony_ci unsigned long last_jiffies; 15962306a36Sopenharmony_ci /* If jiffies timer is used */ 16062306a36Sopenharmony_ci struct timer_list timer; 16162306a36Sopenharmony_ci}; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic struct platform_device *devices[SNDRV_CARDS]; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic inline unsigned int byte_pos(struct loopback_pcm *dpcm, unsigned int x) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci if (dpcm->pcm_rate_shift == NO_PITCH) { 16862306a36Sopenharmony_ci x /= HZ; 16962306a36Sopenharmony_ci } else { 17062306a36Sopenharmony_ci x = div_u64(NO_PITCH * (unsigned long long)x, 17162306a36Sopenharmony_ci HZ * (unsigned long long)dpcm->pcm_rate_shift); 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci return x - (x % dpcm->pcm_salign); 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic inline unsigned int frac_pos(struct loopback_pcm *dpcm, unsigned int x) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci if (dpcm->pcm_rate_shift == NO_PITCH) { /* no pitch */ 17962306a36Sopenharmony_ci return x * HZ; 18062306a36Sopenharmony_ci } else { 18162306a36Sopenharmony_ci x = div_u64(dpcm->pcm_rate_shift * (unsigned long long)x * HZ, 18262306a36Sopenharmony_ci NO_PITCH); 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci return x; 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic inline struct loopback_setup *get_setup(struct loopback_pcm *dpcm) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci int device = dpcm->substream->pstr->pcm->device; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (dpcm->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 19262306a36Sopenharmony_ci device ^= 1; 19362306a36Sopenharmony_ci return &dpcm->loopback->setup[dpcm->substream->number][device]; 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic inline unsigned int get_notify(struct loopback_pcm *dpcm) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci return get_setup(dpcm)->notify; 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic inline unsigned int get_rate_shift(struct loopback_pcm *dpcm) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci return get_setup(dpcm)->rate_shift; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci/* call in cable->lock */ 20762306a36Sopenharmony_cistatic int loopback_jiffies_timer_start(struct loopback_pcm *dpcm) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci unsigned long tick; 21062306a36Sopenharmony_ci unsigned int rate_shift = get_rate_shift(dpcm); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (rate_shift != dpcm->pcm_rate_shift) { 21362306a36Sopenharmony_ci dpcm->pcm_rate_shift = rate_shift; 21462306a36Sopenharmony_ci dpcm->period_size_frac = frac_pos(dpcm, dpcm->pcm_period_size); 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci if (dpcm->period_size_frac <= dpcm->irq_pos) { 21762306a36Sopenharmony_ci dpcm->irq_pos %= dpcm->period_size_frac; 21862306a36Sopenharmony_ci dpcm->period_update_pending = 1; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci tick = dpcm->period_size_frac - dpcm->irq_pos; 22162306a36Sopenharmony_ci tick = DIV_ROUND_UP(tick, dpcm->pcm_bps); 22262306a36Sopenharmony_ci mod_timer(&dpcm->timer, jiffies + tick); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci return 0; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci/* call in cable->lock */ 22862306a36Sopenharmony_cistatic int loopback_snd_timer_start(struct loopback_pcm *dpcm) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci struct loopback_cable *cable = dpcm->cable; 23162306a36Sopenharmony_ci int err; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* Loopback device has to use same period as timer card. Therefore 23462306a36Sopenharmony_ci * wake up for each snd_pcm_period_elapsed() call of timer card. 23562306a36Sopenharmony_ci */ 23662306a36Sopenharmony_ci err = snd_timer_start(cable->snd_timer.instance, 1); 23762306a36Sopenharmony_ci if (err < 0) { 23862306a36Sopenharmony_ci /* do not report error if trying to start but already 23962306a36Sopenharmony_ci * running. For example called by opposite substream 24062306a36Sopenharmony_ci * of the same cable 24162306a36Sopenharmony_ci */ 24262306a36Sopenharmony_ci if (err == -EBUSY) 24362306a36Sopenharmony_ci return 0; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci pcm_err(dpcm->substream->pcm, 24662306a36Sopenharmony_ci "snd_timer_start(%d,%d,%d) failed with %d", 24762306a36Sopenharmony_ci cable->snd_timer.id.card, 24862306a36Sopenharmony_ci cable->snd_timer.id.device, 24962306a36Sopenharmony_ci cable->snd_timer.id.subdevice, 25062306a36Sopenharmony_ci err); 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci return err; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci/* call in cable->lock */ 25762306a36Sopenharmony_cistatic inline int loopback_jiffies_timer_stop(struct loopback_pcm *dpcm) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci del_timer(&dpcm->timer); 26062306a36Sopenharmony_ci dpcm->timer.expires = 0; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci return 0; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci/* call in cable->lock */ 26662306a36Sopenharmony_cistatic int loopback_snd_timer_stop(struct loopback_pcm *dpcm) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci struct loopback_cable *cable = dpcm->cable; 26962306a36Sopenharmony_ci int err; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* only stop if both devices (playback and capture) are not running */ 27262306a36Sopenharmony_ci if (cable->running ^ cable->pause) 27362306a36Sopenharmony_ci return 0; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci err = snd_timer_stop(cable->snd_timer.instance); 27662306a36Sopenharmony_ci if (err < 0) { 27762306a36Sopenharmony_ci pcm_err(dpcm->substream->pcm, 27862306a36Sopenharmony_ci "snd_timer_stop(%d,%d,%d) failed with %d", 27962306a36Sopenharmony_ci cable->snd_timer.id.card, 28062306a36Sopenharmony_ci cable->snd_timer.id.device, 28162306a36Sopenharmony_ci cable->snd_timer.id.subdevice, 28262306a36Sopenharmony_ci err); 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci return err; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic inline int loopback_jiffies_timer_stop_sync(struct loopback_pcm *dpcm) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci del_timer_sync(&dpcm->timer); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci return 0; 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci/* call in loopback->cable_lock */ 29662306a36Sopenharmony_cistatic int loopback_snd_timer_close_cable(struct loopback_pcm *dpcm) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci struct loopback_cable *cable = dpcm->cable; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci /* snd_timer was not opened */ 30162306a36Sopenharmony_ci if (!cable->snd_timer.instance) 30262306a36Sopenharmony_ci return 0; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci /* will only be called from free_cable() when other stream was 30562306a36Sopenharmony_ci * already closed. Other stream cannot be reopened as long as 30662306a36Sopenharmony_ci * loopback->cable_lock is locked. Therefore no need to lock 30762306a36Sopenharmony_ci * cable->lock; 30862306a36Sopenharmony_ci */ 30962306a36Sopenharmony_ci snd_timer_close(cable->snd_timer.instance); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci /* wait till drain work has finished if requested */ 31262306a36Sopenharmony_ci cancel_work_sync(&cable->snd_timer.event_work); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci snd_timer_instance_free(cable->snd_timer.instance); 31562306a36Sopenharmony_ci memset(&cable->snd_timer, 0, sizeof(cable->snd_timer)); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci return 0; 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic int loopback_check_format(struct loopback_cable *cable, int stream) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci struct snd_pcm_runtime *runtime, *cruntime; 32362306a36Sopenharmony_ci struct loopback_setup *setup; 32462306a36Sopenharmony_ci struct snd_card *card; 32562306a36Sopenharmony_ci int check; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if (cable->valid != CABLE_VALID_BOTH) { 32862306a36Sopenharmony_ci if (stream == SNDRV_PCM_STREAM_PLAYBACK) 32962306a36Sopenharmony_ci goto __notify; 33062306a36Sopenharmony_ci return 0; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci runtime = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]-> 33362306a36Sopenharmony_ci substream->runtime; 33462306a36Sopenharmony_ci cruntime = cable->streams[SNDRV_PCM_STREAM_CAPTURE]-> 33562306a36Sopenharmony_ci substream->runtime; 33662306a36Sopenharmony_ci check = runtime->format != cruntime->format || 33762306a36Sopenharmony_ci runtime->rate != cruntime->rate || 33862306a36Sopenharmony_ci runtime->channels != cruntime->channels; 33962306a36Sopenharmony_ci if (!check) 34062306a36Sopenharmony_ci return 0; 34162306a36Sopenharmony_ci if (stream == SNDRV_PCM_STREAM_CAPTURE) { 34262306a36Sopenharmony_ci return -EIO; 34362306a36Sopenharmony_ci } else { 34462306a36Sopenharmony_ci snd_pcm_stop(cable->streams[SNDRV_PCM_STREAM_CAPTURE]-> 34562306a36Sopenharmony_ci substream, SNDRV_PCM_STATE_DRAINING); 34662306a36Sopenharmony_ci __notify: 34762306a36Sopenharmony_ci runtime = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]-> 34862306a36Sopenharmony_ci substream->runtime; 34962306a36Sopenharmony_ci setup = get_setup(cable->streams[SNDRV_PCM_STREAM_PLAYBACK]); 35062306a36Sopenharmony_ci card = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]->loopback->card; 35162306a36Sopenharmony_ci if (setup->format != runtime->format) { 35262306a36Sopenharmony_ci snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, 35362306a36Sopenharmony_ci &setup->format_id); 35462306a36Sopenharmony_ci setup->format = runtime->format; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci if (setup->rate != runtime->rate) { 35762306a36Sopenharmony_ci snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, 35862306a36Sopenharmony_ci &setup->rate_id); 35962306a36Sopenharmony_ci setup->rate = runtime->rate; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci if (setup->channels != runtime->channels) { 36262306a36Sopenharmony_ci snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, 36362306a36Sopenharmony_ci &setup->channels_id); 36462306a36Sopenharmony_ci setup->channels = runtime->channels; 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci return 0; 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic void loopback_active_notify(struct loopback_pcm *dpcm) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci snd_ctl_notify(dpcm->loopback->card, 37362306a36Sopenharmony_ci SNDRV_CTL_EVENT_MASK_VALUE, 37462306a36Sopenharmony_ci &get_setup(dpcm)->active_id); 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic int loopback_trigger(struct snd_pcm_substream *substream, int cmd) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 38062306a36Sopenharmony_ci struct loopback_pcm *dpcm = runtime->private_data; 38162306a36Sopenharmony_ci struct loopback_cable *cable = dpcm->cable; 38262306a36Sopenharmony_ci int err = 0, stream = 1 << substream->stream; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci switch (cmd) { 38562306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 38662306a36Sopenharmony_ci err = loopback_check_format(cable, substream->stream); 38762306a36Sopenharmony_ci if (err < 0) 38862306a36Sopenharmony_ci return err; 38962306a36Sopenharmony_ci dpcm->last_jiffies = jiffies; 39062306a36Sopenharmony_ci dpcm->pcm_rate_shift = 0; 39162306a36Sopenharmony_ci dpcm->last_drift = 0; 39262306a36Sopenharmony_ci spin_lock(&cable->lock); 39362306a36Sopenharmony_ci cable->running |= stream; 39462306a36Sopenharmony_ci cable->pause &= ~stream; 39562306a36Sopenharmony_ci err = cable->ops->start(dpcm); 39662306a36Sopenharmony_ci spin_unlock(&cable->lock); 39762306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 39862306a36Sopenharmony_ci loopback_active_notify(dpcm); 39962306a36Sopenharmony_ci break; 40062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 40162306a36Sopenharmony_ci spin_lock(&cable->lock); 40262306a36Sopenharmony_ci cable->running &= ~stream; 40362306a36Sopenharmony_ci cable->pause &= ~stream; 40462306a36Sopenharmony_ci err = cable->ops->stop(dpcm); 40562306a36Sopenharmony_ci spin_unlock(&cable->lock); 40662306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 40762306a36Sopenharmony_ci loopback_active_notify(dpcm); 40862306a36Sopenharmony_ci break; 40962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 41062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 41162306a36Sopenharmony_ci spin_lock(&cable->lock); 41262306a36Sopenharmony_ci cable->pause |= stream; 41362306a36Sopenharmony_ci err = cable->ops->stop(dpcm); 41462306a36Sopenharmony_ci spin_unlock(&cable->lock); 41562306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 41662306a36Sopenharmony_ci loopback_active_notify(dpcm); 41762306a36Sopenharmony_ci break; 41862306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 41962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 42062306a36Sopenharmony_ci spin_lock(&cable->lock); 42162306a36Sopenharmony_ci dpcm->last_jiffies = jiffies; 42262306a36Sopenharmony_ci cable->pause &= ~stream; 42362306a36Sopenharmony_ci err = cable->ops->start(dpcm); 42462306a36Sopenharmony_ci spin_unlock(&cable->lock); 42562306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 42662306a36Sopenharmony_ci loopback_active_notify(dpcm); 42762306a36Sopenharmony_ci break; 42862306a36Sopenharmony_ci default: 42962306a36Sopenharmony_ci return -EINVAL; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci return err; 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic void params_change(struct snd_pcm_substream *substream) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 43762306a36Sopenharmony_ci struct loopback_pcm *dpcm = runtime->private_data; 43862306a36Sopenharmony_ci struct loopback_cable *cable = dpcm->cable; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci cable->hw.formats = pcm_format_to_bits(runtime->format); 44162306a36Sopenharmony_ci cable->hw.rate_min = runtime->rate; 44262306a36Sopenharmony_ci cable->hw.rate_max = runtime->rate; 44362306a36Sopenharmony_ci cable->hw.channels_min = runtime->channels; 44462306a36Sopenharmony_ci cable->hw.channels_max = runtime->channels; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci if (cable->snd_timer.instance) { 44762306a36Sopenharmony_ci cable->hw.period_bytes_min = 44862306a36Sopenharmony_ci frames_to_bytes(runtime, runtime->period_size); 44962306a36Sopenharmony_ci cable->hw.period_bytes_max = cable->hw.period_bytes_min; 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic int loopback_prepare(struct snd_pcm_substream *substream) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 45762306a36Sopenharmony_ci struct loopback_pcm *dpcm = runtime->private_data; 45862306a36Sopenharmony_ci struct loopback_cable *cable = dpcm->cable; 45962306a36Sopenharmony_ci int err, bps, salign; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci if (cable->ops->stop_sync) { 46262306a36Sopenharmony_ci err = cable->ops->stop_sync(dpcm); 46362306a36Sopenharmony_ci if (err < 0) 46462306a36Sopenharmony_ci return err; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci salign = (snd_pcm_format_physical_width(runtime->format) * 46862306a36Sopenharmony_ci runtime->channels) / 8; 46962306a36Sopenharmony_ci bps = salign * runtime->rate; 47062306a36Sopenharmony_ci if (bps <= 0 || salign <= 0) 47162306a36Sopenharmony_ci return -EINVAL; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci dpcm->buf_pos = 0; 47462306a36Sopenharmony_ci dpcm->pcm_buffer_size = frames_to_bytes(runtime, runtime->buffer_size); 47562306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { 47662306a36Sopenharmony_ci /* clear capture buffer */ 47762306a36Sopenharmony_ci dpcm->silent_size = dpcm->pcm_buffer_size; 47862306a36Sopenharmony_ci snd_pcm_format_set_silence(runtime->format, runtime->dma_area, 47962306a36Sopenharmony_ci runtime->buffer_size * runtime->channels); 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci dpcm->irq_pos = 0; 48362306a36Sopenharmony_ci dpcm->period_update_pending = 0; 48462306a36Sopenharmony_ci dpcm->pcm_bps = bps; 48562306a36Sopenharmony_ci dpcm->pcm_salign = salign; 48662306a36Sopenharmony_ci dpcm->pcm_period_size = frames_to_bytes(runtime, runtime->period_size); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci mutex_lock(&dpcm->loopback->cable_lock); 48962306a36Sopenharmony_ci if (!(cable->valid & ~(1 << substream->stream)) || 49062306a36Sopenharmony_ci (get_setup(dpcm)->notify && 49162306a36Sopenharmony_ci substream->stream == SNDRV_PCM_STREAM_PLAYBACK)) 49262306a36Sopenharmony_ci params_change(substream); 49362306a36Sopenharmony_ci cable->valid |= 1 << substream->stream; 49462306a36Sopenharmony_ci mutex_unlock(&dpcm->loopback->cable_lock); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci return 0; 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cistatic void clear_capture_buf(struct loopback_pcm *dpcm, unsigned int bytes) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = dpcm->substream->runtime; 50262306a36Sopenharmony_ci char *dst = runtime->dma_area; 50362306a36Sopenharmony_ci unsigned int dst_off = dpcm->buf_pos; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci if (dpcm->silent_size >= dpcm->pcm_buffer_size) 50662306a36Sopenharmony_ci return; 50762306a36Sopenharmony_ci if (dpcm->silent_size + bytes > dpcm->pcm_buffer_size) 50862306a36Sopenharmony_ci bytes = dpcm->pcm_buffer_size - dpcm->silent_size; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci for (;;) { 51162306a36Sopenharmony_ci unsigned int size = bytes; 51262306a36Sopenharmony_ci if (dst_off + size > dpcm->pcm_buffer_size) 51362306a36Sopenharmony_ci size = dpcm->pcm_buffer_size - dst_off; 51462306a36Sopenharmony_ci snd_pcm_format_set_silence(runtime->format, dst + dst_off, 51562306a36Sopenharmony_ci bytes_to_frames(runtime, size) * 51662306a36Sopenharmony_ci runtime->channels); 51762306a36Sopenharmony_ci dpcm->silent_size += size; 51862306a36Sopenharmony_ci bytes -= size; 51962306a36Sopenharmony_ci if (!bytes) 52062306a36Sopenharmony_ci break; 52162306a36Sopenharmony_ci dst_off = 0; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_cistatic void copy_play_buf(struct loopback_pcm *play, 52662306a36Sopenharmony_ci struct loopback_pcm *capt, 52762306a36Sopenharmony_ci unsigned int bytes) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = play->substream->runtime; 53062306a36Sopenharmony_ci char *src = runtime->dma_area; 53162306a36Sopenharmony_ci char *dst = capt->substream->runtime->dma_area; 53262306a36Sopenharmony_ci unsigned int src_off = play->buf_pos; 53362306a36Sopenharmony_ci unsigned int dst_off = capt->buf_pos; 53462306a36Sopenharmony_ci unsigned int clear_bytes = 0; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci /* check if playback is draining, trim the capture copy size 53762306a36Sopenharmony_ci * when our pointer is at the end of playback ring buffer */ 53862306a36Sopenharmony_ci if (runtime->state == SNDRV_PCM_STATE_DRAINING && 53962306a36Sopenharmony_ci snd_pcm_playback_hw_avail(runtime) < runtime->buffer_size) { 54062306a36Sopenharmony_ci snd_pcm_uframes_t appl_ptr, appl_ptr1, diff; 54162306a36Sopenharmony_ci appl_ptr = appl_ptr1 = runtime->control->appl_ptr; 54262306a36Sopenharmony_ci appl_ptr1 -= appl_ptr1 % runtime->buffer_size; 54362306a36Sopenharmony_ci appl_ptr1 += play->buf_pos / play->pcm_salign; 54462306a36Sopenharmony_ci if (appl_ptr < appl_ptr1) 54562306a36Sopenharmony_ci appl_ptr1 -= runtime->buffer_size; 54662306a36Sopenharmony_ci diff = (appl_ptr - appl_ptr1) * play->pcm_salign; 54762306a36Sopenharmony_ci if (diff < bytes) { 54862306a36Sopenharmony_ci clear_bytes = bytes - diff; 54962306a36Sopenharmony_ci bytes = diff; 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci for (;;) { 55462306a36Sopenharmony_ci unsigned int size = bytes; 55562306a36Sopenharmony_ci if (src_off + size > play->pcm_buffer_size) 55662306a36Sopenharmony_ci size = play->pcm_buffer_size - src_off; 55762306a36Sopenharmony_ci if (dst_off + size > capt->pcm_buffer_size) 55862306a36Sopenharmony_ci size = capt->pcm_buffer_size - dst_off; 55962306a36Sopenharmony_ci memcpy(dst + dst_off, src + src_off, size); 56062306a36Sopenharmony_ci capt->silent_size = 0; 56162306a36Sopenharmony_ci bytes -= size; 56262306a36Sopenharmony_ci if (!bytes) 56362306a36Sopenharmony_ci break; 56462306a36Sopenharmony_ci src_off = (src_off + size) % play->pcm_buffer_size; 56562306a36Sopenharmony_ci dst_off = (dst_off + size) % capt->pcm_buffer_size; 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci if (clear_bytes > 0) { 56962306a36Sopenharmony_ci clear_capture_buf(capt, clear_bytes); 57062306a36Sopenharmony_ci capt->silent_size = 0; 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_cistatic inline unsigned int bytepos_delta(struct loopback_pcm *dpcm, 57562306a36Sopenharmony_ci unsigned int jiffies_delta) 57662306a36Sopenharmony_ci{ 57762306a36Sopenharmony_ci unsigned long last_pos; 57862306a36Sopenharmony_ci unsigned int delta; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci last_pos = byte_pos(dpcm, dpcm->irq_pos); 58162306a36Sopenharmony_ci dpcm->irq_pos += jiffies_delta * dpcm->pcm_bps; 58262306a36Sopenharmony_ci delta = byte_pos(dpcm, dpcm->irq_pos) - last_pos; 58362306a36Sopenharmony_ci if (delta >= dpcm->last_drift) 58462306a36Sopenharmony_ci delta -= dpcm->last_drift; 58562306a36Sopenharmony_ci dpcm->last_drift = 0; 58662306a36Sopenharmony_ci if (dpcm->irq_pos >= dpcm->period_size_frac) { 58762306a36Sopenharmony_ci dpcm->irq_pos %= dpcm->period_size_frac; 58862306a36Sopenharmony_ci dpcm->period_update_pending = 1; 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci return delta; 59162306a36Sopenharmony_ci} 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_cistatic inline void bytepos_finish(struct loopback_pcm *dpcm, 59462306a36Sopenharmony_ci unsigned int delta) 59562306a36Sopenharmony_ci{ 59662306a36Sopenharmony_ci dpcm->buf_pos += delta; 59762306a36Sopenharmony_ci dpcm->buf_pos %= dpcm->pcm_buffer_size; 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci/* call in cable->lock */ 60162306a36Sopenharmony_cistatic unsigned int loopback_jiffies_timer_pos_update 60262306a36Sopenharmony_ci (struct loopback_cable *cable) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci struct loopback_pcm *dpcm_play = 60562306a36Sopenharmony_ci cable->streams[SNDRV_PCM_STREAM_PLAYBACK]; 60662306a36Sopenharmony_ci struct loopback_pcm *dpcm_capt = 60762306a36Sopenharmony_ci cable->streams[SNDRV_PCM_STREAM_CAPTURE]; 60862306a36Sopenharmony_ci unsigned long delta_play = 0, delta_capt = 0, cur_jiffies; 60962306a36Sopenharmony_ci unsigned int running, count1, count2; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci cur_jiffies = jiffies; 61262306a36Sopenharmony_ci running = cable->running ^ cable->pause; 61362306a36Sopenharmony_ci if (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) { 61462306a36Sopenharmony_ci delta_play = cur_jiffies - dpcm_play->last_jiffies; 61562306a36Sopenharmony_ci dpcm_play->last_jiffies += delta_play; 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci if (running & (1 << SNDRV_PCM_STREAM_CAPTURE)) { 61962306a36Sopenharmony_ci delta_capt = cur_jiffies - dpcm_capt->last_jiffies; 62062306a36Sopenharmony_ci dpcm_capt->last_jiffies += delta_capt; 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci if (delta_play == 0 && delta_capt == 0) 62462306a36Sopenharmony_ci goto unlock; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci if (delta_play > delta_capt) { 62762306a36Sopenharmony_ci count1 = bytepos_delta(dpcm_play, delta_play - delta_capt); 62862306a36Sopenharmony_ci bytepos_finish(dpcm_play, count1); 62962306a36Sopenharmony_ci delta_play = delta_capt; 63062306a36Sopenharmony_ci } else if (delta_play < delta_capt) { 63162306a36Sopenharmony_ci count1 = bytepos_delta(dpcm_capt, delta_capt - delta_play); 63262306a36Sopenharmony_ci clear_capture_buf(dpcm_capt, count1); 63362306a36Sopenharmony_ci bytepos_finish(dpcm_capt, count1); 63462306a36Sopenharmony_ci delta_capt = delta_play; 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci if (delta_play == 0 && delta_capt == 0) 63862306a36Sopenharmony_ci goto unlock; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci /* note delta_capt == delta_play at this moment */ 64162306a36Sopenharmony_ci count1 = bytepos_delta(dpcm_play, delta_play); 64262306a36Sopenharmony_ci count2 = bytepos_delta(dpcm_capt, delta_capt); 64362306a36Sopenharmony_ci if (count1 < count2) { 64462306a36Sopenharmony_ci dpcm_capt->last_drift = count2 - count1; 64562306a36Sopenharmony_ci count1 = count2; 64662306a36Sopenharmony_ci } else if (count1 > count2) { 64762306a36Sopenharmony_ci dpcm_play->last_drift = count1 - count2; 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci copy_play_buf(dpcm_play, dpcm_capt, count1); 65062306a36Sopenharmony_ci bytepos_finish(dpcm_play, count1); 65162306a36Sopenharmony_ci bytepos_finish(dpcm_capt, count1); 65262306a36Sopenharmony_ci unlock: 65362306a36Sopenharmony_ci return running; 65462306a36Sopenharmony_ci} 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_cistatic void loopback_jiffies_timer_function(struct timer_list *t) 65762306a36Sopenharmony_ci{ 65862306a36Sopenharmony_ci struct loopback_pcm *dpcm = from_timer(dpcm, t, timer); 65962306a36Sopenharmony_ci unsigned long flags; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci spin_lock_irqsave(&dpcm->cable->lock, flags); 66262306a36Sopenharmony_ci if (loopback_jiffies_timer_pos_update(dpcm->cable) & 66362306a36Sopenharmony_ci (1 << dpcm->substream->stream)) { 66462306a36Sopenharmony_ci loopback_jiffies_timer_start(dpcm); 66562306a36Sopenharmony_ci if (dpcm->period_update_pending) { 66662306a36Sopenharmony_ci dpcm->period_update_pending = 0; 66762306a36Sopenharmony_ci spin_unlock_irqrestore(&dpcm->cable->lock, flags); 66862306a36Sopenharmony_ci /* need to unlock before calling below */ 66962306a36Sopenharmony_ci snd_pcm_period_elapsed(dpcm->substream); 67062306a36Sopenharmony_ci return; 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci spin_unlock_irqrestore(&dpcm->cable->lock, flags); 67462306a36Sopenharmony_ci} 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci/* call in cable->lock */ 67762306a36Sopenharmony_cistatic int loopback_snd_timer_check_resolution(struct snd_pcm_runtime *runtime, 67862306a36Sopenharmony_ci unsigned long resolution) 67962306a36Sopenharmony_ci{ 68062306a36Sopenharmony_ci if (resolution != runtime->timer_resolution) { 68162306a36Sopenharmony_ci struct loopback_pcm *dpcm = runtime->private_data; 68262306a36Sopenharmony_ci struct loopback_cable *cable = dpcm->cable; 68362306a36Sopenharmony_ci /* Worst case estimation of possible values for resolution 68462306a36Sopenharmony_ci * resolution <= (512 * 1024) frames / 8kHz in nsec 68562306a36Sopenharmony_ci * resolution <= 65.536.000.000 nsec 68662306a36Sopenharmony_ci * 68762306a36Sopenharmony_ci * period_size <= 65.536.000.000 nsec / 1000nsec/usec * 192kHz + 68862306a36Sopenharmony_ci * 500.000 68962306a36Sopenharmony_ci * period_size <= 12.582.912.000.000 <64bit 69062306a36Sopenharmony_ci * / 1.000.000 usec/sec 69162306a36Sopenharmony_ci */ 69262306a36Sopenharmony_ci snd_pcm_uframes_t period_size_usec = 69362306a36Sopenharmony_ci resolution / 1000 * runtime->rate; 69462306a36Sopenharmony_ci /* round to nearest sample rate */ 69562306a36Sopenharmony_ci snd_pcm_uframes_t period_size = 69662306a36Sopenharmony_ci (period_size_usec + 500 * 1000) / (1000 * 1000); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci pcm_err(dpcm->substream->pcm, 69962306a36Sopenharmony_ci "Period size (%lu frames) of loopback device is not corresponding to timer resolution (%lu nsec = %lu frames) of card timer %d,%d,%d. Use period size of %lu frames for loopback device.", 70062306a36Sopenharmony_ci runtime->period_size, resolution, period_size, 70162306a36Sopenharmony_ci cable->snd_timer.id.card, 70262306a36Sopenharmony_ci cable->snd_timer.id.device, 70362306a36Sopenharmony_ci cable->snd_timer.id.subdevice, 70462306a36Sopenharmony_ci period_size); 70562306a36Sopenharmony_ci return -EINVAL; 70662306a36Sopenharmony_ci } 70762306a36Sopenharmony_ci return 0; 70862306a36Sopenharmony_ci} 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_cistatic void loopback_snd_timer_period_elapsed(struct loopback_cable *cable, 71162306a36Sopenharmony_ci int event, 71262306a36Sopenharmony_ci unsigned long resolution) 71362306a36Sopenharmony_ci{ 71462306a36Sopenharmony_ci struct loopback_pcm *dpcm_play, *dpcm_capt; 71562306a36Sopenharmony_ci struct snd_pcm_substream *substream_play, *substream_capt; 71662306a36Sopenharmony_ci struct snd_pcm_runtime *valid_runtime; 71762306a36Sopenharmony_ci unsigned int running, elapsed_bytes; 71862306a36Sopenharmony_ci unsigned long flags; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci spin_lock_irqsave(&cable->lock, flags); 72162306a36Sopenharmony_ci running = cable->running ^ cable->pause; 72262306a36Sopenharmony_ci /* no need to do anything if no stream is running */ 72362306a36Sopenharmony_ci if (!running) { 72462306a36Sopenharmony_ci spin_unlock_irqrestore(&cable->lock, flags); 72562306a36Sopenharmony_ci return; 72662306a36Sopenharmony_ci } 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci dpcm_play = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]; 72962306a36Sopenharmony_ci dpcm_capt = cable->streams[SNDRV_PCM_STREAM_CAPTURE]; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci if (event == SNDRV_TIMER_EVENT_MSTOP) { 73262306a36Sopenharmony_ci if (!dpcm_play || 73362306a36Sopenharmony_ci dpcm_play->substream->runtime->state != 73462306a36Sopenharmony_ci SNDRV_PCM_STATE_DRAINING) { 73562306a36Sopenharmony_ci spin_unlock_irqrestore(&cable->lock, flags); 73662306a36Sopenharmony_ci return; 73762306a36Sopenharmony_ci } 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci substream_play = (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ? 74162306a36Sopenharmony_ci dpcm_play->substream : NULL; 74262306a36Sopenharmony_ci substream_capt = (running & (1 << SNDRV_PCM_STREAM_CAPTURE)) ? 74362306a36Sopenharmony_ci dpcm_capt->substream : NULL; 74462306a36Sopenharmony_ci valid_runtime = (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ? 74562306a36Sopenharmony_ci dpcm_play->substream->runtime : 74662306a36Sopenharmony_ci dpcm_capt->substream->runtime; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci /* resolution is only valid for SNDRV_TIMER_EVENT_TICK events */ 74962306a36Sopenharmony_ci if (event == SNDRV_TIMER_EVENT_TICK) { 75062306a36Sopenharmony_ci /* The hardware rules guarantee that playback and capture period 75162306a36Sopenharmony_ci * are the same. Therefore only one device has to be checked 75262306a36Sopenharmony_ci * here. 75362306a36Sopenharmony_ci */ 75462306a36Sopenharmony_ci if (loopback_snd_timer_check_resolution(valid_runtime, 75562306a36Sopenharmony_ci resolution) < 0) { 75662306a36Sopenharmony_ci spin_unlock_irqrestore(&cable->lock, flags); 75762306a36Sopenharmony_ci if (substream_play) 75862306a36Sopenharmony_ci snd_pcm_stop_xrun(substream_play); 75962306a36Sopenharmony_ci if (substream_capt) 76062306a36Sopenharmony_ci snd_pcm_stop_xrun(substream_capt); 76162306a36Sopenharmony_ci return; 76262306a36Sopenharmony_ci } 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci elapsed_bytes = frames_to_bytes(valid_runtime, 76662306a36Sopenharmony_ci valid_runtime->period_size); 76762306a36Sopenharmony_ci /* The same timer interrupt is used for playback and capture device */ 76862306a36Sopenharmony_ci if ((running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) && 76962306a36Sopenharmony_ci (running & (1 << SNDRV_PCM_STREAM_CAPTURE))) { 77062306a36Sopenharmony_ci copy_play_buf(dpcm_play, dpcm_capt, elapsed_bytes); 77162306a36Sopenharmony_ci bytepos_finish(dpcm_play, elapsed_bytes); 77262306a36Sopenharmony_ci bytepos_finish(dpcm_capt, elapsed_bytes); 77362306a36Sopenharmony_ci } else if (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) { 77462306a36Sopenharmony_ci bytepos_finish(dpcm_play, elapsed_bytes); 77562306a36Sopenharmony_ci } else if (running & (1 << SNDRV_PCM_STREAM_CAPTURE)) { 77662306a36Sopenharmony_ci clear_capture_buf(dpcm_capt, elapsed_bytes); 77762306a36Sopenharmony_ci bytepos_finish(dpcm_capt, elapsed_bytes); 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci spin_unlock_irqrestore(&cable->lock, flags); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci if (substream_play) 78262306a36Sopenharmony_ci snd_pcm_period_elapsed(substream_play); 78362306a36Sopenharmony_ci if (substream_capt) 78462306a36Sopenharmony_ci snd_pcm_period_elapsed(substream_capt); 78562306a36Sopenharmony_ci} 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_cistatic void loopback_snd_timer_function(struct snd_timer_instance *timeri, 78862306a36Sopenharmony_ci unsigned long resolution, 78962306a36Sopenharmony_ci unsigned long ticks) 79062306a36Sopenharmony_ci{ 79162306a36Sopenharmony_ci struct loopback_cable *cable = timeri->callback_data; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci loopback_snd_timer_period_elapsed(cable, SNDRV_TIMER_EVENT_TICK, 79462306a36Sopenharmony_ci resolution); 79562306a36Sopenharmony_ci} 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_cistatic void loopback_snd_timer_work(struct work_struct *work) 79862306a36Sopenharmony_ci{ 79962306a36Sopenharmony_ci struct loopback_cable *cable; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci cable = container_of(work, struct loopback_cable, snd_timer.event_work); 80262306a36Sopenharmony_ci loopback_snd_timer_period_elapsed(cable, SNDRV_TIMER_EVENT_MSTOP, 0); 80362306a36Sopenharmony_ci} 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_cistatic void loopback_snd_timer_event(struct snd_timer_instance *timeri, 80662306a36Sopenharmony_ci int event, 80762306a36Sopenharmony_ci struct timespec64 *tstamp, 80862306a36Sopenharmony_ci unsigned long resolution) 80962306a36Sopenharmony_ci{ 81062306a36Sopenharmony_ci /* Do not lock cable->lock here because timer->lock is already hold. 81162306a36Sopenharmony_ci * There are other functions which first lock cable->lock and than 81262306a36Sopenharmony_ci * timer->lock e.g. 81362306a36Sopenharmony_ci * loopback_trigger() 81462306a36Sopenharmony_ci * spin_lock(&cable->lock) 81562306a36Sopenharmony_ci * loopback_snd_timer_start() 81662306a36Sopenharmony_ci * snd_timer_start() 81762306a36Sopenharmony_ci * spin_lock(&timer->lock) 81862306a36Sopenharmony_ci * Therefore when using the oposit order of locks here it could result 81962306a36Sopenharmony_ci * in a deadlock. 82062306a36Sopenharmony_ci */ 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci if (event == SNDRV_TIMER_EVENT_MSTOP) { 82362306a36Sopenharmony_ci struct loopback_cable *cable = timeri->callback_data; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci /* sound card of the timer was stopped. Therefore there will not 82662306a36Sopenharmony_ci * be any further timer callbacks. Due to this forward audio 82762306a36Sopenharmony_ci * data from here if in draining state. When still in running 82862306a36Sopenharmony_ci * state the streaming will be aborted by the usual timeout. It 82962306a36Sopenharmony_ci * should not be aborted here because may be the timer sound 83062306a36Sopenharmony_ci * card does only a recovery and the timer is back soon. 83162306a36Sopenharmony_ci * This work triggers loopback_snd_timer_work() 83262306a36Sopenharmony_ci */ 83362306a36Sopenharmony_ci schedule_work(&cable->snd_timer.event_work); 83462306a36Sopenharmony_ci } 83562306a36Sopenharmony_ci} 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_cistatic void loopback_jiffies_timer_dpcm_info(struct loopback_pcm *dpcm, 83862306a36Sopenharmony_ci struct snd_info_buffer *buffer) 83962306a36Sopenharmony_ci{ 84062306a36Sopenharmony_ci snd_iprintf(buffer, " update_pending:\t%u\n", 84162306a36Sopenharmony_ci dpcm->period_update_pending); 84262306a36Sopenharmony_ci snd_iprintf(buffer, " irq_pos:\t\t%u\n", dpcm->irq_pos); 84362306a36Sopenharmony_ci snd_iprintf(buffer, " period_frac:\t%u\n", dpcm->period_size_frac); 84462306a36Sopenharmony_ci snd_iprintf(buffer, " last_jiffies:\t%lu (%lu)\n", 84562306a36Sopenharmony_ci dpcm->last_jiffies, jiffies); 84662306a36Sopenharmony_ci snd_iprintf(buffer, " timer_expires:\t%lu\n", dpcm->timer.expires); 84762306a36Sopenharmony_ci} 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_cistatic void loopback_snd_timer_dpcm_info(struct loopback_pcm *dpcm, 85062306a36Sopenharmony_ci struct snd_info_buffer *buffer) 85162306a36Sopenharmony_ci{ 85262306a36Sopenharmony_ci struct loopback_cable *cable = dpcm->cable; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci snd_iprintf(buffer, " sound timer:\thw:%d,%d,%d\n", 85562306a36Sopenharmony_ci cable->snd_timer.id.card, 85662306a36Sopenharmony_ci cable->snd_timer.id.device, 85762306a36Sopenharmony_ci cable->snd_timer.id.subdevice); 85862306a36Sopenharmony_ci snd_iprintf(buffer, " timer open:\t\t%s\n", 85962306a36Sopenharmony_ci (cable->snd_timer.stream == SNDRV_PCM_STREAM_CAPTURE) ? 86062306a36Sopenharmony_ci "capture" : "playback"); 86162306a36Sopenharmony_ci} 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_cistatic snd_pcm_uframes_t loopback_pointer(struct snd_pcm_substream *substream) 86462306a36Sopenharmony_ci{ 86562306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 86662306a36Sopenharmony_ci struct loopback_pcm *dpcm = runtime->private_data; 86762306a36Sopenharmony_ci snd_pcm_uframes_t pos; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci spin_lock(&dpcm->cable->lock); 87062306a36Sopenharmony_ci if (dpcm->cable->ops->pos_update) 87162306a36Sopenharmony_ci dpcm->cable->ops->pos_update(dpcm->cable); 87262306a36Sopenharmony_ci pos = dpcm->buf_pos; 87362306a36Sopenharmony_ci spin_unlock(&dpcm->cable->lock); 87462306a36Sopenharmony_ci return bytes_to_frames(runtime, pos); 87562306a36Sopenharmony_ci} 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_cistatic const struct snd_pcm_hardware loopback_pcm_hardware = 87862306a36Sopenharmony_ci{ 87962306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP | 88062306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE | 88162306a36Sopenharmony_ci SNDRV_PCM_INFO_RESUME), 88262306a36Sopenharmony_ci .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | 88362306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE | 88462306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | 88562306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE | 88662306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE), 88762306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_192000, 88862306a36Sopenharmony_ci .rate_min = 8000, 88962306a36Sopenharmony_ci .rate_max = 192000, 89062306a36Sopenharmony_ci .channels_min = 1, 89162306a36Sopenharmony_ci .channels_max = 32, 89262306a36Sopenharmony_ci .buffer_bytes_max = 2 * 1024 * 1024, 89362306a36Sopenharmony_ci .period_bytes_min = 64, 89462306a36Sopenharmony_ci /* note check overflow in frac_pos() using pcm_rate_shift before 89562306a36Sopenharmony_ci changing period_bytes_max value */ 89662306a36Sopenharmony_ci .period_bytes_max = 1024 * 1024, 89762306a36Sopenharmony_ci .periods_min = 1, 89862306a36Sopenharmony_ci .periods_max = 1024, 89962306a36Sopenharmony_ci .fifo_size = 0, 90062306a36Sopenharmony_ci}; 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_cistatic void loopback_runtime_free(struct snd_pcm_runtime *runtime) 90362306a36Sopenharmony_ci{ 90462306a36Sopenharmony_ci struct loopback_pcm *dpcm = runtime->private_data; 90562306a36Sopenharmony_ci kfree(dpcm); 90662306a36Sopenharmony_ci} 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_cistatic int loopback_hw_free(struct snd_pcm_substream *substream) 90962306a36Sopenharmony_ci{ 91062306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 91162306a36Sopenharmony_ci struct loopback_pcm *dpcm = runtime->private_data; 91262306a36Sopenharmony_ci struct loopback_cable *cable = dpcm->cable; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci mutex_lock(&dpcm->loopback->cable_lock); 91562306a36Sopenharmony_ci cable->valid &= ~(1 << substream->stream); 91662306a36Sopenharmony_ci mutex_unlock(&dpcm->loopback->cable_lock); 91762306a36Sopenharmony_ci return 0; 91862306a36Sopenharmony_ci} 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_cistatic unsigned int get_cable_index(struct snd_pcm_substream *substream) 92162306a36Sopenharmony_ci{ 92262306a36Sopenharmony_ci if (!substream->pcm->device) 92362306a36Sopenharmony_ci return substream->stream; 92462306a36Sopenharmony_ci else 92562306a36Sopenharmony_ci return !substream->stream; 92662306a36Sopenharmony_ci} 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_cistatic int rule_format(struct snd_pcm_hw_params *params, 92962306a36Sopenharmony_ci struct snd_pcm_hw_rule *rule) 93062306a36Sopenharmony_ci{ 93162306a36Sopenharmony_ci struct loopback_pcm *dpcm = rule->private; 93262306a36Sopenharmony_ci struct loopback_cable *cable = dpcm->cable; 93362306a36Sopenharmony_ci struct snd_mask m; 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci snd_mask_none(&m); 93662306a36Sopenharmony_ci mutex_lock(&dpcm->loopback->cable_lock); 93762306a36Sopenharmony_ci m.bits[0] = (u_int32_t)cable->hw.formats; 93862306a36Sopenharmony_ci m.bits[1] = (u_int32_t)(cable->hw.formats >> 32); 93962306a36Sopenharmony_ci mutex_unlock(&dpcm->loopback->cable_lock); 94062306a36Sopenharmony_ci return snd_mask_refine(hw_param_mask(params, rule->var), &m); 94162306a36Sopenharmony_ci} 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_cistatic int rule_rate(struct snd_pcm_hw_params *params, 94462306a36Sopenharmony_ci struct snd_pcm_hw_rule *rule) 94562306a36Sopenharmony_ci{ 94662306a36Sopenharmony_ci struct loopback_pcm *dpcm = rule->private; 94762306a36Sopenharmony_ci struct loopback_cable *cable = dpcm->cable; 94862306a36Sopenharmony_ci struct snd_interval t; 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci mutex_lock(&dpcm->loopback->cable_lock); 95162306a36Sopenharmony_ci t.min = cable->hw.rate_min; 95262306a36Sopenharmony_ci t.max = cable->hw.rate_max; 95362306a36Sopenharmony_ci mutex_unlock(&dpcm->loopback->cable_lock); 95462306a36Sopenharmony_ci t.openmin = t.openmax = 0; 95562306a36Sopenharmony_ci t.integer = 0; 95662306a36Sopenharmony_ci return snd_interval_refine(hw_param_interval(params, rule->var), &t); 95762306a36Sopenharmony_ci} 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_cistatic int rule_channels(struct snd_pcm_hw_params *params, 96062306a36Sopenharmony_ci struct snd_pcm_hw_rule *rule) 96162306a36Sopenharmony_ci{ 96262306a36Sopenharmony_ci struct loopback_pcm *dpcm = rule->private; 96362306a36Sopenharmony_ci struct loopback_cable *cable = dpcm->cable; 96462306a36Sopenharmony_ci struct snd_interval t; 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci mutex_lock(&dpcm->loopback->cable_lock); 96762306a36Sopenharmony_ci t.min = cable->hw.channels_min; 96862306a36Sopenharmony_ci t.max = cable->hw.channels_max; 96962306a36Sopenharmony_ci mutex_unlock(&dpcm->loopback->cable_lock); 97062306a36Sopenharmony_ci t.openmin = t.openmax = 0; 97162306a36Sopenharmony_ci t.integer = 0; 97262306a36Sopenharmony_ci return snd_interval_refine(hw_param_interval(params, rule->var), &t); 97362306a36Sopenharmony_ci} 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_cistatic int rule_period_bytes(struct snd_pcm_hw_params *params, 97662306a36Sopenharmony_ci struct snd_pcm_hw_rule *rule) 97762306a36Sopenharmony_ci{ 97862306a36Sopenharmony_ci struct loopback_pcm *dpcm = rule->private; 97962306a36Sopenharmony_ci struct loopback_cable *cable = dpcm->cable; 98062306a36Sopenharmony_ci struct snd_interval t; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci mutex_lock(&dpcm->loopback->cable_lock); 98362306a36Sopenharmony_ci t.min = cable->hw.period_bytes_min; 98462306a36Sopenharmony_ci t.max = cable->hw.period_bytes_max; 98562306a36Sopenharmony_ci mutex_unlock(&dpcm->loopback->cable_lock); 98662306a36Sopenharmony_ci t.openmin = 0; 98762306a36Sopenharmony_ci t.openmax = 0; 98862306a36Sopenharmony_ci t.integer = 0; 98962306a36Sopenharmony_ci return snd_interval_refine(hw_param_interval(params, rule->var), &t); 99062306a36Sopenharmony_ci} 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_cistatic void free_cable(struct snd_pcm_substream *substream) 99362306a36Sopenharmony_ci{ 99462306a36Sopenharmony_ci struct loopback *loopback = substream->private_data; 99562306a36Sopenharmony_ci int dev = get_cable_index(substream); 99662306a36Sopenharmony_ci struct loopback_cable *cable; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci cable = loopback->cables[substream->number][dev]; 99962306a36Sopenharmony_ci if (!cable) 100062306a36Sopenharmony_ci return; 100162306a36Sopenharmony_ci if (cable->streams[!substream->stream]) { 100262306a36Sopenharmony_ci /* other stream is still alive */ 100362306a36Sopenharmony_ci spin_lock_irq(&cable->lock); 100462306a36Sopenharmony_ci cable->streams[substream->stream] = NULL; 100562306a36Sopenharmony_ci spin_unlock_irq(&cable->lock); 100662306a36Sopenharmony_ci } else { 100762306a36Sopenharmony_ci struct loopback_pcm *dpcm = substream->runtime->private_data; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci if (cable->ops && cable->ops->close_cable && dpcm) 101062306a36Sopenharmony_ci cable->ops->close_cable(dpcm); 101162306a36Sopenharmony_ci /* free the cable */ 101262306a36Sopenharmony_ci loopback->cables[substream->number][dev] = NULL; 101362306a36Sopenharmony_ci kfree(cable); 101462306a36Sopenharmony_ci } 101562306a36Sopenharmony_ci} 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_cistatic int loopback_jiffies_timer_open(struct loopback_pcm *dpcm) 101862306a36Sopenharmony_ci{ 101962306a36Sopenharmony_ci timer_setup(&dpcm->timer, loopback_jiffies_timer_function, 0); 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci return 0; 102262306a36Sopenharmony_ci} 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_cistatic const struct loopback_ops loopback_jiffies_timer_ops = { 102562306a36Sopenharmony_ci .open = loopback_jiffies_timer_open, 102662306a36Sopenharmony_ci .start = loopback_jiffies_timer_start, 102762306a36Sopenharmony_ci .stop = loopback_jiffies_timer_stop, 102862306a36Sopenharmony_ci .stop_sync = loopback_jiffies_timer_stop_sync, 102962306a36Sopenharmony_ci .close_substream = loopback_jiffies_timer_stop_sync, 103062306a36Sopenharmony_ci .pos_update = loopback_jiffies_timer_pos_update, 103162306a36Sopenharmony_ci .dpcm_info = loopback_jiffies_timer_dpcm_info, 103262306a36Sopenharmony_ci}; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_cistatic int loopback_parse_timer_id(const char *str, 103562306a36Sopenharmony_ci struct snd_timer_id *tid) 103662306a36Sopenharmony_ci{ 103762306a36Sopenharmony_ci /* [<pref>:](<card name>|<card idx>)[{.,}<dev idx>[{.,}<subdev idx>]] */ 103862306a36Sopenharmony_ci const char * const sep_dev = ".,"; 103962306a36Sopenharmony_ci const char * const sep_pref = ":"; 104062306a36Sopenharmony_ci const char *name = str; 104162306a36Sopenharmony_ci char *sep, save = '\0'; 104262306a36Sopenharmony_ci int card_idx = 0, dev = 0, subdev = 0; 104362306a36Sopenharmony_ci int err; 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci sep = strpbrk(str, sep_pref); 104662306a36Sopenharmony_ci if (sep) 104762306a36Sopenharmony_ci name = sep + 1; 104862306a36Sopenharmony_ci sep = strpbrk(name, sep_dev); 104962306a36Sopenharmony_ci if (sep) { 105062306a36Sopenharmony_ci save = *sep; 105162306a36Sopenharmony_ci *sep = '\0'; 105262306a36Sopenharmony_ci } 105362306a36Sopenharmony_ci err = kstrtoint(name, 0, &card_idx); 105462306a36Sopenharmony_ci if (err == -EINVAL) { 105562306a36Sopenharmony_ci /* Must be the name, not number */ 105662306a36Sopenharmony_ci for (card_idx = 0; card_idx < snd_ecards_limit; card_idx++) { 105762306a36Sopenharmony_ci struct snd_card *card = snd_card_ref(card_idx); 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci if (card) { 106062306a36Sopenharmony_ci if (!strcmp(card->id, name)) 106162306a36Sopenharmony_ci err = 0; 106262306a36Sopenharmony_ci snd_card_unref(card); 106362306a36Sopenharmony_ci } 106462306a36Sopenharmony_ci if (!err) 106562306a36Sopenharmony_ci break; 106662306a36Sopenharmony_ci } 106762306a36Sopenharmony_ci } 106862306a36Sopenharmony_ci if (sep) { 106962306a36Sopenharmony_ci *sep = save; 107062306a36Sopenharmony_ci if (!err) { 107162306a36Sopenharmony_ci char *sep2, save2 = '\0'; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci sep2 = strpbrk(sep + 1, sep_dev); 107462306a36Sopenharmony_ci if (sep2) { 107562306a36Sopenharmony_ci save2 = *sep2; 107662306a36Sopenharmony_ci *sep2 = '\0'; 107762306a36Sopenharmony_ci } 107862306a36Sopenharmony_ci err = kstrtoint(sep + 1, 0, &dev); 107962306a36Sopenharmony_ci if (sep2) { 108062306a36Sopenharmony_ci *sep2 = save2; 108162306a36Sopenharmony_ci if (!err) 108262306a36Sopenharmony_ci err = kstrtoint(sep2 + 1, 0, &subdev); 108362306a36Sopenharmony_ci } 108462306a36Sopenharmony_ci } 108562306a36Sopenharmony_ci } 108662306a36Sopenharmony_ci if (!err && tid) { 108762306a36Sopenharmony_ci tid->card = card_idx; 108862306a36Sopenharmony_ci tid->device = dev; 108962306a36Sopenharmony_ci tid->subdevice = subdev; 109062306a36Sopenharmony_ci } 109162306a36Sopenharmony_ci return err; 109262306a36Sopenharmony_ci} 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci/* call in loopback->cable_lock */ 109562306a36Sopenharmony_cistatic int loopback_snd_timer_open(struct loopback_pcm *dpcm) 109662306a36Sopenharmony_ci{ 109762306a36Sopenharmony_ci int err = 0; 109862306a36Sopenharmony_ci struct snd_timer_id tid = { 109962306a36Sopenharmony_ci .dev_class = SNDRV_TIMER_CLASS_PCM, 110062306a36Sopenharmony_ci .dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION, 110162306a36Sopenharmony_ci }; 110262306a36Sopenharmony_ci struct snd_timer_instance *timeri; 110362306a36Sopenharmony_ci struct loopback_cable *cable = dpcm->cable; 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci /* check if timer was already opened. It is only opened once 110662306a36Sopenharmony_ci * per playback and capture subdevice (aka cable). 110762306a36Sopenharmony_ci */ 110862306a36Sopenharmony_ci if (cable->snd_timer.instance) 110962306a36Sopenharmony_ci goto exit; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci err = loopback_parse_timer_id(dpcm->loopback->timer_source, &tid); 111262306a36Sopenharmony_ci if (err < 0) { 111362306a36Sopenharmony_ci pcm_err(dpcm->substream->pcm, 111462306a36Sopenharmony_ci "Parsing timer source \'%s\' failed with %d", 111562306a36Sopenharmony_ci dpcm->loopback->timer_source, err); 111662306a36Sopenharmony_ci goto exit; 111762306a36Sopenharmony_ci } 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci cable->snd_timer.stream = dpcm->substream->stream; 112062306a36Sopenharmony_ci cable->snd_timer.id = tid; 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci timeri = snd_timer_instance_new(dpcm->loopback->card->id); 112362306a36Sopenharmony_ci if (!timeri) { 112462306a36Sopenharmony_ci err = -ENOMEM; 112562306a36Sopenharmony_ci goto exit; 112662306a36Sopenharmony_ci } 112762306a36Sopenharmony_ci /* The callback has to be called from another work. If 112862306a36Sopenharmony_ci * SNDRV_TIMER_IFLG_FAST is specified it will be called from the 112962306a36Sopenharmony_ci * snd_pcm_period_elapsed() call of the selected sound card. 113062306a36Sopenharmony_ci * snd_pcm_period_elapsed() helds snd_pcm_stream_lock_irqsave(). 113162306a36Sopenharmony_ci * Due to our callback loopback_snd_timer_function() also calls 113262306a36Sopenharmony_ci * snd_pcm_period_elapsed() which calls snd_pcm_stream_lock_irqsave(). 113362306a36Sopenharmony_ci * This would end up in a dead lock. 113462306a36Sopenharmony_ci */ 113562306a36Sopenharmony_ci timeri->flags |= SNDRV_TIMER_IFLG_AUTO; 113662306a36Sopenharmony_ci timeri->callback = loopback_snd_timer_function; 113762306a36Sopenharmony_ci timeri->callback_data = (void *)cable; 113862306a36Sopenharmony_ci timeri->ccallback = loopback_snd_timer_event; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci /* initialise a work used for draining */ 114162306a36Sopenharmony_ci INIT_WORK(&cable->snd_timer.event_work, loopback_snd_timer_work); 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci /* The mutex loopback->cable_lock is kept locked. 114462306a36Sopenharmony_ci * Therefore snd_timer_open() cannot be called a second time 114562306a36Sopenharmony_ci * by the other device of the same cable. 114662306a36Sopenharmony_ci * Therefore the following issue cannot happen: 114762306a36Sopenharmony_ci * [proc1] Call loopback_timer_open() -> 114862306a36Sopenharmony_ci * Unlock cable->lock for snd_timer_close/open() call 114962306a36Sopenharmony_ci * [proc2] Call loopback_timer_open() -> snd_timer_open(), 115062306a36Sopenharmony_ci * snd_timer_start() 115162306a36Sopenharmony_ci * [proc1] Call snd_timer_open() and overwrite running timer 115262306a36Sopenharmony_ci * instance 115362306a36Sopenharmony_ci */ 115462306a36Sopenharmony_ci err = snd_timer_open(timeri, &cable->snd_timer.id, current->pid); 115562306a36Sopenharmony_ci if (err < 0) { 115662306a36Sopenharmony_ci pcm_err(dpcm->substream->pcm, 115762306a36Sopenharmony_ci "snd_timer_open (%d,%d,%d) failed with %d", 115862306a36Sopenharmony_ci cable->snd_timer.id.card, 115962306a36Sopenharmony_ci cable->snd_timer.id.device, 116062306a36Sopenharmony_ci cable->snd_timer.id.subdevice, 116162306a36Sopenharmony_ci err); 116262306a36Sopenharmony_ci snd_timer_instance_free(timeri); 116362306a36Sopenharmony_ci goto exit; 116462306a36Sopenharmony_ci } 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci cable->snd_timer.instance = timeri; 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ciexit: 116962306a36Sopenharmony_ci return err; 117062306a36Sopenharmony_ci} 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci/* stop_sync() is not required for sound timer because it does not need to be 117362306a36Sopenharmony_ci * restarted in loopback_prepare() on Xrun recovery 117462306a36Sopenharmony_ci */ 117562306a36Sopenharmony_cistatic const struct loopback_ops loopback_snd_timer_ops = { 117662306a36Sopenharmony_ci .open = loopback_snd_timer_open, 117762306a36Sopenharmony_ci .start = loopback_snd_timer_start, 117862306a36Sopenharmony_ci .stop = loopback_snd_timer_stop, 117962306a36Sopenharmony_ci .close_cable = loopback_snd_timer_close_cable, 118062306a36Sopenharmony_ci .dpcm_info = loopback_snd_timer_dpcm_info, 118162306a36Sopenharmony_ci}; 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_cistatic int loopback_open(struct snd_pcm_substream *substream) 118462306a36Sopenharmony_ci{ 118562306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 118662306a36Sopenharmony_ci struct loopback *loopback = substream->private_data; 118762306a36Sopenharmony_ci struct loopback_pcm *dpcm; 118862306a36Sopenharmony_ci struct loopback_cable *cable = NULL; 118962306a36Sopenharmony_ci int err = 0; 119062306a36Sopenharmony_ci int dev = get_cable_index(substream); 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci mutex_lock(&loopback->cable_lock); 119362306a36Sopenharmony_ci dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL); 119462306a36Sopenharmony_ci if (!dpcm) { 119562306a36Sopenharmony_ci err = -ENOMEM; 119662306a36Sopenharmony_ci goto unlock; 119762306a36Sopenharmony_ci } 119862306a36Sopenharmony_ci dpcm->loopback = loopback; 119962306a36Sopenharmony_ci dpcm->substream = substream; 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci cable = loopback->cables[substream->number][dev]; 120262306a36Sopenharmony_ci if (!cable) { 120362306a36Sopenharmony_ci cable = kzalloc(sizeof(*cable), GFP_KERNEL); 120462306a36Sopenharmony_ci if (!cable) { 120562306a36Sopenharmony_ci err = -ENOMEM; 120662306a36Sopenharmony_ci goto unlock; 120762306a36Sopenharmony_ci } 120862306a36Sopenharmony_ci spin_lock_init(&cable->lock); 120962306a36Sopenharmony_ci cable->hw = loopback_pcm_hardware; 121062306a36Sopenharmony_ci if (loopback->timer_source) 121162306a36Sopenharmony_ci cable->ops = &loopback_snd_timer_ops; 121262306a36Sopenharmony_ci else 121362306a36Sopenharmony_ci cable->ops = &loopback_jiffies_timer_ops; 121462306a36Sopenharmony_ci loopback->cables[substream->number][dev] = cable; 121562306a36Sopenharmony_ci } 121662306a36Sopenharmony_ci dpcm->cable = cable; 121762306a36Sopenharmony_ci runtime->private_data = dpcm; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci if (cable->ops->open) { 122062306a36Sopenharmony_ci err = cable->ops->open(dpcm); 122162306a36Sopenharmony_ci if (err < 0) 122262306a36Sopenharmony_ci goto unlock; 122362306a36Sopenharmony_ci } 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci /* use dynamic rules based on actual runtime->hw values */ 122862306a36Sopenharmony_ci /* note that the default rules created in the PCM midlevel code */ 122962306a36Sopenharmony_ci /* are cached -> they do not reflect the actual state */ 123062306a36Sopenharmony_ci err = snd_pcm_hw_rule_add(runtime, 0, 123162306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_FORMAT, 123262306a36Sopenharmony_ci rule_format, dpcm, 123362306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_FORMAT, -1); 123462306a36Sopenharmony_ci if (err < 0) 123562306a36Sopenharmony_ci goto unlock; 123662306a36Sopenharmony_ci err = snd_pcm_hw_rule_add(runtime, 0, 123762306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, 123862306a36Sopenharmony_ci rule_rate, dpcm, 123962306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, -1); 124062306a36Sopenharmony_ci if (err < 0) 124162306a36Sopenharmony_ci goto unlock; 124262306a36Sopenharmony_ci err = snd_pcm_hw_rule_add(runtime, 0, 124362306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_CHANNELS, 124462306a36Sopenharmony_ci rule_channels, dpcm, 124562306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_CHANNELS, -1); 124662306a36Sopenharmony_ci if (err < 0) 124762306a36Sopenharmony_ci goto unlock; 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci /* In case of sound timer the period time of both devices of the same 125062306a36Sopenharmony_ci * loop has to be the same. 125162306a36Sopenharmony_ci * This rule only takes effect if a sound timer was chosen 125262306a36Sopenharmony_ci */ 125362306a36Sopenharmony_ci if (cable->snd_timer.instance) { 125462306a36Sopenharmony_ci err = snd_pcm_hw_rule_add(runtime, 0, 125562306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 125662306a36Sopenharmony_ci rule_period_bytes, dpcm, 125762306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_PERIOD_BYTES, -1); 125862306a36Sopenharmony_ci if (err < 0) 125962306a36Sopenharmony_ci goto unlock; 126062306a36Sopenharmony_ci } 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci /* loopback_runtime_free() has not to be called if kfree(dpcm) was 126362306a36Sopenharmony_ci * already called here. Otherwise it will end up with a double free. 126462306a36Sopenharmony_ci */ 126562306a36Sopenharmony_ci runtime->private_free = loopback_runtime_free; 126662306a36Sopenharmony_ci if (get_notify(dpcm)) 126762306a36Sopenharmony_ci runtime->hw = loopback_pcm_hardware; 126862306a36Sopenharmony_ci else 126962306a36Sopenharmony_ci runtime->hw = cable->hw; 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci spin_lock_irq(&cable->lock); 127262306a36Sopenharmony_ci cable->streams[substream->stream] = dpcm; 127362306a36Sopenharmony_ci spin_unlock_irq(&cable->lock); 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci unlock: 127662306a36Sopenharmony_ci if (err < 0) { 127762306a36Sopenharmony_ci free_cable(substream); 127862306a36Sopenharmony_ci kfree(dpcm); 127962306a36Sopenharmony_ci } 128062306a36Sopenharmony_ci mutex_unlock(&loopback->cable_lock); 128162306a36Sopenharmony_ci return err; 128262306a36Sopenharmony_ci} 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_cistatic int loopback_close(struct snd_pcm_substream *substream) 128562306a36Sopenharmony_ci{ 128662306a36Sopenharmony_ci struct loopback *loopback = substream->private_data; 128762306a36Sopenharmony_ci struct loopback_pcm *dpcm = substream->runtime->private_data; 128862306a36Sopenharmony_ci int err = 0; 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci if (dpcm->cable->ops->close_substream) 129162306a36Sopenharmony_ci err = dpcm->cable->ops->close_substream(dpcm); 129262306a36Sopenharmony_ci mutex_lock(&loopback->cable_lock); 129362306a36Sopenharmony_ci free_cable(substream); 129462306a36Sopenharmony_ci mutex_unlock(&loopback->cable_lock); 129562306a36Sopenharmony_ci return err; 129662306a36Sopenharmony_ci} 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_cistatic const struct snd_pcm_ops loopback_pcm_ops = { 129962306a36Sopenharmony_ci .open = loopback_open, 130062306a36Sopenharmony_ci .close = loopback_close, 130162306a36Sopenharmony_ci .hw_free = loopback_hw_free, 130262306a36Sopenharmony_ci .prepare = loopback_prepare, 130362306a36Sopenharmony_ci .trigger = loopback_trigger, 130462306a36Sopenharmony_ci .pointer = loopback_pointer, 130562306a36Sopenharmony_ci}; 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_cistatic int loopback_pcm_new(struct loopback *loopback, 130862306a36Sopenharmony_ci int device, int substreams) 130962306a36Sopenharmony_ci{ 131062306a36Sopenharmony_ci struct snd_pcm *pcm; 131162306a36Sopenharmony_ci int err; 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci err = snd_pcm_new(loopback->card, "Loopback PCM", device, 131462306a36Sopenharmony_ci substreams, substreams, &pcm); 131562306a36Sopenharmony_ci if (err < 0) 131662306a36Sopenharmony_ci return err; 131762306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &loopback_pcm_ops); 131862306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &loopback_pcm_ops); 131962306a36Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0); 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci pcm->private_data = loopback; 132262306a36Sopenharmony_ci pcm->info_flags = 0; 132362306a36Sopenharmony_ci strcpy(pcm->name, "Loopback PCM"); 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci loopback->pcm[device] = pcm; 132662306a36Sopenharmony_ci return 0; 132762306a36Sopenharmony_ci} 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_cistatic int loopback_rate_shift_info(struct snd_kcontrol *kcontrol, 133062306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 133162306a36Sopenharmony_ci{ 133262306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 133362306a36Sopenharmony_ci uinfo->count = 1; 133462306a36Sopenharmony_ci uinfo->value.integer.min = 80000; 133562306a36Sopenharmony_ci uinfo->value.integer.max = 120000; 133662306a36Sopenharmony_ci uinfo->value.integer.step = 1; 133762306a36Sopenharmony_ci return 0; 133862306a36Sopenharmony_ci} 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_cistatic int loopback_rate_shift_get(struct snd_kcontrol *kcontrol, 134162306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 134262306a36Sopenharmony_ci{ 134362306a36Sopenharmony_ci struct loopback *loopback = snd_kcontrol_chip(kcontrol); 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci mutex_lock(&loopback->cable_lock); 134662306a36Sopenharmony_ci ucontrol->value.integer.value[0] = 134762306a36Sopenharmony_ci loopback->setup[kcontrol->id.subdevice] 134862306a36Sopenharmony_ci [kcontrol->id.device].rate_shift; 134962306a36Sopenharmony_ci mutex_unlock(&loopback->cable_lock); 135062306a36Sopenharmony_ci return 0; 135162306a36Sopenharmony_ci} 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_cistatic int loopback_rate_shift_put(struct snd_kcontrol *kcontrol, 135462306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 135562306a36Sopenharmony_ci{ 135662306a36Sopenharmony_ci struct loopback *loopback = snd_kcontrol_chip(kcontrol); 135762306a36Sopenharmony_ci unsigned int val; 135862306a36Sopenharmony_ci int change = 0; 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci val = ucontrol->value.integer.value[0]; 136162306a36Sopenharmony_ci if (val < 80000) 136262306a36Sopenharmony_ci val = 80000; 136362306a36Sopenharmony_ci if (val > 120000) 136462306a36Sopenharmony_ci val = 120000; 136562306a36Sopenharmony_ci mutex_lock(&loopback->cable_lock); 136662306a36Sopenharmony_ci if (val != loopback->setup[kcontrol->id.subdevice] 136762306a36Sopenharmony_ci [kcontrol->id.device].rate_shift) { 136862306a36Sopenharmony_ci loopback->setup[kcontrol->id.subdevice] 136962306a36Sopenharmony_ci [kcontrol->id.device].rate_shift = val; 137062306a36Sopenharmony_ci change = 1; 137162306a36Sopenharmony_ci } 137262306a36Sopenharmony_ci mutex_unlock(&loopback->cable_lock); 137362306a36Sopenharmony_ci return change; 137462306a36Sopenharmony_ci} 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_cistatic int loopback_notify_get(struct snd_kcontrol *kcontrol, 137762306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 137862306a36Sopenharmony_ci{ 137962306a36Sopenharmony_ci struct loopback *loopback = snd_kcontrol_chip(kcontrol); 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci mutex_lock(&loopback->cable_lock); 138262306a36Sopenharmony_ci ucontrol->value.integer.value[0] = 138362306a36Sopenharmony_ci loopback->setup[kcontrol->id.subdevice] 138462306a36Sopenharmony_ci [kcontrol->id.device].notify; 138562306a36Sopenharmony_ci mutex_unlock(&loopback->cable_lock); 138662306a36Sopenharmony_ci return 0; 138762306a36Sopenharmony_ci} 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_cistatic int loopback_notify_put(struct snd_kcontrol *kcontrol, 139062306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 139162306a36Sopenharmony_ci{ 139262306a36Sopenharmony_ci struct loopback *loopback = snd_kcontrol_chip(kcontrol); 139362306a36Sopenharmony_ci unsigned int val; 139462306a36Sopenharmony_ci int change = 0; 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci val = ucontrol->value.integer.value[0] ? 1 : 0; 139762306a36Sopenharmony_ci mutex_lock(&loopback->cable_lock); 139862306a36Sopenharmony_ci if (val != loopback->setup[kcontrol->id.subdevice] 139962306a36Sopenharmony_ci [kcontrol->id.device].notify) { 140062306a36Sopenharmony_ci loopback->setup[kcontrol->id.subdevice] 140162306a36Sopenharmony_ci [kcontrol->id.device].notify = val; 140262306a36Sopenharmony_ci change = 1; 140362306a36Sopenharmony_ci } 140462306a36Sopenharmony_ci mutex_unlock(&loopback->cable_lock); 140562306a36Sopenharmony_ci return change; 140662306a36Sopenharmony_ci} 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_cistatic int loopback_active_get(struct snd_kcontrol *kcontrol, 140962306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 141062306a36Sopenharmony_ci{ 141162306a36Sopenharmony_ci struct loopback *loopback = snd_kcontrol_chip(kcontrol); 141262306a36Sopenharmony_ci struct loopback_cable *cable; 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci unsigned int val = 0; 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci mutex_lock(&loopback->cable_lock); 141762306a36Sopenharmony_ci cable = loopback->cables[kcontrol->id.subdevice][kcontrol->id.device ^ 1]; 141862306a36Sopenharmony_ci if (cable != NULL) { 141962306a36Sopenharmony_ci unsigned int running = cable->running ^ cable->pause; 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci val = (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ? 1 : 0; 142262306a36Sopenharmony_ci } 142362306a36Sopenharmony_ci mutex_unlock(&loopback->cable_lock); 142462306a36Sopenharmony_ci ucontrol->value.integer.value[0] = val; 142562306a36Sopenharmony_ci return 0; 142662306a36Sopenharmony_ci} 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_cistatic int loopback_format_info(struct snd_kcontrol *kcontrol, 142962306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 143062306a36Sopenharmony_ci{ 143162306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 143262306a36Sopenharmony_ci uinfo->count = 1; 143362306a36Sopenharmony_ci uinfo->value.integer.min = 0; 143462306a36Sopenharmony_ci uinfo->value.integer.max = (__force int)SNDRV_PCM_FORMAT_LAST; 143562306a36Sopenharmony_ci uinfo->value.integer.step = 1; 143662306a36Sopenharmony_ci return 0; 143762306a36Sopenharmony_ci} 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_cistatic int loopback_format_get(struct snd_kcontrol *kcontrol, 144062306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 144162306a36Sopenharmony_ci{ 144262306a36Sopenharmony_ci struct loopback *loopback = snd_kcontrol_chip(kcontrol); 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci ucontrol->value.integer.value[0] = 144562306a36Sopenharmony_ci (__force int)loopback->setup[kcontrol->id.subdevice] 144662306a36Sopenharmony_ci [kcontrol->id.device].format; 144762306a36Sopenharmony_ci return 0; 144862306a36Sopenharmony_ci} 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_cistatic int loopback_rate_info(struct snd_kcontrol *kcontrol, 145162306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 145262306a36Sopenharmony_ci{ 145362306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 145462306a36Sopenharmony_ci uinfo->count = 1; 145562306a36Sopenharmony_ci uinfo->value.integer.min = 0; 145662306a36Sopenharmony_ci uinfo->value.integer.max = 192000; 145762306a36Sopenharmony_ci uinfo->value.integer.step = 1; 145862306a36Sopenharmony_ci return 0; 145962306a36Sopenharmony_ci} 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_cistatic int loopback_rate_get(struct snd_kcontrol *kcontrol, 146262306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 146362306a36Sopenharmony_ci{ 146462306a36Sopenharmony_ci struct loopback *loopback = snd_kcontrol_chip(kcontrol); 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci mutex_lock(&loopback->cable_lock); 146762306a36Sopenharmony_ci ucontrol->value.integer.value[0] = 146862306a36Sopenharmony_ci loopback->setup[kcontrol->id.subdevice] 146962306a36Sopenharmony_ci [kcontrol->id.device].rate; 147062306a36Sopenharmony_ci mutex_unlock(&loopback->cable_lock); 147162306a36Sopenharmony_ci return 0; 147262306a36Sopenharmony_ci} 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_cistatic int loopback_channels_info(struct snd_kcontrol *kcontrol, 147562306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 147662306a36Sopenharmony_ci{ 147762306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 147862306a36Sopenharmony_ci uinfo->count = 1; 147962306a36Sopenharmony_ci uinfo->value.integer.min = 1; 148062306a36Sopenharmony_ci uinfo->value.integer.max = 1024; 148162306a36Sopenharmony_ci uinfo->value.integer.step = 1; 148262306a36Sopenharmony_ci return 0; 148362306a36Sopenharmony_ci} 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_cistatic int loopback_channels_get(struct snd_kcontrol *kcontrol, 148662306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 148762306a36Sopenharmony_ci{ 148862306a36Sopenharmony_ci struct loopback *loopback = snd_kcontrol_chip(kcontrol); 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci mutex_lock(&loopback->cable_lock); 149162306a36Sopenharmony_ci ucontrol->value.integer.value[0] = 149262306a36Sopenharmony_ci loopback->setup[kcontrol->id.subdevice] 149362306a36Sopenharmony_ci [kcontrol->id.device].channels; 149462306a36Sopenharmony_ci mutex_unlock(&loopback->cable_lock); 149562306a36Sopenharmony_ci return 0; 149662306a36Sopenharmony_ci} 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_cistatic const struct snd_kcontrol_new loopback_controls[] = { 149962306a36Sopenharmony_ci{ 150062306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 150162306a36Sopenharmony_ci .name = "PCM Rate Shift 100000", 150262306a36Sopenharmony_ci .info = loopback_rate_shift_info, 150362306a36Sopenharmony_ci .get = loopback_rate_shift_get, 150462306a36Sopenharmony_ci .put = loopback_rate_shift_put, 150562306a36Sopenharmony_ci}, 150662306a36Sopenharmony_ci{ 150762306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 150862306a36Sopenharmony_ci .name = "PCM Notify", 150962306a36Sopenharmony_ci .info = snd_ctl_boolean_mono_info, 151062306a36Sopenharmony_ci .get = loopback_notify_get, 151162306a36Sopenharmony_ci .put = loopback_notify_put, 151262306a36Sopenharmony_ci}, 151362306a36Sopenharmony_ci#define ACTIVE_IDX 2 151462306a36Sopenharmony_ci{ 151562306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 151662306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 151762306a36Sopenharmony_ci .name = "PCM Slave Active", 151862306a36Sopenharmony_ci .info = snd_ctl_boolean_mono_info, 151962306a36Sopenharmony_ci .get = loopback_active_get, 152062306a36Sopenharmony_ci}, 152162306a36Sopenharmony_ci#define FORMAT_IDX 3 152262306a36Sopenharmony_ci{ 152362306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 152462306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 152562306a36Sopenharmony_ci .name = "PCM Slave Format", 152662306a36Sopenharmony_ci .info = loopback_format_info, 152762306a36Sopenharmony_ci .get = loopback_format_get 152862306a36Sopenharmony_ci}, 152962306a36Sopenharmony_ci#define RATE_IDX 4 153062306a36Sopenharmony_ci{ 153162306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 153262306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 153362306a36Sopenharmony_ci .name = "PCM Slave Rate", 153462306a36Sopenharmony_ci .info = loopback_rate_info, 153562306a36Sopenharmony_ci .get = loopback_rate_get 153662306a36Sopenharmony_ci}, 153762306a36Sopenharmony_ci#define CHANNELS_IDX 5 153862306a36Sopenharmony_ci{ 153962306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 154062306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 154162306a36Sopenharmony_ci .name = "PCM Slave Channels", 154262306a36Sopenharmony_ci .info = loopback_channels_info, 154362306a36Sopenharmony_ci .get = loopback_channels_get 154462306a36Sopenharmony_ci} 154562306a36Sopenharmony_ci}; 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_cistatic int loopback_mixer_new(struct loopback *loopback, int notify) 154862306a36Sopenharmony_ci{ 154962306a36Sopenharmony_ci struct snd_card *card = loopback->card; 155062306a36Sopenharmony_ci struct snd_pcm *pcm; 155162306a36Sopenharmony_ci struct snd_kcontrol *kctl; 155262306a36Sopenharmony_ci struct loopback_setup *setup; 155362306a36Sopenharmony_ci int err, dev, substr, substr_count, idx; 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci strcpy(card->mixername, "Loopback Mixer"); 155662306a36Sopenharmony_ci for (dev = 0; dev < 2; dev++) { 155762306a36Sopenharmony_ci pcm = loopback->pcm[dev]; 155862306a36Sopenharmony_ci substr_count = 155962306a36Sopenharmony_ci pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count; 156062306a36Sopenharmony_ci for (substr = 0; substr < substr_count; substr++) { 156162306a36Sopenharmony_ci setup = &loopback->setup[substr][dev]; 156262306a36Sopenharmony_ci setup->notify = notify; 156362306a36Sopenharmony_ci setup->rate_shift = NO_PITCH; 156462306a36Sopenharmony_ci setup->format = SNDRV_PCM_FORMAT_S16_LE; 156562306a36Sopenharmony_ci setup->rate = 48000; 156662306a36Sopenharmony_ci setup->channels = 2; 156762306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(loopback_controls); 156862306a36Sopenharmony_ci idx++) { 156962306a36Sopenharmony_ci kctl = snd_ctl_new1(&loopback_controls[idx], 157062306a36Sopenharmony_ci loopback); 157162306a36Sopenharmony_ci if (!kctl) 157262306a36Sopenharmony_ci return -ENOMEM; 157362306a36Sopenharmony_ci kctl->id.device = dev; 157462306a36Sopenharmony_ci kctl->id.subdevice = substr; 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci /* Add the control before copying the id so that 157762306a36Sopenharmony_ci * the numid field of the id is set in the copy. 157862306a36Sopenharmony_ci */ 157962306a36Sopenharmony_ci err = snd_ctl_add(card, kctl); 158062306a36Sopenharmony_ci if (err < 0) 158162306a36Sopenharmony_ci return err; 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_ci switch (idx) { 158462306a36Sopenharmony_ci case ACTIVE_IDX: 158562306a36Sopenharmony_ci setup->active_id = kctl->id; 158662306a36Sopenharmony_ci break; 158762306a36Sopenharmony_ci case FORMAT_IDX: 158862306a36Sopenharmony_ci setup->format_id = kctl->id; 158962306a36Sopenharmony_ci break; 159062306a36Sopenharmony_ci case RATE_IDX: 159162306a36Sopenharmony_ci setup->rate_id = kctl->id; 159262306a36Sopenharmony_ci break; 159362306a36Sopenharmony_ci case CHANNELS_IDX: 159462306a36Sopenharmony_ci setup->channels_id = kctl->id; 159562306a36Sopenharmony_ci break; 159662306a36Sopenharmony_ci default: 159762306a36Sopenharmony_ci break; 159862306a36Sopenharmony_ci } 159962306a36Sopenharmony_ci } 160062306a36Sopenharmony_ci } 160162306a36Sopenharmony_ci } 160262306a36Sopenharmony_ci return 0; 160362306a36Sopenharmony_ci} 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_cistatic void print_dpcm_info(struct snd_info_buffer *buffer, 160662306a36Sopenharmony_ci struct loopback_pcm *dpcm, 160762306a36Sopenharmony_ci const char *id) 160862306a36Sopenharmony_ci{ 160962306a36Sopenharmony_ci snd_iprintf(buffer, " %s\n", id); 161062306a36Sopenharmony_ci if (dpcm == NULL) { 161162306a36Sopenharmony_ci snd_iprintf(buffer, " inactive\n"); 161262306a36Sopenharmony_ci return; 161362306a36Sopenharmony_ci } 161462306a36Sopenharmony_ci snd_iprintf(buffer, " buffer_size:\t%u\n", dpcm->pcm_buffer_size); 161562306a36Sopenharmony_ci snd_iprintf(buffer, " buffer_pos:\t\t%u\n", dpcm->buf_pos); 161662306a36Sopenharmony_ci snd_iprintf(buffer, " silent_size:\t%u\n", dpcm->silent_size); 161762306a36Sopenharmony_ci snd_iprintf(buffer, " period_size:\t%u\n", dpcm->pcm_period_size); 161862306a36Sopenharmony_ci snd_iprintf(buffer, " bytes_per_sec:\t%u\n", dpcm->pcm_bps); 161962306a36Sopenharmony_ci snd_iprintf(buffer, " sample_align:\t%u\n", dpcm->pcm_salign); 162062306a36Sopenharmony_ci snd_iprintf(buffer, " rate_shift:\t\t%u\n", dpcm->pcm_rate_shift); 162162306a36Sopenharmony_ci if (dpcm->cable->ops->dpcm_info) 162262306a36Sopenharmony_ci dpcm->cable->ops->dpcm_info(dpcm, buffer); 162362306a36Sopenharmony_ci} 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_cistatic void print_substream_info(struct snd_info_buffer *buffer, 162662306a36Sopenharmony_ci struct loopback *loopback, 162762306a36Sopenharmony_ci int sub, 162862306a36Sopenharmony_ci int num) 162962306a36Sopenharmony_ci{ 163062306a36Sopenharmony_ci struct loopback_cable *cable = loopback->cables[sub][num]; 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci snd_iprintf(buffer, "Cable %i substream %i:\n", num, sub); 163362306a36Sopenharmony_ci if (cable == NULL) { 163462306a36Sopenharmony_ci snd_iprintf(buffer, " inactive\n"); 163562306a36Sopenharmony_ci return; 163662306a36Sopenharmony_ci } 163762306a36Sopenharmony_ci snd_iprintf(buffer, " valid: %u\n", cable->valid); 163862306a36Sopenharmony_ci snd_iprintf(buffer, " running: %u\n", cable->running); 163962306a36Sopenharmony_ci snd_iprintf(buffer, " pause: %u\n", cable->pause); 164062306a36Sopenharmony_ci print_dpcm_info(buffer, cable->streams[0], "Playback"); 164162306a36Sopenharmony_ci print_dpcm_info(buffer, cable->streams[1], "Capture"); 164262306a36Sopenharmony_ci} 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_cistatic void print_cable_info(struct snd_info_entry *entry, 164562306a36Sopenharmony_ci struct snd_info_buffer *buffer) 164662306a36Sopenharmony_ci{ 164762306a36Sopenharmony_ci struct loopback *loopback = entry->private_data; 164862306a36Sopenharmony_ci int sub, num; 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci mutex_lock(&loopback->cable_lock); 165162306a36Sopenharmony_ci num = entry->name[strlen(entry->name)-1]; 165262306a36Sopenharmony_ci num = num == '0' ? 0 : 1; 165362306a36Sopenharmony_ci for (sub = 0; sub < MAX_PCM_SUBSTREAMS; sub++) 165462306a36Sopenharmony_ci print_substream_info(buffer, loopback, sub, num); 165562306a36Sopenharmony_ci mutex_unlock(&loopback->cable_lock); 165662306a36Sopenharmony_ci} 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_cistatic int loopback_cable_proc_new(struct loopback *loopback, int cidx) 165962306a36Sopenharmony_ci{ 166062306a36Sopenharmony_ci char name[32]; 166162306a36Sopenharmony_ci 166262306a36Sopenharmony_ci snprintf(name, sizeof(name), "cable#%d", cidx); 166362306a36Sopenharmony_ci return snd_card_ro_proc_new(loopback->card, name, loopback, 166462306a36Sopenharmony_ci print_cable_info); 166562306a36Sopenharmony_ci} 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_cistatic void loopback_set_timer_source(struct loopback *loopback, 166862306a36Sopenharmony_ci const char *value) 166962306a36Sopenharmony_ci{ 167062306a36Sopenharmony_ci if (loopback->timer_source) { 167162306a36Sopenharmony_ci devm_kfree(loopback->card->dev, loopback->timer_source); 167262306a36Sopenharmony_ci loopback->timer_source = NULL; 167362306a36Sopenharmony_ci } 167462306a36Sopenharmony_ci if (value && *value) 167562306a36Sopenharmony_ci loopback->timer_source = devm_kstrdup(loopback->card->dev, 167662306a36Sopenharmony_ci value, GFP_KERNEL); 167762306a36Sopenharmony_ci} 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_cistatic void print_timer_source_info(struct snd_info_entry *entry, 168062306a36Sopenharmony_ci struct snd_info_buffer *buffer) 168162306a36Sopenharmony_ci{ 168262306a36Sopenharmony_ci struct loopback *loopback = entry->private_data; 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_ci mutex_lock(&loopback->cable_lock); 168562306a36Sopenharmony_ci snd_iprintf(buffer, "%s\n", 168662306a36Sopenharmony_ci loopback->timer_source ? loopback->timer_source : ""); 168762306a36Sopenharmony_ci mutex_unlock(&loopback->cable_lock); 168862306a36Sopenharmony_ci} 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_cistatic void change_timer_source_info(struct snd_info_entry *entry, 169162306a36Sopenharmony_ci struct snd_info_buffer *buffer) 169262306a36Sopenharmony_ci{ 169362306a36Sopenharmony_ci struct loopback *loopback = entry->private_data; 169462306a36Sopenharmony_ci char line[64]; 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_ci mutex_lock(&loopback->cable_lock); 169762306a36Sopenharmony_ci if (!snd_info_get_line(buffer, line, sizeof(line))) 169862306a36Sopenharmony_ci loopback_set_timer_source(loopback, strim(line)); 169962306a36Sopenharmony_ci mutex_unlock(&loopback->cable_lock); 170062306a36Sopenharmony_ci} 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_cistatic int loopback_timer_source_proc_new(struct loopback *loopback) 170362306a36Sopenharmony_ci{ 170462306a36Sopenharmony_ci return snd_card_rw_proc_new(loopback->card, "timer_source", loopback, 170562306a36Sopenharmony_ci print_timer_source_info, 170662306a36Sopenharmony_ci change_timer_source_info); 170762306a36Sopenharmony_ci} 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_cistatic int loopback_probe(struct platform_device *devptr) 171062306a36Sopenharmony_ci{ 171162306a36Sopenharmony_ci struct snd_card *card; 171262306a36Sopenharmony_ci struct loopback *loopback; 171362306a36Sopenharmony_ci int dev = devptr->id; 171462306a36Sopenharmony_ci int err; 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ci err = snd_devm_card_new(&devptr->dev, index[dev], id[dev], THIS_MODULE, 171762306a36Sopenharmony_ci sizeof(struct loopback), &card); 171862306a36Sopenharmony_ci if (err < 0) 171962306a36Sopenharmony_ci return err; 172062306a36Sopenharmony_ci loopback = card->private_data; 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ci if (pcm_substreams[dev] < 1) 172362306a36Sopenharmony_ci pcm_substreams[dev] = 1; 172462306a36Sopenharmony_ci if (pcm_substreams[dev] > MAX_PCM_SUBSTREAMS) 172562306a36Sopenharmony_ci pcm_substreams[dev] = MAX_PCM_SUBSTREAMS; 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_ci loopback->card = card; 172862306a36Sopenharmony_ci loopback_set_timer_source(loopback, timer_source[dev]); 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_ci mutex_init(&loopback->cable_lock); 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_ci err = loopback_pcm_new(loopback, 0, pcm_substreams[dev]); 173362306a36Sopenharmony_ci if (err < 0) 173462306a36Sopenharmony_ci return err; 173562306a36Sopenharmony_ci err = loopback_pcm_new(loopback, 1, pcm_substreams[dev]); 173662306a36Sopenharmony_ci if (err < 0) 173762306a36Sopenharmony_ci return err; 173862306a36Sopenharmony_ci err = loopback_mixer_new(loopback, pcm_notify[dev] ? 1 : 0); 173962306a36Sopenharmony_ci if (err < 0) 174062306a36Sopenharmony_ci return err; 174162306a36Sopenharmony_ci loopback_cable_proc_new(loopback, 0); 174262306a36Sopenharmony_ci loopback_cable_proc_new(loopback, 1); 174362306a36Sopenharmony_ci loopback_timer_source_proc_new(loopback); 174462306a36Sopenharmony_ci strcpy(card->driver, "Loopback"); 174562306a36Sopenharmony_ci strcpy(card->shortname, "Loopback"); 174662306a36Sopenharmony_ci sprintf(card->longname, "Loopback %i", dev + 1); 174762306a36Sopenharmony_ci err = snd_card_register(card); 174862306a36Sopenharmony_ci if (err < 0) 174962306a36Sopenharmony_ci return err; 175062306a36Sopenharmony_ci platform_set_drvdata(devptr, card); 175162306a36Sopenharmony_ci return 0; 175262306a36Sopenharmony_ci} 175362306a36Sopenharmony_ci 175462306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 175562306a36Sopenharmony_cistatic int loopback_suspend(struct device *pdev) 175662306a36Sopenharmony_ci{ 175762306a36Sopenharmony_ci struct snd_card *card = dev_get_drvdata(pdev); 175862306a36Sopenharmony_ci 175962306a36Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); 176062306a36Sopenharmony_ci return 0; 176162306a36Sopenharmony_ci} 176262306a36Sopenharmony_ci 176362306a36Sopenharmony_cistatic int loopback_resume(struct device *pdev) 176462306a36Sopenharmony_ci{ 176562306a36Sopenharmony_ci struct snd_card *card = dev_get_drvdata(pdev); 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D0); 176862306a36Sopenharmony_ci return 0; 176962306a36Sopenharmony_ci} 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(loopback_pm, loopback_suspend, loopback_resume); 177262306a36Sopenharmony_ci#define LOOPBACK_PM_OPS &loopback_pm 177362306a36Sopenharmony_ci#else 177462306a36Sopenharmony_ci#define LOOPBACK_PM_OPS NULL 177562306a36Sopenharmony_ci#endif 177662306a36Sopenharmony_ci 177762306a36Sopenharmony_ci#define SND_LOOPBACK_DRIVER "snd_aloop" 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_cistatic struct platform_driver loopback_driver = { 178062306a36Sopenharmony_ci .probe = loopback_probe, 178162306a36Sopenharmony_ci .driver = { 178262306a36Sopenharmony_ci .name = SND_LOOPBACK_DRIVER, 178362306a36Sopenharmony_ci .pm = LOOPBACK_PM_OPS, 178462306a36Sopenharmony_ci }, 178562306a36Sopenharmony_ci}; 178662306a36Sopenharmony_ci 178762306a36Sopenharmony_cistatic void loopback_unregister_all(void) 178862306a36Sopenharmony_ci{ 178962306a36Sopenharmony_ci int i; 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(devices); ++i) 179262306a36Sopenharmony_ci platform_device_unregister(devices[i]); 179362306a36Sopenharmony_ci platform_driver_unregister(&loopback_driver); 179462306a36Sopenharmony_ci} 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_cistatic int __init alsa_card_loopback_init(void) 179762306a36Sopenharmony_ci{ 179862306a36Sopenharmony_ci int i, err, cards; 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_ci err = platform_driver_register(&loopback_driver); 180162306a36Sopenharmony_ci if (err < 0) 180262306a36Sopenharmony_ci return err; 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_ci 180562306a36Sopenharmony_ci cards = 0; 180662306a36Sopenharmony_ci for (i = 0; i < SNDRV_CARDS; i++) { 180762306a36Sopenharmony_ci struct platform_device *device; 180862306a36Sopenharmony_ci if (!enable[i]) 180962306a36Sopenharmony_ci continue; 181062306a36Sopenharmony_ci device = platform_device_register_simple(SND_LOOPBACK_DRIVER, 181162306a36Sopenharmony_ci i, NULL, 0); 181262306a36Sopenharmony_ci if (IS_ERR(device)) 181362306a36Sopenharmony_ci continue; 181462306a36Sopenharmony_ci if (!platform_get_drvdata(device)) { 181562306a36Sopenharmony_ci platform_device_unregister(device); 181662306a36Sopenharmony_ci continue; 181762306a36Sopenharmony_ci } 181862306a36Sopenharmony_ci devices[i] = device; 181962306a36Sopenharmony_ci cards++; 182062306a36Sopenharmony_ci } 182162306a36Sopenharmony_ci if (!cards) { 182262306a36Sopenharmony_ci#ifdef MODULE 182362306a36Sopenharmony_ci printk(KERN_ERR "aloop: No loopback enabled\n"); 182462306a36Sopenharmony_ci#endif 182562306a36Sopenharmony_ci loopback_unregister_all(); 182662306a36Sopenharmony_ci return -ENODEV; 182762306a36Sopenharmony_ci } 182862306a36Sopenharmony_ci return 0; 182962306a36Sopenharmony_ci} 183062306a36Sopenharmony_ci 183162306a36Sopenharmony_cistatic void __exit alsa_card_loopback_exit(void) 183262306a36Sopenharmony_ci{ 183362306a36Sopenharmony_ci loopback_unregister_all(); 183462306a36Sopenharmony_ci} 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_cimodule_init(alsa_card_loopback_init) 183762306a36Sopenharmony_cimodule_exit(alsa_card_loopback_exit) 1838