From 2b58fd5b3193fd3af3d15114d95706087d25a7fe Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Tue, 4 Sep 2012 10:23:07 +0200 Subject: ALSA: snd-usb: Add quirks for Playback Designs devices Playback Designs' USB devices have some hardware limitations on their USB interface. In particular: - They need a 20ms delay after each class compliant request as the hardware ACKs the USB packets before the device is actually ready for the next command. Sending data immediately will result in buffer overflows in the hardware. - The devices send bogus feedback data at the start of each stream which confuse the feedback format auto-detection. This patch introduces a new quirks hook that is called after each control packet and which adds a delay for all devices that match Playback Designs' USB VID for now. In addition, it adds a counter to snd_usb_endpoint to drop received packets on the floor. Another new quirks function that is called once an endpoint is started initializes that counter for these devices on their sync endpoint. Signed-off-by: Daniel Mack Reported-and-tested-by: Andreas Koch Supported-by: Demian Martin Signed-off-by: Takashi Iwai --- sound/usb/endpoint.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'sound/usb/endpoint.c') diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index c41181202688..94b08a6087a3 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -31,6 +31,7 @@ #include "card.h" #include "endpoint.h" #include "pcm.h" +#include "quirks.h" #define EP_FLAG_ACTIVATED 0 #define EP_FLAG_RUNNING 1 @@ -170,6 +171,11 @@ static void retire_inbound_urb(struct snd_usb_endpoint *ep, { struct urb *urb = urb_ctx->urb; + if (unlikely(ep->skip_packets > 0)) { + ep->skip_packets--; + return; + } + if (ep->sync_slave) snd_usb_handle_sync_urb(ep->sync_slave, ep, urb); @@ -825,6 +831,8 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep) ep->unlink_mask = 0; ep->phase = 0; + snd_usb_endpoint_start_quirk(ep); + /* * If this endpoint has a data endpoint as implicit feedback source, * don't start the urbs here. Instead, mark them all as available, -- cgit v1.2.3 From 35ec7aa29833de350f51922736aefe22ebf76c4d Mon Sep 17 00:00:00 2001 From: Dylan Reid Date: Tue, 18 Sep 2012 09:49:47 -0700 Subject: ALSA: usb-audio: Don't require hw_params in endpoint. Change the interface to configure an endpoint so that it doesn't require a hw_params struct. This will allow it to be called from prepare instead of hw_params, configuring it after system resume. Signed-off-by: Dylan Reid Signed-off-by: Takashi Iwai --- sound/usb/endpoint.c | 31 ++++++++++++++++++------------- sound/usb/endpoint.h | 5 ++++- sound/usb/pcm.c | 16 +++++++++++++--- 3 files changed, 35 insertions(+), 17 deletions(-) (limited to 'sound/usb/endpoint.c') diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index a83a18dbac25..152bfd48a311 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -567,20 +567,19 @@ static void release_urbs(struct snd_usb_endpoint *ep, int force) * configure a data endpoint */ static int data_ep_set_params(struct snd_usb_endpoint *ep, - struct snd_pcm_hw_params *hw_params, + snd_pcm_format_t pcm_format, + unsigned int channels, + unsigned int period_bytes, struct audioformat *fmt, struct snd_usb_endpoint *sync_ep) { unsigned int maxsize, i, urb_packs, total_packs, packs_per_ms; - int period_bytes = params_period_bytes(hw_params); - int format = params_format(hw_params); int is_playback = usb_pipeout(ep->pipe); - int frame_bits = snd_pcm_format_physical_width(params_format(hw_params)) * - params_channels(hw_params); + int frame_bits = snd_pcm_format_physical_width(pcm_format) * channels; ep->datainterval = fmt->datainterval; ep->stride = frame_bits >> 3; - ep->silence_value = format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0; + ep->silence_value = pcm_format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0; /* calculate max. frequency */ if (ep->maxpacksize) { @@ -693,7 +692,6 @@ out_of_memory: * configure a sync endpoint */ static int sync_ep_set_params(struct snd_usb_endpoint *ep, - struct snd_pcm_hw_params *hw_params, struct audioformat *fmt) { int i; @@ -736,7 +734,10 @@ out_of_memory: * snd_usb_endpoint_set_params: configure an snd_usb_endpoint * * @ep: the snd_usb_endpoint to configure - * @hw_params: the hardware parameters + * @pcm_format: the audio fomat. + * @channels: the number of audio channels. + * @period_bytes: the number of bytes in one alsa period. + * @rate: the frame rate. * @fmt: the USB audio format information * @sync_ep: the sync endpoint to use, if any * @@ -745,7 +746,10 @@ out_of_memory: * An endpoint that is already running can not be reconfigured. */ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, - struct snd_pcm_hw_params *hw_params, + snd_pcm_format_t pcm_format, + unsigned int channels, + unsigned int period_bytes, + unsigned int rate, struct audioformat *fmt, struct snd_usb_endpoint *sync_ep) { @@ -765,9 +769,9 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, ep->fill_max = !!(fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX); if (snd_usb_get_speed(ep->chip->dev) == USB_SPEED_FULL) - ep->freqn = get_usb_full_speed_rate(params_rate(hw_params)); + ep->freqn = get_usb_full_speed_rate(rate); else - ep->freqn = get_usb_high_speed_rate(params_rate(hw_params)); + ep->freqn = get_usb_high_speed_rate(rate); /* calculate the frequency in 16.16 format */ ep->freqm = ep->freqn; @@ -777,10 +781,11 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, switch (ep->type) { case SND_USB_ENDPOINT_TYPE_DATA: - err = data_ep_set_params(ep, hw_params, fmt, sync_ep); + err = data_ep_set_params(ep, pcm_format, channels, + period_bytes, fmt, sync_ep); break; case SND_USB_ENDPOINT_TYPE_SYNC: - err = sync_ep_set_params(ep, hw_params, fmt); + err = sync_ep_set_params(ep, fmt); break; default: err = -EINVAL; diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h index cbbbdf226d66..6376ccf10fd4 100644 --- a/sound/usb/endpoint.h +++ b/sound/usb/endpoint.h @@ -9,7 +9,10 @@ struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip, int ep_num, int direction, int type); int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, - struct snd_pcm_hw_params *hw_params, + snd_pcm_format_t pcm_format, + unsigned int channels, + unsigned int period_bytes, + unsigned int rate, struct audioformat *fmt, struct snd_usb_endpoint *sync_ep); diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 786f7a05e9a6..62ab4fd5880a 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -491,14 +491,24 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, mutex_lock(&subs->stream->chip->shutdown_mutex); /* format changed */ stop_endpoints(subs, 0, 0, 0); - ret = snd_usb_endpoint_set_params(subs->data_endpoint, hw_params, fmt, + ret = snd_usb_endpoint_set_params(subs->data_endpoint, + format, + channels, + subs->period_bytes, + rate, + fmt, subs->sync_endpoint); if (ret < 0) goto unlock; if (subs->sync_endpoint) - ret = snd_usb_endpoint_set_params(subs->sync_endpoint, - hw_params, fmt, NULL); + ret = snd_usb_endpoint_set_params(subs->data_endpoint, + format, + channels, + subs->period_bytes, + rate, + fmt, + NULL); unlock: mutex_unlock(&subs->stream->chip->shutdown_mutex); } -- cgit v1.2.3