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); |
