improvements

This commit is contained in:
Šerif Rami 2026-01-06 20:01:07 +01:00
parent 7716f17b21
commit c379dc65c1
6 changed files with 202 additions and 161 deletions

View File

@ -87,7 +87,8 @@ int tascam_alloc_urbs(struct tascam_card *tascam)
{ {
int i; int i;
tascam->playback_urb_alloc_size = PLAYBACK_URB_PACKETS * (12 + 2) * BYTES_PER_FRAME; tascam->playback_urb_alloc_size = PLAYBACK_URB_PACKETS * 156;
for (i = 0; i < NUM_PLAYBACK_URBS; i++) { for (i = 0; i < NUM_PLAYBACK_URBS; i++) {
struct urb *urb = usb_alloc_urb(PLAYBACK_URB_PACKETS, GFP_KERNEL); struct urb *urb = usb_alloc_urb(PLAYBACK_URB_PACKETS, GFP_KERNEL);
@ -129,12 +130,13 @@ int tascam_alloc_urbs(struct tascam_card *tascam)
for (i = 0; i < NUM_CAPTURE_URBS; i++) { for (i = 0; i < NUM_CAPTURE_URBS; i++) {
struct urb *urb = usb_alloc_urb(0, GFP_KERNEL); struct urb *urb = usb_alloc_urb(0, GFP_KERNEL);
void *buf;
if (!urb) if (!urb)
return -ENOMEM; return -ENOMEM;
tascam->capture_urbs[i] = urb; tascam->capture_urbs[i] = urb;
void *buf = usb_alloc_coherent(tascam->dev, CAPTURE_PACKET_SIZE, buf = usb_alloc_coherent(tascam->dev, CAPTURE_PACKET_SIZE,
GFP_KERNEL, &urb->transfer_dma); GFP_KERNEL, &urb->transfer_dma);
if (!buf) if (!buf)
return -ENOMEM; return -ENOMEM;
usb_fill_bulk_urb(urb, tascam->dev, usb_fill_bulk_urb(urb, tascam->dev,
@ -188,9 +190,6 @@ static int tascam_suspend(struct usb_interface *intf, pm_message_t message)
usb_kill_anchored_urbs(&tascam->capture_anchor); usb_kill_anchored_urbs(&tascam->capture_anchor);
usb_kill_anchored_urbs(&tascam->midi_anchor); usb_kill_anchored_urbs(&tascam->midi_anchor);
usb_control_msg(tascam->dev, usb_sndctrlpipe(tascam->dev, 0),
VENDOR_REQ_DEEP_SLEEP, RT_H2D_VENDOR_DEV,
0x0000, 0x0000, NULL, 0, USB_CTRL_TIMEOUT_MS);
return 0; return 0;
} }
@ -198,6 +197,8 @@ static int tascam_resume(struct usb_interface *intf)
{ {
struct tascam_card *tascam = usb_get_intfdata(intf); struct tascam_card *tascam = usb_get_intfdata(intf);
int err; int err;
unsigned long flags;
int current_rate;
if (!tascam) if (!tascam)
return 0; return 0;
@ -205,12 +206,17 @@ static int tascam_resume(struct usb_interface *intf)
err = usb_set_interface(tascam->dev, 0, 1); err = usb_set_interface(tascam->dev, 0, 1);
if (err < 0) if (err < 0)
return err; return err;
err = usb_set_interface(tascam->dev, 1, 1); err = usb_set_interface(tascam->dev, 1, 1);
if (err < 0) if (err < 0)
return err; return err;
if (tascam->current_rate > 0) spin_lock_irqsave(&tascam->lock, flags);
us144mkii_configure_device_for_rate(tascam, tascam->current_rate); current_rate = tascam->current_rate;
spin_unlock_irqrestore(&tascam->lock, flags);
if (current_rate > 0)
us144mkii_configure_device_for_rate(tascam, current_rate);
return 0; return 0;
} }
@ -249,6 +255,8 @@ static int tascam_probe(struct usb_interface *intf, const struct usb_device_id *
tascam->card = card; tascam->card = card;
tascam->iface0 = intf; tascam->iface0 = intf;
tascam->dev_id = le16_to_cpu(dev->descriptor.idProduct);
spin_lock_init(&tascam->lock); spin_lock_init(&tascam->lock);
init_usb_anchor(&tascam->playback_anchor); init_usb_anchor(&tascam->playback_anchor);
init_usb_anchor(&tascam->feedback_anchor); init_usb_anchor(&tascam->feedback_anchor);
@ -259,7 +267,7 @@ static int tascam_probe(struct usb_interface *intf, const struct usb_device_id *
INIT_WORK(&tascam->stop_pcm_work, tascam_stop_pcm_work_handler); INIT_WORK(&tascam->stop_pcm_work, tascam_stop_pcm_work_handler);
strscpy(card->driver, DRIVER_NAME, sizeof(card->driver)); strscpy(card->driver, DRIVER_NAME, sizeof(card->driver));
if (le16_to_cpu(dev->descriptor.idProduct) == USB_PID_TASCAM_US144) if (tascam->dev_id == USB_PID_TASCAM_US144)
strscpy(card->shortname, "US-144", sizeof(card->shortname)); strscpy(card->shortname, "US-144", sizeof(card->shortname));
else else
strscpy(card->shortname, "US-144MKII", sizeof(card->shortname)); strscpy(card->shortname, "US-144MKII", sizeof(card->shortname));
@ -326,7 +334,7 @@ static int tascam_probe(struct usb_interface *intf, const struct usb_device_id *
usb_set_intfdata(intf, tascam); usb_set_intfdata(intf, tascam);
return 0; return 0;
free_card: free_card:
snd_card_free(card); snd_card_free(card);
atomic_dec(&dev_idx); atomic_dec(&dev_idx);
return err; return err;

View File

@ -41,7 +41,6 @@ enum uac_control_selector {
enum tascam_vendor_request { enum tascam_vendor_request {
VENDOR_REQ_REGISTER_WRITE = 0x41, VENDOR_REQ_REGISTER_WRITE = 0x41,
VENDOR_REQ_DEEP_SLEEP = 0x44,
VENDOR_REQ_MODE_CONTROL = 0x49, VENDOR_REQ_MODE_CONTROL = 0x49,
}; };
@ -52,25 +51,25 @@ enum tascam_mode_value {
}; };
enum tascam_register { enum tascam_register {
REG_ADDR_UNKNOWN_0D = 0x0d04, REG_ADDR_INIT_0D = 0x0d04,
REG_ADDR_UNKNOWN_0E = 0x0e00, REG_ADDR_INIT_0E = 0x0e00,
REG_ADDR_UNKNOWN_0F = 0x0f00, REG_ADDR_INIT_0F = 0x0f00,
REG_ADDR_RATE_44100 = 0x1000, REG_ADDR_RATE_44100 = 0x1000,
REG_ADDR_RATE_48000 = 0x1002, REG_ADDR_RATE_48000 = 0x1002,
REG_ADDR_RATE_88200 = 0x1008, REG_ADDR_RATE_88200 = 0x1008,
REG_ADDR_RATE_96000 = 0x100a, REG_ADDR_RATE_96000 = 0x100a,
REG_ADDR_UNKNOWN_11 = 0x110b, REG_ADDR_INIT_11 = 0x110b,
}; };
#define REG_VAL_ENABLE 0x0101 #define REG_VAL_ENABLE 0x0101
#define NUM_PLAYBACK_URBS 4 #define NUM_PLAYBACK_URBS 2
#define PLAYBACK_URB_PACKETS 4 #define PLAYBACK_URB_PACKETS 8
#define NUM_FEEDBACK_URBS 2 #define NUM_FEEDBACK_URBS 4
#define FEEDBACK_URB_PACKETS 1 #define FEEDBACK_URB_PACKETS 1
#define FEEDBACK_PACKET_SIZE 3 #define FEEDBACK_PACKET_SIZE 3
#define NUM_CAPTURE_URBS 8 #define NUM_CAPTURE_URBS 8
#define CAPTURE_PACKET_SIZE 512 #define CAPTURE_PACKET_SIZE 4096
#define MIDI_PACKET_SIZE 9 #define MIDI_PACKET_SIZE 9
#define MIDI_PAYLOAD_SIZE 8 #define MIDI_PAYLOAD_SIZE 8
@ -137,6 +136,8 @@ struct tascam_card {
struct snd_pcm *pcm; struct snd_pcm *pcm;
struct snd_rawmidi *rmidi; struct snd_rawmidi *rmidi;
u16 dev_id;
u8 *scratch_buf; u8 *scratch_buf;
struct snd_pcm_substream *playback_substream; struct snd_pcm_substream *playback_substream;

View File

@ -16,7 +16,7 @@ const struct snd_pcm_hardware tascam_capture_hw = {
.channels_min = NUM_CHANNELS, .channels_min = NUM_CHANNELS,
.channels_max = NUM_CHANNELS, .channels_max = NUM_CHANNELS,
.buffer_bytes_max = 1024 * 1024, .buffer_bytes_max = 1024 * 1024,
.period_bytes_min = 32 * BYTES_PER_FRAME, .period_bytes_min = 48 * BYTES_PER_FRAME,
.period_bytes_max = 1024 * BYTES_PER_FRAME, .period_bytes_max = 1024 * BYTES_PER_FRAME,
.periods_min = 2, .periods_min = 2,
.periods_max = 1024, .periods_max = 1024,
@ -80,27 +80,26 @@ static int tascam_capture_trigger(struct snd_pcm_substream *substream, int cmd)
spin_lock_irqsave(&tascam->lock, flags); spin_lock_irqsave(&tascam->lock, flags);
switch (cmd) { switch (cmd) {
case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_RESUME:
if (!atomic_read(&tascam->capture_active)) { if (!atomic_read(&tascam->capture_active)) {
atomic_set(&tascam->capture_active, 1); atomic_set(&tascam->capture_active, 1);
start = true; start = true;
} }
break; break;
case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
atomic_set(&tascam->capture_active, 0); atomic_set(&tascam->capture_active, 0);
stop = true; stop = true;
break; break;
default: default:
ret = -EINVAL; ret = -EINVAL;
break; break;
} }
spin_unlock_irqrestore(&tascam->lock, flags); spin_unlock_irqrestore(&tascam->lock, flags);
if (stop) { if (stop) {
/* Ensure capture_active is updated before unlinking URBs. */
smp_mb(); smp_mb();
for (i = 0; i < NUM_CAPTURE_URBS; i++) { for (i = 0; i < NUM_CAPTURE_URBS; i++) {
if (tascam->capture_urbs[i]) if (tascam->capture_urbs[i])
@ -114,7 +113,6 @@ static int tascam_capture_trigger(struct snd_pcm_substream *substream, int cmd)
if (usb_submit_urb(tascam->capture_urbs[i], GFP_ATOMIC) < 0) { if (usb_submit_urb(tascam->capture_urbs[i], GFP_ATOMIC) < 0) {
usb_unanchor_urb(tascam->capture_urbs[i]); usb_unanchor_urb(tascam->capture_urbs[i]);
atomic_set(&tascam->capture_active, 0); atomic_set(&tascam->capture_active, 0);
/* Ensure capture_active is updated before unlinking URBs due to submission error. */
smp_mb(); smp_mb();
for (int j = 0; j < i; j++) for (int j = 0; j < i; j++)
usb_unlink_urb(tascam->capture_urbs[j]); usb_unlink_urb(tascam->capture_urbs[j]);
@ -130,16 +128,26 @@ static int tascam_capture_trigger(struct snd_pcm_substream *substream, int cmd)
static inline void tascam_unpack_8bytes(const u8 *src, u8 *out_bit0, u8 *out_bit1) static inline void tascam_unpack_8bytes(const u8 *src, u8 *out_bit0, u8 *out_bit1)
{ {
u64 val = get_unaligned_le64(src); u64 val = get_unaligned_le64(src);
u8 b0 = 0, b1 = 0;
int i;
for (i = 0; i < 8; i++) { *out_bit0 =
b0 |= ((val >> (i * 8)) & 1) << (7 - i); (((val >> 0) & 1) << 7) |
b1 |= ((val >> (i * 8 + 1)) & 1) << (7 - i); (((val >> 8) & 1) << 6) |
} (((val >> 16) & 1) << 5) |
(((val >> 24) & 1) << 4) |
(((val >> 32) & 1) << 3) |
(((val >> 40) & 1) << 2) |
(((val >> 48) & 1) << 1) |
(((val >> 56) & 1) << 0);
*out_bit0 = b0; *out_bit1 =
*out_bit1 = b1; (((val >> 1) & 1) << 7) |
(((val >> 9) & 1) << 6) |
(((val >> 17) & 1) << 5) |
(((val >> 25) & 1) << 4) |
(((val >> 33) & 1) << 3) |
(((val >> 41) & 1) << 2) |
(((val >> 49) & 1) << 1) |
(((val >> 57) & 1) << 0);
} }
static void tascam_decode_capture_chunk(const u8 *src, u32 *dst, int frames_to_decode) static void tascam_decode_capture_chunk(const u8 *src, u32 *dst, int frames_to_decode)
@ -151,12 +159,10 @@ static void tascam_decode_capture_chunk(const u8 *src, u32 *dst, int frames_to_d
const u8 *p_src_a = src + (i * 64); const u8 *p_src_a = src + (i * 64);
const u8 *p_src_b = src + (i * 64) + 32; const u8 *p_src_b = src + (i * 64) + 32;
/* Channel 1 (h0) and Channel 3 (h2) */
tascam_unpack_8bytes(p_src_a, &h[0], &h[2]); tascam_unpack_8bytes(p_src_a, &h[0], &h[2]);
tascam_unpack_8bytes(p_src_a + 8, &m[0], &m[2]); tascam_unpack_8bytes(p_src_a + 8, &m[0], &m[2]);
tascam_unpack_8bytes(p_src_a + 16, &l[0], &l[2]); tascam_unpack_8bytes(p_src_a + 16, &l[0], &l[2]);
/* Channel 2 (h1) and Channel 4 (h3) */
tascam_unpack_8bytes(p_src_b, &h[1], &h[3]); tascam_unpack_8bytes(p_src_b, &h[1], &h[3]);
tascam_unpack_8bytes(p_src_b + 8, &m[1], &m[3]); tascam_unpack_8bytes(p_src_b + 8, &m[1], &m[3]);
tascam_unpack_8bytes(p_src_b + 16, &l[1], &l[3]); tascam_unpack_8bytes(p_src_b + 16, &l[1], &l[3]);
@ -168,14 +174,6 @@ static void tascam_decode_capture_chunk(const u8 *src, u32 *dst, int frames_to_d
} }
} }
/**
* capture_urb_complete() - completion handler for capture bulk URBs
* @urb: the completed URB
*
* This function runs in interrupt context. It copies the received raw data
* into an intermediate ring buffer and then schedules the workqueue to process
* it. It then resubmits the URB to receive more data.
*/
void capture_urb_complete(struct urb *urb) void capture_urb_complete(struct urb *urb)
{ {
struct tascam_card *tascam = urb->context; struct tascam_card *tascam = urb->context;
@ -190,18 +188,27 @@ void capture_urb_complete(struct urb *urb)
if (!tascam) if (!tascam)
return; return;
if (!tascam->dev) {
usb_unanchor_urb(urb);
atomic_dec(&tascam->active_urbs);
return;
}
if (urb->status) { if (urb->status) {
usb_unanchor_urb(urb);
atomic_dec(&tascam->active_urbs); atomic_dec(&tascam->active_urbs);
return; return;
} }
substream = tascam->capture_substream; substream = tascam->capture_substream;
if (!substream || !substream->runtime) { if (!substream || !substream->runtime) {
usb_unanchor_urb(urb);
atomic_dec(&tascam->active_urbs); atomic_dec(&tascam->active_urbs);
return; return;
} }
runtime = substream->runtime; runtime = substream->runtime;
if (!runtime->dma_area) { if (!runtime->dma_area) {
usb_unanchor_urb(urb);
atomic_dec(&tascam->active_urbs); atomic_dec(&tascam->active_urbs);
return; return;
} }
@ -223,7 +230,6 @@ void capture_urb_complete(struct urb *urb)
} }
write_pos = tascam->driver_capture_pos; write_pos = tascam->driver_capture_pos;
u32 *dma_ptr = (u32 *)(runtime->dma_area + frames_to_bytes(runtime, write_pos)); u32 *dma_ptr = (u32 *)(runtime->dma_area + frames_to_bytes(runtime, write_pos));
if (write_pos + frames_received <= buffer_size) { if (write_pos + frames_received <= buffer_size) {
@ -260,6 +266,7 @@ void capture_urb_complete(struct urb *urb)
if (usb_submit_urb(urb, GFP_ATOMIC) < 0) { if (usb_submit_urb(urb, GFP_ATOMIC) < 0) {
usb_unanchor_urb(urb); usb_unanchor_urb(urb);
atomic_dec(&tascam->active_urbs); atomic_dec(&tascam->active_urbs);
return;
} }
} }

View File

@ -22,10 +22,11 @@ static void tascam_midi_out_complete(struct urb *urb)
spin_unlock_irqrestore(&tascam->midi_lock, flags); spin_unlock_irqrestore(&tascam->midi_lock, flags);
return; return;
} }
spin_unlock_irqrestore(&tascam->midi_lock, flags);
if (!active) if (!active) {
spin_unlock_irqrestore(&tascam->midi_lock, flags);
return; return;
}
count = snd_rawmidi_transmit(substream, tascam->midi_out_buf, MIDI_PAYLOAD_SIZE); count = snd_rawmidi_transmit(substream, tascam->midi_out_buf, MIDI_PAYLOAD_SIZE);
if (count > 0) { if (count > 0) {
@ -36,20 +37,18 @@ static void tascam_midi_out_complete(struct urb *urb)
urb->transfer_buffer_length = MIDI_PACKET_SIZE; urb->transfer_buffer_length = MIDI_PACKET_SIZE;
submit = true; submit = true;
} else { } else {
spin_lock_irqsave(&tascam->midi_lock, flags);
tascam->midi_out_active = false; tascam->midi_out_active = false;
spin_unlock_irqrestore(&tascam->midi_lock, flags);
} }
if (submit) { if (submit) {
usb_anchor_urb(urb, &tascam->midi_anchor); usb_anchor_urb(urb, &tascam->midi_anchor);
if (usb_submit_urb(urb, GFP_ATOMIC) < 0) { if (usb_submit_urb(urb, GFP_ATOMIC) < 0) {
usb_unanchor_urb(urb); usb_unanchor_urb(urb);
spin_lock_irqsave(&tascam->midi_lock, flags);
tascam->midi_out_active = false; tascam->midi_out_active = false;
spin_unlock_irqrestore(&tascam->midi_lock, flags);
} }
} }
spin_unlock_irqrestore(&tascam->midi_lock, flags);
} }
static void tascam_midi_output_trigger(struct snd_rawmidi_substream *substream, int up) static void tascam_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
@ -57,18 +56,14 @@ static void tascam_midi_output_trigger(struct snd_rawmidi_substream *substream,
struct tascam_card *tascam = substream->rmidi->private_data; struct tascam_card *tascam = substream->rmidi->private_data;
unsigned long flags; unsigned long flags;
int count; int count;
bool do_submit = false;
spin_lock_irqsave(&tascam->midi_lock, flags);
if (up) { if (up) {
spin_lock_irqsave(&tascam->midi_lock, flags);
tascam->midi_output = substream; tascam->midi_output = substream;
if (!tascam->midi_out_active) { if (!tascam->midi_out_active) {
tascam->midi_out_active = true; tascam->midi_out_active = true;
do_submit = true;
}
spin_unlock_irqrestore(&tascam->midi_lock, flags);
if (do_submit) {
count = snd_rawmidi_transmit(substream, tascam->midi_out_buf, MIDI_PAYLOAD_SIZE); count = snd_rawmidi_transmit(substream, tascam->midi_out_buf, MIDI_PAYLOAD_SIZE);
if (count > 0) { if (count > 0) {
if (count < MIDI_PAYLOAD_SIZE) if (count < MIDI_PAYLOAD_SIZE)
@ -80,21 +75,17 @@ static void tascam_midi_output_trigger(struct snd_rawmidi_substream *substream,
usb_anchor_urb(tascam->midi_out_urb, &tascam->midi_anchor); usb_anchor_urb(tascam->midi_out_urb, &tascam->midi_anchor);
if (usb_submit_urb(tascam->midi_out_urb, GFP_ATOMIC) < 0) { if (usb_submit_urb(tascam->midi_out_urb, GFP_ATOMIC) < 0) {
usb_unanchor_urb(tascam->midi_out_urb); usb_unanchor_urb(tascam->midi_out_urb);
spin_lock_irqsave(&tascam->midi_lock, flags);
tascam->midi_out_active = false; tascam->midi_out_active = false;
spin_unlock_irqrestore(&tascam->midi_lock, flags);
} }
} else { } else {
spin_lock_irqsave(&tascam->midi_lock, flags);
tascam->midi_out_active = false; tascam->midi_out_active = false;
spin_unlock_irqrestore(&tascam->midi_lock, flags);
} }
} }
} else { } else {
spin_lock_irqsave(&tascam->midi_lock, flags);
tascam->midi_output = NULL; tascam->midi_output = NULL;
spin_unlock_irqrestore(&tascam->midi_lock, flags);
} }
spin_unlock_irqrestore(&tascam->midi_lock, flags);
} }
static void tascam_midi_in_complete(struct urb *urb) static void tascam_midi_in_complete(struct urb *urb)

View File

@ -11,7 +11,7 @@ static int tascam_write_regs(struct tascam_card *tascam, const u16 *regs, size_t
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
VENDOR_REQ_REGISTER_WRITE, RT_H2D_VENDOR_DEV, VENDOR_REQ_REGISTER_WRITE, RT_H2D_VENDOR_DEV,
regs[i], REG_VAL_ENABLE, NULL, 0, USB_CTRL_TIMEOUT_MS); regs[i], REG_VAL_ENABLE, NULL, 0, USB_CTRL_TIMEOUT_MS);
if (err < 0) if (err < 0)
return err; return err;
} }
@ -31,74 +31,77 @@ static int tascam_write_regs(struct tascam_card *tascam, const u16 *regs, size_t
int us144mkii_configure_device_for_rate(struct tascam_card *tascam, int rate) int us144mkii_configure_device_for_rate(struct tascam_card *tascam, int rate)
{ {
struct usb_device *dev = tascam->dev; struct usb_device *dev = tascam->dev;
u8 *rate_payload_buf; u8 *rate_payload;
int err = 0; int err = 0;
const u8 *current_payload_src; const u8 *current_payload_src;
u16 rate_reg; u16 rate_reg;
static const u8 payload_44100[] = { 0x44, 0xac, 0x00 }; static const u8 payload_44100[] = { 0x44, 0xac, 0x00 };
static const u8 payload_48000[] = { 0x80, 0xbb, 0x00 }; static const u8 payload_48000[] = { 0x80, 0xbb, 0x00 };
static const u8 payload_88200[] = { 0x88, 0x58, 0x01 }; static const u8 payload_88200[] = { 0x88, 0x58, 0x01 };
static const u8 payload_96000[] = { 0x00, 0x77, 0x01 }; static const u8 payload_96000[] = { 0x00, 0x77, 0x01 };
if (!dev)
return -ENODEV;
switch (rate) { switch (rate) {
case 44100: case 44100:
current_payload_src = payload_44100; current_payload_src = payload_44100;
rate_reg = REG_ADDR_RATE_44100; rate_reg = REG_ADDR_RATE_44100;
break; break;
case 48000: case 48000:
current_payload_src = payload_48000; current_payload_src = payload_48000;
rate_reg = REG_ADDR_RATE_48000; rate_reg = REG_ADDR_RATE_48000;
break; break;
case 88200: case 88200:
current_payload_src = payload_88200; current_payload_src = payload_88200;
rate_reg = REG_ADDR_RATE_88200; rate_reg = REG_ADDR_RATE_88200;
break; break;
case 96000: case 96000:
current_payload_src = payload_96000; current_payload_src = payload_96000;
rate_reg = REG_ADDR_RATE_96000; rate_reg = REG_ADDR_RATE_96000;
break; break;
default: default:
return -EINVAL; return -EINVAL;
} }
rate_payload_buf = kmemdup(current_payload_src, 3, GFP_KERNEL); rate_payload = kmemdup(current_payload_src, 3, GFP_KERNEL);
if (!rate_payload_buf) if (!rate_payload)
return -ENOMEM; return -ENOMEM;
err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
VENDOR_REQ_MODE_CONTROL, RT_H2D_VENDOR_DEV, VENDOR_REQ_MODE_CONTROL, RT_H2D_VENDOR_DEV,
MODE_VAL_CONFIG, 0x0000, NULL, 0, USB_CTRL_TIMEOUT_MS); MODE_VAL_CONFIG, 0x0000, NULL, 0, USB_CTRL_TIMEOUT_MS);
if (err < 0) if (err < 0)
goto fail; goto out;
usb_control_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR, err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
RT_H2D_CLASS_EP, UAC_SAMPLING_FREQ_CONTROL, RT_H2D_CLASS_EP, UAC_SAMPLING_FREQ_CONTROL,
EP_AUDIO_IN, rate_payload_buf, 3, USB_CTRL_TIMEOUT_MS); EP_AUDIO_IN, rate_payload, 3, USB_CTRL_TIMEOUT_MS);
usb_control_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR, if (err < 0)
RT_H2D_CLASS_EP, UAC_SAMPLING_FREQ_CONTROL, goto out;
EP_AUDIO_OUT, rate_payload_buf, 3, USB_CTRL_TIMEOUT_MS);
err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
RT_H2D_CLASS_EP, UAC_SAMPLING_FREQ_CONTROL,
EP_AUDIO_OUT, rate_payload, 3, USB_CTRL_TIMEOUT_MS);
if (err < 0)
goto out;
{ {
const u16 regs_to_write[] = { const u16 regs_to_write[] = {
REG_ADDR_UNKNOWN_0D, REG_ADDR_UNKNOWN_0E, REG_ADDR_INIT_0D, REG_ADDR_INIT_0E,
REG_ADDR_UNKNOWN_0F, rate_reg, REG_ADDR_UNKNOWN_11 REG_ADDR_INIT_0F, rate_reg, REG_ADDR_INIT_11
}; };
err = tascam_write_regs(tascam, regs_to_write, ARRAY_SIZE(regs_to_write)); err = tascam_write_regs(tascam, regs_to_write, ARRAY_SIZE(regs_to_write));
if (err < 0) if (err < 0)
goto fail; goto out;
} }
err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
VENDOR_REQ_MODE_CONTROL, RT_H2D_VENDOR_DEV, VENDOR_REQ_MODE_CONTROL, RT_H2D_VENDOR_DEV,
MODE_VAL_STREAM_START, 0x0000, NULL, 0, USB_CTRL_TIMEOUT_MS); MODE_VAL_STREAM_START, 0x0000, NULL, 0, USB_CTRL_TIMEOUT_MS);
if (err < 0)
goto fail;
kfree(rate_payload_buf); out:
return 0; kfree(rate_payload);
fail:
kfree(rate_payload_buf);
return err; return err;
} }
@ -120,12 +123,20 @@ int tascam_pcm_hw_params(struct snd_pcm_substream *substream,
struct tascam_card *tascam = snd_pcm_substream_chip(substream); struct tascam_card *tascam = snd_pcm_substream_chip(substream);
unsigned int rate = params_rate(params); unsigned int rate = params_rate(params);
int err; int err;
unsigned long flags;
if (tascam->current_rate != rate) { spin_lock_irqsave(&tascam->lock, flags);
if (atomic_read(&tascam->playback_active) || if (tascam->current_rate == rate) {
atomic_read(&tascam->capture_active)) { spin_unlock_irqrestore(&tascam->lock, flags);
return -EBUSY; return 0;
} }
if (atomic_read(&tascam->playback_active) ||
atomic_read(&tascam->capture_active)) {
spin_unlock_irqrestore(&tascam->lock, flags);
return -EBUSY;
}
spin_unlock_irqrestore(&tascam->lock, flags);
usb_kill_anchored_urbs(&tascam->playback_anchor); usb_kill_anchored_urbs(&tascam->playback_anchor);
usb_kill_anchored_urbs(&tascam->feedback_anchor); usb_kill_anchored_urbs(&tascam->feedback_anchor);
@ -135,12 +146,17 @@ int tascam_pcm_hw_params(struct snd_pcm_substream *substream,
err = us144mkii_configure_device_for_rate(tascam, rate); err = us144mkii_configure_device_for_rate(tascam, rate);
if (err < 0) { if (err < 0) {
spin_lock_irqsave(&tascam->lock, flags);
tascam->current_rate = 0; tascam->current_rate = 0;
spin_unlock_irqrestore(&tascam->lock, flags);
return err; return err;
} }
spin_lock_irqsave(&tascam->lock, flags);
tascam->current_rate = rate; tascam->current_rate = rate;
} spin_unlock_irqrestore(&tascam->lock, flags);
return 0;
return 0;
} }
/** /**
@ -151,6 +167,9 @@ void tascam_stop_pcm_work_handler(struct work_struct *work)
{ {
struct tascam_card *tascam = container_of(work, struct tascam_card, stop_pcm_work); struct tascam_card *tascam = container_of(work, struct tascam_card, stop_pcm_work);
if (!tascam->dev)
return;
if (tascam->playback_substream) if (tascam->playback_substream)
snd_pcm_stop(tascam->playback_substream, SNDRV_PCM_STATE_XRUN); snd_pcm_stop(tascam->playback_substream, SNDRV_PCM_STATE_XRUN);
} }

View File

@ -15,7 +15,7 @@ const struct snd_pcm_hardware tascam_playback_hw = {
.channels_min = NUM_CHANNELS, .channels_min = NUM_CHANNELS,
.channels_max = NUM_CHANNELS, .channels_max = NUM_CHANNELS,
.buffer_bytes_max = 1024 * 1024, .buffer_bytes_max = 1024 * 1024,
.period_bytes_min = 32 * BYTES_PER_FRAME, .period_bytes_min = 48 * BYTES_PER_FRAME,
.period_bytes_max = 1024 * BYTES_PER_FRAME, .period_bytes_max = 1024 * BYTES_PER_FRAME,
.periods_min = 2, .periods_min = 2,
.periods_max = 1024, .periods_max = 1024,
@ -26,6 +26,7 @@ static int tascam_playback_open(struct snd_pcm_substream *substream)
struct tascam_card *tascam = snd_pcm_substream_chip(substream); struct tascam_card *tascam = snd_pcm_substream_chip(substream);
substream->runtime->hw = tascam_playback_hw; substream->runtime->hw = tascam_playback_hw;
tascam->playback_substream = substream; tascam->playback_substream = substream;
atomic_set(&tascam->playback_active, 0); atomic_set(&tascam->playback_active, 0);
return 0; return 0;
@ -48,7 +49,6 @@ static int tascam_playback_prepare(struct snd_pcm_substream *substream)
struct tascam_card *tascam = snd_pcm_substream_chip(substream); struct tascam_card *tascam = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
int i, u; int i, u;
u32 nominal_q16 = (runtime->rate << 16) / 8000;
size_t nominal_bytes = (runtime->rate / 8000) * BYTES_PER_FRAME; size_t nominal_bytes = (runtime->rate / 8000) * BYTES_PER_FRAME;
usb_kill_anchored_urbs(&tascam->playback_anchor); usb_kill_anchored_urbs(&tascam->playback_anchor);
@ -60,7 +60,7 @@ static int tascam_playback_prepare(struct snd_pcm_substream *substream)
tascam->feedback_synced = false; tascam->feedback_synced = false;
tascam->feedback_urb_skip_count = NUM_FEEDBACK_URBS; tascam->feedback_urb_skip_count = NUM_FEEDBACK_URBS;
tascam->phase_accum = 0; tascam->phase_accum = 0;
tascam->freq_q16 = nominal_q16; tascam->freq_q16 = div_u64(((u64)runtime->rate << 16), 8000);
for (i = 0; i < NUM_FEEDBACK_URBS; i++) { for (i = 0; i < NUM_FEEDBACK_URBS; i++) {
struct urb *f_urb = tascam->feedback_urbs[i]; struct urb *f_urb = tascam->feedback_urbs[i];
@ -75,11 +75,11 @@ static int tascam_playback_prepare(struct snd_pcm_substream *substream)
for (u = 0; u < NUM_PLAYBACK_URBS; u++) { for (u = 0; u < NUM_PLAYBACK_URBS; u++) {
struct urb *urb = tascam->playback_urbs[u]; struct urb *urb = tascam->playback_urbs[u];
int num_packets = PLAYBACK_URB_PACKETS;
memset(urb->transfer_buffer, 0, tascam->playback_urb_alloc_size); urb->number_of_packets = num_packets;
urb->number_of_packets = PLAYBACK_URB_PACKETS; urb->transfer_buffer_length = num_packets * nominal_bytes;
urb->transfer_buffer_length = PLAYBACK_URB_PACKETS * nominal_bytes; for (i = 0; i < num_packets; i++) {
for (i = 0; i < PLAYBACK_URB_PACKETS; i++) {
urb->iso_frame_desc[i].offset = i * nominal_bytes; urb->iso_frame_desc[i].offset = i * nominal_bytes;
urb->iso_frame_desc[i].length = nominal_bytes; urb->iso_frame_desc[i].length = nominal_bytes;
} }
@ -113,27 +113,28 @@ static int tascam_playback_trigger(struct snd_pcm_substream *substream, int cmd)
spin_lock_irqsave(&tascam->lock, flags); spin_lock_irqsave(&tascam->lock, flags);
switch (cmd) { switch (cmd) {
case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_RESUME:
if (!atomic_read(&tascam->playback_active)) { if (!atomic_read(&tascam->playback_active)) {
atomic_set(&tascam->playback_active, 1); atomic_set(&tascam->playback_active, 1);
start = true; tascam->feedback_synced = false;
} tascam->feedback_urb_skip_count = NUM_FEEDBACK_URBS;
break; start = true;
case SNDRV_PCM_TRIGGER_STOP: }
case SNDRV_PCM_TRIGGER_SUSPEND: break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_STOP:
atomic_set(&tascam->playback_active, 0); case SNDRV_PCM_TRIGGER_SUSPEND:
stop = true; case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
break; atomic_set(&tascam->playback_active, 0);
default: stop = true;
ret = -EINVAL; break;
break; default:
ret = -EINVAL;
break;
} }
spin_unlock_irqrestore(&tascam->lock, flags); spin_unlock_irqrestore(&tascam->lock, flags);
if (stop) { if (stop) {
/* Ensure playback_active is updated before unlinking URBs. */
smp_mb(); smp_mb();
for (i = 0; i < NUM_FEEDBACK_URBS; i++) { for (i = 0; i < NUM_FEEDBACK_URBS; i++) {
if (tascam->feedback_urbs[i]) if (tascam->feedback_urbs[i])
@ -150,9 +151,7 @@ static int tascam_playback_trigger(struct snd_pcm_substream *substream, int cmd)
usb_anchor_urb(tascam->feedback_urbs[i], &tascam->feedback_anchor); usb_anchor_urb(tascam->feedback_urbs[i], &tascam->feedback_anchor);
if (usb_submit_urb(tascam->feedback_urbs[i], GFP_ATOMIC) < 0) { if (usb_submit_urb(tascam->feedback_urbs[i], GFP_ATOMIC) < 0) {
usb_unanchor_urb(tascam->feedback_urbs[i]); usb_unanchor_urb(tascam->feedback_urbs[i]);
dev_err(&tascam->dev->dev, "Failed to submit feedback URB %d\n", i);
atomic_set(&tascam->playback_active, 0); atomic_set(&tascam->playback_active, 0);
/* Ensure playback_active is updated before unlinking feedback URBs due to submission error. */
smp_mb(); smp_mb();
for (int j = 0; j < i; j++) for (int j = 0; j < i; j++)
usb_unlink_urb(tascam->feedback_urbs[j]); usb_unlink_urb(tascam->feedback_urbs[j]);
@ -165,9 +164,7 @@ static int tascam_playback_trigger(struct snd_pcm_substream *substream, int cmd)
usb_anchor_urb(tascam->playback_urbs[i], &tascam->playback_anchor); usb_anchor_urb(tascam->playback_urbs[i], &tascam->playback_anchor);
if (usb_submit_urb(tascam->playback_urbs[i], GFP_ATOMIC) < 0) { if (usb_submit_urb(tascam->playback_urbs[i], GFP_ATOMIC) < 0) {
usb_unanchor_urb(tascam->playback_urbs[i]); usb_unanchor_urb(tascam->playback_urbs[i]);
dev_err(&tascam->dev->dev, "Failed to submit playback URB %d\n", i);
atomic_set(&tascam->playback_active, 0); atomic_set(&tascam->playback_active, 0);
/* Ensure playback_active is updated before unlinking playback URBs due to submission error. */
smp_mb(); smp_mb();
for (int j = 0; j < NUM_FEEDBACK_URBS; j++) for (int j = 0; j < NUM_FEEDBACK_URBS; j++)
usb_unlink_urb(tascam->feedback_urbs[j]); usb_unlink_urb(tascam->feedback_urbs[j]);
@ -182,7 +179,7 @@ static int tascam_playback_trigger(struct snd_pcm_substream *substream, int cmd)
schedule_work(&tascam->stop_work); schedule_work(&tascam->stop_work);
} }
error: error:
return ret; return ret;
} }
@ -211,6 +208,7 @@ void playback_urb_complete(struct urb *urb)
return; return;
if (urb->status) { if (urb->status) {
usb_unanchor_urb(urb);
atomic_dec(&tascam->active_urbs); atomic_dec(&tascam->active_urbs);
return; return;
} }
@ -243,6 +241,10 @@ void playback_urb_complete(struct urb *urb)
tascam->phase_accum += tascam->freq_q16; tascam->phase_accum += tascam->freq_q16;
frames_for_packet = tascam->phase_accum >> 16; frames_for_packet = tascam->phase_accum >> 16;
tascam->phase_accum &= 0xFFFF; tascam->phase_accum &= 0xFFFF;
if (frames_for_packet > 13)
frames_for_packet = 13;
urb->iso_frame_desc[i].offset = total_bytes_for_urb; urb->iso_frame_desc[i].offset = total_bytes_for_urb;
urb->iso_frame_desc[i].length = frames_for_packet * BYTES_PER_FRAME; urb->iso_frame_desc[i].length = frames_for_packet * BYTES_PER_FRAME;
total_bytes_for_urb += urb->iso_frame_desc[i].length; total_bytes_for_urb += urb->iso_frame_desc[i].length;
@ -251,6 +253,7 @@ void playback_urb_complete(struct urb *urb)
offset_frames = tascam->driver_playback_pos; offset_frames = tascam->driver_playback_pos;
frames_to_copy = bytes_to_frames(runtime, total_bytes_for_urb); frames_to_copy = bytes_to_frames(runtime, total_bytes_for_urb);
tascam->driver_playback_pos = (offset_frames + frames_to_copy) % buffer_size; tascam->driver_playback_pos = (offset_frames + frames_to_copy) % buffer_size;
if (total_bytes_for_urb > 0) { if (total_bytes_for_urb > 0) {
@ -259,9 +262,10 @@ void playback_urb_complete(struct urb *urb)
if (offset_frames + frames_to_copy > buffer_size) { if (offset_frames + frames_to_copy > buffer_size) {
size_t part1 = buffer_size - offset_frames; size_t part1 = buffer_size - offset_frames;
size_t part1_bytes = frames_to_bytes(runtime, part1);
memcpy(dst_buf, runtime->dma_area + ptr_bytes, frames_to_bytes(runtime, part1)); memcpy(dst_buf, runtime->dma_area + ptr_bytes, part1_bytes);
memcpy(dst_buf + frames_to_bytes(runtime, part1), runtime->dma_area, total_bytes_for_urb - frames_to_bytes(runtime, part1)); memcpy(dst_buf + part1_bytes, runtime->dma_area, total_bytes_for_urb - part1_bytes);
} else { } else {
memcpy(dst_buf, runtime->dma_area + ptr_bytes, total_bytes_for_urb); memcpy(dst_buf, runtime->dma_area + ptr_bytes, total_bytes_for_urb);
} }
@ -286,6 +290,7 @@ void playback_urb_complete(struct urb *urb)
if (usb_submit_urb(urb, GFP_ATOMIC) < 0) { if (usb_submit_urb(urb, GFP_ATOMIC) < 0) {
usb_unanchor_urb(urb); usb_unanchor_urb(urb);
atomic_dec(&tascam->active_urbs); atomic_dec(&tascam->active_urbs);
return;
} }
} }
@ -305,20 +310,26 @@ void feedback_urb_complete(struct urb *urb)
struct tascam_card *tascam = urb->context; struct tascam_card *tascam = urb->context;
int ret, p; int ret, p;
unsigned long flags; unsigned long flags;
bool playback_active;
if (!tascam) if (!tascam)
return; return;
if (urb->status) { if (urb->status) {
atomic_dec(&tascam->active_urbs); usb_unanchor_urb(urb);
return;
}
if (!atomic_read(&tascam->playback_active)) {
atomic_dec(&tascam->active_urbs); atomic_dec(&tascam->active_urbs);
return; return;
} }
spin_lock_irqsave(&tascam->lock, flags); spin_lock_irqsave(&tascam->lock, flags);
playback_active = atomic_read(&tascam->playback_active);
if (!playback_active) {
spin_unlock_irqrestore(&tascam->lock, flags);
usb_unanchor_urb(urb);
atomic_dec(&tascam->active_urbs);
return;
}
if (tascam->feedback_urb_skip_count > 0) { if (tascam->feedback_urb_skip_count > 0) {
tascam->feedback_urb_skip_count--; tascam->feedback_urb_skip_count--;
spin_unlock_irqrestore(&tascam->lock, flags); spin_unlock_irqrestore(&tascam->lock, flags);
@ -327,17 +338,21 @@ void feedback_urb_complete(struct urb *urb)
for (p = 0; p < urb->number_of_packets; p++) { for (p = 0; p < urb->number_of_packets; p++) {
if (urb->iso_frame_desc[p].status == 0 && urb->iso_frame_desc[p].actual_length >= 3) { if (urb->iso_frame_desc[p].status == 0 && urb->iso_frame_desc[p].actual_length >= 3) {
u8 *data = (u8 *)urb->transfer_buffer + urb->iso_frame_desc[p].offset;
u32 sum = data[0] + data[1] + data[2];
u32 target_freq_q16 = ((sum * 65536) / 3) / 8;
tascam->freq_q16 = (tascam->freq_q16 * PLL_FILTER_OLD_WEIGHT + target_freq_q16 * PLL_FILTER_NEW_WEIGHT) / PLL_FILTER_DIVISOR; u8 *data = (u8 *)urb->transfer_buffer + urb->iso_frame_desc[p].offset;
u32 sum_frames_3ms = data[0] + data[1] + data[2];
u32 target_freq_q16 = (sum_frames_3ms * 65536) / 24;
tascam->freq_q16 = (tascam->freq_q16 * PLL_FILTER_OLD_WEIGHT +
target_freq_q16 * PLL_FILTER_NEW_WEIGHT +
(PLL_FILTER_DIVISOR >> 1)) / PLL_FILTER_DIVISOR;
tascam->feedback_synced = true; tascam->feedback_synced = true;
} }
} }
spin_unlock_irqrestore(&tascam->lock, flags); spin_unlock_irqrestore(&tascam->lock, flags);
resubmit: resubmit:
usb_anchor_urb(urb, &tascam->feedback_anchor); usb_anchor_urb(urb, &tascam->feedback_anchor);
ret = usb_submit_urb(urb, GFP_ATOMIC); ret = usb_submit_urb(urb, GFP_ATOMIC);
if (ret < 0) { if (ret < 0) {