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