xref: /openbmc/linux/sound/firewire/dice/dice-pcm.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1da607e19SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2c50fb91fSTakashi Sakamoto /*
3c50fb91fSTakashi Sakamoto  * dice_pcm.c - a part of driver for DICE based devices
4c50fb91fSTakashi Sakamoto  *
5c50fb91fSTakashi Sakamoto  * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
6c50fb91fSTakashi Sakamoto  * Copyright (c) 2014 Takashi Sakamoto <o-takashi@sakamocchi.jp>
7c50fb91fSTakashi Sakamoto  */
8c50fb91fSTakashi Sakamoto 
9c50fb91fSTakashi Sakamoto #include "dice.h"
10c50fb91fSTakashi Sakamoto 
dice_rate_constraint(struct snd_pcm_hw_params * params,struct snd_pcm_hw_rule * rule)11bd2b441cSTakashi Sakamoto static int dice_rate_constraint(struct snd_pcm_hw_params *params,
12bd2b441cSTakashi Sakamoto 				struct snd_pcm_hw_rule *rule)
13bd2b441cSTakashi Sakamoto {
14bd2b441cSTakashi Sakamoto 	struct snd_pcm_substream *substream = rule->private;
15bd2b441cSTakashi Sakamoto 	struct snd_dice *dice = substream->private_data;
16bd2b441cSTakashi Sakamoto 	unsigned int index = substream->pcm->device;
17bd2b441cSTakashi Sakamoto 
18bd2b441cSTakashi Sakamoto 	const struct snd_interval *c =
19bd2b441cSTakashi Sakamoto 		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
20bd2b441cSTakashi Sakamoto 	struct snd_interval *r =
21bd2b441cSTakashi Sakamoto 		hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
22bd2b441cSTakashi Sakamoto 	struct snd_interval rates = {
23bd2b441cSTakashi Sakamoto 		.min = UINT_MAX, .max = 0, .integer = 1
24bd2b441cSTakashi Sakamoto 	};
25bd2b441cSTakashi Sakamoto 	unsigned int *pcm_channels;
26bd2b441cSTakashi Sakamoto 	enum snd_dice_rate_mode mode;
27bd2b441cSTakashi Sakamoto 	unsigned int i, rate;
28bd2b441cSTakashi Sakamoto 
29bd2b441cSTakashi Sakamoto 	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
30bd2b441cSTakashi Sakamoto 		pcm_channels = dice->tx_pcm_chs[index];
31bd2b441cSTakashi Sakamoto 	else
32bd2b441cSTakashi Sakamoto 		pcm_channels = dice->rx_pcm_chs[index];
33bd2b441cSTakashi Sakamoto 
34bd2b441cSTakashi Sakamoto 	for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
35bd2b441cSTakashi Sakamoto 		rate = snd_dice_rates[i];
36bd2b441cSTakashi Sakamoto 		if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
37bd2b441cSTakashi Sakamoto 			continue;
38bd2b441cSTakashi Sakamoto 
39bd2b441cSTakashi Sakamoto 		if (!snd_interval_test(c, pcm_channels[mode]))
40bd2b441cSTakashi Sakamoto 			continue;
41bd2b441cSTakashi Sakamoto 
42bd2b441cSTakashi Sakamoto 		rates.min = min(rates.min, rate);
43bd2b441cSTakashi Sakamoto 		rates.max = max(rates.max, rate);
44bd2b441cSTakashi Sakamoto 	}
45bd2b441cSTakashi Sakamoto 
46bd2b441cSTakashi Sakamoto 	return snd_interval_refine(r, &rates);
47bd2b441cSTakashi Sakamoto }
48bd2b441cSTakashi Sakamoto 
dice_channels_constraint(struct snd_pcm_hw_params * params,struct snd_pcm_hw_rule * rule)49bd2b441cSTakashi Sakamoto static int dice_channels_constraint(struct snd_pcm_hw_params *params,
50bd2b441cSTakashi Sakamoto 				    struct snd_pcm_hw_rule *rule)
51bd2b441cSTakashi Sakamoto {
52bd2b441cSTakashi Sakamoto 	struct snd_pcm_substream *substream = rule->private;
53bd2b441cSTakashi Sakamoto 	struct snd_dice *dice = substream->private_data;
54bd2b441cSTakashi Sakamoto 	unsigned int index = substream->pcm->device;
55bd2b441cSTakashi Sakamoto 
56bd2b441cSTakashi Sakamoto 	const struct snd_interval *r =
57bd2b441cSTakashi Sakamoto 		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
58bd2b441cSTakashi Sakamoto 	struct snd_interval *c =
59bd2b441cSTakashi Sakamoto 		hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
60bd2b441cSTakashi Sakamoto 	struct snd_interval channels = {
61bd2b441cSTakashi Sakamoto 		.min = UINT_MAX, .max = 0, .integer = 1
62bd2b441cSTakashi Sakamoto 	};
63bd2b441cSTakashi Sakamoto 	unsigned int *pcm_channels;
64bd2b441cSTakashi Sakamoto 	enum snd_dice_rate_mode mode;
65bd2b441cSTakashi Sakamoto 	unsigned int i, rate;
66bd2b441cSTakashi Sakamoto 
67bd2b441cSTakashi Sakamoto 	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
68bd2b441cSTakashi Sakamoto 		pcm_channels = dice->tx_pcm_chs[index];
69bd2b441cSTakashi Sakamoto 	else
70bd2b441cSTakashi Sakamoto 		pcm_channels = dice->rx_pcm_chs[index];
71bd2b441cSTakashi Sakamoto 
72bd2b441cSTakashi Sakamoto 	for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
73bd2b441cSTakashi Sakamoto 		rate = snd_dice_rates[i];
74bd2b441cSTakashi Sakamoto 		if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
75bd2b441cSTakashi Sakamoto 			continue;
76bd2b441cSTakashi Sakamoto 
77bd2b441cSTakashi Sakamoto 		if (!snd_interval_test(r, rate))
78bd2b441cSTakashi Sakamoto 			continue;
79bd2b441cSTakashi Sakamoto 
80bd2b441cSTakashi Sakamoto 		channels.min = min(channels.min, pcm_channels[mode]);
81bd2b441cSTakashi Sakamoto 		channels.max = max(channels.max, pcm_channels[mode]);
82bd2b441cSTakashi Sakamoto 	}
83bd2b441cSTakashi Sakamoto 
84bd2b441cSTakashi Sakamoto 	return snd_interval_refine(c, &channels);
85bd2b441cSTakashi Sakamoto }
86bd2b441cSTakashi Sakamoto 
limit_channels_and_rates(struct snd_dice * dice,struct snd_pcm_runtime * runtime,enum amdtp_stream_direction dir,unsigned int index)870d5ee195STakashi Sakamoto static int limit_channels_and_rates(struct snd_dice *dice,
882c2416c8STakashi Sakamoto 				    struct snd_pcm_runtime *runtime,
894bdc495cSTakashi Sakamoto 				    enum amdtp_stream_direction dir,
90bd2b441cSTakashi Sakamoto 				    unsigned int index)
91c50fb91fSTakashi Sakamoto {
922c2416c8STakashi Sakamoto 	struct snd_pcm_hardware *hw = &runtime->hw;
93bd2b441cSTakashi Sakamoto 	unsigned int *pcm_channels;
94bd2b441cSTakashi Sakamoto 	unsigned int i;
952c2416c8STakashi Sakamoto 
96bd2b441cSTakashi Sakamoto 	if (dir == AMDTP_IN_STREAM)
97bd2b441cSTakashi Sakamoto 		pcm_channels = dice->tx_pcm_chs[index];
98bd2b441cSTakashi Sakamoto 	else
99bd2b441cSTakashi Sakamoto 		pcm_channels = dice->rx_pcm_chs[index];
100bd2b441cSTakashi Sakamoto 
101bd2b441cSTakashi Sakamoto 	hw->channels_min = UINT_MAX;
102bd2b441cSTakashi Sakamoto 	hw->channels_max = 0;
103bd2b441cSTakashi Sakamoto 
104bd2b441cSTakashi Sakamoto 	for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
105bd2b441cSTakashi Sakamoto 		enum snd_dice_rate_mode mode;
106bd2b441cSTakashi Sakamoto 		unsigned int rate, channels;
107bd2b441cSTakashi Sakamoto 
108bd2b441cSTakashi Sakamoto 		rate = snd_dice_rates[i];
109bd2b441cSTakashi Sakamoto 		if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
110bd2b441cSTakashi Sakamoto 			continue;
111bd2b441cSTakashi Sakamoto 		hw->rates |= snd_pcm_rate_to_rate_bit(rate);
112bd2b441cSTakashi Sakamoto 
113bd2b441cSTakashi Sakamoto 		channels = pcm_channels[mode];
114bd2b441cSTakashi Sakamoto 		if (channels == 0)
115bd2b441cSTakashi Sakamoto 			continue;
116bd2b441cSTakashi Sakamoto 		hw->channels_min = min(hw->channels_min, channels);
117bd2b441cSTakashi Sakamoto 		hw->channels_max = max(hw->channels_max, channels);
1182c2416c8STakashi Sakamoto 	}
1192c2416c8STakashi Sakamoto 
1202c2416c8STakashi Sakamoto 	snd_pcm_limit_hw_rates(runtime);
1210d5ee195STakashi Sakamoto 
1220d5ee195STakashi Sakamoto 	return 0;
1232c2416c8STakashi Sakamoto }
1242c2416c8STakashi Sakamoto 
init_hw_info(struct snd_dice * dice,struct snd_pcm_substream * substream)1252c2416c8STakashi Sakamoto static int init_hw_info(struct snd_dice *dice,
1262c2416c8STakashi Sakamoto 			struct snd_pcm_substream *substream)
1272c2416c8STakashi Sakamoto {
1282c2416c8STakashi Sakamoto 	struct snd_pcm_runtime *runtime = substream->runtime;
1292c2416c8STakashi Sakamoto 	struct snd_pcm_hardware *hw = &runtime->hw;
130bd2b441cSTakashi Sakamoto 	unsigned int index = substream->pcm->device;
1314bdc495cSTakashi Sakamoto 	enum amdtp_stream_direction dir;
13269dcf3e4STakashi Sakamoto 	struct amdtp_stream *stream;
1332c2416c8STakashi Sakamoto 	int err;
1342c2416c8STakashi Sakamoto 
13569dcf3e4STakashi Sakamoto 	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
13649c7b3fcSTakashi Sakamoto 		hw->formats = AM824_IN_PCM_FORMAT_BITS;
1374bdc495cSTakashi Sakamoto 		dir = AMDTP_IN_STREAM;
138bd2b441cSTakashi Sakamoto 		stream = &dice->tx_stream[index];
13969dcf3e4STakashi Sakamoto 	} else {
14049c7b3fcSTakashi Sakamoto 		hw->formats = AM824_OUT_PCM_FORMAT_BITS;
1414bdc495cSTakashi Sakamoto 		dir = AMDTP_OUT_STREAM;
142bd2b441cSTakashi Sakamoto 		stream = &dice->rx_stream[index];
14369dcf3e4STakashi Sakamoto 	}
14469dcf3e4STakashi Sakamoto 
145bd2b441cSTakashi Sakamoto 	err = limit_channels_and_rates(dice, substream->runtime, dir,
146bd2b441cSTakashi Sakamoto 				       index);
1474bdc495cSTakashi Sakamoto 	if (err < 0)
1484bdc495cSTakashi Sakamoto 		return err;
1494bdc495cSTakashi Sakamoto 
150bd2b441cSTakashi Sakamoto 	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
151bd2b441cSTakashi Sakamoto 				  dice_rate_constraint, substream,
152bd2b441cSTakashi Sakamoto 				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
153bd2b441cSTakashi Sakamoto 	if (err < 0)
154bd2b441cSTakashi Sakamoto 		return err;
155bd2b441cSTakashi Sakamoto 	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
156bd2b441cSTakashi Sakamoto 				  dice_channels_constraint, substream,
157bd2b441cSTakashi Sakamoto 				  SNDRV_PCM_HW_PARAM_RATE, -1);
1580d5ee195STakashi Sakamoto 	if (err < 0)
1590d5ee195STakashi Sakamoto 		return err;
160c50fb91fSTakashi Sakamoto 
1610d5ee195STakashi Sakamoto 	return amdtp_am824_add_pcm_hw_constraints(stream, runtime);
1622c2416c8STakashi Sakamoto }
1632c2416c8STakashi Sakamoto 
pcm_open(struct snd_pcm_substream * substream)1642c2416c8STakashi Sakamoto static int pcm_open(struct snd_pcm_substream *substream)
1652c2416c8STakashi Sakamoto {
1662c2416c8STakashi Sakamoto 	struct snd_dice *dice = substream->private_data;
167a8fb2248STakashi Sakamoto 	struct amdtp_domain *d = &dice->domain;
168bd2b441cSTakashi Sakamoto 	unsigned int source;
169bd2b441cSTakashi Sakamoto 	bool internal;
1702c2416c8STakashi Sakamoto 	int err;
1712c2416c8STakashi Sakamoto 
1722c2416c8STakashi Sakamoto 	err = snd_dice_stream_lock_try(dice);
173c50fb91fSTakashi Sakamoto 	if (err < 0)
174a8fb2248STakashi Sakamoto 		return err;
175c50fb91fSTakashi Sakamoto 
1762c2416c8STakashi Sakamoto 	err = init_hw_info(dice, substream);
1772c2416c8STakashi Sakamoto 	if (err < 0)
1782c2416c8STakashi Sakamoto 		goto err_locked;
1798fc01fc0STakashi Sakamoto 
180bd2b441cSTakashi Sakamoto 	err = snd_dice_transaction_get_clock_source(dice, &source);
181bd2b441cSTakashi Sakamoto 	if (err < 0)
182bd2b441cSTakashi Sakamoto 		goto err_locked;
183bd2b441cSTakashi Sakamoto 	switch (source) {
184bd2b441cSTakashi Sakamoto 	case CLOCK_SOURCE_AES1:
185bd2b441cSTakashi Sakamoto 	case CLOCK_SOURCE_AES2:
186bd2b441cSTakashi Sakamoto 	case CLOCK_SOURCE_AES3:
187bd2b441cSTakashi Sakamoto 	case CLOCK_SOURCE_AES4:
188bd2b441cSTakashi Sakamoto 	case CLOCK_SOURCE_AES_ANY:
189bd2b441cSTakashi Sakamoto 	case CLOCK_SOURCE_ADAT:
190bd2b441cSTakashi Sakamoto 	case CLOCK_SOURCE_TDIF:
191bd2b441cSTakashi Sakamoto 	case CLOCK_SOURCE_WC:
192bd2b441cSTakashi Sakamoto 		internal = false;
193bd2b441cSTakashi Sakamoto 		break;
194bd2b441cSTakashi Sakamoto 	default:
195bd2b441cSTakashi Sakamoto 		internal = true;
196bd2b441cSTakashi Sakamoto 		break;
197bd2b441cSTakashi Sakamoto 	}
198bd2b441cSTakashi Sakamoto 
199a8fb2248STakashi Sakamoto 	mutex_lock(&dice->mutex);
200a8fb2248STakashi Sakamoto 
201a8fb2248STakashi Sakamoto 	// When source of clock is not internal or any stream is reserved for
202a8fb2248STakashi Sakamoto 	// transmission of PCM frames, the available sampling rate is limited
203a8fb2248STakashi Sakamoto 	// at current one.
204bd2b441cSTakashi Sakamoto 	if (!internal ||
205a8fb2248STakashi Sakamoto 	    (dice->substreams_counter > 0 && d->events_per_period > 0)) {
206a8fb2248STakashi Sakamoto 		unsigned int frames_per_period = d->events_per_period;
207ecb40fd2STakashi Sakamoto 		unsigned int frames_per_buffer = d->events_per_buffer;
208bd2b441cSTakashi Sakamoto 		unsigned int rate;
209bd2b441cSTakashi Sakamoto 
210bd2b441cSTakashi Sakamoto 		err = snd_dice_transaction_get_rate(dice, &rate);
211a8fb2248STakashi Sakamoto 		if (err < 0) {
212a8fb2248STakashi Sakamoto 			mutex_unlock(&dice->mutex);
213bd2b441cSTakashi Sakamoto 			goto err_locked;
214bd2b441cSTakashi Sakamoto 		}
215bd2b441cSTakashi Sakamoto 
216a8fb2248STakashi Sakamoto 		substream->runtime->hw.rate_min = rate;
217a8fb2248STakashi Sakamoto 		substream->runtime->hw.rate_max = rate;
218a8fb2248STakashi Sakamoto 
219a8fb2248STakashi Sakamoto 		if (frames_per_period > 0) {
220a8fb2248STakashi Sakamoto 			// For double_pcm_frame quirk.
2219f079c1bSTakashi Sakamoto 			if (rate > 96000 && !dice->disable_double_pcm_frames) {
222a8fb2248STakashi Sakamoto 				frames_per_period *= 2;
223ecb40fd2STakashi Sakamoto 				frames_per_buffer *= 2;
224ecb40fd2STakashi Sakamoto 			}
225a8fb2248STakashi Sakamoto 
226a8fb2248STakashi Sakamoto 			err = snd_pcm_hw_constraint_minmax(substream->runtime,
227a8fb2248STakashi Sakamoto 					SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
228a8fb2248STakashi Sakamoto 					frames_per_period, frames_per_period);
229a8fb2248STakashi Sakamoto 			if (err < 0) {
230a8fb2248STakashi Sakamoto 				mutex_unlock(&dice->mutex);
231a8fb2248STakashi Sakamoto 				goto err_locked;
232a8fb2248STakashi Sakamoto 			}
233ecb40fd2STakashi Sakamoto 
234ecb40fd2STakashi Sakamoto 			err = snd_pcm_hw_constraint_minmax(substream->runtime,
235ecb40fd2STakashi Sakamoto 					SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
236ecb40fd2STakashi Sakamoto 					frames_per_buffer, frames_per_buffer);
237ecb40fd2STakashi Sakamoto 			if (err < 0) {
238ecb40fd2STakashi Sakamoto 				mutex_unlock(&dice->mutex);
239ecb40fd2STakashi Sakamoto 				goto err_locked;
240ecb40fd2STakashi Sakamoto 			}
241a8fb2248STakashi Sakamoto 		}
242a8fb2248STakashi Sakamoto 	}
243a8fb2248STakashi Sakamoto 
244a8fb2248STakashi Sakamoto 	mutex_unlock(&dice->mutex);
245a8fb2248STakashi Sakamoto 
2468fc01fc0STakashi Sakamoto 	snd_pcm_set_sync(substream);
247a8fb2248STakashi Sakamoto 
248a8fb2248STakashi Sakamoto 	return 0;
2492c2416c8STakashi Sakamoto err_locked:
250c50fb91fSTakashi Sakamoto 	snd_dice_stream_lock_release(dice);
251c50fb91fSTakashi Sakamoto 	return err;
252c50fb91fSTakashi Sakamoto }
253c50fb91fSTakashi Sakamoto 
pcm_close(struct snd_pcm_substream * substream)254c50fb91fSTakashi Sakamoto static int pcm_close(struct snd_pcm_substream *substream)
255c50fb91fSTakashi Sakamoto {
256c50fb91fSTakashi Sakamoto 	struct snd_dice *dice = substream->private_data;
257c50fb91fSTakashi Sakamoto 
258c50fb91fSTakashi Sakamoto 	snd_dice_stream_lock_release(dice);
259c50fb91fSTakashi Sakamoto 
260c50fb91fSTakashi Sakamoto 	return 0;
261c50fb91fSTakashi Sakamoto }
262c50fb91fSTakashi Sakamoto 
pcm_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * hw_params)2634dbf4f44STakashi Sakamoto static int pcm_hw_params(struct snd_pcm_substream *substream,
264c50fb91fSTakashi Sakamoto 			 struct snd_pcm_hw_params *hw_params)
265c50fb91fSTakashi Sakamoto {
266c50fb91fSTakashi Sakamoto 	struct snd_dice *dice = substream->private_data;
2677641d549STakashi Iwai 	int err = 0;
26869dcf3e4STakashi Sakamoto 
269*23cb0767STakashi Iwai 	if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
2703cd2c2d7STakashi Sakamoto 		unsigned int rate = params_rate(hw_params);
27194c8101aSTakashi Sakamoto 		unsigned int events_per_period = params_period_size(hw_params);
272ecb40fd2STakashi Sakamoto 		unsigned int events_per_buffer = params_buffer_size(hw_params);
2733cd2c2d7STakashi Sakamoto 
27469dcf3e4STakashi Sakamoto 		mutex_lock(&dice->mutex);
27594c8101aSTakashi Sakamoto 		// For double_pcm_frame quirk.
2769f079c1bSTakashi Sakamoto 		if (rate > 96000 && !dice->disable_double_pcm_frames) {
27794c8101aSTakashi Sakamoto 			events_per_period /= 2;
278ecb40fd2STakashi Sakamoto 			events_per_buffer /= 2;
279ecb40fd2STakashi Sakamoto 		}
28094c8101aSTakashi Sakamoto 		err = snd_dice_stream_reserve_duplex(dice, rate,
281ecb40fd2STakashi Sakamoto 					events_per_period, events_per_buffer);
2823cd2c2d7STakashi Sakamoto 		if (err >= 0)
2833cd2c2d7STakashi Sakamoto 			++dice->substreams_counter;
28469dcf3e4STakashi Sakamoto 		mutex_unlock(&dice->mutex);
28569dcf3e4STakashi Sakamoto 	}
28669dcf3e4STakashi Sakamoto 
2873cd2c2d7STakashi Sakamoto 	return err;
288c50fb91fSTakashi Sakamoto }
289c50fb91fSTakashi Sakamoto 
pcm_hw_free(struct snd_pcm_substream * substream)2904dbf4f44STakashi Sakamoto static int pcm_hw_free(struct snd_pcm_substream *substream)
29169dcf3e4STakashi Sakamoto {
29269dcf3e4STakashi Sakamoto 	struct snd_dice *dice = substream->private_data;
29369dcf3e4STakashi Sakamoto 
29469dcf3e4STakashi Sakamoto 	mutex_lock(&dice->mutex);
29569dcf3e4STakashi Sakamoto 
296*23cb0767STakashi Iwai 	if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
2973cd2c2d7STakashi Sakamoto 		--dice->substreams_counter;
29869dcf3e4STakashi Sakamoto 
29969dcf3e4STakashi Sakamoto 	snd_dice_stream_stop_duplex(dice);
30069dcf3e4STakashi Sakamoto 
30169dcf3e4STakashi Sakamoto 	mutex_unlock(&dice->mutex);
30269dcf3e4STakashi Sakamoto 
3037641d549STakashi Iwai 	return 0;
30469dcf3e4STakashi Sakamoto }
30569dcf3e4STakashi Sakamoto 
capture_prepare(struct snd_pcm_substream * substream)30669dcf3e4STakashi Sakamoto static int capture_prepare(struct snd_pcm_substream *substream)
30769dcf3e4STakashi Sakamoto {
30869dcf3e4STakashi Sakamoto 	struct snd_dice *dice = substream->private_data;
3094bdc495cSTakashi Sakamoto 	struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
31069dcf3e4STakashi Sakamoto 	int err;
31169dcf3e4STakashi Sakamoto 
31269dcf3e4STakashi Sakamoto 	mutex_lock(&dice->mutex);
3133cd2c2d7STakashi Sakamoto 	err = snd_dice_stream_start_duplex(dice);
31469dcf3e4STakashi Sakamoto 	mutex_unlock(&dice->mutex);
31569dcf3e4STakashi Sakamoto 	if (err >= 0)
3168ae25b76STakashi Sakamoto 		amdtp_stream_pcm_prepare(stream);
31769dcf3e4STakashi Sakamoto 
31869dcf3e4STakashi Sakamoto 	return 0;
31969dcf3e4STakashi Sakamoto }
playback_prepare(struct snd_pcm_substream * substream)320c50fb91fSTakashi Sakamoto static int playback_prepare(struct snd_pcm_substream *substream)
321c50fb91fSTakashi Sakamoto {
322c50fb91fSTakashi Sakamoto 	struct snd_dice *dice = substream->private_data;
3234bdc495cSTakashi Sakamoto 	struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
324c50fb91fSTakashi Sakamoto 	int err;
325c50fb91fSTakashi Sakamoto 
326c50fb91fSTakashi Sakamoto 	mutex_lock(&dice->mutex);
3273cd2c2d7STakashi Sakamoto 	err = snd_dice_stream_start_duplex(dice);
328c50fb91fSTakashi Sakamoto 	mutex_unlock(&dice->mutex);
329288a8d0cSTakashi Sakamoto 	if (err >= 0)
3308ae25b76STakashi Sakamoto 		amdtp_stream_pcm_prepare(stream);
331c50fb91fSTakashi Sakamoto 
332288a8d0cSTakashi Sakamoto 	return err;
333c50fb91fSTakashi Sakamoto }
334c50fb91fSTakashi Sakamoto 
capture_trigger(struct snd_pcm_substream * substream,int cmd)33569dcf3e4STakashi Sakamoto static int capture_trigger(struct snd_pcm_substream *substream, int cmd)
33669dcf3e4STakashi Sakamoto {
33769dcf3e4STakashi Sakamoto 	struct snd_dice *dice = substream->private_data;
3384bdc495cSTakashi Sakamoto 	struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
33969dcf3e4STakashi Sakamoto 
34069dcf3e4STakashi Sakamoto 	switch (cmd) {
34169dcf3e4STakashi Sakamoto 	case SNDRV_PCM_TRIGGER_START:
3428ae25b76STakashi Sakamoto 		amdtp_stream_pcm_trigger(stream, substream);
34369dcf3e4STakashi Sakamoto 		break;
34469dcf3e4STakashi Sakamoto 	case SNDRV_PCM_TRIGGER_STOP:
3458ae25b76STakashi Sakamoto 		amdtp_stream_pcm_trigger(stream, NULL);
34669dcf3e4STakashi Sakamoto 		break;
34769dcf3e4STakashi Sakamoto 	default:
34869dcf3e4STakashi Sakamoto 		return -EINVAL;
34969dcf3e4STakashi Sakamoto 	}
35069dcf3e4STakashi Sakamoto 
35169dcf3e4STakashi Sakamoto 	return 0;
35269dcf3e4STakashi Sakamoto }
playback_trigger(struct snd_pcm_substream * substream,int cmd)353c50fb91fSTakashi Sakamoto static int playback_trigger(struct snd_pcm_substream *substream, int cmd)
354c50fb91fSTakashi Sakamoto {
355c50fb91fSTakashi Sakamoto 	struct snd_dice *dice = substream->private_data;
3564bdc495cSTakashi Sakamoto 	struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
357c50fb91fSTakashi Sakamoto 
358c50fb91fSTakashi Sakamoto 	switch (cmd) {
359c50fb91fSTakashi Sakamoto 	case SNDRV_PCM_TRIGGER_START:
3608ae25b76STakashi Sakamoto 		amdtp_stream_pcm_trigger(stream, substream);
361c50fb91fSTakashi Sakamoto 		break;
362c50fb91fSTakashi Sakamoto 	case SNDRV_PCM_TRIGGER_STOP:
3638ae25b76STakashi Sakamoto 		amdtp_stream_pcm_trigger(stream, NULL);
364c50fb91fSTakashi Sakamoto 		break;
365c50fb91fSTakashi Sakamoto 	default:
366c50fb91fSTakashi Sakamoto 		return -EINVAL;
367c50fb91fSTakashi Sakamoto 	}
368c50fb91fSTakashi Sakamoto 
369c50fb91fSTakashi Sakamoto 	return 0;
370c50fb91fSTakashi Sakamoto }
371c50fb91fSTakashi Sakamoto 
capture_pointer(struct snd_pcm_substream * substream)37269dcf3e4STakashi Sakamoto static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream)
37369dcf3e4STakashi Sakamoto {
37469dcf3e4STakashi Sakamoto 	struct snd_dice *dice = substream->private_data;
3754bdc495cSTakashi Sakamoto 	struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
37669dcf3e4STakashi Sakamoto 
377f890f9a0STakashi Sakamoto 	return amdtp_domain_stream_pcm_pointer(&dice->domain, stream);
37869dcf3e4STakashi Sakamoto }
playback_pointer(struct snd_pcm_substream * substream)379c50fb91fSTakashi Sakamoto static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream)
380c50fb91fSTakashi Sakamoto {
381c50fb91fSTakashi Sakamoto 	struct snd_dice *dice = substream->private_data;
3824bdc495cSTakashi Sakamoto 	struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
383c50fb91fSTakashi Sakamoto 
384f890f9a0STakashi Sakamoto 	return amdtp_domain_stream_pcm_pointer(&dice->domain, stream);
385c50fb91fSTakashi Sakamoto }
386c50fb91fSTakashi Sakamoto 
capture_ack(struct snd_pcm_substream * substream)387875becf8STakashi Sakamoto static int capture_ack(struct snd_pcm_substream *substream)
388875becf8STakashi Sakamoto {
389875becf8STakashi Sakamoto 	struct snd_dice *dice = substream->private_data;
390875becf8STakashi Sakamoto 	struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
391875becf8STakashi Sakamoto 
392e6dcc92fSTakashi Sakamoto 	return amdtp_domain_stream_pcm_ack(&dice->domain, stream);
393875becf8STakashi Sakamoto }
394875becf8STakashi Sakamoto 
playback_ack(struct snd_pcm_substream * substream)395875becf8STakashi Sakamoto static int playback_ack(struct snd_pcm_substream *substream)
396875becf8STakashi Sakamoto {
397875becf8STakashi Sakamoto 	struct snd_dice *dice = substream->private_data;
398875becf8STakashi Sakamoto 	struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
399875becf8STakashi Sakamoto 
400e6dcc92fSTakashi Sakamoto 	return amdtp_domain_stream_pcm_ack(&dice->domain, stream);
401875becf8STakashi Sakamoto }
402875becf8STakashi Sakamoto 
snd_dice_create_pcm(struct snd_dice * dice)403c50fb91fSTakashi Sakamoto int snd_dice_create_pcm(struct snd_dice *dice)
404c50fb91fSTakashi Sakamoto {
4055116ffc3SJulia Lawall 	static const struct snd_pcm_ops capture_ops = {
40669dcf3e4STakashi Sakamoto 		.open      = pcm_open,
40769dcf3e4STakashi Sakamoto 		.close     = pcm_close,
4084dbf4f44STakashi Sakamoto 		.hw_params = pcm_hw_params,
4094dbf4f44STakashi Sakamoto 		.hw_free   = pcm_hw_free,
41069dcf3e4STakashi Sakamoto 		.prepare   = capture_prepare,
41169dcf3e4STakashi Sakamoto 		.trigger   = capture_trigger,
41269dcf3e4STakashi Sakamoto 		.pointer   = capture_pointer,
413875becf8STakashi Sakamoto 		.ack       = capture_ack,
41469dcf3e4STakashi Sakamoto 	};
4155116ffc3SJulia Lawall 	static const struct snd_pcm_ops playback_ops = {
416c50fb91fSTakashi Sakamoto 		.open      = pcm_open,
417c50fb91fSTakashi Sakamoto 		.close     = pcm_close,
4184dbf4f44STakashi Sakamoto 		.hw_params = pcm_hw_params,
4194dbf4f44STakashi Sakamoto 		.hw_free   = pcm_hw_free,
420c50fb91fSTakashi Sakamoto 		.prepare   = playback_prepare,
421c50fb91fSTakashi Sakamoto 		.trigger   = playback_trigger,
422c50fb91fSTakashi Sakamoto 		.pointer   = playback_pointer,
423875becf8STakashi Sakamoto 		.ack       = playback_ack,
424c50fb91fSTakashi Sakamoto 	};
425c50fb91fSTakashi Sakamoto 	struct snd_pcm *pcm;
4269c367c01STakashi Sakamoto 	unsigned int capture, playback;
4279c367c01STakashi Sakamoto 	int i, j;
428c50fb91fSTakashi Sakamoto 	int err;
429c50fb91fSTakashi Sakamoto 
4304bdc495cSTakashi Sakamoto 	for (i = 0; i < MAX_STREAMS; i++) {
4314bdc495cSTakashi Sakamoto 		capture = playback = 0;
4329c367c01STakashi Sakamoto 		for (j = 0; j < SND_DICE_RATE_MODE_COUNT; ++j) {
4339c367c01STakashi Sakamoto 			if (dice->tx_pcm_chs[i][j] > 0)
43469dcf3e4STakashi Sakamoto 				capture = 1;
4359c367c01STakashi Sakamoto 			if (dice->rx_pcm_chs[i][j] > 0)
43669dcf3e4STakashi Sakamoto 				playback = 1;
4379c367c01STakashi Sakamoto 		}
43869dcf3e4STakashi Sakamoto 
4394bdc495cSTakashi Sakamoto 		err = snd_pcm_new(dice->card, "DICE", i, playback, capture,
4404bdc495cSTakashi Sakamoto 				  &pcm);
441c50fb91fSTakashi Sakamoto 		if (err < 0)
442c50fb91fSTakashi Sakamoto 			return err;
443c50fb91fSTakashi Sakamoto 		pcm->private_data = dice;
444c50fb91fSTakashi Sakamoto 		strcpy(pcm->name, dice->card->shortname);
44569dcf3e4STakashi Sakamoto 
44669dcf3e4STakashi Sakamoto 		if (capture > 0)
4474bdc495cSTakashi Sakamoto 			snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
4484bdc495cSTakashi Sakamoto 					&capture_ops);
44969dcf3e4STakashi Sakamoto 
45069dcf3e4STakashi Sakamoto 		if (playback > 0)
4514bdc495cSTakashi Sakamoto 			snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
4524bdc495cSTakashi Sakamoto 					&playback_ops);
453a3f4f2d5STakashi Iwai 
4547641d549STakashi Iwai 		snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC,
455a3f4f2d5STakashi Iwai 					       NULL, 0, 0);
4564bdc495cSTakashi Sakamoto 	}
457c50fb91fSTakashi Sakamoto 
458c50fb91fSTakashi Sakamoto 	return 0;
459c50fb91fSTakashi Sakamoto }
460