Blame view

sound/usb/clock.c 9.03 KB
79f920fbf   Daniel Mack   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   Daniel Mack   ALSA: usb-audio: ...
22
23
  #include <linux/string.h>
  #include <linux/usb.h>
79f920fbf   Daniel Mack   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   Daniel Mack   ALSA: usb-audio: ...
30
31
32
  
  #include "usbaudio.h"
  #include "card.h"
79f920fbf   Daniel Mack   ALSA: usb-audio: ...
33
  #include "helper.h"
f22aa9490   Daniel Mack   ALSA: usb-audio: ...
34
  #include "clock.h"
79f920fbf   Daniel Mack   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   Daniel Mack   ALSA: usb-audio: ...
92
93
  			      UAC2_CX_CLOCK_SELECTOR << 8,
  			      snd_usb_ctrl_intf(chip) | (selector_id << 8),
17d900c4a   Clemens Ladisch   ALSA: usb-audio: ...
94
  			      &buf, sizeof(buf));
79f920fbf   Daniel Mack   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   Daniel Mack   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   Daniel Mack   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   Daniel Mack   ALSA: usb-audio: ...
119
120
  			      UAC2_CS_CONTROL_CLOCK_VALID << 8,
  			      snd_usb_ctrl_intf(chip) | (source_id << 8),
17d900c4a   Clemens Ladisch   ALSA: usb-audio: ...
121
  			      &data, sizeof(data));
79f920fbf   Daniel Mack   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   Daniel Mack   ALSA: usb-audio: ...
127
  		return 0;
79f920fbf   Daniel Mack   ALSA: usb-audio: ...
128
129
130
131
  	}
  
  	return !!data;
  }
79f920fbf   Daniel Mack   ALSA: usb-audio: ...
132
  static int __uac_clock_find_source(struct snd_usb_audio *chip,
79f920fbf   Daniel Mack   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   Daniel Mack   ALSA: usb-audio: ...
150
  	source = snd_usb_find_clock_source(chip->ctrl_intf, entity_id);
79f920fbf   Daniel Mack   ALSA: usb-audio: ...
151
152
  	if (source)
  		return source->bClockID;
3d8d4dcfd   Daniel Mack   ALSA: usb-audio: ...
153
  	selector = snd_usb_find_clock_selector(chip->ctrl_intf, entity_id);
79f920fbf   Daniel Mack   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   Daniel Mack   ALSA: usb-audio: ...
162
  		/* Selector values are one-based */
79f920fbf   Daniel Mack   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   Daniel Mack   ALSA: usb-audio: ...
171
  		return __uac_clock_find_source(chip, selector->baCSourceID[ret-1],
79f920fbf   Daniel Mack   ALSA: usb-audio: ...
172
173
174
175
  					       visited);
  	}
  
  	/* FIXME: multipliers only act as pass-thru element for now */
3d8d4dcfd   Daniel Mack   ALSA: usb-audio: ...
176
  	multiplier = snd_usb_find_clock_multiplier(chip->ctrl_intf, entity_id);
79f920fbf   Daniel Mack   ALSA: usb-audio: ...
177
  	if (multiplier)
3d8d4dcfd   Daniel Mack   ALSA: usb-audio: ...
178
179
  		return __uac_clock_find_source(chip, multiplier->bCSourceID,
  						visited);
79f920fbf   Daniel Mack   ALSA: usb-audio: ...
180
181
182
  
  	return -EINVAL;
  }
157a57b6f   Daniel Mack   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   Daniel Mack   ALSA: usb-audio: ...
194
  int snd_usb_clock_find_source(struct snd_usb_audio *chip, int entity_id)
79f920fbf   Daniel Mack   ALSA: usb-audio: ...
195
196
197
  {
  	DECLARE_BITMAP(visited, 256);
  	memset(visited, 0, sizeof(visited));
3d8d4dcfd   Daniel Mack   ALSA: usb-audio: ...
198
  	return __uac_clock_find_source(chip, entity_id, visited);
79f920fbf   Daniel Mack   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   Clemens Ladisch   ALSA: usb-audio: ...
213
  	if (!(fmt->attributes & UAC_EP_CS_ATTR_SAMPLE_RATE))
79f920fbf   Daniel Mack   ALSA: usb-audio: ...
214
  		return 0;
79f920fbf   Daniel Mack   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   Clemens Ladisch   ALSA: usb-audio: ...
222
  				   data, sizeof(data))) < 0) {
79f920fbf   Daniel Mack   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   Clemens Ladisch   ALSA: usb-audio: ...
232
  				   data, sizeof(data))) < 0) {
79f920fbf   Daniel Mack   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   Daniel Mack   ALSA: usb-audio: ...
256
  	int clock = snd_usb_clock_find_source(chip, fmt->clock);
79f920fbf   Daniel Mack   ALSA: usb-audio: ...
257
258
259
260
261
  
  	if (clock < 0)
  		return clock;
  
  	if (!uac_clock_source_is_valid(chip, clock)) {
157a57b6f   Daniel Mack   ALSA: usb-audio: ...
262
  		/* TODO: should we try to find valid clock setups by ourself? */
79f920fbf   Daniel Mack   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   Daniel Mack   ALSA: usb-audio: ...
275
276
  				   UAC2_CS_CONTROL_SAM_FREQ << 8,
  				   snd_usb_ctrl_intf(chip) | (clock << 8),
17d900c4a   Clemens Ladisch   ALSA: usb-audio: ...
277
  				   data, sizeof(data))) < 0) {
79f920fbf   Daniel Mack   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   Daniel Mack   ALSA: usb-audio: ...
286
287
  				   UAC2_CS_CONTROL_SAM_FREQ << 8,
  				   snd_usb_ctrl_intf(chip) | (clock << 8),
17d900c4a   Clemens Ladisch   ALSA: usb-audio: ...
288
  				   data, sizeof(data))) < 0) {
79f920fbf   Daniel Mack   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   Clemens Ladisch   ALSA: usb-audio: ...
311
  	default:
79f920fbf   Daniel Mack   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   Daniel Mack   ALSA: usb-audio: ...
317
  }