diff options
author | Torsten Schenk <torsten.schenk@zoho.com> | 2012-02-22 09:21:12 -0500 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2012-02-22 09:51:06 -0500 |
commit | f90ffbf3c68a69714b4273b203d4deb5ae81d8d6 (patch) | |
tree | e725f3dac0af633e0cc683aa47dafac374b4a201 /sound/usb/6fire | |
parent | 8e247a9c90e65b25b5b064e2159d9c4c2c173a5e (diff) |
ALSA: snd-usb-6fire: add individual volume control for analog channels
Add a stereo volume control for every analog output pair 1/2, 3/4, 5/6.
Signed-off-by: Torsten Schenk <torsten.schenk@zoho.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/usb/6fire')
-rw-r--r-- | sound/usb/6fire/control.c | 146 | ||||
-rw-r--r-- | sound/usb/6fire/control.h | 3 |
2 files changed, 126 insertions, 23 deletions
diff --git a/sound/usb/6fire/control.c b/sound/usb/6fire/control.c index b00b8bb88c63..c22cc29e33d7 100644 --- a/sound/usb/6fire/control.c +++ b/sound/usb/6fire/control.c | |||
@@ -7,6 +7,10 @@ | |||
7 | * Created: Jan 01, 2011 | 7 | * Created: Jan 01, 2011 |
8 | * Copyright: (C) Torsten Schenk | 8 | * Copyright: (C) Torsten Schenk |
9 | * | 9 | * |
10 | * Thanks to: | ||
11 | * - Holger Ruckdeschel: he found out how to control individual channel | ||
12 | * volumes and introduced mute switch | ||
13 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | 14 | * This program is free software; you can redistribute it and/or modify |
11 | * it under the terms of the GNU General Public License as published by | 15 | * it under the terms of the GNU General Public License as published by |
12 | * the Free Software Foundation; either version 2 of the License, or | 16 | * the Free Software Foundation; either version 2 of the License, or |
@@ -39,7 +43,7 @@ init_data[] = { | |||
39 | { 0x22, 0x03, 0x00 }, { 0x20, 0x03, 0x08 }, { 0x22, 0x04, 0x00 }, | 43 | { 0x22, 0x03, 0x00 }, { 0x20, 0x03, 0x08 }, { 0x22, 0x04, 0x00 }, |
40 | { 0x20, 0x04, 0x08 }, { 0x22, 0x05, 0x01 }, { 0x20, 0x05, 0x08 }, | 44 | { 0x20, 0x04, 0x08 }, { 0x22, 0x05, 0x01 }, { 0x20, 0x05, 0x08 }, |
41 | { 0x22, 0x04, 0x01 }, { 0x12, 0x04, 0x00 }, { 0x12, 0x05, 0x00 }, | 45 | { 0x22, 0x04, 0x01 }, { 0x12, 0x04, 0x00 }, { 0x12, 0x05, 0x00 }, |
42 | { 0x12, 0x0d, 0x78 }, { 0x12, 0x21, 0x82 }, { 0x12, 0x22, 0x80 }, | 46 | { 0x12, 0x0d, 0x38 }, { 0x12, 0x21, 0x82 }, { 0x12, 0x22, 0x80 }, |
43 | { 0x12, 0x23, 0x00 }, { 0x12, 0x06, 0x02 }, { 0x12, 0x03, 0x00 }, | 47 | { 0x12, 0x23, 0x00 }, { 0x12, 0x06, 0x02 }, { 0x12, 0x03, 0x00 }, |
44 | { 0x12, 0x02, 0x00 }, { 0x22, 0x03, 0x01 }, | 48 | { 0x12, 0x02, 0x00 }, { 0x22, 0x03, 0x01 }, |
45 | { 0 } /* TERMINATING ENTRY */ | 49 | { 0 } /* TERMINATING ENTRY */ |
@@ -56,15 +60,18 @@ enum { | |||
56 | DIGITAL_THRU_ONLY_SAMPLERATE = 3 | 60 | DIGITAL_THRU_ONLY_SAMPLERATE = 3 |
57 | }; | 61 | }; |
58 | 62 | ||
59 | static void usb6fire_control_master_vol_update(struct control_runtime *rt) | 63 | static void usb6fire_control_output_vol_update(struct control_runtime *rt) |
60 | { | 64 | { |
61 | struct comm_runtime *comm_rt = rt->chip->comm; | 65 | struct comm_runtime *comm_rt = rt->chip->comm; |
62 | if (comm_rt) { | 66 | int i; |
63 | /* set volume */ | 67 | |
64 | comm_rt->write8(comm_rt, 0x12, 0x0f, 180 - rt->master_vol); | 68 | if (comm_rt) |
65 | /* unmute */ | 69 | for (i = 0; i < 6; i++) |
66 | comm_rt->write8(comm_rt, 0x12, 0x0e, 0x00); | 70 | if (!(rt->ovol_updated & (1 << i))) { |
67 | } | 71 | comm_rt->write8(comm_rt, 0x12, 0x0f + i, |
72 | 180 - rt->output_vol[i]); | ||
73 | rt->ovol_updated |= 1 << i; | ||
74 | } | ||
68 | } | 75 | } |
69 | 76 | ||
70 | static void usb6fire_control_line_phono_update(struct control_runtime *rt) | 77 | static void usb6fire_control_line_phono_update(struct control_runtime *rt) |
@@ -146,34 +153,58 @@ static int usb6fire_control_streaming_update(struct control_runtime *rt) | |||
146 | return -EINVAL; | 153 | return -EINVAL; |
147 | } | 154 | } |
148 | 155 | ||
149 | static int usb6fire_control_master_vol_info(struct snd_kcontrol *kcontrol, | 156 | static int usb6fire_control_output_vol_info(struct snd_kcontrol *kcontrol, |
150 | struct snd_ctl_elem_info *uinfo) | 157 | struct snd_ctl_elem_info *uinfo) |
151 | { | 158 | { |
152 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | 159 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; |
153 | uinfo->count = 1; | 160 | uinfo->count = 2; |
154 | uinfo->value.integer.min = 0; | 161 | uinfo->value.integer.min = 0; |
155 | uinfo->value.integer.max = 180; | 162 | uinfo->value.integer.max = 180; |
156 | return 0; | 163 | return 0; |
157 | } | 164 | } |
158 | 165 | ||
159 | static int usb6fire_control_master_vol_put(struct snd_kcontrol *kcontrol, | 166 | static int usb6fire_control_output_vol_put(struct snd_kcontrol *kcontrol, |
160 | struct snd_ctl_elem_value *ucontrol) | 167 | struct snd_ctl_elem_value *ucontrol) |
161 | { | 168 | { |
162 | struct control_runtime *rt = snd_kcontrol_chip(kcontrol); | 169 | struct control_runtime *rt = snd_kcontrol_chip(kcontrol); |
170 | unsigned int ch = kcontrol->private_value; | ||
163 | int changed = 0; | 171 | int changed = 0; |
164 | if (rt->master_vol != ucontrol->value.integer.value[0]) { | 172 | |
165 | rt->master_vol = ucontrol->value.integer.value[0]; | 173 | if (ch > 4) { |
166 | usb6fire_control_master_vol_update(rt); | 174 | snd_printk(KERN_ERR PREFIX "Invalid channel in volume control."); |
175 | return -EINVAL; | ||
176 | } | ||
177 | |||
178 | if (rt->output_vol[ch] != ucontrol->value.integer.value[0]) { | ||
179 | rt->output_vol[ch] = ucontrol->value.integer.value[0]; | ||
180 | rt->ovol_updated &= ~(1 << ch); | ||
167 | changed = 1; | 181 | changed = 1; |
168 | } | 182 | } |
183 | if (rt->output_vol[ch + 1] != ucontrol->value.integer.value[1]) { | ||
184 | rt->output_vol[ch + 1] = ucontrol->value.integer.value[1]; | ||
185 | rt->ovol_updated &= ~(2 << ch); | ||
186 | changed = 1; | ||
187 | } | ||
188 | |||
189 | if (changed) | ||
190 | usb6fire_control_output_vol_update(rt); | ||
191 | |||
169 | return changed; | 192 | return changed; |
170 | } | 193 | } |
171 | 194 | ||
172 | static int usb6fire_control_master_vol_get(struct snd_kcontrol *kcontrol, | 195 | static int usb6fire_control_output_vol_get(struct snd_kcontrol *kcontrol, |
173 | struct snd_ctl_elem_value *ucontrol) | 196 | struct snd_ctl_elem_value *ucontrol) |
174 | { | 197 | { |
175 | struct control_runtime *rt = snd_kcontrol_chip(kcontrol); | 198 | struct control_runtime *rt = snd_kcontrol_chip(kcontrol); |
176 | ucontrol->value.integer.value[0] = rt->master_vol; | 199 | unsigned int ch = kcontrol->private_value; |
200 | |||
201 | if (ch > 4) { | ||
202 | snd_printk(KERN_ERR PREFIX "Invalid channel in volume control."); | ||
203 | return -EINVAL; | ||
204 | } | ||
205 | |||
206 | ucontrol->value.integer.value[0] = rt->output_vol[ch]; | ||
207 | ucontrol->value.integer.value[1] = rt->output_vol[ch + 1]; | ||
177 | return 0; | 208 | return 0; |
178 | } | 209 | } |
179 | 210 | ||
@@ -268,18 +299,47 @@ static int usb6fire_control_digital_thru_get(struct snd_kcontrol *kcontrol, | |||
268 | return 0; | 299 | return 0; |
269 | } | 300 | } |
270 | 301 | ||
271 | static struct __devinitdata snd_kcontrol_new elements[] = { | 302 | static struct __devinitdata snd_kcontrol_new vol_elements[] = { |
272 | { | 303 | { |
273 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | 304 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
274 | .name = "Master Playback Volume", | 305 | .name = "Analog Playback Volume", |
275 | .index = 0, | 306 | .index = 0, |
307 | .private_value = 0, | ||
308 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | | ||
309 | SNDRV_CTL_ELEM_ACCESS_TLV_READ, | ||
310 | .info = usb6fire_control_output_vol_info, | ||
311 | .get = usb6fire_control_output_vol_get, | ||
312 | .put = usb6fire_control_output_vol_put, | ||
313 | .tlv = { .p = tlv_output } | ||
314 | }, | ||
315 | { | ||
316 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
317 | .name = "Analog Playback Volume", | ||
318 | .index = 1, | ||
319 | .private_value = 2, | ||
320 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | | ||
321 | SNDRV_CTL_ELEM_ACCESS_TLV_READ, | ||
322 | .info = usb6fire_control_output_vol_info, | ||
323 | .get = usb6fire_control_output_vol_get, | ||
324 | .put = usb6fire_control_output_vol_put, | ||
325 | .tlv = { .p = tlv_output } | ||
326 | }, | ||
327 | { | ||
328 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
329 | .name = "Analog Playback Volume", | ||
330 | .index = 2, | ||
331 | .private_value = 4, | ||
276 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | | 332 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | |
277 | SNDRV_CTL_ELEM_ACCESS_TLV_READ, | 333 | SNDRV_CTL_ELEM_ACCESS_TLV_READ, |
278 | .info = usb6fire_control_master_vol_info, | 334 | .info = usb6fire_control_output_vol_info, |
279 | .get = usb6fire_control_master_vol_get, | 335 | .get = usb6fire_control_output_vol_get, |
280 | .put = usb6fire_control_master_vol_put, | 336 | .put = usb6fire_control_output_vol_put, |
281 | .tlv = { .p = tlv_output } | 337 | .tlv = { .p = tlv_output } |
282 | }, | 338 | }, |
339 | {} | ||
340 | }; | ||
341 | |||
342 | static struct __devinitdata snd_kcontrol_new elements[] = { | ||
283 | { | 343 | { |
284 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | 344 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
285 | .name = "Line/Phono Capture Route", | 345 | .name = "Line/Phono Capture Route", |
@@ -310,6 +370,40 @@ static struct __devinitdata snd_kcontrol_new elements[] = { | |||
310 | {} | 370 | {} |
311 | }; | 371 | }; |
312 | 372 | ||
373 | static int usb6fire_control_add_virtual( | ||
374 | struct control_runtime *rt, | ||
375 | struct snd_card *card, | ||
376 | char *name, | ||
377 | struct snd_kcontrol_new *elems) | ||
378 | { | ||
379 | int ret; | ||
380 | int i; | ||
381 | struct snd_kcontrol *vmaster = | ||
382 | snd_ctl_make_virtual_master(name, tlv_output); | ||
383 | struct snd_kcontrol *control; | ||
384 | |||
385 | if (!vmaster) | ||
386 | return -ENOMEM; | ||
387 | ret = snd_ctl_add(card, vmaster); | ||
388 | if (ret < 0) | ||
389 | return ret; | ||
390 | |||
391 | i = 0; | ||
392 | while (elems[i].name) { | ||
393 | control = snd_ctl_new1(&elems[i], rt); | ||
394 | if (!control) | ||
395 | return -ENOMEM; | ||
396 | ret = snd_ctl_add(card, control); | ||
397 | if (ret < 0) | ||
398 | return ret; | ||
399 | ret = snd_ctl_add_slave(vmaster, control); | ||
400 | if (ret < 0) | ||
401 | return ret; | ||
402 | i++; | ||
403 | } | ||
404 | return 0; | ||
405 | } | ||
406 | |||
313 | int __devinit usb6fire_control_init(struct sfire_chip *chip) | 407 | int __devinit usb6fire_control_init(struct sfire_chip *chip) |
314 | { | 408 | { |
315 | int i; | 409 | int i; |
@@ -335,9 +429,17 @@ int __devinit usb6fire_control_init(struct sfire_chip *chip) | |||
335 | 429 | ||
336 | usb6fire_control_opt_coax_update(rt); | 430 | usb6fire_control_opt_coax_update(rt); |
337 | usb6fire_control_line_phono_update(rt); | 431 | usb6fire_control_line_phono_update(rt); |
338 | usb6fire_control_master_vol_update(rt); | 432 | usb6fire_control_output_vol_update(rt); |
339 | usb6fire_control_streaming_update(rt); | 433 | usb6fire_control_streaming_update(rt); |
340 | 434 | ||
435 | ret = usb6fire_control_add_virtual(rt, chip->card, | ||
436 | "Master Playback Volume", vol_elements); | ||
437 | if (ret) { | ||
438 | kfree(rt); | ||
439 | snd_printk(KERN_ERR PREFIX "cannot add control.\n"); | ||
440 | return ret; | ||
441 | } | ||
442 | |||
341 | i = 0; | 443 | i = 0; |
342 | while (elements[i].name) { | 444 | while (elements[i].name) { |
343 | ret = snd_ctl_add(chip->card, snd_ctl_new1(&elements[i], rt)); | 445 | ret = snd_ctl_add(chip->card, snd_ctl_new1(&elements[i], rt)); |
diff --git a/sound/usb/6fire/control.h b/sound/usb/6fire/control.h index 0dcb1d2f522c..ce024113c98f 100644 --- a/sound/usb/6fire/control.h +++ b/sound/usb/6fire/control.h | |||
@@ -43,7 +43,8 @@ struct control_runtime { | |||
43 | bool line_phono_switch; | 43 | bool line_phono_switch; |
44 | bool digital_thru_switch; | 44 | bool digital_thru_switch; |
45 | bool usb_streaming; | 45 | bool usb_streaming; |
46 | u8 master_vol; | 46 | u8 output_vol[6]; |
47 | u8 ovol_updated; | ||
47 | }; | 48 | }; |
48 | 49 | ||
49 | int __devinit usb6fire_control_init(struct sfire_chip *chip); | 50 | int __devinit usb6fire_control_init(struct sfire_chip *chip); |