xref: /linux/sound/usb/usx2y/us144mkii_playback.c (revision 05a54fa773284d1a7923cdfdd8f0c8dabb98bd26)
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