ALSA: dice: Split proc interface into a file
[deliverable/linux.git] / sound / firewire / dice / dice-pcm.c
CommitLineData
c50fb91f
TS
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 dice_rate_constraint(struct snd_pcm_hw_params *params,
13 struct snd_pcm_hw_rule *rule)
14{
15 struct snd_dice *dice = rule->private;
16
17 const struct snd_interval *c =
18 hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
19 struct snd_interval *r =
20 hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
21 struct snd_interval rates = {
22 .min = UINT_MAX, .max = 0, .integer = 1
23 };
24 unsigned int i, rate, mode, *pcm_channels = dice->rx_channels;
25
26 for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
27 rate = snd_dice_rates[i];
28 if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
29 continue;
30
31 if (!snd_interval_test(c, pcm_channels[mode]))
32 continue;
33
34 rates.min = min(rates.min, rate);
35 rates.max = max(rates.max, rate);
36 }
37
38 return snd_interval_refine(r, &rates);
39}
40
41static int dice_channels_constraint(struct snd_pcm_hw_params *params,
42 struct snd_pcm_hw_rule *rule)
43{
44 struct snd_dice *dice = rule->private;
45
46 const struct snd_interval *r =
47 hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
48 struct snd_interval *c =
49 hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
50 struct snd_interval channels = {
51 .min = UINT_MAX, .max = 0, .integer = 1
52 };
53 unsigned int i, rate, mode, *pcm_channels = dice->rx_channels;
54
55 for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
56 rate = snd_dice_rates[i];
57 if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
58 continue;
59
60 if (!snd_interval_test(r, rate))
61 continue;
62
63 channels.min = min(channels.min, pcm_channels[mode]);
64 channels.max = max(channels.max, pcm_channels[mode]);
65 }
66
67 return snd_interval_refine(c, &channels);
68}
69
70
71static int pcm_open(struct snd_pcm_substream *substream)
72{
73 static const struct snd_pcm_hardware hardware = {
74 .info = SNDRV_PCM_INFO_MMAP |
75 SNDRV_PCM_INFO_MMAP_VALID |
76 SNDRV_PCM_INFO_BATCH |
77 SNDRV_PCM_INFO_INTERLEAVED |
78 SNDRV_PCM_INFO_BLOCK_TRANSFER,
79 .formats = AMDTP_OUT_PCM_FORMAT_BITS,
80 .channels_min = UINT_MAX,
81 .channels_max = 0,
82 .buffer_bytes_max = 16 * 1024 * 1024,
83 .period_bytes_min = 1,
84 .period_bytes_max = UINT_MAX,
85 .periods_min = 1,
86 .periods_max = UINT_MAX,
87 };
88 struct snd_dice *dice = substream->private_data;
89 struct snd_pcm_runtime *runtime = substream->runtime;
90 unsigned int i;
91 int err;
92
93 err = snd_dice_stream_lock_try(dice);
94 if (err < 0)
95 goto error;
96
97 runtime->hw = hardware;
98
99 for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
100 if (dice->clock_caps & (1 << i))
101 runtime->hw.rates |=
102 snd_pcm_rate_to_rate_bit(snd_dice_rates[i]);
103 }
104 snd_pcm_limit_hw_rates(runtime);
105
106 for (i = 0; i < 3; ++i) {
107 if (dice->rx_channels[i]) {
108 runtime->hw.channels_min = min(runtime->hw.channels_min,
109 dice->rx_channels[i]);
110 runtime->hw.channels_max = max(runtime->hw.channels_max,
111 dice->rx_channels[i]);
112 }
113 }
114
115 err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
116 dice_rate_constraint, dice,
117 SNDRV_PCM_HW_PARAM_CHANNELS, -1);
118 if (err < 0)
119 goto err_lock;
120 err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
121 dice_channels_constraint, dice,
122 SNDRV_PCM_HW_PARAM_RATE, -1);
123 if (err < 0)
124 goto err_lock;
125
126 err = amdtp_stream_add_pcm_hw_constraints(&dice->rx_stream, runtime);
127 if (err < 0)
128 goto err_lock;
129
130 return 0;
131
132err_lock:
133 snd_dice_stream_lock_release(dice);
134error:
135 return err;
136}
137
138static int pcm_close(struct snd_pcm_substream *substream)
139{
140 struct snd_dice *dice = substream->private_data;
141
142 snd_dice_stream_lock_release(dice);
143
144 return 0;
145}
146
147static int playback_hw_params(struct snd_pcm_substream *substream,
148 struct snd_pcm_hw_params *hw_params)
149{
150 struct snd_dice *dice = substream->private_data;
151 unsigned int mode, rate, channels, i;
152 int err;
153
154 mutex_lock(&dice->mutex);
155 snd_dice_stream_stop(dice);
156 mutex_unlock(&dice->mutex);
157
158 err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
159 params_buffer_bytes(hw_params));
160 if (err < 0)
161 return err;
162
163 rate = params_rate(hw_params);
164 err = snd_dice_transaction_set_rate(dice, rate);
165 if (err < 0)
166 return err;
167
168 if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
169 return err;
170
171 /*
172 * At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in
173 * one data block of AMDTP packet. Thus sampling transfer frequency is
174 * a half of PCM sampling frequency, i.e. PCM frames at 192.0 kHz are
175 * transferred on AMDTP packets at 96 kHz. Two successive samples of a
176 * channel are stored consecutively in the packet. This quirk is called
177 * as 'Dual Wire'.
178 * For this quirk, blocking mode is required and PCM buffer size should
179 * be aligned to SYT_INTERVAL.
180 */
181 channels = params_channels(hw_params);
182 if (mode > 1) {
183 if (channels > AMDTP_MAX_CHANNELS_FOR_PCM / 2) {
184 err = -ENOSYS;
185 return err;
186 }
187
188 rate /= 2;
189 channels *= 2;
190 dice->rx_stream.double_pcm_frames = true;
191 } else {
192 dice->rx_stream.double_pcm_frames = false;
193 }
194
195 amdtp_stream_set_parameters(&dice->rx_stream, rate, channels,
196 dice->rx_midi_ports[mode]);
197 if (mode > 1) {
198 channels /= 2;
199
200 for (i = 0; i < channels; i++) {
201 dice->rx_stream.pcm_positions[i] = i * 2;
202 dice->rx_stream.pcm_positions[i + channels] = i * 2 + 1;
203 }
204 }
205
206 amdtp_stream_set_pcm_format(&dice->rx_stream,
207 params_format(hw_params));
208
209 return 0;
210}
211
212static int playback_hw_free(struct snd_pcm_substream *substream)
213{
214 struct snd_dice *dice = substream->private_data;
215
216 mutex_lock(&dice->mutex);
217 snd_dice_stream_stop(dice);
218 mutex_unlock(&dice->mutex);
219
220 return snd_pcm_lib_free_vmalloc_buffer(substream);
221}
222
223static int playback_prepare(struct snd_pcm_substream *substream)
224{
225 struct snd_dice *dice = substream->private_data;
226 int err;
227
228 mutex_lock(&dice->mutex);
229
230 if (amdtp_streaming_error(&dice->rx_stream))
231 snd_dice_stream_stop_packets(dice);
232
233 err = snd_dice_stream_start(dice);
234 if (err < 0) {
235 mutex_unlock(&dice->mutex);
236 return err;
237 }
238
239 mutex_unlock(&dice->mutex);
240
241 amdtp_stream_pcm_prepare(&dice->rx_stream);
242
243 return 0;
244}
245
246static int playback_trigger(struct snd_pcm_substream *substream, int cmd)
247{
248 struct snd_dice *dice = substream->private_data;
249
250 switch (cmd) {
251 case SNDRV_PCM_TRIGGER_START:
252 amdtp_stream_pcm_trigger(&dice->rx_stream, substream);
253 break;
254 case SNDRV_PCM_TRIGGER_STOP:
255 amdtp_stream_pcm_trigger(&dice->rx_stream, NULL);
256 break;
257 default:
258 return -EINVAL;
259 }
260
261 return 0;
262}
263
264static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream)
265{
266 struct snd_dice *dice = substream->private_data;
267
268 return amdtp_stream_pcm_pointer(&dice->rx_stream);
269}
270
271int snd_dice_create_pcm(struct snd_dice *dice)
272{
273 static struct snd_pcm_ops playback_ops = {
274 .open = pcm_open,
275 .close = pcm_close,
276 .ioctl = snd_pcm_lib_ioctl,
277 .hw_params = playback_hw_params,
278 .hw_free = playback_hw_free,
279 .prepare = playback_prepare,
280 .trigger = playback_trigger,
281 .pointer = playback_pointer,
282 .page = snd_pcm_lib_get_vmalloc_page,
283 .mmap = snd_pcm_lib_mmap_vmalloc,
284 };
285 struct snd_pcm *pcm;
286 int err;
287
288 err = snd_pcm_new(dice->card, "DICE", 0, 1, 0, &pcm);
289 if (err < 0)
290 return err;
291 pcm->private_data = dice;
292 strcpy(pcm->name, dice->card->shortname);
293 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
294
295 return 0;
296}
This page took 0.034578 seconds and 5 git commands to generate.