xref: /kernel/linux/linux-6.6/sound/usb/line6/pcm.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Line 6 Linux USB driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/slab.h>
962306a36Sopenharmony_ci#include <linux/export.h>
1062306a36Sopenharmony_ci#include <sound/core.h>
1162306a36Sopenharmony_ci#include <sound/control.h>
1262306a36Sopenharmony_ci#include <sound/pcm.h>
1362306a36Sopenharmony_ci#include <sound/pcm_params.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "capture.h"
1662306a36Sopenharmony_ci#include "driver.h"
1762306a36Sopenharmony_ci#include "playback.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/* impulse response volume controls */
2062306a36Sopenharmony_cistatic int snd_line6_impulse_volume_info(struct snd_kcontrol *kcontrol,
2162306a36Sopenharmony_ci					 struct snd_ctl_elem_info *uinfo)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
2462306a36Sopenharmony_ci	uinfo->count = 1;
2562306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
2662306a36Sopenharmony_ci	uinfo->value.integer.max = 255;
2762306a36Sopenharmony_ci	return 0;
2862306a36Sopenharmony_ci}
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic int snd_line6_impulse_volume_get(struct snd_kcontrol *kcontrol,
3162306a36Sopenharmony_ci					struct snd_ctl_elem_value *ucontrol)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = line6pcm->impulse_volume;
3662306a36Sopenharmony_ci	return 0;
3762306a36Sopenharmony_ci}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic int snd_line6_impulse_volume_put(struct snd_kcontrol *kcontrol,
4062306a36Sopenharmony_ci					struct snd_ctl_elem_value *ucontrol)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
4362306a36Sopenharmony_ci	int value = ucontrol->value.integer.value[0];
4462306a36Sopenharmony_ci	int err;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	if (line6pcm->impulse_volume == value)
4762306a36Sopenharmony_ci		return 0;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	line6pcm->impulse_volume = value;
5062306a36Sopenharmony_ci	if (value > 0) {
5162306a36Sopenharmony_ci		err = line6_pcm_acquire(line6pcm, LINE6_STREAM_IMPULSE, true);
5262306a36Sopenharmony_ci		if (err < 0) {
5362306a36Sopenharmony_ci			line6pcm->impulse_volume = 0;
5462306a36Sopenharmony_ci			return err;
5562306a36Sopenharmony_ci		}
5662306a36Sopenharmony_ci	} else {
5762306a36Sopenharmony_ci		line6_pcm_release(line6pcm, LINE6_STREAM_IMPULSE);
5862306a36Sopenharmony_ci	}
5962306a36Sopenharmony_ci	return 1;
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/* impulse response period controls */
6362306a36Sopenharmony_cistatic int snd_line6_impulse_period_info(struct snd_kcontrol *kcontrol,
6462306a36Sopenharmony_ci					 struct snd_ctl_elem_info *uinfo)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
6762306a36Sopenharmony_ci	uinfo->count = 1;
6862306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
6962306a36Sopenharmony_ci	uinfo->value.integer.max = 2000;
7062306a36Sopenharmony_ci	return 0;
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic int snd_line6_impulse_period_get(struct snd_kcontrol *kcontrol,
7462306a36Sopenharmony_ci					struct snd_ctl_elem_value *ucontrol)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = line6pcm->impulse_period;
7962306a36Sopenharmony_ci	return 0;
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic int snd_line6_impulse_period_put(struct snd_kcontrol *kcontrol,
8362306a36Sopenharmony_ci					struct snd_ctl_elem_value *ucontrol)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
8662306a36Sopenharmony_ci	int value = ucontrol->value.integer.value[0];
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	if (line6pcm->impulse_period == value)
8962306a36Sopenharmony_ci		return 0;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	line6pcm->impulse_period = value;
9262306a36Sopenharmony_ci	return 1;
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci/*
9662306a36Sopenharmony_ci	Unlink all currently active URBs.
9762306a36Sopenharmony_ci*/
9862306a36Sopenharmony_cistatic void line6_unlink_audio_urbs(struct snd_line6_pcm *line6pcm,
9962306a36Sopenharmony_ci				    struct line6_pcm_stream *pcms)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	int i;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	for (i = 0; i < line6pcm->line6->iso_buffers; i++) {
10462306a36Sopenharmony_ci		if (test_bit(i, &pcms->active_urbs)) {
10562306a36Sopenharmony_ci			if (!test_and_set_bit(i, &pcms->unlink_urbs))
10662306a36Sopenharmony_ci				usb_unlink_urb(pcms->urbs[i]);
10762306a36Sopenharmony_ci		}
10862306a36Sopenharmony_ci	}
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci/*
11262306a36Sopenharmony_ci	Wait until unlinking of all currently active URBs has been finished.
11362306a36Sopenharmony_ci*/
11462306a36Sopenharmony_cistatic void line6_wait_clear_audio_urbs(struct snd_line6_pcm *line6pcm,
11562306a36Sopenharmony_ci					struct line6_pcm_stream *pcms)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	int timeout = HZ;
11862306a36Sopenharmony_ci	int i;
11962306a36Sopenharmony_ci	int alive;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	do {
12262306a36Sopenharmony_ci		alive = 0;
12362306a36Sopenharmony_ci		for (i = 0; i < line6pcm->line6->iso_buffers; i++) {
12462306a36Sopenharmony_ci			if (test_bit(i, &pcms->active_urbs))
12562306a36Sopenharmony_ci				alive++;
12662306a36Sopenharmony_ci		}
12762306a36Sopenharmony_ci		if (!alive)
12862306a36Sopenharmony_ci			break;
12962306a36Sopenharmony_ci		set_current_state(TASK_UNINTERRUPTIBLE);
13062306a36Sopenharmony_ci		schedule_timeout(1);
13162306a36Sopenharmony_ci	} while (--timeout > 0);
13262306a36Sopenharmony_ci	if (alive)
13362306a36Sopenharmony_ci		dev_err(line6pcm->line6->ifcdev,
13462306a36Sopenharmony_ci			"timeout: still %d active urbs..\n", alive);
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic inline struct line6_pcm_stream *
13862306a36Sopenharmony_ciget_stream(struct snd_line6_pcm *line6pcm, int direction)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	return (direction == SNDRV_PCM_STREAM_PLAYBACK) ?
14162306a36Sopenharmony_ci		&line6pcm->out : &line6pcm->in;
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci/* allocate a buffer if not opened yet;
14562306a36Sopenharmony_ci * call this in line6pcm.state_mutex
14662306a36Sopenharmony_ci */
14762306a36Sopenharmony_cistatic int line6_buffer_acquire(struct snd_line6_pcm *line6pcm,
14862306a36Sopenharmony_ci				struct line6_pcm_stream *pstr, int direction, int type)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	const int pkt_size =
15162306a36Sopenharmony_ci		(direction == SNDRV_PCM_STREAM_PLAYBACK) ?
15262306a36Sopenharmony_ci			line6pcm->max_packet_size_out :
15362306a36Sopenharmony_ci			line6pcm->max_packet_size_in;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	/* Invoked multiple times in a row so allocate once only */
15662306a36Sopenharmony_ci	if (!test_and_set_bit(type, &pstr->opened) && !pstr->buffer) {
15762306a36Sopenharmony_ci		pstr->buffer =
15862306a36Sopenharmony_ci			kmalloc(array3_size(line6pcm->line6->iso_buffers,
15962306a36Sopenharmony_ci					    LINE6_ISO_PACKETS, pkt_size),
16062306a36Sopenharmony_ci				GFP_KERNEL);
16162306a36Sopenharmony_ci		if (!pstr->buffer)
16262306a36Sopenharmony_ci			return -ENOMEM;
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci	return 0;
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci/* free a buffer if all streams are closed;
16862306a36Sopenharmony_ci * call this in line6pcm.state_mutex
16962306a36Sopenharmony_ci */
17062306a36Sopenharmony_cistatic void line6_buffer_release(struct snd_line6_pcm *line6pcm,
17162306a36Sopenharmony_ci				 struct line6_pcm_stream *pstr, int type)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	clear_bit(type, &pstr->opened);
17462306a36Sopenharmony_ci	if (!pstr->opened) {
17562306a36Sopenharmony_ci		line6_wait_clear_audio_urbs(line6pcm, pstr);
17662306a36Sopenharmony_ci		kfree(pstr->buffer);
17762306a36Sopenharmony_ci		pstr->buffer = NULL;
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci/* start a PCM stream */
18262306a36Sopenharmony_cistatic int line6_stream_start(struct snd_line6_pcm *line6pcm, int direction,
18362306a36Sopenharmony_ci			      int type)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	unsigned long flags;
18662306a36Sopenharmony_ci	struct line6_pcm_stream *pstr = get_stream(line6pcm, direction);
18762306a36Sopenharmony_ci	int ret = 0;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	spin_lock_irqsave(&pstr->lock, flags);
19062306a36Sopenharmony_ci	if (!test_and_set_bit(type, &pstr->running) &&
19162306a36Sopenharmony_ci	    !(pstr->active_urbs || pstr->unlink_urbs)) {
19262306a36Sopenharmony_ci		pstr->count = 0;
19362306a36Sopenharmony_ci		/* Submit all currently available URBs */
19462306a36Sopenharmony_ci		if (direction == SNDRV_PCM_STREAM_PLAYBACK)
19562306a36Sopenharmony_ci			ret = line6_submit_audio_out_all_urbs(line6pcm);
19662306a36Sopenharmony_ci		else
19762306a36Sopenharmony_ci			ret = line6_submit_audio_in_all_urbs(line6pcm);
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	if (ret < 0)
20162306a36Sopenharmony_ci		clear_bit(type, &pstr->running);
20262306a36Sopenharmony_ci	spin_unlock_irqrestore(&pstr->lock, flags);
20362306a36Sopenharmony_ci	return ret;
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci/* stop a PCM stream; this doesn't sync with the unlinked URBs */
20762306a36Sopenharmony_cistatic void line6_stream_stop(struct snd_line6_pcm *line6pcm, int direction,
20862306a36Sopenharmony_ci			  int type)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	unsigned long flags;
21162306a36Sopenharmony_ci	struct line6_pcm_stream *pstr = get_stream(line6pcm, direction);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	spin_lock_irqsave(&pstr->lock, flags);
21462306a36Sopenharmony_ci	clear_bit(type, &pstr->running);
21562306a36Sopenharmony_ci	if (!pstr->running) {
21662306a36Sopenharmony_ci		spin_unlock_irqrestore(&pstr->lock, flags);
21762306a36Sopenharmony_ci		line6_unlink_audio_urbs(line6pcm, pstr);
21862306a36Sopenharmony_ci		spin_lock_irqsave(&pstr->lock, flags);
21962306a36Sopenharmony_ci		if (direction == SNDRV_PCM_STREAM_CAPTURE) {
22062306a36Sopenharmony_ci			line6pcm->prev_fbuf = NULL;
22162306a36Sopenharmony_ci			line6pcm->prev_fsize = 0;
22262306a36Sopenharmony_ci		}
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci	spin_unlock_irqrestore(&pstr->lock, flags);
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci/* common PCM trigger callback */
22862306a36Sopenharmony_ciint snd_line6_trigger(struct snd_pcm_substream *substream, int cmd)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
23162306a36Sopenharmony_ci	struct snd_pcm_substream *s;
23262306a36Sopenharmony_ci	int err;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	clear_bit(LINE6_FLAG_PREPARED, &line6pcm->flags);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	snd_pcm_group_for_each_entry(s, substream) {
23762306a36Sopenharmony_ci		if (s->pcm->card != substream->pcm->card)
23862306a36Sopenharmony_ci			continue;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci		switch (cmd) {
24162306a36Sopenharmony_ci		case SNDRV_PCM_TRIGGER_START:
24262306a36Sopenharmony_ci		case SNDRV_PCM_TRIGGER_RESUME:
24362306a36Sopenharmony_ci			if (s->stream == SNDRV_PCM_STREAM_CAPTURE &&
24462306a36Sopenharmony_ci				(line6pcm->line6->properties->capabilities &
24562306a36Sopenharmony_ci					LINE6_CAP_IN_NEEDS_OUT)) {
24662306a36Sopenharmony_ci				err = line6_stream_start(line6pcm, SNDRV_PCM_STREAM_PLAYBACK,
24762306a36Sopenharmony_ci						 LINE6_STREAM_CAPTURE_HELPER);
24862306a36Sopenharmony_ci				if (err < 0)
24962306a36Sopenharmony_ci					return err;
25062306a36Sopenharmony_ci			}
25162306a36Sopenharmony_ci			err = line6_stream_start(line6pcm, s->stream,
25262306a36Sopenharmony_ci						 LINE6_STREAM_PCM);
25362306a36Sopenharmony_ci			if (err < 0)
25462306a36Sopenharmony_ci				return err;
25562306a36Sopenharmony_ci			break;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci		case SNDRV_PCM_TRIGGER_STOP:
25862306a36Sopenharmony_ci		case SNDRV_PCM_TRIGGER_SUSPEND:
25962306a36Sopenharmony_ci			if (s->stream == SNDRV_PCM_STREAM_CAPTURE &&
26062306a36Sopenharmony_ci				(line6pcm->line6->properties->capabilities &
26162306a36Sopenharmony_ci					LINE6_CAP_IN_NEEDS_OUT)) {
26262306a36Sopenharmony_ci				line6_stream_stop(line6pcm, SNDRV_PCM_STREAM_PLAYBACK,
26362306a36Sopenharmony_ci					  LINE6_STREAM_CAPTURE_HELPER);
26462306a36Sopenharmony_ci			}
26562306a36Sopenharmony_ci			line6_stream_stop(line6pcm, s->stream,
26662306a36Sopenharmony_ci					  LINE6_STREAM_PCM);
26762306a36Sopenharmony_ci			break;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci		case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
27062306a36Sopenharmony_ci			if (s->stream != SNDRV_PCM_STREAM_PLAYBACK)
27162306a36Sopenharmony_ci				return -EINVAL;
27262306a36Sopenharmony_ci			set_bit(LINE6_FLAG_PAUSE_PLAYBACK, &line6pcm->flags);
27362306a36Sopenharmony_ci			break;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci		case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
27662306a36Sopenharmony_ci			if (s->stream != SNDRV_PCM_STREAM_PLAYBACK)
27762306a36Sopenharmony_ci				return -EINVAL;
27862306a36Sopenharmony_ci			clear_bit(LINE6_FLAG_PAUSE_PLAYBACK, &line6pcm->flags);
27962306a36Sopenharmony_ci			break;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci		default:
28262306a36Sopenharmony_ci			return -EINVAL;
28362306a36Sopenharmony_ci		}
28462306a36Sopenharmony_ci	}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	return 0;
28762306a36Sopenharmony_ci}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci/* common PCM pointer callback */
29062306a36Sopenharmony_cisnd_pcm_uframes_t snd_line6_pointer(struct snd_pcm_substream *substream)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
29362306a36Sopenharmony_ci	struct line6_pcm_stream *pstr = get_stream(line6pcm, substream->stream);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	return pstr->pos_done;
29662306a36Sopenharmony_ci}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci/* Acquire and optionally start duplex streams:
29962306a36Sopenharmony_ci * type is either LINE6_STREAM_IMPULSE or LINE6_STREAM_MONITOR
30062306a36Sopenharmony_ci */
30162306a36Sopenharmony_ciint line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int type, bool start)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	struct line6_pcm_stream *pstr;
30462306a36Sopenharmony_ci	int ret = 0, dir;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	/* TODO: We should assert SNDRV_PCM_STREAM_PLAYBACK/CAPTURE == 0/1 */
30762306a36Sopenharmony_ci	mutex_lock(&line6pcm->state_mutex);
30862306a36Sopenharmony_ci	for (dir = 0; dir < 2; dir++) {
30962306a36Sopenharmony_ci		pstr = get_stream(line6pcm, dir);
31062306a36Sopenharmony_ci		ret = line6_buffer_acquire(line6pcm, pstr, dir, type);
31162306a36Sopenharmony_ci		if (ret < 0)
31262306a36Sopenharmony_ci			goto error;
31362306a36Sopenharmony_ci		if (!pstr->running)
31462306a36Sopenharmony_ci			line6_wait_clear_audio_urbs(line6pcm, pstr);
31562306a36Sopenharmony_ci	}
31662306a36Sopenharmony_ci	if (start) {
31762306a36Sopenharmony_ci		for (dir = 0; dir < 2; dir++) {
31862306a36Sopenharmony_ci			ret = line6_stream_start(line6pcm, dir, type);
31962306a36Sopenharmony_ci			if (ret < 0)
32062306a36Sopenharmony_ci				goto error;
32162306a36Sopenharmony_ci		}
32262306a36Sopenharmony_ci	}
32362306a36Sopenharmony_ci error:
32462306a36Sopenharmony_ci	mutex_unlock(&line6pcm->state_mutex);
32562306a36Sopenharmony_ci	if (ret < 0)
32662306a36Sopenharmony_ci		line6_pcm_release(line6pcm, type);
32762306a36Sopenharmony_ci	return ret;
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(line6_pcm_acquire);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci/* Stop and release duplex streams */
33262306a36Sopenharmony_civoid line6_pcm_release(struct snd_line6_pcm *line6pcm, int type)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	struct line6_pcm_stream *pstr;
33562306a36Sopenharmony_ci	int dir;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	mutex_lock(&line6pcm->state_mutex);
33862306a36Sopenharmony_ci	for (dir = 0; dir < 2; dir++)
33962306a36Sopenharmony_ci		line6_stream_stop(line6pcm, dir, type);
34062306a36Sopenharmony_ci	for (dir = 0; dir < 2; dir++) {
34162306a36Sopenharmony_ci		pstr = get_stream(line6pcm, dir);
34262306a36Sopenharmony_ci		line6_buffer_release(line6pcm, pstr, type);
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci	mutex_unlock(&line6pcm->state_mutex);
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(line6_pcm_release);
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci/* common PCM hw_params callback */
34962306a36Sopenharmony_ciint snd_line6_hw_params(struct snd_pcm_substream *substream,
35062306a36Sopenharmony_ci			struct snd_pcm_hw_params *hw_params)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	int ret;
35362306a36Sopenharmony_ci	struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
35462306a36Sopenharmony_ci	struct line6_pcm_stream *pstr = get_stream(line6pcm, substream->stream);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	mutex_lock(&line6pcm->state_mutex);
35762306a36Sopenharmony_ci	ret = line6_buffer_acquire(line6pcm, pstr, substream->stream,
35862306a36Sopenharmony_ci	                           LINE6_STREAM_PCM);
35962306a36Sopenharmony_ci	if (ret < 0)
36062306a36Sopenharmony_ci		goto error;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	pstr->period = params_period_bytes(hw_params);
36362306a36Sopenharmony_ci error:
36462306a36Sopenharmony_ci	mutex_unlock(&line6pcm->state_mutex);
36562306a36Sopenharmony_ci	return ret;
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci/* common PCM hw_free callback */
36962306a36Sopenharmony_ciint snd_line6_hw_free(struct snd_pcm_substream *substream)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
37262306a36Sopenharmony_ci	struct line6_pcm_stream *pstr = get_stream(line6pcm, substream->stream);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	mutex_lock(&line6pcm->state_mutex);
37562306a36Sopenharmony_ci	line6_buffer_release(line6pcm, pstr, LINE6_STREAM_PCM);
37662306a36Sopenharmony_ci	mutex_unlock(&line6pcm->state_mutex);
37762306a36Sopenharmony_ci	return 0;
37862306a36Sopenharmony_ci}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci/* control info callback */
38262306a36Sopenharmony_cistatic int snd_line6_control_playback_info(struct snd_kcontrol *kcontrol,
38362306a36Sopenharmony_ci					   struct snd_ctl_elem_info *uinfo)
38462306a36Sopenharmony_ci{
38562306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
38662306a36Sopenharmony_ci	uinfo->count = 2;
38762306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
38862306a36Sopenharmony_ci	uinfo->value.integer.max = 256;
38962306a36Sopenharmony_ci	return 0;
39062306a36Sopenharmony_ci}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci/* control get callback */
39362306a36Sopenharmony_cistatic int snd_line6_control_playback_get(struct snd_kcontrol *kcontrol,
39462306a36Sopenharmony_ci					  struct snd_ctl_elem_value *ucontrol)
39562306a36Sopenharmony_ci{
39662306a36Sopenharmony_ci	int i;
39762306a36Sopenharmony_ci	struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	for (i = 0; i < 2; i++)
40062306a36Sopenharmony_ci		ucontrol->value.integer.value[i] = line6pcm->volume_playback[i];
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	return 0;
40362306a36Sopenharmony_ci}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci/* control put callback */
40662306a36Sopenharmony_cistatic int snd_line6_control_playback_put(struct snd_kcontrol *kcontrol,
40762306a36Sopenharmony_ci					  struct snd_ctl_elem_value *ucontrol)
40862306a36Sopenharmony_ci{
40962306a36Sopenharmony_ci	int i, changed = 0;
41062306a36Sopenharmony_ci	struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	for (i = 0; i < 2; i++)
41362306a36Sopenharmony_ci		if (line6pcm->volume_playback[i] !=
41462306a36Sopenharmony_ci		    ucontrol->value.integer.value[i]) {
41562306a36Sopenharmony_ci			line6pcm->volume_playback[i] =
41662306a36Sopenharmony_ci			    ucontrol->value.integer.value[i];
41762306a36Sopenharmony_ci			changed = 1;
41862306a36Sopenharmony_ci		}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	return changed;
42162306a36Sopenharmony_ci}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci/* control definition */
42462306a36Sopenharmony_cistatic const struct snd_kcontrol_new line6_controls[] = {
42562306a36Sopenharmony_ci	{
42662306a36Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
42762306a36Sopenharmony_ci		.name = "PCM Playback Volume",
42862306a36Sopenharmony_ci		.info = snd_line6_control_playback_info,
42962306a36Sopenharmony_ci		.get = snd_line6_control_playback_get,
43062306a36Sopenharmony_ci		.put = snd_line6_control_playback_put
43162306a36Sopenharmony_ci	},
43262306a36Sopenharmony_ci	{
43362306a36Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
43462306a36Sopenharmony_ci		.name = "Impulse Response Volume",
43562306a36Sopenharmony_ci		.info = snd_line6_impulse_volume_info,
43662306a36Sopenharmony_ci		.get = snd_line6_impulse_volume_get,
43762306a36Sopenharmony_ci		.put = snd_line6_impulse_volume_put
43862306a36Sopenharmony_ci	},
43962306a36Sopenharmony_ci	{
44062306a36Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
44162306a36Sopenharmony_ci		.name = "Impulse Response Period",
44262306a36Sopenharmony_ci		.info = snd_line6_impulse_period_info,
44362306a36Sopenharmony_ci		.get = snd_line6_impulse_period_get,
44462306a36Sopenharmony_ci		.put = snd_line6_impulse_period_put
44562306a36Sopenharmony_ci	},
44662306a36Sopenharmony_ci};
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci/*
44962306a36Sopenharmony_ci	Cleanup the PCM device.
45062306a36Sopenharmony_ci*/
45162306a36Sopenharmony_cistatic void cleanup_urbs(struct line6_pcm_stream *pcms, int iso_buffers)
45262306a36Sopenharmony_ci{
45362306a36Sopenharmony_ci	int i;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	/* Most likely impossible in current code... */
45662306a36Sopenharmony_ci	if (pcms->urbs == NULL)
45762306a36Sopenharmony_ci		return;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	for (i = 0; i < iso_buffers; i++) {
46062306a36Sopenharmony_ci		if (pcms->urbs[i]) {
46162306a36Sopenharmony_ci			usb_kill_urb(pcms->urbs[i]);
46262306a36Sopenharmony_ci			usb_free_urb(pcms->urbs[i]);
46362306a36Sopenharmony_ci		}
46462306a36Sopenharmony_ci	}
46562306a36Sopenharmony_ci	kfree(pcms->urbs);
46662306a36Sopenharmony_ci	pcms->urbs = NULL;
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_cistatic void line6_cleanup_pcm(struct snd_pcm *pcm)
47062306a36Sopenharmony_ci{
47162306a36Sopenharmony_ci	struct snd_line6_pcm *line6pcm = snd_pcm_chip(pcm);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	cleanup_urbs(&line6pcm->out, line6pcm->line6->iso_buffers);
47462306a36Sopenharmony_ci	cleanup_urbs(&line6pcm->in, line6pcm->line6->iso_buffers);
47562306a36Sopenharmony_ci	kfree(line6pcm);
47662306a36Sopenharmony_ci}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci/* create a PCM device */
47962306a36Sopenharmony_cistatic int snd_line6_new_pcm(struct usb_line6 *line6, struct snd_pcm **pcm_ret)
48062306a36Sopenharmony_ci{
48162306a36Sopenharmony_ci	struct snd_pcm *pcm;
48262306a36Sopenharmony_ci	int err;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	err = snd_pcm_new(line6->card, (char *)line6->properties->name,
48562306a36Sopenharmony_ci			  0, 1, 1, pcm_ret);
48662306a36Sopenharmony_ci	if (err < 0)
48762306a36Sopenharmony_ci		return err;
48862306a36Sopenharmony_ci	pcm = *pcm_ret;
48962306a36Sopenharmony_ci	strcpy(pcm->name, line6->properties->name);
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	/* set operators */
49262306a36Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
49362306a36Sopenharmony_ci			&snd_line6_playback_ops);
49462306a36Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_line6_capture_ops);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	/* pre-allocation of buffers */
49762306a36Sopenharmony_ci	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
49862306a36Sopenharmony_ci				       NULL, 64 * 1024, 128 * 1024);
49962306a36Sopenharmony_ci	return 0;
50062306a36Sopenharmony_ci}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci/*
50362306a36Sopenharmony_ci	Sync with PCM stream stops.
50462306a36Sopenharmony_ci*/
50562306a36Sopenharmony_civoid line6_pcm_disconnect(struct snd_line6_pcm *line6pcm)
50662306a36Sopenharmony_ci{
50762306a36Sopenharmony_ci	line6_unlink_audio_urbs(line6pcm, &line6pcm->out);
50862306a36Sopenharmony_ci	line6_unlink_audio_urbs(line6pcm, &line6pcm->in);
50962306a36Sopenharmony_ci	line6_wait_clear_audio_urbs(line6pcm, &line6pcm->out);
51062306a36Sopenharmony_ci	line6_wait_clear_audio_urbs(line6pcm, &line6pcm->in);
51162306a36Sopenharmony_ci}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci/*
51462306a36Sopenharmony_ci	Create and register the PCM device and mixer entries.
51562306a36Sopenharmony_ci	Create URBs for playback and capture.
51662306a36Sopenharmony_ci*/
51762306a36Sopenharmony_ciint line6_init_pcm(struct usb_line6 *line6,
51862306a36Sopenharmony_ci		   struct line6_pcm_properties *properties)
51962306a36Sopenharmony_ci{
52062306a36Sopenharmony_ci	int i, err;
52162306a36Sopenharmony_ci	unsigned ep_read = line6->properties->ep_audio_r;
52262306a36Sopenharmony_ci	unsigned ep_write = line6->properties->ep_audio_w;
52362306a36Sopenharmony_ci	struct snd_pcm *pcm;
52462306a36Sopenharmony_ci	struct snd_line6_pcm *line6pcm;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	if (!(line6->properties->capabilities & LINE6_CAP_PCM))
52762306a36Sopenharmony_ci		return 0;	/* skip PCM initialization and report success */
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	err = snd_line6_new_pcm(line6, &pcm);
53062306a36Sopenharmony_ci	if (err < 0)
53162306a36Sopenharmony_ci		return err;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	line6pcm = kzalloc(sizeof(*line6pcm), GFP_KERNEL);
53462306a36Sopenharmony_ci	if (!line6pcm)
53562306a36Sopenharmony_ci		return -ENOMEM;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	mutex_init(&line6pcm->state_mutex);
53862306a36Sopenharmony_ci	line6pcm->pcm = pcm;
53962306a36Sopenharmony_ci	line6pcm->properties = properties;
54062306a36Sopenharmony_ci	line6pcm->volume_playback[0] = line6pcm->volume_playback[1] = 255;
54162306a36Sopenharmony_ci	line6pcm->volume_monitor = 255;
54262306a36Sopenharmony_ci	line6pcm->line6 = line6;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	spin_lock_init(&line6pcm->out.lock);
54562306a36Sopenharmony_ci	spin_lock_init(&line6pcm->in.lock);
54662306a36Sopenharmony_ci	line6pcm->impulse_period = LINE6_IMPULSE_DEFAULT_PERIOD;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	line6->line6pcm = line6pcm;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	pcm->private_data = line6pcm;
55162306a36Sopenharmony_ci	pcm->private_free = line6_cleanup_pcm;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	line6pcm->max_packet_size_in =
55462306a36Sopenharmony_ci		usb_maxpacket(line6->usbdev,
55562306a36Sopenharmony_ci			usb_rcvisocpipe(line6->usbdev, ep_read));
55662306a36Sopenharmony_ci	line6pcm->max_packet_size_out =
55762306a36Sopenharmony_ci		usb_maxpacket(line6->usbdev,
55862306a36Sopenharmony_ci			usb_sndisocpipe(line6->usbdev, ep_write));
55962306a36Sopenharmony_ci	if (!line6pcm->max_packet_size_in || !line6pcm->max_packet_size_out) {
56062306a36Sopenharmony_ci		dev_err(line6pcm->line6->ifcdev,
56162306a36Sopenharmony_ci			"cannot get proper max packet size\n");
56262306a36Sopenharmony_ci		return -EINVAL;
56362306a36Sopenharmony_ci	}
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	err = line6_create_audio_out_urbs(line6pcm);
56662306a36Sopenharmony_ci	if (err < 0)
56762306a36Sopenharmony_ci		return err;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	err = line6_create_audio_in_urbs(line6pcm);
57062306a36Sopenharmony_ci	if (err < 0)
57162306a36Sopenharmony_ci		return err;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	/* mixer: */
57462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(line6_controls); i++) {
57562306a36Sopenharmony_ci		err = snd_ctl_add(line6->card,
57662306a36Sopenharmony_ci				  snd_ctl_new1(&line6_controls[i], line6pcm));
57762306a36Sopenharmony_ci		if (err < 0)
57862306a36Sopenharmony_ci			return err;
57962306a36Sopenharmony_ci	}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	return 0;
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(line6_init_pcm);
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci/* prepare pcm callback */
58662306a36Sopenharmony_ciint snd_line6_prepare(struct snd_pcm_substream *substream)
58762306a36Sopenharmony_ci{
58862306a36Sopenharmony_ci	struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
58962306a36Sopenharmony_ci	struct line6_pcm_stream *pstr = get_stream(line6pcm, substream->stream);
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	mutex_lock(&line6pcm->state_mutex);
59262306a36Sopenharmony_ci	if (!pstr->running)
59362306a36Sopenharmony_ci		line6_wait_clear_audio_urbs(line6pcm, pstr);
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	if (!test_and_set_bit(LINE6_FLAG_PREPARED, &line6pcm->flags)) {
59662306a36Sopenharmony_ci		line6pcm->out.count = 0;
59762306a36Sopenharmony_ci		line6pcm->out.pos = 0;
59862306a36Sopenharmony_ci		line6pcm->out.pos_done = 0;
59962306a36Sopenharmony_ci		line6pcm->out.bytes = 0;
60062306a36Sopenharmony_ci		line6pcm->in.count = 0;
60162306a36Sopenharmony_ci		line6pcm->in.pos_done = 0;
60262306a36Sopenharmony_ci		line6pcm->in.bytes = 0;
60362306a36Sopenharmony_ci	}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	mutex_unlock(&line6pcm->state_mutex);
60662306a36Sopenharmony_ci	return 0;
60762306a36Sopenharmony_ci}
608