diff options
Diffstat (limited to 'sound/usb/clock.c')
-rw-r--r-- | sound/usb/clock.c | 189 |
1 files changed, 135 insertions, 54 deletions
diff --git a/sound/usb/clock.c b/sound/usb/clock.c index 9e2703a25156..3a2ce390e278 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c | |||
@@ -32,6 +32,7 @@ | |||
32 | #include "card.h" | 32 | #include "card.h" |
33 | #include "helper.h" | 33 | #include "helper.h" |
34 | #include "clock.h" | 34 | #include "clock.h" |
35 | #include "quirks.h" | ||
35 | 36 | ||
36 | static struct uac_clock_source_descriptor * | 37 | static struct uac_clock_source_descriptor * |
37 | snd_usb_find_clock_source(struct usb_host_interface *ctrl_iface, | 38 | snd_usb_find_clock_source(struct usb_host_interface *ctrl_iface, |
@@ -99,6 +100,41 @@ static int uac_clock_selector_get_val(struct snd_usb_audio *chip, int selector_i | |||
99 | return buf; | 100 | return buf; |
100 | } | 101 | } |
101 | 102 | ||
103 | static int uac_clock_selector_set_val(struct snd_usb_audio *chip, int selector_id, | ||
104 | unsigned char pin) | ||
105 | { | ||
106 | int ret; | ||
107 | |||
108 | ret = snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0), | ||
109 | UAC2_CS_CUR, | ||
110 | USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, | ||
111 | UAC2_CX_CLOCK_SELECTOR << 8, | ||
112 | snd_usb_ctrl_intf(chip) | (selector_id << 8), | ||
113 | &pin, sizeof(pin)); | ||
114 | if (ret < 0) | ||
115 | return ret; | ||
116 | |||
117 | if (ret != sizeof(pin)) { | ||
118 | snd_printk(KERN_ERR | ||
119 | "usb-audio:%d: setting selector (id %d) unexpected length %d\n", | ||
120 | chip->dev->devnum, selector_id, ret); | ||
121 | return -EINVAL; | ||
122 | } | ||
123 | |||
124 | ret = uac_clock_selector_get_val(chip, selector_id); | ||
125 | if (ret < 0) | ||
126 | return ret; | ||
127 | |||
128 | if (ret != pin) { | ||
129 | snd_printk(KERN_ERR | ||
130 | "usb-audio:%d: setting selector (id %d) to %x failed (current: %d)\n", | ||
131 | chip->dev->devnum, selector_id, pin, ret); | ||
132 | return -EINVAL; | ||
133 | } | ||
134 | |||
135 | return ret; | ||
136 | } | ||
137 | |||
102 | static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id) | 138 | static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id) |
103 | { | 139 | { |
104 | int err; | 140 | int err; |
@@ -131,7 +167,8 @@ static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id) | |||
131 | } | 167 | } |
132 | 168 | ||
133 | static int __uac_clock_find_source(struct snd_usb_audio *chip, | 169 | static int __uac_clock_find_source(struct snd_usb_audio *chip, |
134 | int entity_id, unsigned long *visited) | 170 | int entity_id, unsigned long *visited, |
171 | bool validate) | ||
135 | { | 172 | { |
136 | struct uac_clock_source_descriptor *source; | 173 | struct uac_clock_source_descriptor *source; |
137 | struct uac_clock_selector_descriptor *selector; | 174 | struct uac_clock_selector_descriptor *selector; |
@@ -148,12 +185,19 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip, | |||
148 | 185 | ||
149 | /* first, see if the ID we're looking for is a clock source already */ | 186 | /* first, see if the ID we're looking for is a clock source already */ |
150 | source = snd_usb_find_clock_source(chip->ctrl_intf, entity_id); | 187 | source = snd_usb_find_clock_source(chip->ctrl_intf, entity_id); |
151 | if (source) | 188 | if (source) { |
152 | return source->bClockID; | 189 | entity_id = source->bClockID; |
190 | if (validate && !uac_clock_source_is_valid(chip, entity_id)) { | ||
191 | snd_printk(KERN_ERR "usb-audio:%d: clock source %d is not valid, cannot use\n", | ||
192 | chip->dev->devnum, entity_id); | ||
193 | return -ENXIO; | ||
194 | } | ||
195 | return entity_id; | ||
196 | } | ||
153 | 197 | ||
154 | selector = snd_usb_find_clock_selector(chip->ctrl_intf, entity_id); | 198 | selector = snd_usb_find_clock_selector(chip->ctrl_intf, entity_id); |
155 | if (selector) { | 199 | if (selector) { |
156 | int ret; | 200 | int ret, i, cur; |
157 | 201 | ||
158 | /* the entity ID we are looking for is a selector. | 202 | /* the entity ID we are looking for is a selector. |
159 | * find out what it currently selects */ | 203 | * find out what it currently selects */ |
@@ -164,22 +208,49 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip, | |||
164 | /* Selector values are one-based */ | 208 | /* Selector values are one-based */ |
165 | 209 | ||
166 | if (ret > selector->bNrInPins || ret < 1) { | 210 | if (ret > selector->bNrInPins || ret < 1) { |
167 | printk(KERN_ERR | 211 | snd_printk(KERN_ERR |
168 | "%s(): selector reported illegal value, id %d, ret %d\n", | 212 | "%s(): selector reported illegal value, id %d, ret %d\n", |
169 | __func__, selector->bClockID, ret); | 213 | __func__, selector->bClockID, ret); |
170 | 214 | ||
171 | return -EINVAL; | 215 | return -EINVAL; |
172 | } | 216 | } |
173 | 217 | ||
174 | return __uac_clock_find_source(chip, selector->baCSourceID[ret-1], | 218 | cur = ret; |
175 | visited); | 219 | ret = __uac_clock_find_source(chip, selector->baCSourceID[ret - 1], |
220 | visited, validate); | ||
221 | if (!validate || ret > 0 || !chip->autoclock) | ||
222 | return ret; | ||
223 | |||
224 | /* The current clock source is invalid, try others. */ | ||
225 | for (i = 1; i <= selector->bNrInPins; i++) { | ||
226 | int err; | ||
227 | |||
228 | if (i == cur) | ||
229 | continue; | ||
230 | |||
231 | ret = __uac_clock_find_source(chip, selector->baCSourceID[i - 1], | ||
232 | visited, true); | ||
233 | if (ret < 0) | ||
234 | continue; | ||
235 | |||
236 | err = uac_clock_selector_set_val(chip, entity_id, i); | ||
237 | if (err < 0) | ||
238 | continue; | ||
239 | |||
240 | snd_printk(KERN_INFO | ||
241 | "usb-audio:%d: found and selected valid clock source %d\n", | ||
242 | chip->dev->devnum, ret); | ||
243 | return ret; | ||
244 | } | ||
245 | |||
246 | return -ENXIO; | ||
176 | } | 247 | } |
177 | 248 | ||
178 | /* FIXME: multipliers only act as pass-thru element for now */ | 249 | /* FIXME: multipliers only act as pass-thru element for now */ |
179 | multiplier = snd_usb_find_clock_multiplier(chip->ctrl_intf, entity_id); | 250 | multiplier = snd_usb_find_clock_multiplier(chip->ctrl_intf, entity_id); |
180 | if (multiplier) | 251 | if (multiplier) |
181 | return __uac_clock_find_source(chip, multiplier->bCSourceID, | 252 | return __uac_clock_find_source(chip, multiplier->bCSourceID, |
182 | visited); | 253 | visited, validate); |
183 | 254 | ||
184 | return -EINVAL; | 255 | return -EINVAL; |
185 | } | 256 | } |
@@ -195,11 +266,12 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip, | |||
195 | * | 266 | * |
196 | * Returns the clock source UnitID (>=0) on success, or an error. | 267 | * Returns the clock source UnitID (>=0) on success, or an error. |
197 | */ | 268 | */ |
198 | int snd_usb_clock_find_source(struct snd_usb_audio *chip, int entity_id) | 269 | int snd_usb_clock_find_source(struct snd_usb_audio *chip, int entity_id, |
270 | bool validate) | ||
199 | { | 271 | { |
200 | DECLARE_BITMAP(visited, 256); | 272 | DECLARE_BITMAP(visited, 256); |
201 | memset(visited, 0, sizeof(visited)); | 273 | memset(visited, 0, sizeof(visited)); |
202 | return __uac_clock_find_source(chip, entity_id, visited); | 274 | return __uac_clock_find_source(chip, entity_id, visited, validate); |
203 | } | 275 | } |
204 | 276 | ||
205 | static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface, | 277 | static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface, |
@@ -247,66 +319,73 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface, | |||
247 | return 0; | 319 | return 0; |
248 | } | 320 | } |
249 | 321 | ||
322 | static int get_sample_rate_v2(struct snd_usb_audio *chip, int iface, | ||
323 | int altsetting, int clock) | ||
324 | { | ||
325 | struct usb_device *dev = chip->dev; | ||
326 | __le32 data; | ||
327 | int err; | ||
328 | |||
329 | err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR, | ||
330 | USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, | ||
331 | UAC2_CS_CONTROL_SAM_FREQ << 8, | ||
332 | snd_usb_ctrl_intf(chip) | (clock << 8), | ||
333 | &data, sizeof(data)); | ||
334 | if (err < 0) { | ||
335 | snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq (v2): err %d\n", | ||
336 | dev->devnum, iface, altsetting, err); | ||
337 | return 0; | ||
338 | } | ||
339 | |||
340 | return le32_to_cpu(data); | ||
341 | } | ||
342 | |||
250 | static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface, | 343 | static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface, |
251 | struct usb_host_interface *alts, | 344 | struct usb_host_interface *alts, |
252 | struct audioformat *fmt, int rate) | 345 | struct audioformat *fmt, int rate) |
253 | { | 346 | { |
254 | struct usb_device *dev = chip->dev; | 347 | struct usb_device *dev = chip->dev; |
255 | unsigned char data[4]; | 348 | __le32 data; |
256 | int err, cur_rate, prev_rate; | 349 | int err, cur_rate, prev_rate; |
257 | int clock = snd_usb_clock_find_source(chip, fmt->clock); | 350 | int clock; |
351 | bool writeable; | ||
352 | struct uac_clock_source_descriptor *cs_desc; | ||
258 | 353 | ||
354 | clock = snd_usb_clock_find_source(chip, fmt->clock, true); | ||
259 | if (clock < 0) | 355 | if (clock < 0) |
260 | return clock; | 356 | return clock; |
261 | 357 | ||
262 | if (!uac_clock_source_is_valid(chip, clock)) { | 358 | prev_rate = get_sample_rate_v2(chip, iface, fmt->altsetting, clock); |
263 | /* TODO: should we try to find valid clock setups by ourself? */ | 359 | if (prev_rate == rate) |
264 | snd_printk(KERN_ERR "%d:%d:%d: clock source %d is not valid, cannot use\n", | 360 | return 0; |
265 | dev->devnum, iface, fmt->altsetting, clock); | ||
266 | return -ENXIO; | ||
267 | } | ||
268 | |||
269 | err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR, | ||
270 | USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, | ||
271 | UAC2_CS_CONTROL_SAM_FREQ << 8, | ||
272 | snd_usb_ctrl_intf(chip) | (clock << 8), | ||
273 | data, sizeof(data)); | ||
274 | if (err < 0) { | ||
275 | snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq (v2)\n", | ||
276 | dev->devnum, iface, fmt->altsetting); | ||
277 | prev_rate = 0; | ||
278 | } else { | ||
279 | prev_rate = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); | ||
280 | } | ||
281 | 361 | ||
282 | data[0] = rate; | 362 | cs_desc = snd_usb_find_clock_source(chip->ctrl_intf, clock); |
283 | data[1] = rate >> 8; | 363 | writeable = uac2_control_is_writeable(cs_desc->bmControls, UAC2_CS_CONTROL_SAM_FREQ - 1); |
284 | data[2] = rate >> 16; | 364 | if (writeable) { |
285 | data[3] = rate >> 24; | 365 | data = cpu_to_le32(rate); |
286 | if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR, | 366 | err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR, |
287 | USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, | 367 | USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, |
288 | UAC2_CS_CONTROL_SAM_FREQ << 8, | 368 | UAC2_CS_CONTROL_SAM_FREQ << 8, |
289 | snd_usb_ctrl_intf(chip) | (clock << 8), | 369 | snd_usb_ctrl_intf(chip) | (clock << 8), |
290 | data, sizeof(data))) < 0) { | 370 | &data, sizeof(data)); |
291 | snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d (v2)\n", | 371 | if (err < 0) { |
292 | dev->devnum, iface, fmt->altsetting, rate); | 372 | snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d (v2): err %d\n", |
293 | return err; | 373 | dev->devnum, iface, fmt->altsetting, rate, err); |
294 | } | 374 | return err; |
375 | } | ||
295 | 376 | ||
296 | err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR, | 377 | cur_rate = get_sample_rate_v2(chip, iface, fmt->altsetting, clock); |
297 | USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, | ||
298 | UAC2_CS_CONTROL_SAM_FREQ << 8, | ||
299 | snd_usb_ctrl_intf(chip) | (clock << 8), | ||
300 | data, sizeof(data)); | ||
301 | if (err < 0) { | ||
302 | snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq (v2)\n", | ||
303 | dev->devnum, iface, fmt->altsetting); | ||
304 | cur_rate = 0; | ||
305 | } else { | 378 | } else { |
306 | cur_rate = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); | 379 | cur_rate = prev_rate; |
307 | } | 380 | } |
308 | 381 | ||
309 | if (cur_rate != rate) { | 382 | if (cur_rate != rate) { |
383 | if (!writeable) { | ||
384 | snd_printk(KERN_WARNING | ||
385 | "%d:%d:%d: freq mismatch (RO clock): req %d, clock runs @%d\n", | ||
386 | dev->devnum, iface, fmt->altsetting, rate, cur_rate); | ||
387 | return -ENXIO; | ||
388 | } | ||
310 | snd_printd(KERN_WARNING | 389 | snd_printd(KERN_WARNING |
311 | "current rate %d is different from the runtime rate %d\n", | 390 | "current rate %d is different from the runtime rate %d\n", |
312 | cur_rate, rate); | 391 | cur_rate, rate); |
@@ -316,7 +395,9 @@ static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface, | |||
316 | * interface is active. */ | 395 | * interface is active. */ |
317 | if (rate != prev_rate) { | 396 | if (rate != prev_rate) { |
318 | usb_set_interface(dev, iface, 0); | 397 | usb_set_interface(dev, iface, 0); |
398 | snd_usb_set_interface_quirk(dev); | ||
319 | usb_set_interface(dev, iface, fmt->altsetting); | 399 | usb_set_interface(dev, iface, fmt->altsetting); |
400 | snd_usb_set_interface_quirk(dev); | ||
320 | } | 401 | } |
321 | 402 | ||
322 | return 0; | 403 | return 0; |