root/sound/firewire/digi00x/digi00x-stream.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. snd_dg00x_stream_get_local_rate
  2. snd_dg00x_stream_set_local_rate
  3. snd_dg00x_stream_get_clock
  4. snd_dg00x_stream_check_external_clock
  5. snd_dg00x_stream_get_external_rate
  6. finish_session
  7. begin_session
  8. keep_resources
  9. init_stream
  10. destroy_stream
  11. snd_dg00x_stream_init_duplex
  12. snd_dg00x_stream_destroy_duplex
  13. snd_dg00x_stream_reserve_duplex
  14. snd_dg00x_stream_start_duplex
  15. snd_dg00x_stream_stop_duplex
  16. snd_dg00x_stream_update_duplex
  17. snd_dg00x_stream_lock_changed
  18. snd_dg00x_stream_lock_try
  19. snd_dg00x_stream_lock_release

   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 CALLBACK_TIMEOUT 500
  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 
 265         err = amdtp_domain_init(&dg00x->domain);
 266         if (err < 0) {
 267                 destroy_stream(dg00x, &dg00x->rx_stream);
 268                 destroy_stream(dg00x, &dg00x->tx_stream);
 269         }
 270 
 271         return err;
 272 }
 273 
 274 /*
 275  * This function should be called before starting streams or after stopping
 276  * streams.
 277  */
 278 void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x)
 279 {
 280         amdtp_domain_destroy(&dg00x->domain);
 281 
 282         destroy_stream(dg00x, &dg00x->rx_stream);
 283         destroy_stream(dg00x, &dg00x->tx_stream);
 284 }
 285 
 286 int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate)
 287 {
 288         unsigned int curr_rate;
 289         int err;
 290 
 291         err = snd_dg00x_stream_get_local_rate(dg00x, &curr_rate);
 292         if (err < 0)
 293                 return err;
 294         if (rate == 0)
 295                 rate = curr_rate;
 296 
 297         if (dg00x->substreams_counter == 0 || curr_rate != rate) {
 298                 amdtp_domain_stop(&dg00x->domain);
 299 
 300                 finish_session(dg00x);
 301 
 302                 fw_iso_resources_free(&dg00x->tx_resources);
 303                 fw_iso_resources_free(&dg00x->rx_resources);
 304 
 305                 err = snd_dg00x_stream_set_local_rate(dg00x, rate);
 306                 if (err < 0)
 307                         return err;
 308 
 309                 err = keep_resources(dg00x, &dg00x->rx_stream, rate);
 310                 if (err < 0)
 311                         return err;
 312 
 313                 err = keep_resources(dg00x, &dg00x->tx_stream, rate);
 314                 if (err < 0) {
 315                         fw_iso_resources_free(&dg00x->rx_resources);
 316                         return err;
 317                 }
 318         }
 319 
 320         return 0;
 321 }
 322 
 323 int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x)
 324 {
 325         unsigned int generation = dg00x->rx_resources.generation;
 326         int err = 0;
 327 
 328         if (dg00x->substreams_counter == 0)
 329                 return 0;
 330 
 331         if (amdtp_streaming_error(&dg00x->tx_stream) ||
 332             amdtp_streaming_error(&dg00x->rx_stream)) {
 333                 amdtp_domain_stop(&dg00x->domain);
 334                 finish_session(dg00x);
 335         }
 336 
 337         if (generation != fw_parent_device(dg00x->unit)->card->generation) {
 338                 err = fw_iso_resources_update(&dg00x->tx_resources);
 339                 if (err < 0)
 340                         goto error;
 341 
 342                 err = fw_iso_resources_update(&dg00x->rx_resources);
 343                 if (err < 0)
 344                         goto error;
 345         }
 346 
 347         /*
 348          * No packets are transmitted without receiving packets, reagardless of
 349          * which source of clock is used.
 350          */
 351         if (!amdtp_stream_running(&dg00x->rx_stream)) {
 352                 int spd = fw_parent_device(dg00x->unit)->max_speed;
 353 
 354                 err = begin_session(dg00x);
 355                 if (err < 0)
 356                         goto error;
 357 
 358                 err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->rx_stream,
 359                                               dg00x->rx_resources.channel, spd);
 360                 if (err < 0)
 361                         goto error;
 362 
 363                 err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->tx_stream,
 364                                               dg00x->tx_resources.channel, spd);
 365                 if (err < 0)
 366                         goto error;
 367 
 368                 err = amdtp_domain_start(&dg00x->domain);
 369                 if (err < 0)
 370                         goto error;
 371 
 372                 if (!amdtp_stream_wait_callback(&dg00x->rx_stream,
 373                                                 CALLBACK_TIMEOUT) ||
 374                     !amdtp_stream_wait_callback(&dg00x->tx_stream,
 375                                                 CALLBACK_TIMEOUT)) {
 376                         err = -ETIMEDOUT;
 377                         goto error;
 378                 }
 379         }
 380 
 381         return 0;
 382 error:
 383         amdtp_domain_stop(&dg00x->domain);
 384         finish_session(dg00x);
 385 
 386         return err;
 387 }
 388 
 389 void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x)
 390 {
 391         if (dg00x->substreams_counter == 0) {
 392                 amdtp_domain_stop(&dg00x->domain);
 393                 finish_session(dg00x);
 394 
 395                 fw_iso_resources_free(&dg00x->tx_resources);
 396                 fw_iso_resources_free(&dg00x->rx_resources);
 397         }
 398 }
 399 
 400 void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x)
 401 {
 402         fw_iso_resources_update(&dg00x->tx_resources);
 403         fw_iso_resources_update(&dg00x->rx_resources);
 404 
 405         amdtp_stream_update(&dg00x->tx_stream);
 406         amdtp_stream_update(&dg00x->rx_stream);
 407 }
 408 
 409 void snd_dg00x_stream_lock_changed(struct snd_dg00x *dg00x)
 410 {
 411         dg00x->dev_lock_changed = true;
 412         wake_up(&dg00x->hwdep_wait);
 413 }
 414 
 415 int snd_dg00x_stream_lock_try(struct snd_dg00x *dg00x)
 416 {
 417         int err;
 418 
 419         spin_lock_irq(&dg00x->lock);
 420 
 421         /* user land lock this */
 422         if (dg00x->dev_lock_count < 0) {
 423                 err = -EBUSY;
 424                 goto end;
 425         }
 426 
 427         /* this is the first time */
 428         if (dg00x->dev_lock_count++ == 0)
 429                 snd_dg00x_stream_lock_changed(dg00x);
 430         err = 0;
 431 end:
 432         spin_unlock_irq(&dg00x->lock);
 433         return err;
 434 }
 435 
 436 void snd_dg00x_stream_lock_release(struct snd_dg00x *dg00x)
 437 {
 438         spin_lock_irq(&dg00x->lock);
 439 
 440         if (WARN_ON(dg00x->dev_lock_count <= 0))
 441                 goto end;
 442         if (--dg00x->dev_lock_count == 0)
 443                 snd_dg00x_stream_lock_changed(dg00x);
 444 end:
 445         spin_unlock_irq(&dg00x->lock);
 446 }

/* [<][>][^][v][top][bottom][index][help] */