18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Loopback soundcard 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Original code: 68c2ecf20Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * More accurate positioning and full-duplex support: 98c2ecf20Sopenharmony_ci * Copyright (c) Ahmet İnan <ainan at mathematik.uni-freiburg.de> 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Major (almost complete) rewrite: 128c2ecf20Sopenharmony_ci * Copyright (c) by Takashi Iwai <tiwai@suse.de> 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * A next major update in 2010 (separate timers for playback and capture): 158c2ecf20Sopenharmony_ci * Copyright (c) Jaroslav Kysela <perex@perex.cz> 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/init.h> 198c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci#include <linux/time.h> 228c2ecf20Sopenharmony_ci#include <linux/wait.h> 238c2ecf20Sopenharmony_ci#include <linux/module.h> 248c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 258c2ecf20Sopenharmony_ci#include <sound/core.h> 268c2ecf20Sopenharmony_ci#include <sound/control.h> 278c2ecf20Sopenharmony_ci#include <sound/pcm.h> 288c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 298c2ecf20Sopenharmony_ci#include <sound/info.h> 308c2ecf20Sopenharmony_ci#include <sound/initval.h> 318c2ecf20Sopenharmony_ci#include <sound/timer.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); 348c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("A loopback soundcard"); 358c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 368c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{ALSA,Loopback soundcard}}"); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define MAX_PCM_SUBSTREAMS 8 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ 418c2ecf20Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ 428c2ecf20Sopenharmony_cistatic bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0}; 438c2ecf20Sopenharmony_cistatic int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8}; 448c2ecf20Sopenharmony_cistatic int pcm_notify[SNDRV_CARDS]; 458c2ecf20Sopenharmony_cistatic char *timer_source[SNDRV_CARDS]; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cimodule_param_array(index, int, NULL, 0444); 488c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for loopback soundcard."); 498c2ecf20Sopenharmony_cimodule_param_array(id, charp, NULL, 0444); 508c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for loopback soundcard."); 518c2ecf20Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444); 528c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable this loopback soundcard."); 538c2ecf20Sopenharmony_cimodule_param_array(pcm_substreams, int, NULL, 0444); 548c2ecf20Sopenharmony_ciMODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-8) for loopback driver."); 558c2ecf20Sopenharmony_cimodule_param_array(pcm_notify, int, NULL, 0444); 568c2ecf20Sopenharmony_ciMODULE_PARM_DESC(pcm_notify, "Break capture when PCM format/rate/channels changes."); 578c2ecf20Sopenharmony_cimodule_param_array(timer_source, charp, NULL, 0444); 588c2ecf20Sopenharmony_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]."); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#define NO_PITCH 100000 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#define CABLE_VALID_PLAYBACK BIT(SNDRV_PCM_STREAM_PLAYBACK) 638c2ecf20Sopenharmony_ci#define CABLE_VALID_CAPTURE BIT(SNDRV_PCM_STREAM_CAPTURE) 648c2ecf20Sopenharmony_ci#define CABLE_VALID_BOTH (CABLE_VALID_PLAYBACK | CABLE_VALID_CAPTURE) 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistruct loopback_cable; 678c2ecf20Sopenharmony_cistruct loopback_pcm; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistruct loopback_ops { 708c2ecf20Sopenharmony_ci /* optional 718c2ecf20Sopenharmony_ci * call in loopback->cable_lock 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_ci int (*open)(struct loopback_pcm *dpcm); 748c2ecf20Sopenharmony_ci /* required 758c2ecf20Sopenharmony_ci * call in cable->lock 768c2ecf20Sopenharmony_ci */ 778c2ecf20Sopenharmony_ci int (*start)(struct loopback_pcm *dpcm); 788c2ecf20Sopenharmony_ci /* required 798c2ecf20Sopenharmony_ci * call in cable->lock 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_ci int (*stop)(struct loopback_pcm *dpcm); 828c2ecf20Sopenharmony_ci /* optional */ 838c2ecf20Sopenharmony_ci int (*stop_sync)(struct loopback_pcm *dpcm); 848c2ecf20Sopenharmony_ci /* optional */ 858c2ecf20Sopenharmony_ci int (*close_substream)(struct loopback_pcm *dpcm); 868c2ecf20Sopenharmony_ci /* optional 878c2ecf20Sopenharmony_ci * call in loopback->cable_lock 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_ci int (*close_cable)(struct loopback_pcm *dpcm); 908c2ecf20Sopenharmony_ci /* optional 918c2ecf20Sopenharmony_ci * call in cable->lock 928c2ecf20Sopenharmony_ci */ 938c2ecf20Sopenharmony_ci unsigned int (*pos_update)(struct loopback_cable *cable); 948c2ecf20Sopenharmony_ci /* optional */ 958c2ecf20Sopenharmony_ci void (*dpcm_info)(struct loopback_pcm *dpcm, 968c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer); 978c2ecf20Sopenharmony_ci}; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistruct loopback_cable { 1008c2ecf20Sopenharmony_ci spinlock_t lock; 1018c2ecf20Sopenharmony_ci struct loopback_pcm *streams[2]; 1028c2ecf20Sopenharmony_ci struct snd_pcm_hardware hw; 1038c2ecf20Sopenharmony_ci /* flags */ 1048c2ecf20Sopenharmony_ci unsigned int valid; 1058c2ecf20Sopenharmony_ci unsigned int running; 1068c2ecf20Sopenharmony_ci unsigned int pause; 1078c2ecf20Sopenharmony_ci /* timer specific */ 1088c2ecf20Sopenharmony_ci struct loopback_ops *ops; 1098c2ecf20Sopenharmony_ci /* If sound timer is used */ 1108c2ecf20Sopenharmony_ci struct { 1118c2ecf20Sopenharmony_ci int stream; 1128c2ecf20Sopenharmony_ci struct snd_timer_id id; 1138c2ecf20Sopenharmony_ci struct work_struct event_work; 1148c2ecf20Sopenharmony_ci struct snd_timer_instance *instance; 1158c2ecf20Sopenharmony_ci } snd_timer; 1168c2ecf20Sopenharmony_ci}; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistruct loopback_setup { 1198c2ecf20Sopenharmony_ci unsigned int notify: 1; 1208c2ecf20Sopenharmony_ci unsigned int rate_shift; 1218c2ecf20Sopenharmony_ci snd_pcm_format_t format; 1228c2ecf20Sopenharmony_ci unsigned int rate; 1238c2ecf20Sopenharmony_ci unsigned int channels; 1248c2ecf20Sopenharmony_ci struct snd_ctl_elem_id active_id; 1258c2ecf20Sopenharmony_ci struct snd_ctl_elem_id format_id; 1268c2ecf20Sopenharmony_ci struct snd_ctl_elem_id rate_id; 1278c2ecf20Sopenharmony_ci struct snd_ctl_elem_id channels_id; 1288c2ecf20Sopenharmony_ci}; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistruct loopback { 1318c2ecf20Sopenharmony_ci struct snd_card *card; 1328c2ecf20Sopenharmony_ci struct mutex cable_lock; 1338c2ecf20Sopenharmony_ci struct loopback_cable *cables[MAX_PCM_SUBSTREAMS][2]; 1348c2ecf20Sopenharmony_ci struct snd_pcm *pcm[2]; 1358c2ecf20Sopenharmony_ci struct loopback_setup setup[MAX_PCM_SUBSTREAMS][2]; 1368c2ecf20Sopenharmony_ci const char *timer_source; 1378c2ecf20Sopenharmony_ci}; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistruct loopback_pcm { 1408c2ecf20Sopenharmony_ci struct loopback *loopback; 1418c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; 1428c2ecf20Sopenharmony_ci struct loopback_cable *cable; 1438c2ecf20Sopenharmony_ci unsigned int pcm_buffer_size; 1448c2ecf20Sopenharmony_ci unsigned int buf_pos; /* position in buffer */ 1458c2ecf20Sopenharmony_ci unsigned int silent_size; 1468c2ecf20Sopenharmony_ci /* PCM parameters */ 1478c2ecf20Sopenharmony_ci unsigned int pcm_period_size; 1488c2ecf20Sopenharmony_ci unsigned int pcm_bps; /* bytes per second */ 1498c2ecf20Sopenharmony_ci unsigned int pcm_salign; /* bytes per sample * channels */ 1508c2ecf20Sopenharmony_ci unsigned int pcm_rate_shift; /* rate shift value */ 1518c2ecf20Sopenharmony_ci /* flags */ 1528c2ecf20Sopenharmony_ci unsigned int period_update_pending :1; 1538c2ecf20Sopenharmony_ci /* timer stuff */ 1548c2ecf20Sopenharmony_ci unsigned int irq_pos; /* fractional IRQ position in jiffies 1558c2ecf20Sopenharmony_ci * ticks 1568c2ecf20Sopenharmony_ci */ 1578c2ecf20Sopenharmony_ci unsigned int period_size_frac; /* period size in jiffies ticks */ 1588c2ecf20Sopenharmony_ci unsigned int last_drift; 1598c2ecf20Sopenharmony_ci unsigned long last_jiffies; 1608c2ecf20Sopenharmony_ci /* If jiffies timer is used */ 1618c2ecf20Sopenharmony_ci struct timer_list timer; 1628c2ecf20Sopenharmony_ci}; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic struct platform_device *devices[SNDRV_CARDS]; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic inline unsigned int byte_pos(struct loopback_pcm *dpcm, unsigned int x) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci if (dpcm->pcm_rate_shift == NO_PITCH) { 1698c2ecf20Sopenharmony_ci x /= HZ; 1708c2ecf20Sopenharmony_ci } else { 1718c2ecf20Sopenharmony_ci x = div_u64(NO_PITCH * (unsigned long long)x, 1728c2ecf20Sopenharmony_ci HZ * (unsigned long long)dpcm->pcm_rate_shift); 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci return x - (x % dpcm->pcm_salign); 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic inline unsigned int frac_pos(struct loopback_pcm *dpcm, unsigned int x) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci if (dpcm->pcm_rate_shift == NO_PITCH) { /* no pitch */ 1808c2ecf20Sopenharmony_ci return x * HZ; 1818c2ecf20Sopenharmony_ci } else { 1828c2ecf20Sopenharmony_ci x = div_u64(dpcm->pcm_rate_shift * (unsigned long long)x * HZ, 1838c2ecf20Sopenharmony_ci NO_PITCH); 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci return x; 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic inline struct loopback_setup *get_setup(struct loopback_pcm *dpcm) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci int device = dpcm->substream->pstr->pcm->device; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci if (dpcm->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 1938c2ecf20Sopenharmony_ci device ^= 1; 1948c2ecf20Sopenharmony_ci return &dpcm->loopback->setup[dpcm->substream->number][device]; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic inline unsigned int get_notify(struct loopback_pcm *dpcm) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci return get_setup(dpcm)->notify; 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic inline unsigned int get_rate_shift(struct loopback_pcm *dpcm) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci return get_setup(dpcm)->rate_shift; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci/* call in cable->lock */ 2088c2ecf20Sopenharmony_cistatic int loopback_jiffies_timer_start(struct loopback_pcm *dpcm) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci unsigned long tick; 2118c2ecf20Sopenharmony_ci unsigned int rate_shift = get_rate_shift(dpcm); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (rate_shift != dpcm->pcm_rate_shift) { 2148c2ecf20Sopenharmony_ci dpcm->pcm_rate_shift = rate_shift; 2158c2ecf20Sopenharmony_ci dpcm->period_size_frac = frac_pos(dpcm, dpcm->pcm_period_size); 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci if (dpcm->period_size_frac <= dpcm->irq_pos) { 2188c2ecf20Sopenharmony_ci dpcm->irq_pos %= dpcm->period_size_frac; 2198c2ecf20Sopenharmony_ci dpcm->period_update_pending = 1; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci tick = dpcm->period_size_frac - dpcm->irq_pos; 2228c2ecf20Sopenharmony_ci tick = (tick + dpcm->pcm_bps - 1) / dpcm->pcm_bps; 2238c2ecf20Sopenharmony_ci mod_timer(&dpcm->timer, jiffies + tick); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci return 0; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci/* call in cable->lock */ 2298c2ecf20Sopenharmony_cistatic int loopback_snd_timer_start(struct loopback_pcm *dpcm) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct loopback_cable *cable = dpcm->cable; 2328c2ecf20Sopenharmony_ci int err; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci /* Loopback device has to use same period as timer card. Therefore 2358c2ecf20Sopenharmony_ci * wake up for each snd_pcm_period_elapsed() call of timer card. 2368c2ecf20Sopenharmony_ci */ 2378c2ecf20Sopenharmony_ci err = snd_timer_start(cable->snd_timer.instance, 1); 2388c2ecf20Sopenharmony_ci if (err < 0) { 2398c2ecf20Sopenharmony_ci /* do not report error if trying to start but already 2408c2ecf20Sopenharmony_ci * running. For example called by opposite substream 2418c2ecf20Sopenharmony_ci * of the same cable 2428c2ecf20Sopenharmony_ci */ 2438c2ecf20Sopenharmony_ci if (err == -EBUSY) 2448c2ecf20Sopenharmony_ci return 0; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci pcm_err(dpcm->substream->pcm, 2478c2ecf20Sopenharmony_ci "snd_timer_start(%d,%d,%d) failed with %d", 2488c2ecf20Sopenharmony_ci cable->snd_timer.id.card, 2498c2ecf20Sopenharmony_ci cable->snd_timer.id.device, 2508c2ecf20Sopenharmony_ci cable->snd_timer.id.subdevice, 2518c2ecf20Sopenharmony_ci err); 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci return err; 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci/* call in cable->lock */ 2588c2ecf20Sopenharmony_cistatic inline int loopback_jiffies_timer_stop(struct loopback_pcm *dpcm) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci del_timer(&dpcm->timer); 2618c2ecf20Sopenharmony_ci dpcm->timer.expires = 0; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci return 0; 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci/* call in cable->lock */ 2678c2ecf20Sopenharmony_cistatic int loopback_snd_timer_stop(struct loopback_pcm *dpcm) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci struct loopback_cable *cable = dpcm->cable; 2708c2ecf20Sopenharmony_ci int err; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci /* only stop if both devices (playback and capture) are not running */ 2738c2ecf20Sopenharmony_ci if (cable->running ^ cable->pause) 2748c2ecf20Sopenharmony_ci return 0; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci err = snd_timer_stop(cable->snd_timer.instance); 2778c2ecf20Sopenharmony_ci if (err < 0) { 2788c2ecf20Sopenharmony_ci pcm_err(dpcm->substream->pcm, 2798c2ecf20Sopenharmony_ci "snd_timer_stop(%d,%d,%d) failed with %d", 2808c2ecf20Sopenharmony_ci cable->snd_timer.id.card, 2818c2ecf20Sopenharmony_ci cable->snd_timer.id.device, 2828c2ecf20Sopenharmony_ci cable->snd_timer.id.subdevice, 2838c2ecf20Sopenharmony_ci err); 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci return err; 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic inline int loopback_jiffies_timer_stop_sync(struct loopback_pcm *dpcm) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci del_timer_sync(&dpcm->timer); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci return 0; 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci/* call in loopback->cable_lock */ 2978c2ecf20Sopenharmony_cistatic int loopback_snd_timer_close_cable(struct loopback_pcm *dpcm) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci struct loopback_cable *cable = dpcm->cable; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* snd_timer was not opened */ 3028c2ecf20Sopenharmony_ci if (!cable->snd_timer.instance) 3038c2ecf20Sopenharmony_ci return 0; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci /* will only be called from free_cable() when other stream was 3068c2ecf20Sopenharmony_ci * already closed. Other stream cannot be reopened as long as 3078c2ecf20Sopenharmony_ci * loopback->cable_lock is locked. Therefore no need to lock 3088c2ecf20Sopenharmony_ci * cable->lock; 3098c2ecf20Sopenharmony_ci */ 3108c2ecf20Sopenharmony_ci snd_timer_close(cable->snd_timer.instance); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci /* wait till drain work has finished if requested */ 3138c2ecf20Sopenharmony_ci cancel_work_sync(&cable->snd_timer.event_work); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci snd_timer_instance_free(cable->snd_timer.instance); 3168c2ecf20Sopenharmony_ci memset(&cable->snd_timer, 0, sizeof(cable->snd_timer)); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci return 0; 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic int loopback_check_format(struct loopback_cable *cable, int stream) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime, *cruntime; 3248c2ecf20Sopenharmony_ci struct loopback_setup *setup; 3258c2ecf20Sopenharmony_ci struct snd_card *card; 3268c2ecf20Sopenharmony_ci int check; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci if (cable->valid != CABLE_VALID_BOTH) { 3298c2ecf20Sopenharmony_ci if (stream == SNDRV_PCM_STREAM_PLAYBACK) 3308c2ecf20Sopenharmony_ci goto __notify; 3318c2ecf20Sopenharmony_ci return 0; 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci runtime = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]-> 3348c2ecf20Sopenharmony_ci substream->runtime; 3358c2ecf20Sopenharmony_ci cruntime = cable->streams[SNDRV_PCM_STREAM_CAPTURE]-> 3368c2ecf20Sopenharmony_ci substream->runtime; 3378c2ecf20Sopenharmony_ci check = runtime->format != cruntime->format || 3388c2ecf20Sopenharmony_ci runtime->rate != cruntime->rate || 3398c2ecf20Sopenharmony_ci runtime->channels != cruntime->channels; 3408c2ecf20Sopenharmony_ci if (!check) 3418c2ecf20Sopenharmony_ci return 0; 3428c2ecf20Sopenharmony_ci if (stream == SNDRV_PCM_STREAM_CAPTURE) { 3438c2ecf20Sopenharmony_ci return -EIO; 3448c2ecf20Sopenharmony_ci } else { 3458c2ecf20Sopenharmony_ci snd_pcm_stop(cable->streams[SNDRV_PCM_STREAM_CAPTURE]-> 3468c2ecf20Sopenharmony_ci substream, SNDRV_PCM_STATE_DRAINING); 3478c2ecf20Sopenharmony_ci __notify: 3488c2ecf20Sopenharmony_ci runtime = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]-> 3498c2ecf20Sopenharmony_ci substream->runtime; 3508c2ecf20Sopenharmony_ci setup = get_setup(cable->streams[SNDRV_PCM_STREAM_PLAYBACK]); 3518c2ecf20Sopenharmony_ci card = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]->loopback->card; 3528c2ecf20Sopenharmony_ci if (setup->format != runtime->format) { 3538c2ecf20Sopenharmony_ci snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, 3548c2ecf20Sopenharmony_ci &setup->format_id); 3558c2ecf20Sopenharmony_ci setup->format = runtime->format; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci if (setup->rate != runtime->rate) { 3588c2ecf20Sopenharmony_ci snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, 3598c2ecf20Sopenharmony_ci &setup->rate_id); 3608c2ecf20Sopenharmony_ci setup->rate = runtime->rate; 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci if (setup->channels != runtime->channels) { 3638c2ecf20Sopenharmony_ci snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, 3648c2ecf20Sopenharmony_ci &setup->channels_id); 3658c2ecf20Sopenharmony_ci setup->channels = runtime->channels; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci return 0; 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic void loopback_active_notify(struct loopback_pcm *dpcm) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci snd_ctl_notify(dpcm->loopback->card, 3748c2ecf20Sopenharmony_ci SNDRV_CTL_EVENT_MASK_VALUE, 3758c2ecf20Sopenharmony_ci &get_setup(dpcm)->active_id); 3768c2ecf20Sopenharmony_ci} 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_cistatic int loopback_trigger(struct snd_pcm_substream *substream, int cmd) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 3818c2ecf20Sopenharmony_ci struct loopback_pcm *dpcm = runtime->private_data; 3828c2ecf20Sopenharmony_ci struct loopback_cable *cable = dpcm->cable; 3838c2ecf20Sopenharmony_ci int err = 0, stream = 1 << substream->stream; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci switch (cmd) { 3868c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 3878c2ecf20Sopenharmony_ci err = loopback_check_format(cable, substream->stream); 3888c2ecf20Sopenharmony_ci if (err < 0) 3898c2ecf20Sopenharmony_ci return err; 3908c2ecf20Sopenharmony_ci dpcm->last_jiffies = jiffies; 3918c2ecf20Sopenharmony_ci dpcm->pcm_rate_shift = 0; 3928c2ecf20Sopenharmony_ci dpcm->last_drift = 0; 3938c2ecf20Sopenharmony_ci spin_lock(&cable->lock); 3948c2ecf20Sopenharmony_ci cable->running |= stream; 3958c2ecf20Sopenharmony_ci cable->pause &= ~stream; 3968c2ecf20Sopenharmony_ci err = cable->ops->start(dpcm); 3978c2ecf20Sopenharmony_ci spin_unlock(&cable->lock); 3988c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 3998c2ecf20Sopenharmony_ci loopback_active_notify(dpcm); 4008c2ecf20Sopenharmony_ci break; 4018c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 4028c2ecf20Sopenharmony_ci spin_lock(&cable->lock); 4038c2ecf20Sopenharmony_ci cable->running &= ~stream; 4048c2ecf20Sopenharmony_ci cable->pause &= ~stream; 4058c2ecf20Sopenharmony_ci err = cable->ops->stop(dpcm); 4068c2ecf20Sopenharmony_ci spin_unlock(&cable->lock); 4078c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 4088c2ecf20Sopenharmony_ci loopback_active_notify(dpcm); 4098c2ecf20Sopenharmony_ci break; 4108c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 4118c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 4128c2ecf20Sopenharmony_ci spin_lock(&cable->lock); 4138c2ecf20Sopenharmony_ci cable->pause |= stream; 4148c2ecf20Sopenharmony_ci err = cable->ops->stop(dpcm); 4158c2ecf20Sopenharmony_ci spin_unlock(&cable->lock); 4168c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 4178c2ecf20Sopenharmony_ci loopback_active_notify(dpcm); 4188c2ecf20Sopenharmony_ci break; 4198c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 4208c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 4218c2ecf20Sopenharmony_ci spin_lock(&cable->lock); 4228c2ecf20Sopenharmony_ci dpcm->last_jiffies = jiffies; 4238c2ecf20Sopenharmony_ci cable->pause &= ~stream; 4248c2ecf20Sopenharmony_ci err = cable->ops->start(dpcm); 4258c2ecf20Sopenharmony_ci spin_unlock(&cable->lock); 4268c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 4278c2ecf20Sopenharmony_ci loopback_active_notify(dpcm); 4288c2ecf20Sopenharmony_ci break; 4298c2ecf20Sopenharmony_ci default: 4308c2ecf20Sopenharmony_ci return -EINVAL; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci return err; 4338c2ecf20Sopenharmony_ci} 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_cistatic void params_change(struct snd_pcm_substream *substream) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 4388c2ecf20Sopenharmony_ci struct loopback_pcm *dpcm = runtime->private_data; 4398c2ecf20Sopenharmony_ci struct loopback_cable *cable = dpcm->cable; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci cable->hw.formats = pcm_format_to_bits(runtime->format); 4428c2ecf20Sopenharmony_ci cable->hw.rate_min = runtime->rate; 4438c2ecf20Sopenharmony_ci cable->hw.rate_max = runtime->rate; 4448c2ecf20Sopenharmony_ci cable->hw.channels_min = runtime->channels; 4458c2ecf20Sopenharmony_ci cable->hw.channels_max = runtime->channels; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci if (cable->snd_timer.instance) { 4488c2ecf20Sopenharmony_ci cable->hw.period_bytes_min = 4498c2ecf20Sopenharmony_ci frames_to_bytes(runtime, runtime->period_size); 4508c2ecf20Sopenharmony_ci cable->hw.period_bytes_max = cable->hw.period_bytes_min; 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci} 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_cistatic int loopback_prepare(struct snd_pcm_substream *substream) 4568c2ecf20Sopenharmony_ci{ 4578c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 4588c2ecf20Sopenharmony_ci struct loopback_pcm *dpcm = runtime->private_data; 4598c2ecf20Sopenharmony_ci struct loopback_cable *cable = dpcm->cable; 4608c2ecf20Sopenharmony_ci int err, bps, salign; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci if (cable->ops->stop_sync) { 4638c2ecf20Sopenharmony_ci err = cable->ops->stop_sync(dpcm); 4648c2ecf20Sopenharmony_ci if (err < 0) 4658c2ecf20Sopenharmony_ci return err; 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci salign = (snd_pcm_format_physical_width(runtime->format) * 4698c2ecf20Sopenharmony_ci runtime->channels) / 8; 4708c2ecf20Sopenharmony_ci bps = salign * runtime->rate; 4718c2ecf20Sopenharmony_ci if (bps <= 0 || salign <= 0) 4728c2ecf20Sopenharmony_ci return -EINVAL; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci dpcm->buf_pos = 0; 4758c2ecf20Sopenharmony_ci dpcm->pcm_buffer_size = frames_to_bytes(runtime, runtime->buffer_size); 4768c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { 4778c2ecf20Sopenharmony_ci /* clear capture buffer */ 4788c2ecf20Sopenharmony_ci dpcm->silent_size = dpcm->pcm_buffer_size; 4798c2ecf20Sopenharmony_ci snd_pcm_format_set_silence(runtime->format, runtime->dma_area, 4808c2ecf20Sopenharmony_ci runtime->buffer_size * runtime->channels); 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci dpcm->irq_pos = 0; 4848c2ecf20Sopenharmony_ci dpcm->period_update_pending = 0; 4858c2ecf20Sopenharmony_ci dpcm->pcm_bps = bps; 4868c2ecf20Sopenharmony_ci dpcm->pcm_salign = salign; 4878c2ecf20Sopenharmony_ci dpcm->pcm_period_size = frames_to_bytes(runtime, runtime->period_size); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci mutex_lock(&dpcm->loopback->cable_lock); 4908c2ecf20Sopenharmony_ci if (!(cable->valid & ~(1 << substream->stream)) || 4918c2ecf20Sopenharmony_ci (get_setup(dpcm)->notify && 4928c2ecf20Sopenharmony_ci substream->stream == SNDRV_PCM_STREAM_PLAYBACK)) 4938c2ecf20Sopenharmony_ci params_change(substream); 4948c2ecf20Sopenharmony_ci cable->valid |= 1 << substream->stream; 4958c2ecf20Sopenharmony_ci mutex_unlock(&dpcm->loopback->cable_lock); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci return 0; 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_cistatic void clear_capture_buf(struct loopback_pcm *dpcm, unsigned int bytes) 5018c2ecf20Sopenharmony_ci{ 5028c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = dpcm->substream->runtime; 5038c2ecf20Sopenharmony_ci char *dst = runtime->dma_area; 5048c2ecf20Sopenharmony_ci unsigned int dst_off = dpcm->buf_pos; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci if (dpcm->silent_size >= dpcm->pcm_buffer_size) 5078c2ecf20Sopenharmony_ci return; 5088c2ecf20Sopenharmony_ci if (dpcm->silent_size + bytes > dpcm->pcm_buffer_size) 5098c2ecf20Sopenharmony_ci bytes = dpcm->pcm_buffer_size - dpcm->silent_size; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci for (;;) { 5128c2ecf20Sopenharmony_ci unsigned int size = bytes; 5138c2ecf20Sopenharmony_ci if (dst_off + size > dpcm->pcm_buffer_size) 5148c2ecf20Sopenharmony_ci size = dpcm->pcm_buffer_size - dst_off; 5158c2ecf20Sopenharmony_ci snd_pcm_format_set_silence(runtime->format, dst + dst_off, 5168c2ecf20Sopenharmony_ci bytes_to_frames(runtime, size) * 5178c2ecf20Sopenharmony_ci runtime->channels); 5188c2ecf20Sopenharmony_ci dpcm->silent_size += size; 5198c2ecf20Sopenharmony_ci bytes -= size; 5208c2ecf20Sopenharmony_ci if (!bytes) 5218c2ecf20Sopenharmony_ci break; 5228c2ecf20Sopenharmony_ci dst_off = 0; 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci} 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_cistatic void copy_play_buf(struct loopback_pcm *play, 5278c2ecf20Sopenharmony_ci struct loopback_pcm *capt, 5288c2ecf20Sopenharmony_ci unsigned int bytes) 5298c2ecf20Sopenharmony_ci{ 5308c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = play->substream->runtime; 5318c2ecf20Sopenharmony_ci char *src = runtime->dma_area; 5328c2ecf20Sopenharmony_ci char *dst = capt->substream->runtime->dma_area; 5338c2ecf20Sopenharmony_ci unsigned int src_off = play->buf_pos; 5348c2ecf20Sopenharmony_ci unsigned int dst_off = capt->buf_pos; 5358c2ecf20Sopenharmony_ci unsigned int clear_bytes = 0; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci /* check if playback is draining, trim the capture copy size 5388c2ecf20Sopenharmony_ci * when our pointer is at the end of playback ring buffer */ 5398c2ecf20Sopenharmony_ci if (runtime->status->state == SNDRV_PCM_STATE_DRAINING && 5408c2ecf20Sopenharmony_ci snd_pcm_playback_hw_avail(runtime) < runtime->buffer_size) { 5418c2ecf20Sopenharmony_ci snd_pcm_uframes_t appl_ptr, appl_ptr1, diff; 5428c2ecf20Sopenharmony_ci appl_ptr = appl_ptr1 = runtime->control->appl_ptr; 5438c2ecf20Sopenharmony_ci appl_ptr1 -= appl_ptr1 % runtime->buffer_size; 5448c2ecf20Sopenharmony_ci appl_ptr1 += play->buf_pos / play->pcm_salign; 5458c2ecf20Sopenharmony_ci if (appl_ptr < appl_ptr1) 5468c2ecf20Sopenharmony_ci appl_ptr1 -= runtime->buffer_size; 5478c2ecf20Sopenharmony_ci diff = (appl_ptr - appl_ptr1) * play->pcm_salign; 5488c2ecf20Sopenharmony_ci if (diff < bytes) { 5498c2ecf20Sopenharmony_ci clear_bytes = bytes - diff; 5508c2ecf20Sopenharmony_ci bytes = diff; 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci for (;;) { 5558c2ecf20Sopenharmony_ci unsigned int size = bytes; 5568c2ecf20Sopenharmony_ci if (src_off + size > play->pcm_buffer_size) 5578c2ecf20Sopenharmony_ci size = play->pcm_buffer_size - src_off; 5588c2ecf20Sopenharmony_ci if (dst_off + size > capt->pcm_buffer_size) 5598c2ecf20Sopenharmony_ci size = capt->pcm_buffer_size - dst_off; 5608c2ecf20Sopenharmony_ci memcpy(dst + dst_off, src + src_off, size); 5618c2ecf20Sopenharmony_ci capt->silent_size = 0; 5628c2ecf20Sopenharmony_ci bytes -= size; 5638c2ecf20Sopenharmony_ci if (!bytes) 5648c2ecf20Sopenharmony_ci break; 5658c2ecf20Sopenharmony_ci src_off = (src_off + size) % play->pcm_buffer_size; 5668c2ecf20Sopenharmony_ci dst_off = (dst_off + size) % capt->pcm_buffer_size; 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci if (clear_bytes > 0) { 5708c2ecf20Sopenharmony_ci clear_capture_buf(capt, clear_bytes); 5718c2ecf20Sopenharmony_ci capt->silent_size = 0; 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci} 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_cistatic inline unsigned int bytepos_delta(struct loopback_pcm *dpcm, 5768c2ecf20Sopenharmony_ci unsigned int jiffies_delta) 5778c2ecf20Sopenharmony_ci{ 5788c2ecf20Sopenharmony_ci unsigned long last_pos; 5798c2ecf20Sopenharmony_ci unsigned int delta; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci last_pos = byte_pos(dpcm, dpcm->irq_pos); 5828c2ecf20Sopenharmony_ci dpcm->irq_pos += jiffies_delta * dpcm->pcm_bps; 5838c2ecf20Sopenharmony_ci delta = byte_pos(dpcm, dpcm->irq_pos) - last_pos; 5848c2ecf20Sopenharmony_ci if (delta >= dpcm->last_drift) 5858c2ecf20Sopenharmony_ci delta -= dpcm->last_drift; 5868c2ecf20Sopenharmony_ci dpcm->last_drift = 0; 5878c2ecf20Sopenharmony_ci if (dpcm->irq_pos >= dpcm->period_size_frac) { 5888c2ecf20Sopenharmony_ci dpcm->irq_pos %= dpcm->period_size_frac; 5898c2ecf20Sopenharmony_ci dpcm->period_update_pending = 1; 5908c2ecf20Sopenharmony_ci } 5918c2ecf20Sopenharmony_ci return delta; 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_cistatic inline void bytepos_finish(struct loopback_pcm *dpcm, 5958c2ecf20Sopenharmony_ci unsigned int delta) 5968c2ecf20Sopenharmony_ci{ 5978c2ecf20Sopenharmony_ci dpcm->buf_pos += delta; 5988c2ecf20Sopenharmony_ci dpcm->buf_pos %= dpcm->pcm_buffer_size; 5998c2ecf20Sopenharmony_ci} 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci/* call in cable->lock */ 6028c2ecf20Sopenharmony_cistatic unsigned int loopback_jiffies_timer_pos_update 6038c2ecf20Sopenharmony_ci (struct loopback_cable *cable) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci struct loopback_pcm *dpcm_play = 6068c2ecf20Sopenharmony_ci cable->streams[SNDRV_PCM_STREAM_PLAYBACK]; 6078c2ecf20Sopenharmony_ci struct loopback_pcm *dpcm_capt = 6088c2ecf20Sopenharmony_ci cable->streams[SNDRV_PCM_STREAM_CAPTURE]; 6098c2ecf20Sopenharmony_ci unsigned long delta_play = 0, delta_capt = 0, cur_jiffies; 6108c2ecf20Sopenharmony_ci unsigned int running, count1, count2; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci cur_jiffies = jiffies; 6138c2ecf20Sopenharmony_ci running = cable->running ^ cable->pause; 6148c2ecf20Sopenharmony_ci if (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) { 6158c2ecf20Sopenharmony_ci delta_play = cur_jiffies - dpcm_play->last_jiffies; 6168c2ecf20Sopenharmony_ci dpcm_play->last_jiffies += delta_play; 6178c2ecf20Sopenharmony_ci } 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci if (running & (1 << SNDRV_PCM_STREAM_CAPTURE)) { 6208c2ecf20Sopenharmony_ci delta_capt = cur_jiffies - dpcm_capt->last_jiffies; 6218c2ecf20Sopenharmony_ci dpcm_capt->last_jiffies += delta_capt; 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci if (delta_play == 0 && delta_capt == 0) 6258c2ecf20Sopenharmony_ci goto unlock; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci if (delta_play > delta_capt) { 6288c2ecf20Sopenharmony_ci count1 = bytepos_delta(dpcm_play, delta_play - delta_capt); 6298c2ecf20Sopenharmony_ci bytepos_finish(dpcm_play, count1); 6308c2ecf20Sopenharmony_ci delta_play = delta_capt; 6318c2ecf20Sopenharmony_ci } else if (delta_play < delta_capt) { 6328c2ecf20Sopenharmony_ci count1 = bytepos_delta(dpcm_capt, delta_capt - delta_play); 6338c2ecf20Sopenharmony_ci clear_capture_buf(dpcm_capt, count1); 6348c2ecf20Sopenharmony_ci bytepos_finish(dpcm_capt, count1); 6358c2ecf20Sopenharmony_ci delta_capt = delta_play; 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci if (delta_play == 0 && delta_capt == 0) 6398c2ecf20Sopenharmony_ci goto unlock; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci /* note delta_capt == delta_play at this moment */ 6428c2ecf20Sopenharmony_ci count1 = bytepos_delta(dpcm_play, delta_play); 6438c2ecf20Sopenharmony_ci count2 = bytepos_delta(dpcm_capt, delta_capt); 6448c2ecf20Sopenharmony_ci if (count1 < count2) { 6458c2ecf20Sopenharmony_ci dpcm_capt->last_drift = count2 - count1; 6468c2ecf20Sopenharmony_ci count1 = count2; 6478c2ecf20Sopenharmony_ci } else if (count1 > count2) { 6488c2ecf20Sopenharmony_ci dpcm_play->last_drift = count1 - count2; 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ci copy_play_buf(dpcm_play, dpcm_capt, count1); 6518c2ecf20Sopenharmony_ci bytepos_finish(dpcm_play, count1); 6528c2ecf20Sopenharmony_ci bytepos_finish(dpcm_capt, count1); 6538c2ecf20Sopenharmony_ci unlock: 6548c2ecf20Sopenharmony_ci return running; 6558c2ecf20Sopenharmony_ci} 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_cistatic void loopback_jiffies_timer_function(struct timer_list *t) 6588c2ecf20Sopenharmony_ci{ 6598c2ecf20Sopenharmony_ci struct loopback_pcm *dpcm = from_timer(dpcm, t, timer); 6608c2ecf20Sopenharmony_ci unsigned long flags; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci spin_lock_irqsave(&dpcm->cable->lock, flags); 6638c2ecf20Sopenharmony_ci if (loopback_jiffies_timer_pos_update(dpcm->cable) & 6648c2ecf20Sopenharmony_ci (1 << dpcm->substream->stream)) { 6658c2ecf20Sopenharmony_ci loopback_jiffies_timer_start(dpcm); 6668c2ecf20Sopenharmony_ci if (dpcm->period_update_pending) { 6678c2ecf20Sopenharmony_ci dpcm->period_update_pending = 0; 6688c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dpcm->cable->lock, flags); 6698c2ecf20Sopenharmony_ci /* need to unlock before calling below */ 6708c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(dpcm->substream); 6718c2ecf20Sopenharmony_ci return; 6728c2ecf20Sopenharmony_ci } 6738c2ecf20Sopenharmony_ci } 6748c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dpcm->cable->lock, flags); 6758c2ecf20Sopenharmony_ci} 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci/* call in cable->lock */ 6788c2ecf20Sopenharmony_cistatic int loopback_snd_timer_check_resolution(struct snd_pcm_runtime *runtime, 6798c2ecf20Sopenharmony_ci unsigned long resolution) 6808c2ecf20Sopenharmony_ci{ 6818c2ecf20Sopenharmony_ci if (resolution != runtime->timer_resolution) { 6828c2ecf20Sopenharmony_ci struct loopback_pcm *dpcm = runtime->private_data; 6838c2ecf20Sopenharmony_ci struct loopback_cable *cable = dpcm->cable; 6848c2ecf20Sopenharmony_ci /* Worst case estimation of possible values for resolution 6858c2ecf20Sopenharmony_ci * resolution <= (512 * 1024) frames / 8kHz in nsec 6868c2ecf20Sopenharmony_ci * resolution <= 65.536.000.000 nsec 6878c2ecf20Sopenharmony_ci * 6888c2ecf20Sopenharmony_ci * period_size <= 65.536.000.000 nsec / 1000nsec/usec * 192kHz + 6898c2ecf20Sopenharmony_ci * 500.000 6908c2ecf20Sopenharmony_ci * period_size <= 12.582.912.000.000 <64bit 6918c2ecf20Sopenharmony_ci * / 1.000.000 usec/sec 6928c2ecf20Sopenharmony_ci */ 6938c2ecf20Sopenharmony_ci snd_pcm_uframes_t period_size_usec = 6948c2ecf20Sopenharmony_ci resolution / 1000 * runtime->rate; 6958c2ecf20Sopenharmony_ci /* round to nearest sample rate */ 6968c2ecf20Sopenharmony_ci snd_pcm_uframes_t period_size = 6978c2ecf20Sopenharmony_ci (period_size_usec + 500 * 1000) / (1000 * 1000); 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci pcm_err(dpcm->substream->pcm, 7008c2ecf20Sopenharmony_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.", 7018c2ecf20Sopenharmony_ci runtime->period_size, resolution, period_size, 7028c2ecf20Sopenharmony_ci cable->snd_timer.id.card, 7038c2ecf20Sopenharmony_ci cable->snd_timer.id.device, 7048c2ecf20Sopenharmony_ci cable->snd_timer.id.subdevice, 7058c2ecf20Sopenharmony_ci period_size); 7068c2ecf20Sopenharmony_ci return -EINVAL; 7078c2ecf20Sopenharmony_ci } 7088c2ecf20Sopenharmony_ci return 0; 7098c2ecf20Sopenharmony_ci} 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_cistatic void loopback_snd_timer_period_elapsed(struct loopback_cable *cable, 7128c2ecf20Sopenharmony_ci int event, 7138c2ecf20Sopenharmony_ci unsigned long resolution) 7148c2ecf20Sopenharmony_ci{ 7158c2ecf20Sopenharmony_ci struct loopback_pcm *dpcm_play, *dpcm_capt; 7168c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream_play, *substream_capt; 7178c2ecf20Sopenharmony_ci struct snd_pcm_runtime *valid_runtime; 7188c2ecf20Sopenharmony_ci unsigned int running, elapsed_bytes; 7198c2ecf20Sopenharmony_ci unsigned long flags; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci spin_lock_irqsave(&cable->lock, flags); 7228c2ecf20Sopenharmony_ci running = cable->running ^ cable->pause; 7238c2ecf20Sopenharmony_ci /* no need to do anything if no stream is running */ 7248c2ecf20Sopenharmony_ci if (!running) { 7258c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cable->lock, flags); 7268c2ecf20Sopenharmony_ci return; 7278c2ecf20Sopenharmony_ci } 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci dpcm_play = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]; 7308c2ecf20Sopenharmony_ci dpcm_capt = cable->streams[SNDRV_PCM_STREAM_CAPTURE]; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci if (event == SNDRV_TIMER_EVENT_MSTOP) { 7338c2ecf20Sopenharmony_ci if (!dpcm_play || 7348c2ecf20Sopenharmony_ci dpcm_play->substream->runtime->status->state != 7358c2ecf20Sopenharmony_ci SNDRV_PCM_STATE_DRAINING) { 7368c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cable->lock, flags); 7378c2ecf20Sopenharmony_ci return; 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci } 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci substream_play = (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ? 7428c2ecf20Sopenharmony_ci dpcm_play->substream : NULL; 7438c2ecf20Sopenharmony_ci substream_capt = (running & (1 << SNDRV_PCM_STREAM_CAPTURE)) ? 7448c2ecf20Sopenharmony_ci dpcm_capt->substream : NULL; 7458c2ecf20Sopenharmony_ci valid_runtime = (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ? 7468c2ecf20Sopenharmony_ci dpcm_play->substream->runtime : 7478c2ecf20Sopenharmony_ci dpcm_capt->substream->runtime; 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci /* resolution is only valid for SNDRV_TIMER_EVENT_TICK events */ 7508c2ecf20Sopenharmony_ci if (event == SNDRV_TIMER_EVENT_TICK) { 7518c2ecf20Sopenharmony_ci /* The hardware rules guarantee that playback and capture period 7528c2ecf20Sopenharmony_ci * are the same. Therefore only one device has to be checked 7538c2ecf20Sopenharmony_ci * here. 7548c2ecf20Sopenharmony_ci */ 7558c2ecf20Sopenharmony_ci if (loopback_snd_timer_check_resolution(valid_runtime, 7568c2ecf20Sopenharmony_ci resolution) < 0) { 7578c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cable->lock, flags); 7588c2ecf20Sopenharmony_ci if (substream_play) 7598c2ecf20Sopenharmony_ci snd_pcm_stop_xrun(substream_play); 7608c2ecf20Sopenharmony_ci if (substream_capt) 7618c2ecf20Sopenharmony_ci snd_pcm_stop_xrun(substream_capt); 7628c2ecf20Sopenharmony_ci return; 7638c2ecf20Sopenharmony_ci } 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci elapsed_bytes = frames_to_bytes(valid_runtime, 7678c2ecf20Sopenharmony_ci valid_runtime->period_size); 7688c2ecf20Sopenharmony_ci /* The same timer interrupt is used for playback and capture device */ 7698c2ecf20Sopenharmony_ci if ((running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) && 7708c2ecf20Sopenharmony_ci (running & (1 << SNDRV_PCM_STREAM_CAPTURE))) { 7718c2ecf20Sopenharmony_ci copy_play_buf(dpcm_play, dpcm_capt, elapsed_bytes); 7728c2ecf20Sopenharmony_ci bytepos_finish(dpcm_play, elapsed_bytes); 7738c2ecf20Sopenharmony_ci bytepos_finish(dpcm_capt, elapsed_bytes); 7748c2ecf20Sopenharmony_ci } else if (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) { 7758c2ecf20Sopenharmony_ci bytepos_finish(dpcm_play, elapsed_bytes); 7768c2ecf20Sopenharmony_ci } else if (running & (1 << SNDRV_PCM_STREAM_CAPTURE)) { 7778c2ecf20Sopenharmony_ci clear_capture_buf(dpcm_capt, elapsed_bytes); 7788c2ecf20Sopenharmony_ci bytepos_finish(dpcm_capt, elapsed_bytes); 7798c2ecf20Sopenharmony_ci } 7808c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cable->lock, flags); 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci if (substream_play) 7838c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(substream_play); 7848c2ecf20Sopenharmony_ci if (substream_capt) 7858c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(substream_capt); 7868c2ecf20Sopenharmony_ci} 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_cistatic void loopback_snd_timer_function(struct snd_timer_instance *timeri, 7898c2ecf20Sopenharmony_ci unsigned long resolution, 7908c2ecf20Sopenharmony_ci unsigned long ticks) 7918c2ecf20Sopenharmony_ci{ 7928c2ecf20Sopenharmony_ci struct loopback_cable *cable = timeri->callback_data; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci loopback_snd_timer_period_elapsed(cable, SNDRV_TIMER_EVENT_TICK, 7958c2ecf20Sopenharmony_ci resolution); 7968c2ecf20Sopenharmony_ci} 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_cistatic void loopback_snd_timer_work(struct work_struct *work) 7998c2ecf20Sopenharmony_ci{ 8008c2ecf20Sopenharmony_ci struct loopback_cable *cable; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci cable = container_of(work, struct loopback_cable, snd_timer.event_work); 8038c2ecf20Sopenharmony_ci loopback_snd_timer_period_elapsed(cable, SNDRV_TIMER_EVENT_MSTOP, 0); 8048c2ecf20Sopenharmony_ci} 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_cistatic void loopback_snd_timer_event(struct snd_timer_instance *timeri, 8078c2ecf20Sopenharmony_ci int event, 8088c2ecf20Sopenharmony_ci struct timespec64 *tstamp, 8098c2ecf20Sopenharmony_ci unsigned long resolution) 8108c2ecf20Sopenharmony_ci{ 8118c2ecf20Sopenharmony_ci /* Do not lock cable->lock here because timer->lock is already hold. 8128c2ecf20Sopenharmony_ci * There are other functions which first lock cable->lock and than 8138c2ecf20Sopenharmony_ci * timer->lock e.g. 8148c2ecf20Sopenharmony_ci * loopback_trigger() 8158c2ecf20Sopenharmony_ci * spin_lock(&cable->lock) 8168c2ecf20Sopenharmony_ci * loopback_snd_timer_start() 8178c2ecf20Sopenharmony_ci * snd_timer_start() 8188c2ecf20Sopenharmony_ci * spin_lock(&timer->lock) 8198c2ecf20Sopenharmony_ci * Therefore when using the oposit order of locks here it could result 8208c2ecf20Sopenharmony_ci * in a deadlock. 8218c2ecf20Sopenharmony_ci */ 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci if (event == SNDRV_TIMER_EVENT_MSTOP) { 8248c2ecf20Sopenharmony_ci struct loopback_cable *cable = timeri->callback_data; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci /* sound card of the timer was stopped. Therefore there will not 8278c2ecf20Sopenharmony_ci * be any further timer callbacks. Due to this forward audio 8288c2ecf20Sopenharmony_ci * data from here if in draining state. When still in running 8298c2ecf20Sopenharmony_ci * state the streaming will be aborted by the usual timeout. It 8308c2ecf20Sopenharmony_ci * should not be aborted here because may be the timer sound 8318c2ecf20Sopenharmony_ci * card does only a recovery and the timer is back soon. 8328c2ecf20Sopenharmony_ci * This work triggers loopback_snd_timer_work() 8338c2ecf20Sopenharmony_ci */ 8348c2ecf20Sopenharmony_ci schedule_work(&cable->snd_timer.event_work); 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci} 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_cistatic void loopback_jiffies_timer_dpcm_info(struct loopback_pcm *dpcm, 8398c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 8408c2ecf20Sopenharmony_ci{ 8418c2ecf20Sopenharmony_ci snd_iprintf(buffer, " update_pending:\t%u\n", 8428c2ecf20Sopenharmony_ci dpcm->period_update_pending); 8438c2ecf20Sopenharmony_ci snd_iprintf(buffer, " irq_pos:\t\t%u\n", dpcm->irq_pos); 8448c2ecf20Sopenharmony_ci snd_iprintf(buffer, " period_frac:\t%u\n", dpcm->period_size_frac); 8458c2ecf20Sopenharmony_ci snd_iprintf(buffer, " last_jiffies:\t%lu (%lu)\n", 8468c2ecf20Sopenharmony_ci dpcm->last_jiffies, jiffies); 8478c2ecf20Sopenharmony_ci snd_iprintf(buffer, " timer_expires:\t%lu\n", dpcm->timer.expires); 8488c2ecf20Sopenharmony_ci} 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_cistatic void loopback_snd_timer_dpcm_info(struct loopback_pcm *dpcm, 8518c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 8528c2ecf20Sopenharmony_ci{ 8538c2ecf20Sopenharmony_ci struct loopback_cable *cable = dpcm->cable; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci snd_iprintf(buffer, " sound timer:\thw:%d,%d,%d\n", 8568c2ecf20Sopenharmony_ci cable->snd_timer.id.card, 8578c2ecf20Sopenharmony_ci cable->snd_timer.id.device, 8588c2ecf20Sopenharmony_ci cable->snd_timer.id.subdevice); 8598c2ecf20Sopenharmony_ci snd_iprintf(buffer, " timer open:\t\t%s\n", 8608c2ecf20Sopenharmony_ci (cable->snd_timer.stream == SNDRV_PCM_STREAM_CAPTURE) ? 8618c2ecf20Sopenharmony_ci "capture" : "playback"); 8628c2ecf20Sopenharmony_ci} 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t loopback_pointer(struct snd_pcm_substream *substream) 8658c2ecf20Sopenharmony_ci{ 8668c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 8678c2ecf20Sopenharmony_ci struct loopback_pcm *dpcm = runtime->private_data; 8688c2ecf20Sopenharmony_ci snd_pcm_uframes_t pos; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci spin_lock(&dpcm->cable->lock); 8718c2ecf20Sopenharmony_ci if (dpcm->cable->ops->pos_update) 8728c2ecf20Sopenharmony_ci dpcm->cable->ops->pos_update(dpcm->cable); 8738c2ecf20Sopenharmony_ci pos = dpcm->buf_pos; 8748c2ecf20Sopenharmony_ci spin_unlock(&dpcm->cable->lock); 8758c2ecf20Sopenharmony_ci return bytes_to_frames(runtime, pos); 8768c2ecf20Sopenharmony_ci} 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware loopback_pcm_hardware = 8798c2ecf20Sopenharmony_ci{ 8808c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP | 8818c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE | 8828c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_RESUME), 8838c2ecf20Sopenharmony_ci .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | 8848c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE | 8858c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | 8868c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE | 8878c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE), 8888c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_192000, 8898c2ecf20Sopenharmony_ci .rate_min = 8000, 8908c2ecf20Sopenharmony_ci .rate_max = 192000, 8918c2ecf20Sopenharmony_ci .channels_min = 1, 8928c2ecf20Sopenharmony_ci .channels_max = 32, 8938c2ecf20Sopenharmony_ci .buffer_bytes_max = 2 * 1024 * 1024, 8948c2ecf20Sopenharmony_ci .period_bytes_min = 64, 8958c2ecf20Sopenharmony_ci /* note check overflow in frac_pos() using pcm_rate_shift before 8968c2ecf20Sopenharmony_ci changing period_bytes_max value */ 8978c2ecf20Sopenharmony_ci .period_bytes_max = 1024 * 1024, 8988c2ecf20Sopenharmony_ci .periods_min = 1, 8998c2ecf20Sopenharmony_ci .periods_max = 1024, 9008c2ecf20Sopenharmony_ci .fifo_size = 0, 9018c2ecf20Sopenharmony_ci}; 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_cistatic void loopback_runtime_free(struct snd_pcm_runtime *runtime) 9048c2ecf20Sopenharmony_ci{ 9058c2ecf20Sopenharmony_ci struct loopback_pcm *dpcm = runtime->private_data; 9068c2ecf20Sopenharmony_ci kfree(dpcm); 9078c2ecf20Sopenharmony_ci} 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_cistatic int loopback_hw_free(struct snd_pcm_substream *substream) 9108c2ecf20Sopenharmony_ci{ 9118c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 9128c2ecf20Sopenharmony_ci struct loopback_pcm *dpcm = runtime->private_data; 9138c2ecf20Sopenharmony_ci struct loopback_cable *cable = dpcm->cable; 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci mutex_lock(&dpcm->loopback->cable_lock); 9168c2ecf20Sopenharmony_ci cable->valid &= ~(1 << substream->stream); 9178c2ecf20Sopenharmony_ci mutex_unlock(&dpcm->loopback->cable_lock); 9188c2ecf20Sopenharmony_ci return 0; 9198c2ecf20Sopenharmony_ci} 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_cistatic unsigned int get_cable_index(struct snd_pcm_substream *substream) 9228c2ecf20Sopenharmony_ci{ 9238c2ecf20Sopenharmony_ci if (!substream->pcm->device) 9248c2ecf20Sopenharmony_ci return substream->stream; 9258c2ecf20Sopenharmony_ci else 9268c2ecf20Sopenharmony_ci return !substream->stream; 9278c2ecf20Sopenharmony_ci} 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_cistatic int rule_format(struct snd_pcm_hw_params *params, 9308c2ecf20Sopenharmony_ci struct snd_pcm_hw_rule *rule) 9318c2ecf20Sopenharmony_ci{ 9328c2ecf20Sopenharmony_ci struct loopback_pcm *dpcm = rule->private; 9338c2ecf20Sopenharmony_ci struct loopback_cable *cable = dpcm->cable; 9348c2ecf20Sopenharmony_ci struct snd_mask m; 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci snd_mask_none(&m); 9378c2ecf20Sopenharmony_ci mutex_lock(&dpcm->loopback->cable_lock); 9388c2ecf20Sopenharmony_ci m.bits[0] = (u_int32_t)cable->hw.formats; 9398c2ecf20Sopenharmony_ci m.bits[1] = (u_int32_t)(cable->hw.formats >> 32); 9408c2ecf20Sopenharmony_ci mutex_unlock(&dpcm->loopback->cable_lock); 9418c2ecf20Sopenharmony_ci return snd_mask_refine(hw_param_mask(params, rule->var), &m); 9428c2ecf20Sopenharmony_ci} 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_cistatic int rule_rate(struct snd_pcm_hw_params *params, 9458c2ecf20Sopenharmony_ci struct snd_pcm_hw_rule *rule) 9468c2ecf20Sopenharmony_ci{ 9478c2ecf20Sopenharmony_ci struct loopback_pcm *dpcm = rule->private; 9488c2ecf20Sopenharmony_ci struct loopback_cable *cable = dpcm->cable; 9498c2ecf20Sopenharmony_ci struct snd_interval t; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci mutex_lock(&dpcm->loopback->cable_lock); 9528c2ecf20Sopenharmony_ci t.min = cable->hw.rate_min; 9538c2ecf20Sopenharmony_ci t.max = cable->hw.rate_max; 9548c2ecf20Sopenharmony_ci mutex_unlock(&dpcm->loopback->cable_lock); 9558c2ecf20Sopenharmony_ci t.openmin = t.openmax = 0; 9568c2ecf20Sopenharmony_ci t.integer = 0; 9578c2ecf20Sopenharmony_ci return snd_interval_refine(hw_param_interval(params, rule->var), &t); 9588c2ecf20Sopenharmony_ci} 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_cistatic int rule_channels(struct snd_pcm_hw_params *params, 9618c2ecf20Sopenharmony_ci struct snd_pcm_hw_rule *rule) 9628c2ecf20Sopenharmony_ci{ 9638c2ecf20Sopenharmony_ci struct loopback_pcm *dpcm = rule->private; 9648c2ecf20Sopenharmony_ci struct loopback_cable *cable = dpcm->cable; 9658c2ecf20Sopenharmony_ci struct snd_interval t; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci mutex_lock(&dpcm->loopback->cable_lock); 9688c2ecf20Sopenharmony_ci t.min = cable->hw.channels_min; 9698c2ecf20Sopenharmony_ci t.max = cable->hw.channels_max; 9708c2ecf20Sopenharmony_ci mutex_unlock(&dpcm->loopback->cable_lock); 9718c2ecf20Sopenharmony_ci t.openmin = t.openmax = 0; 9728c2ecf20Sopenharmony_ci t.integer = 0; 9738c2ecf20Sopenharmony_ci return snd_interval_refine(hw_param_interval(params, rule->var), &t); 9748c2ecf20Sopenharmony_ci} 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_cistatic int rule_period_bytes(struct snd_pcm_hw_params *params, 9778c2ecf20Sopenharmony_ci struct snd_pcm_hw_rule *rule) 9788c2ecf20Sopenharmony_ci{ 9798c2ecf20Sopenharmony_ci struct loopback_pcm *dpcm = rule->private; 9808c2ecf20Sopenharmony_ci struct loopback_cable *cable = dpcm->cable; 9818c2ecf20Sopenharmony_ci struct snd_interval t; 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci mutex_lock(&dpcm->loopback->cable_lock); 9848c2ecf20Sopenharmony_ci t.min = cable->hw.period_bytes_min; 9858c2ecf20Sopenharmony_ci t.max = cable->hw.period_bytes_max; 9868c2ecf20Sopenharmony_ci mutex_unlock(&dpcm->loopback->cable_lock); 9878c2ecf20Sopenharmony_ci t.openmin = 0; 9888c2ecf20Sopenharmony_ci t.openmax = 0; 9898c2ecf20Sopenharmony_ci t.integer = 0; 9908c2ecf20Sopenharmony_ci return snd_interval_refine(hw_param_interval(params, rule->var), &t); 9918c2ecf20Sopenharmony_ci} 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_cistatic void free_cable(struct snd_pcm_substream *substream) 9948c2ecf20Sopenharmony_ci{ 9958c2ecf20Sopenharmony_ci struct loopback *loopback = substream->private_data; 9968c2ecf20Sopenharmony_ci int dev = get_cable_index(substream); 9978c2ecf20Sopenharmony_ci struct loopback_cable *cable; 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci cable = loopback->cables[substream->number][dev]; 10008c2ecf20Sopenharmony_ci if (!cable) 10018c2ecf20Sopenharmony_ci return; 10028c2ecf20Sopenharmony_ci if (cable->streams[!substream->stream]) { 10038c2ecf20Sopenharmony_ci /* other stream is still alive */ 10048c2ecf20Sopenharmony_ci spin_lock_irq(&cable->lock); 10058c2ecf20Sopenharmony_ci cable->streams[substream->stream] = NULL; 10068c2ecf20Sopenharmony_ci spin_unlock_irq(&cable->lock); 10078c2ecf20Sopenharmony_ci } else { 10088c2ecf20Sopenharmony_ci struct loopback_pcm *dpcm = substream->runtime->private_data; 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci if (cable->ops && cable->ops->close_cable && dpcm) 10118c2ecf20Sopenharmony_ci cable->ops->close_cable(dpcm); 10128c2ecf20Sopenharmony_ci /* free the cable */ 10138c2ecf20Sopenharmony_ci loopback->cables[substream->number][dev] = NULL; 10148c2ecf20Sopenharmony_ci kfree(cable); 10158c2ecf20Sopenharmony_ci } 10168c2ecf20Sopenharmony_ci} 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_cistatic int loopback_jiffies_timer_open(struct loopback_pcm *dpcm) 10198c2ecf20Sopenharmony_ci{ 10208c2ecf20Sopenharmony_ci timer_setup(&dpcm->timer, loopback_jiffies_timer_function, 0); 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci return 0; 10238c2ecf20Sopenharmony_ci} 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_cistatic struct loopback_ops loopback_jiffies_timer_ops = { 10268c2ecf20Sopenharmony_ci .open = loopback_jiffies_timer_open, 10278c2ecf20Sopenharmony_ci .start = loopback_jiffies_timer_start, 10288c2ecf20Sopenharmony_ci .stop = loopback_jiffies_timer_stop, 10298c2ecf20Sopenharmony_ci .stop_sync = loopback_jiffies_timer_stop_sync, 10308c2ecf20Sopenharmony_ci .close_substream = loopback_jiffies_timer_stop_sync, 10318c2ecf20Sopenharmony_ci .pos_update = loopback_jiffies_timer_pos_update, 10328c2ecf20Sopenharmony_ci .dpcm_info = loopback_jiffies_timer_dpcm_info, 10338c2ecf20Sopenharmony_ci}; 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_cistatic int loopback_parse_timer_id(const char *str, 10368c2ecf20Sopenharmony_ci struct snd_timer_id *tid) 10378c2ecf20Sopenharmony_ci{ 10388c2ecf20Sopenharmony_ci /* [<pref>:](<card name>|<card idx>)[{.,}<dev idx>[{.,}<subdev idx>]] */ 10398c2ecf20Sopenharmony_ci const char * const sep_dev = ".,"; 10408c2ecf20Sopenharmony_ci const char * const sep_pref = ":"; 10418c2ecf20Sopenharmony_ci const char *name = str; 10428c2ecf20Sopenharmony_ci char *sep, save = '\0'; 10438c2ecf20Sopenharmony_ci int card_idx = 0, dev = 0, subdev = 0; 10448c2ecf20Sopenharmony_ci int err; 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci sep = strpbrk(str, sep_pref); 10478c2ecf20Sopenharmony_ci if (sep) 10488c2ecf20Sopenharmony_ci name = sep + 1; 10498c2ecf20Sopenharmony_ci sep = strpbrk(name, sep_dev); 10508c2ecf20Sopenharmony_ci if (sep) { 10518c2ecf20Sopenharmony_ci save = *sep; 10528c2ecf20Sopenharmony_ci *sep = '\0'; 10538c2ecf20Sopenharmony_ci } 10548c2ecf20Sopenharmony_ci err = kstrtoint(name, 0, &card_idx); 10558c2ecf20Sopenharmony_ci if (err == -EINVAL) { 10568c2ecf20Sopenharmony_ci /* Must be the name, not number */ 10578c2ecf20Sopenharmony_ci for (card_idx = 0; card_idx < snd_ecards_limit; card_idx++) { 10588c2ecf20Sopenharmony_ci struct snd_card *card = snd_card_ref(card_idx); 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci if (card) { 10618c2ecf20Sopenharmony_ci if (!strcmp(card->id, name)) 10628c2ecf20Sopenharmony_ci err = 0; 10638c2ecf20Sopenharmony_ci snd_card_unref(card); 10648c2ecf20Sopenharmony_ci } 10658c2ecf20Sopenharmony_ci if (!err) 10668c2ecf20Sopenharmony_ci break; 10678c2ecf20Sopenharmony_ci } 10688c2ecf20Sopenharmony_ci } 10698c2ecf20Sopenharmony_ci if (sep) { 10708c2ecf20Sopenharmony_ci *sep = save; 10718c2ecf20Sopenharmony_ci if (!err) { 10728c2ecf20Sopenharmony_ci char *sep2, save2 = '\0'; 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci sep2 = strpbrk(sep + 1, sep_dev); 10758c2ecf20Sopenharmony_ci if (sep2) { 10768c2ecf20Sopenharmony_ci save2 = *sep2; 10778c2ecf20Sopenharmony_ci *sep2 = '\0'; 10788c2ecf20Sopenharmony_ci } 10798c2ecf20Sopenharmony_ci err = kstrtoint(sep + 1, 0, &dev); 10808c2ecf20Sopenharmony_ci if (sep2) { 10818c2ecf20Sopenharmony_ci *sep2 = save2; 10828c2ecf20Sopenharmony_ci if (!err) 10838c2ecf20Sopenharmony_ci err = kstrtoint(sep2 + 1, 0, &subdev); 10848c2ecf20Sopenharmony_ci } 10858c2ecf20Sopenharmony_ci } 10868c2ecf20Sopenharmony_ci } 10878c2ecf20Sopenharmony_ci if (!err && tid) { 10888c2ecf20Sopenharmony_ci tid->card = card_idx; 10898c2ecf20Sopenharmony_ci tid->device = dev; 10908c2ecf20Sopenharmony_ci tid->subdevice = subdev; 10918c2ecf20Sopenharmony_ci } 10928c2ecf20Sopenharmony_ci return err; 10938c2ecf20Sopenharmony_ci} 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci/* call in loopback->cable_lock */ 10968c2ecf20Sopenharmony_cistatic int loopback_snd_timer_open(struct loopback_pcm *dpcm) 10978c2ecf20Sopenharmony_ci{ 10988c2ecf20Sopenharmony_ci int err = 0; 10998c2ecf20Sopenharmony_ci struct snd_timer_id tid = { 11008c2ecf20Sopenharmony_ci .dev_class = SNDRV_TIMER_CLASS_PCM, 11018c2ecf20Sopenharmony_ci .dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION, 11028c2ecf20Sopenharmony_ci }; 11038c2ecf20Sopenharmony_ci struct snd_timer_instance *timeri; 11048c2ecf20Sopenharmony_ci struct loopback_cable *cable = dpcm->cable; 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci /* check if timer was already opened. It is only opened once 11078c2ecf20Sopenharmony_ci * per playback and capture subdevice (aka cable). 11088c2ecf20Sopenharmony_ci */ 11098c2ecf20Sopenharmony_ci if (cable->snd_timer.instance) 11108c2ecf20Sopenharmony_ci goto exit; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci err = loopback_parse_timer_id(dpcm->loopback->timer_source, &tid); 11138c2ecf20Sopenharmony_ci if (err < 0) { 11148c2ecf20Sopenharmony_ci pcm_err(dpcm->substream->pcm, 11158c2ecf20Sopenharmony_ci "Parsing timer source \'%s\' failed with %d", 11168c2ecf20Sopenharmony_ci dpcm->loopback->timer_source, err); 11178c2ecf20Sopenharmony_ci goto exit; 11188c2ecf20Sopenharmony_ci } 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci cable->snd_timer.stream = dpcm->substream->stream; 11218c2ecf20Sopenharmony_ci cable->snd_timer.id = tid; 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci timeri = snd_timer_instance_new(dpcm->loopback->card->id); 11248c2ecf20Sopenharmony_ci if (!timeri) { 11258c2ecf20Sopenharmony_ci err = -ENOMEM; 11268c2ecf20Sopenharmony_ci goto exit; 11278c2ecf20Sopenharmony_ci } 11288c2ecf20Sopenharmony_ci /* The callback has to be called from another work. If 11298c2ecf20Sopenharmony_ci * SNDRV_TIMER_IFLG_FAST is specified it will be called from the 11308c2ecf20Sopenharmony_ci * snd_pcm_period_elapsed() call of the selected sound card. 11318c2ecf20Sopenharmony_ci * snd_pcm_period_elapsed() helds snd_pcm_stream_lock_irqsave(). 11328c2ecf20Sopenharmony_ci * Due to our callback loopback_snd_timer_function() also calls 11338c2ecf20Sopenharmony_ci * snd_pcm_period_elapsed() which calls snd_pcm_stream_lock_irqsave(). 11348c2ecf20Sopenharmony_ci * This would end up in a dead lock. 11358c2ecf20Sopenharmony_ci */ 11368c2ecf20Sopenharmony_ci timeri->flags |= SNDRV_TIMER_IFLG_AUTO; 11378c2ecf20Sopenharmony_ci timeri->callback = loopback_snd_timer_function; 11388c2ecf20Sopenharmony_ci timeri->callback_data = (void *)cable; 11398c2ecf20Sopenharmony_ci timeri->ccallback = loopback_snd_timer_event; 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci /* initialise a work used for draining */ 11428c2ecf20Sopenharmony_ci INIT_WORK(&cable->snd_timer.event_work, loopback_snd_timer_work); 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci /* The mutex loopback->cable_lock is kept locked. 11458c2ecf20Sopenharmony_ci * Therefore snd_timer_open() cannot be called a second time 11468c2ecf20Sopenharmony_ci * by the other device of the same cable. 11478c2ecf20Sopenharmony_ci * Therefore the following issue cannot happen: 11488c2ecf20Sopenharmony_ci * [proc1] Call loopback_timer_open() -> 11498c2ecf20Sopenharmony_ci * Unlock cable->lock for snd_timer_close/open() call 11508c2ecf20Sopenharmony_ci * [proc2] Call loopback_timer_open() -> snd_timer_open(), 11518c2ecf20Sopenharmony_ci * snd_timer_start() 11528c2ecf20Sopenharmony_ci * [proc1] Call snd_timer_open() and overwrite running timer 11538c2ecf20Sopenharmony_ci * instance 11548c2ecf20Sopenharmony_ci */ 11558c2ecf20Sopenharmony_ci err = snd_timer_open(timeri, &cable->snd_timer.id, current->pid); 11568c2ecf20Sopenharmony_ci if (err < 0) { 11578c2ecf20Sopenharmony_ci pcm_err(dpcm->substream->pcm, 11588c2ecf20Sopenharmony_ci "snd_timer_open (%d,%d,%d) failed with %d", 11598c2ecf20Sopenharmony_ci cable->snd_timer.id.card, 11608c2ecf20Sopenharmony_ci cable->snd_timer.id.device, 11618c2ecf20Sopenharmony_ci cable->snd_timer.id.subdevice, 11628c2ecf20Sopenharmony_ci err); 11638c2ecf20Sopenharmony_ci snd_timer_instance_free(timeri); 11648c2ecf20Sopenharmony_ci goto exit; 11658c2ecf20Sopenharmony_ci } 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci cable->snd_timer.instance = timeri; 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ciexit: 11708c2ecf20Sopenharmony_ci return err; 11718c2ecf20Sopenharmony_ci} 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci/* stop_sync() is not required for sound timer because it does not need to be 11748c2ecf20Sopenharmony_ci * restarted in loopback_prepare() on Xrun recovery 11758c2ecf20Sopenharmony_ci */ 11768c2ecf20Sopenharmony_cistatic struct loopback_ops loopback_snd_timer_ops = { 11778c2ecf20Sopenharmony_ci .open = loopback_snd_timer_open, 11788c2ecf20Sopenharmony_ci .start = loopback_snd_timer_start, 11798c2ecf20Sopenharmony_ci .stop = loopback_snd_timer_stop, 11808c2ecf20Sopenharmony_ci .close_cable = loopback_snd_timer_close_cable, 11818c2ecf20Sopenharmony_ci .dpcm_info = loopback_snd_timer_dpcm_info, 11828c2ecf20Sopenharmony_ci}; 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_cistatic int loopback_open(struct snd_pcm_substream *substream) 11858c2ecf20Sopenharmony_ci{ 11868c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 11878c2ecf20Sopenharmony_ci struct loopback *loopback = substream->private_data; 11888c2ecf20Sopenharmony_ci struct loopback_pcm *dpcm; 11898c2ecf20Sopenharmony_ci struct loopback_cable *cable = NULL; 11908c2ecf20Sopenharmony_ci int err = 0; 11918c2ecf20Sopenharmony_ci int dev = get_cable_index(substream); 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci mutex_lock(&loopback->cable_lock); 11948c2ecf20Sopenharmony_ci dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL); 11958c2ecf20Sopenharmony_ci if (!dpcm) { 11968c2ecf20Sopenharmony_ci err = -ENOMEM; 11978c2ecf20Sopenharmony_ci goto unlock; 11988c2ecf20Sopenharmony_ci } 11998c2ecf20Sopenharmony_ci dpcm->loopback = loopback; 12008c2ecf20Sopenharmony_ci dpcm->substream = substream; 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci cable = loopback->cables[substream->number][dev]; 12038c2ecf20Sopenharmony_ci if (!cable) { 12048c2ecf20Sopenharmony_ci cable = kzalloc(sizeof(*cable), GFP_KERNEL); 12058c2ecf20Sopenharmony_ci if (!cable) { 12068c2ecf20Sopenharmony_ci err = -ENOMEM; 12078c2ecf20Sopenharmony_ci goto unlock; 12088c2ecf20Sopenharmony_ci } 12098c2ecf20Sopenharmony_ci spin_lock_init(&cable->lock); 12108c2ecf20Sopenharmony_ci cable->hw = loopback_pcm_hardware; 12118c2ecf20Sopenharmony_ci if (loopback->timer_source) 12128c2ecf20Sopenharmony_ci cable->ops = &loopback_snd_timer_ops; 12138c2ecf20Sopenharmony_ci else 12148c2ecf20Sopenharmony_ci cable->ops = &loopback_jiffies_timer_ops; 12158c2ecf20Sopenharmony_ci loopback->cables[substream->number][dev] = cable; 12168c2ecf20Sopenharmony_ci } 12178c2ecf20Sopenharmony_ci dpcm->cable = cable; 12188c2ecf20Sopenharmony_ci runtime->private_data = dpcm; 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci if (cable->ops->open) { 12218c2ecf20Sopenharmony_ci err = cable->ops->open(dpcm); 12228c2ecf20Sopenharmony_ci if (err < 0) 12238c2ecf20Sopenharmony_ci goto unlock; 12248c2ecf20Sopenharmony_ci } 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci /* use dynamic rules based on actual runtime->hw values */ 12298c2ecf20Sopenharmony_ci /* note that the default rules created in the PCM midlevel code */ 12308c2ecf20Sopenharmony_ci /* are cached -> they do not reflect the actual state */ 12318c2ecf20Sopenharmony_ci err = snd_pcm_hw_rule_add(runtime, 0, 12328c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_FORMAT, 12338c2ecf20Sopenharmony_ci rule_format, dpcm, 12348c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_FORMAT, -1); 12358c2ecf20Sopenharmony_ci if (err < 0) 12368c2ecf20Sopenharmony_ci goto unlock; 12378c2ecf20Sopenharmony_ci err = snd_pcm_hw_rule_add(runtime, 0, 12388c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, 12398c2ecf20Sopenharmony_ci rule_rate, dpcm, 12408c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, -1); 12418c2ecf20Sopenharmony_ci if (err < 0) 12428c2ecf20Sopenharmony_ci goto unlock; 12438c2ecf20Sopenharmony_ci err = snd_pcm_hw_rule_add(runtime, 0, 12448c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_CHANNELS, 12458c2ecf20Sopenharmony_ci rule_channels, dpcm, 12468c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_CHANNELS, -1); 12478c2ecf20Sopenharmony_ci if (err < 0) 12488c2ecf20Sopenharmony_ci goto unlock; 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci /* In case of sound timer the period time of both devices of the same 12518c2ecf20Sopenharmony_ci * loop has to be the same. 12528c2ecf20Sopenharmony_ci * This rule only takes effect if a sound timer was chosen 12538c2ecf20Sopenharmony_ci */ 12548c2ecf20Sopenharmony_ci if (cable->snd_timer.instance) { 12558c2ecf20Sopenharmony_ci err = snd_pcm_hw_rule_add(runtime, 0, 12568c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 12578c2ecf20Sopenharmony_ci rule_period_bytes, dpcm, 12588c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_PERIOD_BYTES, -1); 12598c2ecf20Sopenharmony_ci if (err < 0) 12608c2ecf20Sopenharmony_ci goto unlock; 12618c2ecf20Sopenharmony_ci } 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ci /* loopback_runtime_free() has not to be called if kfree(dpcm) was 12648c2ecf20Sopenharmony_ci * already called here. Otherwise it will end up with a double free. 12658c2ecf20Sopenharmony_ci */ 12668c2ecf20Sopenharmony_ci runtime->private_free = loopback_runtime_free; 12678c2ecf20Sopenharmony_ci if (get_notify(dpcm)) 12688c2ecf20Sopenharmony_ci runtime->hw = loopback_pcm_hardware; 12698c2ecf20Sopenharmony_ci else 12708c2ecf20Sopenharmony_ci runtime->hw = cable->hw; 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci spin_lock_irq(&cable->lock); 12738c2ecf20Sopenharmony_ci cable->streams[substream->stream] = dpcm; 12748c2ecf20Sopenharmony_ci spin_unlock_irq(&cable->lock); 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci unlock: 12778c2ecf20Sopenharmony_ci if (err < 0) { 12788c2ecf20Sopenharmony_ci free_cable(substream); 12798c2ecf20Sopenharmony_ci kfree(dpcm); 12808c2ecf20Sopenharmony_ci } 12818c2ecf20Sopenharmony_ci mutex_unlock(&loopback->cable_lock); 12828c2ecf20Sopenharmony_ci return err; 12838c2ecf20Sopenharmony_ci} 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_cistatic int loopback_close(struct snd_pcm_substream *substream) 12868c2ecf20Sopenharmony_ci{ 12878c2ecf20Sopenharmony_ci struct loopback *loopback = substream->private_data; 12888c2ecf20Sopenharmony_ci struct loopback_pcm *dpcm = substream->runtime->private_data; 12898c2ecf20Sopenharmony_ci int err = 0; 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci if (dpcm->cable->ops->close_substream) 12928c2ecf20Sopenharmony_ci err = dpcm->cable->ops->close_substream(dpcm); 12938c2ecf20Sopenharmony_ci mutex_lock(&loopback->cable_lock); 12948c2ecf20Sopenharmony_ci free_cable(substream); 12958c2ecf20Sopenharmony_ci mutex_unlock(&loopback->cable_lock); 12968c2ecf20Sopenharmony_ci return err; 12978c2ecf20Sopenharmony_ci} 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops loopback_pcm_ops = { 13008c2ecf20Sopenharmony_ci .open = loopback_open, 13018c2ecf20Sopenharmony_ci .close = loopback_close, 13028c2ecf20Sopenharmony_ci .hw_free = loopback_hw_free, 13038c2ecf20Sopenharmony_ci .prepare = loopback_prepare, 13048c2ecf20Sopenharmony_ci .trigger = loopback_trigger, 13058c2ecf20Sopenharmony_ci .pointer = loopback_pointer, 13068c2ecf20Sopenharmony_ci}; 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_cistatic int loopback_pcm_new(struct loopback *loopback, 13098c2ecf20Sopenharmony_ci int device, int substreams) 13108c2ecf20Sopenharmony_ci{ 13118c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 13128c2ecf20Sopenharmony_ci int err; 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci err = snd_pcm_new(loopback->card, "Loopback PCM", device, 13158c2ecf20Sopenharmony_ci substreams, substreams, &pcm); 13168c2ecf20Sopenharmony_ci if (err < 0) 13178c2ecf20Sopenharmony_ci return err; 13188c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &loopback_pcm_ops); 13198c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &loopback_pcm_ops); 13208c2ecf20Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0); 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci pcm->private_data = loopback; 13238c2ecf20Sopenharmony_ci pcm->info_flags = 0; 13248c2ecf20Sopenharmony_ci strcpy(pcm->name, "Loopback PCM"); 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci loopback->pcm[device] = pcm; 13278c2ecf20Sopenharmony_ci return 0; 13288c2ecf20Sopenharmony_ci} 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_cistatic int loopback_rate_shift_info(struct snd_kcontrol *kcontrol, 13318c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 13328c2ecf20Sopenharmony_ci{ 13338c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 13348c2ecf20Sopenharmony_ci uinfo->count = 1; 13358c2ecf20Sopenharmony_ci uinfo->value.integer.min = 80000; 13368c2ecf20Sopenharmony_ci uinfo->value.integer.max = 120000; 13378c2ecf20Sopenharmony_ci uinfo->value.integer.step = 1; 13388c2ecf20Sopenharmony_ci return 0; 13398c2ecf20Sopenharmony_ci} 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_cistatic int loopback_rate_shift_get(struct snd_kcontrol *kcontrol, 13428c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 13438c2ecf20Sopenharmony_ci{ 13448c2ecf20Sopenharmony_ci struct loopback *loopback = snd_kcontrol_chip(kcontrol); 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci mutex_lock(&loopback->cable_lock); 13478c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = 13488c2ecf20Sopenharmony_ci loopback->setup[kcontrol->id.subdevice] 13498c2ecf20Sopenharmony_ci [kcontrol->id.device].rate_shift; 13508c2ecf20Sopenharmony_ci mutex_unlock(&loopback->cable_lock); 13518c2ecf20Sopenharmony_ci return 0; 13528c2ecf20Sopenharmony_ci} 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_cistatic int loopback_rate_shift_put(struct snd_kcontrol *kcontrol, 13558c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 13568c2ecf20Sopenharmony_ci{ 13578c2ecf20Sopenharmony_ci struct loopback *loopback = snd_kcontrol_chip(kcontrol); 13588c2ecf20Sopenharmony_ci unsigned int val; 13598c2ecf20Sopenharmony_ci int change = 0; 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci val = ucontrol->value.integer.value[0]; 13628c2ecf20Sopenharmony_ci if (val < 80000) 13638c2ecf20Sopenharmony_ci val = 80000; 13648c2ecf20Sopenharmony_ci if (val > 120000) 13658c2ecf20Sopenharmony_ci val = 120000; 13668c2ecf20Sopenharmony_ci mutex_lock(&loopback->cable_lock); 13678c2ecf20Sopenharmony_ci if (val != loopback->setup[kcontrol->id.subdevice] 13688c2ecf20Sopenharmony_ci [kcontrol->id.device].rate_shift) { 13698c2ecf20Sopenharmony_ci loopback->setup[kcontrol->id.subdevice] 13708c2ecf20Sopenharmony_ci [kcontrol->id.device].rate_shift = val; 13718c2ecf20Sopenharmony_ci change = 1; 13728c2ecf20Sopenharmony_ci } 13738c2ecf20Sopenharmony_ci mutex_unlock(&loopback->cable_lock); 13748c2ecf20Sopenharmony_ci return change; 13758c2ecf20Sopenharmony_ci} 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_cistatic int loopback_notify_get(struct snd_kcontrol *kcontrol, 13788c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 13798c2ecf20Sopenharmony_ci{ 13808c2ecf20Sopenharmony_ci struct loopback *loopback = snd_kcontrol_chip(kcontrol); 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci mutex_lock(&loopback->cable_lock); 13838c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = 13848c2ecf20Sopenharmony_ci loopback->setup[kcontrol->id.subdevice] 13858c2ecf20Sopenharmony_ci [kcontrol->id.device].notify; 13868c2ecf20Sopenharmony_ci mutex_unlock(&loopback->cable_lock); 13878c2ecf20Sopenharmony_ci return 0; 13888c2ecf20Sopenharmony_ci} 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_cistatic int loopback_notify_put(struct snd_kcontrol *kcontrol, 13918c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 13928c2ecf20Sopenharmony_ci{ 13938c2ecf20Sopenharmony_ci struct loopback *loopback = snd_kcontrol_chip(kcontrol); 13948c2ecf20Sopenharmony_ci unsigned int val; 13958c2ecf20Sopenharmony_ci int change = 0; 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci val = ucontrol->value.integer.value[0] ? 1 : 0; 13988c2ecf20Sopenharmony_ci mutex_lock(&loopback->cable_lock); 13998c2ecf20Sopenharmony_ci if (val != loopback->setup[kcontrol->id.subdevice] 14008c2ecf20Sopenharmony_ci [kcontrol->id.device].notify) { 14018c2ecf20Sopenharmony_ci loopback->setup[kcontrol->id.subdevice] 14028c2ecf20Sopenharmony_ci [kcontrol->id.device].notify = val; 14038c2ecf20Sopenharmony_ci change = 1; 14048c2ecf20Sopenharmony_ci } 14058c2ecf20Sopenharmony_ci mutex_unlock(&loopback->cable_lock); 14068c2ecf20Sopenharmony_ci return change; 14078c2ecf20Sopenharmony_ci} 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_cistatic int loopback_active_get(struct snd_kcontrol *kcontrol, 14108c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 14118c2ecf20Sopenharmony_ci{ 14128c2ecf20Sopenharmony_ci struct loopback *loopback = snd_kcontrol_chip(kcontrol); 14138c2ecf20Sopenharmony_ci struct loopback_cable *cable; 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci unsigned int val = 0; 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci mutex_lock(&loopback->cable_lock); 14188c2ecf20Sopenharmony_ci cable = loopback->cables[kcontrol->id.subdevice][kcontrol->id.device ^ 1]; 14198c2ecf20Sopenharmony_ci if (cable != NULL) { 14208c2ecf20Sopenharmony_ci unsigned int running = cable->running ^ cable->pause; 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci val = (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ? 1 : 0; 14238c2ecf20Sopenharmony_ci } 14248c2ecf20Sopenharmony_ci mutex_unlock(&loopback->cable_lock); 14258c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = val; 14268c2ecf20Sopenharmony_ci return 0; 14278c2ecf20Sopenharmony_ci} 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_cistatic int loopback_format_info(struct snd_kcontrol *kcontrol, 14308c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 14318c2ecf20Sopenharmony_ci{ 14328c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 14338c2ecf20Sopenharmony_ci uinfo->count = 1; 14348c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 14358c2ecf20Sopenharmony_ci uinfo->value.integer.max = (__force int)SNDRV_PCM_FORMAT_LAST; 14368c2ecf20Sopenharmony_ci uinfo->value.integer.step = 1; 14378c2ecf20Sopenharmony_ci return 0; 14388c2ecf20Sopenharmony_ci} 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_cistatic int loopback_format_get(struct snd_kcontrol *kcontrol, 14418c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 14428c2ecf20Sopenharmony_ci{ 14438c2ecf20Sopenharmony_ci struct loopback *loopback = snd_kcontrol_chip(kcontrol); 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = 14468c2ecf20Sopenharmony_ci (__force int)loopback->setup[kcontrol->id.subdevice] 14478c2ecf20Sopenharmony_ci [kcontrol->id.device].format; 14488c2ecf20Sopenharmony_ci return 0; 14498c2ecf20Sopenharmony_ci} 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_cistatic int loopback_rate_info(struct snd_kcontrol *kcontrol, 14528c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 14538c2ecf20Sopenharmony_ci{ 14548c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 14558c2ecf20Sopenharmony_ci uinfo->count = 1; 14568c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 14578c2ecf20Sopenharmony_ci uinfo->value.integer.max = 192000; 14588c2ecf20Sopenharmony_ci uinfo->value.integer.step = 1; 14598c2ecf20Sopenharmony_ci return 0; 14608c2ecf20Sopenharmony_ci} 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_cistatic int loopback_rate_get(struct snd_kcontrol *kcontrol, 14638c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 14648c2ecf20Sopenharmony_ci{ 14658c2ecf20Sopenharmony_ci struct loopback *loopback = snd_kcontrol_chip(kcontrol); 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci mutex_lock(&loopback->cable_lock); 14688c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = 14698c2ecf20Sopenharmony_ci loopback->setup[kcontrol->id.subdevice] 14708c2ecf20Sopenharmony_ci [kcontrol->id.device].rate; 14718c2ecf20Sopenharmony_ci mutex_unlock(&loopback->cable_lock); 14728c2ecf20Sopenharmony_ci return 0; 14738c2ecf20Sopenharmony_ci} 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_cistatic int loopback_channels_info(struct snd_kcontrol *kcontrol, 14768c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 14778c2ecf20Sopenharmony_ci{ 14788c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 14798c2ecf20Sopenharmony_ci uinfo->count = 1; 14808c2ecf20Sopenharmony_ci uinfo->value.integer.min = 1; 14818c2ecf20Sopenharmony_ci uinfo->value.integer.max = 1024; 14828c2ecf20Sopenharmony_ci uinfo->value.integer.step = 1; 14838c2ecf20Sopenharmony_ci return 0; 14848c2ecf20Sopenharmony_ci} 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_cistatic int loopback_channels_get(struct snd_kcontrol *kcontrol, 14878c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 14888c2ecf20Sopenharmony_ci{ 14898c2ecf20Sopenharmony_ci struct loopback *loopback = snd_kcontrol_chip(kcontrol); 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci mutex_lock(&loopback->cable_lock); 14928c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = 14938c2ecf20Sopenharmony_ci loopback->setup[kcontrol->id.subdevice] 14948c2ecf20Sopenharmony_ci [kcontrol->id.device].channels; 14958c2ecf20Sopenharmony_ci mutex_unlock(&loopback->cable_lock); 14968c2ecf20Sopenharmony_ci return 0; 14978c2ecf20Sopenharmony_ci} 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new loopback_controls[] = { 15008c2ecf20Sopenharmony_ci{ 15018c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 15028c2ecf20Sopenharmony_ci .name = "PCM Rate Shift 100000", 15038c2ecf20Sopenharmony_ci .info = loopback_rate_shift_info, 15048c2ecf20Sopenharmony_ci .get = loopback_rate_shift_get, 15058c2ecf20Sopenharmony_ci .put = loopback_rate_shift_put, 15068c2ecf20Sopenharmony_ci}, 15078c2ecf20Sopenharmony_ci{ 15088c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 15098c2ecf20Sopenharmony_ci .name = "PCM Notify", 15108c2ecf20Sopenharmony_ci .info = snd_ctl_boolean_mono_info, 15118c2ecf20Sopenharmony_ci .get = loopback_notify_get, 15128c2ecf20Sopenharmony_ci .put = loopback_notify_put, 15138c2ecf20Sopenharmony_ci}, 15148c2ecf20Sopenharmony_ci#define ACTIVE_IDX 2 15158c2ecf20Sopenharmony_ci{ 15168c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 15178c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 15188c2ecf20Sopenharmony_ci .name = "PCM Slave Active", 15198c2ecf20Sopenharmony_ci .info = snd_ctl_boolean_mono_info, 15208c2ecf20Sopenharmony_ci .get = loopback_active_get, 15218c2ecf20Sopenharmony_ci}, 15228c2ecf20Sopenharmony_ci#define FORMAT_IDX 3 15238c2ecf20Sopenharmony_ci{ 15248c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 15258c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 15268c2ecf20Sopenharmony_ci .name = "PCM Slave Format", 15278c2ecf20Sopenharmony_ci .info = loopback_format_info, 15288c2ecf20Sopenharmony_ci .get = loopback_format_get 15298c2ecf20Sopenharmony_ci}, 15308c2ecf20Sopenharmony_ci#define RATE_IDX 4 15318c2ecf20Sopenharmony_ci{ 15328c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 15338c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 15348c2ecf20Sopenharmony_ci .name = "PCM Slave Rate", 15358c2ecf20Sopenharmony_ci .info = loopback_rate_info, 15368c2ecf20Sopenharmony_ci .get = loopback_rate_get 15378c2ecf20Sopenharmony_ci}, 15388c2ecf20Sopenharmony_ci#define CHANNELS_IDX 5 15398c2ecf20Sopenharmony_ci{ 15408c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 15418c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 15428c2ecf20Sopenharmony_ci .name = "PCM Slave Channels", 15438c2ecf20Sopenharmony_ci .info = loopback_channels_info, 15448c2ecf20Sopenharmony_ci .get = loopback_channels_get 15458c2ecf20Sopenharmony_ci} 15468c2ecf20Sopenharmony_ci}; 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_cistatic int loopback_mixer_new(struct loopback *loopback, int notify) 15498c2ecf20Sopenharmony_ci{ 15508c2ecf20Sopenharmony_ci struct snd_card *card = loopback->card; 15518c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 15528c2ecf20Sopenharmony_ci struct snd_kcontrol *kctl; 15538c2ecf20Sopenharmony_ci struct loopback_setup *setup; 15548c2ecf20Sopenharmony_ci int err, dev, substr, substr_count, idx; 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci strcpy(card->mixername, "Loopback Mixer"); 15578c2ecf20Sopenharmony_ci for (dev = 0; dev < 2; dev++) { 15588c2ecf20Sopenharmony_ci pcm = loopback->pcm[dev]; 15598c2ecf20Sopenharmony_ci substr_count = 15608c2ecf20Sopenharmony_ci pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count; 15618c2ecf20Sopenharmony_ci for (substr = 0; substr < substr_count; substr++) { 15628c2ecf20Sopenharmony_ci setup = &loopback->setup[substr][dev]; 15638c2ecf20Sopenharmony_ci setup->notify = notify; 15648c2ecf20Sopenharmony_ci setup->rate_shift = NO_PITCH; 15658c2ecf20Sopenharmony_ci setup->format = SNDRV_PCM_FORMAT_S16_LE; 15668c2ecf20Sopenharmony_ci setup->rate = 48000; 15678c2ecf20Sopenharmony_ci setup->channels = 2; 15688c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(loopback_controls); 15698c2ecf20Sopenharmony_ci idx++) { 15708c2ecf20Sopenharmony_ci kctl = snd_ctl_new1(&loopback_controls[idx], 15718c2ecf20Sopenharmony_ci loopback); 15728c2ecf20Sopenharmony_ci if (!kctl) 15738c2ecf20Sopenharmony_ci return -ENOMEM; 15748c2ecf20Sopenharmony_ci kctl->id.device = dev; 15758c2ecf20Sopenharmony_ci kctl->id.subdevice = substr; 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci /* Add the control before copying the id so that 15788c2ecf20Sopenharmony_ci * the numid field of the id is set in the copy. 15798c2ecf20Sopenharmony_ci */ 15808c2ecf20Sopenharmony_ci err = snd_ctl_add(card, kctl); 15818c2ecf20Sopenharmony_ci if (err < 0) 15828c2ecf20Sopenharmony_ci return err; 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci switch (idx) { 15858c2ecf20Sopenharmony_ci case ACTIVE_IDX: 15868c2ecf20Sopenharmony_ci setup->active_id = kctl->id; 15878c2ecf20Sopenharmony_ci break; 15888c2ecf20Sopenharmony_ci case FORMAT_IDX: 15898c2ecf20Sopenharmony_ci setup->format_id = kctl->id; 15908c2ecf20Sopenharmony_ci break; 15918c2ecf20Sopenharmony_ci case RATE_IDX: 15928c2ecf20Sopenharmony_ci setup->rate_id = kctl->id; 15938c2ecf20Sopenharmony_ci break; 15948c2ecf20Sopenharmony_ci case CHANNELS_IDX: 15958c2ecf20Sopenharmony_ci setup->channels_id = kctl->id; 15968c2ecf20Sopenharmony_ci break; 15978c2ecf20Sopenharmony_ci default: 15988c2ecf20Sopenharmony_ci break; 15998c2ecf20Sopenharmony_ci } 16008c2ecf20Sopenharmony_ci } 16018c2ecf20Sopenharmony_ci } 16028c2ecf20Sopenharmony_ci } 16038c2ecf20Sopenharmony_ci return 0; 16048c2ecf20Sopenharmony_ci} 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_cistatic void print_dpcm_info(struct snd_info_buffer *buffer, 16078c2ecf20Sopenharmony_ci struct loopback_pcm *dpcm, 16088c2ecf20Sopenharmony_ci const char *id) 16098c2ecf20Sopenharmony_ci{ 16108c2ecf20Sopenharmony_ci snd_iprintf(buffer, " %s\n", id); 16118c2ecf20Sopenharmony_ci if (dpcm == NULL) { 16128c2ecf20Sopenharmony_ci snd_iprintf(buffer, " inactive\n"); 16138c2ecf20Sopenharmony_ci return; 16148c2ecf20Sopenharmony_ci } 16158c2ecf20Sopenharmony_ci snd_iprintf(buffer, " buffer_size:\t%u\n", dpcm->pcm_buffer_size); 16168c2ecf20Sopenharmony_ci snd_iprintf(buffer, " buffer_pos:\t\t%u\n", dpcm->buf_pos); 16178c2ecf20Sopenharmony_ci snd_iprintf(buffer, " silent_size:\t%u\n", dpcm->silent_size); 16188c2ecf20Sopenharmony_ci snd_iprintf(buffer, " period_size:\t%u\n", dpcm->pcm_period_size); 16198c2ecf20Sopenharmony_ci snd_iprintf(buffer, " bytes_per_sec:\t%u\n", dpcm->pcm_bps); 16208c2ecf20Sopenharmony_ci snd_iprintf(buffer, " sample_align:\t%u\n", dpcm->pcm_salign); 16218c2ecf20Sopenharmony_ci snd_iprintf(buffer, " rate_shift:\t\t%u\n", dpcm->pcm_rate_shift); 16228c2ecf20Sopenharmony_ci if (dpcm->cable->ops->dpcm_info) 16238c2ecf20Sopenharmony_ci dpcm->cable->ops->dpcm_info(dpcm, buffer); 16248c2ecf20Sopenharmony_ci} 16258c2ecf20Sopenharmony_ci 16268c2ecf20Sopenharmony_cistatic void print_substream_info(struct snd_info_buffer *buffer, 16278c2ecf20Sopenharmony_ci struct loopback *loopback, 16288c2ecf20Sopenharmony_ci int sub, 16298c2ecf20Sopenharmony_ci int num) 16308c2ecf20Sopenharmony_ci{ 16318c2ecf20Sopenharmony_ci struct loopback_cable *cable = loopback->cables[sub][num]; 16328c2ecf20Sopenharmony_ci 16338c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Cable %i substream %i:\n", num, sub); 16348c2ecf20Sopenharmony_ci if (cable == NULL) { 16358c2ecf20Sopenharmony_ci snd_iprintf(buffer, " inactive\n"); 16368c2ecf20Sopenharmony_ci return; 16378c2ecf20Sopenharmony_ci } 16388c2ecf20Sopenharmony_ci snd_iprintf(buffer, " valid: %u\n", cable->valid); 16398c2ecf20Sopenharmony_ci snd_iprintf(buffer, " running: %u\n", cable->running); 16408c2ecf20Sopenharmony_ci snd_iprintf(buffer, " pause: %u\n", cable->pause); 16418c2ecf20Sopenharmony_ci print_dpcm_info(buffer, cable->streams[0], "Playback"); 16428c2ecf20Sopenharmony_ci print_dpcm_info(buffer, cable->streams[1], "Capture"); 16438c2ecf20Sopenharmony_ci} 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_cistatic void print_cable_info(struct snd_info_entry *entry, 16468c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 16478c2ecf20Sopenharmony_ci{ 16488c2ecf20Sopenharmony_ci struct loopback *loopback = entry->private_data; 16498c2ecf20Sopenharmony_ci int sub, num; 16508c2ecf20Sopenharmony_ci 16518c2ecf20Sopenharmony_ci mutex_lock(&loopback->cable_lock); 16528c2ecf20Sopenharmony_ci num = entry->name[strlen(entry->name)-1]; 16538c2ecf20Sopenharmony_ci num = num == '0' ? 0 : 1; 16548c2ecf20Sopenharmony_ci for (sub = 0; sub < MAX_PCM_SUBSTREAMS; sub++) 16558c2ecf20Sopenharmony_ci print_substream_info(buffer, loopback, sub, num); 16568c2ecf20Sopenharmony_ci mutex_unlock(&loopback->cable_lock); 16578c2ecf20Sopenharmony_ci} 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_cistatic int loopback_cable_proc_new(struct loopback *loopback, int cidx) 16608c2ecf20Sopenharmony_ci{ 16618c2ecf20Sopenharmony_ci char name[32]; 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci snprintf(name, sizeof(name), "cable#%d", cidx); 16648c2ecf20Sopenharmony_ci return snd_card_ro_proc_new(loopback->card, name, loopback, 16658c2ecf20Sopenharmony_ci print_cable_info); 16668c2ecf20Sopenharmony_ci} 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_cistatic void loopback_set_timer_source(struct loopback *loopback, 16698c2ecf20Sopenharmony_ci const char *value) 16708c2ecf20Sopenharmony_ci{ 16718c2ecf20Sopenharmony_ci if (loopback->timer_source) { 16728c2ecf20Sopenharmony_ci devm_kfree(loopback->card->dev, loopback->timer_source); 16738c2ecf20Sopenharmony_ci loopback->timer_source = NULL; 16748c2ecf20Sopenharmony_ci } 16758c2ecf20Sopenharmony_ci if (value && *value) 16768c2ecf20Sopenharmony_ci loopback->timer_source = devm_kstrdup(loopback->card->dev, 16778c2ecf20Sopenharmony_ci value, GFP_KERNEL); 16788c2ecf20Sopenharmony_ci} 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_cistatic void print_timer_source_info(struct snd_info_entry *entry, 16818c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 16828c2ecf20Sopenharmony_ci{ 16838c2ecf20Sopenharmony_ci struct loopback *loopback = entry->private_data; 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_ci mutex_lock(&loopback->cable_lock); 16868c2ecf20Sopenharmony_ci snd_iprintf(buffer, "%s\n", 16878c2ecf20Sopenharmony_ci loopback->timer_source ? loopback->timer_source : ""); 16888c2ecf20Sopenharmony_ci mutex_unlock(&loopback->cable_lock); 16898c2ecf20Sopenharmony_ci} 16908c2ecf20Sopenharmony_ci 16918c2ecf20Sopenharmony_cistatic void change_timer_source_info(struct snd_info_entry *entry, 16928c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 16938c2ecf20Sopenharmony_ci{ 16948c2ecf20Sopenharmony_ci struct loopback *loopback = entry->private_data; 16958c2ecf20Sopenharmony_ci char line[64]; 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_ci mutex_lock(&loopback->cable_lock); 16988c2ecf20Sopenharmony_ci if (!snd_info_get_line(buffer, line, sizeof(line))) 16998c2ecf20Sopenharmony_ci loopback_set_timer_source(loopback, strim(line)); 17008c2ecf20Sopenharmony_ci mutex_unlock(&loopback->cable_lock); 17018c2ecf20Sopenharmony_ci} 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_cistatic int loopback_timer_source_proc_new(struct loopback *loopback) 17048c2ecf20Sopenharmony_ci{ 17058c2ecf20Sopenharmony_ci return snd_card_rw_proc_new(loopback->card, "timer_source", loopback, 17068c2ecf20Sopenharmony_ci print_timer_source_info, 17078c2ecf20Sopenharmony_ci change_timer_source_info); 17088c2ecf20Sopenharmony_ci} 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_cistatic int loopback_probe(struct platform_device *devptr) 17118c2ecf20Sopenharmony_ci{ 17128c2ecf20Sopenharmony_ci struct snd_card *card; 17138c2ecf20Sopenharmony_ci struct loopback *loopback; 17148c2ecf20Sopenharmony_ci int dev = devptr->id; 17158c2ecf20Sopenharmony_ci int err; 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_ci err = snd_card_new(&devptr->dev, index[dev], id[dev], THIS_MODULE, 17188c2ecf20Sopenharmony_ci sizeof(struct loopback), &card); 17198c2ecf20Sopenharmony_ci if (err < 0) 17208c2ecf20Sopenharmony_ci return err; 17218c2ecf20Sopenharmony_ci loopback = card->private_data; 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_ci if (pcm_substreams[dev] < 1) 17248c2ecf20Sopenharmony_ci pcm_substreams[dev] = 1; 17258c2ecf20Sopenharmony_ci if (pcm_substreams[dev] > MAX_PCM_SUBSTREAMS) 17268c2ecf20Sopenharmony_ci pcm_substreams[dev] = MAX_PCM_SUBSTREAMS; 17278c2ecf20Sopenharmony_ci 17288c2ecf20Sopenharmony_ci loopback->card = card; 17298c2ecf20Sopenharmony_ci loopback_set_timer_source(loopback, timer_source[dev]); 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci mutex_init(&loopback->cable_lock); 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_ci err = loopback_pcm_new(loopback, 0, pcm_substreams[dev]); 17348c2ecf20Sopenharmony_ci if (err < 0) 17358c2ecf20Sopenharmony_ci goto __nodev; 17368c2ecf20Sopenharmony_ci err = loopback_pcm_new(loopback, 1, pcm_substreams[dev]); 17378c2ecf20Sopenharmony_ci if (err < 0) 17388c2ecf20Sopenharmony_ci goto __nodev; 17398c2ecf20Sopenharmony_ci err = loopback_mixer_new(loopback, pcm_notify[dev] ? 1 : 0); 17408c2ecf20Sopenharmony_ci if (err < 0) 17418c2ecf20Sopenharmony_ci goto __nodev; 17428c2ecf20Sopenharmony_ci loopback_cable_proc_new(loopback, 0); 17438c2ecf20Sopenharmony_ci loopback_cable_proc_new(loopback, 1); 17448c2ecf20Sopenharmony_ci loopback_timer_source_proc_new(loopback); 17458c2ecf20Sopenharmony_ci strcpy(card->driver, "Loopback"); 17468c2ecf20Sopenharmony_ci strcpy(card->shortname, "Loopback"); 17478c2ecf20Sopenharmony_ci sprintf(card->longname, "Loopback %i", dev + 1); 17488c2ecf20Sopenharmony_ci err = snd_card_register(card); 17498c2ecf20Sopenharmony_ci if (!err) { 17508c2ecf20Sopenharmony_ci platform_set_drvdata(devptr, card); 17518c2ecf20Sopenharmony_ci return 0; 17528c2ecf20Sopenharmony_ci } 17538c2ecf20Sopenharmony_ci __nodev: 17548c2ecf20Sopenharmony_ci snd_card_free(card); 17558c2ecf20Sopenharmony_ci return err; 17568c2ecf20Sopenharmony_ci} 17578c2ecf20Sopenharmony_ci 17588c2ecf20Sopenharmony_cistatic int loopback_remove(struct platform_device *devptr) 17598c2ecf20Sopenharmony_ci{ 17608c2ecf20Sopenharmony_ci snd_card_free(platform_get_drvdata(devptr)); 17618c2ecf20Sopenharmony_ci return 0; 17628c2ecf20Sopenharmony_ci} 17638c2ecf20Sopenharmony_ci 17648c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 17658c2ecf20Sopenharmony_cistatic int loopback_suspend(struct device *pdev) 17668c2ecf20Sopenharmony_ci{ 17678c2ecf20Sopenharmony_ci struct snd_card *card = dev_get_drvdata(pdev); 17688c2ecf20Sopenharmony_ci 17698c2ecf20Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); 17708c2ecf20Sopenharmony_ci return 0; 17718c2ecf20Sopenharmony_ci} 17728c2ecf20Sopenharmony_ci 17738c2ecf20Sopenharmony_cistatic int loopback_resume(struct device *pdev) 17748c2ecf20Sopenharmony_ci{ 17758c2ecf20Sopenharmony_ci struct snd_card *card = dev_get_drvdata(pdev); 17768c2ecf20Sopenharmony_ci 17778c2ecf20Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D0); 17788c2ecf20Sopenharmony_ci return 0; 17798c2ecf20Sopenharmony_ci} 17808c2ecf20Sopenharmony_ci 17818c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(loopback_pm, loopback_suspend, loopback_resume); 17828c2ecf20Sopenharmony_ci#define LOOPBACK_PM_OPS &loopback_pm 17838c2ecf20Sopenharmony_ci#else 17848c2ecf20Sopenharmony_ci#define LOOPBACK_PM_OPS NULL 17858c2ecf20Sopenharmony_ci#endif 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_ci#define SND_LOOPBACK_DRIVER "snd_aloop" 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_cistatic struct platform_driver loopback_driver = { 17908c2ecf20Sopenharmony_ci .probe = loopback_probe, 17918c2ecf20Sopenharmony_ci .remove = loopback_remove, 17928c2ecf20Sopenharmony_ci .driver = { 17938c2ecf20Sopenharmony_ci .name = SND_LOOPBACK_DRIVER, 17948c2ecf20Sopenharmony_ci .pm = LOOPBACK_PM_OPS, 17958c2ecf20Sopenharmony_ci }, 17968c2ecf20Sopenharmony_ci}; 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_cistatic void loopback_unregister_all(void) 17998c2ecf20Sopenharmony_ci{ 18008c2ecf20Sopenharmony_ci int i; 18018c2ecf20Sopenharmony_ci 18028c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(devices); ++i) 18038c2ecf20Sopenharmony_ci platform_device_unregister(devices[i]); 18048c2ecf20Sopenharmony_ci platform_driver_unregister(&loopback_driver); 18058c2ecf20Sopenharmony_ci} 18068c2ecf20Sopenharmony_ci 18078c2ecf20Sopenharmony_cistatic int __init alsa_card_loopback_init(void) 18088c2ecf20Sopenharmony_ci{ 18098c2ecf20Sopenharmony_ci int i, err, cards; 18108c2ecf20Sopenharmony_ci 18118c2ecf20Sopenharmony_ci err = platform_driver_register(&loopback_driver); 18128c2ecf20Sopenharmony_ci if (err < 0) 18138c2ecf20Sopenharmony_ci return err; 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ci cards = 0; 18178c2ecf20Sopenharmony_ci for (i = 0; i < SNDRV_CARDS; i++) { 18188c2ecf20Sopenharmony_ci struct platform_device *device; 18198c2ecf20Sopenharmony_ci if (!enable[i]) 18208c2ecf20Sopenharmony_ci continue; 18218c2ecf20Sopenharmony_ci device = platform_device_register_simple(SND_LOOPBACK_DRIVER, 18228c2ecf20Sopenharmony_ci i, NULL, 0); 18238c2ecf20Sopenharmony_ci if (IS_ERR(device)) 18248c2ecf20Sopenharmony_ci continue; 18258c2ecf20Sopenharmony_ci if (!platform_get_drvdata(device)) { 18268c2ecf20Sopenharmony_ci platform_device_unregister(device); 18278c2ecf20Sopenharmony_ci continue; 18288c2ecf20Sopenharmony_ci } 18298c2ecf20Sopenharmony_ci devices[i] = device; 18308c2ecf20Sopenharmony_ci cards++; 18318c2ecf20Sopenharmony_ci } 18328c2ecf20Sopenharmony_ci if (!cards) { 18338c2ecf20Sopenharmony_ci#ifdef MODULE 18348c2ecf20Sopenharmony_ci printk(KERN_ERR "aloop: No loopback enabled\n"); 18358c2ecf20Sopenharmony_ci#endif 18368c2ecf20Sopenharmony_ci loopback_unregister_all(); 18378c2ecf20Sopenharmony_ci return -ENODEV; 18388c2ecf20Sopenharmony_ci } 18398c2ecf20Sopenharmony_ci return 0; 18408c2ecf20Sopenharmony_ci} 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_cistatic void __exit alsa_card_loopback_exit(void) 18438c2ecf20Sopenharmony_ci{ 18448c2ecf20Sopenharmony_ci loopback_unregister_all(); 18458c2ecf20Sopenharmony_ci} 18468c2ecf20Sopenharmony_ci 18478c2ecf20Sopenharmony_cimodule_init(alsa_card_loopback_init) 18488c2ecf20Sopenharmony_cimodule_exit(alsa_card_loopback_exit) 1849