460 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * digi00x-stream.c - a part of driver for Digidesign Digi 002/003 family4 *5 * Copyright (c) 2014-2015 Takashi Sakamoto6 */7 8#include "digi00x.h"9 10#define READY_TIMEOUT_MS 20011 12const 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. */20const unsigned int21snd_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 30int 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 ®, 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 else46 err = -EIO;47 48 return err;49}50 51int 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 ®, sizeof(reg), 0);67}68 69int 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 ®, 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 88int 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 ®, sizeof(reg), 0);96 if (err >= 0)97 *detect = be32_to_cpu(reg) > 0;98 99 return err;100}101 102int 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 ®, 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 else120 err = -EBUSY;121 122 return err;123}124 125static 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 transmitting141 // functionality for a short time.142 msleep(50);143}144 145static 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 188static 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 else206 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 218static 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 243static 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 else250 fw_iso_resources_destroy(&dg00x->rx_resources);251}252 253int 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 stopping278 * streams.279 */280void 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 288int 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 335int 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 of361 * 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 header382 // in received packets. The sequence of the number of data blocks per packet is383 // 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;395error:396 amdtp_domain_stop(&dg00x->domain);397 finish_session(dg00x);398 399 return err;400}401 402void 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 413void 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 422void snd_dg00x_stream_lock_changed(struct snd_dg00x *dg00x)423{424 dg00x->dev_lock_changed = true;425 wake_up(&dg00x->hwdep_wait);426}427 428int 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;444end:445 spin_unlock_irq(&dg00x->lock);446 return err;447}448 449void 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);457end:458 spin_unlock_irq(&dg00x->lock);459}460