~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/firewire/tascam/tascam-stream.c

Version: ~ [ linux-5.7 ] ~ [ linux-5.6.15 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.43 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.125 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.182 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.225 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.225 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.84 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  * tascam-stream.c - a part of driver for TASCAM FireWire series
  3  *
  4  * Copyright (c) 2015 Takashi Sakamoto
  5  *
  6  * Licensed under the terms of the GNU General Public License, version 2.
  7  */
  8 
  9 #include <linux/delay.h>
 10 #include "tascam.h"
 11 
 12 #define CALLBACK_TIMEOUT 500
 13 
 14 static int get_clock(struct snd_tscm *tscm, u32 *data)
 15 {
 16         __be32 reg;
 17         int err;
 18 
 19         err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
 20                                  TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS,
 21                                  &reg, sizeof(reg), 0);
 22         if (err >= 0)
 23                 *data = be32_to_cpu(reg);
 24 
 25         return err;
 26 }
 27 
 28 static int set_clock(struct snd_tscm *tscm, unsigned int rate,
 29                      enum snd_tscm_clock clock)
 30 {
 31         u32 data;
 32         __be32 reg;
 33         int err;
 34 
 35         err = get_clock(tscm, &data);
 36         if (err < 0)
 37                 return err;
 38         data &= 0x0000ffff;
 39 
 40         if (rate > 0) {
 41                 data &= 0x000000ff;
 42                 /* Base rate. */
 43                 if ((rate % 44100) == 0) {
 44                         data |= 0x00000100;
 45                         /* Multiplier. */
 46                         if (rate / 44100 == 2)
 47                                 data |= 0x00008000;
 48                 } else if ((rate % 48000) == 0) {
 49                         data |= 0x00000200;
 50                         /* Multiplier. */
 51                         if (rate / 48000 == 2)
 52                                 data |= 0x00008000;
 53                 } else {
 54                         return -EAGAIN;
 55                 }
 56         }
 57 
 58         if (clock != INT_MAX) {
 59                 data &= 0x0000ff00;
 60                 data |= clock + 1;
 61         }
 62 
 63         reg = cpu_to_be32(data);
 64 
 65         err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
 66                                  TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS,
 67                                  &reg, sizeof(reg), 0);
 68         if (err < 0)
 69                 return err;
 70 
 71         if (data & 0x00008000)
 72                 reg = cpu_to_be32(0x0000001a);
 73         else
 74                 reg = cpu_to_be32(0x0000000d);
 75 
 76         return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
 77                                   TSCM_ADDR_BASE + TSCM_OFFSET_MULTIPLEX_MODE,
 78                                   &reg, sizeof(reg), 0);
 79 }
 80 
 81 int snd_tscm_stream_get_rate(struct snd_tscm *tscm, unsigned int *rate)
 82 {
 83         u32 data = 0x0;
 84         unsigned int trials = 0;
 85         int err;
 86 
 87         while (data == 0x0 || trials++ < 5) {
 88                 err = get_clock(tscm, &data);
 89                 if (err < 0)
 90                         return err;
 91 
 92                 data = (data & 0xff000000) >> 24;
 93         }
 94 
 95         /* Check base rate. */
 96         if ((data & 0x0f) == 0x01)
 97                 *rate = 44100;
 98         else if ((data & 0x0f) == 0x02)
 99                 *rate = 48000;
100         else
101                 return -EAGAIN;
102 
103         /* Check multiplier. */
104         if ((data & 0xf0) == 0x80)
105                 *rate *= 2;
106         else if ((data & 0xf0) != 0x00)
107                 return -EAGAIN;
108 
109         return err;
110 }
111 
112 int snd_tscm_stream_get_clock(struct snd_tscm *tscm, enum snd_tscm_clock *clock)
113 {
114         u32 data;
115         int err;
116 
117         err = get_clock(tscm, &data);
118         if (err < 0)
119                 return err;
120 
121         *clock = ((data & 0x00ff0000) >> 16) - 1;
122         if (*clock < 0 || *clock > SND_TSCM_CLOCK_ADAT)
123                 return -EIO;
124 
125         return 0;
126 }
127 
128 static int enable_data_channels(struct snd_tscm *tscm)
129 {
130         __be32 reg;
131         u32 data;
132         unsigned int i;
133         int err;
134 
135         data = 0;
136         for (i = 0; i < tscm->spec->pcm_capture_analog_channels; ++i)
137                 data |= BIT(i);
138         if (tscm->spec->has_adat)
139                 data |= 0x0000ff00;
140         if (tscm->spec->has_spdif)
141                 data |= 0x00030000;
142 
143         reg = cpu_to_be32(data);
144         err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
145                                  TSCM_ADDR_BASE + TSCM_OFFSET_TX_PCM_CHANNELS,
146                                  &reg, sizeof(reg), 0);
147         if (err < 0)
148                 return err;
149 
150         data = 0;
151         for (i = 0; i < tscm->spec->pcm_playback_analog_channels; ++i)
152                 data |= BIT(i);
153         if (tscm->spec->has_adat)
154                 data |= 0x0000ff00;
155         if (tscm->spec->has_spdif)
156                 data |= 0x00030000;
157 
158         reg = cpu_to_be32(data);
159         return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
160                                   TSCM_ADDR_BASE + TSCM_OFFSET_RX_PCM_CHANNELS,
161                                   &reg, sizeof(reg), 0);
162 }
163 
164 static int set_stream_formats(struct snd_tscm *tscm, unsigned int rate)
165 {
166         __be32 reg;
167         int err;
168 
169         /* Set an option for unknown purpose. */
170         reg = cpu_to_be32(0x00200000);
171         err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
172                                  TSCM_ADDR_BASE + TSCM_OFFSET_SET_OPTION,
173                                  &reg, sizeof(reg), 0);
174         if (err < 0)
175                 return err;
176 
177         err = enable_data_channels(tscm);
178         if (err < 0)
179                 return err;
180 
181         return set_clock(tscm, rate, INT_MAX);
182 }
183 
184 static void finish_session(struct snd_tscm *tscm)
185 {
186         __be32 reg;
187 
188         reg = 0;
189         snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
190                            TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING,
191                            &reg, sizeof(reg), 0);
192 
193         reg = 0;
194         snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
195                            TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_ON,
196                            &reg, sizeof(reg), 0);
197 
198 }
199 
200 static int begin_session(struct snd_tscm *tscm)
201 {
202         __be32 reg;
203         int err;
204 
205         reg = cpu_to_be32(0x00000001);
206         err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
207                                  TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING,
208                                  &reg, sizeof(reg), 0);
209         if (err < 0)
210                 return err;
211 
212         reg = cpu_to_be32(0x00000001);
213         err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
214                                  TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_ON,
215                                  &reg, sizeof(reg), 0);
216         if (err < 0)
217                 return err;
218 
219         /* Set an option for unknown purpose. */
220         reg = cpu_to_be32(0x00002000);
221         err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
222                                  TSCM_ADDR_BASE + TSCM_OFFSET_SET_OPTION,
223                                  &reg, sizeof(reg), 0);
224         if (err < 0)
225                 return err;
226 
227         /* Start multiplexing PCM samples on packets. */
228         reg = cpu_to_be32(0x00000001);
229         return snd_fw_transaction(tscm->unit,
230                                   TCODE_WRITE_QUADLET_REQUEST,
231                                   TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_ON,
232                                   &reg, sizeof(reg), 0);
233 }
234 
235 static void release_resources(struct snd_tscm *tscm)
236 {
237         __be32 reg;
238 
239         /* Unregister channels. */
240         reg = cpu_to_be32(0x00000000);
241         snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
242                            TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH,
243                            &reg, sizeof(reg), 0);
244         reg = cpu_to_be32(0x00000000);
245         snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
246                            TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN,
247                            &reg, sizeof(reg), 0);
248         reg = cpu_to_be32(0x00000000);
249         snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
250                            TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH,
251                            &reg, sizeof(reg), 0);
252 
253         /* Release isochronous resources. */
254         fw_iso_resources_free(&tscm->tx_resources);
255         fw_iso_resources_free(&tscm->rx_resources);
256 }
257 
258 static int keep_resources(struct snd_tscm *tscm, unsigned int rate)
259 {
260         __be32 reg;
261         int err;
262 
263         /* Keep resources for in-stream. */
264         err = amdtp_tscm_set_parameters(&tscm->tx_stream, rate);
265         if (err < 0)
266                 return err;
267         err = fw_iso_resources_allocate(&tscm->tx_resources,
268                         amdtp_stream_get_max_payload(&tscm->tx_stream),
269                         fw_parent_device(tscm->unit)->max_speed);
270         if (err < 0)
271                 goto error;
272 
273         /* Keep resources for out-stream. */
274         err = amdtp_tscm_set_parameters(&tscm->rx_stream, rate);
275         if (err < 0)
276                 return err;
277         err = fw_iso_resources_allocate(&tscm->rx_resources,
278                         amdtp_stream_get_max_payload(&tscm->rx_stream),
279                         fw_parent_device(tscm->unit)->max_speed);
280         if (err < 0)
281                 return err;
282 
283         /* Register the isochronous channel for transmitting stream. */
284         reg = cpu_to_be32(tscm->tx_resources.channel);
285         err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
286                                  TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH,
287                                  &reg, sizeof(reg), 0);
288         if (err < 0)
289                 goto error;
290 
291         /* Unknown */
292         reg = cpu_to_be32(0x00000002);
293         err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
294                                  TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN,
295                                  &reg, sizeof(reg), 0);
296         if (err < 0)
297                 goto error;
298 
299         /* Register the isochronous channel for receiving stream. */
300         reg = cpu_to_be32(tscm->rx_resources.channel);
301         err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
302                                  TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH,
303                                  &reg, sizeof(reg), 0);
304         if (err < 0)
305                 goto error;
306 
307         return 0;
308 error:
309         release_resources(tscm);
310         return err;
311 }
312 
313 int snd_tscm_stream_init_duplex(struct snd_tscm *tscm)
314 {
315         unsigned int pcm_channels;
316         int err;
317 
318         /* For out-stream. */
319         err = fw_iso_resources_init(&tscm->rx_resources, tscm->unit);
320         if (err < 0)
321                 return err;
322         pcm_channels = tscm->spec->pcm_playback_analog_channels;
323         if (tscm->spec->has_adat)
324                 pcm_channels += 8;
325         if (tscm->spec->has_spdif)
326                 pcm_channels += 2;
327         err = amdtp_tscm_init(&tscm->rx_stream, tscm->unit, AMDTP_OUT_STREAM,
328                               pcm_channels);
329         if (err < 0)
330                 return err;
331 
332         /* For in-stream. */
333         err = fw_iso_resources_init(&tscm->tx_resources, tscm->unit);
334         if (err < 0)
335                 return err;
336         pcm_channels = tscm->spec->pcm_capture_analog_channels;
337         if (tscm->spec->has_adat)
338                 pcm_channels += 8;
339         if (tscm->spec->has_spdif)
340                 pcm_channels += 2;
341         err = amdtp_tscm_init(&tscm->tx_stream, tscm->unit, AMDTP_IN_STREAM,
342                               pcm_channels);
343         if (err < 0)
344                 amdtp_stream_destroy(&tscm->rx_stream);
345 
346         return err;
347 }
348 
349 /* At bus reset, streaming is stopped and some registers are clear. */
350 void snd_tscm_stream_update_duplex(struct snd_tscm *tscm)
351 {
352         amdtp_stream_pcm_abort(&tscm->tx_stream);
353         amdtp_stream_stop(&tscm->tx_stream);
354 
355         amdtp_stream_pcm_abort(&tscm->rx_stream);
356         amdtp_stream_stop(&tscm->rx_stream);
357 }
358 
359 /*
360  * This function should be called before starting streams or after stopping
361  * streams.
362  */
363 void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm)
364 {
365         amdtp_stream_destroy(&tscm->rx_stream);
366         amdtp_stream_destroy(&tscm->tx_stream);
367 
368         fw_iso_resources_destroy(&tscm->rx_resources);
369         fw_iso_resources_destroy(&tscm->tx_resources);
370 }
371 
372 int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
373 {
374         unsigned int curr_rate;
375         int err;
376 
377         if (tscm->substreams_counter == 0)
378                 return 0;
379 
380         err = snd_tscm_stream_get_rate(tscm, &curr_rate);
381         if (err < 0)
382                 return err;
383         if (curr_rate != rate ||
384             amdtp_streaming_error(&tscm->rx_stream) ||
385             amdtp_streaming_error(&tscm->tx_stream)) {
386                 finish_session(tscm);
387 
388                 amdtp_stream_stop(&tscm->rx_stream);
389                 amdtp_stream_stop(&tscm->tx_stream);
390 
391                 release_resources(tscm);
392         }
393 
394         if (!amdtp_stream_running(&tscm->rx_stream)) {
395                 err = keep_resources(tscm, rate);
396                 if (err < 0)
397                         goto error;
398 
399                 err = set_stream_formats(tscm, rate);
400                 if (err < 0)
401                         goto error;
402 
403                 err = begin_session(tscm);
404                 if (err < 0)
405                         goto error;
406 
407                 err = amdtp_stream_start(&tscm->rx_stream,
408                                 tscm->rx_resources.channel,
409                                 fw_parent_device(tscm->unit)->max_speed);
410                 if (err < 0)
411                         goto error;
412 
413                 if (!amdtp_stream_wait_callback(&tscm->rx_stream,
414                                                 CALLBACK_TIMEOUT)) {
415                         err = -ETIMEDOUT;
416                         goto error;
417                 }
418         }
419 
420         if (!amdtp_stream_running(&tscm->tx_stream)) {
421                 err = amdtp_stream_start(&tscm->tx_stream,
422                                 tscm->tx_resources.channel,
423                                 fw_parent_device(tscm->unit)->max_speed);
424                 if (err < 0)
425                         goto error;
426 
427                 if (!amdtp_stream_wait_callback(&tscm->tx_stream,
428                                                 CALLBACK_TIMEOUT)) {
429                         err = -ETIMEDOUT;
430                         goto error;
431                 }
432         }
433 
434         return 0;
435 error:
436         amdtp_stream_stop(&tscm->rx_stream);
437         amdtp_stream_stop(&tscm->tx_stream);
438 
439         finish_session(tscm);
440         release_resources(tscm);
441 
442         return err;
443 }
444 
445 void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm)
446 {
447         if (tscm->substreams_counter > 0)
448                 return;
449 
450         amdtp_stream_stop(&tscm->tx_stream);
451         amdtp_stream_stop(&tscm->rx_stream);
452 
453         finish_session(tscm);
454         release_resources(tscm);
455 }
456 
457 void snd_tscm_stream_lock_changed(struct snd_tscm *tscm)
458 {
459         tscm->dev_lock_changed = true;
460         wake_up(&tscm->hwdep_wait);
461 }
462 
463 int snd_tscm_stream_lock_try(struct snd_tscm *tscm)
464 {
465         int err;
466 
467         spin_lock_irq(&tscm->lock);
468 
469         /* user land lock this */
470         if (tscm->dev_lock_count < 0) {
471                 err = -EBUSY;
472                 goto end;
473         }
474 
475         /* this is the first time */
476         if (tscm->dev_lock_count++ == 0)
477                 snd_tscm_stream_lock_changed(tscm);
478         err = 0;
479 end:
480         spin_unlock_irq(&tscm->lock);
481         return err;
482 }
483 
484 void snd_tscm_stream_lock_release(struct snd_tscm *tscm)
485 {
486         spin_lock_irq(&tscm->lock);
487 
488         if (WARN_ON(tscm->dev_lock_count <= 0))
489                 goto end;
490         if (--tscm->dev_lock_count == 0)
491                 snd_tscm_stream_lock_changed(tscm);
492 end:
493         spin_unlock_irq(&tscm->lock);
494 }
495 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp