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