aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci/hda
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2008-01-10 10:52:42 -0500
committerJaroslav Kysela <perex@perex.cz>2008-01-31 11:29:54 -0500
commit3b0a5f22d4649433a5842ffc7313803292e95718 (patch)
tree02cb156ca8dc6780f8b89fcd08c5794ea94bb42a /sound/pci/hda
parentab40d4f12cda366ed1f308d2a041480769f9a77e (diff)
[ALSA] Add virtual master control helpers
Added helper functions to implement virtual master volume controls. The virtual master control is a control element that has multiple slave controls. The value of master element is equally added to slave elements. The functions are written for general purpose, but it's put in the HD-audio directory as now, since HD-audio driver is the only user. It should be moved to the common place once after other drivers use vmaster. Signed-off-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: Jaroslav Kysela <perex@perex.cz>
Diffstat (limited to 'sound/pci/hda')
-rw-r--r--sound/pci/hda/Makefile2
-rw-r--r--sound/pci/hda/hda_local.h7
-rw-r--r--sound/pci/hda/vmaster.c364
3 files changed, 372 insertions, 1 deletions
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index ab0c726d648e..9e0d8a1268aa 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -2,7 +2,7 @@ snd-hda-intel-y := hda_intel.o
2# since snd-hda-intel is the only driver using hda-codec, 2# since snd-hda-intel is the only driver using hda-codec,
3# merge it into a single module although it was originally 3# merge it into a single module although it was originally
4# designed to be individual modules 4# designed to be individual modules
5snd-hda-intel-y += hda_codec.o 5snd-hda-intel-y += hda_codec.o vmaster.o
6snd-hda-intel-$(CONFIG_PROC_FS) += hda_proc.o 6snd-hda-intel-$(CONFIG_PROC_FS) += hda_proc.o
7snd-hda-intel-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o 7snd-hda-intel-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o
8snd-hda-intel-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o 8snd-hda-intel-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 8c56c9cb0d09..e09f41bd6b2a 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -398,4 +398,11 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
398 hda_nid_t nid); 398 hda_nid_t nid);
399#endif /* CONFIG_SND_HDA_POWER_SAVE */ 399#endif /* CONFIG_SND_HDA_POWER_SAVE */
400 400
401/*
402 * virtual master control
403 */
404struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
405 const unsigned int *tlv);
406int snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave);
407
401#endif /* __SOUND_HDA_LOCAL_H */ 408#endif /* __SOUND_HDA_LOCAL_H */
diff --git a/sound/pci/hda/vmaster.c b/sound/pci/hda/vmaster.c
new file mode 100644
index 000000000000..2da49d20a1fc
--- /dev/null
+++ b/sound/pci/hda/vmaster.c
@@ -0,0 +1,364 @@
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
256/*
257 * ctl callbacks for master controls
258 */
259static 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
275static 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
286static 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
315static 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 */
329struct 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}