Commit e922b0028fad87de0d262f9fa51f98595d2df258
1 parent
4235a31784
Exists in
master
and in
7 other branches
[ALSA] Move vmaster code to sound core
Move the codes for virtual master controls to sound core part so that not only hda-intel drivers can use it. Signed-off-by: Takashi Iwai <tiwai@suse.de>
Showing 7 changed files with 381 additions and 371 deletions Side-by-side Diff
include/sound/control.h
... | ... | @@ -169,5 +169,12 @@ |
169 | 169 | int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol, |
170 | 170 | struct snd_ctl_elem_info *uinfo); |
171 | 171 | |
172 | +/* | |
173 | + * virtual master control | |
174 | + */ | |
175 | +struct snd_kcontrol *snd_ctl_make_virtual_master(char *name, | |
176 | + const unsigned int *tlv); | |
177 | +int snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave); | |
178 | + | |
172 | 179 | #endif /* __SOUND_CONTROL_H */ |
sound/core/Kconfig
sound/core/Makefile
... | ... | @@ -6,6 +6,7 @@ |
6 | 6 | snd-y := sound.o init.o memory.o info.o control.o misc.o device.o |
7 | 7 | snd-$(CONFIG_ISA_DMA_API) += isadma.o |
8 | 8 | snd-$(CONFIG_SND_OSSEMUL) += sound_oss.o info_oss.o |
9 | +snd-$(CONFIG_SND_VMASTER) += vmaster.o | |
9 | 10 | |
10 | 11 | snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \ |
11 | 12 | pcm_memory.o |
sound/core/vmaster.c
1 | +/* | |
2 | + * Virtual master and slave controls | |
3 | + * | |
4 | + * Copyright (c) 2008 by Takashi Iwai <tiwai@suse.de> | |
5 | + * | |
6 | + * This program is free software; you can redistribute it and/or | |
7 | + * modify it under the terms of the GNU General Public License as | |
8 | + * published by the Free Software Foundation, version 2. | |
9 | + * | |
10 | + */ | |
11 | + | |
12 | +#include <linux/slab.h> | |
13 | +#include <sound/core.h> | |
14 | +#include <sound/control.h> | |
15 | + | |
16 | +/* | |
17 | + * a subset of information returned via ctl info callback | |
18 | + */ | |
19 | +struct link_ctl_info { | |
20 | + int type; /* value type */ | |
21 | + int count; /* item count */ | |
22 | + int min_val, max_val; /* min, max values */ | |
23 | +}; | |
24 | + | |
25 | +/* | |
26 | + * link master - this contains a list of slave controls that are | |
27 | + * identical types, i.e. info returns the same value type and value | |
28 | + * ranges, but may have different number of counts. | |
29 | + * | |
30 | + * The master control is so far only mono volume/switch for simplicity. | |
31 | + * The same value will be applied to all slaves. | |
32 | + */ | |
33 | +struct link_master { | |
34 | + struct list_head slaves; | |
35 | + struct link_ctl_info info; | |
36 | + int val; /* the master value */ | |
37 | +}; | |
38 | + | |
39 | +/* | |
40 | + * link slave - this contains a slave control element | |
41 | + * | |
42 | + * It fakes the control callbacsk with additional attenuation by the | |
43 | + * master control. A slave may have either one or two channels. | |
44 | + */ | |
45 | + | |
46 | +struct link_slave { | |
47 | + struct list_head list; | |
48 | + struct link_master *master; | |
49 | + struct link_ctl_info info; | |
50 | + int vals[2]; /* current values */ | |
51 | + struct snd_kcontrol slave; /* the copy of original control entry */ | |
52 | +}; | |
53 | + | |
54 | +/* get the slave ctl info and save the initial values */ | |
55 | +static int slave_init(struct link_slave *slave) | |
56 | +{ | |
57 | + struct snd_ctl_elem_info *uinfo; | |
58 | + struct snd_ctl_elem_value *uctl; | |
59 | + int err, ch; | |
60 | + | |
61 | + if (slave->info.count) | |
62 | + return 0; /* already initialized */ | |
63 | + | |
64 | + uinfo = kmalloc(sizeof(*uinfo), GFP_KERNEL); | |
65 | + if (!uinfo) | |
66 | + return -ENOMEM; | |
67 | + uinfo->id = slave->slave.id; | |
68 | + err = slave->slave.info(&slave->slave, uinfo); | |
69 | + if (err < 0) { | |
70 | + kfree(uinfo); | |
71 | + return err; | |
72 | + } | |
73 | + slave->info.type = uinfo->type; | |
74 | + slave->info.count = uinfo->count; | |
75 | + if (slave->info.count > 2 || | |
76 | + (slave->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER && | |
77 | + slave->info.type != SNDRV_CTL_ELEM_TYPE_BOOLEAN)) { | |
78 | + snd_printk(KERN_ERR "invalid slave element\n"); | |
79 | + kfree(uinfo); | |
80 | + return -EINVAL; | |
81 | + } | |
82 | + slave->info.min_val = uinfo->value.integer.min; | |
83 | + slave->info.max_val = uinfo->value.integer.max; | |
84 | + kfree(uinfo); | |
85 | + | |
86 | + uctl = kmalloc(sizeof(*uctl), GFP_KERNEL); | |
87 | + if (!uctl) | |
88 | + return -ENOMEM; | |
89 | + uctl->id = slave->slave.id; | |
90 | + err = slave->slave.get(&slave->slave, uctl); | |
91 | + for (ch = 0; ch < slave->info.count; ch++) | |
92 | + slave->vals[ch] = uctl->value.integer.value[ch]; | |
93 | + kfree(uctl); | |
94 | + return 0; | |
95 | +} | |
96 | + | |
97 | +/* initialize master volume */ | |
98 | +static int master_init(struct link_master *master) | |
99 | +{ | |
100 | + struct link_slave *slave; | |
101 | + | |
102 | + if (master->info.count) | |
103 | + return 0; /* already initialized */ | |
104 | + | |
105 | + list_for_each_entry(slave, &master->slaves, list) { | |
106 | + int err = slave_init(slave); | |
107 | + if (err < 0) | |
108 | + return err; | |
109 | + master->info = slave->info; | |
110 | + master->info.count = 1; /* always mono */ | |
111 | + /* set full volume as default (= no attenuation) */ | |
112 | + master->val = master->info.max_val; | |
113 | + return 0; | |
114 | + } | |
115 | + return -ENOENT; | |
116 | +} | |
117 | + | |
118 | +static int slave_get_val(struct link_slave *slave, | |
119 | + struct snd_ctl_elem_value *ucontrol) | |
120 | +{ | |
121 | + int err, ch; | |
122 | + | |
123 | + err = slave_init(slave); | |
124 | + if (err < 0) | |
125 | + return err; | |
126 | + for (ch = 0; ch < slave->info.count; ch++) | |
127 | + ucontrol->value.integer.value[ch] = slave->vals[ch]; | |
128 | + return 0; | |
129 | +} | |
130 | + | |
131 | +static int slave_put_val(struct link_slave *slave, | |
132 | + struct snd_ctl_elem_value *ucontrol) | |
133 | +{ | |
134 | + int err, ch, vol; | |
135 | + | |
136 | + err = master_init(slave->master); | |
137 | + if (err < 0) | |
138 | + return err; | |
139 | + | |
140 | + switch (slave->info.type) { | |
141 | + case SNDRV_CTL_ELEM_TYPE_BOOLEAN: | |
142 | + for (ch = 0; ch < slave->info.count; ch++) | |
143 | + ucontrol->value.integer.value[ch] &= | |
144 | + !!slave->master->val; | |
145 | + break; | |
146 | + case SNDRV_CTL_ELEM_TYPE_INTEGER: | |
147 | + for (ch = 0; ch < slave->info.count; ch++) { | |
148 | + /* max master volume is supposed to be 0 dB */ | |
149 | + vol = ucontrol->value.integer.value[ch]; | |
150 | + vol += slave->master->val - slave->master->info.max_val; | |
151 | + if (vol < slave->info.min_val) | |
152 | + vol = slave->info.min_val; | |
153 | + else if (vol > slave->info.max_val) | |
154 | + vol = slave->info.max_val; | |
155 | + ucontrol->value.integer.value[ch] = vol; | |
156 | + } | |
157 | + break; | |
158 | + } | |
159 | + return slave->slave.put(&slave->slave, ucontrol); | |
160 | +} | |
161 | + | |
162 | +/* | |
163 | + * ctl callbacks for slaves | |
164 | + */ | |
165 | +static int slave_info(struct snd_kcontrol *kcontrol, | |
166 | + struct snd_ctl_elem_info *uinfo) | |
167 | +{ | |
168 | + struct link_slave *slave = snd_kcontrol_chip(kcontrol); | |
169 | + return slave->slave.info(&slave->slave, uinfo); | |
170 | +} | |
171 | + | |
172 | +static int slave_get(struct snd_kcontrol *kcontrol, | |
173 | + struct snd_ctl_elem_value *ucontrol) | |
174 | +{ | |
175 | + struct link_slave *slave = snd_kcontrol_chip(kcontrol); | |
176 | + return slave_get_val(slave, ucontrol); | |
177 | +} | |
178 | + | |
179 | +static int slave_put(struct snd_kcontrol *kcontrol, | |
180 | + struct snd_ctl_elem_value *ucontrol) | |
181 | +{ | |
182 | + struct link_slave *slave = snd_kcontrol_chip(kcontrol); | |
183 | + int err, ch, changed = 0; | |
184 | + | |
185 | + err = slave_init(slave); | |
186 | + if (err < 0) | |
187 | + return err; | |
188 | + for (ch = 0; ch < slave->info.count; ch++) { | |
189 | + if (slave->vals[ch] != ucontrol->value.integer.value[ch]) { | |
190 | + changed = 1; | |
191 | + slave->vals[ch] = ucontrol->value.integer.value[ch]; | |
192 | + } | |
193 | + } | |
194 | + if (!changed) | |
195 | + return 0; | |
196 | + return slave_put_val(slave, ucontrol); | |
197 | +} | |
198 | + | |
199 | +static int slave_tlv_cmd(struct snd_kcontrol *kcontrol, | |
200 | + int op_flag, unsigned int size, | |
201 | + unsigned int __user *tlv) | |
202 | +{ | |
203 | + struct link_slave *slave = snd_kcontrol_chip(kcontrol); | |
204 | + /* FIXME: this assumes that the max volume is 0 dB */ | |
205 | + return slave->slave.tlv.c(&slave->slave, op_flag, size, tlv); | |
206 | +} | |
207 | + | |
208 | +static void slave_free(struct snd_kcontrol *kcontrol) | |
209 | +{ | |
210 | + struct link_slave *slave = snd_kcontrol_chip(kcontrol); | |
211 | + if (slave->slave.private_free) | |
212 | + slave->slave.private_free(&slave->slave); | |
213 | + if (slave->master) | |
214 | + list_del(&slave->list); | |
215 | + kfree(slave); | |
216 | +} | |
217 | + | |
218 | +/* | |
219 | + * Add a slave control to the group with the given master control | |
220 | + * | |
221 | + * All slaves must be the same type (returning the same information | |
222 | + * via info callback). The fucntion doesn't check it, so it's your | |
223 | + * responsibility. | |
224 | + * | |
225 | + * Also, some additional limitations: | |
226 | + * - at most two channels | |
227 | + * - logarithmic volume control (dB level), no linear volume | |
228 | + * - master can only attenuate the volume, no gain | |
229 | + */ | |
230 | +int snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave) | |
231 | +{ | |
232 | + struct link_master *master_link = snd_kcontrol_chip(master); | |
233 | + struct link_slave *srec; | |
234 | + | |
235 | + srec = kzalloc(sizeof(*srec) + | |
236 | + slave->count * sizeof(*slave->vd), GFP_KERNEL); | |
237 | + if (!srec) | |
238 | + return -ENOMEM; | |
239 | + srec->slave = *slave; | |
240 | + memcpy(srec->slave.vd, slave->vd, slave->count * sizeof(*slave->vd)); | |
241 | + srec->master = master_link; | |
242 | + | |
243 | + /* override callbacks */ | |
244 | + slave->info = slave_info; | |
245 | + slave->get = slave_get; | |
246 | + slave->put = slave_put; | |
247 | + if (slave->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) | |
248 | + slave->tlv.c = slave_tlv_cmd; | |
249 | + slave->private_data = srec; | |
250 | + slave->private_free = slave_free; | |
251 | + | |
252 | + list_add_tail(&srec->list, &master_link->slaves); | |
253 | + return 0; | |
254 | +} | |
255 | + | |
256 | +EXPORT_SYMBOL(snd_ctl_add_slave); | |
257 | + | |
258 | +/* | |
259 | + * ctl callbacks for master controls | |
260 | + */ | |
261 | +static int master_info(struct snd_kcontrol *kcontrol, | |
262 | + struct snd_ctl_elem_info *uinfo) | |
263 | +{ | |
264 | + struct link_master *master = snd_kcontrol_chip(kcontrol); | |
265 | + int ret; | |
266 | + | |
267 | + ret = master_init(master); | |
268 | + if (ret < 0) | |
269 | + return ret; | |
270 | + uinfo->type = master->info.type; | |
271 | + uinfo->count = master->info.count; | |
272 | + uinfo->value.integer.min = master->info.min_val; | |
273 | + uinfo->value.integer.max = master->info.max_val; | |
274 | + return 0; | |
275 | +} | |
276 | + | |
277 | +static int master_get(struct snd_kcontrol *kcontrol, | |
278 | + struct snd_ctl_elem_value *ucontrol) | |
279 | +{ | |
280 | + struct link_master *master = snd_kcontrol_chip(kcontrol); | |
281 | + int err = master_init(master); | |
282 | + if (err < 0) | |
283 | + return err; | |
284 | + ucontrol->value.integer.value[0] = master->val; | |
285 | + return 0; | |
286 | +} | |
287 | + | |
288 | +static int master_put(struct snd_kcontrol *kcontrol, | |
289 | + struct snd_ctl_elem_value *ucontrol) | |
290 | +{ | |
291 | + struct link_master *master = snd_kcontrol_chip(kcontrol); | |
292 | + struct link_slave *slave; | |
293 | + struct snd_ctl_elem_value *uval; | |
294 | + int err, old_val; | |
295 | + | |
296 | + err = master_init(master); | |
297 | + if (err < 0) | |
298 | + return err; | |
299 | + old_val = master->val; | |
300 | + if (ucontrol->value.integer.value[0] == old_val) | |
301 | + return 0; | |
302 | + | |
303 | + uval = kmalloc(sizeof(*uval), GFP_KERNEL); | |
304 | + if (!uval) | |
305 | + return -ENOMEM; | |
306 | + list_for_each_entry(slave, &master->slaves, list) { | |
307 | + master->val = old_val; | |
308 | + uval->id = slave->slave.id; | |
309 | + slave_get_val(slave, uval); | |
310 | + master->val = ucontrol->value.integer.value[0]; | |
311 | + slave_put_val(slave, uval); | |
312 | + } | |
313 | + kfree(uval); | |
314 | + return 1; | |
315 | +} | |
316 | + | |
317 | +static void master_free(struct snd_kcontrol *kcontrol) | |
318 | +{ | |
319 | + struct link_master *master = snd_kcontrol_chip(kcontrol); | |
320 | + struct link_slave *slave; | |
321 | + | |
322 | + list_for_each_entry(slave, &master->slaves, list) | |
323 | + slave->master = NULL; | |
324 | + kfree(master); | |
325 | +} | |
326 | + | |
327 | + | |
328 | +/* | |
329 | + * Create a virtual master control with the given name | |
330 | + */ | |
331 | +struct snd_kcontrol *snd_ctl_make_virtual_master(char *name, | |
332 | + const unsigned int *tlv) | |
333 | +{ | |
334 | + struct link_master *master; | |
335 | + struct snd_kcontrol *kctl; | |
336 | + struct snd_kcontrol_new knew; | |
337 | + | |
338 | + memset(&knew, 0, sizeof(knew)); | |
339 | + knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER; | |
340 | + knew.name = name; | |
341 | + knew.info = master_info; | |
342 | + | |
343 | + master = kzalloc(sizeof(*master), GFP_KERNEL); | |
344 | + if (!master) | |
345 | + return NULL; | |
346 | + INIT_LIST_HEAD(&master->slaves); | |
347 | + | |
348 | + kctl = snd_ctl_new1(&knew, master); | |
349 | + if (!kctl) { | |
350 | + kfree(master); | |
351 | + return NULL; | |
352 | + } | |
353 | + /* override some callbacks */ | |
354 | + kctl->info = master_info; | |
355 | + kctl->get = master_get; | |
356 | + kctl->put = master_put; | |
357 | + kctl->private_free = master_free; | |
358 | + | |
359 | + /* additional (constant) TLV read */ | |
360 | + if (tlv) { | |
361 | + /* FIXME: this assumes that the max volume is 0 dB */ | |
362 | + kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; | |
363 | + kctl->tlv.p = tlv; | |
364 | + } | |
365 | + return kctl; | |
366 | +} | |
367 | + | |
368 | +EXPORT_SYMBOL(snd_ctl_make_virtual_master); |
sound/pci/Kconfig
sound/pci/hda/hda_local.h
... | ... | @@ -418,12 +418,5 @@ |
418 | 418 | hda_nid_t nid); |
419 | 419 | #endif /* CONFIG_SND_HDA_POWER_SAVE */ |
420 | 420 | |
421 | -/* | |
422 | - * virtual master control | |
423 | - */ | |
424 | -struct snd_kcontrol *snd_ctl_make_virtual_master(char *name, | |
425 | - const unsigned int *tlv); | |
426 | -int snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave); | |
427 | - | |
428 | 421 | #endif /* __SOUND_HDA_LOCAL_H */ |
sound/pci/hda/vmaster.c
1 | -/* | |
2 | - * Virtual master and slave controls | |
3 | - * | |
4 | - * Copyright (c) 2008 by Takashi Iwai <tiwai@suse.de> | |
5 | - * | |
6 | - * This program is free software; you can redistribute it and/or | |
7 | - * modify it under the terms of the GNU General Public License as | |
8 | - * published by the Free Software Foundation, version 2. | |
9 | - * | |
10 | - */ | |
11 | - | |
12 | -#include <linux/slab.h> | |
13 | -#include <sound/core.h> | |
14 | -#include <sound/control.h> | |
15 | - | |
16 | -/* | |
17 | - * a subset of information returned via ctl info callback | |
18 | - */ | |
19 | -struct link_ctl_info { | |
20 | - int type; /* value type */ | |
21 | - int count; /* item count */ | |
22 | - int min_val, max_val; /* min, max values */ | |
23 | -}; | |
24 | - | |
25 | -/* | |
26 | - * link master - this contains a list of slave controls that are | |
27 | - * identical types, i.e. info returns the same value type and value | |
28 | - * ranges, but may have different number of counts. | |
29 | - * | |
30 | - * The master control is so far only mono volume/switch for simplicity. | |
31 | - * The same value will be applied to all slaves. | |
32 | - */ | |
33 | -struct link_master { | |
34 | - struct list_head slaves; | |
35 | - struct link_ctl_info info; | |
36 | - int val; /* the master value */ | |
37 | -}; | |
38 | - | |
39 | -/* | |
40 | - * link slave - this contains a slave control element | |
41 | - * | |
42 | - * It fakes the control callbacsk with additional attenuation by the | |
43 | - * master control. A slave may have either one or two channels. | |
44 | - */ | |
45 | - | |
46 | -struct link_slave { | |
47 | - struct list_head list; | |
48 | - struct link_master *master; | |
49 | - struct link_ctl_info info; | |
50 | - int vals[2]; /* current values */ | |
51 | - struct snd_kcontrol slave; /* the copy of original control entry */ | |
52 | -}; | |
53 | - | |
54 | -/* get the slave ctl info and save the initial values */ | |
55 | -static int slave_init(struct link_slave *slave) | |
56 | -{ | |
57 | - struct snd_ctl_elem_info *uinfo; | |
58 | - struct snd_ctl_elem_value *uctl; | |
59 | - int err, ch; | |
60 | - | |
61 | - if (slave->info.count) | |
62 | - return 0; /* already initialized */ | |
63 | - | |
64 | - uinfo = kmalloc(sizeof(*uinfo), GFP_KERNEL); | |
65 | - if (!uinfo) | |
66 | - return -ENOMEM; | |
67 | - uinfo->id = slave->slave.id; | |
68 | - err = slave->slave.info(&slave->slave, uinfo); | |
69 | - if (err < 0) { | |
70 | - kfree(uinfo); | |
71 | - return err; | |
72 | - } | |
73 | - slave->info.type = uinfo->type; | |
74 | - slave->info.count = uinfo->count; | |
75 | - if (slave->info.count > 2 || | |
76 | - (slave->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER && | |
77 | - slave->info.type != SNDRV_CTL_ELEM_TYPE_BOOLEAN)) { | |
78 | - snd_printk(KERN_ERR "invalid slave element\n"); | |
79 | - kfree(uinfo); | |
80 | - return -EINVAL; | |
81 | - } | |
82 | - slave->info.min_val = uinfo->value.integer.min; | |
83 | - slave->info.max_val = uinfo->value.integer.max; | |
84 | - kfree(uinfo); | |
85 | - | |
86 | - uctl = kmalloc(sizeof(*uctl), GFP_KERNEL); | |
87 | - if (!uctl) | |
88 | - return -ENOMEM; | |
89 | - uctl->id = slave->slave.id; | |
90 | - err = slave->slave.get(&slave->slave, uctl); | |
91 | - for (ch = 0; ch < slave->info.count; ch++) | |
92 | - slave->vals[ch] = uctl->value.integer.value[ch]; | |
93 | - kfree(uctl); | |
94 | - return 0; | |
95 | -} | |
96 | - | |
97 | -/* initialize master volume */ | |
98 | -static int master_init(struct link_master *master) | |
99 | -{ | |
100 | - struct link_slave *slave; | |
101 | - | |
102 | - if (master->info.count) | |
103 | - return 0; /* already initialized */ | |
104 | - | |
105 | - list_for_each_entry(slave, &master->slaves, list) { | |
106 | - int err = slave_init(slave); | |
107 | - if (err < 0) | |
108 | - return err; | |
109 | - master->info = slave->info; | |
110 | - master->info.count = 1; /* always mono */ | |
111 | - /* set full volume as default (= no attenuation) */ | |
112 | - master->val = master->info.max_val; | |
113 | - return 0; | |
114 | - } | |
115 | - return -ENOENT; | |
116 | -} | |
117 | - | |
118 | -static int slave_get_val(struct link_slave *slave, | |
119 | - struct snd_ctl_elem_value *ucontrol) | |
120 | -{ | |
121 | - int err, ch; | |
122 | - | |
123 | - err = slave_init(slave); | |
124 | - if (err < 0) | |
125 | - return err; | |
126 | - for (ch = 0; ch < slave->info.count; ch++) | |
127 | - ucontrol->value.integer.value[ch] = slave->vals[ch]; | |
128 | - return 0; | |
129 | -} | |
130 | - | |
131 | -static int slave_put_val(struct link_slave *slave, | |
132 | - struct snd_ctl_elem_value *ucontrol) | |
133 | -{ | |
134 | - int err, ch, vol; | |
135 | - | |
136 | - err = master_init(slave->master); | |
137 | - if (err < 0) | |
138 | - return err; | |
139 | - | |
140 | - switch (slave->info.type) { | |
141 | - case SNDRV_CTL_ELEM_TYPE_BOOLEAN: | |
142 | - for (ch = 0; ch < slave->info.count; ch++) | |
143 | - ucontrol->value.integer.value[ch] &= | |
144 | - !!slave->master->val; | |
145 | - break; | |
146 | - case SNDRV_CTL_ELEM_TYPE_INTEGER: | |
147 | - for (ch = 0; ch < slave->info.count; ch++) { | |
148 | - /* max master volume is supposed to be 0 dB */ | |
149 | - vol = ucontrol->value.integer.value[ch]; | |
150 | - vol += slave->master->val - slave->master->info.max_val; | |
151 | - if (vol < slave->info.min_val) | |
152 | - vol = slave->info.min_val; | |
153 | - else if (vol > slave->info.max_val) | |
154 | - vol = slave->info.max_val; | |
155 | - ucontrol->value.integer.value[ch] = vol; | |
156 | - } | |
157 | - break; | |
158 | - } | |
159 | - return slave->slave.put(&slave->slave, ucontrol); | |
160 | -} | |
161 | - | |
162 | -/* | |
163 | - * ctl callbacks for slaves | |
164 | - */ | |
165 | -static int slave_info(struct snd_kcontrol *kcontrol, | |
166 | - struct snd_ctl_elem_info *uinfo) | |
167 | -{ | |
168 | - struct link_slave *slave = snd_kcontrol_chip(kcontrol); | |
169 | - return slave->slave.info(&slave->slave, uinfo); | |
170 | -} | |
171 | - | |
172 | -static int slave_get(struct snd_kcontrol *kcontrol, | |
173 | - struct snd_ctl_elem_value *ucontrol) | |
174 | -{ | |
175 | - struct link_slave *slave = snd_kcontrol_chip(kcontrol); | |
176 | - return slave_get_val(slave, ucontrol); | |
177 | -} | |
178 | - | |
179 | -static int slave_put(struct snd_kcontrol *kcontrol, | |
180 | - struct snd_ctl_elem_value *ucontrol) | |
181 | -{ | |
182 | - struct link_slave *slave = snd_kcontrol_chip(kcontrol); | |
183 | - int err, ch, changed = 0; | |
184 | - | |
185 | - err = slave_init(slave); | |
186 | - if (err < 0) | |
187 | - return err; | |
188 | - for (ch = 0; ch < slave->info.count; ch++) { | |
189 | - if (slave->vals[ch] != ucontrol->value.integer.value[ch]) { | |
190 | - changed = 1; | |
191 | - slave->vals[ch] = ucontrol->value.integer.value[ch]; | |
192 | - } | |
193 | - } | |
194 | - if (!changed) | |
195 | - return 0; | |
196 | - return slave_put_val(slave, ucontrol); | |
197 | -} | |
198 | - | |
199 | -static int slave_tlv_cmd(struct snd_kcontrol *kcontrol, | |
200 | - int op_flag, unsigned int size, | |
201 | - unsigned int __user *tlv) | |
202 | -{ | |
203 | - struct link_slave *slave = snd_kcontrol_chip(kcontrol); | |
204 | - /* FIXME: this assumes that the max volume is 0 dB */ | |
205 | - return slave->slave.tlv.c(&slave->slave, op_flag, size, tlv); | |
206 | -} | |
207 | - | |
208 | -static void slave_free(struct snd_kcontrol *kcontrol) | |
209 | -{ | |
210 | - struct link_slave *slave = snd_kcontrol_chip(kcontrol); | |
211 | - if (slave->slave.private_free) | |
212 | - slave->slave.private_free(&slave->slave); | |
213 | - if (slave->master) | |
214 | - list_del(&slave->list); | |
215 | - kfree(slave); | |
216 | -} | |
217 | - | |
218 | -/* | |
219 | - * Add a slave control to the group with the given master control | |
220 | - * | |
221 | - * All slaves must be the same type (returning the same information | |
222 | - * via info callback). The fucntion doesn't check it, so it's your | |
223 | - * responsibility. | |
224 | - * | |
225 | - * Also, some additional limitations: | |
226 | - * - at most two channels | |
227 | - * - logarithmic volume control (dB level), no linear volume | |
228 | - * - master can only attenuate the volume, no gain | |
229 | - */ | |
230 | -int snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave) | |
231 | -{ | |
232 | - struct link_master *master_link = snd_kcontrol_chip(master); | |
233 | - struct link_slave *srec; | |
234 | - | |
235 | - srec = kzalloc(sizeof(*srec) + | |
236 | - slave->count * sizeof(*slave->vd), GFP_KERNEL); | |
237 | - if (!srec) | |
238 | - return -ENOMEM; | |
239 | - srec->slave = *slave; | |
240 | - memcpy(srec->slave.vd, slave->vd, slave->count * sizeof(*slave->vd)); | |
241 | - srec->master = master_link; | |
242 | - | |
243 | - /* override callbacks */ | |
244 | - slave->info = slave_info; | |
245 | - slave->get = slave_get; | |
246 | - slave->put = slave_put; | |
247 | - if (slave->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) | |
248 | - slave->tlv.c = slave_tlv_cmd; | |
249 | - slave->private_data = srec; | |
250 | - slave->private_free = slave_free; | |
251 | - | |
252 | - list_add_tail(&srec->list, &master_link->slaves); | |
253 | - return 0; | |
254 | -} | |
255 | - | |
256 | -/* | |
257 | - * ctl callbacks for master controls | |
258 | - */ | |
259 | -static int master_info(struct snd_kcontrol *kcontrol, | |
260 | - struct snd_ctl_elem_info *uinfo) | |
261 | -{ | |
262 | - struct link_master *master = snd_kcontrol_chip(kcontrol); | |
263 | - int ret; | |
264 | - | |
265 | - ret = master_init(master); | |
266 | - if (ret < 0) | |
267 | - return ret; | |
268 | - uinfo->type = master->info.type; | |
269 | - uinfo->count = master->info.count; | |
270 | - uinfo->value.integer.min = master->info.min_val; | |
271 | - uinfo->value.integer.max = master->info.max_val; | |
272 | - return 0; | |
273 | -} | |
274 | - | |
275 | -static int master_get(struct snd_kcontrol *kcontrol, | |
276 | - struct snd_ctl_elem_value *ucontrol) | |
277 | -{ | |
278 | - struct link_master *master = snd_kcontrol_chip(kcontrol); | |
279 | - int err = master_init(master); | |
280 | - if (err < 0) | |
281 | - return err; | |
282 | - ucontrol->value.integer.value[0] = master->val; | |
283 | - return 0; | |
284 | -} | |
285 | - | |
286 | -static int master_put(struct snd_kcontrol *kcontrol, | |
287 | - struct snd_ctl_elem_value *ucontrol) | |
288 | -{ | |
289 | - struct link_master *master = snd_kcontrol_chip(kcontrol); | |
290 | - struct link_slave *slave; | |
291 | - struct snd_ctl_elem_value *uval; | |
292 | - int err, old_val; | |
293 | - | |
294 | - err = master_init(master); | |
295 | - if (err < 0) | |
296 | - return err; | |
297 | - old_val = master->val; | |
298 | - if (ucontrol->value.integer.value[0] == old_val) | |
299 | - return 0; | |
300 | - | |
301 | - uval = kmalloc(sizeof(*uval), GFP_KERNEL); | |
302 | - if (!uval) | |
303 | - return -ENOMEM; | |
304 | - list_for_each_entry(slave, &master->slaves, list) { | |
305 | - master->val = old_val; | |
306 | - uval->id = slave->slave.id; | |
307 | - slave_get_val(slave, uval); | |
308 | - master->val = ucontrol->value.integer.value[0]; | |
309 | - slave_put_val(slave, uval); | |
310 | - } | |
311 | - kfree(uval); | |
312 | - return 1; | |
313 | -} | |
314 | - | |
315 | -static void master_free(struct snd_kcontrol *kcontrol) | |
316 | -{ | |
317 | - struct link_master *master = snd_kcontrol_chip(kcontrol); | |
318 | - struct link_slave *slave; | |
319 | - | |
320 | - list_for_each_entry(slave, &master->slaves, list) | |
321 | - slave->master = NULL; | |
322 | - kfree(master); | |
323 | -} | |
324 | - | |
325 | - | |
326 | -/* | |
327 | - * Create a virtual master control with the given name | |
328 | - */ | |
329 | -struct snd_kcontrol *snd_ctl_make_virtual_master(char *name, | |
330 | - const unsigned int *tlv) | |
331 | -{ | |
332 | - struct link_master *master; | |
333 | - struct snd_kcontrol *kctl; | |
334 | - struct snd_kcontrol_new knew; | |
335 | - | |
336 | - memset(&knew, 0, sizeof(knew)); | |
337 | - knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER; | |
338 | - knew.name = name; | |
339 | - knew.info = master_info; | |
340 | - | |
341 | - master = kzalloc(sizeof(*master), GFP_KERNEL); | |
342 | - if (!master) | |
343 | - return NULL; | |
344 | - INIT_LIST_HEAD(&master->slaves); | |
345 | - | |
346 | - kctl = snd_ctl_new1(&knew, master); | |
347 | - if (!kctl) { | |
348 | - kfree(master); | |
349 | - return NULL; | |
350 | - } | |
351 | - /* override some callbacks */ | |
352 | - kctl->info = master_info; | |
353 | - kctl->get = master_get; | |
354 | - kctl->put = master_put; | |
355 | - kctl->private_free = master_free; | |
356 | - | |
357 | - /* additional (constant) TLV read */ | |
358 | - if (tlv) { | |
359 | - /* FIXME: this assumes that the max volume is 0 dB */ | |
360 | - kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; | |
361 | - kctl->tlv.p = tlv; | |
362 | - } | |
363 | - return kctl; | |
364 | -} |