Blame view
sound/usb/clock.c
9.03 KB
79f920fbf ALSA: usb-audio: ... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
/* * Clock domain and sample rate management functions * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include <linux/bitops.h> #include <linux/init.h> |
79f920fbf ALSA: usb-audio: ... |
22 23 |
#include <linux/string.h> #include <linux/usb.h> |
79f920fbf ALSA: usb-audio: ... |
24 25 26 27 28 29 |
#include <linux/usb/audio.h> #include <linux/usb/audio-v2.h> #include <sound/core.h> #include <sound/info.h> #include <sound/pcm.h> |
79f920fbf ALSA: usb-audio: ... |
30 31 32 |
#include "usbaudio.h" #include "card.h" |
79f920fbf ALSA: usb-audio: ... |
33 |
#include "helper.h" |
f22aa9490 ALSA: usb-audio: ... |
34 |
#include "clock.h" |
79f920fbf ALSA: usb-audio: ... |
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
static struct uac_clock_source_descriptor * snd_usb_find_clock_source(struct usb_host_interface *ctrl_iface, int clock_id) { struct uac_clock_source_descriptor *cs = NULL; while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra, ctrl_iface->extralen, cs, UAC2_CLOCK_SOURCE))) { if (cs->bClockID == clock_id) return cs; } return NULL; } static struct uac_clock_selector_descriptor * snd_usb_find_clock_selector(struct usb_host_interface *ctrl_iface, int clock_id) { struct uac_clock_selector_descriptor *cs = NULL; while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra, ctrl_iface->extralen, cs, UAC2_CLOCK_SELECTOR))) { if (cs->bClockID == clock_id) return cs; } return NULL; } static struct uac_clock_multiplier_descriptor * snd_usb_find_clock_multiplier(struct usb_host_interface *ctrl_iface, int clock_id) { struct uac_clock_multiplier_descriptor *cs = NULL; while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra, ctrl_iface->extralen, cs, UAC2_CLOCK_MULTIPLIER))) { if (cs->bClockID == clock_id) return cs; } return NULL; } static int uac_clock_selector_get_val(struct snd_usb_audio *chip, int selector_id) { unsigned char buf; int ret; ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), UAC2_CS_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, |
11bcbc443 ALSA: usb-audio: ... |
92 93 |
UAC2_CX_CLOCK_SELECTOR << 8, snd_usb_ctrl_intf(chip) | (selector_id << 8), |
17d900c4a ALSA: usb-audio: ... |
94 |
&buf, sizeof(buf)); |
79f920fbf ALSA: usb-audio: ... |
95 96 97 98 99 100 101 102 103 104 105 106 |
if (ret < 0) return ret; return buf; } static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id) { int err; unsigned char data; struct usb_device *dev = chip->dev; |
3bc6fbc74 ALSA: usb-audio: ... |
107 108 109 110 111 112 113 114 115 |
struct uac_clock_source_descriptor *cs_desc = snd_usb_find_clock_source(chip->ctrl_intf, source_id); if (!cs_desc) return 0; /* If a clock source can't tell us whether it's valid, we assume it is */ if (!uac2_control_is_readable(cs_desc->bmControls, UAC2_CS_CONTROL_CLOCK_VALID)) return 1; |
79f920fbf ALSA: usb-audio: ... |
116 117 118 |
err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR, USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, |
11bcbc443 ALSA: usb-audio: ... |
119 120 |
UAC2_CS_CONTROL_CLOCK_VALID << 8, snd_usb_ctrl_intf(chip) | (source_id << 8), |
17d900c4a ALSA: usb-audio: ... |
121 |
&data, sizeof(data)); |
79f920fbf ALSA: usb-audio: ... |
122 123 124 125 126 |
if (err < 0) { snd_printk(KERN_WARNING "%s(): cannot get clock validity for id %d ", __func__, source_id); |
3bc6fbc74 ALSA: usb-audio: ... |
127 |
return 0; |
79f920fbf ALSA: usb-audio: ... |
128 129 130 131 |
} return !!data; } |
79f920fbf ALSA: usb-audio: ... |
132 |
static int __uac_clock_find_source(struct snd_usb_audio *chip, |
79f920fbf ALSA: usb-audio: ... |
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
int entity_id, unsigned long *visited) { struct uac_clock_source_descriptor *source; struct uac_clock_selector_descriptor *selector; struct uac_clock_multiplier_descriptor *multiplier; entity_id &= 0xff; if (test_and_set_bit(entity_id, visited)) { snd_printk(KERN_WARNING "%s(): recursive clock topology detected, id %d. ", __func__, entity_id); return -EINVAL; } /* first, see if the ID we're looking for is a clock source already */ |
3d8d4dcfd ALSA: usb-audio: ... |
150 |
source = snd_usb_find_clock_source(chip->ctrl_intf, entity_id); |
79f920fbf ALSA: usb-audio: ... |
151 152 |
if (source) return source->bClockID; |
3d8d4dcfd ALSA: usb-audio: ... |
153 |
selector = snd_usb_find_clock_selector(chip->ctrl_intf, entity_id); |
79f920fbf ALSA: usb-audio: ... |
154 155 156 157 158 159 160 161 |
if (selector) { int ret; /* the entity ID we are looking for is a selector. * find out what it currently selects */ ret = uac_clock_selector_get_val(chip, selector->bClockID); if (ret < 0) return ret; |
157a57b6f ALSA: usb-audio: ... |
162 |
/* Selector values are one-based */ |
79f920fbf ALSA: usb-audio: ... |
163 164 165 166 167 168 169 170 |
if (ret > selector->bNrInPins || ret < 1) { printk(KERN_ERR "%s(): selector reported illegal value, id %d, ret %d ", __func__, selector->bClockID, ret); return -EINVAL; } |
3d8d4dcfd ALSA: usb-audio: ... |
171 |
return __uac_clock_find_source(chip, selector->baCSourceID[ret-1], |
79f920fbf ALSA: usb-audio: ... |
172 173 174 175 |
visited); } /* FIXME: multipliers only act as pass-thru element for now */ |
3d8d4dcfd ALSA: usb-audio: ... |
176 |
multiplier = snd_usb_find_clock_multiplier(chip->ctrl_intf, entity_id); |
79f920fbf ALSA: usb-audio: ... |
177 |
if (multiplier) |
3d8d4dcfd ALSA: usb-audio: ... |
178 179 |
return __uac_clock_find_source(chip, multiplier->bCSourceID, visited); |
79f920fbf ALSA: usb-audio: ... |
180 181 182 |
return -EINVAL; } |
157a57b6f ALSA: usb-audio: ... |
183 184 185 186 187 188 189 190 191 192 193 |
/* * For all kinds of sample rate settings and other device queries, * the clock source (end-leaf) must be used. However, clock selectors, * clock multipliers and sample rate converters may be specified as * clock source input to terminal. This functions walks the clock path * to its end and tries to find the source. * * The 'visited' bitfield is used internally to detect recursive loops. * * Returns the clock source UnitID (>=0) on success, or an error. */ |
3d8d4dcfd ALSA: usb-audio: ... |
194 |
int snd_usb_clock_find_source(struct snd_usb_audio *chip, int entity_id) |
79f920fbf ALSA: usb-audio: ... |
195 196 197 |
{ DECLARE_BITMAP(visited, 256); memset(visited, 0, sizeof(visited)); |
3d8d4dcfd ALSA: usb-audio: ... |
198 |
return __uac_clock_find_source(chip, entity_id, visited); |
79f920fbf ALSA: usb-audio: ... |
199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
} static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface, struct usb_host_interface *alts, struct audioformat *fmt, int rate) { struct usb_device *dev = chip->dev; unsigned int ep; unsigned char data[3]; int err, crate; ep = get_endpoint(alts, 0)->bEndpointAddress; /* if endpoint doesn't have sampling rate control, bail out */ |
d32d552e6 ALSA: usb-audio: ... |
213 |
if (!(fmt->attributes & UAC_EP_CS_ATTR_SAMPLE_RATE)) |
79f920fbf ALSA: usb-audio: ... |
214 |
return 0; |
79f920fbf ALSA: usb-audio: ... |
215 216 217 218 219 220 221 |
data[0] = rate; data[1] = rate >> 8; data[2] = rate >> 16; if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR, USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_OUT, UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep, |
17d900c4a ALSA: usb-audio: ... |
222 |
data, sizeof(data))) < 0) { |
79f920fbf ALSA: usb-audio: ... |
223 224 225 226 227 228 229 230 231 |
snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d to ep %#x ", dev->devnum, iface, fmt->altsetting, rate, ep); return err; } if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR, USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN, UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep, |
17d900c4a ALSA: usb-audio: ... |
232 |
data, sizeof(data))) < 0) { |
79f920fbf ALSA: usb-audio: ... |
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 |
snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq at ep %#x ", dev->devnum, iface, fmt->altsetting, ep); return 0; /* some devices don't support reading */ } crate = data[0] | (data[1] << 8) | (data[2] << 16); if (crate != rate) { snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d ", crate, rate); // runtime->rate = crate; } return 0; } static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface, struct usb_host_interface *alts, struct audioformat *fmt, int rate) { struct usb_device *dev = chip->dev; unsigned char data[4]; int err, crate; |
3d8d4dcfd ALSA: usb-audio: ... |
256 |
int clock = snd_usb_clock_find_source(chip, fmt->clock); |
79f920fbf ALSA: usb-audio: ... |
257 258 259 260 261 |
if (clock < 0) return clock; if (!uac_clock_source_is_valid(chip, clock)) { |
157a57b6f ALSA: usb-audio: ... |
262 |
/* TODO: should we try to find valid clock setups by ourself? */ |
79f920fbf ALSA: usb-audio: ... |
263 264 265 266 267 268 269 270 271 272 273 274 |
snd_printk(KERN_ERR "%d:%d:%d: clock source %d is not valid, cannot use ", dev->devnum, iface, fmt->altsetting, clock); return -ENXIO; } data[0] = rate; data[1] = rate >> 8; data[2] = rate >> 16; data[3] = rate >> 24; if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR, USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, |
11bcbc443 ALSA: usb-audio: ... |
275 276 |
UAC2_CS_CONTROL_SAM_FREQ << 8, snd_usb_ctrl_intf(chip) | (clock << 8), |
17d900c4a ALSA: usb-audio: ... |
277 |
data, sizeof(data))) < 0) { |
79f920fbf ALSA: usb-audio: ... |
278 279 280 281 282 283 284 285 |
snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d (v2) ", dev->devnum, iface, fmt->altsetting, rate); return err; } if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR, USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, |
11bcbc443 ALSA: usb-audio: ... |
286 287 |
UAC2_CS_CONTROL_SAM_FREQ << 8, snd_usb_ctrl_intf(chip) | (clock << 8), |
17d900c4a ALSA: usb-audio: ... |
288 |
data, sizeof(data))) < 0) { |
79f920fbf ALSA: usb-audio: ... |
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 |
snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq (v2) ", dev->devnum, iface, fmt->altsetting); return err; } crate = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); if (crate != rate) snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d ", crate, rate); return 0; } int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface, struct usb_host_interface *alts, struct audioformat *fmt, int rate) { struct usb_interface_descriptor *altsd = get_iface_desc(alts); switch (altsd->bInterfaceProtocol) { case UAC_VERSION_1: |
a2acad829 ALSA: usb-audio: ... |
311 |
default: |
79f920fbf ALSA: usb-audio: ... |
312 313 314 315 316 |
return set_sample_rate_v1(chip, iface, alts, fmt, rate); case UAC_VERSION_2: return set_sample_rate_v2(chip, iface, alts, fmt, rate); } |
79f920fbf ALSA: usb-audio: ... |
317 |
} |