ALSA: dice: limit to current sampling transfer frequency
[sfrench/cifs-2.6.git] / sound / firewire / dice / dice-pcm.c
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
12 static int limit_channels_and_rates(struct snd_dice *dice,
13                                     struct snd_pcm_runtime *runtime,
14                                     struct amdtp_stream *stream)
15 {
16         struct snd_pcm_hardware *hw = &runtime->hw;
17         unsigned int rate;
18         __be32 reg[2];
19         int err;
20
21         /*
22          * Retrieve current Multi Bit Linear Audio data channel and limit to
23          * it.
24          */
25         if (stream == &dice->tx_stream) {
26                 err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO,
27                                                    reg, sizeof(reg));
28         } else {
29                 err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO,
30                                                    reg, sizeof(reg));
31         }
32         if (err < 0)
33                 return err;
34
35         hw->channels_min = hw->channels_max = be32_to_cpu(reg[0]);
36
37         /* Retrieve current sampling transfer frequency and limit to it. */
38         err = snd_dice_transaction_get_rate(dice, &rate);
39         if (err < 0)
40                 return err;
41
42         hw->rates = snd_pcm_rate_to_rate_bit(rate);
43         snd_pcm_limit_hw_rates(runtime);
44
45         return 0;
46 }
47
48 static void limit_period_and_buffer(struct snd_pcm_hardware *hw)
49 {
50         hw->periods_min = 2;                    /* SNDRV_PCM_INFO_BATCH */
51         hw->periods_max = UINT_MAX;
52
53         hw->period_bytes_min = 4 * hw->channels_max;    /* byte for a frame */
54
55         /* Just to prevent from allocating much pages. */
56         hw->period_bytes_max = hw->period_bytes_min * 2048;
57         hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min;
58 }
59
60 static int init_hw_info(struct snd_dice *dice,
61                         struct snd_pcm_substream *substream)
62 {
63         struct snd_pcm_runtime *runtime = substream->runtime;
64         struct snd_pcm_hardware *hw = &runtime->hw;
65         struct amdtp_stream *stream;
66         int err;
67
68         hw->info = SNDRV_PCM_INFO_MMAP |
69                    SNDRV_PCM_INFO_MMAP_VALID |
70                    SNDRV_PCM_INFO_BATCH |
71                    SNDRV_PCM_INFO_INTERLEAVED |
72                    SNDRV_PCM_INFO_JOINT_DUPLEX |
73                    SNDRV_PCM_INFO_BLOCK_TRANSFER;
74
75         if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
76                 hw->formats = AM824_IN_PCM_FORMAT_BITS;
77                 stream = &dice->tx_stream;
78         } else {
79                 hw->formats = AM824_OUT_PCM_FORMAT_BITS;
80                 stream = &dice->rx_stream;
81         }
82
83         err = limit_channels_and_rates(dice, runtime, stream);
84         if (err < 0)
85                 return err;
86         limit_period_and_buffer(hw);
87
88         return amdtp_am824_add_pcm_hw_constraints(stream, runtime);
89 }
90
91 static int pcm_open(struct snd_pcm_substream *substream)
92 {
93         struct snd_dice *dice = substream->private_data;
94         int err;
95
96         err = snd_dice_stream_lock_try(dice);
97         if (err < 0)
98                 goto end;
99
100         err = init_hw_info(dice, substream);
101         if (err < 0)
102                 goto err_locked;
103
104         snd_pcm_set_sync(substream);
105 end:
106         return err;
107 err_locked:
108         snd_dice_stream_lock_release(dice);
109         return err;
110 }
111
112 static int pcm_close(struct snd_pcm_substream *substream)
113 {
114         struct snd_dice *dice = substream->private_data;
115
116         snd_dice_stream_lock_release(dice);
117
118         return 0;
119 }
120
121 static int capture_hw_params(struct snd_pcm_substream *substream,
122                              struct snd_pcm_hw_params *hw_params)
123 {
124         struct snd_dice *dice = substream->private_data;
125         int err;
126
127         err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
128                                                params_buffer_bytes(hw_params));
129         if (err < 0)
130                 return err;
131
132         if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
133                 mutex_lock(&dice->mutex);
134                 dice->substreams_counter++;
135                 mutex_unlock(&dice->mutex);
136         }
137
138         amdtp_am824_set_pcm_format(&dice->tx_stream, params_format(hw_params));
139
140         return 0;
141 }
142 static int playback_hw_params(struct snd_pcm_substream *substream,
143                               struct snd_pcm_hw_params *hw_params)
144 {
145         struct snd_dice *dice = substream->private_data;
146         int err;
147
148         err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
149                                                params_buffer_bytes(hw_params));
150         if (err < 0)
151                 return err;
152
153         if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
154                 mutex_lock(&dice->mutex);
155                 dice->substreams_counter++;
156                 mutex_unlock(&dice->mutex);
157         }
158
159         amdtp_am824_set_pcm_format(&dice->rx_stream, params_format(hw_params));
160
161         return 0;
162 }
163
164 static int capture_hw_free(struct snd_pcm_substream *substream)
165 {
166         struct snd_dice *dice = substream->private_data;
167
168         mutex_lock(&dice->mutex);
169
170         if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
171                 dice->substreams_counter--;
172
173         snd_dice_stream_stop_duplex(dice);
174
175         mutex_unlock(&dice->mutex);
176
177         return snd_pcm_lib_free_vmalloc_buffer(substream);
178 }
179
180 static int playback_hw_free(struct snd_pcm_substream *substream)
181 {
182         struct snd_dice *dice = substream->private_data;
183
184         mutex_lock(&dice->mutex);
185
186         if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
187                 dice->substreams_counter--;
188
189         snd_dice_stream_stop_duplex(dice);
190
191         mutex_unlock(&dice->mutex);
192
193         return snd_pcm_lib_free_vmalloc_buffer(substream);
194 }
195
196 static int capture_prepare(struct snd_pcm_substream *substream)
197 {
198         struct snd_dice *dice = substream->private_data;
199         int err;
200
201         mutex_lock(&dice->mutex);
202         err = snd_dice_stream_start_duplex(dice, substream->runtime->rate);
203         mutex_unlock(&dice->mutex);
204         if (err >= 0)
205                 amdtp_stream_pcm_prepare(&dice->tx_stream);
206
207         return 0;
208 }
209 static int playback_prepare(struct snd_pcm_substream *substream)
210 {
211         struct snd_dice *dice = substream->private_data;
212         int err;
213
214         mutex_lock(&dice->mutex);
215         err = snd_dice_stream_start_duplex(dice, substream->runtime->rate);
216         mutex_unlock(&dice->mutex);
217         if (err >= 0)
218                 amdtp_stream_pcm_prepare(&dice->rx_stream);
219
220         return err;
221 }
222
223 static int capture_trigger(struct snd_pcm_substream *substream, int cmd)
224 {
225         struct snd_dice *dice = substream->private_data;
226
227         switch (cmd) {
228         case SNDRV_PCM_TRIGGER_START:
229                 amdtp_stream_pcm_trigger(&dice->tx_stream, substream);
230                 break;
231         case SNDRV_PCM_TRIGGER_STOP:
232                 amdtp_stream_pcm_trigger(&dice->tx_stream, NULL);
233                 break;
234         default:
235                 return -EINVAL;
236         }
237
238         return 0;
239 }
240 static int playback_trigger(struct snd_pcm_substream *substream, int cmd)
241 {
242         struct snd_dice *dice = substream->private_data;
243
244         switch (cmd) {
245         case SNDRV_PCM_TRIGGER_START:
246                 amdtp_stream_pcm_trigger(&dice->rx_stream, substream);
247                 break;
248         case SNDRV_PCM_TRIGGER_STOP:
249                 amdtp_stream_pcm_trigger(&dice->rx_stream, NULL);
250                 break;
251         default:
252                 return -EINVAL;
253         }
254
255         return 0;
256 }
257
258 static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream)
259 {
260         struct snd_dice *dice = substream->private_data;
261
262         return amdtp_stream_pcm_pointer(&dice->tx_stream);
263 }
264 static 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
271 int snd_dice_create_pcm(struct snd_dice *dice)
272 {
273         static struct snd_pcm_ops capture_ops = {
274                 .open      = pcm_open,
275                 .close     = pcm_close,
276                 .ioctl     = snd_pcm_lib_ioctl,
277                 .hw_params = capture_hw_params,
278                 .hw_free   = capture_hw_free,
279                 .prepare   = capture_prepare,
280                 .trigger   = capture_trigger,
281                 .pointer   = capture_pointer,
282                 .page      = snd_pcm_lib_get_vmalloc_page,
283                 .mmap      = snd_pcm_lib_mmap_vmalloc,
284         };
285         static struct snd_pcm_ops playback_ops = {
286                 .open      = pcm_open,
287                 .close     = pcm_close,
288                 .ioctl     = snd_pcm_lib_ioctl,
289                 .hw_params = playback_hw_params,
290                 .hw_free   = playback_hw_free,
291                 .prepare   = playback_prepare,
292                 .trigger   = playback_trigger,
293                 .pointer   = playback_pointer,
294                 .page      = snd_pcm_lib_get_vmalloc_page,
295                 .mmap      = snd_pcm_lib_mmap_vmalloc,
296         };
297         struct snd_pcm *pcm;
298         unsigned int i, capture, playback;
299         int err;
300
301         capture = playback = 0;
302         for (i = 0; i < 3; i++) {
303                 if (dice->tx_channels[i] > 0)
304                         capture = 1;
305                 if (dice->rx_channels[i] > 0)
306                         playback = 1;
307         }
308
309         err = snd_pcm_new(dice->card, "DICE", 0, playback, capture, &pcm);
310         if (err < 0)
311                 return err;
312         pcm->private_data = dice;
313         strcpy(pcm->name, dice->card->shortname);
314
315         if (capture > 0)
316                 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
317
318         if (playback > 0)
319                 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
320
321         return 0;
322 }