1
2
3
4
5
6
7
8
9#include "ff.h"
10
11static inline unsigned int get_multiplier_mode_with_index(unsigned int index)
12{
13 return ((int)index - 1) / 2;
14}
15
16static int hw_rule_rate(struct snd_pcm_hw_params *params,
17 struct snd_pcm_hw_rule *rule)
18{
19 const unsigned int *pcm_channels = rule->private;
20 struct snd_interval *r =
21 hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
22 const struct snd_interval *c =
23 hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
24 struct snd_interval t = {
25 .min = UINT_MAX, .max = 0, .integer = 1
26 };
27 unsigned int i, mode;
28
29 for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) {
30 mode = get_multiplier_mode_with_index(i);
31 if (!snd_interval_test(c, pcm_channels[mode]))
32 continue;
33
34 t.min = min(t.min, amdtp_rate_table[i]);
35 t.max = max(t.max, amdtp_rate_table[i]);
36 }
37
38 return snd_interval_refine(r, &t);
39}
40
41static int hw_rule_channels(struct snd_pcm_hw_params *params,
42 struct snd_pcm_hw_rule *rule)
43{
44 const unsigned int *pcm_channels = rule->private;
45 struct snd_interval *c =
46 hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
47 const struct snd_interval *r =
48 hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
49 struct snd_interval t = {
50 .min = UINT_MAX, .max = 0, .integer = 1
51 };
52 unsigned int i, mode;
53
54 for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) {
55 mode = get_multiplier_mode_with_index(i);
56 if (!snd_interval_test(r, amdtp_rate_table[i]))
57 continue;
58
59 t.min = min(t.min, pcm_channels[mode]);
60 t.max = max(t.max, pcm_channels[mode]);
61 }
62
63 return snd_interval_refine(c, &t);
64}
65
66static void limit_channels_and_rates(struct snd_pcm_hardware *hw,
67 const unsigned int *pcm_channels)
68{
69 unsigned int mode;
70 unsigned int rate, channels;
71 int i;
72
73 hw->channels_min = UINT_MAX;
74 hw->channels_max = 0;
75 hw->rate_min = UINT_MAX;
76 hw->rate_max = 0;
77
78 for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) {
79 mode = get_multiplier_mode_with_index(i);
80
81 channels = pcm_channels[mode];
82 if (pcm_channels[mode] == 0)
83 continue;
84 hw->channels_min = min(hw->channels_min, channels);
85 hw->channels_max = max(hw->channels_max, channels);
86
87 rate = amdtp_rate_table[i];
88 hw->rates |= snd_pcm_rate_to_rate_bit(rate);
89 hw->rate_min = min(hw->rate_min, rate);
90 hw->rate_max = max(hw->rate_max, rate);
91 }
92}
93
94static int pcm_init_hw_params(struct snd_ff *ff,
95 struct snd_pcm_substream *substream)
96{
97 struct snd_pcm_runtime *runtime = substream->runtime;
98 struct amdtp_stream *s;
99 const unsigned int *pcm_channels;
100 int err;
101
102 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
103 runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
104 s = &ff->tx_stream;
105 pcm_channels = ff->spec->pcm_capture_channels;
106 } else {
107 runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
108 s = &ff->rx_stream;
109 pcm_channels = ff->spec->pcm_playback_channels;
110 }
111
112 limit_channels_and_rates(&runtime->hw, pcm_channels);
113
114 err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
115 hw_rule_channels, (void *)pcm_channels,
116 SNDRV_PCM_HW_PARAM_RATE, -1);
117 if (err < 0)
118 return err;
119
120 err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
121 hw_rule_rate, (void *)pcm_channels,
122 SNDRV_PCM_HW_PARAM_CHANNELS, -1);
123 if (err < 0)
124 return err;
125
126 return amdtp_ff_add_pcm_hw_constraints(s, runtime);
127}
128
129static int pcm_open(struct snd_pcm_substream *substream)
130{
131 struct snd_ff *ff = substream->private_data;
132 unsigned int rate;
133 enum snd_ff_clock_src src;
134 int i, err;
135
136 err = snd_ff_stream_lock_try(ff);
137 if (err < 0)
138 return err;
139
140 err = pcm_init_hw_params(ff, substream);
141 if (err < 0)
142 goto release_lock;
143
144 err = ff->spec->protocol->get_clock(ff, &rate, &src);
145 if (err < 0)
146 goto release_lock;
147
148 if (src != SND_FF_CLOCK_SRC_INTERNAL) {
149 for (i = 0; i < CIP_SFC_COUNT; ++i) {
150 if (amdtp_rate_table[i] == rate)
151 break;
152 }
153
154
155
156
157 if (i >= CIP_SFC_COUNT) {
158 err = -EIO;
159 goto release_lock;
160 }
161
162 substream->runtime->hw.rate_min = rate;
163 substream->runtime->hw.rate_max = rate;
164 } else {
165 if (amdtp_stream_pcm_running(&ff->rx_stream) ||
166 amdtp_stream_pcm_running(&ff->tx_stream)) {
167 rate = amdtp_rate_table[ff->rx_stream.sfc];
168 substream->runtime->hw.rate_min = rate;
169 substream->runtime->hw.rate_max = rate;
170 }
171 }
172
173 snd_pcm_set_sync(substream);
174
175 return 0;
176
177release_lock:
178 snd_ff_stream_lock_release(ff);
179 return err;
180}
181
182static int pcm_close(struct snd_pcm_substream *substream)
183{
184 struct snd_ff *ff = substream->private_data;
185
186 snd_ff_stream_lock_release(ff);
187
188 return 0;
189}
190
191static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
192 struct snd_pcm_hw_params *hw_params)
193{
194 struct snd_ff *ff = substream->private_data;
195 int err;
196
197 err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
198 params_buffer_bytes(hw_params));
199 if (err < 0)
200 return err;
201
202 if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
203 mutex_lock(&ff->mutex);
204 ff->substreams_counter++;
205 mutex_unlock(&ff->mutex);
206 }
207
208 return 0;
209}
210
211static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
212 struct snd_pcm_hw_params *hw_params)
213{
214 struct snd_ff *ff = substream->private_data;
215 int err;
216
217 err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
218 params_buffer_bytes(hw_params));
219 if (err < 0)
220 return err;
221
222 if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
223 mutex_lock(&ff->mutex);
224 ff->substreams_counter++;
225 mutex_unlock(&ff->mutex);
226 }
227
228 return 0;
229}
230
231static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
232{
233 struct snd_ff *ff = substream->private_data;
234
235 mutex_lock(&ff->mutex);
236
237 if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
238 ff->substreams_counter--;
239
240 snd_ff_stream_stop_duplex(ff);
241
242 mutex_unlock(&ff->mutex);
243
244 return snd_pcm_lib_free_vmalloc_buffer(substream);
245}
246
247static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
248{
249 struct snd_ff *ff = substream->private_data;
250
251 mutex_lock(&ff->mutex);
252
253 if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
254 ff->substreams_counter--;
255
256 snd_ff_stream_stop_duplex(ff);
257
258 mutex_unlock(&ff->mutex);
259
260 return snd_pcm_lib_free_vmalloc_buffer(substream);
261}
262
263static int pcm_capture_prepare(struct snd_pcm_substream *substream)
264{
265 struct snd_ff *ff = substream->private_data;
266 struct snd_pcm_runtime *runtime = substream->runtime;
267 int err;
268
269 mutex_lock(&ff->mutex);
270
271 err = snd_ff_stream_start_duplex(ff, runtime->rate);
272 if (err >= 0)
273 amdtp_stream_pcm_prepare(&ff->tx_stream);
274
275 mutex_unlock(&ff->mutex);
276
277 return err;
278}
279
280static int pcm_playback_prepare(struct snd_pcm_substream *substream)
281{
282 struct snd_ff *ff = substream->private_data;
283 struct snd_pcm_runtime *runtime = substream->runtime;
284 int err;
285
286 mutex_lock(&ff->mutex);
287
288 err = snd_ff_stream_start_duplex(ff, runtime->rate);
289 if (err >= 0)
290 amdtp_stream_pcm_prepare(&ff->rx_stream);
291
292 mutex_unlock(&ff->mutex);
293
294 return err;
295}
296
297static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
298{
299 struct snd_ff *ff = substream->private_data;
300
301 switch (cmd) {
302 case SNDRV_PCM_TRIGGER_START:
303 amdtp_stream_pcm_trigger(&ff->tx_stream, substream);
304 break;
305 case SNDRV_PCM_TRIGGER_STOP:
306 amdtp_stream_pcm_trigger(&ff->tx_stream, NULL);
307 break;
308 default:
309 return -EINVAL;
310 }
311
312 return 0;
313}
314
315static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
316{
317 struct snd_ff *ff = substream->private_data;
318
319 switch (cmd) {
320 case SNDRV_PCM_TRIGGER_START:
321 amdtp_stream_pcm_trigger(&ff->rx_stream, substream);
322 break;
323 case SNDRV_PCM_TRIGGER_STOP:
324 amdtp_stream_pcm_trigger(&ff->rx_stream, NULL);
325 break;
326 default:
327 return -EINVAL;
328 }
329
330 return 0;
331}
332
333static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
334{
335 struct snd_ff *ff = sbstrm->private_data;
336
337 return amdtp_stream_pcm_pointer(&ff->tx_stream);
338}
339
340static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
341{
342 struct snd_ff *ff = sbstrm->private_data;
343
344 return amdtp_stream_pcm_pointer(&ff->rx_stream);
345}
346
347static int pcm_capture_ack(struct snd_pcm_substream *substream)
348{
349 struct snd_ff *ff = substream->private_data;
350
351 return amdtp_stream_pcm_ack(&ff->tx_stream);
352}
353
354static int pcm_playback_ack(struct snd_pcm_substream *substream)
355{
356 struct snd_ff *ff = substream->private_data;
357
358 return amdtp_stream_pcm_ack(&ff->rx_stream);
359}
360
361int snd_ff_create_pcm_devices(struct snd_ff *ff)
362{
363 static const struct snd_pcm_ops pcm_capture_ops = {
364 .open = pcm_open,
365 .close = pcm_close,
366 .ioctl = snd_pcm_lib_ioctl,
367 .hw_params = pcm_capture_hw_params,
368 .hw_free = pcm_capture_hw_free,
369 .prepare = pcm_capture_prepare,
370 .trigger = pcm_capture_trigger,
371 .pointer = pcm_capture_pointer,
372 .ack = pcm_capture_ack,
373 .page = snd_pcm_lib_get_vmalloc_page,
374 };
375 static const struct snd_pcm_ops pcm_playback_ops = {
376 .open = pcm_open,
377 .close = pcm_close,
378 .ioctl = snd_pcm_lib_ioctl,
379 .hw_params = pcm_playback_hw_params,
380 .hw_free = pcm_playback_hw_free,
381 .prepare = pcm_playback_prepare,
382 .trigger = pcm_playback_trigger,
383 .pointer = pcm_playback_pointer,
384 .ack = pcm_playback_ack,
385 .page = snd_pcm_lib_get_vmalloc_page,
386 .mmap = snd_pcm_lib_mmap_vmalloc,
387 };
388 struct snd_pcm *pcm;
389 int err;
390
391 err = snd_pcm_new(ff->card, ff->card->driver, 0, 1, 1, &pcm);
392 if (err < 0)
393 return err;
394
395 pcm->private_data = ff;
396 snprintf(pcm->name, sizeof(pcm->name),
397 "%s PCM", ff->card->shortname);
398 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops);
399 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
400
401 return 0;
402}
403