aboutsummaryrefslogtreecommitdiffstats
path: root/sound/usb/clock.c
diff options
context:
space:
mode:
authorDaniel Mack <daniel@caiaq.de>2010-05-31 08:51:31 -0400
committerTakashi Iwai <tiwai@suse.de>2010-05-31 12:16:59 -0400
commit79f920fbff566ffc9de44111eb1456a3cef310f0 (patch)
tree97b574ee648320163fcbcf8793b23e826fb3a1f8 /sound/usb/clock.c
parent7176d37a28fa4ea7e32815007673f578cdcebf51 (diff)
ALSA: usb-audio: parse clock topology of UAC2 devices
Audio devices which comply to the UAC2 standard can export complex clock topologies in its descriptors and set up links between them. The entities that are defined are - clock sources, which define the end-leafs. - clock selectors, which act as switch to select one out of many possible clocks sources. - clock multipliers, which have an input clock source, and act as clock source again. They can be used to derive one clock from another. All sample rate changes, clock validity queries and the like must go to clock source elements, while clock selectors and multipliers can be used as terminal clock source. The following patch adds a parser for these elements and functions to iterate over the tree and find the leaf nodes (clock sources). The samplerate set functions were moved to the new clock.c file. Signed-off-by: Daniel Mack <daniel@caiaq.de> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/usb/clock.c')
-rw-r--r--sound/usb/clock.c311
1 files changed, 311 insertions, 0 deletions
diff --git a/sound/usb/clock.c b/sound/usb/clock.c
new file mode 100644
index 000000000000..b7aadd614c70
--- /dev/null
+++ b/sound/usb/clock.c
@@ -0,0 +1,311 @@
1/*
2 * Clock domain and sample rate management functions
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 *
18 */
19
20#include <linux/bitops.h>
21#include <linux/init.h>
22#include <linux/list.h>
23#include <linux/slab.h>
24#include <linux/string.h>
25#include <linux/usb.h>
26#include <linux/moduleparam.h>
27#include <linux/mutex.h>
28#include <linux/usb/audio.h>
29#include <linux/usb/audio-v2.h>
30
31#include <sound/core.h>
32#include <sound/info.h>
33#include <sound/pcm.h>
34#include <sound/pcm_params.h>
35#include <sound/initval.h>
36
37#include "usbaudio.h"
38#include "card.h"
39#include "midi.h"
40#include "mixer.h"
41#include "proc.h"
42#include "quirks.h"
43#include "endpoint.h"
44#include "helper.h"
45#include "debug.h"
46#include "pcm.h"
47#include "urb.h"
48#include "format.h"
49
50static struct uac_clock_source_descriptor *
51 snd_usb_find_clock_source(struct usb_host_interface *ctrl_iface,
52 int clock_id)
53{
54 struct uac_clock_source_descriptor *cs = NULL;
55
56 while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra,
57 ctrl_iface->extralen,
58 cs, UAC2_CLOCK_SOURCE))) {
59 if (cs->bClockID == clock_id)
60 return cs;
61 }
62
63 return NULL;
64}
65
66static struct uac_clock_selector_descriptor *
67 snd_usb_find_clock_selector(struct usb_host_interface *ctrl_iface,
68 int clock_id)
69{
70 struct uac_clock_selector_descriptor *cs = NULL;
71
72 while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra,
73 ctrl_iface->extralen,
74 cs, UAC2_CLOCK_SELECTOR))) {
75 if (cs->bClockID == clock_id)
76 return cs;
77 }
78
79 return NULL;
80}
81
82static struct uac_clock_multiplier_descriptor *
83 snd_usb_find_clock_multiplier(struct usb_host_interface *ctrl_iface,
84 int clock_id)
85{
86 struct uac_clock_multiplier_descriptor *cs = NULL;
87
88 while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra,
89 ctrl_iface->extralen,
90 cs, UAC2_CLOCK_MULTIPLIER))) {
91 if (cs->bClockID == clock_id)
92 return cs;
93 }
94
95 return NULL;
96}
97
98static int uac_clock_selector_get_val(struct snd_usb_audio *chip, int selector_id)
99{
100 unsigned char buf;
101 int ret;
102
103 ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0),
104 UAC2_CS_CUR,
105 USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
106 UAC2_CX_CLOCK_SELECTOR << 8, selector_id << 8,
107 &buf, sizeof(buf), 1000);
108
109 if (ret < 0)
110 return ret;
111
112 return buf;
113}
114
115static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id)
116{
117 int err;
118 unsigned char data;
119 struct usb_device *dev = chip->dev;
120
121 err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
122 USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
123 UAC2_CS_CONTROL_CLOCK_VALID << 8, source_id << 8,
124 &data, sizeof(data), 1000);
125
126 if (err < 0) {
127 snd_printk(KERN_WARNING "%s(): cannot get clock validity for id %d\n",
128 __func__, source_id);
129 return err;
130 }
131
132 return !!data;
133}
134
135/* Try to find the clock source ID of a given clock entity */
136
137static int __uac_clock_find_source(struct snd_usb_audio *chip,
138 struct usb_host_interface *host_iface,
139 int entity_id, unsigned long *visited)
140{
141 struct uac_clock_source_descriptor *source;
142 struct uac_clock_selector_descriptor *selector;
143 struct uac_clock_multiplier_descriptor *multiplier;
144
145 entity_id &= 0xff;
146
147 if (test_and_set_bit(entity_id, visited)) {
148 snd_printk(KERN_WARNING
149 "%s(): recursive clock topology detected, id %d.\n",
150 __func__, entity_id);
151 return -EINVAL;
152 }
153
154 /* first, see if the ID we're looking for is a clock source already */
155 source = snd_usb_find_clock_source(host_iface, entity_id);
156 if (source)
157 return source->bClockID;
158
159 selector = snd_usb_find_clock_selector(host_iface, entity_id);
160 if (selector) {
161 int ret;
162
163 /* the entity ID we are looking for is a selector.
164 * find out what it currently selects */
165 ret = uac_clock_selector_get_val(chip, selector->bClockID);
166 if (ret < 0)
167 return ret;
168
169 if (ret > selector->bNrInPins || ret < 1) {
170 printk(KERN_ERR
171 "%s(): selector reported illegal value, id %d, ret %d\n",
172 __func__, selector->bClockID, ret);
173
174 return -EINVAL;
175 }
176
177 return __uac_clock_find_source(chip, host_iface,
178 selector->baCSourceID[ret-1],
179 visited);
180 }
181
182 /* FIXME: multipliers only act as pass-thru element for now */
183 multiplier = snd_usb_find_clock_multiplier(host_iface, entity_id);
184 if (multiplier)
185 return __uac_clock_find_source(chip, host_iface,
186 multiplier->bCSourceID, visited);
187
188 return -EINVAL;
189}
190
191int snd_usb_clock_find_source(struct snd_usb_audio *chip,
192 struct usb_host_interface *host_iface,
193 int entity_id)
194{
195 DECLARE_BITMAP(visited, 256);
196 memset(visited, 0, sizeof(visited));
197 return __uac_clock_find_source(chip, host_iface, entity_id, visited);
198}
199
200static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
201 struct usb_host_interface *alts,
202 struct audioformat *fmt, int rate)
203{
204 struct usb_device *dev = chip->dev;
205 unsigned int ep;
206 unsigned char data[3];
207 int err, crate;
208
209 ep = get_endpoint(alts, 0)->bEndpointAddress;
210
211 /* if endpoint doesn't have sampling rate control, bail out */
212 if (!(fmt->attributes & UAC_EP_CS_ATTR_SAMPLE_RATE)) {
213 snd_printk(KERN_WARNING "%d:%d:%d: endpoint lacks sample rate attribute bit, cannot set.\n",
214 dev->devnum, iface, fmt->altsetting);
215 return 0;
216 }
217
218 data[0] = rate;
219 data[1] = rate >> 8;
220 data[2] = rate >> 16;
221 if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
222 USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_OUT,
223 UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep,
224 data, sizeof(data), 1000)) < 0) {
225 snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d to ep %#x\n",
226 dev->devnum, iface, fmt->altsetting, rate, ep);
227 return err;
228 }
229
230 if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR,
231 USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN,
232 UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep,
233 data, sizeof(data), 1000)) < 0) {
234 snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq at ep %#x\n",
235 dev->devnum, iface, fmt->altsetting, ep);
236 return 0; /* some devices don't support reading */
237 }
238
239 crate = data[0] | (data[1] << 8) | (data[2] << 16);
240 if (crate != rate) {
241 snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate);
242 // runtime->rate = crate;
243 }
244
245 return 0;
246}
247
248static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface,
249 struct usb_host_interface *alts,
250 struct audioformat *fmt, int rate)
251{
252 struct usb_device *dev = chip->dev;
253 unsigned char data[4];
254 int err, crate;
255 int clock = snd_usb_clock_find_source(chip, chip->ctrl_intf, fmt->clock);
256
257 if (clock < 0)
258 return clock;
259
260 if (!uac_clock_source_is_valid(chip, clock)) {
261 snd_printk(KERN_ERR "%d:%d:%d: clock source %d is not valid, cannot use\n",
262 dev->devnum, iface, fmt->altsetting, clock);
263 return -ENXIO;
264 }
265
266 data[0] = rate;
267 data[1] = rate >> 8;
268 data[2] = rate >> 16;
269 data[3] = rate >> 24;
270 if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR,
271 USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
272 UAC2_CS_CONTROL_SAM_FREQ << 8, clock << 8,
273 data, sizeof(data), 1000)) < 0) {
274 snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d (v2)\n",
275 dev->devnum, iface, fmt->altsetting, rate);
276 return err;
277 }
278
279 if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
280 USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
281 UAC2_CS_CONTROL_SAM_FREQ << 8, clock << 8,
282 data, sizeof(data), 1000)) < 0) {
283 snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq (v2)\n",
284 dev->devnum, iface, fmt->altsetting);
285 return err;
286 }
287
288 crate = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
289 if (crate != rate)
290 snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate);
291
292 return 0;
293}
294
295int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
296 struct usb_host_interface *alts,
297 struct audioformat *fmt, int rate)
298{
299 struct usb_interface_descriptor *altsd = get_iface_desc(alts);
300
301 switch (altsd->bInterfaceProtocol) {
302 case UAC_VERSION_1:
303 return set_sample_rate_v1(chip, iface, alts, fmt, rate);
304
305 case UAC_VERSION_2:
306 return set_sample_rate_v2(chip, iface, alts, fmt, rate);
307 }
308
309 return -EINVAL;
310}
311