From 488fe1665fd90f204fbc825b90060b9e0394e4be Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Fri, 3 Apr 2009 09:41:40 +0200 Subject: sound: usb-audio: show sample format width in proc file When listing the device's sample formats in the stream? proc file, the sample format number itself is rather obscure, so we better show the format width, too. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/usb/usbaudio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c index c2db0f959681..175c7d1da5cf 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c @@ -2147,7 +2147,8 @@ static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct s fp = list_entry(p, struct audioformat, list); snd_iprintf(buffer, " Interface %d\n", fp->iface); snd_iprintf(buffer, " Altset %d\n", fp->altsetting); - snd_iprintf(buffer, " Format: %#x\n", fp->format); + snd_iprintf(buffer, " Format: %#x (%d bits)\n", + fp->format, snd_pcm_format_width(fp->format)); snd_iprintf(buffer, " Channels: %d\n", fp->channels); snd_iprintf(buffer, " Endpoint: %d %s (%s)\n", fp->endpoint & USB_ENDPOINT_NUMBER_MASK, -- cgit v1.2.3 From 4608eb089b41386e96bd1410326073a6c1c221ba Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Fri, 3 Apr 2009 09:42:42 +0200 Subject: sound: usb-audio: remove check_hw_params_convention() This removes the check_hw_params_convention() function because 1) it is not necessary, as the hw_rule_* functions also work correctly (i.e., as no-ops) when the device supports all combinations of the audio format parameters; and 2) it would become too complex when adding a fourth altsetting-dependent hardware parameter, as this would require another three loops to check dependecies with rate/channels/format. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/usb/usbaudio.c | 135 ++++++++------------------------------------------- 1 file changed, 19 insertions(+), 116 deletions(-) (limited to 'sound') diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c index 175c7d1da5cf..b7fa0b0b713d 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c @@ -1736,97 +1736,6 @@ static int hw_rule_format(struct snd_pcm_hw_params *params, return changed; } -#define MAX_MASK 64 - -/* - * check whether the registered audio formats need special hw-constraints - */ -static int check_hw_params_convention(struct snd_usb_substream *subs) -{ - int i; - u32 *channels; - u32 *rates; - u32 cmaster, rmaster; - u32 rate_min = 0, rate_max = 0; - struct list_head *p; - int err = 1; - - channels = kcalloc(MAX_MASK, sizeof(u32), GFP_KERNEL); - rates = kcalloc(MAX_MASK, sizeof(u32), GFP_KERNEL); - if (!channels || !rates) { - err = -ENOMEM; - goto __out; - } - - list_for_each(p, &subs->fmt_list) { - struct audioformat *f; - f = list_entry(p, struct audioformat, list); - /* unconventional channels? */ - if (f->channels > 32) - goto __out; - /* continuous rate min/max matches? */ - if (f->rates & SNDRV_PCM_RATE_CONTINUOUS) { - if (rate_min && f->rate_min != rate_min) - goto __out; - if (rate_max && f->rate_max != rate_max) - goto __out; - rate_min = f->rate_min; - rate_max = f->rate_max; - } - /* combination of continuous rates and fixed rates? */ - if (rates[f->format] & SNDRV_PCM_RATE_CONTINUOUS) { - if (f->rates != rates[f->format]) - goto __out; - } - if (f->rates & SNDRV_PCM_RATE_CONTINUOUS) { - if (rates[f->format] && rates[f->format] != f->rates) - goto __out; - } - channels[f->format] |= 1 << (f->channels - 1); - rates[f->format] |= f->rates; - /* needs knot? */ - if (f->rates & SNDRV_PCM_RATE_KNOT) - goto __out; - } - /* check whether channels and rates match for all formats */ - cmaster = rmaster = 0; - for (i = 0; i < MAX_MASK; i++) { - if (cmaster != channels[i] && cmaster && channels[i]) - goto __out; - if (rmaster != rates[i] && rmaster && rates[i]) - goto __out; - if (channels[i]) - cmaster = channels[i]; - if (rates[i]) - rmaster = rates[i]; - } - /* check whether channels match for all distinct rates */ - memset(channels, 0, MAX_MASK * sizeof(u32)); - list_for_each(p, &subs->fmt_list) { - struct audioformat *f; - f = list_entry(p, struct audioformat, list); - if (f->rates & SNDRV_PCM_RATE_CONTINUOUS) - continue; - for (i = 0; i < 32; i++) { - if (f->rates & (1 << i)) - channels[i] |= 1 << (f->channels - 1); - } - } - cmaster = 0; - for (i = 0; i < 32; i++) { - if (cmaster != channels[i] && cmaster && channels[i]) - goto __out; - if (channels[i]) - cmaster = channels[i]; - } - err = 0; - - __out: - kfree(channels); - kfree(rates); - return err; -} - /* * If the device supports unusual bit rates, does the request meet these? */ @@ -1909,32 +1818,26 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre 1000, /*(nrpacks * MAX_URBS) * 1000*/ UINT_MAX); - err = check_hw_params_convention(subs); - if (err < 0) + if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + hw_rule_rate, subs, + SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_HW_PARAM_CHANNELS, + -1)) < 0) + return err; + if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + hw_rule_channels, subs, + SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_HW_PARAM_RATE, + -1)) < 0) + return err; + if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, + hw_rule_format, subs, + SNDRV_PCM_HW_PARAM_RATE, + SNDRV_PCM_HW_PARAM_CHANNELS, + -1)) < 0) + return err; + if ((err = snd_usb_pcm_check_knot(runtime, subs)) < 0) return err; - else if (err) { - hwc_debug("setting extra hw constraints...\n"); - if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - hw_rule_rate, subs, - SNDRV_PCM_HW_PARAM_FORMAT, - SNDRV_PCM_HW_PARAM_CHANNELS, - -1)) < 0) - return err; - if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - hw_rule_channels, subs, - SNDRV_PCM_HW_PARAM_FORMAT, - SNDRV_PCM_HW_PARAM_RATE, - -1)) < 0) - return err; - if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, - hw_rule_format, subs, - SNDRV_PCM_HW_PARAM_RATE, - SNDRV_PCM_HW_PARAM_CHANNELS, - -1)) < 0) - return err; - if ((err = snd_usb_pcm_check_knot(runtime, subs)) < 0) - return err; - } return 0; } -- cgit v1.2.3 From 744b89e542b9a16c9afb8a88f623fbe059c88ccb Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Fri, 3 Apr 2009 09:45:01 +0200 Subject: sound: usb-audio: save data packet interval in audioformat structure The data packet interval needs to be available in the audioformat structure, together with the other audio format parameters, so that it can be used to influence ALSA hardware parameters. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/usb/usbaudio.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c index b7fa0b0b713d..3f974a64c55e 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c @@ -121,6 +121,7 @@ struct audioformat { unsigned char attributes; /* corresponding attributes of cs endpoint */ unsigned char endpoint; /* endpoint */ unsigned char ep_attr; /* endpoint attributes */ + unsigned char datainterval; /* log_2 of data packet interval */ unsigned int maxpacksize; /* max. packet size */ unsigned int rates; /* rate bitmasks */ unsigned int rate_min, rate_max; /* min/max rates */ @@ -1350,12 +1351,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) subs->datapipe = usb_sndisocpipe(dev, ep); else subs->datapipe = usb_rcvisocpipe(dev, ep); - if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH && - get_endpoint(alts, 0)->bInterval >= 1 && - get_endpoint(alts, 0)->bInterval <= 4) - subs->datainterval = get_endpoint(alts, 0)->bInterval - 1; - else - subs->datainterval = 0; + subs->datainterval = fmt->datainterval; subs->syncpipe = subs->syncinterval = 0; subs->maxpacksize = fmt->maxpacksize; subs->fill_max = 0; @@ -2070,6 +2066,9 @@ static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct s } snd_iprintf(buffer, "\n"); } + if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH) + snd_iprintf(buffer, " Data packet interval: %d us\n", + 125 * (1 << fp->datainterval)); // snd_iprintf(buffer, " Max Packet Size = %d\n", fp->maxpacksize); // snd_iprintf(buffer, " EP Attribute = %#x\n", fp->attributes); } @@ -2563,6 +2562,17 @@ static int parse_audio_format(struct snd_usb_audio *chip, struct audioformat *fp return 0; } +static unsigned char parse_datainterval(struct snd_usb_audio *chip, + struct usb_host_interface *alts) +{ + if (snd_usb_get_speed(chip->dev) == USB_SPEED_HIGH && + get_endpoint(alts, 0)->bInterval >= 1 && + get_endpoint(alts, 0)->bInterval <= 4) + return get_endpoint(alts, 0)->bInterval - 1; + else + return 0; +} + static int audiophile_skip_setting_quirk(struct snd_usb_audio *chip, int iface, int altno); static int parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no) @@ -2668,6 +2678,7 @@ static int parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no) fp->altset_idx = i; fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress; fp->ep_attr = get_endpoint(alts, 0)->bmAttributes; + fp->datainterval = parse_datainterval(chip, alts); fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); if (snd_usb_get_speed(dev) == USB_SPEED_HIGH) fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1) @@ -2859,6 +2870,7 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip, return -EINVAL; } alts = &iface->altsetting[fp->altset_idx]; + fp->datainterval = parse_datainterval(chip, alts); fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); usb_set_interface(chip->dev, fp->iface, 0); init_usb_pitch(chip->dev, fp->iface, alts, fp); @@ -2953,6 +2965,7 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip, fp->iface = altsd->bInterfaceNumber; fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress; fp->ep_attr = get_endpoint(alts, 0)->bmAttributes; + fp->datainterval = 0; fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); switch (fp->maxpacksize) { @@ -3020,6 +3033,7 @@ static int create_ua1000_quirk(struct snd_usb_audio *chip, fp->iface = altsd->bInterfaceNumber; fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress; fp->ep_attr = get_endpoint(alts, 0)->bmAttributes; + fp->datainterval = parse_datainterval(chip, alts); fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); fp->rate_max = fp->rate_min = combine_triple(&alts->extra[8]); @@ -3072,6 +3086,7 @@ static int create_ua101_quirk(struct snd_usb_audio *chip, fp->iface = altsd->bInterfaceNumber; fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress; fp->ep_attr = get_endpoint(alts, 0)->bmAttributes; + fp->datainterval = parse_datainterval(chip, alts); fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); fp->rate_max = fp->rate_min = combine_triple(&alts->extra[15]); -- cgit v1.2.3 From a7d9c0990d5503775784fef7ff44d74d7e3294fd Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Fri, 3 Apr 2009 09:48:26 +0200 Subject: sound: usb-audio: allow period sizes less than 1 ms To enable periods shorter than 1 ms, we have to make sure that short periods are only available for alternate settings that have a small enough data packet interval. Furthermore, the code that aligns URBs to USB frames is now superfluous. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/usb/usbaudio.c | 106 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 86 insertions(+), 20 deletions(-) (limited to 'sound') diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c index 3f974a64c55e..823296d7d578 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c @@ -171,7 +171,6 @@ struct snd_usb_substream { unsigned int curframesize; /* current packet size in frames (for capture) */ unsigned int fill_max: 1; /* fill max packet size always */ unsigned int fmt_type; /* USB audio format type (1-3) */ - unsigned int packs_per_ms; /* packets per millisecond (for playback) */ unsigned int running: 1; /* running status */ @@ -608,9 +607,7 @@ static int prepare_playback_urb(struct snd_usb_substream *subs, break; } } - /* finish at the frame boundary at/after the period boundary */ - if (period_elapsed && - (i & (subs->packs_per_ms - 1)) == subs->packs_per_ms - 1) + if (period_elapsed) /* finish at the period boundary */ break; } if (subs->hwptr_done + offs > runtime->buffer_size) { @@ -1068,7 +1065,6 @@ static int init_substream_urbs(struct snd_usb_substream *subs, unsigned int peri packs_per_ms = 8 >> subs->datainterval; else packs_per_ms = 1; - subs->packs_per_ms = packs_per_ms; if (is_playback) { urb_packs = max(nrpacks, 1); @@ -1088,18 +1084,17 @@ static int init_substream_urbs(struct snd_usb_substream *subs, unsigned int peri minsize -= minsize >> 3; minsize = max(minsize, 1u); total_packs = (period_bytes + minsize - 1) / minsize; - /* round up to multiple of packs_per_ms */ - total_packs = (total_packs + packs_per_ms - 1) - & ~(packs_per_ms - 1); /* we need at least two URBs for queueing */ - if (total_packs < 2 * packs_per_ms) { - total_packs = 2 * packs_per_ms; + if (total_packs < 2) { + total_packs = 2; } else { /* and we don't want too long a queue either */ maxpacks = max(MAX_QUEUE * packs_per_ms, urb_packs * 2); total_packs = min(total_packs, maxpacks); } } else { + while (urb_packs > 1 && urb_packs * maxsize >= period_bytes) + urb_packs >>= 1; total_packs = MAX_URBS * urb_packs; } subs->nurbs = (total_packs + urb_packs - 1) / urb_packs; @@ -1564,11 +1559,15 @@ static struct snd_pcm_hardware snd_usb_hardware = #define hwc_debug(fmt, args...) /**/ #endif -static int hw_check_valid_format(struct snd_pcm_hw_params *params, struct audioformat *fp) +static int hw_check_valid_format(struct snd_usb_substream *subs, + struct snd_pcm_hw_params *params, + struct audioformat *fp) { struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval *ct = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_mask *fmts = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + struct snd_interval *pt = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_TIME); + unsigned int ptime; /* check the format */ if (!snd_mask_test(fmts, fp->format)) { @@ -1589,6 +1588,14 @@ static int hw_check_valid_format(struct snd_pcm_hw_params *params, struct audiof hwc_debug(" > check: rate_max %d < min %d\n", fp->rate_max, it->min); return 0; } + /* check whether the period time is >= the data packet interval */ + if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH) { + ptime = 125 * (1 << fp->datainterval); + if (ptime > pt->max || (ptime == pt->max && pt->openmax)) { + hwc_debug(" > check: ptime %u > max %u\n", ptime, pt->max); + return 0; + } + } return 1; } @@ -1607,7 +1614,7 @@ static int hw_rule_rate(struct snd_pcm_hw_params *params, list_for_each(p, &subs->fmt_list) { struct audioformat *fp; fp = list_entry(p, struct audioformat, list); - if (!hw_check_valid_format(params, fp)) + if (!hw_check_valid_format(subs, params, fp)) continue; if (changed++) { if (rmin > fp->rate_min) @@ -1661,7 +1668,7 @@ static int hw_rule_channels(struct snd_pcm_hw_params *params, list_for_each(p, &subs->fmt_list) { struct audioformat *fp; fp = list_entry(p, struct audioformat, list); - if (!hw_check_valid_format(params, fp)) + if (!hw_check_valid_format(subs, params, fp)) continue; if (changed++) { if (rmin > fp->channels) @@ -1714,7 +1721,7 @@ static int hw_rule_format(struct snd_pcm_hw_params *params, list_for_each(p, &subs->fmt_list) { struct audioformat *fp; fp = list_entry(p, struct audioformat, list); - if (!hw_check_valid_format(params, fp)) + if (!hw_check_valid_format(subs, params, fp)) continue; fbits |= (1ULL << fp->format); } @@ -1732,6 +1739,44 @@ static int hw_rule_format(struct snd_pcm_hw_params *params, return changed; } +static int hw_rule_period_time(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_usb_substream *subs = rule->private; + struct audioformat *fp; + struct snd_interval *it; + unsigned char min_datainterval; + unsigned int pmin; + int changed; + + it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_TIME); + hwc_debug("hw_rule_period_time: (%u,%u)\n", it->min, it->max); + min_datainterval = 0xff; + list_for_each_entry(fp, &subs->fmt_list, list) { + if (!hw_check_valid_format(subs, params, fp)) + continue; + min_datainterval = min(min_datainterval, fp->datainterval); + } + if (min_datainterval == 0xff) { + hwc_debug(" --> get emtpy\n"); + it->empty = 1; + return -EINVAL; + } + pmin = 125 * (1 << min_datainterval); + changed = 0; + if (it->min < pmin) { + it->min = pmin; + it->openmin = 0; + changed = 1; + } + if (snd_interval_checkempty(it)) { + it->empty = 1; + return -EINVAL; + } + hwc_debug(" --> (%u,%u) (changed = %d)\n", it->min, it->max, changed); + return changed; +} + /* * If the device supports unusual bit rates, does the request meet these? */ @@ -1777,6 +1822,8 @@ static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime, static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substream *subs) { struct list_head *p; + unsigned int pt, ptmin; + int param_period_time_if_needed; int err; runtime->hw.formats = subs->formats; @@ -1786,6 +1833,7 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre runtime->hw.channels_min = 256; runtime->hw.channels_max = 0; runtime->hw.rates = 0; + ptmin = UINT_MAX; /* check min/max rates and channels */ list_for_each(p, &subs->fmt_list) { struct audioformat *fp; @@ -1804,34 +1852,52 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre runtime->hw.period_bytes_min = runtime->hw.period_bytes_max = fp->frame_size; } + pt = 125 * (1 << fp->datainterval); + ptmin = min(ptmin, pt); } - /* set the period time minimum 1ms */ - /* FIXME: high-speed mode allows 125us minimum period, but many parts - * in the current code assume the 1ms period. - */ + param_period_time_if_needed = SNDRV_PCM_HW_PARAM_PERIOD_TIME; + if (snd_usb_get_speed(subs->dev) != USB_SPEED_HIGH) + /* full speed devices have fixed data packet interval */ + ptmin = 1000; + if (ptmin == 1000) + /* if period time doesn't go below 1 ms, no rules needed */ + param_period_time_if_needed = -1; snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, - 1000, - /*(nrpacks * MAX_URBS) * 1000*/ UINT_MAX); + ptmin, UINT_MAX); if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, hw_rule_rate, subs, SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_CHANNELS, + param_period_time_if_needed, -1)) < 0) return err; if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, hw_rule_channels, subs, SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_RATE, + param_period_time_if_needed, -1)) < 0) return err; if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, hw_rule_format, subs, SNDRV_PCM_HW_PARAM_RATE, SNDRV_PCM_HW_PARAM_CHANNELS, + param_period_time_if_needed, -1)) < 0) return err; + if (param_period_time_if_needed >= 0) { + err = snd_pcm_hw_rule_add(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_TIME, + hw_rule_period_time, subs, + SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_HW_PARAM_CHANNELS, + SNDRV_PCM_HW_PARAM_RATE, + -1); + if (err < 0) + return err; + } if ((err = snd_usb_pcm_check_knot(runtime, subs)) < 0) return err; return 0; -- cgit v1.2.3