15c8c1079SŠerif Rami // SPDX-License-Identifier: GPL-2.0-only 25c8c1079SŠerif Rami // Copyright (c) 2025 Šerif Rami <ramiserifpersia@gmail.com> 35c8c1079SŠerif Rami 45c8c1079SŠerif Rami #include "us144mkii.h" 55c8c1079SŠerif Rami 65c8c1079SŠerif Rami /** 75c8c1079SŠerif Rami * tascam_playback_open() - Opens the PCM playback substream. 85c8c1079SŠerif Rami * @substream: The ALSA PCM substream to open. 95c8c1079SŠerif Rami * 105c8c1079SŠerif Rami * This function sets the hardware parameters for the playback substream 115c8c1079SŠerif Rami * and stores a reference to the substream in the driver's private data. 125c8c1079SŠerif Rami * 135c8c1079SŠerif Rami * Return: 0 on success. 145c8c1079SŠerif Rami */ 155c8c1079SŠerif Rami static int tascam_playback_open(struct snd_pcm_substream *substream) 165c8c1079SŠerif Rami { 175c8c1079SŠerif Rami struct tascam_card *tascam = snd_pcm_substream_chip(substream); 185c8c1079SŠerif Rami 195c8c1079SŠerif Rami substream->runtime->hw = tascam_pcm_hw; 205c8c1079SŠerif Rami tascam->playback_substream = substream; 215c8c1079SŠerif Rami atomic_set(&tascam->playback_active, 0); 225c8c1079SŠerif Rami 235c8c1079SŠerif Rami return 0; 245c8c1079SŠerif Rami } 255c8c1079SŠerif Rami 265c8c1079SŠerif Rami /** 275c8c1079SŠerif Rami * tascam_playback_close() - Closes the PCM playback substream. 285c8c1079SŠerif Rami * @substream: The ALSA PCM substream to close. 295c8c1079SŠerif Rami * 305c8c1079SŠerif Rami * This function clears the reference to the playback substream in the 315c8c1079SŠerif Rami * driver's private data. 325c8c1079SŠerif Rami * 335c8c1079SŠerif Rami * Return: 0 on success. 345c8c1079SŠerif Rami */ 355c8c1079SŠerif Rami static int tascam_playback_close(struct snd_pcm_substream *substream) 365c8c1079SŠerif Rami { 375c8c1079SŠerif Rami struct tascam_card *tascam = snd_pcm_substream_chip(substream); 385c8c1079SŠerif Rami 395c8c1079SŠerif Rami tascam->playback_substream = NULL; 405c8c1079SŠerif Rami 415c8c1079SŠerif Rami return 0; 425c8c1079SŠerif Rami } 435c8c1079SŠerif Rami 445c8c1079SŠerif Rami /** 455c8c1079SŠerif Rami * tascam_playback_prepare() - Prepares the PCM playback substream for use. 465c8c1079SŠerif Rami * @substream: The ALSA PCM substream to prepare. 475c8c1079SŠerif Rami * 48*a2a2210fSŠerif Rami * This function initializes playback-related counters and flags, and configures 49*a2a2210fSŠerif Rami * the playback URBs with appropriate packet sizes based on the nominal frame 50*a2a2210fSŠerif Rami * rate. 515c8c1079SŠerif Rami * 525c8c1079SŠerif Rami * Return: 0 on success. 535c8c1079SŠerif Rami */ 545c8c1079SŠerif Rami static int tascam_playback_prepare(struct snd_pcm_substream *substream) 555c8c1079SŠerif Rami { 565c8c1079SŠerif Rami struct tascam_card *tascam = snd_pcm_substream_chip(substream); 57*a2a2210fSŠerif Rami struct snd_pcm_runtime *runtime = substream->runtime; 58*a2a2210fSŠerif Rami int i, u; 59*a2a2210fSŠerif Rami size_t nominal_frames_per_packet, nominal_bytes_per_packet; 60*a2a2210fSŠerif Rami size_t total_bytes_in_urb; 615c8c1079SŠerif Rami 625c8c1079SŠerif Rami tascam->driver_playback_pos = 0; 635c8c1079SŠerif Rami tascam->playback_frames_consumed = 0; 64*a2a2210fSŠerif Rami tascam->last_period_pos = 0; 65*a2a2210fSŠerif Rami tascam->feedback_pattern_in_idx = 0; 66*a2a2210fSŠerif Rami tascam->feedback_pattern_out_idx = 0; 67*a2a2210fSŠerif Rami tascam->feedback_synced = false; 68*a2a2210fSŠerif Rami tascam->feedback_consecutive_errors = 0; 69*a2a2210fSŠerif Rami tascam->feedback_urb_skip_count = NUM_FEEDBACK_URBS; 70*a2a2210fSŠerif Rami 71*a2a2210fSŠerif Rami nominal_frames_per_packet = runtime->rate / 8000; 72*a2a2210fSŠerif Rami for (i = 0; i < FEEDBACK_ACCUMULATOR_SIZE; i++) 73*a2a2210fSŠerif Rami tascam->feedback_accumulator_pattern[i] = 74*a2a2210fSŠerif Rami nominal_frames_per_packet; 75*a2a2210fSŠerif Rami 76*a2a2210fSŠerif Rami for (i = 0; i < NUM_FEEDBACK_URBS; i++) { 77*a2a2210fSŠerif Rami struct urb *f_urb = tascam->feedback_urbs[i]; 78*a2a2210fSŠerif Rami int j; 79*a2a2210fSŠerif Rami 80*a2a2210fSŠerif Rami f_urb->number_of_packets = FEEDBACK_URB_PACKETS; 81*a2a2210fSŠerif Rami f_urb->transfer_buffer_length = 82*a2a2210fSŠerif Rami FEEDBACK_URB_PACKETS * FEEDBACK_PACKET_SIZE; 83*a2a2210fSŠerif Rami for (j = 0; j < FEEDBACK_URB_PACKETS; j++) { 84*a2a2210fSŠerif Rami f_urb->iso_frame_desc[j].offset = 85*a2a2210fSŠerif Rami j * FEEDBACK_PACKET_SIZE; 86*a2a2210fSŠerif Rami f_urb->iso_frame_desc[j].length = FEEDBACK_PACKET_SIZE; 87*a2a2210fSŠerif Rami } 88*a2a2210fSŠerif Rami } 89*a2a2210fSŠerif Rami 90*a2a2210fSŠerif Rami nominal_bytes_per_packet = nominal_frames_per_packet * BYTES_PER_FRAME; 91*a2a2210fSŠerif Rami total_bytes_in_urb = nominal_bytes_per_packet * PLAYBACK_URB_PACKETS; 92*a2a2210fSŠerif Rami 93*a2a2210fSŠerif Rami for (u = 0; u < NUM_PLAYBACK_URBS; u++) { 94*a2a2210fSŠerif Rami struct urb *urb = tascam->playback_urbs[u]; 95*a2a2210fSŠerif Rami 96*a2a2210fSŠerif Rami memset(urb->transfer_buffer, 0, 97*a2a2210fSŠerif Rami tascam->playback_urb_alloc_size); 98*a2a2210fSŠerif Rami urb->transfer_buffer_length = total_bytes_in_urb; 99*a2a2210fSŠerif Rami urb->number_of_packets = PLAYBACK_URB_PACKETS; 100*a2a2210fSŠerif Rami for (i = 0; i < PLAYBACK_URB_PACKETS; i++) { 101*a2a2210fSŠerif Rami urb->iso_frame_desc[i].offset = 102*a2a2210fSŠerif Rami i * nominal_bytes_per_packet; 103*a2a2210fSŠerif Rami urb->iso_frame_desc[i].length = 104*a2a2210fSŠerif Rami nominal_bytes_per_packet; 105*a2a2210fSŠerif Rami } 106*a2a2210fSŠerif Rami } 1075c8c1079SŠerif Rami 1085c8c1079SŠerif Rami return 0; 1095c8c1079SŠerif Rami } 1105c8c1079SŠerif Rami 1115c8c1079SŠerif Rami /** 1125c8c1079SŠerif Rami * tascam_playback_pointer() - Returns the current playback pointer position. 1135c8c1079SŠerif Rami * @substream: The ALSA PCM substream. 1145c8c1079SŠerif Rami * 1155c8c1079SŠerif Rami * This function returns the current position of the playback pointer within 1165c8c1079SŠerif Rami * the ALSA ring buffer, in frames. 1175c8c1079SŠerif Rami * 1185c8c1079SŠerif Rami * Return: The current playback pointer position in frames. 1195c8c1079SŠerif Rami */ 1205c8c1079SŠerif Rami static snd_pcm_uframes_t 1215c8c1079SŠerif Rami tascam_playback_pointer(struct snd_pcm_substream *substream) 1225c8c1079SŠerif Rami { 1235c8c1079SŠerif Rami struct tascam_card *tascam = snd_pcm_substream_chip(substream); 1245c8c1079SŠerif Rami struct snd_pcm_runtime *runtime = substream->runtime; 1255c8c1079SŠerif Rami u64 pos; 1265c8c1079SŠerif Rami 1275c8c1079SŠerif Rami if (!atomic_read(&tascam->playback_active)) 1285c8c1079SŠerif Rami return 0; 1295c8c1079SŠerif Rami 1305c8c1079SŠerif Rami scoped_guard(spinlock_irqsave, &tascam->lock) { 1315c8c1079SŠerif Rami pos = tascam->playback_frames_consumed; 1325c8c1079SŠerif Rami } 1335c8c1079SŠerif Rami 1345c8c1079SŠerif Rami if (runtime->buffer_size == 0) 1355c8c1079SŠerif Rami return 0; 1365c8c1079SŠerif Rami 1375c8c1079SŠerif Rami return do_div(pos, runtime->buffer_size); 1385c8c1079SŠerif Rami } 1395c8c1079SŠerif Rami 1405c8c1079SŠerif Rami /** 1415c8c1079SŠerif Rami * tascam_playback_ops - ALSA PCM operations for playback. 1425c8c1079SŠerif Rami * 1435c8c1079SŠerif Rami * This structure defines the callback functions for playback stream operations, 1445c8c1079SŠerif Rami * including open, close, ioctl, hardware parameters, hardware free, prepare, 1455c8c1079SŠerif Rami * trigger, and pointer. 1465c8c1079SŠerif Rami */ 1475c8c1079SŠerif Rami const struct snd_pcm_ops tascam_playback_ops = { 1485c8c1079SŠerif Rami .open = tascam_playback_open, 1495c8c1079SŠerif Rami .close = tascam_playback_close, 1505c8c1079SŠerif Rami .ioctl = snd_pcm_lib_ioctl, 1515c8c1079SŠerif Rami .hw_params = tascam_pcm_hw_params, 1525c8c1079SŠerif Rami .hw_free = tascam_pcm_hw_free, 1535c8c1079SŠerif Rami .prepare = tascam_playback_prepare, 1545c8c1079SŠerif Rami .trigger = tascam_pcm_trigger, 1555c8c1079SŠerif Rami .pointer = tascam_playback_pointer, 1565c8c1079SŠerif Rami }; 157*a2a2210fSŠerif Rami 158*a2a2210fSŠerif Rami void playback_urb_complete(struct urb *urb) 159*a2a2210fSŠerif Rami { 160*a2a2210fSŠerif Rami struct tascam_card *tascam = urb->context; 161*a2a2210fSŠerif Rami struct snd_pcm_substream *substream; 162*a2a2210fSŠerif Rami struct snd_pcm_runtime *runtime; 163*a2a2210fSŠerif Rami size_t total_bytes_for_urb = 0; 164*a2a2210fSŠerif Rami snd_pcm_uframes_t offset_frames; 165*a2a2210fSŠerif Rami snd_pcm_uframes_t frames_to_copy; 166*a2a2210fSŠerif Rami int ret, i; 167*a2a2210fSŠerif Rami 168*a2a2210fSŠerif Rami if (urb->status) { 169*a2a2210fSŠerif Rami if (urb->status != -ENOENT && urb->status != -ECONNRESET && 170*a2a2210fSŠerif Rami urb->status != -ESHUTDOWN && urb->status != -ENODEV) 171*a2a2210fSŠerif Rami dev_err_ratelimited(tascam->card->dev, 172*a2a2210fSŠerif Rami "Playback URB failed: %d\n", 173*a2a2210fSŠerif Rami urb->status); 174*a2a2210fSŠerif Rami goto out; 175*a2a2210fSŠerif Rami } 176*a2a2210fSŠerif Rami if (!tascam || !atomic_read(&tascam->playback_active)) 177*a2a2210fSŠerif Rami goto out; 178*a2a2210fSŠerif Rami 179*a2a2210fSŠerif Rami substream = tascam->playback_substream; 180*a2a2210fSŠerif Rami if (!substream || !substream->runtime) 181*a2a2210fSŠerif Rami goto out; 182*a2a2210fSŠerif Rami runtime = substream->runtime; 183*a2a2210fSŠerif Rami 184*a2a2210fSŠerif Rami scoped_guard(spinlock_irqsave, &tascam->lock) { 185*a2a2210fSŠerif Rami for (i = 0; i < urb->number_of_packets; i++) { 186*a2a2210fSŠerif Rami unsigned int frames_for_packet; 187*a2a2210fSŠerif Rami size_t bytes_for_packet; 188*a2a2210fSŠerif Rami 189*a2a2210fSŠerif Rami if (tascam->feedback_synced) { 190*a2a2210fSŠerif Rami frames_for_packet = 191*a2a2210fSŠerif Rami tascam->feedback_accumulator_pattern 192*a2a2210fSŠerif Rami [tascam->feedback_pattern_out_idx]; 193*a2a2210fSŠerif Rami tascam->feedback_pattern_out_idx = 194*a2a2210fSŠerif Rami (tascam->feedback_pattern_out_idx + 1) % 195*a2a2210fSŠerif Rami FEEDBACK_ACCUMULATOR_SIZE; 196*a2a2210fSŠerif Rami } else { 197*a2a2210fSŠerif Rami frames_for_packet = runtime->rate / 8000; 198*a2a2210fSŠerif Rami } 199*a2a2210fSŠerif Rami bytes_for_packet = frames_for_packet * BYTES_PER_FRAME; 200*a2a2210fSŠerif Rami 201*a2a2210fSŠerif Rami urb->iso_frame_desc[i].offset = total_bytes_for_urb; 202*a2a2210fSŠerif Rami urb->iso_frame_desc[i].length = bytes_for_packet; 203*a2a2210fSŠerif Rami total_bytes_for_urb += bytes_for_packet; 204*a2a2210fSŠerif Rami } 205*a2a2210fSŠerif Rami urb->transfer_buffer_length = total_bytes_for_urb; 206*a2a2210fSŠerif Rami 207*a2a2210fSŠerif Rami offset_frames = tascam->driver_playback_pos; 208*a2a2210fSŠerif Rami frames_to_copy = bytes_to_frames(runtime, total_bytes_for_urb); 209*a2a2210fSŠerif Rami tascam->driver_playback_pos = 210*a2a2210fSŠerif Rami (offset_frames + frames_to_copy) % runtime->buffer_size; 211*a2a2210fSŠerif Rami } 212*a2a2210fSŠerif Rami 213*a2a2210fSŠerif Rami if (total_bytes_for_urb > 0) { 214*a2a2210fSŠerif Rami u8 *dst_buf = urb->transfer_buffer; 215*a2a2210fSŠerif Rami 216*a2a2210fSŠerif Rami /* Handle ring buffer wrap-around */ 217*a2a2210fSŠerif Rami if (offset_frames + frames_to_copy > runtime->buffer_size) { 218*a2a2210fSŠerif Rami size_t first_chunk_bytes = frames_to_bytes( 219*a2a2210fSŠerif Rami runtime, runtime->buffer_size - offset_frames); 220*a2a2210fSŠerif Rami size_t second_chunk_bytes = 221*a2a2210fSŠerif Rami total_bytes_for_urb - first_chunk_bytes; 222*a2a2210fSŠerif Rami 223*a2a2210fSŠerif Rami memcpy(dst_buf, 224*a2a2210fSŠerif Rami runtime->dma_area + 225*a2a2210fSŠerif Rami frames_to_bytes(runtime, offset_frames), 226*a2a2210fSŠerif Rami first_chunk_bytes); 227*a2a2210fSŠerif Rami memcpy(dst_buf + first_chunk_bytes, runtime->dma_area, 228*a2a2210fSŠerif Rami second_chunk_bytes); 229*a2a2210fSŠerif Rami } else { 230*a2a2210fSŠerif Rami memcpy(dst_buf, 231*a2a2210fSŠerif Rami runtime->dma_area + 232*a2a2210fSŠerif Rami frames_to_bytes(runtime, offset_frames), 233*a2a2210fSŠerif Rami total_bytes_for_urb); 234*a2a2210fSŠerif Rami } 235*a2a2210fSŠerif Rami 236*a2a2210fSŠerif Rami process_playback_routing_us144mkii(tascam, dst_buf, dst_buf, 237*a2a2210fSŠerif Rami frames_to_copy); 238*a2a2210fSŠerif Rami } 239*a2a2210fSŠerif Rami 240*a2a2210fSŠerif Rami urb->dev = tascam->dev; 241*a2a2210fSŠerif Rami usb_get_urb(urb); 242*a2a2210fSŠerif Rami usb_anchor_urb(urb, &tascam->playback_anchor); 243*a2a2210fSŠerif Rami ret = usb_submit_urb(urb, GFP_ATOMIC); 244*a2a2210fSŠerif Rami if (ret < 0) { 245*a2a2210fSŠerif Rami dev_err_ratelimited(tascam->card->dev, 246*a2a2210fSŠerif Rami "Failed to resubmit playback URB: %d\n", 247*a2a2210fSŠerif Rami ret); 248*a2a2210fSŠerif Rami usb_unanchor_urb(urb); 249*a2a2210fSŠerif Rami usb_put_urb(urb); 250*a2a2210fSŠerif Rami atomic_dec( 251*a2a2210fSŠerif Rami &tascam->active_urbs); /* Decrement on failed resubmission */ 252*a2a2210fSŠerif Rami } 253*a2a2210fSŠerif Rami out: 254*a2a2210fSŠerif Rami usb_put_urb(urb); 255*a2a2210fSŠerif Rami } 256*a2a2210fSŠerif Rami 257*a2a2210fSŠerif Rami void feedback_urb_complete(struct urb *urb) 258*a2a2210fSŠerif Rami { 259*a2a2210fSŠerif Rami struct tascam_card *tascam = urb->context; 260*a2a2210fSŠerif Rami struct snd_pcm_substream *playback_ss, *capture_ss; 261*a2a2210fSŠerif Rami struct snd_pcm_runtime *playback_rt, *capture_rt; 262*a2a2210fSŠerif Rami u64 total_frames_in_urb = 0; 263*a2a2210fSŠerif Rami int ret, p; 264*a2a2210fSŠerif Rami unsigned int old_in_idx, new_in_idx; 265*a2a2210fSŠerif Rami bool playback_period_elapsed = false; 266*a2a2210fSŠerif Rami bool capture_period_elapsed = false; 267*a2a2210fSŠerif Rami 268*a2a2210fSŠerif Rami if (urb->status) { 269*a2a2210fSŠerif Rami if (urb->status != -ENOENT && urb->status != -ECONNRESET && 270*a2a2210fSŠerif Rami urb->status != -ESHUTDOWN && urb->status != -ENODEV) { 271*a2a2210fSŠerif Rami dev_err_ratelimited(tascam->card->dev, 272*a2a2210fSŠerif Rami "Feedback URB failed: %d\n", 273*a2a2210fSŠerif Rami urb->status); 274*a2a2210fSŠerif Rami atomic_dec( 275*a2a2210fSŠerif Rami &tascam->active_urbs); /* Decrement on failed resubmission */ 276*a2a2210fSŠerif Rami } 277*a2a2210fSŠerif Rami goto out; 278*a2a2210fSŠerif Rami } 279*a2a2210fSŠerif Rami if (!tascam || !atomic_read(&tascam->playback_active)) 280*a2a2210fSŠerif Rami goto out; 281*a2a2210fSŠerif Rami 282*a2a2210fSŠerif Rami playback_ss = tascam->playback_substream; 283*a2a2210fSŠerif Rami if (!playback_ss || !playback_ss->runtime) 284*a2a2210fSŠerif Rami goto out; 285*a2a2210fSŠerif Rami playback_rt = playback_ss->runtime; 286*a2a2210fSŠerif Rami 287*a2a2210fSŠerif Rami capture_ss = tascam->capture_substream; 288*a2a2210fSŠerif Rami capture_rt = capture_ss ? capture_ss->runtime : NULL; 289*a2a2210fSŠerif Rami 290*a2a2210fSŠerif Rami scoped_guard(spinlock_irqsave, &tascam->lock) { 291*a2a2210fSŠerif Rami if (tascam->feedback_urb_skip_count > 0) { 292*a2a2210fSŠerif Rami tascam->feedback_urb_skip_count--; 293*a2a2210fSŠerif Rami break; 294*a2a2210fSŠerif Rami } 295*a2a2210fSŠerif Rami 296*a2a2210fSŠerif Rami old_in_idx = tascam->feedback_pattern_in_idx; 297*a2a2210fSŠerif Rami 298*a2a2210fSŠerif Rami for (p = 0; p < urb->number_of_packets; p++) { 299*a2a2210fSŠerif Rami u8 feedback_value = 0; 300*a2a2210fSŠerif Rami const unsigned int *pattern; 301*a2a2210fSŠerif Rami bool packet_ok = 302*a2a2210fSŠerif Rami (urb->iso_frame_desc[p].status == 0 && 303*a2a2210fSŠerif Rami urb->iso_frame_desc[p].actual_length >= 1); 304*a2a2210fSŠerif Rami 305*a2a2210fSŠerif Rami if (packet_ok) 306*a2a2210fSŠerif Rami feedback_value = 307*a2a2210fSŠerif Rami *((u8 *)urb->transfer_buffer + 308*a2a2210fSŠerif Rami urb->iso_frame_desc[p].offset); 309*a2a2210fSŠerif Rami 310*a2a2210fSŠerif Rami if (packet_ok) { 311*a2a2210fSŠerif Rami int delta = feedback_value - 312*a2a2210fSŠerif Rami tascam->fpo.base_feedback_value + 313*a2a2210fSŠerif Rami tascam->fpo.feedback_offset; 314*a2a2210fSŠerif Rami int pattern_idx; 315*a2a2210fSŠerif Rami 316*a2a2210fSŠerif Rami if (delta < 0) { 317*a2a2210fSŠerif Rami pattern_idx = 318*a2a2210fSŠerif Rami 0; // Clamp to the lowest pattern 319*a2a2210fSŠerif Rami } else if (delta >= 5) { 320*a2a2210fSŠerif Rami pattern_idx = 321*a2a2210fSŠerif Rami 4; // Clamp to the highest pattern 322*a2a2210fSŠerif Rami } else { 323*a2a2210fSŠerif Rami pattern_idx = delta; 324*a2a2210fSŠerif Rami } 325*a2a2210fSŠerif Rami 326*a2a2210fSŠerif Rami pattern = 327*a2a2210fSŠerif Rami tascam->fpo 328*a2a2210fSŠerif Rami .full_frame_patterns[pattern_idx]; 329*a2a2210fSŠerif Rami tascam->feedback_consecutive_errors = 0; 330*a2a2210fSŠerif Rami int i; 331*a2a2210fSŠerif Rami 332*a2a2210fSŠerif Rami for (i = 0; i < 8; i++) { 333*a2a2210fSŠerif Rami unsigned int in_idx = 334*a2a2210fSŠerif Rami (tascam->feedback_pattern_in_idx + 335*a2a2210fSŠerif Rami i) % 336*a2a2210fSŠerif Rami FEEDBACK_ACCUMULATOR_SIZE; 337*a2a2210fSŠerif Rami 338*a2a2210fSŠerif Rami tascam->feedback_accumulator_pattern 339*a2a2210fSŠerif Rami [in_idx] = pattern[i]; 340*a2a2210fSŠerif Rami total_frames_in_urb += pattern[i]; 341*a2a2210fSŠerif Rami } 342*a2a2210fSŠerif Rami } else { 343*a2a2210fSŠerif Rami unsigned int nominal_frames = 344*a2a2210fSŠerif Rami playback_rt->rate / 8000; 345*a2a2210fSŠerif Rami int i; 346*a2a2210fSŠerif Rami 347*a2a2210fSŠerif Rami if (tascam->feedback_synced) { 348*a2a2210fSŠerif Rami tascam->feedback_consecutive_errors++; 349*a2a2210fSŠerif Rami if (tascam->feedback_consecutive_errors > 350*a2a2210fSŠerif Rami FEEDBACK_SYNC_LOSS_THRESHOLD) { 351*a2a2210fSŠerif Rami dev_err(tascam->card->dev, 352*a2a2210fSŠerif Rami "Fatal: Feedback sync lost. Stopping stream.\n"); 353*a2a2210fSŠerif Rami schedule_work( 354*a2a2210fSŠerif Rami &tascam->stop_pcm_work); 355*a2a2210fSŠerif Rami tascam->feedback_synced = false; 356*a2a2210fSŠerif Rami break; 357*a2a2210fSŠerif Rami } 358*a2a2210fSŠerif Rami } 359*a2a2210fSŠerif Rami for (i = 0; i < 8; i++) { 360*a2a2210fSŠerif Rami unsigned int in_idx = 361*a2a2210fSŠerif Rami (tascam->feedback_pattern_in_idx + 362*a2a2210fSŠerif Rami i) % 363*a2a2210fSŠerif Rami FEEDBACK_ACCUMULATOR_SIZE; 364*a2a2210fSŠerif Rami 365*a2a2210fSŠerif Rami tascam->feedback_accumulator_pattern 366*a2a2210fSŠerif Rami [in_idx] = nominal_frames; 367*a2a2210fSŠerif Rami total_frames_in_urb += nominal_frames; 368*a2a2210fSŠerif Rami } 369*a2a2210fSŠerif Rami } 370*a2a2210fSŠerif Rami tascam->feedback_pattern_in_idx = 371*a2a2210fSŠerif Rami (tascam->feedback_pattern_in_idx + 8) % 372*a2a2210fSŠerif Rami FEEDBACK_ACCUMULATOR_SIZE; 373*a2a2210fSŠerif Rami } 374*a2a2210fSŠerif Rami 375*a2a2210fSŠerif Rami new_in_idx = tascam->feedback_pattern_in_idx; 376*a2a2210fSŠerif Rami 377*a2a2210fSŠerif Rami if (!tascam->feedback_synced) { 378*a2a2210fSŠerif Rami unsigned int out_idx = tascam->feedback_pattern_out_idx; 379*a2a2210fSŠerif Rami bool is_ahead = (new_in_idx - out_idx) % 380*a2a2210fSŠerif Rami FEEDBACK_ACCUMULATOR_SIZE < 381*a2a2210fSŠerif Rami (FEEDBACK_ACCUMULATOR_SIZE / 2); 382*a2a2210fSŠerif Rami bool was_behind = (old_in_idx - out_idx) % 383*a2a2210fSŠerif Rami FEEDBACK_ACCUMULATOR_SIZE >= 384*a2a2210fSŠerif Rami (FEEDBACK_ACCUMULATOR_SIZE / 2); 385*a2a2210fSŠerif Rami 386*a2a2210fSŠerif Rami if (is_ahead && was_behind) { 387*a2a2210fSŠerif Rami dev_dbg(tascam->card->dev, 388*a2a2210fSŠerif Rami "Sync Acquired! (in: %u, out: %u)\n", 389*a2a2210fSŠerif Rami new_in_idx, out_idx); 390*a2a2210fSŠerif Rami tascam->feedback_synced = true; 391*a2a2210fSŠerif Rami tascam->feedback_consecutive_errors = 0; 392*a2a2210fSŠerif Rami } 393*a2a2210fSŠerif Rami } 394*a2a2210fSŠerif Rami 395*a2a2210fSŠerif Rami if (total_frames_in_urb > 0) { 396*a2a2210fSŠerif Rami tascam->playback_frames_consumed += total_frames_in_urb; 397*a2a2210fSŠerif Rami if (atomic_read(&tascam->capture_active)) 398*a2a2210fSŠerif Rami tascam->capture_frames_processed += 399*a2a2210fSŠerif Rami total_frames_in_urb; 400*a2a2210fSŠerif Rami } 401*a2a2210fSŠerif Rami 402*a2a2210fSŠerif Rami if (playback_rt->period_size > 0) { 403*a2a2210fSŠerif Rami u64 current_period = 404*a2a2210fSŠerif Rami div_u64(tascam->playback_frames_consumed, 405*a2a2210fSŠerif Rami playback_rt->period_size); 406*a2a2210fSŠerif Rami 407*a2a2210fSŠerif Rami if (current_period > tascam->last_period_pos) { 408*a2a2210fSŠerif Rami tascam->last_period_pos = current_period; 409*a2a2210fSŠerif Rami playback_period_elapsed = true; 410*a2a2210fSŠerif Rami } 411*a2a2210fSŠerif Rami } 412*a2a2210fSŠerif Rami 413*a2a2210fSŠerif Rami if (atomic_read(&tascam->capture_active) && capture_rt && 414*a2a2210fSŠerif Rami capture_rt->period_size > 0) { 415*a2a2210fSŠerif Rami u64 current_capture_period = 416*a2a2210fSŠerif Rami div_u64(tascam->capture_frames_processed, 417*a2a2210fSŠerif Rami capture_rt->period_size); 418*a2a2210fSŠerif Rami 419*a2a2210fSŠerif Rami if (current_capture_period > 420*a2a2210fSŠerif Rami tascam->last_capture_period_pos) { 421*a2a2210fSŠerif Rami tascam->last_capture_period_pos = 422*a2a2210fSŠerif Rami current_capture_period; 423*a2a2210fSŠerif Rami capture_period_elapsed = true; 424*a2a2210fSŠerif Rami } 425*a2a2210fSŠerif Rami } 426*a2a2210fSŠerif Rami } 427*a2a2210fSŠerif Rami if (playback_period_elapsed) 428*a2a2210fSŠerif Rami snd_pcm_period_elapsed(playback_ss); 429*a2a2210fSŠerif Rami if (capture_period_elapsed) 430*a2a2210fSŠerif Rami snd_pcm_period_elapsed(capture_ss); 431*a2a2210fSŠerif Rami 432*a2a2210fSŠerif Rami urb->dev = tascam->dev; 433*a2a2210fSŠerif Rami usb_get_urb(urb); 434*a2a2210fSŠerif Rami usb_anchor_urb(urb, &tascam->feedback_anchor); 435*a2a2210fSŠerif Rami ret = usb_submit_urb(urb, GFP_ATOMIC); 436*a2a2210fSŠerif Rami if (ret < 0) { 437*a2a2210fSŠerif Rami dev_err_ratelimited(tascam->card->dev, 438*a2a2210fSŠerif Rami "Failed to resubmit feedback URB: %d\n", 439*a2a2210fSŠerif Rami ret); 440*a2a2210fSŠerif Rami usb_unanchor_urb(urb); 441*a2a2210fSŠerif Rami usb_put_urb(urb); 442*a2a2210fSŠerif Rami } 443*a2a2210fSŠerif Rami out: 444*a2a2210fSŠerif Rami usb_put_urb(urb); 445*a2a2210fSŠerif Rami } 446*a2a2210fSŠerif Rami 447*a2a2210fSŠerif Rami void tascam_stop_pcm_work_handler(struct work_struct *work) 448*a2a2210fSŠerif Rami { 449*a2a2210fSŠerif Rami struct tascam_card *tascam = 450*a2a2210fSŠerif Rami container_of(work, struct tascam_card, stop_pcm_work); 451*a2a2210fSŠerif Rami 452*a2a2210fSŠerif Rami if (tascam->playback_substream) 453*a2a2210fSŠerif Rami snd_pcm_stop(tascam->playback_substream, SNDRV_PCM_STATE_XRUN); 454*a2a2210fSŠerif Rami if (tascam->capture_substream) 455*a2a2210fSŠerif Rami snd_pcm_stop(tascam->capture_substream, SNDRV_PCM_STATE_XRUN); 456*a2a2210fSŠerif Rami } 457