Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.15.
  1/*
  2 * dice_pcm.c - a part of driver for DICE based devices
  3 *
  4 * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
  5 * Copyright (c) 2014 Takashi Sakamoto <o-takashi@sakamocchi.jp>
  6 *
  7 * Licensed under the terms of the GNU General Public License, version 2.
  8 */
  9
 10#include "dice.h"
 11
 12static int limit_channels_and_rates(struct snd_dice *dice,
 13				    struct snd_pcm_runtime *runtime,
 14				    enum amdtp_stream_direction dir,
 15				    unsigned int index, unsigned int size)
 16{
 17	struct snd_pcm_hardware *hw = &runtime->hw;
 18	struct amdtp_stream *stream;
 19	unsigned int rate;
 20	__be32 reg;
 21	int err;
 22
 23	/*
 24	 * Retrieve current Multi Bit Linear Audio data channel and limit to
 25	 * it.
 26	 */
 27	if (dir == AMDTP_IN_STREAM) {
 28		stream = &dice->tx_stream[index];
 29		err = snd_dice_transaction_read_tx(dice,
 30				size * index + TX_NUMBER_AUDIO,
 31				&reg, sizeof(reg));
 32	} else {
 33		stream = &dice->rx_stream[index];
 34		err = snd_dice_transaction_read_rx(dice,
 35				size * index + RX_NUMBER_AUDIO,
 36				&reg, sizeof(reg));
 37	}
 38	if (err < 0)
 39		return err;
 40
 41	hw->channels_min = hw->channels_max = be32_to_cpu(reg);
 42
 43	/* Retrieve current sampling transfer frequency and limit to it. */
 44	err = snd_dice_transaction_get_rate(dice, &rate);
 45	if (err < 0)
 46		return err;
 47
 48	hw->rates = snd_pcm_rate_to_rate_bit(rate);
 49	snd_pcm_limit_hw_rates(runtime);
 50
 51	return 0;
 52}
 53
 54static void limit_period_and_buffer(struct snd_pcm_hardware *hw)
 55{
 56	hw->periods_min = 2;			/* SNDRV_PCM_INFO_BATCH */
 57	hw->periods_max = UINT_MAX;
 58
 59	hw->period_bytes_min = 4 * hw->channels_max;    /* byte for a frame */
 60
 61	/* Just to prevent from allocating much pages. */
 62	hw->period_bytes_max = hw->period_bytes_min * 2048;
 63	hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min;
 64}
 65
 66static int init_hw_info(struct snd_dice *dice,
 67			struct snd_pcm_substream *substream)
 68{
 69	struct snd_pcm_runtime *runtime = substream->runtime;
 70	struct snd_pcm_hardware *hw = &runtime->hw;
 71	enum amdtp_stream_direction dir;
 72	struct amdtp_stream *stream;
 73	__be32 reg[2];
 74	unsigned int count, size;
 75	int err;
 76
 77	hw->info = SNDRV_PCM_INFO_MMAP |
 78		   SNDRV_PCM_INFO_MMAP_VALID |
 79		   SNDRV_PCM_INFO_BATCH |
 80		   SNDRV_PCM_INFO_INTERLEAVED |
 81		   SNDRV_PCM_INFO_JOINT_DUPLEX |
 82		   SNDRV_PCM_INFO_BLOCK_TRANSFER;
 83
 84	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
 85		hw->formats = AM824_IN_PCM_FORMAT_BITS;
 86		dir = AMDTP_IN_STREAM;
 87		stream = &dice->tx_stream[substream->pcm->device];
 88		err = snd_dice_transaction_read_tx(dice, TX_NUMBER, reg,
 89						   sizeof(reg));
 90	} else {
 91		hw->formats = AM824_OUT_PCM_FORMAT_BITS;
 92		dir = AMDTP_OUT_STREAM;
 93		stream = &dice->rx_stream[substream->pcm->device];
 94		err = snd_dice_transaction_read_rx(dice, RX_NUMBER, reg,
 95						   sizeof(reg));
 96	}
 97
 98	if (err < 0)
 99		return err;
100
101	count = min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS);
102	if (substream->pcm->device >= count)
103		return -ENXIO;
104
105	size = be32_to_cpu(reg[1]) * 4;
106	err = limit_channels_and_rates(dice, substream->runtime, dir,
107				       substream->pcm->device, size);
108	if (err < 0)
109		return err;
110	limit_period_and_buffer(hw);
111
112	return amdtp_am824_add_pcm_hw_constraints(stream, runtime);
113}
114
115static int pcm_open(struct snd_pcm_substream *substream)
116{
117	struct snd_dice *dice = substream->private_data;
118	int err;
119
120	err = snd_dice_stream_lock_try(dice);
121	if (err < 0)
122		goto end;
123
124	err = init_hw_info(dice, substream);
125	if (err < 0)
126		goto err_locked;
127
128	snd_pcm_set_sync(substream);
129end:
130	return err;
131err_locked:
132	snd_dice_stream_lock_release(dice);
133	return err;
134}
135
136static int pcm_close(struct snd_pcm_substream *substream)
137{
138	struct snd_dice *dice = substream->private_data;
139
140	snd_dice_stream_lock_release(dice);
141
142	return 0;
143}
144
145static int capture_hw_params(struct snd_pcm_substream *substream,
146			     struct snd_pcm_hw_params *hw_params)
147{
148	struct snd_dice *dice = substream->private_data;
149	struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
150	int err;
151
152	err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
153					       params_buffer_bytes(hw_params));
154	if (err < 0)
155		return err;
156
157	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
158		mutex_lock(&dice->mutex);
159		dice->substreams_counter++;
160		mutex_unlock(&dice->mutex);
161	}
162
163	amdtp_am824_set_pcm_format(stream, params_format(hw_params));
164
165	return 0;
166}
167static int playback_hw_params(struct snd_pcm_substream *substream,
168			      struct snd_pcm_hw_params *hw_params)
169{
170	struct snd_dice *dice = substream->private_data;
171	struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
172	int err;
173
174	err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
175					       params_buffer_bytes(hw_params));
176	if (err < 0)
177		return err;
178
179	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
180		mutex_lock(&dice->mutex);
181		dice->substreams_counter++;
182		mutex_unlock(&dice->mutex);
183	}
184
185	amdtp_am824_set_pcm_format(stream, params_format(hw_params));
186
187	return 0;
188}
189
190static int capture_hw_free(struct snd_pcm_substream *substream)
191{
192	struct snd_dice *dice = substream->private_data;
193
194	mutex_lock(&dice->mutex);
195
196	if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
197		dice->substreams_counter--;
198
199	snd_dice_stream_stop_duplex(dice);
200
201	mutex_unlock(&dice->mutex);
202
203	return snd_pcm_lib_free_vmalloc_buffer(substream);
204}
205
206static int playback_hw_free(struct snd_pcm_substream *substream)
207{
208	struct snd_dice *dice = substream->private_data;
209
210	mutex_lock(&dice->mutex);
211
212	if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
213		dice->substreams_counter--;
214
215	snd_dice_stream_stop_duplex(dice);
216
217	mutex_unlock(&dice->mutex);
218
219	return snd_pcm_lib_free_vmalloc_buffer(substream);
220}
221
222static int capture_prepare(struct snd_pcm_substream *substream)
223{
224	struct snd_dice *dice = substream->private_data;
225	struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
226	int err;
227
228	mutex_lock(&dice->mutex);
229	err = snd_dice_stream_start_duplex(dice, substream->runtime->rate);
230	mutex_unlock(&dice->mutex);
231	if (err >= 0)
232		amdtp_stream_pcm_prepare(stream);
233
234	return 0;
235}
236static int playback_prepare(struct snd_pcm_substream *substream)
237{
238	struct snd_dice *dice = substream->private_data;
239	struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
240	int err;
241
242	mutex_lock(&dice->mutex);
243	err = snd_dice_stream_start_duplex(dice, substream->runtime->rate);
244	mutex_unlock(&dice->mutex);
245	if (err >= 0)
246		amdtp_stream_pcm_prepare(stream);
247
248	return err;
249}
250
251static int capture_trigger(struct snd_pcm_substream *substream, int cmd)
252{
253	struct snd_dice *dice = substream->private_data;
254	struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
255
256	switch (cmd) {
257	case SNDRV_PCM_TRIGGER_START:
258		amdtp_stream_pcm_trigger(stream, substream);
259		break;
260	case SNDRV_PCM_TRIGGER_STOP:
261		amdtp_stream_pcm_trigger(stream, NULL);
262		break;
263	default:
264		return -EINVAL;
265	}
266
267	return 0;
268}
269static int playback_trigger(struct snd_pcm_substream *substream, int cmd)
270{
271	struct snd_dice *dice = substream->private_data;
272	struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
273
274	switch (cmd) {
275	case SNDRV_PCM_TRIGGER_START:
276		amdtp_stream_pcm_trigger(stream, substream);
277		break;
278	case SNDRV_PCM_TRIGGER_STOP:
279		amdtp_stream_pcm_trigger(stream, NULL);
280		break;
281	default:
282		return -EINVAL;
283	}
284
285	return 0;
286}
287
288static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream)
289{
290	struct snd_dice *dice = substream->private_data;
291	struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
292
293	return amdtp_stream_pcm_pointer(stream);
294}
295static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream)
296{
297	struct snd_dice *dice = substream->private_data;
298	struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
299
300	return amdtp_stream_pcm_pointer(stream);
301}
302
303int snd_dice_create_pcm(struct snd_dice *dice)
304{
305	static struct snd_pcm_ops capture_ops = {
306		.open      = pcm_open,
307		.close     = pcm_close,
308		.ioctl     = snd_pcm_lib_ioctl,
309		.hw_params = capture_hw_params,
310		.hw_free   = capture_hw_free,
311		.prepare   = capture_prepare,
312		.trigger   = capture_trigger,
313		.pointer   = capture_pointer,
314		.page      = snd_pcm_lib_get_vmalloc_page,
315		.mmap      = snd_pcm_lib_mmap_vmalloc,
316	};
317	static struct snd_pcm_ops playback_ops = {
318		.open      = pcm_open,
319		.close     = pcm_close,
320		.ioctl     = snd_pcm_lib_ioctl,
321		.hw_params = playback_hw_params,
322		.hw_free   = playback_hw_free,
323		.prepare   = playback_prepare,
324		.trigger   = playback_trigger,
325		.pointer   = playback_pointer,
326		.page      = snd_pcm_lib_get_vmalloc_page,
327		.mmap      = snd_pcm_lib_mmap_vmalloc,
328	};
329	__be32 reg;
330	struct snd_pcm *pcm;
331	unsigned int i, max_capture, max_playback, capture, playback;
332	int err;
333
334	/* Check whether PCM substreams are required. */
335	if (dice->force_two_pcms) {
336		max_capture = max_playback = 2;
337	} else {
338		max_capture = max_playback = 0;
339		err = snd_dice_transaction_read_tx(dice, TX_NUMBER, &reg,
340						   sizeof(reg));
341		if (err < 0)
342			return err;
343		max_capture = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS);
344
345		err = snd_dice_transaction_read_rx(dice, RX_NUMBER, &reg,
346						   sizeof(reg));
347		if (err < 0)
348			return err;
349		max_playback = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS);
350	}
351
352	for (i = 0; i < MAX_STREAMS; i++) {
353		capture = playback = 0;
354		if (i < max_capture)
355			capture = 1;
356		if (i < max_playback)
357			playback = 1;
358		if (capture == 0 && playback == 0)
359			break;
360
361		err = snd_pcm_new(dice->card, "DICE", i, playback, capture,
362				  &pcm);
363		if (err < 0)
364			return err;
365		pcm->private_data = dice;
366		strcpy(pcm->name, dice->card->shortname);
367
368		if (capture > 0)
369			snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
370					&capture_ops);
371
372		if (playback > 0)
373			snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
374					&playback_ops);
375	}
376
377	return 0;
378}