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

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

Version: ~ [ linux-6.11-rc3 ] ~ [ linux-6.10.4 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.45 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.104 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.164 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.223 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.281 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.319 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ 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.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-only
  2 /*
  3  * digi00x-stream.c - a part of driver for Digidesign Digi 002/003 family
  4  *
  5  * Copyright (c) 2014-2015 Takashi Sakamoto
  6  */
  7 
  8 #include "digi00x.h"
  9 
 10 #define READY_TIMEOUT_MS        200
 11 
 12 const unsigned int snd_dg00x_stream_rates[SND_DG00X_RATE_COUNT] = {
 13         [SND_DG00X_RATE_44100] = 44100,
 14         [SND_DG00X_RATE_48000] = 48000,
 15         [SND_DG00X_RATE_88200] = 88200,
 16         [SND_DG00X_RATE_96000] = 96000,
 17 };
 18 
 19 /* Multi Bit Linear Audio data channels for each sampling transfer frequency. */
 20 const unsigned int
 21 snd_dg00x_stream_pcm_channels[SND_DG00X_RATE_COUNT] = {
 22         /* Analog/ADAT/SPDIF */
 23         [SND_DG00X_RATE_44100] = (8 + 8 + 2),
 24         [SND_DG00X_RATE_48000] = (8 + 8 + 2),
 25         /* Analog/SPDIF */
 26         [SND_DG00X_RATE_88200] = (8 + 2),
 27         [SND_DG00X_RATE_96000] = (8 + 2),
 28 };
 29 
 30 int snd_dg00x_stream_get_local_rate(struct snd_dg00x *dg00x, unsigned int *rate)
 31 {
 32         u32 data;
 33         __be32 reg;
 34         int err;
 35 
 36         err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
 37                                  DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE,
 38                                  &reg, sizeof(reg), 0);
 39         if (err < 0)
 40                 return err;
 41 
 42         data = be32_to_cpu(reg) & 0x0f;
 43         if (data < ARRAY_SIZE(snd_dg00x_stream_rates))
 44                 *rate = snd_dg00x_stream_rates[data];
 45         else
 46                 err = -EIO;
 47 
 48         return err;
 49 }
 50 
 51 int snd_dg00x_stream_set_local_rate(struct snd_dg00x *dg00x, unsigned int rate)
 52 {
 53         __be32 reg;
 54         unsigned int i;
 55 
 56         for (i = 0; i < ARRAY_SIZE(snd_dg00x_stream_rates); i++) {
 57                 if (rate == snd_dg00x_stream_rates[i])
 58                         break;
 59         }
 60         if (i == ARRAY_SIZE(snd_dg00x_stream_rates))
 61                 return -EINVAL;
 62 
 63         reg = cpu_to_be32(i);
 64         return snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
 65                                   DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE,
 66                                   &reg, sizeof(reg), 0);
 67 }
 68 
 69 int snd_dg00x_stream_get_clock(struct snd_dg00x *dg00x,
 70                                enum snd_dg00x_clock *clock)
 71 {
 72         __be32 reg;
 73         int err;
 74 
 75         err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
 76                                  DG00X_ADDR_BASE + DG00X_OFFSET_CLOCK_SOURCE,
 77                                  &reg, sizeof(reg), 0);
 78         if (err < 0)
 79                 return err;
 80 
 81         *clock = be32_to_cpu(reg) & 0x0f;
 82         if (*clock >= SND_DG00X_CLOCK_COUNT)
 83                 err = -EIO;
 84 
 85         return err;
 86 }
 87 
 88 int snd_dg00x_stream_check_external_clock(struct snd_dg00x *dg00x, bool *detect)
 89 {
 90         __be32 reg;
 91         int err;
 92 
 93         err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
 94                                  DG00X_ADDR_BASE + DG00X_OFFSET_DETECT_EXTERNAL,
 95                                  &reg, sizeof(reg), 0);
 96         if (err >= 0)
 97                 *detect = be32_to_cpu(reg) > 0;
 98 
 99         return err;
100 }
101 
102 int snd_dg00x_stream_get_external_rate(struct snd_dg00x *dg00x,
103                                        unsigned int *rate)
104 {
105         u32 data;
106         __be32 reg;
107         int err;
108 
109         err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
110                                  DG00X_ADDR_BASE + DG00X_OFFSET_EXTERNAL_RATE,
111                                  &reg, sizeof(reg), 0);
112         if (err < 0)
113                 return err;
114 
115         data = be32_to_cpu(reg) & 0x0f;
116         if (data < ARRAY_SIZE(snd_dg00x_stream_rates))
117                 *rate = snd_dg00x_stream_rates[data];
118         /* This means desync. */
119         else
120                 err = -EBUSY;
121 
122         return err;
123 }
124 
125 static void finish_session(struct snd_dg00x *dg00x)
126 {
127         __be32 data;
128 
129         data = cpu_to_be32(0x00000003);
130         snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
131                            DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_SET,
132                            &data, sizeof(data), 0);
133 
134         // Unregister isochronous channels for both direction.
135         data = 0;
136         snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
137                            DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
138                            &data, sizeof(data), 0);
139 
140         // Just after finishing the session, the device may lost transmitting
141         // functionality for a short time.
142         msleep(50);
143 }
144 
145 static int begin_session(struct snd_dg00x *dg00x)
146 {
147         __be32 data;
148         u32 curr;
149         int err;
150 
151         // Register isochronous channels for both direction.
152         data = cpu_to_be32((dg00x->tx_resources.channel << 16) |
153                            dg00x->rx_resources.channel);
154         err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
155                                  DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
156                                  &data, sizeof(data), 0);
157         if (err < 0)
158                 return err;
159 
160         err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
161                                  DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_STATE,
162                                  &data, sizeof(data), 0);
163         if (err < 0)
164                 return err;
165         curr = be32_to_cpu(data);
166 
167         if (curr == 0)
168                 curr = 2;
169 
170         curr--;
171         while (curr > 0) {
172                 data = cpu_to_be32(curr);
173                 err = snd_fw_transaction(dg00x->unit,
174                                          TCODE_WRITE_QUADLET_REQUEST,
175                                          DG00X_ADDR_BASE +
176                                          DG00X_OFFSET_STREAMING_SET,
177                                          &data, sizeof(data), 0);
178                 if (err < 0)
179                         break;
180 
181                 msleep(20);
182                 curr--;
183         }
184 
185         return err;
186 }
187 
188 static int keep_resources(struct snd_dg00x *dg00x, struct amdtp_stream *stream,
189                           unsigned int rate)
190 {
191         struct fw_iso_resources *resources;
192         int i;
193         int err;
194 
195         // Check sampling rate.
196         for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
197                 if (snd_dg00x_stream_rates[i] == rate)
198                         break;
199         }
200         if (i == SND_DG00X_RATE_COUNT)
201                 return -EINVAL;
202 
203         if (stream == &dg00x->tx_stream)
204                 resources = &dg00x->tx_resources;
205         else
206                 resources = &dg00x->rx_resources;
207 
208         err = amdtp_dot_set_parameters(stream, rate,
209                                        snd_dg00x_stream_pcm_channels[i]);
210         if (err < 0)
211                 return err;
212 
213         return fw_iso_resources_allocate(resources,
214                                 amdtp_stream_get_max_payload(stream),
215                                 fw_parent_device(dg00x->unit)->max_speed);
216 }
217 
218 static int init_stream(struct snd_dg00x *dg00x, struct amdtp_stream *s)
219 {
220         struct fw_iso_resources *resources;
221         enum amdtp_stream_direction dir;
222         int err;
223 
224         if (s == &dg00x->tx_stream) {
225                 resources = &dg00x->tx_resources;
226                 dir = AMDTP_IN_STREAM;
227         } else {
228                 resources = &dg00x->rx_resources;
229                 dir = AMDTP_OUT_STREAM;
230         }
231 
232         err = fw_iso_resources_init(resources, dg00x->unit);
233         if (err < 0)
234                 return err;
235 
236         err = amdtp_dot_init(s, dg00x->unit, dir);
237         if (err < 0)
238                 fw_iso_resources_destroy(resources);
239 
240         return err;
241 }
242 
243 static void destroy_stream(struct snd_dg00x *dg00x, struct amdtp_stream *s)
244 {
245         amdtp_stream_destroy(s);
246 
247         if (s == &dg00x->tx_stream)
248                 fw_iso_resources_destroy(&dg00x->tx_resources);
249         else
250                 fw_iso_resources_destroy(&dg00x->rx_resources);
251 }
252 
253 int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x)
254 {
255         int err;
256 
257         err = init_stream(dg00x, &dg00x->rx_stream);
258         if (err < 0)
259                 return err;
260 
261         err = init_stream(dg00x, &dg00x->tx_stream);
262         if (err < 0) {
263                 destroy_stream(dg00x, &dg00x->rx_stream);
264                 return err;
265         }
266 
267         err = amdtp_domain_init(&dg00x->domain);
268         if (err < 0) {
269                 destroy_stream(dg00x, &dg00x->rx_stream);
270                 destroy_stream(dg00x, &dg00x->tx_stream);
271         }
272 
273         return err;
274 }
275 
276 /*
277  * This function should be called before starting streams or after stopping
278  * streams.
279  */
280 void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x)
281 {
282         amdtp_domain_destroy(&dg00x->domain);
283 
284         destroy_stream(dg00x, &dg00x->rx_stream);
285         destroy_stream(dg00x, &dg00x->tx_stream);
286 }
287 
288 int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate,
289                                     unsigned int frames_per_period,
290                                     unsigned int frames_per_buffer)
291 {
292         unsigned int curr_rate;
293         int err;
294 
295         err = snd_dg00x_stream_get_local_rate(dg00x, &curr_rate);
296         if (err < 0)
297                 return err;
298         if (rate == 0)
299                 rate = curr_rate;
300 
301         if (dg00x->substreams_counter == 0 || curr_rate != rate) {
302                 amdtp_domain_stop(&dg00x->domain);
303 
304                 finish_session(dg00x);
305 
306                 fw_iso_resources_free(&dg00x->tx_resources);
307                 fw_iso_resources_free(&dg00x->rx_resources);
308 
309                 err = snd_dg00x_stream_set_local_rate(dg00x, rate);
310                 if (err < 0)
311                         return err;
312 
313                 err = keep_resources(dg00x, &dg00x->rx_stream, rate);
314                 if (err < 0)
315                         return err;
316 
317                 err = keep_resources(dg00x, &dg00x->tx_stream, rate);
318                 if (err < 0) {
319                         fw_iso_resources_free(&dg00x->rx_resources);
320                         return err;
321                 }
322 
323                 err = amdtp_domain_set_events_per_period(&dg00x->domain,
324                                         frames_per_period, frames_per_buffer);
325                 if (err < 0) {
326                         fw_iso_resources_free(&dg00x->rx_resources);
327                         fw_iso_resources_free(&dg00x->tx_resources);
328                         return err;
329                 }
330         }
331 
332         return 0;
333 }
334 
335 int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x)
336 {
337         unsigned int generation = dg00x->rx_resources.generation;
338         int err = 0;
339 
340         if (dg00x->substreams_counter == 0)
341                 return 0;
342 
343         if (amdtp_streaming_error(&dg00x->tx_stream) ||
344             amdtp_streaming_error(&dg00x->rx_stream)) {
345                 amdtp_domain_stop(&dg00x->domain);
346                 finish_session(dg00x);
347         }
348 
349         if (generation != fw_parent_device(dg00x->unit)->card->generation) {
350                 err = fw_iso_resources_update(&dg00x->tx_resources);
351                 if (err < 0)
352                         goto error;
353 
354                 err = fw_iso_resources_update(&dg00x->rx_resources);
355                 if (err < 0)
356                         goto error;
357         }
358 
359         /*
360          * No packets are transmitted without receiving packets, reagardless of
361          * which source of clock is used.
362          */
363         if (!amdtp_stream_running(&dg00x->rx_stream)) {
364                 int spd = fw_parent_device(dg00x->unit)->max_speed;
365 
366                 err = begin_session(dg00x);
367                 if (err < 0)
368                         goto error;
369 
370                 err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->rx_stream,
371                                               dg00x->rx_resources.channel, spd);
372                 if (err < 0)
373                         goto error;
374 
375                 err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->tx_stream,
376                                               dg00x->tx_resources.channel, spd);
377                 if (err < 0)
378                         goto error;
379 
380                 // NOTE: The device doesn't start packet transmission till receiving any packet.
381                 // It ignores presentation time expressed by the value of syt field of CIP header
382                 // in received packets. The sequence of the number of data blocks per packet is
383                 // important for media clock recovery.
384                 err = amdtp_domain_start(&dg00x->domain, 0, true, true);
385                 if (err < 0)
386                         goto error;
387 
388                 if (!amdtp_domain_wait_ready(&dg00x->domain, READY_TIMEOUT_MS)) {
389                         err = -ETIMEDOUT;
390                         goto error;
391                 }
392         }
393 
394         return 0;
395 error:
396         amdtp_domain_stop(&dg00x->domain);
397         finish_session(dg00x);
398 
399         return err;
400 }
401 
402 void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x)
403 {
404         if (dg00x->substreams_counter == 0) {
405                 amdtp_domain_stop(&dg00x->domain);
406                 finish_session(dg00x);
407 
408                 fw_iso_resources_free(&dg00x->tx_resources);
409                 fw_iso_resources_free(&dg00x->rx_resources);
410         }
411 }
412 
413 void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x)
414 {
415         fw_iso_resources_update(&dg00x->tx_resources);
416         fw_iso_resources_update(&dg00x->rx_resources);
417 
418         amdtp_stream_update(&dg00x->tx_stream);
419         amdtp_stream_update(&dg00x->rx_stream);
420 }
421 
422 void snd_dg00x_stream_lock_changed(struct snd_dg00x *dg00x)
423 {
424         dg00x->dev_lock_changed = true;
425         wake_up(&dg00x->hwdep_wait);
426 }
427 
428 int snd_dg00x_stream_lock_try(struct snd_dg00x *dg00x)
429 {
430         int err;
431 
432         spin_lock_irq(&dg00x->lock);
433 
434         /* user land lock this */
435         if (dg00x->dev_lock_count < 0) {
436                 err = -EBUSY;
437                 goto end;
438         }
439 
440         /* this is the first time */
441         if (dg00x->dev_lock_count++ == 0)
442                 snd_dg00x_stream_lock_changed(dg00x);
443         err = 0;
444 end:
445         spin_unlock_irq(&dg00x->lock);
446         return err;
447 }
448 
449 void snd_dg00x_stream_lock_release(struct snd_dg00x *dg00x)
450 {
451         spin_lock_irq(&dg00x->lock);
452 
453         if (WARN_ON(dg00x->dev_lock_count <= 0))
454                 goto end;
455         if (--dg00x->dev_lock_count == 0)
456                 snd_dg00x_stream_lock_changed(dg00x);
457 end:
458         spin_unlock_irq(&dg00x->lock);
459 }
460 

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

kernel.org | git.kernel.org | LWN.net | Project Home | 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.

sflogo.php