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_capture_open() - Opens the PCM capture 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 capture 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_capture_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->capture_substream = substream; 215c8c1079SŠerif Rami atomic_set(&tascam->capture_active, 0); 225c8c1079SŠerif Rami 235c8c1079SŠerif Rami return 0; 245c8c1079SŠerif Rami } 255c8c1079SŠerif Rami 265c8c1079SŠerif Rami /** 275c8c1079SŠerif Rami * tascam_capture_close() - Closes the PCM capture substream. 285c8c1079SŠerif Rami * @substream: The ALSA PCM substream to close. 295c8c1079SŠerif Rami * 305c8c1079SŠerif Rami * This function clears the reference to the capture 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_capture_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->capture_substream = NULL; 405c8c1079SŠerif Rami 415c8c1079SŠerif Rami return 0; 425c8c1079SŠerif Rami } 435c8c1079SŠerif Rami 445c8c1079SŠerif Rami /** 455c8c1079SŠerif Rami * tascam_capture_prepare() - Prepares the PCM capture substream for use. 465c8c1079SŠerif Rami * @substream: The ALSA PCM substream to prepare. 475c8c1079SŠerif Rami * 48*c1bb0c13SŠerif Rami * This function initializes capture-related counters and ring buffer pointers. 495c8c1079SŠerif Rami * 505c8c1079SŠerif Rami * Return: 0 on success. 515c8c1079SŠerif Rami */ 525c8c1079SŠerif Rami static int tascam_capture_prepare(struct snd_pcm_substream *substream) 535c8c1079SŠerif Rami { 545c8c1079SŠerif Rami struct tascam_card *tascam = snd_pcm_substream_chip(substream); 555c8c1079SŠerif Rami 565c8c1079SŠerif Rami tascam->driver_capture_pos = 0; 575c8c1079SŠerif Rami tascam->capture_frames_processed = 0; 58a2a2210fSŠerif Rami tascam->last_capture_period_pos = 0; 59*c1bb0c13SŠerif Rami tascam->capture_ring_buffer_read_ptr = 0; 60*c1bb0c13SŠerif Rami tascam->capture_ring_buffer_write_ptr = 0; 615c8c1079SŠerif Rami 625c8c1079SŠerif Rami return 0; 635c8c1079SŠerif Rami } 645c8c1079SŠerif Rami 655c8c1079SŠerif Rami /** 665c8c1079SŠerif Rami * tascam_capture_pointer() - Returns the current capture pointer position. 675c8c1079SŠerif Rami * @substream: The ALSA PCM substream. 685c8c1079SŠerif Rami * 695c8c1079SŠerif Rami * This function returns the current position of the capture pointer within 705c8c1079SŠerif Rami * the ALSA ring buffer, in frames. 715c8c1079SŠerif Rami * 725c8c1079SŠerif Rami * Return: The current capture pointer position in frames. 735c8c1079SŠerif Rami */ 745c8c1079SŠerif Rami static snd_pcm_uframes_t 755c8c1079SŠerif Rami tascam_capture_pointer(struct snd_pcm_substream *substream) 765c8c1079SŠerif Rami { 775c8c1079SŠerif Rami struct tascam_card *tascam = snd_pcm_substream_chip(substream); 785c8c1079SŠerif Rami struct snd_pcm_runtime *runtime = substream->runtime; 795c8c1079SŠerif Rami u64 pos; 805c8c1079SŠerif Rami 815c8c1079SŠerif Rami if (!atomic_read(&tascam->capture_active)) 825c8c1079SŠerif Rami return 0; 835c8c1079SŠerif Rami 845c8c1079SŠerif Rami scoped_guard(spinlock_irqsave, &tascam->lock) { 855c8c1079SŠerif Rami pos = tascam->capture_frames_processed; 865c8c1079SŠerif Rami } 875c8c1079SŠerif Rami 885c8c1079SŠerif Rami if (runtime->buffer_size == 0) 895c8c1079SŠerif Rami return 0; 905c8c1079SŠerif Rami 915c8c1079SŠerif Rami return do_div(pos, runtime->buffer_size); 925c8c1079SŠerif Rami } 935c8c1079SŠerif Rami 945c8c1079SŠerif Rami /** 955c8c1079SŠerif Rami * tascam_capture_ops - ALSA PCM operations for capture. 965c8c1079SŠerif Rami * 975c8c1079SŠerif Rami * This structure defines the callback functions for capture stream operations, 985c8c1079SŠerif Rami * including open, close, ioctl, hardware parameters, hardware free, prepare, 995c8c1079SŠerif Rami * trigger, and pointer. 1005c8c1079SŠerif Rami */ 1015c8c1079SŠerif Rami const struct snd_pcm_ops tascam_capture_ops = { 1025c8c1079SŠerif Rami .open = tascam_capture_open, 1035c8c1079SŠerif Rami .close = tascam_capture_close, 1045c8c1079SŠerif Rami .ioctl = snd_pcm_lib_ioctl, 1055c8c1079SŠerif Rami .hw_params = tascam_pcm_hw_params, 1065c8c1079SŠerif Rami .hw_free = tascam_pcm_hw_free, 1075c8c1079SŠerif Rami .prepare = tascam_capture_prepare, 1085c8c1079SŠerif Rami .trigger = tascam_pcm_trigger, 1095c8c1079SŠerif Rami .pointer = tascam_capture_pointer, 1105c8c1079SŠerif Rami }; 111*c1bb0c13SŠerif Rami 112*c1bb0c13SŠerif Rami /** 113*c1bb0c13SŠerif Rami * decode_tascam_capture_block() - Decodes a raw 512-byte block from the device. 114*c1bb0c13SŠerif Rami * @src_block: Pointer to the 512-byte raw source block. 115*c1bb0c13SŠerif Rami * @dst_block: Pointer to the destination buffer for decoded audio frames. 116*c1bb0c13SŠerif Rami * 117*c1bb0c13SŠerif Rami * The device sends audio data in a complex, multiplexed format. This function 118*c1bb0c13SŠerif Rami * demultiplexes the bits from the raw block into 8 frames of 4-channel, 119*c1bb0c13SŠerif Rami * 24-bit audio (stored in 32-bit containers). 120*c1bb0c13SŠerif Rami */ 121*c1bb0c13SŠerif Rami static void decode_tascam_capture_block(const u8 *src_block, s32 *dst_block) 122*c1bb0c13SŠerif Rami { 123*c1bb0c13SŠerif Rami int frame, bit; 124*c1bb0c13SŠerif Rami 125*c1bb0c13SŠerif Rami memset(dst_block, 0, 126*c1bb0c13SŠerif Rami FRAMES_PER_DECODE_BLOCK * DECODED_CHANNELS_PER_FRAME * 127*c1bb0c13SŠerif Rami DECODED_SAMPLE_SIZE); 128*c1bb0c13SŠerif Rami 129*c1bb0c13SŠerif Rami for (frame = 0; frame < FRAMES_PER_DECODE_BLOCK; ++frame) { 130*c1bb0c13SŠerif Rami const u8 *p_src_frame_base = src_block + frame * 64; 131*c1bb0c13SŠerif Rami s32 *p_dst_frame = dst_block + frame * 4; 132*c1bb0c13SŠerif Rami 133*c1bb0c13SŠerif Rami s32 ch[4] = { 0 }; 134*c1bb0c13SŠerif Rami 135*c1bb0c13SŠerif Rami for (bit = 0; bit < 24; ++bit) { 136*c1bb0c13SŠerif Rami u8 byte1 = p_src_frame_base[bit]; 137*c1bb0c13SŠerif Rami u8 byte2 = p_src_frame_base[bit + 32]; 138*c1bb0c13SŠerif Rami 139*c1bb0c13SŠerif Rami ch[0] = (ch[0] << 1) | (byte1 & 1); 140*c1bb0c13SŠerif Rami ch[2] = (ch[2] << 1) | ((byte1 >> 1) & 1); 141*c1bb0c13SŠerif Rami 142*c1bb0c13SŠerif Rami ch[1] = (ch[1] << 1) | (byte2 & 1); 143*c1bb0c13SŠerif Rami ch[3] = (ch[3] << 1) | ((byte2 >> 1) & 1); 144*c1bb0c13SŠerif Rami } 145*c1bb0c13SŠerif Rami 146*c1bb0c13SŠerif Rami /* 147*c1bb0c13SŠerif Rami * The result is a 24-bit sample. Shift left by 8 to align it to 148*c1bb0c13SŠerif Rami * the most significant bits of a 32-bit integer (S32_LE format). 149*c1bb0c13SŠerif Rami */ 150*c1bb0c13SŠerif Rami p_dst_frame[0] = ch[0] << 8; 151*c1bb0c13SŠerif Rami p_dst_frame[1] = ch[1] << 8; 152*c1bb0c13SŠerif Rami p_dst_frame[2] = ch[2] << 8; 153*c1bb0c13SŠerif Rami p_dst_frame[3] = ch[3] << 8; 154*c1bb0c13SŠerif Rami } 155*c1bb0c13SŠerif Rami } 156*c1bb0c13SŠerif Rami 157*c1bb0c13SŠerif Rami void tascam_capture_work_handler(struct work_struct *work) 158*c1bb0c13SŠerif Rami { 159*c1bb0c13SŠerif Rami struct tascam_card *tascam = 160*c1bb0c13SŠerif Rami container_of(work, struct tascam_card, capture_work); 161*c1bb0c13SŠerif Rami struct snd_pcm_substream *substream = tascam->capture_substream; 162*c1bb0c13SŠerif Rami struct snd_pcm_runtime *runtime; 163*c1bb0c13SŠerif Rami u8 *raw_block = tascam->capture_decode_raw_block; 164*c1bb0c13SŠerif Rami s32 *decoded_block = tascam->capture_decode_dst_block; 165*c1bb0c13SŠerif Rami s32 *routed_block = tascam->capture_routing_buffer; 166*c1bb0c13SŠerif Rami 167*c1bb0c13SŠerif Rami if (!substream || !substream->runtime) 168*c1bb0c13SŠerif Rami return; 169*c1bb0c13SŠerif Rami runtime = substream->runtime; 170*c1bb0c13SŠerif Rami 171*c1bb0c13SŠerif Rami if (!raw_block || !decoded_block || !routed_block) { 172*c1bb0c13SŠerif Rami dev_err(tascam->card->dev, 173*c1bb0c13SŠerif Rami "Capture decode/routing buffers not allocated!\n"); 174*c1bb0c13SŠerif Rami return; 175*c1bb0c13SŠerif Rami } 176*c1bb0c13SŠerif Rami 177*c1bb0c13SŠerif Rami while (atomic_read(&tascam->capture_active)) { 178*c1bb0c13SŠerif Rami size_t write_ptr, read_ptr, available_data; 179*c1bb0c13SŠerif Rami bool can_process; 180*c1bb0c13SŠerif Rami 181*c1bb0c13SŠerif Rami scoped_guard(spinlock_irqsave, &tascam->lock) { 182*c1bb0c13SŠerif Rami write_ptr = tascam->capture_ring_buffer_write_ptr; 183*c1bb0c13SŠerif Rami read_ptr = tascam->capture_ring_buffer_read_ptr; 184*c1bb0c13SŠerif Rami available_data = (write_ptr >= read_ptr) ? 185*c1bb0c13SŠerif Rami (write_ptr - read_ptr) : 186*c1bb0c13SŠerif Rami (CAPTURE_RING_BUFFER_SIZE - 187*c1bb0c13SŠerif Rami read_ptr + write_ptr); 188*c1bb0c13SŠerif Rami can_process = 189*c1bb0c13SŠerif Rami (available_data >= RAW_BYTES_PER_DECODE_BLOCK); 190*c1bb0c13SŠerif Rami 191*c1bb0c13SŠerif Rami if (can_process) { 192*c1bb0c13SŠerif Rami size_t bytes_to_end = 193*c1bb0c13SŠerif Rami CAPTURE_RING_BUFFER_SIZE - read_ptr; 194*c1bb0c13SŠerif Rami if (bytes_to_end >= 195*c1bb0c13SŠerif Rami RAW_BYTES_PER_DECODE_BLOCK) { 196*c1bb0c13SŠerif Rami memcpy(raw_block, 197*c1bb0c13SŠerif Rami tascam->capture_ring_buffer + 198*c1bb0c13SŠerif Rami read_ptr, 199*c1bb0c13SŠerif Rami RAW_BYTES_PER_DECODE_BLOCK); 200*c1bb0c13SŠerif Rami } else { 201*c1bb0c13SŠerif Rami memcpy(raw_block, 202*c1bb0c13SŠerif Rami tascam->capture_ring_buffer + 203*c1bb0c13SŠerif Rami read_ptr, 204*c1bb0c13SŠerif Rami bytes_to_end); 205*c1bb0c13SŠerif Rami memcpy(raw_block + bytes_to_end, 206*c1bb0c13SŠerif Rami tascam->capture_ring_buffer, 207*c1bb0c13SŠerif Rami RAW_BYTES_PER_DECODE_BLOCK - 208*c1bb0c13SŠerif Rami bytes_to_end); 209*c1bb0c13SŠerif Rami } 210*c1bb0c13SŠerif Rami tascam->capture_ring_buffer_read_ptr = 211*c1bb0c13SŠerif Rami (read_ptr + 212*c1bb0c13SŠerif Rami RAW_BYTES_PER_DECODE_BLOCK) % 213*c1bb0c13SŠerif Rami CAPTURE_RING_BUFFER_SIZE; 214*c1bb0c13SŠerif Rami } 215*c1bb0c13SŠerif Rami } 216*c1bb0c13SŠerif Rami 217*c1bb0c13SŠerif Rami if (!can_process) 218*c1bb0c13SŠerif Rami break; 219*c1bb0c13SŠerif Rami 220*c1bb0c13SŠerif Rami decode_tascam_capture_block(raw_block, decoded_block); 221*c1bb0c13SŠerif Rami process_capture_routing_us144mkii(tascam, decoded_block, 222*c1bb0c13SŠerif Rami routed_block); 223*c1bb0c13SŠerif Rami 224*c1bb0c13SŠerif Rami scoped_guard(spinlock_irqsave, &tascam->lock) { 225*c1bb0c13SŠerif Rami if (atomic_read(&tascam->capture_active)) { 226*c1bb0c13SŠerif Rami int f; 227*c1bb0c13SŠerif Rami 228*c1bb0c13SŠerif Rami for (f = 0; f < FRAMES_PER_DECODE_BLOCK; ++f) { 229*c1bb0c13SŠerif Rami u8 *dst_frame_start = 230*c1bb0c13SŠerif Rami runtime->dma_area + 231*c1bb0c13SŠerif Rami frames_to_bytes( 232*c1bb0c13SŠerif Rami runtime, 233*c1bb0c13SŠerif Rami tascam->driver_capture_pos); 234*c1bb0c13SŠerif Rami s32 *routed_frame_start = 235*c1bb0c13SŠerif Rami routed_block + 236*c1bb0c13SŠerif Rami (f * NUM_CHANNELS); 237*c1bb0c13SŠerif Rami int c; 238*c1bb0c13SŠerif Rami 239*c1bb0c13SŠerif Rami for (c = 0; c < NUM_CHANNELS; c++) { 240*c1bb0c13SŠerif Rami u8 *dst_channel = 241*c1bb0c13SŠerif Rami dst_frame_start + 242*c1bb0c13SŠerif Rami (c * BYTES_PER_SAMPLE); 243*c1bb0c13SŠerif Rami s32 *src_channel_s32 = 244*c1bb0c13SŠerif Rami routed_frame_start + c; 245*c1bb0c13SŠerif Rami 246*c1bb0c13SŠerif Rami memcpy(dst_channel, 247*c1bb0c13SŠerif Rami ((char *)src_channel_s32) + 248*c1bb0c13SŠerif Rami 1, 249*c1bb0c13SŠerif Rami 3); 250*c1bb0c13SŠerif Rami } 251*c1bb0c13SŠerif Rami 252*c1bb0c13SŠerif Rami tascam->driver_capture_pos = 253*c1bb0c13SŠerif Rami (tascam->driver_capture_pos + 254*c1bb0c13SŠerif Rami 1) % 255*c1bb0c13SŠerif Rami runtime->buffer_size; 256*c1bb0c13SŠerif Rami } 257*c1bb0c13SŠerif Rami } 258*c1bb0c13SŠerif Rami } 259*c1bb0c13SŠerif Rami } 260*c1bb0c13SŠerif Rami } 261*c1bb0c13SŠerif Rami 262*c1bb0c13SŠerif Rami void capture_urb_complete(struct urb *urb) 263*c1bb0c13SŠerif Rami { 264*c1bb0c13SŠerif Rami struct tascam_card *tascam = urb->context; 265*c1bb0c13SŠerif Rami int ret; 266*c1bb0c13SŠerif Rami 267*c1bb0c13SŠerif Rami if (urb->status) { 268*c1bb0c13SŠerif Rami if (urb->status != -ENOENT && urb->status != -ECONNRESET && 269*c1bb0c13SŠerif Rami urb->status != -ESHUTDOWN && urb->status != -ENODEV && 270*c1bb0c13SŠerif Rami urb->status != -EPROTO) 271*c1bb0c13SŠerif Rami dev_err_ratelimited(tascam->card->dev, 272*c1bb0c13SŠerif Rami "Capture URB failed: %d\n", 273*c1bb0c13SŠerif Rami urb->status); 274*c1bb0c13SŠerif Rami goto out; 275*c1bb0c13SŠerif Rami } 276*c1bb0c13SŠerif Rami if (!tascam || !atomic_read(&tascam->capture_active)) 277*c1bb0c13SŠerif Rami goto out; 278*c1bb0c13SŠerif Rami 279*c1bb0c13SŠerif Rami if (urb->actual_length > 0) { 280*c1bb0c13SŠerif Rami scoped_guard(spinlock_irqsave, &tascam->lock) { 281*c1bb0c13SŠerif Rami size_t write_ptr = tascam->capture_ring_buffer_write_ptr; 282*c1bb0c13SŠerif Rami size_t bytes_to_end = CAPTURE_RING_BUFFER_SIZE - write_ptr; 283*c1bb0c13SŠerif Rami 284*c1bb0c13SŠerif Rami if (urb->actual_length > bytes_to_end) { 285*c1bb0c13SŠerif Rami memcpy(tascam->capture_ring_buffer + write_ptr, 286*c1bb0c13SŠerif Rami urb->transfer_buffer, bytes_to_end); 287*c1bb0c13SŠerif Rami memcpy(tascam->capture_ring_buffer, 288*c1bb0c13SŠerif Rami urb->transfer_buffer + bytes_to_end, 289*c1bb0c13SŠerif Rami urb->actual_length - bytes_to_end); 290*c1bb0c13SŠerif Rami } else { 291*c1bb0c13SŠerif Rami memcpy(tascam->capture_ring_buffer + write_ptr, 292*c1bb0c13SŠerif Rami urb->transfer_buffer, 293*c1bb0c13SŠerif Rami urb->actual_length); 294*c1bb0c13SŠerif Rami } 295*c1bb0c13SŠerif Rami 296*c1bb0c13SŠerif Rami tascam->capture_ring_buffer_write_ptr = 297*c1bb0c13SŠerif Rami (write_ptr + urb->actual_length) % 298*c1bb0c13SŠerif Rami CAPTURE_RING_BUFFER_SIZE; 299*c1bb0c13SŠerif Rami } 300*c1bb0c13SŠerif Rami 301*c1bb0c13SŠerif Rami schedule_work(&tascam->capture_work); 302*c1bb0c13SŠerif Rami } 303*c1bb0c13SŠerif Rami 304*c1bb0c13SŠerif Rami usb_get_urb(urb); 305*c1bb0c13SŠerif Rami usb_anchor_urb(urb, &tascam->capture_anchor); 306*c1bb0c13SŠerif Rami ret = usb_submit_urb(urb, GFP_ATOMIC); 307*c1bb0c13SŠerif Rami if (ret < 0) { 308*c1bb0c13SŠerif Rami dev_err_ratelimited(tascam->card->dev, 309*c1bb0c13SŠerif Rami "Failed to resubmit capture URB: %d\n", 310*c1bb0c13SŠerif Rami ret); 311*c1bb0c13SŠerif Rami usb_unanchor_urb(urb); 312*c1bb0c13SŠerif Rami usb_put_urb(urb); 313*c1bb0c13SŠerif Rami atomic_dec( 314*c1bb0c13SŠerif Rami &tascam->active_urbs); /* Decrement on failed resubmission */ 315*c1bb0c13SŠerif Rami } 316*c1bb0c13SŠerif Rami out: 317*c1bb0c13SŠerif Rami usb_put_urb(urb); 318*c1bb0c13SŠerif Rami } 319*c1bb0c13SŠerif Rami 320