18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Line 6 Linux USB driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/slab.h> 98c2ecf20Sopenharmony_ci#include <linux/export.h> 108c2ecf20Sopenharmony_ci#include <sound/core.h> 118c2ecf20Sopenharmony_ci#include <sound/control.h> 128c2ecf20Sopenharmony_ci#include <sound/pcm.h> 138c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "capture.h" 168c2ecf20Sopenharmony_ci#include "driver.h" 178c2ecf20Sopenharmony_ci#include "playback.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* impulse response volume controls */ 208c2ecf20Sopenharmony_cistatic int snd_line6_impulse_volume_info(struct snd_kcontrol *kcontrol, 218c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 228c2ecf20Sopenharmony_ci{ 238c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 248c2ecf20Sopenharmony_ci uinfo->count = 1; 258c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 268c2ecf20Sopenharmony_ci uinfo->value.integer.max = 255; 278c2ecf20Sopenharmony_ci return 0; 288c2ecf20Sopenharmony_ci} 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic int snd_line6_impulse_volume_get(struct snd_kcontrol *kcontrol, 318c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = line6pcm->impulse_volume; 368c2ecf20Sopenharmony_ci return 0; 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic int snd_line6_impulse_volume_put(struct snd_kcontrol *kcontrol, 408c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); 438c2ecf20Sopenharmony_ci int value = ucontrol->value.integer.value[0]; 448c2ecf20Sopenharmony_ci int err; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci if (line6pcm->impulse_volume == value) 478c2ecf20Sopenharmony_ci return 0; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci line6pcm->impulse_volume = value; 508c2ecf20Sopenharmony_ci if (value > 0) { 518c2ecf20Sopenharmony_ci err = line6_pcm_acquire(line6pcm, LINE6_STREAM_IMPULSE, true); 528c2ecf20Sopenharmony_ci if (err < 0) { 538c2ecf20Sopenharmony_ci line6pcm->impulse_volume = 0; 548c2ecf20Sopenharmony_ci return err; 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci } else { 578c2ecf20Sopenharmony_ci line6_pcm_release(line6pcm, LINE6_STREAM_IMPULSE); 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci return 1; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* impulse response period controls */ 638c2ecf20Sopenharmony_cistatic int snd_line6_impulse_period_info(struct snd_kcontrol *kcontrol, 648c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 678c2ecf20Sopenharmony_ci uinfo->count = 1; 688c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 698c2ecf20Sopenharmony_ci uinfo->value.integer.max = 2000; 708c2ecf20Sopenharmony_ci return 0; 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic int snd_line6_impulse_period_get(struct snd_kcontrol *kcontrol, 748c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = line6pcm->impulse_period; 798c2ecf20Sopenharmony_ci return 0; 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic int snd_line6_impulse_period_put(struct snd_kcontrol *kcontrol, 838c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); 868c2ecf20Sopenharmony_ci int value = ucontrol->value.integer.value[0]; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (line6pcm->impulse_period == value) 898c2ecf20Sopenharmony_ci return 0; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci line6pcm->impulse_period = value; 928c2ecf20Sopenharmony_ci return 1; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/* 968c2ecf20Sopenharmony_ci Unlink all currently active URBs. 978c2ecf20Sopenharmony_ci*/ 988c2ecf20Sopenharmony_cistatic void line6_unlink_audio_urbs(struct snd_line6_pcm *line6pcm, 998c2ecf20Sopenharmony_ci struct line6_pcm_stream *pcms) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci int i; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci for (i = 0; i < line6pcm->line6->iso_buffers; i++) { 1048c2ecf20Sopenharmony_ci if (test_bit(i, &pcms->active_urbs)) { 1058c2ecf20Sopenharmony_ci if (!test_and_set_bit(i, &pcms->unlink_urbs)) 1068c2ecf20Sopenharmony_ci usb_unlink_urb(pcms->urbs[i]); 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* 1128c2ecf20Sopenharmony_ci Wait until unlinking of all currently active URBs has been finished. 1138c2ecf20Sopenharmony_ci*/ 1148c2ecf20Sopenharmony_cistatic void line6_wait_clear_audio_urbs(struct snd_line6_pcm *line6pcm, 1158c2ecf20Sopenharmony_ci struct line6_pcm_stream *pcms) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci int timeout = HZ; 1188c2ecf20Sopenharmony_ci int i; 1198c2ecf20Sopenharmony_ci int alive; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci do { 1228c2ecf20Sopenharmony_ci alive = 0; 1238c2ecf20Sopenharmony_ci for (i = 0; i < line6pcm->line6->iso_buffers; i++) { 1248c2ecf20Sopenharmony_ci if (test_bit(i, &pcms->active_urbs)) 1258c2ecf20Sopenharmony_ci alive++; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci if (!alive) 1288c2ecf20Sopenharmony_ci break; 1298c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 1308c2ecf20Sopenharmony_ci schedule_timeout(1); 1318c2ecf20Sopenharmony_ci } while (--timeout > 0); 1328c2ecf20Sopenharmony_ci if (alive) 1338c2ecf20Sopenharmony_ci dev_err(line6pcm->line6->ifcdev, 1348c2ecf20Sopenharmony_ci "timeout: still %d active urbs..\n", alive); 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic inline struct line6_pcm_stream * 1388c2ecf20Sopenharmony_ciget_stream(struct snd_line6_pcm *line6pcm, int direction) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci return (direction == SNDRV_PCM_STREAM_PLAYBACK) ? 1418c2ecf20Sopenharmony_ci &line6pcm->out : &line6pcm->in; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci/* allocate a buffer if not opened yet; 1458c2ecf20Sopenharmony_ci * call this in line6pcm.state_mutex 1468c2ecf20Sopenharmony_ci */ 1478c2ecf20Sopenharmony_cistatic int line6_buffer_acquire(struct snd_line6_pcm *line6pcm, 1488c2ecf20Sopenharmony_ci struct line6_pcm_stream *pstr, int direction, int type) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci const int pkt_size = 1518c2ecf20Sopenharmony_ci (direction == SNDRV_PCM_STREAM_PLAYBACK) ? 1528c2ecf20Sopenharmony_ci line6pcm->max_packet_size_out : 1538c2ecf20Sopenharmony_ci line6pcm->max_packet_size_in; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci /* Invoked multiple times in a row so allocate once only */ 1568c2ecf20Sopenharmony_ci if (!test_and_set_bit(type, &pstr->opened) && !pstr->buffer) { 1578c2ecf20Sopenharmony_ci pstr->buffer = 1588c2ecf20Sopenharmony_ci kmalloc(array3_size(line6pcm->line6->iso_buffers, 1598c2ecf20Sopenharmony_ci LINE6_ISO_PACKETS, pkt_size), 1608c2ecf20Sopenharmony_ci GFP_KERNEL); 1618c2ecf20Sopenharmony_ci if (!pstr->buffer) 1628c2ecf20Sopenharmony_ci return -ENOMEM; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci return 0; 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci/* free a buffer if all streams are closed; 1688c2ecf20Sopenharmony_ci * call this in line6pcm.state_mutex 1698c2ecf20Sopenharmony_ci */ 1708c2ecf20Sopenharmony_cistatic void line6_buffer_release(struct snd_line6_pcm *line6pcm, 1718c2ecf20Sopenharmony_ci struct line6_pcm_stream *pstr, int type) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci clear_bit(type, &pstr->opened); 1748c2ecf20Sopenharmony_ci if (!pstr->opened) { 1758c2ecf20Sopenharmony_ci line6_wait_clear_audio_urbs(line6pcm, pstr); 1768c2ecf20Sopenharmony_ci kfree(pstr->buffer); 1778c2ecf20Sopenharmony_ci pstr->buffer = NULL; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci/* start a PCM stream */ 1828c2ecf20Sopenharmony_cistatic int line6_stream_start(struct snd_line6_pcm *line6pcm, int direction, 1838c2ecf20Sopenharmony_ci int type) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci unsigned long flags; 1868c2ecf20Sopenharmony_ci struct line6_pcm_stream *pstr = get_stream(line6pcm, direction); 1878c2ecf20Sopenharmony_ci int ret = 0; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci spin_lock_irqsave(&pstr->lock, flags); 1908c2ecf20Sopenharmony_ci if (!test_and_set_bit(type, &pstr->running) && 1918c2ecf20Sopenharmony_ci !(pstr->active_urbs || pstr->unlink_urbs)) { 1928c2ecf20Sopenharmony_ci pstr->count = 0; 1938c2ecf20Sopenharmony_ci /* Submit all currently available URBs */ 1948c2ecf20Sopenharmony_ci if (direction == SNDRV_PCM_STREAM_PLAYBACK) 1958c2ecf20Sopenharmony_ci ret = line6_submit_audio_out_all_urbs(line6pcm); 1968c2ecf20Sopenharmony_ci else 1978c2ecf20Sopenharmony_ci ret = line6_submit_audio_in_all_urbs(line6pcm); 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (ret < 0) 2018c2ecf20Sopenharmony_ci clear_bit(type, &pstr->running); 2028c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pstr->lock, flags); 2038c2ecf20Sopenharmony_ci return ret; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci/* stop a PCM stream; this doesn't sync with the unlinked URBs */ 2078c2ecf20Sopenharmony_cistatic void line6_stream_stop(struct snd_line6_pcm *line6pcm, int direction, 2088c2ecf20Sopenharmony_ci int type) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci unsigned long flags; 2118c2ecf20Sopenharmony_ci struct line6_pcm_stream *pstr = get_stream(line6pcm, direction); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci spin_lock_irqsave(&pstr->lock, flags); 2148c2ecf20Sopenharmony_ci clear_bit(type, &pstr->running); 2158c2ecf20Sopenharmony_ci if (!pstr->running) { 2168c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pstr->lock, flags); 2178c2ecf20Sopenharmony_ci line6_unlink_audio_urbs(line6pcm, pstr); 2188c2ecf20Sopenharmony_ci spin_lock_irqsave(&pstr->lock, flags); 2198c2ecf20Sopenharmony_ci if (direction == SNDRV_PCM_STREAM_CAPTURE) { 2208c2ecf20Sopenharmony_ci line6pcm->prev_fbuf = NULL; 2218c2ecf20Sopenharmony_ci line6pcm->prev_fsize = 0; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pstr->lock, flags); 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci/* common PCM trigger callback */ 2288c2ecf20Sopenharmony_ciint snd_line6_trigger(struct snd_pcm_substream *substream, int cmd) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); 2318c2ecf20Sopenharmony_ci struct snd_pcm_substream *s; 2328c2ecf20Sopenharmony_ci int err; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci clear_bit(LINE6_FLAG_PREPARED, &line6pcm->flags); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci snd_pcm_group_for_each_entry(s, substream) { 2378c2ecf20Sopenharmony_ci if (s->pcm->card != substream->pcm->card) 2388c2ecf20Sopenharmony_ci continue; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci switch (cmd) { 2418c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 2428c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 2438c2ecf20Sopenharmony_ci if (s->stream == SNDRV_PCM_STREAM_CAPTURE && 2448c2ecf20Sopenharmony_ci (line6pcm->line6->properties->capabilities & 2458c2ecf20Sopenharmony_ci LINE6_CAP_IN_NEEDS_OUT)) { 2468c2ecf20Sopenharmony_ci err = line6_stream_start(line6pcm, SNDRV_PCM_STREAM_PLAYBACK, 2478c2ecf20Sopenharmony_ci LINE6_STREAM_CAPTURE_HELPER); 2488c2ecf20Sopenharmony_ci if (err < 0) 2498c2ecf20Sopenharmony_ci return err; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci err = line6_stream_start(line6pcm, s->stream, 2528c2ecf20Sopenharmony_ci LINE6_STREAM_PCM); 2538c2ecf20Sopenharmony_ci if (err < 0) 2548c2ecf20Sopenharmony_ci return err; 2558c2ecf20Sopenharmony_ci break; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 2588c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 2598c2ecf20Sopenharmony_ci if (s->stream == SNDRV_PCM_STREAM_CAPTURE && 2608c2ecf20Sopenharmony_ci (line6pcm->line6->properties->capabilities & 2618c2ecf20Sopenharmony_ci LINE6_CAP_IN_NEEDS_OUT)) { 2628c2ecf20Sopenharmony_ci line6_stream_stop(line6pcm, SNDRV_PCM_STREAM_PLAYBACK, 2638c2ecf20Sopenharmony_ci LINE6_STREAM_CAPTURE_HELPER); 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci line6_stream_stop(line6pcm, s->stream, 2668c2ecf20Sopenharmony_ci LINE6_STREAM_PCM); 2678c2ecf20Sopenharmony_ci break; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 2708c2ecf20Sopenharmony_ci if (s->stream != SNDRV_PCM_STREAM_PLAYBACK) 2718c2ecf20Sopenharmony_ci return -EINVAL; 2728c2ecf20Sopenharmony_ci set_bit(LINE6_FLAG_PAUSE_PLAYBACK, &line6pcm->flags); 2738c2ecf20Sopenharmony_ci break; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 2768c2ecf20Sopenharmony_ci if (s->stream != SNDRV_PCM_STREAM_PLAYBACK) 2778c2ecf20Sopenharmony_ci return -EINVAL; 2788c2ecf20Sopenharmony_ci clear_bit(LINE6_FLAG_PAUSE_PLAYBACK, &line6pcm->flags); 2798c2ecf20Sopenharmony_ci break; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci default: 2828c2ecf20Sopenharmony_ci return -EINVAL; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci return 0; 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci/* common PCM pointer callback */ 2908c2ecf20Sopenharmony_cisnd_pcm_uframes_t snd_line6_pointer(struct snd_pcm_substream *substream) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); 2938c2ecf20Sopenharmony_ci struct line6_pcm_stream *pstr = get_stream(line6pcm, substream->stream); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci return pstr->pos_done; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci/* Acquire and optionally start duplex streams: 2998c2ecf20Sopenharmony_ci * type is either LINE6_STREAM_IMPULSE or LINE6_STREAM_MONITOR 3008c2ecf20Sopenharmony_ci */ 3018c2ecf20Sopenharmony_ciint line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int type, bool start) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci struct line6_pcm_stream *pstr; 3048c2ecf20Sopenharmony_ci int ret = 0, dir; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci /* TODO: We should assert SNDRV_PCM_STREAM_PLAYBACK/CAPTURE == 0/1 */ 3078c2ecf20Sopenharmony_ci mutex_lock(&line6pcm->state_mutex); 3088c2ecf20Sopenharmony_ci for (dir = 0; dir < 2; dir++) { 3098c2ecf20Sopenharmony_ci pstr = get_stream(line6pcm, dir); 3108c2ecf20Sopenharmony_ci ret = line6_buffer_acquire(line6pcm, pstr, dir, type); 3118c2ecf20Sopenharmony_ci if (ret < 0) 3128c2ecf20Sopenharmony_ci goto error; 3138c2ecf20Sopenharmony_ci if (!pstr->running) 3148c2ecf20Sopenharmony_ci line6_wait_clear_audio_urbs(line6pcm, pstr); 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci if (start) { 3178c2ecf20Sopenharmony_ci for (dir = 0; dir < 2; dir++) { 3188c2ecf20Sopenharmony_ci ret = line6_stream_start(line6pcm, dir, type); 3198c2ecf20Sopenharmony_ci if (ret < 0) 3208c2ecf20Sopenharmony_ci goto error; 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci error: 3248c2ecf20Sopenharmony_ci mutex_unlock(&line6pcm->state_mutex); 3258c2ecf20Sopenharmony_ci if (ret < 0) 3268c2ecf20Sopenharmony_ci line6_pcm_release(line6pcm, type); 3278c2ecf20Sopenharmony_ci return ret; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(line6_pcm_acquire); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci/* Stop and release duplex streams */ 3328c2ecf20Sopenharmony_civoid line6_pcm_release(struct snd_line6_pcm *line6pcm, int type) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci struct line6_pcm_stream *pstr; 3358c2ecf20Sopenharmony_ci int dir; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci mutex_lock(&line6pcm->state_mutex); 3388c2ecf20Sopenharmony_ci for (dir = 0; dir < 2; dir++) 3398c2ecf20Sopenharmony_ci line6_stream_stop(line6pcm, dir, type); 3408c2ecf20Sopenharmony_ci for (dir = 0; dir < 2; dir++) { 3418c2ecf20Sopenharmony_ci pstr = get_stream(line6pcm, dir); 3428c2ecf20Sopenharmony_ci line6_buffer_release(line6pcm, pstr, type); 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci mutex_unlock(&line6pcm->state_mutex); 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(line6_pcm_release); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci/* common PCM hw_params callback */ 3498c2ecf20Sopenharmony_ciint snd_line6_hw_params(struct snd_pcm_substream *substream, 3508c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci int ret; 3538c2ecf20Sopenharmony_ci struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); 3548c2ecf20Sopenharmony_ci struct line6_pcm_stream *pstr = get_stream(line6pcm, substream->stream); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci mutex_lock(&line6pcm->state_mutex); 3578c2ecf20Sopenharmony_ci ret = line6_buffer_acquire(line6pcm, pstr, substream->stream, 3588c2ecf20Sopenharmony_ci LINE6_STREAM_PCM); 3598c2ecf20Sopenharmony_ci if (ret < 0) 3608c2ecf20Sopenharmony_ci goto error; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci pstr->period = params_period_bytes(hw_params); 3638c2ecf20Sopenharmony_ci error: 3648c2ecf20Sopenharmony_ci mutex_unlock(&line6pcm->state_mutex); 3658c2ecf20Sopenharmony_ci return ret; 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci/* common PCM hw_free callback */ 3698c2ecf20Sopenharmony_ciint snd_line6_hw_free(struct snd_pcm_substream *substream) 3708c2ecf20Sopenharmony_ci{ 3718c2ecf20Sopenharmony_ci struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); 3728c2ecf20Sopenharmony_ci struct line6_pcm_stream *pstr = get_stream(line6pcm, substream->stream); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci mutex_lock(&line6pcm->state_mutex); 3758c2ecf20Sopenharmony_ci line6_buffer_release(line6pcm, pstr, LINE6_STREAM_PCM); 3768c2ecf20Sopenharmony_ci mutex_unlock(&line6pcm->state_mutex); 3778c2ecf20Sopenharmony_ci return 0; 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci/* control info callback */ 3828c2ecf20Sopenharmony_cistatic int snd_line6_control_playback_info(struct snd_kcontrol *kcontrol, 3838c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 3868c2ecf20Sopenharmony_ci uinfo->count = 2; 3878c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 3888c2ecf20Sopenharmony_ci uinfo->value.integer.max = 256; 3898c2ecf20Sopenharmony_ci return 0; 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci/* control get callback */ 3938c2ecf20Sopenharmony_cistatic int snd_line6_control_playback_get(struct snd_kcontrol *kcontrol, 3948c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci int i; 3978c2ecf20Sopenharmony_ci struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) 4008c2ecf20Sopenharmony_ci ucontrol->value.integer.value[i] = line6pcm->volume_playback[i]; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci return 0; 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci/* control put callback */ 4068c2ecf20Sopenharmony_cistatic int snd_line6_control_playback_put(struct snd_kcontrol *kcontrol, 4078c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci int i, changed = 0; 4108c2ecf20Sopenharmony_ci struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) 4138c2ecf20Sopenharmony_ci if (line6pcm->volume_playback[i] != 4148c2ecf20Sopenharmony_ci ucontrol->value.integer.value[i]) { 4158c2ecf20Sopenharmony_ci line6pcm->volume_playback[i] = 4168c2ecf20Sopenharmony_ci ucontrol->value.integer.value[i]; 4178c2ecf20Sopenharmony_ci changed = 1; 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci return changed; 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci/* control definition */ 4248c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new line6_controls[] = { 4258c2ecf20Sopenharmony_ci { 4268c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4278c2ecf20Sopenharmony_ci .name = "PCM Playback Volume", 4288c2ecf20Sopenharmony_ci .info = snd_line6_control_playback_info, 4298c2ecf20Sopenharmony_ci .get = snd_line6_control_playback_get, 4308c2ecf20Sopenharmony_ci .put = snd_line6_control_playback_put 4318c2ecf20Sopenharmony_ci }, 4328c2ecf20Sopenharmony_ci { 4338c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4348c2ecf20Sopenharmony_ci .name = "Impulse Response Volume", 4358c2ecf20Sopenharmony_ci .info = snd_line6_impulse_volume_info, 4368c2ecf20Sopenharmony_ci .get = snd_line6_impulse_volume_get, 4378c2ecf20Sopenharmony_ci .put = snd_line6_impulse_volume_put 4388c2ecf20Sopenharmony_ci }, 4398c2ecf20Sopenharmony_ci { 4408c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4418c2ecf20Sopenharmony_ci .name = "Impulse Response Period", 4428c2ecf20Sopenharmony_ci .info = snd_line6_impulse_period_info, 4438c2ecf20Sopenharmony_ci .get = snd_line6_impulse_period_get, 4448c2ecf20Sopenharmony_ci .put = snd_line6_impulse_period_put 4458c2ecf20Sopenharmony_ci }, 4468c2ecf20Sopenharmony_ci}; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci/* 4498c2ecf20Sopenharmony_ci Cleanup the PCM device. 4508c2ecf20Sopenharmony_ci*/ 4518c2ecf20Sopenharmony_cistatic void cleanup_urbs(struct line6_pcm_stream *pcms, int iso_buffers) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci int i; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci /* Most likely impossible in current code... */ 4568c2ecf20Sopenharmony_ci if (pcms->urbs == NULL) 4578c2ecf20Sopenharmony_ci return; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci for (i = 0; i < iso_buffers; i++) { 4608c2ecf20Sopenharmony_ci if (pcms->urbs[i]) { 4618c2ecf20Sopenharmony_ci usb_kill_urb(pcms->urbs[i]); 4628c2ecf20Sopenharmony_ci usb_free_urb(pcms->urbs[i]); 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci kfree(pcms->urbs); 4668c2ecf20Sopenharmony_ci pcms->urbs = NULL; 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_cistatic void line6_cleanup_pcm(struct snd_pcm *pcm) 4708c2ecf20Sopenharmony_ci{ 4718c2ecf20Sopenharmony_ci struct snd_line6_pcm *line6pcm = snd_pcm_chip(pcm); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci cleanup_urbs(&line6pcm->out, line6pcm->line6->iso_buffers); 4748c2ecf20Sopenharmony_ci cleanup_urbs(&line6pcm->in, line6pcm->line6->iso_buffers); 4758c2ecf20Sopenharmony_ci kfree(line6pcm); 4768c2ecf20Sopenharmony_ci} 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci/* create a PCM device */ 4798c2ecf20Sopenharmony_cistatic int snd_line6_new_pcm(struct usb_line6 *line6, struct snd_pcm **pcm_ret) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 4828c2ecf20Sopenharmony_ci int err; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci err = snd_pcm_new(line6->card, (char *)line6->properties->name, 4858c2ecf20Sopenharmony_ci 0, 1, 1, pcm_ret); 4868c2ecf20Sopenharmony_ci if (err < 0) 4878c2ecf20Sopenharmony_ci return err; 4888c2ecf20Sopenharmony_ci pcm = *pcm_ret; 4898c2ecf20Sopenharmony_ci strcpy(pcm->name, line6->properties->name); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci /* set operators */ 4928c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, 4938c2ecf20Sopenharmony_ci &snd_line6_playback_ops); 4948c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_line6_capture_ops); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci /* pre-allocation of buffers */ 4978c2ecf20Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, 4988c2ecf20Sopenharmony_ci NULL, 64 * 1024, 128 * 1024); 4998c2ecf20Sopenharmony_ci return 0; 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci/* 5038c2ecf20Sopenharmony_ci Sync with PCM stream stops. 5048c2ecf20Sopenharmony_ci*/ 5058c2ecf20Sopenharmony_civoid line6_pcm_disconnect(struct snd_line6_pcm *line6pcm) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci line6_unlink_audio_urbs(line6pcm, &line6pcm->out); 5088c2ecf20Sopenharmony_ci line6_unlink_audio_urbs(line6pcm, &line6pcm->in); 5098c2ecf20Sopenharmony_ci line6_wait_clear_audio_urbs(line6pcm, &line6pcm->out); 5108c2ecf20Sopenharmony_ci line6_wait_clear_audio_urbs(line6pcm, &line6pcm->in); 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci/* 5148c2ecf20Sopenharmony_ci Create and register the PCM device and mixer entries. 5158c2ecf20Sopenharmony_ci Create URBs for playback and capture. 5168c2ecf20Sopenharmony_ci*/ 5178c2ecf20Sopenharmony_ciint line6_init_pcm(struct usb_line6 *line6, 5188c2ecf20Sopenharmony_ci struct line6_pcm_properties *properties) 5198c2ecf20Sopenharmony_ci{ 5208c2ecf20Sopenharmony_ci int i, err; 5218c2ecf20Sopenharmony_ci unsigned ep_read = line6->properties->ep_audio_r; 5228c2ecf20Sopenharmony_ci unsigned ep_write = line6->properties->ep_audio_w; 5238c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 5248c2ecf20Sopenharmony_ci struct snd_line6_pcm *line6pcm; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci if (!(line6->properties->capabilities & LINE6_CAP_PCM)) 5278c2ecf20Sopenharmony_ci return 0; /* skip PCM initialization and report success */ 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci err = snd_line6_new_pcm(line6, &pcm); 5308c2ecf20Sopenharmony_ci if (err < 0) 5318c2ecf20Sopenharmony_ci return err; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci line6pcm = kzalloc(sizeof(*line6pcm), GFP_KERNEL); 5348c2ecf20Sopenharmony_ci if (!line6pcm) 5358c2ecf20Sopenharmony_ci return -ENOMEM; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci mutex_init(&line6pcm->state_mutex); 5388c2ecf20Sopenharmony_ci line6pcm->pcm = pcm; 5398c2ecf20Sopenharmony_ci line6pcm->properties = properties; 5408c2ecf20Sopenharmony_ci line6pcm->volume_playback[0] = line6pcm->volume_playback[1] = 255; 5418c2ecf20Sopenharmony_ci line6pcm->volume_monitor = 255; 5428c2ecf20Sopenharmony_ci line6pcm->line6 = line6; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci spin_lock_init(&line6pcm->out.lock); 5458c2ecf20Sopenharmony_ci spin_lock_init(&line6pcm->in.lock); 5468c2ecf20Sopenharmony_ci line6pcm->impulse_period = LINE6_IMPULSE_DEFAULT_PERIOD; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci line6->line6pcm = line6pcm; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci pcm->private_data = line6pcm; 5518c2ecf20Sopenharmony_ci pcm->private_free = line6_cleanup_pcm; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci line6pcm->max_packet_size_in = 5548c2ecf20Sopenharmony_ci usb_maxpacket(line6->usbdev, 5558c2ecf20Sopenharmony_ci usb_rcvisocpipe(line6->usbdev, ep_read), 0); 5568c2ecf20Sopenharmony_ci line6pcm->max_packet_size_out = 5578c2ecf20Sopenharmony_ci usb_maxpacket(line6->usbdev, 5588c2ecf20Sopenharmony_ci usb_sndisocpipe(line6->usbdev, ep_write), 1); 5598c2ecf20Sopenharmony_ci if (!line6pcm->max_packet_size_in || !line6pcm->max_packet_size_out) { 5608c2ecf20Sopenharmony_ci dev_err(line6pcm->line6->ifcdev, 5618c2ecf20Sopenharmony_ci "cannot get proper max packet size\n"); 5628c2ecf20Sopenharmony_ci return -EINVAL; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci err = line6_create_audio_out_urbs(line6pcm); 5668c2ecf20Sopenharmony_ci if (err < 0) 5678c2ecf20Sopenharmony_ci return err; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci err = line6_create_audio_in_urbs(line6pcm); 5708c2ecf20Sopenharmony_ci if (err < 0) 5718c2ecf20Sopenharmony_ci return err; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci /* mixer: */ 5748c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(line6_controls); i++) { 5758c2ecf20Sopenharmony_ci err = snd_ctl_add(line6->card, 5768c2ecf20Sopenharmony_ci snd_ctl_new1(&line6_controls[i], line6pcm)); 5778c2ecf20Sopenharmony_ci if (err < 0) 5788c2ecf20Sopenharmony_ci return err; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci return 0; 5828c2ecf20Sopenharmony_ci} 5838c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(line6_init_pcm); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci/* prepare pcm callback */ 5868c2ecf20Sopenharmony_ciint snd_line6_prepare(struct snd_pcm_substream *substream) 5878c2ecf20Sopenharmony_ci{ 5888c2ecf20Sopenharmony_ci struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); 5898c2ecf20Sopenharmony_ci struct line6_pcm_stream *pstr = get_stream(line6pcm, substream->stream); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci mutex_lock(&line6pcm->state_mutex); 5928c2ecf20Sopenharmony_ci if (!pstr->running) 5938c2ecf20Sopenharmony_ci line6_wait_clear_audio_urbs(line6pcm, pstr); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci if (!test_and_set_bit(LINE6_FLAG_PREPARED, &line6pcm->flags)) { 5968c2ecf20Sopenharmony_ci line6pcm->out.count = 0; 5978c2ecf20Sopenharmony_ci line6pcm->out.pos = 0; 5988c2ecf20Sopenharmony_ci line6pcm->out.pos_done = 0; 5998c2ecf20Sopenharmony_ci line6pcm->out.bytes = 0; 6008c2ecf20Sopenharmony_ci line6pcm->in.count = 0; 6018c2ecf20Sopenharmony_ci line6pcm->in.pos_done = 0; 6028c2ecf20Sopenharmony_ci line6pcm->in.bytes = 0; 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci mutex_unlock(&line6pcm->state_mutex); 6068c2ecf20Sopenharmony_ci return 0; 6078c2ecf20Sopenharmony_ci} 608