ALSA: dice: Change the way to start stream
[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
2c2416c8
TS
70static void limit_channels_and_rates(struct snd_dice *dice,
71 struct snd_pcm_runtime *runtime,
72 unsigned int *pcm_channels)
c50fb91f 73{
2c2416c8
TS
74 struct snd_pcm_hardware *hw = &runtime->hw;
75 unsigned int i, rate, mode;
c50fb91f 76
2c2416c8
TS
77 hw->channels_min = UINT_MAX;
78 hw->channels_max = 0;
c50fb91f
TS
79
80 for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
2c2416c8
TS
81 rate = snd_dice_rates[i];
82 if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
83 continue;
84 hw->rates |= snd_pcm_rate_to_rate_bit(rate);
85
86 if (pcm_channels[mode] == 0)
87 continue;
88 hw->channels_min = min(hw->channels_min, pcm_channels[mode]);
89 hw->channels_max = max(hw->channels_max, pcm_channels[mode]);
c50fb91f 90 }
2c2416c8 91
c50fb91f 92 snd_pcm_limit_hw_rates(runtime);
2c2416c8 93}
c50fb91f 94
2c2416c8
TS
95static void limit_period_and_buffer(struct snd_pcm_hardware *hw)
96{
97 hw->periods_min = 2; /* SNDRV_PCM_INFO_BATCH */
98 hw->periods_max = UINT_MAX;
99
100 hw->period_bytes_min = 4 * hw->channels_max; /* byte for a frame */
101
102 /* Just to prevent from allocating much pages. */
103 hw->period_bytes_max = hw->period_bytes_min * 2048;
104 hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min;
105}
106
107static int init_hw_info(struct snd_dice *dice,
108 struct snd_pcm_substream *substream)
109{
110 struct snd_pcm_runtime *runtime = substream->runtime;
111 struct snd_pcm_hardware *hw = &runtime->hw;
112 int err;
113
114 hw->info = SNDRV_PCM_INFO_MMAP |
115 SNDRV_PCM_INFO_MMAP_VALID |
116 SNDRV_PCM_INFO_BATCH |
117 SNDRV_PCM_INFO_INTERLEAVED |
118 SNDRV_PCM_INFO_BLOCK_TRANSFER;
119 hw->formats = AMDTP_OUT_PCM_FORMAT_BITS;
120
121 limit_channels_and_rates(dice, runtime, dice->rx_channels);
122 limit_period_and_buffer(hw);
c50fb91f
TS
123
124 err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
125 dice_rate_constraint, dice,
126 SNDRV_PCM_HW_PARAM_CHANNELS, -1);
127 if (err < 0)
2c2416c8 128 goto end;
c50fb91f
TS
129 err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
130 dice_channels_constraint, dice,
131 SNDRV_PCM_HW_PARAM_RATE, -1);
132 if (err < 0)
2c2416c8 133 goto end;
c50fb91f
TS
134
135 err = amdtp_stream_add_pcm_hw_constraints(&dice->rx_stream, runtime);
2c2416c8
TS
136end:
137 return err;
138}
c50fb91f 139
2c2416c8
TS
140static int pcm_open(struct snd_pcm_substream *substream)
141{
142 struct snd_dice *dice = substream->private_data;
143 int err;
c50fb91f 144
2c2416c8
TS
145 err = snd_dice_stream_lock_try(dice);
146 if (err < 0)
147 goto end;
148
149 err = init_hw_info(dice, substream);
150 if (err < 0)
151 goto err_locked;
152end:
153 return err;
154err_locked:
c50fb91f 155 snd_dice_stream_lock_release(dice);
c50fb91f
TS
156 return err;
157}
158
159static int pcm_close(struct snd_pcm_substream *substream)
160{
161 struct snd_dice *dice = substream->private_data;
162
163 snd_dice_stream_lock_release(dice);
164
165 return 0;
166}
167
168static int playback_hw_params(struct snd_pcm_substream *substream,
169 struct snd_pcm_hw_params *hw_params)
170{
171 struct snd_dice *dice = substream->private_data;
c50fb91f
TS
172 amdtp_stream_set_pcm_format(&dice->rx_stream,
173 params_format(hw_params));
174
288a8d0c
TS
175 return snd_pcm_lib_alloc_vmalloc_buffer(substream,
176 params_buffer_bytes(hw_params));
c50fb91f
TS
177}
178
179static int playback_hw_free(struct snd_pcm_substream *substream)
180{
181 struct snd_dice *dice = substream->private_data;
182
183 mutex_lock(&dice->mutex);
184 snd_dice_stream_stop(dice);
185 mutex_unlock(&dice->mutex);
186
187 return snd_pcm_lib_free_vmalloc_buffer(substream);
188}
189
190static int playback_prepare(struct snd_pcm_substream *substream)
191{
192 struct snd_dice *dice = substream->private_data;
193 int err;
194
195 mutex_lock(&dice->mutex);
288a8d0c 196 err = snd_dice_stream_start(dice, substream->runtime->rate);
c50fb91f 197 mutex_unlock(&dice->mutex);
288a8d0c
TS
198 if (err >= 0)
199 amdtp_stream_pcm_prepare(&dice->rx_stream);
c50fb91f 200
288a8d0c 201 return err;
c50fb91f
TS
202}
203
204static int playback_trigger(struct snd_pcm_substream *substream, int cmd)
205{
206 struct snd_dice *dice = substream->private_data;
207
208 switch (cmd) {
209 case SNDRV_PCM_TRIGGER_START:
210 amdtp_stream_pcm_trigger(&dice->rx_stream, substream);
211 break;
212 case SNDRV_PCM_TRIGGER_STOP:
213 amdtp_stream_pcm_trigger(&dice->rx_stream, NULL);
214 break;
215 default:
216 return -EINVAL;
217 }
218
219 return 0;
220}
221
222static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream)
223{
224 struct snd_dice *dice = substream->private_data;
225
226 return amdtp_stream_pcm_pointer(&dice->rx_stream);
227}
228
229int snd_dice_create_pcm(struct snd_dice *dice)
230{
231 static struct snd_pcm_ops playback_ops = {
232 .open = pcm_open,
233 .close = pcm_close,
234 .ioctl = snd_pcm_lib_ioctl,
235 .hw_params = playback_hw_params,
236 .hw_free = playback_hw_free,
237 .prepare = playback_prepare,
238 .trigger = playback_trigger,
239 .pointer = playback_pointer,
240 .page = snd_pcm_lib_get_vmalloc_page,
241 .mmap = snd_pcm_lib_mmap_vmalloc,
242 };
243 struct snd_pcm *pcm;
244 int err;
245
246 err = snd_pcm_new(dice->card, "DICE", 0, 1, 0, &pcm);
247 if (err < 0)
248 return err;
249 pcm->private_data = dice;
250 strcpy(pcm->name, dice->card->shortname);
251 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
252
253 return 0;
254}
This page took 0.033385 seconds and 5 git commands to generate.