xref: /kernel/linux/linux-5.10/sound/usb/line6/pcm.c (revision 8c2ecf20)
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