aboutsummaryrefslogtreecommitdiffstats
path: root/sound/core
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2008-02-18 07:03:13 -0500
committerTakashi Iwai <tiwai@suse.de>2008-04-24 06:00:12 -0400
commite922b0028fad87de0d262f9fa51f98595d2df258 (patch)
tree60798c8c6c8494edd20577d3adf85fba8bfbacd4 /sound/core
parent4235a31784f59c9be5ff71534743c055091f9735 (diff)
[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>
Diffstat (limited to 'sound/core')
-rw-r--r--sound/core/Kconfig4
-rw-r--r--sound/core/Makefile1
-rw-r--r--sound/core/vmaster.c368
3 files changed, 373 insertions, 0 deletions
diff --git a/sound/core/Kconfig b/sound/core/Kconfig
index 829ca38b595e..a8d71c6c8e75 100644
--- a/sound/core/Kconfig
+++ b/sound/core/Kconfig
@@ -181,3 +181,7 @@ config SND_PCM_XRUN_DEBUG
181 It is usually not required, but if you have trouble with 181 It is usually not required, but if you have trouble with
182 sound clicking when system is loaded, it may help to determine 182 sound clicking when system is loaded, it may help to determine
183 the process or driver which causes the scheduling gaps. 183 the process or driver which causes the scheduling gaps.
184
185config SND_VMASTER
186 bool
187 depends on SND
diff --git a/sound/core/Makefile b/sound/core/Makefile
index 267039a97bd5..da8e685eef9c 100644
--- a/sound/core/Makefile
+++ b/sound/core/Makefile
@@ -6,6 +6,7 @@
6snd-y := sound.o init.o memory.o info.o control.o misc.o device.o 6snd-y := sound.o init.o memory.o info.o control.o misc.o device.o
7snd-$(CONFIG_ISA_DMA_API) += isadma.o 7snd-$(CONFIG_ISA_DMA_API) += isadma.o
8snd-$(CONFIG_SND_OSSEMUL) += sound_oss.o info_oss.o 8snd-$(CONFIG_SND_OSSEMUL) += sound_oss.o info_oss.o
9snd-$(CONFIG_SND_VMASTER) += vmaster.o
9 10
10snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \ 11snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \
11 pcm_memory.o 12 pcm_memory.o
diff --git a/sound/core/vmaster.c b/sound/core/vmaster.c
new file mode 100644
index 000000000000..7cfd8b8fb4e7
--- /dev/null
+++ b/sound/core/vmaster.c
@@ -0,0 +1,368 @@
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 */
19struct 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 */
33struct 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
46struct 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 */
55static 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 */
98static 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
118static 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
131static 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 */
165static 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
172static 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
179static 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
199static 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
208static 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 */
230int 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
256EXPORT_SYMBOL(snd_ctl_add_slave);
257
258/*
259 * ctl callbacks for master controls
260 */
261static 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
277static 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
288static 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
317static 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 */
331struct 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
368EXPORT_SYMBOL(snd_ctl_make_virtual_master);