162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * oxfw_pcm.c - a part of driver for OXFW970/971 based devices
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "oxfw.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_cistatic int hw_rule_rate(struct snd_pcm_hw_params *params,
1162306a36Sopenharmony_ci			struct snd_pcm_hw_rule *rule)
1262306a36Sopenharmony_ci{
1362306a36Sopenharmony_ci	u8 **formats = rule->private;
1462306a36Sopenharmony_ci	struct snd_interval *r =
1562306a36Sopenharmony_ci		hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
1662306a36Sopenharmony_ci	const struct snd_interval *c =
1762306a36Sopenharmony_ci		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
1862306a36Sopenharmony_ci	struct snd_interval t = {
1962306a36Sopenharmony_ci		.min = UINT_MAX, .max = 0, .integer = 1
2062306a36Sopenharmony_ci	};
2162306a36Sopenharmony_ci	struct snd_oxfw_stream_formation formation;
2262306a36Sopenharmony_ci	int i, err;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
2562306a36Sopenharmony_ci		if (formats[i] == NULL)
2662306a36Sopenharmony_ci			continue;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci		err = snd_oxfw_stream_parse_format(formats[i], &formation);
2962306a36Sopenharmony_ci		if (err < 0)
3062306a36Sopenharmony_ci			continue;
3162306a36Sopenharmony_ci		if (!snd_interval_test(c, formation.pcm))
3262306a36Sopenharmony_ci			continue;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci		t.min = min(t.min, formation.rate);
3562306a36Sopenharmony_ci		t.max = max(t.max, formation.rate);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	}
3862306a36Sopenharmony_ci	return snd_interval_refine(r, &t);
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic int hw_rule_channels(struct snd_pcm_hw_params *params,
4262306a36Sopenharmony_ci			    struct snd_pcm_hw_rule *rule)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	u8 **formats = rule->private;
4562306a36Sopenharmony_ci	struct snd_interval *c =
4662306a36Sopenharmony_ci		hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
4762306a36Sopenharmony_ci	const struct snd_interval *r =
4862306a36Sopenharmony_ci		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
4962306a36Sopenharmony_ci	struct snd_oxfw_stream_formation formation;
5062306a36Sopenharmony_ci	int i, j, err;
5162306a36Sopenharmony_ci	unsigned int count, list[SND_OXFW_STREAM_FORMAT_ENTRIES] = {0};
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	count = 0;
5462306a36Sopenharmony_ci	for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
5562306a36Sopenharmony_ci		if (formats[i] == NULL)
5662306a36Sopenharmony_ci			break;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci		err = snd_oxfw_stream_parse_format(formats[i], &formation);
5962306a36Sopenharmony_ci		if (err < 0)
6062306a36Sopenharmony_ci			continue;
6162306a36Sopenharmony_ci		if (!snd_interval_test(r, formation.rate))
6262306a36Sopenharmony_ci			continue;
6362306a36Sopenharmony_ci		if (list[count] == formation.pcm)
6462306a36Sopenharmony_ci			continue;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci		for (j = 0; j < ARRAY_SIZE(list); j++) {
6762306a36Sopenharmony_ci			if (list[j] == formation.pcm)
6862306a36Sopenharmony_ci				break;
6962306a36Sopenharmony_ci		}
7062306a36Sopenharmony_ci		if (j == ARRAY_SIZE(list)) {
7162306a36Sopenharmony_ci			list[count] = formation.pcm;
7262306a36Sopenharmony_ci			if (++count == ARRAY_SIZE(list))
7362306a36Sopenharmony_ci				break;
7462306a36Sopenharmony_ci		}
7562306a36Sopenharmony_ci	}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	return snd_interval_list(c, count, list, 0);
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic void limit_channels_and_rates(struct snd_pcm_hardware *hw, u8 **formats)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	struct snd_oxfw_stream_formation formation;
8362306a36Sopenharmony_ci	int i, err;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	hw->channels_min = UINT_MAX;
8662306a36Sopenharmony_ci	hw->channels_max = 0;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	hw->rate_min = UINT_MAX;
8962306a36Sopenharmony_ci	hw->rate_max = 0;
9062306a36Sopenharmony_ci	hw->rates = 0;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
9362306a36Sopenharmony_ci		if (formats[i] == NULL)
9462306a36Sopenharmony_ci			break;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci		err = snd_oxfw_stream_parse_format(formats[i], &formation);
9762306a36Sopenharmony_ci		if (err < 0)
9862306a36Sopenharmony_ci			continue;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci		hw->channels_min = min(hw->channels_min, formation.pcm);
10162306a36Sopenharmony_ci		hw->channels_max = max(hw->channels_max, formation.pcm);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci		hw->rate_min = min(hw->rate_min, formation.rate);
10462306a36Sopenharmony_ci		hw->rate_max = max(hw->rate_max, formation.rate);
10562306a36Sopenharmony_ci		hw->rates |= snd_pcm_rate_to_rate_bit(formation.rate);
10662306a36Sopenharmony_ci	}
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic int init_hw_params(struct snd_oxfw *oxfw,
11062306a36Sopenharmony_ci			  struct snd_pcm_substream *substream)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
11362306a36Sopenharmony_ci	u8 **formats;
11462306a36Sopenharmony_ci	struct amdtp_stream *stream;
11562306a36Sopenharmony_ci	int err;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
11862306a36Sopenharmony_ci		runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS;
11962306a36Sopenharmony_ci		stream = &oxfw->tx_stream;
12062306a36Sopenharmony_ci		formats = oxfw->tx_stream_formats;
12162306a36Sopenharmony_ci	} else {
12262306a36Sopenharmony_ci		runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS;
12362306a36Sopenharmony_ci		stream = &oxfw->rx_stream;
12462306a36Sopenharmony_ci		formats = oxfw->rx_stream_formats;
12562306a36Sopenharmony_ci	}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	limit_channels_and_rates(&runtime->hw, formats);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
13062306a36Sopenharmony_ci				  hw_rule_channels, formats,
13162306a36Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_RATE, -1);
13262306a36Sopenharmony_ci	if (err < 0)
13362306a36Sopenharmony_ci		goto end;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
13662306a36Sopenharmony_ci				  hw_rule_rate, formats,
13762306a36Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
13862306a36Sopenharmony_ci	if (err < 0)
13962306a36Sopenharmony_ci		goto end;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	err = amdtp_am824_add_pcm_hw_constraints(stream, runtime);
14262306a36Sopenharmony_ciend:
14362306a36Sopenharmony_ci	return err;
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic int limit_to_current_params(struct snd_pcm_substream *substream)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	struct snd_oxfw *oxfw = substream->private_data;
14962306a36Sopenharmony_ci	struct snd_oxfw_stream_formation formation;
15062306a36Sopenharmony_ci	enum avc_general_plug_dir dir;
15162306a36Sopenharmony_ci	int err;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
15462306a36Sopenharmony_ci		dir = AVC_GENERAL_PLUG_DIR_OUT;
15562306a36Sopenharmony_ci	else
15662306a36Sopenharmony_ci		dir = AVC_GENERAL_PLUG_DIR_IN;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation);
15962306a36Sopenharmony_ci	if (err < 0)
16062306a36Sopenharmony_ci		goto end;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	substream->runtime->hw.channels_min = formation.pcm;
16362306a36Sopenharmony_ci	substream->runtime->hw.channels_max = formation.pcm;
16462306a36Sopenharmony_ci	substream->runtime->hw.rate_min = formation.rate;
16562306a36Sopenharmony_ci	substream->runtime->hw.rate_max = formation.rate;
16662306a36Sopenharmony_ciend:
16762306a36Sopenharmony_ci	return err;
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic int pcm_open(struct snd_pcm_substream *substream)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	struct snd_oxfw *oxfw = substream->private_data;
17362306a36Sopenharmony_ci	struct amdtp_domain *d = &oxfw->domain;
17462306a36Sopenharmony_ci	int err;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	err = snd_oxfw_stream_lock_try(oxfw);
17762306a36Sopenharmony_ci	if (err < 0)
17862306a36Sopenharmony_ci		return err;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	err = init_hw_params(oxfw, substream);
18162306a36Sopenharmony_ci	if (err < 0)
18262306a36Sopenharmony_ci		goto err_locked;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	mutex_lock(&oxfw->mutex);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	// When source of clock is not internal or any stream is reserved for
18762306a36Sopenharmony_ci	// transmission of PCM frames, the available sampling rate is limited
18862306a36Sopenharmony_ci	// at current one.
18962306a36Sopenharmony_ci	if (oxfw->substreams_count > 0 && d->events_per_period > 0) {
19062306a36Sopenharmony_ci		unsigned int frames_per_period = d->events_per_period;
19162306a36Sopenharmony_ci		unsigned int frames_per_buffer = d->events_per_buffer;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci		err = limit_to_current_params(substream);
19462306a36Sopenharmony_ci		if (err < 0) {
19562306a36Sopenharmony_ci			mutex_unlock(&oxfw->mutex);
19662306a36Sopenharmony_ci			goto err_locked;
19762306a36Sopenharmony_ci		}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci		if (frames_per_period > 0) {
20062306a36Sopenharmony_ci			err = snd_pcm_hw_constraint_minmax(substream->runtime,
20162306a36Sopenharmony_ci					SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
20262306a36Sopenharmony_ci					frames_per_period, frames_per_period);
20362306a36Sopenharmony_ci			if (err < 0) {
20462306a36Sopenharmony_ci				mutex_unlock(&oxfw->mutex);
20562306a36Sopenharmony_ci				goto err_locked;
20662306a36Sopenharmony_ci			}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci			err = snd_pcm_hw_constraint_minmax(substream->runtime,
20962306a36Sopenharmony_ci					SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
21062306a36Sopenharmony_ci					frames_per_buffer, frames_per_buffer);
21162306a36Sopenharmony_ci			if (err < 0) {
21262306a36Sopenharmony_ci				mutex_unlock(&oxfw->mutex);
21362306a36Sopenharmony_ci				goto err_locked;
21462306a36Sopenharmony_ci			}
21562306a36Sopenharmony_ci		}
21662306a36Sopenharmony_ci	}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	mutex_unlock(&oxfw->mutex);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	snd_pcm_set_sync(substream);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	return 0;
22362306a36Sopenharmony_cierr_locked:
22462306a36Sopenharmony_ci	snd_oxfw_stream_lock_release(oxfw);
22562306a36Sopenharmony_ci	return err;
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic int pcm_close(struct snd_pcm_substream *substream)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	struct snd_oxfw *oxfw = substream->private_data;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	snd_oxfw_stream_lock_release(oxfw);
23362306a36Sopenharmony_ci	return 0;
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistatic int pcm_capture_hw_params(struct snd_pcm_substream *substream,
23762306a36Sopenharmony_ci				 struct snd_pcm_hw_params *hw_params)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	struct snd_oxfw *oxfw = substream->private_data;
24062306a36Sopenharmony_ci	int err = 0;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
24362306a36Sopenharmony_ci		unsigned int rate = params_rate(hw_params);
24462306a36Sopenharmony_ci		unsigned int channels = params_channels(hw_params);
24562306a36Sopenharmony_ci		unsigned int frames_per_period = params_period_size(hw_params);
24662306a36Sopenharmony_ci		unsigned int frames_per_buffer = params_buffer_size(hw_params);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci		mutex_lock(&oxfw->mutex);
24962306a36Sopenharmony_ci		err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->tx_stream,
25062306a36Sopenharmony_ci					rate, channels, frames_per_period,
25162306a36Sopenharmony_ci					frames_per_buffer);
25262306a36Sopenharmony_ci		if (err >= 0)
25362306a36Sopenharmony_ci			++oxfw->substreams_count;
25462306a36Sopenharmony_ci		mutex_unlock(&oxfw->mutex);
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	return err;
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_cistatic int pcm_playback_hw_params(struct snd_pcm_substream *substream,
26062306a36Sopenharmony_ci				  struct snd_pcm_hw_params *hw_params)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	struct snd_oxfw *oxfw = substream->private_data;
26362306a36Sopenharmony_ci	int err = 0;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
26662306a36Sopenharmony_ci		unsigned int rate = params_rate(hw_params);
26762306a36Sopenharmony_ci		unsigned int channels = params_channels(hw_params);
26862306a36Sopenharmony_ci		unsigned int frames_per_period = params_period_size(hw_params);
26962306a36Sopenharmony_ci		unsigned int frames_per_buffer = params_buffer_size(hw_params);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci		mutex_lock(&oxfw->mutex);
27262306a36Sopenharmony_ci		err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->rx_stream,
27362306a36Sopenharmony_ci					rate, channels, frames_per_period,
27462306a36Sopenharmony_ci					frames_per_buffer);
27562306a36Sopenharmony_ci		if (err >= 0)
27662306a36Sopenharmony_ci			++oxfw->substreams_count;
27762306a36Sopenharmony_ci		mutex_unlock(&oxfw->mutex);
27862306a36Sopenharmony_ci	}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	return err;
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cistatic int pcm_capture_hw_free(struct snd_pcm_substream *substream)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	struct snd_oxfw *oxfw = substream->private_data;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	mutex_lock(&oxfw->mutex);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
29062306a36Sopenharmony_ci		--oxfw->substreams_count;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	snd_oxfw_stream_stop_duplex(oxfw);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	mutex_unlock(&oxfw->mutex);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	return 0;
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_cistatic int pcm_playback_hw_free(struct snd_pcm_substream *substream)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	struct snd_oxfw *oxfw = substream->private_data;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	mutex_lock(&oxfw->mutex);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
30562306a36Sopenharmony_ci		--oxfw->substreams_count;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	snd_oxfw_stream_stop_duplex(oxfw);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	mutex_unlock(&oxfw->mutex);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	return 0;
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistatic int pcm_capture_prepare(struct snd_pcm_substream *substream)
31562306a36Sopenharmony_ci{
31662306a36Sopenharmony_ci	struct snd_oxfw *oxfw = substream->private_data;
31762306a36Sopenharmony_ci	int err;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	mutex_lock(&oxfw->mutex);
32062306a36Sopenharmony_ci	err = snd_oxfw_stream_start_duplex(oxfw);
32162306a36Sopenharmony_ci	mutex_unlock(&oxfw->mutex);
32262306a36Sopenharmony_ci	if (err < 0)
32362306a36Sopenharmony_ci		goto end;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	amdtp_stream_pcm_prepare(&oxfw->tx_stream);
32662306a36Sopenharmony_ciend:
32762306a36Sopenharmony_ci	return err;
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_cistatic int pcm_playback_prepare(struct snd_pcm_substream *substream)
33062306a36Sopenharmony_ci{
33162306a36Sopenharmony_ci	struct snd_oxfw *oxfw = substream->private_data;
33262306a36Sopenharmony_ci	int err;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	mutex_lock(&oxfw->mutex);
33562306a36Sopenharmony_ci	err = snd_oxfw_stream_start_duplex(oxfw);
33662306a36Sopenharmony_ci	mutex_unlock(&oxfw->mutex);
33762306a36Sopenharmony_ci	if (err < 0)
33862306a36Sopenharmony_ci		goto end;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	amdtp_stream_pcm_prepare(&oxfw->rx_stream);
34162306a36Sopenharmony_ciend:
34262306a36Sopenharmony_ci	return err;
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cistatic int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
34662306a36Sopenharmony_ci{
34762306a36Sopenharmony_ci	struct snd_oxfw *oxfw = substream->private_data;
34862306a36Sopenharmony_ci	struct snd_pcm_substream *pcm;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	switch (cmd) {
35162306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
35262306a36Sopenharmony_ci		pcm = substream;
35362306a36Sopenharmony_ci		break;
35462306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
35562306a36Sopenharmony_ci		pcm = NULL;
35662306a36Sopenharmony_ci		break;
35762306a36Sopenharmony_ci	default:
35862306a36Sopenharmony_ci		return -EINVAL;
35962306a36Sopenharmony_ci	}
36062306a36Sopenharmony_ci	amdtp_stream_pcm_trigger(&oxfw->tx_stream, pcm);
36162306a36Sopenharmony_ci	return 0;
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_cistatic int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	struct snd_oxfw *oxfw = substream->private_data;
36662306a36Sopenharmony_ci	struct snd_pcm_substream *pcm;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	switch (cmd) {
36962306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
37062306a36Sopenharmony_ci		pcm = substream;
37162306a36Sopenharmony_ci		break;
37262306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
37362306a36Sopenharmony_ci		pcm = NULL;
37462306a36Sopenharmony_ci		break;
37562306a36Sopenharmony_ci	default:
37662306a36Sopenharmony_ci		return -EINVAL;
37762306a36Sopenharmony_ci	}
37862306a36Sopenharmony_ci	amdtp_stream_pcm_trigger(&oxfw->rx_stream, pcm);
37962306a36Sopenharmony_ci	return 0;
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_cistatic snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstm)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	struct snd_oxfw *oxfw = sbstm->private_data;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	return amdtp_domain_stream_pcm_pointer(&oxfw->domain, &oxfw->tx_stream);
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_cistatic snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstm)
38962306a36Sopenharmony_ci{
39062306a36Sopenharmony_ci	struct snd_oxfw *oxfw = sbstm->private_data;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	return amdtp_domain_stream_pcm_pointer(&oxfw->domain, &oxfw->rx_stream);
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cistatic int pcm_capture_ack(struct snd_pcm_substream *substream)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	struct snd_oxfw *oxfw = substream->private_data;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	return amdtp_domain_stream_pcm_ack(&oxfw->domain, &oxfw->tx_stream);
40062306a36Sopenharmony_ci}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_cistatic int pcm_playback_ack(struct snd_pcm_substream *substream)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	struct snd_oxfw *oxfw = substream->private_data;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	return amdtp_domain_stream_pcm_ack(&oxfw->domain, &oxfw->rx_stream);
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ciint snd_oxfw_create_pcm(struct snd_oxfw *oxfw)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	static const struct snd_pcm_ops capture_ops = {
41262306a36Sopenharmony_ci		.open      = pcm_open,
41362306a36Sopenharmony_ci		.close     = pcm_close,
41462306a36Sopenharmony_ci		.hw_params = pcm_capture_hw_params,
41562306a36Sopenharmony_ci		.hw_free   = pcm_capture_hw_free,
41662306a36Sopenharmony_ci		.prepare   = pcm_capture_prepare,
41762306a36Sopenharmony_ci		.trigger   = pcm_capture_trigger,
41862306a36Sopenharmony_ci		.pointer   = pcm_capture_pointer,
41962306a36Sopenharmony_ci		.ack       = pcm_capture_ack,
42062306a36Sopenharmony_ci	};
42162306a36Sopenharmony_ci	static const struct snd_pcm_ops playback_ops = {
42262306a36Sopenharmony_ci		.open      = pcm_open,
42362306a36Sopenharmony_ci		.close     = pcm_close,
42462306a36Sopenharmony_ci		.hw_params = pcm_playback_hw_params,
42562306a36Sopenharmony_ci		.hw_free   = pcm_playback_hw_free,
42662306a36Sopenharmony_ci		.prepare   = pcm_playback_prepare,
42762306a36Sopenharmony_ci		.trigger   = pcm_playback_trigger,
42862306a36Sopenharmony_ci		.pointer   = pcm_playback_pointer,
42962306a36Sopenharmony_ci		.ack       = pcm_playback_ack,
43062306a36Sopenharmony_ci	};
43162306a36Sopenharmony_ci	struct snd_pcm *pcm;
43262306a36Sopenharmony_ci	unsigned int cap = 0;
43362306a36Sopenharmony_ci	int err;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	if (oxfw->has_output)
43662306a36Sopenharmony_ci		cap = 1;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	err = snd_pcm_new(oxfw->card, oxfw->card->driver, 0, 1, cap, &pcm);
43962306a36Sopenharmony_ci	if (err < 0)
44062306a36Sopenharmony_ci		return err;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	pcm->private_data = oxfw;
44362306a36Sopenharmony_ci	strcpy(pcm->name, oxfw->card->shortname);
44462306a36Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
44562306a36Sopenharmony_ci	if (cap > 0)
44662306a36Sopenharmony_ci		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
44762306a36Sopenharmony_ci	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	return 0;
45062306a36Sopenharmony_ci}
451